attaform 0.21.1 → 0.22.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 (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 +20 -18
  20. package/dist/index.d.mts +20 -18
  21. package/dist/index.d.ts +20 -18
  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.D32WwKk6.cjs → attaform.01iKS_lz.cjs} +260 -356
  32. package/dist/shared/attaform.01iKS_lz.cjs.map +1 -0
  33. package/dist/shared/{attaform.Y1ZGhM4k.mjs → attaform.6xE0Lcfd.mjs} +2 -2
  34. package/dist/shared/{attaform.Y1ZGhM4k.mjs.map → attaform.6xE0Lcfd.mjs.map} +1 -1
  35. package/dist/shared/{attaform.S-pYLSo4.cjs → attaform.AyujQoHp.cjs} +13 -16
  36. package/dist/shared/attaform.AyujQoHp.cjs.map +1 -0
  37. package/dist/shared/{attaform.BupwXkj_.mjs → attaform.BFWb6hDk.mjs} +29 -23
  38. package/dist/shared/attaform.BFWb6hDk.mjs.map +1 -0
  39. package/dist/shared/{attaform.NQ8mybyW.d.mts → attaform.BGwNZ9GV.d.cts} +63 -64
  40. package/dist/shared/{attaform.pmtahXKy.mjs → attaform.BKFwekY2.mjs} +257 -356
  41. package/dist/shared/attaform.BKFwekY2.mjs.map +1 -0
  42. package/dist/shared/{attaform.BSkvn43g.cjs → attaform.C-RtnCJM.cjs} +116 -47
  43. package/dist/shared/attaform.C-RtnCJM.cjs.map +1 -0
  44. package/dist/shared/{attaform.Bv7dRDWK.d.ts → attaform.CCCeEPwa.d.mts} +63 -64
  45. package/dist/shared/{attaform.BM6YD9kZ.cjs → attaform.CR6wGvNu.cjs} +29 -23
  46. package/dist/shared/attaform.CR6wGvNu.cjs.map +1 -0
  47. package/dist/shared/{attaform.Bq5sX7TF.cjs → attaform.CRzpFCjV.cjs} +2 -2
  48. package/dist/shared/{attaform.Bq5sX7TF.cjs.map → attaform.CRzpFCjV.cjs.map} +1 -1
  49. package/dist/shared/{attaform.ClXwitZj.cjs → attaform.CjMcwV7W.cjs} +894 -342
  50. package/dist/shared/attaform.CjMcwV7W.cjs.map +1 -0
  51. package/dist/shared/{attaform.DR6RmxWZ.mjs → attaform.CsB-iKbU.mjs} +888 -337
  52. package/dist/shared/attaform.CsB-iKbU.mjs.map +1 -0
  53. package/dist/shared/{attaform.BWfliRIK.d.cts → attaform.D4XYaasQ.d.ts} +63 -64
  54. package/dist/shared/{attaform.Be8NZG9M.mjs → attaform.DCjgGir_.mjs} +19 -45
  55. package/dist/shared/attaform.DCjgGir_.mjs.map +1 -0
  56. package/dist/shared/{attaform.DMEP_ENr.mjs → attaform.DNuiFCXG.mjs} +14 -17
  57. package/dist/shared/attaform.DNuiFCXG.mjs.map +1 -0
  58. package/dist/shared/{attaform.MtrpT6Ki.d.ts → attaform.DUMWQefY.d.ts} +1 -1
  59. package/dist/shared/{attaform.DozgVlCE.mjs → attaform.DgCfLqay.mjs} +116 -47
  60. package/dist/shared/attaform.DgCfLqay.mjs.map +1 -0
  61. package/dist/shared/{attaform.D0dWZsJt.d.mts → attaform.DvUH4a3o.d.cts} +307 -133
  62. package/dist/shared/{attaform.D0dWZsJt.d.cts → attaform.DvUH4a3o.d.mts} +307 -133
  63. package/dist/shared/{attaform.D0dWZsJt.d.ts → attaform.DvUH4a3o.d.ts} +307 -133
  64. package/dist/shared/{attaform.Duecg2NO.d.mts → attaform.FN0vaQAg.d.mts} +1 -1
  65. package/dist/shared/{attaform.CICFZ1iS.cjs → attaform.Q3eAD2wD.cjs} +19 -45
  66. package/dist/shared/attaform.Q3eAD2wD.cjs.map +1 -0
  67. package/dist/shared/{attaform.FudOcHaa.d.cts → attaform.aekT7mMx.d.cts} +1 -1
  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 +31 -22
  89. package/dist/zod.d.mts +31 -22
  90. package/dist/zod.d.ts +31 -22
  91. package/dist/zod.mjs +5 -6
  92. package/dist/zod.mjs.map +1 -1
  93. package/package.json +4 -12
  94. package/dist/shared/attaform.BM6YD9kZ.cjs.map +0 -1
  95. package/dist/shared/attaform.BSkvn43g.cjs.map +0 -1
  96. package/dist/shared/attaform.Be8NZG9M.mjs.map +0 -1
  97. package/dist/shared/attaform.BupwXkj_.mjs.map +0 -1
  98. package/dist/shared/attaform.CICFZ1iS.cjs.map +0 -1
  99. package/dist/shared/attaform.ClXwitZj.cjs.map +0 -1
  100. package/dist/shared/attaform.D32WwKk6.cjs.map +0 -1
  101. package/dist/shared/attaform.DMEP_ENr.mjs.map +0 -1
  102. package/dist/shared/attaform.DR6RmxWZ.mjs.map +0 -1
  103. package/dist/shared/attaform.DozgVlCE.mjs.map +0 -1
  104. package/dist/shared/attaform.S-pYLSo4.cjs.map +0 -1
  105. package/dist/shared/attaform.pmtahXKy.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.D32WwKk6.cjs');
4
+ const paths = require('./attaform.01iKS_lz.cjs');
5
5
 
