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,5 +1,5 @@
1
- import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, toRaw, reactive, watch, markRaw, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, effectScope, nextTick } from 'vue';
2
- import { _ as __DEV__, j as canonicalizePath, G as segmentsForPathKey, t as isPathPrefix, b as FORM_ERRORS_PATH_KEY, S as SubmitErrorHandlerError, H as toError, A as AnonPersistError, k as captureUserCallSite, I as INTERACTIVE_TAG_NAMES, r as getOrAssignElementId, e as ROOT_PATH_KEY, R as ROOT_PATH, h as allowSensitivePersist, F as FORM_ERRORS_PATH, l as coerceToPathKey, v as isSensitivePath, o as createPersistOptInRegistry, d as InvalidUseFormConfigError, q as ensureAttaformInstalled, K as useRegistry, z as kFormContext, B as kFormInstanceId, g as ReservedFormKeyError, n as createIsSensitivePath, y as kAttaformWizardActiveStepResolver, w as kAttaformAncestorWizard } from './attaform.pmtahXKy.mjs';
1
+ import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, toRaw, reactive, isRef, toValue, watch, markRaw, triggerRef, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope, nextTick } from 'vue';
2
+ import { q as pathsEqual, l as isPathPrefix, _ as __DEV__, a as canonicalizePath, s as segmentsForPathKey, F as FORM_ERRORS_PATH_KEY, r as keyForSegments, S as SubmitErrorHandlerError, t as toError, A as AnonPersistError, w as INTERACTIVE_TAG_NAMES, x as getOrAssignElementId, R as ROOT_PATH, y as allowSensitivePersist, z as FORM_ERRORS_PATH, e as ROOT_PATH_KEY, B as segmentsToDotted, C as coerceToPathKey, E as isSensitivePath, G as createPersistOptInRegistry, d as InvalidUseFormConfigError, H as ensureAttaformInstalled, u as useRegistry, J as kFormContext, K as kFormInstanceId, h as ReservedFormKeyError, L as createIsSensitivePath, M as REGISTER_OWNER_MARKER, V as V_REGISTER_MARKER, k as kAttaformWizardActiveStepResolver, N as kAttaformAncestorWizard } from './attaform.BKFwekY2.mjs';
3
3
 
