Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
25 changes: 25 additions & 0 deletions .cursor/rules/codec-packages.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
description: Split Macintosh codecs from Finder UI; keep SIT/rez/dcmp pluggable
alwaysApply: true
---

# Codec packages (ClassicStack-web)

When splitting ClassicStack-web into packages, **do not** put StuffIt, BinHex, MacBinary, ZIP, resource-fork parsing, `dcmp` decompress, icon/BNDL/vers decoders, or rez in the Finder UI package.

Intended packages (names are indicative):

- `@classicstack/finder-ui` — FinderWindow, dialogs, tokens
- `@classicstack/catalog` — Catalog / VNode
- `@classicstack/expand` — expander registry + `expandIncoming`
- `@classicstack/stuffit` — bundled SIT (replaceable)
- `@classicstack/resource-fork` — map parse, SparseBytes
- `@classicstack/resource-types` — ICON/icl8/BNDL/vers
- `@classicstack/appledouble` — AppleSingle / AppleDouble / zip sidecars

Rules:

- Finder depends on `Catalog` + codec **registries** (`src/fs/codecs.ts`), not on a specific SIT or rez implementation.
- Bundled ids: `sit`, `binhex`, `macbinary`, `zip`, `applesingle`, `dcmp`. Re-register the same id to replace the default.
- Third parties add formats via `registerArchiveCodec`, `registerResourceDecompressor`, `registerResourceTypeDecoder`, `registerRezCodec`.
- New archive or resource formats register a codec; do not add `if (ext === …)` branches in `finder-window.ts`.
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# ClassicStackWeb
# ClassicStack-Web
AppleTalk / AFP stack over **WebSerial → TashTalk → LocalTalk** in your **browser**.

