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