attaform 0.29.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/abstract.d.mts +5 -5
  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 +1 -1
  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.BK6Ge2qR.mjs} +34 -188
  11. package/dist/dev/shared/{attaform-dev.A-TpJW0B.mjs → attaform-dev.BQc5QV3Y.mjs} +1 -19
  12. package/dist/dev/shared/{attaform-dev.DrzObAZo.mjs → attaform-dev.BcNtyOmf.mjs} +4 -4
  13. package/dist/dev/shared/{attaform-dev.ButTkwHd.mjs → attaform-dev.BcYT6tFh.mjs} +7 -8
  14. package/dist/dev/shared/{attaform-dev.BgM6FdtA.mjs → attaform-dev.BisFstxh.mjs} +91 -59
  15. package/dist/dev/shared/{attaform-dev.DHuydLAC.mjs → attaform-dev.CQ2rJiAX.mjs} +560 -631
  16. package/dist/dev/shared/{attaform-dev.D8aoKb89.mjs → attaform-dev.D6CLvaQT.mjs} +8 -8
  17. package/dist/dev/shared/{attaform-dev.q6UutkCx.mjs → attaform-dev.DTrHonpc.mjs} +69 -120
  18. package/dist/dev/shared/{attaform-dev.DD5m2W51.mjs → attaform-dev.DUZckyV8.mjs} +1 -3
  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 +2 -2
  23. package/dist/directive.mjs +1 -1
  24. package/dist/history.d.mts +1 -1
  25. package/dist/history.mjs +1 -2
  26. package/dist/index.d.mts +6 -6
  27. package/dist/index.mjs +5 -5
  28. package/dist/nuxt.d.mts +2 -11
  29. package/dist/nuxt.mjs +0 -1
  30. package/dist/runtime/plugins/attaform.mjs +3 -3
  31. package/dist/shared/{attaform.DffRL5pP.mjs → attaform.B3resMQ1.mjs} +8 -8
  32. package/dist/shared/{attaform.Cvao8bW5.mjs → attaform.BRDDQeHV.mjs} +6 -6
  33. package/dist/shared/{attaform.C2MVyuxC.mjs → attaform.BXneBxEj.mjs} +34 -188
  34. package/dist/shared/{attaform.BndjUUYX.mjs → attaform.ByWP6kSx.mjs} +4 -4
  35. package/dist/shared/{attaform.D1fnTifF.mjs → attaform.CJ8DpRGj.mjs} +69 -120
  36. package/dist/shared/{attaform.B0dKL8ST.mjs → attaform.CUfrMK7t.mjs} +91 -59
  37. package/dist/shared/{attaform.CZYt_5N_.mjs → attaform.CdZIEf7_.mjs} +1 -19
  38. package/dist/shared/{attaform.axHXVQO7.d.mts → attaform.Clz0ArIi.d.mts} +1 -1
  39. package/dist/shared/{attaform.CZMAYwSK.mjs → attaform.DNJOWjQw.mjs} +559 -588
  40. package/dist/shared/{attaform.CIWLMY-R.d.mts → attaform.Rc9u4Vfw.d.mts} +1 -1
  41. package/dist/shared/{attaform.DDtqHYv1.d.mts → attaform.UGyOK83T.d.mts} +115 -214
  42. package/dist/shared/{attaform.D0x-qSXm.mjs → attaform.WiGdIqgx.mjs} +1 -3
  43. package/dist/shared/{attaform.CsoeRi3P.d.mts → attaform.bKLzMU0o.d.mts} +99 -493
  44. package/dist/vite.mjs +5 -0
  45. package/dist/zod-v3.d.mts +4 -4
  46. package/dist/zod-v3.mjs +6 -6
  47. package/dist/zod-v4.d.mts +9 -9
  48. package/dist/zod-v4.mjs +6 -6
  49. package/dist/zod.d.mts +4 -4
  50. package/dist/zod.mjs +5 -5
  51. package/package.json +3 -2
  52. package/skills/attaform/SKILL.md +1 -1
  53. package/skills/attaform/references/custom-components.md +1 -1
  54. package/dist/chunks/fingerprint.mjs +0 -183
  55. package/dist/chunks/fingerprint2.mjs +0 -159
  56. package/dist/dev/chunks/fingerprint.mjs +0 -183
  57. package/dist/dev/chunks/fingerprint2.mjs +0 -159
  58. package/dist/dev/shared/attaform-dev.BxfsuwBR.mjs +0 -44
  59. package/dist/shared/attaform.BxfsuwBR.mjs +0 -44
@@ -1,8 +1,8 @@
1
- import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, nextTick, warn, reactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope } from 'vue';
2
- 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-dev.ButTkwHd.mjs';
3
- import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform-dev.DD5m2W51.mjs';
4
- import { R as REGISTER_OWNER_MARKER, d as armDomBinding, V as V_REGISTER_MARKER } from './attaform-dev.D8aoKb89.mjs';
5
- 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-dev.A-TpJW0B.mjs';
1
+ import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, nextTick, warn, reactive, shallowReactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope } from 'vue';
2
+ 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, t as ANONYMOUS_WIZARD_KEY_PREFIX } from './attaform-dev.BcYT6tFh.mjs';
3
+ import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform-dev.DUZckyV8.mjs';
4
+ import { R as REGISTER_OWNER_MARKER, d as armDomBinding, V as V_REGISTER_MARKER } from './attaform-dev.D6CLvaQT.mjs';
5
+ 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-dev.BQc5QV3Y.mjs';
6
6
 
7
7
  const AttaformErrorCode = {
8
8
  /** A required field is in the blank set — user hasn't supplied a value. */
@@ -77,11 +77,6 @@ function makeBlankRequiredError(segments) {
77
77
  }
78
78
  const NO_ERRORS = Object.freeze([]);
79
79
 
80
- function crossCopyState(key, create) {
81
- const carrier = globalThis;
82
- return carrier[key] ?? (carrier[key] = create());
83
- }
84
-
85
80
  function isGateOpen(field, formMeta) {
86
81
  return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
87
82
  }
@@ -103,45 +98,32 @@ function earliestNonNull(a, b) {
103
98
  return a < b ? a : b;
104
99
  }
105
100
  const DEFAULT_TIMINGS = { showDelay: 120, minVisible: 120 };
101
+ const { showDelay, minVisible } = DEFAULT_TIMINGS;
106
102
  const FOCUS_OUT_GRACE = 16;
107
- const defaultFamily = crossCopyState(
108
- /* @__PURE__ */ Symbol.for("attaform:default-display-state-family"),
109
- () => /* @__PURE__ */ new WeakSet()
110
- );
111
- function isDefaultDisplayState(fn) {
112
- return defaultFamily.has(fn);
113
- }
114
- function makeDefaultDisplayState({
115
- showDelay,
116
- minVisible
117
- }) {
118
- const reducer = (prev, { field, formMeta, validatingSince, transformingSince, now }) => {
119
- const verdict = computeVerdict(field, formMeta);
120
- if (!isGateOpen(field, formMeta)) return { display: verdict };
121
- const inFlightSince = earliestNonNull(validatingSince, transformingSince);
122
- if (inFlightSince === null) {
123
- if (prev.display === "pending") {
124
- const shownAt = prev.pendingShownAt ?? now;
125
- if (now < shownAt + minVisible)
126
- return { display: "pending", pendingShownAt: shownAt, reviewAt: shownAt + minVisible };
127
- }
128
- return { display: verdict };
129
- }
130
- if (prev.display === "pending")
131
- return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
132
- const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
133
- if (now - inFlightSince < window) {
134
- return { display: prev.display, reviewAt: inFlightSince + window };
103
+ const defaultDisplayState = (prev, { field, formMeta, validatingSince, transformingSince, now }) => {
104
+ const verdict = computeVerdict(field, formMeta);
105
+ if (!isGateOpen(field, formMeta)) return { display: verdict };
106
+ const inFlightSince = earliestNonNull(validatingSince, transformingSince);
107
+ if (inFlightSince === null) {
108
+ if (prev.display === "pending") {
109
+ const shownAt = prev.pendingShownAt ?? now;
110
+ if (now < shownAt + minVisible)
111
+ return {
112
+ display: "pending",
113
+ pendingShownAt: shownAt,
114
+ reviewAt: shownAt + minVisible
115
+ };
135
116
  }
136
- return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
137
- };
138
- defaultFamily.add(reducer);
139
- return reducer;
140
- }
141
- const defaultDisplayState = makeDefaultDisplayState(DEFAULT_TIMINGS);
142
- function resolveGetDisplayState(config) {
143
- return config ?? defaultDisplayState;
144
- }
117
+ return { display: verdict };
118
+ }
119
+ if (prev.display === "pending")
120
+ return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
121
+ const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
122
+ if (now - inFlightSince < window) {
123
+ return { display: prev.display, reviewAt: inFlightSince + window };
124
+ }
125
+ return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
126
+ };
145
127
 
146
128
  function hashStableString(input, seed = 0) {
147
129
  let h1 = 3735928559 ^ seed;
@@ -229,16 +211,27 @@ function descendStep(value, segment) {
229
211
  }
230
212
  function getAtPath(root, path) {
231
213
  if (path.length === 0) return root;
232
- let current = root;
233
- for (const segment of path) {
234
- const next = descendStep(current, segment);
235
- if (next === NOT_FOUND) return void 0;
236
- current = next;
214
+ try {
215
+ let current = root;
216
+ for (const segment of path) {
217
+ const next = descendStep(current, segment);
218
+ if (next === NOT_FOUND) return void 0;
219
+ current = next;
220
+ }
221
+ return current;
222
+ } catch {
223
+ return void 0;
237
224
  }
238
- return current;
239
225
  }
240
226
  function hasAtPath(root, path) {
241
227
  if (path.length === 0) return true;
228
+ try {
229
+ return hasAtPathUnguarded(root, path);
230
+ } catch {
231
+ return false;
232
+ }
233
+ }
234
+ function hasAtPathUnguarded(root, path) {
242
235
  let current = root;
243
236
  for (let i = 0; i < path.length - 1; i++) {
244
237
  const segment = path[i];
@@ -265,29 +258,15 @@ function isPlainRecord(value) {
265
258
  const proto = Object.getPrototypeOf(value);
266
259
  return proto === null || proto === Object.prototype;
267
260
  }
261
+ const NO_SCHEMA_DEFAULTS = /* @__PURE__ */ new Set();
262
+ const NO_SCHEMA_FILL = {
263
+ getDefaultAtPath: () => void 0,
264
+ arrayShapeAtPath: () => null,
265
+ getSlimPrimitiveTypesAtPath: () => NO_SCHEMA_DEFAULTS,
266
+ entryKeyKindAtPath: () => void 0
267
+ };
268
268
  function setAtPath(root, path, value) {
269
- return setAtPathOffset(root, path, value, 0);
270
- }
271
- function setAtPathOffset(root, path, value, offset) {
272
- if (offset >= path.length) return value;
273
- const head = path[offset];
274
- const nextOffset = offset + 1;
275
- if (root instanceof Map) {
276
- const next = new Map(root);
277
- const key = mapKeyForSegment(root, head);
278
- next.set(key, setAtPathOffset(root.get(key), path, value, nextOffset));
279
- return next;
280
- }
281
- if (!isRebuildableContainer(root, head)) return root;
282
- if (typeof head === "number") {
283
- const arr = Array.isArray(root) ? [...root] : [];
284
- while (arr.length <= head) arr.push(void 0);
285
- arr[head] = setAtPathOffset(arr[head], path, value, nextOffset);
286
- return arr;
287
- }
288
- const rec = isPlainRecord(root) ? { ...root } : {};
289
- safeAssign(rec, head, setAtPathOffset(safeOwnRead(rec, head), path, value, nextOffset));
290
- return rec;
269
+ return setAtPathWithSchemaFill(root, NO_SCHEMA_FILL, path, value);
291
270
  }
292
271
  const NO_IN_PLACE = { applied: false };
293
272
  function tryInPlaceLeafWrite(root, path, value) {
@@ -493,10 +472,10 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
493
472
  }
494
473
  const rec = isPlainRecord(root) ? { ...root } : {};
495
474
  if (isLeafStep) {
496
- rec[head] = value;
475
+ safeAssign(rec, head, value);
497
476
  return rec;
498
477
  }
499
- const existing = rec[head];
478
+ const existing = safeOwnRead(rec, head);
500
479
  let childRoot;
501
480
  if (existing === void 0 || existing !== null && typeof existing !== "object") {
502
481
  const intermPath = [...fullPath.slice(0, startIdx + 1)];
@@ -505,11 +484,14 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
505
484
  } else {
506
485
  childRoot = existing;
507
486
  }
508
- rec[head] = setAtPathWithSchemaFillImpl(childRoot, schema, fullPath, value, startIdx + 1);
487
+ safeAssign(
488
+ rec,
489
+ head,
490
+ setAtPathWithSchemaFillImpl(childRoot, schema, fullPath, value, startIdx + 1)
491
+ );
509
492
  return rec;
510
493
  }
511
494
 
512
- const warnedDisplayStatePredicates = /* @__PURE__ */ new WeakSet();
513
495
  function isUnderStubAncestor(state, segments) {
514
496
  for (let i = 0; i < segments.length; i++) {
515
497
  const ancestorPath = segments.slice(0, i);
@@ -523,16 +505,15 @@ function isUnderStubAncestor(state, segments) {
523
505
  }
524
506
  return false;
525
507
  }
526
- function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, sweep, options) {
508
+ function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, sweep) {
527
509
  const cache = /* @__PURE__ */ new Map();
528
- const predicate = options?.getDisplayState;
529
510
  sweep.onEvict((key) => cache.delete(key));
530
511
  return function getFieldState(pathInput) {
531
512
  const { segments, key } = canonicalizePath(pathInput);
532
513
  const cached = cache.get(key);
533
514
  if (cached !== void 0) return cached;
534
515
  const c = computed(
535
- () => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase, predicate) : buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase, predicate)
516
+ () => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase) : buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase)
536
517
  );
537
518
  cache.set(key, c);
538
519
  sweep.track(segments, key);
@@ -549,7 +530,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
549
530
  const record = state.fields.get(key);
550
531
  const value = state.getValueAtPath(segments);
551
532
  const original = state.originals.get(key)?.value;
552
- const pristine = state.isPristineAtPath(segments);
533
+ const pristine = state.isPristineAtPathByKey(key, segments);
553
534
  const cell = state.errorCells.get(key);
554
535
  const schemaForKey = cell !== void 0 && cell.schema.length > 0 ? cell.schema : void 0;
555
536
  const blankForKey = state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments) ? [makeBlankRequiredError(segments)] : void 0;
@@ -561,7 +542,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
561
542
  const validating = (state.fieldValidationCounts.get(key) ?? 0) > 0;
562
543
  const transforming = (state.fieldTransformCounts.get(key) ?? 0) > 0;
563
544
  const transformError = state.transformErrors.get(key) ?? null;
564
- const gated = state.pathHasAsyncValidation(segments) && !state.firstValidationDone.value;
545
+ const gated = state.pathHasAsyncValidationByKey(key, segments) && !state.firstValidationDone.value;
565
546
  const isOrphan = segments.length > 0 && !hasAtPath(state.form.value, segments) && isUnderStubAncestor(state, segments);
566
547
  const valid = !gated && errors.length === 0 && !validating && !isOrphan;
567
548
  const elementRecord = state.domBinding.value?.elements.get(key);
@@ -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);
@@ -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,23 +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
- if (!warnedDisplayStatePredicates.has(predicate)) {
833
- warnedDisplayStatePredicates.add(predicate);
834
- console.warn(
835
- "[attaform] custom getDisplayState threw \u2014 falling back to defaultDisplayState. Subsequent throws from the same predicate will not warn again.",
836
- err
837
- );
838
- }
839
- machine = state.displayEngine.resolve(key, ctx, defaultDisplayState);
840
- rollupApplies = true;
841
- }
842
- const submitValidating = rollupApplies && isRoot && state.submitting.value && state.activeValidations.value > 0;
843
- 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;
844
808
  const displayState = base.disabled === true ? "idle" : resolvedDisplayState;
