attaform 0.19.0 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/chunks/devtools.cjs +1 -1
- package/dist/chunks/devtools.mjs +1 -1
- package/dist/chunks/indexeddb.cjs +1 -1
- package/dist/chunks/indexeddb.mjs +1 -1
- package/dist/chunks/local-storage.cjs +1 -1
- package/dist/chunks/local-storage.mjs +1 -1
- package/dist/chunks/session-storage.cjs +1 -1
- package/dist/chunks/session-storage.mjs +1 -1
- package/dist/index.cjs +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.BCBxTyMC.cjs +1882 -0
- package/dist/shared/attaform.BCBxTyMC.cjs.map +1 -0
- 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.BTpuvGec.d.ts → attaform.Bh3ACtts.d.ts} +109 -101
- package/dist/shared/{attaform.BTi-PsHr.mjs → attaform.BqZuwLTK.mjs} +1868 -1477
- package/dist/shared/attaform.BqZuwLTK.mjs.map +1 -0
- package/dist/shared/{attaform.JBx8cfMA.cjs → attaform.BrrXNmfK.cjs} +263 -799
- package/dist/shared/attaform.BrrXNmfK.cjs.map +1 -0
- package/dist/shared/{attaform.CXpzmj38.mjs → attaform.BupwXkj_.mjs} +213 -270
- package/dist/shared/attaform.BupwXkj_.mjs.map +1 -0
- package/dist/shared/{attaform.ePUcKxId.d.cts → attaform.D5-1XGQU.d.cts} +109 -101
- package/dist/shared/attaform.D6CwqkPx.mjs +1876 -0
- package/dist/shared/attaform.D6CwqkPx.mjs.map +1 -0
- package/dist/shared/attaform.DHRWn-cu.cjs +785 -0
- package/dist/shared/attaform.DHRWn-cu.cjs.map +1 -0
- package/dist/shared/{attaform.C1msmO2v.cjs → attaform.DLnE5bZa.cjs} +1798 -1405
- package/dist/shared/attaform.DLnE5bZa.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.CRNA0vrd.d.mts → attaform.DoKXru-a.d.mts} +1 -1
- 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.a3uBo-gw.mjs → attaform.iWo9soNX.mjs} +257 -793
- package/dist/shared/attaform.iWo9soNX.mjs.map +1 -0
- package/dist/shared/attaform.tVkmQh5w.mjs +774 -0
- package/dist/shared/attaform.tVkmQh5w.mjs.map +1 -0
- package/dist/transforms.cjs +2 -2
- package/dist/transforms.d.cts +22 -13
- package/dist/transforms.d.mts +22 -13
- package/dist/transforms.d.ts +22 -13
- package/dist/transforms.mjs +1 -1
- package/dist/vite.cjs +8 -7
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.mjs +8 -7
- package/dist/vite.mjs.map +1 -1
- package/dist/zod-v3.cjs +3 -3
- package/dist/zod-v3.d.cts +32 -6
- package/dist/zod-v3.d.mts +32 -6
- package/dist/zod-v3.d.ts +32 -6
- package/dist/zod-v3.mjs +3 -3
- package/dist/zod-v4.cjs +3 -3
- package/dist/zod-v4.d.cts +12 -8
- package/dist/zod-v4.d.mts +12 -8
- package/dist/zod-v4.d.ts +12 -8
- package/dist/zod-v4.mjs +3 -3
- package/dist/zod.cjs +8 -8
- package/dist/zod.cjs.map +1 -1
- package/dist/zod.d.cts +6 -6
- package/dist/zod.d.mts +6 -6
- package/dist/zod.d.ts +6 -6
- package/dist/zod.mjs +6 -6
- package/package.json +2 -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,28 @@
|
|
|
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
|
+
|
|
4
|
+
function safeAssign(target, key, value) {
|
|
5
|
+
if (key === "__proto__") {
|
|
6
|
+
Object.defineProperty(target, key, {
|
|
7
|
+
value,
|
|
8
|
+
writable: true,
|
|
9
|
+
enumerable: true,
|
|
10
|
+
configurable: true
|
|
11
|
+
});
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
target[key] = value;
|
|
15
|
+
}
|
|
16
|
+
function safeOwnRead(target, key) {
|
|
17
|
+
if (key === "__proto__") {
|
|
18
|
+
const desc = Object.getOwnPropertyDescriptor(target, "__proto__");
|
|
19
|
+
return desc?.value;
|
|
20
|
+
}
|
|
21
|
+
return target[key];
|
|
22
|
+
}
|
|
23
|
+
function safeOwnHas(target, key) {
|
|
24
|
+
return Object.prototype.hasOwnProperty.call(target, key);
|
|
25
|
+
}
|
|
3
26
|
|
|
4
27
|
const NOT_FOUND = Symbol("NOT_FOUND");
|
|
5
28
|
function descendStep(value, segment) {
|
|
@@ -63,7 +86,7 @@ function setAtPathOffset(root, path, value, offset) {
|
|
|
63
86
|
return arr;
|
|
64
87
|
}
|
|
65
88
|
const rec = isPlainRecord(root) ? { ...root } : {};
|
|
66
|
-
rec
|
|
89
|
+
safeAssign(rec, head, setAtPathOffset(safeOwnRead(rec, head), path, value, nextOffset));
|
|
67
90
|
return rec;
|
|
68
91
|
}
|
|
69
92
|
function deleteAtPath(root, path) {
|
|
@@ -92,9 +115,9 @@ function deleteAtPathOffset(root, path, offset) {
|
|
|
92
115
|
delete rec2[head];
|
|
93
116
|
return rec2;
|
|
94
117
|
}
|
|
95
|
-
if (!(head
|
|
118
|
+
if (!safeOwnHas(root, head)) return root;
|
|
96
119
|
const rec = { ...root };
|
|
97
|
-
rec
|
|
120
|
+
safeAssign(rec, head, deleteAtPathOffset(safeOwnRead(rec, head), path, nextOffset));
|
|
98
121
|
return rec;
|
|
99
122
|
}
|
|
100
123
|
function resolveArrayShape(schema, scratch) {
|
|
@@ -165,25 +188,25 @@ function mergeStructuralImpl(schema, scratch, consumer, defaultValue) {
|
|
|
165
188
|
let mutated = false;
|
|
166
189
|
const out = { ...consumer };
|
|
167
190
|
for (const key of Object.keys(defaultValue)) {
|
|
168
|
-
if (!(key
|
|
169
|
-
const defAtKey = defaultValue
|
|
191
|
+
if (!safeOwnHas(consumer, key)) {
|
|
192
|
+
const defAtKey = safeOwnRead(defaultValue, key);
|
|
170
193
|
scratch.push(key);
|
|
171
194
|
const filled = mergeStructuralImpl(schema, scratch, void 0, defAtKey);
|
|
172
195
|
scratch.pop();
|
|
173
196
|
if (filled !== void 0) {
|
|
174
|
-
out
|
|
197
|
+
safeAssign(out, key, filled);
|
|
175
198
|
mutated = true;
|
|
176
199
|
}
|
|
177
200
|
}
|
|
178
201
|
}
|
|
179
202
|
for (const key of Object.keys(consumer)) {
|
|
180
|
-
const cVal = consumer
|
|
203
|
+
const cVal = safeOwnRead(consumer, key);
|
|
181
204
|
if (cVal === void 0) continue;
|
|
182
205
|
scratch.push(key);
|
|
183
|
-
const merged = mergeStructuralImpl(schema, scratch, cVal, defaultValue
|
|
206
|
+
const merged = mergeStructuralImpl(schema, scratch, cVal, safeOwnRead(defaultValue, key));
|
|
184
207
|
scratch.pop();
|
|
185
208
|
if (merged !== cVal) {
|
|
186
|
-
out
|
|
209
|
+
safeAssign(out, key, merged);
|
|
187
210
|
mutated = true;
|
|
188
211
|
}
|
|
189
212
|
}
|
|
@@ -377,7 +400,8 @@ function applyChangedKeys(target, source) {
|
|
|
377
400
|
}
|
|
378
401
|
for (const k of changedFirstSegments) {
|
|
379
402
|
if (typeof k === "symbol") continue;
|
|
380
|
-
|
|
403
|
+
const key = String(k);
|
|
404
|
+
safeAssign(t, key, safeOwnRead(s, key));
|
|
381
405
|
}
|
|
382
406
|
}
|
|
383
407
|
return true;
|
|
@@ -429,7 +453,7 @@ function structuralSnapshot(value) {
|
|
|
429
453
|
const src = value;
|
|
430
454
|
const out = {};
|
|
431
455
|
for (const k of Object.keys(src)) {
|
|
432
|
-
out
|
|
456
|
+
safeAssign(out, k, structuralSnapshot(safeOwnRead(src, k)));
|
|
433
457
|
}
|
|
434
458
|
return out;
|
|
435
459
|
}
|
|
@@ -488,7 +512,7 @@ function hashStableString(input, seed = 0) {
|
|
|
488
512
|
const ANON_STEM = "atta";
|
|
489
513
|
function readableFormKeyStem(formKey) {
|
|
490
514
|
if (formKey === "" || formKey.startsWith(ANONYMOUS_FORM_KEY_PREFIX)) return ANON_STEM;
|
|
491
|
-
const sanitized = formKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(
|
|
515
|
+
const sanitized = formKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|(?<!-)-+$/g, "");
|
|
492
516
|
return sanitized === "" ? ANON_STEM : sanitized;
|
|
493
517
|
}
|
|
494
518
|
function fieldIdToken(formInstanceId, pathKey) {
|
|
@@ -522,6 +546,7 @@ function humanize(segment) {
|
|
|
522
546
|
}).join(" ");
|
|
523
547
|
}
|
|
524
548
|
|
|
549
|
+
const warnedDisplayStatePredicates = /* @__PURE__ */ new WeakSet();
|
|
525
550
|
function isUnderStubAncestor(state, segments) {
|
|
526
551
|
for (let i = 0; i < segments.length; i++) {
|
|
527
552
|
const ancestorPath = segments.slice(0, i);
|
|
@@ -605,7 +630,7 @@ function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBa
|
|
|
605
630
|
function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
606
631
|
const formValue = state.form.value;
|
|
607
632
|
const value = state.getValueAtPath(segments);
|
|
608
|
-
const original = state.originals.get(
|
|
633
|
+
const original = state.originals.get(key)?.value;
|
|
609
634
|
let pristine = true;
|
|
610
635
|
let blank = true;
|
|
611
636
|
let dirty = false;
|
|
@@ -618,13 +643,12 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
618
643
|
let validating = false;
|
|
619
644
|
let updatedAt = null;
|
|
620
645
|
let asyncPending = false;
|
|
621
|
-
for (const [, entry] of state.originals) {
|
|
646
|
+
for (const [leafKey, entry] of state.originals) {
|
|
622
647
|
if (!isPathPrefix(segments, entry.segments)) continue;
|
|
623
648
|
if (segments.length === entry.segments.length) continue;
|
|
624
649
|
if (!hasAtPath(formValue, entry.segments)) continue;
|
|
625
|
-
const leafKey = canonicalizePath(entry.segments).key;
|
|
626
650
|
const leafRecord = state.fields.get(leafKey);
|
|
627
|
-
if (!state.
|
|
651
|
+
if (!state.isPristineAtPathByKey(leafKey, entry.segments)) {
|
|
628
652
|
pristine = false;
|
|
629
653
|
dirty = true;
|
|
630
654
|
}
|
|
@@ -636,7 +660,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
636
660
|
if (leafRecord?.blurredAfterInteraction === true) blurredAfterInteraction = true;
|
|
637
661
|
if (leafRecord?.connected === true) connected = true;
|
|
638
662
|
if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0) validating = true;
|
|
639
|
-
if (state.
|
|
663
|
+
if (state.pathHasAsyncValidationByKey(leafKey, entry.segments)) asyncPending = true;
|
|
640
664
|
const ts = leafRecord?.updatedAt;
|
|
641
665
|
if (ts !== void 0 && ts !== null) {
|
|
642
666
|
if (updatedAt === null || ts > updatedAt) updatedAt = ts;
|
|
@@ -691,7 +715,14 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState)
|
|
|
691
715
|
let displayState;
|
|
692
716
|
try {
|
|
693
717
|
displayState = predicate(base, formMeta);
|
|
694
|
-
} catch {
|
|
718
|
+
} catch (err) {
|
|
719
|
+
if (__DEV__ && !warnedDisplayStatePredicates.has(predicate)) {
|
|
720
|
+
warnedDisplayStatePredicates.add(predicate);
|
|
721
|
+
console.warn(
|
|
722
|
+
"[attaform] custom getDisplayState threw \u2014 falling back to defaultDisplayState. Subsequent throws from the same predicate will not warn again.",
|
|
723
|
+
err
|
|
724
|
+
);
|
|
725
|
+
}
|
|
695
726
|
displayState = defaultDisplayState(base, formMeta);
|
|
696
727
|
}
|
|
697
728
|
return {
|
|
@@ -728,6 +759,41 @@ function aggregateErrorsAt(state, prefix) {
|
|
|
728
759
|
}
|
|
729
760
|
const EMPTY_ELEMENTS = Object.freeze([]);
|
|
730
761
|
|
|
762
|
+
function liveKeysAtPath(state, segments) {
|
|
763
|
+
const value = getAtPath(state.form.value, segments);
|
|
764
|
+
if (value === null || value === void 0) return [];
|
|
765
|
+
if (Array.isArray(value)) {
|
|
766
|
+
const keys = new Array(value.length);
|
|
767
|
+
for (let i = 0; i < value.length; i += 1) keys[i] = String(i);
|
|
768
|
+
return keys;
|
|
769
|
+
}
|
|
770
|
+
if (typeof value === "object") return Object.keys(value);
|
|
771
|
+
return [];
|
|
772
|
+
}
|
|
773
|
+
function isArrayPath(state, segments) {
|
|
774
|
+
if (segments.length === 0) return false;
|
|
775
|
+
return Array.isArray(getAtPath(state.form.value, segments));
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
function warnReadOnly(surface, action, key) {
|
|
779
|
+
if (!__DEV__) return;
|
|
780
|
+
const phrase = action === "write" ? `write to "${String(key)}"` : `${action} of "${String(key)}"`;
|
|
781
|
+
console.warn(
|
|
782
|
+
`[attaform] ${surface} is read-only \u2014 ${phrase} was ignored. Mutate the form via setValue / the directive / field-array helpers instead.`
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
function makeReadonlyCoercion(snapshot) {
|
|
786
|
+
const toString = () => JSON.stringify(snapshot());
|
|
787
|
+
return {
|
|
788
|
+
toString,
|
|
789
|
+
valueOf() {
|
|
790
|
+
return this;
|
|
791
|
+
},
|
|
792
|
+
toJSON: snapshot,
|
|
793
|
+
toPrimitive: (hint) => hint === "number" ? NaN : toString()
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
|
|
731
797
|
const INTEGER_SEGMENT = /^(?:0|[1-9]\d*)$/;
|
|
732
798
|
function keyToSegment(key) {
|
|
733
799
|
return INTEGER_SEGMENT.test(key) ? Number(key) : key;
|
|
@@ -759,12 +825,12 @@ function buildSurfaceProxy(opts) {
|
|
|
759
825
|
const existing = containerCache.get(cacheKey);
|
|
760
826
|
if (existing !== void 0) return existing;
|
|
761
827
|
const snapshotContainer = () => opts.materializeContainer === void 0 ? {} : opts.materializeContainer(segments);
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
828
|
+
const {
|
|
829
|
+
toString: containerToString,
|
|
830
|
+
valueOf: containerValueOf,
|
|
831
|
+
toJSON: containerToJSON,
|
|
832
|
+
toPrimitive: containerToPrimitive
|
|
833
|
+
} = makeReadonlyCoercion(snapshotContainer);
|
|
768
834
|
const target = isArrayLike ? [] : (() => {
|
|
769
835
|
});
|
|
770
836
|
const proxy = new Proxy(target, {
|
|
@@ -789,6 +855,9 @@ function buildSurfaceProxy(opts) {
|
|
|
789
855
|
return opts.containerOwnKeys === void 0 ? 0 : opts.containerOwnKeys(segments).length;
|
|
790
856
|
}
|
|
791
857
|
const childSegs = [...segments, keyToSegment(key)];
|
|
858
|
+
if ((isArrayLike || opts.isArrayContainer?.(segments) === true) && typeof keyToSegment(key) === "string" && key in Array.prototype) {
|
|
859
|
+
return Reflect.get(Array.prototype, key);
|
|
860
|
+
}
|
|
792
861
|
if (key === "toString" || key === "valueOf") {
|
|
793
862
|
if (!schemaHasPath(childSegs)) {
|
|
794
863
|
return key === "toString" ? containerToString : containerValueOf;
|
|
@@ -833,10 +902,25 @@ function buildSurfaceProxy(opts) {
|
|
|
833
902
|
};
|
|
834
903
|
},
|
|
835
904
|
// Block writes at the proxy boundary. Mutations go through
|
|
836
|
-
// `setValue`, the directive, or the field-array helpers.
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
905
|
+
// `setValue`, the directive, or the field-array helpers. Each
|
|
906
|
+
// trap returns `true` (warn-and-noop) — returning `false` from a
|
|
907
|
+
// `set`/`delete`/`defineProperty` trap throws `TypeError` under
|
|
908
|
+
// strict mode (every ESM / `<script setup>`), which would surface
|
|
909
|
+
// a host-level exception in consumer code that the library
|
|
910
|
+
// documents as "writes are ignored." Aligns with the contract on
|
|
911
|
+
// `form.values` / `wizard.statuses`.
|
|
912
|
+
set: (_, key) => {
|
|
913
|
+
warnReadOnly("form.fields / form.errors", "write", key);
|
|
914
|
+
return true;
|
|
915
|
+
},
|
|
916
|
+
deleteProperty: (_, key) => {
|
|
917
|
+
warnReadOnly("form.fields / form.errors", "delete", key);
|
|
918
|
+
return true;
|
|
919
|
+
},
|
|
920
|
+
defineProperty: (_, key) => {
|
|
921
|
+
warnReadOnly("form.fields / form.errors", "define", key);
|
|
922
|
+
return true;
|
|
923
|
+
}
|
|
840
924
|
});
|
|
841
925
|
containerCache.set(cacheKey, proxy);
|
|
842
926
|
return proxy;
|
|
@@ -858,11 +942,12 @@ function buildSurfaceProxy(opts) {
|
|
|
858
942
|
}
|
|
859
943
|
return snapshot;
|
|
860
944
|
};
|
|
861
|
-
const
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
945
|
+
const {
|
|
946
|
+
toString: leafToString,
|
|
947
|
+
valueOf: leafValueOf,
|
|
948
|
+
toJSON: leafToJSONHandler,
|
|
949
|
+
toPrimitive: leafToPrimitive
|
|
950
|
+
} = makeReadonlyCoercion(snapshotLeaf);
|
|
866
951
|
const target = (() => {
|
|
867
952
|
});
|
|
868
953
|
const proxy = new Proxy(target, {
|
|
@@ -880,7 +965,7 @@ function buildSurfaceProxy(opts) {
|
|
|
880
965
|
if (typeof key !== "string") return void 0;
|
|
881
966
|
if (key === "toString") return leafToString;
|
|
882
967
|
if (key === "valueOf") return leafValueOf;
|
|
883
|
-
if (key === "toJSON") return
|
|
968
|
+
if (key === "toJSON") return leafToJSONHandler;
|
|
884
969
|
if (leafKeys.has(key)) {
|
|
885
970
|
const leaf = opts.resolveLeaf(segments);
|
|
886
971
|
return readLeafKey(leaf, key);
|
|
@@ -907,9 +992,22 @@ function buildSurfaceProxy(opts) {
|
|
|
907
992
|
writable: false
|
|
908
993
|
};
|
|
909
994
|
},
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
995
|
+
// Same warn-and-noop contract as the container traps above.
|
|
996
|
+
// Returning `true` keeps strict-mode callers from throwing on
|
|
997
|
+
// `form.fields.email.value = …`; the actual readonly guarantee
|
|
998
|
+
// is the absence of any mutation, not the host-level reject.
|
|
999
|
+
set: (_, key) => {
|
|
1000
|
+
warnReadOnly("form.fields.<leaf>", "write", key);
|
|
1001
|
+
return true;
|
|
1002
|
+
},
|
|
1003
|
+
deleteProperty: (_, key) => {
|
|
1004
|
+
warnReadOnly("form.fields.<leaf>", "delete", key);
|
|
1005
|
+
return true;
|
|
1006
|
+
},
|
|
1007
|
+
defineProperty: (_, key) => {
|
|
1008
|
+
warnReadOnly("form.fields.<leaf>", "define", key);
|
|
1009
|
+
return true;
|
|
1010
|
+
}
|
|
913
1011
|
});
|
|
914
1012
|
leafViewCache.set(cacheKey, proxy);
|
|
915
1013
|
return proxy;
|
|
@@ -973,29 +1071,49 @@ function buildErrorsProxy(state) {
|
|
|
973
1071
|
// working — the call-form just extends that semantic to
|
|
974
1072
|
// containers and dynamic paths.
|
|
975
1073
|
resolveCallTarget: (path) => aggregateErrorsAt(state, path),
|
|
976
|
-
//
|
|
977
|
-
//
|
|
978
|
-
//
|
|
979
|
-
//
|
|
980
|
-
//
|
|
981
|
-
|
|
982
|
-
|
|
1074
|
+
// Enumeration unions the live form-data keys at this path with the
|
|
1075
|
+
// first-child segments drawn from every error store. Without the
|
|
1076
|
+
// union, `Object.keys(form.errors)` / `{...form.errors}` /
|
|
1077
|
+
// `v-for="(errs, k) in form.errors"` silently dropped two
|
|
1078
|
+
// important error classes that the dot / call / JSON.stringify
|
|
1079
|
+
// surfaces already exposed:
|
|
1080
|
+
//
|
|
1081
|
+
// - **Form-level** errors at the synthetic `['']` path (set via
|
|
1082
|
+
// `setFormErrors` or root cross-field refines).
|
|
1083
|
+
// - **Server-only** errors at a key the schema doesn't know
|
|
1084
|
+
// about (`['ghost']`, `['address', 'ghost']`).
|
|
1085
|
+
//
|
|
1086
|
+
// The union closes that gap so `ownKeys` agrees with the rest of
|
|
1087
|
+
// the surface. Active-path filter mirrors `resolveLeaf`:
|
|
1088
|
+
// library-produced verdicts (schema + derived-blank) at unreachable
|
|
1089
|
+
// paths stay hidden; user-supplied errors are unconditional.
|
|
1090
|
+
containerOwnKeys: (segments) => errorAwareContainerKeys(state, segments),
|
|
1091
|
+
isArrayContainer: (segments) => isArrayPath(state, segments)
|
|
983
1092
|
});
|
|
984
1093
|
}
|
|
985
|
-
function
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
const
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1094
|
+
function errorAwareContainerKeys(state, segments) {
|
|
1095
|
+
const keys = new Set(liveKeysAtPath(state, segments));
|
|
1096
|
+
const formValue = state.form.value;
|
|
1097
|
+
const walk = (store, applyActivePathFilter) => {
|
|
1098
|
+
for (const [pathKey, errors] of store) {
|
|
1099
|
+
if (errors.length === 0) continue;
|
|
1100
|
+
if (pathKey === FORM_ERRORS_PATH_KEY) {
|
|
1101
|
+
if (segments.length === 0) keys.add("");
|
|
1102
|
+
continue;
|
|
1103
|
+
}
|
|
1104
|
+
const decoded = segmentsForPathKey(pathKey);
|
|
1105
|
+
if (decoded === null) continue;
|
|
1106
|
+
if (decoded.length <= segments.length) continue;
|
|
1107
|
+
if (!isPathPrefix(segments, decoded)) continue;
|
|
1108
|
+
if (applyActivePathFilter && !hasAtPath(formValue, decoded)) continue;
|
|
1109
|
+
const nextSeg = decoded[segments.length];
|
|
1110
|
+
keys.add(typeof nextSeg === "number" ? String(nextSeg) : nextSeg);
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
walk(state.schemaErrors, true);
|
|
1114
|
+
walk(state.derivedBlankErrors.value, true);
|
|
1115
|
+
walk(state.userErrors, false);
|
|
1116
|
+
return [...keys];
|
|
999
1117
|
}
|
|
1000
1118
|
function materializeErrors(state, containerSegments) {
|
|
1001
1119
|
const liveContainer = getAtPath(state.form.value, containerSegments);
|
|
@@ -1047,18 +1165,18 @@ function placeAt(tree, path, errors) {
|
|
|
1047
1165
|
const nextSeg = path[i + 1];
|
|
1048
1166
|
const key = typeof seg === "number" ? String(seg) : seg;
|
|
1049
1167
|
const cursorRecord2 = cursor;
|
|
1050
|
-
let child = cursorRecord2
|
|
1168
|
+
let child = safeOwnRead(cursorRecord2, key);
|
|
1051
1169
|
if (child === null || child === void 0 || typeof child !== "object") {
|
|
1052
1170
|
child = typeof nextSeg === "number" ? [] : {};
|
|
1053
|
-
cursorRecord2
|
|
1171
|
+
safeAssign(cursorRecord2, key, child);
|
|
1054
1172
|
}
|
|
1055
1173
|
cursor = child;
|
|
1056
1174
|
}
|
|
1057
1175
|
const lastSeg = path[path.length - 1];
|
|
1058
1176
|
const lastKey = typeof lastSeg === "number" ? String(lastSeg) : lastSeg;
|
|
1059
1177
|
const cursorRecord = cursor;
|
|
1060
|
-
const existing = cursorRecord
|
|
1061
|
-
cursorRecord
|
|
1178
|
+
const existing = safeOwnRead(cursorRecord, lastKey);
|
|
1179
|
+
safeAssign(cursorRecord, lastKey, Array.isArray(existing) ? [...existing, ...errors] : errors);
|
|
1062
1180
|
}
|
|
1063
1181
|
|
|
1064
1182
|
function buildFieldArrayApi(state) {
|
|
@@ -1088,9 +1206,10 @@ function buildFieldArrayApi(state) {
|
|
|
1088
1206
|
},
|
|
1089
1207
|
insert(path, index, value) {
|
|
1090
1208
|
const next = readArray(path);
|
|
1091
|
-
next.
|
|
1092
|
-
const
|
|
1093
|
-
|
|
1209
|
+
const preLen = next.length;
|
|
1210
|
+
const insertIndex = index < 0 ? Math.max(0, preLen + index) : Math.min(index, preLen);
|
|
1211
|
+
next.splice(insertIndex, 0, value);
|
|
1212
|
+
return writeArray(path, next, { kind: "insert", index: insertIndex });
|
|
1094
1213
|
},
|
|
1095
1214
|
remove(path, index) {
|
|
1096
1215
|
const next = readArray(path);
|
|
@@ -1212,9 +1331,34 @@ function buildFieldStateProxy(state, formInstanceId, getFormMetaBase, options) {
|
|
|
1212
1331
|
writable: false
|
|
1213
1332
|
};
|
|
1214
1333
|
},
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1334
|
+
// Warn-and-noop: returning `false` here throws `TypeError` under
|
|
1335
|
+
// strict mode (`form.fields('email').value = 1` from any ESM
|
|
1336
|
+
// module), which would violate the documented "writes are
|
|
1337
|
+
// ignored" contract. Mirrors `values-proxy` / `wizard-statuses-proxy`.
|
|
1338
|
+
set: (_, key) => {
|
|
1339
|
+
if (__DEV__) {
|
|
1340
|
+
console.warn(
|
|
1341
|
+
`[attaform] form.fields(path) is read-only \u2014 write to "${String(key)}" was ignored. Use form.setValue / the directive / field-array helpers instead.`
|
|
1342
|
+
);
|
|
1343
|
+
}
|
|
1344
|
+
return true;
|
|
1345
|
+
},
|
|
1346
|
+
deleteProperty: (_, key) => {
|
|
1347
|
+
if (__DEV__) {
|
|
1348
|
+
console.warn(
|
|
1349
|
+
`[attaform] form.fields(path) is read-only \u2014 delete of "${String(key)}" was ignored.`
|
|
1350
|
+
);
|
|
1351
|
+
}
|
|
1352
|
+
return true;
|
|
1353
|
+
},
|
|
1354
|
+
defineProperty: (_, key) => {
|
|
1355
|
+
if (__DEV__) {
|
|
1356
|
+
console.warn(
|
|
1357
|
+
`[attaform] form.fields(path) is read-only \u2014 define of "${String(key)}" was ignored.`
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
return true;
|
|
1361
|
+
}
|
|
1218
1362
|
});
|
|
1219
1363
|
terminalCache.set(cacheKey, proxy);
|
|
1220
1364
|
return proxy;
|
|
@@ -1230,21 +1374,6 @@ function buildFieldStateProxy(state, formInstanceId, getFormMetaBase, options) {
|
|
|
1230
1374
|
isArrayContainer: (segments) => isArrayPath(state, segments)
|
|
1231
1375
|
});
|
|
1232
1376
|
}
|
|
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
1377
|
function materializeFields(state, containerSegments, snapshotFieldStateAt) {
|
|
1249
1378
|
const liveValue = getAtPath(state.form.value, containerSegments);
|
|
1250
1379
|
return walk$2(liveValue, containerSegments, state.schema, snapshotFieldStateAt);
|
|
@@ -1288,7 +1417,7 @@ async function getStorageAdapter(storage) {
|
|
|
1288
1417
|
}
|
|
1289
1418
|
}
|
|
1290
1419
|
}
|
|
1291
|
-
const PERSISTED_ENVELOPE_VERSION =
|
|
1420
|
+
const PERSISTED_ENVELOPE_VERSION = 6;
|
|
1292
1421
|
function readPersistedPayload(value) {
|
|
1293
1422
|
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
1294
1423
|
const envelope = value;
|
|
@@ -1312,12 +1441,7 @@ function warnVersionMismatch(observedVersion) {
|
|
|
1312
1441
|
function buildPersistedPayload(form, include, schemaErrors, userErrors, blankPaths) {
|
|
1313
1442
|
let transientList;
|
|
1314
1443
|
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;
|
|
1444
|
+
transientList = [...blankPaths];
|
|
1321
1445
|
}
|
|
1322
1446
|
if (include === "form") {
|
|
1323
1447
|
if (transientList === void 0) return { v: PERSISTED_ENVELOPE_VERSION, data: { form } };
|
|
@@ -1339,30 +1463,35 @@ function buildPersistedPayload(form, include, schemaErrors, userErrors, blankPat
|
|
|
1339
1463
|
function createDebouncedWriter(write, debounceMs) {
|
|
1340
1464
|
let timer = null;
|
|
1341
1465
|
let pending = null;
|
|
1466
|
+
let writeGeneration = 0;
|
|
1467
|
+
function runWrite() {
|
|
1468
|
+
const gen = ++writeGeneration;
|
|
1469
|
+
pending = write().finally(() => {
|
|
1470
|
+
if (writeGeneration === gen) pending = null;
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1342
1473
|
function schedule() {
|
|
1343
1474
|
if (timer !== null) clearTimeout(timer);
|
|
1344
1475
|
if (debounceMs === 0) {
|
|
1345
|
-
|
|
1346
|
-
pending = null;
|
|
1347
|
-
});
|
|
1476
|
+
runWrite();
|
|
1348
1477
|
return;
|
|
1349
1478
|
}
|
|
1350
1479
|
timer = setTimeout(() => {
|
|
1351
1480
|
timer = null;
|
|
1352
|
-
|
|
1353
|
-
pending = null;
|
|
1354
|
-
});
|
|
1481
|
+
runWrite();
|
|
1355
1482
|
}, debounceMs);
|
|
1356
1483
|
}
|
|
1357
1484
|
async function flush() {
|
|
1358
1485
|
if (timer !== null) {
|
|
1359
1486
|
clearTimeout(timer);
|
|
1360
1487
|
timer = null;
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1488
|
+
runWrite();
|
|
1489
|
+
}
|
|
1490
|
+
while (pending !== null) {
|
|
1491
|
+
const awaited = pending;
|
|
1492
|
+
await awaited;
|
|
1493
|
+
if (pending === awaited) break;
|
|
1364
1494
|
}
|
|
1365
|
-
if (pending !== null) await pending;
|
|
1366
1495
|
}
|
|
1367
1496
|
function cancel() {
|
|
1368
1497
|
if (timer !== null) {
|
|
@@ -1375,7 +1504,7 @@ function createDebouncedWriter(write, debounceMs) {
|
|
|
1375
1504
|
function resolveStorageKeyBase(config, formKey) {
|
|
1376
1505
|
return config.key ?? `${PERSISTENCE_KEY_PREFIX}${formKey}`;
|
|
1377
1506
|
}
|
|
1378
|
-
async function
|
|
1507
|
+
async function removeMatchingKeys(adapter, base, keepKey) {
|
|
1379
1508
|
let keys;
|
|
1380
1509
|
try {
|
|
1381
1510
|
keys = await adapter.listKeys(base);
|
|
@@ -1383,12 +1512,15 @@ async function cleanupOrphanKeys(adapter, base, currentKey) {
|
|
|
1383
1512
|
return;
|
|
1384
1513
|
}
|
|
1385
1514
|
for (const key of keys) {
|
|
1386
|
-
if (key ===
|
|
1515
|
+
if (key === keepKey) continue;
|
|
1387
1516
|
if (key === base || key.startsWith(`${base}:`)) {
|
|
1388
1517
|
void adapter.removeItem(key).catch(() => void 0);
|
|
1389
1518
|
}
|
|
1390
1519
|
}
|
|
1391
1520
|
}
|
|
1521
|
+
async function cleanupOrphanKeys(adapter, base, currentKey) {
|
|
1522
|
+
await removeMatchingKeys(adapter, base, currentKey);
|
|
1523
|
+
}
|
|
1392
1524
|
const STANDARD_STORAGE_KINDS = ["local", "session", "indexeddb"];
|
|
1393
1525
|
function normalizePersistConfig(input) {
|
|
1394
1526
|
if (typeof input === "string") return { storage: input };
|
|
@@ -1399,12 +1531,7 @@ async function sweepAllOrphansAcrossStandardStores(base) {
|
|
|
1399
1531
|
for (const kind of STANDARD_STORAGE_KINDS) {
|
|
1400
1532
|
try {
|
|
1401
1533
|
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
|
-
}
|
|
1534
|
+
await removeMatchingKeys(adapter, base);
|
|
1408
1535
|
} catch {
|
|
1409
1536
|
}
|
|
1410
1537
|
}
|
|
@@ -1415,12 +1542,7 @@ async function sweepNonConfiguredStandardStoresForOrphans(configured, base) {
|
|
|
1415
1542
|
if (kind === configuredKind) continue;
|
|
1416
1543
|
try {
|
|
1417
1544
|
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
|
-
}
|
|
1545
|
+
await removeMatchingKeys(adapter, base);
|
|
1424
1546
|
} catch {
|
|
1425
1547
|
}
|
|
1426
1548
|
}
|
|
@@ -1436,6 +1558,29 @@ function pluckPaths(form, pathKeys) {
|
|
|
1436
1558
|
}
|
|
1437
1559
|
return sparse ?? {};
|
|
1438
1560
|
}
|
|
1561
|
+
function stripUnacknowledgedSensitiveLeaves(form, optedInPaths, isSensitivePath) {
|
|
1562
|
+
const acknowledgedSensitive = [];
|
|
1563
|
+
for (const key of optedInPaths) {
|
|
1564
|
+
const segs = segmentsForPathKey(key);
|
|
1565
|
+
if (segs !== null && isSensitivePath(segs)) acknowledgedSensitive.push(segs);
|
|
1566
|
+
}
|
|
1567
|
+
const coveredByAcknowledged = (path) => acknowledgedSensitive.some((prefix) => isPathPrefix(prefix, path));
|
|
1568
|
+
const walk = (path, value) => {
|
|
1569
|
+
if (path.length > 0 && isSensitivePath(path) && !coveredByAcknowledged(path)) {
|
|
1570
|
+
return void 0;
|
|
1571
|
+
}
|
|
1572
|
+
if (value === null || typeof value !== "object") return value;
|
|
1573
|
+
if (Array.isArray(value)) return value.map((item, i) => walk([...path, i], item));
|
|
1574
|
+
if (!isPlainRecord(value)) return value;
|
|
1575
|
+
const out = {};
|
|
1576
|
+
for (const key of Object.keys(value)) {
|
|
1577
|
+
const walked = walk([...path, key], value[key]);
|
|
1578
|
+
if (walked !== void 0) safeAssign(out, key, walked);
|
|
1579
|
+
}
|
|
1580
|
+
return out;
|
|
1581
|
+
};
|
|
1582
|
+
return walk([], form);
|
|
1583
|
+
}
|
|
1439
1584
|
function filterErrorsByPaths(errors, pathKeys) {
|
|
1440
1585
|
const out = /* @__PURE__ */ new Map();
|
|
1441
1586
|
for (const [key, value] of errors) {
|
|
@@ -1464,8 +1609,17 @@ function mergeDeep(target, source, path, schema) {
|
|
|
1464
1609
|
if (isPlainRecord(variantDefault)) {
|
|
1465
1610
|
const out2 = { ...variantDefault };
|
|
1466
1611
|
for (const key of Object.keys(sourceRecord)) {
|
|
1467
|
-
if (!(key
|
|
1468
|
-
|
|
1612
|
+
if (!safeOwnHas(variantDefault, key) && key !== du.discriminatorKey) continue;
|
|
1613
|
+
safeAssign(
|
|
1614
|
+
out2,
|
|
1615
|
+
key,
|
|
1616
|
+
mergeDeep(
|
|
1617
|
+
safeOwnRead(out2, key),
|
|
1618
|
+
safeOwnRead(sourceRecord, key),
|
|
1619
|
+
[...path, key],
|
|
1620
|
+
schema
|
|
1621
|
+
)
|
|
1622
|
+
);
|
|
1469
1623
|
}
|
|
1470
1624
|
return out2;
|
|
1471
1625
|
}
|
|
@@ -1476,7 +1630,16 @@ function mergeDeep(target, source, path, schema) {
|
|
|
1476
1630
|
const mergeTarget = target;
|
|
1477
1631
|
const out = isPlainRecord(mergeTarget) ? { ...mergeTarget } : {};
|
|
1478
1632
|
for (const key of Object.keys(source)) {
|
|
1479
|
-
|
|
1633
|
+
safeAssign(
|
|
1634
|
+
out,
|
|
1635
|
+
key,
|
|
1636
|
+
mergeDeep(
|
|
1637
|
+
safeOwnRead(out, key),
|
|
1638
|
+
safeOwnRead(source, key),
|
|
1639
|
+
[...path, key],
|
|
1640
|
+
schema
|
|
1641
|
+
)
|
|
1642
|
+
);
|
|
1480
1643
|
}
|
|
1481
1644
|
return out;
|
|
1482
1645
|
}
|
|
@@ -1577,39 +1740,44 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1577
1740
|
}
|
|
1578
1741
|
return result;
|
|
1579
1742
|
}
|
|
1580
|
-
async function
|
|
1743
|
+
async function runImperativeValidation(pathInput, config) {
|
|
1581
1744
|
const segments = pathInput === void 0 ? void 0 : toSegments(pathInput);
|
|
1582
1745
|
const dataAtPath = segments === void 0 ? state.form.value : state.getValueAtPath(segments);
|
|
1583
1746
|
try {
|
|
1584
1747
|
state.activeValidations.value += 1;
|
|
1585
|
-
state.cancelFieldValidation();
|
|
1748
|
+
if (config.cancelInFlight) state.cancelFieldValidation();
|
|
1586
1749
|
const refinement = await runRefinementValidation(dataAtPath, segments);
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1750
|
+
if (config.commitToSchemaErrors) {
|
|
1751
|
+
const scopePath = segments ?? [];
|
|
1752
|
+
const errors = refinement.success ? [] : refinement.errors;
|
|
1753
|
+
const reStamped = segments === void 0 ? errors : errors.map((err) => ({
|
|
1754
|
+
...err,
|
|
1755
|
+
path: [...segments, ...err.path]
|
|
1756
|
+
}));
|
|
1757
|
+
state.applySchemaErrorsForSubtree(scopePath, reStamped);
|
|
1758
|
+
}
|
|
1759
|
+
return { ok: true, refinement, segments };
|
|
1595
1760
|
} catch (err) {
|
|
1596
|
-
return adapterThrowResponse(err);
|
|
1761
|
+
return { ok: false, error: adapterThrowResponse(err) };
|
|
1597
1762
|
} finally {
|
|
1598
1763
|
state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
|
|
1599
1764
|
}
|
|
1600
1765
|
}
|
|
1766
|
+
async function validateAsync(pathInput) {
|
|
1767
|
+
const result = await runImperativeValidation(pathInput, {
|
|
1768
|
+
cancelInFlight: true,
|
|
1769
|
+
commitToSchemaErrors: true
|
|
1770
|
+
});
|
|
1771
|
+
if (!result.ok) return result.error;
|
|
1772
|
+
return stripData(composeWithDerivedBlank(result.refinement, result.segments));
|
|
1773
|
+
}
|
|
1601
1774
|
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
|
-
}
|
|
1775
|
+
const result = await runImperativeValidation(pathInput, {
|
|
1776
|
+
cancelInFlight: false,
|
|
1777
|
+
commitToSchemaErrors: false
|
|
1778
|
+
});
|
|
1779
|
+
if (!result.ok) return result.error;
|
|
1780
|
+
return composeWithDerivedBlank(result.refinement, result.segments);
|
|
1613
1781
|
}
|
|
1614
1782
|
function adapterThrowResponse(err) {
|
|
1615
1783
|
return {
|
|
@@ -2047,7 +2215,6 @@ function coerceValue(value, accepted, elementAccepted, index) {
|
|
|
2047
2215
|
}
|
|
2048
2216
|
|
|
2049
2217
|
const EMPTY_TRANSFORMS = Object.freeze([]);
|
|
2050
|
-
const INTERACTIVE_TAG_NAMES = /* @__PURE__ */ new Set(["INPUT", "SELECT", "TEXTAREA"]);
|
|
2051
2218
|
const attaformListenersSymbol = Symbol.for("attaform:focus-listeners");
|
|
2052
2219
|
function attachFocusListeners(state, segments, element, instanceMeta) {
|
|
2053
2220
|
const target = element;
|
|
@@ -2098,7 +2265,11 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2098
2265
|
if (typed !== null && typeof raw === "number" && parseFloat(typed) === raw) {
|
|
2099
2266
|
return typed;
|
|
2100
2267
|
}
|
|
2101
|
-
|
|
2268
|
+
try {
|
|
2269
|
+
return String(raw);
|
|
2270
|
+
} catch {
|
|
2271
|
+
return Object.prototype.toString.call(raw);
|
|
2272
|
+
}
|
|
2102
2273
|
});
|
|
2103
2274
|
const slimDefault = state.schema.getDefaultAtPath(segments);
|
|
2104
2275
|
const slimTypes = state.schema.getSlimPrimitiveTypesAtPath(segments);
|
|
@@ -2257,12 +2428,12 @@ function walk(input, segments, schema, paths) {
|
|
|
2257
2428
|
for (const key of allKeys) {
|
|
2258
2429
|
const orig = input[key];
|
|
2259
2430
|
if (orig === void 0 && inputKeysSet.has(key)) {
|
|
2260
|
-
out
|
|
2431
|
+
safeAssign(out, key, void 0);
|
|
2261
2432
|
mutated = true;
|
|
2262
2433
|
continue;
|
|
2263
2434
|
}
|
|
2264
2435
|
const walked = walk(orig, [...segments, key], schema, paths);
|
|
2265
|
-
out
|
|
2436
|
+
safeAssign(out, key, walked);
|
|
2266
2437
|
if (walked !== orig) mutated = true;
|
|
2267
2438
|
}
|
|
2268
2439
|
return mutated ? out : input;
|
|
@@ -2283,7 +2454,11 @@ function walkUnspecified(slim, segments, paths) {
|
|
|
2283
2454
|
if (slim !== null && typeof slim === "object") {
|
|
2284
2455
|
const out = {};
|
|
2285
2456
|
for (const key of Object.keys(slim)) {
|
|
2286
|
-
|
|
2457
|
+
safeAssign(
|
|
2458
|
+
out,
|
|
2459
|
+
key,
|
|
2460
|
+
walkUnspecified(slim[key], [...segments, key], paths)
|
|
2461
|
+
);
|
|
2287
2462
|
}
|
|
2288
2463
|
return out;
|
|
2289
2464
|
}
|
|
@@ -2318,7 +2493,7 @@ function substitute(input, segments, schema, paths) {
|
|
|
2318
2493
|
for (const key of Object.keys(input)) {
|
|
2319
2494
|
const orig = input[key];
|
|
2320
2495
|
const walked = substitute(orig, [...segments, key], schema, paths);
|
|
2321
|
-
out
|
|
2496
|
+
safeAssign(out, key, walked);
|
|
2322
2497
|
if (walked !== orig) mutated = true;
|
|
2323
2498
|
}
|
|
2324
2499
|
return mutated ? out : input;
|
|
@@ -2366,70 +2541,86 @@ function expandUnsetAt(segments, schema, paths) {
|
|
|
2366
2541
|
return result;
|
|
2367
2542
|
}
|
|
2368
2543
|
|
|
2369
|
-
function
|
|
2370
|
-
const inner = computed(() => readonly(form.value));
|
|
2544
|
+
function buildCallableReadonlySnapshotProxy(opts) {
|
|
2371
2545
|
const target = (() => {
|
|
2372
2546
|
});
|
|
2373
|
-
const
|
|
2374
|
-
const
|
|
2547
|
+
const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(opts.snapshot);
|
|
2548
|
+
const callResolve = opts.resolveCall ?? ((arg) => opts.resolveKey(String(arg)));
|
|
2375
2549
|
return new Proxy(target, {
|
|
2376
2550
|
apply(_, __, args) {
|
|
2377
2551
|
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;
|
|
2552
|
+
if (arg === void 0) return opts.snapshot();
|
|
2553
|
+
return callResolve(arg);
|
|
2386
2554
|
},
|
|
2387
2555
|
get(_, key) {
|
|
2388
2556
|
if (typeof key === "symbol") {
|
|
2389
|
-
if (key === Symbol.toPrimitive) return
|
|
2557
|
+
if (key === Symbol.toPrimitive) return toPrimitive;
|
|
2390
2558
|
return Reflect.get(target, key);
|
|
2391
2559
|
}
|
|
2392
|
-
if (key === "toJSON") return
|
|
2393
|
-
if (key === "toString") return
|
|
2394
|
-
if (key === "valueOf")
|
|
2395
|
-
|
|
2396
|
-
return this;
|
|
2397
|
-
};
|
|
2398
|
-
return inner.value[key];
|
|
2560
|
+
if (key === "toJSON") return toJSON;
|
|
2561
|
+
if (key === "toString") return toString;
|
|
2562
|
+
if (key === "valueOf") return valueOf;
|
|
2563
|
+
return opts.resolveKey(key);
|
|
2399
2564
|
},
|
|
2400
2565
|
has(_, key) {
|
|
2401
2566
|
if (typeof key === "symbol") return Reflect.has(target, key);
|
|
2402
|
-
return
|
|
2403
|
-
},
|
|
2404
|
-
ownKeys() {
|
|
2405
|
-
return Reflect.ownKeys(inner.value);
|
|
2567
|
+
return opts.hasKey(key);
|
|
2406
2568
|
},
|
|
2569
|
+
ownKeys: () => opts.ownKeys(),
|
|
2407
2570
|
getOwnPropertyDescriptor(_, key) {
|
|
2408
|
-
|
|
2409
|
-
if (
|
|
2410
|
-
return
|
|
2571
|
+
if (typeof key !== "string") return void 0;
|
|
2572
|
+
if (opts.describeKey !== void 0) return opts.describeKey(key);
|
|
2573
|
+
if (!opts.hasKey(key)) return void 0;
|
|
2574
|
+
return {
|
|
2575
|
+
configurable: true,
|
|
2576
|
+
enumerable: true,
|
|
2577
|
+
writable: false,
|
|
2578
|
+
value: opts.resolveKey(key)
|
|
2579
|
+
};
|
|
2411
2580
|
},
|
|
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
|
-
}
|
|
2581
|
+
set: (_, key) => {
|
|
2582
|
+
warnReadOnly(opts.surface, "write", key);
|
|
2422
2583
|
return true;
|
|
2423
2584
|
},
|
|
2424
|
-
deleteProperty(_, key) {
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2585
|
+
deleteProperty: (_, key) => {
|
|
2586
|
+
warnReadOnly(opts.surface, "delete", key);
|
|
2587
|
+
return true;
|
|
2588
|
+
},
|
|
2589
|
+
defineProperty: (_, key) => {
|
|
2590
|
+
warnReadOnly(opts.surface, "define", key);
|
|
2430
2591
|
return true;
|
|
2592
|
+
}
|
|
2593
|
+
});
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
function buildValuesProxy(form) {
|
|
2597
|
+
const inner = computed(() => readonly(form.value));
|
|
2598
|
+
return buildCallableReadonlySnapshotProxy({
|
|
2599
|
+
surface: "form.values",
|
|
2600
|
+
snapshot: () => inner.value,
|
|
2601
|
+
// Read through the readonly proxy at access time so Vue's
|
|
2602
|
+
// dependency tracking lands inside the consumer's active effect
|
|
2603
|
+
// — `inner.value[key]` is what triggers per-key tracking.
|
|
2604
|
+
resolveKey: (key) => inner.value[key],
|
|
2605
|
+
// Dynamic path: walk segments through the readonly proxy. Each
|
|
2606
|
+
// step reads through the proxy's own get traps so dependency
|
|
2607
|
+
// tracking propagates at every level.
|
|
2608
|
+
resolveCall: (arg) => {
|
|
2609
|
+
const { segments } = canonicalizePath(arg);
|
|
2610
|
+
let cursor = inner.value;
|
|
2611
|
+
for (const seg of segments) {
|
|
2612
|
+
if (cursor === null || cursor === void 0) return void 0;
|
|
2613
|
+
cursor = cursor[seg];
|
|
2614
|
+
}
|
|
2615
|
+
return cursor;
|
|
2431
2616
|
},
|
|
2432
|
-
|
|
2617
|
+
ownKeys: () => Reflect.ownKeys(inner.value),
|
|
2618
|
+
hasKey: (key) => Reflect.has(inner.value, key),
|
|
2619
|
+
describeKey: (key) => {
|
|
2620
|
+
const desc = Reflect.getOwnPropertyDescriptor(inner.value, key);
|
|
2621
|
+
if (desc !== void 0) desc.configurable = true;
|
|
2622
|
+
return desc;
|
|
2623
|
+
}
|
|
2433
2624
|
});
|
|
2434
2625
|
}
|
|
2435
2626
|
|
|
@@ -2495,10 +2686,18 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2495
2686
|
next,
|
|
2496
2687
|
state.schema
|
|
2497
2688
|
);
|
|
2689
|
+
const ok2 = state.setValueAtPath([], walked2.cleanedValues, withInstanceMeta());
|
|
2690
|
+
if (!ok2) return false;
|
|
2498
2691
|
for (const pathKey of walked2.paths) {
|
|
2499
|
-
|
|
2692
|
+
const blankSegments = segmentsForPathKey(pathKey);
|
|
2693
|
+
if (blankSegments === null) continue;
|
|
2694
|
+
state.setValueAtPath(
|
|
2695
|
+
blankSegments,
|
|
2696
|
+
state.getValueAtPath(blankSegments),
|
|
2697
|
+
withInstanceMeta({ blank: true })
|
|
2698
|
+
);
|
|
2500
2699
|
}
|
|
2501
|
-
return
|
|
2700
|
+
return true;
|
|
2502
2701
|
}
|
|
2503
2702
|
const segments = canonicalizePath(pathOrValue).segments;
|
|
2504
2703
|
const writeUnsetAt = () => {
|
|
@@ -2647,23 +2846,45 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2647
2846
|
const rootFieldState = getRootFieldStateAt([]);
|
|
2648
2847
|
const formMeta = readonly(
|
|
2649
2848
|
reactive({
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
pristine
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2849
|
+
get value() {
|
|
2850
|
+
return rootFieldState.value.value;
|
|
2851
|
+
},
|
|
2852
|
+
get original() {
|
|
2853
|
+
return rootFieldState.value.original;
|
|
2854
|
+
},
|
|
2855
|
+
get pristine() {
|
|
2856
|
+
return rootFieldState.value.pristine;
|
|
2857
|
+
},
|
|
2858
|
+
get dirty() {
|
|
2859
|
+
return rootFieldState.value.dirty;
|
|
2860
|
+
},
|
|
2861
|
+
get focused() {
|
|
2862
|
+
return rootFieldState.value.focused;
|
|
2863
|
+
},
|
|
2864
|
+
get blurred() {
|
|
2865
|
+
return rootFieldState.value.blurred;
|
|
2866
|
+
},
|
|
2867
|
+
get touched() {
|
|
2868
|
+
return rootFieldState.value.touched;
|
|
2869
|
+
},
|
|
2870
|
+
get interacted() {
|
|
2871
|
+
return rootFieldState.value.interacted;
|
|
2872
|
+
},
|
|
2873
|
+
get blurredAfterInteraction() {
|
|
2874
|
+
return rootFieldState.value.blurredAfterInteraction;
|
|
2875
|
+
},
|
|
2876
|
+
get connected() {
|
|
2877
|
+
return rootFieldState.value.connected;
|
|
2878
|
+
},
|
|
2879
|
+
get element() {
|
|
2880
|
+
return rootFieldState.value.element;
|
|
2881
|
+
},
|
|
2882
|
+
get elements() {
|
|
2883
|
+
return rootFieldState.value.elements;
|
|
2884
|
+
},
|
|
2885
|
+
get updatedAt() {
|
|
2886
|
+
return rootFieldState.value.updatedAt;
|
|
2887
|
+
},
|
|
2667
2888
|
// Whole-form validating mirrors the LIFECYCLE counter
|
|
2668
2889
|
// (`state.activeValidations`) ORed with any per-leaf validation
|
|
2669
2890
|
// in flight (via `rootFieldState.validating`). A submit-time
|
|
@@ -2686,21 +2907,51 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2686
2907
|
// FieldState surface, so `form.meta.displayState` matches
|
|
2687
2908
|
// `form.fields().displayState` exactly — the predicate runs once
|
|
2688
2909
|
// at the root and the result is shared.
|
|
2689
|
-
displayState
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2910
|
+
get displayState() {
|
|
2911
|
+
return rootFieldState.value.displayState;
|
|
2912
|
+
},
|
|
2913
|
+
get showErrors() {
|
|
2914
|
+
return rootFieldState.value.showErrors;
|
|
2915
|
+
},
|
|
2916
|
+
get showPending() {
|
|
2917
|
+
return rootFieldState.value.showPending;
|
|
2918
|
+
},
|
|
2919
|
+
get showSuccess() {
|
|
2920
|
+
return rootFieldState.value.showSuccess;
|
|
2921
|
+
},
|
|
2922
|
+
get showIdle() {
|
|
2923
|
+
return rootFieldState.value.showIdle;
|
|
2924
|
+
},
|
|
2925
|
+
get firstError() {
|
|
2926
|
+
return rootFieldState.value.firstError;
|
|
2927
|
+
},
|
|
2928
|
+
get path() {
|
|
2929
|
+
return rootFieldState.value.path;
|
|
2930
|
+
},
|
|
2931
|
+
get id() {
|
|
2932
|
+
return rootFieldState.value.id;
|
|
2933
|
+
},
|
|
2934
|
+
get aria() {
|
|
2935
|
+
return rootFieldState.value.aria;
|
|
2936
|
+
},
|
|
2937
|
+
get key() {
|
|
2938
|
+
return rootFieldState.value.key;
|
|
2939
|
+
},
|
|
2940
|
+
get blank() {
|
|
2941
|
+
return rootFieldState.value.blank;
|
|
2942
|
+
},
|
|
2943
|
+
get label() {
|
|
2944
|
+
return rootFieldState.value.label;
|
|
2945
|
+
},
|
|
2946
|
+
get description() {
|
|
2947
|
+
return rootFieldState.value.description;
|
|
2948
|
+
},
|
|
2949
|
+
get placeholder() {
|
|
2950
|
+
return rootFieldState.value.placeholder;
|
|
2951
|
+
},
|
|
2952
|
+
get meta() {
|
|
2953
|
+
return rootFieldState.value.meta;
|
|
2954
|
+
},
|
|
2704
2955
|
// Lifecycle (form-level only — not on FieldState).
|
|
2705
2956
|
submitting,
|
|
2706
2957
|
submissionAttempts,
|
|
@@ -2709,7 +2960,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2709
2960
|
// Scalar mirror over the array — meta is a single sticky surface
|
|
2710
2961
|
// for both templates and `useWizard`'s `FormStatus`, so the
|
|
2711
2962
|
// projection lives here.
|
|
2712
|
-
errorCount
|
|
2963
|
+
get errorCount() {
|
|
2964
|
+
return metaErrors.value.length;
|
|
2965
|
+
},
|
|
2713
2966
|
submitted,
|
|
2714
2967
|
// Per-`useForm()`-call identity. Stable for one mount; new on
|
|
2715
2968
|
// re-mount; orthogonal to `form.key` (which is the user-supplied
|
|
@@ -2746,12 +2999,20 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2746
2999
|
}
|
|
2747
3000
|
};
|
|
2748
3001
|
function clear(pathInput) {
|
|
2749
|
-
|
|
2750
|
-
|
|
3002
|
+
if (pathInput === void 0) {
|
|
3003
|
+
return setValueImpl(unset);
|
|
3004
|
+
}
|
|
3005
|
+
return setValueImpl(pathInput, unset);
|
|
2751
3006
|
}
|
|
2752
3007
|
const persist = async (pathInput, options2) => {
|
|
2753
3008
|
const segments = canonicalizePath(pathInput).segments;
|
|
2754
|
-
|
|
3009
|
+
if (!allowSensitivePersist(
|
|
3010
|
+
segments,
|
|
3011
|
+
options2?.acknowledgeSensitive === true,
|
|
3012
|
+
state.isSensitivePath
|
|
3013
|
+
)) {
|
|
3014
|
+
return;
|
|
3015
|
+
}
|
|
2755
3016
|
if (persistence === void 0) return;
|
|
2756
3017
|
await persistence.writePathImmediately(segments);
|
|
2757
3018
|
};
|
|
@@ -2817,7 +3078,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2817
3078
|
getFormMetaBase,
|
|
2818
3079
|
fieldStateAccessorOptions
|
|
2819
3080
|
);
|
|
3081
|
+
const needsLazyGate = state.defaultValuesFactory.value !== void 0 || state.hasSsrPrefetch;
|
|
2820
3082
|
function gated(fn) {
|
|
3083
|
+
if (!needsLazyGate) return fn;
|
|
2821
3084
|
return ((...args) => {
|
|
2822
3085
|
void state.activate();
|
|
2823
3086
|
return fn(...args);
|
|
@@ -2842,7 +3105,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2842
3105
|
}
|
|
2843
3106
|
const out = {};
|
|
2844
3107
|
for (const key of Object.keys(value)) {
|
|
2845
|
-
out
|
|
3108
|
+
safeAssign(out, key, callTerminal(`${path}.${key}`));
|
|
2846
3109
|
}
|
|
2847
3110
|
return Object.freeze(out);
|
|
2848
3111
|
}
|
|
@@ -2936,9 +3199,132 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2936
3199
|
};
|
|
2937
3200
|
}
|
|
2938
3201
|
|
|
2939
|
-
function
|
|
2940
|
-
const
|
|
2941
|
-
|
|
3202
|
+
function applyDuStubs(schema, data, options = {}) {
|
|
3203
|
+
const warned = options.warn === true ? /* @__PURE__ */ new Set() : void 0;
|
|
3204
|
+
return walkDuStubs(schema, data, options.basePath ?? [], warned);
|
|
3205
|
+
}
|
|
3206
|
+
function walkDuStubs(schema, value, path, warned) {
|
|
3207
|
+
if (value === null || value === void 0 || typeof value !== "object") return value;
|
|
3208
|
+
if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || typeof value === "function") {
|
|
3209
|
+
return value;
|
|
3210
|
+
}
|
|
3211
|
+
if (Array.isArray(value)) {
|
|
3212
|
+
return value.map((item, i) => walkDuStubs(schema, item, [...path, i], warned));
|
|
3213
|
+
}
|
|
3214
|
+
const rec = value;
|
|
3215
|
+
const du = schema.getUnionDiscriminatorAtPath(path);
|
|
3216
|
+
if (du !== void 0) {
|
|
3217
|
+
const discValue = rec[du.discriminatorKey];
|
|
3218
|
+
if (discValue !== void 0 && !du.isVariantSelected(discValue)) {
|
|
3219
|
+
const isKindBlank = discValue === "" || discValue === 0 || discValue === 0n || discValue === false || discValue === null;
|
|
3220
|
+
if (!isKindBlank && warned !== void 0 && __DEV__) {
|
|
3221
|
+
const dotted = path.map((s) => String(s)).join(".") || "(root)";
|
|
3222
|
+
const key = `${dotted}::${String(discValue)}`;
|
|
3223
|
+
if (!warned.has(key)) {
|
|
3224
|
+
warned.add(key);
|
|
3225
|
+
console.warn(
|
|
3226
|
+
`[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.`
|
|
3227
|
+
);
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
const stub = {};
|
|
3231
|
+
safeAssign(stub, du.discriminatorKey, discValue);
|
|
3232
|
+
return stub;
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
const out = {};
|
|
3236
|
+
for (const k of Object.keys(rec)) {
|
|
3237
|
+
safeAssign(out, k, walkDuStubs(schema, rec[k], [...path, k], warned));
|
|
3238
|
+
}
|
|
3239
|
+
return out;
|
|
3240
|
+
}
|
|
3241
|
+
|
|
3242
|
+
function createVariantMemory() {
|
|
3243
|
+
const memory = /* @__PURE__ */ new Map();
|
|
3244
|
+
function clearAtArrayIndices(arrayPath, indexFilter) {
|
|
3245
|
+
for (const memKey of [...memory.keys()]) {
|
|
3246
|
+
const segs = segmentsForPathKey(memKey);
|
|
3247
|
+
if (segs === null) continue;
|
|
3248
|
+
if (!isPathPrefix(arrayPath, segs)) continue;
|
|
3249
|
+
if (segs.length <= arrayPath.length) continue;
|
|
3250
|
+
const idxSeg = segs[arrayPath.length];
|
|
3251
|
+
if (typeof idxSeg !== "number") continue;
|
|
3252
|
+
if (indexFilter(idxSeg)) memory.delete(memKey);
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
return {
|
|
3256
|
+
clear() {
|
|
3257
|
+
memory.clear();
|
|
3258
|
+
},
|
|
3259
|
+
clearUnderPath(parentPath) {
|
|
3260
|
+
for (const memKey of [...memory.keys()]) {
|
|
3261
|
+
const segs = segmentsForPathKey(memKey);
|
|
3262
|
+
if (segs === null) continue;
|
|
3263
|
+
if (isPathPrefix(parentPath, segs)) memory.delete(memKey);
|
|
3264
|
+
}
|
|
3265
|
+
},
|
|
3266
|
+
applyArrayOp(arrayPath, op) {
|
|
3267
|
+
switch (op.kind) {
|
|
3268
|
+
case "insert":
|
|
3269
|
+
case "remove":
|
|
3270
|
+
clearAtArrayIndices(arrayPath, (i) => i >= op.index);
|
|
3271
|
+
return;
|
|
3272
|
+
case "move": {
|
|
3273
|
+
const lo = Math.min(op.from, op.to);
|
|
3274
|
+
const hi = Math.max(op.from, op.to);
|
|
3275
|
+
clearAtArrayIndices(arrayPath, (i) => i >= lo && i <= hi);
|
|
3276
|
+
return;
|
|
3277
|
+
}
|
|
3278
|
+
case "swap":
|
|
3279
|
+
clearAtArrayIndices(arrayPath, (i) => i === op.a || i === op.b);
|
|
3280
|
+
return;
|
|
3281
|
+
case "replace-at":
|
|
3282
|
+
clearAtArrayIndices(arrayPath, (i) => i === op.index);
|
|
3283
|
+
return;
|
|
3284
|
+
}
|
|
3285
|
+
},
|
|
3286
|
+
recordOutgoing(unionKey, discValue, snapshot) {
|
|
3287
|
+
let perUnion = memory.get(unionKey);
|
|
3288
|
+
if (perUnion === void 0) {
|
|
3289
|
+
perUnion = /* @__PURE__ */ new Map();
|
|
3290
|
+
memory.set(unionKey, perUnion);
|
|
3291
|
+
}
|
|
3292
|
+
perUnion.set(discValue, snapshot);
|
|
3293
|
+
},
|
|
3294
|
+
lookupIncoming(unionKey, discValue) {
|
|
3295
|
+
return memory.get(unionKey)?.get(discValue);
|
|
3296
|
+
}
|
|
3297
|
+
};
|
|
3298
|
+
}
|
|
3299
|
+
function cloneVariantSnapshot(value) {
|
|
3300
|
+
if (value === null || typeof value !== "object") return value;
|
|
3301
|
+
const raw = toRaw(value);
|
|
3302
|
+
if (raw instanceof Date) return new Date(raw.getTime());
|
|
3303
|
+
if (raw instanceof Map) {
|
|
3304
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
3305
|
+
for (const [k, v] of raw.entries()) out2.set(cloneVariantSnapshot(k), cloneVariantSnapshot(v));
|
|
3306
|
+
return out2;
|
|
3307
|
+
}
|
|
3308
|
+
if (raw instanceof Set) {
|
|
3309
|
+
const out2 = /* @__PURE__ */ new Set();
|
|
3310
|
+
for (const v of raw) out2.add(cloneVariantSnapshot(v));
|
|
3311
|
+
return out2;
|
|
3312
|
+
}
|
|
3313
|
+
if (raw instanceof RegExp) return new RegExp(raw.source, raw.flags);
|
|
3314
|
+
if (Array.isArray(raw)) {
|
|
3315
|
+
const out2 = new Array(raw.length);
|
|
3316
|
+
for (let i = 0; i < raw.length; i++) out2[i] = cloneVariantSnapshot(raw[i]);
|
|
3317
|
+
return out2;
|
|
3318
|
+
}
|
|
3319
|
+
const src = raw;
|
|
3320
|
+
const out = {};
|
|
3321
|
+
for (const k of Object.keys(src)) safeAssign(out, k, cloneVariantSnapshot(src[k]));
|
|
3322
|
+
return out;
|
|
3323
|
+
}
|
|
3324
|
+
|
|
3325
|
+
function createArrayIdentity(getArrayLength) {
|
|
3326
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
3327
|
+
const baselines = /* @__PURE__ */ new Map();
|
|
2942
3328
|
let counter = 0;
|
|
2943
3329
|
const allocate = () => `k${(counter++).toString(36)}`;
|
|
2944
3330
|
function ensure(arrayKey, expectedLen) {
|
|
@@ -2998,6 +3384,36 @@ function createArrayIdentity(getArrayLength) {
|
|
|
2998
3384
|
return;
|
|
2999
3385
|
}
|
|
3000
3386
|
},
|
|
3387
|
+
applyRemap(arrayPath, remap) {
|
|
3388
|
+
if (remap.moved.size === 0 && remap.vacated.size === 0 && remap.fresh.size === 0) return;
|
|
3389
|
+
const relocate = (store) => {
|
|
3390
|
+
const idxPos = arrayPath.length;
|
|
3391
|
+
const snapshots = [];
|
|
3392
|
+
for (const [key, value] of store) {
|
|
3393
|
+
const segments = segmentsForPathKey(key);
|
|
3394
|
+
if (segments === null) continue;
|
|
3395
|
+
if (!isPathPrefix(arrayPath, segments)) continue;
|
|
3396
|
+
if (segments.length <= idxPos) continue;
|
|
3397
|
+
const idxSeg = segments[idxPos];
|
|
3398
|
+
if (typeof idxSeg !== "number") continue;
|
|
3399
|
+
if (!remap.moved.has(idxSeg) && !remap.vacated.has(idxSeg) && !remap.fresh.has(idxSeg)) {
|
|
3400
|
+
continue;
|
|
3401
|
+
}
|
|
3402
|
+
snapshots.push({ segments: [...segments], index: idxSeg, value });
|
|
3403
|
+
}
|
|
3404
|
+
if (snapshots.length === 0) return;
|
|
3405
|
+
for (const snap of snapshots) store.delete(canonicalizePath(snap.segments).key);
|
|
3406
|
+
for (const snap of snapshots) {
|
|
3407
|
+
const target = remap.moved.get(snap.index);
|
|
3408
|
+
if (target === void 0) continue;
|
|
3409
|
+
const relocated = snap.segments.slice();
|
|
3410
|
+
relocated[idxPos] = target;
|
|
3411
|
+
store.set(canonicalizePath(relocated).key, snap.value);
|
|
3412
|
+
}
|
|
3413
|
+
};
|
|
3414
|
+
relocate(tokens);
|
|
3415
|
+
relocate(baselines);
|
|
3416
|
+
},
|
|
3001
3417
|
realign(arraySegs) {
|
|
3002
3418
|
ensure(canonicalizePath(arraySegs).key, getArrayLength(arraySegs));
|
|
3003
3419
|
},
|
|
@@ -3112,6 +3528,94 @@ function migrateSetSubtree(set, arrayPath, remap) {
|
|
|
3112
3528
|
}
|
|
3113
3529
|
}
|
|
3114
3530
|
|
|
3531
|
+
function createArrayBookkeeping(deps) {
|
|
3532
|
+
const {
|
|
3533
|
+
form,
|
|
3534
|
+
fields,
|
|
3535
|
+
userErrors,
|
|
3536
|
+
originals,
|
|
3537
|
+
blankPaths,
|
|
3538
|
+
originalBlankPaths,
|
|
3539
|
+
fieldValidationCounts,
|
|
3540
|
+
fieldValidationState,
|
|
3541
|
+
schemaErrors,
|
|
3542
|
+
activeValidations,
|
|
3543
|
+
arrayIdentity,
|
|
3544
|
+
touchFieldRecord,
|
|
3545
|
+
decFieldValidation
|
|
3546
|
+
} = deps;
|
|
3547
|
+
function migrateElementState(arrayPath, remap) {
|
|
3548
|
+
if (remap.moved.size === 0 && remap.vacated.size === 0) return;
|
|
3549
|
+
migrateMapSubtree(fields, arrayPath, remap, (record, segments) => ({
|
|
3550
|
+
...record,
|
|
3551
|
+
path: segments
|
|
3552
|
+
}));
|
|
3553
|
+
migrateMapSubtree(
|
|
3554
|
+
userErrors,
|
|
3555
|
+
arrayPath,
|
|
3556
|
+
remap,
|
|
3557
|
+
(errors, segments) => errors.map((error) => ({ ...error, path: [...segments] }))
|
|
3558
|
+
);
|
|
3559
|
+
migrateMapSubtree(originals, arrayPath, remap, (record, segments) => ({
|
|
3560
|
+
segments,
|
|
3561
|
+
value: record.value
|
|
3562
|
+
}));
|
|
3563
|
+
migrateSetSubtree(blankPaths, arrayPath, remap);
|
|
3564
|
+
migrateSetSubtree(originalBlankPaths, arrayPath, remap);
|
|
3565
|
+
migrateMapSubtree(fieldValidationCounts, arrayPath, remap, (count) => count);
|
|
3566
|
+
arrayIdentity.applyRemap(arrayPath, remap);
|
|
3567
|
+
}
|
|
3568
|
+
function seedFreshElement(arrayPath, freshIndex) {
|
|
3569
|
+
const elementPath = [...arrayPath, freshIndex];
|
|
3570
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3571
|
+
diffAndApply(void 0, getAtPath(form.value, elementPath), elementPath, (patch) => {
|
|
3572
|
+
if (patch.kind !== "added") return;
|
|
3573
|
+
const { key } = canonicalizePath(patch.path);
|
|
3574
|
+
if (!originals.has(key)) originals.set(key, { segments: patch.path, value: void 0 });
|
|
3575
|
+
touchFieldRecord(key, patch.path, { updatedAt: now });
|
|
3576
|
+
});
|
|
3577
|
+
}
|
|
3578
|
+
function dropSchemaErrorsAtChangedIndices(arrayPath, remap) {
|
|
3579
|
+
const changed = changedIndices(remap);
|
|
3580
|
+
if (changed.size === 0) return;
|
|
3581
|
+
const idxPos = arrayPath.length;
|
|
3582
|
+
for (const key of [...schemaErrors.keys()]) {
|
|
3583
|
+
const segs = segmentsForPathKey(key);
|
|
3584
|
+
if (segs === null) continue;
|
|
3585
|
+
if (!isPathPrefix(arrayPath, segs)) continue;
|
|
3586
|
+
if (segs.length <= idxPos) continue;
|
|
3587
|
+
const idx = segs[idxPos];
|
|
3588
|
+
if (typeof idx === "number" && changed.has(idx)) schemaErrors.delete(key);
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
function abortValidationAtVacatedIndices(arrayPath, remap) {
|
|
3592
|
+
if (remap.vacated.size === 0) return;
|
|
3593
|
+
const idxPos = arrayPath.length;
|
|
3594
|
+
for (const [key, entry] of [...fieldValidationState]) {
|
|
3595
|
+
const segs = segmentsForPathKey(key);
|
|
3596
|
+
if (segs === null) continue;
|
|
3597
|
+
if (!isPathPrefix(arrayPath, segs)) continue;
|
|
3598
|
+
if (segs.length <= idxPos) continue;
|
|
3599
|
+
const idx = segs[idxPos];
|
|
3600
|
+
if (typeof idx !== "number" || !remap.vacated.has(idx)) continue;
|
|
3601
|
+
if (entry.timer !== null) {
|
|
3602
|
+
clearTimeout(entry.timer);
|
|
3603
|
+
} else if (!entry.settled) {
|
|
3604
|
+
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
3605
|
+
decFieldValidation(key);
|
|
3606
|
+
}
|
|
3607
|
+
entry.controller.abort();
|
|
3608
|
+
fieldValidationState.delete(key);
|
|
3609
|
+
}
|
|
3610
|
+
}
|
|
3611
|
+
return {
|
|
3612
|
+
migrateElementState,
|
|
3613
|
+
seedFreshElement,
|
|
3614
|
+
dropSchemaErrorsAtChangedIndices,
|
|
3615
|
+
abortValidationAtVacatedIndices
|
|
3616
|
+
};
|
|
3617
|
+
}
|
|
3618
|
+
|
|
3115
3619
|
function isHydratedFieldRecord(value) {
|
|
3116
3620
|
if (typeof value !== "object" || value === null) return false;
|
|
3117
3621
|
const r = value;
|
|
@@ -3135,43 +3639,6 @@ function warnMalformedHydration(formKey, kind, rawKey) {
|
|
|
3135
3639
|
`[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
3640
|
);
|
|
3137
3641
|
}
|
|
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
3642
|
function isPathKeyUnder(existingKey, parentPath) {
|
|
3176
3643
|
const parsed = segmentsForPathKey(existingKey);
|
|
3177
3644
|
if (parsed === null) return false;
|
|
@@ -3207,31 +3674,6 @@ function stripSymbolsDeep(value) {
|
|
|
3207
3674
|
}
|
|
3208
3675
|
return mutated ? out : value;
|
|
3209
3676
|
}
|
|
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
3677
|
function walkAuthoredFromConstraints(value, prefix, out) {
|
|
3236
3678
|
if (prefix.length > 0) out.add(canonicalizePath(prefix).key);
|
|
3237
3679
|
if (isPlainRecord(value)) {
|
|
@@ -3304,7 +3746,6 @@ function createFormStore(options) {
|
|
|
3304
3746
|
const coerceIndex = resolveCoercionIndex(options.coerce);
|
|
3305
3747
|
const resolvedGetDisplayState = resolveGetDisplayState(options.getDisplayState);
|
|
3306
3748
|
const resolvedIsSensitivePath = options.isSensitivePath ?? isSensitivePath;
|
|
3307
|
-
const resolvedSegmentMatchesSensitive = options.segmentMatchesSensitive ?? segmentMatchesSensitive;
|
|
3308
3749
|
const cleanupHooks = [];
|
|
3309
3750
|
const modules = /* @__PURE__ */ new Map();
|
|
3310
3751
|
const completedConstraints = defaultValues === void 0 ? void 0 : mergeStructural(schema, [], defaultValues);
|
|
@@ -3367,117 +3808,17 @@ function createFormStore(options) {
|
|
|
3367
3808
|
blankPaths.add(key);
|
|
3368
3809
|
originalBlankPaths.add(key);
|
|
3369
3810
|
}
|
|
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);
|
|
3811
|
+
const variantMemory = createVariantMemory();
|
|
3812
|
+
const pathOrdinals = /* @__PURE__ */ new Map();
|
|
3813
|
+
let nextOrdinal = 0;
|
|
3814
|
+
function ensurePathOrdinal(key) {
|
|
3815
|
+
let ordinal = pathOrdinals.get(key);
|
|
3816
|
+
if (ordinal === void 0) {
|
|
3817
|
+
ordinal = nextOrdinal;
|
|
3818
|
+
pathOrdinals.set(key, ordinal);
|
|
3819
|
+
nextOrdinal += 1;
|
|
3387
3820
|
}
|
|
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;
|
|
3479
|
-
}
|
|
3480
|
-
return ordinal;
|
|
3821
|
+
return ordinal;
|
|
3481
3822
|
}
|
|
3482
3823
|
const derivedBlankErrors = computed(() => {
|
|
3483
3824
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -3505,7 +3846,9 @@ function createFormStore(options) {
|
|
|
3505
3846
|
const departAttempts = ref(0);
|
|
3506
3847
|
const submissionGeneration = ref(0);
|
|
3507
3848
|
const activeValidations = ref(0);
|
|
3508
|
-
|
|
3849
|
+
const pathSnapshots = /* @__PURE__ */ new Map();
|
|
3850
|
+
let scheduleEpoch = 0;
|
|
3851
|
+
let lastCommittedEpoch = 0;
|
|
3509
3852
|
const hydrating = ref(false);
|
|
3510
3853
|
const hydrateError = ref(null);
|
|
3511
3854
|
const defaultValuesFactory = ref(void 0);
|
|
@@ -3521,9 +3864,12 @@ function createFormStore(options) {
|
|
|
3521
3864
|
const pathAsyncCache = /* @__PURE__ */ new Map();
|
|
3522
3865
|
function pathHasAsyncValidation(path) {
|
|
3523
3866
|
const { key } = canonicalizePath(path);
|
|
3867
|
+
return pathHasAsyncValidationByKey(key, path);
|
|
3868
|
+
}
|
|
3869
|
+
function pathHasAsyncValidationByKey(key, segments) {
|
|
3524
3870
|
const cached = pathAsyncCache.get(key);
|
|
3525
3871
|
if (cached !== void 0) return cached;
|
|
3526
|
-
const candidates = schema.getSchemasAtPath(
|
|
3872
|
+
const candidates = schema.getSchemasAtPath(segments);
|
|
3527
3873
|
const hasAsync = candidates.some((sub) => sub.needsAsyncValidation?.() === true);
|
|
3528
3874
|
pathAsyncCache.set(key, hasAsync);
|
|
3529
3875
|
return hasAsync;
|
|
@@ -3615,6 +3961,21 @@ function createFormStore(options) {
|
|
|
3615
3961
|
blurredAfterInteraction: patch.blurredAfterInteraction ?? current?.blurredAfterInteraction ?? false
|
|
3616
3962
|
});
|
|
3617
3963
|
}
|
|
3964
|
+
const arrayBookkeeping = createArrayBookkeeping({
|
|
3965
|
+
form,
|
|
3966
|
+
fields,
|
|
3967
|
+
userErrors,
|
|
3968
|
+
originals,
|
|
3969
|
+
blankPaths,
|
|
3970
|
+
originalBlankPaths,
|
|
3971
|
+
fieldValidationCounts,
|
|
3972
|
+
fieldValidationState,
|
|
3973
|
+
schemaErrors,
|
|
3974
|
+
activeValidations,
|
|
3975
|
+
arrayIdentity,
|
|
3976
|
+
touchFieldRecord,
|
|
3977
|
+
decFieldValidation
|
|
3978
|
+
});
|
|
3618
3979
|
function applyFormReplacement(next, meta) {
|
|
3619
3980
|
const prev = form.value;
|
|
3620
3981
|
if (Object.is(prev, next)) return;
|
|
@@ -3738,8 +4099,13 @@ function createFormStore(options) {
|
|
|
3738
4099
|
const pathKey = canonicalizePath(path).key;
|
|
3739
4100
|
if (meta?.blank === true) {
|
|
3740
4101
|
blankPaths.add(pathKey);
|
|
3741
|
-
} else
|
|
3742
|
-
blankPaths.delete(pathKey);
|
|
4102
|
+
} else {
|
|
4103
|
+
if (blankPaths.has(pathKey)) blankPaths.delete(pathKey);
|
|
4104
|
+
if (meta?.arrayOp === void 0) {
|
|
4105
|
+
for (const existingKey of [...blankPaths]) {
|
|
4106
|
+
if (isPathKeyUnder(existingKey, path)) blankPaths.delete(existingKey);
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
3743
4109
|
}
|
|
3744
4110
|
const wasAuthoredBefore = authoredPaths.has(pathKey);
|
|
3745
4111
|
walkAuthoredFromConstraints(value, path, authoredPaths);
|
|
@@ -3763,14 +4129,14 @@ function createFormStore(options) {
|
|
|
3763
4129
|
applyFormReplacement(nextForm, meta);
|
|
3764
4130
|
if (meta?.arrayOp !== void 0) {
|
|
3765
4131
|
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
|
-
|
|
4132
|
+
arrayBookkeeping.migrateElementState(path, remap);
|
|
4133
|
+
for (const freshIndex of remap.fresh) arrayBookkeeping.seedFreshElement(path, freshIndex);
|
|
4134
|
+
arrayBookkeeping.dropSchemaErrorsAtChangedIndices(path, remap);
|
|
4135
|
+
arrayBookkeeping.abortValidationAtVacatedIndices(path, remap);
|
|
4136
|
+
variantMemory.applyArrayOp(path, meta.arrayOp);
|
|
3771
4137
|
arrayIdentity.applyOp(path, meta.arrayOp);
|
|
3772
4138
|
} else if (Array.isArray(value) && Array.isArray(currentValue)) {
|
|
3773
|
-
|
|
4139
|
+
variantMemory.clearUnderPath(path);
|
|
3774
4140
|
arrayIdentity.realign(path);
|
|
3775
4141
|
}
|
|
3776
4142
|
const effectiveModeAfterWrite = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
@@ -3795,18 +4161,12 @@ function createFormStore(options) {
|
|
|
3795
4161
|
for (const k of blankPaths) {
|
|
3796
4162
|
if (isPathKeyUnder(k, parentPath)) outgoingBlanks.push(k);
|
|
3797
4163
|
}
|
|
3798
|
-
|
|
3799
|
-
if (memoryForUnion2 === void 0) {
|
|
3800
|
-
memoryForUnion2 = /* @__PURE__ */ new Map();
|
|
3801
|
-
variantMemory.set(parentKey, memoryForUnion2);
|
|
3802
|
-
}
|
|
3803
|
-
memoryForUnion2.set(oldDiscValue, {
|
|
4164
|
+
variantMemory.recordOutgoing(parentKey, oldDiscValue, {
|
|
3804
4165
|
value: currentValue2,
|
|
3805
4166
|
blankPaths: outgoingBlanks
|
|
3806
4167
|
});
|
|
3807
4168
|
}
|
|
3808
|
-
const
|
|
3809
|
-
const restored = memoryForUnion?.get(newDiscValue);
|
|
4169
|
+
const restored = variantMemory.lookupIncoming(parentKey, newDiscValue);
|
|
3810
4170
|
if (restored !== void 0) {
|
|
3811
4171
|
baseline = restored.value;
|
|
3812
4172
|
restoredBlanks = [...restored.blankPaths];
|
|
@@ -3878,12 +4238,10 @@ function createFormStore(options) {
|
|
|
3878
4238
|
const controller = new AbortController();
|
|
3879
4239
|
const fresh = { controller, timer: null, settled: false };
|
|
3880
4240
|
fieldValidationState.set(key, fresh);
|
|
4241
|
+
const myEpoch = ++scheduleEpoch;
|
|
3881
4242
|
const run = () => {
|
|
3882
4243
|
fresh.timer = null;
|
|
3883
4244
|
if (controller.signal.aborted) return;
|
|
3884
|
-
if (effectiveMode === "blur") {
|
|
3885
|
-
lastValidatedSnapshot = { value: structuralSnapshot(form.value) };
|
|
3886
|
-
}
|
|
3887
4245
|
let activeIncremented = false;
|
|
3888
4246
|
try {
|
|
3889
4247
|
activeValidations.value += 1;
|
|
@@ -3895,11 +4253,25 @@ function createFormStore(options) {
|
|
|
3895
4253
|
}
|
|
3896
4254
|
throw err;
|
|
3897
4255
|
}
|
|
3898
|
-
|
|
4256
|
+
const subtreeScope = path.length > 0 && schema.hasContainerOrRootRefine?.() === false;
|
|
4257
|
+
const scopePath = subtreeScope ? path : void 0;
|
|
4258
|
+
const dataAtScope = subtreeScope ? getAtPath(form.value, path) : form.value;
|
|
4259
|
+
const scopeKey = subtreeScope ? canonicalizePath(path).key : ROOT_PATH_KEY;
|
|
4260
|
+
void Promise.resolve().then(() => schema.validateAtPath(dataAtScope, scopePath)).then((response) => {
|
|
3899
4261
|
if (controller.signal.aborted) return;
|
|
4262
|
+
if (myEpoch <= lastCommittedEpoch) return;
|
|
4263
|
+
lastCommittedEpoch = myEpoch;
|
|
4264
|
+
if (effectiveMode === "blur") {
|
|
4265
|
+
const snapshotSource = scopePath !== void 0 ? getAtPath(form.value, scopePath) : form.value;
|
|
4266
|
+
pathSnapshots.set(scopeKey, structuralSnapshot(snapshotSource));
|
|
4267
|
+
}
|
|
3900
4268
|
const errors = response.success ? [] : response.errors;
|
|
3901
4269
|
const filtered = filterAuthoredErrors(errors);
|
|
3902
|
-
|
|
4270
|
+
const restamped = subtreeScope ? filtered.map((err) => ({
|
|
4271
|
+
...err,
|
|
4272
|
+
path: [...path, ...err.path]
|
|
4273
|
+
})) : filtered;
|
|
4274
|
+
applySchemaErrorsForSubtree(scopePath ?? [], restamped);
|
|
3903
4275
|
}).catch(() => {
|
|
3904
4276
|
}).finally(() => {
|
|
3905
4277
|
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
@@ -4142,11 +4514,24 @@ function createFormStore(options) {
|
|
|
4142
4514
|
const focusMode = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
4143
4515
|
if (!focused && focusMode === "blur") {
|
|
4144
4516
|
const firstInteractiveBlur = current?.interacted === true && current.blurredAfterInteraction !== true;
|
|
4145
|
-
|
|
4517
|
+
let snapshot = void 0;
|
|
4518
|
+
let snapshotScopeLength = 0;
|
|
4519
|
+
for (let i = path.length; i >= 0; i--) {
|
|
4520
|
+
const ancestorKey = canonicalizePath(path.slice(0, i)).key;
|
|
4521
|
+
const entry = pathSnapshots.get(ancestorKey);
|
|
4522
|
+
if (entry !== void 0) {
|
|
4523
|
+
snapshot = entry;
|
|
4524
|
+
snapshotScopeLength = i;
|
|
4525
|
+
break;
|
|
4526
|
+
}
|
|
4527
|
+
}
|
|
4146
4528
|
let changed = true;
|
|
4147
|
-
if (!firstInteractiveBlur && snapshot !==
|
|
4529
|
+
if (!firstInteractiveBlur && snapshot !== void 0) {
|
|
4530
|
+
const relPath = path.slice(snapshotScopeLength);
|
|
4531
|
+
const snapshotSubtree = getAtPath(snapshot, relPath);
|
|
4532
|
+
const liveSubtree = getAtPath(form.value, path);
|
|
4148
4533
|
changed = false;
|
|
4149
|
-
diffAndApply(
|
|
4534
|
+
diffAndApply(snapshotSubtree, liveSubtree, path, () => {
|
|
4150
4535
|
changed = true;
|
|
4151
4536
|
});
|
|
4152
4537
|
}
|
|
@@ -4158,10 +4543,6 @@ function createFormStore(options) {
|
|
|
4158
4543
|
}
|
|
4159
4544
|
}
|
|
4160
4545
|
}
|
|
4161
|
-
function markTouched(path) {
|
|
4162
|
-
const { key } = canonicalizePath(path);
|
|
4163
|
-
touchFieldRecord(key, path, { touched: true });
|
|
4164
|
-
}
|
|
4165
4546
|
function markInteracted(path) {
|
|
4166
4547
|
const { key } = canonicalizePath(path);
|
|
4167
4548
|
if (fields.get(key)?.interacted === true) return;
|
|
@@ -4185,9 +4566,6 @@ function createFormStore(options) {
|
|
|
4185
4566
|
);
|
|
4186
4567
|
}
|
|
4187
4568
|
}
|
|
4188
|
-
function clear(path) {
|
|
4189
|
-
return setValueAtPath(path, schema.getEmptyValueAtPath(path));
|
|
4190
|
-
}
|
|
4191
4569
|
function rehydrate() {
|
|
4192
4570
|
const factory = defaultValuesFactory.value;
|
|
4193
4571
|
if (factory === void 0) {
|
|
@@ -4334,6 +4712,9 @@ function createFormStore(options) {
|
|
|
4334
4712
|
submitError.value = null;
|
|
4335
4713
|
departAttempts.value = 0;
|
|
4336
4714
|
cancelFieldValidation();
|
|
4715
|
+
pathSnapshots.clear();
|
|
4716
|
+
scheduleEpoch = 0;
|
|
4717
|
+
lastCommittedEpoch = 0;
|
|
4337
4718
|
variantMemory.clear();
|
|
4338
4719
|
for (const listener of resetListeners) {
|
|
4339
4720
|
try {
|
|
@@ -4345,13 +4726,7 @@ function createFormStore(options) {
|
|
|
4345
4726
|
}
|
|
4346
4727
|
function resetField(path) {
|
|
4347
4728
|
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
|
-
}
|
|
4729
|
+
variantMemory.clearUnderPath(targetSegments);
|
|
4355
4730
|
const leafEntry = originals.get(targetKey);
|
|
4356
4731
|
if (leafEntry !== void 0) {
|
|
4357
4732
|
const wrote = setValueAtPath(targetSegments, leafEntry.value);
|
|
@@ -4412,15 +4787,11 @@ function createFormStore(options) {
|
|
|
4412
4787
|
blurredAfterInteraction: false
|
|
4413
4788
|
});
|
|
4414
4789
|
}
|
|
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
4790
|
function isPristineAtPath(path) {
|
|
4423
4791
|
const { key, segments } = canonicalizePath(path);
|
|
4792
|
+
return isPristineAtPathByKey(key, segments);
|
|
4793
|
+
}
|
|
4794
|
+
function isPristineAtPathByKey(key, segments) {
|
|
4424
4795
|
if (blankPaths.has(key) !== originalBlankPaths.has(key)) return false;
|
|
4425
4796
|
const entry = originals.get(key);
|
|
4426
4797
|
if (entry === void 0) return true;
|
|
@@ -4482,6 +4853,7 @@ function createFormStore(options) {
|
|
|
4482
4853
|
hydrating,
|
|
4483
4854
|
hydrateError,
|
|
4484
4855
|
defaultValuesFactory,
|
|
4856
|
+
hasSsrPrefetch: ssrPrefetch !== void 0,
|
|
4485
4857
|
defaultsResolved,
|
|
4486
4858
|
activated,
|
|
4487
4859
|
activationPromise,
|
|
@@ -4491,6 +4863,7 @@ function createFormStore(options) {
|
|
|
4491
4863
|
activeValidations,
|
|
4492
4864
|
firstValidationDone,
|
|
4493
4865
|
pathHasAsyncValidation,
|
|
4866
|
+
pathHasAsyncValidationByKey,
|
|
4494
4867
|
fieldValidationCounts,
|
|
4495
4868
|
applyFormReplacement,
|
|
4496
4869
|
setValueAtPath,
|
|
@@ -4498,7 +4871,6 @@ function createFormStore(options) {
|
|
|
4498
4871
|
arrayElementKey,
|
|
4499
4872
|
reset,
|
|
4500
4873
|
resetField,
|
|
4501
|
-
clear,
|
|
4502
4874
|
setSchemaErrorsForPath,
|
|
4503
4875
|
setAllSchemaErrors,
|
|
4504
4876
|
clearSchemaErrors,
|
|
@@ -4511,11 +4883,11 @@ function createFormStore(options) {
|
|
|
4511
4883
|
registerElement,
|
|
4512
4884
|
deregisterElement,
|
|
4513
4885
|
markFocused,
|
|
4514
|
-
markTouched,
|
|
4515
4886
|
markInteracted,
|
|
4516
4887
|
touchAtPath,
|
|
4517
4888
|
markConnectedOptimistically,
|
|
4518
4889
|
isPristineAtPath,
|
|
4890
|
+
isPristineAtPathByKey,
|
|
4519
4891
|
hasStructuralChangeUnder,
|
|
4520
4892
|
getFieldRecord,
|
|
4521
4893
|
getOriginalAtPath,
|
|
@@ -4532,7 +4904,6 @@ function createFormStore(options) {
|
|
|
4532
4904
|
modules,
|
|
4533
4905
|
persistOptIns,
|
|
4534
4906
|
isSensitivePath: resolvedIsSensitivePath,
|
|
4535
|
-
segmentMatchesSensitive: resolvedSegmentMatchesSensitive,
|
|
4536
4907
|
noSyncPaths,
|
|
4537
4908
|
incrementNoSyncOptOut,
|
|
4538
4909
|
decrementNoSyncOptOut,
|
|
@@ -4733,931 +5104,987 @@ function createHistoryModule(state, config) {
|
|
|
4733
5104
|
};
|
|
4734
5105
|
}
|
|
4735
5106
|
|
|
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;
|
|
5107
|
+
function wirePersistence(state, config) {
|
|
5108
|
+
let fingerprint;
|
|
5109
|
+
try {
|
|
5110
|
+
fingerprint = hashStableString(state.schema.fingerprint());
|
|
5111
|
+
} catch (err) {
|
|
5112
|
+
if (__DEV__) {
|
|
5113
|
+
console.warn(
|
|
5114
|
+
`[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.`
|
|
5115
|
+
);
|
|
4769
5116
|
}
|
|
4770
|
-
|
|
5117
|
+
fingerprint = "unfingerprinted";
|
|
4771
5118
|
}
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
5119
|
+
const base = resolveStorageKeyBase(config, state.formKey);
|
|
5120
|
+
const key = `${base}:${fingerprint}`;
|
|
5121
|
+
const debounceMs = normalizeNumericOption({
|
|
5122
|
+
value: config.debounceMs ?? DEFAULT_PERSISTENCE_DEBOUNCE_MS,
|
|
5123
|
+
source: "useForm.persist.debounceMs",
|
|
5124
|
+
allowInfinity: false,
|
|
5125
|
+
min: 0,
|
|
5126
|
+
defaultValue: DEFAULT_PERSISTENCE_DEBOUNCE_MS
|
|
5127
|
+
});
|
|
5128
|
+
const include = config.include ?? "form";
|
|
5129
|
+
const clearOnSubmitSuccess = config.clearOnSubmitSuccess ?? true;
|
|
5130
|
+
const adapterPromise = getStorageAdapter(config.storage);
|
|
5131
|
+
let disposed = false;
|
|
5132
|
+
const isDisposed = () => disposed;
|
|
5133
|
+
let inFlightFinalFlush = null;
|
|
5134
|
+
let pendingOptedInPaths = null;
|
|
5135
|
+
const writer = createDebouncedWriter(async () => {
|
|
5136
|
+
const optedInPaths = pendingOptedInPaths ?? new Set(state.persistOptIns.optedInPaths());
|
|
5137
|
+
pendingOptedInPaths = null;
|
|
5138
|
+
const adapter = await adapterPromise;
|
|
5139
|
+
if (isDisposed()) return;
|
|
5140
|
+
if (optedInPaths.size === 0) {
|
|
5141
|
+
await adapter.removeItem(key);
|
|
5142
|
+
return;
|
|
4775
5143
|
}
|
|
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;
|
|
5144
|
+
const rawForm = toRaw(state.form.value);
|
|
5145
|
+
const filteredForm = stripUnacknowledgedSensitiveLeaves(
|
|
5146
|
+
pluckPaths(rawForm, optedInPaths),
|
|
5147
|
+
optedInPaths,
|
|
5148
|
+
state.isSensitivePath
|
|
5149
|
+
);
|
|
5150
|
+
const filteredSchemaErrors = filterErrorsByPaths(state.schemaErrors, optedInPaths);
|
|
5151
|
+
const filteredUserErrors = filterErrorsByPaths(state.userErrors, optedInPaths);
|
|
5152
|
+
const filteredTransientEmpty = /* @__PURE__ */ new Set();
|
|
5153
|
+
for (const tk of state.blankPaths) {
|
|
5154
|
+
if (optedInPaths.has(tk)) filteredTransientEmpty.add(tk);
|
|
4806
5155
|
}
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
5156
|
+
const payload = buildPersistedPayload(
|
|
5157
|
+
filteredForm,
|
|
5158
|
+
include,
|
|
5159
|
+
filteredSchemaErrors,
|
|
5160
|
+
filteredUserErrors,
|
|
5161
|
+
filteredTransientEmpty
|
|
5162
|
+
);
|
|
5163
|
+
await adapter.setItem(key, payload);
|
|
5164
|
+
}, debounceMs);
|
|
5165
|
+
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
5166
|
+
if (isDisposed() || inFlightFinalFlush !== null) return;
|
|
5167
|
+
if (meta?.crossTab === true) return;
|
|
5168
|
+
if (meta?.persist !== true) return;
|
|
5169
|
+
pendingOptedInPaths = new Set(state.persistOptIns.optedInPaths());
|
|
5170
|
+
writer.schedule();
|
|
5171
|
+
});
|
|
5172
|
+
const unsubscribeSuccess = clearOnSubmitSuccess ? state.onSubmitSuccess(() => {
|
|
5173
|
+
if (isDisposed()) return;
|
|
5174
|
+
void (async () => {
|
|
5175
|
+
await writer.flush();
|
|
5176
|
+
if (isDisposed()) return;
|
|
5177
|
+
const adapter = await adapterPromise;
|
|
5178
|
+
if (isDisposed()) return;
|
|
5179
|
+
await adapter.removeItem(key);
|
|
5180
|
+
})();
|
|
5181
|
+
}) : () => void 0;
|
|
5182
|
+
const handlePageHide = () => {
|
|
5183
|
+
if (isDisposed()) return;
|
|
5184
|
+
void writer.flush();
|
|
5185
|
+
};
|
|
5186
|
+
if (typeof window !== "undefined") {
|
|
5187
|
+
window.addEventListener("pagehide", handlePageHide);
|
|
4836
5188
|
}
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
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;
|
|
5189
|
+
void (async () => {
|
|
5190
|
+
const adapter = await adapterPromise;
|
|
5191
|
+
if (isDisposed()) return;
|
|
5192
|
+
void cleanupOrphanKeys(adapter, base, key);
|
|
4871
5193
|
try {
|
|
4872
|
-
|
|
5194
|
+
const raw = await adapter.getItem(key);
|
|
5195
|
+
const payload = readPersistedPayload(raw);
|
|
5196
|
+
if (payload === null) {
|
|
5197
|
+
if (raw !== null && raw !== void 0) {
|
|
5198
|
+
await adapter.removeItem(key);
|
|
5199
|
+
}
|
|
5200
|
+
return;
|
|
5201
|
+
}
|
|
5202
|
+
if (isDisposed()) return;
|
|
5203
|
+
const merged = mergeSparseHydration(
|
|
5204
|
+
toRaw(state.form.value),
|
|
5205
|
+
payload.data.form,
|
|
5206
|
+
state.schema
|
|
5207
|
+
);
|
|
5208
|
+
state.applyFormReplacement(merged, { hydration: true });
|
|
5209
|
+
const persistedLeafPaths = collectPersistedLeafPaths(payload.data.form);
|
|
5210
|
+
for (const k of persistedLeafPaths) {
|
|
5211
|
+
state.blankPaths.delete(k);
|
|
5212
|
+
state.originalBlankPaths.delete(k);
|
|
5213
|
+
}
|
|
5214
|
+
for (const k of payload.data.blankPaths ?? []) {
|
|
5215
|
+
state.blankPaths.add(k);
|
|
5216
|
+
state.originalBlankPaths.add(k);
|
|
5217
|
+
}
|
|
5218
|
+
if (include === "form+errors") {
|
|
5219
|
+
if (payload.data.schemaErrors !== void 0) {
|
|
5220
|
+
const flat = payload.data.schemaErrors.flatMap(([, errs]) => errs);
|
|
5221
|
+
state.setAllSchemaErrors(flat);
|
|
5222
|
+
}
|
|
5223
|
+
if (payload.data.userErrors !== void 0) {
|
|
5224
|
+
const flat = payload.data.userErrors.flatMap(([, errs]) => errs);
|
|
5225
|
+
state.setAllUserErrors(flat);
|
|
5226
|
+
}
|
|
5227
|
+
}
|
|
5228
|
+
state.scheduleFieldValidation(
|
|
5229
|
+
[],
|
|
5230
|
+
true
|
|
5231
|
+
/* immediate */
|
|
5232
|
+
);
|
|
4873
5233
|
} catch {
|
|
4874
5234
|
}
|
|
4875
|
-
}
|
|
4876
|
-
function
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
const
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
const
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
if (isPathLocallySuppressed(p.path)) continue;
|
|
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;
|
|
5235
|
+
})();
|
|
5236
|
+
async function writePathImmediately(path) {
|
|
5237
|
+
if (isDisposed()) return;
|
|
5238
|
+
await writer.flush();
|
|
5239
|
+
if (isDisposed()) return;
|
|
5240
|
+
const adapter = await adapterPromise;
|
|
5241
|
+
if (isDisposed()) return;
|
|
5242
|
+
const raw = await adapter.getItem(key);
|
|
5243
|
+
const existing = readPersistedPayload(raw);
|
|
5244
|
+
const value = getAtPath(toRaw(state.form.value), path);
|
|
5245
|
+
const nextForm = setAtPath(existing?.data.form ?? {}, path, value);
|
|
5246
|
+
const { key: pathKey } = canonicalizePath(path);
|
|
5247
|
+
const transientSet = new Set(
|
|
5248
|
+
(existing?.data.blankPaths ?? []).filter(
|
|
5249
|
+
(k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
|
|
5250
|
+
)
|
|
5251
|
+
);
|
|
5252
|
+
for (const liveKey of state.blankPaths) {
|
|
5253
|
+
if (liveKey === pathKey || isDescendantPathKey(liveKey, pathKey)) {
|
|
5254
|
+
transientSet.add(liveKey);
|
|
5255
|
+
}
|
|
4904
5256
|
}
|
|
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();
|
|
5257
|
+
if (include === "form") {
|
|
5258
|
+
await adapter.setItem(
|
|
5259
|
+
key,
|
|
5260
|
+
buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
|
|
5261
|
+
);
|
|
4921
5262
|
return;
|
|
4922
5263
|
}
|
|
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);
|
|
5264
|
+
const schemaMap = new Map(existing?.data.schemaErrors ?? []);
|
|
5265
|
+
const userMap = new Map(existing?.data.userErrors ?? []);
|
|
5266
|
+
const currentSchema = state.schemaErrors.get(pathKey);
|
|
5267
|
+
const currentUser = state.userErrors.get(pathKey);
|
|
5268
|
+
if (currentSchema !== void 0 && currentSchema.length > 0) {
|
|
5269
|
+
schemaMap.set(pathKey, [...currentSchema]);
|
|
5270
|
+
} else {
|
|
5271
|
+
schemaMap.delete(pathKey);
|
|
4941
5272
|
}
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
safeBlankAdded.push(k);
|
|
5273
|
+
if (currentUser !== void 0 && currentUser.length > 0) {
|
|
5274
|
+
userMap.set(pathKey, [...currentUser]);
|
|
5275
|
+
} else {
|
|
5276
|
+
userMap.delete(pathKey);
|
|
4947
5277
|
}
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
5278
|
+
await adapter.setItem(
|
|
5279
|
+
key,
|
|
5280
|
+
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5281
|
+
);
|
|
5282
|
+
}
|
|
5283
|
+
async function clearPersistedDraft(path) {
|
|
5284
|
+
if (isDisposed()) return;
|
|
5285
|
+
await writer.flush();
|
|
5286
|
+
if (isDisposed()) return;
|
|
5287
|
+
const adapter = await adapterPromise;
|
|
5288
|
+
if (isDisposed()) return;
|
|
5289
|
+
if (path === void 0) {
|
|
5290
|
+
await adapter.removeItem(key);
|
|
5291
|
+
return;
|
|
4953
5292
|
}
|
|
4954
|
-
|
|
5293
|
+
const raw = await adapter.getItem(key);
|
|
5294
|
+
const existing = readPersistedPayload(raw);
|
|
5295
|
+
if (existing === null) return;
|
|
5296
|
+
const nextForm = deleteAtPath(existing.data.form, path);
|
|
5297
|
+
if (isEmptyContainer(nextForm)) {
|
|
5298
|
+
await adapter.removeItem(key);
|
|
4955
5299
|
return;
|
|
4956
5300
|
}
|
|
4957
|
-
const
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
5301
|
+
const { key: pathKey } = canonicalizePath(path);
|
|
5302
|
+
const transientSet = new Set(
|
|
5303
|
+
(existing.data.blankPaths ?? []).filter(
|
|
5304
|
+
(k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
|
|
5305
|
+
)
|
|
5306
|
+
);
|
|
5307
|
+
if (include === "form") {
|
|
5308
|
+
await adapter.setItem(
|
|
5309
|
+
key,
|
|
5310
|
+
buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
|
|
5311
|
+
);
|
|
5312
|
+
return;
|
|
4966
5313
|
}
|
|
4967
|
-
const
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
5314
|
+
const schemaErrors = (existing.data.schemaErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5315
|
+
const userErrors = (existing.data.userErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5316
|
+
const schemaMap = new Map(schemaErrors.map(([k, v]) => [k, [...v]]));
|
|
5317
|
+
const userMap = new Map(userErrors.map(([k, v]) => [k, [...v]]));
|
|
5318
|
+
await adapter.setItem(
|
|
5319
|
+
key,
|
|
5320
|
+
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5321
|
+
);
|
|
4971
5322
|
}
|
|
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();
|
|
5323
|
+
function awaitPendingWrites() {
|
|
5324
|
+
if (inFlightFinalFlush !== null) return inFlightFinalFlush;
|
|
5325
|
+
if (isDisposed()) return Promise.resolve();
|
|
5326
|
+
return writer.flush().catch(() => void 0);
|
|
4986
5327
|
}
|
|
4987
|
-
function
|
|
4988
|
-
|
|
5328
|
+
function dispose() {
|
|
5329
|
+
if (isDisposed() || inFlightFinalFlush !== null) return;
|
|
5330
|
+
unsubscribeChange();
|
|
5331
|
+
unsubscribeSuccess();
|
|
5332
|
+
if (typeof window !== "undefined") {
|
|
5333
|
+
window.removeEventListener("pagehide", handlePageHide);
|
|
5334
|
+
}
|
|
5335
|
+
inFlightFinalFlush = writer.flush().catch(() => void 0).finally(() => {
|
|
5336
|
+
disposed = true;
|
|
5337
|
+
inFlightFinalFlush = null;
|
|
5338
|
+
});
|
|
4989
5339
|
}
|
|
4990
|
-
function respondToSnapshotRequest() {
|
|
4991
|
-
const scrubbedForm = stripSensitivePathsDeep(state.form.value, [], options.isSensitivePath);
|
|
4992
|
-
safePost({
|
|
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;
|
|
5033
|
-
}
|
|
5034
|
-
const sorted = [...peerIds].sort();
|
|
5035
|
-
const leaderId = sorted[0];
|
|
5036
|
-
peerIds.delete(leaderId);
|
|
5037
|
-
leaderAttempts++;
|
|
5038
|
-
safePost({
|
|
5039
|
-
v: PROTOCOL_VERSION,
|
|
5040
|
-
kind: "requestSnapshot",
|
|
5041
|
-
senderId,
|
|
5042
|
-
targetId: leaderId
|
|
5043
|
-
});
|
|
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
|
-
}
|
|
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
5340
|
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
|
|
5341
|
+
wiredConfig: config,
|
|
5342
|
+
writePathImmediately,
|
|
5343
|
+
clearPersistedDraft,
|
|
5344
|
+
awaitPendingWrites,
|
|
5345
|
+
dispose
|
|
5087
5346
|
};
|
|
5088
5347
|
}
|
|
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}`);
|
|
5348
|
+
function isEmptyContainer(value) {
|
|
5349
|
+
if (value === void 0 || value === null) return true;
|
|
5350
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
5351
|
+
if (isPlainRecord(value)) return Object.keys(value).length === 0;
|
|
5352
|
+
return false;
|
|
5098
5353
|
}
|
|
5099
|
-
function
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5354
|
+
function collectPersistedLeafPaths(form) {
|
|
5355
|
+
const out = [];
|
|
5356
|
+
walk(form, []);
|
|
5357
|
+
return out;
|
|
5358
|
+
function walk(node, prefix) {
|
|
5359
|
+
if (Array.isArray(node)) {
|
|
5360
|
+
for (let i = 0; i < node.length; i++) {
|
|
5361
|
+
walk(node[i], [...prefix, i]);
|
|
5362
|
+
}
|
|
5363
|
+
return;
|
|
5364
|
+
}
|
|
5365
|
+
if (isPlainRecord(node)) {
|
|
5366
|
+
for (const key of Object.keys(node)) {
|
|
5367
|
+
walk(node[key], [...prefix, key]);
|
|
5368
|
+
}
|
|
5369
|
+
return;
|
|
5370
|
+
}
|
|
5371
|
+
if (prefix.length === 0) return;
|
|
5372
|
+
out.push(canonicalizePath(prefix).key);
|
|
5107
5373
|
}
|
|
5108
5374
|
}
|
|
5109
|
-
function
|
|
5110
|
-
|
|
5375
|
+
function isDescendantPathKey(candidate, ancestor) {
|
|
5376
|
+
if (candidate.length <= ancestor.length) return false;
|
|
5377
|
+
if (!ancestor.endsWith("]")) return false;
|
|
5378
|
+
const childPrefix = `${ancestor.slice(0, -1)},`;
|
|
5379
|
+
return candidate.startsWith(childPrefix);
|
|
5111
5380
|
}
|
|
5112
5381
|
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5382
|
+
const PROTOCOL_VERSION = 1;
|
|
5383
|
+
const JOIN_COLLECTION_WINDOW_MS = 50;
|
|
5384
|
+
const SNAPSHOT_TIMEOUT_MS = 200;
|
|
5385
|
+
const MAX_LEADER_ATTEMPTS = 3;
|
|
5386
|
+
const SNAPSHOT_RESPONSE_MIN_INTERVAL_MS = 500;
|
|
5387
|
+
function isFileLikeValue(value) {
|
|
5388
|
+
if (typeof File !== "undefined" && value instanceof File) return true;
|
|
5389
|
+
if (typeof Blob !== "undefined" && value instanceof Blob) return true;
|
|
5390
|
+
return false;
|
|
5118
5391
|
}
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
const resolvedDefaults = resolveTrichotomy(configuration.defaultValues);
|
|
5129
|
-
const materialisedDefaults = resolvedDefaults.kind === "sync" ? resolvedDefaults.value : void 0;
|
|
5130
|
-
const { defaultValues: _droppedDefaults, ...configWithoutDefaults } = configuration;
|
|
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
|
-
});
|
|
5147
|
-
}
|
|
5148
|
-
const existing = registry.forms.get(key);
|
|
5149
|
-
if (__DEV__ && existing !== void 0) {
|
|
5150
|
-
warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
|
|
5151
|
-
warnOnPersistDivergence(key, existing, configuration.persist);
|
|
5392
|
+
function isInboundShapeAcceptable(schema, path, value) {
|
|
5393
|
+
if (isFileLikeValue(value)) return false;
|
|
5394
|
+
let kind;
|
|
5395
|
+
if (Array.isArray(value)) {
|
|
5396
|
+
kind = "array";
|
|
5397
|
+
} else if (value !== null && typeof value === "object" && isPlainRecord(value)) {
|
|
5398
|
+
kind = "object";
|
|
5399
|
+
} else {
|
|
5400
|
+
kind = slimKindOf(value);
|
|
5152
5401
|
}
|
|
5153
|
-
const
|
|
5154
|
-
|
|
5155
|
-
if (
|
|
5156
|
-
|
|
5402
|
+
const accepted = schema.getSlimPrimitiveTypesAtPath(path);
|
|
5403
|
+
if (!accepted.has(kind)) return false;
|
|
5404
|
+
if (Array.isArray(value)) {
|
|
5405
|
+
for (let i = 0; i < value.length; i++) {
|
|
5406
|
+
if (!isInboundShapeAcceptable(schema, [...path, i], value[i])) return false;
|
|
5407
|
+
}
|
|
5408
|
+
return true;
|
|
5157
5409
|
}
|
|
5158
|
-
if (
|
|
5159
|
-
const
|
|
5160
|
-
|
|
5161
|
-
if (hadPendingHydration) {
|
|
5162
|
-
state.hydrating.value = false;
|
|
5163
|
-
state.defaultsResolved.value = true;
|
|
5164
|
-
} else if (registry.ssr) {
|
|
5165
|
-
if (configuration.__ssrAccessed === true) {
|
|
5166
|
-
registry.enqueuePrefetch(key);
|
|
5167
|
-
}
|
|
5168
|
-
onServerPrefetch(() => {
|
|
5169
|
-
if (!registry.shouldPrefetch(key)) return;
|
|
5170
|
-
return state.activate();
|
|
5171
|
-
});
|
|
5410
|
+
if (isPlainRecord(value)) {
|
|
5411
|
+
for (const key of Object.keys(value)) {
|
|
5412
|
+
if (!isInboundShapeAcceptable(schema, [...path, key], value[key])) return false;
|
|
5172
5413
|
}
|
|
5414
|
+
return true;
|
|
5173
5415
|
}
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5416
|
+
return true;
|
|
5417
|
+
}
|
|
5418
|
+
function diffBlankPaths(prev, curr) {
|
|
5419
|
+
const added = [];
|
|
5420
|
+
const removed = [];
|
|
5421
|
+
const prevSet = new Set(prev);
|
|
5422
|
+
for (const k of curr) if (!prevSet.has(k)) added.push(k);
|
|
5423
|
+
for (const k of prev) if (!curr.has(k)) removed.push(k);
|
|
5424
|
+
return { added, removed };
|
|
5425
|
+
}
|
|
5426
|
+
function snapshotForm(form) {
|
|
5427
|
+
return structuralSnapshot(form);
|
|
5428
|
+
}
|
|
5429
|
+
function stripSensitivePathsDeep(value, pathSoFar, isSensitivePath) {
|
|
5430
|
+
if (isFileLikeValue(value)) return void 0;
|
|
5431
|
+
if (value === null || typeof value !== "object") return value;
|
|
5432
|
+
if (Array.isArray(value)) {
|
|
5433
|
+
return value.map((item, i) => stripSensitivePathsDeep(item, [...pathSoFar, i], isSensitivePath));
|
|
5177
5434
|
}
|
|
5178
|
-
const
|
|
5179
|
-
if (
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
warnOnceInsecureContext(feature);
|
|
5188
|
-
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
5189
|
-
} else {
|
|
5190
|
-
const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
|
|
5191
|
-
void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
|
|
5192
|
-
const persistenceModule = wirePersistence(state, resolvedPersist);
|
|
5193
|
-
state.modules.set(PERSISTENCE_MODULE_KEY, persistenceModule);
|
|
5194
|
-
state.registerDrain(() => persistenceModule.awaitPendingWrites());
|
|
5195
|
-
state.registerCleanup(() => persistenceModule.dispose());
|
|
5196
|
-
}
|
|
5197
|
-
} else {
|
|
5198
|
-
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
5435
|
+
const proto = Object.getPrototypeOf(value);
|
|
5436
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
5437
|
+
const out = {};
|
|
5438
|
+
const src = value;
|
|
5439
|
+
for (const key of Object.keys(src)) {
|
|
5440
|
+
const childPath = [...pathSoFar, key];
|
|
5441
|
+
if (isSensitivePath(childPath)) {
|
|
5442
|
+
safeAssign(out, key, void 0);
|
|
5443
|
+
continue;
|
|
5199
5444
|
}
|
|
5445
|
+
safeAssign(out, key, stripSensitivePathsDeep(src[key], childPath, isSensitivePath));
|
|
5200
5446
|
}
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5447
|
+
return out;
|
|
5448
|
+
}
|
|
5449
|
+
function isStringArray(value) {
|
|
5450
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
5451
|
+
}
|
|
5452
|
+
function isPatchArray(value) {
|
|
5453
|
+
return Array.isArray(value) && value.every(
|
|
5454
|
+
(p) => p !== null && typeof p === "object" && Array.isArray(p.path)
|
|
5455
|
+
);
|
|
5456
|
+
}
|
|
5457
|
+
function isValidSyncMessage(data) {
|
|
5458
|
+
if (data === null || typeof data !== "object") return false;
|
|
5459
|
+
const m = data;
|
|
5460
|
+
if (m["v"] !== PROTOCOL_VERSION) return false;
|
|
5461
|
+
if (typeof m["senderId"] !== "string") return false;
|
|
5462
|
+
if (typeof m["kind"] !== "string") return false;
|
|
5463
|
+
switch (m["kind"]) {
|
|
5464
|
+
case "hello":
|
|
5465
|
+
case "announce":
|
|
5466
|
+
return true;
|
|
5467
|
+
case "requestSnapshot":
|
|
5468
|
+
return typeof m["targetId"] === "string";
|
|
5469
|
+
case "snapshot":
|
|
5470
|
+
return isStringArray(m["blankPaths"]) && "form" in m;
|
|
5471
|
+
case "patches":
|
|
5472
|
+
return isPatchArray(m["formPatches"]) && isStringArray(m["blankPathsAdded"]) && isStringArray(m["blankPathsRemoved"]);
|
|
5473
|
+
default:
|
|
5474
|
+
return false;
|
|
5229
5475
|
}
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5476
|
+
}
|
|
5477
|
+
function generateSenderId() {
|
|
5478
|
+
try {
|
|
5479
|
+
return globalThis.crypto.randomUUID();
|
|
5480
|
+
} catch {
|
|
5481
|
+
return `atta-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;
|
|
5234
5482
|
}
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5483
|
+
}
|
|
5484
|
+
function createMultiTabSyncModule(state, channelName, options) {
|
|
5485
|
+
if (typeof BroadcastChannel === "undefined") {
|
|
5486
|
+
return {
|
|
5487
|
+
dispose: () => void 0,
|
|
5488
|
+
lifecycle: () => "established",
|
|
5489
|
+
senderId: "",
|
|
5490
|
+
channelName
|
|
5491
|
+
};
|
|
5238
5492
|
}
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5493
|
+
let channel;
|
|
5494
|
+
try {
|
|
5495
|
+
channel = new BroadcastChannel(channelName);
|
|
5496
|
+
} catch {
|
|
5497
|
+
return {
|
|
5498
|
+
dispose: () => void 0,
|
|
5499
|
+
lifecycle: () => "established",
|
|
5500
|
+
senderId: "",
|
|
5501
|
+
channelName
|
|
5502
|
+
};
|
|
5242
5503
|
}
|
|
5243
|
-
const
|
|
5244
|
-
|
|
5245
|
-
|
|
5504
|
+
const senderId = generateSenderId();
|
|
5505
|
+
let lifecycle = "joining";
|
|
5506
|
+
let disposed = false;
|
|
5507
|
+
const peerIds = /* @__PURE__ */ new Set();
|
|
5508
|
+
const lastSnapshotResponseAt = /* @__PURE__ */ new Map();
|
|
5509
|
+
let joinCollectionTimer = null;
|
|
5510
|
+
let snapshotTimeoutTimer = null;
|
|
5511
|
+
let leaderAttempts = 0;
|
|
5512
|
+
let prior = {
|
|
5513
|
+
form: snapshotForm(state.form.value),
|
|
5514
|
+
blankPathsSnapshot: [...state.blankPaths]
|
|
5515
|
+
};
|
|
5516
|
+
function safePost(msg) {
|
|
5517
|
+
if (disposed) return;
|
|
5518
|
+
try {
|
|
5519
|
+
channel.postMessage(msg);
|
|
5520
|
+
} catch {
|
|
5521
|
+
}
|
|
5246
5522
|
}
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5523
|
+
function refreshPrior() {
|
|
5524
|
+
prior = {
|
|
5525
|
+
form: snapshotForm(state.form.value),
|
|
5526
|
+
blankPathsSnapshot: [...state.blankPaths]
|
|
5527
|
+
};
|
|
5250
5528
|
}
|
|
5251
|
-
|
|
5252
|
-
|
|
5529
|
+
function isPathLocallySuppressed(path) {
|
|
5530
|
+
if (options.isSensitivePath(path)) return true;
|
|
5531
|
+
const { key } = canonicalizePath([...path]);
|
|
5532
|
+
if (options.noSyncPaths.has(key)) return true;
|
|
5533
|
+
return false;
|
|
5253
5534
|
}
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5535
|
+
function postPatches() {
|
|
5536
|
+
if (lifecycle !== "established") return;
|
|
5537
|
+
const next = snapshotForm(state.form.value);
|
|
5538
|
+
const rawPatches = [];
|
|
5539
|
+
diffAndApply(prior.form, next, [], (p) => rawPatches.push(p));
|
|
5540
|
+
const safePatches = [];
|
|
5541
|
+
for (const p of rawPatches) {
|
|
5542
|
+
if (isPathLocallySuppressed(p.path)) continue;
|
|
5543
|
+
if ("value" in p && isFileLikeValue(p.value)) continue;
|
|
5544
|
+
safePatches.push(p);
|
|
5545
|
+
}
|
|
5546
|
+
const { added, removed } = diffBlankPaths(prior.blankPathsSnapshot, state.blankPaths);
|
|
5547
|
+
if (safePatches.length === 0 && added.length === 0 && removed.length === 0) {
|
|
5548
|
+
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
5549
|
+
return;
|
|
5550
|
+
}
|
|
5551
|
+
safePost({
|
|
5552
|
+
v: PROTOCOL_VERSION,
|
|
5553
|
+
kind: "patches",
|
|
5554
|
+
senderId,
|
|
5555
|
+
formPatches: safePatches,
|
|
5556
|
+
blankPathsAdded: added,
|
|
5557
|
+
blankPathsRemoved: removed
|
|
5558
|
+
});
|
|
5559
|
+
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
5257
5560
|
}
|
|
5258
|
-
|
|
5259
|
-
|
|
5561
|
+
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
5562
|
+
if (disposed) return;
|
|
5563
|
+
if (lifecycle !== "established") return;
|
|
5564
|
+
if (meta?.crossTab === true) return;
|
|
5565
|
+
if (meta?.hydration === true) {
|
|
5566
|
+
refreshPrior();
|
|
5567
|
+
return;
|
|
5568
|
+
}
|
|
5569
|
+
postPatches();
|
|
5570
|
+
});
|
|
5571
|
+
function applyIncomingForm(form, blankPaths) {
|
|
5572
|
+
state.blankPaths.clear();
|
|
5573
|
+
for (const k of blankPaths) state.blankPaths.add(k);
|
|
5574
|
+
state.applyFormReplacement(form, { crossTab: true, persist: false });
|
|
5575
|
+
refreshPrior();
|
|
5260
5576
|
}
|
|
5261
|
-
|
|
5262
|
-
|
|
5577
|
+
function handlePatches(msg) {
|
|
5578
|
+
if (lifecycle !== "established") return;
|
|
5579
|
+
const safePatches = [];
|
|
5580
|
+
for (const p of msg.formPatches) {
|
|
5581
|
+
if (!Array.isArray(p.path)) continue;
|
|
5582
|
+
if (isPathLocallySuppressed(p.path)) continue;
|
|
5583
|
+
if ("value" in p && !isInboundShapeAcceptable(state.schema, p.path, p.value)) {
|
|
5584
|
+
continue;
|
|
5585
|
+
}
|
|
5586
|
+
safePatches.push(p);
|
|
5587
|
+
}
|
|
5588
|
+
const safeBlankAdded = [];
|
|
5589
|
+
for (const k of msg.blankPathsAdded) {
|
|
5590
|
+
const segs = canonicalizePath(k).segments;
|
|
5591
|
+
if (isPathLocallySuppressed(segs)) continue;
|
|
5592
|
+
safeBlankAdded.push(k);
|
|
5593
|
+
}
|
|
5594
|
+
const safeBlankRemoved = [];
|
|
5595
|
+
for (const k of msg.blankPathsRemoved) {
|
|
5596
|
+
const segs = canonicalizePath(k).segments;
|
|
5597
|
+
if (isPathLocallySuppressed(segs)) continue;
|
|
5598
|
+
safeBlankRemoved.push(k);
|
|
5599
|
+
}
|
|
5600
|
+
if (safePatches.length === 0 && safeBlankAdded.length === 0 && safeBlankRemoved.length === 0) {
|
|
5601
|
+
return;
|
|
5602
|
+
}
|
|
5603
|
+
const candidate = applyPatchesForward(state.form.value, safePatches);
|
|
5604
|
+
try {
|
|
5605
|
+
options.validateForm(state.form.value);
|
|
5606
|
+
try {
|
|
5607
|
+
options.validateForm(candidate);
|
|
5608
|
+
} catch {
|
|
5609
|
+
return;
|
|
5610
|
+
}
|
|
5611
|
+
} catch {
|
|
5612
|
+
}
|
|
5613
|
+
const nextBlankPaths = new Set(state.blankPaths);
|
|
5614
|
+
for (const k of safeBlankRemoved) nextBlankPaths.delete(k);
|
|
5615
|
+
for (const k of safeBlankAdded) nextBlankPaths.add(k);
|
|
5616
|
+
applyIncomingForm(candidate, [...nextBlankPaths]);
|
|
5263
5617
|
}
|
|
5264
|
-
|
|
5265
|
-
|
|
5618
|
+
function handleSnapshot(msg) {
|
|
5619
|
+
if (lifecycle !== "joining") return;
|
|
5620
|
+
if (!isInboundShapeAcceptable(state.schema, [], msg.form)) return;
|
|
5621
|
+
if (snapshotTimeoutTimer !== null) {
|
|
5622
|
+
clearTimeout(snapshotTimeoutTimer);
|
|
5623
|
+
snapshotTimeoutTimer = null;
|
|
5624
|
+
}
|
|
5625
|
+
if (joinCollectionTimer !== null) {
|
|
5626
|
+
clearTimeout(joinCollectionTimer);
|
|
5627
|
+
joinCollectionTimer = null;
|
|
5628
|
+
}
|
|
5629
|
+
applyIncomingForm(msg.form, msg.blankPaths);
|
|
5630
|
+
lifecycle = "established";
|
|
5631
|
+
peerIds.clear();
|
|
5266
5632
|
}
|
|
5267
|
-
|
|
5268
|
-
|
|
5633
|
+
function respondToHello() {
|
|
5634
|
+
safePost({ v: PROTOCOL_VERSION, kind: "announce", senderId });
|
|
5269
5635
|
}
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
const debounceMs = configuration.debounceMs ?? defaults.debounceMs;
|
|
5284
|
-
const getDisplayState = configuration.getDisplayState ?? defaults.getDisplayState;
|
|
5285
|
-
const maxRecursionDepth = configuration.maxRecursionDepth ?? defaults.maxRecursionDepth;
|
|
5286
|
-
const sensitiveNames = configuration.sensitiveNames ?? defaults.sensitiveNames;
|
|
5287
|
-
const multiTab = configuration.multiTab ?? defaults.multiTab;
|
|
5288
|
-
const autoAria = configuration.autoAria ?? defaults.autoAria;
|
|
5289
|
-
return {
|
|
5290
|
-
...configuration,
|
|
5291
|
-
...strict === void 0 ? {} : { strict },
|
|
5292
|
-
...onInvalidSubmit === void 0 ? {} : { onInvalidSubmit },
|
|
5293
|
-
...history === void 0 ? {} : { history },
|
|
5294
|
-
...rememberVariants === void 0 ? {} : { rememberVariants },
|
|
5295
|
-
...coerce === void 0 ? {} : { coerce },
|
|
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;
|
|
5636
|
+
function respondToSnapshotRequest(requesterId) {
|
|
5637
|
+
const now = Date.now();
|
|
5638
|
+
const last = lastSnapshotResponseAt.get(requesterId);
|
|
5639
|
+
if (last !== void 0 && now - last < SNAPSHOT_RESPONSE_MIN_INTERVAL_MS) return;
|
|
5640
|
+
lastSnapshotResponseAt.set(requesterId, now);
|
|
5641
|
+
const scrubbedForm = stripSensitivePathsDeep(state.form.value, [], options.isSensitivePath);
|
|
5642
|
+
safePost({
|
|
5643
|
+
v: PROTOCOL_VERSION,
|
|
5644
|
+
kind: "snapshot",
|
|
5645
|
+
senderId,
|
|
5646
|
+
form: scrubbedForm,
|
|
5647
|
+
blankPaths: [...state.blankPaths]
|
|
5648
|
+
});
|
|
5316
5649
|
}
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5650
|
+
channel.onmessage = (event) => {
|
|
5651
|
+
if (disposed) return;
|
|
5652
|
+
try {
|
|
5653
|
+
const data = event.data;
|
|
5654
|
+
if (!isValidSyncMessage(data)) return;
|
|
5655
|
+
const msg = data;
|
|
5656
|
+
if (msg.senderId === senderId) return;
|
|
5657
|
+
switch (msg.kind) {
|
|
5658
|
+
case "hello":
|
|
5659
|
+
if (lifecycle !== "established") return;
|
|
5660
|
+
respondToHello();
|
|
5661
|
+
break;
|
|
5662
|
+
case "announce":
|
|
5663
|
+
if (lifecycle === "joining") peerIds.add(msg.senderId);
|
|
5664
|
+
break;
|
|
5665
|
+
case "requestSnapshot":
|
|
5666
|
+
if (lifecycle !== "established") return;
|
|
5667
|
+
if (msg.targetId !== senderId) return;
|
|
5668
|
+
respondToSnapshotRequest(msg.senderId);
|
|
5669
|
+
break;
|
|
5670
|
+
case "snapshot":
|
|
5671
|
+
handleSnapshot(msg);
|
|
5672
|
+
break;
|
|
5673
|
+
case "patches":
|
|
5674
|
+
handlePatches(msg);
|
|
5675
|
+
break;
|
|
5342
5676
|
}
|
|
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()
|
|
5677
|
+
} catch {
|
|
5678
|
+
}
|
|
5368
5679
|
};
|
|
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);
|
|
5680
|
+
function electLeaderAndRequest() {
|
|
5681
|
+
if (disposed) return;
|
|
5682
|
+
if (peerIds.size === 0) {
|
|
5683
|
+
lifecycle = "established";
|
|
5684
|
+
refreshPrior();
|
|
5685
|
+
return;
|
|
5380
5686
|
}
|
|
5381
|
-
|
|
5687
|
+
const sorted = [...peerIds].sort();
|
|
5688
|
+
const leaderId = sorted[0];
|
|
5689
|
+
peerIds.delete(leaderId);
|
|
5690
|
+
leaderAttempts++;
|
|
5691
|
+
safePost({
|
|
5692
|
+
v: PROTOCOL_VERSION,
|
|
5693
|
+
kind: "requestSnapshot",
|
|
5694
|
+
senderId,
|
|
5695
|
+
targetId: leaderId
|
|
5696
|
+
});
|
|
5697
|
+
snapshotTimeoutTimer = setTimeout(() => {
|
|
5698
|
+
snapshotTimeoutTimer = null;
|
|
5699
|
+
if (disposed) return;
|
|
5700
|
+
if (lifecycle === "established") return;
|
|
5701
|
+
if (leaderAttempts >= MAX_LEADER_ATTEMPTS || peerIds.size === 0) {
|
|
5702
|
+
lifecycle = "established";
|
|
5703
|
+
refreshPrior();
|
|
5704
|
+
return;
|
|
5705
|
+
}
|
|
5706
|
+
electLeaderAndRequest();
|
|
5707
|
+
}, SNAPSHOT_TIMEOUT_MS);
|
|
5382
5708
|
}
|
|
5383
|
-
|
|
5384
|
-
|
|
5709
|
+
function joinFlow() {
|
|
5710
|
+
safePost({ v: PROTOCOL_VERSION, kind: "hello", senderId });
|
|
5711
|
+
joinCollectionTimer = setTimeout(() => {
|
|
5712
|
+
joinCollectionTimer = null;
|
|
5713
|
+
if (disposed) return;
|
|
5714
|
+
if (lifecycle === "established") return;
|
|
5715
|
+
electLeaderAndRequest();
|
|
5716
|
+
}, JOIN_COLLECTION_WINDOW_MS);
|
|
5385
5717
|
}
|
|
5386
|
-
|
|
5718
|
+
joinFlow();
|
|
5719
|
+
return {
|
|
5720
|
+
dispose: () => {
|
|
5721
|
+
if (disposed) return;
|
|
5722
|
+
disposed = true;
|
|
5723
|
+
if (joinCollectionTimer !== null) {
|
|
5724
|
+
clearTimeout(joinCollectionTimer);
|
|
5725
|
+
joinCollectionTimer = null;
|
|
5726
|
+
}
|
|
5727
|
+
if (snapshotTimeoutTimer !== null) {
|
|
5728
|
+
clearTimeout(snapshotTimeoutTimer);
|
|
5729
|
+
snapshotTimeoutTimer = null;
|
|
5730
|
+
}
|
|
5731
|
+
unsubscribeChange();
|
|
5732
|
+
try {
|
|
5733
|
+
channel.close();
|
|
5734
|
+
} catch {
|
|
5735
|
+
}
|
|
5736
|
+
},
|
|
5737
|
+
lifecycle: () => lifecycle,
|
|
5738
|
+
senderId,
|
|
5739
|
+
channelName
|
|
5740
|
+
};
|
|
5387
5741
|
}
|
|
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
|
-
);
|
|
5742
|
+
const MULTI_TAB_SYNC_MODULE_KEY = "multiTabSync";
|
|
5743
|
+
|
|
5744
|
+
const warned = /* @__PURE__ */ new Set();
|
|
5745
|
+
function warnOnceInsecureContext(feature) {
|
|
5746
|
+
if (!__DEV__) return;
|
|
5747
|
+
if (warned.has(feature)) return;
|
|
5748
|
+
warned.add(feature);
|
|
5749
|
+
const message = featureMessage(feature);
|
|
5750
|
+
console.warn(`[attaform] ${message}`);
|
|
5407
5751
|
}
|
|
5408
|
-
function
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
return;
|
|
5752
|
+
function featureMessage(feature) {
|
|
5753
|
+
switch (feature) {
|
|
5754
|
+
case "multiTab":
|
|
5755
|
+
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.";
|
|
5756
|
+
case "persist:local":
|
|
5757
|
+
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.";
|
|
5758
|
+
case "persist:session":
|
|
5759
|
+
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
5760
|
}
|
|
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
5761
|
}
|
|
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;
|
|
5762
|
+
function isSecureContext() {
|
|
5763
|
+
return typeof window !== "undefined" && window.isSecureContext === true;
|
|
5430
5764
|
}
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
return
|
|
5765
|
+
|
|
5766
|
+
function resolveTrichotomy(input) {
|
|
5767
|
+
if (typeof input === "function") {
|
|
5768
|
+
return { kind: "async", factory: input };
|
|
5769
|
+
}
|
|
5770
|
+
return { kind: "sync", value: input };
|
|
5437
5771
|
}
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5772
|
+
|
|
5773
|
+
function useAbstractForm(configuration, options) {
|
|
5774
|
+
if (configuration === void 0 || configuration === null || configuration.schema === void 0) {
|
|
5775
|
+
throw new InvalidUseFormConfigError();
|
|
5776
|
+
}
|
|
5777
|
+
const key = resolveFormKey(configuration.key);
|
|
5778
|
+
const instance = getCurrentInstance();
|
|
5779
|
+
if (instance !== null) ensureAttaformInstalled(instance.appContext.app);
|
|
5780
|
+
const registry = options?.registry ?? useRegistry();
|
|
5781
|
+
const resolvedDefaults = resolveTrichotomy(configuration.defaultValues);
|
|
5782
|
+
const materialisedDefaults = resolvedDefaults.kind === "sync" ? resolvedDefaults.value : void 0;
|
|
5783
|
+
const { defaultValues: _droppedDefaults, ...configWithoutDefaults } = configuration;
|
|
5784
|
+
const trichotomyOverride = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
|
|
5785
|
+
const merged = mergeWithDefaults(registry.defaults, trichotomyOverride);
|
|
5786
|
+
const maxRecursionDepth = normalizeNumericOption({
|
|
5787
|
+
value: merged.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH,
|
|
5788
|
+
source: "useForm.maxRecursionDepth",
|
|
5789
|
+
allowInfinity: true,
|
|
5446
5790
|
min: 0,
|
|
5447
|
-
defaultValue:
|
|
5791
|
+
defaultValue: DEFAULT_MAX_RECURSION_DEPTH
|
|
5448
5792
|
});
|
|
5449
|
-
const
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
const
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
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();
|
|
5487
|
-
});
|
|
5488
|
-
const unsubscribeSuccess = clearOnSubmitSuccess ? state.onSubmitSuccess(() => {
|
|
5489
|
-
if (disposed) return;
|
|
5490
|
-
void (async () => {
|
|
5491
|
-
await writer.flush();
|
|
5492
|
-
if (disposed) return;
|
|
5493
|
-
const adapter = await adapterPromise;
|
|
5494
|
-
if (disposed) return;
|
|
5495
|
-
await adapter.removeItem(key);
|
|
5496
|
-
})();
|
|
5497
|
-
}) : () => void 0;
|
|
5498
|
-
void (async () => {
|
|
5499
|
-
const adapter = await adapterPromise;
|
|
5500
|
-
if (disposed) return;
|
|
5501
|
-
void cleanupOrphanKeys(adapter, base, key);
|
|
5502
|
-
try {
|
|
5503
|
-
const raw = await adapter.getItem(key);
|
|
5504
|
-
const payload = readPersistedPayload(raw);
|
|
5505
|
-
if (payload === null) {
|
|
5506
|
-
if (raw !== null && raw !== void 0) {
|
|
5507
|
-
await adapter.removeItem(key);
|
|
5508
|
-
}
|
|
5509
|
-
return;
|
|
5510
|
-
}
|
|
5511
|
-
if (disposed) return;
|
|
5512
|
-
const merged = mergeSparseHydration(
|
|
5513
|
-
toRaw(state.form.value),
|
|
5514
|
-
payload.data.form,
|
|
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
|
-
}
|
|
5793
|
+
const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
|
|
5794
|
+
if (configuration.persist !== void 0 && configuration.key === void 0) {
|
|
5795
|
+
throw new AnonPersistError({
|
|
5796
|
+
cause: "no-key",
|
|
5797
|
+
schemaFields: extractSchemaFields(resolvedSchema),
|
|
5798
|
+
callSite: captureUserCallSite()
|
|
5799
|
+
});
|
|
5800
|
+
}
|
|
5801
|
+
const existing = registry.forms.get(key);
|
|
5802
|
+
if (__DEV__ && existing !== void 0) {
|
|
5803
|
+
warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
|
|
5804
|
+
warnOnPersistDivergence(key, existing, configuration.persist);
|
|
5805
|
+
}
|
|
5806
|
+
const hadPendingHydration = registry.pendingHydration.has(key);
|
|
5807
|
+
const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
|
|
5808
|
+
if (existing !== void 0) ; else if (resolvedDefaults.kind === "sync") {
|
|
5809
|
+
state.defaultsResolved.value = true;
|
|
5810
|
+
}
|
|
5811
|
+
if (existing === void 0 && resolvedDefaults.kind === "async") {
|
|
5812
|
+
const factory = resolvedDefaults.factory;
|
|
5813
|
+
state.defaultValuesFactory.value = factory;
|
|
5814
|
+
if (hadPendingHydration) {
|
|
5815
|
+
state.hydrating.value = false;
|
|
5816
|
+
state.defaultsResolved.value = true;
|
|
5817
|
+
} else if (registry.ssr) {
|
|
5818
|
+
if (configuration.__ssrAccessed === true) {
|
|
5819
|
+
registry.enqueuePrefetch(key);
|
|
5537
5820
|
}
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
);
|
|
5543
|
-
} catch {
|
|
5821
|
+
onServerPrefetch(() => {
|
|
5822
|
+
if (!registry.shouldPrefetch(key)) return;
|
|
5823
|
+
return state.activate();
|
|
5824
|
+
});
|
|
5544
5825
|
}
|
|
5545
|
-
}
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5826
|
+
}
|
|
5827
|
+
if (getCurrentScope() !== void 0) {
|
|
5828
|
+
const releaseConsumer = registry.trackConsumer(key);
|
|
5829
|
+
onScopeDispose(releaseConsumer);
|
|
5830
|
+
}
|
|
5831
|
+
const persistDisabledByAnonRule = merged.persist !== void 0 && enforceAnonPersistRule(state.formKey, registry.ssr);
|
|
5832
|
+
if (existing === void 0 && !registry.ssr) {
|
|
5833
|
+
if (merged.persist !== void 0 && !persistDisabledByAnonRule) {
|
|
5834
|
+
const resolvedPersist = normalizePersistConfig(merged.persist);
|
|
5835
|
+
const storageKind = resolvedPersist.storage;
|
|
5836
|
+
const isBuiltinStorage = typeof storageKind === "string";
|
|
5837
|
+
const secureContextOk = !isBuiltinStorage || isSecureContext();
|
|
5838
|
+
if (!secureContextOk) {
|
|
5839
|
+
const feature = storageKind === "session" ? "persist:session" : "persist:local";
|
|
5840
|
+
warnOnceInsecureContext(feature);
|
|
5841
|
+
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
5842
|
+
} else {
|
|
5843
|
+
const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
|
|
5844
|
+
void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
|
|
5845
|
+
const persistenceModule = wirePersistence(state, resolvedPersist);
|
|
5846
|
+
state.modules.set(PERSISTENCE_MODULE_KEY, persistenceModule);
|
|
5847
|
+
state.registerDrain(() => persistenceModule.awaitPendingWrites());
|
|
5848
|
+
state.registerCleanup(() => persistenceModule.dispose());
|
|
5566
5849
|
}
|
|
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
|
-
} else {
|
|
5582
|
-
schemaMap.delete(pathKey);
|
|
5583
|
-
}
|
|
5584
|
-
if (currentUser !== void 0 && currentUser.length > 0) {
|
|
5585
|
-
userMap.set(pathKey, [...currentUser]);
|
|
5586
5850
|
} else {
|
|
5587
|
-
|
|
5851
|
+
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
5588
5852
|
}
|
|
5589
|
-
await adapter.setItem(
|
|
5590
|
-
key,
|
|
5591
|
-
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5592
|
-
);
|
|
5593
5853
|
}
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
if (
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5854
|
+
if (existing === void 0 && merged.multiTab === true && configuration.key !== void 0 && !registry.ssr) {
|
|
5855
|
+
const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
|
|
5856
|
+
const secureContext = isSecureContext();
|
|
5857
|
+
if (hasBroadcastChannel && secureContext) {
|
|
5858
|
+
let channelName;
|
|
5859
|
+
try {
|
|
5860
|
+
channelName = `attaform:sync:${state.formKey}:${hashStableString(state.schema.fingerprint())}`;
|
|
5861
|
+
} catch {
|
|
5862
|
+
channelName = null;
|
|
5863
|
+
}
|
|
5864
|
+
if (channelName !== null) {
|
|
5865
|
+
const syncModule = createMultiTabSyncModule(state, channelName, {
|
|
5866
|
+
isSensitivePath: state.isSensitivePath,
|
|
5867
|
+
noSyncPaths: state.noSyncPaths,
|
|
5868
|
+
validateForm: (form) => {
|
|
5869
|
+
const result = state.schema.validateAtPath(form, void 0, { sync: true });
|
|
5870
|
+
if (result instanceof Promise) return;
|
|
5871
|
+
if (!result.success) {
|
|
5872
|
+
throw new Error("attaform multi-tab sync: post-apply schema validation failed");
|
|
5873
|
+
}
|
|
5874
|
+
}
|
|
5875
|
+
});
|
|
5876
|
+
state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
|
|
5877
|
+
state.registerCleanup(() => syncModule.dispose());
|
|
5878
|
+
}
|
|
5879
|
+
} else if (hasBroadcastChannel && !secureContext) {
|
|
5880
|
+
warnOnceInsecureContext("multiTab");
|
|
5611
5881
|
}
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5882
|
+
}
|
|
5883
|
+
if (existing === void 0 && merged.history !== void 0) {
|
|
5884
|
+
const historyModule = createHistoryModule(state, merged.history);
|
|
5885
|
+
state.modules.set(HISTORY_MODULE_KEY, historyModule);
|
|
5886
|
+
state.registerCleanup(() => historyModule.dispose());
|
|
5887
|
+
}
|
|
5888
|
+
if (configuration.key === void 0) {
|
|
5889
|
+
recordAmbientProvide(registry.ssr);
|
|
5890
|
+
provide(kFormContext, state);
|
|
5891
|
+
}
|
|
5892
|
+
const formInstanceId = getCurrentInstance() !== null ? useId() : `atta:form-instance:${formInstanceCounter++}`;
|
|
5893
|
+
if (getCurrentInstance() !== null) {
|
|
5894
|
+
provide(kFormInstanceId, formInstanceId);
|
|
5895
|
+
}
|
|
5896
|
+
const apiOptions = {};
|
|
5897
|
+
if (merged.onInvalidSubmit !== void 0) {
|
|
5898
|
+
apiOptions.onInvalidSubmit = merged.onInvalidSubmit;
|
|
5899
|
+
}
|
|
5900
|
+
const history = state.modules.get(HISTORY_MODULE_KEY);
|
|
5901
|
+
if (history !== void 0) {
|
|
5902
|
+
apiOptions.history = history;
|
|
5903
|
+
}
|
|
5904
|
+
if (merged.validateOn !== void 0) {
|
|
5905
|
+
apiOptions.validateOn = merged.validateOn;
|
|
5906
|
+
}
|
|
5907
|
+
const mergedDebounceMs = merged.debounceMs;
|
|
5908
|
+
if (mergedDebounceMs !== void 0) {
|
|
5909
|
+
apiOptions.debounceMs = mergedDebounceMs;
|
|
5910
|
+
}
|
|
5911
|
+
if (merged.getDisplayState !== void 0) {
|
|
5912
|
+
apiOptions.getDisplayState = merged.getDisplayState;
|
|
5913
|
+
}
|
|
5914
|
+
if (merged.coerce !== void 0) {
|
|
5915
|
+
apiOptions.coerce = merged.coerce;
|
|
5916
|
+
}
|
|
5917
|
+
if (merged.rememberVariants !== void 0) {
|
|
5918
|
+
apiOptions.rememberVariants = merged.rememberVariants;
|
|
5919
|
+
}
|
|
5920
|
+
if (merged.autoAria !== void 0) {
|
|
5921
|
+
apiOptions.autoAria = merged.autoAria;
|
|
5922
|
+
}
|
|
5923
|
+
return buildFormApi(
|
|
5924
|
+
state,
|
|
5925
|
+
formInstanceId,
|
|
5926
|
+
apiOptions
|
|
5927
|
+
);
|
|
5928
|
+
}
|
|
5929
|
+
function mergeWithDefaults(defaults, configuration) {
|
|
5930
|
+
const strict = configuration.strict ?? defaults.strict;
|
|
5931
|
+
const onInvalidSubmit = configuration.onInvalidSubmit ?? defaults.onInvalidSubmit;
|
|
5932
|
+
const history = configuration.history ?? defaults.history;
|
|
5933
|
+
const rememberVariants = configuration.rememberVariants ?? defaults.rememberVariants;
|
|
5934
|
+
const coerce = configuration.coerce ?? defaults.coerce;
|
|
5935
|
+
const validateOn = configuration.validateOn ?? defaults.validateOn;
|
|
5936
|
+
const debounceMs = configuration.debounceMs ?? defaults.debounceMs;
|
|
5937
|
+
const getDisplayState = configuration.getDisplayState ?? defaults.getDisplayState;
|
|
5938
|
+
const maxRecursionDepth = configuration.maxRecursionDepth ?? defaults.maxRecursionDepth;
|
|
5939
|
+
const sensitiveNames = configuration.sensitiveNames ?? defaults.sensitiveNames;
|
|
5940
|
+
const multiTab = configuration.multiTab ?? defaults.multiTab;
|
|
5941
|
+
const autoAria = configuration.autoAria ?? defaults.autoAria;
|
|
5942
|
+
return {
|
|
5943
|
+
...configuration,
|
|
5944
|
+
...strict === void 0 ? {} : { strict },
|
|
5945
|
+
...onInvalidSubmit === void 0 ? {} : { onInvalidSubmit },
|
|
5946
|
+
...history === void 0 ? {} : { history },
|
|
5947
|
+
...rememberVariants === void 0 ? {} : { rememberVariants },
|
|
5948
|
+
...coerce === void 0 ? {} : { coerce },
|
|
5949
|
+
...validateOn === void 0 ? {} : { validateOn },
|
|
5950
|
+
...debounceMs === void 0 ? {} : { debounceMs },
|
|
5951
|
+
...getDisplayState === void 0 ? {} : { getDisplayState },
|
|
5952
|
+
...maxRecursionDepth === void 0 ? {} : { maxRecursionDepth },
|
|
5953
|
+
...sensitiveNames === void 0 ? {} : { sensitiveNames },
|
|
5954
|
+
...multiTab === void 0 ? {} : { multiTab },
|
|
5955
|
+
...autoAria === void 0 ? {} : { autoAria }
|
|
5956
|
+
};
|
|
5957
|
+
}
|
|
5958
|
+
const HISTORY_MODULE_KEY = "history";
|
|
5959
|
+
function buildFreshState(key, schema, configuration, registry) {
|
|
5960
|
+
const pending = registry.pendingHydration.get(key);
|
|
5961
|
+
if (pending !== void 0) registry.pendingHydration.delete(key);
|
|
5962
|
+
const walked = walkUnsetSentinels(
|
|
5963
|
+
configuration.defaultValues,
|
|
5964
|
+
schema
|
|
5965
|
+
);
|
|
5966
|
+
let initialBlankPaths;
|
|
5967
|
+
if (pending === void 0) {
|
|
5968
|
+
initialBlankPaths = walked.paths;
|
|
5969
|
+
}
|
|
5970
|
+
const resolvedSensitiveNames = configuration.sensitiveNames;
|
|
5971
|
+
const resolvedIsSensitivePath = resolvedSensitiveNames === void 0 ? void 0 : createIsSensitivePath(resolvedSensitiveNames);
|
|
5972
|
+
const createOptions = {
|
|
5973
|
+
formKey: key,
|
|
5974
|
+
schema,
|
|
5975
|
+
defaultValues: walked.cleanedValues,
|
|
5976
|
+
...configuration.strict !== void 0 ? { strict: configuration.strict } : {},
|
|
5977
|
+
hydration: pending,
|
|
5978
|
+
...configuration.validateOn !== void 0 ? { validateOn: configuration.validateOn } : {},
|
|
5979
|
+
...configuration.debounceMs !== void 0 ? { debounceMs: configuration.debounceMs } : {},
|
|
5980
|
+
ssr: registry.ssr,
|
|
5981
|
+
// Server-only: bind the SSR prefetch coordination handles. `enqueue`
|
|
5982
|
+
// records intent on every `state.activate()` so a wizard skip-list
|
|
5983
|
+
// override or a future transform mark has a consistent set to diff
|
|
5984
|
+
// against; `shouldFire` lets the activate path bail when the
|
|
5985
|
+
// wizard explicitly skipped this key — even an explicit
|
|
5986
|
+
// `form.activate()` defers to the wizard's render-efficiency
|
|
5987
|
+
// skip-list on the server.
|
|
5988
|
+
...registry.ssr ? {
|
|
5989
|
+
ssrPrefetch: {
|
|
5990
|
+
enqueue: () => {
|
|
5991
|
+
registry.enqueuePrefetch(key);
|
|
5992
|
+
},
|
|
5993
|
+
shouldFire: () => registry.shouldPrefetch(key)
|
|
5994
|
+
}
|
|
5995
|
+
} : {},
|
|
5996
|
+
...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
|
|
5997
|
+
...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
|
|
5998
|
+
...configuration.getDisplayState !== void 0 ? { getDisplayState: configuration.getDisplayState } : {},
|
|
5999
|
+
...initialBlankPaths !== void 0 ? { initialBlankPaths } : {},
|
|
6000
|
+
...resolvedIsSensitivePath !== void 0 ? { isSensitivePath: resolvedIsSensitivePath } : {}
|
|
6001
|
+
};
|
|
6002
|
+
const state = createFormStore(createOptions);
|
|
6003
|
+
registry.forms.set(
|
|
6004
|
+
key,
|
|
6005
|
+
state
|
|
6006
|
+
);
|
|
6007
|
+
return state;
|
|
6008
|
+
}
|
|
6009
|
+
let anonCounter = 0;
|
|
6010
|
+
let formInstanceCounter = 0;
|
|
6011
|
+
const ambientProvideHistory = __DEV__ ? /* @__PURE__ */ new WeakMap() : null;
|
|
6012
|
+
function recordAmbientProvide(ssr) {
|
|
6013
|
+
if (!__DEV__ || ssr || ambientProvideHistory === null) return;
|
|
6014
|
+
const instance = getCurrentInstance();
|
|
6015
|
+
if (instance === null) return;
|
|
6016
|
+
const instanceKey = instance;
|
|
6017
|
+
const entry = {
|
|
6018
|
+
source: captureUserCallSite()
|
|
6019
|
+
};
|
|
6020
|
+
const existing = ambientProvideHistory.get(instanceKey);
|
|
6021
|
+
if (existing === void 0) {
|
|
6022
|
+
ambientProvideHistory.set(instanceKey, [entry]);
|
|
6023
|
+
return;
|
|
6024
|
+
}
|
|
6025
|
+
existing.push(entry);
|
|
6026
|
+
}
|
|
6027
|
+
function resolveFormKey(key) {
|
|
6028
|
+
if (key !== void 0 && key !== null && key !== "") {
|
|
6029
|
+
if (key.startsWith(RESERVED_KEY_PREFIX)) {
|
|
6030
|
+
throw new ReservedFormKeyError(key);
|
|
5624
6031
|
}
|
|
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
|
-
);
|
|
6032
|
+
return key;
|
|
5633
6033
|
}
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
if (disposed) return Promise.resolve();
|
|
5637
|
-
return writer.flush().catch(() => void 0);
|
|
6034
|
+
if (getCurrentInstance() !== null) {
|
|
6035
|
+
return `${ANONYMOUS_FORM_KEY_PREFIX}${useId()}`;
|
|
5638
6036
|
}
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
6037
|
+
return `${ANONYMOUS_FORM_KEY_PREFIX}${anonCounter++}`;
|
|
6038
|
+
}
|
|
6039
|
+
function warnOnSchemaFingerprintMismatch(key, existing, incoming) {
|
|
6040
|
+
let existingFp;
|
|
6041
|
+
let incomingFp;
|
|
6042
|
+
try {
|
|
6043
|
+
existingFp = existing.fingerprint();
|
|
6044
|
+
incomingFp = incoming.fingerprint();
|
|
6045
|
+
} catch (error) {
|
|
6046
|
+
console.error(
|
|
6047
|
+
`[attaform] fingerprint() threw for key "${key}"; skipping mismatch check.`,
|
|
6048
|
+
error
|
|
6049
|
+
);
|
|
6050
|
+
return;
|
|
5647
6051
|
}
|
|
5648
|
-
return
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
};
|
|
6052
|
+
if (existingFp === incomingFp) return;
|
|
6053
|
+
console.warn(
|
|
6054
|
+
`[attaform] useForm() calls with key "${key}" use different schemas; first wins, second is ignored. Use identical schemas or unique keys.
|
|
6055
|
+
existing: ${existingFp}
|
|
6056
|
+
incoming: ${incomingFp}`
|
|
6057
|
+
);
|
|
5655
6058
|
}
|
|
5656
|
-
function
|
|
5657
|
-
if (
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
6059
|
+
function warnOnPersistDivergence(key, existing, incomingPersist) {
|
|
6060
|
+
if (incomingPersist === void 0) return;
|
|
6061
|
+
const wired = existing.modules.get(PERSISTENCE_MODULE_KEY);
|
|
6062
|
+
const incomingNormalized = normalizePersistConfig(incomingPersist);
|
|
6063
|
+
if (wired === void 0) {
|
|
6064
|
+
console.warn(
|
|
6065
|
+
`[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.`
|
|
6066
|
+
);
|
|
6067
|
+
return;
|
|
6068
|
+
}
|
|
6069
|
+
if (persistConfigsEquivalent(wired.wiredConfig, incomingNormalized)) return;
|
|
6070
|
+
console.warn(
|
|
6071
|
+
`[attaform] useForm({ key: "${key}" }) passed a persist config that differs from the first useForm({ key }) call's; first wins, this one is ignored.
|
|
6072
|
+
wired: ${describePersist(wired.wiredConfig)}
|
|
6073
|
+
incoming: ${describePersist(incomingNormalized)}`
|
|
6074
|
+
);
|
|
6075
|
+
}
|
|
6076
|
+
function persistConfigsEquivalent(a, b) {
|
|
6077
|
+
if (a.storage !== b.storage) return false;
|
|
6078
|
+
if ((a.key ?? void 0) !== (b.key ?? void 0)) return false;
|
|
6079
|
+
if ((a.debounceMs ?? void 0) !== (b.debounceMs ?? void 0)) return false;
|
|
6080
|
+
return true;
|
|
6081
|
+
}
|
|
6082
|
+
function describePersist(config) {
|
|
6083
|
+
const storage = typeof config.storage === "string" ? config.storage : "custom-adapter";
|
|
6084
|
+
const parts = [`storage=${storage}`];
|
|
6085
|
+
if (config.key !== void 0) parts.push(`key=${config.key}`);
|
|
6086
|
+
if (config.debounceMs !== void 0) parts.push(`debounceMs=${config.debounceMs}`);
|
|
6087
|
+
return `{ ${parts.join(", ")} }`;
|
|
5661
6088
|
}
|
|
5662
6089
|
const warnedAnonPersistKeys = /* @__PURE__ */ new Set();
|
|
5663
6090
|
function enforceAnonPersistRule(formKey, ssr) {
|
|
@@ -5675,33 +6102,6 @@ function enforceAnonPersistRule(formKey, ssr) {
|
|
|
5675
6102
|
}
|
|
5676
6103
|
return true;
|
|
5677
6104
|
}
|
|
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
6105
|
|
|
5706
6106
|
let injectedInstanceCounter = 0;
|
|
5707
6107
|
function injectForm(input) {
|
|
@@ -5781,8 +6181,6 @@ function isLazyMarker(value) {
|
|
|
5781
6181
|
}
|
|
5782
6182
|
|
|
5783
6183
|
const NOOP_WIZARD_HISTORY = {
|
|
5784
|
-
push() {
|
|
5785
|
-
},
|
|
5786
6184
|
replace() {
|
|
5787
6185
|
},
|
|
5788
6186
|
read() {
|
|
@@ -5809,21 +6207,16 @@ function createWizardHistory(param) {
|
|
|
5809
6207
|
for (const subscriber of subscribers) subscriber(value);
|
|
5810
6208
|
}
|
|
5811
6209
|
window.addEventListener("popstate", handlePopstate);
|
|
5812
|
-
function
|
|
6210
|
+
function safeReplaceState(key) {
|
|
5813
6211
|
try {
|
|
5814
|
-
|
|
5815
|
-
fn.call(window.history, {}, "", buildUrl(key));
|
|
6212
|
+
window.history.replaceState({}, "", buildUrl(key));
|
|
5816
6213
|
} catch {
|
|
5817
6214
|
}
|
|
5818
6215
|
}
|
|
5819
6216
|
return {
|
|
5820
|
-
push(key) {
|
|
5821
|
-
if (disposed) return;
|
|
5822
|
-
safeWriteState(key, "push");
|
|
5823
|
-
},
|
|
5824
6217
|
replace(key) {
|
|
5825
6218
|
if (disposed) return;
|
|
5826
|
-
|
|
6219
|
+
safeReplaceState(key);
|
|
5827
6220
|
},
|
|
5828
6221
|
read() {
|
|
5829
6222
|
const url = new URL(window.location.href);
|
|
@@ -5882,68 +6275,16 @@ function buildWizardStatusesProxy(statuses) {
|
|
|
5882
6275
|
}
|
|
5883
6276
|
return result;
|
|
5884
6277
|
});
|
|
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
|
|
6278
|
+
return buildCallableReadonlySnapshotProxy({
|
|
6279
|
+
surface: "wizard.statuses",
|
|
6280
|
+
snapshot: () => snapshot.value,
|
|
6281
|
+
resolveKey: (key) => statuses[key]?.value,
|
|
6282
|
+
// Single-key callable form. Strings stringify naturally; non-
|
|
6283
|
+
// string args coerce via `String(arg)` and miss the lookup, which
|
|
6284
|
+
// resolves to `undefined` (consistent with property-access).
|
|
6285
|
+
resolveCall: (arg) => statuses[String(arg)]?.value,
|
|
6286
|
+
ownKeys: () => Object.keys(statuses),
|
|
6287
|
+
hasKey: (key) => Object.hasOwn(statuses, key)
|
|
5947
6288
|
});
|
|
5948
6289
|
}
|
|
5949
6290
|
|
|
@@ -5960,6 +6301,12 @@ const NOOP_VALID_STATUS = {
|
|
|
5960
6301
|
submitted: false,
|
|
5961
6302
|
errorCount: 0
|
|
5962
6303
|
};
|
|
6304
|
+
function asStatusSource(form) {
|
|
6305
|
+
return form;
|
|
6306
|
+
}
|
|
6307
|
+
function asSubmissionSource(form) {
|
|
6308
|
+
return form;
|
|
6309
|
+
}
|
|
5963
6310
|
function useWizard(options) {
|
|
5964
6311
|
const rawSteps = Array.isArray(options.steps) ? options.steps : [];
|
|
5965
6312
|
if (rawSteps.length === 0 && __DEV__) {
|
|
@@ -6047,10 +6394,12 @@ function useWizard(options) {
|
|
|
6047
6394
|
return { configurable: true, enumerable: true, writable: false, value: form };
|
|
6048
6395
|
}
|
|
6049
6396
|
});
|
|
6050
|
-
const slotCtx =
|
|
6397
|
+
const slotCtx = {
|
|
6051
6398
|
forms: slotForms,
|
|
6052
|
-
currentKey
|
|
6053
|
-
|
|
6399
|
+
get currentKey() {
|
|
6400
|
+
return activeKey.value === "" ? void 0 : activeKey.value;
|
|
6401
|
+
}
|
|
6402
|
+
};
|
|
6054
6403
|
function resolveSlot(slot, index, ctx) {
|
|
6055
6404
|
if (typeof slot === "string") {
|
|
6056
6405
|
return getOrBuildNoop(slot);
|
|
@@ -6073,12 +6422,6 @@ function useWizard(options) {
|
|
|
6073
6422
|
}
|
|
6074
6423
|
return result;
|
|
6075
6424
|
}
|
|
6076
|
-
const lazyCtx = {
|
|
6077
|
-
forms: slotForms,
|
|
6078
|
-
get currentKey() {
|
|
6079
|
-
return activeKey.value === "" ? void 0 : activeKey.value;
|
|
6080
|
-
}
|
|
6081
|
-
};
|
|
6082
6425
|
for (let i = 0; i < rawSteps.length; i++) {
|
|
6083
6426
|
const slot = rawSteps[i];
|
|
6084
6427
|
if (isLazyMarker(slot)) {
|
|
@@ -6088,18 +6431,17 @@ function useWizard(options) {
|
|
|
6088
6431
|
idx,
|
|
6089
6432
|
computed(() => {
|
|
6090
6433
|
void lazyEpoch.value;
|
|
6091
|
-
return marker.resolve(
|
|
6434
|
+
return marker.resolve(slotCtx);
|
|
6092
6435
|
})
|
|
6093
6436
|
);
|
|
6094
6437
|
}
|
|
6095
6438
|
}
|
|
6096
6439
|
const compiledSteps = computed(() => {
|
|
6097
|
-
const ctx = slotCtx.value;
|
|
6098
6440
|
const out = [];
|
|
6099
6441
|
const seen = /* @__PURE__ */ new Set();
|
|
6100
6442
|
for (let i = 0; i < rawSteps.length; i++) {
|
|
6101
6443
|
const slot = rawSteps[i];
|
|
6102
|
-
const form = resolveSlot(slot, i,
|
|
6444
|
+
const form = resolveSlot(slot, i, slotCtx);
|
|
6103
6445
|
if (form === void 0) continue;
|
|
6104
6446
|
if (seen.has(form.key)) {
|
|
6105
6447
|
if (__DEV__) {
|
|
@@ -6125,9 +6467,12 @@ function useWizard(options) {
|
|
|
6125
6467
|
return -1;
|
|
6126
6468
|
});
|
|
6127
6469
|
const currentStep = computed(() => {
|
|
6128
|
-
const
|
|
6129
|
-
|
|
6130
|
-
|
|
6470
|
+
const list = compiledSteps.value;
|
|
6471
|
+
const idx = activeIndex.value;
|
|
6472
|
+
if (idx >= 0 && idx < list.length) {
|
|
6473
|
+
return list[idx].key;
|
|
6474
|
+
}
|
|
6475
|
+
const first = list[0];
|
|
6131
6476
|
return first === void 0 ? void 0 : first.key;
|
|
6132
6477
|
});
|
|
6133
6478
|
const activeForm = computed(() => {
|
|
@@ -6150,36 +6495,94 @@ function useWizard(options) {
|
|
|
6150
6495
|
for (const step of compiledSteps.value) out[step.key] = step.form;
|
|
6151
6496
|
return out;
|
|
6152
6497
|
});
|
|
6153
|
-
|
|
6154
|
-
const
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6498
|
+
function isFormReady(key) {
|
|
6499
|
+
const store = registry.forms.get(key);
|
|
6500
|
+
return store?.defaultsResolved.value === true;
|
|
6501
|
+
}
|
|
6502
|
+
function toWizardAggregateError(err, fallbackKey) {
|
|
6503
|
+
const entry = {
|
|
6504
|
+
formKey: err.formKey ?? fallbackKey,
|
|
6505
|
+
path: err.path,
|
|
6506
|
+
message: err.message
|
|
6507
|
+
};
|
|
6508
|
+
if (err.code !== void 0) entry.code = err.code;
|
|
6509
|
+
return entry;
|
|
6510
|
+
}
|
|
6511
|
+
const valuesCache = /* @__PURE__ */ new Map();
|
|
6512
|
+
function valuesFor(form) {
|
|
6513
|
+
const cached = valuesCache.get(form.key);
|
|
6514
|
+
if (cached !== void 0) return cached;
|
|
6515
|
+
const source = asStatusSource(form);
|
|
6516
|
+
const computedValues = computed(() => source.values);
|
|
6517
|
+
valuesCache.set(form.key, computedValues);
|
|
6518
|
+
return computedValues;
|
|
6519
|
+
}
|
|
6520
|
+
const errorsCache = /* @__PURE__ */ new Map();
|
|
6521
|
+
function errorsFor(form) {
|
|
6522
|
+
const cached = errorsCache.get(form.key);
|
|
6523
|
+
if (cached !== void 0) return cached;
|
|
6524
|
+
const source = asStatusSource(form);
|
|
6525
|
+
const computedErrors = computed(() => {
|
|
6526
|
+
if (!isFormReady(form.key)) return [];
|
|
6527
|
+
const errors = source.meta?.errors ?? [];
|
|
6528
|
+
const list = [];
|
|
6529
|
+
for (const err of errors) list.push(toWizardAggregateError(err, form.key));
|
|
6530
|
+
return list;
|
|
6531
|
+
});
|
|
6532
|
+
errorsCache.set(form.key, computedErrors);
|
|
6533
|
+
return computedErrors;
|
|
6534
|
+
}
|
|
6535
|
+
const allValues = new Proxy({}, {
|
|
6536
|
+
get(_, key) {
|
|
6537
|
+
if (typeof key !== "string") return void 0;
|
|
6538
|
+
const form = formsRecord.value[key];
|
|
6539
|
+
if (form === void 0) return void 0;
|
|
6540
|
+
return valuesFor(form).value;
|
|
6541
|
+
},
|
|
6542
|
+
has(_, key) {
|
|
6543
|
+
if (typeof key !== "string") return false;
|
|
6544
|
+
return formsRecord.value[key] !== void 0;
|
|
6545
|
+
},
|
|
6546
|
+
ownKeys() {
|
|
6547
|
+
return Object.keys(formsRecord.value);
|
|
6548
|
+
},
|
|
6549
|
+
getOwnPropertyDescriptor(_, key) {
|
|
6550
|
+
if (typeof key !== "string") return void 0;
|
|
6551
|
+
const form = formsRecord.value[key];
|
|
6552
|
+
if (form === void 0) return void 0;
|
|
6553
|
+
return {
|
|
6554
|
+
configurable: true,
|
|
6555
|
+
enumerable: true,
|
|
6556
|
+
writable: false,
|
|
6557
|
+
value: valuesFor(form).value
|
|
6558
|
+
};
|
|
6158
6559
|
}
|
|
6159
|
-
return out;
|
|
6160
6560
|
});
|
|
6161
|
-
const allErrors =
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
const
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6561
|
+
const allErrors = new Proxy({}, {
|
|
6562
|
+
get(_, key) {
|
|
6563
|
+
if (typeof key !== "string") return void 0;
|
|
6564
|
+
const form = formsRecord.value[key];
|
|
6565
|
+
if (form === void 0) return void 0;
|
|
6566
|
+
return errorsFor(form).value;
|
|
6567
|
+
},
|
|
6568
|
+
has(_, key) {
|
|
6569
|
+
if (typeof key !== "string") return false;
|
|
6570
|
+
return formsRecord.value[key] !== void 0;
|
|
6571
|
+
},
|
|
6572
|
+
ownKeys() {
|
|
6573
|
+
return Object.keys(formsRecord.value);
|
|
6574
|
+
},
|
|
6575
|
+
getOwnPropertyDescriptor(_, key) {
|
|
6576
|
+
if (typeof key !== "string") return void 0;
|
|
6577
|
+
const form = formsRecord.value[key];
|
|
6578
|
+
if (form === void 0) return void 0;
|
|
6579
|
+
return {
|
|
6580
|
+
configurable: true,
|
|
6581
|
+
enumerable: true,
|
|
6582
|
+
writable: false,
|
|
6583
|
+
value: errorsFor(form).value
|
|
6584
|
+
};
|
|
6181
6585
|
}
|
|
6182
|
-
return out;
|
|
6183
6586
|
});
|
|
6184
6587
|
const seedRef = ref(void 0);
|
|
6185
6588
|
const seedInput = options.defaultStatuses;
|
|
@@ -6202,11 +6605,9 @@ function useWizard(options) {
|
|
|
6202
6605
|
function statusFor(form) {
|
|
6203
6606
|
const cached = statusCache.get(form.key);
|
|
6204
6607
|
if (cached !== void 0) return cached;
|
|
6205
|
-
const source = form;
|
|
6608
|
+
const source = asStatusSource(form);
|
|
6206
6609
|
const computedStatus = computed(() => {
|
|
6207
|
-
|
|
6208
|
-
const resolved = store?.defaultsResolved.value === true;
|
|
6209
|
-
if (resolved) {
|
|
6610
|
+
if (isFormReady(form.key)) {
|
|
6210
6611
|
const meta = source.meta;
|
|
6211
6612
|
if (meta !== void 0 && meta !== null) {
|
|
6212
6613
|
return {
|
|
@@ -6349,7 +6750,7 @@ function useWizard(options) {
|
|
|
6349
6750
|
}
|
|
6350
6751
|
if (!registry.ssr) {
|
|
6351
6752
|
for (const step of compiledSteps.value) {
|
|
6352
|
-
const source = step.form;
|
|
6753
|
+
const source = asSubmissionSource(step.form);
|
|
6353
6754
|
if (typeof source.activate === "function") void source.activate();
|
|
6354
6755
|
}
|
|
6355
6756
|
}
|
|
@@ -6393,7 +6794,7 @@ function useWizard(options) {
|
|
|
6393
6794
|
const submissionAttempts = ref(0);
|
|
6394
6795
|
const done = ref(false);
|
|
6395
6796
|
function activateForm(form) {
|
|
6396
|
-
const source = form;
|
|
6797
|
+
const source = asSubmissionSource(form);
|
|
6397
6798
|
if (typeof source.activate === "function") {
|
|
6398
6799
|
void source.activate();
|
|
6399
6800
|
}
|
|
@@ -6493,7 +6894,7 @@ function useWizard(options) {
|
|
|
6493
6894
|
};
|
|
6494
6895
|
}
|
|
6495
6896
|
async function processOne(form) {
|
|
6496
|
-
const full = form;
|
|
6897
|
+
const full = asSubmissionSource(form);
|
|
6497
6898
|
let activationFailure;
|
|
6498
6899
|
try {
|
|
6499
6900
|
if (typeof full.activate === "function") await full.activate();
|
|
@@ -6525,15 +6926,7 @@ function useWizard(options) {
|
|
|
6525
6926
|
for (const step of compiledSteps.value) {
|
|
6526
6927
|
const processed = results.get(step.key);
|
|
6527
6928
|
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
|
-
}
|
|
6929
|
+
for (const err of processed.errors) out.push(toWizardAggregateError(err, step.key));
|
|
6537
6930
|
}
|
|
6538
6931
|
return out;
|
|
6539
6932
|
}
|
|
@@ -6587,8 +6980,7 @@ function useWizard(options) {
|
|
|
6587
6980
|
if (processed !== void 0 && processed.success === true) {
|
|
6588
6981
|
valuesMap[step.key] = processed.data;
|
|
6589
6982
|
} else {
|
|
6590
|
-
|
|
6591
|
-
valuesMap[step.key] = source.values;
|
|
6983
|
+
valuesMap[step.key] = asStatusSource(step.form).values;
|
|
6592
6984
|
}
|
|
6593
6985
|
}
|
|
6594
6986
|
const ctx = buildSubmitContext(valuesMap, currentKey, final);
|
|
@@ -6609,8 +7001,11 @@ function useWizard(options) {
|
|
|
6609
7001
|
moveTo(firstFailedKey);
|
|
6610
7002
|
await nextTick();
|
|
6611
7003
|
const failedForm = formsRecord.value[firstFailedKey];
|
|
6612
|
-
if (failedForm !== void 0
|
|
6613
|
-
failedForm
|
|
7004
|
+
if (failedForm !== void 0) {
|
|
7005
|
+
const failedSource = asSubmissionSource(failedForm);
|
|
7006
|
+
if (typeof failedSource.applyInvalidSubmitPolicy === "function") {
|
|
7007
|
+
failedSource.applyInvalidSubmitPolicy();
|
|
7008
|
+
}
|
|
6614
7009
|
}
|
|
6615
7010
|
}
|
|
6616
7011
|
}
|
|
@@ -6625,7 +7020,7 @@ function useWizard(options) {
|
|
|
6625
7020
|
done.value = false;
|
|
6626
7021
|
lazyEpoch.value += 1;
|
|
6627
7022
|
for (const step of compiledSteps.value) {
|
|
6628
|
-
const full = step.form;
|
|
7023
|
+
const full = asSubmissionSource(step.form);
|
|
6629
7024
|
if (typeof full.reset === "function") full.reset();
|
|
6630
7025
|
}
|
|
6631
7026
|
const firstStep = compiledSteps.value[0];
|
|
@@ -6675,12 +7070,8 @@ function useWizard(options) {
|
|
|
6675
7070
|
return count.value;
|
|
6676
7071
|
},
|
|
6677
7072
|
statuses,
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
},
|
|
6681
|
-
get allErrors() {
|
|
6682
|
-
return allErrors.value;
|
|
6683
|
-
},
|
|
7073
|
+
allValues,
|
|
7074
|
+
allErrors,
|
|
6684
7075
|
get progress() {
|
|
6685
7076
|
return progress.value;
|
|
6686
7077
|
},
|
|
@@ -6811,5 +7202,5 @@ function warnIfAmbientWizardProviderHadDuplicates() {
|
|
|
6811
7202
|
}
|
|
6812
7203
|
}
|
|
6813
7204
|
|
|
6814
|
-
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,
|
|
6815
|
-
//# sourceMappingURL=attaform.
|
|
7205
|
+
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, safeOwnRead as j, setAtPath as k, lazy as l, slimKindOf as m, normalizeNumericOption as n, useAbstractForm as o, useWizard as p, safeAssign as s, unset as u };
|
|
7206
|
+
//# sourceMappingURL=attaform.BqZuwLTK.mjs.map
|