attaform 0.17.2 → 0.18.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/README.md +77 -36
- package/dist/chunks/devtools.cjs +10 -37
- package/dist/chunks/devtools.cjs.map +1 -1
- package/dist/chunks/devtools.mjs +10 -37
- package/dist/chunks/devtools.mjs.map +1 -1
- package/dist/chunks/indexeddb.cjs +4 -4
- package/dist/chunks/indexeddb.cjs.map +1 -1
- package/dist/chunks/indexeddb.mjs +1 -1
- package/dist/chunks/local-storage.cjs +2 -2
- package/dist/chunks/local-storage.cjs.map +1 -1
- package/dist/chunks/local-storage.mjs +1 -1
- package/dist/chunks/session-storage.cjs +2 -2
- package/dist/chunks/session-storage.cjs.map +1 -1
- package/dist/chunks/session-storage.mjs +1 -1
- package/dist/index.cjs +42 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +159 -196
- package/dist/index.d.mts +159 -196
- package/dist/index.d.ts +159 -196
- package/dist/index.mjs +5 -7
- package/dist/index.mjs.map +1 -1
- package/dist/nuxt.cjs +31 -40
- package/dist/nuxt.cjs.map +1 -1
- package/dist/nuxt.d.cts +8 -1
- package/dist/nuxt.d.mts +8 -1
- package/dist/nuxt.d.ts +8 -1
- package/dist/nuxt.mjs +32 -41
- package/dist/nuxt.mjs.map +1 -1
- package/dist/runtime/components/AttaformDevtoolsPanel.d.vue.ts +7 -0
- package/dist/runtime/components/AttaformDevtoolsPanel.vue +453 -0
- package/dist/runtime/components/AttaformDevtoolsPanel.vue.d.ts +7 -0
- package/dist/runtime/components/DevtoolsValueTree.d.vue.ts +37 -0
- package/dist/runtime/components/DevtoolsValueTree.vue +192 -0
- package/dist/runtime/components/DevtoolsValueTree.vue.d.ts +37 -0
- package/dist/runtime/plugins/attaform.cjs +17 -6
- package/dist/runtime/plugins/attaform.cjs.map +1 -1
- package/dist/runtime/plugins/attaform.mjs +15 -4
- package/dist/runtime/plugins/attaform.mjs.map +1 -1
- package/dist/shared/{attaform.C0iFnTN0.d.ts → attaform.2b7M2mww.d.mts} +57 -23
- package/dist/shared/attaform.5UhpSVFI.cjs +63 -0
- package/dist/shared/attaform.5UhpSVFI.cjs.map +1 -0
- package/dist/shared/attaform.BDdFdjeX.mjs +57 -0
- package/dist/shared/attaform.BDdFdjeX.mjs.map +1 -0
- package/dist/shared/{attaform.Dee2rU1P.cjs → attaform.BqK_L4gK.cjs} +310 -24
- package/dist/shared/attaform.BqK_L4gK.cjs.map +1 -0
- package/dist/shared/attaform.Bubm_slq.cjs.map +1 -1
- package/dist/shared/attaform.CXpzmj38.mjs.map +1 -1
- package/dist/shared/{attaform.Drt6fivF.mjs → attaform.CtNUB9nf.mjs} +74 -76
- package/dist/shared/attaform.CtNUB9nf.mjs.map +1 -0
- package/dist/shared/{attaform.C5MH4lNh.d.mts → attaform.DF8wo-ry.d.ts} +4 -4
- package/dist/shared/attaform.DK9aj0N8.d.ts +1651 -0
- package/dist/shared/{attaform.BPRHR3Zs.cjs → attaform.DUHru0OF.cjs} +83 -85
- package/dist/shared/attaform.DUHru0OF.cjs.map +1 -0
- package/dist/shared/{attaform.C6lbmMUe.d.ts → attaform.DVLB6CAn.d.mts} +4 -4
- package/dist/shared/{attaform.C_5aB6EQ.d.ts → attaform.Dj9mwbaV.d.cts} +756 -148
- package/dist/shared/{attaform.C_5aB6EQ.d.mts → attaform.Dj9mwbaV.d.mts} +756 -148
- package/dist/shared/{attaform.C_5aB6EQ.d.cts → attaform.Dj9mwbaV.d.ts} +756 -148
- package/dist/shared/{attaform.BV40t5y2.cjs → attaform.Dlk1jMuv.cjs} +245 -108
- package/dist/shared/attaform.Dlk1jMuv.cjs.map +1 -0
- package/dist/shared/attaform.DoSuaKMd.d.cts +1651 -0
- package/dist/shared/{attaform.B3ZaPIzS.mjs → attaform.DsC3rZHG.mjs} +1804 -219
- package/dist/shared/attaform.DsC3rZHG.mjs.map +1 -0
- package/dist/shared/{attaform.Cer8JO_P.cjs → attaform.II89Pcf4.cjs} +1860 -272
- package/dist/shared/attaform.II89Pcf4.cjs.map +1 -0
- package/dist/shared/{attaform.CHorcsIU.d.cts → attaform.M33WKVV4.d.cts} +57 -23
- package/dist/shared/{attaform.CIEQgJnM.mjs → attaform.Xhg0AYNa.mjs} +300 -26
- package/dist/shared/attaform.Xhg0AYNa.mjs.map +1 -0
- package/dist/shared/{attaform.CpERWz3u.mjs → attaform.Xt0A3QUd.mjs} +232 -95
- package/dist/shared/attaform.Xt0A3QUd.mjs.map +1 -0
- package/dist/shared/attaform.iTqxvl-P.d.mts +1651 -0
- package/dist/shared/{attaform.CuE-bS1C.d.mts → attaform.tsNFcEW7.d.ts} +57 -23
- package/dist/shared/{attaform.DtMN-MAm.d.cts → attaform.tts_OM7j.d.cts} +4 -4
- package/dist/vite.cjs +288 -2
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.mjs +288 -2
- package/dist/vite.mjs.map +1 -1
- package/dist/zod-v3.cjs +11 -8
- package/dist/zod-v3.cjs.map +1 -1
- package/dist/zod-v3.d.cts +6 -6
- package/dist/zod-v3.d.mts +6 -6
- package/dist/zod-v3.d.ts +6 -6
- package/dist/zod-v3.mjs +3 -3
- package/dist/zod-v4.cjs +11 -8
- package/dist/zod-v4.cjs.map +1 -1
- package/dist/zod-v4.d.cts +5 -5
- package/dist/zod-v4.d.mts +5 -5
- package/dist/zod-v4.d.ts +5 -5
- package/dist/zod-v4.mjs +3 -3
- package/dist/zod.cjs +15 -16
- package/dist/zod.cjs.map +1 -1
- package/dist/zod.d.cts +127 -40
- package/dist/zod.d.mts +127 -40
- package/dist/zod.d.ts +127 -40
- package/dist/zod.mjs +7 -11
- package/dist/zod.mjs.map +1 -1
- package/package.json +18 -7
- package/dist/shared/attaform.B1jvxsOF.d.mts +0 -156
- package/dist/shared/attaform.B3ZaPIzS.mjs.map +0 -1
- package/dist/shared/attaform.BBM2muQ9.cjs +0 -101
- package/dist/shared/attaform.BBM2muQ9.cjs.map +0 -1
- package/dist/shared/attaform.BPRHR3Zs.cjs.map +0 -1
- package/dist/shared/attaform.BV40t5y2.cjs.map +0 -1
- package/dist/shared/attaform.C6qzEdIM.d.cts +0 -156
- package/dist/shared/attaform.C8LVFVVe.cjs +0 -32
- package/dist/shared/attaform.C8LVFVVe.cjs.map +0 -1
- package/dist/shared/attaform.CIEQgJnM.mjs.map +0 -1
- package/dist/shared/attaform.CTwNcpLE.d.ts +0 -156
- package/dist/shared/attaform.Cer8JO_P.cjs.map +0 -1
- package/dist/shared/attaform.CpERWz3u.mjs.map +0 -1
- package/dist/shared/attaform.Dee2rU1P.cjs.map +0 -1
- package/dist/shared/attaform.Drt6fivF.mjs.map +0 -1
- package/dist/shared/attaform.Vo-Kft0t.mjs +0 -29
- package/dist/shared/attaform.Vo-Kft0t.mjs.map +0 -1
- package/dist/shared/attaform.h1sq3BFu.mjs +0 -92
- package/dist/shared/attaform.h1sq3BFu.mjs.map +0 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, reactive, watch, markRaw, toRaw, shallowRef, getCurrentInstance, provide, useId, inject } from 'vue';
|
|
2
|
-
import { _ as __DEV__,
|
|
3
|
-
import { c as canonicalizePath, s as segmentsForPathKey, i as isPathPrefix, F as FORM_ERRORS_PATH_KEY, R as ROOT_PATH, b as FORM_ERRORS_PATH } from './attaform.h1sq3BFu.mjs';
|
|
1
|
+
import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, reactive, watch, markRaw, toRaw, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, effectScope, nextTick } from 'vue';
|
|
2
|
+
import { a as canonicalizePath, s as segmentsForPathKey, m as isPathPrefix, F as FORM_ERRORS_PATH_KEY, _ as __DEV__, r as pathKeyToDotted, i as SubmitErrorHandlerError, A as AnonPersistError, t as captureUserCallSite, R as ROOT_PATH, w as enforceSensitiveCheck, x as FORM_ERRORS_PATH, y as coerceToPathKey, z as segmentMatchesSensitive, B as isSensitivePath, C as createPersistOptInRegistry, d as InvalidUseFormConfigError, E as ensureAttaformInstalled, q as useRegistry, G as kFormContext, H as kFormInstanceId, h as ReservedFormKeyError, J as createIsSensitivePath, K as createSegmentMatchesSensitive, k as kAttaformWizardActiveStepResolver, L as kAttaformAncestorWizard } from './attaform.Xhg0AYNa.mjs';
|
|
4
3
|
|
|
5
4
|
const NOT_FOUND = Symbol("NOT_FOUND");
|
|
6
5
|
function descendStep(value, segment) {
|
|
@@ -121,7 +120,12 @@ function mergeStructural(schema, path, consumer, defaultValue = schema.getDefaul
|
|
|
121
120
|
return mergeStructuralImpl(schema, scratch, consumer, defaultValue);
|
|
122
121
|
}
|
|
123
122
|
function mergeStructuralImpl(schema, scratch, consumer, defaultValue) {
|
|
124
|
-
if (consumer === void 0)
|
|
123
|
+
if (consumer === void 0) {
|
|
124
|
+
if (schema.getSlimPrimitiveTypesAtPath?.(scratch).has("undefined") === true) {
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
return defaultValue;
|
|
128
|
+
}
|
|
125
129
|
if (consumer === null) return null;
|
|
126
130
|
if (Array.isArray(consumer)) {
|
|
127
131
|
const shape = resolveArrayShape(schema, scratch);
|
|
@@ -161,7 +165,7 @@ function mergeStructuralImpl(schema, scratch, consumer, defaultValue) {
|
|
|
161
165
|
let mutated = false;
|
|
162
166
|
const out = { ...consumer };
|
|
163
167
|
for (const key of Object.keys(defaultValue)) {
|
|
164
|
-
if (!(key in consumer)
|
|
168
|
+
if (!(key in consumer)) {
|
|
165
169
|
const defAtKey = defaultValue[key];
|
|
166
170
|
scratch.push(key);
|
|
167
171
|
const filled = mergeStructuralImpl(schema, scratch, void 0, defAtKey);
|
|
@@ -173,10 +177,12 @@ function mergeStructuralImpl(schema, scratch, consumer, defaultValue) {
|
|
|
173
177
|
}
|
|
174
178
|
}
|
|
175
179
|
for (const key of Object.keys(consumer)) {
|
|
180
|
+
const cVal = consumer[key];
|
|
181
|
+
if (cVal === void 0) continue;
|
|
176
182
|
scratch.push(key);
|
|
177
|
-
const merged = mergeStructuralImpl(schema, scratch,
|
|
183
|
+
const merged = mergeStructuralImpl(schema, scratch, cVal, defaultValue[key]);
|
|
178
184
|
scratch.pop();
|
|
179
|
-
if (merged !==
|
|
185
|
+
if (merged !== cVal) {
|
|
180
186
|
out[key] = merged;
|
|
181
187
|
mutated = true;
|
|
182
188
|
}
|
|
@@ -504,7 +510,7 @@ function buildLeafFieldStateBase(state, segments, key) {
|
|
|
504
510
|
dirty: !pristine,
|
|
505
511
|
focused: record?.focused ?? null,
|
|
506
512
|
blurred: record?.blurred ?? null,
|
|
507
|
-
touched: record?.touched ??
|
|
513
|
+
touched: record?.touched ?? false,
|
|
508
514
|
connected: record?.connected ?? false,
|
|
509
515
|
element: firstElement,
|
|
510
516
|
elements: elementsArr,
|
|
@@ -646,10 +652,12 @@ function buildSurfaceProxy(opts) {
|
|
|
646
652
|
if (opts.leafKeys !== void 0) return leafViewProxyAt(segs);
|
|
647
653
|
return opts.resolveLeaf(segs);
|
|
648
654
|
}
|
|
655
|
+
if (opts.isTerminalAt?.(segs) === true) return opts.resolveLeaf(segs);
|
|
649
656
|
return containerProxyAt(segs);
|
|
650
657
|
}
|
|
651
658
|
function containerProxyAt(segments) {
|
|
652
|
-
const
|
|
659
|
+
const isArrayLike = opts.isArrayContainer?.(segments) === true;
|
|
660
|
+
const cacheKey = `${JSON.stringify(segments)}+${isArrayLike ? "A" : "O"}`;
|
|
653
661
|
const existing = containerCache.get(cacheKey);
|
|
654
662
|
if (existing !== void 0) return existing;
|
|
655
663
|
const snapshotContainer = () => opts.materializeContainer === void 0 ? {} : opts.materializeContainer(segments);
|
|
@@ -659,7 +667,7 @@ function buildSurfaceProxy(opts) {
|
|
|
659
667
|
return this;
|
|
660
668
|
}
|
|
661
669
|
const containerToPrimitive = (hint) => hint === "number" ? NaN : containerToString();
|
|
662
|
-
const target = (() => {
|
|
670
|
+
const target = isArrayLike ? [] : (() => {
|
|
663
671
|
});
|
|
664
672
|
const proxy = new Proxy(target, {
|
|
665
673
|
apply(_, __, args) {
|
|
@@ -674,7 +682,14 @@ function buildSurfaceProxy(opts) {
|
|
|
674
682
|
return Reflect.get(target, key);
|
|
675
683
|
}
|
|
676
684
|
if (typeof key !== "string") return void 0;
|
|
685
|
+
if (key === "__v_skip") return true;
|
|
686
|
+
if (key === "__v_isReactive" || key === "__v_isReadonly" || key === "__v_isShallow" || key === "__v_isRef" || key === "__v_raw") {
|
|
687
|
+
return void 0;
|
|
688
|
+
}
|
|
677
689
|
if (key === "toJSON") return containerToJSON;
|
|
690
|
+
if (key === "length" && (isArrayLike || opts.isArrayContainer?.(segments) === true)) {
|
|
691
|
+
return opts.containerOwnKeys === void 0 ? 0 : opts.containerOwnKeys(segments).length;
|
|
692
|
+
}
|
|
678
693
|
const childSegs = [...segments, keyToSegment(key)];
|
|
679
694
|
if (key === "toString" || key === "valueOf") {
|
|
680
695
|
if (!schemaHasPath(childSegs)) {
|
|
@@ -687,11 +702,38 @@ function buildSurfaceProxy(opts) {
|
|
|
687
702
|
if (typeof key === "symbol") return Reflect.has(target, key);
|
|
688
703
|
return true;
|
|
689
704
|
},
|
|
690
|
-
//
|
|
691
|
-
//
|
|
692
|
-
//
|
|
693
|
-
|
|
694
|
-
|
|
705
|
+
// Live enumeration. When the surface provides `containerOwnKeys`,
|
|
706
|
+
// `Object.keys(proxy)` / `Object.entries(proxy)` /
|
|
707
|
+
// `v-for="(child, key) in proxy"` reflect whichever keys the
|
|
708
|
+
// underlying form data currently holds at this path: array
|
|
709
|
+
// indices (`'0'`, `'1'`, …) when the live value is an array,
|
|
710
|
+
// object keys when it's a record. `getOwnPropertyDescriptor`
|
|
711
|
+
// descends to the per-key sub-proxy so iteration yields the
|
|
712
|
+
// same objects dot-access would. Without `containerOwnKeys`,
|
|
713
|
+
// the container stays non-enumerable for callers that don't
|
|
714
|
+
// need iteration — `JSON.stringify` still serialises through
|
|
715
|
+
// the `toJSON` trap above either way.
|
|
716
|
+
ownKeys: () => {
|
|
717
|
+
const liveKeys = opts.containerOwnKeys === void 0 ? [] : opts.containerOwnKeys(segments);
|
|
718
|
+
if (isArrayLike) return ["length", ...liveKeys];
|
|
719
|
+
return [...liveKeys];
|
|
720
|
+
},
|
|
721
|
+
getOwnPropertyDescriptor(_, key) {
|
|
722
|
+
if (typeof key !== "string") return void 0;
|
|
723
|
+
if (isArrayLike && key === "length") {
|
|
724
|
+
const length = opts.containerOwnKeys === void 0 ? 0 : opts.containerOwnKeys(segments).length;
|
|
725
|
+
return { configurable: false, enumerable: false, value: length, writable: true };
|
|
726
|
+
}
|
|
727
|
+
if (opts.containerOwnKeys === void 0) return void 0;
|
|
728
|
+
const liveKeys = opts.containerOwnKeys(segments);
|
|
729
|
+
if (!liveKeys.includes(key)) return void 0;
|
|
730
|
+
return {
|
|
731
|
+
configurable: true,
|
|
732
|
+
enumerable: true,
|
|
733
|
+
value: descendOrTerminate([...segments, keyToSegment(key)]),
|
|
734
|
+
writable: false
|
|
735
|
+
};
|
|
736
|
+
},
|
|
695
737
|
// Block writes at the proxy boundary. Mutations go through
|
|
696
738
|
// `setValue`, the directive, or the field-array helpers.
|
|
697
739
|
set: () => false,
|
|
@@ -782,22 +824,47 @@ function buildErrorsProxy(state) {
|
|
|
782
824
|
return buildSurfaceProxy({
|
|
783
825
|
schema: state.schema,
|
|
784
826
|
resolveLeaf: (path) => {
|
|
785
|
-
const
|
|
786
|
-
const
|
|
787
|
-
|
|
827
|
+
const isContainerSelfAccess = path.length > 1 && path[path.length - 1] === "";
|
|
828
|
+
const collectAtKey = (key2, active2, into) => {
|
|
829
|
+
if (active2) {
|
|
830
|
+
const s = state.schemaErrors.get(key2);
|
|
831
|
+
const b = state.derivedBlankErrors.value.get(key2);
|
|
832
|
+
if (s !== void 0) into.push(...s);
|
|
833
|
+
if (b !== void 0) into.push(...b);
|
|
834
|
+
}
|
|
835
|
+
const u = state.userErrors.get(key2);
|
|
836
|
+
if (u !== void 0) into.push(...u);
|
|
837
|
+
};
|
|
788
838
|
const merged = [];
|
|
789
|
-
if (
|
|
790
|
-
const
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
|
|
839
|
+
if (isContainerSelfAccess) {
|
|
840
|
+
const containerPath = path.slice(0, -1);
|
|
841
|
+
const containerKey = canonicalizePath(containerPath).key;
|
|
842
|
+
const literalKey = canonicalizePath(path).key;
|
|
843
|
+
const active2 = hasAtPath(state.form.value, containerPath);
|
|
844
|
+
collectAtKey(containerKey, active2, merged);
|
|
845
|
+
if (literalKey !== containerKey) collectAtKey(literalKey, active2, merged);
|
|
846
|
+
return merged;
|
|
794
847
|
}
|
|
795
|
-
|
|
796
|
-
|
|
848
|
+
const { key } = canonicalizePath(path);
|
|
849
|
+
const isFormLevel = key === FORM_ERRORS_PATH_KEY;
|
|
850
|
+
const active = isFormLevel || hasAtPath(state.form.value, path);
|
|
851
|
+
collectAtKey(key, active, merged);
|
|
852
|
+
return merged;
|
|
797
853
|
},
|
|
798
854
|
// No leafKeys — at a leaf, the resolved value (the merged array or
|
|
799
855
|
// undefined) IS the terminal.
|
|
800
856
|
materializeContainer: (segments) => materializeErrors(state, segments),
|
|
857
|
+
// Any path ending in `''` is a meaningful terminal at the proxy
|
|
858
|
+
// layer: at root it's the form-level bucket; at depth >= 1 it's
|
|
859
|
+
// the container-self sentinel that surfaces cross-field refines
|
|
860
|
+
// and container-targeted marks. `resolveLeaf` translates `[...,
|
|
861
|
+
// '']` lookups to the parent container path before querying the
|
|
862
|
+
// stores. When a schema legitimately owns a `''` field, the
|
|
863
|
+
// literal leaf and any container-self errors share the slot
|
|
864
|
+
// (errors concatenate) — vanishingly rare, accepted as the
|
|
865
|
+
// ergonomic cost of one unified sentinel convention at every
|
|
866
|
+
// depth.
|
|
867
|
+
isTerminalAt: (segs) => segs.length >= 1 && segs[segs.length - 1] === "",
|
|
801
868
|
// Call-form aggregates: `form.errors(path)` returns a single
|
|
802
869
|
// `ValidationError[]` for any depth (leaf or container) — same
|
|
803
870
|
// shared `aggregateErrorsAt` helper that `form.meta.errors` and
|
|
@@ -807,12 +874,31 @@ function buildErrorsProxy(state) {
|
|
|
807
874
|
// when valid) so consumer code that branches on truthiness keeps
|
|
808
875
|
// working — the call-form just extends that semantic to
|
|
809
876
|
// containers and dynamic paths.
|
|
810
|
-
resolveCallTarget: (path) =>
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
877
|
+
resolveCallTarget: (path) => aggregateErrorsAt(state, path),
|
|
878
|
+
// Mirror `form.fields` enumeration: `Object.keys(form.errors.items)`
|
|
879
|
+
// and `v-for="(errs, idx) in form.errors.items"` walk the live
|
|
880
|
+
// array indices / object keys at the path. Iteration yields the
|
|
881
|
+
// descended sub-proxies (one per live key), so consumers can
|
|
882
|
+
// `form.errors.items[idx]` straight from the entry.
|
|
883
|
+
containerOwnKeys: (segments) => liveKeysAtPath$1(state, segments),
|
|
884
|
+
isArrayContainer: (segments) => isArrayPath$1(state, segments)
|
|
814
885
|
});
|
|
815
886
|
}
|
|
887
|
+
function liveKeysAtPath$1(state, segments) {
|
|
888
|
+
const value = getAtPath(state.form.value, segments);
|
|
889
|
+
if (value === null || value === void 0) return [];
|
|
890
|
+
if (Array.isArray(value)) {
|
|
891
|
+
const keys = new Array(value.length);
|
|
892
|
+
for (let i = 0; i < value.length; i += 1) keys[i] = String(i);
|
|
893
|
+
return keys;
|
|
894
|
+
}
|
|
895
|
+
if (typeof value === "object") return Object.keys(value);
|
|
896
|
+
return [];
|
|
897
|
+
}
|
|
898
|
+
function isArrayPath$1(state, segments) {
|
|
899
|
+
if (segments.length === 0) return false;
|
|
900
|
+
return Array.isArray(getAtPath(state.form.value, segments));
|
|
901
|
+
}
|
|
816
902
|
function materializeErrors(state, containerSegments) {
|
|
817
903
|
const liveContainer = getAtPath(state.form.value, containerSegments);
|
|
818
904
|
const tree = Array.isArray(liveContainer) ? [] : {};
|
|
@@ -821,12 +907,33 @@ function materializeErrors(state, containerSegments) {
|
|
|
821
907
|
if (errors.length === 0) continue;
|
|
822
908
|
const fullPath = segmentsForPathKey(pathKey);
|
|
823
909
|
if (fullPath === null) continue;
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
if (fullPath
|
|
910
|
+
const isSyntheticFormLevel = fullPath.length === 1 && fullPath[0] === "";
|
|
911
|
+
if (!isSyntheticFormLevel) {
|
|
912
|
+
if (fullPath.length < containerSegments.length) continue;
|
|
913
|
+
for (let i = 0; i < containerSegments.length; i++) {
|
|
914
|
+
if (fullPath[i] !== containerSegments[i]) continue entries;
|
|
915
|
+
}
|
|
916
|
+
} else if (containerSegments.length !== 0) {
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
if (applyActivePathFilter && !isSyntheticFormLevel && !hasAtPath(state.form.value, fullPath))
|
|
920
|
+
continue;
|
|
921
|
+
let placePath;
|
|
922
|
+
if (isSyntheticFormLevel) {
|
|
923
|
+
placePath = [""];
|
|
924
|
+
} else {
|
|
925
|
+
const relativePath = fullPath.slice(containerSegments.length);
|
|
926
|
+
if (relativePath.length === 0) {
|
|
927
|
+
placePath = [""];
|
|
928
|
+
} else if (state.schema.isLeafAtPath(fullPath)) {
|
|
929
|
+
placePath = relativePath;
|
|
930
|
+
} else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
|
|
931
|
+
placePath = [...relativePath, ""];
|
|
932
|
+
} else {
|
|
933
|
+
placePath = relativePath;
|
|
934
|
+
}
|
|
827
935
|
}
|
|
828
|
-
|
|
829
|
-
placeAt(tree, fullPath.slice(containerSegments.length), errors);
|
|
936
|
+
placeAt(tree, placePath, errors);
|
|
830
937
|
}
|
|
831
938
|
};
|
|
832
939
|
collect(state.schemaErrors, true);
|
|
@@ -1014,9 +1121,26 @@ function buildFieldStateProxy(state, getFormMetaBase, options) {
|
|
|
1014
1121
|
leafKeys: FIELD_STATE_KEYS,
|
|
1015
1122
|
readLeafKey: (computed, key) => computed.value[key],
|
|
1016
1123
|
materializeContainer: (segments) => materializeFields(state, segments, snapshotFieldStateAt),
|
|
1017
|
-
resolveCallTarget: (path) => fieldStateTerminalAt(path)
|
|
1124
|
+
resolveCallTarget: (path) => fieldStateTerminalAt(path),
|
|
1125
|
+
containerOwnKeys: (segments) => liveKeysAtPath(state, segments),
|
|
1126
|
+
isArrayContainer: (segments) => isArrayPath(state, segments)
|
|
1018
1127
|
});
|
|
1019
1128
|
}
|
|
1129
|
+
function liveKeysAtPath(state, segments) {
|
|
1130
|
+
const value = getAtPath(state.form.value, segments);
|
|
1131
|
+
if (value === null || value === void 0) return [];
|
|
1132
|
+
if (Array.isArray(value)) {
|
|
1133
|
+
const keys = new Array(value.length);
|
|
1134
|
+
for (let i = 0; i < value.length; i += 1) keys[i] = String(i);
|
|
1135
|
+
return keys;
|
|
1136
|
+
}
|
|
1137
|
+
if (typeof value === "object") return Object.keys(value);
|
|
1138
|
+
return [];
|
|
1139
|
+
}
|
|
1140
|
+
function isArrayPath(state, segments) {
|
|
1141
|
+
if (segments.length === 0) return false;
|
|
1142
|
+
return Array.isArray(getAtPath(state.form.value, segments));
|
|
1143
|
+
}
|
|
1020
1144
|
function materializeFields(state, containerSegments, snapshotFieldStateAt) {
|
|
1021
1145
|
const liveValue = getAtPath(state.form.value, containerSegments);
|
|
1022
1146
|
return walk$2(liveValue, containerSegments, state.schema, snapshotFieldStateAt);
|
|
@@ -1048,6 +1172,7 @@ const DEFAULT_HISTORY_MAX_SNAPSHOTS = 128;
|
|
|
1048
1172
|
const PERSISTENCE_KEY_PREFIX = "attaform:";
|
|
1049
1173
|
const RESERVED_KEY_PREFIX = "__atta:";
|
|
1050
1174
|
const ANONYMOUS_FORM_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon:`;
|
|
1175
|
+
const ANONYMOUS_WIZARD_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon-wizard:`;
|
|
1051
1176
|
const DEFAULT_MAX_RECURSION_DEPTH = 64;
|
|
1052
1177
|
function normalizeNumericOption(config) {
|
|
1053
1178
|
const { value, source, allowInfinity, min, defaultValue } = config;
|
|
@@ -1082,7 +1207,7 @@ async function getStorageAdapter(storage) {
|
|
|
1082
1207
|
}
|
|
1083
1208
|
}
|
|
1084
1209
|
}
|
|
1085
|
-
const PERSISTED_ENVELOPE_VERSION =
|
|
1210
|
+
const PERSISTED_ENVELOPE_VERSION = 5;
|
|
1086
1211
|
function readPersistedPayload(value) {
|
|
1087
1212
|
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
1088
1213
|
const envelope = value;
|
|
@@ -1104,7 +1229,15 @@ function warnVersionMismatch(observedVersion) {
|
|
|
1104
1229
|
);
|
|
1105
1230
|
}
|
|
1106
1231
|
function buildPersistedPayload(form, include, schemaErrors, userErrors, blankPaths) {
|
|
1107
|
-
|
|
1232
|
+
let transientList;
|
|
1233
|
+
if (blankPaths !== void 0 && blankPaths.size > 0) {
|
|
1234
|
+
const dotted = [];
|
|
1235
|
+
for (const key of blankPaths) {
|
|
1236
|
+
const d = pathKeyToDotted(key);
|
|
1237
|
+
if (d !== null) dotted.push(d);
|
|
1238
|
+
}
|
|
1239
|
+
transientList = dotted.length > 0 ? dotted : void 0;
|
|
1240
|
+
}
|
|
1108
1241
|
if (include === "form") {
|
|
1109
1242
|
if (transientList === void 0) return { v: PERSISTED_ENVELOPE_VERSION, data: { form } };
|
|
1110
1243
|
return {
|
|
@@ -1272,13 +1405,40 @@ const AttaformErrorCode = {
|
|
|
1272
1405
|
NoValueSupplied: "atta:no-value-supplied",
|
|
1273
1406
|
/** The schema adapter's `validateAtPath` threw synchronously. */
|
|
1274
1407
|
AdapterThrew: "atta:adapter-threw",
|
|
1408
|
+
/**
|
|
1409
|
+
* User code inside a `z.preprocess`, `.refine`, or `.transform`
|
|
1410
|
+
* threw (sync or async). The adapter caught the throw and surfaced
|
|
1411
|
+
* it as a `ValidationError` at the field path so the form's normal
|
|
1412
|
+
* error pipeline handles it instead of leaking as an unhandled
|
|
1413
|
+
* rejection or routing through `submitError`.
|
|
1414
|
+
*/
|
|
1415
|
+
ValidatorThrew: "atta:validator-threw",
|
|
1416
|
+
/**
|
|
1417
|
+
* A function-form `defaultValues` factory threw or its promise
|
|
1418
|
+
* rejected. The runtime captures the raw error on `form.hydrateError`
|
|
1419
|
+
* and ALSO surfaces a form-level `ValidationError` (path `[]`) so
|
|
1420
|
+
* the standard error pipeline carries the signal. Critical for the
|
|
1421
|
+
* SSR round-trip: `hydrateError` itself does not ride the wire
|
|
1422
|
+
* payload, but `schemaErrors` does, so the client sees the failure
|
|
1423
|
+
* after rehydration without an extra channel.
|
|
1424
|
+
*/
|
|
1425
|
+
HydrationFailed: "atta:hydration-failed",
|
|
1275
1426
|
/** The supplied path didn't resolve to any node in the schema. */
|
|
1276
|
-
PathNotFound: "atta:path-not-found"
|
|
1427
|
+
PathNotFound: "atta:path-not-found",
|
|
1428
|
+
/**
|
|
1429
|
+
* A walked form's `activate()` (async `defaultValues` factory) threw
|
|
1430
|
+
* during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
|
|
1431
|
+
* `ValidationError` at the form-level path (`[]`) so the wizard's
|
|
1432
|
+
* aggregate error pipeline can carry the failure alongside ordinary
|
|
1433
|
+
* validation errors. The raw factory error remains on
|
|
1434
|
+
* `form.hydrateError` for retry UX.
|
|
1435
|
+
*/
|
|
1436
|
+
ActivationFailed: "atta:activation-failed"
|
|
1277
1437
|
};
|
|
1278
1438
|
|
|
1279
1439
|
const warnedNoScopeStores = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
|
|
1280
1440
|
function buildProcessForm(state, formInstanceId, options = {}) {
|
|
1281
|
-
const invalidPolicy = options.onInvalidSubmit ?? "
|
|
1441
|
+
const invalidPolicy = options.onInvalidSubmit ?? "focus-first-error";
|
|
1282
1442
|
function validate(pathInput) {
|
|
1283
1443
|
const result = ref({
|
|
1284
1444
|
pending: true,
|
|
@@ -1341,7 +1501,15 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1341
1501
|
const dataAtPath = segments === void 0 ? state.form.value : state.getValueAtPath(segments);
|
|
1342
1502
|
try {
|
|
1343
1503
|
state.activeValidations.value += 1;
|
|
1504
|
+
state.cancelFieldValidation();
|
|
1344
1505
|
const refinement = await runRefinementValidation(dataAtPath, segments);
|
|
1506
|
+
const scopePath = segments ?? [];
|
|
1507
|
+
const errors = refinement.success ? [] : refinement.errors;
|
|
1508
|
+
const reStamped = segments === void 0 ? errors : errors.map((err) => ({
|
|
1509
|
+
...err,
|
|
1510
|
+
path: [...segments, ...err.path]
|
|
1511
|
+
}));
|
|
1512
|
+
state.applySchemaErrorsForSubtree(scopePath, reStamped);
|
|
1345
1513
|
return stripData(composeWithDerivedBlank(refinement, segments));
|
|
1346
1514
|
} catch (err) {
|
|
1347
1515
|
return adapterThrowResponse(err);
|
|
@@ -1438,6 +1606,9 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1438
1606
|
state.clearSchemaErrors();
|
|
1439
1607
|
}
|
|
1440
1608
|
await onSubmit(merged.data);
|
|
1609
|
+
if (state.submissionGeneration.value === genAtEntry) {
|
|
1610
|
+
state.submitted.value = true;
|
|
1611
|
+
}
|
|
1441
1612
|
state.emitSubmitSuccess();
|
|
1442
1613
|
} catch (err) {
|
|
1443
1614
|
if (state.submissionGeneration.value === genAtEntry) {
|
|
@@ -1451,7 +1622,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1451
1622
|
state.activeSubmissions.value = Math.max(0, state.activeSubmissions.value - 1);
|
|
1452
1623
|
if (state.submissionGeneration.value === genAtEntry) {
|
|
1453
1624
|
state.submitting.value = state.activeSubmissions.value > 0;
|
|
1454
|
-
state.
|
|
1625
|
+
state.submissionAttempts.value += 1;
|
|
1455
1626
|
}
|
|
1456
1627
|
}
|
|
1457
1628
|
};
|
|
@@ -1545,6 +1716,7 @@ function slimKindOf(value) {
|
|
|
1545
1716
|
if (value instanceof Date) return "date";
|
|
1546
1717
|
if (value instanceof Map) return "map";
|
|
1547
1718
|
if (value instanceof Set) return "set";
|
|
1719
|
+
if (typeof File !== "undefined" && value instanceof File) return "file";
|
|
1548
1720
|
const t = typeof value;
|
|
1549
1721
|
switch (t) {
|
|
1550
1722
|
case "string":
|
|
@@ -1575,6 +1747,7 @@ function isSlimPrimitiveValid(schema, store, path, value) {
|
|
|
1575
1747
|
return walk$1(schema, store, path, value);
|
|
1576
1748
|
}
|
|
1577
1749
|
function walk$1(schema, store, path, value) {
|
|
1750
|
+
if (schema.isPreprocessOrCoerceLeaf(path)) return true;
|
|
1578
1751
|
const accepted = schema.getSlimPrimitiveTypesAtPath(path);
|
|
1579
1752
|
const kind = isLeafValue(value) ? slimKindOf(value) : Array.isArray(value) ? "array" : "object";
|
|
1580
1753
|
if (!accepted.has(kind)) {
|
|
@@ -1804,6 +1977,11 @@ function attachFocusListeners(state, segments, element, instanceMeta) {
|
|
|
1804
1977
|
element.addEventListener("focus", handleFocus);
|
|
1805
1978
|
element.addEventListener("blur", handleBlur);
|
|
1806
1979
|
target[attaformListenersSymbol] = { handleFocus, handleBlur };
|
|
1980
|
+
const rootNode = element.getRootNode();
|
|
1981
|
+
const activeElement = rootNode instanceof Document || rootNode instanceof ShadowRoot ? rootNode.activeElement : null;
|
|
1982
|
+
if (activeElement === element) {
|
|
1983
|
+
state.markFocused(segments, true, focusMeta);
|
|
1984
|
+
}
|
|
1807
1985
|
}
|
|
1808
1986
|
function detachFocusListeners(element) {
|
|
1809
1987
|
const target = element;
|
|
@@ -1840,6 +2018,9 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
1840
2018
|
return String(raw);
|
|
1841
2019
|
});
|
|
1842
2020
|
const slimDefault = state.schema.getDefaultAtPath(segments);
|
|
2021
|
+
const slimTypes = state.schema.getSlimPrimitiveTypesAtPath(segments);
|
|
2022
|
+
const acceptsUndefined = slimTypes.has("undefined");
|
|
2023
|
+
const acceptsString = slimTypes.has("string");
|
|
1843
2024
|
const persist = options?.persist === true;
|
|
1844
2025
|
const acknowledgeSensitive = options?.acknowledgeSensitive === true;
|
|
1845
2026
|
const multiTab = options?.multiTab !== false;
|
|
@@ -1923,7 +2104,9 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
1923
2104
|
...unmarkNoSync !== void 0 ? { unmarkNoSync } : {},
|
|
1924
2105
|
transforms,
|
|
1925
2106
|
coerce,
|
|
1926
|
-
...coerceElement !== void 0 ? { coerceElement } : {}
|
|
2107
|
+
...coerceElement !== void 0 ? { coerceElement } : {},
|
|
2108
|
+
acceptsUndefined,
|
|
2109
|
+
acceptsString
|
|
1927
2110
|
};
|
|
1928
2111
|
return shallowReadonly(internalRv);
|
|
1929
2112
|
};
|
|
@@ -1946,13 +2129,7 @@ function walkUnsetSentinels(values, schema) {
|
|
|
1946
2129
|
}
|
|
1947
2130
|
function walk(input, segments, schema, paths) {
|
|
1948
2131
|
if (isUnset(input)) {
|
|
1949
|
-
|
|
1950
|
-
if (!isPrimitiveOrEmpty(slim)) {
|
|
1951
|
-
warnNonPrimitiveLeaf(segments, slim);
|
|
1952
|
-
return walkUnspecified(slim, segments, paths);
|
|
1953
|
-
}
|
|
1954
|
-
paths.push(canonicalizePath(segments).key);
|
|
1955
|
-
return slim;
|
|
2132
|
+
return expandUnsetAt(segments, schema, paths);
|
|
1956
2133
|
}
|
|
1957
2134
|
if (input === void 0) {
|
|
1958
2135
|
const slim = schema.getDefaultAtPath(segments);
|
|
@@ -1975,6 +2152,7 @@ function walk(input, segments, schema, paths) {
|
|
|
1975
2152
|
if (typeof input === "object") {
|
|
1976
2153
|
const slim = schema.getDefaultAtPath(segments);
|
|
1977
2154
|
const inputKeys = Object.keys(input);
|
|
2155
|
+
const inputKeysSet = new Set(inputKeys);
|
|
1978
2156
|
const allKeys = new Set(inputKeys);
|
|
1979
2157
|
if (slim !== null && slim !== void 0 && typeof slim === "object" && !Array.isArray(slim) && !(slim instanceof Date) && !(slim instanceof RegExp) && !(slim instanceof Map) && !(slim instanceof Set)) {
|
|
1980
2158
|
for (const k of Object.keys(slim)) allKeys.add(k);
|
|
@@ -1983,6 +2161,11 @@ function walk(input, segments, schema, paths) {
|
|
|
1983
2161
|
let mutated = allKeys.size !== inputKeys.length;
|
|
1984
2162
|
for (const key of allKeys) {
|
|
1985
2163
|
const orig = input[key];
|
|
2164
|
+
if (orig === void 0 && inputKeysSet.has(key)) {
|
|
2165
|
+
out[key] = void 0;
|
|
2166
|
+
mutated = true;
|
|
2167
|
+
continue;
|
|
2168
|
+
}
|
|
1986
2169
|
const walked = walk(orig, [...segments, key], schema, paths);
|
|
1987
2170
|
out[key] = walked;
|
|
1988
2171
|
if (walked !== orig) mutated = true;
|
|
@@ -1993,7 +2176,7 @@ function walk(input, segments, schema, paths) {
|
|
|
1993
2176
|
}
|
|
1994
2177
|
function walkUnspecified(slim, segments, paths) {
|
|
1995
2178
|
if (isPrimitiveOrEmpty(slim)) {
|
|
1996
|
-
if (
|
|
2179
|
+
if (isSlimNumericPrimitive(slim)) {
|
|
1997
2180
|
paths.push(canonicalizePath(segments).key);
|
|
1998
2181
|
}
|
|
1999
2182
|
return slim;
|
|
@@ -2018,13 +2201,7 @@ function substituteUnsetSentinels(value, prefix, schema) {
|
|
|
2018
2201
|
}
|
|
2019
2202
|
function substitute(input, segments, schema, paths) {
|
|
2020
2203
|
if (isUnset(input)) {
|
|
2021
|
-
|
|
2022
|
-
if (!isPrimitiveOrEmpty(slim)) {
|
|
2023
|
-
warnNonPrimitiveLeaf(segments, slim);
|
|
2024
|
-
return slim;
|
|
2025
|
-
}
|
|
2026
|
-
paths.push(canonicalizePath(segments).key);
|
|
2027
|
-
return slim;
|
|
2204
|
+
return expandUnsetAt(segments, schema, paths);
|
|
2028
2205
|
}
|
|
2029
2206
|
if (input === void 0 || input === null) return input;
|
|
2030
2207
|
if (input instanceof Date || input instanceof RegExp || input instanceof Map || input instanceof Set || typeof input === "function") {
|
|
@@ -2058,20 +2235,40 @@ function isPrimitiveOrEmpty(value) {
|
|
|
2058
2235
|
const t = typeof value;
|
|
2059
2236
|
return t === "string" || t === "number" || t === "boolean" || t === "bigint";
|
|
2060
2237
|
}
|
|
2061
|
-
function
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
if (
|
|
2068
|
-
|
|
2069
|
-
if (
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
)
|
|
2238
|
+
function isSlimNumericPrimitive(value) {
|
|
2239
|
+
return value === 0 || value === 0n;
|
|
2240
|
+
}
|
|
2241
|
+
function blankForKind(slimDefault) {
|
|
2242
|
+
if (typeof slimDefault === "string") return "";
|
|
2243
|
+
if (typeof slimDefault === "number") return 0;
|
|
2244
|
+
if (typeof slimDefault === "bigint") return 0n;
|
|
2245
|
+
if (typeof slimDefault === "boolean") return false;
|
|
2246
|
+
if (slimDefault === null) return null;
|
|
2247
|
+
return void 0;
|
|
2248
|
+
}
|
|
2249
|
+
function expandUnsetAt(segments, schema, paths) {
|
|
2250
|
+
const du = schema.getUnionDiscriminatorAtPath(segments);
|
|
2251
|
+
if (du !== void 0) {
|
|
2252
|
+
const discPath = [...segments, du.discriminatorKey];
|
|
2253
|
+
const discSlim = schema.getEmptyValueAtPath(discPath);
|
|
2254
|
+
paths.push(canonicalizePath(discPath).key);
|
|
2255
|
+
return { [du.discriminatorKey]: blankForKind(discSlim) };
|
|
2256
|
+
}
|
|
2257
|
+
const slim = schema.getEmptyValueAtPath(segments);
|
|
2258
|
+
if (isPrimitiveOrEmpty(slim)) {
|
|
2259
|
+
paths.push(canonicalizePath(segments).key);
|
|
2260
|
+
return slim;
|
|
2261
|
+
}
|
|
2262
|
+
if (slim instanceof Date || slim instanceof RegExp || slim instanceof Map || slim instanceof Set || typeof slim === "function") {
|
|
2263
|
+
paths.push(canonicalizePath(segments).key);
|
|
2264
|
+
return slim;
|
|
2265
|
+
}
|
|
2266
|
+
if (Array.isArray(slim)) return slim;
|
|
2267
|
+
const result = {};
|
|
2268
|
+
for (const key of Object.keys(slim)) {
|
|
2269
|
+
result[key] = expandUnsetAt([...segments, key], schema, paths);
|
|
2270
|
+
}
|
|
2271
|
+
return result;
|
|
2075
2272
|
}
|
|
2076
2273
|
|
|
2077
2274
|
function buildValuesProxy(form) {
|
|
@@ -2141,28 +2338,6 @@ function buildValuesProxy(form) {
|
|
|
2141
2338
|
});
|
|
2142
2339
|
}
|
|
2143
2340
|
|
|
2144
|
-
function blankForKind(slimDefault) {
|
|
2145
|
-
if (typeof slimDefault === "string") return "";
|
|
2146
|
-
if (typeof slimDefault === "number") return 0;
|
|
2147
|
-
if (typeof slimDefault === "bigint") return 0n;
|
|
2148
|
-
if (typeof slimDefault === "boolean") return false;
|
|
2149
|
-
if (slimDefault === null) return null;
|
|
2150
|
-
return void 0;
|
|
2151
|
-
}
|
|
2152
|
-
function readonlySetSnapshot(source) {
|
|
2153
|
-
const snapshot = new Set(source);
|
|
2154
|
-
return new Proxy(snapshot, {
|
|
2155
|
-
get(target, prop) {
|
|
2156
|
-
if (prop === "add" || prop === "delete" || prop === "clear") {
|
|
2157
|
-
return () => {
|
|
2158
|
-
throw new TypeError(`Cannot mutate readonly Set: '${String(prop)}' is not allowed.`);
|
|
2159
|
-
};
|
|
2160
|
-
}
|
|
2161
|
-
const value = Reflect.get(target, prop, target);
|
|
2162
|
-
return typeof value === "function" ? value.bind(target) : value;
|
|
2163
|
-
}
|
|
2164
|
-
});
|
|
2165
|
-
}
|
|
2166
2341
|
function buildFormApi(state, formInstanceId, options = {}) {
|
|
2167
2342
|
const instanceMeta = (() => {
|
|
2168
2343
|
const bag = {};
|
|
@@ -2185,6 +2360,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2185
2360
|
Object.keys(registerConfig).length > 0 ? registerConfig : void 0
|
|
2186
2361
|
);
|
|
2187
2362
|
const processOptions = options.onInvalidSubmit !== void 0 ? { onInvalidSubmit: options.onInvalidSubmit } : {};
|
|
2363
|
+
const defaultInvalidSubmitPolicy = options.onInvalidSubmit ?? "focus-first-error";
|
|
2188
2364
|
const {
|
|
2189
2365
|
validate: validateBuilt,
|
|
2190
2366
|
validateAsync: validateAsyncBuilt,
|
|
@@ -2205,49 +2381,53 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2205
2381
|
next,
|
|
2206
2382
|
state.schema
|
|
2207
2383
|
);
|
|
2208
|
-
const ok2 = state.setValueAtPath([], walked2.cleanedValues, withInstanceMeta());
|
|
2209
|
-
if (!ok2) return false;
|
|
2210
2384
|
for (const pathKey of walked2.paths) {
|
|
2211
|
-
|
|
2212
|
-
if (segments2 === null) continue;
|
|
2213
|
-
state.setValueAtPath(
|
|
2214
|
-
segments2,
|
|
2215
|
-
state.schema.getDefaultAtPath(segments2),
|
|
2216
|
-
withInstanceMeta({ blank: true })
|
|
2217
|
-
);
|
|
2385
|
+
state.blankPaths.add(pathKey);
|
|
2218
2386
|
}
|
|
2219
|
-
return
|
|
2387
|
+
return state.setValueAtPath([], walked2.cleanedValues, withInstanceMeta());
|
|
2220
2388
|
}
|
|
2221
2389
|
const segments = canonicalizePath(pathOrValue).segments;
|
|
2222
|
-
|
|
2390
|
+
const writeUnsetAt = () => {
|
|
2223
2391
|
const last = segments.length > 0 ? segments[segments.length - 1] : void 0;
|
|
2224
2392
|
if (typeof last === "string") {
|
|
2225
2393
|
const parent = segments.slice(0, -1);
|
|
2226
2394
|
const parentDU = state.schema.getUnionDiscriminatorAtPath(parent);
|
|
2227
2395
|
if (parentDU?.discriminatorKey === last) {
|
|
2228
|
-
const slimDefault = state.schema.
|
|
2396
|
+
const slimDefault = state.schema.getEmptyValueAtPath(segments);
|
|
2229
2397
|
const blank = blankForKind(slimDefault);
|
|
2230
2398
|
return state.setValueAtPath(segments, blank, withInstanceMeta({ blank: true }));
|
|
2231
2399
|
}
|
|
2232
2400
|
}
|
|
2233
|
-
|
|
2401
|
+
const blankPaths = [];
|
|
2402
|
+
const expanded = expandUnsetAt(
|
|
2234
2403
|
segments,
|
|
2235
|
-
state.schema
|
|
2236
|
-
|
|
2404
|
+
state.schema,
|
|
2405
|
+
blankPaths
|
|
2237
2406
|
);
|
|
2238
|
-
|
|
2407
|
+
const segmentsKey = canonicalizePath(segments).key;
|
|
2408
|
+
if (blankPaths.length === 1 && blankPaths[0] === segmentsKey) {
|
|
2409
|
+
return state.setValueAtPath(segments, expanded, withInstanceMeta({ blank: true }));
|
|
2410
|
+
}
|
|
2411
|
+
const ok2 = state.setValueAtPath(segments, expanded, withInstanceMeta());
|
|
2412
|
+
if (!ok2) return false;
|
|
2413
|
+
for (const pathKey of blankPaths) {
|
|
2414
|
+
const blankSegments = segmentsForPathKey(pathKey);
|
|
2415
|
+
if (blankSegments === null) continue;
|
|
2416
|
+
state.setValueAtPath(
|
|
2417
|
+
blankSegments,
|
|
2418
|
+
state.getValueAtPath(blankSegments),
|
|
2419
|
+
withInstanceMeta({ blank: true })
|
|
2420
|
+
);
|
|
2421
|
+
}
|
|
2422
|
+
return true;
|
|
2423
|
+
};
|
|
2424
|
+
if (isUnset(maybeValue)) return writeUnsetAt();
|
|
2239
2425
|
let resolvedValue;
|
|
2240
2426
|
if (typeof maybeValue === "function") {
|
|
2241
2427
|
const current = state.getValueAtPath(segments);
|
|
2242
2428
|
const prev = current === void 0 ? state.schema.getDefaultAtPath(segments) : current;
|
|
2243
2429
|
resolvedValue = maybeValue(prev);
|
|
2244
|
-
if (isUnset(resolvedValue))
|
|
2245
|
-
return state.setValueAtPath(
|
|
2246
|
-
segments,
|
|
2247
|
-
state.schema.getDefaultAtPath(segments),
|
|
2248
|
-
withInstanceMeta({ blank: true })
|
|
2249
|
-
);
|
|
2250
|
-
}
|
|
2430
|
+
if (isUnset(resolvedValue)) return writeUnsetAt();
|
|
2251
2431
|
} else {
|
|
2252
2432
|
resolvedValue = maybeValue;
|
|
2253
2433
|
}
|
|
@@ -2263,7 +2443,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2263
2443
|
if (blankSegments === null) continue;
|
|
2264
2444
|
state.setValueAtPath(
|
|
2265
2445
|
blankSegments,
|
|
2266
|
-
state.
|
|
2446
|
+
state.getValueAtPath(blankSegments),
|
|
2267
2447
|
withInstanceMeta({ blank: true })
|
|
2268
2448
|
);
|
|
2269
2449
|
}
|
|
@@ -2327,8 +2507,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2327
2507
|
state.userErrors.delete(FORM_ERRORS_PATH_KEY);
|
|
2328
2508
|
}
|
|
2329
2509
|
const submitting = computed(() => state.submitting.value);
|
|
2330
|
-
const
|
|
2510
|
+
const submissionAttempts = computed(() => state.submissionAttempts.value);
|
|
2511
|
+
const submitted = computed(() => state.submitted.value);
|
|
2331
2512
|
const submitError = computed(() => state.submitError.value);
|
|
2513
|
+
const departAttempts = computed(() => state.departAttempts.value);
|
|
2332
2514
|
const validating = computed(() => state.activeValidations.value > 0);
|
|
2333
2515
|
const valid = computed(
|
|
2334
2516
|
() => state.firstValidationDone.value && state.schemaErrors.size === 0 && state.userErrors.size === 0 && state.derivedBlankErrors.value.size === 0 && !validating.value
|
|
@@ -2353,8 +2535,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2353
2535
|
return {
|
|
2354
2536
|
...rootBase,
|
|
2355
2537
|
submitting: state.submitting.value,
|
|
2356
|
-
|
|
2538
|
+
submissionAttempts: state.submissionAttempts.value,
|
|
2539
|
+
departAttempts: state.departAttempts.value,
|
|
2357
2540
|
submitError: state.submitError.value,
|
|
2541
|
+
errorCount: rootBase.errors.length,
|
|
2542
|
+
submitted: state.submitted.value,
|
|
2358
2543
|
instanceId: formInstanceId
|
|
2359
2544
|
};
|
|
2360
2545
|
};
|
|
@@ -2414,8 +2599,14 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2414
2599
|
meta: computed(() => rootFieldState.value.meta),
|
|
2415
2600
|
// Lifecycle (form-level only — not on FieldState).
|
|
2416
2601
|
submitting,
|
|
2417
|
-
|
|
2602
|
+
submissionAttempts,
|
|
2603
|
+
departAttempts,
|
|
2418
2604
|
submitError,
|
|
2605
|
+
// Scalar mirror over the array — meta is a single sticky surface
|
|
2606
|
+
// for both templates and `useWizard`'s `FormStatus`, so the
|
|
2607
|
+
// projection lives here.
|
|
2608
|
+
errorCount: computed(() => metaErrors.value.length),
|
|
2609
|
+
submitted,
|
|
2419
2610
|
// Per-`useForm()`-call identity. Stable for one mount; new on
|
|
2420
2611
|
// re-mount; orthogonal to `form.key` (which is the user-supplied
|
|
2421
2612
|
// shared identifier). Useful for devtools panels disambiguating
|
|
@@ -2435,13 +2626,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2435
2626
|
);
|
|
2436
2627
|
state.reset(walked.cleanedValues);
|
|
2437
2628
|
for (const pathKey of walked.paths) {
|
|
2438
|
-
|
|
2439
|
-
if (segments === null) continue;
|
|
2440
|
-
state.setValueAtPath(
|
|
2441
|
-
segments,
|
|
2442
|
-
state.schema.getDefaultAtPath(segments),
|
|
2443
|
-
withInstanceMeta({ blank: true })
|
|
2444
|
-
);
|
|
2629
|
+
state.blankPaths.add(pathKey);
|
|
2445
2630
|
state.originalBlankPaths.add(pathKey);
|
|
2446
2631
|
}
|
|
2447
2632
|
}
|
|
@@ -2491,56 +2676,141 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2491
2676
|
target.element.scrollIntoView(options2);
|
|
2492
2677
|
return true;
|
|
2493
2678
|
};
|
|
2679
|
+
const applyInvalidSubmitPolicyPublic = (policy) => {
|
|
2680
|
+
applyInvalidSubmitPolicy(state, formInstanceId, policy ?? defaultInvalidSubmitPolicy);
|
|
2681
|
+
};
|
|
2494
2682
|
const fieldArrays = buildFieldArrayApi(state);
|
|
2495
2683
|
const blankPathsView = computed(() => {
|
|
2496
|
-
|
|
2684
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2685
|
+
const paths = [];
|
|
2686
|
+
for (const pk of state.blankPaths) {
|
|
2687
|
+
keys.add(pk);
|
|
2688
|
+
const segs = segmentsForPathKey(pk);
|
|
2689
|
+
if (segs !== null) paths.push(segs);
|
|
2690
|
+
}
|
|
2691
|
+
Object.freeze(paths);
|
|
2692
|
+
const view = {
|
|
2693
|
+
get size() {
|
|
2694
|
+
return keys.size;
|
|
2695
|
+
},
|
|
2696
|
+
has(input) {
|
|
2697
|
+
const { key } = canonicalizePath(input);
|
|
2698
|
+
return keys.has(key);
|
|
2699
|
+
},
|
|
2700
|
+
values() {
|
|
2701
|
+
return paths;
|
|
2702
|
+
},
|
|
2703
|
+
[Symbol.iterator]() {
|
|
2704
|
+
return paths[Symbol.iterator]();
|
|
2705
|
+
}
|
|
2706
|
+
};
|
|
2707
|
+
return Object.freeze(view);
|
|
2497
2708
|
});
|
|
2498
2709
|
const valuesProxy = buildValuesProxy(state.form);
|
|
2499
2710
|
const fieldStateProxy = buildFieldStateProxy(state, getFormMetaBase, fieldStateAccessorOptions);
|
|
2711
|
+
function gated(fn) {
|
|
2712
|
+
return ((...args) => {
|
|
2713
|
+
void state.activate();
|
|
2714
|
+
return fn(...args);
|
|
2715
|
+
});
|
|
2716
|
+
}
|
|
2500
2717
|
return {
|
|
2501
|
-
handleSubmit,
|
|
2502
|
-
//
|
|
2503
|
-
//
|
|
2504
|
-
// so
|
|
2505
|
-
//
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2718
|
+
handleSubmit: gated(handleSubmit),
|
|
2719
|
+
// Callable readonly Proxies (`values`, `fields`, `errors`) and the
|
|
2720
|
+
// reactive containers (`meta`, `history`, `blankPaths`) are exposed
|
|
2721
|
+
// through getters so reading them activates the form on first
|
|
2722
|
+
// touch. Each underlying object is identity-stable across reads.
|
|
2723
|
+
get values() {
|
|
2724
|
+
void state.activate();
|
|
2725
|
+
return valuesProxy;
|
|
2726
|
+
},
|
|
2727
|
+
get fields() {
|
|
2728
|
+
void state.activate();
|
|
2729
|
+
return fieldStateProxy;
|
|
2730
|
+
},
|
|
2731
|
+
setValue: gated(setValueImpl),
|
|
2732
|
+
validate: gated(validate),
|
|
2733
|
+
validateAsync: gated(validateAsync),
|
|
2734
|
+
process: gated(process),
|
|
2735
|
+
register: gated(register),
|
|
2514
2736
|
key: state.formKey,
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2737
|
+
// Auto-unwrapping views over the per-store async-defaults lifecycle
|
|
2738
|
+
// refs (see FormStore.hydrating / hydrateError). Reading either
|
|
2739
|
+
// activates the form — observing factory state implies use.
|
|
2740
|
+
get hydrating() {
|
|
2741
|
+
void state.activate();
|
|
2742
|
+
return state.hydrating.value;
|
|
2743
|
+
},
|
|
2744
|
+
get hydrateError() {
|
|
2745
|
+
void state.activate();
|
|
2746
|
+
return state.hydrateError.value;
|
|
2747
|
+
},
|
|
2748
|
+
// Orthogonal to `hydrating` and `hydrateError`: `ready` flips true
|
|
2749
|
+
// once defaults are applied (sync at construction or async factory
|
|
2750
|
+
// resolved successfully). One-way latch — stays true through later
|
|
2751
|
+
// refetches even when those refetches fail, so stale-while-
|
|
2752
|
+
// revalidate UIs keep rendering the prior values while
|
|
2753
|
+
// `hydrateError` surfaces the refresh failure.
|
|
2754
|
+
get ready() {
|
|
2755
|
+
void state.activate();
|
|
2756
|
+
return state.defaultsResolved.value;
|
|
2757
|
+
},
|
|
2758
|
+
// `rehydrate` and `activate` are themselves activation entry points
|
|
2759
|
+
// — they fire the factory by design. Wrapping them with `gated`
|
|
2760
|
+
// would double-fire (`state.activate()` plus the underlying call),
|
|
2761
|
+
// so they call `state` directly.
|
|
2762
|
+
rehydrate: () => state.rehydrate(),
|
|
2763
|
+
activate: () => state.activate(),
|
|
2764
|
+
get errors() {
|
|
2765
|
+
void state.activate();
|
|
2766
|
+
return errorsProxy;
|
|
2767
|
+
},
|
|
2768
|
+
toRef: gated(pathToRef),
|
|
2769
|
+
setFieldErrors: gated(setFieldErrors),
|
|
2770
|
+
addFieldErrors: gated(addFieldErrors),
|
|
2771
|
+
clearFieldErrors: gated(clearFieldErrors),
|
|
2772
|
+
setFormErrors: gated(setFormErrors),
|
|
2773
|
+
clearFormErrors: gated(clearFormErrors),
|
|
2774
|
+
get meta() {
|
|
2775
|
+
void state.activate();
|
|
2776
|
+
return formMeta;
|
|
2777
|
+
},
|
|
2778
|
+
reset: gated(reset),
|
|
2779
|
+
resetField: gated(resetField),
|
|
2780
|
+
clear: gated(clear),
|
|
2781
|
+
persist: gated(persist),
|
|
2782
|
+
clearPersistedDraft: gated(clearPersistedDraft),
|
|
2783
|
+
focusFirstError: gated(focusFirstError),
|
|
2784
|
+
scrollToFirstError: gated(scrollToFirstError),
|
|
2785
|
+
applyInvalidSubmitPolicy: gated(applyInvalidSubmitPolicyPublic),
|
|
2786
|
+
touch: gated(touch),
|
|
2787
|
+
get history() {
|
|
2788
|
+
void state.activate();
|
|
2789
|
+
return formHistory;
|
|
2790
|
+
},
|
|
2791
|
+
append: gated(fieldArrays.append),
|
|
2792
|
+
prepend: gated(fieldArrays.prepend),
|
|
2793
|
+
insert: gated(fieldArrays.insert),
|
|
2794
|
+
remove: gated(fieldArrays.remove),
|
|
2795
|
+
swap: gated(fieldArrays.swap),
|
|
2796
|
+
move: gated(fieldArrays.move),
|
|
2797
|
+
replace: gated(fieldArrays.replace),
|
|
2798
|
+
get blankPaths() {
|
|
2799
|
+
void state.activate();
|
|
2800
|
+
return blankPathsView;
|
|
2801
|
+
}
|
|
2540
2802
|
};
|
|
2541
2803
|
}
|
|
2542
2804
|
|
|
2543
|
-
const defaultShouldShowErrors = (field, formMeta) =>
|
|
2805
|
+
const defaultShouldShowErrors = (field, formMeta) => {
|
|
2806
|
+
const hasOwnError = field.errors.some(
|
|
2807
|
+
(e) => e.path.length === field.path.length && e.path.every((s, i) => s === field.path[i])
|
|
2808
|
+
);
|
|
2809
|
+
if (!hasOwnError) return false;
|
|
2810
|
+
if (field.validating === true) return false;
|
|
2811
|
+
if (formMeta.submissionAttempts > 0) return true;
|
|
2812
|
+
return field.touched === true && field.focused !== true;
|
|
2813
|
+
};
|
|
2544
2814
|
const SHOW_ALWAYS = () => true;
|
|
2545
2815
|
const SHOW_NEVER = () => false;
|
|
2546
2816
|
function resolveShouldShowErrors(config) {
|
|
@@ -2553,7 +2823,7 @@ function resolveShouldShowErrors(config) {
|
|
|
2553
2823
|
function isHydratedFieldRecord(value) {
|
|
2554
2824
|
if (typeof value !== "object" || value === null) return false;
|
|
2555
2825
|
const r = value;
|
|
2556
|
-
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) &&
|
|
2826
|
+
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";
|
|
2557
2827
|
}
|
|
2558
2828
|
function isHydratedValidationErrorArray(value) {
|
|
2559
2829
|
if (!Array.isArray(value)) return false;
|
|
@@ -2590,7 +2860,8 @@ function walkDuStubs(schema, value, path, warned) {
|
|
|
2590
2860
|
if (du !== void 0) {
|
|
2591
2861
|
const discValue = rec[du.discriminatorKey];
|
|
2592
2862
|
if (discValue !== void 0 && !du.isVariantSelected(discValue)) {
|
|
2593
|
-
|
|
2863
|
+
const isKindBlank = discValue === "" || discValue === 0 || discValue === 0n || discValue === false || discValue === null;
|
|
2864
|
+
if (!isKindBlank && warned !== void 0 && __DEV__) {
|
|
2594
2865
|
const dotted = path.map((s) => String(s)).join(".") || "(root)";
|
|
2595
2866
|
const key = `${dotted}::${String(discValue)}`;
|
|
2596
2867
|
if (!warned.has(key)) {
|
|
@@ -2669,9 +2940,45 @@ function cloneVariantSnapshot(value) {
|
|
|
2669
2940
|
for (const k of Object.keys(src)) out[k] = cloneVariantSnapshot(src[k]);
|
|
2670
2941
|
return out;
|
|
2671
2942
|
}
|
|
2943
|
+
function walkAuthoredFromConstraints(value, prefix, out) {
|
|
2944
|
+
if (prefix.length > 0) out.add(canonicalizePath(prefix).key);
|
|
2945
|
+
if (isPlainRecord(value)) {
|
|
2946
|
+
for (const k of Object.keys(value)) {
|
|
2947
|
+
walkAuthoredFromConstraints(value[k], [...prefix, k], out);
|
|
2948
|
+
}
|
|
2949
|
+
return;
|
|
2950
|
+
}
|
|
2951
|
+
if (Array.isArray(value)) {
|
|
2952
|
+
for (let i = 0; i < value.length; i++) {
|
|
2953
|
+
walkAuthoredFromConstraints(value[i], [...prefix, i], out);
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
function walkAuthoredFromSchemaDiff(withDefaults, withoutDefaults, prefix, out) {
|
|
2958
|
+
if (isPlainRecord(withDefaults) && isPlainRecord(withoutDefaults)) {
|
|
2959
|
+
const left = withDefaults;
|
|
2960
|
+
const right = withoutDefaults;
|
|
2961
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)]);
|
|
2962
|
+
for (const k of keys) {
|
|
2963
|
+
walkAuthoredFromSchemaDiff(left[k], right[k], [...prefix, k], out);
|
|
2964
|
+
}
|
|
2965
|
+
return;
|
|
2966
|
+
}
|
|
2967
|
+
if (Array.isArray(withDefaults) && Array.isArray(withoutDefaults)) {
|
|
2968
|
+
const len = Math.max(withDefaults.length, withoutDefaults.length);
|
|
2969
|
+
for (let i = 0; i < len; i++) {
|
|
2970
|
+
walkAuthoredFromSchemaDiff(withDefaults[i], withoutDefaults[i], [...prefix, i], out);
|
|
2971
|
+
}
|
|
2972
|
+
return;
|
|
2973
|
+
}
|
|
2974
|
+
if (!Object.is(withDefaults, withoutDefaults) && prefix.length > 0) {
|
|
2975
|
+
out.add(canonicalizePath(prefix).key);
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2672
2978
|
function createFormStore(options) {
|
|
2673
2979
|
const { formKey, schema, defaultValues, strict = true, hydration } = options;
|
|
2674
2980
|
const ssr = options.ssr === true;
|
|
2981
|
+
const ssrPrefetch = options.ssrPrefetch;
|
|
2675
2982
|
const rememberVariants = options.rememberVariants !== false;
|
|
2676
2983
|
const fieldValidationMode = options.validateOn ?? "change";
|
|
2677
2984
|
const fieldValidationDebounceMs = normalizeNumericOption({
|
|
@@ -2717,6 +3024,29 @@ function createFormStore(options) {
|
|
|
2717
3024
|
strict
|
|
2718
3025
|
});
|
|
2719
3026
|
const schemaInitialData = schemaResponse.data;
|
|
3027
|
+
const authoredPaths = /* @__PURE__ */ new Set();
|
|
3028
|
+
function rebuildAuthoredPaths(constraints, schemaWithDefaultsData) {
|
|
3029
|
+
authoredPaths.clear();
|
|
3030
|
+
if (constraints !== void 0) {
|
|
3031
|
+
walkAuthoredFromConstraints(constraints, [], authoredPaths);
|
|
3032
|
+
}
|
|
3033
|
+
const slimResponse = schema.getDefaultValues({
|
|
3034
|
+
useDefaultSchemaValues: false,
|
|
3035
|
+
strict
|
|
3036
|
+
});
|
|
3037
|
+
walkAuthoredFromSchemaDiff(schemaWithDefaultsData, slimResponse.data, [], authoredPaths);
|
|
3038
|
+
}
|
|
3039
|
+
rebuildAuthoredPaths(defaultValues, schemaInitialData);
|
|
3040
|
+
function filterAuthoredErrors(errors) {
|
|
3041
|
+
return errors.filter((err) => {
|
|
3042
|
+
const pathSegments = err.path;
|
|
3043
|
+
if (pathSegments.length === 0) return true;
|
|
3044
|
+
const value = getAtPath(form.value, pathSegments);
|
|
3045
|
+
if (value !== void 0) return true;
|
|
3046
|
+
if (authoredPaths.has(canonicalizePath(pathSegments).key)) return true;
|
|
3047
|
+
return !schema.isPreprocessOrCoerceLeaf(pathSegments);
|
|
3048
|
+
});
|
|
3049
|
+
}
|
|
2720
3050
|
const initialData = hydration !== void 0 ? hydration.form : structuralSnapshot(schemaInitialData);
|
|
2721
3051
|
const stubbedInitialData = applyDuStubs(schema, initialData, {
|
|
2722
3052
|
warn: true
|
|
@@ -2733,8 +3063,9 @@ function createFormStore(options) {
|
|
|
2733
3063
|
const blankPaths = reactive(/* @__PURE__ */ new Set());
|
|
2734
3064
|
const originalBlankPaths = /* @__PURE__ */ new Set();
|
|
2735
3065
|
for (const raw of initialTransientList) {
|
|
2736
|
-
|
|
2737
|
-
|
|
3066
|
+
const key = coerceToPathKey(raw);
|
|
3067
|
+
blankPaths.add(key);
|
|
3068
|
+
originalBlankPaths.add(key);
|
|
2738
3069
|
}
|
|
2739
3070
|
const variantMemory = /* @__PURE__ */ new Map();
|
|
2740
3071
|
function clearVariantMemoryUnderPath(arrayPath) {
|
|
@@ -2802,10 +3133,18 @@ function createFormStore(options) {
|
|
|
2802
3133
|
});
|
|
2803
3134
|
const submitting = ref(false);
|
|
2804
3135
|
const activeSubmissions = ref(0);
|
|
2805
|
-
const
|
|
3136
|
+
const submissionAttempts = ref(0);
|
|
3137
|
+
const submitted = ref(false);
|
|
2806
3138
|
const submitError = ref(null);
|
|
3139
|
+
const departAttempts = ref(0);
|
|
2807
3140
|
const submissionGeneration = ref(0);
|
|
2808
3141
|
const activeValidations = ref(0);
|
|
3142
|
+
const hydrating = ref(false);
|
|
3143
|
+
const hydrateError = ref(null);
|
|
3144
|
+
const defaultValuesFactory = ref(void 0);
|
|
3145
|
+
const defaultsResolved = ref(false);
|
|
3146
|
+
const activated = ref(false);
|
|
3147
|
+
const activationPromise = ref(void 0);
|
|
2809
3148
|
const firstValidationDone = ref(!strict || schema.needsAsyncValidation?.() !== true);
|
|
2810
3149
|
watch(activeValidations, (now, prev) => {
|
|
2811
3150
|
if (prev > 0 && now === 0) {
|
|
@@ -2870,7 +3209,7 @@ function createFormStore(options) {
|
|
|
2870
3209
|
connected: false,
|
|
2871
3210
|
focused: null,
|
|
2872
3211
|
blurred: null,
|
|
2873
|
-
touched:
|
|
3212
|
+
touched: false
|
|
2874
3213
|
});
|
|
2875
3214
|
});
|
|
2876
3215
|
if (strict && !schemaResponse.success) {
|
|
@@ -2890,9 +3229,16 @@ function createFormStore(options) {
|
|
|
2890
3229
|
path,
|
|
2891
3230
|
updatedAt: patch.updatedAt ?? current?.updatedAt ?? null,
|
|
2892
3231
|
connected: patch.connected ?? current?.connected ?? false,
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
3232
|
+
// focused/blurred use an explicit-undefined guard because
|
|
3233
|
+
// patches legitimately carry `null` to mark a disconnect — the
|
|
3234
|
+
// `??` operator would short-circuit on null and fall through to
|
|
3235
|
+
// `current`, losing the intent. `!== undefined` honours an
|
|
3236
|
+
// explicit null and preserves current only on absence.
|
|
3237
|
+
focused: patch.focused !== void 0 ? patch.focused : current?.focused ?? null,
|
|
3238
|
+
blurred: patch.blurred !== void 0 ? patch.blurred : current?.blurred ?? null,
|
|
3239
|
+
// touched is plain `boolean`; `??` is equivalent to the explicit
|
|
3240
|
+
// guard here because `false` is not nullish.
|
|
3241
|
+
touched: patch.touched ?? current?.touched ?? false
|
|
2896
3242
|
});
|
|
2897
3243
|
}
|
|
2898
3244
|
function applyFormReplacement(next, meta) {
|
|
@@ -2923,7 +3269,6 @@ function createFormStore(options) {
|
|
|
2923
3269
|
}
|
|
2924
3270
|
function setValueAtPath(path, value, meta) {
|
|
2925
3271
|
value = stripSymbolsDeep(value);
|
|
2926
|
-
value = schema.normalizeWriteValueAtPath(value, path);
|
|
2927
3272
|
if (!isSlimPrimitiveValid(schema, form, path, value)) {
|
|
2928
3273
|
return false;
|
|
2929
3274
|
}
|
|
@@ -3022,9 +3367,21 @@ function createFormStore(options) {
|
|
|
3022
3367
|
} else if (blankPaths.has(pathKey)) {
|
|
3023
3368
|
blankPaths.delete(pathKey);
|
|
3024
3369
|
}
|
|
3370
|
+
const wasAuthoredBefore = authoredPaths.has(pathKey);
|
|
3371
|
+
walkAuthoredFromConstraints(value, path, authoredPaths);
|
|
3372
|
+
const newlyAuthored = !wasAuthoredBefore && authoredPaths.has(pathKey);
|
|
3025
3373
|
const completedValue = mergeStructural(schema, path, value);
|
|
3026
3374
|
const currentValue = getAtPath(form.value, path);
|
|
3027
3375
|
if (Object.is(currentValue, completedValue)) {
|
|
3376
|
+
if (newlyAuthored && schema.isPreprocessOrCoerceLeaf(path)) {
|
|
3377
|
+
const modeForAuthoringTransition = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
3378
|
+
if (modeForAuthoringTransition === "change") {
|
|
3379
|
+
scheduleFieldValidation(path, false, {
|
|
3380
|
+
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
3381
|
+
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
3382
|
+
});
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3028
3385
|
return true;
|
|
3029
3386
|
}
|
|
3030
3387
|
const nextForm = setAtPathWithSchemaFill(form.value, schema, path, completedValue);
|
|
@@ -3137,12 +3494,11 @@ function createFormStore(options) {
|
|
|
3137
3494
|
prev.controller.abort();
|
|
3138
3495
|
}
|
|
3139
3496
|
const controller = new AbortController();
|
|
3140
|
-
const fresh = { controller, timer: null };
|
|
3497
|
+
const fresh = { controller, timer: null, settled: false };
|
|
3141
3498
|
fieldValidationState.set(key, fresh);
|
|
3142
3499
|
const run = () => {
|
|
3143
3500
|
fresh.timer = null;
|
|
3144
3501
|
if (controller.signal.aborted) return;
|
|
3145
|
-
const data = getAtPath(form.value, path);
|
|
3146
3502
|
let activeIncremented = false;
|
|
3147
3503
|
try {
|
|
3148
3504
|
activeValidations.value += 1;
|
|
@@ -3154,17 +3510,16 @@ function createFormStore(options) {
|
|
|
3154
3510
|
}
|
|
3155
3511
|
throw err;
|
|
3156
3512
|
}
|
|
3157
|
-
void Promise.resolve().then(() => schema.validateAtPath(
|
|
3513
|
+
void Promise.resolve().then(() => schema.validateAtPath(form.value, void 0)).then((response) => {
|
|
3158
3514
|
if (controller.signal.aborted) return;
|
|
3159
|
-
const
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
}));
|
|
3163
|
-
applySchemaErrorsForSubtree(path, reStamped);
|
|
3515
|
+
const errors = response.success ? [] : response.errors;
|
|
3516
|
+
const filtered = filterAuthoredErrors(errors);
|
|
3517
|
+
applySchemaErrorsForSubtree([], filtered);
|
|
3164
3518
|
}).catch(() => {
|
|
3165
3519
|
}).finally(() => {
|
|
3166
3520
|
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
3167
3521
|
decFieldValidation(key);
|
|
3522
|
+
fresh.settled = true;
|
|
3168
3523
|
});
|
|
3169
3524
|
};
|
|
3170
3525
|
if (immediate || effectiveDebounce === 0) {
|
|
@@ -3174,8 +3529,13 @@ function createFormStore(options) {
|
|
|
3174
3529
|
}
|
|
3175
3530
|
}
|
|
3176
3531
|
function cancelFieldValidation() {
|
|
3177
|
-
for (const entry of fieldValidationState
|
|
3178
|
-
if (entry.timer !== null)
|
|
3532
|
+
for (const [pkey, entry] of fieldValidationState) {
|
|
3533
|
+
if (entry.timer !== null) {
|
|
3534
|
+
clearTimeout(entry.timer);
|
|
3535
|
+
} else if (!entry.settled) {
|
|
3536
|
+
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
3537
|
+
decFieldValidation(pkey);
|
|
3538
|
+
}
|
|
3179
3539
|
entry.controller.abort();
|
|
3180
3540
|
}
|
|
3181
3541
|
fieldValidationState.clear();
|
|
@@ -3344,7 +3704,12 @@ function createFormStore(options) {
|
|
|
3344
3704
|
}
|
|
3345
3705
|
elementToFormInstance.set(element, formInstanceId);
|
|
3346
3706
|
sortedRegistrationsCache = null;
|
|
3347
|
-
|
|
3707
|
+
const current = fields.get(key);
|
|
3708
|
+
touchFieldRecord(key, path, {
|
|
3709
|
+
connected: true,
|
|
3710
|
+
focused: current?.focused ?? false,
|
|
3711
|
+
blurred: current?.blurred ?? true
|
|
3712
|
+
});
|
|
3348
3713
|
return true;
|
|
3349
3714
|
}
|
|
3350
3715
|
function deregisterElement(path, element) {
|
|
@@ -3359,7 +3724,7 @@ function createFormStore(options) {
|
|
|
3359
3724
|
const remaining = record.elements.size;
|
|
3360
3725
|
if (remaining === 0) {
|
|
3361
3726
|
elements.delete(key);
|
|
3362
|
-
touchFieldRecord(key, path, { connected: false });
|
|
3727
|
+
touchFieldRecord(key, path, { connected: false, focused: null, blurred: null });
|
|
3363
3728
|
}
|
|
3364
3729
|
return remaining;
|
|
3365
3730
|
}
|
|
@@ -3368,7 +3733,11 @@ function createFormStore(options) {
|
|
|
3368
3733
|
const { key } = canonicalizePath(path);
|
|
3369
3734
|
const current = fields.get(key);
|
|
3370
3735
|
if (current?.connected === true) return;
|
|
3371
|
-
touchFieldRecord(key, path, {
|
|
3736
|
+
touchFieldRecord(key, path, {
|
|
3737
|
+
connected: true,
|
|
3738
|
+
focused: current?.focused ?? false,
|
|
3739
|
+
blurred: current?.blurred ?? true
|
|
3740
|
+
});
|
|
3372
3741
|
}
|
|
3373
3742
|
function markFocused(path, focused, meta) {
|
|
3374
3743
|
const { key } = canonicalizePath(path);
|
|
@@ -3377,7 +3746,7 @@ function createFormStore(options) {
|
|
|
3377
3746
|
blurred: !focused,
|
|
3378
3747
|
// `touched` flips to true on blur and stays true thereafter; while
|
|
3379
3748
|
// a field is currently focused we keep whatever value it held.
|
|
3380
|
-
touched: focused ? fields.get(key)?.touched ??
|
|
3749
|
+
touched: focused ? fields.get(key)?.touched ?? false : true
|
|
3381
3750
|
});
|
|
3382
3751
|
const focusMode = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
3383
3752
|
if (!focused && focusMode === "blur") {
|
|
@@ -3412,6 +3781,84 @@ function createFormStore(options) {
|
|
|
3412
3781
|
function clear(path) {
|
|
3413
3782
|
return setValueAtPath(path, schema.getEmptyValueAtPath(path));
|
|
3414
3783
|
}
|
|
3784
|
+
function rehydrate() {
|
|
3785
|
+
const factory = defaultValuesFactory.value;
|
|
3786
|
+
if (factory === void 0) {
|
|
3787
|
+
throw new Error(
|
|
3788
|
+
"[attaform] form.rehydrate(): no defaultValues factory was captured. Configure useForm({ defaultValues: () => ... }) to enable rehydrate."
|
|
3789
|
+
);
|
|
3790
|
+
}
|
|
3791
|
+
return fireFactory(factory);
|
|
3792
|
+
}
|
|
3793
|
+
function fireFactory(factory) {
|
|
3794
|
+
activated.value = true;
|
|
3795
|
+
const promise = runFactoryAndApply(factory);
|
|
3796
|
+
activationPromise.value = promise;
|
|
3797
|
+
void promise.finally(() => {
|
|
3798
|
+
if (activationPromise.value === promise) activationPromise.value = void 0;
|
|
3799
|
+
});
|
|
3800
|
+
return promise;
|
|
3801
|
+
}
|
|
3802
|
+
function activate() {
|
|
3803
|
+
if (ssrPrefetch !== void 0) {
|
|
3804
|
+
ssrPrefetch.enqueue();
|
|
3805
|
+
if (!ssrPrefetch.shouldFire()) return Promise.resolve();
|
|
3806
|
+
}
|
|
3807
|
+
if (defaultsResolved.value === true) return Promise.resolve();
|
|
3808
|
+
if (activationPromise.value !== void 0) return activationPromise.value;
|
|
3809
|
+
if (activated.value === true) return Promise.resolve();
|
|
3810
|
+
const factory = defaultValuesFactory.value;
|
|
3811
|
+
if (factory === void 0) return Promise.resolve();
|
|
3812
|
+
return fireFactory(factory);
|
|
3813
|
+
}
|
|
3814
|
+
async function runFactoryAndApply(factory) {
|
|
3815
|
+
hydrating.value = true;
|
|
3816
|
+
try {
|
|
3817
|
+
const value = await factory();
|
|
3818
|
+
walkAuthoredFromConstraints(value, [], authoredPaths);
|
|
3819
|
+
const full = mergeSparseHydration(
|
|
3820
|
+
toRaw(form.value),
|
|
3821
|
+
value,
|
|
3822
|
+
schema
|
|
3823
|
+
);
|
|
3824
|
+
applyFormReplacement(full, { hydration: true });
|
|
3825
|
+
scheduleFieldValidation(
|
|
3826
|
+
[],
|
|
3827
|
+
true
|
|
3828
|
+
/* immediate */
|
|
3829
|
+
);
|
|
3830
|
+
clearHydrationFailedEntry();
|
|
3831
|
+
hydrateError.value = null;
|
|
3832
|
+
defaultsResolved.value = true;
|
|
3833
|
+
} catch (error) {
|
|
3834
|
+
clearHydrationFailedEntry();
|
|
3835
|
+
hydrateError.value = appendHydrationFailedEntry(error);
|
|
3836
|
+
} finally {
|
|
3837
|
+
hydrating.value = false;
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
function clearHydrationFailedEntry() {
|
|
3841
|
+
const existing = schemaErrors.get(FORM_ERRORS_PATH_KEY);
|
|
3842
|
+
if (existing === void 0) return;
|
|
3843
|
+
const filtered = existing.filter((e) => e.code !== AttaformErrorCode.HydrationFailed);
|
|
3844
|
+
if (filtered.length === 0) {
|
|
3845
|
+
schemaErrors.delete(FORM_ERRORS_PATH_KEY);
|
|
3846
|
+
} else {
|
|
3847
|
+
schemaErrors.set(FORM_ERRORS_PATH_KEY, filtered);
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
function appendHydrationFailedEntry(error) {
|
|
3851
|
+
const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Hydration failed";
|
|
3852
|
+
const entry = {
|
|
3853
|
+
message,
|
|
3854
|
+
path: [...FORM_ERRORS_PATH],
|
|
3855
|
+
formKey,
|
|
3856
|
+
code: AttaformErrorCode.HydrationFailed
|
|
3857
|
+
};
|
|
3858
|
+
const existing = schemaErrors.get(FORM_ERRORS_PATH_KEY) ?? [];
|
|
3859
|
+
schemaErrors.set(FORM_ERRORS_PATH_KEY, [...existing, entry]);
|
|
3860
|
+
return entry;
|
|
3861
|
+
}
|
|
3415
3862
|
function reset(nextDefaultValues) {
|
|
3416
3863
|
const resetSource = nextDefaultValues ?? defaultValues;
|
|
3417
3864
|
const completedResetConstraints = resetSource === void 0 ? void 0 : mergeStructural(schema, [], resetSource);
|
|
@@ -3421,6 +3868,7 @@ function createFormStore(options) {
|
|
|
3421
3868
|
strict
|
|
3422
3869
|
});
|
|
3423
3870
|
const next = resetResponse.data;
|
|
3871
|
+
rebuildAuthoredPaths(resetSource, next);
|
|
3424
3872
|
applyFormReplacement(next);
|
|
3425
3873
|
originals.clear();
|
|
3426
3874
|
diffAndApply({}, next, [], (patch) => {
|
|
@@ -3463,16 +3911,18 @@ function createFormStore(options) {
|
|
|
3463
3911
|
path: record.path,
|
|
3464
3912
|
updatedAt: now,
|
|
3465
3913
|
connected: record.connected,
|
|
3466
|
-
focused:
|
|
3467
|
-
blurred:
|
|
3468
|
-
touched:
|
|
3914
|
+
focused: record.focused,
|
|
3915
|
+
blurred: record.blurred,
|
|
3916
|
+
touched: false
|
|
3469
3917
|
});
|
|
3470
3918
|
}
|
|
3471
3919
|
submissionGeneration.value += 1;
|
|
3472
3920
|
submitting.value = false;
|
|
3473
3921
|
activeSubmissions.value = 0;
|
|
3474
|
-
|
|
3922
|
+
submissionAttempts.value = 0;
|
|
3923
|
+
submitted.value = false;
|
|
3475
3924
|
submitError.value = null;
|
|
3925
|
+
departAttempts.value = 0;
|
|
3476
3926
|
cancelFieldValidation();
|
|
3477
3927
|
variantMemory.clear();
|
|
3478
3928
|
for (const listener of resetListeners) {
|
|
@@ -3545,9 +3995,9 @@ function createFormStore(options) {
|
|
|
3545
3995
|
path: record.path,
|
|
3546
3996
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3547
3997
|
connected: record.connected,
|
|
3548
|
-
focused:
|
|
3549
|
-
blurred:
|
|
3550
|
-
touched:
|
|
3998
|
+
focused: record.focused,
|
|
3999
|
+
blurred: record.blurred,
|
|
4000
|
+
touched: false
|
|
3551
4001
|
});
|
|
3552
4002
|
}
|
|
3553
4003
|
function isPathPrefix(prefix, candidate) {
|
|
@@ -3610,8 +4060,18 @@ function createFormStore(options) {
|
|
|
3610
4060
|
shouldShowErrors: resolvedShouldShowErrors,
|
|
3611
4061
|
submitting,
|
|
3612
4062
|
activeSubmissions,
|
|
3613
|
-
|
|
4063
|
+
submissionAttempts,
|
|
4064
|
+
submitted,
|
|
3614
4065
|
submitError,
|
|
4066
|
+
departAttempts,
|
|
4067
|
+
hydrating,
|
|
4068
|
+
hydrateError,
|
|
4069
|
+
defaultValuesFactory,
|
|
4070
|
+
defaultsResolved,
|
|
4071
|
+
activated,
|
|
4072
|
+
activationPromise,
|
|
4073
|
+
rehydrate,
|
|
4074
|
+
activate,
|
|
3615
4075
|
submissionGeneration,
|
|
3616
4076
|
activeValidations,
|
|
3617
4077
|
firstValidationDone,
|
|
@@ -3626,6 +4086,7 @@ function createFormStore(options) {
|
|
|
3626
4086
|
setSchemaErrorsForPath,
|
|
3627
4087
|
setAllSchemaErrors,
|
|
3628
4088
|
clearSchemaErrors,
|
|
4089
|
+
applySchemaErrorsForSubtree,
|
|
3629
4090
|
setAllUserErrors,
|
|
3630
4091
|
addUserErrors,
|
|
3631
4092
|
clearUserErrors,
|
|
@@ -3871,6 +4332,11 @@ const PROTOCOL_VERSION = 1;
|
|
|
3871
4332
|
const JOIN_COLLECTION_WINDOW_MS = 50;
|
|
3872
4333
|
const SNAPSHOT_TIMEOUT_MS = 200;
|
|
3873
4334
|
const MAX_LEADER_ATTEMPTS = 3;
|
|
4335
|
+
function isFileLikeValue(value) {
|
|
4336
|
+
if (typeof File !== "undefined" && value instanceof File) return true;
|
|
4337
|
+
if (typeof Blob !== "undefined" && value instanceof Blob) return true;
|
|
4338
|
+
return false;
|
|
4339
|
+
}
|
|
3874
4340
|
function isDangerousSegment(s) {
|
|
3875
4341
|
return s === "__proto__" || s === "constructor" || s === "prototype";
|
|
3876
4342
|
}
|
|
@@ -3880,6 +4346,32 @@ function pathContainsDangerousSegment(path) {
|
|
|
3880
4346
|
}
|
|
3881
4347
|
return false;
|
|
3882
4348
|
}
|
|
4349
|
+
function isInboundShapeAcceptable(schema, path, value) {
|
|
4350
|
+
if (isFileLikeValue(value)) return false;
|
|
4351
|
+
let kind;
|
|
4352
|
+
if (Array.isArray(value)) {
|
|
4353
|
+
kind = "array";
|
|
4354
|
+
} else if (value !== null && typeof value === "object" && isPlainRecord(value)) {
|
|
4355
|
+
kind = "object";
|
|
4356
|
+
} else {
|
|
4357
|
+
kind = slimKindOf(value);
|
|
4358
|
+
}
|
|
4359
|
+
const accepted = schema.getSlimPrimitiveTypesAtPath(path);
|
|
4360
|
+
if (!accepted.has(kind)) return false;
|
|
4361
|
+
if (Array.isArray(value)) {
|
|
4362
|
+
for (let i = 0; i < value.length; i++) {
|
|
4363
|
+
if (!isInboundShapeAcceptable(schema, [...path, i], value[i])) return false;
|
|
4364
|
+
}
|
|
4365
|
+
return true;
|
|
4366
|
+
}
|
|
4367
|
+
if (isPlainRecord(value)) {
|
|
4368
|
+
for (const key of Object.keys(value)) {
|
|
4369
|
+
if (!isInboundShapeAcceptable(schema, [...path, key], value[key])) return false;
|
|
4370
|
+
}
|
|
4371
|
+
return true;
|
|
4372
|
+
}
|
|
4373
|
+
return true;
|
|
4374
|
+
}
|
|
3883
4375
|
function diffBlankPaths(prev, curr) {
|
|
3884
4376
|
const added = [];
|
|
3885
4377
|
const removed = [];
|
|
@@ -3892,6 +4384,7 @@ function snapshotForm(form) {
|
|
|
3892
4384
|
return structuralSnapshot(form);
|
|
3893
4385
|
}
|
|
3894
4386
|
function stripSensitivePathsDeep(value, pathSoFar, isSensitivePath) {
|
|
4387
|
+
if (isFileLikeValue(value)) return void 0;
|
|
3895
4388
|
if (value === null || typeof value !== "object") return value;
|
|
3896
4389
|
if (Array.isArray(value)) {
|
|
3897
4390
|
return value.map((item, i) => stripSensitivePathsDeep(item, [...pathSoFar, i], isSensitivePath));
|
|
@@ -3996,6 +4489,7 @@ function createMultiTabSyncModule(state, channelName, options) {
|
|
|
3996
4489
|
const safePatches = [];
|
|
3997
4490
|
for (const p of rawPatches) {
|
|
3998
4491
|
if (isPathLocallySuppressed(p.path)) continue;
|
|
4492
|
+
if ("value" in p && isFileLikeValue(p.value)) continue;
|
|
3999
4493
|
safePatches.push(p);
|
|
4000
4494
|
}
|
|
4001
4495
|
const { added, removed } = diffBlankPaths(prior.blankPathsSnapshot, state.blankPaths);
|
|
@@ -4035,6 +4529,9 @@ function createMultiTabSyncModule(state, channelName, options) {
|
|
|
4035
4529
|
for (const p of msg.formPatches) {
|
|
4036
4530
|
if (!Array.isArray(p.path)) continue;
|
|
4037
4531
|
if (isPathLocallySuppressed(p.path)) continue;
|
|
4532
|
+
if ("value" in p && !isInboundShapeAcceptable(state.schema, p.path, p.value)) {
|
|
4533
|
+
continue;
|
|
4534
|
+
}
|
|
4038
4535
|
safePatches.push(p);
|
|
4039
4536
|
}
|
|
4040
4537
|
const safeBlankAdded = [];
|
|
@@ -4069,11 +4566,7 @@ function createMultiTabSyncModule(state, channelName, options) {
|
|
|
4069
4566
|
}
|
|
4070
4567
|
function handleSnapshot(msg) {
|
|
4071
4568
|
if (lifecycle !== "joining") return;
|
|
4072
|
-
|
|
4073
|
-
options.validateForm(msg.form);
|
|
4074
|
-
} catch {
|
|
4075
|
-
return;
|
|
4076
|
-
}
|
|
4569
|
+
if (!isInboundShapeAcceptable(state.schema, [], msg.form)) return;
|
|
4077
4570
|
if (snapshotTimeoutTimer !== null) {
|
|
4078
4571
|
clearTimeout(snapshotTimeoutTimer);
|
|
4079
4572
|
snapshotTimeoutTimer = null;
|
|
@@ -4212,15 +4705,26 @@ function isSecureContext() {
|
|
|
4212
4705
|
return typeof window !== "undefined" && window.isSecureContext === true;
|
|
4213
4706
|
}
|
|
4214
4707
|
|
|
4215
|
-
function
|
|
4708
|
+
function resolveTrichotomy(input) {
|
|
4709
|
+
if (typeof input === "function") {
|
|
4710
|
+
return { kind: "async", factory: input };
|
|
4711
|
+
}
|
|
4712
|
+
return { kind: "sync", value: input };
|
|
4713
|
+
}
|
|
4714
|
+
|
|
4715
|
+
function useAbstractForm(configuration, options) {
|
|
4216
4716
|
if (configuration === void 0 || configuration === null || configuration.schema === void 0) {
|
|
4217
4717
|
throw new InvalidUseFormConfigError();
|
|
4218
4718
|
}
|
|
4219
4719
|
const key = resolveFormKey(configuration.key);
|
|
4220
4720
|
const instance = getCurrentInstance();
|
|
4221
4721
|
if (instance !== null) ensureAttaformInstalled(instance.appContext.app);
|
|
4222
|
-
const registry = useRegistry();
|
|
4223
|
-
const
|
|
4722
|
+
const registry = options?.registry ?? useRegistry();
|
|
4723
|
+
const resolvedDefaults = resolveTrichotomy(configuration.defaultValues);
|
|
4724
|
+
const materialisedDefaults = resolvedDefaults.kind === "sync" ? resolvedDefaults.value : void 0;
|
|
4725
|
+
const { defaultValues: _droppedDefaults, ...configWithoutDefaults } = configuration;
|
|
4726
|
+
const trichotomyOverride = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
|
|
4727
|
+
const merged = mergeWithDefaults(registry.defaults, trichotomyOverride);
|
|
4224
4728
|
const maxRecursionDepth = normalizeNumericOption({
|
|
4225
4729
|
value: merged.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH,
|
|
4226
4730
|
source: "useForm.maxRecursionDepth",
|
|
@@ -4241,7 +4745,27 @@ function useAbstractForm(configuration) {
|
|
|
4241
4745
|
warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
|
|
4242
4746
|
warnOnPersistDivergence(key, existing, configuration.persist);
|
|
4243
4747
|
}
|
|
4748
|
+
const hadPendingHydration = registry.pendingHydration.has(key);
|
|
4244
4749
|
const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
|
|
4750
|
+
if (existing !== void 0) ; else if (resolvedDefaults.kind === "sync") {
|
|
4751
|
+
state.defaultsResolved.value = true;
|
|
4752
|
+
}
|
|
4753
|
+
if (existing === void 0 && resolvedDefaults.kind === "async") {
|
|
4754
|
+
const factory = resolvedDefaults.factory;
|
|
4755
|
+
state.defaultValuesFactory.value = factory;
|
|
4756
|
+
if (hadPendingHydration) {
|
|
4757
|
+
state.hydrating.value = false;
|
|
4758
|
+
state.defaultsResolved.value = true;
|
|
4759
|
+
} else if (registry.ssr) {
|
|
4760
|
+
if (configuration.__ssrAccessed === true) {
|
|
4761
|
+
registry.enqueuePrefetch(key);
|
|
4762
|
+
}
|
|
4763
|
+
onServerPrefetch(() => {
|
|
4764
|
+
if (!registry.shouldPrefetch(key)) return;
|
|
4765
|
+
return state.activate();
|
|
4766
|
+
});
|
|
4767
|
+
}
|
|
4768
|
+
}
|
|
4245
4769
|
if (getCurrentScope() !== void 0) {
|
|
4246
4770
|
const releaseConsumer = registry.trackConsumer(key);
|
|
4247
4771
|
onScopeDispose(releaseConsumer);
|
|
@@ -4269,7 +4793,7 @@ function useAbstractForm(configuration) {
|
|
|
4269
4793
|
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
4270
4794
|
}
|
|
4271
4795
|
}
|
|
4272
|
-
if (existing === void 0 && merged.multiTab
|
|
4796
|
+
if (existing === void 0 && merged.multiTab === true && configuration.key !== void 0 && !registry.ssr) {
|
|
4273
4797
|
const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
|
|
4274
4798
|
const secureContext = isSecureContext();
|
|
4275
4799
|
if (hasBroadcastChannel && secureContext) {
|
|
@@ -4376,7 +4900,10 @@ function buildFreshState(key, schema, configuration, registry) {
|
|
|
4376
4900
|
configuration.defaultValues,
|
|
4377
4901
|
schema
|
|
4378
4902
|
);
|
|
4379
|
-
|
|
4903
|
+
let initialBlankPaths;
|
|
4904
|
+
if (pending === void 0) {
|
|
4905
|
+
initialBlankPaths = walked.paths;
|
|
4906
|
+
}
|
|
4380
4907
|
const resolvedSensitiveNames = configuration.sensitiveNames;
|
|
4381
4908
|
const resolvedIsSensitivePath = resolvedSensitiveNames === void 0 ? void 0 : createIsSensitivePath(resolvedSensitiveNames);
|
|
4382
4909
|
const resolvedSegmentMatchesSensitive = resolvedSensitiveNames === void 0 ? void 0 : createSegmentMatchesSensitive(resolvedSensitiveNames);
|
|
@@ -4389,6 +4916,21 @@ function buildFreshState(key, schema, configuration, registry) {
|
|
|
4389
4916
|
...configuration.validateOn !== void 0 ? { validateOn: configuration.validateOn } : {},
|
|
4390
4917
|
...configuration.debounceMs !== void 0 ? { debounceMs: configuration.debounceMs } : {},
|
|
4391
4918
|
ssr: registry.ssr,
|
|
4919
|
+
// Server-only: bind the SSR prefetch coordination handles. `enqueue`
|
|
4920
|
+
// records intent on every `state.activate()` so a wizard skip-list
|
|
4921
|
+
// override or a future transform mark has a consistent set to diff
|
|
4922
|
+
// against; `shouldFire` lets the activate path bail when the
|
|
4923
|
+
// wizard explicitly skipped this key — even an explicit
|
|
4924
|
+
// `form.activate()` defers to the wizard's render-efficiency
|
|
4925
|
+
// skip-list on the server.
|
|
4926
|
+
...registry.ssr ? {
|
|
4927
|
+
ssrPrefetch: {
|
|
4928
|
+
enqueue: () => {
|
|
4929
|
+
registry.enqueuePrefetch(key);
|
|
4930
|
+
},
|
|
4931
|
+
shouldFire: () => registry.shouldPrefetch(key)
|
|
4932
|
+
}
|
|
4933
|
+
} : {},
|
|
4392
4934
|
...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
|
|
4393
4935
|
...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
|
|
4394
4936
|
...configuration.shouldShowErrors !== void 0 ? { shouldShowErrors: configuration.shouldShowErrors } : {},
|
|
@@ -4569,8 +5111,9 @@ function wirePersistence(state, config) {
|
|
|
4569
5111
|
state.originalBlankPaths.delete(k);
|
|
4570
5112
|
}
|
|
4571
5113
|
for (const k of payload.data.blankPaths ?? []) {
|
|
4572
|
-
|
|
4573
|
-
state.
|
|
5114
|
+
const key2 = coerceToPathKey(k);
|
|
5115
|
+
state.blankPaths.add(key2);
|
|
5116
|
+
state.originalBlankPaths.add(key2);
|
|
4574
5117
|
}
|
|
4575
5118
|
if (include === "form+errors") {
|
|
4576
5119
|
if (payload.data.schemaErrors !== void 0) {
|
|
@@ -4751,7 +5294,9 @@ function isDescendantPathKey(candidate, ancestor) {
|
|
|
4751
5294
|
}
|
|
4752
5295
|
|
|
4753
5296
|
let injectedInstanceCounter = 0;
|
|
4754
|
-
function injectForm(
|
|
5297
|
+
function injectForm(input) {
|
|
5298
|
+
const key = typeof input === "string" ? input : input?.key;
|
|
5299
|
+
const ssrAccessed = typeof input === "object" && input !== null ? input.__ssrAccessed === true : false;
|
|
4755
5300
|
const instance = getCurrentInstance();
|
|
4756
5301
|
if (instance !== null) ensureAttaformInstalled(instance.appContext.app);
|
|
4757
5302
|
const registry = useRegistry();
|
|
@@ -4761,6 +5306,10 @@ function injectForm(key) {
|
|
|
4761
5306
|
const releaseConsumer = registry.trackConsumer(state.formKey);
|
|
4762
5307
|
onScopeDispose(releaseConsumer);
|
|
4763
5308
|
}
|
|
5309
|
+
if (registry.ssr && ssrAccessed) {
|
|
5310
|
+
registry.enqueuePrefetch(state.formKey);
|
|
5311
|
+
onServerPrefetch(() => state.activate());
|
|
5312
|
+
}
|
|
4764
5313
|
const apiOptions = {};
|
|
4765
5314
|
const history = state.modules.get("history");
|
|
4766
5315
|
if (history !== void 0) {
|
|
@@ -4778,20 +5327,17 @@ function resolveState(key, registry) {
|
|
|
4778
5327
|
if (key !== void 0) {
|
|
4779
5328
|
const stored = registry.forms.get(key);
|
|
4780
5329
|
if (stored === void 0) {
|
|
4781
|
-
warnMiss(`no form registered for key '${key}'`, registry.ssr);
|
|
5330
|
+
warnMiss$1(`no form registered for key '${key}'`, registry.ssr);
|
|
4782
5331
|
return null;
|
|
4783
5332
|
}
|
|
4784
5333
|
return stored;
|
|
4785
5334
|
}
|
|
4786
5335
|
const ambient = inject(kFormContext, null);
|
|
4787
|
-
if (ambient === null)
|
|
4788
|
-
warnMiss("no ambient form context", registry.ssr);
|
|
4789
|
-
return null;
|
|
4790
|
-
}
|
|
5336
|
+
if (ambient === null) return null;
|
|
4791
5337
|
warnIfAmbientProviderHadDuplicates();
|
|
4792
5338
|
return ambient;
|
|
4793
5339
|
}
|
|
4794
|
-
function warnMiss(detail, ssr) {
|
|
5340
|
+
function warnMiss$1(detail, ssr) {
|
|
4795
5341
|
if (!__DEV__ || ssr) return;
|
|
4796
5342
|
const frame = captureUserCallSite();
|
|
4797
5343
|
console.warn(
|
|
@@ -4816,5 +5362,1044 @@ function warnIfAmbientProviderHadDuplicates() {
|
|
|
4816
5362
|
}
|
|
4817
5363
|
}
|
|
4818
5364
|
|
|
4819
|
-
|
|
4820
|
-
|
|
5365
|
+
const LAZY_BRAND = Symbol.for("attaform/wizard-lazy");
|
|
5366
|
+
function lazy(resolve) {
|
|
5367
|
+
return { [LAZY_BRAND]: true, resolve };
|
|
5368
|
+
}
|
|
5369
|
+
function isLazyMarker(value) {
|
|
5370
|
+
return typeof value === "object" && value !== null && LAZY_BRAND in value;
|
|
5371
|
+
}
|
|
5372
|
+
|
|
5373
|
+
const NOOP_WIZARD_HISTORY = {
|
|
5374
|
+
push() {
|
|
5375
|
+
},
|
|
5376
|
+
replace() {
|
|
5377
|
+
},
|
|
5378
|
+
read() {
|
|
5379
|
+
return void 0;
|
|
5380
|
+
},
|
|
5381
|
+
subscribe() {
|
|
5382
|
+
},
|
|
5383
|
+
dispose() {
|
|
5384
|
+
}
|
|
5385
|
+
};
|
|
5386
|
+
function createWizardHistory(param) {
|
|
5387
|
+
if (typeof window === "undefined") return NOOP_WIZARD_HISTORY;
|
|
5388
|
+
const subscribers = [];
|
|
5389
|
+
let disposed = false;
|
|
5390
|
+
function buildUrl(key) {
|
|
5391
|
+
const url = new URL(window.location.href);
|
|
5392
|
+
url.searchParams.set(param, key);
|
|
5393
|
+
return url.toString();
|
|
5394
|
+
}
|
|
5395
|
+
function handlePopstate() {
|
|
5396
|
+
if (disposed) return;
|
|
5397
|
+
const url = new URL(window.location.href);
|
|
5398
|
+
const value = url.searchParams.get(param) ?? void 0;
|
|
5399
|
+
for (const subscriber of subscribers) subscriber(value);
|
|
5400
|
+
}
|
|
5401
|
+
window.addEventListener("popstate", handlePopstate);
|
|
5402
|
+
function safeWriteState(key, op) {
|
|
5403
|
+
try {
|
|
5404
|
+
const fn = op === "push" ? window.history.pushState : window.history.replaceState;
|
|
5405
|
+
fn.call(window.history, {}, "", buildUrl(key));
|
|
5406
|
+
} catch {
|
|
5407
|
+
}
|
|
5408
|
+
}
|
|
5409
|
+
return {
|
|
5410
|
+
push(key) {
|
|
5411
|
+
if (disposed) return;
|
|
5412
|
+
safeWriteState(key, "push");
|
|
5413
|
+
},
|
|
5414
|
+
replace(key) {
|
|
5415
|
+
if (disposed) return;
|
|
5416
|
+
safeWriteState(key, "replace");
|
|
5417
|
+
},
|
|
5418
|
+
read() {
|
|
5419
|
+
const url = new URL(window.location.href);
|
|
5420
|
+
return url.searchParams.get(param) ?? void 0;
|
|
5421
|
+
},
|
|
5422
|
+
subscribe(callback) {
|
|
5423
|
+
if (disposed) return;
|
|
5424
|
+
subscribers.push(callback);
|
|
5425
|
+
},
|
|
5426
|
+
dispose() {
|
|
5427
|
+
if (disposed) return;
|
|
5428
|
+
disposed = true;
|
|
5429
|
+
subscribers.length = 0;
|
|
5430
|
+
window.removeEventListener("popstate", handlePopstate);
|
|
5431
|
+
}
|
|
5432
|
+
};
|
|
5433
|
+
}
|
|
5434
|
+
|
|
5435
|
+
const NOOP_FINGERPRINT = "attaform:wizard-noop";
|
|
5436
|
+
const EMPTY_SLIM_KINDS = /* @__PURE__ */ new Set();
|
|
5437
|
+
function buildNoopWizardSchema(formKey) {
|
|
5438
|
+
const emptyValue = {};
|
|
5439
|
+
const success = {
|
|
5440
|
+
success: true,
|
|
5441
|
+
data: emptyValue,
|
|
5442
|
+
errors: void 0,
|
|
5443
|
+
formKey
|
|
5444
|
+
};
|
|
5445
|
+
const defaultsResponse = {
|
|
5446
|
+
success: true,
|
|
5447
|
+
data: emptyValue,
|
|
5448
|
+
errors: void 0,
|
|
5449
|
+
formKey
|
|
5450
|
+
};
|
|
5451
|
+
return {
|
|
5452
|
+
fingerprint: () => NOOP_FINGERPRINT,
|
|
5453
|
+
getDefaultValues: () => defaultsResponse,
|
|
5454
|
+
getDefaultAtPath: () => void 0,
|
|
5455
|
+
getEmptyValueAtPath: () => void 0,
|
|
5456
|
+
isPreprocessOrCoerceLeaf: () => false,
|
|
5457
|
+
arrayShapeAtPath: () => void 0,
|
|
5458
|
+
getSchemasAtPath: () => [],
|
|
5459
|
+
validateAtPath: () => success,
|
|
5460
|
+
getSlimPrimitiveTypesAtPath: () => new Set(EMPTY_SLIM_KINDS),
|
|
5461
|
+
isLeafAtPath: () => false,
|
|
5462
|
+
isRequiredAtPath: () => false,
|
|
5463
|
+
getUnionDiscriminatorAtPath: () => void 0
|
|
5464
|
+
};
|
|
5465
|
+
}
|
|
5466
|
+
|
|
5467
|
+
function buildWizardStatusesProxy(statuses) {
|
|
5468
|
+
const snapshot = computed(() => {
|
|
5469
|
+
const result = {};
|
|
5470
|
+
for (const key of Object.keys(statuses)) {
|
|
5471
|
+
result[key] = statuses[key].value;
|
|
5472
|
+
}
|
|
5473
|
+
return result;
|
|
5474
|
+
});
|
|
5475
|
+
const target = (() => {
|
|
5476
|
+
});
|
|
5477
|
+
const proxyToString = () => JSON.stringify(snapshot.value);
|
|
5478
|
+
const proxyToPrimitive = (hint) => hint === "number" ? NaN : proxyToString();
|
|
5479
|
+
return new Proxy(target, {
|
|
5480
|
+
apply(_, __, args) {
|
|
5481
|
+
const key = args[0];
|
|
5482
|
+
if (key === void 0) return snapshot.value;
|
|
5483
|
+
const computedEntry = statuses[key];
|
|
5484
|
+
if (computedEntry === void 0) return void 0;
|
|
5485
|
+
return computedEntry.value;
|
|
5486
|
+
},
|
|
5487
|
+
get(_, key) {
|
|
5488
|
+
if (typeof key === "symbol") {
|
|
5489
|
+
if (key === Symbol.toPrimitive) return proxyToPrimitive;
|
|
5490
|
+
return Reflect.get(target, key);
|
|
5491
|
+
}
|
|
5492
|
+
if (key === "toJSON") return () => snapshot.value;
|
|
5493
|
+
if (key === "toString") return proxyToString;
|
|
5494
|
+
if (key === "valueOf")
|
|
5495
|
+
return function() {
|
|
5496
|
+
return this;
|
|
5497
|
+
};
|
|
5498
|
+
const computedEntry = statuses[key];
|
|
5499
|
+
if (computedEntry === void 0) return void 0;
|
|
5500
|
+
return computedEntry.value;
|
|
5501
|
+
},
|
|
5502
|
+
has(_, key) {
|
|
5503
|
+
if (typeof key === "symbol") return Reflect.has(target, key);
|
|
5504
|
+
return Object.hasOwn(statuses, key);
|
|
5505
|
+
},
|
|
5506
|
+
ownKeys() {
|
|
5507
|
+
return Object.keys(statuses);
|
|
5508
|
+
},
|
|
5509
|
+
getOwnPropertyDescriptor(_, key) {
|
|
5510
|
+
if (typeof key === "symbol") return void 0;
|
|
5511
|
+
const computedEntry = statuses[key];
|
|
5512
|
+
if (computedEntry === void 0) return void 0;
|
|
5513
|
+
return {
|
|
5514
|
+
configurable: true,
|
|
5515
|
+
enumerable: true,
|
|
5516
|
+
writable: false,
|
|
5517
|
+
value: computedEntry.value
|
|
5518
|
+
};
|
|
5519
|
+
},
|
|
5520
|
+
set(_, key) {
|
|
5521
|
+
if (__DEV__) {
|
|
5522
|
+
console.warn(
|
|
5523
|
+
`[attaform] wizard.statuses is read-only \u2014 write to "${String(key)}" was ignored. Statuses derive from each form's meta; mutate the underlying form instead.`
|
|
5524
|
+
);
|
|
5525
|
+
}
|
|
5526
|
+
return true;
|
|
5527
|
+
},
|
|
5528
|
+
deleteProperty(_, key) {
|
|
5529
|
+
if (__DEV__) {
|
|
5530
|
+
console.warn(
|
|
5531
|
+
`[attaform] wizard.statuses is read-only \u2014 delete of "${String(key)}" was ignored.`
|
|
5532
|
+
);
|
|
5533
|
+
}
|
|
5534
|
+
return true;
|
|
5535
|
+
},
|
|
5536
|
+
defineProperty: () => true
|
|
5537
|
+
});
|
|
5538
|
+
}
|
|
5539
|
+
|
|
5540
|
+
const DEFAULT_STEP_PARAM = "step";
|
|
5541
|
+
const PENDING_STATUS = {
|
|
5542
|
+
valid: false,
|
|
5543
|
+
dirty: false,
|
|
5544
|
+
submitted: false,
|
|
5545
|
+
errorCount: 0
|
|
5546
|
+
};
|
|
5547
|
+
const NOOP_VALID_STATUS = {
|
|
5548
|
+
valid: true,
|
|
5549
|
+
dirty: false,
|
|
5550
|
+
submitted: false,
|
|
5551
|
+
errorCount: 0
|
|
5552
|
+
};
|
|
5553
|
+
function useWizard(options) {
|
|
5554
|
+
const rawSteps = Array.isArray(options.steps) ? options.steps : [];
|
|
5555
|
+
if (rawSteps.length === 0 && __DEV__) {
|
|
5556
|
+
console.error(
|
|
5557
|
+
"[attaform] useWizard({ steps }): expected a non-empty array of step slots. Continuing with an empty step list \u2014 wizard.currentStep reads as undefined, navigation refuses, handleSubmit no-ops."
|
|
5558
|
+
);
|
|
5559
|
+
}
|
|
5560
|
+
const registry = useRegistry();
|
|
5561
|
+
const noopForms = /* @__PURE__ */ new Map();
|
|
5562
|
+
const lazyNoopScope = effectScope(true);
|
|
5563
|
+
for (const slot of rawSteps) {
|
|
5564
|
+
if (typeof slot !== "string") continue;
|
|
5565
|
+
if (noopForms.has(slot)) continue;
|
|
5566
|
+
const noop = useAbstractForm({
|
|
5567
|
+
schema: buildNoopWizardSchema(slot),
|
|
5568
|
+
key: slot
|
|
5569
|
+
});
|
|
5570
|
+
noopForms.set(slot, noop);
|
|
5571
|
+
}
|
|
5572
|
+
function getOrBuildNoop(key) {
|
|
5573
|
+
const existing2 = noopForms.get(key);
|
|
5574
|
+
if (existing2 !== void 0) return existing2;
|
|
5575
|
+
const noop = lazyNoopScope.run(
|
|
5576
|
+
() => useAbstractForm(
|
|
5577
|
+
{
|
|
5578
|
+
schema: buildNoopWizardSchema(key),
|
|
5579
|
+
key
|
|
5580
|
+
},
|
|
5581
|
+
{ registry }
|
|
5582
|
+
)
|
|
5583
|
+
);
|
|
5584
|
+
if (noop === void 0) {
|
|
5585
|
+
const stub = { key };
|
|
5586
|
+
return stub;
|
|
5587
|
+
}
|
|
5588
|
+
noopForms.set(key, noop);
|
|
5589
|
+
formsAccumulator.set(key, noop);
|
|
5590
|
+
return noop;
|
|
5591
|
+
}
|
|
5592
|
+
const trackedKeys = /* @__PURE__ */ new Set();
|
|
5593
|
+
function trackOnce(form) {
|
|
5594
|
+
if (trackedKeys.has(form.key)) return;
|
|
5595
|
+
trackedKeys.add(form.key);
|
|
5596
|
+
if (getCurrentScope() !== void 0) {
|
|
5597
|
+
const release = registry.trackConsumer(form.key);
|
|
5598
|
+
onScopeDispose(release);
|
|
5599
|
+
}
|
|
5600
|
+
}
|
|
5601
|
+
for (const slot of rawSteps) {
|
|
5602
|
+
if (typeof slot === "string") {
|
|
5603
|
+
const noop = noopForms.get(slot);
|
|
5604
|
+
if (noop !== void 0) trackOnce(noop);
|
|
5605
|
+
} else if (isAnyForm(slot)) {
|
|
5606
|
+
trackOnce(slot);
|
|
5607
|
+
}
|
|
5608
|
+
}
|
|
5609
|
+
const lazyEpoch = ref(0);
|
|
5610
|
+
const lazyComputeds = /* @__PURE__ */ new Map();
|
|
5611
|
+
const activeKey = ref("");
|
|
5612
|
+
const formsAccumulator = /* @__PURE__ */ new Map();
|
|
5613
|
+
for (const slot of rawSteps) {
|
|
5614
|
+
if (typeof slot === "string") {
|
|
5615
|
+
const noop = noopForms.get(slot);
|
|
5616
|
+
if (noop !== void 0) formsAccumulator.set(noop.key, noop);
|
|
5617
|
+
} else if (isAnyForm(slot)) {
|
|
5618
|
+
formsAccumulator.set(slot.key, slot);
|
|
5619
|
+
}
|
|
5620
|
+
}
|
|
5621
|
+
const slotForms = new Proxy({}, {
|
|
5622
|
+
get(_, key) {
|
|
5623
|
+
if (typeof key !== "string") return void 0;
|
|
5624
|
+
return formsAccumulator.get(key);
|
|
5625
|
+
},
|
|
5626
|
+
has(_, key) {
|
|
5627
|
+
if (typeof key !== "string") return false;
|
|
5628
|
+
return formsAccumulator.has(key);
|
|
5629
|
+
},
|
|
5630
|
+
ownKeys() {
|
|
5631
|
+
return [...formsAccumulator.keys()];
|
|
5632
|
+
},
|
|
5633
|
+
getOwnPropertyDescriptor(_, key) {
|
|
5634
|
+
if (typeof key !== "string") return void 0;
|
|
5635
|
+
const form = formsAccumulator.get(key);
|
|
5636
|
+
if (form === void 0) return void 0;
|
|
5637
|
+
return { configurable: true, enumerable: true, writable: false, value: form };
|
|
5638
|
+
}
|
|
5639
|
+
});
|
|
5640
|
+
const slotCtx = computed(() => ({
|
|
5641
|
+
forms: slotForms,
|
|
5642
|
+
currentKey: activeKey.value === "" ? void 0 : activeKey.value
|
|
5643
|
+
}));
|
|
5644
|
+
function resolveSlot(slot, index, ctx) {
|
|
5645
|
+
if (typeof slot === "string") {
|
|
5646
|
+
return getOrBuildNoop(slot);
|
|
5647
|
+
}
|
|
5648
|
+
if (isLazyMarker(slot)) {
|
|
5649
|
+
const c = lazyComputeds.get(index);
|
|
5650
|
+
return c === void 0 ? void 0 : resolveSlotResult(c.value);
|
|
5651
|
+
}
|
|
5652
|
+
if (typeof slot === "function") {
|
|
5653
|
+
const result = slot(ctx);
|
|
5654
|
+
return resolveSlotResult(result);
|
|
5655
|
+
}
|
|
5656
|
+
if (isAnyForm(slot)) return slot;
|
|
5657
|
+
return void 0;
|
|
5658
|
+
}
|
|
5659
|
+
function resolveSlotResult(result) {
|
|
5660
|
+
if (result === void 0) return void 0;
|
|
5661
|
+
if (typeof result === "string") {
|
|
5662
|
+
return getOrBuildNoop(result);
|
|
5663
|
+
}
|
|
5664
|
+
return result;
|
|
5665
|
+
}
|
|
5666
|
+
const lazyCtx = {
|
|
5667
|
+
forms: slotForms,
|
|
5668
|
+
get currentKey() {
|
|
5669
|
+
return activeKey.value === "" ? void 0 : activeKey.value;
|
|
5670
|
+
}
|
|
5671
|
+
};
|
|
5672
|
+
for (let i = 0; i < rawSteps.length; i++) {
|
|
5673
|
+
const slot = rawSteps[i];
|
|
5674
|
+
if (isLazyMarker(slot)) {
|
|
5675
|
+
const idx = i;
|
|
5676
|
+
const marker = slot;
|
|
5677
|
+
lazyComputeds.set(
|
|
5678
|
+
idx,
|
|
5679
|
+
computed(() => {
|
|
5680
|
+
void lazyEpoch.value;
|
|
5681
|
+
return marker.resolve(lazyCtx);
|
|
5682
|
+
})
|
|
5683
|
+
);
|
|
5684
|
+
}
|
|
5685
|
+
}
|
|
5686
|
+
const compiledSteps = computed(() => {
|
|
5687
|
+
const ctx = slotCtx.value;
|
|
5688
|
+
const out = [];
|
|
5689
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5690
|
+
for (let i = 0; i < rawSteps.length; i++) {
|
|
5691
|
+
const slot = rawSteps[i];
|
|
5692
|
+
const form = resolveSlot(slot, i, ctx);
|
|
5693
|
+
if (form === void 0) continue;
|
|
5694
|
+
if (seen.has(form.key)) {
|
|
5695
|
+
if (__DEV__) {
|
|
5696
|
+
console.warn(
|
|
5697
|
+
`[attaform] useWizard: step "${form.key}" appears in more than one slot. The wizard treats the first occurrence as canonical and drops later duplicates.`
|
|
5698
|
+
);
|
|
5699
|
+
}
|
|
5700
|
+
continue;
|
|
5701
|
+
}
|
|
5702
|
+
seen.add(form.key);
|
|
5703
|
+
trackOnce(form);
|
|
5704
|
+
out.push({ key: form.key, form });
|
|
5705
|
+
}
|
|
5706
|
+
return out;
|
|
5707
|
+
});
|
|
5708
|
+
const activeIndex = computed(() => {
|
|
5709
|
+
const key = activeKey.value;
|
|
5710
|
+
if (key === "") return -1;
|
|
5711
|
+
const list = compiledSteps.value;
|
|
5712
|
+
for (let i = 0; i < list.length; i++) {
|
|
5713
|
+
if (list[i].key === key) return i;
|
|
5714
|
+
}
|
|
5715
|
+
return -1;
|
|
5716
|
+
});
|
|
5717
|
+
const currentStep = computed(() => {
|
|
5718
|
+
const key = activeKey.value;
|
|
5719
|
+
if (key !== "") return key;
|
|
5720
|
+
const first = compiledSteps.value[0];
|
|
5721
|
+
return first === void 0 ? void 0 : first.key;
|
|
5722
|
+
});
|
|
5723
|
+
const activeForm = computed(() => {
|
|
5724
|
+
const list = compiledSteps.value;
|
|
5725
|
+
const idx = activeIndex.value;
|
|
5726
|
+
if (idx >= 0 && idx < list.length) {
|
|
5727
|
+
return list[idx].form;
|
|
5728
|
+
}
|
|
5729
|
+
const first = list[0];
|
|
5730
|
+
return first === void 0 ? void 0 : first.form;
|
|
5731
|
+
});
|
|
5732
|
+
const isFinalStep = computed(() => {
|
|
5733
|
+
const list = compiledSteps.value;
|
|
5734
|
+
const idx = activeIndex.value;
|
|
5735
|
+
return list.length > 0 && idx === list.length - 1;
|
|
5736
|
+
});
|
|
5737
|
+
const count = computed(() => compiledSteps.value.length);
|
|
5738
|
+
const formsRecord = computed(() => {
|
|
5739
|
+
const out = {};
|
|
5740
|
+
for (const step of compiledSteps.value) out[step.key] = step.form;
|
|
5741
|
+
return out;
|
|
5742
|
+
});
|
|
5743
|
+
const allValues = computed(() => {
|
|
5744
|
+
const out = {};
|
|
5745
|
+
for (const step of compiledSteps.value) {
|
|
5746
|
+
const source = step.form;
|
|
5747
|
+
out[step.key] = source.values;
|
|
5748
|
+
}
|
|
5749
|
+
return out;
|
|
5750
|
+
});
|
|
5751
|
+
const allErrors = computed(() => {
|
|
5752
|
+
const out = {};
|
|
5753
|
+
for (const step of compiledSteps.value) {
|
|
5754
|
+
const source = step.form;
|
|
5755
|
+
const list = [];
|
|
5756
|
+
const store = registry.forms.get(step.key);
|
|
5757
|
+
const resolved = store?.defaultsResolved.value === true;
|
|
5758
|
+
if (resolved) {
|
|
5759
|
+
const errors = source.meta?.errors ?? [];
|
|
5760
|
+
for (const err of errors) {
|
|
5761
|
+
const entry = {
|
|
5762
|
+
formKey: step.key,
|
|
5763
|
+
path: err.path,
|
|
5764
|
+
message: err.message
|
|
5765
|
+
};
|
|
5766
|
+
if (err.code !== void 0) entry.code = err.code;
|
|
5767
|
+
list.push(entry);
|
|
5768
|
+
}
|
|
5769
|
+
}
|
|
5770
|
+
out[step.key] = list;
|
|
5771
|
+
}
|
|
5772
|
+
return out;
|
|
5773
|
+
});
|
|
5774
|
+
const seedRef = ref(void 0);
|
|
5775
|
+
const seedInput = options.defaultStatuses;
|
|
5776
|
+
if (seedInput !== void 0) {
|
|
5777
|
+
const resolved = resolveTrichotomy(seedInput);
|
|
5778
|
+
if (resolved.kind === "sync") {
|
|
5779
|
+
seedRef.value = resolved.value;
|
|
5780
|
+
} else {
|
|
5781
|
+
const eager = resolved.factory();
|
|
5782
|
+
if (eager instanceof Promise) {
|
|
5783
|
+
void eager.then((value) => {
|
|
5784
|
+
seedRef.value = value;
|
|
5785
|
+
});
|
|
5786
|
+
} else {
|
|
5787
|
+
seedRef.value = eager;
|
|
5788
|
+
}
|
|
5789
|
+
}
|
|
5790
|
+
}
|
|
5791
|
+
const statusCache = /* @__PURE__ */ new Map();
|
|
5792
|
+
function statusFor(form) {
|
|
5793
|
+
const cached = statusCache.get(form.key);
|
|
5794
|
+
if (cached !== void 0) return cached;
|
|
5795
|
+
const source = form;
|
|
5796
|
+
const computedStatus = computed(() => {
|
|
5797
|
+
const store = registry.forms.get(form.key);
|
|
5798
|
+
const resolved = store?.defaultsResolved.value === true;
|
|
5799
|
+
if (resolved) {
|
|
5800
|
+
const meta = source.meta;
|
|
5801
|
+
if (meta !== void 0 && meta !== null) {
|
|
5802
|
+
return {
|
|
5803
|
+
valid: meta.valid,
|
|
5804
|
+
dirty: meta.dirty,
|
|
5805
|
+
submitted: meta.submitted,
|
|
5806
|
+
errorCount: meta.errorCount
|
|
5807
|
+
};
|
|
5808
|
+
}
|
|
5809
|
+
}
|
|
5810
|
+
if (noopForms.has(form.key)) return NOOP_VALID_STATUS;
|
|
5811
|
+
const seedMap = seedRef.value;
|
|
5812
|
+
if (seedMap !== void 0 && Object.hasOwn(seedMap, form.key)) {
|
|
5813
|
+
return seedMap[form.key];
|
|
5814
|
+
}
|
|
5815
|
+
return PENDING_STATUS;
|
|
5816
|
+
});
|
|
5817
|
+
statusCache.set(form.key, computedStatus);
|
|
5818
|
+
return computedStatus;
|
|
5819
|
+
}
|
|
5820
|
+
const statusesRecord = new Proxy({}, {
|
|
5821
|
+
get(_, key) {
|
|
5822
|
+
if (typeof key !== "string") return void 0;
|
|
5823
|
+
const form = formsRecord.value[key];
|
|
5824
|
+
if (form === void 0) {
|
|
5825
|
+
const cached = statusCache.get(key);
|
|
5826
|
+
if (cached !== void 0) return cached;
|
|
5827
|
+
return void 0;
|
|
5828
|
+
}
|
|
5829
|
+
return statusFor(form);
|
|
5830
|
+
},
|
|
5831
|
+
ownKeys() {
|
|
5832
|
+
return Object.keys(formsRecord.value);
|
|
5833
|
+
},
|
|
5834
|
+
has(_, key) {
|
|
5835
|
+
if (typeof key !== "string") return false;
|
|
5836
|
+
return formsRecord.value[key] !== void 0;
|
|
5837
|
+
},
|
|
5838
|
+
getOwnPropertyDescriptor(_, key) {
|
|
5839
|
+
if (typeof key !== "string") return void 0;
|
|
5840
|
+
const form = formsRecord.value[key];
|
|
5841
|
+
if (form === void 0) return void 0;
|
|
5842
|
+
return {
|
|
5843
|
+
configurable: true,
|
|
5844
|
+
enumerable: true,
|
|
5845
|
+
writable: false,
|
|
5846
|
+
value: statusFor(form)
|
|
5847
|
+
};
|
|
5848
|
+
}
|
|
5849
|
+
});
|
|
5850
|
+
const statuses = buildWizardStatusesProxy(statusesRecord);
|
|
5851
|
+
if (__DEV__ && seedRef.value !== void 0) {
|
|
5852
|
+
const seedMap = seedRef.value;
|
|
5853
|
+
const known = new Set(compiledSteps.value.map((s) => s.key));
|
|
5854
|
+
const unknown = [];
|
|
5855
|
+
for (const key of Object.keys(seedMap)) {
|
|
5856
|
+
if (!known.has(key)) unknown.push(key);
|
|
5857
|
+
}
|
|
5858
|
+
if (unknown.length > 0) {
|
|
5859
|
+
console.warn(
|
|
5860
|
+
`[attaform] useWizard.defaultStatuses: seed contains unknown key(s) ${unknown.map((k) => `"${k}"`).join(", ")}. Known step keys: ${[...known].map((k) => `"${k}"`).join(", ")}.`
|
|
5861
|
+
);
|
|
5862
|
+
}
|
|
5863
|
+
}
|
|
5864
|
+
const progressOverride = options.progress;
|
|
5865
|
+
const progress = computed(() => {
|
|
5866
|
+
if (progressOverride !== void 0) {
|
|
5867
|
+
return progressOverride(compiledSteps.value);
|
|
5868
|
+
}
|
|
5869
|
+
const list = compiledSteps.value;
|
|
5870
|
+
if (list.length === 0) return 0;
|
|
5871
|
+
let valid = 0;
|
|
5872
|
+
for (const step of list) {
|
|
5873
|
+
const status = statusFor(step.form).value;
|
|
5874
|
+
if (status.valid === true) valid += 1;
|
|
5875
|
+
}
|
|
5876
|
+
return valid / list.length;
|
|
5877
|
+
});
|
|
5878
|
+
const complete = computed(() => {
|
|
5879
|
+
if (!isFinalStep.value) return false;
|
|
5880
|
+
for (const step of compiledSteps.value) {
|
|
5881
|
+
if (statusFor(step.form).value.valid !== true) return false;
|
|
5882
|
+
}
|
|
5883
|
+
return true;
|
|
5884
|
+
});
|
|
5885
|
+
const canAdvance = computed(() => activeIndex.value < count.value - 1);
|
|
5886
|
+
const canGoBack = computed(() => activeIndex.value > 0);
|
|
5887
|
+
const visited = ref([]);
|
|
5888
|
+
const wantsDefaultUrlSync = options.restore !== false || options.persist !== false;
|
|
5889
|
+
const historyHandle = wantsDefaultUrlSync ? createWizardHistory(DEFAULT_STEP_PARAM) : NOOP_WIZARD_HISTORY;
|
|
5890
|
+
const injectedResolver = inject(kAttaformWizardActiveStepResolver, null);
|
|
5891
|
+
const urlMirror = ref(void 0);
|
|
5892
|
+
const initialUrlValue = injectedResolver !== null ? injectedResolver(DEFAULT_STEP_PARAM) : historyHandle.read();
|
|
5893
|
+
urlMirror.value = initialUrlValue;
|
|
5894
|
+
historyHandle.subscribe((value) => {
|
|
5895
|
+
urlMirror.value = value;
|
|
5896
|
+
});
|
|
5897
|
+
const restoreCallback = options.restore === false ? void 0 : options.restore !== void 0 ? options.restore : () => {
|
|
5898
|
+
const value = urlMirror.value;
|
|
5899
|
+
return value === void 0 ? void 0 : { step: value };
|
|
5900
|
+
};
|
|
5901
|
+
const persistCallback = options.persist === false ? void 0 : options.persist !== void 0 ? options.persist : (state) => {
|
|
5902
|
+
if (state.step === void 0) return;
|
|
5903
|
+
historyHandle.replace(state.step);
|
|
5904
|
+
};
|
|
5905
|
+
function isCompiledKey(key) {
|
|
5906
|
+
const list = compiledSteps.value;
|
|
5907
|
+
for (const step of list) if (step.key === key) return true;
|
|
5908
|
+
return false;
|
|
5909
|
+
}
|
|
5910
|
+
function firstKey() {
|
|
5911
|
+
const first = compiledSteps.value[0];
|
|
5912
|
+
return first === void 0 ? void 0 : first.key;
|
|
5913
|
+
}
|
|
5914
|
+
let initialKey;
|
|
5915
|
+
const restoredAtSetup = restoreCallback?.();
|
|
5916
|
+
const restoredStep = restoredAtSetup?.step;
|
|
5917
|
+
if (restoredStep !== void 0 && isCompiledKey(restoredStep)) {
|
|
5918
|
+
initialKey = restoredStep;
|
|
5919
|
+
} else {
|
|
5920
|
+
if (__DEV__ && restoredStep !== void 0 && restoredStep !== "" && !isCompiledKey(restoredStep)) {
|
|
5921
|
+
console.warn(
|
|
5922
|
+
`[attaform] useWizard: restore() yielded step "${restoredStep}" which is not in the compiled step list. Falling back to the first step.`
|
|
5923
|
+
);
|
|
5924
|
+
}
|
|
5925
|
+
initialKey = firstKey();
|
|
5926
|
+
}
|
|
5927
|
+
if (initialKey !== void 0) {
|
|
5928
|
+
activeKey.value = initialKey;
|
|
5929
|
+
visited.value = [initialKey];
|
|
5930
|
+
}
|
|
5931
|
+
if (registry.ssr) {
|
|
5932
|
+
for (const step of compiledSteps.value) {
|
|
5933
|
+
if (step.key === initialKey) {
|
|
5934
|
+
registry.enqueuePrefetch(step.key);
|
|
5935
|
+
} else {
|
|
5936
|
+
registry.skipPrefetch(step.key);
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
}
|
|
5940
|
+
if (!registry.ssr) {
|
|
5941
|
+
for (const step of compiledSteps.value) {
|
|
5942
|
+
const source = step.form;
|
|
5943
|
+
if (typeof source.activate === "function") void source.activate();
|
|
5944
|
+
}
|
|
5945
|
+
}
|
|
5946
|
+
let lastPersisted = initialUrlValue;
|
|
5947
|
+
if (restoreCallback !== void 0) {
|
|
5948
|
+
watch(
|
|
5949
|
+
() => restoreCallback()?.step,
|
|
5950
|
+
(step) => {
|
|
5951
|
+
if (step === void 0) return;
|
|
5952
|
+
if (!isCompiledKey(step)) {
|
|
5953
|
+
if (__DEV__) {
|
|
5954
|
+
console.warn(
|
|
5955
|
+
`[attaform] useWizard: restore() yielded step "${step}" which is not in the compiled step list. Ignoring.`
|
|
5956
|
+
);
|
|
5957
|
+
}
|
|
5958
|
+
return;
|
|
5959
|
+
}
|
|
5960
|
+
if (step === activeKey.value) return;
|
|
5961
|
+
activeKey.value = step;
|
|
5962
|
+
if (!visited.value.includes(step)) visited.value.push(step);
|
|
5963
|
+
}
|
|
5964
|
+
);
|
|
5965
|
+
}
|
|
5966
|
+
if (persistCallback !== void 0) {
|
|
5967
|
+
watch(
|
|
5968
|
+
() => activeKey.value,
|
|
5969
|
+
(next2) => {
|
|
5970
|
+
if (next2 === lastPersisted) return;
|
|
5971
|
+
lastPersisted = next2;
|
|
5972
|
+
persistCallback({ step: next2 });
|
|
5973
|
+
urlMirror.value = next2;
|
|
5974
|
+
}
|
|
5975
|
+
);
|
|
5976
|
+
if (initialKey !== void 0 && initialKey !== initialUrlValue && initialUrlValue === void 0) {
|
|
5977
|
+
lastPersisted = initialKey;
|
|
5978
|
+
persistCallback({ step: initialKey });
|
|
5979
|
+
urlMirror.value = initialKey;
|
|
5980
|
+
}
|
|
5981
|
+
}
|
|
5982
|
+
const submitting = ref(false);
|
|
5983
|
+
const submissionAttempts = ref(0);
|
|
5984
|
+
const done = ref(false);
|
|
5985
|
+
function activateForm(form) {
|
|
5986
|
+
const source = form;
|
|
5987
|
+
if (typeof source.activate === "function") {
|
|
5988
|
+
void source.activate();
|
|
5989
|
+
}
|
|
5990
|
+
}
|
|
5991
|
+
function moveTo(key, options2) {
|
|
5992
|
+
if (activeKey.value === key) return;
|
|
5993
|
+
activeKey.value = key;
|
|
5994
|
+
if (!visited.value.includes(key)) visited.value.push(key);
|
|
5995
|
+
const list = compiledSteps.value;
|
|
5996
|
+
for (const step of list) {
|
|
5997
|
+
if (step.key === key) {
|
|
5998
|
+
activateForm(step.form);
|
|
5999
|
+
return;
|
|
6000
|
+
}
|
|
6001
|
+
}
|
|
6002
|
+
}
|
|
6003
|
+
function recordDeparture(key) {
|
|
6004
|
+
const store = registry.forms.get(key);
|
|
6005
|
+
if (store !== void 0) store.departAttempts.value += 1;
|
|
6006
|
+
}
|
|
6007
|
+
async function next() {
|
|
6008
|
+
if (submitting.value) {
|
|
6009
|
+
if (__DEV__) {
|
|
6010
|
+
console.warn(
|
|
6011
|
+
`[attaform] wizard.next(): blocked while a submit is in flight. Wait for handleSubmit to settle.`
|
|
6012
|
+
);
|
|
6013
|
+
}
|
|
6014
|
+
return;
|
|
6015
|
+
}
|
|
6016
|
+
const list = compiledSteps.value;
|
|
6017
|
+
if (list.length === 0) {
|
|
6018
|
+
if (__DEV__) {
|
|
6019
|
+
console.warn(`[attaform] wizard.next(): wizard has no compiled steps; no-op.`);
|
|
6020
|
+
}
|
|
6021
|
+
return;
|
|
6022
|
+
}
|
|
6023
|
+
const idx = activeIndex.value;
|
|
6024
|
+
if (idx < 0 || idx >= list.length - 1) {
|
|
6025
|
+
if (__DEV__) {
|
|
6026
|
+
console.warn(
|
|
6027
|
+
`[attaform] wizard.next(): already on the final step ("${activeKey.value}"). Use wizard.handleSubmit() to submit.`
|
|
6028
|
+
);
|
|
6029
|
+
}
|
|
6030
|
+
return;
|
|
6031
|
+
}
|
|
6032
|
+
recordDeparture(activeKey.value);
|
|
6033
|
+
const target = list[idx + 1];
|
|
6034
|
+
moveTo(target.key);
|
|
6035
|
+
}
|
|
6036
|
+
function back() {
|
|
6037
|
+
if (submitting.value) {
|
|
6038
|
+
if (__DEV__) {
|
|
6039
|
+
console.warn(`[attaform] wizard.back(): blocked while a submit is in flight.`);
|
|
6040
|
+
}
|
|
6041
|
+
return;
|
|
6042
|
+
}
|
|
6043
|
+
if (compiledSteps.value.length === 0) {
|
|
6044
|
+
if (__DEV__) {
|
|
6045
|
+
console.warn(`[attaform] wizard.back(): wizard has no compiled steps; no-op.`);
|
|
6046
|
+
}
|
|
6047
|
+
return;
|
|
6048
|
+
}
|
|
6049
|
+
const idx = activeIndex.value;
|
|
6050
|
+
if (idx <= 0) {
|
|
6051
|
+
if (__DEV__) {
|
|
6052
|
+
console.warn(`[attaform] wizard.back(): already on the first step ("${activeKey.value}").`);
|
|
6053
|
+
}
|
|
6054
|
+
return;
|
|
6055
|
+
}
|
|
6056
|
+
recordDeparture(activeKey.value);
|
|
6057
|
+
const target = compiledSteps.value[idx - 1];
|
|
6058
|
+
moveTo(target.key);
|
|
6059
|
+
}
|
|
6060
|
+
function goTo(key) {
|
|
6061
|
+
if (submitting.value) {
|
|
6062
|
+
if (__DEV__) {
|
|
6063
|
+
console.warn(`[attaform] wizard.goTo(): blocked while a submit is in flight.`);
|
|
6064
|
+
}
|
|
6065
|
+
return;
|
|
6066
|
+
}
|
|
6067
|
+
if (!isCompiledKey(key)) {
|
|
6068
|
+
if (__DEV__) {
|
|
6069
|
+
const known = compiledSteps.value.map((s) => `"${s.key}"`).join(", ");
|
|
6070
|
+
console.warn(`[attaform] wizard.goTo("${key}"): unknown step key. Known keys: ${known}.`);
|
|
6071
|
+
}
|
|
6072
|
+
return;
|
|
6073
|
+
}
|
|
6074
|
+
if (key !== activeKey.value) recordDeparture(activeKey.value);
|
|
6075
|
+
moveTo(key);
|
|
6076
|
+
}
|
|
6077
|
+
function buildSubmitContext(valuesMap, currentKey, isFinal) {
|
|
6078
|
+
return {
|
|
6079
|
+
values: valuesMap,
|
|
6080
|
+
get: ((form) => valuesMap[form.key]),
|
|
6081
|
+
currentKey,
|
|
6082
|
+
isFinal
|
|
6083
|
+
};
|
|
6084
|
+
}
|
|
6085
|
+
async function processOne(form) {
|
|
6086
|
+
const full = form;
|
|
6087
|
+
let activationFailure;
|
|
6088
|
+
try {
|
|
6089
|
+
if (typeof full.activate === "function") await full.activate();
|
|
6090
|
+
} catch (err) {
|
|
6091
|
+
activationFailure = err?.message ?? String(err);
|
|
6092
|
+
}
|
|
6093
|
+
if (activationFailure === void 0 && full.hydrateError != null) {
|
|
6094
|
+
activationFailure = full.hydrateError.message;
|
|
6095
|
+
}
|
|
6096
|
+
if (activationFailure !== void 0) {
|
|
6097
|
+
return {
|
|
6098
|
+
success: false,
|
|
6099
|
+
data: void 0,
|
|
6100
|
+
errors: [
|
|
6101
|
+
{
|
|
6102
|
+
formKey: form.key,
|
|
6103
|
+
path: [],
|
|
6104
|
+
message: `Form '${form.key}' failed to activate: ${activationFailure}`,
|
|
6105
|
+
code: AttaformErrorCode.ActivationFailed
|
|
6106
|
+
}
|
|
6107
|
+
],
|
|
6108
|
+
formKey: form.key
|
|
6109
|
+
};
|
|
6110
|
+
}
|
|
6111
|
+
return full.process();
|
|
6112
|
+
}
|
|
6113
|
+
function collectErrors(results) {
|
|
6114
|
+
const out = [];
|
|
6115
|
+
for (const step of compiledSteps.value) {
|
|
6116
|
+
const processed = results.get(step.key);
|
|
6117
|
+
if (processed === void 0 || processed.success === true) continue;
|
|
6118
|
+
for (const err of processed.errors) {
|
|
6119
|
+
const entry = {
|
|
6120
|
+
formKey: err.formKey,
|
|
6121
|
+
path: err.path,
|
|
6122
|
+
message: err.message
|
|
6123
|
+
};
|
|
6124
|
+
if (err.code !== void 0) entry.code = err.code;
|
|
6125
|
+
out.push(entry);
|
|
6126
|
+
}
|
|
6127
|
+
}
|
|
6128
|
+
return out;
|
|
6129
|
+
}
|
|
6130
|
+
function handleSubmit(onSubmit, onError) {
|
|
6131
|
+
return async function submitHandler(event) {
|
|
6132
|
+
if (event !== void 0 && typeof event.preventDefault === "function") {
|
|
6133
|
+
event.preventDefault();
|
|
6134
|
+
}
|
|
6135
|
+
if (compiledSteps.value.length === 0) {
|
|
6136
|
+
if (__DEV__) {
|
|
6137
|
+
console.warn(`[attaform] wizard.handleSubmit: wizard has no compiled steps; no-op.`);
|
|
6138
|
+
}
|
|
6139
|
+
return;
|
|
6140
|
+
}
|
|
6141
|
+
if (submitting.value) {
|
|
6142
|
+
if (__DEV__) {
|
|
6143
|
+
console.warn(
|
|
6144
|
+
`[attaform] wizard.handleSubmit: re-entrant submit while a prior call is still in flight; resolving no-op.`
|
|
6145
|
+
);
|
|
6146
|
+
}
|
|
6147
|
+
return;
|
|
6148
|
+
}
|
|
6149
|
+
submitting.value = true;
|
|
6150
|
+
try {
|
|
6151
|
+
const currentKey = activeKey.value;
|
|
6152
|
+
const final = isFinalStep.value;
|
|
6153
|
+
const list = compiledSteps.value;
|
|
6154
|
+
const results = /* @__PURE__ */ new Map();
|
|
6155
|
+
if (final) {
|
|
6156
|
+
await Promise.all(
|
|
6157
|
+
list.map(async (step) => {
|
|
6158
|
+
const result = await processOne(step.form);
|
|
6159
|
+
results.set(step.key, result);
|
|
6160
|
+
})
|
|
6161
|
+
);
|
|
6162
|
+
} else {
|
|
6163
|
+
const active = activeForm.value;
|
|
6164
|
+
const result = await processOne(active);
|
|
6165
|
+
results.set(active.key, result);
|
|
6166
|
+
}
|
|
6167
|
+
for (const key of results.keys()) {
|
|
6168
|
+
const store = registry.forms.get(key);
|
|
6169
|
+
if (store !== void 0) store.submissionAttempts.value += 1;
|
|
6170
|
+
}
|
|
6171
|
+
submissionAttempts.value += 1;
|
|
6172
|
+
const errors = collectErrors(results);
|
|
6173
|
+
if (errors.length === 0) {
|
|
6174
|
+
const valuesMap = {};
|
|
6175
|
+
for (const step of list) {
|
|
6176
|
+
const processed = results.get(step.key);
|
|
6177
|
+
if (processed !== void 0 && processed.success === true) {
|
|
6178
|
+
valuesMap[step.key] = processed.data;
|
|
6179
|
+
} else {
|
|
6180
|
+
const source = step.form;
|
|
6181
|
+
valuesMap[step.key] = source.values;
|
|
6182
|
+
}
|
|
6183
|
+
}
|
|
6184
|
+
const ctx = buildSubmitContext(valuesMap, currentKey, final);
|
|
6185
|
+
await onSubmit(ctx);
|
|
6186
|
+
if (final) {
|
|
6187
|
+
done.value = true;
|
|
6188
|
+
} else {
|
|
6189
|
+
recordDeparture(currentKey);
|
|
6190
|
+
const idx = activeIndex.value;
|
|
6191
|
+
const target = list[idx + 1];
|
|
6192
|
+
if (target !== void 0) moveTo(target.key);
|
|
6193
|
+
}
|
|
6194
|
+
} else {
|
|
6195
|
+
if (onError !== void 0) await onError(errors);
|
|
6196
|
+
if (options.focusFirstError !== false) {
|
|
6197
|
+
const firstFailedKey = errors[0]?.formKey;
|
|
6198
|
+
if (firstFailedKey !== void 0 && isCompiledKey(firstFailedKey)) {
|
|
6199
|
+
moveTo(firstFailedKey);
|
|
6200
|
+
await nextTick();
|
|
6201
|
+
const failedForm = formsRecord.value[firstFailedKey];
|
|
6202
|
+
if (failedForm !== void 0 && typeof failedForm.applyInvalidSubmitPolicy === "function") {
|
|
6203
|
+
failedForm.applyInvalidSubmitPolicy();
|
|
6204
|
+
}
|
|
6205
|
+
}
|
|
6206
|
+
}
|
|
6207
|
+
}
|
|
6208
|
+
} finally {
|
|
6209
|
+
submitting.value = false;
|
|
6210
|
+
}
|
|
6211
|
+
};
|
|
6212
|
+
}
|
|
6213
|
+
function reset() {
|
|
6214
|
+
submissionAttempts.value = 0;
|
|
6215
|
+
done.value = false;
|
|
6216
|
+
lazyEpoch.value += 1;
|
|
6217
|
+
for (const step of compiledSteps.value) {
|
|
6218
|
+
const full = step.form;
|
|
6219
|
+
if (typeof full.reset === "function") full.reset();
|
|
6220
|
+
}
|
|
6221
|
+
const firstStep = compiledSteps.value[0];
|
|
6222
|
+
if (firstStep !== void 0) {
|
|
6223
|
+
activeKey.value = firstStep.key;
|
|
6224
|
+
visited.value = [firstStep.key];
|
|
6225
|
+
if (persistCallback !== void 0) {
|
|
6226
|
+
lastPersisted = firstStep.key;
|
|
6227
|
+
persistCallback({ step: firstStep.key });
|
|
6228
|
+
}
|
|
6229
|
+
}
|
|
6230
|
+
}
|
|
6231
|
+
if (getCurrentScope() !== void 0) {
|
|
6232
|
+
onScopeDispose(() => {
|
|
6233
|
+
historyHandle.dispose();
|
|
6234
|
+
lazyNoopScope.stop();
|
|
6235
|
+
});
|
|
6236
|
+
}
|
|
6237
|
+
const explicitKey = options.key;
|
|
6238
|
+
const wizardKey = resolveWizardKey(explicitKey);
|
|
6239
|
+
const handle = {
|
|
6240
|
+
key: wizardKey,
|
|
6241
|
+
next,
|
|
6242
|
+
back,
|
|
6243
|
+
goTo,
|
|
6244
|
+
handleSubmit,
|
|
6245
|
+
reset,
|
|
6246
|
+
get currentStep() {
|
|
6247
|
+
return currentStep.value;
|
|
6248
|
+
},
|
|
6249
|
+
get activeForm() {
|
|
6250
|
+
return activeForm.value;
|
|
6251
|
+
},
|
|
6252
|
+
get activeIndex() {
|
|
6253
|
+
return activeIndex.value;
|
|
6254
|
+
},
|
|
6255
|
+
get isFinalStep() {
|
|
6256
|
+
return isFinalStep.value;
|
|
6257
|
+
},
|
|
6258
|
+
get steps() {
|
|
6259
|
+
return compiledSteps.value;
|
|
6260
|
+
},
|
|
6261
|
+
get forms() {
|
|
6262
|
+
return formsRecord.value;
|
|
6263
|
+
},
|
|
6264
|
+
get count() {
|
|
6265
|
+
return count.value;
|
|
6266
|
+
},
|
|
6267
|
+
statuses,
|
|
6268
|
+
get allValues() {
|
|
6269
|
+
return allValues.value;
|
|
6270
|
+
},
|
|
6271
|
+
get allErrors() {
|
|
6272
|
+
return allErrors.value;
|
|
6273
|
+
},
|
|
6274
|
+
get progress() {
|
|
6275
|
+
return progress.value;
|
|
6276
|
+
},
|
|
6277
|
+
get canAdvance() {
|
|
6278
|
+
return canAdvance.value;
|
|
6279
|
+
},
|
|
6280
|
+
get canGoBack() {
|
|
6281
|
+
return canGoBack.value;
|
|
6282
|
+
},
|
|
6283
|
+
get complete() {
|
|
6284
|
+
return complete.value;
|
|
6285
|
+
},
|
|
6286
|
+
get done() {
|
|
6287
|
+
return done.value;
|
|
6288
|
+
},
|
|
6289
|
+
get submitting() {
|
|
6290
|
+
return submitting.value;
|
|
6291
|
+
},
|
|
6292
|
+
get submissionAttempts() {
|
|
6293
|
+
return submissionAttempts.value;
|
|
6294
|
+
},
|
|
6295
|
+
get visited() {
|
|
6296
|
+
return visited.value;
|
|
6297
|
+
}
|
|
6298
|
+
};
|
|
6299
|
+
const existing = registry.wizards.get(wizardKey);
|
|
6300
|
+
if (existing === void 0) {
|
|
6301
|
+
registry.wizards.set(wizardKey, handle);
|
|
6302
|
+
} else if (__DEV__ && explicitKey !== void 0) {
|
|
6303
|
+
console.warn(
|
|
6304
|
+
`[attaform] useWizard({ key: "${wizardKey}" }): a wizard with this key is already registered. Keeping the existing handle. Pass a unique key to each useWizard call, or share the original handle via injectWizard("${wizardKey}").`
|
|
6305
|
+
);
|
|
6306
|
+
}
|
|
6307
|
+
if (getCurrentScope() !== void 0) {
|
|
6308
|
+
const releaseWizard = registry.trackWizardConsumer(wizardKey);
|
|
6309
|
+
onScopeDispose(releaseWizard);
|
|
6310
|
+
}
|
|
6311
|
+
if (getCurrentInstance() !== null && explicitKey === void 0) {
|
|
6312
|
+
recordAmbientWizardProvide(registry.ssr);
|
|
6313
|
+
provide(kAttaformAncestorWizard, handle);
|
|
6314
|
+
}
|
|
6315
|
+
return handle;
|
|
6316
|
+
}
|
|
6317
|
+
let anonWizardCounter = 0;
|
|
6318
|
+
const ambientWizardProvideHistory = __DEV__ ? /* @__PURE__ */ new WeakMap() : null;
|
|
6319
|
+
function recordAmbientWizardProvide(ssr) {
|
|
6320
|
+
if (!__DEV__ || ssr || ambientWizardProvideHistory === null) return;
|
|
6321
|
+
const instance = getCurrentInstance();
|
|
6322
|
+
if (instance === null) return;
|
|
6323
|
+
const instanceKey = instance;
|
|
6324
|
+
const entry = {
|
|
6325
|
+
source: captureUserCallSite()
|
|
6326
|
+
};
|
|
6327
|
+
const existing = ambientWizardProvideHistory.get(instanceKey);
|
|
6328
|
+
if (existing === void 0) {
|
|
6329
|
+
ambientWizardProvideHistory.set(instanceKey, [entry]);
|
|
6330
|
+
return;
|
|
6331
|
+
}
|
|
6332
|
+
existing.push(entry);
|
|
6333
|
+
}
|
|
6334
|
+
function resolveWizardKey(key) {
|
|
6335
|
+
if (key !== void 0 && key !== null && key !== "") return key;
|
|
6336
|
+
if (getCurrentInstance() !== null) {
|
|
6337
|
+
return `${ANONYMOUS_WIZARD_KEY_PREFIX}${useId()}`;
|
|
6338
|
+
}
|
|
6339
|
+
return `${ANONYMOUS_WIZARD_KEY_PREFIX}${anonWizardCounter++}`;
|
|
6340
|
+
}
|
|
6341
|
+
function isAnyForm(value) {
|
|
6342
|
+
if (value === null || typeof value !== "object") return false;
|
|
6343
|
+
if (typeof value.key !== "string") return false;
|
|
6344
|
+
return true;
|
|
6345
|
+
}
|
|
6346
|
+
|
|
6347
|
+
function injectWizard(input) {
|
|
6348
|
+
const key = typeof input === "string" ? input : input?.key;
|
|
6349
|
+
const instance = getCurrentInstance();
|
|
6350
|
+
if (instance !== null) ensureAttaformInstalled(instance.appContext.app);
|
|
6351
|
+
const registry = useRegistry();
|
|
6352
|
+
if (key !== void 0) {
|
|
6353
|
+
const handle = registry.wizards.get(key);
|
|
6354
|
+
if (handle === void 0) {
|
|
6355
|
+
warnMiss(
|
|
6356
|
+
`no wizard registered for key '${key}'`,
|
|
6357
|
+
registry.ssr,
|
|
6358
|
+
availableKeysHint(registry.wizards)
|
|
6359
|
+
);
|
|
6360
|
+
return null;
|
|
6361
|
+
}
|
|
6362
|
+
if (getCurrentScope() !== void 0) {
|
|
6363
|
+
const release = registry.trackWizardConsumer(key);
|
|
6364
|
+
onScopeDispose(release);
|
|
6365
|
+
}
|
|
6366
|
+
return handle;
|
|
6367
|
+
}
|
|
6368
|
+
const ambient = inject(kAttaformAncestorWizard, null);
|
|
6369
|
+
if (ambient === null) return null;
|
|
6370
|
+
warnIfAmbientWizardProviderHadDuplicates();
|
|
6371
|
+
return ambient;
|
|
6372
|
+
}
|
|
6373
|
+
function availableKeysHint(wizards) {
|
|
6374
|
+
if (wizards.size === 0) return void 0;
|
|
6375
|
+
const keys = [...wizards.keys()].map((k) => `"${k}"`).join(", ");
|
|
6376
|
+
return `Registered keys: ${keys}.`;
|
|
6377
|
+
}
|
|
6378
|
+
function warnMiss(detail, ssr, hint) {
|
|
6379
|
+
if (!__DEV__ || ssr) return;
|
|
6380
|
+
const frame = captureUserCallSite();
|
|
6381
|
+
const parts = [`[attaform] injectWizard: ${detail}. Returning null.`];
|
|
6382
|
+
if (hint !== void 0) parts.push(hint);
|
|
6383
|
+
if (frame !== void 0) parts.push(frame);
|
|
6384
|
+
console.warn(parts.join(" "));
|
|
6385
|
+
}
|
|
6386
|
+
function warnIfAmbientWizardProviderHadDuplicates() {
|
|
6387
|
+
if (!__DEV__ || ambientWizardProvideHistory === null) return;
|
|
6388
|
+
let ancestor = getCurrentInstance()?.parent ?? null;
|
|
6389
|
+
while (ancestor !== null) {
|
|
6390
|
+
const history = ambientWizardProvideHistory.get(ancestor);
|
|
6391
|
+
if (history !== void 0) {
|
|
6392
|
+
if (history.length > 1) {
|
|
6393
|
+
const lines = history.map((entry) => ` - ${entry.source ?? "<unknown location>"}`);
|
|
6394
|
+
console.warn(
|
|
6395
|
+
"[attaform] injectWizard() (no key) resolved against an ancestor with multiple anonymous useWizard() calls; descendants only see the last-provided wizard. Anonymous useWizard() calls were:\n" + lines.join("\n") + '\nFix: pass a key to each call (e.g. useWizard({ steps, key: "x" })) and reach them via injectWizard("x"), or split the wizards across separate components.'
|
|
6396
|
+
);
|
|
6397
|
+
}
|
|
6398
|
+
return;
|
|
6399
|
+
}
|
|
6400
|
+
ancestor = ancestor.parent;
|
|
6401
|
+
}
|
|
6402
|
+
}
|
|
6403
|
+
|
|
6404
|
+
export { AttaformErrorCode as A, injectWizard as a, isUnset as b, useWizard as c, defaultCoercionRules as d, defaultShouldShowErrors as e, defineCoercion as f, useAbstractForm as g, getAtPath as h, injectForm as i, humanize as j, setAtPath as k, lazy as l, isPlainRecord as m, normalizeNumericOption as n, slimKindOf as s, unset as u };
|
|
6405
|
+
//# sourceMappingURL=attaform.DsC3rZHG.mjs.map
|