attaform 0.18.2 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/chunks/devtools.cjs +1 -1
- package/dist/chunks/devtools.mjs +1 -1
- package/dist/chunks/indexeddb.cjs +1 -1
- package/dist/chunks/indexeddb.mjs +1 -1
- package/dist/chunks/local-storage.cjs +1 -1
- package/dist/chunks/local-storage.mjs +1 -1
- package/dist/chunks/session-storage.cjs +1 -1
- package/dist/chunks/session-storage.mjs +1 -1
- package/dist/index.cjs +4 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -110
- package/dist/index.d.mts +77 -110
- package/dist/index.d.ts +77 -110
- package/dist/index.mjs +5 -5
- package/dist/nuxt.d.cts +1 -1
- package/dist/nuxt.d.mts +1 -1
- package/dist/nuxt.d.ts +1 -1
- package/dist/runtime/components/AttaformDevtoolsPanel.vue +2 -2
- package/dist/runtime/components/DevtoolsValueTree.d.vue.ts +1 -3
- package/dist/runtime/components/DevtoolsValueTree.vue.d.ts +1 -3
- package/dist/runtime/plugins/attaform.cjs +2 -2
- package/dist/runtime/plugins/attaform.mjs +2 -2
- package/dist/shared/{attaform.CDmaxrt2.mjs → attaform.BKozEdTr.mjs} +305 -178
- package/dist/shared/attaform.BKozEdTr.mjs.map +1 -0
- package/dist/shared/{attaform.Bubm_slq.cjs → attaform.BM6YD9kZ.cjs} +212 -269
- package/dist/shared/attaform.BM6YD9kZ.cjs.map +1 -0
- package/dist/shared/{attaform.5UhpSVFI.cjs → attaform.BPxsYtTe.cjs} +2 -26
- package/dist/shared/attaform.BPxsYtTe.cjs.map +1 -0
- package/dist/shared/{attaform.BqK_L4gK.cjs → attaform.BPy-4qRx.cjs} +305 -180
- package/dist/shared/attaform.BPy-4qRx.cjs.map +1 -0
- package/dist/shared/attaform.BWgAFnsj.mjs +770 -0
- package/dist/shared/attaform.BWgAFnsj.mjs.map +1 -0
- package/dist/shared/{attaform.CGX1CNpz.d.ts → attaform.Bh3ACtts.d.ts} +152 -111
- package/dist/shared/{attaform.CXpzmj38.mjs → attaform.BupwXkj_.mjs} +213 -270
- package/dist/shared/attaform.BupwXkj_.mjs.map +1 -0
- package/dist/shared/{attaform.Dlk1jMuv.cjs → attaform.CIn4bMsD.cjs} +263 -799
- package/dist/shared/attaform.CIn4bMsD.cjs.map +1 -0
- package/dist/shared/{attaform.CZ-XtZt_.mjs → attaform.CKFbKFb6.mjs} +2265 -1509
- package/dist/shared/attaform.CKFbKFb6.mjs.map +1 -0
- package/dist/shared/{attaform.CuN7ZhBy.d.cts → attaform.D5-1XGQU.d.cts} +152 -111
- package/dist/shared/{attaform.-1GQTX2T.mjs → attaform.DEBvCjeH.mjs} +257 -793
- package/dist/shared/attaform.DEBvCjeH.mjs.map +1 -0
- package/dist/shared/{attaform.II89Pcf4.cjs → attaform.DL4CQ-oW.cjs} +2270 -1514
- package/dist/shared/attaform.DL4CQ-oW.cjs.map +1 -0
- package/dist/shared/{attaform.FnEwjhvX.d.ts → attaform.DSD85fHb.d.cts} +1 -19
- package/dist/shared/{attaform.CRmmNAYp.d.cts → attaform.DSD85fHb.d.mts} +1 -19
- package/dist/shared/{attaform.D9wuTGu9.d.mts → attaform.DSD85fHb.d.ts} +1 -19
- package/dist/shared/{attaform.B7rzpK1U.d.cts → attaform.DkA5J8NW.d.cts} +1 -17
- package/dist/shared/{attaform.B7rzpK1U.d.mts → attaform.DkA5J8NW.d.mts} +1 -17
- package/dist/shared/{attaform.B7rzpK1U.d.ts → attaform.DkA5J8NW.d.ts} +1 -17
- package/dist/shared/{attaform.B957T6NU.d.ts → attaform.Dl5kDY-A.d.ts} +1 -1
- package/dist/shared/attaform.Dmb6itxC.cjs +781 -0
- package/dist/shared/attaform.Dmb6itxC.cjs.map +1 -0
- package/dist/shared/{attaform.M-RanbyV.d.mts → attaform.DoKXru-a.d.mts} +1 -1
- package/dist/shared/attaform.DvA-CJJW.mjs +1876 -0
- package/dist/shared/attaform.DvA-CJJW.mjs.map +1 -0
- package/dist/shared/{attaform.D1gzu2GL.d.mts → attaform.EMzJcQci.d.mts} +152 -111
- package/dist/shared/attaform.EZG6fOFb.mjs +35 -0
- package/dist/shared/attaform.EZG6fOFb.mjs.map +1 -0
- package/dist/shared/{attaform.XDjA7sRz.d.cts → attaform.GbDo_lJi.d.cts} +1 -1
- package/dist/shared/{attaform.Ca5_6Ky-.d.mts → attaform.SfhU0OEY.d.cts} +499 -116
- package/dist/shared/{attaform.Ca5_6Ky-.d.cts → attaform.SfhU0OEY.d.mts} +499 -116
- package/dist/shared/{attaform.Ca5_6Ky-.d.ts → attaform.SfhU0OEY.d.ts} +499 -116
- package/dist/shared/attaform.jgzuNZVC.cjs +1882 -0
- package/dist/shared/attaform.jgzuNZVC.cjs.map +1 -0
- package/dist/transforms.cjs +2 -2
- package/dist/transforms.d.cts +22 -13
- package/dist/transforms.d.mts +22 -13
- package/dist/transforms.d.ts +22 -13
- package/dist/transforms.mjs +1 -1
- package/dist/vite.cjs +8 -7
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.mjs +8 -7
- package/dist/vite.mjs.map +1 -1
- package/dist/zod-v3.cjs +3 -3
- package/dist/zod-v3.d.cts +32 -6
- package/dist/zod-v3.d.mts +32 -6
- package/dist/zod-v3.d.ts +32 -6
- package/dist/zod-v3.mjs +3 -3
- package/dist/zod-v4.cjs +3 -3
- package/dist/zod-v4.d.cts +12 -8
- package/dist/zod-v4.d.mts +12 -8
- package/dist/zod-v4.d.ts +12 -8
- package/dist/zod-v4.mjs +3 -3
- package/dist/zod.cjs +8 -8
- package/dist/zod.cjs.map +1 -1
- package/dist/zod.d.cts +6 -6
- package/dist/zod.d.mts +6 -6
- package/dist/zod.d.ts +6 -6
- package/dist/zod.mjs +6 -6
- package/package.json +2 -2
- package/dist/shared/attaform.-1GQTX2T.mjs.map +0 -1
- package/dist/shared/attaform.5UhpSVFI.cjs.map +0 -1
- package/dist/shared/attaform.BqK_L4gK.cjs.map +0 -1
- package/dist/shared/attaform.Bubm_slq.cjs.map +0 -1
- package/dist/shared/attaform.C8CyvYa_.cjs +0 -36
- package/dist/shared/attaform.C8CyvYa_.cjs.map +0 -1
- package/dist/shared/attaform.CDmaxrt2.mjs.map +0 -1
- package/dist/shared/attaform.CXpzmj38.mjs.map +0 -1
- package/dist/shared/attaform.CZ-XtZt_.mjs.map +0 -1
- package/dist/shared/attaform.D13GMFgK.mjs +0 -32
- package/dist/shared/attaform.D13GMFgK.mjs.map +0 -1
- package/dist/shared/attaform.DUHru0OF.cjs +0 -1600
- package/dist/shared/attaform.DUHru0OF.cjs.map +0 -1
- package/dist/shared/attaform.Df0tU0Ut.mjs +0 -1594
- package/dist/shared/attaform.Df0tU0Ut.mjs.map +0 -1
- package/dist/shared/attaform.Dl161U6E.mjs +0 -57
- package/dist/shared/attaform.Dl161U6E.mjs.map +0 -1
- package/dist/shared/attaform.Dlk1jMuv.cjs.map +0 -1
- package/dist/shared/attaform.II89Pcf4.cjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, reactive, watch, markRaw,
|
|
2
|
-
import {
|
|
1
|
+
import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, reactive, toRaw, watch, markRaw, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, effectScope, nextTick } from 'vue';
|
|
2
|
+
import { _ as __DEV__, j as canonicalizePath, E as segmentsForPathKey, s as isPathPrefix, b as FORM_ERRORS_PATH_KEY, S as SubmitErrorHandlerError, A as AnonPersistError, k as captureUserCallSite, I as INTERACTIVE_TAG_NAMES, e as ROOT_PATH_KEY, R as ROOT_PATH, h as allowSensitivePersist, F as FORM_ERRORS_PATH, l as coerceToPathKey, u as isSensitivePath, o as createPersistOptInRegistry, d as InvalidUseFormConfigError, q as ensureAttaformInstalled, H as useRegistry, y as kFormContext, z as kFormInstanceId, g as ReservedFormKeyError, n as createIsSensitivePath, x as kAttaformWizardActiveStepResolver, v as kAttaformAncestorWizard } from './attaform.BKozEdTr.mjs';
|
|
3
3
|
|
|
4
4
|
const NOT_FOUND = Symbol("NOT_FOUND");
|
|
5
5
|
function descendStep(value, segment) {
|
|
@@ -62,7 +62,7 @@ function setAtPathOffset(root, path, value, offset) {
|
|
|
62
62
|
arr[head] = setAtPathOffset(arr[head], path, value, nextOffset);
|
|
63
63
|
return arr;
|
|
64
64
|
}
|
|
65
|
-
const rec = isPlainRecord(root) ?
|
|
65
|
+
const rec = isPlainRecord(root) ? Object.assign(/* @__PURE__ */ Object.create(null), root) : /* @__PURE__ */ Object.create(null);
|
|
66
66
|
rec[head] = setAtPathOffset(rec[head], path, value, nextOffset);
|
|
67
67
|
return rec;
|
|
68
68
|
}
|
|
@@ -163,7 +163,7 @@ function mergeStructuralImpl(schema, scratch, consumer, defaultValue) {
|
|
|
163
163
|
return consumer;
|
|
164
164
|
}
|
|
165
165
|
let mutated = false;
|
|
166
|
-
const out =
|
|
166
|
+
const out = Object.assign(/* @__PURE__ */ Object.create(null), consumer);
|
|
167
167
|
for (const key of Object.keys(defaultValue)) {
|
|
168
168
|
if (!(key in consumer)) {
|
|
169
169
|
const defAtKey = defaultValue[key];
|
|
@@ -427,13 +427,81 @@ function structuralSnapshot(value) {
|
|
|
427
427
|
return out2;
|
|
428
428
|
}
|
|
429
429
|
const src = value;
|
|
430
|
-
const out =
|
|
430
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
431
431
|
for (const k of Object.keys(src)) {
|
|
432
432
|
out[k] = structuralSnapshot(src[k]);
|
|
433
433
|
}
|
|
434
434
|
return out;
|
|
435
435
|
}
|
|
436
436
|
|
|
437
|
+
const defaultDisplayState = (field, formMeta) => {
|
|
438
|
+
const gateOpen = formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
|
|
439
|
+
if (!gateOpen) return "idle";
|
|
440
|
+
if (field.validating === true) return "pending";
|
|
441
|
+
const hasOwnError = field.errors.some(
|
|
442
|
+
(e) => e.path.length === field.path.length && e.path.every((s, i) => s === field.path[i])
|
|
443
|
+
);
|
|
444
|
+
if (hasOwnError) return "error";
|
|
445
|
+
if (field.valid === true && field.blank !== true && field.dirty === true) return "success";
|
|
446
|
+
return "idle";
|
|
447
|
+
};
|
|
448
|
+
function resolveGetDisplayState(config) {
|
|
449
|
+
return config ?? defaultDisplayState;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS = 0;
|
|
453
|
+
const DEFAULT_PERSISTENCE_DEBOUNCE_MS = 300;
|
|
454
|
+
const DEFAULT_HISTORY_MAX_SNAPSHOTS = 128;
|
|
455
|
+
const PERSISTENCE_KEY_PREFIX = "attaform:";
|
|
456
|
+
const RESERVED_KEY_PREFIX = "__atta:";
|
|
457
|
+
const ANONYMOUS_FORM_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon:`;
|
|
458
|
+
const ANONYMOUS_WIZARD_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon-wizard:`;
|
|
459
|
+
const DEFAULT_MAX_RECURSION_DEPTH = 64;
|
|
460
|
+
function normalizeNumericOption(config) {
|
|
461
|
+
const { value, source, allowInfinity, min, defaultValue } = config;
|
|
462
|
+
if (allowInfinity && value === Infinity) return Infinity;
|
|
463
|
+
if (typeof value !== "number" || Number.isNaN(value) || value === Infinity || value === -Infinity) {
|
|
464
|
+
if (__DEV__) {
|
|
465
|
+
const acceptedDescription = allowInfinity ? "a non-negative integer or Infinity" : "a non-negative finite integer";
|
|
466
|
+
console.warn(
|
|
467
|
+
`[attaform] ${source} must be ${acceptedDescription}; got ${String(value)}. Falling back to ${String(defaultValue)}.`
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
return defaultValue;
|
|
471
|
+
}
|
|
472
|
+
return Math.max(min, Math.floor(value));
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function hashStableString(input, seed = 0) {
|
|
476
|
+
let h1 = 3735928559 ^ seed;
|
|
477
|
+
let h2 = 1103547991 ^ seed;
|
|
478
|
+
for (let i = 0; i < input.length; i++) {
|
|
479
|
+
const ch = input.charCodeAt(i);
|
|
480
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
481
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
482
|
+
}
|
|
483
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
484
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
485
|
+
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36).padStart(11, "0");
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const ANON_STEM = "atta";
|
|
489
|
+
function readableFormKeyStem(formKey) {
|
|
490
|
+
if (formKey === "" || formKey.startsWith(ANONYMOUS_FORM_KEY_PREFIX)) return ANON_STEM;
|
|
491
|
+
const sanitized = formKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|(?<!-)-+$/g, "");
|
|
492
|
+
return sanitized === "" ? ANON_STEM : sanitized;
|
|
493
|
+
}
|
|
494
|
+
function fieldIdToken(formInstanceId, pathKey) {
|
|
495
|
+
return hashStableString(`${formInstanceId}:${pathKey}`).slice(-7);
|
|
496
|
+
}
|
|
497
|
+
function computeFieldIdentity(formInstanceId, formKey, pathKey) {
|
|
498
|
+
const id = `${readableFormKeyStem(formKey)}-${fieldIdToken(formInstanceId, pathKey)}`;
|
|
499
|
+
return {
|
|
500
|
+
id,
|
|
501
|
+
aria: Object.freeze({ errorId: `${id}-error`, descriptionId: `${id}-description` })
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
437
505
|
const EMPTY_RESOLVED_FIELD_META = Object.freeze({
|
|
438
506
|
label: "",
|
|
439
507
|
description: void 0,
|
|
@@ -454,6 +522,7 @@ function humanize(segment) {
|
|
|
454
522
|
}).join(" ");
|
|
455
523
|
}
|
|
456
524
|
|
|
525
|
+
const warnedDisplayStatePredicates = /* @__PURE__ */ new WeakSet();
|
|
457
526
|
function isUnderStubAncestor(state, segments) {
|
|
458
527
|
for (let i = 0; i < segments.length; i++) {
|
|
459
528
|
const ancestorPath = segments.slice(0, i);
|
|
@@ -467,21 +536,21 @@ function isUnderStubAncestor(state, segments) {
|
|
|
467
536
|
}
|
|
468
537
|
return false;
|
|
469
538
|
}
|
|
470
|
-
function buildFieldStateAccessor(state, getFormMetaBase, options) {
|
|
539
|
+
function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, options) {
|
|
471
540
|
const cache = /* @__PURE__ */ new Map();
|
|
472
|
-
const predicate = options?.
|
|
541
|
+
const predicate = options?.getDisplayState;
|
|
473
542
|
return function getFieldState(pathInput) {
|
|
474
543
|
const { segments, key } = canonicalizePath(pathInput);
|
|
475
544
|
const cached = cache.get(key);
|
|
476
545
|
if (cached !== void 0) return cached;
|
|
477
546
|
const c = computed(
|
|
478
|
-
() => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, getFormMetaBase, predicate) : buildContainerFieldState(state, segments, key, getFormMetaBase, predicate)
|
|
547
|
+
() => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase, predicate) : buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase, predicate)
|
|
479
548
|
);
|
|
480
549
|
cache.set(key, c);
|
|
481
550
|
return c;
|
|
482
551
|
};
|
|
483
552
|
}
|
|
484
|
-
function buildLeafFieldStateBase(state, segments, key) {
|
|
553
|
+
function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
485
554
|
const record = state.fields.get(key);
|
|
486
555
|
const value = state.getValueAtPath(segments);
|
|
487
556
|
const original = state.originals.get(key)?.value;
|
|
@@ -511,6 +580,8 @@ function buildLeafFieldStateBase(state, segments, key) {
|
|
|
511
580
|
focused: record?.focused ?? null,
|
|
512
581
|
blurred: record?.blurred ?? null,
|
|
513
582
|
touched: record?.touched ?? false,
|
|
583
|
+
interacted: record?.interacted ?? false,
|
|
584
|
+
blurredAfterInteraction: record?.blurredAfterInteraction ?? false,
|
|
514
585
|
connected: record?.connected ?? false,
|
|
515
586
|
element: firstElement,
|
|
516
587
|
elements: elementsArr,
|
|
@@ -519,6 +590,8 @@ function buildLeafFieldStateBase(state, segments, key) {
|
|
|
519
590
|
validating,
|
|
520
591
|
valid,
|
|
521
592
|
path: segments,
|
|
593
|
+
...computeFieldIdentity(formInstanceId, state.formKey, key),
|
|
594
|
+
key: state.arrayElementKey(segments),
|
|
522
595
|
blank: state.blankPaths.has(key),
|
|
523
596
|
label,
|
|
524
597
|
description: resolved.description,
|
|
@@ -526,31 +599,32 @@ function buildLeafFieldStateBase(state, segments, key) {
|
|
|
526
599
|
meta: resolved.meta
|
|
527
600
|
};
|
|
528
601
|
}
|
|
529
|
-
function buildLeafFieldState(state, segments, key, getFormMetaBase,
|
|
530
|
-
const base = buildLeafFieldStateBase(state, segments, key);
|
|
531
|
-
return decorateWithDerivedProps(base, state, getFormMetaBase,
|
|
602
|
+
function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
|
|
603
|
+
const base = buildLeafFieldStateBase(state, segments, key, formInstanceId);
|
|
604
|
+
return decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState);
|
|
532
605
|
}
|
|
533
|
-
function buildContainerFieldStateBase(state, segments,
|
|
606
|
+
function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
534
607
|
const formValue = state.form.value;
|
|
535
608
|
const value = state.getValueAtPath(segments);
|
|
536
|
-
const original = state.originals.get(
|
|
609
|
+
const original = state.originals.get(key)?.value;
|
|
537
610
|
let pristine = true;
|
|
538
611
|
let blank = true;
|
|
539
612
|
let dirty = false;
|
|
540
613
|
let focused = false;
|
|
541
614
|
let blurred = false;
|
|
542
615
|
let touched = false;
|
|
616
|
+
let interacted = false;
|
|
617
|
+
let blurredAfterInteraction = false;
|
|
543
618
|
let connected = false;
|
|
544
619
|
let validating = false;
|
|
545
620
|
let updatedAt = null;
|
|
546
621
|
let asyncPending = false;
|
|
547
|
-
for (const [, entry] of state.originals) {
|
|
622
|
+
for (const [leafKey, entry] of state.originals) {
|
|
548
623
|
if (!isPathPrefix(segments, entry.segments)) continue;
|
|
549
624
|
if (segments.length === entry.segments.length) continue;
|
|
550
625
|
if (!hasAtPath(formValue, entry.segments)) continue;
|
|
551
|
-
const leafKey = canonicalizePath(entry.segments).key;
|
|
552
626
|
const leafRecord = state.fields.get(leafKey);
|
|
553
|
-
if (!state.
|
|
627
|
+
if (!state.isPristineAtPathByKey(leafKey, entry.segments)) {
|
|
554
628
|
pristine = false;
|
|
555
629
|
dirty = true;
|
|
556
630
|
}
|
|
@@ -558,14 +632,20 @@ function buildContainerFieldStateBase(state, segments, _key) {
|
|
|
558
632
|
if (leafRecord?.focused === true) focused = true;
|
|
559
633
|
if (leafRecord?.blurred === true) blurred = true;
|
|
560
634
|
if (leafRecord?.touched === true) touched = true;
|
|
635
|
+
if (leafRecord?.interacted === true) interacted = true;
|
|
636
|
+
if (leafRecord?.blurredAfterInteraction === true) blurredAfterInteraction = true;
|
|
561
637
|
if (leafRecord?.connected === true) connected = true;
|
|
562
638
|
if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0) validating = true;
|
|
563
|
-
if (state.
|
|
639
|
+
if (state.pathHasAsyncValidationByKey(leafKey, entry.segments)) asyncPending = true;
|
|
564
640
|
const ts = leafRecord?.updatedAt;
|
|
565
641
|
if (ts !== void 0 && ts !== null) {
|
|
566
642
|
if (updatedAt === null || ts > updatedAt) updatedAt = ts;
|
|
567
643
|
}
|
|
568
644
|
}
|
|
645
|
+
if (!dirty && state.hasStructuralChangeUnder(segments)) {
|
|
646
|
+
pristine = false;
|
|
647
|
+
dirty = true;
|
|
648
|
+
}
|
|
569
649
|
const errors = aggregateErrorsAt(state, segments);
|
|
570
650
|
if (!asyncPending && state.pathHasAsyncValidation(segments)) asyncPending = true;
|
|
571
651
|
const gated = asyncPending && !state.firstValidationDone.value;
|
|
@@ -581,6 +661,8 @@ function buildContainerFieldStateBase(state, segments, _key) {
|
|
|
581
661
|
focused,
|
|
582
662
|
blurred,
|
|
583
663
|
touched,
|
|
664
|
+
interacted,
|
|
665
|
+
blurredAfterInteraction,
|
|
584
666
|
connected,
|
|
585
667
|
element: null,
|
|
586
668
|
elements: EMPTY_ELEMENTS,
|
|
@@ -589,6 +671,8 @@ function buildContainerFieldStateBase(state, segments, _key) {
|
|
|
589
671
|
validating,
|
|
590
672
|
valid,
|
|
591
673
|
path: segments,
|
|
674
|
+
...computeFieldIdentity(formInstanceId, state.formKey, key),
|
|
675
|
+
key: state.arrayElementKey(segments),
|
|
592
676
|
blank,
|
|
593
677
|
label,
|
|
594
678
|
description: resolved.description,
|
|
@@ -596,15 +680,36 @@ function buildContainerFieldStateBase(state, segments, _key) {
|
|
|
596
680
|
meta: resolved.meta
|
|
597
681
|
};
|
|
598
682
|
}
|
|
599
|
-
function buildContainerFieldState(state, segments, key, getFormMetaBase,
|
|
600
|
-
const base = buildContainerFieldStateBase(state, segments);
|
|
601
|
-
return decorateWithDerivedProps(base, state, getFormMetaBase,
|
|
683
|
+
function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
|
|
684
|
+
const base = buildContainerFieldStateBase(state, segments, key, formInstanceId);
|
|
685
|
+
return decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState);
|
|
602
686
|
}
|
|
603
|
-
function decorateWithDerivedProps(base, state, getFormMetaBase,
|
|
687
|
+
function decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState) {
|
|
604
688
|
const firstError = base.errors[0];
|
|
605
|
-
const predicate =
|
|
606
|
-
const
|
|
607
|
-
|
|
689
|
+
const predicate = getDisplayState ?? state.getDisplayState;
|
|
690
|
+
const formMeta = getFormMetaBase();
|
|
691
|
+
let displayState;
|
|
692
|
+
try {
|
|
693
|
+
displayState = predicate(base, formMeta);
|
|
694
|
+
} catch (err) {
|
|
695
|
+
if (__DEV__ && !warnedDisplayStatePredicates.has(predicate)) {
|
|
696
|
+
warnedDisplayStatePredicates.add(predicate);
|
|
697
|
+
console.warn(
|
|
698
|
+
"[attaform] custom getDisplayState threw \u2014 falling back to defaultDisplayState. Subsequent throws from the same predicate will not warn again.",
|
|
699
|
+
err
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
displayState = defaultDisplayState(base, formMeta);
|
|
703
|
+
}
|
|
704
|
+
return {
|
|
705
|
+
...base,
|
|
706
|
+
displayState,
|
|
707
|
+
showErrors: displayState === "error",
|
|
708
|
+
showPending: displayState === "pending",
|
|
709
|
+
showSuccess: displayState === "success",
|
|
710
|
+
showIdle: displayState === "idle",
|
|
711
|
+
firstError
|
|
712
|
+
};
|
|
608
713
|
}
|
|
609
714
|
function aggregateErrorsAt(state, prefix) {
|
|
610
715
|
const formValue = state.form.value;
|
|
@@ -630,6 +735,41 @@ function aggregateErrorsAt(state, prefix) {
|
|
|
630
735
|
}
|
|
631
736
|
const EMPTY_ELEMENTS = Object.freeze([]);
|
|
632
737
|
|
|
738
|
+
function liveKeysAtPath(state, segments) {
|
|
739
|
+
const value = getAtPath(state.form.value, segments);
|
|
740
|
+
if (value === null || value === void 0) return [];
|
|
741
|
+
if (Array.isArray(value)) {
|
|
742
|
+
const keys = new Array(value.length);
|
|
743
|
+
for (let i = 0; i < value.length; i += 1) keys[i] = String(i);
|
|
744
|
+
return keys;
|
|
745
|
+
}
|
|
746
|
+
if (typeof value === "object") return Object.keys(value);
|
|
747
|
+
return [];
|
|
748
|
+
}
|
|
749
|
+
function isArrayPath(state, segments) {
|
|
750
|
+
if (segments.length === 0) return false;
|
|
751
|
+
return Array.isArray(getAtPath(state.form.value, segments));
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function warnReadOnly(surface, action, key) {
|
|
755
|
+
if (!__DEV__) return;
|
|
756
|
+
const phrase = action === "write" ? `write to "${String(key)}"` : `${action} of "${String(key)}"`;
|
|
757
|
+
console.warn(
|
|
758
|
+
`[attaform] ${surface} is read-only \u2014 ${phrase} was ignored. Mutate the form via setValue / the directive / field-array helpers instead.`
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
function makeReadonlyCoercion(snapshot) {
|
|
762
|
+
const toString = () => JSON.stringify(snapshot());
|
|
763
|
+
return {
|
|
764
|
+
toString,
|
|
765
|
+
valueOf() {
|
|
766
|
+
return this;
|
|
767
|
+
},
|
|
768
|
+
toJSON: snapshot,
|
|
769
|
+
toPrimitive: (hint) => hint === "number" ? NaN : toString()
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
|
|
633
773
|
const INTEGER_SEGMENT = /^(?:0|[1-9]\d*)$/;
|
|
634
774
|
function keyToSegment(key) {
|
|
635
775
|
return INTEGER_SEGMENT.test(key) ? Number(key) : key;
|
|
@@ -661,12 +801,12 @@ function buildSurfaceProxy(opts) {
|
|
|
661
801
|
const existing = containerCache.get(cacheKey);
|
|
662
802
|
if (existing !== void 0) return existing;
|
|
663
803
|
const snapshotContainer = () => opts.materializeContainer === void 0 ? {} : opts.materializeContainer(segments);
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
804
|
+
const {
|
|
805
|
+
toString: containerToString,
|
|
806
|
+
valueOf: containerValueOf,
|
|
807
|
+
toJSON: containerToJSON,
|
|
808
|
+
toPrimitive: containerToPrimitive
|
|
809
|
+
} = makeReadonlyCoercion(snapshotContainer);
|
|
670
810
|
const target = isArrayLike ? [] : (() => {
|
|
671
811
|
});
|
|
672
812
|
const proxy = new Proxy(target, {
|
|
@@ -691,6 +831,9 @@ function buildSurfaceProxy(opts) {
|
|
|
691
831
|
return opts.containerOwnKeys === void 0 ? 0 : opts.containerOwnKeys(segments).length;
|
|
692
832
|
}
|
|
693
833
|
const childSegs = [...segments, keyToSegment(key)];
|
|
834
|
+
if ((isArrayLike || opts.isArrayContainer?.(segments) === true) && typeof keyToSegment(key) === "string" && key in Array.prototype) {
|
|
835
|
+
return Reflect.get(Array.prototype, key);
|
|
836
|
+
}
|
|
694
837
|
if (key === "toString" || key === "valueOf") {
|
|
695
838
|
if (!schemaHasPath(childSegs)) {
|
|
696
839
|
return key === "toString" ? containerToString : containerValueOf;
|
|
@@ -735,10 +878,25 @@ function buildSurfaceProxy(opts) {
|
|
|
735
878
|
};
|
|
736
879
|
},
|
|
737
880
|
// Block writes at the proxy boundary. Mutations go through
|
|
738
|
-
// `setValue`, the directive, or the field-array helpers.
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
881
|
+
// `setValue`, the directive, or the field-array helpers. Each
|
|
882
|
+
// trap returns `true` (warn-and-noop) — returning `false` from a
|
|
883
|
+
// `set`/`delete`/`defineProperty` trap throws `TypeError` under
|
|
884
|
+
// strict mode (every ESM / `<script setup>`), which would surface
|
|
885
|
+
// a host-level exception in consumer code that the library
|
|
886
|
+
// documents as "writes are ignored." Aligns with the contract on
|
|
887
|
+
// `form.values` / `wizard.statuses`.
|
|
888
|
+
set: (_, key) => {
|
|
889
|
+
warnReadOnly("form.fields / form.errors", "write", key);
|
|
890
|
+
return true;
|
|
891
|
+
},
|
|
892
|
+
deleteProperty: (_, key) => {
|
|
893
|
+
warnReadOnly("form.fields / form.errors", "delete", key);
|
|
894
|
+
return true;
|
|
895
|
+
},
|
|
896
|
+
defineProperty: (_, key) => {
|
|
897
|
+
warnReadOnly("form.fields / form.errors", "define", key);
|
|
898
|
+
return true;
|
|
899
|
+
}
|
|
742
900
|
});
|
|
743
901
|
containerCache.set(cacheKey, proxy);
|
|
744
902
|
return proxy;
|
|
@@ -760,11 +918,12 @@ function buildSurfaceProxy(opts) {
|
|
|
760
918
|
}
|
|
761
919
|
return snapshot;
|
|
762
920
|
};
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
921
|
+
const {
|
|
922
|
+
toString: leafToString,
|
|
923
|
+
valueOf: leafValueOf,
|
|
924
|
+
toJSON: leafToJSONHandler,
|
|
925
|
+
toPrimitive: leafToPrimitive
|
|
926
|
+
} = makeReadonlyCoercion(snapshotLeaf);
|
|
768
927
|
const target = (() => {
|
|
769
928
|
});
|
|
770
929
|
const proxy = new Proxy(target, {
|
|
@@ -782,7 +941,7 @@ function buildSurfaceProxy(opts) {
|
|
|
782
941
|
if (typeof key !== "string") return void 0;
|
|
783
942
|
if (key === "toString") return leafToString;
|
|
784
943
|
if (key === "valueOf") return leafValueOf;
|
|
785
|
-
if (key === "toJSON") return
|
|
944
|
+
if (key === "toJSON") return leafToJSONHandler;
|
|
786
945
|
if (leafKeys.has(key)) {
|
|
787
946
|
const leaf = opts.resolveLeaf(segments);
|
|
788
947
|
return readLeafKey(leaf, key);
|
|
@@ -809,9 +968,22 @@ function buildSurfaceProxy(opts) {
|
|
|
809
968
|
writable: false
|
|
810
969
|
};
|
|
811
970
|
},
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
971
|
+
// Same warn-and-noop contract as the container traps above.
|
|
972
|
+
// Returning `true` keeps strict-mode callers from throwing on
|
|
973
|
+
// `form.fields.email.value = …`; the actual readonly guarantee
|
|
974
|
+
// is the absence of any mutation, not the host-level reject.
|
|
975
|
+
set: (_, key) => {
|
|
976
|
+
warnReadOnly("form.fields.<leaf>", "write", key);
|
|
977
|
+
return true;
|
|
978
|
+
},
|
|
979
|
+
deleteProperty: (_, key) => {
|
|
980
|
+
warnReadOnly("form.fields.<leaf>", "delete", key);
|
|
981
|
+
return true;
|
|
982
|
+
},
|
|
983
|
+
defineProperty: (_, key) => {
|
|
984
|
+
warnReadOnly("form.fields.<leaf>", "define", key);
|
|
985
|
+
return true;
|
|
986
|
+
}
|
|
815
987
|
});
|
|
816
988
|
leafViewCache.set(cacheKey, proxy);
|
|
817
989
|
return proxy;
|
|
@@ -875,33 +1047,53 @@ function buildErrorsProxy(state) {
|
|
|
875
1047
|
// working — the call-form just extends that semantic to
|
|
876
1048
|
// containers and dynamic paths.
|
|
877
1049
|
resolveCallTarget: (path) => aggregateErrorsAt(state, path),
|
|
878
|
-
//
|
|
879
|
-
//
|
|
880
|
-
//
|
|
881
|
-
//
|
|
882
|
-
//
|
|
883
|
-
|
|
884
|
-
|
|
1050
|
+
// Enumeration unions the live form-data keys at this path with the
|
|
1051
|
+
// first-child segments drawn from every error store. Without the
|
|
1052
|
+
// union, `Object.keys(form.errors)` / `{...form.errors}` /
|
|
1053
|
+
// `v-for="(errs, k) in form.errors"` silently dropped two
|
|
1054
|
+
// important error classes that the dot / call / JSON.stringify
|
|
1055
|
+
// surfaces already exposed:
|
|
1056
|
+
//
|
|
1057
|
+
// - **Form-level** errors at the synthetic `['']` path (set via
|
|
1058
|
+
// `setFormErrors` or root cross-field refines).
|
|
1059
|
+
// - **Server-only** errors at a key the schema doesn't know
|
|
1060
|
+
// about (`['ghost']`, `['address', 'ghost']`).
|
|
1061
|
+
//
|
|
1062
|
+
// The union closes that gap so `ownKeys` agrees with the rest of
|
|
1063
|
+
// the surface. Active-path filter mirrors `resolveLeaf`:
|
|
1064
|
+
// library-produced verdicts (schema + derived-blank) at unreachable
|
|
1065
|
+
// paths stay hidden; user-supplied errors are unconditional.
|
|
1066
|
+
containerOwnKeys: (segments) => errorAwareContainerKeys(state, segments),
|
|
1067
|
+
isArrayContainer: (segments) => isArrayPath(state, segments)
|
|
885
1068
|
});
|
|
886
1069
|
}
|
|
887
|
-
function
|
|
888
|
-
const
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
const
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1070
|
+
function errorAwareContainerKeys(state, segments) {
|
|
1071
|
+
const keys = new Set(liveKeysAtPath(state, segments));
|
|
1072
|
+
const formValue = state.form.value;
|
|
1073
|
+
const walk = (store, applyActivePathFilter) => {
|
|
1074
|
+
for (const [pathKey, errors] of store) {
|
|
1075
|
+
if (errors.length === 0) continue;
|
|
1076
|
+
if (pathKey === FORM_ERRORS_PATH_KEY) {
|
|
1077
|
+
if (segments.length === 0) keys.add("");
|
|
1078
|
+
continue;
|
|
1079
|
+
}
|
|
1080
|
+
const decoded = segmentsForPathKey(pathKey);
|
|
1081
|
+
if (decoded === null) continue;
|
|
1082
|
+
if (decoded.length <= segments.length) continue;
|
|
1083
|
+
if (!isPathPrefix(segments, decoded)) continue;
|
|
1084
|
+
if (applyActivePathFilter && !hasAtPath(formValue, decoded)) continue;
|
|
1085
|
+
const nextSeg = decoded[segments.length];
|
|
1086
|
+
keys.add(typeof nextSeg === "number" ? String(nextSeg) : nextSeg);
|
|
1087
|
+
}
|
|
1088
|
+
};
|
|
1089
|
+
walk(state.schemaErrors, true);
|
|
1090
|
+
walk(state.derivedBlankErrors.value, true);
|
|
1091
|
+
walk(state.userErrors, false);
|
|
1092
|
+
return [...keys];
|
|
901
1093
|
}
|
|
902
1094
|
function materializeErrors(state, containerSegments) {
|
|
903
1095
|
const liveContainer = getAtPath(state.form.value, containerSegments);
|
|
904
|
-
const tree = Array.isArray(liveContainer) ? [] :
|
|
1096
|
+
const tree = Array.isArray(liveContainer) ? [] : /* @__PURE__ */ Object.create(null);
|
|
905
1097
|
const collect = (store, applyActivePathFilter) => {
|
|
906
1098
|
entries: for (const [pathKey, errors] of store) {
|
|
907
1099
|
if (errors.length === 0) continue;
|
|
@@ -951,7 +1143,7 @@ function placeAt(tree, path, errors) {
|
|
|
951
1143
|
const cursorRecord2 = cursor;
|
|
952
1144
|
let child = cursorRecord2[key];
|
|
953
1145
|
if (child === null || child === void 0 || typeof child !== "object") {
|
|
954
|
-
child = typeof nextSeg === "number" ? [] :
|
|
1146
|
+
child = typeof nextSeg === "number" ? [] : /* @__PURE__ */ Object.create(null);
|
|
955
1147
|
cursorRecord2[key] = child;
|
|
956
1148
|
}
|
|
957
1149
|
cursor = child;
|
|
@@ -986,19 +1178,20 @@ function buildFieldArrayApi(state) {
|
|
|
986
1178
|
prepend(path, value) {
|
|
987
1179
|
const next = readArray(path);
|
|
988
1180
|
next.unshift(value);
|
|
989
|
-
return writeArray(path, next, { kind: "
|
|
1181
|
+
return writeArray(path, next, { kind: "insert", index: 0 });
|
|
990
1182
|
},
|
|
991
1183
|
insert(path, index, value) {
|
|
992
1184
|
const next = readArray(path);
|
|
993
|
-
next.
|
|
994
|
-
const
|
|
995
|
-
|
|
1185
|
+
const preLen = next.length;
|
|
1186
|
+
const insertIndex = index < 0 ? Math.max(0, preLen + index) : Math.min(index, preLen);
|
|
1187
|
+
next.splice(insertIndex, 0, value);
|
|
1188
|
+
return writeArray(path, next, { kind: "insert", index: insertIndex });
|
|
996
1189
|
},
|
|
997
1190
|
remove(path, index) {
|
|
998
1191
|
const next = readArray(path);
|
|
999
1192
|
if (index < 0 || index >= next.length) return false;
|
|
1000
1193
|
next.splice(index, 1);
|
|
1001
|
-
return writeArray(path, next, { kind: "
|
|
1194
|
+
return writeArray(path, next, { kind: "remove", index });
|
|
1002
1195
|
},
|
|
1003
1196
|
swap(path, a, b) {
|
|
1004
1197
|
const next = readArray(path);
|
|
@@ -1016,11 +1209,7 @@ function buildFieldArrayApi(state) {
|
|
|
1016
1209
|
const [item] = next.splice(from, 1);
|
|
1017
1210
|
const clampedTo = Math.max(0, Math.min(to, next.length));
|
|
1018
1211
|
next.splice(clampedTo, 0, item);
|
|
1019
|
-
return writeArray(path, next, {
|
|
1020
|
-
kind: "shift-range",
|
|
1021
|
-
fromIndex: Math.min(from, clampedTo),
|
|
1022
|
-
toIndex: Math.max(from, clampedTo)
|
|
1023
|
-
});
|
|
1212
|
+
return writeArray(path, next, { kind: "move", from, to: clampedTo });
|
|
1024
1213
|
},
|
|
1025
1214
|
replace(path, index, value) {
|
|
1026
1215
|
const next = readArray(path);
|
|
@@ -1039,6 +1228,8 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
|
|
|
1039
1228
|
"focused",
|
|
1040
1229
|
"blurred",
|
|
1041
1230
|
"touched",
|
|
1231
|
+
"interacted",
|
|
1232
|
+
"blurredAfterInteraction",
|
|
1042
1233
|
"connected",
|
|
1043
1234
|
"element",
|
|
1044
1235
|
"elements",
|
|
@@ -1046,20 +1237,28 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
|
|
|
1046
1237
|
"errors",
|
|
1047
1238
|
"validating",
|
|
1048
1239
|
"valid",
|
|
1240
|
+
"displayState",
|
|
1049
1241
|
"showErrors",
|
|
1242
|
+
"showPending",
|
|
1243
|
+
"showSuccess",
|
|
1244
|
+
"showIdle",
|
|
1050
1245
|
"firstError",
|
|
1051
1246
|
"path",
|
|
1247
|
+
"id",
|
|
1248
|
+
"aria",
|
|
1249
|
+
"key",
|
|
1052
1250
|
"blank",
|
|
1053
1251
|
"label",
|
|
1054
1252
|
"description",
|
|
1055
1253
|
"placeholder",
|
|
1056
1254
|
"meta"
|
|
1057
1255
|
]);
|
|
1058
|
-
function buildFieldStateProxy(state, getFormMetaBase, options) {
|
|
1256
|
+
function buildFieldStateProxy(state, formInstanceId, getFormMetaBase, options) {
|
|
1059
1257
|
const getFieldStateAt = buildFieldStateAccessor(
|
|
1060
1258
|
state,
|
|
1259
|
+
formInstanceId,
|
|
1061
1260
|
getFormMetaBase,
|
|
1062
|
-
options?.
|
|
1261
|
+
options?.getDisplayState !== void 0 ? { getDisplayState: options.getDisplayState } : void 0
|
|
1063
1262
|
);
|
|
1064
1263
|
const snapshotFieldStateAt = (path) => {
|
|
1065
1264
|
const view = getFieldStateAt(path).value;
|
|
@@ -1108,9 +1307,34 @@ function buildFieldStateProxy(state, getFormMetaBase, options) {
|
|
|
1108
1307
|
writable: false
|
|
1109
1308
|
};
|
|
1110
1309
|
},
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1310
|
+
// Warn-and-noop: returning `false` here throws `TypeError` under
|
|
1311
|
+
// strict mode (`form.fields('email').value = 1` from any ESM
|
|
1312
|
+
// module), which would violate the documented "writes are
|
|
1313
|
+
// ignored" contract. Mirrors `values-proxy` / `wizard-statuses-proxy`.
|
|
1314
|
+
set: (_, key) => {
|
|
1315
|
+
if (__DEV__) {
|
|
1316
|
+
console.warn(
|
|
1317
|
+
`[attaform] form.fields(path) is read-only \u2014 write to "${String(key)}" was ignored. Use form.setValue / the directive / field-array helpers instead.`
|
|
1318
|
+
);
|
|
1319
|
+
}
|
|
1320
|
+
return true;
|
|
1321
|
+
},
|
|
1322
|
+
deleteProperty: (_, key) => {
|
|
1323
|
+
if (__DEV__) {
|
|
1324
|
+
console.warn(
|
|
1325
|
+
`[attaform] form.fields(path) is read-only \u2014 delete of "${String(key)}" was ignored.`
|
|
1326
|
+
);
|
|
1327
|
+
}
|
|
1328
|
+
return true;
|
|
1329
|
+
},
|
|
1330
|
+
defineProperty: (_, key) => {
|
|
1331
|
+
if (__DEV__) {
|
|
1332
|
+
console.warn(
|
|
1333
|
+
`[attaform] form.fields(path) is read-only \u2014 define of "${String(key)}" was ignored.`
|
|
1334
|
+
);
|
|
1335
|
+
}
|
|
1336
|
+
return true;
|
|
1337
|
+
}
|
|
1114
1338
|
});
|
|
1115
1339
|
terminalCache.set(cacheKey, proxy);
|
|
1116
1340
|
return proxy;
|
|
@@ -1126,21 +1350,6 @@ function buildFieldStateProxy(state, getFormMetaBase, options) {
|
|
|
1126
1350
|
isArrayContainer: (segments) => isArrayPath(state, segments)
|
|
1127
1351
|
});
|
|
1128
1352
|
}
|
|
1129
|
-
function liveKeysAtPath(state, segments) {
|
|
1130
|
-
const value = getAtPath(state.form.value, segments);
|
|
1131
|
-
if (value === null || value === void 0) return [];
|
|
1132
|
-
if (Array.isArray(value)) {
|
|
1133
|
-
const keys = new Array(value.length);
|
|
1134
|
-
for (let i = 0; i < value.length; i += 1) keys[i] = String(i);
|
|
1135
|
-
return keys;
|
|
1136
|
-
}
|
|
1137
|
-
if (typeof value === "object") return Object.keys(value);
|
|
1138
|
-
return [];
|
|
1139
|
-
}
|
|
1140
|
-
function isArrayPath(state, segments) {
|
|
1141
|
-
if (segments.length === 0) return false;
|
|
1142
|
-
return Array.isArray(getAtPath(state.form.value, segments));
|
|
1143
|
-
}
|
|
1144
1353
|
function materializeFields(state, containerSegments, snapshotFieldStateAt) {
|
|
1145
1354
|
const liveValue = getAtPath(state.form.value, containerSegments);
|
|
1146
1355
|
return walk$2(liveValue, containerSegments, state.schema, snapshotFieldStateAt);
|
|
@@ -1166,29 +1375,6 @@ function walk$2(value, basePath, schema, snapshotFieldStateAt) {
|
|
|
1166
1375
|
return result;
|
|
1167
1376
|
}
|
|
1168
1377
|
|
|
1169
|
-
const DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS = 0;
|
|
1170
|
-
const DEFAULT_PERSISTENCE_DEBOUNCE_MS = 300;
|
|
1171
|
-
const DEFAULT_HISTORY_MAX_SNAPSHOTS = 128;
|
|
1172
|
-
const PERSISTENCE_KEY_PREFIX = "attaform:";
|
|
1173
|
-
const RESERVED_KEY_PREFIX = "__atta:";
|
|
1174
|
-
const ANONYMOUS_FORM_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon:`;
|
|
1175
|
-
const ANONYMOUS_WIZARD_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon-wizard:`;
|
|
1176
|
-
const DEFAULT_MAX_RECURSION_DEPTH = 64;
|
|
1177
|
-
function normalizeNumericOption(config) {
|
|
1178
|
-
const { value, source, allowInfinity, min, defaultValue } = config;
|
|
1179
|
-
if (allowInfinity && value === Infinity) return Infinity;
|
|
1180
|
-
if (typeof value !== "number" || Number.isNaN(value) || value === Infinity || value === -Infinity) {
|
|
1181
|
-
if (__DEV__) {
|
|
1182
|
-
const acceptedDescription = allowInfinity ? "a non-negative integer or Infinity" : "a non-negative finite integer";
|
|
1183
|
-
console.warn(
|
|
1184
|
-
`[attaform] ${source} must be ${acceptedDescription}; got ${String(value)}. Falling back to ${String(defaultValue)}.`
|
|
1185
|
-
);
|
|
1186
|
-
}
|
|
1187
|
-
return defaultValue;
|
|
1188
|
-
}
|
|
1189
|
-
return Math.max(min, Math.floor(value));
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
1378
|
const PERSISTENCE_MODULE_KEY = "persistence";
|
|
1193
1379
|
async function getStorageAdapter(storage) {
|
|
1194
1380
|
if (typeof storage === "object") return storage;
|
|
@@ -1207,7 +1393,7 @@ async function getStorageAdapter(storage) {
|
|
|
1207
1393
|
}
|
|
1208
1394
|
}
|
|
1209
1395
|
}
|
|
1210
|
-
const PERSISTED_ENVELOPE_VERSION =
|
|
1396
|
+
const PERSISTED_ENVELOPE_VERSION = 6;
|
|
1211
1397
|
function readPersistedPayload(value) {
|
|
1212
1398
|
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
1213
1399
|
const envelope = value;
|
|
@@ -1231,12 +1417,7 @@ function warnVersionMismatch(observedVersion) {
|
|
|
1231
1417
|
function buildPersistedPayload(form, include, schemaErrors, userErrors, blankPaths) {
|
|
1232
1418
|
let transientList;
|
|
1233
1419
|
if (blankPaths !== void 0 && blankPaths.size > 0) {
|
|
1234
|
-
|
|
1235
|
-
for (const key of blankPaths) {
|
|
1236
|
-
const d = pathKeyToDotted(key);
|
|
1237
|
-
if (d !== null) dotted.push(d);
|
|
1238
|
-
}
|
|
1239
|
-
transientList = dotted.length > 0 ? dotted : void 0;
|
|
1420
|
+
transientList = [...blankPaths];
|
|
1240
1421
|
}
|
|
1241
1422
|
if (include === "form") {
|
|
1242
1423
|
if (transientList === void 0) return { v: PERSISTED_ENVELOPE_VERSION, data: { form } };
|
|
@@ -1258,30 +1439,35 @@ function buildPersistedPayload(form, include, schemaErrors, userErrors, blankPat
|
|
|
1258
1439
|
function createDebouncedWriter(write, debounceMs) {
|
|
1259
1440
|
let timer = null;
|
|
1260
1441
|
let pending = null;
|
|
1442
|
+
let writeGeneration = 0;
|
|
1443
|
+
function runWrite() {
|
|
1444
|
+
const gen = ++writeGeneration;
|
|
1445
|
+
pending = write().finally(() => {
|
|
1446
|
+
if (writeGeneration === gen) pending = null;
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1261
1449
|
function schedule() {
|
|
1262
1450
|
if (timer !== null) clearTimeout(timer);
|
|
1263
1451
|
if (debounceMs === 0) {
|
|
1264
|
-
|
|
1265
|
-
pending = null;
|
|
1266
|
-
});
|
|
1452
|
+
runWrite();
|
|
1267
1453
|
return;
|
|
1268
1454
|
}
|
|
1269
1455
|
timer = setTimeout(() => {
|
|
1270
1456
|
timer = null;
|
|
1271
|
-
|
|
1272
|
-
pending = null;
|
|
1273
|
-
});
|
|
1457
|
+
runWrite();
|
|
1274
1458
|
}, debounceMs);
|
|
1275
1459
|
}
|
|
1276
1460
|
async function flush() {
|
|
1277
1461
|
if (timer !== null) {
|
|
1278
1462
|
clearTimeout(timer);
|
|
1279
1463
|
timer = null;
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1464
|
+
runWrite();
|
|
1465
|
+
}
|
|
1466
|
+
while (pending !== null) {
|
|
1467
|
+
const awaited = pending;
|
|
1468
|
+
await awaited;
|
|
1469
|
+
if (pending === awaited) break;
|
|
1283
1470
|
}
|
|
1284
|
-
if (pending !== null) await pending;
|
|
1285
1471
|
}
|
|
1286
1472
|
function cancel() {
|
|
1287
1473
|
if (timer !== null) {
|
|
@@ -1294,7 +1480,7 @@ function createDebouncedWriter(write, debounceMs) {
|
|
|
1294
1480
|
function resolveStorageKeyBase(config, formKey) {
|
|
1295
1481
|
return config.key ?? `${PERSISTENCE_KEY_PREFIX}${formKey}`;
|
|
1296
1482
|
}
|
|
1297
|
-
async function
|
|
1483
|
+
async function removeMatchingKeys(adapter, base, keepKey) {
|
|
1298
1484
|
let keys;
|
|
1299
1485
|
try {
|
|
1300
1486
|
keys = await adapter.listKeys(base);
|
|
@@ -1302,12 +1488,15 @@ async function cleanupOrphanKeys(adapter, base, currentKey) {
|
|
|
1302
1488
|
return;
|
|
1303
1489
|
}
|
|
1304
1490
|
for (const key of keys) {
|
|
1305
|
-
if (key ===
|
|
1491
|
+
if (key === keepKey) continue;
|
|
1306
1492
|
if (key === base || key.startsWith(`${base}:`)) {
|
|
1307
1493
|
void adapter.removeItem(key).catch(() => void 0);
|
|
1308
1494
|
}
|
|
1309
1495
|
}
|
|
1310
1496
|
}
|
|
1497
|
+
async function cleanupOrphanKeys(adapter, base, currentKey) {
|
|
1498
|
+
await removeMatchingKeys(adapter, base, currentKey);
|
|
1499
|
+
}
|
|
1311
1500
|
const STANDARD_STORAGE_KINDS = ["local", "session", "indexeddb"];
|
|
1312
1501
|
function normalizePersistConfig(input) {
|
|
1313
1502
|
if (typeof input === "string") return { storage: input };
|
|
@@ -1318,12 +1507,7 @@ async function sweepAllOrphansAcrossStandardStores(base) {
|
|
|
1318
1507
|
for (const kind of STANDARD_STORAGE_KINDS) {
|
|
1319
1508
|
try {
|
|
1320
1509
|
const adapter = await getStorageAdapter(kind);
|
|
1321
|
-
|
|
1322
|
-
for (const key of keys) {
|
|
1323
|
-
if (key === base || key.startsWith(`${base}:`)) {
|
|
1324
|
-
void adapter.removeItem(key).catch(() => void 0);
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1510
|
+
await removeMatchingKeys(adapter, base);
|
|
1327
1511
|
} catch {
|
|
1328
1512
|
}
|
|
1329
1513
|
}
|
|
@@ -1334,12 +1518,7 @@ async function sweepNonConfiguredStandardStoresForOrphans(configured, base) {
|
|
|
1334
1518
|
if (kind === configuredKind) continue;
|
|
1335
1519
|
try {
|
|
1336
1520
|
const adapter = await getStorageAdapter(kind);
|
|
1337
|
-
|
|
1338
|
-
for (const key of keys) {
|
|
1339
|
-
if (key === base || key.startsWith(`${base}:`)) {
|
|
1340
|
-
void adapter.removeItem(key).catch(() => void 0);
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1521
|
+
await removeMatchingKeys(adapter, base);
|
|
1343
1522
|
} catch {
|
|
1344
1523
|
}
|
|
1345
1524
|
}
|
|
@@ -1355,6 +1534,29 @@ function pluckPaths(form, pathKeys) {
|
|
|
1355
1534
|
}
|
|
1356
1535
|
return sparse ?? {};
|
|
1357
1536
|
}
|
|
1537
|
+
function stripUnacknowledgedSensitiveLeaves(form, optedInPaths, isSensitivePath) {
|
|
1538
|
+
const acknowledgedSensitive = [];
|
|
1539
|
+
for (const key of optedInPaths) {
|
|
1540
|
+
const segs = segmentsForPathKey(key);
|
|
1541
|
+
if (segs !== null && isSensitivePath(segs)) acknowledgedSensitive.push(segs);
|
|
1542
|
+
}
|
|
1543
|
+
const coveredByAcknowledged = (path) => acknowledgedSensitive.some((prefix) => isPathPrefix(prefix, path));
|
|
1544
|
+
const walk = (path, value) => {
|
|
1545
|
+
if (path.length > 0 && isSensitivePath(path) && !coveredByAcknowledged(path)) {
|
|
1546
|
+
return void 0;
|
|
1547
|
+
}
|
|
1548
|
+
if (value === null || typeof value !== "object") return value;
|
|
1549
|
+
if (Array.isArray(value)) return value.map((item, i) => walk([...path, i], item));
|
|
1550
|
+
if (!isPlainRecord(value)) return value;
|
|
1551
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
1552
|
+
for (const key of Object.keys(value)) {
|
|
1553
|
+
const walked = walk([...path, key], value[key]);
|
|
1554
|
+
if (walked !== void 0) out[key] = walked;
|
|
1555
|
+
}
|
|
1556
|
+
return out;
|
|
1557
|
+
};
|
|
1558
|
+
return walk([], form);
|
|
1559
|
+
}
|
|
1358
1560
|
function filterErrorsByPaths(errors, pathKeys) {
|
|
1359
1561
|
const out = /* @__PURE__ */ new Map();
|
|
1360
1562
|
for (const [key, value] of errors) {
|
|
@@ -1381,7 +1583,7 @@ function mergeDeep(target, source, path, schema) {
|
|
|
1381
1583
|
if (sourceDisc !== void 0) {
|
|
1382
1584
|
const variantDefault = du.getVariantDefault(sourceDisc);
|
|
1383
1585
|
if (isPlainRecord(variantDefault)) {
|
|
1384
|
-
const out2 =
|
|
1586
|
+
const out2 = Object.assign(/* @__PURE__ */ Object.create(null), variantDefault);
|
|
1385
1587
|
for (const key of Object.keys(sourceRecord)) {
|
|
1386
1588
|
if (!(key in variantDefault) && key !== du.discriminatorKey) continue;
|
|
1387
1589
|
out2[key] = mergeDeep(out2[key], sourceRecord[key], [...path, key], schema);
|
|
@@ -1393,7 +1595,7 @@ function mergeDeep(target, source, path, schema) {
|
|
|
1393
1595
|
}
|
|
1394
1596
|
}
|
|
1395
1597
|
const mergeTarget = target;
|
|
1396
|
-
const out = isPlainRecord(mergeTarget) ?
|
|
1598
|
+
const out = isPlainRecord(mergeTarget) ? Object.assign(/* @__PURE__ */ Object.create(null), mergeTarget) : /* @__PURE__ */ Object.create(null);
|
|
1397
1599
|
for (const key of Object.keys(source)) {
|
|
1398
1600
|
out[key] = mergeDeep(out[key], source[key], [...path, key], schema);
|
|
1399
1601
|
}
|
|
@@ -1496,39 +1698,44 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1496
1698
|
}
|
|
1497
1699
|
return result;
|
|
1498
1700
|
}
|
|
1499
|
-
async function
|
|
1701
|
+
async function runImperativeValidation(pathInput, config) {
|
|
1500
1702
|
const segments = pathInput === void 0 ? void 0 : toSegments(pathInput);
|
|
1501
1703
|
const dataAtPath = segments === void 0 ? state.form.value : state.getValueAtPath(segments);
|
|
1502
1704
|
try {
|
|
1503
1705
|
state.activeValidations.value += 1;
|
|
1504
|
-
state.cancelFieldValidation();
|
|
1706
|
+
if (config.cancelInFlight) state.cancelFieldValidation();
|
|
1505
1707
|
const refinement = await runRefinementValidation(dataAtPath, segments);
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1708
|
+
if (config.commitToSchemaErrors) {
|
|
1709
|
+
const scopePath = segments ?? [];
|
|
1710
|
+
const errors = refinement.success ? [] : refinement.errors;
|
|
1711
|
+
const reStamped = segments === void 0 ? errors : errors.map((err) => ({
|
|
1712
|
+
...err,
|
|
1713
|
+
path: [...segments, ...err.path]
|
|
1714
|
+
}));
|
|
1715
|
+
state.applySchemaErrorsForSubtree(scopePath, reStamped);
|
|
1716
|
+
}
|
|
1717
|
+
return { ok: true, refinement, segments };
|
|
1514
1718
|
} catch (err) {
|
|
1515
|
-
return adapterThrowResponse(err);
|
|
1719
|
+
return { ok: false, error: adapterThrowResponse(err) };
|
|
1516
1720
|
} finally {
|
|
1517
1721
|
state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
|
|
1518
1722
|
}
|
|
1519
1723
|
}
|
|
1724
|
+
async function validateAsync(pathInput) {
|
|
1725
|
+
const result = await runImperativeValidation(pathInput, {
|
|
1726
|
+
cancelInFlight: true,
|
|
1727
|
+
commitToSchemaErrors: true
|
|
1728
|
+
});
|
|
1729
|
+
if (!result.ok) return result.error;
|
|
1730
|
+
return stripData(composeWithDerivedBlank(result.refinement, result.segments));
|
|
1731
|
+
}
|
|
1520
1732
|
async function process(pathInput) {
|
|
1521
|
-
const
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
} catch (err) {
|
|
1528
|
-
return adapterThrowResponse(err);
|
|
1529
|
-
} finally {
|
|
1530
|
-
state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
|
|
1531
|
-
}
|
|
1733
|
+
const result = await runImperativeValidation(pathInput, {
|
|
1734
|
+
cancelInFlight: false,
|
|
1735
|
+
commitToSchemaErrors: false
|
|
1736
|
+
});
|
|
1737
|
+
if (!result.ok) return result.error;
|
|
1738
|
+
return composeWithDerivedBlank(result.refinement, result.segments);
|
|
1532
1739
|
}
|
|
1533
1740
|
function adapterThrowResponse(err) {
|
|
1534
1741
|
return {
|
|
@@ -1966,7 +2173,6 @@ function coerceValue(value, accepted, elementAccepted, index) {
|
|
|
1966
2173
|
}
|
|
1967
2174
|
|
|
1968
2175
|
const EMPTY_TRANSFORMS = Object.freeze([]);
|
|
1969
|
-
const INTERACTIVE_TAG_NAMES = /* @__PURE__ */ new Set(["INPUT", "SELECT", "TEXTAREA"]);
|
|
1970
2176
|
const attaformListenersSymbol = Symbol.for("attaform:focus-listeners");
|
|
1971
2177
|
function attachFocusListeners(state, segments, element, instanceMeta) {
|
|
1972
2178
|
const target = element;
|
|
@@ -1994,6 +2200,8 @@ function detachFocusListeners(element) {
|
|
|
1994
2200
|
function buildRegister(state, formInstanceId, instanceConfig) {
|
|
1995
2201
|
const coerceIndex = instanceConfig?.coerce !== void 0 ? resolveCoercionIndex(instanceConfig.coerce) : state.coerceIndex;
|
|
1996
2202
|
const instanceMeta = instanceConfig?.instanceMeta;
|
|
2203
|
+
const formAutoAria = instanceConfig?.autoAria ?? true;
|
|
2204
|
+
const getDisplayStateAt = instanceConfig?.getDisplayStateAt;
|
|
1997
2205
|
const withInstanceMeta = (meta) => {
|
|
1998
2206
|
if (instanceMeta === void 0) return meta;
|
|
1999
2207
|
return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
|
|
@@ -2015,7 +2223,11 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2015
2223
|
if (typed !== null && typeof raw === "number" && parseFloat(typed) === raw) {
|
|
2016
2224
|
return typed;
|
|
2017
2225
|
}
|
|
2018
|
-
|
|
2226
|
+
try {
|
|
2227
|
+
return String(raw);
|
|
2228
|
+
} catch {
|
|
2229
|
+
return Object.prototype.toString.call(raw);
|
|
2230
|
+
}
|
|
2019
2231
|
});
|
|
2020
2232
|
const slimDefault = state.schema.getDefaultAtPath(segments);
|
|
2021
2233
|
const slimTypes = state.schema.getSlimPrimitiveTypesAtPath(segments);
|
|
@@ -2048,6 +2260,10 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2048
2260
|
callSite: captureUserCallSite()
|
|
2049
2261
|
});
|
|
2050
2262
|
}
|
|
2263
|
+
const { aria } = computeFieldIdentity(formInstanceId, state.formKey, pathKey);
|
|
2264
|
+
const isRequired = state.schema.isRequiredAtPath(segments);
|
|
2265
|
+
const ariaEnabled = options?.autoAria ?? formAutoAria;
|
|
2266
|
+
const ariaDisplayState = getDisplayStateAt !== void 0 ? computed(() => getDisplayStateAt(segments)) : void 0;
|
|
2051
2267
|
const internalRv = {
|
|
2052
2268
|
innerRef,
|
|
2053
2269
|
displayValue,
|
|
@@ -2062,6 +2278,9 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2062
2278
|
})
|
|
2063
2279
|
);
|
|
2064
2280
|
},
|
|
2281
|
+
markInteracted: () => {
|
|
2282
|
+
state.markInteracted(segments);
|
|
2283
|
+
},
|
|
2065
2284
|
registerElement: (element) => {
|
|
2066
2285
|
if (!INTERACTIVE_TAG_NAMES.has(element.tagName)) return;
|
|
2067
2286
|
const added = state.registerElement(segments, element, formInstanceId);
|
|
@@ -2106,7 +2325,12 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2106
2325
|
coerce,
|
|
2107
2326
|
...coerceElement !== void 0 ? { coerceElement } : {},
|
|
2108
2327
|
acceptsUndefined,
|
|
2109
|
-
acceptsString
|
|
2328
|
+
acceptsString,
|
|
2329
|
+
// --- Aria (internal; consumed by the directive) ---
|
|
2330
|
+
aria,
|
|
2331
|
+
isRequired,
|
|
2332
|
+
ariaEnabled,
|
|
2333
|
+
...ariaDisplayState !== void 0 ? { ariaDisplayState } : {}
|
|
2110
2334
|
};
|
|
2111
2335
|
return shallowReadonly(internalRv);
|
|
2112
2336
|
};
|
|
@@ -2157,7 +2381,7 @@ function walk(input, segments, schema, paths) {
|
|
|
2157
2381
|
if (slim !== null && slim !== void 0 && typeof slim === "object" && !Array.isArray(slim) && !(slim instanceof Date) && !(slim instanceof RegExp) && !(slim instanceof Map) && !(slim instanceof Set)) {
|
|
2158
2382
|
for (const k of Object.keys(slim)) allKeys.add(k);
|
|
2159
2383
|
}
|
|
2160
|
-
const out =
|
|
2384
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
2161
2385
|
let mutated = allKeys.size !== inputKeys.length;
|
|
2162
2386
|
for (const key of allKeys) {
|
|
2163
2387
|
const orig = input[key];
|
|
@@ -2186,7 +2410,7 @@ function walkUnspecified(slim, segments, paths) {
|
|
|
2186
2410
|
}
|
|
2187
2411
|
if (Array.isArray(slim)) return slim;
|
|
2188
2412
|
if (slim !== null && typeof slim === "object") {
|
|
2189
|
-
const out =
|
|
2413
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
2190
2414
|
for (const key of Object.keys(slim)) {
|
|
2191
2415
|
out[key] = walkUnspecified(slim[key], [...segments, key], paths);
|
|
2192
2416
|
}
|
|
@@ -2219,7 +2443,7 @@ function substitute(input, segments, schema, paths) {
|
|
|
2219
2443
|
}
|
|
2220
2444
|
if (typeof input === "object") {
|
|
2221
2445
|
let mutated = false;
|
|
2222
|
-
const out =
|
|
2446
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
2223
2447
|
for (const key of Object.keys(input)) {
|
|
2224
2448
|
const orig = input[key];
|
|
2225
2449
|
const walked = substitute(orig, [...segments, key], schema, paths);
|
|
@@ -2271,70 +2495,86 @@ function expandUnsetAt(segments, schema, paths) {
|
|
|
2271
2495
|
return result;
|
|
2272
2496
|
}
|
|
2273
2497
|
|
|
2274
|
-
function
|
|
2275
|
-
const inner = computed(() => readonly(form.value));
|
|
2498
|
+
function buildCallableReadonlySnapshotProxy(opts) {
|
|
2276
2499
|
const target = (() => {
|
|
2277
2500
|
});
|
|
2278
|
-
const
|
|
2279
|
-
const
|
|
2501
|
+
const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(opts.snapshot);
|
|
2502
|
+
const callResolve = opts.resolveCall ?? ((arg) => opts.resolveKey(String(arg)));
|
|
2280
2503
|
return new Proxy(target, {
|
|
2281
2504
|
apply(_, __, args) {
|
|
2282
2505
|
const arg = args[0];
|
|
2283
|
-
if (arg === void 0) return
|
|
2284
|
-
|
|
2285
|
-
let cursor = inner.value;
|
|
2286
|
-
for (const seg of segments) {
|
|
2287
|
-
if (cursor === null || cursor === void 0) return void 0;
|
|
2288
|
-
cursor = cursor[seg];
|
|
2289
|
-
}
|
|
2290
|
-
return cursor;
|
|
2506
|
+
if (arg === void 0) return opts.snapshot();
|
|
2507
|
+
return callResolve(arg);
|
|
2291
2508
|
},
|
|
2292
2509
|
get(_, key) {
|
|
2293
2510
|
if (typeof key === "symbol") {
|
|
2294
|
-
if (key === Symbol.toPrimitive) return
|
|
2511
|
+
if (key === Symbol.toPrimitive) return toPrimitive;
|
|
2295
2512
|
return Reflect.get(target, key);
|
|
2296
2513
|
}
|
|
2297
|
-
if (key === "toJSON") return
|
|
2298
|
-
if (key === "toString") return
|
|
2299
|
-
if (key === "valueOf")
|
|
2300
|
-
|
|
2301
|
-
return this;
|
|
2302
|
-
};
|
|
2303
|
-
return inner.value[key];
|
|
2514
|
+
if (key === "toJSON") return toJSON;
|
|
2515
|
+
if (key === "toString") return toString;
|
|
2516
|
+
if (key === "valueOf") return valueOf;
|
|
2517
|
+
return opts.resolveKey(key);
|
|
2304
2518
|
},
|
|
2305
2519
|
has(_, key) {
|
|
2306
2520
|
if (typeof key === "symbol") return Reflect.has(target, key);
|
|
2307
|
-
return
|
|
2308
|
-
},
|
|
2309
|
-
ownKeys() {
|
|
2310
|
-
return Reflect.ownKeys(inner.value);
|
|
2521
|
+
return opts.hasKey(key);
|
|
2311
2522
|
},
|
|
2523
|
+
ownKeys: () => opts.ownKeys(),
|
|
2312
2524
|
getOwnPropertyDescriptor(_, key) {
|
|
2313
|
-
|
|
2314
|
-
if (
|
|
2315
|
-
return
|
|
2525
|
+
if (typeof key !== "string") return void 0;
|
|
2526
|
+
if (opts.describeKey !== void 0) return opts.describeKey(key);
|
|
2527
|
+
if (!opts.hasKey(key)) return void 0;
|
|
2528
|
+
return {
|
|
2529
|
+
configurable: true,
|
|
2530
|
+
enumerable: true,
|
|
2531
|
+
writable: false,
|
|
2532
|
+
value: opts.resolveKey(key)
|
|
2533
|
+
};
|
|
2316
2534
|
},
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
// TypeError in strict-mode consumers, surprising users who
|
|
2320
|
-
// assigned through the proxy and expected it to be ignored.
|
|
2321
|
-
set(_, key) {
|
|
2322
|
-
if (__DEV__) {
|
|
2323
|
-
console.warn(
|
|
2324
|
-
`[attaform] form.values is read-only \u2014 write to "${String(key)}" was ignored. Use form.setValue / the directive / field-array helpers instead.`
|
|
2325
|
-
);
|
|
2326
|
-
}
|
|
2535
|
+
set: (_, key) => {
|
|
2536
|
+
warnReadOnly(opts.surface, "write", key);
|
|
2327
2537
|
return true;
|
|
2328
2538
|
},
|
|
2329
|
-
deleteProperty(_, key) {
|
|
2330
|
-
|
|
2331
|
-
console.warn(
|
|
2332
|
-
`[attaform] form.values is read-only \u2014 delete of "${String(key)}" was ignored.`
|
|
2333
|
-
);
|
|
2334
|
-
}
|
|
2539
|
+
deleteProperty: (_, key) => {
|
|
2540
|
+
warnReadOnly(opts.surface, "delete", key);
|
|
2335
2541
|
return true;
|
|
2336
2542
|
},
|
|
2337
|
-
defineProperty: () =>
|
|
2543
|
+
defineProperty: (_, key) => {
|
|
2544
|
+
warnReadOnly(opts.surface, "define", key);
|
|
2545
|
+
return true;
|
|
2546
|
+
}
|
|
2547
|
+
});
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
function buildValuesProxy(form) {
|
|
2551
|
+
const inner = computed(() => readonly(form.value));
|
|
2552
|
+
return buildCallableReadonlySnapshotProxy({
|
|
2553
|
+
surface: "form.values",
|
|
2554
|
+
snapshot: () => inner.value,
|
|
2555
|
+
// Read through the readonly proxy at access time so Vue's
|
|
2556
|
+
// dependency tracking lands inside the consumer's active effect
|
|
2557
|
+
// — `inner.value[key]` is what triggers per-key tracking.
|
|
2558
|
+
resolveKey: (key) => inner.value[key],
|
|
2559
|
+
// Dynamic path: walk segments through the readonly proxy. Each
|
|
2560
|
+
// step reads through the proxy's own get traps so dependency
|
|
2561
|
+
// tracking propagates at every level.
|
|
2562
|
+
resolveCall: (arg) => {
|
|
2563
|
+
const { segments } = canonicalizePath(arg);
|
|
2564
|
+
let cursor = inner.value;
|
|
2565
|
+
for (const seg of segments) {
|
|
2566
|
+
if (cursor === null || cursor === void 0) return void 0;
|
|
2567
|
+
cursor = cursor[seg];
|
|
2568
|
+
}
|
|
2569
|
+
return cursor;
|
|
2570
|
+
},
|
|
2571
|
+
ownKeys: () => Reflect.ownKeys(inner.value),
|
|
2572
|
+
hasKey: (key) => Reflect.has(inner.value, key),
|
|
2573
|
+
describeKey: (key) => {
|
|
2574
|
+
const desc = Reflect.getOwnPropertyDescriptor(inner.value, key);
|
|
2575
|
+
if (desc !== void 0) desc.configurable = true;
|
|
2576
|
+
return desc;
|
|
2577
|
+
}
|
|
2338
2578
|
});
|
|
2339
2579
|
}
|
|
2340
2580
|
|
|
@@ -2350,15 +2590,34 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2350
2590
|
if (instanceMeta === void 0) return meta;
|
|
2351
2591
|
return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
|
|
2352
2592
|
};
|
|
2353
|
-
const
|
|
2354
|
-
|
|
2355
|
-
|
|
2593
|
+
const getFormMetaBase = () => {
|
|
2594
|
+
const rootBase = buildContainerFieldStateBase(state, ROOT_PATH, ROOT_PATH_KEY, formInstanceId);
|
|
2595
|
+
return {
|
|
2596
|
+
...rootBase,
|
|
2597
|
+
submitting: state.submitting.value,
|
|
2598
|
+
submissionAttempts: state.submissionAttempts.value,
|
|
2599
|
+
departAttempts: state.departAttempts.value,
|
|
2600
|
+
submitError: state.submitError.value,
|
|
2601
|
+
errorCount: rootBase.errors.length,
|
|
2602
|
+
submitted: state.submitted.value,
|
|
2603
|
+
instanceId: formInstanceId
|
|
2604
|
+
};
|
|
2356
2605
|
};
|
|
2357
|
-
const
|
|
2606
|
+
const fieldStateAccessorOptions = options.getDisplayState !== void 0 ? { getDisplayState: options.getDisplayState } : void 0;
|
|
2607
|
+
const getRootFieldStateAt = buildFieldStateAccessor(
|
|
2358
2608
|
state,
|
|
2359
2609
|
formInstanceId,
|
|
2360
|
-
|
|
2610
|
+
getFormMetaBase,
|
|
2611
|
+
fieldStateAccessorOptions
|
|
2361
2612
|
);
|
|
2613
|
+
const getDisplayStateAt = (segments) => getRootFieldStateAt(segments).value.displayState;
|
|
2614
|
+
const registerConfig = {
|
|
2615
|
+
...instanceMeta !== void 0 ? { instanceMeta } : {},
|
|
2616
|
+
...options.coerce !== void 0 ? { coerce: options.coerce } : {},
|
|
2617
|
+
...options.autoAria !== void 0 ? { autoAria: options.autoAria } : {},
|
|
2618
|
+
getDisplayStateAt
|
|
2619
|
+
};
|
|
2620
|
+
const register = buildRegister(state, formInstanceId, registerConfig);
|
|
2362
2621
|
const processOptions = options.onInvalidSubmit !== void 0 ? { onInvalidSubmit: options.onInvalidSubmit } : {};
|
|
2363
2622
|
const defaultInvalidSubmitPolicy = options.onInvalidSubmit ?? "focus-first-error";
|
|
2364
2623
|
const {
|
|
@@ -2381,10 +2640,18 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2381
2640
|
next,
|
|
2382
2641
|
state.schema
|
|
2383
2642
|
);
|
|
2643
|
+
const ok2 = state.setValueAtPath([], walked2.cleanedValues, withInstanceMeta());
|
|
2644
|
+
if (!ok2) return false;
|
|
2384
2645
|
for (const pathKey of walked2.paths) {
|
|
2385
|
-
|
|
2646
|
+
const blankSegments = segmentsForPathKey(pathKey);
|
|
2647
|
+
if (blankSegments === null) continue;
|
|
2648
|
+
state.setValueAtPath(
|
|
2649
|
+
blankSegments,
|
|
2650
|
+
state.getValueAtPath(blankSegments),
|
|
2651
|
+
withInstanceMeta({ blank: true })
|
|
2652
|
+
);
|
|
2386
2653
|
}
|
|
2387
|
-
return
|
|
2654
|
+
return true;
|
|
2388
2655
|
}
|
|
2389
2656
|
const segments = canonicalizePath(pathOrValue).segments;
|
|
2390
2657
|
const writeUnsetAt = () => {
|
|
@@ -2530,43 +2797,48 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2530
2797
|
const metaErrors = computed(
|
|
2531
2798
|
() => aggregateErrorsAt(state, [])
|
|
2532
2799
|
);
|
|
2533
|
-
const getFormMetaBase = () => {
|
|
2534
|
-
const rootBase = buildContainerFieldStateBase(state, ROOT_PATH);
|
|
2535
|
-
return {
|
|
2536
|
-
...rootBase,
|
|
2537
|
-
submitting: state.submitting.value,
|
|
2538
|
-
submissionAttempts: state.submissionAttempts.value,
|
|
2539
|
-
departAttempts: state.departAttempts.value,
|
|
2540
|
-
submitError: state.submitError.value,
|
|
2541
|
-
errorCount: rootBase.errors.length,
|
|
2542
|
-
submitted: state.submitted.value,
|
|
2543
|
-
instanceId: formInstanceId
|
|
2544
|
-
};
|
|
2545
|
-
};
|
|
2546
|
-
const fieldStateAccessorOptions = options.shouldShowErrors !== void 0 ? { shouldShowErrors: options.shouldShowErrors } : void 0;
|
|
2547
|
-
const getRootFieldStateAt = buildFieldStateAccessor(
|
|
2548
|
-
state,
|
|
2549
|
-
getFormMetaBase,
|
|
2550
|
-
fieldStateAccessorOptions
|
|
2551
|
-
);
|
|
2552
2800
|
const rootFieldState = getRootFieldStateAt([]);
|
|
2553
2801
|
const formMeta = readonly(
|
|
2554
2802
|
reactive({
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
pristine
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2803
|
+
get value() {
|
|
2804
|
+
return rootFieldState.value.value;
|
|
2805
|
+
},
|
|
2806
|
+
get original() {
|
|
2807
|
+
return rootFieldState.value.original;
|
|
2808
|
+
},
|
|
2809
|
+
get pristine() {
|
|
2810
|
+
return rootFieldState.value.pristine;
|
|
2811
|
+
},
|
|
2812
|
+
get dirty() {
|
|
2813
|
+
return rootFieldState.value.dirty;
|
|
2814
|
+
},
|
|
2815
|
+
get focused() {
|
|
2816
|
+
return rootFieldState.value.focused;
|
|
2817
|
+
},
|
|
2818
|
+
get blurred() {
|
|
2819
|
+
return rootFieldState.value.blurred;
|
|
2820
|
+
},
|
|
2821
|
+
get touched() {
|
|
2822
|
+
return rootFieldState.value.touched;
|
|
2823
|
+
},
|
|
2824
|
+
get interacted() {
|
|
2825
|
+
return rootFieldState.value.interacted;
|
|
2826
|
+
},
|
|
2827
|
+
get blurredAfterInteraction() {
|
|
2828
|
+
return rootFieldState.value.blurredAfterInteraction;
|
|
2829
|
+
},
|
|
2830
|
+
get connected() {
|
|
2831
|
+
return rootFieldState.value.connected;
|
|
2832
|
+
},
|
|
2833
|
+
get element() {
|
|
2834
|
+
return rootFieldState.value.element;
|
|
2835
|
+
},
|
|
2836
|
+
get elements() {
|
|
2837
|
+
return rootFieldState.value.elements;
|
|
2838
|
+
},
|
|
2839
|
+
get updatedAt() {
|
|
2840
|
+
return rootFieldState.value.updatedAt;
|
|
2841
|
+
},
|
|
2570
2842
|
// Whole-form validating mirrors the LIFECYCLE counter
|
|
2571
2843
|
// (`state.activeValidations`) ORed with any per-leaf validation
|
|
2572
2844
|
// in flight (via `rootFieldState.validating`). A submit-time
|
|
@@ -2584,19 +2856,56 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2584
2856
|
// keep the explicit form-level computation for the gate.
|
|
2585
2857
|
valid,
|
|
2586
2858
|
errors: metaErrors,
|
|
2587
|
-
// `
|
|
2588
|
-
// field-state computed as the rest of the
|
|
2589
|
-
// so `form.meta.
|
|
2590
|
-
// exactly — the predicate runs once
|
|
2591
|
-
// is shared.
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2859
|
+
// `displayState` / the `show*` booleans / `firstError` flow
|
|
2860
|
+
// through the same root field-state computed as the rest of the
|
|
2861
|
+
// FieldState surface, so `form.meta.displayState` matches
|
|
2862
|
+
// `form.fields().displayState` exactly — the predicate runs once
|
|
2863
|
+
// at the root and the result is shared.
|
|
2864
|
+
get displayState() {
|
|
2865
|
+
return rootFieldState.value.displayState;
|
|
2866
|
+
},
|
|
2867
|
+
get showErrors() {
|
|
2868
|
+
return rootFieldState.value.showErrors;
|
|
2869
|
+
},
|
|
2870
|
+
get showPending() {
|
|
2871
|
+
return rootFieldState.value.showPending;
|
|
2872
|
+
},
|
|
2873
|
+
get showSuccess() {
|
|
2874
|
+
return rootFieldState.value.showSuccess;
|
|
2875
|
+
},
|
|
2876
|
+
get showIdle() {
|
|
2877
|
+
return rootFieldState.value.showIdle;
|
|
2878
|
+
},
|
|
2879
|
+
get firstError() {
|
|
2880
|
+
return rootFieldState.value.firstError;
|
|
2881
|
+
},
|
|
2882
|
+
get path() {
|
|
2883
|
+
return rootFieldState.value.path;
|
|
2884
|
+
},
|
|
2885
|
+
get id() {
|
|
2886
|
+
return rootFieldState.value.id;
|
|
2887
|
+
},
|
|
2888
|
+
get aria() {
|
|
2889
|
+
return rootFieldState.value.aria;
|
|
2890
|
+
},
|
|
2891
|
+
get key() {
|
|
2892
|
+
return rootFieldState.value.key;
|
|
2893
|
+
},
|
|
2894
|
+
get blank() {
|
|
2895
|
+
return rootFieldState.value.blank;
|
|
2896
|
+
},
|
|
2897
|
+
get label() {
|
|
2898
|
+
return rootFieldState.value.label;
|
|
2899
|
+
},
|
|
2900
|
+
get description() {
|
|
2901
|
+
return rootFieldState.value.description;
|
|
2902
|
+
},
|
|
2903
|
+
get placeholder() {
|
|
2904
|
+
return rootFieldState.value.placeholder;
|
|
2905
|
+
},
|
|
2906
|
+
get meta() {
|
|
2907
|
+
return rootFieldState.value.meta;
|
|
2908
|
+
},
|
|
2600
2909
|
// Lifecycle (form-level only — not on FieldState).
|
|
2601
2910
|
submitting,
|
|
2602
2911
|
submissionAttempts,
|
|
@@ -2605,7 +2914,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2605
2914
|
// Scalar mirror over the array — meta is a single sticky surface
|
|
2606
2915
|
// for both templates and `useWizard`'s `FormStatus`, so the
|
|
2607
2916
|
// projection lives here.
|
|
2608
|
-
errorCount
|
|
2917
|
+
get errorCount() {
|
|
2918
|
+
return metaErrors.value.length;
|
|
2919
|
+
},
|
|
2609
2920
|
submitted,
|
|
2610
2921
|
// Per-`useForm()`-call identity. Stable for one mount; new on
|
|
2611
2922
|
// re-mount; orthogonal to `form.key` (which is the user-supplied
|
|
@@ -2642,12 +2953,20 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2642
2953
|
}
|
|
2643
2954
|
};
|
|
2644
2955
|
function clear(pathInput) {
|
|
2645
|
-
|
|
2646
|
-
|
|
2956
|
+
if (pathInput === void 0) {
|
|
2957
|
+
return setValueImpl(unset);
|
|
2958
|
+
}
|
|
2959
|
+
return setValueImpl(pathInput, unset);
|
|
2647
2960
|
}
|
|
2648
2961
|
const persist = async (pathInput, options2) => {
|
|
2649
2962
|
const segments = canonicalizePath(pathInput).segments;
|
|
2650
|
-
|
|
2963
|
+
if (!allowSensitivePersist(
|
|
2964
|
+
segments,
|
|
2965
|
+
options2?.acknowledgeSensitive === true,
|
|
2966
|
+
state.isSensitivePath
|
|
2967
|
+
)) {
|
|
2968
|
+
return;
|
|
2969
|
+
}
|
|
2651
2970
|
if (persistence === void 0) return;
|
|
2652
2971
|
await persistence.writePathImmediately(segments);
|
|
2653
2972
|
};
|
|
@@ -2707,13 +3026,43 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2707
3026
|
return Object.freeze(view);
|
|
2708
3027
|
});
|
|
2709
3028
|
const valuesProxy = buildValuesProxy(state.form);
|
|
2710
|
-
const fieldStateProxy = buildFieldStateProxy(
|
|
3029
|
+
const fieldStateProxy = buildFieldStateProxy(
|
|
3030
|
+
state,
|
|
3031
|
+
formInstanceId,
|
|
3032
|
+
getFormMetaBase,
|
|
3033
|
+
fieldStateAccessorOptions
|
|
3034
|
+
);
|
|
3035
|
+
const needsLazyGate = state.defaultValuesFactory.value !== void 0 || state.hasSsrPrefetch;
|
|
2711
3036
|
function gated(fn) {
|
|
3037
|
+
if (!needsLazyGate) return fn;
|
|
2712
3038
|
return ((...args) => {
|
|
2713
3039
|
void state.activate();
|
|
2714
3040
|
return fn(...args);
|
|
2715
3041
|
});
|
|
2716
3042
|
}
|
|
3043
|
+
const callTerminal = fieldStateProxy;
|
|
3044
|
+
const EMPTY_FIELD_LIST = Object.freeze([]);
|
|
3045
|
+
function list(path) {
|
|
3046
|
+
const { segments } = canonicalizePath(path);
|
|
3047
|
+
const value = state.getValueAtPath(segments);
|
|
3048
|
+
if (!Array.isArray(value)) return EMPTY_FIELD_LIST;
|
|
3049
|
+
const out = new Array(value.length);
|
|
3050
|
+
for (let i = 0; i < value.length; i += 1) out[i] = callTerminal(`${path}.${i}`);
|
|
3051
|
+
return Object.freeze(out);
|
|
3052
|
+
}
|
|
3053
|
+
const EMPTY_FIELD_RECORD = Object.freeze({});
|
|
3054
|
+
function record(path) {
|
|
3055
|
+
const { segments } = canonicalizePath(path);
|
|
3056
|
+
const value = state.getValueAtPath(segments);
|
|
3057
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
3058
|
+
return EMPTY_FIELD_RECORD;
|
|
3059
|
+
}
|
|
3060
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
3061
|
+
for (const key of Object.keys(value)) {
|
|
3062
|
+
out[key] = callTerminal(`${path}.${key}`);
|
|
3063
|
+
}
|
|
3064
|
+
return Object.freeze(out);
|
|
3065
|
+
}
|
|
2717
3066
|
return {
|
|
2718
3067
|
handleSubmit: gated(handleSubmit),
|
|
2719
3068
|
// Callable readonly Proxies (`values`, `fields`, `errors`) and the
|
|
@@ -2795,6 +3144,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2795
3144
|
swap: gated(fieldArrays.swap),
|
|
2796
3145
|
move: gated(fieldArrays.move),
|
|
2797
3146
|
replace: gated(fieldArrays.replace),
|
|
3147
|
+
list: gated(list),
|
|
3148
|
+
record: gated(record),
|
|
2798
3149
|
get blankPaths() {
|
|
2799
3150
|
void state.activate();
|
|
2800
3151
|
return blankPathsView;
|
|
@@ -2802,47 +3153,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2802
3153
|
};
|
|
2803
3154
|
}
|
|
2804
3155
|
|
|
2805
|
-
const defaultShouldShowErrors = (field, formMeta) => {
|
|
2806
|
-
const hasOwnError = field.errors.some(
|
|
2807
|
-
(e) => e.path.length === field.path.length && e.path.every((s, i) => s === field.path[i])
|
|
2808
|
-
);
|
|
2809
|
-
if (!hasOwnError) return false;
|
|
2810
|
-
if (field.validating === true) return false;
|
|
2811
|
-
if (formMeta.submissionAttempts > 0) return true;
|
|
2812
|
-
return field.touched === true && field.focused !== true;
|
|
2813
|
-
};
|
|
2814
|
-
const SHOW_ALWAYS = () => true;
|
|
2815
|
-
const SHOW_NEVER = () => false;
|
|
2816
|
-
function resolveShouldShowErrors(config) {
|
|
2817
|
-
if (config === void 0) return defaultShouldShowErrors;
|
|
2818
|
-
if (config === true) return SHOW_ALWAYS;
|
|
2819
|
-
if (config === false) return SHOW_NEVER;
|
|
2820
|
-
return config;
|
|
2821
|
-
}
|
|
2822
|
-
|
|
2823
|
-
function isHydratedFieldRecord(value) {
|
|
2824
|
-
if (typeof value !== "object" || value === null) return false;
|
|
2825
|
-
const r = value;
|
|
2826
|
-
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";
|
|
2827
|
-
}
|
|
2828
|
-
function isHydratedValidationErrorArray(value) {
|
|
2829
|
-
if (!Array.isArray(value)) return false;
|
|
2830
|
-
for (const entry of value) {
|
|
2831
|
-
if (typeof entry !== "object" || entry === null) return false;
|
|
2832
|
-
const e = entry;
|
|
2833
|
-
if (typeof e.message !== "string") return false;
|
|
2834
|
-
if (!Array.isArray(e.path)) return false;
|
|
2835
|
-
if (typeof e.formKey !== "string") return false;
|
|
2836
|
-
if (typeof e.code !== "string") return false;
|
|
2837
|
-
}
|
|
2838
|
-
return true;
|
|
2839
|
-
}
|
|
2840
|
-
function warnMalformedHydration(formKey, kind, rawKey) {
|
|
2841
|
-
if (!__DEV__) return;
|
|
2842
|
-
console.warn(
|
|
2843
|
-
`[attaform] hydration: skipping malformed ${kind} entry at key '${rawKey}' on form '${formKey}'. This usually means the SSR bundle is on a different version than the client (rolling deploy / stale cache).`
|
|
2844
|
-
);
|
|
2845
|
-
}
|
|
2846
3156
|
function applyDuStubs(schema, data, options = {}) {
|
|
2847
3157
|
const warned = options.warn === true ? /* @__PURE__ */ new Set() : void 0;
|
|
2848
3158
|
return walkDuStubs(schema, data, options.basePath ?? [], warned);
|
|
@@ -2871,49 +3181,74 @@ function walkDuStubs(schema, value, path, warned) {
|
|
|
2871
3181
|
);
|
|
2872
3182
|
}
|
|
2873
3183
|
}
|
|
2874
|
-
|
|
3184
|
+
const stub = /* @__PURE__ */ Object.create(null);
|
|
3185
|
+
stub[du.discriminatorKey] = discValue;
|
|
3186
|
+
return stub;
|
|
2875
3187
|
}
|
|
2876
3188
|
}
|
|
2877
|
-
const out =
|
|
3189
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
2878
3190
|
for (const k of Object.keys(rec)) {
|
|
2879
3191
|
out[k] = walkDuStubs(schema, rec[k], [...path, k], warned);
|
|
2880
3192
|
}
|
|
2881
3193
|
return out;
|
|
2882
3194
|
}
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
let mutated2 = false;
|
|
2896
|
-
const out2 = new Array(value.length);
|
|
2897
|
-
for (let i = 0; i < value.length; i++) {
|
|
2898
|
-
const cleaned = stripSymbolsDeep(value[i]);
|
|
2899
|
-
out2[i] = cleaned;
|
|
2900
|
-
if (cleaned !== value[i]) mutated2 = true;
|
|
3195
|
+
|
|
3196
|
+
function createVariantMemory() {
|
|
3197
|
+
const memory = /* @__PURE__ */ new Map();
|
|
3198
|
+
function clearAtArrayIndices(arrayPath, indexFilter) {
|
|
3199
|
+
for (const memKey of [...memory.keys()]) {
|
|
3200
|
+
const segs = segmentsForPathKey(memKey);
|
|
3201
|
+
if (segs === null) continue;
|
|
3202
|
+
if (!isPathPrefix(arrayPath, segs)) continue;
|
|
3203
|
+
if (segs.length <= arrayPath.length) continue;
|
|
3204
|
+
const idxSeg = segs[arrayPath.length];
|
|
3205
|
+
if (typeof idxSeg !== "number") continue;
|
|
3206
|
+
if (indexFilter(idxSeg)) memory.delete(memKey);
|
|
2901
3207
|
}
|
|
2902
|
-
return mutated2 ? out2 : value;
|
|
2903
|
-
}
|
|
2904
|
-
const proto = Object.getPrototypeOf(value);
|
|
2905
|
-
if (proto !== Object.prototype && proto !== null) return value;
|
|
2906
|
-
const symKeys = Object.getOwnPropertySymbols(value);
|
|
2907
|
-
const stringKeys = Object.keys(value);
|
|
2908
|
-
let mutated = symKeys.length > 0;
|
|
2909
|
-
const out = {};
|
|
2910
|
-
const src = value;
|
|
2911
|
-
for (const k of stringKeys) {
|
|
2912
|
-
const cleaned = stripSymbolsDeep(src[k]);
|
|
2913
|
-
out[k] = cleaned;
|
|
2914
|
-
if (cleaned !== src[k]) mutated = true;
|
|
2915
3208
|
}
|
|
2916
|
-
return
|
|
3209
|
+
return {
|
|
3210
|
+
clear() {
|
|
3211
|
+
memory.clear();
|
|
3212
|
+
},
|
|
3213
|
+
clearUnderPath(parentPath) {
|
|
3214
|
+
for (const memKey of [...memory.keys()]) {
|
|
3215
|
+
const segs = segmentsForPathKey(memKey);
|
|
3216
|
+
if (segs === null) continue;
|
|
3217
|
+
if (isPathPrefix(parentPath, segs)) memory.delete(memKey);
|
|
3218
|
+
}
|
|
3219
|
+
},
|
|
3220
|
+
applyArrayOp(arrayPath, op) {
|
|
3221
|
+
switch (op.kind) {
|
|
3222
|
+
case "insert":
|
|
3223
|
+
case "remove":
|
|
3224
|
+
clearAtArrayIndices(arrayPath, (i) => i >= op.index);
|
|
3225
|
+
return;
|
|
3226
|
+
case "move": {
|
|
3227
|
+
const lo = Math.min(op.from, op.to);
|
|
3228
|
+
const hi = Math.max(op.from, op.to);
|
|
3229
|
+
clearAtArrayIndices(arrayPath, (i) => i >= lo && i <= hi);
|
|
3230
|
+
return;
|
|
3231
|
+
}
|
|
3232
|
+
case "swap":
|
|
3233
|
+
clearAtArrayIndices(arrayPath, (i) => i === op.a || i === op.b);
|
|
3234
|
+
return;
|
|
3235
|
+
case "replace-at":
|
|
3236
|
+
clearAtArrayIndices(arrayPath, (i) => i === op.index);
|
|
3237
|
+
return;
|
|
3238
|
+
}
|
|
3239
|
+
},
|
|
3240
|
+
recordOutgoing(unionKey, discValue, snapshot) {
|
|
3241
|
+
let perUnion = memory.get(unionKey);
|
|
3242
|
+
if (perUnion === void 0) {
|
|
3243
|
+
perUnion = /* @__PURE__ */ new Map();
|
|
3244
|
+
memory.set(unionKey, perUnion);
|
|
3245
|
+
}
|
|
3246
|
+
perUnion.set(discValue, snapshot);
|
|
3247
|
+
},
|
|
3248
|
+
lookupIncoming(unionKey, discValue) {
|
|
3249
|
+
return memory.get(unionKey)?.get(discValue);
|
|
3250
|
+
}
|
|
3251
|
+
};
|
|
2917
3252
|
}
|
|
2918
3253
|
function cloneVariantSnapshot(value) {
|
|
2919
3254
|
if (value === null || typeof value !== "object") return value;
|
|
@@ -2936,50 +3271,403 @@ function cloneVariantSnapshot(value) {
|
|
|
2936
3271
|
return out2;
|
|
2937
3272
|
}
|
|
2938
3273
|
const src = raw;
|
|
2939
|
-
const out =
|
|
3274
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
2940
3275
|
for (const k of Object.keys(src)) out[k] = cloneVariantSnapshot(src[k]);
|
|
2941
3276
|
return out;
|
|
2942
3277
|
}
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
3278
|
+
|
|
3279
|
+
function createArrayIdentity(getArrayLength) {
|
|
3280
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
3281
|
+
const baselines = /* @__PURE__ */ new Map();
|
|
3282
|
+
let counter = 0;
|
|
3283
|
+
const allocate = () => `k${(counter++).toString(36)}`;
|
|
3284
|
+
function ensure(arrayKey, expectedLen) {
|
|
3285
|
+
let ids = tokens.get(arrayKey);
|
|
3286
|
+
const firstTrack = ids === void 0;
|
|
3287
|
+
if (ids === void 0) {
|
|
3288
|
+
ids = [];
|
|
3289
|
+
tokens.set(arrayKey, ids);
|
|
3290
|
+
}
|
|
3291
|
+
while (ids.length < expectedLen) ids.push(allocate());
|
|
3292
|
+
if (ids.length > expectedLen) ids.length = expectedLen;
|
|
3293
|
+
if (firstTrack) baselines.set(arrayKey, [...ids]);
|
|
3294
|
+
return ids;
|
|
3295
|
+
}
|
|
3296
|
+
function orderPristineForKey(arrayKey) {
|
|
3297
|
+
const baseline = baselines.get(arrayKey);
|
|
3298
|
+
const current = tokens.get(arrayKey);
|
|
3299
|
+
if (baseline === void 0 || current === void 0) return true;
|
|
3300
|
+
if (baseline.length !== current.length) return false;
|
|
3301
|
+
for (let i = 0; i < current.length; i++) {
|
|
3302
|
+
if (current[i] !== baseline[i]) return false;
|
|
2948
3303
|
}
|
|
2949
|
-
return;
|
|
3304
|
+
return true;
|
|
2950
3305
|
}
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
3306
|
+
return {
|
|
3307
|
+
tokenAt(arraySegs, index) {
|
|
3308
|
+
const len = getArrayLength(arraySegs);
|
|
3309
|
+
if (index < 0 || index >= len) return "";
|
|
3310
|
+
const ids = ensure(canonicalizePath(arraySegs).key, len);
|
|
3311
|
+
return ids[index] ?? "";
|
|
3312
|
+
},
|
|
3313
|
+
applyOp(arraySegs, op) {
|
|
3314
|
+
const arrayKey = canonicalizePath(arraySegs).key;
|
|
3315
|
+
const postLen = getArrayLength(arraySegs);
|
|
3316
|
+
const preLen = op.kind === "insert" ? postLen - 1 : op.kind === "remove" ? postLen + 1 : postLen;
|
|
3317
|
+
const ids = ensure(arrayKey, Math.max(0, preLen));
|
|
3318
|
+
switch (op.kind) {
|
|
3319
|
+
case "insert":
|
|
3320
|
+
ids.splice(op.index, 0, allocate());
|
|
3321
|
+
return;
|
|
3322
|
+
case "remove":
|
|
3323
|
+
ids.splice(op.index, 1);
|
|
3324
|
+
return;
|
|
3325
|
+
case "move": {
|
|
3326
|
+
const [moved] = ids.splice(op.from, 1);
|
|
3327
|
+
ids.splice(op.to, 0, moved ?? allocate());
|
|
3328
|
+
return;
|
|
3329
|
+
}
|
|
3330
|
+
case "swap": {
|
|
3331
|
+
const tmp = ids[op.a] ?? allocate();
|
|
3332
|
+
ids[op.a] = ids[op.b] ?? allocate();
|
|
3333
|
+
ids[op.b] = tmp;
|
|
3334
|
+
return;
|
|
3335
|
+
}
|
|
3336
|
+
case "replace-at":
|
|
3337
|
+
ids[op.index] = allocate();
|
|
3338
|
+
return;
|
|
3339
|
+
}
|
|
3340
|
+
},
|
|
3341
|
+
applyRemap(arrayPath, remap) {
|
|
3342
|
+
if (remap.moved.size === 0 && remap.vacated.size === 0 && remap.fresh.size === 0) return;
|
|
3343
|
+
const relocate = (store) => {
|
|
3344
|
+
const idxPos = arrayPath.length;
|
|
3345
|
+
const snapshots = [];
|
|
3346
|
+
for (const [key, value] of store) {
|
|
3347
|
+
const segments = segmentsForPathKey(key);
|
|
3348
|
+
if (segments === null) continue;
|
|
3349
|
+
if (!isPathPrefix(arrayPath, segments)) continue;
|
|
3350
|
+
if (segments.length <= idxPos) continue;
|
|
3351
|
+
const idxSeg = segments[idxPos];
|
|
3352
|
+
if (typeof idxSeg !== "number") continue;
|
|
3353
|
+
if (!remap.moved.has(idxSeg) && !remap.vacated.has(idxSeg) && !remap.fresh.has(idxSeg)) {
|
|
3354
|
+
continue;
|
|
3355
|
+
}
|
|
3356
|
+
snapshots.push({ segments: [...segments], index: idxSeg, value });
|
|
3357
|
+
}
|
|
3358
|
+
if (snapshots.length === 0) return;
|
|
3359
|
+
for (const snap of snapshots) store.delete(canonicalizePath(snap.segments).key);
|
|
3360
|
+
for (const snap of snapshots) {
|
|
3361
|
+
const target = remap.moved.get(snap.index);
|
|
3362
|
+
if (target === void 0) continue;
|
|
3363
|
+
const relocated = snap.segments.slice();
|
|
3364
|
+
relocated[idxPos] = target;
|
|
3365
|
+
store.set(canonicalizePath(relocated).key, snap.value);
|
|
3366
|
+
}
|
|
3367
|
+
};
|
|
3368
|
+
relocate(tokens);
|
|
3369
|
+
relocate(baselines);
|
|
3370
|
+
},
|
|
3371
|
+
realign(arraySegs) {
|
|
3372
|
+
ensure(canonicalizePath(arraySegs).key, getArrayLength(arraySegs));
|
|
3373
|
+
},
|
|
3374
|
+
hasStructuralChangeUnder(prefix) {
|
|
3375
|
+
for (const arrayKey of tokens.keys()) {
|
|
3376
|
+
if (orderPristineForKey(arrayKey)) continue;
|
|
3377
|
+
const segs = segmentsForPathKey(arrayKey);
|
|
3378
|
+
if (segs === null) continue;
|
|
3379
|
+
if (isPathPrefix(prefix, segs)) return true;
|
|
3380
|
+
}
|
|
3381
|
+
return false;
|
|
3382
|
+
},
|
|
3383
|
+
rebaselineAll() {
|
|
3384
|
+
for (const arrayKey of [...tokens.keys()]) {
|
|
3385
|
+
const segs = segmentsForPathKey(arrayKey);
|
|
3386
|
+
if (segs === null) continue;
|
|
3387
|
+
const ids = ensure(arrayKey, getArrayLength(segs));
|
|
3388
|
+
baselines.set(arrayKey, [...ids]);
|
|
3389
|
+
}
|
|
2954
3390
|
}
|
|
2955
|
-
}
|
|
3391
|
+
};
|
|
2956
3392
|
}
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
3393
|
+
|
|
3394
|
+
function remapForOp(op, oldLen) {
|
|
3395
|
+
const moved = /* @__PURE__ */ new Map();
|
|
3396
|
+
const vacated = /* @__PURE__ */ new Set();
|
|
3397
|
+
const fresh = /* @__PURE__ */ new Set();
|
|
3398
|
+
switch (op.kind) {
|
|
3399
|
+
case "insert":
|
|
3400
|
+
for (let i = op.index; i < oldLen; i++) moved.set(i, i + 1);
|
|
3401
|
+
fresh.add(op.index);
|
|
3402
|
+
return { moved, vacated, fresh };
|
|
3403
|
+
case "remove":
|
|
3404
|
+
vacated.add(op.index);
|
|
3405
|
+
for (let i = op.index + 1; i < oldLen; i++) moved.set(i, i - 1);
|
|
3406
|
+
return { moved, vacated, fresh };
|
|
3407
|
+
case "move":
|
|
3408
|
+
if (op.from !== op.to) {
|
|
3409
|
+
moved.set(op.from, op.to);
|
|
3410
|
+
if (op.from < op.to) {
|
|
3411
|
+
for (let i = op.from + 1; i <= op.to; i++) moved.set(i, i - 1);
|
|
3412
|
+
} else {
|
|
3413
|
+
for (let i = op.to; i < op.from; i++) moved.set(i, i + 1);
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
return { moved, vacated, fresh };
|
|
3417
|
+
case "swap":
|
|
3418
|
+
if (op.a !== op.b) {
|
|
3419
|
+
moved.set(op.a, op.b);
|
|
3420
|
+
moved.set(op.b, op.a);
|
|
3421
|
+
}
|
|
3422
|
+
return { moved, vacated, fresh };
|
|
3423
|
+
case "replace-at":
|
|
3424
|
+
vacated.add(op.index);
|
|
3425
|
+
fresh.add(op.index);
|
|
3426
|
+
return { moved, vacated, fresh };
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
function changedIndices(remap) {
|
|
3430
|
+
const changed = new Set(remap.vacated);
|
|
3431
|
+
for (const [from, to] of remap.moved) {
|
|
3432
|
+
changed.add(from);
|
|
3433
|
+
changed.add(to);
|
|
3434
|
+
}
|
|
3435
|
+
for (const index of remap.fresh) changed.add(index);
|
|
3436
|
+
return changed;
|
|
3437
|
+
}
|
|
3438
|
+
function elementIndexUnder(arrayPath, key, idxPos) {
|
|
3439
|
+
const segments = segmentsForPathKey(key);
|
|
3440
|
+
if (segments === null) return null;
|
|
3441
|
+
if (!isPathPrefix(arrayPath, segments)) return null;
|
|
3442
|
+
if (segments.length <= idxPos) return null;
|
|
3443
|
+
const index = segments[idxPos];
|
|
3444
|
+
return typeof index === "number" ? index : null;
|
|
3445
|
+
}
|
|
3446
|
+
function migrateMapSubtree(map, arrayPath, remap, rewriteValue) {
|
|
3447
|
+
const idxPos = arrayPath.length;
|
|
3448
|
+
const snapshots = [];
|
|
3449
|
+
for (const [key, value] of map) {
|
|
3450
|
+
const index = elementIndexUnder(arrayPath, key, idxPos);
|
|
3451
|
+
if (index === null) continue;
|
|
3452
|
+
if (!remap.moved.has(index) && !remap.vacated.has(index)) continue;
|
|
3453
|
+
snapshots.push({ segments: [...segmentsForPathKey(key)], index, value });
|
|
3454
|
+
}
|
|
3455
|
+
if (snapshots.length === 0) return;
|
|
3456
|
+
for (const snap of snapshots) map.delete(canonicalizePath(snap.segments).key);
|
|
3457
|
+
for (const snap of snapshots) {
|
|
3458
|
+
const target = remap.moved.get(snap.index);
|
|
3459
|
+
if (target === void 0) continue;
|
|
3460
|
+
const relocated = snap.segments.slice();
|
|
3461
|
+
relocated[idxPos] = target;
|
|
3462
|
+
map.set(canonicalizePath(relocated).key, rewriteValue(snap.value, relocated));
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
function migrateSetSubtree(set, arrayPath, remap) {
|
|
3466
|
+
const idxPos = arrayPath.length;
|
|
3467
|
+
const snapshots = [];
|
|
3468
|
+
for (const key of set) {
|
|
3469
|
+
const index = elementIndexUnder(arrayPath, key, idxPos);
|
|
3470
|
+
if (index === null) continue;
|
|
3471
|
+
if (!remap.moved.has(index) && !remap.vacated.has(index)) continue;
|
|
3472
|
+
snapshots.push({ segments: [...segmentsForPathKey(key)], index });
|
|
3473
|
+
}
|
|
3474
|
+
if (snapshots.length === 0) return;
|
|
3475
|
+
for (const snap of snapshots) set.delete(canonicalizePath(snap.segments).key);
|
|
3476
|
+
for (const snap of snapshots) {
|
|
3477
|
+
const target = remap.moved.get(snap.index);
|
|
3478
|
+
if (target === void 0) continue;
|
|
3479
|
+
const relocated = snap.segments.slice();
|
|
3480
|
+
relocated[idxPos] = target;
|
|
3481
|
+
set.add(canonicalizePath(relocated).key);
|
|
2976
3482
|
}
|
|
2977
3483
|
}
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
const
|
|
2981
|
-
|
|
2982
|
-
|
|
3484
|
+
|
|
3485
|
+
function createArrayBookkeeping(deps) {
|
|
3486
|
+
const {
|
|
3487
|
+
form,
|
|
3488
|
+
fields,
|
|
3489
|
+
userErrors,
|
|
3490
|
+
originals,
|
|
3491
|
+
blankPaths,
|
|
3492
|
+
originalBlankPaths,
|
|
3493
|
+
fieldValidationCounts,
|
|
3494
|
+
fieldValidationState,
|
|
3495
|
+
schemaErrors,
|
|
3496
|
+
activeValidations,
|
|
3497
|
+
arrayIdentity,
|
|
3498
|
+
touchFieldRecord,
|
|
3499
|
+
decFieldValidation
|
|
3500
|
+
} = deps;
|
|
3501
|
+
function migrateElementState(arrayPath, remap) {
|
|
3502
|
+
if (remap.moved.size === 0 && remap.vacated.size === 0) return;
|
|
3503
|
+
migrateMapSubtree(fields, arrayPath, remap, (record, segments) => ({
|
|
3504
|
+
...record,
|
|
3505
|
+
path: segments
|
|
3506
|
+
}));
|
|
3507
|
+
migrateMapSubtree(
|
|
3508
|
+
userErrors,
|
|
3509
|
+
arrayPath,
|
|
3510
|
+
remap,
|
|
3511
|
+
(errors, segments) => errors.map((error) => ({ ...error, path: [...segments] }))
|
|
3512
|
+
);
|
|
3513
|
+
migrateMapSubtree(originals, arrayPath, remap, (record, segments) => ({
|
|
3514
|
+
segments,
|
|
3515
|
+
value: record.value
|
|
3516
|
+
}));
|
|
3517
|
+
migrateSetSubtree(blankPaths, arrayPath, remap);
|
|
3518
|
+
migrateSetSubtree(originalBlankPaths, arrayPath, remap);
|
|
3519
|
+
migrateMapSubtree(fieldValidationCounts, arrayPath, remap, (count) => count);
|
|
3520
|
+
arrayIdentity.applyRemap(arrayPath, remap);
|
|
3521
|
+
}
|
|
3522
|
+
function seedFreshElement(arrayPath, freshIndex) {
|
|
3523
|
+
const elementPath = [...arrayPath, freshIndex];
|
|
3524
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3525
|
+
diffAndApply(void 0, getAtPath(form.value, elementPath), elementPath, (patch) => {
|
|
3526
|
+
if (patch.kind !== "added") return;
|
|
3527
|
+
const { key } = canonicalizePath(patch.path);
|
|
3528
|
+
if (!originals.has(key)) originals.set(key, { segments: patch.path, value: void 0 });
|
|
3529
|
+
touchFieldRecord(key, patch.path, { updatedAt: now });
|
|
3530
|
+
});
|
|
3531
|
+
}
|
|
3532
|
+
function dropSchemaErrorsAtChangedIndices(arrayPath, remap) {
|
|
3533
|
+
const changed = changedIndices(remap);
|
|
3534
|
+
if (changed.size === 0) return;
|
|
3535
|
+
const idxPos = arrayPath.length;
|
|
3536
|
+
for (const key of [...schemaErrors.keys()]) {
|
|
3537
|
+
const segs = segmentsForPathKey(key);
|
|
3538
|
+
if (segs === null) continue;
|
|
3539
|
+
if (!isPathPrefix(arrayPath, segs)) continue;
|
|
3540
|
+
if (segs.length <= idxPos) continue;
|
|
3541
|
+
const idx = segs[idxPos];
|
|
3542
|
+
if (typeof idx === "number" && changed.has(idx)) schemaErrors.delete(key);
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
function abortValidationAtVacatedIndices(arrayPath, remap) {
|
|
3546
|
+
if (remap.vacated.size === 0) return;
|
|
3547
|
+
const idxPos = arrayPath.length;
|
|
3548
|
+
for (const [key, entry] of [...fieldValidationState]) {
|
|
3549
|
+
const segs = segmentsForPathKey(key);
|
|
3550
|
+
if (segs === null) continue;
|
|
3551
|
+
if (!isPathPrefix(arrayPath, segs)) continue;
|
|
3552
|
+
if (segs.length <= idxPos) continue;
|
|
3553
|
+
const idx = segs[idxPos];
|
|
3554
|
+
if (typeof idx !== "number" || !remap.vacated.has(idx)) continue;
|
|
3555
|
+
if (entry.timer !== null) {
|
|
3556
|
+
clearTimeout(entry.timer);
|
|
3557
|
+
} else if (!entry.settled) {
|
|
3558
|
+
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
3559
|
+
decFieldValidation(key);
|
|
3560
|
+
}
|
|
3561
|
+
entry.controller.abort();
|
|
3562
|
+
fieldValidationState.delete(key);
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3565
|
+
return {
|
|
3566
|
+
migrateElementState,
|
|
3567
|
+
seedFreshElement,
|
|
3568
|
+
dropSchemaErrorsAtChangedIndices,
|
|
3569
|
+
abortValidationAtVacatedIndices
|
|
3570
|
+
};
|
|
3571
|
+
}
|
|
3572
|
+
|
|
3573
|
+
function isHydratedFieldRecord(value) {
|
|
3574
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3575
|
+
const r = value;
|
|
3576
|
+
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";
|
|
3577
|
+
}
|
|
3578
|
+
function isHydratedValidationErrorArray(value) {
|
|
3579
|
+
if (!Array.isArray(value)) return false;
|
|
3580
|
+
for (const entry of value) {
|
|
3581
|
+
if (typeof entry !== "object" || entry === null) return false;
|
|
3582
|
+
const e = entry;
|
|
3583
|
+
if (typeof e.message !== "string") return false;
|
|
3584
|
+
if (!Array.isArray(e.path)) return false;
|
|
3585
|
+
if (typeof e.formKey !== "string") return false;
|
|
3586
|
+
if (typeof e.code !== "string") return false;
|
|
3587
|
+
}
|
|
3588
|
+
return true;
|
|
3589
|
+
}
|
|
3590
|
+
function warnMalformedHydration(formKey, kind, rawKey) {
|
|
3591
|
+
if (!__DEV__) return;
|
|
3592
|
+
console.warn(
|
|
3593
|
+
`[attaform] hydration: skipping malformed ${kind} entry at key '${rawKey}' on form '${formKey}'. This usually means the SSR bundle is on a different version than the client (rolling deploy / stale cache).`
|
|
3594
|
+
);
|
|
3595
|
+
}
|
|
3596
|
+
function isPathKeyUnder(existingKey, parentPath) {
|
|
3597
|
+
const parsed = segmentsForPathKey(existingKey);
|
|
3598
|
+
if (parsed === null) return false;
|
|
3599
|
+
if (parsed.length <= parentPath.length) return false;
|
|
3600
|
+
for (let i = 0; i < parentPath.length; i++) {
|
|
3601
|
+
if (parsed[i] !== parentPath[i]) return false;
|
|
3602
|
+
}
|
|
3603
|
+
return true;
|
|
3604
|
+
}
|
|
3605
|
+
function stripSymbolsDeep(value) {
|
|
3606
|
+
if (value === null || typeof value !== "object") return value;
|
|
3607
|
+
if (Array.isArray(value)) {
|
|
3608
|
+
let mutated2 = false;
|
|
3609
|
+
const out2 = new Array(value.length);
|
|
3610
|
+
for (let i = 0; i < value.length; i++) {
|
|
3611
|
+
const cleaned = stripSymbolsDeep(value[i]);
|
|
3612
|
+
out2[i] = cleaned;
|
|
3613
|
+
if (cleaned !== value[i]) mutated2 = true;
|
|
3614
|
+
}
|
|
3615
|
+
return mutated2 ? out2 : value;
|
|
3616
|
+
}
|
|
3617
|
+
const proto = Object.getPrototypeOf(value);
|
|
3618
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
3619
|
+
const symKeys = Object.getOwnPropertySymbols(value);
|
|
3620
|
+
const stringKeys = Object.keys(value);
|
|
3621
|
+
let mutated = symKeys.length > 0;
|
|
3622
|
+
const out = {};
|
|
3623
|
+
const src = value;
|
|
3624
|
+
for (const k of stringKeys) {
|
|
3625
|
+
const cleaned = stripSymbolsDeep(src[k]);
|
|
3626
|
+
out[k] = cleaned;
|
|
3627
|
+
if (cleaned !== src[k]) mutated = true;
|
|
3628
|
+
}
|
|
3629
|
+
return mutated ? out : value;
|
|
3630
|
+
}
|
|
3631
|
+
function walkAuthoredFromConstraints(value, prefix, out) {
|
|
3632
|
+
if (prefix.length > 0) out.add(canonicalizePath(prefix).key);
|
|
3633
|
+
if (isPlainRecord(value)) {
|
|
3634
|
+
for (const k of Object.keys(value)) {
|
|
3635
|
+
walkAuthoredFromConstraints(value[k], [...prefix, k], out);
|
|
3636
|
+
}
|
|
3637
|
+
return;
|
|
3638
|
+
}
|
|
3639
|
+
if (Array.isArray(value)) {
|
|
3640
|
+
for (let i = 0; i < value.length; i++) {
|
|
3641
|
+
walkAuthoredFromConstraints(value[i], [...prefix, i], out);
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
function walkAuthoredFromSchemaDiff(withDefaults, withoutDefaults, prefix, out) {
|
|
3646
|
+
if (isPlainRecord(withDefaults) && isPlainRecord(withoutDefaults)) {
|
|
3647
|
+
const left = withDefaults;
|
|
3648
|
+
const right = withoutDefaults;
|
|
3649
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)]);
|
|
3650
|
+
for (const k of keys) {
|
|
3651
|
+
walkAuthoredFromSchemaDiff(left[k], right[k], [...prefix, k], out);
|
|
3652
|
+
}
|
|
3653
|
+
return;
|
|
3654
|
+
}
|
|
3655
|
+
if (Array.isArray(withDefaults) && Array.isArray(withoutDefaults)) {
|
|
3656
|
+
const len = Math.max(withDefaults.length, withoutDefaults.length);
|
|
3657
|
+
for (let i = 0; i < len; i++) {
|
|
3658
|
+
walkAuthoredFromSchemaDiff(withDefaults[i], withoutDefaults[i], [...prefix, i], out);
|
|
3659
|
+
}
|
|
3660
|
+
return;
|
|
3661
|
+
}
|
|
3662
|
+
if (!Object.is(withDefaults, withoutDefaults) && prefix.length > 0) {
|
|
3663
|
+
out.add(canonicalizePath(prefix).key);
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
function createFormStore(options) {
|
|
3667
|
+
const { formKey, schema, defaultValues, strict = true, hydration } = options;
|
|
3668
|
+
const ssr = options.ssr === true;
|
|
3669
|
+
const ssrPrefetch = options.ssrPrefetch;
|
|
3670
|
+
const rememberVariants = options.rememberVariants !== false;
|
|
2983
3671
|
const fieldValidationMode = options.validateOn ?? "change";
|
|
2984
3672
|
const fieldValidationDebounceMs = normalizeNumericOption({
|
|
2985
3673
|
value: options.debounceMs ?? DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS,
|
|
@@ -3010,11 +3698,8 @@ function createFormStore(options) {
|
|
|
3010
3698
|
noSyncPathCounts.set(path, current - 1);
|
|
3011
3699
|
}
|
|
3012
3700
|
const coerceIndex = resolveCoercionIndex(options.coerce);
|
|
3013
|
-
const
|
|
3014
|
-
options.shouldShowErrors
|
|
3015
|
-
);
|
|
3701
|
+
const resolvedGetDisplayState = resolveGetDisplayState(options.getDisplayState);
|
|
3016
3702
|
const resolvedIsSensitivePath = options.isSensitivePath ?? isSensitivePath;
|
|
3017
|
-
const resolvedSegmentMatchesSensitive = options.segmentMatchesSensitive ?? segmentMatchesSensitive;
|
|
3018
3703
|
const cleanupHooks = [];
|
|
3019
3704
|
const modules = /* @__PURE__ */ new Map();
|
|
3020
3705
|
const completedConstraints = defaultValues === void 0 ? void 0 : mergeStructural(schema, [], defaultValues);
|
|
@@ -3052,6 +3737,16 @@ function createFormStore(options) {
|
|
|
3052
3737
|
warn: true
|
|
3053
3738
|
});
|
|
3054
3739
|
const form = ref(stubbedInitialData);
|
|
3740
|
+
const arrayIdentity = createArrayIdentity((arraySegs) => {
|
|
3741
|
+
const v = getAtPath(form.value, arraySegs);
|
|
3742
|
+
return Array.isArray(v) ? v.length : 0;
|
|
3743
|
+
});
|
|
3744
|
+
function arrayElementKey(path) {
|
|
3745
|
+
if (path.length === 0) return "";
|
|
3746
|
+
const last = path[path.length - 1];
|
|
3747
|
+
if (typeof last === "number") return arrayIdentity.tokenAt(path.slice(0, -1), last);
|
|
3748
|
+
return "";
|
|
3749
|
+
}
|
|
3055
3750
|
const fields = reactive(/* @__PURE__ */ new Map());
|
|
3056
3751
|
const elements = reactive(/* @__PURE__ */ new Map());
|
|
3057
3752
|
const elementToFormInstance = /* @__PURE__ */ new WeakMap();
|
|
@@ -3067,41 +3762,7 @@ function createFormStore(options) {
|
|
|
3067
3762
|
blankPaths.add(key);
|
|
3068
3763
|
originalBlankPaths.add(key);
|
|
3069
3764
|
}
|
|
3070
|
-
const variantMemory =
|
|
3071
|
-
function clearVariantMemoryUnderPath(arrayPath) {
|
|
3072
|
-
for (const memKey of [...variantMemory.keys()]) {
|
|
3073
|
-
const segs = segmentsForPathKey(memKey);
|
|
3074
|
-
if (segs === null) continue;
|
|
3075
|
-
if (isPathPrefix(arrayPath, segs)) variantMemory.delete(memKey);
|
|
3076
|
-
}
|
|
3077
|
-
}
|
|
3078
|
-
function clearVariantMemoryAtArrayIndices(arrayPath, indexFilter) {
|
|
3079
|
-
for (const memKey of [...variantMemory.keys()]) {
|
|
3080
|
-
const segs = segmentsForPathKey(memKey);
|
|
3081
|
-
if (segs === null) continue;
|
|
3082
|
-
if (!isPathPrefix(arrayPath, segs)) continue;
|
|
3083
|
-
if (segs.length <= arrayPath.length) continue;
|
|
3084
|
-
const idxSeg = segs[arrayPath.length];
|
|
3085
|
-
if (typeof idxSeg !== "number") continue;
|
|
3086
|
-
if (indexFilter(idxSeg)) variantMemory.delete(memKey);
|
|
3087
|
-
}
|
|
3088
|
-
}
|
|
3089
|
-
function applyArrayOpToMemory(arrayPath, op) {
|
|
3090
|
-
switch (op.kind) {
|
|
3091
|
-
case "shift-from":
|
|
3092
|
-
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i >= op.index);
|
|
3093
|
-
return;
|
|
3094
|
-
case "shift-range":
|
|
3095
|
-
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i >= op.fromIndex && i <= op.toIndex);
|
|
3096
|
-
return;
|
|
3097
|
-
case "swap":
|
|
3098
|
-
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i === op.a || i === op.b);
|
|
3099
|
-
return;
|
|
3100
|
-
case "replace-at":
|
|
3101
|
-
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i === op.index);
|
|
3102
|
-
return;
|
|
3103
|
-
}
|
|
3104
|
-
}
|
|
3765
|
+
const variantMemory = createVariantMemory();
|
|
3105
3766
|
const pathOrdinals = /* @__PURE__ */ new Map();
|
|
3106
3767
|
let nextOrdinal = 0;
|
|
3107
3768
|
function ensurePathOrdinal(key) {
|
|
@@ -3139,6 +3800,9 @@ function createFormStore(options) {
|
|
|
3139
3800
|
const departAttempts = ref(0);
|
|
3140
3801
|
const submissionGeneration = ref(0);
|
|
3141
3802
|
const activeValidations = ref(0);
|
|
3803
|
+
const pathSnapshots = /* @__PURE__ */ new Map();
|
|
3804
|
+
let scheduleEpoch = 0;
|
|
3805
|
+
let lastCommittedEpoch = 0;
|
|
3142
3806
|
const hydrating = ref(false);
|
|
3143
3807
|
const hydrateError = ref(null);
|
|
3144
3808
|
const defaultValuesFactory = ref(void 0);
|
|
@@ -3154,9 +3818,12 @@ function createFormStore(options) {
|
|
|
3154
3818
|
const pathAsyncCache = /* @__PURE__ */ new Map();
|
|
3155
3819
|
function pathHasAsyncValidation(path) {
|
|
3156
3820
|
const { key } = canonicalizePath(path);
|
|
3821
|
+
return pathHasAsyncValidationByKey(key, path);
|
|
3822
|
+
}
|
|
3823
|
+
function pathHasAsyncValidationByKey(key, segments) {
|
|
3157
3824
|
const cached = pathAsyncCache.get(key);
|
|
3158
3825
|
if (cached !== void 0) return cached;
|
|
3159
|
-
const candidates = schema.getSchemasAtPath(
|
|
3826
|
+
const candidates = schema.getSchemasAtPath(segments);
|
|
3160
3827
|
const hasAsync = candidates.some((sub) => sub.needsAsyncValidation?.() === true);
|
|
3161
3828
|
pathAsyncCache.set(key, hasAsync);
|
|
3162
3829
|
return hasAsync;
|
|
@@ -3209,7 +3876,9 @@ function createFormStore(options) {
|
|
|
3209
3876
|
connected: false,
|
|
3210
3877
|
focused: null,
|
|
3211
3878
|
blurred: null,
|
|
3212
|
-
touched: false
|
|
3879
|
+
touched: false,
|
|
3880
|
+
interacted: false,
|
|
3881
|
+
blurredAfterInteraction: false
|
|
3213
3882
|
});
|
|
3214
3883
|
});
|
|
3215
3884
|
if (strict && !schemaResponse.success) {
|
|
@@ -3238,9 +3907,29 @@ function createFormStore(options) {
|
|
|
3238
3907
|
blurred: patch.blurred !== void 0 ? patch.blurred : current?.blurred ?? null,
|
|
3239
3908
|
// touched is plain `boolean`; `??` is equivalent to the explicit
|
|
3240
3909
|
// guard here because `false` is not nullish.
|
|
3241
|
-
touched: patch.touched ?? current?.touched ?? false
|
|
3910
|
+
touched: patch.touched ?? current?.touched ?? false,
|
|
3911
|
+
// interacted is sticky-true; a merge patch only ever sets it, so
|
|
3912
|
+
// `??` preserves the current bit. It flips back to false solely
|
|
3913
|
+
// through the reset paths, which reconstruct the record outright.
|
|
3914
|
+
interacted: patch.interacted ?? current?.interacted ?? false,
|
|
3915
|
+
blurredAfterInteraction: patch.blurredAfterInteraction ?? current?.blurredAfterInteraction ?? false
|
|
3242
3916
|
});
|
|
3243
3917
|
}
|
|
3918
|
+
const arrayBookkeeping = createArrayBookkeeping({
|
|
3919
|
+
form,
|
|
3920
|
+
fields,
|
|
3921
|
+
userErrors,
|
|
3922
|
+
originals,
|
|
3923
|
+
blankPaths,
|
|
3924
|
+
originalBlankPaths,
|
|
3925
|
+
fieldValidationCounts,
|
|
3926
|
+
fieldValidationState,
|
|
3927
|
+
schemaErrors,
|
|
3928
|
+
activeValidations,
|
|
3929
|
+
arrayIdentity,
|
|
3930
|
+
touchFieldRecord,
|
|
3931
|
+
decFieldValidation
|
|
3932
|
+
});
|
|
3244
3933
|
function applyFormReplacement(next, meta) {
|
|
3245
3934
|
const prev = form.value;
|
|
3246
3935
|
if (Object.is(prev, next)) return;
|
|
@@ -3364,8 +4053,13 @@ function createFormStore(options) {
|
|
|
3364
4053
|
const pathKey = canonicalizePath(path).key;
|
|
3365
4054
|
if (meta?.blank === true) {
|
|
3366
4055
|
blankPaths.add(pathKey);
|
|
3367
|
-
} else
|
|
3368
|
-
blankPaths.delete(pathKey);
|
|
4056
|
+
} else {
|
|
4057
|
+
if (blankPaths.has(pathKey)) blankPaths.delete(pathKey);
|
|
4058
|
+
if (meta?.arrayOp === void 0) {
|
|
4059
|
+
for (const existingKey of [...blankPaths]) {
|
|
4060
|
+
if (isPathKeyUnder(existingKey, path)) blankPaths.delete(existingKey);
|
|
4061
|
+
}
|
|
4062
|
+
}
|
|
3369
4063
|
}
|
|
3370
4064
|
const wasAuthoredBefore = authoredPaths.has(pathKey);
|
|
3371
4065
|
walkAuthoredFromConstraints(value, path, authoredPaths);
|
|
@@ -3384,12 +4078,20 @@ function createFormStore(options) {
|
|
|
3384
4078
|
}
|
|
3385
4079
|
return true;
|
|
3386
4080
|
}
|
|
4081
|
+
const oldArrayLength = Array.isArray(currentValue) ? currentValue.length : 0;
|
|
3387
4082
|
const nextForm = setAtPathWithSchemaFill(form.value, schema, path, completedValue);
|
|
3388
4083
|
applyFormReplacement(nextForm, meta);
|
|
3389
4084
|
if (meta?.arrayOp !== void 0) {
|
|
3390
|
-
|
|
4085
|
+
const remap = remapForOp(meta.arrayOp, oldArrayLength);
|
|
4086
|
+
arrayBookkeeping.migrateElementState(path, remap);
|
|
4087
|
+
for (const freshIndex of remap.fresh) arrayBookkeeping.seedFreshElement(path, freshIndex);
|
|
4088
|
+
arrayBookkeeping.dropSchemaErrorsAtChangedIndices(path, remap);
|
|
4089
|
+
arrayBookkeeping.abortValidationAtVacatedIndices(path, remap);
|
|
4090
|
+
variantMemory.applyArrayOp(path, meta.arrayOp);
|
|
4091
|
+
arrayIdentity.applyOp(path, meta.arrayOp);
|
|
3391
4092
|
} else if (Array.isArray(value) && Array.isArray(currentValue)) {
|
|
3392
|
-
|
|
4093
|
+
variantMemory.clearUnderPath(path);
|
|
4094
|
+
arrayIdentity.realign(path);
|
|
3393
4095
|
}
|
|
3394
4096
|
const effectiveModeAfterWrite = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
3395
4097
|
if (effectiveModeAfterWrite === "change") {
|
|
@@ -3413,18 +4115,12 @@ function createFormStore(options) {
|
|
|
3413
4115
|
for (const k of blankPaths) {
|
|
3414
4116
|
if (isPathKeyUnder(k, parentPath)) outgoingBlanks.push(k);
|
|
3415
4117
|
}
|
|
3416
|
-
|
|
3417
|
-
if (memoryForUnion2 === void 0) {
|
|
3418
|
-
memoryForUnion2 = /* @__PURE__ */ new Map();
|
|
3419
|
-
variantMemory.set(parentKey, memoryForUnion2);
|
|
3420
|
-
}
|
|
3421
|
-
memoryForUnion2.set(oldDiscValue, {
|
|
4118
|
+
variantMemory.recordOutgoing(parentKey, oldDiscValue, {
|
|
3422
4119
|
value: currentValue2,
|
|
3423
4120
|
blankPaths: outgoingBlanks
|
|
3424
4121
|
});
|
|
3425
4122
|
}
|
|
3426
|
-
const
|
|
3427
|
-
const restored = memoryForUnion?.get(newDiscValue);
|
|
4123
|
+
const restored = variantMemory.lookupIncoming(parentKey, newDiscValue);
|
|
3428
4124
|
if (restored !== void 0) {
|
|
3429
4125
|
baseline = restored.value;
|
|
3430
4126
|
restoredBlanks = [...restored.blankPaths];
|
|
@@ -3496,6 +4192,7 @@ function createFormStore(options) {
|
|
|
3496
4192
|
const controller = new AbortController();
|
|
3497
4193
|
const fresh = { controller, timer: null, settled: false };
|
|
3498
4194
|
fieldValidationState.set(key, fresh);
|
|
4195
|
+
const myEpoch = ++scheduleEpoch;
|
|
3499
4196
|
const run = () => {
|
|
3500
4197
|
fresh.timer = null;
|
|
3501
4198
|
if (controller.signal.aborted) return;
|
|
@@ -3510,11 +4207,25 @@ function createFormStore(options) {
|
|
|
3510
4207
|
}
|
|
3511
4208
|
throw err;
|
|
3512
4209
|
}
|
|
3513
|
-
|
|
4210
|
+
const subtreeScope = path.length > 0 && schema.hasContainerOrRootRefine?.() === false;
|
|
4211
|
+
const scopePath = subtreeScope ? path : void 0;
|
|
4212
|
+
const dataAtScope = subtreeScope ? getAtPath(form.value, path) : form.value;
|
|
4213
|
+
const scopeKey = subtreeScope ? canonicalizePath(path).key : ROOT_PATH_KEY;
|
|
4214
|
+
void Promise.resolve().then(() => schema.validateAtPath(dataAtScope, scopePath)).then((response) => {
|
|
3514
4215
|
if (controller.signal.aborted) return;
|
|
4216
|
+
if (myEpoch <= lastCommittedEpoch) return;
|
|
4217
|
+
lastCommittedEpoch = myEpoch;
|
|
4218
|
+
if (effectiveMode === "blur") {
|
|
4219
|
+
const snapshotSource = scopePath !== void 0 ? getAtPath(form.value, scopePath) : form.value;
|
|
4220
|
+
pathSnapshots.set(scopeKey, structuralSnapshot(snapshotSource));
|
|
4221
|
+
}
|
|
3515
4222
|
const errors = response.success ? [] : response.errors;
|
|
3516
4223
|
const filtered = filterAuthoredErrors(errors);
|
|
3517
|
-
|
|
4224
|
+
const restamped = subtreeScope ? filtered.map((err) => ({
|
|
4225
|
+
...err,
|
|
4226
|
+
path: [...path, ...err.path]
|
|
4227
|
+
})) : filtered;
|
|
4228
|
+
applySchemaErrorsForSubtree(scopePath ?? [], restamped);
|
|
3518
4229
|
}).catch(() => {
|
|
3519
4230
|
}).finally(() => {
|
|
3520
4231
|
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
@@ -3741,24 +4452,55 @@ function createFormStore(options) {
|
|
|
3741
4452
|
}
|
|
3742
4453
|
function markFocused(path, focused, meta) {
|
|
3743
4454
|
const { key } = canonicalizePath(path);
|
|
4455
|
+
const current = fields.get(key);
|
|
3744
4456
|
touchFieldRecord(key, path, {
|
|
3745
4457
|
focused,
|
|
3746
4458
|
blurred: !focused,
|
|
3747
4459
|
// `touched` flips to true on blur and stays true thereafter; while
|
|
3748
4460
|
// a field is currently focused we keep whatever value it held.
|
|
3749
|
-
touched: focused ?
|
|
4461
|
+
touched: focused ? current?.touched ?? false : true,
|
|
4462
|
+
// `blurredAfterInteraction` flips true on the first blur that lands
|
|
4463
|
+
// after a value edit and stays true. A tab-through blur before any
|
|
4464
|
+
// edit leaves it false (`interacted` is still false at that blur),
|
|
4465
|
+
// which is what keeps a clean tab-through from arming the gate.
|
|
4466
|
+
blurredAfterInteraction: !focused && current?.interacted === true ? true : current?.blurredAfterInteraction ?? false
|
|
3750
4467
|
});
|
|
3751
4468
|
const focusMode = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
3752
4469
|
if (!focused && focusMode === "blur") {
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
4470
|
+
const firstInteractiveBlur = current?.interacted === true && current.blurredAfterInteraction !== true;
|
|
4471
|
+
let snapshot = void 0;
|
|
4472
|
+
let snapshotScopeLength = 0;
|
|
4473
|
+
for (let i = path.length; i >= 0; i--) {
|
|
4474
|
+
const ancestorKey = canonicalizePath(path.slice(0, i)).key;
|
|
4475
|
+
const entry = pathSnapshots.get(ancestorKey);
|
|
4476
|
+
if (entry !== void 0) {
|
|
4477
|
+
snapshot = entry;
|
|
4478
|
+
snapshotScopeLength = i;
|
|
4479
|
+
break;
|
|
4480
|
+
}
|
|
4481
|
+
}
|
|
4482
|
+
let changed = true;
|
|
4483
|
+
if (!firstInteractiveBlur && snapshot !== void 0) {
|
|
4484
|
+
const relPath = path.slice(snapshotScopeLength);
|
|
4485
|
+
const snapshotSubtree = getAtPath(snapshot, relPath);
|
|
4486
|
+
const liveSubtree = getAtPath(form.value, path);
|
|
4487
|
+
changed = false;
|
|
4488
|
+
diffAndApply(snapshotSubtree, liveSubtree, path, () => {
|
|
4489
|
+
changed = true;
|
|
4490
|
+
});
|
|
4491
|
+
}
|
|
4492
|
+
if (changed) {
|
|
4493
|
+
scheduleFieldValidation(path, true, {
|
|
4494
|
+
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
4495
|
+
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
4496
|
+
});
|
|
4497
|
+
}
|
|
3757
4498
|
}
|
|
3758
4499
|
}
|
|
3759
|
-
function
|
|
4500
|
+
function markInteracted(path) {
|
|
3760
4501
|
const { key } = canonicalizePath(path);
|
|
3761
|
-
|
|
4502
|
+
if (fields.get(key)?.interacted === true) return;
|
|
4503
|
+
touchFieldRecord(key, path, { interacted: true });
|
|
3762
4504
|
}
|
|
3763
4505
|
function touchAtPath(segments) {
|
|
3764
4506
|
const formValue = form.value;
|
|
@@ -3778,9 +4520,6 @@ function createFormStore(options) {
|
|
|
3778
4520
|
);
|
|
3779
4521
|
}
|
|
3780
4522
|
}
|
|
3781
|
-
function clear(path) {
|
|
3782
|
-
return setValueAtPath(path, schema.getEmptyValueAtPath(path));
|
|
3783
|
-
}
|
|
3784
4523
|
function rehydrate() {
|
|
3785
4524
|
const factory = defaultValuesFactory.value;
|
|
3786
4525
|
if (factory === void 0) {
|
|
@@ -3870,6 +4609,7 @@ function createFormStore(options) {
|
|
|
3870
4609
|
const next = resetResponse.data;
|
|
3871
4610
|
rebuildAuthoredPaths(resetSource, next);
|
|
3872
4611
|
applyFormReplacement(next);
|
|
4612
|
+
arrayIdentity.rebaselineAll();
|
|
3873
4613
|
originals.clear();
|
|
3874
4614
|
diffAndApply({}, next, [], (patch) => {
|
|
3875
4615
|
if (patch.kind !== "added") return;
|
|
@@ -3913,7 +4653,9 @@ function createFormStore(options) {
|
|
|
3913
4653
|
connected: record.connected,
|
|
3914
4654
|
focused: record.focused,
|
|
3915
4655
|
blurred: record.blurred,
|
|
3916
|
-
touched: false
|
|
4656
|
+
touched: false,
|
|
4657
|
+
interacted: false,
|
|
4658
|
+
blurredAfterInteraction: false
|
|
3917
4659
|
});
|
|
3918
4660
|
}
|
|
3919
4661
|
submissionGeneration.value += 1;
|
|
@@ -3924,6 +4666,9 @@ function createFormStore(options) {
|
|
|
3924
4666
|
submitError.value = null;
|
|
3925
4667
|
departAttempts.value = 0;
|
|
3926
4668
|
cancelFieldValidation();
|
|
4669
|
+
pathSnapshots.clear();
|
|
4670
|
+
scheduleEpoch = 0;
|
|
4671
|
+
lastCommittedEpoch = 0;
|
|
3927
4672
|
variantMemory.clear();
|
|
3928
4673
|
for (const listener of resetListeners) {
|
|
3929
4674
|
try {
|
|
@@ -3935,13 +4680,7 @@ function createFormStore(options) {
|
|
|
3935
4680
|
}
|
|
3936
4681
|
function resetField(path) {
|
|
3937
4682
|
const { key: targetKey, segments: targetSegments } = canonicalizePath(path);
|
|
3938
|
-
|
|
3939
|
-
const memSegments = segmentsForPathKey(memKey);
|
|
3940
|
-
if (memSegments === null) continue;
|
|
3941
|
-
if (isPathPrefix(targetSegments, memSegments)) {
|
|
3942
|
-
variantMemory.delete(memKey);
|
|
3943
|
-
}
|
|
3944
|
-
}
|
|
4683
|
+
variantMemory.clearUnderPath(targetSegments);
|
|
3945
4684
|
const leafEntry = originals.get(targetKey);
|
|
3946
4685
|
if (leafEntry !== void 0) {
|
|
3947
4686
|
const wrote = setValueAtPath(targetSegments, leafEntry.value);
|
|
@@ -3997,23 +4736,24 @@ function createFormStore(options) {
|
|
|
3997
4736
|
connected: record.connected,
|
|
3998
4737
|
focused: record.focused,
|
|
3999
4738
|
blurred: record.blurred,
|
|
4000
|
-
touched: false
|
|
4739
|
+
touched: false,
|
|
4740
|
+
interacted: false,
|
|
4741
|
+
blurredAfterInteraction: false
|
|
4001
4742
|
});
|
|
4002
4743
|
}
|
|
4003
|
-
function isPathPrefix(prefix, candidate) {
|
|
4004
|
-
if (prefix.length > candidate.length) return false;
|
|
4005
|
-
for (let i = 0; i < prefix.length; i++) {
|
|
4006
|
-
if (prefix[i] !== candidate[i]) return false;
|
|
4007
|
-
}
|
|
4008
|
-
return true;
|
|
4009
|
-
}
|
|
4010
4744
|
function isPristineAtPath(path) {
|
|
4011
4745
|
const { key, segments } = canonicalizePath(path);
|
|
4746
|
+
return isPristineAtPathByKey(key, segments);
|
|
4747
|
+
}
|
|
4748
|
+
function isPristineAtPathByKey(key, segments) {
|
|
4012
4749
|
if (blankPaths.has(key) !== originalBlankPaths.has(key)) return false;
|
|
4013
4750
|
const entry = originals.get(key);
|
|
4014
4751
|
if (entry === void 0) return true;
|
|
4015
4752
|
return Object.is(getAtPath(form.value, segments), entry.value);
|
|
4016
4753
|
}
|
|
4754
|
+
function hasStructuralChangeUnder(path) {
|
|
4755
|
+
return arrayIdentity.hasStructuralChangeUnder(path);
|
|
4756
|
+
}
|
|
4017
4757
|
function getFieldRecord(path) {
|
|
4018
4758
|
const { key } = canonicalizePath(path);
|
|
4019
4759
|
return fields.get(key);
|
|
@@ -4057,7 +4797,7 @@ function createFormStore(options) {
|
|
|
4057
4797
|
originals,
|
|
4058
4798
|
schema,
|
|
4059
4799
|
ssr,
|
|
4060
|
-
|
|
4800
|
+
getDisplayState: resolvedGetDisplayState,
|
|
4061
4801
|
submitting,
|
|
4062
4802
|
activeSubmissions,
|
|
4063
4803
|
submissionAttempts,
|
|
@@ -4067,6 +4807,7 @@ function createFormStore(options) {
|
|
|
4067
4807
|
hydrating,
|
|
4068
4808
|
hydrateError,
|
|
4069
4809
|
defaultValuesFactory,
|
|
4810
|
+
hasSsrPrefetch: ssrPrefetch !== void 0,
|
|
4070
4811
|
defaultsResolved,
|
|
4071
4812
|
activated,
|
|
4072
4813
|
activationPromise,
|
|
@@ -4076,13 +4817,14 @@ function createFormStore(options) {
|
|
|
4076
4817
|
activeValidations,
|
|
4077
4818
|
firstValidationDone,
|
|
4078
4819
|
pathHasAsyncValidation,
|
|
4820
|
+
pathHasAsyncValidationByKey,
|
|
4079
4821
|
fieldValidationCounts,
|
|
4080
4822
|
applyFormReplacement,
|
|
4081
4823
|
setValueAtPath,
|
|
4082
4824
|
getValueAtPath,
|
|
4825
|
+
arrayElementKey,
|
|
4083
4826
|
reset,
|
|
4084
4827
|
resetField,
|
|
4085
|
-
clear,
|
|
4086
4828
|
setSchemaErrorsForPath,
|
|
4087
4829
|
setAllSchemaErrors,
|
|
4088
4830
|
clearSchemaErrors,
|
|
@@ -4095,10 +4837,12 @@ function createFormStore(options) {
|
|
|
4095
4837
|
registerElement,
|
|
4096
4838
|
deregisterElement,
|
|
4097
4839
|
markFocused,
|
|
4098
|
-
|
|
4840
|
+
markInteracted,
|
|
4099
4841
|
touchAtPath,
|
|
4100
4842
|
markConnectedOptimistically,
|
|
4101
4843
|
isPristineAtPath,
|
|
4844
|
+
isPristineAtPathByKey,
|
|
4845
|
+
hasStructuralChangeUnder,
|
|
4102
4846
|
getFieldRecord,
|
|
4103
4847
|
getOriginalAtPath,
|
|
4104
4848
|
getFirstErrorElement,
|
|
@@ -4114,7 +4858,6 @@ function createFormStore(options) {
|
|
|
4114
4858
|
modules,
|
|
4115
4859
|
persistOptIns,
|
|
4116
4860
|
isSensitivePath: resolvedIsSensitivePath,
|
|
4117
|
-
segmentMatchesSensitive: resolvedSegmentMatchesSensitive,
|
|
4118
4861
|
noSyncPaths,
|
|
4119
4862
|
incrementNoSyncOptOut,
|
|
4120
4863
|
decrementNoSyncOptOut,
|
|
@@ -4315,939 +5058,988 @@ function createHistoryModule(state, config) {
|
|
|
4315
5058
|
};
|
|
4316
5059
|
}
|
|
4317
5060
|
|
|
4318
|
-
function
|
|
4319
|
-
let
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
4328
|
-
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36).padStart(11, "0");
|
|
4329
|
-
}
|
|
4330
|
-
|
|
4331
|
-
const PROTOCOL_VERSION = 1;
|
|
4332
|
-
const JOIN_COLLECTION_WINDOW_MS = 50;
|
|
4333
|
-
const SNAPSHOT_TIMEOUT_MS = 200;
|
|
4334
|
-
const MAX_LEADER_ATTEMPTS = 3;
|
|
4335
|
-
function isFileLikeValue(value) {
|
|
4336
|
-
if (typeof File !== "undefined" && value instanceof File) return true;
|
|
4337
|
-
if (typeof Blob !== "undefined" && value instanceof Blob) return true;
|
|
4338
|
-
return false;
|
|
4339
|
-
}
|
|
4340
|
-
function isDangerousSegment(s) {
|
|
4341
|
-
return s === "__proto__" || s === "constructor" || s === "prototype";
|
|
4342
|
-
}
|
|
4343
|
-
function pathContainsDangerousSegment(path) {
|
|
4344
|
-
for (let i = 0; i < path.length; i++) {
|
|
4345
|
-
if (isDangerousSegment(path[i])) return true;
|
|
4346
|
-
}
|
|
4347
|
-
return false;
|
|
4348
|
-
}
|
|
4349
|
-
function isInboundShapeAcceptable(schema, path, value) {
|
|
4350
|
-
if (isFileLikeValue(value)) return false;
|
|
4351
|
-
let kind;
|
|
4352
|
-
if (Array.isArray(value)) {
|
|
4353
|
-
kind = "array";
|
|
4354
|
-
} else if (value !== null && typeof value === "object" && isPlainRecord(value)) {
|
|
4355
|
-
kind = "object";
|
|
4356
|
-
} else {
|
|
4357
|
-
kind = slimKindOf(value);
|
|
4358
|
-
}
|
|
4359
|
-
const accepted = schema.getSlimPrimitiveTypesAtPath(path);
|
|
4360
|
-
if (!accepted.has(kind)) return false;
|
|
4361
|
-
if (Array.isArray(value)) {
|
|
4362
|
-
for (let i = 0; i < value.length; i++) {
|
|
4363
|
-
if (!isInboundShapeAcceptable(schema, [...path, i], value[i])) return false;
|
|
4364
|
-
}
|
|
4365
|
-
return true;
|
|
4366
|
-
}
|
|
4367
|
-
if (isPlainRecord(value)) {
|
|
4368
|
-
for (const key of Object.keys(value)) {
|
|
4369
|
-
if (!isInboundShapeAcceptable(schema, [...path, key], value[key])) return false;
|
|
5061
|
+
function wirePersistence(state, config) {
|
|
5062
|
+
let fingerprint;
|
|
5063
|
+
try {
|
|
5064
|
+
fingerprint = hashStableString(state.schema.fingerprint());
|
|
5065
|
+
} catch (err) {
|
|
5066
|
+
if (__DEV__) {
|
|
5067
|
+
console.warn(
|
|
5068
|
+
`[attaform] Could not fingerprint the schema for form '${state.formKey}': ${err instanceof Error ? err.message : String(err)}. Persistence falls back to a fingerprint-free key, so a schema change won't auto-invalidate a saved draft.`
|
|
5069
|
+
);
|
|
4370
5070
|
}
|
|
4371
|
-
|
|
5071
|
+
fingerprint = "unfingerprinted";
|
|
4372
5072
|
}
|
|
4373
|
-
|
|
4374
|
-
}
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
}
|
|
4386
|
-
function stripSensitivePathsDeep(value, pathSoFar, isSensitivePath) {
|
|
4387
|
-
if (isFileLikeValue(value)) return void 0;
|
|
4388
|
-
if (value === null || typeof value !== "object") return value;
|
|
4389
|
-
if (Array.isArray(value)) {
|
|
4390
|
-
return value.map((item, i) => stripSensitivePathsDeep(item, [...pathSoFar, i], isSensitivePath));
|
|
4391
|
-
}
|
|
4392
|
-
const proto = Object.getPrototypeOf(value);
|
|
4393
|
-
if (proto !== Object.prototype && proto !== null) return value;
|
|
4394
|
-
const out = {};
|
|
4395
|
-
const src = value;
|
|
4396
|
-
for (const key of Object.keys(src)) {
|
|
4397
|
-
const childPath = [...pathSoFar, key];
|
|
4398
|
-
if (isSensitivePath(childPath)) {
|
|
4399
|
-
out[key] = void 0;
|
|
4400
|
-
continue;
|
|
4401
|
-
}
|
|
4402
|
-
out[key] = stripSensitivePathsDeep(src[key], childPath, isSensitivePath);
|
|
4403
|
-
}
|
|
4404
|
-
return out;
|
|
4405
|
-
}
|
|
4406
|
-
function isValidSyncMessage(data) {
|
|
4407
|
-
if (data === null || typeof data !== "object") return false;
|
|
4408
|
-
const m = data;
|
|
4409
|
-
if (m["v"] !== PROTOCOL_VERSION) return false;
|
|
4410
|
-
if (typeof m["senderId"] !== "string") return false;
|
|
4411
|
-
if (typeof m["kind"] !== "string") return false;
|
|
4412
|
-
switch (m["kind"]) {
|
|
4413
|
-
case "hello":
|
|
4414
|
-
case "announce":
|
|
4415
|
-
return true;
|
|
4416
|
-
case "requestSnapshot":
|
|
4417
|
-
return typeof m["targetId"] === "string";
|
|
4418
|
-
case "snapshot":
|
|
4419
|
-
return Array.isArray(m["blankPaths"]) && "form" in m;
|
|
4420
|
-
case "patches":
|
|
4421
|
-
return Array.isArray(m["formPatches"]) && Array.isArray(m["blankPathsAdded"]) && Array.isArray(m["blankPathsRemoved"]);
|
|
4422
|
-
default:
|
|
4423
|
-
return false;
|
|
4424
|
-
}
|
|
4425
|
-
}
|
|
4426
|
-
function generateSenderId() {
|
|
4427
|
-
try {
|
|
4428
|
-
return globalThis.crypto.randomUUID();
|
|
4429
|
-
} catch {
|
|
4430
|
-
return `atta-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;
|
|
4431
|
-
}
|
|
4432
|
-
}
|
|
4433
|
-
function createMultiTabSyncModule(state, channelName, options) {
|
|
4434
|
-
if (typeof BroadcastChannel === "undefined") {
|
|
4435
|
-
return {
|
|
4436
|
-
dispose: () => void 0,
|
|
4437
|
-
lifecycle: () => "established",
|
|
4438
|
-
senderId: "",
|
|
4439
|
-
channelName
|
|
4440
|
-
};
|
|
4441
|
-
}
|
|
4442
|
-
let channel;
|
|
4443
|
-
try {
|
|
4444
|
-
channel = new BroadcastChannel(channelName);
|
|
4445
|
-
} catch {
|
|
4446
|
-
return {
|
|
4447
|
-
dispose: () => void 0,
|
|
4448
|
-
lifecycle: () => "established",
|
|
4449
|
-
senderId: "",
|
|
4450
|
-
channelName
|
|
4451
|
-
};
|
|
4452
|
-
}
|
|
4453
|
-
const senderId = generateSenderId();
|
|
4454
|
-
let lifecycle = "joining";
|
|
5073
|
+
const base = resolveStorageKeyBase(config, state.formKey);
|
|
5074
|
+
const key = `${base}:${fingerprint}`;
|
|
5075
|
+
const debounceMs = normalizeNumericOption({
|
|
5076
|
+
value: config.debounceMs ?? DEFAULT_PERSISTENCE_DEBOUNCE_MS,
|
|
5077
|
+
source: "useForm.persist.debounceMs",
|
|
5078
|
+
allowInfinity: false,
|
|
5079
|
+
min: 0,
|
|
5080
|
+
defaultValue: DEFAULT_PERSISTENCE_DEBOUNCE_MS
|
|
5081
|
+
});
|
|
5082
|
+
const include = config.include ?? "form";
|
|
5083
|
+
const clearOnSubmitSuccess = config.clearOnSubmitSuccess ?? true;
|
|
5084
|
+
const adapterPromise = getStorageAdapter(config.storage);
|
|
4455
5085
|
let disposed = false;
|
|
4456
|
-
const
|
|
4457
|
-
let
|
|
4458
|
-
let
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
try {
|
|
4467
|
-
channel.postMessage(msg);
|
|
4468
|
-
} catch {
|
|
4469
|
-
}
|
|
4470
|
-
}
|
|
4471
|
-
function refreshPrior() {
|
|
4472
|
-
prior = {
|
|
4473
|
-
form: snapshotForm(state.form.value),
|
|
4474
|
-
blankPathsSnapshot: [...state.blankPaths]
|
|
4475
|
-
};
|
|
4476
|
-
}
|
|
4477
|
-
function isPathLocallySuppressed(path) {
|
|
4478
|
-
if (pathContainsDangerousSegment(path)) return true;
|
|
4479
|
-
if (options.isSensitivePath(path)) return true;
|
|
4480
|
-
const { key } = canonicalizePath([...path]);
|
|
4481
|
-
if (options.noSyncPaths.has(key)) return true;
|
|
4482
|
-
return false;
|
|
4483
|
-
}
|
|
4484
|
-
function postPatches() {
|
|
4485
|
-
if (lifecycle !== "established") return;
|
|
4486
|
-
const next = snapshotForm(state.form.value);
|
|
4487
|
-
const rawPatches = [];
|
|
4488
|
-
diffAndApply(prior.form, next, [], (p) => rawPatches.push(p));
|
|
4489
|
-
const safePatches = [];
|
|
4490
|
-
for (const p of rawPatches) {
|
|
4491
|
-
if (isPathLocallySuppressed(p.path)) continue;
|
|
4492
|
-
if ("value" in p && isFileLikeValue(p.value)) continue;
|
|
4493
|
-
safePatches.push(p);
|
|
4494
|
-
}
|
|
4495
|
-
const { added, removed } = diffBlankPaths(prior.blankPathsSnapshot, state.blankPaths);
|
|
4496
|
-
if (safePatches.length === 0 && added.length === 0 && removed.length === 0) {
|
|
4497
|
-
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
5086
|
+
const isDisposed = () => disposed;
|
|
5087
|
+
let inFlightFinalFlush = null;
|
|
5088
|
+
let pendingOptedInPaths = null;
|
|
5089
|
+
const writer = createDebouncedWriter(async () => {
|
|
5090
|
+
const optedInPaths = pendingOptedInPaths ?? new Set(state.persistOptIns.optedInPaths());
|
|
5091
|
+
pendingOptedInPaths = null;
|
|
5092
|
+
const adapter = await adapterPromise;
|
|
5093
|
+
if (isDisposed()) return;
|
|
5094
|
+
if (optedInPaths.size === 0) {
|
|
5095
|
+
await adapter.removeItem(key);
|
|
4498
5096
|
return;
|
|
4499
5097
|
}
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
5098
|
+
const rawForm = toRaw(state.form.value);
|
|
5099
|
+
const filteredForm = stripUnacknowledgedSensitiveLeaves(
|
|
5100
|
+
pluckPaths(rawForm, optedInPaths),
|
|
5101
|
+
optedInPaths,
|
|
5102
|
+
state.isSensitivePath
|
|
5103
|
+
);
|
|
5104
|
+
const filteredSchemaErrors = filterErrorsByPaths(state.schemaErrors, optedInPaths);
|
|
5105
|
+
const filteredUserErrors = filterErrorsByPaths(state.userErrors, optedInPaths);
|
|
5106
|
+
const filteredTransientEmpty = /* @__PURE__ */ new Set();
|
|
5107
|
+
for (const tk of state.blankPaths) {
|
|
5108
|
+
if (optedInPaths.has(tk)) filteredTransientEmpty.add(tk);
|
|
5109
|
+
}
|
|
5110
|
+
const payload = buildPersistedPayload(
|
|
5111
|
+
filteredForm,
|
|
5112
|
+
include,
|
|
5113
|
+
filteredSchemaErrors,
|
|
5114
|
+
filteredUserErrors,
|
|
5115
|
+
filteredTransientEmpty
|
|
5116
|
+
);
|
|
5117
|
+
await adapter.setItem(key, payload);
|
|
5118
|
+
}, debounceMs);
|
|
4510
5119
|
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
4511
|
-
if (
|
|
4512
|
-
if (lifecycle !== "established") return;
|
|
5120
|
+
if (isDisposed() || inFlightFinalFlush !== null) return;
|
|
4513
5121
|
if (meta?.crossTab === true) return;
|
|
4514
|
-
if (meta?.
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
}
|
|
4518
|
-
postPatches();
|
|
5122
|
+
if (meta?.persist !== true) return;
|
|
5123
|
+
pendingOptedInPaths = new Set(state.persistOptIns.optedInPaths());
|
|
5124
|
+
writer.schedule();
|
|
4519
5125
|
});
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
5126
|
+
const unsubscribeSuccess = clearOnSubmitSuccess ? state.onSubmitSuccess(() => {
|
|
5127
|
+
if (isDisposed()) return;
|
|
5128
|
+
void (async () => {
|
|
5129
|
+
await writer.flush();
|
|
5130
|
+
if (isDisposed()) return;
|
|
5131
|
+
const adapter = await adapterPromise;
|
|
5132
|
+
if (isDisposed()) return;
|
|
5133
|
+
await adapter.removeItem(key);
|
|
5134
|
+
})();
|
|
5135
|
+
}) : () => void 0;
|
|
5136
|
+
const handlePageHide = () => {
|
|
5137
|
+
if (isDisposed()) return;
|
|
5138
|
+
void writer.flush();
|
|
5139
|
+
};
|
|
5140
|
+
if (typeof window !== "undefined") {
|
|
5141
|
+
window.addEventListener("pagehide", handlePageHide);
|
|
4525
5142
|
}
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
if (!Array.isArray(p.path)) continue;
|
|
4531
|
-
if (isPathLocallySuppressed(p.path)) continue;
|
|
4532
|
-
if ("value" in p && !isInboundShapeAcceptable(state.schema, p.path, p.value)) {
|
|
4533
|
-
continue;
|
|
4534
|
-
}
|
|
4535
|
-
safePatches.push(p);
|
|
4536
|
-
}
|
|
4537
|
-
const safeBlankAdded = [];
|
|
4538
|
-
for (const k of msg.blankPathsAdded) {
|
|
4539
|
-
const segs = canonicalizePath(k).segments;
|
|
4540
|
-
if (isPathLocallySuppressed(segs)) continue;
|
|
4541
|
-
safeBlankAdded.push(k);
|
|
4542
|
-
}
|
|
4543
|
-
const safeBlankRemoved = [];
|
|
4544
|
-
for (const k of msg.blankPathsRemoved) {
|
|
4545
|
-
const segs = canonicalizePath(k).segments;
|
|
4546
|
-
if (isPathLocallySuppressed(segs)) continue;
|
|
4547
|
-
safeBlankRemoved.push(k);
|
|
4548
|
-
}
|
|
4549
|
-
if (safePatches.length === 0 && safeBlankAdded.length === 0 && safeBlankRemoved.length === 0) {
|
|
4550
|
-
return;
|
|
4551
|
-
}
|
|
4552
|
-
const candidate = applyPatchesForward(state.form.value, safePatches);
|
|
5143
|
+
void (async () => {
|
|
5144
|
+
const adapter = await adapterPromise;
|
|
5145
|
+
if (isDisposed()) return;
|
|
5146
|
+
void cleanupOrphanKeys(adapter, base, key);
|
|
4553
5147
|
try {
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
5148
|
+
const raw = await adapter.getItem(key);
|
|
5149
|
+
const payload = readPersistedPayload(raw);
|
|
5150
|
+
if (payload === null) {
|
|
5151
|
+
if (raw !== null && raw !== void 0) {
|
|
5152
|
+
await adapter.removeItem(key);
|
|
5153
|
+
}
|
|
4558
5154
|
return;
|
|
4559
5155
|
}
|
|
5156
|
+
if (isDisposed()) return;
|
|
5157
|
+
const merged = mergeSparseHydration(
|
|
5158
|
+
toRaw(state.form.value),
|
|
5159
|
+
payload.data.form,
|
|
5160
|
+
state.schema
|
|
5161
|
+
);
|
|
5162
|
+
state.applyFormReplacement(merged, { hydration: true });
|
|
5163
|
+
const persistedLeafPaths = collectPersistedLeafPaths(payload.data.form);
|
|
5164
|
+
for (const k of persistedLeafPaths) {
|
|
5165
|
+
state.blankPaths.delete(k);
|
|
5166
|
+
state.originalBlankPaths.delete(k);
|
|
5167
|
+
}
|
|
5168
|
+
for (const k of payload.data.blankPaths ?? []) {
|
|
5169
|
+
state.blankPaths.add(k);
|
|
5170
|
+
state.originalBlankPaths.add(k);
|
|
5171
|
+
}
|
|
5172
|
+
if (include === "form+errors") {
|
|
5173
|
+
if (payload.data.schemaErrors !== void 0) {
|
|
5174
|
+
const flat = payload.data.schemaErrors.flatMap(([, errs]) => errs);
|
|
5175
|
+
state.setAllSchemaErrors(flat);
|
|
5176
|
+
}
|
|
5177
|
+
if (payload.data.userErrors !== void 0) {
|
|
5178
|
+
const flat = payload.data.userErrors.flatMap(([, errs]) => errs);
|
|
5179
|
+
state.setAllUserErrors(flat);
|
|
5180
|
+
}
|
|
5181
|
+
}
|
|
5182
|
+
state.scheduleFieldValidation(
|
|
5183
|
+
[],
|
|
5184
|
+
true
|
|
5185
|
+
/* immediate */
|
|
5186
|
+
);
|
|
4560
5187
|
} catch {
|
|
4561
5188
|
}
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
if (
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
5189
|
+
})();
|
|
5190
|
+
async function writePathImmediately(path) {
|
|
5191
|
+
if (isDisposed()) return;
|
|
5192
|
+
await writer.flush();
|
|
5193
|
+
if (isDisposed()) return;
|
|
5194
|
+
const adapter = await adapterPromise;
|
|
5195
|
+
if (isDisposed()) return;
|
|
5196
|
+
const raw = await adapter.getItem(key);
|
|
5197
|
+
const existing = readPersistedPayload(raw);
|
|
5198
|
+
const baseForm = existing?.data.form ?? /* @__PURE__ */ Object.create(null);
|
|
5199
|
+
const value = getAtPath(toRaw(state.form.value), path);
|
|
5200
|
+
const nextForm = setAtPath(baseForm, path, value);
|
|
5201
|
+
const { key: pathKey } = canonicalizePath(path);
|
|
5202
|
+
const transientSet = new Set(
|
|
5203
|
+
(existing?.data.blankPaths ?? []).filter(
|
|
5204
|
+
(k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
|
|
5205
|
+
)
|
|
5206
|
+
);
|
|
5207
|
+
for (const liveKey of state.blankPaths) {
|
|
5208
|
+
if (liveKey === pathKey || isDescendantPathKey(liveKey, pathKey)) {
|
|
5209
|
+
transientSet.add(liveKey);
|
|
5210
|
+
}
|
|
4573
5211
|
}
|
|
4574
|
-
if (
|
|
4575
|
-
|
|
4576
|
-
|
|
5212
|
+
if (include === "form") {
|
|
5213
|
+
await adapter.setItem(
|
|
5214
|
+
key,
|
|
5215
|
+
buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
|
|
5216
|
+
);
|
|
5217
|
+
return;
|
|
4577
5218
|
}
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
5219
|
+
const schemaMap = new Map(existing?.data.schemaErrors ?? []);
|
|
5220
|
+
const userMap = new Map(existing?.data.userErrors ?? []);
|
|
5221
|
+
const currentSchema = state.schemaErrors.get(pathKey);
|
|
5222
|
+
const currentUser = state.userErrors.get(pathKey);
|
|
5223
|
+
if (currentSchema !== void 0 && currentSchema.length > 0) {
|
|
5224
|
+
schemaMap.set(pathKey, [...currentSchema]);
|
|
5225
|
+
} else {
|
|
5226
|
+
schemaMap.delete(pathKey);
|
|
5227
|
+
}
|
|
5228
|
+
if (currentUser !== void 0 && currentUser.length > 0) {
|
|
5229
|
+
userMap.set(pathKey, [...currentUser]);
|
|
5230
|
+
} else {
|
|
5231
|
+
userMap.delete(pathKey);
|
|
5232
|
+
}
|
|
5233
|
+
await adapter.setItem(
|
|
5234
|
+
key,
|
|
5235
|
+
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5236
|
+
);
|
|
4594
5237
|
}
|
|
4595
|
-
|
|
4596
|
-
if (
|
|
4597
|
-
|
|
4598
|
-
if (
|
|
4599
|
-
const
|
|
4600
|
-
if (
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
respondToHello();
|
|
4605
|
-
break;
|
|
4606
|
-
case "announce":
|
|
4607
|
-
if (lifecycle === "joining") peerIds.add(msg.senderId);
|
|
4608
|
-
break;
|
|
4609
|
-
case "requestSnapshot":
|
|
4610
|
-
if (lifecycle !== "established") return;
|
|
4611
|
-
if (msg.targetId !== senderId) return;
|
|
4612
|
-
respondToSnapshotRequest();
|
|
4613
|
-
break;
|
|
4614
|
-
case "snapshot":
|
|
4615
|
-
handleSnapshot(msg);
|
|
4616
|
-
break;
|
|
4617
|
-
case "patches":
|
|
4618
|
-
handlePatches(msg);
|
|
4619
|
-
break;
|
|
5238
|
+
async function clearPersistedDraft(path) {
|
|
5239
|
+
if (isDisposed()) return;
|
|
5240
|
+
await writer.flush();
|
|
5241
|
+
if (isDisposed()) return;
|
|
5242
|
+
const adapter = await adapterPromise;
|
|
5243
|
+
if (isDisposed()) return;
|
|
5244
|
+
if (path === void 0) {
|
|
5245
|
+
await adapter.removeItem(key);
|
|
5246
|
+
return;
|
|
4620
5247
|
}
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
if (
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
5248
|
+
const raw = await adapter.getItem(key);
|
|
5249
|
+
const existing = readPersistedPayload(raw);
|
|
5250
|
+
if (existing === null) return;
|
|
5251
|
+
const nextForm = deleteAtPath(existing.data.form, path);
|
|
5252
|
+
if (isEmptyContainer(nextForm)) {
|
|
5253
|
+
await adapter.removeItem(key);
|
|
4627
5254
|
return;
|
|
4628
5255
|
}
|
|
4629
|
-
const
|
|
4630
|
-
const
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
5256
|
+
const { key: pathKey } = canonicalizePath(path);
|
|
5257
|
+
const transientSet = new Set(
|
|
5258
|
+
(existing.data.blankPaths ?? []).filter(
|
|
5259
|
+
(k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
|
|
5260
|
+
)
|
|
5261
|
+
);
|
|
5262
|
+
if (include === "form") {
|
|
5263
|
+
await adapter.setItem(
|
|
5264
|
+
key,
|
|
5265
|
+
buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
|
|
5266
|
+
);
|
|
5267
|
+
return;
|
|
5268
|
+
}
|
|
5269
|
+
const schemaErrors = (existing.data.schemaErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5270
|
+
const userErrors = (existing.data.userErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5271
|
+
const schemaMap = new Map(schemaErrors.map(([k, v]) => [k, [...v]]));
|
|
5272
|
+
const userMap = new Map(userErrors.map(([k, v]) => [k, [...v]]));
|
|
5273
|
+
await adapter.setItem(
|
|
5274
|
+
key,
|
|
5275
|
+
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5276
|
+
);
|
|
4650
5277
|
}
|
|
4651
|
-
function
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
if (disposed) return;
|
|
4656
|
-
if (lifecycle === "established") return;
|
|
4657
|
-
electLeaderAndRequest();
|
|
4658
|
-
}, JOIN_COLLECTION_WINDOW_MS);
|
|
5278
|
+
function awaitPendingWrites() {
|
|
5279
|
+
if (inFlightFinalFlush !== null) return inFlightFinalFlush;
|
|
5280
|
+
if (isDisposed()) return Promise.resolve();
|
|
5281
|
+
return writer.flush().catch(() => void 0);
|
|
4659
5282
|
}
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
5283
|
+
function dispose() {
|
|
5284
|
+
if (isDisposed() || inFlightFinalFlush !== null) return;
|
|
5285
|
+
unsubscribeChange();
|
|
5286
|
+
unsubscribeSuccess();
|
|
5287
|
+
if (typeof window !== "undefined") {
|
|
5288
|
+
window.removeEventListener("pagehide", handlePageHide);
|
|
5289
|
+
}
|
|
5290
|
+
inFlightFinalFlush = writer.flush().catch(() => void 0).finally(() => {
|
|
4664
5291
|
disposed = true;
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
try {
|
|
4675
|
-
channel.close();
|
|
4676
|
-
} catch {
|
|
4677
|
-
}
|
|
4678
|
-
},
|
|
4679
|
-
lifecycle: () => lifecycle,
|
|
4680
|
-
senderId,
|
|
4681
|
-
channelName
|
|
5292
|
+
inFlightFinalFlush = null;
|
|
5293
|
+
});
|
|
5294
|
+
}
|
|
5295
|
+
return {
|
|
5296
|
+
wiredConfig: config,
|
|
5297
|
+
writePathImmediately,
|
|
5298
|
+
clearPersistedDraft,
|
|
5299
|
+
awaitPendingWrites,
|
|
5300
|
+
dispose
|
|
4682
5301
|
};
|
|
4683
5302
|
}
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
if (warned.has(feature)) return;
|
|
4690
|
-
warned.add(feature);
|
|
4691
|
-
const message = featureMessage(feature);
|
|
4692
|
-
console.warn(`[attaform] ${message}`);
|
|
5303
|
+
function isEmptyContainer(value) {
|
|
5304
|
+
if (value === void 0 || value === null) return true;
|
|
5305
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
5306
|
+
if (isPlainRecord(value)) return Object.keys(value).length === 0;
|
|
5307
|
+
return false;
|
|
4693
5308
|
}
|
|
4694
|
-
function
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
5309
|
+
function collectPersistedLeafPaths(form) {
|
|
5310
|
+
const out = [];
|
|
5311
|
+
walk(form, []);
|
|
5312
|
+
return out;
|
|
5313
|
+
function walk(node, prefix) {
|
|
5314
|
+
if (Array.isArray(node)) {
|
|
5315
|
+
for (let i = 0; i < node.length; i++) {
|
|
5316
|
+
walk(node[i], [...prefix, i]);
|
|
5317
|
+
}
|
|
5318
|
+
return;
|
|
5319
|
+
}
|
|
5320
|
+
if (isPlainRecord(node)) {
|
|
5321
|
+
for (const key of Object.keys(node)) {
|
|
5322
|
+
walk(node[key], [...prefix, key]);
|
|
5323
|
+
}
|
|
5324
|
+
return;
|
|
5325
|
+
}
|
|
5326
|
+
if (prefix.length === 0) return;
|
|
5327
|
+
out.push(canonicalizePath(prefix).key);
|
|
4702
5328
|
}
|
|
4703
5329
|
}
|
|
4704
|
-
function
|
|
4705
|
-
|
|
5330
|
+
function isDescendantPathKey(candidate, ancestor) {
|
|
5331
|
+
if (candidate.length <= ancestor.length) return false;
|
|
5332
|
+
if (!ancestor.endsWith("]")) return false;
|
|
5333
|
+
const childPrefix = `${ancestor.slice(0, -1)},`;
|
|
5334
|
+
return candidate.startsWith(childPrefix);
|
|
4706
5335
|
}
|
|
4707
5336
|
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
5337
|
+
const PROTOCOL_VERSION = 1;
|
|
5338
|
+
const JOIN_COLLECTION_WINDOW_MS = 50;
|
|
5339
|
+
const SNAPSHOT_TIMEOUT_MS = 200;
|
|
5340
|
+
const MAX_LEADER_ATTEMPTS = 3;
|
|
5341
|
+
const SNAPSHOT_RESPONSE_MIN_INTERVAL_MS = 500;
|
|
5342
|
+
function isFileLikeValue(value) {
|
|
5343
|
+
if (typeof File !== "undefined" && value instanceof File) return true;
|
|
5344
|
+
if (typeof Blob !== "undefined" && value instanceof Blob) return true;
|
|
5345
|
+
return false;
|
|
4713
5346
|
}
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
5347
|
+
function isInboundShapeAcceptable(schema, path, value) {
|
|
5348
|
+
if (isFileLikeValue(value)) return false;
|
|
5349
|
+
let kind;
|
|
5350
|
+
if (Array.isArray(value)) {
|
|
5351
|
+
kind = "array";
|
|
5352
|
+
} else if (value !== null && typeof value === "object" && isPlainRecord(value)) {
|
|
5353
|
+
kind = "object";
|
|
5354
|
+
} else {
|
|
5355
|
+
kind = slimKindOf(value);
|
|
4718
5356
|
}
|
|
4719
|
-
const
|
|
4720
|
-
|
|
4721
|
-
if (
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
const materialisedDefaults = resolvedDefaults.kind === "sync" ? resolvedDefaults.value : void 0;
|
|
4725
|
-
const { defaultValues: _droppedDefaults, ...configWithoutDefaults } = configuration;
|
|
4726
|
-
const trichotomyOverride = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
|
|
4727
|
-
const merged = mergeWithDefaults(registry.defaults, trichotomyOverride);
|
|
4728
|
-
const maxRecursionDepth = normalizeNumericOption({
|
|
4729
|
-
value: merged.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH,
|
|
4730
|
-
source: "useForm.maxRecursionDepth",
|
|
4731
|
-
allowInfinity: true,
|
|
4732
|
-
min: 0,
|
|
4733
|
-
defaultValue: DEFAULT_MAX_RECURSION_DEPTH
|
|
4734
|
-
});
|
|
4735
|
-
const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
|
|
4736
|
-
if (configuration.persist !== void 0 && configuration.key === void 0) {
|
|
4737
|
-
throw new AnonPersistError({
|
|
4738
|
-
cause: "no-key",
|
|
4739
|
-
schemaFields: extractSchemaFields(resolvedSchema),
|
|
4740
|
-
callSite: captureUserCallSite()
|
|
4741
|
-
});
|
|
4742
|
-
}
|
|
4743
|
-
const existing = registry.forms.get(key);
|
|
4744
|
-
if (__DEV__ && existing !== void 0) {
|
|
4745
|
-
warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
|
|
4746
|
-
warnOnPersistDivergence(key, existing, configuration.persist);
|
|
4747
|
-
}
|
|
4748
|
-
const hadPendingHydration = registry.pendingHydration.has(key);
|
|
4749
|
-
const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
|
|
4750
|
-
if (existing !== void 0) ; else if (resolvedDefaults.kind === "sync") {
|
|
4751
|
-
state.defaultsResolved.value = true;
|
|
4752
|
-
}
|
|
4753
|
-
if (existing === void 0 && resolvedDefaults.kind === "async") {
|
|
4754
|
-
const factory = resolvedDefaults.factory;
|
|
4755
|
-
state.defaultValuesFactory.value = factory;
|
|
4756
|
-
if (hadPendingHydration) {
|
|
4757
|
-
state.hydrating.value = false;
|
|
4758
|
-
state.defaultsResolved.value = true;
|
|
4759
|
-
} else if (registry.ssr) {
|
|
4760
|
-
if (configuration.__ssrAccessed === true) {
|
|
4761
|
-
registry.enqueuePrefetch(key);
|
|
4762
|
-
}
|
|
4763
|
-
onServerPrefetch(() => {
|
|
4764
|
-
if (!registry.shouldPrefetch(key)) return;
|
|
4765
|
-
return state.activate();
|
|
4766
|
-
});
|
|
4767
|
-
}
|
|
4768
|
-
}
|
|
4769
|
-
if (getCurrentScope() !== void 0) {
|
|
4770
|
-
const releaseConsumer = registry.trackConsumer(key);
|
|
4771
|
-
onScopeDispose(releaseConsumer);
|
|
4772
|
-
}
|
|
4773
|
-
const persistDisabledByAnonRule = merged.persist !== void 0 && enforceAnonPersistRule(state.formKey, registry.ssr);
|
|
4774
|
-
if (existing === void 0 && !registry.ssr) {
|
|
4775
|
-
if (merged.persist !== void 0 && !persistDisabledByAnonRule) {
|
|
4776
|
-
const resolvedPersist = normalizePersistConfig(merged.persist);
|
|
4777
|
-
const storageKind = resolvedPersist.storage;
|
|
4778
|
-
const isBuiltinStorage = typeof storageKind === "string";
|
|
4779
|
-
const secureContextOk = !isBuiltinStorage || isSecureContext();
|
|
4780
|
-
if (!secureContextOk) {
|
|
4781
|
-
const feature = storageKind === "session" ? "persist:session" : "persist:local";
|
|
4782
|
-
warnOnceInsecureContext(feature);
|
|
4783
|
-
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
4784
|
-
} else {
|
|
4785
|
-
const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
|
|
4786
|
-
void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
|
|
4787
|
-
const persistenceModule = wirePersistence(state, resolvedPersist);
|
|
4788
|
-
state.modules.set(PERSISTENCE_MODULE_KEY, persistenceModule);
|
|
4789
|
-
state.registerDrain(() => persistenceModule.awaitPendingWrites());
|
|
4790
|
-
state.registerCleanup(() => persistenceModule.dispose());
|
|
4791
|
-
}
|
|
4792
|
-
} else {
|
|
4793
|
-
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
5357
|
+
const accepted = schema.getSlimPrimitiveTypesAtPath(path);
|
|
5358
|
+
if (!accepted.has(kind)) return false;
|
|
5359
|
+
if (Array.isArray(value)) {
|
|
5360
|
+
for (let i = 0; i < value.length; i++) {
|
|
5361
|
+
if (!isInboundShapeAcceptable(schema, [...path, i], value[i])) return false;
|
|
4794
5362
|
}
|
|
5363
|
+
return true;
|
|
4795
5364
|
}
|
|
4796
|
-
if (
|
|
4797
|
-
const
|
|
4798
|
-
|
|
4799
|
-
if (hasBroadcastChannel && secureContext) {
|
|
4800
|
-
let channelName;
|
|
4801
|
-
try {
|
|
4802
|
-
channelName = `attaform:sync:${state.formKey}:${hashStableString(state.schema.fingerprint())}`;
|
|
4803
|
-
} catch {
|
|
4804
|
-
channelName = null;
|
|
4805
|
-
}
|
|
4806
|
-
if (channelName !== null) {
|
|
4807
|
-
const syncModule = createMultiTabSyncModule(state, channelName, {
|
|
4808
|
-
isSensitivePath: state.isSensitivePath,
|
|
4809
|
-
noSyncPaths: state.noSyncPaths,
|
|
4810
|
-
validateForm: (form) => {
|
|
4811
|
-
const result = state.schema.validateAtPath(form, void 0, { sync: true });
|
|
4812
|
-
if (result instanceof Promise) return;
|
|
4813
|
-
if (!result.success) {
|
|
4814
|
-
throw new Error("attaform multi-tab sync: post-apply schema validation failed");
|
|
4815
|
-
}
|
|
4816
|
-
}
|
|
4817
|
-
});
|
|
4818
|
-
state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
|
|
4819
|
-
state.registerCleanup(() => syncModule.dispose());
|
|
4820
|
-
}
|
|
4821
|
-
} else if (hasBroadcastChannel && !secureContext) {
|
|
4822
|
-
warnOnceInsecureContext("multiTab");
|
|
5365
|
+
if (isPlainRecord(value)) {
|
|
5366
|
+
for (const key of Object.keys(value)) {
|
|
5367
|
+
if (!isInboundShapeAcceptable(schema, [...path, key], value[key])) return false;
|
|
4823
5368
|
}
|
|
5369
|
+
return true;
|
|
4824
5370
|
}
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
5371
|
+
return true;
|
|
5372
|
+
}
|
|
5373
|
+
function diffBlankPaths(prev, curr) {
|
|
5374
|
+
const added = [];
|
|
5375
|
+
const removed = [];
|
|
5376
|
+
const prevSet = new Set(prev);
|
|
5377
|
+
for (const k of curr) if (!prevSet.has(k)) added.push(k);
|
|
5378
|
+
for (const k of prev) if (!curr.has(k)) removed.push(k);
|
|
5379
|
+
return { added, removed };
|
|
5380
|
+
}
|
|
5381
|
+
function snapshotForm(form) {
|
|
5382
|
+
return structuralSnapshot(form);
|
|
5383
|
+
}
|
|
5384
|
+
function stripSensitivePathsDeep(value, pathSoFar, isSensitivePath) {
|
|
5385
|
+
if (isFileLikeValue(value)) return void 0;
|
|
5386
|
+
if (value === null || typeof value !== "object") return value;
|
|
5387
|
+
if (Array.isArray(value)) {
|
|
5388
|
+
return value.map((item, i) => stripSensitivePathsDeep(item, [...pathSoFar, i], isSensitivePath));
|
|
4829
5389
|
}
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
5390
|
+
const proto = Object.getPrototypeOf(value);
|
|
5391
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
5392
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
5393
|
+
const src = value;
|
|
5394
|
+
for (const key of Object.keys(src)) {
|
|
5395
|
+
const childPath = [...pathSoFar, key];
|
|
5396
|
+
if (isSensitivePath(childPath)) {
|
|
5397
|
+
out[key] = void 0;
|
|
5398
|
+
continue;
|
|
5399
|
+
}
|
|
5400
|
+
out[key] = stripSensitivePathsDeep(src[key], childPath, isSensitivePath);
|
|
4833
5401
|
}
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
5402
|
+
return out;
|
|
5403
|
+
}
|
|
5404
|
+
function isStringArray(value) {
|
|
5405
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
5406
|
+
}
|
|
5407
|
+
function isPatchArray(value) {
|
|
5408
|
+
return Array.isArray(value) && value.every(
|
|
5409
|
+
(p) => p !== null && typeof p === "object" && Array.isArray(p.path)
|
|
5410
|
+
);
|
|
5411
|
+
}
|
|
5412
|
+
function isValidSyncMessage(data) {
|
|
5413
|
+
if (data === null || typeof data !== "object") return false;
|
|
5414
|
+
const m = data;
|
|
5415
|
+
if (m["v"] !== PROTOCOL_VERSION) return false;
|
|
5416
|
+
if (typeof m["senderId"] !== "string") return false;
|
|
5417
|
+
if (typeof m["kind"] !== "string") return false;
|
|
5418
|
+
switch (m["kind"]) {
|
|
5419
|
+
case "hello":
|
|
5420
|
+
case "announce":
|
|
5421
|
+
return true;
|
|
5422
|
+
case "requestSnapshot":
|
|
5423
|
+
return typeof m["targetId"] === "string";
|
|
5424
|
+
case "snapshot":
|
|
5425
|
+
return isStringArray(m["blankPaths"]) && "form" in m;
|
|
5426
|
+
case "patches":
|
|
5427
|
+
return isPatchArray(m["formPatches"]) && isStringArray(m["blankPathsAdded"]) && isStringArray(m["blankPathsRemoved"]);
|
|
5428
|
+
default:
|
|
5429
|
+
return false;
|
|
4837
5430
|
}
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
5431
|
+
}
|
|
5432
|
+
function generateSenderId() {
|
|
5433
|
+
try {
|
|
5434
|
+
return globalThis.crypto.randomUUID();
|
|
5435
|
+
} catch {
|
|
5436
|
+
return `atta-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;
|
|
4841
5437
|
}
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
5438
|
+
}
|
|
5439
|
+
function createMultiTabSyncModule(state, channelName, options) {
|
|
5440
|
+
if (typeof BroadcastChannel === "undefined") {
|
|
5441
|
+
return {
|
|
5442
|
+
dispose: () => void 0,
|
|
5443
|
+
lifecycle: () => "established",
|
|
5444
|
+
senderId: "",
|
|
5445
|
+
channelName
|
|
5446
|
+
};
|
|
4845
5447
|
}
|
|
4846
|
-
|
|
4847
|
-
|
|
5448
|
+
let channel;
|
|
5449
|
+
try {
|
|
5450
|
+
channel = new BroadcastChannel(channelName);
|
|
5451
|
+
} catch {
|
|
5452
|
+
return {
|
|
5453
|
+
dispose: () => void 0,
|
|
5454
|
+
lifecycle: () => "established",
|
|
5455
|
+
senderId: "",
|
|
5456
|
+
channelName
|
|
5457
|
+
};
|
|
4848
5458
|
}
|
|
4849
|
-
const
|
|
4850
|
-
|
|
4851
|
-
|
|
5459
|
+
const senderId = generateSenderId();
|
|
5460
|
+
let lifecycle = "joining";
|
|
5461
|
+
let disposed = false;
|
|
5462
|
+
const peerIds = /* @__PURE__ */ new Set();
|
|
5463
|
+
const lastSnapshotResponseAt = /* @__PURE__ */ new Map();
|
|
5464
|
+
let joinCollectionTimer = null;
|
|
5465
|
+
let snapshotTimeoutTimer = null;
|
|
5466
|
+
let leaderAttempts = 0;
|
|
5467
|
+
let prior = {
|
|
5468
|
+
form: snapshotForm(state.form.value),
|
|
5469
|
+
blankPathsSnapshot: [...state.blankPaths]
|
|
5470
|
+
};
|
|
5471
|
+
function safePost(msg) {
|
|
5472
|
+
if (disposed) return;
|
|
5473
|
+
try {
|
|
5474
|
+
channel.postMessage(msg);
|
|
5475
|
+
} catch {
|
|
5476
|
+
}
|
|
4852
5477
|
}
|
|
4853
|
-
|
|
4854
|
-
|
|
5478
|
+
function refreshPrior() {
|
|
5479
|
+
prior = {
|
|
5480
|
+
form: snapshotForm(state.form.value),
|
|
5481
|
+
blankPathsSnapshot: [...state.blankPaths]
|
|
5482
|
+
};
|
|
4855
5483
|
}
|
|
4856
|
-
|
|
4857
|
-
|
|
5484
|
+
function isPathLocallySuppressed(path) {
|
|
5485
|
+
if (options.isSensitivePath(path)) return true;
|
|
5486
|
+
const { key } = canonicalizePath([...path]);
|
|
5487
|
+
if (options.noSyncPaths.has(key)) return true;
|
|
5488
|
+
return false;
|
|
4858
5489
|
}
|
|
4859
|
-
|
|
4860
|
-
|
|
5490
|
+
function postPatches() {
|
|
5491
|
+
if (lifecycle !== "established") return;
|
|
5492
|
+
const next = snapshotForm(state.form.value);
|
|
5493
|
+
const rawPatches = [];
|
|
5494
|
+
diffAndApply(prior.form, next, [], (p) => rawPatches.push(p));
|
|
5495
|
+
const safePatches = [];
|
|
5496
|
+
for (const p of rawPatches) {
|
|
5497
|
+
if (isPathLocallySuppressed(p.path)) continue;
|
|
5498
|
+
if ("value" in p && isFileLikeValue(p.value)) continue;
|
|
5499
|
+
safePatches.push(p);
|
|
5500
|
+
}
|
|
5501
|
+
const { added, removed } = diffBlankPaths(prior.blankPathsSnapshot, state.blankPaths);
|
|
5502
|
+
if (safePatches.length === 0 && added.length === 0 && removed.length === 0) {
|
|
5503
|
+
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
5504
|
+
return;
|
|
5505
|
+
}
|
|
5506
|
+
safePost({
|
|
5507
|
+
v: PROTOCOL_VERSION,
|
|
5508
|
+
kind: "patches",
|
|
5509
|
+
senderId,
|
|
5510
|
+
formPatches: safePatches,
|
|
5511
|
+
blankPathsAdded: added,
|
|
5512
|
+
blankPathsRemoved: removed
|
|
5513
|
+
});
|
|
5514
|
+
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
4861
5515
|
}
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
const maxRecursionDepth = configuration.maxRecursionDepth ?? defaults.maxRecursionDepth;
|
|
4878
|
-
const sensitiveNames = configuration.sensitiveNames ?? defaults.sensitiveNames;
|
|
4879
|
-
const multiTab = configuration.multiTab ?? defaults.multiTab;
|
|
4880
|
-
return {
|
|
4881
|
-
...configuration,
|
|
4882
|
-
...strict === void 0 ? {} : { strict },
|
|
4883
|
-
...onInvalidSubmit === void 0 ? {} : { onInvalidSubmit },
|
|
4884
|
-
...history === void 0 ? {} : { history },
|
|
4885
|
-
...rememberVariants === void 0 ? {} : { rememberVariants },
|
|
4886
|
-
...coerce === void 0 ? {} : { coerce },
|
|
4887
|
-
...validateOn === void 0 ? {} : { validateOn },
|
|
4888
|
-
...debounceMs === void 0 ? {} : { debounceMs },
|
|
4889
|
-
...shouldShowErrors === void 0 ? {} : { shouldShowErrors },
|
|
4890
|
-
...maxRecursionDepth === void 0 ? {} : { maxRecursionDepth },
|
|
4891
|
-
...sensitiveNames === void 0 ? {} : { sensitiveNames },
|
|
4892
|
-
...multiTab === void 0 ? {} : { multiTab }
|
|
4893
|
-
};
|
|
4894
|
-
}
|
|
4895
|
-
const HISTORY_MODULE_KEY = "history";
|
|
4896
|
-
function buildFreshState(key, schema, configuration, registry) {
|
|
4897
|
-
const pending = registry.pendingHydration.get(key);
|
|
4898
|
-
if (pending !== void 0) registry.pendingHydration.delete(key);
|
|
4899
|
-
const walked = walkUnsetSentinels(
|
|
4900
|
-
configuration.defaultValues,
|
|
4901
|
-
schema
|
|
4902
|
-
);
|
|
4903
|
-
let initialBlankPaths;
|
|
4904
|
-
if (pending === void 0) {
|
|
4905
|
-
initialBlankPaths = walked.paths;
|
|
5516
|
+
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
5517
|
+
if (disposed) return;
|
|
5518
|
+
if (lifecycle !== "established") return;
|
|
5519
|
+
if (meta?.crossTab === true) return;
|
|
5520
|
+
if (meta?.hydration === true) {
|
|
5521
|
+
refreshPrior();
|
|
5522
|
+
return;
|
|
5523
|
+
}
|
|
5524
|
+
postPatches();
|
|
5525
|
+
});
|
|
5526
|
+
function applyIncomingForm(form, blankPaths) {
|
|
5527
|
+
state.blankPaths.clear();
|
|
5528
|
+
for (const k of blankPaths) state.blankPaths.add(k);
|
|
5529
|
+
state.applyFormReplacement(form, { crossTab: true, persist: false });
|
|
5530
|
+
refreshPrior();
|
|
4906
5531
|
}
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
hydration: pending,
|
|
4916
|
-
...configuration.validateOn !== void 0 ? { validateOn: configuration.validateOn } : {},
|
|
4917
|
-
...configuration.debounceMs !== void 0 ? { debounceMs: configuration.debounceMs } : {},
|
|
4918
|
-
ssr: registry.ssr,
|
|
4919
|
-
// Server-only: bind the SSR prefetch coordination handles. `enqueue`
|
|
4920
|
-
// records intent on every `state.activate()` so a wizard skip-list
|
|
4921
|
-
// override or a future transform mark has a consistent set to diff
|
|
4922
|
-
// against; `shouldFire` lets the activate path bail when the
|
|
4923
|
-
// wizard explicitly skipped this key — even an explicit
|
|
4924
|
-
// `form.activate()` defers to the wizard's render-efficiency
|
|
4925
|
-
// skip-list on the server.
|
|
4926
|
-
...registry.ssr ? {
|
|
4927
|
-
ssrPrefetch: {
|
|
4928
|
-
enqueue: () => {
|
|
4929
|
-
registry.enqueuePrefetch(key);
|
|
4930
|
-
},
|
|
4931
|
-
shouldFire: () => registry.shouldPrefetch(key)
|
|
5532
|
+
function handlePatches(msg) {
|
|
5533
|
+
if (lifecycle !== "established") return;
|
|
5534
|
+
const safePatches = [];
|
|
5535
|
+
for (const p of msg.formPatches) {
|
|
5536
|
+
if (!Array.isArray(p.path)) continue;
|
|
5537
|
+
if (isPathLocallySuppressed(p.path)) continue;
|
|
5538
|
+
if ("value" in p && !isInboundShapeAcceptable(state.schema, p.path, p.value)) {
|
|
5539
|
+
continue;
|
|
4932
5540
|
}
|
|
4933
|
-
|
|
4934
|
-
...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
|
|
4935
|
-
...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
|
|
4936
|
-
...configuration.shouldShowErrors !== void 0 ? { shouldShowErrors: configuration.shouldShowErrors } : {},
|
|
4937
|
-
...initialBlankPaths !== void 0 ? { initialBlankPaths } : {},
|
|
4938
|
-
...resolvedIsSensitivePath !== void 0 ? { isSensitivePath: resolvedIsSensitivePath } : {},
|
|
4939
|
-
...resolvedSegmentMatchesSensitive !== void 0 ? { segmentMatchesSensitive: resolvedSegmentMatchesSensitive } : {}
|
|
4940
|
-
};
|
|
4941
|
-
const state = createFormStore(createOptions);
|
|
4942
|
-
registry.forms.set(
|
|
4943
|
-
key,
|
|
4944
|
-
state
|
|
4945
|
-
);
|
|
4946
|
-
return state;
|
|
4947
|
-
}
|
|
4948
|
-
let anonCounter = 0;
|
|
4949
|
-
let formInstanceCounter = 0;
|
|
4950
|
-
const ambientProvideHistory = __DEV__ ? /* @__PURE__ */ new WeakMap() : null;
|
|
4951
|
-
function recordAmbientProvide(ssr) {
|
|
4952
|
-
if (!__DEV__ || ssr || ambientProvideHistory === null) return;
|
|
4953
|
-
const instance = getCurrentInstance();
|
|
4954
|
-
if (instance === null) return;
|
|
4955
|
-
const instanceKey = instance;
|
|
4956
|
-
const entry = {
|
|
4957
|
-
source: captureUserCallSite()
|
|
4958
|
-
};
|
|
4959
|
-
const existing = ambientProvideHistory.get(instanceKey);
|
|
4960
|
-
if (existing === void 0) {
|
|
4961
|
-
ambientProvideHistory.set(instanceKey, [entry]);
|
|
4962
|
-
return;
|
|
4963
|
-
}
|
|
4964
|
-
existing.push(entry);
|
|
4965
|
-
}
|
|
4966
|
-
function resolveFormKey(key) {
|
|
4967
|
-
if (key !== void 0 && key !== null && key !== "") {
|
|
4968
|
-
if (key.startsWith(RESERVED_KEY_PREFIX)) {
|
|
4969
|
-
throw new ReservedFormKeyError(key);
|
|
5541
|
+
safePatches.push(p);
|
|
4970
5542
|
}
|
|
4971
|
-
|
|
5543
|
+
const safeBlankAdded = [];
|
|
5544
|
+
for (const k of msg.blankPathsAdded) {
|
|
5545
|
+
const segs = canonicalizePath(k).segments;
|
|
5546
|
+
if (isPathLocallySuppressed(segs)) continue;
|
|
5547
|
+
safeBlankAdded.push(k);
|
|
5548
|
+
}
|
|
5549
|
+
const safeBlankRemoved = [];
|
|
5550
|
+
for (const k of msg.blankPathsRemoved) {
|
|
5551
|
+
const segs = canonicalizePath(k).segments;
|
|
5552
|
+
if (isPathLocallySuppressed(segs)) continue;
|
|
5553
|
+
safeBlankRemoved.push(k);
|
|
5554
|
+
}
|
|
5555
|
+
if (safePatches.length === 0 && safeBlankAdded.length === 0 && safeBlankRemoved.length === 0) {
|
|
5556
|
+
return;
|
|
5557
|
+
}
|
|
5558
|
+
const candidate = applyPatchesForward(state.form.value, safePatches);
|
|
5559
|
+
try {
|
|
5560
|
+
options.validateForm(state.form.value);
|
|
5561
|
+
try {
|
|
5562
|
+
options.validateForm(candidate);
|
|
5563
|
+
} catch {
|
|
5564
|
+
return;
|
|
5565
|
+
}
|
|
5566
|
+
} catch {
|
|
5567
|
+
}
|
|
5568
|
+
const nextBlankPaths = new Set(state.blankPaths);
|
|
5569
|
+
for (const k of safeBlankRemoved) nextBlankPaths.delete(k);
|
|
5570
|
+
for (const k of safeBlankAdded) nextBlankPaths.add(k);
|
|
5571
|
+
applyIncomingForm(candidate, [...nextBlankPaths]);
|
|
4972
5572
|
}
|
|
4973
|
-
|
|
4974
|
-
|
|
5573
|
+
function handleSnapshot(msg) {
|
|
5574
|
+
if (lifecycle !== "joining") return;
|
|
5575
|
+
if (!isInboundShapeAcceptable(state.schema, [], msg.form)) return;
|
|
5576
|
+
if (snapshotTimeoutTimer !== null) {
|
|
5577
|
+
clearTimeout(snapshotTimeoutTimer);
|
|
5578
|
+
snapshotTimeoutTimer = null;
|
|
5579
|
+
}
|
|
5580
|
+
if (joinCollectionTimer !== null) {
|
|
5581
|
+
clearTimeout(joinCollectionTimer);
|
|
5582
|
+
joinCollectionTimer = null;
|
|
5583
|
+
}
|
|
5584
|
+
applyIncomingForm(msg.form, msg.blankPaths);
|
|
5585
|
+
lifecycle = "established";
|
|
5586
|
+
peerIds.clear();
|
|
4975
5587
|
}
|
|
4976
|
-
|
|
4977
|
-
}
|
|
4978
|
-
function warnOnSchemaFingerprintMismatch(key, existing, incoming) {
|
|
4979
|
-
let existingFp;
|
|
4980
|
-
let incomingFp;
|
|
4981
|
-
try {
|
|
4982
|
-
existingFp = existing.fingerprint();
|
|
4983
|
-
incomingFp = incoming.fingerprint();
|
|
4984
|
-
} catch (error) {
|
|
4985
|
-
console.error(
|
|
4986
|
-
`[attaform] fingerprint() threw for key "${key}"; skipping mismatch check.`,
|
|
4987
|
-
error
|
|
4988
|
-
);
|
|
4989
|
-
return;
|
|
5588
|
+
function respondToHello() {
|
|
5589
|
+
safePost({ v: PROTOCOL_VERSION, kind: "announce", senderId });
|
|
4990
5590
|
}
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
`[attaform] useForm({ key: "${key}" }) passed a persist config but the first useForm({ key }) call didn't wire persistence; the new config is silently dropped. Pass persist on the first call, or remove persist here to make the inheritance explicit.`
|
|
5005
|
-
);
|
|
5006
|
-
return;
|
|
5591
|
+
function respondToSnapshotRequest(requesterId) {
|
|
5592
|
+
const now = Date.now();
|
|
5593
|
+
const last = lastSnapshotResponseAt.get(requesterId);
|
|
5594
|
+
if (last !== void 0 && now - last < SNAPSHOT_RESPONSE_MIN_INTERVAL_MS) return;
|
|
5595
|
+
lastSnapshotResponseAt.set(requesterId, now);
|
|
5596
|
+
const scrubbedForm = stripSensitivePathsDeep(state.form.value, [], options.isSensitivePath);
|
|
5597
|
+
safePost({
|
|
5598
|
+
v: PROTOCOL_VERSION,
|
|
5599
|
+
kind: "snapshot",
|
|
5600
|
+
senderId,
|
|
5601
|
+
form: scrubbedForm,
|
|
5602
|
+
blankPaths: [...state.blankPaths]
|
|
5603
|
+
});
|
|
5007
5604
|
}
|
|
5008
|
-
|
|
5009
|
-
console.warn(
|
|
5010
|
-
`[attaform] useForm({ key: "${key}" }) passed a persist config that differs from the first useForm({ key }) call's; first wins, this one is ignored.
|
|
5011
|
-
wired: ${describePersist(wired.wiredConfig)}
|
|
5012
|
-
incoming: ${describePersist(incomingNormalized)}`
|
|
5013
|
-
);
|
|
5014
|
-
}
|
|
5015
|
-
function persistConfigsEquivalent(a, b) {
|
|
5016
|
-
if (a.storage !== b.storage) return false;
|
|
5017
|
-
if ((a.key ?? void 0) !== (b.key ?? void 0)) return false;
|
|
5018
|
-
if ((a.debounceMs ?? void 0) !== (b.debounceMs ?? void 0)) return false;
|
|
5019
|
-
return true;
|
|
5020
|
-
}
|
|
5021
|
-
function describePersist(config) {
|
|
5022
|
-
const storage = typeof config.storage === "string" ? config.storage : "custom-adapter";
|
|
5023
|
-
const parts = [`storage=${storage}`];
|
|
5024
|
-
if (config.key !== void 0) parts.push(`key=${config.key}`);
|
|
5025
|
-
if (config.debounceMs !== void 0) parts.push(`debounceMs=${config.debounceMs}`);
|
|
5026
|
-
return `{ ${parts.join(", ")} }`;
|
|
5027
|
-
}
|
|
5028
|
-
function wirePersistence(state, config) {
|
|
5029
|
-
const fingerprint = hashStableString(state.schema.fingerprint());
|
|
5030
|
-
const base = resolveStorageKeyBase(config, state.formKey);
|
|
5031
|
-
const key = `${base}:${fingerprint}`;
|
|
5032
|
-
const debounceMs = normalizeNumericOption({
|
|
5033
|
-
value: config.debounceMs ?? DEFAULT_PERSISTENCE_DEBOUNCE_MS,
|
|
5034
|
-
source: "useForm.persist.debounceMs",
|
|
5035
|
-
allowInfinity: false,
|
|
5036
|
-
min: 0,
|
|
5037
|
-
defaultValue: DEFAULT_PERSISTENCE_DEBOUNCE_MS
|
|
5038
|
-
});
|
|
5039
|
-
const include = config.include ?? "form";
|
|
5040
|
-
const clearOnSubmitSuccess = config.clearOnSubmitSuccess ?? true;
|
|
5041
|
-
const adapterPromise = getStorageAdapter(config.storage);
|
|
5042
|
-
let disposed = false;
|
|
5043
|
-
let inFlightFinalFlush = null;
|
|
5044
|
-
let pendingOptedInPaths = null;
|
|
5045
|
-
const writer = createDebouncedWriter(async () => {
|
|
5046
|
-
const optedInPaths = pendingOptedInPaths ?? new Set(state.persistOptIns.optedInPaths());
|
|
5047
|
-
pendingOptedInPaths = null;
|
|
5048
|
-
const adapter = await adapterPromise;
|
|
5605
|
+
channel.onmessage = (event) => {
|
|
5049
5606
|
if (disposed) return;
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
return;
|
|
5607
|
+
try {
|
|
5608
|
+
const data = event.data;
|
|
5609
|
+
if (!isValidSyncMessage(data)) return;
|
|
5610
|
+
const msg = data;
|
|
5611
|
+
if (msg.senderId === senderId) return;
|
|
5612
|
+
switch (msg.kind) {
|
|
5613
|
+
case "hello":
|
|
5614
|
+
if (lifecycle !== "established") return;
|
|
5615
|
+
respondToHello();
|
|
5616
|
+
break;
|
|
5617
|
+
case "announce":
|
|
5618
|
+
if (lifecycle === "joining") peerIds.add(msg.senderId);
|
|
5619
|
+
break;
|
|
5620
|
+
case "requestSnapshot":
|
|
5621
|
+
if (lifecycle !== "established") return;
|
|
5622
|
+
if (msg.targetId !== senderId) return;
|
|
5623
|
+
respondToSnapshotRequest(msg.senderId);
|
|
5624
|
+
break;
|
|
5625
|
+
case "snapshot":
|
|
5626
|
+
handleSnapshot(msg);
|
|
5627
|
+
break;
|
|
5628
|
+
case "patches":
|
|
5629
|
+
handlePatches(msg);
|
|
5630
|
+
break;
|
|
5631
|
+
}
|
|
5632
|
+
} catch {
|
|
5053
5633
|
}
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
const filteredSchemaErrors = filterErrorsByPaths(state.schemaErrors, optedInPaths);
|
|
5057
|
-
const filteredUserErrors = filterErrorsByPaths(state.userErrors, optedInPaths);
|
|
5058
|
-
const filteredTransientEmpty = /* @__PURE__ */ new Set();
|
|
5059
|
-
for (const tk of state.blankPaths) {
|
|
5060
|
-
if (optedInPaths.has(tk)) filteredTransientEmpty.add(tk);
|
|
5061
|
-
}
|
|
5062
|
-
const payload = buildPersistedPayload(
|
|
5063
|
-
filteredForm,
|
|
5064
|
-
include,
|
|
5065
|
-
filteredSchemaErrors,
|
|
5066
|
-
filteredUserErrors,
|
|
5067
|
-
filteredTransientEmpty
|
|
5068
|
-
);
|
|
5069
|
-
await adapter.setItem(key, payload);
|
|
5070
|
-
}, debounceMs);
|
|
5071
|
-
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
5072
|
-
if (disposed || inFlightFinalFlush !== null) return;
|
|
5073
|
-
if (meta?.crossTab === true) return;
|
|
5074
|
-
if (meta?.persist !== true) return;
|
|
5075
|
-
pendingOptedInPaths = new Set(state.persistOptIns.optedInPaths());
|
|
5076
|
-
writer.schedule();
|
|
5077
|
-
});
|
|
5078
|
-
const unsubscribeSuccess = clearOnSubmitSuccess ? state.onSubmitSuccess(() => {
|
|
5634
|
+
};
|
|
5635
|
+
function electLeaderAndRequest() {
|
|
5079
5636
|
if (disposed) return;
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5637
|
+
if (peerIds.size === 0) {
|
|
5638
|
+
lifecycle = "established";
|
|
5639
|
+
refreshPrior();
|
|
5640
|
+
return;
|
|
5641
|
+
}
|
|
5642
|
+
const sorted = [...peerIds].sort();
|
|
5643
|
+
const leaderId = sorted[0];
|
|
5644
|
+
peerIds.delete(leaderId);
|
|
5645
|
+
leaderAttempts++;
|
|
5646
|
+
safePost({
|
|
5647
|
+
v: PROTOCOL_VERSION,
|
|
5648
|
+
kind: "requestSnapshot",
|
|
5649
|
+
senderId,
|
|
5650
|
+
targetId: leaderId
|
|
5651
|
+
});
|
|
5652
|
+
snapshotTimeoutTimer = setTimeout(() => {
|
|
5653
|
+
snapshotTimeoutTimer = null;
|
|
5084
5654
|
if (disposed) return;
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
const adapter = await adapterPromise;
|
|
5090
|
-
if (disposed) return;
|
|
5091
|
-
void cleanupOrphanKeys(adapter, base, key);
|
|
5092
|
-
try {
|
|
5093
|
-
const raw = await adapter.getItem(key);
|
|
5094
|
-
const payload = readPersistedPayload(raw);
|
|
5095
|
-
if (payload === null) {
|
|
5096
|
-
if (raw !== null && raw !== void 0) {
|
|
5097
|
-
await adapter.removeItem(key);
|
|
5098
|
-
}
|
|
5655
|
+
if (lifecycle === "established") return;
|
|
5656
|
+
if (leaderAttempts >= MAX_LEADER_ATTEMPTS || peerIds.size === 0) {
|
|
5657
|
+
lifecycle = "established";
|
|
5658
|
+
refreshPrior();
|
|
5099
5659
|
return;
|
|
5100
5660
|
}
|
|
5661
|
+
electLeaderAndRequest();
|
|
5662
|
+
}, SNAPSHOT_TIMEOUT_MS);
|
|
5663
|
+
}
|
|
5664
|
+
function joinFlow() {
|
|
5665
|
+
safePost({ v: PROTOCOL_VERSION, kind: "hello", senderId });
|
|
5666
|
+
joinCollectionTimer = setTimeout(() => {
|
|
5667
|
+
joinCollectionTimer = null;
|
|
5101
5668
|
if (disposed) return;
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5669
|
+
if (lifecycle === "established") return;
|
|
5670
|
+
electLeaderAndRequest();
|
|
5671
|
+
}, JOIN_COLLECTION_WINDOW_MS);
|
|
5672
|
+
}
|
|
5673
|
+
joinFlow();
|
|
5674
|
+
return {
|
|
5675
|
+
dispose: () => {
|
|
5676
|
+
if (disposed) return;
|
|
5677
|
+
disposed = true;
|
|
5678
|
+
if (joinCollectionTimer !== null) {
|
|
5679
|
+
clearTimeout(joinCollectionTimer);
|
|
5680
|
+
joinCollectionTimer = null;
|
|
5112
5681
|
}
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
state.originalBlankPaths.add(key2);
|
|
5682
|
+
if (snapshotTimeoutTimer !== null) {
|
|
5683
|
+
clearTimeout(snapshotTimeoutTimer);
|
|
5684
|
+
snapshotTimeoutTimer = null;
|
|
5117
5685
|
}
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
}
|
|
5123
|
-
if (payload.data.userErrors !== void 0) {
|
|
5124
|
-
const flat = payload.data.userErrors.flatMap(([, errs]) => errs);
|
|
5125
|
-
state.setAllUserErrors(flat);
|
|
5126
|
-
}
|
|
5686
|
+
unsubscribeChange();
|
|
5687
|
+
try {
|
|
5688
|
+
channel.close();
|
|
5689
|
+
} catch {
|
|
5127
5690
|
}
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
(
|
|
5150
|
-
|
|
5151
|
-
)
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5691
|
+
},
|
|
5692
|
+
lifecycle: () => lifecycle,
|
|
5693
|
+
senderId,
|
|
5694
|
+
channelName
|
|
5695
|
+
};
|
|
5696
|
+
}
|
|
5697
|
+
const MULTI_TAB_SYNC_MODULE_KEY = "multiTabSync";
|
|
5698
|
+
|
|
5699
|
+
const warned = /* @__PURE__ */ new Set();
|
|
5700
|
+
function warnOnceInsecureContext(feature) {
|
|
5701
|
+
if (!__DEV__) return;
|
|
5702
|
+
if (warned.has(feature)) return;
|
|
5703
|
+
warned.add(feature);
|
|
5704
|
+
const message = featureMessage(feature);
|
|
5705
|
+
console.warn(`[attaform] ${message}`);
|
|
5706
|
+
}
|
|
5707
|
+
function featureMessage(feature) {
|
|
5708
|
+
switch (feature) {
|
|
5709
|
+
case "multiTab":
|
|
5710
|
+
return "Multi-tab sync requires a secure context (HTTPS or localhost). Plain HTTP on a real hostname is interceptable by network observers, so the sync module is disabled. Serve over HTTPS in production (or develop on `localhost`) to enable cross-tab synchronisation. Use `multiTab: false` on `useForm` to silence this warning.";
|
|
5711
|
+
case "persist:local":
|
|
5712
|
+
return "Built-in `persist: 'local'` storage requires a secure context (HTTPS or localhost). Plain HTTP on a real hostname is MITM-interceptable, so the persistence layer is disabled. Serve over HTTPS to enable localStorage persistence, or pass a custom storage adapter to opt out of the secure-context gate.";
|
|
5713
|
+
case "persist:session":
|
|
5714
|
+
return "Built-in `persist: 'session'` storage requires a secure context (HTTPS or localhost). Plain HTTP on a real hostname is MITM-interceptable, so the persistence layer is disabled. Serve over HTTPS to enable sessionStorage persistence, or pass a custom storage adapter to opt out of the secure-context gate.";
|
|
5715
|
+
}
|
|
5716
|
+
}
|
|
5717
|
+
function isSecureContext() {
|
|
5718
|
+
return typeof window !== "undefined" && window.isSecureContext === true;
|
|
5719
|
+
}
|
|
5720
|
+
|
|
5721
|
+
function resolveTrichotomy(input) {
|
|
5722
|
+
if (typeof input === "function") {
|
|
5723
|
+
return { kind: "async", factory: input };
|
|
5724
|
+
}
|
|
5725
|
+
return { kind: "sync", value: input };
|
|
5726
|
+
}
|
|
5727
|
+
|
|
5728
|
+
function useAbstractForm(configuration, options) {
|
|
5729
|
+
if (configuration === void 0 || configuration === null || configuration.schema === void 0) {
|
|
5730
|
+
throw new InvalidUseFormConfigError();
|
|
5731
|
+
}
|
|
5732
|
+
const key = resolveFormKey(configuration.key);
|
|
5733
|
+
const instance = getCurrentInstance();
|
|
5734
|
+
if (instance !== null) ensureAttaformInstalled(instance.appContext.app);
|
|
5735
|
+
const registry = options?.registry ?? useRegistry();
|
|
5736
|
+
const resolvedDefaults = resolveTrichotomy(configuration.defaultValues);
|
|
5737
|
+
const materialisedDefaults = resolvedDefaults.kind === "sync" ? resolvedDefaults.value : void 0;
|
|
5738
|
+
const { defaultValues: _droppedDefaults, ...configWithoutDefaults } = configuration;
|
|
5739
|
+
const trichotomyOverride = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
|
|
5740
|
+
const merged = mergeWithDefaults(registry.defaults, trichotomyOverride);
|
|
5741
|
+
const maxRecursionDepth = normalizeNumericOption({
|
|
5742
|
+
value: merged.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH,
|
|
5743
|
+
source: "useForm.maxRecursionDepth",
|
|
5744
|
+
allowInfinity: true,
|
|
5745
|
+
min: 0,
|
|
5746
|
+
defaultValue: DEFAULT_MAX_RECURSION_DEPTH
|
|
5747
|
+
});
|
|
5748
|
+
const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
|
|
5749
|
+
if (configuration.persist !== void 0 && configuration.key === void 0) {
|
|
5750
|
+
throw new AnonPersistError({
|
|
5751
|
+
cause: "no-key",
|
|
5752
|
+
schemaFields: extractSchemaFields(resolvedSchema),
|
|
5753
|
+
callSite: captureUserCallSite()
|
|
5754
|
+
});
|
|
5755
|
+
}
|
|
5756
|
+
const existing = registry.forms.get(key);
|
|
5757
|
+
if (__DEV__ && existing !== void 0) {
|
|
5758
|
+
warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
|
|
5759
|
+
warnOnPersistDivergence(key, existing, configuration.persist);
|
|
5760
|
+
}
|
|
5761
|
+
const hadPendingHydration = registry.pendingHydration.has(key);
|
|
5762
|
+
const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
|
|
5763
|
+
if (existing !== void 0) ; else if (resolvedDefaults.kind === "sync") {
|
|
5764
|
+
state.defaultsResolved.value = true;
|
|
5765
|
+
}
|
|
5766
|
+
if (existing === void 0 && resolvedDefaults.kind === "async") {
|
|
5767
|
+
const factory = resolvedDefaults.factory;
|
|
5768
|
+
state.defaultValuesFactory.value = factory;
|
|
5769
|
+
if (hadPendingHydration) {
|
|
5770
|
+
state.hydrating.value = false;
|
|
5771
|
+
state.defaultsResolved.value = true;
|
|
5772
|
+
} else if (registry.ssr) {
|
|
5773
|
+
if (configuration.__ssrAccessed === true) {
|
|
5774
|
+
registry.enqueuePrefetch(key);
|
|
5156
5775
|
}
|
|
5776
|
+
onServerPrefetch(() => {
|
|
5777
|
+
if (!registry.shouldPrefetch(key)) return;
|
|
5778
|
+
return state.activate();
|
|
5779
|
+
});
|
|
5157
5780
|
}
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5781
|
+
}
|
|
5782
|
+
if (getCurrentScope() !== void 0) {
|
|
5783
|
+
const releaseConsumer = registry.trackConsumer(key);
|
|
5784
|
+
onScopeDispose(releaseConsumer);
|
|
5785
|
+
}
|
|
5786
|
+
const persistDisabledByAnonRule = merged.persist !== void 0 && enforceAnonPersistRule(state.formKey, registry.ssr);
|
|
5787
|
+
if (existing === void 0 && !registry.ssr) {
|
|
5788
|
+
if (merged.persist !== void 0 && !persistDisabledByAnonRule) {
|
|
5789
|
+
const resolvedPersist = normalizePersistConfig(merged.persist);
|
|
5790
|
+
const storageKind = resolvedPersist.storage;
|
|
5791
|
+
const isBuiltinStorage = typeof storageKind === "string";
|
|
5792
|
+
const secureContextOk = !isBuiltinStorage || isSecureContext();
|
|
5793
|
+
if (!secureContextOk) {
|
|
5794
|
+
const feature = storageKind === "session" ? "persist:session" : "persist:local";
|
|
5795
|
+
warnOnceInsecureContext(feature);
|
|
5796
|
+
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
5797
|
+
} else {
|
|
5798
|
+
const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
|
|
5799
|
+
void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
|
|
5800
|
+
const persistenceModule = wirePersistence(state, resolvedPersist);
|
|
5801
|
+
state.modules.set(PERSISTENCE_MODULE_KEY, persistenceModule);
|
|
5802
|
+
state.registerDrain(() => persistenceModule.awaitPendingWrites());
|
|
5803
|
+
state.registerCleanup(() => persistenceModule.dispose());
|
|
5804
|
+
}
|
|
5176
5805
|
} else {
|
|
5177
|
-
|
|
5806
|
+
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
5178
5807
|
}
|
|
5179
|
-
await adapter.setItem(
|
|
5180
|
-
key,
|
|
5181
|
-
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5182
|
-
);
|
|
5183
5808
|
}
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
if (
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5809
|
+
if (existing === void 0 && merged.multiTab === true && configuration.key !== void 0 && !registry.ssr) {
|
|
5810
|
+
const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
|
|
5811
|
+
const secureContext = isSecureContext();
|
|
5812
|
+
if (hasBroadcastChannel && secureContext) {
|
|
5813
|
+
let channelName;
|
|
5814
|
+
try {
|
|
5815
|
+
channelName = `attaform:sync:${state.formKey}:${hashStableString(state.schema.fingerprint())}`;
|
|
5816
|
+
} catch {
|
|
5817
|
+
channelName = null;
|
|
5818
|
+
}
|
|
5819
|
+
if (channelName !== null) {
|
|
5820
|
+
const syncModule = createMultiTabSyncModule(state, channelName, {
|
|
5821
|
+
isSensitivePath: state.isSensitivePath,
|
|
5822
|
+
noSyncPaths: state.noSyncPaths,
|
|
5823
|
+
validateForm: (form) => {
|
|
5824
|
+
const result = state.schema.validateAtPath(form, void 0, { sync: true });
|
|
5825
|
+
if (result instanceof Promise) return;
|
|
5826
|
+
if (!result.success) {
|
|
5827
|
+
throw new Error("attaform multi-tab sync: post-apply schema validation failed");
|
|
5828
|
+
}
|
|
5829
|
+
}
|
|
5830
|
+
});
|
|
5831
|
+
state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
|
|
5832
|
+
state.registerCleanup(() => syncModule.dispose());
|
|
5833
|
+
}
|
|
5834
|
+
} else if (hasBroadcastChannel && !secureContext) {
|
|
5835
|
+
warnOnceInsecureContext("multiTab");
|
|
5201
5836
|
}
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5837
|
+
}
|
|
5838
|
+
if (existing === void 0 && merged.history !== void 0) {
|
|
5839
|
+
const historyModule = createHistoryModule(state, merged.history);
|
|
5840
|
+
state.modules.set(HISTORY_MODULE_KEY, historyModule);
|
|
5841
|
+
state.registerCleanup(() => historyModule.dispose());
|
|
5842
|
+
}
|
|
5843
|
+
if (configuration.key === void 0) {
|
|
5844
|
+
recordAmbientProvide(registry.ssr);
|
|
5845
|
+
provide(kFormContext, state);
|
|
5846
|
+
}
|
|
5847
|
+
const formInstanceId = getCurrentInstance() !== null ? useId() : `atta:form-instance:${formInstanceCounter++}`;
|
|
5848
|
+
if (getCurrentInstance() !== null) {
|
|
5849
|
+
provide(kFormInstanceId, formInstanceId);
|
|
5850
|
+
}
|
|
5851
|
+
const apiOptions = {};
|
|
5852
|
+
if (merged.onInvalidSubmit !== void 0) {
|
|
5853
|
+
apiOptions.onInvalidSubmit = merged.onInvalidSubmit;
|
|
5854
|
+
}
|
|
5855
|
+
const history = state.modules.get(HISTORY_MODULE_KEY);
|
|
5856
|
+
if (history !== void 0) {
|
|
5857
|
+
apiOptions.history = history;
|
|
5858
|
+
}
|
|
5859
|
+
if (merged.validateOn !== void 0) {
|
|
5860
|
+
apiOptions.validateOn = merged.validateOn;
|
|
5861
|
+
}
|
|
5862
|
+
const mergedDebounceMs = merged.debounceMs;
|
|
5863
|
+
if (mergedDebounceMs !== void 0) {
|
|
5864
|
+
apiOptions.debounceMs = mergedDebounceMs;
|
|
5865
|
+
}
|
|
5866
|
+
if (merged.getDisplayState !== void 0) {
|
|
5867
|
+
apiOptions.getDisplayState = merged.getDisplayState;
|
|
5868
|
+
}
|
|
5869
|
+
if (merged.coerce !== void 0) {
|
|
5870
|
+
apiOptions.coerce = merged.coerce;
|
|
5871
|
+
}
|
|
5872
|
+
if (merged.rememberVariants !== void 0) {
|
|
5873
|
+
apiOptions.rememberVariants = merged.rememberVariants;
|
|
5874
|
+
}
|
|
5875
|
+
if (merged.autoAria !== void 0) {
|
|
5876
|
+
apiOptions.autoAria = merged.autoAria;
|
|
5877
|
+
}
|
|
5878
|
+
return buildFormApi(
|
|
5879
|
+
state,
|
|
5880
|
+
formInstanceId,
|
|
5881
|
+
apiOptions
|
|
5882
|
+
);
|
|
5883
|
+
}
|
|
5884
|
+
function mergeWithDefaults(defaults, configuration) {
|
|
5885
|
+
const strict = configuration.strict ?? defaults.strict;
|
|
5886
|
+
const onInvalidSubmit = configuration.onInvalidSubmit ?? defaults.onInvalidSubmit;
|
|
5887
|
+
const history = configuration.history ?? defaults.history;
|
|
5888
|
+
const rememberVariants = configuration.rememberVariants ?? defaults.rememberVariants;
|
|
5889
|
+
const coerce = configuration.coerce ?? defaults.coerce;
|
|
5890
|
+
const validateOn = configuration.validateOn ?? defaults.validateOn;
|
|
5891
|
+
const debounceMs = configuration.debounceMs ?? defaults.debounceMs;
|
|
5892
|
+
const getDisplayState = configuration.getDisplayState ?? defaults.getDisplayState;
|
|
5893
|
+
const maxRecursionDepth = configuration.maxRecursionDepth ?? defaults.maxRecursionDepth;
|
|
5894
|
+
const sensitiveNames = configuration.sensitiveNames ?? defaults.sensitiveNames;
|
|
5895
|
+
const multiTab = configuration.multiTab ?? defaults.multiTab;
|
|
5896
|
+
const autoAria = configuration.autoAria ?? defaults.autoAria;
|
|
5897
|
+
return {
|
|
5898
|
+
...configuration,
|
|
5899
|
+
...strict === void 0 ? {} : { strict },
|
|
5900
|
+
...onInvalidSubmit === void 0 ? {} : { onInvalidSubmit },
|
|
5901
|
+
...history === void 0 ? {} : { history },
|
|
5902
|
+
...rememberVariants === void 0 ? {} : { rememberVariants },
|
|
5903
|
+
...coerce === void 0 ? {} : { coerce },
|
|
5904
|
+
...validateOn === void 0 ? {} : { validateOn },
|
|
5905
|
+
...debounceMs === void 0 ? {} : { debounceMs },
|
|
5906
|
+
...getDisplayState === void 0 ? {} : { getDisplayState },
|
|
5907
|
+
...maxRecursionDepth === void 0 ? {} : { maxRecursionDepth },
|
|
5908
|
+
...sensitiveNames === void 0 ? {} : { sensitiveNames },
|
|
5909
|
+
...multiTab === void 0 ? {} : { multiTab },
|
|
5910
|
+
...autoAria === void 0 ? {} : { autoAria }
|
|
5911
|
+
};
|
|
5912
|
+
}
|
|
5913
|
+
const HISTORY_MODULE_KEY = "history";
|
|
5914
|
+
function buildFreshState(key, schema, configuration, registry) {
|
|
5915
|
+
const pending = registry.pendingHydration.get(key);
|
|
5916
|
+
if (pending !== void 0) registry.pendingHydration.delete(key);
|
|
5917
|
+
const walked = walkUnsetSentinels(
|
|
5918
|
+
configuration.defaultValues,
|
|
5919
|
+
schema
|
|
5920
|
+
);
|
|
5921
|
+
let initialBlankPaths;
|
|
5922
|
+
if (pending === void 0) {
|
|
5923
|
+
initialBlankPaths = walked.paths;
|
|
5924
|
+
}
|
|
5925
|
+
const resolvedSensitiveNames = configuration.sensitiveNames;
|
|
5926
|
+
const resolvedIsSensitivePath = resolvedSensitiveNames === void 0 ? void 0 : createIsSensitivePath(resolvedSensitiveNames);
|
|
5927
|
+
const createOptions = {
|
|
5928
|
+
formKey: key,
|
|
5929
|
+
schema,
|
|
5930
|
+
defaultValues: walked.cleanedValues,
|
|
5931
|
+
...configuration.strict !== void 0 ? { strict: configuration.strict } : {},
|
|
5932
|
+
hydration: pending,
|
|
5933
|
+
...configuration.validateOn !== void 0 ? { validateOn: configuration.validateOn } : {},
|
|
5934
|
+
...configuration.debounceMs !== void 0 ? { debounceMs: configuration.debounceMs } : {},
|
|
5935
|
+
ssr: registry.ssr,
|
|
5936
|
+
// Server-only: bind the SSR prefetch coordination handles. `enqueue`
|
|
5937
|
+
// records intent on every `state.activate()` so a wizard skip-list
|
|
5938
|
+
// override or a future transform mark has a consistent set to diff
|
|
5939
|
+
// against; `shouldFire` lets the activate path bail when the
|
|
5940
|
+
// wizard explicitly skipped this key — even an explicit
|
|
5941
|
+
// `form.activate()` defers to the wizard's render-efficiency
|
|
5942
|
+
// skip-list on the server.
|
|
5943
|
+
...registry.ssr ? {
|
|
5944
|
+
ssrPrefetch: {
|
|
5945
|
+
enqueue: () => {
|
|
5946
|
+
registry.enqueuePrefetch(key);
|
|
5947
|
+
},
|
|
5948
|
+
shouldFire: () => registry.shouldPrefetch(key)
|
|
5949
|
+
}
|
|
5950
|
+
} : {},
|
|
5951
|
+
...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
|
|
5952
|
+
...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
|
|
5953
|
+
...configuration.getDisplayState !== void 0 ? { getDisplayState: configuration.getDisplayState } : {},
|
|
5954
|
+
...initialBlankPaths !== void 0 ? { initialBlankPaths } : {},
|
|
5955
|
+
...resolvedIsSensitivePath !== void 0 ? { isSensitivePath: resolvedIsSensitivePath } : {}
|
|
5956
|
+
};
|
|
5957
|
+
const state = createFormStore(createOptions);
|
|
5958
|
+
registry.forms.set(
|
|
5959
|
+
key,
|
|
5960
|
+
state
|
|
5961
|
+
);
|
|
5962
|
+
return state;
|
|
5963
|
+
}
|
|
5964
|
+
let anonCounter = 0;
|
|
5965
|
+
let formInstanceCounter = 0;
|
|
5966
|
+
const ambientProvideHistory = __DEV__ ? /* @__PURE__ */ new WeakMap() : null;
|
|
5967
|
+
function recordAmbientProvide(ssr) {
|
|
5968
|
+
if (!__DEV__ || ssr || ambientProvideHistory === null) return;
|
|
5969
|
+
const instance = getCurrentInstance();
|
|
5970
|
+
if (instance === null) return;
|
|
5971
|
+
const instanceKey = instance;
|
|
5972
|
+
const entry = {
|
|
5973
|
+
source: captureUserCallSite()
|
|
5974
|
+
};
|
|
5975
|
+
const existing = ambientProvideHistory.get(instanceKey);
|
|
5976
|
+
if (existing === void 0) {
|
|
5977
|
+
ambientProvideHistory.set(instanceKey, [entry]);
|
|
5978
|
+
return;
|
|
5979
|
+
}
|
|
5980
|
+
existing.push(entry);
|
|
5981
|
+
}
|
|
5982
|
+
function resolveFormKey(key) {
|
|
5983
|
+
if (key !== void 0 && key !== null && key !== "") {
|
|
5984
|
+
if (key.startsWith(RESERVED_KEY_PREFIX)) {
|
|
5985
|
+
throw new ReservedFormKeyError(key);
|
|
5214
5986
|
}
|
|
5215
|
-
|
|
5216
|
-
const userErrors = (existing.data.userErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5217
|
-
const schemaMap = new Map(schemaErrors.map(([k, v]) => [k, [...v]]));
|
|
5218
|
-
const userMap = new Map(userErrors.map(([k, v]) => [k, [...v]]));
|
|
5219
|
-
await adapter.setItem(
|
|
5220
|
-
key,
|
|
5221
|
-
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5222
|
-
);
|
|
5987
|
+
return key;
|
|
5223
5988
|
}
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
if (disposed) return Promise.resolve();
|
|
5227
|
-
return writer.flush().catch(() => void 0);
|
|
5989
|
+
if (getCurrentInstance() !== null) {
|
|
5990
|
+
return `${ANONYMOUS_FORM_KEY_PREFIX}${useId()}`;
|
|
5228
5991
|
}
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5992
|
+
return `${ANONYMOUS_FORM_KEY_PREFIX}${anonCounter++}`;
|
|
5993
|
+
}
|
|
5994
|
+
function warnOnSchemaFingerprintMismatch(key, existing, incoming) {
|
|
5995
|
+
let existingFp;
|
|
5996
|
+
let incomingFp;
|
|
5997
|
+
try {
|
|
5998
|
+
existingFp = existing.fingerprint();
|
|
5999
|
+
incomingFp = incoming.fingerprint();
|
|
6000
|
+
} catch (error) {
|
|
6001
|
+
console.error(
|
|
6002
|
+
`[attaform] fingerprint() threw for key "${key}"; skipping mismatch check.`,
|
|
6003
|
+
error
|
|
6004
|
+
);
|
|
6005
|
+
return;
|
|
5237
6006
|
}
|
|
5238
|
-
return
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
};
|
|
6007
|
+
if (existingFp === incomingFp) return;
|
|
6008
|
+
console.warn(
|
|
6009
|
+
`[attaform] useForm() calls with key "${key}" use different schemas; first wins, second is ignored. Use identical schemas or unique keys.
|
|
6010
|
+
existing: ${existingFp}
|
|
6011
|
+
incoming: ${incomingFp}`
|
|
6012
|
+
);
|
|
5245
6013
|
}
|
|
5246
|
-
function
|
|
5247
|
-
if (
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
6014
|
+
function warnOnPersistDivergence(key, existing, incomingPersist) {
|
|
6015
|
+
if (incomingPersist === void 0) return;
|
|
6016
|
+
const wired = existing.modules.get(PERSISTENCE_MODULE_KEY);
|
|
6017
|
+
const incomingNormalized = normalizePersistConfig(incomingPersist);
|
|
6018
|
+
if (wired === void 0) {
|
|
6019
|
+
console.warn(
|
|
6020
|
+
`[attaform] useForm({ key: "${key}" }) passed a persist config but the first useForm({ key }) call didn't wire persistence; the new config is silently dropped. Pass persist on the first call, or remove persist here to make the inheritance explicit.`
|
|
6021
|
+
);
|
|
6022
|
+
return;
|
|
6023
|
+
}
|
|
6024
|
+
if (persistConfigsEquivalent(wired.wiredConfig, incomingNormalized)) return;
|
|
6025
|
+
console.warn(
|
|
6026
|
+
`[attaform] useForm({ key: "${key}" }) passed a persist config that differs from the first useForm({ key }) call's; first wins, this one is ignored.
|
|
6027
|
+
wired: ${describePersist(wired.wiredConfig)}
|
|
6028
|
+
incoming: ${describePersist(incomingNormalized)}`
|
|
6029
|
+
);
|
|
6030
|
+
}
|
|
6031
|
+
function persistConfigsEquivalent(a, b) {
|
|
6032
|
+
if (a.storage !== b.storage) return false;
|
|
6033
|
+
if ((a.key ?? void 0) !== (b.key ?? void 0)) return false;
|
|
6034
|
+
if ((a.debounceMs ?? void 0) !== (b.debounceMs ?? void 0)) return false;
|
|
6035
|
+
return true;
|
|
6036
|
+
}
|
|
6037
|
+
function describePersist(config) {
|
|
6038
|
+
const storage = typeof config.storage === "string" ? config.storage : "custom-adapter";
|
|
6039
|
+
const parts = [`storage=${storage}`];
|
|
6040
|
+
if (config.key !== void 0) parts.push(`key=${config.key}`);
|
|
6041
|
+
if (config.debounceMs !== void 0) parts.push(`debounceMs=${config.debounceMs}`);
|
|
6042
|
+
return `{ ${parts.join(", ")} }`;
|
|
5251
6043
|
}
|
|
5252
6044
|
const warnedAnonPersistKeys = /* @__PURE__ */ new Set();
|
|
5253
6045
|
function enforceAnonPersistRule(formKey, ssr) {
|
|
@@ -5265,33 +6057,6 @@ function enforceAnonPersistRule(formKey, ssr) {
|
|
|
5265
6057
|
}
|
|
5266
6058
|
return true;
|
|
5267
6059
|
}
|
|
5268
|
-
function collectPersistedLeafPaths(form) {
|
|
5269
|
-
const out = [];
|
|
5270
|
-
walk(form, []);
|
|
5271
|
-
return out;
|
|
5272
|
-
function walk(node, prefix) {
|
|
5273
|
-
if (Array.isArray(node)) {
|
|
5274
|
-
for (let i = 0; i < node.length; i++) {
|
|
5275
|
-
walk(node[i], [...prefix, i]);
|
|
5276
|
-
}
|
|
5277
|
-
return;
|
|
5278
|
-
}
|
|
5279
|
-
if (isPlainRecord(node)) {
|
|
5280
|
-
for (const key of Object.keys(node)) {
|
|
5281
|
-
walk(node[key], [...prefix, key]);
|
|
5282
|
-
}
|
|
5283
|
-
return;
|
|
5284
|
-
}
|
|
5285
|
-
if (prefix.length === 0) return;
|
|
5286
|
-
out.push(canonicalizePath(prefix).key);
|
|
5287
|
-
}
|
|
5288
|
-
}
|
|
5289
|
-
function isDescendantPathKey(candidate, ancestor) {
|
|
5290
|
-
if (candidate.length <= ancestor.length) return false;
|
|
5291
|
-
if (!ancestor.endsWith("]")) return false;
|
|
5292
|
-
const childPrefix = `${ancestor.slice(0, -1)},`;
|
|
5293
|
-
return candidate.startsWith(childPrefix);
|
|
5294
|
-
}
|
|
5295
6060
|
|
|
5296
6061
|
let injectedInstanceCounter = 0;
|
|
5297
6062
|
function injectForm(input) {
|
|
@@ -5371,8 +6136,6 @@ function isLazyMarker(value) {
|
|
|
5371
6136
|
}
|
|
5372
6137
|
|
|
5373
6138
|
const NOOP_WIZARD_HISTORY = {
|
|
5374
|
-
push() {
|
|
5375
|
-
},
|
|
5376
6139
|
replace() {
|
|
5377
6140
|
},
|
|
5378
6141
|
read() {
|
|
@@ -5399,21 +6162,16 @@ function createWizardHistory(param) {
|
|
|
5399
6162
|
for (const subscriber of subscribers) subscriber(value);
|
|
5400
6163
|
}
|
|
5401
6164
|
window.addEventListener("popstate", handlePopstate);
|
|
5402
|
-
function
|
|
6165
|
+
function safeReplaceState(key) {
|
|
5403
6166
|
try {
|
|
5404
|
-
|
|
5405
|
-
fn.call(window.history, {}, "", buildUrl(key));
|
|
6167
|
+
window.history.replaceState({}, "", buildUrl(key));
|
|
5406
6168
|
} catch {
|
|
5407
6169
|
}
|
|
5408
6170
|
}
|
|
5409
6171
|
return {
|
|
5410
|
-
push(key) {
|
|
5411
|
-
if (disposed) return;
|
|
5412
|
-
safeWriteState(key, "push");
|
|
5413
|
-
},
|
|
5414
6172
|
replace(key) {
|
|
5415
6173
|
if (disposed) return;
|
|
5416
|
-
|
|
6174
|
+
safeReplaceState(key);
|
|
5417
6175
|
},
|
|
5418
6176
|
read() {
|
|
5419
6177
|
const url = new URL(window.location.href);
|
|
@@ -5472,68 +6230,16 @@ function buildWizardStatusesProxy(statuses) {
|
|
|
5472
6230
|
}
|
|
5473
6231
|
return result;
|
|
5474
6232
|
});
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
return computedEntry.value;
|
|
5486
|
-
},
|
|
5487
|
-
get(_, key) {
|
|
5488
|
-
if (typeof key === "symbol") {
|
|
5489
|
-
if (key === Symbol.toPrimitive) return proxyToPrimitive;
|
|
5490
|
-
return Reflect.get(target, key);
|
|
5491
|
-
}
|
|
5492
|
-
if (key === "toJSON") return () => snapshot.value;
|
|
5493
|
-
if (key === "toString") return proxyToString;
|
|
5494
|
-
if (key === "valueOf")
|
|
5495
|
-
return function() {
|
|
5496
|
-
return this;
|
|
5497
|
-
};
|
|
5498
|
-
const computedEntry = statuses[key];
|
|
5499
|
-
if (computedEntry === void 0) return void 0;
|
|
5500
|
-
return computedEntry.value;
|
|
5501
|
-
},
|
|
5502
|
-
has(_, key) {
|
|
5503
|
-
if (typeof key === "symbol") return Reflect.has(target, key);
|
|
5504
|
-
return Object.hasOwn(statuses, key);
|
|
5505
|
-
},
|
|
5506
|
-
ownKeys() {
|
|
5507
|
-
return Object.keys(statuses);
|
|
5508
|
-
},
|
|
5509
|
-
getOwnPropertyDescriptor(_, key) {
|
|
5510
|
-
if (typeof key === "symbol") return void 0;
|
|
5511
|
-
const computedEntry = statuses[key];
|
|
5512
|
-
if (computedEntry === void 0) return void 0;
|
|
5513
|
-
return {
|
|
5514
|
-
configurable: true,
|
|
5515
|
-
enumerable: true,
|
|
5516
|
-
writable: false,
|
|
5517
|
-
value: computedEntry.value
|
|
5518
|
-
};
|
|
5519
|
-
},
|
|
5520
|
-
set(_, key) {
|
|
5521
|
-
if (__DEV__) {
|
|
5522
|
-
console.warn(
|
|
5523
|
-
`[attaform] wizard.statuses is read-only \u2014 write to "${String(key)}" was ignored. Statuses derive from each form's meta; mutate the underlying form instead.`
|
|
5524
|
-
);
|
|
5525
|
-
}
|
|
5526
|
-
return true;
|
|
5527
|
-
},
|
|
5528
|
-
deleteProperty(_, key) {
|
|
5529
|
-
if (__DEV__) {
|
|
5530
|
-
console.warn(
|
|
5531
|
-
`[attaform] wizard.statuses is read-only \u2014 delete of "${String(key)}" was ignored.`
|
|
5532
|
-
);
|
|
5533
|
-
}
|
|
5534
|
-
return true;
|
|
5535
|
-
},
|
|
5536
|
-
defineProperty: () => true
|
|
6233
|
+
return buildCallableReadonlySnapshotProxy({
|
|
6234
|
+
surface: "wizard.statuses",
|
|
6235
|
+
snapshot: () => snapshot.value,
|
|
6236
|
+
resolveKey: (key) => statuses[key]?.value,
|
|
6237
|
+
// Single-key callable form. Strings stringify naturally; non-
|
|
6238
|
+
// string args coerce via `String(arg)` and miss the lookup, which
|
|
6239
|
+
// resolves to `undefined` (consistent with property-access).
|
|
6240
|
+
resolveCall: (arg) => statuses[String(arg)]?.value,
|
|
6241
|
+
ownKeys: () => Object.keys(statuses),
|
|
6242
|
+
hasKey: (key) => Object.hasOwn(statuses, key)
|
|
5537
6243
|
});
|
|
5538
6244
|
}
|
|
5539
6245
|
|
|
@@ -5550,6 +6256,12 @@ const NOOP_VALID_STATUS = {
|
|
|
5550
6256
|
submitted: false,
|
|
5551
6257
|
errorCount: 0
|
|
5552
6258
|
};
|
|
6259
|
+
function asStatusSource(form) {
|
|
6260
|
+
return form;
|
|
6261
|
+
}
|
|
6262
|
+
function asSubmissionSource(form) {
|
|
6263
|
+
return form;
|
|
6264
|
+
}
|
|
5553
6265
|
function useWizard(options) {
|
|
5554
6266
|
const rawSteps = Array.isArray(options.steps) ? options.steps : [];
|
|
5555
6267
|
if (rawSteps.length === 0 && __DEV__) {
|
|
@@ -5637,10 +6349,12 @@ function useWizard(options) {
|
|
|
5637
6349
|
return { configurable: true, enumerable: true, writable: false, value: form };
|
|
5638
6350
|
}
|
|
5639
6351
|
});
|
|
5640
|
-
const slotCtx =
|
|
6352
|
+
const slotCtx = {
|
|
5641
6353
|
forms: slotForms,
|
|
5642
|
-
currentKey
|
|
5643
|
-
|
|
6354
|
+
get currentKey() {
|
|
6355
|
+
return activeKey.value === "" ? void 0 : activeKey.value;
|
|
6356
|
+
}
|
|
6357
|
+
};
|
|
5644
6358
|
function resolveSlot(slot, index, ctx) {
|
|
5645
6359
|
if (typeof slot === "string") {
|
|
5646
6360
|
return getOrBuildNoop(slot);
|
|
@@ -5663,12 +6377,6 @@ function useWizard(options) {
|
|
|
5663
6377
|
}
|
|
5664
6378
|
return result;
|
|
5665
6379
|
}
|
|
5666
|
-
const lazyCtx = {
|
|
5667
|
-
forms: slotForms,
|
|
5668
|
-
get currentKey() {
|
|
5669
|
-
return activeKey.value === "" ? void 0 : activeKey.value;
|
|
5670
|
-
}
|
|
5671
|
-
};
|
|
5672
6380
|
for (let i = 0; i < rawSteps.length; i++) {
|
|
5673
6381
|
const slot = rawSteps[i];
|
|
5674
6382
|
if (isLazyMarker(slot)) {
|
|
@@ -5678,18 +6386,17 @@ function useWizard(options) {
|
|
|
5678
6386
|
idx,
|
|
5679
6387
|
computed(() => {
|
|
5680
6388
|
void lazyEpoch.value;
|
|
5681
|
-
return marker.resolve(
|
|
6389
|
+
return marker.resolve(slotCtx);
|
|
5682
6390
|
})
|
|
5683
6391
|
);
|
|
5684
6392
|
}
|
|
5685
6393
|
}
|
|
5686
6394
|
const compiledSteps = computed(() => {
|
|
5687
|
-
const ctx = slotCtx.value;
|
|
5688
6395
|
const out = [];
|
|
5689
6396
|
const seen = /* @__PURE__ */ new Set();
|
|
5690
6397
|
for (let i = 0; i < rawSteps.length; i++) {
|
|
5691
6398
|
const slot = rawSteps[i];
|
|
5692
|
-
const form = resolveSlot(slot, i,
|
|
6399
|
+
const form = resolveSlot(slot, i, slotCtx);
|
|
5693
6400
|
if (form === void 0) continue;
|
|
5694
6401
|
if (seen.has(form.key)) {
|
|
5695
6402
|
if (__DEV__) {
|
|
@@ -5715,9 +6422,12 @@ function useWizard(options) {
|
|
|
5715
6422
|
return -1;
|
|
5716
6423
|
});
|
|
5717
6424
|
const currentStep = computed(() => {
|
|
5718
|
-
const
|
|
5719
|
-
|
|
5720
|
-
|
|
6425
|
+
const list = compiledSteps.value;
|
|
6426
|
+
const idx = activeIndex.value;
|
|
6427
|
+
if (idx >= 0 && idx < list.length) {
|
|
6428
|
+
return list[idx].key;
|
|
6429
|
+
}
|
|
6430
|
+
const first = list[0];
|
|
5721
6431
|
return first === void 0 ? void 0 : first.key;
|
|
5722
6432
|
});
|
|
5723
6433
|
const activeForm = computed(() => {
|
|
@@ -5740,36 +6450,94 @@ function useWizard(options) {
|
|
|
5740
6450
|
for (const step of compiledSteps.value) out[step.key] = step.form;
|
|
5741
6451
|
return out;
|
|
5742
6452
|
});
|
|
5743
|
-
|
|
5744
|
-
const
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
6453
|
+
function isFormReady(key) {
|
|
6454
|
+
const store = registry.forms.get(key);
|
|
6455
|
+
return store?.defaultsResolved.value === true;
|
|
6456
|
+
}
|
|
6457
|
+
function toWizardAggregateError(err, fallbackKey) {
|
|
6458
|
+
const entry = {
|
|
6459
|
+
formKey: err.formKey ?? fallbackKey,
|
|
6460
|
+
path: err.path,
|
|
6461
|
+
message: err.message
|
|
6462
|
+
};
|
|
6463
|
+
if (err.code !== void 0) entry.code = err.code;
|
|
6464
|
+
return entry;
|
|
6465
|
+
}
|
|
6466
|
+
const valuesCache = /* @__PURE__ */ new Map();
|
|
6467
|
+
function valuesFor(form) {
|
|
6468
|
+
const cached = valuesCache.get(form.key);
|
|
6469
|
+
if (cached !== void 0) return cached;
|
|
6470
|
+
const source = asStatusSource(form);
|
|
6471
|
+
const computedValues = computed(() => source.values);
|
|
6472
|
+
valuesCache.set(form.key, computedValues);
|
|
6473
|
+
return computedValues;
|
|
6474
|
+
}
|
|
6475
|
+
const errorsCache = /* @__PURE__ */ new Map();
|
|
6476
|
+
function errorsFor(form) {
|
|
6477
|
+
const cached = errorsCache.get(form.key);
|
|
6478
|
+
if (cached !== void 0) return cached;
|
|
6479
|
+
const source = asStatusSource(form);
|
|
6480
|
+
const computedErrors = computed(() => {
|
|
6481
|
+
if (!isFormReady(form.key)) return [];
|
|
6482
|
+
const errors = source.meta?.errors ?? [];
|
|
6483
|
+
const list = [];
|
|
6484
|
+
for (const err of errors) list.push(toWizardAggregateError(err, form.key));
|
|
6485
|
+
return list;
|
|
6486
|
+
});
|
|
6487
|
+
errorsCache.set(form.key, computedErrors);
|
|
6488
|
+
return computedErrors;
|
|
6489
|
+
}
|
|
6490
|
+
const allValues = new Proxy({}, {
|
|
6491
|
+
get(_, key) {
|
|
6492
|
+
if (typeof key !== "string") return void 0;
|
|
6493
|
+
const form = formsRecord.value[key];
|
|
6494
|
+
if (form === void 0) return void 0;
|
|
6495
|
+
return valuesFor(form).value;
|
|
6496
|
+
},
|
|
6497
|
+
has(_, key) {
|
|
6498
|
+
if (typeof key !== "string") return false;
|
|
6499
|
+
return formsRecord.value[key] !== void 0;
|
|
6500
|
+
},
|
|
6501
|
+
ownKeys() {
|
|
6502
|
+
return Object.keys(formsRecord.value);
|
|
6503
|
+
},
|
|
6504
|
+
getOwnPropertyDescriptor(_, key) {
|
|
6505
|
+
if (typeof key !== "string") return void 0;
|
|
6506
|
+
const form = formsRecord.value[key];
|
|
6507
|
+
if (form === void 0) return void 0;
|
|
6508
|
+
return {
|
|
6509
|
+
configurable: true,
|
|
6510
|
+
enumerable: true,
|
|
6511
|
+
writable: false,
|
|
6512
|
+
value: valuesFor(form).value
|
|
6513
|
+
};
|
|
5748
6514
|
}
|
|
5749
|
-
return out;
|
|
5750
6515
|
});
|
|
5751
|
-
const allErrors =
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
const
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
6516
|
+
const allErrors = new Proxy({}, {
|
|
6517
|
+
get(_, key) {
|
|
6518
|
+
if (typeof key !== "string") return void 0;
|
|
6519
|
+
const form = formsRecord.value[key];
|
|
6520
|
+
if (form === void 0) return void 0;
|
|
6521
|
+
return errorsFor(form).value;
|
|
6522
|
+
},
|
|
6523
|
+
has(_, key) {
|
|
6524
|
+
if (typeof key !== "string") return false;
|
|
6525
|
+
return formsRecord.value[key] !== void 0;
|
|
6526
|
+
},
|
|
6527
|
+
ownKeys() {
|
|
6528
|
+
return Object.keys(formsRecord.value);
|
|
6529
|
+
},
|
|
6530
|
+
getOwnPropertyDescriptor(_, key) {
|
|
6531
|
+
if (typeof key !== "string") return void 0;
|
|
6532
|
+
const form = formsRecord.value[key];
|
|
6533
|
+
if (form === void 0) return void 0;
|
|
6534
|
+
return {
|
|
6535
|
+
configurable: true,
|
|
6536
|
+
enumerable: true,
|
|
6537
|
+
writable: false,
|
|
6538
|
+
value: errorsFor(form).value
|
|
6539
|
+
};
|
|
5771
6540
|
}
|
|
5772
|
-
return out;
|
|
5773
6541
|
});
|
|
5774
6542
|
const seedRef = ref(void 0);
|
|
5775
6543
|
const seedInput = options.defaultStatuses;
|
|
@@ -5792,11 +6560,9 @@ function useWizard(options) {
|
|
|
5792
6560
|
function statusFor(form) {
|
|
5793
6561
|
const cached = statusCache.get(form.key);
|
|
5794
6562
|
if (cached !== void 0) return cached;
|
|
5795
|
-
const source = form;
|
|
6563
|
+
const source = asStatusSource(form);
|
|
5796
6564
|
const computedStatus = computed(() => {
|
|
5797
|
-
|
|
5798
|
-
const resolved = store?.defaultsResolved.value === true;
|
|
5799
|
-
if (resolved) {
|
|
6565
|
+
if (isFormReady(form.key)) {
|
|
5800
6566
|
const meta = source.meta;
|
|
5801
6567
|
if (meta !== void 0 && meta !== null) {
|
|
5802
6568
|
return {
|
|
@@ -5939,7 +6705,7 @@ function useWizard(options) {
|
|
|
5939
6705
|
}
|
|
5940
6706
|
if (!registry.ssr) {
|
|
5941
6707
|
for (const step of compiledSteps.value) {
|
|
5942
|
-
const source = step.form;
|
|
6708
|
+
const source = asSubmissionSource(step.form);
|
|
5943
6709
|
if (typeof source.activate === "function") void source.activate();
|
|
5944
6710
|
}
|
|
5945
6711
|
}
|
|
@@ -5983,7 +6749,7 @@ function useWizard(options) {
|
|
|
5983
6749
|
const submissionAttempts = ref(0);
|
|
5984
6750
|
const done = ref(false);
|
|
5985
6751
|
function activateForm(form) {
|
|
5986
|
-
const source = form;
|
|
6752
|
+
const source = asSubmissionSource(form);
|
|
5987
6753
|
if (typeof source.activate === "function") {
|
|
5988
6754
|
void source.activate();
|
|
5989
6755
|
}
|
|
@@ -6083,7 +6849,7 @@ function useWizard(options) {
|
|
|
6083
6849
|
};
|
|
6084
6850
|
}
|
|
6085
6851
|
async function processOne(form) {
|
|
6086
|
-
const full = form;
|
|
6852
|
+
const full = asSubmissionSource(form);
|
|
6087
6853
|
let activationFailure;
|
|
6088
6854
|
try {
|
|
6089
6855
|
if (typeof full.activate === "function") await full.activate();
|
|
@@ -6115,15 +6881,7 @@ function useWizard(options) {
|
|
|
6115
6881
|
for (const step of compiledSteps.value) {
|
|
6116
6882
|
const processed = results.get(step.key);
|
|
6117
6883
|
if (processed === void 0 || processed.success === true) continue;
|
|
6118
|
-
for (const err of processed.errors)
|
|
6119
|
-
const entry = {
|
|
6120
|
-
formKey: err.formKey,
|
|
6121
|
-
path: err.path,
|
|
6122
|
-
message: err.message
|
|
6123
|
-
};
|
|
6124
|
-
if (err.code !== void 0) entry.code = err.code;
|
|
6125
|
-
out.push(entry);
|
|
6126
|
-
}
|
|
6884
|
+
for (const err of processed.errors) out.push(toWizardAggregateError(err, step.key));
|
|
6127
6885
|
}
|
|
6128
6886
|
return out;
|
|
6129
6887
|
}
|
|
@@ -6177,8 +6935,7 @@ function useWizard(options) {
|
|
|
6177
6935
|
if (processed !== void 0 && processed.success === true) {
|
|
6178
6936
|
valuesMap[step.key] = processed.data;
|
|
6179
6937
|
} else {
|
|
6180
|
-
|
|
6181
|
-
valuesMap[step.key] = source.values;
|
|
6938
|
+
valuesMap[step.key] = asStatusSource(step.form).values;
|
|
6182
6939
|
}
|
|
6183
6940
|
}
|
|
6184
6941
|
const ctx = buildSubmitContext(valuesMap, currentKey, final);
|
|
@@ -6199,8 +6956,11 @@ function useWizard(options) {
|
|
|
6199
6956
|
moveTo(firstFailedKey);
|
|
6200
6957
|
await nextTick();
|
|
6201
6958
|
const failedForm = formsRecord.value[firstFailedKey];
|
|
6202
|
-
if (failedForm !== void 0
|
|
6203
|
-
failedForm
|
|
6959
|
+
if (failedForm !== void 0) {
|
|
6960
|
+
const failedSource = asSubmissionSource(failedForm);
|
|
6961
|
+
if (typeof failedSource.applyInvalidSubmitPolicy === "function") {
|
|
6962
|
+
failedSource.applyInvalidSubmitPolicy();
|
|
6963
|
+
}
|
|
6204
6964
|
}
|
|
6205
6965
|
}
|
|
6206
6966
|
}
|
|
@@ -6215,7 +6975,7 @@ function useWizard(options) {
|
|
|
6215
6975
|
done.value = false;
|
|
6216
6976
|
lazyEpoch.value += 1;
|
|
6217
6977
|
for (const step of compiledSteps.value) {
|
|
6218
|
-
const full = step.form;
|
|
6978
|
+
const full = asSubmissionSource(step.form);
|
|
6219
6979
|
if (typeof full.reset === "function") full.reset();
|
|
6220
6980
|
}
|
|
6221
6981
|
const firstStep = compiledSteps.value[0];
|
|
@@ -6265,12 +7025,8 @@ function useWizard(options) {
|
|
|
6265
7025
|
return count.value;
|
|
6266
7026
|
},
|
|
6267
7027
|
statuses,
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
},
|
|
6271
|
-
get allErrors() {
|
|
6272
|
-
return allErrors.value;
|
|
6273
|
-
},
|
|
7028
|
+
allValues,
|
|
7029
|
+
allErrors,
|
|
6274
7030
|
get progress() {
|
|
6275
7031
|
return progress.value;
|
|
6276
7032
|
},
|
|
@@ -6401,5 +7157,5 @@ function warnIfAmbientWizardProviderHadDuplicates() {
|
|
|
6401
7157
|
}
|
|
6402
7158
|
}
|
|
6403
7159
|
|
|
6404
|
-
export { AttaformErrorCode as A,
|
|
6405
|
-
//# sourceMappingURL=attaform.
|
|
7160
|
+
export { AttaformErrorCode as A, defaultDisplayState as a, defineCoercion as b, injectWizard as c, defaultCoercionRules as d, isPlainRecord as e, isUnset as f, getAtPath as g, humanize as h, injectForm as i, slimKindOf as j, useAbstractForm as k, lazy as l, useWizard as m, normalizeNumericOption as n, setAtPath as s, unset as u };
|
|
7161
|
+
//# sourceMappingURL=attaform.CKFbKFb6.mjs.map
|