@embedpdf/plugin-form 3.0.0-next.10 → 3.0.0-next.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,10 +1,105 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_scripting = require("./scripting.cjs");
3
+ const require_types = require("./types-C1uIIYfn.cjs");
4
+ const require_contract = require("./contract.cjs");
2
5
  let _embedpdf_core = require("@embedpdf/core");
3
- let _embedpdf_plugin_annotation = require("@embedpdf/plugin-annotation");
4
- let _embedpdf_plugin_annotation_internal = require("@embedpdf/plugin-annotation/internal");
5
- let _embedpdf_plugin_interaction = require("@embedpdf/plugin-interaction");
6
+ let _embedpdf_plugin_actions_contract = require("@embedpdf/plugin-actions/contract");
7
+ let _embedpdf_plugin_actions_contract_host = require("@embedpdf/plugin-actions/contract/host");
8
+ let _embedpdf_plugin_annotation_contract = require("@embedpdf/plugin-annotation/contract");
9
+ let _embedpdf_plugin_annotation_contract_host = require("@embedpdf/plugin-annotation/contract/host");
10
+ let _embedpdf_plugin_interaction_contract = require("@embedpdf/plugin-interaction/contract");
6
11
  let _embedpdf_engine_core_runtime = require("@embedpdf/engine-core/runtime");
7
- let _embedpdf_core_acrojs = require("@embedpdf/core-acrojs");
12
+ let _embedpdf_plugin_annotation_authoring = require("@embedpdf/plugin-annotation/authoring");
13
+ //#region src/field-selection.ts
14
+ /**
15
+ * ISO field selection, written ONCE for ResetForm (Tables 241/242) and
16
+ * SubmitForm (Tables 239/240): a NAME target selects the field AND its
17
+ * descendants (FQN dot-prefix — `parent` selects `parent.c1`, the rule the
18
+ * old exact-match resolution violated); an objectNumber target selects that
19
+ * field dictionary. Include mode keeps the selection; exclude mode keeps
20
+ * the complement (a name excludes its subtree the same way it includes
21
+ * it). `targets === null` (the key ABSENT) selects everything and the flag
22
+ * is ignored; `[]` + include selects NOTHING while `[]` + exclude selects
23
+ * everything — presence and emptiness are different states.
24
+ *
25
+ * `listed` reports whether a field was ADDRESSED by the target list
26
+ * (directly or through its subtree) — the "explicitly listed" predicate the
27
+ * submit honesty diagnostics key on.
28
+ */
29
+ const resolveFieldSelection = (all, targets, exclude) => {
30
+ if (targets === null) return {
31
+ selected: [...all],
32
+ listed: () => false
33
+ };
34
+ const names = targets.filter((t) => t.kind === "name").map((t) => t.name);
35
+ const objectNumbers = new Set(targets.filter((t) => t.kind === "objectNumber").map((t) => t.objectNumber));
36
+ const listed = (field) => field.ref.kind === "objectNumber" && objectNumbers.has(field.ref.fieldObjectNumber) || names.some((name) => field.name === name || field.name.startsWith(`${name}.`));
37
+ return {
38
+ selected: exclude ? all.filter((f) => !listed(f)) : all.filter(listed),
39
+ listed
40
+ };
41
+ };
42
+ /**
43
+ * Build the resolved submission dataset from a live field list (Tables
44
+ * 239/240 semantics; pure — the capability supplies a FRESH engine read so
45
+ * no model-lag class exists):
46
+ *
47
+ * - the unconditional **NoExport veto** — even an explicit include cannot
48
+ * override it (diagnosed when explicitly listed, so the veto is
49
+ * observable, silent when swept in implicitly);
50
+ * - **push-buttons and signatures** never contribute entries in v1 —
51
+ * ISO's explicit-pushbutton-`/AP`-in-FDF rule is unrepresentable in the
52
+ * current DTO, so an EXPLICITLY listed one is diagnosed
53
+ * (`submit-entry-unsupported`), never silently dropped;
54
+ * - an **unsupported `/V` shape** is always diagnosed (silent omission
55
+ * would be data loss);
56
+ * - a valueless field becomes a NAME-ONLY entry (`value: null`) only under
57
+ * IncludeNoValueFields, else it is skipped (the ISO default);
58
+ * - values are entry-faithful: scalar → string, array (multi-select list
59
+ * box) → string[]; the submitted name is the fully qualified name.
60
+ */
61
+ const buildSubmitEntries = (fields, intent, diagnose) => {
62
+ const { selected, listed } = resolveFieldSelection(fields, intent.fields, intent.exclude);
63
+ const explicitInclude = intent.fields !== null && !intent.exclude;
64
+ const entries = [];
65
+ for (const field of selected) {
66
+ const explicit = explicitInclude && listed(field);
67
+ if (field.family === "pushbutton" || field.family === "signature") {
68
+ if (explicit) diagnose({
69
+ code: "submit-entry-unsupported",
70
+ message: `submit: '${field.name}' (${field.family}) has no representable submission value in v1 — skipped`
71
+ });
72
+ continue;
73
+ }
74
+ if (field.flags.noExport) {
75
+ if (explicit) diagnose({
76
+ code: "submit-entry-unsupported",
77
+ message: `submit: '${field.name}' carries the NoExport flag — the veto is unconditional (ISO 12.7.6.2), skipped`
78
+ });
79
+ continue;
80
+ }
81
+ const entry = field.valueEntry;
82
+ if (entry.kind === "scalar") entries.push({
83
+ name: field.name,
84
+ value: entry.value
85
+ });
86
+ else if (entry.kind === "array") entries.push({
87
+ name: field.name,
88
+ value: entry.values
89
+ });
90
+ else if (entry.kind === "none") {
91
+ if (intent.includeNoValueFields) entries.push({
92
+ name: field.name,
93
+ value: null
94
+ });
95
+ } else diagnose({
96
+ code: "submit-entry-unsupported",
97
+ message: `submit: '${field.name}' has an unsupported /V shape — skipped (would otherwise be silent data loss)`
98
+ });
99
+ }
100
+ return entries;
101
+ };
102
+ //#endregion
8
103
  //#region src/core/model.ts
9
104
  const fieldKeyOf = (field) => field.fieldObjectNumber > 0 ? `obj:${field.fieldObjectNumber}` : `fqn:${field.name}`;
