attaform 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +21 -11
  2. package/dist/chunks/devtools.cjs +4 -4
  3. package/dist/chunks/devtools.cjs.map +1 -1
  4. package/dist/chunks/devtools.mjs +2 -2
  5. package/dist/chunks/indexeddb.cjs +4 -4
  6. package/dist/chunks/indexeddb.cjs.map +1 -1
  7. package/dist/chunks/indexeddb.mjs +1 -1
  8. package/dist/chunks/local-storage.cjs +2 -2
  9. package/dist/chunks/local-storage.cjs.map +1 -1
  10. package/dist/chunks/local-storage.mjs +1 -1
  11. package/dist/chunks/session-storage.cjs +2 -2
  12. package/dist/chunks/session-storage.cjs.map +1 -1
  13. package/dist/chunks/session-storage.mjs +1 -1
  14. package/dist/index.cjs +23 -22
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +75 -70
  17. package/dist/index.d.mts +75 -70
  18. package/dist/index.d.ts +75 -70
  19. package/dist/index.mjs +6 -6
  20. package/dist/nuxt.cjs +5 -11
  21. package/dist/nuxt.cjs.map +1 -1
  22. package/dist/nuxt.d.cts +8 -0
  23. package/dist/nuxt.d.mts +8 -0
  24. package/dist/nuxt.d.ts +8 -0
  25. package/dist/nuxt.mjs +6 -12
  26. package/dist/nuxt.mjs.map +1 -1
  27. package/dist/runtime/plugins/attaform.cjs +3 -2
  28. package/dist/runtime/plugins/attaform.cjs.map +1 -1
  29. package/dist/runtime/plugins/attaform.mjs +2 -1
  30. package/dist/runtime/plugins/attaform.mjs.map +1 -1
  31. package/dist/shared/{attaform.BwaYWtMs.d.cts → attaform.B7rzpK1U.d.cts} +34 -5
  32. package/dist/shared/{attaform.BwaYWtMs.d.mts → attaform.B7rzpK1U.d.mts} +34 -5
  33. package/dist/shared/{attaform.BwaYWtMs.d.ts → attaform.B7rzpK1U.d.ts} +34 -5
  34. package/dist/shared/attaform.BAuJTWuT.d.mts +84 -0
  35. package/dist/shared/{attaform.CRk8NhlD.mjs → attaform.BfMxsfmE.mjs} +428 -49
  36. package/dist/shared/attaform.BfMxsfmE.mjs.map +1 -0
  37. package/dist/shared/attaform.Bp1c-uGF.cjs +1561 -0
  38. package/dist/shared/attaform.Bp1c-uGF.cjs.map +1 -0
  39. package/dist/shared/{attaform.CDJVeoJU.cjs → attaform.C9Ph2SMx.cjs} +49 -42
  40. package/dist/shared/{attaform.qxyip_aN.mjs.map → attaform.C9Ph2SMx.cjs.map} +1 -1
  41. package/dist/shared/attaform.CINUMjPq.mjs +29 -0
  42. package/dist/shared/attaform.CINUMjPq.mjs.map +1 -0
  43. package/dist/shared/attaform.CJttVxRj.cjs +32 -0
  44. package/dist/shared/attaform.CJttVxRj.cjs.map +1 -0
  45. package/dist/shared/attaform.CvOXSpCb.mjs +1908 -0
  46. package/dist/shared/attaform.CvOXSpCb.mjs.map +1 -0
  47. package/dist/shared/{attaform.qxyip_aN.mjs → attaform.DILbdvfo.mjs} +12 -5
  48. package/dist/shared/{attaform.CDJVeoJU.cjs.map → attaform.DILbdvfo.mjs.map} +1 -1
  49. package/dist/shared/attaform.DdnithOf.mjs +1555 -0
  50. package/dist/shared/attaform.DdnithOf.mjs.map +1 -0
  51. package/dist/shared/attaform.DfrYByDj.cjs +1916 -0
  52. package/dist/shared/attaform.DfrYByDj.cjs.map +1 -0
  53. package/dist/shared/{attaform.BOi138GE.cjs → attaform.c_NzdRyc.cjs} +4 -4
  54. package/dist/shared/{attaform.BOi138GE.cjs.map → attaform.c_NzdRyc.cjs.map} +1 -1
  55. package/dist/shared/{attaform.DXye3JKf.mjs → attaform.jrxE_xZw.mjs} +2 -2
  56. package/dist/shared/{attaform.DXye3JKf.mjs.map → attaform.jrxE_xZw.mjs.map} +1 -1
  57. package/dist/shared/attaform.ls_7jBYc.d.ts +84 -0
  58. package/dist/shared/{attaform.BgYBU8gV.cjs → attaform.rIRYSUI1.cjs} +461 -61
  59. package/dist/shared/attaform.rIRYSUI1.cjs.map +1 -0
  60. package/dist/shared/attaform.xIcmqscx.d.cts +84 -0
  61. package/dist/vite.cjs +62 -9
  62. package/dist/vite.cjs.map +1 -1
  63. package/dist/vite.d.cts +23 -32
  64. package/dist/vite.d.mts +23 -32
  65. package/dist/vite.d.ts +23 -32
  66. package/dist/vite.mjs +62 -9
  67. package/dist/vite.mjs.map +1 -1
  68. package/dist/zod-v3.cjs +9 -1553
  69. package/dist/zod-v3.cjs.map +1 -1
  70. package/dist/zod-v3.mjs +3 -1553
  71. package/dist/zod-v3.mjs.map +1 -1
  72. package/dist/zod-v4.cjs +21 -0
  73. package/dist/zod-v4.cjs.map +1 -0
  74. package/dist/zod-v4.d.cts +104 -0
  75. package/dist/zod-v4.d.mts +104 -0
  76. package/dist/zod-v4.d.ts +104 -0
  77. package/dist/zod-v4.mjs +4 -0
  78. package/dist/zod-v4.mjs.map +1 -0
  79. package/dist/zod.cjs +19 -1900
  80. package/dist/zod.cjs.map +1 -1
  81. package/dist/zod.d.cts +27 -155
  82. package/dist/zod.d.mts +27 -155
  83. package/dist/zod.d.ts +27 -155
  84. package/dist/zod.mjs +19 -1896
  85. package/dist/zod.mjs.map +1 -1
  86. package/package.json +6 -2
  87. package/dist/shared/attaform.BgYBU8gV.cjs.map +0 -1
  88. package/dist/shared/attaform.CRk8NhlD.mjs.map +0 -1
  89. package/dist/shared/attaform.RypIkgVy.cjs +0 -417
  90. package/dist/shared/attaform.RypIkgVy.cjs.map +0 -1
  91. package/dist/shared/attaform.a99dQV7Q.mjs +0 -392
  92. package/dist/shared/attaform.a99dQV7Q.mjs.map +0 -1
