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