@embedpdf/engine-core 3.0.0-next.12 → 3.0.0-next.14

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.
Files changed (64) hide show
  1. package/dist/{AbortError-8-9JAX1Y.js → AbortError-DwuHPK_I.js} +2 -2
  2. package/dist/{AbortError-8-9JAX1Y.js.map → AbortError-DwuHPK_I.js.map} +1 -1
  3. package/dist/{AbortError-CRSooKsk.cjs → AbortError-P0RU0Mgg.cjs} +4 -4
  4. package/dist/{AbortError-CRSooKsk.cjs.map → AbortError-P0RU0Mgg.cjs.map} +1 -1
  5. package/dist/{AnnotationListSnapshot-x6tKulNV.d.cts → AnnotationListSnapshot-D9R5n2Kf.d.cts} +1390 -176
  6. package/dist/{AnnotationListSnapshot-x6tKulNV.d.ts → AnnotationListSnapshot-D9R5n2Kf.d.ts} +1390 -176
  7. package/dist/{DocumentSecurityService-CK0pejAK.d.cts → DocumentSecurityService-CIx1MT0T.d.cts} +2 -2
  8. package/dist/{DocumentSecurityService-9MawYxP-.d.ts → DocumentSecurityService-Ca6EK3-3.d.ts} +2 -2
  9. package/dist/{Engine--0XF0UAK.d.cts → Engine-EWWHhEIi.d.ts} +179 -11
  10. package/dist/{Engine-Ds0ytbl6.d.ts → Engine-FNaAEjfb.d.cts} +179 -11
  11. package/dist/conformance.cjs +1441 -88
  12. package/dist/conformance.cjs.map +1 -1
  13. package/dist/conformance.d.cts +74 -3
  14. package/dist/conformance.d.ts +74 -3
  15. package/dist/conformance.js +1376 -25
  16. package/dist/conformance.js.map +1 -1
  17. package/dist/derive-BbsDk_Kz.cjs +880 -0
  18. package/dist/derive-BbsDk_Kz.cjs.map +1 -0
  19. package/dist/derive-DAFJolWu.js +647 -0
  20. package/dist/derive-DAFJolWu.js.map +1 -0
  21. package/dist/{PageInsertBlankInput-WKY0tr6S.js → format-Bbphf4zR.js} +95 -3
  22. package/dist/format-Bbphf4zR.js.map +1 -0
  23. package/dist/{PageInsertBlankInput-CzShuSJI.cjs → format-D_epWlob.cjs} +106 -2
  24. package/dist/format-D_epWlob.cjs.map +1 -0
  25. package/dist/index.cjs +137 -88
  26. package/dist/index.d.cts +2 -2
  27. package/dist/index.d.ts +2 -2
  28. package/dist/index.js +5 -5
  29. package/dist/{resolver-D-jumvgM.cjs → resolver-CYVNV4hn.cjs} +216 -10
  30. package/dist/resolver-CYVNV4hn.cjs.map +1 -0
  31. package/dist/{resolver-CHG4waXL.js → resolver-inAVzsWt.js} +157 -11
  32. package/dist/resolver-inAVzsWt.js.map +1 -0
  33. package/dist/runtime.cjs +147 -98
  34. package/dist/runtime.cjs.map +1 -1
  35. package/dist/runtime.d.cts +216 -5
  36. package/dist/runtime.d.ts +216 -5
  37. package/dist/runtime.js +6 -6
  38. package/dist/{schemas-DOyVEPk7.js → schemas-CFQGvWiu.js} +551 -5
  39. package/dist/schemas-CFQGvWiu.js.map +1 -0
  40. package/dist/{schemas-RChsAMWw.cjs → schemas-D0g6tKa9.cjs} +829 -25
  41. package/dist/schemas-D0g6tKa9.cjs.map +1 -0
  42. package/dist/{builders-8Hl1AYNU.cjs → viewport-BWeNHZmb.cjs} +1522 -117
  43. package/dist/viewport-BWeNHZmb.cjs.map +1 -0
  44. package/dist/{builders-DOjcKDWQ.js → viewport-CvMcadfQ.js} +1386 -89
  45. package/dist/viewport-CvMcadfQ.js.map +1 -0
  46. package/dist/wire.cjs +252 -13
  47. package/dist/wire.cjs.map +1 -1
  48. package/dist/wire.d.cts +3147 -5
  49. package/dist/wire.d.ts +3147 -5
  50. package/dist/wire.js +207 -14
  51. package/dist/wire.js.map +1 -1
  52. package/package.json +1 -1
  53. package/dist/PageInsertBlankInput-CzShuSJI.cjs.map +0 -1
  54. package/dist/PageInsertBlankInput-WKY0tr6S.js.map +0 -1
  55. package/dist/builders-8Hl1AYNU.cjs.map +0 -1
  56. package/dist/builders-DOjcKDWQ.js.map +0 -1
  57. package/dist/fold-BfyxxwS6.js +0 -334
  58. package/dist/fold-BfyxxwS6.js.map +0 -1
  59. package/dist/fold-C7I5GpBz.cjs +0 -447
  60. package/dist/fold-C7I5GpBz.cjs.map +0 -1
  61. package/dist/resolver-CHG4waXL.js.map +0 -1
  62. package/dist/resolver-D-jumvgM.cjs.map +0 -1
  63. package/dist/schemas-DOyVEPk7.js.map +0 -1
  64. package/dist/schemas-RChsAMWw.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver-inAVzsWt.js","names":[],"sources":["../src/signature/protection.ts","../src/auth/scope/errors.ts","../src/auth/scope/parser.ts","../src/auth/scope/resolver.ts"],"sourcesContent":["import type { DocCapability } from '../auth/scope/types';\nimport type {\n DocMdpPermission,\n DocumentFieldLock,\n DocumentProtection,\n FieldLockSpec,\n ModificationLevel,\n SignatureDTO,\n} from './types';\n\n/**\n * Bumped whenever the derivation below (or a judgement rule) changes meaning.\n * Rides every protection and every analysis, and keys the cloud's immutable\n * analysis URLs. v4 (the `signature-compat` corpus, 91 Acrobat observations):\n * each signature is judged on the net state of the document against the\n * revision it sealed; an object written again with the sealed value is not\n * a modification; an approval signature permits commenting; field\n * properties are judged per level; a lock binds only the signature that\n * declares it; a changed shared resource must be allowed by every use.\n */\nexport const SIGNATURE_POLICY_VERSION = 4;\n\n/**\n * How a validator reads an approval signature. ISO 32000 defines permitted\n * changes only for certification signatures; Acrobat reads an approval\n * signature as \"Form Fill-in, Signing and Commenting are allowed\" (its own\n * permission text under every such signature) and keeps it valid through a\n * Square annotation added, deleted or recoloured afterwards (corpus\n * `signature-compat` v3 cases 88, 90, 91: \"Annotations Created/Deleted/\n * Modified\", signature valid). pyHanko judges stricter (fill only); Acrobat\n * is the validator recipients use, so the baseline is `annotate`.\n */\nexport const APPROVAL_BASELINE: ModificationLevel = 'annotate';\n\nconst LEVEL_RANK: Record<ModificationLevel, number> = { none: 0, lta: 1, fill: 2, annotate: 3 };\n\n/** DocMDP `/P` → the modification level it permits. */\nexport function levelFromPermission(permission: DocMdpPermission): ModificationLevel {\n return permission === 1 ? 'lta' : permission === 2 ? 'fill' : 'annotate';\n}\n\nexport function minLevel(a: ModificationLevel, b: ModificationLevel): ModificationLevel {\n return LEVEL_RANK[a] <= LEVEL_RANK[b] ? a : b;\n}\n\n/** Whether `level` (null = unsigned, everything allowed) permits changes of kind `needed`. */\nexport function levelAllows(level: ModificationLevel | null, needed: ModificationLevel): boolean {\n return level === null || LEVEL_RANK[level] >= LEVEL_RANK[needed];\n}\n\n/** Hierarchical name match: `group` covers `group.total`. */\nexport function lockNameCovers(lockedName: string, fieldName: string): boolean {\n return fieldName === lockedName || fieldName.startsWith(`${lockedName}.`);\n}\n\nexport function lockCovers(spec: FieldLockSpec, fieldName: string): boolean {\n switch (spec.action) {\n case 'all':\n return true;\n case 'include':\n return spec.fields.some((name) => lockNameCovers(name, fieldName));\n case 'exclude':\n return !spec.fields.some((name) => lockNameCovers(name, fieldName));\n }\n}\n\n/**\n * Derive what the signatures already in a document mean for what comes\n * after. Pure and versioned (`SIGNATURE_POLICY_VERSION`); the same function\n * runs in the browser worker, on the native server, and in the conformance\n * suite.\n *\n * enforced = min(certification P, every signed field's /Lock /P), or null\n * when nothing was declared. What the engine refuses.\n * judged = enforced, or the approval baseline when signatures exist\n * without a declaration, or null when nothing is signed. What\n * the revision analysis judges later changes against. A\n * declaration governs the judgement too: a P=3 certifier really\n * did allow annotations.\n * fieldLocks = every signed signature's FieldMDP and the /Lock of every\n * signed field. Unsigned fields' /Lock entries describe a\n * FUTURE signature and lock nothing yet.\n */\nexport function deriveProtection(signatures: ReadonlyArray<SignatureDTO>): DocumentProtection {\n let signed = false;\n let enforced: ModificationLevel | null = null;\n let certification: DocumentProtection['certification'] = null;\n const fieldLocks: DocumentFieldLock[] = [];\n\n for (const sig of signatures) {\n if (!sig.signed) continue;\n signed = true;\n if (sig.catalogCertification && sig.docMdp !== null && certification === null) {\n certification = { signatureIndex: sig.index, permission: sig.docMdp };\n enforced = minLevel(enforced ?? 'annotate', levelFromPermission(sig.docMdp));\n }\n if (sig.fieldMdp) {\n fieldLocks.push({ signatureIndex: sig.index, source: 'fieldmdp', spec: sig.fieldMdp });\n }\n if (sig.lock) {\n fieldLocks.push({ signatureIndex: sig.index, source: 'lock', spec: sig.lock });\n if (sig.lock.permission !== undefined) {\n enforced = minLevel(enforced ?? 'annotate', levelFromPermission(sig.lock.permission));\n }\n }\n }\n\n const judged = signed ? (enforced ?? APPROVAL_BASELINE) : null;\n return { enforced, judged, certification, fieldLocks, policyVersion: SIGNATURE_POLICY_VERSION };\n}\n\n/** The lock that freezes `fieldName`, if any. */\nexport function fieldLockFor(\n protection: DocumentProtection,\n fieldName: string,\n): DocumentFieldLock | null {\n for (const lock of protection.fieldLocks) {\n if (lockCovers(lock.spec, fieldName)) return lock;\n }\n return null;\n}\n\n/**\n * The capabilities a protection removes from every caller, admin scope\n * included — document-derived authority, exactly like encryption bits.\n *\n * Only what was DECLARED is refused: a refusal is a promise a signer made,\n * never a guess about a validator. A certification (or a lock with /P)\n * declares what may follow, and everything outside that — page edits,\n * redaction, field authoring, attachments, and below P=3 annotations, below\n * P=2 form fill — is refused. A plain approval signature declares nothing:\n * the same edits stay possible and are then JUDGED, the verdict saying the\n * signature no longer holds, exactly as Acrobat does. The one exception is a\n * rewrite: it does not invalidate signatures, it erases them, so any signed\n * document refuses it. Per-field locks are enforced by the form mutator.\n */\nexport function protectedCapabilities(protection: DocumentProtection | null): Set<DocCapability> {\n const out = new Set<DocCapability>();\n if (!protection || protection.judged === null) return out;\n out.add('doc.download.flattened');\n if (protection.enforced === null) return out; // nothing declared: judged, not refused\n out.add('doc.pages.modify');\n out.add('doc.pages.assemble');\n out.add('doc.redact');\n out.add('doc.attachments.modify');\n out.add('doc.forms.modify');\n if (!levelAllows(protection.enforced, 'annotate')) out.add('doc.annotate.modify');\n if (!levelAllows(protection.enforced, 'fill')) out.add('doc.forms.fill');\n return out;\n}\n","/**\n * Thrown by {@link parseScope} / {@link validateScopeArray} when a scope\n * string does not match any known grammar (capability, collab, virtual,\n * or wildcard).\n *\n * Carries the offending scope verbatim so route/JWT-layer error\n * handlers can echo it to the customer.\n */\nexport class InvalidScope extends Error {\n constructor(\n public readonly scope: string,\n reason: string,\n ) {\n super(`invalid scope \"${scope}\": ${reason}`);\n this.name = 'InvalidScope';\n }\n}\n\n/**\n * Thrown by route guards and engine-local enforcement when a capability\n * or collab action is denied for the current request/handle.\n *\n * `required` names what the caller needed (e.g., \"doc.render\" or\n * \"annotations:update\"). `context` is an optional label such as \"local\"\n * or \"/v1/docs/.../pages/2/render\".\n */\nexport class PermissionDenied extends Error {\n constructor(\n public readonly required: string,\n public readonly context?: string,\n ) {\n super(`permission denied${context ? ` (${context})` : ''}: ${required}`);\n this.name = 'PermissionDenied';\n }\n}\n\n/**\n * Thrown at engine-local open time when the supplied scope includes\n * collab filters (`:self`, `:group=...`) but the identity claims needed\n * to resolve them are missing. Fails loudly at open so the configuration\n * mistake is visible immediately instead of producing silent denies at\n * every annotation mutation.\n */\nexport class MissingIdentity extends Error {\n constructor(scope: string) {\n super(`scope \"${scope}\" requires identity claims (user_id and/or groups)`);\n this.name = 'MissingIdentity';\n }\n}\n","import { InvalidScope } from './errors';\nimport type { CollabAction, CollabFilter, DocCapability, ParsedScope } from './types';\n\n/**\n * Closed set of recognized capability strings. Membership is the\n * authoritative source of truth — adding a new capability requires\n * extending the `DocCapability` union AND adding it here. The parser\n * rejects anything outside this set.\n *\n * Removed legacy names (e.g., `doc.read`, `doc.edit-pages`, `doc.save`)\n * are deliberately absent so any JWT carrying them fails at verify\n * time with a clear error.\n */\nconst KNOWN_CAPABILITIES: ReadonlySet<DocCapability> = new Set([\n 'doc.open',\n 'doc.render',\n 'doc.text.select',\n 'doc.text.copy',\n 'doc.text.search',\n 'doc.content.copy',\n 'doc.download',\n 'doc.download.flattened',\n 'doc.print',\n 'doc.print.high',\n 'doc.pages.modify',\n 'doc.pages.assemble',\n 'doc.forms.read',\n 'doc.forms.fill',\n 'doc.forms.modify',\n 'doc.forms.submit',\n 'doc.annotate.read',\n 'doc.annotate.modify',\n 'doc.metadata.modify',\n 'doc.attachments.modify',\n 'doc.redact',\n 'doc.sign',\n 'doc.sign.certify',\n]);\n\n/**\n * Parse a single scope string into a structured `ParsedScope`.\n *\n * Recognised shapes:\n * - `\"*\"` → wildcard\n * - `\"pdf.permissions\"` → virtual (expands at resolve time)\n * - `\"a.b.c...\"` (dotted) → capability\n * - `\"entity:action:filter\"` (colons) → collab\n *\n * Throws {@link InvalidScope} for any string that fails to match one of\n * those shapes, or whose capability/entity/action/filter is not in the\n * closed enum.\n */\nexport function parseScope(raw: string): ParsedScope {\n if (raw === '*') return { kind: 'wildcard' };\n if (raw === 'pdf.permissions') return { kind: 'virtual', name: 'pdf.permissions' };\n if (raw.includes(':')) return parseCollab(raw);\n return parseCapability(raw);\n}\n\n/**\n * Validate every entry in a scope array by attempting to parse it.\n * Throws on the first invalid string. Discards the parse output —\n * callers re-parse at resolution time to keep the array as the\n * authoritative payload.\n */\nexport function validateScopeArray(raw: ReadonlyArray<string>): void {\n for (const s of raw) parseScope(s);\n}\n\nfunction parseCollab(raw: string): ParsedScope {\n // Split on the FIRST two colons only. Filter values may contain\n // colons (UUIDs with `urn:uuid:...`, subject ids like `auth0|user:1`).\n const idx1 = raw.indexOf(':');\n const idx2 = raw.indexOf(':', idx1 + 1);\n if (idx1 === -1 || idx2 === -1) {\n throw new InvalidScope(raw, 'collab scope requires entity:action:filter');\n }\n const entity = raw.slice(0, idx1);\n const action = raw.slice(idx1 + 1, idx2);\n const filterStr = raw.slice(idx2 + 1);\n\n if (entity !== 'annotations') {\n throw new InvalidScope(raw, `unknown collab entity: ${entity}`);\n }\n if (\n action !== 'create' &&\n action !== 'update' &&\n action !== 'delete' &&\n action !== 'set-group' &&\n action !== '*'\n ) {\n throw new InvalidScope(raw, `unknown collab action: ${action}`);\n }\n const filter = parseFilter(filterStr, raw);\n\n // `set-group` is a group-assignment authority, not a per-record collab\n // filter. The only meaningful filter values are `all` (assign to any\n // group) and `group=X` (assign to group X). `:self` and `:createdBy=Y`\n // can't be sensibly applied to \"what group is this annotation in\", so\n // we reject them at parse time rather than silently always-denying.\n if (action === 'set-group' && (filter.kind === 'self' || filter.kind === 'createdBy')) {\n throw new InvalidScope(\n raw,\n `set-group only supports :all or :group=<id> filters (got :${filter.kind})`,\n );\n }\n\n return {\n kind: 'collab',\n entity,\n action: action as CollabAction | '*',\n filter,\n };\n}\n\nfunction parseFilter(s: string, raw: string): CollabFilter {\n if (s === 'all') return { kind: 'all' };\n if (s === 'self') return { kind: 'self' };\n if (s.startsWith('createdBy=')) {\n const v = s.slice('createdBy='.length);\n if (!v) throw new InvalidScope(raw, 'createdBy= requires a value');\n return { kind: 'createdBy', userId: v };\n }\n if (s.startsWith('group=')) {\n const v = s.slice('group='.length);\n if (!v) throw new InvalidScope(raw, 'group= requires a value');\n return { kind: 'group', groupId: v };\n }\n throw new InvalidScope(raw, `unknown filter: ${s}`);\n}\n\n// Capability shape: lowercase dotted segments, e.g. \"doc.text.copy\".\n// Validates shape first, then membership in the closed set.\nconst CAPABILITY_SHAPE = /^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)+$/;\n\nfunction parseCapability(raw: string): ParsedScope {\n if (!CAPABILITY_SHAPE.test(raw)) {\n throw new InvalidScope(raw, 'invalid capability name shape');\n }\n if (!KNOWN_CAPABILITIES.has(raw as DocCapability)) {\n throw new InvalidScope(raw, `unknown capability: ${raw}`);\n }\n return { kind: 'capability', name: raw as DocCapability };\n}\n","import { parseScope } from './parser';\nimport type {\n CollabAction,\n CollabFilter,\n DocCapability,\n IdentityClaims,\n ParsedScope,\n PdfBits,\n} from './types';\nimport { protectedCapabilities } from '../../signature/protection';\nimport type { DocumentProtection } from '../../signature/types';\n\n/**\n * Resolved collab subject — the per-record identity bits used to test\n * collab filters against. Sourced from the target record's stored\n * `/EMBD_Metadata/UserID` and `/GroupID` at mutation time.\n */\nexport interface CollabTarget {\n userId?: string;\n groupId?: string;\n}\n\n/**\n * True iff the given capability is granted by the scope array plus the\n * PDF bits visible via `pdf.permissions` expansion.\n *\n * Wildcard `*` short-circuits to true. Anything not explicitly granted\n * (or expanded from `pdf.permissions`) is denied.\n */\nexport function checkCapability(\n capability: DocCapability,\n rawScope: ReadonlyArray<string>,\n pdfBits: PdfBits,\n protection: DocumentProtection | null = null,\n): boolean {\n // Document-derived authority comes first: what the signatures in the\n // file forbid, no scope grants — the wildcard included.\n if (protectedCapabilities(protection).has(capability)) return false;\n const parsed = rawScope.map(parseScope);\n if (parsed.some((s) => s.kind === 'wildcard')) return true;\n return expandedCapabilities(parsed, pdfBits, protection).has(capability);\n}\n\n/**\n * True iff the scope grants AT LEAST ONE of `capabilities`. Convenience\n * for routes like `/text` (`doc.text.copy` OR `doc.text.search`) and\n * `/geometry` (`doc.text.select` OR `doc.text.search`).\n */\nexport function checkAnyCapability(\n capabilities: ReadonlyArray<DocCapability>,\n rawScope: ReadonlyArray<string>,\n pdfBits: PdfBits,\n protection: DocumentProtection | null = null,\n): boolean {\n return capabilities.some((c) => checkCapability(c, rawScope, pdfBits, protection));\n}\n\n/**\n * True iff the scope grants the collab action against the target record.\n *\n * Narrowing model. For each action independently:\n * 1. wildcard `*` → allow (global escape hatch)\n * 2. if any collab scope applies to this action → NARROW: only those\n * collab filters decide. If none match the target, deny — even if\n * `doc.annotate.modify` is also present. This is what makes\n * `[modify, update:self]` correctly mean \"edit own only\" rather\n * than \"edit anyone via modify-bypass.\"\n * 3. otherwise, if `doc.annotate.modify` is present → allow. This is\n * the broad PDF-bit-derived default for create/update/delete when\n * no per-action collab filter has been written.\n * 4. otherwise → deny.\n *\n * For create: `target` should be built by the caller from JWT identity\n * (`{ userId: caller.user_id, groupId: caller.group_id }`). `:self` and\n * `:all` then trivially pass; `:group=X` is the meaningful filter (only\n * matches when the caller's default group is X).\n *\n * For update / delete: `target` is the existing row's owner.\n */\nexport function checkCollab(\n action: CollabAction,\n target: CollabTarget,\n rawScope: ReadonlyArray<string>,\n identity: IdentityClaims,\n pdfBits: PdfBits,\n): boolean {\n const parsed = rawScope.map(parseScope);\n if (parsed.some((s) => s.kind === 'wildcard')) return true;\n\n const applicableCollab = parsed.filter(\n (s): s is Extract<ParsedScope, { kind: 'collab' }> =>\n s.kind === 'collab' &&\n s.entity === 'annotations' &&\n (s.action === action || s.action === '*'),\n );\n\n if (applicableCollab.length > 0) {\n // Narrow: presence of any applicable collab scope shadows\n // `modify`-as-default for this action.\n return applicableCollab.some((s) => filterMatches(s.filter, target, identity));\n }\n\n // No collab scope for this action — fall back to the broad default.\n return expandedCapabilities(parsed, pdfBits).has('doc.annotate.modify');\n}\n\n/**\n * Compute the full set of granted capabilities after expanding\n * `pdf.permissions` and applying implication rules.\n *\n * Implications applied:\n * - `doc.annotate.modify` implies `doc.annotate.read` (you can't\n * sensibly modify what you can't see).\n * - `doc.forms.modify` implies `doc.forms.fill` and `doc.forms.read`.\n * - `doc.forms.fill` implies `doc.forms.read`.\n * - any annotation collab scope implies `doc.annotate.read`, because\n * mutation routes need to see the target row to evaluate the\n * collab filter against its current owner.\n *\n * Does NOT short-circuit on wildcard — callers do that themselves\n * before calling this. Returning the expanded set is useful for the\n * `/access` response's `effectiveScope` and for advisory UI surfacing.\n */\nexport function expandedCapabilities(\n parsed: ReadonlyArray<ParsedScope>,\n pdfBits: PdfBits,\n protection: DocumentProtection | null = null,\n): Set<DocCapability> {\n const out = new Set<DocCapability>();\n let hasAnnotationCollab = false;\n\n for (const s of parsed) {\n if (s.kind === 'capability') {\n out.add(s.name);\n } else if (s.kind === 'virtual' && s.name === 'pdf.permissions') {\n addPdfPermissions(out, pdfBits);\n } else if (s.kind === 'collab' && s.entity === 'annotations') {\n hasAnnotationCollab = true;\n }\n }\n\n // Implications (apply after the explicit additions above)\n if (out.has('doc.annotate.modify')) out.add('doc.annotate.read');\n if (out.has('doc.forms.modify')) {\n out.add('doc.forms.fill');\n out.add('doc.forms.read');\n }\n if (out.has('doc.forms.fill')) out.add('doc.forms.read');\n if (hasAnnotationCollab) out.add('doc.annotate.read');\n\n // Subtraction last: a signed document's own restrictions win over any\n // grant or implication.\n for (const removed of protectedCapabilities(protection)) out.delete(removed);\n\n return out;\n}\n\n/**\n * Convenience wrapper: takes the raw scope array, parses each entry,\n * returns the expanded capability set. Exposed for the `/access`\n * endpoint and for the SDK helper that surfaces `effectiveScope`.\n */\nexport function expandRawScope(\n rawScope: ReadonlyArray<string>,\n pdfBits: PdfBits,\n protection: DocumentProtection | null = null,\n): Set<DocCapability> {\n return expandedCapabilities(rawScope.map(parseScope), pdfBits, protection);\n}\n\n/**\n * Test a single collab filter against a target record + the caller's\n * identity. Pure function — no side effects, no implicit rules.\n *\n * all → always matches\n * self → matches if identity.user_id === target.userId\n * createdBy=<X> → matches if target.userId === X\n * group=<X> → matches if target.groupId === X\n * AND identity.groups includes X\n *\n * The group-membership check on `group=X` prevents a token from\n * matching annotations in a group it doesn't belong to, even if the\n * target row carries that groupId.\n */\n/**\n * Authority to assign a specific groupId to an annotation.\n *\n * Set-group is decoupled from `doc.annotate.modify`. The reasoning:\n * capabilities like `modify` describe row access — what kind of write\n * you can do to which existing rows — and map to PDF permission bits.\n * Set-group is a cloud-only *assignment authority*: which destination\n * group can you put an annotation into? There is no PDF-bit\n * counterpart, so it doesn't inherit from `modify`.\n *\n * Resolution order:\n * 1. newGroupId === callerDefaultGroupId → true (no real reassignment\n * is happening; the annotation gets the caller's default group)\n * 2. wildcard `*` → true (global escape hatch)\n * 3. `annotations:set-group:all` → true\n * 4. `annotations:set-group:group=<newGroupId>` → true\n * 5. `annotations:*:all` or `annotations:*:group=<newGroupId>` → true\n * (action wildcard includes set-group)\n * 6. otherwise → false\n *\n * Note: independent from membership. A user with\n * `set-group:group=legal` can assign annotations to the legal group\n * even if they're not a member — that's the whole point.\n */\nexport function checkSetGroup(\n newGroupId: string,\n callerDefaultGroupId: string | undefined,\n rawScope: ReadonlyArray<string>,\n _pdfBits: PdfBits,\n): boolean {\n // No authority needed when the caller is assigning their default group.\n if (newGroupId === callerDefaultGroupId) return true;\n\n const parsed = rawScope.map(parseScope);\n if (parsed.some((s) => s.kind === 'wildcard')) return true;\n\n for (const s of parsed) {\n if (s.kind !== 'collab') continue;\n if (s.entity !== 'annotations') continue;\n if (s.action !== 'set-group' && s.action !== '*') continue;\n if (s.filter.kind === 'all') return true;\n if (s.filter.kind === 'group' && s.filter.groupId === newGroupId) return true;\n // :self and :createdBy are rejected at parse time for set-group, so\n // they never appear here. The action-wildcard path could carry those\n // filters legitimately for other actions — silently skip those, they\n // can't satisfy a set-group check.\n }\n return false;\n}\n\nexport function filterMatches(\n filter: CollabFilter,\n target: CollabTarget,\n id: IdentityClaims,\n): boolean {\n switch (filter.kind) {\n case 'all':\n return true;\n case 'self':\n return !!id.user_id && target.userId === id.user_id;\n case 'createdBy':\n return target.userId === filter.userId;\n case 'group':\n return target.groupId === filter.groupId && (id.groups?.includes(filter.groupId) ?? false);\n }\n}\n\n/**\n * Translate `pdf.permissions` (virtual scope) into the concrete\n * capabilities it represents under the current PDF bit configuration.\n *\n * Always adds `doc.open` and `doc.render` — these are cloud-only\n * capabilities with no PDF bit, but `pdf.permissions` is meant to be\n * the \"give the user a working session\" shorthand. Without them, a\n * token with just `['pdf.permissions']` would be useless.\n *\n * Reads are unconditional: ISO 32000 / Acrobat let any reader see\n * existing annotations and form values regardless of permission bits.\n * Bit 6 governs *writing*, not visibility.\n *\n * Bit-derived expansions follow ISO 32000:\n * bit 5 → doc.text.{select, copy, search}, doc.content.copy\n * bit 3 → doc.print\n * bit 12 → doc.print.high (requires bit 3 also set)\n * bit 4 → doc.pages.modify, doc.redact, doc.metadata.modify, doc.attachments.modify\n * bit 11 → doc.pages.assemble\n * bit 6 → doc.annotate.modify\n * bit 6/9 → doc.forms.fill\n * bit 6+4 → doc.forms.modify\n *\n * Note: this same expansion lives in builders.ts as\n * `materializePdfPermissions` for SDK-side use. The two MUST stay in\n * sync; a test in resolver.test.ts pins them together.\n */\nfunction addPdfPermissions(out: Set<DocCapability>, b: PdfBits): void {\n // Always — pdf.permissions means \"give me a working session\"\n out.add('doc.open');\n out.add('doc.render');\n // Reading existing annotations and form values is unconditional —\n // PDF bit 6 governs writes, not visibility.\n out.add('doc.annotate.read');\n out.add('doc.forms.read');\n\n if (b.bit5) {\n out.add('doc.text.select');\n out.add('doc.text.copy');\n out.add('doc.text.search');\n out.add('doc.content.copy');\n }\n if (b.bit3) out.add('doc.print');\n if (b.bit12 && b.bit3) out.add('doc.print.high');\n if (b.bit4) {\n out.add('doc.pages.modify');\n out.add('doc.redact');\n out.add('doc.metadata.modify');\n out.add('doc.attachments.modify');\n }\n if (b.bit11) out.add('doc.pages.assemble');\n if (b.bit6) {\n out.add('doc.annotate.modify');\n }\n if (b.bit6 || b.bit9) {\n out.add('doc.forms.fill');\n out.add('doc.sign');\n }\n if (b.bit6 && b.bit4) out.add('doc.forms.modify');\n}\n"],"mappings":"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"}
package/dist/runtime.cjs CHANGED
@@ -1,9 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_builders = require("./builders-8Hl1AYNU.cjs");
3
- const require_fold = require("./fold-C7I5GpBz.cjs");
4
- const require_PageInsertBlankInput = require("./PageInsertBlankInput-CzShuSJI.cjs");
5
- const require_resolver = require("./resolver-D-jumvgM.cjs");
6
- const require_AbortError = require("./AbortError-CRSooKsk.cjs");
2
+ const require_viewport = require("./viewport-BWeNHZmb.cjs");
3
+ const require_derive = require("./derive-BbsDk_Kz.cjs");
4
+ const require_format = require("./format-D_epWlob.cjs");
5
+ const require_resolver = require("./resolver-CYVNV4hn.cjs");
6
+ const require_AbortError = require("./AbortError-P0RU0Mgg.cjs");
7
7
  //#region src/promise/AbortablePromise.ts
8
8
  let _Symbol$species;
9
9
  _Symbol$species = Symbol.species;
@@ -56,11 +56,11 @@ var AbortablePromise = class AbortablePromise extends Promise {
56
56
  };
57
57
  executor(wrappedResolve, wrappedReject, progress, ctrl.signal);
58
58
  });
