attaform 0.28.0 → 0.30.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 (66) hide show
  1. package/dist/abstract.d.mts +5 -5
  2. package/dist/abstract.mjs +4 -4
  3. package/dist/dev/abstract.mjs +4 -4
  4. package/dist/dev/chunks/dev-key-collision-warnings.mjs +47 -8
  5. package/dist/dev/chunks/devtools.mjs +1 -1
  6. package/dist/dev/directive.mjs +1 -1
  7. package/dist/dev/history.mjs +1 -2
  8. package/dist/dev/index.mjs +5 -5
  9. package/dist/dev/runtime/plugins/attaform.mjs +3 -3
  10. package/dist/dev/shared/{attaform-dev.oYrkP9dj.mjs → attaform-dev.BK6Ge2qR.mjs} +80 -341
  11. package/dist/dev/shared/{attaform-dev.Dr2YzS5B.mjs → attaform-dev.BQc5QV3Y.mjs} +11 -19
  12. package/dist/dev/shared/{attaform-dev.BR8u0YOZ.mjs → attaform-dev.BcNtyOmf.mjs} +4 -4
  13. package/dist/{shared/attaform.h0i6LijT.mjs → dev/shared/attaform-dev.BcYT6tFh.mjs} +152 -20
  14. package/dist/dev/shared/{attaform-dev.6qxD-Yqo.mjs → attaform-dev.BisFstxh.mjs} +227 -88
  15. package/dist/dev/shared/{attaform-dev.WmuhD_CZ.mjs → attaform-dev.CQ2rJiAX.mjs} +833 -656
  16. package/dist/dev/shared/{attaform-dev.P7cseiEG.mjs → attaform-dev.D6CLvaQT.mjs} +20 -11
  17. package/dist/dev/shared/{attaform-dev.D2I8e_Mm.mjs → attaform-dev.DTrHonpc.mjs} +225 -238
  18. package/dist/dev/shared/{attaform-dev.BXE2cr0L.mjs → attaform-dev.DUZckyV8.mjs} +1 -3
  19. package/dist/dev/zod-v3.mjs +6 -6
  20. package/dist/dev/zod-v4.mjs +6 -6
  21. package/dist/dev/zod.mjs +5 -5
  22. package/dist/directive.d.mts +2 -2
  23. package/dist/directive.mjs +1 -1
  24. package/dist/history.d.mts +1 -1
  25. package/dist/history.mjs +1 -2
  26. package/dist/index.d.mts +9 -7
  27. package/dist/index.mjs +5 -5
  28. package/dist/nuxt.d.mts +11 -20
  29. package/dist/nuxt.mjs +0 -1
  30. package/dist/runtime/plugins/attaform.mjs +3 -3
  31. package/dist/shared/{attaform.uLbun31C.mjs → attaform.B3resMQ1.mjs} +19 -11
  32. package/dist/shared/{attaform.Cs2Rbdlo.mjs → attaform.BK0cgu2K.mjs} +123 -139
  33. package/dist/{dev/shared/attaform-dev.By4PRjt0.mjs → shared/attaform.BRDDQeHV.mjs} +136 -26
  34. package/dist/shared/{attaform.NLmerepI.mjs → attaform.BXneBxEj.mjs} +71 -334
  35. package/dist/shared/{attaform.D6DCnnQu.mjs → attaform.ByWP6kSx.mjs} +4 -4
  36. package/dist/shared/{attaform.CJP3RkGa.mjs → attaform.CJ8DpRGj.mjs} +223 -238
  37. package/dist/shared/{attaform.C09fpdLS.mjs → attaform.CUfrMK7t.mjs} +227 -88
  38. package/dist/shared/{attaform.wfY8XFvm.mjs → attaform.CdZIEf7_.mjs} +11 -19
  39. package/dist/shared/{attaform.JfSWdEcb.d.mts → attaform.Clz0ArIi.d.mts} +1 -1
  40. package/dist/shared/{attaform.B8k8JrmK.mjs → attaform.DNJOWjQw.mjs} +826 -612
  41. package/dist/shared/{attaform.B4VkS43-.d.mts → attaform.Rc9u4Vfw.d.mts} +1 -1
  42. package/dist/shared/{attaform.D5V4tZWq.d.mts → attaform.UGyOK83T.d.mts} +200 -218
  43. package/dist/shared/{attaform.Dq4vWxuG.mjs → attaform.WiGdIqgx.mjs} +1 -3
  44. package/dist/shared/{attaform.Cgu-hdSb.d.mts → attaform.bKLzMU0o.d.mts} +321 -523
  45. package/dist/transforms.d.mts +0 -22
  46. package/dist/transforms.mjs +1 -1
  47. package/dist/vite.d.mts +13 -4
  48. package/dist/vite.mjs +8 -2
  49. package/dist/zod-v3.d.mts +6 -4
  50. package/dist/zod-v3.mjs +6 -6
  51. package/dist/zod-v4.d.mts +18 -31
  52. package/dist/zod-v4.mjs +6 -6
  53. package/dist/zod.d.mts +4 -4
  54. package/dist/zod.mjs +5 -5
  55. package/package.json +19 -15
  56. package/skills/attaform/SKILL.md +6 -3
  57. package/skills/attaform/references/custom-components.md +8 -3
  58. package/skills/attaform/references/errors.md +8 -3
  59. package/skills/attaform/references/saving.md +77 -0
  60. package/skills/attaform/references/wizards.md +10 -8
  61. package/dist/chunks/fingerprint.mjs +0 -183
  62. package/dist/chunks/fingerprint2.mjs +0 -159
  63. package/dist/dev/chunks/fingerprint.mjs +0 -183
  64. package/dist/dev/chunks/fingerprint2.mjs +0 -159
  65. package/dist/dev/shared/attaform-dev.BJGA_UOS.mjs +0 -36
  66. package/dist/shared/attaform.BJGA_UOS.mjs +0 -36
@@ -1,8 +1,8 @@
1
- import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, nextTick, warn, reactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope } from 'vue';
2
- import { A as ANONYMOUS_FORM_KEY_PREFIX, i as isShadowedKey, c as safeOwnHas, a as safeOwnRead, b as safeAssign, d as diffAndApply, p as pickDefined, s as structuralSnapshot, n as normalizeNumericOption, e as DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS, f as applyChangedKeys, R as RESERVED_KEY_PREFIX, g as DEFAULT_MAX_RECURSION_DEPTH, h as ANONYMOUS_WIZARD_KEY_PREFIX } from './attaform-dev.By4PRjt0.mjs';
3
- import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform-dev.BXE2cr0L.mjs';
4
- import { R as REGISTER_OWNER_MARKER, d as armDomBinding, V as V_REGISTER_MARKER } from './attaform-dev.P7cseiEG.mjs';
5
- import { i as isPathPrefix, e as canonicalizePath, h as cellEntriesFor, s as segmentsForPathKey, k as keyForSegments, b as ROOT_PATH_KEY, S as SubmitErrorHandlerError, t as toError, j as groupErrorsByKey, n as normalizeErrorInput, R as ROOT_PATH, l as normalizeErrorInputs, m as coerceToPathKey, a as InvalidUseFormConfigError, d as ReservedFormKeyError } from './attaform-dev.Dr2YzS5B.mjs';
1
+ import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, nextTick, warn, reactive, shallowReactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope } from 'vue';
2
+ import { A as ANONYMOUS_FORM_KEY_PREFIX, i as isShadowedKey, d as safeOwnHas, a as safeOwnRead, e as consumerHas, f as spreadConsumerRecord, g as copyConsumerArray, r as readConsumerIndex, b as safeAssign, h as consumerKeys, j as readConsumerProp, k as diffAndApply, p as pickDefined, s as structuralSnapshot, n as normalizeNumericOption, l as DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS, m as consumerSymbolKeys, o as applyChangedKeys, R as RESERVED_KEY_PREFIX, q as DEFAULT_MAX_RECURSION_DEPTH, t as ANONYMOUS_WIZARD_KEY_PREFIX } from './attaform-dev.BcYT6tFh.mjs';
3
+ import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform-dev.DUZckyV8.mjs';
4
+ import { R as REGISTER_OWNER_MARKER, d as armDomBinding, V as V_REGISTER_MARKER } from './attaform-dev.D6CLvaQT.mjs';
5
+ import { i as isPathPrefix, e as canonicalizePath, k as keyForSegments, m as mapSegmentKeys, b as ROOT_PATH_KEY, s as segmentsForPathKey, S as SubmitErrorHandlerError, t as toError, j as groupErrorsByKey, n as normalizeErrorInput, h as SET_MEMBER_SEGMENT, R as ROOT_PATH, l as normalizeErrorInputs, a as InvalidUseFormConfigError, d as ReservedFormKeyError } from './attaform-dev.BQc5QV3Y.mjs';
6
6
 
