@hoardodile/host 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/archive-api.test.ts +156 -0
- package/src/nested-view.ts +15 -4
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/activation.ts","../src/errors.ts","../src/archive/format.ts","../src/archive/name-decode.ts","../src/archive/zip-entries.ts","../src/archive/7z.ts","../src/archive/extract.ts","../src/archive/materialize.ts","../src/archive/nested-entry.ts","../src/archive/extract-archive.ts","../src/archive/nested-cd-cache.ts","../src/archive/pack.ts","../src/hash.ts","../src/nested-view.ts","../src/probe/image.ts","../src/probe/sniff.ts","../src/hoard/order-manifest.ts","../src/directory-container.ts","../src/directory-api.ts","../src/api.ts","../src/capability-guard.ts","../src/dev-runner.ts","../src/discovery.ts","../src/install-api.ts","../src/hooks.ts","../src/sandbox/call-timers.ts","../src/sandbox/protocol.ts","../src/sandbox/sandboxed-plugin.ts","../src/sandbox/host.ts","../src/seed.ts","../src/loader.ts","../src/probe-cache.ts"],"names":["sep","join","existsSync","mkdir","dirname","writeFile","pipeline","createWriteStream","stat","rename","rm","resolve","randomUUID","createReadStream","readFile","buffer","fileTypeFromName","readdir","normalize","PLUGIN_READ_FILE_MAX_BYTES","size","require","readdirSync","pluginManifestSchema","pluginAssetError","unavailableAsset","spawn","tmpdir","readFileSync","createHash"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0CO,SAAS,uBACf,IAAA,EACmB;AACnB,EAAA,eAAe,YACd,KAAA,EACiC;AAIjC,IAAA,OAAO,OAAA,CAAQ,GAAA;AAAA,MACd,KAAA,CAAM,GAAA,CAAI,OAAO,SAAA,KAAc;AAC9B,QAAA,MAAM,MAAA,GAAS,MAAM,cAAA,CAAe,IAAA,CAAK,SAAS,SAAS,CAAA;AAC3D,QAAA,OAAO;AAAA,UACN,IAAI,SAAA,CAAU,EAAA;AAAA,UACd,UAAU,SAAA,CAAU,QAAA;AAAA,UACpB,SAAS,SAAA,CAAU,OAAA;AAAA,UACnB,UAAU,SAAA,CAAU,QAAA;AAAA,UACpB,QAAQ,SAAA,CAAU,MAAA;AAAA,UAClB,OAAO,SAAA,CAAU,KAAA;AAAA,UACjB,OAAA,EAAS,KAAA;AAAA,UACT,OAAA,EAAS,UAAU,MAAA,KAAW,SAAA;AAAA,UAC9B,GAAA,EAAK,UAAU,MAAA,KAAW,KAAA;AAAA,UAC1B,MAAA;AAAA,UACA,UAAU,SAAA,CAAU;AAAA,SACrB;AAAA,MACD,CAAC;AAAA,KACF;AAAA,EACD;AAEA,EAAA,SAAS,qBACR,OAAA,EACwB;AACxB,IAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MAC1B,IAAI,CAAA,CAAE,EAAA;AAAA,MACN,UAAU,CAAA,CAAE,QAAA;AAAA,MACZ,SAAS,CAAA,CAAE,OAAA;AAAA,MACX,UAAU,CAAA,CAAE,QAAA;AAAA,MACZ,QAAQ,CAAA,CAAE,MAAA;AAAA,MACV,OAAO,CAAA,CAAE,KAAA;AAAA,MACT,OAAA,EAAS,IAAA;AAAA,MACT,OAAA,EAAS,KAAA;AAAA,MACT,GAAA,EAAK,KAAA;AAAA,MACL,MAAA,EAAQ,mBAAA,CAAoB,CAAC,4BAA4B,CAAC;AAAA,KAC3D,CAAE,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,EAAE,aAAa,oBAAA,EAAqB;AAC5C;AAEA,eAAe,cAAA,CACd,SACA,SAAA,EAC4B;AAC5B,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,SAAA,CAAU,QAAA,EAAU,SAAS,CAAA;AACrD,EAAA,IAAI,CAAC,UAAA,CAAW,UAAU,CAAA,EAAG;AAC5B,IAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,oBAAA,EAAuB,SAAA,CAAU,EAAE,CAAA,wBAAA,CAA0B,CAAA;AAC1E,IAAA,OAAO,mBAAA,CAAoB,CAAC,mBAAmB,CAAC,CAAA;AAAA,EACjD;AAKA,EAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,UAAA,CAAW;AAAA,IACvC,IAAI,SAAA,CAAU,EAAA;AAAA,IACd,QAAA,EAAU,UAAA;AAAA,IACV,OAAO,SAAA,CAAU,OAAA;AAAA;AAAA,IAEjB,WAAA,EAAa,UAAU,QAAA,CAAS;AAAA,GAChC,CAAA;AACD,EAAA,IAAI,WAAW,MAAA,EAAW;AACzB,IAAA,OAAO,mBAAA,CAAoB,CAAC,oBAAoB,CAAC,CAAA;AAAA,EAClD;AACA,EAAA,OAAO,MAAA;AACR;;;ACtFO,IAAM,WAAA,GAAN,cAA0B,KAAA,CAAM;AAAA,EAC7B,IAAA;AAAA,EACA,IAAA;AAAA,EACA,OAAA;AAAA,EAET,WAAA,CACC,IAAA,EACA,IAAA,EACA,OAAA,EACA,OAAA,EACC;AACD,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAI,OAAA,KAAY,MAAA,EAAW,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,EAC3C;AACD;AAGO,SAAS,OAAA,CACf,IAAA,EACA,OAAA,EACA,OAAA,EACc;AACd,EAAA,OAAO,IAAI,WAAA,CAAY,YAAA,EAAc,IAAA,EAAM,SAAS,OAAO,CAAA;AAC5D;AAGO,SAAS,QAAA,CACf,IAAA,EACA,OAAA,EACA,OAAA,EACc;AACd,EAAA,OAAO,IAAI,WAAA,CAAY,UAAA,EAAY,IAAA,EAAM,SAAS,OAAO,CAAA;AAC1D;AAGO,SAAS,QAAA,CACf,IAAA,EACA,OAAA,EACA,OAAA,EACc;AACd,EAAA,OAAO,IAAI,WAAA,CAAY,WAAA,EAAa,IAAA,EAAM,SAAS,OAAO,CAAA;AAC3D;;;AChEO,IAAM,kBAAA,GAAqB,IAAA;AAKlC,SAAS,QAAA,CAAS,MAAkB,KAAA,EAAmC;AACtE,EAAA,IAAI,IAAA,CAAK,UAAA,GAAa,KAAA,CAAM,MAAA,EAAQ,OAAO,KAAA;AAC3C,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACtC,IAAA,IAAI,KAAK,CAAC,CAAA,KAAM,KAAA,CAAM,CAAC,GAAG,OAAO,KAAA;AAAA,EAClC;AACA,EAAA,OAAO,IAAA;AACR;AAQO,SAAS,qBACf,IAAA,EAC8B;AAC9B,EAAA,IAAI,QAAA,CAAS,MAAM,CAAC,EAAA,EAAM,IAAM,CAAA,EAAM,CAAI,CAAC,CAAA,EAAG,OAAO,KAAA;AACrD,EAAA,IAAI,QAAA,CAAS,MAAM,CAAC,EAAA,EAAM,IAAM,CAAA,EAAM,CAAI,CAAC,CAAA,EAAG,OAAO,KAAA;AACrD,EAAA,IAAI,IAAA,CAAK,cAAc,GAAA,EAAK;AAC3B,IAAA,MAAM,QAAQ,MAAA,CAAO,YAAA;AAAA,MACpB,KAAK,GAAG,CAAA;AAAA,MACR,KAAK,GAAG,CAAA;AAAA,MACR,KAAK,GAAG,CAAA;AAAA,MACR,KAAK,GAAG,CAAA;AAAA,MACR,KAAK,GAAG;AAAA,KACT;AACA,IAAA,IAAI,KAAA,KAAU,SAAS,OAAO,KAAA;AAAA,EAC/B;AACA,EAAA,IAAI,QAAA,CAAS,IAAA,EAAM,CAAC,EAAA,EAAM,GAAA,EAAM,GAAA,EAAM,GAAA,EAAM,EAAA,EAAM,EAAI,CAAC,CAAA,EAAG,OAAO,IAAA;AACjE,EAAA,IAAI,QAAA,CAAS,IAAA,EAAM,CAAC,EAAA,EAAM,EAAA,EAAM,GAAA,EAAM,EAAA,EAAM,EAAA,EAAM,CAAI,CAAC,CAAA,EAAG,OAAO,KAAA;AACjE,EAAA,IAAI,QAAA,CAAS,IAAA,EAAM,CAAC,GAAA,EAAM,EAAA,EAAM,GAAA,EAAM,EAAA,EAAM,EAAA,EAAM,CAAI,CAAC,CAAA,EAAG,OAAO,IAAA;AACjE,EAAA,IAAI,QAAA,CAAS,MAAM,CAAC,EAAA,EAAM,KAAM,CAAI,CAAC,GAAG,OAAO,MAAA;AAC/C,EAAA,OAAO,MAAA;AACR;;;ACjCA,IAAM,QAAQ,MAAA,CAAO,YAAA;AAAA,EACpB,CAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AA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","file":"index.js","sourcesContent":["import { existsSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport type {\n\tFoundPlugin,\n\tMissingPlugin,\n\tPluginDefinition,\n\tPluginRegistryEntry,\n} from \"./api-types.ts\"\nimport { createFailingPlugin } from \"./define-plugin.ts\"\nimport type { PluginSandbox } from \"./sandbox/host.ts\"\n\n/**\n * Plugin activation: turns discovered plugin candidates (found on disk\n * or recorded as missing) into ready {@link PluginRegistryEntry}s by\n * loading their `main.js` into the sandbox. Loads run in parallel —\n * sequential worker spawns would multiply the boot cost on slow or\n * antivirus-scanned disks.\n */\nexport type PluginActivation = {\n\t/** Load every found plugin's `main.js` into the sandbox and return the full registry entries. */\n\treadonly activateAll: (\n\t\tfound: readonly FoundPlugin[],\n\t) => Promise<PluginRegistryEntry[]>\n\t/**\n\t * Build registry entries for plugins whose directory is missing, each\n\t * carrying a failing definition so callers can still render them as\n\t * broken in the UI.\n\t */\n\treadonly createFailingEntries: (\n\t\tmissing: readonly MissingPlugin[],\n\t) => PluginRegistryEntry[]\n}\n\nexport type PluginActivationDeps = {\n\treadonly sandbox: PluginSandbox\n}\n\n/**\n * Create the activation pipeline. The sandbox decides worker lifetimes:\n * enabled plugins keep their worker alive, disabled ones only probe the\n * hook list and respawn lazily if a bound resource still invokes a hook.\n */\nexport function createPluginActivation(\n\tdeps: PluginActivationDeps,\n): PluginActivation {\n\tasync function activateAll(\n\t\tfound: readonly FoundPlugin[],\n\t): Promise<PluginRegistryEntry[]> {\n\t\t// Load plugin bundles in parallel — sequential worker spawns multiply\n\t\t// the boot cost on slow or antivirus-scanned disks. Array order is\n\t\t// kept by `map`, and `loadAll` re-sorts by priority afterwards anyway.\n\t\treturn Promise.all(\n\t\t\tfound.map(async (candidate) => {\n\t\t\t\tconst plugin = await loadDiskPlugin(deps.sandbox, candidate)\n\t\t\t\treturn {\n\t\t\t\t\tid: candidate.id,\n\t\t\t\t\tmanifest: candidate.manifest,\n\t\t\t\t\tenabled: candidate.enabled,\n\t\t\t\t\tpriority: candidate.priority,\n\t\t\t\t\tpinned: candidate.pinned,\n\t\t\t\t\tcolor: candidate.color,\n\t\t\t\t\tmissing: false,\n\t\t\t\t\tbuiltin: candidate.source === \"builtin\",\n\t\t\t\t\tdev: candidate.source === \"dev\",\n\t\t\t\t\tplugin,\n\t\t\t\t\tdiskPath: candidate.diskPath,\n\t\t\t\t}\n\t\t\t}),\n\t\t)\n\t}\n\n\tfunction createFailingEntries(\n\t\tmissing: readonly MissingPlugin[],\n\t): PluginRegistryEntry[] {\n\t\treturn missing.map((m) => ({\n\t\t\tid: m.id,\n\t\t\tmanifest: m.manifest,\n\t\t\tenabled: m.enabled,\n\t\t\tpriority: m.priority,\n\t\t\tpinned: m.pinned,\n\t\t\tcolor: m.color,\n\t\t\tmissing: true,\n\t\t\tbuiltin: false,\n\t\t\tdev: false,\n\t\t\tplugin: createFailingPlugin([\"plugin directory not found\"]),\n\t\t}))\n\t}\n\n\treturn { activateAll, createFailingEntries }\n}\n\nasync function loadDiskPlugin(\n\tsandbox: PluginSandbox,\n\tcandidate: FoundPlugin,\n): Promise<PluginDefinition> {\n\tconst mainJsPath = join(candidate.diskPath, \"main.js\")\n\tif (!existsSync(mainJsPath)) {\n\t\tconsole.warn(`[plugin-activation] ${candidate.id}: no main.js, not loaded`)\n\t\treturn createFailingPlugin([\"main.js not found\"])\n\t}\n\n\t// Enabled plugins keep their worker alive; disabled ones only probe the\n\t// hook list — their worker respawns lazily if a bound resource still\n\t// invokes a hook.\n\tconst plugin = await sandbox.loadPlugin({\n\t\tid: candidate.id,\n\t\tmainPath: mainJsPath,\n\t\teager: candidate.enabled,\n\t\t// The sandbox enforces manifest-gated API surface (container).\n\t\tpermissions: candidate.manifest.permissions,\n\t})\n\tif (plugin === undefined) {\n\t\treturn createFailingPlugin([\"main.js load error\"])\n\t}\n\treturn plugin\n}\n","/**\n * Host-local domain error. Mirrors the wire-format taxonomy of the app's\n * domain errors (`@hoardodile/shared`) so errors raised here keep their\n * code/kind/details across the package boundary — the server's error\n * translation recognizes this shape structurally via `isDomainError`.\n *\n * The host deliberately keeps its dependency graph minimal (consts +\n * sdk-types only) and does not depend on `@hoardodile/shared`; the\n * mirror is kept in sync by the bridge test in\n * `apps/server/src/infra/host-errors.test.ts`.\n */\nexport const domainErrorCodes = [\n\t\"NOT_FOUND\",\n\t\"CONFLICT\",\n\t\"VALIDATION\",\n\t\"UNAUTHORIZED\",\n\t\"FORBIDDEN\",\n\t\"RATE_LIMITED\",\n\t\"UNSUPPORTED\",\n\t\"INTERNAL\",\n] as const\n\nexport type DomainErrorCode = (typeof domainErrorCodes)[number]\n\n/**\n * Host-local domain error carrying a wire-compatible code/kind/details\n * triple. Thrown by host services (containers, loaders, hooks) so the\n * server can translate them into its own domain errors verbatim.\n */\nexport class DomainError extends Error {\n\treadonly code: DomainErrorCode\n\treadonly kind: string\n\treadonly details?: Readonly<Record<string, unknown>>\n\n\tconstructor(\n\t\tcode: DomainErrorCode,\n\t\tkind: string,\n\t\tmessage: string,\n\t\tdetails?: Readonly<Record<string, unknown>>,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = \"DomainError\"\n\t\tthis.code = code\n\t\tthis.kind = kind\n\t\tif (details !== undefined) this.details = details\n\t}\n}\n\n/** Convenience constructor for `VALIDATION` errors. */\nexport function invalid(\n\tkind: string,\n\tmessage: string,\n\tdetails?: Readonly<Record<string, unknown>>,\n): DomainError {\n\treturn new DomainError(\"VALIDATION\", kind, message, details)\n}\n\n/** Convenience constructor for `CONFLICT` errors. */\nexport function conflict(\n\tkind: string,\n\tmessage: string,\n\tdetails?: Readonly<Record<string, unknown>>,\n): DomainError {\n\treturn new DomainError(\"CONFLICT\", kind, message, details)\n}\n\n/** Convenience constructor for `NOT_FOUND` errors. */\nexport function notFound(\n\tkind: string,\n\tmessage: string,\n\tdetails?: Readonly<Record<string, unknown>>,\n): DomainError {\n\treturn new DomainError(\"NOT_FOUND\", kind, message, details)\n}\n","/**\n * Container format identification from magic bytes. The single source of\n * truth for \"what archive format is this\" — the engine matrix in\n * `./index.ts` maps each format to its list/extract/address\n * capabilities, and every dispatch (yauzl vs 7-Zip, nested resolver vs\n * whole-archive) keys off {@link sniffContainerFormat}.\n */\n\n/** Byte window read from an entry to decide container-ness. */\nexport const SNIFF_WINDOW_BYTES = 4100\n\nexport type ContainerFormat = \"zip\" | \"tar\" | \"7z\" | \"rar\" | \"xz\" | \"gzip\"\n\n/** True when `head` starts with the given magic byte sequence. */\nfunction hasMagic(head: Uint8Array, magic: readonly number[]): boolean {\n\tif (head.byteLength < magic.length) return false\n\tfor (let i = 0; i < magic.length; i++) {\n\t\tif (head[i] !== magic[i]) return false\n\t}\n\treturn true\n}\n\n/**\n * Identify a container format from its leading bytes. Zip is addressable\n * as a nested container (`outer!inner`); the rest (tar/7z/rar/xz/gzip)\n * are only extractable as whole archives (see `extract-archive.ts` and\n * `extract.ts`).\n */\nexport function sniffContainerFormat(\n\thead: Uint8Array,\n): ContainerFormat | undefined {\n\tif (hasMagic(head, [0x50, 0x4b, 0x03, 0x04])) return \"zip\"\n\tif (hasMagic(head, [0x50, 0x4b, 0x05, 0x06])) return \"zip\"\n\tif (head.byteLength >= 262) {\n\t\tconst magic = String.fromCharCode(\n\t\t\thead[257]!,\n\t\t\thead[258]!,\n\t\t\thead[259]!,\n\t\t\thead[260]!,\n\t\t\thead[261]!,\n\t\t)\n\t\tif (magic === \"ustar\") return \"tar\"\n\t}\n\tif (hasMagic(head, [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c])) return \"7z\"\n\tif (hasMagic(head, [0x52, 0x61, 0x72, 0x21, 0x1a, 0x07])) return \"rar\"\n\tif (hasMagic(head, [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00])) return \"xz\"\n\tif (hasMagic(head, [0x1f, 0x8b, 0x08])) return \"gzip\"\n\treturn undefined\n}\n","/**\n * Zip entry name decoding for the 7-Zip paths. The zip APPNOTE says\n * names without the UTF-8 flag (general-purpose bit 11) are encoded in\n * the OEM code page; in practice that means cp437 (the same assumption\n * yauzl makes on the native zip path). 7-Zip cannot be told to decode\n * them this way — its `-mcp` switch does not exist in 26.02 and its\n * POSIX builds write the raw name bytes verbatim — so the host decodes\n * the bytes itself, from the listing output and again from the on-disk\n * names after extraction, keeping the two consistent on every platform.\n *\n * ASCII and valid UTF-8 names pass through untouched; anything else is\n * re-interpreted as cp437. The table matches yauzl's, so the native and\n * 7-Zip paths agree on legacy names.\n */\n\nconst CP437 = String.fromCharCode(\n\t0,\n\t9786,\n\t9787,\n\t9829,\n\t9830,\n\t9827,\n\t9824,\n\t8226,\n\t9688,\n\t9675,\n\t9689,\n\t9794,\n\t9792,\n\t9834,\n\t9835,\n\t9788,\n\t9658,\n\t9668,\n\t8597,\n\t8252,\n\t182,\n\t167,\n\t9644,\n\t8616,\n\t8593,\n\t8595,\n\t8594,\n\t8592,\n\t8735,\n\t8596,\n\t9650,\n\t9660,\n\t32,\n\t33,\n\t34,\n\t35,\n\t36,\n\t37,\n\t38,\n\t39,\n\t40,\n\t41,\n\t42,\n\t43,\n\t44,\n\t45,\n\t46,\n\t47,\n\t48,\n\t49,\n\t50,\n\t51,\n\t52,\n\t53,\n\t54,\n\t55,\n\t56,\n\t57,\n\t58,\n\t59,\n\t60,\n\t61,\n\t62,\n\t63,\n\t64,\n\t65,\n\t66,\n\t67,\n\t68,\n\t69,\n\t70,\n\t71,\n\t72,\n\t73,\n\t74,\n\t75,\n\t76,\n\t77,\n\t78,\n\t79,\n\t80,\n\t81,\n\t82,\n\t83,\n\t84,\n\t85,\n\t86,\n\t87,\n\t88,\n\t89,\n\t90,\n\t91,\n\t92,\n\t93,\n\t94,\n\t95,\n\t96,\n\t97,\n\t98,\n\t99,\n\t100,\n\t101,\n\t102,\n\t103,\n\t104,\n\t105,\n\t106,\n\t107,\n\t108,\n\t109,\n\t110,\n\t111,\n\t112,\n\t113,\n\t114,\n\t115,\n\t116,\n\t117,\n\t118,\n\t119,\n\t120,\n\t121,\n\t122,\n\t123,\n\t124,\n\t125,\n\t126,\n\t8962,\n\t199,\n\t252,\n\t233,\n\t226,\n\t228,\n\t224,\n\t229,\n\t231,\n\t234,\n\t235,\n\t232,\n\t239,\n\t238,\n\t236,\n\t196,\n\t197,\n\t201,\n\t230,\n\t198,\n\t244,\n\t246,\n\t242,\n\t251,\n\t249,\n\t255,\n\t214,\n\t220,\n\t162,\n\t163,\n\t165,\n\t8359,\n\t402,\n\t225,\n\t237,\n\t243,\n\t250,\n\t241,\n\t209,\n\t170,\n\t186,\n\t191,\n\t8976,\n\t172,\n\t189,\n\t188,\n\t161,\n\t171,\n\t187,\n\t9617,\n\t9618,\n\t9619,\n\t9474,\n\t9508,\n\t9569,\n\t9570,\n\t9558,\n\t9557,\n\t9571,\n\t9553,\n\t9559,\n\t9565,\n\t9564,\n\t9563,\n\t9488,\n\t9492,\n\t9524,\n\t9516,\n\t9500,\n\t9472,\n\t9532,\n\t9566,\n\t9567,\n\t9562,\n\t9556,\n\t9577,\n\t9574,\n\t9568,\n\t9552,\n\t9580,\n\t9575,\n\t9576,\n\t9572,\n\t9573,\n\t9561,\n\t9560,\n\t9554,\n\t9555,\n\t9579,\n\t9578,\n\t9496,\n\t9484,\n\t9608,\n\t9604,\n\t9612,\n\t9616,\n\t9600,\n\t945,\n\t223,\n\t915,\n\t960,\n\t931,\n\t963,\n\t181,\n\t964,\n\t934,\n\t920,\n\t937,\n\t948,\n\t8734,\n\t966,\n\t949,\n\t8745,\n\t8801,\n\t177,\n\t8805,\n\t8804,\n\t8992,\n\t8993,\n\t247,\n\t8776,\n\t176,\n\t8729,\n\t183,\n\t8730,\n\t8319,\n\t178,\n\t9632,\n\t160,\n)\n\n/**\n * Decode one name byte string: valid UTF-8 stays as-is, anything else\n * falls back to cp437 (the zip legacy-encoding convention).\n */\nexport function decodeLegacyZipName(bytes: Uint8Array): string {\n\ttry {\n\t\treturn new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes)\n\t} catch {\n\t\tlet out = \"\"\n\t\tfor (const b of bytes) out += CP437[b] ?? \"\\uFFFD\"\n\t\treturn out\n\t}\n}\n","import { createReadStream, type ReadStream } from \"node:fs\"\nimport { stat } from \"node:fs/promises\"\nimport { Readable } from \"node:stream\"\nimport { buffer } from \"node:stream/consumers\"\nimport { createInflateRaw } from \"node:zlib\"\nimport yauzl from \"yauzl\"\nimport { invalid } from \"../errors.ts\"\n\n/**\n * Central directory entry shape exposed for the read-path cache. Mirrors\n * the fields yauzl surfaces, plus `dataOffset` resolved from the local\n * header. `dataOffset` and `dataSize` together pinpoint the entry's raw\n * bytes inside the zip file; for STORED entries those bytes are the file\n * itself and can be streamed via {@link streamRange} with a\n * `[start, end]` window.\n *\n * The central directory is parsed by yauzl (the only listing engine —\n * the hand-rolled fast parser was removed; ZIP64 falls out naturally).\n */\nexport type ZipEntry = {\n\treadonly name: string\n\treadonly compressionMethod: number\n\t/** General-purpose bit 0: the entry data is encrypted. */\n\treadonly encrypted: boolean\n\treadonly uncompressedSize: number\n\treadonly compressedSize: number\n\treadonly crc32: number\n\treadonly localHeaderOffset: number\n\t/**\n\t * Absolute byte offset of the entry's raw data inside the zip file.\n\t * Computed as `localHeaderOffset + 30 + nameLen + extraLen` where\n\t * nameLen and extraLen are read from the local file header (not the\n\t * central directory) because they can differ between the two.\n\t */\n\treadonly dataOffset: number\n\treadonly dataSize: number\n\treadonly modifiedAt: number\n}\n\n/**\n * Random-access byte view over a zip archive. File-backed archives\n * (uploaded archive files and CLI fixtures) read via the filesystem;\n * nested archives — a zip entry inside the container — implement the same\n * range reads over the outer entry's inflated bytes. Everything in this\n * module that needs the archive's bytes takes a {@link ArchiveSource},\n * so the container and the plugin API resolve nested zips through one\n * code path.\n */\nexport type ArchiveSource = {\n\t/** Total archive size in bytes. */\n\treadonly size: number\n\t/** Read the inclusive byte range `[start, end]`. */\n\treadonly readRange: (start: number, end: number) => Promise<Buffer>\n}\n\nconst RANGE_READ_CHUNK_BYTES = 256 * 1024\n\n/** Wrap an on-disk zip in a {@link ArchiveSource}. */\nexport async function createFileArchiveSource(\n\tzipPath: string,\n): Promise<ArchiveSource> {\n\tconst info = await stat(zipPath)\n\treturn {\n\t\tsize: info.size,\n\t\treadRange: (start, end) => readFileRange(zipPath, start, end),\n\t}\n}\n\n/**\n * Read the central directory of the zip behind `source`, plus enough of\n * each local file header to compute `dataOffset`. Returns a flat list of\n * records ready for caching. Directory entries (names ending in `/`) are\n * excluded.\n *\n * The listing goes through yauzl over the {@link ArchiveSource} (via its\n * random-access-reader bridge); yauzl handles ZIP64 and validates entry\n * names while parsing. The `dataOffset` for byte-range access is\n * resolved from each local file header afterwards — callers that only\n * need names and sizes (e.g. the legacy-name decode truth) pass\n * `{ dataOffsets: false }` so the per-entry random reads are skipped and\n * `dataOffset` is left at 0.\n *\n * @throws DomainError `resource.archive_open_failed` when the zip cannot\n * be parsed.\n */\nexport async function listZipEntriesFromSource(\n\tsource: ArchiveSource,\n\topts: { readonly dataOffsets?: boolean } = {},\n): Promise<readonly ZipEntry[]> {\n\tconst zipfile = await openZipFromSource(source)\n\tconst records: ZipEntry[] = []\n\ttry {\n\t\tfor await (const entry of zipfile.eachEntry()) {\n\t\t\tif (entry.fileName.endsWith(\"/\")) continue\n\t\t\tconst dataOffset =\n\t\t\t\topts.dataOffsets === false\n\t\t\t\t\t? 0\n\t\t\t\t\t: await resolveDataOffsetFromSource(\n\t\t\t\t\t\t\tsource,\n\t\t\t\t\t\t\tentry.relativeOffsetOfLocalHeader,\n\t\t\t\t\t\t)\n\t\t\trecords.push({\n\t\t\t\tname: entry.fileName,\n\t\t\t\tcompressionMethod: entry.compressionMethod,\n\t\t\t\tencrypted: entry.isEncrypted(),\n\t\t\t\tuncompressedSize: entry.uncompressedSize,\n\t\t\t\tcompressedSize: entry.compressedSize,\n\t\t\t\tcrc32: entry.crc32,\n\t\t\t\tlocalHeaderOffset: entry.relativeOffsetOfLocalHeader,\n\t\t\t\tdataOffset,\n\t\t\t\tdataSize: entry.compressedSize,\n\t\t\t\tmodifiedAt: entry.getLastModDate().getTime(),\n\t\t\t})\n\t\t}\n\t} catch (err) {\n\t\t// yauzl surfaces malformed central directories as plain errors\n\t\t// (e.g. encrypted stored entries with inconsistent sizes) — map\n\t\t// them onto the documented taxonomy.\n\t\tthrow invalid(\n\t\t\t\"resource.archive_open_failed\",\n\t\t\terr instanceof Error ? err.message : \"could not read archive entries\",\n\t\t\t{ size: source.size },\n\t\t)\n\t} finally {\n\t\tzipfile.close()\n\t}\n\treturn records\n}\n\n/** Read the central directory of the zip at `zipPath`. */\nexport async function listZipEntries(\n\tzipPath: string,\n\topts: { readonly dataOffsets?: boolean } = {},\n): Promise<readonly ZipEntry[]> {\n\treturn listZipEntriesFromSource(await createFileArchiveSource(zipPath), opts)\n}\n\n/**\n * Stream one entry's *decompressed* bytes from `source`. STORED entries\n * map to a byte-range stream of the raw data; DEFLATE entries inflate on\n * the fly. Encrypted entries and other compression methods reject with a\n * clear reason (there is no key to decrypt with). Callers are responsible\n * for consuming or destroying the stream (a dangling stream leaks an\n * inflate context).\n */\nexport function openZipEntryStream(\n\tsource: ArchiveSource,\n\trecord: ZipEntry,\n): Readable {\n\tif (record.encrypted) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_open_failed\",\n\t\t\t`zip entry \"${record.name}\" is encrypted — password-protected archives are not supported`,\n\t\t\t{ name: record.name },\n\t\t)\n\t}\n\tswitch (record.compressionMethod) {\n\t\tcase 0:\n\t\t\treturn streamRange(\n\t\t\t\tsource,\n\t\t\t\trecord.dataOffset,\n\t\t\t\trecord.dataOffset + record.dataSize - 1,\n\t\t\t)\n\t\tcase 8: {\n\t\t\tconst raw = streamRange(\n\t\t\t\tsource,\n\t\t\t\trecord.dataOffset,\n\t\t\t\trecord.dataOffset + record.dataSize - 1,\n\t\t\t)\n\t\t\tconst inflater = createInflateRaw()\n\t\t\traw.on(\"error\", (err) => inflater.destroy(err))\n\t\t\treturn raw.pipe(inflater)\n\t\t}\n\t\tdefault:\n\t\t\tthrow invalid(\n\t\t\t\t\"resource.archive_open_failed\",\n\t\t\t\t`unsupported zip entry compression method ${record.compressionMethod} for \"${record.name}\"`,\n\t\t\t\t{ method: record.compressionMethod, name: record.name },\n\t\t\t)\n\t}\n}\n\n/** Stream the inclusive byte range `[start, end]` in bounded chunks. */\nexport function streamRange(\n\tsource: ArchiveSource,\n\tstart: number,\n\tend: number,\n): Readable {\n\treturn Readable.from(\n\t\t(async function* rangeChunks() {\n\t\t\tlet pos = start\n\t\t\twhile (pos <= end) {\n\t\t\t\tconst to = Math.min(end, pos + RANGE_READ_CHUNK_BYTES - 1)\n\t\t\t\tconst chunk = await source.readRange(pos, to)\n\t\t\t\tif (chunk.length === 0) return\n\t\t\t\tyield chunk\n\t\t\t\tpos += chunk.length\n\t\t\t}\n\t\t})(),\n\t)\n}\n\n/** Stream `zipPath` bytes in the range `[start, end]` (inclusive). */ export function readZipRange(\n\tzipPath: string,\n\tstart: number,\n\tend: number,\n): ReadStream {\n\treturn createReadStream(zipPath, { start, end })\n}\n\n/**\n * Read an inclusive byte range from `path`. Uses a read stream so offsets\n * beyond 2 GiB work on Node versions where `fs.read` position must fit in\n * Int32 (older releases assert instead of throwing).\n */\nexport async function readFileRange(\n\tpath: string,\n\tstart: number,\n\tend: number,\n): Promise<Buffer> {\n\tif (\n\t\t!Number.isFinite(start) ||\n\t\t!Number.isFinite(end) ||\n\t\t!Number.isInteger(start) ||\n\t\t!Number.isInteger(end) ||\n\t\tstart < 0 ||\n\t\tend < start\n\t) {\n\t\tthrow invalid(\n\t\t\t\"resource.file_read_failed\",\n\t\t\t`invalid byte range ${start}..${end}`,\n\t\t\t{ path, start, end },\n\t\t)\n\t}\n\tconst length = end - start + 1\n\tif (length <= 0) return Buffer.alloc(0)\n\treturn buffer(readZipRange(path, start, end))\n}\n\nfunction openZipFromSource(source: ArchiveSource): Promise<yauzl.ZipFile> {\n\tconst reader = new RangeReader(source)\n\treturn yauzl\n\t\t.fromRandomAccessReaderPromise(reader, source.size, {\n\t\t\tlazyEntries: true,\n\t\t\tdecodeStrings: true,\n\t\t})\n\t\t.catch((err: unknown) => {\n\t\t\treader.close(() => {})\n\t\t\tthrow invalid(\n\t\t\t\t\"resource.archive_open_failed\",\n\t\t\t\terr instanceof Error ? err.message : \"could not open archive\",\n\t\t\t\t{ size: source.size },\n\t\t\t)\n\t\t})\n}\n\n/**\n * yauzl's random-access-reader bridge. yauzl 3.x reads through\n * `createReadStream`/`read`, which are built on `_readStreamForRange`;\n * a subclass therefore only supplies range streams (chunked, so entries\n * are never buffered whole) plus `_close` (see yauzl's README).\n */\nclass RangeReader extends yauzl.RandomAccessReader {\n\t// Explicit field + assignment: this package ships source to Node's\n\t// type-stripping loader (no build step), which rejects parameter\n\t// properties.\n\tprivate readonly source: ArchiveSource\n\n\tconstructor(source: ArchiveSource) {\n\t\tsuper()\n\t\tthis.source = source\n\t}\n\n\toverride _readStreamForRange(start: number, end: number): Readable {\n\t\t// yauzl's `end` is exclusive (it asserts exactly `end - start`\n\t\t// bytes); `streamRange` takes an inclusive end.\n\t\treturn streamRange(this.source, start, end - 1)\n\t}\n\n\t// yauzl's runtime contract calls `_close` even though the shipped\n\t// types only expose the public `close` — no `override` here.\n\t_close(callback: (err?: Error | null) => void): void {\n\t\tcallback(null)\n\t}\n}\n\nasync function resolveDataOffsetFromSource(\n\tsource: ArchiveSource,\n\tlocalHeaderOffset: number,\n): Promise<number> {\n\tif (localHeaderOffset + 30 > source.size) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_open_failed\",\n\t\t\t`truncated local file header at offset ${localHeaderOffset}`,\n\t\t\t{ localHeaderOffset },\n\t\t)\n\t}\n\tconst head = await source.readRange(localHeaderOffset, localHeaderOffset + 29)\n\tif (head.length < 30) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_open_failed\",\n\t\t\t`truncated local file header at offset ${localHeaderOffset}`,\n\t\t\t{ localHeaderOffset },\n\t\t)\n\t}\n\tconst sig = head.readUInt32LE(0)\n\tif (sig !== 0x04034b50) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_open_failed\",\n\t\t\t`bad local file header signature at offset ${localHeaderOffset}`,\n\t\t\t{ localHeaderOffset, sig: sig.toString(16) },\n\t\t)\n\t}\n\tconst nameLen = head.readUInt16LE(26)\n\tconst extraLen = head.readUInt16LE(28)\n\treturn localHeaderOffset + 30 + nameLen + extraLen\n}\n\n/**\n * yauzl validates entry names while parsing the central directory and\n * rejects traversal/absolute names with a plain `Error`. Reclassify\n * those as the documented `resource.archive_invalid_entry` domain error\n * so every caller observes the same error taxonomy.\n */\nexport function normalizeZipError(err: unknown): unknown {\n\tif (!(err instanceof Error)) return err\n\tconst message = err.message\n\tif (\n\t\tmessage.startsWith(\"invalid relative path: \") ||\n\t\tmessage.startsWith(\"absolute path: \") ||\n\t\tmessage.startsWith(\"invalid characters in fileName: \")\n\t) {\n\t\treturn invalid(\"resource.archive_invalid_entry\", message, {})\n\t}\n\treturn err\n}\n","import { spawn } from \"node:child_process\"\nimport { createRequire } from \"node:module\"\nimport { invalid } from \"../errors.ts\"\nimport { SNIFF_WINDOW_BYTES, sniffContainerFormat } from \"./format.ts\"\nimport { decodeLegacyZipName } from \"./name-decode.ts\"\nimport { readFileRange } from \"./zip-entries.ts\"\n\n/**\n * 7-Zip process wrapper for the extra archive formats (.7z/.rar/.xz) and\n * — since the whole-archive extraction switch — zip/tar/gzip as well.\n * The binary comes from the optional `@hoardodile/7z-bin` package\n * (downloaded at install time); when it is absent every function here\n * rejects with a clear \"unavailable\" error and callers degrade to\n * zip-only support via yauzl.\n *\n * Only whole-archive operations run through 7-Zip: listing with\n * `l -slt -ba` (fixed English fields, locale-independent) and full\n * extraction with `x -y`. Random-access entry streaming stays on the\n * native zip path, which 7-Zip cannot express.\n *\n * Legacy zip names (no UTF-8 flag) are not re-encoded by 7-Zip on every\n * platform — its POSIX builds pass the raw bytes through — so the host\n * decodes them as cp437 itself: the listing's `Path` values are decoded\n * byte-wise for zip archives (see {@link decodeLegacyZipName}), and the\n * extraction sites rename the on-disk files to match (see\n * `normalizeExtractedTree` in extract.ts). Listing output is still\n * forced to UTF-8 (`-sccUTF-8`) so 7-Zip's Windows build emits decoded\n * names as valid UTF-8 rather than console-codepage bytes.\n */\n\nconst requireCjs = createRequire(import.meta.url)\n\nexport const SEVEN_ZIP_PATH_ENV = \"7Z_BIN_PATH\"\n\nexport type SevenZipEntry = {\n\treadonly name: string\n\treadonly sizeBytes: number\n\treadonly folder: boolean\n\treadonly encrypted: boolean\n}\n\n/**\n * Resolve the absolute path of the 7-Zip binary, or `undefined` when the\n * optional package is not installed (install failed / skipped platform).\n */\nexport function resolveSevenZipPath(): string | undefined {\n\tconst fromEnv = process.env[SEVEN_ZIP_PATH_ENV]\n\tif (fromEnv !== undefined && fromEnv.length > 0) return fromEnv\n\ttry {\n\t\tconst mod: unknown = requireCjs(\"@hoardodile/7z-bin\")\n\t\tif (typeof mod === \"string\" && mod.length > 0) return mod\n\t\treturn undefined\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nfunction sevenZipUnavailable(): Error {\n\treturn invalid(\n\t\t\"resource.archive_open_failed\",\n\t\t\"7-Zip is not installed — install @hoardodile/7z-bin or set 7Z_BIN_PATH to enable archive extraction\",\n\t\t{},\n\t)\n}\n\n/**\n * Run 7-Zip with `args`; rejects with a domain error on non-zero exit,\n * timeout, or spawn failure (missing binary, wrong-arch executable).\n * stdout is streamed and collected in memory (no maxBuffer ceiling —\n * `l -slt` on very large archives can exceed a fixed cap); `stderr` is\n * included in the message so operators can see the native error instead\n * of a bare exit code.\n */\nfunction run7z(\n\tbin: string,\n\targs: readonly string[],\n\ttimeoutMs: number,\n): Promise<{ readonly stdout: Buffer }> {\n\treturn new Promise((resolveDone, rejectDone) => {\n\t\tconst child = spawn(bin, [...args], {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\twindowsHide: true,\n\t\t})\n\t\tconst stdout: Buffer[] = []\n\t\tconst stderr: Buffer[] = []\n\t\tlet settled = false\n\n\t\tchild.stdout.on(\"data\", (chunk: Buffer) => stdout.push(chunk))\n\t\tchild.stderr.on(\"data\", (chunk: Buffer) => stderr.push(chunk))\n\n\t\tconst fail = (err: Error): void => {\n\t\t\tif (settled) return\n\t\t\tsettled = true\n\t\t\trejectDone(err)\n\t\t}\n\t\tconst timer = setTimeout(() => {\n\t\t\tchild.kill()\n\t\t\tfail(\n\t\t\t\tinvalid(\n\t\t\t\t\t\"resource.archive_open_failed\",\n\t\t\t\t\t`7-Zip timed out after ${timeoutMs}ms`,\n\t\t\t\t\t{ timeoutMs },\n\t\t\t\t),\n\t\t\t)\n\t\t}, timeoutMs)\n\t\ttimer.unref()\n\n\t\tchild.on(\"error\", (err) => {\n\t\t\tfail(\n\t\t\t\tinvalid(\n\t\t\t\t\t\"resource.archive_open_failed\",\n\t\t\t\t\t`could not start 7-Zip: ${err.message}`,\n\t\t\t\t\t{},\n\t\t\t\t),\n\t\t\t)\n\t\t})\n\t\tchild.on(\"close\", (code, signal) => {\n\t\t\tclearTimeout(timer)\n\t\t\tif (settled) return\n\t\t\tif (code === 0) {\n\t\t\t\tsettled = true\n\t\t\t\tresolveDone({ stdout: Buffer.concat(stdout) })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst detail =\n\t\t\t\tsignal !== null ? `killed by ${signal}` : `exit ${code ?? \"?\"}`\n\t\t\tconst tail = Buffer.concat(stderr)\n\t\t\t\t.toString(\"utf8\")\n\t\t\t\t.trim()\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.at(-1)\n\t\t\tfail(\n\t\t\t\tinvalid(\n\t\t\t\t\t\"resource.archive_open_failed\",\n\t\t\t\t\t`7-Zip ${detail}${tail !== undefined && tail.length > 0 ? `: ${tail}` : \"\"}`,\n\t\t\t\t\t{ code },\n\t\t\t\t),\n\t\t\t)\n\t\t})\n\t})\n}\n\n/** True when `encrypted` field from `-slt` marks an encrypted entry. */\nfunction isEncryptedField(value: string): boolean {\n\treturn value === \"+\"\n}\n\n/** Parse `l -slt -ba` output into per-entry records (blank-line separated). */\nfunction parseSltListing(\n\tstdout: Buffer,\n\tlegacyNames: boolean,\n): SevenZipEntry[] {\n\tconst entries: SevenZipEntry[] = []\n\tlet name: string | undefined\n\tlet sizeBytes = 0\n\tlet folder = false\n\tlet encrypted = false\n\t// latin1 round-trips every output byte losslessly, so the raw `Path`\n\t// value survives for the cp437 decode below (the UTF-8 pass-through\n\t// would replace invalid bytes with U+FFFD first).\n\tfor (const line of stdout.toString(\"latin1\").split(/\\r?\\n/)) {\n\t\tif (line.length === 0) {\n\t\t\tif (name !== undefined) {\n\t\t\t\tentries.push({ name, sizeBytes, folder, encrypted })\n\t\t\t\tname = undefined\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tconst sep = line.indexOf(\" = \")\n\t\tif (sep < 0) continue\n\t\tconst key = line.slice(0, sep)\n\t\tif (key === \"Path\") {\n\t\t\t// 7za prints backslash separators on Windows; normalize to\n\t\t\t// the forward-slash convention used across the archive runtime.\n\t\t\tconst raw = Buffer.from(line.slice(sep + 3), \"latin1\")\n\t\t\tconst decoded = legacyNames\n\t\t\t\t? decodeLegacyZipName(raw)\n\t\t\t\t: raw.toString(\"utf8\")\n\t\t\tname = decoded.replace(/\\\\/g, \"/\")\n\t\t\tcontinue\n\t\t}\n\t\tconst value = line.slice(sep + 3)\n\t\tswitch (key) {\n\t\t\tcase \"Size\":\n\t\t\t\tsizeBytes = Number.parseInt(value, 10) || 0\n\t\t\t\tbreak\n\t\t\tcase \"Attributes\":\n\t\t\t\t// Directories carry `D` (there is no Folder field in -slt).\n\t\t\t\tfolder = value.includes(\"D\")\n\t\t\t\tbreak\n\t\t\tcase \"Encrypted\":\n\t\t\t\tencrypted = isEncryptedField(value)\n\t\t\t\tbreak\n\t\t}\n\t}\n\tif (name !== undefined) {\n\t\tentries.push({ name, sizeBytes, folder, encrypted })\n\t}\n\treturn entries\n}\n\n/**\n * Per-call options for the 7-Zip wrappers.\n */\nexport type SevenZipRunOptions = {\n\treadonly timeoutMs?: number\n}\n\n/**\n * True when `archivePath`'s magic bytes say zip. Read once per call (a\n * 4KB window) to decide whether names decode as legacy cp437 (see\n * {@link decodeLegacyZipName}) — callers never need to know the format.\n * An unreadable header degrades to \"not zip\" (no cp437 fallback), which\n * is the safe direction for every other format.\n */\nasync function isZipArchive(archivePath: string): Promise<boolean> {\n\tconst head = await readFileRange(\n\t\tarchivePath,\n\t\t0,\n\t\tSNIFF_WINDOW_BYTES - 1,\n\t).catch(() => undefined)\n\tif (head === undefined || head.length === 0) return false\n\treturn sniffContainerFormat(head) === \"zip\"\n}\n\n/**\n * List the entries of `archivePath` (zip/tar/7z/rar/xz/gzip). Rejects\n * when the archive is encrypted (7-Zip would prompt for a password on a\n * TTY). Listing output is forced to UTF-8 (`-sccUTF-8`); legacy zip\n * names decode as cp437 byte-wise (see {@link decodeLegacyZipName}).\n */\nexport async function listSevenZipEntries(\n\tarchivePath: string,\n\topts: SevenZipRunOptions = {},\n): Promise<SevenZipEntry[]> {\n\tconst bin = resolveSevenZipPath()\n\tif (bin === undefined) throw sevenZipUnavailable()\n\tconst args = [\"l\", \"-slt\", \"-ba\", \"-sccUTF-8\"]\n\tconst legacyNames = await isZipArchive(archivePath)\n\targs.push(archivePath)\n\tconst { stdout } = await run7z(bin, args, opts.timeoutMs ?? 120_000)\n\treturn parseSltListing(stdout, legacyNames)\n}\n\n/**\n * Extract `archivePath` in full into `destDir`. Callers must pre-validate\n * budgets from {@link listSevenZipEntries} — 7-Zip has no byte-level\n * progress or per-entry abort, so the listing is the enforcement point.\n * Legacy zip names land on disk as 7-Zip wrote them (raw bytes on POSIX);\n * callers fix them up afterwards via `normalizeExtractedTree` (extract.ts).\n */\nexport async function extractSevenZipInto(\n\tarchivePath: string,\n\tdestDir: string,\n\topts: SevenZipRunOptions = {},\n): Promise<void> {\n\tconst bin = resolveSevenZipPath()\n\tif (bin === undefined) throw sevenZipUnavailable()\n\tconst args = [\"x\", archivePath, `-o${destDir}`, \"-y\", \"-bd\"]\n\tawait run7z(bin, args, opts.timeoutMs ?? 10 * 60_000)\n}\n","import { randomUUID } from \"node:crypto\"\nimport { createWriteStream, existsSync } from \"node:fs\"\nimport {\n\tchmod,\n\tlstat,\n\tmkdir,\n\treaddir,\n\trename,\n\trm,\n\twriteFile,\n} from \"node:fs/promises\"\nimport { tmpdir } from \"node:os\"\nimport { dirname, join, normalize, resolve, sep } from \"node:path\"\nimport { Transform } from \"node:stream\"\nimport { pipeline } from \"node:stream/promises\"\nimport yauzl, { type Entry, type ZipFile } from \"yauzl\"\nimport { invalid } from \"../errors.ts\"\nimport {\n\textractSevenZipInto,\n\tlistSevenZipEntries,\n\tresolveSevenZipPath,\n} from \"./7z.ts\"\nimport {\n\ttype ContainerFormat,\n\tSNIFF_WINDOW_BYTES,\n\tsniffContainerFormat,\n} from \"./format.ts\"\nimport { listingBudgetExceeded } from \"./listing.ts\"\nimport { decodeLegacyZipName } from \"./name-decode.ts\"\nimport { listZipEntries, normalizeZipError } from \"./zip-entries.ts\"\n\n/**\n * Whole-archive extraction. Every supported format (zip, tar, 7z, rar,\n * xz, gzip) extracts through 7-Zip with listing-validated budgets; when\n * the optional 7-Zip binary is absent, zip falls back to a yauzl\n * streaming path with a per-byte budget counter. Used by plugin\n * installation, folder-import extraction and the plugin extract API —\n * the workflows that need a directory layout.\n */\n\n/**\n * Extraction progress reporter for {@link extractArchiveInto}. The\n * 7-Zip path reports the two phases only (listing pre-check, done);\n * the yauzl fallback reports per-entry events.\n */\nexport type ZipExtractReporter = (event: ZipExtractReport) => void\n\nexport type ZipExtractReport =\n\t| {\n\t\t\treadonly phase: \"started\"\n\t\t\treadonly totalEntries: number\n\t\t\treadonly totalBytes: number\n\t }\n\t| {\n\t\t\treadonly phase: \"entry\"\n\t\t\treadonly entriesDone: number\n\t\t\treadonly totalEntries: number\n\t\t\treadonly bytesDone: number\n\t\t\treadonly totalBytes: number\n\t }\n\t| { readonly phase: \"done\" }\n\nexport type ExtractArchiveOptions = {\n\t/** Cumulative uncompressed byte budget for the whole archive. */\n\treadonly maxBytes: number\n\t/** Optional entry-count budget (enforced on the listing). */\n\treadonly maxEntries?: number\n\treadonly onProgress?: ZipExtractReporter\n\t/**\n\t * Optional format allow-list (e.g. `[\"zip\"]` for plugin installs).\n\t * An archive sniffed outside the list is rejected up front with\n\t * `resource.archive_format_not_allowed` — project-internal channels\n\t * that only ever produce one format stop admitting the others\n\t * instead of opening every codec to untrusted input.\n\t */\n\treadonly formats?: readonly ContainerFormat[]\n}\n\n/**\n * Stream an archive into `destDir`, format-agnostic: every supported\n * format (zip/tar/7z/rar/xz/gzip) extracts through 7-Zip after\n * validating budgets, encryption and paths from its listing, then\n * re-verifies the extracted tree (size + symlink scan). Without a\n * 7-Zip binary, zip extracts through yauzl with a per-byte budget\n * counter; the other formats error with \"7-Zip is not installed\".\n * Used by plugin installation and folder-import extraction (the\n * workflows that need a directory layout).\n *\n * Refuses any entry whose normalised path escapes `destDir` (zip-slip)\n * or contains an absolute path / drive letter. Defends against zip\n * bombs via `maxBytes` (and `maxEntries` when provided).\n *\n * @throws DomainError VALIDATION when the archive is malformed,\n * contains an unsafe entry, exceeds the budgets, is encrypted, or is\n * not a supported archive at all.\n */\nexport async function extractArchiveInto(\n\tsource: NodeJS.ReadableStream,\n\tdestDir: string,\n\topts: ExtractArchiveOptions,\n): Promise<void> {\n\tconst buffer = await readToBuffer(source)\n\tconst format = sniffContainerFormat(buffer.subarray(0, SNIFF_WINDOW_BYTES))\n\tif (format === undefined) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_open_failed\",\n\t\t\t\"not a supported archive (zip/tar/7z/rar/xz/gzip)\",\n\t\t\t{},\n\t\t)\n\t}\n\tif (opts.formats !== undefined && !opts.formats.includes(format)) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_format_not_allowed\",\n\t\t\t`archive format ${format} is not allowed here (allowed: ${opts.formats.join(\", \")})`,\n\t\t\t{ allowed: opts.formats },\n\t\t)\n\t}\n\tif (format === \"zip\" && resolveSevenZipPath() === undefined) {\n\t\treturn extractZipBuffer(buffer, destDir, opts)\n\t}\n\treturn extractViaSevenZip(buffer, destDir, opts, format)\n}\n\n/**\n * Zip branch when 7-Zip is absent: yauzl streaming with per-byte budget\n * and entry progress. Kept so zip imports work on installs where the\n * optional binary failed to download.\n */\nasync function extractZipBuffer(\n\tbuffer: Buffer,\n\tdestDir: string,\n\topts: ExtractArchiveOptions,\n): Promise<void> {\n\tconst { maxBytes, maxEntries, onProgress } = opts\n\tconst zipfile = await openZipFromBuffer(buffer)\n\tif (maxEntries !== undefined && zipfile.entryCount > maxEntries) {\n\t\tzipfile.close()\n\t\tthrow invalid(\n\t\t\t\"resource.archive_too_large\",\n\t\t\t`archive has ${zipfile.entryCount} entries, exceeding the limit of ${maxEntries}`,\n\t\t\t{ maxEntries },\n\t\t)\n\t}\n\tconst root = resolve(destDir)\n\tconst budget = { remaining: maxBytes, max: maxBytes }\n\tconst totalEntries = zipfile.entryCount\n\tconst counters = { entriesDone: 0, bytesDone: 0, totalBytes: 0 }\n\tif (onProgress !== undefined) {\n\t\tonProgress({ phase: \"started\", totalEntries, totalBytes: 0 })\n\t}\n\ttry {\n\t\tawait new Promise<void>((resolveDone, rejectDone) => {\n\t\t\tzipfile.on(\"error\", (err: unknown) => rejectDone(normalizeZipError(err)))\n\t\t\tzipfile.on(\"end\", resolveDone)\n\t\t\tzipfile.on(\"entry\", (entry: Entry) => {\n\t\t\t\thandleExtractEntry(zipfile, entry, root, budget).then(() => {\n\t\t\t\t\tcounters.entriesDone += 1\n\t\t\t\t\tcounters.bytesDone += entry.uncompressedSize\n\t\t\t\t\tcounters.totalBytes += entry.uncompressedSize\n\t\t\t\t\tif (onProgress !== undefined) {\n\t\t\t\t\t\tonProgress({\n\t\t\t\t\t\t\tphase: \"entry\",\n\t\t\t\t\t\t\tentriesDone: counters.entriesDone,\n\t\t\t\t\t\t\ttotalEntries,\n\t\t\t\t\t\t\tbytesDone: counters.bytesDone,\n\t\t\t\t\t\t\ttotalBytes: counters.totalBytes,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tzipfile.readEntry()\n\t\t\t\t}, rejectDone)\n\t\t\t})\n\t\t\tzipfile.readEntry()\n\t\t})\n\t} finally {\n\t\tzipfile.close()\n\t}\n\tif (onProgress !== undefined) {\n\t\tonProgress({ phase: \"done\" })\n\t}\n}\n\n/**\n * 7-Zip branch for every supported format: write the bytes to a temp\n * file (7-Zip needs a real path), validate budgets, encryption and\n * entry paths from the listing, extract in one pass, then verify the\n * extracted tree (size re-check + symlink refusal).\n */\nasync function extractViaSevenZip(\n\tbuffer: Buffer,\n\tdestDir: string,\n\topts: ExtractArchiveOptions,\n\tformat: ContainerFormat,\n): Promise<void> {\n\tconst { maxBytes, maxEntries, onProgress } = opts\n\tconst tempPath = join(\n\t\ttmpdir(),\n\t\t`hoardodile-7z-${process.pid}-${randomUUID().slice(0, 8)}`,\n\t)\n\tawait writeFile(tempPath, buffer)\n\ttry {\n\t\tconst entries = await listSevenZipEntries(tempPath)\n\t\tconst files = entries.filter((e) => !e.folder)\n\t\tconst exceeded = listingBudgetExceeded(files, { maxBytes, maxEntries })\n\t\tif (exceeded?.kind === \"bytes\") {\n\t\t\tthrow invalid(\n\t\t\t\t\"resource.archive_too_large\",\n\t\t\t\t`archive extracts to more than ${maxBytes} bytes`,\n\t\t\t\t{ maxExtractedBytes: maxBytes },\n\t\t\t)\n\t\t}\n\t\tif (exceeded?.kind === \"entries\") {\n\t\t\tthrow invalid(\n\t\t\t\t\"resource.archive_too_large\",\n\t\t\t\t`archive has ${files.length} entries, exceeding the limit of ${maxEntries}`,\n\t\t\t\t{ maxEntries },\n\t\t\t)\n\t\t}\n\t\tif (entries.some((e) => e.encrypted)) {\n\t\t\tthrow invalid(\n\t\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t\t\"archive is password-protected — encrypted archives are not supported\",\n\t\t\t\t{},\n\t\t\t)\n\t\t}\n\t\tfor (const entry of files) assertSafeEntryPath(entry.name)\n\t\tconst root = resolve(destDir)\n\t\tawait mkdir(root, { recursive: true })\n\t\tconst totalBytes = files.reduce((acc, e) => acc + e.sizeBytes, 0)\n\t\tonProgress?.({ phase: \"started\", totalEntries: files.length, totalBytes })\n\t\tawait extractSevenZipInto(tempPath, root)\n\t\t// 7-Zip writes legacy zip names verbatim on POSIX and restores\n\t\t// entries' mode bits, which can strip the app's own access; fix\n\t\t// both up before the tree is re-walked or probed below. The\n\t\t// decoded names of the archive itself are the ground truth the\n\t\t// legacy-rename pass matches against (see `renameLegacyZipNames`)\n\t\t// — 7-Zip's text listing loses the invalid bytes on POSIX.\n\t\tconst decoded =\n\t\t\tformat === \"zip\"\n\t\t\t\t? await decodeZipNames(\n\t\t\t\t\t\ttempPath,\n\t\t\t\t\t\tfiles.map((e) => ({ name: e.name, sizeBytes: e.sizeBytes })),\n\t\t\t\t\t)\n\t\t\t\t: undefined\n\t\tawait normalizeExtractedTree(root, {\n\t\t\tlegacyZipNames: format === \"zip\",\n\t\t\texpectedNames: decoded?.paths,\n\t\t})\n\t\t// Post-hoc bomb re-check: the listing sizes are advisory; count\n\t\t// what actually landed (and refuse symlinks — 7-Zip creates them\n\t\t// from zip/tar entries carrying unix link modes).\n\t\tawait assertExtractedTree(root, maxBytes)\n\t\tonProgress?.({ phase: \"done\" })\n\t} finally {\n\t\tawait rm(tempPath, { force: true }).catch(() => {})\n\t}\n}\n\n/**\n * Walk an extracted tree verifying that every entry is a regular file\n * or directory (symlinks and special files are refused outright — a\n * malicious archive could otherwise plant a link out of the extraction\n * root) and that the total size stays within `maxBytes`.\n */\nexport async function assertExtractedTree(\n\troot: string,\n\tmaxBytes: number,\n): Promise<void> {\n\tlet total = 0\n\tasync function walk(dir: string): Promise<void> {\n\t\tconst entries = await readdir(dir, { withFileTypes: true })\n\t\tfor (const entry of entries) {\n\t\t\tconst full = join(dir, entry.name)\n\t\t\t// lstat, not dirent: on Windows, junction points (reparse\n\t\t\t// tags) are only reported as links by lstat.\n\t\t\tconst info = await lstat(full)\n\t\t\tif (info.isSymbolicLink()) {\n\t\t\t\tthrow invalid(\n\t\t\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t\t\t`extracted entry is a symlink: ${entry.name}`,\n\t\t\t\t\t{ name: entry.name },\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (info.isDirectory()) {\n\t\t\t\tawait walk(full)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!info.isFile()) {\n\t\t\t\tthrow invalid(\n\t\t\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t\t\t`extracted entry is not a regular file: ${entry.name}`,\n\t\t\t\t\t{ name: entry.name },\n\t\t\t\t)\n\t\t\t}\n\t\t\ttotal += info.size\n\t\t\tif (total > maxBytes) {\n\t\t\t\tthrow invalid(\n\t\t\t\t\t\"resource.archive_too_large\",\n\t\t\t\t\t`archive extracts to more than ${maxBytes} bytes`,\n\t\t\t\t\t{ maxExtractedBytes: maxBytes },\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\tawait walk(root)\n}\n\n/**\n * Post-extraction hygiene for a 7-Zip materialized tree, run before the\n * tree is re-walked by {@link assertExtractedTree} or probed for the\n * manifest. For zip archives, entries whose on-disk names are not valid\n * UTF-8 are renamed to their cp437-decoded form (7-Zip writes the raw\n * bytes verbatim on POSIX, and macOS stores those bytes `%XX`-escaped —\n * see {@link renameLegacyZipNames} for both shapes); every entry gets\n * owner permissions OR'd in so the app can always read, serve and clean\n * up what it extracted (zip/tar entries can carry mode bits that strip\n * owner access). Never follows symlinks — refusal stays with\n * {@link assertExtractedTree}. Windows is unaffected: names are already\n * decoded and mode bits are ignored there.\n */\nexport async function normalizeExtractedTree(\n\troot: string,\n\topts: {\n\t\treadonly legacyZipNames: boolean\n\t\t/**\n\t\t * Decoded listing names (forward-slash, relative) of the archive\n\t\t * being extracted. The disambiguator for the macOS `%XX`-escaped\n\t\t * name shape: an escaped name is valid UTF-8, so validity alone\n\t\t * cannot tell `caf%82.jpg` (a legacy cp437 `café.jpg` after\n\t\t * macOS escaping) apart from a file literally named\n\t\t * `report%82.jpg` — only the listing's decoded name settles it.\n\t\t */\n\t\treadonly expectedNames?: readonly string[]\n\t},\n): Promise<void> {\n\tif (opts.legacyZipNames) {\n\t\tconst expected = opts.expectedNames\n\t\t\t? new Set(opts.expectedNames.map((n) => n.replace(/\\/+$/, \"\")))\n\t\t\t: undefined\n\t\tawait renameLegacyZipNames(Buffer.from(root), root, root, expected)\n\t}\n\tawait makeReadable(root)\n}\n\n/**\n * Decoded names of a zip archive — the ground truth for legacy (cp437)\n * entry names. 7-Zip's text listing drops the bytes that are not valid\n * UTF-8 in the archive: its console conversion removes the escape points\n * on POSIX, so the reported name loses the character entirely. The zip\n * itself is authoritative, so names come from yauzl (`decodeStrings`\n * decodes non-UTF-8 names as cp437). Directory entries are absent from\n * that listing, so every ancestor directory of a file path is added to\n * `paths` for the rename pass. Falls back to the 7-Zip-listing names when\n * the zip cannot be parsed a second time — extraction then proceeds\n * exactly as before.\n */\nexport async function decodeZipNames(\n\tzipPath: string,\n\tfallback: readonly { readonly name: string; readonly sizeBytes: number }[],\n): Promise<{\n\treadonly files: readonly {\n\t\treadonly name: string\n\t\treadonly sizeBytes: number\n\t}[]\n\treadonly paths: readonly string[]\n}> {\n\ttry {\n\t\tconst records = await listZipEntries(zipPath, { dataOffsets: false })\n\t\tconst files = records.map((e) => ({\n\t\t\tname: e.name,\n\t\t\tsizeBytes: e.uncompressedSize,\n\t\t}))\n\t\tconst paths = new Set<string>()\n\t\tfor (const file of files) {\n\t\t\tconst segments = file.name.split(\"/\")\n\t\t\tfor (let i = 1; i < segments.length; i++) {\n\t\t\t\tpaths.add(segments.slice(0, i).join(\"/\"))\n\t\t\t}\n\t\t\tpaths.add(file.name)\n\t\t}\n\t\treturn { files, paths: [...paths] }\n\t} catch {\n\t\treturn { files: fallback, paths: fallback.map((e) => e.name) }\n\t}\n}\n\n/**\n * Rename every entry whose name does not match its decoded listing\n * form. Legacy zip names can land on disk in two shapes:\n *\n * - raw bytes, when the archive name is not valid UTF-8 (7-Zip's POSIX\n * and Windows builds pass/write them verbatim);\n * - `%XX`-escaped bytes, when macOS's UTF-8 filesystem layer stores the\n * raw names from the first shape (`XNU`'s `vfs_utfconv.c` converts an\n * illegal byte to `%` + two hex digits while normalizing);\n * - 7-Zip escape-plane characters (U+EF00+byte), the round-trip form\n * for an illegal byte written without the byte-level conversion.\n *\n * The last two are valid UTF-8 on disk and are reconciled against the\n * decoded names of the archive itself (see {@link decodeZipNames}) — the\n * only lossless source, since 7-Zip's text listing drops those bytes on\n * POSIX. A rename never clobbers an existing entry, and either escaped\n * shape only wins when the recovered name matches a decoded listing path\n * at the same position — a file genuinely named `report%82.jpg` (or with\n * a private-use character) therefore stays untouched.\n */\nasync function renameLegacyZipNames(\n\trawRoot: Buffer,\n\tdecodedRoot: string,\n\troot: string,\n\texpected: ReadonlySet<string> | undefined,\n): Promise<void> {\n\tconst entries = await readdir(rawRoot, {\n\t\twithFileTypes: true,\n\t\tencoding: \"buffer\",\n\t})\n\tfor (const entry of entries) {\n\t\tconst childRaw = Buffer.concat([rawRoot, Buffer.from(sep), entry.name])\n\t\tconst decoded = decodeLegacyZipName(entry.name)\n\t\tconst target = legacyRenameTarget(\n\t\t\tentry.name,\n\t\t\tdecoded,\n\t\t\tdecodedRoot,\n\t\t\troot,\n\t\t\texpected,\n\t\t)\n\t\tif (entry.isDirectory()) {\n\t\t\tif (target !== undefined && !existsSync(target)) {\n\t\t\t\tawait rename(childRaw, target)\n\t\t\t\tawait renameLegacyZipNames(Buffer.from(target), target, root, expected)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// Either no rename applies, or the decoded target already\n\t\t\t// exists (a real entry owns the decoded name) — the raw\n\t\t\t// directory itself stays, but its descendants must still be\n\t\t\t// renamed under the decoded prefix.\n\t\t\tawait renameLegacyZipNames(\n\t\t\t\tchildRaw,\n\t\t\t\ttarget ?? `${decodedRoot}/${decoded}`,\n\t\t\t\troot,\n\t\t\t\texpected,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\t\tif (target === undefined || existsSync(target)) continue\n\t\tawait rename(childRaw, target)\n\t}\n}\n\n/**\n * The decoded path an extracted entry should be renamed to, or\n * `undefined` when the on-disk name already satisfies the decoded\n * convention (or cannot be decided safely).\n */\nfunction legacyRenameTarget(\n\trawName: Buffer,\n\tdecoded: string,\n\tdecodedRoot: string,\n\troot: string,\n\texpected: ReadonlySet<string> | undefined,\n): string | undefined {\n\tconst utf8Name = rawName.toString(\"utf8\")\n\tif (decoded !== utf8Name) {\n\t\t// Raw bytes are not valid UTF-8: 7-Zip wrote the legacy name\n\t\t// verbatim and the fs layer stored it byte-for-byte (Linux,\n\t\t// Windows). Decode as cp437, matching the listing.\n\t\treturn `${decodedRoot}/${decoded}`\n\t}\n\t// The name IS valid UTF-8 — but the raw legacy bytes may have been\n\t// re-encoded on the way to disk (macOS `%XX` escapes, or 7-Zip's\n\t// escape-plane characters U+EF00+byte). Recover the original bytes and\n\t// re-decode; only the listing's blessing keeps a literal\n\t// `report%82.jpg` from being renamed to `reporté.jpg`.\n\tconst raw = recoverRawLegacyName(rawName)\n\tif (raw === undefined) return undefined\n\tconst recovered = decodeLegacyZipName(raw)\n\tif (recovered === utf8Name || expected === undefined) return undefined\n\tconst rel =\n\t\tdecodedRoot === root\n\t\t\t? recovered\n\t\t\t: `${decodedRoot.slice(root.length + 1)}/${recovered}`\n\treturn expected.has(rel) ? `${decodedRoot}/${recovered}` : undefined\n}\n\n/**\n * The original archive bytes of an on-disk name that carried a legacy\n * cp437 byte, and `undefined` when the name has no mangle at all. Two\n * transient shapes exist for a raw byte `b`:\n *\n * - `%XX` hex escapes: macOS's UTF-8 filesystem layer stores illegal\n * filename bytes that way (`XNU`'s `vfs_utfconv.c`);\n * - the escape-plane character U+EF00+b: 7-Zip's internal representation\n * for a byte it cannot show as UTF-8, written verbatim when the name\n * is encoded without the byte-level round-trip.\n *\n * Either shape maps back to exactly one byte per source byte.\n */\nfunction recoverRawLegacyName(name: Buffer): Buffer | undefined {\n\tconst escaped = unescapePercentEscapes(name)\n\tif (escaped !== undefined) return escaped\n\tconst text = name.toString(\"utf8\")\n\tconst out: number[] = []\n\tfor (const ch of text) {\n\t\tconst code = ch.codePointAt(0) ?? 0\n\t\tif (code >= 0xef80 && code <= 0xefff) {\n\t\t\tout.push(code - 0xef00)\n\t\t\tcontinue\n\t\t}\n\t\tif (code > 0x7f) return undefined // mixed with real text — not a mangle\n\t\tout.push(code)\n\t}\n\treturn out.length === text.length ? Buffer.from(out) : undefined\n}\n\n/**\n * Expand the `%XX` sequences a macOS UTF-8 filesystem writes for illegal\n * filename bytes back to their original byte values. Returns `undefined`\n * when the name contains no `%XX` sequence (nothing to recover). Only\n * sequences whose bytes are illegal as UTF-8 matter to the caller —\n * legitimate `%XX` substrings simply survive as decoded characters and\n * fail the listing match.\n */\nfunction unescapePercentEscapes(name: Buffer): Buffer | undefined {\n\tconst text = name.toString(\"latin1\")\n\tif (!text.includes(\"%\")) return undefined\n\tconst out: number[] = []\n\tlet changed = false\n\tfor (let i = 0; i < text.length; i++) {\n\t\tconst hex = i + 2 < text.length ? text.slice(i + 1, i + 3) : undefined\n\t\tif (text[i] === \"%\" && hex !== undefined && /^[0-9a-fA-F]{2}$/.test(hex)) {\n\t\t\tout.push(Number.parseInt(hex, 16))\n\t\t\tchanged = true\n\t\t\ti += 2\n\t\t\tcontinue\n\t\t}\n\t\tout.push(text.charCodeAt(i))\n\t}\n\treturn changed ? Buffer.from(out) : undefined\n}\n\n/**\n * OR owner permissions into every entry (directories rwx, files rw) so\n * extracted archives stay accessible to the app whatever mode bits the\n * entries carried. Symlinks and special files are skipped — they are\n * refused by {@link assertExtractedTree} afterwards and must never be\n * followed here.\n */\nasync function makeReadable(root: string): Promise<void> {\n\tconst entries = await readdir(root, { withFileTypes: true })\n\tfor (const entry of entries) {\n\t\tconst full = join(root, entry.name)\n\t\tif (entry.isDirectory()) {\n\t\t\tawait chmod(full, (await lstat(full)).mode | 0o700)\n\t\t\tawait makeReadable(full)\n\t\t\tcontinue\n\t\t}\n\t\tif (entry.isFile()) {\n\t\t\tawait chmod(full, (await lstat(full)).mode | 0o600)\n\t\t}\n\t}\n}\n\n/** Reject absolute paths and traversal segments (zip-slip guard). */\nfunction assertSafeEntryPath(rawName: string): void {\n\tif (\n\t\trawName.length === 0 ||\n\t\t/^([a-zA-Z]:)?[\\\\/]/.test(rawName) ||\n\t\trawName.split(\"/\").includes(\"..\")\n\t) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t`archive entry has an unsafe path: ${rawName}`,\n\t\t\t{ rawName },\n\t\t)\n\t}\n}\n\nasync function handleExtractEntry(\n\tzipfile: ZipFile,\n\tentry: Entry,\n\troot: string,\n\tbudget: { remaining: number; readonly max: number },\n): Promise<void> {\n\tconst safe = safeExtractEntryPath(entry.fileName, root)\n\tif (entry.fileName.endsWith(\"/\")) {\n\t\tawait mkdir(safe, { recursive: true })\n\t\treturn\n\t}\n\tawait mkdir(dirname(safe), { recursive: true })\n\tconst stream = await openZipEntryStream(zipfile, entry)\n\tawait pipeline(stream, makeByteBudgetCounter(budget), createWriteStream(safe))\n}\n\nfunction safeExtractEntryPath(rawName: string, root: string): string {\n\tif (rawName.length === 0) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t\"archive entry has empty name\",\n\t\t\t{ rawName },\n\t\t)\n\t}\n\tif (/^([a-zA-Z]:)?[\\\\/]/.test(rawName)) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t`archive entry has absolute path: ${rawName}`,\n\t\t\t{ rawName },\n\t\t)\n\t}\n\tconst normalised = normalize(rawName).replace(/\\\\/g, \"/\")\n\tif (normalised.startsWith(\"../\") || normalised === \"..\") {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t`archive entry escapes destination: ${rawName}`,\n\t\t\t{ rawName },\n\t\t)\n\t}\n\tconst candidate = resolve(root, normalised)\n\tif (candidate !== root && !candidate.startsWith(root + sep)) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t`archive entry escapes destination: ${rawName}`,\n\t\t\t{ rawName },\n\t\t)\n\t}\n\treturn candidate\n}\n\n/**\n * Transform that subtracts each chunk's size from a shared byte budget\n * and destroys itself with a VALIDATION error once the budget would go\n * negative. Used to defend against zip bombs across all entries in a\n * single archive on the yauzl fallback path (the 7-Zip path pre-checks\n * the listing and re-verifies the extracted tree instead).\n */\nfunction makeByteBudgetCounter(budget: {\n\tremaining: number\n\treadonly max: number\n}): Transform {\n\treturn new Transform({\n\t\ttransform(chunk, _enc, cb) {\n\t\t\tconst len = Buffer.isBuffer(chunk)\n\t\t\t\t? chunk.length\n\t\t\t\t: Buffer.byteLength(chunk)\n\t\t\tbudget.remaining -= len\n\t\t\tif (budget.remaining < 0) {\n\t\t\t\tcb(\n\t\t\t\t\tinvalid(\n\t\t\t\t\t\t\"resource.archive_too_large\",\n\t\t\t\t\t\t`archive extracts to more than ${budget.max} bytes`,\n\t\t\t\t\t\t{ maxExtractedBytes: budget.max },\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcb(undefined, chunk)\n\t\t},\n\t})\n}\n\nasync function readToBuffer(source: NodeJS.ReadableStream): Promise<Buffer> {\n\tconst chunks: Buffer[] = []\n\tfor await (const chunk of source) {\n\t\tchunks.push(typeof chunk === \"string\" ? Buffer.from(chunk) : chunk)\n\t}\n\treturn Buffer.concat(chunks)\n}\n\nfunction openZipFromBuffer(buffer: Buffer): Promise<ZipFile> {\n\treturn new Promise<ZipFile>((res, rej) => {\n\t\tyauzl.fromBuffer(\n\t\t\tbuffer,\n\t\t\t{ lazyEntries: true },\n\t\t\t(err: Error | null, zip: ZipFile) => {\n\t\t\t\tif (err !== null || zip === undefined) {\n\t\t\t\t\trej(\n\t\t\t\t\t\tinvalid(\n\t\t\t\t\t\t\t\"resource.archive_open_failed\",\n\t\t\t\t\t\t\terr?.message ?? \"could not open archive\",\n\t\t\t\t\t\t\t{},\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tres(zip)\n\t\t\t},\n\t\t)\n\t})\n}\n\nfunction openZipEntryStream(\n\tzipfile: ZipFile,\n\tentry: Entry,\n): Promise<NodeJS.ReadableStream> {\n\treturn new Promise((res, rej) => {\n\t\tzipfile.openReadStream(entry, (err, stream) => {\n\t\t\tif ((err !== null && err !== undefined) || stream === undefined) {\n\t\t\t\trej(\n\t\t\t\t\tinvalid(\n\t\t\t\t\t\t\"resource.archive_entry_unreadable\",\n\t\t\t\t\t\terr?.message ?? `could not read archive entry: ${entry.fileName}`,\n\t\t\t\t\t\t{ entry: entry.fileName },\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tres(stream)\n\t\t})\n\t})\n}\n","import { createWriteStream } from \"node:fs\"\nimport { mkdir, rename, rm, stat, writeFile } from \"node:fs/promises\"\nimport { dirname } from \"node:path\"\nimport type { Readable } from \"node:stream\"\nimport { pipeline } from \"node:stream/promises\"\n\n/**\n * Materialization primitives shared by every code path that writes\n * derived files into the extraction cache: the host's container\n * extractor and the server's artifact view (thumb/probe materialization\n * for both literal and virtual entries). One partial+rename+size-check\n * recipe and one single-flight guard, instead of a copy per caller.\n */\n\n/**\n * Write `openStream`'s bytes to `target` atomically: stream to a\n * `.partial` sibling, verify the written size matches `expectedSize`,\n * then rename into place. On any failure the partial is removed and the\n * error rethrown. An already-correct `target` is left untouched.\n *\n * With `key` set, concurrent calls for the same key share one in-flight\n * write instead of racing it (via {@link globalMaterializeInflight}).\n */\nexport async function materializeFile(opts: {\n\treadonly openStream: () => Readable | Promise<Readable>\n\treadonly target: string\n\treadonly expectedSize: number\n\treadonly key?: string\n}): Promise<void> {\n\tconst { key } = opts\n\tif (key !== undefined) {\n\t\treturn withSingleFlight(key, () => materializeFileWithoutFlight(opts))\n\t}\n\treturn materializeFileWithoutFlight(opts)\n}\n\nasync function materializeFileWithoutFlight(opts: {\n\treadonly openStream: () => Readable | Promise<Readable>\n\treadonly target: string\n\treadonly expectedSize: number\n}): Promise<void> {\n\tconst { openStream, target, expectedSize } = opts\n\tawait mkdir(dirname(target), { recursive: true })\n\tconst partial = `${target}.partial-${process.pid}-${Date.now()}`\n\ttry {\n\t\tif (expectedSize === 0) {\n\t\t\tawait writeFile(partial, Buffer.alloc(0))\n\t\t} else {\n\t\t\tawait pipeline(await openStream(), createWriteStream(partial))\n\t\t}\n\t\tconst written = await stat(partial)\n\t\tif (written.size !== expectedSize) {\n\t\t\tthrow new Error(\n\t\t\t\t`materialized ${target} size mismatch: expected ${expectedSize}, got ${written.size}`,\n\t\t\t)\n\t\t}\n\t\tawait rename(partial, target)\n\t} catch (err) {\n\t\tawait rm(partial, { force: true }).catch(() => {})\n\t\tthrow err\n\t}\n}\n\n/**\n * Deduplicate concurrent materialization of the same `key`: the second\n * caller awaits the first's in-flight work instead of racing it. The\n * inflight map is shared via {@link globalMaterializeInflight} unless a\n * custom one is passed, matching how the server's artifact views share\n * one guard process-wide.\n */\nexport function withSingleFlight<T>(\n\tkey: string,\n\trun: () => Promise<T>,\n\tinflight: Map<string, Promise<unknown>> = globalMaterializeInflight,\n): Promise<T> {\n\tconst pending = inflight.get(key) as Promise<T> | undefined\n\tif (pending !== undefined) return pending\n\tconst work = run()\n\tinflight.set(key, work)\n\tvoid work.then(\n\t\t() => inflight.delete(key),\n\t\t() => inflight.delete(key),\n\t)\n\treturn work\n}\n\n/** Process-wide single-flight guard for materialization (host + server). */\nexport const globalMaterializeInflight = new Map<string, Promise<unknown>>()\n","import type { Readable } from \"node:stream\"\nimport { invalid } from \"../errors.ts\"\nimport { SNIFF_WINDOW_BYTES, sniffContainerFormat } from \"./format.ts\"\nimport type { NestedCdCache } from \"./nested-cd-cache.ts\"\nimport {\n\ttype ArchiveSource,\n\tlistZipEntriesFromSource,\n\topenZipEntryStream,\n} from \"./zip-entries.ts\"\n\n/**\n * Container-entry addressing: plugin hooks (and the server view) may\n * reference a file *inside* a container entry with the virtual path\n * `outer!inner` — e.g. `manga.cbz!Chapter 1/001.jpg`. This module owns\n * the resolution rules and the zip listing (format identification\n * lives in `format.ts`); nested containers reuse `zip-entries.ts` over\n * a range source. Zip is the only addressable nested format —\n * tar/7z/rar/xz/gzip are whole-archive formats (extractable via 7-Zip,\n * not addressable by single entry).\n *\n * Ambiguity rule: `outer` is treated as a container only when its magic\n * bytes say so (zip) AND the archive actually contains `inner`;\n * otherwise the whole path is the literal container entry. A file whose\n * name contains `!` therefore stays addressable.\n */\n\n/** The virtual-path separator between an outer entry and an inner one. */\nexport const VIRTUAL_PATH_SEPARATOR = \"!\"\n\n/**\n * Read interface an outer container must satisfy. Both the plugin-side\n * `ResourceContainer` and the server's artifact view provide these — the\n * virtual resolver is deliberately minimal so it composes with either.\n */\nexport type OuterEntrySource = {\n\t/** Byte length of a literal entry; `undefined` when absent. */\n\treadonly sizeOf: (rel: string) => Promise<number | undefined>\n\t/** Read `[start, end)` of a literal entry (`end` exclusive). */\n\treadonly readSlice: (\n\t\trel: string,\n\t\tstart: number,\n\t\tend: number,\n\t) => Promise<Buffer>\n}\n\n/** A unified, format-agnostic inner entry. */\nexport type ArchiveEntry = {\n\treadonly name: string\n\treadonly sizeBytes: number\n}\n\n/** An inner entry plus its stream opener (format handles attached). */\nexport type OpenableArchiveEntry = ArchiveEntry & {\n\treadonly openStream: () => Readable\n}\n\nexport type PathResolution =\n\t| { readonly kind: \"literal\" }\n\t| {\n\t\t\treadonly kind: \"container\"\n\t\t\treadonly outer: string\n\t\t\treadonly entry: ArchiveEntry\n\t\t\t/** Decompressed bytes of the inner entry. */\n\t\t\treadonly openStream: () => Readable\n\t }\n\nexport type NestedResolver = {\n\t/**\n\t * Resolve `path` against the outer container. `kind: \"container\"`\n\t * means the path addressed a file inside a nested zip/tar; anything\n\t * else keeps the literal semantics (a plain entry, possibly one whose\n\t * name happens to contain `!`).\n\t */\n\treadonly resolve: (path: string) => Promise<PathResolution>\n\t/**\n\t * List every file entry of the container `outer`. Resolves to\n\t * `undefined` when `outer` is not a container (or does not exist).\n\t * Memoized per outer name for the resolver's lifetime.\n\t */\n\treadonly list: (\n\t\touter: string,\n\t) => Promise<readonly OpenableArchiveEntry[] | undefined>\n}\n\n/** Split `outer!inner` at the first separator. */\nexport function splitVirtualPath(path: string):\n\t| {\n\t\t\treadonly outer: string\n\t\t\treadonly inner: string\n\t }\n\t| undefined {\n\tconst sep = path.indexOf(VIRTUAL_PATH_SEPARATOR)\n\tif (sep <= 0) return undefined\n\tconst inner = path.slice(sep + 1)\n\tif (inner.length === 0) return undefined\n\treturn { outer: path.slice(0, sep), inner }\n}\n\n/** Wrap an outer container entry as a {@link ArchiveSource} (real size). */\nexport function createOuterArchiveSource(\n\touter: OuterEntrySource,\n\touterName: string,\n\tsize: number,\n): ArchiveSource {\n\treturn {\n\t\tsize,\n\t\treadRange: (start, end) => outer.readSlice(outerName, start, end + 1),\n\t}\n}\n\n/**\n * Build a resolver over one outer container. Archive listings are\n * memoized per outer entry name for the resolver's lifetime (a\n * resource-level operation parses each nested archive at most once),\n * and — when a {@link NestedCdCache} is provided — across resolver\n * instances, so a multi-hook pass parses each nested archive once.\n *\n * `scope` disambiguates cache keys when one shared cache serves many\n * containers (e.g. every resource of a server): keys become\n * `<scope>:<outerName>`, so two resources holding same-named archives\n * never serve each other's listings.\n */\nexport function createNestedResolver(\n\touter: OuterEntrySource,\n\topts: { readonly cdCache?: NestedCdCache; readonly scope?: string } = {},\n): NestedResolver {\n\tconst cdCache = opts.cdCache\n\tconst scope = opts.scope\n\tconst cacheKey = (outerName: string): string =>\n\t\tscope === undefined ? outerName : `${scope}:${outerName}`\n\tconst listingMemo = new Map<\n\t\tstring,\n\t\tPromise<readonly ListedEntry[] | undefined>\n\t>()\n\tconst listedMemo = new Map<\n\t\tstring,\n\t\tPromise<readonly OpenableArchiveEntry[] | undefined>\n\t>()\n\n\tfunction listOuter(\n\t\touterName: string,\n\t): Promise<readonly ListedEntry[] | undefined> {\n\t\tconst key = cacheKey(outerName)\n\t\tconst pending = listingMemo.get(key)\n\t\tif (pending !== undefined) return pending\n\t\tconst shared = cdCache?.get(key) as\n\t\t\t| Promise<readonly ListedEntry[] | undefined>\n\t\t\t| undefined\n\t\tif (shared !== undefined) {\n\t\t\tlistingMemo.set(key, shared)\n\t\t\treturn shared\n\t\t}\n\t\tconst work = listOuterOnce(outerName)\n\t\tlistingMemo.set(key, work)\n\t\tcdCache?.set(key, work)\n\t\tvoid work.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t)\n\t\treturn work\n\t}\n\n\tasync function listOuterOnce(\n\t\touterName: string,\n\t): Promise<readonly ListedEntry[] | undefined> {\n\t\tconst size = await outer.sizeOf(outerName)\n\t\tif (size === undefined) return undefined\n\t\tif (size === 0) return undefined\n\t\tconst head = await outer.readSlice(\n\t\t\touterName,\n\t\t\t0,\n\t\t\tMath.min(size, SNIFF_WINDOW_BYTES),\n\t\t)\n\t\tconst format = sniffContainerFormat(head)\n\t\t// Zip is the only addressable nested format; tar/7z/rar/xz/gzip\n\t\t// are whole-archive formats and report \"not a container\".\n\t\tif (format !== \"zip\") return undefined\n\t\tconst source = createOuterArchiveSource(outer, outerName, size)\n\t\tconst records = await listZipEntriesFromSource(source)\n\t\treturn records.map((r) => ({\n\t\t\tname: r.name,\n\t\t\tsizeBytes: r.uncompressedSize,\n\t\t\touterSize: size,\n\t\t\tzip: r,\n\t\t}))\n\t}\n\n\tasync function resolve(path: string): Promise<PathResolution> {\n\t\tconst parts = splitVirtualPath(path)\n\t\tif (parts === undefined) return { kind: \"literal\" }\n\t\tconst { outer: outerName, inner } = parts\n\t\tconst entries = await listOuter(outerName)\n\t\tconst found = entries?.find((e) => e.name === inner)\n\t\tif (found === undefined) return { kind: \"literal\" }\n\t\treturn {\n\t\t\tkind: \"container\",\n\t\t\touter: outerName,\n\t\t\tentry: { name: found.name, sizeBytes: found.sizeBytes },\n\t\t\topenStream: () => openListedEntry(outer, outerName, found),\n\t\t}\n\t}\n\n\tasync function list(\n\t\touterName: string,\n\t): Promise<readonly OpenableArchiveEntry[] | undefined> {\n\t\tconst pending = listedMemo.get(outerName)\n\t\tif (pending !== undefined) return pending\n\t\tconst work = listOuter(outerName).then((entries) =>\n\t\t\tentries === undefined\n\t\t\t\t? undefined\n\t\t\t\t: entries.map((e) => listedWithStream(outer, outerName, e)),\n\t\t)\n\t\tlistedMemo.set(outerName, work)\n\t\tvoid work.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t)\n\t\treturn work\n\t}\n\n\treturn { resolve, list }\n}\n\ntype ListedEntry = ArchiveEntry & {\n\t/** Byte size of the outer container entry. */\n\treadonly outerSize: number\n\treadonly zip?: import(\"./zip-entries.ts\").ZipEntry\n}\n\nfunction listedWithStream(\n\touter: OuterEntrySource,\n\touterName: string,\n\tentry: ListedEntry,\n): OpenableArchiveEntry {\n\treturn {\n\t\tname: entry.name,\n\t\tsizeBytes: entry.sizeBytes,\n\t\topenStream: () => openListedEntry(outer, outerName, entry),\n\t}\n}\n\nfunction openListedEntry(\n\touter: OuterEntrySource,\n\touterName: string,\n\tentry: ListedEntry,\n): Readable {\n\tconst source = createOuterArchiveSource(outer, outerName, entry.outerSize)\n\tif (entry.zip !== undefined) return openZipEntryStream(source, entry.zip)\n\tthrow invalid(\n\t\t\"resource.archive_open_failed\",\n\t\t`entry \"${entry.name}\" has no stream backend`,\n\t\t{ name: entry.name },\n\t)\n}\n","import { randomUUID } from \"node:crypto\"\nimport { mkdir, readFile, rename, rm, stat, writeFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport type { MediaKind } from \"@hoardodile/sdk-types\"\nimport { fileTypeFromName } from \"@hoardodile/sdk-types\"\n\nimport { invalid } from \"../errors.ts\"\nimport {\n\textractSevenZipInto,\n\tlistSevenZipEntries,\n\tresolveSevenZipPath,\n} from \"./7z.ts\"\nimport {\n\tassertExtractedTree,\n\tdecodeZipNames,\n\tnormalizeExtractedTree,\n} from \"./extract.ts\"\nimport {\n\ttype ContainerFormat,\n\tSNIFF_WINDOW_BYTES,\n\tsniffContainerFormat,\n} from \"./format.ts\"\nimport { materializeFile } from \"./materialize.ts\"\nimport type { NestedCdCache } from \"./nested-cd-cache.ts\"\nimport {\n\ttype ArchiveEntry,\n\tcreateNestedResolver,\n\tcreateOuterArchiveSource,\n\ttype OuterEntrySource,\n} from \"./nested-entry.ts\"\nimport { streamRange } from \"./zip-entries.ts\"\n\n/**\n * Header window read before handing bytes to the injected image prober.\n * Shared with the host's sharp-backed probes so both sides feed the same\n * bounded window (JPEG SOF markers live in the first KBs; 256KB covers\n * even EXIF-bloated JPEGs).\n */\nexport const PROBE_HEADER_BYTES = 256 * 1024\n\n/** Input accepted by the injected image prober (host's sharp probe). */\nexport type ImageProbeInput = string | Buffer\n\n/** Result of the injected image prober. */\nexport type ImageProbeResult = {\n\treadonly width: number\n\treadonly height: number\n\treadonly animated: boolean\n}\n\n/**\n * Materialize a container entry (zip/tar/7z/rar/xz/gzip) into a cache\n * directory so the browser can serve the inner files over plain URLs.\n * This is the only disk-writing capability of the plugin API; everything\n * else about containers (cover probing, dimensions, hashes) reads\n * through virtual paths without materialization.\n *\n * Extraction is whole-archive through 7-Zip (budgets and paths validated\n * from its listing, then the extracted tree re-verified — see\n * `assertExtractedTree`). The manifest below is built from the listing,\n * so entries carry the exact names and sizes 7-Zip reported.\n *\n * Cache layout (per archive):\n * <cacheDir>/<archiveName>/index.json — completion marker + manifest\n * <cacheDir>/<archiveName>/<innerPath> — extracted file (subdirs kept)\n *\n * The `index.json` marker is written last and atomically: its presence\n * means extraction completed. A partial or failed run leaves files but no\n * marker, so the next call re-extracts idempotently.\n */\n\nexport const EXTRACT_INDEX_VERSION = 1\n\nexport type ExtractedEntry = {\n\treadonly path: string\n\treadonly sizeBytes: number\n\treadonly kind: MediaKind\n\treadonly width?: number\n\treadonly height?: number\n\treadonly animated?: boolean\n}\n\nexport type ExtractionResult = {\n\treadonly entries: readonly ExtractedEntry[]\n}\n\nexport type ExtractProgress = {\n\t/** Entries materialized so far (skipped cached files count too). */\n\treadonly done: number\n\treadonly total: number\n}\n\nexport type ExtractArchiveDeps = {\n\t/** Read access to the outer container (the resource's entries). */\n\treadonly outer: OuterEntrySource\n\t/**\n\t * Absolute cache directory for this resource's plugin extractions\n\t * (e.g. `<cacheRoot>/resources/<id>/extracted/v<N>/archives`).\n\t */\n\treadonly cacheDir: string\n\t/** Cumulative uncompressed byte budget for one archive. */\n\treadonly maxBytes: number\n\t/** Entry-count budget for one archive. */\n\treadonly maxEntries: number\n\t/** Optional image prober (sharp-backed) for per-entry dimensions. */\n\treadonly probeImage?: (\n\t\tsource: ImageProbeInput,\n\t\textHint?: string,\n\t) => Promise<ImageProbeResult | undefined>\n\t/**\n\t * Called as entries are materialized, so a long first extraction can\n\t * surface progress to the user.\n\t */\n\treadonly onProgress?: (progress: ExtractProgress) => void\n\t/** Shared nested-listing cache (see {@link NestedCdCache}). */\n\treadonly nestedCdCache?: NestedCdCache\n}\n\nexport type ArchiveExtractor = {\n\t/**\n\t * Materialize (or re-list) the container `archiveName`. Idempotent\n\t * and single-flighted per archive name.\n\t */\n\treadonly extract: (archiveName: string) => Promise<ExtractionResult>\n\t/**\n\t * List the container's entries without materializing anything into\n\t * the final cache layout. Resolves to `undefined` when `archiveName`\n\t * is not a container (or no 7-Zip binary exists for non-zip\n\t * formats).\n\t */\n\treadonly list: (\n\t\tarchiveName: string,\n\t) => Promise<readonly ArchiveEntry[] | undefined>\n}\n\n/** Build a single-flight, idempotent extractor over one outer container. */\nexport function createArchiveExtractor(\n\tdeps: ExtractArchiveDeps,\n): ArchiveExtractor {\n\tconst resolver = createNestedResolver(deps.outer, {\n\t\tcdCache: deps.nestedCdCache,\n\t})\n\tconst inflight = new Map<string, Promise<ExtractionResult>>()\n\n\tfunction extract(archiveName: string): Promise<ExtractionResult> {\n\t\tconst pending = inflight.get(archiveName)\n\t\tif (pending !== undefined) return pending\n\t\tconst work = extractOnce(archiveName)\n\t\tinflight.set(archiveName, work)\n\t\tvoid work.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t)\n\t\treturn work\n\t}\n\n\tasync function extractOnce(archiveName: string): Promise<ExtractionResult> {\n\t\tconst size = await deps.outer.sizeOf(archiveName)\n\t\tif (size === undefined) {\n\t\t\tthrow invalid(\n\t\t\t\t\"plugin.container_extract_failed\",\n\t\t\t\t`\"${archiveName}\" does not exist`,\n\t\t\t\t{ archiveName },\n\t\t\t)\n\t\t}\n\t\tconst markerPath = join(deps.cacheDir, archiveName, \"index.json\")\n\t\tconst existing = await readExistingManifest(markerPath, archiveName)\n\t\tif (existing !== undefined) return { entries: existing }\n\t\tif (size > 0) {\n\t\t\tconst head = await deps.outer.readSlice(\n\t\t\t\tarchiveName,\n\t\t\t\t0,\n\t\t\t\tMath.min(size, SNIFF_WINDOW_BYTES),\n\t\t\t)\n\t\t\tconst format = sniffContainerFormat(head)\n\t\t\tif (format !== undefined) {\n\t\t\t\treturn extractWithSevenZip(archiveName, size, markerPath, format)\n\t\t\t}\n\t\t}\n\t\tthrow invalid(\n\t\t\t\"plugin.container_extract_failed\",\n\t\t\t`\"${archiveName}\" is not a supported archive (zip/tar/7z/rar/xz/gzip)`,\n\t\t\t{ archiveName },\n\t\t)\n\t}\n\n\t/**\n\t * Whole-archive extraction through 7-Zip: materialize the outer entry\n\t * to a temp file (7-Zip needs a real path), validate budgets and\n\t * encryption from the listing, extract into the cache dir, then build\n\t * the idempotent manifest. Runs for every supported format — zip/tar\n\t * included, exactly like the 7z/rar/xz branch before them.\n\t */\n\tasync function extractWithSevenZip(\n\t\tarchiveName: string,\n\t\tsize: number,\n\t\tmarkerPath: string,\n\t\tformat: ContainerFormat,\n\t): Promise<ExtractionResult> {\n\t\tconst source = createOuterArchiveSource(deps.outer, archiveName, size)\n\t\tconst tempPath = join(\n\t\t\tdeps.cacheDir,\n\t\t\t`${archiveName}.7z-partial-${process.pid}-${randomUUID().slice(0, 8)}`,\n\t\t)\n\t\tawait mkdir(deps.cacheDir, { recursive: true })\n\t\ttry {\n\t\t\tawait materializeFile({\n\t\t\t\topenStream: () => streamRange(source, 0, size - 1),\n\t\t\t\ttarget: tempPath,\n\t\t\t\texpectedSize: size,\n\t\t\t})\n\t\t\tconst entries = await listSevenZipEntries(tempPath)\n\t\t\tconst files = entries.filter((e) => !e.folder)\n\t\t\tcheckExtractionBudgets(archiveName, files, deps.maxBytes, deps.maxEntries)\n\t\t\tif (entries.some((e) => e.encrypted)) {\n\t\t\t\tthrow invalid(\n\t\t\t\t\t\"plugin.container_extract_failed\",\n\t\t\t\t\t`archive \"${archiveName}\" is password-protected — encrypted archives are not supported`,\n\t\t\t\t\t{ archiveName },\n\t\t\t\t)\n\t\t\t}\n\t\t\t// 7-Zip's text listing drops invalid-UTF-8 bytes on POSIX, so\n\t\t\t// for zip archives the decoded names come from the archive\n\t\t\t// itself (yauzl): the manifest, the probes and the legacy-name\n\t\t\t// renames below all agree on them.\n\t\t\tconst decoded =\n\t\t\t\tformat === \"zip\"\n\t\t\t\t\t? await decodeZipNames(\n\t\t\t\t\t\t\ttempPath,\n\t\t\t\t\t\t\tfiles.map((e) => ({ name: e.name, sizeBytes: e.sizeBytes })),\n\t\t\t\t\t\t)\n\t\t\t\t\t: undefined\n\t\t\tconst materialized = decoded?.files ?? files\n\t\t\t// Pre-validate every path from the listing — 7-Zip itself\n\t\t\t// warns-and-skips unsafe names instead of failing.\n\t\t\tfor (const entry of materialized) sanitizeExtractPath(entry.name)\n\t\t\tconst root = join(deps.cacheDir, archiveName)\n\t\t\tawait mkdir(root, { recursive: true })\n\t\t\tawait extractSevenZipInto(tempPath, root)\n\t\t\t// 7-Zip writes legacy zip names verbatim on POSIX and restores\n\t\t\t// mode bits that can strip app access; fix both before the tree\n\t\t\t// is re-walked (symlink scan) or read for the manifest below.\n\t\t\t// The decoded names are the ground truth the legacy rename pass\n\t\t\t// matches against (see `renameLegacyZipNames` in extract.ts).\n\t\t\tawait normalizeExtractedTree(root, {\n\t\t\t\tlegacyZipNames: format === \"zip\",\n\t\t\t\texpectedNames: decoded?.paths,\n\t\t\t})\n\t\t\tawait assertExtractedTree(root, deps.maxBytes)\n\t\t\tdeps.onProgress?.({ done: files.length, total: files.length })\n\t\t\tconst manifest = await buildManifest(\n\t\t\t\tarchiveName,\n\t\t\t\tmaterialized.map((e) => ({ name: e.name, sizeBytes: e.sizeBytes })),\n\t\t\t)\n\t\t\tawait writeManifest(markerPath, archiveName, manifest)\n\t\t\t// The manifest now serves listings (and materialized virtual\n\t\t\t// paths); drop the size-keyed listing cache for this archive.\n\t\t\tawait rm(join(deps.cacheDir, `${archiveName}.7z-list`), {\n\t\t\t\tforce: true,\n\t\t\t}).catch(() => {})\n\t\t\tawait rm(join(deps.cacheDir, `${archiveName}.7z-list.json`), {\n\t\t\t\tforce: true,\n\t\t\t}).catch(() => {})\n\t\t\treturn { entries: manifest }\n\t\t} finally {\n\t\t\tawait rm(tempPath, { force: true }).catch(() => {})\n\t\t}\n\t}\n\n\t/** Shared bomb guards for one extraction: entry count and total bytes. */\n\tfunction checkExtractionBudgets(\n\t\tarchiveName: string,\n\t\tentries: readonly { readonly sizeBytes: number }[],\n\t\tmaxBytes: number,\n\t\tmaxEntries: number,\n\t): void {\n\t\tif (entries.length > maxEntries) {\n\t\t\tthrow invalid(\n\t\t\t\t\"plugin.container_extract_failed\",\n\t\t\t\t`archive \"${archiveName}\" has ${entries.length} entries, exceeding the limit of ${maxEntries}`,\n\t\t\t\t{ archiveName, entries: entries.length, maxEntries },\n\t\t\t)\n\t\t}\n\t\tlet totalBytes = 0\n\t\tfor (const entry of entries) totalBytes += entry.sizeBytes\n\t\tif (totalBytes > maxBytes) {\n\t\t\tthrow invalid(\n\t\t\t\t\"plugin.container_extract_failed\",\n\t\t\t\t`archive \"${archiveName}\" unpacks to ${totalBytes} bytes, exceeding the limit of ${maxBytes}`,\n\t\t\t\t{ archiveName, totalBytes, maxBytes },\n\t\t\t)\n\t\t}\n\t}\n\n\tasync function buildManifest(\n\t\tarchiveName: string,\n\t\tentries: readonly ArchiveEntry[],\n\t): Promise<readonly ExtractedEntry[]> {\n\t\tconst manifest: ExtractedEntry[] = []\n\t\tfor (const entry of entries) {\n\t\t\tconst kind = fileTypeFromName(entry.name)?.kind ?? \"other\"\n\t\t\tconst row: MutableExtractedEntry = {\n\t\t\t\tpath: entry.name,\n\t\t\t\tsizeBytes: entry.sizeBytes,\n\t\t\t\tkind,\n\t\t\t}\n\t\t\tif (kind === \"image\" && deps.probeImage !== undefined) {\n\t\t\t\tconst rel = sanitizeExtractPath(entry.name)\n\t\t\t\tconst filePath = join(deps.cacheDir, archiveName, rel)\n\t\t\t\tconst probe = await probeImageHead(\n\t\t\t\t\tdeps.probeImage,\n\t\t\t\t\tfilePath,\n\t\t\t\t\tentry.name,\n\t\t\t\t)\n\t\t\t\tif (probe !== undefined) {\n\t\t\t\t\trow.width = probe.width\n\t\t\t\t\trow.height = probe.height\n\t\t\t\t\trow.animated = probe.animated\n\t\t\t\t}\n\t\t\t}\n\t\t\tmanifest.push(row)\n\t\t}\n\t\treturn manifest\n\t}\n\n\t/**\n\t * List the container's entries. Zip lists from the nested resolver\n\t * (CD parse, nothing materialized); tar/7z/rar/xz list through the\n\t * 7-Zip binary over a size-keyed materialization cache in the cache\n\t * dir (7-Zip needs a real path; the bytes are written once per outer\n\t * size and reused across hook calls — every hook call builds a fresh\n\t * API instance, so per-call temp files would re-write the whole\n\t * archive each time). Resolves to `undefined` when the entry is not a\n\t * container, is gzip (whole-archive single-stream), or the 7-Zip\n\t * binary is absent (quiet degradation — callers treat it as \"not a\n\t * container\", matching the resolver semantics).\n\t */\n\tasync function list(\n\t\tarchiveName: string,\n\t): Promise<readonly ArchiveEntry[] | undefined> {\n\t\tconst size = await deps.outer.sizeOf(archiveName)\n\t\tif (size === undefined) return undefined\n\t\t// An extracted archive answers from its manifest — cheaper than\n\t\t// any listing, and the manifest is the source of truth for the\n\t\t// materialized virtual paths (see nested-view.ts).\n\t\tconst markerPath = join(deps.cacheDir, archiveName, \"index.json\")\n\t\tconst extracted = await readExistingManifest(markerPath, archiveName)\n\t\tif (extracted !== undefined) {\n\t\t\treturn extracted.map((e) => ({ name: e.path, sizeBytes: e.sizeBytes }))\n\t\t}\n\t\tif (size === 0) return undefined\n\t\tconst head = await deps.outer.readSlice(\n\t\t\tarchiveName,\n\t\t\t0,\n\t\t\tMath.min(size, SNIFF_WINDOW_BYTES),\n\t\t)\n\t\tconst format = sniffContainerFormat(head)\n\t\tif (format === \"zip\") {\n\t\t\tconst entries = await resolver.list(archiveName)\n\t\t\tif (entries === undefined) return undefined\n\t\t\treturn entries.map((e) => ({ name: e.name, sizeBytes: e.sizeBytes }))\n\t\t}\n\t\tif (\n\t\t\tformat === \"tar\" ||\n\t\t\tformat === \"7z\" ||\n\t\t\tformat === \"rar\" ||\n\t\t\tformat === \"xz\"\n\t\t) {\n\t\t\treturn listViaSevenZip(archiveName, size)\n\t\t}\n\t\treturn undefined\n\t}\n\n\t/**\n\t * Non-zip listing through 7-Zip. The outer entry is materialized once\n\t * per size into `<cacheDir>/<archiveName>.7z-list` (keyed by outer\n\t * size — the source is immutable per version, so a size match means\n\t * the cached bytes are the same file) and the parsed listing is\n\t * cached next to it. `extractWithSevenZip` removes both files once a\n\t * real extraction lands, so the manifest takes over afterwards.\n\t */\n\tasync function listViaSevenZip(\n\t\tarchiveName: string,\n\t\tsize: number,\n\t): Promise<readonly ArchiveEntry[] | undefined> {\n\t\tif (resolveSevenZipPath() === undefined) return undefined\n\t\tawait mkdir(deps.cacheDir, { recursive: true })\n\t\tconst cachePath = join(deps.cacheDir, `${archiveName}.7z-list`)\n\t\tconst entriesPath = join(deps.cacheDir, `${archiveName}.7z-list.json`)\n\t\tconst cached = await stat(cachePath).catch(() => undefined)\n\t\tif (cached?.size === size) {\n\t\t\tconst raw = await readFile(entriesPath, \"utf8\").catch(() => undefined)\n\t\t\tif (raw !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.parse(raw) as readonly ArchiveEntry[]\n\t\t\t\t} catch {\n\t\t\t\t\t// Corrupt cache — fall through and re-materialize.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tconst source = createOuterArchiveSource(deps.outer, archiveName, size)\n\t\t\tawait materializeFile({\n\t\t\t\topenStream: () => streamRange(source, 0, size - 1),\n\t\t\t\ttarget: cachePath,\n\t\t\t\texpectedSize: size,\n\t\t\t})\n\t\t\tconst listed = await listSevenZipEntries(cachePath)\n\t\t\tconst files = listed\n\t\t\t\t.filter((e) => !e.folder)\n\t\t\t\t.map((e) => ({ name: e.name, sizeBytes: e.sizeBytes }))\n\t\t\tawait writeFile(entriesPath, JSON.stringify(files))\n\t\t\treturn files\n\t\t} catch (err) {\n\t\t\tawait rm(cachePath, { force: true }).catch(() => {})\n\t\t\tawait rm(entriesPath, { force: true }).catch(() => {})\n\t\t\tthrow err\n\t\t}\n\t}\n\n\treturn { extract, list }\n}\n\ntype MutableExtractedEntry = {\n\tpath: string\n\tsizeBytes: number\n\tkind: MediaKind\n\twidth?: number\n\theight?: number\n\tanimated?: boolean\n}\n\n/**\n * Read and validate a manifest written by {@link writeManifest}: version\n * and archive-name must match, otherwise the marker is stale garbage and\n * the caller re-extracts. Shared with the nested-view materialized-path\n * resolution.\n */\nexport async function readExistingManifest(\n\tmarkerPath: string,\n\tarchiveName: string,\n): Promise<readonly ExtractedEntry[] | undefined> {\n\tconst raw = await readFile(markerPath, \"utf8\").catch(() => undefined)\n\tif (raw === undefined) return undefined\n\ttry {\n\t\tconst parsed = JSON.parse(raw) as {\n\t\t\treadonly v?: number\n\t\t\treadonly archiveName?: string\n\t\t\treadonly entries?: readonly ExtractedEntry[]\n\t\t}\n\t\tif (\n\t\t\tparsed.v !== EXTRACT_INDEX_VERSION ||\n\t\t\tparsed.archiveName !== archiveName ||\n\t\t\t!Array.isArray(parsed.entries)\n\t\t) {\n\t\t\treturn undefined\n\t\t}\n\t\treturn parsed.entries\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nasync function writeManifest(\n\tmarkerPath: string,\n\tarchiveName: string,\n\tentries: readonly ExtractedEntry[],\n): Promise<void> {\n\tconst payload = JSON.stringify({\n\t\tv: EXTRACT_INDEX_VERSION,\n\t\tarchiveName,\n\t\tentries,\n\t})\n\tconst partial = `${markerPath}.partial-${process.pid}`\n\tawait writeFile(partial, payload)\n\tawait rename(partial, markerPath)\n}\n\n/**\n * Normalize an inner entry name to a safe relative path: backslashes\n * become forward slashes, traversal segments and control characters are\n * rejected, and the result must stay inside the extraction root.\n */\nexport function sanitizeExtractPath(name: string): string {\n\tconst normalized = name.replace(/\\\\/g, \"/\")\n\tif (normalized.startsWith(\"/\") || /^[A-Za-z]:/.test(normalized)) {\n\t\tthrow invalid(\n\t\t\t\"plugin.container_extract_failed\",\n\t\t\t`archive entry has an absolute path: \"${name}\"`,\n\t\t\t{ name },\n\t\t)\n\t}\n\tconst segments = normalized.split(\"/\")\n\tfor (const seg of segments) {\n\t\tif (seg.length === 0 || seg === \".\" || seg === \"..\") {\n\t\t\tthrow invalid(\n\t\t\t\t\"plugin.container_extract_failed\",\n\t\t\t\t`archive entry has an unsafe path: \"${name}\"`,\n\t\t\t\t{ name },\n\t\t\t)\n\t\t}\n\t\tif (hasControlChar(seg)) {\n\t\t\tthrow invalid(\n\t\t\t\t\"plugin.container_extract_failed\",\n\t\t\t\t`archive entry contains control characters: \"${name}\"`,\n\t\t\t\t{ name },\n\t\t\t)\n\t\t}\n\t}\n\treturn segments.join(\"/\")\n}\n\n/** True when any char code is a control character (C0 or DEL). */\nfunction hasControlChar(value: string): boolean {\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst code = value.charCodeAt(i)\n\t\tif (code < 0x20 || code === 0x7f) return true\n\t}\n\treturn false\n}\n\n/** Probe one extracted image's header window for dimensions. */\nasync function probeImageHead(\n\tprobeImage: NonNullable<ExtractArchiveDeps[\"probeImage\"]>,\n\tfilePath: string,\n\tname: string,\n): Promise<ImageProbeResult | undefined> {\n\tconst head = await readFile(filePath).then((b) =>\n\t\tb.subarray(0, PROBE_HEADER_BYTES),\n\t)\n\tif (head.length === 0) return undefined\n\tconst ext = name.slice(name.lastIndexOf(\".\")).toLowerCase()\n\treturn probeImage(head, ext)\n}\n","/**\n * Process-wide cache of nested container listings (central directories\n * of zip/tar entries addressed via virtual paths). Resolving\n * `outer!inner` re-parses the outer archive's CD on every hook call;\n * this cache keeps the parsed listing resident so a multi-hook pass\n * (sourceMeta → cover → hashes → listFiles) parses each nested archive\n * once.\n *\n * Keyed by the outer entry name only — callers scope the cache instance\n * to a fixed (resId, fileVersion), and archives are immutable per\n * version, so a key never outlives its content.\n */\nexport type NestedCdCache = {\n\treadonly get: (key: string) => Promise<unknown> | undefined\n\treadonly set: (key: string, value: Promise<unknown>) => void\n\treadonly clear: () => void\n}\n\nexport type NestedCdCacheOptions = {\n\t/** Max number of nested archives kept resident. */\n\treadonly maxEntries: number\n}\n\nconst DEFAULT_MAX_ENTRIES = 512\n\n/** Create an LRU-bounded nested listing cache. */\nexport function createNestedCdCache(\n\topts?: Partial<NestedCdCacheOptions>,\n): NestedCdCache {\n\tconst maxEntries = opts?.maxEntries ?? DEFAULT_MAX_ENTRIES\n\tconst lru = new Map<string, Promise<unknown>>()\n\n\tfunction get(key: string): Promise<unknown> | undefined {\n\t\tconst value = lru.get(key)\n\t\tif (value === undefined) return undefined\n\t\tlru.delete(key)\n\t\tlru.set(key, value)\n\t\treturn value\n\t}\n\n\tfunction set(key: string, value: Promise<unknown>): void {\n\t\tlru.set(key, value)\n\t\twhile (lru.size > maxEntries) {\n\t\t\tconst oldest = lru.keys().next().value\n\t\t\tif (oldest === undefined) break\n\t\t\tlru.delete(oldest)\n\t\t}\n\t}\n\n\tfunction clear(): void {\n\t\tlru.clear()\n\t}\n\n\treturn { get, set, clear }\n}\n","import { createWriteStream, readdirSync } from \"node:fs\"\nimport { join, resolve, sep } from \"node:path\"\nimport type { Readable } from \"node:stream\"\nimport { pipeline } from \"node:stream/promises\"\nimport yazl from \"yazl\"\n\n/**\n * On-the-fly zip exports. Resource sources are stored as bare files on\n * disk — nothing is packed at commit time; this module streams STORED\n * zip bytes straight to the consumer without a staging file.\n */\n\n/**\n * A logical zip entry whose bytes are produced on demand. Streams are\n * read once, in order, when the output is consumed.\n */\nexport type ZipStreamEntry = {\n\treadonly name: string\n\treadonly size: number\n\treadonly openStream: () => Readable\n}\n\n/**\n * Stream a STORED zip from an ordered list of logical entries. Used by\n * the HTTP layer for exports (resource source packs, bulk downloads)\n * without a staging file on disk. Zero-length entries are packed as\n * empty buffers.\n */\nexport function streamStoredZip(entries: readonly ZipStreamEntry[]) {\n\tconst zip = new yazl.ZipFile()\n\tfor (const entry of entries) {\n\t\tif (entry.size === 0) {\n\t\t\tzip.addBuffer(Buffer.alloc(0), entry.name, { compress: false })\n\t\t\tcontinue\n\t\t}\n\t\tzip.addReadStream(entry.openStream(), entry.name, {\n\t\t\tcompress: false,\n\t\t\tsize: entry.size,\n\t\t})\n\t}\n\tzip.end()\n\treturn zip.outputStream\n}\n\n/**\n * Pack the *contents* of `srcDir` (its entries at the archive root) into\n * a deflated zip written to `zipPath`. Entry names use forward slashes,\n * the file order is sorted by relative path, and the output is streamed\n * to disk — deterministic on every platform, memory-bounded regardless\n * of dist size. The canonical packer for project-internal zips (the\n * plugin release artifact via the CLI); resource exports use\n * {@link streamStoredZip} instead.\n */\nexport async function packZipDirectory(\n\tsrcDir: string,\n\tzipPath: string,\n): Promise<void> {\n\tconst root = resolve(srcDir)\n\tconst files = listFilesSorted(root)\n\tconst zip = new yazl.ZipFile()\n\tfor (const abs of files) {\n\t\t// yazl wants metadata names with forward slashes; the local-path\n\t\t// basename order is deterministic thanks to listFilesSorted.\n\t\tconst rel = abs\n\t\t\t.slice(root.length + 1)\n\t\t\t.split(sep)\n\t\t\t.join(\"/\")\n\t\tzip.addFile(abs, rel, { compress: true })\n\t}\n\tzip.end()\n\tawait pipeline(zip.outputStream, createWriteStream(resolve(zipPath)))\n}\n\n/** Every regular file under `root`, sorted by relative path (recursive). */\nfunction listFilesSorted(root: string): string[] {\n\tconst out: string[] = []\n\tconst walk = (dir: string) => {\n\t\tfor (const entry of readdirSync(dir, { withFileTypes: true })) {\n\t\t\tconst abs = join(dir, entry.name)\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\twalk(abs)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!entry.isFile()) continue\n\t\t\tout.push(abs)\n\t\t}\n\t}\n\twalk(root)\n\treturn out.sort()\n}\n","import { createHash } from \"node:crypto\"\nimport type { Readable } from \"node:stream\"\nimport type { ImageHashKind } from \"@hoardodile/sdk-types\"\n\n/**\n * Content hashing for the `imageHashes` plugin hook. All perceptual\n * hashes derive from one 32×32 grayscale rendition (single sharp\n * decode), so requesting several kinds costs one decode:\n * - `sha256`: exact digest of the raw entry bytes (streamed).\n * - `dhash`: difference hash — 9 samples per row of the 32×32 grid\n * (nearest-neighbour spread), adjacent pairs compared, equivalent to\n * the standard 9×8 dHash.\n * - `phash`: standard DCT-based perceptual hash — the 32×32 grid is\n * mean-pooled to 8×8, then the 8×8 DCT coefficients are thresholded\n * at the median.\n *\n * The 64-bit values are hex strings (16 chars), compared by Hamming\n * distance in `@hoardodile/sdk-server` / the server's hash service.\n */\n\n/** Side of the grayscale grid every perceptual hash is derived from. */\nexport const PHASH_GRID = 32\n\n/**\n * Minimum grayscale standard deviation (0–255 scale) for an image to\n * yield perceptual hashes. Near-flat images (solid fills, blank pages)\n * hash to distance-0 neighbours of every other flat image, so they are\n * skipped — `sha256` exact hashing is unaffected.\n */\nexport const MIN_PERCEPTUAL_STDDEV = 8\n\n/** Stream `stream` through a digest of `algo` and resolve its lowercase hex. */\nexport function hashStream(\n\tstream: Readable,\n\talgo: \"md5\" | \"sha256\",\n): Promise<string> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst hash = createHash(algo)\n\t\tstream.on(\"data\", (chunk: string | Buffer) => hash.update(chunk))\n\t\tstream.on(\"end\", () => resolve(hash.digest(\"hex\")))\n\t\tstream.on(\"error\", reject)\n\t})\n}\n\n/**\n * dHash over a `PHASH_GRID × PHASH_GRID` grayscale buffer: 9 samples per\n * row (nearest-neighbour spread across the width), each of 8×8 bits\n * comparing a sample against its right neighbour, packed MSB-first into\n * a 16-char hex string.\n */\nexport function computeDHash(gray: Uint8Array, grid = PHASH_GRID): string {\n\tlet bits = 0n\n\tfor (let row = 0; row < 8; row++) {\n\t\tfor (let col = 0; col < 8; col++) {\n\t\t\tconst left = sampleAt(gray, grid, row, col)\n\t\t\tconst right = sampleAt(gray, grid, row, col + 1)\n\t\t\tbits = (bits << 1n) | BigInt(left > right ? 1 : 0)\n\t\t}\n\t}\n\treturn bits.toString(16).padStart(16, \"0\")\n}\n\n/** Grayscale value at grid position (row, col), clamped to the grid. */\nfunction sampleAt(\n\tgray: Uint8Array,\n\tgrid: number,\n\trow: number,\n\tcol: number,\n): number {\n\tconst x = Math.round((col * (grid - 1)) / 8)\n\treturn gray[row * grid + x] ?? 0\n}\n\n/**\n * pHash over a `PHASH_GRID × PHASH_GRID` grayscale buffer: the grid is\n * mean-pooled to 8×8, then the 8×8 type-II DCT coefficients (precomputed\n * cosine tables) are thresholded at their median, each bit MSB-first\n * into a 16-char hex string.\n */\nexport function computePHash(gray: Uint8Array, grid = PHASH_GRID): string {\n\tconst n = 8\n\tconst small = meanPool(gray, grid, n)\n\tconst cos = precomputeCosines(n, n)\n\tconst coefficients: number[] = []\n\tfor (let v = 0; v < n; v++) {\n\t\tfor (let u = 0; u < n; u++) {\n\t\t\tlet sum = 0\n\t\t\tfor (let y = 0; y < n; y++) {\n\t\t\t\tconst cosYv = cos[v]?.[y] ?? 0\n\t\t\t\tconst cosXu = cos[u] ?? []\n\t\t\t\tlet rowSum = 0\n\t\t\t\tfor (let x = 0; x < n; x++) {\n\t\t\t\t\trowSum += (small[y * n + x] ?? 0) * (cosXu[x] ?? 0)\n\t\t\t\t}\n\t\t\t\tsum += rowSum * cosYv\n\t\t\t}\n\t\t\tconst cu = u === 0 ? 1 / Math.SQRT2 : 1\n\t\t\tconst cv = v === 0 ? 1 / Math.SQRT2 : 1\n\t\t\tcoefficients.push(0.25 * cu * cv * sum)\n\t\t}\n\t}\n\tconst median = medianOf(coefficients)\n\tlet bits = 0n\n\tfor (const coefficient of coefficients) {\n\t\tbits = (bits << 1n) | BigInt(coefficient > median ? 1 : 0)\n\t}\n\treturn bits.toString(16).padStart(16, \"0\")\n}\n\n/** Mean-pool a `grid × grid` buffer into an `n × n` grid of averages. */\nfunction meanPool(gray: Uint8Array, grid: number, n: number): number[] {\n\tconst step = grid / n\n\tconst pooled: number[] = []\n\tfor (let by = 0; by < n; by++) {\n\t\tfor (let bx = 0; bx < n; bx++) {\n\t\t\tlet sum = 0\n\t\t\tfor (let y = 0; y < step; y++) {\n\t\t\t\tfor (let x = 0; x < step; x++) {\n\t\t\t\t\tsum += gray[(by * step + y) * grid + bx * step + x] ?? 0\n\t\t\t\t}\n\t\t\t}\n\t\t\tpooled.push(sum / (step * step))\n\t\t}\n\t}\n\treturn pooled\n}\n\n/**\n * Population standard deviation of a grayscale buffer. Perceptual hashes\n * of near-flat images (solid fills, blank pages) cluster at distance 0\n * regardless of content — callers gate hash emission on this measure.\n */\nexport function grayStddev(gray: Uint8Array): number {\n\tif (gray.length === 0) return 0\n\tlet mean = 0\n\tfor (const value of gray) mean += value\n\tmean /= gray.length\n\tlet squared = 0\n\tfor (const value of gray) {\n\t\tconst delta = value - mean\n\t\tsquared += delta * delta\n\t}\n\treturn Math.sqrt(squared / gray.length)\n}\n\nfunction precomputeCosines(grid: number, n: number): number[][] {\n\tconst cosX: number[][] = []\n\tfor (let k = 0; k < n; k++) {\n\t\tconst row: number[] = []\n\t\tfor (let x = 0; x < grid; x++) {\n\t\t\trow.push(Math.cos(((2 * x + 1) * k * Math.PI) / (2 * grid)))\n\t\t}\n\t\tcosX.push(row)\n\t}\n\treturn cosX\n}\n\nfunction medianOf(values: readonly number[]): number {\n\tconst sorted = [...values].sort((a, b) => a - b)\n\treturn sorted[Math.floor(sorted.length / 2)] ?? 0\n}\n\n/** Kinds whose values come from the single grayscale decode. */\nexport const PERCEPTUAL_HASH_KINDS: readonly ImageHashKind[] = [\n\t\"dhash\",\n\t\"phash\",\n]\n","import { createReadStream } from \"node:fs\"\nimport { readFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport type { Readable } from \"node:stream\"\nimport { buffer } from \"node:stream/consumers\"\nimport {\n\ttype ExtractedEntry,\n\treadExistingManifest,\n} from \"./archive/extract-archive.ts\"\nimport type { NestedCdCache, NestedResolver } from \"./archive/index.ts\"\nimport { createNestedResolver, splitVirtualPath } from \"./archive/index.ts\"\nimport { readFileRange } from \"./archive/zip-entries.ts\"\nimport type { ResourceContainer } from \"./container.ts\"\n\n/**\n * Wrap a {@link ResourceContainer} so every read operation understands\n * virtual paths (`outer!inner`, see `./archive`). The plugin API,\n * the server artifact view and the CLI directory container all funnel\n * entry reads through this wrapper, so nested-container support lands\n * everywhere (hooks, cover pipeline, thumbnails, the HTTP file route)\n * without each consumer knowing about containers.\n *\n * A virtual path resolves in two steps:\n * 1. nested zip addressing — the resolver reads the outer entry's\n * central directory and streams the *decompressed* inner bytes;\n * 2. materialized cache addressing — when `extractCacheDir` is wired\n * and the outer archive was extracted by the plugin API (its\n * manifest exists in `<extractCacheDir>/<outer>/`), the inner file\n * is served straight from disk. This is how non-zip containers\n * (rar/7z/tar — not expressible as random-access sources) get\n * `outer!inner` addressing after extraction, and it is strictly\n * better than streaming: entries are seekable.\n *\n * Literal entries pass through untouched; virtual entries are only\n * addressable by name, never enumerated.\n *\n * `scope` prefixes nested-cache keys when the cache is shared across\n * containers (see {@link createNestedResolver}); `resolveSeekablePath`\n * forwards the base container's capability for literal entries and the\n * extracted file path for materialized ones.\n */\nexport function createNestedAwareContainer(\n\tbase: ResourceContainer,\n\tnestedCdCache?: NestedCdCache,\n\tscope?: string,\n\textractCacheDir?: string,\n): ResourceContainer {\n\tconst resolver = createNestedResolver(\n\t\t{\n\t\t\tsizeOf: (rel) => base.resolveByteRange(rel).then((r) => r?.size),\n\t\t\treadSlice: (rel, start, end) => base.readEntrySlice(rel, start, end),\n\t\t},\n\t\t{ cdCache: nestedCdCache, scope },\n\t)\n\n\t// Manifest reads are memoized per (cacheDir, outer): archives are\n\t// immutable per version, so a parsed manifest never needs invalidation.\n\tconst manifestMemo = new Map<\n\t\tstring,\n\t\tPromise<readonly ExtractedEntry[] | undefined>\n\t>()\n\n\tfunction readManifest(\n\t\touter: string,\n\t): Promise<readonly ExtractedEntry[] | undefined> {\n\t\tconst key = `${extractCacheDir}:${outer}`\n\t\tconst pending = manifestMemo.get(key)\n\t\tif (pending !== undefined) return pending\n\t\tconst work = readExistingManifest(\n\t\t\tjoin(extractCacheDir!, outer, \"index.json\"),\n\t\t\touter,\n\t\t).catch(() => undefined)\n\t\tmanifestMemo.set(key, work)\n\t\treturn work\n\t}\n\n\ttype MaterializedEntry = {\n\t\treadonly kind: \"materialized\"\n\t\treadonly absPath: string\n\t\treadonly sizeBytes: number\n\t}\n\n\t/**\n\t * Resolve `outer!inner` against the extraction cache: the inner path\n\t * must appear in the outer's manifest (the manifest paths were\n\t * sanitized at extraction time, so this is a whitelist — no\n\t * traversal can reach outside the cache dir).\n\t */\n\tasync function resolveMaterialized(\n\t\trel: string,\n\t): Promise<MaterializedEntry | undefined> {\n\t\tif (extractCacheDir === undefined) return undefined\n\t\tconst parts = splitVirtualPath(rel)\n\t\tif (parts === undefined) return undefined\n\t\tconst { outer, inner } = parts\n\t\tconst manifest = await readManifest(outer)\n\t\tconst entry = manifest?.find((e) => e.path === inner)\n\t\tif (entry === undefined) return undefined\n\t\treturn {\n\t\t\tkind: \"materialized\",\n\t\t\tabsPath: join(extractCacheDir, outer, inner),\n\t\t\tsizeBytes: entry.sizeBytes,\n\t\t}\n\t}\n\n\tasync function resolve(rel: string) {\n\t\tconst nested = await resolver.resolve(rel)\n\t\tif (nested.kind === \"container\") return nested\n\t\tconst materialized = await resolveMaterialized(rel)\n\t\treturn materialized ?? nested\n\t}\n\n\tasync function readEntrySlice(\n\t\trel: string,\n\t\tstart: number,\n\t\tend: number,\n\t): Promise<Buffer> {\n\t\tconst resolved = await resolve(rel)\n\t\tif (resolved.kind === \"literal\") {\n\t\t\treturn base.readEntrySlice(rel, start, end)\n\t\t}\n\t\tif (resolved.kind === \"materialized\") {\n\t\t\t// readEntrySlice's `end` is exclusive; readFileRange's is\n\t\t\t// inclusive.\n\t\t\treturn readFileRange(resolved.absPath, start, end - 1)\n\t\t}\n\t\t// Slice the decompressed bytes: consume the stream only up to\n\t\t// `end` so head reads (sniff/probe windows) stay bounded.\n\t\treturn readVirtualSlice(() => resolved.openStream(), start, end)\n\t}\n\n\tasync function openEntryStream(rel: string): Promise<{\n\t\treadonly stream: Readable\n\t\treadonly size: number\n\t\treadonly mtimeMs?: number\n\t\treadonly path?: string\n\t}> {\n\t\tconst resolved = await resolve(rel)\n\t\tif (resolved.kind === \"literal\") {\n\t\t\treturn base.openEntryStream(rel)\n\t\t}\n\t\tif (resolved.kind === \"materialized\") {\n\t\t\treturn {\n\t\t\t\tstream: createReadStream(resolved.absPath),\n\t\t\t\tsize: resolved.sizeBytes,\n\t\t\t\tpath: resolved.absPath,\n\t\t\t}\n\t\t}\n\t\treturn { stream: resolved.openStream(), size: resolved.entry.sizeBytes }\n\t}\n\n\tasync function readEntry(rel: string): Promise<Buffer> {\n\t\tconst resolved = await resolve(rel)\n\t\tif (resolved.kind === \"literal\") {\n\t\t\treturn base.readEntry(rel)\n\t\t}\n\t\tif (resolved.kind === \"materialized\") {\n\t\t\treturn readFile(resolved.absPath)\n\t\t}\n\t\treturn buffer(resolved.openStream())\n\t}\n\n\tasync function resolveByteRange(\n\t\trel: string,\n\t): Promise<{ readonly size: number } | undefined> {\n\t\tconst resolved = await resolve(rel)\n\t\tif (resolved.kind === \"literal\") {\n\t\t\treturn base.resolveByteRange(rel)\n\t\t}\n\t\tif (resolved.kind === \"materialized\") {\n\t\t\treturn { size: resolved.sizeBytes }\n\t\t}\n\t\treturn { size: resolved.entry.sizeBytes }\n\t}\n\n\tasync function resolveSeekablePath(rel: string): Promise<string | undefined> {\n\t\tconst resolved = await resolve(rel)\n\t\tif (resolved.kind === \"literal\") {\n\t\t\treturn base.resolveSeekablePath?.(rel)\n\t\t}\n\t\tif (resolved.kind === \"materialized\") {\n\t\t\t// Extracted files are real on-disk entries — libvips/ffmpeg\n\t\t\t// can mmap/open them directly instead of streaming.\n\t\t\treturn resolved.absPath\n\t\t}\n\t\treturn undefined\n\t}\n\n\treturn {\n\t\tlistEntries: base.listEntries,\n\t\treadEntry,\n\t\treadEntrySlice,\n\t\topenEntryStream,\n\t\tresolveByteRange,\n\t\tresolveSeekablePath,\n\t}\n}\n\n/**\n * Read `[start, end)` of an entry stream without buffering more than\n * `end` bytes. STORED and DEFLATE entries alike stream through this\n * path, so a truncated header slice never inflates the whole entry.\n */\nasync function readVirtualSlice(\n\topenStream: () => Readable,\n\tstart: number,\n\tend: number,\n): Promise<Buffer> {\n\tif (end <= start) return Buffer.alloc(0)\n\tconst chunks: Buffer[] = []\n\tlet collected = 0\n\tawait new Promise<void>((resolveDone, rejectDone) => {\n\t\tconst stream = openStream()\n\t\tstream.on(\"data\", (chunk: Buffer | string) => {\n\t\t\tconst bytes = typeof chunk === \"string\" ? Buffer.from(chunk) : chunk\n\t\t\tconst need = end - collected\n\t\t\tif (need <= 0) {\n\t\t\t\tstream.destroy()\n\t\t\t\tresolveDone()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst take = bytes.subarray(0, Math.min(need, bytes.length))\n\t\t\tchunks.push(take)\n\t\t\tcollected += take.length\n\t\t\tif (collected >= end) {\n\t\t\t\tstream.destroy()\n\t\t\t\tresolveDone()\n\t\t\t}\n\t\t})\n\t\tstream.on(\"end\", () => resolveDone())\n\t\tstream.on(\"error\", (err) => rejectDone(err))\n\t})\n\tconst full = Buffer.concat(chunks, collected)\n\tconst sliceStart = Math.min(start, full.length)\n\treturn full.subarray(sliceStart)\n}\n\n/** Resolver instance type re-export for callers that build their own views. */\nexport type { NestedResolver }\n","import { extname } from \"node:path\"\nimport type { Readable } from \"node:stream\"\nimport { IMAGE_EXTS } from \"@hoardodile/sdk-types/media-exts\"\nimport { PROBE_HEADER_BYTES } from \"../archive/index.ts\"\n\nimport type { ImageInfo } from \"../types.ts\"\n\n/**\n * Max zip entry size read into memory for thumb synthesis. Larger\n * entries are materialized to disk before sharp/ffmpeg runs.\n */\nexport const THUMB_BUFFER_MAX_BYTES = 32 * 1024 * 1024\n\n/**\n * Image probing via sharp. sharp is loaded lazily and declared as an\n * optional peer dependency: the host package stays free of native\n * binaries, and consumers that never probe images (the browser-side mock,\n * dependency-light CI installs) never pull it in. When sharp is missing,\n * probe calls fail loudly instead of degrading silently.\n */\ntype SharpModule = typeof import(\"sharp\")\n\nlet sharpPromise: Promise<SharpModule> | undefined\n\nexport function loadSharp(): Promise<SharpModule> {\n\tsharpPromise ??= import(\"sharp\").catch((err: unknown) => {\n\t\tsharpPromise = undefined\n\t\tthrow new Error(\n\t\t\t`sharp is not installed — add \"sharp\" to your dependencies to enable image probing: ${err instanceof Error ? err.message : String(err)}`,\n\t\t)\n\t})\n\treturn sharpPromise\n}\n\nexport type ImageSourceProbe = {\n\treadonly width: number\n\treadonly height: number\n\treadonly animated: boolean\n}\n\nconst ANIMATED_EXT_HINTS = new Set([\".gif\", \".webp\", \".avif\"])\n\nexport { PROBE_HEADER_BYTES } from \"../archive/index.ts\"\n\n/** True when `ext` names a format that can carry animation. */\nexport function isAnimatedCandidateExt(ext: string): boolean {\n\treturn ANIMATED_EXT_HINTS.has(ext)\n}\n\nexport type ImageMetadataInput =\n\t| string\n\t| Buffer\n\t| Readable\n\t| {\n\t\t\treadonly openStream: () => Promise<Readable>\n\t\t\t/**\n\t\t\t * Optional byte-range read into the underlying source (e.g. a\n\t\t\t * zip entry slice). When present, metadata probes read\n\t\t\t * {@link PROBE_HEADER_BYTES} from the start instead of\n\t\t\t * streaming the whole entry — libvips reads the ENTIRE file\n\t\t\t * for metadata from a non-seekable stream, but only the header\n\t\t\t * from a buffer.\n\t\t\t */\n\t\t\treadonly readRange?: (start: number, end: number) => Promise<Buffer>\n\t }\n\n/**\n * Narrow a Node stream into sharp's readable input without assertion.\n * Async because the sharp module itself is loaded lazily.\n *\n * sharp's constructor rejects `sharp(stream, opts)` — for stream input\n * the input options must be passed AS the first argument (a plain object\n * of input parameters like `pages`/`animated`) and the readable piped\n * into the resulting Duplex.\n */\nexport async function sharpFromReadable(\n\tstream: Readable,\n\toptions?: import(\"sharp\").SharpOptions,\n): Promise<import(\"sharp\").Sharp> {\n\tif (typeof stream.read !== \"function\") {\n\t\tthrow new Error(\"expected a readable stream\")\n\t}\n\tconst { default: sharp } = await loadSharp()\n\tconst descriptor =\n\t\toptions === undefined || Object.keys(options).length === 0\n\t\t\t? undefined\n\t\t\t: ({ ...options } as unknown as import(\"sharp\").SharpInput)\n\tconst instance = descriptor === undefined ? sharp() : sharp(descriptor)\n\tstream.pipe(instance)\n\treturn instance\n}\n\nfunction isReadable(input: unknown): input is Readable {\n\treturn (\n\t\ttypeof input === \"object\" &&\n\t\tinput !== null &&\n\t\ttypeof (input as Readable).pipe === \"function\" &&\n\t\t!(\"openStream\" in input)\n\t)\n}\n\nfunction isReopenableImageStream(\n\tinput: ImageMetadataInput,\n): input is { readonly openStream: () => Promise<Readable> } {\n\treturn (\n\t\ttypeof input === \"object\" &&\n\t\tinput !== null &&\n\t\t!Buffer.isBuffer(input) &&\n\t\t\"openStream\" in input &&\n\t\ttypeof (input as { openStream: unknown }).openStream === \"function\"\n\t)\n}\n\n/**\n * Sharp options for image thumb/probe reads. Enables sequential read for\n * large JPEGs on disk so libvips can shrink-on-load during resize.\n */\nexport function sharpImageInputOpts(\n\tinput: string | Buffer,\n\text: string,\n\tpages: number,\n\tanimated?: boolean,\n): import(\"sharp\").SharpOptions {\n\tconst opts: import(\"sharp\").SharpOptions = { pages }\n\tif (animated === true) opts.animated = true\n\tif (\n\t\ttypeof input === \"string\" &&\n\t\t(ext === \".jpg\" || ext === \".jpeg\" || ext === \".jfif\")\n\t) {\n\t\topts.sequentialRead = true\n\t}\n\treturn opts\n}\n\n/**\n * True when a shallow `{ pages: 1 }` metadata read is not enough to\n * decide animation and the full multi-page scan is required.\n */\nexport function needsFullAnimationScan(\n\tmeta: Pick<\n\t\timport(\"sharp\").Metadata,\n\t\t\"width\" | \"height\" | \"pages\" | \"pageHeight\"\n\t>,\n\text: string,\n): boolean {\n\tif (ext === \".gif\") return true\n\tif (ANIMATED_EXT_HINTS.has(ext) && (meta.pages ?? 1) > 1) return true\n\tif (\n\t\tmeta.pageHeight !== undefined &&\n\t\tmeta.height !== undefined &&\n\t\tmeta.pageHeight !== meta.height\n\t) {\n\t\treturn true\n\t}\n\treturn false\n}\n\n/**\n * Buffer a probe stream with a hard byte cap. Only used for formats whose\n * animation scan needs the whole stream (GIF): probing only needs\n * metadata, so an entry bigger than the cap is treated as unprobed rather\n * than buffered whole into host memory.\n */\nasync function bufferCapped(\n\tinput: Readable,\n\tmaxBytes: number,\n): Promise<Buffer> {\n\tconst chunks: Uint8Array[] = []\n\tlet total = 0\n\tfor await (const chunk of input) {\n\t\tconst bytes: Uint8Array =\n\t\t\ttypeof chunk === \"string\" ? Buffer.from(chunk) : chunk\n\t\ttotal += bytes.byteLength\n\t\tif (total > maxBytes) {\n\t\t\tinput.destroy()\n\t\t\tthrow new Error(`probe input exceeds the ${maxBytes}-byte cap`)\n\t\t}\n\t\tchunks.push(bytes)\n\t}\n\treturn Buffer.concat(chunks)\n}\n\n/**\n * Read image metadata with layered animation detection: static sources\n * stop after a single-page read; animated containers escalate to\n * `{ pages: -1 }` only when the shallow probe signals multi-frame input.\n */\nexport async function readImageMetadata(\n\tinput: ImageMetadataInput,\n\text: string,\n): Promise<{\n\treadonly meta: import(\"sharp\").Metadata\n\treadonly animated: boolean\n}> {\n\tif (isReopenableImageStream(input)) {\n\t\t// Header-slice fast path: probe a small leading window as a buffer\n\t\t// (header-only libvips read) instead of streaming the whole entry\n\t\t// (full-file read). Only definitive static results are accepted —\n\t\t// animation candidates escalate inside the slice read, and a\n\t\t// truncated buffer cannot complete a full scan, so those fall\n\t\t// through to the stream path where frame counts stay correct. GIF\n\t\t// is skipped outright (its frame count lives at the end).\n\t\tif (input.readRange !== undefined && ext !== \".gif\") {\n\t\t\tconst head = await input.readRange(0, PROBE_HEADER_BYTES)\n\t\t\tif (head.length > 0) {\n\t\t\t\ttry {\n\t\t\t\t\tconst { default: sharp } = await loadSharp()\n\t\t\t\t\tconst shallowMeta = await sharp(\n\t\t\t\t\t\thead,\n\t\t\t\t\t\tsharpImageInputOpts(head, ext, 1),\n\t\t\t\t\t).metadata()\n\t\t\t\t\tif (!needsFullAnimationScan(shallowMeta, ext)) {\n\t\t\t\t\t\treturn { meta: shallowMeta, animated: false }\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Truncated header unreadable — fall through to the\n\t\t\t\t\t// stream path.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst stream = await input.openStream()\n\t\tconst shallow = await sharpFromReadable(stream, { pages: 1 })\n\t\tconst shallowMeta = await shallow.metadata()\n\t\tif (!needsFullAnimationScan(shallowMeta, ext)) {\n\t\t\treturn { meta: shallowMeta, animated: false }\n\t\t}\n\t\tconst fullStream = await input.openStream()\n\t\tconst fullMeta = await sharpFromReadable(fullStream, {\n\t\t\tpages: -1,\n\t\t\tanimated: true,\n\t\t}).then((s) => s.metadata())\n\t\treturn { meta: fullMeta, animated: (fullMeta.pages ?? 1) > 1 }\n\t}\n\tif (isReadable(input)) {\n\t\t// GIF frame counts are not stored in the container header, so the\n\t\t// full animation scan needs the whole stream — buffer cap-guarded\n\t\t// and reuse the buffer path.\n\t\tif (ext === \".gif\") {\n\t\t\tconst data = await bufferCapped(input, THUMB_BUFFER_MAX_BYTES)\n\t\t\treturn readImageMetadata(data, ext)\n\t\t}\n\t\t// Static formats: hand the stream straight to sharp so the probe\n\t\t// reads only the header bytes. (Buffering the whole entry first —\n\t\t// the previous behavior, capped at THUMB_BUFFER_MAX_BYTES — turned\n\t\t// every stream probe into a full read and made entries beyond the\n\t\t// cap unprobeable.)\n\t\ttry {\n\t\t\tconst shallow = await sharpFromReadable(input, { pages: 1 })\n\t\t\tconst shallowMeta = await shallow.metadata()\n\t\t\tif (!needsFullAnimationScan(shallowMeta, ext)) {\n\t\t\t\treturn { meta: shallowMeta, animated: false }\n\t\t\t}\n\t\t\t// Animated candidates (webp/avif/tiff) report their total page\n\t\t\t// count from the container header, so the shallow read is\n\t\t\t// conclusive — and the stream cannot be rewound for a full\n\t\t\t// scan anyway.\n\t\t\treturn {\n\t\t\t\tmeta: shallowMeta,\n\t\t\t\tanimated: (shallowMeta.pages ?? 1) > 1,\n\t\t\t}\n\t\t} finally {\n\t\t\tinput.destroy()\n\t\t}\n\t}\n\tconst { default: sharp } = await loadSharp()\n\tconst shallow = sharp(input, sharpImageInputOpts(input, ext, 1))\n\tconst shallowMeta = await shallow.metadata()\n\tif (!needsFullAnimationScan(shallowMeta, ext)) {\n\t\treturn { meta: shallowMeta, animated: false }\n\t}\n\tconst fullMeta = await sharp(\n\t\tinput,\n\t\tsharpImageInputOpts(input, ext, -1, true),\n\t).metadata()\n\treturn { meta: fullMeta, animated: (fullMeta.pages ?? 1) > 1 }\n}\n\n/**\n * Probe an image path or buffer and return pixel dimensions plus whether\n * the source is animated. Probe failures return `undefined`.\n */\nexport async function probeImageSource(\n\tinput: ImageMetadataInput,\n\textHint?: string,\n): Promise<ImageSourceProbe | undefined> {\n\tconst ext =\n\t\textHint ?? (typeof input === \"string\" ? extname(input).toLowerCase() : \"\")\n\tif (ext.length > 0 && !IMAGE_EXTS.has(ext)) return undefined\n\ttry {\n\t\tconst { meta, animated } = await readImageMetadata(input, ext)\n\t\tconst h = meta.pageHeight ?? meta.height\n\t\tif (meta.width === undefined || h === undefined) return undefined\n\t\treturn { width: meta.width, height: h, animated }\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Probe an image source and return its pixel dimensions. Probe failures\n * return `undefined` so callers can treat \"not yet probed\" and \"probe\n * failed\" the same way.\n */\nexport async function probeImage(\n\tsource: ImageMetadataInput,\n): Promise<ImageInfo | undefined> {\n\tconst probe = await probeImageSource(source)\n\tif (probe === undefined) return undefined\n\treturn { width: probe.width, height: probe.height }\n}\n\n/**\n * True when the image has more than one frame (animated GIF / WebP / APNG /\n * AVIF). Uses layered detection so static JPEG/PNG avoid a full-frame scan.\n * Errors are coerced to `false`.\n */\nexport async function probeAnimatedImage(\n\tsource: ImageMetadataInput,\n): Promise<boolean> {\n\tif (\n\t\ttypeof source === \"string\" &&\n\t\t!IMAGE_EXTS.has(extname(source).toLowerCase())\n\t) {\n\t\treturn false\n\t}\n\ttry {\n\t\tconst probe = await probeImageSource(source)\n\t\treturn probe?.animated ?? false\n\t} catch (err) {\n\t\tconst label = typeof source === \"string\" ? source : \"stream\"\n\t\tconsole.warn(\n\t\t\t`[probeAnimatedImage] sharp failed on ${label}: ${err instanceof Error ? err.message : String(err)}`,\n\t\t)\n\t\treturn false\n\t}\n}\n","import { fileTypeFromName } from \"@hoardodile/sdk-types\"\nimport { mimeToKind } from \"@hoardodile/sdk-types/media-exts\"\n\nimport type { FileType } from \"../types.ts\"\n\n/**\n * Content sniffing: what a file's bytes say it is, with the filename as\n * the fallback rather than the verdict.\n *\n * The layering mirrors what the ecosystem settled on (freedesktop's\n * shared-mime-info, Apache Tika, WHATWG mimesniff): magic-byte matching\n * decides for binary formats, and the extension only answers for\n * formats that carry no signature at all — text, subtitles, CSV. That\n * split is why a `.jpg` holding WebP bytes probes as WebP, while a\n * `.txt` still resolves to `text/plain`.\n */\n\n/**\n * Header window handed to the magic matcher. `file-type` documents 4100\n * bytes as the sample size that makes its detection deterministic; the\n * host reads exactly that much and no more, so sniffing an entry costs\n * one small ranged read.\n */\nexport const SNIFF_HEADER_BYTES = 4100\n\ntype FileTypeModule = typeof import(\"file-type\")\n\nlet fileTypePromise: Promise<FileTypeModule> | undefined\n\n/**\n * Load the magic-byte matcher lazily. Sniffing is opt-in per host (the\n * directory backend never sniffs), so an unused host pays nothing for\n * the module graph.\n */\nfunction loadFileType(): Promise<FileTypeModule> {\n\tfileTypePromise ??= import(\"file-type\")\n\treturn fileTypePromise\n}\n\n/**\n * Identify a file from a leading byte window plus its name. `head`\n * should hold the first {@link SNIFF_HEADER_BYTES} bytes; a shorter\n * slice still works for formats whose signature fits inside it.\n */\nexport async function sniffBytes(\n\thead: Uint8Array,\n\tpath: string,\n): Promise<FileType | undefined> {\n\tif (head.byteLength > 0) {\n\t\ttry {\n\t\t\tconst { fileTypeFromBuffer } = await loadFileType()\n\t\t\tconst detected = await fileTypeFromBuffer(head)\n\t\t\tif (detected !== undefined) {\n\t\t\t\treturn {\n\t\t\t\t\tmime: detected.mime,\n\t\t\t\t\text: `.${detected.ext}`,\n\t\t\t\t\tkind: mimeToKind(detected.mime),\n\t\t\t\t\tsource: \"magic\",\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// A malformed or truncated header is not an error condition —\n\t\t\t// fall through to the filename, same as a signature-less file.\n\t\t}\n\t}\n\treturn fileTypeFromName(path)\n}\n","import { readFile, rename, rm, writeFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\n\n/**\n * Explicit file order for a resource's content root (`data/`). Uploads\n * install entries in the user's chosen order and persist that sequence\n * in this manifest so the display order matches the upload order even\n * when filenames sort differently. Written at commit time; because it\n * lives inside the replaced `data/` subtree it is always consistent\n * with the entries around it.\n *\n * The manifest is a JSON array of relative entry names (`/`-separated),\n * listing the final installed names (after collision suffixing). Reads\n * are strict: a malformed manifest, or one whose entries do not all\n * resolve against the actual listing, is ignored entirely and callers\n * fall back to the natural name sort. Strictness matters because\n * `createDirectoryContainer` also serves arbitrary user directories\n * (folder import, plugin dev) that may coincidentally contain a file\n * named `.order`.\n */\nexport const ORDER_MANIFEST_NAME = \".order\"\n\n/** Path of the order manifest inside a resource content root. */\nexport function orderManifestPath(dataDir: string): string {\n\treturn join(dataDir, ORDER_MANIFEST_NAME)\n}\n\n/**\n * Atomically persist `names` as the manifest of `dataDir` (tmp + rename,\n * so a concurrent reader never observes a half-written file).\n */\nexport async function writeOrderManifest(\n\tdataDir: string,\n\tnames: readonly string[],\n): Promise<void> {\n\tconst dest = orderManifestPath(dataDir)\n\tconst tmp = `${dest}.writing-${process.pid}-${Date.now()}`\n\ttry {\n\t\tawait writeFile(tmp, JSON.stringify(names), \"utf8\")\n\t\tawait rename(tmp, dest)\n\t} catch (err) {\n\t\tawait rm(tmp, { force: true }).catch(() => {})\n\t\tthrow err\n\t}\n}\n\n/**\n * Read the manifest of `dataDir`. Resolves to `undefined` when the\n * manifest is absent or fails structural validation\n * ({@link parseOrderManifest}) — callers fall back to natural order.\n */\nexport async function readOrderManifest(\n\tdataDir: string,\n): Promise<readonly string[] | undefined> {\n\tlet raw: string\n\ttry {\n\t\traw = await readFile(orderManifestPath(dataDir), \"utf8\")\n\t} catch {\n\t\treturn undefined\n\t}\n\treturn parseOrderManifest(raw)\n}\n\n/**\n * Structural validation of a raw manifest body: a JSON array of\n * non-empty relative paths with `/` separators that cannot escape the\n * content root. Returns `undefined` when the body is not a usable\n * manifest. Semantic validation (entries exist in the listing) happens\n * in {@link orderEntries}.\n */\nexport function parseOrderManifest(raw: string): readonly string[] | undefined {\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\treturn undefined\n\t}\n\tif (!Array.isArray(parsed) || parsed.length === 0) return undefined\n\tconst names: string[] = []\n\tfor (const entry of parsed) {\n\t\tif (typeof entry !== \"string\") return undefined\n\t\tif (!isManifestPathSafe(entry)) return undefined\n\t\tnames.push(entry)\n\t}\n\tif (names.length === 0) return undefined\n\treturn names\n}\n\n/**\n * Reorder `listed` by the manifest: entries named by the manifest come\n * first, in manifest order; entries the manifest does not mention are\n * appended in natural order. Returns `undefined` when the manifest\n * cannot be applied against this listing (some manifest entry is\n * missing — the strict trust rule), so the caller falls back to the\n * natural sort.\n */\nexport function orderEntries(\n\tmanifest: readonly string[],\n\tlisted: readonly string[],\n): readonly string[] | undefined {\n\tconst available = new Set(listed)\n\tconst uniqueManifest = new Set<string>()\n\tfor (const name of manifest) {\n\t\tif (uniqueManifest.has(name)) return undefined\n\t\tuniqueManifest.add(name)\n\t\tif (!available.has(name)) return undefined\n\t}\n\tconst ordered = manifest.filter((name) => available.has(name))\n\tconst rest = naturalSort(listed.filter((name) => !uniqueManifest.has(name)))\n\treturn [...ordered, ...rest]\n}\n\n/** Case-insensitive natural name sort (`1 < 2 < 10`, `a < b`). */\nexport function naturalSort(names: readonly string[]): readonly string[] {\n\treturn [...names].sort((a, b) =>\n\t\ta.localeCompare(b, undefined, { sensitivity: \"base\", numeric: true }),\n\t)\n}\n\nfunction isManifestPathSafe(entry: string): boolean {\n\tif (entry.length === 0 || entry.includes(\"\\0\") || entry.includes(\"\\\\\")) {\n\t\treturn false\n\t}\n\tconst segments = entry.split(\"/\")\n\tfor (const segment of segments) {\n\t\tif (segment.length === 0 || segment === \".\" || segment === \"..\") {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n","import { createReadStream } from \"node:fs\"\nimport { open, readdir, stat } from \"node:fs/promises\"\nimport { isAbsolute, join, normalize, resolve, sep } from \"node:path\"\nimport type { Readable } from \"node:stream\"\nimport type { ResourceContainer } from \"./container.ts\"\nimport {\n\tnaturalSort,\n\torderEntries,\n\treadOrderManifest,\n} from \"./hoard/order-manifest.ts\"\n\n/**\n * A {@link ResourceContainer} over a raw filesystem directory. Used to\n * run plugin hooks against a plain directory — during import, before\n * resources exist, or from the CLI. Reads only; the directory is treated\n * as an immutable snapshot for the container's lifetime.\n */\nexport function createDirectoryContainer(dir: string): ResourceContainer {\n\tasync function resolvePath(relPath: string): Promise<string> {\n\t\treturn resolveSafeImportPath(dir, relPath)\n\t}\n\n\treturn {\n\t\tasync listEntries(): Promise<readonly string[]> {\n\t\t\tconst out: string[] = []\n\t\t\tasync function collect(current: string, prefix: string): Promise<void> {\n\t\t\t\tconst entries = await readdir(join(dir, current), {\n\t\t\t\t\twithFileTypes: true,\n\t\t\t\t}).catch(() => [] as readonly never[])\n\t\t\t\tfor (const e of entries) {\n\t\t\t\t\tif (e.name.startsWith(\".\")) continue\n\t\t\t\t\tif (e.name.includes(\".uploading-\")) continue\n\t\t\t\t\t// Entry names always use `/` separators, mirroring zip\n\t\t\t\t\t// entry names, regardless of the platform's separator.\n\t\t\t\t\tconst rel = prefix ? `${prefix}/${e.name}` : e.name\n\t\t\t\t\tif (e.isDirectory()) {\n\t\t\t\t\t\tawait collect(join(current, e.name), rel)\n\t\t\t\t\t} else if (e.isFile()) {\n\t\t\t\t\t\tout.push(rel)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait collect(\".\", \"\")\n\t\t\t// An explicit order manifest wins when it validates against\n\t\t\t// this listing; otherwise fall back to the natural name sort.\n\t\t\tconst manifest = await readOrderManifest(dir)\n\t\t\tif (manifest !== undefined) {\n\t\t\t\tconst ordered = orderEntries(manifest, out)\n\t\t\t\tif (ordered !== undefined) return ordered\n\t\t\t}\n\t\t\treturn naturalSort(out)\n\t\t},\n\n\t\tasync readEntry(relPath: string): Promise<Buffer> {\n\t\t\tconst safe = await resolvePath(relPath)\n\t\t\tconst handle = await open(safe, \"r\")\n\t\t\ttry {\n\t\t\t\treturn await handle.readFile()\n\t\t\t} finally {\n\t\t\t\tawait handle.close()\n\t\t\t}\n\t\t},\n\n\t\tasync readEntrySlice(\n\t\t\trelPath: string,\n\t\t\tstart: number,\n\t\t\tend: number,\n\t\t): Promise<Buffer> {\n\t\t\tconst safe = await resolvePath(relPath)\n\t\t\tconst handle = await open(safe, \"r\")\n\t\t\ttry {\n\t\t\t\tconst { size } = await handle.stat()\n\t\t\t\tconst clampedStart = Math.min(Math.max(0, start), size)\n\t\t\t\tconst clampedEnd = Math.min(Math.max(clampedStart, end), size)\n\t\t\t\tconst length = Math.max(0, clampedEnd - clampedStart)\n\t\t\t\tif (length === 0) return Buffer.alloc(0)\n\t\t\t\tconst buf = Buffer.alloc(length)\n\t\t\t\tawait handle.read(buf, 0, length, clampedStart)\n\t\t\t\treturn buf\n\t\t\t} finally {\n\t\t\t\tawait handle.close()\n\t\t\t}\n\t\t},\n\n\t\tasync openEntryStream(relPath: string): Promise<{\n\t\t\treadonly stream: Readable\n\t\t\treadonly size: number\n\t\t\treadonly mtimeMs?: number\n\t\t\treadonly path?: string\n\t\t}> {\n\t\t\tconst safe = await resolvePath(relPath)\n\t\t\tconst info = await stat(safe)\n\t\t\treturn {\n\t\t\t\tstream: createReadStream(safe),\n\t\t\t\tsize: info.size,\n\t\t\t\tmtimeMs: info.mtimeMs,\n\t\t\t\tpath: safe,\n\t\t\t}\n\t\t},\n\n\t\tasync resolveByteRange(\n\t\t\trelPath: string,\n\t\t): Promise<{ readonly size: number } | undefined> {\n\t\t\tconst safe = await resolvePath(relPath)\n\t\t\tconst info = await stat(safe).catch(() => undefined)\n\t\t\treturn info === undefined ? undefined : { size: info.size }\n\t\t},\n\n\t\tasync resolveSeekablePath(relPath: string): Promise<string | undefined> {\n\t\t\tconst safe = await resolvePath(relPath)\n\t\t\tconst info = await stat(safe).catch(() => undefined)\n\t\t\treturn info?.isFile() === true ? safe : undefined\n\t\t},\n\t}\n}\n\n/**\n * Resolve a plugin-supplied relative path against an import directory,\n * rejecting attempts to escape the directory or use absolute paths.\n */\nexport function resolveSafeImportPath(dir: string, relPath: string): string {\n\tif (relPath.length === 0) {\n\t\tthrow new Error(\"path is empty\")\n\t}\n\tif (relPath.includes(\"\\0\")) {\n\t\tthrow new Error(\"path contains null byte\")\n\t}\n\tif (isAbsolute(relPath)) {\n\t\tthrow new Error(\"absolute paths are not allowed\")\n\t}\n\tconst normalized = normalize(relPath)\n\tif (normalized.startsWith(\"..\") || normalized === \"..\") {\n\t\tthrow new Error(\"path escapes import directory\")\n\t}\n\tconst root = resolve(dir)\n\tconst candidate = resolve(root, normalized)\n\tif (candidate !== root && !candidate.startsWith(root + sep)) {\n\t\tthrow new Error(\"path escapes import directory\")\n\t}\n\treturn candidate\n}\n","import type { PluginSchema } from \"@hoardodile/sdk-types\"\nimport { PLUGIN_READ_FILE_MAX_BYTES } from \"@hoardodile/sdk-types/plugin\"\nimport { createPluginResourceAPI } from \"./api.ts\"\nimport { createDirectoryContainer } from \"./directory-container.ts\"\nimport type { ResourceAPI } from \"./types.ts\"\n\n/**\n * Create a minimal {@link ResourceAPI} backed by a raw filesystem\n * directory. Used to run plugin hooks against a plain directory — during\n * import, before resources exist, or from the CLI. Built on the shared\n * {@link createPluginResourceAPI} so directory behavior matches the\n * archive-backed and fixture backends; probes are unsupported and resolve\n * to `undefined`.\n */\nexport function createDirectoryResourceAPI<\n\tTSchema extends PluginSchema = PluginSchema,\n>(\n\tdir: string,\n\topts: {\n\t\treadonly maxReadFileBytes?: number\n\t\t/**\n\t\t * Writable directory for `extractArchive` materialization (the\n\t\t * CLI passes a temp dir). Virtual-path reads (`outer!inner`) work\n\t\t * without it; only the materializing hook needs it.\n\t\t */\n\t\treadonly extractCacheDir?: string\n\t\t/**\n\t\t * Session context handed to hooks as `api.context.detect` (see\n\t\t * `CreatePluginResourceAPIDeps.detectContext`).\n\t\t */\n\t\treadonly detectContext?: unknown\n\t} = {},\n): ResourceAPI<TSchema> {\n\treturn createPluginResourceAPI<TSchema>({\n\t\tview: createDirectoryContainer(dir),\n\t\tmaxReadFileBytes: opts.maxReadFileBytes ?? PLUGIN_READ_FILE_MAX_BYTES,\n\t\textractCacheDir: opts.extractCacheDir,\n\t\tdetectContext: opts.detectContext,\n\t})\n}\n\nexport { resolveSafeImportPath } from \"./directory-container.ts\"\n","import type { Readable } from \"node:stream\"\nimport type {\n\tContainerListing,\n\tImageHashKind,\n\tPluginSchema,\n} from \"@hoardodile/sdk-types\"\nimport { fileTypeFromName, pluginAssetError } from \"@hoardodile/sdk-types\"\nimport { MIME_FFMPEG_INPUT_FORMAT } from \"@hoardodile/sdk-types/media-exts\"\nimport { PLUGIN_READ_FILE_MAX_BYTES } from \"@hoardodile/sdk-types/plugin\"\nimport type { ArchiveEntry, NestedCdCache } from \"./archive/index.ts\"\nimport {\n\ttype ArchiveExtractor,\n\tcreateArchiveExtractor,\n\ttype ExtractionResult,\n\ttype ExtractProgress,\n} from \"./archive/index.ts\"\nimport type { ResourceContainer } from \"./container.ts\"\nimport {\n\tcomputeDHash,\n\tcomputePHash,\n\tgrayStddev,\n\thashStream,\n\tMIN_PERCEPTUAL_STDDEV,\n\tPERCEPTUAL_HASH_KINDS,\n\tPHASH_GRID,\n} from \"./hash.ts\"\nimport { createNestedAwareContainer } from \"./nested-view.ts\"\nimport type { AvProbeOptions } from \"./probe/av.ts\"\nimport {\n\ttype ImageMetadataInput,\n\ttype ImageSourceProbe,\n\tisAnimatedCandidateExt,\n\tPROBE_HEADER_BYTES,\n\tsharpFromReadable,\n} from \"./probe/image.ts\"\nimport { SNIFF_HEADER_BYTES, sniffBytes } from \"./probe/sniff.ts\"\nimport type { PluginProbeCache } from \"./probe-cache.ts\"\nimport type {\n\tFileType,\n\tProbeResult,\n\tReadFileRange,\n\tResourceAPI,\n} from \"./types.ts\"\n\n// The directory-backed ResourceAPI is part of the host so the CLI and the\n// contract suite run the same implementation the server's import path uses.\nexport {\n\tcreateDirectoryResourceAPI as createImportResourceAPI,\n\tresolveSafeImportPath,\n} from \"./directory-api.ts\"\n\n/**\n * Construct a {@link ResourceAPI} on top of a {@link ResourceContainer}.\n * The container abstracts away the storage shape (bare-file resource\n * folder, raw directory, in-memory fixture) so plugin code stays\n * unaware of how source bytes are stored.\n */\nexport type CreatePluginResourceAPIDeps = {\n\treadonly view: ResourceContainer\n\t/**\n\t * Probe implementations, both optional: when absent the matching\n\t * branch of `probe` resolves to\n\t * `{ kind: \"unknown\", reason: \"unavailable\" }` without opening any\n\t * stream — the directory backend ships without probes by design.\n\t * `sniff` never needs a backend; it reads the file's own header.\n\t */\n\treadonly probeImage?: (\n\t\tsource: ImageMetadataInput,\n\t\textHint?: string,\n\t) => Promise<ImageSourceProbe | undefined>\n\treadonly probeAv?: (\n\t\tsource: string | Readable,\n\t\topts: AvProbeOptions,\n\t) => Promise<ProbeResult>\n\t/** Per-call `readFile` byte cap. Defaults to {@link PLUGIN_READ_FILE_MAX_BYTES}. */\n\treadonly maxReadFileBytes?: number\n\t/**\n\t * Process-wide probe cache. Only active together with\n\t * {@link cacheScope}; without it every probe opens a fresh stream.\n\t */\n\treadonly probeCache?: PluginProbeCache\n\t/**\n\t * Cache namespace for this API instance, typically\n\t * `${resId}:${fileVersion}`. Archives are immutable per version, so\n\t * cached probe results never need explicit invalidation.\n\t */\n\treadonly cacheScope?: string\n\t/**\n\t * Absolute directory for plugin container extractions (e.g. the\n\t * server's `local/cache/resources/<id>/extracted/v<N>/archives`, or a\n\t * temp dir for the CLI). When absent, `extractArchive` rejects with\n\t * a clear message.\n\t */\n\treadonly extractCacheDir?: string\n\t/**\n\t * Hard caps for one `extractArchive` call. Defaults to\n\t * {@link DEFAULT_PLUGIN_EXTRACT_MAX_BYTES} /\n\t * {@link DEFAULT_PLUGIN_EXTRACT_MAX_ENTRIES}.\n\t */\n\treadonly maxExtractBytes?: number\n\treadonly maxExtractEntries?: number\n\t/**\n\t * Called with per-entry materialization progress during\n\t * `extractArchive`, so the server can surface it to the user.\n\t */\n\treadonly onExtractProgress?: (progress: ExtractProgress) => void\n\t/**\n\t * Process-wide nested central-directory cache. When provided, virtual\n\t * path resolution (`outer!inner`) reuses parsed listings across hook\n\t * calls instead of re-reading each archive's CD per invocation. The\n\t * server passes one cache per (resId, fileVersion) scope.\n\t */\n\treadonly nestedCdCache?: NestedCdCache\n\t/**\n\t * Session context handed to hooks as `api.context.detect`: the\n\t * payload a prior `detect` invocation returned on a successful\n\t * match. The sandbox worker injects it itself; this escape hatch\n\t * lets in-process hosts (the dev runner) do the same.\n\t */\n\treadonly detectContext?: unknown\n}\n\nexport const DEFAULT_PLUGIN_EXTRACT_MAX_BYTES = 8 * 1024 * 1024 * 1024\nexport const DEFAULT_PLUGIN_EXTRACT_MAX_ENTRIES = 200_000\n\nexport function createPluginResourceAPI<\n\tTSchema extends PluginSchema = PluginSchema,\n>(deps: CreatePluginResourceAPIDeps): ResourceAPI<TSchema> {\n\t// Virtual paths (`outer!inner`) resolve through this wrapper, so\n\t// readFile/sniff/probe/stat/hash all reach into nested containers.\n\t// The cacheScope disambiguates nested-cache keys when one shared\n\t// cache serves many resources (see createNestedAwareContainer); the\n\t// extract cache dir extends virtual addressing to *extracted*\n\t// archives (non-zip formats become addressable once materialized).\n\tconst view = createNestedAwareContainer(\n\t\tdeps.view,\n\t\tdeps.nestedCdCache,\n\t\tdeps.cacheScope,\n\t\tdeps.extractCacheDir,\n\t)\n\tconst maxReadFileBytes = deps.maxReadFileBytes ?? PLUGIN_READ_FILE_MAX_BYTES\n\n\tconst extractor: ArchiveExtractor | undefined =\n\t\tdeps.extractCacheDir === undefined\n\t\t\t? undefined\n\t\t\t: createArchiveExtractor({\n\t\t\t\t\touter: {\n\t\t\t\t\t\tsizeOf: (rel) =>\n\t\t\t\t\t\t\tdeps.view.resolveByteRange(rel).then((r) => r?.size),\n\t\t\t\t\t\treadSlice: (rel, start, end) =>\n\t\t\t\t\t\t\tdeps.view.readEntrySlice(rel, start, end),\n\t\t\t\t\t},\n\t\t\t\t\tcacheDir: deps.extractCacheDir,\n\t\t\t\t\tmaxBytes: deps.maxExtractBytes ?? DEFAULT_PLUGIN_EXTRACT_MAX_BYTES,\n\t\t\t\t\tmaxEntries:\n\t\t\t\t\t\tdeps.maxExtractEntries ?? DEFAULT_PLUGIN_EXTRACT_MAX_ENTRIES,\n\t\t\t\t\tprobeImage: deps.probeImage,\n\t\t\t\t\tonProgress: deps.onExtractProgress,\n\t\t\t\t\tnestedCdCache: deps.nestedCdCache,\n\t\t\t\t})\n\n\tasync function readFileScoped(\n\t\tpath: string,\n\t\trange?: ReadFileRange,\n\t): Promise<Uint8Array> {\n\t\tif (range === undefined) {\n\t\t\tconst size = (await view.resolveByteRange(path))?.size\n\t\t\tif (size !== undefined) assertReadSize(path, size, maxReadFileBytes)\n\t\t\tconst buf = await view.readEntry(path)\n\t\t\treturn new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)\n\t\t}\n\t\tconst start = Math.max(0, range.start ?? 0)\n\t\tconst size = (await view.resolveByteRange(path))?.size\n\t\tif (size === undefined) {\n\t\t\t// Unknown size (missing entry) — let the view raise its own error.\n\t\t\treturn toUint8Array(\n\t\t\t\tawait view.readEntrySlice(path, start, range.end ?? start),\n\t\t\t)\n\t\t}\n\t\tconst end = Math.min(range.end ?? size, size)\n\t\tassertReadSize(path, Math.max(0, end - start), maxReadFileBytes)\n\t\treturn toUint8Array(await view.readEntrySlice(path, start, end))\n\t}\n\n\tasync function listFileNamesScoped(): Promise<readonly string[]> {\n\t\treturn view.listEntries()\n\t}\n\n\t/**\n\t * Run `compute` through the shared probe cache when configured. The\n\t * cache key carries the computation kind — sniffing an entry and\n\t * decoding it are different results for the same path.\n\t */\n\tfunction cached<T extends object | boolean | undefined>(\n\t\tkind: string,\n\t\tpath: string,\n\t\tcompute: () => Promise<T>,\n\t): Promise<T> {\n\t\tif (deps.probeCache === undefined || deps.cacheScope === undefined) {\n\t\t\treturn compute()\n\t\t}\n\t\treturn deps.probeCache.getOrCompute(\n\t\t\t`${deps.cacheScope}:${kind}:${path}`,\n\t\t\tcompute,\n\t\t)\n\t}\n\n\tfunction sniffScoped(path: string): Promise<FileType | undefined> {\n\t\treturn cached(\"sniff\", path, async () => {\n\t\t\t// A missing or unreadable entry has no type — identification\n\t\t\t// never rejects, so callers can sniff freely while walking a\n\t\t\t// file list that may be stale.\n\t\t\tconst head = await view\n\t\t\t\t.readEntrySlice(path, 0, SNIFF_HEADER_BYTES)\n\t\t\t\t.catch(() => undefined)\n\t\t\tif (head === undefined) return undefined\n\t\t\treturn sniffBytes(head, path)\n\t\t})\n\t}\n\n\t/**\n\t * Image metadata plus animation in one pass. Static formats answer\n\t * from a header slice (libvips reads the ENTIRE entry for metadata\n\t * from a stream, but only the header from a buffer); animation\n\t * candidates (gif/webp/avif) must stream, because a truncated buffer\n\t * cannot complete the frame scan and would report an animated source\n\t * as static.\n\t */\n\tasync function probeImageEntry(\n\t\tpath: string,\n\t\ttype: FileType,\n\t): Promise<ProbeResult> {\n\t\tconst probeImage = deps.probeImage\n\t\tif (probeImage === undefined) {\n\t\t\treturn { kind: \"unknown\", reason: \"unavailable\" }\n\t\t}\n\t\tif (!isAnimatedCandidateExt(type.ext)) {\n\t\t\tconst head = await view.readEntrySlice(path, 0, PROBE_HEADER_BYTES)\n\t\t\tif (head.length > 0) {\n\t\t\t\tconst probed = await probeImage(head, type.ext).catch(() => undefined)\n\t\t\t\tif (probed !== undefined) return imageResult(type.mime, probed)\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tconst { stream } = await view.openEntryStream(path)\n\t\t\tconst probed = await probeImage(stream, type.ext)\n\t\t\tif (probed !== undefined) return imageResult(type.mime, probed)\n\t\t} catch (err) {\n\t\t\tconsole.warn(\n\t\t\t\t`[probe] path=${path} image probe threw: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t)\n\t\t}\n\t\treturn { kind: \"unknown\", reason: \"failed\" }\n\t}\n\n\t/**\n\t * Audio/video metadata. The ffmpeg container hint comes from the\n\t * sniffed MIME type rather than the filename, so a mislabelled entry\n\t * still demuxes; ISO-BMFF sources keep their index at the end of the\n\t * file, so a pipe probe can legitimately fail and resolve to\n\t * `{ kind: \"unknown\", reason: \"failed\" }`.\n\t */\n\tasync function probeAvEntry(\n\t\tpath: string,\n\t\ttype: FileType,\n\t): Promise<ProbeResult> {\n\t\tconst probeAv = deps.probeAv\n\t\tif (probeAv === undefined) {\n\t\t\treturn { kind: \"unknown\", reason: \"unavailable\" }\n\t\t}\n\t\tconst inputFormat = MIME_FFMPEG_INPUT_FORMAT[type.mime]\n\t\tif (inputFormat === undefined) {\n\t\t\treturn { kind: \"unknown\", reason: \"unsupported\" }\n\t\t}\n\t\ttry {\n\t\t\tconst { stream } = await view.openEntryStream(path)\n\t\t\treturn await probeAv(stream, { mime: type.mime, inputFormat })\n\t\t} catch {\n\t\t\treturn { kind: \"unknown\", reason: \"failed\" }\n\t\t}\n\t}\n\n\tfunction probeScoped(path: string): Promise<ProbeResult> {\n\t\treturn cached(\"probe\", path, async () => {\n\t\t\tconst type = await sniffScoped(path)\n\t\t\tif (type === undefined) {\n\t\t\t\treturn { kind: \"unknown\", reason: \"unsupported\" } as const\n\t\t\t}\n\t\t\tswitch (type.kind) {\n\t\t\t\tcase \"image\":\n\t\t\t\t\treturn probeImageEntry(path, type)\n\t\t\t\tcase \"video\":\n\t\t\t\tcase \"audio\":\n\t\t\t\t\treturn probeAvEntry(path, type)\n\t\t\t\tdefault:\n\t\t\t\t\treturn { kind: \"other\", mime: type.mime } as const\n\t\t\t}\n\t\t})\n\t}\n\n\tasync function statFileScoped(\n\t\tpath: string,\n\t): Promise<{ readonly sizeBytes: number } | undefined> {\n\t\tconst range = await view.resolveByteRange(path)\n\t\tif (range === undefined) return undefined\n\t\treturn { sizeBytes: range.size }\n\t}\n\n\tasync function statFilesScoped(\n\t\tpaths: readonly string[],\n\t): Promise<readonly ({ readonly sizeBytes: number } | undefined)[]> {\n\t\t// Batch resolution: the zip CD cache makes every range lookup a\n\t\t// warm map hit; parallelizing keeps the fan-out bounded by the\n\t\t// plugin's own concurrency when it chunks the input.\n\t\treturn Promise.all(paths.map(statFileScoped))\n\t}\n\n\tasync function hashBytesScoped(\n\t\tpath: string,\n\t\talgo: \"md5\" | \"sha256\",\n\t): Promise<string> {\n\t\tconst { stream } = await view.openEntryStream(path)\n\t\treturn hashStream(stream, algo)\n\t}\n\n\tasync function computeImageHashesScoped(\n\t\tpath: string,\n\t\tkinds: readonly ImageHashKind[],\n\t): Promise<Readonly<Record<ImageHashKind, string>> | undefined> {\n\t\t// Hashing follows the content, not the name: an image with a\n\t\t// wrong extension is still a duplicate of itself.\n\t\tconst type = await sniffScoped(path)\n\t\tif (type?.kind !== \"image\") return undefined\n\t\tconst result: Partial<Record<ImageHashKind, string>> = {}\n\t\tif (kinds.includes(\"sha256\")) {\n\t\t\tconst { stream } = await view.openEntryStream(path)\n\t\t\tresult.sha256 = await hashStream(stream, \"sha256\")\n\t\t}\n\t\tconst perceptual = kinds.filter((kind) =>\n\t\t\tPERCEPTUAL_HASH_KINDS.includes(kind),\n\t\t)\n\t\tif (perceptual.length > 0) {\n\t\t\tconst { stream } = await view.openEntryStream(path)\n\t\t\tconst gray = await decodeGrayGrid(stream)\n\t\t\tif (gray !== undefined && grayStddev(gray) >= MIN_PERCEPTUAL_STDDEV) {\n\t\t\t\tif (perceptual.includes(\"dhash\")) result.dhash = computeDHash(gray)\n\t\t\t\tif (perceptual.includes(\"phash\")) result.phash = computePHash(gray)\n\t\t\t} else if (result.sha256 === undefined) {\n\t\t\t\t// Not a decodable image (or too flat for a meaningful\n\t\t\t\t// perceptual hash) and no byte hash requested.\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t}\n\t\treturn result as Readonly<Record<ImageHashKind, string>>\n\t}\n\n\t/**\n\t * Decode one frame of the entry as a `PHASH_GRID × PHASH_GRID`\n\t * grayscale buffer. Animated sources render their first frame\n\t * (sharp's default with `pages: 1`). Undecodable input resolves to\n\t * `undefined`.\n\t */\n\tasync function decodeGrayGrid(\n\t\tstream: Readable,\n\t): Promise<Uint8Array | undefined> {\n\t\ttry {\n\t\t\tconst instance = await sharpFromReadable(stream, { pages: 1 })\n\t\t\tconst { data } = await instance\n\t\t\t\t.resize(PHASH_GRID, PHASH_GRID, { fit: \"fill\" })\n\t\t\t\t.grayscale()\n\t\t\t\t.raw()\n\t\t\t\t.toBuffer({ resolveWithObject: true })\n\t\t\treturn new Uint8Array(data.buffer, data.byteOffset, data.byteLength)\n\t\t} catch {\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\treturn {\n\t\t// No-ops: the sandbox host is the plugin log sink — it alone knows\n\t\t// which plugin emitted the line (see dispatchLog in sandbox/host.ts).\n\t\tlogInfo() {},\n\t\tlogWarn() {},\n\t\tlogError() {},\n\t\t// The payload is opaque to this factory (hosts pass `unknown`);\n\t\t// the schema generic only types it for schema-aware consumers.\n\t\tcontext: { detect: deps.detectContext as TSchema[\"detect\"] | undefined },\n\t\tlistFileNames: listFileNamesScoped,\n\t\treadFile: readFileScoped,\n\t\tstatFile: statFileScoped,\n\t\tstatFiles: statFilesScoped,\n\t\tsniff: sniffScoped,\n\t\tprobe: probeScoped,\n\t\thashBytes: hashBytesScoped,\n\t\tcomputeImageHashes: computeImageHashesScoped,\n\t\tlistContainer: (filename) => listContainerScoped(filename, extractor),\n\t\textractArchive: (filename) => extractArchiveScoped(filename, extractor),\n\t\t// The plugin vault is a sandbox-level capability: the sandbox host\n\t\t// intercepts these method names and routes them to the wired\n\t\t// plugin-asset service with the owning plugin id (see\n\t\t// sandbox/host.ts). In-process hosts (directory API, contract\n\t\t// fixtures, dev runner) have no consent channel, so the methods\n\t\t// answer UNAVAILABLE — exactly like the CLI and the workbench.\n\t\tdownload: async () => {\n\t\t\tthrow pluginAssetError(\n\t\t\t\t\"UNAVAILABLE\",\n\t\t\t\t\"download() — this host has no plugin asset service; only the app server host can download into the plugin vault\",\n\t\t\t)\n\t\t},\n\t\tstatAsset: async () => {\n\t\t\tthrow unavailableAsset(\"statAsset\")\n\t\t},\n\t\treadAsset: async () => {\n\t\t\tthrow unavailableAsset(\"readAsset\")\n\t\t},\n\t\tdeleteAsset: async () => {\n\t\t\tthrow unavailableAsset(\"deleteAsset\")\n\t\t},\n\t}\n}\n\nfunction unavailableAsset(method: string): Error {\n\treturn pluginAssetError(\n\t\t\"UNAVAILABLE\",\n\t\t`${method}() — this host has no plugin asset vault; only the app server host manages vault files`,\n\t)\n}\n\n/**\n * List a container entry's files without materializing — metadata-only\n * consumers (detect, card counts) use this so a preview never pays for\n * extraction it does not need.\n */\nasync function listContainerScoped(\n\tfilename: string,\n\textractor: ArchiveExtractor | undefined,\n): Promise<ContainerListing> {\n\tif (extractor === undefined) {\n\t\tthrow new Error(\n\t\t\t`listContainer(\"${filename}\") — this host has no container support; only the archive-backed server host lists containers`,\n\t\t)\n\t}\n\tconst entries = await extractor.list(filename)\n\tif (entries === undefined) {\n\t\tthrow new Error(\n\t\t\t`listContainer(\"${filename}\") — not a supported archive (zip/tar/7z/rar/xz), or no 7-Zip binary for non-zip formats`,\n\t\t)\n\t}\n\treturn { entries: entries.map(bareContainerEntry) }\n}\n\n/** Convert a listed entry to the wire shape (no dimensions). */\nfunction bareContainerEntry(\n\tentry: ArchiveEntry,\n): ContainerListing[\"entries\"][number] {\n\treturn {\n\t\tpath: entry.name,\n\t\tsizeBytes: entry.sizeBytes,\n\t\tkind: fileTypeFromName(entry.name)?.kind ?? \"other\",\n\t}\n}\n\n/**\n * Materialize the container entry `filename` into the configured cache\n * directory (or list its contents without writing in read-only mode).\n * Rejects when this API instance has no extraction cache wired.\n */\nasync function extractArchiveScoped(\n\tfilename: string,\n\textractor: ArchiveExtractor | undefined,\n): Promise<ExtractionResult> {\n\tif (extractor === undefined) {\n\t\tthrow new Error(\n\t\t\t`extractArchive(\"${filename}\") — this host has no extraction cache; only the archive-backed server host materializes containers`,\n\t\t)\n\t}\n\treturn extractor.extract(filename)\n}\n\n/** Assemble the image branch of a {@link ProbeResult}. */\nfunction imageResult(mime: string, probed: ImageSourceProbe): ProbeResult {\n\treturn {\n\t\tkind: \"image\",\n\t\tmime,\n\t\twidth: probed.width,\n\t\theight: probed.height,\n\t\tanimated: probed.animated,\n\t}\n}\n\nfunction assertReadSize(path: string, sizeBytes: number, max: number): void {\n\tif (sizeBytes > max) {\n\t\tthrow new Error(\n\t\t\t`readFile(\"${path}\") requests ${sizeBytes} bytes, exceeding the per-call limit of ${max} bytes — pass a byte range or use readFileChunks()`,\n\t\t)\n\t}\n}\n\nfunction toUint8Array(buf: Buffer): Uint8Array {\n\treturn new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)\n}\n","import type { PluginManifest } from \"@hoardodile/sdk-types\"\nimport type {\n\tPluginCapabilityGate,\n\tPluginCapabilityKey,\n} from \"@hoardodile/sdk-types/plugin-capabilities\"\nimport { PLUGIN_CAPABILITY_GATES } from \"@hoardodile/sdk-types/plugin-capabilities\"\nimport type { PluginRegistryEntry } from \"./api-types.ts\"\n\n/**\n * Capabilities a plugin manifest can declare — derived from the single\n * {@link PLUGIN_CAPABILITY_GATES} table (which is itself keyed by the\n * manifest's `PluginPermissions` keys), so the guard can never drift.\n */\nexport type PluginCapability = PluginCapabilityKey\n\n/** Gate metadata a consumer may want (e.g. for tooltips). */\nexport type { PluginCapabilityGate }\n\n/**\n * Permission checks against a plugin's declared manifest permissions,\n * reading the capability vocabulary from the shared gates table.\n * The server uses these before routing any plugin-scoped work so a\n * manifest that does not declare a capability cannot trigger it.\n */\nexport type CapabilityGuard = {\n\t/** Check whether a manifest grants the given capability. */\n\treadonly check: (\n\t\tmanifest: PluginManifest,\n\t\tcapability: PluginCapability,\n\t) => boolean\n\t/** Assert that a manifest grants the given capability; throw if not. */\n\treadonly require: (\n\t\tmanifest: PluginManifest,\n\t\tcapability: PluginCapability,\n\t) => void\n\t/** Filter entries to only those that grant the given capability. */\n\treadonly filter: (\n\t\tentries: readonly PluginRegistryEntry[],\n\t\tcapability: PluginCapability,\n\t) => readonly PluginRegistryEntry[]\n}\n\n/** Create a stateless capability guard over manifest permissions. */\nexport function createCapabilityGuard(): CapabilityGuard {\n\tfunction check(\n\t\tmanifest: PluginManifest,\n\t\tcapability: PluginCapability,\n\t): boolean {\n\t\treturn manifest.permissions[capability] === true\n\t}\n\n\tfunction require(\n\t\tmanifest: PluginManifest,\n\t\tcapability: PluginCapability,\n\t): void {\n\t\tif (!check(manifest, capability)) {\n\t\t\tthrow new Error(\n\t\t\t\t`${capability} permission denied for plugin ${manifest.id}`,\n\t\t\t)\n\t\t}\n\t}\n\n\tfunction filter(\n\t\tentries: readonly PluginRegistryEntry[],\n\t\tcapability: PluginCapability,\n\t): readonly PluginRegistryEntry[] {\n\t\treturn entries.filter((e) => check(e.manifest, capability))\n\t}\n\n\treturn { check, require, filter }\n}\n\nexport { PLUGIN_CAPABILITY_GATES }\n","import { pathToFileURL } from \"node:url\"\nimport { createDirectoryResourceAPI } from \"./directory-api.ts\"\nimport type { HookName } from \"./sandbox/protocol.ts\"\n\n/**\n * Plugin hooks the dev runner can invoke against a directory — the\n * contract-order list shared with the sandbox host and the CLI.\n */\nexport type PluginHookName = HookName\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null\n}\n\n/**\n * Load a built plugin `main.js` and invoke one hook with a\n * directory-backed {@link ResourceAPI}. Development aid only — the plugin\n * runs unsandboxed in the current process, so only run code you trust.\n * Consumed from plugin tests as a devDependency; never bundled into a\n * shipped plugin.\n *\n * Before the target hook runs, `detect` is invoked once (when the\n * target is not detect itself) so hooks see the session context a real\n * sandbox session would have — `api.context.detect` carries the\n * detection payload. A throwing or missed detection leaves the context\n * absent, exactly like a fresh worker in production.\n */\nexport async function runPluginHook(opts: {\n\treadonly mainPath: string\n\treadonly hook: PluginHookName\n\treadonly dir: string\n\t/** Scratch dir for `extractArchive` materialization. */\n\treadonly extractCacheDir?: string\n}): Promise<{ readonly result: unknown; readonly durationMs: number }> {\n\tconst mod: unknown = await import(pathToFileURL(opts.mainPath).href)\n\tif (!isRecord(mod) || !isRecord(mod.default)) {\n\t\tthrow new Error(\n\t\t\t`Plugin at ${opts.mainPath} must default-export a plugin definition`,\n\t\t)\n\t}\n\tconst hook = mod.default[opts.hook]\n\tif (typeof hook !== \"function\") {\n\t\tthrow new Error(\n\t\t\t`Plugin at ${opts.mainPath} has no \"${opts.hook}\" hook (expected a function on the default export)`,\n\t\t)\n\t}\n\n\tlet detectContext: unknown\n\tif (opts.hook !== \"detect\" && typeof mod.default.detect === \"function\") {\n\t\ttry {\n\t\t\tconst detected = await mod.default.detect(\n\t\t\t\tcreateDirectoryResourceAPI(opts.dir, {\n\t\t\t\t\textractCacheDir: opts.extractCacheDir,\n\t\t\t\t}),\n\t\t\t)\n\t\t\tif (isRecord(detected) && detected.ok === true) {\n\t\t\t\tconst { ok: _ok, ...payload } = detected\n\t\t\t\tif (Object.keys(payload).length > 0) detectContext = payload\n\t\t\t}\n\t\t} catch {\n\t\t\t// A throwing detector leaves the context absent — same as a\n\t\t\t// fresh worker in production.\n\t\t}\n\t}\n\n\tconst api = createDirectoryResourceAPI(opts.dir, {\n\t\textractCacheDir: opts.extractCacheDir,\n\t\tdetectContext,\n\t})\n\tconst started = performance.now()\n\tconst result: unknown = await hook(api)\n\treturn { result, durationMs: performance.now() - started }\n}\n","import { existsSync, readdirSync, readFileSync } from \"node:fs\"\nimport { join, resolve } from \"node:path\"\nimport type { PluginManifest, PluginManifestId } from \"@hoardodile/sdk-types\"\nimport { pluginManifest as pluginManifestSchema } from \"@hoardodile/sdk-types/schema\"\nimport type { FoundPlugin, MissingPlugin } from \"./api-types.ts\"\nimport type { PluginSettingsStore } from \"./settings-store.ts\"\n\n/**\n * Default pin for plugins with no settings row yet (first install /\n * clean state / builtin). Pinned plugins wear their name on resource\n * cards so the type stays readable without a cover.\n */\nconst DEFAULT_PLUGIN_PINNED = true\n\n/** Default first-install priority — reorderable from the UI afterwards. */\nconst DEFAULT_PLUGIN_PRIORITY = 100\n\nexport type PluginDiscoveryDeps = {\n\t/** Directory of the mandatory builtin plugin (`manifest.json` at its root). */\n\treadonly builtinDir?: string\n\t/** Extra plugin directories for development (override same-id disk plugins). */\n\treadonly devPluginDirs?: readonly string[]\n\t/** Directory where installed plugins live, one subdirectory per plugin. */\n\treadonly pluginsDir: string\n\t/** DB-backed per-plugin settings (enabled/priority/pinned/color). */\n\treadonly settings: PluginSettingsStore\n\t/** Skip dev plugins entirely (e.g. production configuration). */\n\treadonly disableDevPlugins?: boolean\n}\n\n/**\n * Plugin discovery: finds plugins on disk (builtin, dev, installed),\n * merges their DB settings, and reports which known plugins are missing.\n */\nexport type PluginDiscovery = {\n\treadonly discover: () => Promise<{\n\t\tfound: FoundPlugin[]\n\t\tmissing: MissingPlugin[]\n\t}>\n}\n\n/**\n * Create the discovery pipeline. Order matters: the builtin plugin wins\n * priority, dev plugins override same-id installed ones, and anything in\n * settings but absent from disk is reported missing.\n */\nexport function createPluginDiscovery(\n\tdeps: PluginDiscoveryDeps,\n): PluginDiscovery {\n\t// The first loadAll runs at boot: a broken builtin there is a config\n\t// error that should fail fast. Later (re)scans degrade gracefully —\n\t// the loader keeps the previous registry when discovery throws.\n\tlet firstLoad = true\n\n\tasync function discover(): Promise<{\n\t\tfound: FoundPlugin[]\n\t\tmissing: MissingPlugin[]\n\t}> {\n\t\tconst found: FoundPlugin[] = []\n\t\t// Every discovered id, across all sources. Guarantees each plugin\n\t\t// id is activated exactly once — a duplicate would make activation\n\t\t// dispose the first worker while the registry still references it\n\t\t// (every hook then fails with \"sandbox disposed\").\n\t\tconst seen = new Set<PluginManifestId>()\n\n\t\t// 1. Builtin plugin — its id is reserved; dev/disk copies are skipped.\n\t\tif (deps.builtinDir !== undefined) {\n\t\t\tconst builtin = discoverBuiltin(deps.builtinDir, firstLoad)\n\t\t\tif (builtin !== undefined) {\n\t\t\t\tfound.push(builtin)\n\t\t\t\tseen.add(builtin.id)\n\t\t\t}\n\t\t}\n\t\tfirstLoad = false\n\n\t\t// 2. Dev plugins. Same-id dev dirs override each other (later wins,\n\t\t// matching the dev-over-disk precedence); the earlier entry is\n\t\t// replaced so activation never loads the same id twice.\n\t\tconst dev = discoverDevPlugins(\n\t\t\tdeps.devPluginDirs,\n\t\t\tdeps.settings,\n\t\t\tdeps.disableDevPlugins ?? false,\n\t\t)\n\t\tfor (const plugin of dev) {\n\t\t\tif (seen.has(plugin.id)) {\n\t\t\t\tconst index = found.findIndex((f) => f.id === plugin.id)\n\t\t\t\tif (index !== -1 && found[index]?.source === \"dev\") {\n\t\t\t\t\t// Duplicate dev dir — the later one wins.\n\t\t\t\t\tfound[index] = plugin\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[plugin-discovery] dev plugin ${plugin.id} (${plugin.manifest.name ?? plugin.id}) overrides an earlier dev dir entry`,\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[plugin-discovery] skipping dev plugin ${plugin.id}: id is reserved by the builtin plugin`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfound.push(plugin)\n\t\t\tseen.add(plugin.id)\n\t\t}\n\n\t\t// 3. Disk plugins, skipping ids overridden by dev or the builtin.\n\t\tconst disk = discoverDiskPlugins(deps.pluginsDir, deps.settings)\n\t\tfor (const plugin of disk) {\n\t\t\tif (seen.has(plugin.id)) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[plugin-discovery] skipping disk plugin ${plugin.id}: id already registered (builtin or dev plugin)`,\n\t\t\t\t)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfound.push(plugin)\n\t\t\tseen.add(plugin.id)\n\t\t}\n\n\t\t// 4. Missing plugins (settings-only)\n\t\tconst missing = discoverMissingPlugins(deps.settings, seen)\n\n\t\treturn { found, missing }\n\t}\n\n\treturn { discover }\n}\n\nfunction discoverBuiltin(\n\tbuiltinDir: string,\n\tfailFast: boolean,\n): FoundPlugin | undefined {\n\tconst resolved = resolve(builtinDir)\n\tconst manifest = parseManifest(resolved, \"builtin\")\n\tif (manifest === undefined) {\n\t\tconst message =\n\t\t\t`Builtin plugin not found or invalid at ${resolved}. ` +\n\t\t\t\"Set BUILTIN_PATH env to a valid plugin directory.\"\n\t\tif (failFast) {\n\t\t\tthrow new Error(message)\n\t\t}\n\t\tconsole.error(`[plugin-discovery] ${message}`)\n\t\treturn undefined\n\t}\n\treturn {\n\t\tid: manifest.id,\n\t\tmanifest,\n\t\tdiskPath: resolved,\n\t\tsource: \"builtin\",\n\t\tenabled: true,\n\t\tpriority: Number.MAX_SAFE_INTEGER,\n\t\tpinned: DEFAULT_PLUGIN_PINNED,\n\t\tcolor: \"\",\n\t}\n}\n\nfunction discoverDevPlugins(\n\tdevPluginDirs: readonly string[] | undefined,\n\tsettings: PluginSettingsStore,\n\tdisableDevPlugins: boolean,\n): FoundPlugin[] {\n\tif (disableDevPlugins) {\n\t\tconsole.info(\"[plugin-discovery] dev plugins disabled by configuration\")\n\t\treturn []\n\t}\n\tif (devPluginDirs === undefined || devPluginDirs.length === 0) return []\n\n\tconst results: FoundPlugin[] = []\n\tfor (const dir of devPluginDirs) {\n\t\tconst resolved = resolve(dir)\n\t\tconst manifest = parseManifest(resolved, \"dev\")\n\t\tif (manifest === undefined) continue\n\n\t\tconst row = settings.get(manifest.id)\n\n\t\tresults.push({\n\t\t\tid: manifest.id,\n\t\t\tmanifest,\n\t\t\tdiskPath: resolved,\n\t\t\tsource: \"dev\",\n\t\t\tenabled: true,\n\t\t\tpriority: row?.priority ?? DEFAULT_PLUGIN_PRIORITY,\n\t\t\tpinned: row?.pinned ?? DEFAULT_PLUGIN_PINNED,\n\t\t\tcolor: row?.color ?? \"\",\n\t\t})\n\t}\n\treturn results\n}\n\nfunction discoverDiskPlugins(\n\tpluginsDir: string,\n\tsettings: PluginSettingsStore,\n): FoundPlugin[] {\n\tif (!existsSync(pluginsDir)) return []\n\n\tconst dirents = readdirSync(pluginsDir, { withFileTypes: true })\n\tif (dirents.length === 0) return []\n\n\tconst results: FoundPlugin[] = []\n\tfor (const dirent of dirents) {\n\t\tif (!dirent.isDirectory()) continue\n\t\tconst dirPath = join(pluginsDir, dirent.name)\n\n\t\tconst manifest = parseManifest(dirPath, dirent.name)\n\t\tif (manifest === undefined) continue\n\n\t\tconst row = settings.get(manifest.id)\n\t\tconst enabled = row?.enabled ?? true\n\t\tconst priority = row?.priority ?? DEFAULT_PLUGIN_PRIORITY\n\t\tconst pinned = row?.pinned ?? DEFAULT_PLUGIN_PINNED\n\t\tconst color = row?.color ?? \"\"\n\n\t\tresults.push({\n\t\t\tid: manifest.id,\n\t\t\tmanifest,\n\t\t\tdiskPath: dirPath,\n\t\t\tsource: \"disk\",\n\t\t\tenabled,\n\t\t\tpriority,\n\t\t\tpinned,\n\t\t\tcolor,\n\t\t})\n\t}\n\treturn results\n}\n\nfunction discoverMissingPlugins(\n\tsettings: PluginSettingsStore,\n\tloadedIds: ReadonlySet<PluginManifestId>,\n): MissingPlugin[] {\n\tconst results: MissingPlugin[] = []\n\tfor (const row of settings.all()) {\n\t\tif (loadedIds.has(row.id)) continue\n\t\tlet manifest: PluginManifest\n\t\ttry {\n\t\t\tmanifest = JSON.parse(row.manifest) as PluginManifest\n\t\t} catch {\n\t\t\tcontinue\n\t\t}\n\t\tresults.push({\n\t\t\tid: row.id,\n\t\t\tmanifest,\n\t\t\tenabled: row.enabled,\n\t\t\tpriority: row.priority,\n\t\t\tpinned: row.pinned,\n\t\t\tcolor: row.color,\n\t\t})\n\t}\n\treturn results\n}\n\n/**\n * Read and validate `manifest.json` in `dirPath` against the zod\n * contract. Logs a warning and returns `undefined` for every failure\n * mode (missing file, unreadable, invalid JSON, schema violation) so\n * callers can skip the directory without crashing the scan.\n */\nexport function parseManifest(\n\tdirPath: string,\n\tdirName: string,\n): PluginManifest | undefined {\n\tconst manifestPath = join(dirPath, \"manifest.json\")\n\tif (!existsSync(manifestPath)) {\n\t\tconsole.warn(`[plugin-discovery] skipping ${dirName}: no manifest.json`)\n\t\treturn undefined\n\t}\n\n\tlet raw: string\n\ttry {\n\t\traw = readFileSync(manifestPath, \"utf-8\")\n\t} catch {\n\t\tconsole.warn(\n\t\t\t`[plugin-discovery] skipping ${dirName}: cannot read manifest.json`,\n\t\t)\n\t\treturn undefined\n\t}\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tconsole.warn(\n\t\t\t`[plugin-discovery] skipping ${dirName}: manifest is not valid JSON`,\n\t\t)\n\t\treturn undefined\n\t}\n\n\tconst result = pluginManifestSchema.safeParse(parsed)\n\tif (!result.success) {\n\t\tconsole.warn(\n\t\t\t`[plugin-discovery] skipping ${dirName}: invalid manifest`,\n\t\t\tresult.error,\n\t\t)\n\t\treturn undefined\n\t}\n\n\treturn result.data\n}\n","import { pluginAssetError } from \"@hoardodile/sdk-types\"\nimport type { ResourceAPI } from \"./types.ts\"\n\n/**\n * The ResourceAPI handed to a plugin's `onInstall` hook: there is no\n * resource attached, so the file surface answers empty (or throws a\n * clear \"no resource\" error) and `context.detect` is `undefined`.\n *\n * The asset surface stays honest: in the app server the sandbox\n * intercepts these methods before the API object is consulted and\n * routes them to the consent-gated asset service with the owning\n * plugin id — the onInstall download flow is identical to the runtime\n * one. In-process hosts (fixtures, dev runner, CLI) answer\n * `UNAVAILABLE` exactly like every other host without a consent\n * channel.\n */\nexport function createInstallScopeApi(): ResourceAPI {\n\tconst noResource = (method: string): Error =>\n\t\tnew Error(\n\t\t\t`${method}() — no resource is attached to the onInstall hook; use the asset methods (download/statAsset/readAsset/deleteAsset) for install-time work`,\n\t\t)\n\treturn {\n\t\tlogInfo() {},\n\t\tlogWarn() {},\n\t\tlogError() {},\n\t\tcontext: { detect: undefined },\n\t\tlistFileNames: async () => [],\n\t\treadFile: async () => {\n\t\t\tthrow noResource(\"readFile\")\n\t\t},\n\t\tstatFile: async () => undefined,\n\t\tstatFiles: async (paths) => paths.map(() => undefined),\n\t\tsniff: async () => undefined,\n\t\tprobe: async () => ({ kind: \"unknown\", reason: \"unavailable\" }),\n\t\thashBytes: async () => {\n\t\t\tthrow noResource(\"hashBytes\")\n\t\t},\n\t\tcomputeImageHashes: async () => undefined,\n\t\tlistContainer: async () => {\n\t\t\tthrow noResource(\"listContainer\")\n\t\t},\n\t\textractArchive: async () => {\n\t\t\tthrow noResource(\"extractArchive\")\n\t\t},\n\t\tdownload: async () => {\n\t\t\tthrow pluginAssetError(\n\t\t\t\t\"UNAVAILABLE\",\n\t\t\t\t\"download() — this host has no plugin asset service; only the app server host can download into the plugin vault\",\n\t\t\t)\n\t\t},\n\t\tstatAsset: async () => {\n\t\t\tthrow unavailableAsset(\"statAsset\")\n\t\t},\n\t\treadAsset: async () => {\n\t\t\tthrow unavailableAsset(\"readAsset\")\n\t\t},\n\t\tdeleteAsset: async () => {\n\t\t\tthrow unavailableAsset(\"deleteAsset\")\n\t\t},\n\t}\n}\n\nfunction unavailableAsset(method: string): Error {\n\treturn pluginAssetError(\n\t\t\"UNAVAILABLE\",\n\t\t`${method}() — this host has no plugin asset vault; only the app server host manages vault files`,\n\t)\n}\n","import type {\n\tImageHash,\n\tImageHashesResult,\n\tPluginManifestId,\n\tSerializedFileList,\n} from \"@hoardodile/sdk-types\"\nimport type { PluginRegistry, PluginRegistryEntry } from \"./api-types.ts\"\nimport { createCapabilityGuard } from \"./capability-guard.ts\"\nimport { createInstallScopeApi } from \"./install-api.ts\"\nimport type { Detection, ResourceAPI } from \"./types.ts\"\n\n/** Absolute cap of hash rows one resource may contribute (host policy). */\nexport const MAX_IMAGE_HASHES_PER_RESOURCE = 2000\n\n/**\n * Run `detect` over the given registry entries in order; returns the id of\n * the first matching plugin, or `undefined` when none match. A throwing\n * detector is logged (tagged with `action`) and skipped — one crash must\n * never abort the scan.\n */\nasync function runDetectors(\n\tentries: readonly PluginRegistryEntry[],\n\tapi: ResourceAPI,\n\taction: string,\n): Promise<PluginManifestId | undefined> {\n\tfor (const entry of entries) {\n\t\tconst result = await invokeHook(entry, entry.plugin.detect, api, action)\n\t\tif (result?.ok) return entry.id\n\t}\n\treturn undefined\n}\n\n/**\n * Invoke one hook of a registry entry with the uniform policy: an\n * unimplemented hook answers `undefined` without running, a throw is\n * logged via {@link hookFailureText} and also answers `undefined`. This\n * is the \"throw means absence\" policy — callers that must tell a hook\n * failure apart from a hook that ran (meta-hook key presence) keep\n * their own try/catch instead.\n */\nasync function invokeHook<T>(\n\tentry: PluginRegistryEntry,\n\thook: ((api: ResourceAPI) => T | Promise<T>) | undefined,\n\tapi: ResourceAPI,\n\taction: string,\n\tlevel: \"warn\" | \"error\" = \"error\",\n): Promise<T | undefined> {\n\tif (hook === undefined) return undefined\n\ttry {\n\t\treturn await hook(api)\n\t} catch (err) {\n\t\tconst log = level === \"warn\" ? console.warn : console.error\n\t\tlog(hookFailureText(action, entry.id, err))\n\t\treturn undefined\n\t}\n}\n\nfunction errorText(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err)\n}\n\nfunction hookFailureText(\n\taction: string,\n\tpluginId: PluginManifestId,\n\terr: unknown,\n): string {\n\treturn `[plugin-hooks] ${action} failed for plugin ${pluginId}: ${errorText(err)}`\n}\n\nexport type PluginHooksDeps = {\n\t/**\n\t * Live accessor for the current registry — called on every hook\n\t * invocation so a `rescan()` replacing the registry never leaves\n\t * consumers holding a stale snapshot.\n\t */\n\treadonly getRegistry: () => PluginRegistry\n}\n\n/**\n * Result of {@link PluginHooks.runMetaHooks}. A present key means the hook\n * ran (permission granted + implemented); `value` is the raw hook result,\n * which may still be `undefined` when the hook itself returned nothing.\n */\nexport type PluginMetaHookResults = {\n\treadonly sourceMeta?: { readonly value: unknown }\n\treadonly searchMeta?: { readonly value: unknown }\n}\n\n/**\n * The single entry point for executing plugin hooks. Owns every\n * hook-invocation policy: priority iteration, builtin fallback, error\n * swallowing/logging, capability checks, and result validation.\n *\n * Methods receive a ready-built {@link ResourceAPI} — this module knows\n * nothing about resources, archives, or paths.\n */\nexport type PluginHooks = {\n\t/** The builtin fallback plugin id. Throws when no builtin is registered. */\n\treadonly defaultPluginId: () => PluginManifestId\n\t/**\n\t * The registry entry that should serve a resource's read paths right\n\t * now: the resource's stored plugin when it is healthy (registered,\n\t * enabled, not missing), otherwise the builtin fallback plugin.\n\t * Throws when no builtin is registered.\n\t */\n\treadonly getEffectiveEntry: (\n\t\tresPluginId: PluginManifestId | null,\n\t) => PluginRegistryEntry\n\t/** Run all enabled plugins' detectors in priority order. Returns the first matching plugin id. Throws if no match (builtin should always match). */\n\treadonly detectFirstMatch: (api: ResourceAPI) => Promise<PluginManifestId>\n\t/** Validate that the current plugin still matches. Returns confirmed plugin id or falls back to builtin id. */\n\treadonly revalidate: (\n\t\tapi: ResourceAPI,\n\t\tcurrentPluginId: PluginManifestId,\n\t) => Promise<PluginManifestId>\n\t/** Run a specific plugin's detector. */\n\treadonly detectForPlugin: (\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t) => Promise<Detection>\n\t/**\n\t * Detector pass for folder-import candidates: non-builtin detectors in\n\t * priority order, falling back to the builtin plugin without invoking\n\t * its detector.\n\t */\n\treadonly detectForImportDir: (api: ResourceAPI) => Promise<PluginManifestId>\n\t/**\n\t * Ask the owning plugin for a custom file list. Returns `undefined`\n\t * when the plugin has no file list hook (or it failed).\n\t */\n\treadonly buildFileList: (\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t) => Promise<SerializedFileList | undefined>\n\t/** Ask the owning plugin which file should be used for the local cover. */\n\treadonly resolveLocalCoverSource: (\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t) => Promise<string | undefined>\n\t/**\n\t * Run the capability-gated meta hooks (`sourceMeta`, `searchMeta`) of\n\t * the owning plugin. Keys are absent when the permission is not\n\t * granted or the hook is not implemented.\n\t */\n\treadonly runMetaHooks: (\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t) => Promise<PluginMetaHookResults>\n\t/**\n\t * True when the plugin's manifest grants `imageHashes` and the plugin\n\t * implements the hook. Used to decide whether a missing hash state is\n\t * a rebuild gap or the plugin's legitimate choice.\n\t */\n\treadonly supportsImageHashes: (pluginId: PluginManifestId) => boolean\n\t/**\n\t * Run the capability-gated `imageHashes` hook of the owning plugin.\n\t * Resolves to `undefined` when the permission is not granted, the\n\t * hook is not implemented, or it threw. The returned entries are\n\t * shape-validated (hex values, per-resource cap).\n\t */\n\treadonly runImageHashes: (\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t) => Promise<ImageHashesResult | undefined>\n\t/**\n\t * Run the plugin's optional `onInstall` hook — best-effort, invoked\n\t * by the host after a successful install/update commit (marketplace\n\t * install/update or a zip upload; never seed/dev plugins). The hook\n\t * receives an install-scoped {@link ResourceAPI}: no resource is\n\t * attached (the file surface answers empty, `context.detect` is\n\t * `undefined`), while the asset methods still work and stay gated by\n\t * the shared consent dialog. A throwing (or consent-denied) hook is\n\t * logged via `hookFailureText` and swallowed — the install itself is\n\t * never failed; plugins must re-check at runtime.\n\t */\n\treadonly runInstallHook: (pluginId: PluginManifestId) => Promise<void>\n}\n\nexport function createPluginHooks(deps: PluginHooksDeps): PluginHooks {\n\tconst { getRegistry } = deps\n\tconst guard = createCapabilityGuard()\n\n\tfunction defaultPluginId(): PluginManifestId {\n\t\tconst builtin = getRegistry().getBuiltin()\n\t\tif (builtin === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t\"No builtin plugin in registry — cannot determine default plugin\",\n\t\t\t)\n\t\t}\n\t\treturn builtin.id\n\t}\n\n\tfunction getEffectiveEntry(\n\t\tresPluginId: PluginManifestId | null,\n\t): PluginRegistryEntry {\n\t\tif (resPluginId !== null) {\n\t\t\tconst entry = getRegistry().getById(resPluginId)\n\t\t\tif (entry?.enabled === true && !entry.missing) {\n\t\t\t\treturn entry\n\t\t\t}\n\t\t}\n\t\tconst builtin = getRegistry().getBuiltin()\n\t\tif (builtin === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t\"No builtin plugin in registry — cannot determine effective plugin\",\n\t\t\t)\n\t\t}\n\t\treturn builtin\n\t}\n\n\tasync function detectFirstMatch(api: ResourceAPI): Promise<PluginManifestId> {\n\t\tconst matched = await runDetectors(\n\t\t\tgetRegistry().getEnabled(),\n\t\t\tapi,\n\t\t\t\"detect\",\n\t\t)\n\t\tif (matched !== undefined) return matched\n\t\tconst builtin = getRegistry().getBuiltin()\n\t\tthrow new Error(\n\t\t\t`No plugin matched resource. Builtin plugin ${builtin?.id ?? \"unknown\"} should have matched but did not.`,\n\t\t)\n\t}\n\n\tasync function revalidate(\n\t\tapi: ResourceAPI,\n\t\tcurrentPluginId: PluginManifestId,\n\t): Promise<PluginManifestId> {\n\t\tconst registry = getRegistry()\n\t\tconst builtin = registry.getBuiltin()\n\t\tif (builtin === undefined) {\n\t\t\tthrow new Error(\"No builtin plugin available for fallback\")\n\t\t}\n\n\t\tconst enabled = registry.getEnabled()\n\t\tconst startIndex = enabled.findIndex((e) => e.id === currentPluginId)\n\t\tif (startIndex < 0) {\n\t\t\treturn builtin.id\n\t\t}\n\n\t\tconst matched = await runDetectors(\n\t\t\tenabled.slice(startIndex),\n\t\t\tapi,\n\t\t\t\"revalidate detect\",\n\t\t)\n\t\treturn matched ?? builtin.id\n\t}\n\n\tasync function detectForPlugin(\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t): Promise<Detection> {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\tif (entry === undefined) {\n\t\t\treturn { ok: false, reasons: [`unknown plugin: ${pluginId}`] }\n\t\t}\n\t\tconst result = await invokeHook(\n\t\t\tentry,\n\t\t\tentry.plugin.detect,\n\t\t\tapi,\n\t\t\t\"detectForPlugin\",\n\t\t)\n\t\tif (result === undefined) {\n\t\t\treturn { ok: false, reasons: [\"detect threw an exception\"] }\n\t\t}\n\t\tif (result.ok) return { ok: true }\n\t\treturn { ok: false, reasons: result.reasons.slice() }\n\t}\n\n\tasync function detectForImportDir(\n\t\tapi: ResourceAPI,\n\t): Promise<PluginManifestId> {\n\t\tconst fallback = defaultPluginId()\n\t\tconst detectors = getRegistry()\n\t\t\t.getEnabled()\n\t\t\t.filter((e) => !e.builtin)\n\t\t\t.sort((a, b) => a.priority - b.priority)\n\t\tconst matched = await runDetectors(detectors, api, \"import detect\")\n\t\treturn matched ?? fallback\n\t}\n\n\tasync function buildFileList(\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t): Promise<SerializedFileList | undefined> {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\tif (entry === undefined) return undefined\n\t\tconst pluginResult = await invokeHook(\n\t\t\tentry,\n\t\t\tentry.plugin.listFiles,\n\t\t\tapi,\n\t\t\t\"listFiles\",\n\t\t)\n\t\tif (pluginResult === undefined) return undefined\n\t\tfor (const item of pluginResult) {\n\t\t\tif (typeof item === \"string\") continue\n\t\t\tif (typeof item === \"object\" && item !== null && !Array.isArray(item)) {\n\t\t\t\tfor (const value of Object.values(item)) {\n\t\t\t\t\tif (value === undefined) continue\n\t\t\t\t\tconst t = typeof value\n\t\t\t\t\tif (t === \"string\" || t === \"number\" || t === \"boolean\") continue\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Plugin ${String(pluginId)} returned an invalid file list item value type: ${t}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tthrow new Error(\n\t\t\t\t`Plugin ${String(pluginId)} returned an invalid file list item`,\n\t\t\t)\n\t\t}\n\t\treturn pluginResult as SerializedFileList\n\t}\n\n\tasync function resolveLocalCoverSource(\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t): Promise<string | undefined> {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\tif (entry === undefined) return undefined\n\t\treturn invokeHook(entry, entry.plugin.coverLocal, api, \"coverLocal\", \"warn\")\n\t}\n\n\tasync function runMetaHooks(\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t): Promise<PluginMetaHookResults> {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\tif (entry === undefined) return {}\n\t\tconst { manifest, plugin } = entry\n\t\tconst results: {\n\t\t\tsourceMeta?: { readonly value: unknown }\n\t\t\tsearchMeta?: { readonly value: unknown }\n\t\t} = {}\n\t\t/**\n\t\t * Run one capability-gated meta hook. A present result key means\n\t\t * the hook ran; a throw leaves the key absent (a failed hook must\n\t\t * not wipe previously computed meta — the patch stays stale).\n\t\t */\n\t\tasync function runMetaHook(\n\t\t\tname: \"sourceMeta\" | \"searchMeta\",\n\t\t): Promise<void> {\n\t\t\tconst hook = plugin[name]\n\t\t\tif (!guard.check(manifest, name) || hook === undefined) return\n\t\t\ttry {\n\t\t\t\tresults[name] = { value: await hook(api) }\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(hookFailureText(name, pluginId, err))\n\t\t\t}\n\t\t}\n\t\tawait runMetaHook(\"sourceMeta\")\n\t\tawait runMetaHook(\"searchMeta\")\n\t\treturn results\n\t}\n\n\tfunction entrySupportsImageHashes(entry: PluginRegistryEntry): boolean {\n\t\treturn (\n\t\t\tguard.check(entry.manifest, \"imageHashes\") &&\n\t\t\tentry.plugin.imageHashes !== undefined\n\t\t)\n\t}\n\n\tfunction supportsImageHashes(pluginId: PluginManifestId): boolean {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\treturn entry !== undefined && entrySupportsImageHashes(entry)\n\t}\n\n\tasync function runImageHashes(\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t): Promise<ImageHashesResult | undefined> {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\tif (entry === undefined || !entrySupportsImageHashes(entry))\n\t\t\treturn undefined\n\t\treturn sanitizeImageHashes(\n\t\t\tawait invokeHook(entry, entry.plugin.imageHashes, api, \"imageHashes\"),\n\t\t\tpluginId,\n\t\t)\n\t}\n\n\tasync function runInstallHook(pluginId: PluginManifestId): Promise<void> {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\tif (entry === undefined) return\n\t\tawait invokeHook(\n\t\t\tentry,\n\t\t\tentry.plugin.onInstall,\n\t\t\tcreateInstallScopeApi(),\n\t\t\t\"onInstall\",\n\t\t\t\"warn\",\n\t\t)\n\t}\n\n\treturn {\n\t\tdefaultPluginId,\n\t\tgetEffectiveEntry,\n\t\tdetectFirstMatch,\n\t\trevalidate,\n\t\tdetectForPlugin,\n\t\tdetectForImportDir,\n\t\tbuildFileList,\n\t\tresolveLocalCoverSource,\n\t\trunMetaHooks,\n\t\tsupportsImageHashes,\n\t\trunImageHashes,\n\t\trunInstallHook,\n\t}\n}\n\nconst HEX_VALUE = /^[0-9a-f]+$/\n\n/**\n * Validate the raw hook result: a `{ hashes }` shape, hex string values,\n * and a bounded row count (excess entries are dropped with a warning —\n * the hook must never be able to bloat the hashes table).\n */\nfunction sanitizeImageHashes(\n\tvalue: unknown,\n\tpluginId: string,\n): ImageHashesResult | undefined {\n\tif (typeof value !== \"object\" || value === null) return undefined\n\tconst hashes = (value as { hashes?: unknown }).hashes\n\tif (!Array.isArray(hashes)) return undefined\n\tconst valid: ImageHash[] = []\n\tfor (const item of hashes) {\n\t\tif (typeof item !== \"object\" || item === null) continue\n\t\tconst { scope, type, value: hex, bits } = item as Record<string, unknown>\n\t\tif (\n\t\t\ttypeof scope === \"string\" &&\n\t\t\tscope.length > 0 &&\n\t\t\ttypeof type === \"string\" &&\n\t\t\ttype.length > 0 &&\n\t\t\ttypeof hex === \"string\" &&\n\t\t\tHEX_VALUE.test(hex)\n\t\t) {\n\t\t\t// `bits` always defaults to the hex length (4 bits per char) so\n\t\t\t// consumers can classify perceptual vs exact hashes by bits.\n\t\t\tvalid.push({\n\t\t\t\tscope,\n\t\t\t\ttype,\n\t\t\t\tvalue: hex,\n\t\t\t\tbits:\n\t\t\t\t\ttypeof bits === \"number\" && Number.isInteger(bits) && bits > 0\n\t\t\t\t\t\t? bits\n\t\t\t\t\t\t: hex.length * 4,\n\t\t\t})\n\t\t}\n\t}\n\tif (valid.length > MAX_IMAGE_HASHES_PER_RESOURCE) {\n\t\tconsole.warn(\n\t\t\t`[plugin-hooks] imageHashes for plugin ${pluginId} produced ${valid.length} entries — capped at ${MAX_IMAGE_HASHES_PER_RESOURCE}`,\n\t\t)\n\t\tvalid.length = MAX_IMAGE_HASHES_PER_RESOURCE\n\t}\n\treturn { hashes: valid }\n}\n","import type { ResourceAPI } from \"../types.ts\"\n\n/**\n * A hook invocation awaiting a worker response. Carries the call's\n * progress state plus the two timers policing it: the inactivity\n * watchdog and the absolute hard timeout.\n */\nexport type PendingCall = {\n\treadonly api: ResourceAPI\n\treadonly resolve: (value: unknown) => void\n\treadonly reject: (err: Error) => void\n\t/**\n\t * Host-side API dispatches currently running for this call. While any\n\t * are in flight the watchdog is paused — the plugin is blocked on the\n\t * host, not hung.\n\t */\n\tapiInFlight: number\n\twatchdog?: NodeJS.Timeout\n\thardTimer?: NodeJS.Timeout\n}\n\nexport type CallTimers = {\n\t/** (Re)arm the inactivity watchdog, replacing any previous one. */\n\treadonly armWatchdog: (call: PendingCall, onExpire: () => void) => void\n\t/** Arm the absolute per-invocation cap. */\n\treadonly armHardTimer: (call: PendingCall, onExpire: () => void) => void\n\t/** Suspend the watchdog while host-side API work is in flight. */\n\treadonly pauseWatchdog: (call: PendingCall) => void\n\t/** Cancel both timers of a call (settled or abandoned). */\n\treadonly clearCallTimers: (call: PendingCall) => void\n}\n\n/**\n * Timer bookkeeping for sandboxed hook invocations: the inactivity\n * watchdog (`watchdogMs`) and the absolute cap (`hardTimeoutMs`). Expiry\n * callbacks are supplied per arm by the owner — which knows the owning\n * state and worker — so this module stays decoupled from the sandbox\n * lifecycle.\n */\nexport function createCallTimers(opts: {\n\treadonly watchdogMs: number\n\treadonly hardTimeoutMs: number\n}): CallTimers {\n\tfunction armWatchdog(call: PendingCall, onExpire: () => void): void {\n\t\tif (call.watchdog !== undefined) clearTimeout(call.watchdog)\n\t\tcall.watchdog = setTimeout(onExpire, opts.watchdogMs)\n\t\tcall.watchdog.unref()\n\t}\n\n\tfunction armHardTimer(call: PendingCall, onExpire: () => void): void {\n\t\tif (call.hardTimer !== undefined) clearTimeout(call.hardTimer)\n\t\tcall.hardTimer = setTimeout(onExpire, opts.hardTimeoutMs)\n\t\tcall.hardTimer.unref()\n\t}\n\n\tfunction pauseWatchdog(call: PendingCall): void {\n\t\tif (call.watchdog === undefined) return\n\t\tclearTimeout(call.watchdog)\n\t\tcall.watchdog = undefined\n\t}\n\n\tfunction clearCallTimers(call: PendingCall): void {\n\t\tif (call.watchdog !== undefined) clearTimeout(call.watchdog)\n\t\tif (call.hardTimer !== undefined) clearTimeout(call.hardTimer)\n\t}\n\n\treturn { armWatchdog, armHardTimer, pauseWatchdog, clearCallTimers }\n}\n","/**\n * Wire protocol between the plugin sandbox host (server process) and the\n * sandbox child entry (`worker-entry.mjs`), which runs as a forked child\n * process with structured-clone IPC. The entry file is plain JS without\n * access to workspace TS sources, so it keeps its own copy of the\n * method/hook name lists — keep the two in sync (enforced by\n * `protocol.test.ts`).\n */\n\n/**\n * Plugin hook names the host can invoke, in contract order — defined in\n * `@hoardodile/sdk-types`, re-exported so host internals keep one import\n * point.\n */\nimport { HOOK_NAMES, type HookName } from \"@hoardodile/sdk-types\"\nimport { PLUGIN_ASSET_ERROR_NAMES } from \"@hoardodile/sdk-types/plugin-asset-limits\"\n\nexport { HOOK_NAMES, type HookName }\n\n/** ResourceAPI method names bridged over RPC. */\nexport const API_METHOD_NAMES = [\n\t\"logInfo\",\n\t\"logWarn\",\n\t\"logError\",\n\t\"listFileNames\",\n\t\"readFile\",\n\t\"statFile\",\n\t\"statFiles\",\n\t\"sniff\",\n\t\"probe\",\n\t\"hashBytes\",\n\t\"computeImageHashes\",\n\t\"listContainer\",\n\t\"extractArchive\",\n\t\"download\",\n\t\"statAsset\",\n\t\"readAsset\",\n\t\"deleteAsset\",\n] as const\n\nexport type ApiMethodName = (typeof API_METHOD_NAMES)[number]\n\n/** Fire-and-forget log methods — no response round-trip. */\nexport const LOG_METHOD_NAMES: ReadonlySet<ApiMethodName> = new Set([\n\t\"logInfo\",\n\t\"logWarn\",\n\t\"logError\",\n])\n\nexport type SerializedError = {\n\treadonly name: string\n\treadonly message: string\n\treadonly stack?: string\n\t/**\n\t * Machine-readable plugin error code (`DENIED`/`UNAVAILABLE`/`POLICY`)\n\t * carried verbatim on the wire — the single field the host side reads\n\t * to restore the plugin-facing `err.name`.\n\t */\n\treadonly code?: string\n}\n\n// -- host → worker --\n\nexport type LoadRequest = {\n\treadonly type: \"load\"\n\treadonly mainPath: string\n}\n\nexport type InvokeRequest = {\n\treadonly type: \"invoke\"\n\treadonly callId: number\n\treadonly hook: HookName\n}\n\nexport type ApiResponse = {\n\treadonly type: \"apiResult\"\n\treadonly apiCallId: number\n\treadonly ok: boolean\n\treadonly value?: unknown\n\treadonly error?: SerializedError\n}\n\n// -- worker → host --\n\nexport type LoadResponse = {\n\treadonly type: \"loaded\"\n\treadonly ok: boolean\n\treadonly hooks?: readonly HookName[]\n\treadonly error?: SerializedError\n}\n\nexport type InvokeResponse = {\n\treadonly type: \"result\"\n\treadonly callId: number\n\treadonly ok: boolean\n\treadonly value?: unknown\n\treadonly error?: SerializedError\n}\n\nexport type ApiRequest = {\n\treadonly type: \"api\"\n\treadonly callId: number\n\treadonly apiCallId: number\n\treadonly method: ApiMethodName\n\treadonly args: readonly unknown[]\n}\n\nexport type LogRequest = {\n\treadonly type: \"log\"\n\treadonly callId: number\n\treadonly method: \"logInfo\" | \"logWarn\" | \"logError\"\n\treadonly args: readonly unknown[]\n}\n\nexport type WorkerMessage =\n\t| LoadResponse\n\t| InvokeResponse\n\t| ApiRequest\n\t| LogRequest\n\nexport function serializeError(err: unknown): SerializedError {\n\tif (err instanceof Error) {\n\t\treturn {\n\t\t\tname: err.name,\n\t\t\tmessage: err.message,\n\t\t\tstack: err.stack,\n\t\t\t// The vocabulary errors carry their code in `name`; the wire\n\t\t\t// keeps an explicit `code` field so the receiving side never\n\t\t\t// re-parses or re-derives it.\n\t\t\tcode: isAssetErrorName(err.name) ? err.name : undefined,\n\t\t}\n\t}\n\treturn { name: \"Error\", message: String(err) }\n}\n\nexport function deserializeError(err: SerializedError): Error {\n\tconst e = new Error(err.message)\n\t// `code` is authoritative when present (the vocabulary survived);\n\t// `name` falls back for plain host errors.\n\te.name = err.code ?? err.name\n\tif (err.stack !== undefined) e.stack = err.stack\n\treturn e\n}\n\nfunction isAssetErrorName(name: string): boolean {\n\treturn (PLUGIN_ASSET_ERROR_NAMES as readonly string[]).includes(name)\n}\n","import { definePlugin } from \"../define-plugin.ts\"\nimport type { PluginDefinition, ResourceAPI } from \"../types.ts\"\nimport type { HookName } from \"./protocol.ts\"\n\nexport type InvokeFn = (hook: HookName, api: ResourceAPI) => Promise<unknown>\n\n/**\n * Build a contract-compliant {@link PluginDefinition} that forwards hooks\n * into a sandboxed worker. Only hooks the plugin actually implements are\n * exposed — presence is semantic (orchestrators branch on e.g.\n * `plugin.listFiles === undefined`).\n */\nexport function createSandboxedPlugin(\n\thooks: readonly HookName[],\n\tinvoke: InvokeFn,\n): PluginDefinition {\n\t// RPC boundary: hook signatures share the shape `(api) => Promise<unknown>`;\n\t// the concrete return type is enforced by the plugin contract at runtime.\n\tconst definition: Record<string, (api: ResourceAPI) => Promise<unknown>> = {}\n\tfor (const hook of hooks) {\n\t\tdefinition[hook] = async (api: ResourceAPI) => invoke(hook, api)\n\t}\n\tif (definition.detect === undefined) {\n\t\tdefinition.detect = async () => ({\n\t\t\tok: false,\n\t\t\treasons: [\"plugin does not implement detect()\"],\n\t\t})\n\t}\n\treturn definePlugin(definition as unknown as PluginDefinition)\n}\n","import { type ChildProcess, fork, spawn } from \"node:child_process\"\nimport { realpathSync } from \"node:fs\"\nimport { tmpdir } from \"node:os\"\nimport { dirname, resolve, sep } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport type {\n\tPluginAssetDeleteResult,\n\tPluginDownloadRequest,\n\tPluginDownloadResult,\n\tPluginPermissions,\n} from \"@hoardodile/sdk-types\"\nimport { CAPABILITY_BY_METHOD } from \"@hoardodile/sdk-types/plugin-capabilities\"\nimport type { PluginDefinition, ResourceAPI } from \"../types.ts\"\nimport { createCallTimers, type PendingCall } from \"./call-timers.ts\"\nimport {\n\tAPI_METHOD_NAMES,\n\ttype ApiMethodName,\n\tdeserializeError,\n\ttype HookName,\n\ttype InvokeRequest,\n\tLOG_METHOD_NAMES,\n\ttype SerializedError,\n\tserializeError,\n\ttype WorkerMessage,\n} from \"./protocol.ts\"\nimport { createSandboxedPlugin } from \"./sandboxed-plugin.ts\"\n\n/**\n * The plugin asset handler: the host-side implementation of the\n * `download`/`statAsset`/`readAsset`/`deleteAsset` ResourceAPI methods,\n * bound to the plugin id (the sandbox knows which plugin issued the\n * call). Wired by the app server; hosts without a consent channel (CLI,\n * workbench) omit it and the methods answer `UNAVAILABLE`.\n */\nexport type PluginAssetHandler = {\n\t/**\n\t * Download one request or a batch of requests. Answers with the\n\t * result array (batch shape) — this adapter unwraps the single\n\t * result for a single request (see {@link dispatchApi}).\n\t */\n\treadonly download: (\n\t\tpluginId: string,\n\t\trequest: PluginDownloadRequest | readonly PluginDownloadRequest[],\n\t) => Promise<readonly PluginDownloadResult[]>\n\treadonly statAsset: (\n\t\tpluginId: string,\n\t\tpath: string,\n\t) => Promise<{ readonly sizeBytes: number } | undefined>\n\treadonly readAsset: (pluginId: string, path: string) => Promise<Uint8Array>\n\treadonly deleteAsset: (\n\t\tpluginId: string,\n\t\tpath: string,\n\t) => Promise<PluginAssetDeleteResult>\n}\n\n/**\n * Kill a plugin sandbox process when an invocation neither returns nor\n * shows resource-API activity for this long. Hooks that keep calling the\n * API reset the watchdog continuously and never trip it; time spent inside\n * a host-side API call does not count as inactivity.\n */\nexport const PLUGIN_WATCHDOG_TIMEOUT_MS = 60_000\n\n/** Absolute cap for a single plugin hook invocation, regardless of activity. */\nexport const PLUGIN_HOOK_HARD_TIMEOUT_MS = 30 * 60_000\n\n/** V8 old-generation memory cap per plugin sandbox process, in MiB. */\nexport const PLUGIN_WORKER_MAX_OLD_SPACE_MB = 512\n\n/**\n * Absolute cap for one hook result crossing the IPC boundary back to the\n * host. Exceeding it turns the hook into an error instead of letting a\n * hostile bundle clone a giant payload into the host process.\n */\nexport const PLUGIN_MAX_RESULT_BYTES = 256 * 1024 * 1024\n\n/** Cap on `log*` messages per hook invocation (see {@link PluginSandboxConfig}). */\nexport const PLUGIN_MAX_LOGS_PER_HOOK = 1_000\n\n/** Cap on ResourceAPI calls per hook invocation (see {@link PluginSandboxConfig}). */\nexport const PLUGIN_MAX_API_CALLS_PER_HOOK = 100_000\n\n/**\n * Sandbox-gated ResourceAPI methods, read from the single capability\n * table (`@hoardodile/sdk-types/plugin-capabilities`) — a permission\n * declared there is enforced here automatically, and a sandbox method\n * listed there but not declared by the manifest is denied with the\n * capability's own name.\n *\n * The asset surface (the `download` capability) is additionally\n * intercepted before touching the ResourceAPI instance: its\n * implementation comes from the wired {@link PluginAssetHandler}, which\n * knows the owning plugin id.\n */\nconst ASSET_METHODS = new Set<ApiMethodName>([\n\t\"download\",\n\t\"statAsset\",\n\t\"readAsset\",\n\t\"deleteAsset\",\n])\n\n/**\n * Max sandbox spawns per plugin within {@link PLUGIN_WORKER_RESPAWN_WINDOW_MS}\n * before the plugin is degraded. It recovers automatically once the crash\n * window slides clean, or immediately on disable/rescan.\n */\nexport const PLUGIN_WORKER_MAX_RESPAWNS = 3\n\n/** Sliding window for {@link PLUGIN_WORKER_MAX_RESPAWNS}. */\nexport const PLUGIN_WORKER_RESPAWN_WINDOW_MS = 60_000\n\nexport type PluginSandboxConfig = {\n\t/**\n\t * Kill the sandbox process when an invocation neither returns nor shows\n\t * API activity for this long. Long-running hooks that keep calling the\n\t * resource API reset the watchdog continuously and never trip it;\n\t * time spent inside a host-side API call does not count as inactivity.\n\t */\n\treadonly watchdogMs: number\n\t/** Absolute per-invocation cap, regardless of activity. */\n\treadonly hardTimeoutMs: number\n\t/** V8 old-generation cap per sandbox process; exceeding it aborts it. */\n\treadonly maxOldSpaceMb: number\n\t/**\n\t * Max sandbox spawns per plugin within {@link respawnWindowMs} before the\n\t * plugin is degraded (all invocations reject). The plugin recovers\n\t * automatically once the crash window slides clean; `unloadPlugin`\n\t * (disable or rescan) resets the budget immediately.\n\t */\n\treadonly maxRespawns: number\n\treadonly respawnWindowMs: number\n\t/** Absolute cap for one hook result returning to the host. */\n\treadonly maxResultBytes: number\n\t/**\n\t * Cap on `log*` messages per hook invocation; exceeding it fails the\n\t * hook (log messages reset the watchdog, so a log flood would\n\t * otherwise stay alive until the hard timeout).\n\t */\n\treadonly maxLogsPerHook: number\n\t/**\n\t * Cap on ResourceAPI calls per hook invocation; exceeding it fails the\n\t * hook. Sized generously so large per-file scans (hashBytes over tens\n\t * of thousands of files) keep working — the cap bounds a runaway RPC\n\t * fan-out that would otherwise pin the host's CPU.\n\t */\n\treadonly maxApiCallsPerHook: number\n\t/**\n\t * Permission-model flag to pass to the sandbox child, or `undefined` to\n\t * probe the running Node for the first accepted name. Tests override\n\t * this to exercise the fail-closed path.\n\t */\n\treadonly permissionFlag?: string\n\t/**\n\t * Host-managed plugin vault directory (`<plugin-dir>/vault/`, or the\n\t * versioned-storage vault for dev plugins). When set, the child gets\n\t * an extra fs-read grant and the module policy gate allows loading\n\t * files from it — downloaded runtimes (JS/WASM) importable from the\n\t * vault are the point of the asset API. A function form lets one\n\t * shared sandbox resolve the path per plugin (disk vs dev plugins\n\t * store their vault differently).\n\t */\n\treadonly assetVaultDir?: string | ((pluginId: string) => string | undefined)\n\t/**\n\t * Plugin asset handler wired by the app server. Absent → the asset\n\t * methods answer `UNAVAILABLE` (CLI, workbench, tests without a\n\t * consent channel).\n\t */\n\treadonly pluginAssets?: PluginAssetHandler\n}\n\nexport const DEFAULT_SANDBOX_CONFIG: PluginSandboxConfig = {\n\twatchdogMs: PLUGIN_WATCHDOG_TIMEOUT_MS,\n\thardTimeoutMs: PLUGIN_HOOK_HARD_TIMEOUT_MS,\n\tmaxOldSpaceMb: PLUGIN_WORKER_MAX_OLD_SPACE_MB,\n\tmaxRespawns: PLUGIN_WORKER_MAX_RESPAWNS,\n\trespawnWindowMs: PLUGIN_WORKER_RESPAWN_WINDOW_MS,\n\tmaxResultBytes: PLUGIN_MAX_RESULT_BYTES,\n\tmaxLogsPerHook: PLUGIN_MAX_LOGS_PER_HOOK,\n\tmaxApiCallsPerHook: PLUGIN_MAX_API_CALLS_PER_HOOK,\n}\n\nexport type PluginSandbox = {\n\t/**\n\t * Register and load a plugin bundle. With `eager` the sandbox process\n\t * stays alive; without it the hook list is probed and the process\n\t * immediately idles (it respawns lazily on first invocation — disabled\n\t * plugins still serve their bound resources without holding a process).\n\t *\n\t * Reloading an already-registered id keeps the previous process alive\n\t * until the new bundle loads successfully — a failed reload returns\n\t * the previous definition instead of stranding a disposed registry.\n\t *\n\t * Returns `undefined` when the bundle cannot be loaded and there is no\n\t * previous definition to fall back on (already logged) — callers fall\n\t * back to a failing plugin.\n\t */\n\treadonly loadPlugin: (opts: {\n\t\treadonly id: string\n\t\treadonly mainPath: string\n\t\treadonly eager: boolean\n\t\t/**\n\t\t * Manifest permissions the sandbox enforces on every API call\n\t\t * (e.g. the `container` gate). Absent keys count as denied here,\n\t\t * so a caller that omits this opts into the strictest view.\n\t\t */\n\t\treadonly permissions?: PluginPermissions\n\t}) => Promise<PluginDefinition | undefined>\n\t/**\n\t * Terminate the plugin's sandbox process (if any) and reset its respawn\n\t * budget. The hook list stays known; the next invocation lazily respawns.\n\t */\n\treadonly unloadPlugin: (id: string) => void\n\t/**\n\t * Terminate and forget every plugin whose id is not in `keepIds`.\n\t * Registered ids keep their processes — used by the loader after a\n\t * successful reload to free plugins that left the registry.\n\t */\n\treadonly disposeExcept: (keepIds: ReadonlySet<string>) => Promise<void>\n\t/** Terminate every process and forget all plugins. Pending invocations reject. */\n\treadonly disposeAll: () => Promise<void>\n}\n\ntype LoadWaiter = {\n\treadonly resolve: () => void\n\treadonly reject: (err: Error) => void\n}\n\ntype PluginState = {\n\treadonly id: string\n\treadonly mainPath: string\n\treadonly permissions: PluginPermissions | undefined\n\tchild: ChildProcess | undefined\n\thooks: readonly HookName[] | undefined\n\tloading: Promise<void> | undefined\n\tloadWaiter: LoadWaiter | undefined\n\treadonly pending: Map<number, PendingCall>\n\trespawnTimes: number[]\n\tdegraded: boolean\n\tdisposed: boolean\n}\n\n/**\n * Flag names the Node permission model accepted over its life: it was\n * introduced as `--experimental-permission` and stabilized under\n * `--permission`. Probe the running binary once and reuse the verdict.\n */\nconst PERMISSION_FLAG_CANDIDATES = [\"--permission\", \"--experimental-permission\"]\n\nlet permissionFlagPromise: Promise<string | undefined> | undefined\n\nasync function resolvePermissionFlag(): Promise<string | undefined> {\n\tpermissionFlagPromise ??= probePermissionFlag()\n\treturn permissionFlagPromise\n}\n\n/**\n * Spawn a probe child for each candidate flag name and keep the first one\n * the runtime accepts. A flag the runtime does not know makes `node` exit\n * non-zero before running the script, so an `exit 0` verdict is the proof.\n */\nasync function probePermissionFlag(): Promise<string | undefined> {\n\tfor (const flag of PERMISSION_FLAG_CANDIDATES) {\n\t\tif (await acceptsFlag(flag)) return flag\n\t}\n\treturn undefined\n}\n\nfunction acceptsFlag(flag: string): Promise<boolean> {\n\treturn new Promise((resolveProbe) => {\n\t\tconst probe = spawn(\n\t\t\tprocess.execPath,\n\t\t\t[flag, `--allow-fs-read=${tmpdir()}`, \"-e\", \"process.exit(0)\"],\n\t\t\t{ stdio: \"ignore\", timeout: 10_000 },\n\t\t)\n\t\tprobe.on(\"exit\", (code) => resolveProbe(code === 0))\n\t\tprobe.on(\"error\", () => resolveProbe(false))\n\t})\n}\n\nexport function createPluginSandbox(\n\tconfig: PluginSandboxConfig = DEFAULT_SANDBOX_CONFIG,\n): PluginSandbox {\n\tconst states = new Map<string, PluginState>()\n\tlet nextCallId = 1\n\tconst timers = createCallTimers({\n\t\twatchdogMs: config.watchdogMs,\n\t\thardTimeoutMs: config.hardTimeoutMs,\n\t})\n\n\tasync function loadPlugin(opts: {\n\t\tid: string\n\t\tmainPath: string\n\t\teager: boolean\n\t\tpermissions?: PluginPermissions\n\t}): Promise<PluginDefinition | undefined> {\n\t\tconst previous = states.get(opts.id)\n\t\tconst state: PluginState = {\n\t\t\tid: opts.id,\n\t\t\tmainPath: opts.mainPath,\n\t\t\tpermissions: opts.permissions,\n\t\t\tchild: undefined,\n\t\t\thooks: undefined,\n\t\t\tloading: undefined,\n\t\t\tloadWaiter: undefined,\n\t\t\tpending: new Map(),\n\t\t\trespawnTimes: [],\n\t\t\tdegraded: false,\n\t\t\tdisposed: false,\n\t\t}\n\t\tstates.set(opts.id, state)\n\t\ttry {\n\t\t\tawait ensureLoaded(state)\n\t\t} catch (err) {\n\t\t\tconsole.error(`[plugin-sandbox] ${opts.id}: failed to load main.js`, err)\n\t\t\tstate.disposed = true\n\t\t\t// A concurrent loadPlugin for the same id may have replaced this\n\t\t\t// state — only act when the map still owns it.\n\t\t\tif (states.get(opts.id) !== state) return undefined\n\t\t\tif (previous !== undefined) {\n\t\t\t\t// The previous process never stopped being healthy — restore\n\t\t\t\t// it so a failed reload can't strand a disposed registry.\n\t\t\t\tprevious.disposed = false\n\t\t\t\tstates.set(opts.id, previous)\n\t\t\t\treturn createSandboxedPlugin(\n\t\t\t\t\tprevious.hooks ?? [\"detect\"],\n\t\t\t\t\t(hook, api) => invoke(previous, hook, api),\n\t\t\t\t)\n\t\t\t}\n\t\t\tstates.delete(opts.id)\n\t\t\treturn undefined\n\t\t}\n\t\t// The new bundle loaded: retire the previous process only now —\n\t\t// in-flight invocations against it settle normally, and a failed\n\t\t// reload above never killed it in the first place.\n\t\tif (previous !== undefined) {\n\t\t\tprevious.disposed = true\n\t\t\tawait teardownChild(previous)\n\t\t}\n\t\tif (!opts.eager) {\n\t\t\t// AWAITED, not fire-and-forget: the hook list was probed by\n\t\t\t// this load, and the loader returns the sandboxed definition\n\t\t\t// the moment we resolve. A caller that invokes a hook\n\t\t\t// immediately after (e.g. the post-install `onInstall`) must\n\t\t\t// not find a half-alive child: `ensureLoaded` would see the\n\t\t\t// not-yet-torn-down child and post its invocation into a\n\t\t\t// dying process (rejected as \"worker stopped\").\n\t\t\tawait teardownChild(state)\n\t\t}\n\t\treturn createSandboxedPlugin(state.hooks ?? [\"detect\"], (hook, api) =>\n\t\t\tinvoke(state, hook, api),\n\t\t)\n\t}\n\n\tfunction unloadPlugin(id: string): void {\n\t\tconst state = states.get(id)\n\t\tif (state === undefined) return\n\t\tteardownChild(state)\n\t\tstate.respawnTimes = []\n\t\tstate.degraded = false\n\t}\n\n\tasync function disposeAll(): Promise<void> {\n\t\tconst tasks: Promise<void>[] = []\n\t\tfor (const state of states.values()) {\n\t\t\tstate.disposed = true\n\t\t\ttasks.push(teardownChild(state))\n\t\t}\n\t\tstates.clear()\n\t\tawait Promise.all(tasks)\n\t}\n\n\tasync function disposeExcept(keepIds: ReadonlySet<string>): Promise<void> {\n\t\tconst tasks: Promise<void>[] = []\n\t\tfor (const [id, state] of states) {\n\t\t\tif (keepIds.has(id)) continue\n\t\t\tstate.disposed = true\n\t\t\tstates.delete(id)\n\t\t\ttasks.push(teardownChild(state))\n\t\t}\n\t\tawait Promise.all(tasks)\n\t}\n\n\t// -- child lifecycle --\n\n\t/**\n\t * Keep the sandbox child referenced only while it has work pending\n\t * (loading or an in-flight invocation); idle children must not hold\n\t * the host process open, but an unref'd child in a host with nothing\n\t * else running (one-shot CLI, dist smoke) would otherwise let the\n\t * process exit before a slow sandbox boot answered.\n\t */\n\tfunction syncRef(state: PluginState): void {\n\t\tconst child = state.child\n\t\tif (child === undefined) return\n\t\tif (\n\t\t\tstate.loading !== undefined ||\n\t\t\tstate.loadWaiter !== undefined ||\n\t\t\tstate.pending.size > 0\n\t\t) {\n\t\t\tchild.ref()\n\t\t} else {\n\t\t\tchild.unref()\n\t\t}\n\t}\n\n\tfunction ensureLoaded(state: PluginState): Promise<void> {\n\t\tif (state.child !== undefined) return Promise.resolve()\n\t\tstate.loading ??= spawnAndLoad(state).finally(() => {\n\t\t\tstate.loading = undefined\n\t\t})\n\t\treturn state.loading\n\t}\n\n\tasync function spawnAndLoad(state: PluginState): Promise<void> {\n\t\tconst now = Date.now()\n\t\tstate.respawnTimes = state.respawnTimes.filter(\n\t\t\t(t) => now - t < config.respawnWindowMs,\n\t\t)\n\t\tif (state.respawnTimes.length >= config.maxRespawns) {\n\t\t\tstate.degraded = true\n\t\t\tthrow new Error(\n\t\t\t\t`plugin ${state.id} unavailable: sandbox respawned ${config.maxRespawns} times within ${config.respawnWindowMs}ms`,\n\t\t\t)\n\t\t}\n\t\tstate.respawnTimes.push(now)\n\n\t\tconst permissionFlag =\n\t\t\tconfig.permissionFlag ?? (await resolvePermissionFlag())\n\t\tif (permissionFlag === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`plugin ${state.id} unavailable: this Node build has no permission-model flag (tried ${PERMISSION_FLAG_CANDIDATES.map((f) => `\"${f}\"`).join(\", \")}); refusing to run plugin code unsandboxed`,\n\t\t\t)\n\t\t}\n\n\t\t// Resolve the sandbox entry through the package's own exports map\n\t\t// (`@hoardodile/host/worker-entry`) when that package is installed.\n\t\t// The sandbox module also gets inlined into the server bundle, where\n\t\t// there is no `@hoardodile/host` on disk — then we fall back to\n\t\t// `dist/chunks/worker-entry.mjs` next to the emitting chunk.\n\t\t// Kept out of a `new URL()` literal so Vite does not detect a browser\n\t\t// worker here: it would bundle worker-entry.mjs with node: builtins\n\t\t// shimmed out and rewrite the URL to an unusable /assets/ path. The\n\t\t// entry ships untransformed and must run as plain ESM in a child\n\t\t// process.\n\t\tconst entryPath = fileURLToPath(resolveWorkerEntryUrl())\n\t\tconst pluginDir = dirname(state.mainPath)\n\t\t// The ESM loader passes every module path through realpathSync under\n\t\t// `--permission`, so the grants must use the canonical on-disk form —\n\t\t// a plugin dir reached through a symlink (macOS `/tmp`→`/private/tmp`,\n\t\t// WSL paths, junctions) would otherwise resolve outside its own grant\n\t\t// and fail to load with ERR_ACCESS_DENIED.\n\t\tconst realEntryPath = realPathOrResolve(entryPath)\n\t\tconst realPluginDir = realPathOrResolve(pluginDir)\n\t\tconst realMainPath = realPathOrResolve(state.mainPath)\n\n\t\t// The child's grants are minimal: one fs-read allowlist (its own\n\t\t// directory, plus the entry file it must load, plus the host-managed\n\t\t// asset vault when one is wired), a memory cap, and nothing else —\n\t\t// no fs write, no child processes, no worker threads, no native\n\t\t// addons. The module policy hook (registered inside the entry via\n\t\t// `registerHooks`) closes the remaining surface: nothing outside the\n\t\t// plugin directory (and the vault) and no `node:` builtins except\n\t\t// `node:url` can be imported.\n\t\tconst fsReadGrants = [`${realPluginDir}${sep}`, realEntryPath]\n\t\tconst assetVaultDir =\n\t\t\ttypeof config.assetVaultDir === \"function\"\n\t\t\t\t? config.assetVaultDir(state.id)\n\t\t\t\t: config.assetVaultDir\n\t\tconst realVaultDir =\n\t\t\tassetVaultDir === undefined ? undefined : realPathOrResolve(assetVaultDir)\n\t\tif (realVaultDir !== undefined) {\n\t\t\tfsReadGrants.push(`${realVaultDir}${sep}`)\n\t\t}\n\t\tconst child = fork(\n\t\t\trealEntryPath,\n\t\t\t[realPluginDir, realEntryPath, realVaultDir].filter(\n\t\t\t\t(v): v is string => v !== undefined,\n\t\t\t),\n\t\t\t{\n\t\t\t\texecArgv: [\n\t\t\t\t\tpermissionFlag,\n\t\t\t\t\t...fsReadGrants.map((p) => `--allow-fs-read=${p}`),\n\t\t\t\t\t`--max-old-space-size=${config.maxOldSpaceMb}`,\n\t\t\t\t],\n\t\t\t\tserialization: \"advanced\",\n\t\t\t\t// Child stderr is inherited (self-check failures must surface);\n\t\t\t\t// plugin console output is dropped — logs flow over the RPC.\n\t\t\t\tstdio: [\"ignore\", \"ignore\", \"inherit\", \"ipc\"],\n\t\t\t\tenv: {\n\t\t\t\t\t...process.env,\n\t\t\t\t\tHOARDODILE_PLUGIN_MAX_RESULT_BYTES: String(config.maxResultBytes),\n\t\t\t\t\tHOARDODILE_PLUGIN_MAX_LOGS_PER_HOOK: String(config.maxLogsPerHook),\n\t\t\t\t\tHOARDODILE_PLUGIN_MAX_API_CALLS_PER_HOOK: String(\n\t\t\t\t\t\tconfig.maxApiCallsPerHook,\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\t// The sandbox must never hold the process open on its own — but\n\t\t// while a load/invocation is pending the child is referenced (see\n\t\t// syncRef) so a short-lived host cannot exit mid-boot.\n\t\tchild.unref()\n\t\tstate.child = child\n\t\tsyncRef(state)\n\n\t\tchild.on(\"message\", (msg: WorkerMessage) =>\n\t\t\thandleMessage(state, child, msg),\n\t\t)\n\t\tchild.on(\"error\", (err: unknown) => failChild(state, child, asError(err)))\n\t\tchild.on(\"exit\", (code) => {\n\t\t\tif (state.child === child) {\n\t\t\t\tfailChild(\n\t\t\t\t\tstate,\n\t\t\t\t\tchild,\n\t\t\t\t\tnew Error(`plugin ${state.id} worker exited (code ${code})`),\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\n\t\tconst loaded = new Promise<void>((resolveLoaded, reject) => {\n\t\t\tstate.loadWaiter = { resolve: resolveLoaded, reject }\n\t\t})\n\t\ttry {\n\t\t\tchild.send({ type: \"load\", mainPath: realMainPath } satisfies {\n\t\t\t\ttype: \"load\"\n\t\t\t\tmainPath: string\n\t\t\t})\n\t\t} catch (err) {\n\t\t\t// The channel never opened (spawn failed hard) — the waiter\n\t\t\t// would hang forever; tear the child down and fail the load.\n\t\t\tawait teardownChild(state, asError(err))\n\t\t\tthrow err\n\t\t}\n\t\ttry {\n\t\t\tawait loaded\n\t\t} catch (err) {\n\t\t\t// A plugin whose main.js throws at import reports `loaded: ok:false`\n\t\t\t// and keeps idling — terminate the child so it never outlives\n\t\t\t// its owning state (failChild already covered error/exit).\n\t\t\tawait teardownChild(state)\n\t\t\tthrow err\n\t\t} finally {\n\t\t\tstate.loadWaiter = undefined\n\t\t}\n\t\t// Loaded and (for eager plugins) alive: an idle sandbox must not\n\t\t// hold the host open, but it stays referenced while invocations\n\t\t// are in flight.\n\t\tsyncRef(state)\n\t}\n\n\t/**\n\t * Terminate the child without touching the respawn budget. Rejects\n\t * the load waiter and every pending call with `err` (or the standard\n\t * \"worker stopped\" error) and clears the child slot; a no-op when\n\t * there is no child.\n\t */\n\tfunction teardownChild(state: PluginState, err?: Error): Promise<void> {\n\t\tconst child = state.child\n\t\tstate.child = undefined\n\t\tstate.loading = undefined\n\t\tconst stopErr = err ?? new Error(`plugin ${state.id} worker stopped`)\n\t\tstate.loadWaiter?.reject(stopErr)\n\t\tstate.loadWaiter = undefined\n\t\trejectAllPending(state, stopErr)\n\t\tif (child === undefined) return Promise.resolve()\n\t\t// SIGTERM on POSIX, TerminateProcess on Windows. Pending calls were\n\t\t// already rejected; stale messages can no longer resolve anything\n\t\t// because the slot is cleared.\n\t\tchild.kill()\n\t\treturn Promise.resolve()\n\t}\n\n\tfunction failChild(\n\t\tstate: PluginState,\n\t\tchild: ChildProcess,\n\t\terr: Error,\n\t): void {\n\t\tif (state.child !== child) return // stale event from a replaced child\n\t\tvoid teardownChild(state, err)\n\t}\n\n\tfunction rejectAllPending(state: PluginState, err: Error): void {\n\t\tfor (const call of state.pending.values()) {\n\t\t\ttimers.clearCallTimers(call)\n\t\t\tcall.reject(err)\n\t\t}\n\t\tstate.pending.clear()\n\t}\n\n\t// -- invocation --\n\n\tasync function invoke(\n\t\tstate: PluginState,\n\t\thook: HookName,\n\t\tapi: ResourceAPI,\n\t): Promise<unknown> {\n\t\tif (state.disposed) {\n\t\t\tthrow new Error(`plugin ${state.id} sandbox disposed`)\n\t\t}\n\t\tif (state.degraded) {\n\t\t\t// Auto-recover once every crash in the budget has aged out of the\n\t\t\t// respawn window — spawnAndLoad would accept a spawn again anyway.\n\t\t\tconst now = Date.now()\n\t\t\tstate.respawnTimes = state.respawnTimes.filter(\n\t\t\t\t(t) => now - t < config.respawnWindowMs,\n\t\t\t)\n\t\t\tif (state.respawnTimes.length === 0) {\n\t\t\t\tstate.degraded = false\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`plugin ${state.id} unavailable: sandbox crashed repeatedly`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tawait ensureLoaded(state)\n\t\tconst child = state.child\n\t\tif (child === undefined) {\n\t\t\tthrow new Error(`plugin ${state.id} sandbox unavailable`)\n\t\t}\n\n\t\ttry {\n\t\t\treturn await invokeOnChild(state, child, hook, api)\n\t\t} finally {\n\t\t\t// An install hook is one-shot and the host may immediately\n\t\t\t// re-commit the same plugin (an update): a kept-alive worker\n\t\t\t// holding the plugin directory (Windows keeps directory ops\n\t\t\t// pinned while a child is alive) would make the vault-move\n\t\t\t// during the next commit fail with EPERM. Drop the child right\n\t\t\t// after an `onInstall`; the next invocation respawns it.\n\t\t\tif (hook === \"onInstall\") {\n\t\t\t\tstate.respawnTimes = []\n\t\t\t\tawait teardownChild(state)\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction invokeOnChild(\n\t\tstate: PluginState,\n\t\tchild: ChildProcess,\n\t\thook: HookName,\n\t\tapi: ResourceAPI,\n\t): Promise<unknown> {\n\t\tconst callId = nextCallId++\n\t\treturn new Promise((resolveCall, reject) => {\n\t\t\tconst call: PendingCall = {\n\t\t\t\tapi,\n\t\t\t\tresolve: resolveCall,\n\t\t\t\treject,\n\t\t\t\tapiInFlight: 0,\n\t\t\t}\n\t\t\tstate.pending.set(callId, call)\n\t\t\tsyncRef(state)\n\t\t\ttimers.armWatchdog(\n\t\t\t\tcall,\n\t\t\t\tfailCall(\n\t\t\t\t\tstate,\n\t\t\t\t\tchild,\n\t\t\t\t\tcallId,\n\t\t\t\t\t`hung: no activity for ${config.watchdogMs}ms`,\n\t\t\t\t),\n\t\t\t)\n\t\t\ttimers.armHardTimer(\n\t\t\t\tcall,\n\t\t\t\tfailCall(\n\t\t\t\t\tstate,\n\t\t\t\t\tchild,\n\t\t\t\t\tcallId,\n\t\t\t\t\t`exceeded hard timeout ${config.hardTimeoutMs}ms`,\n\t\t\t\t),\n\t\t\t)\n\t\t\ttry {\n\t\t\t\tchild.send({\n\t\t\t\t\ttype: \"invoke\",\n\t\t\t\t\tcallId,\n\t\t\t\t\thook,\n\t\t\t\t} satisfies InvokeRequest)\n\t\t\t} catch (err) {\n\t\t\t\tstate.pending.delete(callId)\n\t\t\t\tsyncRef(state)\n\t\t\t\ttimers.clearCallTimers(call)\n\t\t\t\treject(err instanceof Error ? err : new Error(String(err)))\n\t\t\t}\n\t\t})\n\t}\n\n\tfunction failCall(\n\t\tstate: PluginState,\n\t\tchild: ChildProcess,\n\t\tcallId: number,\n\t\treason: string,\n\t): () => void {\n\t\treturn () =>\n\t\t\tfailChild(\n\t\t\t\tstate,\n\t\t\t\tchild,\n\t\t\t\tnew Error(`plugin ${state.id} ${reason} (call ${callId})`),\n\t\t\t)\n\t}\n\n\t// -- message handling --\n\n\tfunction handleMessage(\n\t\tstate: PluginState,\n\t\tchild: ChildProcess,\n\t\tmsg: WorkerMessage,\n\t): void {\n\t\t// Stale child from a replaced spawn: its messages must never\n\t\t// resolve the current child's load waiter or pending calls.\n\t\tif (state.child !== child) return\n\t\tif (msg === null || typeof msg !== \"object\") return\n\t\tswitch (msg.type) {\n\t\t\tcase \"loaded\": {\n\t\t\t\tconst waiter = state.loadWaiter\n\t\t\t\tstate.loadWaiter = undefined\n\t\t\t\tif (msg.ok) {\n\t\t\t\t\tstate.hooks = msg.hooks ?? [\"detect\"]\n\t\t\t\t\twaiter?.resolve()\n\t\t\t\t} else {\n\t\t\t\t\twaiter?.reject(\n\t\t\t\t\t\tdeserializeError(\n\t\t\t\t\t\t\tmsg.error ?? { name: \"Error\", message: \"plugin load failed\" },\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase \"result\": {\n\t\t\t\tconst call = state.pending.get(msg.callId)\n\t\t\t\tif (call === undefined) return\n\t\t\t\tstate.pending.delete(msg.callId)\n\t\t\t\tsyncRef(state)\n\t\t\t\ttimers.clearCallTimers(call)\n\t\t\t\tif (msg.ok) {\n\t\t\t\t\tcall.resolve(msg.value)\n\t\t\t\t} else {\n\t\t\t\t\tcall.reject(\n\t\t\t\t\t\tdeserializeError(\n\t\t\t\t\t\t\tmsg.error ?? { name: \"Error\", message: \"hook failed\" },\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase \"api\": {\n\t\t\t\tconst call = state.pending.get(msg.callId)\n\t\t\t\tif (call === undefined) return\n\t\t\t\t// Pause the watchdog while the host executes the API call —\n\t\t\t\t// a slow readFile/probeVideo is host work, not a hung plugin.\n\t\t\t\tcall.apiInFlight += 1\n\t\t\t\ttimers.pauseWatchdog(call)\n\t\t\t\tvoid dispatchApi(\n\t\t\t\t\tchild,\n\t\t\t\t\tstate,\n\t\t\t\t\tmsg.callId,\n\t\t\t\t\tmsg.apiCallId,\n\t\t\t\t\tcall,\n\t\t\t\t\tmsg.method,\n\t\t\t\t\tmsg.args,\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase \"log\": {\n\t\t\t\tconst call = state.pending.get(msg.callId)\n\t\t\t\tif (call === undefined) return\n\t\t\t\tif (call.apiInFlight === 0) {\n\t\t\t\t\ttimers.armWatchdog(\n\t\t\t\t\t\tcall,\n\t\t\t\t\t\tfailCall(\n\t\t\t\t\t\t\tstate,\n\t\t\t\t\t\t\tchild,\n\t\t\t\t\t\t\tmsg.callId,\n\t\t\t\t\t\t\t`hung: no activity for ${config.watchdogMs}ms`,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tdispatchLog(state.id, msg.method, msg.args)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function dispatchApi(\n\t\tchild: ChildProcess,\n\t\tstate: PluginState,\n\t\tcallId: number,\n\t\tapiCallId: number,\n\t\tcall: PendingCall,\n\t\tmethod: ApiMethodName,\n\t\targs: readonly unknown[],\n\t): Promise<void> {\n\t\tconst respond = (\n\t\t\tok: boolean,\n\t\t\tvalue?: unknown,\n\t\t\terror?: SerializedError,\n\t\t): void => {\n\t\t\t// The child may have died while the API call was in flight.\n\t\t\tif (state.child !== child) return\n\t\t\ttry {\n\t\t\t\tchild.send({ type: \"apiResult\", apiCallId, ok, value, error })\n\t\t\t} catch {\n\t\t\t\t// Channel closed — nothing to deliver.\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tif (!isApiMethod(method) || LOG_METHOD_NAMES.has(method)) {\n\t\t\t\trespond(false, undefined, {\n\t\t\t\t\tname: \"Error\",\n\t\t\t\t\tmessage: `unknown API method: ${String(method)}`,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Manifest permission gate, read from the single capability\n\t\t\t// table: every sandbox-gated method maps to its capability\n\t\t\t// and is denied unless the manifest declared it. The asset\n\t\t\t// surface then routes to the wired handler with the owning\n\t\t\t// plugin id — the ResourceAPI instance itself has no plugin\n\t\t\t// identity (a shared API serves every plugin's hooks).\n\t\t\tconst capability = CAPABILITY_BY_METHOD.get(method)\n\t\t\tif (\n\t\t\t\tcapability !== undefined &&\n\t\t\t\tstate.permissions?.[capability] !== true\n\t\t\t) {\n\t\t\t\trespond(false, undefined, {\n\t\t\t\t\tname: \"POLICY\",\n\t\t\t\t\tcode: \"POLICY\",\n\t\t\t\t\tmessage: `${capability} permission denied for plugin ${state.id} — declare \"${capability}\": true in the manifest to use ${method}()`,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (ASSET_METHODS.has(method)) {\n\t\t\t\tconst handler = config.pluginAssets\n\t\t\t\tif (handler === undefined) {\n\t\t\t\t\trespond(false, undefined, {\n\t\t\t\t\t\tname: \"UNAVAILABLE\",\n\t\t\t\t\t\tcode: \"UNAVAILABLE\",\n\t\t\t\t\t\tmessage: `${method}() is unavailable on this host — plugin asset downloads need the app server runtime`,\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// RPC boundary: the child-side proxy forwards args verbatim,\n\t\t\t\t// so the contract order is guaranteed (request object or\n\t\t\t\t// request array for `download`, string path for the rest).\n\t\t\t\tif (method === \"download\") {\n\t\t\t\t\tconst request = args[0] as\n\t\t\t\t\t\t| PluginDownloadRequest\n\t\t\t\t\t\t| readonly PluginDownloadRequest[]\n\t\t\t\t\tconst results = await handler.download(state.id, request)\n\t\t\t\t\t// Wire rule: array in → array out, single in → single out.\n\t\t\t\t\trespond(true, Array.isArray(request) ? results : results[0])\n\t\t\t\t} else if (method === \"statAsset\") {\n\t\t\t\t\trespond(true, await handler.statAsset(state.id, args[0] as string))\n\t\t\t\t} else if (method === \"readAsset\") {\n\t\t\t\t\trespond(true, await handler.readAsset(state.id, args[0] as string))\n\t\t\t\t} else {\n\t\t\t\t\trespond(true, await handler.deleteAsset(state.id, args[0] as string))\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// RPC boundary: the child-side proxy is generated from the same\n\t\t\t// method list, so args always arrive in contract order.\n\t\t\tconst fn = call.api[method] as (...a: readonly unknown[]) => unknown\n\t\t\trespond(true, await fn(...args))\n\t\t} catch (err) {\n\t\t\trespond(false, undefined, serializeError(err))\n\t\t} finally {\n\t\t\tcall.apiInFlight -= 1\n\t\t\t// Resume the watchdog once the host-side work for this call\n\t\t\t// has drained and the call is still alive.\n\t\t\tif (call.apiInFlight === 0 && state.pending.get(callId) === call) {\n\t\t\t\ttimers.armWatchdog(\n\t\t\t\t\tcall,\n\t\t\t\t\tfailCall(\n\t\t\t\t\t\tstate,\n\t\t\t\t\t\tchild,\n\t\t\t\t\t\tcallId,\n\t\t\t\t\t\t`hung: no activity for ${config.watchdogMs}ms`,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Plugin log sink. The child-side proxy forwards log calls here, where\n\t * the owning plugin id is known — ResourceAPI.log* stay no-ops because a\n\t * shared API instance (e.g. one detect pass fanning out to every plugin)\n\t * cannot attribute a log line to the plugin that emitted it.\n\t */\n\tfunction dispatchLog(\n\t\tpluginId: string,\n\t\tmethod: \"logInfo\" | \"logWarn\" | \"logError\",\n\t\targs: readonly unknown[],\n\t): void {\n\t\tconst message = typeof args[0] === \"string\" ? args[0] : String(args[0])\n\t\tconst data = isPlainRecord(args[1]) ? args[1] : undefined\n\t\tconst line = `[plugin:${pluginId}] ${message}`\n\t\tconst extra = data === undefined ? [] : [data]\n\t\ttry {\n\t\t\tif (method === \"logInfo\") console.log(line, ...extra)\n\t\t\telse if (method === \"logWarn\") console.warn(line, ...extra)\n\t\t\telse console.error(line, ...extra)\n\t\t} catch {\n\t\t\t// Logging must never break the host.\n\t\t}\n\t}\n\n\treturn { loadPlugin, unloadPlugin, disposeExcept, disposeAll }\n}\n\nfunction isApiMethod(name: unknown): name is ApiMethodName {\n\treturn (\n\t\ttypeof name === \"string\" &&\n\t\t(API_METHOD_NAMES as readonly string[]).includes(name)\n\t)\n}\n\nfunction asError(value: unknown): Error {\n\treturn value instanceof Error ? value : new Error(String(value))\n}\n\n/**\n * The canonical on-disk form of a path, falling back to the resolved form\n * when the file does not exist yet (a just-built bundle, a not-yet-created\n * vault). Node's permission model checks the path the ESM loader resolved\n * through `realpathSync`, so a plugin dir reached via a symlink must be\n * granted and passed in its canonical form — otherwise the module resolves\n * outside its own grant and the sandbox refuses to load it.\n */\nfunction realPathOrResolve(path: string): string {\n\ttry {\n\t\treturn realpathSync(path)\n\t} catch {\n\t\treturn resolve(path)\n\t}\n}\n\n/**\n * vite-node's transform rewrites `import.meta` without a working `.resolve`\n * (unlike vitest's), so the worker entry URL falls back to the module's own\n * location. The packaged server bundle has a working `import.meta.resolve`\n * that still cannot see `@hoardodile/host` — that path also falls through\n * here. The sandbox runs from one of two places: the package dist root\n * (`dist/chunks/worker-entry.mjs`, see scripts/postbuild.mjs), or — in dev,\n * where the `development` export condition resolves `@hoardodile/host` to\n * source — `src/sandbox/worker-entry.mjs` next to this module.\n */\nfunction resolveWorkerEntryUrl(): string {\n\ttry {\n\t\tif (typeof import.meta.resolve === \"function\") {\n\t\t\treturn import.meta.resolve(\"@hoardodile/host/worker-entry\")\n\t\t}\n\t} catch {\n\t\t// No package exports (packaged server dist, or a broken install).\n\t}\n\treturn workerEntryUrlFromModule(import.meta.url)\n}\n\nfunction workerEntryUrlFromModule(moduleUrl: string): string {\n\tconst distIdx = moduleUrl.lastIndexOf(\"/dist/\")\n\tif (distIdx >= 0) {\n\t\tconst distRoot = moduleUrl.slice(0, distIdx + \"/dist/\".length)\n\t\treturn new URL(\"chunks/worker-entry.mjs\", distRoot).href\n\t}\n\treturn new URL(\"worker-entry.mjs\", moduleUrl).href\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n","import { createHash } from \"node:crypto\"\nimport {\n\tcpSync,\n\texistsSync,\n\tmkdirSync,\n\treaddirSync,\n\treadFileSync,\n\trenameSync,\n\trmSync,\n\tstatSync,\n} from \"node:fs\"\nimport { join } from \"node:path\"\nimport type { PluginManifest } from \"@hoardodile/sdk-types\"\n\n/**\n * The host-reserved plugin subdirectory a seed never touches: downloaded\n * vault assets live there (see `VersionPaths.pluginVaultDir`) and the\n * tree comparison below must ignore them, or the presence of a vault\n * would mark every seeded plugin as changed on every boot.\n */\nconst VAULT_DIR_NAME = \"vault\"\n\n/**\n * Copy the configured plugin directories into `pluginsDir` so discovery\n * can load them as installed disk plugins. Each source is itself a plugin\n * directory (`manifest.json` at its root). A seed replaces\n * `pluginsDir/<manifest.id>` only when the file set, sizes, or content\n * hashes differ — identical trees are left untouched so a boot does not\n * dirty a synced `versions/` tree.\n */\nexport function seedPlugins(\n\tpluginsDir: string,\n\tseedPluginDirs: readonly string[] | undefined,\n): void {\n\tif (seedPluginDirs === undefined || seedPluginDirs.length === 0) {\n\t\treturn\n\t}\n\n\tfor (const dir of seedPluginDirs) {\n\t\ttry {\n\t\t\tcopyPluginDir(dir, pluginsDir)\n\t\t} catch (err) {\n\t\t\t// A locked directory (e.g. a plugin watch rebuilding dist on\n\t\t\t// Windows) must not abort the whole load — the plugin simply\n\t\t\t// stays as-is or surfaces as missing.\n\t\t\tconsole.warn(\n\t\t\t\t`[plugin-loader] failed to seed plugin dir ${dir}: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t)\n\t\t}\n\t}\n}\n\n/**\n * Copy a single plugin directory into `pluginsDir/<manifest.id>`.\n * Returns false (and copies nothing) when the directory is not a valid\n * plugin. An existing destination that already matches the source tree\n * is left untouched.\n *\n * The host-managed `vault/` subdirectory is never part of the seed: it\n * is skipped in both the tree comparison and the copy, and stashed\n * aside during a replacement — reseeding (the update channel of bundled\n * plugins) preserves the plugin's downloaded assets.\n */\nfunction copyPluginDir(srcDir: string, pluginsDir: string): boolean {\n\tconst id = readManifestId(srcDir)\n\tif (id === undefined) return false\n\tconst dstDir = join(pluginsDir, id)\n\tif (existsSync(dstDir) && treesMatch(srcDir, dstDir)) {\n\t\treturn true\n\t}\n\tif (existsSync(dstDir)) {\n\t\tconst vaultDir = join(dstDir, VAULT_DIR_NAME)\n\t\tconst stashDir = join(pluginsDir, `.vault-${id}-${Date.now()}`)\n\t\tconst hasVault = existsSync(vaultDir)\n\t\tif (hasVault) {\n\t\t\t// Same-volume rename: the vault leaves the tree before the\n\t\t\t// replacement lands, then comes back — a crash in between can\n\t\t\t// at worst strand the stash as a dot-directory (skipped by\n\t\t\t// discovery, version copies and the sync tooling).\n\t\t\trenameSync(vaultDir, stashDir)\n\t\t}\n\t\trmSync(dstDir, { recursive: true, force: true })\n\t\tmkdirSync(dstDir, { recursive: true })\n\t\tfor (const f of readdirSync(srcDir)) {\n\t\t\tif (f === VAULT_DIR_NAME) continue\n\t\t\tcpSync(join(srcDir, f), join(dstDir, f), { recursive: true })\n\t\t}\n\t\tif (existsSync(stashDir)) {\n\t\t\trenameSync(stashDir, join(dstDir, VAULT_DIR_NAME))\n\t\t}\n\t\treturn true\n\t}\n\tmkdirSync(dstDir, { recursive: true })\n\tfor (const f of readdirSync(srcDir)) {\n\t\tif (f === VAULT_DIR_NAME) continue\n\t\tcpSync(join(srcDir, f), join(dstDir, f), { recursive: true })\n\t}\n\treturn true\n}\n\nfunction readManifestId(dir: string): string | undefined {\n\tconst manifestPath = join(dir, \"manifest.json\")\n\tif (!existsSync(manifestPath)) return undefined\n\tlet manifest: PluginManifest\n\ttry {\n\t\tmanifest = JSON.parse(readFileSync(manifestPath, \"utf-8\"))\n\t} catch {\n\t\treturn undefined\n\t}\n\tif (typeof manifest.id !== \"string\" || manifest.id.length === 0) {\n\t\treturn undefined\n\t}\n\treturn manifest.id\n}\n\n/**\n * True when both directories contain the same relative file set, each\n * with matching byte size and sha256. Directories that do not exist are\n * unequal unless both are missing. Comparison ignores mtime so a copy\n * does not look like a change on the next boot.\n */\nfunction treesMatch(a: string, b: string): boolean {\n\tconst left = fingerprintTree(a)\n\tconst right = fingerprintTree(b)\n\tif (left.size !== right.size) return false\n\tfor (const [path, fingerprint] of left) {\n\t\tif (right.get(path) !== fingerprint) return false\n\t}\n\treturn true\n}\n\nfunction fingerprintTree(root: string): Map<string, string> {\n\tconst out = new Map<string, string>()\n\tif (!existsSync(root)) return out\n\twalkFiles(root, \"\", out)\n\treturn out\n}\n\nfunction walkFiles(\n\tabsDir: string,\n\trelDir: string,\n\tout: Map<string, string>,\n): void {\n\tconst entries = readdirSync(absDir, { withFileTypes: true })\n\tfor (const entry of entries) {\n\t\t// Root-level host-reserved vault directory: never fingerprinted,\n\t\t// never copied — see {@link VAULT_DIR_NAME}.\n\t\tif (relDir.length === 0 && entry.name === VAULT_DIR_NAME) continue\n\t\tconst rel = relDir.length === 0 ? entry.name : `${relDir}/${entry.name}`\n\t\tconst abs = join(absDir, entry.name)\n\t\tif (entry.isDirectory()) {\n\t\t\twalkFiles(abs, rel, out)\n\t\t\tcontinue\n\t\t}\n\t\tif (!entry.isFile()) continue\n\t\tconst st = statSync(abs)\n\t\tconst hash = createHash(\"sha256\").update(readFileSync(abs)).digest(\"hex\")\n\t\tout.set(rel.replaceAll(\"\\\\\", \"/\"), `${st.size}:${hash}`)\n\t}\n}\n","import type { PluginManifestId } from \"@hoardodile/sdk-types\"\nimport { createPluginActivation } from \"./activation.ts\"\nimport type { PluginRegistry, PluginRegistryEntry } from \"./api-types.ts\"\nimport { createPluginDiscovery } from \"./discovery.ts\"\nimport { createPluginSandbox, type PluginSandbox } from \"./sandbox/host.ts\"\nimport { seedPlugins } from \"./seed.ts\"\nimport type { PluginSettingsStore } from \"./settings-store.ts\"\n\nexport { seedPlugins } from \"./seed.ts\"\n\n/**\n * Loads plugins into a live {@link PluginRegistry}: seed the configured\n * plugin directories, discover, activate, and sort by priority.\n * `loadAll`/`rescan` are serialized — an overlapping run would replace\n * workers out from under the previous run's in-flight loads.\n *\n * The previous registry and its workers are only replaced AFTER the new\n * registry finished activating: a failed load never strands a stale\n * registry whose workers were already disposed (every hook would then\n * fail until the next restart).\n */\nexport type PluginLoader = {\n\treadonly loadAll: () => Promise<PluginRegistry>\n\treadonly rescan: () => Promise<PluginRegistry>\n\t/** The current registry; throws when `loadAll` has not run yet. */\n\treadonly getRegistry: () => PluginRegistry\n}\n\nexport type PluginLoaderDeps = {\n\treadonly builtinDir?: string\n\treadonly devPluginDirs?: readonly string[]\n\t/**\n\t * Plugin directories (each with `manifest.json` at its root, e.g. a\n\t * built plugin's `dist/`) that are seeded into `pluginsDir` on every\n\t * load. Each plugin is copied into `pluginsDir/<manifest.id>` before\n\t * discovery runs when the destination tree differs — so a seeded\n\t * plugin behaves like a regular installed one (DB settings, caching,\n\t * uninstall). The server passes an empty list and seeds through\n\t * `writeVersioned` instead. When omitted, nothing is seeded.\n\t */\n\treadonly seedPluginDirs?: readonly string[]\n\treadonly pluginsDir: string\n\treadonly settings: PluginSettingsStore\n\treadonly disableDevPlugins?: boolean\n\t/**\n\t * Worker-thread sandbox that executes plugin hooks. Optional so tests\n\t * without any `main.js` on disk can omit it; the default spawns real\n\t * workers when a loadable bundle is found.\n\t */\n\treadonly sandbox?: PluginSandbox\n\t/**\n\t * Optional timing sink for boot diagnostics — receives the duration of\n\t * each `loadAll` step. Defaults to a no-op so tests stay quiet.\n\t */\n\treadonly onTiming?: (step: string, ms: number) => void\n}\n\n/**\n * Create the plugin loader. Workers are respawned on every load so a\n * rescan picks up changed plugin code — the new worker re-imports\n * `main.js`, bypassing the main thread's ESM module cache. Configured\n * {@link PluginLoaderDeps.seedPluginDirs} are copied into `pluginsDir`\n * on each load.\n */\nexport function createPluginLoader(deps: PluginLoaderDeps): PluginLoader {\n\tlet registry: PluginRegistry | undefined\n\t// Serializes loadAll/rescan: an overlapping run would replace workers\n\t// out from under the previous run's in-flight plugin loads.\n\tlet chain: Promise<unknown> = Promise.resolve()\n\n\tconst discovery = createPluginDiscovery(deps)\n\tconst sandbox = deps.sandbox ?? createPluginSandbox()\n\tconst activation = createPluginActivation({ sandbox })\n\n\tfunction loadAll(): Promise<PluginRegistry> {\n\t\tconst run = chain.then(doLoadAll)\n\t\tchain = run.catch(() => {})\n\t\treturn run\n\t}\n\n\tasync function doLoadAll(): Promise<PluginRegistry> {\n\t\t// Seed first — tolerant of individual failures: a locked directory\n\t\t// (Windows/AV races with a plugin watch rebuilding dist) only skips\n\t\t// that plugin instead of aborting the whole load.\n\t\tconst seedStart = performance.now()\n\t\tseedPlugins(deps.pluginsDir, deps.seedPluginDirs)\n\t\tdeps.onTiming?.(\"seed\", Math.round(performance.now() - seedStart))\n\n\t\tconst discoverStart = performance.now()\n\t\tconst { found, missing } = await discovery.discover()\n\t\tdeps.onTiming?.(\"discover\", Math.round(performance.now() - discoverStart))\n\n\t\t// Activate BEFORE disposing anything from the previous registry:\n\t\t// same-id plugins replace their worker in the sandbox, and a\n\t\t// failure anywhere above leaves the previous registry (and its\n\t\t// workers) fully alive instead of stranding a stale registry with\n\t\t// disposed workers.\n\t\tconst activateStart = performance.now()\n\t\tconst activated = await activation.activateAll(found)\n\t\tdeps.onTiming?.(\"activate\", Math.round(performance.now() - activateStart))\n\n\t\tconst failing = activation.createFailingEntries(missing)\n\n\t\tconst entries = [...activated, ...failing]\n\t\tentries.sort((a, b) => a.priority - b.priority)\n\n\t\tconst next = buildRegistry(entries)\n\t\t// Plugins that left the registry (uninstalled/removed) free their\n\t\t// workers; everything still registered keeps the worker activation\n\t\t// just loaded.\n\t\tawait sandbox.disposeExcept(new Set(next.getAll().map((e) => e.id)))\n\t\tregistry = next\n\t\treturn registry\n\t}\n\n\tasync function rescan(): Promise<PluginRegistry> {\n\t\treturn loadAll()\n\t}\n\n\tfunction getRegistry(): PluginRegistry {\n\t\tif (registry === undefined) {\n\t\t\tthrow new Error(\"Plugin registry not initialised — call loadAll() first\")\n\t\t}\n\t\treturn registry\n\t}\n\n\treturn { loadAll, rescan, getRegistry }\n}\n\n/**\n * Build the immutable-ish registry from entries, keyed by plugin UUID.\n * Duplicate ids keep the first entry and warn (the UUID contract is\n * unique — duplicates signal a broken install). Duplicates are dropped\n * from every accessor, not just the id map: keeping a duplicate in\n * `getAll()` would make enabled-list iteration invoke a plugin whose\n * worker activation already replaced (and disposed) the first load.\n * `updateEntry` patches a single entry in place and re-sorts by priority.\n */\nexport function buildRegistry(\n\tentries: readonly PluginRegistryEntry[],\n): PluginRegistry {\n\tconst byId = new Map<PluginManifestId, PluginRegistryEntry>()\n\tconst deduped: PluginRegistryEntry[] = []\n\tfor (const entry of entries) {\n\t\tconst existing = byId.get(entry.id)\n\t\tif (existing !== undefined) {\n\t\t\tconsole.warn(\n\t\t\t\t`[plugin-loader] skipping ${entry.id}: UUID conflicts with already registered plugin (${existing.manifest.name ?? existing.id}), keeping first`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\t\tbyId.set(entry.id, entry)\n\t\tdeduped.push(entry)\n\t}\n\n\tlet sortedEntries = deduped\n\n\treturn {\n\t\tgetAll(): readonly PluginRegistryEntry[] {\n\t\t\treturn sortedEntries\n\t\t},\n\t\tgetEnabled(): readonly PluginRegistryEntry[] {\n\t\t\treturn sortedEntries.filter((e) => e.enabled)\n\t\t},\n\t\tgetById(id: PluginManifestId): PluginRegistryEntry | undefined {\n\t\t\treturn byId.get(id)\n\t\t},\n\t\tgetBuiltin(): PluginRegistryEntry | undefined {\n\t\t\treturn sortedEntries.find((e) => e.builtin)\n\t\t},\n\t\tgetForResource(\n\t\t\tresPluginId: PluginManifestId,\n\t\t): PluginRegistryEntry | undefined {\n\t\t\tconst entry = byId.get(resPluginId)\n\t\t\tif (entry === undefined || !entry.enabled) return undefined\n\t\t\treturn entry\n\t\t},\n\t\tupdateEntry(\n\t\t\tid: PluginManifestId,\n\t\t\tpatch: Partial<\n\t\t\t\tPick<\n\t\t\t\t\tPluginRegistryEntry,\n\t\t\t\t\t\"enabled\" | \"priority\" | \"pinned\" | \"color\" | \"missing\"\n\t\t\t\t>\n\t\t\t>,\n\t\t): void {\n\t\t\tconst index = sortedEntries.findIndex((e) => e.id === id)\n\t\t\tif (index === -1) return\n\t\t\tconst old = sortedEntries[index]\n\t\t\tif (old === undefined) return\n\t\t\tconst updated: PluginRegistryEntry = {\n\t\t\t\tid: old.id,\n\t\t\t\tmanifest: old.manifest,\n\t\t\t\tplugin: old.plugin,\n\t\t\t\tdiskPath: old.diskPath,\n\t\t\t\tenabled: patch.enabled ?? old.enabled,\n\t\t\t\tpriority: patch.priority ?? old.priority,\n\t\t\t\tpinned: patch.pinned ?? old.pinned,\n\t\t\t\tcolor: patch.color ?? old.color,\n\t\t\t\tmissing: patch.missing ?? old.missing,\n\t\t\t\tbuiltin: old.builtin,\n\t\t\t\tdev: old.dev,\n\t\t\t}\n\t\t\tsortedEntries = [\n\t\t\t\t...sortedEntries.slice(0, index),\n\t\t\t\tupdated,\n\t\t\t\t...sortedEntries.slice(index + 1),\n\t\t\t]\n\t\t\tsortedEntries.sort((a, b) => a.priority - b.priority)\n\t\t\tbyId.set(id, updated)\n\t\t},\n\t}\n}\n","/**\n * Process-wide LRU cache for host-side probe results (image/video/audio\n * metadata and animation flags). Source archives are immutable per\n * fileVersion, so entries never need explicit invalidation — the cache\n * scope carries the version and the LRU bound handles eviction.\n */\n\n/** Process-wide bound (entries) for the host-side probe result cache. */\nexport const PLUGIN_PROBE_CACHE_MAX_ENTRIES = 512\n\n/** Cacheable probe result; `undefined` marks a probe that found nothing. */\ntype ProbeValue = object | boolean | undefined\n\nexport type PluginProbeCache = {\n\t/**\n\t * Return the cached result for `key`, computing and storing it on a\n\t * miss. Concurrent callers for the same key share one computation.\n\t */\n\treadonly getOrCompute: <T extends ProbeValue>(\n\t\tkey: string,\n\t\tcompute: () => Promise<T>,\n\t) => Promise<T>\n}\n\n/**\n * Create the probe LRU cache. Keys are caller-composed (probe kind +\n * version + path); concurrent misses single-flight through one shared\n * computation; failures are evicted so the next caller retries.\n */\nexport function createProbeCache(\n\tmaxEntries = PLUGIN_PROBE_CACHE_MAX_ENTRIES,\n): PluginProbeCache {\n\t// Promise-valued cells: a miss is stored before it settles, so\n\t// concurrent probes for the same key single-flight through one\n\t// computation.\n\tconst cells = new Map<string, Promise<ProbeValue>>()\n\n\treturn {\n\t\tgetOrCompute<T extends ProbeValue>(\n\t\t\tkey: string,\n\t\t\tcompute: () => Promise<T>,\n\t\t): Promise<T> {\n\t\t\tconst existing = cells.get(key)\n\t\t\tif (existing !== undefined) {\n\t\t\t\t// Refresh LRU position.\n\t\t\t\tcells.delete(key)\n\t\t\t\tcells.set(key, existing)\n\t\t\t\t// Type-erased store: each key is namespaced by probe kind, so\n\t\t\t\t// the stored promise always resolves to the caller's T.\n\t\t\t\treturn existing as Promise<T>\n\t\t\t}\n\t\t\tconst promise = compute()\n\t\t\tcells.set(key, promise)\n\t\t\tpromise.catch(() => {\n\t\t\t\t// Failures are not cached — the next caller retries.\n\t\t\t\tif (cells.get(key) === promise) cells.delete(key)\n\t\t\t})\n\t\t\tif (cells.size > maxEntries) {\n\t\t\t\tconst oldest = cells.keys().next().value\n\t\t\t\tif (oldest !== undefined) cells.delete(oldest)\n\t\t\t}\n\t\t\treturn promise\n\t\t},\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/activation.ts","../src/errors.ts","../src/archive/format.ts","../src/archive/name-decode.ts","../src/archive/zip-entries.ts","../src/archive/7z.ts","../src/archive/extract.ts","../src/archive/materialize.ts","../src/archive/nested-entry.ts","../src/archive/extract-archive.ts","../src/archive/nested-cd-cache.ts","../src/archive/pack.ts","../src/hash.ts","../src/nested-view.ts","../src/probe/image.ts","../src/probe/sniff.ts","../src/hoard/order-manifest.ts","../src/directory-container.ts","../src/directory-api.ts","../src/api.ts","../src/capability-guard.ts","../src/dev-runner.ts","../src/discovery.ts","../src/install-api.ts","../src/hooks.ts","../src/sandbox/call-timers.ts","../src/sandbox/protocol.ts","../src/sandbox/sandboxed-plugin.ts","../src/sandbox/host.ts","../src/seed.ts","../src/loader.ts","../src/probe-cache.ts"],"names":["sep","join","existsSync","mkdir","dirname","writeFile","pipeline","createWriteStream","stat","rename","rm","resolve","randomUUID","createReadStream","readFile","buffer","fileTypeFromName","readdir","normalize","PLUGIN_READ_FILE_MAX_BYTES","size","require","readdirSync","pluginManifestSchema","pluginAssetError","unavailableAsset","spawn","tmpdir","readFileSync","createHash"],"mappings":"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AA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AA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","file":"index.js","sourcesContent":["import { existsSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport type {\n\tFoundPlugin,\n\tMissingPlugin,\n\tPluginDefinition,\n\tPluginRegistryEntry,\n} from \"./api-types.ts\"\nimport { createFailingPlugin } from \"./define-plugin.ts\"\nimport type { PluginSandbox } from \"./sandbox/host.ts\"\n\n/**\n * Plugin activation: turns discovered plugin candidates (found on disk\n * or recorded as missing) into ready {@link PluginRegistryEntry}s by\n * loading their `main.js` into the sandbox. Loads run in parallel —\n * sequential worker spawns would multiply the boot cost on slow or\n * antivirus-scanned disks.\n */\nexport type PluginActivation = {\n\t/** Load every found plugin's `main.js` into the sandbox and return the full registry entries. */\n\treadonly activateAll: (\n\t\tfound: readonly FoundPlugin[],\n\t) => Promise<PluginRegistryEntry[]>\n\t/**\n\t * Build registry entries for plugins whose directory is missing, each\n\t * carrying a failing definition so callers can still render them as\n\t * broken in the UI.\n\t */\n\treadonly createFailingEntries: (\n\t\tmissing: readonly MissingPlugin[],\n\t) => PluginRegistryEntry[]\n}\n\nexport type PluginActivationDeps = {\n\treadonly sandbox: PluginSandbox\n}\n\n/**\n * Create the activation pipeline. The sandbox decides worker lifetimes:\n * enabled plugins keep their worker alive, disabled ones only probe the\n * hook list and respawn lazily if a bound resource still invokes a hook.\n */\nexport function createPluginActivation(\n\tdeps: PluginActivationDeps,\n): PluginActivation {\n\tasync function activateAll(\n\t\tfound: readonly FoundPlugin[],\n\t): Promise<PluginRegistryEntry[]> {\n\t\t// Load plugin bundles in parallel — sequential worker spawns multiply\n\t\t// the boot cost on slow or antivirus-scanned disks. Array order is\n\t\t// kept by `map`, and `loadAll` re-sorts by priority afterwards anyway.\n\t\treturn Promise.all(\n\t\t\tfound.map(async (candidate) => {\n\t\t\t\tconst plugin = await loadDiskPlugin(deps.sandbox, candidate)\n\t\t\t\treturn {\n\t\t\t\t\tid: candidate.id,\n\t\t\t\t\tmanifest: candidate.manifest,\n\t\t\t\t\tenabled: candidate.enabled,\n\t\t\t\t\tpriority: candidate.priority,\n\t\t\t\t\tpinned: candidate.pinned,\n\t\t\t\t\tcolor: candidate.color,\n\t\t\t\t\tmissing: false,\n\t\t\t\t\tbuiltin: candidate.source === \"builtin\",\n\t\t\t\t\tdev: candidate.source === \"dev\",\n\t\t\t\t\tplugin,\n\t\t\t\t\tdiskPath: candidate.diskPath,\n\t\t\t\t}\n\t\t\t}),\n\t\t)\n\t}\n\n\tfunction createFailingEntries(\n\t\tmissing: readonly MissingPlugin[],\n\t): PluginRegistryEntry[] {\n\t\treturn missing.map((m) => ({\n\t\t\tid: m.id,\n\t\t\tmanifest: m.manifest,\n\t\t\tenabled: m.enabled,\n\t\t\tpriority: m.priority,\n\t\t\tpinned: m.pinned,\n\t\t\tcolor: m.color,\n\t\t\tmissing: true,\n\t\t\tbuiltin: false,\n\t\t\tdev: false,\n\t\t\tplugin: createFailingPlugin([\"plugin directory not found\"]),\n\t\t}))\n\t}\n\n\treturn { activateAll, createFailingEntries }\n}\n\nasync function loadDiskPlugin(\n\tsandbox: PluginSandbox,\n\tcandidate: FoundPlugin,\n): Promise<PluginDefinition> {\n\tconst mainJsPath = join(candidate.diskPath, \"main.js\")\n\tif (!existsSync(mainJsPath)) {\n\t\tconsole.warn(`[plugin-activation] ${candidate.id}: no main.js, not loaded`)\n\t\treturn createFailingPlugin([\"main.js not found\"])\n\t}\n\n\t// Enabled plugins keep their worker alive; disabled ones only probe the\n\t// hook list — their worker respawns lazily if a bound resource still\n\t// invokes a hook.\n\tconst plugin = await sandbox.loadPlugin({\n\t\tid: candidate.id,\n\t\tmainPath: mainJsPath,\n\t\teager: candidate.enabled,\n\t\t// The sandbox enforces manifest-gated API surface (container).\n\t\tpermissions: candidate.manifest.permissions,\n\t})\n\tif (plugin === undefined) {\n\t\treturn createFailingPlugin([\"main.js load error\"])\n\t}\n\treturn plugin\n}\n","/**\n * Host-local domain error. Mirrors the wire-format taxonomy of the app's\n * domain errors (`@hoardodile/shared`) so errors raised here keep their\n * code/kind/details across the package boundary — the server's error\n * translation recognizes this shape structurally via `isDomainError`.\n *\n * The host deliberately keeps its dependency graph minimal (consts +\n * sdk-types only) and does not depend on `@hoardodile/shared`; the\n * mirror is kept in sync by the bridge test in\n * `apps/server/src/infra/host-errors.test.ts`.\n */\nexport const domainErrorCodes = [\n\t\"NOT_FOUND\",\n\t\"CONFLICT\",\n\t\"VALIDATION\",\n\t\"UNAUTHORIZED\",\n\t\"FORBIDDEN\",\n\t\"RATE_LIMITED\",\n\t\"UNSUPPORTED\",\n\t\"INTERNAL\",\n] as const\n\nexport type DomainErrorCode = (typeof domainErrorCodes)[number]\n\n/**\n * Host-local domain error carrying a wire-compatible code/kind/details\n * triple. Thrown by host services (containers, loaders, hooks) so the\n * server can translate them into its own domain errors verbatim.\n */\nexport class DomainError extends Error {\n\treadonly code: DomainErrorCode\n\treadonly kind: string\n\treadonly details?: Readonly<Record<string, unknown>>\n\n\tconstructor(\n\t\tcode: DomainErrorCode,\n\t\tkind: string,\n\t\tmessage: string,\n\t\tdetails?: Readonly<Record<string, unknown>>,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = \"DomainError\"\n\t\tthis.code = code\n\t\tthis.kind = kind\n\t\tif (details !== undefined) this.details = details\n\t}\n}\n\n/** Convenience constructor for `VALIDATION` errors. */\nexport function invalid(\n\tkind: string,\n\tmessage: string,\n\tdetails?: Readonly<Record<string, unknown>>,\n): DomainError {\n\treturn new DomainError(\"VALIDATION\", kind, message, details)\n}\n\n/** Convenience constructor for `CONFLICT` errors. */\nexport function conflict(\n\tkind: string,\n\tmessage: string,\n\tdetails?: Readonly<Record<string, unknown>>,\n): DomainError {\n\treturn new DomainError(\"CONFLICT\", kind, message, details)\n}\n\n/** Convenience constructor for `NOT_FOUND` errors. */\nexport function notFound(\n\tkind: string,\n\tmessage: string,\n\tdetails?: Readonly<Record<string, unknown>>,\n): DomainError {\n\treturn new DomainError(\"NOT_FOUND\", kind, message, details)\n}\n","/**\n * Container format identification from magic bytes. The single source of\n * truth for \"what archive format is this\" — the engine matrix in\n * `./index.ts` maps each format to its list/extract/address\n * capabilities, and every dispatch (yauzl vs 7-Zip, nested resolver vs\n * whole-archive) keys off {@link sniffContainerFormat}.\n */\n\n/** Byte window read from an entry to decide container-ness. */\nexport const SNIFF_WINDOW_BYTES = 4100\n\nexport type ContainerFormat = \"zip\" | \"tar\" | \"7z\" | \"rar\" | \"xz\" | \"gzip\"\n\n/** True when `head` starts with the given magic byte sequence. */\nfunction hasMagic(head: Uint8Array, magic: readonly number[]): boolean {\n\tif (head.byteLength < magic.length) return false\n\tfor (let i = 0; i < magic.length; i++) {\n\t\tif (head[i] !== magic[i]) return false\n\t}\n\treturn true\n}\n\n/**\n * Identify a container format from its leading bytes. Zip is addressable\n * as a nested container (`outer!inner`); the rest (tar/7z/rar/xz/gzip)\n * are only extractable as whole archives (see `extract-archive.ts` and\n * `extract.ts`).\n */\nexport function sniffContainerFormat(\n\thead: Uint8Array,\n): ContainerFormat | undefined {\n\tif (hasMagic(head, [0x50, 0x4b, 0x03, 0x04])) return \"zip\"\n\tif (hasMagic(head, [0x50, 0x4b, 0x05, 0x06])) return \"zip\"\n\tif (head.byteLength >= 262) {\n\t\tconst magic = String.fromCharCode(\n\t\t\thead[257]!,\n\t\t\thead[258]!,\n\t\t\thead[259]!,\n\t\t\thead[260]!,\n\t\t\thead[261]!,\n\t\t)\n\t\tif (magic === \"ustar\") return \"tar\"\n\t}\n\tif (hasMagic(head, [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c])) return \"7z\"\n\tif (hasMagic(head, [0x52, 0x61, 0x72, 0x21, 0x1a, 0x07])) return \"rar\"\n\tif (hasMagic(head, [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00])) return \"xz\"\n\tif (hasMagic(head, [0x1f, 0x8b, 0x08])) return \"gzip\"\n\treturn undefined\n}\n","/**\n * Zip entry name decoding for the 7-Zip paths. The zip APPNOTE says\n * names without the UTF-8 flag (general-purpose bit 11) are encoded in\n * the OEM code page; in practice that means cp437 (the same assumption\n * yauzl makes on the native zip path). 7-Zip cannot be told to decode\n * them this way — its `-mcp` switch does not exist in 26.02 and its\n * POSIX builds write the raw name bytes verbatim — so the host decodes\n * the bytes itself, from the listing output and again from the on-disk\n * names after extraction, keeping the two consistent on every platform.\n *\n * ASCII and valid UTF-8 names pass through untouched; anything else is\n * re-interpreted as cp437. The table matches yauzl's, so the native and\n * 7-Zip paths agree on legacy names.\n */\n\nconst CP437 = String.fromCharCode(\n\t0,\n\t9786,\n\t9787,\n\t9829,\n\t9830,\n\t9827,\n\t9824,\n\t8226,\n\t9688,\n\t9675,\n\t9689,\n\t9794,\n\t9792,\n\t9834,\n\t9835,\n\t9788,\n\t9658,\n\t9668,\n\t8597,\n\t8252,\n\t182,\n\t167,\n\t9644,\n\t8616,\n\t8593,\n\t8595,\n\t8594,\n\t8592,\n\t8735,\n\t8596,\n\t9650,\n\t9660,\n\t32,\n\t33,\n\t34,\n\t35,\n\t36,\n\t37,\n\t38,\n\t39,\n\t40,\n\t41,\n\t42,\n\t43,\n\t44,\n\t45,\n\t46,\n\t47,\n\t48,\n\t49,\n\t50,\n\t51,\n\t52,\n\t53,\n\t54,\n\t55,\n\t56,\n\t57,\n\t58,\n\t59,\n\t60,\n\t61,\n\t62,\n\t63,\n\t64,\n\t65,\n\t66,\n\t67,\n\t68,\n\t69,\n\t70,\n\t71,\n\t72,\n\t73,\n\t74,\n\t75,\n\t76,\n\t77,\n\t78,\n\t79,\n\t80,\n\t81,\n\t82,\n\t83,\n\t84,\n\t85,\n\t86,\n\t87,\n\t88,\n\t89,\n\t90,\n\t91,\n\t92,\n\t93,\n\t94,\n\t95,\n\t96,\n\t97,\n\t98,\n\t99,\n\t100,\n\t101,\n\t102,\n\t103,\n\t104,\n\t105,\n\t106,\n\t107,\n\t108,\n\t109,\n\t110,\n\t111,\n\t112,\n\t113,\n\t114,\n\t115,\n\t116,\n\t117,\n\t118,\n\t119,\n\t120,\n\t121,\n\t122,\n\t123,\n\t124,\n\t125,\n\t126,\n\t8962,\n\t199,\n\t252,\n\t233,\n\t226,\n\t228,\n\t224,\n\t229,\n\t231,\n\t234,\n\t235,\n\t232,\n\t239,\n\t238,\n\t236,\n\t196,\n\t197,\n\t201,\n\t230,\n\t198,\n\t244,\n\t246,\n\t242,\n\t251,\n\t249,\n\t255,\n\t214,\n\t220,\n\t162,\n\t163,\n\t165,\n\t8359,\n\t402,\n\t225,\n\t237,\n\t243,\n\t250,\n\t241,\n\t209,\n\t170,\n\t186,\n\t191,\n\t8976,\n\t172,\n\t189,\n\t188,\n\t161,\n\t171,\n\t187,\n\t9617,\n\t9618,\n\t9619,\n\t9474,\n\t9508,\n\t9569,\n\t9570,\n\t9558,\n\t9557,\n\t9571,\n\t9553,\n\t9559,\n\t9565,\n\t9564,\n\t9563,\n\t9488,\n\t9492,\n\t9524,\n\t9516,\n\t9500,\n\t9472,\n\t9532,\n\t9566,\n\t9567,\n\t9562,\n\t9556,\n\t9577,\n\t9574,\n\t9568,\n\t9552,\n\t9580,\n\t9575,\n\t9576,\n\t9572,\n\t9573,\n\t9561,\n\t9560,\n\t9554,\n\t9555,\n\t9579,\n\t9578,\n\t9496,\n\t9484,\n\t9608,\n\t9604,\n\t9612,\n\t9616,\n\t9600,\n\t945,\n\t223,\n\t915,\n\t960,\n\t931,\n\t963,\n\t181,\n\t964,\n\t934,\n\t920,\n\t937,\n\t948,\n\t8734,\n\t966,\n\t949,\n\t8745,\n\t8801,\n\t177,\n\t8805,\n\t8804,\n\t8992,\n\t8993,\n\t247,\n\t8776,\n\t176,\n\t8729,\n\t183,\n\t8730,\n\t8319,\n\t178,\n\t9632,\n\t160,\n)\n\n/**\n * Decode one name byte string: valid UTF-8 stays as-is, anything else\n * falls back to cp437 (the zip legacy-encoding convention).\n */\nexport function decodeLegacyZipName(bytes: Uint8Array): string {\n\ttry {\n\t\treturn new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes)\n\t} catch {\n\t\tlet out = \"\"\n\t\tfor (const b of bytes) out += CP437[b] ?? \"\\uFFFD\"\n\t\treturn out\n\t}\n}\n","import { createReadStream, type ReadStream } from \"node:fs\"\nimport { stat } from \"node:fs/promises\"\nimport { Readable } from \"node:stream\"\nimport { buffer } from \"node:stream/consumers\"\nimport { createInflateRaw } from \"node:zlib\"\nimport yauzl from \"yauzl\"\nimport { invalid } from \"../errors.ts\"\n\n/**\n * Central directory entry shape exposed for the read-path cache. Mirrors\n * the fields yauzl surfaces, plus `dataOffset` resolved from the local\n * header. `dataOffset` and `dataSize` together pinpoint the entry's raw\n * bytes inside the zip file; for STORED entries those bytes are the file\n * itself and can be streamed via {@link streamRange} with a\n * `[start, end]` window.\n *\n * The central directory is parsed by yauzl (the only listing engine —\n * the hand-rolled fast parser was removed; ZIP64 falls out naturally).\n */\nexport type ZipEntry = {\n\treadonly name: string\n\treadonly compressionMethod: number\n\t/** General-purpose bit 0: the entry data is encrypted. */\n\treadonly encrypted: boolean\n\treadonly uncompressedSize: number\n\treadonly compressedSize: number\n\treadonly crc32: number\n\treadonly localHeaderOffset: number\n\t/**\n\t * Absolute byte offset of the entry's raw data inside the zip file.\n\t * Computed as `localHeaderOffset + 30 + nameLen + extraLen` where\n\t * nameLen and extraLen are read from the local file header (not the\n\t * central directory) because they can differ between the two.\n\t */\n\treadonly dataOffset: number\n\treadonly dataSize: number\n\treadonly modifiedAt: number\n}\n\n/**\n * Random-access byte view over a zip archive. File-backed archives\n * (uploaded archive files and CLI fixtures) read via the filesystem;\n * nested archives — a zip entry inside the container — implement the same\n * range reads over the outer entry's inflated bytes. Everything in this\n * module that needs the archive's bytes takes a {@link ArchiveSource},\n * so the container and the plugin API resolve nested zips through one\n * code path.\n */\nexport type ArchiveSource = {\n\t/** Total archive size in bytes. */\n\treadonly size: number\n\t/** Read the inclusive byte range `[start, end]`. */\n\treadonly readRange: (start: number, end: number) => Promise<Buffer>\n}\n\nconst RANGE_READ_CHUNK_BYTES = 256 * 1024\n\n/** Wrap an on-disk zip in a {@link ArchiveSource}. */\nexport async function createFileArchiveSource(\n\tzipPath: string,\n): Promise<ArchiveSource> {\n\tconst info = await stat(zipPath)\n\treturn {\n\t\tsize: info.size,\n\t\treadRange: (start, end) => readFileRange(zipPath, start, end),\n\t}\n}\n\n/**\n * Read the central directory of the zip behind `source`, plus enough of\n * each local file header to compute `dataOffset`. Returns a flat list of\n * records ready for caching. Directory entries (names ending in `/`) are\n * excluded.\n *\n * The listing goes through yauzl over the {@link ArchiveSource} (via its\n * random-access-reader bridge); yauzl handles ZIP64 and validates entry\n * names while parsing. The `dataOffset` for byte-range access is\n * resolved from each local file header afterwards — callers that only\n * need names and sizes (e.g. the legacy-name decode truth) pass\n * `{ dataOffsets: false }` so the per-entry random reads are skipped and\n * `dataOffset` is left at 0.\n *\n * @throws DomainError `resource.archive_open_failed` when the zip cannot\n * be parsed.\n */\nexport async function listZipEntriesFromSource(\n\tsource: ArchiveSource,\n\topts: { readonly dataOffsets?: boolean } = {},\n): Promise<readonly ZipEntry[]> {\n\tconst zipfile = await openZipFromSource(source)\n\tconst records: ZipEntry[] = []\n\ttry {\n\t\tfor await (const entry of zipfile.eachEntry()) {\n\t\t\tif (entry.fileName.endsWith(\"/\")) continue\n\t\t\tconst dataOffset =\n\t\t\t\topts.dataOffsets === false\n\t\t\t\t\t? 0\n\t\t\t\t\t: await resolveDataOffsetFromSource(\n\t\t\t\t\t\t\tsource,\n\t\t\t\t\t\t\tentry.relativeOffsetOfLocalHeader,\n\t\t\t\t\t\t)\n\t\t\trecords.push({\n\t\t\t\tname: entry.fileName,\n\t\t\t\tcompressionMethod: entry.compressionMethod,\n\t\t\t\tencrypted: entry.isEncrypted(),\n\t\t\t\tuncompressedSize: entry.uncompressedSize,\n\t\t\t\tcompressedSize: entry.compressedSize,\n\t\t\t\tcrc32: entry.crc32,\n\t\t\t\tlocalHeaderOffset: entry.relativeOffsetOfLocalHeader,\n\t\t\t\tdataOffset,\n\t\t\t\tdataSize: entry.compressedSize,\n\t\t\t\tmodifiedAt: entry.getLastModDate().getTime(),\n\t\t\t})\n\t\t}\n\t} catch (err) {\n\t\t// yauzl surfaces malformed central directories as plain errors\n\t\t// (e.g. encrypted stored entries with inconsistent sizes) — map\n\t\t// them onto the documented taxonomy.\n\t\tthrow invalid(\n\t\t\t\"resource.archive_open_failed\",\n\t\t\terr instanceof Error ? err.message : \"could not read archive entries\",\n\t\t\t{ size: source.size },\n\t\t)\n\t} finally {\n\t\tzipfile.close()\n\t}\n\treturn records\n}\n\n/** Read the central directory of the zip at `zipPath`. */\nexport async function listZipEntries(\n\tzipPath: string,\n\topts: { readonly dataOffsets?: boolean } = {},\n): Promise<readonly ZipEntry[]> {\n\treturn listZipEntriesFromSource(await createFileArchiveSource(zipPath), opts)\n}\n\n/**\n * Stream one entry's *decompressed* bytes from `source`. STORED entries\n * map to a byte-range stream of the raw data; DEFLATE entries inflate on\n * the fly. Encrypted entries and other compression methods reject with a\n * clear reason (there is no key to decrypt with). Callers are responsible\n * for consuming or destroying the stream (a dangling stream leaks an\n * inflate context).\n */\nexport function openZipEntryStream(\n\tsource: ArchiveSource,\n\trecord: ZipEntry,\n): Readable {\n\tif (record.encrypted) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_open_failed\",\n\t\t\t`zip entry \"${record.name}\" is encrypted — password-protected archives are not supported`,\n\t\t\t{ name: record.name },\n\t\t)\n\t}\n\tswitch (record.compressionMethod) {\n\t\tcase 0:\n\t\t\treturn streamRange(\n\t\t\t\tsource,\n\t\t\t\trecord.dataOffset,\n\t\t\t\trecord.dataOffset + record.dataSize - 1,\n\t\t\t)\n\t\tcase 8: {\n\t\t\tconst raw = streamRange(\n\t\t\t\tsource,\n\t\t\t\trecord.dataOffset,\n\t\t\t\trecord.dataOffset + record.dataSize - 1,\n\t\t\t)\n\t\t\tconst inflater = createInflateRaw()\n\t\t\traw.on(\"error\", (err) => inflater.destroy(err))\n\t\t\treturn raw.pipe(inflater)\n\t\t}\n\t\tdefault:\n\t\t\tthrow invalid(\n\t\t\t\t\"resource.archive_open_failed\",\n\t\t\t\t`unsupported zip entry compression method ${record.compressionMethod} for \"${record.name}\"`,\n\t\t\t\t{ method: record.compressionMethod, name: record.name },\n\t\t\t)\n\t}\n}\n\n/** Stream the inclusive byte range `[start, end]` in bounded chunks. */\nexport function streamRange(\n\tsource: ArchiveSource,\n\tstart: number,\n\tend: number,\n): Readable {\n\treturn Readable.from(\n\t\t(async function* rangeChunks() {\n\t\t\tlet pos = start\n\t\t\twhile (pos <= end) {\n\t\t\t\tconst to = Math.min(end, pos + RANGE_READ_CHUNK_BYTES - 1)\n\t\t\t\tconst chunk = await source.readRange(pos, to)\n\t\t\t\tif (chunk.length === 0) return\n\t\t\t\tyield chunk\n\t\t\t\tpos += chunk.length\n\t\t\t}\n\t\t})(),\n\t)\n}\n\n/** Stream `zipPath` bytes in the range `[start, end]` (inclusive). */ export function readZipRange(\n\tzipPath: string,\n\tstart: number,\n\tend: number,\n): ReadStream {\n\treturn createReadStream(zipPath, { start, end })\n}\n\n/**\n * Read an inclusive byte range from `path`. Uses a read stream so offsets\n * beyond 2 GiB work on Node versions where `fs.read` position must fit in\n * Int32 (older releases assert instead of throwing).\n */\nexport async function readFileRange(\n\tpath: string,\n\tstart: number,\n\tend: number,\n): Promise<Buffer> {\n\tif (\n\t\t!Number.isFinite(start) ||\n\t\t!Number.isFinite(end) ||\n\t\t!Number.isInteger(start) ||\n\t\t!Number.isInteger(end) ||\n\t\tstart < 0 ||\n\t\tend < start\n\t) {\n\t\tthrow invalid(\n\t\t\t\"resource.file_read_failed\",\n\t\t\t`invalid byte range ${start}..${end}`,\n\t\t\t{ path, start, end },\n\t\t)\n\t}\n\tconst length = end - start + 1\n\tif (length <= 0) return Buffer.alloc(0)\n\treturn buffer(readZipRange(path, start, end))\n}\n\nfunction openZipFromSource(source: ArchiveSource): Promise<yauzl.ZipFile> {\n\tconst reader = new RangeReader(source)\n\treturn yauzl\n\t\t.fromRandomAccessReaderPromise(reader, source.size, {\n\t\t\tlazyEntries: true,\n\t\t\tdecodeStrings: true,\n\t\t})\n\t\t.catch((err: unknown) => {\n\t\t\treader.close(() => {})\n\t\t\tthrow invalid(\n\t\t\t\t\"resource.archive_open_failed\",\n\t\t\t\terr instanceof Error ? err.message : \"could not open archive\",\n\t\t\t\t{ size: source.size },\n\t\t\t)\n\t\t})\n}\n\n/**\n * yauzl's random-access-reader bridge. yauzl 3.x reads through\n * `createReadStream`/`read`, which are built on `_readStreamForRange`;\n * a subclass therefore only supplies range streams (chunked, so entries\n * are never buffered whole) plus `_close` (see yauzl's README).\n */\nclass RangeReader extends yauzl.RandomAccessReader {\n\t// Explicit field + assignment: this package ships source to Node's\n\t// type-stripping loader (no build step), which rejects parameter\n\t// properties.\n\tprivate readonly source: ArchiveSource\n\n\tconstructor(source: ArchiveSource) {\n\t\tsuper()\n\t\tthis.source = source\n\t}\n\n\toverride _readStreamForRange(start: number, end: number): Readable {\n\t\t// yauzl's `end` is exclusive (it asserts exactly `end - start`\n\t\t// bytes); `streamRange` takes an inclusive end.\n\t\treturn streamRange(this.source, start, end - 1)\n\t}\n\n\t// yauzl's runtime contract calls `_close` even though the shipped\n\t// types only expose the public `close` — no `override` here.\n\t_close(callback: (err?: Error | null) => void): void {\n\t\tcallback(null)\n\t}\n}\n\nasync function resolveDataOffsetFromSource(\n\tsource: ArchiveSource,\n\tlocalHeaderOffset: number,\n): Promise<number> {\n\tif (localHeaderOffset + 30 > source.size) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_open_failed\",\n\t\t\t`truncated local file header at offset ${localHeaderOffset}`,\n\t\t\t{ localHeaderOffset },\n\t\t)\n\t}\n\tconst head = await source.readRange(localHeaderOffset, localHeaderOffset + 29)\n\tif (head.length < 30) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_open_failed\",\n\t\t\t`truncated local file header at offset ${localHeaderOffset}`,\n\t\t\t{ localHeaderOffset },\n\t\t)\n\t}\n\tconst sig = head.readUInt32LE(0)\n\tif (sig !== 0x04034b50) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_open_failed\",\n\t\t\t`bad local file header signature at offset ${localHeaderOffset}`,\n\t\t\t{ localHeaderOffset, sig: sig.toString(16) },\n\t\t)\n\t}\n\tconst nameLen = head.readUInt16LE(26)\n\tconst extraLen = head.readUInt16LE(28)\n\treturn localHeaderOffset + 30 + nameLen + extraLen\n}\n\n/**\n * yauzl validates entry names while parsing the central directory and\n * rejects traversal/absolute names with a plain `Error`. Reclassify\n * those as the documented `resource.archive_invalid_entry` domain error\n * so every caller observes the same error taxonomy.\n */\nexport function normalizeZipError(err: unknown): unknown {\n\tif (!(err instanceof Error)) return err\n\tconst message = err.message\n\tif (\n\t\tmessage.startsWith(\"invalid relative path: \") ||\n\t\tmessage.startsWith(\"absolute path: \") ||\n\t\tmessage.startsWith(\"invalid characters in fileName: \")\n\t) {\n\t\treturn invalid(\"resource.archive_invalid_entry\", message, {})\n\t}\n\treturn err\n}\n","import { spawn } from \"node:child_process\"\nimport { createRequire } from \"node:module\"\nimport { invalid } from \"../errors.ts\"\nimport { SNIFF_WINDOW_BYTES, sniffContainerFormat } from \"./format.ts\"\nimport { decodeLegacyZipName } from \"./name-decode.ts\"\nimport { readFileRange } from \"./zip-entries.ts\"\n\n/**\n * 7-Zip process wrapper for the extra archive formats (.7z/.rar/.xz) and\n * — since the whole-archive extraction switch — zip/tar/gzip as well.\n * The binary comes from the optional `@hoardodile/7z-bin` package\n * (downloaded at install time); when it is absent every function here\n * rejects with a clear \"unavailable\" error and callers degrade to\n * zip-only support via yauzl.\n *\n * Only whole-archive operations run through 7-Zip: listing with\n * `l -slt -ba` (fixed English fields, locale-independent) and full\n * extraction with `x -y`. Random-access entry streaming stays on the\n * native zip path, which 7-Zip cannot express.\n *\n * Legacy zip names (no UTF-8 flag) are not re-encoded by 7-Zip on every\n * platform — its POSIX builds pass the raw bytes through — so the host\n * decodes them as cp437 itself: the listing's `Path` values are decoded\n * byte-wise for zip archives (see {@link decodeLegacyZipName}), and the\n * extraction sites rename the on-disk files to match (see\n * `normalizeExtractedTree` in extract.ts). Listing output is still\n * forced to UTF-8 (`-sccUTF-8`) so 7-Zip's Windows build emits decoded\n * names as valid UTF-8 rather than console-codepage bytes.\n */\n\nconst requireCjs = createRequire(import.meta.url)\n\nexport const SEVEN_ZIP_PATH_ENV = \"7Z_BIN_PATH\"\n\nexport type SevenZipEntry = {\n\treadonly name: string\n\treadonly sizeBytes: number\n\treadonly folder: boolean\n\treadonly encrypted: boolean\n}\n\n/**\n * Resolve the absolute path of the 7-Zip binary, or `undefined` when the\n * optional package is not installed (install failed / skipped platform).\n */\nexport function resolveSevenZipPath(): string | undefined {\n\tconst fromEnv = process.env[SEVEN_ZIP_PATH_ENV]\n\tif (fromEnv !== undefined && fromEnv.length > 0) return fromEnv\n\ttry {\n\t\tconst mod: unknown = requireCjs(\"@hoardodile/7z-bin\")\n\t\tif (typeof mod === \"string\" && mod.length > 0) return mod\n\t\treturn undefined\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nfunction sevenZipUnavailable(): Error {\n\treturn invalid(\n\t\t\"resource.archive_open_failed\",\n\t\t\"7-Zip is not installed — install @hoardodile/7z-bin or set 7Z_BIN_PATH to enable archive extraction\",\n\t\t{},\n\t)\n}\n\n/**\n * Run 7-Zip with `args`; rejects with a domain error on non-zero exit,\n * timeout, or spawn failure (missing binary, wrong-arch executable).\n * stdout is streamed and collected in memory (no maxBuffer ceiling —\n * `l -slt` on very large archives can exceed a fixed cap); `stderr` is\n * included in the message so operators can see the native error instead\n * of a bare exit code.\n */\nfunction run7z(\n\tbin: string,\n\targs: readonly string[],\n\ttimeoutMs: number,\n): Promise<{ readonly stdout: Buffer }> {\n\treturn new Promise((resolveDone, rejectDone) => {\n\t\tconst child = spawn(bin, [...args], {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\twindowsHide: true,\n\t\t})\n\t\tconst stdout: Buffer[] = []\n\t\tconst stderr: Buffer[] = []\n\t\tlet settled = false\n\n\t\tchild.stdout.on(\"data\", (chunk: Buffer) => stdout.push(chunk))\n\t\tchild.stderr.on(\"data\", (chunk: Buffer) => stderr.push(chunk))\n\n\t\tconst fail = (err: Error): void => {\n\t\t\tif (settled) return\n\t\t\tsettled = true\n\t\t\trejectDone(err)\n\t\t}\n\t\tconst timer = setTimeout(() => {\n\t\t\tchild.kill()\n\t\t\tfail(\n\t\t\t\tinvalid(\n\t\t\t\t\t\"resource.archive_open_failed\",\n\t\t\t\t\t`7-Zip timed out after ${timeoutMs}ms`,\n\t\t\t\t\t{ timeoutMs },\n\t\t\t\t),\n\t\t\t)\n\t\t}, timeoutMs)\n\t\ttimer.unref()\n\n\t\tchild.on(\"error\", (err) => {\n\t\t\tfail(\n\t\t\t\tinvalid(\n\t\t\t\t\t\"resource.archive_open_failed\",\n\t\t\t\t\t`could not start 7-Zip: ${err.message}`,\n\t\t\t\t\t{},\n\t\t\t\t),\n\t\t\t)\n\t\t})\n\t\tchild.on(\"close\", (code, signal) => {\n\t\t\tclearTimeout(timer)\n\t\t\tif (settled) return\n\t\t\tif (code === 0) {\n\t\t\t\tsettled = true\n\t\t\t\tresolveDone({ stdout: Buffer.concat(stdout) })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst detail =\n\t\t\t\tsignal !== null ? `killed by ${signal}` : `exit ${code ?? \"?\"}`\n\t\t\tconst tail = Buffer.concat(stderr)\n\t\t\t\t.toString(\"utf8\")\n\t\t\t\t.trim()\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.at(-1)\n\t\t\tfail(\n\t\t\t\tinvalid(\n\t\t\t\t\t\"resource.archive_open_failed\",\n\t\t\t\t\t`7-Zip ${detail}${tail !== undefined && tail.length > 0 ? `: ${tail}` : \"\"}`,\n\t\t\t\t\t{ code },\n\t\t\t\t),\n\t\t\t)\n\t\t})\n\t})\n}\n\n/** True when `encrypted` field from `-slt` marks an encrypted entry. */\nfunction isEncryptedField(value: string): boolean {\n\treturn value === \"+\"\n}\n\n/** Parse `l -slt -ba` output into per-entry records (blank-line separated). */\nfunction parseSltListing(\n\tstdout: Buffer,\n\tlegacyNames: boolean,\n): SevenZipEntry[] {\n\tconst entries: SevenZipEntry[] = []\n\tlet name: string | undefined\n\tlet sizeBytes = 0\n\tlet folder = false\n\tlet encrypted = false\n\t// latin1 round-trips every output byte losslessly, so the raw `Path`\n\t// value survives for the cp437 decode below (the UTF-8 pass-through\n\t// would replace invalid bytes with U+FFFD first).\n\tfor (const line of stdout.toString(\"latin1\").split(/\\r?\\n/)) {\n\t\tif (line.length === 0) {\n\t\t\tif (name !== undefined) {\n\t\t\t\tentries.push({ name, sizeBytes, folder, encrypted })\n\t\t\t\tname = undefined\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tconst sep = line.indexOf(\" = \")\n\t\tif (sep < 0) continue\n\t\tconst key = line.slice(0, sep)\n\t\tif (key === \"Path\") {\n\t\t\t// 7za prints backslash separators on Windows; normalize to\n\t\t\t// the forward-slash convention used across the archive runtime.\n\t\t\tconst raw = Buffer.from(line.slice(sep + 3), \"latin1\")\n\t\t\tconst decoded = legacyNames\n\t\t\t\t? decodeLegacyZipName(raw)\n\t\t\t\t: raw.toString(\"utf8\")\n\t\t\tname = decoded.replace(/\\\\/g, \"/\")\n\t\t\tcontinue\n\t\t}\n\t\tconst value = line.slice(sep + 3)\n\t\tswitch (key) {\n\t\t\tcase \"Size\":\n\t\t\t\tsizeBytes = Number.parseInt(value, 10) || 0\n\t\t\t\tbreak\n\t\t\tcase \"Attributes\":\n\t\t\t\t// Directories carry `D` (there is no Folder field in -slt).\n\t\t\t\tfolder = value.includes(\"D\")\n\t\t\t\tbreak\n\t\t\tcase \"Encrypted\":\n\t\t\t\tencrypted = isEncryptedField(value)\n\t\t\t\tbreak\n\t\t}\n\t}\n\tif (name !== undefined) {\n\t\tentries.push({ name, sizeBytes, folder, encrypted })\n\t}\n\treturn entries\n}\n\n/**\n * Per-call options for the 7-Zip wrappers.\n */\nexport type SevenZipRunOptions = {\n\treadonly timeoutMs?: number\n}\n\n/**\n * True when `archivePath`'s magic bytes say zip. Read once per call (a\n * 4KB window) to decide whether names decode as legacy cp437 (see\n * {@link decodeLegacyZipName}) — callers never need to know the format.\n * An unreadable header degrades to \"not zip\" (no cp437 fallback), which\n * is the safe direction for every other format.\n */\nasync function isZipArchive(archivePath: string): Promise<boolean> {\n\tconst head = await readFileRange(\n\t\tarchivePath,\n\t\t0,\n\t\tSNIFF_WINDOW_BYTES - 1,\n\t).catch(() => undefined)\n\tif (head === undefined || head.length === 0) return false\n\treturn sniffContainerFormat(head) === \"zip\"\n}\n\n/**\n * List the entries of `archivePath` (zip/tar/7z/rar/xz/gzip). Rejects\n * when the archive is encrypted (7-Zip would prompt for a password on a\n * TTY). Listing output is forced to UTF-8 (`-sccUTF-8`); legacy zip\n * names decode as cp437 byte-wise (see {@link decodeLegacyZipName}).\n */\nexport async function listSevenZipEntries(\n\tarchivePath: string,\n\topts: SevenZipRunOptions = {},\n): Promise<SevenZipEntry[]> {\n\tconst bin = resolveSevenZipPath()\n\tif (bin === undefined) throw sevenZipUnavailable()\n\tconst args = [\"l\", \"-slt\", \"-ba\", \"-sccUTF-8\"]\n\tconst legacyNames = await isZipArchive(archivePath)\n\targs.push(archivePath)\n\tconst { stdout } = await run7z(bin, args, opts.timeoutMs ?? 120_000)\n\treturn parseSltListing(stdout, legacyNames)\n}\n\n/**\n * Extract `archivePath` in full into `destDir`. Callers must pre-validate\n * budgets from {@link listSevenZipEntries} — 7-Zip has no byte-level\n * progress or per-entry abort, so the listing is the enforcement point.\n * Legacy zip names land on disk as 7-Zip wrote them (raw bytes on POSIX);\n * callers fix them up afterwards via `normalizeExtractedTree` (extract.ts).\n */\nexport async function extractSevenZipInto(\n\tarchivePath: string,\n\tdestDir: string,\n\topts: SevenZipRunOptions = {},\n): Promise<void> {\n\tconst bin = resolveSevenZipPath()\n\tif (bin === undefined) throw sevenZipUnavailable()\n\tconst args = [\"x\", archivePath, `-o${destDir}`, \"-y\", \"-bd\"]\n\tawait run7z(bin, args, opts.timeoutMs ?? 10 * 60_000)\n}\n","import { randomUUID } from \"node:crypto\"\nimport { createWriteStream, existsSync } from \"node:fs\"\nimport {\n\tchmod,\n\tlstat,\n\tmkdir,\n\treaddir,\n\trename,\n\trm,\n\twriteFile,\n} from \"node:fs/promises\"\nimport { tmpdir } from \"node:os\"\nimport { dirname, join, normalize, resolve, sep } from \"node:path\"\nimport { Transform } from \"node:stream\"\nimport { pipeline } from \"node:stream/promises\"\nimport yauzl, { type Entry, type ZipFile } from \"yauzl\"\nimport { invalid } from \"../errors.ts\"\nimport {\n\textractSevenZipInto,\n\tlistSevenZipEntries,\n\tresolveSevenZipPath,\n} from \"./7z.ts\"\nimport {\n\ttype ContainerFormat,\n\tSNIFF_WINDOW_BYTES,\n\tsniffContainerFormat,\n} from \"./format.ts\"\nimport { listingBudgetExceeded } from \"./listing.ts\"\nimport { decodeLegacyZipName } from \"./name-decode.ts\"\nimport { listZipEntries, normalizeZipError } from \"./zip-entries.ts\"\n\n/**\n * Whole-archive extraction. Every supported format (zip, tar, 7z, rar,\n * xz, gzip) extracts through 7-Zip with listing-validated budgets; when\n * the optional 7-Zip binary is absent, zip falls back to a yauzl\n * streaming path with a per-byte budget counter. Used by plugin\n * installation, folder-import extraction and the plugin extract API —\n * the workflows that need a directory layout.\n */\n\n/**\n * Extraction progress reporter for {@link extractArchiveInto}. The\n * 7-Zip path reports the two phases only (listing pre-check, done);\n * the yauzl fallback reports per-entry events.\n */\nexport type ZipExtractReporter = (event: ZipExtractReport) => void\n\nexport type ZipExtractReport =\n\t| {\n\t\t\treadonly phase: \"started\"\n\t\t\treadonly totalEntries: number\n\t\t\treadonly totalBytes: number\n\t }\n\t| {\n\t\t\treadonly phase: \"entry\"\n\t\t\treadonly entriesDone: number\n\t\t\treadonly totalEntries: number\n\t\t\treadonly bytesDone: number\n\t\t\treadonly totalBytes: number\n\t }\n\t| { readonly phase: \"done\" }\n\nexport type ExtractArchiveOptions = {\n\t/** Cumulative uncompressed byte budget for the whole archive. */\n\treadonly maxBytes: number\n\t/** Optional entry-count budget (enforced on the listing). */\n\treadonly maxEntries?: number\n\treadonly onProgress?: ZipExtractReporter\n\t/**\n\t * Optional format allow-list (e.g. `[\"zip\"]` for plugin installs).\n\t * An archive sniffed outside the list is rejected up front with\n\t * `resource.archive_format_not_allowed` — project-internal channels\n\t * that only ever produce one format stop admitting the others\n\t * instead of opening every codec to untrusted input.\n\t */\n\treadonly formats?: readonly ContainerFormat[]\n}\n\n/**\n * Stream an archive into `destDir`, format-agnostic: every supported\n * format (zip/tar/7z/rar/xz/gzip) extracts through 7-Zip after\n * validating budgets, encryption and paths from its listing, then\n * re-verifies the extracted tree (size + symlink scan). Without a\n * 7-Zip binary, zip extracts through yauzl with a per-byte budget\n * counter; the other formats error with \"7-Zip is not installed\".\n * Used by plugin installation and folder-import extraction (the\n * workflows that need a directory layout).\n *\n * Refuses any entry whose normalised path escapes `destDir` (zip-slip)\n * or contains an absolute path / drive letter. Defends against zip\n * bombs via `maxBytes` (and `maxEntries` when provided).\n *\n * @throws DomainError VALIDATION when the archive is malformed,\n * contains an unsafe entry, exceeds the budgets, is encrypted, or is\n * not a supported archive at all.\n */\nexport async function extractArchiveInto(\n\tsource: NodeJS.ReadableStream,\n\tdestDir: string,\n\topts: ExtractArchiveOptions,\n): Promise<void> {\n\tconst buffer = await readToBuffer(source)\n\tconst format = sniffContainerFormat(buffer.subarray(0, SNIFF_WINDOW_BYTES))\n\tif (format === undefined) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_open_failed\",\n\t\t\t\"not a supported archive (zip/tar/7z/rar/xz/gzip)\",\n\t\t\t{},\n\t\t)\n\t}\n\tif (opts.formats !== undefined && !opts.formats.includes(format)) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_format_not_allowed\",\n\t\t\t`archive format ${format} is not allowed here (allowed: ${opts.formats.join(\", \")})`,\n\t\t\t{ allowed: opts.formats },\n\t\t)\n\t}\n\tif (format === \"zip\" && resolveSevenZipPath() === undefined) {\n\t\treturn extractZipBuffer(buffer, destDir, opts)\n\t}\n\treturn extractViaSevenZip(buffer, destDir, opts, format)\n}\n\n/**\n * Zip branch when 7-Zip is absent: yauzl streaming with per-byte budget\n * and entry progress. Kept so zip imports work on installs where the\n * optional binary failed to download.\n */\nasync function extractZipBuffer(\n\tbuffer: Buffer,\n\tdestDir: string,\n\topts: ExtractArchiveOptions,\n): Promise<void> {\n\tconst { maxBytes, maxEntries, onProgress } = opts\n\tconst zipfile = await openZipFromBuffer(buffer)\n\tif (maxEntries !== undefined && zipfile.entryCount > maxEntries) {\n\t\tzipfile.close()\n\t\tthrow invalid(\n\t\t\t\"resource.archive_too_large\",\n\t\t\t`archive has ${zipfile.entryCount} entries, exceeding the limit of ${maxEntries}`,\n\t\t\t{ maxEntries },\n\t\t)\n\t}\n\tconst root = resolve(destDir)\n\tconst budget = { remaining: maxBytes, max: maxBytes }\n\tconst totalEntries = zipfile.entryCount\n\tconst counters = { entriesDone: 0, bytesDone: 0, totalBytes: 0 }\n\tif (onProgress !== undefined) {\n\t\tonProgress({ phase: \"started\", totalEntries, totalBytes: 0 })\n\t}\n\ttry {\n\t\tawait new Promise<void>((resolveDone, rejectDone) => {\n\t\t\tzipfile.on(\"error\", (err: unknown) => rejectDone(normalizeZipError(err)))\n\t\t\tzipfile.on(\"end\", resolveDone)\n\t\t\tzipfile.on(\"entry\", (entry: Entry) => {\n\t\t\t\thandleExtractEntry(zipfile, entry, root, budget).then(() => {\n\t\t\t\t\tcounters.entriesDone += 1\n\t\t\t\t\tcounters.bytesDone += entry.uncompressedSize\n\t\t\t\t\tcounters.totalBytes += entry.uncompressedSize\n\t\t\t\t\tif (onProgress !== undefined) {\n\t\t\t\t\t\tonProgress({\n\t\t\t\t\t\t\tphase: \"entry\",\n\t\t\t\t\t\t\tentriesDone: counters.entriesDone,\n\t\t\t\t\t\t\ttotalEntries,\n\t\t\t\t\t\t\tbytesDone: counters.bytesDone,\n\t\t\t\t\t\t\ttotalBytes: counters.totalBytes,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tzipfile.readEntry()\n\t\t\t\t}, rejectDone)\n\t\t\t})\n\t\t\tzipfile.readEntry()\n\t\t})\n\t} finally {\n\t\tzipfile.close()\n\t}\n\tif (onProgress !== undefined) {\n\t\tonProgress({ phase: \"done\" })\n\t}\n}\n\n/**\n * 7-Zip branch for every supported format: write the bytes to a temp\n * file (7-Zip needs a real path), validate budgets, encryption and\n * entry paths from the listing, extract in one pass, then verify the\n * extracted tree (size re-check + symlink refusal).\n */\nasync function extractViaSevenZip(\n\tbuffer: Buffer,\n\tdestDir: string,\n\topts: ExtractArchiveOptions,\n\tformat: ContainerFormat,\n): Promise<void> {\n\tconst { maxBytes, maxEntries, onProgress } = opts\n\tconst tempPath = join(\n\t\ttmpdir(),\n\t\t`hoardodile-7z-${process.pid}-${randomUUID().slice(0, 8)}`,\n\t)\n\tawait writeFile(tempPath, buffer)\n\ttry {\n\t\tconst entries = await listSevenZipEntries(tempPath)\n\t\tconst files = entries.filter((e) => !e.folder)\n\t\tconst exceeded = listingBudgetExceeded(files, { maxBytes, maxEntries })\n\t\tif (exceeded?.kind === \"bytes\") {\n\t\t\tthrow invalid(\n\t\t\t\t\"resource.archive_too_large\",\n\t\t\t\t`archive extracts to more than ${maxBytes} bytes`,\n\t\t\t\t{ maxExtractedBytes: maxBytes },\n\t\t\t)\n\t\t}\n\t\tif (exceeded?.kind === \"entries\") {\n\t\t\tthrow invalid(\n\t\t\t\t\"resource.archive_too_large\",\n\t\t\t\t`archive has ${files.length} entries, exceeding the limit of ${maxEntries}`,\n\t\t\t\t{ maxEntries },\n\t\t\t)\n\t\t}\n\t\tif (entries.some((e) => e.encrypted)) {\n\t\t\tthrow invalid(\n\t\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t\t\"archive is password-protected — encrypted archives are not supported\",\n\t\t\t\t{},\n\t\t\t)\n\t\t}\n\t\tfor (const entry of files) assertSafeEntryPath(entry.name)\n\t\tconst root = resolve(destDir)\n\t\tawait mkdir(root, { recursive: true })\n\t\tconst totalBytes = files.reduce((acc, e) => acc + e.sizeBytes, 0)\n\t\tonProgress?.({ phase: \"started\", totalEntries: files.length, totalBytes })\n\t\tawait extractSevenZipInto(tempPath, root)\n\t\t// 7-Zip writes legacy zip names verbatim on POSIX and restores\n\t\t// entries' mode bits, which can strip the app's own access; fix\n\t\t// both up before the tree is re-walked or probed below. The\n\t\t// decoded names of the archive itself are the ground truth the\n\t\t// legacy-rename pass matches against (see `renameLegacyZipNames`)\n\t\t// — 7-Zip's text listing loses the invalid bytes on POSIX.\n\t\tconst decoded =\n\t\t\tformat === \"zip\"\n\t\t\t\t? await decodeZipNames(\n\t\t\t\t\t\ttempPath,\n\t\t\t\t\t\tfiles.map((e) => ({ name: e.name, sizeBytes: e.sizeBytes })),\n\t\t\t\t\t)\n\t\t\t\t: undefined\n\t\tawait normalizeExtractedTree(root, {\n\t\t\tlegacyZipNames: format === \"zip\",\n\t\t\texpectedNames: decoded?.paths,\n\t\t})\n\t\t// Post-hoc bomb re-check: the listing sizes are advisory; count\n\t\t// what actually landed (and refuse symlinks — 7-Zip creates them\n\t\t// from zip/tar entries carrying unix link modes).\n\t\tawait assertExtractedTree(root, maxBytes)\n\t\tonProgress?.({ phase: \"done\" })\n\t} finally {\n\t\tawait rm(tempPath, { force: true }).catch(() => {})\n\t}\n}\n\n/**\n * Walk an extracted tree verifying that every entry is a regular file\n * or directory (symlinks and special files are refused outright — a\n * malicious archive could otherwise plant a link out of the extraction\n * root) and that the total size stays within `maxBytes`.\n */\nexport async function assertExtractedTree(\n\troot: string,\n\tmaxBytes: number,\n): Promise<void> {\n\tlet total = 0\n\tasync function walk(dir: string): Promise<void> {\n\t\tconst entries = await readdir(dir, { withFileTypes: true })\n\t\tfor (const entry of entries) {\n\t\t\tconst full = join(dir, entry.name)\n\t\t\t// lstat, not dirent: on Windows, junction points (reparse\n\t\t\t// tags) are only reported as links by lstat.\n\t\t\tconst info = await lstat(full)\n\t\t\tif (info.isSymbolicLink()) {\n\t\t\t\tthrow invalid(\n\t\t\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t\t\t`extracted entry is a symlink: ${entry.name}`,\n\t\t\t\t\t{ name: entry.name },\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (info.isDirectory()) {\n\t\t\t\tawait walk(full)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!info.isFile()) {\n\t\t\t\tthrow invalid(\n\t\t\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t\t\t`extracted entry is not a regular file: ${entry.name}`,\n\t\t\t\t\t{ name: entry.name },\n\t\t\t\t)\n\t\t\t}\n\t\t\ttotal += info.size\n\t\t\tif (total > maxBytes) {\n\t\t\t\tthrow invalid(\n\t\t\t\t\t\"resource.archive_too_large\",\n\t\t\t\t\t`archive extracts to more than ${maxBytes} bytes`,\n\t\t\t\t\t{ maxExtractedBytes: maxBytes },\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\tawait walk(root)\n}\n\n/**\n * Post-extraction hygiene for a 7-Zip materialized tree, run before the\n * tree is re-walked by {@link assertExtractedTree} or probed for the\n * manifest. For zip archives, entries whose on-disk names are not valid\n * UTF-8 are renamed to their cp437-decoded form (7-Zip writes the raw\n * bytes verbatim on POSIX, and macOS stores those bytes `%XX`-escaped —\n * see {@link renameLegacyZipNames} for both shapes); every entry gets\n * owner permissions OR'd in so the app can always read, serve and clean\n * up what it extracted (zip/tar entries can carry mode bits that strip\n * owner access). Never follows symlinks — refusal stays with\n * {@link assertExtractedTree}. Windows is unaffected: names are already\n * decoded and mode bits are ignored there.\n */\nexport async function normalizeExtractedTree(\n\troot: string,\n\topts: {\n\t\treadonly legacyZipNames: boolean\n\t\t/**\n\t\t * Decoded listing names (forward-slash, relative) of the archive\n\t\t * being extracted. The disambiguator for the macOS `%XX`-escaped\n\t\t * name shape: an escaped name is valid UTF-8, so validity alone\n\t\t * cannot tell `caf%82.jpg` (a legacy cp437 `café.jpg` after\n\t\t * macOS escaping) apart from a file literally named\n\t\t * `report%82.jpg` — only the listing's decoded name settles it.\n\t\t */\n\t\treadonly expectedNames?: readonly string[]\n\t},\n): Promise<void> {\n\tif (opts.legacyZipNames) {\n\t\tconst expected = opts.expectedNames\n\t\t\t? new Set(opts.expectedNames.map((n) => n.replace(/\\/+$/, \"\")))\n\t\t\t: undefined\n\t\tawait renameLegacyZipNames(Buffer.from(root), root, root, expected)\n\t}\n\tawait makeReadable(root)\n}\n\n/**\n * Decoded names of a zip archive — the ground truth for legacy (cp437)\n * entry names. 7-Zip's text listing drops the bytes that are not valid\n * UTF-8 in the archive: its console conversion removes the escape points\n * on POSIX, so the reported name loses the character entirely. The zip\n * itself is authoritative, so names come from yauzl (`decodeStrings`\n * decodes non-UTF-8 names as cp437). Directory entries are absent from\n * that listing, so every ancestor directory of a file path is added to\n * `paths` for the rename pass. Falls back to the 7-Zip-listing names when\n * the zip cannot be parsed a second time — extraction then proceeds\n * exactly as before.\n */\nexport async function decodeZipNames(\n\tzipPath: string,\n\tfallback: readonly { readonly name: string; readonly sizeBytes: number }[],\n): Promise<{\n\treadonly files: readonly {\n\t\treadonly name: string\n\t\treadonly sizeBytes: number\n\t}[]\n\treadonly paths: readonly string[]\n}> {\n\ttry {\n\t\tconst records = await listZipEntries(zipPath, { dataOffsets: false })\n\t\tconst files = records.map((e) => ({\n\t\t\tname: e.name,\n\t\t\tsizeBytes: e.uncompressedSize,\n\t\t}))\n\t\tconst paths = new Set<string>()\n\t\tfor (const file of files) {\n\t\t\tconst segments = file.name.split(\"/\")\n\t\t\tfor (let i = 1; i < segments.length; i++) {\n\t\t\t\tpaths.add(segments.slice(0, i).join(\"/\"))\n\t\t\t}\n\t\t\tpaths.add(file.name)\n\t\t}\n\t\treturn { files, paths: [...paths] }\n\t} catch {\n\t\treturn { files: fallback, paths: fallback.map((e) => e.name) }\n\t}\n}\n\n/**\n * Rename every entry whose name does not match its decoded listing\n * form. Legacy zip names can land on disk in two shapes:\n *\n * - raw bytes, when the archive name is not valid UTF-8 (7-Zip's POSIX\n * and Windows builds pass/write them verbatim);\n * - `%XX`-escaped bytes, when macOS's UTF-8 filesystem layer stores the\n * raw names from the first shape (`XNU`'s `vfs_utfconv.c` converts an\n * illegal byte to `%` + two hex digits while normalizing);\n * - 7-Zip escape-plane characters (U+EF00+byte), the round-trip form\n * for an illegal byte written without the byte-level conversion.\n *\n * The last two are valid UTF-8 on disk and are reconciled against the\n * decoded names of the archive itself (see {@link decodeZipNames}) — the\n * only lossless source, since 7-Zip's text listing drops those bytes on\n * POSIX. A rename never clobbers an existing entry, and either escaped\n * shape only wins when the recovered name matches a decoded listing path\n * at the same position — a file genuinely named `report%82.jpg` (or with\n * a private-use character) therefore stays untouched.\n */\nasync function renameLegacyZipNames(\n\trawRoot: Buffer,\n\tdecodedRoot: string,\n\troot: string,\n\texpected: ReadonlySet<string> | undefined,\n): Promise<void> {\n\tconst entries = await readdir(rawRoot, {\n\t\twithFileTypes: true,\n\t\tencoding: \"buffer\",\n\t})\n\tfor (const entry of entries) {\n\t\tconst childRaw = Buffer.concat([rawRoot, Buffer.from(sep), entry.name])\n\t\tconst decoded = decodeLegacyZipName(entry.name)\n\t\tconst target = legacyRenameTarget(\n\t\t\tentry.name,\n\t\t\tdecoded,\n\t\t\tdecodedRoot,\n\t\t\troot,\n\t\t\texpected,\n\t\t)\n\t\tif (entry.isDirectory()) {\n\t\t\tif (target !== undefined && !existsSync(target)) {\n\t\t\t\tawait rename(childRaw, target)\n\t\t\t\tawait renameLegacyZipNames(Buffer.from(target), target, root, expected)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// Either no rename applies, or the decoded target already\n\t\t\t// exists (a real entry owns the decoded name) — the raw\n\t\t\t// directory itself stays, but its descendants must still be\n\t\t\t// renamed under the decoded prefix.\n\t\t\tawait renameLegacyZipNames(\n\t\t\t\tchildRaw,\n\t\t\t\ttarget ?? `${decodedRoot}/${decoded}`,\n\t\t\t\troot,\n\t\t\t\texpected,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\t\tif (target === undefined || existsSync(target)) continue\n\t\tawait rename(childRaw, target)\n\t}\n}\n\n/**\n * The decoded path an extracted entry should be renamed to, or\n * `undefined` when the on-disk name already satisfies the decoded\n * convention (or cannot be decided safely).\n */\nfunction legacyRenameTarget(\n\trawName: Buffer,\n\tdecoded: string,\n\tdecodedRoot: string,\n\troot: string,\n\texpected: ReadonlySet<string> | undefined,\n): string | undefined {\n\tconst utf8Name = rawName.toString(\"utf8\")\n\tif (decoded !== utf8Name) {\n\t\t// Raw bytes are not valid UTF-8: 7-Zip wrote the legacy name\n\t\t// verbatim and the fs layer stored it byte-for-byte (Linux,\n\t\t// Windows). Decode as cp437, matching the listing.\n\t\treturn `${decodedRoot}/${decoded}`\n\t}\n\t// The name IS valid UTF-8 — but the raw legacy bytes may have been\n\t// re-encoded on the way to disk (macOS `%XX` escapes, or 7-Zip's\n\t// escape-plane characters U+EF00+byte). Recover the original bytes and\n\t// re-decode; only the listing's blessing keeps a literal\n\t// `report%82.jpg` from being renamed to `reporté.jpg`.\n\tconst raw = recoverRawLegacyName(rawName)\n\tif (raw === undefined) return undefined\n\tconst recovered = decodeLegacyZipName(raw)\n\tif (recovered === utf8Name || expected === undefined) return undefined\n\tconst rel =\n\t\tdecodedRoot === root\n\t\t\t? recovered\n\t\t\t: `${decodedRoot.slice(root.length + 1)}/${recovered}`\n\treturn expected.has(rel) ? `${decodedRoot}/${recovered}` : undefined\n}\n\n/**\n * The original archive bytes of an on-disk name that carried a legacy\n * cp437 byte, and `undefined` when the name has no mangle at all. Two\n * transient shapes exist for a raw byte `b`:\n *\n * - `%XX` hex escapes: macOS's UTF-8 filesystem layer stores illegal\n * filename bytes that way (`XNU`'s `vfs_utfconv.c`);\n * - the escape-plane character U+EF00+b: 7-Zip's internal representation\n * for a byte it cannot show as UTF-8, written verbatim when the name\n * is encoded without the byte-level round-trip.\n *\n * Either shape maps back to exactly one byte per source byte.\n */\nfunction recoverRawLegacyName(name: Buffer): Buffer | undefined {\n\tconst escaped = unescapePercentEscapes(name)\n\tif (escaped !== undefined) return escaped\n\tconst text = name.toString(\"utf8\")\n\tconst out: number[] = []\n\tfor (const ch of text) {\n\t\tconst code = ch.codePointAt(0) ?? 0\n\t\tif (code >= 0xef80 && code <= 0xefff) {\n\t\t\tout.push(code - 0xef00)\n\t\t\tcontinue\n\t\t}\n\t\tif (code > 0x7f) return undefined // mixed with real text — not a mangle\n\t\tout.push(code)\n\t}\n\treturn out.length === text.length ? Buffer.from(out) : undefined\n}\n\n/**\n * Expand the `%XX` sequences a macOS UTF-8 filesystem writes for illegal\n * filename bytes back to their original byte values. Returns `undefined`\n * when the name contains no `%XX` sequence (nothing to recover). Only\n * sequences whose bytes are illegal as UTF-8 matter to the caller —\n * legitimate `%XX` substrings simply survive as decoded characters and\n * fail the listing match.\n */\nfunction unescapePercentEscapes(name: Buffer): Buffer | undefined {\n\tconst text = name.toString(\"latin1\")\n\tif (!text.includes(\"%\")) return undefined\n\tconst out: number[] = []\n\tlet changed = false\n\tfor (let i = 0; i < text.length; i++) {\n\t\tconst hex = i + 2 < text.length ? text.slice(i + 1, i + 3) : undefined\n\t\tif (text[i] === \"%\" && hex !== undefined && /^[0-9a-fA-F]{2}$/.test(hex)) {\n\t\t\tout.push(Number.parseInt(hex, 16))\n\t\t\tchanged = true\n\t\t\ti += 2\n\t\t\tcontinue\n\t\t}\n\t\tout.push(text.charCodeAt(i))\n\t}\n\treturn changed ? Buffer.from(out) : undefined\n}\n\n/**\n * OR owner permissions into every entry (directories rwx, files rw) so\n * extracted archives stay accessible to the app whatever mode bits the\n * entries carried. Symlinks and special files are skipped — they are\n * refused by {@link assertExtractedTree} afterwards and must never be\n * followed here.\n */\nasync function makeReadable(root: string): Promise<void> {\n\tconst entries = await readdir(root, { withFileTypes: true })\n\tfor (const entry of entries) {\n\t\tconst full = join(root, entry.name)\n\t\tif (entry.isDirectory()) {\n\t\t\tawait chmod(full, (await lstat(full)).mode | 0o700)\n\t\t\tawait makeReadable(full)\n\t\t\tcontinue\n\t\t}\n\t\tif (entry.isFile()) {\n\t\t\tawait chmod(full, (await lstat(full)).mode | 0o600)\n\t\t}\n\t}\n}\n\n/** Reject absolute paths and traversal segments (zip-slip guard). */\nfunction assertSafeEntryPath(rawName: string): void {\n\tif (\n\t\trawName.length === 0 ||\n\t\t/^([a-zA-Z]:)?[\\\\/]/.test(rawName) ||\n\t\trawName.split(\"/\").includes(\"..\")\n\t) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t`archive entry has an unsafe path: ${rawName}`,\n\t\t\t{ rawName },\n\t\t)\n\t}\n}\n\nasync function handleExtractEntry(\n\tzipfile: ZipFile,\n\tentry: Entry,\n\troot: string,\n\tbudget: { remaining: number; readonly max: number },\n): Promise<void> {\n\tconst safe = safeExtractEntryPath(entry.fileName, root)\n\tif (entry.fileName.endsWith(\"/\")) {\n\t\tawait mkdir(safe, { recursive: true })\n\t\treturn\n\t}\n\tawait mkdir(dirname(safe), { recursive: true })\n\tconst stream = await openZipEntryStream(zipfile, entry)\n\tawait pipeline(stream, makeByteBudgetCounter(budget), createWriteStream(safe))\n}\n\nfunction safeExtractEntryPath(rawName: string, root: string): string {\n\tif (rawName.length === 0) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t\"archive entry has empty name\",\n\t\t\t{ rawName },\n\t\t)\n\t}\n\tif (/^([a-zA-Z]:)?[\\\\/]/.test(rawName)) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t`archive entry has absolute path: ${rawName}`,\n\t\t\t{ rawName },\n\t\t)\n\t}\n\tconst normalised = normalize(rawName).replace(/\\\\/g, \"/\")\n\tif (normalised.startsWith(\"../\") || normalised === \"..\") {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t`archive entry escapes destination: ${rawName}`,\n\t\t\t{ rawName },\n\t\t)\n\t}\n\tconst candidate = resolve(root, normalised)\n\tif (candidate !== root && !candidate.startsWith(root + sep)) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_invalid_entry\",\n\t\t\t`archive entry escapes destination: ${rawName}`,\n\t\t\t{ rawName },\n\t\t)\n\t}\n\treturn candidate\n}\n\n/**\n * Transform that subtracts each chunk's size from a shared byte budget\n * and destroys itself with a VALIDATION error once the budget would go\n * negative. Used to defend against zip bombs across all entries in a\n * single archive on the yauzl fallback path (the 7-Zip path pre-checks\n * the listing and re-verifies the extracted tree instead).\n */\nfunction makeByteBudgetCounter(budget: {\n\tremaining: number\n\treadonly max: number\n}): Transform {\n\treturn new Transform({\n\t\ttransform(chunk, _enc, cb) {\n\t\t\tconst len = Buffer.isBuffer(chunk)\n\t\t\t\t? chunk.length\n\t\t\t\t: Buffer.byteLength(chunk)\n\t\t\tbudget.remaining -= len\n\t\t\tif (budget.remaining < 0) {\n\t\t\t\tcb(\n\t\t\t\t\tinvalid(\n\t\t\t\t\t\t\"resource.archive_too_large\",\n\t\t\t\t\t\t`archive extracts to more than ${budget.max} bytes`,\n\t\t\t\t\t\t{ maxExtractedBytes: budget.max },\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcb(undefined, chunk)\n\t\t},\n\t})\n}\n\nasync function readToBuffer(source: NodeJS.ReadableStream): Promise<Buffer> {\n\tconst chunks: Buffer[] = []\n\tfor await (const chunk of source) {\n\t\tchunks.push(typeof chunk === \"string\" ? Buffer.from(chunk) : chunk)\n\t}\n\treturn Buffer.concat(chunks)\n}\n\nfunction openZipFromBuffer(buffer: Buffer): Promise<ZipFile> {\n\treturn new Promise<ZipFile>((res, rej) => {\n\t\tyauzl.fromBuffer(\n\t\t\tbuffer,\n\t\t\t{ lazyEntries: true },\n\t\t\t(err: Error | null, zip: ZipFile) => {\n\t\t\t\tif (err !== null || zip === undefined) {\n\t\t\t\t\trej(\n\t\t\t\t\t\tinvalid(\n\t\t\t\t\t\t\t\"resource.archive_open_failed\",\n\t\t\t\t\t\t\terr?.message ?? \"could not open archive\",\n\t\t\t\t\t\t\t{},\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tres(zip)\n\t\t\t},\n\t\t)\n\t})\n}\n\nfunction openZipEntryStream(\n\tzipfile: ZipFile,\n\tentry: Entry,\n): Promise<NodeJS.ReadableStream> {\n\treturn new Promise((res, rej) => {\n\t\tzipfile.openReadStream(entry, (err, stream) => {\n\t\t\tif ((err !== null && err !== undefined) || stream === undefined) {\n\t\t\t\trej(\n\t\t\t\t\tinvalid(\n\t\t\t\t\t\t\"resource.archive_entry_unreadable\",\n\t\t\t\t\t\terr?.message ?? `could not read archive entry: ${entry.fileName}`,\n\t\t\t\t\t\t{ entry: entry.fileName },\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tres(stream)\n\t\t})\n\t})\n}\n","import { createWriteStream } from \"node:fs\"\nimport { mkdir, rename, rm, stat, writeFile } from \"node:fs/promises\"\nimport { dirname } from \"node:path\"\nimport type { Readable } from \"node:stream\"\nimport { pipeline } from \"node:stream/promises\"\n\n/**\n * Materialization primitives shared by every code path that writes\n * derived files into the extraction cache: the host's container\n * extractor and the server's artifact view (thumb/probe materialization\n * for both literal and virtual entries). One partial+rename+size-check\n * recipe and one single-flight guard, instead of a copy per caller.\n */\n\n/**\n * Write `openStream`'s bytes to `target` atomically: stream to a\n * `.partial` sibling, verify the written size matches `expectedSize`,\n * then rename into place. On any failure the partial is removed and the\n * error rethrown. An already-correct `target` is left untouched.\n *\n * With `key` set, concurrent calls for the same key share one in-flight\n * write instead of racing it (via {@link globalMaterializeInflight}).\n */\nexport async function materializeFile(opts: {\n\treadonly openStream: () => Readable | Promise<Readable>\n\treadonly target: string\n\treadonly expectedSize: number\n\treadonly key?: string\n}): Promise<void> {\n\tconst { key } = opts\n\tif (key !== undefined) {\n\t\treturn withSingleFlight(key, () => materializeFileWithoutFlight(opts))\n\t}\n\treturn materializeFileWithoutFlight(opts)\n}\n\nasync function materializeFileWithoutFlight(opts: {\n\treadonly openStream: () => Readable | Promise<Readable>\n\treadonly target: string\n\treadonly expectedSize: number\n}): Promise<void> {\n\tconst { openStream, target, expectedSize } = opts\n\tawait mkdir(dirname(target), { recursive: true })\n\tconst partial = `${target}.partial-${process.pid}-${Date.now()}`\n\ttry {\n\t\tif (expectedSize === 0) {\n\t\t\tawait writeFile(partial, Buffer.alloc(0))\n\t\t} else {\n\t\t\tawait pipeline(await openStream(), createWriteStream(partial))\n\t\t}\n\t\tconst written = await stat(partial)\n\t\tif (written.size !== expectedSize) {\n\t\t\tthrow new Error(\n\t\t\t\t`materialized ${target} size mismatch: expected ${expectedSize}, got ${written.size}`,\n\t\t\t)\n\t\t}\n\t\tawait rename(partial, target)\n\t} catch (err) {\n\t\tawait rm(partial, { force: true }).catch(() => {})\n\t\tthrow err\n\t}\n}\n\n/**\n * Deduplicate concurrent materialization of the same `key`: the second\n * caller awaits the first's in-flight work instead of racing it. The\n * inflight map is shared via {@link globalMaterializeInflight} unless a\n * custom one is passed, matching how the server's artifact views share\n * one guard process-wide.\n */\nexport function withSingleFlight<T>(\n\tkey: string,\n\trun: () => Promise<T>,\n\tinflight: Map<string, Promise<unknown>> = globalMaterializeInflight,\n): Promise<T> {\n\tconst pending = inflight.get(key) as Promise<T> | undefined\n\tif (pending !== undefined) return pending\n\tconst work = run()\n\tinflight.set(key, work)\n\tvoid work.then(\n\t\t() => inflight.delete(key),\n\t\t() => inflight.delete(key),\n\t)\n\treturn work\n}\n\n/** Process-wide single-flight guard for materialization (host + server). */\nexport const globalMaterializeInflight = new Map<string, Promise<unknown>>()\n","import type { Readable } from \"node:stream\"\nimport { invalid } from \"../errors.ts\"\nimport { SNIFF_WINDOW_BYTES, sniffContainerFormat } from \"./format.ts\"\nimport type { NestedCdCache } from \"./nested-cd-cache.ts\"\nimport {\n\ttype ArchiveSource,\n\tlistZipEntriesFromSource,\n\topenZipEntryStream,\n} from \"./zip-entries.ts\"\n\n/**\n * Container-entry addressing: plugin hooks (and the server view) may\n * reference a file *inside* a container entry with the virtual path\n * `outer!inner` — e.g. `manga.cbz!Chapter 1/001.jpg`. This module owns\n * the resolution rules and the zip listing (format identification\n * lives in `format.ts`); nested containers reuse `zip-entries.ts` over\n * a range source. Zip is the only addressable nested format —\n * tar/7z/rar/xz/gzip are whole-archive formats (extractable via 7-Zip,\n * not addressable by single entry).\n *\n * Ambiguity rule: `outer` is treated as a container only when its magic\n * bytes say so (zip) AND the archive actually contains `inner`;\n * otherwise the whole path is the literal container entry. A file whose\n * name contains `!` therefore stays addressable.\n */\n\n/** The virtual-path separator between an outer entry and an inner one. */\nexport const VIRTUAL_PATH_SEPARATOR = \"!\"\n\n/**\n * Read interface an outer container must satisfy. Both the plugin-side\n * `ResourceContainer` and the server's artifact view provide these — the\n * virtual resolver is deliberately minimal so it composes with either.\n */\nexport type OuterEntrySource = {\n\t/** Byte length of a literal entry; `undefined` when absent. */\n\treadonly sizeOf: (rel: string) => Promise<number | undefined>\n\t/** Read `[start, end)` of a literal entry (`end` exclusive). */\n\treadonly readSlice: (\n\t\trel: string,\n\t\tstart: number,\n\t\tend: number,\n\t) => Promise<Buffer>\n}\n\n/** A unified, format-agnostic inner entry. */\nexport type ArchiveEntry = {\n\treadonly name: string\n\treadonly sizeBytes: number\n}\n\n/** An inner entry plus its stream opener (format handles attached). */\nexport type OpenableArchiveEntry = ArchiveEntry & {\n\treadonly openStream: () => Readable\n}\n\nexport type PathResolution =\n\t| { readonly kind: \"literal\" }\n\t| {\n\t\t\treadonly kind: \"container\"\n\t\t\treadonly outer: string\n\t\t\treadonly entry: ArchiveEntry\n\t\t\t/** Decompressed bytes of the inner entry. */\n\t\t\treadonly openStream: () => Readable\n\t }\n\nexport type NestedResolver = {\n\t/**\n\t * Resolve `path` against the outer container. `kind: \"container\"`\n\t * means the path addressed a file inside a nested zip/tar; anything\n\t * else keeps the literal semantics (a plain entry, possibly one whose\n\t * name happens to contain `!`).\n\t */\n\treadonly resolve: (path: string) => Promise<PathResolution>\n\t/**\n\t * List every file entry of the container `outer`. Resolves to\n\t * `undefined` when `outer` is not a container (or does not exist).\n\t * Memoized per outer name for the resolver's lifetime.\n\t */\n\treadonly list: (\n\t\touter: string,\n\t) => Promise<readonly OpenableArchiveEntry[] | undefined>\n}\n\n/** Split `outer!inner` at the first separator. */\nexport function splitVirtualPath(path: string):\n\t| {\n\t\t\treadonly outer: string\n\t\t\treadonly inner: string\n\t }\n\t| undefined {\n\tconst sep = path.indexOf(VIRTUAL_PATH_SEPARATOR)\n\tif (sep <= 0) return undefined\n\tconst inner = path.slice(sep + 1)\n\tif (inner.length === 0) return undefined\n\treturn { outer: path.slice(0, sep), inner }\n}\n\n/** Wrap an outer container entry as a {@link ArchiveSource} (real size). */\nexport function createOuterArchiveSource(\n\touter: OuterEntrySource,\n\touterName: string,\n\tsize: number,\n): ArchiveSource {\n\treturn {\n\t\tsize,\n\t\treadRange: (start, end) => outer.readSlice(outerName, start, end + 1),\n\t}\n}\n\n/**\n * Build a resolver over one outer container. Archive listings are\n * memoized per outer entry name for the resolver's lifetime (a\n * resource-level operation parses each nested archive at most once),\n * and — when a {@link NestedCdCache} is provided — across resolver\n * instances, so a multi-hook pass parses each nested archive once.\n *\n * `scope` disambiguates cache keys when one shared cache serves many\n * containers (e.g. every resource of a server): keys become\n * `<scope>:<outerName>`, so two resources holding same-named archives\n * never serve each other's listings.\n */\nexport function createNestedResolver(\n\touter: OuterEntrySource,\n\topts: { readonly cdCache?: NestedCdCache; readonly scope?: string } = {},\n): NestedResolver {\n\tconst cdCache = opts.cdCache\n\tconst scope = opts.scope\n\tconst cacheKey = (outerName: string): string =>\n\t\tscope === undefined ? outerName : `${scope}:${outerName}`\n\tconst listingMemo = new Map<\n\t\tstring,\n\t\tPromise<readonly ListedEntry[] | undefined>\n\t>()\n\tconst listedMemo = new Map<\n\t\tstring,\n\t\tPromise<readonly OpenableArchiveEntry[] | undefined>\n\t>()\n\n\tfunction listOuter(\n\t\touterName: string,\n\t): Promise<readonly ListedEntry[] | undefined> {\n\t\tconst key = cacheKey(outerName)\n\t\tconst pending = listingMemo.get(key)\n\t\tif (pending !== undefined) return pending\n\t\tconst shared = cdCache?.get(key) as\n\t\t\t| Promise<readonly ListedEntry[] | undefined>\n\t\t\t| undefined\n\t\tif (shared !== undefined) {\n\t\t\tlistingMemo.set(key, shared)\n\t\t\treturn shared\n\t\t}\n\t\tconst work = listOuterOnce(outerName)\n\t\tlistingMemo.set(key, work)\n\t\tcdCache?.set(key, work)\n\t\tvoid work.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t)\n\t\treturn work\n\t}\n\n\tasync function listOuterOnce(\n\t\touterName: string,\n\t): Promise<readonly ListedEntry[] | undefined> {\n\t\tconst size = await outer.sizeOf(outerName)\n\t\tif (size === undefined) return undefined\n\t\tif (size === 0) return undefined\n\t\tconst head = await outer.readSlice(\n\t\t\touterName,\n\t\t\t0,\n\t\t\tMath.min(size, SNIFF_WINDOW_BYTES),\n\t\t)\n\t\tconst format = sniffContainerFormat(head)\n\t\t// Zip is the only addressable nested format; tar/7z/rar/xz/gzip\n\t\t// are whole-archive formats and report \"not a container\".\n\t\tif (format !== \"zip\") return undefined\n\t\tconst source = createOuterArchiveSource(outer, outerName, size)\n\t\tconst records = await listZipEntriesFromSource(source)\n\t\treturn records.map((r) => ({\n\t\t\tname: r.name,\n\t\t\tsizeBytes: r.uncompressedSize,\n\t\t\touterSize: size,\n\t\t\tzip: r,\n\t\t}))\n\t}\n\n\tasync function resolve(path: string): Promise<PathResolution> {\n\t\tconst parts = splitVirtualPath(path)\n\t\tif (parts === undefined) return { kind: \"literal\" }\n\t\tconst { outer: outerName, inner } = parts\n\t\tconst entries = await listOuter(outerName)\n\t\tconst found = entries?.find((e) => e.name === inner)\n\t\tif (found === undefined) return { kind: \"literal\" }\n\t\treturn {\n\t\t\tkind: \"container\",\n\t\t\touter: outerName,\n\t\t\tentry: { name: found.name, sizeBytes: found.sizeBytes },\n\t\t\topenStream: () => openListedEntry(outer, outerName, found),\n\t\t}\n\t}\n\n\tasync function list(\n\t\touterName: string,\n\t): Promise<readonly OpenableArchiveEntry[] | undefined> {\n\t\tconst pending = listedMemo.get(outerName)\n\t\tif (pending !== undefined) return pending\n\t\tconst work = listOuter(outerName).then((entries) =>\n\t\t\tentries === undefined\n\t\t\t\t? undefined\n\t\t\t\t: entries.map((e) => listedWithStream(outer, outerName, e)),\n\t\t)\n\t\tlistedMemo.set(outerName, work)\n\t\tvoid work.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t)\n\t\treturn work\n\t}\n\n\treturn { resolve, list }\n}\n\ntype ListedEntry = ArchiveEntry & {\n\t/** Byte size of the outer container entry. */\n\treadonly outerSize: number\n\treadonly zip?: import(\"./zip-entries.ts\").ZipEntry\n}\n\nfunction listedWithStream(\n\touter: OuterEntrySource,\n\touterName: string,\n\tentry: ListedEntry,\n): OpenableArchiveEntry {\n\treturn {\n\t\tname: entry.name,\n\t\tsizeBytes: entry.sizeBytes,\n\t\topenStream: () => openListedEntry(outer, outerName, entry),\n\t}\n}\n\nfunction openListedEntry(\n\touter: OuterEntrySource,\n\touterName: string,\n\tentry: ListedEntry,\n): Readable {\n\tconst source = createOuterArchiveSource(outer, outerName, entry.outerSize)\n\tif (entry.zip !== undefined) return openZipEntryStream(source, entry.zip)\n\tthrow invalid(\n\t\t\"resource.archive_open_failed\",\n\t\t`entry \"${entry.name}\" has no stream backend`,\n\t\t{ name: entry.name },\n\t)\n}\n","import { randomUUID } from \"node:crypto\"\nimport { mkdir, readFile, rename, rm, stat, writeFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport type { MediaKind } from \"@hoardodile/sdk-types\"\nimport { fileTypeFromName } from \"@hoardodile/sdk-types\"\n\nimport { invalid } from \"../errors.ts\"\nimport {\n\textractSevenZipInto,\n\tlistSevenZipEntries,\n\tresolveSevenZipPath,\n} from \"./7z.ts\"\nimport {\n\tassertExtractedTree,\n\tdecodeZipNames,\n\tnormalizeExtractedTree,\n} from \"./extract.ts\"\nimport {\n\ttype ContainerFormat,\n\tSNIFF_WINDOW_BYTES,\n\tsniffContainerFormat,\n} from \"./format.ts\"\nimport { materializeFile } from \"./materialize.ts\"\nimport type { NestedCdCache } from \"./nested-cd-cache.ts\"\nimport {\n\ttype ArchiveEntry,\n\tcreateNestedResolver,\n\tcreateOuterArchiveSource,\n\ttype OuterEntrySource,\n} from \"./nested-entry.ts\"\nimport { streamRange } from \"./zip-entries.ts\"\n\n/**\n * Header window read before handing bytes to the injected image prober.\n * Shared with the host's sharp-backed probes so both sides feed the same\n * bounded window (JPEG SOF markers live in the first KBs; 256KB covers\n * even EXIF-bloated JPEGs).\n */\nexport const PROBE_HEADER_BYTES = 256 * 1024\n\n/** Input accepted by the injected image prober (host's sharp probe). */\nexport type ImageProbeInput = string | Buffer\n\n/** Result of the injected image prober. */\nexport type ImageProbeResult = {\n\treadonly width: number\n\treadonly height: number\n\treadonly animated: boolean\n}\n\n/**\n * Materialize a container entry (zip/tar/7z/rar/xz/gzip) into a cache\n * directory so the browser can serve the inner files over plain URLs.\n * This is the only disk-writing capability of the plugin API; everything\n * else about containers (cover probing, dimensions, hashes) reads\n * through virtual paths without materialization.\n *\n * Extraction is whole-archive through 7-Zip (budgets and paths validated\n * from its listing, then the extracted tree re-verified — see\n * `assertExtractedTree`). The manifest below is built from the listing,\n * so entries carry the exact names and sizes 7-Zip reported.\n *\n * Cache layout (per archive):\n * <cacheDir>/<archiveName>/index.json — completion marker + manifest\n * <cacheDir>/<archiveName>/<innerPath> — extracted file (subdirs kept)\n *\n * The `index.json` marker is written last and atomically: its presence\n * means extraction completed. A partial or failed run leaves files but no\n * marker, so the next call re-extracts idempotently.\n */\n\nexport const EXTRACT_INDEX_VERSION = 1\n\nexport type ExtractedEntry = {\n\treadonly path: string\n\treadonly sizeBytes: number\n\treadonly kind: MediaKind\n\treadonly width?: number\n\treadonly height?: number\n\treadonly animated?: boolean\n}\n\nexport type ExtractionResult = {\n\treadonly entries: readonly ExtractedEntry[]\n}\n\nexport type ExtractProgress = {\n\t/** Entries materialized so far (skipped cached files count too). */\n\treadonly done: number\n\treadonly total: number\n}\n\nexport type ExtractArchiveDeps = {\n\t/** Read access to the outer container (the resource's entries). */\n\treadonly outer: OuterEntrySource\n\t/**\n\t * Absolute cache directory for this resource's plugin extractions\n\t * (e.g. `<cacheRoot>/resources/<id>/extracted/v<N>/archives`).\n\t */\n\treadonly cacheDir: string\n\t/** Cumulative uncompressed byte budget for one archive. */\n\treadonly maxBytes: number\n\t/** Entry-count budget for one archive. */\n\treadonly maxEntries: number\n\t/** Optional image prober (sharp-backed) for per-entry dimensions. */\n\treadonly probeImage?: (\n\t\tsource: ImageProbeInput,\n\t\textHint?: string,\n\t) => Promise<ImageProbeResult | undefined>\n\t/**\n\t * Called as entries are materialized, so a long first extraction can\n\t * surface progress to the user.\n\t */\n\treadonly onProgress?: (progress: ExtractProgress) => void\n\t/** Shared nested-listing cache (see {@link NestedCdCache}). */\n\treadonly nestedCdCache?: NestedCdCache\n}\n\nexport type ArchiveExtractor = {\n\t/**\n\t * Materialize (or re-list) the container `archiveName`. Idempotent\n\t * and single-flighted per archive name.\n\t */\n\treadonly extract: (archiveName: string) => Promise<ExtractionResult>\n\t/**\n\t * List the container's entries without materializing anything into\n\t * the final cache layout. Resolves to `undefined` when `archiveName`\n\t * is not a container (or no 7-Zip binary exists for non-zip\n\t * formats).\n\t */\n\treadonly list: (\n\t\tarchiveName: string,\n\t) => Promise<readonly ArchiveEntry[] | undefined>\n}\n\n/** Build a single-flight, idempotent extractor over one outer container. */\nexport function createArchiveExtractor(\n\tdeps: ExtractArchiveDeps,\n): ArchiveExtractor {\n\tconst resolver = createNestedResolver(deps.outer, {\n\t\tcdCache: deps.nestedCdCache,\n\t})\n\tconst inflight = new Map<string, Promise<ExtractionResult>>()\n\n\tfunction extract(archiveName: string): Promise<ExtractionResult> {\n\t\tconst pending = inflight.get(archiveName)\n\t\tif (pending !== undefined) return pending\n\t\tconst work = extractOnce(archiveName)\n\t\tinflight.set(archiveName, work)\n\t\tvoid work.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t)\n\t\treturn work\n\t}\n\n\tasync function extractOnce(archiveName: string): Promise<ExtractionResult> {\n\t\tconst size = await deps.outer.sizeOf(archiveName)\n\t\tif (size === undefined) {\n\t\t\tthrow invalid(\n\t\t\t\t\"plugin.container_extract_failed\",\n\t\t\t\t`\"${archiveName}\" does not exist`,\n\t\t\t\t{ archiveName },\n\t\t\t)\n\t\t}\n\t\tconst markerPath = join(deps.cacheDir, archiveName, \"index.json\")\n\t\tconst existing = await readExistingManifest(markerPath, archiveName)\n\t\tif (existing !== undefined) return { entries: existing }\n\t\tif (size > 0) {\n\t\t\tconst head = await deps.outer.readSlice(\n\t\t\t\tarchiveName,\n\t\t\t\t0,\n\t\t\t\tMath.min(size, SNIFF_WINDOW_BYTES),\n\t\t\t)\n\t\t\tconst format = sniffContainerFormat(head)\n\t\t\tif (format !== undefined) {\n\t\t\t\treturn extractWithSevenZip(archiveName, size, markerPath, format)\n\t\t\t}\n\t\t}\n\t\tthrow invalid(\n\t\t\t\"plugin.container_extract_failed\",\n\t\t\t`\"${archiveName}\" is not a supported archive (zip/tar/7z/rar/xz/gzip)`,\n\t\t\t{ archiveName },\n\t\t)\n\t}\n\n\t/**\n\t * Whole-archive extraction through 7-Zip: materialize the outer entry\n\t * to a temp file (7-Zip needs a real path), validate budgets and\n\t * encryption from the listing, extract into the cache dir, then build\n\t * the idempotent manifest. Runs for every supported format — zip/tar\n\t * included, exactly like the 7z/rar/xz branch before them.\n\t */\n\tasync function extractWithSevenZip(\n\t\tarchiveName: string,\n\t\tsize: number,\n\t\tmarkerPath: string,\n\t\tformat: ContainerFormat,\n\t): Promise<ExtractionResult> {\n\t\tconst source = createOuterArchiveSource(deps.outer, archiveName, size)\n\t\tconst tempPath = join(\n\t\t\tdeps.cacheDir,\n\t\t\t`${archiveName}.7z-partial-${process.pid}-${randomUUID().slice(0, 8)}`,\n\t\t)\n\t\tawait mkdir(deps.cacheDir, { recursive: true })\n\t\ttry {\n\t\t\tawait materializeFile({\n\t\t\t\topenStream: () => streamRange(source, 0, size - 1),\n\t\t\t\ttarget: tempPath,\n\t\t\t\texpectedSize: size,\n\t\t\t})\n\t\t\tconst entries = await listSevenZipEntries(tempPath)\n\t\t\tconst files = entries.filter((e) => !e.folder)\n\t\t\tcheckExtractionBudgets(archiveName, files, deps.maxBytes, deps.maxEntries)\n\t\t\tif (entries.some((e) => e.encrypted)) {\n\t\t\t\tthrow invalid(\n\t\t\t\t\t\"plugin.container_extract_failed\",\n\t\t\t\t\t`archive \"${archiveName}\" is password-protected — encrypted archives are not supported`,\n\t\t\t\t\t{ archiveName },\n\t\t\t\t)\n\t\t\t}\n\t\t\t// 7-Zip's text listing drops invalid-UTF-8 bytes on POSIX, so\n\t\t\t// for zip archives the decoded names come from the archive\n\t\t\t// itself (yauzl): the manifest, the probes and the legacy-name\n\t\t\t// renames below all agree on them.\n\t\t\tconst decoded =\n\t\t\t\tformat === \"zip\"\n\t\t\t\t\t? await decodeZipNames(\n\t\t\t\t\t\t\ttempPath,\n\t\t\t\t\t\t\tfiles.map((e) => ({ name: e.name, sizeBytes: e.sizeBytes })),\n\t\t\t\t\t\t)\n\t\t\t\t\t: undefined\n\t\t\tconst materialized = decoded?.files ?? files\n\t\t\t// Pre-validate every path from the listing — 7-Zip itself\n\t\t\t// warns-and-skips unsafe names instead of failing.\n\t\t\tfor (const entry of materialized) sanitizeExtractPath(entry.name)\n\t\t\tconst root = join(deps.cacheDir, archiveName)\n\t\t\tawait mkdir(root, { recursive: true })\n\t\t\tawait extractSevenZipInto(tempPath, root)\n\t\t\t// 7-Zip writes legacy zip names verbatim on POSIX and restores\n\t\t\t// mode bits that can strip app access; fix both before the tree\n\t\t\t// is re-walked (symlink scan) or read for the manifest below.\n\t\t\t// The decoded names are the ground truth the legacy rename pass\n\t\t\t// matches against (see `renameLegacyZipNames` in extract.ts).\n\t\t\tawait normalizeExtractedTree(root, {\n\t\t\t\tlegacyZipNames: format === \"zip\",\n\t\t\t\texpectedNames: decoded?.paths,\n\t\t\t})\n\t\t\tawait assertExtractedTree(root, deps.maxBytes)\n\t\t\tdeps.onProgress?.({ done: files.length, total: files.length })\n\t\t\tconst manifest = await buildManifest(\n\t\t\t\tarchiveName,\n\t\t\t\tmaterialized.map((e) => ({ name: e.name, sizeBytes: e.sizeBytes })),\n\t\t\t)\n\t\t\tawait writeManifest(markerPath, archiveName, manifest)\n\t\t\t// The manifest now serves listings (and materialized virtual\n\t\t\t// paths); drop the size-keyed listing cache for this archive.\n\t\t\tawait rm(join(deps.cacheDir, `${archiveName}.7z-list`), {\n\t\t\t\tforce: true,\n\t\t\t}).catch(() => {})\n\t\t\tawait rm(join(deps.cacheDir, `${archiveName}.7z-list.json`), {\n\t\t\t\tforce: true,\n\t\t\t}).catch(() => {})\n\t\t\treturn { entries: manifest }\n\t\t} finally {\n\t\t\tawait rm(tempPath, { force: true }).catch(() => {})\n\t\t}\n\t}\n\n\t/** Shared bomb guards for one extraction: entry count and total bytes. */\n\tfunction checkExtractionBudgets(\n\t\tarchiveName: string,\n\t\tentries: readonly { readonly sizeBytes: number }[],\n\t\tmaxBytes: number,\n\t\tmaxEntries: number,\n\t): void {\n\t\tif (entries.length > maxEntries) {\n\t\t\tthrow invalid(\n\t\t\t\t\"plugin.container_extract_failed\",\n\t\t\t\t`archive \"${archiveName}\" has ${entries.length} entries, exceeding the limit of ${maxEntries}`,\n\t\t\t\t{ archiveName, entries: entries.length, maxEntries },\n\t\t\t)\n\t\t}\n\t\tlet totalBytes = 0\n\t\tfor (const entry of entries) totalBytes += entry.sizeBytes\n\t\tif (totalBytes > maxBytes) {\n\t\t\tthrow invalid(\n\t\t\t\t\"plugin.container_extract_failed\",\n\t\t\t\t`archive \"${archiveName}\" unpacks to ${totalBytes} bytes, exceeding the limit of ${maxBytes}`,\n\t\t\t\t{ archiveName, totalBytes, maxBytes },\n\t\t\t)\n\t\t}\n\t}\n\n\tasync function buildManifest(\n\t\tarchiveName: string,\n\t\tentries: readonly ArchiveEntry[],\n\t): Promise<readonly ExtractedEntry[]> {\n\t\tconst manifest: ExtractedEntry[] = []\n\t\tfor (const entry of entries) {\n\t\t\tconst kind = fileTypeFromName(entry.name)?.kind ?? \"other\"\n\t\t\tconst row: MutableExtractedEntry = {\n\t\t\t\tpath: entry.name,\n\t\t\t\tsizeBytes: entry.sizeBytes,\n\t\t\t\tkind,\n\t\t\t}\n\t\t\tif (kind === \"image\" && deps.probeImage !== undefined) {\n\t\t\t\tconst rel = sanitizeExtractPath(entry.name)\n\t\t\t\tconst filePath = join(deps.cacheDir, archiveName, rel)\n\t\t\t\tconst probe = await probeImageHead(\n\t\t\t\t\tdeps.probeImage,\n\t\t\t\t\tfilePath,\n\t\t\t\t\tentry.name,\n\t\t\t\t)\n\t\t\t\tif (probe !== undefined) {\n\t\t\t\t\trow.width = probe.width\n\t\t\t\t\trow.height = probe.height\n\t\t\t\t\trow.animated = probe.animated\n\t\t\t\t}\n\t\t\t}\n\t\t\tmanifest.push(row)\n\t\t}\n\t\treturn manifest\n\t}\n\n\t/**\n\t * List the container's entries. Zip lists from the nested resolver\n\t * (CD parse, nothing materialized); tar/7z/rar/xz list through the\n\t * 7-Zip binary over a size-keyed materialization cache in the cache\n\t * dir (7-Zip needs a real path; the bytes are written once per outer\n\t * size and reused across hook calls — every hook call builds a fresh\n\t * API instance, so per-call temp files would re-write the whole\n\t * archive each time). Resolves to `undefined` when the entry is not a\n\t * container, is gzip (whole-archive single-stream), or the 7-Zip\n\t * binary is absent (quiet degradation — callers treat it as \"not a\n\t * container\", matching the resolver semantics).\n\t */\n\tasync function list(\n\t\tarchiveName: string,\n\t): Promise<readonly ArchiveEntry[] | undefined> {\n\t\tconst size = await deps.outer.sizeOf(archiveName)\n\t\tif (size === undefined) return undefined\n\t\t// An extracted archive answers from its manifest — cheaper than\n\t\t// any listing, and the manifest is the source of truth for the\n\t\t// materialized virtual paths (see nested-view.ts).\n\t\tconst markerPath = join(deps.cacheDir, archiveName, \"index.json\")\n\t\tconst extracted = await readExistingManifest(markerPath, archiveName)\n\t\tif (extracted !== undefined) {\n\t\t\treturn extracted.map((e) => ({ name: e.path, sizeBytes: e.sizeBytes }))\n\t\t}\n\t\tif (size === 0) return undefined\n\t\tconst head = await deps.outer.readSlice(\n\t\t\tarchiveName,\n\t\t\t0,\n\t\t\tMath.min(size, SNIFF_WINDOW_BYTES),\n\t\t)\n\t\tconst format = sniffContainerFormat(head)\n\t\tif (format === \"zip\") {\n\t\t\tconst entries = await resolver.list(archiveName)\n\t\t\tif (entries === undefined) return undefined\n\t\t\treturn entries.map((e) => ({ name: e.name, sizeBytes: e.sizeBytes }))\n\t\t}\n\t\tif (\n\t\t\tformat === \"tar\" ||\n\t\t\tformat === \"7z\" ||\n\t\t\tformat === \"rar\" ||\n\t\t\tformat === \"xz\"\n\t\t) {\n\t\t\treturn listViaSevenZip(archiveName, size)\n\t\t}\n\t\treturn undefined\n\t}\n\n\t/**\n\t * Non-zip listing through 7-Zip. The outer entry is materialized once\n\t * per size into `<cacheDir>/<archiveName>.7z-list` (keyed by outer\n\t * size — the source is immutable per version, so a size match means\n\t * the cached bytes are the same file) and the parsed listing is\n\t * cached next to it. `extractWithSevenZip` removes both files once a\n\t * real extraction lands, so the manifest takes over afterwards.\n\t */\n\tasync function listViaSevenZip(\n\t\tarchiveName: string,\n\t\tsize: number,\n\t): Promise<readonly ArchiveEntry[] | undefined> {\n\t\tif (resolveSevenZipPath() === undefined) return undefined\n\t\tawait mkdir(deps.cacheDir, { recursive: true })\n\t\tconst cachePath = join(deps.cacheDir, `${archiveName}.7z-list`)\n\t\tconst entriesPath = join(deps.cacheDir, `${archiveName}.7z-list.json`)\n\t\tconst cached = await stat(cachePath).catch(() => undefined)\n\t\tif (cached?.size === size) {\n\t\t\tconst raw = await readFile(entriesPath, \"utf8\").catch(() => undefined)\n\t\t\tif (raw !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.parse(raw) as readonly ArchiveEntry[]\n\t\t\t\t} catch {\n\t\t\t\t\t// Corrupt cache — fall through and re-materialize.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tconst source = createOuterArchiveSource(deps.outer, archiveName, size)\n\t\t\tawait materializeFile({\n\t\t\t\topenStream: () => streamRange(source, 0, size - 1),\n\t\t\t\ttarget: cachePath,\n\t\t\t\texpectedSize: size,\n\t\t\t})\n\t\t\tconst listed = await listSevenZipEntries(cachePath)\n\t\t\tconst files = listed\n\t\t\t\t.filter((e) => !e.folder)\n\t\t\t\t.map((e) => ({ name: e.name, sizeBytes: e.sizeBytes }))\n\t\t\tawait writeFile(entriesPath, JSON.stringify(files))\n\t\t\treturn files\n\t\t} catch (err) {\n\t\t\tawait rm(cachePath, { force: true }).catch(() => {})\n\t\t\tawait rm(entriesPath, { force: true }).catch(() => {})\n\t\t\tthrow err\n\t\t}\n\t}\n\n\treturn { extract, list }\n}\n\ntype MutableExtractedEntry = {\n\tpath: string\n\tsizeBytes: number\n\tkind: MediaKind\n\twidth?: number\n\theight?: number\n\tanimated?: boolean\n}\n\n/**\n * Read and validate a manifest written by {@link writeManifest}: version\n * and archive-name must match, otherwise the marker is stale garbage and\n * the caller re-extracts. Shared with the nested-view materialized-path\n * resolution.\n */\nexport async function readExistingManifest(\n\tmarkerPath: string,\n\tarchiveName: string,\n): Promise<readonly ExtractedEntry[] | undefined> {\n\tconst raw = await readFile(markerPath, \"utf8\").catch(() => undefined)\n\tif (raw === undefined) return undefined\n\ttry {\n\t\tconst parsed = JSON.parse(raw) as {\n\t\t\treadonly v?: number\n\t\t\treadonly archiveName?: string\n\t\t\treadonly entries?: readonly ExtractedEntry[]\n\t\t}\n\t\tif (\n\t\t\tparsed.v !== EXTRACT_INDEX_VERSION ||\n\t\t\tparsed.archiveName !== archiveName ||\n\t\t\t!Array.isArray(parsed.entries)\n\t\t) {\n\t\t\treturn undefined\n\t\t}\n\t\treturn parsed.entries\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nasync function writeManifest(\n\tmarkerPath: string,\n\tarchiveName: string,\n\tentries: readonly ExtractedEntry[],\n): Promise<void> {\n\tconst payload = JSON.stringify({\n\t\tv: EXTRACT_INDEX_VERSION,\n\t\tarchiveName,\n\t\tentries,\n\t})\n\tconst partial = `${markerPath}.partial-${process.pid}`\n\tawait writeFile(partial, payload)\n\tawait rename(partial, markerPath)\n}\n\n/**\n * Normalize an inner entry name to a safe relative path: backslashes\n * become forward slashes, traversal segments and control characters are\n * rejected, and the result must stay inside the extraction root.\n */\nexport function sanitizeExtractPath(name: string): string {\n\tconst normalized = name.replace(/\\\\/g, \"/\")\n\tif (normalized.startsWith(\"/\") || /^[A-Za-z]:/.test(normalized)) {\n\t\tthrow invalid(\n\t\t\t\"plugin.container_extract_failed\",\n\t\t\t`archive entry has an absolute path: \"${name}\"`,\n\t\t\t{ name },\n\t\t)\n\t}\n\tconst segments = normalized.split(\"/\")\n\tfor (const seg of segments) {\n\t\tif (seg.length === 0 || seg === \".\" || seg === \"..\") {\n\t\t\tthrow invalid(\n\t\t\t\t\"plugin.container_extract_failed\",\n\t\t\t\t`archive entry has an unsafe path: \"${name}\"`,\n\t\t\t\t{ name },\n\t\t\t)\n\t\t}\n\t\tif (hasControlChar(seg)) {\n\t\t\tthrow invalid(\n\t\t\t\t\"plugin.container_extract_failed\",\n\t\t\t\t`archive entry contains control characters: \"${name}\"`,\n\t\t\t\t{ name },\n\t\t\t)\n\t\t}\n\t}\n\treturn segments.join(\"/\")\n}\n\n/** True when any char code is a control character (C0 or DEL). */\nfunction hasControlChar(value: string): boolean {\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst code = value.charCodeAt(i)\n\t\tif (code < 0x20 || code === 0x7f) return true\n\t}\n\treturn false\n}\n\n/** Probe one extracted image's header window for dimensions. */\nasync function probeImageHead(\n\tprobeImage: NonNullable<ExtractArchiveDeps[\"probeImage\"]>,\n\tfilePath: string,\n\tname: string,\n): Promise<ImageProbeResult | undefined> {\n\tconst head = await readFile(filePath).then((b) =>\n\t\tb.subarray(0, PROBE_HEADER_BYTES),\n\t)\n\tif (head.length === 0) return undefined\n\tconst ext = name.slice(name.lastIndexOf(\".\")).toLowerCase()\n\treturn probeImage(head, ext)\n}\n","/**\n * Process-wide cache of nested container listings (central directories\n * of zip/tar entries addressed via virtual paths). Resolving\n * `outer!inner` re-parses the outer archive's CD on every hook call;\n * this cache keeps the parsed listing resident so a multi-hook pass\n * (sourceMeta → cover → hashes → listFiles) parses each nested archive\n * once.\n *\n * Keyed by the outer entry name only — callers scope the cache instance\n * to a fixed (resId, fileVersion), and archives are immutable per\n * version, so a key never outlives its content.\n */\nexport type NestedCdCache = {\n\treadonly get: (key: string) => Promise<unknown> | undefined\n\treadonly set: (key: string, value: Promise<unknown>) => void\n\treadonly clear: () => void\n}\n\nexport type NestedCdCacheOptions = {\n\t/** Max number of nested archives kept resident. */\n\treadonly maxEntries: number\n}\n\nconst DEFAULT_MAX_ENTRIES = 512\n\n/** Create an LRU-bounded nested listing cache. */\nexport function createNestedCdCache(\n\topts?: Partial<NestedCdCacheOptions>,\n): NestedCdCache {\n\tconst maxEntries = opts?.maxEntries ?? DEFAULT_MAX_ENTRIES\n\tconst lru = new Map<string, Promise<unknown>>()\n\n\tfunction get(key: string): Promise<unknown> | undefined {\n\t\tconst value = lru.get(key)\n\t\tif (value === undefined) return undefined\n\t\tlru.delete(key)\n\t\tlru.set(key, value)\n\t\treturn value\n\t}\n\n\tfunction set(key: string, value: Promise<unknown>): void {\n\t\tlru.set(key, value)\n\t\twhile (lru.size > maxEntries) {\n\t\t\tconst oldest = lru.keys().next().value\n\t\t\tif (oldest === undefined) break\n\t\t\tlru.delete(oldest)\n\t\t}\n\t}\n\n\tfunction clear(): void {\n\t\tlru.clear()\n\t}\n\n\treturn { get, set, clear }\n}\n","import { createWriteStream, readdirSync } from \"node:fs\"\nimport { join, resolve, sep } from \"node:path\"\nimport type { Readable } from \"node:stream\"\nimport { pipeline } from \"node:stream/promises\"\nimport yazl from \"yazl\"\n\n/**\n * On-the-fly zip exports. Resource sources are stored as bare files on\n * disk — nothing is packed at commit time; this module streams STORED\n * zip bytes straight to the consumer without a staging file.\n */\n\n/**\n * A logical zip entry whose bytes are produced on demand. Streams are\n * read once, in order, when the output is consumed.\n */\nexport type ZipStreamEntry = {\n\treadonly name: string\n\treadonly size: number\n\treadonly openStream: () => Readable\n}\n\n/**\n * Stream a STORED zip from an ordered list of logical entries. Used by\n * the HTTP layer for exports (resource source packs, bulk downloads)\n * without a staging file on disk. Zero-length entries are packed as\n * empty buffers.\n */\nexport function streamStoredZip(entries: readonly ZipStreamEntry[]) {\n\tconst zip = new yazl.ZipFile()\n\tfor (const entry of entries) {\n\t\tif (entry.size === 0) {\n\t\t\tzip.addBuffer(Buffer.alloc(0), entry.name, { compress: false })\n\t\t\tcontinue\n\t\t}\n\t\tzip.addReadStream(entry.openStream(), entry.name, {\n\t\t\tcompress: false,\n\t\t\tsize: entry.size,\n\t\t})\n\t}\n\tzip.end()\n\treturn zip.outputStream\n}\n\n/**\n * Pack the *contents* of `srcDir` (its entries at the archive root) into\n * a deflated zip written to `zipPath`. Entry names use forward slashes,\n * the file order is sorted by relative path, and the output is streamed\n * to disk — deterministic on every platform, memory-bounded regardless\n * of dist size. The canonical packer for project-internal zips (the\n * plugin release artifact via the CLI); resource exports use\n * {@link streamStoredZip} instead.\n */\nexport async function packZipDirectory(\n\tsrcDir: string,\n\tzipPath: string,\n): Promise<void> {\n\tconst root = resolve(srcDir)\n\tconst files = listFilesSorted(root)\n\tconst zip = new yazl.ZipFile()\n\tfor (const abs of files) {\n\t\t// yazl wants metadata names with forward slashes; the local-path\n\t\t// basename order is deterministic thanks to listFilesSorted.\n\t\tconst rel = abs\n\t\t\t.slice(root.length + 1)\n\t\t\t.split(sep)\n\t\t\t.join(\"/\")\n\t\tzip.addFile(abs, rel, { compress: true })\n\t}\n\tzip.end()\n\tawait pipeline(zip.outputStream, createWriteStream(resolve(zipPath)))\n}\n\n/** Every regular file under `root`, sorted by relative path (recursive). */\nfunction listFilesSorted(root: string): string[] {\n\tconst out: string[] = []\n\tconst walk = (dir: string) => {\n\t\tfor (const entry of readdirSync(dir, { withFileTypes: true })) {\n\t\t\tconst abs = join(dir, entry.name)\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\twalk(abs)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!entry.isFile()) continue\n\t\t\tout.push(abs)\n\t\t}\n\t}\n\twalk(root)\n\treturn out.sort()\n}\n","import { createHash } from \"node:crypto\"\nimport type { Readable } from \"node:stream\"\nimport type { ImageHashKind } from \"@hoardodile/sdk-types\"\n\n/**\n * Content hashing for the `imageHashes` plugin hook. All perceptual\n * hashes derive from one 32×32 grayscale rendition (single sharp\n * decode), so requesting several kinds costs one decode:\n * - `sha256`: exact digest of the raw entry bytes (streamed).\n * - `dhash`: difference hash — 9 samples per row of the 32×32 grid\n * (nearest-neighbour spread), adjacent pairs compared, equivalent to\n * the standard 9×8 dHash.\n * - `phash`: standard DCT-based perceptual hash — the 32×32 grid is\n * mean-pooled to 8×8, then the 8×8 DCT coefficients are thresholded\n * at the median.\n *\n * The 64-bit values are hex strings (16 chars), compared by Hamming\n * distance in `@hoardodile/sdk-server` / the server's hash service.\n */\n\n/** Side of the grayscale grid every perceptual hash is derived from. */\nexport const PHASH_GRID = 32\n\n/**\n * Minimum grayscale standard deviation (0–255 scale) for an image to\n * yield perceptual hashes. Near-flat images (solid fills, blank pages)\n * hash to distance-0 neighbours of every other flat image, so they are\n * skipped — `sha256` exact hashing is unaffected.\n */\nexport const MIN_PERCEPTUAL_STDDEV = 8\n\n/** Stream `stream` through a digest of `algo` and resolve its lowercase hex. */\nexport function hashStream(\n\tstream: Readable,\n\talgo: \"md5\" | \"sha256\",\n): Promise<string> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst hash = createHash(algo)\n\t\tstream.on(\"data\", (chunk: string | Buffer) => hash.update(chunk))\n\t\tstream.on(\"end\", () => resolve(hash.digest(\"hex\")))\n\t\tstream.on(\"error\", reject)\n\t})\n}\n\n/**\n * dHash over a `PHASH_GRID × PHASH_GRID` grayscale buffer: 9 samples per\n * row (nearest-neighbour spread across the width), each of 8×8 bits\n * comparing a sample against its right neighbour, packed MSB-first into\n * a 16-char hex string.\n */\nexport function computeDHash(gray: Uint8Array, grid = PHASH_GRID): string {\n\tlet bits = 0n\n\tfor (let row = 0; row < 8; row++) {\n\t\tfor (let col = 0; col < 8; col++) {\n\t\t\tconst left = sampleAt(gray, grid, row, col)\n\t\t\tconst right = sampleAt(gray, grid, row, col + 1)\n\t\t\tbits = (bits << 1n) | BigInt(left > right ? 1 : 0)\n\t\t}\n\t}\n\treturn bits.toString(16).padStart(16, \"0\")\n}\n\n/** Grayscale value at grid position (row, col), clamped to the grid. */\nfunction sampleAt(\n\tgray: Uint8Array,\n\tgrid: number,\n\trow: number,\n\tcol: number,\n): number {\n\tconst x = Math.round((col * (grid - 1)) / 8)\n\treturn gray[row * grid + x] ?? 0\n}\n\n/**\n * pHash over a `PHASH_GRID × PHASH_GRID` grayscale buffer: the grid is\n * mean-pooled to 8×8, then the 8×8 type-II DCT coefficients (precomputed\n * cosine tables) are thresholded at their median, each bit MSB-first\n * into a 16-char hex string.\n */\nexport function computePHash(gray: Uint8Array, grid = PHASH_GRID): string {\n\tconst n = 8\n\tconst small = meanPool(gray, grid, n)\n\tconst cos = precomputeCosines(n, n)\n\tconst coefficients: number[] = []\n\tfor (let v = 0; v < n; v++) {\n\t\tfor (let u = 0; u < n; u++) {\n\t\t\tlet sum = 0\n\t\t\tfor (let y = 0; y < n; y++) {\n\t\t\t\tconst cosYv = cos[v]?.[y] ?? 0\n\t\t\t\tconst cosXu = cos[u] ?? []\n\t\t\t\tlet rowSum = 0\n\t\t\t\tfor (let x = 0; x < n; x++) {\n\t\t\t\t\trowSum += (small[y * n + x] ?? 0) * (cosXu[x] ?? 0)\n\t\t\t\t}\n\t\t\t\tsum += rowSum * cosYv\n\t\t\t}\n\t\t\tconst cu = u === 0 ? 1 / Math.SQRT2 : 1\n\t\t\tconst cv = v === 0 ? 1 / Math.SQRT2 : 1\n\t\t\tcoefficients.push(0.25 * cu * cv * sum)\n\t\t}\n\t}\n\tconst median = medianOf(coefficients)\n\tlet bits = 0n\n\tfor (const coefficient of coefficients) {\n\t\tbits = (bits << 1n) | BigInt(coefficient > median ? 1 : 0)\n\t}\n\treturn bits.toString(16).padStart(16, \"0\")\n}\n\n/** Mean-pool a `grid × grid` buffer into an `n × n` grid of averages. */\nfunction meanPool(gray: Uint8Array, grid: number, n: number): number[] {\n\tconst step = grid / n\n\tconst pooled: number[] = []\n\tfor (let by = 0; by < n; by++) {\n\t\tfor (let bx = 0; bx < n; bx++) {\n\t\t\tlet sum = 0\n\t\t\tfor (let y = 0; y < step; y++) {\n\t\t\t\tfor (let x = 0; x < step; x++) {\n\t\t\t\t\tsum += gray[(by * step + y) * grid + bx * step + x] ?? 0\n\t\t\t\t}\n\t\t\t}\n\t\t\tpooled.push(sum / (step * step))\n\t\t}\n\t}\n\treturn pooled\n}\n\n/**\n * Population standard deviation of a grayscale buffer. Perceptual hashes\n * of near-flat images (solid fills, blank pages) cluster at distance 0\n * regardless of content — callers gate hash emission on this measure.\n */\nexport function grayStddev(gray: Uint8Array): number {\n\tif (gray.length === 0) return 0\n\tlet mean = 0\n\tfor (const value of gray) mean += value\n\tmean /= gray.length\n\tlet squared = 0\n\tfor (const value of gray) {\n\t\tconst delta = value - mean\n\t\tsquared += delta * delta\n\t}\n\treturn Math.sqrt(squared / gray.length)\n}\n\nfunction precomputeCosines(grid: number, n: number): number[][] {\n\tconst cosX: number[][] = []\n\tfor (let k = 0; k < n; k++) {\n\t\tconst row: number[] = []\n\t\tfor (let x = 0; x < grid; x++) {\n\t\t\trow.push(Math.cos(((2 * x + 1) * k * Math.PI) / (2 * grid)))\n\t\t}\n\t\tcosX.push(row)\n\t}\n\treturn cosX\n}\n\nfunction medianOf(values: readonly number[]): number {\n\tconst sorted = [...values].sort((a, b) => a - b)\n\treturn sorted[Math.floor(sorted.length / 2)] ?? 0\n}\n\n/** Kinds whose values come from the single grayscale decode. */\nexport const PERCEPTUAL_HASH_KINDS: readonly ImageHashKind[] = [\n\t\"dhash\",\n\t\"phash\",\n]\n","import { createReadStream } from \"node:fs\"\nimport { readFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport type { Readable } from \"node:stream\"\nimport { buffer } from \"node:stream/consumers\"\nimport {\n\ttype ExtractedEntry,\n\treadExistingManifest,\n} from \"./archive/extract-archive.ts\"\nimport type { NestedCdCache, NestedResolver } from \"./archive/index.ts\"\nimport { createNestedResolver, splitVirtualPath } from \"./archive/index.ts\"\nimport { readFileRange } from \"./archive/zip-entries.ts\"\nimport type { ResourceContainer } from \"./container.ts\"\n\n/**\n * Wrap a {@link ResourceContainer} so every read operation understands\n * virtual paths (`outer!inner`, see `./archive`). The plugin API,\n * the server artifact view and the CLI directory container all funnel\n * entry reads through this wrapper, so nested-container support lands\n * everywhere (hooks, cover pipeline, thumbnails, the HTTP file route)\n * without each consumer knowing about containers.\n *\n * A virtual path resolves in two steps:\n * 1. nested zip addressing — the resolver reads the outer entry's\n * central directory and streams the *decompressed* inner bytes;\n * 2. materialized cache addressing — when `extractCacheDir` is wired\n * and the outer archive was extracted by the plugin API (its\n * manifest exists in `<extractCacheDir>/<outer>/`), the inner file\n * is served straight from disk. This is how non-zip containers\n * (rar/7z/tar — not expressible as random-access sources) get\n * `outer!inner` addressing after extraction, and it is strictly\n * better than streaming: entries are seekable.\n *\n * Literal entries pass through untouched; virtual entries are only\n * addressable by name, never enumerated.\n *\n * `scope` prefixes nested-cache keys when the cache is shared across\n * containers (see {@link createNestedResolver}); `resolveSeekablePath`\n * forwards the base container's capability for literal entries and the\n * extracted file path for materialized ones.\n */\nexport function createNestedAwareContainer(\n\tbase: ResourceContainer,\n\tnestedCdCache?: NestedCdCache,\n\tscope?: string,\n\textractCacheDir?: string,\n): ResourceContainer {\n\tconst resolver = createNestedResolver(\n\t\t{\n\t\t\tsizeOf: (rel) => base.resolveByteRange(rel).then((r) => r?.size),\n\t\t\treadSlice: (rel, start, end) => base.readEntrySlice(rel, start, end),\n\t\t},\n\t\t{ cdCache: nestedCdCache, scope },\n\t)\n\n\t// Manifest reads are memoized per (cacheDir, outer) to avoid re-parsing\n\t// the JSON on every virtual read. A **present** manifest is immutable\n\t// per version, so a parsed result never needs invalidation. An\n\t// **absent** manifest, however, is only \"not materialized yet\": a\n\t// plugin may probe a non-zip `outer!inner` before calling\n\t// `extractArchive`, then materialize it. Pinning the \"absent\" state\n\t// would make the same view stale forever, so the cache only keeps a\n\t// positive result and re-checks after an absent read.\n\tconst manifestMemo = new Map<\n\t\tstring,\n\t\tPromise<readonly ExtractedEntry[] | undefined>\n\t>()\n\n\tasync function readManifest(\n\t\touter: string,\n\t): Promise<readonly ExtractedEntry[] | undefined> {\n\t\tconst key = `${extractCacheDir}:${outer}`\n\t\tconst pending = manifestMemo.get(key)\n\t\tif (pending !== undefined) return pending\n\t\t// Share the in-flight read so concurrent callers do not re-read the\n\t\t// same archive's manifest, then drop the key when the answer is\n\t\t// \"absent\" so a later materialization is picked up.\n\t\tconst work = readExistingManifest(\n\t\t\tjoin(extractCacheDir!, outer, \"index.json\"),\n\t\t\touter,\n\t\t).catch(() => undefined)\n\t\tmanifestMemo.set(key, work)\n\t\tconst entries = await work\n\t\tif (entries === undefined) manifestMemo.delete(key)\n\t\treturn entries\n\t}\n\n\ttype MaterializedEntry = {\n\t\treadonly kind: \"materialized\"\n\t\treadonly absPath: string\n\t\treadonly sizeBytes: number\n\t}\n\n\t/**\n\t * Resolve `outer!inner` against the extraction cache: the inner path\n\t * must appear in the outer's manifest (the manifest paths were\n\t * sanitized at extraction time, so this is a whitelist — no\n\t * traversal can reach outside the cache dir).\n\t */\n\tasync function resolveMaterialized(\n\t\trel: string,\n\t): Promise<MaterializedEntry | undefined> {\n\t\tif (extractCacheDir === undefined) return undefined\n\t\tconst parts = splitVirtualPath(rel)\n\t\tif (parts === undefined) return undefined\n\t\tconst { outer, inner } = parts\n\t\tconst manifest = await readManifest(outer)\n\t\tconst entry = manifest?.find((e) => e.path === inner)\n\t\tif (entry === undefined) return undefined\n\t\treturn {\n\t\t\tkind: \"materialized\",\n\t\t\tabsPath: join(extractCacheDir, outer, inner),\n\t\t\tsizeBytes: entry.sizeBytes,\n\t\t}\n\t}\n\n\tasync function resolve(rel: string) {\n\t\tconst nested = await resolver.resolve(rel)\n\t\tif (nested.kind === \"container\") return nested\n\t\tconst materialized = await resolveMaterialized(rel)\n\t\treturn materialized ?? nested\n\t}\n\n\tasync function readEntrySlice(\n\t\trel: string,\n\t\tstart: number,\n\t\tend: number,\n\t): Promise<Buffer> {\n\t\tconst resolved = await resolve(rel)\n\t\tif (resolved.kind === \"literal\") {\n\t\t\treturn base.readEntrySlice(rel, start, end)\n\t\t}\n\t\tif (resolved.kind === \"materialized\") {\n\t\t\t// readEntrySlice's `end` is exclusive; readFileRange's is\n\t\t\t// inclusive.\n\t\t\treturn readFileRange(resolved.absPath, start, end - 1)\n\t\t}\n\t\t// Slice the decompressed bytes: consume the stream only up to\n\t\t// `end` so head reads (sniff/probe windows) stay bounded.\n\t\treturn readVirtualSlice(() => resolved.openStream(), start, end)\n\t}\n\n\tasync function openEntryStream(rel: string): Promise<{\n\t\treadonly stream: Readable\n\t\treadonly size: number\n\t\treadonly mtimeMs?: number\n\t\treadonly path?: string\n\t}> {\n\t\tconst resolved = await resolve(rel)\n\t\tif (resolved.kind === \"literal\") {\n\t\t\treturn base.openEntryStream(rel)\n\t\t}\n\t\tif (resolved.kind === \"materialized\") {\n\t\t\treturn {\n\t\t\t\tstream: createReadStream(resolved.absPath),\n\t\t\t\tsize: resolved.sizeBytes,\n\t\t\t\tpath: resolved.absPath,\n\t\t\t}\n\t\t}\n\t\treturn { stream: resolved.openStream(), size: resolved.entry.sizeBytes }\n\t}\n\n\tasync function readEntry(rel: string): Promise<Buffer> {\n\t\tconst resolved = await resolve(rel)\n\t\tif (resolved.kind === \"literal\") {\n\t\t\treturn base.readEntry(rel)\n\t\t}\n\t\tif (resolved.kind === \"materialized\") {\n\t\t\treturn readFile(resolved.absPath)\n\t\t}\n\t\treturn buffer(resolved.openStream())\n\t}\n\n\tasync function resolveByteRange(\n\t\trel: string,\n\t): Promise<{ readonly size: number } | undefined> {\n\t\tconst resolved = await resolve(rel)\n\t\tif (resolved.kind === \"literal\") {\n\t\t\treturn base.resolveByteRange(rel)\n\t\t}\n\t\tif (resolved.kind === \"materialized\") {\n\t\t\treturn { size: resolved.sizeBytes }\n\t\t}\n\t\treturn { size: resolved.entry.sizeBytes }\n\t}\n\n\tasync function resolveSeekablePath(rel: string): Promise<string | undefined> {\n\t\tconst resolved = await resolve(rel)\n\t\tif (resolved.kind === \"literal\") {\n\t\t\treturn base.resolveSeekablePath?.(rel)\n\t\t}\n\t\tif (resolved.kind === \"materialized\") {\n\t\t\t// Extracted files are real on-disk entries — libvips/ffmpeg\n\t\t\t// can mmap/open them directly instead of streaming.\n\t\t\treturn resolved.absPath\n\t\t}\n\t\treturn undefined\n\t}\n\n\treturn {\n\t\tlistEntries: base.listEntries,\n\t\treadEntry,\n\t\treadEntrySlice,\n\t\topenEntryStream,\n\t\tresolveByteRange,\n\t\tresolveSeekablePath,\n\t}\n}\n\n/**\n * Read `[start, end)` of an entry stream without buffering more than\n * `end` bytes. STORED and DEFLATE entries alike stream through this\n * path, so a truncated header slice never inflates the whole entry.\n */\nasync function readVirtualSlice(\n\topenStream: () => Readable,\n\tstart: number,\n\tend: number,\n): Promise<Buffer> {\n\tif (end <= start) return Buffer.alloc(0)\n\tconst chunks: Buffer[] = []\n\tlet collected = 0\n\tawait new Promise<void>((resolveDone, rejectDone) => {\n\t\tconst stream = openStream()\n\t\tstream.on(\"data\", (chunk: Buffer | string) => {\n\t\t\tconst bytes = typeof chunk === \"string\" ? Buffer.from(chunk) : chunk\n\t\t\tconst need = end - collected\n\t\t\tif (need <= 0) {\n\t\t\t\tstream.destroy()\n\t\t\t\tresolveDone()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst take = bytes.subarray(0, Math.min(need, bytes.length))\n\t\t\tchunks.push(take)\n\t\t\tcollected += take.length\n\t\t\tif (collected >= end) {\n\t\t\t\tstream.destroy()\n\t\t\t\tresolveDone()\n\t\t\t}\n\t\t})\n\t\tstream.on(\"end\", () => resolveDone())\n\t\tstream.on(\"error\", (err) => rejectDone(err))\n\t})\n\tconst full = Buffer.concat(chunks, collected)\n\tconst sliceStart = Math.min(start, full.length)\n\treturn full.subarray(sliceStart)\n}\n\n/** Resolver instance type re-export for callers that build their own views. */\nexport type { NestedResolver }\n","import { extname } from \"node:path\"\nimport type { Readable } from \"node:stream\"\nimport { IMAGE_EXTS } from \"@hoardodile/sdk-types/media-exts\"\nimport { PROBE_HEADER_BYTES } from \"../archive/index.ts\"\n\nimport type { ImageInfo } from \"../types.ts\"\n\n/**\n * Max zip entry size read into memory for thumb synthesis. Larger\n * entries are materialized to disk before sharp/ffmpeg runs.\n */\nexport const THUMB_BUFFER_MAX_BYTES = 32 * 1024 * 1024\n\n/**\n * Image probing via sharp. sharp is loaded lazily and declared as an\n * optional peer dependency: the host package stays free of native\n * binaries, and consumers that never probe images (the browser-side mock,\n * dependency-light CI installs) never pull it in. When sharp is missing,\n * probe calls fail loudly instead of degrading silently.\n */\ntype SharpModule = typeof import(\"sharp\")\n\nlet sharpPromise: Promise<SharpModule> | undefined\n\nexport function loadSharp(): Promise<SharpModule> {\n\tsharpPromise ??= import(\"sharp\").catch((err: unknown) => {\n\t\tsharpPromise = undefined\n\t\tthrow new Error(\n\t\t\t`sharp is not installed — add \"sharp\" to your dependencies to enable image probing: ${err instanceof Error ? err.message : String(err)}`,\n\t\t)\n\t})\n\treturn sharpPromise\n}\n\nexport type ImageSourceProbe = {\n\treadonly width: number\n\treadonly height: number\n\treadonly animated: boolean\n}\n\nconst ANIMATED_EXT_HINTS = new Set([\".gif\", \".webp\", \".avif\"])\n\nexport { PROBE_HEADER_BYTES } from \"../archive/index.ts\"\n\n/** True when `ext` names a format that can carry animation. */\nexport function isAnimatedCandidateExt(ext: string): boolean {\n\treturn ANIMATED_EXT_HINTS.has(ext)\n}\n\nexport type ImageMetadataInput =\n\t| string\n\t| Buffer\n\t| Readable\n\t| {\n\t\t\treadonly openStream: () => Promise<Readable>\n\t\t\t/**\n\t\t\t * Optional byte-range read into the underlying source (e.g. a\n\t\t\t * zip entry slice). When present, metadata probes read\n\t\t\t * {@link PROBE_HEADER_BYTES} from the start instead of\n\t\t\t * streaming the whole entry — libvips reads the ENTIRE file\n\t\t\t * for metadata from a non-seekable stream, but only the header\n\t\t\t * from a buffer.\n\t\t\t */\n\t\t\treadonly readRange?: (start: number, end: number) => Promise<Buffer>\n\t }\n\n/**\n * Narrow a Node stream into sharp's readable input without assertion.\n * Async because the sharp module itself is loaded lazily.\n *\n * sharp's constructor rejects `sharp(stream, opts)` — for stream input\n * the input options must be passed AS the first argument (a plain object\n * of input parameters like `pages`/`animated`) and the readable piped\n * into the resulting Duplex.\n */\nexport async function sharpFromReadable(\n\tstream: Readable,\n\toptions?: import(\"sharp\").SharpOptions,\n): Promise<import(\"sharp\").Sharp> {\n\tif (typeof stream.read !== \"function\") {\n\t\tthrow new Error(\"expected a readable stream\")\n\t}\n\tconst { default: sharp } = await loadSharp()\n\tconst descriptor =\n\t\toptions === undefined || Object.keys(options).length === 0\n\t\t\t? undefined\n\t\t\t: ({ ...options } as unknown as import(\"sharp\").SharpInput)\n\tconst instance = descriptor === undefined ? sharp() : sharp(descriptor)\n\tstream.pipe(instance)\n\treturn instance\n}\n\nfunction isReadable(input: unknown): input is Readable {\n\treturn (\n\t\ttypeof input === \"object\" &&\n\t\tinput !== null &&\n\t\ttypeof (input as Readable).pipe === \"function\" &&\n\t\t!(\"openStream\" in input)\n\t)\n}\n\nfunction isReopenableImageStream(\n\tinput: ImageMetadataInput,\n): input is { readonly openStream: () => Promise<Readable> } {\n\treturn (\n\t\ttypeof input === \"object\" &&\n\t\tinput !== null &&\n\t\t!Buffer.isBuffer(input) &&\n\t\t\"openStream\" in input &&\n\t\ttypeof (input as { openStream: unknown }).openStream === \"function\"\n\t)\n}\n\n/**\n * Sharp options for image thumb/probe reads. Enables sequential read for\n * large JPEGs on disk so libvips can shrink-on-load during resize.\n */\nexport function sharpImageInputOpts(\n\tinput: string | Buffer,\n\text: string,\n\tpages: number,\n\tanimated?: boolean,\n): import(\"sharp\").SharpOptions {\n\tconst opts: import(\"sharp\").SharpOptions = { pages }\n\tif (animated === true) opts.animated = true\n\tif (\n\t\ttypeof input === \"string\" &&\n\t\t(ext === \".jpg\" || ext === \".jpeg\" || ext === \".jfif\")\n\t) {\n\t\topts.sequentialRead = true\n\t}\n\treturn opts\n}\n\n/**\n * True when a shallow `{ pages: 1 }` metadata read is not enough to\n * decide animation and the full multi-page scan is required.\n */\nexport function needsFullAnimationScan(\n\tmeta: Pick<\n\t\timport(\"sharp\").Metadata,\n\t\t\"width\" | \"height\" | \"pages\" | \"pageHeight\"\n\t>,\n\text: string,\n): boolean {\n\tif (ext === \".gif\") return true\n\tif (ANIMATED_EXT_HINTS.has(ext) && (meta.pages ?? 1) > 1) return true\n\tif (\n\t\tmeta.pageHeight !== undefined &&\n\t\tmeta.height !== undefined &&\n\t\tmeta.pageHeight !== meta.height\n\t) {\n\t\treturn true\n\t}\n\treturn false\n}\n\n/**\n * Buffer a probe stream with a hard byte cap. Only used for formats whose\n * animation scan needs the whole stream (GIF): probing only needs\n * metadata, so an entry bigger than the cap is treated as unprobed rather\n * than buffered whole into host memory.\n */\nasync function bufferCapped(\n\tinput: Readable,\n\tmaxBytes: number,\n): Promise<Buffer> {\n\tconst chunks: Uint8Array[] = []\n\tlet total = 0\n\tfor await (const chunk of input) {\n\t\tconst bytes: Uint8Array =\n\t\t\ttypeof chunk === \"string\" ? Buffer.from(chunk) : chunk\n\t\ttotal += bytes.byteLength\n\t\tif (total > maxBytes) {\n\t\t\tinput.destroy()\n\t\t\tthrow new Error(`probe input exceeds the ${maxBytes}-byte cap`)\n\t\t}\n\t\tchunks.push(bytes)\n\t}\n\treturn Buffer.concat(chunks)\n}\n\n/**\n * Read image metadata with layered animation detection: static sources\n * stop after a single-page read; animated containers escalate to\n * `{ pages: -1 }` only when the shallow probe signals multi-frame input.\n */\nexport async function readImageMetadata(\n\tinput: ImageMetadataInput,\n\text: string,\n): Promise<{\n\treadonly meta: import(\"sharp\").Metadata\n\treadonly animated: boolean\n}> {\n\tif (isReopenableImageStream(input)) {\n\t\t// Header-slice fast path: probe a small leading window as a buffer\n\t\t// (header-only libvips read) instead of streaming the whole entry\n\t\t// (full-file read). Only definitive static results are accepted —\n\t\t// animation candidates escalate inside the slice read, and a\n\t\t// truncated buffer cannot complete a full scan, so those fall\n\t\t// through to the stream path where frame counts stay correct. GIF\n\t\t// is skipped outright (its frame count lives at the end).\n\t\tif (input.readRange !== undefined && ext !== \".gif\") {\n\t\t\tconst head = await input.readRange(0, PROBE_HEADER_BYTES)\n\t\t\tif (head.length > 0) {\n\t\t\t\ttry {\n\t\t\t\t\tconst { default: sharp } = await loadSharp()\n\t\t\t\t\tconst shallowMeta = await sharp(\n\t\t\t\t\t\thead,\n\t\t\t\t\t\tsharpImageInputOpts(head, ext, 1),\n\t\t\t\t\t).metadata()\n\t\t\t\t\tif (!needsFullAnimationScan(shallowMeta, ext)) {\n\t\t\t\t\t\treturn { meta: shallowMeta, animated: false }\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Truncated header unreadable — fall through to the\n\t\t\t\t\t// stream path.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst stream = await input.openStream()\n\t\tconst shallow = await sharpFromReadable(stream, { pages: 1 })\n\t\tconst shallowMeta = await shallow.metadata()\n\t\tif (!needsFullAnimationScan(shallowMeta, ext)) {\n\t\t\treturn { meta: shallowMeta, animated: false }\n\t\t}\n\t\tconst fullStream = await input.openStream()\n\t\tconst fullMeta = await sharpFromReadable(fullStream, {\n\t\t\tpages: -1,\n\t\t\tanimated: true,\n\t\t}).then((s) => s.metadata())\n\t\treturn { meta: fullMeta, animated: (fullMeta.pages ?? 1) > 1 }\n\t}\n\tif (isReadable(input)) {\n\t\t// GIF frame counts are not stored in the container header, so the\n\t\t// full animation scan needs the whole stream — buffer cap-guarded\n\t\t// and reuse the buffer path.\n\t\tif (ext === \".gif\") {\n\t\t\tconst data = await bufferCapped(input, THUMB_BUFFER_MAX_BYTES)\n\t\t\treturn readImageMetadata(data, ext)\n\t\t}\n\t\t// Static formats: hand the stream straight to sharp so the probe\n\t\t// reads only the header bytes. (Buffering the whole entry first —\n\t\t// the previous behavior, capped at THUMB_BUFFER_MAX_BYTES — turned\n\t\t// every stream probe into a full read and made entries beyond the\n\t\t// cap unprobeable.)\n\t\ttry {\n\t\t\tconst shallow = await sharpFromReadable(input, { pages: 1 })\n\t\t\tconst shallowMeta = await shallow.metadata()\n\t\t\tif (!needsFullAnimationScan(shallowMeta, ext)) {\n\t\t\t\treturn { meta: shallowMeta, animated: false }\n\t\t\t}\n\t\t\t// Animated candidates (webp/avif/tiff) report their total page\n\t\t\t// count from the container header, so the shallow read is\n\t\t\t// conclusive — and the stream cannot be rewound for a full\n\t\t\t// scan anyway.\n\t\t\treturn {\n\t\t\t\tmeta: shallowMeta,\n\t\t\t\tanimated: (shallowMeta.pages ?? 1) > 1,\n\t\t\t}\n\t\t} finally {\n\t\t\tinput.destroy()\n\t\t}\n\t}\n\tconst { default: sharp } = await loadSharp()\n\tconst shallow = sharp(input, sharpImageInputOpts(input, ext, 1))\n\tconst shallowMeta = await shallow.metadata()\n\tif (!needsFullAnimationScan(shallowMeta, ext)) {\n\t\treturn { meta: shallowMeta, animated: false }\n\t}\n\tconst fullMeta = await sharp(\n\t\tinput,\n\t\tsharpImageInputOpts(input, ext, -1, true),\n\t).metadata()\n\treturn { meta: fullMeta, animated: (fullMeta.pages ?? 1) > 1 }\n}\n\n/**\n * Probe an image path or buffer and return pixel dimensions plus whether\n * the source is animated. Probe failures return `undefined`.\n */\nexport async function probeImageSource(\n\tinput: ImageMetadataInput,\n\textHint?: string,\n): Promise<ImageSourceProbe | undefined> {\n\tconst ext =\n\t\textHint ?? (typeof input === \"string\" ? extname(input).toLowerCase() : \"\")\n\tif (ext.length > 0 && !IMAGE_EXTS.has(ext)) return undefined\n\ttry {\n\t\tconst { meta, animated } = await readImageMetadata(input, ext)\n\t\tconst h = meta.pageHeight ?? meta.height\n\t\tif (meta.width === undefined || h === undefined) return undefined\n\t\treturn { width: meta.width, height: h, animated }\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Probe an image source and return its pixel dimensions. Probe failures\n * return `undefined` so callers can treat \"not yet probed\" and \"probe\n * failed\" the same way.\n */\nexport async function probeImage(\n\tsource: ImageMetadataInput,\n): Promise<ImageInfo | undefined> {\n\tconst probe = await probeImageSource(source)\n\tif (probe === undefined) return undefined\n\treturn { width: probe.width, height: probe.height }\n}\n\n/**\n * True when the image has more than one frame (animated GIF / WebP / APNG /\n * AVIF). Uses layered detection so static JPEG/PNG avoid a full-frame scan.\n * Errors are coerced to `false`.\n */\nexport async function probeAnimatedImage(\n\tsource: ImageMetadataInput,\n): Promise<boolean> {\n\tif (\n\t\ttypeof source === \"string\" &&\n\t\t!IMAGE_EXTS.has(extname(source).toLowerCase())\n\t) {\n\t\treturn false\n\t}\n\ttry {\n\t\tconst probe = await probeImageSource(source)\n\t\treturn probe?.animated ?? false\n\t} catch (err) {\n\t\tconst label = typeof source === \"string\" ? source : \"stream\"\n\t\tconsole.warn(\n\t\t\t`[probeAnimatedImage] sharp failed on ${label}: ${err instanceof Error ? err.message : String(err)}`,\n\t\t)\n\t\treturn false\n\t}\n}\n","import { fileTypeFromName } from \"@hoardodile/sdk-types\"\nimport { mimeToKind } from \"@hoardodile/sdk-types/media-exts\"\n\nimport type { FileType } from \"../types.ts\"\n\n/**\n * Content sniffing: what a file's bytes say it is, with the filename as\n * the fallback rather than the verdict.\n *\n * The layering mirrors what the ecosystem settled on (freedesktop's\n * shared-mime-info, Apache Tika, WHATWG mimesniff): magic-byte matching\n * decides for binary formats, and the extension only answers for\n * formats that carry no signature at all — text, subtitles, CSV. That\n * split is why a `.jpg` holding WebP bytes probes as WebP, while a\n * `.txt` still resolves to `text/plain`.\n */\n\n/**\n * Header window handed to the magic matcher. `file-type` documents 4100\n * bytes as the sample size that makes its detection deterministic; the\n * host reads exactly that much and no more, so sniffing an entry costs\n * one small ranged read.\n */\nexport const SNIFF_HEADER_BYTES = 4100\n\ntype FileTypeModule = typeof import(\"file-type\")\n\nlet fileTypePromise: Promise<FileTypeModule> | undefined\n\n/**\n * Load the magic-byte matcher lazily. Sniffing is opt-in per host (the\n * directory backend never sniffs), so an unused host pays nothing for\n * the module graph.\n */\nfunction loadFileType(): Promise<FileTypeModule> {\n\tfileTypePromise ??= import(\"file-type\")\n\treturn fileTypePromise\n}\n\n/**\n * Identify a file from a leading byte window plus its name. `head`\n * should hold the first {@link SNIFF_HEADER_BYTES} bytes; a shorter\n * slice still works for formats whose signature fits inside it.\n */\nexport async function sniffBytes(\n\thead: Uint8Array,\n\tpath: string,\n): Promise<FileType | undefined> {\n\tif (head.byteLength > 0) {\n\t\ttry {\n\t\t\tconst { fileTypeFromBuffer } = await loadFileType()\n\t\t\tconst detected = await fileTypeFromBuffer(head)\n\t\t\tif (detected !== undefined) {\n\t\t\t\treturn {\n\t\t\t\t\tmime: detected.mime,\n\t\t\t\t\text: `.${detected.ext}`,\n\t\t\t\t\tkind: mimeToKind(detected.mime),\n\t\t\t\t\tsource: \"magic\",\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// A malformed or truncated header is not an error condition —\n\t\t\t// fall through to the filename, same as a signature-less file.\n\t\t}\n\t}\n\treturn fileTypeFromName(path)\n}\n","import { readFile, rename, rm, writeFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\n\n/**\n * Explicit file order for a resource's content root (`data/`). Uploads\n * install entries in the user's chosen order and persist that sequence\n * in this manifest so the display order matches the upload order even\n * when filenames sort differently. Written at commit time; because it\n * lives inside the replaced `data/` subtree it is always consistent\n * with the entries around it.\n *\n * The manifest is a JSON array of relative entry names (`/`-separated),\n * listing the final installed names (after collision suffixing). Reads\n * are strict: a malformed manifest, or one whose entries do not all\n * resolve against the actual listing, is ignored entirely and callers\n * fall back to the natural name sort. Strictness matters because\n * `createDirectoryContainer` also serves arbitrary user directories\n * (folder import, plugin dev) that may coincidentally contain a file\n * named `.order`.\n */\nexport const ORDER_MANIFEST_NAME = \".order\"\n\n/** Path of the order manifest inside a resource content root. */\nexport function orderManifestPath(dataDir: string): string {\n\treturn join(dataDir, ORDER_MANIFEST_NAME)\n}\n\n/**\n * Atomically persist `names` as the manifest of `dataDir` (tmp + rename,\n * so a concurrent reader never observes a half-written file).\n */\nexport async function writeOrderManifest(\n\tdataDir: string,\n\tnames: readonly string[],\n): Promise<void> {\n\tconst dest = orderManifestPath(dataDir)\n\tconst tmp = `${dest}.writing-${process.pid}-${Date.now()}`\n\ttry {\n\t\tawait writeFile(tmp, JSON.stringify(names), \"utf8\")\n\t\tawait rename(tmp, dest)\n\t} catch (err) {\n\t\tawait rm(tmp, { force: true }).catch(() => {})\n\t\tthrow err\n\t}\n}\n\n/**\n * Read the manifest of `dataDir`. Resolves to `undefined` when the\n * manifest is absent or fails structural validation\n * ({@link parseOrderManifest}) — callers fall back to natural order.\n */\nexport async function readOrderManifest(\n\tdataDir: string,\n): Promise<readonly string[] | undefined> {\n\tlet raw: string\n\ttry {\n\t\traw = await readFile(orderManifestPath(dataDir), \"utf8\")\n\t} catch {\n\t\treturn undefined\n\t}\n\treturn parseOrderManifest(raw)\n}\n\n/**\n * Structural validation of a raw manifest body: a JSON array of\n * non-empty relative paths with `/` separators that cannot escape the\n * content root. Returns `undefined` when the body is not a usable\n * manifest. Semantic validation (entries exist in the listing) happens\n * in {@link orderEntries}.\n */\nexport function parseOrderManifest(raw: string): readonly string[] | undefined {\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\treturn undefined\n\t}\n\tif (!Array.isArray(parsed) || parsed.length === 0) return undefined\n\tconst names: string[] = []\n\tfor (const entry of parsed) {\n\t\tif (typeof entry !== \"string\") return undefined\n\t\tif (!isManifestPathSafe(entry)) return undefined\n\t\tnames.push(entry)\n\t}\n\tif (names.length === 0) return undefined\n\treturn names\n}\n\n/**\n * Reorder `listed` by the manifest: entries named by the manifest come\n * first, in manifest order; entries the manifest does not mention are\n * appended in natural order. Returns `undefined` when the manifest\n * cannot be applied against this listing (some manifest entry is\n * missing — the strict trust rule), so the caller falls back to the\n * natural sort.\n */\nexport function orderEntries(\n\tmanifest: readonly string[],\n\tlisted: readonly string[],\n): readonly string[] | undefined {\n\tconst available = new Set(listed)\n\tconst uniqueManifest = new Set<string>()\n\tfor (const name of manifest) {\n\t\tif (uniqueManifest.has(name)) return undefined\n\t\tuniqueManifest.add(name)\n\t\tif (!available.has(name)) return undefined\n\t}\n\tconst ordered = manifest.filter((name) => available.has(name))\n\tconst rest = naturalSort(listed.filter((name) => !uniqueManifest.has(name)))\n\treturn [...ordered, ...rest]\n}\n\n/** Case-insensitive natural name sort (`1 < 2 < 10`, `a < b`). */\nexport function naturalSort(names: readonly string[]): readonly string[] {\n\treturn [...names].sort((a, b) =>\n\t\ta.localeCompare(b, undefined, { sensitivity: \"base\", numeric: true }),\n\t)\n}\n\nfunction isManifestPathSafe(entry: string): boolean {\n\tif (entry.length === 0 || entry.includes(\"\\0\") || entry.includes(\"\\\\\")) {\n\t\treturn false\n\t}\n\tconst segments = entry.split(\"/\")\n\tfor (const segment of segments) {\n\t\tif (segment.length === 0 || segment === \".\" || segment === \"..\") {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n","import { createReadStream } from \"node:fs\"\nimport { open, readdir, stat } from \"node:fs/promises\"\nimport { isAbsolute, join, normalize, resolve, sep } from \"node:path\"\nimport type { Readable } from \"node:stream\"\nimport type { ResourceContainer } from \"./container.ts\"\nimport {\n\tnaturalSort,\n\torderEntries,\n\treadOrderManifest,\n} from \"./hoard/order-manifest.ts\"\n\n/**\n * A {@link ResourceContainer} over a raw filesystem directory. Used to\n * run plugin hooks against a plain directory — during import, before\n * resources exist, or from the CLI. Reads only; the directory is treated\n * as an immutable snapshot for the container's lifetime.\n */\nexport function createDirectoryContainer(dir: string): ResourceContainer {\n\tasync function resolvePath(relPath: string): Promise<string> {\n\t\treturn resolveSafeImportPath(dir, relPath)\n\t}\n\n\treturn {\n\t\tasync listEntries(): Promise<readonly string[]> {\n\t\t\tconst out: string[] = []\n\t\t\tasync function collect(current: string, prefix: string): Promise<void> {\n\t\t\t\tconst entries = await readdir(join(dir, current), {\n\t\t\t\t\twithFileTypes: true,\n\t\t\t\t}).catch(() => [] as readonly never[])\n\t\t\t\tfor (const e of entries) {\n\t\t\t\t\tif (e.name.startsWith(\".\")) continue\n\t\t\t\t\tif (e.name.includes(\".uploading-\")) continue\n\t\t\t\t\t// Entry names always use `/` separators, mirroring zip\n\t\t\t\t\t// entry names, regardless of the platform's separator.\n\t\t\t\t\tconst rel = prefix ? `${prefix}/${e.name}` : e.name\n\t\t\t\t\tif (e.isDirectory()) {\n\t\t\t\t\t\tawait collect(join(current, e.name), rel)\n\t\t\t\t\t} else if (e.isFile()) {\n\t\t\t\t\t\tout.push(rel)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait collect(\".\", \"\")\n\t\t\t// An explicit order manifest wins when it validates against\n\t\t\t// this listing; otherwise fall back to the natural name sort.\n\t\t\tconst manifest = await readOrderManifest(dir)\n\t\t\tif (manifest !== undefined) {\n\t\t\t\tconst ordered = orderEntries(manifest, out)\n\t\t\t\tif (ordered !== undefined) return ordered\n\t\t\t}\n\t\t\treturn naturalSort(out)\n\t\t},\n\n\t\tasync readEntry(relPath: string): Promise<Buffer> {\n\t\t\tconst safe = await resolvePath(relPath)\n\t\t\tconst handle = await open(safe, \"r\")\n\t\t\ttry {\n\t\t\t\treturn await handle.readFile()\n\t\t\t} finally {\n\t\t\t\tawait handle.close()\n\t\t\t}\n\t\t},\n\n\t\tasync readEntrySlice(\n\t\t\trelPath: string,\n\t\t\tstart: number,\n\t\t\tend: number,\n\t\t): Promise<Buffer> {\n\t\t\tconst safe = await resolvePath(relPath)\n\t\t\tconst handle = await open(safe, \"r\")\n\t\t\ttry {\n\t\t\t\tconst { size } = await handle.stat()\n\t\t\t\tconst clampedStart = Math.min(Math.max(0, start), size)\n\t\t\t\tconst clampedEnd = Math.min(Math.max(clampedStart, end), size)\n\t\t\t\tconst length = Math.max(0, clampedEnd - clampedStart)\n\t\t\t\tif (length === 0) return Buffer.alloc(0)\n\t\t\t\tconst buf = Buffer.alloc(length)\n\t\t\t\tawait handle.read(buf, 0, length, clampedStart)\n\t\t\t\treturn buf\n\t\t\t} finally {\n\t\t\t\tawait handle.close()\n\t\t\t}\n\t\t},\n\n\t\tasync openEntryStream(relPath: string): Promise<{\n\t\t\treadonly stream: Readable\n\t\t\treadonly size: number\n\t\t\treadonly mtimeMs?: number\n\t\t\treadonly path?: string\n\t\t}> {\n\t\t\tconst safe = await resolvePath(relPath)\n\t\t\tconst info = await stat(safe)\n\t\t\treturn {\n\t\t\t\tstream: createReadStream(safe),\n\t\t\t\tsize: info.size,\n\t\t\t\tmtimeMs: info.mtimeMs,\n\t\t\t\tpath: safe,\n\t\t\t}\n\t\t},\n\n\t\tasync resolveByteRange(\n\t\t\trelPath: string,\n\t\t): Promise<{ readonly size: number } | undefined> {\n\t\t\tconst safe = await resolvePath(relPath)\n\t\t\tconst info = await stat(safe).catch(() => undefined)\n\t\t\treturn info === undefined ? undefined : { size: info.size }\n\t\t},\n\n\t\tasync resolveSeekablePath(relPath: string): Promise<string | undefined> {\n\t\t\tconst safe = await resolvePath(relPath)\n\t\t\tconst info = await stat(safe).catch(() => undefined)\n\t\t\treturn info?.isFile() === true ? safe : undefined\n\t\t},\n\t}\n}\n\n/**\n * Resolve a plugin-supplied relative path against an import directory,\n * rejecting attempts to escape the directory or use absolute paths.\n */\nexport function resolveSafeImportPath(dir: string, relPath: string): string {\n\tif (relPath.length === 0) {\n\t\tthrow new Error(\"path is empty\")\n\t}\n\tif (relPath.includes(\"\\0\")) {\n\t\tthrow new Error(\"path contains null byte\")\n\t}\n\tif (isAbsolute(relPath)) {\n\t\tthrow new Error(\"absolute paths are not allowed\")\n\t}\n\tconst normalized = normalize(relPath)\n\tif (normalized.startsWith(\"..\") || normalized === \"..\") {\n\t\tthrow new Error(\"path escapes import directory\")\n\t}\n\tconst root = resolve(dir)\n\tconst candidate = resolve(root, normalized)\n\tif (candidate !== root && !candidate.startsWith(root + sep)) {\n\t\tthrow new Error(\"path escapes import directory\")\n\t}\n\treturn candidate\n}\n","import type { PluginSchema } from \"@hoardodile/sdk-types\"\nimport { PLUGIN_READ_FILE_MAX_BYTES } from \"@hoardodile/sdk-types/plugin\"\nimport { createPluginResourceAPI } from \"./api.ts\"\nimport { createDirectoryContainer } from \"./directory-container.ts\"\nimport type { ResourceAPI } from \"./types.ts\"\n\n/**\n * Create a minimal {@link ResourceAPI} backed by a raw filesystem\n * directory. Used to run plugin hooks against a plain directory — during\n * import, before resources exist, or from the CLI. Built on the shared\n * {@link createPluginResourceAPI} so directory behavior matches the\n * archive-backed and fixture backends; probes are unsupported and resolve\n * to `undefined`.\n */\nexport function createDirectoryResourceAPI<\n\tTSchema extends PluginSchema = PluginSchema,\n>(\n\tdir: string,\n\topts: {\n\t\treadonly maxReadFileBytes?: number\n\t\t/**\n\t\t * Writable directory for `extractArchive` materialization (the\n\t\t * CLI passes a temp dir). Virtual-path reads (`outer!inner`) work\n\t\t * without it; only the materializing hook needs it.\n\t\t */\n\t\treadonly extractCacheDir?: string\n\t\t/**\n\t\t * Session context handed to hooks as `api.context.detect` (see\n\t\t * `CreatePluginResourceAPIDeps.detectContext`).\n\t\t */\n\t\treadonly detectContext?: unknown\n\t} = {},\n): ResourceAPI<TSchema> {\n\treturn createPluginResourceAPI<TSchema>({\n\t\tview: createDirectoryContainer(dir),\n\t\tmaxReadFileBytes: opts.maxReadFileBytes ?? PLUGIN_READ_FILE_MAX_BYTES,\n\t\textractCacheDir: opts.extractCacheDir,\n\t\tdetectContext: opts.detectContext,\n\t})\n}\n\nexport { resolveSafeImportPath } from \"./directory-container.ts\"\n","import type { Readable } from \"node:stream\"\nimport type {\n\tContainerListing,\n\tImageHashKind,\n\tPluginSchema,\n} from \"@hoardodile/sdk-types\"\nimport { fileTypeFromName, pluginAssetError } from \"@hoardodile/sdk-types\"\nimport { MIME_FFMPEG_INPUT_FORMAT } from \"@hoardodile/sdk-types/media-exts\"\nimport { PLUGIN_READ_FILE_MAX_BYTES } from \"@hoardodile/sdk-types/plugin\"\nimport type { ArchiveEntry, NestedCdCache } from \"./archive/index.ts\"\nimport {\n\ttype ArchiveExtractor,\n\tcreateArchiveExtractor,\n\ttype ExtractionResult,\n\ttype ExtractProgress,\n} from \"./archive/index.ts\"\nimport type { ResourceContainer } from \"./container.ts\"\nimport {\n\tcomputeDHash,\n\tcomputePHash,\n\tgrayStddev,\n\thashStream,\n\tMIN_PERCEPTUAL_STDDEV,\n\tPERCEPTUAL_HASH_KINDS,\n\tPHASH_GRID,\n} from \"./hash.ts\"\nimport { createNestedAwareContainer } from \"./nested-view.ts\"\nimport type { AvProbeOptions } from \"./probe/av.ts\"\nimport {\n\ttype ImageMetadataInput,\n\ttype ImageSourceProbe,\n\tisAnimatedCandidateExt,\n\tPROBE_HEADER_BYTES,\n\tsharpFromReadable,\n} from \"./probe/image.ts\"\nimport { SNIFF_HEADER_BYTES, sniffBytes } from \"./probe/sniff.ts\"\nimport type { PluginProbeCache } from \"./probe-cache.ts\"\nimport type {\n\tFileType,\n\tProbeResult,\n\tReadFileRange,\n\tResourceAPI,\n} from \"./types.ts\"\n\n// The directory-backed ResourceAPI is part of the host so the CLI and the\n// contract suite run the same implementation the server's import path uses.\nexport {\n\tcreateDirectoryResourceAPI as createImportResourceAPI,\n\tresolveSafeImportPath,\n} from \"./directory-api.ts\"\n\n/**\n * Construct a {@link ResourceAPI} on top of a {@link ResourceContainer}.\n * The container abstracts away the storage shape (bare-file resource\n * folder, raw directory, in-memory fixture) so plugin code stays\n * unaware of how source bytes are stored.\n */\nexport type CreatePluginResourceAPIDeps = {\n\treadonly view: ResourceContainer\n\t/**\n\t * Probe implementations, both optional: when absent the matching\n\t * branch of `probe` resolves to\n\t * `{ kind: \"unknown\", reason: \"unavailable\" }` without opening any\n\t * stream — the directory backend ships without probes by design.\n\t * `sniff` never needs a backend; it reads the file's own header.\n\t */\n\treadonly probeImage?: (\n\t\tsource: ImageMetadataInput,\n\t\textHint?: string,\n\t) => Promise<ImageSourceProbe | undefined>\n\treadonly probeAv?: (\n\t\tsource: string | Readable,\n\t\topts: AvProbeOptions,\n\t) => Promise<ProbeResult>\n\t/** Per-call `readFile` byte cap. Defaults to {@link PLUGIN_READ_FILE_MAX_BYTES}. */\n\treadonly maxReadFileBytes?: number\n\t/**\n\t * Process-wide probe cache. Only active together with\n\t * {@link cacheScope}; without it every probe opens a fresh stream.\n\t */\n\treadonly probeCache?: PluginProbeCache\n\t/**\n\t * Cache namespace for this API instance, typically\n\t * `${resId}:${fileVersion}`. Archives are immutable per version, so\n\t * cached probe results never need explicit invalidation.\n\t */\n\treadonly cacheScope?: string\n\t/**\n\t * Absolute directory for plugin container extractions (e.g. the\n\t * server's `local/cache/resources/<id>/extracted/v<N>/archives`, or a\n\t * temp dir for the CLI). When absent, `extractArchive` rejects with\n\t * a clear message.\n\t */\n\treadonly extractCacheDir?: string\n\t/**\n\t * Hard caps for one `extractArchive` call. Defaults to\n\t * {@link DEFAULT_PLUGIN_EXTRACT_MAX_BYTES} /\n\t * {@link DEFAULT_PLUGIN_EXTRACT_MAX_ENTRIES}.\n\t */\n\treadonly maxExtractBytes?: number\n\treadonly maxExtractEntries?: number\n\t/**\n\t * Called with per-entry materialization progress during\n\t * `extractArchive`, so the server can surface it to the user.\n\t */\n\treadonly onExtractProgress?: (progress: ExtractProgress) => void\n\t/**\n\t * Process-wide nested central-directory cache. When provided, virtual\n\t * path resolution (`outer!inner`) reuses parsed listings across hook\n\t * calls instead of re-reading each archive's CD per invocation. The\n\t * server passes one cache per (resId, fileVersion) scope.\n\t */\n\treadonly nestedCdCache?: NestedCdCache\n\t/**\n\t * Session context handed to hooks as `api.context.detect`: the\n\t * payload a prior `detect` invocation returned on a successful\n\t * match. The sandbox worker injects it itself; this escape hatch\n\t * lets in-process hosts (the dev runner) do the same.\n\t */\n\treadonly detectContext?: unknown\n}\n\nexport const DEFAULT_PLUGIN_EXTRACT_MAX_BYTES = 8 * 1024 * 1024 * 1024\nexport const DEFAULT_PLUGIN_EXTRACT_MAX_ENTRIES = 200_000\n\nexport function createPluginResourceAPI<\n\tTSchema extends PluginSchema = PluginSchema,\n>(deps: CreatePluginResourceAPIDeps): ResourceAPI<TSchema> {\n\t// Virtual paths (`outer!inner`) resolve through this wrapper, so\n\t// readFile/sniff/probe/stat/hash all reach into nested containers.\n\t// The cacheScope disambiguates nested-cache keys when one shared\n\t// cache serves many resources (see createNestedAwareContainer); the\n\t// extract cache dir extends virtual addressing to *extracted*\n\t// archives (non-zip formats become addressable once materialized).\n\tconst view = createNestedAwareContainer(\n\t\tdeps.view,\n\t\tdeps.nestedCdCache,\n\t\tdeps.cacheScope,\n\t\tdeps.extractCacheDir,\n\t)\n\tconst maxReadFileBytes = deps.maxReadFileBytes ?? PLUGIN_READ_FILE_MAX_BYTES\n\n\tconst extractor: ArchiveExtractor | undefined =\n\t\tdeps.extractCacheDir === undefined\n\t\t\t? undefined\n\t\t\t: createArchiveExtractor({\n\t\t\t\t\touter: {\n\t\t\t\t\t\tsizeOf: (rel) =>\n\t\t\t\t\t\t\tdeps.view.resolveByteRange(rel).then((r) => r?.size),\n\t\t\t\t\t\treadSlice: (rel, start, end) =>\n\t\t\t\t\t\t\tdeps.view.readEntrySlice(rel, start, end),\n\t\t\t\t\t},\n\t\t\t\t\tcacheDir: deps.extractCacheDir,\n\t\t\t\t\tmaxBytes: deps.maxExtractBytes ?? DEFAULT_PLUGIN_EXTRACT_MAX_BYTES,\n\t\t\t\t\tmaxEntries:\n\t\t\t\t\t\tdeps.maxExtractEntries ?? DEFAULT_PLUGIN_EXTRACT_MAX_ENTRIES,\n\t\t\t\t\tprobeImage: deps.probeImage,\n\t\t\t\t\tonProgress: deps.onExtractProgress,\n\t\t\t\t\tnestedCdCache: deps.nestedCdCache,\n\t\t\t\t})\n\n\tasync function readFileScoped(\n\t\tpath: string,\n\t\trange?: ReadFileRange,\n\t): Promise<Uint8Array> {\n\t\tif (range === undefined) {\n\t\t\tconst size = (await view.resolveByteRange(path))?.size\n\t\t\tif (size !== undefined) assertReadSize(path, size, maxReadFileBytes)\n\t\t\tconst buf = await view.readEntry(path)\n\t\t\treturn new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)\n\t\t}\n\t\tconst start = Math.max(0, range.start ?? 0)\n\t\tconst size = (await view.resolveByteRange(path))?.size\n\t\tif (size === undefined) {\n\t\t\t// Unknown size (missing entry) — let the view raise its own error.\n\t\t\treturn toUint8Array(\n\t\t\t\tawait view.readEntrySlice(path, start, range.end ?? start),\n\t\t\t)\n\t\t}\n\t\tconst end = Math.min(range.end ?? size, size)\n\t\tassertReadSize(path, Math.max(0, end - start), maxReadFileBytes)\n\t\treturn toUint8Array(await view.readEntrySlice(path, start, end))\n\t}\n\n\tasync function listFileNamesScoped(): Promise<readonly string[]> {\n\t\treturn view.listEntries()\n\t}\n\n\t/**\n\t * Run `compute` through the shared probe cache when configured. The\n\t * cache key carries the computation kind — sniffing an entry and\n\t * decoding it are different results for the same path.\n\t */\n\tfunction cached<T extends object | boolean | undefined>(\n\t\tkind: string,\n\t\tpath: string,\n\t\tcompute: () => Promise<T>,\n\t): Promise<T> {\n\t\tif (deps.probeCache === undefined || deps.cacheScope === undefined) {\n\t\t\treturn compute()\n\t\t}\n\t\treturn deps.probeCache.getOrCompute(\n\t\t\t`${deps.cacheScope}:${kind}:${path}`,\n\t\t\tcompute,\n\t\t)\n\t}\n\n\tfunction sniffScoped(path: string): Promise<FileType | undefined> {\n\t\treturn cached(\"sniff\", path, async () => {\n\t\t\t// A missing or unreadable entry has no type — identification\n\t\t\t// never rejects, so callers can sniff freely while walking a\n\t\t\t// file list that may be stale.\n\t\t\tconst head = await view\n\t\t\t\t.readEntrySlice(path, 0, SNIFF_HEADER_BYTES)\n\t\t\t\t.catch(() => undefined)\n\t\t\tif (head === undefined) return undefined\n\t\t\treturn sniffBytes(head, path)\n\t\t})\n\t}\n\n\t/**\n\t * Image metadata plus animation in one pass. Static formats answer\n\t * from a header slice (libvips reads the ENTIRE entry for metadata\n\t * from a stream, but only the header from a buffer); animation\n\t * candidates (gif/webp/avif) must stream, because a truncated buffer\n\t * cannot complete the frame scan and would report an animated source\n\t * as static.\n\t */\n\tasync function probeImageEntry(\n\t\tpath: string,\n\t\ttype: FileType,\n\t): Promise<ProbeResult> {\n\t\tconst probeImage = deps.probeImage\n\t\tif (probeImage === undefined) {\n\t\t\treturn { kind: \"unknown\", reason: \"unavailable\" }\n\t\t}\n\t\tif (!isAnimatedCandidateExt(type.ext)) {\n\t\t\tconst head = await view.readEntrySlice(path, 0, PROBE_HEADER_BYTES)\n\t\t\tif (head.length > 0) {\n\t\t\t\tconst probed = await probeImage(head, type.ext).catch(() => undefined)\n\t\t\t\tif (probed !== undefined) return imageResult(type.mime, probed)\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tconst { stream } = await view.openEntryStream(path)\n\t\t\tconst probed = await probeImage(stream, type.ext)\n\t\t\tif (probed !== undefined) return imageResult(type.mime, probed)\n\t\t} catch (err) {\n\t\t\tconsole.warn(\n\t\t\t\t`[probe] path=${path} image probe threw: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t)\n\t\t}\n\t\treturn { kind: \"unknown\", reason: \"failed\" }\n\t}\n\n\t/**\n\t * Audio/video metadata. The ffmpeg container hint comes from the\n\t * sniffed MIME type rather than the filename, so a mislabelled entry\n\t * still demuxes; ISO-BMFF sources keep their index at the end of the\n\t * file, so a pipe probe can legitimately fail and resolve to\n\t * `{ kind: \"unknown\", reason: \"failed\" }`.\n\t */\n\tasync function probeAvEntry(\n\t\tpath: string,\n\t\ttype: FileType,\n\t): Promise<ProbeResult> {\n\t\tconst probeAv = deps.probeAv\n\t\tif (probeAv === undefined) {\n\t\t\treturn { kind: \"unknown\", reason: \"unavailable\" }\n\t\t}\n\t\tconst inputFormat = MIME_FFMPEG_INPUT_FORMAT[type.mime]\n\t\tif (inputFormat === undefined) {\n\t\t\treturn { kind: \"unknown\", reason: \"unsupported\" }\n\t\t}\n\t\ttry {\n\t\t\tconst { stream } = await view.openEntryStream(path)\n\t\t\treturn await probeAv(stream, { mime: type.mime, inputFormat })\n\t\t} catch {\n\t\t\treturn { kind: \"unknown\", reason: \"failed\" }\n\t\t}\n\t}\n\n\tfunction probeScoped(path: string): Promise<ProbeResult> {\n\t\treturn cached(\"probe\", path, async () => {\n\t\t\tconst type = await sniffScoped(path)\n\t\t\tif (type === undefined) {\n\t\t\t\treturn { kind: \"unknown\", reason: \"unsupported\" } as const\n\t\t\t}\n\t\t\tswitch (type.kind) {\n\t\t\t\tcase \"image\":\n\t\t\t\t\treturn probeImageEntry(path, type)\n\t\t\t\tcase \"video\":\n\t\t\t\tcase \"audio\":\n\t\t\t\t\treturn probeAvEntry(path, type)\n\t\t\t\tdefault:\n\t\t\t\t\treturn { kind: \"other\", mime: type.mime } as const\n\t\t\t}\n\t\t})\n\t}\n\n\tasync function statFileScoped(\n\t\tpath: string,\n\t): Promise<{ readonly sizeBytes: number } | undefined> {\n\t\tconst range = await view.resolveByteRange(path)\n\t\tif (range === undefined) return undefined\n\t\treturn { sizeBytes: range.size }\n\t}\n\n\tasync function statFilesScoped(\n\t\tpaths: readonly string[],\n\t): Promise<readonly ({ readonly sizeBytes: number } | undefined)[]> {\n\t\t// Batch resolution: the zip CD cache makes every range lookup a\n\t\t// warm map hit; parallelizing keeps the fan-out bounded by the\n\t\t// plugin's own concurrency when it chunks the input.\n\t\treturn Promise.all(paths.map(statFileScoped))\n\t}\n\n\tasync function hashBytesScoped(\n\t\tpath: string,\n\t\talgo: \"md5\" | \"sha256\",\n\t): Promise<string> {\n\t\tconst { stream } = await view.openEntryStream(path)\n\t\treturn hashStream(stream, algo)\n\t}\n\n\tasync function computeImageHashesScoped(\n\t\tpath: string,\n\t\tkinds: readonly ImageHashKind[],\n\t): Promise<Readonly<Record<ImageHashKind, string>> | undefined> {\n\t\t// Hashing follows the content, not the name: an image with a\n\t\t// wrong extension is still a duplicate of itself.\n\t\tconst type = await sniffScoped(path)\n\t\tif (type?.kind !== \"image\") return undefined\n\t\tconst result: Partial<Record<ImageHashKind, string>> = {}\n\t\tif (kinds.includes(\"sha256\")) {\n\t\t\tconst { stream } = await view.openEntryStream(path)\n\t\t\tresult.sha256 = await hashStream(stream, \"sha256\")\n\t\t}\n\t\tconst perceptual = kinds.filter((kind) =>\n\t\t\tPERCEPTUAL_HASH_KINDS.includes(kind),\n\t\t)\n\t\tif (perceptual.length > 0) {\n\t\t\tconst { stream } = await view.openEntryStream(path)\n\t\t\tconst gray = await decodeGrayGrid(stream)\n\t\t\tif (gray !== undefined && grayStddev(gray) >= MIN_PERCEPTUAL_STDDEV) {\n\t\t\t\tif (perceptual.includes(\"dhash\")) result.dhash = computeDHash(gray)\n\t\t\t\tif (perceptual.includes(\"phash\")) result.phash = computePHash(gray)\n\t\t\t} else if (result.sha256 === undefined) {\n\t\t\t\t// Not a decodable image (or too flat for a meaningful\n\t\t\t\t// perceptual hash) and no byte hash requested.\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t}\n\t\treturn result as Readonly<Record<ImageHashKind, string>>\n\t}\n\n\t/**\n\t * Decode one frame of the entry as a `PHASH_GRID × PHASH_GRID`\n\t * grayscale buffer. Animated sources render their first frame\n\t * (sharp's default with `pages: 1`). Undecodable input resolves to\n\t * `undefined`.\n\t */\n\tasync function decodeGrayGrid(\n\t\tstream: Readable,\n\t): Promise<Uint8Array | undefined> {\n\t\ttry {\n\t\t\tconst instance = await sharpFromReadable(stream, { pages: 1 })\n\t\t\tconst { data } = await instance\n\t\t\t\t.resize(PHASH_GRID, PHASH_GRID, { fit: \"fill\" })\n\t\t\t\t.grayscale()\n\t\t\t\t.raw()\n\t\t\t\t.toBuffer({ resolveWithObject: true })\n\t\t\treturn new Uint8Array(data.buffer, data.byteOffset, data.byteLength)\n\t\t} catch {\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\treturn {\n\t\t// No-ops: the sandbox host is the plugin log sink — it alone knows\n\t\t// which plugin emitted the line (see dispatchLog in sandbox/host.ts).\n\t\tlogInfo() {},\n\t\tlogWarn() {},\n\t\tlogError() {},\n\t\t// The payload is opaque to this factory (hosts pass `unknown`);\n\t\t// the schema generic only types it for schema-aware consumers.\n\t\tcontext: { detect: deps.detectContext as TSchema[\"detect\"] | undefined },\n\t\tlistFileNames: listFileNamesScoped,\n\t\treadFile: readFileScoped,\n\t\tstatFile: statFileScoped,\n\t\tstatFiles: statFilesScoped,\n\t\tsniff: sniffScoped,\n\t\tprobe: probeScoped,\n\t\thashBytes: hashBytesScoped,\n\t\tcomputeImageHashes: computeImageHashesScoped,\n\t\tlistContainer: (filename) => listContainerScoped(filename, extractor),\n\t\textractArchive: (filename) => extractArchiveScoped(filename, extractor),\n\t\t// The plugin vault is a sandbox-level capability: the sandbox host\n\t\t// intercepts these method names and routes them to the wired\n\t\t// plugin-asset service with the owning plugin id (see\n\t\t// sandbox/host.ts). In-process hosts (directory API, contract\n\t\t// fixtures, dev runner) have no consent channel, so the methods\n\t\t// answer UNAVAILABLE — exactly like the CLI and the workbench.\n\t\tdownload: async () => {\n\t\t\tthrow pluginAssetError(\n\t\t\t\t\"UNAVAILABLE\",\n\t\t\t\t\"download() — this host has no plugin asset service; only the app server host can download into the plugin vault\",\n\t\t\t)\n\t\t},\n\t\tstatAsset: async () => {\n\t\t\tthrow unavailableAsset(\"statAsset\")\n\t\t},\n\t\treadAsset: async () => {\n\t\t\tthrow unavailableAsset(\"readAsset\")\n\t\t},\n\t\tdeleteAsset: async () => {\n\t\t\tthrow unavailableAsset(\"deleteAsset\")\n\t\t},\n\t}\n}\n\nfunction unavailableAsset(method: string): Error {\n\treturn pluginAssetError(\n\t\t\"UNAVAILABLE\",\n\t\t`${method}() — this host has no plugin asset vault; only the app server host manages vault files`,\n\t)\n}\n\n/**\n * List a container entry's files without materializing — metadata-only\n * consumers (detect, card counts) use this so a preview never pays for\n * extraction it does not need.\n */\nasync function listContainerScoped(\n\tfilename: string,\n\textractor: ArchiveExtractor | undefined,\n): Promise<ContainerListing> {\n\tif (extractor === undefined) {\n\t\tthrow new Error(\n\t\t\t`listContainer(\"${filename}\") — this host has no container support; only the archive-backed server host lists containers`,\n\t\t)\n\t}\n\tconst entries = await extractor.list(filename)\n\tif (entries === undefined) {\n\t\tthrow new Error(\n\t\t\t`listContainer(\"${filename}\") — not a supported archive (zip/tar/7z/rar/xz), or no 7-Zip binary for non-zip formats`,\n\t\t)\n\t}\n\treturn { entries: entries.map(bareContainerEntry) }\n}\n\n/** Convert a listed entry to the wire shape (no dimensions). */\nfunction bareContainerEntry(\n\tentry: ArchiveEntry,\n): ContainerListing[\"entries\"][number] {\n\treturn {\n\t\tpath: entry.name,\n\t\tsizeBytes: entry.sizeBytes,\n\t\tkind: fileTypeFromName(entry.name)?.kind ?? \"other\",\n\t}\n}\n\n/**\n * Materialize the container entry `filename` into the configured cache\n * directory (or list its contents without writing in read-only mode).\n * Rejects when this API instance has no extraction cache wired.\n */\nasync function extractArchiveScoped(\n\tfilename: string,\n\textractor: ArchiveExtractor | undefined,\n): Promise<ExtractionResult> {\n\tif (extractor === undefined) {\n\t\tthrow new Error(\n\t\t\t`extractArchive(\"${filename}\") — this host has no extraction cache; only the archive-backed server host materializes containers`,\n\t\t)\n\t}\n\treturn extractor.extract(filename)\n}\n\n/** Assemble the image branch of a {@link ProbeResult}. */\nfunction imageResult(mime: string, probed: ImageSourceProbe): ProbeResult {\n\treturn {\n\t\tkind: \"image\",\n\t\tmime,\n\t\twidth: probed.width,\n\t\theight: probed.height,\n\t\tanimated: probed.animated,\n\t}\n}\n\nfunction assertReadSize(path: string, sizeBytes: number, max: number): void {\n\tif (sizeBytes > max) {\n\t\tthrow new Error(\n\t\t\t`readFile(\"${path}\") requests ${sizeBytes} bytes, exceeding the per-call limit of ${max} bytes — pass a byte range or use readFileChunks()`,\n\t\t)\n\t}\n}\n\nfunction toUint8Array(buf: Buffer): Uint8Array {\n\treturn new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)\n}\n","import type { PluginManifest } from \"@hoardodile/sdk-types\"\nimport type {\n\tPluginCapabilityGate,\n\tPluginCapabilityKey,\n} from \"@hoardodile/sdk-types/plugin-capabilities\"\nimport { PLUGIN_CAPABILITY_GATES } from \"@hoardodile/sdk-types/plugin-capabilities\"\nimport type { PluginRegistryEntry } from \"./api-types.ts\"\n\n/**\n * Capabilities a plugin manifest can declare — derived from the single\n * {@link PLUGIN_CAPABILITY_GATES} table (which is itself keyed by the\n * manifest's `PluginPermissions` keys), so the guard can never drift.\n */\nexport type PluginCapability = PluginCapabilityKey\n\n/** Gate metadata a consumer may want (e.g. for tooltips). */\nexport type { PluginCapabilityGate }\n\n/**\n * Permission checks against a plugin's declared manifest permissions,\n * reading the capability vocabulary from the shared gates table.\n * The server uses these before routing any plugin-scoped work so a\n * manifest that does not declare a capability cannot trigger it.\n */\nexport type CapabilityGuard = {\n\t/** Check whether a manifest grants the given capability. */\n\treadonly check: (\n\t\tmanifest: PluginManifest,\n\t\tcapability: PluginCapability,\n\t) => boolean\n\t/** Assert that a manifest grants the given capability; throw if not. */\n\treadonly require: (\n\t\tmanifest: PluginManifest,\n\t\tcapability: PluginCapability,\n\t) => void\n\t/** Filter entries to only those that grant the given capability. */\n\treadonly filter: (\n\t\tentries: readonly PluginRegistryEntry[],\n\t\tcapability: PluginCapability,\n\t) => readonly PluginRegistryEntry[]\n}\n\n/** Create a stateless capability guard over manifest permissions. */\nexport function createCapabilityGuard(): CapabilityGuard {\n\tfunction check(\n\t\tmanifest: PluginManifest,\n\t\tcapability: PluginCapability,\n\t): boolean {\n\t\treturn manifest.permissions[capability] === true\n\t}\n\n\tfunction require(\n\t\tmanifest: PluginManifest,\n\t\tcapability: PluginCapability,\n\t): void {\n\t\tif (!check(manifest, capability)) {\n\t\t\tthrow new Error(\n\t\t\t\t`${capability} permission denied for plugin ${manifest.id}`,\n\t\t\t)\n\t\t}\n\t}\n\n\tfunction filter(\n\t\tentries: readonly PluginRegistryEntry[],\n\t\tcapability: PluginCapability,\n\t): readonly PluginRegistryEntry[] {\n\t\treturn entries.filter((e) => check(e.manifest, capability))\n\t}\n\n\treturn { check, require, filter }\n}\n\nexport { PLUGIN_CAPABILITY_GATES }\n","import { pathToFileURL } from \"node:url\"\nimport { createDirectoryResourceAPI } from \"./directory-api.ts\"\nimport type { HookName } from \"./sandbox/protocol.ts\"\n\n/**\n * Plugin hooks the dev runner can invoke against a directory — the\n * contract-order list shared with the sandbox host and the CLI.\n */\nexport type PluginHookName = HookName\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null\n}\n\n/**\n * Load a built plugin `main.js` and invoke one hook with a\n * directory-backed {@link ResourceAPI}. Development aid only — the plugin\n * runs unsandboxed in the current process, so only run code you trust.\n * Consumed from plugin tests as a devDependency; never bundled into a\n * shipped plugin.\n *\n * Before the target hook runs, `detect` is invoked once (when the\n * target is not detect itself) so hooks see the session context a real\n * sandbox session would have — `api.context.detect` carries the\n * detection payload. A throwing or missed detection leaves the context\n * absent, exactly like a fresh worker in production.\n */\nexport async function runPluginHook(opts: {\n\treadonly mainPath: string\n\treadonly hook: PluginHookName\n\treadonly dir: string\n\t/** Scratch dir for `extractArchive` materialization. */\n\treadonly extractCacheDir?: string\n}): Promise<{ readonly result: unknown; readonly durationMs: number }> {\n\tconst mod: unknown = await import(pathToFileURL(opts.mainPath).href)\n\tif (!isRecord(mod) || !isRecord(mod.default)) {\n\t\tthrow new Error(\n\t\t\t`Plugin at ${opts.mainPath} must default-export a plugin definition`,\n\t\t)\n\t}\n\tconst hook = mod.default[opts.hook]\n\tif (typeof hook !== \"function\") {\n\t\tthrow new Error(\n\t\t\t`Plugin at ${opts.mainPath} has no \"${opts.hook}\" hook (expected a function on the default export)`,\n\t\t)\n\t}\n\n\tlet detectContext: unknown\n\tif (opts.hook !== \"detect\" && typeof mod.default.detect === \"function\") {\n\t\ttry {\n\t\t\tconst detected = await mod.default.detect(\n\t\t\t\tcreateDirectoryResourceAPI(opts.dir, {\n\t\t\t\t\textractCacheDir: opts.extractCacheDir,\n\t\t\t\t}),\n\t\t\t)\n\t\t\tif (isRecord(detected) && detected.ok === true) {\n\t\t\t\tconst { ok: _ok, ...payload } = detected\n\t\t\t\tif (Object.keys(payload).length > 0) detectContext = payload\n\t\t\t}\n\t\t} catch {\n\t\t\t// A throwing detector leaves the context absent — same as a\n\t\t\t// fresh worker in production.\n\t\t}\n\t}\n\n\tconst api = createDirectoryResourceAPI(opts.dir, {\n\t\textractCacheDir: opts.extractCacheDir,\n\t\tdetectContext,\n\t})\n\tconst started = performance.now()\n\tconst result: unknown = await hook(api)\n\treturn { result, durationMs: performance.now() - started }\n}\n","import { existsSync, readdirSync, readFileSync } from \"node:fs\"\nimport { join, resolve } from \"node:path\"\nimport type { PluginManifest, PluginManifestId } from \"@hoardodile/sdk-types\"\nimport { pluginManifest as pluginManifestSchema } from \"@hoardodile/sdk-types/schema\"\nimport type { FoundPlugin, MissingPlugin } from \"./api-types.ts\"\nimport type { PluginSettingsStore } from \"./settings-store.ts\"\n\n/**\n * Default pin for plugins with no settings row yet (first install /\n * clean state / builtin). Pinned plugins wear their name on resource\n * cards so the type stays readable without a cover.\n */\nconst DEFAULT_PLUGIN_PINNED = true\n\n/** Default first-install priority — reorderable from the UI afterwards. */\nconst DEFAULT_PLUGIN_PRIORITY = 100\n\nexport type PluginDiscoveryDeps = {\n\t/** Directory of the mandatory builtin plugin (`manifest.json` at its root). */\n\treadonly builtinDir?: string\n\t/** Extra plugin directories for development (override same-id disk plugins). */\n\treadonly devPluginDirs?: readonly string[]\n\t/** Directory where installed plugins live, one subdirectory per plugin. */\n\treadonly pluginsDir: string\n\t/** DB-backed per-plugin settings (enabled/priority/pinned/color). */\n\treadonly settings: PluginSettingsStore\n\t/** Skip dev plugins entirely (e.g. production configuration). */\n\treadonly disableDevPlugins?: boolean\n}\n\n/**\n * Plugin discovery: finds plugins on disk (builtin, dev, installed),\n * merges their DB settings, and reports which known plugins are missing.\n */\nexport type PluginDiscovery = {\n\treadonly discover: () => Promise<{\n\t\tfound: FoundPlugin[]\n\t\tmissing: MissingPlugin[]\n\t}>\n}\n\n/**\n * Create the discovery pipeline. Order matters: the builtin plugin wins\n * priority, dev plugins override same-id installed ones, and anything in\n * settings but absent from disk is reported missing.\n */\nexport function createPluginDiscovery(\n\tdeps: PluginDiscoveryDeps,\n): PluginDiscovery {\n\t// The first loadAll runs at boot: a broken builtin there is a config\n\t// error that should fail fast. Later (re)scans degrade gracefully —\n\t// the loader keeps the previous registry when discovery throws.\n\tlet firstLoad = true\n\n\tasync function discover(): Promise<{\n\t\tfound: FoundPlugin[]\n\t\tmissing: MissingPlugin[]\n\t}> {\n\t\tconst found: FoundPlugin[] = []\n\t\t// Every discovered id, across all sources. Guarantees each plugin\n\t\t// id is activated exactly once — a duplicate would make activation\n\t\t// dispose the first worker while the registry still references it\n\t\t// (every hook then fails with \"sandbox disposed\").\n\t\tconst seen = new Set<PluginManifestId>()\n\n\t\t// 1. Builtin plugin — its id is reserved; dev/disk copies are skipped.\n\t\tif (deps.builtinDir !== undefined) {\n\t\t\tconst builtin = discoverBuiltin(deps.builtinDir, firstLoad)\n\t\t\tif (builtin !== undefined) {\n\t\t\t\tfound.push(builtin)\n\t\t\t\tseen.add(builtin.id)\n\t\t\t}\n\t\t}\n\t\tfirstLoad = false\n\n\t\t// 2. Dev plugins. Same-id dev dirs override each other (later wins,\n\t\t// matching the dev-over-disk precedence); the earlier entry is\n\t\t// replaced so activation never loads the same id twice.\n\t\tconst dev = discoverDevPlugins(\n\t\t\tdeps.devPluginDirs,\n\t\t\tdeps.settings,\n\t\t\tdeps.disableDevPlugins ?? false,\n\t\t)\n\t\tfor (const plugin of dev) {\n\t\t\tif (seen.has(plugin.id)) {\n\t\t\t\tconst index = found.findIndex((f) => f.id === plugin.id)\n\t\t\t\tif (index !== -1 && found[index]?.source === \"dev\") {\n\t\t\t\t\t// Duplicate dev dir — the later one wins.\n\t\t\t\t\tfound[index] = plugin\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[plugin-discovery] dev plugin ${plugin.id} (${plugin.manifest.name ?? plugin.id}) overrides an earlier dev dir entry`,\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[plugin-discovery] skipping dev plugin ${plugin.id}: id is reserved by the builtin plugin`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfound.push(plugin)\n\t\t\tseen.add(plugin.id)\n\t\t}\n\n\t\t// 3. Disk plugins, skipping ids overridden by dev or the builtin.\n\t\tconst disk = discoverDiskPlugins(deps.pluginsDir, deps.settings)\n\t\tfor (const plugin of disk) {\n\t\t\tif (seen.has(plugin.id)) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[plugin-discovery] skipping disk plugin ${plugin.id}: id already registered (builtin or dev plugin)`,\n\t\t\t\t)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfound.push(plugin)\n\t\t\tseen.add(plugin.id)\n\t\t}\n\n\t\t// 4. Missing plugins (settings-only)\n\t\tconst missing = discoverMissingPlugins(deps.settings, seen)\n\n\t\treturn { found, missing }\n\t}\n\n\treturn { discover }\n}\n\nfunction discoverBuiltin(\n\tbuiltinDir: string,\n\tfailFast: boolean,\n): FoundPlugin | undefined {\n\tconst resolved = resolve(builtinDir)\n\tconst manifest = parseManifest(resolved, \"builtin\")\n\tif (manifest === undefined) {\n\t\tconst message =\n\t\t\t`Builtin plugin not found or invalid at ${resolved}. ` +\n\t\t\t\"Set BUILTIN_PATH env to a valid plugin directory.\"\n\t\tif (failFast) {\n\t\t\tthrow new Error(message)\n\t\t}\n\t\tconsole.error(`[plugin-discovery] ${message}`)\n\t\treturn undefined\n\t}\n\treturn {\n\t\tid: manifest.id,\n\t\tmanifest,\n\t\tdiskPath: resolved,\n\t\tsource: \"builtin\",\n\t\tenabled: true,\n\t\tpriority: Number.MAX_SAFE_INTEGER,\n\t\tpinned: DEFAULT_PLUGIN_PINNED,\n\t\tcolor: \"\",\n\t}\n}\n\nfunction discoverDevPlugins(\n\tdevPluginDirs: readonly string[] | undefined,\n\tsettings: PluginSettingsStore,\n\tdisableDevPlugins: boolean,\n): FoundPlugin[] {\n\tif (disableDevPlugins) {\n\t\tconsole.info(\"[plugin-discovery] dev plugins disabled by configuration\")\n\t\treturn []\n\t}\n\tif (devPluginDirs === undefined || devPluginDirs.length === 0) return []\n\n\tconst results: FoundPlugin[] = []\n\tfor (const dir of devPluginDirs) {\n\t\tconst resolved = resolve(dir)\n\t\tconst manifest = parseManifest(resolved, \"dev\")\n\t\tif (manifest === undefined) continue\n\n\t\tconst row = settings.get(manifest.id)\n\n\t\tresults.push({\n\t\t\tid: manifest.id,\n\t\t\tmanifest,\n\t\t\tdiskPath: resolved,\n\t\t\tsource: \"dev\",\n\t\t\tenabled: true,\n\t\t\tpriority: row?.priority ?? DEFAULT_PLUGIN_PRIORITY,\n\t\t\tpinned: row?.pinned ?? DEFAULT_PLUGIN_PINNED,\n\t\t\tcolor: row?.color ?? \"\",\n\t\t})\n\t}\n\treturn results\n}\n\nfunction discoverDiskPlugins(\n\tpluginsDir: string,\n\tsettings: PluginSettingsStore,\n): FoundPlugin[] {\n\tif (!existsSync(pluginsDir)) return []\n\n\tconst dirents = readdirSync(pluginsDir, { withFileTypes: true })\n\tif (dirents.length === 0) return []\n\n\tconst results: FoundPlugin[] = []\n\tfor (const dirent of dirents) {\n\t\tif (!dirent.isDirectory()) continue\n\t\tconst dirPath = join(pluginsDir, dirent.name)\n\n\t\tconst manifest = parseManifest(dirPath, dirent.name)\n\t\tif (manifest === undefined) continue\n\n\t\tconst row = settings.get(manifest.id)\n\t\tconst enabled = row?.enabled ?? true\n\t\tconst priority = row?.priority ?? DEFAULT_PLUGIN_PRIORITY\n\t\tconst pinned = row?.pinned ?? DEFAULT_PLUGIN_PINNED\n\t\tconst color = row?.color ?? \"\"\n\n\t\tresults.push({\n\t\t\tid: manifest.id,\n\t\t\tmanifest,\n\t\t\tdiskPath: dirPath,\n\t\t\tsource: \"disk\",\n\t\t\tenabled,\n\t\t\tpriority,\n\t\t\tpinned,\n\t\t\tcolor,\n\t\t})\n\t}\n\treturn results\n}\n\nfunction discoverMissingPlugins(\n\tsettings: PluginSettingsStore,\n\tloadedIds: ReadonlySet<PluginManifestId>,\n): MissingPlugin[] {\n\tconst results: MissingPlugin[] = []\n\tfor (const row of settings.all()) {\n\t\tif (loadedIds.has(row.id)) continue\n\t\tlet manifest: PluginManifest\n\t\ttry {\n\t\t\tmanifest = JSON.parse(row.manifest) as PluginManifest\n\t\t} catch {\n\t\t\tcontinue\n\t\t}\n\t\tresults.push({\n\t\t\tid: row.id,\n\t\t\tmanifest,\n\t\t\tenabled: row.enabled,\n\t\t\tpriority: row.priority,\n\t\t\tpinned: row.pinned,\n\t\t\tcolor: row.color,\n\t\t})\n\t}\n\treturn results\n}\n\n/**\n * Read and validate `manifest.json` in `dirPath` against the zod\n * contract. Logs a warning and returns `undefined` for every failure\n * mode (missing file, unreadable, invalid JSON, schema violation) so\n * callers can skip the directory without crashing the scan.\n */\nexport function parseManifest(\n\tdirPath: string,\n\tdirName: string,\n): PluginManifest | undefined {\n\tconst manifestPath = join(dirPath, \"manifest.json\")\n\tif (!existsSync(manifestPath)) {\n\t\tconsole.warn(`[plugin-discovery] skipping ${dirName}: no manifest.json`)\n\t\treturn undefined\n\t}\n\n\tlet raw: string\n\ttry {\n\t\traw = readFileSync(manifestPath, \"utf-8\")\n\t} catch {\n\t\tconsole.warn(\n\t\t\t`[plugin-discovery] skipping ${dirName}: cannot read manifest.json`,\n\t\t)\n\t\treturn undefined\n\t}\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tconsole.warn(\n\t\t\t`[plugin-discovery] skipping ${dirName}: manifest is not valid JSON`,\n\t\t)\n\t\treturn undefined\n\t}\n\n\tconst result = pluginManifestSchema.safeParse(parsed)\n\tif (!result.success) {\n\t\tconsole.warn(\n\t\t\t`[plugin-discovery] skipping ${dirName}: invalid manifest`,\n\t\t\tresult.error,\n\t\t)\n\t\treturn undefined\n\t}\n\n\treturn result.data\n}\n","import { pluginAssetError } from \"@hoardodile/sdk-types\"\nimport type { ResourceAPI } from \"./types.ts\"\n\n/**\n * The ResourceAPI handed to a plugin's `onInstall` hook: there is no\n * resource attached, so the file surface answers empty (or throws a\n * clear \"no resource\" error) and `context.detect` is `undefined`.\n *\n * The asset surface stays honest: in the app server the sandbox\n * intercepts these methods before the API object is consulted and\n * routes them to the consent-gated asset service with the owning\n * plugin id — the onInstall download flow is identical to the runtime\n * one. In-process hosts (fixtures, dev runner, CLI) answer\n * `UNAVAILABLE` exactly like every other host without a consent\n * channel.\n */\nexport function createInstallScopeApi(): ResourceAPI {\n\tconst noResource = (method: string): Error =>\n\t\tnew Error(\n\t\t\t`${method}() — no resource is attached to the onInstall hook; use the asset methods (download/statAsset/readAsset/deleteAsset) for install-time work`,\n\t\t)\n\treturn {\n\t\tlogInfo() {},\n\t\tlogWarn() {},\n\t\tlogError() {},\n\t\tcontext: { detect: undefined },\n\t\tlistFileNames: async () => [],\n\t\treadFile: async () => {\n\t\t\tthrow noResource(\"readFile\")\n\t\t},\n\t\tstatFile: async () => undefined,\n\t\tstatFiles: async (paths) => paths.map(() => undefined),\n\t\tsniff: async () => undefined,\n\t\tprobe: async () => ({ kind: \"unknown\", reason: \"unavailable\" }),\n\t\thashBytes: async () => {\n\t\t\tthrow noResource(\"hashBytes\")\n\t\t},\n\t\tcomputeImageHashes: async () => undefined,\n\t\tlistContainer: async () => {\n\t\t\tthrow noResource(\"listContainer\")\n\t\t},\n\t\textractArchive: async () => {\n\t\t\tthrow noResource(\"extractArchive\")\n\t\t},\n\t\tdownload: async () => {\n\t\t\tthrow pluginAssetError(\n\t\t\t\t\"UNAVAILABLE\",\n\t\t\t\t\"download() — this host has no plugin asset service; only the app server host can download into the plugin vault\",\n\t\t\t)\n\t\t},\n\t\tstatAsset: async () => {\n\t\t\tthrow unavailableAsset(\"statAsset\")\n\t\t},\n\t\treadAsset: async () => {\n\t\t\tthrow unavailableAsset(\"readAsset\")\n\t\t},\n\t\tdeleteAsset: async () => {\n\t\t\tthrow unavailableAsset(\"deleteAsset\")\n\t\t},\n\t}\n}\n\nfunction unavailableAsset(method: string): Error {\n\treturn pluginAssetError(\n\t\t\"UNAVAILABLE\",\n\t\t`${method}() — this host has no plugin asset vault; only the app server host manages vault files`,\n\t)\n}\n","import type {\n\tImageHash,\n\tImageHashesResult,\n\tPluginManifestId,\n\tSerializedFileList,\n} from \"@hoardodile/sdk-types\"\nimport type { PluginRegistry, PluginRegistryEntry } from \"./api-types.ts\"\nimport { createCapabilityGuard } from \"./capability-guard.ts\"\nimport { createInstallScopeApi } from \"./install-api.ts\"\nimport type { Detection, ResourceAPI } from \"./types.ts\"\n\n/** Absolute cap of hash rows one resource may contribute (host policy). */\nexport const MAX_IMAGE_HASHES_PER_RESOURCE = 2000\n\n/**\n * Run `detect` over the given registry entries in order; returns the id of\n * the first matching plugin, or `undefined` when none match. A throwing\n * detector is logged (tagged with `action`) and skipped — one crash must\n * never abort the scan.\n */\nasync function runDetectors(\n\tentries: readonly PluginRegistryEntry[],\n\tapi: ResourceAPI,\n\taction: string,\n): Promise<PluginManifestId | undefined> {\n\tfor (const entry of entries) {\n\t\tconst result = await invokeHook(entry, entry.plugin.detect, api, action)\n\t\tif (result?.ok) return entry.id\n\t}\n\treturn undefined\n}\n\n/**\n * Invoke one hook of a registry entry with the uniform policy: an\n * unimplemented hook answers `undefined` without running, a throw is\n * logged via {@link hookFailureText} and also answers `undefined`. This\n * is the \"throw means absence\" policy — callers that must tell a hook\n * failure apart from a hook that ran (meta-hook key presence) keep\n * their own try/catch instead.\n */\nasync function invokeHook<T>(\n\tentry: PluginRegistryEntry,\n\thook: ((api: ResourceAPI) => T | Promise<T>) | undefined,\n\tapi: ResourceAPI,\n\taction: string,\n\tlevel: \"warn\" | \"error\" = \"error\",\n): Promise<T | undefined> {\n\tif (hook === undefined) return undefined\n\ttry {\n\t\treturn await hook(api)\n\t} catch (err) {\n\t\tconst log = level === \"warn\" ? console.warn : console.error\n\t\tlog(hookFailureText(action, entry.id, err))\n\t\treturn undefined\n\t}\n}\n\nfunction errorText(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err)\n}\n\nfunction hookFailureText(\n\taction: string,\n\tpluginId: PluginManifestId,\n\terr: unknown,\n): string {\n\treturn `[plugin-hooks] ${action} failed for plugin ${pluginId}: ${errorText(err)}`\n}\n\nexport type PluginHooksDeps = {\n\t/**\n\t * Live accessor for the current registry — called on every hook\n\t * invocation so a `rescan()` replacing the registry never leaves\n\t * consumers holding a stale snapshot.\n\t */\n\treadonly getRegistry: () => PluginRegistry\n}\n\n/**\n * Result of {@link PluginHooks.runMetaHooks}. A present key means the hook\n * ran (permission granted + implemented); `value` is the raw hook result,\n * which may still be `undefined` when the hook itself returned nothing.\n */\nexport type PluginMetaHookResults = {\n\treadonly sourceMeta?: { readonly value: unknown }\n\treadonly searchMeta?: { readonly value: unknown }\n}\n\n/**\n * The single entry point for executing plugin hooks. Owns every\n * hook-invocation policy: priority iteration, builtin fallback, error\n * swallowing/logging, capability checks, and result validation.\n *\n * Methods receive a ready-built {@link ResourceAPI} — this module knows\n * nothing about resources, archives, or paths.\n */\nexport type PluginHooks = {\n\t/** The builtin fallback plugin id. Throws when no builtin is registered. */\n\treadonly defaultPluginId: () => PluginManifestId\n\t/**\n\t * The registry entry that should serve a resource's read paths right\n\t * now: the resource's stored plugin when it is healthy (registered,\n\t * enabled, not missing), otherwise the builtin fallback plugin.\n\t * Throws when no builtin is registered.\n\t */\n\treadonly getEffectiveEntry: (\n\t\tresPluginId: PluginManifestId | null,\n\t) => PluginRegistryEntry\n\t/** Run all enabled plugins' detectors in priority order. Returns the first matching plugin id. Throws if no match (builtin should always match). */\n\treadonly detectFirstMatch: (api: ResourceAPI) => Promise<PluginManifestId>\n\t/** Validate that the current plugin still matches. Returns confirmed plugin id or falls back to builtin id. */\n\treadonly revalidate: (\n\t\tapi: ResourceAPI,\n\t\tcurrentPluginId: PluginManifestId,\n\t) => Promise<PluginManifestId>\n\t/** Run a specific plugin's detector. */\n\treadonly detectForPlugin: (\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t) => Promise<Detection>\n\t/**\n\t * Detector pass for folder-import candidates: non-builtin detectors in\n\t * priority order, falling back to the builtin plugin without invoking\n\t * its detector.\n\t */\n\treadonly detectForImportDir: (api: ResourceAPI) => Promise<PluginManifestId>\n\t/**\n\t * Ask the owning plugin for a custom file list. Returns `undefined`\n\t * when the plugin has no file list hook (or it failed).\n\t */\n\treadonly buildFileList: (\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t) => Promise<SerializedFileList | undefined>\n\t/** Ask the owning plugin which file should be used for the local cover. */\n\treadonly resolveLocalCoverSource: (\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t) => Promise<string | undefined>\n\t/**\n\t * Run the capability-gated meta hooks (`sourceMeta`, `searchMeta`) of\n\t * the owning plugin. Keys are absent when the permission is not\n\t * granted or the hook is not implemented.\n\t */\n\treadonly runMetaHooks: (\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t) => Promise<PluginMetaHookResults>\n\t/**\n\t * True when the plugin's manifest grants `imageHashes` and the plugin\n\t * implements the hook. Used to decide whether a missing hash state is\n\t * a rebuild gap or the plugin's legitimate choice.\n\t */\n\treadonly supportsImageHashes: (pluginId: PluginManifestId) => boolean\n\t/**\n\t * Run the capability-gated `imageHashes` hook of the owning plugin.\n\t * Resolves to `undefined` when the permission is not granted, the\n\t * hook is not implemented, or it threw. The returned entries are\n\t * shape-validated (hex values, per-resource cap).\n\t */\n\treadonly runImageHashes: (\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t) => Promise<ImageHashesResult | undefined>\n\t/**\n\t * Run the plugin's optional `onInstall` hook — best-effort, invoked\n\t * by the host after a successful install/update commit (marketplace\n\t * install/update or a zip upload; never seed/dev plugins). The hook\n\t * receives an install-scoped {@link ResourceAPI}: no resource is\n\t * attached (the file surface answers empty, `context.detect` is\n\t * `undefined`), while the asset methods still work and stay gated by\n\t * the shared consent dialog. A throwing (or consent-denied) hook is\n\t * logged via `hookFailureText` and swallowed — the install itself is\n\t * never failed; plugins must re-check at runtime.\n\t */\n\treadonly runInstallHook: (pluginId: PluginManifestId) => Promise<void>\n}\n\nexport function createPluginHooks(deps: PluginHooksDeps): PluginHooks {\n\tconst { getRegistry } = deps\n\tconst guard = createCapabilityGuard()\n\n\tfunction defaultPluginId(): PluginManifestId {\n\t\tconst builtin = getRegistry().getBuiltin()\n\t\tif (builtin === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t\"No builtin plugin in registry — cannot determine default plugin\",\n\t\t\t)\n\t\t}\n\t\treturn builtin.id\n\t}\n\n\tfunction getEffectiveEntry(\n\t\tresPluginId: PluginManifestId | null,\n\t): PluginRegistryEntry {\n\t\tif (resPluginId !== null) {\n\t\t\tconst entry = getRegistry().getById(resPluginId)\n\t\t\tif (entry?.enabled === true && !entry.missing) {\n\t\t\t\treturn entry\n\t\t\t}\n\t\t}\n\t\tconst builtin = getRegistry().getBuiltin()\n\t\tif (builtin === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t\"No builtin plugin in registry — cannot determine effective plugin\",\n\t\t\t)\n\t\t}\n\t\treturn builtin\n\t}\n\n\tasync function detectFirstMatch(api: ResourceAPI): Promise<PluginManifestId> {\n\t\tconst matched = await runDetectors(\n\t\t\tgetRegistry().getEnabled(),\n\t\t\tapi,\n\t\t\t\"detect\",\n\t\t)\n\t\tif (matched !== undefined) return matched\n\t\tconst builtin = getRegistry().getBuiltin()\n\t\tthrow new Error(\n\t\t\t`No plugin matched resource. Builtin plugin ${builtin?.id ?? \"unknown\"} should have matched but did not.`,\n\t\t)\n\t}\n\n\tasync function revalidate(\n\t\tapi: ResourceAPI,\n\t\tcurrentPluginId: PluginManifestId,\n\t): Promise<PluginManifestId> {\n\t\tconst registry = getRegistry()\n\t\tconst builtin = registry.getBuiltin()\n\t\tif (builtin === undefined) {\n\t\t\tthrow new Error(\"No builtin plugin available for fallback\")\n\t\t}\n\n\t\tconst enabled = registry.getEnabled()\n\t\tconst startIndex = enabled.findIndex((e) => e.id === currentPluginId)\n\t\tif (startIndex < 0) {\n\t\t\treturn builtin.id\n\t\t}\n\n\t\tconst matched = await runDetectors(\n\t\t\tenabled.slice(startIndex),\n\t\t\tapi,\n\t\t\t\"revalidate detect\",\n\t\t)\n\t\treturn matched ?? builtin.id\n\t}\n\n\tasync function detectForPlugin(\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t): Promise<Detection> {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\tif (entry === undefined) {\n\t\t\treturn { ok: false, reasons: [`unknown plugin: ${pluginId}`] }\n\t\t}\n\t\tconst result = await invokeHook(\n\t\t\tentry,\n\t\t\tentry.plugin.detect,\n\t\t\tapi,\n\t\t\t\"detectForPlugin\",\n\t\t)\n\t\tif (result === undefined) {\n\t\t\treturn { ok: false, reasons: [\"detect threw an exception\"] }\n\t\t}\n\t\tif (result.ok) return { ok: true }\n\t\treturn { ok: false, reasons: result.reasons.slice() }\n\t}\n\n\tasync function detectForImportDir(\n\t\tapi: ResourceAPI,\n\t): Promise<PluginManifestId> {\n\t\tconst fallback = defaultPluginId()\n\t\tconst detectors = getRegistry()\n\t\t\t.getEnabled()\n\t\t\t.filter((e) => !e.builtin)\n\t\t\t.sort((a, b) => a.priority - b.priority)\n\t\tconst matched = await runDetectors(detectors, api, \"import detect\")\n\t\treturn matched ?? fallback\n\t}\n\n\tasync function buildFileList(\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t): Promise<SerializedFileList | undefined> {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\tif (entry === undefined) return undefined\n\t\tconst pluginResult = await invokeHook(\n\t\t\tentry,\n\t\t\tentry.plugin.listFiles,\n\t\t\tapi,\n\t\t\t\"listFiles\",\n\t\t)\n\t\tif (pluginResult === undefined) return undefined\n\t\tfor (const item of pluginResult) {\n\t\t\tif (typeof item === \"string\") continue\n\t\t\tif (typeof item === \"object\" && item !== null && !Array.isArray(item)) {\n\t\t\t\tfor (const value of Object.values(item)) {\n\t\t\t\t\tif (value === undefined) continue\n\t\t\t\t\tconst t = typeof value\n\t\t\t\t\tif (t === \"string\" || t === \"number\" || t === \"boolean\") continue\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Plugin ${String(pluginId)} returned an invalid file list item value type: ${t}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tthrow new Error(\n\t\t\t\t`Plugin ${String(pluginId)} returned an invalid file list item`,\n\t\t\t)\n\t\t}\n\t\treturn pluginResult as SerializedFileList\n\t}\n\n\tasync function resolveLocalCoverSource(\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t): Promise<string | undefined> {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\tif (entry === undefined) return undefined\n\t\treturn invokeHook(entry, entry.plugin.coverLocal, api, \"coverLocal\", \"warn\")\n\t}\n\n\tasync function runMetaHooks(\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t): Promise<PluginMetaHookResults> {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\tif (entry === undefined) return {}\n\t\tconst { manifest, plugin } = entry\n\t\tconst results: {\n\t\t\tsourceMeta?: { readonly value: unknown }\n\t\t\tsearchMeta?: { readonly value: unknown }\n\t\t} = {}\n\t\t/**\n\t\t * Run one capability-gated meta hook. A present result key means\n\t\t * the hook ran; a throw leaves the key absent (a failed hook must\n\t\t * not wipe previously computed meta — the patch stays stale).\n\t\t */\n\t\tasync function runMetaHook(\n\t\t\tname: \"sourceMeta\" | \"searchMeta\",\n\t\t): Promise<void> {\n\t\t\tconst hook = plugin[name]\n\t\t\tif (!guard.check(manifest, name) || hook === undefined) return\n\t\t\ttry {\n\t\t\t\tresults[name] = { value: await hook(api) }\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(hookFailureText(name, pluginId, err))\n\t\t\t}\n\t\t}\n\t\tawait runMetaHook(\"sourceMeta\")\n\t\tawait runMetaHook(\"searchMeta\")\n\t\treturn results\n\t}\n\n\tfunction entrySupportsImageHashes(entry: PluginRegistryEntry): boolean {\n\t\treturn (\n\t\t\tguard.check(entry.manifest, \"imageHashes\") &&\n\t\t\tentry.plugin.imageHashes !== undefined\n\t\t)\n\t}\n\n\tfunction supportsImageHashes(pluginId: PluginManifestId): boolean {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\treturn entry !== undefined && entrySupportsImageHashes(entry)\n\t}\n\n\tasync function runImageHashes(\n\t\tapi: ResourceAPI,\n\t\tpluginId: PluginManifestId,\n\t): Promise<ImageHashesResult | undefined> {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\tif (entry === undefined || !entrySupportsImageHashes(entry))\n\t\t\treturn undefined\n\t\treturn sanitizeImageHashes(\n\t\t\tawait invokeHook(entry, entry.plugin.imageHashes, api, \"imageHashes\"),\n\t\t\tpluginId,\n\t\t)\n\t}\n\n\tasync function runInstallHook(pluginId: PluginManifestId): Promise<void> {\n\t\tconst entry = getRegistry().getById(pluginId)\n\t\tif (entry === undefined) return\n\t\tawait invokeHook(\n\t\t\tentry,\n\t\t\tentry.plugin.onInstall,\n\t\t\tcreateInstallScopeApi(),\n\t\t\t\"onInstall\",\n\t\t\t\"warn\",\n\t\t)\n\t}\n\n\treturn {\n\t\tdefaultPluginId,\n\t\tgetEffectiveEntry,\n\t\tdetectFirstMatch,\n\t\trevalidate,\n\t\tdetectForPlugin,\n\t\tdetectForImportDir,\n\t\tbuildFileList,\n\t\tresolveLocalCoverSource,\n\t\trunMetaHooks,\n\t\tsupportsImageHashes,\n\t\trunImageHashes,\n\t\trunInstallHook,\n\t}\n}\n\nconst HEX_VALUE = /^[0-9a-f]+$/\n\n/**\n * Validate the raw hook result: a `{ hashes }` shape, hex string values,\n * and a bounded row count (excess entries are dropped with a warning —\n * the hook must never be able to bloat the hashes table).\n */\nfunction sanitizeImageHashes(\n\tvalue: unknown,\n\tpluginId: string,\n): ImageHashesResult | undefined {\n\tif (typeof value !== \"object\" || value === null) return undefined\n\tconst hashes = (value as { hashes?: unknown }).hashes\n\tif (!Array.isArray(hashes)) return undefined\n\tconst valid: ImageHash[] = []\n\tfor (const item of hashes) {\n\t\tif (typeof item !== \"object\" || item === null) continue\n\t\tconst { scope, type, value: hex, bits } = item as Record<string, unknown>\n\t\tif (\n\t\t\ttypeof scope === \"string\" &&\n\t\t\tscope.length > 0 &&\n\t\t\ttypeof type === \"string\" &&\n\t\t\ttype.length > 0 &&\n\t\t\ttypeof hex === \"string\" &&\n\t\t\tHEX_VALUE.test(hex)\n\t\t) {\n\t\t\t// `bits` always defaults to the hex length (4 bits per char) so\n\t\t\t// consumers can classify perceptual vs exact hashes by bits.\n\t\t\tvalid.push({\n\t\t\t\tscope,\n\t\t\t\ttype,\n\t\t\t\tvalue: hex,\n\t\t\t\tbits:\n\t\t\t\t\ttypeof bits === \"number\" && Number.isInteger(bits) && bits > 0\n\t\t\t\t\t\t? bits\n\t\t\t\t\t\t: hex.length * 4,\n\t\t\t})\n\t\t}\n\t}\n\tif (valid.length > MAX_IMAGE_HASHES_PER_RESOURCE) {\n\t\tconsole.warn(\n\t\t\t`[plugin-hooks] imageHashes for plugin ${pluginId} produced ${valid.length} entries — capped at ${MAX_IMAGE_HASHES_PER_RESOURCE}`,\n\t\t)\n\t\tvalid.length = MAX_IMAGE_HASHES_PER_RESOURCE\n\t}\n\treturn { hashes: valid }\n}\n","import type { ResourceAPI } from \"../types.ts\"\n\n/**\n * A hook invocation awaiting a worker response. Carries the call's\n * progress state plus the two timers policing it: the inactivity\n * watchdog and the absolute hard timeout.\n */\nexport type PendingCall = {\n\treadonly api: ResourceAPI\n\treadonly resolve: (value: unknown) => void\n\treadonly reject: (err: Error) => void\n\t/**\n\t * Host-side API dispatches currently running for this call. While any\n\t * are in flight the watchdog is paused — the plugin is blocked on the\n\t * host, not hung.\n\t */\n\tapiInFlight: number\n\twatchdog?: NodeJS.Timeout\n\thardTimer?: NodeJS.Timeout\n}\n\nexport type CallTimers = {\n\t/** (Re)arm the inactivity watchdog, replacing any previous one. */\n\treadonly armWatchdog: (call: PendingCall, onExpire: () => void) => void\n\t/** Arm the absolute per-invocation cap. */\n\treadonly armHardTimer: (call: PendingCall, onExpire: () => void) => void\n\t/** Suspend the watchdog while host-side API work is in flight. */\n\treadonly pauseWatchdog: (call: PendingCall) => void\n\t/** Cancel both timers of a call (settled or abandoned). */\n\treadonly clearCallTimers: (call: PendingCall) => void\n}\n\n/**\n * Timer bookkeeping for sandboxed hook invocations: the inactivity\n * watchdog (`watchdogMs`) and the absolute cap (`hardTimeoutMs`). Expiry\n * callbacks are supplied per arm by the owner — which knows the owning\n * state and worker — so this module stays decoupled from the sandbox\n * lifecycle.\n */\nexport function createCallTimers(opts: {\n\treadonly watchdogMs: number\n\treadonly hardTimeoutMs: number\n}): CallTimers {\n\tfunction armWatchdog(call: PendingCall, onExpire: () => void): void {\n\t\tif (call.watchdog !== undefined) clearTimeout(call.watchdog)\n\t\tcall.watchdog = setTimeout(onExpire, opts.watchdogMs)\n\t\tcall.watchdog.unref()\n\t}\n\n\tfunction armHardTimer(call: PendingCall, onExpire: () => void): void {\n\t\tif (call.hardTimer !== undefined) clearTimeout(call.hardTimer)\n\t\tcall.hardTimer = setTimeout(onExpire, opts.hardTimeoutMs)\n\t\tcall.hardTimer.unref()\n\t}\n\n\tfunction pauseWatchdog(call: PendingCall): void {\n\t\tif (call.watchdog === undefined) return\n\t\tclearTimeout(call.watchdog)\n\t\tcall.watchdog = undefined\n\t}\n\n\tfunction clearCallTimers(call: PendingCall): void {\n\t\tif (call.watchdog !== undefined) clearTimeout(call.watchdog)\n\t\tif (call.hardTimer !== undefined) clearTimeout(call.hardTimer)\n\t}\n\n\treturn { armWatchdog, armHardTimer, pauseWatchdog, clearCallTimers }\n}\n","/**\n * Wire protocol between the plugin sandbox host (server process) and the\n * sandbox child entry (`worker-entry.mjs`), which runs as a forked child\n * process with structured-clone IPC. The entry file is plain JS without\n * access to workspace TS sources, so it keeps its own copy of the\n * method/hook name lists — keep the two in sync (enforced by\n * `protocol.test.ts`).\n */\n\n/**\n * Plugin hook names the host can invoke, in contract order — defined in\n * `@hoardodile/sdk-types`, re-exported so host internals keep one import\n * point.\n */\nimport { HOOK_NAMES, type HookName } from \"@hoardodile/sdk-types\"\nimport { PLUGIN_ASSET_ERROR_NAMES } from \"@hoardodile/sdk-types/plugin-asset-limits\"\n\nexport { HOOK_NAMES, type HookName }\n\n/** ResourceAPI method names bridged over RPC. */\nexport const API_METHOD_NAMES = [\n\t\"logInfo\",\n\t\"logWarn\",\n\t\"logError\",\n\t\"listFileNames\",\n\t\"readFile\",\n\t\"statFile\",\n\t\"statFiles\",\n\t\"sniff\",\n\t\"probe\",\n\t\"hashBytes\",\n\t\"computeImageHashes\",\n\t\"listContainer\",\n\t\"extractArchive\",\n\t\"download\",\n\t\"statAsset\",\n\t\"readAsset\",\n\t\"deleteAsset\",\n] as const\n\nexport type ApiMethodName = (typeof API_METHOD_NAMES)[number]\n\n/** Fire-and-forget log methods — no response round-trip. */\nexport const LOG_METHOD_NAMES: ReadonlySet<ApiMethodName> = new Set([\n\t\"logInfo\",\n\t\"logWarn\",\n\t\"logError\",\n])\n\nexport type SerializedError = {\n\treadonly name: string\n\treadonly message: string\n\treadonly stack?: string\n\t/**\n\t * Machine-readable plugin error code (`DENIED`/`UNAVAILABLE`/`POLICY`)\n\t * carried verbatim on the wire — the single field the host side reads\n\t * to restore the plugin-facing `err.name`.\n\t */\n\treadonly code?: string\n}\n\n// -- host → worker --\n\nexport type LoadRequest = {\n\treadonly type: \"load\"\n\treadonly mainPath: string\n}\n\nexport type InvokeRequest = {\n\treadonly type: \"invoke\"\n\treadonly callId: number\n\treadonly hook: HookName\n}\n\nexport type ApiResponse = {\n\treadonly type: \"apiResult\"\n\treadonly apiCallId: number\n\treadonly ok: boolean\n\treadonly value?: unknown\n\treadonly error?: SerializedError\n}\n\n// -- worker → host --\n\nexport type LoadResponse = {\n\treadonly type: \"loaded\"\n\treadonly ok: boolean\n\treadonly hooks?: readonly HookName[]\n\treadonly error?: SerializedError\n}\n\nexport type InvokeResponse = {\n\treadonly type: \"result\"\n\treadonly callId: number\n\treadonly ok: boolean\n\treadonly value?: unknown\n\treadonly error?: SerializedError\n}\n\nexport type ApiRequest = {\n\treadonly type: \"api\"\n\treadonly callId: number\n\treadonly apiCallId: number\n\treadonly method: ApiMethodName\n\treadonly args: readonly unknown[]\n}\n\nexport type LogRequest = {\n\treadonly type: \"log\"\n\treadonly callId: number\n\treadonly method: \"logInfo\" | \"logWarn\" | \"logError\"\n\treadonly args: readonly unknown[]\n}\n\nexport type WorkerMessage =\n\t| LoadResponse\n\t| InvokeResponse\n\t| ApiRequest\n\t| LogRequest\n\nexport function serializeError(err: unknown): SerializedError {\n\tif (err instanceof Error) {\n\t\treturn {\n\t\t\tname: err.name,\n\t\t\tmessage: err.message,\n\t\t\tstack: err.stack,\n\t\t\t// The vocabulary errors carry their code in `name`; the wire\n\t\t\t// keeps an explicit `code` field so the receiving side never\n\t\t\t// re-parses or re-derives it.\n\t\t\tcode: isAssetErrorName(err.name) ? err.name : undefined,\n\t\t}\n\t}\n\treturn { name: \"Error\", message: String(err) }\n}\n\nexport function deserializeError(err: SerializedError): Error {\n\tconst e = new Error(err.message)\n\t// `code` is authoritative when present (the vocabulary survived);\n\t// `name` falls back for plain host errors.\n\te.name = err.code ?? err.name\n\tif (err.stack !== undefined) e.stack = err.stack\n\treturn e\n}\n\nfunction isAssetErrorName(name: string): boolean {\n\treturn (PLUGIN_ASSET_ERROR_NAMES as readonly string[]).includes(name)\n}\n","import { definePlugin } from \"../define-plugin.ts\"\nimport type { PluginDefinition, ResourceAPI } from \"../types.ts\"\nimport type { HookName } from \"./protocol.ts\"\n\nexport type InvokeFn = (hook: HookName, api: ResourceAPI) => Promise<unknown>\n\n/**\n * Build a contract-compliant {@link PluginDefinition} that forwards hooks\n * into a sandboxed worker. Only hooks the plugin actually implements are\n * exposed — presence is semantic (orchestrators branch on e.g.\n * `plugin.listFiles === undefined`).\n */\nexport function createSandboxedPlugin(\n\thooks: readonly HookName[],\n\tinvoke: InvokeFn,\n): PluginDefinition {\n\t// RPC boundary: hook signatures share the shape `(api) => Promise<unknown>`;\n\t// the concrete return type is enforced by the plugin contract at runtime.\n\tconst definition: Record<string, (api: ResourceAPI) => Promise<unknown>> = {}\n\tfor (const hook of hooks) {\n\t\tdefinition[hook] = async (api: ResourceAPI) => invoke(hook, api)\n\t}\n\tif (definition.detect === undefined) {\n\t\tdefinition.detect = async () => ({\n\t\t\tok: false,\n\t\t\treasons: [\"plugin does not implement detect()\"],\n\t\t})\n\t}\n\treturn definePlugin(definition as unknown as PluginDefinition)\n}\n","import { type ChildProcess, fork, spawn } from \"node:child_process\"\nimport { realpathSync } from \"node:fs\"\nimport { tmpdir } from \"node:os\"\nimport { dirname, resolve, sep } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport type {\n\tPluginAssetDeleteResult,\n\tPluginDownloadRequest,\n\tPluginDownloadResult,\n\tPluginPermissions,\n} from \"@hoardodile/sdk-types\"\nimport { CAPABILITY_BY_METHOD } from \"@hoardodile/sdk-types/plugin-capabilities\"\nimport type { PluginDefinition, ResourceAPI } from \"../types.ts\"\nimport { createCallTimers, type PendingCall } from \"./call-timers.ts\"\nimport {\n\tAPI_METHOD_NAMES,\n\ttype ApiMethodName,\n\tdeserializeError,\n\ttype HookName,\n\ttype InvokeRequest,\n\tLOG_METHOD_NAMES,\n\ttype SerializedError,\n\tserializeError,\n\ttype WorkerMessage,\n} from \"./protocol.ts\"\nimport { createSandboxedPlugin } from \"./sandboxed-plugin.ts\"\n\n/**\n * The plugin asset handler: the host-side implementation of the\n * `download`/`statAsset`/`readAsset`/`deleteAsset` ResourceAPI methods,\n * bound to the plugin id (the sandbox knows which plugin issued the\n * call). Wired by the app server; hosts without a consent channel (CLI,\n * workbench) omit it and the methods answer `UNAVAILABLE`.\n */\nexport type PluginAssetHandler = {\n\t/**\n\t * Download one request or a batch of requests. Answers with the\n\t * result array (batch shape) — this adapter unwraps the single\n\t * result for a single request (see {@link dispatchApi}).\n\t */\n\treadonly download: (\n\t\tpluginId: string,\n\t\trequest: PluginDownloadRequest | readonly PluginDownloadRequest[],\n\t) => Promise<readonly PluginDownloadResult[]>\n\treadonly statAsset: (\n\t\tpluginId: string,\n\t\tpath: string,\n\t) => Promise<{ readonly sizeBytes: number } | undefined>\n\treadonly readAsset: (pluginId: string, path: string) => Promise<Uint8Array>\n\treadonly deleteAsset: (\n\t\tpluginId: string,\n\t\tpath: string,\n\t) => Promise<PluginAssetDeleteResult>\n}\n\n/**\n * Kill a plugin sandbox process when an invocation neither returns nor\n * shows resource-API activity for this long. Hooks that keep calling the\n * API reset the watchdog continuously and never trip it; time spent inside\n * a host-side API call does not count as inactivity.\n */\nexport const PLUGIN_WATCHDOG_TIMEOUT_MS = 60_000\n\n/** Absolute cap for a single plugin hook invocation, regardless of activity. */\nexport const PLUGIN_HOOK_HARD_TIMEOUT_MS = 30 * 60_000\n\n/** V8 old-generation memory cap per plugin sandbox process, in MiB. */\nexport const PLUGIN_WORKER_MAX_OLD_SPACE_MB = 512\n\n/**\n * Absolute cap for one hook result crossing the IPC boundary back to the\n * host. Exceeding it turns the hook into an error instead of letting a\n * hostile bundle clone a giant payload into the host process.\n */\nexport const PLUGIN_MAX_RESULT_BYTES = 256 * 1024 * 1024\n\n/** Cap on `log*` messages per hook invocation (see {@link PluginSandboxConfig}). */\nexport const PLUGIN_MAX_LOGS_PER_HOOK = 1_000\n\n/** Cap on ResourceAPI calls per hook invocation (see {@link PluginSandboxConfig}). */\nexport const PLUGIN_MAX_API_CALLS_PER_HOOK = 100_000\n\n/**\n * Sandbox-gated ResourceAPI methods, read from the single capability\n * table (`@hoardodile/sdk-types/plugin-capabilities`) — a permission\n * declared there is enforced here automatically, and a sandbox method\n * listed there but not declared by the manifest is denied with the\n * capability's own name.\n *\n * The asset surface (the `download` capability) is additionally\n * intercepted before touching the ResourceAPI instance: its\n * implementation comes from the wired {@link PluginAssetHandler}, which\n * knows the owning plugin id.\n */\nconst ASSET_METHODS = new Set<ApiMethodName>([\n\t\"download\",\n\t\"statAsset\",\n\t\"readAsset\",\n\t\"deleteAsset\",\n])\n\n/**\n * Max sandbox spawns per plugin within {@link PLUGIN_WORKER_RESPAWN_WINDOW_MS}\n * before the plugin is degraded. It recovers automatically once the crash\n * window slides clean, or immediately on disable/rescan.\n */\nexport const PLUGIN_WORKER_MAX_RESPAWNS = 3\n\n/** Sliding window for {@link PLUGIN_WORKER_MAX_RESPAWNS}. */\nexport const PLUGIN_WORKER_RESPAWN_WINDOW_MS = 60_000\n\nexport type PluginSandboxConfig = {\n\t/**\n\t * Kill the sandbox process when an invocation neither returns nor shows\n\t * API activity for this long. Long-running hooks that keep calling the\n\t * resource API reset the watchdog continuously and never trip it;\n\t * time spent inside a host-side API call does not count as inactivity.\n\t */\n\treadonly watchdogMs: number\n\t/** Absolute per-invocation cap, regardless of activity. */\n\treadonly hardTimeoutMs: number\n\t/** V8 old-generation cap per sandbox process; exceeding it aborts it. */\n\treadonly maxOldSpaceMb: number\n\t/**\n\t * Max sandbox spawns per plugin within {@link respawnWindowMs} before the\n\t * plugin is degraded (all invocations reject). The plugin recovers\n\t * automatically once the crash window slides clean; `unloadPlugin`\n\t * (disable or rescan) resets the budget immediately.\n\t */\n\treadonly maxRespawns: number\n\treadonly respawnWindowMs: number\n\t/** Absolute cap for one hook result returning to the host. */\n\treadonly maxResultBytes: number\n\t/**\n\t * Cap on `log*` messages per hook invocation; exceeding it fails the\n\t * hook (log messages reset the watchdog, so a log flood would\n\t * otherwise stay alive until the hard timeout).\n\t */\n\treadonly maxLogsPerHook: number\n\t/**\n\t * Cap on ResourceAPI calls per hook invocation; exceeding it fails the\n\t * hook. Sized generously so large per-file scans (hashBytes over tens\n\t * of thousands of files) keep working — the cap bounds a runaway RPC\n\t * fan-out that would otherwise pin the host's CPU.\n\t */\n\treadonly maxApiCallsPerHook: number\n\t/**\n\t * Permission-model flag to pass to the sandbox child, or `undefined` to\n\t * probe the running Node for the first accepted name. Tests override\n\t * this to exercise the fail-closed path.\n\t */\n\treadonly permissionFlag?: string\n\t/**\n\t * Host-managed plugin vault directory (`<plugin-dir>/vault/`, or the\n\t * versioned-storage vault for dev plugins). When set, the child gets\n\t * an extra fs-read grant and the module policy gate allows loading\n\t * files from it — downloaded runtimes (JS/WASM) importable from the\n\t * vault are the point of the asset API. A function form lets one\n\t * shared sandbox resolve the path per plugin (disk vs dev plugins\n\t * store their vault differently).\n\t */\n\treadonly assetVaultDir?: string | ((pluginId: string) => string | undefined)\n\t/**\n\t * Plugin asset handler wired by the app server. Absent → the asset\n\t * methods answer `UNAVAILABLE` (CLI, workbench, tests without a\n\t * consent channel).\n\t */\n\treadonly pluginAssets?: PluginAssetHandler\n}\n\nexport const DEFAULT_SANDBOX_CONFIG: PluginSandboxConfig = {\n\twatchdogMs: PLUGIN_WATCHDOG_TIMEOUT_MS,\n\thardTimeoutMs: PLUGIN_HOOK_HARD_TIMEOUT_MS,\n\tmaxOldSpaceMb: PLUGIN_WORKER_MAX_OLD_SPACE_MB,\n\tmaxRespawns: PLUGIN_WORKER_MAX_RESPAWNS,\n\trespawnWindowMs: PLUGIN_WORKER_RESPAWN_WINDOW_MS,\n\tmaxResultBytes: PLUGIN_MAX_RESULT_BYTES,\n\tmaxLogsPerHook: PLUGIN_MAX_LOGS_PER_HOOK,\n\tmaxApiCallsPerHook: PLUGIN_MAX_API_CALLS_PER_HOOK,\n}\n\nexport type PluginSandbox = {\n\t/**\n\t * Register and load a plugin bundle. With `eager` the sandbox process\n\t * stays alive; without it the hook list is probed and the process\n\t * immediately idles (it respawns lazily on first invocation — disabled\n\t * plugins still serve their bound resources without holding a process).\n\t *\n\t * Reloading an already-registered id keeps the previous process alive\n\t * until the new bundle loads successfully — a failed reload returns\n\t * the previous definition instead of stranding a disposed registry.\n\t *\n\t * Returns `undefined` when the bundle cannot be loaded and there is no\n\t * previous definition to fall back on (already logged) — callers fall\n\t * back to a failing plugin.\n\t */\n\treadonly loadPlugin: (opts: {\n\t\treadonly id: string\n\t\treadonly mainPath: string\n\t\treadonly eager: boolean\n\t\t/**\n\t\t * Manifest permissions the sandbox enforces on every API call\n\t\t * (e.g. the `container` gate). Absent keys count as denied here,\n\t\t * so a caller that omits this opts into the strictest view.\n\t\t */\n\t\treadonly permissions?: PluginPermissions\n\t}) => Promise<PluginDefinition | undefined>\n\t/**\n\t * Terminate the plugin's sandbox process (if any) and reset its respawn\n\t * budget. The hook list stays known; the next invocation lazily respawns.\n\t */\n\treadonly unloadPlugin: (id: string) => void\n\t/**\n\t * Terminate and forget every plugin whose id is not in `keepIds`.\n\t * Registered ids keep their processes — used by the loader after a\n\t * successful reload to free plugins that left the registry.\n\t */\n\treadonly disposeExcept: (keepIds: ReadonlySet<string>) => Promise<void>\n\t/** Terminate every process and forget all plugins. Pending invocations reject. */\n\treadonly disposeAll: () => Promise<void>\n}\n\ntype LoadWaiter = {\n\treadonly resolve: () => void\n\treadonly reject: (err: Error) => void\n}\n\ntype PluginState = {\n\treadonly id: string\n\treadonly mainPath: string\n\treadonly permissions: PluginPermissions | undefined\n\tchild: ChildProcess | undefined\n\thooks: readonly HookName[] | undefined\n\tloading: Promise<void> | undefined\n\tloadWaiter: LoadWaiter | undefined\n\treadonly pending: Map<number, PendingCall>\n\trespawnTimes: number[]\n\tdegraded: boolean\n\tdisposed: boolean\n}\n\n/**\n * Flag names the Node permission model accepted over its life: it was\n * introduced as `--experimental-permission` and stabilized under\n * `--permission`. Probe the running binary once and reuse the verdict.\n */\nconst PERMISSION_FLAG_CANDIDATES = [\"--permission\", \"--experimental-permission\"]\n\nlet permissionFlagPromise: Promise<string | undefined> | undefined\n\nasync function resolvePermissionFlag(): Promise<string | undefined> {\n\tpermissionFlagPromise ??= probePermissionFlag()\n\treturn permissionFlagPromise\n}\n\n/**\n * Spawn a probe child for each candidate flag name and keep the first one\n * the runtime accepts. A flag the runtime does not know makes `node` exit\n * non-zero before running the script, so an `exit 0` verdict is the proof.\n */\nasync function probePermissionFlag(): Promise<string | undefined> {\n\tfor (const flag of PERMISSION_FLAG_CANDIDATES) {\n\t\tif (await acceptsFlag(flag)) return flag\n\t}\n\treturn undefined\n}\n\nfunction acceptsFlag(flag: string): Promise<boolean> {\n\treturn new Promise((resolveProbe) => {\n\t\tconst probe = spawn(\n\t\t\tprocess.execPath,\n\t\t\t[flag, `--allow-fs-read=${tmpdir()}`, \"-e\", \"process.exit(0)\"],\n\t\t\t{ stdio: \"ignore\", timeout: 10_000 },\n\t\t)\n\t\tprobe.on(\"exit\", (code) => resolveProbe(code === 0))\n\t\tprobe.on(\"error\", () => resolveProbe(false))\n\t})\n}\n\nexport function createPluginSandbox(\n\tconfig: PluginSandboxConfig = DEFAULT_SANDBOX_CONFIG,\n): PluginSandbox {\n\tconst states = new Map<string, PluginState>()\n\tlet nextCallId = 1\n\tconst timers = createCallTimers({\n\t\twatchdogMs: config.watchdogMs,\n\t\thardTimeoutMs: config.hardTimeoutMs,\n\t})\n\n\tasync function loadPlugin(opts: {\n\t\tid: string\n\t\tmainPath: string\n\t\teager: boolean\n\t\tpermissions?: PluginPermissions\n\t}): Promise<PluginDefinition | undefined> {\n\t\tconst previous = states.get(opts.id)\n\t\tconst state: PluginState = {\n\t\t\tid: opts.id,\n\t\t\tmainPath: opts.mainPath,\n\t\t\tpermissions: opts.permissions,\n\t\t\tchild: undefined,\n\t\t\thooks: undefined,\n\t\t\tloading: undefined,\n\t\t\tloadWaiter: undefined,\n\t\t\tpending: new Map(),\n\t\t\trespawnTimes: [],\n\t\t\tdegraded: false,\n\t\t\tdisposed: false,\n\t\t}\n\t\tstates.set(opts.id, state)\n\t\ttry {\n\t\t\tawait ensureLoaded(state)\n\t\t} catch (err) {\n\t\t\tconsole.error(`[plugin-sandbox] ${opts.id}: failed to load main.js`, err)\n\t\t\tstate.disposed = true\n\t\t\t// A concurrent loadPlugin for the same id may have replaced this\n\t\t\t// state — only act when the map still owns it.\n\t\t\tif (states.get(opts.id) !== state) return undefined\n\t\t\tif (previous !== undefined) {\n\t\t\t\t// The previous process never stopped being healthy — restore\n\t\t\t\t// it so a failed reload can't strand a disposed registry.\n\t\t\t\tprevious.disposed = false\n\t\t\t\tstates.set(opts.id, previous)\n\t\t\t\treturn createSandboxedPlugin(\n\t\t\t\t\tprevious.hooks ?? [\"detect\"],\n\t\t\t\t\t(hook, api) => invoke(previous, hook, api),\n\t\t\t\t)\n\t\t\t}\n\t\t\tstates.delete(opts.id)\n\t\t\treturn undefined\n\t\t}\n\t\t// The new bundle loaded: retire the previous process only now —\n\t\t// in-flight invocations against it settle normally, and a failed\n\t\t// reload above never killed it in the first place.\n\t\tif (previous !== undefined) {\n\t\t\tprevious.disposed = true\n\t\t\tawait teardownChild(previous)\n\t\t}\n\t\tif (!opts.eager) {\n\t\t\t// AWAITED, not fire-and-forget: the hook list was probed by\n\t\t\t// this load, and the loader returns the sandboxed definition\n\t\t\t// the moment we resolve. A caller that invokes a hook\n\t\t\t// immediately after (e.g. the post-install `onInstall`) must\n\t\t\t// not find a half-alive child: `ensureLoaded` would see the\n\t\t\t// not-yet-torn-down child and post its invocation into a\n\t\t\t// dying process (rejected as \"worker stopped\").\n\t\t\tawait teardownChild(state)\n\t\t}\n\t\treturn createSandboxedPlugin(state.hooks ?? [\"detect\"], (hook, api) =>\n\t\t\tinvoke(state, hook, api),\n\t\t)\n\t}\n\n\tfunction unloadPlugin(id: string): void {\n\t\tconst state = states.get(id)\n\t\tif (state === undefined) return\n\t\tteardownChild(state)\n\t\tstate.respawnTimes = []\n\t\tstate.degraded = false\n\t}\n\n\tasync function disposeAll(): Promise<void> {\n\t\tconst tasks: Promise<void>[] = []\n\t\tfor (const state of states.values()) {\n\t\t\tstate.disposed = true\n\t\t\ttasks.push(teardownChild(state))\n\t\t}\n\t\tstates.clear()\n\t\tawait Promise.all(tasks)\n\t}\n\n\tasync function disposeExcept(keepIds: ReadonlySet<string>): Promise<void> {\n\t\tconst tasks: Promise<void>[] = []\n\t\tfor (const [id, state] of states) {\n\t\t\tif (keepIds.has(id)) continue\n\t\t\tstate.disposed = true\n\t\t\tstates.delete(id)\n\t\t\ttasks.push(teardownChild(state))\n\t\t}\n\t\tawait Promise.all(tasks)\n\t}\n\n\t// -- child lifecycle --\n\n\t/**\n\t * Keep the sandbox child referenced only while it has work pending\n\t * (loading or an in-flight invocation); idle children must not hold\n\t * the host process open, but an unref'd child in a host with nothing\n\t * else running (one-shot CLI, dist smoke) would otherwise let the\n\t * process exit before a slow sandbox boot answered.\n\t */\n\tfunction syncRef(state: PluginState): void {\n\t\tconst child = state.child\n\t\tif (child === undefined) return\n\t\tif (\n\t\t\tstate.loading !== undefined ||\n\t\t\tstate.loadWaiter !== undefined ||\n\t\t\tstate.pending.size > 0\n\t\t) {\n\t\t\tchild.ref()\n\t\t} else {\n\t\t\tchild.unref()\n\t\t}\n\t}\n\n\tfunction ensureLoaded(state: PluginState): Promise<void> {\n\t\tif (state.child !== undefined) return Promise.resolve()\n\t\tstate.loading ??= spawnAndLoad(state).finally(() => {\n\t\t\tstate.loading = undefined\n\t\t})\n\t\treturn state.loading\n\t}\n\n\tasync function spawnAndLoad(state: PluginState): Promise<void> {\n\t\tconst now = Date.now()\n\t\tstate.respawnTimes = state.respawnTimes.filter(\n\t\t\t(t) => now - t < config.respawnWindowMs,\n\t\t)\n\t\tif (state.respawnTimes.length >= config.maxRespawns) {\n\t\t\tstate.degraded = true\n\t\t\tthrow new Error(\n\t\t\t\t`plugin ${state.id} unavailable: sandbox respawned ${config.maxRespawns} times within ${config.respawnWindowMs}ms`,\n\t\t\t)\n\t\t}\n\t\tstate.respawnTimes.push(now)\n\n\t\tconst permissionFlag =\n\t\t\tconfig.permissionFlag ?? (await resolvePermissionFlag())\n\t\tif (permissionFlag === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`plugin ${state.id} unavailable: this Node build has no permission-model flag (tried ${PERMISSION_FLAG_CANDIDATES.map((f) => `\"${f}\"`).join(\", \")}); refusing to run plugin code unsandboxed`,\n\t\t\t)\n\t\t}\n\n\t\t// Resolve the sandbox entry through the package's own exports map\n\t\t// (`@hoardodile/host/worker-entry`) when that package is installed.\n\t\t// The sandbox module also gets inlined into the server bundle, where\n\t\t// there is no `@hoardodile/host` on disk — then we fall back to\n\t\t// `dist/chunks/worker-entry.mjs` next to the emitting chunk.\n\t\t// Kept out of a `new URL()` literal so Vite does not detect a browser\n\t\t// worker here: it would bundle worker-entry.mjs with node: builtins\n\t\t// shimmed out and rewrite the URL to an unusable /assets/ path. The\n\t\t// entry ships untransformed and must run as plain ESM in a child\n\t\t// process.\n\t\tconst entryPath = fileURLToPath(resolveWorkerEntryUrl())\n\t\tconst pluginDir = dirname(state.mainPath)\n\t\t// The ESM loader passes every module path through realpathSync under\n\t\t// `--permission`, so the grants must use the canonical on-disk form —\n\t\t// a plugin dir reached through a symlink (macOS `/tmp`→`/private/tmp`,\n\t\t// WSL paths, junctions) would otherwise resolve outside its own grant\n\t\t// and fail to load with ERR_ACCESS_DENIED.\n\t\tconst realEntryPath = realPathOrResolve(entryPath)\n\t\tconst realPluginDir = realPathOrResolve(pluginDir)\n\t\tconst realMainPath = realPathOrResolve(state.mainPath)\n\n\t\t// The child's grants are minimal: one fs-read allowlist (its own\n\t\t// directory, plus the entry file it must load, plus the host-managed\n\t\t// asset vault when one is wired), a memory cap, and nothing else —\n\t\t// no fs write, no child processes, no worker threads, no native\n\t\t// addons. The module policy hook (registered inside the entry via\n\t\t// `registerHooks`) closes the remaining surface: nothing outside the\n\t\t// plugin directory (and the vault) and no `node:` builtins except\n\t\t// `node:url` can be imported.\n\t\tconst fsReadGrants = [`${realPluginDir}${sep}`, realEntryPath]\n\t\tconst assetVaultDir =\n\t\t\ttypeof config.assetVaultDir === \"function\"\n\t\t\t\t? config.assetVaultDir(state.id)\n\t\t\t\t: config.assetVaultDir\n\t\tconst realVaultDir =\n\t\t\tassetVaultDir === undefined ? undefined : realPathOrResolve(assetVaultDir)\n\t\tif (realVaultDir !== undefined) {\n\t\t\tfsReadGrants.push(`${realVaultDir}${sep}`)\n\t\t}\n\t\tconst child = fork(\n\t\t\trealEntryPath,\n\t\t\t[realPluginDir, realEntryPath, realVaultDir].filter(\n\t\t\t\t(v): v is string => v !== undefined,\n\t\t\t),\n\t\t\t{\n\t\t\t\texecArgv: [\n\t\t\t\t\tpermissionFlag,\n\t\t\t\t\t...fsReadGrants.map((p) => `--allow-fs-read=${p}`),\n\t\t\t\t\t`--max-old-space-size=${config.maxOldSpaceMb}`,\n\t\t\t\t],\n\t\t\t\tserialization: \"advanced\",\n\t\t\t\t// Child stderr is inherited (self-check failures must surface);\n\t\t\t\t// plugin console output is dropped — logs flow over the RPC.\n\t\t\t\tstdio: [\"ignore\", \"ignore\", \"inherit\", \"ipc\"],\n\t\t\t\tenv: {\n\t\t\t\t\t...process.env,\n\t\t\t\t\tHOARDODILE_PLUGIN_MAX_RESULT_BYTES: String(config.maxResultBytes),\n\t\t\t\t\tHOARDODILE_PLUGIN_MAX_LOGS_PER_HOOK: String(config.maxLogsPerHook),\n\t\t\t\t\tHOARDODILE_PLUGIN_MAX_API_CALLS_PER_HOOK: String(\n\t\t\t\t\t\tconfig.maxApiCallsPerHook,\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\t// The sandbox must never hold the process open on its own — but\n\t\t// while a load/invocation is pending the child is referenced (see\n\t\t// syncRef) so a short-lived host cannot exit mid-boot.\n\t\tchild.unref()\n\t\tstate.child = child\n\t\tsyncRef(state)\n\n\t\tchild.on(\"message\", (msg: WorkerMessage) =>\n\t\t\thandleMessage(state, child, msg),\n\t\t)\n\t\tchild.on(\"error\", (err: unknown) => failChild(state, child, asError(err)))\n\t\tchild.on(\"exit\", (code) => {\n\t\t\tif (state.child === child) {\n\t\t\t\tfailChild(\n\t\t\t\t\tstate,\n\t\t\t\t\tchild,\n\t\t\t\t\tnew Error(`plugin ${state.id} worker exited (code ${code})`),\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\n\t\tconst loaded = new Promise<void>((resolveLoaded, reject) => {\n\t\t\tstate.loadWaiter = { resolve: resolveLoaded, reject }\n\t\t})\n\t\ttry {\n\t\t\tchild.send({ type: \"load\", mainPath: realMainPath } satisfies {\n\t\t\t\ttype: \"load\"\n\t\t\t\tmainPath: string\n\t\t\t})\n\t\t} catch (err) {\n\t\t\t// The channel never opened (spawn failed hard) — the waiter\n\t\t\t// would hang forever; tear the child down and fail the load.\n\t\t\tawait teardownChild(state, asError(err))\n\t\t\tthrow err\n\t\t}\n\t\ttry {\n\t\t\tawait loaded\n\t\t} catch (err) {\n\t\t\t// A plugin whose main.js throws at import reports `loaded: ok:false`\n\t\t\t// and keeps idling — terminate the child so it never outlives\n\t\t\t// its owning state (failChild already covered error/exit).\n\t\t\tawait teardownChild(state)\n\t\t\tthrow err\n\t\t} finally {\n\t\t\tstate.loadWaiter = undefined\n\t\t}\n\t\t// Loaded and (for eager plugins) alive: an idle sandbox must not\n\t\t// hold the host open, but it stays referenced while invocations\n\t\t// are in flight.\n\t\tsyncRef(state)\n\t}\n\n\t/**\n\t * Terminate the child without touching the respawn budget. Rejects\n\t * the load waiter and every pending call with `err` (or the standard\n\t * \"worker stopped\" error) and clears the child slot; a no-op when\n\t * there is no child.\n\t */\n\tfunction teardownChild(state: PluginState, err?: Error): Promise<void> {\n\t\tconst child = state.child\n\t\tstate.child = undefined\n\t\tstate.loading = undefined\n\t\tconst stopErr = err ?? new Error(`plugin ${state.id} worker stopped`)\n\t\tstate.loadWaiter?.reject(stopErr)\n\t\tstate.loadWaiter = undefined\n\t\trejectAllPending(state, stopErr)\n\t\tif (child === undefined) return Promise.resolve()\n\t\t// SIGTERM on POSIX, TerminateProcess on Windows. Pending calls were\n\t\t// already rejected; stale messages can no longer resolve anything\n\t\t// because the slot is cleared.\n\t\tchild.kill()\n\t\treturn Promise.resolve()\n\t}\n\n\tfunction failChild(\n\t\tstate: PluginState,\n\t\tchild: ChildProcess,\n\t\terr: Error,\n\t): void {\n\t\tif (state.child !== child) return // stale event from a replaced child\n\t\tvoid teardownChild(state, err)\n\t}\n\n\tfunction rejectAllPending(state: PluginState, err: Error): void {\n\t\tfor (const call of state.pending.values()) {\n\t\t\ttimers.clearCallTimers(call)\n\t\t\tcall.reject(err)\n\t\t}\n\t\tstate.pending.clear()\n\t}\n\n\t// -- invocation --\n\n\tasync function invoke(\n\t\tstate: PluginState,\n\t\thook: HookName,\n\t\tapi: ResourceAPI,\n\t): Promise<unknown> {\n\t\tif (state.disposed) {\n\t\t\tthrow new Error(`plugin ${state.id} sandbox disposed`)\n\t\t}\n\t\tif (state.degraded) {\n\t\t\t// Auto-recover once every crash in the budget has aged out of the\n\t\t\t// respawn window — spawnAndLoad would accept a spawn again anyway.\n\t\t\tconst now = Date.now()\n\t\t\tstate.respawnTimes = state.respawnTimes.filter(\n\t\t\t\t(t) => now - t < config.respawnWindowMs,\n\t\t\t)\n\t\t\tif (state.respawnTimes.length === 0) {\n\t\t\t\tstate.degraded = false\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`plugin ${state.id} unavailable: sandbox crashed repeatedly`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tawait ensureLoaded(state)\n\t\tconst child = state.child\n\t\tif (child === undefined) {\n\t\t\tthrow new Error(`plugin ${state.id} sandbox unavailable`)\n\t\t}\n\n\t\ttry {\n\t\t\treturn await invokeOnChild(state, child, hook, api)\n\t\t} finally {\n\t\t\t// An install hook is one-shot and the host may immediately\n\t\t\t// re-commit the same plugin (an update): a kept-alive worker\n\t\t\t// holding the plugin directory (Windows keeps directory ops\n\t\t\t// pinned while a child is alive) would make the vault-move\n\t\t\t// during the next commit fail with EPERM. Drop the child right\n\t\t\t// after an `onInstall`; the next invocation respawns it.\n\t\t\tif (hook === \"onInstall\") {\n\t\t\t\tstate.respawnTimes = []\n\t\t\t\tawait teardownChild(state)\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction invokeOnChild(\n\t\tstate: PluginState,\n\t\tchild: ChildProcess,\n\t\thook: HookName,\n\t\tapi: ResourceAPI,\n\t): Promise<unknown> {\n\t\tconst callId = nextCallId++\n\t\treturn new Promise((resolveCall, reject) => {\n\t\t\tconst call: PendingCall = {\n\t\t\t\tapi,\n\t\t\t\tresolve: resolveCall,\n\t\t\t\treject,\n\t\t\t\tapiInFlight: 0,\n\t\t\t}\n\t\t\tstate.pending.set(callId, call)\n\t\t\tsyncRef(state)\n\t\t\ttimers.armWatchdog(\n\t\t\t\tcall,\n\t\t\t\tfailCall(\n\t\t\t\t\tstate,\n\t\t\t\t\tchild,\n\t\t\t\t\tcallId,\n\t\t\t\t\t`hung: no activity for ${config.watchdogMs}ms`,\n\t\t\t\t),\n\t\t\t)\n\t\t\ttimers.armHardTimer(\n\t\t\t\tcall,\n\t\t\t\tfailCall(\n\t\t\t\t\tstate,\n\t\t\t\t\tchild,\n\t\t\t\t\tcallId,\n\t\t\t\t\t`exceeded hard timeout ${config.hardTimeoutMs}ms`,\n\t\t\t\t),\n\t\t\t)\n\t\t\ttry {\n\t\t\t\tchild.send({\n\t\t\t\t\ttype: \"invoke\",\n\t\t\t\t\tcallId,\n\t\t\t\t\thook,\n\t\t\t\t} satisfies InvokeRequest)\n\t\t\t} catch (err) {\n\t\t\t\tstate.pending.delete(callId)\n\t\t\t\tsyncRef(state)\n\t\t\t\ttimers.clearCallTimers(call)\n\t\t\t\treject(err instanceof Error ? err : new Error(String(err)))\n\t\t\t}\n\t\t})\n\t}\n\n\tfunction failCall(\n\t\tstate: PluginState,\n\t\tchild: ChildProcess,\n\t\tcallId: number,\n\t\treason: string,\n\t): () => void {\n\t\treturn () =>\n\t\t\tfailChild(\n\t\t\t\tstate,\n\t\t\t\tchild,\n\t\t\t\tnew Error(`plugin ${state.id} ${reason} (call ${callId})`),\n\t\t\t)\n\t}\n\n\t// -- message handling --\n\n\tfunction handleMessage(\n\t\tstate: PluginState,\n\t\tchild: ChildProcess,\n\t\tmsg: WorkerMessage,\n\t): void {\n\t\t// Stale child from a replaced spawn: its messages must never\n\t\t// resolve the current child's load waiter or pending calls.\n\t\tif (state.child !== child) return\n\t\tif (msg === null || typeof msg !== \"object\") return\n\t\tswitch (msg.type) {\n\t\t\tcase \"loaded\": {\n\t\t\t\tconst waiter = state.loadWaiter\n\t\t\t\tstate.loadWaiter = undefined\n\t\t\t\tif (msg.ok) {\n\t\t\t\t\tstate.hooks = msg.hooks ?? [\"detect\"]\n\t\t\t\t\twaiter?.resolve()\n\t\t\t\t} else {\n\t\t\t\t\twaiter?.reject(\n\t\t\t\t\t\tdeserializeError(\n\t\t\t\t\t\t\tmsg.error ?? { name: \"Error\", message: \"plugin load failed\" },\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase \"result\": {\n\t\t\t\tconst call = state.pending.get(msg.callId)\n\t\t\t\tif (call === undefined) return\n\t\t\t\tstate.pending.delete(msg.callId)\n\t\t\t\tsyncRef(state)\n\t\t\t\ttimers.clearCallTimers(call)\n\t\t\t\tif (msg.ok) {\n\t\t\t\t\tcall.resolve(msg.value)\n\t\t\t\t} else {\n\t\t\t\t\tcall.reject(\n\t\t\t\t\t\tdeserializeError(\n\t\t\t\t\t\t\tmsg.error ?? { name: \"Error\", message: \"hook failed\" },\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase \"api\": {\n\t\t\t\tconst call = state.pending.get(msg.callId)\n\t\t\t\tif (call === undefined) return\n\t\t\t\t// Pause the watchdog while the host executes the API call —\n\t\t\t\t// a slow readFile/probeVideo is host work, not a hung plugin.\n\t\t\t\tcall.apiInFlight += 1\n\t\t\t\ttimers.pauseWatchdog(call)\n\t\t\t\tvoid dispatchApi(\n\t\t\t\t\tchild,\n\t\t\t\t\tstate,\n\t\t\t\t\tmsg.callId,\n\t\t\t\t\tmsg.apiCallId,\n\t\t\t\t\tcall,\n\t\t\t\t\tmsg.method,\n\t\t\t\t\tmsg.args,\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase \"log\": {\n\t\t\t\tconst call = state.pending.get(msg.callId)\n\t\t\t\tif (call === undefined) return\n\t\t\t\tif (call.apiInFlight === 0) {\n\t\t\t\t\ttimers.armWatchdog(\n\t\t\t\t\t\tcall,\n\t\t\t\t\t\tfailCall(\n\t\t\t\t\t\t\tstate,\n\t\t\t\t\t\t\tchild,\n\t\t\t\t\t\t\tmsg.callId,\n\t\t\t\t\t\t\t`hung: no activity for ${config.watchdogMs}ms`,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tdispatchLog(state.id, msg.method, msg.args)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function dispatchApi(\n\t\tchild: ChildProcess,\n\t\tstate: PluginState,\n\t\tcallId: number,\n\t\tapiCallId: number,\n\t\tcall: PendingCall,\n\t\tmethod: ApiMethodName,\n\t\targs: readonly unknown[],\n\t): Promise<void> {\n\t\tconst respond = (\n\t\t\tok: boolean,\n\t\t\tvalue?: unknown,\n\t\t\terror?: SerializedError,\n\t\t): void => {\n\t\t\t// The child may have died while the API call was in flight.\n\t\t\tif (state.child !== child) return\n\t\t\ttry {\n\t\t\t\tchild.send({ type: \"apiResult\", apiCallId, ok, value, error })\n\t\t\t} catch {\n\t\t\t\t// Channel closed — nothing to deliver.\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tif (!isApiMethod(method) || LOG_METHOD_NAMES.has(method)) {\n\t\t\t\trespond(false, undefined, {\n\t\t\t\t\tname: \"Error\",\n\t\t\t\t\tmessage: `unknown API method: ${String(method)}`,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Manifest permission gate, read from the single capability\n\t\t\t// table: every sandbox-gated method maps to its capability\n\t\t\t// and is denied unless the manifest declared it. The asset\n\t\t\t// surface then routes to the wired handler with the owning\n\t\t\t// plugin id — the ResourceAPI instance itself has no plugin\n\t\t\t// identity (a shared API serves every plugin's hooks).\n\t\t\tconst capability = CAPABILITY_BY_METHOD.get(method)\n\t\t\tif (\n\t\t\t\tcapability !== undefined &&\n\t\t\t\tstate.permissions?.[capability] !== true\n\t\t\t) {\n\t\t\t\trespond(false, undefined, {\n\t\t\t\t\tname: \"POLICY\",\n\t\t\t\t\tcode: \"POLICY\",\n\t\t\t\t\tmessage: `${capability} permission denied for plugin ${state.id} — declare \"${capability}\": true in the manifest to use ${method}()`,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (ASSET_METHODS.has(method)) {\n\t\t\t\tconst handler = config.pluginAssets\n\t\t\t\tif (handler === undefined) {\n\t\t\t\t\trespond(false, undefined, {\n\t\t\t\t\t\tname: \"UNAVAILABLE\",\n\t\t\t\t\t\tcode: \"UNAVAILABLE\",\n\t\t\t\t\t\tmessage: `${method}() is unavailable on this host — plugin asset downloads need the app server runtime`,\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// RPC boundary: the child-side proxy forwards args verbatim,\n\t\t\t\t// so the contract order is guaranteed (request object or\n\t\t\t\t// request array for `download`, string path for the rest).\n\t\t\t\tif (method === \"download\") {\n\t\t\t\t\tconst request = args[0] as\n\t\t\t\t\t\t| PluginDownloadRequest\n\t\t\t\t\t\t| readonly PluginDownloadRequest[]\n\t\t\t\t\tconst results = await handler.download(state.id, request)\n\t\t\t\t\t// Wire rule: array in → array out, single in → single out.\n\t\t\t\t\trespond(true, Array.isArray(request) ? results : results[0])\n\t\t\t\t} else if (method === \"statAsset\") {\n\t\t\t\t\trespond(true, await handler.statAsset(state.id, args[0] as string))\n\t\t\t\t} else if (method === \"readAsset\") {\n\t\t\t\t\trespond(true, await handler.readAsset(state.id, args[0] as string))\n\t\t\t\t} else {\n\t\t\t\t\trespond(true, await handler.deleteAsset(state.id, args[0] as string))\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// RPC boundary: the child-side proxy is generated from the same\n\t\t\t// method list, so args always arrive in contract order.\n\t\t\tconst fn = call.api[method] as (...a: readonly unknown[]) => unknown\n\t\t\trespond(true, await fn(...args))\n\t\t} catch (err) {\n\t\t\trespond(false, undefined, serializeError(err))\n\t\t} finally {\n\t\t\tcall.apiInFlight -= 1\n\t\t\t// Resume the watchdog once the host-side work for this call\n\t\t\t// has drained and the call is still alive.\n\t\t\tif (call.apiInFlight === 0 && state.pending.get(callId) === call) {\n\t\t\t\ttimers.armWatchdog(\n\t\t\t\t\tcall,\n\t\t\t\t\tfailCall(\n\t\t\t\t\t\tstate,\n\t\t\t\t\t\tchild,\n\t\t\t\t\t\tcallId,\n\t\t\t\t\t\t`hung: no activity for ${config.watchdogMs}ms`,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Plugin log sink. The child-side proxy forwards log calls here, where\n\t * the owning plugin id is known — ResourceAPI.log* stay no-ops because a\n\t * shared API instance (e.g. one detect pass fanning out to every plugin)\n\t * cannot attribute a log line to the plugin that emitted it.\n\t */\n\tfunction dispatchLog(\n\t\tpluginId: string,\n\t\tmethod: \"logInfo\" | \"logWarn\" | \"logError\",\n\t\targs: readonly unknown[],\n\t): void {\n\t\tconst message = typeof args[0] === \"string\" ? args[0] : String(args[0])\n\t\tconst data = isPlainRecord(args[1]) ? args[1] : undefined\n\t\tconst line = `[plugin:${pluginId}] ${message}`\n\t\tconst extra = data === undefined ? [] : [data]\n\t\ttry {\n\t\t\tif (method === \"logInfo\") console.log(line, ...extra)\n\t\t\telse if (method === \"logWarn\") console.warn(line, ...extra)\n\t\t\telse console.error(line, ...extra)\n\t\t} catch {\n\t\t\t// Logging must never break the host.\n\t\t}\n\t}\n\n\treturn { loadPlugin, unloadPlugin, disposeExcept, disposeAll }\n}\n\nfunction isApiMethod(name: unknown): name is ApiMethodName {\n\treturn (\n\t\ttypeof name === \"string\" &&\n\t\t(API_METHOD_NAMES as readonly string[]).includes(name)\n\t)\n}\n\nfunction asError(value: unknown): Error {\n\treturn value instanceof Error ? value : new Error(String(value))\n}\n\n/**\n * The canonical on-disk form of a path, falling back to the resolved form\n * when the file does not exist yet (a just-built bundle, a not-yet-created\n * vault). Node's permission model checks the path the ESM loader resolved\n * through `realpathSync`, so a plugin dir reached via a symlink must be\n * granted and passed in its canonical form — otherwise the module resolves\n * outside its own grant and the sandbox refuses to load it.\n */\nfunction realPathOrResolve(path: string): string {\n\ttry {\n\t\treturn realpathSync(path)\n\t} catch {\n\t\treturn resolve(path)\n\t}\n}\n\n/**\n * vite-node's transform rewrites `import.meta` without a working `.resolve`\n * (unlike vitest's), so the worker entry URL falls back to the module's own\n * location. The packaged server bundle has a working `import.meta.resolve`\n * that still cannot see `@hoardodile/host` — that path also falls through\n * here. The sandbox runs from one of two places: the package dist root\n * (`dist/chunks/worker-entry.mjs`, see scripts/postbuild.mjs), or — in dev,\n * where the `development` export condition resolves `@hoardodile/host` to\n * source — `src/sandbox/worker-entry.mjs` next to this module.\n */\nfunction resolveWorkerEntryUrl(): string {\n\ttry {\n\t\tif (typeof import.meta.resolve === \"function\") {\n\t\t\treturn import.meta.resolve(\"@hoardodile/host/worker-entry\")\n\t\t}\n\t} catch {\n\t\t// No package exports (packaged server dist, or a broken install).\n\t}\n\treturn workerEntryUrlFromModule(import.meta.url)\n}\n\nfunction workerEntryUrlFromModule(moduleUrl: string): string {\n\tconst distIdx = moduleUrl.lastIndexOf(\"/dist/\")\n\tif (distIdx >= 0) {\n\t\tconst distRoot = moduleUrl.slice(0, distIdx + \"/dist/\".length)\n\t\treturn new URL(\"chunks/worker-entry.mjs\", distRoot).href\n\t}\n\treturn new URL(\"worker-entry.mjs\", moduleUrl).href\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n","import { createHash } from \"node:crypto\"\nimport {\n\tcpSync,\n\texistsSync,\n\tmkdirSync,\n\treaddirSync,\n\treadFileSync,\n\trenameSync,\n\trmSync,\n\tstatSync,\n} from \"node:fs\"\nimport { join } from \"node:path\"\nimport type { PluginManifest } from \"@hoardodile/sdk-types\"\n\n/**\n * The host-reserved plugin subdirectory a seed never touches: downloaded\n * vault assets live there (see `VersionPaths.pluginVaultDir`) and the\n * tree comparison below must ignore them, or the presence of a vault\n * would mark every seeded plugin as changed on every boot.\n */\nconst VAULT_DIR_NAME = \"vault\"\n\n/**\n * Copy the configured plugin directories into `pluginsDir` so discovery\n * can load them as installed disk plugins. Each source is itself a plugin\n * directory (`manifest.json` at its root). A seed replaces\n * `pluginsDir/<manifest.id>` only when the file set, sizes, or content\n * hashes differ — identical trees are left untouched so a boot does not\n * dirty a synced `versions/` tree.\n */\nexport function seedPlugins(\n\tpluginsDir: string,\n\tseedPluginDirs: readonly string[] | undefined,\n): void {\n\tif (seedPluginDirs === undefined || seedPluginDirs.length === 0) {\n\t\treturn\n\t}\n\n\tfor (const dir of seedPluginDirs) {\n\t\ttry {\n\t\t\tcopyPluginDir(dir, pluginsDir)\n\t\t} catch (err) {\n\t\t\t// A locked directory (e.g. a plugin watch rebuilding dist on\n\t\t\t// Windows) must not abort the whole load — the plugin simply\n\t\t\t// stays as-is or surfaces as missing.\n\t\t\tconsole.warn(\n\t\t\t\t`[plugin-loader] failed to seed plugin dir ${dir}: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t)\n\t\t}\n\t}\n}\n\n/**\n * Copy a single plugin directory into `pluginsDir/<manifest.id>`.\n * Returns false (and copies nothing) when the directory is not a valid\n * plugin. An existing destination that already matches the source tree\n * is left untouched.\n *\n * The host-managed `vault/` subdirectory is never part of the seed: it\n * is skipped in both the tree comparison and the copy, and stashed\n * aside during a replacement — reseeding (the update channel of bundled\n * plugins) preserves the plugin's downloaded assets.\n */\nfunction copyPluginDir(srcDir: string, pluginsDir: string): boolean {\n\tconst id = readManifestId(srcDir)\n\tif (id === undefined) return false\n\tconst dstDir = join(pluginsDir, id)\n\tif (existsSync(dstDir) && treesMatch(srcDir, dstDir)) {\n\t\treturn true\n\t}\n\tif (existsSync(dstDir)) {\n\t\tconst vaultDir = join(dstDir, VAULT_DIR_NAME)\n\t\tconst stashDir = join(pluginsDir, `.vault-${id}-${Date.now()}`)\n\t\tconst hasVault = existsSync(vaultDir)\n\t\tif (hasVault) {\n\t\t\t// Same-volume rename: the vault leaves the tree before the\n\t\t\t// replacement lands, then comes back — a crash in between can\n\t\t\t// at worst strand the stash as a dot-directory (skipped by\n\t\t\t// discovery, version copies and the sync tooling).\n\t\t\trenameSync(vaultDir, stashDir)\n\t\t}\n\t\trmSync(dstDir, { recursive: true, force: true })\n\t\tmkdirSync(dstDir, { recursive: true })\n\t\tfor (const f of readdirSync(srcDir)) {\n\t\t\tif (f === VAULT_DIR_NAME) continue\n\t\t\tcpSync(join(srcDir, f), join(dstDir, f), { recursive: true })\n\t\t}\n\t\tif (existsSync(stashDir)) {\n\t\t\trenameSync(stashDir, join(dstDir, VAULT_DIR_NAME))\n\t\t}\n\t\treturn true\n\t}\n\tmkdirSync(dstDir, { recursive: true })\n\tfor (const f of readdirSync(srcDir)) {\n\t\tif (f === VAULT_DIR_NAME) continue\n\t\tcpSync(join(srcDir, f), join(dstDir, f), { recursive: true })\n\t}\n\treturn true\n}\n\nfunction readManifestId(dir: string): string | undefined {\n\tconst manifestPath = join(dir, \"manifest.json\")\n\tif (!existsSync(manifestPath)) return undefined\n\tlet manifest: PluginManifest\n\ttry {\n\t\tmanifest = JSON.parse(readFileSync(manifestPath, \"utf-8\"))\n\t} catch {\n\t\treturn undefined\n\t}\n\tif (typeof manifest.id !== \"string\" || manifest.id.length === 0) {\n\t\treturn undefined\n\t}\n\treturn manifest.id\n}\n\n/**\n * True when both directories contain the same relative file set, each\n * with matching byte size and sha256. Directories that do not exist are\n * unequal unless both are missing. Comparison ignores mtime so a copy\n * does not look like a change on the next boot.\n */\nfunction treesMatch(a: string, b: string): boolean {\n\tconst left = fingerprintTree(a)\n\tconst right = fingerprintTree(b)\n\tif (left.size !== right.size) return false\n\tfor (const [path, fingerprint] of left) {\n\t\tif (right.get(path) !== fingerprint) return false\n\t}\n\treturn true\n}\n\nfunction fingerprintTree(root: string): Map<string, string> {\n\tconst out = new Map<string, string>()\n\tif (!existsSync(root)) return out\n\twalkFiles(root, \"\", out)\n\treturn out\n}\n\nfunction walkFiles(\n\tabsDir: string,\n\trelDir: string,\n\tout: Map<string, string>,\n): void {\n\tconst entries = readdirSync(absDir, { withFileTypes: true })\n\tfor (const entry of entries) {\n\t\t// Root-level host-reserved vault directory: never fingerprinted,\n\t\t// never copied — see {@link VAULT_DIR_NAME}.\n\t\tif (relDir.length === 0 && entry.name === VAULT_DIR_NAME) continue\n\t\tconst rel = relDir.length === 0 ? entry.name : `${relDir}/${entry.name}`\n\t\tconst abs = join(absDir, entry.name)\n\t\tif (entry.isDirectory()) {\n\t\t\twalkFiles(abs, rel, out)\n\t\t\tcontinue\n\t\t}\n\t\tif (!entry.isFile()) continue\n\t\tconst st = statSync(abs)\n\t\tconst hash = createHash(\"sha256\").update(readFileSync(abs)).digest(\"hex\")\n\t\tout.set(rel.replaceAll(\"\\\\\", \"/\"), `${st.size}:${hash}`)\n\t}\n}\n","import type { PluginManifestId } from \"@hoardodile/sdk-types\"\nimport { createPluginActivation } from \"./activation.ts\"\nimport type { PluginRegistry, PluginRegistryEntry } from \"./api-types.ts\"\nimport { createPluginDiscovery } from \"./discovery.ts\"\nimport { createPluginSandbox, type PluginSandbox } from \"./sandbox/host.ts\"\nimport { seedPlugins } from \"./seed.ts\"\nimport type { PluginSettingsStore } from \"./settings-store.ts\"\n\nexport { seedPlugins } from \"./seed.ts\"\n\n/**\n * Loads plugins into a live {@link PluginRegistry}: seed the configured\n * plugin directories, discover, activate, and sort by priority.\n * `loadAll`/`rescan` are serialized — an overlapping run would replace\n * workers out from under the previous run's in-flight loads.\n *\n * The previous registry and its workers are only replaced AFTER the new\n * registry finished activating: a failed load never strands a stale\n * registry whose workers were already disposed (every hook would then\n * fail until the next restart).\n */\nexport type PluginLoader = {\n\treadonly loadAll: () => Promise<PluginRegistry>\n\treadonly rescan: () => Promise<PluginRegistry>\n\t/** The current registry; throws when `loadAll` has not run yet. */\n\treadonly getRegistry: () => PluginRegistry\n}\n\nexport type PluginLoaderDeps = {\n\treadonly builtinDir?: string\n\treadonly devPluginDirs?: readonly string[]\n\t/**\n\t * Plugin directories (each with `manifest.json` at its root, e.g. a\n\t * built plugin's `dist/`) that are seeded into `pluginsDir` on every\n\t * load. Each plugin is copied into `pluginsDir/<manifest.id>` before\n\t * discovery runs when the destination tree differs — so a seeded\n\t * plugin behaves like a regular installed one (DB settings, caching,\n\t * uninstall). The server passes an empty list and seeds through\n\t * `writeVersioned` instead. When omitted, nothing is seeded.\n\t */\n\treadonly seedPluginDirs?: readonly string[]\n\treadonly pluginsDir: string\n\treadonly settings: PluginSettingsStore\n\treadonly disableDevPlugins?: boolean\n\t/**\n\t * Worker-thread sandbox that executes plugin hooks. Optional so tests\n\t * without any `main.js` on disk can omit it; the default spawns real\n\t * workers when a loadable bundle is found.\n\t */\n\treadonly sandbox?: PluginSandbox\n\t/**\n\t * Optional timing sink for boot diagnostics — receives the duration of\n\t * each `loadAll` step. Defaults to a no-op so tests stay quiet.\n\t */\n\treadonly onTiming?: (step: string, ms: number) => void\n}\n\n/**\n * Create the plugin loader. Workers are respawned on every load so a\n * rescan picks up changed plugin code — the new worker re-imports\n * `main.js`, bypassing the main thread's ESM module cache. Configured\n * {@link PluginLoaderDeps.seedPluginDirs} are copied into `pluginsDir`\n * on each load.\n */\nexport function createPluginLoader(deps: PluginLoaderDeps): PluginLoader {\n\tlet registry: PluginRegistry | undefined\n\t// Serializes loadAll/rescan: an overlapping run would replace workers\n\t// out from under the previous run's in-flight plugin loads.\n\tlet chain: Promise<unknown> = Promise.resolve()\n\n\tconst discovery = createPluginDiscovery(deps)\n\tconst sandbox = deps.sandbox ?? createPluginSandbox()\n\tconst activation = createPluginActivation({ sandbox })\n\n\tfunction loadAll(): Promise<PluginRegistry> {\n\t\tconst run = chain.then(doLoadAll)\n\t\tchain = run.catch(() => {})\n\t\treturn run\n\t}\n\n\tasync function doLoadAll(): Promise<PluginRegistry> {\n\t\t// Seed first — tolerant of individual failures: a locked directory\n\t\t// (Windows/AV races with a plugin watch rebuilding dist) only skips\n\t\t// that plugin instead of aborting the whole load.\n\t\tconst seedStart = performance.now()\n\t\tseedPlugins(deps.pluginsDir, deps.seedPluginDirs)\n\t\tdeps.onTiming?.(\"seed\", Math.round(performance.now() - seedStart))\n\n\t\tconst discoverStart = performance.now()\n\t\tconst { found, missing } = await discovery.discover()\n\t\tdeps.onTiming?.(\"discover\", Math.round(performance.now() - discoverStart))\n\n\t\t// Activate BEFORE disposing anything from the previous registry:\n\t\t// same-id plugins replace their worker in the sandbox, and a\n\t\t// failure anywhere above leaves the previous registry (and its\n\t\t// workers) fully alive instead of stranding a stale registry with\n\t\t// disposed workers.\n\t\tconst activateStart = performance.now()\n\t\tconst activated = await activation.activateAll(found)\n\t\tdeps.onTiming?.(\"activate\", Math.round(performance.now() - activateStart))\n\n\t\tconst failing = activation.createFailingEntries(missing)\n\n\t\tconst entries = [...activated, ...failing]\n\t\tentries.sort((a, b) => a.priority - b.priority)\n\n\t\tconst next = buildRegistry(entries)\n\t\t// Plugins that left the registry (uninstalled/removed) free their\n\t\t// workers; everything still registered keeps the worker activation\n\t\t// just loaded.\n\t\tawait sandbox.disposeExcept(new Set(next.getAll().map((e) => e.id)))\n\t\tregistry = next\n\t\treturn registry\n\t}\n\n\tasync function rescan(): Promise<PluginRegistry> {\n\t\treturn loadAll()\n\t}\n\n\tfunction getRegistry(): PluginRegistry {\n\t\tif (registry === undefined) {\n\t\t\tthrow new Error(\"Plugin registry not initialised — call loadAll() first\")\n\t\t}\n\t\treturn registry\n\t}\n\n\treturn { loadAll, rescan, getRegistry }\n}\n\n/**\n * Build the immutable-ish registry from entries, keyed by plugin UUID.\n * Duplicate ids keep the first entry and warn (the UUID contract is\n * unique — duplicates signal a broken install). Duplicates are dropped\n * from every accessor, not just the id map: keeping a duplicate in\n * `getAll()` would make enabled-list iteration invoke a plugin whose\n * worker activation already replaced (and disposed) the first load.\n * `updateEntry` patches a single entry in place and re-sorts by priority.\n */\nexport function buildRegistry(\n\tentries: readonly PluginRegistryEntry[],\n): PluginRegistry {\n\tconst byId = new Map<PluginManifestId, PluginRegistryEntry>()\n\tconst deduped: PluginRegistryEntry[] = []\n\tfor (const entry of entries) {\n\t\tconst existing = byId.get(entry.id)\n\t\tif (existing !== undefined) {\n\t\t\tconsole.warn(\n\t\t\t\t`[plugin-loader] skipping ${entry.id}: UUID conflicts with already registered plugin (${existing.manifest.name ?? existing.id}), keeping first`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\t\tbyId.set(entry.id, entry)\n\t\tdeduped.push(entry)\n\t}\n\n\tlet sortedEntries = deduped\n\n\treturn {\n\t\tgetAll(): readonly PluginRegistryEntry[] {\n\t\t\treturn sortedEntries\n\t\t},\n\t\tgetEnabled(): readonly PluginRegistryEntry[] {\n\t\t\treturn sortedEntries.filter((e) => e.enabled)\n\t\t},\n\t\tgetById(id: PluginManifestId): PluginRegistryEntry | undefined {\n\t\t\treturn byId.get(id)\n\t\t},\n\t\tgetBuiltin(): PluginRegistryEntry | undefined {\n\t\t\treturn sortedEntries.find((e) => e.builtin)\n\t\t},\n\t\tgetForResource(\n\t\t\tresPluginId: PluginManifestId,\n\t\t): PluginRegistryEntry | undefined {\n\t\t\tconst entry = byId.get(resPluginId)\n\t\t\tif (entry === undefined || !entry.enabled) return undefined\n\t\t\treturn entry\n\t\t},\n\t\tupdateEntry(\n\t\t\tid: PluginManifestId,\n\t\t\tpatch: Partial<\n\t\t\t\tPick<\n\t\t\t\t\tPluginRegistryEntry,\n\t\t\t\t\t\"enabled\" | \"priority\" | \"pinned\" | \"color\" | \"missing\"\n\t\t\t\t>\n\t\t\t>,\n\t\t): void {\n\t\t\tconst index = sortedEntries.findIndex((e) => e.id === id)\n\t\t\tif (index === -1) return\n\t\t\tconst old = sortedEntries[index]\n\t\t\tif (old === undefined) return\n\t\t\tconst updated: PluginRegistryEntry = {\n\t\t\t\tid: old.id,\n\t\t\t\tmanifest: old.manifest,\n\t\t\t\tplugin: old.plugin,\n\t\t\t\tdiskPath: old.diskPath,\n\t\t\t\tenabled: patch.enabled ?? old.enabled,\n\t\t\t\tpriority: patch.priority ?? old.priority,\n\t\t\t\tpinned: patch.pinned ?? old.pinned,\n\t\t\t\tcolor: patch.color ?? old.color,\n\t\t\t\tmissing: patch.missing ?? old.missing,\n\t\t\t\tbuiltin: old.builtin,\n\t\t\t\tdev: old.dev,\n\t\t\t}\n\t\t\tsortedEntries = [\n\t\t\t\t...sortedEntries.slice(0, index),\n\t\t\t\tupdated,\n\t\t\t\t...sortedEntries.slice(index + 1),\n\t\t\t]\n\t\t\tsortedEntries.sort((a, b) => a.priority - b.priority)\n\t\t\tbyId.set(id, updated)\n\t\t},\n\t}\n}\n","/**\n * Process-wide LRU cache for host-side probe results (image/video/audio\n * metadata and animation flags). Source archives are immutable per\n * fileVersion, so entries never need explicit invalidation — the cache\n * scope carries the version and the LRU bound handles eviction.\n */\n\n/** Process-wide bound (entries) for the host-side probe result cache. */\nexport const PLUGIN_PROBE_CACHE_MAX_ENTRIES = 512\n\n/** Cacheable probe result; `undefined` marks a probe that found nothing. */\ntype ProbeValue = object | boolean | undefined\n\nexport type PluginProbeCache = {\n\t/**\n\t * Return the cached result for `key`, computing and storing it on a\n\t * miss. Concurrent callers for the same key share one computation.\n\t */\n\treadonly getOrCompute: <T extends ProbeValue>(\n\t\tkey: string,\n\t\tcompute: () => Promise<T>,\n\t) => Promise<T>\n}\n\n/**\n * Create the probe LRU cache. Keys are caller-composed (probe kind +\n * version + path); concurrent misses single-flight through one shared\n * computation; failures are evicted so the next caller retries.\n */\nexport function createProbeCache(\n\tmaxEntries = PLUGIN_PROBE_CACHE_MAX_ENTRIES,\n): PluginProbeCache {\n\t// Promise-valued cells: a miss is stored before it settles, so\n\t// concurrent probes for the same key single-flight through one\n\t// computation.\n\tconst cells = new Map<string, Promise<ProbeValue>>()\n\n\treturn {\n\t\tgetOrCompute<T extends ProbeValue>(\n\t\t\tkey: string,\n\t\t\tcompute: () => Promise<T>,\n\t\t): Promise<T> {\n\t\t\tconst existing = cells.get(key)\n\t\t\tif (existing !== undefined) {\n\t\t\t\t// Refresh LRU position.\n\t\t\t\tcells.delete(key)\n\t\t\t\tcells.set(key, existing)\n\t\t\t\t// Type-erased store: each key is namespaced by probe kind, so\n\t\t\t\t// the stored promise always resolves to the caller's T.\n\t\t\t\treturn existing as Promise<T>\n\t\t\t}\n\t\t\tconst promise = compute()\n\t\t\tcells.set(key, promise)\n\t\t\tpromise.catch(() => {\n\t\t\t\t// Failures are not cached — the next caller retries.\n\t\t\t\tif (cells.get(key) === promise) cells.delete(key)\n\t\t\t})\n\t\t\tif (cells.size > maxEntries) {\n\t\t\t\tconst oldest = cells.keys().next().value\n\t\t\t\tif (oldest !== undefined) cells.delete(oldest)\n\t\t\t}\n\t\t\treturn promise\n\t\t},\n\t}\n}\n"]}
|