attaform 0.20.2 → 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/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 +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.ceGEAEMk.d.ts → attaform.7lzO9pdM.d.mts} +95 -1
- package/dist/shared/{attaform.99cfHcIt.d.cts → attaform.B1nyO4ec.d.cts} +82 -30
- package/dist/shared/{attaform.99cfHcIt.d.mts → attaform.B1nyO4ec.d.mts} +82 -30
- package/dist/shared/{attaform.99cfHcIt.d.ts → attaform.B1nyO4ec.d.ts} +82 -30
- package/dist/shared/{attaform.z5j3LwJz.cjs → attaform.BA3vRDos.cjs} +3 -3
- package/dist/shared/attaform.BA3vRDos.cjs.map +1 -0
- package/dist/shared/{attaform.BXinSW2T.d.mts → 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.DN5CvZrg.d.ts → attaform.BK1RE2ha.d.ts} +1 -1
- package/dist/shared/{attaform.CywE4y8x.d.cts → 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.CwLjUqmQ.cjs → attaform.BUszFoKq.cjs} +383 -911
- package/dist/shared/attaform.BUszFoKq.cjs.map +1 -0
- package/dist/shared/{attaform.C5aYC_T8.mjs → attaform.BnK_bfcb.mjs} +39 -392
- package/dist/shared/attaform.BnK_bfcb.mjs.map +1 -0
- package/dist/shared/{attaform.DAKrGhxc.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.D2SCCd4O.cjs → attaform.CEf6wYfD.cjs} +2 -2
- package/dist/shared/{attaform.D2SCCd4O.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.sWm8B15V.d.mts → attaform.CRsXyy-Y.d.ts} +95 -1
- package/dist/shared/{attaform.Dt7dEcHk.mjs → attaform.CkjTapyq.mjs} +89 -405
- package/dist/shared/attaform.CkjTapyq.mjs.map +1 -0
- package/dist/shared/{attaform.tiWEVznj.mjs → attaform.DSqO6Db7.mjs} +372 -912
- 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.DbRgDFa7.d.cts → 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.Cd4AOfwu.cjs → attaform.PnqML3xW.cjs} +68 -402
- package/dist/shared/attaform.PnqML3xW.cjs.map +1 -0
- package/dist/shared/{attaform.QG5TG8lB.mjs → attaform.Y_Mgg0Yp.mjs} +3 -3
- package/dist/shared/attaform.Y_Mgg0Yp.mjs.map +1 -0
- package/dist/shared/{attaform.B_hph5AE.cjs → attaform._rsCZy2j.cjs} +172 -20
- package/dist/shared/attaform._rsCZy2j.cjs.map +1 -0
- package/dist/shared/{attaform.CnrxbkB6.mjs → attaform.ezb5Nh2t.mjs} +2 -2
- package/dist/shared/{attaform.CnrxbkB6.mjs.map → attaform.ezb5Nh2t.mjs.map} +1 -1
- package/dist/shared/{attaform.BGk8cfw2.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 +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.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);
|
|
@@ -2643,7 +2614,12 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2643
2614
|
return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
|
|
2644
2615
|
};
|
|
2645
2616
|
const getFormMetaBase = () => {
|
|
2646
|
-
const rootBase = buildContainerFieldStateBase(
|
|
2617
|
+
const { base: rootBase } = buildContainerFieldStateBase(
|
|
2618
|
+
state,
|
|
2619
|
+
paths.ROOT_PATH,
|
|
2620
|
+
paths.ROOT_PATH_KEY,
|
|
2621
|
+
formInstanceId
|
|
2622
|
+
);
|
|
2647
2623
|
return {
|
|
2648
2624
|
...rootBase,
|
|
2649
2625
|
submitting: state.submitting.value,
|
|
@@ -2978,7 +2954,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2978
2954
|
instanceId: formInstanceId
|
|
2979
2955
|
})
|
|
2980
2956
|
);
|
|
2981
|
-
const
|
|
2957
|
+
const persistenceHandle = state.modules.get(PERSISTENCE_MODULE_KEY);
|
|
2982
2958
|
const reset = (nextDefaultValues) => {
|
|
2983
2959
|
if (nextDefaultValues === void 0) {
|
|
2984
2960
|
state.reset();
|
|
@@ -2993,15 +2969,15 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2993
2969
|
state.originalBlankPaths.add(pathKey);
|
|
2994
2970
|
}
|
|
2995
2971
|
}
|
|
2996
|
-
if (
|
|
2997
|
-
void
|
|
2972
|
+
if (persistenceHandle !== void 0) {
|
|
2973
|
+
void persistenceHandle.ready.then((m) => m?.clearPersistedDraft()).catch(() => void 0);
|
|
2998
2974
|
}
|
|
2999
2975
|
};
|
|
3000
2976
|
const resetField = (pathInput) => {
|
|
3001
2977
|
const segments = paths.canonicalizePath(pathInput).segments;
|
|
3002
2978
|
state.resetField(segments);
|
|
3003
|
-
if (
|
|
3004
|
-
void
|
|
2979
|
+
if (persistenceHandle !== void 0) {
|
|
2980
|
+
void persistenceHandle.ready.then((m) => m?.clearPersistedDraft(segments)).catch(() => void 0);
|
|
3005
2981
|
}
|
|
3006
2982
|
};
|
|
3007
2983
|
function clear(pathInput) {
|
|
@@ -3019,10 +2995,14 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3019
2995
|
)) {
|
|
3020
2996
|
return;
|
|
3021
2997
|
}
|
|
2998
|
+
if (persistenceHandle === void 0) return;
|
|
2999
|
+
const persistence = await persistenceHandle.ready;
|
|
3022
3000
|
if (persistence === void 0) return;
|
|
3023
3001
|
await persistence.writePathImmediately(segments);
|
|
3024
3002
|
};
|
|
3025
3003
|
const clearPersistedDraft = async (pathInput) => {
|
|
3004
|
+
if (persistenceHandle === void 0) return;
|
|
3005
|
+
const persistence = await persistenceHandle.ready;
|
|
3026
3006
|
if (persistence === void 0) return;
|
|
3027
3007
|
if (pathInput === void 0) {
|
|
3028
3008
|
await persistence.clearPersistedDraft();
|
|
@@ -3205,6 +3185,93 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3205
3185
|
};
|
|
3206
3186
|
}
|
|
3207
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
|
+
|
|
3208
3275
|
function applyDuStubs(schema, data, options = {}) {
|
|
3209
3276
|
const warned = options.warn === true ? /* @__PURE__ */ new Set() : void 0;
|
|
3210
3277
|
return walkDuStubs(schema, data, options.basePath ?? [], warned);
|
|
@@ -3543,6 +3610,7 @@ function createArrayBookkeeping(deps) {
|
|
|
3543
3610
|
blankPaths,
|
|
3544
3611
|
originalBlankPaths,
|
|
3545
3612
|
fieldValidationCounts,
|
|
3613
|
+
fieldValidatingSince,
|
|
3546
3614
|
fieldValidationState,
|
|
3547
3615
|
schemaErrors,
|
|
3548
3616
|
activeValidations,
|
|
@@ -3568,6 +3636,7 @@ function createArrayBookkeeping(deps) {
|
|
|
3568
3636
|
}));
|
|
3569
3637
|
migrateSetSubtree(blankPaths, arrayPath, remap);
|
|
3570
3638
|
migrateSetSubtree(originalBlankPaths, arrayPath, remap);
|
|
3639
|
+
migrateMapSubtree(fieldValidatingSince, arrayPath, remap, (since) => since);
|
|
3571
3640
|
migrateMapSubtree(fieldValidationCounts, arrayPath, remap, (count) => count);
|
|
3572
3641
|
arrayIdentity.applyRemap(arrayPath, remap);
|
|
3573
3642
|
}
|
|
@@ -3627,6 +3696,18 @@ function isHydratedFieldRecord(value) {
|
|
|
3627
3696
|
const r = value;
|
|
3628
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";
|
|
3629
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
|
+
}
|
|
3630
3711
|
function isHydratedValidationErrorArray(value) {
|
|
3631
3712
|
if (!Array.isArray(value)) return false;
|
|
3632
3713
|
for (const entry of value) {
|
|
@@ -3754,6 +3835,9 @@ function createFormStore(options) {
|
|
|
3754
3835
|
const resolvedIsSensitivePath = options.isSensitivePath ?? paths.isSensitivePath;
|
|
3755
3836
|
const cleanupHooks = [];
|
|
3756
3837
|
const modules = /* @__PURE__ */ new Map();
|
|
3838
|
+
const fieldValidatingSince = vue.reactive(/* @__PURE__ */ new Map());
|
|
3839
|
+
const displayEngine = createDisplayEngine(ssr);
|
|
3840
|
+
registerCleanup(() => displayEngine.dispose());
|
|
3757
3841
|
const completedConstraints = defaultValues === void 0 ? void 0 : mergeStructural(schema, [], defaultValues);
|
|
3758
3842
|
const schemaResponse = schema.getDefaultValues({
|
|
3759
3843
|
useDefaultSchemaValues: true,
|
|
@@ -3882,12 +3966,18 @@ function createFormStore(options) {
|
|
|
3882
3966
|
}
|
|
3883
3967
|
const fieldValidationCounts = vue.reactive(/* @__PURE__ */ new Map());
|
|
3884
3968
|
function incFieldValidation(key) {
|
|
3885
|
-
|
|
3969
|
+
fieldValidatingSince.set(key, ssr ? 0 : Date.now());
|
|
3970
|
+
const prevCount = fieldValidationCounts.get(key) ?? 0;
|
|
3971
|
+
fieldValidationCounts.set(key, prevCount + 1);
|
|
3886
3972
|
}
|
|
3887
3973
|
function decFieldValidation(key) {
|
|
3888
3974
|
const next = (fieldValidationCounts.get(key) ?? 0) - 1;
|
|
3889
|
-
if (next <= 0)
|
|
3890
|
-
|
|
3975
|
+
if (next <= 0) {
|
|
3976
|
+
fieldValidationCounts.delete(key);
|
|
3977
|
+
fieldValidatingSince.delete(key);
|
|
3978
|
+
} else {
|
|
3979
|
+
fieldValidationCounts.set(key, next);
|
|
3980
|
+
}
|
|
3891
3981
|
}
|
|
3892
3982
|
const initStamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3893
3983
|
diffAndApply({}, schemaInitialData, [], (patch) => {
|
|
@@ -3975,6 +4065,7 @@ function createFormStore(options) {
|
|
|
3975
4065
|
blankPaths,
|
|
3976
4066
|
originalBlankPaths,
|
|
3977
4067
|
fieldValidationCounts,
|
|
4068
|
+
fieldValidatingSince,
|
|
3978
4069
|
fieldValidationState,
|
|
3979
4070
|
schemaErrors,
|
|
3980
4071
|
activeValidations,
|
|
@@ -4242,7 +4333,7 @@ function createFormStore(options) {
|
|
|
4242
4333
|
prev.controller.abort();
|
|
4243
4334
|
}
|
|
4244
4335
|
const controller = new AbortController();
|
|
4245
|
-
const fresh = { controller, timer: null, settled: false };
|
|
4336
|
+
const fresh = { controller, timer: null, settled: false, released: false };
|
|
4246
4337
|
fieldValidationState.set(key, fresh);
|
|
4247
4338
|
const myEpoch = ++scheduleEpoch;
|
|
4248
4339
|
const run = () => {
|
|
@@ -4280,8 +4371,10 @@ function createFormStore(options) {
|
|
|
4280
4371
|
applySchemaErrorsForSubtree(scopePath ?? [], restamped);
|
|
4281
4372
|
}).catch(() => {
|
|
4282
4373
|
}).finally(() => {
|
|
4283
|
-
|
|
4284
|
-
|
|
4374
|
+
if (!fresh.released) {
|
|
4375
|
+
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
4376
|
+
decFieldValidation(key);
|
|
4377
|
+
}
|
|
4285
4378
|
fresh.settled = true;
|
|
4286
4379
|
});
|
|
4287
4380
|
};
|
|
@@ -4303,6 +4396,22 @@ function createFormStore(options) {
|
|
|
4303
4396
|
}
|
|
4304
4397
|
fieldValidationState.clear();
|
|
4305
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
|
+
}
|
|
4306
4415
|
function onFormChange(listener) {
|
|
4307
4416
|
formChangeListeners.add(listener);
|
|
4308
4417
|
return () => {
|
|
@@ -4353,6 +4462,7 @@ function createFormStore(options) {
|
|
|
4353
4462
|
drainHooks.length = 0;
|
|
4354
4463
|
modules.clear();
|
|
4355
4464
|
cancelFieldValidation();
|
|
4465
|
+
fieldValidatingSince.clear();
|
|
4356
4466
|
formChangeListeners.clear();
|
|
4357
4467
|
submitSuccessListeners.clear();
|
|
4358
4468
|
resetListeners.clear();
|
|
@@ -4699,16 +4809,7 @@ function createFormStore(options) {
|
|
|
4699
4809
|
}
|
|
4700
4810
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4701
4811
|
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
|
-
});
|
|
4812
|
+
fields.set(pathKey, withClearedHistoryFlags(record, now));
|
|
4712
4813
|
}
|
|
4713
4814
|
submissionGeneration.value += 1;
|
|
4714
4815
|
submitting.value = false;
|
|
@@ -4718,6 +4819,8 @@ function createFormStore(options) {
|
|
|
4718
4819
|
submitError.value = null;
|
|
4719
4820
|
departAttempts.value = 0;
|
|
4720
4821
|
cancelFieldValidation();
|
|
4822
|
+
displayEngine.clear();
|
|
4823
|
+
fieldValidatingSince.clear();
|
|
4721
4824
|
pathSnapshots.clear();
|
|
4722
4825
|
scheduleEpoch = 0;
|
|
4723
4826
|
lastCommittedEpoch = 0;
|
|
@@ -4733,6 +4836,12 @@ function createFormStore(options) {
|
|
|
4733
4836
|
function resetField(path) {
|
|
4734
4837
|
const { key: targetKey, segments: targetSegments } = paths.canonicalizePath(path);
|
|
4735
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
|
+
}
|
|
4736
4845
|
const leafEntry = originals.get(targetKey);
|
|
4737
4846
|
if (leafEntry !== void 0) {
|
|
4738
4847
|
const wrote = setValueAtPath(targetSegments, leafEntry.value);
|
|
@@ -4782,16 +4891,7 @@ function createFormStore(options) {
|
|
|
4782
4891
|
function clearFieldRecordFlags(pathKey) {
|
|
4783
4892
|
const record = fields.get(pathKey);
|
|
4784
4893
|
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
|
-
});
|
|
4894
|
+
fields.set(pathKey, withClearedHistoryFlags(record, (/* @__PURE__ */ new Date()).toISOString()));
|
|
4795
4895
|
}
|
|
4796
4896
|
function isPristineAtPath(path) {
|
|
4797
4897
|
const { key, segments } = paths.canonicalizePath(path);
|
|
@@ -4871,6 +4971,8 @@ function createFormStore(options) {
|
|
|
4871
4971
|
pathHasAsyncValidation,
|
|
4872
4972
|
pathHasAsyncValidationByKey,
|
|
4873
4973
|
fieldValidationCounts,
|
|
4974
|
+
fieldValidatingSince,
|
|
4975
|
+
displayEngine,
|
|
4874
4976
|
applyFormReplacement,
|
|
4875
4977
|
setValueAtPath,
|
|
4876
4978
|
getValueAtPath,
|
|
@@ -4955,7 +5057,7 @@ function errorsEqual(a, b) {
|
|
|
4955
5057
|
}
|
|
4956
5058
|
return true;
|
|
4957
5059
|
}
|
|
4958
|
-
function diffBlankPaths
|
|
5060
|
+
function diffBlankPaths(prev, curr) {
|
|
4959
5061
|
const added = [];
|
|
4960
5062
|
const removed = [];
|
|
4961
5063
|
for (const k of curr) if (!prev.has(k)) added.push(k);
|
|
@@ -5043,7 +5145,7 @@ function createHistoryModule(state, config) {
|
|
|
5043
5145
|
diffAndApply(prevSnap.form, newSnap.form, [], (p) => formPatches.push(p));
|
|
5044
5146
|
const prevBlankSet = new Set(prevSnap.blankPaths);
|
|
5045
5147
|
const currBlankSet = new Set(newSnap.blankPaths);
|
|
5046
|
-
const blankDiff = diffBlankPaths
|
|
5148
|
+
const blankDiff = diffBlankPaths(prevBlankSet, currBlankSet);
|
|
5047
5149
|
const delta = {
|
|
5048
5150
|
formPatches,
|
|
5049
5151
|
blankPathsAdded: blankDiff.added,
|
|
@@ -5110,643 +5212,6 @@ function createHistoryModule(state, config) {
|
|
|
5110
5212
|
};
|
|
5111
5213
|
}
|
|
5112
5214
|
|
|
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
5215
|
const warned = /* @__PURE__ */ new Set();
|
|
5751
5216
|
function warnOnceInsecureContext(feature) {
|
|
5752
5217
|
if (!paths.__DEV__) return;
|
|
@@ -5806,8 +5271,10 @@ function useAbstractForm(configuration, options) {
|
|
|
5806
5271
|
}
|
|
5807
5272
|
const existing = registry.forms.get(key);
|
|
5808
5273
|
if (paths.__DEV__ && existing !== void 0) {
|
|
5809
|
-
|
|
5810
|
-
|
|
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
|
+
});
|
|
5811
5278
|
}
|
|
5812
5279
|
const hadPendingHydration = registry.pendingHydration.has(key);
|
|
5813
5280
|
const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
|
|
@@ -5848,10 +5315,33 @@ function useAbstractForm(configuration, options) {
|
|
|
5848
5315
|
} else {
|
|
5849
5316
|
const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
|
|
5850
5317
|
void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
|
|
5851
|
-
const
|
|
5852
|
-
|
|
5853
|
-
state.
|
|
5854
|
-
|
|
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);
|
|
5855
5345
|
}
|
|
5856
5346
|
} else {
|
|
5857
5347
|
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
@@ -5861,27 +5351,31 @@ function useAbstractForm(configuration, options) {
|
|
|
5861
5351
|
const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
|
|
5862
5352
|
const secureContext = isSecureContext();
|
|
5863
5353
|
if (hasBroadcastChannel && secureContext) {
|
|
5864
|
-
let
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
}
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
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
|
+
}
|
|
5879
5372
|
}
|
|
5880
|
-
}
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5373
|
+
});
|
|
5374
|
+
state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
|
|
5375
|
+
state.registerCleanup(() => syncModule.dispose());
|
|
5376
|
+
} catch {
|
|
5377
|
+
}
|
|
5378
|
+
})();
|
|
5885
5379
|
} else if (hasBroadcastChannel && !secureContext) {
|
|
5886
5380
|
warnOnceInsecureContext("multiTab");
|
|
5887
5381
|
}
|
|
@@ -6042,55 +5536,17 @@ function resolveFormKey(key) {
|
|
|
6042
5536
|
}
|
|
6043
5537
|
return `${ANONYMOUS_FORM_KEY_PREFIX}${anonCounter++}`;
|
|
6044
5538
|
}
|
|
6045
|
-
function
|
|
6046
|
-
let existingFp;
|
|
6047
|
-
let incomingFp;
|
|
5539
|
+
async function resolvePersistFingerprintToken(state) {
|
|
6048
5540
|
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;
|
|
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";
|
|
6074
5549
|
}
|
|
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
5550
|
}
|
|
6095
5551
|
const warnedAnonPersistKeys = /* @__PURE__ */ new Set();
|
|
6096
5552
|
function enforceAnonPersistRule(formKey, ssr) {
|
|
@@ -6258,7 +5714,7 @@ function buildNoopWizardSchema(formKey) {
|
|
|
6258
5714
|
formKey
|
|
6259
5715
|
};
|
|
6260
5716
|
return {
|
|
6261
|
-
fingerprint: () => NOOP_FINGERPRINT,
|
|
5717
|
+
fingerprint: () => Promise.resolve(NOOP_FINGERPRINT),
|
|
6262
5718
|
getDefaultValues: () => defaultsResponse,
|
|
6263
5719
|
getDefaultAtPath: () => void 0,
|
|
6264
5720
|
getEmptyValueAtPath: () => void 0,
|
|
@@ -6975,7 +6431,11 @@ function useWizard(options) {
|
|
|
6975
6431
|
}
|
|
6976
6432
|
for (const key of results.keys()) {
|
|
6977
6433
|
const store = registry.forms.get(key);
|
|
6978
|
-
if (store !== void 0)
|
|
6434
|
+
if (store !== void 0) {
|
|
6435
|
+
store.submissionAttempts.value += 1;
|
|
6436
|
+
store.cancelFieldValidation();
|
|
6437
|
+
store.displayEngine.clear();
|
|
6438
|
+
}
|
|
6979
6439
|
}
|
|
6980
6440
|
submissionAttempts.value += 1;
|
|
6981
6441
|
const errors = collectErrors(results);
|
|
@@ -7209,9 +6669,16 @@ function warnIfAmbientWizardProviderHadDuplicates() {
|
|
|
7209
6669
|
}
|
|
7210
6670
|
|
|
7211
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;
|
|
7212
6677
|
exports.defaultCoercionRules = defaultCoercionRules;
|
|
7213
6678
|
exports.defaultDisplayState = defaultDisplayState;
|
|
7214
6679
|
exports.defineCoercion = defineCoercion;
|
|
6680
|
+
exports.deleteAtPath = deleteAtPath;
|
|
6681
|
+
exports.diffAndApply = diffAndApply;
|
|
7215
6682
|
exports.getAtPath = getAtPath;
|
|
7216
6683
|
exports.humanize = humanize;
|
|
7217
6684
|
exports.injectForm = injectForm;
|
|
@@ -7219,12 +6686,17 @@ exports.injectWizard = injectWizard;
|
|
|
7219
6686
|
exports.isPlainRecord = isPlainRecord;
|
|
7220
6687
|
exports.isUnset = isUnset;
|
|
7221
6688
|
exports.lazy = lazy;
|
|
6689
|
+
exports.makeDefaultDisplayState = makeDefaultDisplayState;
|
|
6690
|
+
exports.mergeSparseHydration = mergeSparseHydration;
|
|
7222
6691
|
exports.normalizeNumericOption = normalizeNumericOption;
|
|
6692
|
+
exports.normalizePersistConfig = normalizePersistConfig;
|
|
6693
|
+
exports.resolveStorageKeyBase = resolveStorageKeyBase;
|
|
7223
6694
|
exports.safeAssign = safeAssign;
|
|
7224
6695
|
exports.safeOwnRead = safeOwnRead;
|
|
7225
6696
|
exports.setAtPath = setAtPath;
|
|
7226
6697
|
exports.slimKindOf = slimKindOf;
|
|
6698
|
+
exports.structuralSnapshot = structuralSnapshot;
|
|
7227
6699
|
exports.unset = unset;
|
|
7228
6700
|
exports.useAbstractForm = useAbstractForm;
|
|
7229
6701
|
exports.useWizard = useWizard;
|
|
7230
|
-
//# sourceMappingURL=attaform.
|
|
6702
|
+
//# sourceMappingURL=attaform.BUszFoKq.cjs.map
|