attaform 0.16.4 → 0.17.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 (84) hide show
  1. package/README.md +4 -2
  2. package/dist/chunks/devtools.cjs +19 -12
  3. package/dist/chunks/devtools.cjs.map +1 -1
  4. package/dist/chunks/devtools.mjs +19 -12
  5. package/dist/chunks/devtools.mjs.map +1 -1
  6. package/dist/chunks/indexeddb.cjs +1 -1
  7. package/dist/chunks/indexeddb.mjs +1 -1
  8. package/dist/chunks/local-storage.cjs +1 -1
  9. package/dist/chunks/local-storage.mjs +1 -1
  10. package/dist/chunks/session-storage.cjs +1 -1
  11. package/dist/chunks/session-storage.mjs +1 -1
  12. package/dist/index.cjs +26 -7
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +52 -9
  15. package/dist/index.d.mts +52 -9
  16. package/dist/index.d.ts +52 -9
  17. package/dist/index.mjs +28 -9
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/nuxt.d.cts +1 -1
  20. package/dist/nuxt.d.mts +1 -1
  21. package/dist/nuxt.d.ts +1 -1
  22. package/dist/runtime/plugins/attaform.cjs +3 -3
  23. package/dist/runtime/plugins/attaform.cjs.map +1 -1
  24. package/dist/runtime/plugins/attaform.mjs +3 -3
  25. package/dist/runtime/plugins/attaform.mjs.map +1 -1
  26. package/dist/shared/{attaform.CMRmwGDt.d.cts → attaform.B1jvxsOF.d.mts} +1 -1
  27. package/dist/shared/{attaform.DyV1O4tI.mjs → attaform.B3ZaPIzS.mjs} +1436 -391
  28. package/dist/shared/attaform.B3ZaPIzS.mjs.map +1 -0
  29. package/dist/shared/{attaform.Dd_pWnmn.cjs → attaform.B5qiXQwN.cjs} +59 -10
  30. package/dist/shared/attaform.B5qiXQwN.cjs.map +1 -0
  31. package/dist/shared/{attaform.CIwZtbGV.cjs → attaform.BBM2muQ9.cjs} +2 -2
  32. package/dist/shared/{attaform.CIwZtbGV.cjs.map → attaform.BBM2muQ9.cjs.map} +1 -1
  33. package/dist/shared/{attaform.keLBaHB6.cjs → attaform.BV40t5y2.cjs} +240 -115
  34. package/dist/shared/attaform.BV40t5y2.cjs.map +1 -0
  35. package/dist/shared/attaform.C0iFnTN0.d.ts +165 -0
  36. package/dist/shared/{attaform.CXMOheyZ.d.mts → attaform.C6qzEdIM.d.cts} +1 -1
  37. package/dist/shared/{attaform.CJttVxRj.cjs → attaform.C8LVFVVe.cjs} +2 -2
  38. package/dist/shared/{attaform.CJttVxRj.cjs.map → attaform.C8LVFVVe.cjs.map} +1 -1
  39. package/dist/shared/attaform.CHorcsIU.d.cts +165 -0
  40. package/dist/shared/{attaform.BfMxsfmE.mjs → attaform.CIEQgJnM.mjs} +143 -78
  41. package/dist/shared/attaform.CIEQgJnM.mjs.map +1 -0
  42. package/dist/shared/{attaform.CCQkY4Ta.d.ts → attaform.CTwNcpLE.d.ts} +1 -1
  43. package/dist/shared/{attaform.UA19EF3J.mjs → attaform.CVCmBKZX.mjs} +59 -10
  44. package/dist/shared/attaform.CVCmBKZX.mjs.map +1 -0
  45. package/dist/shared/{attaform.CU3JperC.d.cts → attaform.C_5aB6EQ.d.cts} +657 -135
  46. package/dist/shared/{attaform.CU3JperC.d.mts → attaform.C_5aB6EQ.d.mts} +657 -135
  47. package/dist/shared/{attaform.CU3JperC.d.ts → attaform.C_5aB6EQ.d.ts} +657 -135
  48. package/dist/shared/{attaform.fegmBJaq.cjs → attaform.Cer8JO_P.cjs} +1435 -389
  49. package/dist/shared/attaform.Cer8JO_P.cjs.map +1 -0
  50. package/dist/shared/{attaform.g7rfuXdz.mjs → attaform.CpERWz3u.mjs} +240 -115
  51. package/dist/shared/attaform.CpERWz3u.mjs.map +1 -0
  52. package/dist/shared/attaform.CuE-bS1C.d.mts +165 -0
  53. package/dist/shared/{attaform.rIRYSUI1.cjs → attaform.Dee2rU1P.cjs} +145 -77
  54. package/dist/shared/attaform.Dee2rU1P.cjs.map +1 -0
  55. package/dist/shared/{attaform.CINUMjPq.mjs → attaform.Vo-Kft0t.mjs} +2 -2
  56. package/dist/shared/{attaform.CINUMjPq.mjs.map → attaform.Vo-Kft0t.mjs.map} +1 -1
  57. package/dist/shared/{attaform.DZRj9s0s.mjs → attaform.h1sq3BFu.mjs} +2 -2
  58. package/dist/shared/{attaform.DZRj9s0s.mjs.map → attaform.h1sq3BFu.mjs.map} +1 -1
  59. package/dist/zod-v3.cjs +3 -3
  60. package/dist/zod-v3.d.cts +27 -5
  61. package/dist/zod-v3.d.mts +27 -5
  62. package/dist/zod-v3.d.ts +27 -5
  63. package/dist/zod-v3.mjs +3 -3
  64. package/dist/zod-v4.cjs +3 -3
  65. package/dist/zod-v4.d.cts +16 -42
  66. package/dist/zod-v4.d.mts +16 -42
  67. package/dist/zod-v4.d.ts +16 -42
  68. package/dist/zod-v4.mjs +3 -3
  69. package/dist/zod.cjs +4 -4
  70. package/dist/zod.cjs.map +1 -1
  71. package/dist/zod.d.cts +7 -5
  72. package/dist/zod.d.mts +7 -5
  73. package/dist/zod.d.ts +7 -5
  74. package/dist/zod.mjs +5 -5
  75. package/dist/zod.mjs.map +1 -1
  76. package/package.json +6 -11
  77. package/dist/shared/attaform.BfMxsfmE.mjs.map +0 -1
  78. package/dist/shared/attaform.Dd_pWnmn.cjs.map +0 -1
  79. package/dist/shared/attaform.DyV1O4tI.mjs.map +0 -1
  80. package/dist/shared/attaform.UA19EF3J.mjs.map +0 -1
  81. package/dist/shared/attaform.fegmBJaq.cjs.map +0 -1
  82. package/dist/shared/attaform.g7rfuXdz.mjs.map +0 -1
  83. package/dist/shared/attaform.keLBaHB6.cjs.map +0 -1
  84. package/dist/shared/attaform.rIRYSUI1.cjs.map +0 -1
