@embedpdf/plugin-form 3.0.0-next.11 → 3.0.0-next.13

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.
@@ -1 +1 @@
1
- {"version":3,"file":"scripting.js","names":[],"sources":["../src/scripting.ts"],"sourcesContent":["import type {\n DocumentHandle,\n FormEffect,\n FormEffectsResult,\n FormFieldDTO,\n FormFieldRef,\n FormFieldValue,\n FormSnapshot,\n PdfActionTree,\n} from '@embedpdf/engine-core/runtime';\nimport {\n createScriptHost,\n DEFAULT_SCRIPT_BUDGET,\n javaScriptProgramFromActionTree,\n resolveScriptIdentity,\n scriptFieldsFromSnapshot,\n seedFrom,\n type ScriptBudget,\n type ScriptDiagnostic,\n type ScriptExecutionError,\n type ScriptFieldInput,\n type ScriptOutput,\n type ScriptTransaction,\n type ScriptUiEffect,\n type ScriptValue,\n type ScriptWorldInput,\n} from '@embedpdf/core-acrojs';\nimport type { DocumentMeta } from '@embedpdf/core';\n\nimport type { FormCommitResult, FormScriptingOptions, FormUiEffect } from './types';\n\ninterface Overlay {\n original: ScriptFieldInput[];\n fields: ScriptFieldInput[];\n resetKeys: Set<string>;\n appearances: Map<string, { ref: FormFieldRef; text: string }>;\n}\n\nexport type FormScriptingControllerOptions = {\n doc: DocumentHandle;\n document(): DocumentMeta | null;\n} & (\n | {\n /** The shared realm's transaction port ({@link ScriptTransaction}) —\n * the actions plugin's per-document host. The K/V/C/F pipeline runs\n * WHOLLY inside one transaction: snapshot fetch, every pass, and the\n * engine commit — the commit-inside-the-boundary law. */\n transaction<T>(body: (txn: ScriptTransaction) => Promise<T>): Promise<T>;\n budget?: ScriptBudget;\n }\n | {\n /** Standalone convenience (stamp's detached documents, direct tests):\n * the controller builds and OWNS its own realm via\n * {@link createFormScriptingHost}; `dispose()` releases it. */\n config: FormScriptingOptions;\n }\n);\n\n/**\n * A STANDALONE realm for one document, configured with the legacy\n * `FormScriptingOptions` vocabulary — stamp's detached stamp-asset documents\n * and direct controller tests construct their own host here; viewer\n * documents get theirs from `actionsPlugin({ javascript })`.\n */\nexport function createFormScriptingHost(options: {\n doc: DocumentHandle;\n document(): DocumentMeta | null;\n config: FormScriptingOptions;\n}): {\n transaction<T>(body: (txn: ScriptTransaction) => Promise<T>): Promise<T>;\n dispose(): void;\n} {\n const { doc, config } = options;\n const host = createScriptHost({\n sandboxFactory:\n config.sandboxFactory ??\n (() =>\n import('@embedpdf/core-js-sandbox').then(({ createQuickJsSandbox }) =>\n createQuickJsSandbox(),\n )),\n document: () => {\n const meta = options.document();\n return {\n id: doc.id,\n fileName: config.fileName?.() ?? meta?.name ?? doc.id,\n pageCount: meta?.pageCount ?? 0,\n pageNumber: 0,\n };\n },\n identity: () => resolveScriptIdentity(doc, config.identity),\n environment: (sequence) => {\n const nowMs = config.now?.() ?? Date.now();\n return {\n nowMs,\n utcOffsetMinutes: config.utcOffsetMinutes?.() ?? -new Date(nowMs).getTimezoneOffset(),\n randomSeed: config.randomSeed?.() ?? seedFrom(doc.id, sequence),\n };\n },\n bootSources: async () => {\n const actions = doc.actions ? await doc.actions.read() : null;\n return (\n actions?.nameTreeScripts.map(({ action }) => javaScriptProgramFromActionTree(action)) ?? []\n );\n },\n ...(config.budget ? { budget: config.budget } : {}),\n });\n return { transaction: host.transaction.bind(host), dispose: () => host.dispose() };\n}\n\nconst refKey = (ref: FormFieldRef): string =>\n ref.kind === 'objectNumber' ? `obj:${ref.fieldObjectNumber}` : `fqn:${ref.name}`;\n\nconst sameRef = (left: FormFieldRef, right: FormFieldRef): boolean =>\n refKey(left) === refKey(right);\n\nconst cloneValue = (value: ScriptValue): ScriptValue => (Array.isArray(value) ? [...value] : value);\n\nconst sameValue = (left: ScriptValue, right: ScriptValue): boolean =>\n Array.isArray(left) && Array.isArray(right)\n ? left.length === right.length && left.every((value, index) => value === right[index])\n : left === right;\n\nfunction cloneFields(fields: ScriptFieldInput[]): ScriptFieldInput[] {\n return fields.map((field) => ({\n ...field,\n ref: { ...field.ref },\n value: cloneValue(field.value),\n defaultValue: cloneValue(field.defaultValue),\n ...(field.options ? { options: field.options.map((option) => ({ ...option })) } : {}),\n }));\n}\n\nfunction fieldByRef<T extends { ref: FormFieldRef }>(\n fields: T[],\n ref: FormFieldRef,\n): T | undefined {\n return fields.find((field) => sameRef(field.ref, ref));\n}\n\nfunction snapshotField(snapshot: FormSnapshot, ref: FormFieldRef): FormFieldDTO | undefined {\n return snapshot.fields.find((field) => sameRef(field.ref, ref));\n}\n\nfunction scriptValueFromFormValue(field: ScriptFieldInput, value: FormFieldValue): ScriptValue {\n if (field.family === 'text' && value.type === 'text') return value.value;\n if ((field.family === 'checkbox' || field.family === 'radio') && value.type === 'toggle') {\n return value.state ?? 'Off';\n }\n if (field.family === 'combobox' && value.type === 'choice') return value.values[0] ?? '';\n if (field.family === 'listbox' && value.type === 'choice') return [...value.values];\n throw new Error(`Form value type '${value.type}' does not match field family '${field.family}'`);\n}\n\nfunction formValueFromScriptValue(field: ScriptFieldInput): FormFieldValue | null {\n switch (field.family) {\n case 'text':\n return { type: 'text', value: String(field.value ?? '') };\n case 'checkbox':\n case 'radio':\n return {\n type: 'toggle',\n state:\n field.value === null || field.value === undefined || String(field.value) === 'Off'\n ? null\n : String(field.value),\n };\n case 'combobox':\n return {\n type: 'choice',\n values: Array.isArray(field.value) ? field.value.slice(0, 1) : [String(field.value ?? '')],\n };\n case 'listbox':\n return {\n type: 'choice',\n values: Array.isArray(field.value) ? [...field.value] : [String(field.value ?? '')],\n };\n default:\n return null;\n }\n}\n\nfunction applyEffects(overlay: Overlay, effects: FormEffect[]): void {\n for (const effect of effects) {\n if (effect.kind === 'reset') {\n for (const ref of effect.refs) {\n const field = fieldByRef(overlay.fields, ref);\n if (!field) continue;\n field.value = cloneValue(field.defaultValue);\n overlay.resetKeys.add(refKey(ref));\n }\n continue;\n }\n\n const field = fieldByRef(overlay.fields, effect.ref);\n if (!field) continue;\n const key = refKey(effect.ref);\n if (effect.kind === 'setValue') {\n field.value = scriptValueFromFormValue(field, effect.value);\n overlay.resetKeys.delete(key);\n } else if (effect.kind === 'setDisplay') {\n field.display = effect.display;\n } else {\n overlay.appearances.set(key, { ref: effect.ref, text: effect.text });\n }\n }\n}\n\nfunction canonicalEffects(overlay: Overlay): FormEffect[] {\n const effects: FormEffect[] = [];\n const resets: FormFieldRef[] = [];\n for (const field of overlay.fields) {\n const original = fieldByRef(overlay.original, field.ref);\n if (!original) continue;\n if (!sameValue(original.value, field.value)) {\n const key = refKey(field.ref);\n if (overlay.resetKeys.has(key) && sameValue(field.value, field.defaultValue)) {\n resets.push(field.ref);\n } else {\n const value = formValueFromScriptValue(field);\n if (value) effects.push({ kind: 'setValue', ref: field.ref, value });\n }\n }\n if (original.display !== field.display) {\n effects.push({ kind: 'setDisplay', ref: field.ref, display: field.display });\n }\n }\n if (resets.length > 0) effects.unshift({ kind: 'reset', refs: resets });\n effects.push(\n ...Array.from(overlay.appearances.values(), ({ ref, text }) => ({\n kind: 'setAppearanceText' as const,\n ref,\n text,\n })),\n );\n return effects;\n}\n\nfunction scriptError(message: string): ScriptExecutionError {\n return { kind: 'invalid-output', message };\n}\n\nfunction utf8Length(value: string): number {\n let length = 0;\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n if (code < 0x80) length += 1;\n else if (code < 0x800) length += 2;\n else if (code >= 0xd800 && code <= 0xdbff && index + 1 < value.length) {\n length += 4;\n index += 1;\n } else length += 3;\n }\n return length;\n}\n\nfunction pageNumberFor(meta: DocumentMeta | null, field: FormFieldDTO): number {\n const pon = field.widgets.find((widget) => widget.pageObjectNumber > 0)?.pageObjectNumber;\n if (!meta || pon === undefined) return 0;\n const index = meta.pages.findIndex((page) => page.pageObjectNumber === pon);\n return Math.max(0, index);\n}\n\nfunction statusFromEffects(result: FormEffectsResult): FormCommitResult['status'] {\n if (result.results.some(({ status }) => status === 'failed' || status === 'skipped')) {\n return 'failed';\n }\n if (result.results.some(({ status }) => status === 'rejected')) return 'failed';\n return result.results.some(({ status }) => status === 'applied') ? 'applied' : 'unchanged';\n}\n\nexport class FormScriptingController {\n private disposed = false;\n private readonly transaction: <T>(body: (txn: ScriptTransaction) => Promise<T>) => Promise<T>;\n private readonly transactionBudget: ScriptBudget | undefined;\n private readonly ownedHost: { dispose(): void } | null;\n\n constructor(private readonly options: FormScriptingControllerOptions) {\n if ('transaction' in options) {\n this.transaction = options.transaction.bind(options);\n this.transactionBudget = options.budget;\n this.ownedHost = null;\n } else {\n const host = createFormScriptingHost({\n doc: options.doc,\n document: () => options.document(),\n config: options.config,\n });\n this.transaction = host.transaction;\n this.transactionBudget = options.config.budget;\n this.ownedHost = host;\n }\n }\n\n /** A shared realm belongs to its HOST (the actions plugin) — this only\n * fences further transactions; a standalone-owned realm is released. */\n dispose(): void {\n this.disposed = true;\n this.ownedHost?.dispose();\n }\n\n async commit(ref: FormFieldRef, proposed: FormFieldValue): Promise<FormCommitResult> {\n return this.transact(ref, proposed);\n }\n\n /** Execute one originating widget's activation action (`/A`, including `/Next`). */\n async activate(ref: FormFieldRef, action: PdfActionTree): Promise<FormCommitResult> {\n return this.transact(ref, undefined, action);\n }\n\n /** Run lazy document boot plus the `/CO` chain without a user field change.\n * The anchor field resolves INSIDE the transaction (undefined ref). */\n async recalculate(): Promise<FormCommitResult> {\n return this.transact(undefined);\n }\n\n\n private async transact(\n refInput: FormFieldRef | undefined,\n proposed?: FormFieldValue,\n activation?: PdfActionTree,\n ): Promise<FormCommitResult> {\n if (this.disposed) throw new Error('Form scripting controller is disposed');\n // EVERYTHING inside the realm transaction — snapshot fetch, every pass,\n // the engine commit — so the next transaction reads post-commit truth.\n return this.transaction((txn) => this.transactBody(txn, refInput, proposed, activation));\n }\n\n private async transactBody(\n txn: ScriptTransaction,\n refInput: FormFieldRef | undefined,\n proposed?: FormFieldValue,\n activation?: PdfActionTree,\n ): Promise<FormCommitResult> {\n const snapshot = await this.options.doc.forms.list();\n // Recalculate's anchor resolves HERE (first live /CO field, else the\n // first field); a zero-field document is an honest no-op.\n const ref =\n refInput ??\n snapshot.calculationOrder.find(\n (candidate): candidate is FormFieldRef =>\n candidate !== null && snapshotField(snapshot, candidate) !== undefined,\n ) ??\n snapshot.fields[0]?.ref;\n if (!ref) {\n return {\n status: 'unchanged',\n scripted: true,\n effectsResult: null,\n uiEffects: [],\n diagnostics: [],\n };\n }\n const target = snapshotField(snapshot, ref);\n if (!target) {\n return this.failed([], [], scriptError(`Form field '${refKey(ref)}' no longer exists`));\n }\n\n const original = scriptFieldsFromSnapshot(snapshot);\n const overlay: Overlay = {\n original,\n fields: cloneFields(original),\n resetKeys: new Set(),\n appearances: new Map(),\n };\n const targetInput = fieldByRef(overlay.fields, ref);\n if (!targetInput) {\n return this.failed([], [], scriptError(`Form field '${refKey(ref)}' has no script view`));\n }\n\n const hasProposedValue = proposed !== undefined;\n let proposedValue: ScriptValue = cloneValue(targetInput.value);\n if (proposed) {\n try {\n proposedValue = scriptValueFromFormValue(targetInput, proposed);\n } catch (error) {\n return this.failed(\n [],\n [],\n scriptError(error instanceof Error ? error.message : String(error)),\n );\n }\n }\n\n const uiEffects: FormUiEffect[] = [];\n const diagnostics: ScriptDiagnostic[] = [];\n const meta = this.options.document();\n // Document/identity/environment are HOST-owned now; the world carries the\n // per-field current page for `this.pageNum`.\n const pageNumber = pageNumberFor(meta, target);\n const budget = this.transactionBudget ?? DEFAULT_SCRIPT_BUDGET;\n let executionMs = 0;\n let aggregateOutputSize = 0;\n\n const consume = (\n output: ScriptOutput,\n phase: FormUiEffect['phase'],\n ): ScriptExecutionError | null => {\n aggregateOutputSize += utf8Length(JSON.stringify(output));\n // Tag every UI request with WHO asked — embedders suppress boot-phase\n // nags (Adobe's version-check alert) but show user-phase validation.\n uiEffects.push(...output.uiEffects.map((effect) => ({ ...effect, phase })));\n diagnostics.push(...output.diagnostics);\n if (aggregateOutputSize > budget.maxOutputBytes) {\n return {\n kind: 'budget',\n message: `Form script transaction output exceeded ${budget.maxOutputBytes} bytes`,\n };\n }\n return output.error ?? null;\n };\n const remainingBudget = (): ScriptBudget | null => {\n const remaining = budget.maxExecutionMs - executionMs;\n return remaining <= 0 ? null : { ...budget, maxExecutionMs: remaining };\n };\n const world = (fields: ScriptFieldInput[], event: ScriptWorldInput['event']): ScriptWorldInput => ({\n fields,\n pageNumber,\n event,\n });\n const run = async (\n source: string,\n input: ScriptWorldInput,\n ): Promise<{ output?: ScriptOutput; error?: ScriptExecutionError }> => {\n const remaining = remainingBudget();\n if (!remaining) {\n return { error: { kind: 'budget', message: 'Form script transaction timed out' } };\n }\n let output: ScriptOutput;\n try {\n const startedAt = Date.now();\n output = await txn.run(source, input, remaining);\n executionMs += Date.now() - startedAt;\n } catch (error) {\n return {\n error: scriptError(error instanceof Error ? error.message : String(error)),\n };\n }\n const error = consume(output, 'user');\n return error ? { error } : { output };\n };\n\n {\n // Boot runs ONCE per realm (host-owned latch) and can only ever\n // degrade, never brick — a failure surfaces a diagnostic and this\n // transaction continues from pristine state.\n let boot: ScriptOutput | null = null;\n try {\n boot = await txn.boot(world(overlay.fields, { kind: 'name-tree-boot' }), budget);\n } catch (error) {\n diagnostics.push({\n code: 'script-error',\n message: `Document boot script failed (continuing without it): ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n if (boot) {\n const bootError = consume(boot, 'boot');\n if (!bootError) applyEffects(overlay, boot.formEffects);\n else {\n diagnostics.push({\n code: 'script-error',\n message: `Document boot script failed (continuing without it): ${bootError.message}`,\n });\n }\n }\n }\n\n const bootEffects = canonicalEffects(overlay);\n\n if (activation) {\n let program: string;\n try {\n program = javaScriptProgramFromActionTree(activation);\n } catch (error) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError(error instanceof Error ? error.message : String(error)),\n );\n }\n if (program) {\n const activated = await run(program, world(overlay.fields, {\n kind: 'widget-activate',\n target: ref,\n source: ref,\n value: targetInput.value,\n }));\n if (activated.error) return this.failed(uiEffects, diagnostics, activated.error);\n applyEffects(overlay, activated.output!.formEffects);\n }\n }\n\n if (!activation && hasProposedValue && target.actions?.keystroke && target.family === 'text') {\n let program: string;\n try {\n program = javaScriptProgramFromActionTree(target.actions.keystroke);\n } catch (error) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError(error instanceof Error ? error.message : String(error)),\n );\n }\n if (program) {\n // Acrobat fires per-typing keystroke events plus one final commit\n // event. This pipeline compresses typing into ONE paste-shaped\n // replacement (Acrobat's own paste contract: the whole string rides\n // `event.change`, willCommit=false) so transform/filter scripts run,\n // then fires the Acrobat-faithful willCommit pass (the full value on\n // `event.value`, empty `change`) so AF* and commit validators see\n // what Adobe's library expects.\n const oldValue = String(targetInput.value ?? '');\n const typing = await run(program, world(overlay.fields, {\n kind: 'field-keystroke',\n target: ref,\n source: ref,\n value: oldValue,\n change: String(proposedValue ?? ''),\n selStart: 0,\n selEnd: oldValue.length,\n willCommit: false,\n }));\n if (typing.error && typing.error.kind !== 'exception') {\n return this.failed(uiEffects, diagnostics, typing.error);\n }\n if (typing.error) {\n // Fault ladder: an exception is not a rejection. Keep the typed\n // value, surface a diagnostic, and let the transaction continue.\n diagnostics.push({\n code: 'script-error',\n message: `Keystroke script failed (kept the typed value): ${typing.error.message}`,\n });\n } else {\n if (!typing.output!.event.rc) {\n return this.finishRejected(bootEffects, uiEffects, diagnostics);\n }\n applyEffects(overlay, typing.output!.formEffects);\n const event = typing.output!.event;\n const base = String(event.value ?? '');\n proposedValue =\n base.slice(0, event.selStart) +\n event.change +\n base.slice(Math.max(event.selStart, event.selEnd));\n }\n const commit = await run(program, world(overlay.fields, {\n kind: 'field-keystroke',\n target: ref,\n source: ref,\n value: proposedValue,\n change: '',\n selStart: 0,\n selEnd: 0,\n willCommit: true,\n }));\n if (commit.error && commit.error.kind !== 'exception') {\n return this.failed(uiEffects, diagnostics, commit.error);\n }\n if (commit.error) {\n diagnostics.push({\n code: 'script-error',\n message: `Keystroke commit script failed (kept the typed value): ${commit.error.message}`,\n });\n } else {\n if (!commit.output!.event.rc) {\n return this.finishRejected(bootEffects, uiEffects, diagnostics);\n }\n applyEffects(overlay, commit.output!.formEffects);\n proposedValue = cloneValue(commit.output!.event.value);\n }\n }\n }\n\n if (!activation && hasProposedValue) {\n targetInput.value = cloneValue(proposedValue);\n overlay.resetKeys.delete(refKey(ref));\n }\n\n if (!activation && hasProposedValue && target.actions?.validate) {\n let program: string;\n try {\n program = javaScriptProgramFromActionTree(target.actions.validate);\n } catch (error) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError(error instanceof Error ? error.message : String(error)),\n );\n }\n if (program) {\n const validation = await run(program, world(overlay.fields, {\n kind: 'field-validate',\n target: ref,\n source: ref,\n value: proposedValue,\n willCommit: true,\n }));\n if (validation.error && validation.error.kind !== 'exception') {\n return this.failed(uiEffects, diagnostics, validation.error);\n }\n if (validation.error) {\n // Fault ladder: a broken validator must not reject the value.\n diagnostics.push({\n code: 'script-error',\n message: `Validate script failed (accepted the value): ${validation.error.message}`,\n });\n } else {\n if (!validation.output!.event.rc) {\n return this.finishRejected(bootEffects, uiEffects, diagnostics);\n }\n targetInput.value = cloneValue(validation.output!.event.value);\n applyEffects(overlay, validation.output!.formEffects);\n }\n }\n }\n\n const formatRefs: FormFieldRef[] = !activation && hasProposedValue ? [ref] : [];\n for (const calculationRef of activation ? [] : snapshot.calculationOrder) {\n if (!calculationRef) continue;\n const calculated = snapshotField(snapshot, calculationRef);\n const tree = calculated?.actions?.calculate;\n if (!calculated || !tree) continue;\n let program: string;\n try {\n program = javaScriptProgramFromActionTree(tree);\n } catch (error) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError(error instanceof Error ? error.message : String(error)),\n );\n }\n const calculatedInput = fieldByRef(overlay.fields, calculationRef);\n if (program && calculatedInput) {\n const calculation = await run(\n program,\n world(overlay.fields, {\n kind: 'field-calculate',\n target: calculationRef,\n ...(hasProposedValue ? { source: ref } : {}),\n value: calculatedInput.value,\n }),\n );\n if (calculation.error && calculation.error.kind !== 'exception') {\n return this.failed(uiEffects, diagnostics, calculation.error);\n }\n if (calculation.error) {\n // Fault ladder: this field's calculation is skipped; the /CO chain continues.\n diagnostics.push({\n code: 'script-error',\n message: `Calculate script failed (field left unchanged): ${calculation.error.message}`,\n });\n } else {\n applyEffects(overlay, calculation.output!.formEffects);\n }\n }\n if (!formatRefs.some((candidate) => sameRef(candidate, calculationRef))) {\n formatRefs.push(calculationRef);\n }\n }\n\n for (const formatRef of formatRefs) {\n const field = snapshotField(snapshot, formatRef);\n const tree = field?.actions?.format;\n const overlayField = fieldByRef(overlay.fields, formatRef);\n if (!field || !tree || !overlayField) continue;\n let program: string;\n try {\n program = javaScriptProgramFromActionTree(tree);\n } catch (error) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError(error instanceof Error ? error.message : String(error)),\n );\n }\n if (!program) continue;\n const format = await run(\n program,\n world(overlay.fields, {\n kind: 'field-format',\n target: formatRef,\n ...(hasProposedValue ? { source: ref } : {}),\n value: overlayField.value,\n }),\n );\n if (format.error && format.error.kind !== 'exception') {\n return this.failed(uiEffects, diagnostics, format.error);\n }\n if (format.error) {\n // Fault ladder: formatting is cosmetic — skip it, keep the raw value.\n diagnostics.push({\n code: 'script-error',\n message: `Format script failed (kept the raw value): ${format.error.message}`,\n });\n continue;\n }\n applyEffects(overlay, format.output!.formEffects);\n }\n\n const effects = canonicalEffects(overlay);\n if (effects.length + uiEffects.length > budget.maxEffects) {\n return this.failed(uiEffects, diagnostics, {\n kind: 'budget',\n message: `Form script transaction exceeded ${budget.maxEffects} effects`,\n });\n }\n if (effects.length === 0) {\n return { status: 'unchanged', scripted: true, effectsResult: null, uiEffects, diagnostics };\n }\n if (!this.options.doc.forms.applyEffects) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError('This engine does not support batched form effects'),\n );\n }\n\n const effectsResult = await this.options.doc.forms.applyEffects(effects);\n const status = statusFromEffects(effectsResult);\n return {\n status,\n scripted: true,\n effectsResult,\n uiEffects,\n diagnostics,\n ...(status === 'failed'\n ? { error: scriptError('One or more native form effects failed') }\n : {}),\n };\n }\n\n private failed(\n uiEffects: FormUiEffect[],\n diagnostics: ScriptDiagnostic[],\n error: ScriptExecutionError,\n ): FormCommitResult {\n return {\n status: 'failed',\n scripted: true,\n effectsResult: null,\n uiEffects,\n diagnostics,\n error,\n };\n }\n\n private async finishRejected(\n bootEffects: FormEffect[],\n uiEffects: FormUiEffect[],\n diagnostics: ScriptDiagnostic[],\n ): Promise<FormCommitResult> {\n if (bootEffects.length === 0) {\n return {\n status: 'rejected',\n scripted: true,\n effectsResult: null,\n uiEffects,\n diagnostics,\n };\n }\n if (!this.options.doc.forms.applyEffects) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError('Batched form effects are unavailable'),\n );\n }\n const effectsResult = await this.options.doc.forms.applyEffects(bootEffects);\n if (statusFromEffects(effectsResult) === 'failed') {\n return this.failed(uiEffects, diagnostics, scriptError('A name-tree boot effect failed'));\n }\n return {\n status: 'rejected',\n scripted: true,\n effectsResult,\n uiEffects,\n diagnostics,\n };\n }\n}\n\nexport function createFormScriptingController(\n options: FormScriptingControllerOptions,\n): FormScriptingController {\n return new FormScriptingController(options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"scripting.js","names":[],"sources":["../src/scripting.ts"],"sourcesContent":["import type {\n DocumentHandle,\n FormEffect,\n FormEffectsResult,\n FormFieldDTO,\n FormFieldRef,\n FormFieldValue,\n FormSnapshot,\n PdfActionTree,\n} from '@embedpdf/engine-core/runtime';\nimport {\n DEFAULT_SCRIPT_BUDGET,\n javaScriptProgramFromActionTree,\n scriptFieldsFromSnapshot,\n type ScriptBudget,\n type ScriptDiagnostic,\n type ScriptExecutionError,\n type ScriptFieldInput,\n type ScriptOutput,\n type ScriptTransaction,\n type ScriptUiEffect,\n type ScriptValue,\n type ScriptWorldInput,\n} from '@embedpdf/core-acrojs';\nimport type { DocumentMeta } from '@embedpdf/core';\n\nimport type { FormCommitResult, FormUiEffect } from './types';\n\ninterface Overlay {\n original: ScriptFieldInput[];\n fields: ScriptFieldInput[];\n resetKeys: Set<string>;\n appearances: Map<string, { ref: FormFieldRef; text: string }>;\n}\n\n/**\n * The controller never owns a realm. It rides a realm PORT — this document's\n * own (the actions plugin's per-document host) or a detached one minted by\n * the actions plugin for a document that is not displayed (stamp's\n * asset documents). Either way the owner disposes the realm; `dispose()`\n * here only fences further transactions.\n */\nexport interface FormScriptingControllerOptions {\n doc: DocumentHandle;\n document(): DocumentMeta | null;\n /** The realm's transaction port ({@link ScriptTransaction}). The K/V/C/F\n * pipeline runs WHOLLY inside one transaction: snapshot fetch, every\n * pass, and the engine commit — the commit-inside-the-boundary law. */\n transaction<T>(body: (txn: ScriptTransaction) => Promise<T>): Promise<T>;\n /** The transaction aggregate (output bytes, wall-clock across passes);\n * the realm owner hands over the same budget its runs enforce. */\n budget?: ScriptBudget;\n}\n\nconst refKey = (ref: FormFieldRef): string =>\n ref.kind === 'objectNumber' ? `obj:${ref.fieldObjectNumber}` : `fqn:${ref.name}`;\n\nconst sameRef = (left: FormFieldRef, right: FormFieldRef): boolean =>\n refKey(left) === refKey(right);\n\nconst cloneValue = (value: ScriptValue): ScriptValue => (Array.isArray(value) ? [...value] : value);\n\nconst sameValue = (left: ScriptValue, right: ScriptValue): boolean =>\n Array.isArray(left) && Array.isArray(right)\n ? left.length === right.length && left.every((value, index) => value === right[index])\n : left === right;\n\nfunction cloneFields(fields: ScriptFieldInput[]): ScriptFieldInput[] {\n return fields.map((field) => ({\n ...field,\n ref: { ...field.ref },\n value: cloneValue(field.value),\n defaultValue: cloneValue(field.defaultValue),\n ...(field.options ? { options: field.options.map((option) => ({ ...option })) } : {}),\n }));\n}\n\nfunction fieldByRef<T extends { ref: FormFieldRef }>(\n fields: T[],\n ref: FormFieldRef,\n): T | undefined {\n return fields.find((field) => sameRef(field.ref, ref));\n}\n\nfunction snapshotField(snapshot: FormSnapshot, ref: FormFieldRef): FormFieldDTO | undefined {\n return snapshot.fields.find((field) => sameRef(field.ref, ref));\n}\n\nfunction scriptValueFromFormValue(field: ScriptFieldInput, value: FormFieldValue): ScriptValue {\n if (field.family === 'text' && value.type === 'text') return value.value;\n if ((field.family === 'checkbox' || field.family === 'radio') && value.type === 'toggle') {\n return value.state ?? 'Off';\n }\n if (field.family === 'combobox' && value.type === 'choice') return value.values[0] ?? '';\n if (field.family === 'listbox' && value.type === 'choice') return [...value.values];\n throw new Error(`Form value type '${value.type}' does not match field family '${field.family}'`);\n}\n\nfunction formValueFromScriptValue(field: ScriptFieldInput): FormFieldValue | null {\n switch (field.family) {\n case 'text':\n return { type: 'text', value: String(field.value ?? '') };\n case 'checkbox':\n case 'radio':\n return {\n type: 'toggle',\n state:\n field.value === null || field.value === undefined || String(field.value) === 'Off'\n ? null\n : String(field.value),\n };\n case 'combobox':\n return {\n type: 'choice',\n values: Array.isArray(field.value) ? field.value.slice(0, 1) : [String(field.value ?? '')],\n };\n case 'listbox':\n return {\n type: 'choice',\n values: Array.isArray(field.value) ? [...field.value] : [String(field.value ?? '')],\n };\n default:\n return null;\n }\n}\n\nfunction applyEffects(overlay: Overlay, effects: FormEffect[]): void {\n for (const effect of effects) {\n if (effect.kind === 'reset') {\n for (const ref of effect.refs) {\n const field = fieldByRef(overlay.fields, ref);\n if (!field) continue;\n field.value = cloneValue(field.defaultValue);\n overlay.resetKeys.add(refKey(ref));\n }\n continue;\n }\n\n const field = fieldByRef(overlay.fields, effect.ref);\n if (!field) continue;\n const key = refKey(effect.ref);\n if (effect.kind === 'setValue') {\n field.value = scriptValueFromFormValue(field, effect.value);\n overlay.resetKeys.delete(key);\n } else if (effect.kind === 'setDisplay') {\n field.display = effect.display;\n } else {\n overlay.appearances.set(key, { ref: effect.ref, text: effect.text });\n }\n }\n}\n\nfunction canonicalEffects(overlay: Overlay): FormEffect[] {\n const effects: FormEffect[] = [];\n const resets: FormFieldRef[] = [];\n for (const field of overlay.fields) {\n const original = fieldByRef(overlay.original, field.ref);\n if (!original) continue;\n if (!sameValue(original.value, field.value)) {\n const key = refKey(field.ref);\n if (overlay.resetKeys.has(key) && sameValue(field.value, field.defaultValue)) {\n resets.push(field.ref);\n } else {\n const value = formValueFromScriptValue(field);\n if (value) effects.push({ kind: 'setValue', ref: field.ref, value });\n }\n }\n if (original.display !== field.display) {\n effects.push({ kind: 'setDisplay', ref: field.ref, display: field.display });\n }\n }\n if (resets.length > 0) effects.unshift({ kind: 'reset', refs: resets });\n effects.push(\n ...Array.from(overlay.appearances.values(), ({ ref, text }) => ({\n kind: 'setAppearanceText' as const,\n ref,\n text,\n })),\n );\n return effects;\n}\n\nfunction scriptError(message: string): ScriptExecutionError {\n return { kind: 'invalid-output', message };\n}\n\nfunction utf8Length(value: string): number {\n let length = 0;\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n if (code < 0x80) length += 1;\n else if (code < 0x800) length += 2;\n else if (code >= 0xd800 && code <= 0xdbff && index + 1 < value.length) {\n length += 4;\n index += 1;\n } else length += 3;\n }\n return length;\n}\n\nfunction pageNumberFor(meta: DocumentMeta | null, field: FormFieldDTO): number {\n const pon = field.widgets.find((widget) => widget.pageObjectNumber > 0)?.pageObjectNumber;\n if (!meta || pon === undefined) return 0;\n const index = meta.pages.findIndex((page) => page.pageObjectNumber === pon);\n return Math.max(0, index);\n}\n\nfunction statusFromEffects(result: FormEffectsResult): FormCommitResult['status'] {\n if (result.results.some(({ status }) => status === 'failed' || status === 'skipped')) {\n return 'failed';\n }\n if (result.results.some(({ status }) => status === 'rejected')) return 'failed';\n return result.results.some(({ status }) => status === 'applied') ? 'applied' : 'unchanged';\n}\n\nexport class FormScriptingController {\n private disposed = false;\n private readonly transaction: <T>(body: (txn: ScriptTransaction) => Promise<T>) => Promise<T>;\n private readonly transactionBudget: ScriptBudget | undefined;\n\n constructor(private readonly options: FormScriptingControllerOptions) {\n this.transaction = options.transaction.bind(options);\n this.transactionBudget = options.budget;\n }\n\n /** The realm belongs to its OWNER (the actions plugin, or the caller that\n * minted a detached one) — this only fences further transactions. */\n dispose(): void {\n this.disposed = true;\n }\n\n async commit(ref: FormFieldRef, proposed: FormFieldValue): Promise<FormCommitResult> {\n return this.transact(ref, proposed);\n }\n\n /** Execute one originating widget's activation action (`/A`, including `/Next`). */\n async activate(ref: FormFieldRef, action: PdfActionTree): Promise<FormCommitResult> {\n return this.transact(ref, undefined, action);\n }\n\n /** Run lazy document boot plus the `/CO` chain without a user field change.\n * The anchor field resolves INSIDE the transaction (undefined ref). */\n async recalculate(): Promise<FormCommitResult> {\n return this.transact(undefined);\n }\n\n private async transact(\n refInput: FormFieldRef | undefined,\n proposed?: FormFieldValue,\n activation?: PdfActionTree,\n ): Promise<FormCommitResult> {\n if (this.disposed) throw new Error('Form scripting controller is disposed');\n // EVERYTHING inside the realm transaction — snapshot fetch, every pass,\n // the engine commit — so the next transaction reads post-commit truth.\n return this.transaction((txn) => this.transactBody(txn, refInput, proposed, activation));\n }\n\n private async transactBody(\n txn: ScriptTransaction,\n refInput: FormFieldRef | undefined,\n proposed?: FormFieldValue,\n activation?: PdfActionTree,\n ): Promise<FormCommitResult> {\n const snapshot = await this.options.doc.forms.list();\n // Recalculate's anchor resolves HERE (first live /CO field, else the\n // first field); a zero-field document is an honest no-op.\n const ref =\n refInput ??\n snapshot.calculationOrder.find(\n (candidate): candidate is FormFieldRef =>\n candidate !== null && snapshotField(snapshot, candidate) !== undefined,\n ) ??\n snapshot.fields[0]?.ref;\n if (!ref) {\n return {\n status: 'unchanged',\n scripted: true,\n effectsResult: null,\n uiEffects: [],\n diagnostics: [],\n };\n }\n const target = snapshotField(snapshot, ref);\n if (!target) {\n return this.failed([], [], scriptError(`Form field '${refKey(ref)}' no longer exists`));\n }\n\n const original = scriptFieldsFromSnapshot(snapshot);\n const overlay: Overlay = {\n original,\n fields: cloneFields(original),\n resetKeys: new Set(),\n appearances: new Map(),\n };\n const targetInput = fieldByRef(overlay.fields, ref);\n if (!targetInput) {\n return this.failed([], [], scriptError(`Form field '${refKey(ref)}' has no script view`));\n }\n\n const hasProposedValue = proposed !== undefined;\n let proposedValue: ScriptValue = cloneValue(targetInput.value);\n if (proposed) {\n try {\n proposedValue = scriptValueFromFormValue(targetInput, proposed);\n } catch (error) {\n return this.failed(\n [],\n [],\n scriptError(error instanceof Error ? error.message : String(error)),\n );\n }\n }\n\n const uiEffects: FormUiEffect[] = [];\n const diagnostics: ScriptDiagnostic[] = [];\n const meta = this.options.document();\n // Document/identity/environment are HOST-owned now; the world carries the\n // per-field current page for `this.pageNum`.\n const pageNumber = pageNumberFor(meta, target);\n const budget = this.transactionBudget ?? DEFAULT_SCRIPT_BUDGET;\n let executionMs = 0;\n let aggregateOutputSize = 0;\n\n const consume = (\n output: ScriptOutput,\n phase: FormUiEffect['phase'],\n ): ScriptExecutionError | null => {\n aggregateOutputSize += utf8Length(JSON.stringify(output));\n // Tag every UI request with WHO asked — embedders suppress boot-phase\n // nags (Adobe's version-check alert) but show user-phase validation.\n uiEffects.push(...output.uiEffects.map((effect) => ({ ...effect, phase })));\n diagnostics.push(...output.diagnostics);\n if (aggregateOutputSize > budget.maxOutputBytes) {\n return {\n kind: 'budget',\n message: `Form script transaction output exceeded ${budget.maxOutputBytes} bytes`,\n };\n }\n return output.error ?? null;\n };\n const remainingBudget = (): ScriptBudget | null => {\n const remaining = budget.maxExecutionMs - executionMs;\n return remaining <= 0 ? null : { ...budget, maxExecutionMs: remaining };\n };\n const world = (\n fields: ScriptFieldInput[],\n event: ScriptWorldInput['event'],\n ): ScriptWorldInput => ({\n fields,\n pageNumber,\n event,\n });\n const run = async (\n source: string,\n input: ScriptWorldInput,\n ): Promise<{ output?: ScriptOutput; error?: ScriptExecutionError }> => {\n const remaining = remainingBudget();\n if (!remaining) {\n return { error: { kind: 'budget', message: 'Form script transaction timed out' } };\n }\n let output: ScriptOutput;\n try {\n const startedAt = Date.now();\n output = await txn.run(source, input, remaining);\n executionMs += Date.now() - startedAt;\n } catch (error) {\n return {\n error: scriptError(error instanceof Error ? error.message : String(error)),\n };\n }\n const error = consume(output, 'user');\n return error ? { error } : { output };\n };\n\n {\n // Boot runs ONCE per realm (host-owned latch) and can only ever\n // degrade, never brick — a failure surfaces a diagnostic and this\n // transaction continues from pristine state.\n let boot: ScriptOutput | null = null;\n try {\n boot = await txn.boot(world(overlay.fields, { kind: 'name-tree-boot' }), budget);\n } catch (error) {\n diagnostics.push({\n code: 'script-error',\n message: `Document boot script failed (continuing without it): ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n if (boot) {\n const bootError = consume(boot, 'boot');\n if (!bootError) applyEffects(overlay, boot.formEffects);\n else {\n diagnostics.push({\n code: 'script-error',\n message: `Document boot script failed (continuing without it): ${bootError.message}`,\n });\n }\n }\n }\n\n const bootEffects = canonicalEffects(overlay);\n\n if (activation) {\n let program: string;\n try {\n program = javaScriptProgramFromActionTree(activation);\n } catch (error) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError(error instanceof Error ? error.message : String(error)),\n );\n }\n if (program) {\n const activated = await run(\n program,\n world(overlay.fields, {\n kind: 'widget-activate',\n target: ref,\n source: ref,\n value: targetInput.value,\n }),\n );\n if (activated.error) return this.failed(uiEffects, diagnostics, activated.error);\n applyEffects(overlay, activated.output!.formEffects);\n }\n }\n\n if (!activation && hasProposedValue && target.actions?.keystroke && target.family === 'text') {\n let program: string;\n try {\n program = javaScriptProgramFromActionTree(target.actions.keystroke);\n } catch (error) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError(error instanceof Error ? error.message : String(error)),\n );\n }\n if (program) {\n // Acrobat fires per-typing keystroke events plus one final commit\n // event. This pipeline compresses typing into ONE paste-shaped\n // replacement (Acrobat's own paste contract: the whole string rides\n // `event.change`, willCommit=false) so transform/filter scripts run,\n // then fires the Acrobat-faithful willCommit pass (the full value on\n // `event.value`, empty `change`) so AF* and commit validators see\n // what Adobe's library expects.\n const oldValue = String(targetInput.value ?? '');\n const typing = await run(\n program,\n world(overlay.fields, {\n kind: 'field-keystroke',\n target: ref,\n source: ref,\n value: oldValue,\n change: String(proposedValue ?? ''),\n selStart: 0,\n selEnd: oldValue.length,\n willCommit: false,\n }),\n );\n if (typing.error && typing.error.kind !== 'exception') {\n return this.failed(uiEffects, diagnostics, typing.error);\n }\n if (typing.error) {\n // Fault ladder: an exception is not a rejection. Keep the typed\n // value, surface a diagnostic, and let the transaction continue.\n diagnostics.push({\n code: 'script-error',\n message: `Keystroke script failed (kept the typed value): ${typing.error.message}`,\n });\n } else {\n if (!typing.output!.event.rc) {\n return this.finishRejected(bootEffects, uiEffects, diagnostics);\n }\n applyEffects(overlay, typing.output!.formEffects);\n const event = typing.output!.event;\n const base = String(event.value ?? '');\n proposedValue =\n base.slice(0, event.selStart) +\n event.change +\n base.slice(Math.max(event.selStart, event.selEnd));\n }\n const commit = await run(\n program,\n world(overlay.fields, {\n kind: 'field-keystroke',\n target: ref,\n source: ref,\n value: proposedValue,\n change: '',\n selStart: 0,\n selEnd: 0,\n willCommit: true,\n }),\n );\n if (commit.error && commit.error.kind !== 'exception') {\n return this.failed(uiEffects, diagnostics, commit.error);\n }\n if (commit.error) {\n diagnostics.push({\n code: 'script-error',\n message: `Keystroke commit script failed (kept the typed value): ${commit.error.message}`,\n });\n } else {\n if (!commit.output!.event.rc) {\n return this.finishRejected(bootEffects, uiEffects, diagnostics);\n }\n applyEffects(overlay, commit.output!.formEffects);\n proposedValue = cloneValue(commit.output!.event.value);\n }\n }\n }\n\n if (!activation && hasProposedValue) {\n targetInput.value = cloneValue(proposedValue);\n overlay.resetKeys.delete(refKey(ref));\n }\n\n if (!activation && hasProposedValue && target.actions?.validate) {\n let program: string;\n try {\n program = javaScriptProgramFromActionTree(target.actions.validate);\n } catch (error) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError(error instanceof Error ? error.message : String(error)),\n );\n }\n if (program) {\n const validation = await run(\n program,\n world(overlay.fields, {\n kind: 'field-validate',\n target: ref,\n source: ref,\n value: proposedValue,\n willCommit: true,\n }),\n );\n if (validation.error && validation.error.kind !== 'exception') {\n return this.failed(uiEffects, diagnostics, validation.error);\n }\n if (validation.error) {\n // Fault ladder: a broken validator must not reject the value.\n diagnostics.push({\n code: 'script-error',\n message: `Validate script failed (accepted the value): ${validation.error.message}`,\n });\n } else {\n if (!validation.output!.event.rc) {\n return this.finishRejected(bootEffects, uiEffects, diagnostics);\n }\n targetInput.value = cloneValue(validation.output!.event.value);\n applyEffects(overlay, validation.output!.formEffects);\n }\n }\n }\n\n const formatRefs: FormFieldRef[] = !activation && hasProposedValue ? [ref] : [];\n for (const calculationRef of activation ? [] : snapshot.calculationOrder) {\n if (!calculationRef) continue;\n const calculated = snapshotField(snapshot, calculationRef);\n const tree = calculated?.actions?.calculate;\n if (!calculated || !tree) continue;\n let program: string;\n try {\n program = javaScriptProgramFromActionTree(tree);\n } catch (error) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError(error instanceof Error ? error.message : String(error)),\n );\n }\n const calculatedInput = fieldByRef(overlay.fields, calculationRef);\n if (program && calculatedInput) {\n const calculation = await run(\n program,\n world(overlay.fields, {\n kind: 'field-calculate',\n target: calculationRef,\n ...(hasProposedValue ? { source: ref } : {}),\n value: calculatedInput.value,\n }),\n );\n if (calculation.error && calculation.error.kind !== 'exception') {\n return this.failed(uiEffects, diagnostics, calculation.error);\n }\n if (calculation.error) {\n // Fault ladder: this field's calculation is skipped; the /CO chain continues.\n diagnostics.push({\n code: 'script-error',\n message: `Calculate script failed (field left unchanged): ${calculation.error.message}`,\n });\n } else {\n applyEffects(overlay, calculation.output!.formEffects);\n }\n }\n if (!formatRefs.some((candidate) => sameRef(candidate, calculationRef))) {\n formatRefs.push(calculationRef);\n }\n }\n\n for (const formatRef of formatRefs) {\n const field = snapshotField(snapshot, formatRef);\n const tree = field?.actions?.format;\n const overlayField = fieldByRef(overlay.fields, formatRef);\n if (!field || !tree || !overlayField) continue;\n let program: string;\n try {\n program = javaScriptProgramFromActionTree(tree);\n } catch (error) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError(error instanceof Error ? error.message : String(error)),\n );\n }\n if (!program) continue;\n const format = await run(\n program,\n world(overlay.fields, {\n kind: 'field-format',\n target: formatRef,\n ...(hasProposedValue ? { source: ref } : {}),\n value: overlayField.value,\n }),\n );\n if (format.error && format.error.kind !== 'exception') {\n return this.failed(uiEffects, diagnostics, format.error);\n }\n if (format.error) {\n // Fault ladder: formatting is cosmetic — skip it, keep the raw value.\n diagnostics.push({\n code: 'script-error',\n message: `Format script failed (kept the raw value): ${format.error.message}`,\n });\n continue;\n }\n applyEffects(overlay, format.output!.formEffects);\n }\n\n const effects = canonicalEffects(overlay);\n if (effects.length + uiEffects.length > budget.maxEffects) {\n return this.failed(uiEffects, diagnostics, {\n kind: 'budget',\n message: `Form script transaction exceeded ${budget.maxEffects} effects`,\n });\n }\n if (effects.length === 0) {\n return { status: 'unchanged', scripted: true, effectsResult: null, uiEffects, diagnostics };\n }\n if (!this.options.doc.forms.applyEffects) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError('This engine does not support batched form effects'),\n );\n }\n\n const effectsResult = await this.options.doc.forms.applyEffects(effects);\n const status = statusFromEffects(effectsResult);\n return {\n status,\n scripted: true,\n effectsResult,\n uiEffects,\n diagnostics,\n ...(status === 'failed'\n ? { error: scriptError('One or more native form effects failed') }\n : {}),\n };\n }\n\n private failed(\n uiEffects: FormUiEffect[],\n diagnostics: ScriptDiagnostic[],\n error: ScriptExecutionError,\n ): FormCommitResult {\n return {\n status: 'failed',\n scripted: true,\n effectsResult: null,\n uiEffects,\n diagnostics,\n error,\n };\n }\n\n private async finishRejected(\n bootEffects: FormEffect[],\n uiEffects: FormUiEffect[],\n diagnostics: ScriptDiagnostic[],\n ): Promise<FormCommitResult> {\n if (bootEffects.length === 0) {\n return {\n status: 'rejected',\n scripted: true,\n effectsResult: null,\n uiEffects,\n diagnostics,\n };\n }\n if (!this.options.doc.forms.applyEffects) {\n return this.failed(\n uiEffects,\n diagnostics,\n scriptError('Batched form effects are unavailable'),\n );\n }\n const effectsResult = await this.options.doc.forms.applyEffects(bootEffects);\n if (statusFromEffects(effectsResult) === 'failed') {\n return this.failed(uiEffects, diagnostics, scriptError('A name-tree boot effect failed'));\n }\n return {\n status: 'rejected',\n scripted: true,\n effectsResult,\n uiEffects,\n diagnostics,\n };\n }\n}\n\nexport function createFormScriptingController(\n options: FormScriptingControllerOptions,\n): FormScriptingController {\n return new FormScriptingController(options);\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { AnnotationRef, FormDataExport, FormDataFormat, FormEffect, FormEffectsResult, FormFieldDTO, FormFieldFamily, FormFieldOption, FormFieldPatch, FormFieldRef, FormFieldValue, FormImportResult, FormRepairOptions, FormRepairResult, FormSetValueResult, FormSnapshot, PdfActionTargetRef, WidgetAppearance } from "@embedpdf/engine-core/runtime";
2
2
  import { ActionContext, ActionDiagnostic, ActionOrigin, ActionSubmitRequest, ActionTriggerResult, PdfAnnotationEventKind, SubmitIntent } from "@embedpdf/plugin-actions/contract";
3
- import { ScriptBudget, ScriptDiagnostic, ScriptExecutionError, ScriptIdentity, ScriptUiEffect } from "@embedpdf/core-acrojs";
4
- import { ScriptSandboxFactory } from "@embedpdf/core-js-sandbox";
3
+ import { ScriptDiagnostic, ScriptExecutionError, ScriptUiEffect } from "@embedpdf/core-acrojs";
4
+ import { AnnotationPropsPatch, ClickCreate } from "@embedpdf/plugin-annotation/contract";
5
5
  //#region src/core/model.d.ts
6
6
  /** A content-space box (top-left origin, y-down, PDF points). */
7
7
  interface Box {
@@ -35,6 +35,13 @@ interface Model {
35
35
  /** Bumped on every change — cheap equality for memoized projections. */
36
36
  seq: number;
37
37
  }
38
+ /** A widget hit: the annotation under the point and the field it belongs to. */
39
+ interface WidgetHit {
40
+ annotObjectNumber: number;
41
+ field: FormFieldDTO;
42
+ /** The widget's content-space box. */
43
+ box: Box;
44
+ }
38
45
  //#endregion
39
46
  //#region src/core/fill-items.d.ts
40
47
  interface FillItemBase {
@@ -68,31 +75,44 @@ type FillItem = FillItemBase & ({
68
75
  selected: string[];
69
76
  } | {
70
77
  control: 'button';
78
+ } |
79
+ /**
80
+ * A signature field's widget. `signed` = it carries a `/V` (a signature
81
+ * dictionary): the field is final and its appearance sealed — the
82
+ * control inspects rather than signs. Unsigned: "sign here" — the
83
+ * signing act itself belongs to the signature plugin.
84
+ */
85
+ {
86
+ control: 'signature';
87
+ signed: boolean;
71
88
  });
72
89
  //#endregion
90
+ //#region src/tools.d.ts
91
+ /** The families the palette can author (push buttons are not authorable). */
92
+ type AuthorableFormFamily = Exclude<FormFieldFamily, 'pushbutton' | 'unknown'>;
93
+ interface FormToolDef {
94
+ id: string;
95
+ /** The field family `placeField` commits (the FORM plane's vocabulary). */
96
+ family: AuthorableFormFamily;
97
+ /** The client kind the ANNOTATION registry routes on (props panel).
98
+ * Not a PDF subtype — every widget is PDF `widget`; this is the view. */
99
+ visualKind: 'widget-text' | 'widget-choice' | 'widget-toggle' | 'widget-box';
100
+ /** What a bare click places (box policies only — fields are boxes). */
101
+ clickCreate: Extract<ClickCreate, {
102
+ width: number;
103
+ }>;
104
+ /** Seed drawing defaults: a placed field is VISIBLE (white box, gray
105
+ * border) and restylable per tool through the shared style panel. */
106
+ defaults: AnnotationPropsPatch;
107
+ cursor: string;
108
+ }
109
+ declare const FORM_TOOLS: readonly FormToolDef[];
110
+ declare const FORM_TOOL_BY_ID: ReadonlyMap<string, FormToolDef>;
111
+ //#endregion
73
112
  //#region src/types.d.ts
74
113
  interface FormState {
75
114
  model: Model;
76
115
  }
77
- /**
78
- * Standalone-realm configuration for `createFormScriptingHost` — stamp's
79
- * detached documents and direct controller tests. Viewer documents configure
80
- * scripting on `actionsPlugin({ javascript })` instead (D8), and every UI
81
- * effect/diagnostic surfaces through the actions port (D9).
82
- */
83
- interface FormScriptingOptions {
84
- /** Override the lazy QuickJS factory (tests or another isolated VM). */
85
- sandboxFactory?: ScriptSandboxFactory;
86
- /** Optional embedder identity fields layered over engine/JWT identity. */
87
- identity?: Partial<ScriptIdentity> | (() => Partial<ScriptIdentity>);
88
- /** Target document filename exposed as `this.documentFileName`. */
89
- fileName?: () => string;
90
- /** Injected deterministic transaction environment. */
91
- now?: () => number;
92
- utcOffsetMinutes?: () => number;
93
- randomSeed?: () => number;
94
- budget?: ScriptBudget;
95
- }
96
116
  /** The scripting switch moved to `actionsPlugin({ javascript })` (D8). */
97
117
  interface FormPluginOptions {}
98
118
  type FormCommitStatus = 'applied' | 'unchanged' | 'rejected' | 'failed';
@@ -126,7 +146,7 @@ type FormUiEffect = ScriptUiEffect & {
126
146
  };
127
147
  /** Input for {@link FormCapability.placeField}. */
128
148
  interface PlaceFieldInput {
129
- family: Exclude<FormFieldFamily, 'pushbutton' | 'signature' | 'unknown'>;
149
+ family: AuthorableFormFamily;
130
150
  pageObjectNumber: number;
131
151
  /** Content-space LOGICAL field box (no visual padding semantics). */
132
152
  box: Box;
@@ -169,6 +189,18 @@ interface FormCapability {
169
189
  ensureGeom(pageObjectNumber: number): void;
170
190
  field(key: FieldKey): FormFieldDTO | null;
171
191
  fieldForWidget(annotObjectNumber: number): FormFieldDTO | null;
192
+ /**
193
+ * The widget under a content-space point on a page (any family), with
194
+ * its field — the hit test a sibling plugin runs on a pointer sample
195
+ * before deciding what a click means (a mark dropped over a signature
196
+ * field). The model's own geometry answers when it is loaded; until then
197
+ * the annotation plane's live boxes do, so the first click on a page
198
+ * already resolves. The smallest containing widget wins.
199
+ */
200
+ widgetAt(pageObjectNumber: number, point: {
201
+ x: number;
202
+ y: number;
203
+ }): WidgetHit | null;
172
204
  /** Commit a text value (call on blur/Enter — keystrokes stay local). */
173
205
  setText(key: FieldKey, value: string): Promise<void>;
174
206
  /** Toggle a checkbox/radio widget by its on-state; null clears the group. */
@@ -273,5 +305,5 @@ interface FormHostCapability extends FormCapability {
273
305
  */
274
306
  declare const FormToken: import("@embedpdf/core").CapabilityToken<FormHostCapability>;
275
307
  //#endregion
276
- export { fieldKeyOf as _, FormHostCapability as a, FormState as c, PlaceFieldInput as d, PlacedField as f, FieldKey as g, Box as h, FormCommitStatus as i, FormToken as l, FillItem as m, FormCapability as n, FormPluginOptions as o, WidgetActivationResult as p, FormCommitResult as r, FormScriptingOptions as s, FormAction as t, FormUiEffect as u };
277
- //# sourceMappingURL=types-Blr4QQM5.d.cts.map
308
+ export { FillItem as _, FormHostCapability as a, WidgetHit as b, FormToken as c, PlacedField as d, WidgetActivationResult as f, FormToolDef as g, FORM_TOOL_BY_ID as h, FormCommitStatus as i, FormUiEffect as l, FORM_TOOLS as m, FormCapability as n, FormPluginOptions as o, AuthorableFormFamily as p, FormCommitResult as r, FormState as s, FormAction as t, PlaceFieldInput as u, Box as v, fieldKeyOf as x, FieldKey as y };
309
+ //# sourceMappingURL=types-Bpc32Qmx.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types-C1uIIYfn.cjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["import type {\n AnnotationRef,\n FormDataExport,\n FormDataFormat,\n FormFieldDTO,\n FormFieldFamily,\n FormFieldPatch,\n FormFieldRef,\n FormFieldValue,\n FormImportResult,\n FormRepairOptions,\n FormRepairResult,\n FormSetValueResult,\n FormEffect,\n FormEffectsResult,\n FormSnapshot,\n PdfActionTargetRef,\n WidgetAppearance,\n} from '@embedpdf/engine-core/runtime';\nimport type {\n ActionContext,\n ActionDiagnostic,\n ActionOrigin,\n ActionSubmitRequest,\n ActionTriggerResult,\n PdfAnnotationEventKind,\n SubmitIntent,\n} from '@embedpdf/plugin-actions/contract';\nimport type {\n ScriptBudget,\n ScriptDiagnostic,\n ScriptExecutionError,\n ScriptIdentity,\n ScriptUiEffect,\n} from '@embedpdf/core-acrojs';\nimport type { ScriptSandboxFactory } from '@embedpdf/core-js-sandbox';\nimport { createCapabilityToken } from '@embedpdf/core';\n\nimport type { FillItem } from './core/fill-items';\nimport type { Box, FieldKey, Model } from './core/model';\n\nexport interface FormState {\n model: Model;\n}\n\n/**\n * Standalone-realm configuration for `createFormScriptingHost` — stamp's\n * detached documents and direct controller tests. Viewer documents configure\n * scripting on `actionsPlugin({ javascript })` instead (D8), and every UI\n * effect/diagnostic surfaces through the actions port (D9).\n */\nexport interface FormScriptingOptions {\n /** Override the lazy QuickJS factory (tests or another isolated VM). */\n sandboxFactory?: ScriptSandboxFactory;\n /** Optional embedder identity fields layered over engine/JWT identity. */\n identity?: Partial<ScriptIdentity> | (() => Partial<ScriptIdentity>);\n /** Target document filename exposed as `this.documentFileName`. */\n fileName?: () => string;\n /** Injected deterministic transaction environment. */\n now?: () => number;\n utcOffsetMinutes?: () => number;\n randomSeed?: () => number;\n budget?: ScriptBudget;\n}\n\n/** The scripting switch moved to `actionsPlugin({ javascript })` (D8). */\nexport interface FormPluginOptions {}\n\nexport type FormCommitStatus = 'applied' | 'unchanged' | 'rejected' | 'failed';\n\nexport interface FormCommitResult {\n status: FormCommitStatus;\n scripted: boolean;\n effectsResult: FormEffectsResult | null;\n uiEffects: FormUiEffect[];\n diagnostics: ScriptDiagnostic[];\n error?: ScriptExecutionError;\n}\n\n/**\n * One DOM-free UI request produced by the curated Acrobat scripting surface.\n * `phase` says WHO asked: `'boot'` = a document-open script (Adobe's\n * version-check boilerplate lives here — embedders typically suppress these\n * nags), `'user'` = a script triggered by the user's own interaction (a\n * validation alert — show it).\n */\nexport type FormUiEffect = ScriptUiEffect & {\n /** WHO asked, script-model axis: `'boot'` = a name-tree/document-open boot\n * script, `'user'` = a runtime script. */\n phase: 'boot' | 'user';\n /**\n * The dispatch-origin axis (present when the script ran inside an action\n * dispatch): a lifecycle `/OpenAction` script is NOT a name-tree boot\n * script — the two axes are deliberately separate. Providers use this for\n * the default visibility matrix (suppress lifecycle alerts, block\n * non-user print); embedder handlers receive it and may decide otherwise.\n */\n origin?: ActionOrigin;\n};\n\n\n/** Input for {@link FormCapability.placeField}. */\nexport interface PlaceFieldInput {\n family: Exclude<FormFieldFamily, 'pushbutton' | 'signature' | 'unknown'>;\n pageObjectNumber: number;\n /** Content-space LOGICAL field box (no visual padding semantics). */\n box: Box;\n /** Widget styling in the engine vocabulary (`WidgetAppearance`). Convert a\n * tool's flat CSS defaults with `widgetAppearanceFromProps` from\n * `@embedpdf/plugin-annotation`. Omitted → the engine's bare defaults. */\n appearance?: WidgetAppearance;\n}\n\n/** What {@link FormCapability.placeField} created. */\nexport interface PlacedField {\n field: FormFieldDTO;\n /** The widget placed on the requested page (join key to the annotation\n * plane for auto-selection), or null when the engine reported none. */\n widget: FormFieldDTO['widgets'][number] | null;\n}\n\nexport type FormAction = { type: 'SET_MODEL'; model: Model };\n\n/**\n * The form plugin's public capability: the FIELD plane. Widgets stay\n * annotations (geometry/appearance live there); this surface owns values,\n * interchange, and the fill-mode projection.\n */\nexport interface FormCapability {\n /** The current reconciled form state (null until the first load lands). */\n snapshot(): FormSnapshot | null;\n /** Re-read the form from the engine (imports/repair/remote bursts). */\n refresh(): Promise<void>;\n\n /** Fill controls for one page — content-space, framework-agnostic. */\n fillItems(pageObjectNumber: number): FillItem[];\n /**\n * The fill control for ONE widget, by annotation object number — the join\n * the annotation-plane render layer uses (its RenderItem carries the live\n * box, so this item's `box` is advisory). Reference-stable per model change.\n * Null until the snapshot lands, or for families with no fill control.\n */\n fillItem(annotObjectNumber: number): FillItem | null;\n /** Make sure a page's widget geometry is loaded (idempotent, lazy). */\n ensureGeom(pageObjectNumber: number): void;\n\n field(key: FieldKey): FormFieldDTO | null;\n fieldForWidget(annotObjectNumber: number): FormFieldDTO | null;\n\n /** Commit a text value (call on blur/Enter — keystrokes stay local). */\n setText(key: FieldKey, value: string): Promise<void>;\n /** Toggle a checkbox/radio widget by its on-state; null clears the group. */\n toggle(key: FieldKey, onState: string | null): Promise<void>;\n /** Select choice options by export value. */\n choose(key: FieldKey, values: string[]): Promise<void>;\n /** Commit one originating-client value transaction, including K/V/C/F scripts when enabled. */\n commitValue(ref: FormFieldRef, value: FormFieldValue): Promise<FormCommitResult>;\n /**\n * Execute one widget's `/A` activation. With the actions plugin installed\n * the FULL tree is delegated to its dispatcher (Hide/ResetForm buttons work\n * even with scripting off) — `kind: 'dispatched'`; without it, today's\n * scripting-transaction path runs — `kind: 'form'`.\n */\n activateWidget(key: FieldKey, annotationRef: AnnotationRef): Promise<WidgetActivationResult>;\n /**\n * Report one widget DOM event (pointer enter/leave, down/up, focus/blur).\n * With the actions plugin present the matching `/AA` tree dispatches\n * (hover rides the shared coalescing pump; `/A` shadows `/AA U` per ISO\n * Table 197); without it — or without a tree — this is a cheap no-op.\n * Fire-and-forget by design: results surface via the actions events.\n */\n notifyWidgetEvent(key: FieldKey, ref: AnnotationRef, event: PdfAnnotationEventKind): void;\n /** Restore a field to its /DV default. */\n reset(key: FieldKey): Promise<void>;\n /** Raw engine passthrough for anything the sugar above doesn't cover. */\n setValue(ref: FormFieldRef, value: FormFieldValue): Promise<FormSetValueResult>;\n\n exportData(format?: FormDataFormat): Promise<FormDataExport>;\n importData(data: Uint8Array | ArrayBuffer, format?: FormDataFormat): Promise<FormImportResult>;\n repair(options?: FormRepairOptions): Promise<FormRepairResult>;\n\n // ── design mode (doc.forms.modify) ─────────────────────────────────────\n /**\n * Create a field of `family` with one styled widget at a content-space box\n * — the palette tools' commit, and the programmatic authoring entry (works\n * with NO annotation plugin: a pure `doc.forms` call). The box is clamped\n * to the page; sizing policy (click default vs drag rect) is the caller's.\n * The field gets a deterministic auto-name (`text_1`, …; rename in the\n * field panel). Resolves AFTER the annotation plane (when present) has\n * re-read the page, so the returned widget is immediately selectable.\n */\n placeField(input: PlaceFieldInput): Promise<PlacedField>;\n /** The page's content box (`{0,0,w,h}`) — page-bound placement math. */\n pageBox(pageObjectNumber: number): Box | null;\n /** Field-plane properties: name, required, options, default value. */\n updateField(key: FieldKey, patch: FormFieldPatch): Promise<void>;\n /** Delete the field and every widget of it (cascades on the page). */\n deleteField(key: FieldKey): Promise<void>;\n /** Unlink one widget: it stays as an inert annotation, the field survives. */\n detachWidget(key: FieldKey, annotObjectNumber: number): Promise<void>;\n\n /**\n * The twins (permissions.md): would the family's verbs succeed now for\n * this session? `canRead` — the form model hydrates at all (false = the\n * hydration gate left it empty by right); `canFill` — value writes\n * (`setText`/`toggle`/`choose`/`reset`; also fused into every\n * `FillItem.disabled`); `canDesign` — the field-design family\n * (`placeField`/`updateField`/`deleteField`/`detachWidget`).\n */\n canRead(): boolean;\n canFill(): boolean;\n canDesign(): boolean;\n}\n\n/** What one widget activation did — which world handled it (see\n * {@link FormCapability.activateWidget}). Both framework call sites ignore\n * the value today; chrome that cares can discriminate on `kind`. */\nexport type WidgetActivationResult =\n | { kind: 'form'; result: FormCommitResult }\n | { kind: 'dispatched'; result: ActionTriggerResult };\n\n/**\n * HOST lens — plugin-to-plugin only (the actions plugin's interim\n * `javascript` / `reset-form` executors). Import the token from\n * `@embedpdf/plugin-form/contract/host`, never from application code.\n */\nexport interface FormHostCapability extends FormCapability {\n /**\n * Execute one ResetForm action: resolve targets against the live snapshot\n * with the shared ISO selection (`null` = every field; a parent NAME\n * selects its descendants too; `exclude` = complement; a resolution\n * yielding zero refs never reaches the engine), reset as ONE engine\n * batch, refresh, then recalculate when scripting is enabled (Acrobat's\n * behaviour). `origin` (default `'user'`) is preserved through\n * recalculation surfacing — a lifecycle ResetForm's alerts stay lifecycle.\n */\n resetFormAction(\n fields: PdfActionTargetRef[] | null,\n exclude: boolean,\n origin?: ActionOrigin,\n ): Promise<FormCommitResult>;\n /**\n * The submit dataset resolver (Phase 4, D7): fresh engine read + the pure\n * ISO builder (`field-selection.ts`). Registered with the actions plugin\n * as THE resolver for both action-node and script submits.\n */\n resolveSubmitDataset(\n intent: SubmitIntent,\n ctx: ActionContext,\n diagnose: (diagnostic: ActionDiagnostic) => void,\n ): Promise<ActionSubmitRequest>;\n /** The form DOCUMENT-commit sink (D3): engine `applyEffects` + snapshot\n * reconciliation. Sink contract: never throws, never enqueues, never\n * touches the script host — callable under a held host transaction. */\n commitScriptFormEffects(effects: FormEffect[]): Promise<FormEffectsResult>;\n}\n\n/**\n * The form capability token. Typed to the full {@link FormHostCapability}\n * here (the package internals + the `/internal` entry use this view). The\n * package root re-exports the SAME token narrowed to {@link FormCapability}.\n */\nexport const FormToken = createCapabilityToken<FormHostCapability>('form');\n\nexport type { FillItem } from './core/fill-items';\nexport type { Box, FieldKey } from './core/model';\n"],"mappings":";;;;;;AAsQA,MAAa,aAAA,2BAAA,CAAA,CAAA,sBAAA,CAAsD,MAAM"}
1
+ {"version":3,"file":"types-C1uIIYfn.cjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["import type {\n AnnotationRef,\n FormDataExport,\n FormDataFormat,\n FormFieldDTO,\n FormFieldFamily,\n FormFieldPatch,\n FormFieldRef,\n FormFieldValue,\n FormImportResult,\n FormRepairOptions,\n FormRepairResult,\n FormSetValueResult,\n FormEffect,\n FormEffectsResult,\n FormSnapshot,\n PdfActionTargetRef,\n WidgetAppearance,\n} from '@embedpdf/engine-core/runtime';\nimport type {\n ActionContext,\n ActionDiagnostic,\n ActionOrigin,\n ActionSubmitRequest,\n ActionTriggerResult,\n PdfAnnotationEventKind,\n SubmitIntent,\n} from '@embedpdf/plugin-actions/contract';\nimport type { ScriptDiagnostic, ScriptExecutionError, ScriptUiEffect } from '@embedpdf/core-acrojs';\nimport { createCapabilityToken } from '@embedpdf/core';\n\nimport type { FillItem } from './core/fill-items';\nimport type { Box, FieldKey, Model, WidgetHit } from './core/model';\nimport type { AuthorableFormFamily } from './tools';\n\nexport interface FormState {\n model: Model;\n}\n\n/** The scripting switch moved to `actionsPlugin({ javascript })` (D8). */\nexport interface FormPluginOptions {}\n\nexport type FormCommitStatus = 'applied' | 'unchanged' | 'rejected' | 'failed';\n\nexport interface FormCommitResult {\n status: FormCommitStatus;\n scripted: boolean;\n effectsResult: FormEffectsResult | null;\n uiEffects: FormUiEffect[];\n diagnostics: ScriptDiagnostic[];\n error?: ScriptExecutionError;\n}\n\n/**\n * One DOM-free UI request produced by the curated Acrobat scripting surface.\n * `phase` says WHO asked: `'boot'` = a document-open script (Adobe's\n * version-check boilerplate lives here — embedders typically suppress these\n * nags), `'user'` = a script triggered by the user's own interaction (a\n * validation alert — show it).\n */\nexport type FormUiEffect = ScriptUiEffect & {\n /** WHO asked, script-model axis: `'boot'` = a name-tree/document-open boot\n * script, `'user'` = a runtime script. */\n phase: 'boot' | 'user';\n /**\n * The dispatch-origin axis (present when the script ran inside an action\n * dispatch): a lifecycle `/OpenAction` script is NOT a name-tree boot\n * script — the two axes are deliberately separate. Providers use this for\n * the default visibility matrix (suppress lifecycle alerts, block\n * non-user print); embedder handlers receive it and may decide otherwise.\n */\n origin?: ActionOrigin;\n};\n\n/** Input for {@link FormCapability.placeField}. */\nexport interface PlaceFieldInput {\n family: AuthorableFormFamily;\n pageObjectNumber: number;\n /** Content-space LOGICAL field box (no visual padding semantics). */\n box: Box;\n /** Widget styling in the engine vocabulary (`WidgetAppearance`). Convert a\n * tool's flat CSS defaults with `widgetAppearanceFromProps` from\n * `@embedpdf/plugin-annotation`. Omitted → the engine's bare defaults. */\n appearance?: WidgetAppearance;\n}\n\n/** What {@link FormCapability.placeField} created. */\nexport interface PlacedField {\n field: FormFieldDTO;\n /** The widget placed on the requested page (join key to the annotation\n * plane for auto-selection), or null when the engine reported none. */\n widget: FormFieldDTO['widgets'][number] | null;\n}\n\nexport type FormAction = { type: 'SET_MODEL'; model: Model };\n\n/**\n * The form plugin's public capability: the FIELD plane. Widgets stay\n * annotations (geometry/appearance live there); this surface owns values,\n * interchange, and the fill-mode projection.\n */\nexport interface FormCapability {\n /** The current reconciled form state (null until the first load lands). */\n snapshot(): FormSnapshot | null;\n /** Re-read the form from the engine (imports/repair/remote bursts). */\n refresh(): Promise<void>;\n\n /** Fill controls for one page — content-space, framework-agnostic. */\n fillItems(pageObjectNumber: number): FillItem[];\n /**\n * The fill control for ONE widget, by annotation object number — the join\n * the annotation-plane render layer uses (its RenderItem carries the live\n * box, so this item's `box` is advisory). Reference-stable per model change.\n * Null until the snapshot lands, or for families with no fill control.\n */\n fillItem(annotObjectNumber: number): FillItem | null;\n /** Make sure a page's widget geometry is loaded (idempotent, lazy). */\n ensureGeom(pageObjectNumber: number): void;\n\n field(key: FieldKey): FormFieldDTO | null;\n fieldForWidget(annotObjectNumber: number): FormFieldDTO | null;\n /**\n * The widget under a content-space point on a page (any family), with\n * its field — the hit test a sibling plugin runs on a pointer sample\n * before deciding what a click means (a mark dropped over a signature\n * field). The model's own geometry answers when it is loaded; until then\n * the annotation plane's live boxes do, so the first click on a page\n * already resolves. The smallest containing widget wins.\n */\n widgetAt(pageObjectNumber: number, point: { x: number; y: number }): WidgetHit | null;\n\n /** Commit a text value (call on blur/Enter — keystrokes stay local). */\n setText(key: FieldKey, value: string): Promise<void>;\n /** Toggle a checkbox/radio widget by its on-state; null clears the group. */\n toggle(key: FieldKey, onState: string | null): Promise<void>;\n /** Select choice options by export value. */\n choose(key: FieldKey, values: string[]): Promise<void>;\n /** Commit one originating-client value transaction, including K/V/C/F scripts when enabled. */\n commitValue(ref: FormFieldRef, value: FormFieldValue): Promise<FormCommitResult>;\n /**\n * Execute one widget's `/A` activation. With the actions plugin installed\n * the FULL tree is delegated to its dispatcher (Hide/ResetForm buttons work\n * even with scripting off) — `kind: 'dispatched'`; without it, today's\n * scripting-transaction path runs — `kind: 'form'`.\n */\n activateWidget(key: FieldKey, annotationRef: AnnotationRef): Promise<WidgetActivationResult>;\n /**\n * Report one widget DOM event (pointer enter/leave, down/up, focus/blur).\n * With the actions plugin present the matching `/AA` tree dispatches\n * (hover rides the shared coalescing pump; `/A` shadows `/AA U` per ISO\n * Table 197); without it — or without a tree — this is a cheap no-op.\n * Fire-and-forget by design: results surface via the actions events.\n */\n notifyWidgetEvent(key: FieldKey, ref: AnnotationRef, event: PdfAnnotationEventKind): void;\n /** Restore a field to its /DV default. */\n reset(key: FieldKey): Promise<void>;\n /** Raw engine passthrough for anything the sugar above doesn't cover. */\n setValue(ref: FormFieldRef, value: FormFieldValue): Promise<FormSetValueResult>;\n\n exportData(format?: FormDataFormat): Promise<FormDataExport>;\n importData(data: Uint8Array | ArrayBuffer, format?: FormDataFormat): Promise<FormImportResult>;\n repair(options?: FormRepairOptions): Promise<FormRepairResult>;\n\n // ── design mode (doc.forms.modify) ─────────────────────────────────────\n /**\n * Create a field of `family` with one styled widget at a content-space box\n * — the palette tools' commit, and the programmatic authoring entry (works\n * with NO annotation plugin: a pure `doc.forms` call). The box is clamped\n * to the page; sizing policy (click default vs drag rect) is the caller's.\n * The field gets a deterministic auto-name (`text_1`, …; rename in the\n * field panel). Resolves AFTER the annotation plane (when present) has\n * re-read the page, so the returned widget is immediately selectable.\n */\n placeField(input: PlaceFieldInput): Promise<PlacedField>;\n /** The page's content box (`{0,0,w,h}`) — page-bound placement math. */\n pageBox(pageObjectNumber: number): Box | null;\n /** Field-plane properties: name, required, options, default value. */\n updateField(key: FieldKey, patch: FormFieldPatch): Promise<void>;\n /** Delete the field and every widget of it (cascades on the page). */\n deleteField(key: FieldKey): Promise<void>;\n /** Unlink one widget: it stays as an inert annotation, the field survives. */\n detachWidget(key: FieldKey, annotObjectNumber: number): Promise<void>;\n\n /**\n * The twins (permissions.md): would the family's verbs succeed now for\n * this session? `canRead` — the form model hydrates at all (false = the\n * hydration gate left it empty by right); `canFill` — value writes\n * (`setText`/`toggle`/`choose`/`reset`; also fused into every\n * `FillItem.disabled`); `canDesign` — the field-design family\n * (`placeField`/`updateField`/`deleteField`/`detachWidget`).\n */\n canRead(): boolean;\n canFill(): boolean;\n canDesign(): boolean;\n}\n\n/** What one widget activation did — which world handled it (see\n * {@link FormCapability.activateWidget}). Both framework call sites ignore\n * the value today; chrome that cares can discriminate on `kind`. */\nexport type WidgetActivationResult =\n | { kind: 'form'; result: FormCommitResult }\n | { kind: 'dispatched'; result: ActionTriggerResult };\n\n/**\n * HOST lens — plugin-to-plugin only (the actions plugin's interim\n * `javascript` / `reset-form` executors). Import the token from\n * `@embedpdf/plugin-form/contract/host`, never from application code.\n */\nexport interface FormHostCapability extends FormCapability {\n /**\n * Execute one ResetForm action: resolve targets against the live snapshot\n * with the shared ISO selection (`null` = every field; a parent NAME\n * selects its descendants too; `exclude` = complement; a resolution\n * yielding zero refs never reaches the engine), reset as ONE engine\n * batch, refresh, then recalculate when scripting is enabled (Acrobat's\n * behaviour). `origin` (default `'user'`) is preserved through\n * recalculation surfacing — a lifecycle ResetForm's alerts stay lifecycle.\n */\n resetFormAction(\n fields: PdfActionTargetRef[] | null,\n exclude: boolean,\n origin?: ActionOrigin,\n ): Promise<FormCommitResult>;\n /**\n * The submit dataset resolver (Phase 4, D7): fresh engine read + the pure\n * ISO builder (`field-selection.ts`). Registered with the actions plugin\n * as THE resolver for both action-node and script submits.\n */\n resolveSubmitDataset(\n intent: SubmitIntent,\n ctx: ActionContext,\n diagnose: (diagnostic: ActionDiagnostic) => void,\n ): Promise<ActionSubmitRequest>;\n /** The form DOCUMENT-commit sink (D3): engine `applyEffects` + snapshot\n * reconciliation. Sink contract: never throws, never enqueues, never\n * touches the script host — callable under a held host transaction. */\n commitScriptFormEffects(effects: FormEffect[]): Promise<FormEffectsResult>;\n}\n\n/**\n * The form capability token. Typed to the full {@link FormHostCapability}\n * here (the package internals + the `/internal` entry use this view). The\n * package root re-exports the SAME token narrowed to {@link FormCapability}.\n */\nexport const FormToken = createCapabilityToken<FormHostCapability>('form');\n\nexport type { FillItem } from './core/fill-items';\nexport type { Box, FieldKey, WidgetHit } from './core/model';\n"],"mappings":";;;;;;AAoPA,MAAa,aAAA,2BAAA,CAAA,CAAA,sBAAA,CAAsD,MAAM"}
@@ -1,7 +1,7 @@
1
1
  import { ActionContext, ActionDiagnostic, ActionOrigin, ActionSubmitRequest, ActionTriggerResult, PdfAnnotationEventKind, SubmitIntent } from "@embedpdf/plugin-actions/contract";
2
+ import { AnnotationPropsPatch, ClickCreate } from "@embedpdf/plugin-annotation/contract";
2
3
  import { AnnotationRef, FormDataExport, FormDataFormat, FormEffect, FormEffectsResult, FormFieldDTO, FormFieldFamily, FormFieldOption, FormFieldPatch, FormFieldRef, FormFieldValue, FormImportResult, FormRepairOptions, FormRepairResult, FormSetValueResult, FormSnapshot, PdfActionTargetRef, WidgetAppearance } from "@embedpdf/engine-core/runtime";
3
- import { ScriptBudget, ScriptDiagnostic, ScriptExecutionError, ScriptIdentity, ScriptUiEffect } from "@embedpdf/core-acrojs";
4
- import { ScriptSandboxFactory } from "@embedpdf/core-js-sandbox";
4
+ import { ScriptDiagnostic, ScriptExecutionError, ScriptUiEffect } from "@embedpdf/core-acrojs";
5
5
  //#region src/core/model.d.ts
6
6
  /** A content-space box (top-left origin, y-down, PDF points). */
7
7
  interface Box {
@@ -35,6 +35,13 @@ interface Model {
35
35
  /** Bumped on every change — cheap equality for memoized projections. */
36
36
  seq: number;
37
37
  }
38
+ /** A widget hit: the annotation under the point and the field it belongs to. */
39
+ interface WidgetHit {
40
+ annotObjectNumber: number;
41
+ field: FormFieldDTO;
42
+ /** The widget's content-space box. */
43
+ box: Box;
44
+ }
38
45
  //#endregion
39
46
  //#region src/core/fill-items.d.ts
40
47
  interface FillItemBase {
@@ -68,31 +75,44 @@ type FillItem = FillItemBase & ({
68
75
  selected: string[];
69
76
  } | {
70
77
  control: 'button';
78
+ } |
79
+ /**
80
+ * A signature field's widget. `signed` = it carries a `/V` (a signature
81
+ * dictionary): the field is final and its appearance sealed — the
82
+ * control inspects rather than signs. Unsigned: "sign here" — the
83
+ * signing act itself belongs to the signature plugin.
84
+ */
85
+ {
86
+ control: 'signature';
87
+ signed: boolean;
71
88
  });
72
89
  //#endregion
90
+ //#region src/tools.d.ts
91
+ /** The families the palette can author (push buttons are not authorable). */
92
+ type AuthorableFormFamily = Exclude<FormFieldFamily, 'pushbutton' | 'unknown'>;
93
+ interface FormToolDef {
94
+ id: string;
95
+ /** The field family `placeField` commits (the FORM plane's vocabulary). */
96
+ family: AuthorableFormFamily;
97
+ /** The client kind the ANNOTATION registry routes on (props panel).
98
+ * Not a PDF subtype — every widget is PDF `widget`; this is the view. */
99
+ visualKind: 'widget-text' | 'widget-choice' | 'widget-toggle' | 'widget-box';
100
+ /** What a bare click places (box policies only — fields are boxes). */
101
+ clickCreate: Extract<ClickCreate, {
102
+ width: number;
103
+ }>;
104
+ /** Seed drawing defaults: a placed field is VISIBLE (white box, gray
105
+ * border) and restylable per tool through the shared style panel. */
106
+ defaults: AnnotationPropsPatch;
107
+ cursor: string;
108
+ }
109
+ declare const FORM_TOOLS: readonly FormToolDef[];
110
+ declare const FORM_TOOL_BY_ID: ReadonlyMap<string, FormToolDef>;
111
+ //#endregion
73
112
  //#region src/types.d.ts
74
113
  interface FormState {
75
114
  model: Model;
76
115
  }
77
- /**
78
- * Standalone-realm configuration for `createFormScriptingHost` — stamp's
79
- * detached documents and direct controller tests. Viewer documents configure
80
- * scripting on `actionsPlugin({ javascript })` instead (D8), and every UI
81
- * effect/diagnostic surfaces through the actions port (D9).
82
- */
83
- interface FormScriptingOptions {
84
- /** Override the lazy QuickJS factory (tests or another isolated VM). */
85
- sandboxFactory?: ScriptSandboxFactory;
86
- /** Optional embedder identity fields layered over engine/JWT identity. */
87
- identity?: Partial<ScriptIdentity> | (() => Partial<ScriptIdentity>);
88
- /** Target document filename exposed as `this.documentFileName`. */
89
- fileName?: () => string;
90
- /** Injected deterministic transaction environment. */
91
- now?: () => number;
92
- utcOffsetMinutes?: () => number;
93
- randomSeed?: () => number;
94
- budget?: ScriptBudget;
95
- }
96
116
  /** The scripting switch moved to `actionsPlugin({ javascript })` (D8). */
97
117
  interface FormPluginOptions {}
98
118
  type FormCommitStatus = 'applied' | 'unchanged' | 'rejected' | 'failed';
@@ -126,7 +146,7 @@ type FormUiEffect = ScriptUiEffect & {
126
146
  };
127
147
  /** Input for {@link FormCapability.placeField}. */
128
148
  interface PlaceFieldInput {
129
- family: Exclude<FormFieldFamily, 'pushbutton' | 'signature' | 'unknown'>;
149
+ family: AuthorableFormFamily;
130
150
  pageObjectNumber: number;
131
151
  /** Content-space LOGICAL field box (no visual padding semantics). */
132
152
  box: Box;
@@ -169,6 +189,18 @@ interface FormCapability {
169
189
  ensureGeom(pageObjectNumber: number): void;
170
190
  field(key: FieldKey): FormFieldDTO | null;
171
191
  fieldForWidget(annotObjectNumber: number): FormFieldDTO | null;
192
+ /**
193
+ * The widget under a content-space point on a page (any family), with
194
+ * its field — the hit test a sibling plugin runs on a pointer sample
195
+ * before deciding what a click means (a mark dropped over a signature
196
+ * field). The model's own geometry answers when it is loaded; until then
197
+ * the annotation plane's live boxes do, so the first click on a page
198
+ * already resolves. The smallest containing widget wins.
199
+ */
200
+ widgetAt(pageObjectNumber: number, point: {
201
+ x: number;
202
+ y: number;
203
+ }): WidgetHit | null;
172
204
  /** Commit a text value (call on blur/Enter — keystrokes stay local). */
173
205
  setText(key: FieldKey, value: string): Promise<void>;
174
206
  /** Toggle a checkbox/radio widget by its on-state; null clears the group. */
@@ -273,5 +305,5 @@ interface FormHostCapability extends FormCapability {
273
305
  */
274
306
  declare const FormToken: import("@embedpdf/core").CapabilityToken<FormHostCapability>;
275
307
  //#endregion
276
- export { fieldKeyOf as _, FormHostCapability as a, FormState as c, PlaceFieldInput as d, PlacedField as f, FieldKey as g, Box as h, FormCommitStatus as i, FormToken as l, FillItem as m, FormCapability as n, FormPluginOptions as o, WidgetActivationResult as p, FormCommitResult as r, FormScriptingOptions as s, FormAction as t, FormUiEffect as u };
277
- //# sourceMappingURL=types-BlXdnNRf.d.ts.map
308
+ export { FillItem as _, FormHostCapability as a, WidgetHit as b, FormToken as c, PlacedField as d, WidgetActivationResult as f, FormToolDef as g, FORM_TOOL_BY_ID as h, FormCommitStatus as i, FormUiEffect as l, FORM_TOOLS as m, FormCapability as n, FormPluginOptions as o, AuthorableFormFamily as p, FormCommitResult as r, FormState as s, FormAction as t, PlaceFieldInput as u, Box as v, fieldKeyOf as x, FieldKey as y };
309
+ //# sourceMappingURL=types-DRirhZKr.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types-DkQmmcTG.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["import type {\n AnnotationRef,\n FormDataExport,\n FormDataFormat,\n FormFieldDTO,\n FormFieldFamily,\n FormFieldPatch,\n FormFieldRef,\n FormFieldValue,\n FormImportResult,\n FormRepairOptions,\n FormRepairResult,\n FormSetValueResult,\n FormEffect,\n FormEffectsResult,\n FormSnapshot,\n PdfActionTargetRef,\n WidgetAppearance,\n} from '@embedpdf/engine-core/runtime';\nimport type {\n ActionContext,\n ActionDiagnostic,\n ActionOrigin,\n ActionSubmitRequest,\n ActionTriggerResult,\n PdfAnnotationEventKind,\n SubmitIntent,\n} from '@embedpdf/plugin-actions/contract';\nimport type {\n ScriptBudget,\n ScriptDiagnostic,\n ScriptExecutionError,\n ScriptIdentity,\n ScriptUiEffect,\n} from '@embedpdf/core-acrojs';\nimport type { ScriptSandboxFactory } from '@embedpdf/core-js-sandbox';\nimport { createCapabilityToken } from '@embedpdf/core';\n\nimport type { FillItem } from './core/fill-items';\nimport type { Box, FieldKey, Model } from './core/model';\n\nexport interface FormState {\n model: Model;\n}\n\n/**\n * Standalone-realm configuration for `createFormScriptingHost` — stamp's\n * detached documents and direct controller tests. Viewer documents configure\n * scripting on `actionsPlugin({ javascript })` instead (D8), and every UI\n * effect/diagnostic surfaces through the actions port (D9).\n */\nexport interface FormScriptingOptions {\n /** Override the lazy QuickJS factory (tests or another isolated VM). */\n sandboxFactory?: ScriptSandboxFactory;\n /** Optional embedder identity fields layered over engine/JWT identity. */\n identity?: Partial<ScriptIdentity> | (() => Partial<ScriptIdentity>);\n /** Target document filename exposed as `this.documentFileName`. */\n fileName?: () => string;\n /** Injected deterministic transaction environment. */\n now?: () => number;\n utcOffsetMinutes?: () => number;\n randomSeed?: () => number;\n budget?: ScriptBudget;\n}\n\n/** The scripting switch moved to `actionsPlugin({ javascript })` (D8). */\nexport interface FormPluginOptions {}\n\nexport type FormCommitStatus = 'applied' | 'unchanged' | 'rejected' | 'failed';\n\nexport interface FormCommitResult {\n status: FormCommitStatus;\n scripted: boolean;\n effectsResult: FormEffectsResult | null;\n uiEffects: FormUiEffect[];\n diagnostics: ScriptDiagnostic[];\n error?: ScriptExecutionError;\n}\n\n/**\n * One DOM-free UI request produced by the curated Acrobat scripting surface.\n * `phase` says WHO asked: `'boot'` = a document-open script (Adobe's\n * version-check boilerplate lives here — embedders typically suppress these\n * nags), `'user'` = a script triggered by the user's own interaction (a\n * validation alert — show it).\n */\nexport type FormUiEffect = ScriptUiEffect & {\n /** WHO asked, script-model axis: `'boot'` = a name-tree/document-open boot\n * script, `'user'` = a runtime script. */\n phase: 'boot' | 'user';\n /**\n * The dispatch-origin axis (present when the script ran inside an action\n * dispatch): a lifecycle `/OpenAction` script is NOT a name-tree boot\n * script — the two axes are deliberately separate. Providers use this for\n * the default visibility matrix (suppress lifecycle alerts, block\n * non-user print); embedder handlers receive it and may decide otherwise.\n */\n origin?: ActionOrigin;\n};\n\n\n/** Input for {@link FormCapability.placeField}. */\nexport interface PlaceFieldInput {\n family: Exclude<FormFieldFamily, 'pushbutton' | 'signature' | 'unknown'>;\n pageObjectNumber: number;\n /** Content-space LOGICAL field box (no visual padding semantics). */\n box: Box;\n /** Widget styling in the engine vocabulary (`WidgetAppearance`). Convert a\n * tool's flat CSS defaults with `widgetAppearanceFromProps` from\n * `@embedpdf/plugin-annotation`. Omitted → the engine's bare defaults. */\n appearance?: WidgetAppearance;\n}\n\n/** What {@link FormCapability.placeField} created. */\nexport interface PlacedField {\n field: FormFieldDTO;\n /** The widget placed on the requested page (join key to the annotation\n * plane for auto-selection), or null when the engine reported none. */\n widget: FormFieldDTO['widgets'][number] | null;\n}\n\nexport type FormAction = { type: 'SET_MODEL'; model: Model };\n\n/**\n * The form plugin's public capability: the FIELD plane. Widgets stay\n * annotations (geometry/appearance live there); this surface owns values,\n * interchange, and the fill-mode projection.\n */\nexport interface FormCapability {\n /** The current reconciled form state (null until the first load lands). */\n snapshot(): FormSnapshot | null;\n /** Re-read the form from the engine (imports/repair/remote bursts). */\n refresh(): Promise<void>;\n\n /** Fill controls for one page — content-space, framework-agnostic. */\n fillItems(pageObjectNumber: number): FillItem[];\n /**\n * The fill control for ONE widget, by annotation object number — the join\n * the annotation-plane render layer uses (its RenderItem carries the live\n * box, so this item's `box` is advisory). Reference-stable per model change.\n * Null until the snapshot lands, or for families with no fill control.\n */\n fillItem(annotObjectNumber: number): FillItem | null;\n /** Make sure a page's widget geometry is loaded (idempotent, lazy). */\n ensureGeom(pageObjectNumber: number): void;\n\n field(key: FieldKey): FormFieldDTO | null;\n fieldForWidget(annotObjectNumber: number): FormFieldDTO | null;\n\n /** Commit a text value (call on blur/Enter — keystrokes stay local). */\n setText(key: FieldKey, value: string): Promise<void>;\n /** Toggle a checkbox/radio widget by its on-state; null clears the group. */\n toggle(key: FieldKey, onState: string | null): Promise<void>;\n /** Select choice options by export value. */\n choose(key: FieldKey, values: string[]): Promise<void>;\n /** Commit one originating-client value transaction, including K/V/C/F scripts when enabled. */\n commitValue(ref: FormFieldRef, value: FormFieldValue): Promise<FormCommitResult>;\n /**\n * Execute one widget's `/A` activation. With the actions plugin installed\n * the FULL tree is delegated to its dispatcher (Hide/ResetForm buttons work\n * even with scripting off) — `kind: 'dispatched'`; without it, today's\n * scripting-transaction path runs — `kind: 'form'`.\n */\n activateWidget(key: FieldKey, annotationRef: AnnotationRef): Promise<WidgetActivationResult>;\n /**\n * Report one widget DOM event (pointer enter/leave, down/up, focus/blur).\n * With the actions plugin present the matching `/AA` tree dispatches\n * (hover rides the shared coalescing pump; `/A` shadows `/AA U` per ISO\n * Table 197); without it — or without a tree — this is a cheap no-op.\n * Fire-and-forget by design: results surface via the actions events.\n */\n notifyWidgetEvent(key: FieldKey, ref: AnnotationRef, event: PdfAnnotationEventKind): void;\n /** Restore a field to its /DV default. */\n reset(key: FieldKey): Promise<void>;\n /** Raw engine passthrough for anything the sugar above doesn't cover. */\n setValue(ref: FormFieldRef, value: FormFieldValue): Promise<FormSetValueResult>;\n\n exportData(format?: FormDataFormat): Promise<FormDataExport>;\n importData(data: Uint8Array | ArrayBuffer, format?: FormDataFormat): Promise<FormImportResult>;\n repair(options?: FormRepairOptions): Promise<FormRepairResult>;\n\n // ── design mode (doc.forms.modify) ─────────────────────────────────────\n /**\n * Create a field of `family` with one styled widget at a content-space box\n * — the palette tools' commit, and the programmatic authoring entry (works\n * with NO annotation plugin: a pure `doc.forms` call). The box is clamped\n * to the page; sizing policy (click default vs drag rect) is the caller's.\n * The field gets a deterministic auto-name (`text_1`, …; rename in the\n * field panel). Resolves AFTER the annotation plane (when present) has\n * re-read the page, so the returned widget is immediately selectable.\n */\n placeField(input: PlaceFieldInput): Promise<PlacedField>;\n /** The page's content box (`{0,0,w,h}`) — page-bound placement math. */\n pageBox(pageObjectNumber: number): Box | null;\n /** Field-plane properties: name, required, options, default value. */\n updateField(key: FieldKey, patch: FormFieldPatch): Promise<void>;\n /** Delete the field and every widget of it (cascades on the page). */\n deleteField(key: FieldKey): Promise<void>;\n /** Unlink one widget: it stays as an inert annotation, the field survives. */\n detachWidget(key: FieldKey, annotObjectNumber: number): Promise<void>;\n\n /**\n * The twins (permissions.md): would the family's verbs succeed now for\n * this session? `canRead` — the form model hydrates at all (false = the\n * hydration gate left it empty by right); `canFill` — value writes\n * (`setText`/`toggle`/`choose`/`reset`; also fused into every\n * `FillItem.disabled`); `canDesign` — the field-design family\n * (`placeField`/`updateField`/`deleteField`/`detachWidget`).\n */\n canRead(): boolean;\n canFill(): boolean;\n canDesign(): boolean;\n}\n\n/** What one widget activation did — which world handled it (see\n * {@link FormCapability.activateWidget}). Both framework call sites ignore\n * the value today; chrome that cares can discriminate on `kind`. */\nexport type WidgetActivationResult =\n | { kind: 'form'; result: FormCommitResult }\n | { kind: 'dispatched'; result: ActionTriggerResult };\n\n/**\n * HOST lens — plugin-to-plugin only (the actions plugin's interim\n * `javascript` / `reset-form` executors). Import the token from\n * `@embedpdf/plugin-form/contract/host`, never from application code.\n */\nexport interface FormHostCapability extends FormCapability {\n /**\n * Execute one ResetForm action: resolve targets against the live snapshot\n * with the shared ISO selection (`null` = every field; a parent NAME\n * selects its descendants too; `exclude` = complement; a resolution\n * yielding zero refs never reaches the engine), reset as ONE engine\n * batch, refresh, then recalculate when scripting is enabled (Acrobat's\n * behaviour). `origin` (default `'user'`) is preserved through\n * recalculation surfacing — a lifecycle ResetForm's alerts stay lifecycle.\n */\n resetFormAction(\n fields: PdfActionTargetRef[] | null,\n exclude: boolean,\n origin?: ActionOrigin,\n ): Promise<FormCommitResult>;\n /**\n * The submit dataset resolver (Phase 4, D7): fresh engine read + the pure\n * ISO builder (`field-selection.ts`). Registered with the actions plugin\n * as THE resolver for both action-node and script submits.\n */\n resolveSubmitDataset(\n intent: SubmitIntent,\n ctx: ActionContext,\n diagnose: (diagnostic: ActionDiagnostic) => void,\n ): Promise<ActionSubmitRequest>;\n /** The form DOCUMENT-commit sink (D3): engine `applyEffects` + snapshot\n * reconciliation. Sink contract: never throws, never enqueues, never\n * touches the script host — callable under a held host transaction. */\n commitScriptFormEffects(effects: FormEffect[]): Promise<FormEffectsResult>;\n}\n\n/**\n * The form capability token. Typed to the full {@link FormHostCapability}\n * here (the package internals + the `/internal` entry use this view). The\n * package root re-exports the SAME token narrowed to {@link FormCapability}.\n */\nexport const FormToken = createCapabilityToken<FormHostCapability>('form');\n\nexport type { FillItem } from './core/fill-items';\nexport type { Box, FieldKey } from './core/model';\n"],"mappings":";;;;;;;AAsQA,MAAa,YAAY,sBAA0C,MAAM"}
1
+ {"version":3,"file":"types-DkQmmcTG.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["import type {\n AnnotationRef,\n FormDataExport,\n FormDataFormat,\n FormFieldDTO,\n FormFieldFamily,\n FormFieldPatch,\n FormFieldRef,\n FormFieldValue,\n FormImportResult,\n FormRepairOptions,\n FormRepairResult,\n FormSetValueResult,\n FormEffect,\n FormEffectsResult,\n FormSnapshot,\n PdfActionTargetRef,\n WidgetAppearance,\n} from '@embedpdf/engine-core/runtime';\nimport type {\n ActionContext,\n ActionDiagnostic,\n ActionOrigin,\n ActionSubmitRequest,\n ActionTriggerResult,\n PdfAnnotationEventKind,\n SubmitIntent,\n} from '@embedpdf/plugin-actions/contract';\nimport type { ScriptDiagnostic, ScriptExecutionError, ScriptUiEffect } from '@embedpdf/core-acrojs';\nimport { createCapabilityToken } from '@embedpdf/core';\n\nimport type { FillItem } from './core/fill-items';\nimport type { Box, FieldKey, Model, WidgetHit } from './core/model';\nimport type { AuthorableFormFamily } from './tools';\n\nexport interface FormState {\n model: Model;\n}\n\n/** The scripting switch moved to `actionsPlugin({ javascript })` (D8). */\nexport interface FormPluginOptions {}\n\nexport type FormCommitStatus = 'applied' | 'unchanged' | 'rejected' | 'failed';\n\nexport interface FormCommitResult {\n status: FormCommitStatus;\n scripted: boolean;\n effectsResult: FormEffectsResult | null;\n uiEffects: FormUiEffect[];\n diagnostics: ScriptDiagnostic[];\n error?: ScriptExecutionError;\n}\n\n/**\n * One DOM-free UI request produced by the curated Acrobat scripting surface.\n * `phase` says WHO asked: `'boot'` = a document-open script (Adobe's\n * version-check boilerplate lives here — embedders typically suppress these\n * nags), `'user'` = a script triggered by the user's own interaction (a\n * validation alert — show it).\n */\nexport type FormUiEffect = ScriptUiEffect & {\n /** WHO asked, script-model axis: `'boot'` = a name-tree/document-open boot\n * script, `'user'` = a runtime script. */\n phase: 'boot' | 'user';\n /**\n * The dispatch-origin axis (present when the script ran inside an action\n * dispatch): a lifecycle `/OpenAction` script is NOT a name-tree boot\n * script — the two axes are deliberately separate. Providers use this for\n * the default visibility matrix (suppress lifecycle alerts, block\n * non-user print); embedder handlers receive it and may decide otherwise.\n */\n origin?: ActionOrigin;\n};\n\n/** Input for {@link FormCapability.placeField}. */\nexport interface PlaceFieldInput {\n family: AuthorableFormFamily;\n pageObjectNumber: number;\n /** Content-space LOGICAL field box (no visual padding semantics). */\n box: Box;\n /** Widget styling in the engine vocabulary (`WidgetAppearance`). Convert a\n * tool's flat CSS defaults with `widgetAppearanceFromProps` from\n * `@embedpdf/plugin-annotation`. Omitted → the engine's bare defaults. */\n appearance?: WidgetAppearance;\n}\n\n/** What {@link FormCapability.placeField} created. */\nexport interface PlacedField {\n field: FormFieldDTO;\n /** The widget placed on the requested page (join key to the annotation\n * plane for auto-selection), or null when the engine reported none. */\n widget: FormFieldDTO['widgets'][number] | null;\n}\n\nexport type FormAction = { type: 'SET_MODEL'; model: Model };\n\n/**\n * The form plugin's public capability: the FIELD plane. Widgets stay\n * annotations (geometry/appearance live there); this surface owns values,\n * interchange, and the fill-mode projection.\n */\nexport interface FormCapability {\n /** The current reconciled form state (null until the first load lands). */\n snapshot(): FormSnapshot | null;\n /** Re-read the form from the engine (imports/repair/remote bursts). */\n refresh(): Promise<void>;\n\n /** Fill controls for one page — content-space, framework-agnostic. */\n fillItems(pageObjectNumber: number): FillItem[];\n /**\n * The fill control for ONE widget, by annotation object number — the join\n * the annotation-plane render layer uses (its RenderItem carries the live\n * box, so this item's `box` is advisory). Reference-stable per model change.\n * Null until the snapshot lands, or for families with no fill control.\n */\n fillItem(annotObjectNumber: number): FillItem | null;\n /** Make sure a page's widget geometry is loaded (idempotent, lazy). */\n ensureGeom(pageObjectNumber: number): void;\n\n field(key: FieldKey): FormFieldDTO | null;\n fieldForWidget(annotObjectNumber: number): FormFieldDTO | null;\n /**\n * The widget under a content-space point on a page (any family), with\n * its field — the hit test a sibling plugin runs on a pointer sample\n * before deciding what a click means (a mark dropped over a signature\n * field). The model's own geometry answers when it is loaded; until then\n * the annotation plane's live boxes do, so the first click on a page\n * already resolves. The smallest containing widget wins.\n */\n widgetAt(pageObjectNumber: number, point: { x: number; y: number }): WidgetHit | null;\n\n /** Commit a text value (call on blur/Enter — keystrokes stay local). */\n setText(key: FieldKey, value: string): Promise<void>;\n /** Toggle a checkbox/radio widget by its on-state; null clears the group. */\n toggle(key: FieldKey, onState: string | null): Promise<void>;\n /** Select choice options by export value. */\n choose(key: FieldKey, values: string[]): Promise<void>;\n /** Commit one originating-client value transaction, including K/V/C/F scripts when enabled. */\n commitValue(ref: FormFieldRef, value: FormFieldValue): Promise<FormCommitResult>;\n /**\n * Execute one widget's `/A` activation. With the actions plugin installed\n * the FULL tree is delegated to its dispatcher (Hide/ResetForm buttons work\n * even with scripting off) — `kind: 'dispatched'`; without it, today's\n * scripting-transaction path runs — `kind: 'form'`.\n */\n activateWidget(key: FieldKey, annotationRef: AnnotationRef): Promise<WidgetActivationResult>;\n /**\n * Report one widget DOM event (pointer enter/leave, down/up, focus/blur).\n * With the actions plugin present the matching `/AA` tree dispatches\n * (hover rides the shared coalescing pump; `/A` shadows `/AA U` per ISO\n * Table 197); without it — or without a tree — this is a cheap no-op.\n * Fire-and-forget by design: results surface via the actions events.\n */\n notifyWidgetEvent(key: FieldKey, ref: AnnotationRef, event: PdfAnnotationEventKind): void;\n /** Restore a field to its /DV default. */\n reset(key: FieldKey): Promise<void>;\n /** Raw engine passthrough for anything the sugar above doesn't cover. */\n setValue(ref: FormFieldRef, value: FormFieldValue): Promise<FormSetValueResult>;\n\n exportData(format?: FormDataFormat): Promise<FormDataExport>;\n importData(data: Uint8Array | ArrayBuffer, format?: FormDataFormat): Promise<FormImportResult>;\n repair(options?: FormRepairOptions): Promise<FormRepairResult>;\n\n // ── design mode (doc.forms.modify) ─────────────────────────────────────\n /**\n * Create a field of `family` with one styled widget at a content-space box\n * — the palette tools' commit, and the programmatic authoring entry (works\n * with NO annotation plugin: a pure `doc.forms` call). The box is clamped\n * to the page; sizing policy (click default vs drag rect) is the caller's.\n * The field gets a deterministic auto-name (`text_1`, …; rename in the\n * field panel). Resolves AFTER the annotation plane (when present) has\n * re-read the page, so the returned widget is immediately selectable.\n */\n placeField(input: PlaceFieldInput): Promise<PlacedField>;\n /** The page's content box (`{0,0,w,h}`) — page-bound placement math. */\n pageBox(pageObjectNumber: number): Box | null;\n /** Field-plane properties: name, required, options, default value. */\n updateField(key: FieldKey, patch: FormFieldPatch): Promise<void>;\n /** Delete the field and every widget of it (cascades on the page). */\n deleteField(key: FieldKey): Promise<void>;\n /** Unlink one widget: it stays as an inert annotation, the field survives. */\n detachWidget(key: FieldKey, annotObjectNumber: number): Promise<void>;\n\n /**\n * The twins (permissions.md): would the family's verbs succeed now for\n * this session? `canRead` — the form model hydrates at all (false = the\n * hydration gate left it empty by right); `canFill` — value writes\n * (`setText`/`toggle`/`choose`/`reset`; also fused into every\n * `FillItem.disabled`); `canDesign` — the field-design family\n * (`placeField`/`updateField`/`deleteField`/`detachWidget`).\n */\n canRead(): boolean;\n canFill(): boolean;\n canDesign(): boolean;\n}\n\n/** What one widget activation did — which world handled it (see\n * {@link FormCapability.activateWidget}). Both framework call sites ignore\n * the value today; chrome that cares can discriminate on `kind`. */\nexport type WidgetActivationResult =\n | { kind: 'form'; result: FormCommitResult }\n | { kind: 'dispatched'; result: ActionTriggerResult };\n\n/**\n * HOST lens — plugin-to-plugin only (the actions plugin's interim\n * `javascript` / `reset-form` executors). Import the token from\n * `@embedpdf/plugin-form/contract/host`, never from application code.\n */\nexport interface FormHostCapability extends FormCapability {\n /**\n * Execute one ResetForm action: resolve targets against the live snapshot\n * with the shared ISO selection (`null` = every field; a parent NAME\n * selects its descendants too; `exclude` = complement; a resolution\n * yielding zero refs never reaches the engine), reset as ONE engine\n * batch, refresh, then recalculate when scripting is enabled (Acrobat's\n * behaviour). `origin` (default `'user'`) is preserved through\n * recalculation surfacing — a lifecycle ResetForm's alerts stay lifecycle.\n */\n resetFormAction(\n fields: PdfActionTargetRef[] | null,\n exclude: boolean,\n origin?: ActionOrigin,\n ): Promise<FormCommitResult>;\n /**\n * The submit dataset resolver (Phase 4, D7): fresh engine read + the pure\n * ISO builder (`field-selection.ts`). Registered with the actions plugin\n * as THE resolver for both action-node and script submits.\n */\n resolveSubmitDataset(\n intent: SubmitIntent,\n ctx: ActionContext,\n diagnose: (diagnostic: ActionDiagnostic) => void,\n ): Promise<ActionSubmitRequest>;\n /** The form DOCUMENT-commit sink (D3): engine `applyEffects` + snapshot\n * reconciliation. Sink contract: never throws, never enqueues, never\n * touches the script host — callable under a held host transaction. */\n commitScriptFormEffects(effects: FormEffect[]): Promise<FormEffectsResult>;\n}\n\n/**\n * The form capability token. Typed to the full {@link FormHostCapability}\n * here (the package internals + the `/internal` entry use this view). The\n * package root re-exports the SAME token narrowed to {@link FormCapability}.\n */\nexport const FormToken = createCapabilityToken<FormHostCapability>('form');\n\nexport type { FillItem } from './core/fill-items';\nexport type { Box, FieldKey, WidgetHit } from './core/model';\n"],"mappings":";;;;;;;AAoPA,MAAa,YAAY,sBAA0C,MAAM"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@embedpdf/plugin-form",
3
- "version": "3.0.0-next.11",
3
+ "version": "3.0.0-next.13",
4
4
  "private": false,
5
5
  "description": "Document-scoped interactive forms: reactive field state over doc.forms, fill-mode controls (FillItem projection), and form-data interchange. Widgets stay annotations; this plugin owns the field plane.",
6
6
  "type": "module",
@@ -34,19 +34,19 @@
34
34
  "dist"
35
35
  ],
36
36
  "dependencies": {
37
- "@embedpdf/core-acrojs": "3.0.0-next.11",
38
- "@embedpdf/core-js-sandbox": "3.0.0-next.11",
39
- "@embedpdf/core": "3.0.0-next.11",
40
- "@embedpdf/plugin-interaction": "3.0.0-next.11",
41
- "@embedpdf/plugin-annotation": "3.0.0-next.11",
42
- "@embedpdf/engine-core": "3.0.0-next.11",
43
- "@embedpdf/plugin-actions": "3.0.0-next.11"
37
+ "@embedpdf/core-acrojs": "3.0.0-next.13",
38
+ "@embedpdf/core-js-sandbox": "3.0.0-next.13",
39
+ "@embedpdf/core": "3.0.0-next.13",
40
+ "@embedpdf/plugin-interaction": "3.0.0-next.13",
41
+ "@embedpdf/engine-core": "3.0.0-next.13",
42
+ "@embedpdf/plugin-annotation": "3.0.0-next.13",
43
+ "@embedpdf/plugin-actions": "3.0.0-next.13"
44
44
  },
45
45
  "devDependencies": {
46
46
  "typescript": "^5.0.0",
47
47
  "vitest": "^2.1.9",
48
- "@embedpdf/tooling-build": "0.0.0",
49
- "@embedpdf/engine": "3.0.0-next.11"
48
+ "@embedpdf/engine": "3.0.0-next.13",
49
+ "@embedpdf/tooling-build": "0.0.0"
50
50
  },
51
51
  "license": "Apache-2.0",
52
52
  "sideEffects": false,