10
105
  const initialModel = () => ({
@@ -218,507 +313,8 @@ function fillItemForWidget(model, annotObjectNumber) {
218
313
  }
219
314
  //#endregion
220
315
  //#region src/mutationQueue.ts
221
- /** Small promise-tail queue: failures never poison later mutations. */
222
- function createSerialMutationQueue() {
223
- let tail = Promise.resolve();
224
- return (operation) => {
225
- const result = tail.then(operation, operation);
226
- tail = result.then(() => void 0, () => void 0);
227
- return result;
228
- };
229
- }
230
- //#endregion
231
- //#region src/scripting.ts
232
- const refKey = (ref) => ref.kind === "objectNumber" ? `obj:${ref.fieldObjectNumber}` : `fqn:${ref.name}`;
233
- const sameRef = (left, right) => refKey(left) === refKey(right);
234
- const cloneValue = (value) => Array.isArray(value) ? [...value] : value;
235
- const sameValue = (left, right) => Array.isArray(left) && Array.isArray(right) ? left.length === right.length && left.every((value, index) => value === right[index]) : left === right;
236
- function cloneFields(fields) {
237
- return fields.map((field) => ({
238
- ...field,
239
- ref: { ...field.ref },
240
- value: cloneValue(field.value),
241
- defaultValue: cloneValue(field.defaultValue),
242
- ...field.options ? { options: field.options.map((option) => ({ ...option })) } : {}
243
- }));
244
- }
245
- function fieldByRef(fields, ref) {
246
- return fields.find((field) => sameRef(field.ref, ref));
247
- }
248
- function snapshotField(snapshot, ref) {
249
- return snapshot.fields.find((field) => sameRef(field.ref, ref));
250
- }
251
- function scriptValueFromFormValue(field, value) {
252
- if (field.family === "text" && value.type === "text") return value.value;
253
- if ((field.family === "checkbox" || field.family === "radio") && value.type === "toggle") return value.state ?? "Off";
254
- if (field.family === "combobox" && value.type === "choice") return value.values[0] ?? "";
255
- if (field.family === "listbox" && value.type === "choice") return [...value.values];
256
- throw new Error(`Form value type '${value.type}' does not match field family '${field.family}'`);
257
- }
258
- function formValueFromScriptValue(field) {
259
- switch (field.family) {
260
- case "text": return {
261
- type: "text",
262
- value: String(field.value ?? "")
263
- };
264
- case "checkbox":
265
- case "radio": return {
266
- type: "toggle",
267
- state: field.value === null || field.value === void 0 || String(field.value) === "Off" ? null : String(field.value)
268
- };
269
- case "combobox": return {
270
- type: "choice",
271
- values: Array.isArray(field.value) ? field.value.slice(0, 1) : [String(field.value ?? "")]
272
- };
273
- case "listbox": return {
274
- type: "choice",
275
- values: Array.isArray(field.value) ? [...field.value] : [String(field.value ?? "")]
276
- };
277
- default: return null;
278
- }
279
- }
280
- function applyEffects(overlay, effects) {
281
- for (const effect of effects) {
282
- if (effect.kind === "reset") {
283
- for (const ref of effect.refs) {
284
- const field = fieldByRef(overlay.fields, ref);
285
- if (!field) continue;
286
- field.value = cloneValue(field.defaultValue);
287
- overlay.resetKeys.add(refKey(ref));
288
- }
289
- continue;
290
- }
291
- const field = fieldByRef(overlay.fields, effect.ref);
292
- if (!field) continue;
293
- const key = refKey(effect.ref);
294
- if (effect.kind === "setValue") {
295
- field.value = scriptValueFromFormValue(field, effect.value);
296
- overlay.resetKeys.delete(key);
297
- } else if (effect.kind === "setDisplay") field.display = effect.display;
298
- else overlay.appearances.set(key, {
299
- ref: effect.ref,
300
- text: effect.text
301
- });
302
- }
303
- }
304
- function canonicalEffects(overlay) {
305
- const effects = [];
306
- const resets = [];
307
- for (const field of overlay.fields) {
308
- const original = fieldByRef(overlay.original, field.ref);
309
- if (!original) continue;
310
- if (!sameValue(original.value, field.value)) {
311
- const key = refKey(field.ref);
312
- if (overlay.resetKeys.has(key) && sameValue(field.value, field.defaultValue)) resets.push(field.ref);
313
- else {
314
- const value = formValueFromScriptValue(field);
315
- if (value) effects.push({
316
- kind: "setValue",
317
- ref: field.ref,
318
- value
319
- });
320
- }
321
- }
322
- if (original.display !== field.display) effects.push({
323
- kind: "setDisplay",
324
- ref: field.ref,
325
- display: field.display
326
- });
327
- }
328
- if (resets.length > 0) effects.unshift({
329
- kind: "reset",
330
- refs: resets
331
- });
332
- effects.push(...Array.from(overlay.appearances.values(), ({ ref, text }) => ({
333
- kind: "setAppearanceText",
334
- ref,
335
- text
336
- })));
337
- return effects;
338
- }
339
- function scriptError(message) {
340
- return {
341
- kind: "invalid-output",
342
- message
343
- };
344
- }
345
- function utf8Length(value) {
346
- let length = 0;
347
- for (let index = 0; index < value.length; index += 1) {
348
- const code = value.charCodeAt(index);
349
- if (code < 128) length += 1;
350
- else if (code < 2048) length += 2;
351
- else if (code >= 55296 && code <= 56319 && index + 1 < value.length) {
352
- length += 4;
353
- index += 1;
354
- } else length += 3;
355
- }
356
- return length;
357
- }
358
- function seedFrom(documentId, sequence) {
359
- let hash = 2166136261;
360
- for (const char of `${documentId}:${sequence}`) {
361
- hash ^= char.charCodeAt(0);
362
- hash = Math.imul(hash, 16777619);
363
- }
364
- return hash >>> 0;
365
- }
366
- function resolvedIdentity(doc, config) {
367
- const claims = doc.security.identity;
368
- const supplied = typeof config.identity === "function" ? config.identity() : config.identity ?? {};
369
- return {
370
- name: supplied.name ?? claims?.display_name ?? claims?.user_id ?? "",
371
- loginName: supplied.loginName ?? claims?.user_id ?? "",
372
- corporation: supplied.corporation ?? claims?.group_id ?? "",
373
- email: supplied.email ?? ""
374
- };
375
- }
376
- function pageNumberFor(meta, field) {
377
- const pon = field.widgets.find((widget) => widget.pageObjectNumber > 0)?.pageObjectNumber;
378
- if (!meta || pon === void 0) return 0;
379
- const index = meta.pages.findIndex((page) => page.pageObjectNumber === pon);
380
- return Math.max(0, index);
381
- }
382
- function statusFromEffects(result) {
383
- if (result.results.some(({ status }) => status === "failed" || status === "skipped")) return "failed";
384
- if (result.results.some(({ status }) => status === "rejected")) return "failed";
385
- return result.results.some(({ status }) => status === "applied") ? "applied" : "unchanged";
386
- }
387
- var FormScriptingController = class {
388
- constructor(options) {
389
- this.options = options;
390
- this.sandboxPromise = null;
391
- this.booted = false;
392
- this.disposed = false;
393
- this.sequence = 0;
394
- }
395
- dispose() {
396
- if (this.disposed) return;
397
- this.disposed = true;
398
- this.sandboxPromise?.then((sandbox) => sandbox.dispose()).catch(() => void 0);
399
- }
400
- async commit(snapshot, ref, proposed) {
401
- return this.transact(snapshot, ref, proposed);
402
- }
403
- /** Execute one originating widget's activation action (`/A`, including `/Next`). */
404
- async activate(snapshot, ref, action) {
405
- return this.transact(snapshot, ref, void 0, action);
406
- }
407
- /** Run lazy document boot plus the `/CO` chain without a user field change. */
408
- async recalculate(snapshot) {
409
- const target = snapshot.calculationOrder.find((ref) => ref !== null && snapshotField(snapshot, ref) !== void 0) ?? snapshot.fields[0]?.ref;
410
- if (!target) return {
411
- status: "unchanged",
412
- scripted: true,
413
- effectsResult: null,
414
- uiEffects: [],
415
- diagnostics: []
416
- };
417
- return this.transact(snapshot, target);
418
- }
419
- async transact(snapshot, ref, proposed, activation) {
420
- if (this.disposed) throw new Error("Form scripting controller is disposed");
421
- const target = snapshotField(snapshot, ref);
422
- if (!target) return this.failed([], [], scriptError(`Form field '${refKey(ref)}' no longer exists`));
423
- const original = (0, _embedpdf_core_acrojs.scriptFieldsFromSnapshot)(snapshot);
424
- const overlay = {
425
- original,
426
- fields: cloneFields(original),
427
- resetKeys: /* @__PURE__ */ new Set(),
428
- appearances: /* @__PURE__ */ new Map()
429
- };
430
- const targetInput = fieldByRef(overlay.fields, ref);
431
- if (!targetInput) return this.failed([], [], scriptError(`Form field '${refKey(ref)}' has no script view`));
432
- const hasProposedValue = proposed !== void 0;
433
- let proposedValue = cloneValue(targetInput.value);
434
- if (proposed) try {
435
- proposedValue = scriptValueFromFormValue(targetInput, proposed);
436
- } catch (error) {
437
- return this.failed([], [], scriptError(error instanceof Error ? error.message : String(error)));
438
- }
439
- const uiEffects = [];
440
- const diagnostics = [];
441
- const meta = this.options.document();
442
- const nowMs = this.options.config.now?.() ?? Date.now();
443
- const inputBase = {
444
- document: {
445
- id: this.options.doc.id,
446
- fileName: this.options.config.fileName?.() ?? meta?.name ?? this.options.doc.id,
447
- pageCount: meta?.pageCount ?? 0,
448
- pageNumber: pageNumberFor(meta, target)
449
- },
450
- identity: resolvedIdentity(this.options.doc, this.options.config),
451
- environment: {
452
- nowMs,
453
- utcOffsetMinutes: this.options.config.utcOffsetMinutes?.() ?? -new Date(nowMs).getTimezoneOffset(),
454
- randomSeed: this.options.config.randomSeed?.() ?? seedFrom(this.options.doc.id, this.sequence++)
455
- }
456
- };
457
- const budget = this.options.config.budget ?? _embedpdf_core_acrojs.DEFAULT_SCRIPT_BUDGET;
458
- let executionMs = 0;
459
- let aggregateOutputSize = 0;
460
- const consume = (output, phase) => {
461
- aggregateOutputSize += utf8Length(JSON.stringify(output));
462
- uiEffects.push(...output.uiEffects.map((effect) => ({
463
- ...effect,
464
- phase
465
- })));
466
- diagnostics.push(...output.diagnostics);
467
- if (aggregateOutputSize > budget.maxOutputBytes) return {
468
- kind: "budget",
469
- message: `Form script transaction output exceeded ${budget.maxOutputBytes} bytes`
470
- };
471
- return output.error ?? null;
472
- };
473
- const remainingBudget = () => {
474
- const remaining = budget.maxExecutionMs - executionMs;
475
- return remaining <= 0 ? null : {
476
- ...budget,
477
- maxExecutionMs: remaining
478
- };
479
- };
480
- const run = async (kind, source, input) => {
481
- const remaining = remainingBudget();
482
- if (!remaining) return { error: {
483
- kind: "budget",
484
- message: "Form script transaction timed out"
485
- } };
486
- let output;
487
- try {
488
- const sandbox = await this.sandbox();
489
- const startedAt = Date.now();
490
- output = kind === "boot" ? sandbox.boot(source, input, remaining) : sandbox.run(source, input, remaining);
491
- executionMs += Date.now() - startedAt;
492
- } catch (error) {
493
- return { error: scriptError(error instanceof Error ? error.message : String(error)) };
494
- }
495
- const error = consume(output, kind === "boot" ? "boot" : "user");
496
- return error ? { error } : { output };
497
- };
498
- if (!this.booted) {
499
- this.booted = true;
500
- let sources = [];
501
- let bootError = null;
502
- try {
503
- sources = (this.options.doc.actions ? await this.options.doc.actions.read() : null)?.nameTreeScripts.map(({ action }) => (0, _embedpdf_core_acrojs.javaScriptProgramFromActionTree)(action)) ?? [];
504
- } catch (error) {
505
- bootError = scriptError(error instanceof Error ? error.message : String(error));
506
- }
507
- if (!bootError && sources.length > 0) {
508
- const boot = await run("boot", sources, {
509
- ...inputBase,
510
- fields: overlay.fields,
511
- event: { kind: "name-tree-boot" }
512
- });
513
- if (boot.error) bootError = boot.error;
514
- else applyEffects(overlay, boot.output.formEffects);
515
- }
516
- if (bootError) diagnostics.push({
517
- code: "script-error",
518
- message: `Document boot script failed (continuing without it): ${bootError.message}`
519
- });
520
- }
521
- const bootEffects = canonicalEffects(overlay);
522
- if (activation) {
523
- let program;
524
- try {
525
- program = (0, _embedpdf_core_acrojs.javaScriptProgramFromActionTree)(activation);
526
- } catch (error) {
527
- return this.failed(uiEffects, diagnostics, scriptError(error instanceof Error ? error.message : String(error)));
528
- }
529
- if (program) {
530
- const activated = await run("run", program, {
531
- ...inputBase,
532
- fields: overlay.fields,
533
- event: {
534
- kind: "widget-activate",
535
- target: ref,
536
- source: ref,
537
- value: targetInput.value
538
- }
539
- });
540
- if (activated.error) return this.failed(uiEffects, diagnostics, activated.error);
541
- applyEffects(overlay, activated.output.formEffects);
542
- }
543
- }
544
- if (!activation && hasProposedValue && target.actions?.keystroke && target.family === "text") {
545
- let program;
546
- try {
547
- program = (0, _embedpdf_core_acrojs.javaScriptProgramFromActionTree)(target.actions.keystroke);
548
- } catch (error) {
549
- return this.failed(uiEffects, diagnostics, scriptError(error instanceof Error ? error.message : String(error)));
550
- }
551
- if (program) {
552
- const oldValue = String(targetInput.value ?? "");
553
- const key = await run("run", program, {
554
- ...inputBase,
555
- fields: overlay.fields,
556
- event: {
557
- kind: "field-keystroke",
558
- target: ref,
559
- source: ref,
560
- value: oldValue,
561
- change: String(proposedValue ?? ""),
562
- selStart: 0,
563
- selEnd: oldValue.length,
564
- willCommit: true
565
- }
566
- });
567
- if (key.error) return this.failed(uiEffects, diagnostics, key.error);
568
- if (!key.output.event.rc) return this.finishRejected(bootEffects, uiEffects, diagnostics);
569
- applyEffects(overlay, key.output.formEffects);
570
- const event = key.output.event;
571
- const base = String(event.value ?? "");
572
- proposedValue = base.slice(0, event.selStart) + event.change + base.slice(Math.max(event.selStart, event.selEnd));
573
- }
574
- }
575
- if (!activation && hasProposedValue) {
576
- targetInput.value = cloneValue(proposedValue);
577
- overlay.resetKeys.delete(refKey(ref));
578
- }
579
- if (!activation && hasProposedValue && target.actions?.validate) {
580
- let program;
581
- try {
582
- program = (0, _embedpdf_core_acrojs.javaScriptProgramFromActionTree)(target.actions.validate);
583
- } catch (error) {
584
- return this.failed(uiEffects, diagnostics, scriptError(error instanceof Error ? error.message : String(error)));
585
- }
586
- if (program) {
587
- const validation = await run("run", program, {
588
- ...inputBase,
589
- fields: overlay.fields,
590
- event: {
591
- kind: "field-validate",
592
- target: ref,
593
- source: ref,
594
- value: proposedValue,
595
- willCommit: true
596
- }
597
- });
598
- if (validation.error) return this.failed(uiEffects, diagnostics, validation.error);
599
- if (!validation.output.event.rc) return this.finishRejected(bootEffects, uiEffects, diagnostics);
600
- targetInput.value = cloneValue(validation.output.event.value);
601
- applyEffects(overlay, validation.output.formEffects);
602
- }
603
- }
604
- const formatRefs = !activation && hasProposedValue ? [ref] : [];
605
- for (const calculationRef of activation ? [] : snapshot.calculationOrder) {
606
- if (!calculationRef) continue;
607
- const calculated = snapshotField(snapshot, calculationRef);
608
- const tree = calculated?.actions?.calculate;
609
- if (!calculated || !tree) continue;
610
- let program;
611
- try {
612
- program = (0, _embedpdf_core_acrojs.javaScriptProgramFromActionTree)(tree);
613
- } catch (error) {
614
- return this.failed(uiEffects, diagnostics, scriptError(error instanceof Error ? error.message : String(error)));
615
- }
616
- const calculatedInput = fieldByRef(overlay.fields, calculationRef);
617
- if (program && calculatedInput) {
618
- const calculation = await run("run", program, {
619
- ...inputBase,
620
- fields: overlay.fields,
621
- event: {
622
- kind: "field-calculate",
623
- target: calculationRef,
624
- ...hasProposedValue ? { source: ref } : {},
625
- value: calculatedInput.value
626
- }
627
- });
628
- if (calculation.error) return this.failed(uiEffects, diagnostics, calculation.error);
629
- applyEffects(overlay, calculation.output.formEffects);
630
- }
631
- if (!formatRefs.some((candidate) => sameRef(candidate, calculationRef))) formatRefs.push(calculationRef);
632
- }
633
- for (const formatRef of formatRefs) {
634
- const field = snapshotField(snapshot, formatRef);
635
- const tree = field?.actions?.format;
636
- const overlayField = fieldByRef(overlay.fields, formatRef);
637
- if (!field || !tree || !overlayField) continue;
638
- let program;
639
- try {
640
- program = (0, _embedpdf_core_acrojs.javaScriptProgramFromActionTree)(tree);
641
- } catch (error) {
642
- return this.failed(uiEffects, diagnostics, scriptError(error instanceof Error ? error.message : String(error)));
643
- }
644
- if (!program) continue;
645
- const format = await run("run", program, {
646
- ...inputBase,
647
- fields: overlay.fields,
648
- event: {
649
- kind: "field-format",
650
- target: formatRef,
651
- ...hasProposedValue ? { source: ref } : {},
652
- value: overlayField.value
653
- }
654
- });
655
- if (format.error) return this.failed(uiEffects, diagnostics, format.error);
656
- applyEffects(overlay, format.output.formEffects);
657
- }
658
- const effects = canonicalEffects(overlay);
659
- if (effects.length + uiEffects.length > budget.maxEffects) return this.failed(uiEffects, diagnostics, {
660
- kind: "budget",
661
- message: `Form script transaction exceeded ${budget.maxEffects} effects`
662
- });
663
- if (effects.length === 0) return {
664
- status: "unchanged",
665
- scripted: true,
666
- effectsResult: null,
667
- uiEffects,
668
- diagnostics
669
- };
670
- if (!this.options.doc.forms.applyEffects) return this.failed(uiEffects, diagnostics, scriptError("This engine does not support batched form effects"));
671
- const effectsResult = await this.options.doc.forms.applyEffects(effects);
672
- const status = statusFromEffects(effectsResult);
673
- return {
674
- status,
675
- scripted: true,
676
- effectsResult,
677
- uiEffects,
678
- diagnostics,
679
- ...status === "failed" ? { error: scriptError("One or more native form effects failed") } : {}
680
- };
681
- }
682
- async sandbox() {
683
- this.sandboxPromise ?? (this.sandboxPromise = this.options.sandboxFactory().then((sandbox) => {
684
- if (this.disposed) sandbox.dispose();
685
- return sandbox;
686
- }));
687
- return this.sandboxPromise;
688
- }
689
- failed(uiEffects, diagnostics, error) {
690
- return {
691
- status: "failed",
692
- scripted: true,
693
- effectsResult: null,
694
- uiEffects,
695
- diagnostics,
696
- error
697
- };
698
- }
699
- async finishRejected(bootEffects, uiEffects, diagnostics) {
700
- if (bootEffects.length === 0) return {
701
- status: "rejected",
702
- scripted: true,
703
- effectsResult: null,
704
- uiEffects,
705
- diagnostics
706
- };
707
- if (!this.options.doc.forms.applyEffects) return this.failed(uiEffects, diagnostics, scriptError("Batched form effects are unavailable"));
708
- const effectsResult = await this.options.doc.forms.applyEffects(bootEffects);
709
- if (statusFromEffects(effectsResult) === "failed") return this.failed(uiEffects, diagnostics, scriptError("A name-tree boot effect failed"));
710
- return {
711
- status: "rejected",
712
- scripted: true,
713
- effectsResult,
714
- uiEffects,
715
- diagnostics
716
- };
717
- }
718
- };
719
- function createFormScriptingController(options) {
720
- return new FormScriptingController(options);
721
- }
316
+ /** The core serial queue under its historical form-plugin name. */
317
+ const createSerialMutationQueue = _embedpdf_core.createSerialQueue;
722
318
  //#endregion
723
319
  //#region src/capability.ts
724
320
  /** PDF user-space rect (y-up) → content-space box (y-down, crop-relative). */
@@ -758,29 +354,22 @@ function createFormCapability(ctx, options = {}) {
758
354
  };
759
355
  };
760
356
  const enqueueMutation = createSerialMutationQueue();
761
- const scripting = options.scripting?.enabled && ctx.doc ? createFormScriptingController({
357
+ const actionsHost = ctx.tryGet(_embedpdf_plugin_actions_contract_host.ActionsToken);
358
+ const realm = actionsHost?.scriptRealm ?? null;
359
+ const scripting = realm && ctx.doc ? require_scripting.createFormScriptingController({
762
360
  doc: ctx.doc,
763
361
  document: () => ctx.document(),
764
- config: options.scripting,
765
- sandboxFactory: options.scripting.sandboxFactory ?? (() => import("@embedpdf/core-js-sandbox").then(({ createQuickJsSandbox }) => createQuickJsSandbox()))
362
+ transaction: realm.transaction.bind(realm),
363
+ budget: realm.budget
766
364
  }) : null;
767
365
  if (scripting) ctx.cleanup(() => scripting.dispose());
768
- let uiEffectProvider = null;
769
- const surfaceScriptingResult = (result) => {
770
- const observe = (callback) => {
771
- if (!callback) return;
772
- try {
773
- callback();
774
- } catch (error) {
775
- globalThis.console?.error("[form] scripting observer failed:", error);
776
- }
777
- };
778
- for (const effect of result.uiEffects) {
779
- observe(options.scripting?.onUiEffect ? () => options.scripting.onUiEffect(effect) : void 0);
780
- observe(uiEffectProvider ? () => uiEffectProvider(effect) : void 0);
781
- }
782
- for (const diagnostic of result.diagnostics) observe(options.scripting?.onDiagnostic ? () => options.scripting.onDiagnostic(diagnostic) : void 0);
783
- if (result.error) observe(options.scripting?.onError ? () => options.scripting.onError(result.error) : void 0);
366
+ /** Every script surface (UI effects, diagnostics, errors) flows through
367
+ * the actions plugin's ONE port — origin/phase attached (D9). */
368
+ const surfaceViaActions = (result, origin) => {
369
+ actionsHost?.surfaceScriptCommit(result, {
370
+ origin,
371
+ realm: "document"
372
+ });
784
373
  };
785
374
  const can = (cap) => ctx.doc?.security.allows(cap) ?? false;
786
375
  let refreshPromise = null;
@@ -861,8 +450,8 @@ function createFormCapability(ctx, options = {}) {
861
450
  const doc = ctx.doc;
862
451
  if (!doc) throw new Error("no document");
863
452
  if (scripting) {
864
- const result = await scripting.commit(await doc.forms.list(), ref, value);
865
- surfaceScriptingResult(result);
453
+ const result = await scripting.commit(ref, value);
454
+ surfaceViaActions(result, "user");
866
455
  if (result.effectsResult !== null) await refresh(true);
867
456
  return result;
868
457
  }
@@ -903,7 +492,7 @@ function createFormCapability(ctx, options = {}) {
903
492
  }
904
493
  });
905
494
  };
906
- const annotationHost = ctx.tryGet(_embedpdf_plugin_annotation_internal.AnnotationToken);
495
+ const annotationHost = ctx.tryGet(_embedpdf_plugin_annotation_contract_host.AnnotationToken);
907
496
  const annotationActivation = async (ref) => {
908
497
  const doc = ctx.doc;
909
498
  if (!doc) return null;
@@ -914,8 +503,7 @@ function createFormCapability(ctx, options = {}) {
914
503
  return annotation?.subtype === "widget" ? annotation.actions?.activate ?? null : null;
915
504
  };
916
505
  const activateWidgetNow = async (key, annotationRef) => {
917
- const doc = ctx.doc;
918
- if (!doc) throw new Error("no document");
506
+ if (!ctx.doc) throw new Error("no document");
919
507
  if (!scripting) return {
920
508
  status: "unchanged",
921
509
  scripted: false,
@@ -931,11 +519,118 @@ function createFormCapability(ctx, options = {}) {
931
519
  uiEffects: [],
932
520
  diagnostics: []
933
521
  };
934
- const result = await scripting.activate(await doc.forms.list(), refKeyOf(key), action);
935
- surfaceScriptingResult(result);
522
+ const result = await scripting.activate(refKeyOf(key), action);
523
+ surfaceViaActions(result, "user");
936
524
  if (result.effectsResult !== null) await refresh(true);
937
525
  return result;
938
526
  };
527
+ const NOT_SCRIPTED = {
528
+ status: "unchanged",
529
+ scripted: false,
530
+ effectsResult: null,
531
+ uiEffects: [],
532
+ diagnostics: []
533
+ };
534
+ /**
535
+ * The shared reset core: one effects batch → refresh → V/C/F
536
+ * recalculation when the transaction port is present. Ridden by BOTH
537
+ * doors — the /ResetForm executor and the public `reset(key)` — so the
538
+ * two can never diverge again (the "reset() asymmetry" fix). `origin` is
539
+ * PRESERVED through recalculation surfacing: a lifecycle/hover ResetForm
540
+ * can no longer launder its alerts into user origin.
541
+ */
542
+ const applyResetBatch = async (refs, origin) => {
543
+ const doc = ctx.doc;
544
+ if (!doc) throw new Error("no document");
545
+ if (!doc.forms.applyEffects) {
546
+ const result = {
547
+ ...NOT_SCRIPTED,
548
+ diagnostics: [{
549
+ code: "unsupported-api",
550
+ message: "this engine has no form-effects batch door"
551
+ }]
552
+ };
553
+ surfaceViaActions(result, origin);
554
+ return result;
555
+ }
556
+ if (refs.length === 0) return NOT_SCRIPTED;
557
+ const effectsResult = await doc.forms.applyEffects([{
558
+ kind: "reset",
559
+ refs
560
+ }]);
561
+ const resetFailed = effectsResult.results.some((entry) => entry.status === "failed" || entry.status === "rejected");
562
+ await refresh(true);
563
+ if (resetFailed) return {
564
+ status: "failed",
565
+ scripted: false,
566
+ effectsResult,
567
+ uiEffects: [],
568
+ diagnostics: []
569
+ };
570
+ let recalc = null;
571
+ if (scripting) {
572
+ recalc = await scripting.recalculate();
573
+ surfaceViaActions(recalc, origin);
574
+ if (recalc.effectsResult !== null) await refresh(true);
575
+ }
576
+ return {
577
+ status: "applied",
578
+ scripted: recalc !== null,
579
+ effectsResult,
580
+ uiEffects: recalc?.uiEffects ?? [],
581
+ diagnostics: recalc?.diagnostics ?? [],
582
+ ...recalc?.error ? { error: recalc.error } : {}
583
+ };
584
+ };
585
+ const resetFormAction = (targets, exclude, origin = "user") => enqueueMutation(async () => {
586
+ const doc = ctx.doc;
587
+ if (!doc) throw new Error("no document");
588
+ const { selected } = resolveFieldSelection((await doc.forms.list()).fields, targets, exclude);
589
+ const resettable = selected.filter((f) => f.family !== "pushbutton" && f.family !== "signature");
590
+ return applyResetBatch(resettable.map((f) => f.ref), origin);
591
+ });
592
+ /**
593
+ * The submit dataset resolver (Phase 4, D7): a FRESH engine read — never
594
+ * the cached model, so no staleness class exists — then the pure ISO
595
+ * builder. Selection/veto/value semantics live in `field-selection.ts`;
596
+ * this door only supplies the live fields and assembles the request.
597
+ */
598
+ const resolveSubmitDataset = async (intent, actionCtx, diagnose) => {
599
+ const doc = ctx.doc;
600
+ if (!doc) throw new Error("no document");
601
+ const snapshot = await doc.forms.list();
602
+ return {
603
+ url: intent.url,
604
+ method: intent.method,
605
+ format: intent.format,
606
+ flagsRaw: intent.flagsRaw,
607
+ ...intent.charSet === void 0 ? {} : { charSet: intent.charSet },
608
+ entries: buildSubmitEntries(snapshot.fields, intent, diagnose),
609
+ origin: actionCtx.origin,
610
+ event: actionCtx.event
611
+ };
612
+ };
613
+ const widgetSource = (key, annotationRef) => ({
614
+ kind: "widget",
615
+ field: refKeyOf(key),
616
+ annotation: annotationRef,
617
+ pon: annotationRef.pageObjectNumber
618
+ });
619
+ /** One shared hover pump (Exit→Enter as one ordered pair; intermediates
620
+ * skipped), created on first use against the resolved actions capability. */
621
+ let widgetPump = null;
622
+ const widgetHoverPump = (actions) => {
623
+ widgetPump ?? (widgetPump = (0, _embedpdf_plugin_actions_contract.createHoverPump)(actions.dispatch));
624
+ return widgetPump;
625
+ };
626
+ const widgetHoverFlags = (annotationRef) => {
627
+ const loaded = annotationHost?.get(annotationRef);
628
+ if (loaded?.subtype !== "widget") return null;
629
+ return {
630
+ enter: Boolean(loaded.actions?.cursorEnter?.root),
631
+ exit: Boolean(loaded.actions?.cursorExit?.root)
632
+ };
633
+ };
939
634
  const nudgeAnnotations = (pons) => {
940
635
  if (!annotationHost) return;
941
636
  for (const pon of new Set(pons)) annotationHost.reloadPage(pon);
@@ -1089,12 +784,27 @@ function createFormCapability(ctx, options = {}) {
1089
784
  key
1090
785
  });
1091
786
  try {
1092
- const result = await doc.forms.reset(refKeyOf(key));
1093
- apply({
1094
- t: "writeDone",
1095
- key,
1096
- field: result.field
1097
- });
787
+ if (doc.forms.applyEffects) {
788
+ const result = await applyResetBatch([refKeyOf(key)], "user");
789
+ if (result.status === "failed") throw new Error(result.effectsResult?.results.find((entry) => entry.status === "failed" || entry.status === "rejected")?.error?.message ?? "reset failed");
790
+ const field = fieldByKey(model(), key);
791
+ if (field) apply({
792
+ t: "writeDone",
793
+ key,
794
+ field
795
+ });
796
+ else apply({
797
+ t: "writeFailed",
798
+ key
799
+ });
800
+ } else {
801
+ const result = await doc.forms.reset(refKeyOf(key));
802
+ apply({
803
+ t: "writeDone",
804
+ key,
805
+ field: result.field
806
+ });
807
+ }
1098
808
  } catch (err) {
1099
809
  apply({
1100
810
  t: "writeFailed",
@@ -1105,9 +815,96 @@ function createFormCapability(ctx, options = {}) {
1105
815
  });
1106
816
  },
1107
817
  commitValue: (ref, value) => enqueueMutation(() => commitValueNow(ref, value)),
1108
- activateWidget: (key, annotationRef) => enqueueMutation(() => activateWidgetNow(key, annotationRef)),
1109
- setUiEffectProvider: (provider) => {
1110
- uiEffectProvider = provider;
818
+ activateWidget: async (key, annotationRef) => {
819
+ const actions = ctx.tryGet(_embedpdf_plugin_actions_contract.ActionsToken);
820
+ if (actions) {
821
+ const result = await actions.dispatch({
822
+ scope: "activate",
823
+ ref: annotationRef,
824
+ pon: annotationRef.pageObjectNumber,
825
+ source: widgetSource(key, annotationRef)
826
+ });
827
+ if (!(result.status === "inert" && result.steps.length === 0 && result.diagnostics.length === 0)) return {
828
+ kind: "dispatched",
829
+ result
830
+ };
831
+ }
832
+ return {
833
+ kind: "form",
834
+ result: await enqueueMutation(() => activateWidgetNow(key, annotationRef))
835
+ };
836
+ },
837
+ notifyWidgetEvent: (key, annotationRef, event) => {
838
+ const actions = ctx.tryGet(_embedpdf_plugin_actions_contract.ActionsToken);
839
+ if (!actions) return;
840
+ const source = widgetSource(key, annotationRef);
841
+ if (event === "cursorEnter" || event === "cursorExit") {
842
+ if (event === "cursorExit") {
843
+ widgetHoverPump(actions).hover(null);
844
+ return;
845
+ }
846
+ const flags = widgetHoverFlags(annotationRef);
847
+ if (flags && !flags.enter && !flags.exit) return;
848
+ widgetHoverPump(actions).hover({
849
+ ref: annotationRef,
850
+ pon: annotationRef.pageObjectNumber,
851
+ source,
852
+ ...flags ? { events: flags } : {}
853
+ });
854
+ return;
855
+ }
856
+ actions.dispatch({
857
+ scope: "annotation",
858
+ event,
859
+ ref: annotationRef,
860
+ pon: annotationRef.pageObjectNumber,
861
+ source
862
+ });
863
+ },
864
+ resetFormAction,
865
+ resolveSubmitDataset,
866
+ commitScriptFormEffects: async (effects) => {
867
+ const doc = ctx.doc;
868
+ if (!doc?.forms.applyEffects) return {
869
+ results: effects.map((_, index) => ({
870
+ index,
871
+ status: "failed",
872
+ fields: [],
873
+ changedWidgets: [],
874
+ error: {
875
+ code: "NotSupported",
876
+ message: "no form-effects batch door"
877
+ }
878
+ })),
879
+ changedWidgets: [],
880
+ meta: null
881
+ };
882
+ let result;
883
+ try {
884
+ result = await doc.forms.applyEffects(effects);
885
+ } catch (error) {
886
+ const message = error instanceof Error ? error.message : String(error);
887
+ return {
888
+ results: effects.map((_, index) => ({
889
+ index,
890
+ status: "failed",
891
+ fields: [],
892
+ changedWidgets: [],
893
+ error: {
894
+ code: "Refused",
895
+ message
896
+ }
897
+ })),
898
+ changedWidgets: [],
899
+ meta: null
900
+ };
901
+ }
902
+ await refresh(true);
903
+ if (annotationHost) {
904
+ const pons = new Set(result.changedWidgets.map((widget) => widget.pageObjectNumber).filter((pon) => pon > 0));
905
+ for (const pon of pons) await annotationHost.reloadPage(pon);
906
+ }
907
+ return result;
1111
908
  },
1112
909
  setValue: (ref, value) => enqueueMutation(async () => {
1113
910
  const doc = ctx.doc;
@@ -1309,11 +1106,11 @@ function createPlaceHandler(form, interaction, annotation) {
1309
1106
  },
1310
1107
  onMove: (s) => {
1311
1108
  if (!origin) return;
1312
- const point = (0, _embedpdf_plugin_interaction.samplePointOn)(s, origin.pon);
1109
+ const point = (0, _embedpdf_plugin_interaction_contract.samplePointOn)(s, origin.pon);
1313
1110
  if (!point) return;
1314
1111
  origin.last = point;
1315
1112
  const box = rectFrom(origin.start, point);
1316
- if (annotation) if (Math.max(box.width, box.height) >= _embedpdf_plugin_annotation.MIN_DRAG) annotation.setPlacementPreview(interaction.activeToolId(), origin.pon, box);
1113
+ if (annotation) if (Math.max(box.width, box.height) >= _embedpdf_plugin_annotation_authoring.MIN_DRAG) annotation.setPlacementPreview(interaction.activeToolId(), origin.pon, box);
1317
1114
  else annotation.clearPlacementPreview();
1318
1115
  },
1319
1116
  onUp: (s) => {
@@ -1324,16 +1121,16 @@ function createPlaceHandler(form, interaction, annotation) {
1324
1121
  const toolId = interaction.activeToolId();
1325
1122
  const tool = FORM_TOOL_BY_ID.get(toolId);
1326
1123
  if (!tool) return;
1327
- const end = (0, _embedpdf_plugin_interaction.samplePointOn)(s, o.pon) ?? o.last;
1124
+ const end = (0, _embedpdf_plugin_interaction_contract.samplePointOn)(s, o.pon) ?? o.last;
1328
1125
  const dragged = rectFrom(o.start, end);
1329
- const isClick = dragged.width < _embedpdf_plugin_annotation.MIN_DRAG && dragged.height < _embedpdf_plugin_annotation.MIN_DRAG;
1126
+ const isClick = dragged.width < _embedpdf_plugin_annotation_authoring.MIN_DRAG && dragged.height < _embedpdf_plugin_annotation_authoring.MIN_DRAG;
1330
1127
  const pageBox = form.pageBox(o.pon);
1331
1128
  const box = isClick ? boxOfClick(o.start, tool.clickCreate, pageBox) : dragged;
1332
1129
  form.placeField({
1333
1130
  family: tool.family,
1334
1131
  pageObjectNumber: o.pon,
1335
1132
  box,
1336
- appearance: (0, _embedpdf_plugin_annotation.widgetAppearanceFromProps)(annotation ? annotation.currentDefaults(toolId) : tool.defaults)
1133
+ appearance: (0, _embedpdf_plugin_annotation_authoring.widgetAppearanceFromProps)(annotation ? annotation.currentDefaults(toolId) : tool.defaults)
1337
1134
  }).then((placed) => {
1338
1135
  if (!annotation || interaction.activeToolId() !== toolId) return;
1339
1136
  const widget = placed.widget;
@@ -1352,7 +1149,7 @@ function createPlaceHandler(form, interaction, annotation) {
1352
1149
  /** The click-create box through the SHARED placement layer (fields are boxes;
1353
1150
  * the policy anchor + page clamp are exactly the annotation click's). */
1354
1151
  function boxOfClick(point, policy, pageBox) {
1355
- const placement = (0, _embedpdf_plugin_annotation.resolveClickPlacement)(point, policy, { pageBox: pageBox ?? void 0 });
1152
+ const placement = (0, _embedpdf_plugin_annotation_authoring.resolveClickPlacement)(point, policy, { pageBox: pageBox ?? void 0 });
1356
1153
  return placement.kind === "box" ? placement.rect : {
1357
1154
  x: point.x,
1358
1155
  y: point.y,
@@ -1366,9 +1163,6 @@ const initialFormState = () => ({ model: initialModel() });
1366
1163
  /** Dumb store: the pure core computes the next model; the shell dispatches it. */
1367
1164
  const formReducer = (state, action) => action.type === "SET_MODEL" ? { model: action.model } : state;
1368
1165
  //#endregion
1369
- //#region src/types.ts
1370
- const FormToken = (0, _embedpdf_core.createCapabilityToken)("form");
1371
- //#endregion
1372
1166
  //#region src/form.plugin.ts
1373
1167
  /**
1374
1168
  * The form plugin: the FIELD plane. Document-scoped; requires the
@@ -1383,17 +1177,17 @@ const FormToken = (0, _embedpdf_core.createCapabilityToken)("form");
1383
1177
  */
1384
1178
  const formPlugin = (options = {}) => (0, _embedpdf_core.definePlugin)({
1385
1179
  id: "form",
1386
- token: FormToken,
1180
+ token: require_types.FormToken,
1387
1181
  scope: "document",
1388
- requires: [_embedpdf_plugin_interaction.InteractionToken],
1389
- optional: [_embedpdf_plugin_annotation.AnnotationToken],
1182
+ requires: [_embedpdf_plugin_interaction_contract.InteractionToken],
1183
+ optional: [_embedpdf_plugin_annotation_contract.AnnotationToken, _embedpdf_plugin_actions_contract.ActionsToken],
1390
1184
  initialState: initialFormState,
1391
1185
  reduce: formReducer,
1392
1186
  capability: (ctx) => createFormCapability(ctx, options),
1393
1187
  effects: registerFormEffects,
1394
1188
  init: (ctx) => {
1395
- const interaction = ctx.get(_embedpdf_plugin_interaction.InteractionToken);
1396
- const form = ctx.get(FormToken);
1189
+ const interaction = ctx.get(_embedpdf_plugin_interaction_contract.InteractionToken);
1190
+ const form = ctx.get(require_types.FormToken);
1397
1191
  interaction.registerTool({
1398
1192
  id: "form-fill",
1399
1193
  cursor: "default",
@@ -1404,7 +1198,7 @@ const formPlugin = (options = {}) => (0, _embedpdf_core.definePlugin)({
1404
1198
  cursor: "default",
1405
1199
  enables: /* @__PURE__ */ new Set(["annotation-edit", "annotation-marquee"])
1406
1200
  });
1407
- const annotation = ctx.tryGet(_embedpdf_plugin_annotation_internal.AnnotationToken);
1201
+ const annotation = ctx.tryGet(_embedpdf_plugin_annotation_contract_host.AnnotationToken);
1408
1202
  for (const t of FORM_TOOLS) if (annotation) annotation.registerTool({
1409
1203
  id: t.id,
1410
1204
  subtype: t.visualKind,
@@ -1424,14 +1218,57 @@ const formPlugin = (options = {}) => (0, _embedpdf_core.definePlugin)({
1424
1218
  matches: (a) => a.subtype.startsWith("widget"),
1425
1219
  engaged: () => interaction.activeTool()?.enables.has("form-fill") ?? false
1426
1220
  });
1221
+ const actions = ctx.tryGet(_embedpdf_plugin_actions_contract_host.ActionsToken);
1222
+ if (actions) {
1223
+ ctx.cleanup(actions.registerFormCommitSink((effects) => {
1224
+ const formHost = ctx.tryGet(require_types.FormToken);
1225
+ if (!formHost) return Promise.resolve({
1226
+ results: effects.map((_, index) => ({
1227
+ index,
1228
+ status: "failed",
1229
+ fields: [],
1230
+ changedWidgets: []
1231
+ })),
1232
+ changedWidgets: [],
1233
+ meta: null
1234
+ });
1235
+ return formHost.commitScriptFormEffects(effects);
1236
+ }));
1237
+ ctx.cleanup(actions.registerSubmitResolver((intent, actionCtx, diagnose) => {
1238
+ const formHost = ctx.tryGet(require_types.FormToken);
1239
+ if (!formHost) throw new Error("form plugin unavailable");
1240
+ return formHost.resolveSubmitDataset(intent, actionCtx, diagnose);
1241
+ }));
1242
+ ctx.cleanup(actions.registerExecutor("reset-form", async (node, actionCtx) => {
1243
+ if (node.type !== "reset-form") return {
1244
+ status: "inert",
1245
+ reason: "not a reset-form node"
1246
+ };
1247
+ const formHost = ctx.tryGet(require_types.FormToken);
1248
+ if (!formHost) return {
1249
+ status: "inert",
1250
+ reason: "form plugin unavailable"
1251
+ };
1252
+ const result = await formHost.resetFormAction(node.fields, node.exclude, actionCtx.origin);
1253
+ if (result.status === "failed") return {
1254
+ status: "failed",
1255
+ error: result.error?.message ?? "reset failed"
1256
+ };
1257
+ if (result.effectsResult === null) return {
1258
+ status: "inert",
1259
+ reason: result.diagnostics[0]?.message ?? "reset-form resolved no fields"
1260
+ };
1261
+ return { status: "executed" };
1262
+ }));
1263
+ }
1427
1264
  }
1428
1265
  });
1429
1266
  //#endregion
1430
1267
  exports.FORM_TOOLS = FORM_TOOLS;
1431
1268
  exports.FORM_TOOL_BY_ID = FORM_TOOL_BY_ID;
1432
- exports.FormScriptingController = FormScriptingController;
1433
- exports.FormToken = FormToken;
1434
- exports.createFormScriptingController = createFormScriptingController;
1269
+ exports.FormScriptingController = require_scripting.FormScriptingController;
1270
+ exports.FormToken = require_contract.FormToken;
1271
+ exports.createFormScriptingController = require_scripting.createFormScriptingController;
1435
1272
  exports.createSerialMutationQueue = createSerialMutationQueue;
1436
1273
  exports.fieldKeyOf = fieldKeyOf;
1437
1274
  exports.formPlugin = formPlugin;