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,82 +1,10 @@
1
1
  import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, reactive, shallowReactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, effectScope, onBeforeMount, onBeforeUpdate, onMounted, nextTick } from 'vue';
2
- import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform.C4shmwyJ.mjs';
3
- import { c as armDomBinding, r as recordInheritedModifiers, w as withInheritedModifiers } from './attaform.DxKQsqFZ.mjs';
2
+ import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform.CdA9bfRv.mjs';
3
+ import { c as armDomBinding, r as recordInheritedModifiers, w as withInheritedModifiers } from './attaform.T0YjhjPU.mjs';
4
4
  import { R as REGISTER_OWNER_MARKER, b as V_REGISTER_MARKER } from './attaform.KvlEedlF.mjs';
5
- 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 ANONYMOUS_WIZARD_KEY_PREFIX } from './attaform.8mfKZCwH.mjs';
6
- 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.CdZIEf7_.mjs';
7
-
8
- const AttaformErrorCode = {
9
- /** A required field is in the blank set: no value was supplied. */
10
- NoValueSupplied: "atta:no-value-supplied",
11
- /** The schema adapter's `validateAtPath` threw synchronously. */
12
- AdapterThrew: "atta:adapter-threw",
13
- /**
14
- * User code inside a `z.preprocess`, `.refine`, or `.transform`
15
- * threw (sync or async). The adapter caught the throw and surfaced
16
- * it as a `ValidationError` at the field path so the form's normal
17
- * error pipeline handles it instead of leaking as an unhandled
18
- * rejection or routing through `submitError`.
19
- */
20
- ValidatorThrew: "atta:validator-threw",
21
- /**
22
- * A function-form `defaultValues` factory threw or its promise
23
- * rejected. The runtime captures the raw error on `form.hydrateError`
24
- * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
25
- * the standard error pipeline carries the signal. Critical for the
26
- * SSR round-trip: `hydrateError` itself does not ride the wire
27
- * payload, but `schemaErrors` does, so the client sees the failure
28
- * after rehydration without an extra channel.
29
- */
30
- HydrationFailed: "atta:hydration-failed",
31
- /** The supplied path didn't resolve to any node in the schema. */
32
- PathNotFound: "atta:path-not-found",
33
- /**
34
- * A walked form's `activate()` (async `defaultValues` factory) threw
35
- * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
36
- * `ValidationError` at the form-level path (`[]`) so the wizard's
37
- * aggregate error pipeline can carry the failure alongside ordinary
38
- * validation errors. The raw factory error remains on
39
- * `form.hydrateError` for retry UX.
40
- */
41
- ActivationFailed: "atta:activation-failed",
42
- /**
43
- * Default code stamped on a manual error set through `form.setErrors`
44
- * when the caller omits an explicit `code`. The `setErrors` input is
45
- * lenient (`code` optional), so this is the fallback that keeps every
46
- * produced `ValidationError` carrying a stable, branchable identifier.
47
- * Override it per error by passing your own `code` (`api:…`, `auth:…`).
48
- */
49
- UserError: "atta:user-error",
50
- /**
51
- * Stamped on a `ValidationError` synthesized from a throw or rejection
52
- * out of a `handleSubmit` `onSubmit` callback. The raw error also lands
53
- * on `form.meta.submitError` (a raw-`Error` inspection channel); this
54
- * code marks the copy piped into the user-error layer so the failure
55
- * also surfaces on `form.errors` / `meta.ownErrors` / `firstOwnError`
56
- * where the UI already reads. A throw carrying its own `path` / `code`
57
- * keeps them; a bare `Error` or a non-Error throw defaults here at the
58
- * form-level path `[]`.
59
- */
60
- SubmitError: "atta:submit-error",
61
- /**
62
- * Synthesized by `wizard.handleSubmit` when the whole-wizard submit
63
- * would otherwise succeed but a `gate()` step is still uncleared. A
64
- * gate clears only on its member form's clean submit (confirmation), so
65
- * a downstream step that happens to be valid-by-default must not let the
66
- * wizard finish past an unconfirmed prerequisite. Emitted at the gate
67
- * form's key (path `[]`) so the aggregate error pipeline routes focus to
68
- * the gate, and `done` never latches.
69
- */
70
- GateNotCleared: "atta:gate-not-cleared"
71
- };
72
- function makeBlankRequiredError(segments) {
73
- return {
74
- message: "No value supplied",
75
- path: [...segments],
76
- code: AttaformErrorCode.NoValueSupplied
77
- };
78
- }
79
- const NO_ERRORS = Object.freeze([]);
5
+ 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.CgyyJVuq.mjs';
6
+ 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.BhtAZysa.mjs';
7
+ 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 } from './attaform.BNUhW4qC.mjs';
80
8
 
81
9
  function isGateOpen(field, formMeta) {
82
10
  return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
@@ -142,7 +70,7 @@ function hashStableString(input, seed = 0) {
142
70
  const ANON_STEM = "atta";
143
71
  function readableFormKeyStem(formKey) {
144
72
  if (formKey === "" || formKey.startsWith(ANONYMOUS_FORM_KEY_PREFIX)) return ANON_STEM;
145
- const sanitized = formKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|(?<!-)-+$/g, "");
73
+ const sanitized = formKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|([^-])-+$/g, "$1");
146
74
  return sanitized === "" ? ANON_STEM : sanitized;
147
75
  }
148
76
  function fieldIdToken(formInstanceId, pathKey) {
@@ -176,323 +104,6 @@ function humanize(segment) {
176
104
  }).join(" ");
177
105
  }
178
106
 
