attaform 0.20.1 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- 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 +2 -2
- package/dist/chunks/devtools.cjs.map +1 -1
- package/dist/chunks/devtools.mjs +2 -2
- package/dist/chunks/devtools.mjs.map +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 +65 -70
- package/dist/index.d.mts +65 -70
- package/dist/index.d.ts +65 -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.D5-1XGQU.d.cts → attaform.7lzO9pdM.d.mts} +95 -1
- package/dist/shared/{attaform.SfhU0OEY.d.mts → attaform.B1nyO4ec.d.cts} +108 -39
- package/dist/shared/{attaform.SfhU0OEY.d.cts → attaform.B1nyO4ec.d.mts} +108 -39
- package/dist/shared/{attaform.SfhU0OEY.d.ts → attaform.B1nyO4ec.d.ts} +108 -39
- package/dist/shared/{attaform.BPy-4qRx.cjs → attaform.BA3vRDos.cjs} +53 -36
- package/dist/shared/attaform.BA3vRDos.cjs.map +1 -0
- package/dist/shared/{attaform.GbDo_lJi.d.cts → attaform.BDIEq9qP.d.cts} +1 -1
- package/dist/shared/attaform.BJGA_UOS.mjs +37 -0
- package/dist/shared/attaform.BJGA_UOS.mjs.map +1 -0
- package/dist/shared/{attaform.Dl5kDY-A.d.ts → attaform.BK1RE2ha.d.ts} +1 -1
- package/dist/shared/{attaform.DoKXru-a.d.mts → attaform.BQ6drorq.d.mts} +1 -1
- package/dist/shared/attaform.BRGIpZo4.cjs +26 -0
- package/dist/shared/attaform.BRGIpZo4.cjs.map +1 -0
- package/dist/shared/{attaform.DLnE5bZa.cjs → attaform.BUszFoKq.cjs} +388 -912
- package/dist/shared/attaform.BUszFoKq.cjs.map +1 -0
- package/dist/shared/{attaform.iWo9soNX.mjs → attaform.BnK_bfcb.mjs} +39 -392
- package/dist/shared/attaform.BnK_bfcb.mjs.map +1 -0
- package/dist/shared/{attaform.BCBxTyMC.cjs → attaform.BzvOdiSI.cjs} +101 -417
- package/dist/shared/attaform.BzvOdiSI.cjs.map +1 -0
- package/dist/shared/attaform.C3Doa9Pt.mjs +24 -0
- package/dist/shared/attaform.C3Doa9Pt.mjs.map +1 -0
- package/dist/shared/{attaform.BPxsYtTe.cjs → attaform.CEf6wYfD.cjs} +2 -2
- package/dist/shared/{attaform.BPxsYtTe.cjs.map → attaform.CEf6wYfD.cjs.map} +1 -1
- package/dist/shared/attaform.CQN9R62B.cjs +39 -0
- package/dist/shared/attaform.CQN9R62B.cjs.map +1 -0
- package/dist/shared/{attaform.EMzJcQci.d.mts → attaform.CRsXyy-Y.d.ts} +95 -1
- package/dist/shared/{attaform.D6CwqkPx.mjs → attaform.CkjTapyq.mjs} +89 -405
- package/dist/shared/attaform.CkjTapyq.mjs.map +1 -0
- package/dist/shared/{attaform.BqZuwLTK.mjs → attaform.DSqO6Db7.mjs} +377 -913
- package/dist/shared/attaform.DSqO6Db7.mjs.map +1 -0
- 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.Bh3ACtts.d.ts → attaform.F8LMHHWV.d.cts} +95 -1
- package/dist/shared/attaform.LEWUFqUw.cjs +54 -0
- package/dist/shared/attaform.LEWUFqUw.cjs.map +1 -0
- package/dist/shared/{attaform.BrrXNmfK.cjs → attaform.PnqML3xW.cjs} +68 -402
- package/dist/shared/attaform.PnqML3xW.cjs.map +1 -0
- package/dist/shared/{attaform.BKozEdTr.mjs → attaform.Y_Mgg0Yp.mjs} +53 -37
- package/dist/shared/attaform.Y_Mgg0Yp.mjs.map +1 -0
- package/dist/shared/{attaform.DHRWn-cu.cjs → attaform._rsCZy2j.cjs} +172 -20
- package/dist/shared/attaform._rsCZy2j.cjs.map +1 -0
- package/dist/shared/{attaform.EZG6fOFb.mjs → attaform.ezb5Nh2t.mjs} +2 -2
- package/dist/shared/{attaform.EZG6fOFb.mjs.map → attaform.ezb5Nh2t.mjs.map} +1 -1
- package/dist/shared/{attaform.tVkmQh5w.mjs → attaform.r3PePkDR.mjs} +172 -21
- package/dist/shared/attaform.r3PePkDR.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 +5 -5
- package/dist/zod.d.mts +5 -5
- package/dist/zod.d.ts +5 -5
- package/dist/zod.mjs +6 -6
- package/package.json +21 -7
- package/dist/shared/attaform.BCBxTyMC.cjs.map +0 -1
- package/dist/shared/attaform.BKozEdTr.mjs.map +0 -1
- package/dist/shared/attaform.BPy-4qRx.cjs.map +0 -1
- package/dist/shared/attaform.BqZuwLTK.mjs.map +0 -1
- package/dist/shared/attaform.BrrXNmfK.cjs.map +0 -1
- package/dist/shared/attaform.D6CwqkPx.mjs.map +0 -1
- package/dist/shared/attaform.DHRWn-cu.cjs.map +0 -1
- package/dist/shared/attaform.DLnE5bZa.cjs.map +0 -1
- package/dist/shared/attaform.iWo9soNX.mjs.map +0 -1
- package/dist/shared/attaform.tVkmQh5w.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const vue = require('vue');
|
|
4
|
-
const paths = require('./attaform.
|
|
4
|
+
const paths = require('./attaform.BA3vRDos.cjs');
|
|
5
5
|
|
|
6
6
|
function safeAssign(target, key, value) {
|
|
7
7
|
if (key === "__proto__") {
|
|
@@ -460,17 +460,51 @@ function structuralSnapshot(value) {
|
|
|
460
460
|
return out;
|
|
461
461
|
}
|
|
462
462
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
463
|
+
function isGateOpen(field, formMeta) {
|
|
464
|
+
return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
|
|
465
|
+
}
|
|
466
|
+
function computeVerdict(field, formMeta) {
|
|
467
|
+
if (!isGateOpen(field, formMeta)) return "idle";
|
|
467
468
|
const hasOwnError = field.errors.some(
|
|
468
469
|
(e) => e.path.length === field.path.length && e.path.every((s, i) => s === field.path[i])
|
|
469
470
|
);
|
|
470
471
|
if (hasOwnError) return "error";
|
|
471
472
|
if (field.valid === true && field.blank !== true && field.dirty === true) return "success";
|
|
472
473
|
return "idle";
|
|
473
|
-
}
|
|
474
|
+
}
|
|
475
|
+
const DEFAULT_TIMINGS = { showDelay: 100, minVisible: 120 };
|
|
476
|
+
const FOCUS_OUT_GRACE = 16;
|
|
477
|
+
const defaultFamily = /* @__PURE__ */ new WeakSet();
|
|
478
|
+
function isDefaultDisplayState(fn) {
|
|
479
|
+
return defaultFamily.has(fn);
|
|
480
|
+
}
|
|
481
|
+
function makeDefaultDisplayState({
|
|
482
|
+
showDelay,
|
|
483
|
+
minVisible
|
|
484
|
+
}) {
|
|
485
|
+
const reducer = (prev, { field, formMeta, validatingSince, now }) => {
|
|
486
|
+
const verdict = computeVerdict(field, formMeta);
|
|
487
|
+
if (!isGateOpen(field, formMeta)) return { display: verdict };
|
|
488
|
+
if (validatingSince === null) {
|
|
489
|
+
if (prev.display === "pending") {
|
|
490
|
+
const shownAt = prev.pendingShownAt ?? now;
|
|
491
|
+
if (now < shownAt + minVisible)
|
|
492
|
+
return { display: "pending", pendingShownAt: shownAt, reviewAt: shownAt + minVisible };
|
|
493
|
+
}
|
|
494
|
+
return { display: verdict };
|
|
495
|
+
}
|
|
496
|
+
if (prev.display === "pending")
|
|
497
|
+
return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
|
|
498
|
+
const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
|
|
499
|
+
if (now - validatingSince < window) {
|
|
500
|
+
return { display: prev.display, reviewAt: validatingSince + window };
|
|
501
|
+
}
|
|
502
|
+
return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
|
|
503
|
+
};
|
|
504
|
+
defaultFamily.add(reducer);
|
|
505
|
+
return reducer;
|
|
506
|
+
}
|
|
507
|
+
const defaultDisplayState = makeDefaultDisplayState(DEFAULT_TIMINGS);
|
|
474
508
|
function resolveGetDisplayState(config) {
|
|
475
509
|
return config ?? defaultDisplayState;
|
|
476
510
|
}
|
|
@@ -627,7 +661,19 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
627
661
|
}
|
|
628
662
|
function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
|
|
629
663
|
const base = buildLeafFieldStateBase(state, segments, key, formInstanceId);
|
|
630
|
-
|
|
664
|
+
const validatingSince = state.fieldValidatingSince.get(key) ?? null;
|
|
665
|
+
return decorateWithDerivedProps(
|
|
666
|
+
base,
|
|
667
|
+
state,
|
|
668
|
+
getFormMetaBase,
|
|
669
|
+
key,
|
|
670
|
+
validatingSince,
|
|
671
|
+
false,
|
|
672
|
+
// revealedDescendantError: leaves have no descendants
|
|
673
|
+
false,
|
|
674
|
+
// isRoot: a leaf is never the form root
|
|
675
|
+
getDisplayState
|
|
676
|
+
);
|
|
631
677
|
}
|
|
632
678
|
function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
633
679
|
const formValue = state.form.value;
|
|
@@ -643,8 +689,11 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
643
689
|
let blurredAfterInteraction = false;
|
|
644
690
|
let connected = false;
|
|
645
691
|
let validating = false;
|
|
692
|
+
let validatingSince = null;
|
|
646
693
|
let updatedAt = null;
|
|
647
694
|
let asyncPending = false;
|
|
695
|
+
const submissionAttempts = state.submissionAttempts.value;
|
|
696
|
+
const blurredLeafSegments = [];
|
|
648
697
|
for (const [leafKey, entry] of state.originals) {
|
|
649
698
|
if (!paths.isPathPrefix(segments, entry.segments)) continue;
|
|
650
699
|
if (segments.length === entry.segments.length) continue;
|
|
@@ -659,9 +708,18 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
659
708
|
if (leafRecord?.blurred === true) blurred = true;
|
|
660
709
|
if (leafRecord?.touched === true) touched = true;
|
|
661
710
|
if (leafRecord?.interacted === true) interacted = true;
|
|
662
|
-
if (leafRecord?.blurredAfterInteraction === true)
|
|
711
|
+
if (leafRecord?.blurredAfterInteraction === true) {
|
|
712
|
+
blurredAfterInteraction = true;
|
|
713
|
+
blurredLeafSegments.push(entry.segments);
|
|
714
|
+
}
|
|
663
715
|
if (leafRecord?.connected === true) connected = true;
|
|
664
|
-
if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0)
|
|
716
|
+
if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0) {
|
|
717
|
+
validating = true;
|
|
718
|
+
const leafGateOpen = submissionAttempts > 0 || leafRecord?.blurredAfterInteraction === true;
|
|
719
|
+
const since = state.fieldValidatingSince.get(leafKey);
|
|
720
|
+
if (leafGateOpen && since !== void 0 && (validatingSince === null || since < validatingSince))
|
|
721
|
+
validatingSince = since;
|
|
722
|
+
}
|
|
665
723
|
if (state.pathHasAsyncValidationByKey(leafKey, entry.segments)) asyncPending = true;
|
|
666
724
|
const ts = leafRecord?.updatedAt;
|
|
667
725
|
if (ts !== void 0 && ts !== null) {
|
|
@@ -673,6 +731,13 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
673
731
|
dirty = true;
|
|
674
732
|
}
|
|
675
733
|
const errors = aggregateErrorsAt(state, segments);
|
|
734
|
+
const revealedDescendantError = errors.length > 0 && errors.some((e) => {
|
|
735
|
+
const ePath = e.path;
|
|
736
|
+
if (ePath.length === segments.length && ePath.every((s, i) => s === segments[i])) return false;
|
|
737
|
+
if (submissionAttempts > 0) return true;
|
|
738
|
+
if (ePath.length === 1 && ePath[0] === "") return blurredAfterInteraction;
|
|
739
|
+
return blurredLeafSegments.some((s) => paths.isPathPrefix(ePath, s));
|
|
740
|
+
});
|
|
676
741
|
if (!asyncPending && state.pathHasAsyncValidation(segments)) asyncPending = true;
|
|
677
742
|
const gated = asyncPending && !state.firstValidationDone.value;
|
|
678
743
|
const valid = !gated && errors.length === 0 && !validating;
|
|
@@ -680,43 +745,70 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
680
745
|
const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
|
|
681
746
|
const label = resolved.label || humanize(lastSegment);
|
|
682
747
|
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
|
-
|
|
748
|
+
base: {
|
|
749
|
+
value,
|
|
750
|
+
original,
|
|
751
|
+
pristine,
|
|
752
|
+
dirty,
|
|
753
|
+
focused,
|
|
754
|
+
blurred,
|
|
755
|
+
touched,
|
|
756
|
+
interacted,
|
|
757
|
+
blurredAfterInteraction,
|
|
758
|
+
connected,
|
|
759
|
+
element: null,
|
|
760
|
+
elements: EMPTY_ELEMENTS,
|
|
761
|
+
updatedAt,
|
|
762
|
+
errors,
|
|
763
|
+
validating,
|
|
764
|
+
valid,
|
|
765
|
+
path: segments,
|
|
766
|
+
...computeFieldIdentity(formInstanceId, state.formKey, key),
|
|
767
|
+
key: state.arrayElementKey(segments),
|
|
768
|
+
blank,
|
|
769
|
+
label,
|
|
770
|
+
description: resolved.description,
|
|
771
|
+
placeholder: resolved.placeholder,
|
|
772
|
+
meta: resolved.meta
|
|
773
|
+
},
|
|
774
|
+
validatingSince,
|
|
775
|
+
revealedDescendantError
|
|
707
776
|
};
|
|
708
777
|
}
|
|
709
778
|
function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
|
|
710
|
-
const base = buildContainerFieldStateBase(
|
|
711
|
-
|
|
779
|
+
const { base, validatingSince, revealedDescendantError } = buildContainerFieldStateBase(
|
|
780
|
+
state,
|
|
781
|
+
segments,
|
|
782
|
+
key,
|
|
783
|
+
formInstanceId
|
|
784
|
+
);
|
|
785
|
+
return decorateWithDerivedProps(
|
|
786
|
+
base,
|
|
787
|
+
state,
|
|
788
|
+
getFormMetaBase,
|
|
789
|
+
key,
|
|
790
|
+
validatingSince,
|
|
791
|
+
revealedDescendantError,
|
|
792
|
+
segments.length === 0,
|
|
793
|
+
getDisplayState
|
|
794
|
+
);
|
|
712
795
|
}
|
|
713
|
-
function decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState) {
|
|
796
|
+
function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, revealedDescendantError, isRoot, getDisplayState) {
|
|
714
797
|
const firstError = base.errors[0];
|
|
715
798
|
const predicate = getDisplayState ?? state.getDisplayState;
|
|
716
799
|
const formMeta = getFormMetaBase();
|
|
717
|
-
|
|
800
|
+
const ctx = {
|
|
801
|
+
field: base,
|
|
802
|
+
formMeta,
|
|
803
|
+
validatingSince,
|
|
804
|
+
// The engine's clock. Frozen to 0 under SSR (no clock, nothing in
|
|
805
|
+
// flight) so the reducer returns the plain verdict and hydration matches.
|
|
806
|
+
now: state.ssr ? 0 : Date.now()
|
|
807
|
+
};
|
|
808
|
+
let machine;
|
|
809
|
+
let rollupApplies = isDefaultDisplayState(predicate);
|
|
718
810
|
try {
|
|
719
|
-
|
|
811
|
+
machine = state.displayEngine.resolve(key, ctx, predicate);
|
|
720
812
|
} catch (err) {
|
|
721
813
|
if (paths.__DEV__ && !warnedDisplayStatePredicates.has(predicate)) {
|
|
722
814
|
warnedDisplayStatePredicates.add(predicate);
|
|
@@ -725,8 +817,11 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState)
|
|
|
725
817
|
err
|
|
726
818
|
);
|
|
727
819
|
}
|
|
728
|
-
|
|
820
|
+
machine = state.displayEngine.resolve(key, ctx, defaultDisplayState);
|
|
821
|
+
rollupApplies = true;
|
|
729
822
|
}
|
|
823
|
+
const submitValidating = rollupApplies && isRoot && state.submitting.value && state.activeValidations.value > 0;
|
|
824
|
+
const displayState = machine.display === "pending" || submitValidating ? "pending" : rollupApplies && revealedDescendantError ? "error" : machine.display;
|
|
730
825
|
return {
|
|
731
826
|
...base,
|
|
732
827
|
displayState,
|
|
@@ -1419,90 +1514,6 @@ async function getStorageAdapter(storage) {
|
|
|
1419
1514
|
}
|
|
1420
1515
|
}
|
|
1421
1516
|
}
|
|
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
1517
|
function resolveStorageKeyBase(config, formKey) {
|
|
1507
1518
|
return config.key ?? `${PERSISTENCE_KEY_PREFIX}${formKey}`;
|
|
1508
1519
|
}
|
|
@@ -1549,47 +1560,6 @@ async function sweepNonConfiguredStandardStoresForOrphans(configured, base) {
|
|
|
1549
1560
|
}
|
|
1550
1561
|
}
|
|
1551
1562
|
}
|
|
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
1563
|
function mergeSparseHydration(schemaDefaults, sparse, schema) {
|
|
1594
1564
|
return mergeDeep(schemaDefaults, sparse, [], schema);
|
|
1595
1565
|
}
|
|
@@ -1827,6 +1797,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1827
1797
|
state.submitting.value = true;
|
|
1828
1798
|
state.submitError.value = null;
|
|
1829
1799
|
state.cancelFieldValidation();
|
|
1800
|
+
state.displayEngine.clear();
|
|
1830
1801
|
state.activeValidations.value += 1;
|
|
1831
1802
|
const refinement = await runRefinementValidation(state.form.value, void 0);
|
|
1832
1803
|
const merged = composeWithDerivedBlank(refinement, void 0);
|
|
@@ -2308,6 +2279,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2308
2279
|
const isRequired = state.schema.isRequiredAtPath(segments);
|
|
2309
2280
|
const ariaEnabled = options?.autoAria ?? formAutoAria;
|
|
2310
2281
|
const ariaDisplayState = getDisplayStateAt !== void 0 ? vue.computed(() => getDisplayStateAt(segments)) : void 0;
|
|
2282
|
+
let boundElement = null;
|
|
2311
2283
|
const internalRv = {
|
|
2312
2284
|
innerRef,
|
|
2313
2285
|
displayValue,
|
|
@@ -2326,6 +2298,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2326
2298
|
state.markInteracted(segments);
|
|
2327
2299
|
},
|
|
2328
2300
|
registerElement: (element) => {
|
|
2301
|
+
boundElement = element;
|
|
2329
2302
|
if (!paths.INTERACTIVE_TAG_NAMES.has(element.tagName)) return;
|
|
2330
2303
|
const added = state.registerElement(segments, element, formInstanceId);
|
|
2331
2304
|
if (added) attachFocusListeners(state, segments, element, instanceMeta);
|
|
@@ -2333,9 +2306,11 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2333
2306
|
deregisterElement: (element) => {
|
|
2334
2307
|
detachFocusListeners(element);
|
|
2335
2308
|
state.deregisterElement(segments, element);
|
|
2309
|
+
if (boundElement === element) boundElement = null;
|
|
2336
2310
|
},
|
|
2337
2311
|
setValueWithInternalPath: (value, meta) => {
|
|
2338
|
-
|
|
2312
|
+
const resolvedMeta = meta === void 0 && boundElement !== null ? { persist: state.persistOptIns.hasOptIn(paths.getOrAssignElementId(boundElement), pathKey) } : meta;
|
|
2313
|
+
return state.setValueAtPath(segments, value, withInstanceMeta(resolvedMeta));
|
|
2339
2314
|
},
|
|
2340
2315
|
// Called by the `vRegisterHint` compile-time transform's wrapping
|
|
2341
2316
|
// IIFE on every server-side render of `<element v-register="…">`.
|
|
@@ -2639,7 +2614,12 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2639
2614
|
return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
|
|
2640
2615
|
};
|
|
2641
2616
|
const getFormMetaBase = () => {
|
|
2642
|
-
const rootBase = buildContainerFieldStateBase(
|
|
2617
|
+
const { base: rootBase } = buildContainerFieldStateBase(
|
|
2618
|
+
state,
|
|
2619
|
+
paths.ROOT_PATH,
|
|
2620
|
+
paths.ROOT_PATH_KEY,
|
|
2621
|
+
formInstanceId
|
|
2622
|
+
);
|
|
2643
2623
|
return {
|
|
2644
2624
|
...rootBase,
|
|
2645
2625
|
submitting: state.submitting.value,
|
|
@@ -2974,7 +2954,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2974
2954
|
instanceId: formInstanceId
|
|
2975
2955
|
})
|
|
2976
2956
|
);
|
|
2977
|
-
const
|
|
2957
|
+
const persistenceHandle = state.modules.get(PERSISTENCE_MODULE_KEY);
|
|
2978
2958
|
const reset = (nextDefaultValues) => {
|
|
2979
2959
|
if (nextDefaultValues === void 0) {
|
|
2980
2960
|
state.reset();
|
|
@@ -2989,15 +2969,15 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2989
2969
|
state.originalBlankPaths.add(pathKey);
|
|
2990
2970
|
}
|
|
2991
2971
|
}
|
|
2992
|
-
if (
|
|
2993
|
-
void
|
|
2972
|
+
if (persistenceHandle !== void 0) {
|
|
2973
|
+
void persistenceHandle.ready.then((m) => m?.clearPersistedDraft()).catch(() => void 0);
|
|
2994
2974
|
}
|
|
2995
2975
|
};
|
|
2996
2976
|
const resetField = (pathInput) => {
|
|
2997
2977
|
const segments = paths.canonicalizePath(pathInput).segments;
|
|
2998
2978
|
state.resetField(segments);
|
|
2999
|
-
if (
|
|
3000
|
-
void
|
|
2979
|
+
if (persistenceHandle !== void 0) {
|
|
2980
|
+
void persistenceHandle.ready.then((m) => m?.clearPersistedDraft(segments)).catch(() => void 0);
|
|
3001
2981
|
}
|
|
3002
2982
|
};
|
|
3003
2983
|
function clear(pathInput) {
|
|
@@ -3015,10 +2995,14 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3015
2995
|
)) {
|
|
3016
2996
|
return;
|
|
3017
2997
|
}
|
|
2998
|
+
if (persistenceHandle === void 0) return;
|
|
2999
|
+
const persistence = await persistenceHandle.ready;
|
|
3018
3000
|
if (persistence === void 0) return;
|
|
3019
3001
|
await persistence.writePathImmediately(segments);
|
|
3020
3002
|
};
|
|
3021
3003
|
const clearPersistedDraft = async (pathInput) => {
|
|
3004
|
+
if (persistenceHandle === void 0) return;
|
|
3005
|
+
const persistence = await persistenceHandle.ready;
|
|
3022
3006
|
if (persistence === void 0) return;
|
|
3023
3007
|
if (pathInput === void 0) {
|
|
3024
3008
|
await persistence.clearPersistedDraft();
|
|
@@ -3201,6 +3185,93 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3201
3185
|
};
|
|
3202
3186
|
}
|
|
3203
3187
|
|
|
3188
|
+
const IDLE = Object.freeze({ display: "idle" });
|
|
3189
|
+
const MAX_DELAY = 2147483647;
|
|
3190
|
+
function createDisplayEngine(ssr) {
|
|
3191
|
+
const machines = /* @__PURE__ */ new Map();
|
|
3192
|
+
const tick = vue.ref(0);
|
|
3193
|
+
let timer = null;
|
|
3194
|
+
let timerTarget = null;
|
|
3195
|
+
let lastFiredTarget = null;
|
|
3196
|
+
function clearTimer() {
|
|
3197
|
+
if (timer !== null) {
|
|
3198
|
+
clearTimeout(timer);
|
|
3199
|
+
timer = null;
|
|
3200
|
+
timerTarget = null;
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
function nearestReviewAt() {
|
|
3204
|
+
let min = null;
|
|
3205
|
+
for (const m of machines.values()) {
|
|
3206
|
+
if (m.reviewAt === void 0 || !Number.isFinite(m.reviewAt)) continue;
|
|
3207
|
+
if (min === null || m.reviewAt < min) min = m.reviewAt;
|
|
3208
|
+
}
|
|
3209
|
+
return min;
|
|
3210
|
+
}
|
|
3211
|
+
function rearm(now) {
|
|
3212
|
+
const target = nearestReviewAt();
|
|
3213
|
+
if (target === null) {
|
|
3214
|
+
clearTimer();
|
|
3215
|
+
return;
|
|
3216
|
+
}
|
|
3217
|
+
if (timer !== null && timerTarget === target) return;
|
|
3218
|
+
if (target === lastFiredTarget) {
|
|
3219
|
+
clearTimer();
|
|
3220
|
+
return;
|
|
3221
|
+
}
|
|
3222
|
+
clearTimer();
|
|
3223
|
+
timerTarget = target;
|
|
3224
|
+
timer = setTimeout(
|
|
3225
|
+
() => {
|
|
3226
|
+
timer = null;
|
|
3227
|
+
timerTarget = null;
|
|
3228
|
+
lastFiredTarget = target;
|
|
3229
|
+
tick.value++;
|
|
3230
|
+
},
|
|
3231
|
+
Math.min(MAX_DELAY, Math.max(0, target - now))
|
|
3232
|
+
);
|
|
3233
|
+
}
|
|
3234
|
+
function resolve(key, ctx, reducer) {
|
|
3235
|
+
void tick.value;
|
|
3236
|
+
const prev = machines.get(key) ?? IDLE;
|
|
3237
|
+
const machine = reducer(prev, ctx);
|
|
3238
|
+
if (ssr) return machine;
|
|
3239
|
+
const active = machine.display !== "idle" || machine.reviewAt !== void 0 && Number.isFinite(machine.reviewAt);
|
|
3240
|
+
if (active) machines.set(key, machine);
|
|
3241
|
+
else machines.delete(key);
|
|
3242
|
+
rearm(ctx.now);
|
|
3243
|
+
return machine;
|
|
3244
|
+
}
|
|
3245
|
+
function clear() {
|
|
3246
|
+
machines.clear();
|
|
3247
|
+
clearTimer();
|
|
3248
|
+
lastFiredTarget = null;
|
|
3249
|
+
}
|
|
3250
|
+
let detachVisibility = null;
|
|
3251
|
+
if (!ssr && typeof document !== "undefined") {
|
|
3252
|
+
const onVisible = () => {
|
|
3253
|
+
if (document.visibilityState === "visible" && machines.size > 0) tick.value++;
|
|
3254
|
+
};
|
|
3255
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
3256
|
+
detachVisibility = () => document.removeEventListener("visibilitychange", onVisible);
|
|
3257
|
+
}
|
|
3258
|
+
function dispose() {
|
|
3259
|
+
clear();
|
|
3260
|
+
if (detachVisibility !== null) {
|
|
3261
|
+
detachVisibility();
|
|
3262
|
+
detachVisibility = null;
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
return {
|
|
3266
|
+
resolve,
|
|
3267
|
+
clear,
|
|
3268
|
+
dispose,
|
|
3269
|
+
size: () => machines.size,
|
|
3270
|
+
has: (key) => machines.has(key),
|
|
3271
|
+
hasTimer: () => timer !== null
|
|
3272
|
+
};
|
|
3273
|
+
}
|
|
3274
|
+
|
|
3204
3275
|
function applyDuStubs(schema, data, options = {}) {
|
|
3205
3276
|
const warned = options.warn === true ? /* @__PURE__ */ new Set() : void 0;
|
|
3206
3277
|
return walkDuStubs(schema, data, options.basePath ?? [], warned);
|
|
@@ -3539,6 +3610,7 @@ function createArrayBookkeeping(deps) {
|
|
|
3539
3610
|
blankPaths,
|
|
3540
3611
|
originalBlankPaths,
|
|
3541
3612
|
fieldValidationCounts,
|
|
3613
|
+
fieldValidatingSince,
|
|
3542
3614
|
fieldValidationState,
|
|
3543
3615
|
schemaErrors,
|
|
3544
3616
|
activeValidations,
|
|
@@ -3564,6 +3636,7 @@ function createArrayBookkeeping(deps) {
|
|
|
3564
3636
|
}));
|
|
3565
3637
|
migrateSetSubtree(blankPaths, arrayPath, remap);
|
|
3566
3638
|
migrateSetSubtree(originalBlankPaths, arrayPath, remap);
|
|
3639
|
+
migrateMapSubtree(fieldValidatingSince, arrayPath, remap, (since) => since);
|
|
3567
3640
|
migrateMapSubtree(fieldValidationCounts, arrayPath, remap, (count) => count);
|
|
3568
3641
|
arrayIdentity.applyRemap(arrayPath, remap);
|
|
3569
3642
|
}
|
|
@@ -3623,6 +3696,18 @@ function isHydratedFieldRecord(value) {
|
|
|
3623
3696
|
const r = value;
|
|
3624
3697
|
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";
|
|
3625
3698
|
}
|
|
3699
|
+
function withClearedHistoryFlags(record, now) {
|
|
3700
|
+
return {
|
|
3701
|
+
path: record.path,
|
|
3702
|
+
updatedAt: now,
|
|
3703
|
+
connected: record.connected,
|
|
3704
|
+
focused: record.focused,
|
|
3705
|
+
blurred: record.blurred,
|
|
3706
|
+
touched: false,
|
|
3707
|
+
interacted: false,
|
|
3708
|
+
blurredAfterInteraction: false
|
|
3709
|
+
};
|
|
3710
|
+
}
|
|
3626
3711
|
function isHydratedValidationErrorArray(value) {
|
|
3627
3712
|
if (!Array.isArray(value)) return false;
|
|
3628
3713
|
for (const entry of value) {
|
|
@@ -3750,6 +3835,9 @@ function createFormStore(options) {
|
|
|
3750
3835
|
const resolvedIsSensitivePath = options.isSensitivePath ?? paths.isSensitivePath;
|
|
3751
3836
|
const cleanupHooks = [];
|
|
3752
3837
|
const modules = /* @__PURE__ */ new Map();
|
|
3838
|
+
const fieldValidatingSince = vue.reactive(/* @__PURE__ */ new Map());
|
|
3839
|
+
const displayEngine = createDisplayEngine(ssr);
|
|
3840
|
+
registerCleanup(() => displayEngine.dispose());
|
|
3753
3841
|
const completedConstraints = defaultValues === void 0 ? void 0 : mergeStructural(schema, [], defaultValues);
|
|
3754
3842
|
const schemaResponse = schema.getDefaultValues({
|
|
3755
3843
|
useDefaultSchemaValues: true,
|
|
@@ -3878,12 +3966,18 @@ function createFormStore(options) {
|
|
|
3878
3966
|
}
|
|
3879
3967
|
const fieldValidationCounts = vue.reactive(/* @__PURE__ */ new Map());
|
|
3880
3968
|
function incFieldValidation(key) {
|
|
3881
|
-
|
|
3969
|
+
fieldValidatingSince.set(key, ssr ? 0 : Date.now());
|
|
3970
|
+
const prevCount = fieldValidationCounts.get(key) ?? 0;
|
|
3971
|
+
fieldValidationCounts.set(key, prevCount + 1);
|
|
3882
3972
|
}
|
|
3883
3973
|
function decFieldValidation(key) {
|
|
3884
3974
|
const next = (fieldValidationCounts.get(key) ?? 0) - 1;
|
|
3885
|
-
if (next <= 0)
|
|
3886
|
-
|
|
3975
|
+
if (next <= 0) {
|
|
3976
|
+
fieldValidationCounts.delete(key);
|
|
3977
|
+
fieldValidatingSince.delete(key);
|
|
3978
|
+
} else {
|
|
3979
|
+
fieldValidationCounts.set(key, next);
|
|
3980
|
+
}
|
|
3887
3981
|
}
|
|
3888
3982
|
const initStamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3889
3983
|
diffAndApply({}, schemaInitialData, [], (patch) => {
|
|
@@ -3971,6 +4065,7 @@ function createFormStore(options) {
|
|
|
3971
4065
|
blankPaths,
|
|
3972
4066
|
originalBlankPaths,
|
|
3973
4067
|
fieldValidationCounts,
|
|
4068
|
+
fieldValidatingSince,
|
|
3974
4069
|
fieldValidationState,
|
|
3975
4070
|
schemaErrors,
|
|
3976
4071
|
activeValidations,
|
|
@@ -4238,7 +4333,7 @@ function createFormStore(options) {
|
|
|
4238
4333
|
prev.controller.abort();
|
|
4239
4334
|
}
|
|
4240
4335
|
const controller = new AbortController();
|
|
4241
|
-
const fresh = { controller, timer: null, settled: false };
|
|
4336
|
+
const fresh = { controller, timer: null, settled: false, released: false };
|
|
4242
4337
|
fieldValidationState.set(key, fresh);
|
|
4243
4338
|
const myEpoch = ++scheduleEpoch;
|
|
4244
4339
|
const run = () => {
|
|
@@ -4276,8 +4371,10 @@ function createFormStore(options) {
|
|
|
4276
4371
|
applySchemaErrorsForSubtree(scopePath ?? [], restamped);
|
|
4277
4372
|
}).catch(() => {
|
|
4278
4373
|
}).finally(() => {
|
|
4279
|
-
|
|
4280
|
-
|
|
4374
|
+
if (!fresh.released) {
|
|
4375
|
+
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
4376
|
+
decFieldValidation(key);
|
|
4377
|
+
}
|
|
4281
4378
|
fresh.settled = true;
|
|
4282
4379
|
});
|
|
4283
4380
|
};
|
|
@@ -4299,6 +4396,22 @@ function createFormStore(options) {
|
|
|
4299
4396
|
}
|
|
4300
4397
|
fieldValidationState.clear();
|
|
4301
4398
|
}
|
|
4399
|
+
function cancelFieldValidationUnder(prefix) {
|
|
4400
|
+
for (const [key, entry] of [...fieldValidationState]) {
|
|
4401
|
+
const segs = paths.segmentsForPathKey(key);
|
|
4402
|
+
if (segs === null) continue;
|
|
4403
|
+
if (!paths.isPathPrefix(prefix, segs)) continue;
|
|
4404
|
+
if (entry.timer !== null) {
|
|
4405
|
+
clearTimeout(entry.timer);
|
|
4406
|
+
} else if (!entry.settled && !entry.released) {
|
|
4407
|
+
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
4408
|
+
decFieldValidation(key);
|
|
4409
|
+
entry.released = true;
|
|
4410
|
+
}
|
|
4411
|
+
entry.controller.abort();
|
|
4412
|
+
fieldValidationState.delete(key);
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4302
4415
|
function onFormChange(listener) {
|
|
4303
4416
|
formChangeListeners.add(listener);
|
|
4304
4417
|
return () => {
|
|
@@ -4349,6 +4462,7 @@ function createFormStore(options) {
|
|
|
4349
4462
|
drainHooks.length = 0;
|
|
4350
4463
|
modules.clear();
|
|
4351
4464
|
cancelFieldValidation();
|
|
4465
|
+
fieldValidatingSince.clear();
|
|
4352
4466
|
formChangeListeners.clear();
|
|
4353
4467
|
submitSuccessListeners.clear();
|
|
4354
4468
|
resetListeners.clear();
|
|
@@ -4695,16 +4809,7 @@ function createFormStore(options) {
|
|
|
4695
4809
|
}
|
|
4696
4810
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4697
4811
|
for (const [pathKey, record] of fields) {
|
|
4698
|
-
fields.set(pathKey,
|
|
4699
|
-
path: record.path,
|
|
4700
|
-
updatedAt: now,
|
|
4701
|
-
connected: record.connected,
|
|
4702
|
-
focused: record.focused,
|
|
4703
|
-
blurred: record.blurred,
|
|
4704
|
-
touched: false,
|
|
4705
|
-
interacted: false,
|
|
4706
|
-
blurredAfterInteraction: false
|
|
4707
|
-
});
|
|
4812
|
+
fields.set(pathKey, withClearedHistoryFlags(record, now));
|
|
4708
4813
|
}
|
|
4709
4814
|
submissionGeneration.value += 1;
|
|
4710
4815
|
submitting.value = false;
|
|
@@ -4714,6 +4819,8 @@ function createFormStore(options) {
|
|
|
4714
4819
|
submitError.value = null;
|
|
4715
4820
|
departAttempts.value = 0;
|
|
4716
4821
|
cancelFieldValidation();
|
|
4822
|
+
displayEngine.clear();
|
|
4823
|
+
fieldValidatingSince.clear();
|
|
4717
4824
|
pathSnapshots.clear();
|
|
4718
4825
|
scheduleEpoch = 0;
|
|
4719
4826
|
lastCommittedEpoch = 0;
|
|
@@ -4729,6 +4836,12 @@ function createFormStore(options) {
|
|
|
4729
4836
|
function resetField(path) {
|
|
4730
4837
|
const { key: targetKey, segments: targetSegments } = paths.canonicalizePath(path);
|
|
4731
4838
|
variantMemory.clearUnderPath(targetSegments);
|
|
4839
|
+
cancelFieldValidationUnder(targetSegments);
|
|
4840
|
+
for (const [snapKey] of [...pathSnapshots]) {
|
|
4841
|
+
const segs = paths.segmentsForPathKey(snapKey);
|
|
4842
|
+
if (segs === null) continue;
|
|
4843
|
+
if (paths.isPathPrefix(targetSegments, segs)) pathSnapshots.delete(snapKey);
|
|
4844
|
+
}
|
|
4732
4845
|
const leafEntry = originals.get(targetKey);
|
|
4733
4846
|
if (leafEntry !== void 0) {
|
|
4734
4847
|
const wrote = setValueAtPath(targetSegments, leafEntry.value);
|
|
@@ -4778,16 +4891,7 @@ function createFormStore(options) {
|
|
|
4778
4891
|
function clearFieldRecordFlags(pathKey) {
|
|
4779
4892
|
const record = fields.get(pathKey);
|
|
4780
4893
|
if (record === void 0) return;
|
|
4781
|
-
fields.set(pathKey,
|
|
4782
|
-
path: record.path,
|
|
4783
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4784
|
-
connected: record.connected,
|
|
4785
|
-
focused: record.focused,
|
|
4786
|
-
blurred: record.blurred,
|
|
4787
|
-
touched: false,
|
|
4788
|
-
interacted: false,
|
|
4789
|
-
blurredAfterInteraction: false
|
|
4790
|
-
});
|
|
4894
|
+
fields.set(pathKey, withClearedHistoryFlags(record, (/* @__PURE__ */ new Date()).toISOString()));
|
|
4791
4895
|
}
|
|
4792
4896
|
function isPristineAtPath(path) {
|
|
4793
4897
|
const { key, segments } = paths.canonicalizePath(path);
|
|
@@ -4867,6 +4971,8 @@ function createFormStore(options) {
|
|
|
4867
4971
|
pathHasAsyncValidation,
|
|
4868
4972
|
pathHasAsyncValidationByKey,
|
|
4869
4973
|
fieldValidationCounts,
|
|
4974
|
+
fieldValidatingSince,
|
|
4975
|
+
displayEngine,
|
|
4870
4976
|
applyFormReplacement,
|
|
4871
4977
|
setValueAtPath,
|
|
4872
4978
|
getValueAtPath,
|
|
@@ -4951,7 +5057,7 @@ function errorsEqual(a, b) {
|
|
|
4951
5057
|
}
|
|
4952
5058
|
return true;
|
|
4953
5059
|
}
|
|
4954
|
-
function diffBlankPaths
|
|
5060
|
+
function diffBlankPaths(prev, curr) {
|
|
4955
5061
|
const added = [];
|
|
4956
5062
|
const removed = [];
|
|
4957
5063
|
for (const k of curr) if (!prev.has(k)) added.push(k);
|
|
@@ -5039,7 +5145,7 @@ function createHistoryModule(state, config) {
|
|
|
5039
5145
|
diffAndApply(prevSnap.form, newSnap.form, [], (p) => formPatches.push(p));
|
|
5040
5146
|
const prevBlankSet = new Set(prevSnap.blankPaths);
|
|
5041
5147
|
const currBlankSet = new Set(newSnap.blankPaths);
|
|
5042
|
-
const blankDiff = diffBlankPaths
|
|
5148
|
+
const blankDiff = diffBlankPaths(prevBlankSet, currBlankSet);
|
|
5043
5149
|
const delta = {
|
|
5044
5150
|
formPatches,
|
|
5045
5151
|
blankPathsAdded: blankDiff.added,
|
|
@@ -5106,643 +5212,6 @@ function createHistoryModule(state, config) {
|
|
|
5106
5212
|
};
|
|
5107
5213
|
}
|
|
5108
5214
|
|
|
5109
|
-
function wirePersistence(state, config) {
|
|
5110
|
-
let fingerprint;
|
|
5111
|
-
try {
|
|
5112
|
-
fingerprint = hashStableString(state.schema.fingerprint());
|
|
5113
|
-
} catch (err) {
|
|
5114
|
-
if (paths.__DEV__) {
|
|
5115
|
-
console.warn(
|
|
5116
|
-
`[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.`
|
|
5117
|
-
);
|
|
5118
|
-
}
|
|
5119
|
-
fingerprint = "unfingerprinted";
|
|
5120
|
-
}
|
|
5121
|
-
const base = resolveStorageKeyBase(config, state.formKey);
|
|
5122
|
-
const key = `${base}:${fingerprint}`;
|
|
5123
|
-
const debounceMs = normalizeNumericOption({
|
|
5124
|
-
value: config.debounceMs ?? DEFAULT_PERSISTENCE_DEBOUNCE_MS,
|
|
5125
|
-
source: "useForm.persist.debounceMs",
|
|
5126
|
-
allowInfinity: false,
|
|
5127
|
-
min: 0,
|
|
5128
|
-
defaultValue: DEFAULT_PERSISTENCE_DEBOUNCE_MS
|
|
5129
|
-
});
|
|
5130
|
-
const include = config.include ?? "form";
|
|
5131
|
-
const clearOnSubmitSuccess = config.clearOnSubmitSuccess ?? true;
|
|
5132
|
-
const adapterPromise = getStorageAdapter(config.storage);
|
|
5133
|
-
let disposed = false;
|
|
5134
|
-
const isDisposed = () => disposed;
|
|
5135
|
-
let inFlightFinalFlush = null;
|
|
5136
|
-
let pendingOptedInPaths = null;
|
|
5137
|
-
const writer = createDebouncedWriter(async () => {
|
|
5138
|
-
const optedInPaths = pendingOptedInPaths ?? new Set(state.persistOptIns.optedInPaths());
|
|
5139
|
-
pendingOptedInPaths = null;
|
|
5140
|
-
const adapter = await adapterPromise;
|
|
5141
|
-
if (isDisposed()) return;
|
|
5142
|
-
if (optedInPaths.size === 0) {
|
|
5143
|
-
await adapter.removeItem(key);
|
|
5144
|
-
return;
|
|
5145
|
-
}
|
|
5146
|
-
const rawForm = vue.toRaw(state.form.value);
|
|
5147
|
-
const filteredForm = stripUnacknowledgedSensitiveLeaves(
|
|
5148
|
-
pluckPaths(rawForm, optedInPaths),
|
|
5149
|
-
optedInPaths,
|
|
5150
|
-
state.isSensitivePath
|
|
5151
|
-
);
|
|
5152
|
-
const filteredSchemaErrors = filterErrorsByPaths(state.schemaErrors, optedInPaths);
|
|
5153
|
-
const filteredUserErrors = filterErrorsByPaths(state.userErrors, optedInPaths);
|
|
5154
|
-
const filteredTransientEmpty = /* @__PURE__ */ new Set();
|
|
5155
|
-
for (const tk of state.blankPaths) {
|
|
5156
|
-
if (optedInPaths.has(tk)) filteredTransientEmpty.add(tk);
|
|
5157
|
-
}
|
|
5158
|
-
const payload = buildPersistedPayload(
|
|
5159
|
-
filteredForm,
|
|
5160
|
-
include,
|
|
5161
|
-
filteredSchemaErrors,
|
|
5162
|
-
filteredUserErrors,
|
|
5163
|
-
filteredTransientEmpty
|
|
5164
|
-
);
|
|
5165
|
-
await adapter.setItem(key, payload);
|
|
5166
|
-
}, debounceMs);
|
|
5167
|
-
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
5168
|
-
if (isDisposed() || inFlightFinalFlush !== null) return;
|
|
5169
|
-
if (meta?.crossTab === true) return;
|
|
5170
|
-
if (meta?.persist !== true) return;
|
|
5171
|
-
pendingOptedInPaths = new Set(state.persistOptIns.optedInPaths());
|
|
5172
|
-
writer.schedule();
|
|
5173
|
-
});
|
|
5174
|
-
const unsubscribeSuccess = clearOnSubmitSuccess ? state.onSubmitSuccess(() => {
|
|
5175
|
-
if (isDisposed()) return;
|
|
5176
|
-
void (async () => {
|
|
5177
|
-
await writer.flush();
|
|
5178
|
-
if (isDisposed()) return;
|
|
5179
|
-
const adapter = await adapterPromise;
|
|
5180
|
-
if (isDisposed()) return;
|
|
5181
|
-
await adapter.removeItem(key);
|
|
5182
|
-
})();
|
|
5183
|
-
}) : () => void 0;
|
|
5184
|
-
const handlePageHide = () => {
|
|
5185
|
-
if (isDisposed()) return;
|
|
5186
|
-
void writer.flush();
|
|
5187
|
-
};
|
|
5188
|
-
if (typeof window !== "undefined") {
|
|
5189
|
-
window.addEventListener("pagehide", handlePageHide);
|
|
5190
|
-
}
|
|
5191
|
-
void (async () => {
|
|
5192
|
-
const adapter = await adapterPromise;
|
|
5193
|
-
if (isDisposed()) return;
|
|
5194
|
-
void cleanupOrphanKeys(adapter, base, key);
|
|
5195
|
-
try {
|
|
5196
|
-
const raw = await adapter.getItem(key);
|
|
5197
|
-
const payload = readPersistedPayload(raw);
|
|
5198
|
-
if (payload === null) {
|
|
5199
|
-
if (raw !== null && raw !== void 0) {
|
|
5200
|
-
await adapter.removeItem(key);
|
|
5201
|
-
}
|
|
5202
|
-
return;
|
|
5203
|
-
}
|
|
5204
|
-
if (isDisposed()) return;
|
|
5205
|
-
const merged = mergeSparseHydration(
|
|
5206
|
-
vue.toRaw(state.form.value),
|
|
5207
|
-
payload.data.form,
|
|
5208
|
-
state.schema
|
|
5209
|
-
);
|
|
5210
|
-
state.applyFormReplacement(merged, { hydration: true });
|
|
5211
|
-
const persistedLeafPaths = collectPersistedLeafPaths(payload.data.form);
|
|
5212
|
-
for (const k of persistedLeafPaths) {
|
|
5213
|
-
state.blankPaths.delete(k);
|
|
5214
|
-
state.originalBlankPaths.delete(k);
|
|
5215
|
-
}
|
|
5216
|
-
for (const k of payload.data.blankPaths ?? []) {
|
|
5217
|
-
state.blankPaths.add(k);
|
|
5218
|
-
state.originalBlankPaths.add(k);
|
|
5219
|
-
}
|
|
5220
|
-
if (include === "form+errors") {
|
|
5221
|
-
if (payload.data.schemaErrors !== void 0) {
|
|
5222
|
-
const flat = payload.data.schemaErrors.flatMap(([, errs]) => errs);
|
|
5223
|
-
state.setAllSchemaErrors(flat);
|
|
5224
|
-
}
|
|
5225
|
-
if (payload.data.userErrors !== void 0) {
|
|
5226
|
-
const flat = payload.data.userErrors.flatMap(([, errs]) => errs);
|
|
5227
|
-
state.setAllUserErrors(flat);
|
|
5228
|
-
}
|
|
5229
|
-
}
|
|
5230
|
-
state.scheduleFieldValidation(
|
|
5231
|
-
[],
|
|
5232
|
-
true
|
|
5233
|
-
/* immediate */
|
|
5234
|
-
);
|
|
5235
|
-
} catch {
|
|
5236
|
-
}
|
|
5237
|
-
})();
|
|
5238
|
-
async function writePathImmediately(path) {
|
|
5239
|
-
if (isDisposed()) return;
|
|
5240
|
-
await writer.flush();
|
|
5241
|
-
if (isDisposed()) return;
|
|
5242
|
-
const adapter = await adapterPromise;
|
|
5243
|
-
if (isDisposed()) return;
|
|
5244
|
-
const raw = await adapter.getItem(key);
|
|
5245
|
-
const existing = readPersistedPayload(raw);
|
|
5246
|
-
const value = getAtPath(vue.toRaw(state.form.value), path);
|
|
5247
|
-
const nextForm = setAtPath(existing?.data.form ?? {}, path, value);
|
|
5248
|
-
const { key: pathKey } = paths.canonicalizePath(path);
|
|
5249
|
-
const transientSet = new Set(
|
|
5250
|
-
(existing?.data.blankPaths ?? []).filter(
|
|
5251
|
-
(k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
|
|
5252
|
-
)
|
|
5253
|
-
);
|
|
5254
|
-
for (const liveKey of state.blankPaths) {
|
|
5255
|
-
if (liveKey === pathKey || isDescendantPathKey(liveKey, pathKey)) {
|
|
5256
|
-
transientSet.add(liveKey);
|
|
5257
|
-
}
|
|
5258
|
-
}
|
|
5259
|
-
if (include === "form") {
|
|
5260
|
-
await adapter.setItem(
|
|
5261
|
-
key,
|
|
5262
|
-
buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
|
|
5263
|
-
);
|
|
5264
|
-
return;
|
|
5265
|
-
}
|
|
5266
|
-
const schemaMap = new Map(existing?.data.schemaErrors ?? []);
|
|
5267
|
-
const userMap = new Map(existing?.data.userErrors ?? []);
|
|
5268
|
-
const currentSchema = state.schemaErrors.get(pathKey);
|
|
5269
|
-
const currentUser = state.userErrors.get(pathKey);
|
|
5270
|
-
if (currentSchema !== void 0 && currentSchema.length > 0) {
|
|
5271
|
-
schemaMap.set(pathKey, [...currentSchema]);
|
|
5272
|
-
} else {
|
|
5273
|
-
schemaMap.delete(pathKey);
|
|
5274
|
-
}
|
|
5275
|
-
if (currentUser !== void 0 && currentUser.length > 0) {
|
|
5276
|
-
userMap.set(pathKey, [...currentUser]);
|
|
5277
|
-
} else {
|
|
5278
|
-
userMap.delete(pathKey);
|
|
5279
|
-
}
|
|
5280
|
-
await adapter.setItem(
|
|
5281
|
-
key,
|
|
5282
|
-
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5283
|
-
);
|
|
5284
|
-
}
|
|
5285
|
-
async function clearPersistedDraft(path) {
|
|
5286
|
-
if (isDisposed()) return;
|
|
5287
|
-
await writer.flush();
|
|
5288
|
-
if (isDisposed()) return;
|
|
5289
|
-
const adapter = await adapterPromise;
|
|
5290
|
-
if (isDisposed()) return;
|
|
5291
|
-
if (path === void 0) {
|
|
5292
|
-
await adapter.removeItem(key);
|
|
5293
|
-
return;
|
|
5294
|
-
}
|
|
5295
|
-
const raw = await adapter.getItem(key);
|
|
5296
|
-
const existing = readPersistedPayload(raw);
|
|
5297
|
-
if (existing === null) return;
|
|
5298
|
-
const nextForm = deleteAtPath(existing.data.form, path);
|
|
5299
|
-
if (isEmptyContainer(nextForm)) {
|
|
5300
|
-
await adapter.removeItem(key);
|
|
5301
|
-
return;
|
|
5302
|
-
}
|
|
5303
|
-
const { key: pathKey } = paths.canonicalizePath(path);
|
|
5304
|
-
const transientSet = new Set(
|
|
5305
|
-
(existing.data.blankPaths ?? []).filter(
|
|
5306
|
-
(k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
|
|
5307
|
-
)
|
|
5308
|
-
);
|
|
5309
|
-
if (include === "form") {
|
|
5310
|
-
await adapter.setItem(
|
|
5311
|
-
key,
|
|
5312
|
-
buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
|
|
5313
|
-
);
|
|
5314
|
-
return;
|
|
5315
|
-
}
|
|
5316
|
-
const schemaErrors = (existing.data.schemaErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5317
|
-
const userErrors = (existing.data.userErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5318
|
-
const schemaMap = new Map(schemaErrors.map(([k, v]) => [k, [...v]]));
|
|
5319
|
-
const userMap = new Map(userErrors.map(([k, v]) => [k, [...v]]));
|
|
5320
|
-
await adapter.setItem(
|
|
5321
|
-
key,
|
|
5322
|
-
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5323
|
-
);
|
|
5324
|
-
}
|
|
5325
|
-
function awaitPendingWrites() {
|
|
5326
|
-
if (inFlightFinalFlush !== null) return inFlightFinalFlush;
|
|
5327
|
-
if (isDisposed()) return Promise.resolve();
|
|
5328
|
-
return writer.flush().catch(() => void 0);
|
|
5329
|
-
}
|
|
5330
|
-
function dispose() {
|
|
5331
|
-
if (isDisposed() || inFlightFinalFlush !== null) return;
|
|
5332
|
-
unsubscribeChange();
|
|
5333
|
-
unsubscribeSuccess();
|
|
5334
|
-
if (typeof window !== "undefined") {
|
|
5335
|
-
window.removeEventListener("pagehide", handlePageHide);
|
|
5336
|
-
}
|
|
5337
|
-
inFlightFinalFlush = writer.flush().catch(() => void 0).finally(() => {
|
|
5338
|
-
disposed = true;
|
|
5339
|
-
inFlightFinalFlush = null;
|
|
5340
|
-
});
|
|
5341
|
-
}
|
|
5342
|
-
return {
|
|
5343
|
-
wiredConfig: config,
|
|
5344
|
-
writePathImmediately,
|
|
5345
|
-
clearPersistedDraft,
|
|
5346
|
-
awaitPendingWrites,
|
|
5347
|
-
dispose
|
|
5348
|
-
};
|
|
5349
|
-
}
|
|
5350
|
-
function isEmptyContainer(value) {
|
|
5351
|
-
if (value === void 0 || value === null) return true;
|
|
5352
|
-
if (Array.isArray(value)) return value.length === 0;
|
|
5353
|
-
if (isPlainRecord(value)) return Object.keys(value).length === 0;
|
|
5354
|
-
return false;
|
|
5355
|
-
}
|
|
5356
|
-
function collectPersistedLeafPaths(form) {
|
|
5357
|
-
const out = [];
|
|
5358
|
-
walk(form, []);
|
|
5359
|
-
return out;
|
|
5360
|
-
function walk(node, prefix) {
|
|
5361
|
-
if (Array.isArray(node)) {
|
|
5362
|
-
for (let i = 0; i < node.length; i++) {
|
|
5363
|
-
walk(node[i], [...prefix, i]);
|
|
5364
|
-
}
|
|
5365
|
-
return;
|
|
5366
|
-
}
|
|
5367
|
-
if (isPlainRecord(node)) {
|
|
5368
|
-
for (const key of Object.keys(node)) {
|
|
5369
|
-
walk(node[key], [...prefix, key]);
|
|
5370
|
-
}
|
|
5371
|
-
return;
|
|
5372
|
-
}
|
|
5373
|
-
if (prefix.length === 0) return;
|
|
5374
|
-
out.push(paths.canonicalizePath(prefix).key);
|
|
5375
|
-
}
|
|
5376
|
-
}
|
|
5377
|
-
function isDescendantPathKey(candidate, ancestor) {
|
|
5378
|
-
if (candidate.length <= ancestor.length) return false;
|
|
5379
|
-
if (!ancestor.endsWith("]")) return false;
|
|
5380
|
-
const childPrefix = `${ancestor.slice(0, -1)},`;
|
|
5381
|
-
return candidate.startsWith(childPrefix);
|
|
5382
|
-
}
|
|
5383
|
-
|
|
5384
|
-
const PROTOCOL_VERSION = 1;
|
|
5385
|
-
const JOIN_COLLECTION_WINDOW_MS = 50;
|
|
5386
|
-
const SNAPSHOT_TIMEOUT_MS = 200;
|
|
5387
|
-
const MAX_LEADER_ATTEMPTS = 3;
|
|
5388
|
-
const SNAPSHOT_RESPONSE_MIN_INTERVAL_MS = 500;
|
|
5389
|
-
function isFileLikeValue(value) {
|
|
5390
|
-
if (typeof File !== "undefined" && value instanceof File) return true;
|
|
5391
|
-
if (typeof Blob !== "undefined" && value instanceof Blob) return true;
|
|
5392
|
-
return false;
|
|
5393
|
-
}
|
|
5394
|
-
function isInboundShapeAcceptable(schema, path, value) {
|
|
5395
|
-
if (isFileLikeValue(value)) return false;
|
|
5396
|
-
let kind;
|
|
5397
|
-
if (Array.isArray(value)) {
|
|
5398
|
-
kind = "array";
|
|
5399
|
-
} else if (value !== null && typeof value === "object" && isPlainRecord(value)) {
|
|
5400
|
-
kind = "object";
|
|
5401
|
-
} else {
|
|
5402
|
-
kind = slimKindOf(value);
|
|
5403
|
-
}
|
|
5404
|
-
const accepted = schema.getSlimPrimitiveTypesAtPath(path);
|
|
5405
|
-
if (!accepted.has(kind)) return false;
|
|
5406
|
-
if (Array.isArray(value)) {
|
|
5407
|
-
for (let i = 0; i < value.length; i++) {
|
|
5408
|
-
if (!isInboundShapeAcceptable(schema, [...path, i], value[i])) return false;
|
|
5409
|
-
}
|
|
5410
|
-
return true;
|
|
5411
|
-
}
|
|
5412
|
-
if (isPlainRecord(value)) {
|
|
5413
|
-
for (const key of Object.keys(value)) {
|
|
5414
|
-
if (!isInboundShapeAcceptable(schema, [...path, key], value[key])) return false;
|
|
5415
|
-
}
|
|
5416
|
-
return true;
|
|
5417
|
-
}
|
|
5418
|
-
return true;
|
|
5419
|
-
}
|
|
5420
|
-
function diffBlankPaths(prev, curr) {
|
|
5421
|
-
const added = [];
|
|
5422
|
-
const removed = [];
|
|
5423
|
-
const prevSet = new Set(prev);
|
|
5424
|
-
for (const k of curr) if (!prevSet.has(k)) added.push(k);
|
|
5425
|
-
for (const k of prev) if (!curr.has(k)) removed.push(k);
|
|
5426
|
-
return { added, removed };
|
|
5427
|
-
}
|
|
5428
|
-
function snapshotForm(form) {
|
|
5429
|
-
return structuralSnapshot(form);
|
|
5430
|
-
}
|
|
5431
|
-
function stripSensitivePathsDeep(value, pathSoFar, isSensitivePath) {
|
|
5432
|
-
if (isFileLikeValue(value)) return void 0;
|
|
5433
|
-
if (value === null || typeof value !== "object") return value;
|
|
5434
|
-
if (Array.isArray(value)) {
|
|
5435
|
-
return value.map((item, i) => stripSensitivePathsDeep(item, [...pathSoFar, i], isSensitivePath));
|
|
5436
|
-
}
|
|
5437
|
-
const proto = Object.getPrototypeOf(value);
|
|
5438
|
-
if (proto !== Object.prototype && proto !== null) return value;
|
|
5439
|
-
const out = {};
|
|
5440
|
-
const src = value;
|
|
5441
|
-
for (const key of Object.keys(src)) {
|
|
5442
|
-
const childPath = [...pathSoFar, key];
|
|
5443
|
-
if (isSensitivePath(childPath)) {
|
|
5444
|
-
safeAssign(out, key, void 0);
|
|
5445
|
-
continue;
|
|
5446
|
-
}
|
|
5447
|
-
safeAssign(out, key, stripSensitivePathsDeep(src[key], childPath, isSensitivePath));
|
|
5448
|
-
}
|
|
5449
|
-
return out;
|
|
5450
|
-
}
|
|
5451
|
-
function isStringArray(value) {
|
|
5452
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
5453
|
-
}
|
|
5454
|
-
function isPatchArray(value) {
|
|
5455
|
-
return Array.isArray(value) && value.every(
|
|
5456
|
-
(p) => p !== null && typeof p === "object" && Array.isArray(p.path)
|
|
5457
|
-
);
|
|
5458
|
-
}
|
|
5459
|
-
function isValidSyncMessage(data) {
|
|
5460
|
-
if (data === null || typeof data !== "object") return false;
|
|
5461
|
-
const m = data;
|
|
5462
|
-
if (m["v"] !== PROTOCOL_VERSION) return false;
|
|
5463
|
-
if (typeof m["senderId"] !== "string") return false;
|
|
5464
|
-
if (typeof m["kind"] !== "string") return false;
|
|
5465
|
-
switch (m["kind"]) {
|
|
5466
|
-
case "hello":
|
|
5467
|
-
case "announce":
|
|
5468
|
-
return true;
|
|
5469
|
-
case "requestSnapshot":
|
|
5470
|
-
return typeof m["targetId"] === "string";
|
|
5471
|
-
case "snapshot":
|
|
5472
|
-
return isStringArray(m["blankPaths"]) && "form" in m;
|
|
5473
|
-
case "patches":
|
|
5474
|
-
return isPatchArray(m["formPatches"]) && isStringArray(m["blankPathsAdded"]) && isStringArray(m["blankPathsRemoved"]);
|
|
5475
|
-
default:
|
|
5476
|
-
return false;
|
|
5477
|
-
}
|
|
5478
|
-
}
|
|
5479
|
-
function generateSenderId() {
|
|
5480
|
-
try {
|
|
5481
|
-
return globalThis.crypto.randomUUID();
|
|
5482
|
-
} catch {
|
|
5483
|
-
return `atta-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;
|
|
5484
|
-
}
|
|
5485
|
-
}
|
|
5486
|
-
function createMultiTabSyncModule(state, channelName, options) {
|
|
5487
|
-
if (typeof BroadcastChannel === "undefined") {
|
|
5488
|
-
return {
|
|
5489
|
-
dispose: () => void 0,
|
|
5490
|
-
lifecycle: () => "established",
|
|
5491
|
-
senderId: "",
|
|
5492
|
-
channelName
|
|
5493
|
-
};
|
|
5494
|
-
}
|
|
5495
|
-
let channel;
|
|
5496
|
-
try {
|
|
5497
|
-
channel = new BroadcastChannel(channelName);
|
|
5498
|
-
} catch {
|
|
5499
|
-
return {
|
|
5500
|
-
dispose: () => void 0,
|
|
5501
|
-
lifecycle: () => "established",
|
|
5502
|
-
senderId: "",
|
|
5503
|
-
channelName
|
|
5504
|
-
};
|
|
5505
|
-
}
|
|
5506
|
-
const senderId = generateSenderId();
|
|
5507
|
-
let lifecycle = "joining";
|
|
5508
|
-
let disposed = false;
|
|
5509
|
-
const peerIds = /* @__PURE__ */ new Set();
|
|
5510
|
-
const lastSnapshotResponseAt = /* @__PURE__ */ new Map();
|
|
5511
|
-
let joinCollectionTimer = null;
|
|
5512
|
-
let snapshotTimeoutTimer = null;
|
|
5513
|
-
let leaderAttempts = 0;
|
|
5514
|
-
let prior = {
|
|
5515
|
-
form: snapshotForm(state.form.value),
|
|
5516
|
-
blankPathsSnapshot: [...state.blankPaths]
|
|
5517
|
-
};
|
|
5518
|
-
function safePost(msg) {
|
|
5519
|
-
if (disposed) return;
|
|
5520
|
-
try {
|
|
5521
|
-
channel.postMessage(msg);
|
|
5522
|
-
} catch {
|
|
5523
|
-
}
|
|
5524
|
-
}
|
|
5525
|
-
function refreshPrior() {
|
|
5526
|
-
prior = {
|
|
5527
|
-
form: snapshotForm(state.form.value),
|
|
5528
|
-
blankPathsSnapshot: [...state.blankPaths]
|
|
5529
|
-
};
|
|
5530
|
-
}
|
|
5531
|
-
function isPathLocallySuppressed(path) {
|
|
5532
|
-
if (options.isSensitivePath(path)) return true;
|
|
5533
|
-
const { key } = paths.canonicalizePath([...path]);
|
|
5534
|
-
if (options.noSyncPaths.has(key)) return true;
|
|
5535
|
-
return false;
|
|
5536
|
-
}
|
|
5537
|
-
function postPatches() {
|
|
5538
|
-
if (lifecycle !== "established") return;
|
|
5539
|
-
const next = snapshotForm(state.form.value);
|
|
5540
|
-
const rawPatches = [];
|
|
5541
|
-
diffAndApply(prior.form, next, [], (p) => rawPatches.push(p));
|
|
5542
|
-
const safePatches = [];
|
|
5543
|
-
for (const p of rawPatches) {
|
|
5544
|
-
if (isPathLocallySuppressed(p.path)) continue;
|
|
5545
|
-
if ("value" in p && isFileLikeValue(p.value)) continue;
|
|
5546
|
-
safePatches.push(p);
|
|
5547
|
-
}
|
|
5548
|
-
const { added, removed } = diffBlankPaths(prior.blankPathsSnapshot, state.blankPaths);
|
|
5549
|
-
if (safePatches.length === 0 && added.length === 0 && removed.length === 0) {
|
|
5550
|
-
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
5551
|
-
return;
|
|
5552
|
-
}
|
|
5553
|
-
safePost({
|
|
5554
|
-
v: PROTOCOL_VERSION,
|
|
5555
|
-
kind: "patches",
|
|
5556
|
-
senderId,
|
|
5557
|
-
formPatches: safePatches,
|
|
5558
|
-
blankPathsAdded: added,
|
|
5559
|
-
blankPathsRemoved: removed
|
|
5560
|
-
});
|
|
5561
|
-
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
5562
|
-
}
|
|
5563
|
-
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
5564
|
-
if (disposed) return;
|
|
5565
|
-
if (lifecycle !== "established") return;
|
|
5566
|
-
if (meta?.crossTab === true) return;
|
|
5567
|
-
if (meta?.hydration === true) {
|
|
5568
|
-
refreshPrior();
|
|
5569
|
-
return;
|
|
5570
|
-
}
|
|
5571
|
-
postPatches();
|
|
5572
|
-
});
|
|
5573
|
-
function applyIncomingForm(form, blankPaths) {
|
|
5574
|
-
state.blankPaths.clear();
|
|
5575
|
-
for (const k of blankPaths) state.blankPaths.add(k);
|
|
5576
|
-
state.applyFormReplacement(form, { crossTab: true, persist: false });
|
|
5577
|
-
refreshPrior();
|
|
5578
|
-
}
|
|
5579
|
-
function handlePatches(msg) {
|
|
5580
|
-
if (lifecycle !== "established") return;
|
|
5581
|
-
const safePatches = [];
|
|
5582
|
-
for (const p of msg.formPatches) {
|
|
5583
|
-
if (!Array.isArray(p.path)) continue;
|
|
5584
|
-
if (isPathLocallySuppressed(p.path)) continue;
|
|
5585
|
-
if ("value" in p && !isInboundShapeAcceptable(state.schema, p.path, p.value)) {
|
|
5586
|
-
continue;
|
|
5587
|
-
}
|
|
5588
|
-
safePatches.push(p);
|
|
5589
|
-
}
|
|
5590
|
-
const safeBlankAdded = [];
|
|
5591
|
-
for (const k of msg.blankPathsAdded) {
|
|
5592
|
-
const segs = paths.canonicalizePath(k).segments;
|
|
5593
|
-
if (isPathLocallySuppressed(segs)) continue;
|
|
5594
|
-
safeBlankAdded.push(k);
|
|
5595
|
-
}
|
|
5596
|
-
const safeBlankRemoved = [];
|
|
5597
|
-
for (const k of msg.blankPathsRemoved) {
|
|
5598
|
-
const segs = paths.canonicalizePath(k).segments;
|
|
5599
|
-
if (isPathLocallySuppressed(segs)) continue;
|
|
5600
|
-
safeBlankRemoved.push(k);
|
|
5601
|
-
}
|
|
5602
|
-
if (safePatches.length === 0 && safeBlankAdded.length === 0 && safeBlankRemoved.length === 0) {
|
|
5603
|
-
return;
|
|
5604
|
-
}
|
|
5605
|
-
const candidate = applyPatchesForward(state.form.value, safePatches);
|
|
5606
|
-
try {
|
|
5607
|
-
options.validateForm(state.form.value);
|
|
5608
|
-
try {
|
|
5609
|
-
options.validateForm(candidate);
|
|
5610
|
-
} catch {
|
|
5611
|
-
return;
|
|
5612
|
-
}
|
|
5613
|
-
} catch {
|
|
5614
|
-
}
|
|
5615
|
-
const nextBlankPaths = new Set(state.blankPaths);
|
|
5616
|
-
for (const k of safeBlankRemoved) nextBlankPaths.delete(k);
|
|
5617
|
-
for (const k of safeBlankAdded) nextBlankPaths.add(k);
|
|
5618
|
-
applyIncomingForm(candidate, [...nextBlankPaths]);
|
|
5619
|
-
}
|
|
5620
|
-
function handleSnapshot(msg) {
|
|
5621
|
-
if (lifecycle !== "joining") return;
|
|
5622
|
-
if (!isInboundShapeAcceptable(state.schema, [], msg.form)) return;
|
|
5623
|
-
if (snapshotTimeoutTimer !== null) {
|
|
5624
|
-
clearTimeout(snapshotTimeoutTimer);
|
|
5625
|
-
snapshotTimeoutTimer = null;
|
|
5626
|
-
}
|
|
5627
|
-
if (joinCollectionTimer !== null) {
|
|
5628
|
-
clearTimeout(joinCollectionTimer);
|
|
5629
|
-
joinCollectionTimer = null;
|
|
5630
|
-
}
|
|
5631
|
-
applyIncomingForm(msg.form, msg.blankPaths);
|
|
5632
|
-
lifecycle = "established";
|
|
5633
|
-
peerIds.clear();
|
|
5634
|
-
}
|
|
5635
|
-
function respondToHello() {
|
|
5636
|
-
safePost({ v: PROTOCOL_VERSION, kind: "announce", senderId });
|
|
5637
|
-
}
|
|
5638
|
-
function respondToSnapshotRequest(requesterId) {
|
|
5639
|
-
const now = Date.now();
|
|
5640
|
-
const last = lastSnapshotResponseAt.get(requesterId);
|
|
5641
|
-
if (last !== void 0 && now - last < SNAPSHOT_RESPONSE_MIN_INTERVAL_MS) return;
|
|
5642
|
-
lastSnapshotResponseAt.set(requesterId, now);
|
|
5643
|
-
const scrubbedForm = stripSensitivePathsDeep(state.form.value, [], options.isSensitivePath);
|
|
5644
|
-
safePost({
|
|
5645
|
-
v: PROTOCOL_VERSION,
|
|
5646
|
-
kind: "snapshot",
|
|
5647
|
-
senderId,
|
|
5648
|
-
form: scrubbedForm,
|
|
5649
|
-
blankPaths: [...state.blankPaths]
|
|
5650
|
-
});
|
|
5651
|
-
}
|
|
5652
|
-
channel.onmessage = (event) => {
|
|
5653
|
-
if (disposed) return;
|
|
5654
|
-
try {
|
|
5655
|
-
const data = event.data;
|
|
5656
|
-
if (!isValidSyncMessage(data)) return;
|
|
5657
|
-
const msg = data;
|
|
5658
|
-
if (msg.senderId === senderId) return;
|
|
5659
|
-
switch (msg.kind) {
|
|
5660
|
-
case "hello":
|
|
5661
|
-
if (lifecycle !== "established") return;
|
|
5662
|
-
respondToHello();
|
|
5663
|
-
break;
|
|
5664
|
-
case "announce":
|
|
5665
|
-
if (lifecycle === "joining") peerIds.add(msg.senderId);
|
|
5666
|
-
break;
|
|
5667
|
-
case "requestSnapshot":
|
|
5668
|
-
if (lifecycle !== "established") return;
|
|
5669
|
-
if (msg.targetId !== senderId) return;
|
|
5670
|
-
respondToSnapshotRequest(msg.senderId);
|
|
5671
|
-
break;
|
|
5672
|
-
case "snapshot":
|
|
5673
|
-
handleSnapshot(msg);
|
|
5674
|
-
break;
|
|
5675
|
-
case "patches":
|
|
5676
|
-
handlePatches(msg);
|
|
5677
|
-
break;
|
|
5678
|
-
}
|
|
5679
|
-
} catch {
|
|
5680
|
-
}
|
|
5681
|
-
};
|
|
5682
|
-
function electLeaderAndRequest() {
|
|
5683
|
-
if (disposed) return;
|
|
5684
|
-
if (peerIds.size === 0) {
|
|
5685
|
-
lifecycle = "established";
|
|
5686
|
-
refreshPrior();
|
|
5687
|
-
return;
|
|
5688
|
-
}
|
|
5689
|
-
const sorted = [...peerIds].sort();
|
|
5690
|
-
const leaderId = sorted[0];
|
|
5691
|
-
peerIds.delete(leaderId);
|
|
5692
|
-
leaderAttempts++;
|
|
5693
|
-
safePost({
|
|
5694
|
-
v: PROTOCOL_VERSION,
|
|
5695
|
-
kind: "requestSnapshot",
|
|
5696
|
-
senderId,
|
|
5697
|
-
targetId: leaderId
|
|
5698
|
-
});
|
|
5699
|
-
snapshotTimeoutTimer = setTimeout(() => {
|
|
5700
|
-
snapshotTimeoutTimer = null;
|
|
5701
|
-
if (disposed) return;
|
|
5702
|
-
if (lifecycle === "established") return;
|
|
5703
|
-
if (leaderAttempts >= MAX_LEADER_ATTEMPTS || peerIds.size === 0) {
|
|
5704
|
-
lifecycle = "established";
|
|
5705
|
-
refreshPrior();
|
|
5706
|
-
return;
|
|
5707
|
-
}
|
|
5708
|
-
electLeaderAndRequest();
|
|
5709
|
-
}, SNAPSHOT_TIMEOUT_MS);
|
|
5710
|
-
}
|
|
5711
|
-
function joinFlow() {
|
|
5712
|
-
safePost({ v: PROTOCOL_VERSION, kind: "hello", senderId });
|
|
5713
|
-
joinCollectionTimer = setTimeout(() => {
|
|
5714
|
-
joinCollectionTimer = null;
|
|
5715
|
-
if (disposed) return;
|
|
5716
|
-
if (lifecycle === "established") return;
|
|
5717
|
-
electLeaderAndRequest();
|
|
5718
|
-
}, JOIN_COLLECTION_WINDOW_MS);
|
|
5719
|
-
}
|
|
5720
|
-
joinFlow();
|
|
5721
|
-
return {
|
|
5722
|
-
dispose: () => {
|
|
5723
|
-
if (disposed) return;
|
|
5724
|
-
disposed = true;
|
|
5725
|
-
if (joinCollectionTimer !== null) {
|
|
5726
|
-
clearTimeout(joinCollectionTimer);
|
|
5727
|
-
joinCollectionTimer = null;
|
|
5728
|
-
}
|
|
5729
|
-
if (snapshotTimeoutTimer !== null) {
|
|
5730
|
-
clearTimeout(snapshotTimeoutTimer);
|
|
5731
|
-
snapshotTimeoutTimer = null;
|
|
5732
|
-
}
|
|
5733
|
-
unsubscribeChange();
|
|
5734
|
-
try {
|
|
5735
|
-
channel.close();
|
|
5736
|
-
} catch {
|
|
5737
|
-
}
|
|
5738
|
-
},
|
|
5739
|
-
lifecycle: () => lifecycle,
|
|
5740
|
-
senderId,
|
|
5741
|
-
channelName
|
|
5742
|
-
};
|
|
5743
|
-
}
|
|
5744
|
-
const MULTI_TAB_SYNC_MODULE_KEY = "multiTabSync";
|
|
5745
|
-
|
|
5746
5215
|
const warned = /* @__PURE__ */ new Set();
|
|
5747
5216
|
function warnOnceInsecureContext(feature) {
|
|
5748
5217
|
if (!paths.__DEV__) return;
|
|
@@ -5802,8 +5271,10 @@ function useAbstractForm(configuration, options) {
|
|
|
5802
5271
|
}
|
|
5803
5272
|
const existing = registry.forms.get(key);
|
|
5804
5273
|
if (paths.__DEV__ && existing !== void 0) {
|
|
5805
|
-
|
|
5806
|
-
|
|
5274
|
+
void import('../chunks/dev-key-collision-warnings.cjs').then((m) => {
|
|
5275
|
+
void m.warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
|
|
5276
|
+
m.warnOnPersistDivergence(key, existing, configuration.persist);
|
|
5277
|
+
});
|
|
5807
5278
|
}
|
|
5808
5279
|
const hadPendingHydration = registry.pendingHydration.has(key);
|
|
5809
5280
|
const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
|
|
@@ -5844,10 +5315,33 @@ function useAbstractForm(configuration, options) {
|
|
|
5844
5315
|
} else {
|
|
5845
5316
|
const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
|
|
5846
5317
|
void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
|
|
5847
|
-
const
|
|
5848
|
-
|
|
5849
|
-
state.
|
|
5850
|
-
|
|
5318
|
+
const adapterPromise = getStorageAdapter(resolvedPersist.storage);
|
|
5319
|
+
let persistDisposed = false;
|
|
5320
|
+
state.registerCleanup(() => {
|
|
5321
|
+
persistDisposed = true;
|
|
5322
|
+
});
|
|
5323
|
+
const ready = (async () => {
|
|
5324
|
+
try {
|
|
5325
|
+
const [{ wirePersistence }, fingerprintToken] = await Promise.all([
|
|
5326
|
+
import('../chunks/wire-persistence.cjs'),
|
|
5327
|
+
resolvePersistFingerprintToken(state)
|
|
5328
|
+
]);
|
|
5329
|
+
if (persistDisposed) return void 0;
|
|
5330
|
+
const persistenceModule = wirePersistence(
|
|
5331
|
+
state,
|
|
5332
|
+
resolvedPersist,
|
|
5333
|
+
adapterPromise,
|
|
5334
|
+
fingerprintToken
|
|
5335
|
+
);
|
|
5336
|
+
state.registerDrain(() => persistenceModule.awaitPendingWrites());
|
|
5337
|
+
state.registerCleanup(() => persistenceModule.dispose());
|
|
5338
|
+
return persistenceModule;
|
|
5339
|
+
} catch {
|
|
5340
|
+
return void 0;
|
|
5341
|
+
}
|
|
5342
|
+
})();
|
|
5343
|
+
const persistenceHandle = { config: resolvedPersist, ready };
|
|
5344
|
+
state.modules.set(PERSISTENCE_MODULE_KEY, persistenceHandle);
|
|
5851
5345
|
}
|
|
5852
5346
|
} else {
|
|
5853
5347
|
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
@@ -5857,27 +5351,31 @@ function useAbstractForm(configuration, options) {
|
|
|
5857
5351
|
const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
|
|
5858
5352
|
const secureContext = isSecureContext();
|
|
5859
5353
|
if (hasBroadcastChannel && secureContext) {
|
|
5860
|
-
let
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
}
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5354
|
+
let formDisposed = false;
|
|
5355
|
+
state.registerCleanup(() => {
|
|
5356
|
+
formDisposed = true;
|
|
5357
|
+
});
|
|
5358
|
+
void (async () => {
|
|
5359
|
+
try {
|
|
5360
|
+
const [{ createMultiTabSyncModule, MULTI_TAB_SYNC_MODULE_KEY }, fingerprint] = await Promise.all([import('../chunks/multi-tab-sync.cjs'), state.schema.fingerprint()]);
|
|
5361
|
+
if (formDisposed) return;
|
|
5362
|
+
const channelName = `attaform:sync:${state.formKey}:${hashStableString(fingerprint)}`;
|
|
5363
|
+
const syncModule = createMultiTabSyncModule(state, channelName, {
|
|
5364
|
+
isSensitivePath: state.isSensitivePath,
|
|
5365
|
+
noSyncPaths: state.noSyncPaths,
|
|
5366
|
+
validateForm: (form) => {
|
|
5367
|
+
const result = state.schema.validateAtPath(form, void 0, { sync: true });
|
|
5368
|
+
if (result instanceof Promise) return;
|
|
5369
|
+
if (!result.success) {
|
|
5370
|
+
throw new Error("attaform multi-tab sync: post-apply schema validation failed");
|
|
5371
|
+
}
|
|
5875
5372
|
}
|
|
5876
|
-
}
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5373
|
+
});
|
|
5374
|
+
state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
|
|
5375
|
+
state.registerCleanup(() => syncModule.dispose());
|
|
5376
|
+
} catch {
|
|
5377
|
+
}
|
|
5378
|
+
})();
|
|
5881
5379
|
} else if (hasBroadcastChannel && !secureContext) {
|
|
5882
5380
|
warnOnceInsecureContext("multiTab");
|
|
5883
5381
|
}
|
|
@@ -6038,55 +5536,17 @@ function resolveFormKey(key) {
|
|
|
6038
5536
|
}
|
|
6039
5537
|
return `${ANONYMOUS_FORM_KEY_PREFIX}${anonCounter++}`;
|
|
6040
5538
|
}
|
|
6041
|
-
function
|
|
6042
|
-
let existingFp;
|
|
6043
|
-
let incomingFp;
|
|
5539
|
+
async function resolvePersistFingerprintToken(state) {
|
|
6044
5540
|
try {
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
return;
|
|
6053
|
-
}
|
|
6054
|
-
if (existingFp === incomingFp) return;
|
|
6055
|
-
console.warn(
|
|
6056
|
-
`[attaform] useForm() calls with key "${key}" use different schemas; first wins, second is ignored. Use identical schemas or unique keys.
|
|
6057
|
-
existing: ${existingFp}
|
|
6058
|
-
incoming: ${incomingFp}`
|
|
6059
|
-
);
|
|
6060
|
-
}
|
|
6061
|
-
function warnOnPersistDivergence(key, existing, incomingPersist) {
|
|
6062
|
-
if (incomingPersist === void 0) return;
|
|
6063
|
-
const wired = existing.modules.get(PERSISTENCE_MODULE_KEY);
|
|
6064
|
-
const incomingNormalized = normalizePersistConfig(incomingPersist);
|
|
6065
|
-
if (wired === void 0) {
|
|
6066
|
-
console.warn(
|
|
6067
|
-
`[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.`
|
|
6068
|
-
);
|
|
6069
|
-
return;
|
|
5541
|
+
return hashStableString(await state.schema.fingerprint());
|
|
5542
|
+
} catch (err) {
|
|
5543
|
+
if (paths.__DEV__) {
|
|
5544
|
+
console.warn(
|
|
5545
|
+
`[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.`
|
|
5546
|
+
);
|
|
5547
|
+
}
|
|
5548
|
+
return "unfingerprinted";
|
|
6070
5549
|
}
|
|
6071
|
-
if (persistConfigsEquivalent(wired.wiredConfig, incomingNormalized)) return;
|
|
6072
|
-
console.warn(
|
|
6073
|
-
`[attaform] useForm({ key: "${key}" }) passed a persist config that differs from the first useForm({ key }) call's; first wins, this one is ignored.
|
|
6074
|
-
wired: ${describePersist(wired.wiredConfig)}
|
|
6075
|
-
incoming: ${describePersist(incomingNormalized)}`
|
|
6076
|
-
);
|
|
6077
|
-
}
|
|
6078
|
-
function persistConfigsEquivalent(a, b) {
|
|
6079
|
-
if (a.storage !== b.storage) return false;
|
|
6080
|
-
if ((a.key ?? void 0) !== (b.key ?? void 0)) return false;
|
|
6081
|
-
if ((a.debounceMs ?? void 0) !== (b.debounceMs ?? void 0)) return false;
|
|
6082
|
-
return true;
|
|
6083
|
-
}
|
|
6084
|
-
function describePersist(config) {
|
|
6085
|
-
const storage = typeof config.storage === "string" ? config.storage : "custom-adapter";
|
|
6086
|
-
const parts = [`storage=${storage}`];
|
|
6087
|
-
if (config.key !== void 0) parts.push(`key=${config.key}`);
|
|
6088
|
-
if (config.debounceMs !== void 0) parts.push(`debounceMs=${config.debounceMs}`);
|
|
6089
|
-
return `{ ${parts.join(", ")} }`;
|
|
6090
5550
|
}
|
|
6091
5551
|
const warnedAnonPersistKeys = /* @__PURE__ */ new Set();
|
|
6092
5552
|
function enforceAnonPersistRule(formKey, ssr) {
|
|
@@ -6254,7 +5714,7 @@ function buildNoopWizardSchema(formKey) {
|
|
|
6254
5714
|
formKey
|
|
6255
5715
|
};
|
|
6256
5716
|
return {
|
|
6257
|
-
fingerprint: () => NOOP_FINGERPRINT,
|
|
5717
|
+
fingerprint: () => Promise.resolve(NOOP_FINGERPRINT),
|
|
6258
5718
|
getDefaultValues: () => defaultsResponse,
|
|
6259
5719
|
getDefaultAtPath: () => void 0,
|
|
6260
5720
|
getEmptyValueAtPath: () => void 0,
|
|
@@ -6971,7 +6431,11 @@ function useWizard(options) {
|
|
|
6971
6431
|
}
|
|
6972
6432
|
for (const key of results.keys()) {
|
|
6973
6433
|
const store = registry.forms.get(key);
|
|
6974
|
-
if (store !== void 0)
|
|
6434
|
+
if (store !== void 0) {
|
|
6435
|
+
store.submissionAttempts.value += 1;
|
|
6436
|
+
store.cancelFieldValidation();
|
|
6437
|
+
store.displayEngine.clear();
|
|
6438
|
+
}
|
|
6975
6439
|
}
|
|
6976
6440
|
submissionAttempts.value += 1;
|
|
6977
6441
|
const errors = collectErrors(results);
|
|
@@ -7205,9 +6669,16 @@ function warnIfAmbientWizardProviderHadDuplicates() {
|
|
|
7205
6669
|
}
|
|
7206
6670
|
|
|
7207
6671
|
exports.AttaformErrorCode = AttaformErrorCode;
|
|
6672
|
+
exports.DEFAULT_PERSISTENCE_DEBOUNCE_MS = DEFAULT_PERSISTENCE_DEBOUNCE_MS;
|
|
6673
|
+
exports.DEFAULT_TIMINGS = DEFAULT_TIMINGS;
|
|
6674
|
+
exports.PERSISTENCE_MODULE_KEY = PERSISTENCE_MODULE_KEY;
|
|
6675
|
+
exports.applyPatchesForward = applyPatchesForward;
|
|
6676
|
+
exports.cleanupOrphanKeys = cleanupOrphanKeys;
|
|
7208
6677
|
exports.defaultCoercionRules = defaultCoercionRules;
|
|
7209
6678
|
exports.defaultDisplayState = defaultDisplayState;
|
|
7210
6679
|
exports.defineCoercion = defineCoercion;
|
|
6680
|
+
exports.deleteAtPath = deleteAtPath;
|
|
6681
|
+
exports.diffAndApply = diffAndApply;
|
|
7211
6682
|
exports.getAtPath = getAtPath;
|
|
7212
6683
|
exports.humanize = humanize;
|
|
7213
6684
|
exports.injectForm = injectForm;
|
|
@@ -7215,12 +6686,17 @@ exports.injectWizard = injectWizard;
|
|
|
7215
6686
|
exports.isPlainRecord = isPlainRecord;
|
|
7216
6687
|
exports.isUnset = isUnset;
|
|
7217
6688
|
exports.lazy = lazy;
|
|
6689
|
+
exports.makeDefaultDisplayState = makeDefaultDisplayState;
|
|
6690
|
+
exports.mergeSparseHydration = mergeSparseHydration;
|
|
7218
6691
|
exports.normalizeNumericOption = normalizeNumericOption;
|
|
6692
|
+
exports.normalizePersistConfig = normalizePersistConfig;
|
|
6693
|
+
exports.resolveStorageKeyBase = resolveStorageKeyBase;
|
|
7219
6694
|
exports.safeAssign = safeAssign;
|
|
7220
6695
|
exports.safeOwnRead = safeOwnRead;
|
|
7221
6696
|
exports.setAtPath = setAtPath;
|
|
7222
6697
|
exports.slimKindOf = slimKindOf;
|
|
6698
|
+
exports.structuralSnapshot = structuralSnapshot;
|
|
7223
6699
|
exports.unset = unset;
|
|
7224
6700
|
exports.useAbstractForm = useAbstractForm;
|
|
7225
6701
|
exports.useWizard = useWizard;
|
|
7226
|
-
//# sourceMappingURL=attaform.
|
|
6702
|
+
//# sourceMappingURL=attaform.BUszFoKq.cjs.map
|