attaform 0.29.0 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/abstract.d.mts +15 -21
  2. package/dist/abstract.mjs +4 -4
  3. package/dist/dev/abstract.mjs +4 -4
  4. package/dist/dev/chunks/dev-key-collision-warnings.mjs +47 -8
  5. package/dist/dev/chunks/devtools.mjs +2 -2
  6. package/dist/dev/directive.mjs +1 -1
  7. package/dist/dev/history.mjs +1 -2
  8. package/dist/dev/index.mjs +5 -5
  9. package/dist/dev/runtime/plugins/attaform.mjs +3 -3
  10. package/dist/dev/shared/{attaform-dev.BO-71R54.mjs → attaform-dev.BJK1kqHY.mjs} +44 -198
  11. package/dist/dev/shared/{attaform-dev.D8aoKb89.mjs → attaform-dev.BMQ3GcgD.mjs} +51 -64
  12. package/dist/dev/shared/{attaform-dev.BgM6FdtA.mjs → attaform-dev.BUIFcTYf.mjs} +94 -62
  13. package/dist/dev/shared/{attaform-dev.q6UutkCx.mjs → attaform-dev.CT7MDjPy.mjs} +92 -152
  14. package/dist/dev/shared/{attaform-dev.ButTkwHd.mjs → attaform-dev.CZz4GYZ6.mjs} +26 -15
  15. package/dist/dev/shared/{attaform-dev.DD5m2W51.mjs → attaform-dev.Chm4D6SJ.mjs} +2 -4
  16. package/dist/dev/shared/{attaform-dev.DrzObAZo.mjs → attaform-dev.DuEE4GU1.mjs} +4 -4
  17. package/dist/dev/shared/{attaform-dev.DHuydLAC.mjs → attaform-dev.DxHdkkPG.mjs} +674 -734
  18. package/dist/dev/shared/{attaform-dev.A-TpJW0B.mjs → attaform-dev.zlvDr0WB.mjs} +3 -21
  19. package/dist/dev/zod-v3.mjs +6 -6
  20. package/dist/dev/zod-v4.mjs +6 -6
  21. package/dist/dev/zod.mjs +5 -5
  22. package/dist/directive.d.mts +4 -4
  23. package/dist/directive.mjs +1 -1
  24. package/dist/esbuild.d.mts +11 -0
  25. package/dist/esbuild.mjs +1 -1
  26. package/dist/history.d.mts +3 -32
  27. package/dist/history.mjs +1 -2
  28. package/dist/index.d.mts +67 -70
  29. package/dist/index.mjs +5 -5
  30. package/dist/nuxt.d.mts +6 -14
  31. package/dist/nuxt.mjs +3 -7
  32. package/dist/rollup.d.mts +11 -0
  33. package/dist/rollup.mjs +1 -1
  34. package/dist/rspack.d.mts +19 -11
  35. package/dist/rspack.mjs +1 -1
  36. package/dist/runtime/components/AttaformDevtoolsPanel.vue +12 -12
  37. package/dist/runtime/components/DevtoolsValueTree.vue +2 -2
  38. package/dist/runtime/plugins/attaform.mjs +3 -3
  39. package/dist/shared/{attaform.CIWLMY-R.d.mts → attaform.2FslhB9R.d.mts} +83 -95
  40. package/dist/shared/{attaform.Cvao8bW5.mjs → attaform.8mfKZCwH.mjs} +7 -7
  41. package/dist/shared/{attaform.DRUT703x.d.mts → attaform.B-Tb62qV.d.mts} +12 -12
  42. package/dist/shared/{attaform.v0fQtWyA.mjs → attaform.B1rH9ht6.mjs} +1 -1
  43. package/dist/shared/{attaform.BndjUUYX.mjs → attaform.B2iebkPA.mjs} +4 -4
  44. package/dist/shared/{attaform.DhnPb6nK.mjs → attaform.BAf3UNn4.mjs} +1 -1
  45. package/dist/shared/{attaform.CZMAYwSK.mjs → attaform.BIWicztt.mjs} +651 -678
  46. package/dist/shared/attaform.BcQwgWaq.d.mts +2153 -0
  47. package/dist/shared/attaform.Br8EBn5B.d.mts +4553 -0
  48. package/dist/shared/{attaform.C2MVyuxC.mjs → attaform.CO918TdU.mjs} +44 -198
  49. package/dist/shared/{attaform.CZYt_5N_.mjs → attaform.CdZIEf7_.mjs} +1 -19
  50. package/dist/shared/{attaform.B0dKL8ST.mjs → attaform.DRsANMok.mjs} +94 -62
  51. package/dist/shared/{attaform.DffRL5pP.mjs → attaform.D_f1WgYf.mjs} +46 -59
  52. package/dist/shared/{attaform.D1fnTifF.mjs → attaform.D_pt1gOj.mjs} +92 -150
  53. package/dist/shared/attaform.L0kUn3vL.d.mts +42 -0
  54. package/dist/shared/{attaform.D0x-qSXm.mjs → attaform.WiGdIqgx.mjs} +1 -3
  55. package/dist/shared/{attaform.BK0cgu2K.mjs → attaform.ZYn2ahzJ.mjs} +3 -3
  56. package/dist/transforms.d.mts +153 -26
  57. package/dist/transforms.mjs +1 -1
  58. package/dist/vite.d.mts +29 -52
  59. package/dist/vite.mjs +8 -3
  60. package/dist/webpack.d.mts +20 -12
  61. package/dist/webpack.mjs +1 -1
  62. package/dist/zod-v3.d.mts +90 -96
  63. package/dist/zod-v3.mjs +6 -6
  64. package/dist/zod-v4.d.mts +119 -119
  65. package/dist/zod-v4.mjs +6 -6
  66. package/dist/zod.d.mts +5 -5
  67. package/dist/zod.mjs +5 -5
  68. package/package.json +9 -7
  69. package/skills/attaform/SKILL.md +1 -1
  70. package/skills/attaform/references/custom-components.md +1 -1
  71. package/dist/chunks/fingerprint.mjs +0 -183
  72. package/dist/chunks/fingerprint2.mjs +0 -159
  73. package/dist/dev/chunks/fingerprint.mjs +0 -183
  74. package/dist/dev/chunks/fingerprint2.mjs +0 -159
  75. package/dist/dev/shared/attaform-dev.BxfsuwBR.mjs +0 -44
  76. package/dist/shared/attaform.BxfsuwBR.mjs +0 -44
  77. package/dist/shared/attaform.CsoeRi3P.d.mts +0 -5155
  78. package/dist/shared/attaform.DDtqHYv1.d.mts +0 -2465
  79. package/dist/shared/attaform.axHXVQO7.d.mts +0 -44
@@ -1,12 +1,12 @@
1
- import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, reactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope, nextTick } from 'vue';
2
- import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform.D0x-qSXm.mjs';
3
- import { c as armDomBinding } from './attaform.DffRL5pP.mjs';
1
+ import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, reactive, shallowReactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope, nextTick } from 'vue';
2
+ import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform.WiGdIqgx.mjs';
3
+ import { c as armDomBinding } from './attaform.D_f1WgYf.mjs';
4
4
  import { R as REGISTER_OWNER_MARKER, b as V_REGISTER_MARKER } from './attaform.KvlEedlF.mjs';
5
- import { A as ANONYMOUS_FORM_KEY_PREFIX, d as consumerHas, i as isShadowedKey, e as safeOwnHas, a as safeOwnRead, f as spreadConsumerRecord, g as copyConsumerArray, r as readConsumerIndex, b as safeAssign, h as consumerKeys, j as readConsumerProp, k as diffAndApply, p as pickDefined, s as structuralSnapshot, n as normalizeNumericOption, l as DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS, m as consumerSymbolKeys, o as applyChangedKeys, R as RESERVED_KEY_PREFIX, q as DEFAULT_MAX_RECURSION_DEPTH, t as ANONYMOUS_WIZARD_KEY_PREFIX } from './attaform.Cvao8bW5.mjs';
6
- import { i as isPathPrefix, e as canonicalizePath, j as cellEntriesFor, s as segmentsForPathKey, k as keyForSegments, m as mapSegmentKeys, b as ROOT_PATH_KEY, S as SubmitErrorHandlerError, t as toError, l as groupErrorsByKey, n as normalizeErrorInput, h as SET_MEMBER_SEGMENT, R as ROOT_PATH, o as normalizeErrorInputs, q as coerceToPathKey, a as InvalidUseFormConfigError, d as ReservedFormKeyError } from './attaform.CZYt_5N_.mjs';
5
+ import { A as ANONYMOUS_FORM_KEY_PREFIX, i as isShadowedKey, d as safeOwnHas, a as safeOwnRead, e as consumerHas, f as spreadConsumerRecord, g as copyConsumerArray, r as readConsumerIndex, b as safeAssign, h as consumerKeys, j as readConsumerProp, k as diffAndApply, p as pickDefined, s as structuralSnapshot, n as normalizeNumericOption, l as DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS, m as consumerSymbolKeys, o as applyChangedKeys, R as RESERVED_KEY_PREFIX, q as DEFAULT_MAX_RECURSION_DEPTH, c as callConsumerFn, t as ANONYMOUS_WIZARD_KEY_PREFIX } from './attaform.8mfKZCwH.mjs';
6
+ import { i as isPathPrefix, e as canonicalizePath, k as keyForSegments, m as mapSegmentKeys, b as ROOT_PATH_KEY, s as segmentsForPathKey, S as SubmitErrorHandlerError, t as toError, j as groupErrorsByKey, n as normalizeErrorInput, h as SET_MEMBER_SEGMENT, R as ROOT_PATH, l as normalizeErrorInputs, a as InvalidUseFormConfigError, d as ReservedFormKeyError } from './attaform.CdZIEf7_.mjs';
7
7
 
8
8
  const AttaformErrorCode = {
9
- /** A required field is in the blank set — user hasn't supplied a value. */
9
+ /** A required field is in the blank set: no value was supplied. */
10
10
  NoValueSupplied: "atta:no-value-supplied",
11
11
  /** The schema adapter's `validateAtPath` threw synchronously. */
12
12
  AdapterThrew: "atta:adapter-threw",
@@ -78,11 +78,6 @@ function makeBlankRequiredError(segments) {
78
78
  }
79
79
  const NO_ERRORS = Object.freeze([]);
80
80
 
81
- function crossCopyState(key, create) {
82
- const carrier = globalThis;
83
- return carrier[key] ?? (carrier[key] = create());
84
- }
85
-
86
81
  function isGateOpen(field, formMeta) {
87
82
  return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
88
83
  }
@@ -104,45 +99,32 @@ function earliestNonNull(a, b) {
104
99
  return a < b ? a : b;
105
100
  }
106
101
  const DEFAULT_TIMINGS = { showDelay: 120, minVisible: 120 };
102
+ const { showDelay, minVisible } = DEFAULT_TIMINGS;
107
103
  const FOCUS_OUT_GRACE = 16;
108
- const defaultFamily = crossCopyState(
109
- /* @__PURE__ */ Symbol.for("attaform:default-display-state-family"),
110
- () => /* @__PURE__ */ new WeakSet()
111
- );
112
- function isDefaultDisplayState(fn) {
113
- return defaultFamily.has(fn);
114
- }
115
- function makeDefaultDisplayState({
116
- showDelay,
117
- minVisible
118
- }) {
119
- const reducer = (prev, { field, formMeta, validatingSince, transformingSince, now }) => {
120
- const verdict = computeVerdict(field, formMeta);
121
- if (!isGateOpen(field, formMeta)) return { display: verdict };
122
- const inFlightSince = earliestNonNull(validatingSince, transformingSince);
123
- if (inFlightSince === null) {
124
- if (prev.display === "pending") {
125
- const shownAt = prev.pendingShownAt ?? now;
126
- if (now < shownAt + minVisible)
127
- return { display: "pending", pendingShownAt: shownAt, reviewAt: shownAt + minVisible };
128
- }
129
- return { display: verdict };
130
- }
131
- if (prev.display === "pending")
132
- return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
133
- const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
134
- if (now - inFlightSince < window) {
135
- return { display: prev.display, reviewAt: inFlightSince + window };
104
+ const defaultDisplayState = (prev, { field, formMeta, validatingSince, transformingSince, now }) => {
105
+ const verdict = computeVerdict(field, formMeta);
106
+ if (!isGateOpen(field, formMeta)) return { display: verdict };
107
+ const inFlightSince = earliestNonNull(validatingSince, transformingSince);
108
+ if (inFlightSince === null) {
109
+ if (prev.display === "pending") {
110
+ const shownAt = prev.pendingShownAt ?? now;
111
+ if (now < shownAt + minVisible)
112
+ return {
113
+ display: "pending",
114
+ pendingShownAt: shownAt,
115
+ reviewAt: shownAt + minVisible
116
+ };
136
117
  }
137
- return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
138
- };
139
- defaultFamily.add(reducer);
140
- return reducer;
141
- }
142
- const defaultDisplayState = makeDefaultDisplayState(DEFAULT_TIMINGS);
143
- function resolveGetDisplayState(config) {
144
- return config ?? defaultDisplayState;
145
- }
118
+ return { display: verdict };
119
+ }
120
+ if (prev.display === "pending")
121
+ return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
122
+ const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
123
+ if (now - inFlightSince < window) {
124
+ return { display: prev.display, reviewAt: inFlightSince + window };
125
+ }
126
+ return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
127
+ };
146
128
 
147
129
  function hashStableString(input, seed = 0) {
148
130
  let h1 = 3735928559 ^ seed;
@@ -230,16 +212,27 @@ function descendStep(value, segment) {
230
212
  }
231
213
  function getAtPath(root, path) {
232
214
  if (path.length === 0) return root;
233
- let current = root;
234
- for (const segment of path) {
235
- const next = descendStep(current, segment);
236
- if (next === NOT_FOUND) return void 0;
237
- current = next;
215
+ try {
216
+ let current = root;
217
+ for (const segment of path) {
218
+ const next = descendStep(current, segment);
219
+ if (next === NOT_FOUND) return void 0;
220
+ current = next;
221
+ }
222
+ return current;
223
+ } catch {
224
+ return void 0;
238
225
  }
239
- return current;
240
226
  }
241
227
  function hasAtPath(root, path) {
242
228
  if (path.length === 0) return true;
229
+ try {
230
+ return hasAtPathUnguarded(root, path);
231
+ } catch {
232
+ return false;
233
+ }
234
+ }
235
+ function hasAtPathUnguarded(root, path) {
243
236
  let current = root;
244
237
  for (let i = 0; i < path.length - 1; i++) {
245
238
  const segment = path[i];
@@ -266,29 +259,15 @@ function isPlainRecord(value) {
266
259
  const proto = Object.getPrototypeOf(value);
267
260
  return proto === null || proto === Object.prototype;
268
261
  }
262
+ const NO_SCHEMA_DEFAULTS = /* @__PURE__ */ new Set();
263
+ const NO_SCHEMA_FILL = {
264
+ getDefaultAtPath: () => void 0,
265
+ arrayShapeAtPath: () => null,
266
+ getSlimPrimitiveTypesAtPath: () => NO_SCHEMA_DEFAULTS,
267
+ entryKeyKindAtPath: () => void 0
268
+ };
269
269
  function setAtPath(root, path, value) {
270
- return setAtPathOffset(root, path, value, 0);
271
- }
272
- function setAtPathOffset(root, path, value, offset) {
273
- if (offset >= path.length) return value;
274
- const head = path[offset];
275
- const nextOffset = offset + 1;
276
- if (root instanceof Map) {
277
- const next = new Map(root);
278
- const key = mapKeyForSegment(root, head);
279
- next.set(key, setAtPathOffset(root.get(key), path, value, nextOffset));
280
- return next;
281
- }
282
- if (!isRebuildableContainer(root, head)) return root;
283
- if (typeof head === "number") {
284
- const arr = Array.isArray(root) ? [...root] : [];
285
- while (arr.length <= head) arr.push(void 0);
286
- arr[head] = setAtPathOffset(arr[head], path, value, nextOffset);
287
- return arr;
288
- }
289
- const rec = isPlainRecord(root) ? { ...root } : {};
290
- safeAssign(rec, head, setAtPathOffset(safeOwnRead(rec, head), path, value, nextOffset));
291
- return rec;
270
+ return setAtPathWithSchemaFill(root, NO_SCHEMA_FILL, path, value);
292
271
  }
293
272
  const NO_IN_PLACE = { applied: false };
294
273
  function tryInPlaceLeafWrite(root, path, value) {
@@ -494,10 +473,10 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
494
473
  }
495
474
  const rec = isPlainRecord(root) ? { ...root } : {};
496
475
  if (isLeafStep) {
497
- rec[head] = value;
476
+ safeAssign(rec, head, value);
498
477
  return rec;
499
478
  }
500
- const existing = rec[head];
479
+ const existing = safeOwnRead(rec, head);
501
480
  let childRoot;
502
481
  if (existing === void 0 || existing !== null && typeof existing !== "object") {
503
482
  const intermPath = [...fullPath.slice(0, startIdx + 1)];
@@ -506,7 +485,11 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
506
485
  } else {
507
486
  childRoot = existing;
508
487
  }
509
- rec[head] = setAtPathWithSchemaFillImpl(childRoot, schema, fullPath, value, startIdx + 1);
488
+ safeAssign(
489
+ rec,
490
+ head,
491
+ setAtPathWithSchemaFillImpl(childRoot, schema, fullPath, value, startIdx + 1)
492
+ );
510
493
  return rec;
511
494
  }
512
495
 
@@ -523,16 +506,15 @@ function isUnderStubAncestor(state, segments) {
523
506
  }
524
507
  return false;
525
508
  }
526
- function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, sweep, options) {
509
+ function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, sweep) {
527
510
  const cache = /* @__PURE__ */ new Map();
528
- const predicate = options?.getDisplayState;
529
511
  sweep.onEvict((key) => cache.delete(key));
530
512
  return function getFieldState(pathInput) {
531
513
  const { segments, key } = canonicalizePath(pathInput);
532
514
  const cached = cache.get(key);
533
515
  if (cached !== void 0) return cached;
534
516
  const c = computed(
535
- () => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase, predicate) : buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase, predicate)
517
+ () => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase) : buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase)
536
518
  );
537
519
  cache.set(key, c);
538
520
  sweep.track(segments, key);
@@ -549,7 +531,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
549
531
  const record = state.fields.get(key);
550
532
  const value = state.getValueAtPath(segments);
551
533
  const original = state.originals.get(key)?.value;
552
- const pristine = state.isPristineAtPath(segments);
534
+ const pristine = state.isPristineAtPathByKey(key, segments);
553
535
  const cell = state.errorCells.get(key);
554
536
  const schemaForKey = cell !== void 0 && cell.schema.length > 0 ? cell.schema : void 0;
555
537
  const blankForKey = state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments) ? [makeBlankRequiredError(segments)] : void 0;