@@ -0,0 +1,165 @@
1
+ import { z } from 'zod';
2
+ import { G as GenericForm, s as FlatPath, B as NestedType, U as UseFormConfiguration, b as AbstractSchema, D as DeepPartial, c as DefaultValuesShape, ag as ValidateOnConfig, d as UseFormReturnType } from './attaform.C_5aB6EQ.js';
3
+
4
+ /**
5
+ * The shape `form.values.<key>` returns at runtime.
6
+ *
7
+ * Per leaf:
8
+ *
9
+ * 1. `z.preprocess(fn, inner)` — compiles to `ZodPipe<ZodTransform, inner>`.
10
+ * The preprocess fn fires at the write boundary (synthesized into
11
+ * `setValue`), so storage holds whatever `inner` stores. Recurse
12
+ * `StorageShape` on `inner` so a defaulted leaf inside `inner` still
13
+ * reads `T` (not `T | undefined`).
14
+ *
15
+ * 2. `inner.transform(fn)` — compiles to `ZodPipe<inner, ZodTransform>`.
16
+ * Transforms fire at submit / validate, NOT at the write boundary,
17
+ * so storage holds whatever `inner` stores. Recurse `StorageShape`
18
+ * on `inner` for the same reason.
19
+ *
20
+ * A bare top-level `ZodTransform` (no `in` schema) reads
21
+ * `_zod.input` directly — there's no inner to recurse into.
22
+ *
23
+ * 3. Codec / generic pipe — neither side is a transform. Read
24
+ * `_zod.output`. Codecs aren't write-boundary-synthesized, so the
25
+ * post-parse view is the only honest storage type.
26
+ *
27
+ * 4. Everything else (defaults, catch, readonly, optional, primitives,
28
+ * nested objects) — read `_zod.output`. Defaults and catches fire
29
+ * at parse time, so the post-init view is what storage holds.
30
+ * Nested objects delegate to Zod's own recursion on `_zod.output`,
31
+ * which peels nested defaults inside structural containers.
32
+ *
33
+ * Recursion: the alias calls itself on the non-transform side of a
34
+ * pipe so the inner shape gets the same per-key storage treatment as
35
+ * the top level. Without it, an inner `.default(...)` inside a
36
+ * transformed object would peel back to `T | undefined` (the broad
37
+ * input contract). Recursion only fires for pipe leaves — most leaves
38
+ * skip it.
39
+ *
40
+ * Implementation note: direct `_zod` property access mirrors Zod's
41
+ * own `$InferObjectOutput` / `$InferObjectInput`, which read
42
+ * `T[k]['_zod']['output']` / `T[k]['_zod']['input']` directly rather
43
+ * than wrapping in the top-level `output<T>` / `input<T>` conditional.
44
+ * Wrapping per key spawns a fresh conditional instantiation for every
45
+ * key; Volar's web-worker checker collapses that per-key walk to
46
+ * `any` once the schema is non-trivial. Property access has no
47
+ * conditional and resolves cleanly under the same budget.
48
+ *
49
+ * Shape access also goes through `_zod.def.shape` rather than
50
+ * `infer Shape from z.ZodObject<Shape>` — the latter collapses to the
51
+ * `$ZodShape` upper bound in the same worker because of
52
+ * `z.ZodObject`'s `out Shape` covariance markers.
53
+ */
54
+ type StorageShape<S> = S extends {
55
+ _zod: {
56
+ def: {
57
+ type: 'object';
58
+ shape: infer Shape;
59
+ };
60
+ };
61
+ } ? {
62
+ [K in keyof Shape]-?: StorageLeaf<Shape[K]>;
63
+ } : StorageLeaf<S>;
64
+ type StorageLeaf<L> = L extends {
65
+ _zod: {
66
+ def: {
67
+ type: 'pipe';
68
+ in: infer A;
69
+ out: infer B;
70
+ };
71
+ };
72
+ } ? A extends {
73
+ _zod: {
74
+ def: {
75
+ type: 'transform';
76
+ };
77
+ };
78
+ } ? StorageShape<B> : B extends {
79
+ _zod: {
80
+ def: {
81
+ type: 'transform';
82
+ };
83
+ };
84
+ } ? StorageShape<A> : L extends {
85
+ _zod: {
86
+ output: infer Out;
87
+ };
88
+ } ? Out : never : L extends {
89
+ _zod: {
90
+ def: {
91
+ type: 'transform';
92
+ };
93
+ };
94
+ } ? L extends {
95
+ _zod: {
96
+ input: infer In;
97
+ };
98
+ } ? In : never : L extends {
99
+ _zod: {
100
+ output: infer Out;
101
+ };
102
+ } ? Out : never;
103
+
104
+ /**
105
+ * Zod v4 adapter entry point. Re-exports the adapter + the useForm
106
+ * wrapper that threads zod-v4-specific schema types through
107
+ * useAbstractForm.
108
+ */
109
+
110
+ /**
111
+ * Type of the value accepted at `Path` for `setValue` / `defaultValues`
112
+ * — the schema's `z.input<Schema>` shape at that path. Matches what
113
+ * `form.values.X` returns at runtime (the honest input view storage
114
+ * holds before transforms run).
115
+ *
116
+ * ```ts
117
+ * const schema = z.object({
118
+ * flag: z.string().transform((v) => v.length > 10),
119
+ * })
120
+ * type FlagWriteIn = PathInput<typeof schema, 'flag'> // string
121
+ * ```
122
+ */
123
+ type PathInput<Schema extends z.ZodType, Path extends string> = z.input<Schema> extends GenericForm ? Path extends FlatPath<z.input<Schema>> ? NestedType<z.input<Schema>, Path> : never : never;
124
+ /**
125
+ * Type produced at `Path` after the full parse pipeline — the schema's
126
+ * `z.output<Schema>` shape at that path. Matches the `data` payload of
127
+ * `form.process()` and the value handed to `handleSubmit`'s callback.
128
+ *
129
+ * ```ts
130
+ * const schema = z.object({
131
+ * flag: z.string().transform((v) => v.length > 10),
132
+ * })
133
+ * type FlagParsedOut = PathOutput<typeof schema, 'flag'> // boolean
134
+ * ```
135
+ */
136
+ type PathOutput<Schema extends z.ZodType, Path extends string> = z.output<Schema> extends GenericForm ? Path extends FlatPath<z.output<Schema>> ? NestedType<z.output<Schema>, Path> : never : never;
137
+ /**
138
+ * Create a form bound to a Zod v4 schema.
139
+ *
140
+ * ```ts
141
+ * import { useForm } from 'attaform/zod'
142
+ * import { z } from 'zod'
143
+ *
144
+ * const form = useForm({
145
+ * schema: z.object({
146
+ * email: z.email(),
147
+ * password: z.string().min(8),
148
+ * }),
149
+ * defaultValues: { email: '' },
150
+ * })
151
+ * ```
152
+ *
153
+ * Returns a form API exposing `register`, `values`, `errors`,
154
+ * `fields`, `setValue`, `handleSubmit`, `meta`, field-array
155
+ * helpers, and more. See `UseFormReturnType` for the full
156
+ * surface.
157
+ *
158
+ * For Zod v3, import from `attaform/zod-v3` instead.
159
+ */
160
+ declare function useForm<Schema extends z.ZodObject>(configuration: Omit<UseFormConfiguration<z.input<Schema> extends GenericForm ? z.input<Schema> : never, z.output<Schema> extends GenericForm ? z.output<Schema> : never, AbstractSchema<z.input<Schema> extends GenericForm ? z.input<Schema> : never, z.output<Schema> extends GenericForm ? z.output<Schema> : never>, DeepPartial<DefaultValuesShape<z.input<Schema> extends GenericForm ? z.input<Schema> : never>>>, 'schema' | 'validateOn' | 'debounceMs'> & {
161
+ schema: Schema;
162
+ } & ValidateOnConfig): UseFormReturnType<z.input<Schema> extends GenericForm ? z.input<Schema> : never, z.output<Schema> extends GenericForm ? z.output<Schema> : never, StorageShape<Schema> extends GenericForm ? StorageShape<Schema> : never>;
163
+
164
+ export { useForm as u };
165
+ export type { PathInput as P, StorageShape as S, PathOutput as a };
@@ -1,4 +1,4 @@
1
- import { G as GenericForm, F as FormKey, d as UseFormReturnType, R as RegisterValue } from './attaform.CU3JperC.mjs';
1
+ import { G as GenericForm, F as FormKey, d as UseFormReturnType, R as RegisterValue } from './attaform.C_5aB6EQ.cjs';
2
2
  import { Ref } from 'vue';
