attaform 0.19.0 → 0.20.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 (110) hide show
  1. package/dist/chunks/devtools.cjs +1 -1
  2. package/dist/chunks/devtools.mjs +1 -1
  3. package/dist/chunks/indexeddb.cjs +1 -1
  4. package/dist/chunks/indexeddb.mjs +1 -1
  5. package/dist/chunks/local-storage.cjs +1 -1
  6. package/dist/chunks/local-storage.mjs +1 -1
  7. package/dist/chunks/session-storage.cjs +1 -1
  8. package/dist/chunks/session-storage.mjs +1 -1
  9. package/dist/index.cjs +3 -6
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +14 -40
  12. package/dist/index.d.mts +14 -40
  13. package/dist/index.d.ts +14 -40
  14. package/dist/index.mjs +5 -5
  15. package/dist/nuxt.d.cts +1 -1
  16. package/dist/nuxt.d.mts +1 -1
  17. package/dist/nuxt.d.ts +1 -1
  18. package/dist/runtime/components/AttaformDevtoolsPanel.vue +2 -2
  19. package/dist/runtime/components/DevtoolsValueTree.d.vue.ts +1 -3
  20. package/dist/runtime/components/DevtoolsValueTree.vue.d.ts +1 -3
  21. package/dist/runtime/plugins/attaform.cjs +2 -2
  22. package/dist/runtime/plugins/attaform.mjs +2 -2
  23. package/dist/shared/{attaform.CrpjyXdO.mjs → attaform.BKozEdTr.mjs} +275 -266
  24. package/dist/shared/attaform.BKozEdTr.mjs.map +1 -0
  25. package/dist/shared/{attaform.Bubm_slq.cjs → attaform.BM6YD9kZ.cjs} +212 -269
  26. package/dist/shared/attaform.BM6YD9kZ.cjs.map +1 -0
  27. package/dist/shared/{attaform.CoxJ8Qm8.cjs → attaform.BPxsYtTe.cjs} +2 -26
  28. package/dist/shared/attaform.BPxsYtTe.cjs.map +1 -0
  29. package/dist/shared/{attaform.BqEfHpVB.cjs → attaform.BPy-4qRx.cjs} +275 -268
  30. package/dist/shared/attaform.BPy-4qRx.cjs.map +1 -0
  31. package/dist/shared/attaform.BWgAFnsj.mjs +770 -0
  32. package/dist/shared/attaform.BWgAFnsj.mjs.map +1 -0
  33. package/dist/shared/{attaform.BTpuvGec.d.ts → attaform.Bh3ACtts.d.ts} +109 -101
  34. package/dist/shared/{attaform.CXpzmj38.mjs → attaform.BupwXkj_.mjs} +213 -270
  35. package/dist/shared/attaform.BupwXkj_.mjs.map +1 -0
  36. package/dist/shared/{attaform.JBx8cfMA.cjs → attaform.CIn4bMsD.cjs} +263 -799
  37. package/dist/shared/attaform.CIn4bMsD.cjs.map +1 -0
  38. package/dist/shared/{attaform.BTi-PsHr.mjs → attaform.CKFbKFb6.mjs} +1818 -1472
  39. package/dist/shared/attaform.CKFbKFb6.mjs.map +1 -0
  40. package/dist/shared/{attaform.ePUcKxId.d.cts → attaform.D5-1XGQU.d.cts} +109 -101
  41. package/dist/shared/{attaform.a3uBo-gw.mjs → attaform.DEBvCjeH.mjs} +257 -793
  42. package/dist/shared/attaform.DEBvCjeH.mjs.map +1 -0
  43. package/dist/shared/{attaform.C1msmO2v.cjs → attaform.DL4CQ-oW.cjs} +1823 -1477
  44. package/dist/shared/attaform.DL4CQ-oW.cjs.map +1 -0
  45. package/dist/shared/{attaform.D4I63aBV.d.ts → attaform.DSD85fHb.d.cts} +1 -19
  46. package/dist/shared/{attaform.CBjmobqk.d.cts → attaform.DSD85fHb.d.mts} +1 -19
  47. package/dist/shared/{attaform.DXYHL99q.d.mts → attaform.DSD85fHb.d.ts} +1 -19
  48. package/dist/shared/{attaform.B7rzpK1U.d.cts → attaform.DkA5J8NW.d.cts} +1 -17
  49. package/dist/shared/{attaform.B7rzpK1U.d.mts → attaform.DkA5J8NW.d.mts} +1 -17
  50. package/dist/shared/{attaform.B7rzpK1U.d.ts → attaform.DkA5J8NW.d.ts} +1 -17
  51. package/dist/shared/{attaform.CJ-e9gYI.d.ts → attaform.Dl5kDY-A.d.ts} +1 -1
  52. package/dist/shared/attaform.Dmb6itxC.cjs +781 -0
  53. package/dist/shared/attaform.Dmb6itxC.cjs.map +1 -0
  54. package/dist/shared/{attaform.CRNA0vrd.d.mts → attaform.DoKXru-a.d.mts} +1 -1
  55. package/dist/shared/attaform.DvA-CJJW.mjs +1876 -0
  56. package/dist/shared/attaform.DvA-CJJW.mjs.map +1 -0
  57. package/dist/shared/{attaform.BtBmfLQN.d.mts → attaform.EMzJcQci.d.mts} +109 -101
  58. package/dist/shared/attaform.EZG6fOFb.mjs +35 -0
  59. package/dist/shared/attaform.EZG6fOFb.mjs.map +1 -0
  60. package/dist/shared/{attaform.QvygsFGh.d.cts → attaform.GbDo_lJi.d.cts} +1 -1
  61. package/dist/shared/{attaform.C0uGZQ4M.d.ts → attaform.SfhU0OEY.d.cts} +134 -30
  62. package/dist/shared/{attaform.C0uGZQ4M.d.cts → attaform.SfhU0OEY.d.mts} +134 -30
  63. package/dist/shared/{attaform.C0uGZQ4M.d.mts → attaform.SfhU0OEY.d.ts} +134 -30
  64. package/dist/shared/attaform.jgzuNZVC.cjs +1882 -0
  65. package/dist/shared/attaform.jgzuNZVC.cjs.map +1 -0
  66. package/dist/transforms.cjs +2 -2
  67. package/dist/transforms.d.cts +22 -13
  68. package/dist/transforms.d.mts +22 -13
  69. package/dist/transforms.d.ts +22 -13
  70. package/dist/transforms.mjs +1 -1
  71. package/dist/vite.cjs +8 -7
  72. package/dist/vite.cjs.map +1 -1
  73. package/dist/vite.mjs +8 -7
  74. package/dist/vite.mjs.map +1 -1
  75. package/dist/zod-v3.cjs +3 -3
  76. package/dist/zod-v3.d.cts +32 -6
  77. package/dist/zod-v3.d.mts +32 -6
  78. package/dist/zod-v3.d.ts +32 -6
  79. package/dist/zod-v3.mjs +3 -3
  80. package/dist/zod-v4.cjs +3 -3
  81. package/dist/zod-v4.d.cts +12 -8
  82. package/dist/zod-v4.d.mts +12 -8
  83. package/dist/zod-v4.d.ts +12 -8
  84. package/dist/zod-v4.mjs +3 -3
  85. package/dist/zod.cjs +8 -8
  86. package/dist/zod.cjs.map +1 -1
  87. package/dist/zod.d.cts +6 -6
  88. package/dist/zod.d.mts +6 -6
  89. package/dist/zod.d.ts +6 -6
  90. package/dist/zod.mjs +6 -6
  91. package/package.json +1 -1
  92. package/dist/shared/attaform.BTi-PsHr.mjs.map +0 -1
  93. package/dist/shared/attaform.BqEfHpVB.cjs.map +0 -1
  94. package/dist/shared/attaform.Bubm_slq.cjs.map +0 -1
  95. package/dist/shared/attaform.C1msmO2v.cjs.map +0 -1
  96. package/dist/shared/attaform.C8CyvYa_.cjs +0 -36
  97. package/dist/shared/attaform.C8CyvYa_.cjs.map +0 -1
  98. package/dist/shared/attaform.CXpzmj38.mjs.map +0 -1
  99. package/dist/shared/attaform.Cghpuav8.mjs +0 -57
  100. package/dist/shared/attaform.Cghpuav8.mjs.map +0 -1
  101. package/dist/shared/attaform.CiMqJHDm.mjs +0 -1594
  102. package/dist/shared/attaform.CiMqJHDm.mjs.map +0 -1
  103. package/dist/shared/attaform.CoxJ8Qm8.cjs.map +0 -1
  104. package/dist/shared/attaform.CrpjyXdO.mjs.map +0 -1
  105. package/dist/shared/attaform.D13GMFgK.mjs +0 -32
  106. package/dist/shared/attaform.D13GMFgK.mjs.map +0 -1
  107. package/dist/shared/attaform.JBx8cfMA.cjs.map +0 -1
  108. package/dist/shared/attaform.OznWyOPy.cjs +0 -1600
  109. package/dist/shared/attaform.OznWyOPy.cjs.map +0 -1
  110. package/dist/shared/attaform.a3uBo-gw.mjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { t as FormKey, ae as SlimPrimitiveKind, C as CoercionEntry, g as CoercionRegistry, G as GenericForm, T as PathKey, S as Path, am as ValidationError, A as AbstractSchema, x as GetDisplayState, aq as WriteMeta, D as DeepPartial, ar as WriteShape, ak as ValidateOn, Y as PersistOptInRegistry, aa as Segment, f as AttaformDefaults, aj as UseFormReturnType, a8 as RegisterValue } from './attaform.C0uGZQ4M.mjs';