7
7
  const AttaformErrorCode = {
8
8
  /** A required field is in the blank set — user hasn't supplied a value. */
@@ -98,42 +98,32 @@ function earliestNonNull(a, b) {
98
98
  return a < b ? a : b;
99
99
  }
100
100
  const DEFAULT_TIMINGS = { showDelay: 120, minVisible: 120 };
101
+ const { showDelay, minVisible } = DEFAULT_TIMINGS;
101
102
  const FOCUS_OUT_GRACE = 16;
102
- const defaultFamily = /* @__PURE__ */ new WeakSet();
103
- function isDefaultDisplayState(fn) {
104
- return defaultFamily.has(fn);
105
- }
106
- function makeDefaultDisplayState({
107
- showDelay,
108
- minVisible
109
- }) {
110
- const reducer = (prev, { field, formMeta, validatingSince, transformingSince, now }) => {
111
- const verdict = computeVerdict(field, formMeta);
112
- if (!isGateOpen(field, formMeta)) return { display: verdict };
113
- const inFlightSince = earliestNonNull(validatingSince, transformingSince);
114
- if (inFlightSince === null) {
115
- if (prev.display === "pending") {
116
- const shownAt = prev.pendingShownAt ?? now;
117
- if (now < shownAt + minVisible)
118
- return { display: "pending", pendingShownAt: shownAt, reviewAt: shownAt + minVisible };
119
- }
120
- return { display: verdict };
121
- }
122
- if (prev.display === "pending")
123
- return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
124
- const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
125
- if (now - inFlightSince < window) {
126
- return { display: prev.display, reviewAt: inFlightSince + window };
103
+ const defaultDisplayState = (prev, { field, formMeta, validatingSince, transformingSince, now }) => {
104
+ const verdict = computeVerdict(field, formMeta);
105
+ if (!isGateOpen(field, formMeta)) return { display: verdict };
106
+ const inFlightSince = earliestNonNull(validatingSince, transformingSince);
107
+ if (inFlightSince === null) {
108
+ if (prev.display === "pending") {
109
+ const shownAt = prev.pendingShownAt ?? now;
110
+ if (now < shownAt + minVisible)
111
+ return {
112
+ display: "pending",
113
+ pendingShownAt: shownAt,
114
+ reviewAt: shownAt + minVisible
115
+ };
127
116
  }
128
- return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
129
- };
130
- defaultFamily.add(reducer);
131
- return reducer;
132
- }
133
- const defaultDisplayState = makeDefaultDisplayState(DEFAULT_TIMINGS);
134
- function resolveGetDisplayState(config) {
135
- return config ?? defaultDisplayState;
136
- }
117
+ return { display: verdict };
118
+ }
119
+ if (prev.display === "pending")
120
+ return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
121
+ const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
122
+ if (now - inFlightSince < window) {
123
+ return { display: prev.display, reviewAt: inFlightSince + window };
124
+ }
125
+ return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
126
+ };
137
127
 
138
128
  function hashStableString(input, seed = 0) {
139
129
  let h1 = 3735928559 ^ seed;
@@ -186,6 +176,17 @@ function humanize(segment) {
186
176
  }
187
177
 
188
178
  const NOT_FOUND = /* @__PURE__ */ Symbol("NOT_FOUND");
179
+ function mapKeyForSegment(map, segment, declared) {
180
+ if (map.has(segment)) return segment;
181
+ if (typeof segment !== "number") return segment;
182
+ const asString = String(segment);
183
+ if (map.has(asString)) return asString;
184
+ return declared === "string" ? asString : segment;
185
+ }
186
+ function isRebuildableContainer(root, segment) {
187
+ if (root === null || root === void 0 || typeof root !== "object") return true;
188
+ return typeof segment === "number" ? Array.isArray(root) : isPlainRecord(root);
189
+ }
189
190
  function descendStep(value, segment) {
190
191
  if (value === null || value === void 0) return NOT_FOUND;
191
192
  if (typeof value !== "object") return NOT_FOUND;
@@ -194,6 +195,11 @@ function descendStep(value, segment) {
194
195
  if (!(segment in value)) return NOT_FOUND;
195
196
  return value[segment];
196
197
  }
198
+ if (value instanceof Map) {
199
+ const key2 = mapKeyForSegment(value, segment);
200
+ if (!value.has(key2)) return NOT_FOUND;
201
+ return value.get(key2);
202
+ }
197
203
  const record = value;
198
204
  const key = typeof segment === "number" ? String(segment) : segment;
199
205
  if (isShadowedKey(key)) {
@@ -205,16 +211,27 @@ function descendStep(value, segment) {
205
211
  }
206
212
  function getAtPath(root, path) {
207
213
  if (path.length === 0) return root;
208
- let current = root;
209
- for (const segment of path) {
210
- const next = descendStep(current, segment);
211
- if (next === NOT_FOUND) return void 0;
212
- current = next;
214
+ try {
215
+ let current = root;
216
+ for (const segment of path) {
217
+ const next = descendStep(current, segment);
218
+ if (next === NOT_FOUND) return void 0;
219
+ current = next;
220
+ }
221
+ return current;
222
+ } catch {
223
+ return void 0;
213
224
  }
214
- return current;
215
225
  }
216
226
  function hasAtPath(root, path) {
217
227
  if (path.length === 0) return true;
228
+ try {
229
+ return hasAtPathUnguarded(root, path);
230
+ } catch {
231
+ return false;
232
+ }
233
+ }
234
+ function hasAtPathUnguarded(root, path) {
218
235
  let current = root;
219
236
  for (let i = 0; i < path.length - 1; i++) {
220
237
  const segment = path[i];
@@ -226,11 +243,14 @@ function hasAtPath(root, path) {
226
243
  if (current === null || current === void 0) return false;
227
244
  if (typeof current !== "object") return false;
228
245
  if (Array.isArray(current)) {
229
- return typeof last === "number" && last in current;
246
+ return typeof last === "number" && consumerHas(current, last);
247
+ }
248
+ if (current instanceof Map) {
249
+ return current.has(mapKeyForSegment(current, last));
230
250
  }
231
251
  const key = typeof last === "number" ? String(last) : last;
232
252
  if (isShadowedKey(key)) return safeOwnHas(current, key);
233
- return key in current;
253
+ return consumerHas(current, key);
234
254
  }
235
255
  function isPlainRecord(value) {
236
256
  if (value === null || typeof value !== "object") return false;
@@ -238,29 +258,30 @@ function isPlainRecord(value) {
238
258
  const proto = Object.getPrototypeOf(value);
239
259
  return proto === null || proto === Object.prototype;
240
260
  }
261
+ const NO_SCHEMA_DEFAULTS = /* @__PURE__ */ new Set();
262
+ const NO_SCHEMA_FILL = {
263
+ getDefaultAtPath: () => void 0,
264
+ arrayShapeAtPath: () => null,
265
+ getSlimPrimitiveTypesAtPath: () => NO_SCHEMA_DEFAULTS,
266
+ entryKeyKindAtPath: () => void 0
267
+ };
241
268
  function setAtPath(root, path, value) {
242
- return setAtPathOffset(root, path, value, 0);
243
- }
244
- function setAtPathOffset(root, path, value, offset) {
245
- if (offset >= path.length) return value;
246
- const head = path[offset];
247
- const nextOffset = offset + 1;
248
- if (typeof head === "number") {
249
- const arr = Array.isArray(root) ? [...root] : [];
250
- while (arr.length <= head) arr.push(void 0);
251
- arr[head] = setAtPathOffset(arr[head], path, value, nextOffset);
252
- return arr;
253
- }
254
- const rec = isPlainRecord(root) ? { ...root } : {};
255
- safeAssign(rec, head, setAtPathOffset(safeOwnRead(rec, head), path, value, nextOffset));
256
- return rec;
269
+ return setAtPathWithSchemaFill(root, NO_SCHEMA_FILL, path, value);
257
270
  }
258
271
  const NO_IN_PLACE = { applied: false };
259
272
  function tryInPlaceLeafWrite(root, path, value) {
260
273
  if (path.length === 0) return NO_IN_PLACE;
274
+ try {
275
+ return descendAndWrite(root, path, value);
276
+ } catch {
277
+ return NO_IN_PLACE;
278
+ }
279
+ }
280
+ function descendAndWrite(root, path, value) {
261
281
  let node = root;
262
282
  for (let i = 0; i < path.length; i++) {
263
283
  const seg = path[i];
284
+ if (node instanceof Map) return NO_IN_PLACE;
264
285
  if (Array.isArray(node)) {
265
286
  if (typeof seg !== "number" || seg < 0 || seg >= node.length) return NO_IN_PLACE;
266
287
  } else if (isPlainRecord(node)) {
@@ -295,17 +316,40 @@ function mergeStructuralImpl(schema, scratch, consumer, defaultValue) {
295
316
  if (Array.isArray(consumer)) {
296
317
  return mergeStructuralArray(schema, scratch, consumer);
297
318
  }
319
+ if (consumer instanceof Map) {
320
+ return mergeStructuralMap(schema, scratch, consumer);
321
+ }
298
322
  if (isPlainRecord(consumer)) {
299
323
  if (!isPlainRecord(defaultValue)) {
300
324
  return consumer;
301
325
  }
302
- const out = { ...consumer };
326
+ const out = spreadConsumerRecord(consumer);
303
327
  const filledAny = fillMissingKeysFromDefault(schema, scratch, consumer, defaultValue, out);
304
328
  const recursedAny = recurseIntoConsumerKeys(schema, scratch, consumer, defaultValue, out);
305
329
  return filledAny || recursedAny ? out : consumer;
306
330
  }
307
331
  return consumer;
308
332
  }
333
+ function mergeStructuralMap(schema, scratch, consumer) {
334
+ if (consumer.size === 0) return consumer;
335
+ let entryDefault;
336
+ let entryDefaultRead = false;
337
+ let out = null;
338
+ for (const [key, entry] of consumer) {
339
+ if (typeof key !== "string" && typeof key !== "number") continue;
340
+ scratch.push(key);
341
+ if (!entryDefaultRead) {
342
+ entryDefault = schema.getDefaultAtPath(scratch);
343
+ entryDefaultRead = true;
344
+ }
345
+ const merged = mergeStructuralImpl(schema, scratch, entry, entryDefault);
346
+ scratch.pop();
347
+ if (merged === entry) continue;
348
+ out ?? (out = new Map(consumer));
349
+ out.set(key, merged);
350
+ }
351
+ return out ?? consumer;
352
+ }
309
353
  function mergeStructuralArray(schema, scratch, consumer) {
310
354
  const shape = schema.arrayShapeAtPath(scratch);
311
355
  const isTuple = typeof shape === "number";
@@ -313,7 +357,7 @@ function mergeStructuralArray(schema, scratch, consumer) {
313
357
  let cachedElementDefault;
314
358
  let cachedElementDefaultRead = false;
315
359
  let mutated = targetLen > consumer.length;
316
- const out = consumer.slice();
360
+ const out = copyConsumerArray(consumer);
317
361
  while (out.length < targetLen) out.push(void 0);
318
362
  for (let i = 0; i < targetLen; i++) {
319
363
  scratch.push(i);
@@ -327,7 +371,7 @@ function mergeStructuralArray(schema, scratch, consumer) {
327
371
  }
328
372
  elemDefault = cachedElementDefault;
329
373
  }
330
- const consumerElem = i < consumer.length ? consumer[i] : void 0;
374
+ const consumerElem = i < consumer.length ? readConsumerIndex(consumer, i) : void 0;
331
375
  const merged = mergeStructuralImpl(schema, scratch, consumerElem, elemDefault);
332
376
  scratch.pop();
333
377
  if (merged !== consumerElem) {
@@ -355,7 +399,7 @@ function fillMissingKeysFromDefault(schema, scratch, consumer, defaultValue, out
355
399
  }
356
400
  function recurseIntoConsumerKeys(schema, scratch, consumer, defaultValue, out) {
357
401
  let mutated = false;
358
- for (const key of Object.keys(consumer)) {
402
+ for (const key of consumerKeys(consumer)) {
359
403
  const cVal = safeOwnRead(consumer, key);
360
404
  if (cVal === void 0) continue;
361
405
  scratch.push(key);
@@ -376,6 +420,21 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
376
420
  if (startIdx >= fullPath.length) return value;
377
421
  const head = fullPath[startIdx];
378
422
  const isLeafStep = startIdx === fullPath.length - 1;
423
+ if (root instanceof Map) {
424
+ const next = new Map(root);
425
+ const key = mapKeyForSegment(root, head, schema.entryKeyKindAtPath(fullPath.slice(0, startIdx)));
426
+ if (isLeafStep) {
427
+ next.set(key, value);
428
+ return next;
429
+ }
430
+ let childRoot2 = next.get(key);
431
+ if (childRoot2 === void 0 || childRoot2 !== null && typeof childRoot2 !== "object") {
432
+ childRoot2 = schema.getDefaultAtPath(fullPath.slice(0, startIdx + 1));
433
+ }
434
+ next.set(key, setAtPathWithSchemaFillImpl(childRoot2, schema, fullPath, value, startIdx + 1));
435
+ return next;
436
+ }
437
+ if (!isRebuildableContainer(root, head)) return root;
379
438
  if (typeof head === "number") {
380
439
  const arr = Array.isArray(root) ? [...root] : [];
381
440
  const prefix = fullPath.slice(0, startIdx);
@@ -413,10 +472,10 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
413
472
  }
414
473
  const rec = isPlainRecord(root) ? { ...root } : {};
415
474
  if (isLeafStep) {
416
- rec[head] = value;
475
+ safeAssign(rec, head, value);
417
476
  return rec;
418
477
  }
419
- const existing = rec[head];
478
+ const existing = safeOwnRead(rec, head);
420
479
  let childRoot;
421
480
  if (existing === void 0 || existing !== null && typeof existing !== "object") {
422
481
  const intermPath = [...fullPath.slice(0, startIdx + 1)];
@@ -425,11 +484,14 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
425
484
  } else {
426
485
  childRoot = existing;
427
486
  }
428
- rec[head] = setAtPathWithSchemaFillImpl(childRoot, schema, fullPath, value, startIdx + 1);
487
+ safeAssign(
488
+ rec,
489
+ head,
490
+ setAtPathWithSchemaFillImpl(childRoot, schema, fullPath, value, startIdx + 1)
491
+ );
429
492
  return rec;
430
493
  }
431
494
 
432
- const warnedDisplayStatePredicates = /* @__PURE__ */ new WeakSet();
433
495
  function isUnderStubAncestor(state, segments) {
434
496
  for (let i = 0; i < segments.length; i++) {
435
497
  const ancestorPath = segments.slice(0, i);
@@ -443,17 +505,18 @@ function isUnderStubAncestor(state, segments) {
443
505
  }
444
506
  return false;
445
507
  }
446
- function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, options) {
508
+ function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, sweep) {
447
509
  const cache = /* @__PURE__ */ new Map();
448
- const predicate = options?.getDisplayState;
510
+ sweep.onEvict((key) => cache.delete(key));
449
511
  return function getFieldState(pathInput) {
450
512
  const { segments, key } = canonicalizePath(pathInput);
451
513
  const cached = cache.get(key);
452
514
  if (cached !== void 0) return cached;
453
515
  const c = computed(
454
- () => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase, predicate) : buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase, predicate)
516
+ () => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase) : buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase)
455
517
  );
456
518
  cache.set(key, c);
519
+ sweep.track(segments, key);
457
520
  return c;
458
521
  };
459
522
  }
@@ -467,7 +530,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
467
530
  const record = state.fields.get(key);
468
531
  const value = state.getValueAtPath(segments);
469
532
  const original = state.originals.get(key)?.value;
470
- const pristine = state.isPristineAtPath(segments);
533
+ const pristine = state.isPristineAtPathByKey(key, segments);
471
534
  const cell = state.errorCells.get(key);
472
535
  const schemaForKey = cell !== void 0 && cell.schema.length > 0 ? cell.schema : void 0;
473
536
  const blankForKey = state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments) ? [makeBlankRequiredError(segments)] : void 0;
@@ -479,7 +542,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
479
542
  const validating = (state.fieldValidationCounts.get(key) ?? 0) > 0;
480
543
  const transforming = (state.fieldTransformCounts.get(key) ?? 0) > 0;
481
544
  const transformError = state.transformErrors.get(key) ?? null;
482
- const gated = state.pathHasAsyncValidation(segments) && !state.firstValidationDone.value;
545
+ const gated = state.pathHasAsyncValidationByKey(key, segments) && !state.firstValidationDone.value;
483
546
  const isOrphan = segments.length > 0 && !hasAtPath(state.form.value, segments) && isUnderStubAncestor(state, segments);
484
547
  const valid = !gated && errors.length === 0 && !validating && !isOrphan;
485
548
  const elementRecord = state.domBinding.value?.elements.get(key);
@@ -522,7 +585,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
522
585
  meta: resolved.meta
523
586
  };
524
587
  }
525
- function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
588
+ function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase) {
526
589
  const base = buildLeafFieldStateBase(state, segments, key, formInstanceId);
527
590
  const validatingSince = state.fieldValidatingSince.get(key) ?? null;
528
591
  const transformingSince = state.fieldTransformingSince.get(key) ?? null;
@@ -535,15 +598,14 @@ function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBa
535
598
  transformingSince,
536
599
  false,
537
600
  // revealedDescendantError: leaves have no descendants
538
- false,
601
+ false
539
602
  // isRoot: a leaf is never the form root
540
- getDisplayState
541
603
  );
542
604
  }
543
605
  function visitActiveLeafPaths(value, base, visit) {
544
606
  if (Array.isArray(value)) {
545
607
  for (let i = 0; i < value.length; i++) {
546
- const child = value[i];
608
+ const child = readConsumerIndex(value, i);
547
609
  if (Array.isArray(child) || isPlainRecord(child)) {
548
610
  visitActiveLeafPaths(child, [...base, i], visit);
549
611
  } else {
@@ -553,8 +615,8 @@ function visitActiveLeafPaths(value, base, visit) {
553
615
  return;
554
616
  }
555
617
  if (isPlainRecord(value)) {
556
- for (const k of Object.keys(value)) {
557
- const child = value[k];
618
+ for (const k of consumerKeys(value)) {
619
+ const child = readConsumerProp(value, k);
558
620
  if (Array.isArray(child) || isPlainRecord(child)) {
559
621
  visitActiveLeafPaths(child, [...base, k], visit);
560
622
  } else {
@@ -635,7 +697,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
635
697
  pristine = false;
636
698
  dirty = true;
637
699
  }
638
- const errors = aggregateErrorsAt(state, segments);
700
+ const errors = aggregateErrorsAt(state, segments, key);
639
701
  const revealedDescendantError = errors.length > 0 && errors.some((e) => {
640
702
  const ePath = e.path;
641
703
  if (ePath.length === segments.length && ePath.every((s, i) => s === segments[i])) return false;
@@ -643,7 +705,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
643
705
  if (ePath.length === 1 && ePath[0] === "") return blurredAfterInteraction;
644
706
  return blurredLeafSegments.some((s) => isPathPrefix(ePath, s));
645
707
  });
646
- if (!asyncPending && state.pathHasAsyncValidation(segments)) asyncPending = true;
708
+ if (!asyncPending && state.pathHasAsyncValidationByKey(key, segments)) asyncPending = true;
647
709
  if ((state.fieldValidationCounts.get(key) ?? 0) > 0) {
648
710
  validating = true;
649
711
  const since = state.fieldValidatingSince.get(key);
@@ -714,7 +776,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
714
776
  revealedDescendantError
715
777
  };
716
778
  }
717
- function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
779
+ function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase) {
718
780
  const { base, validatingSince, transformingSince, revealedDescendantError } = buildContainerFieldStateBase(state, segments, key, formInstanceId);
719
781
  return decorateWithDerivedProps(
720
782
  base,
@@ -724,14 +786,12 @@ function buildContainerFieldState(state, segments, key, formInstanceId, getFormM
724
786
  validatingSince,
725
787
  transformingSince,
726
788
  revealedDescendantError,
727
- segments.length === 0,
728
- getDisplayState
789
+ segments.length === 0
729
790
  );
730
791
  }
731
- function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, transformingSince, revealedDescendantError, isRoot, getDisplayState) {
792
+ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, transformingSince, revealedDescendantError, isRoot) {
732
793
  const firstError = base.errors[0];
733
794
  const firstOwnError = base.ownErrors[0];
734
- const predicate = getDisplayState ?? state.getDisplayState;
735
795
  const formMeta = getFormMetaBase();
736
796
  const ctx = {
737
797
  field: base,
@@ -742,23 +802,9 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
742
802
  // flight) so the reducer returns the plain verdict and hydration matches.
743
803
  now: state.ssr ? 0 : Date.now()
744
804
  };
745
- let machine;
746
- let rollupApplies = isDefaultDisplayState(predicate);
747
- try {
748
- machine = state.displayEngine.resolve(key, ctx, predicate);
749
- } catch (err) {
750
- if (!warnedDisplayStatePredicates.has(predicate)) {
751
- warnedDisplayStatePredicates.add(predicate);
752
- console.warn(
753
- "[attaform] custom getDisplayState threw \u2014 falling back to defaultDisplayState. Subsequent throws from the same predicate will not warn again.",
754
- err
755
- );
756
- }
757
- machine = state.displayEngine.resolve(key, ctx, defaultDisplayState);
758
- rollupApplies = true;
759
- }
760
- const submitValidating = rollupApplies && isRoot && state.submitting.value && state.activeValidations.value > 0;
761
- const resolvedDisplayState = machine.display === "pending" || submitValidating ? "pending" : rollupApplies && revealedDescendantError ? "error" : machine.display;
805
+ const machine = state.displayEngine.resolve(key, ctx, defaultDisplayState);
806
+ const submitValidating = isRoot && state.submitting.value && state.activeValidations.value > 0;
807
+ const resolvedDisplayState = machine.display === "pending" || submitValidating ? "pending" : revealedDescendantError ? "error" : machine.display;
762
808
  const displayState = base.disabled === true ? "idle" : resolvedDisplayState;
763
809
  return {
764
810
  ...base,
@@ -771,26 +817,33 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
771
817
  firstOwnError
772
818
  };
773
819
  }
774
- function aggregateErrorsAt(state, prefix) {
820
+ function aggregateErrorsAt(state, prefix, prefixKey) {
821
+ const candidates = state.errorWindowAt(prefix, prefixKey);
822
+ if (candidates.length === 0) return [];
775
823
  const formValue = state.form.value;
824
+ const cells = state.errorCells;
776
825
  const buckets = /* @__PURE__ */ new Map();
777
- const collect = (errs) => {
778
- for (const [pathKey, list] of errs) {
779
- if (list.length === 0) continue;
780
- const segs = segmentsForPathKey(pathKey);
781
- if (segs === null) continue;
782
- if (!isPathPrefix(prefix, segs)) continue;
783
- if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
784
- const ordinal = state.ensurePathOrdinal(pathKey);
785
- const existing = buckets.get(ordinal);
786
- if (existing === void 0) buckets.set(ordinal, [...list]);
787
- else existing.push(...list);
788
- }
789
- };
790
- collect(cellEntriesFor(state.errorCells, "schema"));
791
- collect(state.derivedBlankErrors.value);
792
- collect(cellEntriesFor(state.errorCells, "user"));
826
+ for (const { key, segments } of candidates) {
827
+ if (!isPathPrefix(prefix, segments)) continue;
828
+ if (segments.length > 0 && !hasAtPath(formValue, segments)) continue;
829
+ const cell = cells.get(key);
830
+ const blankList = state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments) ? [makeBlankRequiredError(segments)] : void 0;
831
+ const schemaList = cell?.schema;
832
+ const userList = cell?.user;
833
+ const total = (schemaList?.length ?? 0) + (blankList?.length ?? 0) + (userList?.length ?? 0);
834
+ if (total === 0) continue;
835
+ const ordinal = state.ensurePathOrdinal(key);
836
+ let bucket = buckets.get(ordinal);
837
+ if (bucket === void 0) {
838
+ bucket = [];
839
+ buckets.set(ordinal, bucket);
840
+ }
841
+ if (schemaList !== void 0) bucket.push(...schemaList);
842
+ if (blankList !== void 0) bucket.push(...blankList);
843
+ if (userList !== void 0) bucket.push(...userList);
844
+ }
793
845
  if (buckets.size === 0) return [];
846
+ if (buckets.size === 1) return [...buckets.values()][0];
794
847
  return [...buckets.entries()].sort(([a], [b]) => a - b).flatMap(([, errs]) => errs);
795
848
  }
796
849
  const EMPTY_ELEMENTS = Object.freeze([]);
@@ -803,6 +856,10 @@ function liveKeysAtPath(state, segments) {
803
856
  for (let i = 0; i < value.length; i += 1) keys[i] = String(i);
804
857
  return keys;
805
858
  }
859
+ if (value instanceof Map) {
860
+ return mapSegmentKeys(value)?.map(String) ?? [];
861
+ }
862
+ if (value instanceof Set) return [];
806
863
  if (typeof value === "object") return Object.keys(value);
807
864
  return [];
808
865
  }
@@ -813,6 +870,12 @@ function liveContainerHasKey(state, segments, key) {
813
870
  const index = Number(key);
814
871
  return Number.isInteger(index) && index >= 0 && index < value.length && String(index) === key;
815
872
  }
873
+ if (value instanceof Map) {
874
+ if (value.has(key)) return true;
875
+ const index = Number(key);
876
+ return Number.isInteger(index) && index >= 0 && String(index) === key && value.has(index);
877
+ }
878
+ if (value instanceof Set) return false;
816
879
  return Object.hasOwn(value, key);
817
880
  }
818
881
  function isArrayPath(state, segments) {
@@ -839,6 +902,7 @@ function makeReadonlyCoercion(snapshot) {
839
902
  }
840
903
 
841
904
  const INTEGER_SEGMENT = /^(?:0|[1-9]\d*)$/;
905
+ const RELEASED = /* @__PURE__ */ Symbol();
842
906
  const EMPTY_DESCENT = Object.freeze({});
843
907
  function keyToSegment(key) {
844
908
  return INTEGER_SEGMENT.test(key) ? Number(key) : key;
@@ -868,12 +932,18 @@ function callableInvokeShim(method, surface, getDescent) {
868
932
  function buildTree(spec) {
869
933
  const containerCache = /* @__PURE__ */ new Map();
870
934
  const existsCache = /* @__PURE__ */ new Map();
935
+ spec.sweep.onEvict((key) => {
936
+ containerCache.delete(`${key}+A`);
937
+ containerCache.delete(`${key}+O`);
938
+ existsCache.delete(key);
939
+ });
871
940
  function schemaHasPath(segs) {
872
- const cacheKey = JSON.stringify(segs);
941
+ const cacheKey = keyForSegments(segs).key;
873
942
  const cached = existsCache.get(cacheKey);
874
943
  if (cached !== void 0) return cached;
875
944
  const result = spec.schema.getSlimPrimitiveTypesAtPath(segs).size > 0;
876
945
  existsCache.set(cacheKey, result);
946
+ spec.sweep.track(segs, cacheKey);
877
947
  return result;
878
948
  }
879
949
  function descend(segs) {
@@ -883,7 +953,8 @@ function buildTree(spec) {
883
953
  }
884
954
  function containerAt(segments) {
885
955
  const isArrayLike = spec.isArrayAt(segments);
886
- const cacheKey = `${JSON.stringify(segments)}+${isArrayLike ? "A" : "O"}`;
956
+ const pathKey = keyForSegments(segments).key;
957
+ const cacheKey = `${pathKey}+${isArrayLike ? "A" : "O"}`;
887
958
  const existing = containerCache.get(cacheKey);
888
959
  if (existing !== void 0) return existing;
889
960
  const isFixedObject = spec.schema.isFixedObjectAtPath(segments);
@@ -973,46 +1044,42 @@ function buildTree(spec) {
973
1044
  }
974
1045
  });
975
1046
  containerCache.set(cacheKey, proxy);
1047
+ spec.sweep.track(segments, pathKey);
976
1048
  return proxy;
977
1049
  }
978
1050
  return containerAt([]);
979
1051
  }
980
- function buildErrorsSurface(state) {
1052
+ function buildErrorsSurface(state, sweep) {
981
1053
  const treeCache = /* @__PURE__ */ new Map();
1054
+ sweep.onEvict((key) => treeCache.delete(key));
982
1055
  const materialize = (segments) => {
983
- const cacheKey = JSON.stringify(segments);
1056
+ const cacheKey = keyForSegments(segments).key;
984
1057
  let tree = treeCache.get(cacheKey);
985
1058
  if (tree === void 0) {
986
1059
  const frozen = [...segments];
987
1060
  tree = computed(() => materializeErrors(state, frozen));
988
1061
  treeCache.set(cacheKey, tree);
1062
+ sweep.track(segments, cacheKey);
989
1063
  }
990
1064
  return tree.value;
991
1065
  };
992
1066
  const resolveLeaf = (path) => {
993
1067
  const isContainerSelfAccess = path.length > 1 && path[path.length - 1] === "";
994
- const collectAtKey = (key2, active2, into) => {
995
- const cell = state.errorCells.get(key2);
996
- if (active2) {
997
- const b = state.derivedBlankErrors.value.get(key2);
998
- if (cell !== void 0) into.push(...cell.schema);
999
- if (b !== void 0) into.push(...b);
1000
- }
1001
- if (cell !== void 0) into.push(...cell.user);
1002
- };
1003
1068
  const merged = [];
1004
1069
  if (isContainerSelfAccess) {
1005
1070
  const containerPath = path.slice(0, -1);
1006
- const containerKey = canonicalizePath(containerPath).key;
1007
- const literalKey = canonicalizePath(path).key;
1071
+ const container = canonicalizePath(containerPath);
1072
+ const literal = canonicalizePath(path);
1008
1073
  const active2 = hasAtPath(state.form.value, containerPath);
1009
- collectAtKey(containerKey, active2, merged);
1010
- if (literalKey !== containerKey) collectAtKey(literalKey, active2, merged);
1074
+ collectErrorsAt(state, container.key, container.segments, active2, merged);
1075
+ if (literal.key !== container.key) {
1076
+ collectErrorsAt(state, literal.key, literal.segments, active2, merged);
1077
+ }
1011
1078
  return merged;
1012
1079
  }
1013
- const { key } = canonicalizePath(path);
1080
+ const { key, segments } = canonicalizePath(path);
1014
1081
  const active = hasAtPath(state.form.value, path);
1015
- collectAtKey(key, active, merged);
1082
+ collectErrorsAt(state, key, segments, active, merged);
1016
1083
  return merged;
1017
1084
  };
1018
1085
  return buildTree({
@@ -1030,64 +1097,80 @@ function buildErrorsSurface(state) {
1030
1097
  // `errors()` / `errors([])` / `errors(path)` are all the subtree
1031
1098
  // aggregate — the same helper `meta.errors` reads, so the surfaces
1032
1099
  // never drift.
1033
- call: (path) => aggregateErrorsAt(state, path),
1034
- surface: "form.errors"
1100
+ call: (path) => aggregateErrorsAt(state, path, keyForSegments(path).key),
1101
+ surface: "form.errors",
1102
+ sweep
1035
1103
  });
1036
1104
  }
1105
+ function collectErrorsAt(state, key, segments, active, into) {
1106
+ const cell = state.errorCells.get(key);
1107
+ if (active) {
1108
+ if (cell !== void 0) into.push(...cell.schema);
1109
+ if (state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments)) {
1110
+ into.push(makeBlankRequiredError(segments));
1111
+ }
1112
+ }
1113
+ if (cell !== void 0) into.push(...cell.user);
1114
+ }
1037
1115
  function errorAwareContainerKeys(state, segments) {
1038
1116
  const keys = new Set(liveKeysAtPath(state, segments));
1039
1117
  const formValue = state.form.value;
1040
- const walk = (store, applyActivePathFilter) => {
1041
- for (const [pathKey, errors] of store) {
1042
- if (errors.length === 0) continue;
1043
- const decoded = segmentsForPathKey(pathKey);
1044
- if (decoded === null) continue;
1045
- if (decoded.length <= segments.length) continue;
1046
- if (!isPathPrefix(segments, decoded)) continue;
1047
- if (applyActivePathFilter && !hasAtPath(formValue, decoded)) continue;
1048
- const nextSeg = decoded[segments.length];
1049
- keys.add(typeof nextSeg === "number" ? String(nextSeg) : nextSeg);
1050
- }
1051
- };
1052
- walk(cellEntriesFor(state.errorCells, "schema"), true);
1053
- walk(state.derivedBlankErrors.value, true);
1054
- walk(cellEntriesFor(state.errorCells, "user"), false);
1118
+ const errors = [];
1119
+ const found = [];
1120
+ for (const { key, segments: decoded } of state.errorWindowAt(
1121
+ segments,
1122
+ keyForSegments(segments).key
1123
+ )) {
1124
+ if (decoded.length <= segments.length) continue;
1125
+ if (!isPathPrefix(segments, decoded)) continue;
1126
+ errors.length = 0;
1127
+ collectErrorsAt(state, key, decoded, hasAtPath(formValue, decoded), errors);
1128
+ if (errors.length === 0) continue;
1129
+ const nextSeg = decoded[segments.length];
1130
+ found.push({
1131
+ childKey: typeof nextSeg === "number" ? String(nextSeg) : nextSeg,
1132
+ ordinal: state.ensurePathOrdinal(key)
1133
+ });
1134
+ }
1135
+ found.sort((a, b) => a.ordinal - b.ordinal);
1136
+ for (const { childKey } of found) keys.add(childKey);
1055
1137
  return [...keys];
1056
1138
  }
1057
1139
  function materializeErrors(state, containerSegments) {
1058
- const liveContainer = getAtPath(state.form.value, containerSegments);
1140
+ const formValue = state.form.value;
1141
+ const liveContainer = getAtPath(formValue, containerSegments);
1059
1142
  const tree = Array.isArray(liveContainer) ? [] : {};
1060
- const collect = (store, applyActivePathFilter) => {
1061
- entries: for (const [pathKey, errors] of store) {
1062
- if (errors.length === 0) continue;
1063
- const fullPath = segmentsForPathKey(pathKey);
1064
- if (fullPath === null) continue;
1065
- if (fullPath.length === 0) {
1066
- if (containerSegments.length === 0) placeAt(tree, [ROOT_PATH_KEY], errors);
1067
- continue;
1068
- }
1069
- if (fullPath.length < containerSegments.length) continue;
1070
- for (let i = 0; i < containerSegments.length; i++) {
1071
- if (fullPath[i] !== containerSegments[i]) continue entries;
1072
- }
1073
- if (applyActivePathFilter && !hasAtPath(state.form.value, fullPath)) continue;
1074
- const relativePath = fullPath.slice(containerSegments.length);
1075
- let placePath;
1076
- if (relativePath.length === 0) {
1077
- placePath = [""];
1078
- } else if (state.schema.isLeafAtPath(fullPath)) {
1079
- placePath = relativePath;
1080
- } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1081
- placePath = [...relativePath, ""];
1082
- } else {
1083
- placePath = relativePath;
1084
- }
1085
- placeAt(tree, placePath, errors);
1143
+ const placements = [];
1144
+ for (const { key, segments: fullPath } of state.errorWindowAt(
1145
+ containerSegments,
1146
+ keyForSegments(containerSegments).key
1147
+ )) {
1148
+ const isRoot = fullPath.length === 0;
1149
+ if (isRoot) {
1150
+ if (containerSegments.length !== 0) continue;
1151
+ } else if (!isPathPrefix(containerSegments, fullPath)) {
1152
+ continue;
1086
1153
  }
1087
- };
1088
- collect(cellEntriesFor(state.errorCells, "schema"), true);
1089
- collect(state.derivedBlankErrors.value, true);
1090
- collect(cellEntriesFor(state.errorCells, "user"), false);
1154
+ const errors = [];
1155
+ collectErrorsAt(state, key, fullPath, isRoot || hasAtPath(formValue, fullPath), errors);
1156
+ if (errors.length === 0) continue;
1157
+ const relativePath = fullPath.slice(containerSegments.length);
1158
+ let placePath;
1159
+ if (isRoot) {
1160
+ placePath = [ROOT_PATH_KEY];
1161
+ } else if (relativePath.length === 0) {
1162
+ placePath = [""];
1163
+ } else if (state.schema.isLeafAtPath(fullPath)) {
1164
+ placePath = relativePath;
1165
+ } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1166
+ placePath = [...relativePath, ""];
1167
+ } else {
1168
+ placePath = relativePath;
1169
+ }
1170
+ placements.push({ placePath, errors, ordinal: state.ensurePathOrdinal(key) });
1171
+ }
1172
+ placements.sort((a, b) => a.ordinal - b.ordinal);
1173
+ for (const { placePath, errors } of placements) placeAt(tree, placePath, errors);
1091
1174
  return tree;
1092
1175
  }
1093
1176
  function placeAt(tree, path, errors) {
@@ -1150,7 +1233,7 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
1150
1233
  "placeholder",
1151
1234
  "meta"
1152
1235
  ]);
1153
- function buildFieldsSurface(state, getFieldStateAt) {
1236
+ function buildFieldsSurface(state, getFieldStateAt, sweep) {
1154
1237
  const snapshotAt = (segments) => {
1155
1238
  const view = getFieldStateAt(segments).value;
1156
1239
  const snapshot = {};
@@ -1158,8 +1241,9 @@ function buildFieldsSurface(state, getFieldStateAt) {
1158
1241
  return snapshot;
1159
1242
  };
1160
1243
  const viewCache = /* @__PURE__ */ new Map();
1244
+ sweep.onEvict((key) => viewCache.delete(key));
1161
1245
  function viewAt(segments) {
1162
- const cacheKey = JSON.stringify(segments);
1246
+ const cacheKey = keyForSegments(segments).key;
1163
1247
  const existing = viewCache.get(cacheKey);
1164
1248
  if (existing !== void 0) return existing;
1165
1249
  const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(
@@ -1212,6 +1296,7 @@ function buildFieldsSurface(state, getFieldStateAt) {
1212
1296
  }
1213
1297
  });
1214
1298
  viewCache.set(cacheKey, proxy);
1299
+ sweep.track(segments, cacheKey);
1215
1300
  return proxy;
1216
1301
  }
1217
1302
  return buildTree({
@@ -1226,7 +1311,8 @@ function buildFieldsSurface(state, getFieldStateAt) {
1226
1311
  // container's rollup); a path the schema doesn't have is a typo and
1227
1312
  // reads `undefined`. The root (`fields()`) is always declared.
1228
1313
  call: (path) => state.schema.getSlimPrimitiveTypesAtPath(path).size > 0 ? viewAt(path) : void 0,
1229
- surface: "form.fields"
1314
+ surface: "form.fields",
1315
+ sweep
1230
1316
  });
1231
1317
  }
1232
1318
  function materializeFields(state, containerSegments, snapshotAt) {
@@ -1260,18 +1346,34 @@ function materializeFormValue(node) {
1260
1346
  }
1261
1347
  return out;
1262
1348
  }
1263
- function buildValuesSurface(form) {
1349
+ function buildValuesSurface(form, onFormChange) {
1264
1350
  const inner = computed(() => readonly(form.value));
1265
- const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(
1266
- () => materializeFormValue(inner.value)
1267
- );
1351
+ let held = null;
1352
+ const snapshot = computed(() => {
1353
+ const box = { value: materializeFormValue(inner.value) };
1354
+ held = box;
1355
+ return box;
1356
+ });
1357
+ onFormChange(() => {
1358
+ if (held === null) return;
1359
+ held.value = RELEASED;
1360
+ held = null;
1361
+ });
1362
+ const readSnapshot = () => {
1363
+ const box = snapshot.value;
1364
+ return box.value === RELEASED ? materializeFormValue(inner.value) : box.value;
1365
+ };
1366
+ const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(() => readSnapshot());
1268
1367
  const target = (() => {
1269
1368
  });
1270
1369
  return new Proxy(target, {
1271
1370
  apply(_, __, args) {
1272
1371
  const arg = args[0];
1273
- if (arg === void 0) return inner.value;
1274
- return getAtPath(inner.value, canonicalizePath(arg).segments);
1372
+ if (arg === void 0) return readSnapshot();
1373
+ const segments = canonicalizePath(arg).segments;
1374
+ const node = getAtPath(inner.value, segments);
1375
+ if (node === null || typeof node !== "object") return node;
1376
+ return getAtPath(readSnapshot(), segments);
1275
1377
  },
1276
1378
  get(_, key) {
1277
1379
  if (typeof key === "symbol") {
@@ -1556,9 +1658,12 @@ function cloneVariantSnapshot(value) {
1556
1658
  for (let i = 0; i < raw.length; i++) out2[i] = cloneVariantSnapshot(raw[i]);
1557
1659
  return out2;
1558
1660
  }
1661
+ if (!isPlainRecord(raw)) return raw;
1559
1662
  const src = raw;
1560
1663
  const out = {};
1561
- for (const k of Object.keys(src)) safeAssign(out, k, cloneVariantSnapshot(src[k]));
1664
+ for (const k of consumerKeys(src)) {
1665
+ safeAssign(out, k, cloneVariantSnapshot(readConsumerProp(src, k)));
1666
+ }
1562
1667
  return out;
1563
1668
  }
1564
1669
  function createArrayBookkeeping(deps) {
@@ -1733,8 +1838,8 @@ function deriveSubmitErrors(err) {
1733
1838
  messageless: true
1734
1839
  };
1735
1840
  }
1736
- function buildProcessForm(state, formInstanceId, options = {}) {
1737
- const invalidPolicy = options.onInvalidSubmit ?? "focus-first-error";
1841
+ function buildProcessForm(state, options = {}) {
1842
+ const applyInvalidSubmit = options.applyInvalidSubmit;
1738
1843
  function validate(pathInput) {
1739
1844
  const result = ref({
1740
1845
  pending: true,
@@ -1832,7 +1937,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1832
1937
  };
1833
1938
  }
1834
1939
  async function runRefinementValidation(data, path) {
1835
- return await state.schema.validateAtPath(data, path);
1940
+ return stampFormKey(await state.schema.validateAtPath(data, path), state.formKey);
1836
1941
  }
1837
1942
  function composeWithDerivedBlank(refinement, scope) {
1838
1943
  const blankErrors = collectScopedBlankErrors(state, scope);
@@ -1853,6 +1958,11 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1853
1958
  event.preventDefault();
1854
1959
  }
1855
1960
  if (state.activeSubmissions.value > 0) {
1961
+ if (event === void 0) {
1962
+ console.warn(
1963
+ `[attaform] handleSubmit: a submit is already in flight on form "${String(state.formKey)}", so this call was ignored; its callback never ran. Await the first submit before starting another, or move the work into that submit's callback.`
1964
+ );
1965
+ }
1856
1966
  return;
1857
1967
  }
1858
1968
  const genAtEntry = state.submissionGeneration.value;
@@ -1878,7 +1988,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1878
1988
  }
1879
1989
  }
1880
1990
  if (generationStillValid) {
1881
- applyInvalidSubmitPolicy(state, formInstanceId, invalidPolicy);
1991
+ applyInvalidSubmit?.();
1882
1992
  }
1883
1993
  if (onError !== void 0) {
1884
1994
  try {
@@ -1895,7 +2005,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1895
2005
  await onSubmit(merged.data);
1896
2006
  if (hasUserErrorEntries(state)) {
1897
2007
  if (state.submissionGeneration.value === genAtEntry) {
1898
- applyInvalidSubmitPolicy(state, formInstanceId, invalidPolicy);
2008
+ applyInvalidSubmit?.();
1899
2009
  }
1900
2010
  return;
1901
2011
  }
@@ -1913,7 +2023,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1913
2023
  if (segments === null) continue;
1914
2024
  state.setUserErrorsForPath([...segments], bucket);
1915
2025
  }
1916
- applyInvalidSubmitPolicy(state, formInstanceId, invalidPolicy);
2026
+ applyInvalidSubmit?.();
1917
2027
  if (messageless) {
1918
2028
  console.warn(
1919
2029
  "[attaform] handleSubmit callback threw a non-Error value; throw an Error or a ValidationError ({ message, path? }) so the failure surfaces with a usable message."
@@ -1933,6 +2043,10 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1933
2043
  };
1934
2044
  return { validate, parse, handleSubmit };
1935
2045
  }
2046
+ function stampFormKey(verdict, formKey) {
2047
+ if (verdict.success) return { ...verdict, formKey };
2048
+ return verdict.data === void 0 ? { data: void 0, errors: verdict.errors, success: false, formKey } : { data: verdict.data, errors: verdict.errors, success: false, formKey };
2049
+ }
1936
2050
  function toSegments(pathInput) {
1937
2051
  return canonicalizePath(pathInput).segments;
1938
2052
  }
@@ -1966,24 +2080,9 @@ function collectScopedBlankErrors(state, scope) {
1966
2080
  }
1967
2081
  return errors;
1968
2082
  }
1969
- function applyInvalidSubmitPolicy(state, formInstanceId, policy) {
1970
- if (policy === "none") return;
1971
- const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
1972
- if (target === null) return;
1973
- if (policy === "scroll-to-first-error") {
1974
- target.element.scrollIntoView();
1975
- return;
1976
- }
1977
- if (policy === "focus-first-error") {
1978
- target.element.focus({ focusVisible: true });
1979
- return;
1980
- }
1981
- target.element.scrollIntoView();
1982
- target.element.focus({ preventScroll: true, focusVisible: true });
1983
- }
1984
2083
 
1985
2084
  const warnedRejections = /* @__PURE__ */ new WeakMap() ;
1986
- function shouldWarnOnce$1(store, key) {
2085
+ function shouldWarnOnce(store, key) {
1987
2086
  if (warnedRejections === null) return false;
1988
2087
  let set = warnedRejections.get(store);
1989
2088
  if (set === void 0) {
@@ -2039,15 +2138,16 @@ function walk(schema, store, path, value) {
2039
2138
  reportRejection(store, path, kind, accepted);
2040
2139
  return false;
2041
2140
  }
2141
+ if (schema.isOpaqueLeafAtPath(path)) return true;
2042
2142
  if (Array.isArray(value)) {
2043
2143
  for (let i = 0; i < value.length; i++) {
2044
- if (!walk(schema, store, [...path, i], value[i])) return false;
2144
+ if (!walk(schema, store, [...path, i], readConsumerIndex(value, i))) return false;
2045
2145
  }
2046
2146
  return true;
2047
2147
  }
2048
2148
  if (isPlainRecord(value)) {
2049
- for (const key of Object.keys(value)) {
2050
- if (!walk(schema, store, [...path, key], value[key])) {
2149
+ for (const key of consumerKeys(value)) {
2150
+ if (!walk(schema, store, [...path, key], readConsumerProp(value, key))) {
2051
2151
  return false;
2052
2152
  }
2053
2153
  }
@@ -2058,7 +2158,7 @@ function walk(schema, store, path, value) {
2058
2158
  function reportRejection(store, path, kind, accepted) {
2059
2159
  const dotted = path.map((s) => String(s)).join(".") || "(root)";
2060
2160
  const key = `${dotted}::${kind}`;
2061
- if (!shouldWarnOnce$1(store, key)) return;
2161
+ if (!shouldWarnOnce(store, key)) return;
2062
2162
  if (accepted.size === 0) {
2063
2163
  console.warn(
2064
2164
  `[attaform] Cannot write to '${dotted}' \u2014 this path is not in your schema.
@@ -2091,170 +2191,95 @@ function formatExpectedKinds(accepted) {
2091
2191
  return `one of: ${list.join(", ")}`;
2092
2192
  }
2093
2193
 
2094
- function defineCoercion(entry) {
2095
- return entry;
2096
- }
2097
2194
  const IDENTITY = (v) => v;
2098
- const EMPTY_INDEX = /* @__PURE__ */ new Map();
2099
- const defaultCoercionRules = [
2100
- defineCoercion({
2101
- input: "string",
2102
- output: "number",
2103
- transform: (s) => {
2104
- const trimmed = s.trim();
2105
- if (trimmed === "") return { coerced: false };
2106
- const n = Number(trimmed);
2107
- if (!Number.isFinite(n)) return { coerced: false };
2108
- return { coerced: true, value: n };
2109
- }
2110
- }),
2111
- defineCoercion({
2112
- input: "string",
2113
- output: "boolean",
2114
- transform: (s) => {
2115
- const normalized = s.trim().toLowerCase();
2116
- if (normalized === "true") return { coerced: true, value: true };
2117
- if (normalized === "false") return { coerced: true, value: false };
2118
- return { coerced: false };
2119
- }
2120
- })
2121
- ];
2122
- function resolveCoercionIndex(config) {
2123
- if (config === false) return EMPTY_INDEX;
2124
- const rules = config === void 0 || config === true ? defaultCoercionRules : config;
2125
- return indexRules(rules);
2126
- }
2127
- function indexRules(rules) {
2128
- const idx = /* @__PURE__ */ new Map();
2129
- for (const entry of rules) {
2130
- const candidate = entry;
2131
- if (candidate === null || typeof candidate !== "object" || typeof candidate.transform !== "function") {
2132
- {
2133
- console.warn("[attaform] coercion entry missing or invalid `transform` \u2014 skipped.");
2134
- }
2135
- continue;
2136
- }
2137
- const key = `${entry.input}->${entry.output}`;
2138
- if (idx.has(key) && true) {
2139
- console.warn(`[attaform] duplicate coercion rule for '${key}' \u2014 last entry wins.`);
2140
- }
2141
- idx.set(key, entry);
2195
+ function resolveCoerceEnabled(config) {
2196
+ return config !== false;
2197
+ }
2198
+ function memberSlimTypes(schema, segments, accepted) {
2199
+ if (accepted.has("array")) return schema.getSlimPrimitiveTypesAtPath([...segments, 0]);
2200
+ if (accepted.has("set")) {
2201
+ return schema.getSlimPrimitiveTypesAtPath([...segments, SET_MEMBER_SEGMENT]);
2142
2202
  }
2143
- return idx;
2203
+ return void 0;
2144
2204
  }
2145
- function buildCoerceFn(schema, segments, index) {
2146
- if (index === EMPTY_INDEX) return IDENTITY;
2147
- if (index.size === 0) return IDENTITY;
2205
+ function buildCoerceFn(schema, segments, enabled) {
2206
+ if (!enabled) return IDENTITY;
2148
2207
  const accepted = schema.getSlimPrimitiveTypesAtPath(segments);
2149
- const elementAccepted = accepted.has("array") || accepted.has("set") ? schema.getSlimPrimitiveTypesAtPath([...segments, 0]) : void 0;
2150
- return (value) => coerceValue(value, accepted, elementAccepted, index);
2208
+ const elementAccepted = memberSlimTypes(schema, segments, accepted);
2209
+ return (value) => coerceValue(value, accepted, elementAccepted);
2151
2210
  }
2152
- function buildElementCoerceFn(schema, segments, index) {
2153
- if (index === EMPTY_INDEX) return void 0;
2154
- if (index.size === 0) return void 0;
2211
+ function buildElementCoerceFn(schema, segments, enabled) {
2212
+ if (!enabled) return void 0;
2155
2213
  const accepted = schema.getSlimPrimitiveTypesAtPath(segments);
2156
- if (!accepted.has("array") && !accepted.has("set")) return void 0;
2157
- const elementAccepted = schema.getSlimPrimitiveTypesAtPath([...segments, 0]);
2158
- return (value) => coerceScalar(value, elementAccepted, index);
2214
+ const elementAccepted = memberSlimTypes(schema, segments, accepted);
2215
+ if (elementAccepted === void 0) return void 0;
2216
+ return (value) => coerceScalar(value, elementAccepted);
2159
2217
  }
2160
2218
  function pickScalarTarget(accepted) {
2161
2219
  if (accepted.has("string")) return null;
2162
2220
  if (accepted.has("number")) return "number";
2163
2221
  if (accepted.has("boolean")) return "boolean";
2164
- if (accepted.has("bigint")) return "bigint";
2165
2222
  return null;
2166
2223
  }
2167
- const warnedCoerce = /* @__PURE__ */ new WeakMap() ;
2168
- const sharedWarnStore = {};
2169
- function shouldWarnOnce(key) {
2170
- if (warnedCoerce === null) return false;
2171
- let set = warnedCoerce.get(sharedWarnStore);
2172
- if (set === void 0) {
2173
- set = /* @__PURE__ */ new Set();
2174
- warnedCoerce.set(sharedWarnStore, set);
2175
- }
2176
- if (set.has(key)) return false;
2177
- set.add(key);
2178
- return true;
2224
+ function toNumber(source) {
2225
+ const trimmed = source.trim();
2226
+ if (trimmed === "") return source;
2227
+ const n = Number(trimmed);
2228
+ return Number.isFinite(n) ? n : source;
2179
2229
  }
2180
- function coerceScalar(value, accepted, index) {
2230
+ function toBoolean(source) {
2231
+ const normalized = source.trim().toLowerCase();
2232
+ if (normalized === "true") return true;
2233
+ if (normalized === "false") return false;
2234
+ return source;
2235
+ }
2236
+ function coerceScalar(value, accepted) {
2181
2237
  if (accepted.size === 0) return value;
2182
2238
  const sourceKind = slimKindOf(value);
2183
2239
  if (accepted.has(sourceKind)) return value;
2240
+ if (sourceKind !== "string") return value;
2184
2241
  const target = pickScalarTarget(accepted);
2185
- if (target === null) return value;
2186
- const entry = index.get(`${sourceKind}->${target}`);
2187
- if (entry === void 0) return value;
2188
- let result;
2189
- try {
2190
- result = entry.transform(value);
2191
- } catch (err) {
2192
- if (shouldWarnOnce(`${entry.input}->${entry.output}::throw`)) {
2193
- console.warn(
2194
- `[attaform] coercion '${entry.input}->${entry.output}' threw \u2014 write passes through.`,
2195
- err
2196
- );
2197
- }
2198
- return value;
2199
- }
2200
- if (!result.coerced) return value;
2201
- const returnedKind = slimKindOf(result.value);
2202
- if (returnedKind !== entry.output) {
2203
- if (shouldWarnOnce(`${entry.input}->${entry.output}::wrong-kind:${returnedKind}`)) {
2204
- console.warn(
2205
- `[attaform] coercion '${entry.input}->${entry.output}' produced a ${returnedKind} \u2014 write passes through.`
2206
- );
2207
- }
2208
- return value;
2209
- }
2210
- if (entry.output === "number" && !Number.isFinite(result.value)) {
2211
- if (shouldWarnOnce(`${entry.input}->${entry.output}::nan`)) {
2212
- console.warn(
2213
- `[attaform] coercion '${entry.input}->${entry.output}' produced a non-finite number \u2014 write passes through.`
2214
- );
2215
- }
2216
- return value;
2217
- }
2218
- return result.value;
2242
+ if (target === "number") return toNumber(value);
2243
+ if (target === "boolean") return toBoolean(value);
2244
+ return value;
2219
2245
  }
2220
- function coerceArrayMembers(arr, elementAccepted, index) {
2246
+ function coerceArrayMembers(arr, elementAccepted) {
2221
2247
  let changed = false;
2222
2248
  const out = [];
2223
2249
  for (const el of arr) {
2224
- const next = coerceScalar(el, elementAccepted, index);
2250
+ const next = coerceScalar(el, elementAccepted);
2225
2251
  if (next !== el) changed = true;
2226
2252
  out.push(next);
2227
2253
  }
2228
2254
  return changed ? out : arr;
2229
2255
  }
2230
- function coerceSetMembers(set, elementAccepted, index) {
2256
+ function coerceSetMembers(set, elementAccepted) {
2231
2257
  let changed = false;
2232
2258
  const out = [];
2233
2259
  for (const el of set) {
2234
- const next = coerceScalar(el, elementAccepted, index);
2260
+ const next = coerceScalar(el, elementAccepted);
2235
2261
  if (next !== el) changed = true;
2236
2262
  out.push(next);
2237
2263
  }
2238
2264
  return changed ? new Set(out) : set;
2239
2265
  }
2240
- function coerceValue(value, accepted, elementAccepted, index) {
2266
+ function coerceValue(value, accepted, elementAccepted) {
2241
2267
  if (Array.isArray(value)) {
2242
2268
  if (!accepted.has("array") || elementAccepted === void 0) return value;
2243
- return coerceArrayMembers(value, elementAccepted, index);
2269
+ return coerceArrayMembers(value, elementAccepted);
2244
2270
  }
2245
2271
  if (value instanceof Set) {
2246
2272
  if (!accepted.has("set") || elementAccepted === void 0) return value;
2247
- return coerceSetMembers(value, elementAccepted, index);
2273
+ return coerceSetMembers(value, elementAccepted);
2248
2274
  }
2249
- return coerceScalar(value, accepted, index);
2275
+ return coerceScalar(value, accepted);
2250
2276
  }
2251
2277
 
2252
2278
  const warnedMultiRootHosts = /* @__PURE__ */ new Set();
2253
2279
  const EMPTY_TRANSFORMS = Object.freeze([]);
2254
2280
  function buildRegister(state, formInstanceId, instanceConfig) {
2255
- const coerceIndex = instanceConfig?.coerce !== void 0 ? resolveCoercionIndex(instanceConfig.coerce) : state.coerceIndex;
2281
+ const coerceEnabled = instanceConfig?.coerce !== void 0 ? resolveCoerceEnabled(instanceConfig.coerce) : state.coerceEnabled;
2256
2282
  const instanceMeta = instanceConfig?.instanceMeta;
2257
- const formAutoAria = instanceConfig?.autoAria ?? true;
2258
2283
  const getDisplayStateAt = instanceConfig?.getDisplayStateAt;
2259
2284
  const withInstanceMeta = (meta) => {
2260
2285
  if (instanceMeta === void 0) return meta;
@@ -2294,16 +2319,15 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2294
2319
  const coerce = buildCoerceFn(
2295
2320
  state.schema,
2296
2321
  segments,
2297
- coerceIndex
2322
+ coerceEnabled
2298
2323
  );
2299
2324
  const coerceElement = buildElementCoerceFn(
2300
2325
  state.schema,
2301
2326
  segments,
2302
- coerceIndex
2327
+ coerceEnabled
2303
2328
  );
2304
2329
  const { aria } = computeFieldIdentity(formInstanceId, state.formKey, pathKey);
2305
2330
  const isRequired = state.schema.isRequiredAtPath(segments);
2306
- const ariaEnabled = options?.autoAria ?? formAutoAria;
2307
2331
  const ariaDisplayState = getDisplayStateAt !== void 0 ? computed(() => getDisplayStateAt(segments)) : void 0;
2308
2332
  const markBlank = () => state.setValueAtPath(segments, slimDefault, withInstanceMeta({ blank: true }));
2309
2333
  const internalRv = {
@@ -2423,7 +2447,6 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2423
2447
  // --- Aria (internal; consumed by the directive) ---
2424
2448
  aria,
2425
2449
  isRequired,
2426
- ariaEnabled,
2427
2450
  ...ariaDisplayState !== void 0 ? { ariaDisplayState } : {}
2428
2451
  };
2429
2452
  return shallowReadonly(internalRv);
@@ -2445,8 +2468,8 @@ function walkUnsetSentinels(values, schema) {
2445
2468
  const cleaned = walkCore(values, [], schema, paths, true);
2446
2469
  return { cleanedValues: cleaned, paths };
2447
2470
  }
2448
- function isOpaqueLeaf(value) {
2449
- return value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || typeof value === "function";
2471
+ function isRecursable(value) {
2472
+ return Array.isArray(value) || isPlainRecord(value);
2450
2473
  }
2451
2474
  function walkCore(input, segments, schema, paths, synthesizeSchemaKeys) {
2452
2475
  if (isUnset(input)) {
@@ -2460,20 +2483,21 @@ function walkCore(input, segments, schema, paths, synthesizeSchemaKeys) {
2460
2483
  return input;
2461
2484
  }
2462
2485
  if (input === null) return null;
2463
- if (isOpaqueLeaf(input)) return input;
2486
+ if (!isRecursable(input)) return input;
2464
2487
  if (Array.isArray(input)) {
2465
2488
  const out = new Array(input.length);
2466
2489
  let mutated = false;
2467
2490
  for (let i = 0; i < input.length; i++) {
2468
- const walked = walkCore(input[i], [...segments, i], schema, paths, synthesizeSchemaKeys);
2491
+ const original = readConsumerIndex(input, i);
2492
+ const walked = walkCore(original, [...segments, i], schema, paths, synthesizeSchemaKeys);
2469
2493
  out[i] = walked;
2470
- if (walked !== input[i]) mutated = true;
2494
+ if (walked !== original) mutated = true;
2471
2495
  }
2472
2496
  return mutated ? out : input;
2473
2497
  }
2474
2498
  if (typeof input === "object") {
2475
2499
  const obj = input;
2476
- const inputKeys = Object.keys(obj);
2500
+ const inputKeys = consumerKeys(obj);
2477
2501
  let keys = inputKeys;
2478
2502
  let mutated = false;
2479
2503
  let inputKeysSet = null;
@@ -2481,7 +2505,7 @@ function walkCore(input, segments, schema, paths, synthesizeSchemaKeys) {
2481
2505
  inputKeysSet = new Set(inputKeys);
2482
2506
  const allKeys = new Set(inputKeys);
2483
2507
  const slim = schema.getDefaultAtPath(segments);
2484
- if (slim !== null && slim !== void 0 && typeof slim === "object" && !Array.isArray(slim) && !isOpaqueLeaf(slim)) {
2508
+ if (isPlainRecord(slim)) {
2485
2509
  for (const k of Object.keys(slim)) allKeys.add(k);
2486
2510
  }
2487
2511
  keys = allKeys;
@@ -2489,7 +2513,7 @@ function walkCore(input, segments, schema, paths, synthesizeSchemaKeys) {
2489
2513
  }
2490
2514
  const out = {};
2491
2515
  for (const key of keys) {
2492
- const orig = obj[key];
2516
+ const orig = readConsumerProp(obj, key);
2493
2517
  if (synthesizeSchemaKeys && orig === void 0 && inputKeysSet?.has(key) === true) {
2494
2518
  safeAssign(out, key, void 0);
2495
2519
  mutated = true;
@@ -2510,22 +2534,12 @@ function walkUnspecified(slim, segments, paths) {
2510
2534
  }
2511
2535
  return slim;
2512
2536
  }
2513
- if (isOpaqueLeaf(slim)) {
2514
- return slim;
2515
- }
2516
- if (Array.isArray(slim)) return slim;
2517
- if (slim !== null && typeof slim === "object") {
2518
- const out = {};
2519
- for (const key of Object.keys(slim)) {
2520
- safeAssign(
2521
- out,
2522
- key,
2523
- walkUnspecified(slim[key], [...segments, key], paths)
2524
- );
2525
- }
2526
- return out;
2537
+ if (!isPlainRecord(slim)) return slim;
2538
+ const out = {};
2539
+ for (const key of Object.keys(slim)) {
2540
+ safeAssign(out, key, walkUnspecified(slim[key], [...segments, key], paths));
2527
2541
  }
2528
- return slim;
2542
+ return out;
2529
2543
  }
2530
2544
  function substituteUnsetSentinels(value, prefix, schema) {
2531
2545
  const paths = [];
@@ -2561,11 +2575,11 @@ function expandUnsetAt(segments, schema, paths) {
2561
2575
  paths.push(canonicalizePath(segments).key);
2562
2576
  return slim;
2563
2577
  }
2564
- if (isOpaqueLeaf(slim)) {
2578
+ if (Array.isArray(slim)) return slim;
2579
+ if (!isPlainRecord(slim)) {
2565
2580
  paths.push(canonicalizePath(segments).key);
2566
2581
  return slim;
2567
2582
  }
2568
- if (Array.isArray(slim)) return slim;
2569
2583
  const result = {};
2570
2584
  for (const key of Object.keys(slim)) {
2571
2585
  result[key] = expandUnsetAt([...segments, key], schema, paths);
@@ -2575,14 +2589,14 @@ function expandUnsetAt(segments, schema, paths) {
2575
2589
  function walkAuthoredFromConstraints(value, prefix, out) {
2576
2590
  if (prefix.length > 0) out.add(canonicalizePath(prefix).key);
2577
2591
  if (isPlainRecord(value)) {
2578
- for (const k of Object.keys(value)) {
2579
- walkAuthoredFromConstraints(value[k], [...prefix, k], out);
2592
+ for (const k of consumerKeys(value)) {
2593
+ walkAuthoredFromConstraints(readConsumerProp(value, k), [...prefix, k], out);
2580
2594
  }
2581
2595
  return;
2582
2596
  }
2583
2597
  if (Array.isArray(value)) {
2584
2598
  for (let i = 0; i < value.length; i++) {
2585
- walkAuthoredFromConstraints(value[i], [...prefix, i], out);
2599
+ walkAuthoredFromConstraints(readConsumerIndex(value, i), [...prefix, i], out);
2586
2600
  }
2587
2601
  }
2588
2602
  }
@@ -2596,11 +2610,50 @@ const DERIVED_DISPLAY_KEYS = /* @__PURE__ */ new Set([
2596
2610
  "firstError",
2597
2611
  "firstOwnError"
2598
2612
  ]);
2599
- const META_SPECIAL_KEYS = /* @__PURE__ */ new Set(["errors", "validating", "valid", "transforming", "busy"]);
2600
- const BASE_MIRROR_KEYS = [...FIELD_STATE_KEYS].filter((k) => !DERIVED_DISPLAY_KEYS.has(k));
2601
- const META_MIRROR_KEYS = [...FIELD_STATE_KEYS].filter((k) => !META_SPECIAL_KEYS.has(k));
2602
- function defineGetter(target, key, get) {
2603
- Object.defineProperty(target, key, { get, enumerable: true, configurable: true });
2613
+ const META_KEYS = [
2614
+ ...FIELD_STATE_KEYS,
2615
+ "submitting",
2616
+ "submissionAttempts",
2617
+ "departAttempts",
2618
+ "submitError",
2619
+ "submitted",
2620
+ "instanceId",
2621
+ "errorCount"
2622
+ ];
2623
+ const BASE_META_KEYS = META_KEYS.filter((k) => !DERIVED_DISPLAY_KEYS.has(k));
2624
+ let INERT_HISTORY;
2625
+ function inertHistory() {
2626
+ INERT_HISTORY ?? (INERT_HISTORY = readonly({
2627
+ undo: () => false,
2628
+ redo: () => false,
2629
+ clear: () => {
2630
+ },
2631
+ canUndo: false,
2632
+ canRedo: false,
2633
+ size: 0
2634
+ }));
2635
+ return INERT_HISTORY;
2636
+ }
2637
+ const denyWrite = () => false;
2638
+ function buildMetaProxy(keys, read) {
2639
+ const target = {};
2640
+ const owns = new Set(keys);
2641
+ return new Proxy(target, {
2642
+ get: (_, key) => owns.has(key) ? read(key) : Reflect.get(target, key),
2643
+ has: (_, key) => owns.has(key) || Reflect.has(target, key),
2644
+ // Safe to hand back the same array every time: the spec copies a
2645
+ // trap's key list before using it.
2646
+ ownKeys: () => keys,
2647
+ getOwnPropertyDescriptor(_, key) {
2648
+ if (!owns.has(key)) return Reflect.getOwnPropertyDescriptor(target, key);
2649
+ return { configurable: true, enumerable: true, value: read(key), writable: false };
2650
+ },
2651
+ // A getter with no setter throws on assignment under strict mode,
2652
+ // which every module here is. `false` keeps that.
2653
+ set: denyWrite,
2654
+ deleteProperty: denyWrite,
2655
+ defineProperty: denyWrite
2656
+ });
2604
2657
  }
2605
2658
  function buildFormApi(state, formInstanceId, options = {}) {
2606
2659
  const instanceBag = pickDefined({
@@ -2627,32 +2680,26 @@ function buildFormApi(state, formInstanceId, options = {}) {
2627
2680
  const rootBaseComputed = computed(
2628
2681
  () => buildContainerFieldStateBase(state, ROOT_PATH, ROOT_PATH_KEY, formInstanceId).base
2629
2682
  );
2630
- const metaBase = {
2631
- instanceId: formInstanceId,
2632
- get submitting() {
2633
- return state.submitting.value;
2634
- },
2635
- get submissionAttempts() {
2636
- return state.submissionAttempts.value;
2637
- },
2638
- get departAttempts() {
2639
- return state.departAttempts.value;
2640
- },
2641
- get submitError() {
2642
- return state.submitError.value;
2643
- },
2644
- get submitted() {
2645
- return state.submitted.value;
2683
+ const metaBase = buildMetaProxy(BASE_META_KEYS, (key) => {
2684
+ switch (key) {
2685
+ case "instanceId":
2686
+ return formInstanceId;
2687
+ case "errorCount":
2688
+ return rootBaseComputed.value.errors.length;
2689
+ case "submitting":
2690
+ return state.submitting.value;
2691
+ case "submissionAttempts":
2692
+ return state.submissionAttempts.value;
2693
+ case "departAttempts":
2694
+ return state.departAttempts.value;
2695
+ case "submitError":
2696
+ return state.submitError.value;
2697
+ case "submitted":
2698
+ return state.submitted.value;
2699
+ default:
2700
+ return rootBaseComputed.value[key];
2646
2701
  }
2647
- };
2648
- for (const k of BASE_MIRROR_KEYS) {
2649
- defineGetter(
2650
- metaBase,
2651
- k,
2652
- () => rootBaseComputed.value[k]
2653
- );
2654
- }
2655
- defineGetter(metaBase, "errorCount", () => rootBaseComputed.value.errors.length);
2702
+ });
2656
2703
  const getFormMetaBase = () => {
2657
2704
  void state.submitting.value;
2658
2705
  void state.submissionAttempts.value;
@@ -2661,26 +2708,41 @@ function buildFormApi(state, formInstanceId, options = {}) {
2661
2708
  void state.submitted.value;
2662
2709
  return metaBase;
2663
2710
  };
2664
- const fieldStateAccessorOptions = options.getDisplayState !== void 0 ? { getDisplayState: options.getDisplayState } : void 0;
2711
+ const pathSweep = state.pathSweep;
2665
2712
  const getRootFieldStateAt = buildFieldStateAccessor(
2666
2713
  state,
2667
2714
  formInstanceId,
2668
2715
  getFormMetaBase,
2669
- fieldStateAccessorOptions
2716
+ pathSweep
2670
2717
  );
2671
2718
  const getDisplayStateAt = (segments) => getRootFieldStateAt(segments).value.displayState;
2672
2719
  const registerConfig = {
2673
- ...pickDefined({ instanceMeta, coerce: options.coerce, autoAria: options.autoAria }),
2720
+ ...pickDefined({ instanceMeta, coerce: options.coerce }),
2674
2721
  getDisplayStateAt
2675
2722
  };
2676
2723
  const register = buildRegister(state, formInstanceId, registerConfig);
2677
- const processOptions = pickDefined({ onInvalidSubmit: options.onInvalidSubmit });
2678
- const defaultInvalidSubmitPolicy = options.onInvalidSubmit ?? "focus-first-error";
2724
+ const focusFirstError = (options2) => {
2725
+ const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
2726
+ if (target === null) return false;
2727
+ target.element.focus({ focusVisible: true, ...options2 });
2728
+ return true;
2729
+ };
2730
+ const scrollToFirstError = (options2) => {
2731
+ const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
2732
+ if (target === null) return false;
2733
+ target.element.scrollIntoView(options2);
2734
+ return true;
2735
+ };
2736
+ const applyInvalidSubmitPolicyPublic = () => {
2737
+ if (options.focusOnInvalidSubmit !== false) focusFirstError();
2738
+ };
2679
2739
  const {
2680
2740
  validate: validateBuilt,
2681
2741
  parse: parseBuilt,
2682
2742
  handleSubmit
2683
- } = buildProcessForm(state, formInstanceId, processOptions);
2743
+ } = buildProcessForm(state, {
2744
+ applyInvalidSubmit: applyInvalidSubmitPolicyPublic
2745
+ });
2684
2746
  const validate = (pathInput) => validateBuilt(pathInput);
2685
2747
  const parse = (arg1, arg2) => {
2686
2748
  const isPathArg = typeof arg1 === "string" || Array.isArray(arg1);
@@ -2692,6 +2754,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
2692
2754
  const segments = canonicalizePath(pathInput).segments;
2693
2755
  return computed(() => getAtPath(state.form.value, segments));
2694
2756
  }
2757
+ function pathStoresFunctions(segments) {
2758
+ if (state.schema.isOpaqueLeafAtPath(segments)) return false;
2759
+ return state.schema.getSlimPrimitiveTypesAtPath(segments).has("function");
2760
+ }
2695
2761
  function setValueImpl(pathOrValue, maybeValue) {
2696
2762
  const argc = arguments.length;
2697
2763
  const isPathForm = argc >= 2 && (typeof pathOrValue === "string" || Array.isArray(pathOrValue));
@@ -2744,7 +2810,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2744
2810
  };
2745
2811
  if (isUnset(maybeValue)) return writeUnsetAt();
2746
2812
  let resolvedValue;
2747
- if (typeof maybeValue === "function") {
2813
+ if (typeof maybeValue === "function" && !pathStoresFunctions(segments)) {
2748
2814
  const current = state.getValueAtPath(segments);
2749
2815
  const prev = current === void 0 ? state.schema.getDefaultAtPath(segments) : current;
2750
2816
  resolvedValue = maybeValue(prev);
@@ -2762,7 +2828,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2762
2828
  reMarkBlanksAfterSubstitution(walked.paths);
2763
2829
  return true;
2764
2830
  }
2765
- const errorsProxy = buildErrorsSurface(state);
2831
+ const errorsProxy = buildErrorsSurface(state, pathSweep);
2766
2832
  function flattenUserErrors() {
2767
2833
  const all = [];
2768
2834
  for (const cell of state.errorCells.values()) all.push(...cell.user);
@@ -2804,22 +2870,21 @@ function buildFormApi(state, formInstanceId, options = {}) {
2804
2870
  () => state.firstValidationDone.value && state.errorCells.size === 0 && state.derivedBlankErrors.value.size === 0 && !validating.value
2805
2871
  );
2806
2872
  const history = options.history;
2807
- const formHistory = readonly(
2873
+ const formHistory = history === void 0 ? inertHistory() : readonly(
2808
2874
  reactive({
2809
- undo: history?.undo ?? (() => false),
2810
- redo: history?.redo ?? (() => false),
2811
- clear: history?.clear ?? (() => {
2812
- }),
2813
- canUndo: history?.canUndo ?? computed(() => false),
2814
- canRedo: history?.canRedo ?? computed(() => false),
2815
- size: history?.historySize ?? computed(() => 0)
2875
+ undo: history.undo,
2876
+ redo: history.redo,
2877
+ clear: history.clear,
2878
+ canUndo: history.canUndo,
2879
+ canRedo: history.canRedo,
2880
+ size: history.historySize
2816
2881
  })
2817
2882
  );
2818
2883
  const metaErrors = computed(
2819
- () => aggregateErrorsAt(state, [])
2884
+ () => aggregateErrorsAt(state, [], ROOT_PATH_KEY)
2820
2885
  );
2821
2886
  const rootFieldState = getRootFieldStateAt([]);
2822
- const metaTarget = {
2887
+ const metaOwn = {
2823
2888
  // Whole-form work signals compose the LIFECYCLE counters with the
2824
2889
  // per-leaf rollup: a submit-time validate shows up as
2825
2890
  // activeValidations, per-field debounced validators as
@@ -2848,21 +2913,19 @@ function buildFormApi(state, formInstanceId, options = {}) {
2848
2913
  // identifier).
2849
2914
  instanceId: formInstanceId
2850
2915
  };
2851
- for (const k of META_MIRROR_KEYS) {
2852
- defineGetter(
2853
- metaTarget,
2854
- k,
2855
- () => rootFieldState.value[k]
2856
- );
2857
- }
2858
- defineGetter(metaTarget, "errorCount", () => metaErrors.value.length);
2916
+ const metaTarget = buildMetaProxy(META_KEYS, (key) => {
2917
+ if (key === "errorCount") return metaErrors.value.length;
2918
+ if (Object.hasOwn(metaOwn, key)) return metaOwn[key];
2919
+ return rootFieldState.value[key];
2920
+ });
2859
2921
  const formMeta = readonly(reactive(metaTarget));
2860
2922
  const reset = (nextDefaultValues) => {
2861
2923
  if (nextDefaultValues === void 0) {
2862
2924
  state.reset();
2863
2925
  } else {
2864
- const walked = walkUnsetSentinels(
2926
+ const walked = substituteUnsetSentinels(
2865
2927
  nextDefaultValues,
2928
+ [],
2866
2929
  state.schema
2867
2930
  );
2868
2931
  state.reset(walked.cleanedValues);
@@ -2894,21 +2957,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
2894
2957
  } catch {
2895
2958
  }
2896
2959
  }
2897
- const focusFirstError = (options2) => {
2898
- const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
2899
- if (target === null) return false;
2900
- target.element.focus({ focusVisible: true, ...options2 });
2901
- return true;
2902
- };
2903
- const scrollToFirstError = (options2) => {
2904
- const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
2905
- if (target === null) return false;
2906
- target.element.scrollIntoView(options2);
2907
- return true;
2908
- };
2909
- const applyInvalidSubmitPolicyPublic = (policy) => {
2910
- applyInvalidSubmitPolicy(state, formInstanceId, policy ?? defaultInvalidSubmitPolicy);
2911
- };
2912
2960
  const fieldArrays = buildFieldArrayApi(state);
2913
2961
  const blankPathsView = computed(() => {
2914
2962
  const keys = /* @__PURE__ */ new Set();
@@ -2936,8 +2984,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
2936
2984
  };
2937
2985
  return Object.freeze(view);
2938
2986
  });
2939
- const valuesProxy = buildValuesSurface(state.form);
2940
- const fieldStateProxy = buildFieldsSurface(state, getRootFieldStateAt);
2987
+ const valuesProxy = buildValuesSurface(state.form, state.onFormChange);
2988
+ const fieldStateProxy = buildFieldsSurface(state, getRootFieldStateAt, pathSweep);
2941
2989
  const needsLazyGate = state.defaultValuesFactory.value !== void 0 || state.hasSsrPrefetch;
2942
2990
  function gated(fn) {
2943
2991
  if (!needsLazyGate) return fn;
@@ -3083,8 +3131,53 @@ function shortenSourceFrame(frame) {
3083
3131
  return `(${path}:${line})`;
3084
3132
  }
3085
3133
 
3134
+ const SWEEP_SLICE = 8;
3135
+ function createDynamicPathSweep(deps) {
3136
+ const dynamicKeys = /* @__PURE__ */ new Set();
3137
+ const evictors = [];
3138
+ const isDynamicPath = (segments) => {
3139
+ for (let i = 0; i < segments.length; i++) {
3140
+ if (typeof segments[i] === "number") return true;
3141
+ if (!deps.isFixedObjectAtPath(segments.slice(0, i))) return true;
3142
+ }
3143
+ return false;
3144
+ };
3145
+ let sweepCursor = null;
3146
+ deps.onFormChange((next) => {
3147
+ if (dynamicKeys.size === 0) return;
3148
+ const raw = toRaw(next);
3149
+ for (let i = 0; i < SWEEP_SLICE; i++) {
3150
+ sweepCursor ?? (sweepCursor = dynamicKeys.values());
3151
+ const step = sweepCursor.next();
3152
+ if (step.done === true) {
3153
+ sweepCursor = null;
3154
+ break;
3155
+ }
3156
+ const segments = segmentsForPathKey(step.value);
3157
+ if (segments !== null && hasAtPath(raw, segments)) continue;
3158
+ dynamicKeys.delete(step.value);
3159
+ for (const evict of evictors) evict(step.value);
3160
+ }
3161
+ });
3162
+ return {
3163
+ track(segments, key) {
3164
+ if (dynamicKeys.has(key)) return;
3165
+ if (isDynamicPath(segments)) dynamicKeys.add(key);
3166
+ },
3167
+ onEvict(evict) {
3168
+ evictors.push(evict);
3169
+ }
3170
+ };
3171
+ }
3172
+
3086
3173
  const IDLE = Object.freeze({ display: "idle" });
3087
3174
  const MAX_DELAY = 2147483647;
3175
+ const PENDING_LIVENESS_MS = 250;
3176
+ function withLiveness(machine, now) {
3177
+ if (machine.display !== "pending") return machine;
3178
+ if (machine.reviewAt !== void 0 && Number.isFinite(machine.reviewAt)) return machine;
3179
+ return { ...machine, reviewAt: now + PENDING_LIVENESS_MS };
3180
+ }
3088
3181
  function createDisplayEngine(ssr) {
3089
3182
  const machines = /* @__PURE__ */ new Map();
3090
3183
  const tick = ref(0);
@@ -3113,7 +3206,7 @@ function createDisplayEngine(ssr) {
3113
3206
  return;
3114
3207
  }
3115
3208
  if (timer !== null && timerTarget === target) return;
3116
- if (target === lastFiredTarget) {
3209
+ if (target === lastFiredTarget && target <= now) {
3117
3210
  clearTimer();
3118
3211
  return;
3119
3212
  }
@@ -3132,8 +3225,9 @@ function createDisplayEngine(ssr) {
3132
3225
  function resolve(key, ctx, reducer) {
3133
3226
  void tick.value;
3134
3227
  const prev = machines.get(key) ?? IDLE;
3135
- const machine = reducer(prev, ctx);
3136
- if (ssr) return machine;
3228
+ const reduced = reducer(prev, ctx);
3229
+ if (ssr) return reduced;
3230
+ const machine = withLiveness(reduced, ctx.now);
3137
3231
  const active = machine.display !== "idle" || machine.reviewAt !== void 0 && Number.isFinite(machine.reviewAt);
3138
3232
  if (active) machines.set(key, machine);
3139
3233
  else machines.delete(key);
@@ -3169,6 +3263,58 @@ function createDisplayEngine(ssr) {
3169
3263
  return engine;
3170
3264
  }
3171
3265
 
3266
+ function buildErrorPathIndex(cells, blank) {
3267
+ const keys = /* @__PURE__ */ new Set();
3268
+ for (const [key, cell] of cells) {
3269
+ if (cell.schema.length > 0 || cell.user.length > 0) keys.add(key);
3270
+ }
3271
+ for (const [key, list] of blank) {
3272
+ if (list.length > 0) keys.add(key);
3273
+ }
3274
+ if (keys.size === 0) return EMPTY_INDEX;
3275
+ const entries = [];
3276
+ for (const key of keys) {
3277
+ const segments = segmentsForPathKey(key);
3278
+ if (segments !== null) entries.push({ key, segments });
3279
+ }
3280
+ entries.sort(compareByKey);
3281
+ return entries;
3282
+ }
3283
+ const EMPTY_INDEX = Object.freeze([]);
3284
+ function compareByKey(a, b) {
3285
+ return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
3286
+ }
3287
+ function windowUnder(index, prefix) {
3288
+ if (prefix.length === 0 || index.length === 0) return index;
3289
+ const prefixKey = keyForSegments(prefix).key;
3290
+ const marker = `${prefixKey.slice(0, -1)},`;
3291
+ const start = lowerBound(index, marker);
3292
+ if (start >= index.length) return EMPTY_INDEX;
3293
+ let end = start;
3294
+ for (; end < index.length; end += 1) {
3295
+ const entry = index[end];
3296
+ if (entry === void 0 || entry.key > prefixKey) break;
3297
+ }
3298
+ return start === 0 && end === index.length ? index : index.slice(start, end);
3299
+ }
3300
+ function isSameWindow(a, b) {
3301
+ if (a.length !== b.length) return false;
3302
+ for (let i = 0; i < a.length; i += 1) {
3303
+ if (a[i]?.key !== b[i]?.key) return false;
3304
+ }
3305
+ return true;
3306
+ }
3307
+ function lowerBound(index, target) {
3308
+ let lo = 0;
3309
+ let hi = index.length;
3310
+ while (lo < hi) {
3311
+ const mid = lo + hi >>> 1;
3312
+ if ((index[mid]?.key ?? target) < target) lo = mid + 1;
3313
+ else hi = mid;
3314
+ }
3315
+ return lo;
3316
+ }
3317
+
3172
3318
  function mergeSparseHydration(schemaDefaults, sparse, schema) {
3173
3319
  return mergeDeep(schemaDefaults, sparse, [], schema);
3174
3320
  }
@@ -3230,6 +3376,7 @@ async function runFactoryAndApply(st, factory) {
3230
3376
  st.schema
3231
3377
  );
3232
3378
  st.applyFormReplacement(full, { hydration: true });
3379
+ st.adoptResolvedDefaults(value);
3233
3380
  st.scheduleFieldValidation(
3234
3381
  [],
3235
3382
  true
@@ -3314,12 +3461,10 @@ function applyDuStubs(schema, data, options = {}) {
3314
3461
  }
3315
3462
  function walkDuStubs(schema, value, path, warned) {
3316
3463
  if (value === null || value === void 0 || typeof value !== "object") return value;
3317
- if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || typeof value === "function") {
3318
- return value;
3319
- }
3320
3464
  if (Array.isArray(value)) {
3321
3465
  return value.map((item, i) => walkDuStubs(schema, item, [...path, i], warned));
3322
3466
  }
3467
+ if (!isPlainRecord(value)) return value;
3323
3468
  const rec = value;
3324
3469
  const du = schema.getUnionDiscriminatorAtPath(path);
3325
3470
  if (du !== void 0) {
@@ -3342,8 +3487,8 @@ function walkDuStubs(schema, value, path, warned) {
3342
3487
  }
3343
3488
  }
3344
3489
  const out = {};
3345
- for (const k of Object.keys(rec)) {
3346
- safeAssign(out, k, walkDuStubs(schema, rec[k], [...path, k], warned));
3490
+ for (const k of consumerKeys(rec)) {
3491
+ safeAssign(out, k, walkDuStubs(schema, readConsumerProp(rec, k), [...path, k], warned));
3347
3492
  }
3348
3493
  return out;
3349
3494
  }
@@ -3353,23 +3498,25 @@ function stripSymbolsDeep(value) {
3353
3498
  let mutated2 = false;
3354
3499
  const out2 = new Array(value.length);
3355
3500
  for (let i = 0; i < value.length; i++) {
3356
- const cleaned = stripSymbolsDeep(value[i]);
3501
+ const original = readConsumerIndex(value, i);
3502
+ const cleaned = stripSymbolsDeep(original);
3357
3503
  out2[i] = cleaned;
3358
- if (cleaned !== value[i]) mutated2 = true;
3504
+ if (cleaned !== original) mutated2 = true;
3359
3505
  }
3360
3506
  return mutated2 ? out2 : value;
3361
3507
  }
3362
3508
  const proto = Object.getPrototypeOf(value);
3363
3509
  if (proto !== Object.prototype && proto !== null) return value;
3364
- const symKeys = Object.getOwnPropertySymbols(value);
3365
- const stringKeys = Object.keys(value);
3510
+ const symKeys = consumerSymbolKeys(value);
3511
+ const stringKeys = consumerKeys(value);
3366
3512
  let mutated = symKeys.length > 0;
3367
3513
  const out = {};
3368
3514
  const src = value;
3369
3515
  for (const k of stringKeys) {
3370
- const cleaned = stripSymbolsDeep(src[k]);
3516
+ const original = readConsumerProp(src, k);
3517
+ const cleaned = stripSymbolsDeep(original);
3371
3518
  out[k] = cleaned;
3372
- if (cleaned !== src[k]) mutated = true;
3519
+ if (cleaned !== original) mutated = true;
3373
3520
  }
3374
3521
  return mutated ? out : value;
3375
3522
  }
@@ -3394,16 +3541,15 @@ function walkAuthoredFromSchemaDiff(withDefaults, withoutDefaults, prefix, out)
3394
3541
  out.add(canonicalizePath(prefix).key);
3395
3542
  }
3396
3543
  }
3397
- function computeBaselineResponse(schema, strict, source) {
3544
+ function computeBaselineResponse(schema, source) {
3398
3545
  const completed = source === void 0 ? void 0 : mergeStructural(schema, [], source);
3399
3546
  return schema.getDefaultValues({
3400
3547
  useDefaultSchemaValues: true,
3401
- constraints: completed,
3402
- strict
3548
+ constraints: completed
3403
3549
  });
3404
3550
  }
3405
- function initialFirstValidationGate(schema, strict) {
3406
- return !strict || schema.needsAsyncValidation?.() !== true;
3551
+ function initialFirstValidationGate(schema) {
3552
+ return schema.needsAsyncValidation?.() !== true;
3407
3553
  }
3408
3554
  function seedOriginalsFromBaseline(st, baseline, ensureOrdinals) {
3409
3555
  st.originals.clear();
@@ -3415,7 +3561,7 @@ function seedOriginalsFromBaseline(st, baseline, ensureOrdinals) {
3415
3561
  });
3416
3562
  }
3417
3563
  function queueInitialAsyncValidation(st) {
3418
- if (!st.ssr && st.strict && st.schema.needsAsyncValidation?.() === true) {
3564
+ if (!st.ssr && st.schema.needsAsyncValidation?.() === true) {
3419
3565
  queueMicrotask(() => scheduleFieldValidation(
3420
3566
  st,
3421
3567
  [],
@@ -3457,10 +3603,6 @@ function ensurePathOrdinal(st, key) {
3457
3603
  }
3458
3604
  return ordinal;
3459
3605
  }
3460
- function pathHasAsyncValidation(st, path) {
3461
- const { key } = canonicalizePath(path);
3462
- return pathHasAsyncValidationByKey(st, key, path);
3463
- }
3464
3606
  function pathHasAsyncValidationByKey(st, key, segments) {
3465
3607
  const cached = st.pathAsyncCache.get(key);
3466
3608
  if (cached !== void 0) return cached;
@@ -3603,10 +3745,15 @@ function commitWritePatches(st, patches, meta) {
3603
3745
  const now = (/* @__PURE__ */ new Date()).toISOString();
3604
3746
  for (const patch of patches) {
3605
3747
  const { key } = canonicalizePath(patch.path);
3606
- if (patch.kind === "added" && !st.originals.has(key)) {
3748
+ if (!st.originals.has(key)) {
3607
3749
  st.originals.set(key, { segments: patch.path, value: void 0 });
3608
3750
  }
3609
3751
  touchFieldRecord(st, key, patch.path, { updatedAt: now });
3752
+ st.pathSweep.track(patch.path, key);
3753
+ for (let i = 1; i < patch.path.length; i++) {
3754
+ const ancestor = patch.path.slice(0, i);
3755
+ st.pathSweep.track(ancestor, canonicalizePath(ancestor).key);
3756
+ }
3610
3757
  }
3611
3758
  for (const listener of st.formChangeListeners) {
3612
3759
  try {
@@ -3817,10 +3964,7 @@ function setValueAtPath(st, path, value, meta) {
3817
3964
  if (newlyAuthored && st.schema.isPreprocessOrCoerceLeaf(path)) {
3818
3965
  const modeForAuthoringTransition = meta?.instance?.validateOn ?? st.fieldValidationMode;
3819
3966
  if (modeForAuthoringTransition === "change") {
3820
- scheduleFieldValidation(st, path, false, {
3821
- ...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
3822
- ...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
3823
- });
3967
+ scheduleFieldValidation(st, path, false, meta?.instance);
3824
3968
  }
3825
3969
  }
3826
3970
  return true;
@@ -3836,15 +3980,12 @@ function setValueAtPath(st, path, value, meta) {
3836
3980
  st.arrayIdentity.realign(path);
3837
3981
  } else if (isContainer(currentValue) && !isContainer(value)) {
3838
3982
  if (subtreeHadRealBaseline(st, path, currentValue)) {
3839
- st.removedSubtrees.add(canonicalizePath(path).key);
3983
+ st.removedSubtrees.add(pathKey);
3840
3984
  }
3841
3985
  }
3842
3986
  const effectiveModeAfterWrite = meta?.instance?.validateOn ?? st.fieldValidationMode;
3843
3987
  if (effectiveModeAfterWrite === "change") {
3844
- scheduleFieldValidation(st, path, false, {
3845
- ...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
3846
- ...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
3847
- });
3988
+ scheduleFieldValidation(st, path, false, meta?.instance);
3848
3989
  }
3849
3990
  return true;
3850
3991
  }
@@ -3905,12 +4046,11 @@ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variant
3905
4046
  path: [...parentPath, ...err.path]
3906
4047
  }));
3907
4048
  applySchemaErrorsForSubtree(st, parentPath, reStamped);
3908
- const { key: parentKey2 } = canonicalizePath(parentPath);
3909
- const prevValidation = st.fieldValidationState.get(parentKey2);
4049
+ const prevValidation = st.fieldValidationState.get(parentKey);
3910
4050
  if (prevValidation !== void 0) {
3911
4051
  if (prevValidation.timer !== null) clearTimeout(prevValidation.timer);
3912
4052
  prevValidation.aborted = true;
3913
- st.fieldValidationState.delete(parentKey2);
4053
+ st.fieldValidationState.delete(parentKey);
3914
4054
  }
3915
4055
  appliedSync = true;
3916
4056
  }
@@ -3918,17 +4058,14 @@ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variant
3918
4058
  applyFormReplacement(st, nextForm, meta);
3919
4059
  for (const k of newBlankPaths) st.blankPaths.add(k);
3920
4060
  if (reshapeMode === "change" && !appliedSync) {
3921
- scheduleFieldValidation(st, parentPath, false, {
3922
- ...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
3923
- ...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
3924
- });
4061
+ scheduleFieldValidation(st, parentPath, false, meta?.instance);
3925
4062
  }
3926
4063
  return true;
3927
4064
  }
3928
- function scheduleFieldValidation(st, path, immediate, override) {
3929
- const effectiveMode = override?.mode ?? st.fieldValidationMode;
4065
+ function scheduleFieldValidation(st, path, immediate, instance) {
4066
+ const effectiveMode = instance?.validateOn ?? st.fieldValidationMode;
3930
4067
  if (effectiveMode === "submit") return;
3931
- const effectiveDebounce = override?.debounceMs ?? st.fieldValidationDebounceMs;
4068
+ const effectiveDebounce = instance?.debounceMs ?? st.fieldValidationDebounceMs;
3932
4069
  const { key } = canonicalizePath(path);
3933
4070
  const prev = st.fieldValidationState.get(key);
3934
4071
  if (prev !== void 0) {
@@ -4202,10 +4339,7 @@ function markFocused(st, path, focused, meta) {
4202
4339
  });
4203
4340
  }
4204
4341
  if (changed) {
4205
- scheduleFieldValidation(st, path, true, {
4206
- ...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
4207
- ...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
4208
- });
4342
+ scheduleFieldValidation(st, path, true, meta?.instance);
4209
4343
  }
4210
4344
  }
4211
4345
  }
@@ -4218,11 +4352,10 @@ function markInteracted(st, path) {
4218
4352
  function touchAtPath(st, segments) {
4219
4353
  const formValue = st.form.value;
4220
4354
  let touchedAny = false;
4221
- for (const [, entry] of st.originals) {
4355
+ for (const [leafKey, entry] of st.originals) {
4222
4356
  if (!isPathPrefix(segments, entry.segments)) continue;
4223
4357
  if (!hasAtPath(formValue, entry.segments)) continue;
4224
4358
  touchedAny = true;
4225
- const leafKey = canonicalizePath(entry.segments).key;
4226
4359
  const current = st.fields.get(leafKey);
4227
4360
  if (current?.touched === true) continue;
4228
4361
  touchFieldRecord(st, leafKey, entry.segments, { touched: true });
@@ -4237,11 +4370,10 @@ function interactAtPath(st, segments) {
4237
4370
  if (st.effectiveDisabled.value) return false;
4238
4371
  const formValue = st.form.value;
4239
4372
  let interactedAny = false;
4240
- for (const [, entry] of st.originals) {
4373
+ for (const [leafKey, entry] of st.originals) {
4241
4374
  if (!isPathPrefix(segments, entry.segments)) continue;
4242
4375
  if (!hasAtPath(formValue, entry.segments)) continue;
4243
4376
  interactedAny = true;
4244
- const leafKey = canonicalizePath(entry.segments).key;
4245
4377
  const current = st.fields.get(leafKey);
4246
4378
  if (current?.touched === true && current.interacted === true && current.blurredAfterInteraction === true) {
4247
4379
  continue;
@@ -4290,9 +4422,24 @@ function activate(st) {
4290
4422
  if (factory === void 0) return Promise.resolve();
4291
4423
  return fireFactory(st, factory);
4292
4424
  }
4425
+ function adoptResolvedDefaults(st, value) {
4426
+ st.defaultValues = structuralSnapshot(
4427
+ mergeSparseHydration(
4428
+ st.defaultValues,
4429
+ value,
4430
+ st.schema
4431
+ )
4432
+ );
4433
+ seedOriginalsFromBaseline(st, computeBaselineResponse(st.schema, st.defaultValues).data, false);
4434
+ }
4293
4435
  function reset(st, nextDefaultValues) {
4294
- const resetSource = nextDefaultValues ?? st.defaultValues;
4295
- const resetResponse = computeBaselineResponse(st.schema, st.strict, resetSource);
4436
+ const resetSource = nextDefaultValues === void 0 ? st.defaultValues : mergeSparseHydration(
4437
+ st.defaultValues,
4438
+ nextDefaultValues,
4439
+ st.schema
4440
+ );
4441
+ st.defaultValues = structuralSnapshot(resetSource);
4442
+ const resetResponse = computeBaselineResponse(st.schema, resetSource);
4296
4443
  const next = resetResponse.data;
4297
4444
  rebuildAuthoredPaths(st, resetSource, next);
4298
4445
  applyFormReplacement(st, next);
@@ -4300,25 +4447,23 @@ function reset(st, nextDefaultValues) {
4300
4447
  st.removedSubtrees.clear();
4301
4448
  seedOriginalsFromBaseline(st, next, false);
4302
4449
  if (nextDefaultValues !== void 0) {
4303
- st.blankPaths.clear();
4304
- st.originalBlankPaths.clear();
4305
- } else {
4306
- st.blankPaths.clear();
4307
- for (const key of st.originalBlankPaths) {
4308
- st.blankPaths.add(key);
4309
- }
4450
+ const mentioned = /* @__PURE__ */ new Set();
4451
+ walkAuthoredFromConstraints(nextDefaultValues, [], mentioned);
4452
+ for (const key of mentioned) st.originalBlankPaths.delete(key);
4453
+ }
4454
+ st.blankPaths.clear();
4455
+ for (const key of st.originalBlankPaths) {
4456
+ st.blankPaths.add(key);
4310
4457
  }
4311
4458
  st.errorCells.clear();
4312
- if (st.strict && !resetResponse.success) {
4459
+ if (!resetResponse.success) {
4313
4460
  replaceErrorChannel(st, "schema", resetResponse.errors);
4314
4461
  }
4315
- if (st.strict) {
4316
- const syncResult = st.schema.validateAtPath(st.form.value, void 0, { sync: true });
4317
- if (!(syncResult instanceof Promise) && !syncResult.success) {
4318
- applySchemaErrorsForSubtree(st, [], syncResult.errors);
4319
- }
4462
+ const syncResult = st.schema.validateAtPath(st.form.value, void 0, { sync: true });
4463
+ if (!(syncResult instanceof Promise) && !syncResult.success) {
4464
+ applySchemaErrorsForSubtree(st, [], syncResult.errors);
4320
4465
  }
4321
- st.firstValidationDone.value = initialFirstValidationGate(st.schema, st.strict);
4466
+ st.firstValidationDone.value = initialFirstValidationGate(st.schema);
4322
4467
  queueInitialAsyncValidation(st);
4323
4468
  const now = (/* @__PURE__ */ new Date()).toISOString();
4324
4469
  for (const [pathKey, record] of st.fields) {
@@ -4408,10 +4553,6 @@ function clearFieldRecordFlags(st, pathKey) {
4408
4553
  if (record === void 0) return;
4409
4554
  st.fields.set(pathKey, withClearedHistoryFlags(record, (/* @__PURE__ */ new Date()).toISOString()));
4410
4555
  }
4411
- function isPristineAtPath(st, path) {
4412
- const { key, segments } = canonicalizePath(path);
4413
- return isPristineAtPathByKey(st, key, segments);
4414
- }
4415
4556
  function isPristineAtPathByKey(st, key, segments) {
4416
4557
  if (st.blankPaths.has(key) !== st.originalBlankPaths.has(key)) return false;
4417
4558
  const entry = st.originals.get(key);
@@ -4445,12 +4586,8 @@ function getFieldRecord(st, path) {
4445
4586
  const { key } = canonicalizePath(path);
4446
4587
  return st.fields.get(key);
4447
4588
  }
4448
- function getOriginalAtPath(st, path) {
4449
- const { key } = canonicalizePath(path);
4450
- return st.originals.get(key)?.value;
4451
- }
4452
4589
  function createFormStore(options) {
4453
- const { formKey, schema, defaultValues, strict = true, hydration } = options;
4590
+ const { formKey, schema, defaultValues, hydration } = options;
4454
4591
  const ssr = options.ssr === true;
4455
4592
  const ssrPrefetch = options.ssrPrefetch;
4456
4593
  const rememberVariants = options.rememberVariants !== false;
@@ -4458,22 +4595,16 @@ function createFormStore(options) {
4458
4595
  const fieldValidationDebounceMs = normalizeNumericOption({
4459
4596
  value: options.debounceMs ?? DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS,
4460
4597
  source: "useForm.debounceMs",
4461
- allowInfinity: false,
4462
4598
  min: 0,
4463
4599
  defaultValue: DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS
4464
4600
  });
4465
- const coerceIndex = resolveCoercionIndex(options.coerce);
4466
- const resolvedGetDisplayState = resolveGetDisplayState(options.getDisplayState);
4601
+ const coerceEnabled = resolveCoerceEnabled(options.coerce);
4467
4602
  const cleanupHooks = [];
4468
4603
  const modules = /* @__PURE__ */ new Map();
4469
4604
  const fieldValidatingSince = reactive(/* @__PURE__ */ new Map());
4470
4605
  const displayEngine = createDisplayEngine(ssr);
4471
4606
  cleanupHooks.push(() => displayEngine.dispose());
4472
- const schemaResponse = computeBaselineResponse(
4473
- schema,
4474
- strict,
4475
- defaultValues
4476
- );
4607
+ const schemaResponse = computeBaselineResponse(schema, defaultValues);
4477
4608
  const schemaInitialData = schemaResponse.data;
4478
4609
  const authoredPaths = /* @__PURE__ */ new Set();
4479
4610
  const initialData = hydration !== void 0 ? hydration.form : structuralSnapshot(schemaInitialData);
@@ -4486,16 +4617,15 @@ function createFormStore(options) {
4486
4617
  const v = getAtPath(toRaw(form.value), arraySegs);
4487
4618
  return Array.isArray(v) ? v.length : 0;
4488
4619
  });
4489
- const fields = reactive(/* @__PURE__ */ new Map());
4620
+ const fields = shallowReactive(/* @__PURE__ */ new Map());
4490
4621
  const domBinding = shallowRef(null);
4491
- const errorCells = reactive(/* @__PURE__ */ new Map());
4492
- const originals = reactive(/* @__PURE__ */ new Map());
4622
+ const errorCells = shallowReactive(/* @__PURE__ */ new Map());
4623
+ const originals = shallowReactive(/* @__PURE__ */ new Map());
4493
4624
  const removedSubtrees = /* @__PURE__ */ new Set();
4494
- const initialTransientList = hydration?.blankPaths ?? options.initialBlankPaths ?? [];
4495
4625
  const blankPaths = reactive(/* @__PURE__ */ new Set());
4496
4626
  const originalBlankPaths = /* @__PURE__ */ new Set();
4497
- for (const raw of initialTransientList) {
4498
- const key = coerceToPathKey(raw);
4627
+ const seededBlankPaths = hydration !== void 0 ? (hydration.blankPaths ?? []).map((dotted) => canonicalizePath(dotted).key) : options.initialBlankPaths ?? [];
4628
+ for (const key of seededBlankPaths) {
4499
4629
  blankPaths.add(key);
4500
4630
  originalBlankPaths.add(key);
4501
4631
  }
@@ -4512,6 +4642,9 @@ function createFormStore(options) {
4512
4642
  }
4513
4643
  return result;
4514
4644
  });
4645
+ const errorPathIndex = computed(
4646
+ () => buildErrorPathIndex(errorCells, derivedBlankErrors.value)
4647
+ );
4515
4648
  const submitting = ref(false);
4516
4649
  const activeSubmissions = ref(0);
4517
4650
  const submissionAttempts = ref(0);
@@ -4544,7 +4677,7 @@ function createFormStore(options) {
4544
4677
  const defaultsResolved = ref(false);
4545
4678
  const activated = ref(false);
4546
4679
  const activationPromise = ref(void 0);
4547
- const firstValidationDone = ref(initialFirstValidationGate(schema, strict));
4680
+ const firstValidationDone = ref(initialFirstValidationGate(schema));
4548
4681
  watch(activeValidations, (now, prev) => {
4549
4682
  if (prev > 0 && now === 0) {
4550
4683
  firstValidationDone.value = true;
@@ -4562,6 +4695,33 @@ function createFormStore(options) {
4562
4695
  const activeTransforms = ref(0);
4563
4696
  const transformRuns = /* @__PURE__ */ new Map();
4564
4697
  const transformWaiters = [];
4698
+ const errorWindows = /* @__PURE__ */ new Map();
4699
+ const pathSweep = createDynamicPathSweep({
4700
+ onFormChange: (listener) => {
4701
+ formChangeListeners.add(listener);
4702
+ },
4703
+ isFixedObjectAtPath: (path) => schema.isFixedObjectAtPath(path)
4704
+ });
4705
+ pathSweep.onEvict((key) => {
4706
+ fields.delete(key);
4707
+ fieldValidationState.delete(key);
4708
+ authoredPaths.delete(key);
4709
+ if (originals.get(key)?.value === void 0) originals.delete(key);
4710
+ errorWindows.delete(key);
4711
+ });
4712
+ const errorWindowAt = (prefix, prefixKey) => {
4713
+ let cached = errorWindows.get(prefixKey);
4714
+ if (cached === void 0) {
4715
+ const frozen = [...prefix];
4716
+ cached = computed((prev) => {
4717
+ const next = windowUnder(errorPathIndex.value, frozen);
4718
+ return prev !== void 0 && isSameWindow(prev, next) ? prev : next;
4719
+ });
4720
+ errorWindows.set(prefixKey, cached);
4721
+ pathSweep.track(frozen, prefixKey);
4722
+ }
4723
+ return cached.value;
4724
+ };
4565
4725
  const arrayBookkeeping = createArrayBookkeeping({
4566
4726
  form,
4567
4727
  fields,
@@ -4579,6 +4739,7 @@ function createFormStore(options) {
4579
4739
  touchFieldRecord: (pathKey, path, patch) => touchFieldRecord(st, pathKey, path, patch),
4580
4740
  decFieldValidation: (key) => decFieldValidation(st, key)
4581
4741
  });
4742
+ const bind = (fn) => (...args) => fn(st, ...args);
4582
4743
  const st = {
4583
4744
  // --- public data (the FormStore contract's state members) ---
4584
4745
  formKey,
@@ -4586,10 +4747,11 @@ function createFormStore(options) {
4586
4747
  fields,
4587
4748
  errorCells,
4588
4749
  derivedBlankErrors,
4750
+ errorWindowAt,
4589
4751
  originals,
4752
+ pathSweep,
4590
4753
  schema,
4591
4754
  ssr,
4592
- getDisplayState: resolvedGetDisplayState,
4593
4755
  submitting,
4594
4756
  activeSubmissions,
4595
4757
  submissionAttempts,
@@ -4617,12 +4779,20 @@ function createFormStore(options) {
4617
4779
  displayEngine,
4618
4780
  domBinding,
4619
4781
  modules,
4620
- coerceIndex,
4782
+ coerceEnabled,
4621
4783
  blankPaths,
4622
4784
  originalBlankPaths,
4623
4785
  // --- kernel-internal state ---
4624
- strict,
4625
- defaultValues,
4786
+ // Defensive copy, not a fix for an observed alias: today
4787
+ // `getDefaultValues` happens to build a fresh tree, so form storage
4788
+ // does not currently share structure with the consumer's object.
4789
+ // That is an adapter implementation detail rather than a contract,
4790
+ // and this field is now durable state that every `reset()` reads, so
4791
+ // it should not depend on it. The copy also means a consumer who
4792
+ // keeps a reference to the literal they passed cannot mutate the
4793
+ // form's defaults from outside. `reset()` snapshots for a harder
4794
+ // reason (see there) where an alias IS reachable.
4795
+ defaultValues: structuralSnapshot(defaultValues),
4626
4796
  ssrPrefetch,
4627
4797
  rememberVariants,
4628
4798
  fieldValidationMode,
@@ -4649,16 +4819,20 @@ function createFormStore(options) {
4649
4819
  transformTokenSeq: 0,
4650
4820
  warnedDisabledWrite: false,
4651
4821
  // --- methods: thin per-instance skins over the module kernel ---
4652
- rehydrate: () => rehydrate(st),
4653
- activate: () => activate(st),
4654
- pathHasAsyncValidation: (path) => pathHasAsyncValidation(st, path),
4655
- pathHasAsyncValidationByKey: (key, segments) => pathHasAsyncValidationByKey(st, key, segments),
4656
- applyFormReplacement: (next, meta) => applyFormReplacement(st, next, meta),
4657
- setValueAtPath: (path, value, meta) => setValueAtPath(st, path, value, meta),
4658
- getValueAtPath: (path) => getValueAtPath(st, path),
4659
- arrayElementKey: (path) => arrayElementKey(st, path),
4660
- reset: (nextDefaultValues) => reset(st, nextDefaultValues),
4661
- resetField: (path) => resetField(st, path),
4822
+ rehydrate: bind(rehydrate),
4823
+ activate: bind(activate),
4824
+ adoptResolvedDefaults: bind(adoptResolvedDefaults),
4825
+ pathHasAsyncValidationByKey: bind(pathHasAsyncValidationByKey),
4826
+ applyFormReplacement: bind(applyFormReplacement),
4827
+ setValueAtPath: bind(setValueAtPath),
4828
+ getValueAtPath: bind(getValueAtPath),
4829
+ arrayElementKey: bind(arrayElementKey),
4830
+ reset: bind(reset),
4831
+ resetField: bind(resetField),
4832
+ // The schema/user pair below reads as a fold waiting to happen. It was
4833
+ // tried: a `setErrorsForPathIn(channel)` factory measured 10 B LARGER,
4834
+ // because gzip had already collected the rent on two adjacent copies
4835
+ // and the helper added a name the original did not need.
4662
4836
  setSchemaErrorsForPath: (path, entries) => setErrorChannelForKey(
4663
4837
  st,
4664
4838
  canonicalizePath(path).key,
@@ -4667,7 +4841,7 @@ function createFormStore(options) {
4667
4841
  ),
4668
4842
  setAllSchemaErrors: (entries) => replaceErrorChannel(st, "schema", entries),
4669
4843
  clearSchemaErrors: (path) => clearErrorChannel(st, "schema", path),
4670
- applySchemaErrorsForSubtree: (path, entries) => applySchemaErrorsForSubtree(st, path, entries),
4844
+ applySchemaErrorsForSubtree: bind(applySchemaErrorsForSubtree),
4671
4845
  setAllUserErrors: (entries) => replaceErrorChannel(st, "user", entries),
4672
4846
  setUserErrorsForPath: (path, entries) => setErrorChannelForKey(
4673
4847
  st,
@@ -4676,37 +4850,34 @@ function createFormStore(options) {
4676
4850
  entries.length === 0 ? NO_ERRORS : [...entries]
4677
4851
  ),
4678
4852
  clearUserErrors: (path) => clearErrorChannel(st, "user", path),
4679
- restoreErrorCells: (entries) => restoreErrorCells(st, entries),
4680
- getErrorsForPath: (path) => getErrorsForPath(st, path),
4681
- ensurePathOrdinal: (key) => ensurePathOrdinal(st, key),
4682
- noteDomConnected: (path) => noteDomConnected(st, path),
4683
- noteDomDisconnected: (path) => noteDomDisconnected(st, path),
4684
- markFocused: (path, focused, meta) => markFocused(st, path, focused, meta),
4685
- markInteracted: (path) => markInteracted(st, path),
4686
- touchAtPath: (segments) => touchAtPath(st, segments),
4687
- interactAtPath: (segments) => interactAtPath(st, segments),
4688
- markConnectedOptimistically: (path) => markConnectedOptimistically(st, path),
4689
- isPristineAtPath: (path) => isPristineAtPath(st, path),
4690
- isPristineAtPathByKey: (key, segments) => isPristineAtPathByKey(st, key, segments),
4691
- hasStructuralChangeUnder: (path) => hasStructuralChangeUnder(st, path),
4692
- hasRemovedSubtreeUnder: (path) => hasRemovedSubtreeUnder(st, path),
4693
- getFieldRecord: (path) => getFieldRecord(st, path),
4694
- getOriginalAtPath: (path) => getOriginalAtPath(st, path),
4695
- cancelFieldValidation: () => cancelFieldValidation(st),
4696
- beginTransform: (key, holder) => beginTransform(st, key, holder),
4697
- isCurrentTransform: (key, token) => isCurrentTransform(st, key, token),
4698
- endTransform: (key, token) => endTransform(st, key, token),
4699
- setTransformError: (key, err) => setTransformError(st, key, err),
4700
- cancelTransforms: () => cancelTransforms(st),
4701
- cancelTransformsUnder: (prefix) => cancelTransformsUnder(st, prefix),
4702
- settleTransforms: (path) => settleTransforms(st, path),
4703
- scheduleFieldValidation: (path, immediate, override) => scheduleFieldValidation(st, path, immediate, override),
4704
- onFormChange: (listener) => onFormChange(st, listener),
4705
- onSubmitSuccess: (listener) => onSubmitSuccess(st, listener),
4706
- onReset: (listener) => onReset(st, listener),
4707
- emitSubmitSuccess: () => emitSubmitSuccess(st),
4708
- registerCleanup: (fn) => registerCleanup(st, fn),
4709
- dispose: () => dispose(st)
4853
+ restoreErrorCells: bind(restoreErrorCells),
4854
+ getErrorsForPath: bind(getErrorsForPath),
4855
+ ensurePathOrdinal: bind(ensurePathOrdinal),
4856
+ noteDomConnected: bind(noteDomConnected),
4857
+ noteDomDisconnected: bind(noteDomDisconnected),
4858
+ markFocused: bind(markFocused),
4859
+ markInteracted: bind(markInteracted),
4860
+ touchAtPath: bind(touchAtPath),
4861
+ interactAtPath: bind(interactAtPath),
4862
+ markConnectedOptimistically: bind(markConnectedOptimistically),
4863
+ isPristineAtPathByKey: bind(isPristineAtPathByKey),
4864
+ hasStructuralChangeUnder: bind(hasStructuralChangeUnder),
4865
+ hasRemovedSubtreeUnder: bind(hasRemovedSubtreeUnder),
4866
+ getFieldRecord: bind(getFieldRecord),
4867
+ cancelFieldValidation: bind(cancelFieldValidation),
4868
+ beginTransform: bind(beginTransform),
4869
+ isCurrentTransform: bind(isCurrentTransform),
4870
+ endTransform: bind(endTransform),
4871
+ setTransformError: bind(setTransformError),
4872
+ cancelTransformsUnder: bind(cancelTransformsUnder),
4873
+ settleTransforms: bind(settleTransforms),
4874
+ scheduleFieldValidation: bind(scheduleFieldValidation),
4875
+ onFormChange: bind(onFormChange),
4876
+ onSubmitSuccess: bind(onSubmitSuccess),
4877
+ onReset: bind(onReset),
4878
+ emitSubmitSuccess: bind(emitSubmitSuccess),
4879
+ registerCleanup: bind(registerCleanup),
4880
+ dispose: bind(dispose)
4710
4881
  };
4711
4882
  rebuildAuthoredPaths(st, defaultValues, schemaInitialData);
4712
4883
  seedOriginalsFromBaseline(st, schemaInitialData, true);
@@ -4748,7 +4919,7 @@ function createFormStore(options) {
4748
4919
  blurredAfterInteraction: false
4749
4920
  });
4750
4921
  });
4751
- if (strict && !schemaResponse.success) {
4922
+ if (!schemaResponse.success) {
4752
4923
  replaceErrorChannel(st, "schema", schemaResponse.errors);
4753
4924
  }
4754
4925
  }
@@ -4779,24 +4950,23 @@ function useAbstractForm(configuration, options) {
4779
4950
  const resolvedDefaults = resolveTrichotomy(configuration.defaultValues);
4780
4951
  const materialisedDefaults = resolvedDefaults.kind === "sync" ? resolvedDefaults.value : void 0;
4781
4952
  const { defaultValues: _droppedDefaults, ...configWithoutDefaults } = configuration;
4782
- const trichotomyOverride = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
4783
- const merged = mergeWithDefaults(registry.defaults, trichotomyOverride);
4784
- const maxRecursionDepth = normalizeNumericOption({
4785
- value: merged.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH,
4786
- source: "useForm.maxRecursionDepth",
4787
- allowInfinity: true,
4788
- min: 0,
4789
- defaultValue: DEFAULT_MAX_RECURSION_DEPTH
4790
- });
4791
- const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
4953
+ const materialisedConfiguration = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
4792
4954
  const existing = registry.forms.get(key);
4955
+ const resolvedSchema = getComputedSchema(key, configuration.schema, {
4956
+ maxRecursionDepth: DEFAULT_MAX_RECURSION_DEPTH
4957
+ }) ;
4793
4958
  if (existing !== void 0) {
4794
4959
  void import('../chunks/dev-key-collision-warnings.mjs').then((m) => {
4795
4960
  void m.warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
4796
4961
  });
4797
4962
  }
4798
4963
  const hadPendingHydration = registry.pendingHydration.has(key);
4799
- const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
4964
+ const state = existing ?? buildFreshState(
4965
+ key,
4966
+ resolvedSchema,
4967
+ materialisedConfiguration,
4968
+ registry
4969
+ );
4800
4970
  if (existing !== void 0) ; else if (resolvedDefaults.kind === "sync") {
4801
4971
  state.defaultsResolved.value = true;
4802
4972
  }
@@ -4804,6 +4974,7 @@ function useAbstractForm(configuration, options) {
4804
4974
  const factory = resolvedDefaults.factory;
4805
4975
  state.defaultValuesFactory.value = factory;
4806
4976
  if (hadPendingHydration) {
4977
+ state.adoptResolvedDefaults(toRaw(state.form.value));
4807
4978
  state.hydrating.value = false;
4808
4979
  state.defaultsResolved.value = true;
4809
4980
  } else if (registry.ssr) {
@@ -4820,8 +4991,8 @@ function useAbstractForm(configuration, options) {
4820
4991
  const releaseConsumer = registry.trackConsumer(key);
4821
4992
  onScopeDispose(releaseConsumer);
4822
4993
  }
4823
- if (existing === void 0 && merged.history !== void 0) {
4824
- const historyModule = merged.history.attach(state);
4994
+ if (existing === void 0 && materialisedConfiguration.history !== void 0) {
4995
+ const historyModule = materialisedConfiguration.history.attach(state);
4825
4996
  state.modules.set(HISTORY_MODULE_KEY, historyModule);
4826
4997
  state.registerCleanup(() => historyModule.dispose());
4827
4998
  }
@@ -4834,36 +5005,16 @@ function useAbstractForm(configuration, options) {
4834
5005
  provide(kFormInstanceId, formInstanceId);
4835
5006
  }
4836
5007
  const apiOptions = pickDefined({
4837
- onInvalidSubmit: merged.onInvalidSubmit,
5008
+ focusOnInvalidSubmit: materialisedConfiguration.focusOnInvalidSubmit,
4838
5009
  history: state.modules.get(HISTORY_MODULE_KEY),
4839
- validateOn: merged.validateOn,
4840
- debounceMs: merged.debounceMs,
4841
- getDisplayState: merged.getDisplayState,
4842
- coerce: merged.coerce,
4843
- rememberVariants: merged.rememberVariants,
4844
- autoAria: merged.autoAria
5010
+ validateOn: materialisedConfiguration.validateOn,
5011
+ debounceMs: materialisedConfiguration.debounceMs,
5012
+ coerce: materialisedConfiguration.coerce,
5013
+ rememberVariants: materialisedConfiguration.rememberVariants
4845
5014
  });
4846
5015
  const api = buildFormApi(state, formInstanceId, apiOptions);
4847
5016
  return api;
4848
5017
  }
4849
- function mergeWithDefaults(defaults, configuration) {
4850
- return {
4851
- ...configuration,
4852
- ...pickDefined({
4853
- strict: configuration.strict ?? defaults.strict,
4854
- onInvalidSubmit: configuration.onInvalidSubmit ?? defaults.onInvalidSubmit,
4855
- history: configuration.history ?? defaults.history,
4856
- rememberVariants: configuration.rememberVariants ?? defaults.rememberVariants,
4857
- disabled: configuration.disabled ?? defaults.disabled,
4858
- coerce: configuration.coerce ?? defaults.coerce,
4859
- validateOn: configuration.validateOn ?? defaults.validateOn,
4860
- debounceMs: configuration.debounceMs ?? defaults.debounceMs,
4861
- getDisplayState: configuration.getDisplayState ?? defaults.getDisplayState,
4862
- maxRecursionDepth: configuration.maxRecursionDepth ?? defaults.maxRecursionDepth,
4863
- autoAria: configuration.autoAria ?? defaults.autoAria
4864
- })
4865
- };
4866
- }
4867
5018
  const HISTORY_MODULE_KEY = "history";
4868
5019
  function buildFreshState(key, schema, configuration, registry) {
4869
5020
  const pending = registry.pendingHydration.get(key);
@@ -4883,13 +5034,11 @@ function buildFreshState(key, schema, configuration, registry) {
4883
5034
  hydration: pending,
4884
5035
  ssr: registry.ssr,
4885
5036
  ...pickDefined({
4886
- strict: configuration.strict,
4887
5037
  validateOn: configuration.validateOn,
4888
5038
  debounceMs: configuration.debounceMs,
4889
5039
  rememberVariants: configuration.rememberVariants,
4890
5040
  disabled: configuration.disabled,
4891
5041
  coerce: configuration.coerce,
4892
- getDisplayState: configuration.getDisplayState,
4893
5042
  initialBlankPaths
4894
5043
  }),
4895
5044
  // Server-only: bind the SSR prefetch coordination handles. `enqueue`
@@ -5032,12 +5181,13 @@ function makeRegisterValueProxy(capturedRegisterValue) {
5032
5181
  get(_target, prop) {
5033
5182
  if (prop === "__v_isRef") return true;
5034
5183
  if (prop === "value") return capturedRegisterValue.value;
5184
+ if (prop === "isBound") return capturedRegisterValue.value !== void 0;
5035
5185
  const v = capturedRegisterValue.value;
5036
5186
  if (v === void 0) return void 0;
5037
5187
  return Reflect.get(v, prop);
5038
5188
  },
5039
5189
  has(_target, prop) {
5040
- if (prop === "__v_isRef" || prop === "value") return true;
5190
+ if (prop === "__v_isRef" || prop === "value" || prop === "isBound") return true;
5041
5191
  const v = capturedRegisterValue.value;
5042
5192
  if (v === void 0) return false;
5043
5193
  return Reflect.has(v, prop);
@@ -5058,7 +5208,7 @@ function makeRegisterValueProxy(capturedRegisterValue) {
5058
5208
  }
5059
5209
  });
5060
5210
  }
5061
- function useRegister() {
5211
+ function useRegister(options) {
5062
5212
  const instance = getCurrentInstance();
5063
5213
  if (instance === null) {
5064
5214
  warnOutsideSetup();
@@ -5098,7 +5248,7 @@ function useRegister() {
5098
5248
  if (el !== null && el !== void 0 && typeof el === "object") {
5099
5249
  el[REGISTER_OWNER_MARKER] = true;
5100
5250
  }
5101
- if (capturedRegisterValue.value === void 0) {
5251
+ if (capturedRegisterValue.value === void 0 && options?.optional !== true) {
5102
5252
  warnNoParentRV(instance);
5103
5253
  }
5104
5254
  });
@@ -5118,7 +5268,7 @@ function warnNoParentRV(instance) {
5118
5268
  warnedNoParentRV.add(instance);
5119
5269
  const frame = captureUserCallSite();
5120
5270
  console.warn(
5121
- `[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}` : "")
5271
+ `[attaform] useRegister: no parent registerValue prop; RegisterValue fields will read as undefined. Pass v-register on the parent: \`<YourComponent v-register="form.register('field')" />\`. If this component is meant to work without a form too, say so with \`useRegister({ optional: true })\` and branch on \`rv?.isBound\`.` + (frame !== void 0 ? ` ${frame}` : "")
5122
5272
  );
5123
5273
  }
5124
5274
 
@@ -5202,38 +5352,36 @@ function createWizardHistory(param) {
5202
5352
  };
5203
5353
  }
5204
5354
 
5205
- const NOOP_FINGERPRINT = "attaform:wizard-noop";
5206
5355
  const EMPTY_SLIM_KINDS = /* @__PURE__ */ new Set();
5207
- function buildNoopWizardSchema(formKey) {
5208
- const emptyValue = {};
5209
- const success = {
5210
- success: true,
5211
- data: emptyValue,
5212
- errors: void 0,
5213
- formKey
5214
- };
5215
- const defaultsResponse = {
5216
- success: true,
5217
- data: emptyValue,
5218
- errors: void 0,
5219
- formKey
5220
- };
5221
- return {
5222
- fingerprint: () => Promise.resolve(NOOP_FINGERPRINT),
5223
- getDefaultValues: () => defaultsResponse,
5356
+ const EMPTY_VALUE = {};
5357
+ const NOOP_SUCCESS = {
5358
+ success: true,
5359
+ data: EMPTY_VALUE,
5360
+ errors: void 0
5361
+ };
5362
+ const NOOP_DEFAULTS = NOOP_SUCCESS;
5363
+ let NOOP_SCHEMA;
5364
+ function buildNoopWizardSchema() {
5365
+ NOOP_SCHEMA ?? (NOOP_SCHEMA = {
5366
+ getDefaultValues: () => NOOP_DEFAULTS,
5224
5367
  getDefaultAtPath: () => void 0,
5225
5368
  getEmptyValueAtPath: () => void 0,
5226
5369
  isPreprocessOrCoerceLeaf: () => false,
5370
+ isOpaqueLeafAtPath: () => false,
5227
5371
  arrayShapeAtPath: () => null,
5228
5372
  isFixedObjectAtPath: (path) => path.length === 0,
5373
+ // The noop schema declares no shape at all, so nothing under it is
5374
+ // addressable, root included.
5375
+ entryKeyKindAtPath: () => void 0,
5229
5376
  getSchemasAtPath: () => [],
5230
- validateAtPath: () => success,
5377
+ validateAtPath: () => NOOP_SUCCESS,
5231
5378
  getSlimPrimitiveTypesAtPath: () => new Set(EMPTY_SLIM_KINDS),
5232
5379
  isLeafAtPath: () => false,
5233
5380
  isRequiredAtPath: () => false,
5234
5381
  getUnionDiscriminatorAtPath: () => void 0,
5235
5382
  hasDiscriminatedUnions: () => false
5236
- };
5383
+ });
5384
+ return NOOP_SCHEMA;
5237
5385
  }
5238
5386
 
5239
5387
  function buildCallableReadonlySnapshotProxy(opts) {
@@ -5352,7 +5500,7 @@ function useWizard(options) {
5352
5500
  if (typeof slot !== "string") continue;
5353
5501
  if (noopForms.has(slot)) continue;
5354
5502
  const noop = useAbstractForm({
5355
- schema: buildNoopWizardSchema(slot),
5503
+ schema: buildNoopWizardSchema(),
5356
5504
  key: slot
5357
5505
  });
5358
5506
  noopForms.set(slot, noop);
@@ -5363,7 +5511,7 @@ function useWizard(options) {
5363
5511
  const noop = lazyNoopScope.run(
5364
5512
  () => useAbstractForm(
5365
5513
  {
5366
- schema: buildNoopWizardSchema(key),
5514
+ schema: buildNoopWizardSchema(),
5367
5515
  key
5368
5516
  },
5369
5517
  { registry }
@@ -6024,6 +6172,24 @@ function useWizard(options) {
6024
6172
  const target = list[idx + 1];
6025
6173
  moveTo(target.key);
6026
6174
  }
6175
+ let cancelDeferredAdvance = null;
6176
+ function advanceWhenInFlightSubmitLands(store, key) {
6177
+ cancelDeferredAdvance?.();
6178
+ const attemptsAtRequest = store.submissionAttempts.value;
6179
+ const off = store.onSubmitSuccess(() => {
6180
+ off();
6181
+ cancelDeferredAdvance = null;
6182
+ if (store.submissionAttempts.value !== attemptsAtRequest) return;
6183
+ if (activeKey.value !== key) return;
6184
+ advanceOne();
6185
+ });
6186
+ cancelDeferredAdvance = off;
6187
+ }
6188
+ function inFlightActiveStore() {
6189
+ const store = registry.forms.get(activeKey.value);
6190
+ if (store === void 0 || store.activeSubmissions.value === 0) return void 0;
6191
+ return store;
6192
+ }
6027
6193
  async function next() {
6028
6194
  if (submitting.value) {
6029
6195
  {
@@ -6050,7 +6216,12 @@ function useWizard(options) {
6050
6216
  return;
6051
6217
  }
6052
6218
  const active = list[idx];
6053
- if (active !== void 0 && active.isGate) {
6219
+ if (active !== void 0 && active.isGate && !clearedGates.has(active.key)) {
6220
+ const inFlight = inFlightActiveStore();
6221
+ if (inFlight !== void 0) {
6222
+ advanceWhenInFlightSubmitLands(inFlight, activeKey.value);
6223
+ return;
6224
+ }
6054
6225
  await tryNext();
6055
6226
  return;
6056
6227
  }
@@ -6082,11 +6253,16 @@ function useWizard(options) {
6082
6253
  const form = activeForm.value;
6083
6254
  if (form === void 0) return false;
6084
6255
  const before = activeKey.value;
6256
+ const inFlight = inFlightActiveStore();
6257
+ if (inFlight !== void 0) {
6258
+ advanceWhenInFlightSubmitLands(inFlight, before);
6259
+ return false;
6260
+ }
6085
6261
  let ranClean = false;
6086
6262
  await asHandleSubmitSource(form).handleSubmit(() => {
6087
6263
  ranClean = true;
6088
6264
  })();
6089
- if (ranClean) advanceOne();
6265
+ if (ranClean && activeKey.value === before) advanceOne();
6090
6266
  return activeKey.value !== before;
6091
6267
  }
6092
6268
  function back() {
@@ -6366,6 +6542,7 @@ function useWizard(options) {
6366
6542
  onScopeDispose(() => {
6367
6543
  historyHandle.dispose();
6368
6544
  lazyNoopScope.stop();
6545
+ cancelDeferredAdvance?.();
6369
6546
  });
6370
6547
  }
6371
6548
  const explicitKey = options.key;
@@ -6558,4 +6735,4 @@ function escapeForInlineScript(json) {
6558
6735
  });
6559
6736
  }
6560
6737
 
6561
- export { AttaformErrorCode as A, DEFAULT_TIMINGS as D, defaultDisplayState as a, defineCoercion as b, injectWizard as c, defaultCoercionRules as d, escapeForInlineScript as e, isUnset as f, gate as g, useAbstractForm as h, injectForm as i, useRegister as j, useWizard as k, lazy as l, makeDefaultDisplayState as m, isPlainRecord as n, humanize as o, slimKindOf as s, unset as u };
6738
+ export { AttaformErrorCode as A, injectWizard as a, isUnset as b, useAbstractForm as c, useRegister as d, escapeForInlineScript as e, useWizard as f, gate as g, getAtPath as h, injectForm as i, humanize as j, isPlainRecord as k, lazy as l, slimKindOf as s, unset as u };