@hwp-editor/server 1.0.0-rc.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli-engine.ts","../src/session.ts","../src/routes.ts"],"sourcesContent":["/**\n * CliEngine — HwpEngine implementation that shells out to the hwp-cli binary.\n *\n * Hardening ported from the ax deployment wrapper (sites/ax/lib/hwp-cli.ts):\n * execFile only (never a shell), an owned 60s budget enforced by this\n * module's own AbortController with SIGTERM-to-SIGKILL escalation (execFile's\n * built-in `timeout` signals once and never escalates, so a signal-ignoring\n * child would hang the request past every budget), a 32MB maxBuffer on every\n * invocation, a scrubbed child environment, and per-call temp directories\n * that are removed on every path including failure. The runCli promise\n * settles ONLY from the execFile callback, which fires after the child has\n * exited; that is what keeps `withWorkDir`'s removal ordered strictly after\n * child exit, so a racing timer must never settle it. Generalizations: the\n * binary is resolved by option/env/PATH instead of a bundled artifact (this\n * package runs on developer machines and servers, not one fixed lambda), and\n * the per-process verification is a minimum-version check instead of a\n * pinned checksum (there is no single reviewed artifact here).\n */\n\nimport { execFile } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { copyFile, mkdtemp, readdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\n\nimport {\n OP_FLAGS,\n opsToArgv,\n parseCatEnvelope,\n protectedReasonFromDiagnostics,\n type Capabilities,\n type CatEnvelope,\n type ComposeResult,\n type DocumentHandle,\n type DocumentSpecV2,\n type EditOp,\n type EditOptions,\n type HwpEngine,\n type HwpErrorCode,\n type PageImage,\n type PageImageFormat,\n type RenderOptions,\n type ValidationError,\n type ValidationReport,\n} from \"@hwp-editor/core\";\n\nexport const HWP_TIMEOUT_MS = 60_000;\nconst HWP_MAX_BUFFER = 32 * 1024 * 1024;\n/**\n * Grace between SIGTERM and SIGKILL. A convention, not a measurement:\n * hwp-cli does no cleanup on signal, so any value is safe, and the engine's\n * 60s budget makes the exact figure uncritical.\n */\nconst KILL_GRACE_MS = 3_000;\nconst MIN_VERSION: readonly [number, number, number] = [0, 16, 0];\n\n/**\n * Upper bound on the accepted binary, EXCLUSIVE.\n *\n * The floor is hard because a binary below it lacks flags this engine emits.\n * The ceiling is deliberately only a major-version gate, and stays one now\n * that the floor equals the tested tag. A major bump is the one signal\n * upstream gives that the contract may have broken; everything below it is\n * carried by the flag handshake, which checks what the binary actually\n * accepts rather than what it calls itself. A tighter numeric ceiling would\n * catch nothing the handshake misses, and would refuse a patch release on a\n * version string alone.\n */\nconst MAX_VERSION_EXCLUSIVE: readonly [number, number, number] = [1, 0, 0];\n\n/**\n * A long flag as `--help` prints it, matched on both boundaries.\n *\n * The trailing lookahead is the whole point: a naive `help.includes(flag)`\n * passes for any flag that is a prefix of another, and the real `hwp edit\n * --help` contains both `--set-cell` and `--set-cell-by-label`. A binary that\n * dropped the first while keeping the second would sail through a substring\n * test and then fail at edit time.\n */\nconst FLAG_TOKEN = /(?:^|\\s)(--[a-z][a-z0-9-]*)(?=[\\s,=<]|$)/gm;\n\n/**\n * The flag surface the resolved binary must accept before this engine will\n * use it. Derived from the grammar's own table, so adding an op kind widens\n * the check automatically and no second list can drift.\n *\n * Scope is `edit` only. `--verify` and `--allow-partial` are the two other\n * flags this engine puts on an `edit` argv and come out of the same 5.5 KB\n * help output for free. The flags hardcoded on the other eight subcommands\n * (cat, render, compose, validate, info, fields, bookmarks, slots) are a\n * known, accepted gap: `edit` is the whole 28-op surface and the highest-risk\n * one, and covering the rest would cost four or five more `--help` spawns on\n * every cold serverless start.\n */\nconst HANDSHAKE_FLAGS: readonly string[] = [\n ...Object.values(OP_FLAGS),\n \"--verify\",\n \"--allow-partial\",\n];\n\n/** Hancom binary .hwp is a CFBF (OLE2) container; .hwpx is a zip. */\nconst CFBF_SIGNATURE = [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1];\n\n/** The eight-byte PNG file signature (PNG spec 5.2). */\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\n/**\n * The engine half of the published `HwpErrorCode` vocabulary. Derived with\n * `Extract<>` rather than aliased to the full union on purpose: an alias\n * would make `statusFor`'s switch (routes.ts) non-exhaustive by five at\n * once, and the natural fix for that is a `default:` clause, which\n * permanently destroys the exhaustiveness check that must catch the next\n * code addition.\n *\n * `cancelled` and `output_too_large` were added in Phase 4. Both are engine\n * reasons rather than route-layer codes because both are decided inside\n * `runCli`, from a cause this module owns: only the code that started the\n * child knows whether it ended because the caller went away or because it\n * outran the stdout ceiling. A route-layer code would have to re-infer that\n * from an error shape, which is exactly the guessing this rewrite removed.\n */\nexport type HwpCliErrorReason = Extract<\n HwpErrorCode,\n | \"unavailable\"\n | \"version\"\n | \"timeout\"\n | \"failed\"\n | \"bad_request\"\n | \"unsupported_format\"\n | \"protected\"\n | \"cancelled\"\n | \"output_too_large\"\n>;\n\n/**\n * Two channels, and which one you use decides who sees it.\n *\n * `message` is serialized into the `ErrorResponse` body and crosses the wire\n * to an untrusted client, so it carries the operation and the outcome and\n * nothing else — never the resolved binary path, never a staged temp path,\n * never raw CLI stdout or stderr. `stderr` and `detail` are NOT serialized by\n * `routes.ts`; they are where that context is retained.\n *\n * The scrub is a property of construction rather than a filter applied on the\n * way out: a filter has to be remembered at every new throw site, and the one\n * that is forgotten is the one that leaks.\n *\n * There is no logger in this package, per the no-`console` convention that\n * holds across every package source tree. The host catches the error and\n * decides what to do with `stderr` and `detail` — log them, surface them to\n * an operator, discard them. This module does not make that choice for it.\n */\nexport class HwpCliError extends Error {\n constructor(\n public readonly reason: HwpCliErrorReason,\n message: string,\n /** Raw child stderr, verbatim. Read by `protectedReasonFromStderr`. */\n public readonly stderr?: string,\n /** Operator-facing context: the resolved binary path, CLI output. */\n public readonly detail?: string,\n ) {\n super(message);\n this.name = \"HwpCliError\";\n }\n}\n\n/** `bin` alone, or `bin: output` — the standard shape of a `detail`. */\nfunction detailFor(bin: string, output?: string): string {\n const trimmed = output?.trim() ?? \"\";\n return trimmed === \"\" ? bin : `${bin}: ${trimmed}`;\n}\n\nexport interface CliEngineOptions {\n /**\n * Explicit path to the hwp binary. Resolution order: this option ->\n * HWP_EDITOR_BIN env -> HWP_CLI env -> `hwp` on PATH.\n */\n bin?: string;\n /**\n * Per-invocation timeout in ms, default HWP_TIMEOUT_MS. Hosts with a hard\n * request budget (e.g. a 60s serverless function) should set this a few\n * seconds below it so the engine's 504 beats the platform's kill.\n */\n timeoutMs?: number;\n /**\n * Language passed to the child as HWP_LANG, default `en`. Accepts\n * `en`/`eng`/`english`/`c`/`posix` and `ko`/`kor`/`korean`. This sets\n * HWP_LANG only: LANG, LC_ALL and LC_MESSAGES stay pinned to `C.UTF-8`\n * regardless, so a host cannot accidentally change the child's encoding\n * while changing its language.\n */\n locale?: string;\n}\n\n/** Everything `read` gathers beyond the pinned CatEnvelope wire shape. */\nexport interface DocumentInspection {\n envelope: CatEnvelope;\n /** Raw `hwp fields --json` payload (array), null on failure. */\n fields: unknown;\n /** Raw `hwp bookmarks --json` payload (array), null on failure. */\n bookmarks: unknown;\n /** Raw `hwp slots --json` payload (object), null on failure. */\n slots: unknown;\n /** Raw `hwp info --json` payload, null on failure. */\n info: unknown;\n /** Per-document editability derived from `info` (see below). */\n capabilities: { editable: boolean; reason?: string };\n}\n\n/**\n * Per-call options this transport accepts beyond the `HwpEngine` contract.\n *\n * Carried as an extra OPTIONAL trailing parameter on every spawning method,\n * which keeps each method assignable to its `HwpEngine` counterpart: the\n * shared interface in `packages/core` is not widened, so the three\n * transports stay interchangeable.\n */\nexport interface CliCallOptions {\n /**\n * Aborting this terminates the child (SIGTERM, then SIGKILL after the\n * grace) and rejects with reason `cancelled`. Route handlers pass\n * `req.signal` so a client that disconnects does not leave an orphan.\n */\n signal?: AbortSignal;\n /**\n * Tenant scope from the server's `authorize` hook, salted into every cache\n * key this engine owns (SEC-04, D-07). One engine instance serves every\n * request a handler sees, so `inspections` and `snapshots` are otherwise a\n * cross-tenant channel: two callers uploading identical bytes would share\n * both entries.\n *\n * The engine RECEIVES a scope and never derives one — the single\n * `authorize` call in routes.ts is the only place it is decided. Omitting\n * it uses `DEFAULT_CALL_SCOPE`, so a direct `CliEngine` consumer with no\n * tenancy of its own keeps working unchanged.\n */\n scope?: string;\n}\n\nexport interface CliEngine extends HwpEngine {\n /**\n * Full read pipeline: cat --with-segments plus fields/bookmarks/slots/info.\n * `read()` is this with the extras dropped, per the pinned wire contract.\n */\n describe(document: DocumentHandle, call?: CliCallOptions): Promise<DocumentInspection>;\n read(document: DocumentHandle, call?: CliCallOptions): Promise<CatEnvelope>;\n render(\n document: DocumentHandle,\n options?: RenderOptions,\n call?: CliCallOptions,\n ): Promise<PageImage[]>;\n edit(\n document: DocumentHandle,\n ops: EditOp[],\n options?: EditOptions,\n call?: CliCallOptions,\n ): Promise<DocumentHandle>;\n compose(\n spec: DocumentSpecV2,\n name: string,\n call?: CliCallOptions,\n ): Promise<ComposeResult>;\n validate(document: DocumentHandle, call?: CliCallOptions): Promise<ValidationReport>;\n /**\n * Return the pre-edit snapshot of a document this engine edited, or null.\n * Keyed by the edited document's content hash SALTED WITH THE CALL SCOPE;\n * consumed on use. Takes the same trailing options as the spawning methods\n * (it spawns nothing, but its read must salt exactly as `edit`'s write did,\n * or a scoped write is findable by nobody).\n */\n undo(document: DocumentHandle, call?: CliCallOptions): DocumentHandle | null;\n /** Resolved binary path and verified version. */\n binaryInfo(): Promise<{ bin: string; version: string }>;\n}\n\n/**\n * The only `HWP_*` variables copied from the operator's environment.\n *\n * An explicit list of one rather than the `HWP_` prefix it replaces: hwp-cli\n * 0.14.0 reads roughly two dozen `HWP_*` variables, the prefix is upstream's\n * namespace, and upstream adds to it freely — so a prefix match silently\n * admits whatever the next release invents. `HWP_CERTIFY_ORACLE_RUNTIME` is\n * the illustration: hwp-cli reads it as a path to an executable\n * (crates/hwp-cli/src/certification.rs), reachable only through `hwp\n * certify`, which this engine never invokes.\n *\n * Of the whole set exactly two matter here, and one of them (`HWP_LANG`) is\n * pinned unconditionally below, so the pass-through gave it nothing. A host\n * that needs another variable should get a new option for it rather than a\n * wider window onto the ambient environment.\n */\nconst HWP_ENV_ALLOWLIST = [\"HWP_FONT_DIR\"] as const;\n\nexport function scrubbedEnv(locale?: string): Record<string, string> {\n // An inherited env is the usual way a subprocess reaches credentials it has\n // no business with. The CLI needs PATH (for helpers) and little else;\n // HWP_* is the CLI's own configuration surface (HWP_LANG, HWP_FONT_DIR...).\n const env: Record<string, string> = {};\n for (const key of [\"PATH\", \"HOME\"]) {\n const value = process.env[key];\n if (value !== undefined) env[key] = value;\n }\n for (const key of HWP_ENV_ALLOWLIST) {\n const value = process.env[key];\n if (value !== undefined) env[key] = value;\n }\n // The four locale variables are pinned AFTER the allow-list, which is the\n // only thing that could copy an inherited HWP_LANG in. hwp-cli's precedence\n // chain is --lang -> HWP_LANG -> LC_ALL -> LC_MESSAGES -> LANG\n // (i18n.rs:36-68); LC_MESSAGES is pinned alongside the other two so no link\n // is left open for whoever adds an entry to HWP_ENV_ALLOWLIST later. C.UTF-8 over\n // en_US.UTF-8 because it exists in slim container images, where an\n // ungenerated en_US.UTF-8 silently degrades to C and breaks UTF-8 handling.\n // hwp-cli's Lang::parse splits on `.`, `_`, `-`, `@` and lowercases the\n // head, so `c` and `posix` also resolve to English; `en` is just canonical.\n env.LANG = \"C.UTF-8\";\n env.LC_ALL = \"C.UTF-8\";\n env.LC_MESSAGES = \"C.UTF-8\";\n env.HWP_LANG = locale?.trim() || \"en\";\n return env;\n}\n\ninterface RunResult {\n stdout: string;\n stderr: string;\n code: number;\n}\n\n/**\n * Every terminal cause below is a value this module chose. Nothing is\n * inferred from `error.signal`: measured, a built-in timeout and a foreign\n * SIGTERM are byte-identical there, while a maxBuffer overflow and an abort\n * set no signal at all.\n */\nfunction runCli(\n bin: string,\n args: string[],\n timeoutMs: number = HWP_TIMEOUT_MS,\n // Required (not optional) on purpose: tsc then enumerates every call site\n // rather than letting one silently keep the default locale.\n locale: string | undefined,\n // Required for the same reason: a once-per-process path (ensureVersion)\n // must pass `undefined` explicitly, because one cancelled request must\n // never poison binary verification for every later request.\n requestSignal: AbortSignal | undefined,\n): Promise<RunResult> {\n return new Promise((resolve, reject) => {\n // Already gone before the child exists: an aborted signal never\n // re-dispatches, so a listener added now would never fire and the child\n // would run to completion for a caller that stopped listening.\n if (requestSignal?.aborted === true) {\n reject(new HwpCliError(\"cancelled\", `hwp ${args[0] ?? \"\"} was cancelled by the caller`));\n return;\n }\n let cause: \"timeout\" | \"cancelled\" | null = null;\n let escalation: ReturnType<typeof setTimeout> | undefined;\n\n /**\n * SIGTERM once, then SIGKILL after the grace, because execFile escalates\n * never and `child.killed` means only that a signal was delivered.\n *\n * The kill is done here rather than by handing execFile a `signal`\n * option: measured, Node's abort path destroys the child's stdio and\n * fires the callback the instant it delivers SIGTERM, which would settle\n * this promise — and so run withWorkDir's `finally` — while a\n * signal-ignoring child still held the staged input. Killing the child\n * directly leaves the callback on the real exit, which is what SEC-09's\n * ordering clause needs. `escalation` doubles as the already-signalled\n * guard, so a cancellation after a timeout does not re-send.\n */\n const signalChild = () => {\n if (escalation !== undefined) return;\n child.kill(\"SIGTERM\");\n escalation = setTimeout(() => child.kill(\"SIGKILL\"), KILL_GRACE_MS);\n escalation.unref();\n };\n\n const timer = setTimeout(() => {\n cause = \"timeout\";\n signalChild();\n }, timeoutMs);\n // `??=`: a cancellation arriving after the timeout fired must not\n // relabel it, so two racing causes settle deterministically as whichever\n // was recorded first.\n const onCancel = () => {\n cause ??= \"cancelled\";\n signalChild();\n };\n requestSignal?.addEventListener(\"abort\", onCancel, { once: true });\n\n // The only settle site. It fires after the child has exited, which is\n // what keeps withWorkDir's removal ordered after exit (SEC-09): never\n // race this against a timer.\n const child = execFile(\n bin,\n args,\n {\n maxBuffer: HWP_MAX_BUFFER,\n env: scrubbedEnv(locale),\n encoding: \"utf8\",\n },\n (error, stdout, stderr) => {\n clearTimeout(timer);\n if (escalation !== undefined) clearTimeout(escalation);\n requestSignal?.removeEventListener(\"abort\", onCancel);\n // The recorded cause outranks the exit status, and is therefore read\n // FIRST. `cause` is set only by the timer or the abort listener, and\n // each of those also signalled the child - so a zero exit after one\n // of them means the child chose to exit zero on SIGTERM (a wrapper\n // that traps and cleans up), not that the run succeeded. Reading\n // `error === null` first reported a blown deadline, or a request the\n // caller had abandoned, as a normal 200 with a complete body.\n if (cause === \"timeout\") {\n reject(new HwpCliError(\"timeout\", `hwp ${args[0] ?? \"\"} timed out after ${timeoutMs}ms`));\n return;\n }\n if (cause === \"cancelled\") {\n reject(new HwpCliError(\"cancelled\", `hwp ${args[0] ?? \"\"} was cancelled by the caller`));\n return;\n }\n if (error === null) {\n resolve({ stdout, stderr, code: 0 });\n return;\n }\n const raw = (error as { code?: unknown }).code;\n if (raw === \"ERR_CHILD_PROCESS_STDIO_MAXBUFFER\") {\n reject(new HwpCliError(\n \"output_too_large\",\n `hwp ${args[0] ?? \"\"} produced more than ${HWP_MAX_BUFFER} bytes on stdout`,\n ));\n return;\n }\n if (raw === \"ENOENT\") {\n // `binary not found` is pinned: packages/react/src/errors.ts\n // substring-matches it for the pre-1.0 fallback classifier. The\n // scrub moves the path off the message, it does not reword this.\n reject(new HwpCliError(\n \"unavailable\",\n \"hwp binary not found (install hwp-cli >= 0.16.0, or set HWP_EDITOR_BIN / the bin option)\",\n undefined,\n detailFor(bin),\n ));\n return;\n }\n // Non-zero exit still carries stdout/stderr; let callers that expect\n // failure output (validate) inspect it instead of always throwing.\n resolve({ stdout, stderr, code: typeof raw === \"number\" ? raw : 1 });\n },\n );\n });\n}\n\n/** Run a command that must succeed; throw a rich error otherwise. */\nasync function runCliOk(\n bin: string,\n args: string[],\n timeoutMs: number | undefined,\n locale: string | undefined,\n requestSignal: AbortSignal | undefined,\n): Promise<RunResult> {\n const result = await runCli(bin, args, timeoutMs, locale, requestSignal);\n if (result.code !== 0) {\n // The subcommand and the exit code, and nothing else: the CLI's own text\n // routinely names the staged input file, so interpolating it here would\n // hand a temp path to whoever made the request.\n throw new HwpCliError(\n \"failed\",\n `hwp ${args[0] ?? \"\"} failed (exit ${result.code})`,\n result.stderr,\n detailFor(bin, result.stderr.trim() || result.stdout.trim()),\n );\n }\n return result;\n}\n\nfunction parseVersion(stdout: string): [number, number, number] | null {\n const match = stdout.match(/(\\d+)\\.(\\d+)\\.(\\d+)/);\n if (match === null) return null;\n return [Number(match[1]), Number(match[2]), Number(match[3])];\n}\n\nfunction versionAtLeast(v: [number, number, number], min: readonly [number, number, number]): boolean {\n for (let i = 0; i < 3; i++) {\n if (v[i]! > min[i]!) return true;\n if (v[i]! < min[i]!) return false;\n }\n return true;\n}\n\nfunction sha256(data: Uint8Array): string {\n return createHash(\"sha256\").update(data).digest(\"hex\");\n}\n\n/** Scope used when a caller supplies none; see `CliCallOptions.scope`. */\nconst DEFAULT_CALL_SCOPE = \"default\";\n\n/**\n * The key for every entry in this engine's two caches: the content hash\n * salted with the tenant scope (SEC-04, D-07).\n *\n * Written once so `inspections` and `snapshots` cannot disagree about the\n * separator, and so `edit`'s snapshot write and `undo`'s read are the same\n * expression. The `\\0` separator is what makes the pair unambiguous: without\n * it a scope ending in hex digits could produce the key some other scope\n * produces for different content.\n */\nfunction cacheKey(scope: string | undefined, data: Uint8Array): string {\n return createHash(\"sha256\")\n .update(`${scope ?? DEFAULT_CALL_SCOPE}\\0${sha256(data)}`)\n .digest(\"hex\");\n}\n\nfunction sniffExtension(document: DocumentHandle): \".hwp\" | \".hwpx\" {\n const ext = path.extname(document.name).toLowerCase();\n if (ext === \".hwp\" || ext === \".hwpx\") return ext;\n const isCfbf =\n document.data.length >= CFBF_SIGNATURE.length &&\n CFBF_SIGNATURE.every((byte, i) => document.data[i] === byte);\n return isCfbf ? \".hwp\" : \".hwpx\";\n}\n\n/** Basename + validated extension, so a hostile name can never escape tmp. */\nfunction safeOutputName(name: string, fallbackExt: \".hwp\" | \".hwpx\"): string {\n const base = path.basename(name).replace(/[^\\w.가-힣-]/g, \"_\") || `document${fallbackExt}`;\n const ext = path.extname(base).toLowerCase();\n if (ext === \".hwp\" || ext === \".hwpx\") return base;\n return `${base}${fallbackExt}`;\n}\n\nasync function tryJson(\n bin: string,\n args: string[],\n timeoutMs: number | undefined,\n locale: string | undefined,\n requestSignal: AbortSignal | undefined,\n): Promise<unknown> {\n try {\n const result = await runCliOk(bin, args, timeoutMs, locale, requestSignal);\n return JSON.parse(result.stdout) as unknown;\n } catch (error) {\n // Best-effort covers this probe failing, not the whole request ending. A\n // cancellation belongs to the request: swallowing it let describe()\n // resolve - and then CACHE - an all-null inspection for a caller that was\n // already gone, and answer 200 where the contract says 499. The cache\n // never re-probes a hit, so that entry degraded every later request for\n // the same document under the same scope. Rethrowing here is what keeps\n // describe()'s cache write unreachable on a cancelled call.\n if (error instanceof HwpCliError && error.reason === \"cancelled\") throw error;\n return null;\n }\n}\n\n/**\n * hwp5 protection labels exactly as `hwp info --json` emits them in\n * `attributes[]` (hwp-cli/crates/hwp5/src/file_header.rs:220-236).\n * `check_body_readable()` refuses five conditions (Encrypted, CertEncrypted,\n * CertDrm, Drm, Signed) but `info --json` exposes only two of them as\n * booleans, so without this table four protection kinds pass the pre-flight\n * silently. Best-effort only: the 0.8.7 changelog, which introduced these\n * refusals, states the certificate/DRM/signature branches are unverified\n * against a genuine protected file, and no release through 0.16.1 has since\n * verified them - 0.11.0 added password-protected read and left those three\n * on \"their existing typed refusals\". So the marker table behind\n * `protectedReasonFromStderr` (the stderr backstop, shared from core) is what\n * actually carries the requirement.\n */\nconst PROTECTED_ATTRIBUTES: Readonly<Record<string, string>> = {\n \"DRM 보안\": \"DRM-protected document (DRM 보안)\",\n \"공인 인증서 암호화\": \"certificate-encrypted document (공인 인증서 암호화)\",\n \"공인 인증서 DRM 보안\": \"certificate DRM-protected document (공인 인증서 DRM 보안)\",\n \"전자 서명 정보\": \"signed document (전자 서명 정보)\",\n};\n\n/**\n * Distribution/encrypted documents: hwp-cli reads them but refuses edit/fill.\n *\n * The origin is the 0.8.7 changelog, not a later one. 0.8.7 added reading of\n * Hancom distribution documents to `cat`/`convert`/`render` and stated that\n * the source-preserving edit path (`hwp edit`, `hwp fill`) still refuses\n * them, because their content lives in ViewText streams rather than BodyText,\n * so there is no source structure to rewrite against. Every section through\n * 0.16.1 was checked and none reverses it; 0.11.0 corrects its own READMEs\n * for having described these documents as refused \"although they have been\n * read since v0.8.7\", which is about the read path, not the edit path.\n *\n * The evidence is upstream's, not ours. Besides the changelog, hwp-cli's\n * own `crates/hwp-cli/src/commands/cat.rs` at tag v0.16.0 records that\n * \"the source-preserving edit path fails closed on a /ViewText-only\n * document instead of writing anything\", attributed to a 2026-08-20\n * measurement against a genuine distribution document. This repository\n * has no distribution-document fixture and hwp-cli cannot synthesize one,\n * so the refusal has not been observed here. The reason strings below name no\n * version on purpose: they reach the host as `capabilities.reason`, where a\n * version number is a maintenance liability with no reader benefit.\n */\nexport function documentEditability(info: unknown): { editable: boolean; reason?: string } {\n if (typeof info !== \"object\" || info === null) return { editable: true };\n const record = info as Record<string, unknown>;\n if (record[\"encrypted\"] === true) {\n return { editable: false, reason: \"encrypted document; hwp-cli refuses edit/fill\" };\n }\n if (record[\"distribution\"] === true) {\n return {\n editable: false,\n reason: \"distribution (배포용) document; hwp-cli refuses edit/fill\",\n };\n }\n const attributes = record[\"attributes\"];\n if (Array.isArray(attributes)) {\n for (const attribute of attributes) {\n const label = typeof attribute === \"string\" ? PROTECTED_ATTRIBUTES[attribute] : undefined;\n if (label !== undefined) {\n return { editable: false, reason: `${label}; hwp-cli refuses edit/fill` };\n }\n }\n }\n return { editable: true };\n}\n\n/**\n * The stderr backstop for this transport, kept under its stderr-specific\n * name because that is what this transport actually reads. The marker table\n * itself lives in `@hwp-editor/core`: hwp-cli's Korean diagnostics are its\n * vocabulary, not the CLI server's, and the Tauri bridge needs the same\n * table for the same reason (a second copy would drift the moment hwp-cli\n * rewords a message).\n *\n * The stderr argument is never interpolated into the result: raw CLI output\n * stays on the non-serialized `HwpCliError.stderr` field, so this path adds\n * no new CLI-output-to-client leak (Phase 4 SEC-06 owns the pre-existing one\n * in `runCliOk`).\n */\nexport function protectedReasonFromStderr(stderr: string): string | null {\n return protectedReasonFromDiagnostics(stderr);\n}\n\n/**\n * Rethrow a generic CLI failure as `protected` when its stderr carries a\n * protection marker. Applied at the `edit` and `compose` call sites only,\n * never inside `runCliOk`: that is what keeps `read` and `render` succeeding\n * on protected documents hwp-cli can still read (D-11).\n */\nfunction rethrowProtected(error: unknown): never {\n if (error instanceof HwpCliError && error.reason === \"failed\") {\n const message = protectedReasonFromStderr(error.stderr ?? \"\");\n if (message !== null) throw new HwpCliError(\"protected\", message, error.stderr);\n }\n throw error;\n}\n\n/**\n * A page dimension is only trustworthy when it is finite and strictly\n * positive. `PageCanvas`'s `aspectRatio` collapses on a zero, and\n * `page.width_pt` upstream is an `f32`, so a degenerate value means the\n * producer emitted corruption rather than that this parser mis-read it.\n */\nfunction positiveSize(width: number, height: number): { width: number; height: number } | null {\n if (!Number.isFinite(width) || !Number.isFinite(height)) return null;\n if (width <= 0 || height <= 0) return null;\n return { width, height };\n}\n\n/**\n * Dimensions from a PNG IHDR. Exported for `render-size.test.ts`; deliberately\n * NOT re-exported from `src/index.ts` — this is engine-internal parsing.\n *\n * The signature AND the `IHDR` chunk type are both validated before any\n * integer is read: a length check alone lets any payload of 24 bytes or more\n * yield two arbitrary uint32s presented to the client as real dimensions,\n * which fails invisibly and so is worse than the 0x0 BUG-04 names.\n */\nexport function pngSize(data: Uint8Array): { width: number; height: number } | null {\n if (data.length < 24) return null;\n if (PNG_SIGNATURE.some((byte, i) => data[i] !== byte)) return null;\n // ASCII \"IHDR\" must be the first chunk type, at offsets 12-15.\n if (data[12] !== 0x49 || data[13] !== 0x48 || data[14] !== 0x44 || data[15] !== 0x52) return null;\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n return positiveSize(view.getUint32(16), view.getUint32(20));\n}\n\nconst SVG_UNITS = \"pt|px|mm|in|cm|em|%\";\n\n/**\n * Dimensions from an SVG root tag. Exported for `render-size.test.ts` only.\n *\n * The numeric pattern stays `[\\d.]+` on purpose: `NaN`, `inf` and a leading\n * minus are exactly the degenerate `f32` spellings this must reject, not\n * gaps to widen away.\n */\nexport function svgSize(source: string): { width: number; height: number } | null {\n const tag = source.match(/<svg\\b[^>]*>/i);\n if (tag === null) return null;\n const width = tag[0].match(new RegExp(`\\\\bwidth=\"([\\\\d.]+)(${SVG_UNITS})?\"`, \"i\"));\n const height = tag[0].match(new RegExp(`\\\\bheight=\"([\\\\d.]+)(${SVG_UNITS})?\"`, \"i\"));\n if (width !== null && height !== null) {\n // The numbers feed PageCanvas's aspectRatio, meaningful only when both\n // dimensions share a unit. A mixed-unit tag (210mm x 841.86pt) must\n // fail loudly here rather than silently render a wrong ratio; no unit\n // conversion — hwp-cli emits pt only.\n if ((width[2] ?? \"\") !== (height[2] ?? \"\")) return null;\n return positiveSize(Number(width[1]), Number(height[1]));\n }\n // An attribute that is present but unparseable (`NaN`, `inf`, `-5.00`) is a\n // degenerate producer value, not an absent attribute: reject rather than\n // silently substituting the viewBox, which would resurrect the guess D-16\n // exists to remove.\n if (/\\b(?:width|height)=\"/i.test(tag[0])) return null;\n // Defensive padding against a future upstream change: hwp-cli's only SVG\n // root emission always writes both attributes (hwp-render/src/svg.rs:17-26).\n const viewBox = tag[0].match(/\\bviewBox=\"[\\d.-]+\\s+[\\d.-]+\\s+([\\d.]+)\\s+([\\d.]+)\"/i);\n if (viewBox === null) return null;\n return positiveSize(Number(viewBox[1]), Number(viewBox[2]));\n}\n\nfunction parseSinglePage(pages: string | undefined): number | null {\n if (pages !== undefined && /^\\d+$/.test(pages)) return Number(pages);\n return null;\n}\n\nexport function createCliEngine(opts: CliEngineOptions = {}): CliEngine {\n const timeoutMs = opts.timeoutMs ?? HWP_TIMEOUT_MS;\n\n function resolveBin(): string {\n const fromOpts = opts.bin?.trim();\n if (fromOpts) return fromOpts;\n const fromEditorEnv = process.env.HWP_EDITOR_BIN?.trim();\n if (fromEditorEnv) return fromEditorEnv;\n const fromCliEnv = process.env.HWP_CLI?.trim();\n if (fromCliEnv) return fromCliEnv;\n return \"hwp\";\n }\n\n // One version verification per resolved binary per process.\n let verifiedVersion: Promise<string> | null = null;\n function ensureVersion(): Promise<string> {\n verifiedVersion ??= (async () => {\n const bin = resolveBin();\n let result: RunResult;\n try {\n // No request signal, deliberately: this memo is shared by every\n // later request in the process, so one cancellation must not poison\n // binary verification for all of them.\n result = await runCli(bin, [\"--version\"], timeoutMs, opts.locale, undefined);\n } catch (error) {\n if (error instanceof HwpCliError) throw error;\n // `not executable` is pinned: packages/react/src/errors.ts\n // substring-matches it for the pre-1.0 fallback classifier. The path\n // and the underlying error text move to `detail`; the phrase stays.\n throw new HwpCliError(\n \"unavailable\",\n \"hwp binary is not executable (set HWP_EDITOR_BIN or the bin option)\",\n undefined,\n detailFor(bin, error instanceof Error ? error.message : String(error)),\n );\n }\n if (result.code !== 0) {\n throw new HwpCliError(\n \"unavailable\",\n `hwp --version failed (exit ${result.code})`,\n result.stderr,\n detailFor(bin, result.stderr),\n );\n }\n const version = parseVersion(result.stdout);\n if (version === null) {\n throw new HwpCliError(\n \"version\",\n \"cannot parse a semver from the hwp --version output\",\n undefined,\n detailFor(bin, result.stdout),\n );\n }\n // The parsed numbers are this engine's own reading, not CLI output, so\n // stating them is what makes a version message actionable.\n if (!versionAtLeast(version, MIN_VERSION)) {\n throw new HwpCliError(\n \"version\",\n `hwp ${version.join(\".\")} is too old; >= ${MIN_VERSION.join(\".\")} required`,\n undefined,\n detailFor(bin),\n );\n }\n if (versionAtLeast(version, MAX_VERSION_EXCLUSIVE)) {\n throw new HwpCliError(\n \"version\",\n `hwp ${version.join(\".\")} is newer than this engine supports; ` +\n `< ${MAX_VERSION_EXCLUSIVE.join(\".\")} required`,\n undefined,\n detailFor(bin),\n );\n }\n // Flag handshake, inside this same memo rather than a second one: it\n // runs once per process for the same reason the version check does, and\n // with the same explicitly `undefined` request signal, so one cancelled\n // request can never poison binary verification for every later one.\n const help = await runCli(bin, [\"edit\", \"--help\"], timeoutMs, opts.locale, undefined);\n if (help.code !== 0) {\n throw new HwpCliError(\n \"version\",\n `hwp edit --help failed (exit ${help.code}); the edit flag surface cannot be verified`,\n help.stderr,\n detailFor(bin, help.stderr),\n );\n }\n const present = new Set([...help.stdout.matchAll(FLAG_TOKEN)].map((match) => match[1]!));\n const missing = HANDSHAKE_FLAGS.filter((flag) => !present.has(flag));\n if (missing.length > 0) {\n throw new HwpCliError(\n \"version\",\n `hwp ${version.join(\".\")} does not accept ${missing.join(\", \")} on edit; ` +\n \"the binary does not match this engine's edit grammar\",\n undefined,\n detailFor(bin),\n );\n }\n return version.join(\".\");\n })();\n return verifiedVersion;\n }\n\n /**\n * Per-call private workspace; removed on every path including failure.\n *\n * The removal is ordered strictly after child exit, and stays so only\n * because `runCli` settles from the execFile callback alone (SEC-09). A\n * racing timer that settled the promise early would run this `finally`\n * while the child still held the directory.\n */\n async function withWorkDir<T>(fn: (dir: string) => Promise<T>): Promise<T> {\n const dir = await mkdtemp(path.join(tmpdir(), \"hwp-editor-\"));\n try {\n return await fn(dir);\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n }\n\n async function stage(dir: string, document: DocumentHandle): Promise<string> {\n const file = path.join(dir, `in${sniffExtension(document)}`);\n await writeFile(file, document.data, { mode: 0o600 });\n return file;\n }\n\n const inspections = new Map<string, DocumentInspection>();\n const snapshots = new Map<string, DocumentHandle>();\n\n async function describe(\n document: DocumentHandle,\n call?: CliCallOptions,\n ): Promise<DocumentInspection> {\n await ensureVersion();\n const bin = resolveBin();\n const key = cacheKey(call?.scope, document.data);\n const cached = inspections.get(key);\n if (cached !== undefined) return cached;\n const signal = call?.signal;\n const inspection = await withWorkDir(async (dir) => {\n const file = await stage(dir, document);\n const cat = await runCliOk(bin, [\"cat\", file, \"--format\", \"markdown\", \"--with-segments\"], timeoutMs, opts.locale, signal);\n const envelope = parseCatEnvelope(cat.stdout);\n // Best-effort extras: a document that cats fine but fails fields should\n // still read; the extras inform editing UI, not the wire contract.\n const [fields, bookmarks, slots, info] = await Promise.all([\n tryJson(bin, [\"fields\", file, \"--json\"], timeoutMs, opts.locale, signal),\n tryJson(bin, [\"bookmarks\", file, \"--json\"], timeoutMs, opts.locale, signal),\n tryJson(bin, [\"slots\", file, \"--json\"], timeoutMs, opts.locale, signal),\n tryJson(bin, [\"info\", file, \"--json\"], timeoutMs, opts.locale, signal),\n ]);\n return {\n envelope,\n fields,\n bookmarks,\n slots,\n info,\n capabilities: documentEditability(info),\n } satisfies DocumentInspection;\n });\n if (inspections.size >= 64) {\n const oldest = inspections.keys().next().value;\n if (oldest !== undefined) inspections.delete(oldest);\n }\n inspections.set(key, inspection);\n return inspection;\n }\n\n const engine: CliEngine = {\n async read(document, call) {\n return (await describe(document, call)).envelope;\n },\n\n describe,\n\n async render(document, options = {}, call) {\n await ensureVersion();\n const bin = resolveBin();\n const requested = options.format ?? \"svg\";\n if (requested === \"jpeg\" || requested === \"webp\") {\n throw new HwpCliError(\n \"unsupported_format\",\n `hwp-cli render supports png and svg only; got \"${requested}\"`,\n );\n }\n const dpi = options.dpi ?? 96;\n if (!Number.isFinite(dpi) || dpi < 36 || dpi > 600) {\n throw new HwpCliError(\"bad_request\", `dpi must be within 36..=600; got ${options.dpi}`);\n }\n const pages = options.pages ?? \"all\";\n if (pages !== \"all\" && !/^\\d+(-\\d+)?$/.test(pages)) {\n throw new HwpCliError(\"bad_request\", `invalid page range: ${pages}`);\n }\n return withWorkDir(async (dir) => {\n const input = await stage(dir, document);\n const attempt = async (format: \"svg\" | \"png\"): Promise<PageImage[]> => {\n const outBase = path.join(dir, `page.${format}`);\n const reportPath = path.join(dir, \"render-report.json\");\n await runCliOk(bin, [\n \"render\", input, \"-o\", outBase,\n \"--format\", format, \"--pages\", pages, \"--dpi\", String(dpi),\n \"--report\", reportPath,\n ], timeoutMs, opts.locale, call?.signal);\n // Multi-page renders land as page-<n>.<ext>; a single selected page\n // keeps the exact -o name. The report's selected_pages pins numbers.\n const filePattern = new RegExp(`^page-(\\\\d+)\\\\.${format}$`);\n const files = (await readdir(dir))\n .filter((f) => f === `page.${format}` || filePattern.test(f))\n .sort((a, b) => {\n const na = Number(filePattern.exec(a)?.[1] ?? 0);\n const nb = Number(filePattern.exec(b)?.[1] ?? 0);\n return na - nb;\n });\n let selected: number[] | null = null;\n try {\n const report = JSON.parse(await readFile(reportPath, \"utf8\")) as {\n selected_pages?: unknown;\n };\n if (Array.isArray(report.selected_pages)) {\n selected = report.selected_pages.filter((n): n is number => typeof n === \"number\");\n }\n } catch {\n selected = null;\n }\n const images: PageImage[] = [];\n for (let i = 0; i < files.length; i++) {\n const file = files[i]!;\n const data = new Uint8Array(await readFile(path.join(dir, file)));\n const suffix = file.match(/^page-(\\d+)\\./);\n const page =\n suffix !== null\n ? Number(suffix[1])\n : (selected?.[i] ?? parseSinglePage(options.pages) ?? i + 1);\n const size =\n format === \"png\"\n ? pngSize(data)\n : svgSize(Buffer.from(data).toString(\"utf8\"));\n // Output whose dimensions cannot be read cannot be trusted, so it\n // is discarded rather than handed to PageCanvas, whose aspectRatio\n // collapses on the zero the old fallback substituted (D-16).\n //\n // This composes with the SVG->PNG retry immediately below: that\n // retry fires on `requested === \"svg\" && reason === \"failed\"`, so\n // an SVG dimension failure is retried as PNG before it surfaces\n // while a PNG one surfaces directly. That is D-16 meeting the\n // retry D-15 deliberately kept, not a bug to \"fix\".\n if (size === null) {\n // The page number identifies it for the client; the staged file\n // name would only tell them where this server keeps its temps.\n throw new HwpCliError(\n \"failed\",\n `unreadable ${format} page dimensions on page ${page}`,\n undefined,\n detailFor(bin, file),\n );\n }\n images.push({\n page,\n width: size.width,\n height: size.height,\n dpi,\n format,\n data,\n });\n }\n return images;\n };\n try {\n return await attempt(requested);\n } catch (error) {\n // SVG is the default; fall back to PNG when the renderer refuses.\n if (requested === \"svg\" && error instanceof HwpCliError && error.reason === \"failed\") {\n return attempt(\"png\");\n }\n throw error;\n }\n });\n },\n\n async edit(document, ops: EditOp[], options: EditOptions = {}, call?: CliCallOptions) {\n await ensureVersion();\n const bin = resolveBin();\n if (!Array.isArray(ops)) {\n throw new HwpCliError(\"bad_request\", \"ops must be an array of edit operations\");\n }\n const ext = sniffExtension(document);\n const edited = await withWorkDir(async (dir) => {\n const input = await stage(dir, document);\n // Pre-flight: refuse a protected document before spawning `hwp edit`.\n // Reuse the cached inspection when the normal read-then-edit path has\n // already warmed it (routes.ts describes the same pre-edit bytes); on\n // a miss spawn `info` alone, never describe() — that costs five\n // parallel spawns and `info` is the only one this check reads.\n // compose() has no input DocumentHandle to inspect (its signature is\n // (spec, name)), so the pre-flight has no subject there; the stderr\n // backstop below covers it.\n const cached = inspections.get(cacheKey(call?.scope, document.data))?.capabilities;\n const capabilities =\n cached ?? documentEditability(await tryJson(bin, [\"info\", input, \"--json\"], timeoutMs, opts.locale, call?.signal));\n if (!capabilities.editable) {\n throw new HwpCliError(\n \"protected\",\n capabilities.reason ?? \"protected document; hwp-cli refuses edit/compose\",\n );\n }\n const output = path.join(dir, `out${ext}`);\n const args = [\"edit\", input, \"-o\", output, ...opsToArgv(ops)];\n if (options.verify !== false) args.push(\"--verify\");\n if (options.allowPartial === true) args.push(\"--allow-partial\");\n await runCliOk(bin, args, timeoutMs, opts.locale, call?.signal).catch(rethrowProtected);\n return new Uint8Array(await readFile(output));\n });\n // Pre-edit snapshot keyed by the edited hash SALTED WITH THIS CALL'S\n // SCOPE: undo(edited, same scope) -> original, and no other scope.\n snapshots.set(cacheKey(call?.scope, edited), { name: document.name, data: document.data });\n if (snapshots.size > 256) {\n const oldest = snapshots.keys().next().value;\n if (oldest !== undefined) snapshots.delete(oldest);\n }\n return { name: document.name, data: edited };\n },\n\n undo(document, call) {\n // Salts identically to the `edit` write above; any divergence here makes\n // every scoped snapshot unreachable rather than merely mis-scoped.\n const key = cacheKey(call?.scope, document.data);\n const snapshot = snapshots.get(key) ?? null;\n if (snapshot !== null) snapshots.delete(key);\n return snapshot;\n },\n\n async compose(spec: DocumentSpecV2, name: string, call?: CliCallOptions) {\n await ensureVersion();\n const bin = resolveBin();\n const outName = safeOutputName(name, \".hwpx\");\n return withWorkDir(async (dir) => {\n const specPath = path.join(dir, \"spec.json\");\n await writeFile(specPath, JSON.stringify(spec), { mode: 0o600 });\n const outPath = path.join(dir, outName);\n const result = await runCliOk(\n bin,\n [\"compose\", specPath, \"-o\", outPath, \"--report\"],\n timeoutMs,\n opts.locale,\n call?.signal,\n ).catch(rethrowProtected);\n let report: unknown;\n try {\n report = JSON.parse(result.stdout) as unknown;\n } catch {\n report = undefined;\n }\n const data = new Uint8Array(await readFile(outPath));\n const composeResult: ComposeResult = { document: { name: outName, data } };\n if (report !== undefined) composeResult.report = report;\n return composeResult;\n });\n },\n\n async validate(document, call) {\n await ensureVersion();\n const bin = resolveBin();\n return withWorkDir(async (dir) => {\n const file = await stage(dir, document);\n // Exit 1 means \"invalid\" and still prints the JSON report.\n const result = await runCli(bin, [\"validate\", file, \"--json\"], timeoutMs, opts.locale, call?.signal);\n let parsed: { valid?: unknown; errors?: unknown };\n try {\n parsed = JSON.parse(result.stdout) as { valid?: unknown; errors?: unknown };\n } catch {\n // \"no JSON report\" is what distinguishes this from a plain non-zero\n // validate exit, which is a legitimate \"invalid document\" result.\n throw new HwpCliError(\n \"failed\",\n `hwp validate failed (exit ${result.code}): no JSON report`,\n result.stderr,\n detailFor(bin, result.stderr.trim() || result.stdout.trim()),\n );\n }\n const rawErrors = Array.isArray(parsed.errors) ? parsed.errors : [];\n const errors: ValidationError[] = rawErrors.map((entry) =>\n typeof entry === \"string\"\n ? { code: \"invalid\", message: entry }\n : { code: \"invalid\", message: JSON.stringify(entry) },\n );\n const report: ValidationReport = { valid: parsed.valid === true, errors };\n return report;\n });\n },\n\n async capabilities(): Promise<Capabilities> {\n const version = await ensureVersion();\n return { version, editable: true, formats: [\"hwp\", \"hwpx\"] };\n },\n\n async binaryInfo() {\n return { bin: resolveBin(), version: await ensureVersion() };\n },\n };\n\n return engine;\n}\n","/**\n * Server-internal cache of read-pipeline inspections.\n *\n * A per-process, in-memory map from an opaque session id to the extras a\n * `describe()` produced (fields, bookmarks, slots, info, editability), with an\n * idle TTL swept on growth. It touches the filesystem on no code path and\n * retains no document bytes: the wire contract (protocol.ts) is stateless, so\n * every request already carries the document it operates on, and the cache\n * exists only because opening one document otherwise spawns about seven CLI\n * processes.\n *\n * What this store used to be, and why it is not: it kept the current bytes on\n * disk, a pre-edit snapshot history (up to twenty full document copies per\n * session) and an export handle. Nothing in this repository could read any of\n * it back — no route ever called `undo()` or `exportBytes()`, and protocol.ts\n * has no session or undo surface — so the history was disk amplification with\n * no reader (BUG-07, D-05/D-06). Undo lives in the client store,\n * `packages/core/src/state.ts`, bounded at 50 snapshots, and that is the one\n * undo model.\n *\n * Session ids are UUIDs, never client-supplied paths, and `lookup` still\n * checks the shape before the map: with no filesystem left there is no path to\n * confine, but an id from a client is still an id from a client.\n */\n\nimport { randomUUID } from \"node:crypto\";\n\nimport type { DocumentInspection } from \"./cli-engine.js\";\n\nexport const DEFAULT_TTL_MS = 30 * 60 * 1000;\nconst SESSION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\n\nexport class SessionNotFoundError extends Error {\n constructor(id: string) {\n super(`unknown or expired session: ${id}`);\n this.name = \"SessionNotFoundError\";\n }\n}\n\nexport interface DocumentSession {\n id: string;\n /** Uploaded document file name (basename only); labelling, never a path. */\n name: string;\n createdAt: number;\n touchedAt: number;\n /** Cached read-pipeline extras, when the engine provided them. */\n inspection?: DocumentInspection;\n}\n\nexport interface SessionStoreOptions {\n /** Idle time after which sweep() removes a session. Default 30min. */\n ttlMs?: number;\n}\n\nexport interface SessionStore {\n /** Register a session for an uploaded document. Bytes are not retained. */\n create(name: string): DocumentSession;\n get(id: string): DocumentSession;\n has(id: string): boolean;\n attachInspection(id: string, inspection: DocumentInspection): void;\n /** Remove expired sessions; returns how many were removed. */\n sweep(now?: number): number;\n /** Drop every session. */\n dispose(): void;\n size(): number;\n /** All live session ids. */\n ids(): string[];\n}\n\nexport function createSessionStore(opts: SessionStoreOptions = {}): SessionStore {\n const ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;\n const sessions = new Map<string, DocumentSession>();\n\n function lookup(id: string): DocumentSession {\n if (!SESSION_ID_PATTERN.test(id)) throw new SessionNotFoundError(id);\n const session = sessions.get(id);\n if (session === undefined) throw new SessionNotFoundError(id);\n session.touchedAt = Date.now();\n return session;\n }\n\n function sweepExpired(now = Date.now()): number {\n let removed = 0;\n for (const [id, session] of [...sessions]) {\n if (session.touchedAt + ttlMs < now) {\n sessions.delete(id);\n removed++;\n }\n }\n return removed;\n }\n\n return {\n create(name) {\n // Best-effort expiry on growth; no background timer (serverless-safe).\n sweepExpired();\n const id = randomUUID();\n const now = Date.now();\n const session: DocumentSession = {\n id,\n // Basename only: the name rides out in nothing but this record, but a\n // client-supplied string with a path in it should not be kept as one.\n name: name.split(/[/\\\\]/).pop() || \"document.hwpx\",\n createdAt: now,\n touchedAt: now,\n };\n sessions.set(id, session);\n return session;\n },\n\n get(id) {\n return lookup(id);\n },\n\n has(id) {\n return sessions.has(id);\n },\n\n attachInspection(id, inspection) {\n lookup(id).inspection = inspection;\n },\n\n sweep(now = Date.now()) {\n return sweepExpired(now);\n },\n\n dispose() {\n sessions.clear();\n },\n\n size() {\n return sessions.size;\n },\n\n ids() {\n return [...sessions.keys()];\n },\n };\n}\n","/**\n * Framework-agnostic HTTP handler implementing the wire contract of\n * packages/core/src/protocol.ts as Web Standards `(Request) => Response`.\n * Framework adapters (next.ts, or any Fetch-API runtime) delegate here.\n *\n * Binary payloads cross the wire as base64 inside JSON responses; uploads\n * are multipart/form-data parsed with Request.formData(). Every failure is\n * an ErrorResponse with a non-2xx status.\n *\n * The admission gate runs in a fixed order before any body is buffered:\n * action (404) -> method (405) -> authorize (403) -> size (400/413). Only\n * after all four does a handler touch `req.formData()` or `req.json()`, so a\n * refusal costs zero uploaded bytes and zero engine calls (D-04).\n *\n * Two further checks run after the bytes are in hand, in this order: the\n * magic-byte sniff at the single buffering site (`sniffFormat`, refusing\n * anything that is not an HWP or HWPX document, SEC-07), and the op-path\n * filter in the edit path (refusing `insert-image` and `seal`, which name a\n * file on the server's own disk, SEC-05). Both still precede the engine call.\n *\n * Archive limits — declared entry sizes, decompressed byte ceilings and\n * compression-ratio caps — are NOT reimplemented here. They are hwp-cli's\n * default-on `hwp-cli-native-v1` profile (D-12); this handler relies on it,\n * so bumping the binary means re-checking that the profile still applies.\n */\n\nimport { createHash } from \"node:crypto\";\n\nimport type {\n DocumentHandle,\n EditOp,\n EditOptions,\n HwpEngine,\n HwpErrorCode,\n PageImageFormat,\n RenderOptions,\n} from \"@hwp-editor/core\";\nimport type {\n ComposeRequest,\n ComposeResponse,\n EditResponse,\n ErrorResponse,\n RenderPageWire,\n RenderResponse,\n} from \"@hwp-editor/core\";\n\nimport { createCliEngine, HwpCliError, type CliEngine } from \"./cli-engine.js\";\nimport { createSessionStore, SessionNotFoundError, type SessionStore } from \"./session.js\";\n\n/** The six actions this handler serves; the runtime guard and `HwpAction` share it. */\nconst ACTION_LIST = [\"read\", \"render\", \"edit\", \"compose\", \"validate\", \"capabilities\"] as const;\n\n/** One of the six action names the handler dispatches on. */\nexport type HwpAction = (typeof ACTION_LIST)[number];\n\n/**\n * Host-supplied admission hook. Its return value answers two questions at\n * once, deliberately (D-01): a string ADMITS the request AND is the tenant\n * scope every server-side cache key is salted with; `null` REFUSES it with\n * HTTP 403 and code `forbidden`. One call decides both, so admission and\n * tenancy can never disagree.\n *\n * Called once per request, awaited, before any body is buffered — a refusal\n * therefore costs zero bytes of upload and zero engine calls (D-04).\n *\n * The refusal message is a fixed literal: a `{ allow, scope, reason }` shape\n * was rejected precisely so no host-authored reason string can ride out to\n * an unauthenticated client.\n */\nexport type AuthorizeFn = (req: Request, action: HwpAction) => Promise<string | null>;\n\n/**\n * Scope used when no `authorize` is supplied. D-02: no hook means allow, with\n * one fixed scope — the documented `createHwpEditorRoutes({ bin })` one-liner\n * keeps working and single-tenant hosts need no configuration.\n */\nconst DEFAULT_SCOPE = \"default\";\n\n/**\n * Default combined request cap, 50 MiB. It bounds multipart buffering, the\n * base64 response it produces and the bytes staged on disk. It is NOT a\n * memory bound: hwp-cli's own per-package ceiling is 2 GiB, and a measured\n * 9.0 MB HWPX drove `hwp cat` to 1.70 GB RSS. Size the container off that\n * amplification, not off this number.\n */\nconst DEFAULT_MAX_REQUEST_BYTES = 50 * 1024 * 1024;\n\nexport interface RoutesOptions {\n /** Engine to serve; defaults to a CliEngine resolved from env/PATH. */\n engine?: HwpEngine;\n /** Convenience for the default engine: explicit hwp binary path. */\n bin?: string;\n /** Convenience for the default engine: per-invocation timeout in ms. */\n timeoutMs?: number;\n /**\n * Convenience for the default engine: language passed to the child as\n * HWP_LANG, default `en`. Accepts `en`/`eng`/`english`/`c`/`posix` and\n * `ko`/`kor`/`korean`. Applies to the default engine only — an explicit\n * `engine` carries its own locale.\n */\n locale?: string;\n /**\n * Largest request admitted, in bytes; defaults to 52428800 (50 MiB).\n * The figure is the WHOLE request envelope — multipart boundaries, field\n * names and part headers included — not the document alone, because it is\n * compared against `Content-Length`. Checked before any buffering; a\n * request over it is refused with 413 and a request with no measurable\n * `Content-Length` with 400.\n */\n maxRequestBytes?: number;\n /**\n * Cache of read-pipeline inspections, keyed by an opaque session id. Pass\n * false to disable; defaults to a per-handler in-memory store. It retains no\n * document bytes and touches no filesystem — the wire is stateless and undo\n * lives in the client store (D-05/D-06).\n */\n sessions?: SessionStore | false;\n /**\n * Admission hook run before any body is read. Defaults to allow-all with a\n * fixed scope: this package owns no auth, the host owns the trust boundary\n * (see the trust-boundary section of packages/server/README.md).\n */\n authorize?: AuthorizeFn;\n}\n\nexport type HwpEditorHandler = (req: Request) => Promise<Response>;\n\nconst ACTIONS: ReadonlySet<string> = new Set<string>(ACTION_LIST);\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json; charset=utf-8\" },\n });\n}\n\nfunction error(status: number, code: HwpErrorCode, message: string): Response {\n const body: ErrorResponse = { error: { code, message } };\n return json(body, status);\n}\n\nfunction statusFor(err: HwpCliError): number {\n switch (err.reason) {\n case \"bad_request\":\n case \"unsupported_format\":\n return 400;\n case \"unavailable\":\n return 503;\n case \"timeout\":\n return 504;\n case \"version\":\n return 500;\n // The client went away; nothing was produced and nobody is listening.\n case \"cancelled\":\n return 499;\n // The document's CLI output exceeded the 32 MiB stdout ceiling.\n case \"output_too_large\":\n return 413;\n case \"failed\":\n // 403 is deliberately left unclaimed for Phase 4's `authorize`\n // rejections, so a host can tell an auth refusal from a document\n // refusal by status alone.\n case \"protected\":\n return 422;\n }\n}\n\nfunction toBase64(data: Uint8Array): string {\n return Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString(\"base64\");\n}\n\nfunction sha256(data: Uint8Array): string {\n return createHash(\"sha256\").update(data).digest(\"hex\");\n}\n\nfunction sha256Text(text: string): string {\n return createHash(\"sha256\").update(text).digest(\"hex\");\n}\n\n/**\n * CFBF/OLE2 container signature — the first eight bytes of every HWP5 file.\n *\n * Deliberately a second copy of `cli-engine.ts`'s constant rather than a\n * shared export: the two answer different questions and must be free to\n * diverge (see the note on `sniffFormat`).\n */\nconst CFBF_SIGNATURE = [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1];\n\n/**\n * The OPC media type an HWPX package declares in its first zip entry.\n *\n * hwp-cli's own writer emits that entry first and STORED\n * (hwp-cli/crates/hwpx/src/write/mod.rs:331-332), and `hwp validate` warns\n * when the layout is violated (commands/validate.rs:100-103). The layout was\n * checked against 19 real HWPX files — 14 hwp-cli-produced and 5\n * Hancom-authored — and 19 of 19 passed.\n */\nconst HWPX_MIMETYPE = \"application/hwp+zip\";\n\n/**\n * Decide whether these bytes are admissible as an HWP or HWPX document, from\n * the leading bytes alone. `null` means refuse.\n *\n * Reads at most about ninety bytes and decompresses nothing, so it does not\n * itself widen the archive surface. Every archive-structure defence — entry\n * count, per-entry and total decompressed size, compression ratio, XML size,\n * duplicate and traversing entry names — is enforced by hwp-cli's default-on\n * `hwp-cli-native-v1` profile, twice (declared central-directory sizes before\n * the archive is opened, and actual decompressed bytes), and was verified\n * effective. No second limit is written here (D-12).\n *\n * A `PK\\x03\\x04` signature alone is NOT accepted: it admits any zip (D-11).\n * The cost of the strict layout is a possible false rejection from an exotic\n * producer, whose failure mode is a clear 400 rather than a silent one.\n */\nfunction sniffFormat(d: Uint8Array): \".hwp\" | \".hwpx\" | null {\n if (d.length >= CFBF_SIGNATURE.length && CFBF_SIGNATURE.every((b, i) => d[i] === b)) {\n return \".hwp\";\n }\n // 30-byte local file header + an 8-byte `mimetype` name is the floor below\n // which none of the reads below are in range.\n if (d.length < 38) return null;\n if (!(d[0] === 0x50 && d[1] === 0x4b && d[2] === 0x03 && d[3] === 0x04)) return null;\n const view = new DataView(d.buffer, d.byteOffset, d.byteLength);\n if (view.getUint16(8, true) !== 0) return null; // compression method must be STORED\n const nameLen = view.getUint16(26, true);\n const extraLen = view.getUint16(28, true);\n if (nameLen !== 8) return null;\n if (new TextDecoder().decode(d.subarray(30, 38)) !== \"mimetype\") return null;\n const start = 30 + nameLen + extraLen;\n const end = start + HWPX_MIMETYPE.length;\n if (d.length < end) return null;\n return new TextDecoder().decode(d.subarray(start, end)) === HWPX_MIMETYPE ? \".hwpx\" : null;\n}\n\n/** Extract the uploaded document from a multipart form. */\nasync function formDocument(req: Request): Promise<{ form: FormData; document: DocumentHandle }> {\n let form: FormData;\n try {\n form = await req.formData();\n } catch {\n throw new HwpCliError(\"bad_request\", \"expected multipart/form-data with a file field\");\n }\n const file = form.get(\"file\");\n if (file === null || typeof file === \"string\") {\n throw new HwpCliError(\"bad_request\", 'multipart field \"file\" is required');\n }\n const blob = file as Blob;\n const name =\n \"name\" in blob && typeof (blob as { name?: unknown }).name === \"string\" && (blob as File).name !== \"\"\n ? (blob as File).name\n : \"document.hwpx\";\n const data = new Uint8Array(await blob.arrayBuffer());\n if (data.length === 0) {\n throw new HwpCliError(\"bad_request\", 'multipart field \"file\" is empty');\n }\n // The single buffering site D-04 pins, so this one call covers read,\n // render, edit and validate alike. The sniffed extension is deliberately\n // discarded: `sniffExtension` in cli-engine.ts keeps answering its own\n // question (\"what extension do I stage this under?\"), and once the route\n // has rejected non-HWP input that guess is only ever choosing between two\n // valid answers. Do not merge the two.\n if (sniffFormat(data) === null) {\n throw new HwpCliError(\"bad_request\", \"file is not an HWP or HWPX document\");\n }\n return { form, document: { name, data } };\n}\n\nfunction formString(form: FormData, key: string): string | undefined {\n const value = form.get(key);\n return typeof value === \"string\" && value !== \"\" ? value : undefined;\n}\n\nfunction formFlag(form: FormData, key: string): boolean | undefined {\n const value = form.get(key);\n if (value === null) return undefined;\n return value === \"true\" || value === \"1\";\n}\n\nexport function createHwpEditorHandler(opts: RoutesOptions = {}): HwpEditorHandler {\n const engine: HwpEngine = opts.engine ?? createCliEngine({\n ...(opts.bin === undefined ? {} : { bin: opts.bin }),\n ...(opts.timeoutMs === undefined ? {} : { timeoutMs: opts.timeoutMs }),\n ...(opts.locale === undefined ? {} : { locale: opts.locale }),\n });\n // Resolved once, here, and nowhere else: every handler that spawns needs\n // the same narrowing to pass per-call options, and a second `\"describe\" in\n // engine` check inside one handler is how the other four end up without\n // one. A host-supplied plain HwpEngine keeps working; it simply receives no\n // per-call options, so its children are not cancellable from here.\n const cli: CliEngine | null = \"describe\" in engine ? (engine as CliEngine) : null;\n const maxRequestBytes = opts.maxRequestBytes ?? DEFAULT_MAX_REQUEST_BYTES;\n const sessions: SessionStore | null =\n opts.sessions === false ? null : (opts.sessions ?? createSessionStore());\n\n /**\n * Get-or-create the session tracking this exact document content, within\n * this scope. The scope is a PARAMETER, never a closure field: one handler\n * serves concurrent requests, so a field would be a cross-request channel\n * of exactly the shape SEC-04 forbids.\n */\n function sessionFor(document: DocumentHandle, scope: string): string | null {\n if (sessions === null) return null;\n // Same salted shape as cli-engine.ts's `cacheKey`, written separately\n // because the two files share no module; the SHAPE is the contract, not\n // the function (SEC-04, D-07).\n const key = sha256Text(`${scope}\\0${sha256(document.data)}`);\n const existing = hashToSession.get(key);\n if (existing !== undefined && sessions.has(existing)) return existing;\n const session = sessions.create(document.name);\n hashToSession.set(key, session.id);\n return session.id;\n }\n\n const hashToSession = new Map<string, string>();\n\n async function handleRead(req: Request, scope: string): Promise<Response> {\n const { document } = await formDocument(req);\n // describe() runs cat + fields + bookmarks + slots + info; the extras are\n // cached on the session because the wire shape is pinned to CatEnvelope.\n if (sessions !== null && cli !== null) {\n const inspection = await cli.describe(document, { signal: req.signal, scope });\n const id = sessionFor(document, scope);\n if (id !== null) sessions.attachInspection(id, inspection);\n return json(inspection.envelope);\n }\n return json(\n cli !== null\n ? await cli.read(document, { signal: req.signal, scope })\n : await engine.read(document),\n );\n }\n\n async function handleRender(req: Request, scope: string): Promise<Response> {\n const { form, document } = await formDocument(req);\n const dpiField = formString(form, \"dpi\");\n const dpi = dpiField === undefined ? undefined : Number(dpiField);\n if (dpi !== undefined && !Number.isFinite(dpi)) {\n throw new HwpCliError(\"bad_request\", `dpi must be a number; got \"${dpiField}\"`);\n }\n const format = formString(form, \"format\") as PageImageFormat | undefined;\n const options: RenderOptions = {};\n const pagesField = formString(form, \"pages\");\n if (pagesField !== undefined) options.pages = pagesField;\n if (dpi !== undefined) options.dpi = dpi;\n if (format !== undefined) options.format = format;\n const pages =\n cli !== null\n ? await cli.render(document, options, { signal: req.signal, scope })\n : await engine.render(document, options);\n const body: RenderResponse = {\n pages: pages.map(\n (p): RenderPageWire => ({\n page: p.page,\n width: p.width,\n height: p.height,\n dpi: p.dpi,\n format: p.format,\n dataBase64: toBase64(p.data),\n }),\n ),\n };\n return json(body);\n }\n\n async function handleEdit(req: Request, scope: string): Promise<Response> {\n const { form, document } = await formDocument(req);\n const opsField = form.get(\"ops\");\n if (typeof opsField !== \"string\" || opsField === \"\") {\n throw new HwpCliError(\"bad_request\", 'multipart field \"ops\" (EditOp[] JSON) is required');\n }\n let ops: EditOp[];\n try {\n const parsed: unknown = JSON.parse(opsField);\n if (!Array.isArray(parsed)) throw new Error(\"not an array\");\n ops = parsed as EditOp[];\n } catch {\n throw new HwpCliError(\"bad_request\", 'multipart field \"ops\" is not a JSON array');\n }\n // `opValue` in packages/core/src/ops.ts hands `op.path` straight to argv\n // for exactly these two kinds and no others. `execFile` runs without a\n // shell, which stops command injection but NOT path resolution, so over\n // HTTP a client could otherwise name any file the server process can read\n // and have it embedded in the output — which is why refusing, rather than\n // sanitizing, is the scope-correct fix. Both ops keep working on the\n // Tauri transport, because that is a local application (D-10). Phase 7\n // EXT-01 owns the staged-asset upload flow that makes them usable here.\n // `path_traversal` rather than `bad_request`: D-08 keeps that code in the\n // union specifically as this reuse site, and both answer 400.\n if (ops.some((op) => op?.kind === \"insert-image\" || op?.kind === \"seal\")) {\n return error(\n 400,\n \"path_traversal\",\n 'ops \"insert-image\" and \"seal\" name a server-local path and are not accepted over HTTP; upload the asset with the request instead',\n );\n }\n const options: EditOptions = {};\n const verify = formFlag(form, \"verify\");\n if (verify !== undefined) options.verify = verify;\n const allowPartial = formFlag(form, \"allowPartial\");\n if (allowPartial !== undefined) options.allowPartial = allowPartial;\n\n // No session is created or written here. The pre-edit copy this path used\n // to snapshot was unreadable by anything in the repository (BUG-07, D-05);\n // undo is the client store's job (packages/core/src/state.ts).\n //\n // The scope matters most on this call and is the easiest to leave off:\n // the engine's `inspections` protected pre-flight AND its `snapshots`\n // write both key off this argument, so an unthreaded edit falls back to\n // the default scope and two tenants editing identical bytes share both.\n const edited =\n cli !== null\n ? await cli.edit(document, ops, options, { signal: req.signal, scope })\n : await engine.edit(document, ops, options);\n const body: EditResponse = { name: edited.name, dataBase64: toBase64(edited.data) };\n return json(body);\n }\n\n async function handleCompose(req: Request, scope: string): Promise<Response> {\n let body: ComposeRequest;\n try {\n body = (await req.json()) as ComposeRequest;\n } catch {\n throw new HwpCliError(\"bad_request\", \"expected a JSON ComposeRequest body\");\n }\n if (typeof body !== \"object\" || body === null || typeof body.spec !== \"object\" || body.spec === null) {\n throw new HwpCliError(\"bad_request\", 'ComposeRequest requires a \"spec\" object');\n }\n if (typeof body.name !== \"string\" || body.name === \"\") {\n throw new HwpCliError(\"bad_request\", 'ComposeRequest requires a non-empty \"name\"');\n }\n const result =\n cli !== null\n ? await cli.compose(body.spec, body.name, { signal: req.signal, scope })\n : await engine.compose(body.spec, body.name);\n const responseBody: ComposeResponse = {\n name: result.document.name,\n dataBase64: toBase64(result.document.data),\n };\n if (result.report !== undefined) responseBody.report = result.report;\n return json(responseBody);\n }\n\n async function handleValidate(req: Request, scope: string): Promise<Response> {\n const { document } = await formDocument(req);\n return json(\n cli !== null\n ? await cli.validate(document, { signal: req.signal, scope })\n : await engine.validate(document),\n );\n }\n\n async function handleCapabilities(): Promise<Response> {\n return json(await engine.capabilities());\n }\n\n return async function handler(req: Request): Promise<Response> {\n const url = new URL(req.url);\n const segments = url.pathname.split(\"/\").filter(Boolean);\n const action = segments[segments.length - 1] ?? \"\";\n try {\n if (!ACTIONS.has(action)) {\n return error(404, \"not_found\", `unknown action: ${action || \"(empty)\"}`);\n }\n if (action === \"capabilities\") {\n if (req.method !== \"GET\") return error(405, \"method_not_allowed\", \"capabilities requires GET\");\n } else if (req.method !== \"POST\") {\n return error(405, \"method_not_allowed\", `${action} requires POST`);\n }\n // Admission, above every buffering site and above the capabilities\n // early return — /capabilities discloses the binary version (SEC-12),\n // so it is gated too. The scope is bound here and nowhere else: later\n // plans salt cache keys with this local, never re-derive it.\n const scope =\n opts.authorize === undefined ? DEFAULT_SCOPE : await opts.authorize(req, action as HwpAction);\n if (scope === null) return error(403, \"forbidden\", \"forbidden\");\n if (action === \"capabilities\") {\n return await handleCapabilities();\n }\n // Size gate: POST actions only, so /capabilities never needs a body\n // header. Refusing an absent Content-Length rather than falling through\n // to a post-buffer check is what keeps D-04's promise provable — a\n // counting guard over req.body would have to read the body first.\n // Every request the reference client sends carries the header (measured:\n // FormData and JSON bodies both), so this costs no legitimate caller;\n // only a deliberately chunked upload is refused (Pitfall 3).\n const declared = req.headers.get(\"content-length\");\n if (declared === null) {\n return error(400, \"bad_request\", \"content-length is required\");\n }\n // /^\\d+$/ before Number(): `Number(\" 10\")` is 10 and `Number(\"\")` is 0,\n // so isSafeInteger alone admits both (Pitfall 4).\n const bytes = /^\\d+$/.test(declared) ? Number(declared) : NaN;\n if (!Number.isSafeInteger(bytes)) {\n return error(400, \"bad_request\", \"invalid content-length\");\n }\n if (bytes > maxRequestBytes) {\n return error(413, \"bad_request\", `request exceeds the ${maxRequestBytes} byte limit`);\n }\n switch (action) {\n case \"read\":\n return await handleRead(req, scope);\n case \"render\":\n return await handleRender(req, scope);\n case \"edit\":\n return await handleEdit(req, scope);\n case \"compose\":\n return await handleCompose(req, scope);\n case \"validate\":\n return await handleValidate(req, scope);\n default:\n return error(404, \"not_found\", `unknown action: ${action}`);\n }\n } catch (err) {\n if (err instanceof HwpCliError) {\n return error(statusFor(err), err.reason, err.message);\n }\n if (err instanceof SessionNotFoundError) {\n return error(404, \"session_not_found\", err.message);\n }\n // Unclassified: this is the single serialization boundary where an\n // internal value could become a client-visible string, and an\n // unclassified throw can carry a temp path, the binary path or CLI\n // stderr. The message is therefore a fixed literal BY CONSTRUCTION —\n // nothing derived from `err` is interpolated, so there is no filter to\n // get wrong and no encoding question. The branches above keep their own\n // messages, which 04-03 already scrubbed at the engine. A host that\n // needs the detail catches the error itself; this package has no logger\n // and adds none (SEC-06, route half).\n return error(500, \"internal\", \"internal error\");\n }\n };\n}\n"],"mappings":"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","names":["error","createHash","sha256","createHash","CFBF_SIGNATURE"]}