attaform 0.30.0 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/abstract.d.mts +15 -21
  2. package/dist/abstract.mjs +2 -2
  3. package/dist/dev/abstract.mjs +4 -4
  4. package/dist/dev/chunks/devtools.mjs +2 -2
  5. package/dist/dev/directive.mjs +1 -1
  6. package/dist/dev/history.mjs +1 -1
  7. package/dist/dev/index.mjs +5 -5
  8. package/dist/dev/runtime/plugins/attaform.mjs +1 -1
  9. package/dist/dev/shared/{attaform-dev.BK6Ge2qR.mjs → attaform-dev.BJK1kqHY.mjs} +14 -14
  10. package/dist/dev/shared/{attaform-dev.D6CLvaQT.mjs → attaform-dev.BMQ3GcgD.mjs} +46 -59
  11. package/dist/dev/shared/{attaform-dev.BisFstxh.mjs → attaform-dev.BUIFcTYf.mjs} +6 -6
  12. package/dist/dev/shared/{attaform-dev.DTrHonpc.mjs → attaform-dev.CT7MDjPy.mjs} +27 -36
  13. package/dist/dev/shared/{attaform-dev.BcYT6tFh.mjs → attaform-dev.CZz4GYZ6.mjs} +21 -9
  14. package/dist/dev/shared/{attaform-dev.DUZckyV8.mjs → attaform-dev.Chm4D6SJ.mjs} +2 -2
  15. package/dist/dev/shared/{attaform-dev.BcNtyOmf.mjs → attaform-dev.DuEE4GU1.mjs} +4 -4
  16. package/dist/dev/shared/{attaform-dev.CQ2rJiAX.mjs → attaform-dev.DxHdkkPG.mjs} +126 -115
  17. package/dist/dev/shared/{attaform-dev.BQc5QV3Y.mjs → attaform-dev.zlvDr0WB.mjs} +2 -2
  18. package/dist/dev/zod-v3.mjs +6 -6
  19. package/dist/dev/zod-v4.mjs +6 -6
  20. package/dist/dev/zod.mjs +5 -5
  21. package/dist/directive.d.mts +4 -4
  22. package/dist/directive.mjs +1 -1
  23. package/dist/esbuild.d.mts +11 -0
  24. package/dist/esbuild.mjs +1 -1
  25. package/dist/history.d.mts +3 -32
  26. package/dist/history.mjs +1 -1
  27. package/dist/index.d.mts +67 -70
  28. package/dist/index.mjs +3 -3
  29. package/dist/nuxt.d.mts +4 -3
  30. package/dist/nuxt.mjs +3 -6
  31. package/dist/rollup.d.mts +11 -0
  32. package/dist/rollup.mjs +1 -1
  33. package/dist/rspack.d.mts +19 -11
  34. package/dist/rspack.mjs +1 -1
  35. package/dist/runtime/components/AttaformDevtoolsPanel.vue +12 -12
  36. package/dist/runtime/components/DevtoolsValueTree.vue +2 -2
  37. package/dist/shared/{attaform.Rc9u4Vfw.d.mts → attaform.2FslhB9R.d.mts} +83 -95
  38. package/dist/shared/{attaform.BRDDQeHV.mjs → attaform.8mfKZCwH.mjs} +2 -2
  39. package/dist/shared/{attaform.DRUT703x.d.mts → attaform.B-Tb62qV.d.mts} +12 -12
  40. package/dist/shared/{attaform.v0fQtWyA.mjs → attaform.B1rH9ht6.mjs} +1 -1
  41. package/dist/shared/{attaform.ByWP6kSx.mjs → attaform.B2iebkPA.mjs} +3 -3
  42. package/dist/shared/{attaform.DhnPb6nK.mjs → attaform.BAf3UNn4.mjs} +1 -1
  43. package/dist/shared/{attaform.DNJOWjQw.mjs → attaform.BIWicztt.mjs} +102 -100
  44. package/dist/shared/attaform.BcQwgWaq.d.mts +2153 -0
  45. package/dist/shared/attaform.Br8EBn5B.d.mts +4553 -0
  46. package/dist/shared/{attaform.BXneBxEj.mjs → attaform.CO918TdU.mjs} +13 -13
  47. package/dist/shared/{attaform.CUfrMK7t.mjs → attaform.DRsANMok.mjs} +5 -5
  48. package/dist/shared/{attaform.B3resMQ1.mjs → attaform.D_f1WgYf.mjs} +40 -53
  49. package/dist/shared/{attaform.CJ8DpRGj.mjs → attaform.D_pt1gOj.mjs} +26 -33
  50. package/dist/shared/attaform.L0kUn3vL.d.mts +42 -0
  51. package/dist/shared/{attaform.BK0cgu2K.mjs → attaform.ZYn2ahzJ.mjs} +3 -3
  52. package/dist/transforms.d.mts +153 -26
  53. package/dist/transforms.mjs +1 -1
  54. package/dist/vite.d.mts +29 -52
  55. package/dist/vite.mjs +3 -3
  56. package/dist/webpack.d.mts +20 -12
  57. package/dist/webpack.mjs +1 -1
  58. package/dist/zod-v3.d.mts +90 -96
  59. package/dist/zod-v3.mjs +4 -4
  60. package/dist/zod-v4.d.mts +118 -118
  61. package/dist/zod-v4.mjs +4 -4
  62. package/dist/zod.d.mts +5 -5
  63. package/dist/zod.mjs +3 -3
  64. package/package.json +7 -6
  65. package/dist/shared/attaform.Clz0ArIi.d.mts +0 -44
  66. package/dist/shared/attaform.UGyOK83T.d.mts +0 -2366
  67. package/dist/shared/attaform.bKLzMU0o.d.mts +0 -4761