@@ -1,8 +1,144 @@
1
1
  'use strict';
2
2
 
3
- const sensitiveNames = require('./attaform.RypIkgVy.cjs');
4
3
  const vue = require('vue');
5
4
 
5
+ const __DEV__ = typeof process !== "undefined" && process.env["NODE_ENV"] !== "production";
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
10
+ class AttaformError extends Error {
11
+ constructor(message, options) {
12
+ super(message, options);
13
+ this.name = new.target.name;
14
+ }
15
+ }
16
+ class InvalidPathError extends AttaformError {
17
+ }
18
+ class InvalidUseFormConfigError extends AttaformError {
19
+ constructor() {
20
+ super(
21
+ "[attaform] useForm received an invalid configuration (a schema directly, no argument, or no `schema` field). Pass it as `useForm({ schema })` \u2014 the schema is one of several configuration options. See https://attaform.com/docs/api/use-form-return for the full configuration shape."
22
+ );
23
+ }
24
+ }
25
+ class SubmitErrorHandlerError extends AttaformError {
26
+ }
27
+ class RegistryNotInstalledError extends AttaformError {
28
+ constructor() {
29
+ super(
30
+ "[attaform] No registry attached to this Vue app. Component-level useForm / injectForm / useRegister auto-install the registry, but SSR helpers (renderAttaformState, hydrateAttaformState) run outside setup and require an explicit `app.use(createAttaform())` at server-render time. Add it to your SSR entry, before `renderToString`."
31
+ );
32
+ }
33
+ }
34
+ class OutsideSetupError extends AttaformError {
35
+ constructor() {
36
+ super(
37
+ "[attaform] useForm / injectForm called outside Vue setup(). Move into setup or mount a child component to trigger from an event."
38
+ );
39
+ }
40
+ }
41
+ class ReservedFormKeyError extends AttaformError {
42
+ constructor(key) {
43
+ super(
44
+ `[attaform] Form key "${key}" uses the reserved "__atta:" namespace. Use a different prefix \u2014 "__atta:" is for library-internal synthetic keys (anonymous useForm() calls without an explicit key).`
45
+ );
46
+ }
47
+ }
48
+ class SensitivePersistFieldError extends AttaformError {
49
+ constructor(path) {
50
+ const display = Array.isArray(path) ? path.join(".") : String(path);
51
+ super(
52
+ `[attaform] Refusing to persist "${display}" \u2014 this path matches a sensitive-name pattern (password / cvv / ssn / token / etc.). Storing sensitive data in client-side storage is a compliance risk (HIPAA / PII / PCI-DSS / SOC2). Fix: persist this server-side, OR pass \`acknowledgeSensitive: true\` to register() (or form.persist()) if the client-side persistence is intentional.`
53
+ );
54
+ }
55
+ }
56
+ class AnonPersistError extends AttaformError {
57
+ constructor(opts) {
58
+ super(formatAnonPersistMessage(opts));
59
+ __publicField(this, "schemaFields");
60
+ __publicField(this, "callSite");
61
+ __publicField(this, "cause");
62
+ this.schemaFields = opts.schemaFields;
63
+ this.callSite = opts.callSite;
64
+ this.cause = opts.cause;
65
+ }
66
+ }
67
+ function formatAnonPersistMessage(opts) {
68
+ const head = opts.cause === "no-key" ? `useForm({ persist: ... }) requires an explicit \`key:\`. Anonymous synthetic keys (\`__atta:anon:*\`) drift across mounts and can collide between unrelated forms \u2014 refusing to persist to a non-deterministic location.` : `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.`;
69
+ const fields = opts.schemaFields !== void 0 && opts.schemaFields.length > 0 ? ` Form fields: { ${opts.schemaFields.join(", ")} }.` : "";
70
+ const fix = opts.cause === "no-key" ? ` Fix: add \`key: '<stable-id>'\` to useForm().` : ` Fix: add \`persist: 'session'\` (or 'local') and \`key:\` to useForm(), or remove \`{ persist: true }\` from this register() call.`;
71
+ const where = opts.callSite !== void 0 ? ` ${opts.callSite}` : "";
72
+ return `[attaform] ${head}${fields}${fix}${where}`;
73
+ }
74
+
75
+ function detectSSR(options = {}) {
76
+ if (options.override !== void 0) return options.override;
77
+ return typeof window === "undefined" && typeof document === "undefined";
78
+ }
79
+
80
+ const kAttaformRegistry = Symbol.for("attaform:registry");
81
+ const kFormContext = Symbol.for("attaform:form-context");
82
+ const kFormInstanceId = Symbol.for("attaform:form-instance-id");
83
+ function createRegistry(options = {}) {
84
+ const ssr = detectSSR(options);
85
+ const defaults = Object.freeze({ ...options.defaults ?? {} });
86
+ const forms = vue.shallowReactive(/* @__PURE__ */ new Map());
87
+ const pendingHydration = vue.shallowReactive(/* @__PURE__ */ new Map());
88
+ const consumers = /* @__PURE__ */ new Map();
89
+ const evicting = /* @__PURE__ */ new Set();
90
+ function trackConsumer(key) {
91
+ consumers.set(key, (consumers.get(key) ?? 0) + 1);
92
+ let disposed = false;
93
+ return () => {
94
+ if (disposed) return;
95
+ disposed = true;
96
+ const remaining = (consumers.get(key) ?? 1) - 1;
97
+ if (remaining <= 0) {
98
+ const state = forms.get(key);
99
+ consumers.delete(key);
100
+ forms.delete(key);
101
+ if (state !== void 0) {
102
+ evicting.add(state);
103
+ void state.awaitPendingWrites().catch(() => void 0).finally(() => {
104
+ evicting.delete(state);
105
+ state.dispose();
106
+ });
107
+ }
108
+ } else {
109
+ consumers.set(key, remaining);
110
+ }
111
+ };
112
+ }
113
+ async function shutdown() {
114
+ const states = [...forms.values(), ...evicting];
115
+ await Promise.allSettled(states.map((state) => state.awaitPendingWrites()));
116
+ }
117
+ return { forms, pendingHydration, ssr, defaults, trackConsumer, shutdown };
118
+ }
119
+ function useRegistry() {
120
+ const instance = vue.getCurrentInstance();
121
+ if (instance === null) {
122
+ throw new OutsideSetupError();
123
+ }
124
+ const registry = vue.inject(kAttaformRegistry, null);
125
+ if (registry === null) {
126
+ throw new RegistryNotInstalledError();
127
+ }
128
+ return registry;
129
+ }
130
+ function getRegistryFromApp(app) {
131
+ const registry = app._attaform;
132
+ if (registry === void 0) {
133
+ throw new RegistryNotInstalledError();
134
+ }
135
+ return registry;
136
+ }
137
+ function attachRegistryToApp(app, registry) {
138
+ app.provide(kAttaformRegistry, registry);
139
+ app._attaform = registry;
140
+ }
141
+
6
142
  const isArray = Array.isArray;