6
6
  function safeAssign(target, key, value) {
7
7
  if (key === "__proto__") {
@@ -36,7 +36,7 @@ function descendStep(value, segment) {
36
36
  if (typeof value !== "object") return NOT_FOUND;
37
37
  if (Array.isArray(value)) {
38
38
  if (typeof segment !== "number") return NOT_FOUND;
39
- if (segment < 0 || segment >= value.length) return NOT_FOUND;
39
+ if (!(segment in value)) return NOT_FOUND;
40
40
  return value[segment];
41
41
  }
42
42
  const record = value;
@@ -71,7 +71,7 @@ function hasAtPath(root, path) {
71
71
  if (current === null || current === void 0) return false;
72
72
  if (typeof current !== "object") return false;
73
73
  if (Array.isArray(current)) {
74
- return typeof last === "number" && last >= 0 && last < current.length;
74
+ return typeof last === "number" && last in current;
75
75
  }
76
76
  const key = typeof last === "number" ? String(last) : last;
77
77
  if (isShadowedKey(key)) return safeOwnHas(current, key);
@@ -100,6 +100,31 @@ function setAtPathOffset(root, path, value, offset) {
100
100
  safeAssign(rec, head, setAtPathOffset(safeOwnRead(rec, head), path, value, nextOffset));
101
101
  return rec;
102
102
  }
103
+ const NO_IN_PLACE = { applied: false };
104
+ function tryInPlaceLeafWrite(root, path, value) {
105
+ if (path.length === 0) return NO_IN_PLACE;
106
+ let node = root;
107
+ for (let i = 0; i < path.length; i++) {
108
+ const seg = path[i];
109
+ if (Array.isArray(node)) {
110
+ if (typeof seg !== "number" || seg < 0 || seg >= node.length) return NO_IN_PLACE;
111
+ } else if (isPlainRecord(node)) {
112
+ if (typeof seg !== "string" || isShadowedKey(seg) || !(seg in node)) return NO_IN_PLACE;
113
+ } else {
114
+ return NO_IN_PLACE;
115
+ }
116
+ const container = node;
117
+ if (i < path.length - 1) {
118
+ node = container[seg];
119
+ continue;
120
+ }
121
+ const old = container[seg];
122
+ if (isPlainRecord(old) || Array.isArray(old)) return NO_IN_PLACE;
123
+ container[seg] = value;
124
+ return { applied: true, old };
125
+ }
126
+ return NO_IN_PLACE;
127
+ }
103
128
  function deleteAtPath(root, path) {
104
129
  return deleteAtPathOffset(root, path, 0);
105
130
  }
@@ -162,68 +187,80 @@ function mergeStructuralImpl(schema, scratch, consumer, defaultValue) {
162
187
  }
163
188
  if (consumer === null) return null;
164
189
  if (Array.isArray(consumer)) {
165
- const shape = resolveArrayShape(schema, scratch);
166
- const isTuple = typeof shape === "number";
167
- const targetLen = isTuple ? shape : consumer.length;
168
- let cachedElementDefault;
169
- let cachedElementDefaultRead = false;
170
- let mutated = targetLen > consumer.length;
171
- const out = consumer.slice();
172
- while (out.length < targetLen) out.push(void 0);
173
- for (let i = 0; i < targetLen; i++) {
174
- scratch.push(i);
175
- let elemDefault;
176
- if (isTuple) {
177
- elemDefault = schema.getDefaultAtPath(scratch);
178
- } else {
179
- if (!cachedElementDefaultRead) {
180
- cachedElementDefault = schema.getDefaultAtPath(scratch);
181
- cachedElementDefaultRead = true;
182
- }
183
- elemDefault = cachedElementDefault;
184
- }
185
- const consumerElem = i < consumer.length ? consumer[i] : void 0;
186
- const merged = mergeStructuralImpl(schema, scratch, consumerElem, elemDefault);
187
- scratch.pop();
188
- if (merged !== consumerElem) {
189
- out[i] = merged;
190
- mutated = true;
191
- }
192
- }
193
- return mutated ? out : consumer;
190
+ return mergeStructuralArray(schema, scratch, consumer);
194
191
  }
195
192
  if (isPlainRecord(consumer)) {
196
193
  if (!isPlainRecord(defaultValue)) {
197
194
  return consumer;
198
195
  }
199
- let mutated = false;
200
196
  const out = { ...consumer };
201
- for (const key of Object.keys(defaultValue)) {
202
- if (!safeOwnHas(consumer, key)) {
203
- const defAtKey = safeOwnRead(defaultValue, key);
204
- scratch.push(key);
205
- const filled = mergeStructuralImpl(schema, scratch, void 0, defAtKey);
206
- scratch.pop();
207
- if (filled !== void 0) {
208
- safeAssign(out, key, filled);
209
- mutated = true;
210
- }
197
+ const filledAny = fillMissingKeysFromDefault(schema, scratch, consumer, defaultValue, out);
198
+ const recursedAny = recurseIntoConsumerKeys(schema, scratch, consumer, defaultValue, out);
199
+ return filledAny || recursedAny ? out : consumer;
200
+ }
201
+ return consumer;
202
+ }
203
+ function mergeStructuralArray(schema, scratch, consumer) {
204
+ const shape = resolveArrayShape(schema, scratch);
205
+ const isTuple = typeof shape === "number";
206
+ const targetLen = isTuple ? shape : consumer.length;
207
+ let cachedElementDefault;
208
+ let cachedElementDefaultRead = false;
209
+ let mutated = targetLen > consumer.length;
210
+ const out = consumer.slice();
211
+ while (out.length < targetLen) out.push(void 0);
212
+ for (let i = 0; i < targetLen; i++) {
213
+ scratch.push(i);
214
+ let elemDefault;
215
+ if (isTuple) {
216
+ elemDefault = schema.getDefaultAtPath(scratch);
217
+ } else {
218
+ if (!cachedElementDefaultRead) {
219
+ cachedElementDefault = schema.getDefaultAtPath(scratch);
220
+ cachedElementDefaultRead = true;
211
221
  }
222
+ elemDefault = cachedElementDefault;
223
+ }
224
+ const consumerElem = i < consumer.length ? consumer[i] : void 0;
225
+ const merged = mergeStructuralImpl(schema, scratch, consumerElem, elemDefault);
226
+ scratch.pop();
227
+ if (merged !== consumerElem) {
228
+ out[i] = merged;
229
+ mutated = true;
212
230
  }
213
- for (const key of Object.keys(consumer)) {
214
- const cVal = safeOwnRead(consumer, key);
215
- if (cVal === void 0) continue;
231
+ }
232
+ return mutated ? out : consumer;
233
+ }
234
+ function fillMissingKeysFromDefault(schema, scratch, consumer, defaultValue, out) {
235
+ let mutated = false;
236
+ for (const key of Object.keys(defaultValue)) {
237
+ if (!safeOwnHas(consumer, key)) {
238
+ const defAtKey = safeOwnRead(defaultValue, key);
216
239
  scratch.push(key);
217
- const merged = mergeStructuralImpl(schema, scratch, cVal, safeOwnRead(defaultValue, key));
240
+ const filled = mergeStructuralImpl(schema, scratch, void 0, defAtKey);
218
241
  scratch.pop();
219
- if (merged !== cVal) {
220
- safeAssign(out, key, merged);
242
+ if (filled !== void 0) {
243
+ safeAssign(out, key, filled);
221
244
  mutated = true;
222
245
  }
223
246
  }
224
- return mutated ? out : consumer;
225
247
  }
226
- return consumer;
248
+ return mutated;
249
+ }
250
+ function recurseIntoConsumerKeys(schema, scratch, consumer, defaultValue, out) {
251
+ let mutated = false;
252
+ for (const key of Object.keys(consumer)) {
253
+ const cVal = safeOwnRead(consumer, key);
254
+ if (cVal === void 0) continue;
255
+ scratch.push(key);
256
+ const merged = mergeStructuralImpl(schema, scratch, cVal, safeOwnRead(defaultValue, key));
257
+ scratch.pop();
258
+ if (merged !== cVal) {
259
+ safeAssign(out, key, merged);
260
+ mutated = true;
261
+ }
262
+ }
263
+ return mutated;
227
264
  }
228
265
  function setAtPathWithSchemaFill(root, schema, fullPath, value) {
229
266
  if (fullPath.length === 0) return value;
@@ -306,55 +343,27 @@ function diffAndApply(oldValue, newValue, prefix, visit) {
306
343
  const oldIsDescendable = isDescendable(oldValue);
307
344
  const newIsDescendable = isDescendable(newValue);
308
345
  if (oldValue === void 0 && newIsDescendable) {
309
- if (Array.isArray(newValue)) {
310
- for (let i = 0; i < newValue.length; i++) {
311
- diffAndApply(void 0, newValue[i], appendSegment(prefix, i), visit);
312
- }
313
- } else {
314
- const rec = newValue;
315
- for (const k of Object.keys(rec)) {
316
- diffAndApply(void 0, rec[k], appendSegment(prefix, k), visit);
317
- }
318
- }
346
+ walkNewDescendable(newValue, prefix, visit);
319
347
  return;
320
348
  }
321
349
  if (oldIsDescendable && newValue === void 0) {
322
- if (Array.isArray(oldValue)) {
323
- for (let i = 0; i < oldValue.length; i++) {
324
- diffAndApply(oldValue[i], void 0, appendSegment(prefix, i), visit);
325
- }
326
- } else {
327
- const rec = oldValue;
328
- for (const k of Object.keys(rec)) {
329
- diffAndApply(rec[k], void 0, appendSegment(prefix, k), visit);
330
- }
331
- }
350
+ walkOldDescendable(oldValue, prefix, visit);
332
351
  return;
333
352
  }
334
353
  if (oldIsDescendable && newIsDescendable) {
335
354
  const oldIsArray = Array.isArray(oldValue);
336
355
  const newIsArray = Array.isArray(newValue);
337
356
  if (oldIsArray && newIsArray) {
338
- const oldArr = oldValue;
339
- const newArr = newValue;
340
- const max = Math.max(oldArr.length, newArr.length);
341
- for (let i = 0; i < max; i++) {
342
- diffAndApply(oldArr[i], newArr[i], appendSegment(prefix, i), visit);
343
- }
357
+ diffArraysLockstep(oldValue, newValue, prefix, visit);
344
358
  return;
345
359
  }
346
360
  if (!oldIsArray && !newIsArray) {
347
- const oldRec = oldValue;
348
- const newRec = newValue;
349
- const seen = /* @__PURE__ */ new Set();
350
- for (const k of Object.keys(oldRec)) {
351
- seen.add(k);
352
- diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
353
- }
354
- for (const k of Object.keys(newRec)) {
355
- if (seen.has(k)) continue;
356
- diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
357
- }
361
+ diffObjectsLockstep(
362
+ oldValue,
363
+ newValue,
364
+ prefix,
365
+ visit
366
+ );
358
367
  return;
359
368
  }
360
369
  visit({ kind: "changed", path: prefix, oldValue, newValue });
@@ -378,7 +387,48 @@ function diffAndApply(oldValue, newValue, prefix, visit) {
378
387
  }
379
388
  visit({ kind: "changed", path: prefix, oldValue, newValue });
380
389
  }
381
- function applyChangedKeys(target, source) {
390
+ function walkNewDescendable(newValue, prefix, visit) {
391
+ if (Array.isArray(newValue)) {
392
+ for (let i = 0; i < newValue.length; i++) {
393
+ diffAndApply(void 0, newValue[i], appendSegment(prefix, i), visit);
394
+ }
395
+ } else {
396
+ const rec = newValue;
397
+ for (const k of Object.keys(rec)) {
398
+ diffAndApply(void 0, rec[k], appendSegment(prefix, k), visit);
399
+ }
400
+ }
401
+ }
402
+ function walkOldDescendable(oldValue, prefix, visit) {
403
+ if (Array.isArray(oldValue)) {
404
+ for (let i = 0; i < oldValue.length; i++) {
405
+ diffAndApply(oldValue[i], void 0, appendSegment(prefix, i), visit);
406
+ }
407
+ } else {
408
+ const rec = oldValue;
409
+ for (const k of Object.keys(rec)) {
410
+ diffAndApply(rec[k], void 0, appendSegment(prefix, k), visit);
411
+ }
412
+ }
413
+ }
414
+ function diffArraysLockstep(oldArr, newArr, prefix, visit) {
415
+ const max = Math.max(oldArr.length, newArr.length);
416
+ for (let i = 0; i < max; i++) {
417
+ diffAndApply(oldArr[i], newArr[i], appendSegment(prefix, i), visit);
418
+ }
419
+ }
420
+ function diffObjectsLockstep(oldRec, newRec, prefix, visit) {
421
+ const seen = /* @__PURE__ */ new Set();
422
+ for (const k of Object.keys(oldRec)) {
423
+ seen.add(k);
424
+ diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
425
+ }
426
+ for (const k of Object.keys(newRec)) {
427
+ if (seen.has(k)) continue;
428
+ diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
429
+ }
430
+ }
431
+ function applyChangedKeys(target, source, arrayOpPath, currentPath) {
382
432
  if (!isDescendable(target) || !isDescendable(source)) return false;
383
433
  const targetIsArray = Array.isArray(target);
384
434
  const sourceIsArray = Array.isArray(source);
@@ -396,11 +446,27 @@ function applyChangedKeys(target, source) {
396
446
  if (targetIsArray) {
397
447
  const t = target;
398
448
  const s = source;
399
- if (t.length > s.length) t.length = s.length;
400
- for (const idx of changedFirstSegments) {
401
- if (typeof idx === "symbol") continue;
402
- const i = typeof idx === "number" ? idx : Number(idx);
403
- t[i] = s[i];
449
+ if (arrayOpPath === null || paths.pathsEqual(currentPath, arrayOpPath)) {
450
+ if (t.length > s.length) t.length = s.length;
451
+ for (const idx of changedFirstSegments) {
452
+ if (typeof idx === "symbol") continue;
453
+ const i = typeof idx === "number" ? idx : Number(idx);
454
+ if (i >= s.length) continue;
455
+ t[i] = s[i];
456
+ }
457
+ } else {
458
+ for (const idx of changedFirstSegments) {
459
+ if (typeof idx === "symbol") continue;
460
+ const i = typeof idx === "number" ? idx : Number(idx);
461
+ if (i >= s.length) continue;
462
+ const childPath = appendSegment(currentPath, i);
463
+ const curEl = t[i];
464
+ const nextEl = s[i];
465
+ if (paths.isPathPrefix(childPath, arrayOpPath) && isDescendable(curEl) && isDescendable(nextEl) && applyChangedKeys(curEl, nextEl, arrayOpPath, childPath)) {
466
+ continue;
467
+ }
468
+ t[i] = nextEl;
469
+ }
404
470
  }
405
471
  } else {
406
472
  const t = target;
@@ -412,7 +478,14 @@ function applyChangedKeys(target, source) {
412
478
  for (const k of changedFirstSegments) {
413
479
  if (typeof k === "symbol") continue;
414
480
  const key = String(k);
415
- safeAssign(t, key, safeOwnRead(s, key));
481
+ const nextVal = safeOwnRead(s, key);
482
+ if (arrayOpPath !== null) {
483
+ const curVal = safeOwnRead(t, key);
484
+ if (isDescendable(curVal) && isDescendable(nextVal) && applyChangedKeys(curVal, nextVal, arrayOpPath, appendSegment(currentPath, key))) {
485
+ continue;
486
+ }
487
+ }
488
+ safeAssign(t, key, nextVal);
416
489
  }
417
490
  }
418
491
  return true;
@@ -524,6 +597,50 @@ function resolveGetDisplayState(config) {
524
597
  return config ?? defaultDisplayState;
525
598
  }
526
599
 
600
+ const AttaformErrorCode = {
601
+ /** A required field is in the blank set — user hasn't supplied a value. */
602
+ NoValueSupplied: "atta:no-value-supplied",
603
+ /** The schema adapter's `validateAtPath` threw synchronously. */
604
+ AdapterThrew: "atta:adapter-threw",
605
+ /**
606
+ * User code inside a `z.preprocess`, `.refine`, or `.transform`
607
+ * threw (sync or async). The adapter caught the throw and surfaced
608
+ * it as a `ValidationError` at the field path so the form's normal
609
+ * error pipeline handles it instead of leaking as an unhandled
610
+ * rejection or routing through `submitError`.
611
+ */
612
+ ValidatorThrew: "atta:validator-threw",
613
+ /**
614
+ * A function-form `defaultValues` factory threw or its promise
615
+ * rejected. The runtime captures the raw error on `form.hydrateError`
616
+ * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
617
+ * the standard error pipeline carries the signal. Critical for the
618
+ * SSR round-trip: `hydrateError` itself does not ride the wire
619
+ * payload, but `schemaErrors` does, so the client sees the failure
620
+ * after rehydration without an extra channel.
621
+ */
622
+ HydrationFailed: "atta:hydration-failed",
623
+ /** The supplied path didn't resolve to any node in the schema. */
624
+ PathNotFound: "atta:path-not-found",
625
+ /**
626
+ * A walked form's `activate()` (async `defaultValues` factory) threw
627
+ * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
628
+ * `ValidationError` at the form-level path (`[]`) so the wizard's
629
+ * aggregate error pipeline can carry the failure alongside ordinary
630
+ * validation errors. The raw factory error remains on
631
+ * `form.hydrateError` for retry UX.
632
+ */
633
+ ActivationFailed: "atta:activation-failed"
634
+ };
635
+ function makeBlankRequiredError(segments, formKey) {
636
+ return {
637
+ message: "No value supplied",
638
+ path: [...segments],
639
+ formKey,
640
+ code: AttaformErrorCode.NoValueSupplied
641
+ };
642
+ }
643
+
527
644
  const DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS = 0;
528
645
  const DEFAULT_PERSISTENCE_DEBOUNCE_MS = 300;
529
646
  const DEFAULT_HISTORY_MAX_SNAPSHOTS = 128;
@@ -625,13 +742,19 @@ function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, options
625
742
  return c;
626
743
  };
627
744
  }
745
+ function resolveFieldMetaAndLabel(state, segments) {
746
+ const resolved = state.schema.getFieldMetaAtPath ? state.schema.getFieldMetaAtPath(segments) : EMPTY_RESOLVED_FIELD_META;
747
+ const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
748
+ const label = resolved.label || humanize(lastSegment);
749
+ return { resolved, label };
750
+ }
628
751
  function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
629
752
  const record = state.fields.get(key);
630
753
  const value = state.getValueAtPath(segments);
631
754
  const original = state.originals.get(key)?.value;
632
755
  const pristine = state.isPristineAtPath(segments);
633
756
  const schemaForKey = state.schemaErrors.get(key);
634
- const blankForKey = state.derivedBlankErrors.value.get(key);
757
+ const blankForKey = state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments) ? [makeBlankRequiredError(segments, state.formKey)] : void 0;
635
758
  const userForKey = state.userErrors.get(key);
636
759
  const errors = [];
637
760
  if (schemaForKey !== void 0) errors.push(...schemaForKey);
@@ -646,9 +769,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
646
769
  const elementRecord = state.elements.get(key);
647
770
  const elementsArr = elementRecord ? Object.freeze([...elementRecord.elements]) : EMPTY_ELEMENTS;
648
771
  const firstElement = elementsArr[0] ?? null;
649
- const resolved = state.schema.getFieldMetaAtPath ? state.schema.getFieldMetaAtPath(segments) : EMPTY_RESOLVED_FIELD_META;
650
- const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
651
- const label = resolved.label || humanize(lastSegment);
772
+ const { resolved, label } = resolveFieldMetaAndLabel(state, segments);
652
773
  return {
653
774
  value,
654
775
  original,
@@ -697,9 +818,32 @@ function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBa
697
818
  getDisplayState
698
819
  );
699
820
  }
821
+ function visitActiveLeafPaths(value, base, visit) {
822
+ if (Array.isArray(value)) {
823
+ for (let i = 0; i < value.length; i++) {
824
+ const child = value[i];
825
+ if (Array.isArray(child) || isPlainRecord(child)) {
826
+ visitActiveLeafPaths(child, [...base, i], visit);
827
+ } else {
828
+ visit([...base, i]);
829
+ }
830
+ }
831
+ return;
832
+ }
833
+ if (isPlainRecord(value)) {
834
+ for (const k of Object.keys(value)) {
835
+ const child = value[k];
836
+ if (Array.isArray(child) || isPlainRecord(child)) {
837
+ visitActiveLeafPaths(child, [...base, k], visit);
838
+ } else {
839
+ visit([...base, k]);
840
+ }
841
+ }
842
+ }
843
+ }
700
844
  function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
701
845
  const formValue = state.form.value;
702
- const value = state.getValueAtPath(segments);
846
+ const value = getAtPath(formValue, segments);
703
847
  const original = state.originals.get(key)?.value;
704
848
  let pristine = true;
705
849
  let blank = true;
@@ -718,12 +862,16 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
718
862
  let asyncPending = false;
719
863
  const submissionAttempts = state.submissionAttempts.value;
720
864
  const blurredLeafSegments = [];
721
- for (const [leafKey, entry] of state.originals) {
722
- if (!paths.isPathPrefix(segments, entry.segments)) continue;
723
- if (segments.length === entry.segments.length) continue;
724
- if (!hasAtPath(formValue, entry.segments)) continue;
865
+ const descendantLeaves = [];
866
+ visitActiveLeafPaths(value, segments, (leafSegments) => {
867
+ const { key: leafKey } = paths.keyForSegments(leafSegments);
868
+ const entry = state.originals.get(leafKey);
869
+ if (entry === void 0) return;
870
+ descendantLeaves.push({ key: leafKey, segments: entry.segments });
871
+ });
872
+ for (const { key: leafKey, segments: leafSeg } of descendantLeaves) {
725
873
  const leafRecord = state.fields.get(leafKey);
726
- if (!state.isPristineAtPathByKey(leafKey, entry.segments)) {
874
+ if (!state.isPristineAtPathByKey(leafKey, leafSeg)) {
727
875
  pristine = false;
728
876
  dirty = true;
729
877
  }
@@ -734,7 +882,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
734
882
  if (leafRecord?.interacted === true) interacted = true;
735
883
  if (leafRecord?.blurredAfterInteraction === true) {
736
884
  blurredAfterInteraction = true;
737
- blurredLeafSegments.push(entry.segments);
885
+ blurredLeafSegments.push(leafSeg);
738
886
  }
739
887
  if (leafRecord?.connected === true) connected = true;
740
888
  if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0) {
@@ -751,7 +899,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
751
899
  if (leafGateOpen && since !== void 0 && (transformingSince === null || since < transformingSince))
752
900
  transformingSince = since;
753
901
  }
754
- if (state.pathHasAsyncValidationByKey(leafKey, entry.segments)) asyncPending = true;
902
+ if (state.pathHasAsyncValidationByKey(leafKey, leafSeg)) asyncPending = true;
755
903
  const ts = leafRecord?.updatedAt;
756
904
  if (ts !== void 0 && ts !== null) {
757
905
  if (updatedAt === null || ts > updatedAt) updatedAt = ts;
@@ -785,9 +933,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
785
933
  const ownTransformError = state.transformErrors.get(key) ?? null;
786
934
  const gated = asyncPending && !state.firstValidationDone.value;
787
935
  const valid = !gated && errors.length === 0 && !validating;
788
- const resolved = state.schema.getFieldMetaAtPath ? state.schema.getFieldMetaAtPath(segments) : EMPTY_RESOLVED_FIELD_META;
789
- const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
790
- const label = resolved.label || humanize(lastSegment);
936
+ const { resolved, label } = resolveFieldMetaAndLabel(state, segments);
791
937
  return {
792
938
  base: {
793
939
  value,
@@ -1380,7 +1526,7 @@ function buildFieldArrayApi(state) {
1380
1526
  append(path, value) {
1381
1527
  const next = readArray(path);
1382
1528
  next.push(value);
1383
- return writeArray(path, next);
1529
+ return writeArray(path, next, { kind: "insert", index: next.length - 1 });
1384
1530
  },
1385
1531
  prepend(path, value) {
1386
1532
  const next = readArray(path);
@@ -1570,20 +1716,20 @@ function buildFieldStateProxy(state, formInstanceId, getFormMetaBase, options) {
1570
1716
  }
1571
1717
  function materializeFields(state, containerSegments, snapshotFieldStateAt) {
1572
1718
  const liveValue = getAtPath(state.form.value, containerSegments);
1573
- return walk$2(liveValue, containerSegments, state.schema, snapshotFieldStateAt);
1719
+ return walk$1(liveValue, containerSegments, state.schema, snapshotFieldStateAt);
1574
1720
  }
1575
- function walk$2(value, basePath, schema, snapshotFieldStateAt) {
1721
+ function walk$1(value, basePath, schema, snapshotFieldStateAt) {
1576
1722
  if (schema.isLeafAtPath(basePath)) return snapshotFieldStateAt(basePath);
1577
1723
  if (value === null || value === void 0) return value;
1578
1724
  if (typeof value !== "object") {
1579
1725
  return value;
1580
1726
  }
1581
1727
  if (Array.isArray(value)) {
1582
- return value.map((_, i) => walk$2(value[i], [...basePath, i], schema, snapshotFieldStateAt));
1728
+ return value.map((_, i) => walk$1(value[i], [...basePath, i], schema, snapshotFieldStateAt));
1583
1729
  }
1584
1730
  const result = {};
1585
1731
  for (const key of Object.keys(value)) {
1586
- result[key] = walk$2(
1732
+ result[key] = walk$1(
1587
1733
  value[key],
1588
1734
  [...basePath, key],
1589
1735
  schema,
@@ -1667,87 +1813,46 @@ function mergeDeep(target, source, path, schema) {
1667
1813
  if (!isPlainRecord(source)) return source;
1668
1814
  if (schema !== void 0) {
1669
1815
  const du = schema.getUnionDiscriminatorAtPath(path);
1670
- if (du !== void 0) {
1671
- const sourceRecord = source;
1672
- const sourceDisc = sourceRecord[du.discriminatorKey];
1673
- if (sourceDisc !== void 0 && !du.isVariantSelected(sourceDisc)) {
1674
- return { [du.discriminatorKey]: sourceDisc };
1675
- }
1676
- if (sourceDisc !== void 0) {
1677
- const variantDefault = du.getVariantDefault(sourceDisc);
1678
- if (isPlainRecord(variantDefault)) {
1679
- const out2 = { ...variantDefault };
1680
- for (const key of Object.keys(sourceRecord)) {
1681
- if (!safeOwnHas(variantDefault, key) && key !== du.discriminatorKey) continue;
1682
- safeAssign(
1683
- out2,
1684
- key,
1685
- mergeDeep(
1686
- safeOwnRead(out2, key),
1687
- safeOwnRead(sourceRecord, key),
1688
- [...path, key],
1689
- schema
1690
- )
1691
- );
1692
- }
1693
- return out2;
1694
- }
1695
- }
1696
- return {};
1816
+ if (du !== void 0) return mergeDuAwareKeys(source, path, schema, du);
1817
+ }
1818
+ return mergeObjectKeys(target, source, path, schema);
1819
+ }
1820
+ function mergeDuAwareKeys(source, path, schema, du) {
1821
+ const sourceDisc = source[du.discriminatorKey];
1822
+ if (sourceDisc !== void 0 && !du.isVariantSelected(sourceDisc)) {
1823
+ return { [du.discriminatorKey]: sourceDisc };
1824
+ }
1825
+ if (sourceDisc !== void 0) {
1826
+ const variantDefault = du.getVariantDefault(sourceDisc);
1827
+ if (isPlainRecord(variantDefault)) {
1828
+ return mergeVariantKeys(source, variantDefault, path, schema, du);
1697
1829
  }
1698
1830
  }
1699
- const mergeTarget = target;
1700
- const out = isPlainRecord(mergeTarget) ? { ...mergeTarget } : {};
1831
+ return {};
1832
+ }
1833
+ function mergeVariantKeys(source, variantDefault, path, schema, du) {
1834
+ const out = { ...variantDefault };
1701
1835
  for (const key of Object.keys(source)) {
1836
+ if (!safeOwnHas(variantDefault, key) && key !== du.discriminatorKey) continue;
1702
1837
  safeAssign(
1703
1838
  out,
1704
1839
  key,
1705
- mergeDeep(
1706
- safeOwnRead(out, key),
1707
- safeOwnRead(source, key),
1708
- [...path, key],
1709
- schema
1710
- )
1840
+ mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
1841
+ );
1842
+ }
1843
+ return out;
1844
+ }
1845
+ function mergeObjectKeys(target, source, path, schema) {
1846
+ const out = isPlainRecord(target) ? { ...target } : {};
1847
+ for (const key of Object.keys(source)) {
1848
+ safeAssign(
1849
+ out,
1850
+ key,
1851
+ mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
1711
1852
  );
1712
1853
  }
1713
1854
  return out;
1714
1855
  }
1715
-
1716
- const AttaformErrorCode = {
1717
- /** A required field is in the blank set — user hasn't supplied a value. */
1718
- NoValueSupplied: "atta:no-value-supplied",
1719
- /** The schema adapter's `validateAtPath` threw synchronously. */
1720
- AdapterThrew: "atta:adapter-threw",
1721
- /**
1722
- * User code inside a `z.preprocess`, `.refine`, or `.transform`
1723
- * threw (sync or async). The adapter caught the throw and surfaced
1724
- * it as a `ValidationError` at the field path so the form's normal
1725
- * error pipeline handles it instead of leaking as an unhandled
1726
- * rejection or routing through `submitError`.
1727
- */
1728
- ValidatorThrew: "atta:validator-threw",
1729
- /**
1730
- * A function-form `defaultValues` factory threw or its promise
1731
- * rejected. The runtime captures the raw error on `form.hydrateError`
1732
- * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
1733
- * the standard error pipeline carries the signal. Critical for the
1734
- * SSR round-trip: `hydrateError` itself does not ride the wire
1735
- * payload, but `schemaErrors` does, so the client sees the failure
1736
- * after rehydration without an extra channel.
1737
- */
1738
- HydrationFailed: "atta:hydration-failed",
1739
- /** The supplied path didn't resolve to any node in the schema. */
1740
- PathNotFound: "atta:path-not-found",
1741
- /**
1742
- * A walked form's `activate()` (async `defaultValues` factory) threw
1743
- * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
1744
- * `ValidationError` at the form-level path (`[]`) so the wizard's
1745
- * aggregate error pipeline can carry the failure alongside ordinary
1746
- * validation errors. The raw factory error remains on
1747
- * `form.hydrateError` for retry UX.
1748
- */
1749
- ActivationFailed: "atta:activation-failed"
1750
- };
1751
1856
 
1752
1857
  const warnedNoScopeStores = paths.__DEV__ ? /* @__PURE__ */ new WeakSet() : null;
1753
1858
  function buildProcessForm(state, formInstanceId, options = {}) {
@@ -2006,6 +2111,33 @@ function applyInvalidSubmitPolicy(state, formInstanceId, policy) {
2006
2111
  target.element.focus({ preventScroll: true });
2007
2112
  }
2008
2113
 
2114
+ function captureUserCallSite() {
2115
+ const raw = new Error().stack;
2116
+ if (typeof raw !== "string") return void 0;
2117
+ const lines = raw.split("\n");
2118
+ for (let i = 1; i < lines.length; i++) {
2119
+ const frame = lines[i];
2120
+ if (frame === void 0) continue;
2121
+ if (/attaform[/-]forms?/i.test(frame)) continue;
2122
+ if (/\bforms\.[A-Za-z0-9_-]+\.m?js\b/.test(frame)) continue;
2123
+ const trimmed = frame.trim();
2124
+ if (trimmed.length === 0) continue;
2125
+ return shortenSourceFrame(trimmed);
2126
+ }
2127
+ return void 0;
2128
+ }
2129
+ function shortenSourceFrame(frame) {
2130
+ const match = /(?:^|\s|\()([^\s()]+):(\d+):\d+\)?$/.exec(frame);
2131
+ if (match === null) return frame;
2132
+ const [, urlOrPath, line] = match;
2133
+ if (urlOrPath === void 0 || line === void 0) return frame;
2134
+ let path = urlOrPath;
2135
+ path = path.replace(/^[a-z]+:\/\/[^/]+\//i, "");
2136
+ path = path.replace(/^_nuxt\//, "");
2137
+ path = path.replace(/^\//, "");
2138
+ return `(${path}:${line})`;
2139
+ }
2140
+
2009
2141
  function extractSchemaFields(schema) {
2010
2142
  try {
2011
2143
  const root = schema.getDefaultAtPath([]);
@@ -2064,9 +2196,9 @@ function isLeafValue(value) {
2064
2196
  return true;
2065
2197
  }
2066
2198
  function isSlimPrimitiveValid(schema, store, path, value) {
2067
- return walk$1(schema, store, path, value);
2199
+ return walk(schema, store, path, value);
2068
2200
  }
2069
- function walk$1(schema, store, path, value) {
2201
+ function walk(schema, store, path, value) {
2070
2202
  if (schema.isPreprocessOrCoerceLeaf(path)) return true;
2071
2203
  const accepted = schema.getSlimPrimitiveTypesAtPath(path);
2072
2204
  const kind = isLeafValue(value) ? slimKindOf(value) : Array.isArray(value) ? "array" : "object";
@@ -2076,13 +2208,13 @@ function walk$1(schema, store, path, value) {
2076
2208
  }
2077
2209
  if (Array.isArray(value)) {
2078
2210
  for (let i = 0; i < value.length; i++) {
2079
- if (!walk$1(schema, store, [...path, i], value[i])) return false;
2211
+ if (!walk(schema, store, [...path, i], value[i])) return false;
2080
2212
  }
2081
2213
  return true;
2082
2214
  }
2083
2215
  if (isPlainRecord(value)) {
2084
2216
  for (const key of Object.keys(value)) {
2085
- if (!walk$1(schema, store, [...path, key], value[key])) {
2217
+ if (!walk(schema, store, [...path, key], value[key])) {
2086
2218
  return false;
2087
2219
  }
2088
2220
  }
@@ -2370,7 +2502,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2370
2502
  throw new paths.AnonPersistError({
2371
2503
  cause: "register-without-config",
2372
2504
  schemaFields: extractSchemaFields(state.schema),
2373
- callSite: paths.captureUserCallSite()
2505
+ callSite: captureUserCallSite()
2374
2506
  });
2375
2507
  }
2376
2508
  const { aria } = computeFieldIdentity(formInstanceId, state.formKey, pathKey);
@@ -2482,49 +2614,60 @@ function walkUnsetSentinels(values, schema) {
2482
2614
  walkUnspecified(rootSlim, [], paths);
2483
2615
  return { cleanedValues: void 0, paths };
2484
2616
  }
2485
- const cleaned = walk(values, [], schema, paths);
2617
+ const cleaned = walkCore(values, [], schema, paths, true);
2486
2618
  return { cleanedValues: cleaned, paths };
2487
2619
  }
2488
- function walk(input, segments, schema, paths) {
2620
+ function isOpaqueLeaf(value) {
2621
+ return value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || typeof value === "function";
2622
+ }
2623
+ function walkCore(input, segments, schema, paths, synthesizeSchemaKeys) {
2489
2624
  if (isUnset(input)) {
2490
2625
  return expandUnsetAt(segments, schema, paths);
2491
2626
  }
2492
2627
  if (input === void 0) {
2493
- const slim = schema.getDefaultAtPath(segments);
2494
- return walkUnspecified(slim, segments, paths);
2495
- }
2496
- if (input === null) return null;
2497
- if (input instanceof Date || input instanceof RegExp || input instanceof Map || input instanceof Set || typeof input === "function") {
2628
+ if (synthesizeSchemaKeys) {
2629
+ const slim = schema.getDefaultAtPath(segments);
2630
+ return walkUnspecified(slim, segments, paths);
2631
+ }
2498
2632
  return input;
2499
2633
  }
2634
+ if (input === null) return null;
2635
+ if (isOpaqueLeaf(input)) return input;
2500
2636
  if (Array.isArray(input)) {
2501
2637
  const out = new Array(input.length);
2502
2638
  let mutated = false;
2503
2639
  for (let i = 0; i < input.length; i++) {
2504
- const walked = walk(input[i], [...segments, i], schema, paths);
2640
+ const walked = walkCore(input[i], [...segments, i], schema, paths, synthesizeSchemaKeys);
2505
2641
  out[i] = walked;
2506
2642
  if (walked !== input[i]) mutated = true;
2507
2643
  }
2508
2644
  return mutated ? out : input;
2509
2645
  }
2510
2646
  if (typeof input === "object") {
2511
- const slim = schema.getDefaultAtPath(segments);
2512
- const inputKeys = Object.keys(input);
2513
- const inputKeysSet = new Set(inputKeys);
2514
- const allKeys = new Set(inputKeys);
2515
- if (slim !== null && slim !== void 0 && typeof slim === "object" && !Array.isArray(slim) && !(slim instanceof Date) && !(slim instanceof RegExp) && !(slim instanceof Map) && !(slim instanceof Set)) {
2516
- for (const k of Object.keys(slim)) allKeys.add(k);
2647
+ const obj = input;
2648
+ const inputKeys = Object.keys(obj);
2649
+ let keys = inputKeys;
2650
+ let mutated = false;
2651
+ let inputKeysSet = null;
2652
+ if (synthesizeSchemaKeys) {
2653
+ inputKeysSet = new Set(inputKeys);
2654
+ const allKeys = new Set(inputKeys);
2655
+ const slim = schema.getDefaultAtPath(segments);
2656
+ if (slim !== null && slim !== void 0 && typeof slim === "object" && !Array.isArray(slim) && !isOpaqueLeaf(slim)) {
2657
+ for (const k of Object.keys(slim)) allKeys.add(k);
2658
+ }
2659
+ keys = allKeys;
2660
+ mutated = allKeys.size !== inputKeys.length;
2517
2661
  }
2518
2662
  const out = {};
2519
- let mutated = allKeys.size !== inputKeys.length;
2520
- for (const key of allKeys) {
2521
- const orig = input[key];
2522
- if (orig === void 0 && inputKeysSet.has(key)) {
2663
+ for (const key of keys) {
2664
+ const orig = obj[key];
2665
+ if (synthesizeSchemaKeys && orig === void 0 && inputKeysSet?.has(key) === true) {
2523
2666
  safeAssign(out, key, void 0);
2524
2667
  mutated = true;
2525
2668
  continue;
2526
2669
  }
2527
- const walked = walk(orig, [...segments, key], schema, paths);
2670
+ const walked = walkCore(orig, [...segments, key], schema, paths, synthesizeSchemaKeys);
2528
2671
  safeAssign(out, key, walked);
2529
2672
  if (walked !== orig) mutated = true;
2530
2673
  }
@@ -2539,7 +2682,7 @@ function walkUnspecified(slim, segments, paths$1) {
2539
2682
  }
2540
2683
  return slim;
2541
2684
  }
2542
- if (slim instanceof Date || slim instanceof RegExp || slim instanceof Map || slim instanceof Set || typeof slim === "function") {
2685
+ if (isOpaqueLeaf(slim)) {
2543
2686
  return slim;
2544
2687
  }
2545
2688
  if (Array.isArray(slim)) return slim;
@@ -2558,40 +2701,9 @@ function walkUnspecified(slim, segments, paths$1) {
2558
2701
  }
2559
2702
  function substituteUnsetSentinels(value, prefix, schema) {
2560
2703
  const paths = [];
2561
- const cleaned = substitute(value, [...prefix], schema, paths);
2704
+ const cleaned = walkCore(value, [...prefix], schema, paths, false);
2562
2705
  return { cleanedValues: cleaned, paths };
2563
2706
  }
2564
- function substitute(input, segments, schema, paths) {
2565
- if (isUnset(input)) {
2566
- return expandUnsetAt(segments, schema, paths);
2567
- }
2568
- if (input === void 0 || input === null) return input;
2569
- if (input instanceof Date || input instanceof RegExp || input instanceof Map || input instanceof Set || typeof input === "function") {
2570
- return input;
2571
- }
2572
- if (Array.isArray(input)) {
2573
- let mutated = false;
2574
- const out = new Array(input.length);
2575
- for (let i = 0; i < input.length; i++) {
2576
- const walked = substitute(input[i], [...segments, i], schema, paths);
2577
- out[i] = walked;
2578
- if (walked !== input[i]) mutated = true;
2579
- }
2580
- return mutated ? out : input;
2581
- }
2582
- if (typeof input === "object") {
2583
- let mutated = false;
2584
- const out = {};
2585
- for (const key of Object.keys(input)) {
2586
- const orig = input[key];
2587
- const walked = substitute(orig, [...segments, key], schema, paths);
2588
- safeAssign(out, key, walked);
2589
- if (walked !== orig) mutated = true;
2590
- }
2591
- return mutated ? out : input;
2592
- }
2593
- return input;
2594
- }
2595
2707
  function isPrimitiveOrEmpty(value) {
2596
2708
  if (value === null || value === void 0) return true;
2597
2709
  const t = typeof value;
@@ -2621,7 +2733,7 @@ function expandUnsetAt(segments, schema, paths$1) {
2621
2733
  paths$1.push(paths.canonicalizePath(segments).key);
2622
2734
  return slim;
2623
2735
  }
2624
- if (slim instanceof Date || slim instanceof RegExp || slim instanceof Map || slim instanceof Set || typeof slim === "function") {
2736
+ if (isOpaqueLeaf(slim)) {
2625
2737
  paths$1.push(paths.canonicalizePath(segments).key);
2626
2738
  return slim;
2627
2739
  }
@@ -2755,20 +2867,128 @@ function buildFormApi(state, formInstanceId, options = {}) {
2755
2867
  if (instanceMeta === void 0) return meta;
2756
2868
  return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
2757
2869
  };
2870
+ const reMarkBlanksAfterSubstitution = (paths$1) => {
2871
+ for (const pathKey of paths$1) {
2872
+ const blankSegments = paths.segmentsForPathKey(pathKey);
2873
+ if (blankSegments === null) continue;
2874
+ state.setValueAtPath(
2875
+ blankSegments,
2876
+ state.getValueAtPath(blankSegments),
2877
+ withInstanceMeta({ blank: true })
2878
+ );
2879
+ }
2880
+ };
2758
2881
  const getFormMetaBase = () => {
2759
- const { base: rootBase } = buildContainerFieldStateBase(
2882
+ let rollup;
2883
+ const rootBase = () => rollup ?? (rollup = buildContainerFieldStateBase(
2760
2884
  state,
2761
2885
  paths.ROOT_PATH,
2762
2886
  paths.ROOT_PATH_KEY,
2763
2887
  formInstanceId
2764
- );
2888
+ ).base);
2765
2889
  return {
2766
- ...rootBase,
2890
+ // Rollup-derived (FieldStateBase) — the whole rollup builds once, on the
2891
+ // first access of any of these.
2892
+ get value() {
2893
+ return rootBase().value;
2894
+ },
2895
+ get original() {
2896
+ return rootBase().original;
2897
+ },
2898
+ get pristine() {
2899
+ return rootBase().pristine;
2900
+ },
2901
+ get dirty() {
2902
+ return rootBase().dirty;
2903
+ },
2904
+ get focused() {
2905
+ return rootBase().focused;
2906
+ },
2907
+ get blurred() {
2908
+ return rootBase().blurred;
2909
+ },
2910
+ get touched() {
2911
+ return rootBase().touched;
2912
+ },
2913
+ get interacted() {
2914
+ return rootBase().interacted;
2915
+ },
2916
+ get blurredAfterInteraction() {
2917
+ return rootBase().blurredAfterInteraction;
2918
+ },
2919
+ get connected() {
2920
+ return rootBase().connected;
2921
+ },
2922
+ get element() {
2923
+ return rootBase().element;
2924
+ },
2925
+ get elements() {
2926
+ return rootBase().elements;
2927
+ },
2928
+ get updatedAt() {
2929
+ return rootBase().updatedAt;
2930
+ },
2931
+ get errors() {
2932
+ return rootBase().errors;
2933
+ },
2934
+ get validating() {
2935
+ return rootBase().validating;
2936
+ },
2937
+ get valid() {
2938
+ return rootBase().valid;
2939
+ },
2940
+ get transforming() {
2941
+ return rootBase().transforming;
2942
+ },
2943
+ get busy() {
2944
+ return rootBase().busy;
2945
+ },
2946
+ get transformError() {
2947
+ return rootBase().transformError;
2948
+ },
2949
+ get path() {
2950
+ return rootBase().path;
2951
+ },
2952
+ get id() {
2953
+ return rootBase().id;
2954
+ },
2955
+ get aria() {
2956
+ return rootBase().aria;
2957
+ },
2958
+ get key() {
2959
+ return rootBase().key;
2960
+ },
2961
+ get blank() {
2962
+ return rootBase().blank;
2963
+ },
2964
+ get label() {
2965
+ return rootBase().label;
2966
+ },
2967
+ get description() {
2968
+ return rootBase().description;
2969
+ },
2970
+ get placeholder() {
2971
+ return rootBase().placeholder;
2972
+ },
2973
+ get meta() {
2974
+ return rootBase().meta;
2975
+ },
2976
+ get errorCount() {
2977
+ return rootBase().errors.length;
2978
+ },
2979
+ // Form-level scalars — EAGER reads, tracked on every field-state eval.
2980
+ // They are O(1) refs that never change on a keystroke, so tracking them
2981
+ // per field costs nothing on the hot path. Kept eager (NOT lazy like the
2982
+ // rollup) because behaviors beyond the predicate's own output depend on
2983
+ // every field re-evaluating when they flip — most notably, the display
2984
+ // engine is cleared on submit (revealing held spinners), and that
2985
+ // imperative reset only becomes visible if `submitting` is a tracked dep
2986
+ // of each field. Matches the pre-bust dependency set for these scalars
2987
+ // exactly.
2767
2988
  submitting: state.submitting.value,
2768
2989
  submissionAttempts: state.submissionAttempts.value,
2769
2990
  departAttempts: state.departAttempts.value,
2770
2991
  submitError: state.submitError.value,
2771
- errorCount: rootBase.errors.length,
2772
2992
  submitted: state.submitted.value,
2773
2993
  instanceId: formInstanceId
2774
2994
  };
@@ -2803,24 +3023,21 @@ function buildFormApi(state, formInstanceId, options = {}) {
2803
3023
  const segments = paths.canonicalizePath(pathInput).segments;
2804
3024
  return vue.computed(() => getAtPath(state.form.value, segments));
2805
3025
  }
2806
- function setValueImpl(pathOrValue, maybeValue) {
2807
- if (arguments.length === 1) {
3026
+ function setValueImpl(pathOrValue, maybeValue, maybeOptions) {
3027
+ const argc = arguments.length;
3028
+ const isPathForm = argc >= 2 && (typeof pathOrValue === "string" || Array.isArray(pathOrValue));
3029
+ const options2 = isPathForm ? maybeOptions : argc >= 2 ? maybeValue : void 0;
3030
+ const silent = options2?.silent === true;
3031
+ const writeMeta = (extra) => withInstanceMeta(silent ? { ...extra, silent: true } : extra);
3032
+ if (!isPathForm) {
2808
3033
  const next = typeof pathOrValue === "function" ? pathOrValue(structuralSnapshot(state.form.value)) : pathOrValue;
2809
3034
  const walked2 = walkUnsetSentinels(
2810
3035
  next,
2811
3036
  state.schema
2812
3037
  );
2813
- const ok2 = state.setValueAtPath([], walked2.cleanedValues, withInstanceMeta());
3038
+ const ok2 = state.setValueAtPath([], walked2.cleanedValues, writeMeta());
2814
3039
  if (!ok2) return false;
2815
- for (const pathKey of walked2.paths) {
2816
- const blankSegments = paths.segmentsForPathKey(pathKey);
2817
- if (blankSegments === null) continue;
2818
- state.setValueAtPath(
2819
- blankSegments,
2820
- state.getValueAtPath(blankSegments),
2821
- withInstanceMeta({ blank: true })
2822
- );
2823
- }
3040
+ reMarkBlanksAfterSubstitution(walked2.paths);
2824
3041
  return true;
2825
3042
  }
2826
3043
  const segments = paths.canonicalizePath(pathOrValue).segments;
@@ -2832,7 +3049,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2832
3049
  if (parentDU?.discriminatorKey === last) {
2833
3050
  const slimDefault = state.schema.getEmptyValueAtPath(segments);
2834
3051
  const blank = blankForKind(slimDefault);
2835
- return state.setValueAtPath(segments, blank, withInstanceMeta({ blank: true }));
3052
+ return state.setValueAtPath(segments, blank, writeMeta({ blank: true }));
2836
3053
  }
2837
3054
  }
2838
3055
  const blankPaths = [];
@@ -2843,9 +3060,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
2843
3060
  );
2844
3061
  const segmentsKey = paths.canonicalizePath(segments).key;
2845
3062
  if (blankPaths.length === 1 && blankPaths[0] === segmentsKey) {
2846
- return state.setValueAtPath(segments, expanded, withInstanceMeta({ blank: true }));
3063
+ return state.setValueAtPath(segments, expanded, writeMeta({ blank: true }));
2847
3064
  }
2848
- const ok2 = state.setValueAtPath(segments, expanded, withInstanceMeta());
3065
+ const ok2 = state.setValueAtPath(segments, expanded, writeMeta());
2849
3066
  if (!ok2) return false;
2850
3067
  for (const pathKey of blankPaths) {
2851
3068
  const blankSegments = paths.segmentsForPathKey(pathKey);
@@ -2853,7 +3070,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2853
3070
  state.setValueAtPath(
2854
3071
  blankSegments,
2855
3072
  state.getValueAtPath(blankSegments),
2856
- withInstanceMeta({ blank: true })
3073
+ writeMeta({ blank: true })
2857
3074
  );
2858
3075
  }
2859
3076
  return true;
@@ -2873,17 +3090,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
2873
3090
  segments,
2874
3091
  state.schema
2875
3092
  );
2876
- const ok = state.setValueAtPath(segments, walked.cleanedValues, withInstanceMeta());
3093
+ const ok = state.setValueAtPath(segments, walked.cleanedValues, writeMeta());
2877
3094
  if (!ok) return false;
2878
- for (const pathKey of walked.paths) {
2879
- const blankSegments = paths.segmentsForPathKey(pathKey);
2880
- if (blankSegments === null) continue;
2881
- state.setValueAtPath(
2882
- blankSegments,
2883
- state.getValueAtPath(blankSegments),
2884
- withInstanceMeta({ blank: true })
2885
- );
2886
- }
3095
+ reMarkBlanksAfterSubstitution(walked.paths);
2887
3096
  return true;
2888
3097
  }
2889
3098
  const errorsProxy = buildErrorsProxy(state);
@@ -3252,7 +3461,19 @@ function buildFormApi(state, formInstanceId, options = {}) {
3252
3461
  }
3253
3462
  return Object.freeze(out);
3254
3463
  }
3255
- return {
3464
+ const formHandle = {
3465
+ current: void 0
3466
+ };
3467
+ function onChangeImpl(a, b, c) {
3468
+ const sourced = typeof b === "function";
3469
+ const source = sourced ? a : void 0;
3470
+ const handler = sourced ? b : a;
3471
+ const options2 = sourced ? c : b;
3472
+ const stop = state.registerOnChange(source, handler, options2, () => formHandle.current);
3473
+ if (vue.getCurrentScope() !== void 0) vue.onScopeDispose(stop);
3474
+ return stop;
3475
+ }
3476
+ const api = {
3256
3477
  handleSubmit: gated(handleSubmit),
3257
3478
  // Callable readonly Proxies (`values`, `fields`, `errors`) and the
3258
3479
  // reactive containers (`meta`, `history`, `blankPaths`) are exposed
@@ -3339,8 +3560,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
3339
3560
  get blankPaths() {
3340
3561
  void state.activate();
3341
3562
  return blankPathsView;
3342
- }
3563
+ },
3564
+ onChange: onChangeImpl
3343
3565
  };
3566
+ formHandle.current = api;
3567
+ return api;
3344
3568
  }
3345
3569
 
3346
3570
  const IDLE = Object.freeze({ display: "idle" });
@@ -3767,6 +3991,7 @@ function createArrayBookkeeping(deps) {
3767
3991
  originals,
3768
3992
  blankPaths,
3769
3993
  originalBlankPaths,
3994
+ authoredPaths,
3770
3995
  fieldValidationCounts,
3771
3996
  fieldValidatingSince,
3772
3997
  fieldValidationState,
@@ -3794,6 +4019,7 @@ function createArrayBookkeeping(deps) {
3794
4019
  }));
3795
4020
  migrateSetSubtree(blankPaths, arrayPath, remap);
3796
4021
  migrateSetSubtree(originalBlankPaths, arrayPath, remap);
4022
+ migrateSetSubtree(authoredPaths, arrayPath, remap);
3797
4023
  migrateMapSubtree(fieldValidatingSince, arrayPath, remap, (since) => since);
3798
4024
  migrateMapSubtree(fieldValidationCounts, arrayPath, remap, (count) => count);
3799
4025
  arrayIdentity.applyRemap(arrayPath, remap);
@@ -3837,7 +4063,7 @@ function createArrayBookkeeping(deps) {
3837
4063
  activeValidations.value = Math.max(0, activeValidations.value - 1);
3838
4064
  decFieldValidation(key);
3839
4065
  }
3840
- entry.controller.abort();
4066
+ entry.aborted = true;
3841
4067
  fieldValidationState.delete(key);
3842
4068
  }
3843
4069
  }
@@ -3849,6 +4075,170 @@ function createArrayBookkeeping(deps) {
3849
4075
  };
3850
4076
  }
3851
4077
 
4078
+ const NOOP_STOP = () => {
4079
+ };
4080
+ function isThenable(value) {
4081
+ return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
4082
+ }
4083
+ function isSuppressed(meta) {
4084
+ return meta?.hydration === true || meta?.crossTab === true || meta?.silent === true;
4085
+ }
4086
+ function canonicalizeSourceList(raw) {
4087
+ const list = typeof raw === "string" ? [raw] : raw;
4088
+ const out = [];
4089
+ const seen = /* @__PURE__ */ new Set();
4090
+ for (const entry of list) {
4091
+ const { segments, key } = paths.canonicalizePath(entry);
4092
+ if (seen.has(key)) continue;
4093
+ seen.add(key);
4094
+ out.push({ segments, key, dotted: paths.segmentsToDotted(segments) });
4095
+ }
4096
+ return out;
4097
+ }
4098
+ function makeResolver(source) {
4099
+ if (source === void 0) {
4100
+ const root = [{ segments: paths.ROOT_PATH, key: paths.ROOT_PATH_KEY, dotted: "" }];
4101
+ return () => root;
4102
+ }
4103
+ if (typeof source !== "function" && !vue.isRef(source)) {
4104
+ const fixed = canonicalizeSourceList(source);
4105
+ return () => fixed;
4106
+ }
4107
+ return () => canonicalizeSourceList(vue.toValue(source));
4108
+ }
4109
+ function createOnChangeRegistry(deps) {
4110
+ const handlers = /* @__PURE__ */ new Set();
4111
+ function isCurrent(reg, key, token) {
4112
+ return reg.runs.get(key)?.token === token;
4113
+ }
4114
+ function routeError(reg, error, source, changed, value, previous, attempt, token) {
4115
+ if (reg.onError === void 0) {
4116
+ if (paths.__DEV__) {
4117
+ console.error(
4118
+ `[attaform] onChange handler threw for path '${source.dotted}' \u2014 error swallowed. Pass { onError } to handle it. Original error:`,
4119
+ error
4120
+ );
4121
+ }
4122
+ return;
4123
+ }
4124
+ const retry = () => {
4125
+ if (!isCurrent(reg, source.key, token)) return;
4126
+ runHandler(reg, source, changed, value, previous, attempt + 1);
4127
+ };
4128
+ const errCtx = {
4129
+ path: source.dotted,
4130
+ value,
4131
+ attempt,
4132
+ retry,
4133
+ form: reg.getForm()
4134
+ };
4135
+ try {
4136
+ reg.onError(error, errCtx);
4137
+ } catch (err) {
4138
+ if (paths.__DEV__) console.error("[attaform] onChange onError threw:", err);
4139
+ }
4140
+ }
4141
+ function runHandler(reg, source, changed, value, previous, attempt) {
4142
+ const prior = reg.runs.get(source.key);
4143
+ if (prior) prior.controller.abort();
4144
+ const controller = new AbortController();
4145
+ const token = ++reg.seq;
4146
+ reg.runs.set(source.key, { token, controller });
4147
+ const ctx = {
4148
+ path: source.dotted,
4149
+ previous,
4150
+ signal: controller.signal,
4151
+ attempt,
4152
+ form: reg.getForm(),
4153
+ changed
4154
+ };
4155
+ let result;
4156
+ try {
4157
+ result = reg.handler(value, ctx);
4158
+ } catch (error) {
4159
+ routeError(reg, error, source, changed, value, previous, attempt, token);
4160
+ return;
4161
+ }
4162
+ if (isThenable(result)) {
4163
+ result.then(void 0, (error) => {
4164
+ if (isCurrent(reg, source.key, token)) {
4165
+ routeError(reg, error, source, changed, value, previous, attempt, token);
4166
+ }
4167
+ });
4168
+ }
4169
+ }
4170
+ function dispatch(patches, meta) {
4171
+ if (handlers.size === 0 || isSuppressed(meta)) return;
4172
+ for (const reg of handlers) {
4173
+ let sources;
4174
+ try {
4175
+ sources = reg.resolve();
4176
+ } catch (error) {
4177
+ if (paths.__DEV__) console.error("[attaform] onChange source getter threw:", error);
4178
+ continue;
4179
+ }
4180
+ for (const source of sources) {
4181
+ let changed;
4182
+ for (const patch of patches) {
4183
+ if (paths.isPathPrefix(source.segments, patch.path) || paths.isPathPrefix(patch.path, source.segments)) {
4184
+ (changed ?? (changed = [])).push(paths.segmentsToDotted(patch.path));
4185
+ }
4186
+ }
4187
+ if (changed === void 0) continue;
4188
+ const value = deps.getValueAtPath(source.segments);
4189
+ const previous = reg.previous.has(source.key) ? reg.previous.get(source.key) : value;
4190
+ reg.previous.set(source.key, value);
4191
+ runHandler(reg, source, changed, value, previous, 0);
4192
+ }
4193
+ }
4194
+ }
4195
+ function register(source, handler, options, getForm) {
4196
+ if (deps.ssr) return NOOP_STOP;
4197
+ const reg = {
4198
+ handler,
4199
+ onError: options?.onError,
4200
+ getForm,
4201
+ resolve: makeResolver(source),
4202
+ previous: /* @__PURE__ */ new Map(),
4203
+ runs: /* @__PURE__ */ new Map(),
4204
+ seq: 0
4205
+ };
4206
+ try {
4207
+ for (const { key, segments } of reg.resolve()) {
4208
+ reg.previous.set(key, deps.getValueAtPath(segments));
4209
+ }
4210
+ } catch (error) {
4211
+ if (paths.__DEV__) console.error("[attaform] onChange source getter threw at registration:", error);
4212
+ }
4213
+ handlers.add(reg);
4214
+ let stopped = false;
4215
+ return () => {
4216
+ if (stopped) return;
4217
+ stopped = true;
4218
+ handlers.delete(reg);
4219
+ for (const run of reg.runs.values()) run.controller.abort();
4220
+ reg.runs.clear();
4221
+ reg.previous.clear();
4222
+ };
4223
+ }
4224
+ function dispose() {
4225
+ for (const reg of handlers) {
4226
+ for (const run of reg.runs.values()) run.controller.abort();
4227
+ reg.runs.clear();
4228
+ reg.previous.clear();
4229
+ }
4230
+ handlers.clear();
4231
+ }
4232
+ return {
4233
+ get active() {
4234
+ return handlers.size > 0;
4235
+ },
4236
+ register,
4237
+ dispatch,
4238
+ dispose
4239
+ };
4240
+ }
4241
+
3852
4242
  function isHydratedFieldRecord(value) {
3853
4243
  if (typeof value !== "object" || value === null) return false;
3854
4244
  const r = value;
@@ -3933,6 +4323,17 @@ function walkAuthoredFromConstraints(value, prefix, out) {
3933
4323
  }
3934
4324
  }
3935
4325
  }
4326
+ function freshElementIndices(op) {
4327
+ switch (op.kind) {
4328
+ case "insert":
4329
+ case "replace-at":
4330
+ return [op.index];
4331
+ case "remove":
4332
+ case "swap":
4333
+ case "move":
4334
+ return [];
4335
+ }
4336
+ }
3936
4337
  function walkAuthoredFromSchemaDiff(withDefaults, withoutDefaults, prefix, out) {
3937
4338
  if (isPlainRecord(withDefaults) && isPlainRecord(withoutDefaults)) {
3938
4339
  const left = withDefaults;
@@ -3971,6 +4372,7 @@ function createFormStore(options) {
3971
4372
  const formChangeListeners = /* @__PURE__ */ new Set();
3972
4373
  const submitSuccessListeners = /* @__PURE__ */ new Set();
3973
4374
  const resetListeners = /* @__PURE__ */ new Set();
4375
+ const onChangeRegistry = createOnChangeRegistry({ getValueAtPath, ssr });
3974
4376
  const persistOptIns = paths.createPersistOptInRegistry();
3975
4377
  const noSyncPaths = /* @__PURE__ */ new Set();
3976
4378
  const noSyncPathCounts = /* @__PURE__ */ new Map();
@@ -4009,11 +4411,8 @@ function createFormStore(options) {
4009
4411
  if (constraints !== void 0) {
4010
4412
  walkAuthoredFromConstraints(constraints, [], authoredPaths);
4011
4413
  }
4012
- const slimResponse = schema.getDefaultValues({
4013
- useDefaultSchemaValues: false,
4014
- strict
4015
- });
4016
- walkAuthoredFromSchemaDiff(schemaWithDefaultsData, slimResponse.data, [], authoredPaths);
4414
+ const slimBaseline = schema.getEmptyValueAtPath([]);
4415
+ walkAuthoredFromSchemaDiff(schemaWithDefaultsData, slimBaseline, [], authoredPaths);
4017
4416
  }
4018
4417
  rebuildAuthoredPaths(defaultValues, schemaInitialData);
4019
4418
  function filterAuthoredErrors(errors) {
@@ -4032,7 +4431,7 @@ function createFormStore(options) {
4032
4431
  });
4033
4432
  const form = vue.ref(stubbedInitialData);
4034
4433
  const arrayIdentity = createArrayIdentity((arraySegs) => {
4035
- const v = getAtPath(form.value, arraySegs);
4434
+ const v = getAtPath(vue.toRaw(form.value), arraySegs);
4036
4435
  return Array.isArray(v) ? v.length : 0;
4037
4436
  });
4038
4437
  function arrayElementKey(path) {
@@ -4075,14 +4474,7 @@ function createFormStore(options) {
4075
4474
  const segments = paths.segmentsForPathKey(pathKey);
4076
4475
  if (segments === null) continue;
4077
4476
  if (!schema.isRequiredAtPath(segments)) continue;
4078
- result.set(pathKey, [
4079
- {
4080
- message: "No value supplied",
4081
- path: [...segments],
4082
- formKey,
4083
- code: AttaformErrorCode.NoValueSupplied
4084
- }
4085
- ]);
4477
+ result.set(pathKey, [makeBlankRequiredError(segments, formKey)]);
4086
4478
  }
4087
4479
  return result;
4088
4480
  });
@@ -4322,6 +4714,7 @@ function createFormStore(options) {
4322
4714
  originals,
4323
4715
  blankPaths,
4324
4716
  originalBlankPaths,
4717
+ authoredPaths,
4325
4718
  fieldValidationCounts,
4326
4719
  fieldValidatingSince,
4327
4720
  fieldValidationState,
@@ -4331,17 +4724,8 @@ function createFormStore(options) {
4331
4724
  touchFieldRecord,
4332
4725
  decFieldValidation
4333
4726
  });
4334
- function applyFormReplacement(next, meta) {
4335
- const prev = form.value;
4336
- if (Object.is(prev, next)) return;
4727
+ function commitWritePatches(patches, meta) {
4337
4728
  const now = (/* @__PURE__ */ new Date()).toISOString();
4338
- const patches = [];
4339
- diffAndApply(prev, next, [], (patch) => {
4340
- patches.push(patch);
4341
- });
4342
- if (!applyChangedKeys(prev, next)) {
4343
- form.value = next;
4344
- }
4345
4729
  for (const patch of patches) {
4346
4730
  const { key } = paths.canonicalizePath(patch.path);
4347
4731
  if (patch.kind === "added" && !originals.has(key)) {
@@ -4356,10 +4740,63 @@ function createFormStore(options) {
4356
4740
  console.error("[attaform] onFormChange threw:", err);
4357
4741
  }
4358
4742
  }
4743
+ if (onChangeRegistry.active) onChangeRegistry.dispatch(patches, meta);
4744
+ }
4745
+ function applyFormReplacementWithPath(next, meta, arrayOpPath) {
4746
+ const prev = form.value;
4747
+ if (Object.is(prev, next)) return;
4748
+ const patches = [];
4749
+ diffAndApply(prev, next, [], (patch) => {
4750
+ patches.push(patch);
4751
+ });
4752
+ if (!applyChangedKeys(prev, next, arrayOpPath, [])) {
4753
+ form.value = next;
4754
+ } else if (patches.some(
4755
+ (p) => p.path.length > 0 && typeof p.path[0] === "string" && isShadowedKey(p.path[0])
4756
+ )) {
4757
+ vue.triggerRef(form);
4758
+ }
4759
+ commitWritePatches(patches, meta);
4760
+ }
4761
+ function applyFormReplacement(next, meta) {
4762
+ applyFormReplacementWithPath(next, meta, null);
4763
+ }
4764
+ function applyTargetedWrite(path, completedValue, meta) {
4765
+ const result = tryInPlaceLeafWrite(form.value, path, completedValue);
4766
+ if (!result.applied) {
4767
+ applyFormReplacementWithPath(
4768
+ setAtPathWithSchemaFill(form.value, schema, path, completedValue),
4769
+ meta,
4770
+ meta?.arrayOp !== void 0 ? path : null
4771
+ );
4772
+ return;
4773
+ }
4774
+ const patches = [];
4775
+ diffAndApply(result.old, completedValue, path, (patch) => {
4776
+ patches.push(patch);
4777
+ });
4778
+ commitWritePatches(patches, meta);
4359
4779
  }
4360
4780
  function setValueAtPath(path, value, meta) {
4361
- value = stripSymbolsDeep(value);
4362
- if (!isSlimPrimitiveValid(schema, form, path, value)) {
4781
+ if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4782
+ for (const idx of freshElementIndices(meta.arrayOp)) {
4783
+ value[idx] = stripSymbolsDeep(value[idx]);
4784
+ }
4785
+ } else {
4786
+ value = stripSymbolsDeep(value);
4787
+ }
4788
+ let slimOk = true;
4789
+ if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4790
+ for (const idx of freshElementIndices(meta.arrayOp)) {
4791
+ if (!isSlimPrimitiveValid(schema, form, [...path, idx], value[idx])) {
4792
+ slimOk = false;
4793
+ break;
4794
+ }
4795
+ }
4796
+ } else {
4797
+ slimOk = isSlimPrimitiveValid(schema, form, path, value);
4798
+ }
4799
+ if (!slimOk) {
4363
4800
  return false;
4364
4801
  }
4365
4802
  if (path.length >= 2) {
@@ -4452,22 +4889,37 @@ function createFormStore(options) {
4452
4889
  }
4453
4890
  }
4454
4891
  }
4892
+ const currentValue = getAtPath(form.value, path);
4455
4893
  const pathKey = paths.canonicalizePath(path).key;
4456
4894
  if (meta?.blank === true) {
4457
4895
  blankPaths.add(pathKey);
4458
4896
  } else {
4459
4897
  if (blankPaths.has(pathKey)) blankPaths.delete(pathKey);
4460
- if (meta?.arrayOp === void 0) {
4898
+ if (meta?.arrayOp === void 0 && (isPlainRecord(currentValue) || Array.isArray(currentValue))) {
4461
4899
  for (const existingKey of [...blankPaths]) {
4462
4900
  if (isPathKeyUnder(existingKey, path)) blankPaths.delete(existingKey);
4463
4901
  }
4464
4902
  }
4465
4903
  }
4466
4904
  const wasAuthoredBefore = authoredPaths.has(pathKey);
4467
- walkAuthoredFromConstraints(value, path, authoredPaths);
4905
+ if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4906
+ if (path.length > 0) authoredPaths.add(pathKey);
4907
+ for (const idx of freshElementIndices(meta.arrayOp)) {
4908
+ walkAuthoredFromConstraints(value[idx], [...path, idx], authoredPaths);
4909
+ }
4910
+ } else {
4911
+ walkAuthoredFromConstraints(value, path, authoredPaths);
4912
+ }
4468
4913
  const newlyAuthored = !wasAuthoredBefore && authoredPaths.has(pathKey);
4469
- const completedValue = mergeStructural(schema, path, value);
4470
- const currentValue = getAtPath(form.value, path);
4914
+ let completedValue;
4915
+ if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4916
+ for (const idx of freshElementIndices(meta.arrayOp)) {
4917
+ value[idx] = mergeStructural(schema, [...path, idx], value[idx]);
4918
+ }
4919
+ completedValue = value;
4920
+ } else {
4921
+ completedValue = mergeStructural(schema, path, value);
4922
+ }
4471
4923
  if (Object.is(currentValue, completedValue)) {
4472
4924
  if (newlyAuthored && schema.isPreprocessOrCoerceLeaf(path)) {
4473
4925
  const modeForAuthoringTransition = meta?.instance?.validateOn ?? fieldValidationMode;
@@ -4481,8 +4933,7 @@ function createFormStore(options) {
4481
4933
  return true;
4482
4934
  }
4483
4935
  const oldArrayLength = Array.isArray(currentValue) ? currentValue.length : 0;
4484
- const nextForm = setAtPathWithSchemaFill(form.value, schema, path, completedValue);
4485
- applyFormReplacement(nextForm, meta);
4936
+ applyTargetedWrite(path, completedValue, meta);
4486
4937
  if (meta?.arrayOp !== void 0) {
4487
4938
  const remap = remapForOp(meta.arrayOp, oldArrayLength);
4488
4939
  arrayBookkeeping.migrateElementState(path, remap);
@@ -4565,7 +5016,7 @@ function createFormStore(options) {
4565
5016
  const prevValidation = fieldValidationState.get(parentKey2);
4566
5017
  if (prevValidation !== void 0) {
4567
5018
  if (prevValidation.timer !== null) clearTimeout(prevValidation.timer);
4568
- prevValidation.controller.abort();
5019
+ prevValidation.aborted = true;
4569
5020
  fieldValidationState.delete(parentKey2);
4570
5021
  }
4571
5022
  appliedSync = true;
@@ -4589,15 +5040,19 @@ function createFormStore(options) {
4589
5040
  const prev = fieldValidationState.get(key);
4590
5041
  if (prev !== void 0) {
4591
5042
  if (prev.timer !== null) clearTimeout(prev.timer);
4592
- prev.controller.abort();
5043
+ prev.aborted = true;
4593
5044
  }
4594
- const controller = new AbortController();
4595
- const fresh = { controller, timer: null, settled: false, released: false };
5045
+ const fresh = {
5046
+ aborted: false,
5047
+ timer: null,
5048
+ settled: false,
5049
+ released: false
5050
+ };
4596
5051
  fieldValidationState.set(key, fresh);
4597
5052
  const myEpoch = ++scheduleEpoch;
4598
5053
  const run = () => {
4599
5054
  fresh.timer = null;
4600
- if (controller.signal.aborted) return;
5055
+ if (fresh.aborted) return;
4601
5056
  let activeIncremented = false;
4602
5057
  try {
4603
5058
  activeValidations.value += 1;
@@ -4614,7 +5069,7 @@ function createFormStore(options) {
4614
5069
  const dataAtScope = subtreeScope ? getAtPath(form.value, path) : form.value;
4615
5070
  const scopeKey = subtreeScope ? paths.canonicalizePath(path).key : paths.ROOT_PATH_KEY;
4616
5071
  void Promise.resolve().then(() => schema.validateAtPath(dataAtScope, scopePath)).then((response) => {
4617
- if (controller.signal.aborted) return;
5072
+ if (fresh.aborted) return;
4618
5073
  if (myEpoch <= lastCommittedEpoch) return;
4619
5074
  lastCommittedEpoch = myEpoch;
4620
5075
  if (effectiveMode === "blur") {
@@ -4651,7 +5106,7 @@ function createFormStore(options) {
4651
5106
  activeValidations.value = Math.max(0, activeValidations.value - 1);
4652
5107
  decFieldValidation(pkey);
4653
5108
  }
4654
- entry.controller.abort();
5109
+ entry.aborted = true;
4655
5110
  }
4656
5111
  fieldValidationState.clear();
4657
5112
  }
@@ -4667,7 +5122,7 @@ function createFormStore(options) {
4667
5122
  decFieldValidation(key);
4668
5123
  entry.released = true;
4669
5124
  }
4670
- entry.controller.abort();
5125
+ entry.aborted = true;
4671
5126
  fieldValidationState.delete(key);
4672
5127
  }
4673
5128
  }
@@ -4726,6 +5181,7 @@ function createFormStore(options) {
4726
5181
  formChangeListeners.clear();
4727
5182
  submitSuccessListeners.clear();
4728
5183
  resetListeners.clear();
5184
+ onChangeRegistry.dispose();
4729
5185
  persistOptIns.clear();
4730
5186
  noSyncPaths.clear();
4731
5187
  noSyncPathCounts.clear();
@@ -5031,7 +5487,7 @@ function createFormStore(options) {
5031
5487
  });
5032
5488
  const next = resetResponse.data;
5033
5489
  rebuildAuthoredPaths(resetSource, next);
5034
- applyFormReplacement(next);
5490
+ applyFormReplacement(next, { silent: true });
5035
5491
  arrayIdentity.rebaselineAll();
5036
5492
  originals.clear();
5037
5493
  diffAndApply({}, next, [], (patch) => {
@@ -5277,6 +5733,7 @@ function createFormStore(options) {
5277
5733
  settleTransforms,
5278
5734
  scheduleFieldValidation,
5279
5735
  onFormChange,
5736
+ registerOnChange: onChangeRegistry.register,
5280
5737
  onSubmitSuccess,
5281
5738
  onReset,
5282
5739
  emitSubmitSuccess,
@@ -5306,6 +5763,13 @@ function captureErrorEntries(map) {
5306
5763
  for (const [k, v] of map) out.push([k, [...v]]);
5307
5764
  return out;
5308
5765
  }
5766
+ function errorFieldsEqual(av, bvi) {
5767
+ if (av === bvi) return true;
5768
+ if (av.message !== bvi.message) return false;
5769
+ if (av.code !== bvi.code) return false;
5770
+ if (av.formKey !== bvi.formKey) return false;
5771
+ return av.path === bvi.path || paths.pathsEqual(av.path, bvi.path);
5772
+ }
5309
5773
  function errorsEqual(a, b) {
5310
5774
  if (a.length !== b.length) return false;
5311
5775
  const bMap = /* @__PURE__ */ new Map();
@@ -5315,18 +5779,7 @@ function errorsEqual(a, b) {
5315
5779
  if (bv === void 0) return false;
5316
5780
  if (v.length !== bv.length) return false;
5317
5781
  for (let i = 0; i < v.length; i++) {
5318
- const av = v[i];
5319
- const bvi = bv[i];
5320
- if (av === bvi) continue;
5321
- if (av.message !== bvi.message) return false;
5322
- if (av.code !== bvi.code) return false;
5323
- if (av.formKey !== bvi.formKey) return false;
5324
- if (av.path !== bvi.path) {
5325
- if (av.path.length !== bvi.path.length) return false;
5326
- for (let j = 0; j < av.path.length; j++) {
5327
- if (av.path[j] !== bvi.path[j]) return false;
5328
- }
5329
- }
5782
+ if (!errorFieldsEqual(v[i], bv[i])) return false;
5330
5783
  }
5331
5784
  }
5332
5785
  return true;
@@ -5540,7 +5993,7 @@ function useAbstractForm(configuration, options) {
5540
5993
  throw new paths.AnonPersistError({
5541
5994
  cause: "no-key",
5542
5995
  schemaFields: extractSchemaFields(resolvedSchema),
5543
- callSite: paths.captureUserCallSite()
5996
+ callSite: captureUserCallSite()
5544
5997
  });
5545
5998
  }
5546
5999
  const existing = registry.forms.get(key);
@@ -5694,11 +6147,16 @@ function useAbstractForm(configuration, options) {
5694
6147
  if (merged.autoAria !== void 0) {
5695
6148
  apiOptions.autoAria = merged.autoAria;
5696
6149
  }
5697
- return buildFormApi(
5698
- state,
5699
- formInstanceId,
5700
- apiOptions
5701
- );
6150
+ const api = buildFormApi(state, formInstanceId, apiOptions);
6151
+ const onChangeConfig = configuration.onChange;
6152
+ if (onChangeConfig !== void 0) {
6153
+ const handler = typeof onChangeConfig === "function" ? onChangeConfig : onChangeConfig.handler;
6154
+ const onError = typeof onChangeConfig === "function" ? void 0 : onChangeConfig.onError;
6155
+ const options2 = onError === void 0 ? void 0 : { onError };
6156
+ const stop = state.registerOnChange(void 0, handler, options2, () => api);
6157
+ if (vue.getCurrentScope() !== void 0) vue.onScopeDispose(stop);
6158
+ }
6159
+ return api;
5702
6160
  }
5703
6161
  function mergeWithDefaults(defaults, configuration) {
5704
6162
  const strict = configuration.strict ?? defaults.strict;
@@ -5789,7 +6247,7 @@ function recordAmbientProvide(ssr) {
5789
6247
  if (instance === null) return;
5790
6248
  const instanceKey = instance;
5791
6249
  const entry = {
5792
- source: paths.captureUserCallSite()
6250
+ source: captureUserCallSite()
5793
6251
  };
5794
6252
  const existing = ambientProvideHistory.get(instanceKey);
5795
6253
  if (existing === void 0) {
@@ -5828,7 +6286,7 @@ function enforceAnonPersistRule(formKey, ssr) {
5828
6286
  if (paths.__DEV__)
5829
6287
  throw new paths.AnonPersistError({
5830
6288
  cause: "no-key",
5831
- callSite: paths.captureUserCallSite()
6289
+ callSite: captureUserCallSite()
5832
6290
  });
5833
6291
  if (!ssr && !warnedAnonPersistKeys.has(formKey)) {
5834
6292
  warnedAnonPersistKeys.add(formKey);
@@ -5885,7 +6343,7 @@ function resolveState(key, registry) {
5885
6343
  }
5886
6344
  function warnMiss$1(detail, ssr) {
5887
6345
  if (!paths.__DEV__ || ssr) return;
5888
- const frame = paths.captureUserCallSite();
6346
+ const frame = captureUserCallSite();
5889
6347
  console.warn(
5890
6348
  `[attaform] injectForm: ${detail}. Returning null.` + (frame !== void 0 ? ` ${frame}` : "")
5891
6349
  );
@@ -5908,6 +6366,99 @@ function warnIfAmbientProviderHadDuplicates() {
5908
6366
  }
5909
6367
  }
5910
6368
 
6369
+ const warnedNoParentRV = paths.__DEV__ ? /* @__PURE__ */ new WeakSet() : null;
6370
+ let warnedOutsideSetup = false;
6371
+ function makeRegisterValueProxy(capturedRegisterValue) {
6372
+ return new Proxy({}, {
6373
+ get(_target, prop) {
6374
+ if (prop === "__v_isRef") return true;
6375
+ if (prop === "value") return capturedRegisterValue.value;
6376
+ const v = capturedRegisterValue.value;
6377
+ if (v === void 0) return void 0;
6378
+ return Reflect.get(v, prop);
6379
+ },
6380
+ has(_target, prop) {
6381
+ if (prop === "__v_isRef" || prop === "value") return true;
6382
+ const v = capturedRegisterValue.value;
6383
+ if (v === void 0) return false;
6384
+ return Reflect.has(v, prop);
6385
+ },
6386
+ ownKeys(_target) {
6387
+ const v = capturedRegisterValue.value;
6388
+ if (v === void 0) return [];
6389
+ return Reflect.ownKeys(v);
6390
+ },
6391
+ getOwnPropertyDescriptor(_target, prop) {
6392
+ const v = capturedRegisterValue.value;
6393
+ if (v === void 0) return void 0;
6394
+ const desc = Reflect.getOwnPropertyDescriptor(v, prop);
6395
+ if (desc !== void 0) {
6396
+ desc.configurable = true;
6397
+ }
6398
+ return desc;
6399
+ }
6400
+ });
6401
+ }
6402
+ function useRegister() {
6403
+ const instance = vue.getCurrentInstance();
6404
+ if (instance === null) {
6405
+ warnOutsideSetup();
6406
+ return makeRegisterValueProxy(vue.shallowRef(void 0));
6407
+ }
6408
+ paths.ensureAttaformInstalled(instance.appContext.app);
6409
+ const capturedRegisterValue = vue.shallowRef(void 0);
6410
+ const refreshAndStripBridgeAttrs = () => {
6411
+ const rawAttrs = instance.attrs;
6412
+ if ("registerValue" in rawAttrs) {
6413
+ capturedRegisterValue.value = rawAttrs["registerValue"];
6414
+ delete rawAttrs["registerValue"];
6415
+ } else {
6416
+ const dirs = instance.vnode.dirs;
6417
+ if (dirs !== null && dirs !== void 0) {
6418
+ for (const dir of dirs) {
6419
+ const marked = dir.dir?.[paths.V_REGISTER_MARKER];
6420
+ if (marked === true) {
6421
+ capturedRegisterValue.value = dir.value;
6422
+ break;
6423
+ }
6424
+ }
6425
+ }
6426
+ }
6427
+ if ("value" in rawAttrs) delete rawAttrs["value"];
6428
+ };
6429
+ refreshAndStripBridgeAttrs();
6430
+ vue.onBeforeMount(refreshAndStripBridgeAttrs);
6431
+ vue.onBeforeUpdate(refreshAndStripBridgeAttrs);
6432
+ vue.onMounted(() => {
6433
+ const el = instance.vnode.el;
6434
+ if (el !== null && el !== void 0 && typeof el === "object") {
6435
+ el[paths.REGISTER_OWNER_MARKER] = true;
6436
+ }
6437
+ if (capturedRegisterValue.value === void 0) {
6438
+ warnNoParentRV(instance);
6439
+ }
6440
+ });
6441
+ return makeRegisterValueProxy(capturedRegisterValue);
6442
+ }
6443
+ function warnOutsideSetup() {
6444
+ if (!paths.__DEV__) return;
6445
+ if (warnedOutsideSetup) return;
6446
+ warnedOutsideSetup = true;
6447
+ const frame = captureUserCallSite();
6448
+ console.warn(
6449
+ `[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}` : "")
6450
+ );
6451
+ }
6452
+ function warnNoParentRV(instance) {
6453
+ if (!paths.__DEV__ || warnedNoParentRV === null) return;
6454
+ if (warnedNoParentRV.has(instance)) return;
6455
+ warnedNoParentRV.add(instance);
6456
+ const frame = captureUserCallSite();
6457
+ console.warn(
6458
+ `[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}` : "")
6459
+ );
6460
+ }
6461
+
5911
6462
  const LAZY_BRAND = Symbol.for("attaform/wizard-lazy");
5912
6463
  function lazy(resolve) {
5913
6464
  return { [LAZY_BRAND]: true, resolve };
@@ -6892,7 +7443,7 @@ function recordAmbientWizardProvide(ssr) {
6892
7443
  if (instance === null) return;
6893
7444
  const instanceKey = instance;
6894
7445
  const entry = {
6895
- source: paths.captureUserCallSite()
7446
+ source: captureUserCallSite()
6896
7447
  };
6897
7448
  const existing = ambientWizardProvideHistory.get(instanceKey);
6898
7449
  if (existing === void 0) {
@@ -6947,7 +7498,7 @@ function availableKeysHint(wizards) {
6947
7498
  }
6948
7499
  function warnMiss(detail, ssr, hint) {
6949
7500
  if (!paths.__DEV__ || ssr) return;
6950
- const frame = paths.captureUserCallSite();
7501
+ const frame = captureUserCallSite();
6951
7502
  const parts = [`[attaform] injectWizard: ${detail}. Returning null.`];
6952
7503
  if (hint !== void 0) parts.push(hint);
6953
7504
  if (frame !== void 0) parts.push(frame);
@@ -7001,5 +7552,6 @@ exports.slimKindOf = slimKindOf;
7001
7552
  exports.structuralSnapshot = structuralSnapshot;
7002
7553
  exports.unset = unset;
7003
7554
  exports.useAbstractForm = useAbstractForm;
7555
+ exports.useRegister = useRegister;
7004
7556
  exports.useWizard = useWizard;
7005
- //# sourceMappingURL=attaform.ClXwitZj.cjs.map
7557
+ //# sourceMappingURL=attaform.CjMcwV7W.cjs.map