179
- const NOT_FOUND = /* @__PURE__ */ Symbol("NOT_FOUND");
180
- function mapKeyForSegment(map, segment, declared) {
181
- if (map.has(segment)) return segment;
182
- if (typeof segment !== "number") return segment;
183
- const asString = String(segment);
184
- if (map.has(asString)) return asString;
185
- return declared === "string" ? asString : segment;
186
- }
187
- function isRebuildableContainer(root, segment) {
188
- if (root === null || root === void 0 || typeof root !== "object") return true;
189
- return typeof segment === "number" ? Array.isArray(root) : isPlainRecord(root);
190
- }
191
- function descendStep(value, segment) {
192
- if (value === null || value === void 0) return NOT_FOUND;
193
- if (typeof value !== "object") return NOT_FOUND;
194
- if (Array.isArray(value)) {
195
- if (typeof segment !== "number") return NOT_FOUND;
196
- if (!(segment in value)) return NOT_FOUND;
197
- return value[segment];
198
- }
199
- if (value instanceof Map) {
200
- const key2 = mapKeyForSegment(value, segment);
201
- if (!value.has(key2)) return NOT_FOUND;
202
- return value.get(key2);
203
- }
204
- const record = value;
205
- const key = typeof segment === "number" ? String(segment) : segment;
206
- if (isShadowedKey(key)) {
207
- if (!safeOwnHas(record, key)) return NOT_FOUND;
208
- return safeOwnRead(record, key);
209
- }
210
- if (!(key in record)) return NOT_FOUND;
211
- return record[key];
212
- }
213
- function getAtPath(root, path) {
214
- if (path.length === 0) return root;
215
- try {
216
- let current = root;
217
- for (const segment of path) {
218
- const next = descendStep(current, segment);
219
- if (next === NOT_FOUND) return void 0;
220
- current = next;
221
- }
222
- return current;
223
- } catch {
224
- return void 0;
225
- }
226
- }
227
- function hasAtPath(root, path) {
228
- if (path.length === 0) return true;
229
- try {
230
- return hasAtPathUnguarded(root, path);
231
- } catch {
232
- return false;
233
- }
234
- }
235
- function hasAtPathUnguarded(root, path) {
236
- let current = root;
237
- for (let i = 0; i < path.length - 1; i++) {
238
- const segment = path[i];
239
- const next = descendStep(current, segment);
240
- if (next === NOT_FOUND) return false;
241
- current = next;
242
- }
243
- const last = path[path.length - 1];
244
- if (current === null || current === void 0) return false;
245
- if (typeof current !== "object") return false;
246
- if (Array.isArray(current)) {
247
- return typeof last === "number" && consumerHas(current, last);
248
- }
249
- if (current instanceof Map) {
250
- return current.has(mapKeyForSegment(current, last));
251
- }
252
- const key = typeof last === "number" ? String(last) : last;
253
- if (isShadowedKey(key)) return safeOwnHas(current, key);
254
- return consumerHas(current, key);
255
- }
256
- function isPlainRecord(value) {
257
- if (value === null || typeof value !== "object") return false;
258
- if (Array.isArray(value)) return false;
259
- const proto = Object.getPrototypeOf(value);
260
- return proto === null || proto === Object.prototype;
261
- }
262
- const NO_SCHEMA_DEFAULTS = /* @__PURE__ */ new Set();
263
- const NO_SCHEMA_FILL = {
264
- getDefaultAtPath: () => void 0,
265
- arrayShapeAtPath: () => null,
266
- getSlimPrimitiveTypesAtPath: () => NO_SCHEMA_DEFAULTS,
267
- entryKeyKindAtPath: () => void 0
268
- };
269
- function setAtPath(root, path, value) {
270
- return setAtPathWithSchemaFill(root, NO_SCHEMA_FILL, path, value);
271
- }
272
- const NO_IN_PLACE = { applied: false };
273
- function tryInPlaceLeafWrite(root, path, value) {
274
- if (path.length === 0) return NO_IN_PLACE;
275
- try {
276
- return descendAndWrite(root, path, value);
277
- } catch {
278
- return NO_IN_PLACE;
279
- }
280
- }
281
- function descendAndWrite(root, path, value) {
282
- let node = root;
283
- for (let i = 0; i < path.length; i++) {
284
- const seg = path[i];
285
- if (node instanceof Map) return NO_IN_PLACE;
286
- if (Array.isArray(node)) {
287
- if (typeof seg !== "number" || seg < 0 || seg >= node.length) return NO_IN_PLACE;
288
- } else if (isPlainRecord(node)) {
289
- if (typeof seg !== "string" || isShadowedKey(seg) || !(seg in node)) return NO_IN_PLACE;
290
- } else {
291
- return NO_IN_PLACE;
292
- }
293
- const container = node;
294
- if (i < path.length - 1) {
295
- node = container[seg];
296
- continue;
297
- }
298
- const old = container[seg];
299
- if (isPlainRecord(old) || Array.isArray(old)) return NO_IN_PLACE;
300
- container[seg] = value;
301
- return { applied: true, old };
302
- }
303
- return NO_IN_PLACE;
304
- }
305
- function mergeStructural(schema, path, consumer, defaultValue = schema.getDefaultAtPath(path)) {
306
- const scratch = path.slice();
307
- return mergeStructuralImpl(schema, scratch, consumer, defaultValue);
308
- }
309
- function mergeStructuralImpl(schema, scratch, consumer, defaultValue) {
310
- if (consumer === void 0) {
311
- if (schema.getSlimPrimitiveTypesAtPath(scratch).has("undefined")) {
312
- return void 0;
313
- }
314
- return defaultValue;
315
- }
316
- if (consumer === null) return null;
317
- if (Array.isArray(consumer)) {
318
- return mergeStructuralArray(schema, scratch, consumer);
319
- }
320
- if (consumer instanceof Map) {
321
- return mergeStructuralMap(schema, scratch, consumer);
322
- }
323
- if (isPlainRecord(consumer)) {
324
- if (!isPlainRecord(defaultValue)) {
325
- return consumer;
326
- }
327
- const out = spreadConsumerRecord(consumer);
328
- const filledAny = fillMissingKeysFromDefault(schema, scratch, consumer, defaultValue, out);
329
- const recursedAny = recurseIntoConsumerKeys(schema, scratch, consumer, defaultValue, out);
330
- return filledAny || recursedAny ? out : consumer;
331
- }
332
- return consumer;
333
- }
334
- function mergeStructuralMap(schema, scratch, consumer) {
335
- if (consumer.size === 0) return consumer;
336
- let entryDefault;
337
- let entryDefaultRead = false;
338
- let out = null;
339
- for (const [key, entry] of consumer) {
340
- if (typeof key !== "string" && typeof key !== "number") continue;
341
- scratch.push(key);
342
- if (!entryDefaultRead) {
343
- entryDefault = schema.getDefaultAtPath(scratch);
344
- entryDefaultRead = true;
345
- }
346
- const merged = mergeStructuralImpl(schema, scratch, entry, entryDefault);
347
- scratch.pop();
348
- if (merged === entry) continue;
349
- out ?? (out = new Map(consumer));
350
- out.set(key, merged);
351
- }
352
- return out ?? consumer;
353
- }
354
- function mergeStructuralArray(schema, scratch, consumer) {
355
- const shape = schema.arrayShapeAtPath(scratch);
356
- const isTuple = typeof shape === "number";
357
- const targetLen = isTuple ? shape : consumer.length;
358
- let cachedElementDefault;
359
- let cachedElementDefaultRead = false;
360
- let mutated = targetLen > consumer.length;
361
- const out = copyConsumerArray(consumer);
362
- while (out.length < targetLen) out.push(void 0);
363
- for (let i = 0; i < targetLen; i++) {
364
- scratch.push(i);
365
- let elemDefault;
366
- if (isTuple) {
367
- elemDefault = schema.getDefaultAtPath(scratch);
368
- } else {
369
- if (!cachedElementDefaultRead) {
370
- cachedElementDefault = schema.getDefaultAtPath(scratch);
371
- cachedElementDefaultRead = true;
372
- }
373
- elemDefault = cachedElementDefault;
374
- }
375
- const consumerElem = i < consumer.length ? readConsumerIndex(consumer, i) : void 0;
376
- const merged = mergeStructuralImpl(schema, scratch, consumerElem, elemDefault);
377
- scratch.pop();
378
- if (merged !== consumerElem) {
379
- out[i] = merged;
380
- mutated = true;
381
- }
382
- }
383
- return mutated ? out : consumer;
384
- }
385
- function fillMissingKeysFromDefault(schema, scratch, consumer, defaultValue, out) {
386
- let mutated = false;
387
- for (const key of Object.keys(defaultValue)) {
388
- if (!safeOwnHas(consumer, key)) {
389
- const defAtKey = safeOwnRead(defaultValue, key);
390
- scratch.push(key);
391
- const filled = mergeStructuralImpl(schema, scratch, void 0, defAtKey);
392
- scratch.pop();
393
- if (filled !== void 0) {
394
- safeAssign(out, key, filled);
395
- mutated = true;
396
- }
397
- }
398
- }
399
- return mutated;
400
- }
401
- function recurseIntoConsumerKeys(schema, scratch, consumer, defaultValue, out) {
402
- let mutated = false;
403
- for (const key of consumerKeys(consumer)) {
404
- const cVal = safeOwnRead(consumer, key);
405
- if (cVal === void 0) continue;
406
- scratch.push(key);
407
- const merged = mergeStructuralImpl(schema, scratch, cVal, safeOwnRead(defaultValue, key));
408
- scratch.pop();
409
- if (merged !== cVal) {
410
- safeAssign(out, key, merged);
411
- mutated = true;
412
- }
413
- }
414
- return mutated;
415
- }
416
- function setAtPathWithSchemaFill(root, schema, fullPath, value) {
417
- if (fullPath.length === 0) return value;
418
- return setAtPathWithSchemaFillImpl(root, schema, fullPath, value, 0);
419
- }
420
- function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
421
- if (startIdx >= fullPath.length) return value;
422
- const head = fullPath[startIdx];
423
- const isLeafStep = startIdx === fullPath.length - 1;
424
- if (root instanceof Map) {
425
- const next = new Map(root);
426
- const key = mapKeyForSegment(root, head, schema.entryKeyKindAtPath(fullPath.slice(0, startIdx)));
427
- if (isLeafStep) {
428
- next.set(key, value);
429
- return next;
430
- }
431
- let childRoot2 = next.get(key);
432
- if (childRoot2 === void 0 || childRoot2 !== null && typeof childRoot2 !== "object") {
433
- childRoot2 = schema.getDefaultAtPath(fullPath.slice(0, startIdx + 1));
434
- }
435
- next.set(key, setAtPathWithSchemaFillImpl(childRoot2, schema, fullPath, value, startIdx + 1));
436
- return next;
437
- }
438
- if (!isRebuildableContainer(root, head)) return root;
439
- if (typeof head === "number") {
440
- const arr = Array.isArray(root) ? [...root] : [];
441
- const prefix = fullPath.slice(0, startIdx);
442
- if (arr.length < head) {
443
- const scratch = prefix.slice();
444
- const shape = schema.arrayShapeAtPath(scratch);
445
- const tupleLike = typeof shape === "number";
446
- let cachedArrayDefault;
447
- if (!tupleLike) {
448
- scratch.push(0);
449
- cachedArrayDefault = schema.getDefaultAtPath(scratch);
450
- scratch.pop();
451
- }
452
- while (arr.length < head) {
453
- const idx = arr.length;
454
- if (tupleLike) {
455
- scratch.push(idx);
456
- arr.push(schema.getDefaultAtPath(scratch));
457
- scratch.pop();
458
- } else {
459
- arr.push(cachedArrayDefault);
460
- }
461
- }
462
- }
463
- if (isLeafStep) {
464
- arr[head] = value;
465
- return arr;
466
- }
467
- let childRoot2 = arr[head];
468
- if (childRoot2 === void 0 || childRoot2 !== null && typeof childRoot2 !== "object") {
469
- childRoot2 = schema.getDefaultAtPath([...prefix, head]);
470
- }
471
- arr[head] = setAtPathWithSchemaFillImpl(childRoot2, schema, fullPath, value, startIdx + 1);
472
- return arr;
473
- }
474
- const rec = isPlainRecord(root) ? { ...root } : {};
475
- if (isLeafStep) {
476
- safeAssign(rec, head, value);
477
- return rec;
478
- }
479
- const existing = safeOwnRead(rec, head);
480
- let childRoot;
481
- if (existing === void 0 || existing !== null && typeof existing !== "object") {
482
- const intermPath = [...fullPath.slice(0, startIdx + 1)];
483
- const intermDefault = schema.getDefaultAtPath(intermPath);
484
- childRoot = intermDefault;
485
- } else {
486
- childRoot = existing;
487
- }
488
- safeAssign(
489
- rec,
490
- head,
491
- setAtPathWithSchemaFillImpl(childRoot, schema, fullPath, value, startIdx + 1)
492
- );
493
- return rec;
494
- }
495
-
496
107
  function isUnderStubAncestor(state, segments) {
497
108
  for (let i = 0; i < segments.length; i++) {
498
109
  const ancestorPath = segments.slice(0, i);
@@ -628,7 +239,6 @@ function visitActiveLeafPaths(value, base, visit) {
628
239
  function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
629
240
  const formValue = state.form.value;
630
241
  const value = getAtPath(formValue, segments);
631
- const original = state.originals.get(key)?.value;
632
242
  let pristine = true;
633
243
  let blank = true;
634
244
  let dirty = false;
@@ -646,6 +256,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
646
256
  let asyncPending = false;
647
257
  const submissionAttempts = state.submissionAttempts.value;
648
258
  const blurredLeafSegments = [];
259
+ const errorsByKey = errorBucketsAt(state, segments, key);
649
260
  const descendantLeaves = [];
650
261
  visitActiveLeafPaths(value, segments, (leafSegments) => {
651
262
  const { key: leafKey } = keyForSegments(leafSegments);
@@ -655,6 +266,8 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
655
266
  });
656
267
  for (const { key: leafKey, segments: leafSeg } of descendantLeaves) {
657
268
  const leafRecord = state.fields.get(leafKey);
269
+ const leafErrors = errorsByKey.get(leafKey);
270
+ if (leafErrors !== void 0) leafErrors.walked = true;
658
271
  if (!state.isPristineAtPathByKey(leafKey, leafSeg)) {
659
272
  pristine = false;
660
273
  dirty = true;
@@ -689,22 +302,28 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
689
302
  if (updatedAt === null || ts > updatedAt) updatedAt = ts;
690
303
  }
691
304
  }
692
- if (!dirty && state.hasStructuralChangeUnder(segments)) {
305
+ if (!dirty && !(Array.isArray(value) || isPlainRecord(value)) && !state.isPristineAtPathByKey(key, segments)) {
693
306
  pristine = false;
694
307
  dirty = true;
695
308
  }
696
- if (!dirty && state.hasRemovedSubtreeUnder(segments)) {
309
+ if (!dirty && state.hasStructuralChangeUnder(segments, key)) {
697
310
  pristine = false;
698
311
  dirty = true;
699
312
  }
700
- const errors = aggregateErrorsAt(state, segments, key);
701
- const revealedDescendantError = errors.length > 0 && errors.some((e) => {
702
- const ePath = e.path;
703
- if (ePath.length === segments.length && ePath.every((s, i) => s === segments[i])) return false;
704
- if (submissionAttempts > 0) return true;
705
- if (ePath.length === 1 && ePath[0] === "") return blurredAfterInteraction;
706
- return blurredLeafSegments.some((s) => isPathPrefix(ePath, s));
707
- });
313
+ if (!dirty && state.hasRemovedSubtreeUnder(segments, key)) {
314
+ pristine = false;
315
+ dirty = true;
316
+ }
317
+ const errorBuckets = sortErrorBuckets(errorsByKey);
318
+ const errors = flattenErrorBuckets(errorBuckets);
319
+ const revealedDescendantError = errors.length > 0 && descendantErrorRevealed(
320
+ state,
321
+ segments,
322
+ errorBuckets,
323
+ submissionAttempts,
324
+ blurredAfterInteraction,
325
+ blurredLeafSegments
326
+ );
708
327
  if (!asyncPending && state.pathHasAsyncValidationByKey(key, segments)) asyncPending = true;
709
328
  if ((state.fieldValidationCounts.get(key) ?? 0) > 0) {
710
329
  validating = true;
@@ -726,6 +345,8 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
726
345
  if (ownRecord.interacted === true) interacted = true;
727
346
  if (ownRecord.blurredAfterInteraction === true) blurredAfterInteraction = true;
728
347
  if (ownRecord.connected === true) connected = true;
348
+ const ts = ownRecord.updatedAt;
349
+ if (ts !== null && (updatedAt === null || ts > updatedAt)) updatedAt = ts;
729
350
  }
730
351
  const ownTransformError = state.transformErrors.get(key) ?? null;
731
352
  const gated = asyncPending && !state.firstValidationDone.value;
@@ -734,7 +355,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
734
355
  return {
735
356
  base: {
736
357
  value,
737
- original,
358
+ original: state.getOriginalAtPath(segments),
738
359
  pristine,
739
360
  dirty,
740
361
  focused,
@@ -817,9 +438,41 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
817
438
  firstOwnError
818
439
  };
819
440
  }
441
+ function descendantErrorRevealed(state, segments, buckets, submissionAttempts, blurredAfterInteraction, blurredLeafSegments) {
442
+ for (const bucket of buckets) {
443
+ let ownHistory;
444
+ for (const e of bucket.errors) {
445
+ const ePath = e.path;
446
+ if (pathsEqual(ePath, segments)) continue;
447
+ if (submissionAttempts > 0) return true;
448
+ if (ePath.length === 1 && ePath[0] === "") {
449
+ if (blurredAfterInteraction) return true;
450
+ continue;
451
+ }
452
+ for (const leaf of blurredLeafSegments) if (isPathPrefix(ePath, leaf)) return true;
453
+ if (bucket.walked) continue;
454
+ ownHistory ?? (ownHistory = state.fields.get(bucket.key)?.blurredAfterInteraction === true);
455
+ if (ownHistory) return true;
456
+ }
457
+ }
458
+ return false;
459
+ }
820
460
  function aggregateErrorsAt(state, prefix, prefixKey) {
461
+ return flattenErrorBuckets(sortErrorBuckets(errorBucketsAt(state, prefix, prefixKey)));
462
+ }
463
+ const NO_ERROR_BUCKETS = /* @__PURE__ */ new Map();
464
+ function sortErrorBuckets(buckets) {
465
+ if (buckets.size <= 1) return [...buckets.values()];
466
+ return [...buckets.values()].sort((a, b) => a.ordinal - b.ordinal);
467
+ }
468
+ function flattenErrorBuckets(buckets) {
469
+ if (buckets.length === 0) return [];
470
+ if (buckets.length === 1) return buckets[0]?.errors ?? [];
471
+ return buckets.flatMap((bucket) => bucket.errors);
472
+ }
473
+ function errorBucketsAt(state, prefix, prefixKey) {
821
474
  const candidates = state.errorWindowAt(prefix, prefixKey);
822
- if (candidates.length === 0) return [];
475
+ if (candidates.length === 0) return NO_ERROR_BUCKETS;
823
476
  const formValue = state.form.value;
824
477
  const cells = state.errorCells();
825
478
  const buckets = /* @__PURE__ */ new Map();
@@ -832,19 +485,16 @@ function aggregateErrorsAt(state, prefix, prefixKey) {
832
485
  const userList = cell?.user;
833
486
  const total = (schemaList?.length ?? 0) + (blankList?.length ?? 0) + (userList?.length ?? 0);
834
487
  if (total === 0) continue;
835
- const ordinal = state.ensurePathOrdinal(key);
836
- let bucket = buckets.get(ordinal);
488
+ let bucket = buckets.get(key);
837
489
  if (bucket === void 0) {
838
- bucket = [];
839
- buckets.set(ordinal, bucket);
490
+ bucket = { key, ordinal: state.ensurePathOrdinal(key), errors: [], walked: false };
491
+ buckets.set(key, bucket);
840
492
  }
841
- if (schemaList !== void 0) bucket.push(...schemaList);
842
- if (blankList !== void 0) bucket.push(...blankList);
843
- if (userList !== void 0) bucket.push(...userList);
493
+ if (schemaList !== void 0) bucket.errors.push(...schemaList);
494
+ if (blankList !== void 0) bucket.errors.push(...blankList);
495
+ if (userList !== void 0) bucket.errors.push(...userList);
844
496
  }
845
- if (buckets.size === 0) return [];
846
- if (buckets.size === 1) return [...buckets.values()][0];
847
- return [...buckets.entries()].sort(([a], [b]) => a - b).flatMap(([, errs]) => errs);
497
+ return buckets;
848
498
  }
849
499
  const EMPTY_ELEMENTS = Object.freeze([]);
850
500
 
@@ -876,7 +526,7 @@ function liveContainerHasKey(state, segments, key) {
876
526
  return Number.isInteger(index) && index >= 0 && String(index) === key && value.has(index);
877
527
  }
878
528
  if (value instanceof Set) return false;
879
- return Object.hasOwn(value, key);
529
+ return safeOwnHas(value, key);
880
530
  }
881
531
  function isArrayPath(state, segments) {
882
532
  if (segments.length === 0) return false;
@@ -1402,406 +1052,6 @@ function buildValuesSurface(form, onFormChange) {
1402
1052
  });
1403
1053
  }
1404
1054
 
1405
- function remapForOp(op, oldLen) {
1406
- const moved = /* @__PURE__ */ new Map();
1407
- const vacated = /* @__PURE__ */ new Set();
1408
- const fresh = /* @__PURE__ */ new Set();
1409
- let newLen = oldLen;
1410
- switch (op.kind) {
1411
- case "insert":
1412
- for (let i = op.index; i < oldLen; i++) moved.set(i, i + 1);
1413
- fresh.add(op.index);
1414
- newLen = oldLen + 1;
1415
- break;
1416
- case "remove":
1417
- vacated.add(op.index);
1418
- for (let i = op.index + 1; i < oldLen; i++) moved.set(i, i - 1);
1419
- newLen = Math.max(0, oldLen - 1);
1420
- break;
1421
- case "move":
1422
- if (op.from !== op.to) {
1423
- moved.set(op.from, op.to);
1424
- if (op.from < op.to) {
1425
- for (let i = op.from + 1; i <= op.to; i++) moved.set(i, i - 1);
1426
- } else {
1427
- for (let i = op.to; i < op.from; i++) moved.set(i, i + 1);
1428
- }
1429
- }
1430
- break;
1431
- case "swap":
1432
- if (op.a !== op.b) {
1433
- moved.set(op.a, op.b);
1434
- moved.set(op.b, op.a);
1435
- }
1436
- break;
1437
- case "replace-at":
1438
- vacated.add(op.index);
1439
- fresh.add(op.index);
1440
- break;
1441
- }
1442
- return { moved, vacated, fresh, oldLen, newLen };
1443
- }
1444
- function changedIndices(remap) {
1445
- const changed = new Set(remap.vacated);
1446
- for (const [from, to] of remap.moved) {
1447
- changed.add(from);
1448
- changed.add(to);
1449
- }
1450
- for (const index of remap.fresh) changed.add(index);
1451
- return changed;
1452
- }
1453
- function permuteList(list, remap, fill) {
1454
- const claimed = new Array(remap.newLen);
1455
- for (let i = 0; i < remap.oldLen; i++) {
1456
- if (remap.vacated.has(i)) continue;
1457
- const to = remap.moved.get(i) ?? i;
1458
- if (to < remap.newLen) claimed[to] = list[i];
1459
- }
1460
- const next = [];
1461
- for (let i = 0; i < remap.newLen; i++) {
1462
- next.push(claimed[i] ?? fill());
1463
- }
1464
- return next;
1465
- }
1466
- function elementIndexUnder(arrayPath, key, idxPos) {
1467
- const segments = segmentsForPathKey(key);
1468
- if (segments === null) return null;
1469
- if (!isPathPrefix(arrayPath, segments)) return null;
1470
- if (segments.length <= idxPos) return null;
1471
- const index = segments[idxPos];
1472
- return typeof index === "number" ? index : null;
1473
- }
1474
- function collectKeysAtIndices(keys, arrayPath, member) {
1475
- const idxPos = arrayPath.length;
1476
- const hits = [];
1477
- for (const key of keys) {
1478
- const index = elementIndexUnder(arrayPath, key, idxPos);
1479
- if (index === null || !member(index)) continue;
1480
- const segments = segmentsForPathKey(key);
1481
- if (segments === null) continue;
1482
- hits.push({ key, segments: [...segments], index });
1483
- }
1484
- return hits;
1485
- }
1486
- function remapAffects(remap) {
1487
- return (index) => remap.moved.has(index) || remap.vacated.has(index) || remap.fresh.has(index);
1488
- }
1489
- function migrateMapSubtree(map, arrayPath, remap, rewriteValue) {
1490
- const hits = collectKeysAtIndices(map.keys(), arrayPath, remapAffects(remap));
1491
- if (hits.length === 0) return;
1492
- const idxPos = arrayPath.length;
1493
- const snapshots = [];
1494
- for (const hit of hits) {
1495
- const value = map.get(hit.key);
1496
- if (value !== void 0) snapshots.push({ ...hit, value });
1497
- }
1498
- for (const snap of snapshots) map.delete(snap.key);
1499
- for (const snap of snapshots) {
1500
- const target = remap.moved.get(snap.index);
1501
- if (target === void 0) continue;
1502
- const relocated = snap.segments.slice();
1503
- relocated[idxPos] = target;
1504
- map.set(canonicalizePath(relocated).key, rewriteValue(snap.value, relocated));
1505
- }
1506
- }
1507
- function migrateSetSubtree(set, arrayPath, remap) {
1508
- const hits = collectKeysAtIndices(set, arrayPath, remapAffects(remap));
1509
- if (hits.length === 0) return;
1510
- const idxPos = arrayPath.length;
1511
- for (const hit of hits) set.delete(hit.key);
1512
- for (const hit of hits) {
1513
- const target = remap.moved.get(hit.index);
1514
- if (target === void 0) continue;
1515
- const relocated = hit.segments.slice();
1516
- relocated[idxPos] = target;
1517
- set.add(canonicalizePath(relocated).key);
1518
- }
1519
- }
1520
- function evictSchemaVerdictsAtIndices(errorCells, arrayPath, indices) {
1521
- for (const hit of collectKeysAtIndices(
1522
- [...errorCells.keys()],
1523
- arrayPath,
1524
- (index) => indices.has(index)
1525
- )) {
1526
- const cell = errorCells.get(hit.key);
1527
- if (cell === void 0) continue;
1528
- if (cell.user.length === 0) errorCells.delete(hit.key);
1529
- else if (cell.schema.length > 0) errorCells.set(hit.key, { schema: NO_ERRORS, user: cell.user });
1530
- }
1531
- }
1532
- function deleteKeysAtIndices(keys, del, arrayPath, indices) {
1533
- for (const hit of collectKeysAtIndices(keys, arrayPath, (index) => indices.has(index))) {
1534
- del(hit.key);
1535
- }
1536
- }
1537
- function createArrayIdentity(getArrayLength) {
1538
- const tokens = /* @__PURE__ */ new Map();
1539
- const baselines = /* @__PURE__ */ new Map();
1540
- let counter = 0;
1541
- const allocate = () => `k${(counter++).toString(36)}`;
1542
- function ensure(arrayKey, expectedLen) {
1543
- let ids = tokens.get(arrayKey);
1544
- const firstTrack = ids === void 0;
1545
- if (ids === void 0) {
1546
- ids = [];
1547
- tokens.set(arrayKey, ids);
1548
- }
1549
- while (ids.length < expectedLen) ids.push(allocate());
1550
- if (ids.length > expectedLen) ids.length = expectedLen;
1551
- if (firstTrack) baselines.set(arrayKey, [...ids]);
1552
- return ids;
1553
- }
1554
- function orderPristineForKey(arrayKey) {
1555
- const baseline = baselines.get(arrayKey);
1556
- const current = tokens.get(arrayKey);
1557
- if (baseline === void 0 || current === void 0) return true;
1558
- if (baseline.length !== current.length) return false;
1559
- for (let i = 0; i < current.length; i++) {
1560
- if (current[i] !== baseline[i]) return false;
1561
- }
1562
- return true;
1563
- }
1564
- return {
1565
- tokenAt(arraySegs, index) {
1566
- const len = getArrayLength(arraySegs);
1567
- if (index < 0 || index >= len) return "";
1568
- const ids = ensure(canonicalizePath(arraySegs).key, len);
1569
- return ids[index] ?? "";
1570
- },
1571
- applyOp(arraySegs, remap) {
1572
- const arrayKey = canonicalizePath(arraySegs).key;
1573
- const ids = ensure(arrayKey, remap.oldLen);
1574
- tokens.set(arrayKey, permuteList(ids, remap, allocate));
1575
- },
1576
- applyRemap(arrayPath, remap) {
1577
- if (remap.moved.size === 0 && remap.vacated.size === 0 && remap.fresh.size === 0) return;
1578
- migrateMapSubtree(tokens, arrayPath, remap, (value) => value);
1579
- migrateMapSubtree(baselines, arrayPath, remap, (value) => value);
1580
- },
1581
- realign(arraySegs) {
1582
- ensure(canonicalizePath(arraySegs).key, getArrayLength(arraySegs));
1583
- },
1584
- hasStructuralChangeUnder(prefix) {
1585
- for (const arrayKey of tokens.keys()) {
1586
- if (orderPristineForKey(arrayKey)) continue;
1587
- const segs = segmentsForPathKey(arrayKey);
1588
- if (segs === null) continue;
1589
- if (isPathPrefix(prefix, segs)) return true;
1590
- }
1591
- return false;
1592
- },
1593
- rebaselineAll() {
1594
- for (const arrayKey of [...tokens.keys()]) {
1595
- const segs = segmentsForPathKey(arrayKey);
1596
- if (segs === null) continue;
1597
- const ids = ensure(arrayKey, getArrayLength(segs));
1598
- baselines.set(arrayKey, [...ids]);
1599
- }
1600
- }
1601
- };
1602
- }
1603
- function createVariantMemory() {
1604
- const memory = /* @__PURE__ */ new Map();
1605
- return {
1606
- clear() {
1607
- memory.clear();
1608
- },
1609
- clearUnderPath(parentPath) {
1610
- for (const memKey of [...memory.keys()]) {
1611
- const segs = segmentsForPathKey(memKey);
1612
- if (segs === null) continue;
1613
- if (isPathPrefix(parentPath, segs)) memory.delete(memKey);
1614
- }
1615
- },
1616
- dropAtIndices(arrayPath, indices) {
1617
- deleteKeysAtIndices([...memory.keys()], (key) => memory.delete(key), arrayPath, indices);
1618
- },
1619
- recordOutgoing(unionKey, discValue, snapshot) {
1620
- let perUnion = memory.get(unionKey);
1621
- if (perUnion === void 0) {
1622
- perUnion = /* @__PURE__ */ new Map();
1623
- memory.set(unionKey, perUnion);
1624
- }
1625
- perUnion.set(discValue, snapshot);
1626
- },
1627
- lookupIncoming(unionKey, discValue) {
1628
- return memory.get(unionKey)?.get(discValue);
1629
- }
1630
- };
1631
- }
1632
- function cloneVariantSnapshot(value) {
1633
- if (value === null || typeof value !== "object") return value;
1634
- const raw = toRaw(value);
1635
- if (raw instanceof Date) return new Date(raw.getTime());
1636
- if (raw instanceof Map) {
1637
- const out2 = /* @__PURE__ */ new Map();
1638
- for (const [k, v] of raw.entries()) out2.set(cloneVariantSnapshot(k), cloneVariantSnapshot(v));
1639
- return out2;
1640
- }
1641
- if (raw instanceof Set) {
1642
- const out2 = /* @__PURE__ */ new Set();
1643
- for (const v of raw) out2.add(cloneVariantSnapshot(v));
1644
- return out2;
1645
- }
1646
- if (raw instanceof RegExp) return new RegExp(raw.source, raw.flags);
1647
- if (Array.isArray(raw)) {
1648
- const out2 = new Array(raw.length);
1649
- for (let i = 0; i < raw.length; i++) out2[i] = cloneVariantSnapshot(raw[i]);
1650
- return out2;
1651
- }
1652
- if (!isPlainRecord(raw)) return raw;
1653
- const src = raw;
1654
- const out = {};
1655
- for (const k of consumerKeys(src)) {
1656
- safeAssign(out, k, cloneVariantSnapshot(readConsumerProp(src, k)));
1657
- }
1658
- return out;
1659
- }
1660
- function createArrayBookkeeping(deps) {
1661
- const {
1662
- form,
1663
- fields,
1664
- errorCells,
1665
- originals,
1666
- blankPaths,
1667
- originalBlankPaths,
1668
- authoredPaths,
1669
- fieldValidationCounts,
1670
- fieldValidatingSince,
1671
- fieldValidationState,
1672
- activeValidations,
1673
- arrayIdentity,
1674
- variantMemory,
1675
- touchFieldRecord,
1676
- decFieldValidation
1677
- } = deps;
1678
- function migrateElementState(arrayPath, remap) {
1679
- if (remap.moved.size === 0 && remap.vacated.size === 0) return;
1680
- migrateMapSubtree(fields, arrayPath, remap, (record, segments) => ({
1681
- ...record,
1682
- path: segments
1683
- }));
1684
- migrateMapSubtree(errorCells(), arrayPath, remap, (cell, segments) => ({
1685
- schema: NO_ERRORS,
1686
- user: cell.user.map((error) => ({ ...error, path: [...segments] }))
1687
- }));
1688
- migrateMapSubtree(originals, arrayPath, remap, (record, segments) => ({
1689
- segments,
1690
- value: record.value
1691
- }));
1692
- migrateSetSubtree(blankPaths, arrayPath, remap);
1693
- migrateSetSubtree(originalBlankPaths, arrayPath, remap);
1694
- migrateSetSubtree(authoredPaths, arrayPath, remap);
1695
- migrateMapSubtree(fieldValidatingSince, arrayPath, remap, (since) => since);
1696
- migrateMapSubtree(fieldValidationCounts, arrayPath, remap, (count) => count);
1697
- arrayIdentity.applyRemap(arrayPath, remap);
1698
- }
1699
- function seedFreshElement(arrayPath, freshIndex) {
1700
- const elementPath = [...arrayPath, freshIndex];
1701
- const now = (/* @__PURE__ */ new Date()).toISOString();
1702
- diffAndApply(void 0, getAtPath(form.value, elementPath), elementPath, (patch) => {
1703
- if (patch.kind !== "added") return;
1704
- const { key } = canonicalizePath(patch.path);
1705
- if (!originals.has(key)) originals.set(key, { segments: patch.path, value: void 0 });
1706
- touchFieldRecord(key, patch.path, { updatedAt: now });
1707
- });
1708
- }
1709
- function abortValidationAtVacatedIndices(arrayPath, remap) {
1710
- if (remap.vacated.size === 0) return;
1711
- const hits = collectKeysAtIndices(
1712
- [...fieldValidationState.keys()],
1713
- arrayPath,
1714
- (index) => remap.vacated.has(index)
1715
- );
1716
- for (const hit of hits) {
1717
- const entry = fieldValidationState.get(hit.key);
1718
- if (entry === void 0) continue;
1719
- if (entry.timer !== null) {
1720
- clearTimeout(entry.timer);
1721
- } else if (!entry.settled) {
1722
- activeValidations.value = Math.max(0, activeValidations.value - 1);
1723
- decFieldValidation(hit.key);
1724
- }
1725
- entry.aborted = true;
1726
- fieldValidationState.delete(hit.key);
1727
- }
1728
- }
1729
- return {
1730
- applyStructuralOp(arrayPath, remap) {
1731
- migrateElementState(arrayPath, remap);
1732
- for (const freshIndex of remap.fresh) seedFreshElement(arrayPath, freshIndex);
1733
- const changed = changedIndices(remap);
1734
- evictSchemaVerdictsAtIndices(errorCells(), arrayPath, changed);
1735
- abortValidationAtVacatedIndices(arrayPath, remap);
1736
- variantMemory.dropAtIndices(arrayPath, changed);
1737
- arrayIdentity.applyOp(arrayPath, remap);
1738
- }
1739
- };
1740
- }
1741
- function buildFieldArrayApi(state) {
1742
- function readArray(path) {
1743
- const segments = canonicalizePath(path).segments;
1744
- const current = state.getValueAtPath(segments);
1745
- return Array.isArray(current) ? current.slice() : [];
1746
- }
1747
- function writeArray(path, next, arrayOp) {
1748
- const { segments } = canonicalizePath(path);
1749
- const meta = {
1750
- ...arrayOp !== void 0 ? { arrayOp } : {}
1751
- };
1752
- return state.setValueAtPath(segments, next, meta);
1753
- }
1754
- return {
1755
- append(path, value) {
1756
- const next = readArray(path);
1757
- next.push(value);
1758
- return writeArray(path, next, { kind: "insert", index: next.length - 1 });
1759
- },
1760
- prepend(path, value) {
1761
- const next = readArray(path);
1762
- next.unshift(value);
1763
- return writeArray(path, next, { kind: "insert", index: 0 });
1764
- },
1765
- insert(path, index, value) {
1766
- const next = readArray(path);
1767
- const preLen = next.length;
1768
- const insertIndex = index < 0 ? Math.max(0, preLen + index) : Math.min(index, preLen);
1769
- next.splice(insertIndex, 0, value);
1770
- return writeArray(path, next, { kind: "insert", index: insertIndex });
1771
- },
1772
- remove(path, index) {
1773
- const next = readArray(path);
1774
- if (index < 0 || index >= next.length) return false;
1775
- next.splice(index, 1);
1776
- return writeArray(path, next, { kind: "remove", index });
1777
- },
1778
- swap(path, a, b) {
1779
- const next = readArray(path);
1780
- if (a < 0 || a >= next.length) return false;
1781
- if (b < 0 || b >= next.length) return false;
1782
- if (a === b) return false;
1783
- const tmp = next[a];
1784
- next[a] = next[b];
1785
- next[b] = tmp;
1786
- return writeArray(path, next, { kind: "swap", a, b });
1787
- },
1788
- move(path, from, to) {
1789
- const next = readArray(path);
1790
- if (from < 0 || from >= next.length) return false;
1791
- const [item] = next.splice(from, 1);
1792
- const clampedTo = Math.max(0, Math.min(to, next.length));
1793
- next.splice(clampedTo, 0, item);
1794
- return writeArray(path, next, { kind: "move", from, to: clampedTo });
1795
- },
1796
- replace(path, index, value) {
1797
- const next = readArray(path);
1798
- if (index < 0 || index >= next.length) return false;
1799
- next[index] = value;
1800
- return writeArray(path, next, { kind: "replace-at", index });
1801
- }
1802
- };
1803
- }
1804
-
1805
1055
  function isErrorInputLike(value) {
1806
1056
  if (value instanceof Error) return true;
1807
1057
  if (typeof value !== "object" || value === null) return false;
@@ -2461,12 +1711,12 @@ function isPrimitiveOrEmpty(value) {
2461
1711
  return t === "string" || t === "number" || t === "boolean" || t === "bigint";
2462
1712
  }
2463
1713
  function isSlimNumericPrimitive(value) {
2464
- return value === 0 || value === 0n;
1714
+ return value === 0 || typeof value === "bigint" && !value;
2465
1715
  }
2466
1716
  function blankForKind(slimDefault) {
2467
1717
  if (typeof slimDefault === "string") return "";
2468
1718
  if (typeof slimDefault === "number") return 0;
2469
- if (typeof slimDefault === "bigint") return 0n;
1719
+ if (typeof slimDefault === "bigint") return typeof BigInt === "function" ? BigInt(0) : void 0;
2470
1720
  if (typeof slimDefault === "boolean") return false;
2471
1721
  if (slimDefault === null) return null;
2472
1722
  return void 0;
@@ -2835,7 +2085,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2835
2085
  };
2836
2086
  const metaTarget = buildMetaProxy(META_KEYS, (key) => {
2837
2087
  if (key === "errorCount") return metaErrors.value.length;
2838
- if (Object.hasOwn(metaOwn, key)) return metaOwn[key];
2088
+ if (safeOwnHas(metaOwn, key)) return metaOwn[key];
2839
2089
  return rootFieldState.value[key];
2840
2090
  });
2841
2091
  const formMeta = readonly(reactive(metaTarget));
@@ -2850,8 +2100,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
2850
2100
  );
2851
2101
  state.reset(walked.cleanedValues);
2852
2102
  for (const pathKey of walked.paths) {
2853
- state.blankPaths.add(pathKey);
2854
2103
  state.originalBlankPaths.add(pathKey);
2104
+ state.blankPaths.add(pathKey);
2855
2105
  }
2856
2106
  }
2857
2107
  };
@@ -3260,7 +2510,7 @@ function mergeObjectKeys(target, source, path, schema) {
3260
2510
 
3261
2511
  async function runFactoryAndApply(st, factory) {
3262
2512
  try {
3263
- const value = await factory();
2513
+ const value = structuralSnapshot(await factory());
3264
2514
  if (st.hasPreprocessOrCoerce) walkAuthoredFromConstraints(value, [], st.authoredPaths);
3265
2515
  const full = mergeSparseHydration(
3266
2516
  toRaw(st.form.value),
@@ -3304,16 +2554,15 @@ function appendHydrationFailedEntry(st, error) {
3304
2554
  return entry;
3305
2555
  }
3306
2556
 
3307
- const isContainer = (value) => Array.isArray(value) || isPlainRecord(value);
3308
2557
  function isHydratedFieldRecord(value) {
3309
2558
  if (typeof value !== "object" || value === null) return false;
3310
2559
  const r = value;
3311
2560
  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";
3312
2561
  }
3313
- function withClearedHistoryFlags(record, now) {
2562
+ function withClearedHistoryFlags(record, updatedAt) {
3314
2563
  return {
3315
2564
  path: record.path,
3316
- updatedAt: now,
2565
+ updatedAt,
3317
2566
  connected: record.connected,
3318
2567
  focused: record.focused,
3319
2568
  blurred: record.blurred,
@@ -3371,7 +2620,7 @@ function walkDuStubs(schema, value, path, warned) {
3371
2620
  function stripSymbolsDeep(value) {
3372
2621
  if (value === null || typeof value !== "object") return value;
3373
2622
  if (Array.isArray(value)) {
3374
- let mutated2 = false;
2623
+ let mutated2 = !consumerExtensible(value);
3375
2624
  const out2 = new Array(value.length);
3376
2625
  for (let i = 0; i < value.length; i++) {
3377
2626
  const original = readConsumerIndex(value, i);
@@ -3385,7 +2634,7 @@ function stripSymbolsDeep(value) {
3385
2634
  if (proto !== Object.prototype && proto !== null) return value;
3386
2635
  const symKeys = consumerSymbolKeys(value);
3387
2636
  const stringKeys = consumerKeys(value);
3388
- let mutated = symKeys.length > 0;
2637
+ let mutated = symKeys.length > 0 || !consumerExtensible(value);
3389
2638
  const out = {};
3390
2639
  const src = value;
3391
2640
  for (const k of stringKeys) {
@@ -3428,14 +2677,27 @@ function initialFirstValidationGate(schema) {
3428
2677
  return schema.needsAsyncValidation?.() !== true;
3429
2678
  }
3430
2679
  function seedOriginalsFromBaseline(st, baseline) {
3431
- st.originals.clear();
2680
+ const seeded = /* @__PURE__ */ new Map();
3432
2681
  diffAndApply({}, baseline, [], (patch) => {
3433
- if (patch.kind !== "added") return;
3434
- st.originals.set(canonicalizePath(patch.path).key, {
3435
- segments: patch.path,
3436
- value: patch.newValue
3437
- });
2682
+ if (patch.kind === "added") seedBaseline(seeded, canonicalizePath(patch.path).key, patch);
3438
2683
  });
2684
+ replaceOriginals(st, seeded);
2685
+ }
2686
+ function replaceOriginals(st, next) {
2687
+ for (const [key, entry] of next) st.originals.set(key, entry);
2688
+ for (const key of [...toRaw(st.originals).keys()]) {
2689
+ if (!next.has(key)) st.originals.delete(key);
2690
+ }
2691
+ }
2692
+ function seedBaseline(originals, key, patch) {
2693
+ const value = patch.newValue;
2694
+ if (!isDescendable(value)) {
2695
+ originals.set(key, { segments: patch.path, value });
2696
+ return false;
2697
+ }
2698
+ if (!(value instanceof Map))
2699
+ originals.set(key, { segments: patch.path, value: Array.isArray(value) ? [] : {} });
2700
+ return true;
3439
2701
  }
3440
2702
  function queueInitialAsyncValidation(st) {
3441
2703
  if (!st.ssr && st.schema.needsAsyncValidation?.() === true) {
@@ -3597,8 +2859,18 @@ function settleTransforms(st, path) {
3597
2859
  st.transformWaiters.push({ key, resolve });
3598
2860
  });
3599
2861
  }
2862
+ function writeInstant(st) {
2863
+ const clock = (/* @__PURE__ */ new Date()).toISOString();
2864
+ if (clock > st.latestStamp) return clock;
2865
+ const after = new Date(new Date(st.latestStamp).getTime() + 1);
2866
+ return Number.isFinite(after.getTime()) ? after.toISOString() : clock;
2867
+ }
2868
+ function noteStamp(st, stamp) {
2869
+ if (stamp !== null && stamp > st.latestStamp) st.latestStamp = stamp;
2870
+ }
3600
2871
  function touchFieldRecord(st, pathKey, path, patch) {
3601
2872
  const current = st.fields.get(pathKey);
2873
+ if (patch.updatedAt !== void 0) noteStamp(st, patch.updatedAt);
3602
2874
  st.fields.set(pathKey, {
3603
2875
  path,
3604
2876
  updatedAt: patch.updatedAt ?? current?.updatedAt ?? null,
@@ -3617,19 +2889,189 @@ function touchFieldRecord(st, pathKey, path, patch) {
3617
2889
  blurredAfterInteraction: patch.blurredAfterInteraction ?? current?.blurredAfterInteraction ?? false
3618
2890
  });
3619
2891
  }
3620
- function commitWritePatches(st, patches, meta) {
3621
- const now = (/* @__PURE__ */ new Date()).toISOString();
2892
+ function stampContainers(st, containers, now) {
2893
+ eachAncestorOnce(containers, (key, segments) => raiseStamp(st, key, segments, now));
2894
+ }
2895
+ function raiseStamp(st, key, segments, stamp) {
2896
+ if ((st.fields.get(key)?.updatedAt ?? "") < stamp) {
2897
+ touchFieldRecord(st, key, segments, { updatedAt: stamp });
2898
+ }
2899
+ }
2900
+ function holdRemovedStamps(st, patches, next) {
2901
+ const held = /* @__PURE__ */ new Map();
2902
+ const hold = (path, depth, leaf) => noteHold(st, held, next, path, depth, leaf);
3622
2903
  for (const patch of patches) {
3623
- const { key } = canonicalizePath(patch.path);
3624
- if (!st.originals.has(key)) {
2904
+ if (patch.kind === "added") continue;
2905
+ const { oldValue, path } = patch;
2906
+ if (Array.isArray(oldValue) || isPlainRecord(oldValue)) {
2907
+ if (patch.kind === "changed") {
2908
+ visitActiveLeafPaths(oldValue, path, (leaf) => hold(path, path.length, leaf));
2909
+ }
2910
+ } else if (patch.kind === "removed" ? !hasAtPath(next, path) : Array.isArray(patch.newValue) || isPlainRecord(patch.newValue)) {
2911
+ hold(path, path.length - 1, path);
2912
+ }
2913
+ }
2914
+ applyHolds(st, held);
2915
+ }
2916
+ function noteHold(st, held, next, path, depth, leaf) {
2917
+ const { key: leafKey } = keyForSegments(leaf);
2918
+ const stamp = toRaw(st.originals).has(leafKey) ? toRaw(st.fields).get(leafKey)?.updatedAt : null;
2919
+ if (stamp === null || stamp === void 0) return;
2920
+ let kept = false;
2921
+ for (let at = depth; at >= 0; at--) {
2922
+ const segments = path.slice(0, at);
2923
+ if (!kept && !hasAtPath(next, segments)) continue;
2924
+ kept = true;
2925
+ const { key } = keyForSegments(segments);
2926
+ const current = held.get(key);
2927
+ if (current === void 0) held.set(key, { segments, stamp });
2928
+ else if (stamp > current.stamp) current.stamp = stamp;
2929
+ }
2930
+ }
2931
+ function applyHolds(st, held) {
2932
+ for (const [key, { segments, stamp }] of held) raiseStamp(st, key, segments, stamp);
2933
+ }
2934
+ function stampBareContainers(st, patches, bare, now) {
2935
+ for (const index of bare) {
2936
+ const path = patches[index]?.path ?? [];
2937
+ for (let depth = path.length; depth >= 0; depth--) {
2938
+ const container = path.slice(0, depth);
2939
+ if (depth < path.length && reachedBesides(patches, index, container)) break;
2940
+ const { key, segments } = keyForSegments(container);
2941
+ raiseStamp(st, key, segments, now);
2942
+ }
2943
+ }
2944
+ }
2945
+ function reachedBesides(patches, index, container) {
2946
+ for (const step of [-1, 1]) {
2947
+ for (let i = index + step; ; i += step) {
2948
+ const patch = patches[i];
2949
+ if (patch === void 0 || !isPathPrefix(container, patch.path)) break;
2950
+ if (!isBarePatch(patch)) return true;
2951
+ }
2952
+ }
2953
+ return false;
2954
+ }
2955
+ function eachAncestorOnce(paths, visit) {
2956
+ const seen = /* @__PURE__ */ new Set();
2957
+ for (const path of paths) {
2958
+ for (let depth = path.length; depth >= 0; depth--) {
2959
+ const { key, segments } = keyForSegments(path.slice(0, depth));
2960
+ if (seen.has(key)) break;
2961
+ seen.add(key);
2962
+ visit(key, segments);
2963
+ }
2964
+ }
2965
+ }
2966
+ function foldInteractionHistory(st, path, released) {
2967
+ let touched = false;
2968
+ let interacted = false;
2969
+ let blurredAfterInteraction = false;
2970
+ for (const record of released) {
2971
+ touched || (touched = record.touched);
2972
+ interacted || (interacted = record.interacted);
2973
+ blurredAfterInteraction || (blurredAfterInteraction = record.blurredAfterInteraction);
2974
+ }
2975
+ if (!touched && !interacted && !blurredAfterInteraction) return;
2976
+ st.noteReleased?.(released);
2977
+ for (let depth = path.length; depth >= 0; depth--) {
2978
+ const { key, segments } = keyForSegments(path.slice(0, depth));
2979
+ const current = st.fields.get(key);
2980
+ touchFieldRecord(st, key, segments, {
2981
+ touched: touched || current?.touched === true,
2982
+ interacted: interacted || current?.interacted === true,
2983
+ blurredAfterInteraction: blurredAfterInteraction || current?.blurredAfterInteraction === true
2984
+ });
2985
+ }
2986
+ }
2987
+ function releaseRemovedRecords(st, segments, raw) {
2988
+ const released = [];
2989
+ let depth = segments.length;
2990
+ do {
2991
+ const { key } = keyForSegments(segments.slice(0, depth));
2992
+ abortValidationAt(st, key);
2993
+ const record = st.fields.get(key);
2994
+ if (record !== void 0) {
2995
+ released.push(record);
2996
+ if (record.connected) st.fields.set(key, withClearedHistoryFlags(record, null));
2997
+ else st.fields.delete(key);
2998
+ }
2999
+ depth--;
3000
+ } while (depth > 0 && !hasAtPath(raw, segments.slice(0, depth)));
3001
+ const present = segments.slice(0, depth);
3002
+ foldInteractionHistory(st, present, released);
3003
+ return present;
3004
+ }
3005
+ function noteRemovedBaseline(st, key, segments, notify) {
3006
+ if (st.originals.get(key)?.value === void 0) return;
3007
+ st.removedPaths.set(key, segments);
3008
+ notify.push(segments);
3009
+ }
3010
+ function trackForSweep(st, path, key) {
3011
+ st.pathSweep.track(path, key);
3012
+ for (let i = 1; i < path.length; i++) {
3013
+ const ancestor = path.slice(0, i);
3014
+ st.pathSweep.track(ancestor, canonicalizePath(ancestor).key);
3015
+ }
3016
+ }
3017
+ function commitWritePatches(st, patches, meta, structuralOp, now) {
3018
+ const arrayOpPath = structuralOp?.path;
3019
+ const reshaped = [];
3020
+ const removed = [];
3021
+ const resizing = [];
3022
+ const raw = toRaw(st.form.value);
3023
+ const bareAt = [];
3024
+ for (const [index, patch] of patches.entries()) {
3025
+ const { key, segments } = canonicalizePath(patch.path);
3026
+ const bare = isBarePatch(patch);
3027
+ if (!bare && !st.originals.has(key)) {
3625
3028
  st.originals.set(key, { segments: patch.path, value: void 0 });
3626
3029
  }
3627
- touchFieldRecord(st, key, patch.path, { updatedAt: now });
3628
- st.pathSweep.track(patch.path, key);
3629
- for (let i = 1; i < patch.path.length; i++) {
3630
- const ancestor = patch.path.slice(0, i);
3631
- st.pathSweep.track(ancestor, canonicalizePath(ancestor).key);
3030
+ const positional = arrayOpPath !== void 0 && segments.length > arrayOpPath.length && isPathPrefix(arrayOpPath, segments);
3031
+ if (structuralOp === null && (patch.kind !== "changed" || typeof patch.oldValue === "object" || typeof patch.newValue === "object")) {
3032
+ resizing.push(segments);
3632
3033
  }
3034
+ if (!positional) {
3035
+ if (patch.kind === "removed" && !hasAtPath(raw, segments)) {
3036
+ reshaped.push(releaseRemovedRecords(st, segments, raw));
3037
+ noteRemovedBaseline(st, key, segments, removed);
3038
+ } else {
3039
+ touchFieldRecord(st, key, segments, { updatedAt: now });
3040
+ if (bare) bareAt.push(index);
3041
+ if (patch.kind === "changed" && typeof patch.oldValue === "object" && patch.oldValue !== null) {
3042
+ diffAndApply(patch.oldValue, void 0, segments, (gone) => {
3043
+ if (!hasAtPath(raw, gone.path)) {
3044
+ reshaped.push(releaseRemovedRecords(st, gone.path, raw));
3045
+ const goneAt = canonicalizePath(gone.path);
3046
+ noteRemovedBaseline(st, goneAt.key, goneAt.segments, removed);
3047
+ }
3048
+ });
3049
+ }
3050
+ if (patch.kind === "changed" && isDescendable(patch.newValue)) {
3051
+ diffAndApply(void 0, patch.newValue, segments, (fresh) => {
3052
+ const at = canonicalizePath(fresh.path);
3053
+ trackForSweep(st, fresh.path, at.key);
3054
+ if (fresh.kind === "added" && isDescendable(fresh.newValue)) {
3055
+ reshaped.push(at.segments);
3056
+ return;
3057
+ }
3058
+ if (!st.originals.has(at.key)) {
3059
+ st.originals.set(at.key, { segments: fresh.path, value: void 0 });
3060
+ }
3061
+ touchFieldRecord(st, at.key, at.segments, { updatedAt: now });
3062
+ });
3063
+ }
3064
+ }
3065
+ }
3066
+ trackForSweep(st, patch.path, key);
3067
+ }
3068
+ if (arrayOpPath !== void 0 && patches.length > 0) reshaped.push(arrayOpPath);
3069
+ if (reshaped.length > 0) stampContainers(st, reshaped, now);
3070
+ if (bareAt.length > 0) stampBareContainers(st, patches, bareAt, now);
3071
+ if (removed.length > 0) pulseStructure(st.structureSignal, removed);
3072
+ if (resizing.length > 0) st.arrayIdentity.realignContaining(resizing);
3073
+ if (structuralOp !== null) {
3074
+ st.arrayBookkeeping.applyStructuralOp(structuralOp.path, structuralOp.remap, now);
3633
3075
  }
3634
3076
  for (const listener of st.formChangeListeners) {
3635
3077
  try {
@@ -3639,33 +3081,39 @@ function commitWritePatches(st, patches, meta) {
3639
3081
  }
3640
3082
  }
3641
3083
  }
3642
- function applyFormReplacementWithPath(st, next, meta, arrayOpPath) {
3084
+ function applyFormReplacementWithPath(st, next, meta, structuralOp, now) {
3643
3085
  const prev = st.form.value;
3644
3086
  if (Object.is(prev, next)) return;
3645
3087
  const patches = [];
3646
3088
  diffAndApply(prev, next, [], (patch) => {
3647
3089
  patches.push(patch);
3648
3090
  });
3649
- if (!applyChangedKeys(prev, next, arrayOpPath, [], patches)) {
3091
+ holdRemovedStamps(st, patches, next);
3092
+ const reconciled = callConsumerFn(
3093
+ () => applyChangedKeys(prev, next, structuralOp?.path ?? null, [], patches),
3094
+ null);
3095
+ if (reconciled === null) return;
3096
+ if (!reconciled) {
3650
3097
  st.form.value = next;
3651
3098
  } else if (patches.some(
3652
3099
  (p) => p.path.length > 0 && typeof p.path[0] === "string" && isShadowedKey(p.path[0])
3653
3100
  )) {
3654
3101
  triggerRef(st.form);
3655
3102
  }
3656
- commitWritePatches(st, patches, meta);
3103
+ commitWritePatches(st, patches, meta, structuralOp, now);
3657
3104
  }
3658
3105
  function applyFormReplacement(st, next, meta) {
3659
- applyFormReplacementWithPath(st, next, meta, null);
3106
+ applyFormReplacementWithPath(st, next, meta, null, writeInstant(st));
3660
3107
  }
3661
- function applyTargetedWrite(st, path, completedValue, meta) {
3108
+ function applyTargetedWrite(st, path, completedValue, meta, structuralOp, now) {
3662
3109
  const result = tryInPlaceLeafWrite(st.form.value, path, completedValue);
3663
3110
  if (!result.applied) {
3664
3111
  applyFormReplacementWithPath(
3665
3112
  st,
3666
3113
  setAtPathWithSchemaFill(st.form.value, st.schema, path, completedValue),
3667
3114
  meta,
3668
- meta?.arrayOp !== void 0 ? path : null
3115
+ structuralOp,
3116
+ now
3669
3117
  );
3670
3118
  return;
3671
3119
  }
@@ -3673,9 +3121,9 @@ function applyTargetedWrite(st, path, completedValue, meta) {
3673
3121
  diffAndApply(result.old, completedValue, path, (patch) => {
3674
3122
  patches.push(patch);
3675
3123
  });
3676
- commitWritePatches(st, patches, meta);
3124
+ commitWritePatches(st, patches, meta, null, now);
3677
3125
  }
3678
- function setValueAtPath(st, path, value, meta) {
3126
+ function setValueAtPath(st, path, value, meta, writeStamp) {
3679
3127
  if (st.effectiveDisabled.value) {
3680
3128
  return false;
3681
3129
  }
@@ -3748,7 +3196,8 @@ function setValueAtPath(st, path, value, meta) {
3748
3196
  value,
3749
3197
  variantDefault,
3750
3198
  void 0,
3751
- meta
3199
+ meta,
3200
+ writeStamp
3752
3201
  );
3753
3202
  }
3754
3203
  return reshapeUnionVariant(
@@ -3758,7 +3207,8 @@ function setValueAtPath(st, path, value, meta) {
3758
3207
  value,
3759
3208
  { [last]: value },
3760
3209
  void 0,
3761
- meta
3210
+ meta,
3211
+ writeStamp
3762
3212
  );
3763
3213
  }
3764
3214
  }
@@ -3782,7 +3232,8 @@ function setValueAtPath(st, path, value, meta) {
3782
3232
  discValue,
3783
3233
  variantDefault,
3784
3234
  valueRecord,
3785
- meta
3235
+ meta,
3236
+ writeStamp
3786
3237
  );
3787
3238
  }
3788
3239
  return reshapeUnionVariant(
@@ -3792,10 +3243,20 @@ function setValueAtPath(st, path, value, meta) {
3792
3243
  discValue,
3793
3244
  { [discKey]: discValue },
3794
3245
  void 0,
3795
- meta
3246
+ meta,
3247
+ writeStamp
3796
3248
  );
3797
3249
  }
3798
- return reshapeUnionVariant(st, path, oldDiscValue, void 0, {}, void 0, meta);
3250
+ return reshapeUnionVariant(
3251
+ st,
3252
+ path,
3253
+ oldDiscValue,
3254
+ void 0,
3255
+ {},
3256
+ void 0,
3257
+ meta,
3258
+ writeStamp
3259
+ );
3799
3260
  }
3800
3261
  }
3801
3262
  }
@@ -3845,16 +3306,17 @@ function setValueAtPath(st, path, value, meta) {
3845
3306
  if (arrayOpRemap === null && Array.isArray(value) && Array.isArray(currentValue)) {
3846
3307
  st.arrayIdentity.realign(path);
3847
3308
  }
3848
- applyTargetedWrite(st, path, completedValue, meta);
3849
- if (arrayOpRemap !== null) {
3850
- st.arrayBookkeeping.applyStructuralOp(path, arrayOpRemap);
3851
- } else if (Array.isArray(value) && Array.isArray(currentValue)) {
3309
+ const now = writeStamp ?? writeInstant(st);
3310
+ applyTargetedWrite(
3311
+ st,
3312
+ path,
3313
+ completedValue,
3314
+ meta,
3315
+ arrayOpRemap !== null ? { path, remap: arrayOpRemap } : null,
3316
+ now
3317
+ );
3318
+ if (arrayOpRemap === null && Array.isArray(value) && Array.isArray(currentValue)) {
3852
3319
  st.variantMemory.clearUnderPath(path);
3853
- st.arrayIdentity.realign(path);
3854
- } else if (isContainer(currentValue) && !isContainer(value)) {
3855
- if (subtreeHadRealBaseline(st, path, currentValue)) {
3856
- st.removedSubtrees.add(pathKey);
3857
- }
3858
3320
  }
3859
3321
  const effectiveModeAfterWrite = meta?.instance?.validateOn ?? st.fieldValidationMode;
3860
3322
  if (effectiveModeAfterWrite === "change") {
@@ -3862,7 +3324,7 @@ function setValueAtPath(st, path, value, meta) {
3862
3324
  }
3863
3325
  return true;
3864
3326
  }
3865
- function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variantDefault, consumerOverrides, meta) {
3327
+ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variantDefault, consumerOverrides, meta, writeStamp) {
3866
3328
  const sameDisc = Object.is(oldDiscValue, newDiscValue);
3867
3329
  const parentKey = canonicalizePath(parentPath).key;
3868
3330
  let baseline = variantDefault;
@@ -3928,7 +3390,7 @@ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variant
3928
3390
  appliedSync = true;
3929
3391
  }
3930
3392
  }
3931
- applyFormReplacement(st, nextForm, meta);
3393
+ applyFormReplacementWithPath(st, nextForm, meta, null, writeStamp ?? writeInstant(st));
3932
3394
  for (const k of newBlankPaths) st.blankPaths.add(k);
3933
3395
  if (reshapeMode === "change" && !appliedSync) {
3934
3396
  scheduleFieldValidation(st, parentPath, false, meta?.instance);
@@ -3949,7 +3411,9 @@ function scheduleFieldValidation(st, path, immediate, instance) {
3949
3411
  aborted: false,
3950
3412
  timer: null,
3951
3413
  settled: false,
3952
- released: false
3414
+ released: false,
3415
+ key,
3416
+ path
3953
3417
  };
3954
3418
  st.fieldValidationState.set(key, fresh);
3955
3419
  const myEpoch = ++st.scheduleEpoch;
@@ -3960,19 +3424,20 @@ function scheduleFieldValidation(st, path, immediate, instance) {
3960
3424
  try {
3961
3425
  st.activeValidations.value += 1;
3962
3426
  activeIncremented = true;
3963
- incFieldValidation(st, key);
3427
+ incFieldValidation(st, fresh.key);
3964
3428
  } catch (err) {
3965
3429
  if (activeIncremented) {
3966
3430
  st.activeValidations.value = Math.max(0, st.activeValidations.value - 1);
3967
3431
  }
3968
3432
  throw err;
3969
3433
  }
3970
- const subtreeScope = path.length > 0 && st.schema.hasContainerOrRootRefine?.() === false;
3971
- const scopePath = subtreeScope ? path : void 0;
3972
- const dataAtScope = subtreeScope ? getAtPath(st.form.value, path) : st.form.value;
3973
- const scopeKey = subtreeScope ? canonicalizePath(path).key : ROOT_PATH_KEY;
3974
- void Promise.resolve().then(() => st.schema.validateAtPath(dataAtScope, scopePath)).then((response) => {
3434
+ const startPath = fresh.path;
3435
+ const subtreeScope = startPath.length > 0 && st.schema.hasContainerOrRootRefine?.() === false;
3436
+ const dataAtScope = subtreeScope ? getAtPath(st.form.value, startPath) : st.form.value;
3437
+ void Promise.resolve().then(() => st.schema.validateAtPath(dataAtScope, subtreeScope ? startPath : void 0)).then((response) => {
3975
3438
  if (fresh.aborted) return;
3439
+ const scopePath = subtreeScope ? fresh.path : void 0;
3440
+ const scopeKey = subtreeScope ? fresh.key : ROOT_PATH_KEY;
3976
3441
  if (myEpoch <= st.lastCommittedEpoch) return;
3977
3442
  st.lastCommittedEpoch = myEpoch;
3978
3443
  if (effectiveMode === "blur") {
@@ -3983,14 +3448,14 @@ function scheduleFieldValidation(st, path, immediate, instance) {
3983
3448
  const filtered = filterAuthoredErrors(st, errors);
3984
3449
  const restamped = subtreeScope ? filtered.map((err) => ({
3985
3450
  ...err,
3986
- path: [...path, ...err.path]
3451
+ path: [...scopePath ?? [], ...err.path]
3987
3452
  })) : filtered;
3988
3453
  applySchemaErrorsForSubtree(st, scopePath ?? [], restamped);
3989
3454
  }).catch(() => {
3990
3455
  }).finally(() => {
3991
3456
  if (!fresh.released) {
3992
3457
  st.activeValidations.value = Math.max(0, st.activeValidations.value - 1);
3993
- decFieldValidation(st, key);
3458
+ decFieldValidation(st, fresh.key);
3994
3459
  }
3995
3460
  fresh.settled = true;
3996
3461
  });
@@ -4029,6 +3494,19 @@ function cancelFieldValidationUnder(st, prefix) {
4029
3494
  st.fieldValidationState.delete(key);
4030
3495
  }
4031
3496
  }
3497
+ function abortValidationAt(st, key) {
3498
+ const entry = st.fieldValidationState.get(key);
3499
+ if (entry === void 0) return;
3500
+ if (entry.timer !== null) {
3501
+ clearTimeout(entry.timer);
3502
+ } else if (!entry.settled && !entry.released) {
3503
+ st.activeValidations.value = Math.max(0, st.activeValidations.value - 1);
3504
+ decFieldValidation(st, key);
3505
+ entry.released = true;
3506
+ }
3507
+ entry.aborted = true;
3508
+ st.fieldValidationState.delete(key);
3509
+ }
4032
3510
  function onFormChange(st, listener) {
4033
3511
  st.formChangeListeners.add(listener);
4034
3512
  return () => {
@@ -4079,6 +3557,34 @@ function dispose(st) {
4079
3557
  function getValueAtPath(st, path) {
4080
3558
  return getAtPath(st.form.value, path);
4081
3559
  }
3560
+ function freezeBaseline(value) {
3561
+ if (Object.isFrozen(value)) return value;
3562
+ if (Array.isArray(value)) {
3563
+ for (const item of value) freezeBaseline(item);
3564
+ return Object.freeze(value);
3565
+ }
3566
+ if (isPlainRecord(value)) {
3567
+ for (const k of Object.keys(value)) freezeBaseline(safeOwnRead(value, k));
3568
+ return Object.freeze(value);
3569
+ }
3570
+ return value;
3571
+ }
3572
+ function getOriginalAtPath(st, path) {
3573
+ let node = st.baselineValue;
3574
+ for (let i = 0; i < path.length; i++) {
3575
+ const segment = path[i];
3576
+ if (Array.isArray(node)) {
3577
+ const at = typeof segment === "number" ? st.arrayIdentity.baselineIndexOf(path.slice(0, i), segment) : -1;
3578
+ if (at < 0) return void 0;
3579
+ node = node[at];
3580
+ } else if (isPlainRecord(node) && typeof segment === "string") {
3581
+ node = safeOwnRead(node, segment);
3582
+ } else {
3583
+ return void 0;
3584
+ }
3585
+ }
3586
+ return freezeBaseline(node);
3587
+ }
4082
3588
  const ERROR_SOURCES = ["schema", "user"];
4083
3589
  function createErrorCells(schema, pristine) {
4084
3590
  const raw = /* @__PURE__ */ new Map();
@@ -4156,6 +3662,54 @@ function restoreErrorCells(st, entries) {
4156
3662
  st.errorCells().set(key, { schema: [...cell.schema], user: [...cell.user] });
4157
3663
  }
4158
3664
  }
3665
+ function captureStructure(st) {
3666
+ return {
3667
+ identity: st.arrayIdentity.snapshot(),
3668
+ originals: [...toRaw(st.originals)],
3669
+ originalBlank: [...st.originalBlankPaths],
3670
+ removed: [...st.removedPaths]
3671
+ };
3672
+ }
3673
+ function restoreStructure(st, snapshot, remaps, returning) {
3674
+ const originals = new Map(snapshot.originals);
3675
+ const raw = toRaw(st.form.value);
3676
+ const held = /* @__PURE__ */ new Map();
3677
+ for (const [key, { segments }] of toRaw(st.originals)) {
3678
+ if (!originals.has(key) && hasAtPath(raw, segments)) {
3679
+ noteHold(st, held, raw, segments, segments.length - 1, segments);
3680
+ }
3681
+ }
3682
+ applyHolds(st, held);
3683
+ st.arrayIdentity.restore(snapshot.identity);
3684
+ replaceOriginals(st, originals);
3685
+ st.originalBlankPaths.clear();
3686
+ for (const key of snapshot.originalBlank) st.originalBlankPaths.add(key);
3687
+ const pulsed = [...st.removedPaths.values()];
3688
+ st.removedPaths.clear();
3689
+ for (const [key, segments] of snapshot.removed) {
3690
+ st.removedPaths.set(key, segments);
3691
+ pulsed.push(segments);
3692
+ }
3693
+ for (const [segments, remap] of remaps) {
3694
+ migrateFieldRecords(st.fields, segments, remap, (path, released) => {
3695
+ let stamp = "";
3696
+ for (const { updatedAt } of released) {
3697
+ if (updatedAt !== null && updatedAt > stamp) stamp = updatedAt;
3698
+ }
3699
+ if (stamp !== "") eachAncestorOnce([path], (key, at) => raiseStamp(st, key, at, stamp));
3700
+ foldInteractionHistory(st, path, released);
3701
+ });
3702
+ }
3703
+ for (const record of returning) {
3704
+ st.fields.set(canonicalizePath(record.path).key, {
3705
+ ...record,
3706
+ connected: false,
3707
+ focused: null,
3708
+ blurred: null
3709
+ });
3710
+ }
3711
+ if (pulsed.length > 0) pulseStructure(st.structureSignal, pulsed);
3712
+ }
4159
3713
  function getErrorsForPath(st, path) {
4160
3714
  const { key } = canonicalizePath(path);
4161
3715
  const cell = st.errorCells().get(key);
@@ -4180,6 +3734,7 @@ function noteDomConnected(st, path) {
4180
3734
  }
4181
3735
  function noteDomDisconnected(st, path) {
4182
3736
  const { key } = canonicalizePath(path);
3737
+ if (!st.fields.has(key)) return;
4183
3738
  touchFieldRecord(st, key, path, { connected: false, focused: null, blurred: null });
4184
3739
  }
4185
3740
  function markConnectedOptimistically(st, path) {
@@ -4239,22 +3794,44 @@ function markInteracted(st, path) {
4239
3794
  if (st.fields.get(key)?.interacted === true) return;
4240
3795
  touchFieldRecord(st, key, path, { interacted: true });
4241
3796
  }
3797
+ function markBareContainers(st, segments, ladder) {
3798
+ const value = getAtPath(toRaw(st.form.value), segments);
3799
+ if (!isDescendable(value)) return false;
3800
+ const bare = [];
3801
+ diffAndApply(void 0, value, segments, (patch) => {
3802
+ if (isBarePatch(patch)) bare.push(patch.path);
3803
+ });
3804
+ eachAncestorOnce(bare, (key, path) => {
3805
+ const current = st.fields.get(key);
3806
+ if (current?.touched === true && (!ladder || current.interacted && current.blurredAfterInteraction)) {
3807
+ return;
3808
+ }
3809
+ touchFieldRecord(
3810
+ st,
3811
+ key,
3812
+ path,
3813
+ ladder ? { touched: true, interacted: true, blurredAfterInteraction: true } : { touched: true }
3814
+ );
3815
+ });
3816
+ return bare.length > 0;
3817
+ }
4242
3818
  function touchAtPath(st, segments) {
4243
3819
  const formValue = st.form.value;
4244
3820
  for (const [leafKey, entry] of st.originals) {
4245
- if (!isPathPrefix(segments, entry.segments)) continue;
3821
+ if (!isPathPrefix(segments, entry.segments) || isDescendable(entry.value)) continue;
4246
3822
  if (!hasAtPath(formValue, entry.segments)) continue;
4247
3823
  const current = st.fields.get(leafKey);
4248
3824
  if (current?.touched === true) continue;
4249
3825
  touchFieldRecord(st, leafKey, entry.segments, { touched: true });
4250
3826
  }
3827
+ if (markBareContainers(st, segments, false)) ;
4251
3828
  }
4252
3829
  function interactAtPath(st, segments) {
4253
3830
  if (st.effectiveDisabled.value) return false;
4254
3831
  const formValue = st.form.value;
4255
3832
  let interactedAny = false;
4256
3833
  for (const [leafKey, entry] of st.originals) {
4257
- if (!isPathPrefix(segments, entry.segments)) continue;
3834
+ if (!isPathPrefix(segments, entry.segments) || isDescendable(entry.value)) continue;
4258
3835
  if (!hasAtPath(formValue, entry.segments)) continue;
4259
3836
  interactedAny = true;
4260
3837
  const current = st.fields.get(leafKey);
@@ -4267,6 +3844,7 @@ function interactAtPath(st, segments) {
4267
3844
  blurredAfterInteraction: true
4268
3845
  });
4269
3846
  }
3847
+ if (markBareContainers(st, segments, true)) interactedAny = true;
4270
3848
  return interactedAny;
4271
3849
  }
4272
3850
  function rehydrate(st) {
@@ -4308,14 +3886,31 @@ function adoptResolvedDefaults(st, value) {
4308
3886
  st.schema
4309
3887
  )
4310
3888
  );
4311
- seedOriginalsFromBaseline(
4312
- st,
4313
- computeBaseline(
4314
- st.schema,
4315
- st.defaultValues,
4316
- st.schema.getDefaultValues({ useDefaultSchemaValues: true })
4317
- )
3889
+ st.baselineValue = computeBaseline(
3890
+ st.schema,
3891
+ st.defaultValues,
3892
+ st.schema.getDefaultValues({ useDefaultSchemaValues: true })
4318
3893
  );
3894
+ seedOriginalsFromBaseline(st, st.baselineValue);
3895
+ st.arrayIdentity.rebaseline((segments) => holdsListAt(value, segments));
3896
+ const dropped = [];
3897
+ for (const [key, segments] of st.removedPaths) {
3898
+ if (st.originals.get(key)?.value !== void 0) continue;
3899
+ st.removedPaths.delete(key);
3900
+ dropped.push(segments);
3901
+ }
3902
+ if (dropped.length > 0) pulseStructure(st.structureSignal, dropped);
3903
+ }
3904
+ function holdsListAt(value, segments) {
3905
+ let node = value;
3906
+ for (const segment of segments) {
3907
+ if (Array.isArray(node)) return true;
3908
+ if (!isPlainRecord(node) || typeof segment !== "string" || !safeOwnHas(node, segment)) {
3909
+ return false;
3910
+ }
3911
+ node = safeOwnRead(node, segment);
3912
+ }
3913
+ return Array.isArray(node);
4319
3914
  }
4320
3915
  function reset(st, nextDefaultValues) {
4321
3916
  const resetSource = nextDefaultValues === void 0 ? st.defaultValues : mergeSparseHydration(
@@ -4326,19 +3921,25 @@ function reset(st, nextDefaultValues) {
4326
3921
  st.defaultValues = structuralSnapshot(resetSource);
4327
3922
  const next = computeBaseline(
4328
3923
  st.schema,
4329
- resetSource,
3924
+ st.defaultValues,
4330
3925
  st.schema.getDefaultValues({ useDefaultSchemaValues: true })
4331
3926
  );
4332
3927
  rebuildAuthoredPaths(st, resetSource, next);
4333
- applyFormReplacement(st, next);
4334
- st.arrayIdentity.rebaselineAll();
4335
- st.removedSubtrees.clear();
4336
- seedOriginalsFromBaseline(st, next);
4337
3928
  if (nextDefaultValues !== void 0) {
4338
3929
  const mentioned = /* @__PURE__ */ new Set();
4339
3930
  walkAuthoredFromConstraints(nextDefaultValues, [], mentioned);
4340
3931
  for (const key of mentioned) st.originalBlankPaths.delete(key);
4341
3932
  }
3933
+ const now = writeInstant(st);
3934
+ applyFormReplacementWithPath(st, structuralSnapshot(next), void 0, null, now);
3935
+ st.arrayIdentity.rebaseline(() => true);
3936
+ if (st.removedPaths.size > 0) {
3937
+ const cleared = [...st.removedPaths.values()];
3938
+ st.removedPaths.clear();
3939
+ pulseStructure(st.structureSignal, cleared);
3940
+ }
3941
+ st.baselineValue = next;
3942
+ seedOriginalsFromBaseline(st, next);
4342
3943
  st.blankPaths.clear();
4343
3944
  for (const key of st.originalBlankPaths) {
4344
3945
  st.blankPaths.add(key);
@@ -4347,7 +3948,7 @@ function reset(st, nextDefaultValues) {
4347
3948
  replaceErrorChannel(st, "schema", st.schema.validateSync(next));
4348
3949
  st.firstValidationDone.value = initialFirstValidationGate(st.schema);
4349
3950
  queueInitialAsyncValidation(st);
4350
- const now = (/* @__PURE__ */ new Date()).toISOString();
3951
+ noteStamp(st, now);
4351
3952
  for (const [pathKey, record] of st.fields) {
4352
3953
  st.fields.set(pathKey, withClearedHistoryFlags(record, now));
4353
3954
  }
@@ -4376,6 +3977,7 @@ function reset(st, nextDefaultValues) {
4376
3977
  }
4377
3978
  function resetField(st, path) {
4378
3979
  const { key: targetKey, segments: targetSegments } = canonicalizePath(path);
3980
+ const now = writeInstant(st);
4379
3981
  st.variantMemory.clearUnderPath(targetSegments);
4380
3982
  cancelFieldValidationUnder(st, targetSegments);
4381
3983
  cancelTransformsUnder(st, targetSegments);
@@ -4384,9 +3986,21 @@ function resetField(st, path) {
4384
3986
  if (segs === null) continue;
4385
3987
  if (isPathPrefix(targetSegments, segs)) st.pathSnapshots.delete(snapKey);
4386
3988
  }
3989
+ const cleared = [];
3990
+ for (const [fieldKey, record] of Array.from(st.fields.entries())) {
3991
+ if (!isPathPrefix(targetSegments, record.path)) continue;
3992
+ st.fields.set(fieldKey, withClearedHistoryFlags(record, record.updatedAt));
3993
+ cleared.push(fieldKey);
3994
+ }
4387
3995
  const leafEntry = st.originals.get(targetKey);
4388
3996
  if (leafEntry !== void 0) {
4389
- const wrote = setValueAtPath(st, targetSegments, leafEntry.value);
3997
+ const wrote = setValueAtPath(
3998
+ st,
3999
+ targetSegments,
4000
+ structuralSnapshot(leafEntry.value),
4001
+ void 0,
4002
+ now
4003
+ );
4390
4004
  if (!wrote) {
4391
4005
  console.error(
4392
4006
  `[attaform] AF11 attaform.dev/e/af11 '${targetKey}'`
@@ -4404,10 +4018,10 @@ function resetField(st, path) {
4404
4018
  if (subtree === void 0) {
4405
4019
  subtree = typeof relative[0] === "number" ? [] : {};
4406
4020
  }
4407
- subtree = setAtPath(subtree, relative, entry.value);
4021
+ subtree = setAtPath(subtree, relative, structuralSnapshot(entry.value));
4408
4022
  }
4409
4023
  if (anyMatch) {
4410
- const wroteSubtree = setValueAtPath(st, targetSegments, subtree);
4024
+ const wroteSubtree = setValueAtPath(st, targetSegments, subtree, void 0, now);
4411
4025
  if (!wroteSubtree) {
4412
4026
  console.error(
4413
4027
  `[attaform] AF12 attaform.dev/e/af12 '${targetKey}'`
@@ -4416,8 +4030,14 @@ function resetField(st, path) {
4416
4030
  }
4417
4031
  }
4418
4032
  deleteErrorCellsUnderPrefix(st, targetSegments);
4419
- for (const [fieldKey, record] of Array.from(st.fields.entries())) {
4420
- if (isPathPrefix(targetSegments, record.path)) clearFieldRecordFlags(st, fieldKey);
4033
+ const raw = toRaw(st.form.value);
4034
+ for (const fieldKey of cleared) {
4035
+ const record = st.fields.get(fieldKey);
4036
+ if (record === void 0 || !hasAtPath(raw, record.path)) continue;
4037
+ raiseStamp(st, fieldKey, record.path, now);
4038
+ }
4039
+ if (cleared.length > 0 && hasAtPath(raw, targetSegments)) {
4040
+ stampContainers(st, [targetSegments], now);
4421
4041
  }
4422
4042
  }
4423
4043
  function deleteErrorCellsUnderPrefix(st, prefix) {
@@ -4430,36 +4050,35 @@ function deleteErrorCellsUnderPrefix(st, prefix) {
4430
4050
  }
4431
4051
  }
4432
4052
  }
4433
- function clearFieldRecordFlags(st, pathKey) {
4434
- const record = st.fields.get(pathKey);
4435
- if (record === void 0) return;
4436
- st.fields.set(pathKey, withClearedHistoryFlags(record, (/* @__PURE__ */ new Date()).toISOString()));
4437
- }
4438
4053
  function isPristineAtPathByKey(st, key, segments) {
4439
4054
  if (st.blankPaths.has(key) !== st.originalBlankPaths.has(key)) return false;
4440
4055
  const entry = st.originals.get(key);
4441
4056
  if (entry === void 0) return true;
4442
4057
  return Object.is(getAtPath(st.form.value, segments), entry.value);
4443
4058
  }
4444
- function hasStructuralChangeUnder(st, path) {
4059
+ function hasStructuralChangeUnder(st, path, key) {
4060
+ subscribeStructure(st.structureSignal, key);
4445
4061
  return st.arrayIdentity.hasStructuralChangeUnder(path);
4446
4062
  }
4447
- function subtreeHadRealBaseline(st, prefix, removedValue) {
4448
- let had = false;
4449
- diffAndApply(removedValue, void 0, prefix, (patch) => {
4450
- if (had || patch.kind !== "removed") return;
4451
- const record = st.originals.get(canonicalizePath(patch.path).key);
4452
- if (record?.value !== void 0) had = true;
4063
+ function pulseStructure(signal, paths) {
4064
+ const keys = signal.keys;
4065
+ if (keys === null) return;
4066
+ eachAncestorOnce(paths, (key) => {
4067
+ keys.add(key);
4068
+ keys.delete(key);
4453
4069
  });
4454
- return had;
4455
4070
  }
4456
- function hasRemovedSubtreeUnder(st, prefix) {
4457
- if (st.removedSubtrees.size === 0) return false;
4458
- for (const key of st.removedSubtrees) {
4459
- const segments = segmentsForPathKey(key);
4460
- if (segments === null) continue;
4071
+ function subscribeStructure(signal, key) {
4072
+ signal.keys ?? (signal.keys = shallowReactive(/* @__PURE__ */ new Set()));
4073
+ void signal.keys.has(key);
4074
+ }
4075
+ function hasRemovedSubtreeUnder(st, prefix, key) {
4076
+ subscribeStructure(st.structureSignal, key);
4077
+ if (st.removedPaths.size === 0) return false;
4078
+ const raw = toRaw(st.form.value);
4079
+ for (const segments of st.removedPaths.values()) {
4461
4080
  if (!isPathPrefix(prefix, segments)) continue;
4462
- if (isContainer(getAtPath(toRaw(st.form.value), segments))) continue;
4081
+ if (hasAtPath(raw, segments)) continue;
4463
4082
  return true;
4464
4083
  }
4465
4084
  return false;
@@ -4495,10 +4114,14 @@ function createFormStore(options) {
4495
4114
  warn: true
4496
4115
  }) : initialData;
4497
4116
  const form = ref(stubbedInitialData);
4498
- const arrayIdentity = createArrayIdentity((arraySegs) => {
4499
- const v = getAtPath(toRaw(form.value), arraySegs);
4500
- return Array.isArray(v) ? v.length : 0;
4501
- });
4117
+ const structureSignal = { keys: null };
4118
+ const arrayIdentity = createArrayIdentity(
4119
+ (arraySegs) => {
4120
+ const v = getAtPath(toRaw(form.value), arraySegs);
4121
+ return Array.isArray(v) ? v.length : 0;
4122
+ },
4123
+ (arrayPaths) => pulseStructure(structureSignal, arrayPaths)
4124
+ );
4502
4125
  const fields = shallowReactive(/* @__PURE__ */ new Map());
4503
4126
  const domBinding = shallowRef(null);
4504
4127
  const errorStore = createErrorCells(
@@ -4507,7 +4130,7 @@ function createFormStore(options) {
4507
4130
  );
4508
4131
  const errorCells = () => errorStore(true);
4509
4132
  const originals = shallowReactive(/* @__PURE__ */ new Map());
4510
- const removedSubtrees = /* @__PURE__ */ new Set();
4133
+ const removedPaths = /* @__PURE__ */ new Map();
4511
4134
  const blankPaths = reactive(/* @__PURE__ */ new Set());
4512
4135
  const originalBlankPaths = /* @__PURE__ */ new Set();
4513
4136
  const seededBlankPaths = hydration !== void 0 ? (hydration.blankPaths ?? []).map((dotted) => canonicalizePath(dotted).key) : options.initialBlankPaths ?? [];
@@ -4608,6 +4231,8 @@ function createFormStore(options) {
4608
4231
  blankPaths,
4609
4232
  originalBlankPaths,
4610
4233
  authoredPaths,
4234
+ removedPaths,
4235
+ notifyStructure: (paths) => pulseStructure(structureSignal, paths),
4611
4236
  fieldValidationCounts,
4612
4237
  fieldValidatingSince,
4613
4238
  fieldValidationState,
@@ -4615,6 +4240,9 @@ function createFormStore(options) {
4615
4240
  arrayIdentity,
4616
4241
  variantMemory,
4617
4242
  touchFieldRecord: (pathKey, path, patch) => touchFieldRecord(st, pathKey, path, patch),
4243
+ stampContainers: (containers, now) => stampContainers(st, containers, now),
4244
+ stampBareContainers: (patches, bare, now) => stampBareContainers(st, patches, bare, now),
4245
+ foldInteractionHistory: (path, released) => foldInteractionHistory(st, path, released),
4618
4246
  decFieldValidation: (key) => decFieldValidation(st, key)
4619
4247
  });
4620
4248
  const bind = (fn) => (...args) => fn(st, ...args);
@@ -4665,6 +4293,8 @@ function createFormStore(options) {
4665
4293
  // the literal they passed from mutating the form's defaults from outside.
4666
4294
  // `reset()` snapshots for a harder reason, where an alias IS reachable.
4667
4295
  defaultValues: ownDefaultValues,
4296
+ // The form holds a copy of it, or the hydration payload.
4297
+ baselineValue: schemaInitialData,
4668
4298
  ssrPrefetch,
4669
4299
  rememberVariants,
4670
4300
  fieldValidationMode,
@@ -4678,7 +4308,8 @@ function createFormStore(options) {
4678
4308
  cleanupHooks,
4679
4309
  authoredPaths,
4680
4310
  arrayIdentity,
4681
- removedSubtrees,
4311
+ removedPaths,
4312
+ structureSignal,
4682
4313
  variantMemory,
4683
4314
  pathOrdinals,
4684
4315
  pathSnapshots,
@@ -4688,6 +4319,7 @@ function createFormStore(options) {
4688
4319
  arrayBookkeeping,
4689
4320
  skipErrorSeed: () => errorStore(false),
4690
4321
  nextOrdinal: 0,
4322
+ latestStamp: "",
4691
4323
  scheduleEpoch: 0,
4692
4324
  lastCommittedEpoch: 0,
4693
4325
  transformTokenSeq: 0,
@@ -4700,6 +4332,7 @@ function createFormStore(options) {
4700
4332
  applyFormReplacement: bind(applyFormReplacement),
4701
4333
  setValueAtPath: bind(setValueAtPath),
4702
4334
  getValueAtPath: bind(getValueAtPath),
4335
+ getOriginalAtPath: bind(getOriginalAtPath),
4703
4336
  arrayElementKey: bind(arrayElementKey),
4704
4337
  reset: bind(reset),
4705
4338
  resetField: bind(resetField),
@@ -4725,6 +4358,8 @@ function createFormStore(options) {
4725
4358
  ),
4726
4359
  clearUserErrors: (path) => clearErrorChannel(st, "user", path),
4727
4360
  restoreErrorCells: bind(restoreErrorCells),
4361
+ captureStructure: bind(captureStructure),
4362
+ restoreStructure: bind(restoreStructure),
4728
4363
  getErrorsForPath: bind(getErrorsForPath),
4729
4364
  ensurePathOrdinal: bind(ensurePathOrdinal),
4730
4365
  noteDomConnected: bind(noteDomConnected),
@@ -4758,7 +4393,7 @@ function createFormStore(options) {
4758
4393
  diffAndApply({}, schemaInitialData, [], (patch) => {
4759
4394
  if (patch.kind !== "added") return;
4760
4395
  const { key } = canonicalizePath(patch.path);
4761
- originals.set(key, { segments: patch.path, value: patch.newValue });
4396
+ if (seedBaseline(originals, key, patch)) return;
4762
4397
  ensurePathOrdinal(st, key);
4763
4398
  if (hydration === void 0) {
4764
4399
  fields.set(key, {
@@ -4773,12 +4408,14 @@ function createFormStore(options) {
4773
4408
  });
4774
4409
  }
4775
4410
  });
4411
+ if (fields.size > 0) noteStamp(st, initStamp);
4776
4412
  if (hydration !== void 0) {
4777
4413
  for (const [rawKey, record] of hydration.fields) {
4778
4414
  if (typeof rawKey !== "string" || !isHydratedFieldRecord(record)) {
4779
4415
  continue;
4780
4416
  }
4781
4417
  fields.set(rawKey, record);
4418
+ noteStamp(st, record.updatedAt);
4782
4419
  }
4783
4420
  for (const [rawKey, errs] of hydration.schemaErrors) {
4784
4421
  if (typeof rawKey !== "string" || !isHydratedValidationErrorArray(errs)) {
@@ -5282,7 +4919,7 @@ function buildWizardStatusesProxy(statuses) {
5282
4919
  // resolves to `undefined` (consistent with property-access).
5283
4920
  resolveCall: (arg) => statuses[String(arg)]?.value,
5284
4921
  ownKeys: () => Object.keys(statuses),
5285
- hasKey: (key) => Object.hasOwn(statuses, key)
4922
+ hasKey: (key) => safeOwnHas(statuses, key)
5286
4923
  });
5287
4924
  }
5288
4925
 
@@ -6432,4 +6069,4 @@ function escapeForInlineScript(json) {
6432
6069
  });
6433
6070
  }
6434
6071
 
6435
- 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 };
6072
+ 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 };