attaform 0.19.0 → 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/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 +3 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -40
- package/dist/index.d.mts +14 -40
- package/dist/index.d.ts +14 -40
- 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.CrpjyXdO.mjs → attaform.BKozEdTr.mjs} +275 -266
- 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.CoxJ8Qm8.cjs → attaform.BPxsYtTe.cjs} +2 -26
- package/dist/shared/attaform.BPxsYtTe.cjs.map +1 -0
- package/dist/shared/{attaform.BqEfHpVB.cjs → attaform.BPy-4qRx.cjs} +275 -268
- 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.BTpuvGec.d.ts → attaform.Bh3ACtts.d.ts} +109 -101
- package/dist/shared/{attaform.CXpzmj38.mjs → attaform.BupwXkj_.mjs} +213 -270
- package/dist/shared/attaform.BupwXkj_.mjs.map +1 -0
- package/dist/shared/{attaform.JBx8cfMA.cjs → attaform.CIn4bMsD.cjs} +263 -799
- package/dist/shared/attaform.CIn4bMsD.cjs.map +1 -0
- package/dist/shared/{attaform.BTi-PsHr.mjs → attaform.CKFbKFb6.mjs} +1818 -1472
- package/dist/shared/attaform.CKFbKFb6.mjs.map +1 -0
- package/dist/shared/{attaform.ePUcKxId.d.cts → attaform.D5-1XGQU.d.cts} +109 -101
- package/dist/shared/{attaform.a3uBo-gw.mjs → attaform.DEBvCjeH.mjs} +257 -793
- package/dist/shared/attaform.DEBvCjeH.mjs.map +1 -0
- package/dist/shared/{attaform.C1msmO2v.cjs → attaform.DL4CQ-oW.cjs} +1823 -1477
- package/dist/shared/attaform.DL4CQ-oW.cjs.map +1 -0
- package/dist/shared/{attaform.D4I63aBV.d.ts → attaform.DSD85fHb.d.cts} +1 -19
- package/dist/shared/{attaform.CBjmobqk.d.cts → attaform.DSD85fHb.d.mts} +1 -19
- package/dist/shared/{attaform.DXYHL99q.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.CJ-e9gYI.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.CRNA0vrd.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.BtBmfLQN.d.mts → attaform.EMzJcQci.d.mts} +109 -101
- package/dist/shared/attaform.EZG6fOFb.mjs +35 -0
- package/dist/shared/attaform.EZG6fOFb.mjs.map +1 -0
- package/dist/shared/{attaform.QvygsFGh.d.cts → attaform.GbDo_lJi.d.cts} +1 -1
- package/dist/shared/{attaform.C0uGZQ4M.d.ts → attaform.SfhU0OEY.d.cts} +134 -30
- package/dist/shared/{attaform.C0uGZQ4M.d.cts → attaform.SfhU0OEY.d.mts} +134 -30
- package/dist/shared/{attaform.C0uGZQ4M.d.mts → attaform.SfhU0OEY.d.ts} +134 -30
- 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 +1 -1
- package/dist/shared/attaform.BTi-PsHr.mjs.map +0 -1
- package/dist/shared/attaform.BqEfHpVB.cjs.map +0 -1
- package/dist/shared/attaform.Bubm_slq.cjs.map +0 -1
- package/dist/shared/attaform.C1msmO2v.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.CXpzmj38.mjs.map +0 -1
- package/dist/shared/attaform.Cghpuav8.mjs +0 -57
- package/dist/shared/attaform.Cghpuav8.mjs.map +0 -1
- package/dist/shared/attaform.CiMqJHDm.mjs +0 -1594
- package/dist/shared/attaform.CiMqJHDm.mjs.map +0 -1
- package/dist/shared/attaform.CoxJ8Qm8.cjs.map +0 -1
- package/dist/shared/attaform.CrpjyXdO.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.JBx8cfMA.cjs.map +0 -1
- package/dist/shared/attaform.OznWyOPy.cjs +0 -1600
- package/dist/shared/attaform.OznWyOPy.cjs.map +0 -1
- package/dist/shared/attaform.a3uBo-gw.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const vue = require('vue');
|
|
4
|
-
const paths = require('./attaform.
|
|
4
|
+
const paths = require('./attaform.BPy-4qRx.cjs');
|
|
5
5
|
|
|
6
6
|
const NOT_FOUND = Symbol("NOT_FOUND");
|
|
7
7
|
function descendStep(value, segment) {
|
|
@@ -64,7 +64,7 @@ function setAtPathOffset(root, path, value, offset) {
|
|
|
64
64
|
arr[head] = setAtPathOffset(arr[head], path, value, nextOffset);
|
|
65
65
|
return arr;
|
|
66
66
|
}
|
|
67
|
-
const rec = isPlainRecord(root) ?
|
|
67
|
+
const rec = isPlainRecord(root) ? Object.assign(/* @__PURE__ */ Object.create(null), root) : /* @__PURE__ */ Object.create(null);
|
|
68
68
|
rec[head] = setAtPathOffset(rec[head], path, value, nextOffset);
|
|
69
69
|
return rec;
|
|
70
70
|
}
|
|
@@ -165,7 +165,7 @@ function mergeStructuralImpl(schema, scratch, consumer, defaultValue) {
|
|
|
165
165
|
return consumer;
|
|
166
166
|
}
|
|
167
167
|
let mutated = false;
|
|
168
|
-
const out =
|
|
168
|
+
const out = Object.assign(/* @__PURE__ */ Object.create(null), consumer);
|
|
169
169
|
for (const key of Object.keys(defaultValue)) {
|
|
170
170
|
if (!(key in consumer)) {
|
|
171
171
|
const defAtKey = defaultValue[key];
|
|
@@ -429,7 +429,7 @@ function structuralSnapshot(value) {
|
|
|
429
429
|
return out2;
|
|
430
430
|
}
|
|
431
431
|
const src = value;
|
|
432
|
-
const out =
|
|
432
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
433
433
|
for (const k of Object.keys(src)) {
|
|
434
434
|
out[k] = structuralSnapshot(src[k]);
|
|
435
435
|
}
|
|
@@ -490,7 +490,7 @@ function hashStableString(input, seed = 0) {
|
|
|
490
490
|
const ANON_STEM = "atta";
|
|
491
491
|
function readableFormKeyStem(formKey) {
|
|
492
492
|
if (formKey === "" || formKey.startsWith(ANONYMOUS_FORM_KEY_PREFIX)) return ANON_STEM;
|
|
493
|
-
const sanitized = formKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(
|
|
493
|
+
const sanitized = formKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|(?<!-)-+$/g, "");
|
|
494
494
|
return sanitized === "" ? ANON_STEM : sanitized;
|
|
495
495
|
}
|
|
496
496
|
function fieldIdToken(formInstanceId, pathKey) {
|
|
@@ -524,6 +524,7 @@ function humanize(segment) {
|
|
|
524
524
|
}).join(" ");
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
+
const warnedDisplayStatePredicates = /* @__PURE__ */ new WeakSet();
|
|
527
528
|
function isUnderStubAncestor(state, segments) {
|
|
528
529
|
for (let i = 0; i < segments.length; i++) {
|
|
529
530
|
const ancestorPath = segments.slice(0, i);
|
|
@@ -607,7 +608,7 @@ function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBa
|
|
|
607
608
|
function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
608
609
|
const formValue = state.form.value;
|
|
609
610
|
const value = state.getValueAtPath(segments);
|
|
610
|
-
const original = state.originals.get(
|
|
611
|
+
const original = state.originals.get(key)?.value;
|
|
611
612
|
let pristine = true;
|
|
612
613
|
let blank = true;
|
|
613
614
|
let dirty = false;
|
|
@@ -620,13 +621,12 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
620
621
|
let validating = false;
|
|
621
622
|
let updatedAt = null;
|
|
622
623
|
let asyncPending = false;
|
|
623
|
-
for (const [, entry] of state.originals) {
|
|
624
|
+
for (const [leafKey, entry] of state.originals) {
|
|
624
625
|
if (!paths.isPathPrefix(segments, entry.segments)) continue;
|
|
625
626
|
if (segments.length === entry.segments.length) continue;
|
|
626
627
|
if (!hasAtPath(formValue, entry.segments)) continue;
|
|
627
|
-
const leafKey = paths.canonicalizePath(entry.segments).key;
|
|
628
628
|
const leafRecord = state.fields.get(leafKey);
|
|
629
|
-
if (!state.
|
|
629
|
+
if (!state.isPristineAtPathByKey(leafKey, entry.segments)) {
|
|
630
630
|
pristine = false;
|
|
631
631
|
dirty = true;
|
|
632
632
|
}
|
|
@@ -638,7 +638,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
638
638
|
if (leafRecord?.blurredAfterInteraction === true) blurredAfterInteraction = true;
|
|
639
639
|
if (leafRecord?.connected === true) connected = true;
|
|
640
640
|
if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0) validating = true;
|
|
641
|
-
if (state.
|
|
641
|
+
if (state.pathHasAsyncValidationByKey(leafKey, entry.segments)) asyncPending = true;
|
|
642
642
|
const ts = leafRecord?.updatedAt;
|
|
643
643
|
if (ts !== void 0 && ts !== null) {
|
|
644
644
|
if (updatedAt === null || ts > updatedAt) updatedAt = ts;
|
|
@@ -693,7 +693,14 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState)
|
|
|
693
693
|
let displayState;
|
|
694
694
|
try {
|
|
695
695
|
displayState = predicate(base, formMeta);
|
|
696
|
-
} catch {
|
|
696
|
+
} catch (err) {
|
|
697
|
+
if (paths.__DEV__ && !warnedDisplayStatePredicates.has(predicate)) {
|
|
698
|
+
warnedDisplayStatePredicates.add(predicate);
|
|
699
|
+
console.warn(
|
|
700
|
+
"[attaform] custom getDisplayState threw \u2014 falling back to defaultDisplayState. Subsequent throws from the same predicate will not warn again.",
|
|
701
|
+
err
|
|
702
|
+
);
|
|
703
|
+
}
|
|
697
704
|
displayState = defaultDisplayState(base, formMeta);
|
|
698
705
|
}
|
|
699
706
|
return {
|
|
@@ -730,6 +737,41 @@ function aggregateErrorsAt(state, prefix) {
|
|
|
730
737
|
}
|
|
731
738
|
const EMPTY_ELEMENTS = Object.freeze([]);
|
|
732
739
|
|
|
740
|
+
function liveKeysAtPath(state, segments) {
|
|
741
|
+
const value = getAtPath(state.form.value, segments);
|
|
742
|
+
if (value === null || value === void 0) return [];
|
|
743
|
+
if (Array.isArray(value)) {
|
|
744
|
+
const keys = new Array(value.length);
|
|
745
|
+
for (let i = 0; i < value.length; i += 1) keys[i] = String(i);
|
|
746
|
+
return keys;
|
|
747
|
+
}
|
|
748
|
+
if (typeof value === "object") return Object.keys(value);
|
|
749
|
+
return [];
|
|
750
|
+
}
|
|
751
|
+
function isArrayPath(state, segments) {
|
|
752
|
+
if (segments.length === 0) return false;
|
|
753
|
+
return Array.isArray(getAtPath(state.form.value, segments));
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function warnReadOnly(surface, action, key) {
|
|
757
|
+
if (!paths.__DEV__) return;
|
|
758
|
+
const phrase = action === "write" ? `write to "${String(key)}"` : `${action} of "${String(key)}"`;
|
|
759
|
+
console.warn(
|
|
760
|
+
`[attaform] ${surface} is read-only \u2014 ${phrase} was ignored. Mutate the form via setValue / the directive / field-array helpers instead.`
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
function makeReadonlyCoercion(snapshot) {
|
|
764
|
+
const toString = () => JSON.stringify(snapshot());
|
|
765
|
+
return {
|
|
766
|
+
toString,
|
|
767
|
+
valueOf() {
|
|
768
|
+
return this;
|
|
769
|
+
},
|
|
770
|
+
toJSON: snapshot,
|
|
771
|
+
toPrimitive: (hint) => hint === "number" ? NaN : toString()
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
|
|
733
775
|
const INTEGER_SEGMENT = /^(?:0|[1-9]\d*)$/;
|
|
734
776
|
function keyToSegment(key) {
|
|
735
777
|
return INTEGER_SEGMENT.test(key) ? Number(key) : key;
|
|
@@ -761,12 +803,12 @@ function buildSurfaceProxy(opts) {
|
|
|
761
803
|
const existing = containerCache.get(cacheKey);
|
|
762
804
|
if (existing !== void 0) return existing;
|
|
763
805
|
const snapshotContainer = () => opts.materializeContainer === void 0 ? {} : opts.materializeContainer(segments);
|
|
764
|
-
const
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
806
|
+
const {
|
|
807
|
+
toString: containerToString,
|
|
808
|
+
valueOf: containerValueOf,
|
|
809
|
+
toJSON: containerToJSON,
|
|
810
|
+
toPrimitive: containerToPrimitive
|
|
811
|
+
} = makeReadonlyCoercion(snapshotContainer);
|
|
770
812
|
const target = isArrayLike ? [] : (() => {
|
|
771
813
|
});
|
|
772
814
|
const proxy = new Proxy(target, {
|
|
@@ -791,6 +833,9 @@ function buildSurfaceProxy(opts) {
|
|
|
791
833
|
return opts.containerOwnKeys === void 0 ? 0 : opts.containerOwnKeys(segments).length;
|
|
792
834
|
}
|
|
793
835
|
const childSegs = [...segments, keyToSegment(key)];
|
|
836
|
+
if ((isArrayLike || opts.isArrayContainer?.(segments) === true) && typeof keyToSegment(key) === "string" && key in Array.prototype) {
|
|
837
|
+
return Reflect.get(Array.prototype, key);
|
|
838
|
+
}
|
|
794
839
|
if (key === "toString" || key === "valueOf") {
|
|
795
840
|
if (!schemaHasPath(childSegs)) {
|
|
796
841
|
return key === "toString" ? containerToString : containerValueOf;
|
|
@@ -835,10 +880,25 @@ function buildSurfaceProxy(opts) {
|
|
|
835
880
|
};
|
|
836
881
|
},
|
|
837
882
|
// Block writes at the proxy boundary. Mutations go through
|
|
838
|
-
// `setValue`, the directive, or the field-array helpers.
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
883
|
+
// `setValue`, the directive, or the field-array helpers. Each
|
|
884
|
+
// trap returns `true` (warn-and-noop) — returning `false` from a
|
|
885
|
+
// `set`/`delete`/`defineProperty` trap throws `TypeError` under
|
|
886
|
+
// strict mode (every ESM / `<script setup>`), which would surface
|
|
887
|
+
// a host-level exception in consumer code that the library
|
|
888
|
+
// documents as "writes are ignored." Aligns with the contract on
|
|
889
|
+
// `form.values` / `wizard.statuses`.
|
|
890
|
+
set: (_, key) => {
|
|
891
|
+
warnReadOnly("form.fields / form.errors", "write", key);
|
|
892
|
+
return true;
|
|
893
|
+
},
|
|
894
|
+
deleteProperty: (_, key) => {
|
|
895
|
+
warnReadOnly("form.fields / form.errors", "delete", key);
|
|
896
|
+
return true;
|
|
897
|
+
},
|
|
898
|
+
defineProperty: (_, key) => {
|
|
899
|
+
warnReadOnly("form.fields / form.errors", "define", key);
|
|
900
|
+
return true;
|
|
901
|
+
}
|
|
842
902
|
});
|
|
843
903
|
containerCache.set(cacheKey, proxy);
|
|
844
904
|
return proxy;
|
|
@@ -860,11 +920,12 @@ function buildSurfaceProxy(opts) {
|
|
|
860
920
|
}
|
|
861
921
|
return snapshot;
|
|
862
922
|
};
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
923
|
+
const {
|
|
924
|
+
toString: leafToString,
|
|
925
|
+
valueOf: leafValueOf,
|
|
926
|
+
toJSON: leafToJSONHandler,
|
|
927
|
+
toPrimitive: leafToPrimitive
|
|
928
|
+
} = makeReadonlyCoercion(snapshotLeaf);
|
|
868
929
|
const target = (() => {
|
|
869
930
|
});
|
|
870
931
|
const proxy = new Proxy(target, {
|
|
@@ -882,7 +943,7 @@ function buildSurfaceProxy(opts) {
|
|
|
882
943
|
if (typeof key !== "string") return void 0;
|
|
883
944
|
if (key === "toString") return leafToString;
|
|
884
945
|
if (key === "valueOf") return leafValueOf;
|
|
885
|
-
if (key === "toJSON") return
|
|
946
|
+
if (key === "toJSON") return leafToJSONHandler;
|
|
886
947
|
if (leafKeys.has(key)) {
|
|
887
948
|
const leaf = opts.resolveLeaf(segments);
|
|
888
949
|
return readLeafKey(leaf, key);
|
|
@@ -909,9 +970,22 @@ function buildSurfaceProxy(opts) {
|
|
|
909
970
|
writable: false
|
|
910
971
|
};
|
|
911
972
|
},
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
973
|
+
// Same warn-and-noop contract as the container traps above.
|
|
974
|
+
// Returning `true` keeps strict-mode callers from throwing on
|
|
975
|
+
// `form.fields.email.value = …`; the actual readonly guarantee
|
|
976
|
+
// is the absence of any mutation, not the host-level reject.
|
|
977
|
+
set: (_, key) => {
|
|
978
|
+
warnReadOnly("form.fields.<leaf>", "write", key);
|
|
979
|
+
return true;
|
|
980
|
+
},
|
|
981
|
+
deleteProperty: (_, key) => {
|
|
982
|
+
warnReadOnly("form.fields.<leaf>", "delete", key);
|
|
983
|
+
return true;
|
|
984
|
+
},
|
|
985
|
+
defineProperty: (_, key) => {
|
|
986
|
+
warnReadOnly("form.fields.<leaf>", "define", key);
|
|
987
|
+
return true;
|
|
988
|
+
}
|
|
915
989
|
});
|
|
916
990
|
leafViewCache.set(cacheKey, proxy);
|
|
917
991
|
return proxy;
|
|
@@ -975,33 +1049,53 @@ function buildErrorsProxy(state) {
|
|
|
975
1049
|
// working — the call-form just extends that semantic to
|
|
976
1050
|
// containers and dynamic paths.
|
|
977
1051
|
resolveCallTarget: (path) => aggregateErrorsAt(state, path),
|
|
978
|
-
//
|
|
979
|
-
//
|
|
980
|
-
//
|
|
981
|
-
//
|
|
982
|
-
//
|
|
983
|
-
|
|
984
|
-
|
|
1052
|
+
// Enumeration unions the live form-data keys at this path with the
|
|
1053
|
+
// first-child segments drawn from every error store. Without the
|
|
1054
|
+
// union, `Object.keys(form.errors)` / `{...form.errors}` /
|
|
1055
|
+
// `v-for="(errs, k) in form.errors"` silently dropped two
|
|
1056
|
+
// important error classes that the dot / call / JSON.stringify
|
|
1057
|
+
// surfaces already exposed:
|
|
1058
|
+
//
|
|
1059
|
+
// - **Form-level** errors at the synthetic `['']` path (set via
|
|
1060
|
+
// `setFormErrors` or root cross-field refines).
|
|
1061
|
+
// - **Server-only** errors at a key the schema doesn't know
|
|
1062
|
+
// about (`['ghost']`, `['address', 'ghost']`).
|
|
1063
|
+
//
|
|
1064
|
+
// The union closes that gap so `ownKeys` agrees with the rest of
|
|
1065
|
+
// the surface. Active-path filter mirrors `resolveLeaf`:
|
|
1066
|
+
// library-produced verdicts (schema + derived-blank) at unreachable
|
|
1067
|
+
// paths stay hidden; user-supplied errors are unconditional.
|
|
1068
|
+
containerOwnKeys: (segments) => errorAwareContainerKeys(state, segments),
|
|
1069
|
+
isArrayContainer: (segments) => isArrayPath(state, segments)
|
|
985
1070
|
});
|
|
986
1071
|
}
|
|
987
|
-
function
|
|
988
|
-
const
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
const
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1072
|
+
function errorAwareContainerKeys(state, segments) {
|
|
1073
|
+
const keys = new Set(liveKeysAtPath(state, segments));
|
|
1074
|
+
const formValue = state.form.value;
|
|
1075
|
+
const walk = (store, applyActivePathFilter) => {
|
|
1076
|
+
for (const [pathKey, errors] of store) {
|
|
1077
|
+
if (errors.length === 0) continue;
|
|
1078
|
+
if (pathKey === paths.FORM_ERRORS_PATH_KEY) {
|
|
1079
|
+
if (segments.length === 0) keys.add("");
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
const decoded = paths.segmentsForPathKey(pathKey);
|
|
1083
|
+
if (decoded === null) continue;
|
|
1084
|
+
if (decoded.length <= segments.length) continue;
|
|
1085
|
+
if (!paths.isPathPrefix(segments, decoded)) continue;
|
|
1086
|
+
if (applyActivePathFilter && !hasAtPath(formValue, decoded)) continue;
|
|
1087
|
+
const nextSeg = decoded[segments.length];
|
|
1088
|
+
keys.add(typeof nextSeg === "number" ? String(nextSeg) : nextSeg);
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
walk(state.schemaErrors, true);
|
|
1092
|
+
walk(state.derivedBlankErrors.value, true);
|
|
1093
|
+
walk(state.userErrors, false);
|
|
1094
|
+
return [...keys];
|
|
1001
1095
|
}
|
|
1002
1096
|
function materializeErrors(state, containerSegments) {
|
|
1003
1097
|
const liveContainer = getAtPath(state.form.value, containerSegments);
|
|
1004
|
-
const tree = Array.isArray(liveContainer) ? [] :
|
|
1098
|
+
const tree = Array.isArray(liveContainer) ? [] : /* @__PURE__ */ Object.create(null);
|
|
1005
1099
|
const collect = (store, applyActivePathFilter) => {
|
|
1006
1100
|
entries: for (const [pathKey, errors] of store) {
|
|
1007
1101
|
if (errors.length === 0) continue;
|
|
@@ -1051,7 +1145,7 @@ function placeAt(tree, path, errors) {
|
|
|
1051
1145
|
const cursorRecord2 = cursor;
|
|
1052
1146
|
let child = cursorRecord2[key];
|
|
1053
1147
|
if (child === null || child === void 0 || typeof child !== "object") {
|
|
1054
|
-
child = typeof nextSeg === "number" ? [] :
|
|
1148
|
+
child = typeof nextSeg === "number" ? [] : /* @__PURE__ */ Object.create(null);
|
|
1055
1149
|
cursorRecord2[key] = child;
|
|
1056
1150
|
}
|
|
1057
1151
|
cursor = child;
|
|
@@ -1090,9 +1184,10 @@ function buildFieldArrayApi(state) {
|
|
|
1090
1184
|
},
|
|
1091
1185
|
insert(path, index, value) {
|
|
1092
1186
|
const next = readArray(path);
|
|
1093
|
-
next.
|
|
1094
|
-
const
|
|
1095
|
-
|
|
1187
|
+
const preLen = next.length;
|
|
1188
|
+
const insertIndex = index < 0 ? Math.max(0, preLen + index) : Math.min(index, preLen);
|
|
1189
|
+
next.splice(insertIndex, 0, value);
|
|
1190
|
+
return writeArray(path, next, { kind: "insert", index: insertIndex });
|
|
1096
1191
|
},
|
|
1097
1192
|
remove(path, index) {
|
|
1098
1193
|
const next = readArray(path);
|
|
@@ -1214,9 +1309,34 @@ function buildFieldStateProxy(state, formInstanceId, getFormMetaBase, options) {
|
|
|
1214
1309
|
writable: false
|
|
1215
1310
|
};
|
|
1216
1311
|
},
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1312
|
+
// Warn-and-noop: returning `false` here throws `TypeError` under
|
|
1313
|
+
// strict mode (`form.fields('email').value = 1` from any ESM
|
|
1314
|
+
// module), which would violate the documented "writes are
|
|
1315
|
+
// ignored" contract. Mirrors `values-proxy` / `wizard-statuses-proxy`.
|
|
1316
|
+
set: (_, key) => {
|
|
1317
|
+
if (paths.__DEV__) {
|
|
1318
|
+
console.warn(
|
|
1319
|
+
`[attaform] form.fields(path) is read-only \u2014 write to "${String(key)}" was ignored. Use form.setValue / the directive / field-array helpers instead.`
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
return true;
|
|
1323
|
+
},
|
|
1324
|
+
deleteProperty: (_, key) => {
|
|
1325
|
+
if (paths.__DEV__) {
|
|
1326
|
+
console.warn(
|
|
1327
|
+
`[attaform] form.fields(path) is read-only \u2014 delete of "${String(key)}" was ignored.`
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
return true;
|
|
1331
|
+
},
|
|
1332
|
+
defineProperty: (_, key) => {
|
|
1333
|
+
if (paths.__DEV__) {
|
|
1334
|
+
console.warn(
|
|
1335
|
+
`[attaform] form.fields(path) is read-only \u2014 define of "${String(key)}" was ignored.`
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
return true;
|
|
1339
|
+
}
|
|
1220
1340
|
});
|
|
1221
1341
|
terminalCache.set(cacheKey, proxy);
|
|
1222
1342
|
return proxy;
|
|
@@ -1232,21 +1352,6 @@ function buildFieldStateProxy(state, formInstanceId, getFormMetaBase, options) {
|
|
|
1232
1352
|
isArrayContainer: (segments) => isArrayPath(state, segments)
|
|
1233
1353
|
});
|
|
1234
1354
|
}
|
|
1235
|
-
function liveKeysAtPath(state, segments) {
|
|
1236
|
-
const value = getAtPath(state.form.value, segments);
|
|
1237
|
-
if (value === null || value === void 0) return [];
|
|
1238
|
-
if (Array.isArray(value)) {
|
|
1239
|
-
const keys = new Array(value.length);
|
|
1240
|
-
for (let i = 0; i < value.length; i += 1) keys[i] = String(i);
|
|
1241
|
-
return keys;
|
|
1242
|
-
}
|
|
1243
|
-
if (typeof value === "object") return Object.keys(value);
|
|
1244
|
-
return [];
|
|
1245
|
-
}
|
|
1246
|
-
function isArrayPath(state, segments) {
|
|
1247
|
-
if (segments.length === 0) return false;
|
|
1248
|
-
return Array.isArray(getAtPath(state.form.value, segments));
|
|
1249
|
-
}
|
|
1250
1355
|
function materializeFields(state, containerSegments, snapshotFieldStateAt) {
|
|
1251
1356
|
const liveValue = getAtPath(state.form.value, containerSegments);
|
|
1252
1357
|
return walk$2(liveValue, containerSegments, state.schema, snapshotFieldStateAt);
|
|
@@ -1290,7 +1395,7 @@ async function getStorageAdapter(storage) {
|
|
|
1290
1395
|
}
|
|
1291
1396
|
}
|
|
1292
1397
|
}
|
|
1293
|
-
const PERSISTED_ENVELOPE_VERSION =
|
|
1398
|
+
const PERSISTED_ENVELOPE_VERSION = 6;
|
|
1294
1399
|
function readPersistedPayload(value) {
|
|
1295
1400
|
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
1296
1401
|
const envelope = value;
|
|
@@ -1314,12 +1419,7 @@ function warnVersionMismatch(observedVersion) {
|
|
|
1314
1419
|
function buildPersistedPayload(form, include, schemaErrors, userErrors, blankPaths) {
|
|
1315
1420
|
let transientList;
|
|
1316
1421
|
if (blankPaths !== void 0 && blankPaths.size > 0) {
|
|
1317
|
-
|
|
1318
|
-
for (const key of blankPaths) {
|
|
1319
|
-
const d = paths.pathKeyToDotted(key);
|
|
1320
|
-
if (d !== null) dotted.push(d);
|
|
1321
|
-
}
|
|
1322
|
-
transientList = dotted.length > 0 ? dotted : void 0;
|
|
1422
|
+
transientList = [...blankPaths];
|
|
1323
1423
|
}
|
|
1324
1424
|
if (include === "form") {
|
|
1325
1425
|
if (transientList === void 0) return { v: PERSISTED_ENVELOPE_VERSION, data: { form } };
|
|
@@ -1341,30 +1441,35 @@ function buildPersistedPayload(form, include, schemaErrors, userErrors, blankPat
|
|
|
1341
1441
|
function createDebouncedWriter(write, debounceMs) {
|
|
1342
1442
|
let timer = null;
|
|
1343
1443
|
let pending = null;
|
|
1444
|
+
let writeGeneration = 0;
|
|
1445
|
+
function runWrite() {
|
|
1446
|
+
const gen = ++writeGeneration;
|
|
1447
|
+
pending = write().finally(() => {
|
|
1448
|
+
if (writeGeneration === gen) pending = null;
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1344
1451
|
function schedule() {
|
|
1345
1452
|
if (timer !== null) clearTimeout(timer);
|
|
1346
1453
|
if (debounceMs === 0) {
|
|
1347
|
-
|
|
1348
|
-
pending = null;
|
|
1349
|
-
});
|
|
1454
|
+
runWrite();
|
|
1350
1455
|
return;
|
|
1351
1456
|
}
|
|
1352
1457
|
timer = setTimeout(() => {
|
|
1353
1458
|
timer = null;
|
|
1354
|
-
|
|
1355
|
-
pending = null;
|
|
1356
|
-
});
|
|
1459
|
+
runWrite();
|
|
1357
1460
|
}, debounceMs);
|
|
1358
1461
|
}
|
|
1359
1462
|
async function flush() {
|
|
1360
1463
|
if (timer !== null) {
|
|
1361
1464
|
clearTimeout(timer);
|
|
1362
1465
|
timer = null;
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1466
|
+
runWrite();
|
|
1467
|
+
}
|
|
1468
|
+
while (pending !== null) {
|
|
1469
|
+
const awaited = pending;
|
|
1470
|
+
await awaited;
|
|
1471
|
+
if (pending === awaited) break;
|
|
1366
1472
|
}
|
|
1367
|
-
if (pending !== null) await pending;
|
|
1368
1473
|
}
|
|
1369
1474
|
function cancel() {
|
|
1370
1475
|
if (timer !== null) {
|
|
@@ -1377,7 +1482,7 @@ function createDebouncedWriter(write, debounceMs) {
|
|
|
1377
1482
|
function resolveStorageKeyBase(config, formKey) {
|
|
1378
1483
|
return config.key ?? `${PERSISTENCE_KEY_PREFIX}${formKey}`;
|
|
1379
1484
|
}
|
|
1380
|
-
async function
|
|
1485
|
+
async function removeMatchingKeys(adapter, base, keepKey) {
|
|
1381
1486
|
let keys;
|
|
1382
1487
|
try {
|
|
1383
1488
|
keys = await adapter.listKeys(base);
|
|
@@ -1385,12 +1490,15 @@ async function cleanupOrphanKeys(adapter, base, currentKey) {
|
|
|
1385
1490
|
return;
|
|
1386
1491
|
}
|
|
1387
1492
|
for (const key of keys) {
|
|
1388
|
-
if (key ===
|
|
1493
|
+
if (key === keepKey) continue;
|
|
1389
1494
|
if (key === base || key.startsWith(`${base}:`)) {
|
|
1390
1495
|
void adapter.removeItem(key).catch(() => void 0);
|
|
1391
1496
|
}
|
|
1392
1497
|
}
|
|
1393
1498
|
}
|
|
1499
|
+
async function cleanupOrphanKeys(adapter, base, currentKey) {
|
|
1500
|
+
await removeMatchingKeys(adapter, base, currentKey);
|
|
1501
|
+
}
|
|
1394
1502
|
const STANDARD_STORAGE_KINDS = ["local", "session", "indexeddb"];
|
|
1395
1503
|
function normalizePersistConfig(input) {
|
|
1396
1504
|
if (typeof input === "string") return { storage: input };
|
|
@@ -1401,12 +1509,7 @@ async function sweepAllOrphansAcrossStandardStores(base) {
|
|
|
1401
1509
|
for (const kind of STANDARD_STORAGE_KINDS) {
|
|
1402
1510
|
try {
|
|
1403
1511
|
const adapter = await getStorageAdapter(kind);
|
|
1404
|
-
|
|
1405
|
-
for (const key of keys) {
|
|
1406
|
-
if (key === base || key.startsWith(`${base}:`)) {
|
|
1407
|
-
void adapter.removeItem(key).catch(() => void 0);
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1512
|
+
await removeMatchingKeys(adapter, base);
|
|
1410
1513
|
} catch {
|
|
1411
1514
|
}
|
|
1412
1515
|
}
|
|
@@ -1417,12 +1520,7 @@ async function sweepNonConfiguredStandardStoresForOrphans(configured, base) {
|
|
|
1417
1520
|
if (kind === configuredKind) continue;
|
|
1418
1521
|
try {
|
|
1419
1522
|
const adapter = await getStorageAdapter(kind);
|
|
1420
|
-
|
|
1421
|
-
for (const key of keys) {
|
|
1422
|
-
if (key === base || key.startsWith(`${base}:`)) {
|
|
1423
|
-
void adapter.removeItem(key).catch(() => void 0);
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1523
|
+
await removeMatchingKeys(adapter, base);
|
|
1426
1524
|
} catch {
|
|
1427
1525
|
}
|
|
1428
1526
|
}
|
|
@@ -1438,6 +1536,29 @@ function pluckPaths(form, pathKeys) {
|
|
|
1438
1536
|
}
|
|
1439
1537
|
return sparse ?? {};
|
|
1440
1538
|
}
|
|
1539
|
+
function stripUnacknowledgedSensitiveLeaves(form, optedInPaths, isSensitivePath) {
|
|
1540
|
+
const acknowledgedSensitive = [];
|
|
1541
|
+
for (const key of optedInPaths) {
|
|
1542
|
+
const segs = paths.segmentsForPathKey(key);
|
|
1543
|
+
if (segs !== null && isSensitivePath(segs)) acknowledgedSensitive.push(segs);
|
|
1544
|
+
}
|
|
1545
|
+
const coveredByAcknowledged = (path) => acknowledgedSensitive.some((prefix) => paths.isPathPrefix(prefix, path));
|
|
1546
|
+
const walk = (path, value) => {
|
|
1547
|
+
if (path.length > 0 && isSensitivePath(path) && !coveredByAcknowledged(path)) {
|
|
1548
|
+
return void 0;
|
|
1549
|
+
}
|
|
1550
|
+
if (value === null || typeof value !== "object") return value;
|
|
1551
|
+
if (Array.isArray(value)) return value.map((item, i) => walk([...path, i], item));
|
|
1552
|
+
if (!isPlainRecord(value)) return value;
|
|
1553
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
1554
|
+
for (const key of Object.keys(value)) {
|
|
1555
|
+
const walked = walk([...path, key], value[key]);
|
|
1556
|
+
if (walked !== void 0) out[key] = walked;
|
|
1557
|
+
}
|
|
1558
|
+
return out;
|
|
1559
|
+
};
|
|
1560
|
+
return walk([], form);
|
|
1561
|
+
}
|
|
1441
1562
|
function filterErrorsByPaths(errors, pathKeys) {
|
|
1442
1563
|
const out = /* @__PURE__ */ new Map();
|
|
1443
1564
|
for (const [key, value] of errors) {
|
|
@@ -1464,7 +1585,7 @@ function mergeDeep(target, source, path, schema) {
|
|
|
1464
1585
|
if (sourceDisc !== void 0) {
|
|
1465
1586
|
const variantDefault = du.getVariantDefault(sourceDisc);
|
|
1466
1587
|
if (isPlainRecord(variantDefault)) {
|
|
1467
|
-
const out2 =
|
|
1588
|
+
const out2 = Object.assign(/* @__PURE__ */ Object.create(null), variantDefault);
|
|
1468
1589
|
for (const key of Object.keys(sourceRecord)) {
|
|
1469
1590
|
if (!(key in variantDefault) && key !== du.discriminatorKey) continue;
|
|
1470
1591
|
out2[key] = mergeDeep(out2[key], sourceRecord[key], [...path, key], schema);
|
|
@@ -1476,7 +1597,7 @@ function mergeDeep(target, source, path, schema) {
|
|
|
1476
1597
|
}
|
|
1477
1598
|
}
|
|
1478
1599
|
const mergeTarget = target;
|
|
1479
|
-
const out = isPlainRecord(mergeTarget) ?
|
|
1600
|
+
const out = isPlainRecord(mergeTarget) ? Object.assign(/* @__PURE__ */ Object.create(null), mergeTarget) : /* @__PURE__ */ Object.create(null);
|
|
1480
1601
|
for (const key of Object.keys(source)) {
|
|
1481
1602
|
out[key] = mergeDeep(out[key], source[key], [...path, key], schema);
|
|
1482
1603
|
}
|
|
@@ -1579,39 +1700,44 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1579
1700
|
}
|
|
1580
1701
|
return result;
|
|
1581
1702
|
}
|
|
1582
|
-
async function
|
|
1703
|
+
async function runImperativeValidation(pathInput, config) {
|
|
1583
1704
|
const segments = pathInput === void 0 ? void 0 : toSegments(pathInput);
|
|
1584
1705
|
const dataAtPath = segments === void 0 ? state.form.value : state.getValueAtPath(segments);
|
|
1585
1706
|
try {
|
|
1586
1707
|
state.activeValidations.value += 1;
|
|
1587
|
-
state.cancelFieldValidation();
|
|
1708
|
+
if (config.cancelInFlight) state.cancelFieldValidation();
|
|
1588
1709
|
const refinement = await runRefinementValidation(dataAtPath, segments);
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1710
|
+
if (config.commitToSchemaErrors) {
|
|
1711
|
+
const scopePath = segments ?? [];
|
|
1712
|
+
const errors = refinement.success ? [] : refinement.errors;
|
|
1713
|
+
const reStamped = segments === void 0 ? errors : errors.map((err) => ({
|
|
1714
|
+
...err,
|
|
1715
|
+
path: [...segments, ...err.path]
|
|
1716
|
+
}));
|
|
1717
|
+
state.applySchemaErrorsForSubtree(scopePath, reStamped);
|
|
1718
|
+
}
|
|
1719
|
+
return { ok: true, refinement, segments };
|
|
1597
1720
|
} catch (err) {
|
|
1598
|
-
return adapterThrowResponse(err);
|
|
1721
|
+
return { ok: false, error: adapterThrowResponse(err) };
|
|
1599
1722
|
} finally {
|
|
1600
1723
|
state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
|
|
1601
1724
|
}
|
|
1602
1725
|
}
|
|
1726
|
+
async function validateAsync(pathInput) {
|
|
1727
|
+
const result = await runImperativeValidation(pathInput, {
|
|
1728
|
+
cancelInFlight: true,
|
|
1729
|
+
commitToSchemaErrors: true
|
|
1730
|
+
});
|
|
1731
|
+
if (!result.ok) return result.error;
|
|
1732
|
+
return stripData(composeWithDerivedBlank(result.refinement, result.segments));
|
|
1733
|
+
}
|
|
1603
1734
|
async function process(pathInput) {
|
|
1604
|
-
const
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
} catch (err) {
|
|
1611
|
-
return adapterThrowResponse(err);
|
|
1612
|
-
} finally {
|
|
1613
|
-
state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
|
|
1614
|
-
}
|
|
1735
|
+
const result = await runImperativeValidation(pathInput, {
|
|
1736
|
+
cancelInFlight: false,
|
|
1737
|
+
commitToSchemaErrors: false
|
|
1738
|
+
});
|
|
1739
|
+
if (!result.ok) return result.error;
|
|
1740
|
+
return composeWithDerivedBlank(result.refinement, result.segments);
|
|
1615
1741
|
}
|
|
1616
1742
|
function adapterThrowResponse(err) {
|
|
1617
1743
|
return {
|
|
@@ -2049,7 +2175,6 @@ function coerceValue(value, accepted, elementAccepted, index) {
|
|
|
2049
2175
|
}
|
|
2050
2176
|
|
|
2051
2177
|
const EMPTY_TRANSFORMS = Object.freeze([]);
|
|
2052
|
-
const INTERACTIVE_TAG_NAMES = /* @__PURE__ */ new Set(["INPUT", "SELECT", "TEXTAREA"]);
|
|
2053
2178
|
const attaformListenersSymbol = Symbol.for("attaform:focus-listeners");
|
|
2054
2179
|
function attachFocusListeners(state, segments, element, instanceMeta) {
|
|
2055
2180
|
const target = element;
|
|
@@ -2100,7 +2225,11 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2100
2225
|
if (typed !== null && typeof raw === "number" && parseFloat(typed) === raw) {
|
|
2101
2226
|
return typed;
|
|
2102
2227
|
}
|
|
2103
|
-
|
|
2228
|
+
try {
|
|
2229
|
+
return String(raw);
|
|
2230
|
+
} catch {
|
|
2231
|
+
return Object.prototype.toString.call(raw);
|
|
2232
|
+
}
|
|
2104
2233
|
});
|
|
2105
2234
|
const slimDefault = state.schema.getDefaultAtPath(segments);
|
|
2106
2235
|
const slimTypes = state.schema.getSlimPrimitiveTypesAtPath(segments);
|
|
@@ -2155,7 +2284,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2155
2284
|
state.markInteracted(segments);
|
|
2156
2285
|
},
|
|
2157
2286
|
registerElement: (element) => {
|
|
2158
|
-
if (!INTERACTIVE_TAG_NAMES.has(element.tagName)) return;
|
|
2287
|
+
if (!paths.INTERACTIVE_TAG_NAMES.has(element.tagName)) return;
|
|
2159
2288
|
const added = state.registerElement(segments, element, formInstanceId);
|
|
2160
2289
|
if (added) attachFocusListeners(state, segments, element, instanceMeta);
|
|
2161
2290
|
},
|
|
@@ -2254,7 +2383,7 @@ function walk(input, segments, schema, paths) {
|
|
|
2254
2383
|
if (slim !== null && slim !== void 0 && typeof slim === "object" && !Array.isArray(slim) && !(slim instanceof Date) && !(slim instanceof RegExp) && !(slim instanceof Map) && !(slim instanceof Set)) {
|
|
2255
2384
|
for (const k of Object.keys(slim)) allKeys.add(k);
|
|
2256
2385
|
}
|
|
2257
|
-
const out =
|
|
2386
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
2258
2387
|
let mutated = allKeys.size !== inputKeys.length;
|
|
2259
2388
|
for (const key of allKeys) {
|
|
2260
2389
|
const orig = input[key];
|
|
@@ -2283,7 +2412,7 @@ function walkUnspecified(slim, segments, paths$1) {
|
|
|
2283
2412
|
}
|
|
2284
2413
|
if (Array.isArray(slim)) return slim;
|
|
2285
2414
|
if (slim !== null && typeof slim === "object") {
|
|
2286
|
-
const out =
|
|
2415
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
2287
2416
|
for (const key of Object.keys(slim)) {
|
|
2288
2417
|
out[key] = walkUnspecified(slim[key], [...segments, key], paths$1);
|
|
2289
2418
|
}
|
|
@@ -2316,7 +2445,7 @@ function substitute(input, segments, schema, paths) {
|
|
|
2316
2445
|
}
|
|
2317
2446
|
if (typeof input === "object") {
|
|
2318
2447
|
let mutated = false;
|
|
2319
|
-
const out =
|
|
2448
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
2320
2449
|
for (const key of Object.keys(input)) {
|
|
2321
2450
|
const orig = input[key];
|
|
2322
2451
|
const walked = substitute(orig, [...segments, key], schema, paths);
|
|
@@ -2368,70 +2497,86 @@ function expandUnsetAt(segments, schema, paths$1) {
|
|
|
2368
2497
|
return result;
|
|
2369
2498
|
}
|
|
2370
2499
|
|
|
2371
|
-
function
|
|
2372
|
-
const inner = vue.computed(() => vue.readonly(form.value));
|
|
2500
|
+
function buildCallableReadonlySnapshotProxy(opts) {
|
|
2373
2501
|
const target = (() => {
|
|
2374
2502
|
});
|
|
2375
|
-
const
|
|
2376
|
-
const
|
|
2503
|
+
const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(opts.snapshot);
|
|
2504
|
+
const callResolve = opts.resolveCall ?? ((arg) => opts.resolveKey(String(arg)));
|
|
2377
2505
|
return new Proxy(target, {
|
|
2378
2506
|
apply(_, __, args) {
|
|
2379
2507
|
const arg = args[0];
|
|
2380
|
-
if (arg === void 0) return
|
|
2381
|
-
|
|
2382
|
-
let cursor = inner.value;
|
|
2383
|
-
for (const seg of segments) {
|
|
2384
|
-
if (cursor === null || cursor === void 0) return void 0;
|
|
2385
|
-
cursor = cursor[seg];
|
|
2386
|
-
}
|
|
2387
|
-
return cursor;
|
|
2508
|
+
if (arg === void 0) return opts.snapshot();
|
|
2509
|
+
return callResolve(arg);
|
|
2388
2510
|
},
|
|
2389
2511
|
get(_, key) {
|
|
2390
2512
|
if (typeof key === "symbol") {
|
|
2391
|
-
if (key === Symbol.toPrimitive) return
|
|
2513
|
+
if (key === Symbol.toPrimitive) return toPrimitive;
|
|
2392
2514
|
return Reflect.get(target, key);
|
|
2393
2515
|
}
|
|
2394
|
-
if (key === "toJSON") return
|
|
2395
|
-
if (key === "toString") return
|
|
2396
|
-
if (key === "valueOf")
|
|
2397
|
-
|
|
2398
|
-
return this;
|
|
2399
|
-
};
|
|
2400
|
-
return inner.value[key];
|
|
2516
|
+
if (key === "toJSON") return toJSON;
|
|
2517
|
+
if (key === "toString") return toString;
|
|
2518
|
+
if (key === "valueOf") return valueOf;
|
|
2519
|
+
return opts.resolveKey(key);
|
|
2401
2520
|
},
|
|
2402
2521
|
has(_, key) {
|
|
2403
2522
|
if (typeof key === "symbol") return Reflect.has(target, key);
|
|
2404
|
-
return
|
|
2405
|
-
},
|
|
2406
|
-
ownKeys() {
|
|
2407
|
-
return Reflect.ownKeys(inner.value);
|
|
2523
|
+
return opts.hasKey(key);
|
|
2408
2524
|
},
|
|
2525
|
+
ownKeys: () => opts.ownKeys(),
|
|
2409
2526
|
getOwnPropertyDescriptor(_, key) {
|
|
2410
|
-
|
|
2411
|
-
if (
|
|
2412
|
-
return
|
|
2527
|
+
if (typeof key !== "string") return void 0;
|
|
2528
|
+
if (opts.describeKey !== void 0) return opts.describeKey(key);
|
|
2529
|
+
if (!opts.hasKey(key)) return void 0;
|
|
2530
|
+
return {
|
|
2531
|
+
configurable: true,
|
|
2532
|
+
enumerable: true,
|
|
2533
|
+
writable: false,
|
|
2534
|
+
value: opts.resolveKey(key)
|
|
2535
|
+
};
|
|
2413
2536
|
},
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
// TypeError in strict-mode consumers, surprising users who
|
|
2417
|
-
// assigned through the proxy and expected it to be ignored.
|
|
2418
|
-
set(_, key) {
|
|
2419
|
-
if (paths.__DEV__) {
|
|
2420
|
-
console.warn(
|
|
2421
|
-
`[attaform] form.values is read-only \u2014 write to "${String(key)}" was ignored. Use form.setValue / the directive / field-array helpers instead.`
|
|
2422
|
-
);
|
|
2423
|
-
}
|
|
2537
|
+
set: (_, key) => {
|
|
2538
|
+
warnReadOnly(opts.surface, "write", key);
|
|
2424
2539
|
return true;
|
|
2425
2540
|
},
|
|
2426
|
-
deleteProperty(_, key) {
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2541
|
+
deleteProperty: (_, key) => {
|
|
2542
|
+
warnReadOnly(opts.surface, "delete", key);
|
|
2543
|
+
return true;
|
|
2544
|
+
},
|
|
2545
|
+
defineProperty: (_, key) => {
|
|
2546
|
+
warnReadOnly(opts.surface, "define", key);
|
|
2432
2547
|
return true;
|
|
2548
|
+
}
|
|
2549
|
+
});
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
function buildValuesProxy(form) {
|
|
2553
|
+
const inner = vue.computed(() => vue.readonly(form.value));
|
|
2554
|
+
return buildCallableReadonlySnapshotProxy({
|
|
2555
|
+
surface: "form.values",
|
|
2556
|
+
snapshot: () => inner.value,
|
|
2557
|
+
// Read through the readonly proxy at access time so Vue's
|
|
2558
|
+
// dependency tracking lands inside the consumer's active effect
|
|
2559
|
+
// — `inner.value[key]` is what triggers per-key tracking.
|
|
2560
|
+
resolveKey: (key) => inner.value[key],
|
|
2561
|
+
// Dynamic path: walk segments through the readonly proxy. Each
|
|
2562
|
+
// step reads through the proxy's own get traps so dependency
|
|
2563
|
+
// tracking propagates at every level.
|
|
2564
|
+
resolveCall: (arg) => {
|
|
2565
|
+
const { segments } = paths.canonicalizePath(arg);
|
|
2566
|
+
let cursor = inner.value;
|
|
2567
|
+
for (const seg of segments) {
|
|
2568
|
+
if (cursor === null || cursor === void 0) return void 0;
|
|
2569
|
+
cursor = cursor[seg];
|
|
2570
|
+
}
|
|
2571
|
+
return cursor;
|
|
2433
2572
|
},
|
|
2434
|
-
|
|
2573
|
+
ownKeys: () => Reflect.ownKeys(inner.value),
|
|
2574
|
+
hasKey: (key) => Reflect.has(inner.value, key),
|
|
2575
|
+
describeKey: (key) => {
|
|
2576
|
+
const desc = Reflect.getOwnPropertyDescriptor(inner.value, key);
|
|
2577
|
+
if (desc !== void 0) desc.configurable = true;
|
|
2578
|
+
return desc;
|
|
2579
|
+
}
|
|
2435
2580
|
});
|
|
2436
2581
|
}
|
|
2437
2582
|
|
|
@@ -2497,10 +2642,18 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2497
2642
|
next,
|
|
2498
2643
|
state.schema
|
|
2499
2644
|
);
|
|
2645
|
+
const ok2 = state.setValueAtPath([], walked2.cleanedValues, withInstanceMeta());
|
|
2646
|
+
if (!ok2) return false;
|
|
2500
2647
|
for (const pathKey of walked2.paths) {
|
|
2501
|
-
|
|
2648
|
+
const blankSegments = paths.segmentsForPathKey(pathKey);
|
|
2649
|
+
if (blankSegments === null) continue;
|
|
2650
|
+
state.setValueAtPath(
|
|
2651
|
+
blankSegments,
|
|
2652
|
+
state.getValueAtPath(blankSegments),
|
|
2653
|
+
withInstanceMeta({ blank: true })
|
|
2654
|
+
);
|
|
2502
2655
|
}
|
|
2503
|
-
return
|
|
2656
|
+
return true;
|
|
2504
2657
|
}
|
|
2505
2658
|
const segments = paths.canonicalizePath(pathOrValue).segments;
|
|
2506
2659
|
const writeUnsetAt = () => {
|
|
@@ -2649,23 +2802,45 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2649
2802
|
const rootFieldState = getRootFieldStateAt([]);
|
|
2650
2803
|
const formMeta = vue.readonly(
|
|
2651
2804
|
vue.reactive({
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
pristine
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2805
|
+
get value() {
|
|
2806
|
+
return rootFieldState.value.value;
|
|
2807
|
+
},
|
|
2808
|
+
get original() {
|
|
2809
|
+
return rootFieldState.value.original;
|
|
2810
|
+
},
|
|
2811
|
+
get pristine() {
|
|
2812
|
+
return rootFieldState.value.pristine;
|
|
2813
|
+
},
|
|
2814
|
+
get dirty() {
|
|
2815
|
+
return rootFieldState.value.dirty;
|
|
2816
|
+
},
|
|
2817
|
+
get focused() {
|
|
2818
|
+
return rootFieldState.value.focused;
|
|
2819
|
+
},
|
|
2820
|
+
get blurred() {
|
|
2821
|
+
return rootFieldState.value.blurred;
|
|
2822
|
+
},
|
|
2823
|
+
get touched() {
|
|
2824
|
+
return rootFieldState.value.touched;
|
|
2825
|
+
},
|
|
2826
|
+
get interacted() {
|
|
2827
|
+
return rootFieldState.value.interacted;
|
|
2828
|
+
},
|
|
2829
|
+
get blurredAfterInteraction() {
|
|
2830
|
+
return rootFieldState.value.blurredAfterInteraction;
|
|
2831
|
+
},
|
|
2832
|
+
get connected() {
|
|
2833
|
+
return rootFieldState.value.connected;
|
|
2834
|
+
},
|
|
2835
|
+
get element() {
|
|
2836
|
+
return rootFieldState.value.element;
|
|
2837
|
+
},
|
|
2838
|
+
get elements() {
|
|
2839
|
+
return rootFieldState.value.elements;
|
|
2840
|
+
},
|
|
2841
|
+
get updatedAt() {
|
|
2842
|
+
return rootFieldState.value.updatedAt;
|
|
2843
|
+
},
|
|
2669
2844
|
// Whole-form validating mirrors the LIFECYCLE counter
|
|
2670
2845
|
// (`state.activeValidations`) ORed with any per-leaf validation
|
|
2671
2846
|
// in flight (via `rootFieldState.validating`). A submit-time
|
|
@@ -2688,21 +2863,51 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2688
2863
|
// FieldState surface, so `form.meta.displayState` matches
|
|
2689
2864
|
// `form.fields().displayState` exactly — the predicate runs once
|
|
2690
2865
|
// at the root and the result is shared.
|
|
2691
|
-
displayState
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2866
|
+
get displayState() {
|
|
2867
|
+
return rootFieldState.value.displayState;
|
|
2868
|
+
},
|
|
2869
|
+
get showErrors() {
|
|
2870
|
+
return rootFieldState.value.showErrors;
|
|
2871
|
+
},
|
|
2872
|
+
get showPending() {
|
|
2873
|
+
return rootFieldState.value.showPending;
|
|
2874
|
+
},
|
|
2875
|
+
get showSuccess() {
|
|
2876
|
+
return rootFieldState.value.showSuccess;
|
|
2877
|
+
},
|
|
2878
|
+
get showIdle() {
|
|
2879
|
+
return rootFieldState.value.showIdle;
|
|
2880
|
+
},
|
|
2881
|
+
get firstError() {
|
|
2882
|
+
return rootFieldState.value.firstError;
|
|
2883
|
+
},
|
|
2884
|
+
get path() {
|
|
2885
|
+
return rootFieldState.value.path;
|
|
2886
|
+
},
|
|
2887
|
+
get id() {
|
|
2888
|
+
return rootFieldState.value.id;
|
|
2889
|
+
},
|
|
2890
|
+
get aria() {
|
|
2891
|
+
return rootFieldState.value.aria;
|
|
2892
|
+
},
|
|
2893
|
+
get key() {
|
|
2894
|
+
return rootFieldState.value.key;
|
|
2895
|
+
},
|
|
2896
|
+
get blank() {
|
|
2897
|
+
return rootFieldState.value.blank;
|
|
2898
|
+
},
|
|
2899
|
+
get label() {
|
|
2900
|
+
return rootFieldState.value.label;
|
|
2901
|
+
},
|
|
2902
|
+
get description() {
|
|
2903
|
+
return rootFieldState.value.description;
|
|
2904
|
+
},
|
|
2905
|
+
get placeholder() {
|
|
2906
|
+
return rootFieldState.value.placeholder;
|
|
2907
|
+
},
|
|
2908
|
+
get meta() {
|
|
2909
|
+
return rootFieldState.value.meta;
|
|
2910
|
+
},
|
|
2706
2911
|
// Lifecycle (form-level only — not on FieldState).
|
|
2707
2912
|
submitting,
|
|
2708
2913
|
submissionAttempts,
|
|
@@ -2711,7 +2916,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2711
2916
|
// Scalar mirror over the array — meta is a single sticky surface
|
|
2712
2917
|
// for both templates and `useWizard`'s `FormStatus`, so the
|
|
2713
2918
|
// projection lives here.
|
|
2714
|
-
errorCount
|
|
2919
|
+
get errorCount() {
|
|
2920
|
+
return metaErrors.value.length;
|
|
2921
|
+
},
|
|
2715
2922
|
submitted,
|
|
2716
2923
|
// Per-`useForm()`-call identity. Stable for one mount; new on
|
|
2717
2924
|
// re-mount; orthogonal to `form.key` (which is the user-supplied
|
|
@@ -2748,12 +2955,20 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2748
2955
|
}
|
|
2749
2956
|
};
|
|
2750
2957
|
function clear(pathInput) {
|
|
2751
|
-
|
|
2752
|
-
|
|
2958
|
+
if (pathInput === void 0) {
|
|
2959
|
+
return setValueImpl(unset);
|
|
2960
|
+
}
|
|
2961
|
+
return setValueImpl(pathInput, unset);
|
|
2753
2962
|
}
|
|
2754
2963
|
const persist = async (pathInput, options2) => {
|
|
2755
2964
|
const segments = paths.canonicalizePath(pathInput).segments;
|
|
2756
|
-
paths.
|
|
2965
|
+
if (!paths.allowSensitivePersist(
|
|
2966
|
+
segments,
|
|
2967
|
+
options2?.acknowledgeSensitive === true,
|
|
2968
|
+
state.isSensitivePath
|
|
2969
|
+
)) {
|
|
2970
|
+
return;
|
|
2971
|
+
}
|
|
2757
2972
|
if (persistence === void 0) return;
|
|
2758
2973
|
await persistence.writePathImmediately(segments);
|
|
2759
2974
|
};
|
|
@@ -2819,7 +3034,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2819
3034
|
getFormMetaBase,
|
|
2820
3035
|
fieldStateAccessorOptions
|
|
2821
3036
|
);
|
|
3037
|
+
const needsLazyGate = state.defaultValuesFactory.value !== void 0 || state.hasSsrPrefetch;
|
|
2822
3038
|
function gated(fn) {
|
|
3039
|
+
if (!needsLazyGate) return fn;
|
|
2823
3040
|
return ((...args) => {
|
|
2824
3041
|
void state.activate();
|
|
2825
3042
|
return fn(...args);
|
|
@@ -2842,7 +3059,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2842
3059
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2843
3060
|
return EMPTY_FIELD_RECORD;
|
|
2844
3061
|
}
|
|
2845
|
-
const out =
|
|
3062
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
2846
3063
|
for (const key of Object.keys(value)) {
|
|
2847
3064
|
out[key] = callTerminal(`${path}.${key}`);
|
|
2848
3065
|
}
|
|
@@ -2938,19 +3155,142 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2938
3155
|
};
|
|
2939
3156
|
}
|
|
2940
3157
|
|
|
2941
|
-
function
|
|
2942
|
-
const
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
3158
|
+
function applyDuStubs(schema, data, options = {}) {
|
|
3159
|
+
const warned = options.warn === true ? /* @__PURE__ */ new Set() : void 0;
|
|
3160
|
+
return walkDuStubs(schema, data, options.basePath ?? [], warned);
|
|
3161
|
+
}
|
|
3162
|
+
function walkDuStubs(schema, value, path, warned) {
|
|
3163
|
+
if (value === null || value === void 0 || typeof value !== "object") return value;
|
|
3164
|
+
if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || typeof value === "function") {
|
|
3165
|
+
return value;
|
|
3166
|
+
}
|
|
3167
|
+
if (Array.isArray(value)) {
|
|
3168
|
+
return value.map((item, i) => walkDuStubs(schema, item, [...path, i], warned));
|
|
3169
|
+
}
|
|
3170
|
+
const rec = value;
|
|
3171
|
+
const du = schema.getUnionDiscriminatorAtPath(path);
|
|
3172
|
+
if (du !== void 0) {
|
|
3173
|
+
const discValue = rec[du.discriminatorKey];
|
|
3174
|
+
if (discValue !== void 0 && !du.isVariantSelected(discValue)) {
|
|
3175
|
+
const isKindBlank = discValue === "" || discValue === 0 || discValue === 0n || discValue === false || discValue === null;
|
|
3176
|
+
if (!isKindBlank && warned !== void 0 && paths.__DEV__) {
|
|
3177
|
+
const dotted = path.map((s) => String(s)).join(".") || "(root)";
|
|
3178
|
+
const key = `${dotted}::${String(discValue)}`;
|
|
3179
|
+
if (!warned.has(key)) {
|
|
3180
|
+
warned.add(key);
|
|
3181
|
+
console.warn(
|
|
3182
|
+
`[attaform] defaultValues at '${dotted}' carries discriminator '${du.discriminatorKey}=${JSON.stringify(discValue)}' which isn't a known variant. Form mounts in a stub holding only the discriminator key. Validation will surface the mismatch.`
|
|
3183
|
+
);
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
const stub = /* @__PURE__ */ Object.create(null);
|
|
3187
|
+
stub[du.discriminatorKey] = discValue;
|
|
3188
|
+
return stub;
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
3192
|
+
for (const k of Object.keys(rec)) {
|
|
3193
|
+
out[k] = walkDuStubs(schema, rec[k], [...path, k], warned);
|
|
3194
|
+
}
|
|
3195
|
+
return out;
|
|
3196
|
+
}
|
|
3197
|
+
|
|
3198
|
+
function createVariantMemory() {
|
|
3199
|
+
const memory = /* @__PURE__ */ new Map();
|
|
3200
|
+
function clearAtArrayIndices(arrayPath, indexFilter) {
|
|
3201
|
+
for (const memKey of [...memory.keys()]) {
|
|
3202
|
+
const segs = paths.segmentsForPathKey(memKey);
|
|
3203
|
+
if (segs === null) continue;
|
|
3204
|
+
if (!paths.isPathPrefix(arrayPath, segs)) continue;
|
|
3205
|
+
if (segs.length <= arrayPath.length) continue;
|
|
3206
|
+
const idxSeg = segs[arrayPath.length];
|
|
3207
|
+
if (typeof idxSeg !== "number") continue;
|
|
3208
|
+
if (indexFilter(idxSeg)) memory.delete(memKey);
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
return {
|
|
3212
|
+
clear() {
|
|
3213
|
+
memory.clear();
|
|
3214
|
+
},
|
|
3215
|
+
clearUnderPath(parentPath) {
|
|
3216
|
+
for (const memKey of [...memory.keys()]) {
|
|
3217
|
+
const segs = paths.segmentsForPathKey(memKey);
|
|
3218
|
+
if (segs === null) continue;
|
|
3219
|
+
if (paths.isPathPrefix(parentPath, segs)) memory.delete(memKey);
|
|
3220
|
+
}
|
|
3221
|
+
},
|
|
3222
|
+
applyArrayOp(arrayPath, op) {
|
|
3223
|
+
switch (op.kind) {
|
|
3224
|
+
case "insert":
|
|
3225
|
+
case "remove":
|
|
3226
|
+
clearAtArrayIndices(arrayPath, (i) => i >= op.index);
|
|
3227
|
+
return;
|
|
3228
|
+
case "move": {
|
|
3229
|
+
const lo = Math.min(op.from, op.to);
|
|
3230
|
+
const hi = Math.max(op.from, op.to);
|
|
3231
|
+
clearAtArrayIndices(arrayPath, (i) => i >= lo && i <= hi);
|
|
3232
|
+
return;
|
|
3233
|
+
}
|
|
3234
|
+
case "swap":
|
|
3235
|
+
clearAtArrayIndices(arrayPath, (i) => i === op.a || i === op.b);
|
|
3236
|
+
return;
|
|
3237
|
+
case "replace-at":
|
|
3238
|
+
clearAtArrayIndices(arrayPath, (i) => i === op.index);
|
|
3239
|
+
return;
|
|
3240
|
+
}
|
|
3241
|
+
},
|
|
3242
|
+
recordOutgoing(unionKey, discValue, snapshot) {
|
|
3243
|
+
let perUnion = memory.get(unionKey);
|
|
3244
|
+
if (perUnion === void 0) {
|
|
3245
|
+
perUnion = /* @__PURE__ */ new Map();
|
|
3246
|
+
memory.set(unionKey, perUnion);
|
|
3247
|
+
}
|
|
3248
|
+
perUnion.set(discValue, snapshot);
|
|
3249
|
+
},
|
|
3250
|
+
lookupIncoming(unionKey, discValue) {
|
|
3251
|
+
return memory.get(unionKey)?.get(discValue);
|
|
3252
|
+
}
|
|
3253
|
+
};
|
|
3254
|
+
}
|
|
3255
|
+
function cloneVariantSnapshot(value) {
|
|
3256
|
+
if (value === null || typeof value !== "object") return value;
|
|
3257
|
+
const raw = vue.toRaw(value);
|
|
3258
|
+
if (raw instanceof Date) return new Date(raw.getTime());
|
|
3259
|
+
if (raw instanceof Map) {
|
|
3260
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
3261
|
+
for (const [k, v] of raw.entries()) out2.set(cloneVariantSnapshot(k), cloneVariantSnapshot(v));
|
|
3262
|
+
return out2;
|
|
3263
|
+
}
|
|
3264
|
+
if (raw instanceof Set) {
|
|
3265
|
+
const out2 = /* @__PURE__ */ new Set();
|
|
3266
|
+
for (const v of raw) out2.add(cloneVariantSnapshot(v));
|
|
3267
|
+
return out2;
|
|
3268
|
+
}
|
|
3269
|
+
if (raw instanceof RegExp) return new RegExp(raw.source, raw.flags);
|
|
3270
|
+
if (Array.isArray(raw)) {
|
|
3271
|
+
const out2 = new Array(raw.length);
|
|
3272
|
+
for (let i = 0; i < raw.length; i++) out2[i] = cloneVariantSnapshot(raw[i]);
|
|
3273
|
+
return out2;
|
|
3274
|
+
}
|
|
3275
|
+
const src = raw;
|
|
3276
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
3277
|
+
for (const k of Object.keys(src)) out[k] = cloneVariantSnapshot(src[k]);
|
|
3278
|
+
return out;
|
|
3279
|
+
}
|
|
3280
|
+
|
|
3281
|
+
function createArrayIdentity(getArrayLength) {
|
|
3282
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
3283
|
+
const baselines = /* @__PURE__ */ new Map();
|
|
3284
|
+
let counter = 0;
|
|
3285
|
+
const allocate = () => `k${(counter++).toString(36)}`;
|
|
3286
|
+
function ensure(arrayKey, expectedLen) {
|
|
3287
|
+
let ids = tokens.get(arrayKey);
|
|
3288
|
+
const firstTrack = ids === void 0;
|
|
3289
|
+
if (ids === void 0) {
|
|
3290
|
+
ids = [];
|
|
3291
|
+
tokens.set(arrayKey, ids);
|
|
3292
|
+
}
|
|
3293
|
+
while (ids.length < expectedLen) ids.push(allocate());
|
|
2954
3294
|
if (ids.length > expectedLen) ids.length = expectedLen;
|
|
2955
3295
|
if (firstTrack) baselines.set(arrayKey, [...ids]);
|
|
2956
3296
|
return ids;
|
|
@@ -3000,6 +3340,36 @@ function createArrayIdentity(getArrayLength) {
|
|
|
3000
3340
|
return;
|
|
3001
3341
|
}
|
|
3002
3342
|
},
|
|
3343
|
+
applyRemap(arrayPath, remap) {
|
|
3344
|
+
if (remap.moved.size === 0 && remap.vacated.size === 0 && remap.fresh.size === 0) return;
|
|
3345
|
+
const relocate = (store) => {
|
|
3346
|
+
const idxPos = arrayPath.length;
|
|
3347
|
+
const snapshots = [];
|
|
3348
|
+
for (const [key, value] of store) {
|
|
3349
|
+
const segments = paths.segmentsForPathKey(key);
|
|
3350
|
+
if (segments === null) continue;
|
|
3351
|
+
if (!paths.isPathPrefix(arrayPath, segments)) continue;
|
|
3352
|
+
if (segments.length <= idxPos) continue;
|
|
3353
|
+
const idxSeg = segments[idxPos];
|
|
3354
|
+
if (typeof idxSeg !== "number") continue;
|
|
3355
|
+
if (!remap.moved.has(idxSeg) && !remap.vacated.has(idxSeg) && !remap.fresh.has(idxSeg)) {
|
|
3356
|
+
continue;
|
|
3357
|
+
}
|
|
3358
|
+
snapshots.push({ segments: [...segments], index: idxSeg, value });
|
|
3359
|
+
}
|
|
3360
|
+
if (snapshots.length === 0) return;
|
|
3361
|
+
for (const snap of snapshots) store.delete(paths.canonicalizePath(snap.segments).key);
|
|
3362
|
+
for (const snap of snapshots) {
|
|
3363
|
+
const target = remap.moved.get(snap.index);
|
|
3364
|
+
if (target === void 0) continue;
|
|
3365
|
+
const relocated = snap.segments.slice();
|
|
3366
|
+
relocated[idxPos] = target;
|
|
3367
|
+
store.set(paths.canonicalizePath(relocated).key, snap.value);
|
|
3368
|
+
}
|
|
3369
|
+
};
|
|
3370
|
+
relocate(tokens);
|
|
3371
|
+
relocate(baselines);
|
|
3372
|
+
},
|
|
3003
3373
|
realign(arraySegs) {
|
|
3004
3374
|
ensure(paths.canonicalizePath(arraySegs).key, getArrayLength(arraySegs));
|
|
3005
3375
|
},
|
|
@@ -3114,6 +3484,94 @@ function migrateSetSubtree(set, arrayPath, remap) {
|
|
|
3114
3484
|
}
|
|
3115
3485
|
}
|
|
3116
3486
|
|
|
3487
|
+
function createArrayBookkeeping(deps) {
|
|
3488
|
+
const {
|
|
3489
|
+
form,
|
|
3490
|
+
fields,
|
|
3491
|
+
userErrors,
|
|
3492
|
+
originals,
|
|
3493
|
+
blankPaths,
|
|
3494
|
+
originalBlankPaths,
|
|
3495
|
+
fieldValidationCounts,
|
|
3496
|
+
fieldValidationState,
|
|
3497
|
+
schemaErrors,
|
|
3498
|
+
activeValidations,
|
|
3499
|
+
arrayIdentity,
|
|
3500
|
+
touchFieldRecord,
|
|
3501
|
+
decFieldValidation
|
|
3502
|
+
} = deps;
|
|
3503
|
+
function migrateElementState(arrayPath, remap) {
|
|
3504
|
+
if (remap.moved.size === 0 && remap.vacated.size === 0) return;
|
|
3505
|
+
migrateMapSubtree(fields, arrayPath, remap, (record, segments) => ({
|
|
3506
|
+
...record,
|
|
3507
|
+
path: segments
|
|
3508
|
+
}));
|
|
3509
|
+
migrateMapSubtree(
|
|
3510
|
+
userErrors,
|
|
3511
|
+
arrayPath,
|
|
3512
|
+
remap,
|
|
3513
|
+
(errors, segments) => errors.map((error) => ({ ...error, path: [...segments] }))
|
|
3514
|
+
);
|
|
3515
|
+
migrateMapSubtree(originals, arrayPath, remap, (record, segments) => ({
|
|
3516
|
+
segments,
|
|
3517
|
+
value: record.value
|
|
3518
|
+
}));
|
|
3519
|
+
migrateSetSubtree(blankPaths, arrayPath, remap);
|
|
3520
|
+
migrateSetSubtree(originalBlankPaths, arrayPath, remap);
|
|
3521
|
+
migrateMapSubtree(fieldValidationCounts, arrayPath, remap, (count) => count);
|
|
3522
|
+
arrayIdentity.applyRemap(arrayPath, remap);
|
|
3523
|
+
}
|
|
3524
|
+
function seedFreshElement(arrayPath, freshIndex) {
|
|
3525
|
+
const elementPath = [...arrayPath, freshIndex];
|
|
3526
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3527
|
+
diffAndApply(void 0, getAtPath(form.value, elementPath), elementPath, (patch) => {
|
|
3528
|
+
if (patch.kind !== "added") return;
|
|
3529
|
+
const { key } = paths.canonicalizePath(patch.path);
|
|
3530
|
+
if (!originals.has(key)) originals.set(key, { segments: patch.path, value: void 0 });
|
|
3531
|
+
touchFieldRecord(key, patch.path, { updatedAt: now });
|
|
3532
|
+
});
|
|
3533
|
+
}
|
|
3534
|
+
function dropSchemaErrorsAtChangedIndices(arrayPath, remap) {
|
|
3535
|
+
const changed = changedIndices(remap);
|
|
3536
|
+
if (changed.size === 0) return;
|
|
3537
|
+
const idxPos = arrayPath.length;
|
|
3538
|
+
for (const key of [...schemaErrors.keys()]) {
|
|
3539
|
+
const segs = paths.segmentsForPathKey(key);
|
|
3540
|
+
if (segs === null) continue;
|
|
3541
|
+
if (!paths.isPathPrefix(arrayPath, segs)) continue;
|
|
3542
|
+
if (segs.length <= idxPos) continue;
|
|
3543
|
+
const idx = segs[idxPos];
|
|
3544
|
+
if (typeof idx === "number" && changed.has(idx)) schemaErrors.delete(key);
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
function abortValidationAtVacatedIndices(arrayPath, remap) {
|
|
3548
|
+
if (remap.vacated.size === 0) return;
|
|
3549
|
+
const idxPos = arrayPath.length;
|
|
3550
|
+
for (const [key, entry] of [...fieldValidationState]) {
|
|
3551
|
+
const segs = paths.segmentsForPathKey(key);
|
|
3552
|
+
if (segs === null) continue;
|
|
3553
|
+
if (!paths.isPathPrefix(arrayPath, segs)) continue;
|
|
3554
|
+
if (segs.length <= idxPos) continue;
|
|
3555
|
+
const idx = segs[idxPos];
|
|
3556
|
+
if (typeof idx !== "number" || !remap.vacated.has(idx)) continue;
|
|
3557
|
+
if (entry.timer !== null) {
|
|
3558
|
+
clearTimeout(entry.timer);
|
|
3559
|
+
} else if (!entry.settled) {
|
|
3560
|
+
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
3561
|
+
decFieldValidation(key);
|
|
3562
|
+
}
|
|
3563
|
+
entry.controller.abort();
|
|
3564
|
+
fieldValidationState.delete(key);
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
return {
|
|
3568
|
+
migrateElementState,
|
|
3569
|
+
seedFreshElement,
|
|
3570
|
+
dropSchemaErrorsAtChangedIndices,
|
|
3571
|
+
abortValidationAtVacatedIndices
|
|
3572
|
+
};
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3117
3575
|
function isHydratedFieldRecord(value) {
|
|
3118
3576
|
if (typeof value !== "object" || value === null) return false;
|
|
3119
3577
|
const r = value;
|
|
@@ -3137,43 +3595,6 @@ function warnMalformedHydration(formKey, kind, rawKey) {
|
|
|
3137
3595
|
`[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).`
|
|
3138
3596
|
);
|
|
3139
3597
|
}
|
|
3140
|
-
function applyDuStubs(schema, data, options = {}) {
|
|
3141
|
-
const warned = options.warn === true ? /* @__PURE__ */ new Set() : void 0;
|
|
3142
|
-
return walkDuStubs(schema, data, options.basePath ?? [], warned);
|
|
3143
|
-
}
|
|
3144
|
-
function walkDuStubs(schema, value, path, warned) {
|
|
3145
|
-
if (value === null || value === void 0 || typeof value !== "object") return value;
|
|
3146
|
-
if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || typeof value === "function") {
|
|
3147
|
-
return value;
|
|
3148
|
-
}
|
|
3149
|
-
if (Array.isArray(value)) {
|
|
3150
|
-
return value.map((item, i) => walkDuStubs(schema, item, [...path, i], warned));
|
|
3151
|
-
}
|
|
3152
|
-
const rec = value;
|
|
3153
|
-
const du = schema.getUnionDiscriminatorAtPath(path);
|
|
3154
|
-
if (du !== void 0) {
|
|
3155
|
-
const discValue = rec[du.discriminatorKey];
|
|
3156
|
-
if (discValue !== void 0 && !du.isVariantSelected(discValue)) {
|
|
3157
|
-
const isKindBlank = discValue === "" || discValue === 0 || discValue === 0n || discValue === false || discValue === null;
|
|
3158
|
-
if (!isKindBlank && warned !== void 0 && paths.__DEV__) {
|
|
3159
|
-
const dotted = path.map((s) => String(s)).join(".") || "(root)";
|
|
3160
|
-
const key = `${dotted}::${String(discValue)}`;
|
|
3161
|
-
if (!warned.has(key)) {
|
|
3162
|
-
warned.add(key);
|
|
3163
|
-
console.warn(
|
|
3164
|
-
`[attaform] defaultValues at '${dotted}' carries discriminator '${du.discriminatorKey}=${JSON.stringify(discValue)}' which isn't a known variant. Form mounts in a stub holding only the discriminator key. Validation will surface the mismatch.`
|
|
3165
|
-
);
|
|
3166
|
-
}
|
|
3167
|
-
}
|
|
3168
|
-
return { [du.discriminatorKey]: discValue };
|
|
3169
|
-
}
|
|
3170
|
-
}
|
|
3171
|
-
const out = {};
|
|
3172
|
-
for (const k of Object.keys(rec)) {
|
|
3173
|
-
out[k] = walkDuStubs(schema, rec[k], [...path, k], warned);
|
|
3174
|
-
}
|
|
3175
|
-
return out;
|
|
3176
|
-
}
|
|
3177
3598
|
function isPathKeyUnder(existingKey, parentPath) {
|
|
3178
3599
|
const parsed = paths.segmentsForPathKey(existingKey);
|
|
3179
3600
|
if (parsed === null) return false;
|
|
@@ -3209,31 +3630,6 @@ function stripSymbolsDeep(value) {
|
|
|
3209
3630
|
}
|
|
3210
3631
|
return mutated ? out : value;
|
|
3211
3632
|
}
|
|
3212
|
-
function cloneVariantSnapshot(value) {
|
|
3213
|
-
if (value === null || typeof value !== "object") return value;
|
|
3214
|
-
const raw = vue.toRaw(value);
|
|
3215
|
-
if (raw instanceof Date) return new Date(raw.getTime());
|
|
3216
|
-
if (raw instanceof Map) {
|
|
3217
|
-
const out2 = /* @__PURE__ */ new Map();
|
|
3218
|
-
for (const [k, v] of raw.entries()) out2.set(cloneVariantSnapshot(k), cloneVariantSnapshot(v));
|
|
3219
|
-
return out2;
|
|
3220
|
-
}
|
|
3221
|
-
if (raw instanceof Set) {
|
|
3222
|
-
const out2 = /* @__PURE__ */ new Set();
|
|
3223
|
-
for (const v of raw) out2.add(cloneVariantSnapshot(v));
|
|
3224
|
-
return out2;
|
|
3225
|
-
}
|
|
3226
|
-
if (raw instanceof RegExp) return new RegExp(raw.source, raw.flags);
|
|
3227
|
-
if (Array.isArray(raw)) {
|
|
3228
|
-
const out2 = new Array(raw.length);
|
|
3229
|
-
for (let i = 0; i < raw.length; i++) out2[i] = cloneVariantSnapshot(raw[i]);
|
|
3230
|
-
return out2;
|
|
3231
|
-
}
|
|
3232
|
-
const src = raw;
|
|
3233
|
-
const out = {};
|
|
3234
|
-
for (const k of Object.keys(src)) out[k] = cloneVariantSnapshot(src[k]);
|
|
3235
|
-
return out;
|
|
3236
|
-
}
|
|
3237
3633
|
function walkAuthoredFromConstraints(value, prefix, out) {
|
|
3238
3634
|
if (prefix.length > 0) out.add(paths.canonicalizePath(prefix).key);
|
|
3239
3635
|
if (isPlainRecord(value)) {
|
|
@@ -3306,7 +3702,6 @@ function createFormStore(options) {
|
|
|
3306
3702
|
const coerceIndex = resolveCoercionIndex(options.coerce);
|
|
3307
3703
|
const resolvedGetDisplayState = resolveGetDisplayState(options.getDisplayState);
|
|
3308
3704
|
const resolvedIsSensitivePath = options.isSensitivePath ?? paths.isSensitivePath;
|
|
3309
|
-
const resolvedSegmentMatchesSensitive = options.segmentMatchesSensitive ?? paths.segmentMatchesSensitive;
|
|
3310
3705
|
const cleanupHooks = [];
|
|
3311
3706
|
const modules = /* @__PURE__ */ new Map();
|
|
3312
3707
|
const completedConstraints = defaultValues === void 0 ? void 0 : mergeStructural(schema, [], defaultValues);
|
|
@@ -3369,117 +3764,17 @@ function createFormStore(options) {
|
|
|
3369
3764
|
blankPaths.add(key);
|
|
3370
3765
|
originalBlankPaths.add(key);
|
|
3371
3766
|
}
|
|
3372
|
-
const variantMemory =
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3767
|
+
const variantMemory = createVariantMemory();
|
|
3768
|
+
const pathOrdinals = /* @__PURE__ */ new Map();
|
|
3769
|
+
let nextOrdinal = 0;
|
|
3770
|
+
function ensurePathOrdinal(key) {
|
|
3771
|
+
let ordinal = pathOrdinals.get(key);
|
|
3772
|
+
if (ordinal === void 0) {
|
|
3773
|
+
ordinal = nextOrdinal;
|
|
3774
|
+
pathOrdinals.set(key, ordinal);
|
|
3775
|
+
nextOrdinal += 1;
|
|
3378
3776
|
}
|
|
3379
|
-
|
|
3380
|
-
function clearVariantMemoryAtArrayIndices(arrayPath, indexFilter) {
|
|
3381
|
-
for (const memKey of [...variantMemory.keys()]) {
|
|
3382
|
-
const segs = paths.segmentsForPathKey(memKey);
|
|
3383
|
-
if (segs === null) continue;
|
|
3384
|
-
if (!isPathPrefix(arrayPath, segs)) continue;
|
|
3385
|
-
if (segs.length <= arrayPath.length) continue;
|
|
3386
|
-
const idxSeg = segs[arrayPath.length];
|
|
3387
|
-
if (typeof idxSeg !== "number") continue;
|
|
3388
|
-
if (indexFilter(idxSeg)) variantMemory.delete(memKey);
|
|
3389
|
-
}
|
|
3390
|
-
}
|
|
3391
|
-
function applyArrayOpToMemory(arrayPath, op) {
|
|
3392
|
-
switch (op.kind) {
|
|
3393
|
-
case "insert":
|
|
3394
|
-
case "remove":
|
|
3395
|
-
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i >= op.index);
|
|
3396
|
-
return;
|
|
3397
|
-
case "move": {
|
|
3398
|
-
const lo = Math.min(op.from, op.to);
|
|
3399
|
-
const hi = Math.max(op.from, op.to);
|
|
3400
|
-
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i >= lo && i <= hi);
|
|
3401
|
-
return;
|
|
3402
|
-
}
|
|
3403
|
-
case "swap":
|
|
3404
|
-
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i === op.a || i === op.b);
|
|
3405
|
-
return;
|
|
3406
|
-
case "replace-at":
|
|
3407
|
-
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i === op.index);
|
|
3408
|
-
return;
|
|
3409
|
-
}
|
|
3410
|
-
}
|
|
3411
|
-
function migrateArrayElementState(arrayPath, remap) {
|
|
3412
|
-
if (remap.moved.size === 0 && remap.vacated.size === 0) return;
|
|
3413
|
-
migrateMapSubtree(fields, arrayPath, remap, (record, segments) => ({
|
|
3414
|
-
...record,
|
|
3415
|
-
path: segments
|
|
3416
|
-
}));
|
|
3417
|
-
migrateMapSubtree(
|
|
3418
|
-
userErrors,
|
|
3419
|
-
arrayPath,
|
|
3420
|
-
remap,
|
|
3421
|
-
(errors, segments) => errors.map((error) => ({ ...error, path: [...segments] }))
|
|
3422
|
-
);
|
|
3423
|
-
migrateMapSubtree(originals, arrayPath, remap, (record, segments) => ({
|
|
3424
|
-
segments,
|
|
3425
|
-
value: record.value
|
|
3426
|
-
}));
|
|
3427
|
-
migrateSetSubtree(blankPaths, arrayPath, remap);
|
|
3428
|
-
migrateSetSubtree(originalBlankPaths, arrayPath, remap);
|
|
3429
|
-
}
|
|
3430
|
-
function seedFreshElement(arrayPath, freshIndex) {
|
|
3431
|
-
const elementPath = [...arrayPath, freshIndex];
|
|
3432
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3433
|
-
diffAndApply(void 0, getAtPath(form.value, elementPath), elementPath, (patch) => {
|
|
3434
|
-
if (patch.kind !== "added") return;
|
|
3435
|
-
const { key } = paths.canonicalizePath(patch.path);
|
|
3436
|
-
if (!originals.has(key)) originals.set(key, { segments: patch.path, value: void 0 });
|
|
3437
|
-
touchFieldRecord(key, patch.path, { updatedAt: now });
|
|
3438
|
-
});
|
|
3439
|
-
}
|
|
3440
|
-
function dropSchemaErrorsAtChangedIndices(arrayPath, remap) {
|
|
3441
|
-
const changed = changedIndices(remap);
|
|
3442
|
-
if (changed.size === 0) return;
|
|
3443
|
-
const idxPos = arrayPath.length;
|
|
3444
|
-
for (const key of [...schemaErrors.keys()]) {
|
|
3445
|
-
const segs = paths.segmentsForPathKey(key);
|
|
3446
|
-
if (segs === null) continue;
|
|
3447
|
-
if (!isPathPrefix(arrayPath, segs)) continue;
|
|
3448
|
-
if (segs.length <= idxPos) continue;
|
|
3449
|
-
const idx = segs[idxPos];
|
|
3450
|
-
if (typeof idx === "number" && changed.has(idx)) schemaErrors.delete(key);
|
|
3451
|
-
}
|
|
3452
|
-
}
|
|
3453
|
-
function abortValidationAtVacatedIndices(arrayPath, remap) {
|
|
3454
|
-
if (remap.vacated.size === 0) return;
|
|
3455
|
-
const idxPos = arrayPath.length;
|
|
3456
|
-
for (const [key, entry] of [...fieldValidationState]) {
|
|
3457
|
-
const segs = paths.segmentsForPathKey(key);
|
|
3458
|
-
if (segs === null) continue;
|
|
3459
|
-
if (!isPathPrefix(arrayPath, segs)) continue;
|
|
3460
|
-
if (segs.length <= idxPos) continue;
|
|
3461
|
-
const idx = segs[idxPos];
|
|
3462
|
-
if (typeof idx !== "number" || !remap.vacated.has(idx)) continue;
|
|
3463
|
-
if (entry.timer !== null) {
|
|
3464
|
-
clearTimeout(entry.timer);
|
|
3465
|
-
} else if (!entry.settled) {
|
|
3466
|
-
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
3467
|
-
decFieldValidation(key);
|
|
3468
|
-
}
|
|
3469
|
-
entry.controller.abort();
|
|
3470
|
-
fieldValidationState.delete(key);
|
|
3471
|
-
}
|
|
3472
|
-
}
|
|
3473
|
-
const pathOrdinals = /* @__PURE__ */ new Map();
|
|
3474
|
-
let nextOrdinal = 0;
|
|
3475
|
-
function ensurePathOrdinal(key) {
|
|
3476
|
-
let ordinal = pathOrdinals.get(key);
|
|
3477
|
-
if (ordinal === void 0) {
|
|
3478
|
-
ordinal = nextOrdinal;
|
|
3479
|
-
pathOrdinals.set(key, ordinal);
|
|
3480
|
-
nextOrdinal += 1;
|
|
3481
|
-
}
|
|
3482
|
-
return ordinal;
|
|
3777
|
+
return ordinal;
|
|
3483
3778
|
}
|
|
3484
3779
|
const derivedBlankErrors = vue.computed(() => {
|
|
3485
3780
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -3507,7 +3802,9 @@ function createFormStore(options) {
|
|
|
3507
3802
|
const departAttempts = vue.ref(0);
|
|
3508
3803
|
const submissionGeneration = vue.ref(0);
|
|
3509
3804
|
const activeValidations = vue.ref(0);
|
|
3510
|
-
|
|
3805
|
+
const pathSnapshots = /* @__PURE__ */ new Map();
|
|
3806
|
+
let scheduleEpoch = 0;
|
|
3807
|
+
let lastCommittedEpoch = 0;
|
|
3511
3808
|
const hydrating = vue.ref(false);
|
|
3512
3809
|
const hydrateError = vue.ref(null);
|
|
3513
3810
|
const defaultValuesFactory = vue.ref(void 0);
|
|
@@ -3523,9 +3820,12 @@ function createFormStore(options) {
|
|
|
3523
3820
|
const pathAsyncCache = /* @__PURE__ */ new Map();
|
|
3524
3821
|
function pathHasAsyncValidation(path) {
|
|
3525
3822
|
const { key } = paths.canonicalizePath(path);
|
|
3823
|
+
return pathHasAsyncValidationByKey(key, path);
|
|
3824
|
+
}
|
|
3825
|
+
function pathHasAsyncValidationByKey(key, segments) {
|
|
3526
3826
|
const cached = pathAsyncCache.get(key);
|
|
3527
3827
|
if (cached !== void 0) return cached;
|
|
3528
|
-
const candidates = schema.getSchemasAtPath(
|
|
3828
|
+
const candidates = schema.getSchemasAtPath(segments);
|
|
3529
3829
|
const hasAsync = candidates.some((sub) => sub.needsAsyncValidation?.() === true);
|
|
3530
3830
|
pathAsyncCache.set(key, hasAsync);
|
|
3531
3831
|
return hasAsync;
|
|
@@ -3617,6 +3917,21 @@ function createFormStore(options) {
|
|
|
3617
3917
|
blurredAfterInteraction: patch.blurredAfterInteraction ?? current?.blurredAfterInteraction ?? false
|
|
3618
3918
|
});
|
|
3619
3919
|
}
|
|
3920
|
+
const arrayBookkeeping = createArrayBookkeeping({
|
|
3921
|
+
form,
|
|
3922
|
+
fields,
|
|
3923
|
+
userErrors,
|
|
3924
|
+
originals,
|
|
3925
|
+
blankPaths,
|
|
3926
|
+
originalBlankPaths,
|
|
3927
|
+
fieldValidationCounts,
|
|
3928
|
+
fieldValidationState,
|
|
3929
|
+
schemaErrors,
|
|
3930
|
+
activeValidations,
|
|
3931
|
+
arrayIdentity,
|
|
3932
|
+
touchFieldRecord,
|
|
3933
|
+
decFieldValidation
|
|
3934
|
+
});
|
|
3620
3935
|
function applyFormReplacement(next, meta) {
|
|
3621
3936
|
const prev = form.value;
|
|
3622
3937
|
if (Object.is(prev, next)) return;
|
|
@@ -3740,8 +4055,13 @@ function createFormStore(options) {
|
|
|
3740
4055
|
const pathKey = paths.canonicalizePath(path).key;
|
|
3741
4056
|
if (meta?.blank === true) {
|
|
3742
4057
|
blankPaths.add(pathKey);
|
|
3743
|
-
} else
|
|
3744
|
-
blankPaths.delete(pathKey);
|
|
4058
|
+
} else {
|
|
4059
|
+
if (blankPaths.has(pathKey)) blankPaths.delete(pathKey);
|
|
4060
|
+
if (meta?.arrayOp === void 0) {
|
|
4061
|
+
for (const existingKey of [...blankPaths]) {
|
|
4062
|
+
if (isPathKeyUnder(existingKey, path)) blankPaths.delete(existingKey);
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
3745
4065
|
}
|
|
3746
4066
|
const wasAuthoredBefore = authoredPaths.has(pathKey);
|
|
3747
4067
|
walkAuthoredFromConstraints(value, path, authoredPaths);
|
|
@@ -3765,14 +4085,14 @@ function createFormStore(options) {
|
|
|
3765
4085
|
applyFormReplacement(nextForm, meta);
|
|
3766
4086
|
if (meta?.arrayOp !== void 0) {
|
|
3767
4087
|
const remap = remapForOp(meta.arrayOp, oldArrayLength);
|
|
3768
|
-
|
|
3769
|
-
for (const freshIndex of remap.fresh) seedFreshElement(path, freshIndex);
|
|
3770
|
-
dropSchemaErrorsAtChangedIndices(path, remap);
|
|
3771
|
-
abortValidationAtVacatedIndices(path, remap);
|
|
3772
|
-
|
|
4088
|
+
arrayBookkeeping.migrateElementState(path, remap);
|
|
4089
|
+
for (const freshIndex of remap.fresh) arrayBookkeeping.seedFreshElement(path, freshIndex);
|
|
4090
|
+
arrayBookkeeping.dropSchemaErrorsAtChangedIndices(path, remap);
|
|
4091
|
+
arrayBookkeeping.abortValidationAtVacatedIndices(path, remap);
|
|
4092
|
+
variantMemory.applyArrayOp(path, meta.arrayOp);
|
|
3773
4093
|
arrayIdentity.applyOp(path, meta.arrayOp);
|
|
3774
4094
|
} else if (Array.isArray(value) && Array.isArray(currentValue)) {
|
|
3775
|
-
|
|
4095
|
+
variantMemory.clearUnderPath(path);
|
|
3776
4096
|
arrayIdentity.realign(path);
|
|
3777
4097
|
}
|
|
3778
4098
|
const effectiveModeAfterWrite = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
@@ -3797,18 +4117,12 @@ function createFormStore(options) {
|
|
|
3797
4117
|
for (const k of blankPaths) {
|
|
3798
4118
|
if (isPathKeyUnder(k, parentPath)) outgoingBlanks.push(k);
|
|
3799
4119
|
}
|
|
3800
|
-
|
|
3801
|
-
if (memoryForUnion2 === void 0) {
|
|
3802
|
-
memoryForUnion2 = /* @__PURE__ */ new Map();
|
|
3803
|
-
variantMemory.set(parentKey, memoryForUnion2);
|
|
3804
|
-
}
|
|
3805
|
-
memoryForUnion2.set(oldDiscValue, {
|
|
4120
|
+
variantMemory.recordOutgoing(parentKey, oldDiscValue, {
|
|
3806
4121
|
value: currentValue2,
|
|
3807
4122
|
blankPaths: outgoingBlanks
|
|
3808
4123
|
});
|
|
3809
4124
|
}
|
|
3810
|
-
const
|
|
3811
|
-
const restored = memoryForUnion?.get(newDiscValue);
|
|
4125
|
+
const restored = variantMemory.lookupIncoming(parentKey, newDiscValue);
|
|
3812
4126
|
if (restored !== void 0) {
|
|
3813
4127
|
baseline = restored.value;
|
|
3814
4128
|
restoredBlanks = [...restored.blankPaths];
|
|
@@ -3880,12 +4194,10 @@ function createFormStore(options) {
|
|
|
3880
4194
|
const controller = new AbortController();
|
|
3881
4195
|
const fresh = { controller, timer: null, settled: false };
|
|
3882
4196
|
fieldValidationState.set(key, fresh);
|
|
4197
|
+
const myEpoch = ++scheduleEpoch;
|
|
3883
4198
|
const run = () => {
|
|
3884
4199
|
fresh.timer = null;
|
|
3885
4200
|
if (controller.signal.aborted) return;
|
|
3886
|
-
if (effectiveMode === "blur") {
|
|
3887
|
-
lastValidatedSnapshot = { value: structuralSnapshot(form.value) };
|
|
3888
|
-
}
|
|
3889
4201
|
let activeIncremented = false;
|
|
3890
4202
|
try {
|
|
3891
4203
|
activeValidations.value += 1;
|
|
@@ -3897,11 +4209,25 @@ function createFormStore(options) {
|
|
|
3897
4209
|
}
|
|
3898
4210
|
throw err;
|
|
3899
4211
|
}
|
|
3900
|
-
|
|
4212
|
+
const subtreeScope = path.length > 0 && schema.hasContainerOrRootRefine?.() === false;
|
|
4213
|
+
const scopePath = subtreeScope ? path : void 0;
|
|
4214
|
+
const dataAtScope = subtreeScope ? getAtPath(form.value, path) : form.value;
|
|
4215
|
+
const scopeKey = subtreeScope ? paths.canonicalizePath(path).key : paths.ROOT_PATH_KEY;
|
|
4216
|
+
void Promise.resolve().then(() => schema.validateAtPath(dataAtScope, scopePath)).then((response) => {
|
|
3901
4217
|
if (controller.signal.aborted) return;
|
|
4218
|
+
if (myEpoch <= lastCommittedEpoch) return;
|
|
4219
|
+
lastCommittedEpoch = myEpoch;
|
|
4220
|
+
if (effectiveMode === "blur") {
|
|
4221
|
+
const snapshotSource = scopePath !== void 0 ? getAtPath(form.value, scopePath) : form.value;
|
|
4222
|
+
pathSnapshots.set(scopeKey, structuralSnapshot(snapshotSource));
|
|
4223
|
+
}
|
|
3902
4224
|
const errors = response.success ? [] : response.errors;
|
|
3903
4225
|
const filtered = filterAuthoredErrors(errors);
|
|
3904
|
-
|
|
4226
|
+
const restamped = subtreeScope ? filtered.map((err) => ({
|
|
4227
|
+
...err,
|
|
4228
|
+
path: [...path, ...err.path]
|
|
4229
|
+
})) : filtered;
|
|
4230
|
+
applySchemaErrorsForSubtree(scopePath ?? [], restamped);
|
|
3905
4231
|
}).catch(() => {
|
|
3906
4232
|
}).finally(() => {
|
|
3907
4233
|
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
@@ -4144,11 +4470,24 @@ function createFormStore(options) {
|
|
|
4144
4470
|
const focusMode = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
4145
4471
|
if (!focused && focusMode === "blur") {
|
|
4146
4472
|
const firstInteractiveBlur = current?.interacted === true && current.blurredAfterInteraction !== true;
|
|
4147
|
-
|
|
4473
|
+
let snapshot = void 0;
|
|
4474
|
+
let snapshotScopeLength = 0;
|
|
4475
|
+
for (let i = path.length; i >= 0; i--) {
|
|
4476
|
+
const ancestorKey = paths.canonicalizePath(path.slice(0, i)).key;
|
|
4477
|
+
const entry = pathSnapshots.get(ancestorKey);
|
|
4478
|
+
if (entry !== void 0) {
|
|
4479
|
+
snapshot = entry;
|
|
4480
|
+
snapshotScopeLength = i;
|
|
4481
|
+
break;
|
|
4482
|
+
}
|
|
4483
|
+
}
|
|
4148
4484
|
let changed = true;
|
|
4149
|
-
if (!firstInteractiveBlur && snapshot !==
|
|
4485
|
+
if (!firstInteractiveBlur && snapshot !== void 0) {
|
|
4486
|
+
const relPath = path.slice(snapshotScopeLength);
|
|
4487
|
+
const snapshotSubtree = getAtPath(snapshot, relPath);
|
|
4488
|
+
const liveSubtree = getAtPath(form.value, path);
|
|
4150
4489
|
changed = false;
|
|
4151
|
-
diffAndApply(
|
|
4490
|
+
diffAndApply(snapshotSubtree, liveSubtree, path, () => {
|
|
4152
4491
|
changed = true;
|
|
4153
4492
|
});
|
|
4154
4493
|
}
|
|
@@ -4160,10 +4499,6 @@ function createFormStore(options) {
|
|
|
4160
4499
|
}
|
|
4161
4500
|
}
|
|
4162
4501
|
}
|
|
4163
|
-
function markTouched(path) {
|
|
4164
|
-
const { key } = paths.canonicalizePath(path);
|
|
4165
|
-
touchFieldRecord(key, path, { touched: true });
|
|
4166
|
-
}
|
|
4167
4502
|
function markInteracted(path) {
|
|
4168
4503
|
const { key } = paths.canonicalizePath(path);
|
|
4169
4504
|
if (fields.get(key)?.interacted === true) return;
|
|
@@ -4173,7 +4508,7 @@ function createFormStore(options) {
|
|
|
4173
4508
|
const formValue = form.value;
|
|
4174
4509
|
let touchedAny = false;
|
|
4175
4510
|
for (const [, entry] of originals) {
|
|
4176
|
-
if (!isPathPrefix(segments, entry.segments)) continue;
|
|
4511
|
+
if (!paths.isPathPrefix(segments, entry.segments)) continue;
|
|
4177
4512
|
if (!hasAtPath(formValue, entry.segments)) continue;
|
|
4178
4513
|
touchedAny = true;
|
|
4179
4514
|
const leafKey = paths.canonicalizePath(entry.segments).key;
|
|
@@ -4187,9 +4522,6 @@ function createFormStore(options) {
|
|
|
4187
4522
|
);
|
|
4188
4523
|
}
|
|
4189
4524
|
}
|
|
4190
|
-
function clear(path) {
|
|
4191
|
-
return setValueAtPath(path, schema.getEmptyValueAtPath(path));
|
|
4192
|
-
}
|
|
4193
4525
|
function rehydrate() {
|
|
4194
4526
|
const factory = defaultValuesFactory.value;
|
|
4195
4527
|
if (factory === void 0) {
|
|
@@ -4336,6 +4668,9 @@ function createFormStore(options) {
|
|
|
4336
4668
|
submitError.value = null;
|
|
4337
4669
|
departAttempts.value = 0;
|
|
4338
4670
|
cancelFieldValidation();
|
|
4671
|
+
pathSnapshots.clear();
|
|
4672
|
+
scheduleEpoch = 0;
|
|
4673
|
+
lastCommittedEpoch = 0;
|
|
4339
4674
|
variantMemory.clear();
|
|
4340
4675
|
for (const listener of resetListeners) {
|
|
4341
4676
|
try {
|
|
@@ -4347,13 +4682,7 @@ function createFormStore(options) {
|
|
|
4347
4682
|
}
|
|
4348
4683
|
function resetField(path) {
|
|
4349
4684
|
const { key: targetKey, segments: targetSegments } = paths.canonicalizePath(path);
|
|
4350
|
-
|
|
4351
|
-
const memSegments = paths.segmentsForPathKey(memKey);
|
|
4352
|
-
if (memSegments === null) continue;
|
|
4353
|
-
if (isPathPrefix(targetSegments, memSegments)) {
|
|
4354
|
-
variantMemory.delete(memKey);
|
|
4355
|
-
}
|
|
4356
|
-
}
|
|
4685
|
+
variantMemory.clearUnderPath(targetSegments);
|
|
4357
4686
|
const leafEntry = originals.get(targetKey);
|
|
4358
4687
|
if (leafEntry !== void 0) {
|
|
4359
4688
|
const wrote = setValueAtPath(targetSegments, leafEntry.value);
|
|
@@ -4367,7 +4696,7 @@ function createFormStore(options) {
|
|
|
4367
4696
|
let anyMatch = false;
|
|
4368
4697
|
for (const [, entry] of originals) {
|
|
4369
4698
|
const leafSegments = entry.segments;
|
|
4370
|
-
if (!isPathPrefix(targetSegments, leafSegments)) continue;
|
|
4699
|
+
if (!paths.isPathPrefix(targetSegments, leafSegments)) continue;
|
|
4371
4700
|
if (leafSegments.length === targetSegments.length) continue;
|
|
4372
4701
|
anyMatch = true;
|
|
4373
4702
|
const relative = leafSegments.slice(targetSegments.length);
|
|
@@ -4388,14 +4717,14 @@ function createFormStore(options) {
|
|
|
4388
4717
|
deleteErrorsUnderPrefix(schemaErrors, targetSegments);
|
|
4389
4718
|
deleteErrorsUnderPrefix(userErrors, targetSegments);
|
|
4390
4719
|
for (const [fieldKey, record] of Array.from(fields.entries())) {
|
|
4391
|
-
if (isPathPrefix(targetSegments, record.path)) clearFieldRecordFlags(fieldKey);
|
|
4720
|
+
if (paths.isPathPrefix(targetSegments, record.path)) clearFieldRecordFlags(fieldKey);
|
|
4392
4721
|
}
|
|
4393
4722
|
}
|
|
4394
4723
|
function deleteErrorsUnderPrefix(map, prefix) {
|
|
4395
4724
|
for (const [errorKey, errs] of Array.from(map.entries())) {
|
|
4396
4725
|
const first = errs[0];
|
|
4397
4726
|
if (first === void 0) continue;
|
|
4398
|
-
if (isPathPrefix(prefix, first.path)) {
|
|
4727
|
+
if (paths.isPathPrefix(prefix, first.path)) {
|
|
4399
4728
|
map.delete(errorKey);
|
|
4400
4729
|
}
|
|
4401
4730
|
}
|
|
@@ -4414,15 +4743,11 @@ function createFormStore(options) {
|
|
|
4414
4743
|
blurredAfterInteraction: false
|
|
4415
4744
|
});
|
|
4416
4745
|
}
|
|
4417
|
-
function isPathPrefix(prefix, candidate) {
|
|
4418
|
-
if (prefix.length > candidate.length) return false;
|
|
4419
|
-
for (let i = 0; i < prefix.length; i++) {
|
|
4420
|
-
if (prefix[i] !== candidate[i]) return false;
|
|
4421
|
-
}
|
|
4422
|
-
return true;
|
|
4423
|
-
}
|
|
4424
4746
|
function isPristineAtPath(path) {
|
|
4425
4747
|
const { key, segments } = paths.canonicalizePath(path);
|
|
4748
|
+
return isPristineAtPathByKey(key, segments);
|
|
4749
|
+
}
|
|
4750
|
+
function isPristineAtPathByKey(key, segments) {
|
|
4426
4751
|
if (blankPaths.has(key) !== originalBlankPaths.has(key)) return false;
|
|
4427
4752
|
const entry = originals.get(key);
|
|
4428
4753
|
if (entry === void 0) return true;
|
|
@@ -4484,6 +4809,7 @@ function createFormStore(options) {
|
|
|
4484
4809
|
hydrating,
|
|
4485
4810
|
hydrateError,
|
|
4486
4811
|
defaultValuesFactory,
|
|
4812
|
+
hasSsrPrefetch: ssrPrefetch !== void 0,
|
|
4487
4813
|
defaultsResolved,
|
|
4488
4814
|
activated,
|
|
4489
4815
|
activationPromise,
|
|
@@ -4493,6 +4819,7 @@ function createFormStore(options) {
|
|
|
4493
4819
|
activeValidations,
|
|
4494
4820
|
firstValidationDone,
|
|
4495
4821
|
pathHasAsyncValidation,
|
|
4822
|
+
pathHasAsyncValidationByKey,
|
|
4496
4823
|
fieldValidationCounts,
|
|
4497
4824
|
applyFormReplacement,
|
|
4498
4825
|
setValueAtPath,
|
|
@@ -4500,7 +4827,6 @@ function createFormStore(options) {
|
|
|
4500
4827
|
arrayElementKey,
|
|
4501
4828
|
reset,
|
|
4502
4829
|
resetField,
|
|
4503
|
-
clear,
|
|
4504
4830
|
setSchemaErrorsForPath,
|
|
4505
4831
|
setAllSchemaErrors,
|
|
4506
4832
|
clearSchemaErrors,
|
|
@@ -4513,11 +4839,11 @@ function createFormStore(options) {
|
|
|
4513
4839
|
registerElement,
|
|
4514
4840
|
deregisterElement,
|
|
4515
4841
|
markFocused,
|
|
4516
|
-
markTouched,
|
|
4517
4842
|
markInteracted,
|
|
4518
4843
|
touchAtPath,
|
|
4519
4844
|
markConnectedOptimistically,
|
|
4520
4845
|
isPristineAtPath,
|
|
4846
|
+
isPristineAtPathByKey,
|
|
4521
4847
|
hasStructuralChangeUnder,
|
|
4522
4848
|
getFieldRecord,
|
|
4523
4849
|
getOriginalAtPath,
|
|
@@ -4534,7 +4860,6 @@ function createFormStore(options) {
|
|
|
4534
4860
|
modules,
|
|
4535
4861
|
persistOptIns,
|
|
4536
4862
|
isSensitivePath: resolvedIsSensitivePath,
|
|
4537
|
-
segmentMatchesSensitive: resolvedSegmentMatchesSensitive,
|
|
4538
4863
|
noSyncPaths,
|
|
4539
4864
|
incrementNoSyncOptOut,
|
|
4540
4865
|
decrementNoSyncOptOut,
|
|
@@ -4735,931 +5060,988 @@ function createHistoryModule(state, config) {
|
|
|
4735
5060
|
};
|
|
4736
5061
|
}
|
|
4737
5062
|
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
function isDangerousSegment(s) {
|
|
4748
|
-
return s === "__proto__" || s === "constructor" || s === "prototype";
|
|
4749
|
-
}
|
|
4750
|
-
function pathContainsDangerousSegment(path) {
|
|
4751
|
-
for (let i = 0; i < path.length; i++) {
|
|
4752
|
-
if (isDangerousSegment(path[i])) return true;
|
|
4753
|
-
}
|
|
4754
|
-
return false;
|
|
4755
|
-
}
|
|
4756
|
-
function isInboundShapeAcceptable(schema, path, value) {
|
|
4757
|
-
if (isFileLikeValue(value)) return false;
|
|
4758
|
-
let kind;
|
|
4759
|
-
if (Array.isArray(value)) {
|
|
4760
|
-
kind = "array";
|
|
4761
|
-
} else if (value !== null && typeof value === "object" && isPlainRecord(value)) {
|
|
4762
|
-
kind = "object";
|
|
4763
|
-
} else {
|
|
4764
|
-
kind = slimKindOf(value);
|
|
4765
|
-
}
|
|
4766
|
-
const accepted = schema.getSlimPrimitiveTypesAtPath(path);
|
|
4767
|
-
if (!accepted.has(kind)) return false;
|
|
4768
|
-
if (Array.isArray(value)) {
|
|
4769
|
-
for (let i = 0; i < value.length; i++) {
|
|
4770
|
-
if (!isInboundShapeAcceptable(schema, [...path, i], value[i])) return false;
|
|
5063
|
+
function wirePersistence(state, config) {
|
|
5064
|
+
let fingerprint;
|
|
5065
|
+
try {
|
|
5066
|
+
fingerprint = hashStableString(state.schema.fingerprint());
|
|
5067
|
+
} catch (err) {
|
|
5068
|
+
if (paths.__DEV__) {
|
|
5069
|
+
console.warn(
|
|
5070
|
+
`[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.`
|
|
5071
|
+
);
|
|
4771
5072
|
}
|
|
4772
|
-
|
|
5073
|
+
fingerprint = "unfingerprinted";
|
|
4773
5074
|
}
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
5075
|
+
const base = resolveStorageKeyBase(config, state.formKey);
|
|
5076
|
+
const key = `${base}:${fingerprint}`;
|
|
5077
|
+
const debounceMs = normalizeNumericOption({
|
|
5078
|
+
value: config.debounceMs ?? DEFAULT_PERSISTENCE_DEBOUNCE_MS,
|
|
5079
|
+
source: "useForm.persist.debounceMs",
|
|
5080
|
+
allowInfinity: false,
|
|
5081
|
+
min: 0,
|
|
5082
|
+
defaultValue: DEFAULT_PERSISTENCE_DEBOUNCE_MS
|
|
5083
|
+
});
|
|
5084
|
+
const include = config.include ?? "form";
|
|
5085
|
+
const clearOnSubmitSuccess = config.clearOnSubmitSuccess ?? true;
|
|
5086
|
+
const adapterPromise = getStorageAdapter(config.storage);
|
|
5087
|
+
let disposed = false;
|
|
5088
|
+
const isDisposed = () => disposed;
|
|
5089
|
+
let inFlightFinalFlush = null;
|
|
5090
|
+
let pendingOptedInPaths = null;
|
|
5091
|
+
const writer = createDebouncedWriter(async () => {
|
|
5092
|
+
const optedInPaths = pendingOptedInPaths ?? new Set(state.persistOptIns.optedInPaths());
|
|
5093
|
+
pendingOptedInPaths = null;
|
|
5094
|
+
const adapter = await adapterPromise;
|
|
5095
|
+
if (isDisposed()) return;
|
|
5096
|
+
if (optedInPaths.size === 0) {
|
|
5097
|
+
await adapter.removeItem(key);
|
|
5098
|
+
return;
|
|
4777
5099
|
}
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
}
|
|
4790
|
-
function snapshotForm(form) {
|
|
4791
|
-
return structuralSnapshot(form);
|
|
4792
|
-
}
|
|
4793
|
-
function stripSensitivePathsDeep(value, pathSoFar, isSensitivePath) {
|
|
4794
|
-
if (isFileLikeValue(value)) return void 0;
|
|
4795
|
-
if (value === null || typeof value !== "object") return value;
|
|
4796
|
-
if (Array.isArray(value)) {
|
|
4797
|
-
return value.map((item, i) => stripSensitivePathsDeep(item, [...pathSoFar, i], isSensitivePath));
|
|
4798
|
-
}
|
|
4799
|
-
const proto = Object.getPrototypeOf(value);
|
|
4800
|
-
if (proto !== Object.prototype && proto !== null) return value;
|
|
4801
|
-
const out = {};
|
|
4802
|
-
const src = value;
|
|
4803
|
-
for (const key of Object.keys(src)) {
|
|
4804
|
-
const childPath = [...pathSoFar, key];
|
|
4805
|
-
if (isSensitivePath(childPath)) {
|
|
4806
|
-
out[key] = void 0;
|
|
4807
|
-
continue;
|
|
5100
|
+
const rawForm = vue.toRaw(state.form.value);
|
|
5101
|
+
const filteredForm = stripUnacknowledgedSensitiveLeaves(
|
|
5102
|
+
pluckPaths(rawForm, optedInPaths),
|
|
5103
|
+
optedInPaths,
|
|
5104
|
+
state.isSensitivePath
|
|
5105
|
+
);
|
|
5106
|
+
const filteredSchemaErrors = filterErrorsByPaths(state.schemaErrors, optedInPaths);
|
|
5107
|
+
const filteredUserErrors = filterErrorsByPaths(state.userErrors, optedInPaths);
|
|
5108
|
+
const filteredTransientEmpty = /* @__PURE__ */ new Set();
|
|
5109
|
+
for (const tk of state.blankPaths) {
|
|
5110
|
+
if (optedInPaths.has(tk)) filteredTransientEmpty.add(tk);
|
|
4808
5111
|
}
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
5112
|
+
const payload = buildPersistedPayload(
|
|
5113
|
+
filteredForm,
|
|
5114
|
+
include,
|
|
5115
|
+
filteredSchemaErrors,
|
|
5116
|
+
filteredUserErrors,
|
|
5117
|
+
filteredTransientEmpty
|
|
5118
|
+
);
|
|
5119
|
+
await adapter.setItem(key, payload);
|
|
5120
|
+
}, debounceMs);
|
|
5121
|
+
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
5122
|
+
if (isDisposed() || inFlightFinalFlush !== null) return;
|
|
5123
|
+
if (meta?.crossTab === true) return;
|
|
5124
|
+
if (meta?.persist !== true) return;
|
|
5125
|
+
pendingOptedInPaths = new Set(state.persistOptIns.optedInPaths());
|
|
5126
|
+
writer.schedule();
|
|
5127
|
+
});
|
|
5128
|
+
const unsubscribeSuccess = clearOnSubmitSuccess ? state.onSubmitSuccess(() => {
|
|
5129
|
+
if (isDisposed()) return;
|
|
5130
|
+
void (async () => {
|
|
5131
|
+
await writer.flush();
|
|
5132
|
+
if (isDisposed()) return;
|
|
5133
|
+
const adapter = await adapterPromise;
|
|
5134
|
+
if (isDisposed()) return;
|
|
5135
|
+
await adapter.removeItem(key);
|
|
5136
|
+
})();
|
|
5137
|
+
}) : () => void 0;
|
|
5138
|
+
const handlePageHide = () => {
|
|
5139
|
+
if (isDisposed()) return;
|
|
5140
|
+
void writer.flush();
|
|
5141
|
+
};
|
|
5142
|
+
if (typeof window !== "undefined") {
|
|
5143
|
+
window.addEventListener("pagehide", handlePageHide);
|
|
4831
5144
|
}
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
} catch {
|
|
4837
|
-
return `atta-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;
|
|
4838
|
-
}
|
|
4839
|
-
}
|
|
4840
|
-
function createMultiTabSyncModule(state, channelName, options) {
|
|
4841
|
-
if (typeof BroadcastChannel === "undefined") {
|
|
4842
|
-
return {
|
|
4843
|
-
dispose: () => void 0,
|
|
4844
|
-
lifecycle: () => "established",
|
|
4845
|
-
senderId: "",
|
|
4846
|
-
channelName
|
|
4847
|
-
};
|
|
4848
|
-
}
|
|
4849
|
-
let channel;
|
|
4850
|
-
try {
|
|
4851
|
-
channel = new BroadcastChannel(channelName);
|
|
4852
|
-
} catch {
|
|
4853
|
-
return {
|
|
4854
|
-
dispose: () => void 0,
|
|
4855
|
-
lifecycle: () => "established",
|
|
4856
|
-
senderId: "",
|
|
4857
|
-
channelName
|
|
4858
|
-
};
|
|
4859
|
-
}
|
|
4860
|
-
const senderId = generateSenderId();
|
|
4861
|
-
let lifecycle = "joining";
|
|
4862
|
-
let disposed = false;
|
|
4863
|
-
const peerIds = /* @__PURE__ */ new Set();
|
|
4864
|
-
let joinCollectionTimer = null;
|
|
4865
|
-
let snapshotTimeoutTimer = null;
|
|
4866
|
-
let leaderAttempts = 0;
|
|
4867
|
-
let prior = {
|
|
4868
|
-
form: snapshotForm(state.form.value),
|
|
4869
|
-
blankPathsSnapshot: [...state.blankPaths]
|
|
4870
|
-
};
|
|
4871
|
-
function safePost(msg) {
|
|
4872
|
-
if (disposed) return;
|
|
5145
|
+
void (async () => {
|
|
5146
|
+
const adapter = await adapterPromise;
|
|
5147
|
+
if (isDisposed()) return;
|
|
5148
|
+
void cleanupOrphanKeys(adapter, base, key);
|
|
4873
5149
|
try {
|
|
4874
|
-
|
|
5150
|
+
const raw = await adapter.getItem(key);
|
|
5151
|
+
const payload = readPersistedPayload(raw);
|
|
5152
|
+
if (payload === null) {
|
|
5153
|
+
if (raw !== null && raw !== void 0) {
|
|
5154
|
+
await adapter.removeItem(key);
|
|
5155
|
+
}
|
|
5156
|
+
return;
|
|
5157
|
+
}
|
|
5158
|
+
if (isDisposed()) return;
|
|
5159
|
+
const merged = mergeSparseHydration(
|
|
5160
|
+
vue.toRaw(state.form.value),
|
|
5161
|
+
payload.data.form,
|
|
5162
|
+
state.schema
|
|
5163
|
+
);
|
|
5164
|
+
state.applyFormReplacement(merged, { hydration: true });
|
|
5165
|
+
const persistedLeafPaths = collectPersistedLeafPaths(payload.data.form);
|
|
5166
|
+
for (const k of persistedLeafPaths) {
|
|
5167
|
+
state.blankPaths.delete(k);
|
|
5168
|
+
state.originalBlankPaths.delete(k);
|
|
5169
|
+
}
|
|
5170
|
+
for (const k of payload.data.blankPaths ?? []) {
|
|
5171
|
+
state.blankPaths.add(k);
|
|
5172
|
+
state.originalBlankPaths.add(k);
|
|
5173
|
+
}
|
|
5174
|
+
if (include === "form+errors") {
|
|
5175
|
+
if (payload.data.schemaErrors !== void 0) {
|
|
5176
|
+
const flat = payload.data.schemaErrors.flatMap(([, errs]) => errs);
|
|
5177
|
+
state.setAllSchemaErrors(flat);
|
|
5178
|
+
}
|
|
5179
|
+
if (payload.data.userErrors !== void 0) {
|
|
5180
|
+
const flat = payload.data.userErrors.flatMap(([, errs]) => errs);
|
|
5181
|
+
state.setAllUserErrors(flat);
|
|
5182
|
+
}
|
|
5183
|
+
}
|
|
5184
|
+
state.scheduleFieldValidation(
|
|
5185
|
+
[],
|
|
5186
|
+
true
|
|
5187
|
+
/* immediate */
|
|
5188
|
+
);
|
|
4875
5189
|
} catch {
|
|
4876
5190
|
}
|
|
4877
|
-
}
|
|
4878
|
-
function
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
const
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
if ("value" in p && isFileLikeValue(p.value)) continue;
|
|
4900
|
-
safePatches.push(p);
|
|
4901
|
-
}
|
|
4902
|
-
const { added, removed } = diffBlankPaths(prior.blankPathsSnapshot, state.blankPaths);
|
|
4903
|
-
if (safePatches.length === 0 && added.length === 0 && removed.length === 0) {
|
|
4904
|
-
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
4905
|
-
return;
|
|
5191
|
+
})();
|
|
5192
|
+
async function writePathImmediately(path) {
|
|
5193
|
+
if (isDisposed()) return;
|
|
5194
|
+
await writer.flush();
|
|
5195
|
+
if (isDisposed()) return;
|
|
5196
|
+
const adapter = await adapterPromise;
|
|
5197
|
+
if (isDisposed()) return;
|
|
5198
|
+
const raw = await adapter.getItem(key);
|
|
5199
|
+
const existing = readPersistedPayload(raw);
|
|
5200
|
+
const baseForm = existing?.data.form ?? /* @__PURE__ */ Object.create(null);
|
|
5201
|
+
const value = getAtPath(vue.toRaw(state.form.value), path);
|
|
5202
|
+
const nextForm = setAtPath(baseForm, path, value);
|
|
5203
|
+
const { key: pathKey } = paths.canonicalizePath(path);
|
|
5204
|
+
const transientSet = new Set(
|
|
5205
|
+
(existing?.data.blankPaths ?? []).filter(
|
|
5206
|
+
(k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
|
|
5207
|
+
)
|
|
5208
|
+
);
|
|
5209
|
+
for (const liveKey of state.blankPaths) {
|
|
5210
|
+
if (liveKey === pathKey || isDescendantPathKey(liveKey, pathKey)) {
|
|
5211
|
+
transientSet.add(liveKey);
|
|
5212
|
+
}
|
|
4906
5213
|
}
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
blankPathsAdded: added,
|
|
4913
|
-
blankPathsRemoved: removed
|
|
4914
|
-
});
|
|
4915
|
-
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
4916
|
-
}
|
|
4917
|
-
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
4918
|
-
if (disposed) return;
|
|
4919
|
-
if (lifecycle !== "established") return;
|
|
4920
|
-
if (meta?.crossTab === true) return;
|
|
4921
|
-
if (meta?.hydration === true) {
|
|
4922
|
-
refreshPrior();
|
|
5214
|
+
if (include === "form") {
|
|
5215
|
+
await adapter.setItem(
|
|
5216
|
+
key,
|
|
5217
|
+
buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
|
|
5218
|
+
);
|
|
4923
5219
|
return;
|
|
4924
5220
|
}
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
state.
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
function handlePatches(msg) {
|
|
4934
|
-
if (lifecycle !== "established") return;
|
|
4935
|
-
const safePatches = [];
|
|
4936
|
-
for (const p of msg.formPatches) {
|
|
4937
|
-
if (!Array.isArray(p.path)) continue;
|
|
4938
|
-
if (isPathLocallySuppressed(p.path)) continue;
|
|
4939
|
-
if ("value" in p && !isInboundShapeAcceptable(state.schema, p.path, p.value)) {
|
|
4940
|
-
continue;
|
|
4941
|
-
}
|
|
4942
|
-
safePatches.push(p);
|
|
4943
|
-
}
|
|
4944
|
-
const safeBlankAdded = [];
|
|
4945
|
-
for (const k of msg.blankPathsAdded) {
|
|
4946
|
-
const segs = paths.canonicalizePath(k).segments;
|
|
4947
|
-
if (isPathLocallySuppressed(segs)) continue;
|
|
4948
|
-
safeBlankAdded.push(k);
|
|
5221
|
+
const schemaMap = new Map(existing?.data.schemaErrors ?? []);
|
|
5222
|
+
const userMap = new Map(existing?.data.userErrors ?? []);
|
|
5223
|
+
const currentSchema = state.schemaErrors.get(pathKey);
|
|
5224
|
+
const currentUser = state.userErrors.get(pathKey);
|
|
5225
|
+
if (currentSchema !== void 0 && currentSchema.length > 0) {
|
|
5226
|
+
schemaMap.set(pathKey, [...currentSchema]);
|
|
5227
|
+
} else {
|
|
5228
|
+
schemaMap.delete(pathKey);
|
|
4949
5229
|
}
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
safeBlankRemoved.push(k);
|
|
5230
|
+
if (currentUser !== void 0 && currentUser.length > 0) {
|
|
5231
|
+
userMap.set(pathKey, [...currentUser]);
|
|
5232
|
+
} else {
|
|
5233
|
+
userMap.delete(pathKey);
|
|
4955
5234
|
}
|
|
4956
|
-
|
|
5235
|
+
await adapter.setItem(
|
|
5236
|
+
key,
|
|
5237
|
+
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5238
|
+
);
|
|
5239
|
+
}
|
|
5240
|
+
async function clearPersistedDraft(path) {
|
|
5241
|
+
if (isDisposed()) return;
|
|
5242
|
+
await writer.flush();
|
|
5243
|
+
if (isDisposed()) return;
|
|
5244
|
+
const adapter = await adapterPromise;
|
|
5245
|
+
if (isDisposed()) return;
|
|
5246
|
+
if (path === void 0) {
|
|
5247
|
+
await adapter.removeItem(key);
|
|
4957
5248
|
return;
|
|
4958
5249
|
}
|
|
4959
|
-
const
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
}
|
|
4967
|
-
} catch {
|
|
4968
|
-
}
|
|
4969
|
-
const nextBlankPaths = new Set(state.blankPaths);
|
|
4970
|
-
for (const k of safeBlankRemoved) nextBlankPaths.delete(k);
|
|
4971
|
-
for (const k of safeBlankAdded) nextBlankPaths.add(k);
|
|
4972
|
-
applyIncomingForm(candidate, [...nextBlankPaths]);
|
|
4973
|
-
}
|
|
4974
|
-
function handleSnapshot(msg) {
|
|
4975
|
-
if (lifecycle !== "joining") return;
|
|
4976
|
-
if (!isInboundShapeAcceptable(state.schema, [], msg.form)) return;
|
|
4977
|
-
if (snapshotTimeoutTimer !== null) {
|
|
4978
|
-
clearTimeout(snapshotTimeoutTimer);
|
|
4979
|
-
snapshotTimeoutTimer = null;
|
|
5250
|
+
const raw = await adapter.getItem(key);
|
|
5251
|
+
const existing = readPersistedPayload(raw);
|
|
5252
|
+
if (existing === null) return;
|
|
5253
|
+
const nextForm = deleteAtPath(existing.data.form, path);
|
|
5254
|
+
if (isEmptyContainer(nextForm)) {
|
|
5255
|
+
await adapter.removeItem(key);
|
|
5256
|
+
return;
|
|
4980
5257
|
}
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
5258
|
+
const { key: pathKey } = paths.canonicalizePath(path);
|
|
5259
|
+
const transientSet = new Set(
|
|
5260
|
+
(existing.data.blankPaths ?? []).filter(
|
|
5261
|
+
(k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
|
|
5262
|
+
)
|
|
5263
|
+
);
|
|
5264
|
+
if (include === "form") {
|
|
5265
|
+
await adapter.setItem(
|
|
5266
|
+
key,
|
|
5267
|
+
buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
|
|
5268
|
+
);
|
|
5269
|
+
return;
|
|
4984
5270
|
}
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
5271
|
+
const schemaErrors = (existing.data.schemaErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5272
|
+
const userErrors = (existing.data.userErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5273
|
+
const schemaMap = new Map(schemaErrors.map(([k, v]) => [k, [...v]]));
|
|
5274
|
+
const userMap = new Map(userErrors.map(([k, v]) => [k, [...v]]));
|
|
5275
|
+
await adapter.setItem(
|
|
5276
|
+
key,
|
|
5277
|
+
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5278
|
+
);
|
|
4988
5279
|
}
|
|
4989
|
-
function
|
|
4990
|
-
|
|
5280
|
+
function awaitPendingWrites() {
|
|
5281
|
+
if (inFlightFinalFlush !== null) return inFlightFinalFlush;
|
|
5282
|
+
if (isDisposed()) return Promise.resolve();
|
|
5283
|
+
return writer.flush().catch(() => void 0);
|
|
4991
5284
|
}
|
|
4992
|
-
function
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
form: scrubbedForm,
|
|
4999
|
-
blankPaths: [...state.blankPaths]
|
|
5000
|
-
});
|
|
5001
|
-
}
|
|
5002
|
-
channel.onmessage = (event) => {
|
|
5003
|
-
if (disposed) return;
|
|
5004
|
-
const data = event.data;
|
|
5005
|
-
if (!isValidSyncMessage(data)) return;
|
|
5006
|
-
const msg = data;
|
|
5007
|
-
if (msg.senderId === senderId) return;
|
|
5008
|
-
switch (msg.kind) {
|
|
5009
|
-
case "hello":
|
|
5010
|
-
if (lifecycle !== "established") return;
|
|
5011
|
-
respondToHello();
|
|
5012
|
-
break;
|
|
5013
|
-
case "announce":
|
|
5014
|
-
if (lifecycle === "joining") peerIds.add(msg.senderId);
|
|
5015
|
-
break;
|
|
5016
|
-
case "requestSnapshot":
|
|
5017
|
-
if (lifecycle !== "established") return;
|
|
5018
|
-
if (msg.targetId !== senderId) return;
|
|
5019
|
-
respondToSnapshotRequest();
|
|
5020
|
-
break;
|
|
5021
|
-
case "snapshot":
|
|
5022
|
-
handleSnapshot(msg);
|
|
5023
|
-
break;
|
|
5024
|
-
case "patches":
|
|
5025
|
-
handlePatches(msg);
|
|
5026
|
-
break;
|
|
5027
|
-
}
|
|
5028
|
-
};
|
|
5029
|
-
function electLeaderAndRequest() {
|
|
5030
|
-
if (disposed) return;
|
|
5031
|
-
if (peerIds.size === 0) {
|
|
5032
|
-
lifecycle = "established";
|
|
5033
|
-
refreshPrior();
|
|
5034
|
-
return;
|
|
5285
|
+
function dispose() {
|
|
5286
|
+
if (isDisposed() || inFlightFinalFlush !== null) return;
|
|
5287
|
+
unsubscribeChange();
|
|
5288
|
+
unsubscribeSuccess();
|
|
5289
|
+
if (typeof window !== "undefined") {
|
|
5290
|
+
window.removeEventListener("pagehide", handlePageHide);
|
|
5035
5291
|
}
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
leaderAttempts++;
|
|
5040
|
-
safePost({
|
|
5041
|
-
v: PROTOCOL_VERSION,
|
|
5042
|
-
kind: "requestSnapshot",
|
|
5043
|
-
senderId,
|
|
5044
|
-
targetId: leaderId
|
|
5292
|
+
inFlightFinalFlush = writer.flush().catch(() => void 0).finally(() => {
|
|
5293
|
+
disposed = true;
|
|
5294
|
+
inFlightFinalFlush = null;
|
|
5045
5295
|
});
|
|
5046
|
-
snapshotTimeoutTimer = setTimeout(() => {
|
|
5047
|
-
snapshotTimeoutTimer = null;
|
|
5048
|
-
if (disposed) return;
|
|
5049
|
-
if (lifecycle === "established") return;
|
|
5050
|
-
if (leaderAttempts >= MAX_LEADER_ATTEMPTS || peerIds.size === 0) {
|
|
5051
|
-
lifecycle = "established";
|
|
5052
|
-
refreshPrior();
|
|
5053
|
-
return;
|
|
5054
|
-
}
|
|
5055
|
-
electLeaderAndRequest();
|
|
5056
|
-
}, SNAPSHOT_TIMEOUT_MS);
|
|
5057
|
-
}
|
|
5058
|
-
function joinFlow() {
|
|
5059
|
-
safePost({ v: PROTOCOL_VERSION, kind: "hello", senderId });
|
|
5060
|
-
joinCollectionTimer = setTimeout(() => {
|
|
5061
|
-
joinCollectionTimer = null;
|
|
5062
|
-
if (disposed) return;
|
|
5063
|
-
if (lifecycle === "established") return;
|
|
5064
|
-
electLeaderAndRequest();
|
|
5065
|
-
}, JOIN_COLLECTION_WINDOW_MS);
|
|
5066
5296
|
}
|
|
5067
|
-
joinFlow();
|
|
5068
5297
|
return {
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
joinCollectionTimer = null;
|
|
5075
|
-
}
|
|
5076
|
-
if (snapshotTimeoutTimer !== null) {
|
|
5077
|
-
clearTimeout(snapshotTimeoutTimer);
|
|
5078
|
-
snapshotTimeoutTimer = null;
|
|
5079
|
-
}
|
|
5080
|
-
unsubscribeChange();
|
|
5081
|
-
try {
|
|
5082
|
-
channel.close();
|
|
5083
|
-
} catch {
|
|
5084
|
-
}
|
|
5085
|
-
},
|
|
5086
|
-
lifecycle: () => lifecycle,
|
|
5087
|
-
senderId,
|
|
5088
|
-
channelName
|
|
5298
|
+
wiredConfig: config,
|
|
5299
|
+
writePathImmediately,
|
|
5300
|
+
clearPersistedDraft,
|
|
5301
|
+
awaitPendingWrites,
|
|
5302
|
+
dispose
|
|
5089
5303
|
};
|
|
5090
5304
|
}
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
if (warned.has(feature)) return;
|
|
5097
|
-
warned.add(feature);
|
|
5098
|
-
const message = featureMessage(feature);
|
|
5099
|
-
console.warn(`[attaform] ${message}`);
|
|
5305
|
+
function isEmptyContainer(value) {
|
|
5306
|
+
if (value === void 0 || value === null) return true;
|
|
5307
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
5308
|
+
if (isPlainRecord(value)) return Object.keys(value).length === 0;
|
|
5309
|
+
return false;
|
|
5100
5310
|
}
|
|
5101
|
-
function
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5311
|
+
function collectPersistedLeafPaths(form) {
|
|
5312
|
+
const out = [];
|
|
5313
|
+
walk(form, []);
|
|
5314
|
+
return out;
|
|
5315
|
+
function walk(node, prefix) {
|
|
5316
|
+
if (Array.isArray(node)) {
|
|
5317
|
+
for (let i = 0; i < node.length; i++) {
|
|
5318
|
+
walk(node[i], [...prefix, i]);
|
|
5319
|
+
}
|
|
5320
|
+
return;
|
|
5321
|
+
}
|
|
5322
|
+
if (isPlainRecord(node)) {
|
|
5323
|
+
for (const key of Object.keys(node)) {
|
|
5324
|
+
walk(node[key], [...prefix, key]);
|
|
5325
|
+
}
|
|
5326
|
+
return;
|
|
5327
|
+
}
|
|
5328
|
+
if (prefix.length === 0) return;
|
|
5329
|
+
out.push(paths.canonicalizePath(prefix).key);
|
|
5109
5330
|
}
|
|
5110
5331
|
}
|
|
5111
|
-
function
|
|
5112
|
-
|
|
5332
|
+
function isDescendantPathKey(candidate, ancestor) {
|
|
5333
|
+
if (candidate.length <= ancestor.length) return false;
|
|
5334
|
+
if (!ancestor.endsWith("]")) return false;
|
|
5335
|
+
const childPrefix = `${ancestor.slice(0, -1)},`;
|
|
5336
|
+
return candidate.startsWith(childPrefix);
|
|
5113
5337
|
}
|
|
5114
5338
|
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5339
|
+
const PROTOCOL_VERSION = 1;
|
|
5340
|
+
const JOIN_COLLECTION_WINDOW_MS = 50;
|
|
5341
|
+
const SNAPSHOT_TIMEOUT_MS = 200;
|
|
5342
|
+
const MAX_LEADER_ATTEMPTS = 3;
|
|
5343
|
+
const SNAPSHOT_RESPONSE_MIN_INTERVAL_MS = 500;
|
|
5344
|
+
function isFileLikeValue(value) {
|
|
5345
|
+
if (typeof File !== "undefined" && value instanceof File) return true;
|
|
5346
|
+
if (typeof Blob !== "undefined" && value instanceof Blob) return true;
|
|
5347
|
+
return false;
|
|
5120
5348
|
}
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5349
|
+
function isInboundShapeAcceptable(schema, path, value) {
|
|
5350
|
+
if (isFileLikeValue(value)) return false;
|
|
5351
|
+
let kind;
|
|
5352
|
+
if (Array.isArray(value)) {
|
|
5353
|
+
kind = "array";
|
|
5354
|
+
} else if (value !== null && typeof value === "object" && isPlainRecord(value)) {
|
|
5355
|
+
kind = "object";
|
|
5356
|
+
} else {
|
|
5357
|
+
kind = slimKindOf(value);
|
|
5125
5358
|
}
|
|
5126
|
-
const
|
|
5127
|
-
|
|
5128
|
-
if (
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
const trichotomyOverride = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
|
|
5134
|
-
const merged = mergeWithDefaults(registry.defaults, trichotomyOverride);
|
|
5135
|
-
const maxRecursionDepth = normalizeNumericOption({
|
|
5136
|
-
value: merged.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH,
|
|
5137
|
-
source: "useForm.maxRecursionDepth",
|
|
5138
|
-
allowInfinity: true,
|
|
5139
|
-
min: 0,
|
|
5140
|
-
defaultValue: DEFAULT_MAX_RECURSION_DEPTH
|
|
5141
|
-
});
|
|
5142
|
-
const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
|
|
5143
|
-
if (configuration.persist !== void 0 && configuration.key === void 0) {
|
|
5144
|
-
throw new paths.AnonPersistError({
|
|
5145
|
-
cause: "no-key",
|
|
5146
|
-
schemaFields: extractSchemaFields(resolvedSchema),
|
|
5147
|
-
callSite: paths.captureUserCallSite()
|
|
5148
|
-
});
|
|
5359
|
+
const accepted = schema.getSlimPrimitiveTypesAtPath(path);
|
|
5360
|
+
if (!accepted.has(kind)) return false;
|
|
5361
|
+
if (Array.isArray(value)) {
|
|
5362
|
+
for (let i = 0; i < value.length; i++) {
|
|
5363
|
+
if (!isInboundShapeAcceptable(schema, [...path, i], value[i])) return false;
|
|
5364
|
+
}
|
|
5365
|
+
return true;
|
|
5149
5366
|
}
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5367
|
+
if (isPlainRecord(value)) {
|
|
5368
|
+
for (const key of Object.keys(value)) {
|
|
5369
|
+
if (!isInboundShapeAcceptable(schema, [...path, key], value[key])) return false;
|
|
5370
|
+
}
|
|
5371
|
+
return true;
|
|
5154
5372
|
}
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5373
|
+
return true;
|
|
5374
|
+
}
|
|
5375
|
+
function diffBlankPaths(prev, curr) {
|
|
5376
|
+
const added = [];
|
|
5377
|
+
const removed = [];
|
|
5378
|
+
const prevSet = new Set(prev);
|
|
5379
|
+
for (const k of curr) if (!prevSet.has(k)) added.push(k);
|
|
5380
|
+
for (const k of prev) if (!curr.has(k)) removed.push(k);
|
|
5381
|
+
return { added, removed };
|
|
5382
|
+
}
|
|
5383
|
+
function snapshotForm(form) {
|
|
5384
|
+
return structuralSnapshot(form);
|
|
5385
|
+
}
|
|
5386
|
+
function stripSensitivePathsDeep(value, pathSoFar, isSensitivePath) {
|
|
5387
|
+
if (isFileLikeValue(value)) return void 0;
|
|
5388
|
+
if (value === null || typeof value !== "object") return value;
|
|
5389
|
+
if (Array.isArray(value)) {
|
|
5390
|
+
return value.map((item, i) => stripSensitivePathsDeep(item, [...pathSoFar, i], isSensitivePath));
|
|
5159
5391
|
}
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
}
|
|
5170
|
-
vue.onServerPrefetch(() => {
|
|
5171
|
-
if (!registry.shouldPrefetch(key)) return;
|
|
5172
|
-
return state.activate();
|
|
5173
|
-
});
|
|
5392
|
+
const proto = Object.getPrototypeOf(value);
|
|
5393
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
5394
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
5395
|
+
const src = value;
|
|
5396
|
+
for (const key of Object.keys(src)) {
|
|
5397
|
+
const childPath = [...pathSoFar, key];
|
|
5398
|
+
if (isSensitivePath(childPath)) {
|
|
5399
|
+
out[key] = void 0;
|
|
5400
|
+
continue;
|
|
5174
5401
|
}
|
|
5402
|
+
out[key] = stripSensitivePathsDeep(src[key], childPath, isSensitivePath);
|
|
5175
5403
|
}
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5404
|
+
return out;
|
|
5405
|
+
}
|
|
5406
|
+
function isStringArray(value) {
|
|
5407
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
5408
|
+
}
|
|
5409
|
+
function isPatchArray(value) {
|
|
5410
|
+
return Array.isArray(value) && value.every(
|
|
5411
|
+
(p) => p !== null && typeof p === "object" && Array.isArray(p.path)
|
|
5412
|
+
);
|
|
5413
|
+
}
|
|
5414
|
+
function isValidSyncMessage(data) {
|
|
5415
|
+
if (data === null || typeof data !== "object") return false;
|
|
5416
|
+
const m = data;
|
|
5417
|
+
if (m["v"] !== PROTOCOL_VERSION) return false;
|
|
5418
|
+
if (typeof m["senderId"] !== "string") return false;
|
|
5419
|
+
if (typeof m["kind"] !== "string") return false;
|
|
5420
|
+
switch (m["kind"]) {
|
|
5421
|
+
case "hello":
|
|
5422
|
+
case "announce":
|
|
5423
|
+
return true;
|
|
5424
|
+
case "requestSnapshot":
|
|
5425
|
+
return typeof m["targetId"] === "string";
|
|
5426
|
+
case "snapshot":
|
|
5427
|
+
return isStringArray(m["blankPaths"]) && "form" in m;
|
|
5428
|
+
case "patches":
|
|
5429
|
+
return isPatchArray(m["formPatches"]) && isStringArray(m["blankPathsAdded"]) && isStringArray(m["blankPathsRemoved"]);
|
|
5430
|
+
default:
|
|
5431
|
+
return false;
|
|
5202
5432
|
}
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
channelName = `attaform:sync:${state.formKey}:${hashStableString(state.schema.fingerprint())}`;
|
|
5210
|
-
} catch {
|
|
5211
|
-
channelName = null;
|
|
5212
|
-
}
|
|
5213
|
-
if (channelName !== null) {
|
|
5214
|
-
const syncModule = createMultiTabSyncModule(state, channelName, {
|
|
5215
|
-
isSensitivePath: state.isSensitivePath,
|
|
5216
|
-
noSyncPaths: state.noSyncPaths,
|
|
5217
|
-
validateForm: (form) => {
|
|
5218
|
-
const result = state.schema.validateAtPath(form, void 0, { sync: true });
|
|
5219
|
-
if (result instanceof Promise) return;
|
|
5220
|
-
if (!result.success) {
|
|
5221
|
-
throw new Error("attaform multi-tab sync: post-apply schema validation failed");
|
|
5222
|
-
}
|
|
5223
|
-
}
|
|
5224
|
-
});
|
|
5225
|
-
state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
|
|
5226
|
-
state.registerCleanup(() => syncModule.dispose());
|
|
5227
|
-
}
|
|
5228
|
-
} else if (hasBroadcastChannel && !secureContext) {
|
|
5229
|
-
warnOnceInsecureContext("multiTab");
|
|
5230
|
-
}
|
|
5433
|
+
}
|
|
5434
|
+
function generateSenderId() {
|
|
5435
|
+
try {
|
|
5436
|
+
return globalThis.crypto.randomUUID();
|
|
5437
|
+
} catch {
|
|
5438
|
+
return `atta-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;
|
|
5231
5439
|
}
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5440
|
+
}
|
|
5441
|
+
function createMultiTabSyncModule(state, channelName, options) {
|
|
5442
|
+
if (typeof BroadcastChannel === "undefined") {
|
|
5443
|
+
return {
|
|
5444
|
+
dispose: () => void 0,
|
|
5445
|
+
lifecycle: () => "established",
|
|
5446
|
+
senderId: "",
|
|
5447
|
+
channelName
|
|
5448
|
+
};
|
|
5236
5449
|
}
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5450
|
+
let channel;
|
|
5451
|
+
try {
|
|
5452
|
+
channel = new BroadcastChannel(channelName);
|
|
5453
|
+
} catch {
|
|
5454
|
+
return {
|
|
5455
|
+
dispose: () => void 0,
|
|
5456
|
+
lifecycle: () => "established",
|
|
5457
|
+
senderId: "",
|
|
5458
|
+
channelName
|
|
5459
|
+
};
|
|
5240
5460
|
}
|
|
5241
|
-
const
|
|
5242
|
-
|
|
5243
|
-
|
|
5461
|
+
const senderId = generateSenderId();
|
|
5462
|
+
let lifecycle = "joining";
|
|
5463
|
+
let disposed = false;
|
|
5464
|
+
const peerIds = /* @__PURE__ */ new Set();
|
|
5465
|
+
const lastSnapshotResponseAt = /* @__PURE__ */ new Map();
|
|
5466
|
+
let joinCollectionTimer = null;
|
|
5467
|
+
let snapshotTimeoutTimer = null;
|
|
5468
|
+
let leaderAttempts = 0;
|
|
5469
|
+
let prior = {
|
|
5470
|
+
form: snapshotForm(state.form.value),
|
|
5471
|
+
blankPathsSnapshot: [...state.blankPaths]
|
|
5472
|
+
};
|
|
5473
|
+
function safePost(msg) {
|
|
5474
|
+
if (disposed) return;
|
|
5475
|
+
try {
|
|
5476
|
+
channel.postMessage(msg);
|
|
5477
|
+
} catch {
|
|
5478
|
+
}
|
|
5244
5479
|
}
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5480
|
+
function refreshPrior() {
|
|
5481
|
+
prior = {
|
|
5482
|
+
form: snapshotForm(state.form.value),
|
|
5483
|
+
blankPathsSnapshot: [...state.blankPaths]
|
|
5484
|
+
};
|
|
5248
5485
|
}
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5486
|
+
function isPathLocallySuppressed(path) {
|
|
5487
|
+
if (options.isSensitivePath(path)) return true;
|
|
5488
|
+
const { key } = paths.canonicalizePath([...path]);
|
|
5489
|
+
if (options.noSyncPaths.has(key)) return true;
|
|
5490
|
+
return false;
|
|
5252
5491
|
}
|
|
5253
|
-
|
|
5254
|
-
|
|
5492
|
+
function postPatches() {
|
|
5493
|
+
if (lifecycle !== "established") return;
|
|
5494
|
+
const next = snapshotForm(state.form.value);
|
|
5495
|
+
const rawPatches = [];
|
|
5496
|
+
diffAndApply(prior.form, next, [], (p) => rawPatches.push(p));
|
|
5497
|
+
const safePatches = [];
|
|
5498
|
+
for (const p of rawPatches) {
|
|
5499
|
+
if (isPathLocallySuppressed(p.path)) continue;
|
|
5500
|
+
if ("value" in p && isFileLikeValue(p.value)) continue;
|
|
5501
|
+
safePatches.push(p);
|
|
5502
|
+
}
|
|
5503
|
+
const { added, removed } = diffBlankPaths(prior.blankPathsSnapshot, state.blankPaths);
|
|
5504
|
+
if (safePatches.length === 0 && added.length === 0 && removed.length === 0) {
|
|
5505
|
+
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
5506
|
+
return;
|
|
5507
|
+
}
|
|
5508
|
+
safePost({
|
|
5509
|
+
v: PROTOCOL_VERSION,
|
|
5510
|
+
kind: "patches",
|
|
5511
|
+
senderId,
|
|
5512
|
+
formPatches: safePatches,
|
|
5513
|
+
blankPathsAdded: added,
|
|
5514
|
+
blankPathsRemoved: removed
|
|
5515
|
+
});
|
|
5516
|
+
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
5255
5517
|
}
|
|
5256
|
-
const
|
|
5257
|
-
|
|
5258
|
-
|
|
5518
|
+
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
5519
|
+
if (disposed) return;
|
|
5520
|
+
if (lifecycle !== "established") return;
|
|
5521
|
+
if (meta?.crossTab === true) return;
|
|
5522
|
+
if (meta?.hydration === true) {
|
|
5523
|
+
refreshPrior();
|
|
5524
|
+
return;
|
|
5525
|
+
}
|
|
5526
|
+
postPatches();
|
|
5527
|
+
});
|
|
5528
|
+
function applyIncomingForm(form, blankPaths) {
|
|
5529
|
+
state.blankPaths.clear();
|
|
5530
|
+
for (const k of blankPaths) state.blankPaths.add(k);
|
|
5531
|
+
state.applyFormReplacement(form, { crossTab: true, persist: false });
|
|
5532
|
+
refreshPrior();
|
|
5259
5533
|
}
|
|
5260
|
-
|
|
5261
|
-
|
|
5534
|
+
function handlePatches(msg) {
|
|
5535
|
+
if (lifecycle !== "established") return;
|
|
5536
|
+
const safePatches = [];
|
|
5537
|
+
for (const p of msg.formPatches) {
|
|
5538
|
+
if (!Array.isArray(p.path)) continue;
|
|
5539
|
+
if (isPathLocallySuppressed(p.path)) continue;
|
|
5540
|
+
if ("value" in p && !isInboundShapeAcceptable(state.schema, p.path, p.value)) {
|
|
5541
|
+
continue;
|
|
5542
|
+
}
|
|
5543
|
+
safePatches.push(p);
|
|
5544
|
+
}
|
|
5545
|
+
const safeBlankAdded = [];
|
|
5546
|
+
for (const k of msg.blankPathsAdded) {
|
|
5547
|
+
const segs = paths.canonicalizePath(k).segments;
|
|
5548
|
+
if (isPathLocallySuppressed(segs)) continue;
|
|
5549
|
+
safeBlankAdded.push(k);
|
|
5550
|
+
}
|
|
5551
|
+
const safeBlankRemoved = [];
|
|
5552
|
+
for (const k of msg.blankPathsRemoved) {
|
|
5553
|
+
const segs = paths.canonicalizePath(k).segments;
|
|
5554
|
+
if (isPathLocallySuppressed(segs)) continue;
|
|
5555
|
+
safeBlankRemoved.push(k);
|
|
5556
|
+
}
|
|
5557
|
+
if (safePatches.length === 0 && safeBlankAdded.length === 0 && safeBlankRemoved.length === 0) {
|
|
5558
|
+
return;
|
|
5559
|
+
}
|
|
5560
|
+
const candidate = applyPatchesForward(state.form.value, safePatches);
|
|
5561
|
+
try {
|
|
5562
|
+
options.validateForm(state.form.value);
|
|
5563
|
+
try {
|
|
5564
|
+
options.validateForm(candidate);
|
|
5565
|
+
} catch {
|
|
5566
|
+
return;
|
|
5567
|
+
}
|
|
5568
|
+
} catch {
|
|
5569
|
+
}
|
|
5570
|
+
const nextBlankPaths = new Set(state.blankPaths);
|
|
5571
|
+
for (const k of safeBlankRemoved) nextBlankPaths.delete(k);
|
|
5572
|
+
for (const k of safeBlankAdded) nextBlankPaths.add(k);
|
|
5573
|
+
applyIncomingForm(candidate, [...nextBlankPaths]);
|
|
5262
5574
|
}
|
|
5263
|
-
|
|
5264
|
-
|
|
5575
|
+
function handleSnapshot(msg) {
|
|
5576
|
+
if (lifecycle !== "joining") return;
|
|
5577
|
+
if (!isInboundShapeAcceptable(state.schema, [], msg.form)) return;
|
|
5578
|
+
if (snapshotTimeoutTimer !== null) {
|
|
5579
|
+
clearTimeout(snapshotTimeoutTimer);
|
|
5580
|
+
snapshotTimeoutTimer = null;
|
|
5581
|
+
}
|
|
5582
|
+
if (joinCollectionTimer !== null) {
|
|
5583
|
+
clearTimeout(joinCollectionTimer);
|
|
5584
|
+
joinCollectionTimer = null;
|
|
5585
|
+
}
|
|
5586
|
+
applyIncomingForm(msg.form, msg.blankPaths);
|
|
5587
|
+
lifecycle = "established";
|
|
5588
|
+
peerIds.clear();
|
|
5265
5589
|
}
|
|
5266
|
-
|
|
5267
|
-
|
|
5590
|
+
function respondToHello() {
|
|
5591
|
+
safePost({ v: PROTOCOL_VERSION, kind: "announce", senderId });
|
|
5268
5592
|
}
|
|
5269
|
-
|
|
5270
|
-
|
|
5593
|
+
function respondToSnapshotRequest(requesterId) {
|
|
5594
|
+
const now = Date.now();
|
|
5595
|
+
const last = lastSnapshotResponseAt.get(requesterId);
|
|
5596
|
+
if (last !== void 0 && now - last < SNAPSHOT_RESPONSE_MIN_INTERVAL_MS) return;
|
|
5597
|
+
lastSnapshotResponseAt.set(requesterId, now);
|
|
5598
|
+
const scrubbedForm = stripSensitivePathsDeep(state.form.value, [], options.isSensitivePath);
|
|
5599
|
+
safePost({
|
|
5600
|
+
v: PROTOCOL_VERSION,
|
|
5601
|
+
kind: "snapshot",
|
|
5602
|
+
senderId,
|
|
5603
|
+
form: scrubbedForm,
|
|
5604
|
+
blankPaths: [...state.blankPaths]
|
|
5605
|
+
});
|
|
5271
5606
|
}
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
...validateOn === void 0 ? {} : { validateOn },
|
|
5299
|
-
...debounceMs === void 0 ? {} : { debounceMs },
|
|
5300
|
-
...getDisplayState === void 0 ? {} : { getDisplayState },
|
|
5301
|
-
...maxRecursionDepth === void 0 ? {} : { maxRecursionDepth },
|
|
5302
|
-
...sensitiveNames === void 0 ? {} : { sensitiveNames },
|
|
5303
|
-
...multiTab === void 0 ? {} : { multiTab },
|
|
5304
|
-
...autoAria === void 0 ? {} : { autoAria }
|
|
5305
|
-
};
|
|
5306
|
-
}
|
|
5307
|
-
const HISTORY_MODULE_KEY = "history";
|
|
5308
|
-
function buildFreshState(key, schema, configuration, registry) {
|
|
5309
|
-
const pending = registry.pendingHydration.get(key);
|
|
5310
|
-
if (pending !== void 0) registry.pendingHydration.delete(key);
|
|
5311
|
-
const walked = walkUnsetSentinels(
|
|
5312
|
-
configuration.defaultValues,
|
|
5313
|
-
schema
|
|
5314
|
-
);
|
|
5315
|
-
let initialBlankPaths;
|
|
5316
|
-
if (pending === void 0) {
|
|
5317
|
-
initialBlankPaths = walked.paths;
|
|
5318
|
-
}
|
|
5319
|
-
const resolvedSensitiveNames = configuration.sensitiveNames;
|
|
5320
|
-
const resolvedIsSensitivePath = resolvedSensitiveNames === void 0 ? void 0 : paths.createIsSensitivePath(resolvedSensitiveNames);
|
|
5321
|
-
const resolvedSegmentMatchesSensitive = resolvedSensitiveNames === void 0 ? void 0 : paths.createSegmentMatchesSensitive(resolvedSensitiveNames);
|
|
5322
|
-
const createOptions = {
|
|
5323
|
-
formKey: key,
|
|
5324
|
-
schema,
|
|
5325
|
-
defaultValues: walked.cleanedValues,
|
|
5326
|
-
...configuration.strict !== void 0 ? { strict: configuration.strict } : {},
|
|
5327
|
-
hydration: pending,
|
|
5328
|
-
...configuration.validateOn !== void 0 ? { validateOn: configuration.validateOn } : {},
|
|
5329
|
-
...configuration.debounceMs !== void 0 ? { debounceMs: configuration.debounceMs } : {},
|
|
5330
|
-
ssr: registry.ssr,
|
|
5331
|
-
// Server-only: bind the SSR prefetch coordination handles. `enqueue`
|
|
5332
|
-
// records intent on every `state.activate()` so a wizard skip-list
|
|
5333
|
-
// override or a future transform mark has a consistent set to diff
|
|
5334
|
-
// against; `shouldFire` lets the activate path bail when the
|
|
5335
|
-
// wizard explicitly skipped this key — even an explicit
|
|
5336
|
-
// `form.activate()` defers to the wizard's render-efficiency
|
|
5337
|
-
// skip-list on the server.
|
|
5338
|
-
...registry.ssr ? {
|
|
5339
|
-
ssrPrefetch: {
|
|
5340
|
-
enqueue: () => {
|
|
5341
|
-
registry.enqueuePrefetch(key);
|
|
5342
|
-
},
|
|
5343
|
-
shouldFire: () => registry.shouldPrefetch(key)
|
|
5607
|
+
channel.onmessage = (event) => {
|
|
5608
|
+
if (disposed) return;
|
|
5609
|
+
try {
|
|
5610
|
+
const data = event.data;
|
|
5611
|
+
if (!isValidSyncMessage(data)) return;
|
|
5612
|
+
const msg = data;
|
|
5613
|
+
if (msg.senderId === senderId) return;
|
|
5614
|
+
switch (msg.kind) {
|
|
5615
|
+
case "hello":
|
|
5616
|
+
if (lifecycle !== "established") return;
|
|
5617
|
+
respondToHello();
|
|
5618
|
+
break;
|
|
5619
|
+
case "announce":
|
|
5620
|
+
if (lifecycle === "joining") peerIds.add(msg.senderId);
|
|
5621
|
+
break;
|
|
5622
|
+
case "requestSnapshot":
|
|
5623
|
+
if (lifecycle !== "established") return;
|
|
5624
|
+
if (msg.targetId !== senderId) return;
|
|
5625
|
+
respondToSnapshotRequest(msg.senderId);
|
|
5626
|
+
break;
|
|
5627
|
+
case "snapshot":
|
|
5628
|
+
handleSnapshot(msg);
|
|
5629
|
+
break;
|
|
5630
|
+
case "patches":
|
|
5631
|
+
handlePatches(msg);
|
|
5632
|
+
break;
|
|
5344
5633
|
}
|
|
5345
|
-
}
|
|
5346
|
-
|
|
5347
|
-
...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
|
|
5348
|
-
...configuration.getDisplayState !== void 0 ? { getDisplayState: configuration.getDisplayState } : {},
|
|
5349
|
-
...initialBlankPaths !== void 0 ? { initialBlankPaths } : {},
|
|
5350
|
-
...resolvedIsSensitivePath !== void 0 ? { isSensitivePath: resolvedIsSensitivePath } : {},
|
|
5351
|
-
...resolvedSegmentMatchesSensitive !== void 0 ? { segmentMatchesSensitive: resolvedSegmentMatchesSensitive } : {}
|
|
5352
|
-
};
|
|
5353
|
-
const state = createFormStore(createOptions);
|
|
5354
|
-
registry.forms.set(
|
|
5355
|
-
key,
|
|
5356
|
-
state
|
|
5357
|
-
);
|
|
5358
|
-
return state;
|
|
5359
|
-
}
|
|
5360
|
-
let anonCounter = 0;
|
|
5361
|
-
let formInstanceCounter = 0;
|
|
5362
|
-
const ambientProvideHistory = paths.__DEV__ ? /* @__PURE__ */ new WeakMap() : null;
|
|
5363
|
-
function recordAmbientProvide(ssr) {
|
|
5364
|
-
if (!paths.__DEV__ || ssr || ambientProvideHistory === null) return;
|
|
5365
|
-
const instance = vue.getCurrentInstance();
|
|
5366
|
-
if (instance === null) return;
|
|
5367
|
-
const instanceKey = instance;
|
|
5368
|
-
const entry = {
|
|
5369
|
-
source: paths.captureUserCallSite()
|
|
5634
|
+
} catch {
|
|
5635
|
+
}
|
|
5370
5636
|
};
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
}
|
|
5378
|
-
function resolveFormKey(key) {
|
|
5379
|
-
if (key !== void 0 && key !== null && key !== "") {
|
|
5380
|
-
if (key.startsWith(RESERVED_KEY_PREFIX)) {
|
|
5381
|
-
throw new paths.ReservedFormKeyError(key);
|
|
5637
|
+
function electLeaderAndRequest() {
|
|
5638
|
+
if (disposed) return;
|
|
5639
|
+
if (peerIds.size === 0) {
|
|
5640
|
+
lifecycle = "established";
|
|
5641
|
+
refreshPrior();
|
|
5642
|
+
return;
|
|
5382
5643
|
}
|
|
5383
|
-
|
|
5644
|
+
const sorted = [...peerIds].sort();
|
|
5645
|
+
const leaderId = sorted[0];
|
|
5646
|
+
peerIds.delete(leaderId);
|
|
5647
|
+
leaderAttempts++;
|
|
5648
|
+
safePost({
|
|
5649
|
+
v: PROTOCOL_VERSION,
|
|
5650
|
+
kind: "requestSnapshot",
|
|
5651
|
+
senderId,
|
|
5652
|
+
targetId: leaderId
|
|
5653
|
+
});
|
|
5654
|
+
snapshotTimeoutTimer = setTimeout(() => {
|
|
5655
|
+
snapshotTimeoutTimer = null;
|
|
5656
|
+
if (disposed) return;
|
|
5657
|
+
if (lifecycle === "established") return;
|
|
5658
|
+
if (leaderAttempts >= MAX_LEADER_ATTEMPTS || peerIds.size === 0) {
|
|
5659
|
+
lifecycle = "established";
|
|
5660
|
+
refreshPrior();
|
|
5661
|
+
return;
|
|
5662
|
+
}
|
|
5663
|
+
electLeaderAndRequest();
|
|
5664
|
+
}, SNAPSHOT_TIMEOUT_MS);
|
|
5384
5665
|
}
|
|
5385
|
-
|
|
5386
|
-
|
|
5666
|
+
function joinFlow() {
|
|
5667
|
+
safePost({ v: PROTOCOL_VERSION, kind: "hello", senderId });
|
|
5668
|
+
joinCollectionTimer = setTimeout(() => {
|
|
5669
|
+
joinCollectionTimer = null;
|
|
5670
|
+
if (disposed) return;
|
|
5671
|
+
if (lifecycle === "established") return;
|
|
5672
|
+
electLeaderAndRequest();
|
|
5673
|
+
}, JOIN_COLLECTION_WINDOW_MS);
|
|
5387
5674
|
}
|
|
5388
|
-
|
|
5675
|
+
joinFlow();
|
|
5676
|
+
return {
|
|
5677
|
+
dispose: () => {
|
|
5678
|
+
if (disposed) return;
|
|
5679
|
+
disposed = true;
|
|
5680
|
+
if (joinCollectionTimer !== null) {
|
|
5681
|
+
clearTimeout(joinCollectionTimer);
|
|
5682
|
+
joinCollectionTimer = null;
|
|
5683
|
+
}
|
|
5684
|
+
if (snapshotTimeoutTimer !== null) {
|
|
5685
|
+
clearTimeout(snapshotTimeoutTimer);
|
|
5686
|
+
snapshotTimeoutTimer = null;
|
|
5687
|
+
}
|
|
5688
|
+
unsubscribeChange();
|
|
5689
|
+
try {
|
|
5690
|
+
channel.close();
|
|
5691
|
+
} catch {
|
|
5692
|
+
}
|
|
5693
|
+
},
|
|
5694
|
+
lifecycle: () => lifecycle,
|
|
5695
|
+
senderId,
|
|
5696
|
+
channelName
|
|
5697
|
+
};
|
|
5389
5698
|
}
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
error
|
|
5400
|
-
);
|
|
5401
|
-
return;
|
|
5402
|
-
}
|
|
5403
|
-
if (existingFp === incomingFp) return;
|
|
5404
|
-
console.warn(
|
|
5405
|
-
`[attaform] useForm() calls with key "${key}" use different schemas; first wins, second is ignored. Use identical schemas or unique keys.
|
|
5406
|
-
existing: ${existingFp}
|
|
5407
|
-
incoming: ${incomingFp}`
|
|
5408
|
-
);
|
|
5699
|
+
const MULTI_TAB_SYNC_MODULE_KEY = "multiTabSync";
|
|
5700
|
+
|
|
5701
|
+
const warned = /* @__PURE__ */ new Set();
|
|
5702
|
+
function warnOnceInsecureContext(feature) {
|
|
5703
|
+
if (!paths.__DEV__) return;
|
|
5704
|
+
if (warned.has(feature)) return;
|
|
5705
|
+
warned.add(feature);
|
|
5706
|
+
const message = featureMessage(feature);
|
|
5707
|
+
console.warn(`[attaform] ${message}`);
|
|
5409
5708
|
}
|
|
5410
|
-
function
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
return;
|
|
5709
|
+
function featureMessage(feature) {
|
|
5710
|
+
switch (feature) {
|
|
5711
|
+
case "multiTab":
|
|
5712
|
+
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.";
|
|
5713
|
+
case "persist:local":
|
|
5714
|
+
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.";
|
|
5715
|
+
case "persist:session":
|
|
5716
|
+
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.";
|
|
5419
5717
|
}
|
|
5420
|
-
if (persistConfigsEquivalent(wired.wiredConfig, incomingNormalized)) return;
|
|
5421
|
-
console.warn(
|
|
5422
|
-
`[attaform] useForm({ key: "${key}" }) passed a persist config that differs from the first useForm({ key }) call's; first wins, this one is ignored.
|
|
5423
|
-
wired: ${describePersist(wired.wiredConfig)}
|
|
5424
|
-
incoming: ${describePersist(incomingNormalized)}`
|
|
5425
|
-
);
|
|
5426
5718
|
}
|
|
5427
|
-
function
|
|
5428
|
-
|
|
5429
|
-
if ((a.key ?? void 0) !== (b.key ?? void 0)) return false;
|
|
5430
|
-
if ((a.debounceMs ?? void 0) !== (b.debounceMs ?? void 0)) return false;
|
|
5431
|
-
return true;
|
|
5719
|
+
function isSecureContext() {
|
|
5720
|
+
return typeof window !== "undefined" && window.isSecureContext === true;
|
|
5432
5721
|
}
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
return
|
|
5722
|
+
|
|
5723
|
+
function resolveTrichotomy(input) {
|
|
5724
|
+
if (typeof input === "function") {
|
|
5725
|
+
return { kind: "async", factory: input };
|
|
5726
|
+
}
|
|
5727
|
+
return { kind: "sync", value: input };
|
|
5439
5728
|
}
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5729
|
+
|
|
5730
|
+
function useAbstractForm(configuration, options) {
|
|
5731
|
+
if (configuration === void 0 || configuration === null || configuration.schema === void 0) {
|
|
5732
|
+
throw new paths.InvalidUseFormConfigError();
|
|
5733
|
+
}
|
|
5734
|
+
const key = resolveFormKey(configuration.key);
|
|
5735
|
+
const instance = vue.getCurrentInstance();
|
|
5736
|
+
if (instance !== null) paths.ensureAttaformInstalled(instance.appContext.app);
|
|
5737
|
+
const registry = options?.registry ?? paths.useRegistry();
|
|
5738
|
+
const resolvedDefaults = resolveTrichotomy(configuration.defaultValues);
|
|
5739
|
+
const materialisedDefaults = resolvedDefaults.kind === "sync" ? resolvedDefaults.value : void 0;
|
|
5740
|
+
const { defaultValues: _droppedDefaults, ...configWithoutDefaults } = configuration;
|
|
5741
|
+
const trichotomyOverride = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
|
|
5742
|
+
const merged = mergeWithDefaults(registry.defaults, trichotomyOverride);
|
|
5743
|
+
const maxRecursionDepth = normalizeNumericOption({
|
|
5744
|
+
value: merged.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH,
|
|
5745
|
+
source: "useForm.maxRecursionDepth",
|
|
5746
|
+
allowInfinity: true,
|
|
5448
5747
|
min: 0,
|
|
5449
|
-
defaultValue:
|
|
5748
|
+
defaultValue: DEFAULT_MAX_RECURSION_DEPTH
|
|
5450
5749
|
});
|
|
5451
|
-
const
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
const
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
filteredUserErrors,
|
|
5479
|
-
filteredTransientEmpty
|
|
5480
|
-
);
|
|
5481
|
-
await adapter.setItem(key, payload);
|
|
5482
|
-
}, debounceMs);
|
|
5483
|
-
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
5484
|
-
if (disposed || inFlightFinalFlush !== null) return;
|
|
5485
|
-
if (meta?.crossTab === true) return;
|
|
5486
|
-
if (meta?.persist !== true) return;
|
|
5487
|
-
pendingOptedInPaths = new Set(state.persistOptIns.optedInPaths());
|
|
5488
|
-
writer.schedule();
|
|
5489
|
-
});
|
|
5490
|
-
const unsubscribeSuccess = clearOnSubmitSuccess ? state.onSubmitSuccess(() => {
|
|
5491
|
-
if (disposed) return;
|
|
5492
|
-
void (async () => {
|
|
5493
|
-
await writer.flush();
|
|
5494
|
-
if (disposed) return;
|
|
5495
|
-
const adapter = await adapterPromise;
|
|
5496
|
-
if (disposed) return;
|
|
5497
|
-
await adapter.removeItem(key);
|
|
5498
|
-
})();
|
|
5499
|
-
}) : () => void 0;
|
|
5500
|
-
void (async () => {
|
|
5501
|
-
const adapter = await adapterPromise;
|
|
5502
|
-
if (disposed) return;
|
|
5503
|
-
void cleanupOrphanKeys(adapter, base, key);
|
|
5504
|
-
try {
|
|
5505
|
-
const raw = await adapter.getItem(key);
|
|
5506
|
-
const payload = readPersistedPayload(raw);
|
|
5507
|
-
if (payload === null) {
|
|
5508
|
-
if (raw !== null && raw !== void 0) {
|
|
5509
|
-
await adapter.removeItem(key);
|
|
5510
|
-
}
|
|
5511
|
-
return;
|
|
5512
|
-
}
|
|
5513
|
-
if (disposed) return;
|
|
5514
|
-
const merged = mergeSparseHydration(
|
|
5515
|
-
vue.toRaw(state.form.value),
|
|
5516
|
-
payload.data.form,
|
|
5517
|
-
state.schema
|
|
5518
|
-
);
|
|
5519
|
-
state.applyFormReplacement(merged, { hydration: true });
|
|
5520
|
-
const persistedLeafPaths = collectPersistedLeafPaths(payload.data.form);
|
|
5521
|
-
for (const k of persistedLeafPaths) {
|
|
5522
|
-
state.blankPaths.delete(k);
|
|
5523
|
-
state.originalBlankPaths.delete(k);
|
|
5524
|
-
}
|
|
5525
|
-
for (const k of payload.data.blankPaths ?? []) {
|
|
5526
|
-
const key2 = paths.coerceToPathKey(k);
|
|
5527
|
-
state.blankPaths.add(key2);
|
|
5528
|
-
state.originalBlankPaths.add(key2);
|
|
5529
|
-
}
|
|
5530
|
-
if (include === "form+errors") {
|
|
5531
|
-
if (payload.data.schemaErrors !== void 0) {
|
|
5532
|
-
const flat = payload.data.schemaErrors.flatMap(([, errs]) => errs);
|
|
5533
|
-
state.setAllSchemaErrors(flat);
|
|
5534
|
-
}
|
|
5535
|
-
if (payload.data.userErrors !== void 0) {
|
|
5536
|
-
const flat = payload.data.userErrors.flatMap(([, errs]) => errs);
|
|
5537
|
-
state.setAllUserErrors(flat);
|
|
5538
|
-
}
|
|
5750
|
+
const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
|
|
5751
|
+
if (configuration.persist !== void 0 && configuration.key === void 0) {
|
|
5752
|
+
throw new paths.AnonPersistError({
|
|
5753
|
+
cause: "no-key",
|
|
5754
|
+
schemaFields: extractSchemaFields(resolvedSchema),
|
|
5755
|
+
callSite: paths.captureUserCallSite()
|
|
5756
|
+
});
|
|
5757
|
+
}
|
|
5758
|
+
const existing = registry.forms.get(key);
|
|
5759
|
+
if (paths.__DEV__ && existing !== void 0) {
|
|
5760
|
+
warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
|
|
5761
|
+
warnOnPersistDivergence(key, existing, configuration.persist);
|
|
5762
|
+
}
|
|
5763
|
+
const hadPendingHydration = registry.pendingHydration.has(key);
|
|
5764
|
+
const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
|
|
5765
|
+
if (existing !== void 0) ; else if (resolvedDefaults.kind === "sync") {
|
|
5766
|
+
state.defaultsResolved.value = true;
|
|
5767
|
+
}
|
|
5768
|
+
if (existing === void 0 && resolvedDefaults.kind === "async") {
|
|
5769
|
+
const factory = resolvedDefaults.factory;
|
|
5770
|
+
state.defaultValuesFactory.value = factory;
|
|
5771
|
+
if (hadPendingHydration) {
|
|
5772
|
+
state.hydrating.value = false;
|
|
5773
|
+
state.defaultsResolved.value = true;
|
|
5774
|
+
} else if (registry.ssr) {
|
|
5775
|
+
if (configuration.__ssrAccessed === true) {
|
|
5776
|
+
registry.enqueuePrefetch(key);
|
|
5539
5777
|
}
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
);
|
|
5545
|
-
} catch {
|
|
5778
|
+
vue.onServerPrefetch(() => {
|
|
5779
|
+
if (!registry.shouldPrefetch(key)) return;
|
|
5780
|
+
return state.activate();
|
|
5781
|
+
});
|
|
5546
5782
|
}
|
|
5547
|
-
}
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5783
|
+
}
|
|
5784
|
+
if (vue.getCurrentScope() !== void 0) {
|
|
5785
|
+
const releaseConsumer = registry.trackConsumer(key);
|
|
5786
|
+
vue.onScopeDispose(releaseConsumer);
|
|
5787
|
+
}
|
|
5788
|
+
const persistDisabledByAnonRule = merged.persist !== void 0 && enforceAnonPersistRule(state.formKey, registry.ssr);
|
|
5789
|
+
if (existing === void 0 && !registry.ssr) {
|
|
5790
|
+
if (merged.persist !== void 0 && !persistDisabledByAnonRule) {
|
|
5791
|
+
const resolvedPersist = normalizePersistConfig(merged.persist);
|
|
5792
|
+
const storageKind = resolvedPersist.storage;
|
|
5793
|
+
const isBuiltinStorage = typeof storageKind === "string";
|
|
5794
|
+
const secureContextOk = !isBuiltinStorage || isSecureContext();
|
|
5795
|
+
if (!secureContextOk) {
|
|
5796
|
+
const feature = storageKind === "session" ? "persist:session" : "persist:local";
|
|
5797
|
+
warnOnceInsecureContext(feature);
|
|
5798
|
+
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
5799
|
+
} else {
|
|
5800
|
+
const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
|
|
5801
|
+
void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
|
|
5802
|
+
const persistenceModule = wirePersistence(state, resolvedPersist);
|
|
5803
|
+
state.modules.set(PERSISTENCE_MODULE_KEY, persistenceModule);
|
|
5804
|
+
state.registerDrain(() => persistenceModule.awaitPendingWrites());
|
|
5805
|
+
state.registerCleanup(() => persistenceModule.dispose());
|
|
5568
5806
|
}
|
|
5569
|
-
}
|
|
5570
|
-
if (include === "form") {
|
|
5571
|
-
await adapter.setItem(
|
|
5572
|
-
key,
|
|
5573
|
-
buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
|
|
5574
|
-
);
|
|
5575
|
-
return;
|
|
5576
|
-
}
|
|
5577
|
-
const schemaMap = new Map(existing?.data.schemaErrors ?? []);
|
|
5578
|
-
const userMap = new Map(existing?.data.userErrors ?? []);
|
|
5579
|
-
const currentSchema = state.schemaErrors.get(pathKey);
|
|
5580
|
-
const currentUser = state.userErrors.get(pathKey);
|
|
5581
|
-
if (currentSchema !== void 0 && currentSchema.length > 0) {
|
|
5582
|
-
schemaMap.set(pathKey, [...currentSchema]);
|
|
5583
5807
|
} else {
|
|
5584
|
-
|
|
5585
|
-
}
|
|
5586
|
-
if (currentUser !== void 0 && currentUser.length > 0) {
|
|
5587
|
-
userMap.set(pathKey, [...currentUser]);
|
|
5588
|
-
} else {
|
|
5589
|
-
userMap.delete(pathKey);
|
|
5808
|
+
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
5590
5809
|
}
|
|
5591
|
-
await adapter.setItem(
|
|
5592
|
-
key,
|
|
5593
|
-
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5594
|
-
);
|
|
5595
5810
|
}
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
if (
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5811
|
+
if (existing === void 0 && merged.multiTab === true && configuration.key !== void 0 && !registry.ssr) {
|
|
5812
|
+
const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
|
|
5813
|
+
const secureContext = isSecureContext();
|
|
5814
|
+
if (hasBroadcastChannel && secureContext) {
|
|
5815
|
+
let channelName;
|
|
5816
|
+
try {
|
|
5817
|
+
channelName = `attaform:sync:${state.formKey}:${hashStableString(state.schema.fingerprint())}`;
|
|
5818
|
+
} catch {
|
|
5819
|
+
channelName = null;
|
|
5820
|
+
}
|
|
5821
|
+
if (channelName !== null) {
|
|
5822
|
+
const syncModule = createMultiTabSyncModule(state, channelName, {
|
|
5823
|
+
isSensitivePath: state.isSensitivePath,
|
|
5824
|
+
noSyncPaths: state.noSyncPaths,
|
|
5825
|
+
validateForm: (form) => {
|
|
5826
|
+
const result = state.schema.validateAtPath(form, void 0, { sync: true });
|
|
5827
|
+
if (result instanceof Promise) return;
|
|
5828
|
+
if (!result.success) {
|
|
5829
|
+
throw new Error("attaform multi-tab sync: post-apply schema validation failed");
|
|
5830
|
+
}
|
|
5831
|
+
}
|
|
5832
|
+
});
|
|
5833
|
+
state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
|
|
5834
|
+
state.registerCleanup(() => syncModule.dispose());
|
|
5835
|
+
}
|
|
5836
|
+
} else if (hasBroadcastChannel && !secureContext) {
|
|
5837
|
+
warnOnceInsecureContext("multiTab");
|
|
5613
5838
|
}
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5839
|
+
}
|
|
5840
|
+
if (existing === void 0 && merged.history !== void 0) {
|
|
5841
|
+
const historyModule = createHistoryModule(state, merged.history);
|
|
5842
|
+
state.modules.set(HISTORY_MODULE_KEY, historyModule);
|
|
5843
|
+
state.registerCleanup(() => historyModule.dispose());
|
|
5844
|
+
}
|
|
5845
|
+
if (configuration.key === void 0) {
|
|
5846
|
+
recordAmbientProvide(registry.ssr);
|
|
5847
|
+
vue.provide(paths.kFormContext, state);
|
|
5848
|
+
}
|
|
5849
|
+
const formInstanceId = vue.getCurrentInstance() !== null ? vue.useId() : `atta:form-instance:${formInstanceCounter++}`;
|
|
5850
|
+
if (vue.getCurrentInstance() !== null) {
|
|
5851
|
+
vue.provide(paths.kFormInstanceId, formInstanceId);
|
|
5852
|
+
}
|
|
5853
|
+
const apiOptions = {};
|
|
5854
|
+
if (merged.onInvalidSubmit !== void 0) {
|
|
5855
|
+
apiOptions.onInvalidSubmit = merged.onInvalidSubmit;
|
|
5856
|
+
}
|
|
5857
|
+
const history = state.modules.get(HISTORY_MODULE_KEY);
|
|
5858
|
+
if (history !== void 0) {
|
|
5859
|
+
apiOptions.history = history;
|
|
5860
|
+
}
|
|
5861
|
+
if (merged.validateOn !== void 0) {
|
|
5862
|
+
apiOptions.validateOn = merged.validateOn;
|
|
5863
|
+
}
|
|
5864
|
+
const mergedDebounceMs = merged.debounceMs;
|
|
5865
|
+
if (mergedDebounceMs !== void 0) {
|
|
5866
|
+
apiOptions.debounceMs = mergedDebounceMs;
|
|
5867
|
+
}
|
|
5868
|
+
if (merged.getDisplayState !== void 0) {
|
|
5869
|
+
apiOptions.getDisplayState = merged.getDisplayState;
|
|
5870
|
+
}
|
|
5871
|
+
if (merged.coerce !== void 0) {
|
|
5872
|
+
apiOptions.coerce = merged.coerce;
|
|
5873
|
+
}
|
|
5874
|
+
if (merged.rememberVariants !== void 0) {
|
|
5875
|
+
apiOptions.rememberVariants = merged.rememberVariants;
|
|
5876
|
+
}
|
|
5877
|
+
if (merged.autoAria !== void 0) {
|
|
5878
|
+
apiOptions.autoAria = merged.autoAria;
|
|
5879
|
+
}
|
|
5880
|
+
return buildFormApi(
|
|
5881
|
+
state,
|
|
5882
|
+
formInstanceId,
|
|
5883
|
+
apiOptions
|
|
5884
|
+
);
|
|
5885
|
+
}
|
|
5886
|
+
function mergeWithDefaults(defaults, configuration) {
|
|
5887
|
+
const strict = configuration.strict ?? defaults.strict;
|
|
5888
|
+
const onInvalidSubmit = configuration.onInvalidSubmit ?? defaults.onInvalidSubmit;
|
|
5889
|
+
const history = configuration.history ?? defaults.history;
|
|
5890
|
+
const rememberVariants = configuration.rememberVariants ?? defaults.rememberVariants;
|
|
5891
|
+
const coerce = configuration.coerce ?? defaults.coerce;
|
|
5892
|
+
const validateOn = configuration.validateOn ?? defaults.validateOn;
|
|
5893
|
+
const debounceMs = configuration.debounceMs ?? defaults.debounceMs;
|
|
5894
|
+
const getDisplayState = configuration.getDisplayState ?? defaults.getDisplayState;
|
|
5895
|
+
const maxRecursionDepth = configuration.maxRecursionDepth ?? defaults.maxRecursionDepth;
|
|
5896
|
+
const sensitiveNames = configuration.sensitiveNames ?? defaults.sensitiveNames;
|
|
5897
|
+
const multiTab = configuration.multiTab ?? defaults.multiTab;
|
|
5898
|
+
const autoAria = configuration.autoAria ?? defaults.autoAria;
|
|
5899
|
+
return {
|
|
5900
|
+
...configuration,
|
|
5901
|
+
...strict === void 0 ? {} : { strict },
|
|
5902
|
+
...onInvalidSubmit === void 0 ? {} : { onInvalidSubmit },
|
|
5903
|
+
...history === void 0 ? {} : { history },
|
|
5904
|
+
...rememberVariants === void 0 ? {} : { rememberVariants },
|
|
5905
|
+
...coerce === void 0 ? {} : { coerce },
|
|
5906
|
+
...validateOn === void 0 ? {} : { validateOn },
|
|
5907
|
+
...debounceMs === void 0 ? {} : { debounceMs },
|
|
5908
|
+
...getDisplayState === void 0 ? {} : { getDisplayState },
|
|
5909
|
+
...maxRecursionDepth === void 0 ? {} : { maxRecursionDepth },
|
|
5910
|
+
...sensitiveNames === void 0 ? {} : { sensitiveNames },
|
|
5911
|
+
...multiTab === void 0 ? {} : { multiTab },
|
|
5912
|
+
...autoAria === void 0 ? {} : { autoAria }
|
|
5913
|
+
};
|
|
5914
|
+
}
|
|
5915
|
+
const HISTORY_MODULE_KEY = "history";
|
|
5916
|
+
function buildFreshState(key, schema, configuration, registry) {
|
|
5917
|
+
const pending = registry.pendingHydration.get(key);
|
|
5918
|
+
if (pending !== void 0) registry.pendingHydration.delete(key);
|
|
5919
|
+
const walked = walkUnsetSentinels(
|
|
5920
|
+
configuration.defaultValues,
|
|
5921
|
+
schema
|
|
5922
|
+
);
|
|
5923
|
+
let initialBlankPaths;
|
|
5924
|
+
if (pending === void 0) {
|
|
5925
|
+
initialBlankPaths = walked.paths;
|
|
5926
|
+
}
|
|
5927
|
+
const resolvedSensitiveNames = configuration.sensitiveNames;
|
|
5928
|
+
const resolvedIsSensitivePath = resolvedSensitiveNames === void 0 ? void 0 : paths.createIsSensitivePath(resolvedSensitiveNames);
|
|
5929
|
+
const createOptions = {
|
|
5930
|
+
formKey: key,
|
|
5931
|
+
schema,
|
|
5932
|
+
defaultValues: walked.cleanedValues,
|
|
5933
|
+
...configuration.strict !== void 0 ? { strict: configuration.strict } : {},
|
|
5934
|
+
hydration: pending,
|
|
5935
|
+
...configuration.validateOn !== void 0 ? { validateOn: configuration.validateOn } : {},
|
|
5936
|
+
...configuration.debounceMs !== void 0 ? { debounceMs: configuration.debounceMs } : {},
|
|
5937
|
+
ssr: registry.ssr,
|
|
5938
|
+
// Server-only: bind the SSR prefetch coordination handles. `enqueue`
|
|
5939
|
+
// records intent on every `state.activate()` so a wizard skip-list
|
|
5940
|
+
// override or a future transform mark has a consistent set to diff
|
|
5941
|
+
// against; `shouldFire` lets the activate path bail when the
|
|
5942
|
+
// wizard explicitly skipped this key — even an explicit
|
|
5943
|
+
// `form.activate()` defers to the wizard's render-efficiency
|
|
5944
|
+
// skip-list on the server.
|
|
5945
|
+
...registry.ssr ? {
|
|
5946
|
+
ssrPrefetch: {
|
|
5947
|
+
enqueue: () => {
|
|
5948
|
+
registry.enqueuePrefetch(key);
|
|
5949
|
+
},
|
|
5950
|
+
shouldFire: () => registry.shouldPrefetch(key)
|
|
5951
|
+
}
|
|
5952
|
+
} : {},
|
|
5953
|
+
...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
|
|
5954
|
+
...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
|
|
5955
|
+
...configuration.getDisplayState !== void 0 ? { getDisplayState: configuration.getDisplayState } : {},
|
|
5956
|
+
...initialBlankPaths !== void 0 ? { initialBlankPaths } : {},
|
|
5957
|
+
...resolvedIsSensitivePath !== void 0 ? { isSensitivePath: resolvedIsSensitivePath } : {}
|
|
5958
|
+
};
|
|
5959
|
+
const state = createFormStore(createOptions);
|
|
5960
|
+
registry.forms.set(
|
|
5961
|
+
key,
|
|
5962
|
+
state
|
|
5963
|
+
);
|
|
5964
|
+
return state;
|
|
5965
|
+
}
|
|
5966
|
+
let anonCounter = 0;
|
|
5967
|
+
let formInstanceCounter = 0;
|
|
5968
|
+
const ambientProvideHistory = paths.__DEV__ ? /* @__PURE__ */ new WeakMap() : null;
|
|
5969
|
+
function recordAmbientProvide(ssr) {
|
|
5970
|
+
if (!paths.__DEV__ || ssr || ambientProvideHistory === null) return;
|
|
5971
|
+
const instance = vue.getCurrentInstance();
|
|
5972
|
+
if (instance === null) return;
|
|
5973
|
+
const instanceKey = instance;
|
|
5974
|
+
const entry = {
|
|
5975
|
+
source: paths.captureUserCallSite()
|
|
5976
|
+
};
|
|
5977
|
+
const existing = ambientProvideHistory.get(instanceKey);
|
|
5978
|
+
if (existing === void 0) {
|
|
5979
|
+
ambientProvideHistory.set(instanceKey, [entry]);
|
|
5980
|
+
return;
|
|
5981
|
+
}
|
|
5982
|
+
existing.push(entry);
|
|
5983
|
+
}
|
|
5984
|
+
function resolveFormKey(key) {
|
|
5985
|
+
if (key !== void 0 && key !== null && key !== "") {
|
|
5986
|
+
if (key.startsWith(RESERVED_KEY_PREFIX)) {
|
|
5987
|
+
throw new paths.ReservedFormKeyError(key);
|
|
5626
5988
|
}
|
|
5627
|
-
|
|
5628
|
-
const userErrors = (existing.data.userErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5629
|
-
const schemaMap = new Map(schemaErrors.map(([k, v]) => [k, [...v]]));
|
|
5630
|
-
const userMap = new Map(userErrors.map(([k, v]) => [k, [...v]]));
|
|
5631
|
-
await adapter.setItem(
|
|
5632
|
-
key,
|
|
5633
|
-
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5634
|
-
);
|
|
5989
|
+
return key;
|
|
5635
5990
|
}
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
if (disposed) return Promise.resolve();
|
|
5639
|
-
return writer.flush().catch(() => void 0);
|
|
5991
|
+
if (vue.getCurrentInstance() !== null) {
|
|
5992
|
+
return `${ANONYMOUS_FORM_KEY_PREFIX}${vue.useId()}`;
|
|
5640
5993
|
}
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5994
|
+
return `${ANONYMOUS_FORM_KEY_PREFIX}${anonCounter++}`;
|
|
5995
|
+
}
|
|
5996
|
+
function warnOnSchemaFingerprintMismatch(key, existing, incoming) {
|
|
5997
|
+
let existingFp;
|
|
5998
|
+
let incomingFp;
|
|
5999
|
+
try {
|
|
6000
|
+
existingFp = existing.fingerprint();
|
|
6001
|
+
incomingFp = incoming.fingerprint();
|
|
6002
|
+
} catch (error) {
|
|
6003
|
+
console.error(
|
|
6004
|
+
`[attaform] fingerprint() threw for key "${key}"; skipping mismatch check.`,
|
|
6005
|
+
error
|
|
6006
|
+
);
|
|
6007
|
+
return;
|
|
5649
6008
|
}
|
|
5650
|
-
return
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
};
|
|
6009
|
+
if (existingFp === incomingFp) return;
|
|
6010
|
+
console.warn(
|
|
6011
|
+
`[attaform] useForm() calls with key "${key}" use different schemas; first wins, second is ignored. Use identical schemas or unique keys.
|
|
6012
|
+
existing: ${existingFp}
|
|
6013
|
+
incoming: ${incomingFp}`
|
|
6014
|
+
);
|
|
5657
6015
|
}
|
|
5658
|
-
function
|
|
5659
|
-
if (
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
6016
|
+
function warnOnPersistDivergence(key, existing, incomingPersist) {
|
|
6017
|
+
if (incomingPersist === void 0) return;
|
|
6018
|
+
const wired = existing.modules.get(PERSISTENCE_MODULE_KEY);
|
|
6019
|
+
const incomingNormalized = normalizePersistConfig(incomingPersist);
|
|
6020
|
+
if (wired === void 0) {
|
|
6021
|
+
console.warn(
|
|
6022
|
+
`[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.`
|
|
6023
|
+
);
|
|
6024
|
+
return;
|
|
6025
|
+
}
|
|
6026
|
+
if (persistConfigsEquivalent(wired.wiredConfig, incomingNormalized)) return;
|
|
6027
|
+
console.warn(
|
|
6028
|
+
`[attaform] useForm({ key: "${key}" }) passed a persist config that differs from the first useForm({ key }) call's; first wins, this one is ignored.
|
|
6029
|
+
wired: ${describePersist(wired.wiredConfig)}
|
|
6030
|
+
incoming: ${describePersist(incomingNormalized)}`
|
|
6031
|
+
);
|
|
6032
|
+
}
|
|
6033
|
+
function persistConfigsEquivalent(a, b) {
|
|
6034
|
+
if (a.storage !== b.storage) return false;
|
|
6035
|
+
if ((a.key ?? void 0) !== (b.key ?? void 0)) return false;
|
|
6036
|
+
if ((a.debounceMs ?? void 0) !== (b.debounceMs ?? void 0)) return false;
|
|
6037
|
+
return true;
|
|
6038
|
+
}
|
|
6039
|
+
function describePersist(config) {
|
|
6040
|
+
const storage = typeof config.storage === "string" ? config.storage : "custom-adapter";
|
|
6041
|
+
const parts = [`storage=${storage}`];
|
|
6042
|
+
if (config.key !== void 0) parts.push(`key=${config.key}`);
|
|
6043
|
+
if (config.debounceMs !== void 0) parts.push(`debounceMs=${config.debounceMs}`);
|
|
6044
|
+
return `{ ${parts.join(", ")} }`;
|
|
5663
6045
|
}
|
|
5664
6046
|
const warnedAnonPersistKeys = /* @__PURE__ */ new Set();
|
|
5665
6047
|
function enforceAnonPersistRule(formKey, ssr) {
|
|
@@ -5677,33 +6059,6 @@ function enforceAnonPersistRule(formKey, ssr) {
|
|
|
5677
6059
|
}
|
|
5678
6060
|
return true;
|
|
5679
6061
|
}
|
|
5680
|
-
function collectPersistedLeafPaths(form) {
|
|
5681
|
-
const out = [];
|
|
5682
|
-
walk(form, []);
|
|
5683
|
-
return out;
|
|
5684
|
-
function walk(node, prefix) {
|
|
5685
|
-
if (Array.isArray(node)) {
|
|
5686
|
-
for (let i = 0; i < node.length; i++) {
|
|
5687
|
-
walk(node[i], [...prefix, i]);
|
|
5688
|
-
}
|
|
5689
|
-
return;
|
|
5690
|
-
}
|
|
5691
|
-
if (isPlainRecord(node)) {
|
|
5692
|
-
for (const key of Object.keys(node)) {
|
|
5693
|
-
walk(node[key], [...prefix, key]);
|
|
5694
|
-
}
|
|
5695
|
-
return;
|
|
5696
|
-
}
|
|
5697
|
-
if (prefix.length === 0) return;
|
|
5698
|
-
out.push(paths.canonicalizePath(prefix).key);
|
|
5699
|
-
}
|
|
5700
|
-
}
|
|
5701
|
-
function isDescendantPathKey(candidate, ancestor) {
|
|
5702
|
-
if (candidate.length <= ancestor.length) return false;
|
|
5703
|
-
if (!ancestor.endsWith("]")) return false;
|
|
5704
|
-
const childPrefix = `${ancestor.slice(0, -1)},`;
|
|
5705
|
-
return candidate.startsWith(childPrefix);
|
|
5706
|
-
}
|
|
5707
6062
|
|
|
5708
6063
|
let injectedInstanceCounter = 0;
|
|
5709
6064
|
function injectForm(input) {
|
|
@@ -5783,8 +6138,6 @@ function isLazyMarker(value) {
|
|
|
5783
6138
|
}
|
|
5784
6139
|
|
|
5785
6140
|
const NOOP_WIZARD_HISTORY = {
|
|
5786
|
-
push() {
|
|
5787
|
-
},
|
|
5788
6141
|
replace() {
|
|
5789
6142
|
},
|
|
5790
6143
|
read() {
|
|
@@ -5811,21 +6164,16 @@ function createWizardHistory(param) {
|
|
|
5811
6164
|
for (const subscriber of subscribers) subscriber(value);
|
|
5812
6165
|
}
|
|
5813
6166
|
window.addEventListener("popstate", handlePopstate);
|
|
5814
|
-
function
|
|
6167
|
+
function safeReplaceState(key) {
|
|
5815
6168
|
try {
|
|
5816
|
-
|
|
5817
|
-
fn.call(window.history, {}, "", buildUrl(key));
|
|
6169
|
+
window.history.replaceState({}, "", buildUrl(key));
|
|
5818
6170
|
} catch {
|
|
5819
6171
|
}
|
|
5820
6172
|
}
|
|
5821
6173
|
return {
|
|
5822
|
-
push(key) {
|
|
5823
|
-
if (disposed) return;
|
|
5824
|
-
safeWriteState(key, "push");
|
|
5825
|
-
},
|
|
5826
6174
|
replace(key) {
|
|
5827
6175
|
if (disposed) return;
|
|
5828
|
-
|
|
6176
|
+
safeReplaceState(key);
|
|
5829
6177
|
},
|
|
5830
6178
|
read() {
|
|
5831
6179
|
const url = new URL(window.location.href);
|
|
@@ -5884,68 +6232,16 @@ function buildWizardStatusesProxy(statuses) {
|
|
|
5884
6232
|
}
|
|
5885
6233
|
return result;
|
|
5886
6234
|
});
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
return computedEntry.value;
|
|
5898
|
-
},
|
|
5899
|
-
get(_, key) {
|
|
5900
|
-
if (typeof key === "symbol") {
|
|
5901
|
-
if (key === Symbol.toPrimitive) return proxyToPrimitive;
|
|
5902
|
-
return Reflect.get(target, key);
|
|
5903
|
-
}
|
|
5904
|
-
if (key === "toJSON") return () => snapshot.value;
|
|
5905
|
-
if (key === "toString") return proxyToString;
|
|
5906
|
-
if (key === "valueOf")
|
|
5907
|
-
return function() {
|
|
5908
|
-
return this;
|
|
5909
|
-
};
|
|
5910
|
-
const computedEntry = statuses[key];
|
|
5911
|
-
if (computedEntry === void 0) return void 0;
|
|
5912
|
-
return computedEntry.value;
|
|
5913
|
-
},
|
|
5914
|
-
has(_, key) {
|
|
5915
|
-
if (typeof key === "symbol") return Reflect.has(target, key);
|
|
5916
|
-
return Object.hasOwn(statuses, key);
|
|
5917
|
-
},
|
|
5918
|
-
ownKeys() {
|
|
5919
|
-
return Object.keys(statuses);
|
|
5920
|
-
},
|
|
5921
|
-
getOwnPropertyDescriptor(_, key) {
|
|
5922
|
-
if (typeof key === "symbol") return void 0;
|
|
5923
|
-
const computedEntry = statuses[key];
|
|
5924
|
-
if (computedEntry === void 0) return void 0;
|
|
5925
|
-
return {
|
|
5926
|
-
configurable: true,
|
|
5927
|
-
enumerable: true,
|
|
5928
|
-
writable: false,
|
|
5929
|
-
value: computedEntry.value
|
|
5930
|
-
};
|
|
5931
|
-
},
|
|
5932
|
-
set(_, key) {
|
|
5933
|
-
if (paths.__DEV__) {
|
|
5934
|
-
console.warn(
|
|
5935
|
-
`[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.`
|
|
5936
|
-
);
|
|
5937
|
-
}
|
|
5938
|
-
return true;
|
|
5939
|
-
},
|
|
5940
|
-
deleteProperty(_, key) {
|
|
5941
|
-
if (paths.__DEV__) {
|
|
5942
|
-
console.warn(
|
|
5943
|
-
`[attaform] wizard.statuses is read-only \u2014 delete of "${String(key)}" was ignored.`
|
|
5944
|
-
);
|
|
5945
|
-
}
|
|
5946
|
-
return true;
|
|
5947
|
-
},
|
|
5948
|
-
defineProperty: () => true
|
|
6235
|
+
return buildCallableReadonlySnapshotProxy({
|
|
6236
|
+
surface: "wizard.statuses",
|
|
6237
|
+
snapshot: () => snapshot.value,
|
|
6238
|
+
resolveKey: (key) => statuses[key]?.value,
|
|
6239
|
+
// Single-key callable form. Strings stringify naturally; non-
|
|
6240
|
+
// string args coerce via `String(arg)` and miss the lookup, which
|
|
6241
|
+
// resolves to `undefined` (consistent with property-access).
|
|
6242
|
+
resolveCall: (arg) => statuses[String(arg)]?.value,
|
|
6243
|
+
ownKeys: () => Object.keys(statuses),
|
|
6244
|
+
hasKey: (key) => Object.hasOwn(statuses, key)
|
|
5949
6245
|
});
|
|
5950
6246
|
}
|
|
5951
6247
|
|
|
@@ -5962,6 +6258,12 @@ const NOOP_VALID_STATUS = {
|
|
|
5962
6258
|
submitted: false,
|
|
5963
6259
|
errorCount: 0
|
|
5964
6260
|
};
|
|
6261
|
+
function asStatusSource(form) {
|
|
6262
|
+
return form;
|
|
6263
|
+
}
|
|
6264
|
+
function asSubmissionSource(form) {
|
|
6265
|
+
return form;
|
|
6266
|
+
}
|
|
5965
6267
|
function useWizard(options) {
|
|
5966
6268
|
const rawSteps = Array.isArray(options.steps) ? options.steps : [];
|
|
5967
6269
|
if (rawSteps.length === 0 && paths.__DEV__) {
|
|
@@ -6049,10 +6351,12 @@ function useWizard(options) {
|
|
|
6049
6351
|
return { configurable: true, enumerable: true, writable: false, value: form };
|
|
6050
6352
|
}
|
|
6051
6353
|
});
|
|
6052
|
-
const slotCtx =
|
|
6354
|
+
const slotCtx = {
|
|
6053
6355
|
forms: slotForms,
|
|
6054
|
-
currentKey
|
|
6055
|
-
|
|
6356
|
+
get currentKey() {
|
|
6357
|
+
return activeKey.value === "" ? void 0 : activeKey.value;
|
|
6358
|
+
}
|
|
6359
|
+
};
|
|
6056
6360
|
function resolveSlot(slot, index, ctx) {
|
|
6057
6361
|
if (typeof slot === "string") {
|
|
6058
6362
|
return getOrBuildNoop(slot);
|
|
@@ -6075,12 +6379,6 @@ function useWizard(options) {
|
|
|
6075
6379
|
}
|
|
6076
6380
|
return result;
|
|
6077
6381
|
}
|
|
6078
|
-
const lazyCtx = {
|
|
6079
|
-
forms: slotForms,
|
|
6080
|
-
get currentKey() {
|
|
6081
|
-
return activeKey.value === "" ? void 0 : activeKey.value;
|
|
6082
|
-
}
|
|
6083
|
-
};
|
|
6084
6382
|
for (let i = 0; i < rawSteps.length; i++) {
|
|
6085
6383
|
const slot = rawSteps[i];
|
|
6086
6384
|
if (isLazyMarker(slot)) {
|
|
@@ -6090,18 +6388,17 @@ function useWizard(options) {
|
|
|
6090
6388
|
idx,
|
|
6091
6389
|
vue.computed(() => {
|
|
6092
6390
|
void lazyEpoch.value;
|
|
6093
|
-
return marker.resolve(
|
|
6391
|
+
return marker.resolve(slotCtx);
|
|
6094
6392
|
})
|
|
6095
6393
|
);
|
|
6096
6394
|
}
|
|
6097
6395
|
}
|
|
6098
6396
|
const compiledSteps = vue.computed(() => {
|
|
6099
|
-
const ctx = slotCtx.value;
|
|
6100
6397
|
const out = [];
|
|
6101
6398
|
const seen = /* @__PURE__ */ new Set();
|
|
6102
6399
|
for (let i = 0; i < rawSteps.length; i++) {
|
|
6103
6400
|
const slot = rawSteps[i];
|
|
6104
|
-
const form = resolveSlot(slot, i,
|
|
6401
|
+
const form = resolveSlot(slot, i, slotCtx);
|
|
6105
6402
|
if (form === void 0) continue;
|
|
6106
6403
|
if (seen.has(form.key)) {
|
|
6107
6404
|
if (paths.__DEV__) {
|
|
@@ -6127,9 +6424,12 @@ function useWizard(options) {
|
|
|
6127
6424
|
return -1;
|
|
6128
6425
|
});
|
|
6129
6426
|
const currentStep = vue.computed(() => {
|
|
6130
|
-
const
|
|
6131
|
-
|
|
6132
|
-
|
|
6427
|
+
const list = compiledSteps.value;
|
|
6428
|
+
const idx = activeIndex.value;
|
|
6429
|
+
if (idx >= 0 && idx < list.length) {
|
|
6430
|
+
return list[idx].key;
|
|
6431
|
+
}
|
|
6432
|
+
const first = list[0];
|
|
6133
6433
|
return first === void 0 ? void 0 : first.key;
|
|
6134
6434
|
});
|
|
6135
6435
|
const activeForm = vue.computed(() => {
|
|
@@ -6152,36 +6452,94 @@ function useWizard(options) {
|
|
|
6152
6452
|
for (const step of compiledSteps.value) out[step.key] = step.form;
|
|
6153
6453
|
return out;
|
|
6154
6454
|
});
|
|
6155
|
-
|
|
6156
|
-
const
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6455
|
+
function isFormReady(key) {
|
|
6456
|
+
const store = registry.forms.get(key);
|
|
6457
|
+
return store?.defaultsResolved.value === true;
|
|
6458
|
+
}
|
|
6459
|
+
function toWizardAggregateError(err, fallbackKey) {
|
|
6460
|
+
const entry = {
|
|
6461
|
+
formKey: err.formKey ?? fallbackKey,
|
|
6462
|
+
path: err.path,
|
|
6463
|
+
message: err.message
|
|
6464
|
+
};
|
|
6465
|
+
if (err.code !== void 0) entry.code = err.code;
|
|
6466
|
+
return entry;
|
|
6467
|
+
}
|
|
6468
|
+
const valuesCache = /* @__PURE__ */ new Map();
|
|
6469
|
+
function valuesFor(form) {
|
|
6470
|
+
const cached = valuesCache.get(form.key);
|
|
6471
|
+
if (cached !== void 0) return cached;
|
|
6472
|
+
const source = asStatusSource(form);
|
|
6473
|
+
const computedValues = vue.computed(() => source.values);
|
|
6474
|
+
valuesCache.set(form.key, computedValues);
|
|
6475
|
+
return computedValues;
|
|
6476
|
+
}
|
|
6477
|
+
const errorsCache = /* @__PURE__ */ new Map();
|
|
6478
|
+
function errorsFor(form) {
|
|
6479
|
+
const cached = errorsCache.get(form.key);
|
|
6480
|
+
if (cached !== void 0) return cached;
|
|
6481
|
+
const source = asStatusSource(form);
|
|
6482
|
+
const computedErrors = vue.computed(() => {
|
|
6483
|
+
if (!isFormReady(form.key)) return [];
|
|
6484
|
+
const errors = source.meta?.errors ?? [];
|
|
6485
|
+
const list = [];
|
|
6486
|
+
for (const err of errors) list.push(toWizardAggregateError(err, form.key));
|
|
6487
|
+
return list;
|
|
6488
|
+
});
|
|
6489
|
+
errorsCache.set(form.key, computedErrors);
|
|
6490
|
+
return computedErrors;
|
|
6491
|
+
}
|
|
6492
|
+
const allValues = new Proxy({}, {
|
|
6493
|
+
get(_, key) {
|
|
6494
|
+
if (typeof key !== "string") return void 0;
|
|
6495
|
+
const form = formsRecord.value[key];
|
|
6496
|
+
if (form === void 0) return void 0;
|
|
6497
|
+
return valuesFor(form).value;
|
|
6498
|
+
},
|
|
6499
|
+
has(_, key) {
|
|
6500
|
+
if (typeof key !== "string") return false;
|
|
6501
|
+
return formsRecord.value[key] !== void 0;
|
|
6502
|
+
},
|
|
6503
|
+
ownKeys() {
|
|
6504
|
+
return Object.keys(formsRecord.value);
|
|
6505
|
+
},
|
|
6506
|
+
getOwnPropertyDescriptor(_, key) {
|
|
6507
|
+
if (typeof key !== "string") return void 0;
|
|
6508
|
+
const form = formsRecord.value[key];
|
|
6509
|
+
if (form === void 0) return void 0;
|
|
6510
|
+
return {
|
|
6511
|
+
configurable: true,
|
|
6512
|
+
enumerable: true,
|
|
6513
|
+
writable: false,
|
|
6514
|
+
value: valuesFor(form).value
|
|
6515
|
+
};
|
|
6160
6516
|
}
|
|
6161
|
-
return out;
|
|
6162
6517
|
});
|
|
6163
|
-
const allErrors =
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
const
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6518
|
+
const allErrors = new Proxy({}, {
|
|
6519
|
+
get(_, key) {
|
|
6520
|
+
if (typeof key !== "string") return void 0;
|
|
6521
|
+
const form = formsRecord.value[key];
|
|
6522
|
+
if (form === void 0) return void 0;
|
|
6523
|
+
return errorsFor(form).value;
|
|
6524
|
+
},
|
|
6525
|
+
has(_, key) {
|
|
6526
|
+
if (typeof key !== "string") return false;
|
|
6527
|
+
return formsRecord.value[key] !== void 0;
|
|
6528
|
+
},
|
|
6529
|
+
ownKeys() {
|
|
6530
|
+
return Object.keys(formsRecord.value);
|
|
6531
|
+
},
|
|
6532
|
+
getOwnPropertyDescriptor(_, key) {
|
|
6533
|
+
if (typeof key !== "string") return void 0;
|
|
6534
|
+
const form = formsRecord.value[key];
|
|
6535
|
+
if (form === void 0) return void 0;
|
|
6536
|
+
return {
|
|
6537
|
+
configurable: true,
|
|
6538
|
+
enumerable: true,
|
|
6539
|
+
writable: false,
|
|
6540
|
+
value: errorsFor(form).value
|
|
6541
|
+
};
|
|
6183
6542
|
}
|
|
6184
|
-
return out;
|
|
6185
6543
|
});
|
|
6186
6544
|
const seedRef = vue.ref(void 0);
|
|
6187
6545
|
const seedInput = options.defaultStatuses;
|
|
@@ -6204,11 +6562,9 @@ function useWizard(options) {
|
|
|
6204
6562
|
function statusFor(form) {
|
|
6205
6563
|
const cached = statusCache.get(form.key);
|
|
6206
6564
|
if (cached !== void 0) return cached;
|
|
6207
|
-
const source = form;
|
|
6565
|
+
const source = asStatusSource(form);
|
|
6208
6566
|
const computedStatus = vue.computed(() => {
|
|
6209
|
-
|
|
6210
|
-
const resolved = store?.defaultsResolved.value === true;
|
|
6211
|
-
if (resolved) {
|
|
6567
|
+
if (isFormReady(form.key)) {
|
|
6212
6568
|
const meta = source.meta;
|
|
6213
6569
|
if (meta !== void 0 && meta !== null) {
|
|
6214
6570
|
return {
|
|
@@ -6351,7 +6707,7 @@ function useWizard(options) {
|
|
|
6351
6707
|
}
|
|
6352
6708
|
if (!registry.ssr) {
|
|
6353
6709
|
for (const step of compiledSteps.value) {
|
|
6354
|
-
const source = step.form;
|
|
6710
|
+
const source = asSubmissionSource(step.form);
|
|
6355
6711
|
if (typeof source.activate === "function") void source.activate();
|
|
6356
6712
|
}
|
|
6357
6713
|
}
|
|
@@ -6395,7 +6751,7 @@ function useWizard(options) {
|
|
|
6395
6751
|
const submissionAttempts = vue.ref(0);
|
|
6396
6752
|
const done = vue.ref(false);
|
|
6397
6753
|
function activateForm(form) {
|
|
6398
|
-
const source = form;
|
|
6754
|
+
const source = asSubmissionSource(form);
|
|
6399
6755
|
if (typeof source.activate === "function") {
|
|
6400
6756
|
void source.activate();
|
|
6401
6757
|
}
|
|
@@ -6495,7 +6851,7 @@ function useWizard(options) {
|
|
|
6495
6851
|
};
|
|
6496
6852
|
}
|
|
6497
6853
|
async function processOne(form) {
|
|
6498
|
-
const full = form;
|
|
6854
|
+
const full = asSubmissionSource(form);
|
|
6499
6855
|
let activationFailure;
|
|
6500
6856
|
try {
|
|
6501
6857
|
if (typeof full.activate === "function") await full.activate();
|
|
@@ -6527,15 +6883,7 @@ function useWizard(options) {
|
|
|
6527
6883
|
for (const step of compiledSteps.value) {
|
|
6528
6884
|
const processed = results.get(step.key);
|
|
6529
6885
|
if (processed === void 0 || processed.success === true) continue;
|
|
6530
|
-
for (const err of processed.errors)
|
|
6531
|
-
const entry = {
|
|
6532
|
-
formKey: err.formKey,
|
|
6533
|
-
path: err.path,
|
|
6534
|
-
message: err.message
|
|
6535
|
-
};
|
|
6536
|
-
if (err.code !== void 0) entry.code = err.code;
|
|
6537
|
-
out.push(entry);
|
|
6538
|
-
}
|
|
6886
|
+
for (const err of processed.errors) out.push(toWizardAggregateError(err, step.key));
|
|
6539
6887
|
}
|
|
6540
6888
|
return out;
|
|
6541
6889
|
}
|
|
@@ -6589,8 +6937,7 @@ function useWizard(options) {
|
|
|
6589
6937
|
if (processed !== void 0 && processed.success === true) {
|
|
6590
6938
|
valuesMap[step.key] = processed.data;
|
|
6591
6939
|
} else {
|
|
6592
|
-
|
|
6593
|
-
valuesMap[step.key] = source.values;
|
|
6940
|
+
valuesMap[step.key] = asStatusSource(step.form).values;
|
|
6594
6941
|
}
|
|
6595
6942
|
}
|
|
6596
6943
|
const ctx = buildSubmitContext(valuesMap, currentKey, final);
|
|
@@ -6611,8 +6958,11 @@ function useWizard(options) {
|
|
|
6611
6958
|
moveTo(firstFailedKey);
|
|
6612
6959
|
await vue.nextTick();
|
|
6613
6960
|
const failedForm = formsRecord.value[firstFailedKey];
|
|
6614
|
-
if (failedForm !== void 0
|
|
6615
|
-
failedForm
|
|
6961
|
+
if (failedForm !== void 0) {
|
|
6962
|
+
const failedSource = asSubmissionSource(failedForm);
|
|
6963
|
+
if (typeof failedSource.applyInvalidSubmitPolicy === "function") {
|
|
6964
|
+
failedSource.applyInvalidSubmitPolicy();
|
|
6965
|
+
}
|
|
6616
6966
|
}
|
|
6617
6967
|
}
|
|
6618
6968
|
}
|
|
@@ -6627,7 +6977,7 @@ function useWizard(options) {
|
|
|
6627
6977
|
done.value = false;
|
|
6628
6978
|
lazyEpoch.value += 1;
|
|
6629
6979
|
for (const step of compiledSteps.value) {
|
|
6630
|
-
const full = step.form;
|
|
6980
|
+
const full = asSubmissionSource(step.form);
|
|
6631
6981
|
if (typeof full.reset === "function") full.reset();
|
|
6632
6982
|
}
|
|
6633
6983
|
const firstStep = compiledSteps.value[0];
|
|
@@ -6677,12 +7027,8 @@ function useWizard(options) {
|
|
|
6677
7027
|
return count.value;
|
|
6678
7028
|
},
|
|
6679
7029
|
statuses,
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
},
|
|
6683
|
-
get allErrors() {
|
|
6684
|
-
return allErrors.value;
|
|
6685
|
-
},
|
|
7030
|
+
allValues,
|
|
7031
|
+
allErrors,
|
|
6686
7032
|
get progress() {
|
|
6687
7033
|
return progress.value;
|
|
6688
7034
|
},
|
|
@@ -6830,4 +7176,4 @@ exports.slimKindOf = slimKindOf;
|
|
|
6830
7176
|
exports.unset = unset;
|
|
6831
7177
|
exports.useAbstractForm = useAbstractForm;
|
|
6832
7178
|
exports.useWizard = useWizard;
|
|
6833
|
-
//# sourceMappingURL=attaform.
|
|
7179
|
+
//# sourceMappingURL=attaform.DL4CQ-oW.cjs.map
|