@@ -561,7 +543,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
561
543
  const validating = (state.fieldValidationCounts.get(key) ?? 0) > 0;
562
544
  const transforming = (state.fieldTransformCounts.get(key) ?? 0) > 0;
563
545
  const transformError = state.transformErrors.get(key) ?? null;
564
- const gated = state.pathHasAsyncValidation(segments) && !state.firstValidationDone.value;
546
+ const gated = state.pathHasAsyncValidationByKey(key, segments) && !state.firstValidationDone.value;
565
547
  const isOrphan = segments.length > 0 && !hasAtPath(state.form.value, segments) && isUnderStubAncestor(state, segments);
566
548
  const valid = !gated && errors.length === 0 && !validating && !isOrphan;
567
549
  const elementRecord = state.domBinding.value?.elements.get(key);
@@ -584,9 +566,8 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
584
566
  updatedAt: record?.updatedAt ?? null,
585
567
  errors,
586
568
  // A leaf has no descendants, so its own-bucket errors ARE its subtree
587
- // errors. Reuse the same array reference: referential `ownErrors ===
588
- // errors`, and no second read of the whole-form `derivedBlankErrors`
589
- // map (preserves the P3 blank-reactivity optimization above).
569
+ // errors. Reuse the array reference, which makes `ownErrors === errors`
570
+ // and avoids a second read of the whole-form `derivedBlankErrors` map.
590
571
  ownErrors: errors,
591
572
  validating,
592
573
  valid,
@@ -604,7 +585,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
604
585
  meta: resolved.meta
605
586
  };
606
587
  }
607
- function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
588
+ function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase) {
608
589
  const base = buildLeafFieldStateBase(state, segments, key, formInstanceId);
609
590
  const validatingSince = state.fieldValidatingSince.get(key) ?? null;
610
591
  const transformingSince = state.fieldTransformingSince.get(key) ?? null;
@@ -617,9 +598,8 @@ function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBa
617
598
  transformingSince,
618
599
  false,
619
600
  // revealedDescendantError: leaves have no descendants
620
- false,
601
+ false
621
602
  // isRoot: a leaf is never the form root
622
- getDisplayState
623
603
  );
624
604
  }
625
605
  function visitActiveLeafPaths(value, base, visit) {
@@ -717,7 +697,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
717
697
  pristine = false;
718
698
  dirty = true;
719
699
  }
720
- const errors = aggregateErrorsAt(state, segments);
700
+ const errors = aggregateErrorsAt(state, segments, key);
721
701
  const revealedDescendantError = errors.length > 0 && errors.some((e) => {
722
702
  const ePath = e.path;
723
703
  if (ePath.length === segments.length && ePath.every((s, i) => s === segments[i])) return false;
@@ -725,7 +705,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
725
705
  if (ePath.length === 1 && ePath[0] === "") return blurredAfterInteraction;
726
706
  return blurredLeafSegments.some((s) => isPathPrefix(ePath, s));
727
707
  });
728
- if (!asyncPending && state.pathHasAsyncValidation(segments)) asyncPending = true;
708
+ if (!asyncPending && state.pathHasAsyncValidationByKey(key, segments)) asyncPending = true;
729
709
  if ((state.fieldValidationCounts.get(key) ?? 0) > 0) {
730
710
  validating = true;
731
711
  const since = state.fieldValidatingSince.get(key);
@@ -767,19 +747,19 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
767
747
  elements: EMPTY_ELEMENTS,
768
748
  updatedAt,
769
749
  errors,
770
- // The container's OWN bucket only (this exact path), excluding every
771
- // descendant that `errors` rolls up: a cross-field `.refine()` here,
772
- // or `setErrors` pinned at this node. The same raw union the leaf
773
- // builds from the three stores; the container already depends on all
774
- // three via `aggregateErrorsAt`, so there's no new reactivity cost.
750
+ // The container's OWN bucket at this exact path, excluding every
751
+ // descendant `errors` rolls up: a cross-field `.refine()` here, or a
752
+ // `setErrors` pinned at this node. The same raw union the leaf builds
753
+ // from the three stores, and the container already depends on all three
754
+ // through `aggregateErrorsAt`, so it costs no new reactivity.
775
755
  ownErrors: state.getErrorsForPath(segments),
776
756
  validating,
777
757
  valid,
778
758
  transforming,
779
759
  busy: transforming || validating,
780
- // A container surfaces its OWN transform failure (a transform registered
781
- // on the container path, e.g. a file normalizer) but never rolls up a
782
- // descendant leaf's failure — that stays a per-field channel.
760
+ // A container surfaces its OWN transform failure, from a transform
761
+ // registered on the container path such as a file normalizer, and never
762
+ // rolls up a descendant's: that stays a per-field channel.
783
763
  transformError: ownTransformError,
784
764
  path: segments,
785
765
  ...computeFieldIdentity(formInstanceId, state.formKey, key),
@@ -796,7 +776,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
796
776
  revealedDescendantError
797
777
  };
798
778
  }
799
- function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
779
+ function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase) {
800
780
  const { base, validatingSince, transformingSince, revealedDescendantError } = buildContainerFieldStateBase(state, segments, key, formInstanceId);
801
781
  return decorateWithDerivedProps(
802
782
  base,
@@ -806,14 +786,12 @@ function buildContainerFieldState(state, segments, key, formInstanceId, getFormM
806
786
  validatingSince,
807
787
  transformingSince,
808
788
  revealedDescendantError,
809
- segments.length === 0,
810
- getDisplayState
789
+ segments.length === 0
811
790
  );
812
791
  }
813
- function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, transformingSince, revealedDescendantError, isRoot, getDisplayState) {
792
+ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, transformingSince, revealedDescendantError, isRoot) {
814
793
  const firstError = base.errors[0];
815
794
  const firstOwnError = base.ownErrors[0];
816
- const predicate = getDisplayState ?? state.getDisplayState;
817
795
  const formMeta = getFormMetaBase();
818
796
  const ctx = {
819
797
  field: base,
@@ -824,16 +802,9 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
824
802
  // flight) so the reducer returns the plain verdict and hydration matches.
825
803
  now: state.ssr ? 0 : Date.now()
826
804
  };
827
- let machine;
828
- let rollupApplies = isDefaultDisplayState(predicate);
829
- try {
830
- machine = state.displayEngine.resolve(key, ctx, predicate);
831
- } catch (err) {
832
- machine = state.displayEngine.resolve(key, ctx, defaultDisplayState);
833
- rollupApplies = true;
834
- }
835
- const submitValidating = rollupApplies && isRoot && state.submitting.value && state.activeValidations.value > 0;
836
- const resolvedDisplayState = machine.display === "pending" || submitValidating ? "pending" : rollupApplies && revealedDescendantError ? "error" : machine.display;
805
+ const machine = state.displayEngine.resolve(key, ctx, defaultDisplayState);
806
+ const submitValidating = isRoot && state.submitting.value && state.activeValidations.value > 0;
807
+ const resolvedDisplayState = machine.display === "pending" || submitValidating ? "pending" : revealedDescendantError ? "error" : machine.display;
837
808
  const displayState = base.disabled === true ? "idle" : resolvedDisplayState;
838
809
  return {
839
810
  ...base,
@@ -846,26 +817,33 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
846
817
  firstOwnError
847
818
  };
848
819
  }
849
- function aggregateErrorsAt(state, prefix) {
820
+ function aggregateErrorsAt(state, prefix, prefixKey) {
821
+ const candidates = state.errorWindowAt(prefix, prefixKey);
822
+ if (candidates.length === 0) return [];
850
823
  const formValue = state.form.value;
824
+ const cells = state.errorCells;
851
825
  const buckets = /* @__PURE__ */ new Map();
852
- const collect = (errs) => {
853
- for (const [pathKey, list] of errs) {
854
- if (list.length === 0) continue;
855
- const segs = segmentsForPathKey(pathKey);
856
- if (segs === null) continue;
857
- if (!isPathPrefix(prefix, segs)) continue;
858
- if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
859
- const ordinal = state.ensurePathOrdinal(pathKey);
860
- const existing = buckets.get(ordinal);
861
- if (existing === void 0) buckets.set(ordinal, [...list]);
862
- else existing.push(...list);
863
- }
864
- };
865
- collect(cellEntriesFor(state.errorCells, "schema"));
866
- collect(state.derivedBlankErrors.value);
867
- collect(cellEntriesFor(state.errorCells, "user"));
826
+ for (const { key, segments } of candidates) {
827
+ if (!isPathPrefix(prefix, segments)) continue;
828
+ if (segments.length > 0 && !hasAtPath(formValue, segments)) continue;
829
+ const cell = cells.get(key);
830
+ const blankList = state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments) ? [makeBlankRequiredError(segments)] : void 0;
831
+ const schemaList = cell?.schema;
832
+ const userList = cell?.user;
833
+ const total = (schemaList?.length ?? 0) + (blankList?.length ?? 0) + (userList?.length ?? 0);
834
+ if (total === 0) continue;
835
+ const ordinal = state.ensurePathOrdinal(key);
836
+ let bucket = buckets.get(ordinal);
837
+ if (bucket === void 0) {
838
+ bucket = [];
839
+ buckets.set(ordinal, bucket);
840
+ }
841
+ if (schemaList !== void 0) bucket.push(...schemaList);
842
+ if (blankList !== void 0) bucket.push(...blankList);
843
+ if (userList !== void 0) bucket.push(...userList);
844
+ }
868
845
  if (buckets.size === 0) return [];
846
+ if (buckets.size === 1) return [...buckets.values()][0];
869
847
  return [...buckets.entries()].sort(([a], [b]) => a - b).flatMap(([, errs]) => errs);
870
848
  }
871
849
  const EMPTY_ELEMENTS = Object.freeze([]);
@@ -957,7 +935,7 @@ function buildTree(spec) {
957
935
  existsCache.delete(key);
958
936
  });