59
- require_fold._defineProperty(this, "signal", void 0);
60
- require_fold._defineProperty(this, "_ctrl", void 0);
61
- require_fold._defineProperty(this, "_progressCbs", void 0);
62
- require_fold._defineProperty(this, "_state", void 0);
63
- require_fold._defineProperty(this, "_internalReject", void 0);
59
+ require_derive._defineProperty(this, "signal", void 0);
60
+ require_derive._defineProperty(this, "_ctrl", void 0);
61
+ require_derive._defineProperty(this, "_progressCbs", void 0);
62
+ require_derive._defineProperty(this, "_state", void 0);
63
+ require_derive._defineProperty(this, "_internalReject", void 0);
64
64
  this._ctrl = ctrl;
65
65
  this.signal = ctrl.signal;
66
66
  this._progressCbs = progressCbs;
@@ -231,13 +231,13 @@ const CONTINUOUS_RENDER_POLICY = { kind: "continuous" };
231
231
  */
232
232
  function snapFullPageViewport(policy, viewport, opts) {
233
233
  if (policy.kind === "continuous") return viewport;
234
- if (policy.fullPage.widths.length === 0) throw new require_fold.EngineError(require_PageInsertBlankInput.EngineErrorCode.InvalidArg, "render policy has an empty width ladder");
234
+ if (policy.fullPage.widths.length === 0) throw new require_derive.EngineError(require_format.EngineErrorCode.InvalidArg, "render policy has an empty width ladder");
235
235
  const widths = [...policy.fullPage.widths].sort((a, b) => a - b);
236
236
  let requested;
237
237
  if (viewport.kind === "width") requested = viewport.width;
238
238
  else {
239
239
  const pageWidth = opts?.pageWidth;
240
- if (pageWidth === void 0 || pageWidth <= 0) throw new require_fold.EngineError(require_PageInsertBlankInput.EngineErrorCode.InvalidArg, "snapping a scale viewport to a width lattice requires opts.pageWidth");
240
+ if (pageWidth === void 0 || pageWidth <= 0) throw new require_derive.EngineError(require_format.EngineErrorCode.InvalidArg, "snapping a scale viewport to a width lattice requires opts.pageWidth");
241
241
  requested = (viewport.scale ?? 1) * pageWidth;
242
242
  }
243
243
  return {
@@ -255,7 +255,7 @@ function snapFullPageViewport(policy, viewport, opts) {
255
255
  */
256
256
  function snapAppearanceScale(policy, scale) {
257
257
  if (policy.kind === "continuous" || policy.appearances === void 0) return scale;
258
- if (policy.appearances.scales.length === 0) throw new require_fold.EngineError(require_PageInsertBlankInput.EngineErrorCode.InvalidArg, "render policy has an empty appearance-scale lattice");
258
+ if (policy.appearances.scales.length === 0) throw new require_derive.EngineError(require_format.EngineErrorCode.InvalidArg, "render policy has an empty appearance-scale lattice");
259
259
  const scales = [...policy.appearances.scales].sort((a, b) => a - b);
260
260
  return scales.find((s) => s >= scale) ?? scales[scales.length - 1];
261
261
  }
@@ -271,7 +271,7 @@ function snapAppearanceScale(policy, scale) {
271
271
  */
272
272
  function snapTileScale(policy, scale) {
273
273
  if (policy.kind === "continuous" || policy.tiles === void 0) return scale;
274
- if (policy.tiles.scales.length === 0) throw new require_fold.EngineError(require_PageInsertBlankInput.EngineErrorCode.InvalidArg, "render policy has an empty tile-scale lattice");
274
+ if (policy.tiles.scales.length === 0) throw new require_derive.EngineError(require_format.EngineErrorCode.InvalidArg, "render policy has an empty tile-scale lattice");
275
275
  const scales = [...policy.tiles.scales].sort((a, b) => a - b);
276
276
  return scales.find((s) => s >= scale) ?? scales[scales.length - 1];
277
277
  }
@@ -357,116 +357,165 @@ function wirePack(payload, transfer = EMPTY_TRANSFER) {
357
357
  };
358
358
  }
359
359
  //#endregion
360
- exports.ANNOTATION_SUBTYPES = require_PageInsertBlankInput.ANNOTATION_SUBTYPES;
360
+ exports.ANNOTATION_SUBTYPES = require_format.ANNOTATION_SUBTYPES;
361
361
  exports.AbortError = require_AbortError.AbortError;
362
362
  exports.AbortablePromise = AbortablePromise;
363
363
  exports.CONTINUOUS_RENDER_POLICY = CONTINUOUS_RENDER_POLICY;
364
- exports.DEFAULT_PDF_SAVE_MODE = require_builders.DEFAULT_PDF_SAVE_MODE;
364
+ exports.DEFAULT_EDGE_RESOLVER_BUDGET = require_viewport.DEFAULT_EDGE_RESOLVER_BUDGET;
365
+ exports.DEFAULT_PDF_SAVE_MODE = require_viewport.DEFAULT_PDF_SAVE_MODE;
365
366
  exports.EMPTY_TRANSFER = EMPTY_TRANSFER;
366
- exports.EngineError = require_fold.EngineError;
367
- exports.EngineErrorCode = require_PageInsertBlankInput.EngineErrorCode;
367
+ exports.EdgeResolver = require_viewport.EdgeResolver;
368
+ exports.EngineError = require_derive.EngineError;
369
+ exports.EngineErrorCode = require_format.EngineErrorCode;
368
370
  exports.InvalidScope = require_resolver.InvalidScope;
371
+ exports.METRES = require_derive.METRES;
369
372
  exports.MissingIdentity = require_resolver.MissingIdentity;
370
- exports.NO_ANNOTATION_FLAGS = require_builders.NO_ANNOTATION_FLAGS;
371
- exports.PAGE_INSERT_BLANK_MAX_COUNT = require_PageInsertBlankInput.PAGE_INSERT_BLANK_MAX_COUNT;
372
- exports.PDF_BITS = require_builders.PDF_BITS;
373
- exports.PDF_CODE_TO_SUBTYPE = require_PageInsertBlankInput.PDF_CODE_TO_SUBTYPE;
374
- exports.PDF_SUBTYPE_TO_CODE = require_PageInsertBlankInput.PDF_SUBTYPE_TO_CODE;
375
- exports.PdfAnnotationSubtypeCode = require_PageInsertBlankInput.PdfAnnotationSubtypeCode;
373
+ exports.NO_ANNOTATION_FLAGS = require_viewport.NO_ANNOTATION_FLAGS;
374
+ exports.PAGE_INSERT_BLANK_MAX_COUNT = require_format.PAGE_INSERT_BLANK_MAX_COUNT;
375
+ exports.PDF_BITS = require_viewport.PDF_BITS;
376
+ exports.PDF_CODE_TO_SUBTYPE = require_format.PDF_CODE_TO_SUBTYPE;
377
+ exports.PDF_SUBTYPE_TO_CODE = require_format.PDF_SUBTYPE_TO_CODE;
378
+ exports.PdfAnnotationSubtypeCode = require_format.PdfAnnotationSubtypeCode;
376
379
  exports.PermissionDenied = require_resolver.PermissionDenied;
377
- exports.SEARCH_FOLD_VERSION = require_fold.SEARCH_FOLD_VERSION;
378
- exports.SEARCH_REGEX_MAX_LENGTH = require_builders.SEARCH_REGEX_MAX_LENGTH;
379
- exports.SEARCH_SNIPPET_CONTEXT = require_builders.SEARCH_SNIPPET_CONTEXT;
380
- exports.UNKNOWN_WEAK_ANNOTATION_STATE = require_builders.UNKNOWN_WEAK_ANNOTATION_STATE;
380
+ exports.SEARCH_FOLD_VERSION = require_derive.SEARCH_FOLD_VERSION;
381
+ exports.SEARCH_REGEX_MAX_LENGTH = require_viewport.SEARCH_REGEX_MAX_LENGTH;
382
+ exports.SEARCH_SNIPPET_CONTEXT = require_viewport.SEARCH_SNIPPET_CONTEXT;
383
+ exports.SIGNATURE_POLICY_VERSION = require_resolver.SIGNATURE_POLICY_VERSION;
384
+ exports.UNKNOWN_WEAK_ANNOTATION_STATE = require_viewport.UNKNOWN_WEAK_ANNOTATION_STATE;
385
+ exports.USAGE_INCOMPLETE = require_viewport.USAGE_INCOMPLETE;
381
386
  exports.advisoryFromPdfBits = advisoryFromPdfBits;
382
- exports.appearanceImpactOf = require_builders.appearanceImpactOf;
383
- exports.boundaryTextOffset = require_PageInsertBlankInput.boundaryTextOffset;
384
- exports.buildCommentThreads = require_builders.buildCommentThreads;
385
- exports.buildPageTextLayout = require_builders.buildPageTextLayout;
386
- exports.buildSnippet = require_builders.buildSnippet;
387
- exports.buildThreads = require_builders.buildThreads;
388
- exports.canonicalSearchQuery = require_builders.canonicalSearchQuery;
389
- exports.caps = require_builders.caps;
390
- exports.changesAnnotationList = require_builders.changesAnnotationList;
391
- exports.charBoundaryAtTextOffset = require_PageInsertBlankInput.charBoundaryAtTextOffset;
392
- exports.charMapViolation = require_PageInsertBlankInput.charMapViolation;
393
- exports.charRangeForTextOffsets = require_PageInsertBlankInput.charRangeForTextOffsets;
387
+ exports.appearanceImpactOf = require_viewport.appearanceImpactOf;
388
+ exports.areaUnitLabel = require_derive.areaUnitLabel;
389
+ exports.assertPdfFloat = require_derive.assertPdfFloat;
390
+ exports.assertWritableMeasure = require_derive.assertWritableMeasure;
391
+ exports.assessmentOf = require_viewport.assessmentOf;
392
+ exports.boundaryTextOffset = require_format.boundaryTextOffset;
393
+ exports.buildCommentThreads = require_viewport.buildCommentThreads;
394
+ exports.buildPageTextLayout = require_viewport.buildPageTextLayout;
395
+ exports.buildSnippet = require_viewport.buildSnippet;
396
+ exports.buildThreads = require_derive.buildThreads;
397
+ exports.canonicalSearchQuery = require_viewport.canonicalSearchQuery;
398
+ exports.caps = require_viewport.caps;
399
+ exports.changedKeys = require_viewport.changedKeys;
400
+ exports.changesAnnotationList = require_viewport.changesAnnotationList;
401
+ exports.charBoundaryAtTextOffset = require_format.charBoundaryAtTextOffset;
402
+ exports.charMapViolation = require_format.charMapViolation;
403
+ exports.charRangeForTextOffsets = require_format.charRangeForTextOffsets;
394
404
  exports.checkAnyCapability = require_resolver.checkAnyCapability;
395
405
  exports.checkCapability = require_resolver.checkCapability;
396
406
  exports.checkCollab = require_resolver.checkCollab;
397
407
  exports.checkSetGroup = require_resolver.checkSetGroup;
398
- exports.classifyRelation = require_builders.classifyRelation;
399
- exports.collab = require_builders.collab;
400
- exports.createPageImageHandle = require_builders.createPageImageHandle;
401
- exports.decodeFieldRefKey = require_builders.decodeFieldRefKey;
402
- exports.decodePdfBits = require_builders.decodePdfBits;
403
- exports.decodeStableIdKey = require_builders.decodeStableIdKey;
404
- exports.decodeSubmitFormFlags = require_builders.decodeSubmitFormFlags;
405
- exports.deserializeError = require_fold.deserializeError;
406
- exports.encodeFieldRefKey = require_builders.encodeFieldRefKey;
407
- exports.encodeStableIdKey = require_builders.encodeStableIdKey;
408
+ exports.classifyRelation = require_derive.classifyRelation;
409
+ exports.collab = require_viewport.collab;
410
+ exports.combine = require_viewport.combine;
411
+ exports.conclude = require_viewport.conclude;
412
+ exports.createPageImageHandle = require_viewport.createPageImageHandle;
413
+ exports.decodeFieldRefKey = require_viewport.decodeFieldRefKey;
414
+ exports.decodePdfBits = require_viewport.decodePdfBits;
415
+ exports.decodeStableIdKey = require_viewport.decodeStableIdKey;
416
+ exports.decodeSubmitFormFlags = require_viewport.decodeSubmitFormFlags;
417
+ exports.deriveMeasurementLabel = require_derive.deriveMeasurementLabel;
418
+ exports.deriveProtection = require_resolver.deriveProtection;
419
+ exports.deserializeError = require_derive.deserializeError;
420
+ exports.dictEntries = require_viewport.dictEntries;
421
+ exports.encodeFieldRefKey = require_viewport.encodeFieldRefKey;
422
+ exports.encodeStableIdKey = require_viewport.encodeStableIdKey;
423
+ exports.evaluateStep = require_viewport.evaluateStep;
408
424
  exports.expandRawScope = require_resolver.expandRawScope;
409
- exports.expandTextRangeToLine = require_builders.expandTextRangeToLine;
410
- exports.expandTextRangeToWord = require_builders.expandTextRangeToWord;
425
+ exports.expandTextRangeToLine = require_viewport.expandTextRangeToLine;
426
+ exports.expandTextRangeToWord = require_viewport.expandTextRangeToWord;
411
427
  exports.expandedCapabilities = require_resolver.expandedCapabilities;
428
+ exports.fieldLockFor = require_resolver.fieldLockFor;
412
429
  exports.filterMatches = require_resolver.filterMatches;
413
- exports.foldOptionsFor = require_builders.foldOptionsFor;
414
- exports.foldText = require_fold.foldText;
415
- exports.glyphLooseBounds = require_fold.glyphLooseBounds;
416
- exports.glyphLooseQuad = require_fold.glyphLooseQuad;
417
- exports.invalidatesWeakIndexRefs = require_builders.invalidatesWeakIndexRefs;
430
+ exports.foldOptionsFor = require_viewport.foldOptionsFor;
431
+ exports.foldText = require_derive.foldText;
432
+ exports.formatMeasurement = require_format.formatMeasurement;
433
+ exports.glyphLooseBounds = require_derive.glyphLooseBounds;
434
+ exports.glyphLooseQuad = require_derive.glyphLooseQuad;
435
+ exports.invalidatesWeakIndexRefs = require_viewport.invalidatesWeakIndexRefs;
418
436
  exports.isAbortError = require_AbortError.isAbortError;
419
- exports.isRotatedGeometryRun = require_fold.isRotatedGeometryRun;
420
- exports.isStateAnnotation = require_builders.isStateAnnotation;
421
- exports.isValidPageObjectNumber = require_builders.isValidPageObjectNumber;
422
- exports.knownWeakAnnotationState = require_builders.knownWeakAnnotationState;
423
- exports.matchLiteral = require_builders.matchLiteral;
424
- exports.matchPageText = require_builders.matchPageText;
425
- exports.matchRegex = require_builders.matchRegex;
426
- exports.materializePdfPermissions = require_builders.materializePdfPermissions;
427
- exports.normalizeAnnotationDraft = require_builders.normalizeAnnotationDraft;
428
- exports.normalizeAnnotationPatch = require_builders.normalizeAnnotationPatch;
429
- exports.normalizeAttachmentFileSource = require_builders.normalizeAttachmentFileSource;
430
- exports.normalizePdfRect = require_fold.normalizePdfRect;
437
+ exports.isDimension = require_derive.isDimension;
438
+ exports.isReadout = require_derive.isReadout;
439
+ exports.isRotatedGeometryRun = require_derive.isRotatedGeometryRun;
440
+ exports.isStateAnnotation = require_viewport.isStateAnnotation;
441
+ exports.isValidPageObjectNumber = require_viewport.isValidPageObjectNumber;
442
+ exports.knownWeakAnnotationState = require_viewport.knownWeakAnnotationState;
443
+ exports.levelAllows = require_resolver.levelAllows;
444
+ exports.levelFromPermission = require_resolver.levelFromPermission;
445
+ exports.lockCovers = require_resolver.lockCovers;
446
+ exports.lockNameCovers = require_resolver.lockNameCovers;
447
+ exports.matchLiteral = require_viewport.matchLiteral;
448
+ exports.matchPageText = require_viewport.matchPageText;
449
+ exports.matchRegex = require_viewport.matchRegex;
450
+ exports.materializePdfPermissions = require_viewport.materializePdfPermissions;
451
+ exports.measureFromKnownLength = require_derive.measureFromKnownLength;
452
+ exports.measureFromRatio = require_derive.measureFromRatio;
453
+ exports.measurementPoint = require_derive.measurementPoint;
454
+ exports.measurementReadout = require_derive.measurementReadout;
455
+ exports.minLevel = require_resolver.minLevel;
456
+ exports.normalizeAnnotationDraft = require_viewport.normalizeAnnotationDraft;
457
+ exports.normalizeAnnotationPatch = require_viewport.normalizeAnnotationPatch;
458
+ exports.normalizeAttachmentFileSource = require_viewport.normalizeAttachmentFileSource;
459
+ exports.normalizePdfRect = require_derive.normalizePdfRect;
460
+ exports.parsePdfValue = require_viewport.parsePdfValue;
431
461
  exports.parseScope = require_resolver.parseScope;
432
462
  exports.passwordPromptFromState = passwordPromptFromState;
433
- exports.pdfPermissions = require_builders.pdfPermissions;
434
- exports.pdfQuadBounds = require_fold.pdfQuadBounds;
435
- exports.pdfQuadCorners = require_fold.pdfQuadCorners;
436
- exports.pdfQuadFromCorners = require_fold.pdfQuadFromCorners;
437
- exports.pdfRectFromOriginSize = require_fold.pdfRectFromOriginSize;
438
- exports.pdfRectHeight = require_fold.pdfRectHeight;
439
- exports.pdfRectSize = require_fold.pdfRectSize;
440
- exports.pdfRectToOriginSize = require_fold.pdfRectToOriginSize;
441
- exports.pdfRectWidth = require_fold.pdfRectWidth;
463
+ exports.pathLength = require_derive.pathLength;
464
+ exports.pdfPermissions = require_viewport.pdfPermissions;
465
+ exports.pdfQuadBounds = require_derive.pdfQuadBounds;
466
+ exports.pdfQuadCorners = require_derive.pdfQuadCorners;
467
+ exports.pdfQuadFromCorners = require_derive.pdfQuadFromCorners;
468
+ exports.pdfRectFromOriginSize = require_derive.pdfRectFromOriginSize;
469
+ exports.pdfRectHeight = require_derive.pdfRectHeight;
470
+ exports.pdfRectSize = require_derive.pdfRectSize;
471
+ exports.pdfRectToOriginSize = require_derive.pdfRectToOriginSize;
472
+ exports.pdfRectWidth = require_derive.pdfRectWidth;
473
+ exports.pdfValueEquals = require_viewport.pdfValueEquals;
442
474
  exports.permissionInfoFromProbe = permissionInfoFromProbe;
443
475
  exports.permissionInfoWithAdvisory = permissionInfoWithAdvisory;
444
- exports.refKey = require_builders.refKey;
445
- exports.resolveBinarySource = require_builders.resolveBinarySource;
446
- exports.revisionTokensEqual = require_builders.revisionTokensEqual;
447
- exports.searchContentEpoch = require_builders.searchContentEpoch;
448
- exports.searchRectsForRange = require_builders.searchRectsForRange;
449
- exports.searchSegmentsForRange = require_builders.searchSegmentsForRange;
476
+ exports.polygonArea = require_derive.polygonArea;
477
+ exports.primaryFinding = require_viewport.primaryFinding;
478
+ exports.protectedCapabilities = require_resolver.protectedCapabilities;
479
+ exports.refKey = require_derive.refKey;
480
+ exports.refsOf = require_viewport.refsOf;
481
+ exports.resolveBinarySource = require_viewport.resolveBinarySource;
482
+ exports.restrictionsFor = require_viewport.restrictionsFor;
483
+ exports.restrictionsOf = require_viewport.restrictionsOf;
484
+ exports.revisionTokensEqual = require_viewport.revisionTokensEqual;
485
+ exports.richTextParagraphsFromPlainText = require_viewport.richTextParagraphsFromPlainText;
486
+ exports.richTextPlainText = require_viewport.richTextPlainText;
487
+ exports.sameEffectiveValue = require_viewport.sameEffectiveValue;
488
+ exports.scaleOf = require_derive.scaleOf;
489
+ exports.searchContentEpoch = require_viewport.searchContentEpoch;
490
+ exports.searchRectsForRange = require_viewport.searchRectsForRange;
491
+ exports.searchSegmentsForRange = require_viewport.searchSegmentsForRange;
450
492
  exports.securityStateFromHead = securityStateFromHead;
451
493
  exports.securityStateFromProbe = securityStateFromProbe;
452
- exports.semanticEqual = require_builders.semanticEqual;
453
- exports.serializeError = require_fold.serializeError;
454
- exports.shiftsExistingAnnotationIndices = require_builders.shiftsExistingAnnotationIndices;
455
- exports.sliceTextByChars = require_PageInsertBlankInput.sliceTextByChars;
494
+ exports.semanticEqual = require_viewport.semanticEqual;
495
+ exports.serializeError = require_derive.serializeError;
496
+ exports.shiftsExistingAnnotationIndices = require_viewport.shiftsExistingAnnotationIndices;
497
+ exports.sliceTextByChars = require_format.sliceTextByChars;
456
498
  exports.snapAppearanceScale = snapAppearanceScale;
457
499
  exports.snapFullPageViewport = snapFullPageViewport;
458
500
  exports.snapTileScale = snapTileScale;
459
- exports.sniffBinaryMetadata = require_builders.sniffBinaryMetadata;
460
- exports.subtypeFromCode = require_PageInsertBlankInput.subtypeFromCode;
461
- exports.textGlyphAt = require_builders.textGlyphAt;
462
- exports.textGlyphQuad = require_builders.textGlyphQuad;
463
- exports.textSegmentsForRange = require_builders.textSegmentsForRange;
464
- exports.toOriginalRange = require_fold.toOriginalRange;
501
+ exports.sniffBinaryMetadata = require_viewport.sniffBinaryMetadata;
502
+ exports.squareMetres = require_derive.squareMetres;
503
+ exports.squareOf = require_derive.squareOf;
504
+ exports.stableStringify = require_viewport.stableStringify;
505
+ exports.subtypeFromCode = require_format.subtypeFromCode;
506
+ exports.textGlyphAt = require_viewport.textGlyphAt;
507
+ exports.textGlyphQuad = require_viewport.textGlyphQuad;
508
+ exports.textSegmentsForRange = require_viewport.textSegmentsForRange;
509
+ exports.toOriginalRange = require_derive.toOriginalRange;
510
+ exports.validAreaBoundary = require_derive.validAreaBoundary;
511
+ exports.validNumberFormat = require_format.validNumberFormat;
465
512
  exports.validateScopeArray = require_resolver.validateScopeArray;
466
- exports.validateSearchQuery = require_builders.validateSearchQuery;
467
- exports.validateSearchRegex = require_builders.validateSearchRegex;
513
+ exports.validateSearchQuery = require_viewport.validateSearchQuery;
514
+ exports.validateSearchRegex = require_viewport.validateSearchRegex;
515
+ exports.viewportForPoint = require_viewport.viewportForPoint;
468
516
  exports.wirePack = wirePack;
469
- exports.wordAt = require_builders.wordAt;
470
- exports.wordBefore = require_builders.wordBefore;
517
+ exports.wordAt = require_viewport.wordAt;
518
+ exports.wordBefore = require_viewport.wordBefore;
519
+ exports.worstVerdict = require_viewport.worstVerdict;
471
520
 
472
521
  //# sourceMappingURL=runtime.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.cjs","names":["AbortError","EngineError","EngineErrorCode"],"sources":["../src/promise/AbortablePromise.ts","../src/engine/document-security-state.ts","../src/engine/DocumentRenderService.ts","../src/engine/passwordPrompt.ts","../src/wire/WirePack.ts"],"sourcesContent":["import { AbortError } from './AbortError';\n\nexport type AbortableExecutor<T, P> = (\n resolve: (value: T | PromiseLike<T>) => void,\n reject: (reason: unknown) => void,\n progress: (p: P) => void,\n signal: AbortSignal,\n) => void;\n\n/**\n * Awaitable Promise subclass with abort and progress.\n *\n * Symbol.species returns plain Promise so chained .then/.catch/.finally\n * return regular promises and never try to synthesize a new\n * AbortablePromise (which would have no executor).\n *\n * Settlement contract:\n * - The first settlement wins. resolve/reject handed to the executor\n * are wrapped so that any subsequent call (whether by the executor,\n * by abort(), or by both racing) becomes a silent no-op.\n * - abort() rejects the public promise with AbortError immediately,\n * even if the executor never observes the AbortSignal. The executor\n * still receives the AbortSignal so it can stop real work\n * cooperatively (release a queue slot, abort fetch, post a worker\n * abort, etc.). Anything the executor does after that — including\n * a late resolve(value), a late reject(error), or a rejected inner\n * Promise inside AbortablePromise.run — is dropped silently.\n * - abort() after settlement is a no-op.\n */\nexport class AbortablePromise<T, P = never> extends Promise<T> {\n static override get [Symbol.species](): PromiseConstructor {\n return Promise;\n }\n\n readonly signal: AbortSignal;\n\n private readonly _ctrl: AbortController;\n private readonly _progressCbs: Set<(p: P) => void>;\n private readonly _state: { settled: boolean };\n private readonly _internalReject: (reason: unknown) => void;\n\n constructor(executor: AbortableExecutor<T, P>) {\n const ctrl = new AbortController();\n const progressCbs = new Set<(p: P) => void>();\n const state = { settled: false };\n let capturedReject!: (reason: unknown) => void;\n\n super((resolve, reject) => {\n capturedReject = reject;\n\n const wrappedResolve = (value: T | PromiseLike<T>) => {\n if (state.settled) return;\n state.settled = true;\n resolve(value);\n };\n const wrappedReject = (reason: unknown) => {\n if (state.settled) return;\n state.settled = true;\n reject(reason);\n };\n const progress = (p: P) => {\n if (state.settled) return;\n for (const cb of progressCbs) {\n try {\n cb(p);\n } catch {\n // ignore subscriber errors\n }\n }\n };\n\n executor(wrappedResolve, wrappedReject, progress, ctrl.signal);\n });\n\n this._ctrl = ctrl;\n this.signal = ctrl.signal;\n this._progressCbs = progressCbs;\n this._state = state;\n this._internalReject = capturedReject;\n }\n\n /**\n * Abort the underlying operation.\n *\n * - If the promise has already settled, this is a no-op.\n * - Otherwise the public promise rejects synchronously with an\n * AbortError (wrapping the supplied reason if it is not already an\n * AbortError) and the AbortSignal fires for any cooperative\n * cancellation the executor wired up.\n */\n abort(reason?: unknown): void {\n if (this._state.settled) return;\n if (this._ctrl.signal.aborted) return;\n\n const err = reason instanceof AbortError ? reason : new AbortError(reason);\n\n // Mark settled before signalling so any abort listener the executor\n // attached cannot race a late resolve/reject through the wrapped\n // settlers (they all consult state.settled).\n this._state.settled = true;\n this._ctrl.abort(err);\n this._internalReject(err);\n }\n\n /**\n * Subscribe to progress events. Returns an unsubscribe function.\n */\n onProgress(cb: (p: P) => void): () => void {\n this._progressCbs.add(cb);\n return () => {\n this._progressCbs.delete(cb);\n };\n }\n\n /**\n * Wrap a value in an already-resolved AbortablePromise. Mirrors Promise.resolve.\n */\n static resolveValue<T>(value: T | PromiseLike<T>): AbortablePromise<T> {\n return new AbortablePromise<T>((resolve) => resolve(value));\n }\n\n /**\n * Wrap a thrown reason in an already-rejected AbortablePromise.\n */\n static rejectReason<T = never>(reason: unknown): AbortablePromise<T> {\n return new AbortablePromise<T>((_resolve, reject) => reject(reason));\n }\n\n /**\n * Run an async function as an AbortablePromise so callers get .abort() and .signal.\n * The async function receives the AbortSignal (and a progress emitter) so it can\n * cooperate with the abort.\n *\n * If abort() fires while the inner async function is still running, the\n * public promise rejects with AbortError immediately. The inner async\n * function's eventual settlement (resolve or reject) is funneled through\n * the wrapped settlers and silently dropped — including a late rejection\n * from the underlying work, which prevents an UnhandledRejection because\n * the .then(resolve, reject) below \"handles\" it via the no-op.\n */\n static run<T, P = never>(\n fn: (signal: AbortSignal, progress: (p: P) => void) => Promise<T>,\n ): AbortablePromise<T, P> {\n return new AbortablePromise<T, P>((resolve, reject, progress, signal) => {\n fn(signal, progress).then(resolve, reject);\n });\n }\n}\n","import type {\n DocumentAccessReason,\n DocumentSecurityState,\n PdfPermissionAdvisory,\n PdfPermissionInfo,\n} from './DocumentSecurityService';\nimport type { PdfBits } from '../auth/scope';\nimport type { DocumentSecurityProbeInfo } from '../wire/worker-protocol';\n\nexport interface DocumentHeadLike {\n encryption: {\n state: DocumentSecurityState['encryption']['state'];\n requiresPassword: boolean | null;\n };\n permissions: DocumentSecurityState['permissions'];\n access: DocumentSecurityState['access'];\n}\n\nexport function securityStateFromHead(head: DocumentHeadLike): DocumentSecurityState {\n return {\n encryption: {\n state: head.encryption.state,\n requiresPassword: head.encryption.requiresPassword,\n },\n permissions: { ...head.permissions },\n access: {\n required: head.access.required,\n reasons: [...head.access.reasons],\n ...(head.access.endpoint ? { endpoint: head.access.endpoint } : {}),\n },\n };\n}\n\nexport function permissionInfoFromProbe(info: DocumentSecurityProbeInfo): PdfPermissionInfo | null {\n if (info.pdfPermissionsBits === null) return null;\n return {\n known: true,\n bits: info.pdfPermissionsBits,\n allAllowed: info.pdfPermissionsAllAllowed,\n openedAs: info.pdfOpenedAs,\n securityHandlerRevision: info.securityHandlerRevision,\n };\n}\n\n/**\n * Same as {@link permissionInfoFromProbe} but additionally populates\n * `flags` (typed PdfBits view) and `advisory` (capability-shaped\n * booleans for UI badges). Used by routes that have a decoded `PdfBits`\n * in hand — notably `/access`, which always does.\n *\n * Returns `null` for the same reason as the base function — when the\n * PDF hasn't been probed yet so bits are unknown.\n */\nexport function permissionInfoWithAdvisory(\n info: DocumentSecurityProbeInfo,\n pdfBits: PdfBits,\n): PdfPermissionInfo | null {\n const base = permissionInfoFromProbe(info);\n if (!base) return null;\n return {\n ...base,\n flags: pdfBits,\n advisory: advisoryFromPdfBits(pdfBits),\n };\n}\n\n/**\n * Translate a {@link PdfBits} view into the capability-shaped\n * {@link PdfPermissionAdvisory}. The rules here MUST mirror the bit\n * combinations the scope resolver uses for `pdf.permissions`\n * expansion — `doc.print.high` requires bit 12 AND bit 3, etc.\n */\nexport function advisoryFromPdfBits(b: PdfBits): PdfPermissionAdvisory {\n return {\n canPrint: b.bit3,\n canPrintHigh: b.bit12 && b.bit3,\n canCopy: b.bit5,\n canAnnotate: b.bit6,\n canFillForms: b.bit6 || b.bit9,\n canModifyForms: b.bit6 && b.bit4,\n canModifyPages: b.bit4,\n canAssemble: b.bit11,\n };\n}\n\nexport function securityStateFromProbe(\n info: DocumentSecurityProbeInfo,\n opts: { accessEndpoint?: string; cdnRequired?: boolean } = {},\n): DocumentSecurityState {\n const permission = permissionInfoFromProbe(info);\n const reasons: DocumentAccessReason[] = [];\n if (info.encryptionRequiresPassword === true && !permission) reasons.push('password');\n if (opts.cdnRequired) reasons.push('cdn');\n if (info.encryptionState === 'unknown') reasons.push('permissions-unknown');\n\n return {\n encryption: {\n state: info.encryptionState,\n requiresPassword: info.encryptionRequiresPassword,\n },\n permissions: {\n known: permission?.known ?? false,\n bits: permission?.bits ?? null,\n allAllowed: permission?.allAllowed ?? null,\n openedAs: permission?.openedAs ?? null,\n securityHandlerRevision: permission?.securityHandlerRevision ?? null,\n canUpgradeToOwner: info.encryptionState === 'encrypted' && info.pdfOpenedAs !== 'owner',\n },\n access: {\n required: reasons.length > 0,\n reasons,\n ...(reasons.length > 0 && opts.accessEndpoint ? { endpoint: opts.accessEndpoint } : {}),\n },\n };\n}\n","import { EngineError } from '../errors/EngineError';\nimport { EngineErrorCode } from '../errors/EngineErrorCode';\nimport type { PageRenderViewport } from '../dto/PageRender';\n\n/**\n * The engine's render-parameter policy.\n *\n * - `continuous` — any viewport renders exactly as requested. The LOCAL\n * engine always answers this: rendering is in-process, there is no\n * shared cache to protect, and exactness is the local product promise.\n * - `lattice` — the deployment treats a finite set of render points as\n * durable, CDN-shared artifacts. The bounded quantity\n * is OUTPUT PIXELS, never zoom:\n *\n * - `fullPage.widths` — full-page renders quantize on `viewport.width`\n * (a scale lattice bounds artifact COUNT but not SIZE: PDF page space\n * is effectively unbounded, so scale 1 of a poster page is a memory\n * bomb while width caps the dominant dimension by construction).\n * - `tiles` — RESERVED for the deep-zoom vertical: a scale-based\n * pyramid × fixed tile size, where per-job cost is constant. Absent\n * until the tiling plugin ships; reserving the shape keeps the SDK\n * contract from churning twice.\n * - `maxRenderPixels` — the worker-side output budget for degenerate\n * geometry (width bounds width, not height).\n *\n * The SDK never conforms a request implicitly — the same `render.image`\n * call must not return different pixels on local vs cloud. Conformance is\n * the caller's visible choice via {@link snapFullPageViewport}.\n */\nexport type EngineRenderPolicy =\n | { readonly kind: 'continuous' }\n | {\n readonly kind: 'lattice';\n readonly fullPage: { readonly widths: readonly number[] };\n readonly tiles?: {\n readonly tileSizes: readonly number[];\n readonly scales: readonly number[];\n };\n /**\n * Annotation-appearance lattice — SCALE-based, unlike full pages:\n * an appearance is sized by `annotation.rect × scale`, rects span\n * orders of magnitude (a 4pt caret vs a page-sized stamp), and the\n * composite must track the PAGE's effective render scale or\n * annotations sit blurry on a crisp raster. Small images → the\n * ladder affords finer, taller steps than pages ever could.\n */\n readonly appearances?: { readonly scales: readonly number[] };\n readonly maxRenderPixels?: number;\n readonly formats: readonly ('webp' | 'png')[];\n readonly background: 'white';\n readonly enforced: boolean;\n };\n\nexport const CONTINUOUS_RENDER_POLICY: EngineRenderPolicy = { kind: 'continuous' };\n\n/**\n * Document-scoped render policy surface (`doc.render`). Async because the\n * cloud engine learns its policy from `/v1/access`; the local engine\n * resolves immediately with `continuous`.\n *\n * Per-page rendering itself stays on `page(pon).render` — this service\n * carries POLICY, not pixels.\n */\nexport interface DocumentRenderService {\n policy(): Promise<EngineRenderPolicy>;\n}\n\n/**\n * Conform a FULL-PAGE viewport to a render policy — the ONE snap\n * implementation, shared by every engine and plugin. Pure and explicit\n * by design:\n *\n * - `continuous` → identity (local/cloud plugin code stays byte-identical).\n * - `lattice` → returns a `width`-kind viewport at the smallest ladder\n * width ≥ the requested detail (snap UP: always crisp, CSS downscales\n * for free), capped at the largest width — detail beyond the cap is the\n * tile pyramid's job, by design.\n * - `scale`-kind requests convert through the page's width in PDF user\n * units — pass `opts.pageWidth` (the caller has it from the layout);\n * omitting it for a scale viewport under a lattice policy is a\n * programmer error and throws `InvalidArg`.\n */\nexport function snapFullPageViewport(\n policy: EngineRenderPolicy,\n viewport: PageRenderViewport,\n opts?: { pageWidth?: number },\n): PageRenderViewport {\n if (policy.kind === 'continuous') return viewport;\n if (policy.fullPage.widths.length === 0) {\n throw new EngineError(EngineErrorCode.InvalidArg, 'render policy has an empty width ladder');\n }\n const widths = [...policy.fullPage.widths].sort((a, b) => a - b);\n\n let requested: number;\n if (viewport.kind === 'width') {\n requested = viewport.width;\n } else {\n const pageWidth = opts?.pageWidth;\n if (pageWidth === undefined || pageWidth <= 0) {\n throw new EngineError(\n EngineErrorCode.InvalidArg,\n 'snapping a scale viewport to a width lattice requires opts.pageWidth',\n );\n }\n requested = (viewport.scale ?? 1) * pageWidth;\n }\n\n const snapped = widths.find((width) => width >= requested) ?? widths[widths.length - 1]!;\n return { kind: 'width', width: snapped };\n}\n\n/**\n * Conform an annotation-appearance scale to the policy's appearance\n * lattice. Same discipline as {@link snapFullPageViewport}: identity on\n * continuous (and when the policy carries no appearance block), snap UP,\n * cap at the largest point. Callers derive `scale` from the page's\n * effective render scale (`snappedWidth / pageWidth`, × devicePixelRatio\n * as needed) so composites stay crisp against the page raster.\n */\nexport function snapAppearanceScale(policy: EngineRenderPolicy, scale: number): number {\n if (policy.kind === 'continuous' || policy.appearances === undefined) return scale;\n if (policy.appearances.scales.length === 0) {\n throw new EngineError(\n EngineErrorCode.InvalidArg,\n 'render policy has an empty appearance-scale lattice',\n );\n }\n const scales = [...policy.appearances.scales].sort((a, b) => a - b);\n return scales.find((s) => s >= scale) ?? scales[scales.length - 1]!;\n}\n\n/**\n * Conform a tile-pyramid scale to the policy's tile lattice — the third\n * member of the `snap<PolicyBlock><Quantity>` family. Identity on\n * continuous and while the deployment doesn't advertise a `tiles` block\n * (the block is reserved until the deep-zoom vertical ships server-side;\n * the tiling client uses its own default pyramid then — see\n * plugin-render). Snap UP, cap at the largest scale: per-tile cost is\n * constant by construction (`tileSize²` pixels regardless of level), so\n * the cap bounds artifact COUNT, not memory.\n */\nexport function snapTileScale(policy: EngineRenderPolicy, scale: number): number {\n if (policy.kind === 'continuous' || policy.tiles === undefined) return scale;\n if (policy.tiles.scales.length === 0) {\n throw new EngineError(\n EngineErrorCode.InvalidArg,\n 'render policy has an empty tile-scale lattice',\n );\n }\n const scales = [...policy.tiles.scales].sort((a, b) => a - b);\n return scales.find((s) => s >= scale) ?? scales[scales.length - 1]!;\n}\n","/**\n * Unified \"should I ask the user for a password?\" shape.\n *\n * A PDF has two distinct password moments:\n * 1. **Required** — the document is encrypted and we couldn't read\n * anything anonymously. The dev MUST gather a password and call\n * `unlock()` before any other operation succeeds.\n * 2. **Optional** — the document is open and readable, but more\n * permissions become available with the owner password. The dev\n * MAY surface an \"unlock for full access\" affordance.\n *\n * Plus the trivial third state: **none** — nothing to do.\n *\n * The discriminated union encodes the invariants:\n * - `required` and `optional` cannot both be true (a doc you can't\n * read can't be \"upgraded\" — you have to read first).\n * - `hint` is only present when meaningful (no hint in the `none` arm,\n * mandatory `owner` hint in `optional` since that's the only thing\n * an upgrade asks for).\n *\n * Computed identically on local + cloud via\n * {@link passwordPromptFromState} so the dev's UI gating code is\n * engine-agnostic.\n */\nimport type { DocumentSecurityState } from './DocumentSecurityService';\n\nexport type PasswordPrompt =\n | { readonly state: 'none' }\n | { readonly state: 'required'; readonly hint: 'user' | 'owner' | null }\n | { readonly state: 'optional'; readonly hint: 'owner' };\n\n/**\n * Derive the high-level password prompt from the raw security state.\n *\n * The function is pure and total — every `DocumentSecurityState`\n * maps to exactly one `PasswordPrompt`. Use this as the SINGLE source\n * of truth wherever the SDK exposes a \"do you need a password?\" flag:\n * both `LocalDocumentSecurityService` and `CloudDocumentSecurityService`\n * call it, so the contract can never drift.\n *\n * Mapping rules (top to bottom; first match wins):\n *\n * 1. encryption.state === 'none'\n * → { state: 'none' } the document isn't encrypted\n *\n * 2. !permissions.known\n * → { state: 'required', hint: null } we haven't probed yet; can't\n * tell user-vs-owner; show\n * generic prompt\n *\n * 3. requiresPassword && openedAs === 'none'\n * → { state: 'required', hint: 'user' } encrypted, anonymous open\n * failed → user pwd needed\n *\n * 4. openedAs === 'owner'\n * → { state: 'none' } already at the top — nothing more\n * to unlock\n *\n * 5. permissions.canUpgradeToOwner === true\n * → { state: 'optional', hint: 'owner' } open as user (or as none\n * for owner-pwd-only docs)\n * but more is available\n *\n * 6. fallthrough → { state: 'none' } open and nothing more available\n */\nexport function passwordPromptFromState(state: DocumentSecurityState): PasswordPrompt {\n if (state.encryption.state === 'none') {\n return { state: 'none' };\n }\n\n if (!state.permissions.known) {\n return { state: 'required', hint: null };\n }\n\n if (state.encryption.requiresPassword && state.permissions.openedAs === 'none') {\n return { state: 'required', hint: 'user' };\n }\n\n if (state.permissions.openedAs === 'owner') {\n return { state: 'none' };\n }\n\n if (state.permissions.canUpgradeToOwner) {\n return { state: 'optional', hint: 'owner' };\n }\n\n return { state: 'none' };\n}\n","/**\n * Wire-level packaging for messages crossing a Web Worker / Node\n * `worker_thread` boundary.\n *\n * `WirePack<P>` is the typed pair every producer hands to a transport:\n * the structured payload (validated against the wire schema) and an\n * explicit, ordered list of `Transferable` objects that the producer\n * intends to move (zero-copy) to the receiver.\n *\n * Why this is a first-class type and not a transport-side argument:\n *\n * - **Producer is the only one who knows.** Whether a particular\n * `ArrayBuffer` should be moved (zero-copy, sender's reference\n * detached) or copied (structured-cloned, both sides retain\n * access) is a semantic decision belonging to the code that\n * produced the buffer. Inferring it later — by walking the\n * payload looking for `ArrayBuffer` instances and assuming they\n * should all be transferred — is the v2 footgun this design\n * replaces.\n *\n * - **Type-checked at the boundary.** A handler returning\n * `WirePack<RenderResult>` cannot forget to declare its\n * transfer list; the producer literally writes\n * `wirePack({ ..., pixels }, [pixels.buffer])` in a single\n * return statement. Forgetting yields a structurally identical\n * copy (slow but correct), never a silent leak.\n *\n * - **Symmetric across the boundary.** Same `WirePack<P>` on the\n * request path (main → worker) and the response path (worker →\n * main). Inline transports treat `transfer` as a no-op since\n * there's no thread boundary to cross.\n *\n * - **Schema-orthogonal.** Zod schemas validate `payload` and\n * nothing else. `transfer` is not data; it's a memory-ownership\n * manifest, and the design keeps those concerns separate.\n *\n * The receiver of a `postMessage(payload, { transfer })` call gets\n * the payload back **with the listed buffers already moved into it**\n * — there's no second argument on the receiving end. So `WirePack`\n * only ever travels the producer-to-transport leg of the trip; the\n * consumer side just sees the unwrapped `payload`.\n */\nexport interface WirePack<P> {\n readonly payload: P;\n readonly transfer: readonly Transferable[];\n}\n\n/**\n * Singleton empty transfer list. Frozen so accidental in-place\n * mutation throws in strict mode rather than silently mutating the\n * default for future calls.\n */\nexport const EMPTY_TRANSFER: readonly Transferable[] = Object.freeze([]);\n\n/**\n * Producer-side helper: pair a typed payload with its transfer list.\n *\n * The `transfer` argument is optional; omitting it (or passing `[]`)\n * is the explicit, type-checked way to say \"I declare this message\n * carries no transferables.\" That's structurally identical to the\n * shape the v2 walker generated for non-binary messages, but here\n * the declaration is at construction time, in the producer's own\n * code, where the knowledge actually lives.\n *\n * wirePack({ kind: 'metadata.read', jobId, docId })\n * wirePack({ kind: 'open.fatMem', jobId, docId, bytes }, [bytes])\n * wirePack({ tag: 'page.render', pixels }, [pixels.buffer])\n */\nexport function wirePack<P>(\n payload: P,\n transfer: readonly Transferable[] = EMPTY_TRANSFER,\n): WirePack<P> {\n return { payload, transfer };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runtime.cjs","names":["AbortError","EngineError","EngineErrorCode"],"sources":["../src/promise/AbortablePromise.ts","../src/engine/document-security-state.ts","../src/engine/DocumentRenderService.ts","../src/engine/passwordPrompt.ts","../src/wire/WirePack.ts"],"sourcesContent":["import { AbortError } from './AbortError';\n\nexport type AbortableExecutor<T, P> = (\n resolve: (value: T | PromiseLike<T>) => void,\n reject: (reason: unknown) => void,\n progress: (p: P) => void,\n signal: AbortSignal,\n) => void;\n\n/**\n * Awaitable Promise subclass with abort and progress.\n *\n * Symbol.species returns plain Promise so chained .then/.catch/.finally\n * return regular promises and never try to synthesize a new\n * AbortablePromise (which would have no executor).\n *\n * Settlement contract:\n * - The first settlement wins. resolve/reject handed to the executor\n * are wrapped so that any subsequent call (whether by the executor,\n * by abort(), or by both racing) becomes a silent no-op.\n * - abort() rejects the public promise with AbortError immediately,\n * even if the executor never observes the AbortSignal. The executor\n * still receives the AbortSignal so it can stop real work\n * cooperatively (release a queue slot, abort fetch, post a worker\n * abort, etc.). Anything the executor does after that — including\n * a late resolve(value), a late reject(error), or a rejected inner\n * Promise inside AbortablePromise.run — is dropped silently.\n * - abort() after settlement is a no-op.\n */\nexport class AbortablePromise<T, P = never> extends Promise<T> {\n static override get [Symbol.species](): PromiseConstructor {\n return Promise;\n }\n\n readonly signal: AbortSignal;\n\n private readonly _ctrl: AbortController;\n private readonly _progressCbs: Set<(p: P) => void>;\n private readonly _state: { settled: boolean };\n private readonly _internalReject: (reason: unknown) => void;\n\n constructor(executor: AbortableExecutor<T, P>) {\n const ctrl = new AbortController();\n const progressCbs = new Set<(p: P) => void>();\n const state = { settled: false };\n let capturedReject!: (reason: unknown) => void;\n\n super((resolve, reject) => {\n capturedReject = reject;\n\n const wrappedResolve = (value: T | PromiseLike<T>) => {\n if (state.settled) return;\n state.settled = true;\n resolve(value);\n };\n const wrappedReject = (reason: unknown) => {\n if (state.settled) return;\n state.settled = true;\n reject(reason);\n };\n const progress = (p: P) => {\n if (state.settled) return;\n for (const cb of progressCbs) {\n try {\n cb(p);\n } catch {\n // ignore subscriber errors\n }\n }\n };\n\n executor(wrappedResolve, wrappedReject, progress, ctrl.signal);\n });\n\n this._ctrl = ctrl;\n this.signal = ctrl.signal;\n this._progressCbs = progressCbs;\n this._state = state;\n this._internalReject = capturedReject;\n }\n\n /**\n * Abort the underlying operation.\n *\n * - If the promise has already settled, this is a no-op.\n * - Otherwise the public promise rejects synchronously with an\n * AbortError (wrapping the supplied reason if it is not already an\n * AbortError) and the AbortSignal fires for any cooperative\n * cancellation the executor wired up.\n */\n abort(reason?: unknown): void {\n if (this._state.settled) return;\n if (this._ctrl.signal.aborted) return;\n\n const err = reason instanceof AbortError ? reason : new AbortError(reason);\n\n // Mark settled before signalling so any abort listener the executor\n // attached cannot race a late resolve/reject through the wrapped\n // settlers (they all consult state.settled).\n this._state.settled = true;\n this._ctrl.abort(err);\n this._internalReject(err);\n }\n\n /**\n * Subscribe to progress events. Returns an unsubscribe function.\n */\n onProgress(cb: (p: P) => void): () => void {\n this._progressCbs.add(cb);\n return () => {\n this._progressCbs.delete(cb);\n };\n }\n\n /**\n * Wrap a value in an already-resolved AbortablePromise. Mirrors Promise.resolve.\n */\n static resolveValue<T>(value: T | PromiseLike<T>): AbortablePromise<T> {\n return new AbortablePromise<T>((resolve) => resolve(value));\n }\n\n /**\n * Wrap a thrown reason in an already-rejected AbortablePromise.\n */\n static rejectReason<T = never>(reason: unknown): AbortablePromise<T> {\n return new AbortablePromise<T>((_resolve, reject) => reject(reason));\n }\n\n /**\n * Run an async function as an AbortablePromise so callers get .abort() and .signal.\n * The async function receives the AbortSignal (and a progress emitter) so it can\n * cooperate with the abort.\n *\n * If abort() fires while the inner async function is still running, the\n * public promise rejects with AbortError immediately. The inner async\n * function's eventual settlement (resolve or reject) is funneled through\n * the wrapped settlers and silently dropped — including a late rejection\n * from the underlying work, which prevents an UnhandledRejection because\n * the .then(resolve, reject) below \"handles\" it via the no-op.\n */\n static run<T, P = never>(\n fn: (signal: AbortSignal, progress: (p: P) => void) => Promise<T>,\n ): AbortablePromise<T, P> {\n return new AbortablePromise<T, P>((resolve, reject, progress, signal) => {\n fn(signal, progress).then(resolve, reject);\n });\n }\n}\n","import type {\n DocumentAccessReason,\n DocumentSecurityState,\n PdfPermissionAdvisory,\n PdfPermissionInfo,\n} from './DocumentSecurityService';\nimport type { PdfBits } from '../auth/scope';\nimport type { DocumentSecurityProbeInfo } from '../wire/worker-protocol';\n\nexport interface DocumentHeadLike {\n encryption: {\n state: DocumentSecurityState['encryption']['state'];\n requiresPassword: boolean | null;\n };\n permissions: DocumentSecurityState['permissions'];\n access: DocumentSecurityState['access'];\n}\n\nexport function securityStateFromHead(head: DocumentHeadLike): DocumentSecurityState {\n return {\n encryption: {\n state: head.encryption.state,\n requiresPassword: head.encryption.requiresPassword,\n },\n permissions: { ...head.permissions },\n access: {\n required: head.access.required,\n reasons: [...head.access.reasons],\n ...(head.access.endpoint ? { endpoint: head.access.endpoint } : {}),\n },\n };\n}\n\nexport function permissionInfoFromProbe(info: DocumentSecurityProbeInfo): PdfPermissionInfo | null {\n if (info.pdfPermissionsBits === null) return null;\n return {\n known: true,\n bits: info.pdfPermissionsBits,\n allAllowed: info.pdfPermissionsAllAllowed,\n openedAs: info.pdfOpenedAs,\n securityHandlerRevision: info.securityHandlerRevision,\n };\n}\n\n/**\n * Same as {@link permissionInfoFromProbe} but additionally populates\n * `flags` (typed PdfBits view) and `advisory` (capability-shaped\n * booleans for UI badges). Used by routes that have a decoded `PdfBits`\n * in hand — notably `/access`, which always does.\n *\n * Returns `null` for the same reason as the base function — when the\n * PDF hasn't been probed yet so bits are unknown.\n */\nexport function permissionInfoWithAdvisory(\n info: DocumentSecurityProbeInfo,\n pdfBits: PdfBits,\n): PdfPermissionInfo | null {\n const base = permissionInfoFromProbe(info);\n if (!base) return null;\n return {\n ...base,\n flags: pdfBits,\n advisory: advisoryFromPdfBits(pdfBits),\n };\n}\n\n/**\n * Translate a {@link PdfBits} view into the capability-shaped\n * {@link PdfPermissionAdvisory}. The rules here MUST mirror the bit\n * combinations the scope resolver uses for `pdf.permissions`\n * expansion — `doc.print.high` requires bit 12 AND bit 3, etc.\n */\nexport function advisoryFromPdfBits(b: PdfBits): PdfPermissionAdvisory {\n return {\n canPrint: b.bit3,\n canPrintHigh: b.bit12 && b.bit3,\n canCopy: b.bit5,\n canAnnotate: b.bit6,\n canFillForms: b.bit6 || b.bit9,\n canModifyForms: b.bit6 && b.bit4,\n canModifyPages: b.bit4,\n canAssemble: b.bit11,\n };\n}\n\nexport function securityStateFromProbe(\n info: DocumentSecurityProbeInfo,\n opts: { accessEndpoint?: string; cdnRequired?: boolean } = {},\n): DocumentSecurityState {\n const permission = permissionInfoFromProbe(info);\n const reasons: DocumentAccessReason[] = [];\n if (info.encryptionRequiresPassword === true && !permission) reasons.push('password');\n if (opts.cdnRequired) reasons.push('cdn');\n if (info.encryptionState === 'unknown') reasons.push('permissions-unknown');\n\n return {\n encryption: {\n state: info.encryptionState,\n requiresPassword: info.encryptionRequiresPassword,\n },\n permissions: {\n known: permission?.known ?? false,\n bits: permission?.bits ?? null,\n allAllowed: permission?.allAllowed ?? null,\n openedAs: permission?.openedAs ?? null,\n securityHandlerRevision: permission?.securityHandlerRevision ?? null,\n canUpgradeToOwner: info.encryptionState === 'encrypted' && info.pdfOpenedAs !== 'owner',\n },\n access: {\n required: reasons.length > 0,\n reasons,\n ...(reasons.length > 0 && opts.accessEndpoint ? { endpoint: opts.accessEndpoint } : {}),\n },\n };\n}\n","import { EngineError } from '../errors/EngineError';\nimport { EngineErrorCode } from '../errors/EngineErrorCode';\nimport type { PageRenderViewport } from '../dto/PageRender';\n\n/**\n * The engine's render-parameter policy.\n *\n * - `continuous` — any viewport renders exactly as requested. The LOCAL\n * engine always answers this: rendering is in-process, there is no\n * shared cache to protect, and exactness is the local product promise.\n * - `lattice` — the deployment treats a finite set of render points as\n * durable, CDN-shared artifacts. The bounded quantity\n * is OUTPUT PIXELS, never zoom:\n *\n * - `fullPage.widths` — full-page renders quantize on `viewport.width`\n * (a scale lattice bounds artifact COUNT but not SIZE: PDF page space\n * is effectively unbounded, so scale 1 of a poster page is a memory\n * bomb while width caps the dominant dimension by construction).\n * - `tiles` — RESERVED for the deep-zoom vertical: a scale-based\n * pyramid × fixed tile size, where per-job cost is constant. Absent\n * until the tiling plugin ships; reserving the shape keeps the SDK\n * contract from churning twice.\n * - `maxRenderPixels` — the worker-side output budget for degenerate\n * geometry (width bounds width, not height).\n *\n * The SDK never conforms a request implicitly — the same `render.image`\n * call must not return different pixels on local vs cloud. Conformance is\n * the caller's visible choice via {@link snapFullPageViewport}.\n */\nexport type EngineRenderPolicy =\n | { readonly kind: 'continuous' }\n | {\n readonly kind: 'lattice';\n readonly fullPage: { readonly widths: readonly number[] };\n readonly tiles?: {\n readonly tileSizes: readonly number[];\n readonly scales: readonly number[];\n };\n /**\n * Annotation-appearance lattice — SCALE-based, unlike full pages:\n * an appearance is sized by `annotation.rect × scale`, rects span\n * orders of magnitude (a 4pt caret vs a page-sized stamp), and the\n * composite must track the PAGE's effective render scale or\n * annotations sit blurry on a crisp raster. Small images → the\n * ladder affords finer, taller steps than pages ever could.\n */\n readonly appearances?: { readonly scales: readonly number[] };\n readonly maxRenderPixels?: number;\n readonly formats: readonly ('webp' | 'png')[];\n readonly background: 'white';\n readonly enforced: boolean;\n };\n\nexport const CONTINUOUS_RENDER_POLICY: EngineRenderPolicy = { kind: 'continuous' };\n\n/**\n * Document-scoped render policy surface (`doc.render`). Async because the\n * cloud engine learns its policy from `/v1/access`; the local engine\n * resolves immediately with `continuous`.\n *\n * Per-page rendering itself stays on `page(pon).render` — this service\n * carries POLICY, not pixels.\n */\nexport interface DocumentRenderService {\n policy(): Promise<EngineRenderPolicy>;\n}\n\n/**\n * Conform a FULL-PAGE viewport to a render policy — the ONE snap\n * implementation, shared by every engine and plugin. Pure and explicit\n * by design:\n *\n * - `continuous` → identity (local/cloud plugin code stays byte-identical).\n * - `lattice` → returns a `width`-kind viewport at the smallest ladder\n * width ≥ the requested detail (snap UP: always crisp, CSS downscales\n * for free), capped at the largest width — detail beyond the cap is the\n * tile pyramid's job, by design.\n * - `scale`-kind requests convert through the page's width in PDF user\n * units — pass `opts.pageWidth` (the caller has it from the layout);\n * omitting it for a scale viewport under a lattice policy is a\n * programmer error and throws `InvalidArg`.\n */\nexport function snapFullPageViewport(\n policy: EngineRenderPolicy,\n viewport: PageRenderViewport,\n opts?: { pageWidth?: number },\n): PageRenderViewport {\n if (policy.kind === 'continuous') return viewport;\n if (policy.fullPage.widths.length === 0) {\n throw new EngineError(EngineErrorCode.InvalidArg, 'render policy has an empty width ladder');\n }\n const widths = [...policy.fullPage.widths].sort((a, b) => a - b);\n\n let requested: number;\n if (viewport.kind === 'width') {\n requested = viewport.width;\n } else {\n const pageWidth = opts?.pageWidth;\n if (pageWidth === undefined || pageWidth <= 0) {\n throw new EngineError(\n EngineErrorCode.InvalidArg,\n 'snapping a scale viewport to a width lattice requires opts.pageWidth',\n );\n }\n requested = (viewport.scale ?? 1) * pageWidth;\n }\n\n const snapped = widths.find((width) => width >= requested) ?? widths[widths.length - 1]!;\n return { kind: 'width', width: snapped };\n}\n\n/**\n * Conform an annotation-appearance scale to the policy's appearance\n * lattice. Same discipline as {@link snapFullPageViewport}: identity on\n * continuous (and when the policy carries no appearance block), snap UP,\n * cap at the largest point. Callers derive `scale` from the page's\n * effective render scale (`snappedWidth / pageWidth`, × devicePixelRatio\n * as needed) so composites stay crisp against the page raster.\n */\nexport function snapAppearanceScale(policy: EngineRenderPolicy, scale: number): number {\n if (policy.kind === 'continuous' || policy.appearances === undefined) return scale;\n if (policy.appearances.scales.length === 0) {\n throw new EngineError(\n EngineErrorCode.InvalidArg,\n 'render policy has an empty appearance-scale lattice',\n );\n }\n const scales = [...policy.appearances.scales].sort((a, b) => a - b);\n return scales.find((s) => s >= scale) ?? scales[scales.length - 1]!;\n}\n\n/**\n * Conform a tile-pyramid scale to the policy's tile lattice — the third\n * member of the `snap<PolicyBlock><Quantity>` family. Identity on\n * continuous and while the deployment doesn't advertise a `tiles` block\n * (the block is reserved until the deep-zoom vertical ships server-side;\n * the tiling client uses its own default pyramid then — see\n * plugin-render). Snap UP, cap at the largest scale: per-tile cost is\n * constant by construction (`tileSize²` pixels regardless of level), so\n * the cap bounds artifact COUNT, not memory.\n */\nexport function snapTileScale(policy: EngineRenderPolicy, scale: number): number {\n if (policy.kind === 'continuous' || policy.tiles === undefined) return scale;\n if (policy.tiles.scales.length === 0) {\n throw new EngineError(\n EngineErrorCode.InvalidArg,\n 'render policy has an empty tile-scale lattice',\n );\n }\n const scales = [...policy.tiles.scales].sort((a, b) => a - b);\n return scales.find((s) => s >= scale) ?? scales[scales.length - 1]!;\n}\n","/**\n * Unified \"should I ask the user for a password?\" shape.\n *\n * A PDF has two distinct password moments:\n * 1. **Required** — the document is encrypted and we couldn't read\n * anything anonymously. The dev MUST gather a password and call\n * `unlock()` before any other operation succeeds.\n * 2. **Optional** — the document is open and readable, but more\n * permissions become available with the owner password. The dev\n * MAY surface an \"unlock for full access\" affordance.\n *\n * Plus the trivial third state: **none** — nothing to do.\n *\n * The discriminated union encodes the invariants:\n * - `required` and `optional` cannot both be true (a doc you can't\n * read can't be \"upgraded\" — you have to read first).\n * - `hint` is only present when meaningful (no hint in the `none` arm,\n * mandatory `owner` hint in `optional` since that's the only thing\n * an upgrade asks for).\n *\n * Computed identically on local + cloud via\n * {@link passwordPromptFromState} so the dev's UI gating code is\n * engine-agnostic.\n */\nimport type { DocumentSecurityState } from './DocumentSecurityService';\n\nexport type PasswordPrompt =\n | { readonly state: 'none' }\n | { readonly state: 'required'; readonly hint: 'user' | 'owner' | null }\n | { readonly state: 'optional'; readonly hint: 'owner' };\n\n/**\n * Derive the high-level password prompt from the raw security state.\n *\n * The function is pure and total — every `DocumentSecurityState`\n * maps to exactly one `PasswordPrompt`. Use this as the SINGLE source\n * of truth wherever the SDK exposes a \"do you need a password?\" flag:\n * both `LocalDocumentSecurityService` and `CloudDocumentSecurityService`\n * call it, so the contract can never drift.\n *\n * Mapping rules (top to bottom; first match wins):\n *\n * 1. encryption.state === 'none'\n * → { state: 'none' } the document isn't encrypted\n *\n * 2. !permissions.known\n * → { state: 'required', hint: null } we haven't probed yet; can't\n * tell user-vs-owner; show\n * generic prompt\n *\n * 3. requiresPassword && openedAs === 'none'\n * → { state: 'required', hint: 'user' } encrypted, anonymous open\n * failed → user pwd needed\n *\n * 4. openedAs === 'owner'\n * → { state: 'none' } already at the top — nothing more\n * to unlock\n *\n * 5. permissions.canUpgradeToOwner === true\n * → { state: 'optional', hint: 'owner' } open as user (or as none\n * for owner-pwd-only docs)\n * but more is available\n *\n * 6. fallthrough → { state: 'none' } open and nothing more available\n */\nexport function passwordPromptFromState(state: DocumentSecurityState): PasswordPrompt {\n if (state.encryption.state === 'none') {\n return { state: 'none' };\n }\n\n if (!state.permissions.known) {\n return { state: 'required', hint: null };\n }\n\n if (state.encryption.requiresPassword && state.permissions.openedAs === 'none') {\n return { state: 'required', hint: 'user' };\n }\n\n if (state.permissions.openedAs === 'owner') {\n return { state: 'none' };\n }\n\n if (state.permissions.canUpgradeToOwner) {\n return { state: 'optional', hint: 'owner' };\n }\n\n return { state: 'none' };\n}\n","/**\n * Wire-level packaging for messages crossing a Web Worker / Node\n * `worker_thread` boundary.\n *\n * `WirePack<P>` is the typed pair every producer hands to a transport:\n * the structured payload (validated against the wire schema) and an\n * explicit, ordered list of `Transferable` objects that the producer\n * intends to move (zero-copy) to the receiver.\n *\n * Why this is a first-class type and not a transport-side argument:\n *\n * - **Producer is the only one who knows.** Whether a particular\n * `ArrayBuffer` should be moved (zero-copy, sender's reference\n * detached) or copied (structured-cloned, both sides retain\n * access) is a semantic decision belonging to the code that\n * produced the buffer. Inferring it later — by walking the\n * payload looking for `ArrayBuffer` instances and assuming they\n * should all be transferred — is the v2 footgun this design\n * replaces.\n *\n * - **Type-checked at the boundary.** A handler returning\n * `WirePack<RenderResult>` cannot forget to declare its\n * transfer list; the producer literally writes\n * `wirePack({ ..., pixels }, [pixels.buffer])` in a single\n * return statement. Forgetting yields a structurally identical\n * copy (slow but correct), never a silent leak.\n *\n * - **Symmetric across the boundary.** Same `WirePack<P>` on the\n * request path (main → worker) and the response path (worker →\n * main). Inline transports treat `transfer` as a no-op since\n * there's no thread boundary to cross.\n *\n * - **Schema-orthogonal.** Zod schemas validate `payload` and\n * nothing else. `transfer` is not data; it's a memory-ownership\n * manifest, and the design keeps those concerns separate.\n *\n * The receiver of a `postMessage(payload, { transfer })` call gets\n * the payload back **with the listed buffers already moved into it**\n * — there's no second argument on the receiving end. So `WirePack`\n * only ever travels the producer-to-transport leg of the trip; the\n * consumer side just sees the unwrapped `payload`.\n */\nexport interface WirePack<P> {\n readonly payload: P;\n readonly transfer: readonly Transferable[];\n}\n\n/**\n * Singleton empty transfer list. Frozen so accidental in-place\n * mutation throws in strict mode rather than silently mutating the\n * default for future calls.\n */\nexport const EMPTY_TRANSFER: readonly Transferable[] = Object.freeze([]);\n\n/**\n * Producer-side helper: pair a typed payload with its transfer list.\n *\n * The `transfer` argument is optional; omitting it (or passing `[]`)\n * is the explicit, type-checked way to say \"I declare this message\n * carries no transferables.\" That's structurally identical to the\n * shape the v2 walker generated for non-binary messages, but here\n * the declaration is at construction time, in the producer's own\n * code, where the knowledge actually lives.\n *\n * wirePack({ kind: 'metadata.read', jobId, docId })\n * wirePack({ kind: 'open.fatMem', jobId, docId, bytes }, [bytes])\n * wirePack({ tag: 'page.render', pixels }, [pixels.buffer])\n */\nexport function wirePack<P>(\n payload: P,\n transfer: readonly Transferable[] = EMPTY_TRANSFER,\n): WirePack<P> {\n return { payload, transfer };\n}\n"],"mappings":"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"}