@@ -1,12 +1,12 @@
1
1
  import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, reactive, shallowReactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope, nextTick } from 'vue';
2
2
  import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform.WiGdIqgx.mjs';
3
- import { c as armDomBinding } from './attaform.B3resMQ1.mjs';
3
+ import { c as armDomBinding } from './attaform.D_f1WgYf.mjs';
4
4
  import { R as REGISTER_OWNER_MARKER, b as V_REGISTER_MARKER } from './attaform.KvlEedlF.mjs';
5
- import { A as ANONYMOUS_FORM_KEY_PREFIX, 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.BRDDQeHV.mjs';
5
+ import { A as ANONYMOUS_FORM_KEY_PREFIX, i as isShadowedKey, d as safeOwnHas, a as safeOwnRead, e as consumerHas, f as spreadConsumerRecord, g as copyConsumerArray, r as readConsumerIndex, b as safeAssign, h as consumerKeys, j as readConsumerProp, k as diffAndApply, p as pickDefined, s as structuralSnapshot, n as normalizeNumericOption, l as DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS, m as consumerSymbolKeys, o as applyChangedKeys, R as RESERVED_KEY_PREFIX, q as DEFAULT_MAX_RECURSION_DEPTH, c as callConsumerFn, t as ANONYMOUS_WIZARD_KEY_PREFIX } from './attaform.8mfKZCwH.mjs';
6
6
  import { i as isPathPrefix, e as canonicalizePath, k as keyForSegments, m as mapSegmentKeys, b as ROOT_PATH_KEY, s as segmentsForPathKey, S as SubmitErrorHandlerError, t as toError, j as groupErrorsByKey, n as normalizeErrorInput, h as SET_MEMBER_SEGMENT, R as ROOT_PATH, l as normalizeErrorInputs, a as InvalidUseFormConfigError, d as ReservedFormKeyError } from './attaform.CdZIEf7_.mjs';
7
7
 
8
8
  const AttaformErrorCode = {
9
- /** A required field is in the blank set — user hasn't supplied a value. */
9
+ /** A required field is in the blank set: no value was supplied. */
10
10
  NoValueSupplied: "atta:no-value-supplied",
11
11
  /** The schema adapter's `validateAtPath` threw synchronously. */
12
12
  AdapterThrew: "atta:adapter-threw",
@@ -566,9 +566,8 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
566
566
  updatedAt: record?.updatedAt ?? null,
567
567
  errors,
568
568
  // A leaf has no descendants, so its own-bucket errors ARE its subtree
569
- // errors. Reuse the same array reference: referential `ownErrors ===
570
- // errors`, and no second read of the whole-form `derivedBlankErrors`
571
- // map (preserves the P3 blank-reactivity optimization above).
569
+ // errors. Reuse the array reference, which makes `ownErrors === errors`
570
+ // and avoids a second read of the whole-form `derivedBlankErrors` map.
572
571
  ownErrors: errors,
573
572
  validating,
574
573
  valid,
@@ -748,19 +747,19 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
748
747
  elements: EMPTY_ELEMENTS,
749
748
  updatedAt,
750
749
  errors,
751
- // The container's OWN bucket only (this exact path), excluding every
752
- // descendant that `errors` rolls up: a cross-field `.refine()` here,
753
- // or `setErrors` pinned at this node. The same raw union the leaf
754
- // builds from the three stores; the container already depends on all
755
- // three via `aggregateErrorsAt`, so there's no new reactivity cost.
750
+ // The container's OWN bucket at this exact path, excluding every
751
+ // descendant `errors` rolls up: a cross-field `.refine()` here, or a
752
+ // `setErrors` pinned at this node. The same raw union the leaf builds
753
+ // from the three stores, and the container already depends on all three
754
+ // through `aggregateErrorsAt`, so it costs no new reactivity.
756
755
  ownErrors: state.getErrorsForPath(segments),
757
756
  validating,
758
757
  valid,
759
758
  transforming,
760
759
  busy: transforming || validating,
761
- // A container surfaces its OWN transform failure (a transform registered
762
- // on the container path, e.g. a file normalizer) but never rolls up a
763
- // descendant leaf's failure — that stays a per-field channel.
760
+ // A container surfaces its OWN transform failure, from a transform
761
+ // registered on the container path such as a file normalizer, and never
762
+ // rolls up a descendant's: that stays a per-field channel.
764
763
  transformError: ownTransformError,
765
764
  path: segments,
766
765
  ...computeFieldIdentity(formInstanceId, state.formKey, key),
@@ -1092,9 +1091,9 @@ function buildErrorsSurface(state, sweep) {
1092
1091
  // non-schema keys reachable while a genuinely-absent key stays out.
1093
1092
  hasOwn: (segments, key) => liveContainerHasKey(state, segments, key) || errorAwareContainerKeys(state, segments).includes(key),
1094
1093
  isArrayAt: (segments) => isArrayPath(state, segments),
1095
- // `errors()` / `errors([])` / `errors(path)` are all the subtree
1096
- // aggregate — the same helper `meta.errors` reads, so the surfaces
1097
- // never drift.
1094
+ // `errors()`, `errors([])` and `errors(path)` are all the subtree
1095
+ // aggregate, through the helper `meta.errors` reads, so they cannot
1096
+ // drift.
1098
1097
  call: (path) => aggregateErrorsAt(state, path, keyForSegments(path).key),
1099
1098
  surface: "form.errors",
1100
1099
  sweep
@@ -2294,7 +2293,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2294
2293
  const accepted = isBlankSignal ? markBlank() : state.setValueAtPath(segments, value, withInstanceMeta(void 0));
2295
2294
  return accepted;
2296
2295
  },
2297
- // Called by the `vRegisterHint` compile-time transform's wrapping
2296
+ // Called by the `vRegisterHintTransform` compile-time transform's wrapping
2298
2297
  // IIFE on every server-side render of `<element v-register="…">`.
2299
2298
  // Without it, every SSR'd FieldState serialises `connected: false`
2300
2299
  // (because Vue skips directive lifecycle during SSR) and the client
@@ -2323,12 +2322,12 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2323
2322
  }
2324
2323
  return false;
2325
2324
  },
