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,5 +1,5 @@
|
|
|
1
1
|
import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, reactive, toRaw, watch, markRaw, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, effectScope, nextTick } from 'vue';
|
|
2
|
-
import { _ as __DEV__, j as canonicalizePath,
|
|
2
|
+
import { _ as __DEV__, j as canonicalizePath, G as segmentsForPathKey, t as isPathPrefix, b as FORM_ERRORS_PATH_KEY, S as SubmitErrorHandlerError, A as AnonPersistError, k as captureUserCallSite, I as INTERACTIVE_TAG_NAMES, r as getOrAssignElementId, e as ROOT_PATH_KEY, R as ROOT_PATH, h as allowSensitivePersist, F as FORM_ERRORS_PATH, l as coerceToPathKey, v as isSensitivePath, o as createPersistOptInRegistry, d as InvalidUseFormConfigError, q as ensureAttaformInstalled, J as useRegistry, z as kFormContext, B as kFormInstanceId, g as ReservedFormKeyError, n as createIsSensitivePath, y as kAttaformWizardActiveStepResolver, w as kAttaformAncestorWizard } from './attaform.Y_Mgg0Yp.mjs';
|
|
3
3
|
|
|
4
4
|
function safeAssign(target, key, value) {
|
|
5
5
|
if (key === "__proto__") {
|
|
@@ -458,17 +458,51 @@ function structuralSnapshot(value) {
|
|
|
458
458
|
return out;
|
|
459
459
|
}
|
|
460
460
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
461
|
+
function isGateOpen(field, formMeta) {
|
|
462
|
+
return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
|
|
463
|
+
}
|
|
464
|
+
function computeVerdict(field, formMeta) {
|
|
465
|
+
if (!isGateOpen(field, formMeta)) return "idle";
|
|
465
466
|
const hasOwnError = field.errors.some(
|
|
466
467
|
(e) => e.path.length === field.path.length && e.path.every((s, i) => s === field.path[i])
|
|
467
468
|
);
|
|
468
469
|
if (hasOwnError) return "error";
|
|
469
470
|
if (field.valid === true && field.blank !== true && field.dirty === true) return "success";
|
|
470
471
|
return "idle";
|
|
471
|
-
}
|
|
472
|
+
}
|
|
473
|
+
const DEFAULT_TIMINGS = { showDelay: 100, minVisible: 120 };
|
|
474
|
+
const FOCUS_OUT_GRACE = 16;
|
|
475
|
+
const defaultFamily = /* @__PURE__ */ new WeakSet();
|
|
476
|
+
function isDefaultDisplayState(fn) {
|
|
477
|
+
return defaultFamily.has(fn);
|
|
478
|
+
}
|
|
479
|
+
function makeDefaultDisplayState({
|
|
480
|
+
showDelay,
|
|
481
|
+
minVisible
|
|
482
|
+
}) {
|
|
483
|
+
const reducer = (prev, { field, formMeta, validatingSince, now }) => {
|
|
484
|
+
const verdict = computeVerdict(field, formMeta);
|
|
485
|
+
if (!isGateOpen(field, formMeta)) return { display: verdict };
|
|
486
|
+
if (validatingSince === null) {
|
|
487
|
+
if (prev.display === "pending") {
|
|
488
|
+
const shownAt = prev.pendingShownAt ?? now;
|
|
489
|
+
if (now < shownAt + minVisible)
|
|
490
|
+
return { display: "pending", pendingShownAt: shownAt, reviewAt: shownAt + minVisible };
|
|
491
|
+
}
|
|
492
|
+
return { display: verdict };
|
|
493
|
+
}
|
|
494
|
+
if (prev.display === "pending")
|
|
495
|
+
return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
|
|
496
|
+
const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
|
|
497
|
+
if (now - validatingSince < window) {
|
|
498
|
+
return { display: prev.display, reviewAt: validatingSince + window };
|
|
499
|
+
}
|
|
500
|
+
return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
|
|
501
|
+
};
|
|
502
|
+
defaultFamily.add(reducer);
|
|
503
|
+
return reducer;
|
|
504
|
+
}
|
|
505
|
+
const defaultDisplayState = makeDefaultDisplayState(DEFAULT_TIMINGS);
|
|
472
506
|
function resolveGetDisplayState(config) {
|
|
473
507
|
return config ?? defaultDisplayState;
|
|
474
508
|
}
|
|
@@ -625,7 +659,19 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
625
659
|
}
|
|
626
660
|
function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
|
|
627
661
|
const base = buildLeafFieldStateBase(state, segments, key, formInstanceId);
|
|
628
|
-
|
|
662
|
+
const validatingSince = state.fieldValidatingSince.get(key) ?? null;
|
|
663
|
+
return decorateWithDerivedProps(
|
|
664
|
+
base,
|
|
665
|
+
state,
|
|
666
|
+
getFormMetaBase,
|
|
667
|
+
key,
|
|
668
|
+
validatingSince,
|
|
669
|
+
false,
|
|
670
|
+
// revealedDescendantError: leaves have no descendants
|
|
671
|
+
false,
|
|
672
|
+
// isRoot: a leaf is never the form root
|
|
673
|
+
getDisplayState
|
|
674
|
+
);
|
|
629
675
|
}
|
|
630
676
|
function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
631
677
|
const formValue = state.form.value;
|
|
@@ -641,8 +687,11 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
641
687
|
let blurredAfterInteraction = false;
|
|
642
688
|
let connected = false;
|
|
643
689
|
let validating = false;
|
|
690
|
+
let validatingSince = null;
|
|
644
691
|
let updatedAt = null;
|
|
645
692
|
let asyncPending = false;
|
|
693
|
+
const submissionAttempts = state.submissionAttempts.value;
|
|
694
|
+
const blurredLeafSegments = [];
|
|
646
695
|
for (const [leafKey, entry] of state.originals) {
|
|
647
696
|
if (!isPathPrefix(segments, entry.segments)) continue;
|
|
648
697
|
if (segments.length === entry.segments.length) continue;
|
|
@@ -657,9 +706,18 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
657
706
|
if (leafRecord?.blurred === true) blurred = true;
|
|
658
707
|
if (leafRecord?.touched === true) touched = true;
|
|
659
708
|
if (leafRecord?.interacted === true) interacted = true;
|
|
660
|
-
if (leafRecord?.blurredAfterInteraction === true)
|
|
709
|
+
if (leafRecord?.blurredAfterInteraction === true) {
|
|
710
|
+
blurredAfterInteraction = true;
|
|
711
|
+
blurredLeafSegments.push(entry.segments);
|
|
712
|
+
}
|
|
661
713
|
if (leafRecord?.connected === true) connected = true;
|
|
662
|
-
if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0)
|
|
714
|
+
if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0) {
|
|
715
|
+
validating = true;
|
|
716
|
+
const leafGateOpen = submissionAttempts > 0 || leafRecord?.blurredAfterInteraction === true;
|
|
717
|
+
const since = state.fieldValidatingSince.get(leafKey);
|
|
718
|
+
if (leafGateOpen && since !== void 0 && (validatingSince === null || since < validatingSince))
|
|
719
|
+
validatingSince = since;
|
|
720
|
+
}
|
|
663
721
|
if (state.pathHasAsyncValidationByKey(leafKey, entry.segments)) asyncPending = true;
|
|
664
722
|
const ts = leafRecord?.updatedAt;
|
|
665
723
|
if (ts !== void 0 && ts !== null) {
|
|
@@ -671,6 +729,13 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
671
729
|
dirty = true;
|
|
672
730
|
}
|
|
673
731
|
const errors = aggregateErrorsAt(state, segments);
|
|
732
|
+
const revealedDescendantError = errors.length > 0 && errors.some((e) => {
|
|
733
|
+
const ePath = e.path;
|
|
734
|
+
if (ePath.length === segments.length && ePath.every((s, i) => s === segments[i])) return false;
|
|
735
|
+
if (submissionAttempts > 0) return true;
|
|
736
|
+
if (ePath.length === 1 && ePath[0] === "") return blurredAfterInteraction;
|
|
737
|
+
return blurredLeafSegments.some((s) => isPathPrefix(ePath, s));
|
|
738
|
+
});
|
|
674
739
|
if (!asyncPending && state.pathHasAsyncValidation(segments)) asyncPending = true;
|
|
675
740
|
const gated = asyncPending && !state.firstValidationDone.value;
|
|
676
741
|
const valid = !gated && errors.length === 0 && !validating;
|
|
@@ -678,43 +743,70 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
678
743
|
const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
|
|
679
744
|
const label = resolved.label || humanize(lastSegment);
|
|
680
745
|
return {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
746
|
+
base: {
|
|
747
|
+
value,
|
|
748
|
+
original,
|
|
749
|
+
pristine,
|
|
750
|
+
dirty,
|
|
751
|
+
focused,
|
|
752
|
+
blurred,
|
|
753
|
+
touched,
|
|
754
|
+
interacted,
|
|
755
|
+
blurredAfterInteraction,
|
|
756
|
+
connected,
|
|
757
|
+
element: null,
|
|
758
|
+
elements: EMPTY_ELEMENTS,
|
|
759
|
+
updatedAt,
|
|
760
|
+
errors,
|
|
761
|
+
validating,
|
|
762
|
+
valid,
|
|
763
|
+
path: segments,
|
|
764
|
+
...computeFieldIdentity(formInstanceId, state.formKey, key),
|
|
765
|
+
key: state.arrayElementKey(segments),
|
|
766
|
+
blank,
|
|
767
|
+
label,
|
|
768
|
+
description: resolved.description,
|
|
769
|
+
placeholder: resolved.placeholder,
|
|
770
|
+
meta: resolved.meta
|
|
771
|
+
},
|
|
772
|
+
validatingSince,
|
|
773
|
+
revealedDescendantError
|
|
705
774
|
};
|
|
706
775
|
}
|
|
707
776
|
function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
|
|
708
|
-
const base = buildContainerFieldStateBase(
|
|
709
|
-
|
|
777
|
+
const { base, validatingSince, revealedDescendantError } = buildContainerFieldStateBase(
|
|
778
|
+
state,
|
|
779
|
+
segments,
|
|
780
|
+
key,
|
|
781
|
+
formInstanceId
|
|
782
|
+
);
|
|
783
|
+
return decorateWithDerivedProps(
|
|
784
|
+
base,
|
|
785
|
+
state,
|
|
786
|
+
getFormMetaBase,
|
|
787
|
+
key,
|
|
788
|
+
validatingSince,
|
|
789
|
+
revealedDescendantError,
|
|
790
|
+
segments.length === 0,
|
|
791
|
+
getDisplayState
|
|
792
|
+
);
|
|
710
793
|
}
|
|
711
|
-
function decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState) {
|
|
794
|
+
function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, revealedDescendantError, isRoot, getDisplayState) {
|
|
712
795
|
const firstError = base.errors[0];
|
|
713
796
|
const predicate = getDisplayState ?? state.getDisplayState;
|
|
714
797
|
const formMeta = getFormMetaBase();
|
|
715
|
-
|
|
798
|
+
const ctx = {
|
|
799
|
+
field: base,
|
|
800
|
+
formMeta,
|
|
801
|
+
validatingSince,
|
|
802
|
+
// The engine's clock. Frozen to 0 under SSR (no clock, nothing in
|
|
803
|
+
// flight) so the reducer returns the plain verdict and hydration matches.
|
|
804
|
+
now: state.ssr ? 0 : Date.now()
|
|
805
|
+
};
|
|
806
|
+
let machine;
|
|
807
|
+
let rollupApplies = isDefaultDisplayState(predicate);
|
|
716
808
|
try {
|
|
717
|
-
|
|
809
|
+
machine = state.displayEngine.resolve(key, ctx, predicate);
|
|
718
810
|
} catch (err) {
|
|
719
811
|
if (__DEV__ && !warnedDisplayStatePredicates.has(predicate)) {
|
|
720
812
|
warnedDisplayStatePredicates.add(predicate);
|
|
@@ -723,8 +815,11 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState)
|
|
|
723
815
|
err
|
|
724
816
|
);
|
|
725
817
|
}
|
|
726
|
-
|
|
818
|
+
machine = state.displayEngine.resolve(key, ctx, defaultDisplayState);
|
|
819
|
+
rollupApplies = true;
|
|
727
820
|
}
|
|
821
|
+
const submitValidating = rollupApplies && isRoot && state.submitting.value && state.activeValidations.value > 0;
|
|
822
|
+
const displayState = machine.display === "pending" || submitValidating ? "pending" : rollupApplies && revealedDescendantError ? "error" : machine.display;
|
|
728
823
|
return {
|
|
729
824
|
...base,
|
|
730
825
|
displayState,
|
|
@@ -1417,90 +1512,6 @@ async function getStorageAdapter(storage) {
|
|
|
1417
1512
|
}
|
|
1418
1513
|
}
|
|
1419
1514
|
}
|
|
1420
|
-
const PERSISTED_ENVELOPE_VERSION = 6;
|
|
1421
|
-
function readPersistedPayload(value) {
|
|
1422
|
-
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
1423
|
-
const envelope = value;
|
|
1424
|
-
if (typeof envelope.v !== "number") return null;
|
|
1425
|
-
if (envelope.v !== PERSISTED_ENVELOPE_VERSION) {
|
|
1426
|
-
warnVersionMismatch(envelope.v);
|
|
1427
|
-
return null;
|
|
1428
|
-
}
|
|
1429
|
-
if (envelope.data === void 0 || typeof envelope.data !== "object") return null;
|
|
1430
|
-
return envelope;
|
|
1431
|
-
}
|
|
1432
|
-
const warnedVersions = __DEV__ ? /* @__PURE__ */ new Set() : null;
|
|
1433
|
-
function warnVersionMismatch(observedVersion) {
|
|
1434
|
-
if (warnedVersions === null) return;
|
|
1435
|
-
if (warnedVersions.has(observedVersion)) return;
|
|
1436
|
-
warnedVersions.add(observedVersion);
|
|
1437
|
-
console.warn(
|
|
1438
|
-
`[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.`
|
|
1439
|
-
);
|
|
1440
|
-
}
|
|
1441
|
-
function buildPersistedPayload(form, include, schemaErrors, userErrors, blankPaths) {
|
|
1442
|
-
let transientList;
|
|
1443
|
-
if (blankPaths !== void 0 && blankPaths.size > 0) {
|
|
1444
|
-
transientList = [...blankPaths];
|
|
1445
|
-
}
|
|
1446
|
-
if (include === "form") {
|
|
1447
|
-
if (transientList === void 0) return { v: PERSISTED_ENVELOPE_VERSION, data: { form } };
|
|
1448
|
-
return {
|
|
1449
|
-
v: PERSISTED_ENVELOPE_VERSION,
|
|
1450
|
-
data: { form, blankPaths: transientList }
|
|
1451
|
-
};
|
|
1452
|
-
}
|
|
1453
|
-
return {
|
|
1454
|
-
v: PERSISTED_ENVELOPE_VERSION,
|
|
1455
|
-
data: {
|
|
1456
|
-
form,
|
|
1457
|
-
schemaErrors: [...schemaErrors.entries()].map(([k, v]) => [k, [...v]]),
|
|
1458
|
-
userErrors: [...userErrors.entries()].map(([k, v]) => [k, [...v]]),
|
|
1459
|
-
...transientList !== void 0 ? { blankPaths: transientList } : {}
|
|
1460
|
-
}
|
|
1461
|
-
};
|
|
1462
|
-
}
|
|
1463
|
-
function createDebouncedWriter(write, debounceMs) {
|
|
1464
|
-
let timer = null;
|
|
1465
|
-
let pending = null;
|
|
1466
|
-
let writeGeneration = 0;
|
|
1467
|
-
function runWrite() {
|
|
1468
|
-
const gen = ++writeGeneration;
|
|
1469
|
-
pending = write().finally(() => {
|
|
1470
|
-
if (writeGeneration === gen) pending = null;
|
|
1471
|
-
});
|
|
1472
|
-
}
|
|
1473
|
-
function schedule() {
|
|
1474
|
-
if (timer !== null) clearTimeout(timer);
|
|
1475
|
-
if (debounceMs === 0) {
|
|
1476
|
-
runWrite();
|
|
1477
|
-
return;
|
|
1478
|
-
}
|
|
1479
|
-
timer = setTimeout(() => {
|
|
1480
|
-
timer = null;
|
|
1481
|
-
runWrite();
|
|
1482
|
-
}, debounceMs);
|
|
1483
|
-
}
|
|
1484
|
-
async function flush() {
|
|
1485
|
-
if (timer !== null) {
|
|
1486
|
-
clearTimeout(timer);
|
|
1487
|
-
timer = null;
|
|
1488
|
-
runWrite();
|
|
1489
|
-
}
|
|
1490
|
-
while (pending !== null) {
|
|
1491
|
-
const awaited = pending;
|
|
1492
|
-
await awaited;
|
|
1493
|
-
if (pending === awaited) break;
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
function cancel() {
|
|
1497
|
-
if (timer !== null) {
|
|
1498
|
-
clearTimeout(timer);
|
|
1499
|
-
timer = null;
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
return { schedule, flush, cancel };
|
|
1503
|
-
}
|
|
1504
1515
|
function resolveStorageKeyBase(config, formKey) {
|
|
1505
1516
|
return config.key ?? `${PERSISTENCE_KEY_PREFIX}${formKey}`;
|
|
1506
1517
|
}
|
|
@@ -1547,47 +1558,6 @@ async function sweepNonConfiguredStandardStoresForOrphans(configured, base) {
|
|
|
1547
1558
|
}
|
|
1548
1559
|
}
|
|
1549
1560
|
}
|
|
1550
|
-
function pluckPaths(form, pathKeys) {
|
|
1551
|
-
let sparse = void 0;
|
|
1552
|
-
for (const pathKey of pathKeys) {
|
|
1553
|
-
const segments = segmentsForPathKey(pathKey);
|
|
1554
|
-
if (segments === null) continue;
|
|
1555
|
-
const value = getAtPath(form, segments);
|
|
1556
|
-
if (value === void 0) continue;
|
|
1557
|
-
sparse = setAtPath(sparse ?? {}, segments, value);
|
|
1558
|
-
}
|
|
1559
|
-
return sparse ?? {};
|
|
1560
|
-
}
|
|
1561
|
-
function stripUnacknowledgedSensitiveLeaves(form, optedInPaths, isSensitivePath) {
|
|
1562
|
-
const acknowledgedSensitive = [];
|
|
1563
|
-
for (const key of optedInPaths) {
|
|
1564
|
-
const segs = segmentsForPathKey(key);
|
|
1565
|
-
if (segs !== null && isSensitivePath(segs)) acknowledgedSensitive.push(segs);
|
|
1566
|
-
}
|
|
1567
|
-
const coveredByAcknowledged = (path) => acknowledgedSensitive.some((prefix) => isPathPrefix(prefix, path));
|
|
1568
|
-
const walk = (path, value) => {
|
|
1569
|
-
if (path.length > 0 && isSensitivePath(path) && !coveredByAcknowledged(path)) {
|
|
1570
|
-
return void 0;
|
|
1571
|
-
}
|
|
1572
|
-
if (value === null || typeof value !== "object") return value;
|
|
1573
|
-
if (Array.isArray(value)) return value.map((item, i) => walk([...path, i], item));
|
|
1574
|
-
if (!isPlainRecord(value)) return value;
|
|
1575
|
-
const out = {};
|
|
1576
|
-
for (const key of Object.keys(value)) {
|
|
1577
|
-
const walked = walk([...path, key], value[key]);
|
|
1578
|
-
if (walked !== void 0) safeAssign(out, key, walked);
|
|
1579
|
-
}
|
|
1580
|
-
return out;
|
|
1581
|
-
};
|
|
1582
|
-
return walk([], form);
|
|
1583
|
-
}
|
|
1584
|
-
function filterErrorsByPaths(errors, pathKeys) {
|
|
1585
|
-
const out = /* @__PURE__ */ new Map();
|
|
1586
|
-
for (const [key, value] of errors) {
|
|
1587
|
-
if (pathKeys.has(key)) out.set(key, value);
|
|
1588
|
-
}
|
|
1589
|
-
return out;
|
|
1590
|
-
}
|
|
1591
1561
|
function mergeSparseHydration(schemaDefaults, sparse, schema) {
|
|
1592
1562
|
return mergeDeep(schemaDefaults, sparse, [], schema);
|
|
1593
1563
|
}
|
|
@@ -1825,6 +1795,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1825
1795
|
state.submitting.value = true;
|
|
1826
1796
|
state.submitError.value = null;
|
|
1827
1797
|
state.cancelFieldValidation();
|
|
1798
|
+
state.displayEngine.clear();
|
|
1828
1799
|
state.activeValidations.value += 1;
|
|
1829
1800
|
const refinement = await runRefinementValidation(state.form.value, void 0);
|
|
1830
1801
|
const merged = composeWithDerivedBlank(refinement, void 0);
|
|
@@ -2306,6 +2277,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2306
2277
|
const isRequired = state.schema.isRequiredAtPath(segments);
|
|
2307
2278
|
const ariaEnabled = options?.autoAria ?? formAutoAria;
|
|
2308
2279
|
const ariaDisplayState = getDisplayStateAt !== void 0 ? computed(() => getDisplayStateAt(segments)) : void 0;
|
|
2280
|
+
let boundElement = null;
|
|
2309
2281
|
const internalRv = {
|
|
2310
2282
|
innerRef,
|
|
2311
2283
|
displayValue,
|
|
@@ -2324,6 +2296,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2324
2296
|
state.markInteracted(segments);
|
|
2325
2297
|
},
|
|
2326
2298
|
registerElement: (element) => {
|
|
2299
|
+
boundElement = element;
|
|
2327
2300
|
if (!INTERACTIVE_TAG_NAMES.has(element.tagName)) return;
|
|
2328
2301
|
const added = state.registerElement(segments, element, formInstanceId);
|
|
2329
2302
|
if (added) attachFocusListeners(state, segments, element, instanceMeta);
|
|
@@ -2331,9 +2304,11 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2331
2304
|
deregisterElement: (element) => {
|
|
2332
2305
|
detachFocusListeners(element);
|
|
2333
2306
|
state.deregisterElement(segments, element);
|
|
2307
|
+
if (boundElement === element) boundElement = null;
|
|
2334
2308
|
},
|
|
2335
2309
|
setValueWithInternalPath: (value, meta) => {
|
|
2336
|
-
|
|
2310
|
+
const resolvedMeta = meta === void 0 && boundElement !== null ? { persist: state.persistOptIns.hasOptIn(getOrAssignElementId(boundElement), pathKey) } : meta;
|
|
2311
|
+
return state.setValueAtPath(segments, value, withInstanceMeta(resolvedMeta));
|
|
2337
2312
|
},
|
|
2338
2313
|
// Called by the `vRegisterHint` compile-time transform's wrapping
|
|
2339
2314
|
// IIFE on every server-side render of `<element v-register="…">`.
|
|
@@ -2637,7 +2612,12 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2637
2612
|
return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
|
|
2638
2613
|
};
|
|
2639
2614
|
const getFormMetaBase = () => {
|
|
2640
|
-
const rootBase = buildContainerFieldStateBase(
|
|
2615
|
+
const { base: rootBase } = buildContainerFieldStateBase(
|
|
2616
|
+
state,
|
|
2617
|
+
ROOT_PATH,
|
|
2618
|
+
ROOT_PATH_KEY,
|
|
2619
|
+
formInstanceId
|
|
2620
|
+
);
|
|
2641
2621
|
return {
|
|
2642
2622
|
...rootBase,
|
|
2643
2623
|
submitting: state.submitting.value,
|
|
@@ -2972,7 +2952,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2972
2952
|
instanceId: formInstanceId
|
|
2973
2953
|
})
|
|
2974
2954
|
);
|
|
2975
|
-
const
|
|
2955
|
+
const persistenceHandle = state.modules.get(PERSISTENCE_MODULE_KEY);
|
|
2976
2956
|
const reset = (nextDefaultValues) => {
|
|
2977
2957
|
if (nextDefaultValues === void 0) {
|
|
2978
2958
|
state.reset();
|
|
@@ -2987,15 +2967,15 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2987
2967
|
state.originalBlankPaths.add(pathKey);
|
|
2988
2968
|
}
|
|
2989
2969
|
}
|
|
2990
|
-
if (
|
|
2991
|
-
void
|
|
2970
|
+
if (persistenceHandle !== void 0) {
|
|
2971
|
+
void persistenceHandle.ready.then((m) => m?.clearPersistedDraft()).catch(() => void 0);
|
|
2992
2972
|
}
|
|
2993
2973
|
};
|
|
2994
2974
|
const resetField = (pathInput) => {
|
|
2995
2975
|
const segments = canonicalizePath(pathInput).segments;
|
|
2996
2976
|
state.resetField(segments);
|
|
2997
|
-
if (
|
|
2998
|
-
void
|
|
2977
|
+
if (persistenceHandle !== void 0) {
|
|
2978
|
+
void persistenceHandle.ready.then((m) => m?.clearPersistedDraft(segments)).catch(() => void 0);
|
|
2999
2979
|
}
|
|
3000
2980
|
};
|
|
3001
2981
|
function clear(pathInput) {
|
|
@@ -3013,10 +2993,14 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3013
2993
|
)) {
|
|
3014
2994
|
return;
|
|
3015
2995
|
}
|
|
2996
|
+
if (persistenceHandle === void 0) return;
|
|
2997
|
+
const persistence = await persistenceHandle.ready;
|
|
3016
2998
|
if (persistence === void 0) return;
|
|
3017
2999
|
await persistence.writePathImmediately(segments);
|
|
3018
3000
|
};
|
|
3019
3001
|
const clearPersistedDraft = async (pathInput) => {
|
|
3002
|
+
if (persistenceHandle === void 0) return;
|
|
3003
|
+
const persistence = await persistenceHandle.ready;
|
|
3020
3004
|
if (persistence === void 0) return;
|
|
3021
3005
|
if (pathInput === void 0) {
|
|
3022
3006
|
await persistence.clearPersistedDraft();
|
|
@@ -3199,6 +3183,93 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3199
3183
|
};
|
|
3200
3184
|
}
|
|
3201
3185
|
|
|
3186
|
+
const IDLE = Object.freeze({ display: "idle" });
|
|
3187
|
+
const MAX_DELAY = 2147483647;
|
|
3188
|
+
function createDisplayEngine(ssr) {
|
|
3189
|
+
const machines = /* @__PURE__ */ new Map();
|
|
3190
|
+
const tick = ref(0);
|
|
3191
|
+
let timer = null;
|
|
3192
|
+
let timerTarget = null;
|
|
3193
|
+
let lastFiredTarget = null;
|
|
3194
|
+
function clearTimer() {
|
|
3195
|
+
if (timer !== null) {
|
|
3196
|
+
clearTimeout(timer);
|
|
3197
|
+
timer = null;
|
|
3198
|
+
timerTarget = null;
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
function nearestReviewAt() {
|
|
3202
|
+
let min = null;
|
|
3203
|
+
for (const m of machines.values()) {
|
|
3204
|
+
if (m.reviewAt === void 0 || !Number.isFinite(m.reviewAt)) continue;
|
|
3205
|
+
if (min === null || m.reviewAt < min) min = m.reviewAt;
|
|
3206
|
+
}
|
|
3207
|
+
return min;
|
|
3208
|
+
}
|
|
3209
|
+
function rearm(now) {
|
|
3210
|
+
const target = nearestReviewAt();
|
|
3211
|
+
if (target === null) {
|
|
3212
|
+
clearTimer();
|
|
3213
|
+
return;
|
|
3214
|
+
}
|
|
3215
|
+
if (timer !== null && timerTarget === target) return;
|
|
3216
|
+
if (target === lastFiredTarget) {
|
|
3217
|
+
clearTimer();
|
|
3218
|
+
return;
|
|
3219
|
+
}
|
|
3220
|
+
clearTimer();
|
|
3221
|
+
timerTarget = target;
|
|
3222
|
+
timer = setTimeout(
|
|
3223
|
+
() => {
|
|
3224
|
+
timer = null;
|
|
3225
|
+
timerTarget = null;
|
|
3226
|
+
lastFiredTarget = target;
|
|
3227
|
+
tick.value++;
|
|
3228
|
+
},
|
|
3229
|
+
Math.min(MAX_DELAY, Math.max(0, target - now))
|
|
3230
|
+
);
|
|
3231
|
+
}
|
|
3232
|
+
function resolve(key, ctx, reducer) {
|
|
3233
|
+
void tick.value;
|
|
3234
|
+
const prev = machines.get(key) ?? IDLE;
|
|
3235
|
+
const machine = reducer(prev, ctx);
|
|
3236
|
+
if (ssr) return machine;
|
|
3237
|
+
const active = machine.display !== "idle" || machine.reviewAt !== void 0 && Number.isFinite(machine.reviewAt);
|
|
3238
|
+
if (active) machines.set(key, machine);
|
|
3239
|
+
else machines.delete(key);
|
|
3240
|
+
rearm(ctx.now);
|
|
3241
|
+
return machine;
|
|
3242
|
+
}
|
|
3243
|
+
function clear() {
|
|
3244
|
+
machines.clear();
|
|
3245
|
+
clearTimer();
|
|
3246
|
+
lastFiredTarget = null;
|
|
3247
|
+
}
|
|
3248
|
+
let detachVisibility = null;
|
|
3249
|
+
if (!ssr && typeof document !== "undefined") {
|
|
3250
|
+
const onVisible = () => {
|
|
3251
|
+
if (document.visibilityState === "visible" && machines.size > 0) tick.value++;
|
|
3252
|
+
};
|
|
3253
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
3254
|
+
detachVisibility = () => document.removeEventListener("visibilitychange", onVisible);
|
|
3255
|
+
}
|
|
3256
|
+
function dispose() {
|
|
3257
|
+
clear();
|
|
3258
|
+
if (detachVisibility !== null) {
|
|
3259
|
+
detachVisibility();
|
|
3260
|
+
detachVisibility = null;
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
return {
|
|
3264
|
+
resolve,
|
|
3265
|
+
clear,
|
|
3266
|
+
dispose,
|
|
3267
|
+
size: () => machines.size,
|
|
3268
|
+
has: (key) => machines.has(key),
|
|
3269
|
+
hasTimer: () => timer !== null
|
|
3270
|
+
};
|
|
3271
|
+
}
|
|
3272
|
+
|
|
3202
3273
|
function applyDuStubs(schema, data, options = {}) {
|
|
3203
3274
|
const warned = options.warn === true ? /* @__PURE__ */ new Set() : void 0;
|
|
3204
3275
|
return walkDuStubs(schema, data, options.basePath ?? [], warned);
|
|
@@ -3537,6 +3608,7 @@ function createArrayBookkeeping(deps) {
|
|
|
3537
3608
|
blankPaths,
|
|
3538
3609
|
originalBlankPaths,
|
|
3539
3610
|
fieldValidationCounts,
|
|
3611
|
+
fieldValidatingSince,
|
|
3540
3612
|
fieldValidationState,
|
|
3541
3613
|
schemaErrors,
|
|
3542
3614
|
activeValidations,
|
|
@@ -3562,6 +3634,7 @@ function createArrayBookkeeping(deps) {
|
|
|
3562
3634
|
}));
|
|
3563
3635
|
migrateSetSubtree(blankPaths, arrayPath, remap);
|
|
3564
3636
|
migrateSetSubtree(originalBlankPaths, arrayPath, remap);
|
|
3637
|
+
migrateMapSubtree(fieldValidatingSince, arrayPath, remap, (since) => since);
|
|
3565
3638
|
migrateMapSubtree(fieldValidationCounts, arrayPath, remap, (count) => count);
|
|
3566
3639
|
arrayIdentity.applyRemap(arrayPath, remap);
|
|
3567
3640
|
}
|
|
@@ -3621,6 +3694,18 @@ function isHydratedFieldRecord(value) {
|
|
|
3621
3694
|
const r = value;
|
|
3622
3695
|
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";
|
|
3623
3696
|
}
|
|
3697
|
+
function withClearedHistoryFlags(record, now) {
|
|
3698
|
+
return {
|
|
3699
|
+
path: record.path,
|
|
3700
|
+
updatedAt: now,
|
|
3701
|
+
connected: record.connected,
|
|
3702
|
+
focused: record.focused,
|
|
3703
|
+
blurred: record.blurred,
|
|
3704
|
+
touched: false,
|
|
3705
|
+
interacted: false,
|
|
3706
|
+
blurredAfterInteraction: false
|
|
3707
|
+
};
|
|
3708
|
+
}
|
|
3624
3709
|
function isHydratedValidationErrorArray(value) {
|
|
3625
3710
|
if (!Array.isArray(value)) return false;
|
|
3626
3711
|
for (const entry of value) {
|
|
@@ -3748,6 +3833,9 @@ function createFormStore(options) {
|
|
|
3748
3833
|
const resolvedIsSensitivePath = options.isSensitivePath ?? isSensitivePath;
|
|
3749
3834
|
const cleanupHooks = [];
|
|
3750
3835
|
const modules = /* @__PURE__ */ new Map();
|
|
3836
|
+
const fieldValidatingSince = reactive(/* @__PURE__ */ new Map());
|
|
3837
|
+
const displayEngine = createDisplayEngine(ssr);
|
|
3838
|
+
registerCleanup(() => displayEngine.dispose());
|
|
3751
3839
|
const completedConstraints = defaultValues === void 0 ? void 0 : mergeStructural(schema, [], defaultValues);
|
|
3752
3840
|
const schemaResponse = schema.getDefaultValues({
|
|
3753
3841
|
useDefaultSchemaValues: true,
|
|
@@ -3876,12 +3964,18 @@ function createFormStore(options) {
|
|
|
3876
3964
|
}
|
|
3877
3965
|
const fieldValidationCounts = reactive(/* @__PURE__ */ new Map());
|
|
3878
3966
|
function incFieldValidation(key) {
|
|
3879
|
-
|
|
3967
|
+
fieldValidatingSince.set(key, ssr ? 0 : Date.now());
|
|
3968
|
+
const prevCount = fieldValidationCounts.get(key) ?? 0;
|
|
3969
|
+
fieldValidationCounts.set(key, prevCount + 1);
|
|
3880
3970
|
}
|
|
3881
3971
|
function decFieldValidation(key) {
|
|
3882
3972
|
const next = (fieldValidationCounts.get(key) ?? 0) - 1;
|
|
3883
|
-
if (next <= 0)
|
|
3884
|
-
|
|
3973
|
+
if (next <= 0) {
|
|
3974
|
+
fieldValidationCounts.delete(key);
|
|
3975
|
+
fieldValidatingSince.delete(key);
|
|
3976
|
+
} else {
|
|
3977
|
+
fieldValidationCounts.set(key, next);
|
|
3978
|
+
}
|
|
3885
3979
|
}
|
|
3886
3980
|
const initStamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3887
3981
|
diffAndApply({}, schemaInitialData, [], (patch) => {
|
|
@@ -3969,6 +4063,7 @@ function createFormStore(options) {
|
|
|
3969
4063
|
blankPaths,
|
|
3970
4064
|
originalBlankPaths,
|
|
3971
4065
|
fieldValidationCounts,
|
|
4066
|
+
fieldValidatingSince,
|
|
3972
4067
|
fieldValidationState,
|
|
3973
4068
|
schemaErrors,
|
|
3974
4069
|
activeValidations,
|
|
@@ -4236,7 +4331,7 @@ function createFormStore(options) {
|
|
|
4236
4331
|
prev.controller.abort();
|
|
4237
4332
|
}
|
|
4238
4333
|
const controller = new AbortController();
|
|
4239
|
-
const fresh = { controller, timer: null, settled: false };
|
|
4334
|
+
const fresh = { controller, timer: null, settled: false, released: false };
|
|
4240
4335
|
fieldValidationState.set(key, fresh);
|
|
4241
4336
|
const myEpoch = ++scheduleEpoch;
|
|
4242
4337
|
const run = () => {
|
|
@@ -4274,8 +4369,10 @@ function createFormStore(options) {
|
|
|
4274
4369
|
applySchemaErrorsForSubtree(scopePath ?? [], restamped);
|
|
4275
4370
|
}).catch(() => {
|
|
4276
4371
|
}).finally(() => {
|
|
4277
|
-
|
|
4278
|
-
|
|
4372
|
+
if (!fresh.released) {
|
|
4373
|
+
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
4374
|
+
decFieldValidation(key);
|
|
4375
|
+
}
|
|
4279
4376
|
fresh.settled = true;
|
|
4280
4377
|
});
|
|
4281
4378
|
};
|
|
@@ -4297,6 +4394,22 @@ function createFormStore(options) {
|
|
|
4297
4394
|
}
|
|
4298
4395
|
fieldValidationState.clear();
|
|
4299
4396
|
}
|
|
4397
|
+
function cancelFieldValidationUnder(prefix) {
|
|
4398
|
+
for (const [key, entry] of [...fieldValidationState]) {
|
|
4399
|
+
const segs = segmentsForPathKey(key);
|
|
4400
|
+
if (segs === null) continue;
|
|
4401
|
+
if (!isPathPrefix(prefix, segs)) continue;
|
|
4402
|
+
if (entry.timer !== null) {
|
|
4403
|
+
clearTimeout(entry.timer);
|
|
4404
|
+
} else if (!entry.settled && !entry.released) {
|
|
4405
|
+
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
4406
|
+
decFieldValidation(key);
|
|
4407
|
+
entry.released = true;
|
|
4408
|
+
}
|
|
4409
|
+
entry.controller.abort();
|
|
4410
|
+
fieldValidationState.delete(key);
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4300
4413
|
function onFormChange(listener) {
|
|
4301
4414
|
formChangeListeners.add(listener);
|
|
4302
4415
|
return () => {
|
|
@@ -4347,6 +4460,7 @@ function createFormStore(options) {
|
|
|
4347
4460
|
drainHooks.length = 0;
|
|
4348
4461
|
modules.clear();
|
|
4349
4462
|
cancelFieldValidation();
|
|
4463
|
+
fieldValidatingSince.clear();
|
|
4350
4464
|
formChangeListeners.clear();
|
|
4351
4465
|
submitSuccessListeners.clear();
|
|
4352
4466
|
resetListeners.clear();
|
|
@@ -4693,16 +4807,7 @@ function createFormStore(options) {
|
|
|
4693
4807
|
}
|
|
4694
4808
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4695
4809
|
for (const [pathKey, record] of fields) {
|
|
4696
|
-
fields.set(pathKey,
|
|
4697
|
-
path: record.path,
|
|
4698
|
-
updatedAt: now,
|
|
4699
|
-
connected: record.connected,
|
|
4700
|
-
focused: record.focused,
|
|
4701
|
-
blurred: record.blurred,
|
|
4702
|
-
touched: false,
|
|
4703
|
-
interacted: false,
|
|
4704
|
-
blurredAfterInteraction: false
|
|
4705
|
-
});
|
|
4810
|
+
fields.set(pathKey, withClearedHistoryFlags(record, now));
|
|
4706
4811
|
}
|
|
4707
4812
|
submissionGeneration.value += 1;
|
|
4708
4813
|
submitting.value = false;
|
|
@@ -4712,6 +4817,8 @@ function createFormStore(options) {
|
|
|
4712
4817
|
submitError.value = null;
|
|
4713
4818
|
departAttempts.value = 0;
|
|
4714
4819
|
cancelFieldValidation();
|
|
4820
|
+
displayEngine.clear();
|
|
4821
|
+
fieldValidatingSince.clear();
|
|
4715
4822
|
pathSnapshots.clear();
|
|
4716
4823
|
scheduleEpoch = 0;
|
|
4717
4824
|
lastCommittedEpoch = 0;
|
|
@@ -4727,6 +4834,12 @@ function createFormStore(options) {
|
|
|
4727
4834
|
function resetField(path) {
|
|
4728
4835
|
const { key: targetKey, segments: targetSegments } = canonicalizePath(path);
|
|
4729
4836
|
variantMemory.clearUnderPath(targetSegments);
|
|
4837
|
+
cancelFieldValidationUnder(targetSegments);
|
|
4838
|
+
for (const [snapKey] of [...pathSnapshots]) {
|
|
4839
|
+
const segs = segmentsForPathKey(snapKey);
|
|
4840
|
+
if (segs === null) continue;
|
|
4841
|
+
if (isPathPrefix(targetSegments, segs)) pathSnapshots.delete(snapKey);
|
|
4842
|
+
}
|
|
4730
4843
|
const leafEntry = originals.get(targetKey);
|
|
4731
4844
|
if (leafEntry !== void 0) {
|
|
4732
4845
|
const wrote = setValueAtPath(targetSegments, leafEntry.value);
|
|
@@ -4776,16 +4889,7 @@ function createFormStore(options) {
|
|
|
4776
4889
|
function clearFieldRecordFlags(pathKey) {
|
|
4777
4890
|
const record = fields.get(pathKey);
|
|
4778
4891
|
if (record === void 0) return;
|
|
4779
|
-
fields.set(pathKey,
|
|
4780
|
-
path: record.path,
|
|
4781
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4782
|
-
connected: record.connected,
|
|
4783
|
-
focused: record.focused,
|
|
4784
|
-
blurred: record.blurred,
|
|
4785
|
-
touched: false,
|
|
4786
|
-
interacted: false,
|
|
4787
|
-
blurredAfterInteraction: false
|
|
4788
|
-
});
|
|
4892
|
+
fields.set(pathKey, withClearedHistoryFlags(record, (/* @__PURE__ */ new Date()).toISOString()));
|
|
4789
4893
|
}
|
|
4790
4894
|
function isPristineAtPath(path) {
|
|
4791
4895
|
const { key, segments } = canonicalizePath(path);
|
|
@@ -4865,6 +4969,8 @@ function createFormStore(options) {
|
|
|
4865
4969
|
pathHasAsyncValidation,
|
|
4866
4970
|
pathHasAsyncValidationByKey,
|
|
4867
4971
|
fieldValidationCounts,
|
|
4972
|
+
fieldValidatingSince,
|
|
4973
|
+
displayEngine,
|
|
4868
4974
|
applyFormReplacement,
|
|
4869
4975
|
setValueAtPath,
|
|
4870
4976
|
getValueAtPath,
|
|
@@ -4949,7 +5055,7 @@ function errorsEqual(a, b) {
|
|
|
4949
5055
|
}
|
|
4950
5056
|
return true;
|
|
4951
5057
|
}
|
|
4952
|
-
function diffBlankPaths
|
|
5058
|
+
function diffBlankPaths(prev, curr) {
|
|
4953
5059
|
const added = [];
|
|
4954
5060
|
const removed = [];
|
|
4955
5061
|
for (const k of curr) if (!prev.has(k)) added.push(k);
|
|
@@ -5037,7 +5143,7 @@ function createHistoryModule(state, config) {
|
|
|
5037
5143
|
diffAndApply(prevSnap.form, newSnap.form, [], (p) => formPatches.push(p));
|
|
5038
5144
|
const prevBlankSet = new Set(prevSnap.blankPaths);
|
|
5039
5145
|
const currBlankSet = new Set(newSnap.blankPaths);
|
|
5040
|
-
const blankDiff = diffBlankPaths
|
|
5146
|
+
const blankDiff = diffBlankPaths(prevBlankSet, currBlankSet);
|
|
5041
5147
|
const delta = {
|
|
5042
5148
|
formPatches,
|
|
5043
5149
|
blankPathsAdded: blankDiff.added,
|
|
@@ -5104,643 +5210,6 @@ function createHistoryModule(state, config) {
|
|
|
5104
5210
|
};
|
|
5105
5211
|
}
|
|
5106
5212
|
|
|
5107
|
-
function wirePersistence(state, config) {
|
|
5108
|
-
let fingerprint;
|
|
5109
|
-
try {
|
|
5110
|
-
fingerprint = hashStableString(state.schema.fingerprint());
|
|
5111
|
-
} catch (err) {
|
|
5112
|
-
if (__DEV__) {
|
|
5113
|
-
console.warn(
|
|
5114
|
-
`[attaform] Could not fingerprint the schema for form '${state.formKey}': ${err instanceof Error ? err.message : String(err)}. Persistence falls back to a fingerprint-free key, so a schema change won't auto-invalidate a saved draft.`
|
|
5115
|
-
);
|
|
5116
|
-
}
|
|
5117
|
-
fingerprint = "unfingerprinted";
|
|
5118
|
-
}
|
|
5119
|
-
const base = resolveStorageKeyBase(config, state.formKey);
|
|
5120
|
-
const key = `${base}:${fingerprint}`;
|
|
5121
|
-
const debounceMs = normalizeNumericOption({
|
|
5122
|
-
value: config.debounceMs ?? DEFAULT_PERSISTENCE_DEBOUNCE_MS,
|
|
5123
|
-
source: "useForm.persist.debounceMs",
|
|
5124
|
-
allowInfinity: false,
|
|
5125
|
-
min: 0,
|
|
5126
|
-
defaultValue: DEFAULT_PERSISTENCE_DEBOUNCE_MS
|
|
5127
|
-
});
|
|
5128
|
-
const include = config.include ?? "form";
|
|
5129
|
-
const clearOnSubmitSuccess = config.clearOnSubmitSuccess ?? true;
|
|
5130
|
-
const adapterPromise = getStorageAdapter(config.storage);
|
|
5131
|
-
let disposed = false;
|
|
5132
|
-
const isDisposed = () => disposed;
|
|
5133
|
-
let inFlightFinalFlush = null;
|
|
5134
|
-
let pendingOptedInPaths = null;
|
|
5135
|
-
const writer = createDebouncedWriter(async () => {
|
|
5136
|
-
const optedInPaths = pendingOptedInPaths ?? new Set(state.persistOptIns.optedInPaths());
|
|
5137
|
-
pendingOptedInPaths = null;
|
|
5138
|
-
const adapter = await adapterPromise;
|
|
5139
|
-
if (isDisposed()) return;
|
|
5140
|
-
if (optedInPaths.size === 0) {
|
|
5141
|
-
await adapter.removeItem(key);
|
|
5142
|
-
return;
|
|
5143
|
-
}
|
|
5144
|
-
const rawForm = toRaw(state.form.value);
|
|
5145
|
-
const filteredForm = stripUnacknowledgedSensitiveLeaves(
|
|
5146
|
-
pluckPaths(rawForm, optedInPaths),
|
|
5147
|
-
optedInPaths,
|
|
5148
|
-
state.isSensitivePath
|
|
5149
|
-
);
|
|
5150
|
-
const filteredSchemaErrors = filterErrorsByPaths(state.schemaErrors, optedInPaths);
|
|
5151
|
-
const filteredUserErrors = filterErrorsByPaths(state.userErrors, optedInPaths);
|
|
5152
|
-
const filteredTransientEmpty = /* @__PURE__ */ new Set();
|
|
5153
|
-
for (const tk of state.blankPaths) {
|
|
5154
|
-
if (optedInPaths.has(tk)) filteredTransientEmpty.add(tk);
|
|
5155
|
-
}
|
|
5156
|
-
const payload = buildPersistedPayload(
|
|
5157
|
-
filteredForm,
|
|
5158
|
-
include,
|
|
5159
|
-
filteredSchemaErrors,
|
|
5160
|
-
filteredUserErrors,
|
|
5161
|
-
filteredTransientEmpty
|
|
5162
|
-
);
|
|
5163
|
-
await adapter.setItem(key, payload);
|
|
5164
|
-
}, debounceMs);
|
|
5165
|
-
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
5166
|
-
if (isDisposed() || inFlightFinalFlush !== null) return;
|
|
5167
|
-
if (meta?.crossTab === true) return;
|
|
5168
|
-
if (meta?.persist !== true) return;
|
|
5169
|
-
pendingOptedInPaths = new Set(state.persistOptIns.optedInPaths());
|
|
5170
|
-
writer.schedule();
|
|
5171
|
-
});
|
|
5172
|
-
const unsubscribeSuccess = clearOnSubmitSuccess ? state.onSubmitSuccess(() => {
|
|
5173
|
-
if (isDisposed()) return;
|
|
5174
|
-
void (async () => {
|
|
5175
|
-
await writer.flush();
|
|
5176
|
-
if (isDisposed()) return;
|
|
5177
|
-
const adapter = await adapterPromise;
|
|
5178
|
-
if (isDisposed()) return;
|
|
5179
|
-
await adapter.removeItem(key);
|
|
5180
|
-
})();
|
|
5181
|
-
}) : () => void 0;
|
|
5182
|
-
const handlePageHide = () => {
|
|
5183
|
-
if (isDisposed()) return;
|
|
5184
|
-
void writer.flush();
|
|
5185
|
-
};
|
|
5186
|
-
if (typeof window !== "undefined") {
|
|
5187
|
-
window.addEventListener("pagehide", handlePageHide);
|
|
5188
|
-
}
|
|
5189
|
-
void (async () => {
|
|
5190
|
-
const adapter = await adapterPromise;
|
|
5191
|
-
if (isDisposed()) return;
|
|
5192
|
-
void cleanupOrphanKeys(adapter, base, key);
|
|
5193
|
-
try {
|
|
5194
|
-
const raw = await adapter.getItem(key);
|
|
5195
|
-
const payload = readPersistedPayload(raw);
|
|
5196
|
-
if (payload === null) {
|
|
5197
|
-
if (raw !== null && raw !== void 0) {
|
|
5198
|
-
await adapter.removeItem(key);
|
|
5199
|
-
}
|
|
5200
|
-
return;
|
|
5201
|
-
}
|
|
5202
|
-
if (isDisposed()) return;
|
|
5203
|
-
const merged = mergeSparseHydration(
|
|
5204
|
-
toRaw(state.form.value),
|
|
5205
|
-
payload.data.form,
|
|
5206
|
-
state.schema
|
|
5207
|
-
);
|
|
5208
|
-
state.applyFormReplacement(merged, { hydration: true });
|
|
5209
|
-
const persistedLeafPaths = collectPersistedLeafPaths(payload.data.form);
|
|
5210
|
-
for (const k of persistedLeafPaths) {
|
|
5211
|
-
state.blankPaths.delete(k);
|
|
5212
|
-
state.originalBlankPaths.delete(k);
|
|
5213
|
-
}
|
|
5214
|
-
for (const k of payload.data.blankPaths ?? []) {
|
|
5215
|
-
state.blankPaths.add(k);
|
|
5216
|
-
state.originalBlankPaths.add(k);
|
|
5217
|
-
}
|
|
5218
|
-
if (include === "form+errors") {
|
|
5219
|
-
if (payload.data.schemaErrors !== void 0) {
|
|
5220
|
-
const flat = payload.data.schemaErrors.flatMap(([, errs]) => errs);
|
|
5221
|
-
state.setAllSchemaErrors(flat);
|
|
5222
|
-
}
|
|
5223
|
-
if (payload.data.userErrors !== void 0) {
|
|
5224
|
-
const flat = payload.data.userErrors.flatMap(([, errs]) => errs);
|
|
5225
|
-
state.setAllUserErrors(flat);
|
|
5226
|
-
}
|
|
5227
|
-
}
|
|
5228
|
-
state.scheduleFieldValidation(
|
|
5229
|
-
[],
|
|
5230
|
-
true
|
|
5231
|
-
/* immediate */
|
|
5232
|
-
);
|
|
5233
|
-
} catch {
|
|
5234
|
-
}
|
|
5235
|
-
})();
|
|
5236
|
-
async function writePathImmediately(path) {
|
|
5237
|
-
if (isDisposed()) return;
|
|
5238
|
-
await writer.flush();
|
|
5239
|
-
if (isDisposed()) return;
|
|
5240
|
-
const adapter = await adapterPromise;
|
|
5241
|
-
if (isDisposed()) return;
|
|
5242
|
-
const raw = await adapter.getItem(key);
|
|
5243
|
-
const existing = readPersistedPayload(raw);
|
|
5244
|
-
const value = getAtPath(toRaw(state.form.value), path);
|
|
5245
|
-
const nextForm = setAtPath(existing?.data.form ?? {}, path, value);
|
|
5246
|
-
const { key: pathKey } = canonicalizePath(path);
|
|
5247
|
-
const transientSet = new Set(
|
|
5248
|
-
(existing?.data.blankPaths ?? []).filter(
|
|
5249
|
-
(k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
|
|
5250
|
-
)
|
|
5251
|
-
);
|
|
5252
|
-
for (const liveKey of state.blankPaths) {
|
|
5253
|
-
if (liveKey === pathKey || isDescendantPathKey(liveKey, pathKey)) {
|
|
5254
|
-
transientSet.add(liveKey);
|
|
5255
|
-
}
|
|
5256
|
-
}
|
|
5257
|
-
if (include === "form") {
|
|
5258
|
-
await adapter.setItem(
|
|
5259
|
-
key,
|
|
5260
|
-
buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
|
|
5261
|
-
);
|
|
5262
|
-
return;
|
|
5263
|
-
}
|
|
5264
|
-
const schemaMap = new Map(existing?.data.schemaErrors ?? []);
|
|
5265
|
-
const userMap = new Map(existing?.data.userErrors ?? []);
|
|
5266
|
-
const currentSchema = state.schemaErrors.get(pathKey);
|
|
5267
|
-
const currentUser = state.userErrors.get(pathKey);
|
|
5268
|
-
if (currentSchema !== void 0 && currentSchema.length > 0) {
|
|
5269
|
-
schemaMap.set(pathKey, [...currentSchema]);
|
|
5270
|
-
} else {
|
|
5271
|
-
schemaMap.delete(pathKey);
|
|
5272
|
-
}
|
|
5273
|
-
if (currentUser !== void 0 && currentUser.length > 0) {
|
|
5274
|
-
userMap.set(pathKey, [...currentUser]);
|
|
5275
|
-
} else {
|
|
5276
|
-
userMap.delete(pathKey);
|
|
5277
|
-
}
|
|
5278
|
-
await adapter.setItem(
|
|
5279
|
-
key,
|
|
5280
|
-
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5281
|
-
);
|
|
5282
|
-
}
|
|
5283
|
-
async function clearPersistedDraft(path) {
|
|
5284
|
-
if (isDisposed()) return;
|
|
5285
|
-
await writer.flush();
|
|
5286
|
-
if (isDisposed()) return;
|
|
5287
|
-
const adapter = await adapterPromise;
|
|
5288
|
-
if (isDisposed()) return;
|
|
5289
|
-
if (path === void 0) {
|
|
5290
|
-
await adapter.removeItem(key);
|
|
5291
|
-
return;
|
|
5292
|
-
}
|
|
5293
|
-
const raw = await adapter.getItem(key);
|
|
5294
|
-
const existing = readPersistedPayload(raw);
|
|
5295
|
-
if (existing === null) return;
|
|
5296
|
-
const nextForm = deleteAtPath(existing.data.form, path);
|
|
5297
|
-
if (isEmptyContainer(nextForm)) {
|
|
5298
|
-
await adapter.removeItem(key);
|
|
5299
|
-
return;
|
|
5300
|
-
}
|
|
5301
|
-
const { key: pathKey } = canonicalizePath(path);
|
|
5302
|
-
const transientSet = new Set(
|
|
5303
|
-
(existing.data.blankPaths ?? []).filter(
|
|
5304
|
-
(k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
|
|
5305
|
-
)
|
|
5306
|
-
);
|
|
5307
|
-
if (include === "form") {
|
|
5308
|
-
await adapter.setItem(
|
|
5309
|
-
key,
|
|
5310
|
-
buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
|
|
5311
|
-
);
|
|
5312
|
-
return;
|
|
5313
|
-
}
|
|
5314
|
-
const schemaErrors = (existing.data.schemaErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5315
|
-
const userErrors = (existing.data.userErrors ?? []).filter(([k]) => k !== pathKey);
|
|
5316
|
-
const schemaMap = new Map(schemaErrors.map(([k, v]) => [k, [...v]]));
|
|
5317
|
-
const userMap = new Map(userErrors.map(([k, v]) => [k, [...v]]));
|
|
5318
|
-
await adapter.setItem(
|
|
5319
|
-
key,
|
|
5320
|
-
buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
|
|
5321
|
-
);
|
|
5322
|
-
}
|
|
5323
|
-
function awaitPendingWrites() {
|
|
5324
|
-
if (inFlightFinalFlush !== null) return inFlightFinalFlush;
|
|
5325
|
-
if (isDisposed()) return Promise.resolve();
|
|
5326
|
-
return writer.flush().catch(() => void 0);
|
|
5327
|
-
}
|
|
5328
|
-
function dispose() {
|
|
5329
|
-
if (isDisposed() || inFlightFinalFlush !== null) return;
|
|
5330
|
-
unsubscribeChange();
|
|
5331
|
-
unsubscribeSuccess();
|
|
5332
|
-
if (typeof window !== "undefined") {
|
|
5333
|
-
window.removeEventListener("pagehide", handlePageHide);
|
|
5334
|
-
}
|
|
5335
|
-
inFlightFinalFlush = writer.flush().catch(() => void 0).finally(() => {
|
|
5336
|
-
disposed = true;
|
|
5337
|
-
inFlightFinalFlush = null;
|
|
5338
|
-
});
|
|
5339
|
-
}
|
|
5340
|
-
return {
|
|
5341
|
-
wiredConfig: config,
|
|
5342
|
-
writePathImmediately,
|
|
5343
|
-
clearPersistedDraft,
|
|
5344
|
-
awaitPendingWrites,
|
|
5345
|
-
dispose
|
|
5346
|
-
};
|
|
5347
|
-
}
|
|
5348
|
-
function isEmptyContainer(value) {
|
|
5349
|
-
if (value === void 0 || value === null) return true;
|
|
5350
|
-
if (Array.isArray(value)) return value.length === 0;
|
|
5351
|
-
if (isPlainRecord(value)) return Object.keys(value).length === 0;
|
|
5352
|
-
return false;
|
|
5353
|
-
}
|
|
5354
|
-
function collectPersistedLeafPaths(form) {
|
|
5355
|
-
const out = [];
|
|
5356
|
-
walk(form, []);
|
|
5357
|
-
return out;
|
|
5358
|
-
function walk(node, prefix) {
|
|
5359
|
-
if (Array.isArray(node)) {
|
|
5360
|
-
for (let i = 0; i < node.length; i++) {
|
|
5361
|
-
walk(node[i], [...prefix, i]);
|
|
5362
|
-
}
|
|
5363
|
-
return;
|
|
5364
|
-
}
|
|
5365
|
-
if (isPlainRecord(node)) {
|
|
5366
|
-
for (const key of Object.keys(node)) {
|
|
5367
|
-
walk(node[key], [...prefix, key]);
|
|
5368
|
-
}
|
|
5369
|
-
return;
|
|
5370
|
-
}
|
|
5371
|
-
if (prefix.length === 0) return;
|
|
5372
|
-
out.push(canonicalizePath(prefix).key);
|
|
5373
|
-
}
|
|
5374
|
-
}
|
|
5375
|
-
function isDescendantPathKey(candidate, ancestor) {
|
|
5376
|
-
if (candidate.length <= ancestor.length) return false;
|
|
5377
|
-
if (!ancestor.endsWith("]")) return false;
|
|
5378
|
-
const childPrefix = `${ancestor.slice(0, -1)},`;
|
|
5379
|
-
return candidate.startsWith(childPrefix);
|
|
5380
|
-
}
|
|
5381
|
-
|
|
5382
|
-
const PROTOCOL_VERSION = 1;
|
|
5383
|
-
const JOIN_COLLECTION_WINDOW_MS = 50;
|
|
5384
|
-
const SNAPSHOT_TIMEOUT_MS = 200;
|
|
5385
|
-
const MAX_LEADER_ATTEMPTS = 3;
|
|
5386
|
-
const SNAPSHOT_RESPONSE_MIN_INTERVAL_MS = 500;
|
|
5387
|
-
function isFileLikeValue(value) {
|
|
5388
|
-
if (typeof File !== "undefined" && value instanceof File) return true;
|
|
5389
|
-
if (typeof Blob !== "undefined" && value instanceof Blob) return true;
|
|
5390
|
-
return false;
|
|
5391
|
-
}
|
|
5392
|
-
function isInboundShapeAcceptable(schema, path, value) {
|
|
5393
|
-
if (isFileLikeValue(value)) return false;
|
|
5394
|
-
let kind;
|
|
5395
|
-
if (Array.isArray(value)) {
|
|
5396
|
-
kind = "array";
|
|
5397
|
-
} else if (value !== null && typeof value === "object" && isPlainRecord(value)) {
|
|
5398
|
-
kind = "object";
|
|
5399
|
-
} else {
|
|
5400
|
-
kind = slimKindOf(value);
|
|
5401
|
-
}
|
|
5402
|
-
const accepted = schema.getSlimPrimitiveTypesAtPath(path);
|
|
5403
|
-
if (!accepted.has(kind)) return false;
|
|
5404
|
-
if (Array.isArray(value)) {
|
|
5405
|
-
for (let i = 0; i < value.length; i++) {
|
|
5406
|
-
if (!isInboundShapeAcceptable(schema, [...path, i], value[i])) return false;
|
|
5407
|
-
}
|
|
5408
|
-
return true;
|
|
5409
|
-
}
|
|
5410
|
-
if (isPlainRecord(value)) {
|
|
5411
|
-
for (const key of Object.keys(value)) {
|
|
5412
|
-
if (!isInboundShapeAcceptable(schema, [...path, key], value[key])) return false;
|
|
5413
|
-
}
|
|
5414
|
-
return true;
|
|
5415
|
-
}
|
|
5416
|
-
return true;
|
|
5417
|
-
}
|
|
5418
|
-
function diffBlankPaths(prev, curr) {
|
|
5419
|
-
const added = [];
|
|
5420
|
-
const removed = [];
|
|
5421
|
-
const prevSet = new Set(prev);
|
|
5422
|
-
for (const k of curr) if (!prevSet.has(k)) added.push(k);
|
|
5423
|
-
for (const k of prev) if (!curr.has(k)) removed.push(k);
|
|
5424
|
-
return { added, removed };
|
|
5425
|
-
}
|
|
5426
|
-
function snapshotForm(form) {
|
|
5427
|
-
return structuralSnapshot(form);
|
|
5428
|
-
}
|
|
5429
|
-
function stripSensitivePathsDeep(value, pathSoFar, isSensitivePath) {
|
|
5430
|
-
if (isFileLikeValue(value)) return void 0;
|
|
5431
|
-
if (value === null || typeof value !== "object") return value;
|
|
5432
|
-
if (Array.isArray(value)) {
|
|
5433
|
-
return value.map((item, i) => stripSensitivePathsDeep(item, [...pathSoFar, i], isSensitivePath));
|
|
5434
|
-
}
|
|
5435
|
-
const proto = Object.getPrototypeOf(value);
|
|
5436
|
-
if (proto !== Object.prototype && proto !== null) return value;
|
|
5437
|
-
const out = {};
|
|
5438
|
-
const src = value;
|
|
5439
|
-
for (const key of Object.keys(src)) {
|
|
5440
|
-
const childPath = [...pathSoFar, key];
|
|
5441
|
-
if (isSensitivePath(childPath)) {
|
|
5442
|
-
safeAssign(out, key, void 0);
|
|
5443
|
-
continue;
|
|
5444
|
-
}
|
|
5445
|
-
safeAssign(out, key, stripSensitivePathsDeep(src[key], childPath, isSensitivePath));
|
|
5446
|
-
}
|
|
5447
|
-
return out;
|
|
5448
|
-
}
|
|
5449
|
-
function isStringArray(value) {
|
|
5450
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
5451
|
-
}
|
|
5452
|
-
function isPatchArray(value) {
|
|
5453
|
-
return Array.isArray(value) && value.every(
|
|
5454
|
-
(p) => p !== null && typeof p === "object" && Array.isArray(p.path)
|
|
5455
|
-
);
|
|
5456
|
-
}
|
|
5457
|
-
function isValidSyncMessage(data) {
|
|
5458
|
-
if (data === null || typeof data !== "object") return false;
|
|
5459
|
-
const m = data;
|
|
5460
|
-
if (m["v"] !== PROTOCOL_VERSION) return false;
|
|
5461
|
-
if (typeof m["senderId"] !== "string") return false;
|
|
5462
|
-
if (typeof m["kind"] !== "string") return false;
|
|
5463
|
-
switch (m["kind"]) {
|
|
5464
|
-
case "hello":
|
|
5465
|
-
case "announce":
|
|
5466
|
-
return true;
|
|
5467
|
-
case "requestSnapshot":
|
|
5468
|
-
return typeof m["targetId"] === "string";
|
|
5469
|
-
case "snapshot":
|
|
5470
|
-
return isStringArray(m["blankPaths"]) && "form" in m;
|
|
5471
|
-
case "patches":
|
|
5472
|
-
return isPatchArray(m["formPatches"]) && isStringArray(m["blankPathsAdded"]) && isStringArray(m["blankPathsRemoved"]);
|
|
5473
|
-
default:
|
|
5474
|
-
return false;
|
|
5475
|
-
}
|
|
5476
|
-
}
|
|
5477
|
-
function generateSenderId() {
|
|
5478
|
-
try {
|
|
5479
|
-
return globalThis.crypto.randomUUID();
|
|
5480
|
-
} catch {
|
|
5481
|
-
return `atta-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;
|
|
5482
|
-
}
|
|
5483
|
-
}
|
|
5484
|
-
function createMultiTabSyncModule(state, channelName, options) {
|
|
5485
|
-
if (typeof BroadcastChannel === "undefined") {
|
|
5486
|
-
return {
|
|
5487
|
-
dispose: () => void 0,
|
|
5488
|
-
lifecycle: () => "established",
|
|
5489
|
-
senderId: "",
|
|
5490
|
-
channelName
|
|
5491
|
-
};
|
|
5492
|
-
}
|
|
5493
|
-
let channel;
|
|
5494
|
-
try {
|
|
5495
|
-
channel = new BroadcastChannel(channelName);
|
|
5496
|
-
} catch {
|
|
5497
|
-
return {
|
|
5498
|
-
dispose: () => void 0,
|
|
5499
|
-
lifecycle: () => "established",
|
|
5500
|
-
senderId: "",
|
|
5501
|
-
channelName
|
|
5502
|
-
};
|
|
5503
|
-
}
|
|
5504
|
-
const senderId = generateSenderId();
|
|
5505
|
-
let lifecycle = "joining";
|
|
5506
|
-
let disposed = false;
|
|
5507
|
-
const peerIds = /* @__PURE__ */ new Set();
|
|
5508
|
-
const lastSnapshotResponseAt = /* @__PURE__ */ new Map();
|
|
5509
|
-
let joinCollectionTimer = null;
|
|
5510
|
-
let snapshotTimeoutTimer = null;
|
|
5511
|
-
let leaderAttempts = 0;
|
|
5512
|
-
let prior = {
|
|
5513
|
-
form: snapshotForm(state.form.value),
|
|
5514
|
-
blankPathsSnapshot: [...state.blankPaths]
|
|
5515
|
-
};
|
|
5516
|
-
function safePost(msg) {
|
|
5517
|
-
if (disposed) return;
|
|
5518
|
-
try {
|
|
5519
|
-
channel.postMessage(msg);
|
|
5520
|
-
} catch {
|
|
5521
|
-
}
|
|
5522
|
-
}
|
|
5523
|
-
function refreshPrior() {
|
|
5524
|
-
prior = {
|
|
5525
|
-
form: snapshotForm(state.form.value),
|
|
5526
|
-
blankPathsSnapshot: [...state.blankPaths]
|
|
5527
|
-
};
|
|
5528
|
-
}
|
|
5529
|
-
function isPathLocallySuppressed(path) {
|
|
5530
|
-
if (options.isSensitivePath(path)) return true;
|
|
5531
|
-
const { key } = canonicalizePath([...path]);
|
|
5532
|
-
if (options.noSyncPaths.has(key)) return true;
|
|
5533
|
-
return false;
|
|
5534
|
-
}
|
|
5535
|
-
function postPatches() {
|
|
5536
|
-
if (lifecycle !== "established") return;
|
|
5537
|
-
const next = snapshotForm(state.form.value);
|
|
5538
|
-
const rawPatches = [];
|
|
5539
|
-
diffAndApply(prior.form, next, [], (p) => rawPatches.push(p));
|
|
5540
|
-
const safePatches = [];
|
|
5541
|
-
for (const p of rawPatches) {
|
|
5542
|
-
if (isPathLocallySuppressed(p.path)) continue;
|
|
5543
|
-
if ("value" in p && isFileLikeValue(p.value)) continue;
|
|
5544
|
-
safePatches.push(p);
|
|
5545
|
-
}
|
|
5546
|
-
const { added, removed } = diffBlankPaths(prior.blankPathsSnapshot, state.blankPaths);
|
|
5547
|
-
if (safePatches.length === 0 && added.length === 0 && removed.length === 0) {
|
|
5548
|
-
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
5549
|
-
return;
|
|
5550
|
-
}
|
|
5551
|
-
safePost({
|
|
5552
|
-
v: PROTOCOL_VERSION,
|
|
5553
|
-
kind: "patches",
|
|
5554
|
-
senderId,
|
|
5555
|
-
formPatches: safePatches,
|
|
5556
|
-
blankPathsAdded: added,
|
|
5557
|
-
blankPathsRemoved: removed
|
|
5558
|
-
});
|
|
5559
|
-
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
5560
|
-
}
|
|
5561
|
-
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
5562
|
-
if (disposed) return;
|
|
5563
|
-
if (lifecycle !== "established") return;
|
|
5564
|
-
if (meta?.crossTab === true) return;
|
|
5565
|
-
if (meta?.hydration === true) {
|
|
5566
|
-
refreshPrior();
|
|
5567
|
-
return;
|
|
5568
|
-
}
|
|
5569
|
-
postPatches();
|
|
5570
|
-
});
|
|
5571
|
-
function applyIncomingForm(form, blankPaths) {
|
|
5572
|
-
state.blankPaths.clear();
|
|
5573
|
-
for (const k of blankPaths) state.blankPaths.add(k);
|
|
5574
|
-
state.applyFormReplacement(form, { crossTab: true, persist: false });
|
|
5575
|
-
refreshPrior();
|
|
5576
|
-
}
|
|
5577
|
-
function handlePatches(msg) {
|
|
5578
|
-
if (lifecycle !== "established") return;
|
|
5579
|
-
const safePatches = [];
|
|
5580
|
-
for (const p of msg.formPatches) {
|
|
5581
|
-
if (!Array.isArray(p.path)) continue;
|
|
5582
|
-
if (isPathLocallySuppressed(p.path)) continue;
|
|
5583
|
-
if ("value" in p && !isInboundShapeAcceptable(state.schema, p.path, p.value)) {
|
|
5584
|
-
continue;
|
|
5585
|
-
}
|
|
5586
|
-
safePatches.push(p);
|
|
5587
|
-
}
|
|
5588
|
-
const safeBlankAdded = [];
|
|
5589
|
-
for (const k of msg.blankPathsAdded) {
|
|
5590
|
-
const segs = canonicalizePath(k).segments;
|
|
5591
|
-
if (isPathLocallySuppressed(segs)) continue;
|
|
5592
|
-
safeBlankAdded.push(k);
|
|
5593
|
-
}
|
|
5594
|
-
const safeBlankRemoved = [];
|
|
5595
|
-
for (const k of msg.blankPathsRemoved) {
|
|
5596
|
-
const segs = canonicalizePath(k).segments;
|
|
5597
|
-
if (isPathLocallySuppressed(segs)) continue;
|
|
5598
|
-
safeBlankRemoved.push(k);
|
|
5599
|
-
}
|
|
5600
|
-
if (safePatches.length === 0 && safeBlankAdded.length === 0 && safeBlankRemoved.length === 0) {
|
|
5601
|
-
return;
|
|
5602
|
-
}
|
|
5603
|
-
const candidate = applyPatchesForward(state.form.value, safePatches);
|
|
5604
|
-
try {
|
|
5605
|
-
options.validateForm(state.form.value);
|
|
5606
|
-
try {
|
|
5607
|
-
options.validateForm(candidate);
|
|
5608
|
-
} catch {
|
|
5609
|
-
return;
|
|
5610
|
-
}
|
|
5611
|
-
} catch {
|
|
5612
|
-
}
|
|
5613
|
-
const nextBlankPaths = new Set(state.blankPaths);
|
|
5614
|
-
for (const k of safeBlankRemoved) nextBlankPaths.delete(k);
|
|
5615
|
-
for (const k of safeBlankAdded) nextBlankPaths.add(k);
|
|
5616
|
-
applyIncomingForm(candidate, [...nextBlankPaths]);
|
|
5617
|
-
}
|
|
5618
|
-
function handleSnapshot(msg) {
|
|
5619
|
-
if (lifecycle !== "joining") return;
|
|
5620
|
-
if (!isInboundShapeAcceptable(state.schema, [], msg.form)) return;
|
|
5621
|
-
if (snapshotTimeoutTimer !== null) {
|
|
5622
|
-
clearTimeout(snapshotTimeoutTimer);
|
|
5623
|
-
snapshotTimeoutTimer = null;
|
|
5624
|
-
}
|
|
5625
|
-
if (joinCollectionTimer !== null) {
|
|
5626
|
-
clearTimeout(joinCollectionTimer);
|
|
5627
|
-
joinCollectionTimer = null;
|
|
5628
|
-
}
|
|
5629
|
-
applyIncomingForm(msg.form, msg.blankPaths);
|
|
5630
|
-
lifecycle = "established";
|
|
5631
|
-
peerIds.clear();
|
|
5632
|
-
}
|
|
5633
|
-
function respondToHello() {
|
|
5634
|
-
safePost({ v: PROTOCOL_VERSION, kind: "announce", senderId });
|
|
5635
|
-
}
|
|
5636
|
-
function respondToSnapshotRequest(requesterId) {
|
|
5637
|
-
const now = Date.now();
|
|
5638
|
-
const last = lastSnapshotResponseAt.get(requesterId);
|
|
5639
|
-
if (last !== void 0 && now - last < SNAPSHOT_RESPONSE_MIN_INTERVAL_MS) return;
|
|
5640
|
-
lastSnapshotResponseAt.set(requesterId, now);
|
|
5641
|
-
const scrubbedForm = stripSensitivePathsDeep(state.form.value, [], options.isSensitivePath);
|
|
5642
|
-
safePost({
|
|
5643
|
-
v: PROTOCOL_VERSION,
|
|
5644
|
-
kind: "snapshot",
|
|
5645
|
-
senderId,
|
|
5646
|
-
form: scrubbedForm,
|
|
5647
|
-
blankPaths: [...state.blankPaths]
|
|
5648
|
-
});
|
|
5649
|
-
}
|
|
5650
|
-
channel.onmessage = (event) => {
|
|
5651
|
-
if (disposed) return;
|
|
5652
|
-
try {
|
|
5653
|
-
const data = event.data;
|
|
5654
|
-
if (!isValidSyncMessage(data)) return;
|
|
5655
|
-
const msg = data;
|
|
5656
|
-
if (msg.senderId === senderId) return;
|
|
5657
|
-
switch (msg.kind) {
|
|
5658
|
-
case "hello":
|
|
5659
|
-
if (lifecycle !== "established") return;
|
|
5660
|
-
respondToHello();
|
|
5661
|
-
break;
|
|
5662
|
-
case "announce":
|
|
5663
|
-
if (lifecycle === "joining") peerIds.add(msg.senderId);
|
|
5664
|
-
break;
|
|
5665
|
-
case "requestSnapshot":
|
|
5666
|
-
if (lifecycle !== "established") return;
|
|
5667
|
-
if (msg.targetId !== senderId) return;
|
|
5668
|
-
respondToSnapshotRequest(msg.senderId);
|
|
5669
|
-
break;
|
|
5670
|
-
case "snapshot":
|
|
5671
|
-
handleSnapshot(msg);
|
|
5672
|
-
break;
|
|
5673
|
-
case "patches":
|
|
5674
|
-
handlePatches(msg);
|
|
5675
|
-
break;
|
|
5676
|
-
}
|
|
5677
|
-
} catch {
|
|
5678
|
-
}
|
|
5679
|
-
};
|
|
5680
|
-
function electLeaderAndRequest() {
|
|
5681
|
-
if (disposed) return;
|
|
5682
|
-
if (peerIds.size === 0) {
|
|
5683
|
-
lifecycle = "established";
|
|
5684
|
-
refreshPrior();
|
|
5685
|
-
return;
|
|
5686
|
-
}
|
|
5687
|
-
const sorted = [...peerIds].sort();
|
|
5688
|
-
const leaderId = sorted[0];
|
|
5689
|
-
peerIds.delete(leaderId);
|
|
5690
|
-
leaderAttempts++;
|
|
5691
|
-
safePost({
|
|
5692
|
-
v: PROTOCOL_VERSION,
|
|
5693
|
-
kind: "requestSnapshot",
|
|
5694
|
-
senderId,
|
|
5695
|
-
targetId: leaderId
|
|
5696
|
-
});
|
|
5697
|
-
snapshotTimeoutTimer = setTimeout(() => {
|
|
5698
|
-
snapshotTimeoutTimer = null;
|
|
5699
|
-
if (disposed) return;
|
|
5700
|
-
if (lifecycle === "established") return;
|
|
5701
|
-
if (leaderAttempts >= MAX_LEADER_ATTEMPTS || peerIds.size === 0) {
|
|
5702
|
-
lifecycle = "established";
|
|
5703
|
-
refreshPrior();
|
|
5704
|
-
return;
|
|
5705
|
-
}
|
|
5706
|
-
electLeaderAndRequest();
|
|
5707
|
-
}, SNAPSHOT_TIMEOUT_MS);
|
|
5708
|
-
}
|
|
5709
|
-
function joinFlow() {
|
|
5710
|
-
safePost({ v: PROTOCOL_VERSION, kind: "hello", senderId });
|
|
5711
|
-
joinCollectionTimer = setTimeout(() => {
|
|
5712
|
-
joinCollectionTimer = null;
|
|
5713
|
-
if (disposed) return;
|
|
5714
|
-
if (lifecycle === "established") return;
|
|
5715
|
-
electLeaderAndRequest();
|
|
5716
|
-
}, JOIN_COLLECTION_WINDOW_MS);
|
|
5717
|
-
}
|
|
5718
|
-
joinFlow();
|
|
5719
|
-
return {
|
|
5720
|
-
dispose: () => {
|
|
5721
|
-
if (disposed) return;
|
|
5722
|
-
disposed = true;
|
|
5723
|
-
if (joinCollectionTimer !== null) {
|
|
5724
|
-
clearTimeout(joinCollectionTimer);
|
|
5725
|
-
joinCollectionTimer = null;
|
|
5726
|
-
}
|
|
5727
|
-
if (snapshotTimeoutTimer !== null) {
|
|
5728
|
-
clearTimeout(snapshotTimeoutTimer);
|
|
5729
|
-
snapshotTimeoutTimer = null;
|
|
5730
|
-
}
|
|
5731
|
-
unsubscribeChange();
|
|
5732
|
-
try {
|
|
5733
|
-
channel.close();
|
|
5734
|
-
} catch {
|
|
5735
|
-
}
|
|
5736
|
-
},
|
|
5737
|
-
lifecycle: () => lifecycle,
|
|
5738
|
-
senderId,
|
|
5739
|
-
channelName
|
|
5740
|
-
};
|
|
5741
|
-
}
|
|
5742
|
-
const MULTI_TAB_SYNC_MODULE_KEY = "multiTabSync";
|
|
5743
|
-
|
|
5744
5213
|
const warned = /* @__PURE__ */ new Set();
|
|
5745
5214
|
function warnOnceInsecureContext(feature) {
|
|
5746
5215
|
if (!__DEV__) return;
|
|
@@ -5800,8 +5269,10 @@ function useAbstractForm(configuration, options) {
|
|
|
5800
5269
|
}
|
|
5801
5270
|
const existing = registry.forms.get(key);
|
|
5802
5271
|
if (__DEV__ && existing !== void 0) {
|
|
5803
|
-
|
|
5804
|
-
|
|
5272
|
+
void import('../chunks/dev-key-collision-warnings.mjs').then((m) => {
|
|
5273
|
+
void m.warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
|
|
5274
|
+
m.warnOnPersistDivergence(key, existing, configuration.persist);
|
|
5275
|
+
});
|
|
5805
5276
|
}
|
|
5806
5277
|
const hadPendingHydration = registry.pendingHydration.has(key);
|
|
5807
5278
|
const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
|
|
@@ -5842,10 +5313,33 @@ function useAbstractForm(configuration, options) {
|
|
|
5842
5313
|
} else {
|
|
5843
5314
|
const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
|
|
5844
5315
|
void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
|
|
5845
|
-
const
|
|
5846
|
-
|
|
5847
|
-
state.
|
|
5848
|
-
|
|
5316
|
+
const adapterPromise = getStorageAdapter(resolvedPersist.storage);
|
|
5317
|
+
let persistDisposed = false;
|
|
5318
|
+
state.registerCleanup(() => {
|
|
5319
|
+
persistDisposed = true;
|
|
5320
|
+
});
|
|
5321
|
+
const ready = (async () => {
|
|
5322
|
+
try {
|
|
5323
|
+
const [{ wirePersistence }, fingerprintToken] = await Promise.all([
|
|
5324
|
+
import('../chunks/wire-persistence.mjs'),
|
|
5325
|
+
resolvePersistFingerprintToken(state)
|
|
5326
|
+
]);
|
|
5327
|
+
if (persistDisposed) return void 0;
|
|
5328
|
+
const persistenceModule = wirePersistence(
|
|
5329
|
+
state,
|
|
5330
|
+
resolvedPersist,
|
|
5331
|
+
adapterPromise,
|
|
5332
|
+
fingerprintToken
|
|
5333
|
+
);
|
|
5334
|
+
state.registerDrain(() => persistenceModule.awaitPendingWrites());
|
|
5335
|
+
state.registerCleanup(() => persistenceModule.dispose());
|
|
5336
|
+
return persistenceModule;
|
|
5337
|
+
} catch {
|
|
5338
|
+
return void 0;
|
|
5339
|
+
}
|
|
5340
|
+
})();
|
|
5341
|
+
const persistenceHandle = { config: resolvedPersist, ready };
|
|
5342
|
+
state.modules.set(PERSISTENCE_MODULE_KEY, persistenceHandle);
|
|
5849
5343
|
}
|
|
5850
5344
|
} else {
|
|
5851
5345
|
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
@@ -5855,27 +5349,31 @@ function useAbstractForm(configuration, options) {
|
|
|
5855
5349
|
const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
|
|
5856
5350
|
const secureContext = isSecureContext();
|
|
5857
5351
|
if (hasBroadcastChannel && secureContext) {
|
|
5858
|
-
let
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
}
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5352
|
+
let formDisposed = false;
|
|
5353
|
+
state.registerCleanup(() => {
|
|
5354
|
+
formDisposed = true;
|
|
5355
|
+
});
|
|
5356
|
+
void (async () => {
|
|
5357
|
+
try {
|
|
5358
|
+
const [{ createMultiTabSyncModule, MULTI_TAB_SYNC_MODULE_KEY }, fingerprint] = await Promise.all([import('../chunks/multi-tab-sync.mjs'), state.schema.fingerprint()]);
|
|
5359
|
+
if (formDisposed) return;
|
|
5360
|
+
const channelName = `attaform:sync:${state.formKey}:${hashStableString(fingerprint)}`;
|
|
5361
|
+
const syncModule = createMultiTabSyncModule(state, channelName, {
|
|
5362
|
+
isSensitivePath: state.isSensitivePath,
|
|
5363
|
+
noSyncPaths: state.noSyncPaths,
|
|
5364
|
+
validateForm: (form) => {
|
|
5365
|
+
const result = state.schema.validateAtPath(form, void 0, { sync: true });
|
|
5366
|
+
if (result instanceof Promise) return;
|
|
5367
|
+
if (!result.success) {
|
|
5368
|
+
throw new Error("attaform multi-tab sync: post-apply schema validation failed");
|
|
5369
|
+
}
|
|
5873
5370
|
}
|
|
5874
|
-
}
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5371
|
+
});
|
|
5372
|
+
state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
|
|
5373
|
+
state.registerCleanup(() => syncModule.dispose());
|
|
5374
|
+
} catch {
|
|
5375
|
+
}
|
|
5376
|
+
})();
|
|
5879
5377
|
} else if (hasBroadcastChannel && !secureContext) {
|
|
5880
5378
|
warnOnceInsecureContext("multiTab");
|
|
5881
5379
|
}
|
|
@@ -6036,55 +5534,17 @@ function resolveFormKey(key) {
|
|
|
6036
5534
|
}
|
|
6037
5535
|
return `${ANONYMOUS_FORM_KEY_PREFIX}${anonCounter++}`;
|
|
6038
5536
|
}
|
|
6039
|
-
function
|
|
6040
|
-
let existingFp;
|
|
6041
|
-
let incomingFp;
|
|
5537
|
+
async function resolvePersistFingerprintToken(state) {
|
|
6042
5538
|
try {
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
return;
|
|
6051
|
-
}
|
|
6052
|
-
if (existingFp === incomingFp) return;
|
|
6053
|
-
console.warn(
|
|
6054
|
-
`[attaform] useForm() calls with key "${key}" use different schemas; first wins, second is ignored. Use identical schemas or unique keys.
|
|
6055
|
-
existing: ${existingFp}
|
|
6056
|
-
incoming: ${incomingFp}`
|
|
6057
|
-
);
|
|
6058
|
-
}
|
|
6059
|
-
function warnOnPersistDivergence(key, existing, incomingPersist) {
|
|
6060
|
-
if (incomingPersist === void 0) return;
|
|
6061
|
-
const wired = existing.modules.get(PERSISTENCE_MODULE_KEY);
|
|
6062
|
-
const incomingNormalized = normalizePersistConfig(incomingPersist);
|
|
6063
|
-
if (wired === void 0) {
|
|
6064
|
-
console.warn(
|
|
6065
|
-
`[attaform] useForm({ key: "${key}" }) passed a persist config but the first useForm({ key }) call didn't wire persistence; the new config is silently dropped. Pass persist on the first call, or remove persist here to make the inheritance explicit.`
|
|
6066
|
-
);
|
|
6067
|
-
return;
|
|
5539
|
+
return hashStableString(await state.schema.fingerprint());
|
|
5540
|
+
} catch (err) {
|
|
5541
|
+
if (__DEV__) {
|
|
5542
|
+
console.warn(
|
|
5543
|
+
`[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.`
|
|
5544
|
+
);
|
|
5545
|
+
}
|
|
5546
|
+
return "unfingerprinted";
|
|
6068
5547
|
}
|
|
6069
|
-
if (persistConfigsEquivalent(wired.wiredConfig, incomingNormalized)) return;
|
|
6070
|
-
console.warn(
|
|
6071
|
-
`[attaform] useForm({ key: "${key}" }) passed a persist config that differs from the first useForm({ key }) call's; first wins, this one is ignored.
|
|
6072
|
-
wired: ${describePersist(wired.wiredConfig)}
|
|
6073
|
-
incoming: ${describePersist(incomingNormalized)}`
|
|
6074
|
-
);
|
|
6075
|
-
}
|
|
6076
|
-
function persistConfigsEquivalent(a, b) {
|
|
6077
|
-
if (a.storage !== b.storage) return false;
|
|
6078
|
-
if ((a.key ?? void 0) !== (b.key ?? void 0)) return false;
|
|
6079
|
-
if ((a.debounceMs ?? void 0) !== (b.debounceMs ?? void 0)) return false;
|
|
6080
|
-
return true;
|
|
6081
|
-
}
|
|
6082
|
-
function describePersist(config) {
|
|
6083
|
-
const storage = typeof config.storage === "string" ? config.storage : "custom-adapter";
|
|
6084
|
-
const parts = [`storage=${storage}`];
|
|
6085
|
-
if (config.key !== void 0) parts.push(`key=${config.key}`);
|
|
6086
|
-
if (config.debounceMs !== void 0) parts.push(`debounceMs=${config.debounceMs}`);
|
|
6087
|
-
return `{ ${parts.join(", ")} }`;
|
|
6088
5548
|
}
|
|
6089
5549
|
const warnedAnonPersistKeys = /* @__PURE__ */ new Set();
|
|
6090
5550
|
function enforceAnonPersistRule(formKey, ssr) {
|
|
@@ -6252,7 +5712,7 @@ function buildNoopWizardSchema(formKey) {
|
|
|
6252
5712
|
formKey
|
|
6253
5713
|
};
|
|
6254
5714
|
return {
|
|
6255
|
-
fingerprint: () => NOOP_FINGERPRINT,
|
|
5715
|
+
fingerprint: () => Promise.resolve(NOOP_FINGERPRINT),
|
|
6256
5716
|
getDefaultValues: () => defaultsResponse,
|
|
6257
5717
|
getDefaultAtPath: () => void 0,
|
|
6258
5718
|
getEmptyValueAtPath: () => void 0,
|
|
@@ -6969,7 +6429,11 @@ function useWizard(options) {
|
|
|
6969
6429
|
}
|
|
6970
6430
|
for (const key of results.keys()) {
|
|
6971
6431
|
const store = registry.forms.get(key);
|
|
6972
|
-
if (store !== void 0)
|
|
6432
|
+
if (store !== void 0) {
|
|
6433
|
+
store.submissionAttempts.value += 1;
|
|
6434
|
+
store.cancelFieldValidation();
|
|
6435
|
+
store.displayEngine.clear();
|
|
6436
|
+
}
|
|
6973
6437
|
}
|
|
6974
6438
|
submissionAttempts.value += 1;
|
|
6975
6439
|
const errors = collectErrors(results);
|
|
@@ -7202,5 +6666,5 @@ function warnIfAmbientWizardProviderHadDuplicates() {
|
|
|
7202
6666
|
}
|
|
7203
6667
|
}
|
|
7204
6668
|
|
|
7205
|
-
export { AttaformErrorCode as A,
|
|
7206
|
-
//# sourceMappingURL=attaform.
|
|
6669
|
+
export { AttaformErrorCode as A, useAbstractForm as B, useWizard as C, DEFAULT_PERSISTENCE_DEBOUNCE_MS as D, PERSISTENCE_MODULE_KEY as P, DEFAULT_TIMINGS as a, applyPatchesForward as b, cleanupOrphanKeys as c, defaultCoercionRules as d, defaultDisplayState as e, defineCoercion as f, deleteAtPath as g, diffAndApply as h, getAtPath as i, humanize as j, injectForm as k, injectWizard as l, isPlainRecord as m, isUnset as n, lazy as o, makeDefaultDisplayState as p, mergeSparseHydration as q, normalizeNumericOption as r, normalizePersistConfig as s, resolveStorageKeyBase as t, safeAssign as u, safeOwnRead as v, setAtPath as w, slimKindOf as x, structuralSnapshot as y, unset as z };
|
|
6670
|
+
//# sourceMappingURL=attaform.DSqO6Db7.mjs.map
|