attaform 0.14.0 → 0.15.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.
- package/dist/chunks/devtools.cjs +3 -3
- package/dist/chunks/devtools.cjs.map +1 -1
- package/dist/chunks/devtools.mjs +3 -3
- package/dist/chunks/devtools.mjs.map +1 -1
- package/dist/chunks/indexeddb.cjs +1 -1
- package/dist/chunks/indexeddb.mjs +1 -1
- package/dist/chunks/local-storage.cjs +1 -1
- package/dist/chunks/local-storage.mjs +1 -1
- package/dist/chunks/session-storage.cjs +1 -1
- package/dist/chunks/session-storage.mjs +1 -1
- package/dist/index.cjs +5 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.mjs +6 -6
- package/dist/nuxt.d.cts +1 -1
- package/dist/nuxt.d.mts +1 -1
- package/dist/nuxt.d.ts +1 -1
- package/dist/runtime/plugins/attaform.cjs +1 -1
- package/dist/runtime/plugins/attaform.mjs +1 -1
- package/dist/shared/{attaform.DDXrY-1Q.d.mts → attaform.0Gxd_OOx.d.cts} +558 -174
- package/dist/shared/{attaform.DDXrY-1Q.d.ts → attaform.0Gxd_OOx.d.mts} +558 -174
- package/dist/shared/{attaform.DDXrY-1Q.d.cts → attaform.0Gxd_OOx.d.ts} +558 -174
- package/dist/shared/{attaform.xKWYHMdq.cjs → attaform.BOi138GE.cjs} +10 -2
- package/dist/shared/{attaform.xKWYHMdq.cjs.map → attaform.BOi138GE.cjs.map} +1 -1
- package/dist/shared/{attaform.CRgix6_n.cjs → attaform.BgYBU8gV.cjs} +18 -17
- package/dist/shared/attaform.BgYBU8gV.cjs.map +1 -0
- package/dist/shared/attaform.Bubm_slq.cjs.map +1 -1
- package/dist/shared/{attaform.CNJO3mME.cjs → attaform.CDJVeoJU.cjs} +633 -236
- package/dist/shared/attaform.CDJVeoJU.cjs.map +1 -0
- package/dist/shared/{attaform.CXZgUECn.d.cts → attaform.CPx7zTgS.d.mts} +39 -9
- package/dist/shared/{attaform.DlgKK10S.mjs → attaform.CRk8NhlD.mjs} +18 -17
- package/dist/shared/attaform.CRk8NhlD.mjs.map +1 -0
- package/dist/shared/attaform.CXpzmj38.mjs.map +1 -1
- package/dist/shared/{attaform.DOKOyb3Y.d.mts → attaform.D-eHWfVx.d.cts} +39 -9
- package/dist/shared/{attaform.Cc93zNzD.mjs → attaform.DXye3JKf.mjs} +10 -3
- package/dist/shared/{attaform.Cc93zNzD.mjs.map → attaform.DXye3JKf.mjs.map} +1 -1
- package/dist/shared/{attaform.B5GWYl76.cjs → attaform.RypIkgVy.cjs} +38 -7
- package/dist/shared/attaform.RypIkgVy.cjs.map +1 -0
- package/dist/shared/{attaform.al_rpt7_.mjs → attaform.a99dQV7Q.mjs} +39 -8
- package/dist/shared/attaform.a99dQV7Q.mjs.map +1 -0
- package/dist/shared/{attaform.BRTxpA3q.mjs → attaform.qxyip_aN.mjs} +634 -238
- package/dist/shared/attaform.qxyip_aN.mjs.map +1 -0
- package/dist/shared/{attaform.BYc9kugA.d.ts → attaform.riAENZQM.d.ts} +39 -9
- package/dist/transforms.d.cts +2 -2
- package/dist/transforms.d.mts +2 -2
- package/dist/transforms.d.ts +2 -2
- package/dist/zod-v3.cjs +55 -3
- package/dist/zod-v3.cjs.map +1 -1
- package/dist/zod-v3.d.cts +77 -4
- package/dist/zod-v3.d.mts +77 -4
- package/dist/zod-v3.d.ts +77 -4
- package/dist/zod-v3.mjs +56 -6
- package/dist/zod-v3.mjs.map +1 -1
- package/dist/zod.cjs +372 -5
- package/dist/zod.cjs.map +1 -1
- package/dist/zod.d.cts +120 -4
- package/dist/zod.d.mts +120 -4
- package/dist/zod.d.ts +120 -4
- package/dist/zod.mjs +371 -8
- package/dist/zod.mjs.map +1 -1
- package/package.json +3 -1
- package/dist/shared/attaform.B5GWYl76.cjs.map +0 -1
- package/dist/shared/attaform.BRTxpA3q.mjs.map +0 -1
- package/dist/shared/attaform.CNJO3mME.cjs.map +0 -1
- package/dist/shared/attaform.CRgix6_n.cjs.map +0 -1
- package/dist/shared/attaform.DlgKK10S.mjs.map +0 -1
- package/dist/shared/attaform.al_rpt7_.mjs.map +0 -1
|
@@ -72,7 +72,7 @@ const kAttaformRegistry = Symbol.for("attaform:registry");
|
|
|
72
72
|
const kFormContext = Symbol.for("attaform:form-context");
|
|
73
73
|
const kFormInstanceId = Symbol.for("attaform:form-instance-id");
|
|
74
74
|
function createRegistry(options = {}) {
|
|
75
|
-
const
|
|
75
|
+
const ssr = detectSSR(options);
|
|
76
76
|
const defaults = Object.freeze({ ...options.defaults ?? {} });
|
|
77
77
|
const forms = vue.shallowReactive(/* @__PURE__ */ new Map());
|
|
78
78
|
const pendingHydration = vue.shallowReactive(/* @__PURE__ */ new Map());
|
|
@@ -105,7 +105,7 @@ function createRegistry(options = {}) {
|
|
|
105
105
|
const states = [...forms.values(), ...evicting];
|
|
106
106
|
await Promise.allSettled(states.map((state) => state.awaitPendingWrites()));
|
|
107
107
|
}
|
|
108
|
-
return { forms, pendingHydration,
|
|
108
|
+
return { forms, pendingHydration, ssr, defaults, trackConsumer, shutdown };
|
|
109
109
|
}
|
|
110
110
|
function useRegistry() {
|
|
111
111
|
const instance = vue.getCurrentInstance();
|
|
@@ -160,11 +160,42 @@ function shortenSourceFrame(frame) {
|
|
|
160
160
|
const REGISTER_OWNER_MARKER = Symbol.for("attaform:register-owner-marker");
|
|
161
161
|
const warnedNoParentRV = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
|
|
162
162
|
let warnedOutsideSetup = false;
|
|
163
|
+
function makeRegisterValueProxy(capturedRegisterValue) {
|
|
164
|
+
return new Proxy({}, {
|
|
165
|
+
get(_target, prop) {
|
|
166
|
+
if (prop === "__v_isRef") return true;
|
|
167
|
+
if (prop === "value") return capturedRegisterValue.value;
|
|
168
|
+
const v = capturedRegisterValue.value;
|
|
169
|
+
if (v === void 0) return void 0;
|
|
170
|
+
return Reflect.get(v, prop);
|
|
171
|
+
},
|
|
172
|
+
has(_target, prop) {
|
|
173
|
+
if (prop === "__v_isRef" || prop === "value") return true;
|
|
174
|
+
const v = capturedRegisterValue.value;
|
|
175
|
+
if (v === void 0) return false;
|
|
176
|
+
return Reflect.has(v, prop);
|
|
177
|
+
},
|
|
178
|
+
ownKeys(_target) {
|
|
179
|
+
const v = capturedRegisterValue.value;
|
|
180
|
+
if (v === void 0) return [];
|
|
181
|
+
return Reflect.ownKeys(v);
|
|
182
|
+
},
|
|
183
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
184
|
+
const v = capturedRegisterValue.value;
|
|
185
|
+
if (v === void 0) return void 0;
|
|
186
|
+
const desc = Reflect.getOwnPropertyDescriptor(v, prop);
|
|
187
|
+
if (desc !== void 0) {
|
|
188
|
+
desc.configurable = true;
|
|
189
|
+
}
|
|
190
|
+
return desc;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
163
194
|
function useRegister() {
|
|
164
195
|
const instance = vue.getCurrentInstance();
|
|
165
196
|
if (instance === null) {
|
|
166
197
|
warnOutsideSetup();
|
|
167
|
-
return vue.
|
|
198
|
+
return makeRegisterValueProxy(vue.shallowRef(void 0));
|
|
168
199
|
}
|
|
169
200
|
const capturedRegisterValue = vue.shallowRef(void 0);
|
|
170
201
|
const refreshAndStripBridgeAttrs = () => {
|
|
@@ -187,7 +218,7 @@ function useRegister() {
|
|
|
187
218
|
warnNoParentRV(instance);
|
|
188
219
|
}
|
|
189
220
|
});
|
|
190
|
-
return
|
|
221
|
+
return makeRegisterValueProxy(capturedRegisterValue);
|
|
191
222
|
}
|
|
192
223
|
function warnOutsideSetup() {
|
|
193
224
|
if (!__DEV__) return;
|
|
@@ -195,7 +226,7 @@ function warnOutsideSetup() {
|
|
|
195
226
|
warnedOutsideSetup = true;
|
|
196
227
|
const frame = captureUserCallSite();
|
|
197
228
|
console.warn(
|
|
198
|
-
`[attaform] useRegister() called outside a component setup; returning
|
|
229
|
+
`[attaform] useRegister() called outside a component setup; returning an unbound RegisterValue proxy. Fix: call it inside <script setup> or a setup() function \u2014 not from an event handler or async callback.` + (frame !== void 0 ? ` ${frame}` : "")
|
|
199
230
|
);
|
|
200
231
|
}
|
|
201
232
|
function warnNoParentRV(instance) {
|
|
@@ -204,7 +235,7 @@ function warnNoParentRV(instance) {
|
|
|
204
235
|
warnedNoParentRV.add(instance);
|
|
205
236
|
const frame = captureUserCallSite();
|
|
206
237
|
console.warn(
|
|
207
|
-
`[attaform] useRegister: no parent registerValue prop;
|
|
238
|
+
`[attaform] useRegister: no parent registerValue prop; RegisterValue fields will read as undefined. Pass v-register on the parent: \`<YourComponent v-register="form.register('field')" />\`.` + (frame !== void 0 ? ` ${frame}` : "")
|
|
208
239
|
);
|
|
209
240
|
}
|
|
210
241
|
|
|
@@ -383,4 +414,4 @@ exports.kFormInstanceId = kFormInstanceId;
|
|
|
383
414
|
exports.segmentMatchesSensitive = segmentMatchesSensitive;
|
|
384
415
|
exports.useRegister = useRegister;
|
|
385
416
|
exports.useRegistry = useRegistry;
|
|
386
|
-
//# sourceMappingURL=attaform.
|
|
417
|
+
//# sourceMappingURL=attaform.RypIkgVy.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attaform.RypIkgVy.cjs","sources":["../../src/runtime/core/dev.ts","../../src/runtime/core/errors.ts","../../src/runtime/core/ssr.ts","../../src/runtime/core/registry.ts","../../src/runtime/core/dev-stack-trace.ts","../../src/runtime/composables/use-register.ts","../../src/runtime/core/persistence/opt-in-registry.ts","../../src/runtime/core/persistence/sensitive-names.ts"],"sourcesContent":["/**\n * Portable dev-mode flag. True when the consumer's bundle / runtime\n * signals a non-production build; false in production.\n *\n * Resolves in this order:\n * 1. `process.env.NODE_ENV` — replaced at build time by Vite,\n * Webpack, Rollup + `@rollup/plugin-replace`, and read directly\n * in Node.\n * 2. Falls back to `false` when `process` is undeclared (some\n * sandboxed runtimes).\n *\n * Using this instead of `import.meta.dev` (Vite / Nuxt-specific)\n * keeps the library portable across bundlers and avoids esbuild's\n * `empty-import-meta` warning in non-ESM contexts.\n *\n * **Trade-off (browser CDN consumers).** When the library is\n * imported directly via a browser-native ESM CDN (esm.sh, Skypack,\n * unpkg) WITHOUT a bundler in front, `process` is undeclared and\n * `__DEV__` permanently resolves to `false` — every dev-only warning\n * is silenced even when the consumer is debugging. The library\n * works correctly; only the diagnostic surface degrades. The fix is\n * to put a bundler (Vite, Webpack, Rollup, esbuild) in the consumer\n * pipeline so `process.env.NODE_ENV` gets replaced. This is the\n * recommended path for any production app; CDN imports are useful\n * for prototyping but lose tree-shaking + dev diagnostics either way.\n *\n * Switching to `import.meta.env.DEV` would resolve correctly under\n * Vite but break Node consumers (no `import.meta.env`) and\n * pre-bundled distributions (esbuild emits an `empty-import-meta`\n * warning when `import.meta` resolves to `{}`). The current\n * `process.env.NODE_ENV` choice is the broadest-compatibility option.\n */\nexport const __DEV__: boolean =\n typeof process !== 'undefined' && process.env['NODE_ENV'] !== 'production'\n","/**\n * Typed error classes thrown by the form library. Each one signals a\n * distinct misuse so calling code can branch on `instanceof` instead\n * of pattern-matching error messages.\n *\n * Every class extends `AttaformError`, so consumers can write a single\n * polymorphic catch (`catch (e) { if (e instanceof AttaformError) ... }`)\n * instead of OR-chaining checks for each subclass. `AttaformError` itself\n * extends the standard `Error`, so existing `instanceof Error` usage\n * keeps working.\n */\n\n/**\n * Base for every error class thrown by `attaform`. Sets\n * `this.name` from the constructor's `new.target.name`, so subclasses\n * don't have to redeclare their own name override.\n */\nexport class AttaformError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options)\n this.name = new.target.name\n }\n}\n\n/**\n * Thrown when a path string is malformed — typically a dotted path\n * with empty segments (e.g. `'a..b'`, leading or trailing dots).\n * Use array form (`['a', 'b']`) for keys that contain literal dots.\n */\nexport class InvalidPathError extends AttaformError {}\n\n/**\n * Thrown when a `handleSubmit`-supplied `onError` callback itself\n * throws or rejects. Wraps the inner failure so both the original\n * cause (via `error.cause`) and the propagation site are visible.\n */\nexport class SubmitErrorHandlerError extends AttaformError {}\n\n/**\n * Thrown by `useForm` / `injectForm` when the form library's\n * plugin hasn't been installed on the current Vue app.\n *\n * Fix: add `app.use(createAttaform())` to your app entry\n * (or `attaform/nuxt` for Nuxt projects).\n */\nexport class RegistryNotInstalledError extends AttaformError {\n constructor() {\n super('[attaform] Registry not found. Install the plugin via `app.use(createAttaform())`.')\n }\n}\n\n/**\n * Thrown when `useForm` / `injectForm` is called outside of a\n * Vue `setup()` function — typically from an event handler, watcher,\n * or async callback that runs after mount.\n *\n * Fix: move the call into `setup()`, or trigger it from a child\n * component whose `setup()` runs the composable.\n */\nexport class OutsideSetupError extends AttaformError {\n constructor() {\n super(\n '[attaform] useForm / injectForm called outside Vue setup(). ' +\n 'Move into setup or mount a child component to trigger from an event.'\n )\n }\n}\n\n/**\n * Thrown when a `useForm({ key })` call uses a key starting with\n * `__atta:`. That prefix is reserved for keys the library generates\n * internally (e.g. for anonymous `useForm()` calls without an\n * explicit key). Pick a different prefix for your form.\n */\nexport class ReservedFormKeyError extends AttaformError {\n constructor(key: string) {\n super(\n `[attaform] Form key \"${key}\" uses the reserved \"__atta:\" namespace. ` +\n `Use a different prefix — \"__atta:\" is for library-internal synthetic keys ` +\n `(anonymous useForm() calls without an explicit key).`\n )\n }\n}\n\n/**\n * Thrown (in dev) when `useForm({ persist: ... })` is configured on\n * an anonymous form (no `key:` provided). The synthetic `__atta:anon:`\n * identity isn't stable across remounts (Vue's `useId()` allocator\n * drifts under HMR, and any sibling `useId()` call shifts subsequent\n * IDs), so the persistence layer can't reliably find the previous\n * mount's draft. Result: stale entries pile up in storage and the\n * user's most recent edit doesn't always come back.\n *\n * Fix: pass an explicit `key` to `useForm()`.\n *\n * In production builds the runtime downgrades this to a one-shot\n * `console.warn` so a deployed third-party app shipping the\n * anti-pattern doesn't hard-crash.\n */\n// (AnonPersistError class declaration is below; this docblock is the\n// historical preamble — kept here so blame/PR review can trace the\n// original intent. The richer class supersedes the earlier basic version.)\n\n/**\n * Thrown when `register(path, { persist: true })` or `form.persist(path)`\n * targets a path whose name matches a sensitive-data heuristic\n * (password, cvv, ssn, token, etc.) without an explicit\n * `acknowledgeSensitive: true` override.\n *\n * Sensitive data in client-side storage (localStorage, sessionStorage,\n * IndexedDB) is a compliance risk — it survives logouts, is readable\n * by any same-origin script, and is unencrypted at rest.\n *\n * Fix: pass `acknowledgeSensitive: true` to confirm the persistence\n * is intentional, or persist the data server-side instead.\n */\nexport class SensitivePersistFieldError extends AttaformError {\n constructor(path: ReadonlyArray<string | number> | string) {\n const display = Array.isArray(path) ? path.join('.') : String(path)\n super(\n `[attaform] Refusing to persist \"${display}\" — this path matches a ` +\n `sensitive-name pattern (password / cvv / ssn / token / etc.). Storing sensitive ` +\n `data in client-side storage is a compliance risk (HIPAA / PII / PCI-DSS / SOC2). ` +\n `Fix: persist this server-side, OR pass \\`acknowledgeSensitive: true\\` to register() ` +\n `(or form.persist()) if the client-side persistence is intentional.`\n )\n }\n}\n\n/**\n * Thrown when persistence is misconfigured in a way that would either\n * (a) silently drop writes, or (b) namespace storage under a\n * non-deterministic synthetic key — both of which become security bugs\n * the moment encrypted persistence backends are added (the same key\n * may be derived for two unrelated forms).\n *\n * Two `cause` values, one error shape:\n *\n * - `'no-key'` — `useForm({ persist: ... })` called without `key:`.\n * Anonymous keys (`__atta:anon:*`) drift across mounts; persisting\n * to a non-deterministic location is refused outright.\n *\n * - `'register-without-config'` — `register(_, { persist: true })`\n * declared on a form whose `useForm()` options omit `persist:`.\n * The opt-in is recorded but nothing would ever land in storage.\n *\n * Fix: align the two layers — set `persist:` + `key:` on `useForm()`,\n * or remove `{ persist: true }` from the offending `register()` call.\n */\nexport class AnonPersistError extends AttaformError {\n readonly schemaFields: readonly string[] | undefined\n readonly callSite: string | undefined\n override readonly cause: 'no-key' | 'register-without-config'\n constructor(opts: {\n schemaFields?: readonly string[] | undefined\n callSite?: string | undefined\n cause: 'no-key' | 'register-without-config'\n }) {\n super(formatAnonPersistMessage(opts))\n this.schemaFields = opts.schemaFields\n this.callSite = opts.callSite\n this.cause = opts.cause\n }\n}\n\nfunction formatAnonPersistMessage(opts: {\n schemaFields?: readonly string[] | undefined\n callSite?: string | undefined\n cause: 'no-key' | 'register-without-config'\n}): string {\n const head =\n opts.cause === 'no-key'\n ? `useForm({ persist: ... }) requires an explicit \\`key:\\`. Anonymous synthetic keys (\\`__atta:anon:*\\`) drift across mounts and can collide between unrelated forms — refusing to persist to a non-deterministic location.`\n : `register(_, { persist: true }) declared on a form whose useForm() options have no \\`persist:\\` configured. The opt-in is recorded but nothing would ever land in storage.`\n const fields =\n opts.schemaFields !== undefined && opts.schemaFields.length > 0\n ? ` Form fields: { ${opts.schemaFields.join(', ')} }.`\n : ''\n const fix =\n opts.cause === 'no-key'\n ? ` Fix: add \\`key: '<stable-id>'\\` to useForm().`\n : ` Fix: add \\`persist: 'session'\\` (or 'local') and \\`key:\\` to useForm(), or remove \\`{ persist: true }\\` from this register() call.`\n const where = opts.callSite !== undefined ? ` ${opts.callSite}` : ''\n return `[attaform] ${head}${fields}${fix}${where}`\n}\n","/**\n * Portable SSR detection. The plugin captures this value at install time and\n * exposes it via the registry so every runtime branch reads a single source\n * of truth instead of sniffing `import.meta.*` (bundler-specific) at each\n * call site.\n *\n * Consumers can override explicitly via `createAttaform({ ssr: true })`;\n * the default heuristic handles the common Node-vs-browser split without\n * relying on any bundler-injected flag.\n */\n\nexport interface SSRDetectOptions {\n override?: boolean\n}\n\n/**\n * Returns true when running in a server-rendering context (no `window` / no\n * `document`). Explicit override always wins.\n *\n * Note: JSDOM-based test environments define `window`, so tests that need to\n * exercise SSR code paths must pass `{ override: true }` explicitly.\n */\nexport function detectSSR(options: SSRDetectOptions = {}): boolean {\n if (options.override !== undefined) return options.override\n return typeof window === 'undefined' && typeof document === 'undefined'\n}\n","import type { App, InjectionKey } from 'vue'\nimport { getCurrentInstance, inject, shallowReactive } from 'vue'\nimport type { AttaformDefaults, FormKey } from '../types/types-api'\nimport type { GenericForm } from '../types/types-core'\nimport type { FormStore } from './create-form-store'\nimport { OutsideSetupError, RegistryNotInstalledError } from './errors'\nimport { detectSSR, type SSRDetectOptions } from './ssr'\n\n/**\n * Per-Vue-app container for all form state instances. Each\n * `app.use(createAttaform())` call gets its own registry,\n * so the library runs under bare Vue 3 + SSR (via\n * `@vue/server-renderer`) and Nuxt with the same code path.\n *\n * Each form's state lives in `forms: Map<FormKey, FormStore<GenericForm>>`.\n * The type relaxation at storage time is necessary because different\n * forms in the same app have different `Form` generics; callers recover\n * the specific form type via `useForm`'s overloads.\n */\n\n/**\n * Serialised snapshot of one form's state, captured by\n * `renderAttaformState` for SSR and replayed by\n * `hydrateAttaformState` on the client. Round-trips through\n * JSON-safe tuples; field references are intentionally omitted\n * (DOM nodes don't survive serialisation).\n */\nexport type SerializedFormData = {\n /** The form's value at snapshot time. */\n readonly form: unknown\n /**\n * Errors produced by the schema at snapshot time. Replayed into\n * the client form's error state at hydration; cleared on\n * successful re-validation client-side.\n */\n readonly schemaErrors: ReadonlyArray<readonly [string, unknown]>\n /**\n * Errors set explicitly via `setFieldErrors` / `addFieldErrors`\n * (typically from a server response parsed via `parseApiErrors`)\n * at snapshot time. Replayed at hydration; persists across\n * client-side re-validation.\n */\n readonly userErrors: ReadonlyArray<readonly [string, unknown]>\n /** Per-field metadata (timestamps, raw values, connection flags) captured at snapshot time. */\n readonly fields: ReadonlyArray<readonly [string, unknown]>\n /**\n * Path keys that were in the form's `blankPaths` set at\n * snapshot time. Round-trips the \"displayed empty\" UI state across\n * the SSR boundary — without it, the client briefly renders\n * `String(slim-default)` (e.g. `'0'`) for fields the server\n * rendered as blank. Optional in the wire format so older payload\n * shapes deserialise cleanly.\n */\n readonly blankPaths?: ReadonlyArray<string>\n}\n\nexport type PendingHydration = Map<FormKey, SerializedFormData>\n\n/**\n * The library's per-Vue-app container. One `AttaformRegistry` is\n * created per `app.use(createAttaform())` call.\n *\n * Most consumers never touch this directly — `useForm` and\n * `injectForm` reach the registry on your behalf. Access it\n * explicitly only when wiring SSR or a custom plugin integration.\n */\nexport type AttaformRegistry = {\n /**\n * Live forms keyed by `FormKey`.\n * @internal\n */\n readonly forms: Map<FormKey, FormStore<GenericForm>>\n /**\n * Snapshots staged by `hydrateAttaformState` waiting to be consumed by the next `useForm` call.\n * @internal\n */\n readonly pendingHydration: PendingHydration\n /** `true` while running on the server during SSR; `false` on the client. */\n readonly ssr: boolean\n /** App-level defaults applied to every `useForm` call. */\n readonly defaults: AttaformDefaults\n /**\n * Track a consumer of `key`. Returns a dispose function — call it\n * when the consumer unmounts. The form is evicted automatically\n * when the last consumer disposes, so long-running SPAs don't\n * leak detached state across navigations.\n * @internal\n */\n readonly trackConsumer: (key: FormKey) => () => void\n /**\n * Wait for all pending persistence writes across every live form\n * to settle. Useful for SSR shutdown and integration tests that\n * need a deterministic teardown.\n * @internal\n */\n readonly shutdown: () => Promise<void>\n}\n\n/**\n * The Vue `InjectionKey` under which the registry is provided on the\n * app. Most consumers never need this — `useForm` and\n * `injectForm` resolve the registry automatically.\n */\n// `Symbol.for(...)` so the key survives module duplication. If Vite's\n// dep optimizer ends up serving attaform as two separate copies (one\n// live-ESM, one pre-bundled — the standard hazard for linked-source\n// installs that opt into `optimizeDeps.include`), each copy still\n// resolves the same global symbol from the well-known string. Plugin\n// install's `app.provide(kAttaformRegistry, ...)` and the page's\n// `inject(kAttaformRegistry, null)` agree on the key, so `useForm`\n// finds its registry regardless of which copy did the provide. The\n// `attaform:` prefix namespaces the key safely. Same reasoning\n// for `kFormContext` and `kFormInstanceId` below.\nexport const kAttaformRegistry: InjectionKey<AttaformRegistry> = Symbol.for('attaform:registry')\n\n/**\n * Provides the current form's FormStore to descendants. Installed by\n * `useAbstractForm` after it resolves the state, so any nested component\n * can call `injectForm()` without prop-threading the form API.\n *\n * Typed as `FormStore<GenericForm>` — the descendant that re-emerges the\n * shape must supply its own `Form` generic, because Vue's InjectionKey\n * erases the generic at the provide/inject boundary.\n */\nexport const kFormContext: InjectionKey<FormStore<GenericForm>> =\n Symbol.for('attaform:form-context')\n\n/**\n * Provide / inject key for the per-`useForm()`-call instance ID. Provided\n * alongside `kFormContext` so descendants reaching via `injectForm()`\n * inherit the ancestor's `formInstanceId` and their locally-registered\n * elements tag against the SAME instance — keeps parent-submit-focus\n * working for inputs registered by deep children.\n *\n * Sibling `useForm({ key })` calls (e.g. sidebar + main rendering the\n * same form) sit at distinct tree positions, so each provides its own\n * ID; descendants of each branch inherit the branch's ID. Two ID spaces\n * stay isolated even when the underlying FormStore is shared.\n */\nexport const kFormInstanceId: InjectionKey<string> = Symbol.for('attaform:form-instance-id')\n\ndeclare module 'vue' {\n interface App {\n /** @internal */\n _attaform?: AttaformRegistry\n }\n}\n\n/** Options for `createRegistry`. */\nexport type CreateRegistryOptions = SSRDetectOptions & {\n /**\n * App-level defaults applied to every `useForm` call. Per-form\n * options always win. Omitted is equivalent to `{}`.\n */\n defaults?: AttaformDefaults\n}\n\n/**\n * Create a fresh `AttaformRegistry`. `createAttaform()` calls\n * this internally — most consumers never need to call it directly.\n * Use it when building a custom plugin that doesn't want the\n * `createAttaform` plugin's auto-install behaviour (e.g. test\n * harnesses, embedded apps).\n */\nexport function createRegistry(options: CreateRegistryOptions = {}): AttaformRegistry {\n const ssr = detectSSR(options)\n // Frozen so accidental writes downstream throw in dev. Public surface\n // (`createAttaform({ defaults })`) treats this as data, not as\n // a mutation point — there's no public API to update defaults after\n // install, and adding one would invite race conditions with already-\n // mounted forms.\n const defaults: AttaformDefaults = Object.freeze({ ...(options.defaults ?? {}) })\n // The outer object is plain (it holds references we never rebind); inner\n // Maps are reactive via Vue's collection handlers so per-key reads track\n // per-key. `shallowReactive` avoids Vue's deep Ref-unwrapping, which would\n // mangle FormStore.form's Ref<F> type into F on lookup.\n const forms = shallowReactive(new Map<FormKey, FormStore<GenericForm>>())\n const pendingHydration = shallowReactive(new Map<FormKey, SerializedFormData>())\n // Consumer counts are bookkeeping — not reactive. No template should ever\n // depend on \"how many useForm calls are live\", and using a plain Map\n // avoids triggering watchers when we increment on every mount.\n const consumers = new Map<FormKey, number>()\n\n // Stores that have been evicted from `forms` but still have a\n // pending drain. `shutdown()` awaits these too so a process-exit\n // hook doesn't tear down before debounced writes from already-\n // unmounted forms have a chance to flush.\n const evicting = new Set<FormStore<GenericForm>>()\n\n function trackConsumer(key: FormKey): () => void {\n consumers.set(key, (consumers.get(key) ?? 0) + 1)\n let disposed = false\n return () => {\n if (disposed) return\n disposed = true\n const remaining = (consumers.get(key) ?? 1) - 1\n if (remaining <= 0) {\n // Tear down non-reactive resources the FormStore owns (field-\n // validation timers, abort controllers) BEFORE dropping the\n // registry reference — once the Map entry is gone we can't\n // reach the state anymore.\n const state = forms.get(key)\n consumers.delete(key)\n // Eviction from `forms` stays synchronous: any consumer that\n // reads `registry.forms` after unmount (tests, devtools) sees\n // the form gone immediately. Drain-then-dispose runs async in\n // the background so the persistence layer's debounced final\n // write can complete — the FormStore is reachable through the\n // closure here even after `forms.delete`.\n forms.delete(key)\n if (state !== undefined) {\n evicting.add(state)\n void state\n .awaitPendingWrites()\n .catch(() => undefined)\n .finally(() => {\n evicting.delete(state)\n state.dispose()\n })\n }\n } else {\n consumers.set(key, remaining)\n }\n }\n }\n\n async function shutdown(): Promise<void> {\n // Snapshot the keys — `awaitPendingWrites` may resolve mid-iteration\n // and trigger eviction that mutates `forms` while we're walking.\n // Include the evicting set so in-flight drains from already-\n // unmounted forms also flush before shutdown returns.\n const states = [...forms.values(), ...evicting]\n await Promise.allSettled(states.map((state) => state.awaitPendingWrites()))\n }\n\n return { forms, pendingHydration, ssr, defaults, trackConsumer, shutdown }\n}\n\n/**\n * Look up the current app's registry from inside a component's\n * `setup()` (or any synchronous code on the setup call stack).\n *\n * Most consumers don't need this — `useForm` and `injectForm`\n * call it on your behalf. Reach for it directly when building\n * custom integrations that need the raw registry.\n *\n * Throws:\n * - `OutsideSetupError` when called outside a Vue setup context\n * (e.g. from an event handler or async callback). Move the call\n * into setup, or trigger it from a child component.\n * - `RegistryNotInstalledError` when called inside setup but the\n * plugin wasn't installed. Add\n * `app.use(createAttaform())` to your app entry.\n */\nexport function useRegistry(): AttaformRegistry {\n const instance = getCurrentInstance()\n if (instance === null) {\n throw new OutsideSetupError()\n }\n const registry = inject(kAttaformRegistry, null)\n if (registry === null) {\n throw new RegistryNotInstalledError()\n }\n return registry\n}\n\n/**\n * Look up a Vue app's registry by `App` reference. Used by\n * SSR helpers (`renderAttaformState`, `hydrateAttaformState`) that\n * run outside a component setup context.\n *\n * Throws `RegistryNotInstalledError` when the app hasn't been wired\n * with `createAttaform()`.\n */\nexport function getRegistryFromApp(app: App): AttaformRegistry {\n const registry = app._attaform\n if (registry === undefined) {\n throw new RegistryNotInstalledError()\n }\n return registry\n}\n\nexport function attachRegistryToApp(app: App, registry: AttaformRegistry): void {\n app.provide(kAttaformRegistry, registry)\n app._attaform = registry\n}\n","/**\n * Dev-only call-site capture for warnings that want to point the\n * reader at the offending line of their code (not at a attaform-internal\n * frame). Walks the stack past `attaform` frames, picks the\n * first frame that looks like user code, then strips the dev-server\n * scheme + host + Vite/Nuxt's `/_nuxt/` prefix so the warning doesn't\n * carry a wall of `https://localhost:3000/_nuxt/...` noise.\n *\n * Returns `undefined` on engines that don't expose `.stack` or when\n * parsing fails — callers should degrade to a generic message rather\n * than printing nothing.\n *\n * Click-through navigation isn't sacrificed: `console.warn` already\n * renders its own clickable stack trace below the message in\n * Chrome / Firefox DevTools (V8 frame format → Sources tab). The\n * captured frame is purely an inline pointer in the message text,\n * and short paths read better there than full URLs.\n *\n * The attaform-frame regex matches both the published path\n * (`attaform/...`) and the linked / source path\n * (`attaform/...`) so local dev via `make link-attaform` surfaces\n * the same trimmed frames.\n *\n * Dev-only; callers should gate on `__DEV__` before invoking.\n */\nexport function captureUserCallSite(): string | undefined {\n const raw = new Error().stack\n if (typeof raw !== 'string') return undefined\n const lines = raw.split('\\n')\n // Skip the \"Error\" message line and any frame inside attaform itself.\n for (let i = 1; i < lines.length; i++) {\n const frame = lines[i]\n if (frame === undefined) continue\n if (/attaform[/-]forms?/i.test(frame)) continue\n if (/\\bforms\\.[A-Za-z0-9_-]+\\.m?js\\b/.test(frame)) continue\n const trimmed = frame.trim()\n if (trimmed.length === 0) continue\n return shortenSourceFrame(trimmed)\n }\n return undefined\n}\n\n/**\n * Reduce a raw stack frame to `(<path>:<line>)`.\n *\n * Inputs we expect (V8, with or without `at fn (…)` wrapper):\n * - `at setup (https://example.test/_nuxt/pages/spike.vue:18:18)`\n * - `at https://example.com/foo.js:1:1`\n * - `at file:///Users/x/proj/spike.vue:18:18`\n * - `pages/foo.vue:18:18` (already path-like, no V8 wrapper)\n *\n * Outputs:\n * - `(pages/spike.vue:18)`\n * - `(foo.js:1)`\n * - `(Users/x/proj/spike.vue:18)`\n * - `(pages/foo.vue:18)`\n *\n * Why drop the column: Vite's sourcemaps round-trip line accurately\n * but column resolution is fuzzy in compiled contexts (Vue render\n * functions, JSX, anywhere the source-to-output mapping isn't\n * 1-to-1 per character). For a script-setup `useForm()` call the\n * column is meaningful; for a template-inlined `register(...)` it\n * lands somewhere mid-compiled-blob and is actively misleading. The\n * uniform `path:line` format avoids that asymmetry — line is enough\n * to navigate, the editor lands on the right region either way.\n *\n * If the frame doesn't match the trailing `…:line:col` shape at all,\n * the original trimmed frame is returned unchanged — better to\n * surface something than nothing.\n */\nexport function shortenSourceFrame(frame: string): string {\n const match = /(?:^|\\s|\\()([^\\s()]+):(\\d+):\\d+\\)?$/.exec(frame)\n if (match === null) return frame\n const [, urlOrPath, line] = match\n if (urlOrPath === undefined || line === undefined) return frame\n let path = urlOrPath\n // Strip `scheme://host/` (https://…, http://…). file:// gets the\n // same treatment, leaving the absolute filesystem path; we then\n // also strip its leading slash below so it reads as a relative path.\n path = path.replace(/^[a-z]+:\\/\\/[^/]+\\//i, '')\n // Strip Vite/Nuxt's dev-server prefix.\n path = path.replace(/^_nuxt\\//, '')\n // Strip leading slash (left over from file:// or absolute paths).\n path = path.replace(/^\\//, '')\n // Wrap in parens. Chrome's console auto-linker partial-matches\n // bare `pages/foo.vue:137` (it picks up `/foo.vue:137` and\n // drops the `pages` prefix). Parens are the V8 stack-frame\n // convention and Chrome reliably auto-links them end-to-end.\n return `(${path}:${line})`\n}\n","/**\n * Re-bind a parent's `v-register` onto an inner native element. Use\n * inside a component that wraps a single form field whose root is\n * NOT the input itself (e.g. a labelled-row that renders `<label>`\n * around the input).\n *\n * ```vue\n * <!-- Parent -->\n * <MyInput v-register=\"form.register('email')\" />\n *\n * <!-- MyInput.vue -->\n * <script setup lang=\"ts\">\n * import { useRegister } from 'attaform'\n * const rv = useRegister()\n * // rv.path / rv.segments / rv.formKey / rv.formInstanceId / rv.innerRef\n * // are all reachable directly — no `.value` unwrap.\n * </script>\n *\n * <template>\n * <label class=\"field\">\n * <span>Email</span>\n * <input v-register=\"rv\" />\n * </label>\n * </template>\n * ```\n *\n * Returns a hybrid Proxy: it answers `__v_isRef` / `.value` like a\n * Vue `Ref<RegisterValue | undefined>` (so templates auto-unwrap\n * correctly and `v-register=\"rv\"` feeds the underlying RV to the\n * directive — preserving the directive's path-migration diff across\n * renders), AND every other property read pierces to the captured\n * RV's field (so `rv.path` works directly in script setup). Reads\n * inside reactive scopes (`computed` / `watchEffect`) track the\n * underlying `shallowRef`, so `rv.path` re-runs when the parent\n * rebinds to a different path.\n *\n * Unbound state: when the parent didn't pass `v-register`, every\n * piercing read returns `undefined` at runtime even though the type\n * says otherwise. The composable's `onMounted` warn fires once per\n * instance to flag this misuse — the type \"lies\" because the bound\n * case is the only correct one, and forcing every consumer through\n * a `T | undefined` narrow at every property access is a worse\n * trade than the runtime warn.\n *\n * Diagnostic: in dev mode, a single `console.warn` fires per instance\n * at `onMounted` if the captured value is still `undefined` — by then\n * the parent has had its full mount lifecycle to bind, so a missing\n * binding is conclusive misuse. The warn does NOT fire on every read\n * of the proxy, and is intentionally silent under SSR\n * (`renderToString` skips `onMounted`); the CSR hydration pass\n * surfaces the same diagnostic without double-counting through Nuxt's\n * `dev:ssr-logs` channel.\n *\n * When the wrapper's root IS the input itself, Vue's attribute\n * fallthrough handles the binding and `useRegister` is unnecessary.\n * For wrappers that bind multiple fields (compound forms), use\n * `injectForm<Form>(key?)` and call `ctx.register(...)` directly.\n */\nimport {\n getCurrentInstance,\n onBeforeMount,\n onBeforeUpdate,\n onMounted,\n shallowRef,\n type Ref,\n} from 'vue'\nimport { __DEV__ } from '../core/dev'\nimport { captureUserCallSite } from '../core/dev-stack-trace'\nimport type { RegisterValue } from '../types/types-api'\n\n/**\n * Return type of `useRegister()`. Hybrid of `RegisterValue<V>` (so\n * `rv.path` / `rv.segments` / `rv.formKey` etc. work directly in\n * script setup) and `Ref<RegisterValue<V> | undefined>` (so Vue's\n * template auto-unwrap surfaces the underlying RV to `v-register`\n * and the directive's path-migration diff sees the real RV across\n * renders).\n *\n * The two surfaces don't clash at the type level: `RegisterValue`\n * doesn't carry a `value` field, and `Ref<T>`'s `value: T` becomes\n * the hybrid's only `.value`. Older code that read `rv.value?.path`\n * keeps working; new code can write `rv.path` directly.\n */\nexport type UseRegisterReturn<V = unknown> = RegisterValue<V> & Ref<RegisterValue<V> | undefined>\n\n/**\n * Marker on the rendered root DOM element. Set by `useRegister`'s\n * `onMounted` hook; read by the directive's deferred warn check to\n * skip the \"is a no-op\" warn for components that handle binding via\n * an inner v-register.\n *\n * `Symbol.for(...)` so the marker round-trips across duplicate copies\n * of attaform — see `assignKey` in core/directive.ts for the same\n * reasoning. `useRegister` and the directive are typically loaded\n * from the same module copy, but a consumer importing from\n * `attaform/zod` (Vite-optimized bundle) and the Nuxt\n * plugin's relative-path import (live ESM) can land on different\n * copies; a global symbol means the marker check still works.\n */\nexport const REGISTER_OWNER_MARKER: unique symbol = Symbol.for('attaform:register-owner-marker')\n\nconst warnedNoParentRV: WeakSet<object> | null = __DEV__ ? new WeakSet<object>() : null\nlet warnedOutsideSetup = false\n\n/**\n * Build the hybrid Proxy. The `__v_isRef` field makes Vue's `unref`\n * / template auto-unwrap treat the proxy as a `Ref<RegisterValue |\n * undefined>` and surface `value` (the captured RV) to consumers\n * that go through that path — including `v-register=\"rv\"` in a\n * template, which is what feeds the directive its `binding.value`.\n *\n * Every other property read pierces to `capturedRegisterValue.value`,\n * so `rv.path` / `rv.segments` / `rv.formKey` work in script setup.\n *\n * Methods don't need `this` rebinding: every method on a real\n * `RegisterValue` is an arrow-function closure built in\n * `register-api.ts`, capturing `state` / `segments` lexically. So\n * `rv.registerElement(el)` works through the proxy without a\n * `bind` pass. The `has` / `ownKeys` traps cooperate with\n * `'innerRef' in rv` / `Object.keys(rv)` — including the\n * `isRegisterValue` type guard the directive uses.\n */\nfunction makeRegisterValueProxy<V>(\n capturedRegisterValue: Ref<RegisterValue<V> | undefined>\n): UseRegisterReturn<V> {\n return new Proxy({} as object, {\n get(_target, prop) {\n if (prop === '__v_isRef') return true\n if (prop === 'value') return capturedRegisterValue.value\n const v = capturedRegisterValue.value\n if (v === undefined) return undefined\n return Reflect.get(v as object, prop)\n },\n has(_target, prop) {\n if (prop === '__v_isRef' || prop === 'value') return true\n const v = capturedRegisterValue.value\n if (v === undefined) return false\n return Reflect.has(v as object, prop)\n },\n ownKeys(_target) {\n const v = capturedRegisterValue.value\n if (v === undefined) return []\n return Reflect.ownKeys(v as object)\n },\n getOwnPropertyDescriptor(_target, prop) {\n const v = capturedRegisterValue.value\n if (v === undefined) return undefined\n const desc = Reflect.getOwnPropertyDescriptor(v as object, prop)\n if (desc !== undefined) {\n // Proxy invariant: any property reported via ownKeys must be\n // configurable on the target OR match a non-configurable\n // descriptor on the target. Empty target has no own props,\n // so we MUST return descriptors with `configurable: true`.\n desc.configurable = true\n }\n return desc\n },\n }) as unknown as UseRegisterReturn<V>\n}\n\nexport function useRegister<V = unknown>(): UseRegisterReturn<V> {\n const instance = getCurrentInstance()\n if (instance === null) {\n warnOutsideSetup()\n return makeRegisterValueProxy<V>(shallowRef<RegisterValue<V> | undefined>(undefined))\n }\n\n // Capture the bridge `registerValue` from instance.attrs into a\n // local ref, then STRIP the bridge keys (`registerValue` + `value`)\n // from the attrs object. This prevents fallthrough to the rendered\n // root: without the strip, Vue would merge attrs onto the root's\n // vnode and the wrapper would render with stringified DOM attrs\n // (`<label registerValue=\"[object Object]\">`). Class/style/aria/data\n // fallthrough is unaffected — only the bridge keys are removed, so\n // the consumer doesn't have to set `defineOptions({ inheritAttrs:\n // false })` and lose those legitimate fallthroughs.\n //\n // Vue's `setFullProps` repopulates attrs on every parent re-render\n // (it iterates rawProps and re-assigns each key into attrs). So the\n // capture+strip has to run on every update, not just at setup. The\n // `onBeforeUpdate` hook fires after `updateComponentPreRender`\n // (which calls setFullProps) and before `renderComponentRoot`\n // (which reads attrs for fallthrough), giving us a clean window.\n //\n // We don't read from `useAttrs()` proxy because the proxy reads\n // off the same target we're mutating — after the strip, the proxy\n // returns undefined for the bridge keys. The captured ref is the\n // source of truth instead, refreshed in lockstep with attrs.\n //\n // `shallowRef` (not `ref`) — `ref` calls `reactive()` on object\n // values, which would wrap the parent's RV in a reactive proxy and\n // break referential equality. The directive hooks downstream rely\n // on the rv being the same reference the parent holds, so we keep\n // it raw.\n const capturedRegisterValue = shallowRef<RegisterValue<V> | undefined>(undefined)\n\n const refreshAndStripBridgeAttrs = (): void => {\n const rawAttrs = instance.attrs as Record<string, unknown>\n // Capture only when the bridge key is present. The strip below\n // removes `registerValue` from attrs, so a second invocation of\n // this function (e.g. `onBeforeMount` after the synchronous setup\n // call) would otherwise overwrite the captured rv with `undefined`.\n // Vue's `setFullProps` re-populates attrs on every parent render,\n // so the `onBeforeUpdate` invocation correctly sees the key again\n // and re-captures.\n if ('registerValue' in rawAttrs) {\n capturedRegisterValue.value = rawAttrs['registerValue'] as RegisterValue<V> | undefined\n delete rawAttrs['registerValue']\n }\n if ('value' in rawAttrs) delete rawAttrs['value']\n }\n // Capture+strip three times: synchronously in setup, then on\n // beforeMount, then on every beforeUpdate. The synchronous call is\n // load-bearing for SSR — Vue skips lifecycle hooks during\n // `renderToString`, so an `onBeforeMount`-only capture leaves\n // `capturedRegisterValue` at `undefined` and the directive's first\n // server-side template read would otherwise misrender. Vue's\n // `setupComponent` runs `initProps` (which populates\n // `instance.attrs.registerValue` from the parent's `:registerValue`\n // binding injected by `selectNodeTransform`) before `setup()` runs,\n // so the sync read sees the correct value on both server and client.\n // The `onBeforeMount` hook stays as defence in depth against any\n // re-population that could happen after setup (e.g. from a parent's\n // directive re-running) — idempotent, safe to duplicate. The\n // `onBeforeUpdate` hook handles parent re-renders, where Vue's\n // `setFullProps` runs again and re-puts the bridge keys.\n refreshAndStripBridgeAttrs()\n onBeforeMount(refreshAndStripBridgeAttrs)\n onBeforeUpdate(refreshAndStripBridgeAttrs)\n\n // Single post-mount hook does two jobs: (1) marks the rendered root\n // DOM element with `REGISTER_OWNER_MARKER` so the parent directive's\n // deferred warn check skips the \"is a no-op\" warn for components that\n // handle binding via an inner v-register, and (2) emits the\n // no-parent-RV diagnostic exactly once per instance if the captured\n // value is still `undefined` by mount time — by then the parent has\n // had its full lifecycle to bind, so still-undefined is conclusive\n // misuse. The proxy stays pure: reads don't trigger diagnostics, so\n // a consumer that conditionally consumes the value (or reads it many\n // times) gets exactly the right behaviour. SSR is intentionally\n // silent — `onMounted` doesn't fire on the server, and the CSR\n // hydration pass surfaces the diagnostic on the only surface a\n // developer can act on without double-counting through the Nuxt\n // `dev:ssr-logs` channel.\n onMounted(() => {\n const el = instance.vnode.el\n if (el !== null && el !== undefined && typeof el === 'object') {\n ;(el as unknown as { [k: symbol]: unknown })[REGISTER_OWNER_MARKER] = true\n }\n if (capturedRegisterValue.value === undefined) {\n warnNoParentRV(instance as unknown as object)\n }\n })\n\n return makeRegisterValueProxy(capturedRegisterValue)\n}\n\nfunction warnOutsideSetup(): void {\n if (!__DEV__) return\n if (warnedOutsideSetup) return\n warnedOutsideSetup = true\n const frame = captureUserCallSite()\n console.warn(\n `[attaform] useRegister() called outside a component setup; returning an unbound RegisterValue proxy. ` +\n `Fix: call it inside <script setup> or a setup() function — not from an event handler ` +\n `or async callback.` +\n (frame !== undefined ? ` ${frame}` : '')\n )\n}\n\nfunction warnNoParentRV(instance: object): void {\n if (!__DEV__ || warnedNoParentRV === null) return\n if (warnedNoParentRV.has(instance)) return\n warnedNoParentRV.add(instance)\n const frame = captureUserCallSite()\n console.warn(\n `[attaform] useRegister: no parent registerValue prop; RegisterValue fields will read as undefined. ` +\n `Pass v-register on the parent: \\`<YourComponent v-register=\"form.register('field')\" />\\`.` +\n (frame !== undefined ? ` ${frame}` : '')\n )\n}\n","import type { PathKey } from '../paths'\n\n/**\n * Per-element identity for the persistence opt-in registry.\n *\n * Why WeakMap-keyed monotonic counter:\n * - **No DOM mutation.** A `data-atta-id` attribute would alter SSR\n * output and risk hydration discrepancies. WeakMap is invisible.\n * - **Auto-GC.** When the element is removed from the DOM and goes\n * out of all references, the WeakMap entry vanishes — no leak.\n * - **Counter over UUID.** Element IDs never cross runtime boundaries\n * (the directive that consumes them is client-only), so collision\n * resistance across processes is irrelevant. Smaller, easier to\n * debug (\"el-7\" vs a UUID).\n */\nconst idGenerator = (() => {\n let counter = 0\n return () => `el-${++counter}`\n})()\n\nconst elementIds = new WeakMap<HTMLElement, string>()\n\nexport function getOrAssignElementId(el: HTMLElement): string {\n let id = elementIds.get(el)\n if (id === undefined) {\n id = idGenerator()\n elementIds.set(el, id)\n }\n return id\n}\n\n/**\n * Per-FormStore registry tracking which DOM elements have opted into\n * persistence for which paths. Lives on the FormStore so that two SFCs\n * sharing a key share the registry — opt-ins are per-element, not\n * per-component.\n *\n * The directive's input handler computes `meta.persist` for each write\n * by calling `hasOptIn(elementId, path)` — only THIS element's writes\n * persist if THIS element opted in. Other call sites that aren't tied\n * to a single element (history undo/redo, field-array helpers, devtools\n * edits) use `hasAnyOptInForPath(path)` — persist if any element has\n * opted into that path.\n *\n * Internal data structure: `Map<PathKey, Set<elementId>>`. Small forms\n * have ~10-50 paths; iteration is cheap. All operations are O(1) given\n * (id, path).\n */\nexport type PersistOptInRegistry = {\n /** Add an opt-in entry; idempotent. */\n add(elementId: string, path: PathKey): void\n /** Remove a single (element, path) entry. */\n remove(elementId: string, path: PathKey): void\n /** Remove every opt-in for `elementId`. Called from directive's beforeUnmount. */\n removeAllFor(elementId: string): void\n /** Check whether THIS element has opted into THIS path. */\n hasOptIn(elementId: string, path: PathKey): boolean\n /** Check whether ANY element has opted into this path. */\n hasAnyOptInForPath(path: PathKey): boolean\n /** Iterate every path that currently has at least one opt-in. */\n optedInPaths(): IterableIterator<PathKey>\n /** True iff no element has opted into any path. */\n isEmpty(): boolean\n /** Drop every entry. Called from FormStore.dispose. */\n clear(): void\n}\n\nexport function createPersistOptInRegistry(): PersistOptInRegistry {\n const byPath = new Map<PathKey, Set<string>>()\n\n function add(elementId: string, path: PathKey): void {\n const existing = byPath.get(path)\n if (existing === undefined) {\n byPath.set(path, new Set([elementId]))\n return\n }\n existing.add(elementId)\n }\n\n function remove(elementId: string, path: PathKey): void {\n const existing = byPath.get(path)\n if (existing === undefined) return\n existing.delete(elementId)\n if (existing.size === 0) byPath.delete(path)\n }\n\n function removeAllFor(elementId: string): void {\n for (const [path, ids] of byPath) {\n if (!ids.delete(elementId)) continue\n if (ids.size === 0) byPath.delete(path)\n }\n }\n\n function hasOptIn(elementId: string, path: PathKey): boolean {\n return byPath.get(path)?.has(elementId) ?? false\n }\n\n function hasAnyOptInForPath(path: PathKey): boolean {\n const ids = byPath.get(path)\n return ids !== undefined && ids.size > 0\n }\n\n function optedInPaths(): IterableIterator<PathKey> {\n return byPath.keys()\n }\n\n function isEmpty(): boolean {\n return byPath.size === 0\n }\n\n function clear(): void {\n byPath.clear()\n }\n\n return {\n add,\n remove,\n removeAllFor,\n hasOptIn,\n hasAnyOptInForPath,\n optedInPaths,\n isEmpty,\n clear,\n }\n}\n","import { SensitivePersistFieldError } from '../errors'\nimport type { Path, PathKey, Segment } from '../paths'\n\n/**\n * Sensitive-name heuristic: a small, intentionally conservative set of\n * regexes that flag a path segment as \"this looks like data the consumer\n * almost certainly does not want serialised to client-side storage.\"\n *\n * The check fires when a binding opts into persistence\n * (`register(path, { persist: true })`) or when an imperative\n * `form.persist(path)` is called — the binding can override with\n * `{ acknowledgeSensitive: true }` if the persistence is genuinely\n * intentional.\n *\n * **Non-goals.** This is not a soundness guarantee. Adversarial paths\n * (`'pswd'`, `'cred'`, `'sensitive_data'`) can slip through; misnamed\n * fields (`'CCV'` instead of `'CVV'`, `'social-sec-num'`) may not match\n * depending on locale or naming convention. The intent is a code-review\n * trigger for the common-case footgun: a developer adds a `password`\n * field to a form that already has `persist: { storage: 'local' }` and\n * doesn't notice that the existing persistence config now reaches the\n * new field. The per-element opt-in model already requires explicit\n * intent for each field; the sensitive-name heuristic adds a second\n * speed bump for the names everyone agrees never belong in localStorage.\n *\n * Word-boundary anchors (`\\b`) on short tokens prevent false positives:\n * `'description'` does not match `pwd`; `'tokenizer'` does not match\n * `token`. Multi-word forms (`api[_\\s-]?key`) tolerate snake_case,\n * kebab-case, and space-separated variants for path segments emitted\n * by humans.\n */\nexport const SENSITIVE_NAME_PATTERNS: readonly RegExp[] = [\n // Passwords and PIN-like\n /password/i,\n /passwd/i,\n /passwords/i,\n /\\bpwd\\b/i,\n /\\bpin\\b/i,\n // Card / payment\n /\\bcvv\\b/i,\n /\\bcvc\\b/i,\n /card[_\\s-]?(?:number|num)/i,\n /\\bcard\\b/i,\n /\\biban\\b/i,\n /routing[_\\s-]?number/i,\n /account[_\\s-]?number/i,\n // Government / identity\n /\\bssn\\b/i,\n /social[_\\s-]?security/i,\n /\\bdob\\b/i,\n /date[_\\s-]?of[_\\s-]?birth/i,\n /passport/i,\n /driver[_\\s-]?license/i,\n // Tax IDs (US + international common variants)\n /\\btin\\b/i,\n /\\bein\\b/i,\n /\\bitin\\b/i,\n /tax[_\\s-]?id/i,\n // Tokens, secrets, API/auth credentials\n /\\btoken\\b/i,\n /\\btokens\\b/i,\n /secret/i,\n /secrets/i,\n /api[_\\s-]?key/i,\n /api[_\\s-]?secret/i,\n /api[_\\s-]?token/i,\n /private[_\\s-]?key/i,\n /\\bbearer\\b/i,\n /\\boauth\\b/i,\n /auth[_\\s-]?token/i,\n /access[_\\s-]?token/i,\n /refresh[_\\s-]?token/i,\n /session[_\\s-]?(?:id|key|token)/i,\n // MFA / OTP\n /\\botp\\b/i,\n /one[_\\s-]?time[_\\s-]?(?:password|code)/i,\n /mfa[_\\s-]?(?:secret|seed|code|token)/i,\n /two[_\\s-]?factor[_\\s-]?(?:code|token)/i,\n /\\b2fa[_\\s-]?(?:code|token)?\\b/i,\n /recovery[_\\s-]?code/i,\n /backup[_\\s-]?code/i,\n] as const\n\n/**\n * True iff `segment` itself matches a sensitive-name pattern. Numeric\n * segments are never sensitive (array indices carry no semantic\n * weight). Used by `isSensitivePath` AND by the devtools redact\n * walk, which short-circuits whole subtrees the moment any ancestor\n * segment matches — saving an O(leaves × ancestors) regex sweep\n * per timeline event.\n */\nexport function segmentMatchesSensitive(segment: Segment): boolean {\n if (typeof segment !== 'string') return false\n for (const pattern of SENSITIVE_NAME_PATTERNS) {\n if (pattern.test(segment)) return true\n }\n return false\n}\n\n/**\n * True iff any segment of the path matches a sensitive-name pattern.\n * Match is per-segment: `'profile.password'` triggers via the `password`\n * segment; `'description.text'` does NOT match `desc` because of the\n * word boundaries on the short tokens.\n *\n * Accepts either a structured `Path` (canonical segments) or a string\n * `PathKey` (canonicalised JSON form). For PathKey, the JSON-bracket\n * shape `[\"profile\",\"password\"]` parses cleanly into segments; falling\n * back to a dotted-string split keeps simple cases working without\n * a JSON.parse round-trip.\n */\nexport function isSensitivePath(path: Path | PathKey | string): boolean {\n if (typeof path !== 'string') {\n for (const segment of path) {\n if (segmentMatchesSensitive(segment)) return true\n }\n return false\n }\n // String input: try JSON-array first (PathKey), fall back to dotted.\n if (path.startsWith('[')) {\n try {\n const parsed = JSON.parse(path) as unknown[]\n if (Array.isArray(parsed)) {\n for (const segment of parsed) {\n if (segmentMatchesSensitive(segment as Segment)) return true\n }\n return false\n }\n } catch {\n // fall through\n }\n }\n for (const segment of path.split('.')) {\n if (segmentMatchesSensitive(segment)) return true\n }\n return false\n}\n\n/**\n * Throw `SensitivePersistFieldError` if `path` matches the heuristic\n * and `acknowledged` is not true. Idempotent / pure — the call site is\n * the directive's opt-in lifecycle (on every add) and `form.persist`\n * (on every imperative checkpoint).\n */\nexport function enforceSensitiveCheck(path: Path | PathKey | string, acknowledged: boolean): void {\n if (acknowledged) return\n if (!isSensitivePath(path)) return\n throw new SensitivePersistFieldError(path)\n}\n"],"names":["shallowReactive","getCurrentInstance","inject","shallowRef","onBeforeMount","onBeforeUpdate","onMounted"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { shallowReactive, getCurrentInstance, inject,
|
|
1
|
+
import { shallowReactive, getCurrentInstance, inject, shallowRef, onBeforeMount, onBeforeUpdate, onMounted } from 'vue';
|
|
2
2
|
|
|
3
3
|
const __DEV__ = typeof process !== "undefined" && process.env["NODE_ENV"] !== "production";
|
|
4
4
|
|
|
@@ -70,7 +70,7 @@ const kAttaformRegistry = Symbol.for("attaform:registry");
|
|
|
70
70
|
const kFormContext = Symbol.for("attaform:form-context");
|
|
71
71
|
const kFormInstanceId = Symbol.for("attaform:form-instance-id");
|
|
72
72
|
function createRegistry(options = {}) {
|
|
73
|
-
const
|
|
73
|
+
const ssr = detectSSR(options);
|
|
74
74
|
const defaults = Object.freeze({ ...options.defaults ?? {} });
|
|
75
75
|
const forms = shallowReactive(/* @__PURE__ */ new Map());
|
|
76
76
|
const pendingHydration = shallowReactive(/* @__PURE__ */ new Map());
|
|
@@ -103,7 +103,7 @@ function createRegistry(options = {}) {
|
|
|
103
103
|
const states = [...forms.values(), ...evicting];
|
|
104
104
|
await Promise.allSettled(states.map((state) => state.awaitPendingWrites()));
|
|
105
105
|
}
|
|
106
|
-
return { forms, pendingHydration,
|
|
106
|
+
return { forms, pendingHydration, ssr, defaults, trackConsumer, shutdown };
|
|
107
107
|
}
|
|
108
108
|
function useRegistry() {
|
|
109
109
|
const instance = getCurrentInstance();
|
|
@@ -158,11 +158,42 @@ function shortenSourceFrame(frame) {
|
|
|
158
158
|
const REGISTER_OWNER_MARKER = Symbol.for("attaform:register-owner-marker");
|
|
159
159
|
const warnedNoParentRV = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
|
|
160
160
|
let warnedOutsideSetup = false;
|
|
161
|
+
function makeRegisterValueProxy(capturedRegisterValue) {
|
|
162
|
+
return new Proxy({}, {
|
|
163
|
+
get(_target, prop) {
|
|
164
|
+
if (prop === "__v_isRef") return true;
|
|
165
|
+
if (prop === "value") return capturedRegisterValue.value;
|
|
166
|
+
const v = capturedRegisterValue.value;
|
|
167
|
+
if (v === void 0) return void 0;
|
|
168
|
+
return Reflect.get(v, prop);
|
|
169
|
+
},
|
|
170
|
+
has(_target, prop) {
|
|
171
|
+
if (prop === "__v_isRef" || prop === "value") return true;
|
|
172
|
+
const v = capturedRegisterValue.value;
|
|
173
|
+
if (v === void 0) return false;
|
|
174
|
+
return Reflect.has(v, prop);
|
|
175
|
+
},
|
|
176
|
+
ownKeys(_target) {
|
|
177
|
+
const v = capturedRegisterValue.value;
|
|
178
|
+
if (v === void 0) return [];
|
|
179
|
+
return Reflect.ownKeys(v);
|
|
180
|
+
},
|
|
181
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
182
|
+
const v = capturedRegisterValue.value;
|
|
183
|
+
if (v === void 0) return void 0;
|
|
184
|
+
const desc = Reflect.getOwnPropertyDescriptor(v, prop);
|
|
185
|
+
if (desc !== void 0) {
|
|
186
|
+
desc.configurable = true;
|
|
187
|
+
}
|
|
188
|
+
return desc;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
161
192
|
function useRegister() {
|
|
162
193
|
const instance = getCurrentInstance();
|
|
163
194
|
if (instance === null) {
|
|
164
195
|
warnOutsideSetup();
|
|
165
|
-
return
|
|
196
|
+
return makeRegisterValueProxy(shallowRef(void 0));
|
|
166
197
|
}
|
|
167
198
|
const capturedRegisterValue = shallowRef(void 0);
|
|
168
199
|
const refreshAndStripBridgeAttrs = () => {
|
|
@@ -185,7 +216,7 @@ function useRegister() {
|
|
|
185
216
|
warnNoParentRV(instance);
|
|
186
217
|
}
|
|
187
218
|
});
|
|
188
|
-
return
|
|
219
|
+
return makeRegisterValueProxy(capturedRegisterValue);
|
|
189
220
|
}
|
|
190
221
|
function warnOutsideSetup() {
|
|
191
222
|
if (!__DEV__) return;
|
|
@@ -193,7 +224,7 @@ function warnOutsideSetup() {
|
|
|
193
224
|
warnedOutsideSetup = true;
|
|
194
225
|
const frame = captureUserCallSite();
|
|
195
226
|
console.warn(
|
|
196
|
-
`[attaform] useRegister() called outside a component setup; returning
|
|
227
|
+
`[attaform] useRegister() called outside a component setup; returning an unbound RegisterValue proxy. Fix: call it inside <script setup> or a setup() function \u2014 not from an event handler or async callback.` + (frame !== void 0 ? ` ${frame}` : "")
|
|
197
228
|
);
|
|
198
229
|
}
|
|
199
230
|
function warnNoParentRV(instance) {
|
|
@@ -202,7 +233,7 @@ function warnNoParentRV(instance) {
|
|
|
202
233
|
warnedNoParentRV.add(instance);
|
|
203
234
|
const frame = captureUserCallSite();
|
|
204
235
|
console.warn(
|
|
205
|
-
`[attaform] useRegister: no parent registerValue prop;
|
|
236
|
+
`[attaform] useRegister: no parent registerValue prop; RegisterValue fields will read as undefined. Pass v-register on the parent: \`<YourComponent v-register="form.register('field')" />\`.` + (frame !== void 0 ? ` ${frame}` : "")
|
|
206
237
|
);
|
|
207
238
|
}
|
|
208
239
|
|
|
@@ -358,4 +389,4 @@ function enforceSensitiveCheck(path, acknowledged) {
|
|
|
358
389
|
}
|
|
359
390
|
|
|
360
391
|
export { AnonPersistError as A, InvalidPathError as I, OutsideSetupError as O, RegistryNotInstalledError as R, SensitivePersistFieldError as S, __DEV__ as _, AttaformError as a, ReservedFormKeyError as b, SubmitErrorHandlerError as c, createRegistry as d, useRegistry as e, getOrAssignElementId as f, getRegistryFromApp as g, REGISTER_OWNER_MARKER as h, isSensitivePath as i, enforceSensitiveCheck as j, kAttaformRegistry as k, attachRegistryToApp as l, captureUserCallSite as m, createPersistOptInRegistry as n, kFormContext as o, kFormInstanceId as p, segmentMatchesSensitive as s, useRegister as u };
|
|
361
|
-
//# sourceMappingURL=attaform.
|
|
392
|
+
//# sourceMappingURL=attaform.a99dQV7Q.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attaform.a99dQV7Q.mjs","sources":["../../src/runtime/core/dev.ts","../../src/runtime/core/errors.ts","../../src/runtime/core/ssr.ts","../../src/runtime/core/registry.ts","../../src/runtime/core/dev-stack-trace.ts","../../src/runtime/composables/use-register.ts","../../src/runtime/core/persistence/opt-in-registry.ts","../../src/runtime/core/persistence/sensitive-names.ts"],"sourcesContent":["/**\n * Portable dev-mode flag. True when the consumer's bundle / runtime\n * signals a non-production build; false in production.\n *\n * Resolves in this order:\n * 1. `process.env.NODE_ENV` — replaced at build time by Vite,\n * Webpack, Rollup + `@rollup/plugin-replace`, and read directly\n * in Node.\n * 2. Falls back to `false` when `process` is undeclared (some\n * sandboxed runtimes).\n *\n * Using this instead of `import.meta.dev` (Vite / Nuxt-specific)\n * keeps the library portable across bundlers and avoids esbuild's\n * `empty-import-meta` warning in non-ESM contexts.\n *\n * **Trade-off (browser CDN consumers).** When the library is\n * imported directly via a browser-native ESM CDN (esm.sh, Skypack,\n * unpkg) WITHOUT a bundler in front, `process` is undeclared and\n * `__DEV__` permanently resolves to `false` — every dev-only warning\n * is silenced even when the consumer is debugging. The library\n * works correctly; only the diagnostic surface degrades. The fix is\n * to put a bundler (Vite, Webpack, Rollup, esbuild) in the consumer\n * pipeline so `process.env.NODE_ENV` gets replaced. This is the\n * recommended path for any production app; CDN imports are useful\n * for prototyping but lose tree-shaking + dev diagnostics either way.\n *\n * Switching to `import.meta.env.DEV` would resolve correctly under\n * Vite but break Node consumers (no `import.meta.env`) and\n * pre-bundled distributions (esbuild emits an `empty-import-meta`\n * warning when `import.meta` resolves to `{}`). The current\n * `process.env.NODE_ENV` choice is the broadest-compatibility option.\n */\nexport const __DEV__: boolean =\n typeof process !== 'undefined' && process.env['NODE_ENV'] !== 'production'\n","/**\n * Typed error classes thrown by the form library. Each one signals a\n * distinct misuse so calling code can branch on `instanceof` instead\n * of pattern-matching error messages.\n *\n * Every class extends `AttaformError`, so consumers can write a single\n * polymorphic catch (`catch (e) { if (e instanceof AttaformError) ... }`)\n * instead of OR-chaining checks for each subclass. `AttaformError` itself\n * extends the standard `Error`, so existing `instanceof Error` usage\n * keeps working.\n */\n\n/**\n * Base for every error class thrown by `attaform`. Sets\n * `this.name` from the constructor's `new.target.name`, so subclasses\n * don't have to redeclare their own name override.\n */\nexport class AttaformError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options)\n this.name = new.target.name\n }\n}\n\n/**\n * Thrown when a path string is malformed — typically a dotted path\n * with empty segments (e.g. `'a..b'`, leading or trailing dots).\n * Use array form (`['a', 'b']`) for keys that contain literal dots.\n */\nexport class InvalidPathError extends AttaformError {}\n\n/**\n * Thrown when a `handleSubmit`-supplied `onError` callback itself\n * throws or rejects. Wraps the inner failure so both the original\n * cause (via `error.cause`) and the propagation site are visible.\n */\nexport class SubmitErrorHandlerError extends AttaformError {}\n\n/**\n * Thrown by `useForm` / `injectForm` when the form library's\n * plugin hasn't been installed on the current Vue app.\n *\n * Fix: add `app.use(createAttaform())` to your app entry\n * (or `attaform/nuxt` for Nuxt projects).\n */\nexport class RegistryNotInstalledError extends AttaformError {\n constructor() {\n super('[attaform] Registry not found. Install the plugin via `app.use(createAttaform())`.')\n }\n}\n\n/**\n * Thrown when `useForm` / `injectForm` is called outside of a\n * Vue `setup()` function — typically from an event handler, watcher,\n * or async callback that runs after mount.\n *\n * Fix: move the call into `setup()`, or trigger it from a child\n * component whose `setup()` runs the composable.\n */\nexport class OutsideSetupError extends AttaformError {\n constructor() {\n super(\n '[attaform] useForm / injectForm called outside Vue setup(). ' +\n 'Move into setup or mount a child component to trigger from an event.'\n )\n }\n}\n\n/**\n * Thrown when a `useForm({ key })` call uses a key starting with\n * `__atta:`. That prefix is reserved for keys the library generates\n * internally (e.g. for anonymous `useForm()` calls without an\n * explicit key). Pick a different prefix for your form.\n */\nexport class ReservedFormKeyError extends AttaformError {\n constructor(key: string) {\n super(\n `[attaform] Form key \"${key}\" uses the reserved \"__atta:\" namespace. ` +\n `Use a different prefix — \"__atta:\" is for library-internal synthetic keys ` +\n `(anonymous useForm() calls without an explicit key).`\n )\n }\n}\n\n/**\n * Thrown (in dev) when `useForm({ persist: ... })` is configured on\n * an anonymous form (no `key:` provided). The synthetic `__atta:anon:`\n * identity isn't stable across remounts (Vue's `useId()` allocator\n * drifts under HMR, and any sibling `useId()` call shifts subsequent\n * IDs), so the persistence layer can't reliably find the previous\n * mount's draft. Result: stale entries pile up in storage and the\n * user's most recent edit doesn't always come back.\n *\n * Fix: pass an explicit `key` to `useForm()`.\n *\n * In production builds the runtime downgrades this to a one-shot\n * `console.warn` so a deployed third-party app shipping the\n * anti-pattern doesn't hard-crash.\n */\n// (AnonPersistError class declaration is below; this docblock is the\n// historical preamble — kept here so blame/PR review can trace the\n// original intent. The richer class supersedes the earlier basic version.)\n\n/**\n * Thrown when `register(path, { persist: true })` or `form.persist(path)`\n * targets a path whose name matches a sensitive-data heuristic\n * (password, cvv, ssn, token, etc.) without an explicit\n * `acknowledgeSensitive: true` override.\n *\n * Sensitive data in client-side storage (localStorage, sessionStorage,\n * IndexedDB) is a compliance risk — it survives logouts, is readable\n * by any same-origin script, and is unencrypted at rest.\n *\n * Fix: pass `acknowledgeSensitive: true` to confirm the persistence\n * is intentional, or persist the data server-side instead.\n */\nexport class SensitivePersistFieldError extends AttaformError {\n constructor(path: ReadonlyArray<string | number> | string) {\n const display = Array.isArray(path) ? path.join('.') : String(path)\n super(\n `[attaform] Refusing to persist \"${display}\" — this path matches a ` +\n `sensitive-name pattern (password / cvv / ssn / token / etc.). Storing sensitive ` +\n `data in client-side storage is a compliance risk (HIPAA / PII / PCI-DSS / SOC2). ` +\n `Fix: persist this server-side, OR pass \\`acknowledgeSensitive: true\\` to register() ` +\n `(or form.persist()) if the client-side persistence is intentional.`\n )\n }\n}\n\n/**\n * Thrown when persistence is misconfigured in a way that would either\n * (a) silently drop writes, or (b) namespace storage under a\n * non-deterministic synthetic key — both of which become security bugs\n * the moment encrypted persistence backends are added (the same key\n * may be derived for two unrelated forms).\n *\n * Two `cause` values, one error shape:\n *\n * - `'no-key'` — `useForm({ persist: ... })` called without `key:`.\n * Anonymous keys (`__atta:anon:*`) drift across mounts; persisting\n * to a non-deterministic location is refused outright.\n *\n * - `'register-without-config'` — `register(_, { persist: true })`\n * declared on a form whose `useForm()` options omit `persist:`.\n * The opt-in is recorded but nothing would ever land in storage.\n *\n * Fix: align the two layers — set `persist:` + `key:` on `useForm()`,\n * or remove `{ persist: true }` from the offending `register()` call.\n */\nexport class AnonPersistError extends AttaformError {\n readonly schemaFields: readonly string[] | undefined\n readonly callSite: string | undefined\n override readonly cause: 'no-key' | 'register-without-config'\n constructor(opts: {\n schemaFields?: readonly string[] | undefined\n callSite?: string | undefined\n cause: 'no-key' | 'register-without-config'\n }) {\n super(formatAnonPersistMessage(opts))\n this.schemaFields = opts.schemaFields\n this.callSite = opts.callSite\n this.cause = opts.cause\n }\n}\n\nfunction formatAnonPersistMessage(opts: {\n schemaFields?: readonly string[] | undefined\n callSite?: string | undefined\n cause: 'no-key' | 'register-without-config'\n}): string {\n const head =\n opts.cause === 'no-key'\n ? `useForm({ persist: ... }) requires an explicit \\`key:\\`. Anonymous synthetic keys (\\`__atta:anon:*\\`) drift across mounts and can collide between unrelated forms — refusing to persist to a non-deterministic location.`\n : `register(_, { persist: true }) declared on a form whose useForm() options have no \\`persist:\\` configured. The opt-in is recorded but nothing would ever land in storage.`\n const fields =\n opts.schemaFields !== undefined && opts.schemaFields.length > 0\n ? ` Form fields: { ${opts.schemaFields.join(', ')} }.`\n : ''\n const fix =\n opts.cause === 'no-key'\n ? ` Fix: add \\`key: '<stable-id>'\\` to useForm().`\n : ` Fix: add \\`persist: 'session'\\` (or 'local') and \\`key:\\` to useForm(), or remove \\`{ persist: true }\\` from this register() call.`\n const where = opts.callSite !== undefined ? ` ${opts.callSite}` : ''\n return `[attaform] ${head}${fields}${fix}${where}`\n}\n","/**\n * Portable SSR detection. The plugin captures this value at install time and\n * exposes it via the registry so every runtime branch reads a single source\n * of truth instead of sniffing `import.meta.*` (bundler-specific) at each\n * call site.\n *\n * Consumers can override explicitly via `createAttaform({ ssr: true })`;\n * the default heuristic handles the common Node-vs-browser split without\n * relying on any bundler-injected flag.\n */\n\nexport interface SSRDetectOptions {\n override?: boolean\n}\n\n/**\n * Returns true when running in a server-rendering context (no `window` / no\n * `document`). Explicit override always wins.\n *\n * Note: JSDOM-based test environments define `window`, so tests that need to\n * exercise SSR code paths must pass `{ override: true }` explicitly.\n */\nexport function detectSSR(options: SSRDetectOptions = {}): boolean {\n if (options.override !== undefined) return options.override\n return typeof window === 'undefined' && typeof document === 'undefined'\n}\n","import type { App, InjectionKey } from 'vue'\nimport { getCurrentInstance, inject, shallowReactive } from 'vue'\nimport type { AttaformDefaults, FormKey } from '../types/types-api'\nimport type { GenericForm } from '../types/types-core'\nimport type { FormStore } from './create-form-store'\nimport { OutsideSetupError, RegistryNotInstalledError } from './errors'\nimport { detectSSR, type SSRDetectOptions } from './ssr'\n\n/**\n * Per-Vue-app container for all form state instances. Each\n * `app.use(createAttaform())` call gets its own registry,\n * so the library runs under bare Vue 3 + SSR (via\n * `@vue/server-renderer`) and Nuxt with the same code path.\n *\n * Each form's state lives in `forms: Map<FormKey, FormStore<GenericForm>>`.\n * The type relaxation at storage time is necessary because different\n * forms in the same app have different `Form` generics; callers recover\n * the specific form type via `useForm`'s overloads.\n */\n\n/**\n * Serialised snapshot of one form's state, captured by\n * `renderAttaformState` for SSR and replayed by\n * `hydrateAttaformState` on the client. Round-trips through\n * JSON-safe tuples; field references are intentionally omitted\n * (DOM nodes don't survive serialisation).\n */\nexport type SerializedFormData = {\n /** The form's value at snapshot time. */\n readonly form: unknown\n /**\n * Errors produced by the schema at snapshot time. Replayed into\n * the client form's error state at hydration; cleared on\n * successful re-validation client-side.\n */\n readonly schemaErrors: ReadonlyArray<readonly [string, unknown]>\n /**\n * Errors set explicitly via `setFieldErrors` / `addFieldErrors`\n * (typically from a server response parsed via `parseApiErrors`)\n * at snapshot time. Replayed at hydration; persists across\n * client-side re-validation.\n */\n readonly userErrors: ReadonlyArray<readonly [string, unknown]>\n /** Per-field metadata (timestamps, raw values, connection flags) captured at snapshot time. */\n readonly fields: ReadonlyArray<readonly [string, unknown]>\n /**\n * Path keys that were in the form's `blankPaths` set at\n * snapshot time. Round-trips the \"displayed empty\" UI state across\n * the SSR boundary — without it, the client briefly renders\n * `String(slim-default)` (e.g. `'0'`) for fields the server\n * rendered as blank. Optional in the wire format so older payload\n * shapes deserialise cleanly.\n */\n readonly blankPaths?: ReadonlyArray<string>\n}\n\nexport type PendingHydration = Map<FormKey, SerializedFormData>\n\n/**\n * The library's per-Vue-app container. One `AttaformRegistry` is\n * created per `app.use(createAttaform())` call.\n *\n * Most consumers never touch this directly — `useForm` and\n * `injectForm` reach the registry on your behalf. Access it\n * explicitly only when wiring SSR or a custom plugin integration.\n */\nexport type AttaformRegistry = {\n /**\n * Live forms keyed by `FormKey`.\n * @internal\n */\n readonly forms: Map<FormKey, FormStore<GenericForm>>\n /**\n * Snapshots staged by `hydrateAttaformState` waiting to be consumed by the next `useForm` call.\n * @internal\n */\n readonly pendingHydration: PendingHydration\n /** `true` while running on the server during SSR; `false` on the client. */\n readonly ssr: boolean\n /** App-level defaults applied to every `useForm` call. */\n readonly defaults: AttaformDefaults\n /**\n * Track a consumer of `key`. Returns a dispose function — call it\n * when the consumer unmounts. The form is evicted automatically\n * when the last consumer disposes, so long-running SPAs don't\n * leak detached state across navigations.\n * @internal\n */\n readonly trackConsumer: (key: FormKey) => () => void\n /**\n * Wait for all pending persistence writes across every live form\n * to settle. Useful for SSR shutdown and integration tests that\n * need a deterministic teardown.\n * @internal\n */\n readonly shutdown: () => Promise<void>\n}\n\n/**\n * The Vue `InjectionKey` under which the registry is provided on the\n * app. Most consumers never need this — `useForm` and\n * `injectForm` resolve the registry automatically.\n */\n// `Symbol.for(...)` so the key survives module duplication. If Vite's\n// dep optimizer ends up serving attaform as two separate copies (one\n// live-ESM, one pre-bundled — the standard hazard for linked-source\n// installs that opt into `optimizeDeps.include`), each copy still\n// resolves the same global symbol from the well-known string. Plugin\n// install's `app.provide(kAttaformRegistry, ...)` and the page's\n// `inject(kAttaformRegistry, null)` agree on the key, so `useForm`\n// finds its registry regardless of which copy did the provide. The\n// `attaform:` prefix namespaces the key safely. Same reasoning\n// for `kFormContext` and `kFormInstanceId` below.\nexport const kAttaformRegistry: InjectionKey<AttaformRegistry> = Symbol.for('attaform:registry')\n\n/**\n * Provides the current form's FormStore to descendants. Installed by\n * `useAbstractForm` after it resolves the state, so any nested component\n * can call `injectForm()` without prop-threading the form API.\n *\n * Typed as `FormStore<GenericForm>` — the descendant that re-emerges the\n * shape must supply its own `Form` generic, because Vue's InjectionKey\n * erases the generic at the provide/inject boundary.\n */\nexport const kFormContext: InjectionKey<FormStore<GenericForm>> =\n Symbol.for('attaform:form-context')\n\n/**\n * Provide / inject key for the per-`useForm()`-call instance ID. Provided\n * alongside `kFormContext` so descendants reaching via `injectForm()`\n * inherit the ancestor's `formInstanceId` and their locally-registered\n * elements tag against the SAME instance — keeps parent-submit-focus\n * working for inputs registered by deep children.\n *\n * Sibling `useForm({ key })` calls (e.g. sidebar + main rendering the\n * same form) sit at distinct tree positions, so each provides its own\n * ID; descendants of each branch inherit the branch's ID. Two ID spaces\n * stay isolated even when the underlying FormStore is shared.\n */\nexport const kFormInstanceId: InjectionKey<string> = Symbol.for('attaform:form-instance-id')\n\ndeclare module 'vue' {\n interface App {\n /** @internal */\n _attaform?: AttaformRegistry\n }\n}\n\n/** Options for `createRegistry`. */\nexport type CreateRegistryOptions = SSRDetectOptions & {\n /**\n * App-level defaults applied to every `useForm` call. Per-form\n * options always win. Omitted is equivalent to `{}`.\n */\n defaults?: AttaformDefaults\n}\n\n/**\n * Create a fresh `AttaformRegistry`. `createAttaform()` calls\n * this internally — most consumers never need to call it directly.\n * Use it when building a custom plugin that doesn't want the\n * `createAttaform` plugin's auto-install behaviour (e.g. test\n * harnesses, embedded apps).\n */\nexport function createRegistry(options: CreateRegistryOptions = {}): AttaformRegistry {\n const ssr = detectSSR(options)\n // Frozen so accidental writes downstream throw in dev. Public surface\n // (`createAttaform({ defaults })`) treats this as data, not as\n // a mutation point — there's no public API to update defaults after\n // install, and adding one would invite race conditions with already-\n // mounted forms.\n const defaults: AttaformDefaults = Object.freeze({ ...(options.defaults ?? {}) })\n // The outer object is plain (it holds references we never rebind); inner\n // Maps are reactive via Vue's collection handlers so per-key reads track\n // per-key. `shallowReactive` avoids Vue's deep Ref-unwrapping, which would\n // mangle FormStore.form's Ref<F> type into F on lookup.\n const forms = shallowReactive(new Map<FormKey, FormStore<GenericForm>>())\n const pendingHydration = shallowReactive(new Map<FormKey, SerializedFormData>())\n // Consumer counts are bookkeeping — not reactive. No template should ever\n // depend on \"how many useForm calls are live\", and using a plain Map\n // avoids triggering watchers when we increment on every mount.\n const consumers = new Map<FormKey, number>()\n\n // Stores that have been evicted from `forms` but still have a\n // pending drain. `shutdown()` awaits these too so a process-exit\n // hook doesn't tear down before debounced writes from already-\n // unmounted forms have a chance to flush.\n const evicting = new Set<FormStore<GenericForm>>()\n\n function trackConsumer(key: FormKey): () => void {\n consumers.set(key, (consumers.get(key) ?? 0) + 1)\n let disposed = false\n return () => {\n if (disposed) return\n disposed = true\n const remaining = (consumers.get(key) ?? 1) - 1\n if (remaining <= 0) {\n // Tear down non-reactive resources the FormStore owns (field-\n // validation timers, abort controllers) BEFORE dropping the\n // registry reference — once the Map entry is gone we can't\n // reach the state anymore.\n const state = forms.get(key)\n consumers.delete(key)\n // Eviction from `forms` stays synchronous: any consumer that\n // reads `registry.forms` after unmount (tests, devtools) sees\n // the form gone immediately. Drain-then-dispose runs async in\n // the background so the persistence layer's debounced final\n // write can complete — the FormStore is reachable through the\n // closure here even after `forms.delete`.\n forms.delete(key)\n if (state !== undefined) {\n evicting.add(state)\n void state\n .awaitPendingWrites()\n .catch(() => undefined)\n .finally(() => {\n evicting.delete(state)\n state.dispose()\n })\n }\n } else {\n consumers.set(key, remaining)\n }\n }\n }\n\n async function shutdown(): Promise<void> {\n // Snapshot the keys — `awaitPendingWrites` may resolve mid-iteration\n // and trigger eviction that mutates `forms` while we're walking.\n // Include the evicting set so in-flight drains from already-\n // unmounted forms also flush before shutdown returns.\n const states = [...forms.values(), ...evicting]\n await Promise.allSettled(states.map((state) => state.awaitPendingWrites()))\n }\n\n return { forms, pendingHydration, ssr, defaults, trackConsumer, shutdown }\n}\n\n/**\n * Look up the current app's registry from inside a component's\n * `setup()` (or any synchronous code on the setup call stack).\n *\n * Most consumers don't need this — `useForm` and `injectForm`\n * call it on your behalf. Reach for it directly when building\n * custom integrations that need the raw registry.\n *\n * Throws:\n * - `OutsideSetupError` when called outside a Vue setup context\n * (e.g. from an event handler or async callback). Move the call\n * into setup, or trigger it from a child component.\n * - `RegistryNotInstalledError` when called inside setup but the\n * plugin wasn't installed. Add\n * `app.use(createAttaform())` to your app entry.\n */\nexport function useRegistry(): AttaformRegistry {\n const instance = getCurrentInstance()\n if (instance === null) {\n throw new OutsideSetupError()\n }\n const registry = inject(kAttaformRegistry, null)\n if (registry === null) {\n throw new RegistryNotInstalledError()\n }\n return registry\n}\n\n/**\n * Look up a Vue app's registry by `App` reference. Used by\n * SSR helpers (`renderAttaformState`, `hydrateAttaformState`) that\n * run outside a component setup context.\n *\n * Throws `RegistryNotInstalledError` when the app hasn't been wired\n * with `createAttaform()`.\n */\nexport function getRegistryFromApp(app: App): AttaformRegistry {\n const registry = app._attaform\n if (registry === undefined) {\n throw new RegistryNotInstalledError()\n }\n return registry\n}\n\nexport function attachRegistryToApp(app: App, registry: AttaformRegistry): void {\n app.provide(kAttaformRegistry, registry)\n app._attaform = registry\n}\n","/**\n * Dev-only call-site capture for warnings that want to point the\n * reader at the offending line of their code (not at a attaform-internal\n * frame). Walks the stack past `attaform` frames, picks the\n * first frame that looks like user code, then strips the dev-server\n * scheme + host + Vite/Nuxt's `/_nuxt/` prefix so the warning doesn't\n * carry a wall of `https://localhost:3000/_nuxt/...` noise.\n *\n * Returns `undefined` on engines that don't expose `.stack` or when\n * parsing fails — callers should degrade to a generic message rather\n * than printing nothing.\n *\n * Click-through navigation isn't sacrificed: `console.warn` already\n * renders its own clickable stack trace below the message in\n * Chrome / Firefox DevTools (V8 frame format → Sources tab). The\n * captured frame is purely an inline pointer in the message text,\n * and short paths read better there than full URLs.\n *\n * The attaform-frame regex matches both the published path\n * (`attaform/...`) and the linked / source path\n * (`attaform/...`) so local dev via `make link-attaform` surfaces\n * the same trimmed frames.\n *\n * Dev-only; callers should gate on `__DEV__` before invoking.\n */\nexport function captureUserCallSite(): string | undefined {\n const raw = new Error().stack\n if (typeof raw !== 'string') return undefined\n const lines = raw.split('\\n')\n // Skip the \"Error\" message line and any frame inside attaform itself.\n for (let i = 1; i < lines.length; i++) {\n const frame = lines[i]\n if (frame === undefined) continue\n if (/attaform[/-]forms?/i.test(frame)) continue\n if (/\\bforms\\.[A-Za-z0-9_-]+\\.m?js\\b/.test(frame)) continue\n const trimmed = frame.trim()\n if (trimmed.length === 0) continue\n return shortenSourceFrame(trimmed)\n }\n return undefined\n}\n\n/**\n * Reduce a raw stack frame to `(<path>:<line>)`.\n *\n * Inputs we expect (V8, with or without `at fn (…)` wrapper):\n * - `at setup (https://example.test/_nuxt/pages/spike.vue:18:18)`\n * - `at https://example.com/foo.js:1:1`\n * - `at file:///Users/x/proj/spike.vue:18:18`\n * - `pages/foo.vue:18:18` (already path-like, no V8 wrapper)\n *\n * Outputs:\n * - `(pages/spike.vue:18)`\n * - `(foo.js:1)`\n * - `(Users/x/proj/spike.vue:18)`\n * - `(pages/foo.vue:18)`\n *\n * Why drop the column: Vite's sourcemaps round-trip line accurately\n * but column resolution is fuzzy in compiled contexts (Vue render\n * functions, JSX, anywhere the source-to-output mapping isn't\n * 1-to-1 per character). For a script-setup `useForm()` call the\n * column is meaningful; for a template-inlined `register(...)` it\n * lands somewhere mid-compiled-blob and is actively misleading. The\n * uniform `path:line` format avoids that asymmetry — line is enough\n * to navigate, the editor lands on the right region either way.\n *\n * If the frame doesn't match the trailing `…:line:col` shape at all,\n * the original trimmed frame is returned unchanged — better to\n * surface something than nothing.\n */\nexport function shortenSourceFrame(frame: string): string {\n const match = /(?:^|\\s|\\()([^\\s()]+):(\\d+):\\d+\\)?$/.exec(frame)\n if (match === null) return frame\n const [, urlOrPath, line] = match\n if (urlOrPath === undefined || line === undefined) return frame\n let path = urlOrPath\n // Strip `scheme://host/` (https://…, http://…). file:// gets the\n // same treatment, leaving the absolute filesystem path; we then\n // also strip its leading slash below so it reads as a relative path.\n path = path.replace(/^[a-z]+:\\/\\/[^/]+\\//i, '')\n // Strip Vite/Nuxt's dev-server prefix.\n path = path.replace(/^_nuxt\\//, '')\n // Strip leading slash (left over from file:// or absolute paths).\n path = path.replace(/^\\//, '')\n // Wrap in parens. Chrome's console auto-linker partial-matches\n // bare `pages/foo.vue:137` (it picks up `/foo.vue:137` and\n // drops the `pages` prefix). Parens are the V8 stack-frame\n // convention and Chrome reliably auto-links them end-to-end.\n return `(${path}:${line})`\n}\n","/**\n * Re-bind a parent's `v-register` onto an inner native element. Use\n * inside a component that wraps a single form field whose root is\n * NOT the input itself (e.g. a labelled-row that renders `<label>`\n * around the input).\n *\n * ```vue\n * <!-- Parent -->\n * <MyInput v-register=\"form.register('email')\" />\n *\n * <!-- MyInput.vue -->\n * <script setup lang=\"ts\">\n * import { useRegister } from 'attaform'\n * const rv = useRegister()\n * // rv.path / rv.segments / rv.formKey / rv.formInstanceId / rv.innerRef\n * // are all reachable directly — no `.value` unwrap.\n * </script>\n *\n * <template>\n * <label class=\"field\">\n * <span>Email</span>\n * <input v-register=\"rv\" />\n * </label>\n * </template>\n * ```\n *\n * Returns a hybrid Proxy: it answers `__v_isRef` / `.value` like a\n * Vue `Ref<RegisterValue | undefined>` (so templates auto-unwrap\n * correctly and `v-register=\"rv\"` feeds the underlying RV to the\n * directive — preserving the directive's path-migration diff across\n * renders), AND every other property read pierces to the captured\n * RV's field (so `rv.path` works directly in script setup). Reads\n * inside reactive scopes (`computed` / `watchEffect`) track the\n * underlying `shallowRef`, so `rv.path` re-runs when the parent\n * rebinds to a different path.\n *\n * Unbound state: when the parent didn't pass `v-register`, every\n * piercing read returns `undefined` at runtime even though the type\n * says otherwise. The composable's `onMounted` warn fires once per\n * instance to flag this misuse — the type \"lies\" because the bound\n * case is the only correct one, and forcing every consumer through\n * a `T | undefined` narrow at every property access is a worse\n * trade than the runtime warn.\n *\n * Diagnostic: in dev mode, a single `console.warn` fires per instance\n * at `onMounted` if the captured value is still `undefined` — by then\n * the parent has had its full mount lifecycle to bind, so a missing\n * binding is conclusive misuse. The warn does NOT fire on every read\n * of the proxy, and is intentionally silent under SSR\n * (`renderToString` skips `onMounted`); the CSR hydration pass\n * surfaces the same diagnostic without double-counting through Nuxt's\n * `dev:ssr-logs` channel.\n *\n * When the wrapper's root IS the input itself, Vue's attribute\n * fallthrough handles the binding and `useRegister` is unnecessary.\n * For wrappers that bind multiple fields (compound forms), use\n * `injectForm<Form>(key?)` and call `ctx.register(...)` directly.\n */\nimport {\n getCurrentInstance,\n onBeforeMount,\n onBeforeUpdate,\n onMounted,\n shallowRef,\n type Ref,\n} from 'vue'\nimport { __DEV__ } from '../core/dev'\nimport { captureUserCallSite } from '../core/dev-stack-trace'\nimport type { RegisterValue } from '../types/types-api'\n\n/**\n * Return type of `useRegister()`. Hybrid of `RegisterValue<V>` (so\n * `rv.path` / `rv.segments` / `rv.formKey` etc. work directly in\n * script setup) and `Ref<RegisterValue<V> | undefined>` (so Vue's\n * template auto-unwrap surfaces the underlying RV to `v-register`\n * and the directive's path-migration diff sees the real RV across\n * renders).\n *\n * The two surfaces don't clash at the type level: `RegisterValue`\n * doesn't carry a `value` field, and `Ref<T>`'s `value: T` becomes\n * the hybrid's only `.value`. Older code that read `rv.value?.path`\n * keeps working; new code can write `rv.path` directly.\n */\nexport type UseRegisterReturn<V = unknown> = RegisterValue<V> & Ref<RegisterValue<V> | undefined>\n\n/**\n * Marker on the rendered root DOM element. Set by `useRegister`'s\n * `onMounted` hook; read by the directive's deferred warn check to\n * skip the \"is a no-op\" warn for components that handle binding via\n * an inner v-register.\n *\n * `Symbol.for(...)` so the marker round-trips across duplicate copies\n * of attaform — see `assignKey` in core/directive.ts for the same\n * reasoning. `useRegister` and the directive are typically loaded\n * from the same module copy, but a consumer importing from\n * `attaform/zod` (Vite-optimized bundle) and the Nuxt\n * plugin's relative-path import (live ESM) can land on different\n * copies; a global symbol means the marker check still works.\n */\nexport const REGISTER_OWNER_MARKER: unique symbol = Symbol.for('attaform:register-owner-marker')\n\nconst warnedNoParentRV: WeakSet<object> | null = __DEV__ ? new WeakSet<object>() : null\nlet warnedOutsideSetup = false\n\n/**\n * Build the hybrid Proxy. The `__v_isRef` field makes Vue's `unref`\n * / template auto-unwrap treat the proxy as a `Ref<RegisterValue |\n * undefined>` and surface `value` (the captured RV) to consumers\n * that go through that path — including `v-register=\"rv\"` in a\n * template, which is what feeds the directive its `binding.value`.\n *\n * Every other property read pierces to `capturedRegisterValue.value`,\n * so `rv.path` / `rv.segments` / `rv.formKey` work in script setup.\n *\n * Methods don't need `this` rebinding: every method on a real\n * `RegisterValue` is an arrow-function closure built in\n * `register-api.ts`, capturing `state` / `segments` lexically. So\n * `rv.registerElement(el)` works through the proxy without a\n * `bind` pass. The `has` / `ownKeys` traps cooperate with\n * `'innerRef' in rv` / `Object.keys(rv)` — including the\n * `isRegisterValue` type guard the directive uses.\n */\nfunction makeRegisterValueProxy<V>(\n capturedRegisterValue: Ref<RegisterValue<V> | undefined>\n): UseRegisterReturn<V> {\n return new Proxy({} as object, {\n get(_target, prop) {\n if (prop === '__v_isRef') return true\n if (prop === 'value') return capturedRegisterValue.value\n const v = capturedRegisterValue.value\n if (v === undefined) return undefined\n return Reflect.get(v as object, prop)\n },\n has(_target, prop) {\n if (prop === '__v_isRef' || prop === 'value') return true\n const v = capturedRegisterValue.value\n if (v === undefined) return false\n return Reflect.has(v as object, prop)\n },\n ownKeys(_target) {\n const v = capturedRegisterValue.value\n if (v === undefined) return []\n return Reflect.ownKeys(v as object)\n },\n getOwnPropertyDescriptor(_target, prop) {\n const v = capturedRegisterValue.value\n if (v === undefined) return undefined\n const desc = Reflect.getOwnPropertyDescriptor(v as object, prop)\n if (desc !== undefined) {\n // Proxy invariant: any property reported via ownKeys must be\n // configurable on the target OR match a non-configurable\n // descriptor on the target. Empty target has no own props,\n // so we MUST return descriptors with `configurable: true`.\n desc.configurable = true\n }\n return desc\n },\n }) as unknown as UseRegisterReturn<V>\n}\n\nexport function useRegister<V = unknown>(): UseRegisterReturn<V> {\n const instance = getCurrentInstance()\n if (instance === null) {\n warnOutsideSetup()\n return makeRegisterValueProxy<V>(shallowRef<RegisterValue<V> | undefined>(undefined))\n }\n\n // Capture the bridge `registerValue` from instance.attrs into a\n // local ref, then STRIP the bridge keys (`registerValue` + `value`)\n // from the attrs object. This prevents fallthrough to the rendered\n // root: without the strip, Vue would merge attrs onto the root's\n // vnode and the wrapper would render with stringified DOM attrs\n // (`<label registerValue=\"[object Object]\">`). Class/style/aria/data\n // fallthrough is unaffected — only the bridge keys are removed, so\n // the consumer doesn't have to set `defineOptions({ inheritAttrs:\n // false })` and lose those legitimate fallthroughs.\n //\n // Vue's `setFullProps` repopulates attrs on every parent re-render\n // (it iterates rawProps and re-assigns each key into attrs). So the\n // capture+strip has to run on every update, not just at setup. The\n // `onBeforeUpdate` hook fires after `updateComponentPreRender`\n // (which calls setFullProps) and before `renderComponentRoot`\n // (which reads attrs for fallthrough), giving us a clean window.\n //\n // We don't read from `useAttrs()` proxy because the proxy reads\n // off the same target we're mutating — after the strip, the proxy\n // returns undefined for the bridge keys. The captured ref is the\n // source of truth instead, refreshed in lockstep with attrs.\n //\n // `shallowRef` (not `ref`) — `ref` calls `reactive()` on object\n // values, which would wrap the parent's RV in a reactive proxy and\n // break referential equality. The directive hooks downstream rely\n // on the rv being the same reference the parent holds, so we keep\n // it raw.\n const capturedRegisterValue = shallowRef<RegisterValue<V> | undefined>(undefined)\n\n const refreshAndStripBridgeAttrs = (): void => {\n const rawAttrs = instance.attrs as Record<string, unknown>\n // Capture only when the bridge key is present. The strip below\n // removes `registerValue` from attrs, so a second invocation of\n // this function (e.g. `onBeforeMount` after the synchronous setup\n // call) would otherwise overwrite the captured rv with `undefined`.\n // Vue's `setFullProps` re-populates attrs on every parent render,\n // so the `onBeforeUpdate` invocation correctly sees the key again\n // and re-captures.\n if ('registerValue' in rawAttrs) {\n capturedRegisterValue.value = rawAttrs['registerValue'] as RegisterValue<V> | undefined\n delete rawAttrs['registerValue']\n }\n if ('value' in rawAttrs) delete rawAttrs['value']\n }\n // Capture+strip three times: synchronously in setup, then on\n // beforeMount, then on every beforeUpdate. The synchronous call is\n // load-bearing for SSR — Vue skips lifecycle hooks during\n // `renderToString`, so an `onBeforeMount`-only capture leaves\n // `capturedRegisterValue` at `undefined` and the directive's first\n // server-side template read would otherwise misrender. Vue's\n // `setupComponent` runs `initProps` (which populates\n // `instance.attrs.registerValue` from the parent's `:registerValue`\n // binding injected by `selectNodeTransform`) before `setup()` runs,\n // so the sync read sees the correct value on both server and client.\n // The `onBeforeMount` hook stays as defence in depth against any\n // re-population that could happen after setup (e.g. from a parent's\n // directive re-running) — idempotent, safe to duplicate. The\n // `onBeforeUpdate` hook handles parent re-renders, where Vue's\n // `setFullProps` runs again and re-puts the bridge keys.\n refreshAndStripBridgeAttrs()\n onBeforeMount(refreshAndStripBridgeAttrs)\n onBeforeUpdate(refreshAndStripBridgeAttrs)\n\n // Single post-mount hook does two jobs: (1) marks the rendered root\n // DOM element with `REGISTER_OWNER_MARKER` so the parent directive's\n // deferred warn check skips the \"is a no-op\" warn for components that\n // handle binding via an inner v-register, and (2) emits the\n // no-parent-RV diagnostic exactly once per instance if the captured\n // value is still `undefined` by mount time — by then the parent has\n // had its full lifecycle to bind, so still-undefined is conclusive\n // misuse. The proxy stays pure: reads don't trigger diagnostics, so\n // a consumer that conditionally consumes the value (or reads it many\n // times) gets exactly the right behaviour. SSR is intentionally\n // silent — `onMounted` doesn't fire on the server, and the CSR\n // hydration pass surfaces the diagnostic on the only surface a\n // developer can act on without double-counting through the Nuxt\n // `dev:ssr-logs` channel.\n onMounted(() => {\n const el = instance.vnode.el\n if (el !== null && el !== undefined && typeof el === 'object') {\n ;(el as unknown as { [k: symbol]: unknown })[REGISTER_OWNER_MARKER] = true\n }\n if (capturedRegisterValue.value === undefined) {\n warnNoParentRV(instance as unknown as object)\n }\n })\n\n return makeRegisterValueProxy(capturedRegisterValue)\n}\n\nfunction warnOutsideSetup(): void {\n if (!__DEV__) return\n if (warnedOutsideSetup) return\n warnedOutsideSetup = true\n const frame = captureUserCallSite()\n console.warn(\n `[attaform] useRegister() called outside a component setup; returning an unbound RegisterValue proxy. ` +\n `Fix: call it inside <script setup> or a setup() function — not from an event handler ` +\n `or async callback.` +\n (frame !== undefined ? ` ${frame}` : '')\n )\n}\n\nfunction warnNoParentRV(instance: object): void {\n if (!__DEV__ || warnedNoParentRV === null) return\n if (warnedNoParentRV.has(instance)) return\n warnedNoParentRV.add(instance)\n const frame = captureUserCallSite()\n console.warn(\n `[attaform] useRegister: no parent registerValue prop; RegisterValue fields will read as undefined. ` +\n `Pass v-register on the parent: \\`<YourComponent v-register=\"form.register('field')\" />\\`.` +\n (frame !== undefined ? ` ${frame}` : '')\n )\n}\n","import type { PathKey } from '../paths'\n\n/**\n * Per-element identity for the persistence opt-in registry.\n *\n * Why WeakMap-keyed monotonic counter:\n * - **No DOM mutation.** A `data-atta-id` attribute would alter SSR\n * output and risk hydration discrepancies. WeakMap is invisible.\n * - **Auto-GC.** When the element is removed from the DOM and goes\n * out of all references, the WeakMap entry vanishes — no leak.\n * - **Counter over UUID.** Element IDs never cross runtime boundaries\n * (the directive that consumes them is client-only), so collision\n * resistance across processes is irrelevant. Smaller, easier to\n * debug (\"el-7\" vs a UUID).\n */\nconst idGenerator = (() => {\n let counter = 0\n return () => `el-${++counter}`\n})()\n\nconst elementIds = new WeakMap<HTMLElement, string>()\n\nexport function getOrAssignElementId(el: HTMLElement): string {\n let id = elementIds.get(el)\n if (id === undefined) {\n id = idGenerator()\n elementIds.set(el, id)\n }\n return id\n}\n\n/**\n * Per-FormStore registry tracking which DOM elements have opted into\n * persistence for which paths. Lives on the FormStore so that two SFCs\n * sharing a key share the registry — opt-ins are per-element, not\n * per-component.\n *\n * The directive's input handler computes `meta.persist` for each write\n * by calling `hasOptIn(elementId, path)` — only THIS element's writes\n * persist if THIS element opted in. Other call sites that aren't tied\n * to a single element (history undo/redo, field-array helpers, devtools\n * edits) use `hasAnyOptInForPath(path)` — persist if any element has\n * opted into that path.\n *\n * Internal data structure: `Map<PathKey, Set<elementId>>`. Small forms\n * have ~10-50 paths; iteration is cheap. All operations are O(1) given\n * (id, path).\n */\nexport type PersistOptInRegistry = {\n /** Add an opt-in entry; idempotent. */\n add(elementId: string, path: PathKey): void\n /** Remove a single (element, path) entry. */\n remove(elementId: string, path: PathKey): void\n /** Remove every opt-in for `elementId`. Called from directive's beforeUnmount. */\n removeAllFor(elementId: string): void\n /** Check whether THIS element has opted into THIS path. */\n hasOptIn(elementId: string, path: PathKey): boolean\n /** Check whether ANY element has opted into this path. */\n hasAnyOptInForPath(path: PathKey): boolean\n /** Iterate every path that currently has at least one opt-in. */\n optedInPaths(): IterableIterator<PathKey>\n /** True iff no element has opted into any path. */\n isEmpty(): boolean\n /** Drop every entry. Called from FormStore.dispose. */\n clear(): void\n}\n\nexport function createPersistOptInRegistry(): PersistOptInRegistry {\n const byPath = new Map<PathKey, Set<string>>()\n\n function add(elementId: string, path: PathKey): void {\n const existing = byPath.get(path)\n if (existing === undefined) {\n byPath.set(path, new Set([elementId]))\n return\n }\n existing.add(elementId)\n }\n\n function remove(elementId: string, path: PathKey): void {\n const existing = byPath.get(path)\n if (existing === undefined) return\n existing.delete(elementId)\n if (existing.size === 0) byPath.delete(path)\n }\n\n function removeAllFor(elementId: string): void {\n for (const [path, ids] of byPath) {\n if (!ids.delete(elementId)) continue\n if (ids.size === 0) byPath.delete(path)\n }\n }\n\n function hasOptIn(elementId: string, path: PathKey): boolean {\n return byPath.get(path)?.has(elementId) ?? false\n }\n\n function hasAnyOptInForPath(path: PathKey): boolean {\n const ids = byPath.get(path)\n return ids !== undefined && ids.size > 0\n }\n\n function optedInPaths(): IterableIterator<PathKey> {\n return byPath.keys()\n }\n\n function isEmpty(): boolean {\n return byPath.size === 0\n }\n\n function clear(): void {\n byPath.clear()\n }\n\n return {\n add,\n remove,\n removeAllFor,\n hasOptIn,\n hasAnyOptInForPath,\n optedInPaths,\n isEmpty,\n clear,\n }\n}\n","import { SensitivePersistFieldError } from '../errors'\nimport type { Path, PathKey, Segment } from '../paths'\n\n/**\n * Sensitive-name heuristic: a small, intentionally conservative set of\n * regexes that flag a path segment as \"this looks like data the consumer\n * almost certainly does not want serialised to client-side storage.\"\n *\n * The check fires when a binding opts into persistence\n * (`register(path, { persist: true })`) or when an imperative\n * `form.persist(path)` is called — the binding can override with\n * `{ acknowledgeSensitive: true }` if the persistence is genuinely\n * intentional.\n *\n * **Non-goals.** This is not a soundness guarantee. Adversarial paths\n * (`'pswd'`, `'cred'`, `'sensitive_data'`) can slip through; misnamed\n * fields (`'CCV'` instead of `'CVV'`, `'social-sec-num'`) may not match\n * depending on locale or naming convention. The intent is a code-review\n * trigger for the common-case footgun: a developer adds a `password`\n * field to a form that already has `persist: { storage: 'local' }` and\n * doesn't notice that the existing persistence config now reaches the\n * new field. The per-element opt-in model already requires explicit\n * intent for each field; the sensitive-name heuristic adds a second\n * speed bump for the names everyone agrees never belong in localStorage.\n *\n * Word-boundary anchors (`\\b`) on short tokens prevent false positives:\n * `'description'` does not match `pwd`; `'tokenizer'` does not match\n * `token`. Multi-word forms (`api[_\\s-]?key`) tolerate snake_case,\n * kebab-case, and space-separated variants for path segments emitted\n * by humans.\n */\nexport const SENSITIVE_NAME_PATTERNS: readonly RegExp[] = [\n // Passwords and PIN-like\n /password/i,\n /passwd/i,\n /passwords/i,\n /\\bpwd\\b/i,\n /\\bpin\\b/i,\n // Card / payment\n /\\bcvv\\b/i,\n /\\bcvc\\b/i,\n /card[_\\s-]?(?:number|num)/i,\n /\\bcard\\b/i,\n /\\biban\\b/i,\n /routing[_\\s-]?number/i,\n /account[_\\s-]?number/i,\n // Government / identity\n /\\bssn\\b/i,\n /social[_\\s-]?security/i,\n /\\bdob\\b/i,\n /date[_\\s-]?of[_\\s-]?birth/i,\n /passport/i,\n /driver[_\\s-]?license/i,\n // Tax IDs (US + international common variants)\n /\\btin\\b/i,\n /\\bein\\b/i,\n /\\bitin\\b/i,\n /tax[_\\s-]?id/i,\n // Tokens, secrets, API/auth credentials\n /\\btoken\\b/i,\n /\\btokens\\b/i,\n /secret/i,\n /secrets/i,\n /api[_\\s-]?key/i,\n /api[_\\s-]?secret/i,\n /api[_\\s-]?token/i,\n /private[_\\s-]?key/i,\n /\\bbearer\\b/i,\n /\\boauth\\b/i,\n /auth[_\\s-]?token/i,\n /access[_\\s-]?token/i,\n /refresh[_\\s-]?token/i,\n /session[_\\s-]?(?:id|key|token)/i,\n // MFA / OTP\n /\\botp\\b/i,\n /one[_\\s-]?time[_\\s-]?(?:password|code)/i,\n /mfa[_\\s-]?(?:secret|seed|code|token)/i,\n /two[_\\s-]?factor[_\\s-]?(?:code|token)/i,\n /\\b2fa[_\\s-]?(?:code|token)?\\b/i,\n /recovery[_\\s-]?code/i,\n /backup[_\\s-]?code/i,\n] as const\n\n/**\n * True iff `segment` itself matches a sensitive-name pattern. Numeric\n * segments are never sensitive (array indices carry no semantic\n * weight). Used by `isSensitivePath` AND by the devtools redact\n * walk, which short-circuits whole subtrees the moment any ancestor\n * segment matches — saving an O(leaves × ancestors) regex sweep\n * per timeline event.\n */\nexport function segmentMatchesSensitive(segment: Segment): boolean {\n if (typeof segment !== 'string') return false\n for (const pattern of SENSITIVE_NAME_PATTERNS) {\n if (pattern.test(segment)) return true\n }\n return false\n}\n\n/**\n * True iff any segment of the path matches a sensitive-name pattern.\n * Match is per-segment: `'profile.password'` triggers via the `password`\n * segment; `'description.text'` does NOT match `desc` because of the\n * word boundaries on the short tokens.\n *\n * Accepts either a structured `Path` (canonical segments) or a string\n * `PathKey` (canonicalised JSON form). For PathKey, the JSON-bracket\n * shape `[\"profile\",\"password\"]` parses cleanly into segments; falling\n * back to a dotted-string split keeps simple cases working without\n * a JSON.parse round-trip.\n */\nexport function isSensitivePath(path: Path | PathKey | string): boolean {\n if (typeof path !== 'string') {\n for (const segment of path) {\n if (segmentMatchesSensitive(segment)) return true\n }\n return false\n }\n // String input: try JSON-array first (PathKey), fall back to dotted.\n if (path.startsWith('[')) {\n try {\n const parsed = JSON.parse(path) as unknown[]\n if (Array.isArray(parsed)) {\n for (const segment of parsed) {\n if (segmentMatchesSensitive(segment as Segment)) return true\n }\n return false\n }\n } catch {\n // fall through\n }\n }\n for (const segment of path.split('.')) {\n if (segmentMatchesSensitive(segment)) return true\n }\n return false\n}\n\n/**\n * Throw `SensitivePersistFieldError` if `path` matches the heuristic\n * and `acknowledged` is not true. Idempotent / pure — the call site is\n * the directive's opt-in lifecycle (on every add) and `form.persist`\n * (on every imperative checkpoint).\n */\nexport function enforceSensitiveCheck(path: Path | PathKey | string, acknowledged: boolean): void {\n if (acknowledged) return\n if (!isSensitivePath(path)) return\n throw new SensitivePersistFieldError(path)\n}\n"],"names":[],"mappings":"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"}
|