attaform 0.31.0 → 0.31.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/abstract.d.mts +5 -5
  2. package/dist/abstract.mjs +5 -4
  3. package/dist/dev/abstract.mjs +5 -4
  4. package/dist/dev/chunks/devtools.mjs +1 -1
  5. package/dist/dev/directive.mjs +1 -1
  6. package/dist/dev/history.mjs +126 -6
  7. package/dist/dev/index.mjs +6 -5
  8. package/dist/dev/runtime/plugins/attaform.mjs +1 -1
  9. package/dist/dev/shared/{attaform-dev.zlvDr0WB.mjs → attaform-dev.04aupFBW.mjs} +3 -2
  10. package/dist/dev/shared/{attaform-dev.CtyqGia7.mjs → attaform-dev.BKWPjEB2.mjs} +5 -4
  11. package/dist/dev/shared/{attaform-dev.DlaT4xGd.mjs → attaform-dev.BnLLxX2l.mjs} +4 -4
  12. package/dist/dev/shared/{attaform-dev.B1q-x9R9.mjs → attaform-dev.Bu6wgrA5.mjs} +1 -1
  13. package/dist/dev/shared/{attaform-dev.k5X47GMD.mjs → attaform-dev.CKSqs_xQ.mjs} +3 -2
  14. package/dist/dev/shared/{attaform-dev.DJ3nsWw6.mjs → attaform-dev.Cf_hZfbT.mjs} +18 -9
  15. package/dist/dev/shared/{attaform-dev.y9x9Vp9F.mjs → attaform-dev.DLk51W7-.mjs} +565 -926
  16. package/dist/dev/shared/attaform-dev.DoG8mZTq.mjs +127 -0
  17. package/dist/dev/shared/attaform-dev.G-3XDl9F.mjs +1192 -0
  18. package/dist/dev/shared/{attaform-dev.Caibt7sb.mjs → attaform-dev.oguHqv1W.mjs} +11 -10
  19. package/dist/dev/zod-v3.mjs +7 -6
  20. package/dist/dev/zod-v4.mjs +7 -6
  21. package/dist/dev/zod.mjs +6 -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 +126 -6
  26. package/dist/index.d.mts +16 -55
  27. package/dist/index.mjs +6 -5
  28. package/dist/runtime/plugins/attaform.mjs +1 -1
  29. package/dist/shared/{attaform.DL2nl_QR.mjs → attaform.3kV6PqVJ.mjs} +11 -10
  30. package/dist/shared/{attaform.L011tHgJ.mjs → attaform.BJeiuJE2.mjs} +4 -4
  31. package/dist/shared/attaform.BNUhW4qC.mjs +103 -0
  32. package/dist/shared/{attaform.CdZIEf7_.mjs → attaform.BhtAZysa.mjs} +3 -2
  33. package/dist/shared/{attaform.BWUn8Kjy.mjs → attaform.BlAmZ-WU.mjs} +18 -9
  34. package/dist/shared/{attaform.LUPC-2Qg.mjs → attaform.CMzc4bPI.mjs} +5 -4
  35. package/dist/shared/{attaform.C4shmwyJ.mjs → attaform.CdA9bfRv.mjs} +1 -1
  36. package/dist/shared/attaform.CgyyJVuq.mjs +1187 -0
  37. package/dist/shared/{attaform.DAo0QBmS.d.mts → attaform.CibLYe9-.d.mts} +214 -173
  38. package/dist/shared/{attaform.DNPJPOO7.mjs → attaform.DCUJCKyG.mjs} +562 -925
  39. package/dist/shared/{attaform.D9RqPgGu.d.mts → attaform.DCyZT7u-.d.mts} +1 -1
  40. package/dist/shared/{attaform.CRVe8s82.d.mts → attaform.DD0Y8krq.d.mts} +43 -19
  41. package/dist/shared/{attaform.DxKQsqFZ.mjs → attaform.T0YjhjPU.mjs} +3 -2
  42. package/dist/shared/{attaform.DZnrDrNb.d.mts → attaform.bI_4nKZp.d.mts} +119 -8
  43. package/dist/zod-v3.d.mts +4 -4
  44. package/dist/zod-v3.mjs +7 -6
  45. package/dist/zod-v4.d.mts +15 -11
  46. package/dist/zod-v4.mjs +7 -6
  47. package/dist/zod.d.mts +4 -4
  48. package/dist/zod.mjs +6 -5
  49. package/package.json +2 -1
  50. package/skills/attaform/SKILL.md +1 -0
  51. package/dist/dev/shared/attaform-dev.CZz4GYZ6.mjs +0 -386
  52. package/dist/shared/attaform.8mfKZCwH.mjs +0 -358
@@ -1,81 +1,9 @@
1
1
  import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, nextTick, warn, reactive, shallowReactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, effectScope, onBeforeMount, onBeforeUpdate, onMounted } 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, c as callConsumerFn, t as reportConsumerThrow, u as ANONYMOUS_WIZARD_KEY_PREFIX } from './attaform-dev.CZz4GYZ6.mjs';
3
- import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform-dev.B1q-x9R9.mjs';
4
- import { R as REGISTER_OWNER_MARKER, V as V_REGISTER_MARKER, d as armDomBinding, r as recordInheritedModifiers, w as withInheritedModifiers } from './attaform-dev.k5X47GMD.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.zlvDr0WB.mjs';
6
-
7
- const AttaformErrorCode = {
8
- /** A required field is in the blank set: no value was supplied. */
9
- NoValueSupplied: "atta:no-value-supplied",
10
- /** The schema adapter's `validateAtPath` threw synchronously. */
11
- AdapterThrew: "atta:adapter-threw",
12
- /**
13
- * User code inside a `z.preprocess`, `.refine`, or `.transform`
14
- * threw (sync or async). The adapter caught the throw and surfaced
15
- * it as a `ValidationError` at the field path so the form's normal
16
- * error pipeline handles it instead of leaking as an unhandled
17
- * rejection or routing through `submitError`.
18
- */
19
- ValidatorThrew: "atta:validator-threw",
20
- /**
21
- * A function-form `defaultValues` factory threw or its promise
22
- * rejected. The runtime captures the raw error on `form.hydrateError`
23
- * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
24
- * the standard error pipeline carries the signal. Critical for the
25
- * SSR round-trip: `hydrateError` itself does not ride the wire
26
- * payload, but `schemaErrors` does, so the client sees the failure
27
- * after rehydration without an extra channel.
28
- */
29
- HydrationFailed: "atta:hydration-failed",
30
- /** The supplied path didn't resolve to any node in the schema. */
31
- PathNotFound: "atta:path-not-found",
32
- /**
33
- * A walked form's `activate()` (async `defaultValues` factory) threw
34
- * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
35
- * `ValidationError` at the form-level path (`[]`) so the wizard's
36
- * aggregate error pipeline can carry the failure alongside ordinary
37
- * validation errors. The raw factory error remains on
38
- * `form.hydrateError` for retry UX.
39
- */
40
- ActivationFailed: "atta:activation-failed",
41
- /**
42
- * Default code stamped on a manual error set through `form.setErrors`
43
- * when the caller omits an explicit `code`. The `setErrors` input is
44
- * lenient (`code` optional), so this is the fallback that keeps every
45
- * produced `ValidationError` carrying a stable, branchable identifier.
46
- * Override it per error by passing your own `code` (`api:…`, `auth:…`).
47
- */
48
- UserError: "atta:user-error",
49
- /**
50
- * Stamped on a `ValidationError` synthesized from a throw or rejection
51
- * out of a `handleSubmit` `onSubmit` callback. The raw error also lands
52
- * on `form.meta.submitError` (a raw-`Error` inspection channel); this
53
- * code marks the copy piped into the user-error layer so the failure
54
- * also surfaces on `form.errors` / `meta.ownErrors` / `firstOwnError`
55
- * where the UI already reads. A throw carrying its own `path` / `code`
56
- * keeps them; a bare `Error` or a non-Error throw defaults here at the
57
- * form-level path `[]`.
58
- */
59
- SubmitError: "atta:submit-error",
60
- /**
61
- * Synthesized by `wizard.handleSubmit` when the whole-wizard submit
62
- * would otherwise succeed but a `gate()` step is still uncleared. A
63
- * gate clears only on its member form's clean submit (confirmation), so
64
- * a downstream step that happens to be valid-by-default must not let the
65
- * wizard finish past an unconfirmed prerequisite. Emitted at the gate
66
- * form's key (path `[]`) so the aggregate error pipeline routes focus to
67
- * the gate, and `done` never latches.
68
- */
69
- GateNotCleared: "atta:gate-not-cleared"
70
- };
71
- function makeBlankRequiredError(segments) {
72
- return {
73
- message: "No value supplied",
74
- path: [...segments],
75
- code: AttaformErrorCode.NoValueSupplied
76
- };
77
- }
78
- const NO_ERRORS = Object.freeze([]);
2
+ import { a as ANONYMOUS_FORM_KEY_PREFIX, g as getAtPath, i as isPlainRecord, h as hasAtPath, b as makeBlankRequiredError, A as AttaformErrorCode, p as pickDefined, c as buildFieldArrayApi, s as structuralSnapshot, n as normalizeNumericOption, d as DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS, e as diffAndApply, f as mergeStructural, N as NO_ERRORS, j as createArrayBookkeeping, k as createVariantMemory, l as createArrayIdentity, o as isDescendable, q as migrateFieldRecords, r as setAtPath, t as remapForOp, u as isBarePatch, v as applyChangedKeys, w as cloneVariantSnapshot, x as setAtPathWithSchemaFill, y as tryInPlaceLeafWrite, R as RESERVED_KEY_PREFIX, z as DEFAULT_MAX_RECURSION_DEPTH, B as ANONYMOUS_WIZARD_KEY_PREFIX } from './attaform-dev.G-3XDl9F.mjs';
3
+ import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform-dev.Bu6wgrA5.mjs';
4
+ import { R as REGISTER_OWNER_MARKER, V as V_REGISTER_MARKER, d as armDomBinding, r as recordInheritedModifiers, w as withInheritedModifiers } from './attaform-dev.CKSqs_xQ.mjs';
5
+ import { e as canonicalizePath, i as isPathPrefix, f as pathsEqual, k as keyForSegments, m as mapSegmentKeys, b as ROOT_PATH_KEY, S as SubmitErrorHandlerError, t as toError, j as groupErrorsByKey, s as segmentsForPathKey, n as normalizeErrorInput, h as SET_MEMBER_SEGMENT, R as ROOT_PATH, l as normalizeErrorInputs, a as InvalidUseFormConfigError, d as ReservedFormKeyError } from './attaform-dev.04aupFBW.mjs';
6
+ import { r as readConsumerIndex, c as consumerKeys, b as readConsumerProp, d as safeOwnHas, i as isShadowedKey, s as safeAssign, a as safeOwnRead, h as callConsumerFn, j as consumerExtensible, k as consumerSymbolKeys, l as reportConsumerThrow } from './attaform-dev.DoG8mZTq.mjs';
79
7
 
80
8
  function isGateOpen(field, formMeta) {
81
9
  return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
@@ -141,7 +69,7 @@ function hashStableString(input, seed = 0) {
141
69
  const ANON_STEM = "atta";
142
70
  function readableFormKeyStem(formKey) {
143
71
  if (formKey === "" || formKey.startsWith(ANONYMOUS_FORM_KEY_PREFIX)) return ANON_STEM;
144
- const sanitized = formKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|(?<!-)-+$/g, "");
72
+ const sanitized = formKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|([^-])-+$/g, "$1");
145
73
  return sanitized === "" ? ANON_STEM : sanitized;
146
74
  }
147
75
  function fieldIdToken(formInstanceId, pathKey) {
@@ -175,323 +103,6 @@ function humanize(segment) {
175
103
  }).join(" ");
176
104
  }