2326
- // --- Async transform lifecycle (internal; the directive's
2327
- // deferred orchestrator is the only legitimate consumer). Thin
2328
- // path-bound delegates to the store's per-path token / counter
2329
- // machinery — same pattern as `markBlank` / `setValueWithInternalPath`,
2330
- // so the directive (which holds only this RegisterValue, never the
2331
- // store) can drive the busy/discard/error bookkeeping. ---
2325
+ // --- Async transform lifecycle (internal; the directive's deferred
2326
+ // orchestrator is the only legitimate consumer). Thin path-bound
2327
+ // delegates to the store's per-path token and counter machinery,
2328
+ // the same pattern as `markBlank` and `setValueWithInternalPath`, so
2329
+ // the directive (which holds this RegisterValue and never the store)
2330
+ // can drive the busy / discard / error bookkeeping. ---
2332
2331
  beginTransform: (holder) => state.beginTransform(pathKey, holder),
2333
2332
  isCurrentTransform: (token) => state.isCurrentTransform(pathKey, token),
2334
2333
  endTransform: (token) => state.endTransform(pathKey, token),
@@ -2341,10 +2340,10 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2341
2340
  return (state.fieldTransformCounts.get(pathKey) ?? 0) > 0;
2342
2341
  },
2343
2342
  path: pathKey,