845
809
  return {
846
810
  ...base,
@@ -853,26 +817,33 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
853
817
  firstOwnError
854
818
  };
855
819
  }
856
- function aggregateErrorsAt(state, prefix) {
820
+ function aggregateErrorsAt(state, prefix, prefixKey) {
821
+ const candidates = state.errorWindowAt(prefix, prefixKey);
822
+ if (candidates.length === 0) return [];
857
823
  const formValue = state.form.value;
824
+ const cells = state.errorCells;
858
825
  const buckets = /* @__PURE__ */ new Map();
859
- const collect = (errs) => {
860
- for (const [pathKey, list] of errs) {
861
- if (list.length === 0) continue;
862
- const segs = segmentsForPathKey(pathKey);
863
- if (segs === null) continue;
864
- if (!isPathPrefix(prefix, segs)) continue;
865
- if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
866
- const ordinal = state.ensurePathOrdinal(pathKey);
867
- const existing = buckets.get(ordinal);
868
- if (existing === void 0) buckets.set(ordinal, [...list]);
869
- else existing.push(...list);
870
- }
871
- };
872
- collect(cellEntriesFor(state.errorCells, "schema"));
873
- collect(state.derivedBlankErrors.value);
874
- 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
+ }
875
845
  if (buckets.size === 0) return [];
846
+ if (buckets.size === 1) return [...buckets.values()][0];
876
847
  return [...buckets.entries()].sort(([a], [b]) => a - b).flatMap(([, errs]) => errs);
877
848
  }
878
849
  const EMPTY_ELEMENTS = Object.freeze([]);
@@ -967,7 +938,7 @@ function buildTree(spec) {
967
938
  existsCache.delete(key);
968
939
  });