177
105
 
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
- }
190
- function descendStep(value, segment) {
191
- if (value === null || value === void 0) return NOT_FOUND;
192
- if (typeof value !== "object") return NOT_FOUND;
193
- if (Array.isArray(value)) {
194
- if (typeof segment !== "number") return NOT_FOUND;
195
- if (!(segment in value)) return NOT_FOUND;
196
- return value[segment];
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
- }
203
- const record = value;
204
- const key = typeof segment === "number" ? String(segment) : segment;
205
- if (isShadowedKey(key)) {
206
- if (!safeOwnHas(record, key)) return NOT_FOUND;
207
- return safeOwnRead(record, key);
208
- }
209
- if (!(key in record)) return NOT_FOUND;
210
- return record[key];
211
- }
212
- function getAtPath(root, path) {
213
- if (path.length === 0) return root;
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;
224
- }
225
- }
226
- function hasAtPath(root, path) {
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) {
235
- let current = root;
236
- for (let i = 0; i < path.length - 1; i++) {
237
- const segment = path[i];
238
- const next = descendStep(current, segment);
239
- if (next === NOT_FOUND) return false;
240
- current = next;
241
- }
242
- const last = path[path.length - 1];
243
- if (current === null || current === void 0) return false;
244
- if (typeof current !== "object") return false;
245
- if (Array.isArray(current)) {
246
- return typeof last === "number" && consumerHas(current, last);
247
- }
248
- if (current instanceof Map) {
249
- return current.has(mapKeyForSegment(current, last));
250
- }
251
- const key = typeof last === "number" ? String(last) : last;
252
- if (isShadowedKey(key)) return safeOwnHas(current, key);
253
- return consumerHas(current, key);
254
- }
255
- function isPlainRecord(value) {
256
- if (value === null || typeof value !== "object") return false;
257
- if (Array.isArray(value)) return false;
258
- const proto = Object.getPrototypeOf(value);
259
- return proto === null || proto === Object.prototype;
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
- };
268
- function setAtPath(root, path, value) {
269
- return setAtPathWithSchemaFill(root, NO_SCHEMA_FILL, path, value);
270
- }
271
- const NO_IN_PLACE = { applied: false };
272
- function tryInPlaceLeafWrite(root, path, value) {
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) {
281
- let node = root;
282
- for (let i = 0; i < path.length; i++) {
283
- const seg = path[i];
284
- if (node instanceof Map) return NO_IN_PLACE;
285
- if (Array.isArray(node)) {
286
- if (typeof seg !== "number" || seg < 0 || seg >= node.length) return NO_IN_PLACE;
287
- } else if (isPlainRecord(node)) {
288
- if (typeof seg !== "string" || isShadowedKey(seg) || !(seg in node)) return NO_IN_PLACE;
289
- } else {
290
- return NO_IN_PLACE;
291
- }
292
- const container = node;
293
- if (i < path.length - 1) {
294
- node = container[seg];
295
- continue;
296
- }
297
- const old = container[seg];
298
- if (isPlainRecord(old) || Array.isArray(old)) return NO_IN_PLACE;
299
- container[seg] = value;
300
- return { applied: true, old };
301
- }
302
- return NO_IN_PLACE;
303
- }
304
- function mergeStructural(schema, path, consumer, defaultValue = schema.getDefaultAtPath(path)) {
305
- const scratch = path.slice();
306
- return mergeStructuralImpl(schema, scratch, consumer, defaultValue);
307
- }
308
- function mergeStructuralImpl(schema, scratch, consumer, defaultValue) {
309
- if (consumer === void 0) {
310
- if (schema.getSlimPrimitiveTypesAtPath(scratch).has("undefined")) {
311
- return void 0;
312
- }
313
- return defaultValue;
314
- }
315
- if (consumer === null) return null;
316
- if (Array.isArray(consumer)) {
317
- return mergeStructuralArray(schema, scratch, consumer);
318
- }
319
- if (consumer instanceof Map) {
320
- return mergeStructuralMap(schema, scratch, consumer);
321
- }
322
- if (isPlainRecord(consumer)) {
323
- if (!isPlainRecord(defaultValue)) {
324
- return consumer;
325
- }
326
- const out = spreadConsumerRecord(consumer);
327
- const filledAny = fillMissingKeysFromDefault(schema, scratch, consumer, defaultValue, out);
328
- const recursedAny = recurseIntoConsumerKeys(schema, scratch, consumer, defaultValue, out);
329
- return filledAny || recursedAny ? out : consumer;
330
- }
331
- return consumer;
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
- }
353
- function mergeStructuralArray(schema, scratch, consumer) {
354
- const shape = schema.arrayShapeAtPath(scratch);
355
- const isTuple = typeof shape === "number";
356
- const targetLen = isTuple ? shape : consumer.length;
357
- let cachedElementDefault;
358
- let cachedElementDefaultRead = false;
359
- let mutated = targetLen > consumer.length;
360
- const out = copyConsumerArray(consumer);
361
- while (out.length < targetLen) out.push(void 0);
362
- for (let i = 0; i < targetLen; i++) {
363
- scratch.push(i);
364
- let elemDefault;
365
- if (isTuple) {
366
- elemDefault = schema.getDefaultAtPath(scratch);
367
- } else {
368
- if (!cachedElementDefaultRead) {
369
- cachedElementDefault = schema.getDefaultAtPath(scratch);
370
- cachedElementDefaultRead = true;
371
- }
372
- elemDefault = cachedElementDefault;
373
- }
374
- const consumerElem = i < consumer.length ? readConsumerIndex(consumer, i) : void 0;
375
- const merged = mergeStructuralImpl(schema, scratch, consumerElem, elemDefault);
376
- scratch.pop();
377
- if (merged !== consumerElem) {
378
- out[i] = merged;
379
- mutated = true;
380
- }
381
- }
382
- return mutated ? out : consumer;
383
- }
384
- function fillMissingKeysFromDefault(schema, scratch, consumer, defaultValue, out) {
385
- let mutated = false;
386
- for (const key of Object.keys(defaultValue)) {
387
- if (!safeOwnHas(consumer, key)) {
388
- const defAtKey = safeOwnRead(defaultValue, key);
389
- scratch.push(key);
390
- const filled = mergeStructuralImpl(schema, scratch, void 0, defAtKey);
391
- scratch.pop();
392
- if (filled !== void 0) {
393
- safeAssign(out, key, filled);
394
- mutated = true;
395
- }
396
- }
397
- }
398
- return mutated;
399
- }
400
- function recurseIntoConsumerKeys(schema, scratch, consumer, defaultValue, out) {
401
- let mutated = false;
402
- for (const key of consumerKeys(consumer)) {
403
- const cVal = safeOwnRead(consumer, key);
404
- if (cVal === void 0) continue;
405
- scratch.push(key);
406
- const merged = mergeStructuralImpl(schema, scratch, cVal, safeOwnRead(defaultValue, key));
407
- scratch.pop();
408
- if (merged !== cVal) {
409
- safeAssign(out, key, merged);
410
- mutated = true;
411
- }
412
- }
413
- return mutated;
414
- }
415
- function setAtPathWithSchemaFill(root, schema, fullPath, value) {
416
- if (fullPath.length === 0) return value;
417
- return setAtPathWithSchemaFillImpl(root, schema, fullPath, value, 0);
418
- }
419
- function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
420
- if (startIdx >= fullPath.length) return value;
421
- const head = fullPath[startIdx];
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;
438
- if (typeof head === "number") {
439
- const arr = Array.isArray(root) ? [...root] : [];
440
- const prefix = fullPath.slice(0, startIdx);
441
- if (arr.length < head) {
442
- const scratch = prefix.slice();
443
- const shape = schema.arrayShapeAtPath(scratch);
444
- const tupleLike = typeof shape === "number";
445
- let cachedArrayDefault;
446
- if (!tupleLike) {
447
- scratch.push(0);
448
- cachedArrayDefault = schema.getDefaultAtPath(scratch);
449
- scratch.pop();
450
- }
451
- while (arr.length < head) {
452
- const idx = arr.length;
453
- if (tupleLike) {
454
- scratch.push(idx);
455
- arr.push(schema.getDefaultAtPath(scratch));
456
- scratch.pop();
457
- } else {
458
- arr.push(cachedArrayDefault);
459
- }
460
- }
461
- }
462
- if (isLeafStep) {
463
- arr[head] = value;
464
- return arr;
465
- }
466
- let childRoot2 = arr[head];
467
- if (childRoot2 === void 0 || childRoot2 !== null && typeof childRoot2 !== "object") {
468
- childRoot2 = schema.getDefaultAtPath([...prefix, head]);
469
- }
470
- arr[head] = setAtPathWithSchemaFillImpl(childRoot2, schema, fullPath, value, startIdx + 1);
471
- return arr;
472
- }
473
- const rec = isPlainRecord(root) ? { ...root } : {};
474
- if (isLeafStep) {
475
- safeAssign(rec, head, value);
476
- return rec;
477
- }
478
- const existing = safeOwnRead(rec, head);
479
- let childRoot;
480
- if (existing === void 0 || existing !== null && typeof existing !== "object") {
481
- const intermPath = [...fullPath.slice(0, startIdx + 1)];
482
- const intermDefault = schema.getDefaultAtPath(intermPath);
483
- childRoot = intermDefault;
484
- } else {
485
- childRoot = existing;
486
- }
487
- safeAssign(
488
- rec,
489
- head,
490
- setAtPathWithSchemaFillImpl(childRoot, schema, fullPath, value, startIdx + 1)
491
- );
492
- return rec;
493
- }
494
-
495
106
  function isUnderStubAncestor(state, segments) {
496
107
  for (let i = 0; i < segments.length; i++) {
497
108
  const ancestorPath = segments.slice(0, i);
@@ -627,7 +238,6 @@ function visitActiveLeafPaths(value, base, visit) {
627
238
  function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
628
239
  const formValue = state.form.value;
629
240
  const value = getAtPath(formValue, segments);
630
- const original = state.originals.get(key)?.value;
631
241
  let pristine = true;
632
242
  let blank = true;
633
243
  let dirty = false;
@@ -645,6 +255,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
645
255
  let asyncPending = false;
646
256
  const submissionAttempts = state.submissionAttempts.value;
647
257
  const blurredLeafSegments = [];
258
+ const errorsByKey = errorBucketsAt(state, segments, key);
648
259
  const descendantLeaves = [];
649
260
  visitActiveLeafPaths(value, segments, (leafSegments) => {
650
261
  const { key: leafKey } = keyForSegments(leafSegments);
@@ -654,6 +265,8 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
654
265
  });
655
266
  for (const { key: leafKey, segments: leafSeg } of descendantLeaves) {
656
267
  const leafRecord = state.fields.get(leafKey);
268
+ const leafErrors = errorsByKey.get(leafKey);
269
+ if (leafErrors !== void 0) leafErrors.walked = true;
657
270
  if (!state.isPristineAtPathByKey(leafKey, leafSeg)) {
658
271
  pristine = false;
659
272
  dirty = true;
@@ -688,22 +301,28 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
688
301
  if (updatedAt === null || ts > updatedAt) updatedAt = ts;
689
302
  }
690
303
  }
691
- if (!dirty && state.hasStructuralChangeUnder(segments)) {
304
+ if (!dirty && !(Array.isArray(value) || isPlainRecord(value)) && !state.isPristineAtPathByKey(key, segments)) {
692
305
  pristine = false;
693
306
  dirty = true;
694
307
  }
695
- if (!dirty && state.hasRemovedSubtreeUnder(segments)) {
308
+ if (!dirty && state.hasStructuralChangeUnder(segments, key)) {
696
309
  pristine = false;
697
310
  dirty = true;
698
311
  }
699
- const errors = aggregateErrorsAt(state, segments, key);
700
- const revealedDescendantError = errors.length > 0 && errors.some((e) => {
701
- const ePath = e.path;
702
- if (ePath.length === segments.length && ePath.every((s, i) => s === segments[i])) return false;
703
- if (submissionAttempts > 0) return true;
704
- if (ePath.length === 1 && ePath[0] === "") return blurredAfterInteraction;
705
- return blurredLeafSegments.some((s) => isPathPrefix(ePath, s));
706
- });
312
+ if (!dirty && state.hasRemovedSubtreeUnder(segments, key)) {
313
+ pristine = false;
314
+ dirty = true;
315
+ }
316
+ const errorBuckets = sortErrorBuckets(errorsByKey);
317
+ const errors = flattenErrorBuckets(errorBuckets);
318
+ const revealedDescendantError = errors.length > 0 && descendantErrorRevealed(
319
+ state,
320
+ segments,
321
+ errorBuckets,
322
+ submissionAttempts,
323
+ blurredAfterInteraction,
324
+ blurredLeafSegments
325
+ );
707
326
  if (!asyncPending && state.pathHasAsyncValidationByKey(key, segments)) asyncPending = true;
708
327
  if ((state.fieldValidationCounts.get(key) ?? 0) > 0) {
709
328
  validating = true;
@@ -725,6 +344,8 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
725
344
  if (ownRecord.interacted === true) interacted = true;
726
345
  if (ownRecord.blurredAfterInteraction === true) blurredAfterInteraction = true;
727
346
  if (ownRecord.connected === true) connected = true;
347
+ const ts = ownRecord.updatedAt;
348
+ if (ts !== null && (updatedAt === null || ts > updatedAt)) updatedAt = ts;
728
349
  }
729
350
  const ownTransformError = state.transformErrors.get(key) ?? null;
730
351
  const gated = asyncPending && !state.firstValidationDone.value;
@@ -733,7 +354,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
733
354
  return {
734
355
  base: {
735
356
  value,
736
- original,
357
+ original: state.getOriginalAtPath(segments),
737
358
  pristine,
738
359
  dirty,
739
360
  focused,
@@ -816,9 +437,41 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
816
437
  firstOwnError
817
438
  };
818
439
  }
440
+ function descendantErrorRevealed(state, segments, buckets, submissionAttempts, blurredAfterInteraction, blurredLeafSegments) {
441
+ for (const bucket of buckets) {
442
+ let ownHistory;
443
+ for (const e of bucket.errors) {
444
+ const ePath = e.path;
445
+ if (pathsEqual(ePath, segments)) continue;
446
+ if (submissionAttempts > 0) return true;
447
+ if (ePath.length === 1 && ePath[0] === "") {
448
+ if (blurredAfterInteraction) return true;
449
+ continue;
450
+ }
451
+ for (const leaf of blurredLeafSegments) if (isPathPrefix(ePath, leaf)) return true;
452
+ if (bucket.walked) continue;
453
+ ownHistory ?? (ownHistory = state.fields.get(bucket.key)?.blurredAfterInteraction === true);
454
+ if (ownHistory) return true;
455
+ }
456
+ }
457
+ return false;
458
+ }
819
459
  function aggregateErrorsAt(state, prefix, prefixKey) {
460
+ return flattenErrorBuckets(sortErrorBuckets(errorBucketsAt(state, prefix, prefixKey)));
461
+ }
462
+ const NO_ERROR_BUCKETS = /* @__PURE__ */ new Map();
463
+ function sortErrorBuckets(buckets) {
464
+ if (buckets.size <= 1) return [...buckets.values()];
465
+ return [...buckets.values()].sort((a, b) => a.ordinal - b.ordinal);
466
+ }
467
+ function flattenErrorBuckets(buckets) {
468
+ if (buckets.length === 0) return [];
469
+ if (buckets.length === 1) return buckets[0]?.errors ?? [];
470
+ return buckets.flatMap((bucket) => bucket.errors);
471
+ }
472
+ function errorBucketsAt(state, prefix, prefixKey) {
820
473
  const candidates = state.errorWindowAt(prefix, prefixKey);
821
- if (candidates.length === 0) return [];
474
+ if (candidates.length === 0) return NO_ERROR_BUCKETS;
822
475
  const formValue = state.form.value;
823
476
  const cells = state.errorCells();
824
477
  const buckets = /* @__PURE__ */ new Map();
@@ -831,19 +484,16 @@ function aggregateErrorsAt(state, prefix, prefixKey) {
831
484
  const userList = cell?.user;
832
485
  const total = (schemaList?.length ?? 0) + (blankList?.length ?? 0) + (userList?.length ?? 0);
833
486
  if (total === 0) continue;
834
- const ordinal = state.ensurePathOrdinal(key);
835
- let bucket = buckets.get(ordinal);
487
+ let bucket = buckets.get(key);
836
488
  if (bucket === void 0) {
837
- bucket = [];
838
- buckets.set(ordinal, bucket);
489
+ bucket = { key, ordinal: state.ensurePathOrdinal(key), errors: [], walked: false };
490
+ buckets.set(key, bucket);
839
491
  }
840
- if (schemaList !== void 0) bucket.push(...schemaList);
841
- if (blankList !== void 0) bucket.push(...blankList);
842
- if (userList !== void 0) bucket.push(...userList);
492
+ if (schemaList !== void 0) bucket.errors.push(...schemaList);
493
+ if (blankList !== void 0) bucket.errors.push(...blankList);
494
+ if (userList !== void 0) bucket.errors.push(...userList);
843
495
  }
844
- if (buckets.size === 0) return [];
845
- if (buckets.size === 1) return [...buckets.values()][0];
846
- return [...buckets.entries()].sort(([a], [b]) => a - b).flatMap(([, errs]) => errs);
496
+ return buckets;
847
497
  }
848
498
  const EMPTY_ELEMENTS = Object.freeze([]);
849
499
 
@@ -875,7 +525,7 @@ function liveContainerHasKey(state, segments, key) {
875
525
  return Number.isInteger(index) && index >= 0 && String(index) === key && value.has(index);
876
526
  }
877
527
  if (value instanceof Set) return false;
878
- return Object.hasOwn(value, key);
528
+ return safeOwnHas(value, key);
879
529
  }
880
530
  function isArrayPath(state, segments) {
881
531
  if (segments.length === 0) return false;
@@ -1410,406 +1060,6 @@ function buildValuesSurface(form, onFormChange) {
1410
1060
  });
1411
1061
  }
1412
1062
 
1413
- function remapForOp(op, oldLen) {
1414
- const moved = /* @__PURE__ */ new Map();
1415
- const vacated = /* @__PURE__ */ new Set();
1416
- const fresh = /* @__PURE__ */ new Set();
1417
- let newLen = oldLen;
1418
- switch (op.kind) {
1419
- case "insert":
1420
- for (let i = op.index; i < oldLen; i++) moved.set(i, i + 1);
1421
- fresh.add(op.index);
1422
- newLen = oldLen + 1;
1423
- break;
1424
- case "remove":
1425
- vacated.add(op.index);
1426
- for (let i = op.index + 1; i < oldLen; i++) moved.set(i, i - 1);
1427
- newLen = Math.max(0, oldLen - 1);
1428
- break;
1429
- case "move":
1430
- if (op.from !== op.to) {
1431
- moved.set(op.from, op.to);
1432
- if (op.from < op.to) {
1433
- for (let i = op.from + 1; i <= op.to; i++) moved.set(i, i - 1);
1434
- } else {
1435
- for (let i = op.to; i < op.from; i++) moved.set(i, i + 1);
1436
- }
1437
- }
1438
- break;
1439
- case "swap":
1440
- if (op.a !== op.b) {
1441
- moved.set(op.a, op.b);
1442
- moved.set(op.b, op.a);
1443
- }
1444
- break;
1445
- case "replace-at":
1446
- vacated.add(op.index);
1447
- fresh.add(op.index);
1448
- break;
1449
- }
1450
- return { moved, vacated, fresh, oldLen, newLen };
1451
- }
1452
- function changedIndices(remap) {
1453
- const changed = new Set(remap.vacated);
1454
- for (const [from, to] of remap.moved) {
1455
- changed.add(from);
1456
- changed.add(to);
1457
- }
1458
- for (const index of remap.fresh) changed.add(index);
1459
- return changed;
1460
- }
1461
- function permuteList(list, remap, fill) {
1462
- const claimed = new Array(remap.newLen);
1463
- for (let i = 0; i < remap.oldLen; i++) {
1464
- if (remap.vacated.has(i)) continue;
1465
- const to = remap.moved.get(i) ?? i;
1466
- if (to < remap.newLen) claimed[to] = list[i];
1467
- }
1468
- const next = [];
1469
- for (let i = 0; i < remap.newLen; i++) {
1470
- next.push(claimed[i] ?? fill());
1471
- }
1472
- return next;
1473
- }
1474
- function elementIndexUnder(arrayPath, key, idxPos) {
1475
- const segments = segmentsForPathKey(key);
1476
- if (segments === null) return null;
1477
- if (!isPathPrefix(arrayPath, segments)) return null;
1478
- if (segments.length <= idxPos) return null;
1479
- const index = segments[idxPos];
1480
- return typeof index === "number" ? index : null;
1481
- }
1482
- function collectKeysAtIndices(keys, arrayPath, member) {
1483
- const idxPos = arrayPath.length;
1484
- const hits = [];
1485
- for (const key of keys) {
1486
- const index = elementIndexUnder(arrayPath, key, idxPos);
1487
- if (index === null || !member(index)) continue;
1488
- const segments = segmentsForPathKey(key);
1489
- if (segments === null) continue;
1490
- hits.push({ key, segments: [...segments], index });
1491
- }
1492
- return hits;
1493
- }
1494
- function remapAffects(remap) {
1495
- return (index) => remap.moved.has(index) || remap.vacated.has(index) || remap.fresh.has(index);
1496
- }
1497
- function migrateMapSubtree(map, arrayPath, remap, rewriteValue) {
1498
- const hits = collectKeysAtIndices(map.keys(), arrayPath, remapAffects(remap));
1499
- if (hits.length === 0) return;
1500
- const idxPos = arrayPath.length;
1501
- const snapshots = [];
1502
- for (const hit of hits) {
1503
- const value = map.get(hit.key);
1504
- if (value !== void 0) snapshots.push({ ...hit, value });
1505
- }
1506
- for (const snap of snapshots) map.delete(snap.key);
1507
- for (const snap of snapshots) {
1508
- const target = remap.moved.get(snap.index);
1509
- if (target === void 0) continue;
1510
- const relocated = snap.segments.slice();
1511
- relocated[idxPos] = target;
1512
- map.set(canonicalizePath(relocated).key, rewriteValue(snap.value, relocated));
1513
- }
1514
- }
1515
- function migrateSetSubtree(set, arrayPath, remap) {
1516
- const hits = collectKeysAtIndices(set, arrayPath, remapAffects(remap));
1517
- if (hits.length === 0) return;
1518
- const idxPos = arrayPath.length;
1519
- for (const hit of hits) set.delete(hit.key);
1520
- for (const hit of hits) {
1521
- const target = remap.moved.get(hit.index);
1522
- if (target === void 0) continue;
1523
- const relocated = hit.segments.slice();
1524
- relocated[idxPos] = target;
1525
- set.add(canonicalizePath(relocated).key);
1526
- }
1527
- }
1528
- function evictSchemaVerdictsAtIndices(errorCells, arrayPath, indices) {
1529
- for (const hit of collectKeysAtIndices(
1530
- [...errorCells.keys()],
1531
- arrayPath,
1532
- (index) => indices.has(index)
1533
- )) {
1534
- const cell = errorCells.get(hit.key);
1535
- if (cell === void 0) continue;
1536
- if (cell.user.length === 0) errorCells.delete(hit.key);
1537
- else if (cell.schema.length > 0) errorCells.set(hit.key, { schema: NO_ERRORS, user: cell.user });
1538
- }
1539
- }
1540
- function deleteKeysAtIndices(keys, del, arrayPath, indices) {
1541
- for (const hit of collectKeysAtIndices(keys, arrayPath, (index) => indices.has(index))) {
1542
- del(hit.key);
1543
- }
1544
- }
1545
- function createArrayIdentity(getArrayLength) {
1546
- const tokens = /* @__PURE__ */ new Map();
1547
- const baselines = /* @__PURE__ */ new Map();
1548
- let counter = 0;
1549
- const allocate = () => `k${(counter++).toString(36)}`;
1550
- function ensure(arrayKey, expectedLen) {
1551
- let ids = tokens.get(arrayKey);
1552
- const firstTrack = ids === void 0;
1553
- if (ids === void 0) {
1554
- ids = [];
1555
- tokens.set(arrayKey, ids);
1556
- }
1557
- while (ids.length < expectedLen) ids.push(allocate());
1558
- if (ids.length > expectedLen) ids.length = expectedLen;
1559
- if (firstTrack) baselines.set(arrayKey, [...ids]);
1560
- return ids;
1561
- }
1562
- function orderPristineForKey(arrayKey) {
1563
- const baseline = baselines.get(arrayKey);
1564
- const current = tokens.get(arrayKey);
1565
- if (baseline === void 0 || current === void 0) return true;
1566
- if (baseline.length !== current.length) return false;
1567
- for (let i = 0; i < current.length; i++) {
1568
- if (current[i] !== baseline[i]) return false;
1569
- }
1570
- return true;
1571
- }
1572
- return {
1573
- tokenAt(arraySegs, index) {
1574
- const len = getArrayLength(arraySegs);
1575
- if (index < 0 || index >= len) return "";
1576
- const ids = ensure(canonicalizePath(arraySegs).key, len);
1577
- return ids[index] ?? "";
1578
- },
1579
- applyOp(arraySegs, remap) {
1580
- const arrayKey = canonicalizePath(arraySegs).key;
1581
- const ids = ensure(arrayKey, remap.oldLen);
1582
- tokens.set(arrayKey, permuteList(ids, remap, allocate));
1583
- },
1584
- applyRemap(arrayPath, remap) {
1585
- if (remap.moved.size === 0 && remap.vacated.size === 0 && remap.fresh.size === 0) return;
1586
- migrateMapSubtree(tokens, arrayPath, remap, (value) => value);
1587
- migrateMapSubtree(baselines, arrayPath, remap, (value) => value);
1588
- },
1589
- realign(arraySegs) {
1590
- ensure(canonicalizePath(arraySegs).key, getArrayLength(arraySegs));
1591
- },
1592
- hasStructuralChangeUnder(prefix) {
1593
- for (const arrayKey of tokens.keys()) {
1594
- if (orderPristineForKey(arrayKey)) continue;
1595
- const segs = segmentsForPathKey(arrayKey);
1596
- if (segs === null) continue;
1597
- if (isPathPrefix(prefix, segs)) return true;
1598
- }
1599
- return false;
1600
- },
1601
- rebaselineAll() {
1602
- for (const arrayKey of [...tokens.keys()]) {
1603
- const segs = segmentsForPathKey(arrayKey);
1604
- if (segs === null) continue;
1605
- const ids = ensure(arrayKey, getArrayLength(segs));
1606
- baselines.set(arrayKey, [...ids]);
1607
- }
1608
- }
1609
- };
1610
- }
1611
- function createVariantMemory() {
1612
- const memory = /* @__PURE__ */ new Map();
1613
- return {
1614
- clear() {
1615
- memory.clear();
1616
- },
1617
- clearUnderPath(parentPath) {
1618
- for (const memKey of [...memory.keys()]) {
1619
- const segs = segmentsForPathKey(memKey);
1620
- if (segs === null) continue;
1621
- if (isPathPrefix(parentPath, segs)) memory.delete(memKey);
1622
- }
1623
- },
1624
- dropAtIndices(arrayPath, indices) {
1625
- deleteKeysAtIndices([...memory.keys()], (key) => memory.delete(key), arrayPath, indices);
1626
- },
1627
- recordOutgoing(unionKey, discValue, snapshot) {
1628
- let perUnion = memory.get(unionKey);
1629
- if (perUnion === void 0) {
1630
- perUnion = /* @__PURE__ */ new Map();
1631
- memory.set(unionKey, perUnion);
1632
- }
1633
- perUnion.set(discValue, snapshot);
1634
- },
1635
- lookupIncoming(unionKey, discValue) {
1636
- return memory.get(unionKey)?.get(discValue);
1637
- }
1638
- };
1639
- }
1640
- function cloneVariantSnapshot(value) {
1641
- if (value === null || typeof value !== "object") return value;
1642
- const raw = toRaw(value);
1643
- if (raw instanceof Date) return new Date(raw.getTime());
1644
- if (raw instanceof Map) {
1645
- const out2 = /* @__PURE__ */ new Map();
1646
- for (const [k, v] of raw.entries()) out2.set(cloneVariantSnapshot(k), cloneVariantSnapshot(v));
1647
- return out2;
1648
- }
1649
- if (raw instanceof Set) {
1650
- const out2 = /* @__PURE__ */ new Set();
1651
- for (const v of raw) out2.add(cloneVariantSnapshot(v));
1652
- return out2;
1653
- }
1654
- if (raw instanceof RegExp) return new RegExp(raw.source, raw.flags);
1655
- if (Array.isArray(raw)) {
1656
- const out2 = new Array(raw.length);
1657
- for (let i = 0; i < raw.length; i++) out2[i] = cloneVariantSnapshot(raw[i]);
1658
- return out2;
1659
- }
1660
- if (!isPlainRecord(raw)) return raw;
1661
- const src = raw;
1662
- const out = {};
1663
- for (const k of consumerKeys(src)) {
1664
- safeAssign(out, k, cloneVariantSnapshot(readConsumerProp(src, k)));
1665
- }
1666
- return out;
1667
- }
1668
- function createArrayBookkeeping(deps) {
1669
- const {
1670
- form,
1671
- fields,
1672
- errorCells,
1673
- originals,
1674
- blankPaths,
1675
- originalBlankPaths,
1676
- authoredPaths,
1677
- fieldValidationCounts,
1678
- fieldValidatingSince,
1679
- fieldValidationState,
1680
- activeValidations,
1681
- arrayIdentity,
1682
- variantMemory,
1683
- touchFieldRecord,
1684
- decFieldValidation
1685
- } = deps;
1686
- function migrateElementState(arrayPath, remap) {
1687
- if (remap.moved.size === 0 && remap.vacated.size === 0) return;
1688
- migrateMapSubtree(fields, arrayPath, remap, (record, segments) => ({
1689
- ...record,
1690
- path: segments
1691
- }));
1692
- migrateMapSubtree(errorCells(), arrayPath, remap, (cell, segments) => ({
1693
- schema: NO_ERRORS,
1694
- user: cell.user.map((error) => ({ ...error, path: [...segments] }))
1695
- }));
1696
- migrateMapSubtree(originals, arrayPath, remap, (record, segments) => ({
1697
- segments,
1698
- value: record.value
1699
- }));
1700
- migrateSetSubtree(blankPaths, arrayPath, remap);
1701
- migrateSetSubtree(originalBlankPaths, arrayPath, remap);
1702
- migrateSetSubtree(authoredPaths, arrayPath, remap);
1703
- migrateMapSubtree(fieldValidatingSince, arrayPath, remap, (since) => since);
1704
- migrateMapSubtree(fieldValidationCounts, arrayPath, remap, (count) => count);
1705
- arrayIdentity.applyRemap(arrayPath, remap);
1706
- }
1707
- function seedFreshElement(arrayPath, freshIndex) {
1708
- const elementPath = [...arrayPath, freshIndex];
1709
- const now = (/* @__PURE__ */ new Date()).toISOString();
1710
- diffAndApply(void 0, getAtPath(form.value, elementPath), elementPath, (patch) => {
1711
- if (patch.kind !== "added") return;
1712
- const { key } = canonicalizePath(patch.path);
1713
- if (!originals.has(key)) originals.set(key, { segments: patch.path, value: void 0 });
1714
- touchFieldRecord(key, patch.path, { updatedAt: now });
1715
- });
1716
- }
1717
- function abortValidationAtVacatedIndices(arrayPath, remap) {
1718
- if (remap.vacated.size === 0) return;
1719
- const hits = collectKeysAtIndices(
1720
- [...fieldValidationState.keys()],
1721
- arrayPath,
1722
- (index) => remap.vacated.has(index)
1723
- );
1724
- for (const hit of hits) {
1725
- const entry = fieldValidationState.get(hit.key);
1726
- if (entry === void 0) continue;
1727
- if (entry.timer !== null) {
1728
- clearTimeout(entry.timer);
1729
- } else if (!entry.settled) {
1730
- activeValidations.value = Math.max(0, activeValidations.value - 1);
1731
- decFieldValidation(hit.key);
1732
- }
1733
- entry.aborted = true;
1734
- fieldValidationState.delete(hit.key);
1735
- }
1736
- }
1737
- return {
1738
- applyStructuralOp(arrayPath, remap) {
1739
- migrateElementState(arrayPath, remap);
1740
- for (const freshIndex of remap.fresh) seedFreshElement(arrayPath, freshIndex);
1741
- const changed = changedIndices(remap);
1742
- evictSchemaVerdictsAtIndices(errorCells(), arrayPath, changed);
1743
- abortValidationAtVacatedIndices(arrayPath, remap);
1744
- variantMemory.dropAtIndices(arrayPath, changed);
1745
- arrayIdentity.applyOp(arrayPath, remap);
1746
- }
1747
- };
1748
- }
1749
- function buildFieldArrayApi(state) {
1750
- function readArray(path) {
1751
- const segments = canonicalizePath(path).segments;
1752
- const current = state.getValueAtPath(segments);
1753
- return Array.isArray(current) ? current.slice() : [];
1754
- }
1755
- function writeArray(path, next, arrayOp) {
1756
- const { segments } = canonicalizePath(path);
1757
- const meta = {
1758
- ...arrayOp !== void 0 ? { arrayOp } : {}
1759
- };
1760
- return state.setValueAtPath(segments, next, meta);
1761
- }
1762
- return {
1763
- append(path, value) {
1764
- const next = readArray(path);
1765
- next.push(value);
1766
- return writeArray(path, next, { kind: "insert", index: next.length - 1 });
1767
- },
1768
- prepend(path, value) {
1769
- const next = readArray(path);
1770
- next.unshift(value);
1771
- return writeArray(path, next, { kind: "insert", index: 0 });
1772
- },
1773
- insert(path, index, value) {
1774
- const next = readArray(path);
1775
- const preLen = next.length;
1776
- const insertIndex = index < 0 ? Math.max(0, preLen + index) : Math.min(index, preLen);
1777
- next.splice(insertIndex, 0, value);
1778
- return writeArray(path, next, { kind: "insert", index: insertIndex });
1779
- },
1780
- remove(path, index) {
1781
- const next = readArray(path);
1782
- if (index < 0 || index >= next.length) return false;
1783
- next.splice(index, 1);
1784
- return writeArray(path, next, { kind: "remove", index });
1785
- },
1786
- swap(path, a, b) {
1787
- const next = readArray(path);
1788
- if (a < 0 || a >= next.length) return false;
1789
- if (b < 0 || b >= next.length) return false;
1790
- if (a === b) return false;
1791
- const tmp = next[a];
1792
- next[a] = next[b];
1793
- next[b] = tmp;
1794
- return writeArray(path, next, { kind: "swap", a, b });
1795
- },
1796
- move(path, from, to) {
1797
- const next = readArray(path);
1798
- if (from < 0 || from >= next.length) return false;
1799
- const [item] = next.splice(from, 1);
1800
- const clampedTo = Math.max(0, Math.min(to, next.length));
1801
- next.splice(clampedTo, 0, item);
1802
- return writeArray(path, next, { kind: "move", from, to: clampedTo });
1803
- },
1804
- replace(path, index, value) {
1805
- const next = readArray(path);
1806
- if (index < 0 || index >= next.length) return false;
1807
- next[index] = value;
1808
- return writeArray(path, next, { kind: "replace-at", index });
1809
- }
1810
- };
1811
- }
1812
-
1813
1063
  const warnedNoScopeStores = /* @__PURE__ */ new WeakSet() ;
1814
1064
  function isErrorInputLike(value) {
1815
1065
  if (value instanceof Error) return true;
@@ -2552,12 +1802,12 @@ function isPrimitiveOrEmpty(value) {
2552
1802
  return t === "string" || t === "number" || t === "boolean" || t === "bigint";
2553
1803
  }
2554
1804
  function isSlimNumericPrimitive(value) {
2555
- return value === 0 || value === 0n;
1805
+ return value === 0 || typeof value === "bigint" && !value;
2556
1806
  }
2557
1807
  function blankForKind(slimDefault) {
2558
1808
  if (typeof slimDefault === "string") return "";
2559
1809
  if (typeof slimDefault === "number") return 0;
2560
- if (typeof slimDefault === "bigint") return 0n;
1810
+ if (typeof slimDefault === "bigint") return typeof BigInt === "function" ? BigInt(0) : void 0;
2561
1811
  if (typeof slimDefault === "boolean") return false;
2562
1812
  if (slimDefault === null) return null;
2563
1813
  return void 0;
@@ -2926,7 +2176,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2926
2176
  };
2927
2177
  const metaTarget = buildMetaProxy(META_KEYS, (key) => {
2928
2178
  if (key === "errorCount") return metaErrors.value.length;
2929
- if (Object.hasOwn(metaOwn, key)) return metaOwn[key];
2179
+ if (safeOwnHas(metaOwn, key)) return metaOwn[key];
2930
2180
  return rootFieldState.value[key];
2931
2181
  });
2932
2182
  const formMeta = readonly(reactive(metaTarget));
@@ -2941,8 +2191,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
2941
2191
  );
2942
2192
  state.reset(walked.cleanedValues);
2943
2193
  for (const pathKey of walked.paths) {
2944
- state.blankPaths.add(pathKey);
2945
2194
  state.originalBlankPaths.add(pathKey);
2195
+ state.blankPaths.add(pathKey);
2946
2196
  }
2947
2197
  }
2948
2198
  };
@@ -3383,7 +2633,7 @@ function mergeObjectKeys(target, source, path, schema) {
3383
2633
 
3384
2634
  async function runFactoryAndApply(st, factory) {
3385
2635
  try {
3386
- const value = await factory();
2636
+ const value = structuralSnapshot(await factory());
3387
2637
  if (st.hasPreprocessOrCoerce) walkAuthoredFromConstraints(value, [], st.authoredPaths);
3388
2638
  const full = mergeSparseHydration(
3389
2639
  toRaw(st.form.value),
@@ -3427,16 +2677,15 @@ function appendHydrationFailedEntry(st, error) {
3427
2677
  return entry;
3428
2678
  }
3429
2679
 
3430
- const isContainer = (value) => Array.isArray(value) || isPlainRecord(value);
3431
2680
  function isHydratedFieldRecord(value) {
3432
2681
  if (typeof value !== "object" || value === null) return false;
3433
2682
  const r = value;
3434
2683
  return Array.isArray(r.path) && (typeof r.updatedAt === "string" || r.updatedAt === null) && typeof r.connected === "boolean" && (typeof r.focused === "boolean" || r.focused === null) && (typeof r.blurred === "boolean" || r.blurred === null) && typeof r.touched === "boolean" && typeof r.interacted === "boolean" && typeof r.blurredAfterInteraction === "boolean";
3435
2684
  }
3436
- function withClearedHistoryFlags(record, now) {
2685
+ function withClearedHistoryFlags(record, updatedAt) {
3437
2686
  return {
3438
2687
  path: record.path,
3439
- updatedAt: now,
2688
+ updatedAt,
3440
2689
  connected: record.connected,
3441
2690
  focused: record.focused,
3442
2691
  blurred: record.blurred,
@@ -3485,7 +2734,7 @@ function walkDuStubs(schema, value, path, warned) {
3485
2734
  if (du !== void 0) {
3486
2735
  const discValue = rec[du.discriminatorKey];
3487
2736
  if (discValue !== void 0 && !du.isVariantSelected(discValue)) {
3488
- const isKindBlank = discValue === "" || discValue === 0 || discValue === 0n || discValue === false || discValue === null;
2737
+ const isKindBlank = discValue === "" || discValue === 0 || typeof discValue === "bigint" && !discValue || discValue === false || discValue === null;
3489
2738
  if (!isKindBlank && warned !== void 0 && true) {
3490
2739
  const dotted = path.map((s) => String(s)).join(".") || "(root)";
3491
2740
  const key = `${dotted}::${String(discValue)}`;
@@ -3510,7 +2759,7 @@ function walkDuStubs(schema, value, path, warned) {
3510
2759
  function stripSymbolsDeep(value) {
3511
2760
  if (value === null || typeof value !== "object") return value;
3512
2761
  if (Array.isArray(value)) {
3513
- let mutated2 = false;
2762
+ let mutated2 = !consumerExtensible(value);
3514
2763
  const out2 = new Array(value.length);
3515
2764
  for (let i = 0; i < value.length; i++) {
3516
2765
  const original = readConsumerIndex(value, i);
@@ -3524,7 +2773,7 @@ function stripSymbolsDeep(value) {
3524
2773
  if (proto !== Object.prototype && proto !== null) return value;
3525
2774
  const symKeys = consumerSymbolKeys(value);
3526
2775
  const stringKeys = consumerKeys(value);
3527
- let mutated = symKeys.length > 0;
2776
+ let mutated = symKeys.length > 0 || !consumerExtensible(value);
3528
2777
  const out = {};
3529
2778
  const src = value;
3530
2779
  for (const k of stringKeys) {
@@ -3567,14 +2816,27 @@ function initialFirstValidationGate(schema) {
3567
2816
  return schema.needsAsyncValidation?.() !== true;
3568
2817
  }
3569
2818
  function seedOriginalsFromBaseline(st, baseline) {
3570
- st.originals.clear();
2819
+ const seeded = /* @__PURE__ */ new Map();
3571
2820
  diffAndApply({}, baseline, [], (patch) => {
3572
- if (patch.kind !== "added") return;
3573
- st.originals.set(canonicalizePath(patch.path).key, {
3574
- segments: patch.path,
3575
- value: patch.newValue
3576
- });
2821
+ if (patch.kind === "added") seedBaseline(seeded, canonicalizePath(patch.path).key, patch);
3577
2822
  });
2823
+ replaceOriginals(st, seeded);
2824
+ }
2825
+ function replaceOriginals(st, next) {
2826
+ for (const [key, entry] of next) st.originals.set(key, entry);
2827
+ for (const key of [...toRaw(st.originals).keys()]) {
2828
+ if (!next.has(key)) st.originals.delete(key);
2829
+ }
2830
+ }
2831
+ function seedBaseline(originals, key, patch) {
2832
+ const value = patch.newValue;
2833
+ if (!isDescendable(value)) {
2834
+ originals.set(key, { segments: patch.path, value });
2835
+ return false;
2836
+ }
2837
+ if (!(value instanceof Map))
2838
+ originals.set(key, { segments: patch.path, value: Array.isArray(value) ? [] : {} });
2839
+ return true;
3578
2840
  }
3579
2841
  function queueInitialAsyncValidation(st) {
3580
2842
  if (!st.ssr && st.schema.needsAsyncValidation?.() === true) {
@@ -3736,8 +2998,18 @@ function settleTransforms(st, path) {
3736
2998
  st.transformWaiters.push({ key, resolve });
3737
2999
  });
3738
3000
  }
3001
+ function writeInstant(st) {
3002
+ const clock = (/* @__PURE__ */ new Date()).toISOString();
3003
+ if (clock > st.latestStamp) return clock;
3004
+ const after = new Date(new Date(st.latestStamp).getTime() + 1);
3005
+ return Number.isFinite(after.getTime()) ? after.toISOString() : clock;
3006
+ }
3007
+ function noteStamp(st, stamp) {
3008
+ if (stamp !== null && stamp > st.latestStamp) st.latestStamp = stamp;
3009
+ }
3739
3010
  function touchFieldRecord(st, pathKey, path, patch) {
3740
3011
  const current = st.fields.get(pathKey);
3012
+ if (patch.updatedAt !== void 0) noteStamp(st, patch.updatedAt);
3741
3013
  st.fields.set(pathKey, {
3742
3014
  path,
3743
3015
  updatedAt: patch.updatedAt ?? current?.updatedAt ?? null,
@@ -3756,19 +3028,189 @@ function touchFieldRecord(st, pathKey, path, patch) {
3756
3028
  blurredAfterInteraction: patch.blurredAfterInteraction ?? current?.blurredAfterInteraction ?? false
3757
3029
  });
3758
3030
  }
3759
- function commitWritePatches(st, patches, meta) {
3760
- const now = (/* @__PURE__ */ new Date()).toISOString();
3031
+ function stampContainers(st, containers, now) {
3032
+ eachAncestorOnce(containers, (key, segments) => raiseStamp(st, key, segments, now));
3033
+ }
3034
+ function raiseStamp(st, key, segments, stamp) {
3035
+ if ((st.fields.get(key)?.updatedAt ?? "") < stamp) {
3036
+ touchFieldRecord(st, key, segments, { updatedAt: stamp });
3037
+ }
3038
+ }
3039
+ function holdRemovedStamps(st, patches, next) {
3040
+ const held = /* @__PURE__ */ new Map();
3041
+ const hold = (path, depth, leaf) => noteHold(st, held, next, path, depth, leaf);
3761
3042
  for (const patch of patches) {
3762
- const { key } = canonicalizePath(patch.path);
3763
- if (!st.originals.has(key)) {
3043
+ if (patch.kind === "added") continue;
3044
+ const { oldValue, path } = patch;
3045
+ if (Array.isArray(oldValue) || isPlainRecord(oldValue)) {
3046
+ if (patch.kind === "changed") {
3047
+ visitActiveLeafPaths(oldValue, path, (leaf) => hold(path, path.length, leaf));
3048
+ }
3049
+ } else if (patch.kind === "removed" ? !hasAtPath(next, path) : Array.isArray(patch.newValue) || isPlainRecord(patch.newValue)) {
3050
+ hold(path, path.length - 1, path);
3051
+ }
3052
+ }
3053
+ applyHolds(st, held);
3054
+ }
3055
+ function noteHold(st, held, next, path, depth, leaf) {
3056
+ const { key: leafKey } = keyForSegments(leaf);
3057
+ const stamp = toRaw(st.originals).has(leafKey) ? toRaw(st.fields).get(leafKey)?.updatedAt : null;
3058
+ if (stamp === null || stamp === void 0) return;
3059
+ let kept = false;
3060
+ for (let at = depth; at >= 0; at--) {
3061
+ const segments = path.slice(0, at);
3062
+ if (!kept && !hasAtPath(next, segments)) continue;
3063
+ kept = true;
3064
+ const { key } = keyForSegments(segments);
3065
+ const current = held.get(key);
3066
+ if (current === void 0) held.set(key, { segments, stamp });
3067
+ else if (stamp > current.stamp) current.stamp = stamp;
3068
+ }
3069
+ }
3070
+ function applyHolds(st, held) {
3071
+ for (const [key, { segments, stamp }] of held) raiseStamp(st, key, segments, stamp);
3072
+ }
3073
+ function stampBareContainers(st, patches, bare, now) {
3074
+ for (const index of bare) {
3075
+ const path = patches[index]?.path ?? [];
3076
+ for (let depth = path.length; depth >= 0; depth--) {
3077
+ const container = path.slice(0, depth);
3078
+ if (depth < path.length && reachedBesides(patches, index, container)) break;
3079
+ const { key, segments } = keyForSegments(container);
3080
+ raiseStamp(st, key, segments, now);
3081
+ }
3082
+ }
3083
+ }
3084
+ function reachedBesides(patches, index, container) {
3085
+ for (const step of [-1, 1]) {
3086
+ for (let i = index + step; ; i += step) {
3087
+ const patch = patches[i];
3088
+ if (patch === void 0 || !isPathPrefix(container, patch.path)) break;
3089
+ if (!isBarePatch(patch)) return true;
3090
+ }
3091
+ }
3092
+ return false;
3093
+ }
3094
+ function eachAncestorOnce(paths, visit) {
3095
+ const seen = /* @__PURE__ */ new Set();
3096
+ for (const path of paths) {
3097
+ for (let depth = path.length; depth >= 0; depth--) {
3098
+ const { key, segments } = keyForSegments(path.slice(0, depth));
3099
+ if (seen.has(key)) break;
3100
+ seen.add(key);
3101
+ visit(key, segments);
3102
+ }
3103
+ }
3104
+ }
3105
+ function foldInteractionHistory(st, path, released) {
3106
+ let touched = false;
3107
+ let interacted = false;
3108
+ let blurredAfterInteraction = false;
3109
+ for (const record of released) {
3110
+ touched || (touched = record.touched);
3111
+ interacted || (interacted = record.interacted);
3112
+ blurredAfterInteraction || (blurredAfterInteraction = record.blurredAfterInteraction);
3113
+ }
3114
+ if (!touched && !interacted && !blurredAfterInteraction) return;
3115
+ st.noteReleased?.(released);
3116
+ for (let depth = path.length; depth >= 0; depth--) {
3117
+ const { key, segments } = keyForSegments(path.slice(0, depth));
3118
+ const current = st.fields.get(key);
3119
+ touchFieldRecord(st, key, segments, {
3120
+ touched: touched || current?.touched === true,
3121
+ interacted: interacted || current?.interacted === true,
3122
+ blurredAfterInteraction: blurredAfterInteraction || current?.blurredAfterInteraction === true
3123
+ });
3124
+ }
3125
+ }
3126
+ function releaseRemovedRecords(st, segments, raw) {
3127
+ const released = [];
3128
+ let depth = segments.length;
3129
+ do {
3130
+ const { key } = keyForSegments(segments.slice(0, depth));
3131
+ abortValidationAt(st, key);
3132
+ const record = st.fields.get(key);
3133
+ if (record !== void 0) {
3134
+ released.push(record);
3135
+ if (record.connected) st.fields.set(key, withClearedHistoryFlags(record, null));
3136
+ else st.fields.delete(key);
3137
+ }
3138
+ depth--;
3139
+ } while (depth > 0 && !hasAtPath(raw, segments.slice(0, depth)));
3140
+ const present = segments.slice(0, depth);
3141
+ foldInteractionHistory(st, present, released);
3142
+ return present;
3143
+ }
3144
+ function noteRemovedBaseline(st, key, segments, notify) {
3145
+ if (st.originals.get(key)?.value === void 0) return;
3146
+ st.removedPaths.set(key, segments);
3147
+ notify.push(segments);
3148
+ }
3149
+ function trackForSweep(st, path, key) {
3150
+ st.pathSweep.track(path, key);
3151
+ for (let i = 1; i < path.length; i++) {
3152
+ const ancestor = path.slice(0, i);
3153
+ st.pathSweep.track(ancestor, canonicalizePath(ancestor).key);
3154
+ }
3155
+ }
3156
+ function commitWritePatches(st, patches, meta, structuralOp, now) {
3157
+ const arrayOpPath = structuralOp?.path;
3158
+ const reshaped = [];
3159
+ const removed = [];
3160
+ const resizing = [];
3161
+ const raw = toRaw(st.form.value);
3162
+ const bareAt = [];
3163
+ for (const [index, patch] of patches.entries()) {
3164
+ const { key, segments } = canonicalizePath(patch.path);
3165
+ const bare = isBarePatch(patch);
3166
+ if (!bare && !st.originals.has(key)) {
3764
3167
  st.originals.set(key, { segments: patch.path, value: void 0 });
3765
3168
  }
3766
- touchFieldRecord(st, key, patch.path, { updatedAt: now });
3767
- st.pathSweep.track(patch.path, key);
3768
- for (let i = 1; i < patch.path.length; i++) {
3769
- const ancestor = patch.path.slice(0, i);
3770
- st.pathSweep.track(ancestor, canonicalizePath(ancestor).key);
3169
+ const positional = arrayOpPath !== void 0 && segments.length > arrayOpPath.length && isPathPrefix(arrayOpPath, segments);
3170
+ if (structuralOp === null && (patch.kind !== "changed" || typeof patch.oldValue === "object" || typeof patch.newValue === "object")) {
3171
+ resizing.push(segments);
3771
3172
  }
3173
+ if (!positional) {
3174
+ if (patch.kind === "removed" && !hasAtPath(raw, segments)) {
3175
+ reshaped.push(releaseRemovedRecords(st, segments, raw));
3176
+ noteRemovedBaseline(st, key, segments, removed);
3177
+ } else {
3178
+ touchFieldRecord(st, key, segments, { updatedAt: now });
3179
+ if (bare) bareAt.push(index);
3180
+ if (patch.kind === "changed" && typeof patch.oldValue === "object" && patch.oldValue !== null) {
3181
+ diffAndApply(patch.oldValue, void 0, segments, (gone) => {
3182
+ if (!hasAtPath(raw, gone.path)) {
3183
+ reshaped.push(releaseRemovedRecords(st, gone.path, raw));
3184
+ const goneAt = canonicalizePath(gone.path);
3185
+ noteRemovedBaseline(st, goneAt.key, goneAt.segments, removed);
3186
+ }
3187
+ });
3188
+ }
3189
+ if (patch.kind === "changed" && isDescendable(patch.newValue)) {
3190
+ diffAndApply(void 0, patch.newValue, segments, (fresh) => {
3191
+ const at = canonicalizePath(fresh.path);
3192
+ trackForSweep(st, fresh.path, at.key);
3193
+ if (fresh.kind === "added" && isDescendable(fresh.newValue)) {
3194
+ reshaped.push(at.segments);
3195
+ return;
3196
+ }
3197
+ if (!st.originals.has(at.key)) {
3198
+ st.originals.set(at.key, { segments: fresh.path, value: void 0 });
3199
+ }
3200
+ touchFieldRecord(st, at.key, at.segments, { updatedAt: now });
3201
+ });
3202
+ }
3203
+ }
3204
+ }
3205
+ trackForSweep(st, patch.path, key);
3206
+ }
3207
+ if (arrayOpPath !== void 0 && patches.length > 0) reshaped.push(arrayOpPath);
3208
+ if (reshaped.length > 0) stampContainers(st, reshaped, now);
3209
+ if (bareAt.length > 0) stampBareContainers(st, patches, bareAt, now);
3210
+ if (removed.length > 0) pulseStructure(st.structureSignal, removed);
3211
+ if (resizing.length > 0) st.arrayIdentity.realignContaining(resizing);
3212
+ if (structuralOp !== null) {
3213
+ st.arrayBookkeeping.applyStructuralOp(structuralOp.path, structuralOp.remap, now);
3772
3214
  }
3773
3215
  for (const listener of st.formChangeListeners) {
3774
3216
  try {
@@ -3778,33 +3220,41 @@ function commitWritePatches(st, patches, meta) {
3778
3220
  }
3779
3221
  }
3780
3222
  }
3781
- function applyFormReplacementWithPath(st, next, meta, arrayOpPath) {
3223
+ function applyFormReplacementWithPath(st, next, meta, structuralOp, now) {
3782
3224
  const prev = st.form.value;
3783
3225
  if (Object.is(prev, next)) return;
3784
3226
  const patches = [];
3785
3227
  diffAndApply(prev, next, [], (patch) => {
3786
3228
  patches.push(patch);
3787
3229
  });
3788
- if (!applyChangedKeys(prev, next, arrayOpPath, [], patches)) {
3230
+ holdRemovedStamps(st, patches, next);
3231
+ const reconciled = callConsumerFn(
3232
+ () => applyChangedKeys(prev, next, structuralOp?.path ?? null, [], patches),
3233
+ null,
3234
+ "stored-value"
3235
+ );
3236
+ if (reconciled === null) return;
3237
+ if (!reconciled) {
3789
3238
  st.form.value = next;
3790
3239
  } else if (patches.some(
3791
3240
  (p) => p.path.length > 0 && typeof p.path[0] === "string" && isShadowedKey(p.path[0])
3792
3241
  )) {
3793
3242
  triggerRef(st.form);
3794
3243
  }
3795
- commitWritePatches(st, patches, meta);
3244
+ commitWritePatches(st, patches, meta, structuralOp, now);
3796
3245
  }
3797
3246
  function applyFormReplacement(st, next, meta) {
3798
- applyFormReplacementWithPath(st, next, meta, null);
3247
+ applyFormReplacementWithPath(st, next, meta, null, writeInstant(st));
3799
3248
  }
3800
- function applyTargetedWrite(st, path, completedValue, meta) {
3249
+ function applyTargetedWrite(st, path, completedValue, meta, structuralOp, now) {
3801
3250
  const result = tryInPlaceLeafWrite(st.form.value, path, completedValue);
3802
3251
  if (!result.applied) {
3803
3252
  applyFormReplacementWithPath(
3804
3253
  st,
3805
3254
  setAtPathWithSchemaFill(st.form.value, st.schema, path, completedValue),
3806
3255
  meta,
3807
- meta?.arrayOp !== void 0 ? path : null
3256
+ structuralOp,
3257
+ now
3808
3258
  );
3809
3259
  return;
3810
3260
  }
@@ -3812,9 +3262,9 @@ function applyTargetedWrite(st, path, completedValue, meta) {
3812
3262
  diffAndApply(result.old, completedValue, path, (patch) => {
3813
3263
  patches.push(patch);
3814
3264
  });
3815
- commitWritePatches(st, patches, meta);
3265
+ commitWritePatches(st, patches, meta, null, now);
3816
3266
  }
3817
- function setValueAtPath(st, path, value, meta) {
3267
+ function setValueAtPath(st, path, value, meta, writeStamp) {
3818
3268
  if (st.effectiveDisabled.value) {
3819
3269
  if (!st.warnedDisabledWrite) {
3820
3270
  st.warnedDisabledWrite = true;
@@ -3893,7 +3343,8 @@ function setValueAtPath(st, path, value, meta) {
3893
3343
  value,
3894
3344
  variantDefault,
3895
3345
  void 0,
3896
- meta
3346
+ meta,
3347
+ writeStamp
3897
3348
  );
3898
3349
  }
3899
3350
  return reshapeUnionVariant(
@@ -3903,7 +3354,8 @@ function setValueAtPath(st, path, value, meta) {
3903
3354
  value,
3904
3355
  { [last]: value },
3905
3356
  void 0,
3906
- meta
3357
+ meta,
3358
+ writeStamp
3907
3359
  );
3908
3360
  }
3909
3361
  }
@@ -3927,7 +3379,8 @@ function setValueAtPath(st, path, value, meta) {
3927
3379
  discValue,
3928
3380
  variantDefault,
3929
3381
  valueRecord,
3930
- meta
3382
+ meta,
3383
+ writeStamp
3931
3384
  );
3932
3385
  }
3933
3386
  return reshapeUnionVariant(
@@ -3937,10 +3390,20 @@ function setValueAtPath(st, path, value, meta) {
3937
3390
  discValue,
3938
3391
  { [discKey]: discValue },
3939
3392
  void 0,
3940
- meta
3393
+ meta,
3394
+ writeStamp
3941
3395
  );
3942
3396
  }
3943
- return reshapeUnionVariant(st, path, oldDiscValue, void 0, {}, void 0, meta);
3397
+ return reshapeUnionVariant(
3398
+ st,
3399
+ path,
3400
+ oldDiscValue,
3401
+ void 0,
3402
+ {},
3403
+ void 0,
3404
+ meta,
3405
+ writeStamp
3406
+ );
3944
3407
  }
3945
3408
  }
3946
3409
  }
@@ -3990,16 +3453,17 @@ function setValueAtPath(st, path, value, meta) {
3990
3453
  if (arrayOpRemap === null && Array.isArray(value) && Array.isArray(currentValue)) {
3991
3454
  st.arrayIdentity.realign(path);
3992
3455
  }
3993
- applyTargetedWrite(st, path, completedValue, meta);
3994
- if (arrayOpRemap !== null) {
3995
- st.arrayBookkeeping.applyStructuralOp(path, arrayOpRemap);
3996
- } else if (Array.isArray(value) && Array.isArray(currentValue)) {
3456
+ const now = writeStamp ?? writeInstant(st);
3457
+ applyTargetedWrite(
3458
+ st,
3459
+ path,
3460
+ completedValue,
3461
+ meta,
3462
+ arrayOpRemap !== null ? { path, remap: arrayOpRemap } : null,
3463
+ now
3464
+ );
3465
+ if (arrayOpRemap === null && Array.isArray(value) && Array.isArray(currentValue)) {
3997
3466
  st.variantMemory.clearUnderPath(path);
3998
- st.arrayIdentity.realign(path);
3999
- } else if (isContainer(currentValue) && !isContainer(value)) {
4000
- if (subtreeHadRealBaseline(st, path, currentValue)) {
4001
- st.removedSubtrees.add(pathKey);
4002
- }
4003
3467
  }
4004
3468
  const effectiveModeAfterWrite = meta?.instance?.validateOn ?? st.fieldValidationMode;
4005
3469
  if (effectiveModeAfterWrite === "change") {
@@ -4007,7 +3471,7 @@ function setValueAtPath(st, path, value, meta) {
4007
3471
  }
4008
3472
  return true;
4009
3473
  }
4010
- function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variantDefault, consumerOverrides, meta) {
3474
+ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variantDefault, consumerOverrides, meta, writeStamp) {
4011
3475
  const sameDisc = Object.is(oldDiscValue, newDiscValue);
4012
3476
  const parentKey = canonicalizePath(parentPath).key;
4013
3477
  let baseline = variantDefault;
@@ -4073,7 +3537,7 @@ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variant
4073
3537
  appliedSync = true;
4074
3538
  }
4075
3539
  }
4076
- applyFormReplacement(st, nextForm, meta);
3540
+ applyFormReplacementWithPath(st, nextForm, meta, null, writeStamp ?? writeInstant(st));
4077
3541
  for (const k of newBlankPaths) st.blankPaths.add(k);
4078
3542
  if (reshapeMode === "change" && !appliedSync) {
4079
3543
  scheduleFieldValidation(st, parentPath, false, meta?.instance);
@@ -4094,7 +3558,9 @@ function scheduleFieldValidation(st, path, immediate, instance) {
4094
3558
  aborted: false,
4095
3559
  timer: null,
4096
3560
  settled: false,
4097
- released: false
3561
+ released: false,
3562
+ key,
3563
+ path
4098
3564
  };
4099
3565
  st.fieldValidationState.set(key, fresh);
4100
3566
  const myEpoch = ++st.scheduleEpoch;
@@ -4105,19 +3571,20 @@ function scheduleFieldValidation(st, path, immediate, instance) {
4105
3571
  try {
4106
3572
  st.activeValidations.value += 1;
4107
3573
  activeIncremented = true;
4108
- incFieldValidation(st, key);
3574
+ incFieldValidation(st, fresh.key);
4109
3575
  } catch (err) {
4110
3576
  if (activeIncremented) {
4111
3577
  st.activeValidations.value = Math.max(0, st.activeValidations.value - 1);
4112
3578
  }
4113
3579
  throw err;
4114
3580
  }
4115
- const subtreeScope = path.length > 0 && st.schema.hasContainerOrRootRefine?.() === false;
4116
- const scopePath = subtreeScope ? path : void 0;
4117
- const dataAtScope = subtreeScope ? getAtPath(st.form.value, path) : st.form.value;
4118
- const scopeKey = subtreeScope ? canonicalizePath(path).key : ROOT_PATH_KEY;
4119
- void Promise.resolve().then(() => st.schema.validateAtPath(dataAtScope, scopePath)).then((response) => {
3581
+ const startPath = fresh.path;
3582
+ const subtreeScope = startPath.length > 0 && st.schema.hasContainerOrRootRefine?.() === false;
3583
+ const dataAtScope = subtreeScope ? getAtPath(st.form.value, startPath) : st.form.value;
3584
+ void Promise.resolve().then(() => st.schema.validateAtPath(dataAtScope, subtreeScope ? startPath : void 0)).then((response) => {
4120
3585
  if (fresh.aborted) return;
3586
+ const scopePath = subtreeScope ? fresh.path : void 0;
3587
+ const scopeKey = subtreeScope ? fresh.key : ROOT_PATH_KEY;
4121
3588
  if (myEpoch <= st.lastCommittedEpoch) return;
4122
3589
  st.lastCommittedEpoch = myEpoch;
4123
3590
  if (effectiveMode === "blur") {
@@ -4128,14 +3595,14 @@ function scheduleFieldValidation(st, path, immediate, instance) {
4128
3595
  const filtered = filterAuthoredErrors(st, errors);
4129
3596
  const restamped = subtreeScope ? filtered.map((err) => ({
4130
3597
  ...err,
4131
- path: [...path, ...err.path]
3598
+ path: [...scopePath ?? [], ...err.path]
4132
3599
  })) : filtered;
4133
3600
  applySchemaErrorsForSubtree(st, scopePath ?? [], restamped);
4134
3601
  }).catch(() => {
4135
3602
  }).finally(() => {
4136
3603
  if (!fresh.released) {
4137
3604
  st.activeValidations.value = Math.max(0, st.activeValidations.value - 1);
4138
- decFieldValidation(st, key);
3605
+ decFieldValidation(st, fresh.key);
4139
3606
  }
4140
3607
  fresh.settled = true;
4141
3608
  });
@@ -4174,6 +3641,19 @@ function cancelFieldValidationUnder(st, prefix) {
4174
3641
  st.fieldValidationState.delete(key);
4175
3642
  }
4176
3643
  }
3644
+ function abortValidationAt(st, key) {
3645
+ const entry = st.fieldValidationState.get(key);
3646
+ if (entry === void 0) return;
3647
+ if (entry.timer !== null) {
3648
+ clearTimeout(entry.timer);
3649
+ } else if (!entry.settled && !entry.released) {
3650
+ st.activeValidations.value = Math.max(0, st.activeValidations.value - 1);
3651
+ decFieldValidation(st, key);
3652
+ entry.released = true;
3653
+ }
3654
+ entry.aborted = true;
3655
+ st.fieldValidationState.delete(key);
3656
+ }
4177
3657
  function onFormChange(st, listener) {
4178
3658
  st.formChangeListeners.add(listener);
4179
3659
  return () => {
@@ -4224,6 +3704,34 @@ function dispose(st) {
4224
3704
  function getValueAtPath(st, path) {
4225
3705
  return getAtPath(st.form.value, path);
4226
3706
  }
3707
+ function freezeBaseline(value) {
3708
+ if (Object.isFrozen(value)) return value;
3709
+ if (Array.isArray(value)) {
3710
+ for (const item of value) freezeBaseline(item);
3711
+ return Object.freeze(value);
3712
+ }
3713
+ if (isPlainRecord(value)) {
3714
+ for (const k of Object.keys(value)) freezeBaseline(safeOwnRead(value, k));
3715
+ return Object.freeze(value);
3716
+ }
3717
+ return value;
3718
+ }
3719
+ function getOriginalAtPath(st, path) {
3720
+ let node = st.baselineValue;
3721
+ for (let i = 0; i < path.length; i++) {
3722
+ const segment = path[i];
3723
+ if (Array.isArray(node)) {
3724
+ const at = typeof segment === "number" ? st.arrayIdentity.baselineIndexOf(path.slice(0, i), segment) : -1;
3725
+ if (at < 0) return void 0;
3726
+ node = node[at];
3727
+ } else if (isPlainRecord(node) && typeof segment === "string") {
3728
+ node = safeOwnRead(node, segment);
3729
+ } else {
3730
+ return void 0;
3731
+ }
3732
+ }
3733
+ return freezeBaseline(node);
3734
+ }
4227
3735
  const ERROR_SOURCES = ["schema", "user"];
4228
3736
  function createErrorCells(schema, pristine) {
4229
3737
  const raw = /* @__PURE__ */ new Map();
@@ -4301,6 +3809,54 @@ function restoreErrorCells(st, entries) {
4301
3809
  st.errorCells().set(key, { schema: [...cell.schema], user: [...cell.user] });
4302
3810
  }
4303
3811
  }
3812
+ function captureStructure(st) {
3813
+ return {
3814
+ identity: st.arrayIdentity.snapshot(),
3815
+ originals: [...toRaw(st.originals)],
3816
+ originalBlank: [...st.originalBlankPaths],
3817
+ removed: [...st.removedPaths]
3818
+ };
3819
+ }
3820
+ function restoreStructure(st, snapshot, remaps, returning) {
3821
+ const originals = new Map(snapshot.originals);
3822
+ const raw = toRaw(st.form.value);
3823
+ const held = /* @__PURE__ */ new Map();
3824
+ for (const [key, { segments }] of toRaw(st.originals)) {
3825
+ if (!originals.has(key) && hasAtPath(raw, segments)) {
3826
+ noteHold(st, held, raw, segments, segments.length - 1, segments);
3827
+ }
3828
+ }
3829
+ applyHolds(st, held);
3830
+ st.arrayIdentity.restore(snapshot.identity);
3831
+ replaceOriginals(st, originals);
3832
+ st.originalBlankPaths.clear();
3833
+ for (const key of snapshot.originalBlank) st.originalBlankPaths.add(key);
3834
+ const pulsed = [...st.removedPaths.values()];
3835
+ st.removedPaths.clear();
3836
+ for (const [key, segments] of snapshot.removed) {
3837
+ st.removedPaths.set(key, segments);
3838
+ pulsed.push(segments);
3839
+ }
3840
+ for (const [segments, remap] of remaps) {
3841
+ migrateFieldRecords(st.fields, segments, remap, (path, released) => {
3842
+ let stamp = "";
3843
+ for (const { updatedAt } of released) {
3844
+ if (updatedAt !== null && updatedAt > stamp) stamp = updatedAt;
3845
+ }
3846
+ if (stamp !== "") eachAncestorOnce([path], (key, at) => raiseStamp(st, key, at, stamp));
3847
+ foldInteractionHistory(st, path, released);
3848
+ });
3849
+ }
3850
+ for (const record of returning) {
3851
+ st.fields.set(canonicalizePath(record.path).key, {
3852
+ ...record,
3853
+ connected: false,
3854
+ focused: null,
3855
+ blurred: null
3856
+ });
3857
+ }
3858
+ if (pulsed.length > 0) pulseStructure(st.structureSignal, pulsed);
3859
+ }
4304
3860
  function getErrorsForPath(st, path) {
4305
3861
  const { key } = canonicalizePath(path);
4306
3862
  const cell = st.errorCells().get(key);
@@ -4325,6 +3881,7 @@ function noteDomConnected(st, path) {
4325
3881
  }
4326
3882
  function noteDomDisconnected(st, path) {
4327
3883
  const { key } = canonicalizePath(path);
3884
+ if (!st.fields.has(key)) return;
4328
3885
  touchFieldRecord(st, key, path, { connected: false, focused: null, blurred: null });
4329
3886
  }
4330
3887
  function markConnectedOptimistically(st, path) {
@@ -4384,17 +3941,39 @@ function markInteracted(st, path) {
4384
3941
  if (st.fields.get(key)?.interacted === true) return;
4385
3942
  touchFieldRecord(st, key, path, { interacted: true });
4386
3943
  }
3944
+ function markBareContainers(st, segments, ladder) {
3945
+ const value = getAtPath(toRaw(st.form.value), segments);
3946
+ if (!isDescendable(value)) return false;
3947
+ const bare = [];
3948
+ diffAndApply(void 0, value, segments, (patch) => {
3949
+ if (isBarePatch(patch)) bare.push(patch.path);
3950
+ });
3951
+ eachAncestorOnce(bare, (key, path) => {
3952
+ const current = st.fields.get(key);
3953
+ if (current?.touched === true && (!ladder || current.interacted && current.blurredAfterInteraction)) {
3954
+ return;
3955
+ }
3956
+ touchFieldRecord(
3957
+ st,
3958
+ key,
3959
+ path,
3960
+ ladder ? { touched: true, interacted: true, blurredAfterInteraction: true } : { touched: true }
3961
+ );
3962
+ });
3963
+ return bare.length > 0;
3964
+ }
4387
3965
  function touchAtPath(st, segments) {
4388
3966
  const formValue = st.form.value;
4389
3967
  let touchedAny = false;
4390
3968
  for (const [leafKey, entry] of st.originals) {
4391
- if (!isPathPrefix(segments, entry.segments)) continue;
3969
+ if (!isPathPrefix(segments, entry.segments) || isDescendable(entry.value)) continue;
4392
3970
  if (!hasAtPath(formValue, entry.segments)) continue;
4393
3971
  touchedAny = true;
4394
3972
  const current = st.fields.get(leafKey);
4395
3973
  if (current?.touched === true) continue;
4396
3974
  touchFieldRecord(st, leafKey, entry.segments, { touched: true });
4397
3975
  }
3976
+ if (markBareContainers(st, segments, false)) touchedAny = true;
4398
3977
  if (!touchedAny && true) {
4399
3978
  console.warn(
4400
3979
  `[attaform] form.touch(): no fields resolved at path ${JSON.stringify(segments)}. Check the path matches an existing field or container.`
@@ -4406,7 +3985,7 @@ function interactAtPath(st, segments) {
4406
3985
  const formValue = st.form.value;
4407
3986
  let interactedAny = false;
4408
3987
  for (const [leafKey, entry] of st.originals) {
4409
- if (!isPathPrefix(segments, entry.segments)) continue;
3988
+ if (!isPathPrefix(segments, entry.segments) || isDescendable(entry.value)) continue;
4410
3989
  if (!hasAtPath(formValue, entry.segments)) continue;
4411
3990
  interactedAny = true;
4412
3991
  const current = st.fields.get(leafKey);
@@ -4419,6 +3998,7 @@ function interactAtPath(st, segments) {
4419
3998
  blurredAfterInteraction: true
4420
3999
  });
4421
4000
  }
4001
+ if (markBareContainers(st, segments, true)) interactedAny = true;
4422
4002
  if (!interactedAny && true) {
4423
4003
  console.warn(
4424
4004
  `[attaform] form.interact(): no fields resolved at path ${JSON.stringify(segments)}. Check the path matches an existing field or container.`
@@ -4465,14 +4045,31 @@ function adoptResolvedDefaults(st, value) {
4465
4045
  st.schema
4466
4046
  )
4467
4047
  );
4468
- seedOriginalsFromBaseline(
4469
- st,
4470
- computeBaseline(
4471
- st.schema,
4472
- st.defaultValues,
4473
- st.schema.getDefaultValues({ useDefaultSchemaValues: true })
4474
- )
4048
+ st.baselineValue = computeBaseline(
4049
+ st.schema,
4050
+ st.defaultValues,
4051
+ st.schema.getDefaultValues({ useDefaultSchemaValues: true })
4475
4052
  );
4053
+ seedOriginalsFromBaseline(st, st.baselineValue);
4054
+ st.arrayIdentity.rebaseline((segments) => holdsListAt(value, segments));
4055
+ const dropped = [];
4056
+ for (const [key, segments] of st.removedPaths) {
4057
+ if (st.originals.get(key)?.value !== void 0) continue;
4058
+ st.removedPaths.delete(key);
4059
+ dropped.push(segments);
4060
+ }
4061
+ if (dropped.length > 0) pulseStructure(st.structureSignal, dropped);
4062
+ }
4063
+ function holdsListAt(value, segments) {
4064
+ let node = value;
4065
+ for (const segment of segments) {
4066
+ if (Array.isArray(node)) return true;
4067
+ if (!isPlainRecord(node) || typeof segment !== "string" || !safeOwnHas(node, segment)) {
4068
+ return false;
4069
+ }
4070
+ node = safeOwnRead(node, segment);
4071
+ }
4072
+ return Array.isArray(node);
4476
4073
  }
4477
4074
  function reset(st, nextDefaultValues) {
4478
4075
  const resetSource = nextDefaultValues === void 0 ? st.defaultValues : mergeSparseHydration(
@@ -4483,19 +4080,25 @@ function reset(st, nextDefaultValues) {
4483
4080
  st.defaultValues = structuralSnapshot(resetSource);
4484
4081
  const next = computeBaseline(
4485
4082
  st.schema,
4486
- resetSource,
4083
+ st.defaultValues,
4487
4084
  st.schema.getDefaultValues({ useDefaultSchemaValues: true })
4488
4085
  );
4489
4086
  rebuildAuthoredPaths(st, resetSource, next);
4490
- applyFormReplacement(st, next);
4491
- st.arrayIdentity.rebaselineAll();
4492
- st.removedSubtrees.clear();
4493
- seedOriginalsFromBaseline(st, next);
4494
4087
  if (nextDefaultValues !== void 0) {
4495
4088
  const mentioned = /* @__PURE__ */ new Set();
4496
4089
  walkAuthoredFromConstraints(nextDefaultValues, [], mentioned);
4497
4090
  for (const key of mentioned) st.originalBlankPaths.delete(key);
4498
4091
  }
4092
+ const now = writeInstant(st);
4093
+ applyFormReplacementWithPath(st, structuralSnapshot(next), void 0, null, now);
4094
+ st.arrayIdentity.rebaseline(() => true);
4095
+ if (st.removedPaths.size > 0) {
4096
+ const cleared = [...st.removedPaths.values()];
4097
+ st.removedPaths.clear();
4098
+ pulseStructure(st.structureSignal, cleared);
4099
+ }
4100
+ st.baselineValue = next;
4101
+ seedOriginalsFromBaseline(st, next);
4499
4102
  st.blankPaths.clear();
4500
4103
  for (const key of st.originalBlankPaths) {
4501
4104
  st.blankPaths.add(key);
@@ -4504,7 +4107,7 @@ function reset(st, nextDefaultValues) {
4504
4107
  replaceErrorChannel(st, "schema", st.schema.validateSync(next));
4505
4108
  st.firstValidationDone.value = initialFirstValidationGate(st.schema);
4506
4109
  queueInitialAsyncValidation(st);
4507
- const now = (/* @__PURE__ */ new Date()).toISOString();
4110
+ noteStamp(st, now);
4508
4111
  for (const [pathKey, record] of st.fields) {
4509
4112
  st.fields.set(pathKey, withClearedHistoryFlags(record, now));
4510
4113
  }
@@ -4533,6 +4136,7 @@ function reset(st, nextDefaultValues) {
4533
4136
  }
4534
4137
  function resetField(st, path) {
4535
4138
  const { key: targetKey, segments: targetSegments } = canonicalizePath(path);
4139
+ const now = writeInstant(st);
4536
4140
  st.variantMemory.clearUnderPath(targetSegments);
4537
4141
  cancelFieldValidationUnder(st, targetSegments);
4538
4142
  cancelTransformsUnder(st, targetSegments);
@@ -4541,9 +4145,21 @@ function resetField(st, path) {
4541
4145
  if (segs === null) continue;
4542
4146
  if (isPathPrefix(targetSegments, segs)) st.pathSnapshots.delete(snapKey);
4543
4147
  }
4148
+ const cleared = [];
4149
+ for (const [fieldKey, record] of Array.from(st.fields.entries())) {
4150
+ if (!isPathPrefix(targetSegments, record.path)) continue;
4151
+ st.fields.set(fieldKey, withClearedHistoryFlags(record, record.updatedAt));
4152
+ cleared.push(fieldKey);
4153
+ }
4544
4154
  const leafEntry = st.originals.get(targetKey);
4545
4155
  if (leafEntry !== void 0) {
4546
- const wrote = setValueAtPath(st, targetSegments, leafEntry.value);
4156
+ const wrote = setValueAtPath(
4157
+ st,
4158
+ targetSegments,
4159
+ structuralSnapshot(leafEntry.value),
4160
+ void 0,
4161
+ now
4162
+ );
4547
4163
  if (!wrote) {
4548
4164
  console.error(
4549
4165
  `[attaform] resetField: leaf write rejected for path '${targetKey}'. Originals contain a value that doesn't satisfy the slim primitive shape. This is a bug in the construction pipeline.`
@@ -4561,10 +4177,10 @@ function resetField(st, path) {
4561
4177
  if (subtree === void 0) {
4562
4178
  subtree = typeof relative[0] === "number" ? [] : {};
4563
4179
  }
4564
- subtree = setAtPath(subtree, relative, entry.value);
4180
+ subtree = setAtPath(subtree, relative, structuralSnapshot(entry.value));
4565
4181
  }
4566
4182
  if (anyMatch) {
4567
- const wroteSubtree = setValueAtPath(st, targetSegments, subtree);
4183
+ const wroteSubtree = setValueAtPath(st, targetSegments, subtree, void 0, now);
4568
4184
  if (!wroteSubtree) {
4569
4185
  console.error(
4570
4186
  `[attaform] resetField: subtree write rejected at path '${targetKey}'. Originals contain values that don't satisfy the slim primitive shape. This is a bug in the construction pipeline.`
@@ -4573,8 +4189,14 @@ function resetField(st, path) {
4573
4189
  }
4574
4190
  }
4575
4191
  deleteErrorCellsUnderPrefix(st, targetSegments);
4576
- for (const [fieldKey, record] of Array.from(st.fields.entries())) {
4577
- if (isPathPrefix(targetSegments, record.path)) clearFieldRecordFlags(st, fieldKey);
4192
+ const raw = toRaw(st.form.value);
4193
+ for (const fieldKey of cleared) {
4194
+ const record = st.fields.get(fieldKey);
4195
+ if (record === void 0 || !hasAtPath(raw, record.path)) continue;
4196
+ raiseStamp(st, fieldKey, record.path, now);
4197
+ }
4198
+ if (cleared.length > 0 && hasAtPath(raw, targetSegments)) {
4199
+ stampContainers(st, [targetSegments], now);
4578
4200
  }
4579
4201
  }
4580
4202
  function deleteErrorCellsUnderPrefix(st, prefix) {
@@ -4587,36 +4209,35 @@ function deleteErrorCellsUnderPrefix(st, prefix) {
4587
4209
  }
4588
4210
  }
4589
4211
  }
4590
- function clearFieldRecordFlags(st, pathKey) {
4591
- const record = st.fields.get(pathKey);
4592
- if (record === void 0) return;
4593
- st.fields.set(pathKey, withClearedHistoryFlags(record, (/* @__PURE__ */ new Date()).toISOString()));
4594
- }
4595
4212
  function isPristineAtPathByKey(st, key, segments) {
4596
4213
  if (st.blankPaths.has(key) !== st.originalBlankPaths.has(key)) return false;
4597
4214
  const entry = st.originals.get(key);
4598
4215
  if (entry === void 0) return true;
4599
4216
  return Object.is(getAtPath(st.form.value, segments), entry.value);
4600
4217
  }
4601
- function hasStructuralChangeUnder(st, path) {
4218
+ function hasStructuralChangeUnder(st, path, key) {
4219
+ subscribeStructure(st.structureSignal, key);
4602
4220
  return st.arrayIdentity.hasStructuralChangeUnder(path);
4603
4221
  }
4604
- function subtreeHadRealBaseline(st, prefix, removedValue) {
4605
- let had = false;
4606
- diffAndApply(removedValue, void 0, prefix, (patch) => {
4607
- if (had || patch.kind !== "removed") return;
4608
- const record = st.originals.get(canonicalizePath(patch.path).key);
4609
- if (record?.value !== void 0) had = true;
4222
+ function pulseStructure(signal, paths) {
4223
+ const keys = signal.keys;
4224
+ if (keys === null) return;
4225
+ eachAncestorOnce(paths, (key) => {
4226
+ keys.add(key);
4227
+ keys.delete(key);
4610
4228
  });
4611
- return had;
4612
4229
  }
4613
- function hasRemovedSubtreeUnder(st, prefix) {
4614
- if (st.removedSubtrees.size === 0) return false;
4615
- for (const key of st.removedSubtrees) {
4616
- const segments = segmentsForPathKey(key);
4617
- if (segments === null) continue;
4230
+ function subscribeStructure(signal, key) {
4231
+ signal.keys ?? (signal.keys = shallowReactive(/* @__PURE__ */ new Set()));
4232
+ void signal.keys.has(key);
4233
+ }
4234
+ function hasRemovedSubtreeUnder(st, prefix, key) {
4235
+ subscribeStructure(st.structureSignal, key);
4236
+ if (st.removedPaths.size === 0) return false;
4237
+ const raw = toRaw(st.form.value);
4238
+ for (const segments of st.removedPaths.values()) {
4618
4239
  if (!isPathPrefix(prefix, segments)) continue;
4619
- if (isContainer(getAtPath(toRaw(st.form.value), segments))) continue;
4240
+ if (hasAtPath(raw, segments)) continue;
4620
4241
  return true;
4621
4242
  }
4622
4243
  return false;
@@ -4652,10 +4273,14 @@ function createFormStore(options) {
4652
4273
  warn: true
4653
4274
  }) : initialData;
4654
4275
  const form = ref(stubbedInitialData);
4655
- const arrayIdentity = createArrayIdentity((arraySegs) => {
4656
- const v = getAtPath(toRaw(form.value), arraySegs);
4657
- return Array.isArray(v) ? v.length : 0;
4658
- });
4276
+ const structureSignal = { keys: null };
4277
+ const arrayIdentity = createArrayIdentity(
4278
+ (arraySegs) => {
4279
+ const v = getAtPath(toRaw(form.value), arraySegs);
4280
+ return Array.isArray(v) ? v.length : 0;
4281
+ },
4282
+ (arrayPaths) => pulseStructure(structureSignal, arrayPaths)
4283
+ );
4659
4284
  const fields = shallowReactive(/* @__PURE__ */ new Map());
4660
4285
  const domBinding = shallowRef(null);
4661
4286
  const errorStore = createErrorCells(
@@ -4664,7 +4289,7 @@ function createFormStore(options) {
4664
4289
  );
4665
4290
  const errorCells = () => errorStore(true);
4666
4291
  const originals = shallowReactive(/* @__PURE__ */ new Map());
4667
- const removedSubtrees = /* @__PURE__ */ new Set();
4292
+ const removedPaths = /* @__PURE__ */ new Map();
4668
4293
  const blankPaths = reactive(/* @__PURE__ */ new Set());
4669
4294
  const originalBlankPaths = /* @__PURE__ */ new Set();
4670
4295
  const seededBlankPaths = hydration !== void 0 ? (hydration.blankPaths ?? []).map((dotted) => canonicalizePath(dotted).key) : options.initialBlankPaths ?? [];
@@ -4773,6 +4398,8 @@ function createFormStore(options) {
4773
4398
  blankPaths,
4774
4399
  originalBlankPaths,
4775
4400
  authoredPaths,
4401
+ removedPaths,
4402
+ notifyStructure: (paths) => pulseStructure(structureSignal, paths),
4776
4403
  fieldValidationCounts,
4777
4404
  fieldValidatingSince,
4778
4405
  fieldValidationState,
@@ -4780,6 +4407,9 @@ function createFormStore(options) {
4780
4407
  arrayIdentity,
4781
4408
  variantMemory,
4782
4409
  touchFieldRecord: (pathKey, path, patch) => touchFieldRecord(st, pathKey, path, patch),
4410
+ stampContainers: (containers, now) => stampContainers(st, containers, now),
4411
+ stampBareContainers: (patches, bare, now) => stampBareContainers(st, patches, bare, now),
4412
+ foldInteractionHistory: (path, released) => foldInteractionHistory(st, path, released),
4783
4413
  decFieldValidation: (key) => decFieldValidation(st, key)
4784
4414
  });
4785
4415
  const bind = (fn) => (...args) => fn(st, ...args);
@@ -4830,6 +4460,8 @@ function createFormStore(options) {
4830
4460
  // the literal they passed from mutating the form's defaults from outside.
4831
4461
  // `reset()` snapshots for a harder reason, where an alias IS reachable.
4832
4462
  defaultValues: ownDefaultValues,
4463
+ // The form holds a copy of it, or the hydration payload.
4464
+ baselineValue: schemaInitialData,
4833
4465
  ssrPrefetch,
4834
4466
  rememberVariants,
4835
4467
  fieldValidationMode,
@@ -4843,7 +4475,8 @@ function createFormStore(options) {
4843
4475
  cleanupHooks,
4844
4476
  authoredPaths,
4845
4477
  arrayIdentity,
4846
- removedSubtrees,
4478
+ removedPaths,
4479
+ structureSignal,
4847
4480
  variantMemory,
4848
4481
  pathOrdinals,
4849
4482
  pathSnapshots,
@@ -4853,6 +4486,7 @@ function createFormStore(options) {
4853
4486
  arrayBookkeeping,
4854
4487
  skipErrorSeed: () => errorStore(false),
4855
4488
  nextOrdinal: 0,
4489
+ latestStamp: "",
4856
4490
  scheduleEpoch: 0,
4857
4491
  lastCommittedEpoch: 0,
4858
4492
  transformTokenSeq: 0,
@@ -4865,6 +4499,7 @@ function createFormStore(options) {
4865
4499
  applyFormReplacement: bind(applyFormReplacement),
4866
4500
  setValueAtPath: bind(setValueAtPath),
4867
4501
  getValueAtPath: bind(getValueAtPath),
4502
+ getOriginalAtPath: bind(getOriginalAtPath),
4868
4503
  arrayElementKey: bind(arrayElementKey),
4869
4504
  reset: bind(reset),
4870
4505
  resetField: bind(resetField),
@@ -4890,6 +4525,8 @@ function createFormStore(options) {
4890
4525
  ),
4891
4526
  clearUserErrors: (path) => clearErrorChannel(st, "user", path),
4892
4527
  restoreErrorCells: bind(restoreErrorCells),
4528
+ captureStructure: bind(captureStructure),
4529
+ restoreStructure: bind(restoreStructure),
4893
4530
  getErrorsForPath: bind(getErrorsForPath),
4894
4531
  ensurePathOrdinal: bind(ensurePathOrdinal),
4895
4532
  noteDomConnected: bind(noteDomConnected),
@@ -4923,7 +4560,7 @@ function createFormStore(options) {
4923
4560
  diffAndApply({}, schemaInitialData, [], (patch) => {
4924
4561
  if (patch.kind !== "added") return;
4925
4562
  const { key } = canonicalizePath(patch.path);
4926
- originals.set(key, { segments: patch.path, value: patch.newValue });
4563
+ if (seedBaseline(originals, key, patch)) return;
4927
4564
  ensurePathOrdinal(st, key);
4928
4565
  if (hydration === void 0) {
4929
4566
  fields.set(key, {
@@ -4938,6 +4575,7 @@ function createFormStore(options) {
4938
4575
  });
4939
4576
  }
4940
4577
  });
4578
+ if (fields.size > 0) noteStamp(st, initStamp);
4941
4579
  if (hydration !== void 0) {
4942
4580
  for (const [rawKey, record] of hydration.fields) {
4943
4581
  if (typeof rawKey !== "string" || !isHydratedFieldRecord(record)) {
@@ -4945,6 +4583,7 @@ function createFormStore(options) {
4945
4583
  continue;
4946
4584
  }
4947
4585
  fields.set(rawKey, record);
4586
+ noteStamp(st, record.updatedAt);
4948
4587
  }
4949
4588
  for (const [rawKey, errs] of hydration.schemaErrors) {
4950
4589
  if (typeof rawKey !== "string" || !isHydratedValidationErrorArray(errs)) {
@@ -5522,7 +5161,7 @@ function buildWizardStatusesProxy(statuses) {
5522
5161
  // resolves to `undefined` (consistent with property-access).
5523
5162
  resolveCall: (arg) => statuses[String(arg)]?.value,
5524
5163
  ownKeys: () => Object.keys(statuses),
5525
- hasKey: (key) => Object.hasOwn(statuses, key)
5164
+ hasKey: (key) => safeOwnHas(statuses, key)
5526
5165
  });
5527
5166
  }
5528
5167
 
@@ -6825,4 +6464,4 @@ function escapeForInlineScript(json) {
6825
6464
  });
6826
6465
  }
6827
6466
 
6828
- 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 };
6467
+ export { injectWizard as a, isUnset as b, useAbstractForm as c, useRegister as d, escapeForInlineScript as e, useWizard as f, gate as g, humanize as h, injectForm as i, lazy as l, slimKindOf as s, unset as u };