3
3
 
4
4
  /**
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const plugin = require('./attaform.rIRYSUI1.cjs');
3
+ const plugin = require('./attaform.Dee2rU1P.cjs');
4
4
 
5
5
  function renderAttaformState(app) {
6
6
  const registry = plugin.getRegistryFromApp(app);
@@ -29,4 +29,4 @@ function hydrateAttaformState(app, payload) {
29
29
 
30
30
  exports.hydrateAttaformState = hydrateAttaformState;
31
31
  exports.renderAttaformState = renderAttaformState;
32
- //# sourceMappingURL=attaform.CJttVxRj.cjs.map
32
+ //# sourceMappingURL=attaform.C8LVFVVe.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"attaform.CJttVxRj.cjs","sources":["../../src/runtime/core/serialize.ts"],"sourcesContent":["import type { App } from 'vue'\nimport type { FormKey } from '../types/types-api'\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\").\n const transientList = Array.from(state.blankPaths)\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"],"names":["getRegistryFromApp"],"mappings":";;;;AAsCO,SAAS,oBAAoB,GAAA,EAAmC;AACrE,EAAA,MAAM,QAAA,GAAWA,0BAAmB,GAAG,CAAA;AACvC,EAAA,MAAM,QAAuD,EAAC;AAC9D,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,SAAS,KAAA,EAAO;AAKzC,IAAA,MAAM,aAAA,GAAgB,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,UAAU,CAAA;AACjD,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,GAAWA,0BAAmB,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;;;;;"}
1
+ {"version":3,"file":"attaform.C8LVFVVe.cjs","sources":["../../src/runtime/core/serialize.ts"],"sourcesContent":["import type { App } from 'vue'\nimport type { FormKey } from '../types/types-api'\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\").\n const transientList = Array.from(state.blankPaths)\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"],"names":["getRegistryFromApp"],"mappings":";;;;AAsCO,SAAS,oBAAoB,GAAA,EAAmC;AACrE,EAAA,MAAM,QAAA,GAAWA,0BAAmB,GAAG,CAAA;AACvC,EAAA,MAAM,QAAuD,EAAC;AAC9D,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,SAAS,KAAA,EAAO;AAKzC,IAAA,MAAM,aAAA,GAAgB,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,UAAU,CAAA;AACjD,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,GAAWA,0BAAmB,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;;;;;"}
@@ -0,0 +1,165 @@
1
+ import { z } from 'zod';
2
+ import { G as GenericForm, s as FlatPath, B as NestedType, U as UseFormConfiguration, b as AbstractSchema, D as DeepPartial, c as DefaultValuesShape, ag as ValidateOnConfig, d as UseFormReturnType } from './attaform.C_5aB6EQ.cjs';
3
+
4
+ /**
5
+ * The shape `form.values.<key>` returns at runtime.
6
+ *
7
+ * Per leaf:
8
+ *
9
+ * 1. `z.preprocess(fn, inner)` — compiles to `ZodPipe<ZodTransform, inner>`.
10
+ * The preprocess fn fires at the write boundary (synthesized into
11
+ * `setValue`), so storage holds whatever `inner` stores. Recurse
12
+ * `StorageShape` on `inner` so a defaulted leaf inside `inner` still
13
+ * reads `T` (not `T | undefined`).
14
+ *
15
+ * 2. `inner.transform(fn)` — compiles to `ZodPipe<inner, ZodTransform>`.
16
+ * Transforms fire at submit / validate, NOT at the write boundary,
17
+ * so storage holds whatever `inner` stores. Recurse `StorageShape`
18
+ * on `inner` for the same reason.
19
+ *
20
+ * A bare top-level `ZodTransform` (no `in` schema) reads
21
+ * `_zod.input` directly — there's no inner to recurse into.
22
+ *
23
+ * 3. Codec / generic pipe — neither side is a transform. Read
24
+ * `_zod.output`. Codecs aren't write-boundary-synthesized, so the
25
+ * post-parse view is the only honest storage type.
26
+ *
27
+ * 4. Everything else (defaults, catch, readonly, optional, primitives,
28
+ * nested objects) — read `_zod.output`. Defaults and catches fire
29
+ * at parse time, so the post-init view is what storage holds.
30
+ * Nested objects delegate to Zod's own recursion on `_zod.output`,
31
+ * which peels nested defaults inside structural containers.
32
+ *
33
+ * Recursion: the alias calls itself on the non-transform side of a
34
+ * pipe so the inner shape gets the same per-key storage treatment as
35
+ * the top level. Without it, an inner `.default(...)` inside a
36
+ * transformed object would peel back to `T | undefined` (the broad
37
+ * input contract). Recursion only fires for pipe leaves — most leaves
38
+ * skip it.
39
+ *
40
+ * Implementation note: direct `_zod` property access mirrors Zod's
41
+ * own `$InferObjectOutput` / `$InferObjectInput`, which read
42
+ * `T[k]['_zod']['output']` / `T[k]['_zod']['input']` directly rather
43
+ * than wrapping in the top-level `output<T>` / `input<T>` conditional.
44
+ * Wrapping per key spawns a fresh conditional instantiation for every
45
+ * key; Volar's web-worker checker collapses that per-key walk to
46
+ * `any` once the schema is non-trivial. Property access has no
47
+ * conditional and resolves cleanly under the same budget.
48
+ *
49
+ * Shape access also goes through `_zod.def.shape` rather than
50
+ * `infer Shape from z.ZodObject<Shape>` — the latter collapses to the
51
+ * `$ZodShape` upper bound in the same worker because of
52
+ * `z.ZodObject`'s `out Shape` covariance markers.
53
+ */
54
+ type StorageShape<S> = S extends {
55
+ _zod: {
56
+ def: {
57
+ type: 'object';
58
+ shape: infer Shape;
59
+ };
60
+ };
61
+ } ? {
62
+ [K in keyof Shape]-?: StorageLeaf<Shape[K]>;
63
+ } : StorageLeaf<S>;
64
+ type StorageLeaf<L> = L extends {
65
+ _zod: {
66
+ def: {
67
+ type: 'pipe';
68
+ in: infer A;
69
+ out: infer B;
70
+ };
71
+ };
72
+ } ? A extends {
73
+ _zod: {
74
+ def: {
75
+ type: 'transform';
76
+ };
77
+ };
78
+ } ? StorageShape<B> : B extends {
79
+ _zod: {
80
+ def: {
81
+ type: 'transform';
82
+ };
83
+ };
84
+ } ? StorageShape<A> : L extends {
85
+ _zod: {
86
+ output: infer Out;
87
+ };
88
+ } ? Out : never : L extends {
89
+ _zod: {
90
+ def: {
91
+ type: 'transform';
92
+ };
93
+ };
94
+ } ? L extends {
95
+ _zod: {
96
+ input: infer In;
97
+ };
98
+ } ? In : never : L extends {
99
+ _zod: {
100
+ output: infer Out;
101
+ };
102
+ } ? Out : never;
103
+
104
+ /**
105
+ * Zod v4 adapter entry point. Re-exports the adapter + the useForm
106
+ * wrapper that threads zod-v4-specific schema types through
107
+ * useAbstractForm.
108
+ */
109
+
110
+ /**
111
+ * Type of the value accepted at `Path` for `setValue` / `defaultValues`
112
+ * — the schema's `z.input<Schema>` shape at that path. Matches what
113
+ * `form.values.X` returns at runtime (the honest input view storage
114
+ * holds before transforms run).
115
+ *
116
+ * ```ts
117
+ * const schema = z.object({
118
+ * flag: z.string().transform((v) => v.length > 10),
119
+ * })
120
+ * type FlagWriteIn = PathInput<typeof schema, 'flag'> // string
121
+ * ```
122
+ */
123
+ type PathInput<Schema extends z.ZodType, Path extends string> = z.input<Schema> extends GenericForm ? Path extends FlatPath<z.input<Schema>> ? NestedType<z.input<Schema>, Path> : never : never;
124
+ /**
125
+ * Type produced at `Path` after the full parse pipeline — the schema's
126
+ * `z.output<Schema>` shape at that path. Matches the `data` payload of
127
+ * `form.process()` and the value handed to `handleSubmit`'s callback.
128
+ *
129
+ * ```ts
130
+ * const schema = z.object({
131
+ * flag: z.string().transform((v) => v.length > 10),
132
+ * })
133
+ * type FlagParsedOut = PathOutput<typeof schema, 'flag'> // boolean
134
+ * ```
135
+ */
136
+ type PathOutput<Schema extends z.ZodType, Path extends string> = z.output<Schema> extends GenericForm ? Path extends FlatPath<z.output<Schema>> ? NestedType<z.output<Schema>, Path> : never : never;
137
+ /**
138
+ * Create a form bound to a Zod v4 schema.
139
+ *
140
+ * ```ts
141
+ * import { useForm } from 'attaform/zod'
142
+ * import { z } from 'zod'
143
+ *
144
+ * const form = useForm({
145
+ * schema: z.object({
146
+ * email: z.email(),
147
+ * password: z.string().min(8),
148
+ * }),
149
+ * defaultValues: { email: '' },
150
+ * })
151
+ * ```
152
+ *
153
+ * Returns a form API exposing `register`, `values`, `errors`,
154
+ * `fields`, `setValue`, `handleSubmit`, `meta`, field-array
155
+ * helpers, and more. See `UseFormReturnType` for the full
156
+ * surface.
157
+ *
158
+ * For Zod v3, import from `attaform/zod-v3` instead.
159
+ */
160
+ declare function useForm<Schema extends z.ZodObject>(configuration: Omit<UseFormConfiguration<z.input<Schema> extends GenericForm ? z.input<Schema> : never, z.output<Schema> extends GenericForm ? z.output<Schema> : never, AbstractSchema<z.input<Schema> extends GenericForm ? z.input<Schema> : never, z.output<Schema> extends GenericForm ? z.output<Schema> : never>, DeepPartial<DefaultValuesShape<z.input<Schema> extends GenericForm ? z.input<Schema> : never>>>, 'schema' | 'validateOn' | 'debounceMs'> & {
161
+ schema: Schema;
162
+ } & ValidateOnConfig): UseFormReturnType<z.input<Schema> extends GenericForm ? z.input<Schema> : never, z.output<Schema> extends GenericForm ? z.output<Schema> : never, StorageShape<Schema> extends GenericForm ? StorageShape<Schema> : never>;
163
+
164
+ export { useForm as u };
165
+ export type { PathInput as P, StorageShape as S, PathOutput as a };
@@ -71,7 +71,7 @@ function formatAnonPersistMessage(opts) {
71
71
  }
