attaform 0.21.0 → 0.21.2

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 (108) hide show
  1. package/dist/chunks/dev-key-collision-warnings.cjs +1 -1
  2. package/dist/chunks/dev-key-collision-warnings.mjs +1 -1
  3. package/dist/chunks/devtools.cjs +1 -1
  4. package/dist/chunks/devtools.mjs +1 -1
  5. package/dist/chunks/fingerprint2.cjs +1 -1
  6. package/dist/chunks/fingerprint2.mjs +1 -1
  7. package/dist/chunks/indexeddb.cjs +1 -1
  8. package/dist/chunks/indexeddb.mjs +1 -1
  9. package/dist/chunks/local-storage.cjs +1 -1
  10. package/dist/chunks/local-storage.mjs +1 -1
  11. package/dist/chunks/multi-tab-sync.cjs +2 -2
  12. package/dist/chunks/multi-tab-sync.mjs +2 -2
  13. package/dist/chunks/session-storage.cjs +1 -1
  14. package/dist/chunks/session-storage.mjs +1 -1
  15. package/dist/chunks/wire-persistence.cjs +2 -2
  16. package/dist/chunks/wire-persistence.mjs +2 -2
  17. package/dist/index.cjs +37 -24
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +31 -28
  20. package/dist/index.d.mts +31 -28
  21. package/dist/index.d.ts +31 -28
  22. package/dist/index.mjs +38 -25
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/nuxt.d.cts +1 -1
  25. package/dist/nuxt.d.mts +1 -1
  26. package/dist/nuxt.d.ts +1 -1
  27. package/dist/runtime/components/AttaformDevtoolsPanel.vue +396 -216
  28. package/dist/runtime/components/DevtoolsValueTree.vue +176 -114
  29. package/dist/runtime/plugins/attaform.cjs +2 -2
  30. package/dist/runtime/plugins/attaform.mjs +2 -2
  31. package/dist/shared/{attaform.BA3vRDos.cjs → attaform.B5LNzqQh.cjs} +349 -277
  32. package/dist/shared/attaform.B5LNzqQh.cjs.map +1 -0
  33. package/dist/shared/{attaform.PnqML3xW.cjs → attaform.BBDIKtKY.cjs} +13 -16
  34. package/dist/shared/attaform.BBDIKtKY.cjs.map +1 -0
  35. package/dist/shared/{attaform.CRsXyy-Y.d.ts → attaform.BCcrLApm.d.mts} +131 -64
  36. package/dist/shared/{attaform.BupwXkj_.mjs → attaform.BFWb6hDk.mjs} +29 -23
  37. package/dist/shared/attaform.BFWb6hDk.mjs.map +1 -0
  38. package/dist/shared/{attaform.7lzO9pdM.d.mts → attaform.BGf_J22U.d.ts} +131 -64
  39. package/dist/shared/{attaform.BnK_bfcb.mjs → attaform.BVeLgfEh.mjs} +14 -17
  40. package/dist/shared/attaform.BVeLgfEh.mjs.map +1 -0
  41. package/dist/shared/{attaform.BK1RE2ha.d.ts → attaform.BYgioWLF.d.ts} +2 -2
  42. package/dist/shared/{attaform.F8LMHHWV.d.cts → attaform.BkjJfMvJ.d.cts} +131 -64
  43. package/dist/shared/{attaform.BDIEq9qP.d.cts → attaform.BoY6RZUl.d.cts} +2 -2
  44. package/dist/shared/{attaform.CEf6wYfD.cjs → attaform.BwLp9KM7.cjs} +2 -2
  45. package/dist/shared/{attaform.CEf6wYfD.cjs.map → attaform.BwLp9KM7.cjs.map} +1 -1
  46. package/dist/shared/{attaform._rsCZy2j.cjs → attaform.BwrowMp2.cjs} +25 -45
  47. package/dist/shared/attaform.BwrowMp2.cjs.map +1 -0
  48. package/dist/shared/{attaform.ezb5Nh2t.mjs → attaform.C41gjp-a.mjs} +2 -2
  49. package/dist/shared/{attaform.ezb5Nh2t.mjs.map → attaform.C41gjp-a.mjs.map} +1 -1
  50. package/dist/shared/{attaform.BM6YD9kZ.cjs → attaform.CR6wGvNu.cjs} +29 -23
  51. package/dist/shared/attaform.CR6wGvNu.cjs.map +1 -0
  52. package/dist/shared/{attaform.DSqO6Db7.mjs → attaform.CTheKoTc.mjs} +705 -282
  53. package/dist/shared/attaform.CTheKoTc.mjs.map +1 -0
  54. package/dist/shared/{attaform.BzvOdiSI.cjs → attaform.CcnF1AKJ.cjs} +4 -4
  55. package/dist/shared/attaform.CcnF1AKJ.cjs.map +1 -0
  56. package/dist/shared/{attaform.BQ6drorq.d.mts → attaform.CnEl--PF.d.mts} +2 -2
  57. package/dist/shared/{attaform.CkjTapyq.mjs → attaform.CrD73S4m.mjs} +4 -4
  58. package/dist/shared/attaform.CrD73S4m.mjs.map +1 -0
  59. package/dist/shared/{attaform.BUszFoKq.cjs → attaform.D2ZuIOCf.cjs} +711 -287
  60. package/dist/shared/attaform.D2ZuIOCf.cjs.map +1 -0
  61. package/dist/shared/{attaform.r3PePkDR.mjs → attaform.D6GYGshL.mjs} +25 -45
  62. package/dist/shared/attaform.D6GYGshL.mjs.map +1 -0
  63. package/dist/shared/{attaform.Y_Mgg0Yp.mjs → attaform.DP-u7_tk.mjs} +348 -277
  64. package/dist/shared/attaform.DP-u7_tk.mjs.map +1 -0
  65. package/dist/shared/{attaform.B1nyO4ec.d.cts → attaform.ory-3WhV.d.cts} +395 -176
  66. package/dist/shared/{attaform.B1nyO4ec.d.mts → attaform.ory-3WhV.d.mts} +395 -176
  67. package/dist/shared/{attaform.B1nyO4ec.d.ts → attaform.ory-3WhV.d.ts} +395 -176
  68. package/dist/transforms.cjs +1 -1
  69. package/dist/transforms.mjs +1 -1
  70. package/dist/vite.cjs +1 -1
  71. package/dist/vite.mjs +1 -1
  72. package/dist/zod-v3.cjs +3 -4
  73. package/dist/zod-v3.cjs.map +1 -1
  74. package/dist/zod-v3.d.cts +4 -4
  75. package/dist/zod-v3.d.mts +4 -4
  76. package/dist/zod-v3.d.ts +4 -4
  77. package/dist/zod-v3.mjs +2 -3
  78. package/dist/zod-v3.mjs.map +1 -1
  79. package/dist/zod-v4.cjs +3 -4
  80. package/dist/zod-v4.cjs.map +1 -1
  81. package/dist/zod-v4.d.cts +4 -4
  82. package/dist/zod-v4.d.mts +4 -4
  83. package/dist/zod-v4.d.ts +4 -4
  84. package/dist/zod-v4.mjs +2 -3
  85. package/dist/zod-v4.mjs.map +1 -1
  86. package/dist/zod.cjs +6 -6
  87. package/dist/zod.cjs.map +1 -1
  88. package/dist/zod.d.cts +77 -26
  89. package/dist/zod.d.mts +77 -26
  90. package/dist/zod.d.ts +77 -26
  91. package/dist/zod.mjs +5 -6
  92. package/dist/zod.mjs.map +1 -1
  93. package/package.json +3 -11
  94. package/dist/shared/attaform.BA3vRDos.cjs.map +0 -1
  95. package/dist/shared/attaform.BM6YD9kZ.cjs.map +0 -1
  96. package/dist/shared/attaform.BUszFoKq.cjs.map +0 -1
  97. package/dist/shared/attaform.BnK_bfcb.mjs.map +0 -1
  98. package/dist/shared/attaform.BupwXkj_.mjs.map +0 -1
  99. package/dist/shared/attaform.BzvOdiSI.cjs.map +0 -1
  100. package/dist/shared/attaform.CkjTapyq.mjs.map +0 -1
  101. package/dist/shared/attaform.DSqO6Db7.mjs.map +0 -1
  102. package/dist/shared/attaform.PnqML3xW.cjs.map +0 -1
  103. package/dist/shared/attaform.Y_Mgg0Yp.mjs.map +0 -1
  104. package/dist/shared/attaform._rsCZy2j.cjs.map +0 -1
  105. package/dist/shared/attaform.r3PePkDR.mjs.map +0 -1
  106. package/dist/shared/{attaform.DSD85fHb.d.cts → attaform.nf83TIR5.d.cts} +10 -10
  107. package/dist/shared/{attaform.DSD85fHb.d.mts → attaform.nf83TIR5.d.mts} +10 -10
  108. package/dist/shared/{attaform.DSD85fHb.d.ts → attaform.nf83TIR5.d.ts} +10 -10
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const vue = require('vue');
4
- const paths = require('./attaform.BA3vRDos.cjs');
4
+ const paths = require('./attaform.B5LNzqQh.cjs');
5
5
 
6
6
  function safeAssign(target, key, value) {
7
7
  if (key === "__proto__") {
@@ -15,10 +15,14 @@ function safeAssign(target, key, value) {
15
15
  }
16
16
  target[key] = value;
17
17
  }
18
+ function isShadowedKey(key) {
19
+ return key in Object.prototype;
20
+ }
18
21
  function safeOwnRead(target, key) {
19
- if (key === "__proto__") {
20
- const desc = Object.getOwnPropertyDescriptor(target, "__proto__");
21
- return desc?.value;
22
+ if (isShadowedKey(key)) {
23
+ const desc = Object.getOwnPropertyDescriptor(target, key);
24
+ if (desc === void 0) return void 0;
25
+ return "value" in desc ? desc.value : target[key];
22
26
  }
23
27
  return target[key];
24
28
  }
@@ -37,6 +41,10 @@ function descendStep(value, segment) {
37
41
  }
38
42
  const record = value;
39
43
  const key = typeof segment === "number" ? String(segment) : segment;
44
+ if (isShadowedKey(key)) {
45
+ if (!safeOwnHas(record, key)) return NOT_FOUND;
46
+ return safeOwnRead(record, key);
47
+ }
40
48
  if (!(key in record)) return NOT_FOUND;
41
49
  return record[key];
42
50
  }
@@ -66,6 +74,7 @@ function hasAtPath(root, path) {
66
74
  return typeof last === "number" && last >= 0 && last < current.length;
67
75
  }
68
76
  const key = typeof last === "number" ? String(last) : last;
77
+ if (isShadowedKey(key)) return safeOwnHas(current, key);
69
78
  return key in current;
70
79
  }