1
+ import { t as FormKey, ae as SlimPrimitiveKind, C as CoercionEntry, g as CoercionRegistry, G as GenericForm, T as PathKey, S as Path, am as ValidationError, A as AbstractSchema, x as GetDisplayState, aq as WriteMeta, D as DeepPartial, ar as WriteShape, ak as ValidateOn, Y as PersistOptInRegistry, f as AttaformDefaults, aj as UseFormReturnType, a8 as RegisterValue } from './attaform.SfhU0OEY.mjs';
2
2
  import { Ref, ComputedRef, App, InjectionKey } from 'vue';
3
3
 
4
4
  /**
@@ -61,7 +61,7 @@ type FormStatus = {
61
61
  * entry carries the formKey + path tuple so consumers can route to the
62
62
  * offending field from a wizard-wide error summary.
63
63
  */
64
- type AggregateError = {
64
+ type WizardAggregateError = {
65
65
  readonly formKey: FormKey;
66
66
  readonly path: ReadonlyArray<string | number>;
67
67
  readonly message: string;
@@ -213,7 +213,7 @@ type WizardOnSubmit = (ctx: WizardSubmitContext) => void | Promise<void>;
213
213
  * validation and activation failures (`atta:activation-failed`). Sync
214
214
  * or async; the returned promise gates `wizard.submitting`.
215
215
  */
216
- type WizardOnError = (errors: readonly AggregateError[]) => void | Promise<void>;
216
+ type WizardOnError = (errors: readonly WizardAggregateError[]) => void | Promise<void>;
217
217
  /**
218
218
  * Options for `useWizard({ steps, … })`. `steps` is the only required
219
219
  * field; the rest are optional and default sensibly for the common
@@ -439,7 +439,7 @@ type UseWizardReturnType<S extends ReadonlyArray<StepSlot> = ReadonlyArray<StepS
439
439
  readonly count: number;
440
440
  readonly statuses: WizardStatusesProxy<Record<string, FormStatus>>;
441
441
  readonly allValues: Readonly<Record<FormKey, unknown>>;
442
- readonly allErrors: Readonly<Record<FormKey, readonly AggregateError[]>>;
442
+ readonly allErrors: Readonly<Record<FormKey, readonly WizardAggregateError[]>>;
443
443
  readonly progress: number;
444
444
  readonly canAdvance: boolean;
445
445
  readonly canGoBack: boolean;
@@ -701,6 +701,15 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
701
701
  * plain-value forms. Read by `form.rehydrate()`.
702
702
  */
703
703
  readonly defaultValuesFactory: Ref<(() => unknown | Promise<unknown>) | undefined>;
704
+ /**
705
+ * `true` when this store carries an SSR prefetch queue (server path
706
+ * where `state.activate()` must enqueue intent before deciding
707
+ * whether to fire). The flag lets `buildFormApi` skip the lazy
708
+ * activation gate for forms with no factory AND no SSR prefetch —
709
+ * the common client-side case where `gated()` is otherwise pure
710
+ * reactive overhead on every public method call.
711
+ */
712
+ readonly hasSsrPrefetch: boolean;
704
713
  /**
705
714
  * `true` once the form's effective defaults have been applied —
706
715
  * sync `defaultValues` at construction, or async factory whose
@@ -797,6 +806,14 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
797
806
  * answer.
798
807
  */
799
808
  pathHasAsyncValidation(path: Path): boolean;
809
+ /**
810
+ * Precomputed-key shortcut for `pathHasAsyncValidation`. The
811
+ * canonical key is required and must correspond to `segments`; the
812
+ * helper skips the `canonicalizePath` round-trip so descendant-walk
813
+ * loops (whose Map iteration already yields the canonical key) can
814
+ * read the async-gate verdict without a per-leaf canonicalize.
815
+ */
816
+ pathHasAsyncValidationByKey(key: PathKey, segments: Path): boolean;
800
817
  /**
801
818
  * Per-path counter of in-flight field-level validation runs.
802
819
  * `field.validating` on `FieldState` mirrors
@@ -852,14 +869,6 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
852
869
  arrayElementKey(path: Path): string;
853
870
  reset(nextDefaultValues?: DeepPartial<WriteShape<F>>): void;
854
871
  resetField(path: Path): void;
855
- /**
856
- * Wipe `path` (or the whole form when `path === ''`) to the
857
- * schema's "appropriate nullish value" — the underlying type's
858
- * empty/falsy concrete, with `.default()` / `.catch()` wrappers
859
- * INTENTIONALLY skipped. Sugar for
860
- * `setValueAtPath(path, schema.getEmptyValueAtPath(path))`.
861
- */
862
- clear(path: Path): boolean;
863
872
  setSchemaErrorsForPath(path: Path, errors: ValidationError[]): void;
864
873
  setAllSchemaErrors(errors: readonly ValidationError[]): void;
865
874
  clearSchemaErrors(path?: Path): void;
@@ -912,7 +921,6 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
912
921
  markFocused(path: Path, focused: boolean, meta?: {
913
922
  readonly instance?: WriteMeta['instance'];
914
923
  }): void;
915
- markTouched(path: Path): void;
916
924
  /**
917
925
  * Flip `interacted: true` on a leaf — the sticky value-mutation flag.
918
926
  * Driven by the directive's input listeners (via the RegisterValue's
@@ -945,6 +953,14 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
945
953
  * isn't exposed to consumers.
946
954
  */
947
955
  isPristineAtPath(path: Path): boolean;
956
+ /**
957
+ * Precomputed-key shortcut for `isPristineAtPath`. The canonical
958
+ * key is required and must correspond to `segments`; the helper
959
+ * skips the `canonicalizePath` round-trip so descendant-walk loops
960
+ * (whose Map iteration already yields the canonical key) can read
961
+ * the pristine verdict without a per-leaf canonicalize.
962
+ */
963
+ isPristineAtPathByKey(key: PathKey, segments: Path): boolean;
948
964
  /**
949
965
  * Whether any tracked array under `path` has changed shape — a reorder,
950
966
  * insert, or removal — relative to its construction/reset baseline. The
@@ -1086,7 +1102,7 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
1086
1102
  * Resolved sensitive-path predicate for THIS form. Honors the
1087
1103
  * cascade (`useForm({ sensitiveNames })` > global default >
1088
1104
  * library `DEFAULT_SENSITIVE_NAMES`). Used by:
1089
- * - persistence enforcement (`enforceSensitiveCheck` at write time);
1105
+ * - the persistence opt-in gate (`allowSensitivePersist`);
1090
1106
  * - the multi-tab sync module (outbound strip + inbound reject);
1091
1107
  * - DevTools edit rejection;
1092
1108
  * - any future surface that needs to flag "this path holds
@@ -1097,14 +1113,6 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
1097
1113
  * resolved-config surface.
1098
1114
  */
1099
1115
  readonly isSensitivePath: (path: Path | PathKey | string) => boolean;
1100
- /**
1101
- * Single-segment variant of `isSensitivePath`. Used by the DevTools
1102
- * redact walk to short-circuit whole subtrees the moment any
1103
- * ancestor segment matches — saving an O(leaves × ancestors) regex
1104
- * sweep per timeline event. Resolved from the same `sensitiveNames`
1105
- * cascade as `isSensitivePath`.
1106
- */
1107
- readonly segmentMatchesSensitive: (segment: Segment) => boolean;
1108
1116
  /**
1109
1117
  * Canonical path keys explicitly opted OUT of multi-tab sync by
1110
1118
  * `register(path, { multiTab: false })`. The sync module's outbound
@@ -1428,6 +1436,83 @@ type InjectFormInput = {
1428
1436
  */
1429
1437
  declare function injectForm<Form extends GenericForm, GetValueFormType extends GenericForm = Form>(input?: FormKey | InjectFormInput): UseFormReturnType<Form, GetValueFormType> | null;
1430
1438
 
1439
+ /**
1440
+ * Re-bind a parent's `v-register` onto an inner native element. Use
1441
+ * inside a component that wraps a single form field whose root is
1442
+ * NOT the input itself (e.g. a labelled-row that renders `<label>`
1443
+ * around the input).
1444
+ *
1445
+ * ```vue
1446
+ * <!-- Parent -->
1447
+ * <MyInput v-register="form.register('email')" />
1448
+ *
1449
+ * <!-- MyInput.vue -->
1450
+ * <script setup lang="ts">
1451
+ * import { useRegister } from 'attaform'
1452
+ * const rv = useRegister()
1453
+ * // rv.path / rv.segments / rv.formKey / rv.formInstanceId / rv.innerRef
1454
+ * // are all reachable directly — no `.value` unwrap.
1455
+ * </script>
1456
+ *
1457
+ * <template>
1458
+ * <label class="field">
1459
+ * <span>Email</span>
1460
+ * <input v-register="rv" />
1461
+ * </label>
1462
+ * </template>
1463
+ * ```
1464
+ *
1465
+ * Returns a hybrid Proxy: it answers `__v_isRef` / `.value` like a
1466
+ * Vue `Ref<RegisterValue | undefined>` (so templates auto-unwrap
1467
+ * correctly and `v-register="rv"` feeds the underlying RV to the
1468
+ * directive — preserving the directive's path-migration diff across
1469
+ * renders), AND every other property read pierces to the captured
1470
+ * RV's field (so `rv.path` works directly in script setup). Reads
1471
+ * inside reactive scopes (`computed` / `watchEffect`) track the
1472
+ * underlying `shallowRef`, so `rv.path` re-runs when the parent
1473
+ * rebinds to a different path.
1474
+ *
1475
+ * Unbound state: when the parent didn't pass `v-register`, every
1476
+ * piercing read returns `undefined` at runtime, and the return type
1477
+ * surfaces this honestly as `UseRegisterReturn<V> | undefined`.
1478
+ * Consumers defend with optional chaining (`rv?.formKey`,
1479
+ * `rv?.segments`); the directive accepts `undefined` peacefully (its
1480
+ * binding value type is already `RegisterValue<V> | undefined`), so
1481
+ * `v-register="rv"` works whether or not a parent has bound. The
1482
+ * composable's `onMounted` warn fires once per instance to surface
1483
+ * the misuse case at runtime.
1484
+ *
1485
+ * Diagnostic: in dev mode, a single `console.warn` fires per instance
1486
+ * at `onMounted` if the captured value is still `undefined` — by then
1487
+ * the parent has had its full mount lifecycle to bind, so a missing
1488
+ * binding is conclusive misuse. The warn does NOT fire on every read
1489
+ * of the proxy, and is intentionally silent under SSR
1490
+ * (`renderToString` skips `onMounted`); the CSR hydration pass
1491
+ * surfaces the same diagnostic without double-counting through Nuxt's
1492
+ * `dev:ssr-logs` channel.
1493
+ *
1494
+ * When the wrapper's root IS the input itself, Vue's attribute
1495
+ * fallthrough handles the binding and `useRegister` is unnecessary.
1496
+ * For wrappers that bind multiple fields (compound forms), use
1497
+ * `injectForm<Form>(key?)` and call `ctx.register(...)` directly.
1498
+ */
1499
+
1500
+ /**
1501
+ * Return type of `useRegister()`. Hybrid of `RegisterValue<V>` (so
1502
+ * `rv.path` / `rv.segments` / `rv.formKey` etc. work directly in
1503
+ * script setup) and `Ref<RegisterValue<V> | undefined>` (so Vue's
1504
+ * template auto-unwrap surfaces the underlying RV to `v-register`
1505
+ * and the directive's path-migration diff sees the real RV across
1506
+ * renders).
1507
+ *
1508
+ * The two surfaces don't clash at the type level: `RegisterValue`
1509
+ * doesn't carry a `value` field, and `Ref<T>`'s `value: T` becomes
1510
+ * the hybrid's only `.value`. Older code that read `rv.value?.path`
1511
+ * keeps working; new code can write `rv.path` directly.
1512
+ */
1513
+ type UseRegisterReturn<V = unknown> = RegisterValue<V> & Ref<RegisterValue<V> | undefined>;
1514
+ declare function useRegister<V = unknown>(): UseRegisterReturn<V> | undefined;
1515
+
1431
1516
  /**
1432
1517
  * Multistep-form orchestrator built around an ordered list of step slots.
1433
1518
  * Each slot resolves to a participating form: an existing `useForm`
@@ -1543,83 +1628,6 @@ declare function injectWizard(input?: string | InjectWizardInput): UseWizardRetu
1543
1628
  */
1544
1629
  declare function lazy<Ctx = WizardCtx>(resolve: (ctx: Ctx) => AnyForm | string | undefined): LazyMarker<Ctx>;
1545
1630
 
1546
- /**
1547
- * Re-bind a parent's `v-register` onto an inner native element. Use
1548
- * inside a component that wraps a single form field whose root is
1549
- * NOT the input itself (e.g. a labelled-row that renders `<label>`
1550
- * around the input).
1551
- *
1552
- * ```vue
1553
- * <!-- Parent -->
1554
- * <MyInput v-register="form.register('email')" />
1555
- *
1556
- * <!-- MyInput.vue -->
1557
- * <script setup lang="ts">
1558
- * import { useRegister } from 'attaform'
1559
- * const rv = useRegister()
1560
- * // rv.path / rv.segments / rv.formKey / rv.formInstanceId / rv.innerRef
1561
- * // are all reachable directly — no `.value` unwrap.
1562
- * </script>
1563
- *
1564
- * <template>
1565
- * <label class="field">
1566
- * <span>Email</span>
1567
- * <input v-register="rv" />
1568
- * </label>
1569
- * </template>
1570
- * ```
1571
- *
1572
- * Returns a hybrid Proxy: it answers `__v_isRef` / `.value` like a
1573
- * Vue `Ref<RegisterValue | undefined>` (so templates auto-unwrap
1574
- * correctly and `v-register="rv"` feeds the underlying RV to the
1575
- * directive — preserving the directive's path-migration diff across
1576
- * renders), AND every other property read pierces to the captured
1577
- * RV's field (so `rv.path` works directly in script setup). Reads
1578
- * inside reactive scopes (`computed` / `watchEffect`) track the
1579
- * underlying `shallowRef`, so `rv.path` re-runs when the parent
1580
- * rebinds to a different path.
1581
- *
1582
- * Unbound state: when the parent didn't pass `v-register`, every
1583
- * piercing read returns `undefined` at runtime, and the return type
1584
- * surfaces this honestly as `UseRegisterReturn<V> | undefined`.
1585
- * Consumers defend with optional chaining (`rv?.formKey`,
1586
- * `rv?.segments`); the directive accepts `undefined` peacefully (its
1587
- * binding value type is already `RegisterValue<V> | undefined`), so
1588
- * `v-register="rv"` works whether or not a parent has bound. The
1589
- * composable's `onMounted` warn fires once per instance to surface
1590
- * the misuse case at runtime.
1591
- *
1592
- * Diagnostic: in dev mode, a single `console.warn` fires per instance
1593
- * at `onMounted` if the captured value is still `undefined` — by then
1594
- * the parent has had its full mount lifecycle to bind, so a missing
1595
- * binding is conclusive misuse. The warn does NOT fire on every read
1596
- * of the proxy, and is intentionally silent under SSR
1597
- * (`renderToString` skips `onMounted`); the CSR hydration pass
1598
- * surfaces the same diagnostic without double-counting through Nuxt's
1599
- * `dev:ssr-logs` channel.
1600
- *
1601
- * When the wrapper's root IS the input itself, Vue's attribute
1602
- * fallthrough handles the binding and `useRegister` is unnecessary.
1603
- * For wrappers that bind multiple fields (compound forms), use
1604
- * `injectForm<Form>(key?)` and call `ctx.register(...)` directly.
1605
- */
1606
-
1607
- /**
1608
- * Return type of `useRegister()`. Hybrid of `RegisterValue<V>` (so
1609
- * `rv.path` / `rv.segments` / `rv.formKey` etc. work directly in
1610
- * script setup) and `Ref<RegisterValue<V> | undefined>` (so Vue's
1611
- * template auto-unwrap surfaces the underlying RV to `v-register`
1612
- * and the directive's path-migration diff sees the real RV across
1613
- * renders).
1614
- *
1615
- * The two surfaces don't clash at the type level: `RegisterValue`
1616
- * doesn't carry a `value` field, and `Ref<T>`'s `value: T` becomes
1617
- * the hybrid's only `.value`. Older code that read `rv.value?.path`
1618
- * keeps working; new code can write `rv.path` directly.
1619
- */
1620
- type UseRegisterReturn<V = unknown> = RegisterValue<V> & Ref<RegisterValue<V> | undefined>;
1621
- declare function useRegister<V = unknown>(): UseRegisterReturn<V> | undefined;
1622
-
1623
1631
  /**
1624
1632
  * Stable identifiers for library-emitted `ValidationError` codes.
1625
1633
  *
@@ -1680,5 +1688,5 @@ declare const AttaformErrorCode: {
1680
1688
  };
1681
1689
  type AttaformErrorCode = (typeof AttaformErrorCode)[keyof typeof AttaformErrorCode];
1682
1690
 
1683
- export { AttaformErrorCode as b, createRegistry as p, defaultCoercionRules as q, defineCoercion as r, getRegistryFromApp as s, injectForm as t, injectWizard as u, kAttaformRegistry as v, lazy as w, useRegister as x, useRegistry as y, useWizard as z };
1684
- export type { AggregateError as A, CompiledStep as C, FormStatus as F, InjectWizardInput as I, LazyMarker as L, SSRDetectOptions as S, UseRegisterReturn as U, WizardCtx as W, AnyForm as a, AttaformRegistry as c, SerializedFormData as d, StepSlot as e, UseWizardReturnType as f, WizardCtxForm as g, WizardOnError as h, WizardOnSubmit as i, WizardOptions as j, WizardPersistFn as k, WizardRestoreFn as l, WizardRestoreState as m, WizardStatusesProxy as n, WizardSubmitContext as o };
1691
+ export { AttaformErrorCode as a, createRegistry as p, defaultCoercionRules as q, defineCoercion as r, getRegistryFromApp as s, injectForm as t, injectWizard as u, kAttaformRegistry as v, lazy as w, useRegister as x, useRegistry as y, useWizard as z };
1692
+ export type { AnyForm as A, CompiledStep as C, FormStatus as F, InjectWizardInput as I, LazyMarker as L, SSRDetectOptions as S, UseRegisterReturn as U, WizardAggregateError as W, AttaformRegistry as b, SerializedFormData as c, StepSlot as d, UseWizardReturnType as e, WizardCtx as f, WizardCtxForm as g, WizardOnError as h, WizardOnSubmit as i, WizardOptions as j, WizardPersistFn as k, WizardRestoreFn as l, WizardRestoreState as m, WizardStatusesProxy as n, WizardSubmitContext as o };
@@ -0,0 +1,35 @@
1
+ import { r as getRegistryFromApp, C as pathKeyToDotted } from './attaform.BKozEdTr.mjs';
2
+
3
+ function renderAttaformState(app) {
4
+ const registry = getRegistryFromApp(app);
5
+ const forms = [];
6
+ for (const [key, state] of registry.forms) {
7
+ const transientList = [];
8
+ for (const pk of state.blankPaths) {
9
+ const d = pathKeyToDotted(pk);
10
+ if (d !== null) transientList.push(d);
11
+ }
12
+ forms.push([
13
+ key,
14
+ {
15
+ form: state.form.value,
16
+ schemaErrors: Array.from(state.schemaErrors.entries()),
17
+ userErrors: Array.from(state.userErrors.entries()),
18
+ fields: Array.from(state.fields.entries()),
19
+ ...transientList.length > 0 ? { blankPaths: transientList } : {}
20
+ }
21
+ ]);
22
+ }
23
+ return { forms };
24
+ }
25
+ function hydrateAttaformState(app, payload) {
26
+ const registry = getRegistryFromApp(app);
27
+ for (const [key, data] of payload.forms) {
28
+ registry.pendingHydration.set(key, data);
29
+ }
30
+ }
31
+
32
+ const DEVTOOLS_WINDOW_KEY = "__attaform_devtools__";
33
+
34
+ export { DEVTOOLS_WINDOW_KEY as D, hydrateAttaformState as h, renderAttaformState as r };
35
+ //# sourceMappingURL=attaform.EZG6fOFb.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attaform.EZG6fOFb.mjs","sources":["../../src/runtime/core/serialize.ts","../../src/runtime/core/devtools-shared.ts"],"sourcesContent":["import type { App } from 'vue'\nimport type { FormKey } from '../types/types-api'\nimport { pathKeyToDotted, type PathKey } from './paths'\nimport { getRegistryFromApp, type SerializedFormData } from './registry'\n\n/**\n * Serialised snapshot of every form in a Vue app, produced by\n * `renderAttaformState` and consumed by `hydrateAttaformState`.\n *\n * JSON-safe — pass to `JSON.stringify`, `devalue`, or any other\n * serialiser before embedding in your SSR payload.\n */\nexport type SerializedAttaformState = {\n /** Tuples of `[formKey, snapshot]` for every form in the app. */\n readonly forms: ReadonlyArray<readonly [FormKey, SerializedFormData]>\n}\n\n/**\n * Snapshot every form on a Vue app for SSR. Call from your server\n * entry after rendering the app:\n *\n * ```ts\n * import { renderToString } from '@vue/server-renderer'\n * import { renderAttaformState, escapeForInlineScript } from 'attaform'\n *\n * const html = await renderToString(app)\n * const state = renderAttaformState(app)\n * const payload = escapeForInlineScript(JSON.stringify(state))\n *\n * return `\n * ${html}\n * <script>window.__ATTAFORM_STATE__ = ${payload}</script>\n * `\n * ```\n *\n * Pair with `hydrateAttaformState` on the client to restore the\n * forms in their server-rendered state. Nuxt users don't need this —\n * `attaform/nuxt` wires SSR automatically.\n */\nexport function renderAttaformState(app: App): SerializedAttaformState {\n const registry = getRegistryFromApp(app)\n const forms: Array<readonly [FormKey, SerializedFormData]> = []\n for (const [key, state] of registry.forms) {\n // Skip the blank field when the set is empty so the\n // wire payload stays minimal for forms that don't use it. The\n // optional shape on the consuming side handles the absence\n // cleanly (defaults to \"no blank paths\"). PathKey → dotted at\n // the boundary so the wire shape matches the rest of the\n // public path notation.\n const transientList: string[] = []\n for (const pk of state.blankPaths) {\n const d = pathKeyToDotted(pk as PathKey)\n if (d !== null) transientList.push(d)\n }\n forms.push([\n key,\n {\n form: state.form.value,\n schemaErrors: Array.from(state.schemaErrors.entries()),\n userErrors: Array.from(state.userErrors.entries()),\n fields: Array.from(state.fields.entries()),\n ...(transientList.length > 0 ? { blankPaths: transientList } : {}),\n },\n ])\n }\n return { forms }\n}\n\n/**\n * Restore forms from a server-rendered snapshot on the client. Call\n * from your client entry before mounting:\n *\n * ```ts\n * import { createApp } from 'vue'\n * import { createAttaform, hydrateAttaformState } from 'attaform'\n *\n * const app = createApp(App).use(createAttaform())\n * hydrateAttaformState(app, window.__ATTAFORM_STATE__)\n * app.mount('#app')\n * ```\n *\n * The next `useForm({ key })` call for each serialised form picks up\n * the snapshot transparently — no further action is required.\n */\nexport function hydrateAttaformState(app: App, payload: SerializedAttaformState): void {\n const registry = getRegistryFromApp(app)\n for (const [key, data] of payload.forms) {\n registry.pendingHydration.set(key, data)\n }\n}\n","/**\n * Shared building blocks for Attaform's two devtools surfaces — the Vue\n * DevTools (Chrome-extension) inspector wired up in `./devtools.ts`, and\n * the Nuxt DevTools (overlay) panel wired up via `../../nuxt.ts` +\n * `../pages/_attaform_devtools.vue`.\n *\n * Houses the window-bridge contract both surfaces consume so a new\n * bridge field lands in one file. Both surfaces render RAW form values\n * by design — DevTools is a dev-only surface, and redaction across every\n * place a value surfaces is impractical security theater rather than a\n * real safeguard.\n */\nimport type { AttaformRegistry } from './registry'\n\n/**\n * Property key on `window` that the Nuxt-side dev plugin attaches the\n * bridge object to. The iframe-mounted overlay panel reads\n * `window.parent[DEVTOOLS_WINDOW_KEY]` to reach the host app's registry.\n *\n * Underscored + namespaced to make accidental collision with consumer\n * globals vanishingly unlikely. Stable across versions — bumping it\n * would silently disconnect older library builds from newer overlay\n * panels in the same browser tab during a library upgrade.\n */\nexport const DEVTOOLS_WINDOW_KEY = '__attaform_devtools__'\n\n/**\n * Shape of the object the host plugin attaches to `window` in dev mode.\n * The iframe overlay panel reads this to discover the live registry and\n * render its forms.\n *\n * Single-registry assumption: the latest `createAttaform()` install\n * wins. Multi-app pages (rare; typically only seen in micro-frontend\n * setups) will only see one app's forms in the panel. Documented but\n * not actively supported — the alternative (a Set of registries with\n * union-rendering) is a future call if a real consumer hits it.\n */\nexport interface AttaformDevtoolsBridge {\n registry: AttaformRegistry\n /**\n * The library version, surfaced in the panel's footer for support /\n * bug-report context. Read from `package.json` at host-plugin init.\n */\n version: string\n}\n\ndeclare global {\n interface Window {\n [DEVTOOLS_WINDOW_KEY]?: AttaformDevtoolsBridge\n }\n}\n"],"names":[],"mappings":";;AAuCO,SAAS,oBAAoB,GAAA,EAAmC;AACrE,EAAA,MAAM,QAAA,GAAW,mBAAmB,GAAG,CAAA;AACvC,EAAA,MAAM,QAAuD,EAAC;AAC9D,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,SAAS,KAAA,EAAO;AAOzC,IAAA,MAAM,gBAA0B,EAAC;AACjC,IAAA,KAAA,MAAW,EAAA,IAAM,MAAM,UAAA,EAAY;AACjC,MAAA,MAAM,CAAA,GAAI,gBAAgB,EAAa,CAAA;AACvC,MAAA,IAAI,CAAA,KAAM,IAAA,EAAM,aAAA,CAAc,IAAA,CAAK,CAAC,CAAA;AAAA,IACtC;AACA,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,GAAA;AAAA,MACA;AAAA,QACE,IAAA,EAAM,MAAM,IAAA,CAAK,KAAA;AAAA,QACjB,cAAc,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,YAAA,CAAa,SAAS,CAAA;AAAA,QACrD,YAAY,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,UAAA,CAAW,SAAS,CAAA;AAAA,QACjD,QAAQ,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,SAAS,CAAA;AAAA,QACzC,GAAI,cAAc,MAAA,GAAS,CAAA,GAAI,EAAE,UAAA,EAAY,aAAA,KAAkB;AAAC;AAClE,KACD,CAAA;AAAA,EACH;AACA,EAAA,OAAO,EAAE,KAAA,EAAM;AACjB;AAkBO,SAAS,oBAAA,CAAqB,KAAU,OAAA,EAAwC;AACrF,EAAA,MAAM,QAAA,GAAW,mBAAmB,GAAG,CAAA;AACvC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,IAAI,CAAA,IAAK,QAAQ,KAAA,EAAO;AACvC,IAAA,QAAA,CAAS,gBAAA,CAAiB,GAAA,CAAI,GAAA,EAAK,IAAI,CAAA;AAAA,EACzC;AACF;;ACjEO,MAAM,mBAAA,GAAsB;;;;"}
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { G as GenericForm, q as FlatPath, B as NestedType, t as FormKey, ai as UseFormConfiguration, A as AbstractSchema, j as DefaultValuesInput, al as ValidateOnConfig, aj as UseFormReturnType } from './attaform.C0uGZQ4M.cjs';
2
+ import { G as GenericForm, q as FlatPath, B as NestedType, t as FormKey, ai as UseFormConfiguration, A as AbstractSchema, j as DefaultValuesInput, al as ValidateOnConfig, aj as UseFormReturnType } from './attaform.SfhU0OEY.cjs';
3
3
 
4
4
  /**
5
5
  * The shape `form.values.<key>` returns at runtime.
@@ -136,6 +136,28 @@ declare function isUnset(value: unknown): value is Unset;
136
136
  type GenericForm = Record<string, unknown>;
137
137
  /** Internal helper — `true` when `T` is an object or array. */
138
138
  type IsObjectOrArray<T> = T extends GenericForm ? true : T extends Array<unknown> ? true : false;
139
+ /**
140
+ * Shared recursion body backing `PartialFlatPath` and `RegisterFlatPath`.
141
+ * One walk over `Form`; `Mode` controls whether intermediate container
142
+ * paths are emitted alongside their reachable leaves.
143
+ *
144
+ * - `'partial'`: emit every container path (object containers, nested-
145
+ * object array roots, array-of-object element containers). Used by
146
+ * `setValue` / `form.values.<path>` / every read-side API that needs
147
+ * to address a container.
148
+ * - `'register'`: skip container paths. `v-register` binds onto a
149
+ * leaf-backing native element (`<input>`, `<select>`, `<textarea>`),
150
+ * so container paths aren't registrable. Primitive arrays still
151
+ * admit the array-root path under both modes (multi-select and
152
+ * grouped-checkbox bindings register onto the array itself).
153
+ *
154
+ * Both walkers were independent recursions before; threading `Mode`
155
+ * through one body keeps the surface guarded by a single source of
156
+ * truth, and `PartialFlatPath` / `RegisterFlatPath` stay byte-
157
+ * identical to the prior hand-written walks (see
158
+ * `test/types/flat-path-walker.test.ts`).
159
+ */
160
+ type FlatPathBuilder<Form, Mode extends 'partial' | 'register', Key extends keyof Form = keyof Form> = IsObjectOrArray<Form> extends true ? Key extends string ? Form[Key] extends infer Value ? Value extends Array<infer ArrayItem> ? IsObjectOrArray<ArrayItem> extends true ? Mode extends 'partial' ? `${Key}` | `${Key}.${number}` | `${Key}.${number}.${FlatPathBuilder<ArrayItem, Mode>}` : `${Key}.${number}.${FlatPathBuilder<ArrayItem, Mode>}` : `${Key}` | `${Key}.${number}` : Value extends GenericForm ? Mode extends 'partial' ? `${Key}` | `${Key}.${FlatPathBuilder<Value, Mode>}` : `${Key}.${FlatPathBuilder<Value, Mode>}` : `${Key}` : never : Key extends number ? `${Key}` | (Form[Key] extends GenericForm ? `${Key}.${FlatPathBuilder<Form[Key], Mode>}` : Form[Key] extends Array<infer ArrayItem> ? IsObjectOrArray<ArrayItem> extends true ? Mode extends 'partial' ? `${Key}.${number}` | `${Key}.${number}.${FlatPathBuilder<ArrayItem, Mode>}` : `${Key}.${number}.${FlatPathBuilder<ArrayItem, Mode>}` : `${Key}.${number}` : never) : never : never;
139
161
  /**
140
162
  * Implementation detail backing `FlatPath` in its default
141
163
  * (partial-path) mode. Exported so `rollup-plugin-dts` preserves it
@@ -146,8 +168,7 @@ type IsObjectOrArray<T> = T extends GenericForm ? true : T extends Array<unknown
146
168
  * when multiple complex forms share a scope. Consumers should reach
147
169
  * for `FlatPath` instead; this alias is not part of the stable surface.
148
170
  */
149
- type PartialFlatPath<Form, Key extends keyof Form = keyof Form> = IsObjectOrArray<Form> extends true ? Key extends string ? Form[Key] extends infer Value ? Value extends Array<infer ArrayItem> ? `${Key}` | `${Key}.${number}` | `${Key}.${number}.${PartialFlatPath<ArrayItem>}` : Value extends GenericForm ? `${Key}` | `${Key}.${PartialFlatPath<Value>}` : `${Key}` : never : Key extends number ? `${Key}` | (Form[Key] extends GenericForm ? `${Key}.${PartialFlatPath<Form[Key]>}` : Form[Key] extends Array<infer ArrayItem> ? IsObjectOrArray<ArrayItem> extends true ? `${Key}.${number}` | `${Key}.${number}.${PartialFlatPath<ArrayItem>}` : `${Key}.${number}` : never) : never : never;
150
- type CompleteFlatPath<Form, Key extends keyof Form = keyof Form> = IsObjectOrArray<Form> extends true ? Key extends string ? Form[Key] extends infer Value ? Value extends Array<infer ArrayItem> ? `${Key}.${number}.${CompleteFlatPath<ArrayItem>}` : Value extends GenericForm ? `${Key}.${CompleteFlatPath<Value>}` : `${Key}` : never : Key extends number ? `${Key}` | (Form[Key] extends GenericForm ? `${Key}.${CompleteFlatPath<Form[Key]>}` : Form[Key] extends Array<infer ArrayItem> ? IsObjectOrArray<ArrayItem> extends true ? `${Key}.${number}.${CompleteFlatPath<ArrayItem>}` : `${Key}.${number}` : never) : never : never;
171
+ type PartialFlatPath<Form, Key extends keyof Form = keyof Form> = FlatPathBuilder<Form, 'partial', Key>;
151
172
  /**
152
173
  * Union of dotted-string paths reachable inside `Form`, e.g. for
153
174
  * `{ user: { email: string }, items: string[] }`:
@@ -157,11 +178,8 @@ type CompleteFlatPath<Form, Key extends keyof Form = keyof Form> = IsObjectOrArr
157
178
  * Used by every path-addressed API (`setValue(path, value)`,
158
179
  * `register(path)`, `toRef(path)`, etc.) so paths autocomplete in
159
180
  * the IDE and typos compile-error.
160
- *
161
- * Set `ForceFullPath` to `true` to restrict to leaf paths only
162
- * (no intermediate container paths).
163
181
  */
164
- type FlatPath<Form, Key extends keyof Form = keyof Form, ForceFullPath extends boolean = false> = ForceFullPath extends true ? CompleteFlatPath<Form, Key> : PartialFlatPath<Form, Key>;
182
+ type FlatPath<Form, Key extends keyof Form = keyof Form> = PartialFlatPath<Form, Key>;
165
183
  /**
166
184
  * Convert a tuple of path segments to its dotted-string equivalent.
167
185
  *
@@ -251,6 +269,31 @@ type LiftedValueShape<T> = [T] extends [
251
269
  type DeepPartial<T> = T extends Primitive ? T : T extends Array<infer ArrayItem> ? DeepPartial<ArrayItem>[] : T extends object ? {
252
270
  [Key in keyof T]?: DeepPartial<T[Key]>;
253
271
  } : T;
272
+ /**
273
+ * Shared descent body backing `NestedType` and `NestedReadType`. The
274
+ * recursion is identical — segment-by-segment, distributing over
275
+ * union members via `KeyofUnion` / `ValueOfUnion`. The two walkers
276
+ * diverge only at the leaf:
277
+ *
278
+ * - `TaintArrayCrossings extends false` (`NestedType` mode): leaves
279
+ * are returned untouched. Used by strict write-side APIs that need
280
+ * the exact resolved type (`setValue`'s value parameter,
281
+ * `form.fields.<path>`'s state map).
282
+ * - `TaintArrayCrossings extends true` (`NestedReadType` mode): leaves
283
+ * are widened with `| undefined` whenever any segment in the walk
284
+ * was numeric (array index). Reflects the runtime possibility of an
285
+ * out-of-bounds read.
286
+ *
287
+ * `_Tainted` propagates the array-crossing under taint mode; under
288
+ * strict mode it stays `false` through every recursion arm. Both
289
+ * arms strip nullishness at the root (`_RootValue = NonNullable<…>`)
290
+ * — the prior `NestedType.FilterOutNullishTypesDuringRecursion` flag
291
+ * was vestigial, never overridden from the outside.
292
+ *
293
+ * Not part of the stable consumer surface — reach for `NestedType` or
294
+ * `NestedReadType` directly.
295
+ */
296
+ type NestedTypeBuilder<RootValue, FlattenedPath extends string, TaintArrayCrossings extends boolean, _Tainted extends boolean = false, _RootValue = NonNullable<RootValue>> = IsObjectOrArray<_RootValue> extends false ? never : FlattenedPath extends `${infer Key}.${infer Rest}` ? Key extends `${number}` ? Key extends KeyofUnion<_RootValue> ? NestedTypeBuilder<ValueOfUnion<_RootValue, Key>, Rest, TaintArrayCrossings, TaintArrayCrossings extends true ? true : _Tainted> : Key extends `${infer NumericKey extends number}` ? NumericKey extends KeyofUnion<_RootValue> ? NestedTypeBuilder<ValueOfUnion<_RootValue, NumericKey>, Rest, TaintArrayCrossings, TaintArrayCrossings extends true ? true : _Tainted> : never : never : Key extends KeyofUnion<_RootValue> ? NestedTypeBuilder<ValueOfUnion<_RootValue, Key>, Rest, TaintArrayCrossings, _Tainted> : never : FlattenedPath extends `${number}` ? FlattenedPath extends KeyofUnion<_RootValue> ? TaintArrayCrossings extends true ? ValueOfUnion<_RootValue, FlattenedPath> | undefined : ValueOfUnion<_RootValue, FlattenedPath> : FlattenedPath extends `${infer NumericKey extends number}` ? NumericKey extends KeyofUnion<_RootValue> ? TaintArrayCrossings extends true ? ValueOfUnion<_RootValue, NumericKey> | undefined : ValueOfUnion<_RootValue, NumericKey> : never : never : FlattenedPath extends KeyofUnion<_RootValue> ? _Tainted extends true ? ValueOfUnion<_RootValue, FlattenedPath> | undefined : ValueOfUnion<_RootValue, FlattenedPath> : never;
254
297
  /**
255
298
  * Resolve the type at a dotted-string path inside `RootValue`. Used
256
299
  * by the strict (write-side) APIs to derive the type at a path:
@@ -264,11 +307,16 @@ type DeepPartial<T> = T extends Primitive ? T : T extends Array<infer ArrayItem>
264
307
  * useful value type (vs. silently collapsing to `never` because
265
308
  * `keyof (A|B|C)` would be the intersection of all variants' keys).
266
309
  *
310
+ * Composed over `NestedTypeBuilder` with array-crossing tainting OFF,
311
+ * so leaves return their exact resolved type. The companion
312
+ * `NestedReadType` shares the same recursion body but enables
313
+ * tainting for read-side APIs.
314
+ *
267
315
  * TypeScript caps conditional-type recursion at around 50 levels;
268
316
  * paths deeper than that resolve to `never`. Real form schemas
269
317
  * never reach this depth.
270
318
  */
271
- type NestedType<RootValue, FlattenedPath extends string, FilterOutNullishTypesDuringRecursion extends boolean = true, _RootValue = FilterOutNullishTypesDuringRecursion extends false ? RootValue : NonNullable<RootValue>> = IsObjectOrArray<_RootValue> extends false ? never : FlattenedPath extends `${infer Key}.${infer Rest}` ? Key extends `${number}` ? Key extends KeyofUnion<_RootValue> ? NestedType<ValueOfUnion<_RootValue, Key>, Rest, FilterOutNullishTypesDuringRecursion> : Key extends `${infer NumericKey extends number}` ? NumericKey extends KeyofUnion<_RootValue> ? NestedType<ValueOfUnion<_RootValue, NumericKey>, Rest, FilterOutNullishTypesDuringRecursion> : never : never : Key extends KeyofUnion<_RootValue> ? NestedType<ValueOfUnion<_RootValue, Key>, Rest, FilterOutNullishTypesDuringRecursion> : never : FlattenedPath extends `${number}` ? FlattenedPath extends KeyofUnion<_RootValue> ? ValueOfUnion<_RootValue, FlattenedPath> : FlattenedPath extends `${infer NumericKey extends number}` ? NumericKey extends KeyofUnion<_RootValue> ? ValueOfUnion<_RootValue, NumericKey> : never : never : FlattenedPath extends KeyofUnion<_RootValue> ? ValueOfUnion<_RootValue, FlattenedPath> : never;
319
+ type NestedType<RootValue, FlattenedPath extends string> = NestedTypeBuilder<RootValue, FlattenedPath, false>;
272
320
  /**
273
321
  * Implementation-detail primitive-leaf marker used by `DeepPartial`
274
322
  * and sibling structural walkers. Exported so the bundled `.d.ts`
@@ -300,7 +348,7 @@ type IsTuple<T extends readonly unknown[]> = number extends T['length'] ? false
300
348
  * `register(path).innerRef` so the compile-time type honours the
301
349
  * runtime possibility of a missing array position.
302
350
  */
303
- type NestedReadType<RootValue, FlattenedPath extends string, _Tainted extends boolean = false, _RootValue = NonNullable<RootValue>> = IsObjectOrArray<_RootValue> extends false ? never : FlattenedPath extends `${infer Key}.${infer Rest}` ? Key extends `${number}` ? Key extends KeyofUnion<_RootValue> ? NestedReadType<ValueOfUnion<_RootValue, Key>, Rest, true> : Key extends `${infer NumericKey extends number}` ? NumericKey extends KeyofUnion<_RootValue> ? NestedReadType<ValueOfUnion<_RootValue, NumericKey>, Rest, true> : never : never : Key extends KeyofUnion<_RootValue> ? NestedReadType<ValueOfUnion<_RootValue, Key>, Rest, _Tainted> : never : FlattenedPath extends `${number}` ? FlattenedPath extends KeyofUnion<_RootValue> ? ValueOfUnion<_RootValue, FlattenedPath> | undefined : FlattenedPath extends `${infer NumericKey extends number}` ? NumericKey extends KeyofUnion<_RootValue> ? ValueOfUnion<_RootValue, NumericKey> | undefined : never : never : FlattenedPath extends KeyofUnion<_RootValue> ? _Tainted extends true ? ValueOfUnion<_RootValue, FlattenedPath> | undefined : ValueOfUnion<_RootValue, FlattenedPath> : never;
351
+ type NestedReadType<RootValue, FlattenedPath extends string> = NestedTypeBuilder<RootValue, FlattenedPath, true>;
304
352
  /**
305
353
  * Filter FlatPath<Form> down to the subset of paths whose resolved leaf
306
354
  * is an array. Used by the typed field-array helpers (append / remove /
@@ -388,6 +436,28 @@ type WriteShape<T> = T extends string | number | boolean | bigint | symbol | nul
388
436
  } : Array<WriteShape<U>> : T extends object ? {
389
437
  [K in keyof T]: WriteShape<T[K]>;
390
438
  } : T;
439
+ /**
440
+ * Walk `T` and add `| Unset` at every primitive leaf (except symbol /
441
+ * null / undefined), every opaque leaf (`Date`, `RegExp`, `Map`,
442
+ * `Set`, functions), and every container position (object, tuple,
443
+ * array). The recursion topology mirrors `WriteShape<T>` exactly —
444
+ * `DefaultValuesShape<T>` is then a 1-line composition.
445
+ *
446
+ * Symbol / null / undefined leaves pass through untouched so the
447
+ * runtime sentinel doesn't pollute leaf semantics it has no business
448
+ * carrying. Container positions widen so a single `unset` at any
449
+ * level recursively marks every descendant primitive blank.
450
+ *
451
+ * Not part of the stable consumer-facing surface — reach for
452
+ * `DefaultValuesShape` instead.
453
+ */
454
+ type AugmentWithUnset<T> = T extends string | number | boolean | bigint ? T | Unset : T extends symbol | null | undefined ? T : T extends Date | RegExp | Map<unknown, unknown> | Set<unknown> | ((...args: never) => unknown) ? T | Unset : T extends readonly [unknown, ...unknown[]] ? {
455
+ -readonly [K in keyof T]: AugmentWithUnset<T[K]>;
456
+ } | Unset : T extends ReadonlyArray<infer U> ? IsTuple<T> extends true ? {
457
+ -readonly [K in keyof T]: AugmentWithUnset<T[K]>;
458
+ } | Unset : Array<AugmentWithUnset<U>> | Unset : T extends object ? {
459
+ [K in keyof T]: AugmentWithUnset<T[K]>;
460
+ } | Unset : T;
391
461
  /**
392
462
  * Like `WriteShape<T>`, but additionally widens every primitive leaf
393
463
  * (`string`, `number`, `boolean`, `bigint`) to admit `Unset` — the
@@ -401,8 +471,10 @@ type WriteShape<T> = T extends string | number | boolean | bigint | symbol | nul
401
471
  * descendant blank, so `defaultValues: { profile: unset }` and
402
472
  * `setValue('cargo', unset)` typecheck cleanly.
403
473
  *
404
- * The recursion mirrors `WriteShape<T>` exactly so `defaultValues`
405
- * stays compatible at every nested position. Tuple positions,
474
+ * Composed as `AugmentWithUnset<WriteShape<T>>`: stage 1 widens
475
+ * primitive literals, stage 2 adds `| Unset` everywhere — the prior
476
+ * inline walker hand-synced this in a single body. The composition
477
+ * stays compatible at every nested position; tuple positions,
406
478
  * unbounded arrays, and nested records all flow through unchanged.
407
479
  *
408
480
  * Example:
@@ -413,13 +485,7 @@ type WriteShape<T> = T extends string | number | boolean | bigint | symbol | nul
413
485
  * Used by `UseFormConfiguration.defaultValues`, `setValue`'s value
414
486
  * parameter, and `reset`'s parameter.
415
487
  */
416
- type DefaultValuesShape<T> = T extends string | number | boolean | bigint | symbol | null | undefined ? T extends string ? string | Unset : T extends number ? number | Unset : T extends boolean ? boolean | Unset : T extends bigint ? bigint | Unset : T extends symbol ? symbol : T : T extends Date | RegExp | Map<unknown, unknown> | Set<unknown> | ((...args: never) => unknown) ? T | Unset : T extends readonly [unknown, ...unknown[]] ? {
417
- -readonly [K in keyof T]: DefaultValuesShape<T[K]>;
418
- } | Unset : T extends ReadonlyArray<infer U> ? IsTuple<T> extends true ? {
419
- -readonly [K in keyof T]: DefaultValuesShape<T[K]>;
420
- } | Unset : Array<DefaultValuesShape<U>> | Unset : T extends object ? {
421
- [K in keyof T]: DefaultValuesShape<T[K]>;
422
- } | Unset : T;
488
+ type DefaultValuesShape<T> = AugmentWithUnset<WriteShape<T>>;
423
489
  /**
424
490
  * Single-walker fusion of `DeepPartial` and `DefaultValuesShape` — the
425
491
  * type accepted at `defaultValues`, `reset()`'s parameter, and every
@@ -713,6 +779,15 @@ type MaybePromise<T> = T | Promise<T>;
713
779
  type ValidateOptions = {
714
780
  sync?: boolean;
715
781
  };
782
+ /**
783
+ * Configuration passed to `AbstractSchema.getDefaultValues`. Adapters
784
+ * receive `useDefaultSchemaValues` (honor `.default(x)` wrappers vs.
785
+ * empty/falsy fallbacks), an optional `strict` mode (refinement
786
+ * preservation), and an optional `constraints` overlay merged into the
787
+ * derived defaults so the runtime can stamp user-supplied defaults at
788
+ * construction. Exported so adapter authors can co-implement the
789
+ * service contract.
790
+ */
716
791
  type GetDefaultValuesConfig<Form> = {
717
792
  useDefaultSchemaValues: boolean;
718
793
  /**
@@ -1092,6 +1167,32 @@ type AbstractSchema<Form, GetValueFormType> = {
1092
1167
  * answers without a separate top-level overload.
1093
1168
  */
1094
1169
  needsAsyncValidation?(): boolean;
1170
+ /**
1171
+ * Return `true` iff the schema carries a refine / check / transform
1172
+ * at any NON-LEAF position — a container node (object / array /
1173
+ * tuple / union / intersection / record / map / set) or the root
1174
+ * itself. False means every check this schema runs is leaf-local,
1175
+ * so a per-keystroke `validateAtPath(form, leafPath)` catches the
1176
+ * same verdicts as a whole-form pass — no ancestor refine reads
1177
+ * the form's wider state.
1178
+ *
1179
+ * The runtime uses this at the per-keystroke schedule to scope
1180
+ * field-level validation to the changed subtree when it can,
1181
+ * falling back to a whole-form pass when an ancestor refine
1182
+ * (cross-field equality, sum constraints, etc.) could be moved
1183
+ * by a leaf write. Optional. The runtime treats a missing
1184
+ * implementation as `() => true` — conservative whole-form,
1185
+ * preserving correctness for adapters that don't yet model
1186
+ * container-refine detection.
1187
+ *
1188
+ * Detection is best-effort: false negatives (returning `true`
1189
+ * when no container refine exists) only lose a perf win and
1190
+ * still validate correctly; false positives (returning `false`
1191
+ * when a container refine exists) would let an ancestor verdict
1192
+ * go stale and are the real risk — implementations should bias
1193
+ * toward returning `true` when in doubt.
1194
+ */
1195
+ hasContainerOrRootRefine?(): boolean;
1095
1196
  };
1096
1197
  /**
1097
1198
  * Adapter-returned info for a discriminated union — its discriminator
@@ -1795,8 +1896,9 @@ type UseFormConfiguration<Form extends GenericForm, GetValueFormType, Schema ext
1795
1896
  maxRecursionDepth?: number;
1796
1897
  /**
1797
1898
  * Override the path-segment name stems treated as sensitive for this
1798
- * form. Sensitive paths are excluded from persistence writes,
1799
- * multi-tab sync broadcasts, AND the DevTools redact walk.
1899
+ * form. Sensitive paths are excluded from persistence writes and
1900
+ * multi-tab sync broadcasts. (DevTools renders raw values by design;
1901
+ * it does not redact.)
1800
1902
  *
1801
1903
  * Resolution: per-form value (this field) > global default
1802
1904
  * (`createAttaform({ defaults: { sensitiveNames } })`) > library
@@ -2022,9 +2124,10 @@ type AttaformDefaults = {
2022
2124
  maxRecursionDepth?: number;
2023
2125
  /**
2024
2126
  * Override the path-segment name stems treated as sensitive.
2025
- * Sensitive paths are excluded from persistence writes, multi-tab
2026
- * sync broadcasts, AND the DevTools redact walk one configurable
2027
- * source of truth across every surface.
2127
+ * Sensitive paths are excluded from persistence writes and multi-tab
2128
+ * sync broadcasts one configurable source of truth across those
2129
+ * surfaces. (DevTools renders raw values by design; it does not
2130
+ * redact.)
2028
2131
  *
2029
2132
  * Library default is `DEFAULT_SENSITIVE_NAMES` (exported from
2030
2133
  * `attaform`); compose to extend:
@@ -2235,7 +2338,7 @@ type MetaTrackerValue = {
2235
2338
  */
2236
2339
  blank: boolean;
2237
2340
  };
2238
- type RegisterFlatPath<Form, Key extends keyof Form = keyof Form> = IsObjectOrArray<Form> extends true ? Key extends string ? Form[Key] extends infer Value ? Value extends Array<infer ArrayItem> ? IsObjectOrArray<ArrayItem> extends true ? `${Key}.${number}.${RegisterFlatPath<ArrayItem>}` : `${Key}` | `${Key}.${number}` : Value extends GenericForm ? `${Key}.${RegisterFlatPath<Value>}` : `${Key}` : never : Key extends number ? `${Key}` | (Form[Key] extends GenericForm ? `${Key}.${RegisterFlatPath<Form[Key]>}` : Form[Key] extends Array<infer ArrayItem> ? IsObjectOrArray<ArrayItem> extends true ? `${Key}.${number}.${RegisterFlatPath<ArrayItem>}` : `${Key}` | `${Key}.${number}` : never) : never : never;
2341
+ type RegisterFlatPath<Form, Key extends keyof Form = keyof Form> = FlatPathBuilder<Form, 'register', Key>;
2239
2342
  /**
2240
2343
  * Sync transformation applied to a field's value as user input flows
2241
2344
  * from DOM through the directive's assigner. Composes left-to-right
@@ -2341,9 +2444,10 @@ type RegisterOptions = {
2341
2444
  * If multiple inputs bind to the same path, the path keeps
2342
2445
  * persisting as long as any opted-in input is mounted.
2343
2446
  *
2344
- * Throws `SensitivePersistFieldError` when the path looks
2345
- * sensitive (password / cvv / ssn / token / etc.) unless
2346
- * `acknowledgeSensitive: true` is also set.
2447
+ * When the path looks sensitive (password / cvv / ssn / token /
2448
+ * etc.) the opt-in is skipped with a one-shot dev warning unless
2449
+ * `acknowledgeSensitive: true` is also set — the field simply isn't
2450
+ * persisted (the secure default). It never throws.
2347
2451
  */
2348
2452
  persist?: boolean;
2349
2453
  /**
@@ -2528,7 +2632,7 @@ type RegisterValue<Value = unknown> = Readonly<{
2528
2632
  /**
2529
2633
  * Resolved sensitive-path predicate honoring this form's
2530
2634
  * `sensitiveNames` cascade. The directive calls this through
2531
- * `enforceSensitiveCheck` when a `register('path', { persist: true })`
2635
+ * `allowSensitivePersist` when a `register('path', { persist: true })`
2532
2636
  * binding mounts so a per-form custom list (e.g. extending with
2533
2637
  * `'mrn'`) gates persistence enrolment correctly.
2534
2638
  * @internal
@@ -4347,9 +4451,9 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4347
4451
  * paths in the persisted draft are preserved (this is a merge,
4348
4452
  * not a replace).
4349
4453
  *
4350
- * Throws `SensitivePersistFieldError` for sensitive-looking paths
4351
- * unless you pass `{ acknowledgeSensitive: true }`. No-op when
4352
- * `useForm({ persist })` wasn't configured.
4454
+ * For sensitive-looking paths, warns once and no-ops unless you pass
4455
+ * `{ acknowledgeSensitive: true }` — it never throws. Also a no-op
4456
+ * when `useForm({ persist })` wasn't configured.
4353
4457
  */
4354
4458
  persist: (path: FlatPath<Form>, options?: {
4355
4459
  acknowledgeSensitive?: boolean;