72
72
 
73
73
  function detectSSR(options = {}) {
74
- if (options.override !== void 0) return options.override;
74
+ if (options.ssr !== void 0) return options.ssr;
75
75
  return typeof window === "undefined" && typeof document === "undefined";
76
76
  }
77
77
 
@@ -381,91 +381,138 @@ function createPersistOptInRegistry() {
381
381
  };
382
382
  }
383
383
 
384
- const SENSITIVE_NAME_PATTERNS = [
385
- // Passwords and PIN-like
386
- /password/i,
387
- /passwd/i,
388
- /passwords/i,
389
- /\bpwd\b/i,
390
- /\bpin\b/i,
384
+ const DEFAULT_SENSITIVE_NAMES = Object.freeze([
385
+ // Passwords + PIN-like
386
+ "password",
387
+ "passwd",
388
+ "pwd",
389
+ "pin",
391
390
  // Card / payment
392
- /\bcvv\b/i,
393
- /\bcvc\b/i,
394
- /card[_\s-]?(?:number|num)/i,
395
- /\bcard\b/i,
396
- /\biban\b/i,
397
- /routing[_\s-]?number/i,
398
- /account[_\s-]?number/i,
391
+ "cvv",
392
+ "cvc",
393
+ "card_number",
394
+ "card_num",
395
+ "card",
396
+ "iban",
397
+ "routing_number",
398
+ "account_number",
399
399
  // Government / identity
400
- /\bssn\b/i,
401
- /social[_\s-]?security/i,
402
- /\bdob\b/i,
403
- /date[_\s-]?of[_\s-]?birth/i,
404
- /passport/i,
405
- /driver[_\s-]?license/i,
406
- // Tax IDs (US + international common variants)
407
- /\btin\b/i,
408
- /\bein\b/i,
409
- /\bitin\b/i,
410
- /tax[_\s-]?id/i,
411
- // Tokens, secrets, API/auth credentials
412
- /\btoken\b/i,
413
- /\btokens\b/i,
414
- /secret/i,
415
- /secrets/i,
416
- /api[_\s-]?key/i,
417
- /api[_\s-]?secret/i,
418
- /api[_\s-]?token/i,
419
- /private[_\s-]?key/i,
420
- /\bbearer\b/i,
421
- /\boauth\b/i,
422
- /auth[_\s-]?token/i,
423
- /access[_\s-]?token/i,
424
- /refresh[_\s-]?token/i,
425
- /session[_\s-]?(?:id|key|token)/i,
400
+ "ssn",
401
+ "social_security",
402
+ "dob",
403
+ "date_of_birth",
404
+ "passport",
405
+ "driver_license",
406
+ // Tax IDs
407
+ "tin",
408
+ "ein",
409
+ "itin",
410
+ "tax_id",
411
+ // Tokens / secrets / API auth
412
+ "token",
413
+ "tokens",
414
+ "secret",
415
+ "secrets",
416
+ "api_key",
417
+ "api_secret",
418
+ "api_token",
419
+ "private_key",
420
+ "bearer",
421
+ "oauth",
422
+ "auth_token",
423
+ "access_token",
424
+ "refresh_token",
425
+ "session_id",
426
+ "session_key",
427
+ "session_token",
426
428
  // MFA / OTP
427
- /\botp\b/i,
428
- /one[_\s-]?time[_\s-]?(?:password|code)/i,
429
- /mfa[_\s-]?(?:secret|seed|code|token)/i,
430
- /two[_\s-]?factor[_\s-]?(?:code|token)/i,
431
- /\b2fa[_\s-]?(?:code|token)?\b/i,
432
- /recovery[_\s-]?code/i,
433
- /backup[_\s-]?code/i
434
- ];
435
- function segmentMatchesSensitive(segment) {
436
- if (typeof segment !== "string") return false;
437
- for (const pattern of SENSITIVE_NAME_PATTERNS) {
438
- if (pattern.test(segment)) return true;
429
+ "otp",
430
+ "one_time_password",
431
+ "one_time_code",
432
+ "mfa_secret",
433
+ "mfa_seed",
434
+ "mfa_code",
435
+ "mfa_token",
436
+ "two_factor_code",
437
+ "two_factor_token",
438
+ "2fa",
439
+ "2fa_code",
440
+ "2fa_token",
441
+ "recovery_code",
442
+ "backup_code"
443
+ ]);
444
+ const WORD_BOUNDARY_THRESHOLD = 5;
445
+ function escapeRegex(s) {
446
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
447
+ }
448
+ function nameToRegex(name) {
449
+ const parts = name.split(/[_\s-]/).filter((p) => p.length > 0);
450
+ if (parts.length === 0) {
451
+ return /(?!)/;
439
452
  }
440
- return false;
441
- }
442
- function isSensitivePath(path) {
443
- if (typeof path !== "string") {
444
- for (const segment of path) {
445
- if (segmentMatchesSensitive(segment)) return true;
453
+ const escaped = parts.map(escapeRegex).join("[_\\s-]?");
454
+ const compactLength = parts.reduce((sum, p) => sum + p.length, 0);
455
+ const useBoundary = compactLength <= WORD_BOUNDARY_THRESHOLD;
456
+ const source = useBoundary ? `\\b${escaped}\\b` : escaped;
457
+ return new RegExp(source, "i");
458
+ }
459
+ function namesToPatterns(names) {
460
+ const patterns = [];
461
+ for (const name of names) {
462
+ if (typeof name !== "string" || name.length === 0) continue;
463
+ patterns.push(nameToRegex(name));
464
+ }
465
+ return patterns;
466
+ }
467
+ const DEFAULT_PATTERNS = namesToPatterns(DEFAULT_SENSITIVE_NAMES);
468
+ function createSegmentMatchesSensitive(names = DEFAULT_SENSITIVE_NAMES) {
469
+ const patterns = names === DEFAULT_SENSITIVE_NAMES ? DEFAULT_PATTERNS : namesToPatterns(names);
470
+ return (segment) => {
471
+ if (typeof segment !== "string") return false;
472
+ for (const p of patterns) {
473
+ if (p.test(segment)) return true;
446
474
  }
447
475
  return false;
448
- }
449
- if (path.startsWith("[")) {
450
- try {
451
- const parsed = JSON.parse(path);
452
- if (Array.isArray(parsed)) {
453
- for (const segment of parsed) {
454
- if (segmentMatchesSensitive(segment)) return true;
476
+ };
477
+ }
478
+ function createIsSensitivePath(names = DEFAULT_SENSITIVE_NAMES) {
479
+ const segmentMatches = createSegmentMatchesSensitive(names);
480
+ return (path) => {
481
+ if (typeof path !== "string") {
482
+ for (const segment of path) {
483
+ if (segmentMatches(segment)) return true;
484
+ }
485
+ return false;
486
+ }
487
+ if (path.startsWith("[")) {
488
+ try {
489
+ const parsed = JSON.parse(path);
490
+ if (Array.isArray(parsed)) {
491
+ for (const segment of parsed) {
492
+ if (segmentMatches(segment)) return true;
493
+ }
494
+ return false;
455
495
  }
456
- return false;
496
+ } catch {
457
497
  }
458
- } catch {
459
498
  }
460
- }
461
- for (const segment of path.split(".")) {
462
- if (segmentMatchesSensitive(segment)) return true;
463
- }
464
- return false;
499
+ for (const segment of path.split(".")) {
500
+ if (segmentMatches(segment)) return true;
501
+ }
502
+ return false;
503
+ };
465
504
  }
466
- function enforceSensitiveCheck(path, acknowledged) {
505
+ const defaultSegmentMatches = createSegmentMatchesSensitive();
506
+ const defaultIsSensitivePath = createIsSensitivePath();
507
+ function segmentMatchesSensitive(segment) {
508
+ return defaultSegmentMatches(segment);
509
+ }
510
+ function isSensitivePath(path) {
511
+ return defaultIsSensitivePath(path);
512
+ }
513
+ function enforceSensitiveCheck(path, acknowledged, isSensitive = defaultIsSensitivePath) {
467
514
  if (acknowledged) return;
468
- if (!isSensitivePath(path)) return;
515
+ if (!isSensitive(path)) return;
469
516
  throw new SensitivePersistFieldError(path);
470
517
  }
471
518
 
@@ -607,10 +654,25 @@ function syncPersistOptIn(el, value, oldValue) {
607
654
  }
608
655
  if (wantsOptIn) {
609
656
  const v = value;
610
- enforceSensitiveCheck(v.path, v.acknowledgeSensitive);
657
+ enforceSensitiveCheck(v.path, v.acknowledgeSensitive, v.isSensitivePath);
611
658
  v.persistOptIns.add(elementId, v.path);
612
659
  }
613
660
  }
661
+ function syncMultiTabOptOut(value, oldValue) {
662
+ const wasOptedOut = isRegisterValue(oldValue) && oldValue.unmarkNoSync !== void 0;
663
+ const wantsOptOut = isRegisterValue(value) && value.markNoSync !== void 0;
664
+ if (!wasOptedOut && !wantsOptOut) return;
665
+ if (wasOptedOut) {
666
+ const old = oldValue;
667
+ const samePath = wantsOptOut && value.path === old.path;
668
+ if (!samePath) old.unmarkNoSync?.();
669
+ }
670
+ if (wantsOptOut) {
671
+ const v = value;
672
+ const samePathOld = wasOptedOut && oldValue.path === v.path;
673
+ if (!samePathOld) v.markNoSync?.();
674
+ }
675
+ }
614
676
  function syncElementRegistration(el, value, oldValue) {
615
677
  const wasRegistered = isRegisterValue(oldValue);
616
678
  const isRegistered = isRegisterValue(value);
@@ -1059,6 +1121,7 @@ const warnedUnsupportedElements = __DEV__ ? /* @__PURE__ */ new WeakSet() : null
1059
1121
  const vRegisterDynamic = {
1060
1122
  created(el, binding, vnode) {
1061
1123
  syncPersistOptIn(el, binding.value, void 0);
1124
+ syncMultiTabOptOut(binding.value, void 0);
1062
1125
  callModelHook(el, binding, vnode, null, "created");
1063
1126
  if (__DEV__ && warnedUnsupportedElements !== null && !SUPPORTED_TAGS.has(el.tagName) && !warnedUnsupportedElements.has(el)) {
1064
1127
  void nextTick(() => {
@@ -1080,6 +1143,7 @@ const vRegisterDynamic = {
1080
1143
  },
1081
1144
  beforeUpdate(el, binding, vnode, prevVNode) {
1082
1145
  syncPersistOptIn(el, binding.value, binding.oldValue);
1146
+ syncMultiTabOptOut(binding.value, binding.oldValue);
1083
1147
  syncElementRegistration(el, binding.value, binding.oldValue);
1084
1148
  callModelHook(el, binding, vnode, prevVNode, "beforeUpdate");
1085
1149
  },
@@ -1090,6 +1154,7 @@ const vRegisterDynamic = {
1090
1154
  removeTrackedListeners(el);
1091
1155
  if (isRegisterValue(value)) {
1092
1156
  value.persistOptIns.removeAllFor(getOrAssignElementId(el));
1157
+ value.unmarkNoSync?.();
1093
1158
  }
1094
1159
  if (!isRegisterValue(value)) return;
1095
1160
  value.deregisterElement(el);
@@ -1165,5 +1230,5 @@ function createAttaform(options = {}) {
1165
1230
  return plugin;
1166
1231
  }
1167
1232
 
1168
- export { AnonPersistError as A, InvalidPathError as I, OutsideSetupError as O, RegistryNotInstalledError as R, SensitivePersistFieldError as S, __DEV__ as _, AttaformError as a, InvalidUseFormConfigError as b, createAttaform as c, ReservedFormKeyError as d, SubmitErrorHandlerError as e, assignKey as f, getRegistryFromApp as g, createRegistry as h, isSensitivePath as i, isRegisterValue as j, kAttaformRegistry as k, useRegistry as l, captureUserCallSite as m, enforceSensitiveCheck as n, createPersistOptInRegistry as o, ensureAttaformInstalled as p, kFormContext as q, kFormInstanceId as r, segmentMatchesSensitive as s, useRegister as u, vRegister as v };
1169
- //# sourceMappingURL=attaform.BfMxsfmE.mjs.map
1233
+ export { AnonPersistError as A, DEFAULT_SENSITIVE_NAMES as D, InvalidPathError as I, OutsideSetupError as O, RegistryNotInstalledError as R, SensitivePersistFieldError as S, __DEV__ as _, AttaformError as a, InvalidUseFormConfigError as b, createAttaform as c, ReservedFormKeyError as d, SubmitErrorHandlerError as e, assignKey as f, getRegistryFromApp as g, createRegistry as h, isRegisterValue as i, useRegistry as j, kAttaformRegistry as k, captureUserCallSite as l, enforceSensitiveCheck as m, isSensitivePath as n, createPersistOptInRegistry as o, ensureAttaformInstalled as p, kFormContext as q, kFormInstanceId as r, segmentMatchesSensitive as s, createIsSensitivePath as t, useRegister as u, vRegister as v, createSegmentMatchesSensitive as w };
1234
+ //# sourceMappingURL=attaform.CIEQgJnM.mjs.map