4
4
  function safeAssign(target, key, value) {
5
5
  if (key === "__proto__") {
@@ -34,7 +34,7 @@ function descendStep(value, segment) {
34
34
  if (typeof value !== "object") return NOT_FOUND;
35
35
  if (Array.isArray(value)) {
36
36
  if (typeof segment !== "number") return NOT_FOUND;
37
- if (segment < 0 || segment >= value.length) return NOT_FOUND;
37
+ if (!(segment in value)) return NOT_FOUND;
38
38
  return value[segment];
39
39
  }
40
40
  const record = value;
@@ -69,7 +69,7 @@ function hasAtPath(root, path) {
69
69
  if (current === null || current === void 0) return false;
70
70
  if (typeof current !== "object") return false;
71
71
  if (Array.isArray(current)) {
72
- return typeof last === "number" && last >= 0 && last < current.length;
72
+ return typeof last === "number" && last in current;
73
73
  }
74
74
  const key = typeof last === "number" ? String(last) : last;
75
75
  if (isShadowedKey(key)) return safeOwnHas(current, key);
@@ -98,6 +98,31 @@ function setAtPathOffset(root, path, value, offset) {
98
98
  safeAssign(rec, head, setAtPathOffset(safeOwnRead(rec, head), path, value, nextOffset));
99
99
  return rec;
100
100
  }
101
+ const NO_IN_PLACE = { applied: false };
102
+ function tryInPlaceLeafWrite(root, path, value) {
103
+ if (path.length === 0) return NO_IN_PLACE;
104
+ let node = root;
105
+ for (let i = 0; i < path.length; i++) {
106
+ const seg = path[i];
107
+ if (Array.isArray(node)) {
108
+ if (typeof seg !== "number" || seg < 0 || seg >= node.length) return NO_IN_PLACE;
109
+ } else if (isPlainRecord(node)) {
110
+ if (typeof seg !== "string" || isShadowedKey(seg) || !(seg in node)) return NO_IN_PLACE;
111
+ } else {
112
+ return NO_IN_PLACE;
113
+ }
114
+ const container = node;
115
+ if (i < path.length - 1) {
116
+ node = container[seg];
117
+ continue;
118
+ }
119
+ const old = container[seg];
120
+ if (isPlainRecord(old) || Array.isArray(old)) return NO_IN_PLACE;
121
+ container[seg] = value;
122
+ return { applied: true, old };
123
+ }
124
+ return NO_IN_PLACE;
125
+ }
101
126
  function deleteAtPath(root, path) {
102
127
  return deleteAtPathOffset(root, path, 0);
103
128
  }
@@ -160,68 +185,80 @@ function mergeStructuralImpl(schema, scratch, consumer, defaultValue) {
160
185
  }
161
186
  if (consumer === null) return null;
162
187
  if (Array.isArray(consumer)) {
163
- const shape = resolveArrayShape(schema, scratch);
164
- const isTuple = typeof shape === "number";
165
- const targetLen = isTuple ? shape : consumer.length;
166
- let cachedElementDefault;
167
- let cachedElementDefaultRead = false;
168
- let mutated = targetLen > consumer.length;
169
- const out = consumer.slice();
170
- while (out.length < targetLen) out.push(void 0);
171
- for (let i = 0; i < targetLen; i++) {
172
- scratch.push(i);
173
- let elemDefault;
174
- if (isTuple) {
175
- elemDefault = schema.getDefaultAtPath(scratch);
176
- } else {
177
- if (!cachedElementDefaultRead) {
178
- cachedElementDefault = schema.getDefaultAtPath(scratch);
179
- cachedElementDefaultRead = true;
180
- }
181
- elemDefault = cachedElementDefault;
182
- }
183
- const consumerElem = i < consumer.length ? consumer[i] : void 0;
184
- const merged = mergeStructuralImpl(schema, scratch, consumerElem, elemDefault);
185
- scratch.pop();
186
- if (merged !== consumerElem) {
187
- out[i] = merged;
188
- mutated = true;
189
- }
190
- }
191
- return mutated ? out : consumer;
188
+ return mergeStructuralArray(schema, scratch, consumer);
192
189
  }
193
190
  if (isPlainRecord(consumer)) {
194
191
  if (!isPlainRecord(defaultValue)) {
195
192
  return consumer;
196
193
  }
197
- let mutated = false;
198
194
  const out = { ...consumer };
199
- for (const key of Object.keys(defaultValue)) {
200
- if (!safeOwnHas(consumer, key)) {
201
- const defAtKey = safeOwnRead(defaultValue, key);
202
- scratch.push(key);
203
- const filled = mergeStructuralImpl(schema, scratch, void 0, defAtKey);
204
- scratch.pop();
205
- if (filled !== void 0) {
206
- safeAssign(out, key, filled);
207
- mutated = true;
208
- }
195
+ const filledAny = fillMissingKeysFromDefault(schema, scratch, consumer, defaultValue, out);
196
+ const recursedAny = recurseIntoConsumerKeys(schema, scratch, consumer, defaultValue, out);
197
+ return filledAny || recursedAny ? out : consumer;
198
+ }
199
+ return consumer;
200
+ }
201
+ function mergeStructuralArray(schema, scratch, consumer) {
202
+ const shape = resolveArrayShape(schema, scratch);
203
+ const isTuple = typeof shape === "number";
204
+ const targetLen = isTuple ? shape : consumer.length;
205
+ let cachedElementDefault;
206
+ let cachedElementDefaultRead = false;
207
+ let mutated = targetLen > consumer.length;
208
+ const out = consumer.slice();
209
+ while (out.length < targetLen) out.push(void 0);
210
+ for (let i = 0; i < targetLen; i++) {
211
+ scratch.push(i);
212
+ let elemDefault;
213
+ if (isTuple) {
214
+ elemDefault = schema.getDefaultAtPath(scratch);
215
+ } else {
216
+ if (!cachedElementDefaultRead) {
217
+ cachedElementDefault = schema.getDefaultAtPath(scratch);
218
+ cachedElementDefaultRead = true;
209
219
  }
220
+ elemDefault = cachedElementDefault;
221
+ }
222
+ const consumerElem = i < consumer.length ? consumer[i] : void 0;
223
+ const merged = mergeStructuralImpl(schema, scratch, consumerElem, elemDefault);
224
+ scratch.pop();
225
+ if (merged !== consumerElem) {
226
+ out[i] = merged;
227
+ mutated = true;
210
228
  }
211
- for (const key of Object.keys(consumer)) {
212
- const cVal = safeOwnRead(consumer, key);
213
- if (cVal === void 0) continue;
229
+ }
230
+ return mutated ? out : consumer;
231
+ }
232
+ function fillMissingKeysFromDefault(schema, scratch, consumer, defaultValue, out) {
233
+ let mutated = false;
234
+ for (const key of Object.keys(defaultValue)) {
235
+ if (!safeOwnHas(consumer, key)) {
236
+ const defAtKey = safeOwnRead(defaultValue, key);
214
237
  scratch.push(key);
215
- const merged = mergeStructuralImpl(schema, scratch, cVal, safeOwnRead(defaultValue, key));
238
+ const filled = mergeStructuralImpl(schema, scratch, void 0, defAtKey);
216
239
  scratch.pop();
217
- if (merged !== cVal) {
218
- safeAssign(out, key, merged);
240
+ if (filled !== void 0) {
241
+ safeAssign(out, key, filled);
219
242
  mutated = true;
220
243
  }
221
244
  }
222
- return mutated ? out : consumer;
223
245
  }
224
- return consumer;
246
+ return mutated;
247
+ }
248
+ function recurseIntoConsumerKeys(schema, scratch, consumer, defaultValue, out) {
249
+ let mutated = false;
250
+ for (const key of Object.keys(consumer)) {
251
+ const cVal = safeOwnRead(consumer, key);
252
+ if (cVal === void 0) continue;
253
+ scratch.push(key);
254
+ const merged = mergeStructuralImpl(schema, scratch, cVal, safeOwnRead(defaultValue, key));
255
+ scratch.pop();
256
+ if (merged !== cVal) {
257
+ safeAssign(out, key, merged);
258
+ mutated = true;
259
+ }
260
+ }
261
+ return mutated;
225
262
  }
226
263
  function setAtPathWithSchemaFill(root, schema, fullPath, value) {
227
264
  if (fullPath.length === 0) return value;
@@ -304,55 +341,27 @@ function diffAndApply(oldValue, newValue, prefix, visit) {
304
341
  const oldIsDescendable = isDescendable(oldValue);
305
342
  const newIsDescendable = isDescendable(newValue);
306
343
  if (oldValue === void 0 && newIsDescendable) {
307
- if (Array.isArray(newValue)) {
308
- for (let i = 0; i < newValue.length; i++) {
309
- diffAndApply(void 0, newValue[i], appendSegment(prefix, i), visit);
310
- }
311
- } else {
312
- const rec = newValue;
313
- for (const k of Object.keys(rec)) {
314
- diffAndApply(void 0, rec[k], appendSegment(prefix, k), visit);
315
- }
316
- }
344
+ walkNewDescendable(newValue, prefix, visit);
317
345
  return;
318
346
  }
319
347
  if (oldIsDescendable && newValue === void 0) {
320
- if (Array.isArray(oldValue)) {
321
- for (let i = 0; i < oldValue.length; i++) {
322
- diffAndApply(oldValue[i], void 0, appendSegment(prefix, i), visit);
323
- }
324
- } else {
325
- const rec = oldValue;
326
- for (const k of Object.keys(rec)) {
327
- diffAndApply(rec[k], void 0, appendSegment(prefix, k), visit);
328
- }
329
- }
348
+ walkOldDescendable(oldValue, prefix, visit);
330
349
  return;
331
350
  }
332
351
  if (oldIsDescendable && newIsDescendable) {
333
352
  const oldIsArray = Array.isArray(oldValue);
334
353
  const newIsArray = Array.isArray(newValue);
335
354
  if (oldIsArray && newIsArray) {
336
- const oldArr = oldValue;
337
- const newArr = newValue;
338
- const max = Math.max(oldArr.length, newArr.length);
339
- for (let i = 0; i < max; i++) {
340
- diffAndApply(oldArr[i], newArr[i], appendSegment(prefix, i), visit);
341
- }
355
+ diffArraysLockstep(oldValue, newValue, prefix, visit);
342
356
  return;
343
357
  }
344
358
  if (!oldIsArray && !newIsArray) {
345
- const oldRec = oldValue;
346
- const newRec = newValue;
347
- const seen = /* @__PURE__ */ new Set();
348
- for (const k of Object.keys(oldRec)) {
349
- seen.add(k);
350
- diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
351
- }
352
- for (const k of Object.keys(newRec)) {
353
- if (seen.has(k)) continue;
354
- diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
355
- }
359
+ diffObjectsLockstep(
360
+ oldValue,
361
+ newValue,
362
+ prefix,
363
+ visit
364
+ );
356
365
  return;
357
366
  }
358
367
  visit({ kind: "changed", path: prefix, oldValue, newValue });
@@ -376,7 +385,48 @@ function diffAndApply(oldValue, newValue, prefix, visit) {
376
385
  }
377
386
  visit({ kind: "changed", path: prefix, oldValue, newValue });
378
387
  }
379
- function applyChangedKeys(target, source) {
388
+ function walkNewDescendable(newValue, prefix, visit) {
389
+ if (Array.isArray(newValue)) {
390
+ for (let i = 0; i < newValue.length; i++) {
391
+ diffAndApply(void 0, newValue[i], appendSegment(prefix, i), visit);
392
+ }
393
+ } else {
394
+ const rec = newValue;
395
+ for (const k of Object.keys(rec)) {
396
+ diffAndApply(void 0, rec[k], appendSegment(prefix, k), visit);
397
+ }
398
+ }
399
+ }
400
+ function walkOldDescendable(oldValue, prefix, visit) {
401
+ if (Array.isArray(oldValue)) {
402
+ for (let i = 0; i < oldValue.length; i++) {
403
+ diffAndApply(oldValue[i], void 0, appendSegment(prefix, i), visit);
404
+ }
405
+ } else {
406
+ const rec = oldValue;
407
+ for (const k of Object.keys(rec)) {
408
+ diffAndApply(rec[k], void 0, appendSegment(prefix, k), visit);
409
+ }
410
+ }
411
+ }
412
+ function diffArraysLockstep(oldArr, newArr, prefix, visit) {
413
+ const max = Math.max(oldArr.length, newArr.length);
414
+ for (let i = 0; i < max; i++) {
415
+ diffAndApply(oldArr[i], newArr[i], appendSegment(prefix, i), visit);
416
+ }
417
+ }
418
+ function diffObjectsLockstep(oldRec, newRec, prefix, visit) {
419
+ const seen = /* @__PURE__ */ new Set();
420
+ for (const k of Object.keys(oldRec)) {
421
+ seen.add(k);
422
+ diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
423
+ }
424
+ for (const k of Object.keys(newRec)) {
425
+ if (seen.has(k)) continue;
426
+ diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
427
+ }
428
+ }
429
+ function applyChangedKeys(target, source, arrayOpPath, currentPath) {
380
430
  if (!isDescendable(target) || !isDescendable(source)) return false;
381
431
  const targetIsArray = Array.isArray(target);
382
432
  const sourceIsArray = Array.isArray(source);
@@ -394,11 +444,27 @@ function applyChangedKeys(target, source) {
394
444
  if (targetIsArray) {
395
445
  const t = target;
396
446
  const s = source;
397
- if (t.length > s.length) t.length = s.length;
398
- for (const idx of changedFirstSegments) {
399
- if (typeof idx === "symbol") continue;
400
- const i = typeof idx === "number" ? idx : Number(idx);
401
- t[i] = s[i];
447
+ if (arrayOpPath === null || pathsEqual(currentPath, arrayOpPath)) {
448
+ if (t.length > s.length) t.length = s.length;
449
+ for (const idx of changedFirstSegments) {
450
+ if (typeof idx === "symbol") continue;
451
+ const i = typeof idx === "number" ? idx : Number(idx);
452
+ if (i >= s.length) continue;
453
+ t[i] = s[i];
454
+ }
455
+ } else {
456
+ for (const idx of changedFirstSegments) {
457
+ if (typeof idx === "symbol") continue;
458
+ const i = typeof idx === "number" ? idx : Number(idx);
459
+ if (i >= s.length) continue;
460
+ const childPath = appendSegment(currentPath, i);
461
+ const curEl = t[i];
462
+ const nextEl = s[i];
463
+ if (isPathPrefix(childPath, arrayOpPath) && isDescendable(curEl) && isDescendable(nextEl) && applyChangedKeys(curEl, nextEl, arrayOpPath, childPath)) {
464
+ continue;
465
+ }
466
+ t[i] = nextEl;
467
+ }
402
468
  }
403
469
  } else {
404
470
  const t = target;
@@ -410,7 +476,14 @@ function applyChangedKeys(target, source) {
410
476
  for (const k of changedFirstSegments) {
411
477
  if (typeof k === "symbol") continue;
412
478
  const key = String(k);
413
- safeAssign(t, key, safeOwnRead(s, key));
479
+ const nextVal = safeOwnRead(s, key);
480
+ if (arrayOpPath !== null) {
481
+ const curVal = safeOwnRead(t, key);
482
+ if (isDescendable(curVal) && isDescendable(nextVal) && applyChangedKeys(curVal, nextVal, arrayOpPath, appendSegment(currentPath, key))) {
483
+ continue;
484
+ }
485
+ }
486
+ safeAssign(t, key, nextVal);
414
487
  }
415
488
  }
416
489
  return true;
@@ -522,6 +595,50 @@ function resolveGetDisplayState(config) {
522
595
  return config ?? defaultDisplayState;
523
596
  }
524
597
 
598
+ const AttaformErrorCode = {
599
+ /** A required field is in the blank set — user hasn't supplied a value. */
600
+ NoValueSupplied: "atta:no-value-supplied",
601
+ /** The schema adapter's `validateAtPath` threw synchronously. */
602
+ AdapterThrew: "atta:adapter-threw",
603
+ /**
604
+ * User code inside a `z.preprocess`, `.refine`, or `.transform`
605
+ * threw (sync or async). The adapter caught the throw and surfaced
606
+ * it as a `ValidationError` at the field path so the form's normal
607
+ * error pipeline handles it instead of leaking as an unhandled
608
+ * rejection or routing through `submitError`.
609
+ */
610
+ ValidatorThrew: "atta:validator-threw",
611
+ /**
612
+ * A function-form `defaultValues` factory threw or its promise
613
+ * rejected. The runtime captures the raw error on `form.hydrateError`
614
+ * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
615
+ * the standard error pipeline carries the signal. Critical for the
616
+ * SSR round-trip: `hydrateError` itself does not ride the wire
617
+ * payload, but `schemaErrors` does, so the client sees the failure
618
+ * after rehydration without an extra channel.
619
+ */
620
+ HydrationFailed: "atta:hydration-failed",
621
+ /** The supplied path didn't resolve to any node in the schema. */
622
+ PathNotFound: "atta:path-not-found",
623
+ /**
624
+ * A walked form's `activate()` (async `defaultValues` factory) threw
625
+ * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
626
+ * `ValidationError` at the form-level path (`[]`) so the wizard's
627
+ * aggregate error pipeline can carry the failure alongside ordinary
628
+ * validation errors. The raw factory error remains on
629
+ * `form.hydrateError` for retry UX.
630
+ */
631
+ ActivationFailed: "atta:activation-failed"
632
+ };
633
+ function makeBlankRequiredError(segments, formKey) {
634
+ return {
635
+ message: "No value supplied",
636
+ path: [...segments],
637
+ formKey,
638
+ code: AttaformErrorCode.NoValueSupplied
639
+ };
640
+ }
641
+
525
642
  const DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS = 0;
526
643
  const DEFAULT_PERSISTENCE_DEBOUNCE_MS = 300;
527
644
  const DEFAULT_HISTORY_MAX_SNAPSHOTS = 128;
@@ -623,13 +740,19 @@ function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, options
623
740
  return c;
624
741
  };
625
742
  }
743
+ function resolveFieldMetaAndLabel(state, segments) {
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);
747
+ return { resolved, label };
748
+ }
626
749
  function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
627
750
  const record = state.fields.get(key);
628
751
  const value = state.getValueAtPath(segments);
629
752
  const original = state.originals.get(key)?.value;
630
753
  const pristine = state.isPristineAtPath(segments);
631
754
  const schemaForKey = state.schemaErrors.get(key);
632
- const blankForKey = state.derivedBlankErrors.value.get(key);
755
+ const blankForKey = state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments) ? [makeBlankRequiredError(segments, state.formKey)] : void 0;
633
756
  const userForKey = state.userErrors.get(key);
634
757
  const errors = [];
635
758
  if (schemaForKey !== void 0) errors.push(...schemaForKey);
@@ -644,9 +767,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
644
767
  const elementRecord = state.elements.get(key);
645
768
  const elementsArr = elementRecord ? Object.freeze([...elementRecord.elements]) : EMPTY_ELEMENTS;
646
769
  const firstElement = elementsArr[0] ?? null;
647
- const resolved = state.schema.getFieldMetaAtPath ? state.schema.getFieldMetaAtPath(segments) : EMPTY_RESOLVED_FIELD_META;
648
- const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
649
- const label = resolved.label || humanize(lastSegment);
770
+ const { resolved, label } = resolveFieldMetaAndLabel(state, segments);
650
771
  return {
651
772
  value,
652
773
  original,
@@ -695,9 +816,32 @@ function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBa
695
816
  getDisplayState
696
817
  );
697
818
  }
819
+ function visitActiveLeafPaths(value, base, visit) {
820
+ if (Array.isArray(value)) {
821
+ for (let i = 0; i < value.length; i++) {
822
+ const child = value[i];
823
+ if (Array.isArray(child) || isPlainRecord(child)) {
824
+ visitActiveLeafPaths(child, [...base, i], visit);
825
+ } else {
826
+ visit([...base, i]);
827
+ }
828
+ }
829
+ return;
830
+ }
831
+ if (isPlainRecord(value)) {
832
+ for (const k of Object.keys(value)) {
833
+ const child = value[k];
834
+ if (Array.isArray(child) || isPlainRecord(child)) {
835
+ visitActiveLeafPaths(child, [...base, k], visit);
836
+ } else {
837
+ visit([...base, k]);
838
+ }
839
+ }
840
+ }
841
+ }
698
842
  function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
699
843
  const formValue = state.form.value;
700
- const value = state.getValueAtPath(segments);
844
+ const value = getAtPath(formValue, segments);
701
845
  const original = state.originals.get(key)?.value;
702
846
  let pristine = true;
703
847
  let blank = true;
@@ -716,12 +860,16 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
716
860
  let asyncPending = false;
717
861
  const submissionAttempts = state.submissionAttempts.value;
718
862
  const blurredLeafSegments = [];
719
- for (const [leafKey, entry] of state.originals) {
720
- if (!isPathPrefix(segments, entry.segments)) continue;
721
- if (segments.length === entry.segments.length) continue;
722
- if (!hasAtPath(formValue, entry.segments)) continue;
863
+ const descendantLeaves = [];
864
+ visitActiveLeafPaths(value, segments, (leafSegments) => {
865
+ const { key: leafKey } = keyForSegments(leafSegments);
866
+ const entry = state.originals.get(leafKey);
867
+ if (entry === void 0) return;
868
+ descendantLeaves.push({ key: leafKey, segments: entry.segments });
869
+ });
870
+ for (const { key: leafKey, segments: leafSeg } of descendantLeaves) {
723
871
  const leafRecord = state.fields.get(leafKey);
724
- if (!state.isPristineAtPathByKey(leafKey, entry.segments)) {
872
+ if (!state.isPristineAtPathByKey(leafKey, leafSeg)) {
725
873
  pristine = false;
726
874
  dirty = true;
727
875
  }
@@ -732,7 +880,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
732
880
  if (leafRecord?.interacted === true) interacted = true;
733
881
  if (leafRecord?.blurredAfterInteraction === true) {
734
882
  blurredAfterInteraction = true;
735
- blurredLeafSegments.push(entry.segments);
883
+ blurredLeafSegments.push(leafSeg);
736
884
  }
737
885
  if (leafRecord?.connected === true) connected = true;
738
886
  if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0) {
@@ -749,7 +897,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
749
897
  if (leafGateOpen && since !== void 0 && (transformingSince === null || since < transformingSince))
750
898
  transformingSince = since;
751
899
  }
752
- if (state.pathHasAsyncValidationByKey(leafKey, entry.segments)) asyncPending = true;
900
+ if (state.pathHasAsyncValidationByKey(leafKey, leafSeg)) asyncPending = true;
753
901
  const ts = leafRecord?.updatedAt;
754
902
  if (ts !== void 0 && ts !== null) {
755
903
  if (updatedAt === null || ts > updatedAt) updatedAt = ts;
@@ -783,9 +931,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
783
931
  const ownTransformError = state.transformErrors.get(key) ?? null;
784
932
  const gated = asyncPending && !state.firstValidationDone.value;
785
933
  const valid = !gated && errors.length === 0 && !validating;
786
- const resolved = state.schema.getFieldMetaAtPath ? state.schema.getFieldMetaAtPath(segments) : EMPTY_RESOLVED_FIELD_META;
787
- const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
788
- const label = resolved.label || humanize(lastSegment);
934
+ const { resolved, label } = resolveFieldMetaAndLabel(state, segments);
789
935
  return {
790
936
  base: {
791
937
  value,
@@ -1378,7 +1524,7 @@ function buildFieldArrayApi(state) {
1378
1524
  append(path, value) {
1379
1525
  const next = readArray(path);
1380
1526
  next.push(value);
1381
- return writeArray(path, next);
1527
+ return writeArray(path, next, { kind: "insert", index: next.length - 1 });
1382
1528
  },
1383
1529
  prepend(path, value) {
1384
1530
  const next = readArray(path);
@@ -1568,20 +1714,20 @@ function buildFieldStateProxy(state, formInstanceId, getFormMetaBase, options) {
1568
1714
  }
1569
1715
  function materializeFields(state, containerSegments, snapshotFieldStateAt) {
1570
1716
  const liveValue = getAtPath(state.form.value, containerSegments);
1571
- return walk$2(liveValue, containerSegments, state.schema, snapshotFieldStateAt);
1717
+ return walk$1(liveValue, containerSegments, state.schema, snapshotFieldStateAt);
1572
1718
  }
1573
- function walk$2(value, basePath, schema, snapshotFieldStateAt) {
1719
+ function walk$1(value, basePath, schema, snapshotFieldStateAt) {
1574
1720
  if (schema.isLeafAtPath(basePath)) return snapshotFieldStateAt(basePath);
1575
1721
  if (value === null || value === void 0) return value;
1576
1722
  if (typeof value !== "object") {
1577
1723
  return value;
1578
1724
  }
1579
1725
  if (Array.isArray(value)) {
1580
- return value.map((_, i) => walk$2(value[i], [...basePath, i], schema, snapshotFieldStateAt));
1726
+ return value.map((_, i) => walk$1(value[i], [...basePath, i], schema, snapshotFieldStateAt));
1581
1727
  }
1582
1728
  const result = {};
1583
1729
  for (const key of Object.keys(value)) {
1584
- result[key] = walk$2(
1730
+ result[key] = walk$1(
1585
1731
  value[key],
1586
1732
  [...basePath, key],
1587
1733
  schema,
@@ -1665,87 +1811,46 @@ function mergeDeep(target, source, path, schema) {
1665
1811
  if (!isPlainRecord(source)) return source;
1666
1812
  if (schema !== void 0) {
1667
1813
  const du = schema.getUnionDiscriminatorAtPath(path);
1668
- if (du !== void 0) {
1669
- const sourceRecord = source;
1670
- const sourceDisc = sourceRecord[du.discriminatorKey];
1671
- if (sourceDisc !== void 0 && !du.isVariantSelected(sourceDisc)) {
1672
- return { [du.discriminatorKey]: sourceDisc };
1673
- }
1674
- if (sourceDisc !== void 0) {
1675
- const variantDefault = du.getVariantDefault(sourceDisc);
1676
- if (isPlainRecord(variantDefault)) {
1677
- const out2 = { ...variantDefault };
1678
- for (const key of Object.keys(sourceRecord)) {
1679
- if (!safeOwnHas(variantDefault, key) && key !== du.discriminatorKey) continue;
1680
- safeAssign(
1681
- out2,
1682
- key,
1683
- mergeDeep(
1684
- safeOwnRead(out2, key),
1685
- safeOwnRead(sourceRecord, key),
1686
- [...path, key],
1687
- schema
1688
- )
1689
- );
1690
- }
1691
- return out2;
1692
- }
1693
- }
1694
- return {};
1814
+ if (du !== void 0) return mergeDuAwareKeys(source, path, schema, du);
1815
+ }
1816
+ return mergeObjectKeys(target, source, path, schema);
1817
+ }
1818
+ function mergeDuAwareKeys(source, path, schema, du) {
1819
+ const sourceDisc = source[du.discriminatorKey];
1820
+ if (sourceDisc !== void 0 && !du.isVariantSelected(sourceDisc)) {
1821
+ return { [du.discriminatorKey]: sourceDisc };
1822
+ }
1823
+ if (sourceDisc !== void 0) {
1824
+ const variantDefault = du.getVariantDefault(sourceDisc);
1825
+ if (isPlainRecord(variantDefault)) {
1826
+ return mergeVariantKeys(source, variantDefault, path, schema, du);
1695
1827
  }
1696
1828
  }
1697
- const mergeTarget = target;
1698
- const out = isPlainRecord(mergeTarget) ? { ...mergeTarget } : {};
1829
+ return {};
1830
+ }
1831
+ function mergeVariantKeys(source, variantDefault, path, schema, du) {
1832
+ const out = { ...variantDefault };
1699
1833
  for (const key of Object.keys(source)) {
1834
+ if (!safeOwnHas(variantDefault, key) && key !== du.discriminatorKey) continue;
1700
1835
  safeAssign(
1701
1836
  out,
1702
1837
  key,
1703
- mergeDeep(
1704
- safeOwnRead(out, key),
1705
- safeOwnRead(source, key),
1706
- [...path, key],
1707
- schema
1708
- )
1838
+ mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
1839
+ );
1840
+ }
1841
+ return out;
1842
+ }
1843
+ function mergeObjectKeys(target, source, path, schema) {
1844
+ const out = isPlainRecord(target) ? { ...target } : {};
1845
+ for (const key of Object.keys(source)) {
1846
+ safeAssign(
1847
+ out,
1848
+ key,
1849
+ mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
1709
1850
  );
1710
1851
  }
1711
1852
  return out;
1712
1853
  }
1713
-
1714
- const AttaformErrorCode = {
1715
- /** A required field is in the blank set — user hasn't supplied a value. */
1716
- NoValueSupplied: "atta:no-value-supplied",
1717
- /** The schema adapter's `validateAtPath` threw synchronously. */
1718
- AdapterThrew: "atta:adapter-threw",
1719
- /**
1720
- * User code inside a `z.preprocess`, `.refine`, or `.transform`
1721
- * threw (sync or async). The adapter caught the throw and surfaced
1722
- * it as a `ValidationError` at the field path so the form's normal
1723
- * error pipeline handles it instead of leaking as an unhandled
1724
- * rejection or routing through `submitError`.
1725
- */
1726
- ValidatorThrew: "atta:validator-threw",
1727
- /**
1728
- * A function-form `defaultValues` factory threw or its promise
1729
- * rejected. The runtime captures the raw error on `form.hydrateError`
1730
- * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
1731
- * the standard error pipeline carries the signal. Critical for the
1732
- * SSR round-trip: `hydrateError` itself does not ride the wire
1733
- * payload, but `schemaErrors` does, so the client sees the failure
1734
- * after rehydration without an extra channel.
1735
- */
1736
- HydrationFailed: "atta:hydration-failed",
1737
- /** The supplied path didn't resolve to any node in the schema. */
1738
- PathNotFound: "atta:path-not-found",
1739
- /**
1740
- * A walked form's `activate()` (async `defaultValues` factory) threw
1741
- * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
1742
- * `ValidationError` at the form-level path (`[]`) so the wizard's
1743
- * aggregate error pipeline can carry the failure alongside ordinary
1744
- * validation errors. The raw factory error remains on
1745
- * `form.hydrateError` for retry UX.
1746
- */
1747
- ActivationFailed: "atta:activation-failed"
1748
- };
1749
1854
 
1750
1855
  const warnedNoScopeStores = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
1751
1856
  function buildProcessForm(state, formInstanceId, options = {}) {
@@ -2004,6 +2109,33 @@ function applyInvalidSubmitPolicy(state, formInstanceId, policy) {
2004
2109
  target.element.focus({ preventScroll: true });
2005
2110
  }
2006
2111
 
2112
+ function captureUserCallSite() {
2113
+ const raw = new Error().stack;
2114
+ if (typeof raw !== "string") return void 0;
2115
+ const lines = raw.split("\n");
2116
+ for (let i = 1; i < lines.length; i++) {
2117
+ const frame = lines[i];
2118
+ if (frame === void 0) continue;
2119
+ if (/attaform[/-]forms?/i.test(frame)) continue;
2120
+ if (/\bforms\.[A-Za-z0-9_-]+\.m?js\b/.test(frame)) continue;
2121
+ const trimmed = frame.trim();
2122
+ if (trimmed.length === 0) continue;
2123
+ return shortenSourceFrame(trimmed);
2124
+ }
2125
+ return void 0;
2126
+ }
2127
+ function shortenSourceFrame(frame) {
2128
+ const match = /(?:^|\s|\()([^\s()]+):(\d+):\d+\)?$/.exec(frame);
2129
+ if (match === null) return frame;
2130
+ const [, urlOrPath, line] = match;
2131
+ if (urlOrPath === void 0 || line === void 0) return frame;
2132
+ let path = urlOrPath;
2133
+ path = path.replace(/^[a-z]+:\/\/[^/]+\//i, "");
2134
+ path = path.replace(/^_nuxt\//, "");
2135
+ path = path.replace(/^\//, "");
2136
+ return `(${path}:${line})`;
2137
+ }
2138
+
2007
2139
  function extractSchemaFields(schema) {
2008
2140
  try {
2009
2141
  const root = schema.getDefaultAtPath([]);
@@ -2062,9 +2194,9 @@ function isLeafValue(value) {
2062
2194
  return true;
2063
2195
  }
2064
2196
  function isSlimPrimitiveValid(schema, store, path, value) {
2065
- return walk$1(schema, store, path, value);
2197
+ return walk(schema, store, path, value);
2066
2198
  }
2067
- function walk$1(schema, store, path, value) {
2199
+ function walk(schema, store, path, value) {
2068
2200
  if (schema.isPreprocessOrCoerceLeaf(path)) return true;
2069
2201
  const accepted = schema.getSlimPrimitiveTypesAtPath(path);
2070
2202
  const kind = isLeafValue(value) ? slimKindOf(value) : Array.isArray(value) ? "array" : "object";
@@ -2074,13 +2206,13 @@ function walk$1(schema, store, path, value) {
2074
2206
  }
2075
2207
  if (Array.isArray(value)) {
2076
2208
  for (let i = 0; i < value.length; i++) {
2077
- if (!walk$1(schema, store, [...path, i], value[i])) return false;
2209
+ if (!walk(schema, store, [...path, i], value[i])) return false;
2078
2210
  }
2079
2211
  return true;
2080
2212
  }
2081
2213
  if (isPlainRecord(value)) {
2082
2214
  for (const key of Object.keys(value)) {
2083
- if (!walk$1(schema, store, [...path, key], value[key])) {
2215
+ if (!walk(schema, store, [...path, key], value[key])) {
2084
2216
  return false;
2085
2217
  }
2086
2218
  }
@@ -2480,49 +2612,60 @@ function walkUnsetSentinels(values, schema) {
2480
2612
  walkUnspecified(rootSlim, [], paths);
2481
2613
  return { cleanedValues: void 0, paths };
2482
2614
  }
2483
- const cleaned = walk(values, [], schema, paths);
2615
+ const cleaned = walkCore(values, [], schema, paths, true);
2484
2616
  return { cleanedValues: cleaned, paths };
2485
2617
  }
2486
- function walk(input, segments, schema, paths) {
2618
+ function isOpaqueLeaf(value) {
2619
+ return value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || typeof value === "function";
2620
+ }
2621
+ function walkCore(input, segments, schema, paths, synthesizeSchemaKeys) {
2487
2622
  if (isUnset(input)) {
2488
2623
  return expandUnsetAt(segments, schema, paths);
2489
2624
  }
2490
2625
  if (input === void 0) {
2491
- const slim = schema.getDefaultAtPath(segments);
2492
- return walkUnspecified(slim, segments, paths);
2493
- }
2494
- if (input === null) return null;
2495
- if (input instanceof Date || input instanceof RegExp || input instanceof Map || input instanceof Set || typeof input === "function") {
2626
+ if (synthesizeSchemaKeys) {
2627
+ const slim = schema.getDefaultAtPath(segments);
2628
+ return walkUnspecified(slim, segments, paths);
2629
+ }
2496
2630
  return input;
2497
2631
  }
2632
+ if (input === null) return null;
2633
+ if (isOpaqueLeaf(input)) return input;
2498
2634
  if (Array.isArray(input)) {
2499
2635
  const out = new Array(input.length);
2500
2636
  let mutated = false;
2501
2637
  for (let i = 0; i < input.length; i++) {
2502
- const walked = walk(input[i], [...segments, i], schema, paths);
2638
+ const walked = walkCore(input[i], [...segments, i], schema, paths, synthesizeSchemaKeys);
2503
2639
  out[i] = walked;
2504
2640
  if (walked !== input[i]) mutated = true;
2505
2641
  }
2506
2642
  return mutated ? out : input;
2507
2643
  }
2508
2644
  if (typeof input === "object") {
2509
- const slim = schema.getDefaultAtPath(segments);
2510
- const inputKeys = Object.keys(input);
2511
- const inputKeysSet = new Set(inputKeys);
2512
- const allKeys = new Set(inputKeys);
2513
- if (slim !== null && slim !== void 0 && typeof slim === "object" && !Array.isArray(slim) && !(slim instanceof Date) && !(slim instanceof RegExp) && !(slim instanceof Map) && !(slim instanceof Set)) {
2514
- for (const k of Object.keys(slim)) allKeys.add(k);
2645
+ const obj = input;
2646
+ const inputKeys = Object.keys(obj);
2647
+ let keys = inputKeys;
2648
+ let mutated = false;
2649
+ let inputKeysSet = null;
2650
+ if (synthesizeSchemaKeys) {
2651
+ inputKeysSet = new Set(inputKeys);
2652
+ const allKeys = new Set(inputKeys);
2653
+ const slim = schema.getDefaultAtPath(segments);
2654
+ if (slim !== null && slim !== void 0 && typeof slim === "object" && !Array.isArray(slim) && !isOpaqueLeaf(slim)) {
2655
+ for (const k of Object.keys(slim)) allKeys.add(k);
2656
+ }
2657
+ keys = allKeys;
2658
+ mutated = allKeys.size !== inputKeys.length;
2515
2659
  }
2516
2660
  const out = {};
2517
- let mutated = allKeys.size !== inputKeys.length;
2518
- for (const key of allKeys) {
2519
- const orig = input[key];
2520
- if (orig === void 0 && inputKeysSet.has(key)) {
2661
+ for (const key of keys) {
2662
+ const orig = obj[key];
2663
+ if (synthesizeSchemaKeys && orig === void 0 && inputKeysSet?.has(key) === true) {
2521
2664
  safeAssign(out, key, void 0);
2522
2665
  mutated = true;
2523
2666
  continue;
2524
2667
  }
2525
- const walked = walk(orig, [...segments, key], schema, paths);
2668
+ const walked = walkCore(orig, [...segments, key], schema, paths, synthesizeSchemaKeys);
2526
2669
  safeAssign(out, key, walked);
2527
2670
  if (walked !== orig) mutated = true;
2528
2671
  }
@@ -2537,7 +2680,7 @@ function walkUnspecified(slim, segments, paths) {
2537
2680
  }
2538
2681
  return slim;
2539
2682
  }
2540
- if (slim instanceof Date || slim instanceof RegExp || slim instanceof Map || slim instanceof Set || typeof slim === "function") {
2683
+ if (isOpaqueLeaf(slim)) {
2541
2684
  return slim;
2542
2685
  }
2543
2686
  if (Array.isArray(slim)) return slim;
@@ -2556,40 +2699,9 @@ function walkUnspecified(slim, segments, paths) {
2556
2699
  }
2557
2700
  function substituteUnsetSentinels(value, prefix, schema) {
2558
2701
  const paths = [];
2559
- const cleaned = substitute(value, [...prefix], schema, paths);
2702
+ const cleaned = walkCore(value, [...prefix], schema, paths, false);
2560
2703
  return { cleanedValues: cleaned, paths };
2561
2704
  }
2562
- function substitute(input, segments, schema, paths) {
2563
- if (isUnset(input)) {
2564
- return expandUnsetAt(segments, schema, paths);
2565
- }
2566
- if (input === void 0 || input === null) return input;
2567
- if (input instanceof Date || input instanceof RegExp || input instanceof Map || input instanceof Set || typeof input === "function") {
2568
- return input;
2569
- }
2570
- if (Array.isArray(input)) {
2571
- let mutated = false;
2572
- const out = new Array(input.length);
2573
- for (let i = 0; i < input.length; i++) {
2574
- const walked = substitute(input[i], [...segments, i], schema, paths);
2575
- out[i] = walked;
2576
- if (walked !== input[i]) mutated = true;
2577
- }
2578
- return mutated ? out : input;
2579
- }
2580
- if (typeof input === "object") {
2581
- let mutated = false;
2582
- const out = {};
2583
- for (const key of Object.keys(input)) {
2584
- const orig = input[key];
2585
- const walked = substitute(orig, [...segments, key], schema, paths);
2586
- safeAssign(out, key, walked);
2587
- if (walked !== orig) mutated = true;
2588
- }
2589
- return mutated ? out : input;
2590
- }
2591
- return input;
2592
- }
2593
2705
  function isPrimitiveOrEmpty(value) {
2594
2706
  if (value === null || value === void 0) return true;
2595
2707
  const t = typeof value;
@@ -2619,7 +2731,7 @@ function expandUnsetAt(segments, schema, paths) {
2619
2731
  paths.push(canonicalizePath(segments).key);
2620
2732
  return slim;
2621
2733
  }
2622
- if (slim instanceof Date || slim instanceof RegExp || slim instanceof Map || slim instanceof Set || typeof slim === "function") {
2734
+ if (isOpaqueLeaf(slim)) {
2623
2735
  paths.push(canonicalizePath(segments).key);
2624
2736
  return slim;
2625
2737
  }
@@ -2753,20 +2865,128 @@ function buildFormApi(state, formInstanceId, options = {}) {
2753
2865
  if (instanceMeta === void 0) return meta;
2754
2866
  return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
2755
2867
  };
2868
+ const reMarkBlanksAfterSubstitution = (paths) => {
2869
+ for (const pathKey of paths) {
2870
+ const blankSegments = segmentsForPathKey(pathKey);
2871
+ if (blankSegments === null) continue;
2872
+ state.setValueAtPath(
2873
+ blankSegments,
2874
+ state.getValueAtPath(blankSegments),
2875
+ withInstanceMeta({ blank: true })
2876
+ );
2877
+ }
2878
+ };
2756
2879
  const getFormMetaBase = () => {
2757
- const { base: rootBase } = buildContainerFieldStateBase(
2880
+ let rollup;
2881
+ const rootBase = () => rollup ?? (rollup = buildContainerFieldStateBase(
2758
2882
  state,
2759
2883
  ROOT_PATH,
2760
2884
  ROOT_PATH_KEY,
2761
2885
  formInstanceId
2762
- );
2886
+ ).base);
2763
2887
  return {
2764
- ...rootBase,
2888
+ // Rollup-derived (FieldStateBase) — the whole rollup builds once, on the
2889
+ // first access of any of these.
2890
+ get value() {
2891
+ return rootBase().value;
2892
+ },
2893
+ get original() {
2894
+ return rootBase().original;
2895
+ },
2896
+ get pristine() {
2897
+ return rootBase().pristine;
2898
+ },
2899
+ get dirty() {
2900
+ return rootBase().dirty;
2901
+ },
2902
+ get focused() {
2903
+ return rootBase().focused;
2904
+ },
2905
+ get blurred() {
2906
+ return rootBase().blurred;
2907
+ },
2908
+ get touched() {
2909
+ return rootBase().touched;
2910
+ },
2911
+ get interacted() {
2912
+ return rootBase().interacted;
2913
+ },
2914
+ get blurredAfterInteraction() {
2915
+ return rootBase().blurredAfterInteraction;
2916
+ },
2917
+ get connected() {
2918
+ return rootBase().connected;
2919
+ },
2920
+ get element() {
2921
+ return rootBase().element;
2922
+ },
2923
+ get elements() {
2924
+ return rootBase().elements;
2925
+ },
2926
+ get updatedAt() {
2927
+ return rootBase().updatedAt;
2928
+ },
2929
+ get errors() {
2930
+ return rootBase().errors;
2931
+ },
2932
+ get validating() {
2933
+ return rootBase().validating;
2934
+ },
2935
+ get valid() {
2936
+ return rootBase().valid;
2937
+ },
2938
+ get transforming() {
2939
+ return rootBase().transforming;
2940
+ },
2941
+ get busy() {
2942
+ return rootBase().busy;
2943
+ },
2944
+ get transformError() {
2945
+ return rootBase().transformError;
2946
+ },
2947
+ get path() {
2948
+ return rootBase().path;
2949
+ },
2950
+ get id() {
2951
+ return rootBase().id;
2952
+ },
2953
+ get aria() {
2954
+ return rootBase().aria;
2955
+ },
2956
+ get key() {
2957
+ return rootBase().key;
2958
+ },
2959
+ get blank() {
2960
+ return rootBase().blank;
2961
+ },
2962
+ get label() {
2963
+ return rootBase().label;
2964
+ },
2965
+ get description() {
2966
+ return rootBase().description;
2967
+ },
2968
+ get placeholder() {
2969
+ return rootBase().placeholder;
2970
+ },
2971
+ get meta() {
2972
+ return rootBase().meta;
2973
+ },
2974
+ get errorCount() {
2975
+ return rootBase().errors.length;
2976
+ },
2977
+ // Form-level scalars — EAGER reads, tracked on every field-state eval.
2978
+ // They are O(1) refs that never change on a keystroke, so tracking them
2979
+ // per field costs nothing on the hot path. Kept eager (NOT lazy like the
2980
+ // rollup) because behaviors beyond the predicate's own output depend on
2981
+ // every field re-evaluating when they flip — most notably, the display
2982
+ // engine is cleared on submit (revealing held spinners), and that
2983
+ // imperative reset only becomes visible if `submitting` is a tracked dep
2984
+ // of each field. Matches the pre-bust dependency set for these scalars
2985
+ // exactly.
2765
2986
  submitting: state.submitting.value,
2766
2987
  submissionAttempts: state.submissionAttempts.value,
2767
2988
  departAttempts: state.departAttempts.value,
2768
2989
  submitError: state.submitError.value,
2769
- errorCount: rootBase.errors.length,
2770
2990
  submitted: state.submitted.value,
2771
2991
  instanceId: formInstanceId
2772
2992
  };
@@ -2801,24 +3021,21 @@ function buildFormApi(state, formInstanceId, options = {}) {
2801
3021
  const segments = canonicalizePath(pathInput).segments;
2802
3022
  return computed(() => getAtPath(state.form.value, segments));
2803
3023
  }
2804
- function setValueImpl(pathOrValue, maybeValue) {
2805
- if (arguments.length === 1) {
3024
+ function setValueImpl(pathOrValue, maybeValue, maybeOptions) {
3025
+ const argc = arguments.length;
3026
+ const isPathForm = argc >= 2 && (typeof pathOrValue === "string" || Array.isArray(pathOrValue));
3027
+ const options2 = isPathForm ? maybeOptions : argc >= 2 ? maybeValue : void 0;
3028
+ const silent = options2?.silent === true;
3029
+ const writeMeta = (extra) => withInstanceMeta(silent ? { ...extra, silent: true } : extra);
3030
+ if (!isPathForm) {
2806
3031
  const next = typeof pathOrValue === "function" ? pathOrValue(structuralSnapshot(state.form.value)) : pathOrValue;
2807
3032
  const walked2 = walkUnsetSentinels(
2808
3033
  next,
2809
3034
  state.schema
2810
3035
  );
2811
- const ok2 = state.setValueAtPath([], walked2.cleanedValues, withInstanceMeta());
3036
+ const ok2 = state.setValueAtPath([], walked2.cleanedValues, writeMeta());
2812
3037
  if (!ok2) return false;
2813
- for (const pathKey of walked2.paths) {
2814
- const blankSegments = segmentsForPathKey(pathKey);
2815
- if (blankSegments === null) continue;
2816
- state.setValueAtPath(
2817
- blankSegments,
2818
- state.getValueAtPath(blankSegments),
2819
- withInstanceMeta({ blank: true })
2820
- );
2821
- }
3038
+ reMarkBlanksAfterSubstitution(walked2.paths);
2822
3039
  return true;
2823
3040
  }
2824
3041
  const segments = canonicalizePath(pathOrValue).segments;
@@ -2830,7 +3047,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2830
3047
  if (parentDU?.discriminatorKey === last) {
2831
3048
  const slimDefault = state.schema.getEmptyValueAtPath(segments);
2832
3049
  const blank = blankForKind(slimDefault);
2833
- return state.setValueAtPath(segments, blank, withInstanceMeta({ blank: true }));
3050
+ return state.setValueAtPath(segments, blank, writeMeta({ blank: true }));
2834
3051
  }
2835
3052
  }
2836
3053
  const blankPaths = [];
@@ -2841,9 +3058,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
2841
3058
  );
2842
3059
  const segmentsKey = canonicalizePath(segments).key;
2843
3060
  if (blankPaths.length === 1 && blankPaths[0] === segmentsKey) {
2844
- return state.setValueAtPath(segments, expanded, withInstanceMeta({ blank: true }));
3061
+ return state.setValueAtPath(segments, expanded, writeMeta({ blank: true }));
2845
3062
  }
2846
- const ok2 = state.setValueAtPath(segments, expanded, withInstanceMeta());
3063
+ const ok2 = state.setValueAtPath(segments, expanded, writeMeta());
2847
3064
  if (!ok2) return false;
2848
3065
  for (const pathKey of blankPaths) {
2849
3066
  const blankSegments = segmentsForPathKey(pathKey);
@@ -2851,7 +3068,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2851
3068
  state.setValueAtPath(
2852
3069
  blankSegments,
2853
3070
  state.getValueAtPath(blankSegments),
2854
- withInstanceMeta({ blank: true })
3071
+ writeMeta({ blank: true })
2855
3072
  );
2856
3073
  }
2857
3074
  return true;
@@ -2871,17 +3088,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
2871
3088
  segments,
2872
3089
  state.schema
2873
3090
  );
2874
- const ok = state.setValueAtPath(segments, walked.cleanedValues, withInstanceMeta());
3091
+ const ok = state.setValueAtPath(segments, walked.cleanedValues, writeMeta());
2875
3092
  if (!ok) return false;
2876
- for (const pathKey of walked.paths) {
2877
- const blankSegments = segmentsForPathKey(pathKey);
2878
- if (blankSegments === null) continue;
2879
- state.setValueAtPath(
2880
- blankSegments,
2881
- state.getValueAtPath(blankSegments),
2882
- withInstanceMeta({ blank: true })
2883
- );
2884
- }
3093
+ reMarkBlanksAfterSubstitution(walked.paths);
2885
3094
  return true;
2886
3095
  }
2887
3096
  const errorsProxy = buildErrorsProxy(state);
@@ -3250,7 +3459,19 @@ function buildFormApi(state, formInstanceId, options = {}) {
3250
3459
  }
3251
3460
  return Object.freeze(out);
3252
3461
  }
3253
- return {
3462
+ const formHandle = {
3463
+ current: void 0
3464
+ };
3465
+ function onChangeImpl(a, b, c) {
3466
+ const sourced = typeof b === "function";
3467
+ const source = sourced ? a : void 0;
3468
+ const handler = sourced ? b : a;
3469
+ const options2 = sourced ? c : b;
3470
+ const stop = state.registerOnChange(source, handler, options2, () => formHandle.current);
3471
+ if (getCurrentScope() !== void 0) onScopeDispose(stop);
3472
+ return stop;
3473
+ }
3474
+ const api = {
3254
3475
  handleSubmit: gated(handleSubmit),
3255
3476
  // Callable readonly Proxies (`values`, `fields`, `errors`) and the
3256
3477
  // reactive containers (`meta`, `history`, `blankPaths`) are exposed
@@ -3337,8 +3558,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
3337
3558
  get blankPaths() {
3338
3559
  void state.activate();
3339
3560
  return blankPathsView;
3340
- }
3561
+ },
3562
+ onChange: onChangeImpl
3341
3563
  };
3564
+ formHandle.current = api;
3565
+ return api;
3342
3566
  }
3343
3567
 
3344
3568
  const IDLE = Object.freeze({ display: "idle" });
@@ -3765,6 +3989,7 @@ function createArrayBookkeeping(deps) {
3765
3989
  originals,
3766
3990
  blankPaths,
3767
3991
  originalBlankPaths,
3992
+ authoredPaths,
3768
3993
  fieldValidationCounts,
3769
3994
  fieldValidatingSince,
3770
3995
  fieldValidationState,
@@ -3792,6 +4017,7 @@ function createArrayBookkeeping(deps) {
3792
4017
  }));
3793
4018
  migrateSetSubtree(blankPaths, arrayPath, remap);
3794
4019
  migrateSetSubtree(originalBlankPaths, arrayPath, remap);
4020
+ migrateSetSubtree(authoredPaths, arrayPath, remap);
3795
4021
  migrateMapSubtree(fieldValidatingSince, arrayPath, remap, (since) => since);
3796
4022
  migrateMapSubtree(fieldValidationCounts, arrayPath, remap, (count) => count);
3797
4023
  arrayIdentity.applyRemap(arrayPath, remap);
@@ -3835,7 +4061,7 @@ function createArrayBookkeeping(deps) {
3835
4061
  activeValidations.value = Math.max(0, activeValidations.value - 1);
3836
4062
  decFieldValidation(key);
3837
4063
  }
3838
- entry.controller.abort();
4064
+ entry.aborted = true;
3839
4065
  fieldValidationState.delete(key);
3840
4066
  }
3841
4067
  }
@@ -3847,6 +4073,170 @@ function createArrayBookkeeping(deps) {
3847
4073
  };
3848
4074
  }
3849
4075
 
4076
+ const NOOP_STOP = () => {
4077
+ };
4078
+ function isThenable(value) {
4079
+ return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
4080
+ }
4081
+ function isSuppressed(meta) {
4082
+ return meta?.hydration === true || meta?.crossTab === true || meta?.silent === true;
4083
+ }
4084
+ function canonicalizeSourceList(raw) {
4085
+ const list = typeof raw === "string" ? [raw] : raw;
4086
+ const out = [];
4087
+ const seen = /* @__PURE__ */ new Set();
4088
+ for (const entry of list) {
4089
+ const { segments, key } = canonicalizePath(entry);
4090
+ if (seen.has(key)) continue;
4091
+ seen.add(key);
4092
+ out.push({ segments, key, dotted: segmentsToDotted(segments) });
4093
+ }
4094
+ return out;
4095
+ }
4096
+ function makeResolver(source) {
4097
+ if (source === void 0) {
4098
+ const root = [{ segments: ROOT_PATH, key: ROOT_PATH_KEY, dotted: "" }];
4099
+ return () => root;
4100
+ }
4101
+ if (typeof source !== "function" && !isRef(source)) {
4102
+ const fixed = canonicalizeSourceList(source);
4103
+ return () => fixed;
4104
+ }
4105
+ return () => canonicalizeSourceList(toValue(source));
4106
+ }
4107
+ function createOnChangeRegistry(deps) {
4108
+ const handlers = /* @__PURE__ */ new Set();
4109
+ function isCurrent(reg, key, token) {
4110
+ return reg.runs.get(key)?.token === token;
4111
+ }
4112
+ function routeError(reg, error, source, changed, value, previous, attempt, token) {
4113
+ if (reg.onError === void 0) {
4114
+ if (__DEV__) {
4115
+ console.error(
4116
+ `[attaform] onChange handler threw for path '${source.dotted}' \u2014 error swallowed. Pass { onError } to handle it. Original error:`,
4117
+ error
4118
+ );
4119
+ }
4120
+ return;
4121
+ }
4122
+ const retry = () => {
4123
+ if (!isCurrent(reg, source.key, token)) return;
4124
+ runHandler(reg, source, changed, value, previous, attempt + 1);
4125
+ };
4126
+ const errCtx = {
4127
+ path: source.dotted,
4128
+ value,
4129
+ attempt,
4130
+ retry,
4131
+ form: reg.getForm()
4132
+ };
4133
+ try {
4134
+ reg.onError(error, errCtx);
4135
+ } catch (err) {
4136
+ if (__DEV__) console.error("[attaform] onChange onError threw:", err);
4137
+ }
4138
+ }
4139
+ function runHandler(reg, source, changed, value, previous, attempt) {
4140
+ const prior = reg.runs.get(source.key);
4141
+ if (prior) prior.controller.abort();
4142
+ const controller = new AbortController();
4143
+ const token = ++reg.seq;
4144
+ reg.runs.set(source.key, { token, controller });
4145
+ const ctx = {
4146
+ path: source.dotted,
4147
+ previous,
4148
+ signal: controller.signal,
4149
+ attempt,
4150
+ form: reg.getForm(),
4151
+ changed
4152
+ };
4153
+ let result;
4154
+ try {
4155
+ result = reg.handler(value, ctx);
4156
+ } catch (error) {
4157
+ routeError(reg, error, source, changed, value, previous, attempt, token);
4158
+ return;
4159
+ }
4160
+ if (isThenable(result)) {
4161
+ result.then(void 0, (error) => {
4162
+ if (isCurrent(reg, source.key, token)) {
4163
+ routeError(reg, error, source, changed, value, previous, attempt, token);
4164
+ }
4165
+ });
4166
+ }
4167
+ }
4168
+ function dispatch(patches, meta) {
4169
+ if (handlers.size === 0 || isSuppressed(meta)) return;
4170
+ for (const reg of handlers) {
4171
+ let sources;
4172
+ try {
4173
+ sources = reg.resolve();
4174
+ } catch (error) {
4175
+ if (__DEV__) console.error("[attaform] onChange source getter threw:", error);
4176
+ continue;
4177
+ }
4178
+ for (const source of sources) {
4179
+ let changed;
4180
+ for (const patch of patches) {
4181
+ if (isPathPrefix(source.segments, patch.path) || isPathPrefix(patch.path, source.segments)) {
4182
+ (changed ?? (changed = [])).push(segmentsToDotted(patch.path));
4183
+ }
4184
+ }
4185
+ if (changed === void 0) continue;
4186
+ const value = deps.getValueAtPath(source.segments);
4187
+ const previous = reg.previous.has(source.key) ? reg.previous.get(source.key) : value;
4188
+ reg.previous.set(source.key, value);
4189
+ runHandler(reg, source, changed, value, previous, 0);
4190
+ }
4191
+ }
4192
+ }
4193
+ function register(source, handler, options, getForm) {
4194
+ if (deps.ssr) return NOOP_STOP;
4195
+ const reg = {
4196
+ handler,
4197
+ onError: options?.onError,
4198
+ getForm,
4199
+ resolve: makeResolver(source),
4200
+ previous: /* @__PURE__ */ new Map(),
4201
+ runs: /* @__PURE__ */ new Map(),
4202
+ seq: 0
4203
+ };
4204
+ try {
4205
+ for (const { key, segments } of reg.resolve()) {
4206
+ reg.previous.set(key, deps.getValueAtPath(segments));
4207
+ }
4208
+ } catch (error) {
4209
+ if (__DEV__) console.error("[attaform] onChange source getter threw at registration:", error);
4210
+ }
4211
+ handlers.add(reg);
4212
+ let stopped = false;
4213
+ return () => {
4214
+ if (stopped) return;
4215
+ stopped = true;
4216
+ handlers.delete(reg);
4217
+ for (const run of reg.runs.values()) run.controller.abort();
4218
+ reg.runs.clear();
4219
+ reg.previous.clear();
4220
+ };
4221
+ }
4222
+ function dispose() {
4223
+ for (const reg of handlers) {
4224
+ for (const run of reg.runs.values()) run.controller.abort();
4225
+ reg.runs.clear();
4226
+ reg.previous.clear();
4227
+ }
4228
+ handlers.clear();
4229
+ }
4230
+ return {
4231
+ get active() {
4232
+ return handlers.size > 0;
4233
+ },
4234
+ register,
4235
+ dispatch,
4236
+ dispose
4237
+ };
4238
+ }
4239
+
3850
4240
  function isHydratedFieldRecord(value) {
3851
4241
  if (typeof value !== "object" || value === null) return false;
3852
4242
  const r = value;
@@ -3931,6 +4321,17 @@ function walkAuthoredFromConstraints(value, prefix, out) {
3931
4321
  }
3932
4322
  }
3933
4323
  }
4324
+ function freshElementIndices(op) {
4325
+ switch (op.kind) {
4326
+ case "insert":
4327
+ case "replace-at":
4328
+ return [op.index];
4329
+ case "remove":
4330
+ case "swap":
4331
+ case "move":
4332
+ return [];
4333
+ }
4334
+ }
3934
4335
  function walkAuthoredFromSchemaDiff(withDefaults, withoutDefaults, prefix, out) {
3935
4336
  if (isPlainRecord(withDefaults) && isPlainRecord(withoutDefaults)) {
3936
4337
  const left = withDefaults;
@@ -3969,6 +4370,7 @@ function createFormStore(options) {
3969
4370
  const formChangeListeners = /* @__PURE__ */ new Set();
3970
4371
  const submitSuccessListeners = /* @__PURE__ */ new Set();
3971
4372
  const resetListeners = /* @__PURE__ */ new Set();
4373
+ const onChangeRegistry = createOnChangeRegistry({ getValueAtPath, ssr });
3972
4374
  const persistOptIns = createPersistOptInRegistry();
3973
4375
  const noSyncPaths = /* @__PURE__ */ new Set();
3974
4376
  const noSyncPathCounts = /* @__PURE__ */ new Map();
@@ -4007,11 +4409,8 @@ function createFormStore(options) {
4007
4409
  if (constraints !== void 0) {
4008
4410
  walkAuthoredFromConstraints(constraints, [], authoredPaths);
4009
4411
  }
4010
- const slimResponse = schema.getDefaultValues({
4011
- useDefaultSchemaValues: false,
4012
- strict
4013
- });
4014
- walkAuthoredFromSchemaDiff(schemaWithDefaultsData, slimResponse.data, [], authoredPaths);
4412
+ const slimBaseline = schema.getEmptyValueAtPath([]);
4413
+ walkAuthoredFromSchemaDiff(schemaWithDefaultsData, slimBaseline, [], authoredPaths);
4015
4414
  }
4016
4415
  rebuildAuthoredPaths(defaultValues, schemaInitialData);
4017
4416
  function filterAuthoredErrors(errors) {
@@ -4030,7 +4429,7 @@ function createFormStore(options) {
4030
4429
  });
4031
4430
  const form = ref(stubbedInitialData);
4032
4431
  const arrayIdentity = createArrayIdentity((arraySegs) => {
4033
- const v = getAtPath(form.value, arraySegs);
4432
+ const v = getAtPath(toRaw(form.value), arraySegs);
4034
4433
  return Array.isArray(v) ? v.length : 0;
4035
4434
  });
4036
4435
  function arrayElementKey(path) {
@@ -4073,14 +4472,7 @@ function createFormStore(options) {
4073
4472
  const segments = segmentsForPathKey(pathKey);
4074
4473
  if (segments === null) continue;
4075
4474
  if (!schema.isRequiredAtPath(segments)) continue;
4076
- result.set(pathKey, [
4077
- {
4078
- message: "No value supplied",
4079
- path: [...segments],
4080
- formKey,
4081
- code: AttaformErrorCode.NoValueSupplied
4082
- }
4083
- ]);
4475
+ result.set(pathKey, [makeBlankRequiredError(segments, formKey)]);
4084
4476
  }
4085
4477
  return result;
4086
4478
  });
@@ -4320,6 +4712,7 @@ function createFormStore(options) {
4320
4712
  originals,
4321
4713
  blankPaths,
4322
4714
  originalBlankPaths,
4715
+ authoredPaths,
4323
4716
  fieldValidationCounts,
4324
4717
  fieldValidatingSince,
4325
4718
  fieldValidationState,
@@ -4329,17 +4722,8 @@ function createFormStore(options) {
4329
4722
  touchFieldRecord,
4330
4723
  decFieldValidation
4331
4724
  });
4332
- function applyFormReplacement(next, meta) {
4333
- const prev = form.value;
4334
- if (Object.is(prev, next)) return;
4725
+ function commitWritePatches(patches, meta) {
4335
4726
  const now = (/* @__PURE__ */ new Date()).toISOString();
4336
- const patches = [];
4337
- diffAndApply(prev, next, [], (patch) => {
4338
- patches.push(patch);
4339
- });
4340
- if (!applyChangedKeys(prev, next)) {
4341
- form.value = next;
4342
- }
4343
4727
  for (const patch of patches) {
4344
4728
  const { key } = canonicalizePath(patch.path);
4345
4729
  if (patch.kind === "added" && !originals.has(key)) {
@@ -4354,10 +4738,63 @@ function createFormStore(options) {
4354
4738
  console.error("[attaform] onFormChange threw:", err);
4355
4739
  }
4356
4740
  }
4741
+ if (onChangeRegistry.active) onChangeRegistry.dispatch(patches, meta);
4742
+ }
4743
+ function applyFormReplacementWithPath(next, meta, arrayOpPath) {
4744
+ const prev = form.value;
4745
+ if (Object.is(prev, next)) return;
4746
+ const patches = [];
4747
+ diffAndApply(prev, next, [], (patch) => {
4748
+ patches.push(patch);
4749
+ });
4750
+ if (!applyChangedKeys(prev, next, arrayOpPath, [])) {
4751
+ form.value = next;
4752
+ } else if (patches.some(
4753
+ (p) => p.path.length > 0 && typeof p.path[0] === "string" && isShadowedKey(p.path[0])
4754
+ )) {
4755
+ triggerRef(form);
4756
+ }
4757
+ commitWritePatches(patches, meta);
4758
+ }
4759
+ function applyFormReplacement(next, meta) {
4760
+ applyFormReplacementWithPath(next, meta, null);
4761
+ }
4762
+ function applyTargetedWrite(path, completedValue, meta) {
4763
+ const result = tryInPlaceLeafWrite(form.value, path, completedValue);
4764
+ if (!result.applied) {
4765
+ applyFormReplacementWithPath(
4766
+ setAtPathWithSchemaFill(form.value, schema, path, completedValue),
4767
+ meta,
4768
+ meta?.arrayOp !== void 0 ? path : null
4769
+ );
4770
+ return;
4771
+ }
4772
+ const patches = [];
4773
+ diffAndApply(result.old, completedValue, path, (patch) => {
4774
+ patches.push(patch);
4775
+ });
4776
+ commitWritePatches(patches, meta);
4357
4777
  }
4358
4778
  function setValueAtPath(path, value, meta) {
4359
- value = stripSymbolsDeep(value);
4360
- if (!isSlimPrimitiveValid(schema, form, path, value)) {
4779
+ if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4780
+ for (const idx of freshElementIndices(meta.arrayOp)) {
4781
+ value[idx] = stripSymbolsDeep(value[idx]);
4782
+ }
4783
+ } else {
4784
+ value = stripSymbolsDeep(value);
4785
+ }
4786
+ let slimOk = true;
4787
+ if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4788
+ for (const idx of freshElementIndices(meta.arrayOp)) {
4789
+ if (!isSlimPrimitiveValid(schema, form, [...path, idx], value[idx])) {
4790
+ slimOk = false;
4791
+ break;
4792
+ }
4793
+ }
4794
+ } else {
4795
+ slimOk = isSlimPrimitiveValid(schema, form, path, value);
4796
+ }
4797
+ if (!slimOk) {
4361
4798
  return false;
4362
4799
  }
4363
4800
  if (path.length >= 2) {
@@ -4450,22 +4887,37 @@ function createFormStore(options) {
4450
4887
  }
4451
4888
  }
4452
4889
  }
4890
+ const currentValue = getAtPath(form.value, path);
4453
4891
  const pathKey = canonicalizePath(path).key;
4454
4892
  if (meta?.blank === true) {
4455
4893
  blankPaths.add(pathKey);
4456
4894
  } else {
4457
4895
  if (blankPaths.has(pathKey)) blankPaths.delete(pathKey);
4458
- if (meta?.arrayOp === void 0) {
4896
+ if (meta?.arrayOp === void 0 && (isPlainRecord(currentValue) || Array.isArray(currentValue))) {
4459
4897
  for (const existingKey of [...blankPaths]) {
4460
4898
  if (isPathKeyUnder(existingKey, path)) blankPaths.delete(existingKey);
4461
4899
  }
4462
4900
  }
4463
4901
  }
4464
4902
  const wasAuthoredBefore = authoredPaths.has(pathKey);
4465
- walkAuthoredFromConstraints(value, path, authoredPaths);
4903
+ if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4904
+ if (path.length > 0) authoredPaths.add(pathKey);
4905
+ for (const idx of freshElementIndices(meta.arrayOp)) {
4906
+ walkAuthoredFromConstraints(value[idx], [...path, idx], authoredPaths);
4907
+ }
4908
+ } else {
4909
+ walkAuthoredFromConstraints(value, path, authoredPaths);
4910
+ }
4466
4911
  const newlyAuthored = !wasAuthoredBefore && authoredPaths.has(pathKey);
4467
- const completedValue = mergeStructural(schema, path, value);
4468
- const currentValue = getAtPath(form.value, path);
4912
+ let completedValue;
4913
+ if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4914
+ for (const idx of freshElementIndices(meta.arrayOp)) {
4915
+ value[idx] = mergeStructural(schema, [...path, idx], value[idx]);
4916
+ }
4917
+ completedValue = value;
4918
+ } else {
4919
+ completedValue = mergeStructural(schema, path, value);
4920
+ }
4469
4921
  if (Object.is(currentValue, completedValue)) {
4470
4922
  if (newlyAuthored && schema.isPreprocessOrCoerceLeaf(path)) {
4471
4923
  const modeForAuthoringTransition = meta?.instance?.validateOn ?? fieldValidationMode;
@@ -4479,8 +4931,7 @@ function createFormStore(options) {
4479
4931
  return true;
4480
4932
  }
4481
4933
  const oldArrayLength = Array.isArray(currentValue) ? currentValue.length : 0;
4482
- const nextForm = setAtPathWithSchemaFill(form.value, schema, path, completedValue);
4483
- applyFormReplacement(nextForm, meta);
4934
+ applyTargetedWrite(path, completedValue, meta);
4484
4935
  if (meta?.arrayOp !== void 0) {
4485
4936
  const remap = remapForOp(meta.arrayOp, oldArrayLength);
4486
4937
  arrayBookkeeping.migrateElementState(path, remap);
@@ -4563,7 +5014,7 @@ function createFormStore(options) {
4563
5014
  const prevValidation = fieldValidationState.get(parentKey2);
4564
5015
  if (prevValidation !== void 0) {
4565
5016
  if (prevValidation.timer !== null) clearTimeout(prevValidation.timer);
4566
- prevValidation.controller.abort();
5017
+ prevValidation.aborted = true;
4567
5018
  fieldValidationState.delete(parentKey2);
4568
5019
  }
4569
5020
  appliedSync = true;
@@ -4587,15 +5038,19 @@ function createFormStore(options) {
4587
5038
  const prev = fieldValidationState.get(key);
4588
5039
  if (prev !== void 0) {
4589
5040
  if (prev.timer !== null) clearTimeout(prev.timer);
4590
- prev.controller.abort();
5041
+ prev.aborted = true;
4591
5042
  }
4592
- const controller = new AbortController();
4593
- const fresh = { controller, timer: null, settled: false, released: false };
5043
+ const fresh = {
5044
+ aborted: false,
5045
+ timer: null,
5046
+ settled: false,
5047
+ released: false
5048
+ };
4594
5049
  fieldValidationState.set(key, fresh);
4595
5050
  const myEpoch = ++scheduleEpoch;
4596
5051
  const run = () => {
4597
5052
  fresh.timer = null;
4598
- if (controller.signal.aborted) return;
5053
+ if (fresh.aborted) return;
4599
5054
  let activeIncremented = false;
4600
5055
  try {
4601
5056
  activeValidations.value += 1;
@@ -4612,7 +5067,7 @@ function createFormStore(options) {
4612
5067
  const dataAtScope = subtreeScope ? getAtPath(form.value, path) : form.value;
4613
5068
  const scopeKey = subtreeScope ? canonicalizePath(path).key : ROOT_PATH_KEY;
4614
5069
  void Promise.resolve().then(() => schema.validateAtPath(dataAtScope, scopePath)).then((response) => {
4615
- if (controller.signal.aborted) return;
5070
+ if (fresh.aborted) return;
4616
5071
  if (myEpoch <= lastCommittedEpoch) return;
4617
5072
  lastCommittedEpoch = myEpoch;
4618
5073
  if (effectiveMode === "blur") {
@@ -4649,7 +5104,7 @@ function createFormStore(options) {
4649
5104
  activeValidations.value = Math.max(0, activeValidations.value - 1);
4650
5105
  decFieldValidation(pkey);
4651
5106
  }
4652
- entry.controller.abort();
5107
+ entry.aborted = true;
4653
5108
  }
4654
5109
  fieldValidationState.clear();
4655
5110
  }
@@ -4665,7 +5120,7 @@ function createFormStore(options) {
4665
5120
  decFieldValidation(key);
4666
5121
  entry.released = true;
4667
5122
  }
4668
- entry.controller.abort();
5123
+ entry.aborted = true;
4669
5124
  fieldValidationState.delete(key);
4670
5125
  }
4671
5126
  }
@@ -4724,6 +5179,7 @@ function createFormStore(options) {
4724
5179
  formChangeListeners.clear();
4725
5180
  submitSuccessListeners.clear();
4726
5181
  resetListeners.clear();
5182
+ onChangeRegistry.dispose();
4727
5183
  persistOptIns.clear();
4728
5184
  noSyncPaths.clear();
4729
5185
  noSyncPathCounts.clear();
@@ -5029,7 +5485,7 @@ function createFormStore(options) {
5029
5485
  });
5030
5486
  const next = resetResponse.data;
5031
5487
  rebuildAuthoredPaths(resetSource, next);
5032
- applyFormReplacement(next);
5488
+ applyFormReplacement(next, { silent: true });
5033
5489
  arrayIdentity.rebaselineAll();
5034
5490
  originals.clear();
5035
5491
  diffAndApply({}, next, [], (patch) => {
@@ -5275,6 +5731,7 @@ function createFormStore(options) {
5275
5731
  settleTransforms,
5276
5732
  scheduleFieldValidation,
5277
5733
  onFormChange,
5734
+ registerOnChange: onChangeRegistry.register,
5278
5735
  onSubmitSuccess,
5279
5736
  onReset,
5280
5737
  emitSubmitSuccess,
@@ -5304,6 +5761,13 @@ function captureErrorEntries(map) {
5304
5761
  for (const [k, v] of map) out.push([k, [...v]]);
5305
5762
  return out;
5306
5763
  }
5764
+ function errorFieldsEqual(av, bvi) {
5765
+ if (av === bvi) return true;
5766
+ if (av.message !== bvi.message) return false;
5767
+ if (av.code !== bvi.code) return false;
5768
+ if (av.formKey !== bvi.formKey) return false;
5769
+ return av.path === bvi.path || pathsEqual(av.path, bvi.path);
5770
+ }
5307
5771
  function errorsEqual(a, b) {
5308
5772
  if (a.length !== b.length) return false;
5309
5773
  const bMap = /* @__PURE__ */ new Map();
@@ -5313,18 +5777,7 @@ function errorsEqual(a, b) {
5313
5777
  if (bv === void 0) return false;
5314
5778
  if (v.length !== bv.length) return false;
5315
5779
  for (let i = 0; i < v.length; i++) {
5316
- const av = v[i];
5317
- const bvi = bv[i];
5318
- if (av === bvi) continue;
5319
- if (av.message !== bvi.message) return false;
5320
- if (av.code !== bvi.code) return false;
5321
- if (av.formKey !== bvi.formKey) return false;
5322
- if (av.path !== bvi.path) {
5323
- if (av.path.length !== bvi.path.length) return false;
5324
- for (let j = 0; j < av.path.length; j++) {
5325
- if (av.path[j] !== bvi.path[j]) return false;
5326
- }
5327
- }
5780
+ if (!errorFieldsEqual(v[i], bv[i])) return false;
5328
5781
  }
5329
5782
  }
5330
5783
  return true;
@@ -5692,11 +6145,16 @@ function useAbstractForm(configuration, options) {
5692
6145
  if (merged.autoAria !== void 0) {
5693
6146
  apiOptions.autoAria = merged.autoAria;
5694
6147
  }
5695
- return buildFormApi(
5696
- state,
5697
- formInstanceId,
5698
- apiOptions
5699
- );
6148
+ const api = buildFormApi(state, formInstanceId, apiOptions);
6149
+ const onChangeConfig = configuration.onChange;
6150
+ if (onChangeConfig !== void 0) {
6151
+ const handler = typeof onChangeConfig === "function" ? onChangeConfig : onChangeConfig.handler;
6152
+ const onError = typeof onChangeConfig === "function" ? void 0 : onChangeConfig.onError;
6153
+ const options2 = onError === void 0 ? void 0 : { onError };
6154
+ const stop = state.registerOnChange(void 0, handler, options2, () => api);
6155
+ if (getCurrentScope() !== void 0) onScopeDispose(stop);
6156
+ }
6157
+ return api;
5700
6158
  }
5701
6159
  function mergeWithDefaults(defaults, configuration) {
5702
6160
  const strict = configuration.strict ?? defaults.strict;
@@ -5906,6 +6364,99 @@ function warnIfAmbientProviderHadDuplicates() {
5906
6364
  }
5907
6365
  }
5908
6366
 
6367
+ const warnedNoParentRV = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
6368
+ let warnedOutsideSetup = false;
6369
+ function makeRegisterValueProxy(capturedRegisterValue) {
6370
+ return new Proxy({}, {
6371
+ get(_target, prop) {
6372
+ if (prop === "__v_isRef") return true;
6373
+ if (prop === "value") return capturedRegisterValue.value;
6374
+ const v = capturedRegisterValue.value;
6375
+ if (v === void 0) return void 0;
6376
+ return Reflect.get(v, prop);
6377
+ },
6378
+ has(_target, prop) {
6379
+ if (prop === "__v_isRef" || prop === "value") return true;
6380
+ const v = capturedRegisterValue.value;
6381
+ if (v === void 0) return false;
6382
+ return Reflect.has(v, prop);
6383
+ },
6384
+ ownKeys(_target) {
6385
+ const v = capturedRegisterValue.value;
6386
+ if (v === void 0) return [];
6387
+ return Reflect.ownKeys(v);
6388
+ },
6389
+ getOwnPropertyDescriptor(_target, prop) {
6390
+ const v = capturedRegisterValue.value;
6391
+ if (v === void 0) return void 0;
6392
+ const desc = Reflect.getOwnPropertyDescriptor(v, prop);
6393
+ if (desc !== void 0) {
6394
+ desc.configurable = true;
6395
+ }
6396
+ return desc;
6397
+ }
6398
+ });
6399
+ }
6400
+ function useRegister() {
6401
+ const instance = getCurrentInstance();
6402
+ if (instance === null) {
6403
+ warnOutsideSetup();
6404
+ return makeRegisterValueProxy(shallowRef(void 0));
6405
+ }
6406
+ ensureAttaformInstalled(instance.appContext.app);
6407
+ const capturedRegisterValue = shallowRef(void 0);
6408
+ const refreshAndStripBridgeAttrs = () => {
6409
+ const rawAttrs = instance.attrs;
6410
+ if ("registerValue" in rawAttrs) {
6411
+ capturedRegisterValue.value = rawAttrs["registerValue"];
6412
+ delete rawAttrs["registerValue"];
6413
+ } else {
6414
+ const dirs = instance.vnode.dirs;
6415
+ if (dirs !== null && dirs !== void 0) {
6416
+ for (const dir of dirs) {
6417
+ const marked = dir.dir?.[V_REGISTER_MARKER];
6418
+ if (marked === true) {
6419
+ capturedRegisterValue.value = dir.value;
6420
+ break;
6421
+ }
6422
+ }
6423
+ }
6424
+ }
6425
+ if ("value" in rawAttrs) delete rawAttrs["value"];
6426
+ };
6427
+ refreshAndStripBridgeAttrs();
6428
+ onBeforeMount(refreshAndStripBridgeAttrs);
6429
+ onBeforeUpdate(refreshAndStripBridgeAttrs);
6430
+ onMounted(() => {
6431
+ const el = instance.vnode.el;
6432
+ if (el !== null && el !== void 0 && typeof el === "object") {
6433
+ el[REGISTER_OWNER_MARKER] = true;
6434
+ }
6435
+ if (capturedRegisterValue.value === void 0) {
6436
+ warnNoParentRV(instance);
6437
+ }
6438
+ });
6439
+ return makeRegisterValueProxy(capturedRegisterValue);
6440
+ }
6441
+ function warnOutsideSetup() {
6442
+ if (!__DEV__) return;
6443
+ if (warnedOutsideSetup) return;
6444
+ warnedOutsideSetup = true;
6445
+ const frame = captureUserCallSite();
6446
+ console.warn(
6447
+ `[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}` : "")
6448
+ );
6449
+ }
6450
+ function warnNoParentRV(instance) {
6451
+ if (!__DEV__ || warnedNoParentRV === null) return;
6452
+ if (warnedNoParentRV.has(instance)) return;
6453
+ warnedNoParentRV.add(instance);
6454
+ const frame = captureUserCallSite();
6455
+ console.warn(
6456
+ `[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}` : "")
6457
+ );
6458
+ }
6459
+
5909
6460
  const LAZY_BRAND = Symbol.for("attaform/wizard-lazy");
5910
6461
  function lazy(resolve) {
5911
6462
  return { [LAZY_BRAND]: true, resolve };
@@ -6969,5 +7520,5 @@ function warnIfAmbientWizardProviderHadDuplicates() {
6969
7520
  }
6970
7521
  }
6971
7522
 
6972
- export { AttaformErrorCode as A, useAbstractForm as B, useWizard as C, DEFAULT_PERSISTENCE_DEBOUNCE_MS as D, PERSISTENCE_MODULE_KEY as P, DEFAULT_TIMINGS as a, applyPatchesForward as b, cleanupOrphanKeys as c, defaultCoercionRules as d, defaultDisplayState as e, defineCoercion as f, deleteAtPath as g, diffAndApply as h, getAtPath as i, humanize as j, injectForm as k, injectWizard as l, isPlainRecord as m, isUnset as n, lazy as o, makeDefaultDisplayState as p, mergeSparseHydration as q, normalizeNumericOption as r, normalizePersistConfig as s, resolveStorageKeyBase as t, safeAssign as u, safeOwnRead as v, setAtPath as w, slimKindOf as x, structuralSnapshot as y, unset as z };
6973
- //# sourceMappingURL=attaform.DR6RmxWZ.mjs.map
7523
+ export { AttaformErrorCode as A, deleteAtPath as B, safeOwnRead as C, DEFAULT_TIMINGS as D, humanize as E, PERSISTENCE_MODULE_KEY as P, injectWizard as a, isUnset as b, useRegister as c, useWizard as d, isPlainRecord as e, safeAssign as f, diffAndApply as g, slimKindOf as h, injectForm as i, applyPatchesForward as j, normalizeNumericOption as k, lazy as l, defaultCoercionRules as m, normalizePersistConfig as n, defaultDisplayState as o, defineCoercion as p, makeDefaultDisplayState as q, useAbstractForm as r, structuralSnapshot as s, getAtPath as t, unset as u, setAtPath as v, resolveStorageKeyBase as w, DEFAULT_PERSISTENCE_DEBOUNCE_MS as x, cleanupOrphanKeys as y, mergeSparseHydration as z };
7524
+ //# sourceMappingURL=attaform.CsB-iKbU.mjs.map