7
143
  function isFunction(value) {
8
144
  return typeof value === "function";
@@ -74,6 +210,267 @@ function invokeArrayFns(fns, ...args) {
74
210
  }
75
211
  }
76
212
 
213
+ function captureUserCallSite() {
214
+ const raw = new Error().stack;
215
+ if (typeof raw !== "string") return void 0;
216
+ const lines = raw.split("\n");
217
+ for (let i = 1; i < lines.length; i++) {
218
+ const frame = lines[i];
219
+ if (frame === void 0) continue;
220
+ if (/attaform[/-]forms?/i.test(frame)) continue;
221
+ if (/\bforms\.[A-Za-z0-9_-]+\.m?js\b/.test(frame)) continue;
222
+ const trimmed = frame.trim();
223
+ if (trimmed.length === 0) continue;
224
+ return shortenSourceFrame(trimmed);
225
+ }
226
+ return void 0;
227
+ }
228
+ function shortenSourceFrame(frame) {
229
+ const match = /(?:^|\s|\()([^\s()]+):(\d+):\d+\)?$/.exec(frame);
230
+ if (match === null) return frame;
231
+ const [, urlOrPath, line] = match;
232
+ if (urlOrPath === void 0 || line === void 0) return frame;
233
+ let path = urlOrPath;
234
+ path = path.replace(/^[a-z]+:\/\/[^/]+\//i, "");
235
+ path = path.replace(/^_nuxt\//, "");
236
+ path = path.replace(/^\//, "");
237
+ return `(${path}:${line})`;
238
+ }
239
+
240
+ const REGISTER_OWNER_MARKER = Symbol.for("attaform:register-owner-marker");
241
+ const warnedNoParentRV = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
242
+ let warnedOutsideSetup = false;
243
+ function makeRegisterValueProxy(capturedRegisterValue) {
244
+ return new Proxy({}, {
245
+ get(_target, prop) {
246
+ if (prop === "__v_isRef") return true;
247
+ if (prop === "value") return capturedRegisterValue.value;
248
+ const v = capturedRegisterValue.value;
249
+ if (v === void 0) return void 0;
250
+ return Reflect.get(v, prop);
251
+ },
252
+ has(_target, prop) {
253
+ if (prop === "__v_isRef" || prop === "value") return true;
254
+ const v = capturedRegisterValue.value;
255
+ if (v === void 0) return false;
256
+ return Reflect.has(v, prop);
257
+ },
258
+ ownKeys(_target) {
259
+ const v = capturedRegisterValue.value;
260
+ if (v === void 0) return [];
261
+ return Reflect.ownKeys(v);
262
+ },
263
+ getOwnPropertyDescriptor(_target, prop) {
264
+ const v = capturedRegisterValue.value;
265
+ if (v === void 0) return void 0;
266
+ const desc = Reflect.getOwnPropertyDescriptor(v, prop);
267
+ if (desc !== void 0) {
268
+ desc.configurable = true;
269
+ }
270
+ return desc;
271
+ }
272
+ });
273
+ }
274
+ function useRegister() {
275
+ const instance = vue.getCurrentInstance();
276
+ if (instance === null) {
277
+ warnOutsideSetup();
278
+ return makeRegisterValueProxy(vue.shallowRef(void 0));
279
+ }
280
+ ensureAttaformInstalled(instance.appContext.app);
281
+ const capturedRegisterValue = vue.shallowRef(void 0);
282
+ const refreshAndStripBridgeAttrs = () => {
283
+ const rawAttrs = instance.attrs;
284
+ if ("registerValue" in rawAttrs) {
285
+ capturedRegisterValue.value = rawAttrs["registerValue"];
286
+ delete rawAttrs["registerValue"];
287
+ }
288
+ if ("value" in rawAttrs) delete rawAttrs["value"];
289
+ };
290
+ refreshAndStripBridgeAttrs();
291
+ vue.onBeforeMount(refreshAndStripBridgeAttrs);
292
+ vue.onBeforeUpdate(refreshAndStripBridgeAttrs);
293
+ vue.onMounted(() => {
294
+ const el = instance.vnode.el;
295
+ if (el !== null && el !== void 0 && typeof el === "object") {
296
+ el[REGISTER_OWNER_MARKER] = true;
297
+ }
298
+ if (capturedRegisterValue.value === void 0) {
299
+ warnNoParentRV(instance);
300
+ }
301
+ });
302
+ return makeRegisterValueProxy(capturedRegisterValue);
303
+ }
304
+ function warnOutsideSetup() {
305
+ if (!__DEV__) return;
306
+ if (warnedOutsideSetup) return;
307
+ warnedOutsideSetup = true;
308
+ const frame = captureUserCallSite();
309
+ console.warn(
310
+ `[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}` : "")
311
+ );
312
+ }
313
+ function warnNoParentRV(instance) {
314
+ if (!__DEV__ || warnedNoParentRV === null) return;
315
+ if (warnedNoParentRV.has(instance)) return;
316
+ warnedNoParentRV.add(instance);
317
+ const frame = captureUserCallSite();
318
+ console.warn(
319
+ `[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}` : "")
320
+ );
321
+ }
322
+
323
+ const idGenerator = /* @__PURE__ */ (() => {
324
+ let counter = 0;
325
+ return () => `el-${++counter}`;
326
+ })();
327
+ const elementIds = /* @__PURE__ */ new WeakMap();
328
+ function getOrAssignElementId(el) {
329
+ let id = elementIds.get(el);
330
+ if (id === void 0) {
331
+ id = idGenerator();
332
+ elementIds.set(el, id);
333
+ }
334
+ return id;
335
+ }
336
+ function createPersistOptInRegistry() {
337
+ const byPath = /* @__PURE__ */ new Map();
338
+ function add(elementId, path) {
339
+ const existing = byPath.get(path);
340
+ if (existing === void 0) {
341
+ byPath.set(path, /* @__PURE__ */ new Set([elementId]));
342
+ return;
343
+ }
344
+ existing.add(elementId);
345
+ }
346
+ function remove(elementId, path) {
347
+ const existing = byPath.get(path);
348
+ if (existing === void 0) return;
349
+ existing.delete(elementId);
350
+ if (existing.size === 0) byPath.delete(path);
351
+ }
352
+ function removeAllFor(elementId) {
353
+ for (const [path, ids] of byPath) {
354
+ if (!ids.delete(elementId)) continue;
355
+ if (ids.size === 0) byPath.delete(path);
356
+ }
357
+ }
358
+ function hasOptIn(elementId, path) {
359
+ return byPath.get(path)?.has(elementId) ?? false;
360
+ }
361
+ function hasAnyOptInForPath(path) {
362
+ const ids = byPath.get(path);
363
+ return ids !== void 0 && ids.size > 0;
364
+ }
365
+ function optedInPaths() {
366
+ return byPath.keys();
367
+ }
368
+ function isEmpty() {
369
+ return byPath.size === 0;
370
+ }
371
+ function clear() {
372
+ byPath.clear();
373
+ }
374
+ return {
375
+ add,
376
+ remove,
377
+ removeAllFor,
378
+ hasOptIn,
379
+ hasAnyOptInForPath,
380
+ optedInPaths,
381
+ isEmpty,
382
+ clear
383
+ };
384
+ }
385
+
386
+ const SENSITIVE_NAME_PATTERNS = [
387
+ // Passwords and PIN-like
388
+ /password/i,
389
+ /passwd/i,
390
+ /passwords/i,
391
+ /\bpwd\b/i,
392
+ /\bpin\b/i,
393
+ // Card / payment
394
+ /\bcvv\b/i,
395
+ /\bcvc\b/i,
396
+ /card[_\s-]?(?:number|num)/i,
397
+ /\bcard\b/i,
398
+ /\biban\b/i,
399
+ /routing[_\s-]?number/i,
400
+ /account[_\s-]?number/i,
401
+ // Government / identity
402
+ /\bssn\b/i,
403
+ /social[_\s-]?security/i,
404
+ /\bdob\b/i,
405
+ /date[_\s-]?of[_\s-]?birth/i,
406
+ /passport/i,
407
+ /driver[_\s-]?license/i,
408
+ // Tax IDs (US + international common variants)
409
+ /\btin\b/i,
410
+ /\bein\b/i,
411
+ /\bitin\b/i,
412
+ /tax[_\s-]?id/i,
413
+ // Tokens, secrets, API/auth credentials
414
+ /\btoken\b/i,
415
+ /\btokens\b/i,
416
+ /secret/i,
417
+ /secrets/i,
418
+ /api[_\s-]?key/i,
419
+ /api[_\s-]?secret/i,
420
+ /api[_\s-]?token/i,
421
+ /private[_\s-]?key/i,
422
+ /\bbearer\b/i,
423
+ /\boauth\b/i,
424
+ /auth[_\s-]?token/i,
425
+ /access[_\s-]?token/i,
426
+ /refresh[_\s-]?token/i,
427
+ /session[_\s-]?(?:id|key|token)/i,
428
+ // MFA / OTP
429
+ /\botp\b/i,
430
+ /one[_\s-]?time[_\s-]?(?:password|code)/i,
431
+ /mfa[_\s-]?(?:secret|seed|code|token)/i,
432
+ /two[_\s-]?factor[_\s-]?(?:code|token)/i,
433
+ /\b2fa[_\s-]?(?:code|token)?\b/i,
434
+ /recovery[_\s-]?code/i,
435
+ /backup[_\s-]?code/i
436
+ ];
437
+ function segmentMatchesSensitive(segment) {
438
+ if (typeof segment !== "string") return false;
439
+ for (const pattern of SENSITIVE_NAME_PATTERNS) {
440
+ if (pattern.test(segment)) return true;
441
+ }
442
+ return false;
443
+ }
444
+ function isSensitivePath(path) {
445
+ if (typeof path !== "string") {
446
+ for (const segment of path) {
447
+ if (segmentMatchesSensitive(segment)) return true;
448
+ }
449
+ return false;
450
+ }
451
+ if (path.startsWith("[")) {
452
+ try {
453
+ const parsed = JSON.parse(path);
454
+ if (Array.isArray(parsed)) {
455
+ for (const segment of parsed) {
456
+ if (segmentMatchesSensitive(segment)) return true;
457
+ }
458
+ return false;
459
+ }
460
+ } catch {
461
+ }
462
+ }
463
+ for (const segment of path.split(".")) {
464
+ if (segmentMatchesSensitive(segment)) return true;
465
+ }
466
+ return false;
467
+ }
468
+ function enforceSensitiveCheck(path, acknowledged) {
469
+ if (acknowledged) return;
470
+ if (!isSensitivePath(path)) return;
471
+ throw new SensitivePersistFieldError(path);
472
+ }
473
+
77
474
  const assignKey = Symbol.for("attaform:assign-key");
78
475
  const listenersKey = Symbol.for("attaform:directive-listeners");
79
476
  function isRegisterValue(val) {
@@ -106,7 +503,7 @@ function writeLastTypedForm(rv, next) {
106
503
  rv.lastTypedForm.value = next;
107
504
  }
108
505
  function computePersistMeta(el, registerValue) {
109
- const elementId = sensitiveNames.getOrAssignElementId(el);
506
+ const elementId = getOrAssignElementId(el);
110
507
  return { persist: registerValue.persistOptIns.hasOptIn(elementId, registerValue.path) };
111
508
  }
112
509
  const DEFAULT_ASSIGNER_TAG = Symbol.for("attaform:default-assigner-tag");
@@ -139,7 +536,7 @@ function runTransforms(initial, registerValue) {
139
536
  return { ok: true, value: v };
140
537
  }
141
538
  function logTransformFailure(path, index, fn, err) {
142
- if (sensitiveNames.__DEV__) {
539
+ if (__DEV__) {
143
540
  const namePart = fn.name !== "" ? `, '${fn.name}'` : "";
144
541
  console.error(
145
542
  `[attaform] transform threw for path '${path}' (index ${index}${namePart}) \u2014 write aborted. Transforms must not throw; wrap your own try/catch if the throw is recoverable. Original error:`,
@@ -158,7 +555,7 @@ function applyElementCoerce(value, registerValue) {
158
555
  return registerValue.coerceElement !== void 0 ? registerValue.coerceElement(value) : value;
159
556
  }
160
557
  function logTransformAsync(path) {
161
- if (sensitiveNames.__DEV__) {
558
+ if (__DEV__) {
162
559
  console.error(
163
560
  `[attaform] transform pipeline for path '${path}' returned a Promise \u2014 transforms must be sync. Use async field validation for canonicalize-before-write patterns. Write aborted.`
164
561
  );
@@ -202,7 +599,7 @@ function syncPersistOptIn(el, value, oldValue) {
202
599
  const wasOptedIn = isRegisterValue(oldValue) && oldValue.persist === true;
203
600
  const wantsOptIn = isRegisterValue(value) && value.persist === true;
204
601
  if (!wasOptedIn && !wantsOptIn) return;
205
- const elementId = sensitiveNames.getOrAssignElementId(el);
602
+ const elementId = getOrAssignElementId(el);
206
603
  if (wasOptedIn) {
207
604
  const old = oldValue;
208
605
  const samePathAndRegistry = wantsOptIn && value.path === old.path && value.persistOptIns === old.persistOptIns;
@@ -212,7 +609,7 @@ function syncPersistOptIn(el, value, oldValue) {
212
609
  }
213
610
  if (wantsOptIn) {
214
611
  const v = value;
215
- sensitiveNames.enforceSensitiveCheck(v.path, v.acknowledgeSensitive);
612
+ enforceSensitiveCheck(v.path, v.acknowledgeSensitive);
216
613
  v.persistOptIns.add(elementId, v.path);
217
614
  }
218
615
  }
@@ -604,7 +1001,7 @@ function setSelected(el, value) {
604
1001
  const isMultiple = el.multiple;
605
1002
  const isArrayValue = isArray(externalValue);
606
1003
  if (isMultiple && !isArrayValue && !isSet(externalValue)) {
607
- if (sensitiveNames.__DEV__) {
1004
+ if (__DEV__) {
608
1005
  vue.warn(
609
1006
  `<select multiple v-register> expected an Array or Set, got ${Object.prototype.toString.call(externalValue).slice(8, -1)}. Bind to a list-typed schema (e.g. z.array(z.string()) or z.set(z.string())).`
610
1007
  );
@@ -612,7 +1009,7 @@ function setSelected(el, value) {
612
1009
  return;
613
1010
  }
614
1011
  if (!isMultiple && (isArrayValue || isSet(externalValue))) {
615
- if (sensitiveNames.__DEV__) {
1012
+ if (__DEV__) {
616
1013
  vue.warn(
617
1014
  `<select v-register> (no \`multiple\` attribute) expected a scalar value for its binding, but got ${Object.prototype.toString.call(externalValue).slice(8, -1)}. Add the \`multiple\` attribute to bind to a list, or use a scalar schema (e.g. \`z.string()\`) for a single-select binding.`
618
1015
  );
@@ -660,15 +1057,15 @@ function getCheckboxValue(el, checked) {
660
1057
  return key in el ? el[key] : checked;
661
1058
  }
662
1059
  const SUPPORTED_TAGS = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT"]);
663
- const warnedUnsupportedElements = sensitiveNames.__DEV__ ? /* @__PURE__ */ new WeakSet() : null;
1060
+ const warnedUnsupportedElements = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
664
1061
  const vRegisterDynamic = {
665
1062
  created(el, binding, vnode) {
666
1063
  syncPersistOptIn(el, binding.value, void 0);
667
1064
  callModelHook(el, binding, vnode, null, "created");
668
- if (sensitiveNames.__DEV__ && warnedUnsupportedElements !== null && !SUPPORTED_TAGS.has(el.tagName) && !warnedUnsupportedElements.has(el)) {
1065
+ if (__DEV__ && warnedUnsupportedElements !== null && !SUPPORTED_TAGS.has(el.tagName) && !warnedUnsupportedElements.has(el)) {
669
1066
  void vue.nextTick(() => {
670
1067
  if (warnedUnsupportedElements.has(el)) return;
671
- const hasMarker = el[sensitiveNames.REGISTER_OWNER_MARKER] === true;
1068
+ const hasMarker = el[REGISTER_OWNER_MARKER] === true;
672
1069
  const hasUserAssigner = !isDefaultAssigner(
673
1070
  el[assignKey]
674
1071
  );
@@ -694,7 +1091,7 @@ const vRegisterDynamic = {
694
1091
  beforeUnmount(el, { value }) {
695
1092
  removeTrackedListeners(el);
696
1093
  if (isRegisterValue(value)) {
697
- value.persistOptIns.removeAllFor(sensitiveNames.getOrAssignElementId(el));
1094
+ value.persistOptIns.removeAllFor(getOrAssignElementId(el));
698
1095
  }
699
1096
  if (!isRegisterValue(value)) return;
700
1097
  value.deregisterElement(el);
@@ -707,7 +1104,7 @@ const vRegisterFileNoop = {
707
1104
  created(el, { value }) {
708
1105
  if (!isRegisterValue(value)) return;
709
1106
  value.registerElement(el);
710
- if (sensitiveNames.__DEV__) {
1107
+ if (__DEV__) {
711
1108
  vue.warn(
712
1109
  '[attaform] v-register on <input type="file"> is not supported. Handle uploads with a manual @change listener.'
713
1110
  );
@@ -735,63 +1132,66 @@ function callModelHook(el, binding, vnode, prevVNode, hook) {
735
1132
  }
736
1133
  const vRegister = vRegisterDynamic;
737
1134
 
1135
+ function installAttaformOnApp(app, options, source) {
1136
+ if (app._attaform !== void 0) {
1137
+ if (__DEV__ && source === "explicit") {
1138
+ console.warn(
1139
+ "[attaform] createAttaform() install was called twice on the same app; the second call is a no-op. Likely cause: registering the plugin via both the Nuxt module AND a manual `app.use(...)`."
1140
+ );
1141
+ }
1142
+ return app._attaform;
1143
+ }
1144
+ const registry = createRegistry(options);
1145
+ attachRegistryToApp(app, registry);
1146
+ app.directive("register", vRegister);
1147
+ if (options.devtools !== false && !registry.ssr) {
1148
+ void (async () => {
1149
+ try {
1150
+ const { setupAttaformDevtools } = await import('../chunks/devtools.cjs');
1151
+ await setupAttaformDevtools(app, registry);
1152
+ } catch {
1153
+ }
1154
+ })();
1155
+ }
1156
+ return registry;
1157
+ }
1158
+ function ensureAttaformInstalled(app) {
1159
+ return installAttaformOnApp(app, {}, "lazy");
1160
+ }
738
1161
  function createAttaform(options = {}) {
739
1162
  const plugin = {
740
1163
  install(app) {
741
- if (app._attaform !== void 0) {
742
- if (sensitiveNames.__DEV__) {
743
- console.warn(
744
- "[attaform] createAttaform() install was called twice on the same app; the second call is a no-op. Likely cause: registering the plugin via both the Nuxt module AND a manual `app.use(...)`."
745
- );
746
- }
747
- return;
748
- }
749
- const registry = sensitiveNames.createRegistry(options);
750
- sensitiveNames.attachRegistryToApp(app, registry);
751
- app.directive("register", vRegister);
752
- if (options.devtools !== false && !registry.ssr) {
753
- void (async () => {
754
- try {
755
- const { setupAttaformDevtools } = await import('../chunks/devtools.cjs');
756
- await setupAttaformDevtools(app, registry);
757
- } catch {
758
- }
759
- })();
760
- }
1164
+ installAttaformOnApp(app, options, "explicit");
761
1165
  }
762
1166
  };
763
1167
  return plugin;
764
1168
  }
765
1169
 
766
- function renderAttaformState(app) {
767
- const registry = sensitiveNames.getRegistryFromApp(app);
768
- const forms = [];
769
- for (const [key, state] of registry.forms) {
770
- const transientList = Array.from(state.blankPaths);
771
- forms.push([
772
- key,
773
- {
774
- form: state.form.value,
775
- schemaErrors: Array.from(state.schemaErrors.entries()),
776
- userErrors: Array.from(state.userErrors.entries()),
777
- fields: Array.from(state.fields.entries()),
778
- ...transientList.length > 0 ? { blankPaths: transientList } : {}
779
- }
780
- ]);
781
- }
782
- return { forms };
783
- }
784
- function hydrateAttaformState(app, payload) {
785
- const registry = sensitiveNames.getRegistryFromApp(app);
786
- for (const [key, data] of payload.forms) {
787
- registry.pendingHydration.set(key, data);
788
- }
789
- }
790
-
1170
+ exports.AnonPersistError = AnonPersistError;
1171
+ exports.AttaformError = AttaformError;
1172
+ exports.InvalidPathError = InvalidPathError;
1173
+ exports.InvalidUseFormConfigError = InvalidUseFormConfigError;
1174
+ exports.OutsideSetupError = OutsideSetupError;
1175
+ exports.RegistryNotInstalledError = RegistryNotInstalledError;
1176
+ exports.ReservedFormKeyError = ReservedFormKeyError;
1177
+ exports.SensitivePersistFieldError = SensitivePersistFieldError;
1178
+ exports.SubmitErrorHandlerError = SubmitErrorHandlerError;
1179
+ exports.__DEV__ = __DEV__;
791
1180
  exports.assignKey = assignKey;
1181
+ exports.captureUserCallSite = captureUserCallSite;
792
1182
  exports.createAttaform = createAttaform;
793
- exports.hydrateAttaformState = hydrateAttaformState;
1183
+ exports.createPersistOptInRegistry = createPersistOptInRegistry;
1184
+ exports.createRegistry = createRegistry;
1185
+ exports.enforceSensitiveCheck = enforceSensitiveCheck;
1186
+ exports.ensureAttaformInstalled = ensureAttaformInstalled;
1187
+ exports.getRegistryFromApp = getRegistryFromApp;
794
1188
  exports.isRegisterValue = isRegisterValue;
795
- exports.renderAttaformState = renderAttaformState;
1189
+ exports.isSensitivePath = isSensitivePath;
1190
+ exports.kAttaformRegistry = kAttaformRegistry;
1191
+ exports.kFormContext = kFormContext;
1192
+ exports.kFormInstanceId = kFormInstanceId;
1193
+ exports.segmentMatchesSensitive = segmentMatchesSensitive;
1194
+ exports.useRegister = useRegister;
1195
+ exports.useRegistry = useRegistry;
796
1196
  exports.vRegister = vRegister;
797
- //# sourceMappingURL=attaform.BgYBU8gV.cjs.map
1197
+ //# sourceMappingURL=attaform.rIRYSUI1.cjs.map