Browser AppleTalk / AFP stack over **WebSerial → TashTalk → LocalTalk**.
> ## Need more features?
> Checkout [ClassicStack](https://github.com/ObsoleteMadness/ClassicStack) - a full-featured Apple File Server,
> IPX/NetBeui SMB and Netware Server and Client for Windows, MacOS and Linux.

## Features

Expand All @@ -15,7 +18,9 @@ Browser AppleTalk / AFP stack over **WebSerial → TashTalk → LocalTalk**.
- Advanced → **Extension editor…** maps filename suffixes to Macintosh creator/type plus a comment (saved in localStorage; used on import when there is no AppleDouble metadata)
- Dropped BinHex (`.hqx`), MacBinary (`.bin`), StuffIt (`.sit`), and ZIP (`.zip`) are decoded into the inner Macintosh files (name, forks, type/creator); ZIP merges `._` and `__MACOSX` AppleDouble. Toggle via Advanced → **Auto-expand files**
- Resource-fork icons (BNDL / ICN# / icl8) with `./icons` system fallbacks and a clearable local type-icon cache
- Advanced → **Resource Fork…** (or Get Info / context menu **Resources…**) lists every resource type, id, and BNDL mapping in the selected file
- Windows `.ico` plus icons embedded in PE/NE `.exe` / `.scr` (and `.dll` / `.cpl`)
- Advanced → **Resource Fork…** (or Get Info / context menu **Resources…**) lists every Macintosh resource type, id, and BNDL mapping — hidden when the volume has no resource forks. Get Info type/creator editors hide when the volume has no Finder info
- View → **Windows Resources…** (or Get Info / context **Windows Resources…** on `.exe` / `.dll` / `.ico`) lists PE/NE RT_* types with icon, bitmap, version, string, and hex previews

## Requirements

Expand Down Expand Up @@ -44,6 +49,14 @@ See the plan: TashTalk → LLAP/DDP → NBP → ATP → ASP → AFP, with a Virt

Protocol codecs mirror [ClassicStack](https://github.com/ObsoleteMadness/ClassicStack).

Finder UI (`src/ui/finder-window.ts`) must stay independent of archive and resource-fork codecs. StuffIt, BinHex, MacBinary, ZIP, Apple compressed resources (`dcmp`), icon/BNDL decoders, Windows ICO/PE/NE icon extractors, and any future **rez** decompiler live under `src/fs/` and register through `src/fs/codecs.ts` (`classicstack-web/fs/codecs`). When this repo splits into packages, those modules become their own packages (`@classicstack/finder-ui`, `@classicstack/expand`, `@classicstack/stuffit`, `@classicstack/resource-fork`, `@classicstack/winicon`, …) so a third party can ship a replacement SIT expander or rez decoder without forking the PWA.

The Finder sidebar layout is owned by the host: set `RemoteEndpoint.group` / `badge` and implement `FinderHost.sidebarGroups()`. ClassicStack groups local shares vs AppleTalk / SMB / NetWare / EtherDFS clients; the TashTalk PWA keeps a single LocalTalk list.

The extension→type/creator editor is shared (`ExtensionEditorDialog`). Persistence is a pluggable `ExtensionMapStore`: the PWA uses browser localStorage; ClassicStack’s SPA uses the Go `/extmap` API (Netatalk `extmap.conf`).

Register with `registerArchiveCodec`, `registerResourceDecompressor`, `registerResourceTypeDecoder`, or `registerRezCodec`. Re-registering the bundled ids (`sit`, `binhex`, `macbinary`, `zip`, `applesingle`, `dcmp`) replaces the default implementation.

## Credits

ClassicStack is indebted to the following source code and authors:
Expand All @@ -53,12 +66,14 @@ ClassicStack is indebted to the following source code and authors:
- [XADMaster](https://github.com/MacPaw/XADMaster) / The Unarchiver — [Dag Ågren](https://github.com/DagAgren), [Dirk Stöcker](https://www.dstoecker.eu/xadmaster.html), and the xadmaster library — StuffIt archive formats (also via [stuffit-rs](https://github.com/benletchford/stuffit-rs))
- [Matthias Wiesmann](https://github.com/wiesmann) for [QuickDrawViewer](https://github.com/wiesmann/QuickDrawViewer) (PICT / QuickDraw opcode layout and PackBits; Apache 2.0)
- [Harald Kuhr](https://github.com/haraldk) for [TwelveMonkeys](https://github.com/haraldk/TwelveMonkeys) [imageio-pict](https://github.com/haraldk/TwelveMonkeys/tree/master/imageio/imageio-pict) (PICT bitmap packing; BSD-3-Clause)
- Icons by [Icons8](https://icons8.com/)

## Notes

- Guest login (`No User Authent`) plus `Cleartxt Passwrd` and Randnum UAMs when connecting as a client
- Resource-fork icon decoder (ICN# / icl8 / BNDL) for Finder icons; system glyphs from `./icons`; application type icons cached locally (Advanced → Clear icon cache)
- Resource Fork explorer (Advanced → Resource Fork…) dumps types, ids, BNDL/FREF mappings, and decoded icon previews from the selected file
- Resource-fork icon decoder (ICN# / icl8 / BNDL) plus Windows ICO/PE/NE `.exe`/`.scr` icons; system glyphs from `./icons`; type icons cached locally (View → Clear icon cache)
- Resource Fork explorer (View → Resource Fork…) dumps types, ids, BNDL/FREF mappings, and decoded icon previews — omitted when the volume has no resource forks
- Windows resource explorer (View → Windows Resources…) dumps PE/NE RT_* types from the data fork (icons, bitmaps, version info, string tables, manifests) with a hex dump, modeled on the Macintosh viewer
- Desktop DB Add/GetIcon is implemented on the server
- AFP Write uses a simplified path; full WriteContinue parity may need tuning against specific Mac OS versions
- Netboot ChainBoot keeps the selected HFS image in browser memory for the session (writes are not persisted back to the file)
Expand Down
Binary file added icons/classic/AppleShare.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/MacTCP Prep.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/apple-ip-gw.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/appletalk-cp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/appletalk-mac.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/bios.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/dos-prompt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/ethernet gateway.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/ethertalk.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/gateway-settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/group.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/ibm-share.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/ip-tunnel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/ipx-cp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/localtalk-bridge-cp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/localtalk-bridge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/mac-group.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/macipx-cp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/macipx.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/mactcp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/macx.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/monitor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/novell.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/open-transport-cp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/osi-cp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/classic/pc-cp.png
Binary file added icons/classic/pc-disk.png
Binary file added icons/classic/phonenet.png
Binary file added icons/classic/prompt-cp.png
Binary file added icons/classic/serial-switch.png
Binary file added icons/classic/tcp-cp.png
Binary file added icons/classic/user.png
Binary file added icons/classic/users.png
Binary file added icons/classic/windows-mac.png
Binary file added icons/classic/windows-share.png
Binary file added icons/classic/windows-share2.png
Binary file added icons/classic/windows-share3.png
Binary file added icons/ui/NetwareBridge.png
Binary file added icons/ui/ShareApple.png
Binary file added icons/ui/ShareDos.png
Binary file added icons/ui/ShareFolder.png
Binary file added icons/ui/ShareNetware.png
Binary file added icons/ui/ShareWindows.png
Binary file added icons/ui/icons8-25-de-abril-bridge-100.png
Binary file added icons/ui/icons8-25-de-abril-bridge-50.png
Binary file added icons/ui/icons8-apple-inc-100.png
Binary file added icons/ui/icons8-apple-inc-50.png
Binary file added icons/ui/icons8-application-window-32.png
Binary file added icons/ui/icons8-application-window-48.png
Binary file added icons/ui/icons8-application-window-96.png
Binary file added icons/ui/icons8-archive-32.png
Binary file added icons/ui/icons8-archive-48.png
Binary file added icons/ui/icons8-archive-96.png
Binary file added icons/ui/icons8-archive-folder-100.png
Binary file added icons/ui/icons8-archive-folder-32.png
Binary file added icons/ui/icons8-archive-folder-48.png
Binary file added icons/ui/icons8-archive-folder-50.png
Binary file added icons/ui/icons8-archive-folder-96.png
Binary file added icons/ui/icons8-backend-development-100.png
Binary file added icons/ui/icons8-backend-development-50.png
Binary file added icons/ui/icons8-binary-file-32.png
Binary file added icons/ui/icons8-binary-file-48.png
Binary file added icons/ui/icons8-binary-file-96.png
Binary file added icons/ui/icons8-bios-100.png
Binary file added icons/ui/icons8-bios-50.png
Binary file added icons/ui/icons8-broadcasting-100.png
Binary file added icons/ui/icons8-broadcasting-50.png
Binary file added icons/ui/icons8-c-drive-2-100.png
Binary file added icons/ui/icons8-c-drive-2-50.png
Binary file added icons/ui/icons8-code-file-32.png
Binary file added icons/ui/icons8-code-file-48.png
Binary file added icons/ui/icons8-code-file-96.png
Binary file added icons/ui/icons8-command-line-100.png
Binary file added icons/ui/icons8-command-line-32.png
Binary file added icons/ui/icons8-command-line-48.png
Binary file added icons/ui/icons8-command-line-50.png
Binary file added icons/ui/icons8-command-line-96.png
Binary file added icons/ui/icons8-connected-100.png
Binary file added icons/ui/icons8-connected-50.png
Binary file added icons/ui/icons8-console-100.png
Binary file added icons/ui/icons8-console-50.png
Binary file added icons/ui/icons8-disconnected-100.png
Binary file added icons/ui/icons8-disconnected-50.png
Binary file added icons/ui/icons8-dll-32.png
Binary file added icons/ui/icons8-dll-48.png
Binary file added icons/ui/icons8-dll-96.png
Binary file added icons/ui/icons8-dos-100.png
Binary file added icons/ui/icons8-dos-50.png
Binary file added icons/ui/icons8-edit-property-100.png
Binary file added icons/ui/icons8-edit-property-50.png
Binary file added icons/ui/icons8-electronics-100.png
Binary file added icons/ui/icons8-electronics-50.png
Binary file added icons/ui/icons8-environment-care-100.png
Binary file added icons/ui/icons8-environment-care-50.png
Binary file added icons/ui/icons8-exe-32.png
Binary file added icons/ui/icons8-exe-48.png
Binary file added icons/ui/icons8-exe-96.png
Binary file added icons/ui/icons8-fork-100.png
Binary file added icons/ui/icons8-fork-50.png
Binary file added icons/ui/icons8-gear-100.png
Binary file added icons/ui/icons8-gear-50.png
Binary file added icons/ui/icons8-gif-32.png
Binary file added icons/ui/icons8-gif-48.png
Binary file added icons/ui/icons8-gif-96.png
Binary file added icons/ui/icons8-happy-mac-100.png
Binary file added icons/ui/icons8-happy-mac-50.png
Binary file added icons/ui/icons8-hdd-100.png
Binary file added icons/ui/icons8-hdd-50.png
Binary file added icons/ui/icons8-ibm-100.png
Binary file added icons/ui/icons8-ibm-50.png
Binary file added icons/ui/icons8-ibm-pc-dos-5.0-logo-100.png
Binary file added icons/ui/icons8-ibm-pc-dos-5.0-logo-50.png
Binary file added icons/ui/icons8-image-32.png
Binary file added icons/ui/icons8-image-48.png
Binary file added icons/ui/icons8-image-96.png
Binary file added icons/ui/icons8-image-file-32.png
Binary file added icons/ui/icons8-image-file-48.png
Binary file added icons/ui/icons8-image-file-96.png
Binary file added icons/ui/icons8-integrated-circuit-100.png
Binary file added icons/ui/icons8-integrated-circuit-50.png
Binary file added icons/ui/icons8-jpg-32.png
Binary file added icons/ui/icons8-jpg-48.png
Binary file added icons/ui/icons8-jpg-96.png
Binary file added icons/ui/icons8-layers-100.png
Binary file added icons/ui/icons8-layers-50.png
Binary file added icons/ui/icons8-mac-client-100.png
Binary file added icons/ui/icons8-mac-client-50.png
Binary file added icons/ui/icons8-mac-folder-100.png
Binary file added icons/ui/icons8-mac-folder-50.png
Binary file added icons/ui/icons8-mac-logo-100.png
Binary file added icons/ui/icons8-mac-logo-50.png
Binary file added icons/ui/icons8-moment-sharing-100.png
Binary file added icons/ui/icons8-moment-sharing-50.png
Binary file added icons/ui/icons8-monitor-100.png
Binary file added icons/ui/icons8-monitor-50.png
Binary file added icons/ui/icons8-multicast-100.png
Binary file added icons/ui/icons8-multicast-50.png
Binary file added icons/ui/icons8-netware-logo-100.png
Binary file added icons/ui/icons8-netware-logo-50.png
Binary file added icons/ui/icons8-network-card-100.png
Binary file added icons/ui/icons8-network-card-50.png
Binary file added icons/ui/icons8-network-gateway-100.png
Binary file added icons/ui/icons8-network-gateway-50.png
Binary file added icons/ui/icons8-notification-100.png
Binary file added icons/ui/icons8-notification-50.png
Binary file added icons/ui/icons8-old-computer-100.png
Binary file added icons/ui/icons8-old-computer-50.png
Binary file added icons/ui/icons8-online-100.png
Binary file added icons/ui/icons8-online-50.png
Binary file added icons/ui/icons8-pdf-1-32.png
Binary file added icons/ui/icons8-pdf-1-48.png
Binary file added icons/ui/icons8-pdf-1-96.png
Binary file added icons/ui/icons8-pdf-32.png
Binary file added icons/ui/icons8-pdf-48.png
Binary file added icons/ui/icons8-pdf-96.png
Binary file added icons/ui/icons8-ps2-female-100.png
Binary file added icons/ui/icons8-ps2-female-50.png
Binary file added icons/ui/icons8-pseudo-minidin-8b-diagram-100.png
Binary file added icons/ui/icons8-pseudo-minidin-8b-diagram-50.png
Binary file added icons/ui/icons8-radio-tower-100.png
Binary file added icons/ui/icons8-radio-tower-50.png
Binary file added icons/ui/icons8-resources-100.png
Binary file added icons/ui/icons8-resources-50.png
Binary file added icons/ui/icons8-rj45-100.png
Binary file added icons/ui/icons8-rj45-50.png
Binary file added icons/ui/icons8-router-symbol-100.png
Binary file added icons/ui/icons8-router-symbol-50.png
Binary file added icons/ui/icons8-scroll-32.png
Binary file added icons/ui/icons8-scroll-48.png
Binary file added icons/ui/icons8-scroll-96.png
Binary file added icons/ui/icons8-server-100.png
Binary file added icons/ui/icons8-server-50.png
Binary file added icons/ui/icons8-service-100.png
Binary file added icons/ui/icons8-service-50.png
Binary file added icons/ui/icons8-services-100.png
Binary file added icons/ui/icons8-services-50.png
Binary file added icons/ui/icons8-settings-1-100.png
Binary file added icons/ui/icons8-settings-1-50.png
Binary file added icons/ui/icons8-settings-100.png
Binary file added icons/ui/icons8-settings-50.png
Binary file added icons/ui/icons8-shutdown-100.png
Binary file added icons/ui/icons8-shutdown-50.png
Binary file added icons/ui/icons8-switch-100.png
Binary file added icons/ui/icons8-switch-50.png
Binary file added icons/ui/icons8-unicast-100.png
Binary file added icons/ui/icons8-unicast-50.png
Binary file added icons/ui/icons8-usb-2-100.png
Binary file added icons/ui/icons8-usb-2-50.png
Binary file added icons/ui/icons8-usb-connector-100.png
Binary file added icons/ui/icons8-usb-connector-50.png
Binary file added icons/ui/icons8-user-groups-100.png
Binary file added icons/ui/icons8-user-groups-50.png
Binary file added icons/ui/icons8-vpn-100.png
Binary file added icons/ui/icons8-vpn-50.png
Binary file added icons/ui/icons8-wi-fi-100.png
Binary file added icons/ui/icons8-wi-fi-50.png
Binary file added icons/ui/icons8-windows-95-100.png
Binary file added icons/ui/icons8-windows-95-50.png
Binary file added icons/ui/icons8-wired-network-100.png
Binary file added icons/ui/icons8-wired-network-50.png
Binary file added icons/ui/icons8-work-boot-100.png
Binary file added icons/ui/icons8-work-boot-50.png
65 changes: 65 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,71 @@
"version": "0.1.0",
"license": "GPL-3.0",
"type": "module",
"exports": {
"./ui/finder": "./src/ui/finder-window.ts",
"./ui/menu-bar-track": "./src/ui/menu-bar-track.ts",
"./ui/finder-host": "./src/ui/finder-host.ts",
"./ui/finder-sidebar": "./src/ui/finder-sidebar.ts",
"./ui/extension-editor": "./src/ui/extension-editor-dialog.ts",
"./ui/styles": "./src/ui/styles/tokens.css",
"./ui/login": "./src/ui/login-dialog.ts",
"./ui/alert": "./src/ui/alert-dialog.ts",
"./ui/name-conflict": "./src/ui/name-conflict-dialog.ts",
"./ui/get-info": "./src/ui/get-info-window.ts",
"./ui/resource-explorer": "./src/ui/resource-fork-explorer.ts",
"./ui/win-resource-explorer": "./src/ui/win-resource-explorer.ts",
"./finder": "./src/finder/index.ts",
"./finder/api": "./src/finder/api.ts",
"./finder/api-catalog": "./src/finder/api-catalog.ts",
"./finder/afp-finder-api": "./src/finder/afp-finder-api.ts",
"./finder/afp-finder-host": "./src/finder/afp-finder-host.ts",
"./finder/bind-catalog": "./src/finder/bind-catalog.ts",
"./finder/progress": "./src/finder/progress.ts",
"./finder/types": "./src/finder/types.ts",
"./protocol/afp": "./src/protocol/afp/constants.ts",
"./protocol/asp": "./src/protocol/asp.ts",
"./protocol/atp": "./src/protocol/atp.ts",
"./protocol/ddp": "./src/protocol/ddp.ts",
"./protocol/nbp": "./src/protocol/nbp.ts",
"./protocol/llap": "./src/protocol/llap.ts",
"./protocol/abp": "./src/protocol/abp.ts",
"./protocol/macroman": "./src/protocol/macroman.ts",
"./protocol/binary": "./src/protocol/binary.ts",
"./protocol/host-filename": "./src/protocol/host-filename.ts",
"./protocol/crc16": "./src/protocol/crc16.ts",
"./protocol/crc32": "./src/protocol/crc32.ts",
"./services/afp-client": "./src/services/afp-client/client.ts",
"./services/afp-client/commands": "./src/services/afp-client/commands.ts",
"./services/afp-server": "./src/services/afp-server/server.ts",
"./services/asp-client": "./src/services/asp-client.ts",
"./services/atp-client": "./src/services/atp-client.ts",
"./services/atp-server": "./src/services/atp-server.ts",
"./services/nbp": "./src/services/nbp.ts",
"./transport/tashtalk": "./src/transport/tashtalk.ts",
"./transport/webserial": "./src/transport/webserial.ts",
"./net/stack": "./src/net/stack.ts",
"./fs/catalog": "./src/fs/virtual-fs.ts",
"./fs/virtual-fs": "./src/fs/virtual-fs.ts",
"./fs/remote-vfs": "./src/fs/remote-vfs.ts",
"./fs/catalog-caps": "./src/fs/catalog-caps.ts",
"./fs/volume-chrome": "./src/fs/volume-chrome.ts",
"./fs/capability-catalog": "./src/fs/capability-catalog.ts",
"./fs/extension-map": "./src/fs/extension-map.ts",
"./fs/extension-map-netatalk": "./src/fs/extension-map-netatalk.ts",
"./fs/codecs": "./src/fs/codecs.ts",
"./fs/expand": "./src/fs/expand-incoming.ts",
"./fs/stuffit": "./src/fs/stuffit.ts",
"./fs/binhex": "./src/fs/binhex.ts",
"./fs/macbinary": "./src/fs/macbinary.ts",
"./fs/zip": "./src/fs/zip.ts",
"./fs/mac-file": "./src/fs/mac-file.ts",
"./fs/finder-info": "./src/fs/finder-info.ts",
"./fs/name-conflict": "./src/fs/name-conflict.ts",
"./fs/winicon": "./src/fs/winicon/index.ts",
"./fs/resource-fork": "./src/fs/resource-fork.ts",
"./fs/resource-compress": "./src/fs/resource-compress.ts",
"./fs/appledouble": "./src/fs/appledouble.ts"
},
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
Expand Down
185 changes: 185 additions & 0 deletions src/finder/afp-finder-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import type { CatalogWithBackend, FinderAPI } from './api';
import type { FinderNodeDto, FinderSessionDto, OpProgress, CrossTransferRequest, CnidNodeDto } from './types';
import { bindCatalog } from './bind-catalog';
import { copyBetweenCatalogs, expandOnCatalog, moveBetweenCatalogs } from './catalog-copy';
import type { Catalog, VNode } from '../fs/virtual-fs';
import type { NodeRef } from '../fs/catalog-caps';

const LOCAL_SESSION = 'local';

/**
* In-browser FinderAPI over VirtualFS / RemoteVfs. Copy, move, and expand stay
* in the client (TashTalk owns the AFP session). Same-server files use FPCopyFile.
*/
export class AfpFinderAPI implements FinderAPI {
readonly backendId = 'afp';
private readonly catalogs = new Map<string, Catalog>();

bindLocal(vfs: Catalog): CatalogWithBackend {
return this.register(LOCAL_SESSION, vfs);
}

bindRemote(sessionId: string, cat: Catalog): CatalogWithBackend {
return this.register(sessionId, cat);
}

unbind(sessionId: string): void {
this.catalogs.delete(sessionId);
}

localCatalog(): CatalogWithBackend | undefined {
const cat = this.catalogs.get(LOCAL_SESSION);
return cat ? bindCatalog(cat, this, LOCAL_SESSION) : undefined;
}

register(sessionId: string, cat: Catalog): CatalogWithBackend {
this.catalogs.set(sessionId, cat);
return bindCatalog(cat, this, sessionId);
}

openCatalog(session: FinderSessionDto): Catalog {
const cat = this.catalogs.get(session.sessionId);
if (!cat) throw new Error(`no catalog for session ${session.sessionId}`);
return bindCatalog(cat, this, session.sessionId);
}

async getNode(sessionId: string, ref: NodeRef): Promise<FinderNodeDto> {
const node = await this.catalogFor(sessionId).get(ref);
if (!node) throw new Error('not found');
return this.toNode(node);
}
async children(sessionId: string, parent: NodeRef): Promise<FinderNodeDto[]> {
return (await this.catalogFor(sessionId).children(parent)).map((n) => this.toNode(n));
}
async lookup(sessionId: string, parent: NodeRef, name: string): Promise<FinderNodeDto | null> {
const node = await this.catalogFor(sessionId).lookup(parent, name);
return node ? this.toNode(node) : null;
}
async mkdir(sessionId: string, parent: NodeRef, name: string): Promise<FinderNodeDto> {
return this.toNode(await this.catalogFor(sessionId).mkdir(parent, name));
}
async create(
sessionId: string,
parent: NodeRef,
name: string,
body?: { data?: Uint8Array; resource?: Uint8Array; finderInfo?: Uint8Array },
): Promise<FinderNodeDto> {
return this.toNode(
await this.catalogFor(sessionId).createFile(
parent,
name,
body?.data ?? new Uint8Array(),
body?.resource ?? new Uint8Array(),
body?.finderInfo,
),
);
}
async rename(sessionId: string, ref: NodeRef, name: string): Promise<void> {
await this.catalogFor(sessionId).rename(ref, name);
}
async move(sessionId: string, ref: NodeRef, parent: NodeRef): Promise<void> {
await this.catalogFor(sessionId).move(ref, parent);
}
async remove(sessionId: string, ref: NodeRef): Promise<void> {
await this.catalogFor(sessionId).remove(ref);
}
async readFork(sessionId: string, ref: NodeRef, resource: boolean): Promise<Uint8Array> {
const node = await this.catalogFor(sessionId).ensureContent(ref);
if (!node) throw new Error('not found');
return resource ? node.resource : node.data;
}
async writeFork(sessionId: string, ref: NodeRef, resource: boolean, _off: number, data: Uint8Array): Promise<void> {
const cat = this.catalogFor(sessionId);
const node = await cat.ensureContent(ref);
if (!node || node.isDir) throw new Error('not found');
if (resource) node.resource = data;
else node.data = data;
await cat.put(node);
}
async writeFinderInfo(sessionId: string, ref: NodeRef, finderInfo: Uint8Array): Promise<void> {
const cat = this.catalogFor(sessionId);
const node = await cat.get(ref);
if (!node) throw new Error('not found');
node.finderInfo = finderInfo;
await cat.put(node);
}
async writeAttrs(sessionId: string, ref: NodeRef, patch: Record<string, boolean>): Promise<void> {
const cat = this.catalogFor(sessionId);
if (cat.setAttrs) {
await cat.setAttrs(ref, patch);
return;
}
throw new Error('setAttrs not supported');
}
async resolvePath(sessionId: string, path: string): Promise<FinderNodeDto | null> {
const node = await this.catalogFor(sessionId).resolvePath(path);
return node ? this.toNode(node) : null;
}
async pathOf(sessionId: string, ref: NodeRef): Promise<string> {
return this.catalogFor(sessionId).pathOf(ref);
}

copy(req: CrossTransferRequest, signal?: AbortSignal): AsyncIterable<OpProgress> {
return this.runCopy(req, signal);
}
moveAcross(req: CrossTransferRequest, signal?: AbortSignal): AsyncIterable<OpProgress> {
return this.runMove(req, signal);
}
expand(sessionId: string, ref: NodeRef, signal?: AbortSignal): AsyncIterable<OpProgress> {
return expandOnCatalog(this.catalogFor(sessionId), ref, signal);
}

private async *runCopy(req: CrossTransferRequest, signal?: AbortSignal): AsyncGenerator<OpProgress> {
yield* copyBetweenCatalogs(this.catalogFor(req.srcSession), this.catalogFor(req.destSession), req, signal);
yield { phase: 'copying', destName: req.destName, destParentId: req.destParentId, done: true };
}

private async *runMove(req: CrossTransferRequest, signal?: AbortSignal): AsyncGenerator<OpProgress> {
yield* moveBetweenCatalogs(this.catalogFor(req.srcSession), this.catalogFor(req.destSession), req, signal);
yield { phase: 'moving', destName: req.destName, destParentId: req.destParentId, done: true };
}

private catalogFor(sessionId: string): Catalog {
const cat = this.catalogs.get(sessionId);
if (!cat) throw new Error(sessionId === LOCAL_SESSION ? 'no local catalog' : 'no AFP session');
return cat;
}

private toNode(node: VNode): FinderNodeDto {
if (node.addr === 'path') {
return {
addr: 'path',
path: node.path,
parentPath: node.parentPath,
name: node.name,
isDir: node.isDir,
dataBytes: node.dataBytes ?? node.data.length,
resourceBytes: node.resourceBytes ?? node.resource.length,
createDate: node.createDate,
modDate: node.modDate,
accessDate: node.accessDate,
backupDate: node.backupDate,
shortName: node.shortName,
mediumName: node.mediumName,
attrs: node.attrs,
};
}
const cnid: CnidNodeDto & FinderNodeDto = {
addr: 'cnid',
id: node.id,
parentId: node.parentId,
name: node.name,
isDir: node.isDir,
dataBytes: node.dataBytes ?? node.data.length,
resourceBytes: node.resourceBytes ?? node.resource.length,
createDate: node.createDate,
modDate: node.modDate,
accessDate: node.accessDate,
backupDate: node.backupDate,
shortName: node.shortName,
mediumName: node.mediumName,
attrs: node.attrs,
};
return cnid;
}
}
Loading
Loading