attaform 0.20.2 → 0.21.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/dist/chunks/dev-key-collision-warnings.cjs +58 -0
- package/dist/chunks/dev-key-collision-warnings.cjs.map +1 -0
- package/dist/chunks/dev-key-collision-warnings.mjs +55 -0
- package/dist/chunks/dev-key-collision-warnings.mjs.map +1 -0
- package/dist/chunks/devtools.cjs +1 -1
- package/dist/chunks/devtools.mjs +1 -1
- package/dist/chunks/fingerprint.cjs +186 -0
- package/dist/chunks/fingerprint.cjs.map +1 -0
- package/dist/chunks/fingerprint.mjs +184 -0
- package/dist/chunks/fingerprint.mjs.map +1 -0
- package/dist/chunks/fingerprint2.cjs +162 -0
- package/dist/chunks/fingerprint2.cjs.map +1 -0
- package/dist/chunks/fingerprint2.mjs +160 -0
- package/dist/chunks/fingerprint2.mjs.map +1 -0
- 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/multi-tab-sync.cjs +367 -0
- package/dist/chunks/multi-tab-sync.cjs.map +1 -0
- package/dist/chunks/multi-tab-sync.mjs +364 -0
- package/dist/chunks/multi-tab-sync.mjs.map +1 -0
- package/dist/chunks/session-storage.cjs +1 -1
- package/dist/chunks/session-storage.mjs +1 -1
- package/dist/chunks/wire-persistence.cjs +396 -0
- package/dist/chunks/wire-persistence.cjs.map +1 -0
- package/dist/chunks/wire-persistence.mjs +394 -0
- package/dist/chunks/wire-persistence.mjs.map +1 -0
- package/dist/esbuild.cjs +28 -0
- package/dist/esbuild.cjs.map +1 -0
- package/dist/esbuild.d.cts +56 -0
- package/dist/esbuild.d.mts +56 -0
- package/dist/esbuild.d.ts +56 -0
- package/dist/esbuild.mjs +26 -0
- package/dist/esbuild.mjs.map +1 -0
- package/dist/index.cjs +5 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -70
- package/dist/index.d.mts +66 -70
- package/dist/index.d.ts +66 -70
- 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/rollup.cjs +24 -0
- package/dist/rollup.cjs.map +1 -0
- package/dist/rollup.d.cts +35 -0
- package/dist/rollup.d.mts +35 -0
- package/dist/rollup.d.ts +35 -0
- package/dist/rollup.mjs +22 -0
- package/dist/rollup.mjs.map +1 -0
- package/dist/rspack.cjs +10 -0
- package/dist/rspack.cjs.map +1 -0
- package/dist/rspack.d.cts +40 -0
- package/dist/rspack.d.mts +40 -0
- package/dist/rspack.d.ts +40 -0
- package/dist/rspack.mjs +8 -0
- package/dist/rspack.mjs.map +1 -0
- package/dist/runtime/plugins/attaform.cjs +2 -2
- package/dist/runtime/plugins/attaform.mjs +2 -2
- package/dist/shared/attaform.BJGA_UOS.mjs +37 -0
- package/dist/shared/attaform.BJGA_UOS.mjs.map +1 -0
- package/dist/shared/attaform.BRGIpZo4.cjs +26 -0
- package/dist/shared/attaform.BRGIpZo4.cjs.map +1 -0
- package/dist/shared/{attaform.DAKrGhxc.cjs → attaform.BSkvn43g.cjs} +101 -417
- package/dist/shared/attaform.BSkvn43g.cjs.map +1 -0
- package/dist/shared/{attaform.sWm8B15V.d.mts → attaform.BWfliRIK.d.cts} +172 -2
- package/dist/shared/{attaform.BGk8cfw2.mjs → attaform.Be8NZG9M.mjs} +178 -21
- package/dist/shared/attaform.Be8NZG9M.mjs.map +1 -0
- package/dist/shared/{attaform.D2SCCd4O.cjs → attaform.Bq5sX7TF.cjs} +2 -2
- package/dist/shared/{attaform.D2SCCd4O.cjs.map → attaform.Bq5sX7TF.cjs.map} +1 -1
- package/dist/shared/{attaform.ceGEAEMk.d.ts → attaform.Bv7dRDWK.d.ts} +172 -2
- package/dist/shared/attaform.C3Doa9Pt.mjs +24 -0
- package/dist/shared/attaform.C3Doa9Pt.mjs.map +1 -0
- package/dist/shared/{attaform.B_hph5AE.cjs → attaform.CICFZ1iS.cjs} +178 -20
- package/dist/shared/attaform.CICFZ1iS.cjs.map +1 -0
- package/dist/shared/attaform.CQN9R62B.cjs +39 -0
- package/dist/shared/attaform.CQN9R62B.cjs.map +1 -0
- package/dist/shared/{attaform.CwLjUqmQ.cjs → attaform.ClXwitZj.cjs} +735 -960
- package/dist/shared/attaform.ClXwitZj.cjs.map +1 -0
- package/dist/shared/{attaform.99cfHcIt.d.cts → attaform.D0dWZsJt.d.cts} +349 -77
- package/dist/shared/{attaform.99cfHcIt.d.mts → attaform.D0dWZsJt.d.mts} +349 -77
- package/dist/shared/{attaform.99cfHcIt.d.ts → attaform.D0dWZsJt.d.ts} +349 -77
- package/dist/shared/{attaform.z5j3LwJz.cjs → attaform.D32WwKk6.cjs} +216 -35
- package/dist/shared/attaform.D32WwKk6.cjs.map +1 -0
- package/dist/shared/{attaform.C5aYC_T8.mjs → attaform.DMEP_ENr.mjs} +39 -392
- package/dist/shared/attaform.DMEP_ENr.mjs.map +1 -0
- package/dist/shared/{attaform.tiWEVznj.mjs → attaform.DR6RmxWZ.mjs} +725 -962
- package/dist/shared/attaform.DR6RmxWZ.mjs.map +1 -0
- package/dist/shared/{attaform.Dt7dEcHk.mjs → attaform.DozgVlCE.mjs} +89 -405
- package/dist/shared/attaform.DozgVlCE.mjs.map +1 -0
- package/dist/shared/{attaform.DN5CvZrg.d.ts → attaform.Duecg2NO.d.mts} +2 -2
- package/dist/shared/attaform.DuzQYscR.d.cts +41 -0
- package/dist/shared/attaform.DuzQYscR.d.mts +41 -0
- package/dist/shared/attaform.DuzQYscR.d.ts +41 -0
- package/dist/shared/{attaform.BXinSW2T.d.mts → attaform.FudOcHaa.d.cts} +2 -2
- package/dist/shared/attaform.LEWUFqUw.cjs +54 -0
- package/dist/shared/attaform.LEWUFqUw.cjs.map +1 -0
- package/dist/shared/{attaform.CywE4y8x.d.cts → attaform.MtrpT6Ki.d.ts} +2 -2
- package/dist/shared/{attaform.DbRgDFa7.d.cts → attaform.NQ8mybyW.d.mts} +172 -2
- package/dist/shared/{attaform.Cd4AOfwu.cjs → attaform.S-pYLSo4.cjs} +68 -402
- package/dist/shared/attaform.S-pYLSo4.cjs.map +1 -0
- package/dist/shared/{attaform.CnrxbkB6.mjs → attaform.Y1ZGhM4k.mjs} +2 -2
- package/dist/shared/{attaform.CnrxbkB6.mjs.map → attaform.Y1ZGhM4k.mjs.map} +1 -1
- package/dist/shared/{attaform.QG5TG8lB.mjs → attaform.pmtahXKy.mjs} +216 -36
- package/dist/shared/attaform.pmtahXKy.mjs.map +1 -0
- package/dist/shared/attaform.sHkHv_98.mjs +51 -0
- package/dist/shared/attaform.sHkHv_98.mjs.map +1 -0
- package/dist/vite.cjs +9 -45
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.cts +36 -0
- package/dist/vite.d.mts +36 -0
- package/dist/vite.d.ts +36 -0
- package/dist/vite.mjs +8 -44
- package/dist/vite.mjs.map +1 -1
- package/dist/webpack.cjs +10 -0
- package/dist/webpack.cjs.map +1 -0
- package/dist/webpack.d.cts +37 -0
- package/dist/webpack.d.mts +37 -0
- package/dist/webpack.d.ts +37 -0
- package/dist/webpack.mjs +8 -0
- package/dist/webpack.mjs.map +1 -0
- package/dist/zod-v3.cjs +3 -3
- package/dist/zod-v3.d.cts +3 -3
- package/dist/zod-v3.d.mts +3 -3
- package/dist/zod-v3.d.ts +3 -3
- package/dist/zod-v3.mjs +3 -3
- package/dist/zod-v4.cjs +3 -3
- package/dist/zod-v4.d.cts +4 -4
- package/dist/zod-v4.d.mts +4 -4
- package/dist/zod-v4.d.ts +4 -4
- 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 +52 -10
- package/dist/zod.d.mts +52 -10
- package/dist/zod.d.ts +52 -10
- package/dist/zod.mjs +6 -6
- package/dist/zod.mjs.map +1 -1
- package/package.json +19 -5
- package/dist/shared/attaform.BGk8cfw2.mjs.map +0 -1
- package/dist/shared/attaform.B_hph5AE.cjs.map +0 -1
- package/dist/shared/attaform.C5aYC_T8.mjs.map +0 -1
- package/dist/shared/attaform.Cd4AOfwu.cjs.map +0 -1
- package/dist/shared/attaform.CwLjUqmQ.cjs.map +0 -1
- package/dist/shared/attaform.DAKrGhxc.cjs.map +0 -1
- package/dist/shared/attaform.Dt7dEcHk.mjs.map +0 -1
- package/dist/shared/attaform.QG5TG8lB.mjs.map +0 -1
- package/dist/shared/attaform.tiWEVznj.mjs.map +0 -1
- package/dist/shared/attaform.z5j3LwJz.cjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const vue = require('vue');
|
|
4
|
-
const paths = require('./attaform.
|
|
4
|
+
const paths = require('./attaform.D32WwKk6.cjs');
|
|
5
5
|
|
|
6
6
|
function safeAssign(target, key, value) {
|
|
7
7
|
if (key === "__proto__") {
|
|
@@ -15,10 +15,14 @@ function safeAssign(target, key, value) {
|
|
|
15
15
|
}
|
|
16
16
|
target[key] = value;
|
|
17
17
|
}
|
|
18
|
+
function isShadowedKey(key) {
|
|
19
|
+
return key in Object.prototype;
|
|
20
|
+
}
|
|
18
21
|
function safeOwnRead(target, key) {
|
|
19
|
-
if (key
|
|
20
|
-
const desc = Object.getOwnPropertyDescriptor(target,
|
|
21
|
-
return
|
|
22
|
+
if (isShadowedKey(key)) {
|
|
23
|
+
const desc = Object.getOwnPropertyDescriptor(target, key);
|
|
24
|
+
if (desc === void 0) return void 0;
|
|
25
|
+
return "value" in desc ? desc.value : target[key];
|
|
22
26
|
}
|
|
23
27
|
return target[key];
|
|
24
28
|
}
|
|
@@ -37,6 +41,10 @@ function descendStep(value, segment) {
|
|
|
37
41
|
}
|
|
38
42
|
const record = value;
|
|
39
43
|
const key = typeof segment === "number" ? String(segment) : segment;
|
|
44
|
+
if (isShadowedKey(key)) {
|
|
45
|
+
if (!safeOwnHas(record, key)) return NOT_FOUND;
|
|
46
|
+
return safeOwnRead(record, key);
|
|
47
|
+
}
|
|
40
48
|
if (!(key in record)) return NOT_FOUND;
|
|
41
49
|
return record[key];
|
|
42
50
|
}
|
|
@@ -66,6 +74,7 @@ function hasAtPath(root, path) {
|
|
|
66
74
|
return typeof last === "number" && last >= 0 && last < current.length;
|
|
67
75
|
}
|
|
68
76
|
const key = typeof last === "number" ? String(last) : last;
|
|
77
|
+
if (isShadowedKey(key)) return safeOwnHas(current, key);
|
|
69
78
|
return key in current;
|
|
70
79
|
}
|
|
71
80
|
function isPlainRecord(value) {
|
|
@@ -460,17 +469,57 @@ function structuralSnapshot(value) {
|
|
|
460
469
|
return out;
|
|
461
470
|
}
|
|
462
471
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
472
|
+
function isGateOpen(field, formMeta) {
|
|
473
|
+
return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
|
|
474
|
+
}
|
|
475
|
+
function computeVerdict(field, formMeta) {
|
|
476
|
+
if (!isGateOpen(field, formMeta)) return "idle";
|
|
467
477
|
const hasOwnError = field.errors.some(
|
|
468
478
|
(e) => e.path.length === field.path.length && e.path.every((s, i) => s === field.path[i])
|
|
469
479
|
);
|
|
470
480
|
if (hasOwnError) return "error";
|
|
471
481
|
if (field.valid === true && field.blank !== true && field.dirty === true) return "success";
|
|
472
482
|
return "idle";
|
|
473
|
-
}
|
|
483
|
+
}
|
|
484
|
+
function earliestNonNull(a, b) {
|
|
485
|
+
if (a === null) return b;
|
|
486
|
+
if (b === null) return a;
|
|
487
|
+
return a < b ? a : b;
|
|
488
|
+
}
|
|
489
|
+
const DEFAULT_TIMINGS = { showDelay: 120, minVisible: 120 };
|
|
490
|
+
const FOCUS_OUT_GRACE = 16;
|
|
491
|
+
const defaultFamily = /* @__PURE__ */ new WeakSet();
|
|
492
|
+
function isDefaultDisplayState(fn) {
|
|
493
|
+
return defaultFamily.has(fn);
|
|
494
|
+
}
|
|
495
|
+
function makeDefaultDisplayState({
|
|
496
|
+
showDelay,
|
|
497
|
+
minVisible
|
|
498
|
+
}) {
|
|
499
|
+
const reducer = (prev, { field, formMeta, validatingSince, transformingSince, now }) => {
|
|
500
|
+
const verdict = computeVerdict(field, formMeta);
|
|
501
|
+
if (!isGateOpen(field, formMeta)) return { display: verdict };
|
|
502
|
+
const inFlightSince = earliestNonNull(validatingSince, transformingSince);
|
|
503
|
+
if (inFlightSince === null) {
|
|
504
|
+
if (prev.display === "pending") {
|
|
505
|
+
const shownAt = prev.pendingShownAt ?? now;
|
|
506
|
+
if (now < shownAt + minVisible)
|
|
507
|
+
return { display: "pending", pendingShownAt: shownAt, reviewAt: shownAt + minVisible };
|
|
508
|
+
}
|
|
509
|
+
return { display: verdict };
|
|
510
|
+
}
|
|
511
|
+
if (prev.display === "pending")
|
|
512
|
+
return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
|
|
513
|
+
const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
|
|
514
|
+
if (now - inFlightSince < window) {
|
|
515
|
+
return { display: prev.display, reviewAt: inFlightSince + window };
|
|
516
|
+
}
|
|
517
|
+
return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
|
|
518
|
+
};
|
|
519
|
+
defaultFamily.add(reducer);
|
|
520
|
+
return reducer;
|
|
521
|
+
}
|
|
522
|
+
const defaultDisplayState = makeDefaultDisplayState(DEFAULT_TIMINGS);
|
|
474
523
|
function resolveGetDisplayState(config) {
|
|
475
524
|
return config ?? defaultDisplayState;
|
|
476
525
|
}
|
|
@@ -589,6 +638,8 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
589
638
|
if (blankForKey !== void 0) errors.push(...blankForKey);
|
|
590
639
|
if (userForKey !== void 0) errors.push(...userForKey);
|
|
591
640
|
const validating = (state.fieldValidationCounts.get(key) ?? 0) > 0;
|
|
641
|
+
const transforming = (state.fieldTransformCounts.get(key) ?? 0) > 0;
|
|
642
|
+
const transformError = state.transformErrors.get(key) ?? null;
|
|
592
643
|
const gated = state.pathHasAsyncValidation(segments) && !state.firstValidationDone.value;
|
|
593
644
|
const isOrphan = segments.length > 0 && !hasAtPath(state.form.value, segments) && isUnderStubAncestor(state, segments);
|
|
594
645
|
const valid = !gated && errors.length === 0 && !validating && !isOrphan;
|
|
@@ -615,6 +666,9 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
615
666
|
errors,
|
|
616
667
|
validating,
|
|
617
668
|
valid,
|
|
669
|
+
transforming,
|
|
670
|
+
busy: transforming || validating,
|
|
671
|
+
transformError,
|
|
618
672
|
path: segments,
|
|
619
673
|
...computeFieldIdentity(formInstanceId, state.formKey, key),
|
|
620
674
|
key: state.arrayElementKey(segments),
|
|
@@ -627,7 +681,21 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
627
681
|
}
|
|
628
682
|
function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
|
|
629
683
|
const base = buildLeafFieldStateBase(state, segments, key, formInstanceId);
|
|
630
|
-
|
|
684
|
+
const validatingSince = state.fieldValidatingSince.get(key) ?? null;
|
|
685
|
+
const transformingSince = state.fieldTransformingSince.get(key) ?? null;
|
|
686
|
+
return decorateWithDerivedProps(
|
|
687
|
+
base,
|
|
688
|
+
state,
|
|
689
|
+
getFormMetaBase,
|
|
690
|
+
key,
|
|
691
|
+
validatingSince,
|
|
692
|
+
transformingSince,
|
|
693
|
+
false,
|
|
694
|
+
// revealedDescendantError: leaves have no descendants
|
|
695
|
+
false,
|
|
696
|
+
// isRoot: a leaf is never the form root
|
|
697
|
+
getDisplayState
|
|
698
|
+
);
|
|
631
699
|
}
|
|
632
700
|
function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
633
701
|
const formValue = state.form.value;
|
|
@@ -643,8 +711,13 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
643
711
|
let blurredAfterInteraction = false;
|
|
644
712
|
let connected = false;
|
|
645
713
|
let validating = false;
|
|
714
|
+
let validatingSince = null;
|
|
715
|
+
let transforming = false;
|
|
716
|
+
let transformingSince = null;
|
|
646
717
|
let updatedAt = null;
|
|
647
718
|
let asyncPending = false;
|
|
719
|
+
const submissionAttempts = state.submissionAttempts.value;
|
|
720
|
+
const blurredLeafSegments = [];
|
|
648
721
|
for (const [leafKey, entry] of state.originals) {
|
|
649
722
|
if (!paths.isPathPrefix(segments, entry.segments)) continue;
|
|
650
723
|
if (segments.length === entry.segments.length) continue;
|
|
@@ -659,9 +732,25 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
659
732
|
if (leafRecord?.blurred === true) blurred = true;
|
|
660
733
|
if (leafRecord?.touched === true) touched = true;
|
|
661
734
|
if (leafRecord?.interacted === true) interacted = true;
|
|
662
|
-
if (leafRecord?.blurredAfterInteraction === true)
|
|
735
|
+
if (leafRecord?.blurredAfterInteraction === true) {
|
|
736
|
+
blurredAfterInteraction = true;
|
|
737
|
+
blurredLeafSegments.push(entry.segments);
|
|
738
|
+
}
|
|
663
739
|
if (leafRecord?.connected === true) connected = true;
|
|
664
|
-
if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0)
|
|
740
|
+
if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0) {
|
|
741
|
+
validating = true;
|
|
742
|
+
const leafGateOpen = submissionAttempts > 0 || leafRecord?.blurredAfterInteraction === true;
|
|
743
|
+
const since = state.fieldValidatingSince.get(leafKey);
|
|
744
|
+
if (leafGateOpen && since !== void 0 && (validatingSince === null || since < validatingSince))
|
|
745
|
+
validatingSince = since;
|
|
746
|
+
}
|
|
747
|
+
if ((state.fieldTransformCounts.get(leafKey) ?? 0) > 0) {
|
|
748
|
+
transforming = true;
|
|
749
|
+
const leafGateOpen = submissionAttempts > 0 || leafRecord?.blurredAfterInteraction === true;
|
|
750
|
+
const since = state.fieldTransformingSince.get(leafKey);
|
|
751
|
+
if (leafGateOpen && since !== void 0 && (transformingSince === null || since < transformingSince))
|
|
752
|
+
transformingSince = since;
|
|
753
|
+
}
|
|
665
754
|
if (state.pathHasAsyncValidationByKey(leafKey, entry.segments)) asyncPending = true;
|
|
666
755
|
const ts = leafRecord?.updatedAt;
|
|
667
756
|
if (ts !== void 0 && ts !== null) {
|
|
@@ -673,50 +762,101 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
673
762
|
dirty = true;
|
|
674
763
|
}
|
|
675
764
|
const errors = aggregateErrorsAt(state, segments);
|
|
765
|
+
const revealedDescendantError = errors.length > 0 && errors.some((e) => {
|
|
766
|
+
const ePath = e.path;
|
|
767
|
+
if (ePath.length === segments.length && ePath.every((s, i) => s === segments[i])) return false;
|
|
768
|
+
if (submissionAttempts > 0) return true;
|
|
769
|
+
if (ePath.length === 1 && ePath[0] === "") return blurredAfterInteraction;
|
|
770
|
+
return blurredLeafSegments.some((s) => paths.isPathPrefix(ePath, s));
|
|
771
|
+
});
|
|
676
772
|
if (!asyncPending && state.pathHasAsyncValidation(segments)) asyncPending = true;
|
|
773
|
+
if ((state.fieldValidationCounts.get(key) ?? 0) > 0) {
|
|
774
|
+
validating = true;
|
|
775
|
+
const since = state.fieldValidatingSince.get(key);
|
|
776
|
+
if (since !== void 0 && (validatingSince === null || since < validatingSince))
|
|
777
|
+
validatingSince = since;
|
|
778
|
+
}
|
|
779
|
+
if ((state.fieldTransformCounts.get(key) ?? 0) > 0) {
|
|
780
|
+
transforming = true;
|
|
781
|
+
const since = state.fieldTransformingSince.get(key);
|
|
782
|
+
if (since !== void 0 && (transformingSince === null || since < transformingSince))
|
|
783
|
+
transformingSince = since;
|
|
784
|
+
}
|
|
785
|
+
const ownTransformError = state.transformErrors.get(key) ?? null;
|
|
677
786
|
const gated = asyncPending && !state.firstValidationDone.value;
|
|
678
787
|
const valid = !gated && errors.length === 0 && !validating;
|
|
679
788
|
const resolved = state.schema.getFieldMetaAtPath ? state.schema.getFieldMetaAtPath(segments) : EMPTY_RESOLVED_FIELD_META;
|
|
680
789
|
const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
|
|
681
790
|
const label = resolved.label || humanize(lastSegment);
|
|
682
791
|
return {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
792
|
+
base: {
|
|
793
|
+
value,
|
|
794
|
+
original,
|
|
795
|
+
pristine,
|
|
796
|
+
dirty,
|
|
797
|
+
focused,
|
|
798
|
+
blurred,
|
|
799
|
+
touched,
|
|
800
|
+
interacted,
|
|
801
|
+
blurredAfterInteraction,
|
|
802
|
+
connected,
|
|
803
|
+
element: null,
|
|
804
|
+
elements: EMPTY_ELEMENTS,
|
|
805
|
+
updatedAt,
|
|
806
|
+
errors,
|
|
807
|
+
validating,
|
|
808
|
+
valid,
|
|
809
|
+
transforming,
|
|
810
|
+
busy: transforming || validating,
|
|
811
|
+
// A container surfaces its OWN transform failure (a transform registered
|
|
812
|
+
// on the container path, e.g. a file normalizer) but never rolls up a
|
|
813
|
+
// descendant leaf's failure — that stays a per-field channel.
|
|
814
|
+
transformError: ownTransformError,
|
|
815
|
+
path: segments,
|
|
816
|
+
...computeFieldIdentity(formInstanceId, state.formKey, key),
|
|
817
|
+
key: state.arrayElementKey(segments),
|
|
818
|
+
blank,
|
|
819
|
+
label,
|
|
820
|
+
description: resolved.description,
|
|
821
|
+
placeholder: resolved.placeholder,
|
|
822
|
+
meta: resolved.meta
|
|
823
|
+
},
|
|
824
|
+
validatingSince,
|
|
825
|
+
transformingSince,
|
|
826
|
+
revealedDescendantError
|
|
707
827
|
};
|
|
708
828
|
}
|
|
709
829
|
function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
|
|
710
|
-
const base = buildContainerFieldStateBase(state, segments, key, formInstanceId);
|
|
711
|
-
return decorateWithDerivedProps(
|
|
830
|
+
const { base, validatingSince, transformingSince, revealedDescendantError } = buildContainerFieldStateBase(state, segments, key, formInstanceId);
|
|
831
|
+
return decorateWithDerivedProps(
|
|
832
|
+
base,
|
|
833
|
+
state,
|
|
834
|
+
getFormMetaBase,
|
|
835
|
+
key,
|
|
836
|
+
validatingSince,
|
|
837
|
+
transformingSince,
|
|
838
|
+
revealedDescendantError,
|
|
839
|
+
segments.length === 0,
|
|
840
|
+
getDisplayState
|
|
841
|
+
);
|
|
712
842
|
}
|
|
713
|
-
function decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState) {
|
|
843
|
+
function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, transformingSince, revealedDescendantError, isRoot, getDisplayState) {
|
|
714
844
|
const firstError = base.errors[0];
|
|
715
845
|
const predicate = getDisplayState ?? state.getDisplayState;
|
|
716
846
|
const formMeta = getFormMetaBase();
|
|
717
|
-
|
|
847
|
+
const ctx = {
|
|
848
|
+
field: base,
|
|
849
|
+
formMeta,
|
|
850
|
+
validatingSince,
|
|
851
|
+
transformingSince,
|
|
852
|
+
// The engine's clock. Frozen to 0 under SSR (no clock, nothing in
|
|
853
|
+
// flight) so the reducer returns the plain verdict and hydration matches.
|
|
854
|
+
now: state.ssr ? 0 : Date.now()
|
|
855
|
+
};
|
|
856
|
+
let machine;
|
|
857
|
+
let rollupApplies = isDefaultDisplayState(predicate);
|
|
718
858
|
try {
|
|
719
|
-
|
|
859
|
+
machine = state.displayEngine.resolve(key, ctx, predicate);
|
|
720
860
|
} catch (err) {
|
|
721
861
|
if (paths.__DEV__ && !warnedDisplayStatePredicates.has(predicate)) {
|
|
722
862
|
warnedDisplayStatePredicates.add(predicate);
|
|
@@ -725,8 +865,11 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState)
|
|
|
725
865
|
err
|
|
726
866
|
);
|
|
727
867
|
}
|
|
728
|
-
|
|
868
|
+
machine = state.displayEngine.resolve(key, ctx, defaultDisplayState);
|
|
869
|
+
rollupApplies = true;
|
|
729
870
|
}
|
|
871
|
+
const submitValidating = rollupApplies && isRoot && state.submitting.value && state.activeValidations.value > 0;
|
|
872
|
+
const displayState = machine.display === "pending" || submitValidating ? "pending" : rollupApplies && revealedDescendantError ? "error" : machine.display;
|
|
730
873
|
return {
|
|
731
874
|
...base,
|
|
732
875
|
displayState,
|
|
@@ -772,6 +915,15 @@ function liveKeysAtPath(state, segments) {
|
|
|
772
915
|
if (typeof value === "object") return Object.keys(value);
|
|
773
916
|
return [];
|
|
774
917
|
}
|
|
918
|
+
function liveContainerHasKey(state, segments, key) {
|
|
919
|
+
const value = getAtPath(state.form.value, segments);
|
|
920
|
+
if (value === null || value === void 0 || typeof value !== "object") return false;
|
|
921
|
+
if (Array.isArray(value)) {
|
|
922
|
+
const index = Number(key);
|
|
923
|
+
return Number.isInteger(index) && index >= 0 && index < value.length && String(index) === key;
|
|
924
|
+
}
|
|
925
|
+
return Object.hasOwn(value, key);
|
|
926
|
+
}
|
|
775
927
|
function isArrayPath(state, segments) {
|
|
776
928
|
if (segments.length === 0) return false;
|
|
777
929
|
return Array.isArray(getAtPath(state.form.value, segments));
|
|
@@ -800,6 +952,21 @@ const INTEGER_SEGMENT = /^(?:0|[1-9]\d*)$/;
|
|
|
800
952
|
function keyToSegment(key) {
|
|
801
953
|
return INTEGER_SEGMENT.test(key) ? Number(key) : key;
|
|
802
954
|
}
|
|
955
|
+
function callableInvokeShim(method, surface, getDescent) {
|
|
956
|
+
const fnMethod = Reflect.get(Function.prototype, method);
|
|
957
|
+
return new Proxy((() => {
|
|
958
|
+
}), {
|
|
959
|
+
apply: (_target, _thisArg, args) => Reflect.apply(fnMethod, surface, args),
|
|
960
|
+
get: (_target, key) => Reflect.get(getDescent(), key),
|
|
961
|
+
has: (_target, key) => Reflect.has(getDescent(), key),
|
|
962
|
+
ownKeys: () => Reflect.ownKeys(getDescent()),
|
|
963
|
+
getOwnPropertyDescriptor: (_target, key) => {
|
|
964
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(getDescent(), key);
|
|
965
|
+
if (descriptor !== void 0) descriptor.configurable = true;
|
|
966
|
+
return descriptor;
|
|
967
|
+
}
|
|
968
|
+
});
|
|
969
|
+
}
|
|
803
970
|
function buildSurfaceProxy(opts) {
|
|
804
971
|
const containerCache = /* @__PURE__ */ new Map();
|
|
805
972
|
const leafViewCache = /* @__PURE__ */ new Map();
|
|
@@ -826,6 +993,8 @@ function buildSurfaceProxy(opts) {
|
|
|
826
993
|
const cacheKey = `${JSON.stringify(segments)}+${isArrayLike ? "A" : "O"}`;
|
|
827
994
|
const existing = containerCache.get(cacheKey);
|
|
828
995
|
if (existing !== void 0) return existing;
|
|
996
|
+
const isFixedObject = opts.schema.isFixedObjectAtPath(segments);
|
|
997
|
+
const containerHasKey = (k) => opts.containerHasOwnKey !== void 0 ? opts.containerHasOwnKey(segments, k) : opts.containerOwnKeys?.(segments).includes(k) === true;
|
|
829
998
|
const snapshotContainer = () => opts.materializeContainer === void 0 ? {} : opts.materializeContainer(segments);
|
|
830
999
|
const {
|
|
831
1000
|
toString: containerToString,
|
|
@@ -833,8 +1002,9 @@ function buildSurfaceProxy(opts) {
|
|
|
833
1002
|
toJSON: containerToJSON,
|
|
834
1003
|
toPrimitive: containerToPrimitive
|
|
835
1004
|
} = makeReadonlyCoercion(snapshotContainer);
|
|
836
|
-
const
|
|
837
|
-
|
|
1005
|
+
const isRoot = segments.length === 0;
|
|
1006
|
+
const target = isRoot ? (() => {
|
|
1007
|
+
}) : isArrayLike ? [] : {};
|
|
838
1008
|
const proxy = new Proxy(target, {
|
|
839
1009
|
apply(_, __, args) {
|
|
840
1010
|
const arg = args[0];
|
|
@@ -865,7 +1035,16 @@ function buildSurfaceProxy(opts) {
|
|
|
865
1035
|
return key === "toString" ? containerToString : containerValueOf;
|
|
866
1036
|
}
|
|
867
1037
|
}
|
|
868
|
-
|
|
1038
|
+
if (key === "hasOwnProperty" && !schemaHasPath(childSegs)) {
|
|
1039
|
+
return Object.prototype.hasOwnProperty;
|
|
1040
|
+
}
|
|
1041
|
+
if (isRoot && (key === "call" || key === "apply" || key === "bind")) {
|
|
1042
|
+
return callableInvokeShim(key, proxy, () => descendOrTerminate(childSegs));
|
|
1043
|
+
}
|
|
1044
|
+
if (opts.isTerminalAt?.(childSegs) === true || isFixedObject && schemaHasPath(childSegs) || containerHasKey(key)) {
|
|
1045
|
+
return descendOrTerminate(childSegs);
|
|
1046
|
+
}
|
|
1047
|
+
return void 0;
|
|
869
1048
|
},
|
|
870
1049
|
has(_, key) {
|
|
871
1050
|
if (typeof key === "symbol") return Reflect.has(target, key);
|
|
@@ -950,15 +1129,8 @@ function buildSurfaceProxy(opts) {
|
|
|
950
1129
|
toJSON: leafToJSONHandler,
|
|
951
1130
|
toPrimitive: leafToPrimitive
|
|
952
1131
|
} = makeReadonlyCoercion(snapshotLeaf);
|
|
953
|
-
const target =
|
|
954
|
-
});
|
|
1132
|
+
const target = {};
|
|
955
1133
|
const proxy = new Proxy(target, {
|
|
956
|
-
apply(_, __, args) {
|
|
957
|
-
const arg = args[0];
|
|
958
|
-
if (arg === void 0) return opts.resolveCallTarget(segments);
|
|
959
|
-
const { segments: argSegs } = paths.canonicalizePath(arg);
|
|
960
|
-
return opts.resolveCallTarget(argSegs);
|
|
961
|
-
},
|
|
962
1134
|
get(_, key) {
|
|
963
1135
|
if (typeof key === "symbol") {
|
|
964
1136
|
if (key === Symbol.toPrimitive) return leafToPrimitive;
|
|
@@ -968,6 +1140,9 @@ function buildSurfaceProxy(opts) {
|
|
|
968
1140
|
if (key === "toString") return leafToString;
|
|
969
1141
|
if (key === "valueOf") return leafValueOf;
|
|
970
1142
|
if (key === "toJSON") return leafToJSONHandler;
|
|
1143
|
+
if (key === "hasOwnProperty" && !schemaHasPath([...segments, keyToSegment(key)])) {
|
|
1144
|
+
return Object.prototype.hasOwnProperty;
|
|
1145
|
+
}
|
|
971
1146
|
if (leafKeys.has(key)) {
|
|
972
1147
|
const leaf = opts.resolveLeaf(segments);
|
|
973
1148
|
return readLeafKey(leaf, key);
|
|
@@ -980,8 +1155,8 @@ function buildSurfaceProxy(opts) {
|
|
|
980
1155
|
return true;
|
|
981
1156
|
},
|
|
982
1157
|
// Iteration: leaf-views expose the leaf-key set so
|
|
983
|
-
// `
|
|
984
|
-
//
|
|
1158
|
+
// `Object.keys(form.fields.email)` / spread enumerate the
|
|
1159
|
+
// FieldState props. (`JSON.stringify` routes through `toJSON` above.)
|
|
985
1160
|
ownKeys: () => Array.from(leafKeys),
|
|
986
1161
|
getOwnPropertyDescriptor(_, key) {
|
|
987
1162
|
if (typeof key !== "string") return void 0;
|
|
@@ -1090,6 +1265,12 @@ function buildErrorsProxy(state) {
|
|
|
1090
1265
|
// library-produced verdicts (schema + derived-blank) at unreachable
|
|
1091
1266
|
// paths stay hidden; user-supplied errors are unconditional.
|
|
1092
1267
|
containerOwnKeys: (segments) => errorAwareContainerKeys(state, segments),
|
|
1268
|
+
// Fast path: a key the live form data holds short-circuits before the
|
|
1269
|
+
// O(n) error-store scan, so iterating `form.errors.<array>` over live
|
|
1270
|
+
// indices stays linear. The scan still runs for a key with no live
|
|
1271
|
+
// home — a server error at a non-schema key (`form.errors.ghost`) —
|
|
1272
|
+
// so it keeps surfacing while a genuinely-absent key reads undefined.
|
|
1273
|
+
containerHasOwnKey: (segments, key) => liveContainerHasKey(state, segments, key) || errorAwareContainerKeys(state, segments).includes(key),
|
|
1093
1274
|
isArrayContainer: (segments) => isArrayPath(state, segments)
|
|
1094
1275
|
});
|
|
1095
1276
|
}
|
|
@@ -1263,6 +1444,9 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
|
|
|
1263
1444
|
"errors",
|
|
1264
1445
|
"validating",
|
|
1265
1446
|
"valid",
|
|
1447
|
+
"transforming",
|
|
1448
|
+
"busy",
|
|
1449
|
+
"transformError",
|
|
1266
1450
|
"displayState",
|
|
1267
1451
|
"showErrors",
|
|
1268
1452
|
"showPending",
|
|
@@ -1365,14 +1549,22 @@ function buildFieldStateProxy(state, formInstanceId, getFormMetaBase, options) {
|
|
|
1365
1549
|
terminalCache.set(cacheKey, proxy);
|
|
1366
1550
|
return proxy;
|
|
1367
1551
|
}
|
|
1552
|
+
const surfaceSchema = state.schema;
|
|
1368
1553
|
return buildSurfaceProxy({
|
|
1369
|
-
schema:
|
|
1554
|
+
schema: surfaceSchema,
|
|
1370
1555
|
resolveLeaf: (path) => getFieldStateAt(path),
|
|
1371
1556
|
leafKeys: FIELD_STATE_KEYS,
|
|
1372
1557
|
readLeafKey: (computed, key) => computed.value[key],
|
|
1373
1558
|
materializeContainer: (segments) => materializeFields(state, segments, snapshotFieldStateAt),
|
|
1374
|
-
|
|
1559
|
+
// `form.fields(path)` resolves a FieldState for any path the SCHEMA
|
|
1560
|
+
// declares — a leaf, a container, an inactive discriminated-union
|
|
1561
|
+
// variant key, or an out-of-bounds array index (the element schema
|
|
1562
|
+
// admits any index). A path the schema doesn't have is a typo, not a
|
|
1563
|
+
// field, so it reads `undefined` rather than a phantom stub. The
|
|
1564
|
+
// empty path (`form.fields()`) is the root object, always valid.
|
|
1565
|
+
resolveCallTarget: (path) => surfaceSchema.getSlimPrimitiveTypesAtPath(path).size > 0 ? fieldStateTerminalAt(path) : void 0,
|
|
1375
1566
|
containerOwnKeys: (segments) => liveKeysAtPath(state, segments),
|
|
1567
|
+
containerHasOwnKey: (segments, key) => liveContainerHasKey(state, segments, key),
|
|
1376
1568
|
isArrayContainer: (segments) => isArrayPath(state, segments)
|
|
1377
1569
|
});
|
|
1378
1570
|
}
|
|
@@ -1419,90 +1611,6 @@ async function getStorageAdapter(storage) {
|
|
|
1419
1611
|
}
|
|
1420
1612
|
}
|
|
1421
1613
|
}
|
|
1422
|
-
const PERSISTED_ENVELOPE_VERSION = 6;
|
|
1423
|
-
function readPersistedPayload(value) {
|
|
1424
|
-
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
1425
|
-
const envelope = value;
|
|
1426
|
-
if (typeof envelope.v !== "number") return null;
|
|
1427
|
-
if (envelope.v !== PERSISTED_ENVELOPE_VERSION) {
|
|
1428
|
-
warnVersionMismatch(envelope.v);
|
|
1429
|
-
return null;
|
|
1430
|
-
}
|
|
1431
|
-
if (envelope.data === void 0 || typeof envelope.data !== "object") return null;
|
|
1432
|
-
return envelope;
|
|
1433
|
-
}
|
|
1434
|
-
const warnedVersions = paths.__DEV__ ? /* @__PURE__ */ new Set() : null;
|
|
1435
|
-
function warnVersionMismatch(observedVersion) {
|
|
1436
|
-
if (warnedVersions === null) return;
|
|
1437
|
-
if (warnedVersions.has(observedVersion)) return;
|
|
1438
|
-
warnedVersions.add(observedVersion);
|
|
1439
|
-
console.warn(
|
|
1440
|
-
`[attaform] Dropping persisted draft \u2014 envelope v=${observedVersion}, but this version of the library expects v=${PERSISTED_ENVELOPE_VERSION}. The persisted shape changed across releases; older drafts can't be restored. New drafts saved this session will use the current envelope.`
|
|
1441
|
-
);
|
|
1442
|
-
}
|
|
1443
|
-
function buildPersistedPayload(form, include, schemaErrors, userErrors, blankPaths) {
|
|
1444
|
-
let transientList;
|
|
1445
|
-
if (blankPaths !== void 0 && blankPaths.size > 0) {
|
|
1446
|
-
transientList = [...blankPaths];
|
|
1447
|
-
}
|
|
1448
|
-
if (include === "form") {
|
|
1449
|
-
if (transientList === void 0) return { v: PERSISTED_ENVELOPE_VERSION, data: { form } };
|
|
1450
|
-
return {
|
|
1451
|
-
v: PERSISTED_ENVELOPE_VERSION,
|
|
1452
|
-
data: { form, blankPaths: transientList }
|
|
1453
|
-
};
|
|
1454
|
-
}
|
|
1455
|
-
return {
|
|
1456
|
-
v: PERSISTED_ENVELOPE_VERSION,
|
|
1457
|
-
data: {
|
|
1458
|
-
form,
|
|
1459
|
-
schemaErrors: [...schemaErrors.entries()].map(([k, v]) => [k, [...v]]),
|
|
1460
|
-
userErrors: [...userErrors.entries()].map(([k, v]) => [k, [...v]]),
|
|
1461
|
-
...transientList !== void 0 ? { blankPaths: transientList } : {}
|
|
1462
|
-
}
|
|
1463
|
-
};
|
|
1464
|
-
}
|
|
1465
|
-
function createDebouncedWriter(write, debounceMs) {
|
|
1466
|
-
let timer = null;
|
|
1467
|
-
let pending = null;
|
|
1468
|
-
let writeGeneration = 0;
|
|
1469
|
-
function runWrite() {
|
|
1470
|
-
const gen = ++writeGeneration;
|
|
1471
|
-
pending = write().finally(() => {
|
|
1472
|
-
if (writeGeneration === gen) pending = null;
|
|
1473
|
-
});
|
|
1474
|
-
}
|
|
1475
|
-
function schedule() {
|
|
1476
|
-
if (timer !== null) clearTimeout(timer);
|
|
1477
|
-
if (debounceMs === 0) {
|
|
1478
|
-
runWrite();
|
|
1479
|
-
return;
|
|
1480
|
-
}
|
|
1481
|
-
timer = setTimeout(() => {
|
|
1482
|
-
timer = null;
|
|
1483
|
-
runWrite();
|
|
1484
|
-
}, debounceMs);
|
|
1485
|
-
}
|
|
1486
|
-
async function flush() {
|
|
1487
|
-
if (timer !== null) {
|
|
1488
|
-
clearTimeout(timer);
|
|
1489
|
-
timer = null;
|
|
1490
|
-
runWrite();
|
|
1491
|
-
}
|
|
1492
|
-
while (pending !== null) {
|
|
1493
|
-
const awaited = pending;
|
|
1494
|
-
await awaited;
|
|
1495
|
-
if (pending === awaited) break;
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
function cancel() {
|
|
1499
|
-
if (timer !== null) {
|
|
1500
|
-
clearTimeout(timer);
|
|
1501
|
-
timer = null;
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
return { schedule, flush, cancel };
|
|
1505
|
-
}
|
|
1506
1614
|
function resolveStorageKeyBase(config, formKey) {
|
|
1507
1615
|
return config.key ?? `${PERSISTENCE_KEY_PREFIX}${formKey}`;
|
|
1508
1616
|
}
|
|
@@ -1549,47 +1657,6 @@ async function sweepNonConfiguredStandardStoresForOrphans(configured, base) {
|
|
|
1549
1657
|
}
|
|
1550
1658
|
}
|
|
1551
1659
|
}
|
|
1552
|
-
function pluckPaths(form, pathKeys) {
|
|
1553
|
-
let sparse = void 0;
|
|
1554
|
-
for (const pathKey of pathKeys) {
|
|
1555
|
-
const segments = paths.segmentsForPathKey(pathKey);
|
|
1556
|
-
if (segments === null) continue;
|
|
1557
|
-
const value = getAtPath(form, segments);
|
|
1558
|
-
if (value === void 0) continue;
|
|
1559
|
-
sparse = setAtPath(sparse ?? {}, segments, value);
|
|
1560
|
-
}
|
|
1561
|
-
return sparse ?? {};
|
|
1562
|
-
}
|
|
1563
|
-
function stripUnacknowledgedSensitiveLeaves(form, optedInPaths, isSensitivePath) {
|
|
1564
|
-
const acknowledgedSensitive = [];
|
|
1565
|
-
for (const key of optedInPaths) {
|
|
1566
|
-
const segs = paths.segmentsForPathKey(key);
|
|
1567
|
-
if (segs !== null && isSensitivePath(segs)) acknowledgedSensitive.push(segs);
|
|
1568
|
-
}
|
|
1569
|
-
const coveredByAcknowledged = (path) => acknowledgedSensitive.some((prefix) => paths.isPathPrefix(prefix, path));
|
|
1570
|
-
const walk = (path, value) => {
|
|
1571
|
-
if (path.length > 0 && isSensitivePath(path) && !coveredByAcknowledged(path)) {
|
|
1572
|
-
return void 0;
|
|
1573
|
-
}
|
|
1574
|
-
if (value === null || typeof value !== "object") return value;
|
|
1575
|
-
if (Array.isArray(value)) return value.map((item, i) => walk([...path, i], item));
|
|
1576
|
-
if (!isPlainRecord(value)) return value;
|
|
1577
|
-
const out = {};
|
|
1578
|
-
for (const key of Object.keys(value)) {
|
|
1579
|
-
const walked = walk([...path, key], value[key]);
|
|
1580
|
-
if (walked !== void 0) safeAssign(out, key, walked);
|
|
1581
|
-
}
|
|
1582
|
-
return out;
|
|
1583
|
-
};
|
|
1584
|
-
return walk([], form);
|
|
1585
|
-
}
|
|
1586
|
-
function filterErrorsByPaths(errors, pathKeys) {
|
|
1587
|
-
const out = /* @__PURE__ */ new Map();
|
|
1588
|
-
for (const [key, value] of errors) {
|
|
1589
|
-
if (pathKeys.has(key)) out.set(key, value);
|
|
1590
|
-
}
|
|
1591
|
-
return out;
|
|
1592
|
-
}
|
|
1593
1660
|
function mergeSparseHydration(schemaDefaults, sparse, schema) {
|
|
1594
1661
|
return mergeDeep(schemaDefaults, sparse, [], schema);
|
|
1595
1662
|
}
|
|
@@ -1773,7 +1840,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1773
1840
|
if (!result.ok) return result.error;
|
|
1774
1841
|
return stripData(composeWithDerivedBlank(result.refinement, result.segments));
|
|
1775
1842
|
}
|
|
1776
|
-
async function
|
|
1843
|
+
async function parse(pathInput) {
|
|
1777
1844
|
const result = await runImperativeValidation(pathInput, {
|
|
1778
1845
|
cancelInFlight: false,
|
|
1779
1846
|
commitToSchemaErrors: false
|
|
@@ -1826,7 +1893,10 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1826
1893
|
state.activeSubmissions.value += 1;
|
|
1827
1894
|
state.submitting.value = true;
|
|
1828
1895
|
state.submitError.value = null;
|
|
1896
|
+
state.clearUserErrors();
|
|
1897
|
+
while (state.activeTransforms.value > 0) await state.settleTransforms();
|
|
1829
1898
|
state.cancelFieldValidation();
|
|
1899
|
+
state.displayEngine.clear();
|
|
1830
1900
|
state.activeValidations.value += 1;
|
|
1831
1901
|
const refinement = await runRefinementValidation(state.form.value, void 0);
|
|
1832
1902
|
const merged = composeWithDerivedBlank(refinement, void 0);
|
|
@@ -1863,9 +1933,8 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1863
1933
|
state.emitSubmitSuccess();
|
|
1864
1934
|
} catch (err) {
|
|
1865
1935
|
if (state.submissionGeneration.value === genAtEntry) {
|
|
1866
|
-
state.submitError.value = err;
|
|
1936
|
+
state.submitError.value = paths.toError(err);
|
|
1867
1937
|
}
|
|
1868
|
-
throw err;
|
|
1869
1938
|
} finally {
|
|
1870
1939
|
if (!validationSettled) {
|
|
1871
1940
|
state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
|
|
@@ -1879,7 +1948,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1879
1948
|
};
|
|
1880
1949
|
return submitHandler;
|
|
1881
1950
|
};
|
|
1882
|
-
return { validate, validateAsync,
|
|
1951
|
+
return { validate, validateAsync, parse, handleSubmit };
|
|
1883
1952
|
}
|
|
1884
1953
|
function toSegments(pathInput) {
|
|
1885
1954
|
return paths.canonicalizePath(pathInput).segments;
|
|
@@ -2352,6 +2421,23 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2352
2421
|
markConnectedOptimistically: () => {
|
|
2353
2422
|
state.markConnectedOptimistically(segments);
|
|
2354
2423
|
},
|
|
2424
|
+
// --- Async transform lifecycle (internal; the directive's
|
|
2425
|
+
// deferred orchestrator is the only legitimate consumer). Thin
|
|
2426
|
+
// path-bound delegates to the store's per-path token / counter
|
|
2427
|
+
// machinery — same pattern as `markBlank` / `setValueWithInternalPath`,
|
|
2428
|
+
// so the directive (which holds only this RegisterValue, never the
|
|
2429
|
+
// store) can drive the busy/discard/error bookkeeping. ---
|
|
2430
|
+
beginTransform: (holder) => state.beginTransform(pathKey, holder),
|
|
2431
|
+
isCurrentTransform: (token) => state.isCurrentTransform(pathKey, token),
|
|
2432
|
+
endTransform: (token) => state.endTransform(pathKey, token),
|
|
2433
|
+
setTransformError: (err) => state.setTransformError(pathKey, err),
|
|
2434
|
+
// Synchronous read of "is a transform in flight at this path". The
|
|
2435
|
+
// orchestrator's `beginTransform` bumps the count before the
|
|
2436
|
+
// listener's force-sync block runs, so the directive reads this to
|
|
2437
|
+
// skip reverting the DOM to stale storage mid-flight.
|
|
2438
|
+
get transforming() {
|
|
2439
|
+
return (state.fieldTransformCounts.get(pathKey) ?? 0) > 0;
|
|
2440
|
+
},
|
|
2355
2441
|
path: pathKey,
|
|
2356
2442
|
// Frozen so a wrapper component can pass `rv.segments` directly
|
|
2357
2443
|
// to `form.fields(...)` without defensive copying — and so test
|
|
@@ -2550,7 +2636,9 @@ function expandUnsetAt(segments, schema, paths$1) {
|
|
|
2550
2636
|
function buildCallableReadonlySnapshotProxy(opts) {
|
|
2551
2637
|
const target = (() => {
|
|
2552
2638
|
});
|
|
2553
|
-
const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(
|
|
2639
|
+
const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(
|
|
2640
|
+
opts.coercionSnapshot ?? opts.snapshot
|
|
2641
|
+
);
|
|
2554
2642
|
const callResolve = opts.resolveCall ?? ((arg) => opts.resolveKey(String(arg)));
|
|
2555
2643
|
return new Proxy(target, {
|
|
2556
2644
|
apply(_, __, args) {
|
|
@@ -2599,27 +2687,52 @@ function buildCallableReadonlySnapshotProxy(opts) {
|
|
|
2599
2687
|
});
|
|
2600
2688
|
}
|
|
2601
2689
|
|
|
2690
|
+
function materializeFormValue(node) {
|
|
2691
|
+
if (node === null || typeof node !== "object") return node;
|
|
2692
|
+
if (Array.isArray(node)) {
|
|
2693
|
+
const out2 = new Array(node.length);
|
|
2694
|
+
for (let i = 0; i < node.length; i++) out2[i] = materializeFormValue(node[i]);
|
|
2695
|
+
return out2;
|
|
2696
|
+
}
|
|
2697
|
+
if (!isPlainRecord(node)) return vue.toRaw(node);
|
|
2698
|
+
const rec = node;
|
|
2699
|
+
const out = {};
|
|
2700
|
+
for (const key of Object.keys(rec)) {
|
|
2701
|
+
safeAssign(out, key, materializeFormValue(safeOwnRead(rec, key)));
|
|
2702
|
+
}
|
|
2703
|
+
return out;
|
|
2704
|
+
}
|
|
2602
2705
|
function buildValuesProxy(form) {
|
|
2603
2706
|
const inner = vue.computed(() => vue.readonly(form.value));
|
|
2604
2707
|
return buildCallableReadonlySnapshotProxy({
|
|
2605
2708
|
surface: "form.values",
|
|
2606
2709
|
snapshot: () => inner.value,
|
|
2710
|
+
// Faithful, reactivity-preserving serialisation: walk the reactive
|
|
2711
|
+
// proxy with own-safe reads so `JSON.stringify(form.values)` /
|
|
2712
|
+
// `String(form.values)` reflect the stored data — including a field
|
|
2713
|
+
// literally named `hasOwnProperty` that Vue would otherwise shim —
|
|
2714
|
+
// while still tracking the per-key reads that drive re-render.
|
|
2715
|
+
coercionSnapshot: () => materializeFormValue(inner.value),
|
|
2607
2716
|
// Read through the readonly proxy at access time so Vue's
|
|
2608
2717
|
// dependency tracking lands inside the consumer's active effect
|
|
2609
2718
|
// — `inner.value[key]` is what triggers per-key tracking.
|
|
2610
|
-
|
|
2611
|
-
//
|
|
2612
|
-
//
|
|
2613
|
-
//
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2719
|
+
//
|
|
2720
|
+
// Prototype-shadowed names (`hasOwnProperty`, `constructor`, …) read
|
|
2721
|
+
// off the RAW target instead: own-shadows-inherited semantics still
|
|
2722
|
+
// hold (a data field by that name returns its stored value), but
|
|
2723
|
+
// when there's no such field the inherited member resolves — so
|
|
2724
|
+
// `form.values.hasOwnProperty('x')` keeps working as the real
|
|
2725
|
+
// method. The raw read dodges Vue's `hasOwnProperty` proxy shim,
|
|
2726
|
+
// which would otherwise mask a data field of that name. (`toString`
|
|
2727
|
+
// / `valueOf` / `toJSON` never reach here — the base get trap
|
|
2728
|
+
// intercepts them as coercion handlers first.)
|
|
2729
|
+
resolveKey: (key) => isShadowedKey(key) ? vue.toRaw(inner.value)[key] : inner.value[key],
|
|
2730
|
+
// Dynamic path: walk segments through the readonly proxy with the
|
|
2731
|
+
// same own-property-safe descent the rest of the runtime uses
|
|
2732
|
+
// (`getAtPath`), so `form.values('a.hasOwnProperty')` resolves the
|
|
2733
|
+
// stored value. Per-level reads still propagate Vue's tracking for
|
|
2734
|
+
// ordinary keys.
|
|
2735
|
+
resolveCall: (arg) => getAtPath(inner.value, paths.canonicalizePath(arg).segments),
|
|
2623
2736
|
ownKeys: () => Reflect.ownKeys(inner.value),
|
|
2624
2737
|
hasKey: (key) => Reflect.has(inner.value, key),
|
|
2625
2738
|
describeKey: (key) => {
|
|
@@ -2643,7 +2756,12 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2643
2756
|
return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
|
|
2644
2757
|
};
|
|
2645
2758
|
const getFormMetaBase = () => {
|
|
2646
|
-
const rootBase = buildContainerFieldStateBase(
|
|
2759
|
+
const { base: rootBase } = buildContainerFieldStateBase(
|
|
2760
|
+
state,
|
|
2761
|
+
paths.ROOT_PATH,
|
|
2762
|
+
paths.ROOT_PATH_KEY,
|
|
2763
|
+
formInstanceId
|
|
2764
|
+
);
|
|
2647
2765
|
return {
|
|
2648
2766
|
...rootBase,
|
|
2649
2767
|
submitting: state.submitting.value,
|
|
@@ -2675,12 +2793,12 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2675
2793
|
const {
|
|
2676
2794
|
validate: validateBuilt,
|
|
2677
2795
|
validateAsync: validateAsyncBuilt,
|
|
2678
|
-
|
|
2796
|
+
parse: parseBuilt,
|
|
2679
2797
|
handleSubmit
|
|
2680
2798
|
} = buildProcessForm(state, formInstanceId, processOptions);
|
|
2681
2799
|
const validate = (pathInput) => validateBuilt(pathInput);
|
|
2682
2800
|
const validateAsync = (pathInput) => validateAsyncBuilt(pathInput);
|
|
2683
|
-
const
|
|
2801
|
+
const parse = (pathInput) => parseBuilt(pathInput);
|
|
2684
2802
|
function pathToRef(pathInput) {
|
|
2685
2803
|
const segments = paths.canonicalizePath(pathInput).segments;
|
|
2686
2804
|
return vue.computed(() => getAtPath(state.form.value, segments));
|
|
@@ -2908,6 +3026,21 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2908
3026
|
// keep the explicit form-level computation for the gate.
|
|
2909
3027
|
valid,
|
|
2910
3028
|
errors: metaErrors,
|
|
3029
|
+
// Whole-form transforming mirrors the global `activeTransforms`
|
|
3030
|
+
// counter ORed with any per-leaf transform in flight (the root
|
|
3031
|
+
// rollup), exactly as `validating` composes its lifecycle and
|
|
3032
|
+
// per-field sources. `busy` is the union of both work signals at
|
|
3033
|
+
// the form level. `transformError` is leaf-only, so the root
|
|
3034
|
+
// rollup reads it as `null` (kept for FieldState-shape parity).
|
|
3035
|
+
transforming: vue.computed(
|
|
3036
|
+
() => state.activeTransforms.value > 0 || rootFieldState.value.transforming
|
|
3037
|
+
),
|
|
3038
|
+
busy: vue.computed(
|
|
3039
|
+
() => state.activeValidations.value > 0 || state.activeTransforms.value > 0 || rootFieldState.value.validating || rootFieldState.value.transforming
|
|
3040
|
+
),
|
|
3041
|
+
get transformError() {
|
|
3042
|
+
return rootFieldState.value.transformError;
|
|
3043
|
+
},
|
|
2911
3044
|
// `displayState` / the `show*` booleans / `firstError` flow
|
|
2912
3045
|
// through the same root field-state computed as the rest of the
|
|
2913
3046
|
// FieldState surface, so `form.meta.displayState` matches
|
|
@@ -2978,7 +3111,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2978
3111
|
instanceId: formInstanceId
|
|
2979
3112
|
})
|
|
2980
3113
|
);
|
|
2981
|
-
const
|
|
3114
|
+
const persistenceHandle = state.modules.get(PERSISTENCE_MODULE_KEY);
|
|
2982
3115
|
const reset = (nextDefaultValues) => {
|
|
2983
3116
|
if (nextDefaultValues === void 0) {
|
|
2984
3117
|
state.reset();
|
|
@@ -2993,15 +3126,15 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2993
3126
|
state.originalBlankPaths.add(pathKey);
|
|
2994
3127
|
}
|
|
2995
3128
|
}
|
|
2996
|
-
if (
|
|
2997
|
-
void
|
|
3129
|
+
if (persistenceHandle !== void 0) {
|
|
3130
|
+
void persistenceHandle.ready.then((m) => m?.clearPersistedDraft()).catch(() => void 0);
|
|
2998
3131
|
}
|
|
2999
3132
|
};
|
|
3000
3133
|
const resetField = (pathInput) => {
|
|
3001
3134
|
const segments = paths.canonicalizePath(pathInput).segments;
|
|
3002
3135
|
state.resetField(segments);
|
|
3003
|
-
if (
|
|
3004
|
-
void
|
|
3136
|
+
if (persistenceHandle !== void 0) {
|
|
3137
|
+
void persistenceHandle.ready.then((m) => m?.clearPersistedDraft(segments)).catch(() => void 0);
|
|
3005
3138
|
}
|
|
3006
3139
|
};
|
|
3007
3140
|
function clear(pathInput) {
|
|
@@ -3019,10 +3152,14 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3019
3152
|
)) {
|
|
3020
3153
|
return;
|
|
3021
3154
|
}
|
|
3155
|
+
if (persistenceHandle === void 0) return;
|
|
3156
|
+
const persistence = await persistenceHandle.ready;
|
|
3022
3157
|
if (persistence === void 0) return;
|
|
3023
3158
|
await persistence.writePathImmediately(segments);
|
|
3024
3159
|
};
|
|
3025
3160
|
const clearPersistedDraft = async (pathInput) => {
|
|
3161
|
+
if (persistenceHandle === void 0) return;
|
|
3162
|
+
const persistence = await persistenceHandle.ready;
|
|
3026
3163
|
if (persistence === void 0) return;
|
|
3027
3164
|
if (pathInput === void 0) {
|
|
3028
3165
|
await persistence.clearPersistedDraft();
|
|
@@ -3132,7 +3269,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3132
3269
|
setValue: gated(setValueImpl),
|
|
3133
3270
|
validate: gated(validate),
|
|
3134
3271
|
validateAsync: gated(validateAsync),
|
|
3135
|
-
|
|
3272
|
+
parse: gated(parse),
|
|
3273
|
+
settleTransforms: gated(state.settleTransforms),
|
|
3136
3274
|
register: gated(register),
|
|
3137
3275
|
key: state.formKey,
|
|
3138
3276
|
// Auto-unwrapping views over the per-store async-defaults lifecycle
|
|
@@ -3205,6 +3343,93 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3205
3343
|
};
|
|
3206
3344
|
}
|
|
3207
3345
|
|
|
3346
|
+
const IDLE = Object.freeze({ display: "idle" });
|
|
3347
|
+
const MAX_DELAY = 2147483647;
|
|
3348
|
+
function createDisplayEngine(ssr) {
|
|
3349
|
+
const machines = /* @__PURE__ */ new Map();
|
|
3350
|
+
const tick = vue.ref(0);
|
|
3351
|
+
let timer = null;
|
|
3352
|
+
let timerTarget = null;
|
|
3353
|
+
let lastFiredTarget = null;
|
|
3354
|
+
function clearTimer() {
|
|
3355
|
+
if (timer !== null) {
|
|
3356
|
+
clearTimeout(timer);
|
|
3357
|
+
timer = null;
|
|
3358
|
+
timerTarget = null;
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
function nearestReviewAt() {
|
|
3362
|
+
let min = null;
|
|
3363
|
+
for (const m of machines.values()) {
|
|
3364
|
+
if (m.reviewAt === void 0 || !Number.isFinite(m.reviewAt)) continue;
|
|
3365
|
+
if (min === null || m.reviewAt < min) min = m.reviewAt;
|
|
3366
|
+
}
|
|
3367
|
+
return min;
|
|
3368
|
+
}
|
|
3369
|
+
function rearm(now) {
|
|
3370
|
+
const target = nearestReviewAt();
|
|
3371
|
+
if (target === null) {
|
|
3372
|
+
clearTimer();
|
|
3373
|
+
return;
|
|
3374
|
+
}
|
|
3375
|
+
if (timer !== null && timerTarget === target) return;
|
|
3376
|
+
if (target === lastFiredTarget) {
|
|
3377
|
+
clearTimer();
|
|
3378
|
+
return;
|
|
3379
|
+
}
|
|
3380
|
+
clearTimer();
|
|
3381
|
+
timerTarget = target;
|
|
3382
|
+
timer = setTimeout(
|
|
3383
|
+
() => {
|
|
3384
|
+
timer = null;
|
|
3385
|
+
timerTarget = null;
|
|
3386
|
+
lastFiredTarget = target;
|
|
3387
|
+
tick.value++;
|
|
3388
|
+
},
|
|
3389
|
+
Math.min(MAX_DELAY, Math.max(0, target - now))
|
|
3390
|
+
);
|
|
3391
|
+
}
|
|
3392
|
+
function resolve(key, ctx, reducer) {
|
|
3393
|
+
void tick.value;
|
|
3394
|
+
const prev = machines.get(key) ?? IDLE;
|
|
3395
|
+
const machine = reducer(prev, ctx);
|
|
3396
|
+
if (ssr) return machine;
|
|
3397
|
+
const active = machine.display !== "idle" || machine.reviewAt !== void 0 && Number.isFinite(machine.reviewAt);
|
|
3398
|
+
if (active) machines.set(key, machine);
|
|
3399
|
+
else machines.delete(key);
|
|
3400
|
+
rearm(ctx.now);
|
|
3401
|
+
return machine;
|
|
3402
|
+
}
|
|
3403
|
+
function clear() {
|
|
3404
|
+
machines.clear();
|
|
3405
|
+
clearTimer();
|
|
3406
|
+
lastFiredTarget = null;
|
|
3407
|
+
}
|
|
3408
|
+
let detachVisibility = null;
|
|
3409
|
+
if (!ssr && typeof document !== "undefined") {
|
|
3410
|
+
const onVisible = () => {
|
|
3411
|
+
if (document.visibilityState === "visible" && machines.size > 0) tick.value++;
|
|
3412
|
+
};
|
|
3413
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
3414
|
+
detachVisibility = () => document.removeEventListener("visibilitychange", onVisible);
|
|
3415
|
+
}
|
|
3416
|
+
function dispose() {
|
|
3417
|
+
clear();
|
|
3418
|
+
if (detachVisibility !== null) {
|
|
3419
|
+
detachVisibility();
|
|
3420
|
+
detachVisibility = null;
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
return {
|
|
3424
|
+
resolve,
|
|
3425
|
+
clear,
|
|
3426
|
+
dispose,
|
|
3427
|
+
size: () => machines.size,
|
|
3428
|
+
has: (key) => machines.has(key),
|
|
3429
|
+
hasTimer: () => timer !== null
|
|
3430
|
+
};
|
|
3431
|
+
}
|
|
3432
|
+
|
|
3208
3433
|
function applyDuStubs(schema, data, options = {}) {
|
|
3209
3434
|
const warned = options.warn === true ? /* @__PURE__ */ new Set() : void 0;
|
|
3210
3435
|
return walkDuStubs(schema, data, options.basePath ?? [], warned);
|
|
@@ -3543,6 +3768,7 @@ function createArrayBookkeeping(deps) {
|
|
|
3543
3768
|
blankPaths,
|
|
3544
3769
|
originalBlankPaths,
|
|
3545
3770
|
fieldValidationCounts,
|
|
3771
|
+
fieldValidatingSince,
|
|
3546
3772
|
fieldValidationState,
|
|
3547
3773
|
schemaErrors,
|
|
3548
3774
|
activeValidations,
|
|
@@ -3568,6 +3794,7 @@ function createArrayBookkeeping(deps) {
|
|
|
3568
3794
|
}));
|
|
3569
3795
|
migrateSetSubtree(blankPaths, arrayPath, remap);
|
|
3570
3796
|
migrateSetSubtree(originalBlankPaths, arrayPath, remap);
|
|
3797
|
+
migrateMapSubtree(fieldValidatingSince, arrayPath, remap, (since) => since);
|
|
3571
3798
|
migrateMapSubtree(fieldValidationCounts, arrayPath, remap, (count) => count);
|
|
3572
3799
|
arrayIdentity.applyRemap(arrayPath, remap);
|
|
3573
3800
|
}
|
|
@@ -3627,6 +3854,18 @@ function isHydratedFieldRecord(value) {
|
|
|
3627
3854
|
const r = value;
|
|
3628
3855
|
return Array.isArray(r.path) && (typeof r.updatedAt === "string" || r.updatedAt === null) && typeof r.connected === "boolean" && (typeof r.focused === "boolean" || r.focused === null) && (typeof r.blurred === "boolean" || r.blurred === null) && typeof r.touched === "boolean" && typeof r.interacted === "boolean" && typeof r.blurredAfterInteraction === "boolean";
|
|
3629
3856
|
}
|
|
3857
|
+
function withClearedHistoryFlags(record, now) {
|
|
3858
|
+
return {
|
|
3859
|
+
path: record.path,
|
|
3860
|
+
updatedAt: now,
|
|
3861
|
+
connected: record.connected,
|
|
3862
|
+
focused: record.focused,
|
|
3863
|
+
blurred: record.blurred,
|
|
3864
|
+
touched: false,
|
|
3865
|
+
interacted: false,
|
|
3866
|
+
blurredAfterInteraction: false
|
|
3867
|
+
};
|
|
3868
|
+
}
|
|
3630
3869
|
function isHydratedValidationErrorArray(value) {
|
|
3631
3870
|
if (!Array.isArray(value)) return false;
|
|
3632
3871
|
for (const entry of value) {
|
|
@@ -3754,6 +3993,9 @@ function createFormStore(options) {
|
|
|
3754
3993
|
const resolvedIsSensitivePath = options.isSensitivePath ?? paths.isSensitivePath;
|
|
3755
3994
|
const cleanupHooks = [];
|
|
3756
3995
|
const modules = /* @__PURE__ */ new Map();
|
|
3996
|
+
const fieldValidatingSince = vue.reactive(/* @__PURE__ */ new Map());
|
|
3997
|
+
const displayEngine = createDisplayEngine(ssr);
|
|
3998
|
+
registerCleanup(() => displayEngine.dispose());
|
|
3757
3999
|
const completedConstraints = defaultValues === void 0 ? void 0 : mergeStructural(schema, [], defaultValues);
|
|
3758
4000
|
const schemaResponse = schema.getDefaultValues({
|
|
3759
4001
|
useDefaultSchemaValues: true,
|
|
@@ -3882,12 +4124,118 @@ function createFormStore(options) {
|
|
|
3882
4124
|
}
|
|
3883
4125
|
const fieldValidationCounts = vue.reactive(/* @__PURE__ */ new Map());
|
|
3884
4126
|
function incFieldValidation(key) {
|
|
3885
|
-
|
|
4127
|
+
fieldValidatingSince.set(key, ssr ? 0 : Date.now());
|
|
4128
|
+
const prevCount = fieldValidationCounts.get(key) ?? 0;
|
|
4129
|
+
fieldValidationCounts.set(key, prevCount + 1);
|
|
3886
4130
|
}
|
|
3887
4131
|
function decFieldValidation(key) {
|
|
3888
4132
|
const next = (fieldValidationCounts.get(key) ?? 0) - 1;
|
|
3889
|
-
if (next <= 0)
|
|
3890
|
-
|
|
4133
|
+
if (next <= 0) {
|
|
4134
|
+
fieldValidationCounts.delete(key);
|
|
4135
|
+
fieldValidatingSince.delete(key);
|
|
4136
|
+
} else {
|
|
4137
|
+
fieldValidationCounts.set(key, next);
|
|
4138
|
+
}
|
|
4139
|
+
}
|
|
4140
|
+
const fieldTransformCounts = vue.reactive(/* @__PURE__ */ new Map());
|
|
4141
|
+
const fieldTransformingSince = vue.reactive(/* @__PURE__ */ new Map());
|
|
4142
|
+
const transformErrors = vue.reactive(/* @__PURE__ */ new Map());
|
|
4143
|
+
const activeTransforms = vue.ref(0);
|
|
4144
|
+
const transformRuns = /* @__PURE__ */ new Map();
|
|
4145
|
+
let transformTokenSeq = 0;
|
|
4146
|
+
const transformWaiters = [];
|
|
4147
|
+
function incFieldTransform(key) {
|
|
4148
|
+
fieldTransformingSince.set(key, ssr ? 0 : Date.now());
|
|
4149
|
+
fieldTransformCounts.set(key, (fieldTransformCounts.get(key) ?? 0) + 1);
|
|
4150
|
+
}
|
|
4151
|
+
function decFieldTransform(key) {
|
|
4152
|
+
const next = (fieldTransformCounts.get(key) ?? 0) - 1;
|
|
4153
|
+
if (next <= 0) {
|
|
4154
|
+
fieldTransformCounts.delete(key);
|
|
4155
|
+
fieldTransformingSince.delete(key);
|
|
4156
|
+
} else {
|
|
4157
|
+
fieldTransformCounts.set(key, next);
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
function flushSettledTransformWaiters() {
|
|
4161
|
+
if (transformWaiters.length === 0) return;
|
|
4162
|
+
const globalIdle = activeTransforms.value === 0;
|
|
4163
|
+
for (let i = transformWaiters.length - 1; i >= 0; i--) {
|
|
4164
|
+
const w = transformWaiters[i];
|
|
4165
|
+
if (w === void 0) continue;
|
|
4166
|
+
const idle = w.key === null ? globalIdle : (fieldTransformCounts.get(w.key) ?? 0) === 0;
|
|
4167
|
+
if (idle) {
|
|
4168
|
+
transformWaiters.splice(i, 1);
|
|
4169
|
+
w.resolve();
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4173
|
+
function releaseTransformRun(key, run) {
|
|
4174
|
+
if (run.released) return;
|
|
4175
|
+
run.released = true;
|
|
4176
|
+
run.holder.aborted = true;
|
|
4177
|
+
run.holder.controller?.abort();
|
|
4178
|
+
activeTransforms.value = Math.max(0, activeTransforms.value - 1);
|
|
4179
|
+
decFieldTransform(key);
|
|
4180
|
+
}
|
|
4181
|
+
function beginTransform(key, holder) {
|
|
4182
|
+
const prior = transformRuns.get(key);
|
|
4183
|
+
if (prior !== void 0) releaseTransformRun(key, prior);
|
|
4184
|
+
const token = ++transformTokenSeq;
|
|
4185
|
+
transformRuns.set(key, { token, holder, released: false });
|
|
4186
|
+
incFieldTransform(key);
|
|
4187
|
+
activeTransforms.value += 1;
|
|
4188
|
+
if (transformErrors.has(key)) transformErrors.delete(key);
|
|
4189
|
+
return token;
|
|
4190
|
+
}
|
|
4191
|
+
function isCurrentTransform(key, token) {
|
|
4192
|
+
return transformRuns.get(key)?.token === token;
|
|
4193
|
+
}
|
|
4194
|
+
function endTransform(key, token) {
|
|
4195
|
+
const run = transformRuns.get(key);
|
|
4196
|
+
if (run?.token === token) {
|
|
4197
|
+
if (!run.released) {
|
|
4198
|
+
activeTransforms.value = Math.max(0, activeTransforms.value - 1);
|
|
4199
|
+
decFieldTransform(key);
|
|
4200
|
+
}
|
|
4201
|
+
transformRuns.delete(key);
|
|
4202
|
+
}
|
|
4203
|
+
flushSettledTransformWaiters();
|
|
4204
|
+
}
|
|
4205
|
+
function setTransformError(key, err) {
|
|
4206
|
+
transformErrors.set(key, err);
|
|
4207
|
+
}
|
|
4208
|
+
function cancelTransforms() {
|
|
4209
|
+
for (const [key, run] of [...transformRuns]) {
|
|
4210
|
+
releaseTransformRun(key, run);
|
|
4211
|
+
transformRuns.delete(key);
|
|
4212
|
+
}
|
|
4213
|
+
if (transformErrors.size > 0) transformErrors.clear();
|
|
4214
|
+
flushSettledTransformWaiters();
|
|
4215
|
+
}
|
|
4216
|
+
function cancelTransformsUnder(prefix) {
|
|
4217
|
+
for (const [key, run] of [...transformRuns]) {
|
|
4218
|
+
const segs = paths.segmentsForPathKey(key);
|
|
4219
|
+
if (segs === null) continue;
|
|
4220
|
+
if (!paths.isPathPrefix(prefix, segs)) continue;
|
|
4221
|
+
releaseTransformRun(key, run);
|
|
4222
|
+
transformRuns.delete(key);
|
|
4223
|
+
transformErrors.delete(key);
|
|
4224
|
+
}
|
|
4225
|
+
flushSettledTransformWaiters();
|
|
4226
|
+
}
|
|
4227
|
+
function settleTransforms(path) {
|
|
4228
|
+
if (path === void 0) {
|
|
4229
|
+
if (activeTransforms.value === 0) return Promise.resolve();
|
|
4230
|
+
return new Promise((resolve) => {
|
|
4231
|
+
transformWaiters.push({ key: null, resolve });
|
|
4232
|
+
});
|
|
4233
|
+
}
|
|
4234
|
+
const { key } = paths.canonicalizePath(path);
|
|
4235
|
+
if ((fieldTransformCounts.get(key) ?? 0) === 0) return Promise.resolve();
|
|
4236
|
+
return new Promise((resolve) => {
|
|
4237
|
+
transformWaiters.push({ key, resolve });
|
|
4238
|
+
});
|
|
3891
4239
|
}
|
|
3892
4240
|
const initStamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3893
4241
|
diffAndApply({}, schemaInitialData, [], (patch) => {
|
|
@@ -3975,6 +4323,7 @@ function createFormStore(options) {
|
|
|
3975
4323
|
blankPaths,
|
|
3976
4324
|
originalBlankPaths,
|
|
3977
4325
|
fieldValidationCounts,
|
|
4326
|
+
fieldValidatingSince,
|
|
3978
4327
|
fieldValidationState,
|
|
3979
4328
|
schemaErrors,
|
|
3980
4329
|
activeValidations,
|
|
@@ -4037,6 +4386,7 @@ function createFormStore(options) {
|
|
|
4037
4386
|
}
|
|
4038
4387
|
}
|
|
4039
4388
|
}
|
|
4389
|
+
if (transformRuns.size !== 0) cancelTransformsUnder(path);
|
|
4040
4390
|
if (meta?.skipDiscriminatorReshape !== true) {
|
|
4041
4391
|
if (path.length > 0) {
|
|
4042
4392
|
const last = path[path.length - 1];
|
|
@@ -4242,7 +4592,7 @@ function createFormStore(options) {
|
|
|
4242
4592
|
prev.controller.abort();
|
|
4243
4593
|
}
|
|
4244
4594
|
const controller = new AbortController();
|
|
4245
|
-
const fresh = { controller, timer: null, settled: false };
|
|
4595
|
+
const fresh = { controller, timer: null, settled: false, released: false };
|
|
4246
4596
|
fieldValidationState.set(key, fresh);
|
|
4247
4597
|
const myEpoch = ++scheduleEpoch;
|
|
4248
4598
|
const run = () => {
|
|
@@ -4280,8 +4630,10 @@ function createFormStore(options) {
|
|
|
4280
4630
|
applySchemaErrorsForSubtree(scopePath ?? [], restamped);
|
|
4281
4631
|
}).catch(() => {
|
|
4282
4632
|
}).finally(() => {
|
|
4283
|
-
|
|
4284
|
-
|
|
4633
|
+
if (!fresh.released) {
|
|
4634
|
+
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
4635
|
+
decFieldValidation(key);
|
|
4636
|
+
}
|
|
4285
4637
|
fresh.settled = true;
|
|
4286
4638
|
});
|
|
4287
4639
|
};
|
|
@@ -4303,6 +4655,22 @@ function createFormStore(options) {
|
|
|
4303
4655
|
}
|
|
4304
4656
|
fieldValidationState.clear();
|
|
4305
4657
|
}
|
|
4658
|
+
function cancelFieldValidationUnder(prefix) {
|
|
4659
|
+
for (const [key, entry] of [...fieldValidationState]) {
|
|
4660
|
+
const segs = paths.segmentsForPathKey(key);
|
|
4661
|
+
if (segs === null) continue;
|
|
4662
|
+
if (!paths.isPathPrefix(prefix, segs)) continue;
|
|
4663
|
+
if (entry.timer !== null) {
|
|
4664
|
+
clearTimeout(entry.timer);
|
|
4665
|
+
} else if (!entry.settled && !entry.released) {
|
|
4666
|
+
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
4667
|
+
decFieldValidation(key);
|
|
4668
|
+
entry.released = true;
|
|
4669
|
+
}
|
|
4670
|
+
entry.controller.abort();
|
|
4671
|
+
fieldValidationState.delete(key);
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4306
4674
|
function onFormChange(listener) {
|
|
4307
4675
|
formChangeListeners.add(listener);
|
|
4308
4676
|
return () => {
|
|
@@ -4353,6 +4721,8 @@ function createFormStore(options) {
|
|
|
4353
4721
|
drainHooks.length = 0;
|
|
4354
4722
|
modules.clear();
|
|
4355
4723
|
cancelFieldValidation();
|
|
4724
|
+
cancelTransforms();
|
|
4725
|
+
fieldValidatingSince.clear();
|
|
4356
4726
|
formChangeListeners.clear();
|
|
4357
4727
|
submitSuccessListeners.clear();
|
|
4358
4728
|
resetListeners.clear();
|
|
@@ -4488,6 +4858,7 @@ function createFormStore(options) {
|
|
|
4488
4858
|
if (remaining === 0) {
|
|
4489
4859
|
elements.delete(key);
|
|
4490
4860
|
touchFieldRecord(key, path, { connected: false, focused: null, blurred: null });
|
|
4861
|
+
if (transformRuns.size !== 0) cancelTransformsUnder(path);
|
|
4491
4862
|
}
|
|
4492
4863
|
return remaining;
|
|
4493
4864
|
}
|
|
@@ -4699,16 +5070,7 @@ function createFormStore(options) {
|
|
|
4699
5070
|
}
|
|
4700
5071
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4701
5072
|
for (const [pathKey, record] of fields) {
|
|
4702
|
-
fields.set(pathKey,
|
|
4703
|
-
path: record.path,
|
|
4704
|
-
updatedAt: now,
|
|
4705
|
-
connected: record.connected,
|
|
4706
|
-
focused: record.focused,
|
|
4707
|
-
blurred: record.blurred,
|
|
4708
|
-
touched: false,
|
|
4709
|
-
interacted: false,
|
|
4710
|
-
blurredAfterInteraction: false
|
|
4711
|
-
});
|
|
5073
|
+
fields.set(pathKey, withClearedHistoryFlags(record, now));
|
|
4712
5074
|
}
|
|
4713
5075
|
submissionGeneration.value += 1;
|
|
4714
5076
|
submitting.value = false;
|
|
@@ -4718,6 +5080,9 @@ function createFormStore(options) {
|
|
|
4718
5080
|
submitError.value = null;
|
|
4719
5081
|
departAttempts.value = 0;
|
|
4720
5082
|
cancelFieldValidation();
|
|
5083
|
+
cancelTransforms();
|
|
5084
|
+
displayEngine.clear();
|
|
5085
|
+
fieldValidatingSince.clear();
|
|
4721
5086
|
pathSnapshots.clear();
|
|
4722
5087
|
scheduleEpoch = 0;
|
|
4723
5088
|
lastCommittedEpoch = 0;
|
|
@@ -4733,6 +5098,13 @@ function createFormStore(options) {
|
|
|
4733
5098
|
function resetField(path) {
|
|
4734
5099
|
const { key: targetKey, segments: targetSegments } = paths.canonicalizePath(path);
|
|
4735
5100
|
variantMemory.clearUnderPath(targetSegments);
|
|
5101
|
+
cancelFieldValidationUnder(targetSegments);
|
|
5102
|
+
cancelTransformsUnder(targetSegments);
|
|
5103
|
+
for (const [snapKey] of [...pathSnapshots]) {
|
|
5104
|
+
const segs = paths.segmentsForPathKey(snapKey);
|
|
5105
|
+
if (segs === null) continue;
|
|
5106
|
+
if (paths.isPathPrefix(targetSegments, segs)) pathSnapshots.delete(snapKey);
|
|
5107
|
+
}
|
|
4736
5108
|
const leafEntry = originals.get(targetKey);
|
|
4737
5109
|
if (leafEntry !== void 0) {
|
|
4738
5110
|
const wrote = setValueAtPath(targetSegments, leafEntry.value);
|
|
@@ -4782,16 +5154,7 @@ function createFormStore(options) {
|
|
|
4782
5154
|
function clearFieldRecordFlags(pathKey) {
|
|
4783
5155
|
const record = fields.get(pathKey);
|
|
4784
5156
|
if (record === void 0) return;
|
|
4785
|
-
fields.set(pathKey,
|
|
4786
|
-
path: record.path,
|
|
4787
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4788
|
-
connected: record.connected,
|
|
4789
|
-
focused: record.focused,
|
|
4790
|
-
blurred: record.blurred,
|
|
4791
|
-
touched: false,
|
|
4792
|
-
interacted: false,
|
|
4793
|
-
blurredAfterInteraction: false
|
|
4794
|
-
});
|
|
5157
|
+
fields.set(pathKey, withClearedHistoryFlags(record, (/* @__PURE__ */ new Date()).toISOString()));
|
|
4795
5158
|
}
|
|
4796
5159
|
function isPristineAtPath(path) {
|
|
4797
5160
|
const { key, segments } = paths.canonicalizePath(path);
|
|
@@ -4871,6 +5234,12 @@ function createFormStore(options) {
|
|
|
4871
5234
|
pathHasAsyncValidation,
|
|
4872
5235
|
pathHasAsyncValidationByKey,
|
|
4873
5236
|
fieldValidationCounts,
|
|
5237
|
+
fieldValidatingSince,
|
|
5238
|
+
fieldTransformCounts,
|
|
5239
|
+
fieldTransformingSince,
|
|
5240
|
+
transformErrors,
|
|
5241
|
+
activeTransforms,
|
|
5242
|
+
displayEngine,
|
|
4874
5243
|
applyFormReplacement,
|
|
4875
5244
|
setValueAtPath,
|
|
4876
5245
|
getValueAtPath,
|
|
@@ -4899,6 +5268,13 @@ function createFormStore(options) {
|
|
|
4899
5268
|
getOriginalAtPath,
|
|
4900
5269
|
getFirstErrorElement,
|
|
4901
5270
|
cancelFieldValidation,
|
|
5271
|
+
beginTransform,
|
|
5272
|
+
isCurrentTransform,
|
|
5273
|
+
endTransform,
|
|
5274
|
+
setTransformError,
|
|
5275
|
+
cancelTransforms,
|
|
5276
|
+
cancelTransformsUnder,
|
|
5277
|
+
settleTransforms,
|
|
4902
5278
|
scheduleFieldValidation,
|
|
4903
5279
|
onFormChange,
|
|
4904
5280
|
onSubmitSuccess,
|
|
@@ -4955,7 +5331,7 @@ function errorsEqual(a, b) {
|
|
|
4955
5331
|
}
|
|
4956
5332
|
return true;
|
|
4957
5333
|
}
|
|
4958
|
-
function diffBlankPaths
|
|
5334
|
+
function diffBlankPaths(prev, curr) {
|
|
4959
5335
|
const added = [];
|
|
4960
5336
|
const removed = [];
|
|
4961
5337
|
for (const k of curr) if (!prev.has(k)) added.push(k);
|
|
@@ -5043,7 +5419,7 @@ function createHistoryModule(state, config) {
|
|
|
5043
5419
|
diffAndApply(prevSnap.form, newSnap.form, [], (p) => formPatches.push(p));
|
|
5044
5420
|
const prevBlankSet = new Set(prevSnap.blankPaths);
|
|
5045
5421
|
const currBlankSet = new Set(newSnap.blankPaths);
|
|
5046
|
-
const blankDiff = diffBlankPaths
|
|
5422
|
+
const blankDiff = diffBlankPaths(prevBlankSet, currBlankSet);
|
|
5047
5423
|
const delta = {
|
|
5048
5424
|
formPatches,
|
|
5049
5425
|
blankPathsAdded: blankDiff.added,
|
|
@@ -5110,643 +5486,6 @@ function createHistoryModule(state, config) {
|
|
|
5110
5486
|
};
|
|
5111
5487
|
}
|
|
5112
5488
|
|
|
5113
|
-
function wirePersistence(state, config) {
|
|
5114
|
-
let fingerprint;
|
|
5115
|
-
try {
|
|
5116
|
-
fingerprint = hashStableString(state.schema.fingerprint());
|
|
5117
|
-
} catch (err) {
|
|
5118
|
-
if (paths.__DEV__) {
|
|
5119
|
-
console.warn(
|
|
5120
|
-
`[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.`
|
|
5121
|
-
);
|
|
5122
|
-
}
|
|
5123
|
-
fingerprint = "unfingerprinted";
|
|
5124
|
-
}
|
|
5125
|
-
const base = resolveStorageKeyBase(config, state.formKey);
|
|
5126
|
-
const key = `${base}:${fingerprint}`;
|
|
5127
|
-
const debounceMs = normalizeNumericOption({
|
|
5128
|
-
value: config.debounceMs ?? DEFAULT_PERSISTENCE_DEBOUNCE_MS,
|
|
5129
|
-
source: "useForm.persist.debounceMs",
|
|
5130
|
-
allowInfinity: false,
|
|
5131
|
-
min: 0,
|
|
5132
|
-
defaultValue: DEFAULT_PERSISTENCE_DEBOUNCE_MS
|
|
5133
|
-
});
|
|
5134
|
-
const include = config.include ?? "form";
|
|
5135
|
-
const clearOnSubmitSuccess = config.clearOnSubmitSuccess ?? true;
|
|
5136
|
-
const adapterPromise = getStorageAdapter(config.storage);
|
|
5137
|
-
let disposed = false;
|
|
5138
|
-
const isDisposed = () => disposed;
|
|
5139
|
-
let inFlightFinalFlush = null;
|
|
5140
|
-
let pendingOptedInPaths = null;
|
|
5141
|
-
const writer = createDebouncedWriter(async () => {
|
|
5142
|
-
const optedInPaths = pendingOptedInPaths ?? new Set(state.persistOptIns.optedInPaths());
|
|
5143
|
-
pendingOptedInPaths = null;
|
|
5144
|
-
const adapter = await adapterPromise;
|
|
5145
|
-
if (isDisposed()) return;
|
|
5146
|
-
if (optedInPaths.size === 0) {
|
|
5147
|
-
await adapter.removeItem(key);
|
|
5148
|
-
return;
|
|
5149
|
-
}
|
|
5150
|
-
const rawForm = vue.toRaw(state.form.value);
|
|
5151
|
-
const filteredForm = stripUnacknowledgedSensitiveLeaves(
|
|
5152
|
-
pluckPaths(rawForm, optedInPaths),
|
|
5153
|
-
optedInPaths,
|
|
5154
|
-
state.isSensitivePath
|
|
5155
|
-
);
|
|
5156
|
-
const filteredSchemaErrors = filterErrorsByPaths(state.schemaErrors, optedInPaths);
|
|
5157
|
-
const filteredUserErrors = filterErrorsByPaths(state.userErrors, optedInPaths);
|
|
5158
|
-
const filteredTransientEmpty = /* @__PURE__ */ new Set();
|
|
5159
|
-
for (const tk of state.blankPaths) {
|
|
5160
|
-
if (optedInPaths.has(tk)) filteredTransientEmpty.add(tk);
|
|
5161
|
-
}
|
|
5162
|
-
const payload = buildPersistedPayload(
|
|
5163
|
-
filteredForm,
|
|
5164
|
-
include,
|
|
5165
|
-
filteredSchemaErrors,
|
|
5166
|
-
filteredUserErrors,
|
|
5167
|
-
filteredTransientEmpty
|
|
5168
|
-
);
|
|
5169
|
-
await adapter.setItem(key, payload);
|
|
5170
|
-
}, debounceMs);
|
|
5171
|
-
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
5172
|
-
if (isDisposed() || inFlightFinalFlush !== null) return;
|
|
5173
|
-
if (meta?.crossTab === true) return;
|
|
5174
|
-
if (meta?.persist !== true) return;
|
|
5175
|
-
pendingOptedInPaths = new Set(state.persistOptIns.optedInPaths());
|
|
5176
|
-
writer.schedule();
|
|
5177
|
-
});
|
|
5178
|
-
const unsubscribeSuccess = clearOnSubmitSuccess ? state.onSubmitSuccess(() => {
|
|
5179
|
-
if (isDisposed()) return;
|
|
5180
|
-
void (async () => {
|
|
5181
|
-
await writer.flush();
|
|
5182
|
-
if (isDisposed()) return;
|
|
5183
|
-
const adapter = await adapterPromise;
|
|
5184
|
-
if (isDisposed()) return;
|
|
5185
|
-
await adapter.removeItem(key);
|
|
5186
|
-
})();
|
|
5187
|
-
}) : () => void 0;
|
|
5188
|
-
const handlePageHide = () => {
|
|
5189
|
-
if (isDisposed()) return;
|
|
5190
|
-
void writer.flush();
|
|
5191
|
-
};
|
|
5192
|
-
if (typeof window !== "undefined") {
|
|
5193
|
-
window.addEventListener("pagehide", handlePageHide);
|
|
5194
|
-
}
|
|
5195
|
-
void (async () => {
|
|
5196
|
-
const adapter = await adapterPromise;
|
|
5197
|
-
if (isDisposed()) return;
|
|
5198
|
-
void cleanupOrphanKeys(adapter, base, key);
|
|
5199
|
-
try {
|
|
5200
|
-
const raw = await adapter.getItem(key);
|
|
5201
|
-
const payload = readPersistedPayload(raw);
|
|
5202
|
-
if (payload === null) {
|
|
5203
|
-
if (raw !== null && raw !== void 0) {
|
|
5204
|
-
await adapter.removeItem(key);
|
|
5205
|
-
}
|
|
5206
|
-
return;
|
|
5207
|
-
}
|
|
5208
|
-
if (isDisposed()) return;
|
|
5209
|
-
const merged = mergeSparseHydration(
|
|
5210
|
-
vue.toRaw(state.form.value),
|
|
5211
|
-
payload.data.form,
|
|
5212
|
-
state.schema
|
|
5213
|
-
);
|
|
5214
|
-
state.applyFormReplacement(merged, { hydration: true });
|
|
5215
|
-
const persistedLeafPaths = collectPersistedLeafPaths(payload.data.form);
|
|
5216
|
-
for (const k of persistedLeafPaths) {
|
|
5217
|
-
state.blankPaths.delete(k);
|
|
5218
|
-
state.originalBlankPaths.delete(k);
|
|
5219
|
-
}
|
|
5220
|
-
for (const k of payload.data.blankPaths ?? []) {
|
|
5221
|
-
state.blankPaths.add(k);
|
|
5222
|
-
state.originalBlankPaths.add(k);
|
|
5223
|
-
}
|
|
5224
|
-
if (include === "form+errors") {
|
|
5225
|
-
if (payload.data.schemaErrors !== void 0) {
|
|
5226
|
-
const flat = payload.data.schemaErrors.flatMap(([, errs]) => errs);
|
|
5227
|
-
state.setAllSchemaErrors(flat);
|
|
5228
|
-
}
|
|
5229
|
-
if (payload.data.userErrors !== void 0) {
|
|
5230
|
-
const flat = payload.data.userErrors.flatMap(([, errs]) => errs);
|
|
5231
|
-
state.setAllUserErrors(flat);
|
|
5232
|
-
}
|
|
5233
|
-
}
|
|
5234
|
-
state.scheduleFieldValidation(
|
|
5235
|
-
[],
|
|
5236
|
-
true
|
|
5237
|
-
/* immediate */
|
|
5238
|
-
);
|
|
5239
|
-
} catch {
|
|
5240
|
-
}
|
|
5241
|
-
})();
|
|
5242
|
-
async function writePathImmediately(path) {
|
|
5243
|
-
if (isDisposed()) return;
|
|
5244
|
-
await writer.flush();
|
|
5245
|
-
if (isDisposed()) return;
|
|
5246
|
-
const adapter = await adapterPromise;
|
|
5247
|
-
if (isDisposed()) return;
|
|
5248
|
-
const raw = await adapter.getItem(key);
|
|
5249
|
-
const existing = readPersistedPayload(raw);
|
|
5250
|
-
const value = getAtPath(vue.toRaw(state.form.value), path);
|
|
5251
|
-
const nextForm = setAtPath(existing?.data.form ?? {}, path, value);
|
|
5252
|
-
const { key: pathKey } = paths.canonicalizePath(path);
|
|
5253
|
-
const transientSet = new Set(
|
|
5254
|
-
(existing?.data.blankPaths ?? []).filter(
|
|
5255
|
-
(k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
|
|
5256
|
-
)
|
|
5257
|
-
);
|
|
5258
|
-
for (const liveKey of state.blankPaths) {
|
|
5259
|
-
if (liveKey === pathKey || isDescendantPathKey(liveKey, pathKey)) {
|
|
5260
|
-
transientSet.add(liveKey);
|
|
5261
|
-
}
|
|
5262
|
-
}
|
|
5263
|
-
if (include === "form") {
|
|
5264
|
-
await adapter.setItem(
|
|
5265
|
-
key,
|
|
5266
|
-
buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
|
|
5267
|
-
);
|
|
5268
|
-
return;
|
|
5269
|
-
}
|
|
5270
|
-
const schemaMap = new Map(existing?.data.schemaErrors ?? []);
|
|
5271
|
-
const userMap = new Map(existing?.data.userErrors ?? []);
|
|
5272
|
-
const currentSchema = state.schemaErrors.get(pathKey);
|
|
5273
|
-
const currentUser = state.userErrors.get(pathKey);
|
|
5274
|
-
if (currentSchema !== void 0 && currentSchema.length > 0) {
|
|
5275
|
-
schemaMap.set(pathKey, [...currentSchema]);
|
|
5276
|
-
} else {
|
|
5277
|
-
schemaMap.delete(pathKey);
|
|
5278
|
-
}
|
|
5279
|
-
if (currentUser !== void 0 && currentUser.length > 0) {
|
|
5280
|
-
userMap.set(pathKey, [...currentUser]);
|
|
5281
|
-
} else {
|
|
5282
|
-
userMap.delete(pathKey);
|
|
5283
|
-
}
|
|
5284
|
-
await adapter.setItem(
|
|
5285
|
-
key,
|
|
5286
|
-
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5287
|
-
);
|
|
5288
|
-
}
|
|
5289
|
-
async function clearPersistedDraft(path) {
|
|
5290
|
-
if (isDisposed()) return;
|
|
5291
|
-
await writer.flush();
|
|
5292
|
-
if (isDisposed()) return;
|
|
5293
|
-
const adapter = await adapterPromise;
|
|
5294
|
-
if (isDisposed()) return;
|
|
5295
|
-
if (path === void 0) {
|
|
5296
|
-
await adapter.removeItem(key);
|
|
5297
|
-
return;
|
|
5298
|
-
}
|
|
5299
|
-
const raw = await adapter.getItem(key);
|
|
5300
|
-
const existing = readPersistedPayload(raw);
|
|
5301
|
-
if (existing === null) return;
|
|
5302
|
-
const nextForm = deleteAtPath(existing.data.form, path);
|
|
5303
|
-
if (isEmptyContainer(nextForm)) {
|
|
5304
|
-
await adapter.removeItem(key);
|
|
5305
|
-
return;
|
|
5306
|
-
}
|
|
5307
|
-
const { key: pathKey } = paths.canonicalizePath(path);
|
|
5308
|
-
const transientSet = new Set(
|
|
5309
|
-
(existing.data.blankPaths ?? []).filter(
|
|
5310
|
-
(k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
|
|
5311
|
-
)
|
|
5312
|
-
);
|
|
5313
|
-
if (include === "form") {
|
|
5314
|
-
await adapter.setItem(
|
|
5315
|
-
key,
|
|
5316
|
-
buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
|
|
5317
|
-
);
|
|
5318
|
-
return;
|
|
5319
|
-
}
|
|
5320
|
-
const schemaErrors = (existing.data.schemaErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5321
|
-
const userErrors = (existing.data.userErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5322
|
-
const schemaMap = new Map(schemaErrors.map(([k, v]) => [k, [...v]]));
|
|
5323
|
-
const userMap = new Map(userErrors.map(([k, v]) => [k, [...v]]));
|
|
5324
|
-
await adapter.setItem(
|
|
5325
|
-
key,
|
|
5326
|
-
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5327
|
-
);
|
|
5328
|
-
}
|
|
5329
|
-
function awaitPendingWrites() {
|
|
5330
|
-
if (inFlightFinalFlush !== null) return inFlightFinalFlush;
|
|
5331
|
-
if (isDisposed()) return Promise.resolve();
|
|
5332
|
-
return writer.flush().catch(() => void 0);
|
|
5333
|
-
}
|
|
5334
|
-
function dispose() {
|
|
5335
|
-
if (isDisposed() || inFlightFinalFlush !== null) return;
|
|
5336
|
-
unsubscribeChange();
|
|
5337
|
-
unsubscribeSuccess();
|
|
5338
|
-
if (typeof window !== "undefined") {
|
|
5339
|
-
window.removeEventListener("pagehide", handlePageHide);
|
|
5340
|
-
}
|
|
5341
|
-
inFlightFinalFlush = writer.flush().catch(() => void 0).finally(() => {
|
|
5342
|
-
disposed = true;
|
|
5343
|
-
inFlightFinalFlush = null;
|
|
5344
|
-
});
|
|
5345
|
-
}
|
|
5346
|
-
return {
|
|
5347
|
-
wiredConfig: config,
|
|
5348
|
-
writePathImmediately,
|
|
5349
|
-
clearPersistedDraft,
|
|
5350
|
-
awaitPendingWrites,
|
|
5351
|
-
dispose
|
|
5352
|
-
};
|
|
5353
|
-
}
|
|
5354
|
-
function isEmptyContainer(value) {
|
|
5355
|
-
if (value === void 0 || value === null) return true;
|
|
5356
|
-
if (Array.isArray(value)) return value.length === 0;
|
|
5357
|
-
if (isPlainRecord(value)) return Object.keys(value).length === 0;
|
|
5358
|
-
return false;
|
|
5359
|
-
}
|
|
5360
|
-
function collectPersistedLeafPaths(form) {
|
|
5361
|
-
const out = [];
|
|
5362
|
-
walk(form, []);
|
|
5363
|
-
return out;
|
|
5364
|
-
function walk(node, prefix) {
|
|
5365
|
-
if (Array.isArray(node)) {
|
|
5366
|
-
for (let i = 0; i < node.length; i++) {
|
|
5367
|
-
walk(node[i], [...prefix, i]);
|
|
5368
|
-
}
|
|
5369
|
-
return;
|
|
5370
|
-
}
|
|
5371
|
-
if (isPlainRecord(node)) {
|
|
5372
|
-
for (const key of Object.keys(node)) {
|
|
5373
|
-
walk(node[key], [...prefix, key]);
|
|
5374
|
-
}
|
|
5375
|
-
return;
|
|
5376
|
-
}
|
|
5377
|
-
if (prefix.length === 0) return;
|
|
5378
|
-
out.push(paths.canonicalizePath(prefix).key);
|
|
5379
|
-
}
|
|
5380
|
-
}
|
|
5381
|
-
function isDescendantPathKey(candidate, ancestor) {
|
|
5382
|
-
if (candidate.length <= ancestor.length) return false;
|
|
5383
|
-
if (!ancestor.endsWith("]")) return false;
|
|
5384
|
-
const childPrefix = `${ancestor.slice(0, -1)},`;
|
|
5385
|
-
return candidate.startsWith(childPrefix);
|
|
5386
|
-
}
|
|
5387
|
-
|
|
5388
|
-
const PROTOCOL_VERSION = 1;
|
|
5389
|
-
const JOIN_COLLECTION_WINDOW_MS = 50;
|
|
5390
|
-
const SNAPSHOT_TIMEOUT_MS = 200;
|
|
5391
|
-
const MAX_LEADER_ATTEMPTS = 3;
|
|
5392
|
-
const SNAPSHOT_RESPONSE_MIN_INTERVAL_MS = 500;
|
|
5393
|
-
function isFileLikeValue(value) {
|
|
5394
|
-
if (typeof File !== "undefined" && value instanceof File) return true;
|
|
5395
|
-
if (typeof Blob !== "undefined" && value instanceof Blob) return true;
|
|
5396
|
-
return false;
|
|
5397
|
-
}
|
|
5398
|
-
function isInboundShapeAcceptable(schema, path, value) {
|
|
5399
|
-
if (isFileLikeValue(value)) return false;
|
|
5400
|
-
let kind;
|
|
5401
|
-
if (Array.isArray(value)) {
|
|
5402
|
-
kind = "array";
|
|
5403
|
-
} else if (value !== null && typeof value === "object" && isPlainRecord(value)) {
|
|
5404
|
-
kind = "object";
|
|
5405
|
-
} else {
|
|
5406
|
-
kind = slimKindOf(value);
|
|
5407
|
-
}
|
|
5408
|
-
const accepted = schema.getSlimPrimitiveTypesAtPath(path);
|
|
5409
|
-
if (!accepted.has(kind)) return false;
|
|
5410
|
-
if (Array.isArray(value)) {
|
|
5411
|
-
for (let i = 0; i < value.length; i++) {
|
|
5412
|
-
if (!isInboundShapeAcceptable(schema, [...path, i], value[i])) return false;
|
|
5413
|
-
}
|
|
5414
|
-
return true;
|
|
5415
|
-
}
|
|
5416
|
-
if (isPlainRecord(value)) {
|
|
5417
|
-
for (const key of Object.keys(value)) {
|
|
5418
|
-
if (!isInboundShapeAcceptable(schema, [...path, key], value[key])) return false;
|
|
5419
|
-
}
|
|
5420
|
-
return true;
|
|
5421
|
-
}
|
|
5422
|
-
return true;
|
|
5423
|
-
}
|
|
5424
|
-
function diffBlankPaths(prev, curr) {
|
|
5425
|
-
const added = [];
|
|
5426
|
-
const removed = [];
|
|
5427
|
-
const prevSet = new Set(prev);
|
|
5428
|
-
for (const k of curr) if (!prevSet.has(k)) added.push(k);
|
|
5429
|
-
for (const k of prev) if (!curr.has(k)) removed.push(k);
|
|
5430
|
-
return { added, removed };
|
|
5431
|
-
}
|
|
5432
|
-
function snapshotForm(form) {
|
|
5433
|
-
return structuralSnapshot(form);
|
|
5434
|
-
}
|
|
5435
|
-
function stripSensitivePathsDeep(value, pathSoFar, isSensitivePath) {
|
|
5436
|
-
if (isFileLikeValue(value)) return void 0;
|
|
5437
|
-
if (value === null || typeof value !== "object") return value;
|
|
5438
|
-
if (Array.isArray(value)) {
|
|
5439
|
-
return value.map((item, i) => stripSensitivePathsDeep(item, [...pathSoFar, i], isSensitivePath));
|
|
5440
|
-
}
|
|
5441
|
-
const proto = Object.getPrototypeOf(value);
|
|
5442
|
-
if (proto !== Object.prototype && proto !== null) return value;
|
|
5443
|
-
const out = {};
|
|
5444
|
-
const src = value;
|
|
5445
|
-
for (const key of Object.keys(src)) {
|
|
5446
|
-
const childPath = [...pathSoFar, key];
|
|
5447
|
-
if (isSensitivePath(childPath)) {
|
|
5448
|
-
safeAssign(out, key, void 0);
|
|
5449
|
-
continue;
|
|
5450
|
-
}
|
|
5451
|
-
safeAssign(out, key, stripSensitivePathsDeep(src[key], childPath, isSensitivePath));
|
|
5452
|
-
}
|
|
5453
|
-
return out;
|
|
5454
|
-
}
|
|
5455
|
-
function isStringArray(value) {
|
|
5456
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
5457
|
-
}
|
|
5458
|
-
function isPatchArray(value) {
|
|
5459
|
-
return Array.isArray(value) && value.every(
|
|
5460
|
-
(p) => p !== null && typeof p === "object" && Array.isArray(p.path)
|
|
5461
|
-
);
|
|
5462
|
-
}
|
|
5463
|
-
function isValidSyncMessage(data) {
|
|
5464
|
-
if (data === null || typeof data !== "object") return false;
|
|
5465
|
-
const m = data;
|
|
5466
|
-
if (m["v"] !== PROTOCOL_VERSION) return false;
|
|
5467
|
-
if (typeof m["senderId"] !== "string") return false;
|
|
5468
|
-
if (typeof m["kind"] !== "string") return false;
|
|
5469
|
-
switch (m["kind"]) {
|
|
5470
|
-
case "hello":
|
|
5471
|
-
case "announce":
|
|
5472
|
-
return true;
|
|
5473
|
-
case "requestSnapshot":
|
|
5474
|
-
return typeof m["targetId"] === "string";
|
|
5475
|
-
case "snapshot":
|
|
5476
|
-
return isStringArray(m["blankPaths"]) && "form" in m;
|
|
5477
|
-
case "patches":
|
|
5478
|
-
return isPatchArray(m["formPatches"]) && isStringArray(m["blankPathsAdded"]) && isStringArray(m["blankPathsRemoved"]);
|
|
5479
|
-
default:
|
|
5480
|
-
return false;
|
|
5481
|
-
}
|
|
5482
|
-
}
|
|
5483
|
-
function generateSenderId() {
|
|
5484
|
-
try {
|
|
5485
|
-
return globalThis.crypto.randomUUID();
|
|
5486
|
-
} catch {
|
|
5487
|
-
return `atta-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;
|
|
5488
|
-
}
|
|
5489
|
-
}
|
|
5490
|
-
function createMultiTabSyncModule(state, channelName, options) {
|
|
5491
|
-
if (typeof BroadcastChannel === "undefined") {
|
|
5492
|
-
return {
|
|
5493
|
-
dispose: () => void 0,
|
|
5494
|
-
lifecycle: () => "established",
|
|
5495
|
-
senderId: "",
|
|
5496
|
-
channelName
|
|
5497
|
-
};
|
|
5498
|
-
}
|
|
5499
|
-
let channel;
|
|
5500
|
-
try {
|
|
5501
|
-
channel = new BroadcastChannel(channelName);
|
|
5502
|
-
} catch {
|
|
5503
|
-
return {
|
|
5504
|
-
dispose: () => void 0,
|
|
5505
|
-
lifecycle: () => "established",
|
|
5506
|
-
senderId: "",
|
|
5507
|
-
channelName
|
|
5508
|
-
};
|
|
5509
|
-
}
|
|
5510
|
-
const senderId = generateSenderId();
|
|
5511
|
-
let lifecycle = "joining";
|
|
5512
|
-
let disposed = false;
|
|
5513
|
-
const peerIds = /* @__PURE__ */ new Set();
|
|
5514
|
-
const lastSnapshotResponseAt = /* @__PURE__ */ new Map();
|
|
5515
|
-
let joinCollectionTimer = null;
|
|
5516
|
-
let snapshotTimeoutTimer = null;
|
|
5517
|
-
let leaderAttempts = 0;
|
|
5518
|
-
let prior = {
|
|
5519
|
-
form: snapshotForm(state.form.value),
|
|
5520
|
-
blankPathsSnapshot: [...state.blankPaths]
|
|
5521
|
-
};
|
|
5522
|
-
function safePost(msg) {
|
|
5523
|
-
if (disposed) return;
|
|
5524
|
-
try {
|
|
5525
|
-
channel.postMessage(msg);
|
|
5526
|
-
} catch {
|
|
5527
|
-
}
|
|
5528
|
-
}
|
|
5529
|
-
function refreshPrior() {
|
|
5530
|
-
prior = {
|
|
5531
|
-
form: snapshotForm(state.form.value),
|
|
5532
|
-
blankPathsSnapshot: [...state.blankPaths]
|
|
5533
|
-
};
|
|
5534
|
-
}
|
|
5535
|
-
function isPathLocallySuppressed(path) {
|
|
5536
|
-
if (options.isSensitivePath(path)) return true;
|
|
5537
|
-
const { key } = paths.canonicalizePath([...path]);
|
|
5538
|
-
if (options.noSyncPaths.has(key)) return true;
|
|
5539
|
-
return false;
|
|
5540
|
-
}
|
|
5541
|
-
function postPatches() {
|
|
5542
|
-
if (lifecycle !== "established") return;
|
|
5543
|
-
const next = snapshotForm(state.form.value);
|
|
5544
|
-
const rawPatches = [];
|
|
5545
|
-
diffAndApply(prior.form, next, [], (p) => rawPatches.push(p));
|
|
5546
|
-
const safePatches = [];
|
|
5547
|
-
for (const p of rawPatches) {
|
|
5548
|
-
if (isPathLocallySuppressed(p.path)) continue;
|
|
5549
|
-
if ("value" in p && isFileLikeValue(p.value)) continue;
|
|
5550
|
-
safePatches.push(p);
|
|
5551
|
-
}
|
|
5552
|
-
const { added, removed } = diffBlankPaths(prior.blankPathsSnapshot, state.blankPaths);
|
|
5553
|
-
if (safePatches.length === 0 && added.length === 0 && removed.length === 0) {
|
|
5554
|
-
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
5555
|
-
return;
|
|
5556
|
-
}
|
|
5557
|
-
safePost({
|
|
5558
|
-
v: PROTOCOL_VERSION,
|
|
5559
|
-
kind: "patches",
|
|
5560
|
-
senderId,
|
|
5561
|
-
formPatches: safePatches,
|
|
5562
|
-
blankPathsAdded: added,
|
|
5563
|
-
blankPathsRemoved: removed
|
|
5564
|
-
});
|
|
5565
|
-
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
5566
|
-
}
|
|
5567
|
-
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
5568
|
-
if (disposed) return;
|
|
5569
|
-
if (lifecycle !== "established") return;
|
|
5570
|
-
if (meta?.crossTab === true) return;
|
|
5571
|
-
if (meta?.hydration === true) {
|
|
5572
|
-
refreshPrior();
|
|
5573
|
-
return;
|
|
5574
|
-
}
|
|
5575
|
-
postPatches();
|
|
5576
|
-
});
|
|
5577
|
-
function applyIncomingForm(form, blankPaths) {
|
|
5578
|
-
state.blankPaths.clear();
|
|
5579
|
-
for (const k of blankPaths) state.blankPaths.add(k);
|
|
5580
|
-
state.applyFormReplacement(form, { crossTab: true, persist: false });
|
|
5581
|
-
refreshPrior();
|
|
5582
|
-
}
|
|
5583
|
-
function handlePatches(msg) {
|
|
5584
|
-
if (lifecycle !== "established") return;
|
|
5585
|
-
const safePatches = [];
|
|
5586
|
-
for (const p of msg.formPatches) {
|
|
5587
|
-
if (!Array.isArray(p.path)) continue;
|
|
5588
|
-
if (isPathLocallySuppressed(p.path)) continue;
|
|
5589
|
-
if ("value" in p && !isInboundShapeAcceptable(state.schema, p.path, p.value)) {
|
|
5590
|
-
continue;
|
|
5591
|
-
}
|
|
5592
|
-
safePatches.push(p);
|
|
5593
|
-
}
|
|
5594
|
-
const safeBlankAdded = [];
|
|
5595
|
-
for (const k of msg.blankPathsAdded) {
|
|
5596
|
-
const segs = paths.canonicalizePath(k).segments;
|
|
5597
|
-
if (isPathLocallySuppressed(segs)) continue;
|
|
5598
|
-
safeBlankAdded.push(k);
|
|
5599
|
-
}
|
|
5600
|
-
const safeBlankRemoved = [];
|
|
5601
|
-
for (const k of msg.blankPathsRemoved) {
|
|
5602
|
-
const segs = paths.canonicalizePath(k).segments;
|
|
5603
|
-
if (isPathLocallySuppressed(segs)) continue;
|
|
5604
|
-
safeBlankRemoved.push(k);
|
|
5605
|
-
}
|
|
5606
|
-
if (safePatches.length === 0 && safeBlankAdded.length === 0 && safeBlankRemoved.length === 0) {
|
|
5607
|
-
return;
|
|
5608
|
-
}
|
|
5609
|
-
const candidate = applyPatchesForward(state.form.value, safePatches);
|
|
5610
|
-
try {
|
|
5611
|
-
options.validateForm(state.form.value);
|
|
5612
|
-
try {
|
|
5613
|
-
options.validateForm(candidate);
|
|
5614
|
-
} catch {
|
|
5615
|
-
return;
|
|
5616
|
-
}
|
|
5617
|
-
} catch {
|
|
5618
|
-
}
|
|
5619
|
-
const nextBlankPaths = new Set(state.blankPaths);
|
|
5620
|
-
for (const k of safeBlankRemoved) nextBlankPaths.delete(k);
|
|
5621
|
-
for (const k of safeBlankAdded) nextBlankPaths.add(k);
|
|
5622
|
-
applyIncomingForm(candidate, [...nextBlankPaths]);
|
|
5623
|
-
}
|
|
5624
|
-
function handleSnapshot(msg) {
|
|
5625
|
-
if (lifecycle !== "joining") return;
|
|
5626
|
-
if (!isInboundShapeAcceptable(state.schema, [], msg.form)) return;
|
|
5627
|
-
if (snapshotTimeoutTimer !== null) {
|
|
5628
|
-
clearTimeout(snapshotTimeoutTimer);
|
|
5629
|
-
snapshotTimeoutTimer = null;
|
|
5630
|
-
}
|
|
5631
|
-
if (joinCollectionTimer !== null) {
|
|
5632
|
-
clearTimeout(joinCollectionTimer);
|
|
5633
|
-
joinCollectionTimer = null;
|
|
5634
|
-
}
|
|
5635
|
-
applyIncomingForm(msg.form, msg.blankPaths);
|
|
5636
|
-
lifecycle = "established";
|
|
5637
|
-
peerIds.clear();
|
|
5638
|
-
}
|
|
5639
|
-
function respondToHello() {
|
|
5640
|
-
safePost({ v: PROTOCOL_VERSION, kind: "announce", senderId });
|
|
5641
|
-
}
|
|
5642
|
-
function respondToSnapshotRequest(requesterId) {
|
|
5643
|
-
const now = Date.now();
|
|
5644
|
-
const last = lastSnapshotResponseAt.get(requesterId);
|
|
5645
|
-
if (last !== void 0 && now - last < SNAPSHOT_RESPONSE_MIN_INTERVAL_MS) return;
|
|
5646
|
-
lastSnapshotResponseAt.set(requesterId, now);
|
|
5647
|
-
const scrubbedForm = stripSensitivePathsDeep(state.form.value, [], options.isSensitivePath);
|
|
5648
|
-
safePost({
|
|
5649
|
-
v: PROTOCOL_VERSION,
|
|
5650
|
-
kind: "snapshot",
|
|
5651
|
-
senderId,
|
|
5652
|
-
form: scrubbedForm,
|
|
5653
|
-
blankPaths: [...state.blankPaths]
|
|
5654
|
-
});
|
|
5655
|
-
}
|
|
5656
|
-
channel.onmessage = (event) => {
|
|
5657
|
-
if (disposed) return;
|
|
5658
|
-
try {
|
|
5659
|
-
const data = event.data;
|
|
5660
|
-
if (!isValidSyncMessage(data)) return;
|
|
5661
|
-
const msg = data;
|
|
5662
|
-
if (msg.senderId === senderId) return;
|
|
5663
|
-
switch (msg.kind) {
|
|
5664
|
-
case "hello":
|
|
5665
|
-
if (lifecycle !== "established") return;
|
|
5666
|
-
respondToHello();
|
|
5667
|
-
break;
|
|
5668
|
-
case "announce":
|
|
5669
|
-
if (lifecycle === "joining") peerIds.add(msg.senderId);
|
|
5670
|
-
break;
|
|
5671
|
-
case "requestSnapshot":
|
|
5672
|
-
if (lifecycle !== "established") return;
|
|
5673
|
-
if (msg.targetId !== senderId) return;
|
|
5674
|
-
respondToSnapshotRequest(msg.senderId);
|
|
5675
|
-
break;
|
|
5676
|
-
case "snapshot":
|
|
5677
|
-
handleSnapshot(msg);
|
|
5678
|
-
break;
|
|
5679
|
-
case "patches":
|
|
5680
|
-
handlePatches(msg);
|
|
5681
|
-
break;
|
|
5682
|
-
}
|
|
5683
|
-
} catch {
|
|
5684
|
-
}
|
|
5685
|
-
};
|
|
5686
|
-
function electLeaderAndRequest() {
|
|
5687
|
-
if (disposed) return;
|
|
5688
|
-
if (peerIds.size === 0) {
|
|
5689
|
-
lifecycle = "established";
|
|
5690
|
-
refreshPrior();
|
|
5691
|
-
return;
|
|
5692
|
-
}
|
|
5693
|
-
const sorted = [...peerIds].sort();
|
|
5694
|
-
const leaderId = sorted[0];
|
|
5695
|
-
peerIds.delete(leaderId);
|
|
5696
|
-
leaderAttempts++;
|
|
5697
|
-
safePost({
|
|
5698
|
-
v: PROTOCOL_VERSION,
|
|
5699
|
-
kind: "requestSnapshot",
|
|
5700
|
-
senderId,
|
|
5701
|
-
targetId: leaderId
|
|
5702
|
-
});
|
|
5703
|
-
snapshotTimeoutTimer = setTimeout(() => {
|
|
5704
|
-
snapshotTimeoutTimer = null;
|
|
5705
|
-
if (disposed) return;
|
|
5706
|
-
if (lifecycle === "established") return;
|
|
5707
|
-
if (leaderAttempts >= MAX_LEADER_ATTEMPTS || peerIds.size === 0) {
|
|
5708
|
-
lifecycle = "established";
|
|
5709
|
-
refreshPrior();
|
|
5710
|
-
return;
|
|
5711
|
-
}
|
|
5712
|
-
electLeaderAndRequest();
|
|
5713
|
-
}, SNAPSHOT_TIMEOUT_MS);
|
|
5714
|
-
}
|
|
5715
|
-
function joinFlow() {
|
|
5716
|
-
safePost({ v: PROTOCOL_VERSION, kind: "hello", senderId });
|
|
5717
|
-
joinCollectionTimer = setTimeout(() => {
|
|
5718
|
-
joinCollectionTimer = null;
|
|
5719
|
-
if (disposed) return;
|
|
5720
|
-
if (lifecycle === "established") return;
|
|
5721
|
-
electLeaderAndRequest();
|
|
5722
|
-
}, JOIN_COLLECTION_WINDOW_MS);
|
|
5723
|
-
}
|
|
5724
|
-
joinFlow();
|
|
5725
|
-
return {
|
|
5726
|
-
dispose: () => {
|
|
5727
|
-
if (disposed) return;
|
|
5728
|
-
disposed = true;
|
|
5729
|
-
if (joinCollectionTimer !== null) {
|
|
5730
|
-
clearTimeout(joinCollectionTimer);
|
|
5731
|
-
joinCollectionTimer = null;
|
|
5732
|
-
}
|
|
5733
|
-
if (snapshotTimeoutTimer !== null) {
|
|
5734
|
-
clearTimeout(snapshotTimeoutTimer);
|
|
5735
|
-
snapshotTimeoutTimer = null;
|
|
5736
|
-
}
|
|
5737
|
-
unsubscribeChange();
|
|
5738
|
-
try {
|
|
5739
|
-
channel.close();
|
|
5740
|
-
} catch {
|
|
5741
|
-
}
|
|
5742
|
-
},
|
|
5743
|
-
lifecycle: () => lifecycle,
|
|
5744
|
-
senderId,
|
|
5745
|
-
channelName
|
|
5746
|
-
};
|
|
5747
|
-
}
|
|
5748
|
-
const MULTI_TAB_SYNC_MODULE_KEY = "multiTabSync";
|
|
5749
|
-
|
|
5750
5489
|
const warned = /* @__PURE__ */ new Set();
|
|
5751
5490
|
function warnOnceInsecureContext(feature) {
|
|
5752
5491
|
if (!paths.__DEV__) return;
|
|
@@ -5806,8 +5545,10 @@ function useAbstractForm(configuration, options) {
|
|
|
5806
5545
|
}
|
|
5807
5546
|
const existing = registry.forms.get(key);
|
|
5808
5547
|
if (paths.__DEV__ && existing !== void 0) {
|
|
5809
|
-
|
|
5810
|
-
|
|
5548
|
+
void import('../chunks/dev-key-collision-warnings.cjs').then((m) => {
|
|
5549
|
+
void m.warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
|
|
5550
|
+
m.warnOnPersistDivergence(key, existing, configuration.persist);
|
|
5551
|
+
});
|
|
5811
5552
|
}
|
|
5812
5553
|
const hadPendingHydration = registry.pendingHydration.has(key);
|
|
5813
5554
|
const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
|
|
@@ -5848,10 +5589,33 @@ function useAbstractForm(configuration, options) {
|
|
|
5848
5589
|
} else {
|
|
5849
5590
|
const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
|
|
5850
5591
|
void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
|
|
5851
|
-
const
|
|
5852
|
-
|
|
5853
|
-
state.
|
|
5854
|
-
|
|
5592
|
+
const adapterPromise = getStorageAdapter(resolvedPersist.storage);
|
|
5593
|
+
let persistDisposed = false;
|
|
5594
|
+
state.registerCleanup(() => {
|
|
5595
|
+
persistDisposed = true;
|
|
5596
|
+
});
|
|
5597
|
+
const ready = (async () => {
|
|
5598
|
+
try {
|
|
5599
|
+
const [{ wirePersistence }, fingerprintToken] = await Promise.all([
|
|
5600
|
+
import('../chunks/wire-persistence.cjs'),
|
|
5601
|
+
resolvePersistFingerprintToken(state)
|
|
5602
|
+
]);
|
|
5603
|
+
if (persistDisposed) return void 0;
|
|
5604
|
+
const persistenceModule = wirePersistence(
|
|
5605
|
+
state,
|
|
5606
|
+
resolvedPersist,
|
|
5607
|
+
adapterPromise,
|
|
5608
|
+
fingerprintToken
|
|
5609
|
+
);
|
|
5610
|
+
state.registerDrain(() => persistenceModule.awaitPendingWrites());
|
|
5611
|
+
state.registerCleanup(() => persistenceModule.dispose());
|
|
5612
|
+
return persistenceModule;
|
|
5613
|
+
} catch {
|
|
5614
|
+
return void 0;
|
|
5615
|
+
}
|
|
5616
|
+
})();
|
|
5617
|
+
const persistenceHandle = { config: resolvedPersist, ready };
|
|
5618
|
+
state.modules.set(PERSISTENCE_MODULE_KEY, persistenceHandle);
|
|
5855
5619
|
}
|
|
5856
5620
|
} else {
|
|
5857
5621
|
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
@@ -5861,27 +5625,31 @@ function useAbstractForm(configuration, options) {
|
|
|
5861
5625
|
const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
|
|
5862
5626
|
const secureContext = isSecureContext();
|
|
5863
5627
|
if (hasBroadcastChannel && secureContext) {
|
|
5864
|
-
let
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
}
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5628
|
+
let formDisposed = false;
|
|
5629
|
+
state.registerCleanup(() => {
|
|
5630
|
+
formDisposed = true;
|
|
5631
|
+
});
|
|
5632
|
+
void (async () => {
|
|
5633
|
+
try {
|
|
5634
|
+
const [{ createMultiTabSyncModule, MULTI_TAB_SYNC_MODULE_KEY }, fingerprint] = await Promise.all([import('../chunks/multi-tab-sync.cjs'), state.schema.fingerprint()]);
|
|
5635
|
+
if (formDisposed) return;
|
|
5636
|
+
const channelName = `attaform:sync:${state.formKey}:${hashStableString(fingerprint)}`;
|
|
5637
|
+
const syncModule = createMultiTabSyncModule(state, channelName, {
|
|
5638
|
+
isSensitivePath: state.isSensitivePath,
|
|
5639
|
+
noSyncPaths: state.noSyncPaths,
|
|
5640
|
+
validateForm: (form) => {
|
|
5641
|
+
const result = state.schema.validateAtPath(form, void 0, { sync: true });
|
|
5642
|
+
if (result instanceof Promise) return;
|
|
5643
|
+
if (!result.success) {
|
|
5644
|
+
throw new Error("attaform multi-tab sync: post-apply schema validation failed");
|
|
5645
|
+
}
|
|
5879
5646
|
}
|
|
5880
|
-
}
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5647
|
+
});
|
|
5648
|
+
state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
|
|
5649
|
+
state.registerCleanup(() => syncModule.dispose());
|
|
5650
|
+
} catch {
|
|
5651
|
+
}
|
|
5652
|
+
})();
|
|
5885
5653
|
} else if (hasBroadcastChannel && !secureContext) {
|
|
5886
5654
|
warnOnceInsecureContext("multiTab");
|
|
5887
5655
|
}
|
|
@@ -6042,55 +5810,17 @@ function resolveFormKey(key) {
|
|
|
6042
5810
|
}
|
|
6043
5811
|
return `${ANONYMOUS_FORM_KEY_PREFIX}${anonCounter++}`;
|
|
6044
5812
|
}
|
|
6045
|
-
function
|
|
6046
|
-
let existingFp;
|
|
6047
|
-
let incomingFp;
|
|
5813
|
+
async function resolvePersistFingerprintToken(state) {
|
|
6048
5814
|
try {
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
return;
|
|
6057
|
-
}
|
|
6058
|
-
if (existingFp === incomingFp) return;
|
|
6059
|
-
console.warn(
|
|
6060
|
-
`[attaform] useForm() calls with key "${key}" use different schemas; first wins, second is ignored. Use identical schemas or unique keys.
|
|
6061
|
-
existing: ${existingFp}
|
|
6062
|
-
incoming: ${incomingFp}`
|
|
6063
|
-
);
|
|
6064
|
-
}
|
|
6065
|
-
function warnOnPersistDivergence(key, existing, incomingPersist) {
|
|
6066
|
-
if (incomingPersist === void 0) return;
|
|
6067
|
-
const wired = existing.modules.get(PERSISTENCE_MODULE_KEY);
|
|
6068
|
-
const incomingNormalized = normalizePersistConfig(incomingPersist);
|
|
6069
|
-
if (wired === void 0) {
|
|
6070
|
-
console.warn(
|
|
6071
|
-
`[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.`
|
|
6072
|
-
);
|
|
6073
|
-
return;
|
|
5815
|
+
return hashStableString(await state.schema.fingerprint());
|
|
5816
|
+
} catch (err) {
|
|
5817
|
+
if (paths.__DEV__) {
|
|
5818
|
+
console.warn(
|
|
5819
|
+
`[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.`
|
|
5820
|
+
);
|
|
5821
|
+
}
|
|
5822
|
+
return "unfingerprinted";
|
|
6074
5823
|
}
|
|
6075
|
-
if (persistConfigsEquivalent(wired.wiredConfig, incomingNormalized)) return;
|
|
6076
|
-
console.warn(
|
|
6077
|
-
`[attaform] useForm({ key: "${key}" }) passed a persist config that differs from the first useForm({ key }) call's; first wins, this one is ignored.
|
|
6078
|
-
wired: ${describePersist(wired.wiredConfig)}
|
|
6079
|
-
incoming: ${describePersist(incomingNormalized)}`
|
|
6080
|
-
);
|
|
6081
|
-
}
|
|
6082
|
-
function persistConfigsEquivalent(a, b) {
|
|
6083
|
-
if (a.storage !== b.storage) return false;
|
|
6084
|
-
if ((a.key ?? void 0) !== (b.key ?? void 0)) return false;
|
|
6085
|
-
if ((a.debounceMs ?? void 0) !== (b.debounceMs ?? void 0)) return false;
|
|
6086
|
-
return true;
|
|
6087
|
-
}
|
|
6088
|
-
function describePersist(config) {
|
|
6089
|
-
const storage = typeof config.storage === "string" ? config.storage : "custom-adapter";
|
|
6090
|
-
const parts = [`storage=${storage}`];
|
|
6091
|
-
if (config.key !== void 0) parts.push(`key=${config.key}`);
|
|
6092
|
-
if (config.debounceMs !== void 0) parts.push(`debounceMs=${config.debounceMs}`);
|
|
6093
|
-
return `{ ${parts.join(", ")} }`;
|
|
6094
5824
|
}
|
|
6095
5825
|
const warnedAnonPersistKeys = /* @__PURE__ */ new Set();
|
|
6096
5826
|
function enforceAnonPersistRule(formKey, ssr) {
|
|
@@ -6187,6 +5917,8 @@ function isLazyMarker(value) {
|
|
|
6187
5917
|
}
|
|
6188
5918
|
|
|
6189
5919
|
const NOOP_WIZARD_HISTORY = {
|
|
5920
|
+
push() {
|
|
5921
|
+
},
|
|
6190
5922
|
replace() {
|
|
6191
5923
|
},
|
|
6192
5924
|
read() {
|
|
@@ -6201,6 +5933,9 @@ function createWizardHistory(param) {
|
|
|
6201
5933
|
if (typeof window === "undefined") return NOOP_WIZARD_HISTORY;
|
|
6202
5934
|
const subscribers = [];
|
|
6203
5935
|
let disposed = false;
|
|
5936
|
+
function currentKey() {
|
|
5937
|
+
return new URL(window.location.href).searchParams.get(param) ?? void 0;
|
|
5938
|
+
}
|
|
6204
5939
|
function buildUrl(key) {
|
|
6205
5940
|
const url = new URL(window.location.href);
|
|
6206
5941
|
url.searchParams.set(param, key);
|
|
@@ -6208,25 +5943,29 @@ function createWizardHistory(param) {
|
|
|
6208
5943
|
}
|
|
6209
5944
|
function handlePopstate() {
|
|
6210
5945
|
if (disposed) return;
|
|
6211
|
-
const
|
|
6212
|
-
const value = url.searchParams.get(param) ?? void 0;
|
|
5946
|
+
const value = currentKey();
|
|
6213
5947
|
for (const subscriber of subscribers) subscriber(value);
|
|
6214
5948
|
}
|
|
6215
5949
|
window.addEventListener("popstate", handlePopstate);
|
|
6216
|
-
function
|
|
5950
|
+
function safeWrite(key, mode) {
|
|
6217
5951
|
try {
|
|
6218
|
-
window.history.
|
|
5952
|
+
if (mode === "push") window.history.pushState({}, "", buildUrl(key));
|
|
5953
|
+
else window.history.replaceState({}, "", buildUrl(key));
|
|
6219
5954
|
} catch {
|
|
6220
5955
|
}
|
|
6221
5956
|
}
|
|
6222
5957
|
return {
|
|
5958
|
+
push(key) {
|
|
5959
|
+
if (disposed) return;
|
|
5960
|
+
if (currentKey() === key) return;
|
|
5961
|
+
safeWrite(key, "push");
|
|
5962
|
+
},
|
|
6223
5963
|
replace(key) {
|
|
6224
5964
|
if (disposed) return;
|
|
6225
|
-
|
|
5965
|
+
safeWrite(key, "replace");
|
|
6226
5966
|
},
|
|
6227
5967
|
read() {
|
|
6228
|
-
|
|
6229
|
-
return url.searchParams.get(param) ?? void 0;
|
|
5968
|
+
return currentKey();
|
|
6230
5969
|
},
|
|
6231
5970
|
subscribe(callback) {
|
|
6232
5971
|
if (disposed) return;
|
|
@@ -6258,12 +5997,13 @@ function buildNoopWizardSchema(formKey) {
|
|
|
6258
5997
|
formKey
|
|
6259
5998
|
};
|
|
6260
5999
|
return {
|
|
6261
|
-
fingerprint: () => NOOP_FINGERPRINT,
|
|
6000
|
+
fingerprint: () => Promise.resolve(NOOP_FINGERPRINT),
|
|
6262
6001
|
getDefaultValues: () => defaultsResponse,
|
|
6263
6002
|
getDefaultAtPath: () => void 0,
|
|
6264
6003
|
getEmptyValueAtPath: () => void 0,
|
|
6265
6004
|
isPreprocessOrCoerceLeaf: () => false,
|
|
6266
6005
|
arrayShapeAtPath: () => void 0,
|
|
6006
|
+
isFixedObjectAtPath: (path) => path.length === 0,
|
|
6267
6007
|
getSchemasAtPath: () => [],
|
|
6268
6008
|
validateAtPath: () => success,
|
|
6269
6009
|
getSlimPrimitiveTypesAtPath: () => new Set(EMPTY_SLIM_KINDS),
|
|
@@ -6717,7 +6457,10 @@ function useWizard(options) {
|
|
|
6717
6457
|
};
|
|
6718
6458
|
const persistCallback = options.persist === false ? void 0 : options.persist !== void 0 ? options.persist : (state) => {
|
|
6719
6459
|
if (state.step === void 0) return;
|
|
6720
|
-
historyHandle.
|
|
6460
|
+
const current = historyHandle.read();
|
|
6461
|
+
const effectiveCurrent = current !== void 0 && isCompiledKey(current) ? current : firstKey();
|
|
6462
|
+
if (state.step === effectiveCurrent) historyHandle.replace(state.step);
|
|
6463
|
+
else historyHandle.push(state.step);
|
|
6721
6464
|
};
|
|
6722
6465
|
function isCompiledKey(key) {
|
|
6723
6466
|
const list = compiledSteps.value;
|
|
@@ -6798,6 +6541,7 @@ function useWizard(options) {
|
|
|
6798
6541
|
}
|
|
6799
6542
|
const submitting = vue.ref(false);
|
|
6800
6543
|
const submissionAttempts = vue.ref(0);
|
|
6544
|
+
const submitError = vue.ref(null);
|
|
6801
6545
|
const done = vue.ref(false);
|
|
6802
6546
|
function activateForm(form) {
|
|
6803
6547
|
const source = asSubmissionSource(form);
|
|
@@ -6925,7 +6669,7 @@ function useWizard(options) {
|
|
|
6925
6669
|
formKey: form.key
|
|
6926
6670
|
};
|
|
6927
6671
|
}
|
|
6928
|
-
return full.
|
|
6672
|
+
return full.parse();
|
|
6929
6673
|
}
|
|
6930
6674
|
function collectErrors(results) {
|
|
6931
6675
|
const out = [];
|
|
@@ -6956,6 +6700,7 @@ function useWizard(options) {
|
|
|
6956
6700
|
return;
|
|
6957
6701
|
}
|
|
6958
6702
|
submitting.value = true;
|
|
6703
|
+
submitError.value = null;
|
|
6959
6704
|
try {
|
|
6960
6705
|
const currentKey = activeKey.value;
|
|
6961
6706
|
const final = isFinalStep.value;
|
|
@@ -6964,18 +6709,24 @@ function useWizard(options) {
|
|
|
6964
6709
|
if (final) {
|
|
6965
6710
|
await Promise.all(
|
|
6966
6711
|
list.map(async (step) => {
|
|
6712
|
+
registry.forms.get(step.key)?.clearUserErrors();
|
|
6967
6713
|
const result = await processOne(step.form);
|
|
6968
6714
|
results.set(step.key, result);
|
|
6969
6715
|
})
|
|
6970
6716
|
);
|
|
6971
6717
|
} else {
|
|
6972
6718
|
const active = activeForm.value;
|
|
6719
|
+
registry.forms.get(active.key)?.clearUserErrors();
|
|
6973
6720
|
const result = await processOne(active);
|
|
6974
6721
|
results.set(active.key, result);
|
|
6975
6722
|
}
|
|
6976
6723
|
for (const key of results.keys()) {
|
|
6977
6724
|
const store = registry.forms.get(key);
|
|
6978
|
-
if (store !== void 0)
|
|
6725
|
+
if (store !== void 0) {
|
|
6726
|
+
store.submissionAttempts.value += 1;
|
|
6727
|
+
store.cancelFieldValidation();
|
|
6728
|
+
store.displayEngine.clear();
|
|
6729
|
+
}
|
|
6979
6730
|
}
|
|
6980
6731
|
submissionAttempts.value += 1;
|
|
6981
6732
|
const errors = collectErrors(results);
|
|
@@ -7000,7 +6751,6 @@ function useWizard(options) {
|
|
|
7000
6751
|
if (target !== void 0) moveTo(target.key);
|
|
7001
6752
|
}
|
|
7002
6753
|
} else {
|
|
7003
|
-
if (onError !== void 0) await onError(errors);
|
|
7004
6754
|
if (options.focusFirstError !== false) {
|
|
7005
6755
|
const firstFailedKey = errors[0]?.formKey;
|
|
7006
6756
|
if (firstFailedKey !== void 0 && isCompiledKey(firstFailedKey)) {
|
|
@@ -7015,7 +6765,16 @@ function useWizard(options) {
|
|
|
7015
6765
|
}
|
|
7016
6766
|
}
|
|
7017
6767
|
}
|
|
6768
|
+
if (onError !== void 0) {
|
|
6769
|
+
try {
|
|
6770
|
+
await onError(errors);
|
|
6771
|
+
} catch (cause) {
|
|
6772
|
+
throw new paths.SubmitErrorHandlerError("User-provided onError threw", { cause });
|
|
6773
|
+
}
|
|
6774
|
+
}
|
|
7018
6775
|
}
|
|
6776
|
+
} catch (err) {
|
|
6777
|
+
submitError.value = paths.toError(err);
|
|
7019
6778
|
} finally {
|
|
7020
6779
|
submitting.value = false;
|
|
7021
6780
|
}
|
|
@@ -7024,6 +6783,7 @@ function useWizard(options) {
|
|
|
7024
6783
|
function reset() {
|
|
7025
6784
|
submissionAttempts.value = 0;
|
|
7026
6785
|
done.value = false;
|
|
6786
|
+
submitError.value = null;
|
|
7027
6787
|
lazyEpoch.value += 1;
|
|
7028
6788
|
for (const step of compiledSteps.value) {
|
|
7029
6789
|
const full = asSubmissionSource(step.form);
|
|
@@ -7099,6 +6859,9 @@ function useWizard(options) {
|
|
|
7099
6859
|
get submissionAttempts() {
|
|
7100
6860
|
return submissionAttempts.value;
|
|
7101
6861
|
},
|
|
6862
|
+
get submitError() {
|
|
6863
|
+
return submitError.value;
|
|
6864
|
+
},
|
|
7102
6865
|
get visited() {
|
|
7103
6866
|
return visited.value;
|
|
7104
6867
|
}
|
|
@@ -7209,9 +6972,16 @@ function warnIfAmbientWizardProviderHadDuplicates() {
|
|
|
7209
6972
|
}
|
|
7210
6973
|
|
|
7211
6974
|
exports.AttaformErrorCode = AttaformErrorCode;
|
|
6975
|
+
exports.DEFAULT_PERSISTENCE_DEBOUNCE_MS = DEFAULT_PERSISTENCE_DEBOUNCE_MS;
|
|
6976
|
+
exports.DEFAULT_TIMINGS = DEFAULT_TIMINGS;
|
|
6977
|
+
exports.PERSISTENCE_MODULE_KEY = PERSISTENCE_MODULE_KEY;
|
|
6978
|
+
exports.applyPatchesForward = applyPatchesForward;
|
|
6979
|
+
exports.cleanupOrphanKeys = cleanupOrphanKeys;
|
|
7212
6980
|
exports.defaultCoercionRules = defaultCoercionRules;
|
|
7213
6981
|
exports.defaultDisplayState = defaultDisplayState;
|
|
7214
6982
|
exports.defineCoercion = defineCoercion;
|
|
6983
|
+
exports.deleteAtPath = deleteAtPath;
|
|
6984
|
+
exports.diffAndApply = diffAndApply;
|
|
7215
6985
|
exports.getAtPath = getAtPath;
|
|
7216
6986
|
exports.humanize = humanize;
|
|
7217
6987
|
exports.injectForm = injectForm;
|
|
@@ -7219,12 +6989,17 @@ exports.injectWizard = injectWizard;
|
|
|
7219
6989
|
exports.isPlainRecord = isPlainRecord;
|
|
7220
6990
|
exports.isUnset = isUnset;
|
|
7221
6991
|
exports.lazy = lazy;
|
|
6992
|
+
exports.makeDefaultDisplayState = makeDefaultDisplayState;
|
|
6993
|
+
exports.mergeSparseHydration = mergeSparseHydration;
|
|
7222
6994
|
exports.normalizeNumericOption = normalizeNumericOption;
|
|
6995
|
+
exports.normalizePersistConfig = normalizePersistConfig;
|
|
6996
|
+
exports.resolveStorageKeyBase = resolveStorageKeyBase;
|
|
7223
6997
|
exports.safeAssign = safeAssign;
|
|
7224
6998
|
exports.safeOwnRead = safeOwnRead;
|
|
7225
6999
|
exports.setAtPath = setAtPath;
|
|
7226
7000
|
exports.slimKindOf = slimKindOf;
|
|
7001
|
+
exports.structuralSnapshot = structuralSnapshot;
|
|
7227
7002
|
exports.unset = unset;
|
|
7228
7003
|
exports.useAbstractForm = useAbstractForm;
|
|
7229
7004
|
exports.useWizard = useWizard;
|
|
7230
|
-
//# sourceMappingURL=attaform.
|
|
7005
|
+
//# sourceMappingURL=attaform.ClXwitZj.cjs.map
|