@hoardodile/host 0.0.0
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/LICENSE +18 -0
- package/README.md +73 -0
- package/dist/chunks/worker-entry.mjs +422 -0
- package/dist/container-fKxefO7p.d.ts +58 -0
- package/dist/contract/index.d.ts +49 -0
- package/dist/contract/index.js +187 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/hoard/index.d.ts +754 -0
- package/dist/hoard/index.js +1939 -0
- package/dist/hoard/index.js.map +1 -0
- package/dist/image-Dx2Be3Bj.d.ts +168 -0
- package/dist/index.d.ts +788 -0
- package/dist/index.js +3311 -0
- package/dist/index.js.map +1 -0
- package/dist/media/index.d.ts +96 -0
- package/dist/media/index.js +152 -0
- package/dist/media/index.js.map +1 -0
- package/dist/probe/index.d.ts +113 -0
- package/dist/probe/index.js +454 -0
- package/dist/probe/index.js.map +1 -0
- package/dist/probe-cache-BkRI7HEK.d.ts +25 -0
- package/dist/render/index.d.ts +125 -0
- package/dist/render/index.js +430 -0
- package/dist/render/index.js.map +1 -0
- package/package.json +103 -0
- package/src/__testutils__/zip-fixtures.ts +139 -0
- package/src/activation.ts +116 -0
- package/src/api-types.ts +60 -0
- package/src/api.test.ts +564 -0
- package/src/api.ts +501 -0
- package/src/archive/7z.test.ts +222 -0
- package/src/archive/7z.ts +261 -0
- package/src/archive/archive-ops.test.ts +654 -0
- package/src/archive/extract-archive.test.ts +237 -0
- package/src/archive/extract-archive.ts +534 -0
- package/src/archive/extract.ts +693 -0
- package/src/archive/format.ts +49 -0
- package/src/archive/index.ts +116 -0
- package/src/archive/listing.ts +121 -0
- package/src/archive/materialize.ts +88 -0
- package/src/archive/name-decode.test.ts +34 -0
- package/src/archive/name-decode.ts +287 -0
- package/src/archive/nested-cd-cache.ts +55 -0
- package/src/archive/nested-entry.test.ts +194 -0
- package/src/archive/nested-entry.ts +254 -0
- package/src/archive/pack.ts +40 -0
- package/src/archive/zip-entries.test.ts +56 -0
- package/src/archive/zip-entries.ts +336 -0
- package/src/capability-guard.ts +73 -0
- package/src/container.ts +62 -0
- package/src/contract/container-fixture.ts +74 -0
- package/src/contract/index.ts +10 -0
- package/src/contract/suite.test.ts +101 -0
- package/src/contract/suite.ts +205 -0
- package/src/define-plugin.test.ts +53 -0
- package/src/define-plugin.ts +12 -0
- package/src/dev-runner.test.ts +98 -0
- package/src/dev-runner.ts +73 -0
- package/src/directory-api.test.ts +229 -0
- package/src/directory-api.ts +42 -0
- package/src/directory-container.ts +141 -0
- package/src/discovery.ts +295 -0
- package/src/errors.ts +74 -0
- package/src/fixtures.ts +8 -0
- package/src/hash.test.ts +301 -0
- package/src/hash.ts +167 -0
- package/src/hoard/dir-size.ts +58 -0
- package/src/hoard/index.ts +84 -0
- package/src/hoard/migrate-script.test.ts +539 -0
- package/src/hoard/order-manifest.test.ts +115 -0
- package/src/hoard/order-manifest.ts +131 -0
- package/src/hoard/paths.test.ts +226 -0
- package/src/hoard/paths.ts +569 -0
- package/src/hoard/plugin-vault.test.ts +160 -0
- package/src/hoard/plugin-vault.ts +233 -0
- package/src/hoard/sanitize.test.ts +149 -0
- package/src/hoard/sanitize.ts +230 -0
- package/src/hoard/staging-dir.ts +133 -0
- package/src/hoard/version.test.ts +285 -0
- package/src/hoard/version.ts +216 -0
- package/src/hoard/versioned-folder-ops.ts +152 -0
- package/src/hoard/write-versioned.test.ts +67 -0
- package/src/hoard/write-versioned.ts +39 -0
- package/src/hooks.test.ts +528 -0
- package/src/hooks.ts +428 -0
- package/src/index.ts +135 -0
- package/src/loader.test.ts +442 -0
- package/src/loader.ts +213 -0
- package/src/media/index.ts +16 -0
- package/src/media/render-cache.ts +27 -0
- package/src/media/seekable.ts +35 -0
- package/src/media/thumb-input.test.ts +109 -0
- package/src/media/thumb-input.ts +194 -0
- package/src/nested-view.test.ts +223 -0
- package/src/nested-view.ts +239 -0
- package/src/probe/audio.test.ts +135 -0
- package/src/probe/audio.ts +140 -0
- package/src/probe/av.ts +84 -0
- package/src/probe/ffprobe.ts +197 -0
- package/src/probe/image.test.ts +251 -0
- package/src/probe/image.ts +336 -0
- package/src/probe/index.ts +54 -0
- package/src/probe/sniff.test.ts +72 -0
- package/src/probe/sniff.ts +67 -0
- package/src/probe/video.test.ts +41 -0
- package/src/probe/video.ts +116 -0
- package/src/probe-cache.test.ts +89 -0
- package/src/probe-cache.ts +65 -0
- package/src/render/ffmpeg.ts +78 -0
- package/src/render/index.ts +34 -0
- package/src/render/pipeline.test.ts +187 -0
- package/src/render/pipeline.ts +479 -0
- package/src/sandbox/call-timers.ts +68 -0
- package/src/sandbox/fixtures/api-error-plugin.mjs +11 -0
- package/src/sandbox/fixtures/asset-plugin.mjs +13 -0
- package/src/sandbox/fixtures/chatty-plugin.mjs +14 -0
- package/src/sandbox/fixtures/container-plugin.mjs +8 -0
- package/src/sandbox/fixtures/context-plugin.mjs +19 -0
- package/src/sandbox/fixtures/crash-plugin.mjs +6 -0
- package/src/sandbox/fixtures/echo-plugin.mjs +17 -0
- package/src/sandbox/fixtures/exit-plugin.mjs +6 -0
- package/src/sandbox/fixtures/fetch-plugin.mjs +8 -0
- package/src/sandbox/fixtures/flood-api-plugin.mjs +8 -0
- package/src/sandbox/fixtures/flood-log-plugin.mjs +8 -0
- package/src/sandbox/fixtures/hostile-dynamic-plugin.mjs +9 -0
- package/src/sandbox/fixtures/hostile-plugin.mjs +13 -0
- package/src/sandbox/fixtures/huge-result-plugin.mjs +8 -0
- package/src/sandbox/fixtures/logging-plugin.mjs +9 -0
- package/src/sandbox/fixtures/range-plugin.mjs +9 -0
- package/src/sandbox/fixtures/slow-api-plugin.mjs +11 -0
- package/src/sandbox/fixtures/spin-plugin.mjs +9 -0
- package/src/sandbox/fixtures/stuck-plugin.mjs +12 -0
- package/src/sandbox/fixtures/thrower-plugin.mjs +7 -0
- package/src/sandbox/host.test.ts +619 -0
- package/src/sandbox/host.ts +933 -0
- package/src/sandbox/host.unit.test.ts +330 -0
- package/src/sandbox/protocol.test.ts +85 -0
- package/src/sandbox/protocol.ts +147 -0
- package/src/sandbox/sandboxed-plugin.ts +30 -0
- package/src/sandbox/worker-entry.mjs +422 -0
- package/src/seed.test.ts +102 -0
- package/src/seed.ts +160 -0
- package/src/settings-store.ts +26 -0
- package/src/types.ts +21 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/errors.ts","../../src/archive/format.ts","../../src/archive/name-decode.ts","../../src/archive/zip-entries.ts","../../src/archive/7z.ts","../../src/archive/listing.ts","../../src/archive/extract.ts","../../src/archive/pack.ts","../../src/hoard/dir-size.ts","../../src/hoard/order-manifest.ts","../../src/hoard/version.ts","../../src/hoard/paths.ts","../../src/hoard/plugin-vault.ts","../../src/hoard/sanitize.ts","../../src/hoard/staging-dir.ts","../../src/hoard/write-versioned.ts","../../src/hoard/versioned-folder-ops.ts"],"names":["sep","buffer","yauzl","readdir","join","stat","writeFile","rename","rm","resolve","existsSync","readFile","randomUUID","mkdir","createHash","createReadStream","FORBIDDEN_VISIBLE_CHARS","WINDOWS_RESERVED","pipeline","createWriteStream","extname"],"mappings":"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AA,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;AA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","file":"index.js","sourcesContent":["/**\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 { invalid } from \"../errors.ts\"\nimport { listSevenZipEntries, resolveSevenZipPath } from \"./7z.ts\"\nimport { SNIFF_WINDOW_BYTES, sniffContainerFormat } from \"./format.ts\"\nimport type { ArchiveEntry } from \"./nested-entry.ts\"\nimport { listZipEntries, readFileRange } from \"./zip-entries.ts\"\n\n/**\n * Cross-engine archive listing and budget validation. Zip lists through\n * yauzl, tar/7z/rar/xz/gzip through the 7-Zip binary; the engine choice\n * (and the zip-only legacy-cp437 name decoding, handled inside the 7-Zip\n * wrapper) lives here so callers never branch on format.\n */\n\n/**\n * List the file entries of an on-disk archive across engines: zip lists\n * through yauzl, tar/7z/rar/xz/gzip through the 7-Zip binary (gzip\n * reports its single decompressed stream).\n *\n * @throws DomainError `resource.archive_open_failed` when the file is\n * not a readable archive, or 7-Zip is missing for a non-zip format.\n */\nexport async function listArchiveEntries(\n\tarchivePath: string,\n): Promise<readonly ArchiveEntry[]> {\n\tconst head = await readFileRange(archivePath, 0, SNIFF_WINDOW_BYTES - 1)\n\tconst format = sniffContainerFormat(head)\n\tif (format === \"zip\") {\n\t\tconst records = await listZipEntries(archivePath)\n\t\treturn records.map((r) => ({ name: r.name, sizeBytes: r.uncompressedSize }))\n\t}\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 (resolveSevenZipPath() === undefined) {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_open_failed\",\n\t\t\t\"7-Zip is not installed — cannot list tar/7z/rar/xz/gzip archives\",\n\t\t\t{},\n\t\t)\n\t}\n\tconst entries = await listSevenZipEntries(archivePath)\n\treturn entries\n\t\t.filter((e) => !e.folder)\n\t\t.map((e) => ({ name: e.name, sizeBytes: e.sizeBytes }))\n}\n\n/**\n * Which listing budget is exceeded, or `undefined` when the listing\n * fits. Pure computation — callers decide the error kind and message\n * (the plugin extractor and the whole-archive extractor use different\n * domain kinds), so one budget rule serves both.\n */\nexport function listingBudgetExceeded(\n\tentries: readonly { readonly sizeBytes: number }[],\n\topts: { readonly maxBytes: number; readonly maxEntries?: number },\n):\n\t| {\n\t\t\treadonly kind: \"bytes\"\n\t\t\treadonly totalBytes: number\n\t\t\treadonly maxBytes: number\n\t }\n\t| {\n\t\t\treadonly kind: \"entries\"\n\t\t\treadonly count: number\n\t\t\treadonly maxEntries: number\n\t }\n\t| undefined {\n\tif (opts.maxEntries !== undefined && entries.length > opts.maxEntries) {\n\t\treturn {\n\t\t\tkind: \"entries\",\n\t\t\tcount: entries.length,\n\t\t\tmaxEntries: opts.maxEntries,\n\t\t}\n\t}\n\tlet totalBytes = 0\n\tfor (const entry of entries) {\n\t\ttotalBytes += entry.sizeBytes\n\t\tif (totalBytes > opts.maxBytes) {\n\t\t\treturn {\n\t\t\t\tkind: \"bytes\",\n\t\t\t\ttotalBytes,\n\t\t\t\tmaxBytes: opts.maxBytes,\n\t\t\t}\n\t\t}\n\t}\n\treturn undefined\n}\n\n/**\n * Validate an on-disk archive's cumulative uncompressed size against\n * `maxBytes` without inflating any entry bytes (metadata-only — the\n * listing never decompresses). Format-agnostic via\n * {@link listArchiveEntries}; unknown content and non-zip formats\n * without the 7-Zip binary are rejected outright.\n *\n * Used at commit time for uploaded archives so every supported format\n * passes the same bomb gate.\n *\n * @throws DomainError `resource.archive_open_failed` when the file is\n * not a readable archive (or 7-Zip is missing for non-zip formats).\n * @throws DomainError `resource.archive_too_large` when the cumulative\n * uncompressed size exceeds `maxBytes`.\n */\nexport async function validateArchiveBudget(\n\tarchivePath: string,\n\tmaxBytes: number,\n): Promise<void> {\n\tconst entries = await listArchiveEntries(archivePath)\n\tconst exceeded = listingBudgetExceeded(entries, { maxBytes })\n\tif (exceeded?.kind === \"bytes\") {\n\t\tthrow invalid(\n\t\t\t\"resource.archive_too_large\",\n\t\t\t`archive uncompressed size exceeds ${maxBytes} bytes`,\n\t\t\t{ maxBytes },\n\t\t)\n\t}\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}\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 (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 type { Readable } from \"node:stream\"\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 * Shared directory-size walk: the one implementation of \"recursive byte\n * size of a directory tree\" for host storage. Consumers opt into\n * different file policies via options:\n *\n * - vault totals skip dot-prefixed files (host temp staging), while\n * storage totals count everything — see {@link vaultTotalSize} and\n * `apps/server/src/domain/storage/service.ts`.\n *\n * Symbolic links are skipped so cycles cannot hang the walk and\n * linked-out files are not double counted; missing/transient entries\n * are ignored.\n */\n\nimport type { Dirent } from \"node:fs\"\nimport { readdir, stat } from \"node:fs/promises\"\nimport { join } from \"node:path\"\n\nexport type DirSizeOptions = {\n\t/** Skip file names starting with a dot (host temp staging). */\n\treadonly excludeDotPrefix?: boolean\n}\n\nexport async function sumDirSizes(\n\troot: string,\n\topts: DirSizeOptions = {},\n): Promise<number> {\n\tlet total = 0\n\tconst stack = [root]\n\twhile (stack.length > 0) {\n\t\tconst dir = stack.pop()\n\t\tif (dir === undefined) continue\n\t\tlet entries: Dirent[]\n\t\ttry {\n\t\t\tentries = await readdir(dir, { withFileTypes: true })\n\t\t} catch {\n\t\t\tcontinue\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.isSymbolicLink()) continue\n\t\t\tconst full = join(dir, entry.name)\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tstack.push(full)\n\t\t\t} else if (entry.isFile()) {\n\t\t\t\tif (opts.excludeDotPrefix === true && entry.name.startsWith(\".\")) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst info = await stat(full)\n\t\t\t\t\ttotal += info.size\n\t\t\t\t} catch {\n\t\t\t\t\t// File disappeared between readdir and stat.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn total\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 {\n\tcpSync,\n\texistsSync,\n\tmkdirSync,\n\treaddirSync,\n\treadFileSync,\n\trmSync,\n\twriteFileSync,\n} from \"node:fs\"\nimport { join, resolve } from \"node:path\"\nimport { conflict, notFound } from \"../errors.ts\"\n\n/**\n * Persisted version-state file (under `<root>/local/`, not `versions/`,\n * because `versions/` is reserved for version folders only).\n */\nconst STATE_FILENAME = \"version-state.json\"\n\n/**\n * On-disk shape of the version state.\n *\n * - `active` — the version the user is currently viewing. When `active`\n * equals the current (max) version the server runs in normal R/W mode;\n * when `active < current` the server runs READ-ONLY against a cloned\n * snapshot of the active version's DB.\n */\ntype VersionState = {\n\treadonly active: number\n}\n\n/** Root layout helper: the versions root directory `<root>/versions`. */\nfunction versionsRoot(root: string): string {\n\treturn resolve(root, \"versions\")\n}\n\n/** State file lives under `<root>/local/version-state.json`. */\nfunction stateFile(root: string): string {\n\treturn resolve(root, \"local\", STATE_FILENAME)\n}\n\n/**\n * Enumerate version directories under `<root>/versions/`. Names that are\n * not pure positive integers are ignored. Result is sorted ascending.\n */\nexport function listVersions(root: string): readonly number[] {\n\tconst dir = versionsRoot(root)\n\tif (!existsSync(dir)) return []\n\tconst names = readdirSync(dir, { withFileTypes: true })\n\t\t.filter((e) => e.isDirectory())\n\t\t.map((e) => e.name)\n\tconst nums: number[] = []\n\tfor (const n of names) {\n\t\tif (!/^[1-9][0-9]*$/.test(n)) continue\n\t\tnums.push(Number.parseInt(n, 10))\n\t}\n\tnums.sort((a, b) => a - b)\n\treturn nums\n}\n\n/**\n * Current (maximum) version on disk. Returns `0` when no version exists\n * yet (caller is expected to bootstrap version 1 in that case).\n */\nexport function currentVersion(root: string): number {\n\tconst all = listVersions(root)\n\treturn all.length === 0 ? 0 : (all[all.length - 1] ?? 0)\n}\n\n/**\n * Read the persisted active version. Falls back to current when the\n * state file is missing or malformed, or when the recorded value points\n * at a version that no longer exists.\n */\nexport function readActiveVersion(root: string): number {\n\tconst cur = currentVersion(root)\n\tconst file = stateFile(root)\n\tif (!existsSync(file)) return cur\n\ttry {\n\t\tconst parsed = JSON.parse(\n\t\t\treadFileSync(file, \"utf8\"),\n\t\t) as Partial<VersionState>\n\t\tconst active = typeof parsed.active === \"number\" ? parsed.active : cur\n\t\tconst all = listVersions(root)\n\t\tif (all.includes(active)) return active\n\t\treturn cur\n\t} catch {\n\t\treturn cur\n\t}\n}\n\n/**\n * Persist the active version. Caller must ensure `version` exists on\n * disk (use {@link listVersions}).\n *\n * @throws DomainError `version.not_found` when `version` is not a known\n * version directory.\n */\nexport function writeActiveVersion(root: string, version: number): void {\n\tconst all = listVersions(root)\n\tif (!all.includes(version)) {\n\t\tthrow notFound(\"version.not_found\", `version ${version} does not exist`, {\n\t\t\tversion,\n\t\t})\n\t}\n\tconst file = stateFile(root)\n\tmkdirSync(resolve(root, \"local\"), { recursive: true })\n\tconst payload: VersionState = { active: version }\n\twriteFileSync(file, JSON.stringify(payload), \"utf8\")\n}\n\n/**\n * Bootstrap version 1 if the versions root has no version directories yet.\n * Idempotent; a no-op when any version already exists.\n */\nexport function ensureBootstrapVersion(root: string): number {\n\tconst cur = currentVersion(root)\n\tif (cur > 0) return cur\n\tconst v1 = resolve(versionsRoot(root), \"1\")\n\tmkdirSync(v1, { recursive: true })\n\treturn 1\n}\n\nexport type CreateNextVersionResult = {\n\treadonly previous: number\n\treadonly created: number\n}\n\n/**\n * Snapshot the current version into a freshly-numbered next version.\n *\n * Process:\n * 1. Compute `next = currentVersion + 1`.\n * 2. Refuse to proceed when `versions/<prev>/app.sqlite` already exists.\n * 3. Create `<root>/versions/<next>/` and copy installed plugins from\n * `versions/<prev>/plugins` (resource binaries stay at their\n * `fileVersion`; plugins have no equivalent pointer).\n * 4. Vacuum-snapshot the live DB into `<root>/versions/<prev>/app.sqlite`.\n * (Caller passes a `vacuumInto` function; we don't take a DB handle\n * here to keep this module database-agnostic.)\n * 5. On any failure after step 3, remove `versions/<next>` so\n * {@link currentVersion} does not jump to an empty directory.\n *\n * @throws DomainError `version.bootstrap_required` when no version exists\n * yet (caller must bootstrap first).\n * @throws DomainError `version.already_exists` when the previous\n * version's DB snapshot already exists.\n */\nexport function createNextVersion(\n\troot: string,\n\tvacuumInto: (destination: string) => void,\n): CreateNextVersionResult {\n\tconst prev = currentVersion(root)\n\tif (prev === 0) {\n\t\tthrow conflict(\n\t\t\t\"version.bootstrap_required\",\n\t\t\t\"no current version to snapshot from\",\n\t\t)\n\t}\n\tconst next = prev + 1\n\tconst prevDir = resolve(versionsRoot(root), String(prev))\n\tconst nextDir = resolve(versionsRoot(root), String(next))\n\t// Archive the current live DB under the PREVIOUS version directory so\n\t// that `versions/<prev>/app.sqlite` becomes the immutable snapshot.\n\t// The live database stays at `<root>/app.sqlite`; only this archived\n\t// copy lands in `versions/`.\n\tconst dest = resolve(prevDir, \"app.sqlite\")\n\tif (existsSync(dest)) {\n\t\tthrow conflict(\n\t\t\t\"version.already_exists\",\n\t\t\t`app.sqlite already exists for version ${next}`,\n\t\t\t{ version: next },\n\t\t)\n\t}\n\tmkdirSync(nextDir, { recursive: true })\n\ttry {\n\t\tcopyVersionPlugins(prevDir, nextDir)\n\t\tvacuumInto(dest)\n\t} catch (err) {\n\t\trmSync(nextDir, { recursive: true, force: true })\n\t\tthrow err\n\t}\n\treturn { previous: prev, created: next }\n}\n\n/**\n * Copy installed plugin directories from one version folder into the\n * next. Only subdirectories that look like plugins (contain\n * `manifest.json`) are copied; dot-directories such as leftover\n * staging folders are skipped.\n */\nfunction copyVersionPlugins(prevDir: string, nextDir: string): void {\n\tconst src = join(prevDir, \"plugins\")\n\tif (!existsSync(src)) return\n\tconst entries = readdirSync(src, { withFileTypes: true })\n\tfor (const entry of entries) {\n\t\tif (!entry.isDirectory()) continue\n\t\tif (entry.name.startsWith(\".\")) continue\n\t\tconst from = join(src, entry.name)\n\t\tif (!existsSync(join(from, \"manifest.json\"))) continue\n\t\tcpSync(from, join(nextDir, \"plugins\", entry.name), { recursive: true })\n\t}\n}\n\n/**\n * Path to the DB file for version `v`: `<root>/versions/<v>/app.sqlite`.\n */\nexport function versionedDbFile(root: string, v: number): string {\n\treturn resolve(versionsRoot(root), String(v), \"app.sqlite\")\n}\n\n/**\n * Path to the per-version archive directory `<root>/versions/<v>`.\n */\nexport function versionedPath(root: string, v: number): string {\n\treturn resolve(versionsRoot(root), String(v))\n}\n","import { createHash } from \"node:crypto\"\nimport { isAbsolute, resolve, sep } from \"node:path\"\nimport {\n\tcurrentVersion as diskCurrentVersion,\n\treadActiveVersion,\n} from \"./version.ts\"\n\n/**\n * Windows reserved base names (case-insensitive). These must not appear as\n * the base of any filename we create, regardless of extension, or CreateFile\n * will fail with bizarre errors. The set is strict on Windows and advisory\n * on other platforms.\n *\n * @see https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file\n */\nconst WINDOWS_RESERVED = new Set([\n\t\"CON\",\n\t\"PRN\",\n\t\"AUX\",\n\t\"NUL\",\n\t\"COM1\",\n\t\"COM2\",\n\t\"COM3\",\n\t\"COM4\",\n\t\"COM5\",\n\t\"COM6\",\n\t\"COM7\",\n\t\"COM8\",\n\t\"COM9\",\n\t\"LPT1\",\n\t\"LPT2\",\n\t\"LPT3\",\n\t\"LPT4\",\n\t\"LPT5\",\n\t\"LPT6\",\n\t\"LPT7\",\n\t\"LPT8\",\n\t\"LPT9\",\n])\n\n/**\n * Name of the content subfolder inside a resource folder. User entries\n * live under `resources/<id>/data/`; metadata dotfiles (`.cover.*`,\n * `.deleted`, `.order`) stay at the resource root. Content and metadata\n * are replaced/kept independently: commits swap the whole `data/`\n * subtree, while root dotfiles survive re-uploads.\n */\nexport const RESOURCE_DATA_DIR_NAME = \"data\"\n\nconst FORBIDDEN_VISIBLE_CHARS = '<>:\"|?*'\n\nfunction hasControlChar(segment: string): boolean {\n\tfor (let i = 0; i < segment.length; i++) {\n\t\tif (segment.charCodeAt(i) < 32) return true\n\t}\n\treturn false\n}\n\nfunction hasForbiddenVisibleChar(segment: string): boolean {\n\tfor (let i = 0; i < segment.length; i++) {\n\t\tconst ch = segment[i]\n\t\tif (ch !== undefined && FORBIDDEN_VISIBLE_CHARS.includes(ch)) return true\n\t}\n\treturn false\n}\n\n/**\n * Root-level subdirectory semantics:\n * - `versions/<version>/` is the user's manual-sync scope, partitioned by\n * archive version. Each version folder holds: `app.sqlite` (the\n * per-version database snapshot), `db-backups/` (manual backups,\n * only kept for the current version), `snapshots/` (automatic daily\n * snapshots, only kept for the current version), `resources/<id>/`,\n * `characters/<id>/`, `plugins/<id>/` (installed content plugins\n * frozen with that version; the builtin `file` plugin is not stored\n * here). Old versions are FROZEN: no writes ever land in\n * `versions/<v>` once a `versions/<v+1>` exists.\n * - `local` holds host-only state. Derived caches (thumbs, previews,\n * extraction caches, tmp, read-only DB clones for past-version\n * viewing) live under `local/cache/` and are wiped wholesale by\n * clear cache; the rest (`logs`, `trash`, session key, upload\n * staging) is persistent. It never leaves the host.\n *\n * `paths.active` resolves to the **active** version (which may be the\n * latest version, or a past version when the user is viewing\n * read-only). `paths.latest` always points at the latest version --\n * the only version writers may target. `paths.atVersion(v)` exposes\n * arbitrary cross-version reads (used for character avatar/fullbody\n * fallback once `avatarVersion` / `fullbodyVersion` columns point at a\n * historical archive).\n *\n * Every resolved path MUST come out of this module so the frozen-version\n * boundary stays enforceable.\n */\nexport type StoragePaths = {\n\treadonly root: string\n\treadonly activeVersion: number\n\treadonly latestVersion: number\n\treadonly active: VersionPaths\n\treadonly latest: VersionPaths\n\treadonly local: LocalPaths\n\t/** Per-version archive paths. Use for cross-version fallback reads. */\n\tatVersion(v: number): VersionPaths\n\t/**\n\t * Path to the live runtime SQLite DB: `<root>/app.sqlite`.\n\t * This file is the only writable database during normal operation.\n\t * It lives outside `versions/` so that syncing `versions/` to other\n\t * devices cannot corrupt the in-use database. Only archived snapshots\n\t * (written by {@link createNextVersion}) and backup files belong in\n\t * `versions/`.\n\t */\n\truntimeDb(): string\n}\n\nexport type VersionPaths = {\n\treadonly root: string\n\treadonly version: number\n\t/** Path to the per-version SQLite DB: `<root>/versions/<v>/app.sqlite`. */\n\tversionSnapshotDb(): string\n\t/** Root folder of a resource: `<root>/versions/<v>/resources/<id>`. */\n\tresource(id: string): string\n\t/**\n\t * Content root of a resource — where user entries live:\n\t * `<root>/versions/<v>/resources/<id>/data`. The container reads\n\t * entries from here; the resource root only holds metadata dotfiles.\n\t */\n\tresourceData(id: string): string\n\t/** Root folder of all resources in this version: `<root>/versions/<v>/resources`. */\n\tresources(): string\n\t/** Root folder of all characters in this version: `<root>/versions/<v>/characters`. */\n\tcharacters(): string\n\t/** Root folder of all documents in this version: `<root>/versions/<v>/documents`. */\n\tdocuments(): string\n\t/** Root folder of a character: `<root>/versions/<v>/characters/<id>`. */\n\tcharacter(id: string): string\n\t/** Root of manual backups: `<root>/versions/<v>/db-backups`. */\n\tdbBackups(): string\n\t/** Path to one manual backup: `<root>/versions/<v>/db-backups/<name>`. */\n\tdbBackup(name: string): string\n\t/** Root of automatic snapshots: `<root>/versions/<v>/snapshots`. */\n\tsnapshots(): string\n\t/** Path to one automatic snapshot: `<root>/versions/<v>/snapshots/<name>`. */\n\tsnapshot(name: string): string\n\t/**\n\t * Path to a deleted-entity placeholder\n\t * (`<root>/versions/<v>/<kind>/<id>/.deleted`) written when hard\n\t * delete cannot remove a folder whose files live under frozen past\n\t * archives.\n\t */\n\tdeletedMarker(kind: \"resources\" | \"characters\", id: string): string\n\t/** Root folder of a document: `<root>/versions/<v>/documents/<id>`. */\n\tdocument(id: string): string\n\t/**\n\t * Installed content plugins for this version:\n\t * `<root>/versions/<v>/plugins`. Each subdirectory is named by\n\t * `manifest.id`. The builtin `file` plugin is not stored here.\n\t */\n\tplugins(): string\n\t/**\n\t * Host-managed plugin asset vault of an installed plugin:\n\t * `<root>/versions/<v>/plugins/<id>/vault`. Downloads land here\n\t * (user-consented, vault-confined); the directory is host-owned,\n\t * reserved against plugin zips (upload rejects a shipped `vault/`),\n\t * and its lifecycle follows the plugin's own directory.\n\t */\n\tpluginVaultDir(id: string): string\n}\n\nexport type LocalPaths = {\n\t/** The local (non-synced) root: `<root>/local`. */\n\treadonly root: string\n\t/** Derived cache root: `<localRoot>/cache`. */\n\tcache(): string\n\t/** Server logs: `<localRoot>/logs`. */\n\tlogs(): string\n\t/**\n\t * Path to a local derived cover/thumb variant:\n\t * `<localRoot>/cache/<resources|characters>/<id>/<variant>.<format>`.\n\t * Holds synthesized covers (resource covers, character avatars and\n\t * fullbody images); re-rendered when cleared.\n\t */\n\tlocalCover(\n\t\tsubjectKind: \"resource\" | \"character\",\n\t\tid: string,\n\t\tvariant: string,\n\t\tformat?: string,\n\t): string\n\t/**\n\t * Per-file derived image variant:\n\t * `<localRoot>/cache/resources/<id>/file-preview/<sourceCacheId>__<variantKey>.<format>`.\n\t * The source identity (see {@link sourceCacheId}) is collision-free\n\t * across nested paths; the variant key (see {@link imageVariantKey})\n\t * encodes the render spec, so distinct specs never collide on one\n\t * cache file.\n\t */\n\tresFileVariant(\n\t\tid: string,\n\t\tfilename: string,\n\t\tvariantKey: string,\n\t\tformat?: string,\n\t): string\n\t/** Directory holding a resource's per-file variants. */\n\tresFilePreviewDir(id: string): string\n\t/** Resource file-list sidecar: `<localRoot>/cache/resources/<id>/files-cache.json`. */\n\tresFilesCache(id: string): string\n\t/**\n\t * Root of the local per-resource directory:\n\t * `<localRoot>/cache/resources/<id>`.\n\t */\n\tresource(id: string): string\n\t/**\n\t * Root of the local per-character directory:\n\t * `<localRoot>/cache/characters/<id>`.\n\t * Holds (a) versioned copies of replaced avatar / fullbody images and\n\t * (b) thumbnail variants (`avatar.webp`, `fullbody.webp`).\n\t */\n\tcharacter(id: string): string\n\t/** Root of the trash: `<localRoot>/trash`. */\n\ttrash(): string\n\t/** Path to a single trashed item: `<localRoot>/trash/<id>`. */\n\ttrashItem(id: string): string\n\t/**\n\t * Root of the local temp directory: `<localRoot>/cache/tmp`. Holds\n\t * short-lived working files (upload buffers, backup markers, read-only\n\t * `view-<v>.sqlite` clones). Lives under the cache root so clear cache\n\t * wipes it together with the other derived data; it is also cleaned on\n\t * server startup.\n\t */\n\ttmp(): string\n\t/** Path to a single temp file: `<localRoot>/cache/tmp/<name>`. */\n\ttmpFile(name: string): string\n\t/**\n\t * Path to the iron-session seal key file: `<root>/local/.session-key`.\n\t * 32-byte base64-encoded secret, auto-generated on first boot. Lives in\n\t * `local/` (never synced) so each host has its own seal key.\n\t */\n\tsessionKey(): string\n\t/**\n\t * Root of the host-only temporary directory tree:\n\t * `<localRoot>/.tmp`. Holds the global staging pool\n\t * ({@link stagingPoolRoot}) plus short-lived extraction directories\n\t * (`extract-*`). The leading dot keeps host-only state out of any\n\t * user-facing listing. Cleared on server startup together with\n\t * {@link LocalPaths.tmp}.\n\t */\n\tuploadStagingRoot(): string\n\t/**\n\t * Root of the global staging pool: `<localRoot>/.tmp/staging`.\n\t * Every file uploaded through the per-file upload endpoint lands\n\t * here as `<fileId><ext>` and is addressed by its `fileId` alone —\n\t * there is no per-batch grouping. Files are removed individually\n\t * on client delete or consumed (and deleted) by commit at resource\n\t * creation. Cleared on startup together with\n\t * {@link LocalPaths.uploadStagingRoot}.\n\t */\n\tstagingPoolRoot(): string\n\t/**\n\t * Path of a single staged file in the global pool:\n\t * `<stagingPoolRoot>/<fileId><ext>`. `ext` is the lower-cased\n\t * extension of the original filename (empty for extensionless\n\t * uploads).\n\t */\n\tstagingPoolFile(fileId: string, ext: string): string\n\t/**\n\t * Per-video-frame thumbnail cache:\n\t * `<localRoot>/cache/resources/<id>/frames/<sourceCacheId>/<timeMs>.avif`.\n\t * Synthesised on-demand by the video hover preview endpoint.\n\t */\n\tresVideoFrame(id: string, filename: string, timeMs: number): string\n\t/**\n\t * Root of persisted zip-entry extractions for a resource version:\n\t * `<localRoot>/cache/resources/<id>/extracted/v<fileVersion>/`.\n\t */\n\tresExtractedDir(id: string, fileVersion: number): string\n\t/**\n\t * On-disk path for a materialized zip entry used by probe/ffmpeg\n\t * paths: `<localRoot>/cache/resources/<id>/extracted/v<v>/<sourceCacheId>`.\n\t */\n\tresExtractedEntry(id: string, fileVersion: number, entryName: string): string\n\t/**\n\t * Plugin container extraction root for a resource version:\n\t * `<localRoot>/cache/resources/<id>/extracted/v<fileVersion>/archives`.\n\t * `extractArchive` materializes each archive into its own\n\t * subdirectory here (`archives/<archiveName>/<innerPath>`), served to\n\t * the browser via the `/extracted/` route.\n\t */\n\tresExtractedArchivesDir(id: string, fileVersion: number): string\n}\n\nexport type CreateStoragePathsOptions = {\n\treadonly root: string\n\t/**\n\t * Active (viewing) version. When omitted together with latestVersion,\n\t * resolves from `local/version-state.json` and the version dirs under\n\t * `versions/`. When only latestVersion is pinned explicitly, defaults to\n\t * that same value (call sites that override max version only).\n\t */\n\treadonly activeVersion?: number\n\t/**\n\t * Latest (current, writable) version. When omitted, the maximum\n\t * version directory under `versions/` is used, or `1` when none exist.\n\t */\n\treadonly latestVersion?: number\n}\n\n/**\n * Build a {@link StoragePaths} rooted at `opts.root`. The root must be an\n * absolute path (the sync boundary would not be well-defined otherwise).\n *\n * @throws `Error` when `root` is not absolute.\n */\nexport function createStoragePaths(\n\topts: CreateStoragePathsOptions,\n): StoragePaths {\n\tif (!isAbsolute(opts.root)) {\n\t\tthrow new Error(`storage root must be an absolute path: ${opts.root}`)\n\t}\n\tconst root = resolve(opts.root)\n\tconst versionsRootPath = resolve(root, \"versions\")\n\tconst localRoot = resolve(root, \"local\")\n\n\tconst diskMax = diskCurrentVersion(root)\n\tlet latestVersion: number\n\tif (opts.latestVersion !== undefined) {\n\t\tlatestVersion = opts.latestVersion\n\t} else {\n\t\tlatestVersion = diskMax > 0 ? diskMax : 1\n\t}\n\n\tlet activeVersion: number\n\tif (opts.activeVersion !== undefined) {\n\t\tactiveVersion = opts.activeVersion\n\t} else if (opts.latestVersion !== undefined) {\n\t\tactiveVersion = latestVersion\n\t} else {\n\t\tactiveVersion = diskMax > 0 ? readActiveVersion(root) : latestVersion\n\t}\n\n\tfunction versionAt(version: number): VersionPaths {\n\t\tconst vSeg = assertSafeSegment(String(version))\n\t\tconst vRoot = join(versionsRootPath, vSeg)\n\t\treturn {\n\t\t\troot: vRoot,\n\t\t\tversion,\n\t\t\tversionSnapshotDb: () => join(vRoot, \"app.sqlite\"),\n\t\t\tresource: (id) => join(vRoot, \"resources\", assertSafeSegment(id)),\n\t\t\tresourceData: (id) =>\n\t\t\t\tjoin(vRoot, \"resources\", assertSafeSegment(id), RESOURCE_DATA_DIR_NAME),\n\t\t\tresources: () => join(vRoot, \"resources\"),\n\t\t\tcharacters: () => join(vRoot, \"characters\"),\n\t\t\tdocuments: () => join(vRoot, \"documents\"),\n\t\t\tcharacter: (id) => join(vRoot, \"characters\", assertSafeSegment(id)),\n\t\t\tdbBackups: () => join(vRoot, \"db-backups\"),\n\t\t\tdbBackup: (name) => join(vRoot, \"db-backups\", assertSafeSegment(name)),\n\t\t\tsnapshots: () => join(vRoot, \"snapshots\"),\n\t\t\tsnapshot: (name) => join(vRoot, \"snapshots\", assertSafeSegment(name)),\n\t\t\tdeletedMarker: (kind, id) =>\n\t\t\t\tjoin(vRoot, kind, assertSafeSegment(id), \".deleted\"),\n\t\t\tdocument: (id) => join(vRoot, \"documents\", assertSafeSegment(id)),\n\t\t\tplugins: () => join(vRoot, \"plugins\"),\n\t\t\tpluginVaultDir: (id) =>\n\t\t\t\tjoin(vRoot, \"plugins\", assertSafeSegment(id), \"vault\"),\n\t\t}\n\t}\n\n\tconst active = versionAt(activeVersion)\n\tconst latest = versionAt(latestVersion)\n\tconst uploadStagingRootPath = join(localRoot, \".tmp\")\n\tconst cacheRoot = join(localRoot, \"cache\")\n\n\tconst local: LocalPaths = {\n\t\troot: localRoot,\n\t\tcache: () => cacheRoot,\n\t\tlogs: () => join(localRoot, \"logs\"),\n\t\tlocalCover: (subjectKind, id, variant, format) =>\n\t\t\tjoin(\n\t\t\t\tcacheRoot,\n\t\t\t\tlocalCoverSubjectDir(subjectKind),\n\t\t\t\tassertSafeSegment(id),\n\t\t\t\t`${assertSafeSegment(variant)}.${format ?? \"avif\"}`,\n\t\t\t),\n\t\tresFileVariant: (id, filename, variantKey, format) =>\n\t\t\tjoin(\n\t\t\t\tcacheRoot,\n\t\t\t\t\"resources\",\n\t\t\t\tassertSafeSegment(id),\n\t\t\t\t\"file-preview\",\n\t\t\t\t`${assertSafeSegment(sourceCacheId(filename))}__${assertSafeSegment(variantKey)}.${format ?? \"avif\"}`,\n\t\t\t),\n\t\tresFilePreviewDir: (id) =>\n\t\t\tjoin(cacheRoot, \"resources\", assertSafeSegment(id), \"file-preview\"),\n\t\tresFilesCache: (id) =>\n\t\t\tjoin(cacheRoot, \"resources\", assertSafeSegment(id), \"files-cache.json\"),\n\t\tresource: (id) => join(cacheRoot, \"resources\", assertSafeSegment(id)),\n\t\tcharacter: (id) => join(cacheRoot, \"characters\", assertSafeSegment(id)),\n\t\ttrash: () => join(localRoot, \"trash\"),\n\t\ttrashItem: (id) => join(localRoot, \"trash\", assertSafeSegment(id)),\n\t\ttmp: () => join(cacheRoot, \"tmp\"),\n\t\ttmpFile: (name) => join(cacheRoot, \"tmp\", assertSafeSegment(name)),\n\t\tsessionKey: () => join(localRoot, \".session-key\"),\n\t\tuploadStagingRoot: () => uploadStagingRootPath,\n\t\tstagingPoolRoot: () => join(uploadStagingRootPath, \"staging\"),\n\t\tstagingPoolFile: (fileId, ext) =>\n\t\t\tjoin(\n\t\t\t\tuploadStagingRootPath,\n\t\t\t\t\"staging\",\n\t\t\t\t`${assertSafeSegment(fileId)}${ext}`,\n\t\t\t),\n\t\tresVideoFrame: (id, filename, timeMs) =>\n\t\t\tjoin(\n\t\t\t\tcacheRoot,\n\t\t\t\t\"resources\",\n\t\t\t\tassertSafeSegment(id),\n\t\t\t\t\"frames\",\n\t\t\t\tassertSafeSegment(sourceCacheId(filename)),\n\t\t\t\t`${timeMs}.avif`,\n\t\t\t),\n\t\tresExtractedDir: (id, fileVersion) =>\n\t\t\tjoin(\n\t\t\t\tcacheRoot,\n\t\t\t\t\"resources\",\n\t\t\t\tassertSafeSegment(id),\n\t\t\t\t\"extracted\",\n\t\t\t\t`v${fileVersion}`,\n\t\t\t),\n\t\tresExtractedEntry: (id, fileVersion, entryName) =>\n\t\t\tjoin(\n\t\t\t\tcacheRoot,\n\t\t\t\t\"resources\",\n\t\t\t\tassertSafeSegment(id),\n\t\t\t\t\"extracted\",\n\t\t\t\t`v${fileVersion}`,\n\t\t\t\tassertSafeSegment(sourceCacheId(entryName)),\n\t\t\t),\n\t\tresExtractedArchivesDir: (id, fileVersion) =>\n\t\t\tjoin(\n\t\t\t\tcacheRoot,\n\t\t\t\t\"resources\",\n\t\t\t\tassertSafeSegment(id),\n\t\t\t\t\"extracted\",\n\t\t\t\t`v${fileVersion}`,\n\t\t\t\t\"archives\",\n\t\t\t),\n\t}\n\n\treturn {\n\t\troot,\n\t\tactiveVersion,\n\t\tlatestVersion,\n\t\tactive,\n\t\tlatest,\n\t\tlocal,\n\t\tatVersion: (v) => versionAt(v),\n\t\truntimeDb: () => join(root, \"app.sqlite\"),\n\t}\n}\n\n/**\n * Map a {@link LocalPaths.thumb} subjectKind onto its on-disk subdirectory.\n * Variants now live flat inside the per-id local directory (no enclosing\n * `thumbs/` parent), so `resource` -> `resources` and `character` ->\n * `characters` (both plural to match the storage layout convention).\n */\nfunction localCoverSubjectDir(subjectKind: \"resource\" | \"character\"): string {\n\treturn subjectKind === \"resource\" ? \"resources\" : \"characters\"\n}\n\n/**\n * Map a source filename to the readable prefix of its derived-cache\n * identity (see {@link sourceCacheId}). Strips the source extension\n * before {@link LocalPaths.thumb} appends `.webp`, so `1.jpeg` becomes\n * `1__jpeg` (not `1.jpeg.webp`). The source extension is folded into\n * the basename so two sources sharing a stem (e.g. `01.png` /\n * `01.jpg`) cannot collide on the same cache file. Separators are\n * flattened so subdirectory entry names never trip the single-segment\n * path authority check.\n *\n * The encoding (`__<ext>`) avoids any character that\n * {@link assertSafeSegment} rejects (no dot, no separator, no control\n * char) so the result passes the boundary check unchanged.\n */\nfunction toCacheBasename(filename: string): string {\n\tconst dot = filename.lastIndexOf(\".\")\n\tif (dot <= 0) return filename.replace(/[/\\\\]/g, \"__\")\n\tconst stem = filename.slice(0, dot).replace(/[/\\\\]/g, \"__\")\n\tconst ext = filename.slice(dot + 1)\n\treturn `${stem}__${ext}`\n}\n\n/**\n * Stable, collision-free cache identity for one source file:\n * the flattened basename (human-readable) plus a short hash of the\n * full relative path. The hash disambiguates flattening ambiguities —\n * `a__b/c.png` and `a/b__c.png` both flatten to `a__b__c__png`, but\n * their paths hash differently, so distinct files never share a cache\n * entry. Pure hex suffix, so the result passes\n * {@link assertSafeSegment} unchanged.\n */\nfunction sourceCacheId(filename: string): string {\n\treturn `${toCacheBasename(filename)}__${shortHash(filename)}`\n}\n\n/**\n * Short, stable hash fragment: the first 8 hex chars of the sha256 of\n * `input`. Pure hex, so the result passes {@link assertSafeSegment}\n * unchanged.\n */\nfunction shortHash(input: string): string {\n\treturn createHash(\"sha256\").update(input).digest(\"hex\").slice(0, 8)\n}\n\n/**\n * Short, stable cache identity for one derived image variant: the\n * sha256 of the spec's canonical string (see\n * `imageVariantCanonical` in `@hoardodile/sdk-types/image-variant`),\n * truncated to 8 hex chars.\n */\nexport function imageVariantKey(canonical: string): string {\n\treturn shortHash(canonical)\n}\n\n/**\n * Validate a single path segment. We reject anything that embeds a path\n * separator, a drive letter, a NUL or other control code, a Windows\n * reserved basename, or a trailing dot/space (Windows normalises those away\n * and you get the wrong file).\n *\n * @throws `Error` when `segment` is empty or rejected by any of the rules.\n */\nexport function assertSafeSegment(segment: string): string {\n\tif (segment.length === 0) throw new Error(\"path segment must not be empty\")\n\tif (segment === \".\" || segment === \"..\") {\n\t\tthrow new Error(`path segment must not be '${segment}'`)\n\t}\n\tif (segment.includes(\"/\") || segment.includes(\"\\\\\")) {\n\t\tthrow new Error(`path segment must not contain separators: ${segment}`)\n\t}\n\tif (hasForbiddenVisibleChar(segment) || hasControlChar(segment)) {\n\t\tthrow new Error(`path segment contains disallowed characters: ${segment}`)\n\t}\n\tif (segment.endsWith(\".\") || segment.endsWith(\" \")) {\n\t\tthrow new Error(\n\t\t\t`path segment must not end with dot or space: ${JSON.stringify(segment)}`,\n\t\t)\n\t}\n\tconst base = segment.split(\".\")[0]?.toUpperCase()\n\tif (base !== undefined && WINDOWS_RESERVED.has(base)) {\n\t\tthrow new Error(`path segment is a reserved name: ${segment}`)\n\t}\n\treturn segment\n}\n\nfunction join(...segments: readonly string[]): string {\n\treturn resolve(...segments)\n}\n\n/**\n * Ensure `candidate` is contained within `ancestor` (after `resolve`). Used\n * as a belt-and-braces check before any disk operation that mixes user\n * input with a base directory.\n */\nexport function assertInside(ancestor: string, candidate: string): string {\n\tconst resolved = resolve(candidate)\n\tconst base = resolve(ancestor)\n\tif (resolved !== base && !resolved.startsWith(base + sep)) {\n\t\tthrow new Error(`path ${resolved} escapes ${base}`)\n\t}\n\treturn resolved\n}\n","/**\n * The plugin asset vault storage primitives: resolving vault-relative\n * destinations (the isolation boundary — a `dest` can never escape the\n * plugin's own vault), reading/statting vault files, removing them, and\n * committing downloaded files atomically.\n *\n * The vault root is `<version>/plugins/<id>/vault` (see\n * {@link VersionPaths.pluginVaultDir}); the primitives operate on an\n * explicit vault directory so the server can pass the `latest` version\n * for writes (inside `writeVersioned`) or the active version for reads.\n *\n * Cost note (deliberate): the vault lives inside the versioned tree, so\n * each archive version snapshots a full copy of it — durability over\n * deduplication, same as `resources/<id>/data/`.\n *\n * Misuse answers {@link PluginAssetError} with `POLICY` so the plugin\n * sees a machine-readable rejection before any network happens.\n */\nimport { createHash, randomUUID } from \"node:crypto\"\nimport { createReadStream } from \"node:fs\"\nimport { mkdir, readFile, rename, rm, stat } from \"node:fs/promises\"\nimport { join, resolve } from \"node:path\"\nimport { pluginAssetError } from \"@hoardodile/sdk-types\"\nimport { sumDirSizes } from \"./dir-size.ts\"\nimport { assertInside, assertSafeSegment } from \"./paths.ts\"\n\n/** Thrown when a vault path violates the isolation rules (POLICY). */\nexport class PluginVaultPathError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message)\n\t\tthis.name = \"PluginVaultPathError\"\n\t}\n}\n\n/**\n * Parse a vault-relative destination and resolve it against the vault\n * root. Every segment goes through {@link assertSafeSegment} (no empty\n * segments, `.`/`..`, separators inside a segment, drive letters,\n * control chars, Windows-reserved names, trailing dot/space) and the\n * final path is re-checked with {@link assertInside} — doubly so, the\n * whole point is that a downloaded file can only ever land inside the\n * plugin's own vault.\n */\nexport function parsePluginVaultDest(\n\tvaultDir: string,\n\tdest: string,\n): { readonly rel: string; readonly abs: string } {\n\tif (dest.length === 0) {\n\t\tthrow new PluginVaultPathError(\"plugin vault destination must not be empty\")\n\t}\n\tconst segments = dest.split(\"/\")\n\tlet abs = vaultDir\n\tfor (const segment of segments) {\n\t\tif (segment.length === 0) {\n\t\t\tthrow new PluginVaultPathError(\n\t\t\t\t`plugin vault destination must not contain empty segments: ${JSON.stringify(dest)}`,\n\t\t\t)\n\t\t}\n\t\ttry {\n\t\t\tabs = join(abs, assertSafeSegment(segment))\n\t\t} catch (err) {\n\t\t\t// assertSafeSegment answers plain Errors — the boundary keeps\n\t\t\t// one vocabulary so callers can map them to POLICY uniformly.\n\t\t\tthrow new PluginVaultPathError(\n\t\t\t\terr instanceof Error ? err.message : String(err),\n\t\t\t)\n\t\t}\n\t}\n\ttry {\n\t\tconst resolved = assertInside(vaultDir, abs)\n\t\treturn { rel: segments.join(\"/\"), abs: resolved }\n\t} catch (err) {\n\t\tthrow new PluginVaultPathError(\n\t\t\terr instanceof Error ? err.message : String(err),\n\t\t)\n\t}\n}\n\n/**\n * Byte size of a vault file (or `undefined` when absent or not a\n * regular file). The cheap presence check `download` builds on.\n */\nexport async function vaultStatFile(\n\tvaultDir: string,\n\trel: string,\n): Promise<{ readonly sizeBytes: number } | undefined> {\n\tconst { abs } = parsePluginVaultDest(vaultDir, rel)\n\tconst info = await stat(abs).catch(() => undefined)\n\tif (info === undefined || !info.isFile()) return undefined\n\treturn { sizeBytes: info.size }\n}\n\n/** Stream a vault file's bytes (bounded by `maxBytes`). */\nexport async function vaultReadFile(\n\tvaultDir: string,\n\trel: string,\n\tmaxBytes: number,\n): Promise<Uint8Array> {\n\tconst { abs } = parsePluginVaultDest(vaultDir, rel)\n\tconst info = await stat(abs).catch(() => undefined)\n\tif (info === undefined || !info.isFile()) {\n\t\tthrow pluginAssetError(\"POLICY\", `plugin vault file not found: ${rel}`)\n\t}\n\tif (info.size > maxBytes) {\n\t\tthrow pluginAssetError(\n\t\t\t\"POLICY\",\n\t\t\t`plugin vault file \"${rel}\" exceeds the ${maxBytes}-byte read cap`,\n\t\t)\n\t}\n\tconst bytes = await readFile(abs)\n\treturn new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength)\n}\n\n/**\n * Remove a vault file. Idempotent (absent → false); directories and\n * anything outside the vault are rejected.\n */\nexport async function vaultRemoveFile(\n\tvaultDir: string,\n\trel: string,\n): Promise<boolean> {\n\tconst { abs } = parsePluginVaultDest(vaultDir, rel)\n\tconst info = await stat(abs).catch(() => undefined)\n\tif (info === undefined) return false\n\tif (!info.isFile()) {\n\t\tthrow pluginAssetError(\n\t\t\t\"POLICY\",\n\t\t\t`plugin vault destination is not a file: ${rel}`,\n\t\t)\n\t}\n\tawait rm(abs)\n\treturn true\n}\n\n/**\n * Path of the caller's download temp file inside the vault (host-side\n * staging — never a user-visible name). The downloader streams into it;\n * {@link commitVaultFile} renames it into place.\n */\nexport function vaultTempFile(vaultDir: string): string {\n\treturn join(vaultDir, `.${randomUUID()}.tmp`)\n}\n\n/** Delete a temp file (download failure path). */\nexport async function discardVaultTempFile(tempPath: string): Promise<void> {\n\tawait rm(tempPath, { force: true }).catch(() => {})\n}\n\n/**\n * Commit a downloaded temp file into the vault: mkdir the nested\n * destination, verify the per-plugin total-size budget (the destination's\n * own previous bytes count against the quota only once — re-downloading\n * replaces, it does not add), then rename the temp file into place\n * (atomic — the final path never holds a partial download).\n */\nexport async function commitVaultFile(opts: {\n\treadonly vaultDir: string\n\treadonly rel: string\n\treadonly tempPath: string\n\treadonly maxFileBytes: number\n\treadonly maxTotalBytes: number\n}): Promise<{ readonly sizeBytes: number; readonly sha256: string }> {\n\tconst { abs } = parsePluginVaultDest(opts.vaultDir, opts.rel)\n\tconst existing = await stat(abs).catch(() => undefined)\n\tif (existing !== undefined && !existing.isFile()) {\n\t\tthrow pluginAssetError(\n\t\t\t\"POLICY\",\n\t\t\t`plugin vault destination is not a file: ${opts.rel}`,\n\t\t)\n\t}\n\tconst tempInfo = await stat(opts.tempPath).catch(() => undefined)\n\tif (tempInfo === undefined || !tempInfo.isFile()) {\n\t\tthrow pluginAssetError(\n\t\t\t\"POLICY\",\n\t\t\t`plugin vault download lost its staging file: ${opts.rel}`,\n\t\t)\n\t}\n\tif (tempInfo.size > opts.maxFileBytes) {\n\t\tthrow pluginAssetError(\n\t\t\t\"POLICY\",\n\t\t\t`plugin vault file \"${opts.rel}\" exceeds the ${opts.maxFileBytes}-byte download cap`,\n\t\t)\n\t}\n\tconst currentTotal = await vaultTotalSize(opts.vaultDir)\n\tconst replacedBytes = existing?.isFile() ? existing.size : 0\n\tif (currentTotal - replacedBytes + tempInfo.size > opts.maxTotalBytes) {\n\t\tthrow pluginAssetError(\n\t\t\t\"POLICY\",\n\t\t\t`plugin vault \"${opts.rel}\" would exceed the ${opts.maxTotalBytes}-byte plugin quota (current ${currentTotal})`,\n\t\t)\n\t}\n\tawait mkdir(resolve(abs, \"..\"), { recursive: true })\n\tawait rename(opts.tempPath, abs)\n\treturn { sizeBytes: tempInfo.size, sha256: await hashFileAbs(abs) }\n}\n\n/**\n * Total bytes of the vault's regular files (shared directory-size walk;\n * host temp staging files — leading `.` — are excluded as transient).\n */\nexport async function vaultTotalSize(vaultDir: string): Promise<number> {\n\treturn sumDirSizes(vaultDir, { excludeDotPrefix: true })\n}\n\n/** sha256 of a vault file's bytes (streamed; callers verify pins against it). */\nexport async function vaultFileSha256(\n\tvaultDir: string,\n\trel: string,\n): Promise<string> {\n\tconst { abs } = parsePluginVaultDest(vaultDir, rel)\n\tconst info = await stat(abs).catch(() => undefined)\n\tif (info === undefined || !info.isFile()) {\n\t\tthrow pluginAssetError(\"POLICY\", `plugin vault file not found: ${rel}`)\n\t}\n\treturn hashFileAbs(abs)\n}\n\nasync function hashFileAbs(abs: string): Promise<string> {\n\tconst hash = createHash(\"sha256\")\n\tawait new Promise<void>((resolveDone, reject) => {\n\t\tconst stream = createReadStream(abs)\n\t\tstream.on(\"data\", (chunk: string | Buffer) => hash.update(chunk))\n\t\tstream.on(\"end\", () => resolveDone())\n\t\tstream.on(\"error\", reject)\n\t})\n\treturn hash.digest(\"hex\")\n}\n\n/** Result of {@link commitVaultFile}: the stored file's identity. */\nexport type VaultCommitResult = {\n\treadonly sizeBytes: number\n\treadonly sha256: string\n}\n","/**\n * Filename hygiene for bare-file resources: map a user-supplied name\n * (upload filename or zip entry name) onto a safe relative path inside\n * a resource folder.\n *\n * Resource folders hold user entries next to metadata dotfiles\n * (`.cover.*`, `.deleted`), so every rule here also guarantees the result\n * can never collide with those: leading dots are stripped, and a\n * trailing-dot/space rule mirrors Windows normalization.\n *\n * Cross-platform guarantees: names are NFC-normalized (macOS stores\n * NFD on disk — one canonical form keeps names stable when a library\n * moves between systems), segments are capped at 240 UTF-8 bytes\n * (ext4's 255-byte component limit, minus suffix headroom), and the\n * Windows reserved-name set includes the device aliases (`CONIN$`,\n * superscript COM/LPT variants).\n *\n * The migration script (`scripts/migrate-hoard-to-files.mjs`) mirrors\n * these rules verbatim — keep both in sync when changing them.\n */\n\nconst FORBIDDEN_VISIBLE_CHARS = '<>:\"|?*'\n\n/** Max UTF-8 bytes per path segment (ext4 limit 255, suffix headroom). */\nconst MAX_SEGMENT_BYTES = 240\n\n/** Max characters for the whole relative path. */\nconst MAX_REL_PATH_CHARS = 700\n\n/** Safety net for the collision-suffix loop in {@link uniqueEntryName}. */\nconst MAX_UNIQUE_RETRIES = 10_000\n\n/** Windows reserved base names (case-insensitive), incl. device aliases. */\nconst WINDOWS_RESERVED = new Set([\n\t\"CON\",\n\t\"PRN\",\n\t\"AUX\",\n\t\"NUL\",\n\t\"COM0\",\n\t\"COM1\",\n\t\"COM2\",\n\t\"COM3\",\n\t\"COM4\",\n\t\"COM5\",\n\t\"COM6\",\n\t\"COM7\",\n\t\"COM8\",\n\t\"COM9\",\n\t\"LPT0\",\n\t\"LPT1\",\n\t\"LPT2\",\n\t\"LPT3\",\n\t\"LPT4\",\n\t\"LPT5\",\n\t\"LPT6\",\n\t\"LPT7\",\n\t\"LPT8\",\n\t\"LPT9\",\n\t\"CONIN$\",\n\t\"CONOUT$\",\n\t\"COM\\u00b9\",\n\t\"COM\\u00b2\",\n\t\"COM\\u00b3\",\n\t\"LPT\\u00b9\",\n\t\"LPT\\u00b2\",\n\t\"LPT\\u00b3\",\n])\n\n/**\n * Sanitize a single entry name into a safe relative path. Separators\n * (`/` and `\\`) become subdirectory boundaries; `..` and absolute paths\n * are rejected. Returns `undefined` when the name is unusable (empty,\n * escapes the folder, or cleans to nothing) — callers drop or reject\n * such entries.\n */\nexport function sanitizeEntryName(name: string): string | undefined {\n\tif (name.length === 0 || name.includes(\"\\0\")) return undefined\n\t// One canonical Unicode form so names survive filesystem\n\t// normalization (macOS NFD) when a library moves between hosts.\n\tconst normalized = name.normalize(\"NFC\")\n\tconst rawSegments = normalized.replace(/\\\\/g, \"/\").split(\"/\")\n\tconst segments: string[] = []\n\tfor (const raw of rawSegments) {\n\t\tconst cleaned = cleanSegment(raw)\n\t\tif (cleaned === undefined) continue\n\t\tsegments.push(cleaned)\n\t}\n\tif (segments.length === 0) return undefined\n\tconst relPath = segments.join(\"/\")\n\tif (relPath.length > MAX_REL_PATH_CHARS) return undefined\n\treturn relPath\n}\n\n/** Clean one path segment; `undefined` when it must be dropped entirely. */\nfunction cleanSegment(raw: string): string | undefined {\n\tif (raw.length === 0 || raw === \".\" || raw === \"..\") return undefined\n\tlet out = \"\"\n\tfor (const ch of raw) {\n\t\tconst code = ch.codePointAt(0)\n\t\tif (code !== undefined && code < 32) continue\n\t\tif (FORBIDDEN_VISIBLE_CHARS.includes(ch)) {\n\t\t\tout += \"_\"\n\t\t\tcontinue\n\t\t}\n\t\tout += ch\n\t}\n\t// Leading dots would make the entry a dotfile (colliding with the\n\t// metadata namespace `.cover.*` / `.deleted`); trailing dots and\n\t// spaces vanish on Windows and would break path equality.\n\tlet trimmed = out.replace(/^\\.+/, \"\").replace(/[. ]+$/, \"\")\n\tif (trimmed.length === 0) return undefined\n\ttrimmed = truncateUtf8(trimmed, MAX_SEGMENT_BYTES)\n\tif (trimmed.length === 0) return undefined\n\tconst base = trimmed.split(\".\")[0]?.toUpperCase()\n\tif (base !== undefined && WINDOWS_RESERVED.has(base)) {\n\t\ttrimmed = `_${trimmed}`\n\t}\n\treturn trimmed\n}\n\n/** Truncate `value` so its UTF-8 length stays within `maxBytes`. */\nfunction truncateUtf8(value: string, maxBytes: number): string {\n\tlet bytes = 0\n\tlet out = \"\"\n\tfor (const ch of value) {\n\t\tconst len = Buffer.byteLength(ch, \"utf8\")\n\t\tif (bytes + len > maxBytes) break\n\t\tbytes += len\n\t\tout += ch\n\t}\n\treturn out\n}\n\n/**\n * Occupancy bookkeeping for name resolution: which paths are already\n * taken by files (blocking everything) and which are directories\n * (blocking only exact-path installs — a file cannot replace a folder).\n */\nexport type OccupiedNames = {\n\treadonly files: Set<string>\n\treadonly dirs: Set<string>\n}\n\n/** Build occupancy from optional existing on-disk names (with types). */\nexport function createOccupiedNames(existing?: {\n\treadonly files?: Iterable<string>\n\treadonly dirs?: Iterable<string>\n}): OccupiedNames {\n\treturn {\n\t\tfiles: new Set(existing?.files ?? []),\n\t\tdirs: new Set(existing?.dirs ?? []),\n\t}\n}\n\n/**\n * Record `relPath` as an installed file; every ancestor prefix becomes\n * a directory. A later entry colliding with one of the ancestors (a\n * file that sits on a directory path) is resolved by\n * {@link uniqueEntryName}.\n */\nexport function occupyEntryName(\n\toccupied: OccupiedNames,\n\trelPath: string,\n): void {\n\toccupied.files.add(relPath)\n\tlet prefix = \"\"\n\tfor (const segment of relPath.split(\"/\").slice(0, -1)) {\n\t\tprefix = prefix.length > 0 ? `${prefix}/${segment}` : segment\n\t\toccupied.dirs.add(prefix)\n\t}\n}\n\n/**\n * Resolve a sanitized relative path against the names already present\n * in the target folder by suffixing the colliding segment (`-1`, `-2`,\n * …). The comparison is case-insensitive (Windows and macOS filesystems\n * are). Files block their path and every ancestor prefix; directories\n * block only their exact path. The suffix lands on the segment whose\n * prefix collides, so `x/y.txt` against an occupied file `x` becomes\n * `x-1/y.txt`, not `x/y-1.txt` (which would collide forever).\n */\nexport function uniqueEntryName(\n\toccupied: OccupiedNames,\n\trelPath: string,\n): string {\n\tconst lowerFiles = new Set([...occupied.files].map((p) => p.toLowerCase()))\n\tconst lowerDirs = new Set([...occupied.dirs].map((p) => p.toLowerCase()))\n\tlet candidate = relPath\n\tfor (let guard = 0; guard < MAX_UNIQUE_RETRIES; guard += 1) {\n\t\tconst segIdx = collidingSegment(lowerFiles, lowerDirs, candidate)\n\t\tif (segIdx < 0) return candidate\n\t\tconst segments = candidate.split(\"/\")\n\t\tconst target = segments[segIdx]!\n\t\tconst dot = target.lastIndexOf(\".\")\n\t\tconst stem = dot > 0 ? target.slice(0, dot) : target\n\t\tconst ext = dot > 0 ? target.slice(dot) : \"\"\n\t\tfor (let i = 1; ; i += 1) {\n\t\t\tconst next = `${stem}-${i}${ext}`\n\t\t\tconst rebuilt = segments\n\t\t\t\t.map((seg, idx) => (idx === segIdx ? next : seg))\n\t\t\t\t.join(\"/\")\n\t\t\tif (collidingSegment(lowerFiles, lowerDirs, rebuilt) < 0) {\n\t\t\t\tcandidate = rebuilt\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tthrow new Error(`unable to resolve a unique name for ${relPath}`)\n}\n\n/**\n * Index of the segment whose path prefix is taken by a FILE, or whose\n * exact path is taken by a file or directory, or `-1`. Directory\n * ancestors are not collisions (nested entries need them).\n */\nfunction collidingSegment(\n\tlowerFiles: ReadonlySet<string>,\n\tlowerDirs: ReadonlySet<string>,\n\tcandidate: string,\n): number {\n\tconst segments = candidate.split(\"/\")\n\tconst lower = candidate.toLowerCase()\n\tif (lowerFiles.has(lower) || lowerDirs.has(lower)) return segments.length - 1\n\tlet prefix = \"\"\n\tfor (let i = 0; i < segments.length - 1; i += 1) {\n\t\tprefix = prefix.length > 0 ? `${prefix}/${segments[i]}` : segments[i]!\n\t\tif (lowerFiles.has(prefix.toLowerCase())) return i\n\t}\n\treturn -1\n}\n","import { createWriteStream } from \"node:fs\"\nimport { mkdir, readdir, rename, rm, stat } from \"node:fs/promises\"\nimport { basename, extname, join } from \"node:path\"\nimport { pipeline } from \"node:stream/promises\"\nimport type { StoragePaths } from \"./paths.ts\"\n\n/**\n * Extension used for archive (zip) uploads staged in the global pool.\n * The original filename is ignored — archive uploads are always stored\n * as `<fileId>.zip` so commit / extraction can locate them by `fileId`\n * alone.\n */\nconst STAGED_ARCHIVE_EXT = \".zip\"\n\n/**\n * Resolve the on-disk path of a file staged in the global pool by\n * `fileId`. The pool is flat: each file is stored as `<fileId><ext>`\n * where `ext` is the lower-cased extension of the original filename\n * (or `.zip` for archive uploads). Because the extension is part of the\n * filename we scan the directory for a sibling whose stem matches\n * `fileId`.\n *\n * Returns `undefined` when no staged file matches `fileId`.\n */\nexport async function findStagedPoolFile(\n\tpaths: StoragePaths,\n\tfileId: string,\n): Promise<string | undefined> {\n\tconst poolDir = paths.local.stagingPoolRoot()\n\tconst names = await readdir(poolDir).catch(() => [])\n\tfor (const name of names) {\n\t\tconst ext = extname(name)\n\t\tconst stem = basename(name, ext)\n\t\tif (stem === fileId) return join(poolDir, name)\n\t}\n\treturn undefined\n}\n\n/**\n * Resolve multiple pool files by `fileId` with a SINGLE directory scan.\n * Committing an ordered upload resolves and then consumes every staged\n * file; the per-file {@link findStagedPoolFile} scan would cost one full\n * `readdir` per file, and the pool can hold hundreds of entries under\n * concurrent uploads. Returns a map keyed by `fileId`; ids without a\n * staged match are absent.\n */\nexport async function resolveStagedPoolFiles(\n\tpaths: StoragePaths,\n\tfileIds: readonly string[],\n): Promise<Map<string, string>> {\n\tconst poolDir = paths.local.stagingPoolRoot()\n\tconst names = await readdir(poolDir).catch(() => [])\n\tconst byStem = new Map<string, string>()\n\tfor (const name of names) {\n\t\tconst ext = extname(name)\n\t\tconst stem = basename(name, ext)\n\t\tif (!byStem.has(stem)) byStem.set(stem, join(poolDir, name))\n\t}\n\tconst out = new Map<string, string>()\n\tfor (const fileId of fileIds) {\n\t\tconst hit = byStem.get(fileId)\n\t\tif (hit !== undefined) out.set(fileId, hit)\n\t}\n\treturn out\n}\n\n/**\n * Stream a single file into the global staging pool as\n * `<fileId><ext>`. The file is first written to a `.tmp-*` sibling and\n * atomically renamed so concurrent readers (preview) never observe a\n * partial file. Returns the final staged path.\n */\nexport async function writeStagedPoolFile(\n\tpaths: StoragePaths,\n\tfileId: string,\n\tfilename: string,\n\tstream: NodeJS.ReadableStream,\n): Promise<string> {\n\tawait mkdir(paths.local.stagingPoolRoot(), { recursive: true })\n\tconst ext = extname(filename).toLowerCase()\n\tconst dest = paths.local.stagingPoolFile(fileId, ext)\n\tconst tmp = `${dest}.tmp-${Date.now()}`\n\ttry {\n\t\tawait pipeline(stream, createWriteStream(tmp))\n\t\tawait rename(tmp, dest)\n\t\treturn dest\n\t} catch (err) {\n\t\tawait rm(tmp, { force: true }).catch(() => {})\n\t\tthrow err\n\t}\n}\n\n/**\n * Stream an archive (zip) upload into the global staging pool as\n * `<fileId>.zip`. Same atomic write semantics as\n * {@link writeStagedPoolFile}. Returns the final staged path.\n */\nexport async function writeStagedArchiveFile(\n\tpaths: StoragePaths,\n\tfileId: string,\n\tstream: NodeJS.ReadableStream,\n): Promise<string> {\n\treturn writeStagedPoolFile(paths, fileId, \"archive.zip\", stream)\n}\n\n/** Resolve the staged path of an archive upload by `fileId`. */\nexport async function findStagedArchiveFile(\n\tpaths: StoragePaths,\n\tfileId: string,\n): Promise<string | undefined> {\n\tconst existing = await findStagedPoolFile(paths, fileId)\n\tif (existing === undefined) return undefined\n\t// Archives are always stored with the `.zip` extension.\n\treturn extname(existing).toLowerCase() === STAGED_ARCHIVE_EXT\n\t\t? existing\n\t\t: undefined\n}\n\n/**\n * Remove a single file from the global staging pool. Returns `true` when\n * a file was removed, `false` when `fileId` was not present.\n */\nexport async function removeStagedPoolFile(\n\tpaths: StoragePaths,\n\tfileId: string,\n): Promise<boolean> {\n\tconst existing = await findStagedPoolFile(paths, fileId)\n\tif (existing === undefined) return false\n\tawait rm(existing, { force: true }).catch(() => {})\n\t// Best-effort: the file may have been removed concurrently.\n\tconst stillThere = await stat(existing).catch(() => undefined)\n\treturn stillThere === undefined\n}\n","import { conflict } from \"../errors.ts\"\nimport type { StoragePaths } from \"./paths.ts\"\n\n/**\n * Command that performs a write under the **current** (latest, writable)\n * archive version. Receiving `paths.latest` instead of the whole\n * {@link StoragePaths} object makes it impossible for callers to accidentally\n * target `paths.active`, which may point at a frozen past version when the\n * server is in read-only viewing mode.\n */\nexport type VersionedWriteCommand<T> = (\n\tpaths: StoragePaths[\"latest\"],\n) => T | Promise<T>\n\n/**\n * Central gate for every file-system write that lands under `versions/<v>`.\n *\n * - Blocks the operation when `readOnly` is true, surfacing a domain conflict\n * with code `server.read_only_archive`.\n * - Forces the command to operate on `paths.latest` (the latest archive\n * version) so past frozen versions can never be mutated.\n *\n * Local-only writes (thumbs, caches, staging pool, trash) should NOT use this\n * helper; they live under `local/` and are governed by the route-level\n * write gate instead.\n */\nexport async function writeVersioned<T>(\n\tpaths: StoragePaths,\n\treadOnly: boolean,\n\tcmd: VersionedWriteCommand<T>,\n): Promise<T> {\n\tif (readOnly) {\n\t\tthrow conflict(\n\t\t\t\"server.read_only_archive\",\n\t\t\t\"server is viewing a read-only archive; versioned writes are blocked\",\n\t\t)\n\t}\n\treturn await cmd(paths.latest)\n}\n","import { mkdir, readdir, rename, rm, writeFile } from \"node:fs/promises\"\nimport { extname, join } from \"node:path\"\nimport type { StoragePaths } from \"./paths.ts\"\nimport { writeVersioned } from \"./write-versioned.ts\"\n\nexport type VersionedFolderOps = {\n\t/** Ensure the current-version entity folder exists. */\n\tensureFolder(id: string): Promise<void>\n\t/** Remove the current-version entity folder. Swallows missing-path errors. */\n\tremoveFolder(id: string): Promise<void>\n\t/**\n\t * When an entity's files live only under frozen past archives, hard\n\t * delete cannot remove those folders; drop a `.deleted` placeholder in\n\t * the current-version folder instead.\n\t */\n\tmarkDeleted(id: string): Promise<string>\n\t/**\n\t * Move the current-version entity folder into `local/trash/` with a\n\t * unique directory name. No-op when the source path is missing (same as\n\t * a removed folder). Returns the destination path (whether or not a move\n\t * occurred).\n\t */\n\tmoveFolderToTrash(id: string): Promise<string>\n}\n\n/**\n * The four lifecycle operations shared by the resource and character\n * file-system layers. They differ only in which versioned folder they\n * target and how the trash / placeholder names are derived.\n *\n * `moveFolderToTrash` treats `EPERM`/`EBUSY`/`UNKNOWN` as transient Windows\n * locks (a file inside the folder is still open) and leaves the source in\n * place; a boot-time orphan sweep reclaims it later.\n *\n * `readOnly` is a live `{ current: boolean }` ref (the server's runtime\n * read-only flag), so a version switch mid-request re-reads it.\n */\nexport function buildVersionedFolderOps(\n\tpaths: StoragePaths,\n\treadOnly: { readonly current: boolean },\n\tkind: \"resource\" | \"character\",\n): VersionedFolderOps {\n\tconst folderOf =\n\t\t(id: string) =>\n\t\t(current: StoragePaths[\"latest\"]): string =>\n\t\t\tkind === \"resource\" ? current.resource(id) : current.character(id)\n\tconst trashPrefix = kind === \"resource\" ? \"resources-\" : \"characters-\"\n\tconst deletedKind = kind === \"resource\" ? \"resources\" : \"characters\"\n\n\tasync function ensureFolder(id: string): Promise<void> {\n\t\tawait writeVersioned(paths, readOnly.current, (current) =>\n\t\t\tmkdir(folderOf(id)(current), { recursive: true }),\n\t\t)\n\t}\n\n\tasync function removeFolder(id: string): Promise<void> {\n\t\tawait writeVersioned(paths, readOnly.current, (current) =>\n\t\t\trm(folderOf(id)(current), {\n\t\t\t\trecursive: true,\n\t\t\t\tforce: true,\n\t\t\t}).catch(() => {}),\n\t\t)\n\t}\n\n\tasync function markDeleted(id: string): Promise<string> {\n\t\treturn writeVersioned(paths, readOnly.current, async (current) => {\n\t\t\tconst folder = folderOf(id)(current)\n\t\t\tawait mkdir(folder, { recursive: true })\n\t\t\tconst marker = current.deletedMarker(deletedKind, id)\n\t\t\t// Empty file: the placeholder's existence is the whole signal.\n\t\t\tawait writeFile(marker, \"\")\n\t\t\treturn marker\n\t\t})\n\t}\n\n\tasync function moveFolderToTrash(id: string): Promise<string> {\n\t\t// write-local-only: trash directory is under local/, not versions/.\n\t\tawait mkdir(paths.local.trash(), { recursive: true })\n\t\treturn writeVersioned(paths, readOnly.current, async (current) => {\n\t\t\tconst src = folderOf(id)(current)\n\t\t\tconst dest = join(\n\t\t\t\tpaths.local.trash(),\n\t\t\t\t`${trashPrefix}${id}-${Date.now()}`,\n\t\t\t)\n\t\t\ttry {\n\t\t\t\tawait rename(src, dest)\n\t\t\t} catch (err) {\n\t\t\t\tconst code = (err as NodeJS.ErrnoException).code\n\t\t\t\tif (code === \"ENOENT\") {\n\t\t\t\t\t// Already gone; return dest as a conventional indicator.\n\t\t\t\t\treturn dest\n\t\t\t\t}\n\t\t\t\t// Windows may throw EPERM/EBUSY/UNKNOWN when a file inside src is\n\t\t\t\t// still open (thumb pipeline, HTTP stream, zip handle, ...). Don't\n\t\t\t\t// let a transient lock block the hard-delete; an orphan sweep at\n\t\t\t\t// boot will reclaim the leftover folder later.\n\t\t\t\tif (code === \"EPERM\" || code === \"EBUSY\" || code === \"UNKNOWN\") {\n\t\t\t\t\treturn src\n\t\t\t\t}\n\t\t\t\tthrow err\n\t\t\t}\n\t\t\treturn dest\n\t\t})\n\t}\n\n\treturn {\n\t\tensureFolder,\n\t\tremoveFolder,\n\t\tmarkDeleted,\n\t\tmoveFolderToTrash,\n\t}\n}\n\n/**\n * Move every file in `sourceFolder` whose name matches `match` into\n * `destFolder` under a timestamped archive name (`<prefix><stamp><ext>`, or\n * `<prefix><stamp>_<i><ext>` when several files match). Moves to the local\n * (non-synced) directory so replaced files are preserved without polluting\n * the versions sync scope. Creates `destFolder` on demand; silently ignores\n * ENOENT on source files and a missing source folder.\n */\nexport async function archiveStaleFiles(args: {\n\treadonly sourceFolder: string\n\treadonly destFolder: string\n\treadonly match: (name: string) => boolean\n\treadonly archivePrefix: string\n}): Promise<void> {\n\tconst { sourceFolder, destFolder, match, archivePrefix } = args\n\tconst entries = await readdir(sourceFolder).catch(() => [])\n\tconst stale = entries.filter(match)\n\tif (stale.length === 0) return\n\tawait mkdir(destFolder, { recursive: true })\n\tconst stamp = Date.now()\n\tawait Promise.all(\n\t\tstale.map(async (name, i) => {\n\t\t\tconst ext = extname(name)\n\t\t\tconst archiveName =\n\t\t\t\tstale.length === 1\n\t\t\t\t\t? `${archivePrefix}${stamp}${ext}`\n\t\t\t\t\t: `${archivePrefix}${stamp}_${i}${ext}`\n\t\t\ttry {\n\t\t\t\tawait rename(join(sourceFolder, name), join(destFolder, archiveName))\n\t\t\t} catch (err) {\n\t\t\t\tif (!isEnoentError(err)) throw err\n\t\t\t}\n\t\t}),\n\t)\n}\n\nfunction isEnoentError(err: unknown): boolean {\n\treturn err instanceof Error && \"code\" in err && err.code === \"ENOENT\"\n}\n"]}
|