2344
- // Frozen so a wrapper component can pass `rv.segments` directly
2345
- // to `form.fields(...)` without defensive copying — and so test
2346
- // fixtures or downstream code can't mutate the canonical
2347
- // segment list out from under the directive.
2343
+ // Frozen so a wrapper component can pass `rv.segments` straight to
2344
+ // `form.fields(...)` with no defensive copy, and so neither a test
2345
+ // fixture nor downstream code can mutate the canonical segment list
2346
+ // out from under the directive.
2348
2347
  segments: Object.freeze(segments.slice()),
2349
2348
  formKey: state.formKey,
2350
2349
  formInstanceId,
@@ -2550,15 +2549,15 @@ function buildMetaProxy(keys, read) {
2550
2549
  return new Proxy(target, {
2551
2550
  get: (_, key) => owns.has(key) ? read(key) : Reflect.get(target, key),
2552
2551
  has: (_, key) => owns.has(key) || Reflect.has(target, key),
2553
- // Safe to hand back the same array every time: the spec copies a
2554
- // trap's key list before using it.
2552
+ // Safe to hand back the same array every time: the spec copies a trap's
2553
+ // key list before using it.
2555
2554
  ownKeys: () => keys,
2556
2555
  getOwnPropertyDescriptor(_, key) {
2557
2556
  if (!owns.has(key)) return Reflect.getOwnPropertyDescriptor(target, key);
2558
2557
  return { configurable: true, enumerable: true, value: read(key), writable: false };
2559
2558
  },
2560
- // A getter with no setter throws on assignment under strict mode,
2561
- // which every module here is. `false` keeps that.
2559
+ // A getter with no setter throws on assignment under strict mode, which
2560
+ // every module here is; `false` keeps that.
2562
2561
  set: denyWrite,
2563
2562
  deleteProperty: denyWrite,
2564
2563
  defineProperty: denyWrite
@@ -2794,12 +2793,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
2794
2793
  );
2795
2794
  const rootFieldState = getRootFieldStateAt([]);
2796
2795
  const metaOwn = {
2797
- // Whole-form work signals compose the LIFECYCLE counters with the
2798
- // per-leaf rollup: a submit-time validate shows up as
2799
- // activeValidations, per-field debounced validators as
2800
- // fieldValidationCounts — either flips the flag. `valid` keeps the
2801
- // form-level `firstValidationDone` mount gate; `errors` is the
2802
- // unfiltered whole-form aggregate.
2796
+ // Whole-form work signals compose the LIFECYCLE counters with the per-leaf
2797
+ // rollup: a submit-time validate shows up in `activeValidations` and a
2798
+ // per-field debounced validator in `fieldValidationCounts`, and either
2799
+ // flips the flag. `valid` keeps the form-level mount gate, and `errors` is
2800
+ // the unfiltered whole-form aggregate.
2803
2801
  validating: computed(
2804
2802
  () => state.activeValidations.value > 0 || rootFieldState.value.validating
2805
2803
  ),
@@ -2811,7 +2809,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2811
2809
  busy: computed(
2812
2810
  () => state.activeValidations.value > 0 || state.activeTransforms.value > 0 || rootFieldState.value.validating || rootFieldState.value.transforming
2813
2811
  ),
2814
- // Lifecycle (form-level only — not on FieldState).
2812
+ // Lifecycle, form-level only and never on FieldState.
2815
2813
  submitting,
2816
2814
  submissionAttempts,
2817
2815
  departAttempts,
@@ -2928,10 +2926,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
2928
2926
  }
2929
2927
  const api = {
2930
2928
  handleSubmit: gated(handleSubmit),
2931
- // Callable readonly Proxies (`values`, `fields`, `errors`) and the
2932
- // reactive containers (`meta`, `history`, `blankPaths`) are exposed
2933
- // through getters so reading them activates the form on first
2934
- // touch. Each underlying object is identity-stable across reads.
2929
+ // The callable readonly Proxies (`values`, `fields`, `errors`) and the
2930
+ // reactive containers (`meta`, `history`, `blankPaths`) are exposed through
2931
+ // getters, so reading one activates the form on first touch. Each
2932
+ // underlying object is identity-stable across reads.
2935
2933
  get values() {
2936
2934
  void state.activate();
2937
2935
  return valuesProxy;
@@ -2948,7 +2946,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2948
2946
  key: state.formKey,
2949
2947
  // Auto-unwrapping views over the per-store async-defaults lifecycle
2950
2948
  // refs (see FormStore.hydrating / hydrateError). Reading either
2951
- // activates the form — observing factory state implies use.
2949
+ // activates the form: observing factory state implies use.
2952
2950
  get hydrating() {
2953
2951
  void state.activate();
2954
2952
  return state.hydrating.value;
@@ -2957,20 +2955,18 @@ function buildFormApi(state, formInstanceId, options = {}) {
2957
2955
  void state.activate();
2958
2956
  return state.hydrateError.value;
2959
2957
  },
2960
- // Orthogonal to `hydrating` and `hydrateError`: `ready` flips true
2961
- // once defaults are applied (sync at construction or async factory
2962
- // resolved successfully). One-way latch — stays true through later
2963
- // refetches even when those refetches fail, so stale-while-
2964
- // revalidate UIs keep rendering the prior values while
2965
- // `hydrateError` surfaces the refresh failure.
2958
+ // Orthogonal to `hydrating` and `hydrateError`. `ready` flips true once
2959
+ // defaults are applied, whether sync at construction or from a factory
2960
+ // that resolved, and it is a one-way latch: it stays true through later
2961
+ // refetches even when those fail, so a stale-while-revalidate UI keeps
2962
+ // rendering the prior values while `hydrateError` surfaces the failure.
2966
2963
  get ready() {
2967
2964
  void state.activate();
2968
2965
  return state.defaultsResolved.value;
2969
2966
  },
2970
- // `rehydrate` and `activate` are themselves activation entry points
2971
- // — they fire the factory by design. Wrapping them with `gated`
2972
- // would double-fire (`state.activate()` plus the underlying call),
2973
- // so they call `state` directly.
2967
+ // `rehydrate` and `activate` are activation entry points themselves and
2968
+ // fire the factory by design, so wrapping them in `gated` would
2969
+ // double-fire. They call `state` directly.
2974
2970
  rehydrate: () => state.rehydrate(),
2975
2971
  activate: () => state.activate(),
2976
2972
  get errors() {
@@ -3585,19 +3581,16 @@ function touchFieldRecord(st, pathKey, path, patch) {
3585
3581
  path,
3586
3582
  updatedAt: patch.updatedAt ?? current?.updatedAt ?? null,
3587
3583
  connected: patch.connected ?? current?.connected ?? false,
3588
- // focused/blurred use an explicit-undefined guard because
3589
- // patches legitimately carry `null` to mark a disconnect — the
3590
- // `??` operator would short-circuit on null and fall through to
3591
- // `current`, losing the intent. `!== undefined` honours an
3592
- // explicit null and preserves current only on absence.
3584
+ // `focused` / `blurred` need an explicit-undefined guard: a patch
3585
+ // legitimately carries `null` to mark a disconnect, and `??` would
3586
+ // short-circuit on it and fall through to `current`, losing the intent.
3593
3587
  focused: patch.focused !== void 0 ? patch.focused : current?.focused ?? null,
3594
3588
  blurred: patch.blurred !== void 0 ? patch.blurred : current?.blurred ?? null,
3595
- // touched is plain `boolean`; `??` is equivalent to the explicit
3596
- // guard here because `false` is not nullish.
3589
+ // `touched` is a plain boolean, so `??` is equivalent to the guard above.
3597
3590
  touched: patch.touched ?? current?.touched ?? false,
3598
- // interacted is sticky-true; a merge patch only ever sets it, so
3599
- // `??` preserves the current bit. It flips back to false solely
3600
- // through the reset paths, which reconstruct the record outright.
3591
+ // `interacted` is sticky-true and a merge patch only ever sets it, so `??`
3592
+ // preserves the current bit. Only the reset paths clear it, and they
3593
+ // reconstruct the record outright.
3601
3594
  interacted: patch.interacted ?? current?.interacted ?? false,
3602
3595
  blurredAfterInteraction: patch.blurredAfterInteraction ?? current?.blurredAfterInteraction ?? false
3603
3596
  });
@@ -4160,13 +4153,13 @@ function markFocused(st, path, focused, meta) {
4160
4153
  touchFieldRecord(st, key, path, {
4161
4154
  focused,
4162
4155
  blurred: !focused,
4163
- // `touched` flips to true on blur and stays true thereafter; while
4164
- // a field is currently focused we keep whatever value it held.
4156
+ // `touched` flips true on blur and stays; a focused field keeps whatever
4157
+ // it held.
4165
4158
  touched: focused ? current?.touched ?? false : true,
4166
- // `blurredAfterInteraction` flips true on the first blur that lands
4167
- // after a value edit and stays true. A tab-through blur before any
4168
- // edit leaves it false (`interacted` is still false at that blur),
4169
- // which is what keeps a clean tab-through from arming the gate.
4159
+ // `blurredAfterInteraction` flips true on the first blur after a value
4160
+ // edit and stays. A tab-through blur before any edit leaves it false,
4161
+ // since `interacted` is still false, which is what keeps a clean
4162
+ // tab-through from arming the gate.
4170
4163
  blurredAfterInteraction: !focused && current?.interacted === true ? true : current?.blurredAfterInteraction ?? false
4171
4164
  });
4172
4165
  const focusMode = meta?.instance?.validateOn ?? st.fieldValidationMode;
@@ -4618,15 +4611,12 @@ function createFormStore(options) {
4618
4611
  blankPaths,
4619
4612
  originalBlankPaths,
4620
4613
  // --- kernel-internal state ---
4621
- // Defensive copy, not a fix for an observed alias: today
4622
- // `getDefaultValues` happens to build a fresh tree, so form storage
4623
- // does not currently share structure with the consumer's object.
4624
- // That is an adapter implementation detail rather than a contract,
4625
- // and this field is now durable state that every `reset()` reads, so
4626
- // it should not depend on it. The copy also means a consumer who
4627
- // keeps a reference to the literal they passed cannot mutate the
4628
- // form's defaults from outside. `reset()` snapshots for a harder
4629
- // reason (see there) where an alias IS reachable.
4614
+ // A defensive copy. `getDefaultValues` building a fresh tree is an adapter
4615
+ // implementation detail, not a contract, and this field is durable state
4616
+ // that every `reset()` reads, so it must not depend on one. The copy also
4617
+ // stops a consumer holding a reference to the literal they passed from
4618
+ // mutating the form's defaults from outside. `reset()` snapshots for a
4619
+ // harder reason, where an alias IS reachable.
4630
4620
  defaultValues: structuralSnapshot(defaultValues),
4631
4621
  ssrPrefetch,
4632
4622
  rememberVariants,
@@ -4664,10 +4654,10 @@ function createFormStore(options) {
4664
4654
  arrayElementKey: bind(arrayElementKey),
4665
4655
  reset: bind(reset),
4666
4656
  resetField: bind(resetField),
4667
- // The schema/user pair below reads as a fold waiting to happen. It was
4668
- // tried: a `setErrorsForPathIn(channel)` factory measured 10 B LARGER,
4669
- // because gzip had already collected the rent on two adjacent copies
4670
- // and the helper added a name the original did not need.
4657
+ // The schema/user pair below reads as a fold waiting to happen. Folding
4658
+ // them into a `setErrorsForPathIn(channel)` factory measures 10 B LARGER:
4659
+ // gzip has already collected the rent on two adjacent copies, and the
4660
+ // helper adds a name the original does not need.
4671
4661
  setSchemaErrorsForPath: (path, entries) => setErrorChannelForKey(
4672
4662
  st,
4673
4663
  canonicalizePath(path).key,
@@ -4868,12 +4858,10 @@ function buildFreshState(key, schema, configuration, registry) {
4868
4858
  coerce: configuration.coerce,
4869
4859
  initialBlankPaths
4870
4860
  }),
4871
- // Server-only: bind the SSR prefetch coordination handles. `enqueue`
4872
- // records intent on every `state.activate()` so a wizard skip-list
4873
- // override or a future transform mark has a consistent set to diff
4874
- // against; `shouldFire` lets the activate path bail when the
4875
- // wizard explicitly skipped this key — even an explicit
4876
- // `form.activate()` defers to the wizard's render-efficiency
4861
+ // Server only. `enqueue` records intent on every `state.activate()`
4862
+ // so a wizard skip-list override has a consistent set to diff
4863
+ // against; `shouldFire` lets the activate path bail on a key the
4864
+ // wizard skipped. Even an explicit `form.activate()` defers to that
4877
4865
  // skip-list on the server.
4878
4866
  ...registry.ssr ? {
4879
4867
  ssrPrefetch: {
@@ -5335,27 +5323,33 @@ function useWizard(options) {
5335
5323
  return activeKey.value === "" ? void 0 : activeKey.value;
5336
5324
  }
5337
5325
  };
5338
- function lazyComputedFor(marker) {
5326
+ function lazyComputedFor(marker, index) {
5339
5327
  const cached = lazyComputeds.get(marker);
5340
5328
  if (cached !== void 0) return cached;
5341
5329
  const c = computed(() => {
5342
5330
  void lazyEpoch.value;
5343
- return marker.resolve(slotCtx);
5331
+ return callConsumerFn(
5332
+ () => marker.resolve(slotCtx),
5333
+ void 0);
5344
5334
  });
5345
5335
  lazyComputeds.set(marker, c);
5346
5336
  return c;
5347
5337
  }
5348
5338
  const MAX_SLOT_DEPTH = 32;
5349
- function normalizeSlot(slot, ctx, gated, depth) {
5339
+ function normalizeSlot(slot, ctx, gated, depth, index) {
5350
5340
  if (depth > MAX_SLOT_DEPTH) {
5351
5341
  return void 0;
5352
5342
  }
5353
5343
  if (slot === void 0 || slot === null) return void 0;
5354
5344
  if (typeof slot === "string") return { form: getOrBuildNoop(slot), gated };
5355
5345
  if (isGateMarker(slot)) return normalizeSlot(slot.inner, ctx, true, depth + 1);
5356
- if (isLazyMarker(slot)) return normalizeSlot(lazyComputedFor(slot).value, ctx, gated, depth + 1);
5346
+ if (isLazyMarker(slot)) {
5347
+ return normalizeSlot(lazyComputedFor(slot).value, ctx, gated, depth + 1);
5348
+ }
5357
5349
  if (typeof slot === "function") {
5358
- const result = slot(ctx);
5350
+ const result = callConsumerFn(
5351
+ () => slot(ctx),
5352
+ void 0);
5359
5353
  return normalizeSlot(result, ctx, gated, depth + 1);
5360
5354
  }
5361
5355
  if (isAnyForm(slot)) return { form: slot, gated };
@@ -5673,13 +5667,14 @@ function useWizard(options) {
5673
5667
  seedRef.value = resolved.value;
5674
5668
  applyGateSeed(resolved.value);
5675
5669
  } else {
5676
- const eager = resolved.factory();
5670
+ const eager = callConsumerFn(() => resolved.factory(), void 0);
5677
5671
  if (eager instanceof Promise) {
5678
5672
  void eager.then((value) => {
5679
5673
  seedRef.value = value;
5680
5674
  applyGateSeed(value);
5675
+ }).catch((err) => {
5681
5676
  });
5682
- } else {
5677
+ } else if (eager !== void 0) {
5683
5678
  seedRef.value = eager;
5684
5679
  applyGateSeed(eager);
5685
5680
  }
@@ -5752,10 +5747,13 @@ function useWizard(options) {
5752
5747
  const statuses = buildWizardStatusesProxy(statusesRecord);
5753
5748
  const progressOverride = options.progress;
5754
5749
  const progress = computed(() => {
5750
+ const list = compiledSteps.value;
5755
5751
  if (progressOverride !== void 0) {
5756
- return progressOverride(compiledSteps.value);
5752
+ const override = callConsumerFn(
5753
+ () => progressOverride(list),
5754
+ null);
5755
+ if (override !== null) return override;
5757
5756
  }
5758
- const list = compiledSteps.value;
5759
5757
  if (list.length === 0) return 0;
5760
5758
  let valid = 0;
5761
5759
  for (const step of list) {
@@ -5783,11 +5781,15 @@ function useWizard(options) {
5783
5781
  historyHandle.subscribe((value) => {
5784
5782
  urlMirror.value = value;
5785
5783
  });
5786
- const restoreCallback = options.restore === false ? void 0 : options.restore !== void 0 ? options.restore : () => {
5784
+ const consumerRestore = options.restore;
5785
+ const restoreCallback = consumerRestore === false ? void 0 : consumerRestore !== void 0 ? () => callConsumerFn(() => consumerRestore(), void 0) : () => {
5787
5786
  const value = urlMirror.value;
5788
5787
  return value === void 0 ? void 0 : { step: value };
5789
5788
  };
5790
- const persistCallback = options.persist === false ? void 0 : options.persist !== void 0 ? options.persist : (state) => {
5789
+ const consumerPersist = options.persist;
5790
+ const persistCallback = consumerPersist === false ? void 0 : consumerPersist !== void 0 ? (state) => {
5791
+ callConsumerFn(() => consumerPersist(state), void 0);
5792
+ } : (state) => {
5791
5793
  if (state.step === void 0) return;
5792
5794
  const current = historyHandle.read();
5793
5795
  const effectiveCurrent = current !== void 0 && isCompiledKey(current) ? current : firstKey();