959
937
  function schemaHasPath(segs) {
960
- const cacheKey = JSON.stringify(segs);
938
+ const cacheKey = keyForSegments(segs).key;
961
939
  const cached = existsCache.get(cacheKey);
962
940
  if (cached !== void 0) return cached;
963
941
  const result = spec.schema.getSlimPrimitiveTypesAtPath(segs).size > 0;
@@ -972,7 +950,7 @@ function buildTree(spec) {
972
950
  }
973
951
  function containerAt(segments) {
974
952
  const isArrayLike = spec.isArrayAt(segments);
975
- const pathKey = JSON.stringify(segments);
953
+ const pathKey = keyForSegments(segments).key;
976
954
  const cacheKey = `${pathKey}+${isArrayLike ? "A" : "O"}`;
977
955
  const existing = containerCache.get(cacheKey);
978
956
  if (existing !== void 0) return existing;
@@ -1072,7 +1050,7 @@ function buildErrorsSurface(state, sweep) {
1072
1050
  const treeCache = /* @__PURE__ */ new Map();
1073
1051
  sweep.onEvict((key) => treeCache.delete(key));
1074
1052
  const materialize = (segments) => {
1075
- const cacheKey = JSON.stringify(segments);
1053
+ const cacheKey = keyForSegments(segments).key;
1076
1054
  let tree = treeCache.get(cacheKey);
1077
1055
  if (tree === void 0) {
1078
1056
  const frozen = [...segments];
@@ -1084,28 +1062,21 @@ function buildErrorsSurface(state, sweep) {
1084
1062
  };
1085
1063
  const resolveLeaf = (path) => {
1086
1064
  const isContainerSelfAccess = path.length > 1 && path[path.length - 1] === "";
1087
- const collectAtKey = (key2, active2, into) => {
1088
- const cell = state.errorCells.get(key2);
1089
- if (active2) {
1090
- const b = state.derivedBlankErrors.value.get(key2);
1091
- if (cell !== void 0) into.push(...cell.schema);
1092
- if (b !== void 0) into.push(...b);
1093
- }
1094
- if (cell !== void 0) into.push(...cell.user);
1095
- };
1096
1065
  const merged = [];
1097
1066
  if (isContainerSelfAccess) {
1098
1067
  const containerPath = path.slice(0, -1);
1099
- const containerKey = canonicalizePath(containerPath).key;
1100
- const literalKey = canonicalizePath(path).key;
1068
+ const container = canonicalizePath(containerPath);
1069
+ const literal = canonicalizePath(path);
1101
1070
  const active2 = hasAtPath(state.form.value, containerPath);
1102
- collectAtKey(containerKey, active2, merged);
1103
- if (literalKey !== containerKey) collectAtKey(literalKey, active2, merged);
1071
+ collectErrorsAt(state, container.key, container.segments, active2, merged);
1072
+ if (literal.key !== container.key) {
1073
+ collectErrorsAt(state, literal.key, literal.segments, active2, merged);
1074
+ }
1104
1075
  return merged;
1105
1076
  }
1106
- const { key } = canonicalizePath(path);
1077
+ const { key, segments } = canonicalizePath(path);
1107
1078
  const active = hasAtPath(state.form.value, path);
1108
- collectAtKey(key, active, merged);
1079
+ collectErrorsAt(state, key, segments, active, merged);
1109
1080
  return merged;
1110
1081
  };
1111
1082
  return buildTree({
@@ -1120,68 +1091,83 @@ function buildErrorsSurface(state, sweep) {
1120
1091
  // non-schema keys reachable while a genuinely-absent key stays out.
1121
1092
  hasOwn: (segments, key) => liveContainerHasKey(state, segments, key) || errorAwareContainerKeys(state, segments).includes(key),
1122
1093
  isArrayAt: (segments) => isArrayPath(state, segments),
1123
- // `errors()` / `errors([])` / `errors(path)` are all the subtree
1124
- // aggregate — the same helper `meta.errors` reads, so the surfaces
1125
- // never drift.
1126
- call: (path) => aggregateErrorsAt(state, path),
1094
+ // `errors()`, `errors([])` and `errors(path)` are all the subtree
1095
+ // aggregate, through the helper `meta.errors` reads, so they cannot
1096
+ // drift.
1097
+ call: (path) => aggregateErrorsAt(state, path, keyForSegments(path).key),
1127
1098
  surface: "form.errors",
1128
1099
  sweep
1129
1100
  });
1130
1101
  }
1102
+ function collectErrorsAt(state, key, segments, active, into) {
1103
+ const cell = state.errorCells.get(key);
1104
+ if (active) {
1105
+ if (cell !== void 0) into.push(...cell.schema);
1106
+ if (state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments)) {
1107
+ into.push(makeBlankRequiredError(segments));
1108
+ }
1109
+ }
1110
+ if (cell !== void 0) into.push(...cell.user);
1111
+ }
1131
1112
  function errorAwareContainerKeys(state, segments) {
1132
1113
  const keys = new Set(liveKeysAtPath(state, segments));
1133
1114
  const formValue = state.form.value;
1134
- const walk = (store, applyActivePathFilter) => {
1135
- for (const [pathKey, errors] of store) {
1136
- if (errors.length === 0) continue;
1137
- const decoded = segmentsForPathKey(pathKey);
1138
- if (decoded === null) continue;
1139
- if (decoded.length <= segments.length) continue;
1140
- if (!isPathPrefix(segments, decoded)) continue;
1141
- if (applyActivePathFilter && !hasAtPath(formValue, decoded)) continue;
1142
- const nextSeg = decoded[segments.length];
1143
- keys.add(typeof nextSeg === "number" ? String(nextSeg) : nextSeg);
1144
- }
1145
- };
1146
- walk(cellEntriesFor(state.errorCells, "schema"), true);
1147
- walk(state.derivedBlankErrors.value, true);
1148
- walk(cellEntriesFor(state.errorCells, "user"), false);
1115
+ const errors = [];
1116
+ const found = [];
1117
+ for (const { key, segments: decoded } of state.errorWindowAt(
1118
+ segments,
1119
+ keyForSegments(segments).key
1120
+ )) {
1121
+ if (decoded.length <= segments.length) continue;
1122
+ if (!isPathPrefix(segments, decoded)) continue;
1123
+ errors.length = 0;
1124
+ collectErrorsAt(state, key, decoded, hasAtPath(formValue, decoded), errors);
1125
+ if (errors.length === 0) continue;
1126
+ const nextSeg = decoded[segments.length];
1127
+ found.push({
1128
+ childKey: typeof nextSeg === "number" ? String(nextSeg) : nextSeg,
1129
+ ordinal: state.ensurePathOrdinal(key)
1130
+ });
1131
+ }
1132
+ found.sort((a, b) => a.ordinal - b.ordinal);
1133
+ for (const { childKey } of found) keys.add(childKey);
1149
1134
  return [...keys];
1150
1135
  }
1151
1136
  function materializeErrors(state, containerSegments) {
1152
- const liveContainer = getAtPath(state.form.value, containerSegments);
1137
+ const formValue = state.form.value;
1138
+ const liveContainer = getAtPath(formValue, containerSegments);
1153
1139
  const tree = Array.isArray(liveContainer) ? [] : {};
1154
- const collect = (store, applyActivePathFilter) => {
1155
- entries: for (const [pathKey, errors] of store) {
1156
- if (errors.length === 0) continue;
1157
- const fullPath = segmentsForPathKey(pathKey);
1158
- if (fullPath === null) continue;
1159
- if (fullPath.length === 0) {
1160
- if (containerSegments.length === 0) placeAt(tree, [ROOT_PATH_KEY], errors);
1161
- continue;
1162
- }
1163
- if (fullPath.length < containerSegments.length) continue;
1164
- for (let i = 0; i < containerSegments.length; i++) {
1165
- if (fullPath[i] !== containerSegments[i]) continue entries;
1166
- }
1167
- if (applyActivePathFilter && !hasAtPath(state.form.value, fullPath)) continue;
1168
- const relativePath = fullPath.slice(containerSegments.length);
1169
- let placePath;
1170
- if (relativePath.length === 0) {
1171
- placePath = [""];
1172
- } else if (state.schema.isLeafAtPath(fullPath)) {
1173
- placePath = relativePath;
1174
- } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1175
- placePath = [...relativePath, ""];
1176
- } else {
1177
- placePath = relativePath;
1178
- }
1179
- placeAt(tree, placePath, errors);
1140
+ const placements = [];
1141
+ for (const { key, segments: fullPath } of state.errorWindowAt(
1142
+ containerSegments,
1143
+ keyForSegments(containerSegments).key
1144
+ )) {
1145
+ const isRoot = fullPath.length === 0;
1146
+ if (isRoot) {
1147
+ if (containerSegments.length !== 0) continue;
1148
+ } else if (!isPathPrefix(containerSegments, fullPath)) {
1149
+ continue;
1180
1150
  }
1181
- };
1182
- collect(cellEntriesFor(state.errorCells, "schema"), true);
1183
- collect(state.derivedBlankErrors.value, true);
1184
- collect(cellEntriesFor(state.errorCells, "user"), false);
1151
+ const errors = [];
1152
+ collectErrorsAt(state, key, fullPath, isRoot || hasAtPath(formValue, fullPath), errors);
1153
+ if (errors.length === 0) continue;
1154
+ const relativePath = fullPath.slice(containerSegments.length);
1155
+ let placePath;
1156
+ if (isRoot) {
1157
+ placePath = [ROOT_PATH_KEY];
1158
+ } else if (relativePath.length === 0) {
1159
+ placePath = [""];
1160
+ } else if (state.schema.isLeafAtPath(fullPath)) {
1161
+ placePath = relativePath;
1162
+ } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1163
+ placePath = [...relativePath, ""];
1164
+ } else {
1165
+ placePath = relativePath;
1166
+ }
1167
+ placements.push({ placePath, errors, ordinal: state.ensurePathOrdinal(key) });
1168
+ }
1169
+ placements.sort((a, b) => a.ordinal - b.ordinal);
1170
+ for (const { placePath, errors } of placements) placeAt(tree, placePath, errors);
1185
1171
  return tree;
1186
1172
  }
1187
1173
  function placeAt(tree, path, errors) {
@@ -1254,7 +1240,7 @@ function buildFieldsSurface(state, getFieldStateAt, sweep) {
1254
1240
  const viewCache = /* @__PURE__ */ new Map();
1255
1241
  sweep.onEvict((key) => viewCache.delete(key));
1256
1242
  function viewAt(segments) {
1257
- const cacheKey = JSON.stringify(segments);
1243
+ const cacheKey = keyForSegments(segments).key;
1258
1244
  const existing = viewCache.get(cacheKey);
1259
1245
  if (existing !== void 0) return existing;
1260
1246
  const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(
@@ -1816,45 +1802,6 @@ function buildFieldArrayApi(state) {
1816
1802
  };
1817
1803
  }
1818
1804
 
1819
- const SWEEP_SLICE = 8;
1820
- function createDynamicPathSweep(state) {
1821
- const dynamicKeys = /* @__PURE__ */ new Set();
1822
- const evictors = [];
1823
- const isDynamicPath = (segments) => {
1824
- for (let i = 0; i < segments.length; i++) {
1825
- if (typeof segments[i] === "number") return true;
1826
- if (!state.schema.isFixedObjectAtPath(segments.slice(0, i))) return true;
1827
- }
1828
- return false;
1829
- };
1830
- let sweepCursor = null;
1831
- state.onFormChange((next) => {
1832
- if (dynamicKeys.size === 0) return;
1833
- const raw = toRaw(next);
1834
- for (let i = 0; i < SWEEP_SLICE; i++) {
1835
- sweepCursor ?? (sweepCursor = dynamicKeys.values());
1836
- const step = sweepCursor.next();
1837
- if (step.done === true) {
1838
- sweepCursor = null;
1839
- break;
1840
- }
1841
- const segments = segmentsForPathKey(step.value);
1842
- if (segments !== null && hasAtPath(raw, segments)) continue;
1843
- dynamicKeys.delete(step.value);
1844
- for (const evict of evictors) evict(step.value);
1845
- }
1846
- });
1847
- return {
1848
- track(segments, key) {
1849
- if (dynamicKeys.has(key)) return;
1850
- if (isDynamicPath(segments)) dynamicKeys.add(key);
1851
- },
1852
- onEvict(evict) {
1853
- evictors.push(evict);
1854
- }
1855
- };
1856
- }
1857
-
1858
1805
  function isErrorInputLike(value) {
1859
1806
  if (value instanceof Error) return true;
1860
1807
  if (typeof value !== "object" || value === null) return false;
@@ -1881,8 +1828,8 @@ function deriveSubmitErrors(err) {
1881
1828
  messageless: true
1882
1829
  };
1883
1830
  }
1884
- function buildProcessForm(state, formInstanceId, options = {}) {
1885
- const invalidPolicy = options.onInvalidSubmit ?? "focus-first-error";
1831
+ function buildProcessForm(state, options = {}) {
1832
+ const applyInvalidSubmit = options.applyInvalidSubmit;
1886
1833
  function validate(pathInput) {
1887
1834
  const result = ref({
1888
1835
  pending: true,
@@ -1975,7 +1922,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1975
1922
  };
1976
1923
  }
1977
1924
  async function runRefinementValidation(data, path) {
1978
- return await state.schema.validateAtPath(data, path);
1925
+ return stampFormKey(await state.schema.validateAtPath(data, path), state.formKey);
1979
1926
  }
1980
1927
  function composeWithDerivedBlank(refinement, scope) {
1981
1928
  const blankErrors = collectScopedBlankErrors(state, scope);
@@ -2021,7 +1968,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
2021
1968
  }
2022
1969
  }
2023
1970
  if (generationStillValid) {
2024
- applyInvalidSubmitPolicy(state, formInstanceId, invalidPolicy);
1971
+ applyInvalidSubmit?.();
2025
1972
  }
2026
1973
  if (onError !== void 0) {
2027
1974
  try {
@@ -2038,7 +1985,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
2038
1985
  await onSubmit(merged.data);
2039
1986
  if (hasUserErrorEntries(state)) {
2040
1987
  if (state.submissionGeneration.value === genAtEntry) {
2041
- applyInvalidSubmitPolicy(state, formInstanceId, invalidPolicy);
1988
+ applyInvalidSubmit?.();
2042
1989
  }
2043
1990
  return;
2044
1991
  }
@@ -2056,7 +2003,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
2056
2003
  if (segments === null) continue;
2057
2004
  state.setUserErrorsForPath([...segments], bucket);
2058
2005
  }
2059
- applyInvalidSubmitPolicy(state, formInstanceId, invalidPolicy);
2006
+ applyInvalidSubmit?.();
2060
2007
  }
2061
2008
  }
2062
2009
  } finally {
@@ -2071,6 +2018,10 @@ function buildProcessForm(state, formInstanceId, options = {}) {
2071
2018
  };
2072
2019
  return { validate, parse, handleSubmit };
2073
2020
  }
2021
+ function stampFormKey(verdict, formKey) {
2022
+ if (verdict.success) return { ...verdict, formKey };
2023
+ return verdict.data === void 0 ? { data: void 0, errors: verdict.errors, success: false, formKey } : { data: verdict.data, errors: verdict.errors, success: false, formKey };
2024
+ }
2074
2025
  function toSegments(pathInput) {
2075
2026
  return canonicalizePath(pathInput).segments;
2076
2027
  }
@@ -2104,21 +2055,6 @@ function collectScopedBlankErrors(state, scope) {
2104
2055
  }
2105
2056
  return errors;
2106
2057
  }
2107
- function applyInvalidSubmitPolicy(state, formInstanceId, policy) {
2108
- if (policy === "none") return;
2109
- const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
2110
- if (target === null) return;
2111
- if (policy === "scroll-to-first-error") {
2112
- target.element.scrollIntoView();
2113
- return;
2114
- }
2115
- if (policy === "focus-first-error") {
2116
- target.element.focus({ focusVisible: true });
2117
- return;
2118
- }
2119
- target.element.scrollIntoView();
2120
- target.element.focus({ preventScroll: true, focusVisible: true });
2121
- }
2122
2058
 
2123
2059
  function slimKindOf(value) {
2124
2060
  if (value === null) return "null";
@@ -2182,51 +2118,9 @@ function walk(schema, store, path, value) {
2182
2118
  return true;
2183
2119
  }
2184
2120
 
2185
- function defineCoercion(entry) {
2186
- return entry;
2187
- }
2188
2121
  const IDENTITY = (v) => v;
2189
- const EMPTY_INDEX = /* @__PURE__ */ new Map();
2190
- const defaultCoercionRules = [
2191
- defineCoercion({
2192
- input: "string",
2193
- output: "number",
2194
- transform: (s) => {
2195
- const trimmed = s.trim();
2196
- if (trimmed === "") return { coerced: false };
2197
- const n = Number(trimmed);
2198
- if (!Number.isFinite(n)) return { coerced: false };
2199
- return { coerced: true, value: n };
2200
- }
2201
- }),
2202
- defineCoercion({
2203
- input: "string",
2204
- output: "boolean",
2205
- transform: (s) => {
2206
- const normalized = s.trim().toLowerCase();
2207
- if (normalized === "true") return { coerced: true, value: true };
2208
- if (normalized === "false") return { coerced: true, value: false };
2209
- return { coerced: false };
2210
- }
2211
- })
2212
- ];
2213
- function resolveCoercionIndex(config) {
2214
- if (config === false) return EMPTY_INDEX;
2215
- const rules = config === void 0 || config === true ? defaultCoercionRules : config;
2216
- return indexRules(rules);
2217
- }
2218
- function indexRules(rules) {
2219
- const idx = /* @__PURE__ */ new Map();
2220
- for (const entry of rules) {
2221
- const candidate = entry;
2222
- if (candidate === null || typeof candidate !== "object" || typeof candidate.transform !== "function") {
2223
- continue;
2224
- }
2225
- const key = `${entry.input}->${entry.output}`;
2226
- if (idx.has(key) && false) ;
2227
- idx.set(key, entry);
2228
- }
2229
- return idx;
2122
+ function resolveCoerceEnabled(config) {
2123
+ return config !== false;
2230
2124
  }
2231
2125
  function memberSlimTypes(schema, segments, accepted) {
2232
2126
  if (accepted.has("array")) return schema.getSlimPrimitiveTypesAtPath([...segments, 0]);
@@ -2235,89 +2129,83 @@ function memberSlimTypes(schema, segments, accepted) {
2235
2129
  }
2236
2130
  return void 0;
2237
2131
  }
2238
- function buildCoerceFn(schema, segments, index) {
2239
- if (index === EMPTY_INDEX) return IDENTITY;
2240
- if (index.size === 0) return IDENTITY;
2132
+ function buildCoerceFn(schema, segments, enabled) {
2133
+ if (!enabled) return IDENTITY;
2241
2134
  const accepted = schema.getSlimPrimitiveTypesAtPath(segments);
2242
2135
  const elementAccepted = memberSlimTypes(schema, segments, accepted);
2243
- return (value) => coerceValue(value, accepted, elementAccepted, index);
2136
+ return (value) => coerceValue(value, accepted, elementAccepted);
2244
2137
  }
2245
- function buildElementCoerceFn(schema, segments, index) {
2246
- if (index === EMPTY_INDEX) return void 0;
2247
- if (index.size === 0) return void 0;
2138
+ function buildElementCoerceFn(schema, segments, enabled) {
2139
+ if (!enabled) return void 0;
2248
2140
  const accepted = schema.getSlimPrimitiveTypesAtPath(segments);
2249
2141
  const elementAccepted = memberSlimTypes(schema, segments, accepted);
2250
2142
  if (elementAccepted === void 0) return void 0;
2251
- return (value) => coerceScalar(value, elementAccepted, index);
2143
+ return (value) => coerceScalar(value, elementAccepted);
2252
2144
  }
2253
2145
  function pickScalarTarget(accepted) {
2254
2146
  if (accepted.has("string")) return null;
2255
2147
  if (accepted.has("number")) return "number";
2256
2148
  if (accepted.has("boolean")) return "boolean";
2257
- if (accepted.has("bigint")) return "bigint";
2258
2149
  return null;
2259
2150
  }
2260
- function coerceScalar(value, accepted, index) {
2151
+ function toNumber(source) {
2152
+ const trimmed = source.trim();
2153
+ if (trimmed === "") return source;
2154
+ const n = Number(trimmed);
2155
+ return Number.isFinite(n) ? n : source;
2156
+ }
2157
+ function toBoolean(source) {
2158
+ const normalized = source.trim().toLowerCase();
2159
+ if (normalized === "true") return true;
2160
+ if (normalized === "false") return false;
2161
+ return source;
2162
+ }
2163
+ function coerceScalar(value, accepted) {
2261
2164
  if (accepted.size === 0) return value;
2262
2165
  const sourceKind = slimKindOf(value);
2263
2166
  if (accepted.has(sourceKind)) return value;
2167
+ if (sourceKind !== "string") return value;
2264
2168
  const target = pickScalarTarget(accepted);
2265
- if (target === null) return value;
2266
- const entry = index.get(`${sourceKind}->${target}`);
2267
- if (entry === void 0) return value;
2268
- let result;
2269
- try {
2270
- result = entry.transform(value);
2271
- } catch (err) {
2272
- return value;
2273
- }
2274
- if (!result.coerced) return value;
2275
- const returnedKind = slimKindOf(result.value);
2276
- if (returnedKind !== entry.output) {
2277
- return value;
2278
- }
2279
- if (entry.output === "number" && !Number.isFinite(result.value)) {
2280
- return value;
2281
- }
2282
- return result.value;
2169
+ if (target === "number") return toNumber(value);
2170
+ if (target === "boolean") return toBoolean(value);
2171
+ return value;
2283
2172
  }
2284
- function coerceArrayMembers(arr, elementAccepted, index) {
2173
+ function coerceArrayMembers(arr, elementAccepted) {
2285
2174
  let changed = false;
2286
2175
  const out = [];
2287
2176
  for (const el of arr) {
2288
- const next = coerceScalar(el, elementAccepted, index);
2177
+ const next = coerceScalar(el, elementAccepted);
2289
2178
  if (next !== el) changed = true;
2290
2179
  out.push(next);
2291
2180
  }
2292
2181
  return changed ? out : arr;
2293
2182
  }
2294
- function coerceSetMembers(set, elementAccepted, index) {
2183
+ function coerceSetMembers(set, elementAccepted) {
2295
2184
  let changed = false;
2296
2185
  const out = [];
2297
2186
  for (const el of set) {
2298
- const next = coerceScalar(el, elementAccepted, index);
2187
+ const next = coerceScalar(el, elementAccepted);
2299
2188
  if (next !== el) changed = true;
2300
2189
  out.push(next);
2301
2190
  }
2302
2191
  return changed ? new Set(out) : set;
2303
2192
  }
2304
- function coerceValue(value, accepted, elementAccepted, index) {
2193
+ function coerceValue(value, accepted, elementAccepted) {
2305
2194
  if (Array.isArray(value)) {
2306
2195
  if (!accepted.has("array") || elementAccepted === void 0) return value;
2307
- return coerceArrayMembers(value, elementAccepted, index);
2196
+ return coerceArrayMembers(value, elementAccepted);
2308
2197
  }
2309
2198
  if (value instanceof Set) {
2310
2199
  if (!accepted.has("set") || elementAccepted === void 0) return value;
2311
- return coerceSetMembers(value, elementAccepted, index);
2200
+ return coerceSetMembers(value, elementAccepted);
2312
2201
  }
2313
- return coerceScalar(value, accepted, index);
2202
+ return coerceScalar(value, accepted);
2314
2203
  }
2315
2204
 
2316
2205
  const EMPTY_TRANSFORMS = Object.freeze([]);
2317
2206
  function buildRegister(state, formInstanceId, instanceConfig) {
2318
- const coerceIndex = instanceConfig?.coerce !== void 0 ? resolveCoercionIndex(instanceConfig.coerce) : state.coerceIndex;
2207
+ const coerceEnabled = instanceConfig?.coerce !== void 0 ? resolveCoerceEnabled(instanceConfig.coerce) : state.coerceEnabled;
2319
2208
  const instanceMeta = instanceConfig?.instanceMeta;
2320
- const formAutoAria = instanceConfig?.autoAria ?? true;
2321
2209
  const getDisplayStateAt = instanceConfig?.getDisplayStateAt;
2322
2210
  const withInstanceMeta = (meta) => {
2323
2211
  if (instanceMeta === void 0) return meta;
@@ -2357,16 +2245,15 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2357
2245
  const coerce = buildCoerceFn(
2358
2246
  state.schema,
2359
2247
  segments,
2360
- coerceIndex
2248
+ coerceEnabled
2361
2249
  );
2362
2250
  const coerceElement = buildElementCoerceFn(
2363
2251
  state.schema,
2364
2252
  segments,
2365
- coerceIndex
2253
+ coerceEnabled
2366
2254
  );
2367
2255
  const { aria } = computeFieldIdentity(formInstanceId, state.formKey, pathKey);
2368
2256
  const isRequired = state.schema.isRequiredAtPath(segments);
2369
- const ariaEnabled = options?.autoAria ?? formAutoAria;
2370
2257
  const ariaDisplayState = getDisplayStateAt !== void 0 ? computed(() => getDisplayStateAt(segments)) : void 0;
2371
2258
  const markBlank = () => state.setValueAtPath(segments, slimDefault, withInstanceMeta({ blank: true }));
2372
2259
  const internalRv = {
@@ -2406,7 +2293,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2406
2293
  const accepted = isBlankSignal ? markBlank() : state.setValueAtPath(segments, value, withInstanceMeta(void 0));
2407
2294
  return accepted;
2408
2295
  },
2409
- // Called by the `vRegisterHint` compile-time transform's wrapping
2296
+ // Called by the `vRegisterHintTransform` compile-time transform's wrapping
2410
2297
  // IIFE on every server-side render of `<element v-register="…">`.
2411
2298
  // Without it, every SSR'd FieldState serialises `connected: false`
2412
2299
  // (because Vue skips directive lifecycle during SSR) and the client
@@ -2435,12 +2322,12 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2435
2322
  }
2436
2323
  return false;
2437
2324
  },
2438
- // --- Async transform lifecycle (internal; the directive's
2439
- // deferred orchestrator is the only legitimate consumer). Thin
2440
- // path-bound delegates to the store's per-path token / counter
2441
- // machinery — same pattern as `markBlank` / `setValueWithInternalPath`,
2442
- // so the directive (which holds only this RegisterValue, never the
2443
- // store) can drive the busy/discard/error bookkeeping. ---
2325
+ // --- Async transform lifecycle (internal; the directive's deferred
2326
+ // orchestrator is the only legitimate consumer). Thin path-bound
2327
+ // delegates to the store's per-path token and counter machinery,
2328
+ // the same pattern as `markBlank` and `setValueWithInternalPath`, so
2329
+ // the directive (which holds this RegisterValue and never the store)
2330
+ // can drive the busy / discard / error bookkeeping. ---
2444
2331
  beginTransform: (holder) => state.beginTransform(pathKey, holder),
2445
2332
  isCurrentTransform: (token) => state.isCurrentTransform(pathKey, token),
2446
2333
  endTransform: (token) => state.endTransform(pathKey, token),
@@ -2453,10 +2340,10 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2453
2340
  return (state.fieldTransformCounts.get(pathKey) ?? 0) > 0;
2454
2341
  },
2455
2342
  path: pathKey,
2456
- // Frozen so a wrapper component can pass `rv.segments` directly
2457
- // to `form.fields(...)` without defensive copying — and so test
2458
- // fixtures or downstream code can't mutate the canonical
2459
- // segment list out from under the directive.
2343
+ // Frozen so a wrapper component can pass `rv.segments` straight to
2344
+ // `form.fields(...)` with no defensive copy, and so neither a test
2345
+ // fixture nor downstream code can mutate the canonical segment list
2346
+ // out from under the directive.
2460
2347
  segments: Object.freeze(segments.slice()),
2461
2348
  formKey: state.formKey,
2462
2349
  formInstanceId,
@@ -2468,7 +2355,6 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2468
2355
  // --- Aria (internal; consumed by the directive) ---
2469
2356
  aria,
2470
2357
  isRequired,
2471
- ariaEnabled,
2472
2358
  ...ariaDisplayState !== void 0 ? { ariaDisplayState } : {}
2473
2359
  };
2474
2360
  return shallowReadonly(internalRv);
@@ -2632,11 +2518,50 @@ const DERIVED_DISPLAY_KEYS = /* @__PURE__ */ new Set([
2632
2518
  "firstError",
2633
2519
  "firstOwnError"
2634
2520
  ]);
2635
- const META_SPECIAL_KEYS = /* @__PURE__ */ new Set(["errors", "validating", "valid", "transforming", "busy"]);
2636
- const BASE_MIRROR_KEYS = [...FIELD_STATE_KEYS].filter((k) => !DERIVED_DISPLAY_KEYS.has(k));
2637
- const META_MIRROR_KEYS = [...FIELD_STATE_KEYS].filter((k) => !META_SPECIAL_KEYS.has(k));
2638
- function defineGetter(target, key, get) {
2639
- Object.defineProperty(target, key, { get, enumerable: true, configurable: true });
2521
+ const META_KEYS = [
2522
+ ...FIELD_STATE_KEYS,
2523
+ "submitting",
2524
+ "submissionAttempts",
2525
+ "departAttempts",
2526
+ "submitError",
2527
+ "submitted",
2528
+ "instanceId",
2529
+ "errorCount"
2530
+ ];
2531
+ const BASE_META_KEYS = META_KEYS.filter((k) => !DERIVED_DISPLAY_KEYS.has(k));
2532
+ let INERT_HISTORY;
2533
+ function inertHistory() {
2534
+ INERT_HISTORY ?? (INERT_HISTORY = readonly({
2535
+ undo: () => false,
2536
+ redo: () => false,
2537
+ clear: () => {
2538
+ },
2539
+ canUndo: false,
2540
+ canRedo: false,
2541
+ size: 0
2542
+ }));
2543
+ return INERT_HISTORY;
2544
+ }
2545
+ const denyWrite = () => false;
2546
+ function buildMetaProxy(keys, read) {
2547
+ const target = {};
2548
+ const owns = new Set(keys);
2549
+ return new Proxy(target, {
2550
+ get: (_, key) => owns.has(key) ? read(key) : Reflect.get(target, key),
2551
+ has: (_, key) => owns.has(key) || Reflect.has(target, key),
2552
+ // Safe to hand back the same array every time: the spec copies a trap's
2553
+ // key list before using it.
2554
+ ownKeys: () => keys,
2555
+ getOwnPropertyDescriptor(_, key) {
2556
+ if (!owns.has(key)) return Reflect.getOwnPropertyDescriptor(target, key);
2557
+ return { configurable: true, enumerable: true, value: read(key), writable: false };
2558
+ },
2559
+ // A getter with no setter throws on assignment under strict mode, which
2560
+ // every module here is; `false` keeps that.
2561
+ set: denyWrite,
2562
+ deleteProperty: denyWrite,
2563
+ defineProperty: denyWrite
2564
+ });
2640
2565
  }
2641
2566
  function buildFormApi(state, formInstanceId, options = {}) {
2642
2567
  const instanceBag = pickDefined({
@@ -2663,32 +2588,26 @@ function buildFormApi(state, formInstanceId, options = {}) {
2663
2588
  const rootBaseComputed = computed(
2664
2589
  () => buildContainerFieldStateBase(state, ROOT_PATH, ROOT_PATH_KEY, formInstanceId).base
2665
2590
  );
2666
- const metaBase = {
2667
- instanceId: formInstanceId,
2668
- get submitting() {
2669
- return state.submitting.value;
2670
- },
2671
- get submissionAttempts() {
2672
- return state.submissionAttempts.value;
2673
- },
2674
- get departAttempts() {
2675
- return state.departAttempts.value;
2676
- },
2677
- get submitError() {
2678
- return state.submitError.value;
2679
- },
2680
- get submitted() {
2681
- return state.submitted.value;
2591
+ const metaBase = buildMetaProxy(BASE_META_KEYS, (key) => {
2592
+ switch (key) {
2593
+ case "instanceId":
2594
+ return formInstanceId;
2595
+ case "errorCount":
2596
+ return rootBaseComputed.value.errors.length;
2597
+ case "submitting":
2598
+ return state.submitting.value;
2599
+ case "submissionAttempts":
2600
+ return state.submissionAttempts.value;
2601
+ case "departAttempts":
2602
+ return state.departAttempts.value;
2603
+ case "submitError":
2604
+ return state.submitError.value;
2605
+ case "submitted":
2606
+ return state.submitted.value;
2607
+ default:
2608
+ return rootBaseComputed.value[key];
2682
2609
  }
2683
- };
2684
- for (const k of BASE_MIRROR_KEYS) {
2685
- defineGetter(
2686
- metaBase,
2687
- k,
2688
- () => rootBaseComputed.value[k]
2689
- );
2690
- }
2691
- defineGetter(metaBase, "errorCount", () => rootBaseComputed.value.errors.length);
2610
+ });
2692
2611
  const getFormMetaBase = () => {
2693
2612
  void state.submitting.value;
2694
2613
  void state.submissionAttempts.value;
@@ -2697,28 +2616,41 @@ function buildFormApi(state, formInstanceId, options = {}) {
2697
2616
  void state.submitted.value;
2698
2617
  return metaBase;
2699
2618
  };
2700
- const fieldStateAccessorOptions = options.getDisplayState !== void 0 ? { getDisplayState: options.getDisplayState } : void 0;
2701
- const pathSweep = createDynamicPathSweep(state);
2619
+ const pathSweep = state.pathSweep;
2702
2620
  const getRootFieldStateAt = buildFieldStateAccessor(
2703
2621
  state,
2704
2622
  formInstanceId,
2705
2623
  getFormMetaBase,
2706
- pathSweep,
2707
- fieldStateAccessorOptions
2624
+ pathSweep
2708
2625
  );
2709
2626
  const getDisplayStateAt = (segments) => getRootFieldStateAt(segments).value.displayState;
2710
2627
  const registerConfig = {
2711
- ...pickDefined({ instanceMeta, coerce: options.coerce, autoAria: options.autoAria }),
2628
+ ...pickDefined({ instanceMeta, coerce: options.coerce }),
2712
2629
  getDisplayStateAt
2713
2630
  };
2714
2631
  const register = buildRegister(state, formInstanceId, registerConfig);
2715
- const processOptions = pickDefined({ onInvalidSubmit: options.onInvalidSubmit });
2716
- const defaultInvalidSubmitPolicy = options.onInvalidSubmit ?? "focus-first-error";
2632
+ const focusFirstError = (options2) => {
2633
+ const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
2634
+ if (target === null) return false;
2635
+ target.element.focus({ focusVisible: true, ...options2 });
2636
+ return true;
2637
+ };
2638
+ const scrollToFirstError = (options2) => {
2639
+ const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
2640
+ if (target === null) return false;
2641
+ target.element.scrollIntoView(options2);
2642
+ return true;
2643
+ };
2644
+ const applyInvalidSubmitPolicyPublic = () => {
2645
+ if (options.focusOnInvalidSubmit !== false) focusFirstError();
2646
+ };
2717
2647
  const {
2718
2648
  validate: validateBuilt,
2719
2649
  parse: parseBuilt,
2720
2650
  handleSubmit
2721
- } = buildProcessForm(state, formInstanceId, processOptions);
2651
+ } = buildProcessForm(state, {
2652
+ applyInvalidSubmit: applyInvalidSubmitPolicyPublic
2653
+ });
2722
2654
  const validate = (pathInput) => validateBuilt(pathInput);
2723
2655
  const parse = (arg1, arg2) => {
2724
2656
  const isPathArg = typeof arg1 === "string" || Array.isArray(arg1);
@@ -2846,28 +2778,26 @@ function buildFormApi(state, formInstanceId, options = {}) {
2846
2778
  () => state.firstValidationDone.value && state.errorCells.size === 0 && state.derivedBlankErrors.value.size === 0 && !validating.value
2847
2779
  );
2848
2780
  const history = options.history;
2849
- const formHistory = readonly(
2781
+ const formHistory = history === void 0 ? inertHistory() : readonly(
2850
2782
  reactive({
2851
- undo: history?.undo ?? (() => false),
2852
- redo: history?.redo ?? (() => false),
2853
- clear: history?.clear ?? (() => {
2854
- }),
2855
- canUndo: history?.canUndo ?? computed(() => false),
2856
- canRedo: history?.canRedo ?? computed(() => false),
2857
- size: history?.historySize ?? computed(() => 0)
2783
+ undo: history.undo,
2784
+ redo: history.redo,
2785
+ clear: history.clear,
2786
+ canUndo: history.canUndo,
2787
+ canRedo: history.canRedo,
2788
+ size: history.historySize
2858
2789
  })
2859
2790
  );
2860
2791
  const metaErrors = computed(
2861
- () => aggregateErrorsAt(state, [])
2792
+ () => aggregateErrorsAt(state, [], ROOT_PATH_KEY)
2862
2793
  );
2863
2794
  const rootFieldState = getRootFieldStateAt([]);
2864
- const metaTarget = {
2865
- // Whole-form work signals compose the LIFECYCLE counters with the
2866
- // per-leaf rollup: a submit-time validate shows up as
2867
- // activeValidations, per-field debounced validators as
2868
- // fieldValidationCounts — either flips the flag. `valid` keeps the
2869
- // form-level `firstValidationDone` mount gate; `errors` is the
2870
- // unfiltered whole-form aggregate.
2795
+ const metaOwn = {
2796
+ // Whole-form work signals compose the LIFECYCLE counters with the per-leaf
2797
+ // rollup: a submit-time validate shows up in `activeValidations` and a
2798
+ // per-field debounced validator in `fieldValidationCounts`, and either
2799
+ // flips the flag. `valid` keeps the form-level mount gate, and `errors` is
2800
+ // the unfiltered whole-form aggregate.
2871
2801
  validating: computed(
2872
2802
  () => state.activeValidations.value > 0 || rootFieldState.value.validating
2873
2803
  ),
@@ -2879,7 +2809,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2879
2809
  busy: computed(
2880
2810
  () => state.activeValidations.value > 0 || state.activeTransforms.value > 0 || rootFieldState.value.validating || rootFieldState.value.transforming
2881
2811
  ),
2882
- // Lifecycle (form-level only — not on FieldState).
2812
+ // Lifecycle, form-level only and never on FieldState.
2883
2813
  submitting,
2884
2814
  submissionAttempts,
2885
2815
  departAttempts,
@@ -2890,14 +2820,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
2890
2820
  // identifier).
2891
2821
  instanceId: formInstanceId
2892
2822
  };
2893
- for (const k of META_MIRROR_KEYS) {
2894
- defineGetter(
2895
- metaTarget,
2896
- k,
2897
- () => rootFieldState.value[k]
2898
- );
2899
- }
2900
- defineGetter(metaTarget, "errorCount", () => metaErrors.value.length);
2823
+ const metaTarget = buildMetaProxy(META_KEYS, (key) => {
2824
+ if (key === "errorCount") return metaErrors.value.length;
2825
+ if (Object.hasOwn(metaOwn, key)) return metaOwn[key];
2826
+ return rootFieldState.value[key];
2827
+ });
2901
2828
  const formMeta = readonly(reactive(metaTarget));
2902
2829
  const reset = (nextDefaultValues) => {
2903
2830
  if (nextDefaultValues === void 0) {
@@ -2937,21 +2864,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
2937
2864
  } catch {
2938
2865
  }
2939
2866
  }
2940
- const focusFirstError = (options2) => {
2941
- const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
2942
- if (target === null) return false;
2943
- target.element.focus({ focusVisible: true, ...options2 });
2944
- return true;
2945
- };
2946
- const scrollToFirstError = (options2) => {
2947
- const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
2948
- if (target === null) return false;
2949
- target.element.scrollIntoView(options2);
2950
- return true;
2951
- };
2952
- const applyInvalidSubmitPolicyPublic = (policy) => {
2953
- applyInvalidSubmitPolicy(state, formInstanceId, policy ?? defaultInvalidSubmitPolicy);
2954
- };
2955
2867
  const fieldArrays = buildFieldArrayApi(state);
2956
2868
  const blankPathsView = computed(() => {
2957
2869
  const keys = /* @__PURE__ */ new Set();
@@ -3014,10 +2926,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
3014
2926
  }
3015
2927
  const api = {
3016
2928
  handleSubmit: gated(handleSubmit),
3017
- // Callable readonly Proxies (`values`, `fields`, `errors`) and the
3018
- // reactive containers (`meta`, `history`, `blankPaths`) are exposed
3019
- // through getters so reading them activates the form on first
3020
- // touch. Each underlying object is identity-stable across reads.
2929
+ // The callable readonly Proxies (`values`, `fields`, `errors`) and the
2930
+ // reactive containers (`meta`, `history`, `blankPaths`) are exposed through
2931
+ // getters, so reading one activates the form on first touch. Each
2932
+ // underlying object is identity-stable across reads.
3021
2933
  get values() {
3022
2934
  void state.activate();
3023
2935
  return valuesProxy;
@@ -3034,7 +2946,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
3034
2946
  key: state.formKey,
3035
2947
  // Auto-unwrapping views over the per-store async-defaults lifecycle
3036
2948
  // refs (see FormStore.hydrating / hydrateError). Reading either
3037
- // activates the form — observing factory state implies use.
2949
+ // activates the form: observing factory state implies use.
3038
2950
  get hydrating() {
3039
2951
  void state.activate();
3040
2952
  return state.hydrating.value;
@@ -3043,20 +2955,18 @@ function buildFormApi(state, formInstanceId, options = {}) {
3043
2955
  void state.activate();
3044
2956
  return state.hydrateError.value;
3045
2957
  },
3046
- // Orthogonal to `hydrating` and `hydrateError`: `ready` flips true
3047
- // once defaults are applied (sync at construction or async factory
3048
- // resolved successfully). One-way latch — stays true through later
3049
- // refetches even when those refetches fail, so stale-while-
3050
- // revalidate UIs keep rendering the prior values while
3051
- // `hydrateError` surfaces the refresh failure.
2958
+ // Orthogonal to `hydrating` and `hydrateError`. `ready` flips true once
2959
+ // defaults are applied, whether sync at construction or from a factory
2960
+ // that resolved, and it is a one-way latch: it stays true through later
2961
+ // refetches even when those fail, so a stale-while-revalidate UI keeps
2962
+ // rendering the prior values while `hydrateError` surfaces the failure.
3052
2963
  get ready() {
3053
2964
  void state.activate();
3054
2965
  return state.defaultsResolved.value;
3055
2966
  },
3056
- // `rehydrate` and `activate` are themselves activation entry points
3057
- // — they fire the factory by design. Wrapping them with `gated`
3058
- // would double-fire (`state.activate()` plus the underlying call),
3059
- // so they call `state` directly.
2967
+ // `rehydrate` and `activate` are activation entry points themselves and
2968
+ // fire the factory by design, so wrapping them in `gated` would
2969
+ // double-fire. They call `state` directly.
3060
2970
  rehydrate: () => state.rehydrate(),
3061
2971
  activate: () => state.activate(),
3062
2972
  get errors() {
@@ -3099,6 +3009,45 @@ function buildFormApi(state, formInstanceId, options = {}) {
3099
3009
  return api;
3100
3010
  }
3101
3011
 
3012
+ const SWEEP_SLICE = 8;
3013
+ function createDynamicPathSweep(deps) {
3014
+ const dynamicKeys = /* @__PURE__ */ new Set();
3015
+ const evictors = [];
3016
+ const isDynamicPath = (segments) => {
3017
+ for (let i = 0; i < segments.length; i++) {
3018
+ if (typeof segments[i] === "number") return true;
3019
+ if (!deps.isFixedObjectAtPath(segments.slice(0, i))) return true;
3020
+ }
3021
+ return false;
3022
+ };
3023
+ let sweepCursor = null;
3024
+ deps.onFormChange((next) => {
3025
+ if (dynamicKeys.size === 0) return;
3026
+ const raw = toRaw(next);
3027
+ for (let i = 0; i < SWEEP_SLICE; i++) {
3028
+ sweepCursor ?? (sweepCursor = dynamicKeys.values());
3029
+ const step = sweepCursor.next();
3030
+ if (step.done === true) {
3031
+ sweepCursor = null;
3032
+ break;
3033
+ }
3034
+ const segments = segmentsForPathKey(step.value);
3035
+ if (segments !== null && hasAtPath(raw, segments)) continue;
3036
+ dynamicKeys.delete(step.value);
3037
+ for (const evict of evictors) evict(step.value);
3038
+ }
3039
+ });
3040
+ return {
3041
+ track(segments, key) {
3042
+ if (dynamicKeys.has(key)) return;
3043
+ if (isDynamicPath(segments)) dynamicKeys.add(key);
3044
+ },
3045
+ onEvict(evict) {
3046
+ evictors.push(evict);
3047
+ }
3048
+ };
3049
+ }
3050
+
3102
3051
  const IDLE = Object.freeze({ display: "idle" });
3103
3052
  const MAX_DELAY = 2147483647;
3104
3053
  const PENDING_LIVENESS_MS = 250;
@@ -3187,6 +3136,58 @@ function createDisplayEngine(ssr) {
3187
3136
  return engine;
3188
3137
  }
3189
3138
 
3139
+ function buildErrorPathIndex(cells, blank) {
3140
+ const keys = /* @__PURE__ */ new Set();
3141
+ for (const [key, cell] of cells) {
3142
+ if (cell.schema.length > 0 || cell.user.length > 0) keys.add(key);
3143
+ }
3144
+ for (const [key, list] of blank) {
3145
+ if (list.length > 0) keys.add(key);
3146
+ }
3147
+ if (keys.size === 0) return EMPTY_INDEX;
3148
+ const entries = [];
3149
+ for (const key of keys) {
3150
+ const segments = segmentsForPathKey(key);
3151
+ if (segments !== null) entries.push({ key, segments });
3152
+ }
3153
+ entries.sort(compareByKey);
3154
+ return entries;
3155
+ }
3156
+ const EMPTY_INDEX = Object.freeze([]);
3157
+ function compareByKey(a, b) {
3158
+ return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
3159
+ }
3160
+ function windowUnder(index, prefix) {
3161
+ if (prefix.length === 0 || index.length === 0) return index;
3162
+ const prefixKey = keyForSegments(prefix).key;
3163
+ const marker = `${prefixKey.slice(0, -1)},`;
3164
+ const start = lowerBound(index, marker);
3165
+ if (start >= index.length) return EMPTY_INDEX;
3166
+ let end = start;
3167
+ for (; end < index.length; end += 1) {
3168
+ const entry = index[end];
3169
+ if (entry === void 0 || entry.key > prefixKey) break;
3170
+ }
3171
+ return start === 0 && end === index.length ? index : index.slice(start, end);
3172
+ }
3173
+ function isSameWindow(a, b) {
3174
+ if (a.length !== b.length) return false;
3175
+ for (let i = 0; i < a.length; i += 1) {
3176
+ if (a[i]?.key !== b[i]?.key) return false;
3177
+ }
3178
+ return true;
3179
+ }
3180
+ function lowerBound(index, target) {
3181
+ let lo = 0;
3182
+ let hi = index.length;
3183
+ while (lo < hi) {
3184
+ const mid = lo + hi >>> 1;
3185
+ if ((index[mid]?.key ?? target) < target) lo = mid + 1;
3186
+ else hi = mid;
3187
+ }
3188
+ return lo;
3189
+ }
3190
+
3190
3191
  function mergeSparseHydration(schemaDefaults, sparse, schema) {
3191
3192
  return mergeDeep(schemaDefaults, sparse, [], schema);
3192
3193
  }
@@ -3397,16 +3398,15 @@ function walkAuthoredFromSchemaDiff(withDefaults, withoutDefaults, prefix, out)
3397
3398
  out.add(canonicalizePath(prefix).key);
3398
3399
  }
3399
3400
  }
3400
- function computeBaselineResponse(schema, strict, source) {
3401
+ function computeBaselineResponse(schema, source) {
3401
3402
  const completed = source === void 0 ? void 0 : mergeStructural(schema, [], source);
3402
3403
  return schema.getDefaultValues({
3403
3404
  useDefaultSchemaValues: true,
3404
- constraints: completed,
3405
- strict
3405
+ constraints: completed
3406
3406
  });
3407
3407
  }
3408
- function initialFirstValidationGate(schema, strict) {
3409
- return !strict || schema.needsAsyncValidation?.() !== true;
3408
+ function initialFirstValidationGate(schema) {
3409
+ return schema.needsAsyncValidation?.() !== true;
3410
3410
  }
3411
3411
  function seedOriginalsFromBaseline(st, baseline, ensureOrdinals) {
3412
3412
  st.originals.clear();
@@ -3418,7 +3418,7 @@ function seedOriginalsFromBaseline(st, baseline, ensureOrdinals) {
3418
3418
  });
3419
3419
  }
3420
3420
  function queueInitialAsyncValidation(st) {
3421
- if (!st.ssr && st.strict && st.schema.needsAsyncValidation?.() === true) {
3421
+ if (!st.ssr && st.schema.needsAsyncValidation?.() === true) {
3422
3422
  queueMicrotask(() => scheduleFieldValidation(
3423
3423
  st,
3424
3424
  [],
@@ -3460,10 +3460,6 @@ function ensurePathOrdinal(st, key) {
3460
3460
  }
3461
3461
  return ordinal;
3462
3462
  }
3463
- function pathHasAsyncValidation(st, path) {
3464
- const { key } = canonicalizePath(path);
3465
- return pathHasAsyncValidationByKey(st, key, path);
3466
- }
3467
3463
  function pathHasAsyncValidationByKey(st, key, segments) {
3468
3464
  const cached = st.pathAsyncCache.get(key);
3469
3465
  if (cached !== void 0) return cached;
@@ -3585,19 +3581,16 @@ function touchFieldRecord(st, pathKey, path, patch) {
3585
3581
  path,
3586
3582
  updatedAt: patch.updatedAt ?? current?.updatedAt ?? null,
3587
3583
  connected: patch.connected ?? current?.connected ?? false,
3588
- // focused/blurred use an explicit-undefined guard because
3589
- // patches legitimately carry `null` to mark a disconnect — the
3590
- // `??` operator would short-circuit on null and fall through to
3591
- // `current`, losing the intent. `!== undefined` honours an
3592
- // explicit null and preserves current only on absence.
3584
+ // `focused` / `blurred` need an explicit-undefined guard: a patch
3585
+ // legitimately carries `null` to mark a disconnect, and `??` would
3586
+ // short-circuit on it and fall through to `current`, losing the intent.
3593
3587
  focused: patch.focused !== void 0 ? patch.focused : current?.focused ?? null,
3594
3588
  blurred: patch.blurred !== void 0 ? patch.blurred : current?.blurred ?? null,
3595
- // touched is plain `boolean`; `??` is equivalent to the explicit
3596
- // guard here because `false` is not nullish.
3589
+ // `touched` is a plain boolean, so `??` is equivalent to the guard above.
3597
3590
  touched: patch.touched ?? current?.touched ?? false,
3598
- // interacted is sticky-true; a merge patch only ever sets it, so
3599
- // `??` preserves the current bit. It flips back to false solely
3600
- // through the reset paths, which reconstruct the record outright.
3591
+ // `interacted` is sticky-true and a merge patch only ever sets it, so `??`
3592
+ // preserves the current bit. Only the reset paths clear it, and they
3593
+ // reconstruct the record outright.
3601
3594
  interacted: patch.interacted ?? current?.interacted ?? false,
3602
3595
  blurredAfterInteraction: patch.blurredAfterInteraction ?? current?.blurredAfterInteraction ?? false
3603
3596
  });
@@ -3606,10 +3599,15 @@ function commitWritePatches(st, patches, meta) {
3606
3599
  const now = (/* @__PURE__ */ new Date()).toISOString();
3607
3600
  for (const patch of patches) {
3608
3601
  const { key } = canonicalizePath(patch.path);
3609
- if (patch.kind === "added" && !st.originals.has(key)) {
3602
+ if (!st.originals.has(key)) {
3610
3603
  st.originals.set(key, { segments: patch.path, value: void 0 });
3611
3604
  }
3612
3605
  touchFieldRecord(st, key, patch.path, { updatedAt: now });
3606
+ st.pathSweep.track(patch.path, key);
3607
+ for (let i = 1; i < patch.path.length; i++) {
3608
+ const ancestor = patch.path.slice(0, i);
3609
+ st.pathSweep.track(ancestor, canonicalizePath(ancestor).key);
3610
+ }
3613
3611
  }
3614
3612
  for (const listener of st.formChangeListeners) {
3615
3613
  try {
@@ -3814,10 +3812,7 @@ function setValueAtPath(st, path, value, meta) {
3814
3812
  if (newlyAuthored && st.schema.isPreprocessOrCoerceLeaf(path)) {
3815
3813
  const modeForAuthoringTransition = meta?.instance?.validateOn ?? st.fieldValidationMode;
3816
3814
  if (modeForAuthoringTransition === "change") {
3817
- scheduleFieldValidation(st, path, false, {
3818
- ...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
3819
- ...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
3820
- });
3815
+ scheduleFieldValidation(st, path, false, meta?.instance);
3821
3816
  }
3822
3817
  }
3823
3818
  return true;
@@ -3833,15 +3828,12 @@ function setValueAtPath(st, path, value, meta) {
3833
3828
  st.arrayIdentity.realign(path);
3834
3829
  } else if (isContainer(currentValue) && !isContainer(value)) {
3835
3830
  if (subtreeHadRealBaseline(st, path, currentValue)) {
3836
- st.removedSubtrees.add(canonicalizePath(path).key);
3831
+ st.removedSubtrees.add(pathKey);
3837
3832
  }
3838
3833
  }
3839
3834
  const effectiveModeAfterWrite = meta?.instance?.validateOn ?? st.fieldValidationMode;
3840
3835
  if (effectiveModeAfterWrite === "change") {
3841
- scheduleFieldValidation(st, path, false, {
3842
- ...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
3843
- ...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
3844
- });
3836
+ scheduleFieldValidation(st, path, false, meta?.instance);
3845
3837
  }
3846
3838
  return true;
3847
3839
  }
@@ -3902,12 +3894,11 @@ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variant
3902
3894
  path: [...parentPath, ...err.path]
3903
3895
  }));
3904
3896
  applySchemaErrorsForSubtree(st, parentPath, reStamped);
3905
- const { key: parentKey2 } = canonicalizePath(parentPath);
3906
- const prevValidation = st.fieldValidationState.get(parentKey2);
3897
+ const prevValidation = st.fieldValidationState.get(parentKey);
3907
3898
  if (prevValidation !== void 0) {
3908
3899
  if (prevValidation.timer !== null) clearTimeout(prevValidation.timer);
3909
3900
  prevValidation.aborted = true;
3910
- st.fieldValidationState.delete(parentKey2);
3901
+ st.fieldValidationState.delete(parentKey);
3911
3902
  }
3912
3903
  appliedSync = true;
3913
3904
  }
@@ -3915,17 +3906,14 @@ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variant
3915
3906
  applyFormReplacement(st, nextForm, meta);
3916
3907
  for (const k of newBlankPaths) st.blankPaths.add(k);
3917
3908
  if (reshapeMode === "change" && !appliedSync) {
3918
- scheduleFieldValidation(st, parentPath, false, {
3919
- ...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
3920
- ...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
3921
- });
3909
+ scheduleFieldValidation(st, parentPath, false, meta?.instance);
3922
3910
  }
3923
3911
  return true;
3924
3912
  }
3925
- function scheduleFieldValidation(st, path, immediate, override) {
3926
- const effectiveMode = override?.mode ?? st.fieldValidationMode;
3913
+ function scheduleFieldValidation(st, path, immediate, instance) {
3914
+ const effectiveMode = instance?.validateOn ?? st.fieldValidationMode;
3927
3915
  if (effectiveMode === "submit") return;
3928
- const effectiveDebounce = override?.debounceMs ?? st.fieldValidationDebounceMs;
3916
+ const effectiveDebounce = instance?.debounceMs ?? st.fieldValidationDebounceMs;
3929
3917
  const { key } = canonicalizePath(path);
3930
3918
  const prev = st.fieldValidationState.get(key);
3931
3919
  if (prev !== void 0) {
@@ -4165,13 +4153,13 @@ function markFocused(st, path, focused, meta) {
4165
4153
  touchFieldRecord(st, key, path, {
4166
4154
  focused,
4167
4155
  blurred: !focused,
4168
- // `touched` flips to true on blur and stays true thereafter; while
4169
- // a field is currently focused we keep whatever value it held.
4156
+ // `touched` flips true on blur and stays; a focused field keeps whatever
4157
+ // it held.
4170
4158
  touched: focused ? current?.touched ?? false : true,
4171
- // `blurredAfterInteraction` flips true on the first blur that lands
4172
- // after a value edit and stays true. A tab-through blur before any
4173
- // edit leaves it false (`interacted` is still false at that blur),
4174
- // which is what keeps a clean tab-through from arming the gate.
4159
+ // `blurredAfterInteraction` flips true on the first blur after a value
4160
+ // edit and stays. A tab-through blur before any edit leaves it false,
4161
+ // since `interacted` is still false, which is what keeps a clean
4162
+ // tab-through from arming the gate.
4175
4163
  blurredAfterInteraction: !focused && current?.interacted === true ? true : current?.blurredAfterInteraction ?? false
4176
4164
  });
4177
4165
  const focusMode = meta?.instance?.validateOn ?? st.fieldValidationMode;
@@ -4199,10 +4187,7 @@ function markFocused(st, path, focused, meta) {
4199
4187
  });
4200
4188
  }
4201
4189
  if (changed) {
4202
- scheduleFieldValidation(st, path, true, {
4203
- ...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
4204
- ...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
4205
- });
4190
+ scheduleFieldValidation(st, path, true, meta?.instance);
4206
4191
  }
4207
4192
  }
4208
4193
  }
@@ -4214,10 +4199,9 @@ function markInteracted(st, path) {
4214
4199
  }
4215
4200
  function touchAtPath(st, segments) {
4216
4201
  const formValue = st.form.value;
4217
- for (const [, entry] of st.originals) {
4202
+ for (const [leafKey, entry] of st.originals) {
4218
4203
  if (!isPathPrefix(segments, entry.segments)) continue;
4219
4204
  if (!hasAtPath(formValue, entry.segments)) continue;
4220
- const leafKey = canonicalizePath(entry.segments).key;
4221
4205
  const current = st.fields.get(leafKey);
4222
4206
  if (current?.touched === true) continue;
4223
4207
  touchFieldRecord(st, leafKey, entry.segments, { touched: true });
@@ -4227,11 +4211,10 @@ function interactAtPath(st, segments) {
4227
4211
  if (st.effectiveDisabled.value) return false;
4228
4212
  const formValue = st.form.value;
4229
4213
  let interactedAny = false;
4230
- for (const [, entry] of st.originals) {
4214
+ for (const [leafKey, entry] of st.originals) {
4231
4215
  if (!isPathPrefix(segments, entry.segments)) continue;
4232
4216
  if (!hasAtPath(formValue, entry.segments)) continue;
4233
4217
  interactedAny = true;
4234
- const leafKey = canonicalizePath(entry.segments).key;
4235
4218
  const current = st.fields.get(leafKey);
4236
4219
  if (current?.touched === true && current.interacted === true && current.blurredAfterInteraction === true) {
4237
4220
  continue;
@@ -4283,11 +4266,7 @@ function adoptResolvedDefaults(st, value) {
4283
4266
  st.schema
4284
4267
  )
4285
4268
  );
4286
- seedOriginalsFromBaseline(
4287
- st,
4288
- computeBaselineResponse(st.schema, st.strict, st.defaultValues).data,
4289
- false
4290
- );
4269
+ seedOriginalsFromBaseline(st, computeBaselineResponse(st.schema, st.defaultValues).data, false);
4291
4270
  }
4292
4271
  function reset(st, nextDefaultValues) {
4293
4272
  const resetSource = nextDefaultValues === void 0 ? st.defaultValues : mergeSparseHydration(
@@ -4296,7 +4275,7 @@ function reset(st, nextDefaultValues) {
4296
4275
  st.schema
4297
4276
  );
4298
4277
  st.defaultValues = structuralSnapshot(resetSource);
4299
- const resetResponse = computeBaselineResponse(st.schema, st.strict, resetSource);
4278
+ const resetResponse = computeBaselineResponse(st.schema, resetSource);
4300
4279
  const next = resetResponse.data;
4301
4280
  rebuildAuthoredPaths(st, resetSource, next);
4302
4281
  applyFormReplacement(st, next);
@@ -4313,16 +4292,14 @@ function reset(st, nextDefaultValues) {
4313
4292
  st.blankPaths.add(key);
4314
4293
  }
4315
4294
  st.errorCells.clear();
4316
- if (st.strict && !resetResponse.success) {
4295
+ if (!resetResponse.success) {
4317
4296
  replaceErrorChannel(st, "schema", resetResponse.errors);
4318
4297
  }
4319
- if (st.strict) {
4320
- const syncResult = st.schema.validateAtPath(st.form.value, void 0, { sync: true });
4321
- if (!(syncResult instanceof Promise) && !syncResult.success) {
4322
- applySchemaErrorsForSubtree(st, [], syncResult.errors);
4323
- }
4298
+ const syncResult = st.schema.validateAtPath(st.form.value, void 0, { sync: true });
4299
+ if (!(syncResult instanceof Promise) && !syncResult.success) {
4300
+ applySchemaErrorsForSubtree(st, [], syncResult.errors);
4324
4301
  }
4325
- st.firstValidationDone.value = initialFirstValidationGate(st.schema, st.strict);
4302
+ st.firstValidationDone.value = initialFirstValidationGate(st.schema);
4326
4303
  queueInitialAsyncValidation(st);
4327
4304
  const now = (/* @__PURE__ */ new Date()).toISOString();
4328
4305
  for (const [pathKey, record] of st.fields) {
@@ -4412,10 +4389,6 @@ function clearFieldRecordFlags(st, pathKey) {
4412
4389
  if (record === void 0) return;
4413
4390
  st.fields.set(pathKey, withClearedHistoryFlags(record, (/* @__PURE__ */ new Date()).toISOString()));
4414
4391
  }
4415
- function isPristineAtPath(st, path) {
4416
- const { key, segments } = canonicalizePath(path);
4417
- return isPristineAtPathByKey(st, key, segments);
4418
- }
4419
4392
  function isPristineAtPathByKey(st, key, segments) {
4420
4393
  if (st.blankPaths.has(key) !== st.originalBlankPaths.has(key)) return false;
4421
4394
  const entry = st.originals.get(key);
@@ -4449,12 +4422,8 @@ function getFieldRecord(st, path) {
4449
4422
  const { key } = canonicalizePath(path);
4450
4423
  return st.fields.get(key);
4451
4424
  }
4452
- function getOriginalAtPath(st, path) {
4453
- const { key } = canonicalizePath(path);
4454
- return st.originals.get(key)?.value;
4455
- }
4456
4425
  function createFormStore(options) {
4457
- const { formKey, schema, defaultValues, strict = true, hydration } = options;
4426
+ const { formKey, schema, defaultValues, hydration } = options;
4458
4427
  const ssr = options.ssr === true;
4459
4428
  const ssrPrefetch = options.ssrPrefetch;
4460
4429
  const rememberVariants = options.rememberVariants !== false;
@@ -4462,22 +4431,16 @@ function createFormStore(options) {
4462
4431
  const fieldValidationDebounceMs = normalizeNumericOption({
4463
4432
  value: options.debounceMs ?? DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS,
4464
4433
  source: "useForm.debounceMs",
4465
- allowInfinity: false,
4466
4434
  min: 0,
4467
4435
  defaultValue: DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS
4468
4436
  });
4469
- const coerceIndex = resolveCoercionIndex(options.coerce);
4470
- const resolvedGetDisplayState = resolveGetDisplayState(options.getDisplayState);
4437
+ const coerceEnabled = resolveCoerceEnabled(options.coerce);
4471
4438
  const cleanupHooks = [];
4472
4439
  const modules = /* @__PURE__ */ new Map();
4473
4440
  const fieldValidatingSince = reactive(/* @__PURE__ */ new Map());
4474
4441
  const displayEngine = createDisplayEngine(ssr);
4475
4442
  cleanupHooks.push(() => displayEngine.dispose());
4476
- const schemaResponse = computeBaselineResponse(
4477
- schema,
4478
- strict,
4479
- defaultValues
4480
- );
4443
+ const schemaResponse = computeBaselineResponse(schema, defaultValues);
4481
4444
  const schemaInitialData = schemaResponse.data;
4482
4445
  const authoredPaths = /* @__PURE__ */ new Set();
4483
4446
  const initialData = hydration !== void 0 ? hydration.form : structuralSnapshot(schemaInitialData);
@@ -4490,16 +4453,15 @@ function createFormStore(options) {
4490
4453
  const v = getAtPath(toRaw(form.value), arraySegs);
4491
4454
  return Array.isArray(v) ? v.length : 0;
4492
4455
  });
4493
- const fields = reactive(/* @__PURE__ */ new Map());
4456
+ const fields = shallowReactive(/* @__PURE__ */ new Map());
4494
4457
  const domBinding = shallowRef(null);
4495
- const errorCells = reactive(/* @__PURE__ */ new Map());
4496
- const originals = reactive(/* @__PURE__ */ new Map());
4458
+ const errorCells = shallowReactive(/* @__PURE__ */ new Map());
4459
+ const originals = shallowReactive(/* @__PURE__ */ new Map());
4497
4460
  const removedSubtrees = /* @__PURE__ */ new Set();
4498
- const initialTransientList = hydration?.blankPaths ?? options.initialBlankPaths ?? [];
4499
4461
  const blankPaths = reactive(/* @__PURE__ */ new Set());
4500
4462
  const originalBlankPaths = /* @__PURE__ */ new Set();
4501
- for (const raw of initialTransientList) {
4502
- const key = coerceToPathKey(raw);
4463
+ const seededBlankPaths = hydration !== void 0 ? (hydration.blankPaths ?? []).map((dotted) => canonicalizePath(dotted).key) : options.initialBlankPaths ?? [];
4464
+ for (const key of seededBlankPaths) {
4503
4465
  blankPaths.add(key);
4504
4466
  originalBlankPaths.add(key);
4505
4467
  }
@@ -4516,6 +4478,9 @@ function createFormStore(options) {
4516
4478
  }
4517
4479
  return result;
4518
4480
  });
4481
+ const errorPathIndex = computed(
4482
+ () => buildErrorPathIndex(errorCells, derivedBlankErrors.value)
4483
+ );
4519
4484
  const submitting = ref(false);
4520
4485
  const activeSubmissions = ref(0);
4521
4486
  const submissionAttempts = ref(0);
@@ -4540,7 +4505,7 @@ function createFormStore(options) {
4540
4505
  const defaultsResolved = ref(false);
4541
4506
  const activated = ref(false);
4542
4507
  const activationPromise = ref(void 0);
4543
- const firstValidationDone = ref(initialFirstValidationGate(schema, strict));
4508
+ const firstValidationDone = ref(initialFirstValidationGate(schema));
4544
4509
  watch(activeValidations, (now, prev) => {
4545
4510
  if (prev > 0 && now === 0) {
4546
4511
  firstValidationDone.value = true;
@@ -4558,6 +4523,33 @@ function createFormStore(options) {
4558
4523
  const activeTransforms = ref(0);
4559
4524
  const transformRuns = /* @__PURE__ */ new Map();
4560
4525
  const transformWaiters = [];
4526
+ const errorWindows = /* @__PURE__ */ new Map();
4527
+ const pathSweep = createDynamicPathSweep({
4528
+ onFormChange: (listener) => {
4529
+ formChangeListeners.add(listener);
4530
+ },
4531
+ isFixedObjectAtPath: (path) => schema.isFixedObjectAtPath(path)
4532
+ });
4533
+ pathSweep.onEvict((key) => {
4534
+ fields.delete(key);
4535
+ fieldValidationState.delete(key);
4536
+ authoredPaths.delete(key);
4537
+ if (originals.get(key)?.value === void 0) originals.delete(key);
4538
+ errorWindows.delete(key);
4539
+ });
4540
+ const errorWindowAt = (prefix, prefixKey) => {
4541
+ let cached = errorWindows.get(prefixKey);
4542
+ if (cached === void 0) {
4543
+ const frozen = [...prefix];
4544
+ cached = computed((prev) => {
4545
+ const next = windowUnder(errorPathIndex.value, frozen);
4546
+ return prev !== void 0 && isSameWindow(prev, next) ? prev : next;
4547
+ });
4548
+ errorWindows.set(prefixKey, cached);
4549
+ pathSweep.track(frozen, prefixKey);
4550
+ }
4551
+ return cached.value;
4552
+ };
4561
4553
  const arrayBookkeeping = createArrayBookkeeping({
4562
4554
  form,
4563
4555
  fields,
@@ -4575,6 +4567,7 @@ function createFormStore(options) {
4575
4567
  touchFieldRecord: (pathKey, path, patch) => touchFieldRecord(st, pathKey, path, patch),
4576
4568
  decFieldValidation: (key) => decFieldValidation(st, key)
4577
4569
  });
4570
+ const bind = (fn) => (...args) => fn(st, ...args);
4578
4571
  const st = {
4579
4572
  // --- public data (the FormStore contract's state members) ---
4580
4573
  formKey,
@@ -4582,10 +4575,11 @@ function createFormStore(options) {
4582
4575
  fields,
4583
4576
  errorCells,
4584
4577
  derivedBlankErrors,
4578
+ errorWindowAt,
4585
4579
  originals,
4580
+ pathSweep,
4586
4581
  schema,
4587
4582
  ssr,
4588
- getDisplayState: resolvedGetDisplayState,
4589
4583
  submitting,
4590
4584
  activeSubmissions,
4591
4585
  submissionAttempts,
@@ -4613,20 +4607,16 @@ function createFormStore(options) {
4613
4607
  displayEngine,
4614
4608
  domBinding,
4615
4609
  modules,
4616
- coerceIndex,
4610
+ coerceEnabled,
4617
4611
  blankPaths,
4618
4612
  originalBlankPaths,
4619
4613
  // --- kernel-internal state ---
4620
- strict,
4621
- // Defensive copy, not a fix for an observed alias: today
4622
- // `getDefaultValues` happens to build a fresh tree, so form storage
4623
- // does not currently share structure with the consumer's object.
4624
- // That is an adapter implementation detail rather than a contract,
4625
- // and this field is now durable state that every `reset()` reads, so
4626
- // it should not depend on it. The copy also means a consumer who
4627
- // keeps a reference to the literal they passed cannot mutate the
4628
- // form's defaults from outside. `reset()` snapshots for a harder
4629
- // reason (see there) where an alias IS reachable.
4614
+ // A defensive copy. `getDefaultValues` building a fresh tree is an adapter
4615
+ // implementation detail, not a contract, and this field is durable state
4616
+ // that every `reset()` reads, so it must not depend on one. The copy also
4617
+ // stops a consumer holding a reference to the literal they passed from
4618
+ // mutating the form's defaults from outside. `reset()` snapshots for a
4619
+ // harder reason, where an alias IS reachable.
4630
4620
  defaultValues: structuralSnapshot(defaultValues),
4631
4621
  ssrPrefetch,
4632
4622
  rememberVariants,
@@ -4654,17 +4644,20 @@ function createFormStore(options) {
4654
4644
  transformTokenSeq: 0,
4655
4645
  warnedDisabledWrite: false,
4656
4646
  // --- methods: thin per-instance skins over the module kernel ---
4657
- rehydrate: () => rehydrate(st),
4658
- activate: () => activate(st),
4659
- adoptResolvedDefaults: (value) => adoptResolvedDefaults(st, value),
4660
- pathHasAsyncValidation: (path) => pathHasAsyncValidation(st, path),
4661
- pathHasAsyncValidationByKey: (key, segments) => pathHasAsyncValidationByKey(st, key, segments),
4662
- applyFormReplacement: (next, meta) => applyFormReplacement(st, next, meta),
4663
- setValueAtPath: (path, value, meta) => setValueAtPath(st, path, value, meta),
4664
- getValueAtPath: (path) => getValueAtPath(st, path),
4665
- arrayElementKey: (path) => arrayElementKey(st, path),
4666
- reset: (nextDefaultValues) => reset(st, nextDefaultValues),
4667
- resetField: (path) => resetField(st, path),
4647
+ rehydrate: bind(rehydrate),
4648
+ activate: bind(activate),
4649
+ adoptResolvedDefaults: bind(adoptResolvedDefaults),
4650
+ pathHasAsyncValidationByKey: bind(pathHasAsyncValidationByKey),
4651
+ applyFormReplacement: bind(applyFormReplacement),
4652
+ setValueAtPath: bind(setValueAtPath),
4653
+ getValueAtPath: bind(getValueAtPath),
4654
+ arrayElementKey: bind(arrayElementKey),
4655
+ reset: bind(reset),
4656
+ resetField: bind(resetField),
4657
+ // The schema/user pair below reads as a fold waiting to happen. Folding
4658
+ // them into a `setErrorsForPathIn(channel)` factory measures 10 B LARGER:
4659
+ // gzip has already collected the rent on two adjacent copies, and the
4660
+ // helper adds a name the original does not need.
4668
4661
  setSchemaErrorsForPath: (path, entries) => setErrorChannelForKey(
4669
4662
  st,
4670
4663
  canonicalizePath(path).key,
@@ -4673,7 +4666,7 @@ function createFormStore(options) {
4673
4666
  ),
4674
4667
  setAllSchemaErrors: (entries) => replaceErrorChannel(st, "schema", entries),
4675
4668
  clearSchemaErrors: (path) => clearErrorChannel(st, "schema", path),
4676
- applySchemaErrorsForSubtree: (path, entries) => applySchemaErrorsForSubtree(st, path, entries),
4669
+ applySchemaErrorsForSubtree: bind(applySchemaErrorsForSubtree),
4677
4670
  setAllUserErrors: (entries) => replaceErrorChannel(st, "user", entries),
4678
4671
  setUserErrorsForPath: (path, entries) => setErrorChannelForKey(
4679
4672
  st,
@@ -4682,37 +4675,34 @@ function createFormStore(options) {
4682
4675
  entries.length === 0 ? NO_ERRORS : [...entries]
4683
4676
  ),
4684
4677
  clearUserErrors: (path) => clearErrorChannel(st, "user", path),
4685
- restoreErrorCells: (entries) => restoreErrorCells(st, entries),
4686
- getErrorsForPath: (path) => getErrorsForPath(st, path),
4687
- ensurePathOrdinal: (key) => ensurePathOrdinal(st, key),
4688
- noteDomConnected: (path) => noteDomConnected(st, path),
4689
- noteDomDisconnected: (path) => noteDomDisconnected(st, path),
4690
- markFocused: (path, focused, meta) => markFocused(st, path, focused, meta),
4691
- markInteracted: (path) => markInteracted(st, path),
4692
- touchAtPath: (segments) => touchAtPath(st, segments),
4693
- interactAtPath: (segments) => interactAtPath(st, segments),
4694
- markConnectedOptimistically: (path) => markConnectedOptimistically(st, path),
4695
- isPristineAtPath: (path) => isPristineAtPath(st, path),
4696
- isPristineAtPathByKey: (key, segments) => isPristineAtPathByKey(st, key, segments),
4697
- hasStructuralChangeUnder: (path) => hasStructuralChangeUnder(st, path),
4698
- hasRemovedSubtreeUnder: (path) => hasRemovedSubtreeUnder(st, path),
4699
- getFieldRecord: (path) => getFieldRecord(st, path),
4700
- getOriginalAtPath: (path) => getOriginalAtPath(st, path),
4701
- cancelFieldValidation: () => cancelFieldValidation(st),
4702
- beginTransform: (key, holder) => beginTransform(st, key, holder),
4703
- isCurrentTransform: (key, token) => isCurrentTransform(st, key, token),
4704
- endTransform: (key, token) => endTransform(st, key, token),
4705
- setTransformError: (key, err) => setTransformError(st, key, err),
4706
- cancelTransforms: () => cancelTransforms(st),
4707
- cancelTransformsUnder: (prefix) => cancelTransformsUnder(st, prefix),
4708
- settleTransforms: (path) => settleTransforms(st, path),
4709
- scheduleFieldValidation: (path, immediate, override) => scheduleFieldValidation(st, path, immediate, override),
4710
- onFormChange: (listener) => onFormChange(st, listener),
4711
- onSubmitSuccess: (listener) => onSubmitSuccess(st, listener),
4712
- onReset: (listener) => onReset(st, listener),
4713
- emitSubmitSuccess: () => emitSubmitSuccess(st),
4714
- registerCleanup: (fn) => registerCleanup(st, fn),
4715
- dispose: () => dispose(st)
4678
+ restoreErrorCells: bind(restoreErrorCells),
4679
+ getErrorsForPath: bind(getErrorsForPath),
4680
+ ensurePathOrdinal: bind(ensurePathOrdinal),
4681
+ noteDomConnected: bind(noteDomConnected),
4682
+ noteDomDisconnected: bind(noteDomDisconnected),
4683
+ markFocused: bind(markFocused),
4684
+ markInteracted: bind(markInteracted),
4685
+ touchAtPath: bind(touchAtPath),
4686
+ interactAtPath: bind(interactAtPath),
4687
+ markConnectedOptimistically: bind(markConnectedOptimistically),
4688
+ isPristineAtPathByKey: bind(isPristineAtPathByKey),
4689
+ hasStructuralChangeUnder: bind(hasStructuralChangeUnder),
4690
+ hasRemovedSubtreeUnder: bind(hasRemovedSubtreeUnder),
4691
+ getFieldRecord: bind(getFieldRecord),
4692
+ cancelFieldValidation: bind(cancelFieldValidation),
4693
+ beginTransform: bind(beginTransform),
4694
+ isCurrentTransform: bind(isCurrentTransform),
4695
+ endTransform: bind(endTransform),
4696
+ setTransformError: bind(setTransformError),
4697
+ cancelTransformsUnder: bind(cancelTransformsUnder),
4698
+ settleTransforms: bind(settleTransforms),
4699
+ scheduleFieldValidation: bind(scheduleFieldValidation),
4700
+ onFormChange: bind(onFormChange),
4701
+ onSubmitSuccess: bind(onSubmitSuccess),
4702
+ onReset: bind(onReset),
4703
+ emitSubmitSuccess: bind(emitSubmitSuccess),
4704
+ registerCleanup: bind(registerCleanup),
4705
+ dispose: bind(dispose)
4716
4706
  };
4717
4707
  rebuildAuthoredPaths(st, defaultValues, schemaInitialData);
4718
4708
  seedOriginalsFromBaseline(st, schemaInitialData, true);
@@ -4751,7 +4741,7 @@ function createFormStore(options) {
4751
4741
  blurredAfterInteraction: false
4752
4742
  });
4753
4743
  });
4754
- if (strict && !schemaResponse.success) {
4744
+ if (!schemaResponse.success) {
4755
4745
  replaceErrorChannel(st, "schema", schemaResponse.errors);
4756
4746
  }
4757
4747
  }
@@ -4782,19 +4772,18 @@ function useAbstractForm(configuration, options) {
4782
4772
  const resolvedDefaults = resolveTrichotomy(configuration.defaultValues);
4783
4773
  const materialisedDefaults = resolvedDefaults.kind === "sync" ? resolvedDefaults.value : void 0;
4784
4774
  const { defaultValues: _droppedDefaults, ...configWithoutDefaults } = configuration;
4785
- const trichotomyOverride = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
4786
- const merged = mergeWithDefaults(registry.defaults, trichotomyOverride);
4787
- const maxRecursionDepth = normalizeNumericOption({
4788
- value: merged.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH,
4789
- source: "useForm.maxRecursionDepth",
4790
- allowInfinity: true,
4791
- min: 0,
4792
- defaultValue: DEFAULT_MAX_RECURSION_DEPTH
4793
- });
4794
- const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
4775
+ const materialisedConfiguration = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
4795
4776
  const existing = registry.forms.get(key);
4777
+ const resolvedSchema = existing === void 0 || false ? getComputedSchema(key, configuration.schema, {
4778
+ maxRecursionDepth: DEFAULT_MAX_RECURSION_DEPTH
4779
+ }) : existing.schema;
4796
4780
  const hadPendingHydration = registry.pendingHydration.has(key);
4797
- const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
4781
+ const state = existing ?? buildFreshState(
4782
+ key,
4783
+ resolvedSchema,
4784
+ materialisedConfiguration,
4785
+ registry
4786
+ );
4798
4787
  if (existing !== void 0) ; else if (resolvedDefaults.kind === "sync") {
4799
4788
  state.defaultsResolved.value = true;
4800
4789
  }
@@ -4819,8 +4808,8 @@ function useAbstractForm(configuration, options) {
4819
4808
  const releaseConsumer = registry.trackConsumer(key);
4820
4809
  onScopeDispose(releaseConsumer);
4821
4810
  }
4822
- if (existing === void 0 && merged.history !== void 0) {
4823
- const historyModule = merged.history.attach(state);
4811
+ if (existing === void 0 && materialisedConfiguration.history !== void 0) {
4812
+ const historyModule = materialisedConfiguration.history.attach(state);
4824
4813
  state.modules.set(HISTORY_MODULE_KEY, historyModule);
4825
4814
  state.registerCleanup(() => historyModule.dispose());
4826
4815
  }
@@ -4833,36 +4822,16 @@ function useAbstractForm(configuration, options) {
4833
4822
  provide(kFormInstanceId, formInstanceId);
4834
4823
  }
4835
4824
  const apiOptions = pickDefined({
4836
- onInvalidSubmit: merged.onInvalidSubmit,
4825
+ focusOnInvalidSubmit: materialisedConfiguration.focusOnInvalidSubmit,
4837
4826
  history: state.modules.get(HISTORY_MODULE_KEY),
4838
- validateOn: merged.validateOn,
4839
- debounceMs: merged.debounceMs,
4840
- getDisplayState: merged.getDisplayState,
4841
- coerce: merged.coerce,
4842
- rememberVariants: merged.rememberVariants,
4843
- autoAria: merged.autoAria
4827
+ validateOn: materialisedConfiguration.validateOn,
4828
+ debounceMs: materialisedConfiguration.debounceMs,
4829
+ coerce: materialisedConfiguration.coerce,
4830
+ rememberVariants: materialisedConfiguration.rememberVariants
4844
4831
  });
4845
4832
  const api = buildFormApi(state, formInstanceId, apiOptions);
4846
4833
  return api;
4847
4834
  }
4848
- function mergeWithDefaults(defaults, configuration) {
4849
- return {
4850
- ...configuration,
4851
- ...pickDefined({
4852
- strict: configuration.strict ?? defaults.strict,
4853
- onInvalidSubmit: configuration.onInvalidSubmit ?? defaults.onInvalidSubmit,
4854
- history: configuration.history ?? defaults.history,
4855
- rememberVariants: configuration.rememberVariants ?? defaults.rememberVariants,
4856
- disabled: configuration.disabled ?? defaults.disabled,
4857
- coerce: configuration.coerce ?? defaults.coerce,
4858
- validateOn: configuration.validateOn ?? defaults.validateOn,
4859
- debounceMs: configuration.debounceMs ?? defaults.debounceMs,
4860
- getDisplayState: configuration.getDisplayState ?? defaults.getDisplayState,
4861
- maxRecursionDepth: configuration.maxRecursionDepth ?? defaults.maxRecursionDepth,
4862
- autoAria: configuration.autoAria ?? defaults.autoAria
4863
- })
4864
- };
4865
- }
4866
4835
  const HISTORY_MODULE_KEY = "history";
4867
4836
  function buildFreshState(key, schema, configuration, registry) {
4868
4837
  const pending = registry.pendingHydration.get(key);
@@ -4882,21 +4851,17 @@ function buildFreshState(key, schema, configuration, registry) {
4882
4851
  hydration: pending,
4883
4852
  ssr: registry.ssr,
4884
4853
  ...pickDefined({
4885
- strict: configuration.strict,
4886
4854
  validateOn: configuration.validateOn,
4887
4855
  debounceMs: configuration.debounceMs,
4888
4856
  rememberVariants: configuration.rememberVariants,
4889
4857
  disabled: configuration.disabled,
4890
4858
  coerce: configuration.coerce,
4891
- getDisplayState: configuration.getDisplayState,
4892
4859
  initialBlankPaths
4893
4860
  }),
4894
- // Server-only: bind the SSR prefetch coordination handles. `enqueue`
4895
- // records intent on every `state.activate()` so a wizard skip-list
4896
- // override or a future transform mark has a consistent set to diff
4897
- // against; `shouldFire` lets the activate path bail when the
4898
- // wizard explicitly skipped this key — even an explicit
4899
- // `form.activate()` defers to the wizard's render-efficiency
4861
+ // Server only. `enqueue` records intent on every `state.activate()`
4862
+ // so a wizard skip-list override has a consistent set to diff
4863
+ // against; `shouldFire` lets the activate path bail on a key the
4864
+ // wizard skipped. Even an explicit `form.activate()` defers to that
4900
4865
  // skip-list on the server.
4901
4866
  ...registry.ssr ? {
4902
4867
  ssrPrefetch: {
@@ -5135,25 +5100,18 @@ function createWizardHistory(param) {
5135
5100
  };
5136
5101
  }
5137
5102
 
5138
- const NOOP_FINGERPRINT = "attaform:wizard-noop";
5139
5103
  const EMPTY_SLIM_KINDS = /* @__PURE__ */ new Set();
5140
- function buildNoopWizardSchema(formKey) {
5141
- const emptyValue = {};
5142
- const success = {
5143
- success: true,
5144
- data: emptyValue,
5145
- errors: void 0,
5146
- formKey
5147
- };
5148
- const defaultsResponse = {
5149
- success: true,
5150
- data: emptyValue,
5151
- errors: void 0,
5152
- formKey
5153
- };
5154
- return {
5155
- fingerprint: () => Promise.resolve(NOOP_FINGERPRINT),
5156
- getDefaultValues: () => defaultsResponse,
5104
+ const EMPTY_VALUE = {};
5105
+ const NOOP_SUCCESS = {
5106
+ success: true,
5107
+ data: EMPTY_VALUE,
5108
+ errors: void 0
5109
+ };
5110
+ const NOOP_DEFAULTS = NOOP_SUCCESS;
5111
+ let NOOP_SCHEMA;
5112
+ function buildNoopWizardSchema() {
5113
+ NOOP_SCHEMA ?? (NOOP_SCHEMA = {
5114
+ getDefaultValues: () => NOOP_DEFAULTS,
5157
5115
  getDefaultAtPath: () => void 0,
5158
5116
  getEmptyValueAtPath: () => void 0,
5159
5117
  isPreprocessOrCoerceLeaf: () => false,
@@ -5164,13 +5122,14 @@ function buildNoopWizardSchema(formKey) {
5164
5122
  // addressable, root included.
5165
5123
  entryKeyKindAtPath: () => void 0,
5166
5124
  getSchemasAtPath: () => [],
5167
- validateAtPath: () => success,
5125
+ validateAtPath: () => NOOP_SUCCESS,
5168
5126
  getSlimPrimitiveTypesAtPath: () => new Set(EMPTY_SLIM_KINDS),
5169
5127
  isLeafAtPath: () => false,
5170
5128
  isRequiredAtPath: () => false,
5171
5129
  getUnionDiscriminatorAtPath: () => void 0,
5172
5130
  hasDiscriminatedUnions: () => false
5173
- };
5131
+ });
5132
+ return NOOP_SCHEMA;
5174
5133
  }
5175
5134
 
5176
5135
  function buildCallableReadonlySnapshotProxy(opts) {
@@ -5285,7 +5244,7 @@ function useWizard(options) {
5285
5244
  if (typeof slot !== "string") continue;
5286
5245
  if (noopForms.has(slot)) continue;
5287
5246
  const noop = useAbstractForm({
5288
- schema: buildNoopWizardSchema(slot),
5247
+ schema: buildNoopWizardSchema(),
5289
5248
  key: slot
5290
5249
  });
5291
5250
  noopForms.set(slot, noop);
@@ -5296,7 +5255,7 @@ function useWizard(options) {
5296
5255
  const noop = lazyNoopScope.run(
5297
5256
  () => useAbstractForm(
5298
5257
  {
5299
- schema: buildNoopWizardSchema(key),
5258
+ schema: buildNoopWizardSchema(),
5300
5259
  key
5301
5260
  },
5302
5261
  { registry }
@@ -5364,27 +5323,33 @@ function useWizard(options) {
5364
5323
  return activeKey.value === "" ? void 0 : activeKey.value;
5365
5324
  }
5366
5325
  };
5367
- function lazyComputedFor(marker) {
5326
+ function lazyComputedFor(marker, index) {
5368
5327
  const cached = lazyComputeds.get(marker);
5369
5328
  if (cached !== void 0) return cached;
5370
5329
  const c = computed(() => {
5371
5330
  void lazyEpoch.value;
5372
- return marker.resolve(slotCtx);
5331
+ return callConsumerFn(
5332
+ () => marker.resolve(slotCtx),
5333
+ void 0);
5373
5334
  });
5374
5335
  lazyComputeds.set(marker, c);
5375
5336
  return c;
5376
5337
  }
5377
5338
  const MAX_SLOT_DEPTH = 32;
5378
- function normalizeSlot(slot, ctx, gated, depth) {
5339
+ function normalizeSlot(slot, ctx, gated, depth, index) {
5379
5340
  if (depth > MAX_SLOT_DEPTH) {
5380
5341
  return void 0;
5381
5342
  }
5382
5343
  if (slot === void 0 || slot === null) return void 0;
5383
5344
  if (typeof slot === "string") return { form: getOrBuildNoop(slot), gated };
5384
5345
  if (isGateMarker(slot)) return normalizeSlot(slot.inner, ctx, true, depth + 1);
5385
- if (isLazyMarker(slot)) return normalizeSlot(lazyComputedFor(slot).value, ctx, gated, depth + 1);
5346
+ if (isLazyMarker(slot)) {
5347
+ return normalizeSlot(lazyComputedFor(slot).value, ctx, gated, depth + 1);
5348
+ }
5386
5349
  if (typeof slot === "function") {
5387
- const result = slot(ctx);
5350
+ const result = callConsumerFn(
5351
+ () => slot(ctx),
5352
+ void 0);
5388
5353
  return normalizeSlot(result, ctx, gated, depth + 1);
5389
5354
  }
5390
5355
  if (isAnyForm(slot)) return { form: slot, gated };
@@ -5702,13 +5667,14 @@ function useWizard(options) {
5702
5667
  seedRef.value = resolved.value;
5703
5668
  applyGateSeed(resolved.value);
5704
5669
  } else {
5705
- const eager = resolved.factory();
5670
+ const eager = callConsumerFn(() => resolved.factory(), void 0);
5706
5671
  if (eager instanceof Promise) {
5707
5672
  void eager.then((value) => {
5708
5673
  seedRef.value = value;
5709
5674
  applyGateSeed(value);
5675
+ }).catch((err) => {
5710
5676
  });
5711
- } else {
5677
+ } else if (eager !== void 0) {
5712
5678
  seedRef.value = eager;
5713
5679
  applyGateSeed(eager);
5714
5680
  }
@@ -5781,10 +5747,13 @@ function useWizard(options) {
5781
5747
  const statuses = buildWizardStatusesProxy(statusesRecord);
5782
5748
  const progressOverride = options.progress;
5783
5749
  const progress = computed(() => {
5750
+ const list = compiledSteps.value;
5784
5751
  if (progressOverride !== void 0) {
5785
- return progressOverride(compiledSteps.value);
5752
+ const override = callConsumerFn(
5753
+ () => progressOverride(list),
5754
+ null);
5755
+ if (override !== null) return override;
5786
5756
  }
5787
- const list = compiledSteps.value;
5788
5757
  if (list.length === 0) return 0;
5789
5758
  let valid = 0;
5790
5759
  for (const step of list) {
@@ -5812,11 +5781,15 @@ function useWizard(options) {
5812
5781
  historyHandle.subscribe((value) => {
5813
5782
  urlMirror.value = value;
5814
5783
  });
5815
- const restoreCallback = options.restore === false ? void 0 : options.restore !== void 0 ? options.restore : () => {
5784
+ const consumerRestore = options.restore;
5785
+ const restoreCallback = consumerRestore === false ? void 0 : consumerRestore !== void 0 ? () => callConsumerFn(() => consumerRestore(), void 0) : () => {
5816
5786
  const value = urlMirror.value;
5817
5787
  return value === void 0 ? void 0 : { step: value };
5818
5788
  };
5819
- const persistCallback = options.persist === false ? void 0 : options.persist !== void 0 ? options.persist : (state) => {
5789
+ const consumerPersist = options.persist;
5790
+ const persistCallback = consumerPersist === false ? void 0 : consumerPersist !== void 0 ? (state) => {
5791
+ callConsumerFn(() => consumerPersist(state), void 0);
5792
+ } : (state) => {
5820
5793
  if (state.step === void 0) return;
5821
5794
  const current = historyHandle.read();
5822
5795
  const effectiveCurrent = current !== void 0 && isCompiledKey(current) ? current : firstKey();
@@ -6380,4 +6353,4 @@ function escapeForInlineScript(json) {
6380
6353
  });
6381
6354
  }
6382
6355
 
6383
- export { AttaformErrorCode as A, DEFAULT_TIMINGS as D, defaultDisplayState as a, defineCoercion as b, injectWizard as c, defaultCoercionRules as d, escapeForInlineScript as e, isUnset as f, gate as g, useAbstractForm as h, injectForm as i, useRegister as j, useWizard as k, lazy as l, makeDefaultDisplayState as m, isPlainRecord as n, crossCopyState as o, getAtPath as p, humanize as q, slimKindOf as s, unset as u };
6356
+ export { AttaformErrorCode as A, injectWizard as a, isUnset as b, useAbstractForm as c, useRegister as d, escapeForInlineScript as e, useWizard as f, gate as g, getAtPath as h, injectForm as i, humanize as j, isPlainRecord as k, lazy as l, slimKindOf as s, unset as u };