969
940
  function schemaHasPath(segs) {
970
- const cacheKey = JSON.stringify(segs);
941
+ const cacheKey = keyForSegments(segs).key;
971
942
  const cached = existsCache.get(cacheKey);
972
943
  if (cached !== void 0) return cached;
973
944
  const result = spec.schema.getSlimPrimitiveTypesAtPath(segs).size > 0;
@@ -982,7 +953,7 @@ function buildTree(spec) {
982
953
  }
983
954
  function containerAt(segments) {
984
955
  const isArrayLike = spec.isArrayAt(segments);
985
- const pathKey = JSON.stringify(segments);
956
+ const pathKey = keyForSegments(segments).key;
986
957
  const cacheKey = `${pathKey}+${isArrayLike ? "A" : "O"}`;
987
958
  const existing = containerCache.get(cacheKey);
988
959
  if (existing !== void 0) return existing;
@@ -1082,7 +1053,7 @@ function buildErrorsSurface(state, sweep) {
1082
1053
  const treeCache = /* @__PURE__ */ new Map();
1083
1054
  sweep.onEvict((key) => treeCache.delete(key));
1084
1055
  const materialize = (segments) => {
1085
- const cacheKey = JSON.stringify(segments);
1056
+ const cacheKey = keyForSegments(segments).key;
1086
1057
  let tree = treeCache.get(cacheKey);
1087
1058
  if (tree === void 0) {
1088
1059
  const frozen = [...segments];
@@ -1094,28 +1065,21 @@ function buildErrorsSurface(state, sweep) {
1094
1065
  };
1095
1066
  const resolveLeaf = (path) => {
1096
1067
  const isContainerSelfAccess = path.length > 1 && path[path.length - 1] === "";
1097
- const collectAtKey = (key2, active2, into) => {
1098
- const cell = state.errorCells.get(key2);
1099
- if (active2) {
1100
- const b = state.derivedBlankErrors.value.get(key2);
1101
- if (cell !== void 0) into.push(...cell.schema);
1102
- if (b !== void 0) into.push(...b);
1103
- }
1104
- if (cell !== void 0) into.push(...cell.user);
1105
- };
1106
1068
  const merged = [];
1107
1069
  if (isContainerSelfAccess) {
1108
1070
  const containerPath = path.slice(0, -1);
1109
- const containerKey = canonicalizePath(containerPath).key;
1110
- const literalKey = canonicalizePath(path).key;
1071
+ const container = canonicalizePath(containerPath);
1072
+ const literal = canonicalizePath(path);
1111
1073
  const active2 = hasAtPath(state.form.value, containerPath);
1112
- collectAtKey(containerKey, active2, merged);
1113
- if (literalKey !== containerKey) collectAtKey(literalKey, active2, merged);
1074
+ collectErrorsAt(state, container.key, container.segments, active2, merged);
1075
+ if (literal.key !== container.key) {
1076
+ collectErrorsAt(state, literal.key, literal.segments, active2, merged);
1077
+ }
1114
1078
  return merged;
1115
1079
  }
1116
- const { key } = canonicalizePath(path);
1080
+ const { key, segments } = canonicalizePath(path);
1117
1081
  const active = hasAtPath(state.form.value, path);
1118
- collectAtKey(key, active, merged);
1082
+ collectErrorsAt(state, key, segments, active, merged);
1119
1083
  return merged;
1120
1084
  };
1121
1085
  return buildTree({
@@ -1133,65 +1097,80 @@ function buildErrorsSurface(state, sweep) {
1133
1097
  // `errors()` / `errors([])` / `errors(path)` are all the subtree
1134
1098
  // aggregate — the same helper `meta.errors` reads, so the surfaces
1135
1099
  // never drift.
1136
- call: (path) => aggregateErrorsAt(state, path),
1100
+ call: (path) => aggregateErrorsAt(state, path, keyForSegments(path).key),
1137
1101
  surface: "form.errors",
1138
1102
  sweep
1139
1103
  });
1140
1104
  }
1105
+ function collectErrorsAt(state, key, segments, active, into) {
1106
+ const cell = state.errorCells.get(key);
1107
+ if (active) {
1108
+ if (cell !== void 0) into.push(...cell.schema);
1109
+ if (state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments)) {
1110
+ into.push(makeBlankRequiredError(segments));
1111
+ }
1112
+ }
1113
+ if (cell !== void 0) into.push(...cell.user);
1114
+ }
1141
1115
  function errorAwareContainerKeys(state, segments) {
1142
1116
  const keys = new Set(liveKeysAtPath(state, segments));
1143
1117
  const formValue = state.form.value;
1144
- const walk = (store, applyActivePathFilter) => {
1145
- for (const [pathKey, errors] of store) {
1146
- if (errors.length === 0) continue;
1147
- const decoded = segmentsForPathKey(pathKey);
1148
- if (decoded === null) continue;
1149
- if (decoded.length <= segments.length) continue;
1150
- if (!isPathPrefix(segments, decoded)) continue;
1151
- if (applyActivePathFilter && !hasAtPath(formValue, decoded)) continue;
1152
- const nextSeg = decoded[segments.length];
1153
- keys.add(typeof nextSeg === "number" ? String(nextSeg) : nextSeg);
1154
- }
1155
- };
1156
- walk(cellEntriesFor(state.errorCells, "schema"), true);
1157
- walk(state.derivedBlankErrors.value, true);
1158
- walk(cellEntriesFor(state.errorCells, "user"), false);
1118
+ const errors = [];
1119
+ const found = [];
1120
+ for (const { key, segments: decoded } of state.errorWindowAt(
1121
+ segments,
1122
+ keyForSegments(segments).key
1123
+ )) {
1124
+ if (decoded.length <= segments.length) continue;
1125
+ if (!isPathPrefix(segments, decoded)) continue;
1126
+ errors.length = 0;
1127
+ collectErrorsAt(state, key, decoded, hasAtPath(formValue, decoded), errors);
1128
+ if (errors.length === 0) continue;
1129
+ const nextSeg = decoded[segments.length];
1130
+ found.push({
1131
+ childKey: typeof nextSeg === "number" ? String(nextSeg) : nextSeg,
1132
+ ordinal: state.ensurePathOrdinal(key)
1133
+ });
1134
+ }
1135
+ found.sort((a, b) => a.ordinal - b.ordinal);
1136
+ for (const { childKey } of found) keys.add(childKey);
1159
1137
  return [...keys];
1160
1138
  }
1161
1139
  function materializeErrors(state, containerSegments) {
1162
- const liveContainer = getAtPath(state.form.value, containerSegments);
1140
+ const formValue = state.form.value;
1141
+ const liveContainer = getAtPath(formValue, containerSegments);
1163
1142
  const tree = Array.isArray(liveContainer) ? [] : {};
1164
- const collect = (store, applyActivePathFilter) => {
1165
- entries: for (const [pathKey, errors] of store) {
1166
- if (errors.length === 0) continue;
1167
- const fullPath = segmentsForPathKey(pathKey);
1168
- if (fullPath === null) continue;
1169
- if (fullPath.length === 0) {
1170
- if (containerSegments.length === 0) placeAt(tree, [ROOT_PATH_KEY], errors);
1171
- continue;
1172
- }
1173
- if (fullPath.length < containerSegments.length) continue;
1174
- for (let i = 0; i < containerSegments.length; i++) {
1175
- if (fullPath[i] !== containerSegments[i]) continue entries;
1176
- }
1177
- if (applyActivePathFilter && !hasAtPath(state.form.value, fullPath)) continue;
1178
- const relativePath = fullPath.slice(containerSegments.length);
1179
- let placePath;
1180
- if (relativePath.length === 0) {
1181
- placePath = [""];
1182
- } else if (state.schema.isLeafAtPath(fullPath)) {
1183
- placePath = relativePath;
1184
- } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1185
- placePath = [...relativePath, ""];
1186
- } else {
1187
- placePath = relativePath;
1188
- }
1189
- placeAt(tree, placePath, errors);
1143
+ const placements = [];
1144
+ for (const { key, segments: fullPath } of state.errorWindowAt(
1145
+ containerSegments,
1146
+ keyForSegments(containerSegments).key
1147
+ )) {
1148
+ const isRoot = fullPath.length === 0;
1149
+ if (isRoot) {
1150
+ if (containerSegments.length !== 0) continue;
1151
+ } else if (!isPathPrefix(containerSegments, fullPath)) {
1152
+ continue;
1190
1153
  }
1191
- };
1192
- collect(cellEntriesFor(state.errorCells, "schema"), true);
1193
- collect(state.derivedBlankErrors.value, true);
1194
- collect(cellEntriesFor(state.errorCells, "user"), false);
1154
+ const errors = [];
1155
+ collectErrorsAt(state, key, fullPath, isRoot || hasAtPath(formValue, fullPath), errors);
1156
+ if (errors.length === 0) continue;
1157
+ const relativePath = fullPath.slice(containerSegments.length);
1158
+ let placePath;
1159
+ if (isRoot) {
1160
+ placePath = [ROOT_PATH_KEY];
1161
+ } else if (relativePath.length === 0) {
1162
+ placePath = [""];
1163
+ } else if (state.schema.isLeafAtPath(fullPath)) {
1164
+ placePath = relativePath;
1165
+ } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1166
+ placePath = [...relativePath, ""];
1167
+ } else {
1168
+ placePath = relativePath;
1169
+ }
1170
+ placements.push({ placePath, errors, ordinal: state.ensurePathOrdinal(key) });
1171
+ }
1172
+ placements.sort((a, b) => a.ordinal - b.ordinal);
1173
+ for (const { placePath, errors } of placements) placeAt(tree, placePath, errors);
1195
1174
  return tree;
1196
1175
  }
1197
1176
  function placeAt(tree, path, errors) {
@@ -1264,7 +1243,7 @@ function buildFieldsSurface(state, getFieldStateAt, sweep) {
1264
1243
  const viewCache = /* @__PURE__ */ new Map();
1265
1244
  sweep.onEvict((key) => viewCache.delete(key));
1266
1245
  function viewAt(segments) {
1267
- const cacheKey = JSON.stringify(segments);
1246
+ const cacheKey = keyForSegments(segments).key;
1268
1247
  const existing = viewCache.get(cacheKey);
1269
1248
  if (existing !== void 0) return existing;
1270
1249
  const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(
@@ -1832,45 +1811,6 @@ function buildFieldArrayApi(state) {
1832
1811
  };
1833
1812
  }
1834
1813
 
1835
- const SWEEP_SLICE = 8;
1836
- function createDynamicPathSweep(state) {
1837
- const dynamicKeys = /* @__PURE__ */ new Set();
1838
- const evictors = [];
1839
- const isDynamicPath = (segments) => {
1840
- for (let i = 0; i < segments.length; i++) {
1841
- if (typeof segments[i] === "number") return true;
1842
- if (!state.schema.isFixedObjectAtPath(segments.slice(0, i))) return true;
1843
- }
1844
- return false;
1845
- };
1846
- let sweepCursor = null;
1847
- state.onFormChange((next) => {
1848
- if (dynamicKeys.size === 0) return;
1849
- const raw = toRaw(next);
1850
- for (let i = 0; i < SWEEP_SLICE; i++) {
1851
- sweepCursor ?? (sweepCursor = dynamicKeys.values());
1852
- const step = sweepCursor.next();
1853
- if (step.done === true) {
1854
- sweepCursor = null;
1855
- break;
1856
- }
1857
- const segments = segmentsForPathKey(step.value);
1858
- if (segments !== null && hasAtPath(raw, segments)) continue;
1859
- dynamicKeys.delete(step.value);
1860
- for (const evict of evictors) evict(step.value);
1861
- }
1862
- });
1863
- return {
1864
- track(segments, key) {
1865
- if (dynamicKeys.has(key)) return;
1866
- if (isDynamicPath(segments)) dynamicKeys.add(key);
1867
- },
1868
- onEvict(evict) {
1869
- evictors.push(evict);
1870
- }
1871
- };
1872
- }
1873
-
1874
1814
  const warnedNoScopeStores = /* @__PURE__ */ new WeakSet() ;
1875
1815
  function isErrorInputLike(value) {
1876
1816
  if (value instanceof Error) return true;
@@ -1898,8 +1838,8 @@ function deriveSubmitErrors(err) {
1898
1838
  messageless: true
1899
1839
  };
1900
1840
  }
1901
- function buildProcessForm(state, formInstanceId, options = {}) {
1902
- const invalidPolicy = options.onInvalidSubmit ?? "focus-first-error";
1841
+ function buildProcessForm(state, options = {}) {
1842
+ const applyInvalidSubmit = options.applyInvalidSubmit;
1903
1843
  function validate(pathInput) {
1904
1844
  const result = ref({
1905
1845
  pending: true,
@@ -1997,7 +1937,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1997
1937
  };
1998
1938
  }
1999
1939
  async function runRefinementValidation(data, path) {
2000
- return await state.schema.validateAtPath(data, path);
1940
+ return stampFormKey(await state.schema.validateAtPath(data, path), state.formKey);
2001
1941
  }
2002
1942
  function composeWithDerivedBlank(refinement, scope) {
2003
1943
  const blankErrors = collectScopedBlankErrors(state, scope);
@@ -2048,7 +1988,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
2048
1988
  }
2049
1989
  }
2050
1990
  if (generationStillValid) {
2051
- applyInvalidSubmitPolicy(state, formInstanceId, invalidPolicy);
1991
+ applyInvalidSubmit?.();
2052
1992
  }
2053
1993
  if (onError !== void 0) {
2054
1994
  try {
@@ -2065,7 +2005,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
2065
2005
  await onSubmit(merged.data);
2066
2006
  if (hasUserErrorEntries(state)) {
2067
2007
  if (state.submissionGeneration.value === genAtEntry) {
2068
- applyInvalidSubmitPolicy(state, formInstanceId, invalidPolicy);
2008
+ applyInvalidSubmit?.();
2069
2009
  }
2070
2010
  return;
2071
2011
  }
@@ -2083,7 +2023,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
2083
2023
  if (segments === null) continue;
2084
2024
  state.setUserErrorsForPath([...segments], bucket);
2085
2025
  }
2086
- applyInvalidSubmitPolicy(state, formInstanceId, invalidPolicy);
2026
+ applyInvalidSubmit?.();
2087
2027
  if (messageless) {
2088
2028
  console.warn(
2089
2029
  "[attaform] handleSubmit callback threw a non-Error value; throw an Error or a ValidationError ({ message, path? }) so the failure surfaces with a usable message."
@@ -2103,6 +2043,10 @@ function buildProcessForm(state, formInstanceId, options = {}) {
2103
2043
  };
2104
2044
  return { validate, parse, handleSubmit };
2105
2045
  }
2046
+ function stampFormKey(verdict, formKey) {
2047
+ if (verdict.success) return { ...verdict, formKey };
2048
+ return verdict.data === void 0 ? { data: void 0, errors: verdict.errors, success: false, formKey } : { data: verdict.data, errors: verdict.errors, success: false, formKey };
2049
+ }
2106
2050
  function toSegments(pathInput) {
2107
2051
  return canonicalizePath(pathInput).segments;
2108
2052
  }
@@ -2136,24 +2080,9 @@ function collectScopedBlankErrors(state, scope) {
2136
2080
  }
2137
2081
  return errors;
2138
2082
  }
2139
- function applyInvalidSubmitPolicy(state, formInstanceId, policy) {
2140
- if (policy === "none") return;
2141
- const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
2142
- if (target === null) return;
2143
- if (policy === "scroll-to-first-error") {
2144
- target.element.scrollIntoView();
2145
- return;
2146
- }
2147
- if (policy === "focus-first-error") {
2148
- target.element.focus({ focusVisible: true });
2149
- return;
2150
- }
2151
- target.element.scrollIntoView();
2152
- target.element.focus({ preventScroll: true, focusVisible: true });
2153
- }
2154
2083
 
2155
2084
  const warnedRejections = /* @__PURE__ */ new WeakMap() ;
2156
- function shouldWarnOnce$1(store, key) {
2085
+ function shouldWarnOnce(store, key) {
2157
2086
  if (warnedRejections === null) return false;
2158
2087
  let set = warnedRejections.get(store);
2159
2088
  if (set === void 0) {
@@ -2229,7 +2158,7 @@ function walk(schema, store, path, value) {
2229
2158
  function reportRejection(store, path, kind, accepted) {
2230
2159
  const dotted = path.map((s) => String(s)).join(".") || "(root)";
2231
2160
  const key = `${dotted}::${kind}`;
2232
- if (!shouldWarnOnce$1(store, key)) return;
2161
+ if (!shouldWarnOnce(store, key)) return;
2233
2162
  if (accepted.size === 0) {
2234
2163
  console.warn(
2235
2164
  `[attaform] Cannot write to '${dotted}' \u2014 this path is not in your schema.
@@ -2262,56 +2191,9 @@ function formatExpectedKinds(accepted) {
2262
2191
  return `one of: ${list.join(", ")}`;
2263
2192
  }
2264
2193
 
2265
- function defineCoercion(entry) {
2266
- return entry;
2267
- }
2268
2194
  const IDENTITY = (v) => v;
2269
- const EMPTY_INDEX = /* @__PURE__ */ new Map();
2270
- const defaultCoercionRules = [
2271
- defineCoercion({
2272
- input: "string",
2273
- output: "number",
2274
- transform: (s) => {
2275
- const trimmed = s.trim();
2276
- if (trimmed === "") return { coerced: false };
2277
- const n = Number(trimmed);
2278
- if (!Number.isFinite(n)) return { coerced: false };
2279
- return { coerced: true, value: n };
2280
- }
2281
- }),
2282
- defineCoercion({
2283
- input: "string",
2284
- output: "boolean",
2285
- transform: (s) => {
2286
- const normalized = s.trim().toLowerCase();
2287
- if (normalized === "true") return { coerced: true, value: true };
2288
- if (normalized === "false") return { coerced: true, value: false };
2289
- return { coerced: false };
2290
- }
2291
- })
2292
- ];
2293
- function resolveCoercionIndex(config) {
2294
- if (config === false) return EMPTY_INDEX;
2295
- const rules = config === void 0 || config === true ? defaultCoercionRules : config;
2296
- return indexRules(rules);
2297
- }
2298
- function indexRules(rules) {
2299
- const idx = /* @__PURE__ */ new Map();
2300
- for (const entry of rules) {
2301
- const candidate = entry;
2302
- if (candidate === null || typeof candidate !== "object" || typeof candidate.transform !== "function") {
2303
- {
2304
- console.warn("[attaform] coercion entry missing or invalid `transform` \u2014 skipped.");
2305
- }
2306
- continue;
2307
- }
2308
- const key = `${entry.input}->${entry.output}`;
2309
- if (idx.has(key) && true) {
2310
- console.warn(`[attaform] duplicate coercion rule for '${key}' \u2014 last entry wins.`);
2311
- }
2312
- idx.set(key, entry);
2313
- }
2314
- return idx;
2195
+ function resolveCoerceEnabled(config) {
2196
+ return config !== false;
2315
2197
  }
2316
2198
  function memberSlimTypes(schema, segments, accepted) {
2317
2199
  if (accepted.has("array")) return schema.getSlimPrimitiveTypesAtPath([...segments, 0]);
@@ -2320,119 +2202,84 @@ function memberSlimTypes(schema, segments, accepted) {
2320
2202
  }
2321
2203
  return void 0;
2322
2204
  }
2323
- function buildCoerceFn(schema, segments, index) {
2324
- if (index === EMPTY_INDEX) return IDENTITY;
2325
- if (index.size === 0) return IDENTITY;
2205
+ function buildCoerceFn(schema, segments, enabled) {
2206
+ if (!enabled) return IDENTITY;
2326
2207
  const accepted = schema.getSlimPrimitiveTypesAtPath(segments);
2327
2208
  const elementAccepted = memberSlimTypes(schema, segments, accepted);
2328
- return (value) => coerceValue(value, accepted, elementAccepted, index);
2209
+ return (value) => coerceValue(value, accepted, elementAccepted);
2329
2210
  }
2330
- function buildElementCoerceFn(schema, segments, index) {
2331
- if (index === EMPTY_INDEX) return void 0;
2332
- if (index.size === 0) return void 0;
2211
+ function buildElementCoerceFn(schema, segments, enabled) {
2212
+ if (!enabled) return void 0;
2333
2213
  const accepted = schema.getSlimPrimitiveTypesAtPath(segments);
2334
2214
  const elementAccepted = memberSlimTypes(schema, segments, accepted);
2335
2215
  if (elementAccepted === void 0) return void 0;
2336
- return (value) => coerceScalar(value, elementAccepted, index);
2216
+ return (value) => coerceScalar(value, elementAccepted);
2337
2217
  }
2338
2218
  function pickScalarTarget(accepted) {
2339
2219
  if (accepted.has("string")) return null;
2340
2220
  if (accepted.has("number")) return "number";
2341
2221
  if (accepted.has("boolean")) return "boolean";
2342
- if (accepted.has("bigint")) return "bigint";
2343
2222
  return null;
2344
2223
  }
2345
- const warnedCoerce = /* @__PURE__ */ new WeakMap() ;
2346
- const sharedWarnStore = {};
2347
- function shouldWarnOnce(key) {
2348
- if (warnedCoerce === null) return false;
2349
- let set = warnedCoerce.get(sharedWarnStore);
2350
- if (set === void 0) {
2351
- set = /* @__PURE__ */ new Set();
2352
- warnedCoerce.set(sharedWarnStore, set);
2353
- }
2354
- if (set.has(key)) return false;
2355
- set.add(key);
2356
- return true;
2224
+ function toNumber(source) {
2225
+ const trimmed = source.trim();
2226
+ if (trimmed === "") return source;
2227
+ const n = Number(trimmed);
2228
+ return Number.isFinite(n) ? n : source;
2229
+ }
2230
+ function toBoolean(source) {
2231
+ const normalized = source.trim().toLowerCase();
2232
+ if (normalized === "true") return true;
2233
+ if (normalized === "false") return false;
2234
+ return source;
2357
2235
  }
2358
- function coerceScalar(value, accepted, index) {
2236
+ function coerceScalar(value, accepted) {
2359
2237
  if (accepted.size === 0) return value;
2360
2238
  const sourceKind = slimKindOf(value);
2361
2239
  if (accepted.has(sourceKind)) return value;
2240
+ if (sourceKind !== "string") return value;
2362
2241
  const target = pickScalarTarget(accepted);
2363
- if (target === null) return value;
2364
- const entry = index.get(`${sourceKind}->${target}`);
2365
- if (entry === void 0) return value;
2366
- let result;
2367
- try {
2368
- result = entry.transform(value);
2369
- } catch (err) {
2370
- if (shouldWarnOnce(`${entry.input}->${entry.output}::throw`)) {
2371
- console.warn(
2372
- `[attaform] coercion '${entry.input}->${entry.output}' threw \u2014 write passes through.`,
2373
- err
2374
- );
2375
- }
2376
- return value;
2377
- }
2378
- if (!result.coerced) return value;
2379
- const returnedKind = slimKindOf(result.value);
2380
- if (returnedKind !== entry.output) {
2381
- if (shouldWarnOnce(`${entry.input}->${entry.output}::wrong-kind:${returnedKind}`)) {
2382
- console.warn(
2383
- `[attaform] coercion '${entry.input}->${entry.output}' produced a ${returnedKind} \u2014 write passes through.`
2384
- );
2385
- }
2386
- return value;
2387
- }
2388
- if (entry.output === "number" && !Number.isFinite(result.value)) {
2389
- if (shouldWarnOnce(`${entry.input}->${entry.output}::nan`)) {
2390
- console.warn(
2391
- `[attaform] coercion '${entry.input}->${entry.output}' produced a non-finite number \u2014 write passes through.`
2392
- );
2393
- }
2394
- return value;
2395
- }
2396
- return result.value;
2242
+ if (target === "number") return toNumber(value);
2243
+ if (target === "boolean") return toBoolean(value);
2244
+ return value;
2397
2245
  }
2398
- function coerceArrayMembers(arr, elementAccepted, index) {
2246
+ function coerceArrayMembers(arr, elementAccepted) {
2399
2247
  let changed = false;
2400
2248
  const out = [];
2401
2249
  for (const el of arr) {
2402
- const next = coerceScalar(el, elementAccepted, index);
2250
+ const next = coerceScalar(el, elementAccepted);
2403
2251
  if (next !== el) changed = true;
2404
2252
  out.push(next);
2405
2253
  }
2406
2254
  return changed ? out : arr;
2407
2255
  }
2408
- function coerceSetMembers(set, elementAccepted, index) {
2256
+ function coerceSetMembers(set, elementAccepted) {
2409
2257
  let changed = false;
2410
2258
  const out = [];
2411
2259
  for (const el of set) {
2412
- const next = coerceScalar(el, elementAccepted, index);
2260
+ const next = coerceScalar(el, elementAccepted);
2413
2261
  if (next !== el) changed = true;
2414
2262
  out.push(next);
2415
2263
  }
2416
2264
  return changed ? new Set(out) : set;
2417
2265
  }
2418
- function coerceValue(value, accepted, elementAccepted, index) {
2266
+ function coerceValue(value, accepted, elementAccepted) {
2419
2267
  if (Array.isArray(value)) {
2420
2268
  if (!accepted.has("array") || elementAccepted === void 0) return value;
2421
- return coerceArrayMembers(value, elementAccepted, index);
2269
+ return coerceArrayMembers(value, elementAccepted);
2422
2270
  }
2423
2271
  if (value instanceof Set) {
2424
2272
  if (!accepted.has("set") || elementAccepted === void 0) return value;
2425
- return coerceSetMembers(value, elementAccepted, index);
2273
+ return coerceSetMembers(value, elementAccepted);
2426
2274
  }
2427
- return coerceScalar(value, accepted, index);
2275
+ return coerceScalar(value, accepted);
2428
2276
  }
2429
2277
 
2430
2278
  const warnedMultiRootHosts = /* @__PURE__ */ new Set();
2431
2279
  const EMPTY_TRANSFORMS = Object.freeze([]);
2432
2280
  function buildRegister(state, formInstanceId, instanceConfig) {
2433
- const coerceIndex = instanceConfig?.coerce !== void 0 ? resolveCoercionIndex(instanceConfig.coerce) : state.coerceIndex;
2281
+ const coerceEnabled = instanceConfig?.coerce !== void 0 ? resolveCoerceEnabled(instanceConfig.coerce) : state.coerceEnabled;
2434
2282
  const instanceMeta = instanceConfig?.instanceMeta;
2435
- const formAutoAria = instanceConfig?.autoAria ?? true;
2436
2283
  const getDisplayStateAt = instanceConfig?.getDisplayStateAt;
2437
2284
  const withInstanceMeta = (meta) => {
2438
2285
  if (instanceMeta === void 0) return meta;
@@ -2472,16 +2319,15 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2472
2319
  const coerce = buildCoerceFn(
2473
2320
  state.schema,
2474
2321
  segments,
2475
- coerceIndex
2322
+ coerceEnabled
2476
2323
  );
2477
2324
  const coerceElement = buildElementCoerceFn(
2478
2325
  state.schema,
2479
2326
  segments,
2480
- coerceIndex
2327
+ coerceEnabled
2481
2328
  );
2482
2329
  const { aria } = computeFieldIdentity(formInstanceId, state.formKey, pathKey);
2483
2330
  const isRequired = state.schema.isRequiredAtPath(segments);
2484
- const ariaEnabled = options?.autoAria ?? formAutoAria;
2485
2331
  const ariaDisplayState = getDisplayStateAt !== void 0 ? computed(() => getDisplayStateAt(segments)) : void 0;
2486
2332
  const markBlank = () => state.setValueAtPath(segments, slimDefault, withInstanceMeta({ blank: true }));
2487
2333
  const internalRv = {
@@ -2601,7 +2447,6 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2601
2447
  // --- Aria (internal; consumed by the directive) ---
2602
2448
  aria,
2603
2449
  isRequired,
2604
- ariaEnabled,
2605
2450
  ...ariaDisplayState !== void 0 ? { ariaDisplayState } : {}
2606
2451
  };
2607
2452
  return shallowReadonly(internalRv);
@@ -2765,11 +2610,50 @@ const DERIVED_DISPLAY_KEYS = /* @__PURE__ */ new Set([
2765
2610
  "firstError",
2766
2611
  "firstOwnError"
2767
2612
  ]);
2768
- const META_SPECIAL_KEYS = /* @__PURE__ */ new Set(["errors", "validating", "valid", "transforming", "busy"]);
2769
- const BASE_MIRROR_KEYS = [...FIELD_STATE_KEYS].filter((k) => !DERIVED_DISPLAY_KEYS.has(k));
2770
- const META_MIRROR_KEYS = [...FIELD_STATE_KEYS].filter((k) => !META_SPECIAL_KEYS.has(k));
2771
- function defineGetter(target, key, get) {
2772
- Object.defineProperty(target, key, { get, enumerable: true, configurable: true });
2613
+ const META_KEYS = [
2614
+ ...FIELD_STATE_KEYS,
2615
+ "submitting",
2616
+ "submissionAttempts",
2617
+ "departAttempts",
2618
+ "submitError",
2619
+ "submitted",
2620
+ "instanceId",
2621
+ "errorCount"
2622
+ ];
2623
+ const BASE_META_KEYS = META_KEYS.filter((k) => !DERIVED_DISPLAY_KEYS.has(k));
2624
+ let INERT_HISTORY;
2625
+ function inertHistory() {
2626
+ INERT_HISTORY ?? (INERT_HISTORY = readonly({
2627
+ undo: () => false,
2628
+ redo: () => false,
2629
+ clear: () => {
2630
+ },
2631
+ canUndo: false,
2632
+ canRedo: false,
2633
+ size: 0
2634
+ }));
2635
+ return INERT_HISTORY;
2636
+ }
2637
+ const denyWrite = () => false;
2638
+ function buildMetaProxy(keys, read) {
2639
+ const target = {};
2640
+ const owns = new Set(keys);
2641
+ return new Proxy(target, {
2642
+ get: (_, key) => owns.has(key) ? read(key) : Reflect.get(target, key),
2643
+ has: (_, key) => owns.has(key) || Reflect.has(target, key),
2644
+ // Safe to hand back the same array every time: the spec copies a
2645
+ // trap's key list before using it.
2646
+ ownKeys: () => keys,
2647
+ getOwnPropertyDescriptor(_, key) {
2648
+ if (!owns.has(key)) return Reflect.getOwnPropertyDescriptor(target, key);
2649
+ return { configurable: true, enumerable: true, value: read(key), writable: false };
2650
+ },
2651
+ // A getter with no setter throws on assignment under strict mode,
2652
+ // which every module here is. `false` keeps that.
2653
+ set: denyWrite,
2654
+ deleteProperty: denyWrite,
2655
+ defineProperty: denyWrite
2656
+ });
2773
2657
  }
2774
2658
  function buildFormApi(state, formInstanceId, options = {}) {
2775
2659
  const instanceBag = pickDefined({
@@ -2796,32 +2680,26 @@ function buildFormApi(state, formInstanceId, options = {}) {
2796
2680
  const rootBaseComputed = computed(
2797
2681
  () => buildContainerFieldStateBase(state, ROOT_PATH, ROOT_PATH_KEY, formInstanceId).base
2798
2682
  );
2799
- const metaBase = {
2800
- instanceId: formInstanceId,
2801
- get submitting() {
2802
- return state.submitting.value;
2803
- },
2804
- get submissionAttempts() {
2805
- return state.submissionAttempts.value;
2806
- },
2807
- get departAttempts() {
2808
- return state.departAttempts.value;
2809
- },
2810
- get submitError() {
2811
- return state.submitError.value;
2812
- },
2813
- get submitted() {
2814
- return state.submitted.value;
2683
+ const metaBase = buildMetaProxy(BASE_META_KEYS, (key) => {
2684
+ switch (key) {
2685
+ case "instanceId":
2686
+ return formInstanceId;
2687
+ case "errorCount":
2688
+ return rootBaseComputed.value.errors.length;
2689
+ case "submitting":
2690
+ return state.submitting.value;
2691
+ case "submissionAttempts":
2692
+ return state.submissionAttempts.value;
2693
+ case "departAttempts":
2694
+ return state.departAttempts.value;
2695
+ case "submitError":
2696
+ return state.submitError.value;
2697
+ case "submitted":
2698
+ return state.submitted.value;
2699
+ default:
2700
+ return rootBaseComputed.value[key];
2815
2701
  }
2816
- };
2817
- for (const k of BASE_MIRROR_KEYS) {
2818
- defineGetter(
2819
- metaBase,
2820
- k,
2821
- () => rootBaseComputed.value[k]
2822
- );
2823
- }
2824
- defineGetter(metaBase, "errorCount", () => rootBaseComputed.value.errors.length);
2702
+ });
2825
2703
  const getFormMetaBase = () => {
2826
2704
  void state.submitting.value;
2827
2705
  void state.submissionAttempts.value;
@@ -2830,28 +2708,41 @@ function buildFormApi(state, formInstanceId, options = {}) {
2830
2708
  void state.submitted.value;
2831
2709
  return metaBase;
2832
2710
  };
2833
- const fieldStateAccessorOptions = options.getDisplayState !== void 0 ? { getDisplayState: options.getDisplayState } : void 0;
2834
- const pathSweep = createDynamicPathSweep(state);
2711
+ const pathSweep = state.pathSweep;
2835
2712
  const getRootFieldStateAt = buildFieldStateAccessor(
2836
2713
  state,
2837
2714
  formInstanceId,
2838
2715
  getFormMetaBase,
2839
- pathSweep,
2840
- fieldStateAccessorOptions
2716
+ pathSweep
2841
2717
  );
2842
2718
  const getDisplayStateAt = (segments) => getRootFieldStateAt(segments).value.displayState;
2843
2719
  const registerConfig = {
2844
- ...pickDefined({ instanceMeta, coerce: options.coerce, autoAria: options.autoAria }),
2720
+ ...pickDefined({ instanceMeta, coerce: options.coerce }),
2845
2721
  getDisplayStateAt
2846
2722
  };
2847
2723
  const register = buildRegister(state, formInstanceId, registerConfig);
2848
- const processOptions = pickDefined({ onInvalidSubmit: options.onInvalidSubmit });
2849
- const defaultInvalidSubmitPolicy = options.onInvalidSubmit ?? "focus-first-error";
2724
+ const focusFirstError = (options2) => {
2725
+ const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
2726
+ if (target === null) return false;
2727
+ target.element.focus({ focusVisible: true, ...options2 });
2728
+ return true;
2729
+ };
2730
+ const scrollToFirstError = (options2) => {
2731
+ const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
2732
+ if (target === null) return false;
2733
+ target.element.scrollIntoView(options2);
2734
+ return true;
2735
+ };
2736
+ const applyInvalidSubmitPolicyPublic = () => {
2737
+ if (options.focusOnInvalidSubmit !== false) focusFirstError();
2738
+ };
2850
2739
  const {
2851
2740
  validate: validateBuilt,
2852
2741
  parse: parseBuilt,
2853
2742
  handleSubmit
2854
- } = buildProcessForm(state, formInstanceId, processOptions);
2743
+ } = buildProcessForm(state, {
2744
+ applyInvalidSubmit: applyInvalidSubmitPolicyPublic
2745
+ });
2855
2746
  const validate = (pathInput) => validateBuilt(pathInput);
2856
2747
  const parse = (arg1, arg2) => {
2857
2748
  const isPathArg = typeof arg1 === "string" || Array.isArray(arg1);
@@ -2979,22 +2870,21 @@ function buildFormApi(state, formInstanceId, options = {}) {
2979
2870
  () => state.firstValidationDone.value && state.errorCells.size === 0 && state.derivedBlankErrors.value.size === 0 && !validating.value
2980
2871
  );
2981
2872
  const history = options.history;
2982
- const formHistory = readonly(
2873
+ const formHistory = history === void 0 ? inertHistory() : readonly(
2983
2874
  reactive({
2984
- undo: history?.undo ?? (() => false),
2985
- redo: history?.redo ?? (() => false),
2986
- clear: history?.clear ?? (() => {
2987
- }),
2988
- canUndo: history?.canUndo ?? computed(() => false),
2989
- canRedo: history?.canRedo ?? computed(() => false),
2990
- size: history?.historySize ?? computed(() => 0)
2875
+ undo: history.undo,
2876
+ redo: history.redo,
2877
+ clear: history.clear,
2878
+ canUndo: history.canUndo,
2879
+ canRedo: history.canRedo,
2880
+ size: history.historySize
2991
2881
  })
2992
2882
  );
2993
2883
  const metaErrors = computed(
2994
- () => aggregateErrorsAt(state, [])
2884
+ () => aggregateErrorsAt(state, [], ROOT_PATH_KEY)
2995
2885
  );
2996
2886
  const rootFieldState = getRootFieldStateAt([]);
2997
- const metaTarget = {
2887
+ const metaOwn = {
2998
2888
  // Whole-form work signals compose the LIFECYCLE counters with the
2999
2889
  // per-leaf rollup: a submit-time validate shows up as
3000
2890
  // activeValidations, per-field debounced validators as
@@ -3023,14 +2913,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
3023
2913
  // identifier).
3024
2914
  instanceId: formInstanceId
3025
2915
  };
3026
- for (const k of META_MIRROR_KEYS) {
3027
- defineGetter(
3028
- metaTarget,
3029
- k,
3030
- () => rootFieldState.value[k]
3031
- );
3032
- }
3033
- defineGetter(metaTarget, "errorCount", () => metaErrors.value.length);
2916
+ const metaTarget = buildMetaProxy(META_KEYS, (key) => {
2917
+ if (key === "errorCount") return metaErrors.value.length;
2918
+ if (Object.hasOwn(metaOwn, key)) return metaOwn[key];
2919
+ return rootFieldState.value[key];
2920
+ });
3034
2921
  const formMeta = readonly(reactive(metaTarget));
3035
2922
  const reset = (nextDefaultValues) => {
3036
2923
  if (nextDefaultValues === void 0) {
@@ -3070,21 +2957,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
3070
2957
  } catch {
3071
2958
  }
3072
2959
  }
3073
- const focusFirstError = (options2) => {
3074
- const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
3075
- if (target === null) return false;
3076
- target.element.focus({ focusVisible: true, ...options2 });
3077
- return true;
3078
- };
3079
- const scrollToFirstError = (options2) => {
3080
- const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
3081
- if (target === null) return false;
3082
- target.element.scrollIntoView(options2);
3083
- return true;
3084
- };
3085
- const applyInvalidSubmitPolicyPublic = (policy) => {
3086
- applyInvalidSubmitPolicy(state, formInstanceId, policy ?? defaultInvalidSubmitPolicy);
3087
- };
3088
2960
  const fieldArrays = buildFieldArrayApi(state);
3089
2961
  const blankPathsView = computed(() => {
3090
2962
  const keys = /* @__PURE__ */ new Set();
@@ -3259,6 +3131,45 @@ function shortenSourceFrame(frame) {
3259
3131
  return `(${path}:${line})`;
3260
3132
  }
3261
3133
 
3134
+ const SWEEP_SLICE = 8;
3135
+ function createDynamicPathSweep(deps) {
3136
+ const dynamicKeys = /* @__PURE__ */ new Set();
3137
+ const evictors = [];
3138
+ const isDynamicPath = (segments) => {
3139
+ for (let i = 0; i < segments.length; i++) {
3140
+ if (typeof segments[i] === "number") return true;
3141
+ if (!deps.isFixedObjectAtPath(segments.slice(0, i))) return true;
3142
+ }
3143
+ return false;
3144
+ };
3145
+ let sweepCursor = null;
3146
+ deps.onFormChange((next) => {
3147
+ if (dynamicKeys.size === 0) return;
3148
+ const raw = toRaw(next);
3149
+ for (let i = 0; i < SWEEP_SLICE; i++) {
3150
+ sweepCursor ?? (sweepCursor = dynamicKeys.values());
3151
+ const step = sweepCursor.next();
3152
+ if (step.done === true) {
3153
+ sweepCursor = null;
3154
+ break;
3155
+ }
3156
+ const segments = segmentsForPathKey(step.value);
3157
+ if (segments !== null && hasAtPath(raw, segments)) continue;
3158
+ dynamicKeys.delete(step.value);
3159
+ for (const evict of evictors) evict(step.value);
3160
+ }
3161
+ });
3162
+ return {
3163
+ track(segments, key) {
3164
+ if (dynamicKeys.has(key)) return;
3165
+ if (isDynamicPath(segments)) dynamicKeys.add(key);
3166
+ },
3167
+ onEvict(evict) {
3168
+ evictors.push(evict);
3169
+ }
3170
+ };
3171
+ }
3172
+
3262
3173
  const IDLE = Object.freeze({ display: "idle" });
3263
3174
  const MAX_DELAY = 2147483647;
3264
3175
  const PENDING_LIVENESS_MS = 250;
@@ -3352,6 +3263,58 @@ function createDisplayEngine(ssr) {
3352
3263
  return engine;
3353
3264
  }
3354
3265
 
3266
+ function buildErrorPathIndex(cells, blank) {
3267
+ const keys = /* @__PURE__ */ new Set();
3268
+ for (const [key, cell] of cells) {
3269
+ if (cell.schema.length > 0 || cell.user.length > 0) keys.add(key);
3270
+ }
3271
+ for (const [key, list] of blank) {
3272
+ if (list.length > 0) keys.add(key);
3273
+ }
3274
+ if (keys.size === 0) return EMPTY_INDEX;
3275
+ const entries = [];
3276
+ for (const key of keys) {
3277
+ const segments = segmentsForPathKey(key);
3278
+ if (segments !== null) entries.push({ key, segments });
3279
+ }
3280
+ entries.sort(compareByKey);
3281
+ return entries;
3282
+ }
3283
+ const EMPTY_INDEX = Object.freeze([]);
3284
+ function compareByKey(a, b) {
3285
+ return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
3286
+ }
3287
+ function windowUnder(index, prefix) {
3288
+ if (prefix.length === 0 || index.length === 0) return index;
3289
+ const prefixKey = keyForSegments(prefix).key;
3290
+ const marker = `${prefixKey.slice(0, -1)},`;
3291
+ const start = lowerBound(index, marker);
3292
+ if (start >= index.length) return EMPTY_INDEX;
3293
+ let end = start;
3294
+ for (; end < index.length; end += 1) {
3295
+ const entry = index[end];
3296
+ if (entry === void 0 || entry.key > prefixKey) break;
3297
+ }
3298
+ return start === 0 && end === index.length ? index : index.slice(start, end);
3299
+ }
3300
+ function isSameWindow(a, b) {
3301
+ if (a.length !== b.length) return false;
3302
+ for (let i = 0; i < a.length; i += 1) {
3303
+ if (a[i]?.key !== b[i]?.key) return false;
3304
+ }
3305
+ return true;
3306
+ }
3307
+ function lowerBound(index, target) {
3308
+ let lo = 0;
3309
+ let hi = index.length;
3310
+ while (lo < hi) {
3311
+ const mid = lo + hi >>> 1;
3312
+ if ((index[mid]?.key ?? target) < target) lo = mid + 1;
3313
+ else hi = mid;
3314
+ }
3315
+ return lo;
3316
+ }
3317
+
3355
3318
  function mergeSparseHydration(schemaDefaults, sparse, schema) {
3356
3319
  return mergeDeep(schemaDefaults, sparse, [], schema);
3357
3320
  }
@@ -3578,16 +3541,15 @@ function walkAuthoredFromSchemaDiff(withDefaults, withoutDefaults, prefix, out)
3578
3541
  out.add(canonicalizePath(prefix).key);
3579
3542
  }
3580
3543
  }
3581
- function computeBaselineResponse(schema, strict, source) {
3544
+ function computeBaselineResponse(schema, source) {
3582
3545
  const completed = source === void 0 ? void 0 : mergeStructural(schema, [], source);
3583
3546
  return schema.getDefaultValues({
3584
3547
  useDefaultSchemaValues: true,
3585
- constraints: completed,
3586
- strict
3548
+ constraints: completed
3587
3549
  });
3588
3550
  }
3589
- function initialFirstValidationGate(schema, strict) {
3590
- return !strict || schema.needsAsyncValidation?.() !== true;
3551
+ function initialFirstValidationGate(schema) {
3552
+ return schema.needsAsyncValidation?.() !== true;
3591
3553
  }
3592
3554
  function seedOriginalsFromBaseline(st, baseline, ensureOrdinals) {
3593
3555
  st.originals.clear();
@@ -3599,7 +3561,7 @@ function seedOriginalsFromBaseline(st, baseline, ensureOrdinals) {
3599
3561
  });
3600
3562
  }
3601
3563
  function queueInitialAsyncValidation(st) {
3602
- if (!st.ssr && st.strict && st.schema.needsAsyncValidation?.() === true) {
3564
+ if (!st.ssr && st.schema.needsAsyncValidation?.() === true) {
3603
3565
  queueMicrotask(() => scheduleFieldValidation(
3604
3566
  st,
3605
3567
  [],
@@ -3641,10 +3603,6 @@ function ensurePathOrdinal(st, key) {
3641
3603
  }
3642
3604
  return ordinal;
3643
3605
  }
3644
- function pathHasAsyncValidation(st, path) {
3645
- const { key } = canonicalizePath(path);
3646
- return pathHasAsyncValidationByKey(st, key, path);
3647
- }
3648
3606
  function pathHasAsyncValidationByKey(st, key, segments) {
3649
3607
  const cached = st.pathAsyncCache.get(key);
3650
3608
  if (cached !== void 0) return cached;
@@ -3787,10 +3745,15 @@ function commitWritePatches(st, patches, meta) {
3787
3745
  const now = (/* @__PURE__ */ new Date()).toISOString();
3788
3746
  for (const patch of patches) {
3789
3747
  const { key } = canonicalizePath(patch.path);
3790
- if (patch.kind === "added" && !st.originals.has(key)) {
3748
+ if (!st.originals.has(key)) {
3791
3749
  st.originals.set(key, { segments: patch.path, value: void 0 });
3792
3750
  }
3793
3751
  touchFieldRecord(st, key, patch.path, { updatedAt: now });
3752
+ st.pathSweep.track(patch.path, key);
3753
+ for (let i = 1; i < patch.path.length; i++) {
3754
+ const ancestor = patch.path.slice(0, i);
3755
+ st.pathSweep.track(ancestor, canonicalizePath(ancestor).key);
3756
+ }
3794
3757
  }
3795
3758
  for (const listener of st.formChangeListeners) {
3796
3759
  try {
@@ -4001,10 +3964,7 @@ function setValueAtPath(st, path, value, meta) {
4001
3964
  if (newlyAuthored && st.schema.isPreprocessOrCoerceLeaf(path)) {
4002
3965
  const modeForAuthoringTransition = meta?.instance?.validateOn ?? st.fieldValidationMode;
4003
3966
  if (modeForAuthoringTransition === "change") {
4004
- scheduleFieldValidation(st, path, false, {
4005
- ...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
4006
- ...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
4007
- });
3967
+ scheduleFieldValidation(st, path, false, meta?.instance);
4008
3968
  }
4009
3969
  }
4010
3970
  return true;
@@ -4020,15 +3980,12 @@ function setValueAtPath(st, path, value, meta) {
4020
3980
  st.arrayIdentity.realign(path);
4021
3981
  } else if (isContainer(currentValue) && !isContainer(value)) {
4022
3982
  if (subtreeHadRealBaseline(st, path, currentValue)) {
4023
- st.removedSubtrees.add(canonicalizePath(path).key);
3983
+ st.removedSubtrees.add(pathKey);
4024
3984
  }
4025
3985
  }
4026
3986
  const effectiveModeAfterWrite = meta?.instance?.validateOn ?? st.fieldValidationMode;
4027
3987
  if (effectiveModeAfterWrite === "change") {
4028
- scheduleFieldValidation(st, path, false, {
4029
- ...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
4030
- ...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
4031
- });
3988
+ scheduleFieldValidation(st, path, false, meta?.instance);
4032
3989
  }
4033
3990
  return true;
4034
3991
  }
@@ -4089,12 +4046,11 @@ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variant
4089
4046
  path: [...parentPath, ...err.path]
4090
4047
  }));
4091
4048
  applySchemaErrorsForSubtree(st, parentPath, reStamped);
4092
- const { key: parentKey2 } = canonicalizePath(parentPath);
4093
- const prevValidation = st.fieldValidationState.get(parentKey2);
4049
+ const prevValidation = st.fieldValidationState.get(parentKey);
4094
4050
  if (prevValidation !== void 0) {
4095
4051
  if (prevValidation.timer !== null) clearTimeout(prevValidation.timer);
4096
4052
  prevValidation.aborted = true;
4097
- st.fieldValidationState.delete(parentKey2);
4053
+ st.fieldValidationState.delete(parentKey);
4098
4054
  }
4099
4055
  appliedSync = true;
4100
4056
  }
@@ -4102,17 +4058,14 @@ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variant
4102
4058
  applyFormReplacement(st, nextForm, meta);
4103
4059
  for (const k of newBlankPaths) st.blankPaths.add(k);
4104
4060
  if (reshapeMode === "change" && !appliedSync) {
4105
- scheduleFieldValidation(st, parentPath, false, {
4106
- ...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
4107
- ...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
4108
- });
4061
+ scheduleFieldValidation(st, parentPath, false, meta?.instance);
4109
4062
  }
4110
4063
  return true;
4111
4064
  }
4112
- function scheduleFieldValidation(st, path, immediate, override) {
4113
- const effectiveMode = override?.mode ?? st.fieldValidationMode;
4065
+ function scheduleFieldValidation(st, path, immediate, instance) {
4066
+ const effectiveMode = instance?.validateOn ?? st.fieldValidationMode;
4114
4067
  if (effectiveMode === "submit") return;
4115
- const effectiveDebounce = override?.debounceMs ?? st.fieldValidationDebounceMs;
4068
+ const effectiveDebounce = instance?.debounceMs ?? st.fieldValidationDebounceMs;
4116
4069
  const { key } = canonicalizePath(path);
4117
4070
  const prev = st.fieldValidationState.get(key);
4118
4071
  if (prev !== void 0) {
@@ -4386,10 +4339,7 @@ function markFocused(st, path, focused, meta) {
4386
4339
  });
4387
4340
  }
4388
4341
  if (changed) {
4389
- scheduleFieldValidation(st, path, true, {
4390
- ...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
4391
- ...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
4392
- });
4342
+ scheduleFieldValidation(st, path, true, meta?.instance);
4393
4343
  }
4394
4344
  }
4395
4345
  }
@@ -4402,11 +4352,10 @@ function markInteracted(st, path) {
4402
4352
  function touchAtPath(st, segments) {
4403
4353
  const formValue = st.form.value;
4404
4354
  let touchedAny = false;
4405
- for (const [, entry] of st.originals) {
4355
+ for (const [leafKey, entry] of st.originals) {
4406
4356
  if (!isPathPrefix(segments, entry.segments)) continue;
4407
4357
  if (!hasAtPath(formValue, entry.segments)) continue;
4408
4358
  touchedAny = true;
4409
- const leafKey = canonicalizePath(entry.segments).key;
4410
4359
  const current = st.fields.get(leafKey);
4411
4360
  if (current?.touched === true) continue;
4412
4361
  touchFieldRecord(st, leafKey, entry.segments, { touched: true });
@@ -4421,11 +4370,10 @@ function interactAtPath(st, segments) {
4421
4370
  if (st.effectiveDisabled.value) return false;
4422
4371
  const formValue = st.form.value;
4423
4372
  let interactedAny = false;
4424
- for (const [, entry] of st.originals) {
4373
+ for (const [leafKey, entry] of st.originals) {
4425
4374
  if (!isPathPrefix(segments, entry.segments)) continue;
4426
4375
  if (!hasAtPath(formValue, entry.segments)) continue;
4427
4376
  interactedAny = true;
4428
- const leafKey = canonicalizePath(entry.segments).key;
4429
4377
  const current = st.fields.get(leafKey);
4430
4378
  if (current?.touched === true && current.interacted === true && current.blurredAfterInteraction === true) {
4431
4379
  continue;
@@ -4482,11 +4430,7 @@ function adoptResolvedDefaults(st, value) {
4482
4430
  st.schema
4483
4431
  )
4484
4432
  );
4485
- seedOriginalsFromBaseline(
4486
- st,
4487
- computeBaselineResponse(st.schema, st.strict, st.defaultValues).data,
4488
- false
4489
- );
4433
+ seedOriginalsFromBaseline(st, computeBaselineResponse(st.schema, st.defaultValues).data, false);
4490
4434
  }
4491
4435
  function reset(st, nextDefaultValues) {
4492
4436
  const resetSource = nextDefaultValues === void 0 ? st.defaultValues : mergeSparseHydration(
@@ -4495,7 +4439,7 @@ function reset(st, nextDefaultValues) {
4495
4439
  st.schema
4496
4440
  );
4497
4441
  st.defaultValues = structuralSnapshot(resetSource);
4498
- const resetResponse = computeBaselineResponse(st.schema, st.strict, resetSource);
4442
+ const resetResponse = computeBaselineResponse(st.schema, resetSource);
4499
4443
  const next = resetResponse.data;
4500
4444
  rebuildAuthoredPaths(st, resetSource, next);
4501
4445
  applyFormReplacement(st, next);
@@ -4512,16 +4456,14 @@ function reset(st, nextDefaultValues) {
4512
4456
  st.blankPaths.add(key);
4513
4457
  }
4514
4458
  st.errorCells.clear();
4515
- if (st.strict && !resetResponse.success) {
4459
+ if (!resetResponse.success) {
4516
4460
  replaceErrorChannel(st, "schema", resetResponse.errors);
4517
4461
  }
4518
- if (st.strict) {
4519
- const syncResult = st.schema.validateAtPath(st.form.value, void 0, { sync: true });
4520
- if (!(syncResult instanceof Promise) && !syncResult.success) {
4521
- applySchemaErrorsForSubtree(st, [], syncResult.errors);
4522
- }
4462
+ const syncResult = st.schema.validateAtPath(st.form.value, void 0, { sync: true });
4463
+ if (!(syncResult instanceof Promise) && !syncResult.success) {
4464
+ applySchemaErrorsForSubtree(st, [], syncResult.errors);
4523
4465
  }
4524
- st.firstValidationDone.value = initialFirstValidationGate(st.schema, st.strict);
4466
+ st.firstValidationDone.value = initialFirstValidationGate(st.schema);
4525
4467
  queueInitialAsyncValidation(st);
4526
4468
  const now = (/* @__PURE__ */ new Date()).toISOString();
4527
4469
  for (const [pathKey, record] of st.fields) {
@@ -4611,10 +4553,6 @@ function clearFieldRecordFlags(st, pathKey) {
4611
4553
  if (record === void 0) return;
4612
4554
  st.fields.set(pathKey, withClearedHistoryFlags(record, (/* @__PURE__ */ new Date()).toISOString()));
4613
4555
  }
4614
- function isPristineAtPath(st, path) {
4615
- const { key, segments } = canonicalizePath(path);
4616
- return isPristineAtPathByKey(st, key, segments);
4617
- }
4618
4556
  function isPristineAtPathByKey(st, key, segments) {
4619
4557
  if (st.blankPaths.has(key) !== st.originalBlankPaths.has(key)) return false;
4620
4558
  const entry = st.originals.get(key);
@@ -4648,12 +4586,8 @@ function getFieldRecord(st, path) {
4648
4586
  const { key } = canonicalizePath(path);
4649
4587
  return st.fields.get(key);
4650
4588
  }
4651
- function getOriginalAtPath(st, path) {
4652
- const { key } = canonicalizePath(path);
4653
- return st.originals.get(key)?.value;
4654
- }
4655
4589
  function createFormStore(options) {
4656
- const { formKey, schema, defaultValues, strict = true, hydration } = options;
4590
+ const { formKey, schema, defaultValues, hydration } = options;
4657
4591
  const ssr = options.ssr === true;
4658
4592
  const ssrPrefetch = options.ssrPrefetch;
4659
4593
  const rememberVariants = options.rememberVariants !== false;
@@ -4661,22 +4595,16 @@ function createFormStore(options) {
4661
4595
  const fieldValidationDebounceMs = normalizeNumericOption({
4662
4596
  value: options.debounceMs ?? DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS,
4663
4597
  source: "useForm.debounceMs",
4664
- allowInfinity: false,
4665
4598
  min: 0,
4666
4599
  defaultValue: DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS
4667
4600
  });
4668
- const coerceIndex = resolveCoercionIndex(options.coerce);
4669
- const resolvedGetDisplayState = resolveGetDisplayState(options.getDisplayState);
4601
+ const coerceEnabled = resolveCoerceEnabled(options.coerce);
4670
4602
  const cleanupHooks = [];
4671
4603
  const modules = /* @__PURE__ */ new Map();
4672
4604
  const fieldValidatingSince = reactive(/* @__PURE__ */ new Map());
4673
4605
  const displayEngine = createDisplayEngine(ssr);
4674
4606
  cleanupHooks.push(() => displayEngine.dispose());
4675
- const schemaResponse = computeBaselineResponse(
4676
- schema,
4677
- strict,
4678
- defaultValues
4679
- );
4607
+ const schemaResponse = computeBaselineResponse(schema, defaultValues);
4680
4608
  const schemaInitialData = schemaResponse.data;
4681
4609
  const authoredPaths = /* @__PURE__ */ new Set();
4682
4610
  const initialData = hydration !== void 0 ? hydration.form : structuralSnapshot(schemaInitialData);
@@ -4689,16 +4617,15 @@ function createFormStore(options) {
4689
4617
  const v = getAtPath(toRaw(form.value), arraySegs);
4690
4618
  return Array.isArray(v) ? v.length : 0;
4691
4619
  });
4692
- const fields = reactive(/* @__PURE__ */ new Map());
4620
+ const fields = shallowReactive(/* @__PURE__ */ new Map());
4693
4621
  const domBinding = shallowRef(null);
4694
- const errorCells = reactive(/* @__PURE__ */ new Map());
4695
- const originals = reactive(/* @__PURE__ */ new Map());
4622
+ const errorCells = shallowReactive(/* @__PURE__ */ new Map());
4623
+ const originals = shallowReactive(/* @__PURE__ */ new Map());
4696
4624
  const removedSubtrees = /* @__PURE__ */ new Set();
4697
- const initialTransientList = hydration?.blankPaths ?? options.initialBlankPaths ?? [];
4698
4625
  const blankPaths = reactive(/* @__PURE__ */ new Set());
4699
4626
  const originalBlankPaths = /* @__PURE__ */ new Set();
4700
- for (const raw of initialTransientList) {
4701
- const key = coerceToPathKey(raw);
4627
+ const seededBlankPaths = hydration !== void 0 ? (hydration.blankPaths ?? []).map((dotted) => canonicalizePath(dotted).key) : options.initialBlankPaths ?? [];
4628
+ for (const key of seededBlankPaths) {
4702
4629
  blankPaths.add(key);
4703
4630
  originalBlankPaths.add(key);
4704
4631
  }
@@ -4715,6 +4642,9 @@ function createFormStore(options) {
4715
4642
  }
4716
4643
  return result;
4717
4644
  });
4645
+ const errorPathIndex = computed(
4646
+ () => buildErrorPathIndex(errorCells, derivedBlankErrors.value)
4647
+ );
4718
4648
  const submitting = ref(false);
4719
4649
  const activeSubmissions = ref(0);
4720
4650
  const submissionAttempts = ref(0);
@@ -4747,7 +4677,7 @@ function createFormStore(options) {
4747
4677
  const defaultsResolved = ref(false);
4748
4678
  const activated = ref(false);
4749
4679
  const activationPromise = ref(void 0);
4750
- const firstValidationDone = ref(initialFirstValidationGate(schema, strict));
4680
+ const firstValidationDone = ref(initialFirstValidationGate(schema));
4751
4681
  watch(activeValidations, (now, prev) => {
4752
4682
  if (prev > 0 && now === 0) {
4753
4683
  firstValidationDone.value = true;
@@ -4765,6 +4695,33 @@ function createFormStore(options) {
4765
4695
  const activeTransforms = ref(0);
4766
4696
  const transformRuns = /* @__PURE__ */ new Map();
4767
4697
  const transformWaiters = [];
4698
+ const errorWindows = /* @__PURE__ */ new Map();
4699
+ const pathSweep = createDynamicPathSweep({
4700
+ onFormChange: (listener) => {
4701
+ formChangeListeners.add(listener);
4702
+ },
4703
+ isFixedObjectAtPath: (path) => schema.isFixedObjectAtPath(path)
4704
+ });
4705
+ pathSweep.onEvict((key) => {
4706
+ fields.delete(key);
4707
+ fieldValidationState.delete(key);
4708
+ authoredPaths.delete(key);
4709
+ if (originals.get(key)?.value === void 0) originals.delete(key);
4710
+ errorWindows.delete(key);
4711
+ });
4712
+ const errorWindowAt = (prefix, prefixKey) => {
4713
+ let cached = errorWindows.get(prefixKey);
4714
+ if (cached === void 0) {
4715
+ const frozen = [...prefix];
4716
+ cached = computed((prev) => {
4717
+ const next = windowUnder(errorPathIndex.value, frozen);
4718
+ return prev !== void 0 && isSameWindow(prev, next) ? prev : next;
4719
+ });
4720
+ errorWindows.set(prefixKey, cached);
4721
+ pathSweep.track(frozen, prefixKey);
4722
+ }
4723
+ return cached.value;
4724
+ };
4768
4725
  const arrayBookkeeping = createArrayBookkeeping({
4769
4726
  form,
4770
4727
  fields,
@@ -4782,6 +4739,7 @@ function createFormStore(options) {
4782
4739
  touchFieldRecord: (pathKey, path, patch) => touchFieldRecord(st, pathKey, path, patch),
4783
4740
  decFieldValidation: (key) => decFieldValidation(st, key)
4784
4741
  });
4742
+ const bind = (fn) => (...args) => fn(st, ...args);
4785
4743
  const st = {
4786
4744
  // --- public data (the FormStore contract's state members) ---
4787
4745
  formKey,
@@ -4789,10 +4747,11 @@ function createFormStore(options) {
4789
4747
  fields,
4790
4748
  errorCells,
4791
4749
  derivedBlankErrors,
4750
+ errorWindowAt,
4792
4751
  originals,
4752
+ pathSweep,
4793
4753
  schema,
4794
4754
  ssr,
4795
- getDisplayState: resolvedGetDisplayState,
4796
4755
  submitting,
4797
4756
  activeSubmissions,
4798
4757
  submissionAttempts,
@@ -4820,11 +4779,10 @@ function createFormStore(options) {
4820
4779
  displayEngine,
4821
4780
  domBinding,
4822
4781
  modules,
4823
- coerceIndex,
4782
+ coerceEnabled,
4824
4783
  blankPaths,
4825
4784
  originalBlankPaths,
4826
4785
  // --- kernel-internal state ---
4827
- strict,
4828
4786
  // Defensive copy, not a fix for an observed alias: today
4829
4787
  // `getDefaultValues` happens to build a fresh tree, so form storage
4830
4788
  // does not currently share structure with the consumer's object.
@@ -4861,17 +4819,20 @@ function createFormStore(options) {
4861
4819
  transformTokenSeq: 0,
4862
4820
  warnedDisabledWrite: false,
4863
4821
  // --- methods: thin per-instance skins over the module kernel ---
4864
- rehydrate: () => rehydrate(st),
4865
- activate: () => activate(st),
4866
- adoptResolvedDefaults: (value) => adoptResolvedDefaults(st, value),
4867
- pathHasAsyncValidation: (path) => pathHasAsyncValidation(st, path),
4868
- pathHasAsyncValidationByKey: (key, segments) => pathHasAsyncValidationByKey(st, key, segments),
4869
- applyFormReplacement: (next, meta) => applyFormReplacement(st, next, meta),
4870
- setValueAtPath: (path, value, meta) => setValueAtPath(st, path, value, meta),
4871
- getValueAtPath: (path) => getValueAtPath(st, path),
4872
- arrayElementKey: (path) => arrayElementKey(st, path),
4873
- reset: (nextDefaultValues) => reset(st, nextDefaultValues),
4874
- resetField: (path) => resetField(st, path),
4822
+ rehydrate: bind(rehydrate),
4823
+ activate: bind(activate),
4824
+ adoptResolvedDefaults: bind(adoptResolvedDefaults),
4825
+ pathHasAsyncValidationByKey: bind(pathHasAsyncValidationByKey),
4826
+ applyFormReplacement: bind(applyFormReplacement),
4827
+ setValueAtPath: bind(setValueAtPath),
4828
+ getValueAtPath: bind(getValueAtPath),
4829
+ arrayElementKey: bind(arrayElementKey),
4830
+ reset: bind(reset),
4831
+ resetField: bind(resetField),
4832
+ // The schema/user pair below reads as a fold waiting to happen. It was
4833
+ // tried: a `setErrorsForPathIn(channel)` factory measured 10 B LARGER,
4834
+ // because gzip had already collected the rent on two adjacent copies
4835
+ // and the helper added a name the original did not need.
4875
4836
  setSchemaErrorsForPath: (path, entries) => setErrorChannelForKey(
4876
4837
  st,
4877
4838
  canonicalizePath(path).key,
@@ -4880,7 +4841,7 @@ function createFormStore(options) {
4880
4841
  ),
4881
4842
  setAllSchemaErrors: (entries) => replaceErrorChannel(st, "schema", entries),
4882
4843
  clearSchemaErrors: (path) => clearErrorChannel(st, "schema", path),
4883
- applySchemaErrorsForSubtree: (path, entries) => applySchemaErrorsForSubtree(st, path, entries),
4844
+ applySchemaErrorsForSubtree: bind(applySchemaErrorsForSubtree),
4884
4845
  setAllUserErrors: (entries) => replaceErrorChannel(st, "user", entries),
4885
4846
  setUserErrorsForPath: (path, entries) => setErrorChannelForKey(
4886
4847
  st,
@@ -4889,37 +4850,34 @@ function createFormStore(options) {
4889
4850
  entries.length === 0 ? NO_ERRORS : [...entries]
4890
4851
  ),
4891
4852
  clearUserErrors: (path) => clearErrorChannel(st, "user", path),
4892
- restoreErrorCells: (entries) => restoreErrorCells(st, entries),
4893
- getErrorsForPath: (path) => getErrorsForPath(st, path),
4894
- ensurePathOrdinal: (key) => ensurePathOrdinal(st, key),
4895
- noteDomConnected: (path) => noteDomConnected(st, path),
4896
- noteDomDisconnected: (path) => noteDomDisconnected(st, path),
4897
- markFocused: (path, focused, meta) => markFocused(st, path, focused, meta),
4898
- markInteracted: (path) => markInteracted(st, path),
4899
- touchAtPath: (segments) => touchAtPath(st, segments),
4900
- interactAtPath: (segments) => interactAtPath(st, segments),
4901
- markConnectedOptimistically: (path) => markConnectedOptimistically(st, path),
4902
- isPristineAtPath: (path) => isPristineAtPath(st, path),
4903
- isPristineAtPathByKey: (key, segments) => isPristineAtPathByKey(st, key, segments),
4904
- hasStructuralChangeUnder: (path) => hasStructuralChangeUnder(st, path),
4905
- hasRemovedSubtreeUnder: (path) => hasRemovedSubtreeUnder(st, path),
4906
- getFieldRecord: (path) => getFieldRecord(st, path),
4907
- getOriginalAtPath: (path) => getOriginalAtPath(st, path),
4908
- cancelFieldValidation: () => cancelFieldValidation(st),
4909
- beginTransform: (key, holder) => beginTransform(st, key, holder),
4910
- isCurrentTransform: (key, token) => isCurrentTransform(st, key, token),
4911
- endTransform: (key, token) => endTransform(st, key, token),
4912
- setTransformError: (key, err) => setTransformError(st, key, err),
4913
- cancelTransforms: () => cancelTransforms(st),
4914
- cancelTransformsUnder: (prefix) => cancelTransformsUnder(st, prefix),
4915
- settleTransforms: (path) => settleTransforms(st, path),
4916
- scheduleFieldValidation: (path, immediate, override) => scheduleFieldValidation(st, path, immediate, override),
4917
- onFormChange: (listener) => onFormChange(st, listener),
4918
- onSubmitSuccess: (listener) => onSubmitSuccess(st, listener),
4919
- onReset: (listener) => onReset(st, listener),
4920
- emitSubmitSuccess: () => emitSubmitSuccess(st),
4921
- registerCleanup: (fn) => registerCleanup(st, fn),
4922
- dispose: () => dispose(st)
4853
+ restoreErrorCells: bind(restoreErrorCells),
4854
+ getErrorsForPath: bind(getErrorsForPath),
4855
+ ensurePathOrdinal: bind(ensurePathOrdinal),
4856
+ noteDomConnected: bind(noteDomConnected),
4857
+ noteDomDisconnected: bind(noteDomDisconnected),
4858
+ markFocused: bind(markFocused),
4859
+ markInteracted: bind(markInteracted),
4860
+ touchAtPath: bind(touchAtPath),
4861
+ interactAtPath: bind(interactAtPath),
4862
+ markConnectedOptimistically: bind(markConnectedOptimistically),
4863
+ isPristineAtPathByKey: bind(isPristineAtPathByKey),
4864
+ hasStructuralChangeUnder: bind(hasStructuralChangeUnder),
4865
+ hasRemovedSubtreeUnder: bind(hasRemovedSubtreeUnder),
4866
+ getFieldRecord: bind(getFieldRecord),
4867
+ cancelFieldValidation: bind(cancelFieldValidation),
4868
+ beginTransform: bind(beginTransform),
4869
+ isCurrentTransform: bind(isCurrentTransform),
4870
+ endTransform: bind(endTransform),
4871
+ setTransformError: bind(setTransformError),
4872
+ cancelTransformsUnder: bind(cancelTransformsUnder),
4873
+ settleTransforms: bind(settleTransforms),
4874
+ scheduleFieldValidation: bind(scheduleFieldValidation),
4875
+ onFormChange: bind(onFormChange),
4876
+ onSubmitSuccess: bind(onSubmitSuccess),
4877
+ onReset: bind(onReset),
4878
+ emitSubmitSuccess: bind(emitSubmitSuccess),
4879
+ registerCleanup: bind(registerCleanup),
4880
+ dispose: bind(dispose)
4923
4881
  };
4924
4882
  rebuildAuthoredPaths(st, defaultValues, schemaInitialData);
4925
4883
  seedOriginalsFromBaseline(st, schemaInitialData, true);
@@ -4961,7 +4919,7 @@ function createFormStore(options) {
4961
4919
  blurredAfterInteraction: false
4962
4920
  });
4963
4921
  });
4964
- if (strict && !schemaResponse.success) {
4922
+ if (!schemaResponse.success) {
4965
4923
  replaceErrorChannel(st, "schema", schemaResponse.errors);
4966
4924
  }
4967
4925
  }
@@ -4992,24 +4950,23 @@ function useAbstractForm(configuration, options) {
4992
4950
  const resolvedDefaults = resolveTrichotomy(configuration.defaultValues);
4993
4951
  const materialisedDefaults = resolvedDefaults.kind === "sync" ? resolvedDefaults.value : void 0;
4994
4952
  const { defaultValues: _droppedDefaults, ...configWithoutDefaults } = configuration;
4995
- const trichotomyOverride = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
4996
- const merged = mergeWithDefaults(registry.defaults, trichotomyOverride);
4997
- const maxRecursionDepth = normalizeNumericOption({
4998
- value: merged.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH,
4999
- source: "useForm.maxRecursionDepth",
5000
- allowInfinity: true,
5001
- min: 0,
5002
- defaultValue: DEFAULT_MAX_RECURSION_DEPTH
5003
- });
5004
- const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
4953
+ const materialisedConfiguration = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
5005
4954
  const existing = registry.forms.get(key);
4955
+ const resolvedSchema = getComputedSchema(key, configuration.schema, {
4956
+ maxRecursionDepth: DEFAULT_MAX_RECURSION_DEPTH
4957
+ }) ;
5006
4958
  if (existing !== void 0) {
5007
4959
  void import('../chunks/dev-key-collision-warnings.mjs').then((m) => {
5008
4960
  void m.warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
5009
4961
  });
5010
4962
  }
5011
4963
  const hadPendingHydration = registry.pendingHydration.has(key);
5012
- const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
4964
+ const state = existing ?? buildFreshState(
4965
+ key,
4966
+ resolvedSchema,
4967
+ materialisedConfiguration,
4968
+ registry
4969
+ );
5013
4970
  if (existing !== void 0) ; else if (resolvedDefaults.kind === "sync") {
5014
4971
  state.defaultsResolved.value = true;
5015
4972
  }
@@ -5034,8 +4991,8 @@ function useAbstractForm(configuration, options) {
5034
4991
  const releaseConsumer = registry.trackConsumer(key);
5035
4992
  onScopeDispose(releaseConsumer);
5036
4993
  }
5037
- if (existing === void 0 && merged.history !== void 0) {
5038
- const historyModule = merged.history.attach(state);
4994
+ if (existing === void 0 && materialisedConfiguration.history !== void 0) {
4995
+ const historyModule = materialisedConfiguration.history.attach(state);
5039
4996
  state.modules.set(HISTORY_MODULE_KEY, historyModule);
5040
4997
  state.registerCleanup(() => historyModule.dispose());
5041
4998
  }
@@ -5048,36 +5005,16 @@ function useAbstractForm(configuration, options) {
5048
5005
  provide(kFormInstanceId, formInstanceId);
5049
5006
  }
5050
5007
  const apiOptions = pickDefined({
5051
- onInvalidSubmit: merged.onInvalidSubmit,
5008
+ focusOnInvalidSubmit: materialisedConfiguration.focusOnInvalidSubmit,
5052
5009
  history: state.modules.get(HISTORY_MODULE_KEY),
5053
- validateOn: merged.validateOn,
5054
- debounceMs: merged.debounceMs,
5055
- getDisplayState: merged.getDisplayState,
5056
- coerce: merged.coerce,
5057
- rememberVariants: merged.rememberVariants,
5058
- autoAria: merged.autoAria
5010
+ validateOn: materialisedConfiguration.validateOn,
5011
+ debounceMs: materialisedConfiguration.debounceMs,
5012
+ coerce: materialisedConfiguration.coerce,
5013
+ rememberVariants: materialisedConfiguration.rememberVariants
5059
5014
  });
5060
5015
  const api = buildFormApi(state, formInstanceId, apiOptions);
5061
5016
  return api;
5062
5017
  }
5063
- function mergeWithDefaults(defaults, configuration) {
5064
- return {
5065
- ...configuration,
5066
- ...pickDefined({
5067
- strict: configuration.strict ?? defaults.strict,
5068
- onInvalidSubmit: configuration.onInvalidSubmit ?? defaults.onInvalidSubmit,
5069
- history: configuration.history ?? defaults.history,
5070
- rememberVariants: configuration.rememberVariants ?? defaults.rememberVariants,
5071
- disabled: configuration.disabled ?? defaults.disabled,
5072
- coerce: configuration.coerce ?? defaults.coerce,
5073
- validateOn: configuration.validateOn ?? defaults.validateOn,
5074
- debounceMs: configuration.debounceMs ?? defaults.debounceMs,
5075
- getDisplayState: configuration.getDisplayState ?? defaults.getDisplayState,
5076
- maxRecursionDepth: configuration.maxRecursionDepth ?? defaults.maxRecursionDepth,
5077
- autoAria: configuration.autoAria ?? defaults.autoAria
5078
- })
5079
- };
5080
- }
5081
5018
  const HISTORY_MODULE_KEY = "history";
5082
5019
  function buildFreshState(key, schema, configuration, registry) {
5083
5020
  const pending = registry.pendingHydration.get(key);
@@ -5097,13 +5034,11 @@ function buildFreshState(key, schema, configuration, registry) {
5097
5034
  hydration: pending,
5098
5035
  ssr: registry.ssr,
5099
5036
  ...pickDefined({
5100
- strict: configuration.strict,
5101
5037
  validateOn: configuration.validateOn,
5102
5038
  debounceMs: configuration.debounceMs,
5103
5039
  rememberVariants: configuration.rememberVariants,
5104
5040
  disabled: configuration.disabled,
5105
5041
  coerce: configuration.coerce,
5106
- getDisplayState: configuration.getDisplayState,
5107
5042
  initialBlankPaths
5108
5043
  }),
5109
5044
  // Server-only: bind the SSR prefetch coordination handles. `enqueue`
@@ -5417,25 +5352,18 @@ function createWizardHistory(param) {
5417
5352
  };
5418
5353
  }
5419
5354
 
5420
- const NOOP_FINGERPRINT = "attaform:wizard-noop";
5421
5355
  const EMPTY_SLIM_KINDS = /* @__PURE__ */ new Set();
5422
- function buildNoopWizardSchema(formKey) {
5423
- const emptyValue = {};
5424
- const success = {
5425
- success: true,
5426
- data: emptyValue,
5427
- errors: void 0,
5428
- formKey
5429
- };
5430
- const defaultsResponse = {
5431
- success: true,
5432
- data: emptyValue,
5433
- errors: void 0,
5434
- formKey
5435
- };
5436
- return {
5437
- fingerprint: () => Promise.resolve(NOOP_FINGERPRINT),
5438
- getDefaultValues: () => defaultsResponse,
5356
+ const EMPTY_VALUE = {};
5357
+ const NOOP_SUCCESS = {
5358
+ success: true,
5359
+ data: EMPTY_VALUE,
5360
+ errors: void 0
5361
+ };
5362
+ const NOOP_DEFAULTS = NOOP_SUCCESS;
5363
+ let NOOP_SCHEMA;
5364
+ function buildNoopWizardSchema() {
5365
+ NOOP_SCHEMA ?? (NOOP_SCHEMA = {
5366
+ getDefaultValues: () => NOOP_DEFAULTS,
5439
5367
  getDefaultAtPath: () => void 0,
5440
5368
  getEmptyValueAtPath: () => void 0,
5441
5369
  isPreprocessOrCoerceLeaf: () => false,
@@ -5446,13 +5374,14 @@ function buildNoopWizardSchema(formKey) {
5446
5374
  // addressable, root included.
5447
5375
  entryKeyKindAtPath: () => void 0,
5448
5376
  getSchemasAtPath: () => [],
5449
- validateAtPath: () => success,
5377
+ validateAtPath: () => NOOP_SUCCESS,
5450
5378
  getSlimPrimitiveTypesAtPath: () => new Set(EMPTY_SLIM_KINDS),
5451
5379
  isLeafAtPath: () => false,
5452
5380
  isRequiredAtPath: () => false,
5453
5381
  getUnionDiscriminatorAtPath: () => void 0,
5454
5382
  hasDiscriminatedUnions: () => false
5455
- };
5383
+ });
5384
+ return NOOP_SCHEMA;
5456
5385
  }
5457
5386
 
5458
5387
  function buildCallableReadonlySnapshotProxy(opts) {
@@ -5571,7 +5500,7 @@ function useWizard(options) {
5571
5500
  if (typeof slot !== "string") continue;
5572
5501
  if (noopForms.has(slot)) continue;
5573
5502
  const noop = useAbstractForm({
5574
- schema: buildNoopWizardSchema(slot),
5503
+ schema: buildNoopWizardSchema(),
5575
5504
  key: slot
5576
5505
  });
5577
5506
  noopForms.set(slot, noop);
@@ -5582,7 +5511,7 @@ function useWizard(options) {
5582
5511
  const noop = lazyNoopScope.run(
5583
5512
  () => useAbstractForm(
5584
5513
  {
5585
- schema: buildNoopWizardSchema(key),
5514
+ schema: buildNoopWizardSchema(),
5586
5515
  key
5587
5516
  },
5588
5517
  { registry }
@@ -6806,4 +6735,4 @@ function escapeForInlineScript(json) {
6806
6735
  });
6807
6736
  }
6808
6737
 
6809
- 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 };
6738
+ 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 };