71
80
  function isPlainRecord(value) {
@@ -153,68 +162,80 @@ function mergeStructuralImpl(schema, scratch, consumer, defaultValue) {
153
162
  }
154
163
  if (consumer === null) return null;
155
164
  if (Array.isArray(consumer)) {
156
- const shape = resolveArrayShape(schema, scratch);
157
- const isTuple = typeof shape === "number";
158
- const targetLen = isTuple ? shape : consumer.length;
159
- let cachedElementDefault;
160
- let cachedElementDefaultRead = false;
161
- let mutated = targetLen > consumer.length;
162
- const out = consumer.slice();
163
- while (out.length < targetLen) out.push(void 0);
164
- for (let i = 0; i < targetLen; i++) {
165
- scratch.push(i);
166
- let elemDefault;
167
- if (isTuple) {
168
- elemDefault = schema.getDefaultAtPath(scratch);
169
- } else {
170
- if (!cachedElementDefaultRead) {
171
- cachedElementDefault = schema.getDefaultAtPath(scratch);
172
- cachedElementDefaultRead = true;
173
- }
174
- elemDefault = cachedElementDefault;
175
- }
176
- const consumerElem = i < consumer.length ? consumer[i] : void 0;
177
- const merged = mergeStructuralImpl(schema, scratch, consumerElem, elemDefault);
178
- scratch.pop();
179
- if (merged !== consumerElem) {
180
- out[i] = merged;
181
- mutated = true;
182
- }
183
- }
184
- return mutated ? out : consumer;
165
+ return mergeStructuralArray(schema, scratch, consumer);
185
166
  }
186
167
  if (isPlainRecord(consumer)) {
187
168
  if (!isPlainRecord(defaultValue)) {
188
169
  return consumer;
189
170
  }
190
- let mutated = false;
191
171
  const out = { ...consumer };
192
- for (const key of Object.keys(defaultValue)) {
193
- if (!safeOwnHas(consumer, key)) {
194
- const defAtKey = safeOwnRead(defaultValue, key);
195
- scratch.push(key);
196
- const filled = mergeStructuralImpl(schema, scratch, void 0, defAtKey);
197
- scratch.pop();
198
- if (filled !== void 0) {
199
- safeAssign(out, key, filled);
200
- mutated = true;
201
- }
172
+ const filledAny = fillMissingKeysFromDefault(schema, scratch, consumer, defaultValue, out);
173
+ const recursedAny = recurseIntoConsumerKeys(schema, scratch, consumer, defaultValue, out);
174
+ return filledAny || recursedAny ? out : consumer;
175
+ }
176
+ return consumer;
177
+ }
178
+ function mergeStructuralArray(schema, scratch, consumer) {
179
+ const shape = resolveArrayShape(schema, scratch);
180
+ const isTuple = typeof shape === "number";
181
+ const targetLen = isTuple ? shape : consumer.length;
182
+ let cachedElementDefault;
183
+ let cachedElementDefaultRead = false;
184
+ let mutated = targetLen > consumer.length;
185
+ const out = consumer.slice();
186
+ while (out.length < targetLen) out.push(void 0);
187
+ for (let i = 0; i < targetLen; i++) {
188
+ scratch.push(i);
189
+ let elemDefault;
190
+ if (isTuple) {
191
+ elemDefault = schema.getDefaultAtPath(scratch);
192
+ } else {
193
+ if (!cachedElementDefaultRead) {
194
+ cachedElementDefault = schema.getDefaultAtPath(scratch);
195
+ cachedElementDefaultRead = true;
202
196
  }
197
+ elemDefault = cachedElementDefault;
203
198
  }
204
- for (const key of Object.keys(consumer)) {
205
- const cVal = safeOwnRead(consumer, key);
206
- if (cVal === void 0) continue;
199
+ const consumerElem = i < consumer.length ? consumer[i] : void 0;
200
+ const merged = mergeStructuralImpl(schema, scratch, consumerElem, elemDefault);
201
+ scratch.pop();
202
+ if (merged !== consumerElem) {
203
+ out[i] = merged;
204
+ mutated = true;
205
+ }
206
+ }
207
+ return mutated ? out : consumer;
208
+ }
209
+ function fillMissingKeysFromDefault(schema, scratch, consumer, defaultValue, out) {
210
+ let mutated = false;
211
+ for (const key of Object.keys(defaultValue)) {
212
+ if (!safeOwnHas(consumer, key)) {
213
+ const defAtKey = safeOwnRead(defaultValue, key);
207
214
  scratch.push(key);
208
- const merged = mergeStructuralImpl(schema, scratch, cVal, safeOwnRead(defaultValue, key));
215
+ const filled = mergeStructuralImpl(schema, scratch, void 0, defAtKey);
209
216
  scratch.pop();
210
- if (merged !== cVal) {
211
- safeAssign(out, key, merged);
217
+ if (filled !== void 0) {
218
+ safeAssign(out, key, filled);
212
219
  mutated = true;
213
220
  }
214
221
  }
215
- return mutated ? out : consumer;
216
222
  }
217
- return consumer;
223
+ return mutated;
224
+ }
225
+ function recurseIntoConsumerKeys(schema, scratch, consumer, defaultValue, out) {
226
+ let mutated = false;
227
+ for (const key of Object.keys(consumer)) {
228
+ const cVal = safeOwnRead(consumer, key);
229
+ if (cVal === void 0) continue;
230
+ scratch.push(key);
231
+ const merged = mergeStructuralImpl(schema, scratch, cVal, safeOwnRead(defaultValue, key));
232
+ scratch.pop();
233
+ if (merged !== cVal) {
234
+ safeAssign(out, key, merged);
235
+ mutated = true;
236
+ }
237
+ }
238
+ return mutated;
218
239
  }
219
240
  function setAtPathWithSchemaFill(root, schema, fullPath, value) {
220
241
  if (fullPath.length === 0) return value;
@@ -297,55 +318,27 @@ function diffAndApply(oldValue, newValue, prefix, visit) {
297
318
  const oldIsDescendable = isDescendable(oldValue);
298
319
  const newIsDescendable = isDescendable(newValue);
299
320
  if (oldValue === void 0 && newIsDescendable) {
300
- if (Array.isArray(newValue)) {
301
- for (let i = 0; i < newValue.length; i++) {
302
- diffAndApply(void 0, newValue[i], appendSegment(prefix, i), visit);
303
- }
304
- } else {
305
- const rec = newValue;
306
- for (const k of Object.keys(rec)) {
307
- diffAndApply(void 0, rec[k], appendSegment(prefix, k), visit);
308
- }
309
- }
321
+ walkNewDescendable(newValue, prefix, visit);
310
322
  return;
311
323
  }
312
324
  if (oldIsDescendable && newValue === void 0) {
313
- if (Array.isArray(oldValue)) {
314
- for (let i = 0; i < oldValue.length; i++) {
315
- diffAndApply(oldValue[i], void 0, appendSegment(prefix, i), visit);
316
- }
317
- } else {
318
- const rec = oldValue;
319
- for (const k of Object.keys(rec)) {
320
- diffAndApply(rec[k], void 0, appendSegment(prefix, k), visit);
321
- }
322
- }
325
+ walkOldDescendable(oldValue, prefix, visit);
323
326
  return;
324
327
  }
325
328
  if (oldIsDescendable && newIsDescendable) {
326
329
  const oldIsArray = Array.isArray(oldValue);
327
330
  const newIsArray = Array.isArray(newValue);
328
331
  if (oldIsArray && newIsArray) {
329
- const oldArr = oldValue;
330
- const newArr = newValue;
331
- const max = Math.max(oldArr.length, newArr.length);
332
- for (let i = 0; i < max; i++) {
333
- diffAndApply(oldArr[i], newArr[i], appendSegment(prefix, i), visit);
334
- }
332
+ diffArraysLockstep(oldValue, newValue, prefix, visit);
335
333
  return;
336
334
  }
337
335
  if (!oldIsArray && !newIsArray) {
338
- const oldRec = oldValue;
339
- const newRec = newValue;
340
- const seen = /* @__PURE__ */ new Set();
341
- for (const k of Object.keys(oldRec)) {
342
- seen.add(k);
343
- diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
344
- }
345
- for (const k of Object.keys(newRec)) {
346
- if (seen.has(k)) continue;
347
- diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
348
- }
336
+ diffObjectsLockstep(
337
+ oldValue,
338
+ newValue,
339
+ prefix,
340
+ visit
341
+ );
349
342
  return;
350
343
  }
351
344
  visit({ kind: "changed", path: prefix, oldValue, newValue });
@@ -369,6 +362,47 @@ function diffAndApply(oldValue, newValue, prefix, visit) {
369
362
  }
370
363
  visit({ kind: "changed", path: prefix, oldValue, newValue });
371
364
  }
365
+ function walkNewDescendable(newValue, prefix, visit) {
366
+ if (Array.isArray(newValue)) {
367
+ for (let i = 0; i < newValue.length; i++) {
368
+ diffAndApply(void 0, newValue[i], appendSegment(prefix, i), visit);
369
+ }
370
+ } else {
371
+ const rec = newValue;
372
+ for (const k of Object.keys(rec)) {
373
+ diffAndApply(void 0, rec[k], appendSegment(prefix, k), visit);
374
+ }
375
+ }
376
+ }
377
+ function walkOldDescendable(oldValue, prefix, visit) {
378
+ if (Array.isArray(oldValue)) {
379
+ for (let i = 0; i < oldValue.length; i++) {
380
+ diffAndApply(oldValue[i], void 0, appendSegment(prefix, i), visit);
381
+ }
382
+ } else {
383
+ const rec = oldValue;
384
+ for (const k of Object.keys(rec)) {
385
+ diffAndApply(rec[k], void 0, appendSegment(prefix, k), visit);
386
+ }
387
+ }
388
+ }
389
+ function diffArraysLockstep(oldArr, newArr, prefix, visit) {
390
+ const max = Math.max(oldArr.length, newArr.length);
391
+ for (let i = 0; i < max; i++) {
392
+ diffAndApply(oldArr[i], newArr[i], appendSegment(prefix, i), visit);
393
+ }
394
+ }
395
+ function diffObjectsLockstep(oldRec, newRec, prefix, visit) {
396
+ const seen = /* @__PURE__ */ new Set();
397
+ for (const k of Object.keys(oldRec)) {
398
+ seen.add(k);
399
+ diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
400
+ }
401
+ for (const k of Object.keys(newRec)) {
402
+ if (seen.has(k)) continue;
403
+ diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
404
+ }
405
+ }
372
406
  function applyChangedKeys(target, source) {
373
407
  if (!isDescendable(target) || !isDescendable(source)) return false;
374
408
  const targetIsArray = Array.isArray(target);
@@ -472,7 +506,12 @@ function computeVerdict(field, formMeta) {
472
506
  if (field.valid === true && field.blank !== true && field.dirty === true) return "success";
473
507
  return "idle";
474
508
  }
475
- const DEFAULT_TIMINGS = { showDelay: 100, minVisible: 120 };
509
+ function earliestNonNull(a, b) {
510
+ if (a === null) return b;
511
+ if (b === null) return a;
512
+ return a < b ? a : b;
513
+ }
514
+ const DEFAULT_TIMINGS = { showDelay: 120, minVisible: 120 };
476
515
  const FOCUS_OUT_GRACE = 16;
477
516
  const defaultFamily = /* @__PURE__ */ new WeakSet();
478
517
  function isDefaultDisplayState(fn) {
@@ -482,10 +521,11 @@ function makeDefaultDisplayState({
482
521
  showDelay,
483
522
  minVisible
484
523
  }) {
485
- const reducer = (prev, { field, formMeta, validatingSince, now }) => {
524
+ const reducer = (prev, { field, formMeta, validatingSince, transformingSince, now }) => {
486
525
  const verdict = computeVerdict(field, formMeta);
487
526
  if (!isGateOpen(field, formMeta)) return { display: verdict };
488
- if (validatingSince === null) {
527
+ const inFlightSince = earliestNonNull(validatingSince, transformingSince);
528
+ if (inFlightSince === null) {
489
529
  if (prev.display === "pending") {
490
530
  const shownAt = prev.pendingShownAt ?? now;
491
531
  if (now < shownAt + minVisible)
@@ -496,8 +536,8 @@ function makeDefaultDisplayState({
496
536
  if (prev.display === "pending")
497
537
  return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
498
538
  const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
499
- if (now - validatingSince < window) {
500
- return { display: prev.display, reviewAt: validatingSince + window };
539
+ if (now - inFlightSince < window) {
540
+ return { display: prev.display, reviewAt: inFlightSince + window };
501
541
  }
502
542
  return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
503
543
  };
@@ -610,6 +650,12 @@ function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, options
610
650
  return c;
611
651
  };
612
652
  }
653
+ function resolveFieldMetaAndLabel(state, segments) {
654
+ const resolved = state.schema.getFieldMetaAtPath ? state.schema.getFieldMetaAtPath(segments) : EMPTY_RESOLVED_FIELD_META;
655
+ const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
656
+ const label = resolved.label || humanize(lastSegment);
657
+ return { resolved, label };
658
+ }
613
659
  function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
614
660
  const record = state.fields.get(key);
615
661
  const value = state.getValueAtPath(segments);
@@ -623,15 +669,15 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
623
669
  if (blankForKey !== void 0) errors.push(...blankForKey);
624
670
  if (userForKey !== void 0) errors.push(...userForKey);
625
671
  const validating = (state.fieldValidationCounts.get(key) ?? 0) > 0;
672
+ const transforming = (state.fieldTransformCounts.get(key) ?? 0) > 0;
673
+ const transformError = state.transformErrors.get(key) ?? null;
626
674
  const gated = state.pathHasAsyncValidation(segments) && !state.firstValidationDone.value;
627
675
  const isOrphan = segments.length > 0 && !hasAtPath(state.form.value, segments) && isUnderStubAncestor(state, segments);
628
676
  const valid = !gated && errors.length === 0 && !validating && !isOrphan;
629
677
  const elementRecord = state.elements.get(key);
630
678
  const elementsArr = elementRecord ? Object.freeze([...elementRecord.elements]) : EMPTY_ELEMENTS;
631
679
  const firstElement = elementsArr[0] ?? null;
632
- const resolved = state.schema.getFieldMetaAtPath ? state.schema.getFieldMetaAtPath(segments) : EMPTY_RESOLVED_FIELD_META;
633
- const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
634
- const label = resolved.label || humanize(lastSegment);
680
+ const { resolved, label } = resolveFieldMetaAndLabel(state, segments);
635
681
  return {
636
682
  value,
637
683
  original,
@@ -649,6 +695,9 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
649
695
  errors,
650
696
  validating,
651
697
  valid,
698
+ transforming,
699
+ busy: transforming || validating,
700
+ transformError,
652
701
  path: segments,
653
702
  ...computeFieldIdentity(formInstanceId, state.formKey, key),
654
703
  key: state.arrayElementKey(segments),
@@ -662,12 +711,14 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
662
711
  function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
663
712
  const base = buildLeafFieldStateBase(state, segments, key, formInstanceId);
664
713
  const validatingSince = state.fieldValidatingSince.get(key) ?? null;
714
+ const transformingSince = state.fieldTransformingSince.get(key) ?? null;
665
715
  return decorateWithDerivedProps(
666
716
  base,
667
717
  state,
668
718
  getFormMetaBase,
669
719
  key,
670
720
  validatingSince,
721
+ transformingSince,
671
722
  false,
672
723
  // revealedDescendantError: leaves have no descendants
673
724
  false,
@@ -690,6 +741,8 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
690
741
  let connected = false;
691
742
  let validating = false;
692
743
  let validatingSince = null;
744
+ let transforming = false;
745
+ let transformingSince = null;
693
746
  let updatedAt = null;
694
747
  let asyncPending = false;
695
748
  const submissionAttempts = state.submissionAttempts.value;
@@ -720,6 +773,13 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
720
773
  if (leafGateOpen && since !== void 0 && (validatingSince === null || since < validatingSince))
721
774
  validatingSince = since;
722
775
  }
776
+ if ((state.fieldTransformCounts.get(leafKey) ?? 0) > 0) {
777
+ transforming = true;
778
+ const leafGateOpen = submissionAttempts > 0 || leafRecord?.blurredAfterInteraction === true;
779
+ const since = state.fieldTransformingSince.get(leafKey);
780
+ if (leafGateOpen && since !== void 0 && (transformingSince === null || since < transformingSince))
781
+ transformingSince = since;
782
+ }
723
783
  if (state.pathHasAsyncValidationByKey(leafKey, entry.segments)) asyncPending = true;
724
784
  const ts = leafRecord?.updatedAt;
725
785
  if (ts !== void 0 && ts !== null) {
@@ -739,11 +799,22 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
739
799
  return blurredLeafSegments.some((s) => paths.isPathPrefix(ePath, s));
740
800
  });
741
801
  if (!asyncPending && state.pathHasAsyncValidation(segments)) asyncPending = true;
802
+ if ((state.fieldValidationCounts.get(key) ?? 0) > 0) {
803
+ validating = true;
804
+ const since = state.fieldValidatingSince.get(key);
805
+ if (since !== void 0 && (validatingSince === null || since < validatingSince))
806
+ validatingSince = since;
807
+ }
808
+ if ((state.fieldTransformCounts.get(key) ?? 0) > 0) {
809
+ transforming = true;
810
+ const since = state.fieldTransformingSince.get(key);
811
+ if (since !== void 0 && (transformingSince === null || since < transformingSince))
812
+ transformingSince = since;
813
+ }
814
+ const ownTransformError = state.transformErrors.get(key) ?? null;
742
815
  const gated = asyncPending && !state.firstValidationDone.value;
743
816
  const valid = !gated && errors.length === 0 && !validating;
744
- const resolved = state.schema.getFieldMetaAtPath ? state.schema.getFieldMetaAtPath(segments) : EMPTY_RESOLVED_FIELD_META;
745
- const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
746
- const label = resolved.label || humanize(lastSegment);
817
+ const { resolved, label } = resolveFieldMetaAndLabel(state, segments);
747
818
  return {
748
819
  base: {
749
820
  value,
@@ -762,6 +833,12 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
762
833
  errors,
763
834
  validating,
764
835
  valid,
836
+ transforming,
837
+ busy: transforming || validating,
838
+ // A container surfaces its OWN transform failure (a transform registered
839
+ // on the container path, e.g. a file normalizer) but never rolls up a
840
+ // descendant leaf's failure — that stays a per-field channel.
841
+ transformError: ownTransformError,
765
842
  path: segments,
766
843
  ...computeFieldIdentity(formInstanceId, state.formKey, key),
767
844
  key: state.arrayElementKey(segments),
@@ -772,28 +849,25 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
772
849
  meta: resolved.meta
773
850
  },
774
851
  validatingSince,
852
+ transformingSince,
775
853
  revealedDescendantError
776
854
  };
777
855
  }
778
856
  function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
779
- const { base, validatingSince, revealedDescendantError } = buildContainerFieldStateBase(
780
- state,
781
- segments,
782
- key,
783
- formInstanceId
784
- );
857
+ const { base, validatingSince, transformingSince, revealedDescendantError } = buildContainerFieldStateBase(state, segments, key, formInstanceId);
785
858
  return decorateWithDerivedProps(
786
859
  base,
787
860
  state,
788
861
  getFormMetaBase,
789
862
  key,
790
863
  validatingSince,
864
+ transformingSince,
791
865
  revealedDescendantError,
792
866
  segments.length === 0,
793
867
  getDisplayState
794
868
  );
795
869
  }
796
- function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, revealedDescendantError, isRoot, getDisplayState) {
870
+ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, transformingSince, revealedDescendantError, isRoot, getDisplayState) {
797
871
  const firstError = base.errors[0];
798
872
  const predicate = getDisplayState ?? state.getDisplayState;
799
873
  const formMeta = getFormMetaBase();
@@ -801,6 +875,7 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
801
875
  field: base,
802
876
  formMeta,
803
877
  validatingSince,
878
+ transformingSince,
804
879
  // The engine's clock. Frozen to 0 under SSR (no clock, nothing in
805
880
  // flight) so the reducer returns the plain verdict and hydration matches.
806
881
  now: state.ssr ? 0 : Date.now()
@@ -867,6 +942,15 @@ function liveKeysAtPath(state, segments) {
867
942
  if (typeof value === "object") return Object.keys(value);
868
943
  return [];
869
944
  }
945
+ function liveContainerHasKey(state, segments, key) {
946
+ const value = getAtPath(state.form.value, segments);
947
+ if (value === null || value === void 0 || typeof value !== "object") return false;
948
+ if (Array.isArray(value)) {
949
+ const index = Number(key);
950
+ return Number.isInteger(index) && index >= 0 && index < value.length && String(index) === key;
951
+ }
952
+ return Object.hasOwn(value, key);
953
+ }
870
954
  function isArrayPath(state, segments) {
871
955
  if (segments.length === 0) return false;
872
956
  return Array.isArray(getAtPath(state.form.value, segments));
@@ -895,6 +979,21 @@ const INTEGER_SEGMENT = /^(?:0|[1-9]\d*)$/;
895
979
  function keyToSegment(key) {
896
980
  return INTEGER_SEGMENT.test(key) ? Number(key) : key;
897
981
  }
982
+ function callableInvokeShim(method, surface, getDescent) {
983
+ const fnMethod = Reflect.get(Function.prototype, method);
984
+ return new Proxy((() => {
985
+ }), {
986
+ apply: (_target, _thisArg, args) => Reflect.apply(fnMethod, surface, args),
987
+ get: (_target, key) => Reflect.get(getDescent(), key),
988
+ has: (_target, key) => Reflect.has(getDescent(), key),
989
+ ownKeys: () => Reflect.ownKeys(getDescent()),
990
+ getOwnPropertyDescriptor: (_target, key) => {
991
+ const descriptor = Reflect.getOwnPropertyDescriptor(getDescent(), key);
992
+ if (descriptor !== void 0) descriptor.configurable = true;
993
+ return descriptor;
994
+ }
995
+ });
996
+ }
898
997
  function buildSurfaceProxy(opts) {
899
998
  const containerCache = /* @__PURE__ */ new Map();
900
999
  const leafViewCache = /* @__PURE__ */ new Map();
@@ -921,6 +1020,8 @@ function buildSurfaceProxy(opts) {
921
1020
  const cacheKey = `${JSON.stringify(segments)}+${isArrayLike ? "A" : "O"}`;
922
1021
  const existing = containerCache.get(cacheKey);
923
1022
  if (existing !== void 0) return existing;
1023
+ const isFixedObject = opts.schema.isFixedObjectAtPath(segments);
1024
+ const containerHasKey = (k) => opts.containerHasOwnKey !== void 0 ? opts.containerHasOwnKey(segments, k) : opts.containerOwnKeys?.(segments).includes(k) === true;
924
1025
  const snapshotContainer = () => opts.materializeContainer === void 0 ? {} : opts.materializeContainer(segments);
925
1026
  const {
926
1027
  toString: containerToString,
@@ -928,8 +1029,9 @@ function buildSurfaceProxy(opts) {
928
1029
  toJSON: containerToJSON,
929
1030
  toPrimitive: containerToPrimitive
930
1031
  } = makeReadonlyCoercion(snapshotContainer);
931
- const target = isArrayLike ? [] : (() => {
932
- });
1032
+ const isRoot = segments.length === 0;
1033
+ const target = isRoot ? (() => {
1034
+ }) : isArrayLike ? [] : {};
933
1035
  const proxy = new Proxy(target, {
934
1036
  apply(_, __, args) {
935
1037
  const arg = args[0];
@@ -960,7 +1062,16 @@ function buildSurfaceProxy(opts) {
960
1062
  return key === "toString" ? containerToString : containerValueOf;
961
1063
  }
962
1064
  }
963
- return descendOrTerminate(childSegs);
1065
+ if (key === "hasOwnProperty" && !schemaHasPath(childSegs)) {
1066
+ return Object.prototype.hasOwnProperty;
1067
+ }
1068
+ if (isRoot && (key === "call" || key === "apply" || key === "bind")) {
1069
+ return callableInvokeShim(key, proxy, () => descendOrTerminate(childSegs));
1070
+ }
1071
+ if (opts.isTerminalAt?.(childSegs) === true || isFixedObject && schemaHasPath(childSegs) || containerHasKey(key)) {
1072
+ return descendOrTerminate(childSegs);
1073
+ }
1074
+ return void 0;
964
1075
  },
965
1076
  has(_, key) {
966
1077
  if (typeof key === "symbol") return Reflect.has(target, key);
@@ -1045,15 +1156,8 @@ function buildSurfaceProxy(opts) {
1045
1156
  toJSON: leafToJSONHandler,
1046
1157
  toPrimitive: leafToPrimitive
1047
1158
  } = makeReadonlyCoercion(snapshotLeaf);
1048
- const target = (() => {
1049
- });
1159
+ const target = {};
1050
1160
  const proxy = new Proxy(target, {
1051
- apply(_, __, args) {
1052
- const arg = args[0];
1053
- if (arg === void 0) return opts.resolveCallTarget(segments);
1054
- const { segments: argSegs } = paths.canonicalizePath(arg);
1055
- return opts.resolveCallTarget(argSegs);
1056
- },
1057
1161
  get(_, key) {
1058
1162
  if (typeof key === "symbol") {
1059
1163
  if (key === Symbol.toPrimitive) return leafToPrimitive;
@@ -1063,6 +1167,9 @@ function buildSurfaceProxy(opts) {
1063
1167
  if (key === "toString") return leafToString;
1064
1168
  if (key === "valueOf") return leafValueOf;
1065
1169
  if (key === "toJSON") return leafToJSONHandler;
1170
+ if (key === "hasOwnProperty" && !schemaHasPath([...segments, keyToSegment(key)])) {
1171
+ return Object.prototype.hasOwnProperty;
1172
+ }
1066
1173
  if (leafKeys.has(key)) {
1067
1174
  const leaf = opts.resolveLeaf(segments);
1068
1175
  return readLeafKey(leaf, key);
@@ -1075,8 +1182,8 @@ function buildSurfaceProxy(opts) {
1075
1182
  return true;
1076
1183
  },
1077
1184
  // Iteration: leaf-views expose the leaf-key set so
1078
- // `JSON.stringify(form.fields.email)` produces a FieldState
1079
- // snapshot rather than the function-target placeholder.
1185
+ // `Object.keys(form.fields.email)` / spread enumerate the
1186
+ // FieldState props. (`JSON.stringify` routes through `toJSON` above.)
1080
1187
  ownKeys: () => Array.from(leafKeys),
1081
1188
  getOwnPropertyDescriptor(_, key) {
1082
1189
  if (typeof key !== "string") return void 0;
@@ -1185,6 +1292,12 @@ function buildErrorsProxy(state) {
1185
1292
  // library-produced verdicts (schema + derived-blank) at unreachable
1186
1293
  // paths stay hidden; user-supplied errors are unconditional.
1187
1294
  containerOwnKeys: (segments) => errorAwareContainerKeys(state, segments),
1295
+ // Fast path: a key the live form data holds short-circuits before the
1296
+ // O(n) error-store scan, so iterating `form.errors.<array>` over live
1297
+ // indices stays linear. The scan still runs for a key with no live
1298
+ // home — a server error at a non-schema key (`form.errors.ghost`) —
1299
+ // so it keeps surfacing while a genuinely-absent key reads undefined.
1300
+ containerHasOwnKey: (segments, key) => liveContainerHasKey(state, segments, key) || errorAwareContainerKeys(state, segments).includes(key),
1188
1301
  isArrayContainer: (segments) => isArrayPath(state, segments)
1189
1302
  });
1190
1303
  }
@@ -1358,6 +1471,9 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
1358
1471
  "errors",
1359
1472
  "validating",
1360
1473
  "valid",
1474
+ "transforming",
1475
+ "busy",
1476
+ "transformError",
1361
1477
  "displayState",
1362
1478
  "showErrors",
1363
1479
  "showPending",
@@ -1460,33 +1576,41 @@ function buildFieldStateProxy(state, formInstanceId, getFormMetaBase, options) {
1460
1576
  terminalCache.set(cacheKey, proxy);
1461
1577
  return proxy;
1462
1578
  }
1579
+ const surfaceSchema = state.schema;
1463
1580
  return buildSurfaceProxy({
1464
- schema: state.schema,
1581
+ schema: surfaceSchema,
1465
1582
  resolveLeaf: (path) => getFieldStateAt(path),
1466
1583
  leafKeys: FIELD_STATE_KEYS,
1467
1584
  readLeafKey: (computed, key) => computed.value[key],
1468
1585
  materializeContainer: (segments) => materializeFields(state, segments, snapshotFieldStateAt),
1469
- resolveCallTarget: (path) => fieldStateTerminalAt(path),
1586
+ // `form.fields(path)` resolves a FieldState for any path the SCHEMA
1587
+ // declares — a leaf, a container, an inactive discriminated-union
1588
+ // variant key, or an out-of-bounds array index (the element schema
1589
+ // admits any index). A path the schema doesn't have is a typo, not a
1590
+ // field, so it reads `undefined` rather than a phantom stub. The
1591
+ // empty path (`form.fields()`) is the root object, always valid.
1592
+ resolveCallTarget: (path) => surfaceSchema.getSlimPrimitiveTypesAtPath(path).size > 0 ? fieldStateTerminalAt(path) : void 0,
1470
1593
  containerOwnKeys: (segments) => liveKeysAtPath(state, segments),
1594
+ containerHasOwnKey: (segments, key) => liveContainerHasKey(state, segments, key),
1471
1595
  isArrayContainer: (segments) => isArrayPath(state, segments)
1472
1596
  });
1473
1597
  }
1474
1598
  function materializeFields(state, containerSegments, snapshotFieldStateAt) {
1475
1599
  const liveValue = getAtPath(state.form.value, containerSegments);
1476
- return walk$2(liveValue, containerSegments, state.schema, snapshotFieldStateAt);
1600
+ return walk$1(liveValue, containerSegments, state.schema, snapshotFieldStateAt);
1477
1601
  }
1478
- function walk$2(value, basePath, schema, snapshotFieldStateAt) {
1602
+ function walk$1(value, basePath, schema, snapshotFieldStateAt) {
1479
1603
  if (schema.isLeafAtPath(basePath)) return snapshotFieldStateAt(basePath);
1480
1604
  if (value === null || value === void 0) return value;
1481
1605
  if (typeof value !== "object") {
1482
1606
  return value;
1483
1607
  }
1484
1608
  if (Array.isArray(value)) {
1485
- return value.map((_, i) => walk$2(value[i], [...basePath, i], schema, snapshotFieldStateAt));
1609
+ return value.map((_, i) => walk$1(value[i], [...basePath, i], schema, snapshotFieldStateAt));
1486
1610
  }
1487
1611
  const result = {};
1488
1612
  for (const key of Object.keys(value)) {
1489
- result[key] = walk$2(
1613
+ result[key] = walk$1(
1490
1614
  value[key],
1491
1615
  [...basePath, key],
1492
1616
  schema,
@@ -1570,47 +1694,42 @@ function mergeDeep(target, source, path, schema) {
1570
1694
  if (!isPlainRecord(source)) return source;
1571
1695
  if (schema !== void 0) {
1572
1696
  const du = schema.getUnionDiscriminatorAtPath(path);
1573
- if (du !== void 0) {
1574
- const sourceRecord = source;
1575
- const sourceDisc = sourceRecord[du.discriminatorKey];
1576
- if (sourceDisc !== void 0 && !du.isVariantSelected(sourceDisc)) {
1577
- return { [du.discriminatorKey]: sourceDisc };
1578
- }
1579
- if (sourceDisc !== void 0) {
1580
- const variantDefault = du.getVariantDefault(sourceDisc);
1581
- if (isPlainRecord(variantDefault)) {
1582
- const out2 = { ...variantDefault };
1583
- for (const key of Object.keys(sourceRecord)) {
1584
- if (!safeOwnHas(variantDefault, key) && key !== du.discriminatorKey) continue;
1585
- safeAssign(
1586
- out2,
1587
- key,
1588
- mergeDeep(
1589
- safeOwnRead(out2, key),
1590
- safeOwnRead(sourceRecord, key),
1591
- [...path, key],
1592
- schema
1593
- )
1594
- );
1595
- }
1596
- return out2;
1597
- }
1598
- }
1599
- return {};
1697
+ if (du !== void 0) return mergeDuAwareKeys(source, path, schema, du);
1698
+ }
1699
+ return mergeObjectKeys(target, source, path, schema);
1700
+ }
1701
+ function mergeDuAwareKeys(source, path, schema, du) {
1702
+ const sourceDisc = source[du.discriminatorKey];
1703
+ if (sourceDisc !== void 0 && !du.isVariantSelected(sourceDisc)) {
1704
+ return { [du.discriminatorKey]: sourceDisc };
1705
+ }
1706
+ if (sourceDisc !== void 0) {
1707
+ const variantDefault = du.getVariantDefault(sourceDisc);
1708
+ if (isPlainRecord(variantDefault)) {
1709
+ return mergeVariantKeys(source, variantDefault, path, schema, du);
1600
1710
  }
1601
1711
  }
1602
- const mergeTarget = target;
1603
- const out = isPlainRecord(mergeTarget) ? { ...mergeTarget } : {};
1712
+ return {};
1713
+ }
1714
+ function mergeVariantKeys(source, variantDefault, path, schema, du) {
1715
+ const out = { ...variantDefault };
1604
1716
  for (const key of Object.keys(source)) {
1717
+ if (!safeOwnHas(variantDefault, key) && key !== du.discriminatorKey) continue;
1605
1718
  safeAssign(
1606
1719
  out,
1607
1720
  key,
1608
- mergeDeep(
1609
- safeOwnRead(out, key),
1610
- safeOwnRead(source, key),
1611
- [...path, key],
1612
- schema
1613
- )
1721
+ mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
1722
+ );
1723
+ }
1724
+ return out;
1725
+ }
1726
+ function mergeObjectKeys(target, source, path, schema) {
1727
+ const out = isPlainRecord(target) ? { ...target } : {};
1728
+ for (const key of Object.keys(source)) {
1729
+ safeAssign(
1730
+ out,
1731
+ key,
1732
+ mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
1614
1733
  );
1615
1734
  }
1616
1735
  return out;
@@ -1743,7 +1862,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1743
1862
  if (!result.ok) return result.error;
1744
1863
  return stripData(composeWithDerivedBlank(result.refinement, result.segments));
1745
1864
  }
1746
- async function process(pathInput) {
1865
+ async function parse(pathInput) {
1747
1866
  const result = await runImperativeValidation(pathInput, {
1748
1867
  cancelInFlight: false,
1749
1868
  commitToSchemaErrors: false
@@ -1796,6 +1915,8 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1796
1915
  state.activeSubmissions.value += 1;
1797
1916
  state.submitting.value = true;
1798
1917
  state.submitError.value = null;
1918
+ state.clearUserErrors();
1919
+ while (state.activeTransforms.value > 0) await state.settleTransforms();
1799
1920
  state.cancelFieldValidation();
1800
1921
  state.displayEngine.clear();
1801
1922
  state.activeValidations.value += 1;
@@ -1834,9 +1955,8 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1834
1955
  state.emitSubmitSuccess();
1835
1956
  } catch (err) {
1836
1957
  if (state.submissionGeneration.value === genAtEntry) {
1837
- state.submitError.value = err;
1958
+ state.submitError.value = paths.toError(err);
1838
1959
  }
1839
- throw err;
1840
1960
  } finally {
1841
1961
  if (!validationSettled) {
1842
1962
  state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
@@ -1850,7 +1970,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1850
1970
  };
1851
1971
  return submitHandler;
1852
1972
  };
1853
- return { validate, validateAsync, process, handleSubmit };
1973
+ return { validate, validateAsync, parse, handleSubmit };
1854
1974
  }
1855
1975
  function toSegments(pathInput) {
1856
1976
  return paths.canonicalizePath(pathInput).segments;
@@ -1908,6 +2028,33 @@ function applyInvalidSubmitPolicy(state, formInstanceId, policy) {
1908
2028
  target.element.focus({ preventScroll: true });
1909
2029
  }
1910
2030
 
2031
+ function captureUserCallSite() {
2032
+ const raw = new Error().stack;
2033
+ if (typeof raw !== "string") return void 0;
2034
+ const lines = raw.split("\n");
2035
+ for (let i = 1; i < lines.length; i++) {
2036
+ const frame = lines[i];
2037
+ if (frame === void 0) continue;
2038
+ if (/attaform[/-]forms?/i.test(frame)) continue;
2039
+ if (/\bforms\.[A-Za-z0-9_-]+\.m?js\b/.test(frame)) continue;
2040
+ const trimmed = frame.trim();
2041
+ if (trimmed.length === 0) continue;
2042
+ return shortenSourceFrame(trimmed);
2043
+ }
2044
+ return void 0;
2045
+ }
2046
+ function shortenSourceFrame(frame) {
2047
+ const match = /(?:^|\s|\()([^\s()]+):(\d+):\d+\)?$/.exec(frame);
2048
+ if (match === null) return frame;
2049
+ const [, urlOrPath, line] = match;
2050
+ if (urlOrPath === void 0 || line === void 0) return frame;
2051
+ let path = urlOrPath;
2052
+ path = path.replace(/^[a-z]+:\/\/[^/]+\//i, "");
2053
+ path = path.replace(/^_nuxt\//, "");
2054
+ path = path.replace(/^\//, "");
2055
+ return `(${path}:${line})`;
2056
+ }
2057
+
1911
2058
  function extractSchemaFields(schema) {
1912
2059
  try {
1913
2060
  const root = schema.getDefaultAtPath([]);
@@ -1966,9 +2113,9 @@ function isLeafValue(value) {
1966
2113
  return true;
1967
2114
  }
1968
2115
  function isSlimPrimitiveValid(schema, store, path, value) {
1969
- return walk$1(schema, store, path, value);
2116
+ return walk(schema, store, path, value);
1970
2117
  }
1971
- function walk$1(schema, store, path, value) {
2118
+ function walk(schema, store, path, value) {
1972
2119
  if (schema.isPreprocessOrCoerceLeaf(path)) return true;
1973
2120
  const accepted = schema.getSlimPrimitiveTypesAtPath(path);
1974
2121
  const kind = isLeafValue(value) ? slimKindOf(value) : Array.isArray(value) ? "array" : "object";
@@ -1978,13 +2125,13 @@ function walk$1(schema, store, path, value) {
1978
2125
  }
1979
2126
  if (Array.isArray(value)) {
1980
2127
  for (let i = 0; i < value.length; i++) {
1981
- if (!walk$1(schema, store, [...path, i], value[i])) return false;
2128
+ if (!walk(schema, store, [...path, i], value[i])) return false;
1982
2129
  }
1983
2130
  return true;
1984
2131
  }
1985
2132
  if (isPlainRecord(value)) {
1986
2133
  for (const key of Object.keys(value)) {
1987
- if (!walk$1(schema, store, [...path, key], value[key])) {
2134
+ if (!walk(schema, store, [...path, key], value[key])) {
1988
2135
  return false;
1989
2136
  }
1990
2137
  }
@@ -2272,7 +2419,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2272
2419
  throw new paths.AnonPersistError({
2273
2420
  cause: "register-without-config",
2274
2421
  schemaFields: extractSchemaFields(state.schema),
2275
- callSite: paths.captureUserCallSite()
2422
+ callSite: captureUserCallSite()
2276
2423
  });
2277
2424
  }
2278
2425
  const { aria } = computeFieldIdentity(formInstanceId, state.formKey, pathKey);
@@ -2323,6 +2470,23 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2323
2470
  markConnectedOptimistically: () => {
2324
2471
  state.markConnectedOptimistically(segments);
2325
2472
  },
2473
+ // --- Async transform lifecycle (internal; the directive's
2474
+ // deferred orchestrator is the only legitimate consumer). Thin
2475
+ // path-bound delegates to the store's per-path token / counter
2476
+ // machinery — same pattern as `markBlank` / `setValueWithInternalPath`,
2477
+ // so the directive (which holds only this RegisterValue, never the
2478
+ // store) can drive the busy/discard/error bookkeeping. ---
2479
+ beginTransform: (holder) => state.beginTransform(pathKey, holder),
2480
+ isCurrentTransform: (token) => state.isCurrentTransform(pathKey, token),
2481
+ endTransform: (token) => state.endTransform(pathKey, token),
2482
+ setTransformError: (err) => state.setTransformError(pathKey, err),
2483
+ // Synchronous read of "is a transform in flight at this path". The
2484
+ // orchestrator's `beginTransform` bumps the count before the
2485
+ // listener's force-sync block runs, so the directive reads this to
2486
+ // skip reverting the DOM to stale storage mid-flight.
2487
+ get transforming() {
2488
+ return (state.fieldTransformCounts.get(pathKey) ?? 0) > 0;
2489
+ },
2326
2490
  path: pathKey,
2327
2491
  // Frozen so a wrapper component can pass `rv.segments` directly
2328
2492
  // to `form.fields(...)` without defensive copying — and so test
@@ -2367,49 +2531,60 @@ function walkUnsetSentinels(values, schema) {
2367
2531
  walkUnspecified(rootSlim, [], paths);
2368
2532
  return { cleanedValues: void 0, paths };
2369
2533
  }
2370
- const cleaned = walk(values, [], schema, paths);
2534
+ const cleaned = walkCore(values, [], schema, paths, true);
2371
2535
  return { cleanedValues: cleaned, paths };
2372
2536
  }
2373
- function walk(input, segments, schema, paths) {
2537
+ function isOpaqueLeaf(value) {
2538
+ return value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || typeof value === "function";
2539
+ }
2540
+ function walkCore(input, segments, schema, paths, synthesizeSchemaKeys) {
2374
2541
  if (isUnset(input)) {
2375
2542
  return expandUnsetAt(segments, schema, paths);
2376
2543
  }
2377
2544
  if (input === void 0) {
2378
- const slim = schema.getDefaultAtPath(segments);
2379
- return walkUnspecified(slim, segments, paths);
2380
- }
2381
- if (input === null) return null;
2382
- if (input instanceof Date || input instanceof RegExp || input instanceof Map || input instanceof Set || typeof input === "function") {
2545
+ if (synthesizeSchemaKeys) {
2546
+ const slim = schema.getDefaultAtPath(segments);
2547
+ return walkUnspecified(slim, segments, paths);
2548
+ }
2383
2549
  return input;
2384
2550
  }
2551
+ if (input === null) return null;
2552
+ if (isOpaqueLeaf(input)) return input;
2385
2553
  if (Array.isArray(input)) {
2386
2554
  const out = new Array(input.length);
2387
2555
  let mutated = false;
2388
2556
  for (let i = 0; i < input.length; i++) {
2389
- const walked = walk(input[i], [...segments, i], schema, paths);
2557
+ const walked = walkCore(input[i], [...segments, i], schema, paths, synthesizeSchemaKeys);
2390
2558
  out[i] = walked;
2391
2559
  if (walked !== input[i]) mutated = true;
2392
2560
  }
2393
2561
  return mutated ? out : input;
2394
2562
  }
2395
2563
  if (typeof input === "object") {
2396
- const slim = schema.getDefaultAtPath(segments);
2397
- const inputKeys = Object.keys(input);
2398
- const inputKeysSet = new Set(inputKeys);
2399
- const allKeys = new Set(inputKeys);
2400
- if (slim !== null && slim !== void 0 && typeof slim === "object" && !Array.isArray(slim) && !(slim instanceof Date) && !(slim instanceof RegExp) && !(slim instanceof Map) && !(slim instanceof Set)) {
2401
- for (const k of Object.keys(slim)) allKeys.add(k);
2564
+ const obj = input;
2565
+ const inputKeys = Object.keys(obj);
2566
+ let keys = inputKeys;
2567
+ let mutated = false;
2568
+ let inputKeysSet = null;
2569
+ if (synthesizeSchemaKeys) {
2570
+ inputKeysSet = new Set(inputKeys);
2571
+ const allKeys = new Set(inputKeys);
2572
+ const slim = schema.getDefaultAtPath(segments);
2573
+ if (slim !== null && slim !== void 0 && typeof slim === "object" && !Array.isArray(slim) && !isOpaqueLeaf(slim)) {
2574
+ for (const k of Object.keys(slim)) allKeys.add(k);
2575
+ }
2576
+ keys = allKeys;
2577
+ mutated = allKeys.size !== inputKeys.length;
2402
2578
  }
2403
2579
  const out = {};
2404
- let mutated = allKeys.size !== inputKeys.length;
2405
- for (const key of allKeys) {
2406
- const orig = input[key];
2407
- if (orig === void 0 && inputKeysSet.has(key)) {
2580
+ for (const key of keys) {
2581
+ const orig = obj[key];
2582
+ if (synthesizeSchemaKeys && orig === void 0 && inputKeysSet?.has(key) === true) {
2408
2583
  safeAssign(out, key, void 0);
2409
2584
  mutated = true;
2410
2585
  continue;
2411
2586
  }
2412
- const walked = walk(orig, [...segments, key], schema, paths);
2587
+ const walked = walkCore(orig, [...segments, key], schema, paths, synthesizeSchemaKeys);
2413
2588
  safeAssign(out, key, walked);
2414
2589
  if (walked !== orig) mutated = true;
2415
2590
  }
@@ -2424,7 +2599,7 @@ function walkUnspecified(slim, segments, paths$1) {
2424
2599
  }
2425
2600
  return slim;
2426
2601
  }
2427
- if (slim instanceof Date || slim instanceof RegExp || slim instanceof Map || slim instanceof Set || typeof slim === "function") {
2602
+ if (isOpaqueLeaf(slim)) {
2428
2603
  return slim;
2429
2604
  }
2430
2605
  if (Array.isArray(slim)) return slim;
@@ -2443,40 +2618,9 @@ function walkUnspecified(slim, segments, paths$1) {
2443
2618
  }
2444
2619
  function substituteUnsetSentinels(value, prefix, schema) {
2445
2620
  const paths = [];
2446
- const cleaned = substitute(value, [...prefix], schema, paths);
2621
+ const cleaned = walkCore(value, [...prefix], schema, paths, false);
2447
2622
  return { cleanedValues: cleaned, paths };
2448
2623
  }
2449
- function substitute(input, segments, schema, paths) {
2450
- if (isUnset(input)) {
2451
- return expandUnsetAt(segments, schema, paths);
2452
- }
2453
- if (input === void 0 || input === null) return input;
2454
- if (input instanceof Date || input instanceof RegExp || input instanceof Map || input instanceof Set || typeof input === "function") {
2455
- return input;
2456
- }
2457
- if (Array.isArray(input)) {
2458
- let mutated = false;
2459
- const out = new Array(input.length);
2460
- for (let i = 0; i < input.length; i++) {
2461
- const walked = substitute(input[i], [...segments, i], schema, paths);
2462
- out[i] = walked;
2463
- if (walked !== input[i]) mutated = true;
2464
- }
2465
- return mutated ? out : input;
2466
- }
2467
- if (typeof input === "object") {
2468
- let mutated = false;
2469
- const out = {};
2470
- for (const key of Object.keys(input)) {
2471
- const orig = input[key];
2472
- const walked = substitute(orig, [...segments, key], schema, paths);
2473
- safeAssign(out, key, walked);
2474
- if (walked !== orig) mutated = true;
2475
- }
2476
- return mutated ? out : input;
2477
- }
2478
- return input;
2479
- }
2480
2624
  function isPrimitiveOrEmpty(value) {
2481
2625
  if (value === null || value === void 0) return true;
2482
2626
  const t = typeof value;
@@ -2506,7 +2650,7 @@ function expandUnsetAt(segments, schema, paths$1) {
2506
2650
  paths$1.push(paths.canonicalizePath(segments).key);
2507
2651
  return slim;
2508
2652
  }
2509
- if (slim instanceof Date || slim instanceof RegExp || slim instanceof Map || slim instanceof Set || typeof slim === "function") {
2653
+ if (isOpaqueLeaf(slim)) {
2510
2654
  paths$1.push(paths.canonicalizePath(segments).key);
2511
2655
  return slim;
2512
2656
  }
@@ -2521,7 +2665,9 @@ function expandUnsetAt(segments, schema, paths$1) {
2521
2665
  function buildCallableReadonlySnapshotProxy(opts) {
2522
2666
  const target = (() => {
2523
2667
  });
2524
- const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(opts.snapshot);
2668
+ const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(
2669
+ opts.coercionSnapshot ?? opts.snapshot
2670
+ );
2525
2671
  const callResolve = opts.resolveCall ?? ((arg) => opts.resolveKey(String(arg)));
2526
2672
  return new Proxy(target, {
2527
2673
  apply(_, __, args) {
@@ -2570,27 +2716,52 @@ function buildCallableReadonlySnapshotProxy(opts) {
2570
2716
  });
2571
2717
  }
2572
2718
 
2719
+ function materializeFormValue(node) {
2720
+ if (node === null || typeof node !== "object") return node;
2721
+ if (Array.isArray(node)) {
2722
+ const out2 = new Array(node.length);
2723
+ for (let i = 0; i < node.length; i++) out2[i] = materializeFormValue(node[i]);
2724
+ return out2;
2725
+ }
2726
+ if (!isPlainRecord(node)) return vue.toRaw(node);
2727
+ const rec = node;
2728
+ const out = {};
2729
+ for (const key of Object.keys(rec)) {
2730
+ safeAssign(out, key, materializeFormValue(safeOwnRead(rec, key)));
2731
+ }
2732
+ return out;
2733
+ }
2573
2734
  function buildValuesProxy(form) {
2574
2735
  const inner = vue.computed(() => vue.readonly(form.value));
2575
2736
  return buildCallableReadonlySnapshotProxy({
2576
2737
  surface: "form.values",
2577
2738
  snapshot: () => inner.value,
2739
+ // Faithful, reactivity-preserving serialisation: walk the reactive
2740
+ // proxy with own-safe reads so `JSON.stringify(form.values)` /
2741
+ // `String(form.values)` reflect the stored data — including a field
2742
+ // literally named `hasOwnProperty` that Vue would otherwise shim —
2743
+ // while still tracking the per-key reads that drive re-render.
2744
+ coercionSnapshot: () => materializeFormValue(inner.value),
2578
2745
  // Read through the readonly proxy at access time so Vue's
2579
2746
  // dependency tracking lands inside the consumer's active effect
2580
2747
  // — `inner.value[key]` is what triggers per-key tracking.
2581
- resolveKey: (key) => inner.value[key],
2582
- // Dynamic path: walk segments through the readonly proxy. Each
2583
- // step reads through the proxy's own get traps so dependency
2584
- // tracking propagates at every level.
2585
- resolveCall: (arg) => {
2586
- const { segments } = paths.canonicalizePath(arg);
2587
- let cursor = inner.value;
2588
- for (const seg of segments) {
2589
- if (cursor === null || cursor === void 0) return void 0;
2590
- cursor = cursor[seg];
2591
- }
2592
- return cursor;
2593
- },
2748
+ //
2749
+ // Prototype-shadowed names (`hasOwnProperty`, `constructor`, …) read
2750
+ // off the RAW target instead: own-shadows-inherited semantics still
2751
+ // hold (a data field by that name returns its stored value), but
2752
+ // when there's no such field the inherited member resolves — so
2753
+ // `form.values.hasOwnProperty('x')` keeps working as the real
2754
+ // method. The raw read dodges Vue's `hasOwnProperty` proxy shim,
2755
+ // which would otherwise mask a data field of that name. (`toString`
2756
+ // / `valueOf` / `toJSON` never reach here the base get trap
2757
+ // intercepts them as coercion handlers first.)
2758
+ resolveKey: (key) => isShadowedKey(key) ? vue.toRaw(inner.value)[key] : inner.value[key],
2759
+ // Dynamic path: walk segments through the readonly proxy with the
2760
+ // same own-property-safe descent the rest of the runtime uses
2761
+ // (`getAtPath`), so `form.values('a.hasOwnProperty')` resolves the
2762
+ // stored value. Per-level reads still propagate Vue's tracking for
2763
+ // ordinary keys.
2764
+ resolveCall: (arg) => getAtPath(inner.value, paths.canonicalizePath(arg).segments),
2594
2765
  ownKeys: () => Reflect.ownKeys(inner.value),
2595
2766
  hasKey: (key) => Reflect.has(inner.value, key),
2596
2767
  describeKey: (key) => {
@@ -2613,6 +2784,17 @@ function buildFormApi(state, formInstanceId, options = {}) {
2613
2784
  if (instanceMeta === void 0) return meta;
2614
2785
  return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
2615
2786
  };
2787
+ const reMarkBlanksAfterSubstitution = (paths$1) => {
2788
+ for (const pathKey of paths$1) {
2789
+ const blankSegments = paths.segmentsForPathKey(pathKey);
2790
+ if (blankSegments === null) continue;
2791
+ state.setValueAtPath(
2792
+ blankSegments,
2793
+ state.getValueAtPath(blankSegments),
2794
+ withInstanceMeta({ blank: true })
2795
+ );
2796
+ }
2797
+ };
2616
2798
  const getFormMetaBase = () => {
2617
2799
  const { base: rootBase } = buildContainerFieldStateBase(
2618
2800
  state,
@@ -2651,12 +2833,12 @@ function buildFormApi(state, formInstanceId, options = {}) {
2651
2833
  const {
2652
2834
  validate: validateBuilt,
2653
2835
  validateAsync: validateAsyncBuilt,
2654
- process: processBuilt,
2836
+ parse: parseBuilt,
2655
2837
  handleSubmit
2656
2838
  } = buildProcessForm(state, formInstanceId, processOptions);
2657
2839
  const validate = (pathInput) => validateBuilt(pathInput);
2658
2840
  const validateAsync = (pathInput) => validateAsyncBuilt(pathInput);
2659
- const process = (pathInput) => processBuilt(pathInput);
2841
+ const parse = (pathInput) => parseBuilt(pathInput);
2660
2842
  function pathToRef(pathInput) {
2661
2843
  const segments = paths.canonicalizePath(pathInput).segments;
2662
2844
  return vue.computed(() => getAtPath(state.form.value, segments));
@@ -2670,15 +2852,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2670
2852
  );
2671
2853
  const ok2 = state.setValueAtPath([], walked2.cleanedValues, withInstanceMeta());
2672
2854
  if (!ok2) return false;
2673
- for (const pathKey of walked2.paths) {
2674
- const blankSegments = paths.segmentsForPathKey(pathKey);
2675
- if (blankSegments === null) continue;
2676
- state.setValueAtPath(
2677
- blankSegments,
2678
- state.getValueAtPath(blankSegments),
2679
- withInstanceMeta({ blank: true })
2680
- );
2681
- }
2855
+ reMarkBlanksAfterSubstitution(walked2.paths);
2682
2856
  return true;
2683
2857
  }
2684
2858
  const segments = paths.canonicalizePath(pathOrValue).segments;
@@ -2733,15 +2907,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2733
2907
  );
2734
2908
  const ok = state.setValueAtPath(segments, walked.cleanedValues, withInstanceMeta());
2735
2909
  if (!ok) return false;
2736
- for (const pathKey of walked.paths) {
2737
- const blankSegments = paths.segmentsForPathKey(pathKey);
2738
- if (blankSegments === null) continue;
2739
- state.setValueAtPath(
2740
- blankSegments,
2741
- state.getValueAtPath(blankSegments),
2742
- withInstanceMeta({ blank: true })
2743
- );
2744
- }
2910
+ reMarkBlanksAfterSubstitution(walked.paths);
2745
2911
  return true;
2746
2912
  }
2747
2913
  const errorsProxy = buildErrorsProxy(state);
@@ -2884,6 +3050,21 @@ function buildFormApi(state, formInstanceId, options = {}) {
2884
3050
  // keep the explicit form-level computation for the gate.
2885
3051
  valid,
2886
3052
  errors: metaErrors,
3053
+ // Whole-form transforming mirrors the global `activeTransforms`
3054
+ // counter ORed with any per-leaf transform in flight (the root
3055
+ // rollup), exactly as `validating` composes its lifecycle and
3056
+ // per-field sources. `busy` is the union of both work signals at
3057
+ // the form level. `transformError` is leaf-only, so the root
3058
+ // rollup reads it as `null` (kept for FieldState-shape parity).
3059
+ transforming: vue.computed(
3060
+ () => state.activeTransforms.value > 0 || rootFieldState.value.transforming
3061
+ ),
3062
+ busy: vue.computed(
3063
+ () => state.activeValidations.value > 0 || state.activeTransforms.value > 0 || rootFieldState.value.validating || rootFieldState.value.transforming
3064
+ ),
3065
+ get transformError() {
3066
+ return rootFieldState.value.transformError;
3067
+ },
2887
3068
  // `displayState` / the `show*` booleans / `firstError` flow
2888
3069
  // through the same root field-state computed as the rest of the
2889
3070
  // FieldState surface, so `form.meta.displayState` matches
@@ -3112,7 +3293,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
3112
3293
  setValue: gated(setValueImpl),
3113
3294
  validate: gated(validate),
3114
3295
  validateAsync: gated(validateAsync),
3115
- process: gated(process),
3296
+ parse: gated(parse),
3297
+ settleTransforms: gated(state.settleTransforms),
3116
3298
  register: gated(register),
3117
3299
  key: state.formKey,
3118
3300
  // Auto-unwrapping views over the per-store async-defaults lifecycle
@@ -3979,6 +4161,106 @@ function createFormStore(options) {
3979
4161
  fieldValidationCounts.set(key, next);
3980
4162
  }
3981
4163
  }
4164
+ const fieldTransformCounts = vue.reactive(/* @__PURE__ */ new Map());
4165
+ const fieldTransformingSince = vue.reactive(/* @__PURE__ */ new Map());
4166
+ const transformErrors = vue.reactive(/* @__PURE__ */ new Map());
4167
+ const activeTransforms = vue.ref(0);
4168
+ const transformRuns = /* @__PURE__ */ new Map();
4169
+ let transformTokenSeq = 0;
4170
+ const transformWaiters = [];
4171
+ function incFieldTransform(key) {
4172
+ fieldTransformingSince.set(key, ssr ? 0 : Date.now());
4173
+ fieldTransformCounts.set(key, (fieldTransformCounts.get(key) ?? 0) + 1);
4174
+ }
4175
+ function decFieldTransform(key) {
4176
+ const next = (fieldTransformCounts.get(key) ?? 0) - 1;
4177
+ if (next <= 0) {
4178
+ fieldTransformCounts.delete(key);
4179
+ fieldTransformingSince.delete(key);
4180
+ } else {
4181
+ fieldTransformCounts.set(key, next);
4182
+ }
4183
+ }
4184
+ function flushSettledTransformWaiters() {
4185
+ if (transformWaiters.length === 0) return;
4186
+ const globalIdle = activeTransforms.value === 0;
4187
+ for (let i = transformWaiters.length - 1; i >= 0; i--) {
4188
+ const w = transformWaiters[i];
4189
+ if (w === void 0) continue;
4190
+ const idle = w.key === null ? globalIdle : (fieldTransformCounts.get(w.key) ?? 0) === 0;
4191
+ if (idle) {
4192
+ transformWaiters.splice(i, 1);
4193
+ w.resolve();
4194
+ }
4195
+ }
4196
+ }
4197
+ function releaseTransformRun(key, run) {
4198
+ if (run.released) return;
4199
+ run.released = true;
4200
+ run.holder.aborted = true;
4201
+ run.holder.controller?.abort();
4202
+ activeTransforms.value = Math.max(0, activeTransforms.value - 1);
4203
+ decFieldTransform(key);
4204
+ }
4205
+ function beginTransform(key, holder) {
4206
+ const prior = transformRuns.get(key);
4207
+ if (prior !== void 0) releaseTransformRun(key, prior);
4208
+ const token = ++transformTokenSeq;
4209
+ transformRuns.set(key, { token, holder, released: false });
4210
+ incFieldTransform(key);
4211
+ activeTransforms.value += 1;
4212
+ if (transformErrors.has(key)) transformErrors.delete(key);
4213
+ return token;
4214
+ }
4215
+ function isCurrentTransform(key, token) {
4216
+ return transformRuns.get(key)?.token === token;
4217
+ }
4218
+ function endTransform(key, token) {
4219
+ const run = transformRuns.get(key);
4220
+ if (run?.token === token) {
4221
+ if (!run.released) {
4222
+ activeTransforms.value = Math.max(0, activeTransforms.value - 1);
4223
+ decFieldTransform(key);
4224
+ }
4225
+ transformRuns.delete(key);
4226
+ }
4227
+ flushSettledTransformWaiters();
4228
+ }
4229
+ function setTransformError(key, err) {
4230
+ transformErrors.set(key, err);
4231
+ }
4232
+ function cancelTransforms() {
4233
+ for (const [key, run] of [...transformRuns]) {
4234
+ releaseTransformRun(key, run);
4235
+ transformRuns.delete(key);
4236
+ }
4237
+ if (transformErrors.size > 0) transformErrors.clear();
4238
+ flushSettledTransformWaiters();
4239
+ }
4240
+ function cancelTransformsUnder(prefix) {
4241
+ for (const [key, run] of [...transformRuns]) {
4242
+ const segs = paths.segmentsForPathKey(key);
4243
+ if (segs === null) continue;
4244
+ if (!paths.isPathPrefix(prefix, segs)) continue;
4245
+ releaseTransformRun(key, run);
4246
+ transformRuns.delete(key);
4247
+ transformErrors.delete(key);
4248
+ }
4249
+ flushSettledTransformWaiters();
4250
+ }
4251
+ function settleTransforms(path) {
4252
+ if (path === void 0) {
4253
+ if (activeTransforms.value === 0) return Promise.resolve();
4254
+ return new Promise((resolve) => {
4255
+ transformWaiters.push({ key: null, resolve });
4256
+ });
4257
+ }
4258
+ const { key } = paths.canonicalizePath(path);
4259
+ if ((fieldTransformCounts.get(key) ?? 0) === 0) return Promise.resolve();
4260
+ return new Promise((resolve) => {
4261
+ transformWaiters.push({ key, resolve });
4262
+ });
4263
+ }
3982
4264
  const initStamp = (/* @__PURE__ */ new Date()).toISOString();
3983
4265
  diffAndApply({}, schemaInitialData, [], (patch) => {
3984
4266
  if (patch.kind !== "added") return;
@@ -4128,6 +4410,7 @@ function createFormStore(options) {
4128
4410
  }
4129
4411
  }
4130
4412
  }
4413
+ if (transformRuns.size !== 0) cancelTransformsUnder(path);
4131
4414
  if (meta?.skipDiscriminatorReshape !== true) {
4132
4415
  if (path.length > 0) {
4133
4416
  const last = path[path.length - 1];
@@ -4462,6 +4745,7 @@ function createFormStore(options) {
4462
4745
  drainHooks.length = 0;
4463
4746
  modules.clear();
4464
4747
  cancelFieldValidation();
4748
+ cancelTransforms();
4465
4749
  fieldValidatingSince.clear();
4466
4750
  formChangeListeners.clear();
4467
4751
  submitSuccessListeners.clear();
@@ -4598,6 +4882,7 @@ function createFormStore(options) {
4598
4882
  if (remaining === 0) {
4599
4883
  elements.delete(key);
4600
4884
  touchFieldRecord(key, path, { connected: false, focused: null, blurred: null });
4885
+ if (transformRuns.size !== 0) cancelTransformsUnder(path);
4601
4886
  }
4602
4887
  return remaining;
4603
4888
  }
@@ -4819,6 +5104,7 @@ function createFormStore(options) {
4819
5104
  submitError.value = null;
4820
5105
  departAttempts.value = 0;
4821
5106
  cancelFieldValidation();
5107
+ cancelTransforms();
4822
5108
  displayEngine.clear();
4823
5109
  fieldValidatingSince.clear();
4824
5110
  pathSnapshots.clear();
@@ -4837,6 +5123,7 @@ function createFormStore(options) {
4837
5123
  const { key: targetKey, segments: targetSegments } = paths.canonicalizePath(path);
4838
5124
  variantMemory.clearUnderPath(targetSegments);
4839
5125
  cancelFieldValidationUnder(targetSegments);
5126
+ cancelTransformsUnder(targetSegments);
4840
5127
  for (const [snapKey] of [...pathSnapshots]) {
4841
5128
  const segs = paths.segmentsForPathKey(snapKey);
4842
5129
  if (segs === null) continue;
@@ -4972,6 +5259,10 @@ function createFormStore(options) {
4972
5259
  pathHasAsyncValidationByKey,
4973
5260
  fieldValidationCounts,
4974
5261
  fieldValidatingSince,
5262
+ fieldTransformCounts,
5263
+ fieldTransformingSince,
5264
+ transformErrors,
5265
+ activeTransforms,
4975
5266
  displayEngine,
4976
5267
  applyFormReplacement,
4977
5268
  setValueAtPath,
@@ -5001,6 +5292,13 @@ function createFormStore(options) {
5001
5292
  getOriginalAtPath,
5002
5293
  getFirstErrorElement,
5003
5294
  cancelFieldValidation,
5295
+ beginTransform,
5296
+ isCurrentTransform,
5297
+ endTransform,
5298
+ setTransformError,
5299
+ cancelTransforms,
5300
+ cancelTransformsUnder,
5301
+ settleTransforms,
5004
5302
  scheduleFieldValidation,
5005
5303
  onFormChange,
5006
5304
  onSubmitSuccess,
@@ -5032,6 +5330,20 @@ function captureErrorEntries(map) {
5032
5330
  for (const [k, v] of map) out.push([k, [...v]]);
5033
5331
  return out;
5034
5332
  }
5333
+ function pathsEqual(a, b) {
5334
+ if (a.length !== b.length) return false;
5335
+ for (let j = 0; j < a.length; j++) {
5336
+ if (a[j] !== b[j]) return false;
5337
+ }
5338
+ return true;
5339
+ }
5340
+ function errorFieldsEqual(av, bvi) {
5341
+ if (av === bvi) return true;
5342
+ if (av.message !== bvi.message) return false;
5343
+ if (av.code !== bvi.code) return false;
5344
+ if (av.formKey !== bvi.formKey) return false;
5345
+ return av.path === bvi.path || pathsEqual(av.path, bvi.path);
5346
+ }
5035
5347
  function errorsEqual(a, b) {
5036
5348
  if (a.length !== b.length) return false;
5037
5349
  const bMap = /* @__PURE__ */ new Map();
@@ -5041,18 +5353,7 @@ function errorsEqual(a, b) {
5041
5353
  if (bv === void 0) return false;
5042
5354
  if (v.length !== bv.length) return false;
5043
5355
  for (let i = 0; i < v.length; i++) {
5044
- const av = v[i];
5045
- const bvi = bv[i];
5046
- if (av === bvi) continue;
5047
- if (av.message !== bvi.message) return false;
5048
- if (av.code !== bvi.code) return false;
5049
- if (av.formKey !== bvi.formKey) return false;
5050
- if (av.path !== bvi.path) {
5051
- if (av.path.length !== bvi.path.length) return false;
5052
- for (let j = 0; j < av.path.length; j++) {
5053
- if (av.path[j] !== bvi.path[j]) return false;
5054
- }
5055
- }
5356
+ if (!errorFieldsEqual(v[i], bv[i])) return false;
5056
5357
  }
5057
5358
  }
5058
5359
  return true;
@@ -5266,7 +5567,7 @@ function useAbstractForm(configuration, options) {
5266
5567
  throw new paths.AnonPersistError({
5267
5568
  cause: "no-key",
5268
5569
  schemaFields: extractSchemaFields(resolvedSchema),
5269
- callSite: paths.captureUserCallSite()
5570
+ callSite: captureUserCallSite()
5270
5571
  });
5271
5572
  }
5272
5573
  const existing = registry.forms.get(key);
@@ -5515,7 +5816,7 @@ function recordAmbientProvide(ssr) {
5515
5816
  if (instance === null) return;
5516
5817
  const instanceKey = instance;
5517
5818
  const entry = {
5518
- source: paths.captureUserCallSite()
5819
+ source: captureUserCallSite()
5519
5820
  };
5520
5821
  const existing = ambientProvideHistory.get(instanceKey);
5521
5822
  if (existing === void 0) {
@@ -5554,7 +5855,7 @@ function enforceAnonPersistRule(formKey, ssr) {
5554
5855
  if (paths.__DEV__)
5555
5856
  throw new paths.AnonPersistError({
5556
5857
  cause: "no-key",
5557
- callSite: paths.captureUserCallSite()
5858
+ callSite: captureUserCallSite()
5558
5859
  });
5559
5860
  if (!ssr && !warnedAnonPersistKeys.has(formKey)) {
5560
5861
  warnedAnonPersistKeys.add(formKey);
@@ -5611,7 +5912,7 @@ function resolveState(key, registry) {
5611
5912
  }
5612
5913
  function warnMiss$1(detail, ssr) {
5613
5914
  if (!paths.__DEV__ || ssr) return;
5614
- const frame = paths.captureUserCallSite();
5915
+ const frame = captureUserCallSite();
5615
5916
  console.warn(
5616
5917
  `[attaform] injectForm: ${detail}. Returning null.` + (frame !== void 0 ? ` ${frame}` : "")
5617
5918
  );
@@ -5634,6 +5935,99 @@ function warnIfAmbientProviderHadDuplicates() {
5634
5935
  }
5635
5936
  }
5636
5937
 
5938
+ const warnedNoParentRV = paths.__DEV__ ? /* @__PURE__ */ new WeakSet() : null;
5939
+ let warnedOutsideSetup = false;
5940
+ function makeRegisterValueProxy(capturedRegisterValue) {
5941
+ return new Proxy({}, {
5942
+ get(_target, prop) {
5943
+ if (prop === "__v_isRef") return true;
5944
+ if (prop === "value") return capturedRegisterValue.value;
5945
+ const v = capturedRegisterValue.value;
5946
+ if (v === void 0) return void 0;
5947
+ return Reflect.get(v, prop);
5948
+ },
5949
+ has(_target, prop) {
5950
+ if (prop === "__v_isRef" || prop === "value") return true;
5951
+ const v = capturedRegisterValue.value;
5952
+ if (v === void 0) return false;
5953
+ return Reflect.has(v, prop);
5954
+ },
5955
+ ownKeys(_target) {
5956
+ const v = capturedRegisterValue.value;
5957
+ if (v === void 0) return [];
5958
+ return Reflect.ownKeys(v);
5959
+ },
5960
+ getOwnPropertyDescriptor(_target, prop) {
5961
+ const v = capturedRegisterValue.value;
5962
+ if (v === void 0) return void 0;
5963
+ const desc = Reflect.getOwnPropertyDescriptor(v, prop);
5964
+ if (desc !== void 0) {
5965
+ desc.configurable = true;
5966
+ }
5967
+ return desc;
5968
+ }
5969
+ });
5970
+ }
5971
+ function useRegister() {
5972
+ const instance = vue.getCurrentInstance();
5973
+ if (instance === null) {
5974
+ warnOutsideSetup();
5975
+ return makeRegisterValueProxy(vue.shallowRef(void 0));
5976
+ }
5977
+ paths.ensureAttaformInstalled(instance.appContext.app);
5978
+ const capturedRegisterValue = vue.shallowRef(void 0);
5979
+ const refreshAndStripBridgeAttrs = () => {
5980
+ const rawAttrs = instance.attrs;
5981
+ if ("registerValue" in rawAttrs) {
5982
+ capturedRegisterValue.value = rawAttrs["registerValue"];
5983
+ delete rawAttrs["registerValue"];
5984
+ } else {
5985
+ const dirs = instance.vnode.dirs;
5986
+ if (dirs !== null && dirs !== void 0) {
5987
+ for (const dir of dirs) {
5988
+ const marked = dir.dir?.[paths.V_REGISTER_MARKER];
5989
+ if (marked === true) {
5990
+ capturedRegisterValue.value = dir.value;
5991
+ break;
5992
+ }
5993
+ }
5994
+ }
5995
+ }
5996
+ if ("value" in rawAttrs) delete rawAttrs["value"];
5997
+ };
5998
+ refreshAndStripBridgeAttrs();
5999
+ vue.onBeforeMount(refreshAndStripBridgeAttrs);
6000
+ vue.onBeforeUpdate(refreshAndStripBridgeAttrs);
6001
+ vue.onMounted(() => {
6002
+ const el = instance.vnode.el;
6003
+ if (el !== null && el !== void 0 && typeof el === "object") {
6004
+ el[paths.REGISTER_OWNER_MARKER] = true;
6005
+ }
6006
+ if (capturedRegisterValue.value === void 0) {
6007
+ warnNoParentRV(instance);
6008
+ }
6009
+ });
6010
+ return makeRegisterValueProxy(capturedRegisterValue);
6011
+ }
6012
+ function warnOutsideSetup() {
6013
+ if (!paths.__DEV__) return;
6014
+ if (warnedOutsideSetup) return;
6015
+ warnedOutsideSetup = true;
6016
+ const frame = captureUserCallSite();
6017
+ console.warn(
6018
+ `[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}` : "")
6019
+ );
6020
+ }
6021
+ function warnNoParentRV(instance) {
6022
+ if (!paths.__DEV__ || warnedNoParentRV === null) return;
6023
+ if (warnedNoParentRV.has(instance)) return;
6024
+ warnedNoParentRV.add(instance);
6025
+ const frame = captureUserCallSite();
6026
+ console.warn(
6027
+ `[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}` : "")
6028
+ );
6029
+ }
6030
+
5637
6031
  const LAZY_BRAND = Symbol.for("attaform/wizard-lazy");
5638
6032
  function lazy(resolve) {
5639
6033
  return { [LAZY_BRAND]: true, resolve };
@@ -5643,6 +6037,8 @@ function isLazyMarker(value) {
5643
6037
  }
5644
6038
 
5645
6039
  const NOOP_WIZARD_HISTORY = {
6040
+ push() {
6041
+ },
5646
6042
  replace() {
5647
6043
  },
5648
6044
  read() {
@@ -5657,6 +6053,9 @@ function createWizardHistory(param) {
5657
6053
  if (typeof window === "undefined") return NOOP_WIZARD_HISTORY;
5658
6054
  const subscribers = [];
5659
6055
  let disposed = false;
6056
+ function currentKey() {
6057
+ return new URL(window.location.href).searchParams.get(param) ?? void 0;
6058
+ }
5660
6059
  function buildUrl(key) {
5661
6060
  const url = new URL(window.location.href);
5662
6061
  url.searchParams.set(param, key);
@@ -5664,25 +6063,29 @@ function createWizardHistory(param) {
5664
6063
  }
5665
6064
  function handlePopstate() {
5666
6065
  if (disposed) return;
5667
- const url = new URL(window.location.href);
5668
- const value = url.searchParams.get(param) ?? void 0;
6066
+ const value = currentKey();
5669
6067
  for (const subscriber of subscribers) subscriber(value);
5670
6068
  }
5671
6069
  window.addEventListener("popstate", handlePopstate);
5672
- function safeReplaceState(key) {
6070
+ function safeWrite(key, mode) {
5673
6071
  try {
5674
- window.history.replaceState({}, "", buildUrl(key));
6072
+ if (mode === "push") window.history.pushState({}, "", buildUrl(key));
6073
+ else window.history.replaceState({}, "", buildUrl(key));
5675
6074
  } catch {
5676
6075
  }
5677
6076
  }
5678
6077
  return {
6078
+ push(key) {
6079
+ if (disposed) return;
6080
+ if (currentKey() === key) return;
6081
+ safeWrite(key, "push");
6082
+ },
5679
6083
  replace(key) {
5680
6084
  if (disposed) return;
5681
- safeReplaceState(key);
6085
+ safeWrite(key, "replace");
5682
6086
  },
5683
6087
  read() {
5684
- const url = new URL(window.location.href);
5685
- return url.searchParams.get(param) ?? void 0;
6088
+ return currentKey();
5686
6089
  },
5687
6090
  subscribe(callback) {
5688
6091
  if (disposed) return;
@@ -5720,6 +6123,7 @@ function buildNoopWizardSchema(formKey) {
5720
6123
  getEmptyValueAtPath: () => void 0,
5721
6124
  isPreprocessOrCoerceLeaf: () => false,
5722
6125
  arrayShapeAtPath: () => void 0,
6126
+ isFixedObjectAtPath: (path) => path.length === 0,
5723
6127
  getSchemasAtPath: () => [],
5724
6128
  validateAtPath: () => success,
5725
6129
  getSlimPrimitiveTypesAtPath: () => new Set(EMPTY_SLIM_KINDS),
@@ -6173,7 +6577,10 @@ function useWizard(options) {
6173
6577
  };
6174
6578
  const persistCallback = options.persist === false ? void 0 : options.persist !== void 0 ? options.persist : (state) => {
6175
6579
  if (state.step === void 0) return;
6176
- historyHandle.replace(state.step);
6580
+ const current = historyHandle.read();
6581
+ const effectiveCurrent = current !== void 0 && isCompiledKey(current) ? current : firstKey();
6582
+ if (state.step === effectiveCurrent) historyHandle.replace(state.step);
6583
+ else historyHandle.push(state.step);
6177
6584
  };
6178
6585
  function isCompiledKey(key) {
6179
6586
  const list = compiledSteps.value;
@@ -6254,6 +6661,7 @@ function useWizard(options) {
6254
6661
  }
6255
6662
  const submitting = vue.ref(false);
6256
6663
  const submissionAttempts = vue.ref(0);
6664
+ const submitError = vue.ref(null);
6257
6665
  const done = vue.ref(false);
6258
6666
  function activateForm(form) {
6259
6667
  const source = asSubmissionSource(form);
@@ -6381,7 +6789,7 @@ function useWizard(options) {
6381
6789
  formKey: form.key
6382
6790
  };
6383
6791
  }
6384
- return full.process();
6792
+ return full.parse();
6385
6793
  }
6386
6794
  function collectErrors(results) {
6387
6795
  const out = [];
@@ -6412,6 +6820,7 @@ function useWizard(options) {
6412
6820
  return;
6413
6821
  }
6414
6822
  submitting.value = true;
6823
+ submitError.value = null;
6415
6824
  try {
6416
6825
  const currentKey = activeKey.value;
6417
6826
  const final = isFinalStep.value;
@@ -6420,12 +6829,14 @@ function useWizard(options) {
6420
6829
  if (final) {
6421
6830
  await Promise.all(
6422
6831
  list.map(async (step) => {
6832
+ registry.forms.get(step.key)?.clearUserErrors();
6423
6833
  const result = await processOne(step.form);
6424
6834
  results.set(step.key, result);
6425
6835
  })
6426
6836
  );
6427
6837
  } else {
6428
6838
  const active = activeForm.value;
6839
+ registry.forms.get(active.key)?.clearUserErrors();
6429
6840
  const result = await processOne(active);
6430
6841
  results.set(active.key, result);
6431
6842
  }
@@ -6460,7 +6871,6 @@ function useWizard(options) {
6460
6871
  if (target !== void 0) moveTo(target.key);
6461
6872
  }
6462
6873
  } else {
6463
- if (onError !== void 0) await onError(errors);
6464
6874
  if (options.focusFirstError !== false) {
6465
6875
  const firstFailedKey = errors[0]?.formKey;
6466
6876
  if (firstFailedKey !== void 0 && isCompiledKey(firstFailedKey)) {
@@ -6475,7 +6885,16 @@ function useWizard(options) {
6475
6885
  }
6476
6886
  }
6477
6887
  }
6888
+ if (onError !== void 0) {
6889
+ try {
6890
+ await onError(errors);
6891
+ } catch (cause) {
6892
+ throw new paths.SubmitErrorHandlerError("User-provided onError threw", { cause });
6893
+ }
6894
+ }
6478
6895
  }
6896
+ } catch (err) {
6897
+ submitError.value = paths.toError(err);
6479
6898
  } finally {
6480
6899
  submitting.value = false;
6481
6900
  }
@@ -6484,6 +6903,7 @@ function useWizard(options) {
6484
6903
  function reset() {
6485
6904
  submissionAttempts.value = 0;
6486
6905
  done.value = false;
6906
+ submitError.value = null;
6487
6907
  lazyEpoch.value += 1;
6488
6908
  for (const step of compiledSteps.value) {
6489
6909
  const full = asSubmissionSource(step.form);
@@ -6559,6 +6979,9 @@ function useWizard(options) {
6559
6979
  get submissionAttempts() {
6560
6980
  return submissionAttempts.value;
6561
6981
  },
6982
+ get submitError() {
6983
+ return submitError.value;
6984
+ },
6562
6985
  get visited() {
6563
6986
  return visited.value;
6564
6987
  }
@@ -6589,7 +7012,7 @@ function recordAmbientWizardProvide(ssr) {
6589
7012
  if (instance === null) return;
6590
7013
  const instanceKey = instance;
6591
7014
  const entry = {
6592
- source: paths.captureUserCallSite()
7015
+ source: captureUserCallSite()
6593
7016
  };
6594
7017
  const existing = ambientWizardProvideHistory.get(instanceKey);
6595
7018
  if (existing === void 0) {
@@ -6644,7 +7067,7 @@ function availableKeysHint(wizards) {
6644
7067
  }
6645
7068
  function warnMiss(detail, ssr, hint) {
6646
7069
  if (!paths.__DEV__ || ssr) return;
6647
- const frame = paths.captureUserCallSite();
7070
+ const frame = captureUserCallSite();
6648
7071
  const parts = [`[attaform] injectWizard: ${detail}. Returning null.`];
6649
7072
  if (hint !== void 0) parts.push(hint);
6650
7073
  if (frame !== void 0) parts.push(frame);
@@ -6698,5 +7121,6 @@ exports.slimKindOf = slimKindOf;
6698
7121
  exports.structuralSnapshot = structuralSnapshot;
6699
7122
  exports.unset = unset;
6700
7123
  exports.useAbstractForm = useAbstractForm;
7124
+ exports.useRegister = useRegister;
6701
7125
  exports.useWizard = useWizard;
6702
- //# sourceMappingURL=attaform.BUszFoKq.cjs.map
7126
+ //# sourceMappingURL=attaform.D2ZuIOCf.cjs.map