attaform 0.29.0 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abstract.d.mts +15 -21
- package/dist/abstract.mjs +4 -4
- package/dist/dev/abstract.mjs +4 -4
- package/dist/dev/chunks/dev-key-collision-warnings.mjs +47 -8
- package/dist/dev/chunks/devtools.mjs +2 -2
- package/dist/dev/directive.mjs +1 -1
- package/dist/dev/history.mjs +1 -2
- package/dist/dev/index.mjs +5 -5
- package/dist/dev/runtime/plugins/attaform.mjs +3 -3
- package/dist/dev/shared/{attaform-dev.BO-71R54.mjs → attaform-dev.BJK1kqHY.mjs} +44 -198
- package/dist/dev/shared/{attaform-dev.D8aoKb89.mjs → attaform-dev.BMQ3GcgD.mjs} +51 -64
- package/dist/dev/shared/{attaform-dev.BgM6FdtA.mjs → attaform-dev.BUIFcTYf.mjs} +94 -62
- package/dist/dev/shared/{attaform-dev.q6UutkCx.mjs → attaform-dev.CT7MDjPy.mjs} +92 -152
- package/dist/dev/shared/{attaform-dev.ButTkwHd.mjs → attaform-dev.CZz4GYZ6.mjs} +26 -15
- package/dist/dev/shared/{attaform-dev.DD5m2W51.mjs → attaform-dev.Chm4D6SJ.mjs} +2 -4
- package/dist/dev/shared/{attaform-dev.DrzObAZo.mjs → attaform-dev.DuEE4GU1.mjs} +4 -4
- package/dist/dev/shared/{attaform-dev.DHuydLAC.mjs → attaform-dev.DxHdkkPG.mjs} +674 -734
- package/dist/dev/shared/{attaform-dev.A-TpJW0B.mjs → attaform-dev.zlvDr0WB.mjs} +3 -21
- package/dist/dev/zod-v3.mjs +6 -6
- package/dist/dev/zod-v4.mjs +6 -6
- package/dist/dev/zod.mjs +5 -5
- package/dist/directive.d.mts +4 -4
- package/dist/directive.mjs +1 -1
- package/dist/esbuild.d.mts +11 -0
- package/dist/esbuild.mjs +1 -1
- package/dist/history.d.mts +3 -32
- package/dist/history.mjs +1 -2
- package/dist/index.d.mts +67 -70
- package/dist/index.mjs +5 -5
- package/dist/nuxt.d.mts +6 -14
- package/dist/nuxt.mjs +3 -7
- package/dist/rollup.d.mts +11 -0
- package/dist/rollup.mjs +1 -1
- package/dist/rspack.d.mts +19 -11
- package/dist/rspack.mjs +1 -1
- package/dist/runtime/components/AttaformDevtoolsPanel.vue +12 -12
- package/dist/runtime/components/DevtoolsValueTree.vue +2 -2
- package/dist/runtime/plugins/attaform.mjs +3 -3
- package/dist/shared/{attaform.CIWLMY-R.d.mts → attaform.2FslhB9R.d.mts} +83 -95
- package/dist/shared/{attaform.Cvao8bW5.mjs → attaform.8mfKZCwH.mjs} +7 -7
- package/dist/shared/{attaform.DRUT703x.d.mts → attaform.B-Tb62qV.d.mts} +12 -12
- package/dist/shared/{attaform.v0fQtWyA.mjs → attaform.B1rH9ht6.mjs} +1 -1
- package/dist/shared/{attaform.BndjUUYX.mjs → attaform.B2iebkPA.mjs} +4 -4
- package/dist/shared/{attaform.DhnPb6nK.mjs → attaform.BAf3UNn4.mjs} +1 -1
- package/dist/shared/{attaform.CZMAYwSK.mjs → attaform.BIWicztt.mjs} +651 -678
- package/dist/shared/attaform.BcQwgWaq.d.mts +2153 -0
- package/dist/shared/attaform.Br8EBn5B.d.mts +4553 -0
- package/dist/shared/{attaform.C2MVyuxC.mjs → attaform.CO918TdU.mjs} +44 -198
- package/dist/shared/{attaform.CZYt_5N_.mjs → attaform.CdZIEf7_.mjs} +1 -19
- package/dist/shared/{attaform.B0dKL8ST.mjs → attaform.DRsANMok.mjs} +94 -62
- package/dist/shared/{attaform.DffRL5pP.mjs → attaform.D_f1WgYf.mjs} +46 -59
- package/dist/shared/{attaform.D1fnTifF.mjs → attaform.D_pt1gOj.mjs} +92 -150
- package/dist/shared/attaform.L0kUn3vL.d.mts +42 -0
- package/dist/shared/{attaform.D0x-qSXm.mjs → attaform.WiGdIqgx.mjs} +1 -3
- package/dist/shared/{attaform.BK0cgu2K.mjs → attaform.ZYn2ahzJ.mjs} +3 -3
- package/dist/transforms.d.mts +153 -26
- package/dist/transforms.mjs +1 -1
- package/dist/vite.d.mts +29 -52
- package/dist/vite.mjs +8 -3
- package/dist/webpack.d.mts +20 -12
- package/dist/webpack.mjs +1 -1
- package/dist/zod-v3.d.mts +90 -96
- package/dist/zod-v3.mjs +6 -6
- package/dist/zod-v4.d.mts +119 -119
- package/dist/zod-v4.mjs +6 -6
- package/dist/zod.d.mts +5 -5
- package/dist/zod.mjs +5 -5
- package/package.json +9 -7
- package/skills/attaform/SKILL.md +1 -1
- package/skills/attaform/references/custom-components.md +1 -1
- package/dist/chunks/fingerprint.mjs +0 -183
- package/dist/chunks/fingerprint2.mjs +0 -159
- package/dist/dev/chunks/fingerprint.mjs +0 -183
- package/dist/dev/chunks/fingerprint2.mjs +0 -159
- package/dist/dev/shared/attaform-dev.BxfsuwBR.mjs +0 -44
- package/dist/shared/attaform.BxfsuwBR.mjs +0 -44
- package/dist/shared/attaform.CsoeRi3P.d.mts +0 -5155
- package/dist/shared/attaform.DDtqHYv1.d.mts +0 -2465
- package/dist/shared/attaform.axHXVQO7.d.mts +0 -44
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, nextTick, warn, reactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope } from 'vue';
|
|
2
|
-
import { A as ANONYMOUS_FORM_KEY_PREFIX,
|
|
3
|
-
import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform-dev.
|
|
4
|
-
import { R as REGISTER_OWNER_MARKER, d as armDomBinding, V as V_REGISTER_MARKER } from './attaform-dev.
|
|
5
|
-
import { i as isPathPrefix, e as canonicalizePath,
|
|
1
|
+
import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, nextTick, warn, reactive, shallowReactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope } from 'vue';
|
|
2
|
+
import { A as ANONYMOUS_FORM_KEY_PREFIX, i as isShadowedKey, d as safeOwnHas, a as safeOwnRead, e as consumerHas, f as spreadConsumerRecord, g as copyConsumerArray, r as readConsumerIndex, b as safeAssign, h as consumerKeys, j as readConsumerProp, k as diffAndApply, p as pickDefined, s as structuralSnapshot, n as normalizeNumericOption, l as DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS, m as consumerSymbolKeys, o as applyChangedKeys, R as RESERVED_KEY_PREFIX, q as DEFAULT_MAX_RECURSION_DEPTH, c as callConsumerFn, t as reportConsumerThrow, u as ANONYMOUS_WIZARD_KEY_PREFIX } from './attaform-dev.CZz4GYZ6.mjs';
|
|
3
|
+
import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform-dev.Chm4D6SJ.mjs';
|
|
4
|
+
import { R as REGISTER_OWNER_MARKER, d as armDomBinding, V as V_REGISTER_MARKER } from './attaform-dev.BMQ3GcgD.mjs';
|
|
5
|
+
import { i as isPathPrefix, e as canonicalizePath, k as keyForSegments, m as mapSegmentKeys, b as ROOT_PATH_KEY, s as segmentsForPathKey, S as SubmitErrorHandlerError, t as toError, j as groupErrorsByKey, n as normalizeErrorInput, h as SET_MEMBER_SEGMENT, R as ROOT_PATH, l as normalizeErrorInputs, a as InvalidUseFormConfigError, d as ReservedFormKeyError } from './attaform-dev.zlvDr0WB.mjs';
|
|
6
6
|
|
|
7
7
|
const AttaformErrorCode = {
|
|
8
|
-
/** A required field is in the blank set
|
|
8
|
+
/** A required field is in the blank set: no value was supplied. */
|
|
9
9
|
NoValueSupplied: "atta:no-value-supplied",
|
|
10
10
|
/** The schema adapter's `validateAtPath` threw synchronously. */
|
|
11
11
|
AdapterThrew: "atta:adapter-threw",
|
|
@@ -77,11 +77,6 @@ function makeBlankRequiredError(segments) {
|
|
|
77
77
|
}
|
|
78
78
|
const NO_ERRORS = Object.freeze([]);
|
|
79
79
|
|
|
80
|
-
function crossCopyState(key, create) {
|
|
81
|
-
const carrier = globalThis;
|
|
82
|
-
return carrier[key] ?? (carrier[key] = create());
|
|
83
|
-
}
|
|
84
|
-
|
|
85
80
|
function isGateOpen(field, formMeta) {
|
|
86
81
|
return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
|
|
87
82
|
}
|
|
@@ -103,45 +98,32 @@ function earliestNonNull(a, b) {
|
|
|
103
98
|
return a < b ? a : b;
|
|
104
99
|
}
|
|
105
100
|
const DEFAULT_TIMINGS = { showDelay: 120, minVisible: 120 };
|
|
101
|
+
const { showDelay, minVisible } = DEFAULT_TIMINGS;
|
|
106
102
|
const FOCUS_OUT_GRACE = 16;
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
()
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (!isGateOpen(field, formMeta)) return { display: verdict };
|
|
121
|
-
const inFlightSince = earliestNonNull(validatingSince, transformingSince);
|
|
122
|
-
if (inFlightSince === null) {
|
|
123
|
-
if (prev.display === "pending") {
|
|
124
|
-
const shownAt = prev.pendingShownAt ?? now;
|
|
125
|
-
if (now < shownAt + minVisible)
|
|
126
|
-
return { display: "pending", pendingShownAt: shownAt, reviewAt: shownAt + minVisible };
|
|
127
|
-
}
|
|
128
|
-
return { display: verdict };
|
|
129
|
-
}
|
|
130
|
-
if (prev.display === "pending")
|
|
131
|
-
return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
|
|
132
|
-
const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
|
|
133
|
-
if (now - inFlightSince < window) {
|
|
134
|
-
return { display: prev.display, reviewAt: inFlightSince + window };
|
|
103
|
+
const defaultDisplayState = (prev, { field, formMeta, validatingSince, transformingSince, now }) => {
|
|
104
|
+
const verdict = computeVerdict(field, formMeta);
|
|
105
|
+
if (!isGateOpen(field, formMeta)) return { display: verdict };
|
|
106
|
+
const inFlightSince = earliestNonNull(validatingSince, transformingSince);
|
|
107
|
+
if (inFlightSince === null) {
|
|
108
|
+
if (prev.display === "pending") {
|
|
109
|
+
const shownAt = prev.pendingShownAt ?? now;
|
|
110
|
+
if (now < shownAt + minVisible)
|
|
111
|
+
return {
|
|
112
|
+
display: "pending",
|
|
113
|
+
pendingShownAt: shownAt,
|
|
114
|
+
reviewAt: shownAt + minVisible
|
|
115
|
+
};
|
|
135
116
|
}
|
|
136
|
-
return { display:
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
117
|
+
return { display: verdict };
|
|
118
|
+
}
|
|
119
|
+
if (prev.display === "pending")
|
|
120
|
+
return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
|
|
121
|
+
const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
|
|
122
|
+
if (now - inFlightSince < window) {
|
|
123
|
+
return { display: prev.display, reviewAt: inFlightSince + window };
|
|
124
|
+
}
|
|
125
|
+
return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
|
|
126
|
+
};
|
|
145
127
|
|
|
146
128
|
function hashStableString(input, seed = 0) {
|
|
147
129
|
let h1 = 3735928559 ^ seed;
|
|
@@ -229,16 +211,27 @@ function descendStep(value, segment) {
|
|
|
229
211
|
}
|
|
230
212
|
function getAtPath(root, path) {
|
|
231
213
|
if (path.length === 0) return root;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
214
|
+
try {
|
|
215
|
+
let current = root;
|
|
216
|
+
for (const segment of path) {
|
|
217
|
+
const next = descendStep(current, segment);
|
|
218
|
+
if (next === NOT_FOUND) return void 0;
|
|
219
|
+
current = next;
|
|
220
|
+
}
|
|
221
|
+
return current;
|
|
222
|
+
} catch {
|
|
223
|
+
return void 0;
|
|
237
224
|
}
|
|
238
|
-
return current;
|
|
239
225
|
}
|
|
240
226
|
function hasAtPath(root, path) {
|
|
241
227
|
if (path.length === 0) return true;
|
|
228
|
+
try {
|
|
229
|
+
return hasAtPathUnguarded(root, path);
|
|
230
|
+
} catch {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function hasAtPathUnguarded(root, path) {
|
|
242
235
|
let current = root;
|
|
243
236
|
for (let i = 0; i < path.length - 1; i++) {
|
|
244
237
|
const segment = path[i];
|
|
@@ -265,29 +258,15 @@ function isPlainRecord(value) {
|
|
|
265
258
|
const proto = Object.getPrototypeOf(value);
|
|
266
259
|
return proto === null || proto === Object.prototype;
|
|
267
260
|
}
|
|
261
|
+
const NO_SCHEMA_DEFAULTS = /* @__PURE__ */ new Set();
|
|
262
|
+
const NO_SCHEMA_FILL = {
|
|
263
|
+
getDefaultAtPath: () => void 0,
|
|
264
|
+
arrayShapeAtPath: () => null,
|
|
265
|
+
getSlimPrimitiveTypesAtPath: () => NO_SCHEMA_DEFAULTS,
|
|
266
|
+
entryKeyKindAtPath: () => void 0
|
|
267
|
+
};
|
|
268
268
|
function setAtPath(root, path, value) {
|
|
269
|
-
return
|
|
270
|
-
}
|
|
271
|
-
function setAtPathOffset(root, path, value, offset) {
|
|
272
|
-
if (offset >= path.length) return value;
|
|
273
|
-
const head = path[offset];
|
|
274
|
-
const nextOffset = offset + 1;
|
|
275
|
-
if (root instanceof Map) {
|
|
276
|
-
const next = new Map(root);
|
|
277
|
-
const key = mapKeyForSegment(root, head);
|
|
278
|
-
next.set(key, setAtPathOffset(root.get(key), path, value, nextOffset));
|
|
279
|
-
return next;
|
|
280
|
-
}
|
|
281
|
-
if (!isRebuildableContainer(root, head)) return root;
|
|
282
|
-
if (typeof head === "number") {
|
|
283
|
-
const arr = Array.isArray(root) ? [...root] : [];
|
|
284
|
-
while (arr.length <= head) arr.push(void 0);
|
|
285
|
-
arr[head] = setAtPathOffset(arr[head], path, value, nextOffset);
|
|
286
|
-
return arr;
|
|
287
|
-
}
|
|
288
|
-
const rec = isPlainRecord(root) ? { ...root } : {};
|
|
289
|
-
safeAssign(rec, head, setAtPathOffset(safeOwnRead(rec, head), path, value, nextOffset));
|
|
290
|
-
return rec;
|
|
269
|
+
return setAtPathWithSchemaFill(root, NO_SCHEMA_FILL, path, value);
|
|
291
270
|
}
|
|
292
271
|
const NO_IN_PLACE = { applied: false };
|
|
293
272
|
function tryInPlaceLeafWrite(root, path, value) {
|
|
@@ -493,10 +472,10 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
|
|
|
493
472
|
}
|
|
494
473
|
const rec = isPlainRecord(root) ? { ...root } : {};
|
|
495
474
|
if (isLeafStep) {
|
|
496
|
-
rec
|
|
475
|
+
safeAssign(rec, head, value);
|
|
497
476
|
return rec;
|
|
498
477
|
}
|
|
499
|
-
const existing = rec
|
|
478
|
+
const existing = safeOwnRead(rec, head);
|
|
500
479
|
let childRoot;
|
|
501
480
|
if (existing === void 0 || existing !== null && typeof existing !== "object") {
|
|
502
481
|
const intermPath = [...fullPath.slice(0, startIdx + 1)];
|
|
@@ -505,11 +484,14 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
|
|
|
505
484
|
} else {
|
|
506
485
|
childRoot = existing;
|
|
507
486
|
}
|
|
508
|
-
|
|
487
|
+
safeAssign(
|
|
488
|
+
rec,
|
|
489
|
+
head,
|
|
490
|
+
setAtPathWithSchemaFillImpl(childRoot, schema, fullPath, value, startIdx + 1)
|
|
491
|
+
);
|
|
509
492
|
return rec;
|
|
510
493
|
}
|
|
511
494
|
|
|
512
|
-
const warnedDisplayStatePredicates = /* @__PURE__ */ new WeakSet();
|
|
513
495
|
function isUnderStubAncestor(state, segments) {
|
|
514
496
|
for (let i = 0; i < segments.length; i++) {
|
|
515
497
|
const ancestorPath = segments.slice(0, i);
|
|
@@ -523,16 +505,15 @@ function isUnderStubAncestor(state, segments) {
|
|
|
523
505
|
}
|
|
524
506
|
return false;
|
|
525
507
|
}
|
|
526
|
-
function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, sweep
|
|
508
|
+
function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, sweep) {
|
|
527
509
|
const cache = /* @__PURE__ */ new Map();
|
|
528
|
-
const predicate = options?.getDisplayState;
|
|
529
510
|
sweep.onEvict((key) => cache.delete(key));
|
|
530
511
|
return function getFieldState(pathInput) {
|
|
531
512
|
const { segments, key } = canonicalizePath(pathInput);
|
|
532
513
|
const cached = cache.get(key);
|
|
533
514
|
if (cached !== void 0) return cached;
|
|
534
515
|
const c = computed(
|
|
535
|
-
() => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase
|
|
516
|
+
() => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase) : buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase)
|
|
536
517
|
);
|
|
537
518
|
cache.set(key, c);
|
|
538
519
|
sweep.track(segments, key);
|
|
@@ -549,7 +530,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
549
530
|
const record = state.fields.get(key);
|
|
550
531
|
const value = state.getValueAtPath(segments);
|
|
551
532
|
const original = state.originals.get(key)?.value;
|
|
552
|
-
const pristine = state.
|
|
533
|
+
const pristine = state.isPristineAtPathByKey(key, segments);
|
|
553
534
|
const cell = state.errorCells.get(key);
|
|
554
535
|
const schemaForKey = cell !== void 0 && cell.schema.length > 0 ? cell.schema : void 0;
|
|
555
536
|
const blankForKey = state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments) ? [makeBlankRequiredError(segments)] : void 0;
|
|
@@ -561,7 +542,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
561
542
|
const validating = (state.fieldValidationCounts.get(key) ?? 0) > 0;
|
|
562
543
|
const transforming = (state.fieldTransformCounts.get(key) ?? 0) > 0;
|
|
563
544
|
const transformError = state.transformErrors.get(key) ?? null;
|
|
564
|
-
const gated = state.
|
|
545
|
+
const gated = state.pathHasAsyncValidationByKey(key, segments) && !state.firstValidationDone.value;
|
|
565
546
|
const isOrphan = segments.length > 0 && !hasAtPath(state.form.value, segments) && isUnderStubAncestor(state, segments);
|
|
566
547
|
const valid = !gated && errors.length === 0 && !validating && !isOrphan;
|
|
567
548
|
const elementRecord = state.domBinding.value?.elements.get(key);
|
|
@@ -584,9 +565,8 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
584
565
|
updatedAt: record?.updatedAt ?? null,
|
|
585
566
|
errors,
|
|
586
567
|
// A leaf has no descendants, so its own-bucket errors ARE its subtree
|
|
587
|
-
// errors. Reuse the
|
|
588
|
-
//
|
|
589
|
-
// map (preserves the P3 blank-reactivity optimization above).
|
|
568
|
+
// errors. Reuse the array reference, which makes `ownErrors === errors`
|
|
569
|
+
// and avoids a second read of the whole-form `derivedBlankErrors` map.
|
|
590
570
|
ownErrors: errors,
|
|
591
571
|
validating,
|
|
592
572
|
valid,
|
|
@@ -604,7 +584,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
604
584
|
meta: resolved.meta
|
|
605
585
|
};
|
|
606
586
|
}
|
|
607
|
-
function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase
|
|
587
|
+
function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase) {
|
|
608
588
|
const base = buildLeafFieldStateBase(state, segments, key, formInstanceId);
|
|
609
589
|
const validatingSince = state.fieldValidatingSince.get(key) ?? null;
|
|
610
590
|
const transformingSince = state.fieldTransformingSince.get(key) ?? null;
|
|
@@ -617,9 +597,8 @@ function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBa
|
|
|
617
597
|
transformingSince,
|
|
618
598
|
false,
|
|
619
599
|
// revealedDescendantError: leaves have no descendants
|
|
620
|
-
false
|
|
600
|
+
false
|
|
621
601
|
// isRoot: a leaf is never the form root
|
|
622
|
-
getDisplayState
|
|
623
602
|
);
|
|
624
603
|
}
|
|
625
604
|
function visitActiveLeafPaths(value, base, visit) {
|
|
@@ -717,7 +696,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
717
696
|
pristine = false;
|
|
718
697
|
dirty = true;
|
|
719
698
|
}
|
|
720
|
-
const errors = aggregateErrorsAt(state, segments);
|
|
699
|
+
const errors = aggregateErrorsAt(state, segments, key);
|
|
721
700
|
const revealedDescendantError = errors.length > 0 && errors.some((e) => {
|
|
722
701
|
const ePath = e.path;
|
|
723
702
|
if (ePath.length === segments.length && ePath.every((s, i) => s === segments[i])) return false;
|
|
@@ -725,7 +704,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
725
704
|
if (ePath.length === 1 && ePath[0] === "") return blurredAfterInteraction;
|
|
726
705
|
return blurredLeafSegments.some((s) => isPathPrefix(ePath, s));
|
|
727
706
|
});
|
|
728
|
-
if (!asyncPending && state.
|
|
707
|
+
if (!asyncPending && state.pathHasAsyncValidationByKey(key, segments)) asyncPending = true;
|
|
729
708
|
if ((state.fieldValidationCounts.get(key) ?? 0) > 0) {
|
|
730
709
|
validating = true;
|
|
731
710
|
const since = state.fieldValidatingSince.get(key);
|
|
@@ -767,19 +746,19 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
767
746
|
elements: EMPTY_ELEMENTS,
|
|
768
747
|
updatedAt,
|
|
769
748
|
errors,
|
|
770
|
-
// The container's OWN bucket
|
|
771
|
-
// descendant
|
|
772
|
-
//
|
|
773
|
-
//
|
|
774
|
-
//
|
|
749
|
+
// The container's OWN bucket at this exact path, excluding every
|
|
750
|
+
// descendant `errors` rolls up: a cross-field `.refine()` here, or a
|
|
751
|
+
// `setErrors` pinned at this node. The same raw union the leaf builds
|
|
752
|
+
// from the three stores, and the container already depends on all three
|
|
753
|
+
// through `aggregateErrorsAt`, so it costs no new reactivity.
|
|
775
754
|
ownErrors: state.getErrorsForPath(segments),
|
|
776
755
|
validating,
|
|
777
756
|
valid,
|
|
778
757
|
transforming,
|
|
779
758
|
busy: transforming || validating,
|
|
780
|
-
// A container surfaces its OWN transform failure
|
|
781
|
-
// on the container path
|
|
782
|
-
// descendant
|
|
759
|
+
// A container surfaces its OWN transform failure, from a transform
|
|
760
|
+
// registered on the container path such as a file normalizer, and never
|
|
761
|
+
// rolls up a descendant's: that stays a per-field channel.
|
|
783
762
|
transformError: ownTransformError,
|
|
784
763
|
path: segments,
|
|
785
764
|
...computeFieldIdentity(formInstanceId, state.formKey, key),
|
|
@@ -796,7 +775,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
796
775
|
revealedDescendantError
|
|
797
776
|
};
|
|
798
777
|
}
|
|
799
|
-
function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase
|
|
778
|
+
function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase) {
|
|
800
779
|
const { base, validatingSince, transformingSince, revealedDescendantError } = buildContainerFieldStateBase(state, segments, key, formInstanceId);
|
|
801
780
|
return decorateWithDerivedProps(
|
|
802
781
|
base,
|
|
@@ -806,14 +785,12 @@ function buildContainerFieldState(state, segments, key, formInstanceId, getFormM
|
|
|
806
785
|
validatingSince,
|
|
807
786
|
transformingSince,
|
|
808
787
|
revealedDescendantError,
|
|
809
|
-
segments.length === 0
|
|
810
|
-
getDisplayState
|
|
788
|
+
segments.length === 0
|
|
811
789
|
);
|
|
812
790
|
}
|
|
813
|
-
function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, transformingSince, revealedDescendantError, isRoot
|
|
791
|
+
function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, transformingSince, revealedDescendantError, isRoot) {
|
|
814
792
|
const firstError = base.errors[0];
|
|
815
793
|
const firstOwnError = base.ownErrors[0];
|
|
816
|
-
const predicate = getDisplayState ?? state.getDisplayState;
|
|
817
794
|
const formMeta = getFormMetaBase();
|
|
818
795
|
const ctx = {
|
|
819
796
|
field: base,
|
|
@@ -824,23 +801,9 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
|
|
|
824
801
|
// flight) so the reducer returns the plain verdict and hydration matches.
|
|
825
802
|
now: state.ssr ? 0 : Date.now()
|
|
826
803
|
};
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
machine = state.displayEngine.resolve(key, ctx, predicate);
|
|
831
|
-
} catch (err) {
|
|
832
|
-
if (!warnedDisplayStatePredicates.has(predicate)) {
|
|
833
|
-
warnedDisplayStatePredicates.add(predicate);
|
|
834
|
-
console.warn(
|
|
835
|
-
"[attaform] custom getDisplayState threw \u2014 falling back to defaultDisplayState. Subsequent throws from the same predicate will not warn again.",
|
|
836
|
-
err
|
|
837
|
-
);
|
|
838
|
-
}
|
|
839
|
-
machine = state.displayEngine.resolve(key, ctx, defaultDisplayState);
|
|
840
|
-
rollupApplies = true;
|
|
841
|
-
}
|
|
842
|
-
const submitValidating = rollupApplies && isRoot && state.submitting.value && state.activeValidations.value > 0;
|
|
843
|
-
const resolvedDisplayState = machine.display === "pending" || submitValidating ? "pending" : rollupApplies && revealedDescendantError ? "error" : machine.display;
|
|
804
|
+
const machine = state.displayEngine.resolve(key, ctx, defaultDisplayState);
|
|
805
|
+
const submitValidating = isRoot && state.submitting.value && state.activeValidations.value > 0;
|
|
806
|
+
const resolvedDisplayState = machine.display === "pending" || submitValidating ? "pending" : revealedDescendantError ? "error" : machine.display;
|
|
844
807
|
const displayState = base.disabled === true ? "idle" : resolvedDisplayState;
|
|
845
808
|
return {
|
|
846
809
|
...base,
|
|
@@ -853,26 +816,33 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
|
|
|
853
816
|
firstOwnError
|
|
854
817
|
};
|
|
855
818
|
}
|
|
856
|
-
function aggregateErrorsAt(state, prefix) {
|
|
819
|
+
function aggregateErrorsAt(state, prefix, prefixKey) {
|
|
820
|
+
const candidates = state.errorWindowAt(prefix, prefixKey);
|
|
821
|
+
if (candidates.length === 0) return [];
|
|
857
822
|
const formValue = state.form.value;
|
|
823
|
+
const cells = state.errorCells;
|
|
858
824
|
const buckets = /* @__PURE__ */ new Map();
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
825
|
+
for (const { key, segments } of candidates) {
|
|
826
|
+
if (!isPathPrefix(prefix, segments)) continue;
|
|
827
|
+
if (segments.length > 0 && !hasAtPath(formValue, segments)) continue;
|
|
828
|
+
const cell = cells.get(key);
|
|
829
|
+
const blankList = state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments) ? [makeBlankRequiredError(segments)] : void 0;
|
|
830
|
+
const schemaList = cell?.schema;
|
|
831
|
+
const userList = cell?.user;
|
|
832
|
+
const total = (schemaList?.length ?? 0) + (blankList?.length ?? 0) + (userList?.length ?? 0);
|
|
833
|
+
if (total === 0) continue;
|
|
834
|
+
const ordinal = state.ensurePathOrdinal(key);
|
|
835
|
+
let bucket = buckets.get(ordinal);
|
|
836
|
+
if (bucket === void 0) {
|
|
837
|
+
bucket = [];
|
|
838
|
+
buckets.set(ordinal, bucket);
|
|
839
|
+
}
|
|
840
|
+
if (schemaList !== void 0) bucket.push(...schemaList);
|
|
841
|
+
if (blankList !== void 0) bucket.push(...blankList);
|
|
842
|
+
if (userList !== void 0) bucket.push(...userList);
|
|
843
|
+
}
|
|
875
844
|
if (buckets.size === 0) return [];
|
|
845
|
+
if (buckets.size === 1) return [...buckets.values()][0];
|
|
876
846
|
return [...buckets.entries()].sort(([a], [b]) => a - b).flatMap(([, errs]) => errs);
|
|
877
847
|
}
|
|
878
848
|
const EMPTY_ELEMENTS = Object.freeze([]);
|
|
@@ -915,7 +885,7 @@ function isArrayPath(state, segments) {
|
|
|
915
885
|
function warnReadOnly(surface, action, key) {
|
|
916
886
|
const phrase = action === "write" ? `write to "${String(key)}"` : `${action} of "${String(key)}"`;
|
|
917
887
|
console.warn(
|
|
918
|
-
`[attaform] ${surface} is read-only
|
|
888
|
+
`[attaform] ${surface} is read-only; ${phrase} was ignored. Mutate the form via setValue / the directive / field-array helpers instead.`
|
|
919
889
|
);
|
|
920
890
|
}
|
|
921
891
|
function makeReadonlyCoercion(snapshot) {
|
|
@@ -967,7 +937,7 @@ function buildTree(spec) {
|
|
|
967
937
|
existsCache.delete(key);
|
|
968
938
|
});
|
|
969
939
|
function schemaHasPath(segs) {
|
|
970
|
-
const cacheKey =
|
|
940
|
+
const cacheKey = keyForSegments(segs).key;
|
|
971
941
|
const cached = existsCache.get(cacheKey);
|
|
972
942
|
if (cached !== void 0) return cached;
|
|
973
943
|
const result = spec.schema.getSlimPrimitiveTypesAtPath(segs).size > 0;
|
|
@@ -982,7 +952,7 @@ function buildTree(spec) {
|
|
|
982
952
|
}
|
|
983
953
|
function containerAt(segments) {
|
|
984
954
|
const isArrayLike = spec.isArrayAt(segments);
|
|
985
|
-
const pathKey =
|
|
955
|
+
const pathKey = keyForSegments(segments).key;
|
|
986
956
|
const cacheKey = `${pathKey}+${isArrayLike ? "A" : "O"}`;
|
|
987
957
|
const existing = containerCache.get(cacheKey);
|
|
988
958
|
if (existing !== void 0) return existing;
|
|
@@ -1082,7 +1052,7 @@ function buildErrorsSurface(state, sweep) {
|
|
|
1082
1052
|
const treeCache = /* @__PURE__ */ new Map();
|
|
1083
1053
|
sweep.onEvict((key) => treeCache.delete(key));
|
|
1084
1054
|
const materialize = (segments) => {
|
|
1085
|
-
const cacheKey =
|
|
1055
|
+
const cacheKey = keyForSegments(segments).key;
|
|
1086
1056
|
let tree = treeCache.get(cacheKey);
|
|
1087
1057
|
if (tree === void 0) {
|
|
1088
1058
|
const frozen = [...segments];
|
|
@@ -1094,28 +1064,21 @@ function buildErrorsSurface(state, sweep) {
|
|
|
1094
1064
|
};
|
|
1095
1065
|
const resolveLeaf = (path) => {
|
|
1096
1066
|
const isContainerSelfAccess = path.length > 1 && path[path.length - 1] === "";
|
|
1097
|
-
const collectAtKey = (key2, active2, into) => {
|
|
1098
|
-
const cell = state.errorCells.get(key2);
|
|
1099
|
-
if (active2) {
|
|
1100
|
-
const b = state.derivedBlankErrors.value.get(key2);
|
|
1101
|
-
if (cell !== void 0) into.push(...cell.schema);
|
|
1102
|
-
if (b !== void 0) into.push(...b);
|
|
1103
|
-
}
|
|
1104
|
-
if (cell !== void 0) into.push(...cell.user);
|
|
1105
|
-
};
|
|
1106
1067
|
const merged = [];
|
|
1107
1068
|
if (isContainerSelfAccess) {
|
|
1108
1069
|
const containerPath = path.slice(0, -1);
|
|
1109
|
-
const
|
|
1110
|
-
const
|
|
1070
|
+
const container = canonicalizePath(containerPath);
|
|
1071
|
+
const literal = canonicalizePath(path);
|
|
1111
1072
|
const active2 = hasAtPath(state.form.value, containerPath);
|
|
1112
|
-
|
|
1113
|
-
if (
|
|
1073
|
+
collectErrorsAt(state, container.key, container.segments, active2, merged);
|
|
1074
|
+
if (literal.key !== container.key) {
|
|
1075
|
+
collectErrorsAt(state, literal.key, literal.segments, active2, merged);
|
|
1076
|
+
}
|
|
1114
1077
|
return merged;
|
|
1115
1078
|
}
|
|
1116
|
-
const { key } = canonicalizePath(path);
|
|
1079
|
+
const { key, segments } = canonicalizePath(path);
|
|
1117
1080
|
const active = hasAtPath(state.form.value, path);
|
|
1118
|
-
|
|
1081
|
+
collectErrorsAt(state, key, segments, active, merged);
|
|
1119
1082
|
return merged;
|
|
1120
1083
|
};
|
|
1121
1084
|
return buildTree({
|
|
@@ -1130,68 +1093,83 @@ function buildErrorsSurface(state, sweep) {
|
|
|
1130
1093
|
// non-schema keys reachable while a genuinely-absent key stays out.
|
|
1131
1094
|
hasOwn: (segments, key) => liveContainerHasKey(state, segments, key) || errorAwareContainerKeys(state, segments).includes(key),
|
|
1132
1095
|
isArrayAt: (segments) => isArrayPath(state, segments),
|
|
1133
|
-
// `errors()
|
|
1134
|
-
// aggregate
|
|
1135
|
-
//
|
|
1136
|
-
call: (path) => aggregateErrorsAt(state, path),
|
|
1096
|
+
// `errors()`, `errors([])` and `errors(path)` are all the subtree
|
|
1097
|
+
// aggregate, through the helper `meta.errors` reads, so they cannot
|
|
1098
|
+
// drift.
|
|
1099
|
+
call: (path) => aggregateErrorsAt(state, path, keyForSegments(path).key),
|
|
1137
1100
|
surface: "form.errors",
|
|
1138
1101
|
sweep
|
|
1139
1102
|
});
|
|
1140
1103
|
}
|
|
1104
|
+
function collectErrorsAt(state, key, segments, active, into) {
|
|
1105
|
+
const cell = state.errorCells.get(key);
|
|
1106
|
+
if (active) {
|
|
1107
|
+
if (cell !== void 0) into.push(...cell.schema);
|
|
1108
|
+
if (state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments)) {
|
|
1109
|
+
into.push(makeBlankRequiredError(segments));
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
if (cell !== void 0) into.push(...cell.user);
|
|
1113
|
+
}
|
|
1141
1114
|
function errorAwareContainerKeys(state, segments) {
|
|
1142
1115
|
const keys = new Set(liveKeysAtPath(state, segments));
|
|
1143
1116
|
const formValue = state.form.value;
|
|
1144
|
-
const
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1117
|
+
const errors = [];
|
|
1118
|
+
const found = [];
|
|
1119
|
+
for (const { key, segments: decoded } of state.errorWindowAt(
|
|
1120
|
+
segments,
|
|
1121
|
+
keyForSegments(segments).key
|
|
1122
|
+
)) {
|
|
1123
|
+
if (decoded.length <= segments.length) continue;
|
|
1124
|
+
if (!isPathPrefix(segments, decoded)) continue;
|
|
1125
|
+
errors.length = 0;
|
|
1126
|
+
collectErrorsAt(state, key, decoded, hasAtPath(formValue, decoded), errors);
|
|
1127
|
+
if (errors.length === 0) continue;
|
|
1128
|
+
const nextSeg = decoded[segments.length];
|
|
1129
|
+
found.push({
|
|
1130
|
+
childKey: typeof nextSeg === "number" ? String(nextSeg) : nextSeg,
|
|
1131
|
+
ordinal: state.ensurePathOrdinal(key)
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
found.sort((a, b) => a.ordinal - b.ordinal);
|
|
1135
|
+
for (const { childKey } of found) keys.add(childKey);
|
|
1159
1136
|
return [...keys];
|
|
1160
1137
|
}
|
|
1161
1138
|
function materializeErrors(state, containerSegments) {
|
|
1162
|
-
const
|
|
1139
|
+
const formValue = state.form.value;
|
|
1140
|
+
const liveContainer = getAtPath(formValue, containerSegments);
|
|
1163
1141
|
const tree = Array.isArray(liveContainer) ? [] : {};
|
|
1164
|
-
const
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
for (let i = 0; i < containerSegments.length; i++) {
|
|
1175
|
-
if (fullPath[i] !== containerSegments[i]) continue entries;
|
|
1176
|
-
}
|
|
1177
|
-
if (applyActivePathFilter && !hasAtPath(state.form.value, fullPath)) continue;
|
|
1178
|
-
const relativePath = fullPath.slice(containerSegments.length);
|
|
1179
|
-
let placePath;
|
|
1180
|
-
if (relativePath.length === 0) {
|
|
1181
|
-
placePath = [""];
|
|
1182
|
-
} else if (state.schema.isLeafAtPath(fullPath)) {
|
|
1183
|
-
placePath = relativePath;
|
|
1184
|
-
} else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
|
|
1185
|
-
placePath = [...relativePath, ""];
|
|
1186
|
-
} else {
|
|
1187
|
-
placePath = relativePath;
|
|
1188
|
-
}
|
|
1189
|
-
placeAt(tree, placePath, errors);
|
|
1142
|
+
const placements = [];
|
|
1143
|
+
for (const { key, segments: fullPath } of state.errorWindowAt(
|
|
1144
|
+
containerSegments,
|
|
1145
|
+
keyForSegments(containerSegments).key
|
|
1146
|
+
)) {
|
|
1147
|
+
const isRoot = fullPath.length === 0;
|
|
1148
|
+
if (isRoot) {
|
|
1149
|
+
if (containerSegments.length !== 0) continue;
|
|
1150
|
+
} else if (!isPathPrefix(containerSegments, fullPath)) {
|
|
1151
|
+
continue;
|
|
1190
1152
|
}
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1153
|
+
const errors = [];
|
|
1154
|
+
collectErrorsAt(state, key, fullPath, isRoot || hasAtPath(formValue, fullPath), errors);
|
|
1155
|
+
if (errors.length === 0) continue;
|
|
1156
|
+
const relativePath = fullPath.slice(containerSegments.length);
|
|
1157
|
+
let placePath;
|
|
1158
|
+
if (isRoot) {
|
|
1159
|
+
placePath = [ROOT_PATH_KEY];
|
|
1160
|
+
} else if (relativePath.length === 0) {
|
|
1161
|
+
placePath = [""];
|
|
1162
|
+
} else if (state.schema.isLeafAtPath(fullPath)) {
|
|
1163
|
+
placePath = relativePath;
|
|
1164
|
+
} else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
|
|
1165
|
+
placePath = [...relativePath, ""];
|
|
1166
|
+
} else {
|
|
1167
|
+
placePath = relativePath;
|
|
1168
|
+
}
|
|
1169
|
+
placements.push({ placePath, errors, ordinal: state.ensurePathOrdinal(key) });
|
|
1170
|
+
}
|
|
1171
|
+
placements.sort((a, b) => a.ordinal - b.ordinal);
|
|
1172
|
+
for (const { placePath, errors } of placements) placeAt(tree, placePath, errors);
|
|
1195
1173
|
return tree;
|
|
1196
1174
|
}
|
|
1197
1175
|
function placeAt(tree, path, errors) {
|
|
@@ -1264,7 +1242,7 @@ function buildFieldsSurface(state, getFieldStateAt, sweep) {
|
|
|
1264
1242
|
const viewCache = /* @__PURE__ */ new Map();
|
|
1265
1243
|
sweep.onEvict((key) => viewCache.delete(key));
|
|
1266
1244
|
function viewAt(segments) {
|
|
1267
|
-
const cacheKey =
|
|
1245
|
+
const cacheKey = keyForSegments(segments).key;
|
|
1268
1246
|
const existing = viewCache.get(cacheKey);
|
|
1269
1247
|
if (existing !== void 0) return existing;
|
|
1270
1248
|
const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(
|
|
@@ -1832,45 +1810,6 @@ function buildFieldArrayApi(state) {
|
|
|
1832
1810
|
};
|
|
1833
1811
|
}
|
|
1834
1812
|
|
|
1835
|
-
const SWEEP_SLICE = 8;
|
|
1836
|
-
function createDynamicPathSweep(state) {
|
|
1837
|
-
const dynamicKeys = /* @__PURE__ */ new Set();
|
|
1838
|
-
const evictors = [];
|
|
1839
|
-
const isDynamicPath = (segments) => {
|
|
1840
|
-
for (let i = 0; i < segments.length; i++) {
|
|
1841
|
-
if (typeof segments[i] === "number") return true;
|
|
1842
|
-
if (!state.schema.isFixedObjectAtPath(segments.slice(0, i))) return true;
|
|
1843
|
-
}
|
|
1844
|
-
return false;
|
|
1845
|
-
};
|
|
1846
|
-
let sweepCursor = null;
|
|
1847
|
-
state.onFormChange((next) => {
|
|
1848
|
-
if (dynamicKeys.size === 0) return;
|
|
1849
|
-
const raw = toRaw(next);
|
|
1850
|
-
for (let i = 0; i < SWEEP_SLICE; i++) {
|
|
1851
|
-
sweepCursor ?? (sweepCursor = dynamicKeys.values());
|
|
1852
|
-
const step = sweepCursor.next();
|
|
1853
|
-
if (step.done === true) {
|
|
1854
|
-
sweepCursor = null;
|
|
1855
|
-
break;
|
|
1856
|
-
}
|
|
1857
|
-
const segments = segmentsForPathKey(step.value);
|
|
1858
|
-
if (segments !== null && hasAtPath(raw, segments)) continue;
|
|
1859
|
-
dynamicKeys.delete(step.value);
|
|
1860
|
-
for (const evict of evictors) evict(step.value);
|
|
1861
|
-
}
|
|
1862
|
-
});
|
|
1863
|
-
return {
|
|
1864
|
-
track(segments, key) {
|
|
1865
|
-
if (dynamicKeys.has(key)) return;
|
|
1866
|
-
if (isDynamicPath(segments)) dynamicKeys.add(key);
|
|
1867
|
-
},
|
|
1868
|
-
onEvict(evict) {
|
|
1869
|
-
evictors.push(evict);
|
|
1870
|
-
}
|
|
1871
|
-
};
|
|
1872
|
-
}
|
|
1873
|
-
|
|
1874
1813
|
const warnedNoScopeStores = /* @__PURE__ */ new WeakSet() ;
|
|
1875
1814
|
function isErrorInputLike(value) {
|
|
1876
1815
|
if (value instanceof Error) return true;
|
|
@@ -1898,8 +1837,8 @@ function deriveSubmitErrors(err) {
|
|
|
1898
1837
|
messageless: true
|
|
1899
1838
|
};
|
|
1900
1839
|
}
|
|
1901
|
-
function buildProcessForm(state,
|
|
1902
|
-
const
|
|
1840
|
+
function buildProcessForm(state, options = {}) {
|
|
1841
|
+
const applyInvalidSubmit = options.applyInvalidSubmit;
|
|
1903
1842
|
function validate(pathInput) {
|
|
1904
1843
|
const result = ref({
|
|
1905
1844
|
pending: true,
|
|
@@ -1997,7 +1936,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1997
1936
|
};
|
|
1998
1937
|
}
|
|
1999
1938
|
async function runRefinementValidation(data, path) {
|
|
2000
|
-
return await state.schema.validateAtPath(data, path);
|
|
1939
|
+
return stampFormKey(await state.schema.validateAtPath(data, path), state.formKey);
|
|
2001
1940
|
}
|
|
2002
1941
|
function composeWithDerivedBlank(refinement, scope) {
|
|
2003
1942
|
const blankErrors = collectScopedBlankErrors(state, scope);
|
|
@@ -2048,7 +1987,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
2048
1987
|
}
|
|
2049
1988
|
}
|
|
2050
1989
|
if (generationStillValid) {
|
|
2051
|
-
|
|
1990
|
+
applyInvalidSubmit?.();
|
|
2052
1991
|
}
|
|
2053
1992
|
if (onError !== void 0) {
|
|
2054
1993
|
try {
|
|
@@ -2065,7 +2004,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
2065
2004
|
await onSubmit(merged.data);
|
|
2066
2005
|
if (hasUserErrorEntries(state)) {
|
|
2067
2006
|
if (state.submissionGeneration.value === genAtEntry) {
|
|
2068
|
-
|
|
2007
|
+
applyInvalidSubmit?.();
|
|
2069
2008
|
}
|
|
2070
2009
|
return;
|
|
2071
2010
|
}
|
|
@@ -2083,7 +2022,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
2083
2022
|
if (segments === null) continue;
|
|
2084
2023
|
state.setUserErrorsForPath([...segments], bucket);
|
|
2085
2024
|
}
|
|
2086
|
-
|
|
2025
|
+
applyInvalidSubmit?.();
|
|
2087
2026
|
if (messageless) {
|
|
2088
2027
|
console.warn(
|
|
2089
2028
|
"[attaform] handleSubmit callback threw a non-Error value; throw an Error or a ValidationError ({ message, path? }) so the failure surfaces with a usable message."
|
|
@@ -2103,6 +2042,10 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
2103
2042
|
};
|
|
2104
2043
|
return { validate, parse, handleSubmit };
|
|
2105
2044
|
}
|
|
2045
|
+
function stampFormKey(verdict, formKey) {
|
|
2046
|
+
if (verdict.success) return { ...verdict, formKey };
|
|
2047
|
+
return verdict.data === void 0 ? { data: void 0, errors: verdict.errors, success: false, formKey } : { data: verdict.data, errors: verdict.errors, success: false, formKey };
|
|
2048
|
+
}
|
|
2106
2049
|
function toSegments(pathInput) {
|
|
2107
2050
|
return canonicalizePath(pathInput).segments;
|
|
2108
2051
|
}
|
|
@@ -2136,24 +2079,9 @@ function collectScopedBlankErrors(state, scope) {
|
|
|
2136
2079
|
}
|
|
2137
2080
|
return errors;
|
|
2138
2081
|
}
|
|
2139
|
-
function applyInvalidSubmitPolicy(state, formInstanceId, policy) {
|
|
2140
|
-
if (policy === "none") return;
|
|
2141
|
-
const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
|
|
2142
|
-
if (target === null) return;
|
|
2143
|
-
if (policy === "scroll-to-first-error") {
|
|
2144
|
-
target.element.scrollIntoView();
|
|
2145
|
-
return;
|
|
2146
|
-
}
|
|
2147
|
-
if (policy === "focus-first-error") {
|
|
2148
|
-
target.element.focus({ focusVisible: true });
|
|
2149
|
-
return;
|
|
2150
|
-
}
|
|
2151
|
-
target.element.scrollIntoView();
|
|
2152
|
-
target.element.focus({ preventScroll: true, focusVisible: true });
|
|
2153
|
-
}
|
|
2154
2082
|
|
|
2155
2083
|
const warnedRejections = /* @__PURE__ */ new WeakMap() ;
|
|
2156
|
-
function shouldWarnOnce
|
|
2084
|
+
function shouldWarnOnce(store, key) {
|
|
2157
2085
|
if (warnedRejections === null) return false;
|
|
2158
2086
|
let set = warnedRejections.get(store);
|
|
2159
2087
|
if (set === void 0) {
|
|
@@ -2229,12 +2157,12 @@ function walk(schema, store, path, value) {
|
|
|
2229
2157
|
function reportRejection(store, path, kind, accepted) {
|
|
2230
2158
|
const dotted = path.map((s) => String(s)).join(".") || "(root)";
|
|
2231
2159
|
const key = `${dotted}::${kind}`;
|
|
2232
|
-
if (!shouldWarnOnce
|
|
2160
|
+
if (!shouldWarnOnce(store, key)) return;
|
|
2233
2161
|
if (accepted.size === 0) {
|
|
2234
2162
|
console.warn(
|
|
2235
|
-
`[attaform] Cannot write to '${dotted}'
|
|
2163
|
+
`[attaform] Cannot write to '${dotted}': this path is not in your schema.
|
|
2236
2164
|
Fix: check for a typo in register('${dotted}'); it should match a leaf key in your schema.
|
|
2237
|
-
(If the path resolves to a never-typed schema, it explicitly admits no values
|
|
2165
|
+
(If the path resolves to a never-typed schema, it explicitly admits no values; relax the schema if intentional.)
|
|
2238
2166
|
The write was a no-op.`
|
|
2239
2167
|
);
|
|
2240
2168
|
return;
|
|
@@ -2242,7 +2170,7 @@ function reportRejection(store, path, kind, accepted) {
|
|
|
2242
2170
|
const expected = formatExpectedKinds(accepted);
|
|
2243
2171
|
if (kind === "string" && accepted.has("number")) {
|
|
2244
2172
|
console.warn(
|
|
2245
|
-
`[attaform] Cannot write a string to '${dotted}'
|
|
2173
|
+
`[attaform] Cannot write a string to '${dotted}': the schema expects ${expected}.
|
|
2246
2174
|
Fix: add type="number" to the input, OR use the .number modifier on v-register:
|
|
2247
2175
|
<input type="number" v-register="register('${dotted}')" />
|
|
2248
2176
|
<input v-register.number="register('${dotted}')" />
|
|
@@ -2251,7 +2179,7 @@ function reportRejection(store, path, kind, accepted) {
|
|
|
2251
2179
|
return;
|
|
2252
2180
|
}
|
|
2253
2181
|
console.warn(
|
|
2254
|
-
`[attaform] Cannot write a ${kind} to '${dotted}'
|
|
2182
|
+
`[attaform] Cannot write a ${kind} to '${dotted}': the schema expects ${expected}.
|
|
2255
2183
|
The write was a no-op.`
|
|
2256
2184
|
);
|
|
2257
2185
|
}
|
|
@@ -2262,56 +2190,9 @@ function formatExpectedKinds(accepted) {
|
|
|
2262
2190
|
return `one of: ${list.join(", ")}`;
|
|
2263
2191
|
}
|
|
2264
2192
|
|
|
2265
|
-
function defineCoercion(entry) {
|
|
2266
|
-
return entry;
|
|
2267
|
-
}
|
|
2268
2193
|
const IDENTITY = (v) => v;
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
defineCoercion({
|
|
2272
|
-
input: "string",
|
|
2273
|
-
output: "number",
|
|
2274
|
-
transform: (s) => {
|
|
2275
|
-
const trimmed = s.trim();
|
|
2276
|
-
if (trimmed === "") return { coerced: false };
|
|
2277
|
-
const n = Number(trimmed);
|
|
2278
|
-
if (!Number.isFinite(n)) return { coerced: false };
|
|
2279
|
-
return { coerced: true, value: n };
|
|
2280
|
-
}
|
|
2281
|
-
}),
|
|
2282
|
-
defineCoercion({
|
|
2283
|
-
input: "string",
|
|
2284
|
-
output: "boolean",
|
|
2285
|
-
transform: (s) => {
|
|
2286
|
-
const normalized = s.trim().toLowerCase();
|
|
2287
|
-
if (normalized === "true") return { coerced: true, value: true };
|
|
2288
|
-
if (normalized === "false") return { coerced: true, value: false };
|
|
2289
|
-
return { coerced: false };
|
|
2290
|
-
}
|
|
2291
|
-
})
|
|
2292
|
-
];
|
|
2293
|
-
function resolveCoercionIndex(config) {
|
|
2294
|
-
if (config === false) return EMPTY_INDEX;
|
|
2295
|
-
const rules = config === void 0 || config === true ? defaultCoercionRules : config;
|
|
2296
|
-
return indexRules(rules);
|
|
2297
|
-
}
|
|
2298
|
-
function indexRules(rules) {
|
|
2299
|
-
const idx = /* @__PURE__ */ new Map();
|
|
2300
|
-
for (const entry of rules) {
|
|
2301
|
-
const candidate = entry;
|
|
2302
|
-
if (candidate === null || typeof candidate !== "object" || typeof candidate.transform !== "function") {
|
|
2303
|
-
{
|
|
2304
|
-
console.warn("[attaform] coercion entry missing or invalid `transform` \u2014 skipped.");
|
|
2305
|
-
}
|
|
2306
|
-
continue;
|
|
2307
|
-
}
|
|
2308
|
-
const key = `${entry.input}->${entry.output}`;
|
|
2309
|
-
if (idx.has(key) && true) {
|
|
2310
|
-
console.warn(`[attaform] duplicate coercion rule for '${key}' \u2014 last entry wins.`);
|
|
2311
|
-
}
|
|
2312
|
-
idx.set(key, entry);
|
|
2313
|
-
}
|
|
2314
|
-
return idx;
|
|
2194
|
+
function resolveCoerceEnabled(config) {
|
|
2195
|
+
return config !== false;
|
|
2315
2196
|
}
|
|
2316
2197
|
function memberSlimTypes(schema, segments, accepted) {
|
|
2317
2198
|
if (accepted.has("array")) return schema.getSlimPrimitiveTypesAtPath([...segments, 0]);
|
|
@@ -2320,119 +2201,84 @@ function memberSlimTypes(schema, segments, accepted) {
|
|
|
2320
2201
|
}
|
|
2321
2202
|
return void 0;
|
|
2322
2203
|
}
|
|
2323
|
-
function buildCoerceFn(schema, segments,
|
|
2324
|
-
if (
|
|
2325
|
-
if (index.size === 0) return IDENTITY;
|
|
2204
|
+
function buildCoerceFn(schema, segments, enabled) {
|
|
2205
|
+
if (!enabled) return IDENTITY;
|
|
2326
2206
|
const accepted = schema.getSlimPrimitiveTypesAtPath(segments);
|
|
2327
2207
|
const elementAccepted = memberSlimTypes(schema, segments, accepted);
|
|
2328
|
-
return (value) => coerceValue(value, accepted, elementAccepted
|
|
2208
|
+
return (value) => coerceValue(value, accepted, elementAccepted);
|
|
2329
2209
|
}
|
|
2330
|
-
function buildElementCoerceFn(schema, segments,
|
|
2331
|
-
if (
|
|
2332
|
-
if (index.size === 0) return void 0;
|
|
2210
|
+
function buildElementCoerceFn(schema, segments, enabled) {
|
|
2211
|
+
if (!enabled) return void 0;
|
|
2333
2212
|
const accepted = schema.getSlimPrimitiveTypesAtPath(segments);
|
|
2334
2213
|
const elementAccepted = memberSlimTypes(schema, segments, accepted);
|
|
2335
2214
|
if (elementAccepted === void 0) return void 0;
|
|
2336
|
-
return (value) => coerceScalar(value, elementAccepted
|
|
2215
|
+
return (value) => coerceScalar(value, elementAccepted);
|
|
2337
2216
|
}
|
|
2338
2217
|
function pickScalarTarget(accepted) {
|
|
2339
2218
|
if (accepted.has("string")) return null;
|
|
2340
2219
|
if (accepted.has("number")) return "number";
|
|
2341
2220
|
if (accepted.has("boolean")) return "boolean";
|
|
2342
|
-
if (accepted.has("bigint")) return "bigint";
|
|
2343
2221
|
return null;
|
|
2344
2222
|
}
|
|
2345
|
-
|
|
2346
|
-
const
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
if (
|
|
2355
|
-
|
|
2356
|
-
return true;
|
|
2223
|
+
function toNumber(source) {
|
|
2224
|
+
const trimmed = source.trim();
|
|
2225
|
+
if (trimmed === "") return source;
|
|
2226
|
+
const n = Number(trimmed);
|
|
2227
|
+
return Number.isFinite(n) ? n : source;
|
|
2228
|
+
}
|
|
2229
|
+
function toBoolean(source) {
|
|
2230
|
+
const normalized = source.trim().toLowerCase();
|
|
2231
|
+
if (normalized === "true") return true;
|
|
2232
|
+
if (normalized === "false") return false;
|
|
2233
|
+
return source;
|
|
2357
2234
|
}
|
|
2358
|
-
function coerceScalar(value, accepted
|
|
2235
|
+
function coerceScalar(value, accepted) {
|
|
2359
2236
|
if (accepted.size === 0) return value;
|
|
2360
2237
|
const sourceKind = slimKindOf(value);
|
|
2361
2238
|
if (accepted.has(sourceKind)) return value;
|
|
2239
|
+
if (sourceKind !== "string") return value;
|
|
2362
2240
|
const target = pickScalarTarget(accepted);
|
|
2363
|
-
if (target ===
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
let result;
|
|
2367
|
-
try {
|
|
2368
|
-
result = entry.transform(value);
|
|
2369
|
-
} catch (err) {
|
|
2370
|
-
if (shouldWarnOnce(`${entry.input}->${entry.output}::throw`)) {
|
|
2371
|
-
console.warn(
|
|
2372
|
-
`[attaform] coercion '${entry.input}->${entry.output}' threw \u2014 write passes through.`,
|
|
2373
|
-
err
|
|
2374
|
-
);
|
|
2375
|
-
}
|
|
2376
|
-
return value;
|
|
2377
|
-
}
|
|
2378
|
-
if (!result.coerced) return value;
|
|
2379
|
-
const returnedKind = slimKindOf(result.value);
|
|
2380
|
-
if (returnedKind !== entry.output) {
|
|
2381
|
-
if (shouldWarnOnce(`${entry.input}->${entry.output}::wrong-kind:${returnedKind}`)) {
|
|
2382
|
-
console.warn(
|
|
2383
|
-
`[attaform] coercion '${entry.input}->${entry.output}' produced a ${returnedKind} \u2014 write passes through.`
|
|
2384
|
-
);
|
|
2385
|
-
}
|
|
2386
|
-
return value;
|
|
2387
|
-
}
|
|
2388
|
-
if (entry.output === "number" && !Number.isFinite(result.value)) {
|
|
2389
|
-
if (shouldWarnOnce(`${entry.input}->${entry.output}::nan`)) {
|
|
2390
|
-
console.warn(
|
|
2391
|
-
`[attaform] coercion '${entry.input}->${entry.output}' produced a non-finite number \u2014 write passes through.`
|
|
2392
|
-
);
|
|
2393
|
-
}
|
|
2394
|
-
return value;
|
|
2395
|
-
}
|
|
2396
|
-
return result.value;
|
|
2241
|
+
if (target === "number") return toNumber(value);
|
|
2242
|
+
if (target === "boolean") return toBoolean(value);
|
|
2243
|
+
return value;
|
|
2397
2244
|
}
|
|
2398
|
-
function coerceArrayMembers(arr, elementAccepted
|
|
2245
|
+
function coerceArrayMembers(arr, elementAccepted) {
|
|
2399
2246
|
let changed = false;
|
|
2400
2247
|
const out = [];
|
|
2401
2248
|
for (const el of arr) {
|
|
2402
|
-
const next = coerceScalar(el, elementAccepted
|
|
2249
|
+
const next = coerceScalar(el, elementAccepted);
|
|
2403
2250
|
if (next !== el) changed = true;
|
|
2404
2251
|
out.push(next);
|
|
2405
2252
|
}
|
|
2406
2253
|
return changed ? out : arr;
|
|
2407
2254
|
}
|
|
2408
|
-
function coerceSetMembers(set, elementAccepted
|
|
2255
|
+
function coerceSetMembers(set, elementAccepted) {
|
|
2409
2256
|
let changed = false;
|
|
2410
2257
|
const out = [];
|
|
2411
2258
|
for (const el of set) {
|
|
2412
|
-
const next = coerceScalar(el, elementAccepted
|
|
2259
|
+
const next = coerceScalar(el, elementAccepted);
|
|
2413
2260
|
if (next !== el) changed = true;
|
|
2414
2261
|
out.push(next);
|
|
2415
2262
|
}
|
|
2416
2263
|
return changed ? new Set(out) : set;
|
|
2417
2264
|
}
|
|
2418
|
-
function coerceValue(value, accepted, elementAccepted
|
|
2265
|
+
function coerceValue(value, accepted, elementAccepted) {
|
|
2419
2266
|
if (Array.isArray(value)) {
|
|
2420
2267
|
if (!accepted.has("array") || elementAccepted === void 0) return value;
|
|
2421
|
-
return coerceArrayMembers(value, elementAccepted
|
|
2268
|
+
return coerceArrayMembers(value, elementAccepted);
|
|
2422
2269
|
}
|
|
2423
2270
|
if (value instanceof Set) {
|
|
2424
2271
|
if (!accepted.has("set") || elementAccepted === void 0) return value;
|
|
2425
|
-
return coerceSetMembers(value, elementAccepted
|
|
2272
|
+
return coerceSetMembers(value, elementAccepted);
|
|
2426
2273
|
}
|
|
2427
|
-
return coerceScalar(value, accepted
|
|
2274
|
+
return coerceScalar(value, accepted);
|
|
2428
2275
|
}
|
|
2429
2276
|
|
|
2430
2277
|
const warnedMultiRootHosts = /* @__PURE__ */ new Set();
|
|
2431
2278
|
const EMPTY_TRANSFORMS = Object.freeze([]);
|
|
2432
2279
|
function buildRegister(state, formInstanceId, instanceConfig) {
|
|
2433
|
-
const
|
|
2280
|
+
const coerceEnabled = instanceConfig?.coerce !== void 0 ? resolveCoerceEnabled(instanceConfig.coerce) : state.coerceEnabled;
|
|
2434
2281
|
const instanceMeta = instanceConfig?.instanceMeta;
|
|
2435
|
-
const formAutoAria = instanceConfig?.autoAria ?? true;
|
|
2436
2282
|
const getDisplayStateAt = instanceConfig?.getDisplayStateAt;
|
|
2437
2283
|
const withInstanceMeta = (meta) => {
|
|
2438
2284
|
if (instanceMeta === void 0) return meta;
|
|
@@ -2472,16 +2318,15 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2472
2318
|
const coerce = buildCoerceFn(
|
|
2473
2319
|
state.schema,
|
|
2474
2320
|
segments,
|
|
2475
|
-
|
|
2321
|
+
coerceEnabled
|
|
2476
2322
|
);
|
|
2477
2323
|
const coerceElement = buildElementCoerceFn(
|
|
2478
2324
|
state.schema,
|
|
2479
2325
|
segments,
|
|
2480
|
-
|
|
2326
|
+
coerceEnabled
|
|
2481
2327
|
);
|
|
2482
2328
|
const { aria } = computeFieldIdentity(formInstanceId, state.formKey, pathKey);
|
|
2483
2329
|
const isRequired = state.schema.isRequiredAtPath(segments);
|
|
2484
|
-
const ariaEnabled = options?.autoAria ?? formAutoAria;
|
|
2485
2330
|
const ariaDisplayState = getDisplayStateAt !== void 0 ? computed(() => getDisplayStateAt(segments)) : void 0;
|
|
2486
2331
|
const markBlank = () => state.setValueAtPath(segments, slimDefault, withInstanceMeta({ blank: true }));
|
|
2487
2332
|
const internalRv = {
|
|
@@ -2539,7 +2384,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2539
2384
|
}
|
|
2540
2385
|
return accepted;
|
|
2541
2386
|
},
|
|
2542
|
-
// Called by the `
|
|
2387
|
+
// Called by the `vRegisterHintTransform` compile-time transform's wrapping
|
|
2543
2388
|
// IIFE on every server-side render of `<element v-register="…">`.
|
|
2544
2389
|
// Without it, every SSR'd FieldState serialises `connected: false`
|
|
2545
2390
|
// (because Vue skips directive lifecycle during SSR) and the client
|
|
@@ -2568,12 +2413,12 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2568
2413
|
}
|
|
2569
2414
|
return false;
|
|
2570
2415
|
},
|
|
2571
|
-
// --- Async transform lifecycle (internal; the directive's
|
|
2572
|
-
//
|
|
2573
|
-
//
|
|
2574
|
-
//
|
|
2575
|
-
//
|
|
2576
|
-
//
|
|
2416
|
+
// --- Async transform lifecycle (internal; the directive's deferred
|
|
2417
|
+
// orchestrator is the only legitimate consumer). Thin path-bound
|
|
2418
|
+
// delegates to the store's per-path token and counter machinery,
|
|
2419
|
+
// the same pattern as `markBlank` and `setValueWithInternalPath`, so
|
|
2420
|
+
// the directive (which holds this RegisterValue and never the store)
|
|
2421
|
+
// can drive the busy / discard / error bookkeeping. ---
|
|
2577
2422
|
beginTransform: (holder) => state.beginTransform(pathKey, holder),
|
|
2578
2423
|
isCurrentTransform: (token) => state.isCurrentTransform(pathKey, token),
|
|
2579
2424
|
endTransform: (token) => state.endTransform(pathKey, token),
|
|
@@ -2586,10 +2431,10 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2586
2431
|
return (state.fieldTransformCounts.get(pathKey) ?? 0) > 0;
|
|
2587
2432
|
},
|
|
2588
2433
|
path: pathKey,
|
|
2589
|
-
// Frozen so a wrapper component can pass `rv.segments`
|
|
2590
|
-
//
|
|
2591
|
-
//
|
|
2592
|
-
//
|
|
2434
|
+
// Frozen so a wrapper component can pass `rv.segments` straight to
|
|
2435
|
+
// `form.fields(...)` with no defensive copy, and so neither a test
|
|
2436
|
+
// fixture nor downstream code can mutate the canonical segment list
|
|
2437
|
+
// out from under the directive.
|
|
2593
2438
|
segments: Object.freeze(segments.slice()),
|
|
2594
2439
|
formKey: state.formKey,
|
|
2595
2440
|
formInstanceId,
|
|
@@ -2601,7 +2446,6 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2601
2446
|
// --- Aria (internal; consumed by the directive) ---
|
|
2602
2447
|
aria,
|
|
2603
2448
|
isRequired,
|
|
2604
|
-
ariaEnabled,
|
|
2605
2449
|
...ariaDisplayState !== void 0 ? { ariaDisplayState } : {}
|
|
2606
2450
|
};
|
|
2607
2451
|
return shallowReadonly(internalRv);
|
|
@@ -2765,11 +2609,50 @@ const DERIVED_DISPLAY_KEYS = /* @__PURE__ */ new Set([
|
|
|
2765
2609
|
"firstError",
|
|
2766
2610
|
"firstOwnError"
|
|
2767
2611
|
]);
|
|
2768
|
-
const
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2612
|
+
const META_KEYS = [
|
|
2613
|
+
...FIELD_STATE_KEYS,
|
|
2614
|
+
"submitting",
|
|
2615
|
+
"submissionAttempts",
|
|
2616
|
+
"departAttempts",
|
|
2617
|
+
"submitError",
|
|
2618
|
+
"submitted",
|
|
2619
|
+
"instanceId",
|
|
2620
|
+
"errorCount"
|
|
2621
|
+
];
|
|
2622
|
+
const BASE_META_KEYS = META_KEYS.filter((k) => !DERIVED_DISPLAY_KEYS.has(k));
|
|
2623
|
+
let INERT_HISTORY;
|
|
2624
|
+
function inertHistory() {
|
|
2625
|
+
INERT_HISTORY ?? (INERT_HISTORY = readonly({
|
|
2626
|
+
undo: () => false,
|
|
2627
|
+
redo: () => false,
|
|
2628
|
+
clear: () => {
|
|
2629
|
+
},
|
|
2630
|
+
canUndo: false,
|
|
2631
|
+
canRedo: false,
|
|
2632
|
+
size: 0
|
|
2633
|
+
}));
|
|
2634
|
+
return INERT_HISTORY;
|
|
2635
|
+
}
|
|
2636
|
+
const denyWrite = () => false;
|
|
2637
|
+
function buildMetaProxy(keys, read) {
|
|
2638
|
+
const target = {};
|
|
2639
|
+
const owns = new Set(keys);
|
|
2640
|
+
return new Proxy(target, {
|
|
2641
|
+
get: (_, key) => owns.has(key) ? read(key) : Reflect.get(target, key),
|
|
2642
|
+
has: (_, key) => owns.has(key) || Reflect.has(target, key),
|
|
2643
|
+
// Safe to hand back the same array every time: the spec copies a trap's
|
|
2644
|
+
// key list before using it.
|
|
2645
|
+
ownKeys: () => keys,
|
|
2646
|
+
getOwnPropertyDescriptor(_, key) {
|
|
2647
|
+
if (!owns.has(key)) return Reflect.getOwnPropertyDescriptor(target, key);
|
|
2648
|
+
return { configurable: true, enumerable: true, value: read(key), writable: false };
|
|
2649
|
+
},
|
|
2650
|
+
// A getter with no setter throws on assignment under strict mode, which
|
|
2651
|
+
// every module here is; `false` keeps that.
|
|
2652
|
+
set: denyWrite,
|
|
2653
|
+
deleteProperty: denyWrite,
|
|
2654
|
+
defineProperty: denyWrite
|
|
2655
|
+
});
|
|
2773
2656
|
}
|
|
2774
2657
|
function buildFormApi(state, formInstanceId, options = {}) {
|
|
2775
2658
|
const instanceBag = pickDefined({
|
|
@@ -2796,32 +2679,26 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2796
2679
|
const rootBaseComputed = computed(
|
|
2797
2680
|
() => buildContainerFieldStateBase(state, ROOT_PATH, ROOT_PATH_KEY, formInstanceId).base
|
|
2798
2681
|
);
|
|
2799
|
-
const metaBase = {
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2682
|
+
const metaBase = buildMetaProxy(BASE_META_KEYS, (key) => {
|
|
2683
|
+
switch (key) {
|
|
2684
|
+
case "instanceId":
|
|
2685
|
+
return formInstanceId;
|
|
2686
|
+
case "errorCount":
|
|
2687
|
+
return rootBaseComputed.value.errors.length;
|
|
2688
|
+
case "submitting":
|
|
2689
|
+
return state.submitting.value;
|
|
2690
|
+
case "submissionAttempts":
|
|
2691
|
+
return state.submissionAttempts.value;
|
|
2692
|
+
case "departAttempts":
|
|
2693
|
+
return state.departAttempts.value;
|
|
2694
|
+
case "submitError":
|
|
2695
|
+
return state.submitError.value;
|
|
2696
|
+
case "submitted":
|
|
2697
|
+
return state.submitted.value;
|
|
2698
|
+
default:
|
|
2699
|
+
return rootBaseComputed.value[key];
|
|
2815
2700
|
}
|
|
2816
|
-
};
|
|
2817
|
-
for (const k of BASE_MIRROR_KEYS) {
|
|
2818
|
-
defineGetter(
|
|
2819
|
-
metaBase,
|
|
2820
|
-
k,
|
|
2821
|
-
() => rootBaseComputed.value[k]
|
|
2822
|
-
);
|
|
2823
|
-
}
|
|
2824
|
-
defineGetter(metaBase, "errorCount", () => rootBaseComputed.value.errors.length);
|
|
2701
|
+
});
|
|
2825
2702
|
const getFormMetaBase = () => {
|
|
2826
2703
|
void state.submitting.value;
|
|
2827
2704
|
void state.submissionAttempts.value;
|
|
@@ -2830,28 +2707,41 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2830
2707
|
void state.submitted.value;
|
|
2831
2708
|
return metaBase;
|
|
2832
2709
|
};
|
|
2833
|
-
const
|
|
2834
|
-
const pathSweep = createDynamicPathSweep(state);
|
|
2710
|
+
const pathSweep = state.pathSweep;
|
|
2835
2711
|
const getRootFieldStateAt = buildFieldStateAccessor(
|
|
2836
2712
|
state,
|
|
2837
2713
|
formInstanceId,
|
|
2838
2714
|
getFormMetaBase,
|
|
2839
|
-
pathSweep
|
|
2840
|
-
fieldStateAccessorOptions
|
|
2715
|
+
pathSweep
|
|
2841
2716
|
);
|
|
2842
2717
|
const getDisplayStateAt = (segments) => getRootFieldStateAt(segments).value.displayState;
|
|
2843
2718
|
const registerConfig = {
|
|
2844
|
-
...pickDefined({ instanceMeta, coerce: options.coerce
|
|
2719
|
+
...pickDefined({ instanceMeta, coerce: options.coerce }),
|
|
2845
2720
|
getDisplayStateAt
|
|
2846
2721
|
};
|
|
2847
2722
|
const register = buildRegister(state, formInstanceId, registerConfig);
|
|
2848
|
-
const
|
|
2849
|
-
|
|
2723
|
+
const focusFirstError = (options2) => {
|
|
2724
|
+
const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
|
|
2725
|
+
if (target === null) return false;
|
|
2726
|
+
target.element.focus({ focusVisible: true, ...options2 });
|
|
2727
|
+
return true;
|
|
2728
|
+
};
|
|
2729
|
+
const scrollToFirstError = (options2) => {
|
|
2730
|
+
const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
|
|
2731
|
+
if (target === null) return false;
|
|
2732
|
+
target.element.scrollIntoView(options2);
|
|
2733
|
+
return true;
|
|
2734
|
+
};
|
|
2735
|
+
const applyInvalidSubmitPolicyPublic = () => {
|
|
2736
|
+
if (options.focusOnInvalidSubmit !== false) focusFirstError();
|
|
2737
|
+
};
|
|
2850
2738
|
const {
|
|
2851
2739
|
validate: validateBuilt,
|
|
2852
2740
|
parse: parseBuilt,
|
|
2853
2741
|
handleSubmit
|
|
2854
|
-
} = buildProcessForm(state,
|
|
2742
|
+
} = buildProcessForm(state, {
|
|
2743
|
+
applyInvalidSubmit: applyInvalidSubmitPolicyPublic
|
|
2744
|
+
});
|
|
2855
2745
|
const validate = (pathInput) => validateBuilt(pathInput);
|
|
2856
2746
|
const parse = (arg1, arg2) => {
|
|
2857
2747
|
const isPathArg = typeof arg1 === "string" || Array.isArray(arg1);
|
|
@@ -2979,28 +2869,26 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2979
2869
|
() => state.firstValidationDone.value && state.errorCells.size === 0 && state.derivedBlankErrors.value.size === 0 && !validating.value
|
|
2980
2870
|
);
|
|
2981
2871
|
const history = options.history;
|
|
2982
|
-
const formHistory = readonly(
|
|
2872
|
+
const formHistory = history === void 0 ? inertHistory() : readonly(
|
|
2983
2873
|
reactive({
|
|
2984
|
-
undo: history
|
|
2985
|
-
redo: history
|
|
2986
|
-
clear: history
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
size: history?.historySize ?? computed(() => 0)
|
|
2874
|
+
undo: history.undo,
|
|
2875
|
+
redo: history.redo,
|
|
2876
|
+
clear: history.clear,
|
|
2877
|
+
canUndo: history.canUndo,
|
|
2878
|
+
canRedo: history.canRedo,
|
|
2879
|
+
size: history.historySize
|
|
2991
2880
|
})
|
|
2992
2881
|
);
|
|
2993
2882
|
const metaErrors = computed(
|
|
2994
|
-
() => aggregateErrorsAt(state, [])
|
|
2883
|
+
() => aggregateErrorsAt(state, [], ROOT_PATH_KEY)
|
|
2995
2884
|
);
|
|
2996
2885
|
const rootFieldState = getRootFieldStateAt([]);
|
|
2997
|
-
const
|
|
2998
|
-
// Whole-form work signals compose the LIFECYCLE counters with the
|
|
2999
|
-
//
|
|
3000
|
-
//
|
|
3001
|
-
//
|
|
3002
|
-
//
|
|
3003
|
-
// unfiltered whole-form aggregate.
|
|
2886
|
+
const metaOwn = {
|
|
2887
|
+
// Whole-form work signals compose the LIFECYCLE counters with the per-leaf
|
|
2888
|
+
// rollup: a submit-time validate shows up in `activeValidations` and a
|
|
2889
|
+
// per-field debounced validator in `fieldValidationCounts`, and either
|
|
2890
|
+
// flips the flag. `valid` keeps the form-level mount gate, and `errors` is
|
|
2891
|
+
// the unfiltered whole-form aggregate.
|
|
3004
2892
|
validating: computed(
|
|
3005
2893
|
() => state.activeValidations.value > 0 || rootFieldState.value.validating
|
|
3006
2894
|
),
|
|
@@ -3012,7 +2900,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3012
2900
|
busy: computed(
|
|
3013
2901
|
() => state.activeValidations.value > 0 || state.activeTransforms.value > 0 || rootFieldState.value.validating || rootFieldState.value.transforming
|
|
3014
2902
|
),
|
|
3015
|
-
// Lifecycle
|
|
2903
|
+
// Lifecycle, form-level only and never on FieldState.
|
|
3016
2904
|
submitting,
|
|
3017
2905
|
submissionAttempts,
|
|
3018
2906
|
departAttempts,
|
|
@@ -3023,14 +2911,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3023
2911
|
// identifier).
|
|
3024
2912
|
instanceId: formInstanceId
|
|
3025
2913
|
};
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
);
|
|
3032
|
-
}
|
|
3033
|
-
defineGetter(metaTarget, "errorCount", () => metaErrors.value.length);
|
|
2914
|
+
const metaTarget = buildMetaProxy(META_KEYS, (key) => {
|
|
2915
|
+
if (key === "errorCount") return metaErrors.value.length;
|
|
2916
|
+
if (Object.hasOwn(metaOwn, key)) return metaOwn[key];
|
|
2917
|
+
return rootFieldState.value[key];
|
|
2918
|
+
});
|
|
3034
2919
|
const formMeta = readonly(reactive(metaTarget));
|
|
3035
2920
|
const reset = (nextDefaultValues) => {
|
|
3036
2921
|
if (nextDefaultValues === void 0) {
|
|
@@ -3070,21 +2955,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3070
2955
|
} catch {
|
|
3071
2956
|
}
|
|
3072
2957
|
}
|
|
3073
|
-
const focusFirstError = (options2) => {
|
|
3074
|
-
const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
|
|
3075
|
-
if (target === null) return false;
|
|
3076
|
-
target.element.focus({ focusVisible: true, ...options2 });
|
|
3077
|
-
return true;
|
|
3078
|
-
};
|
|
3079
|
-
const scrollToFirstError = (options2) => {
|
|
3080
|
-
const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
|
|
3081
|
-
if (target === null) return false;
|
|
3082
|
-
target.element.scrollIntoView(options2);
|
|
3083
|
-
return true;
|
|
3084
|
-
};
|
|
3085
|
-
const applyInvalidSubmitPolicyPublic = (policy) => {
|
|
3086
|
-
applyInvalidSubmitPolicy(state, formInstanceId, policy ?? defaultInvalidSubmitPolicy);
|
|
3087
|
-
};
|
|
3088
2958
|
const fieldArrays = buildFieldArrayApi(state);
|
|
3089
2959
|
const blankPathsView = computed(() => {
|
|
3090
2960
|
const keys = /* @__PURE__ */ new Set();
|
|
@@ -3147,10 +3017,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3147
3017
|
}
|
|
3148
3018
|
const api = {
|
|
3149
3019
|
handleSubmit: gated(handleSubmit),
|
|
3150
|
-
//
|
|
3151
|
-
// reactive containers (`meta`, `history`, `blankPaths`) are exposed
|
|
3152
|
-
//
|
|
3153
|
-
//
|
|
3020
|
+
// The callable readonly Proxies (`values`, `fields`, `errors`) and the
|
|
3021
|
+
// reactive containers (`meta`, `history`, `blankPaths`) are exposed through
|
|
3022
|
+
// getters, so reading one activates the form on first touch. Each
|
|
3023
|
+
// underlying object is identity-stable across reads.
|
|
3154
3024
|
get values() {
|
|
3155
3025
|
void state.activate();
|
|
3156
3026
|
return valuesProxy;
|
|
@@ -3167,7 +3037,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3167
3037
|
key: state.formKey,
|
|
3168
3038
|
// Auto-unwrapping views over the per-store async-defaults lifecycle
|
|
3169
3039
|
// refs (see FormStore.hydrating / hydrateError). Reading either
|
|
3170
|
-
// activates the form
|
|
3040
|
+
// activates the form: observing factory state implies use.
|
|
3171
3041
|
get hydrating() {
|
|
3172
3042
|
void state.activate();
|
|
3173
3043
|
return state.hydrating.value;
|
|
@@ -3176,20 +3046,18 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3176
3046
|
void state.activate();
|
|
3177
3047
|
return state.hydrateError.value;
|
|
3178
3048
|
},
|
|
3179
|
-
// Orthogonal to `hydrating` and `hydrateError
|
|
3180
|
-
//
|
|
3181
|
-
// resolved
|
|
3182
|
-
// refetches even when those
|
|
3183
|
-
//
|
|
3184
|
-
// `hydrateError` surfaces the refresh failure.
|
|
3049
|
+
// Orthogonal to `hydrating` and `hydrateError`. `ready` flips true once
|
|
3050
|
+
// defaults are applied, whether sync at construction or from a factory
|
|
3051
|
+
// that resolved, and it is a one-way latch: it stays true through later
|
|
3052
|
+
// refetches even when those fail, so a stale-while-revalidate UI keeps
|
|
3053
|
+
// rendering the prior values while `hydrateError` surfaces the failure.
|
|
3185
3054
|
get ready() {
|
|
3186
3055
|
void state.activate();
|
|
3187
3056
|
return state.defaultsResolved.value;
|
|
3188
3057
|
},
|
|
3189
|
-
// `rehydrate` and `activate` are
|
|
3190
|
-
//
|
|
3191
|
-
//
|
|
3192
|
-
// so they call `state` directly.
|
|
3058
|
+
// `rehydrate` and `activate` are activation entry points themselves and
|
|
3059
|
+
// fire the factory by design, so wrapping them in `gated` would
|
|
3060
|
+
// double-fire. They call `state` directly.
|
|
3193
3061
|
rehydrate: () => state.rehydrate(),
|
|
3194
3062
|
activate: () => state.activate(),
|
|
3195
3063
|
get errors() {
|
|
@@ -3259,6 +3127,45 @@ function shortenSourceFrame(frame) {
|
|
|
3259
3127
|
return `(${path}:${line})`;
|
|
3260
3128
|
}
|
|
3261
3129
|
|
|
3130
|
+
const SWEEP_SLICE = 8;
|
|
3131
|
+
function createDynamicPathSweep(deps) {
|
|
3132
|
+
const dynamicKeys = /* @__PURE__ */ new Set();
|
|
3133
|
+
const evictors = [];
|
|
3134
|
+
const isDynamicPath = (segments) => {
|
|
3135
|
+
for (let i = 0; i < segments.length; i++) {
|
|
3136
|
+
if (typeof segments[i] === "number") return true;
|
|
3137
|
+
if (!deps.isFixedObjectAtPath(segments.slice(0, i))) return true;
|
|
3138
|
+
}
|
|
3139
|
+
return false;
|
|
3140
|
+
};
|
|
3141
|
+
let sweepCursor = null;
|
|
3142
|
+
deps.onFormChange((next) => {
|
|
3143
|
+
if (dynamicKeys.size === 0) return;
|
|
3144
|
+
const raw = toRaw(next);
|
|
3145
|
+
for (let i = 0; i < SWEEP_SLICE; i++) {
|
|
3146
|
+
sweepCursor ?? (sweepCursor = dynamicKeys.values());
|
|
3147
|
+
const step = sweepCursor.next();
|
|
3148
|
+
if (step.done === true) {
|
|
3149
|
+
sweepCursor = null;
|
|
3150
|
+
break;
|
|
3151
|
+
}
|
|
3152
|
+
const segments = segmentsForPathKey(step.value);
|
|
3153
|
+
if (segments !== null && hasAtPath(raw, segments)) continue;
|
|
3154
|
+
dynamicKeys.delete(step.value);
|
|
3155
|
+
for (const evict of evictors) evict(step.value);
|
|
3156
|
+
}
|
|
3157
|
+
});
|
|
3158
|
+
return {
|
|
3159
|
+
track(segments, key) {
|
|
3160
|
+
if (dynamicKeys.has(key)) return;
|
|
3161
|
+
if (isDynamicPath(segments)) dynamicKeys.add(key);
|
|
3162
|
+
},
|
|
3163
|
+
onEvict(evict) {
|
|
3164
|
+
evictors.push(evict);
|
|
3165
|
+
}
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3262
3169
|
const IDLE = Object.freeze({ display: "idle" });
|
|
3263
3170
|
const MAX_DELAY = 2147483647;
|
|
3264
3171
|
const PENDING_LIVENESS_MS = 250;
|
|
@@ -3352,6 +3259,58 @@ function createDisplayEngine(ssr) {
|
|
|
3352
3259
|
return engine;
|
|
3353
3260
|
}
|
|
3354
3261
|
|
|
3262
|
+
function buildErrorPathIndex(cells, blank) {
|
|
3263
|
+
const keys = /* @__PURE__ */ new Set();
|
|
3264
|
+
for (const [key, cell] of cells) {
|
|
3265
|
+
if (cell.schema.length > 0 || cell.user.length > 0) keys.add(key);
|
|
3266
|
+
}
|
|
3267
|
+
for (const [key, list] of blank) {
|
|
3268
|
+
if (list.length > 0) keys.add(key);
|
|
3269
|
+
}
|
|
3270
|
+
if (keys.size === 0) return EMPTY_INDEX;
|
|
3271
|
+
const entries = [];
|
|
3272
|
+
for (const key of keys) {
|
|
3273
|
+
const segments = segmentsForPathKey(key);
|
|
3274
|
+
if (segments !== null) entries.push({ key, segments });
|
|
3275
|
+
}
|
|
3276
|
+
entries.sort(compareByKey);
|
|
3277
|
+
return entries;
|
|
3278
|
+
}
|
|
3279
|
+
const EMPTY_INDEX = Object.freeze([]);
|
|
3280
|
+
function compareByKey(a, b) {
|
|
3281
|
+
return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
|
|
3282
|
+
}
|
|
3283
|
+
function windowUnder(index, prefix) {
|
|
3284
|
+
if (prefix.length === 0 || index.length === 0) return index;
|
|
3285
|
+
const prefixKey = keyForSegments(prefix).key;
|
|
3286
|
+
const marker = `${prefixKey.slice(0, -1)},`;
|
|
3287
|
+
const start = lowerBound(index, marker);
|
|
3288
|
+
if (start >= index.length) return EMPTY_INDEX;
|
|
3289
|
+
let end = start;
|
|
3290
|
+
for (; end < index.length; end += 1) {
|
|
3291
|
+
const entry = index[end];
|
|
3292
|
+
if (entry === void 0 || entry.key > prefixKey) break;
|
|
3293
|
+
}
|
|
3294
|
+
return start === 0 && end === index.length ? index : index.slice(start, end);
|
|
3295
|
+
}
|
|
3296
|
+
function isSameWindow(a, b) {
|
|
3297
|
+
if (a.length !== b.length) return false;
|
|
3298
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
3299
|
+
if (a[i]?.key !== b[i]?.key) return false;
|
|
3300
|
+
}
|
|
3301
|
+
return true;
|
|
3302
|
+
}
|
|
3303
|
+
function lowerBound(index, target) {
|
|
3304
|
+
let lo = 0;
|
|
3305
|
+
let hi = index.length;
|
|
3306
|
+
while (lo < hi) {
|
|
3307
|
+
const mid = lo + hi >>> 1;
|
|
3308
|
+
if ((index[mid]?.key ?? target) < target) lo = mid + 1;
|
|
3309
|
+
else hi = mid;
|
|
3310
|
+
}
|
|
3311
|
+
return lo;
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3355
3314
|
function mergeSparseHydration(schemaDefaults, sparse, schema) {
|
|
3356
3315
|
return mergeDeep(schemaDefaults, sparse, [], schema);
|
|
3357
3316
|
}
|
|
@@ -3578,16 +3537,15 @@ function walkAuthoredFromSchemaDiff(withDefaults, withoutDefaults, prefix, out)
|
|
|
3578
3537
|
out.add(canonicalizePath(prefix).key);
|
|
3579
3538
|
}
|
|
3580
3539
|
}
|
|
3581
|
-
function computeBaselineResponse(schema,
|
|
3540
|
+
function computeBaselineResponse(schema, source) {
|
|
3582
3541
|
const completed = source === void 0 ? void 0 : mergeStructural(schema, [], source);
|
|
3583
3542
|
return schema.getDefaultValues({
|
|
3584
3543
|
useDefaultSchemaValues: true,
|
|
3585
|
-
constraints: completed
|
|
3586
|
-
strict
|
|
3544
|
+
constraints: completed
|
|
3587
3545
|
});
|
|
3588
3546
|
}
|
|
3589
|
-
function initialFirstValidationGate(schema
|
|
3590
|
-
return
|
|
3547
|
+
function initialFirstValidationGate(schema) {
|
|
3548
|
+
return schema.needsAsyncValidation?.() !== true;
|
|
3591
3549
|
}
|
|
3592
3550
|
function seedOriginalsFromBaseline(st, baseline, ensureOrdinals) {
|
|
3593
3551
|
st.originals.clear();
|
|
@@ -3599,7 +3557,7 @@ function seedOriginalsFromBaseline(st, baseline, ensureOrdinals) {
|
|
|
3599
3557
|
});
|
|
3600
3558
|
}
|
|
3601
3559
|
function queueInitialAsyncValidation(st) {
|
|
3602
|
-
if (!st.ssr && st.
|
|
3560
|
+
if (!st.ssr && st.schema.needsAsyncValidation?.() === true) {
|
|
3603
3561
|
queueMicrotask(() => scheduleFieldValidation(
|
|
3604
3562
|
st,
|
|
3605
3563
|
[],
|
|
@@ -3641,10 +3599,6 @@ function ensurePathOrdinal(st, key) {
|
|
|
3641
3599
|
}
|
|
3642
3600
|
return ordinal;
|
|
3643
3601
|
}
|
|
3644
|
-
function pathHasAsyncValidation(st, path) {
|
|
3645
|
-
const { key } = canonicalizePath(path);
|
|
3646
|
-
return pathHasAsyncValidationByKey(st, key, path);
|
|
3647
|
-
}
|
|
3648
3602
|
function pathHasAsyncValidationByKey(st, key, segments) {
|
|
3649
3603
|
const cached = st.pathAsyncCache.get(key);
|
|
3650
3604
|
if (cached !== void 0) return cached;
|
|
@@ -3766,19 +3720,16 @@ function touchFieldRecord(st, pathKey, path, patch) {
|
|
|
3766
3720
|
path,
|
|
3767
3721
|
updatedAt: patch.updatedAt ?? current?.updatedAt ?? null,
|
|
3768
3722
|
connected: patch.connected ?? current?.connected ?? false,
|
|
3769
|
-
// focused/blurred
|
|
3770
|
-
//
|
|
3771
|
-
//
|
|
3772
|
-
// `current`, losing the intent. `!== undefined` honours an
|
|
3773
|
-
// explicit null and preserves current only on absence.
|
|
3723
|
+
// `focused` / `blurred` need an explicit-undefined guard: a patch
|
|
3724
|
+
// legitimately carries `null` to mark a disconnect, and `??` would
|
|
3725
|
+
// short-circuit on it and fall through to `current`, losing the intent.
|
|
3774
3726
|
focused: patch.focused !== void 0 ? patch.focused : current?.focused ?? null,
|
|
3775
3727
|
blurred: patch.blurred !== void 0 ? patch.blurred : current?.blurred ?? null,
|
|
3776
|
-
// touched is plain
|
|
3777
|
-
// guard here because `false` is not nullish.
|
|
3728
|
+
// `touched` is a plain boolean, so `??` is equivalent to the guard above.
|
|
3778
3729
|
touched: patch.touched ?? current?.touched ?? false,
|
|
3779
|
-
// interacted is sticky-true
|
|
3780
|
-
//
|
|
3781
|
-
//
|
|
3730
|
+
// `interacted` is sticky-true and a merge patch only ever sets it, so `??`
|
|
3731
|
+
// preserves the current bit. Only the reset paths clear it, and they
|
|
3732
|
+
// reconstruct the record outright.
|
|
3782
3733
|
interacted: patch.interacted ?? current?.interacted ?? false,
|
|
3783
3734
|
blurredAfterInteraction: patch.blurredAfterInteraction ?? current?.blurredAfterInteraction ?? false
|
|
3784
3735
|
});
|
|
@@ -3787,10 +3738,15 @@ function commitWritePatches(st, patches, meta) {
|
|
|
3787
3738
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3788
3739
|
for (const patch of patches) {
|
|
3789
3740
|
const { key } = canonicalizePath(patch.path);
|
|
3790
|
-
if (
|
|
3741
|
+
if (!st.originals.has(key)) {
|
|
3791
3742
|
st.originals.set(key, { segments: patch.path, value: void 0 });
|
|
3792
3743
|
}
|
|
3793
3744
|
touchFieldRecord(st, key, patch.path, { updatedAt: now });
|
|
3745
|
+
st.pathSweep.track(patch.path, key);
|
|
3746
|
+
for (let i = 1; i < patch.path.length; i++) {
|
|
3747
|
+
const ancestor = patch.path.slice(0, i);
|
|
3748
|
+
st.pathSweep.track(ancestor, canonicalizePath(ancestor).key);
|
|
3749
|
+
}
|
|
3794
3750
|
}
|
|
3795
3751
|
for (const listener of st.formChangeListeners) {
|
|
3796
3752
|
try {
|
|
@@ -4001,10 +3957,7 @@ function setValueAtPath(st, path, value, meta) {
|
|
|
4001
3957
|
if (newlyAuthored && st.schema.isPreprocessOrCoerceLeaf(path)) {
|
|
4002
3958
|
const modeForAuthoringTransition = meta?.instance?.validateOn ?? st.fieldValidationMode;
|
|
4003
3959
|
if (modeForAuthoringTransition === "change") {
|
|
4004
|
-
scheduleFieldValidation(st, path, false,
|
|
4005
|
-
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
4006
|
-
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
4007
|
-
});
|
|
3960
|
+
scheduleFieldValidation(st, path, false, meta?.instance);
|
|
4008
3961
|
}
|
|
4009
3962
|
}
|
|
4010
3963
|
return true;
|
|
@@ -4020,15 +3973,12 @@ function setValueAtPath(st, path, value, meta) {
|
|
|
4020
3973
|
st.arrayIdentity.realign(path);
|
|
4021
3974
|
} else if (isContainer(currentValue) && !isContainer(value)) {
|
|
4022
3975
|
if (subtreeHadRealBaseline(st, path, currentValue)) {
|
|
4023
|
-
st.removedSubtrees.add(
|
|
3976
|
+
st.removedSubtrees.add(pathKey);
|
|
4024
3977
|
}
|
|
4025
3978
|
}
|
|
4026
3979
|
const effectiveModeAfterWrite = meta?.instance?.validateOn ?? st.fieldValidationMode;
|
|
4027
3980
|
if (effectiveModeAfterWrite === "change") {
|
|
4028
|
-
scheduleFieldValidation(st, path, false,
|
|
4029
|
-
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
4030
|
-
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
4031
|
-
});
|
|
3981
|
+
scheduleFieldValidation(st, path, false, meta?.instance);
|
|
4032
3982
|
}
|
|
4033
3983
|
return true;
|
|
4034
3984
|
}
|
|
@@ -4089,12 +4039,11 @@ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variant
|
|
|
4089
4039
|
path: [...parentPath, ...err.path]
|
|
4090
4040
|
}));
|
|
4091
4041
|
applySchemaErrorsForSubtree(st, parentPath, reStamped);
|
|
4092
|
-
const
|
|
4093
|
-
const prevValidation = st.fieldValidationState.get(parentKey2);
|
|
4042
|
+
const prevValidation = st.fieldValidationState.get(parentKey);
|
|
4094
4043
|
if (prevValidation !== void 0) {
|
|
4095
4044
|
if (prevValidation.timer !== null) clearTimeout(prevValidation.timer);
|
|
4096
4045
|
prevValidation.aborted = true;
|
|
4097
|
-
st.fieldValidationState.delete(
|
|
4046
|
+
st.fieldValidationState.delete(parentKey);
|
|
4098
4047
|
}
|
|
4099
4048
|
appliedSync = true;
|
|
4100
4049
|
}
|
|
@@ -4102,17 +4051,14 @@ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variant
|
|
|
4102
4051
|
applyFormReplacement(st, nextForm, meta);
|
|
4103
4052
|
for (const k of newBlankPaths) st.blankPaths.add(k);
|
|
4104
4053
|
if (reshapeMode === "change" && !appliedSync) {
|
|
4105
|
-
scheduleFieldValidation(st, parentPath, false,
|
|
4106
|
-
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
4107
|
-
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
4108
|
-
});
|
|
4054
|
+
scheduleFieldValidation(st, parentPath, false, meta?.instance);
|
|
4109
4055
|
}
|
|
4110
4056
|
return true;
|
|
4111
4057
|
}
|
|
4112
|
-
function scheduleFieldValidation(st, path, immediate,
|
|
4113
|
-
const effectiveMode =
|
|
4058
|
+
function scheduleFieldValidation(st, path, immediate, instance) {
|
|
4059
|
+
const effectiveMode = instance?.validateOn ?? st.fieldValidationMode;
|
|
4114
4060
|
if (effectiveMode === "submit") return;
|
|
4115
|
-
const effectiveDebounce =
|
|
4061
|
+
const effectiveDebounce = instance?.debounceMs ?? st.fieldValidationDebounceMs;
|
|
4116
4062
|
const { key } = canonicalizePath(path);
|
|
4117
4063
|
const prev = st.fieldValidationState.get(key);
|
|
4118
4064
|
if (prev !== void 0) {
|
|
@@ -4352,13 +4298,13 @@ function markFocused(st, path, focused, meta) {
|
|
|
4352
4298
|
touchFieldRecord(st, key, path, {
|
|
4353
4299
|
focused,
|
|
4354
4300
|
blurred: !focused,
|
|
4355
|
-
// `touched` flips
|
|
4356
|
-
//
|
|
4301
|
+
// `touched` flips true on blur and stays; a focused field keeps whatever
|
|
4302
|
+
// it held.
|
|
4357
4303
|
touched: focused ? current?.touched ?? false : true,
|
|
4358
|
-
// `blurredAfterInteraction` flips true on the first blur
|
|
4359
|
-
//
|
|
4360
|
-
//
|
|
4361
|
-
//
|
|
4304
|
+
// `blurredAfterInteraction` flips true on the first blur after a value
|
|
4305
|
+
// edit and stays. A tab-through blur before any edit leaves it false,
|
|
4306
|
+
// since `interacted` is still false, which is what keeps a clean
|
|
4307
|
+
// tab-through from arming the gate.
|
|
4362
4308
|
blurredAfterInteraction: !focused && current?.interacted === true ? true : current?.blurredAfterInteraction ?? false
|
|
4363
4309
|
});
|
|
4364
4310
|
const focusMode = meta?.instance?.validateOn ?? st.fieldValidationMode;
|
|
@@ -4386,10 +4332,7 @@ function markFocused(st, path, focused, meta) {
|
|
|
4386
4332
|
});
|
|
4387
4333
|
}
|
|
4388
4334
|
if (changed) {
|
|
4389
|
-
scheduleFieldValidation(st, path, true,
|
|
4390
|
-
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
4391
|
-
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
4392
|
-
});
|
|
4335
|
+
scheduleFieldValidation(st, path, true, meta?.instance);
|
|
4393
4336
|
}
|
|
4394
4337
|
}
|
|
4395
4338
|
}
|
|
@@ -4402,11 +4345,10 @@ function markInteracted(st, path) {
|
|
|
4402
4345
|
function touchAtPath(st, segments) {
|
|
4403
4346
|
const formValue = st.form.value;
|
|
4404
4347
|
let touchedAny = false;
|
|
4405
|
-
for (const [, entry] of st.originals) {
|
|
4348
|
+
for (const [leafKey, entry] of st.originals) {
|
|
4406
4349
|
if (!isPathPrefix(segments, entry.segments)) continue;
|
|
4407
4350
|
if (!hasAtPath(formValue, entry.segments)) continue;
|
|
4408
4351
|
touchedAny = true;
|
|
4409
|
-
const leafKey = canonicalizePath(entry.segments).key;
|
|
4410
4352
|
const current = st.fields.get(leafKey);
|
|
4411
4353
|
if (current?.touched === true) continue;
|
|
4412
4354
|
touchFieldRecord(st, leafKey, entry.segments, { touched: true });
|
|
@@ -4421,11 +4363,10 @@ function interactAtPath(st, segments) {
|
|
|
4421
4363
|
if (st.effectiveDisabled.value) return false;
|
|
4422
4364
|
const formValue = st.form.value;
|
|
4423
4365
|
let interactedAny = false;
|
|
4424
|
-
for (const [, entry] of st.originals) {
|
|
4366
|
+
for (const [leafKey, entry] of st.originals) {
|
|
4425
4367
|
if (!isPathPrefix(segments, entry.segments)) continue;
|
|
4426
4368
|
if (!hasAtPath(formValue, entry.segments)) continue;
|
|
4427
4369
|
interactedAny = true;
|
|
4428
|
-
const leafKey = canonicalizePath(entry.segments).key;
|
|
4429
4370
|
const current = st.fields.get(leafKey);
|
|
4430
4371
|
if (current?.touched === true && current.interacted === true && current.blurredAfterInteraction === true) {
|
|
4431
4372
|
continue;
|
|
@@ -4482,11 +4423,7 @@ function adoptResolvedDefaults(st, value) {
|
|
|
4482
4423
|
st.schema
|
|
4483
4424
|
)
|
|
4484
4425
|
);
|
|
4485
|
-
seedOriginalsFromBaseline(
|
|
4486
|
-
st,
|
|
4487
|
-
computeBaselineResponse(st.schema, st.strict, st.defaultValues).data,
|
|
4488
|
-
false
|
|
4489
|
-
);
|
|
4426
|
+
seedOriginalsFromBaseline(st, computeBaselineResponse(st.schema, st.defaultValues).data, false);
|
|
4490
4427
|
}
|
|
4491
4428
|
function reset(st, nextDefaultValues) {
|
|
4492
4429
|
const resetSource = nextDefaultValues === void 0 ? st.defaultValues : mergeSparseHydration(
|
|
@@ -4495,7 +4432,7 @@ function reset(st, nextDefaultValues) {
|
|
|
4495
4432
|
st.schema
|
|
4496
4433
|
);
|
|
4497
4434
|
st.defaultValues = structuralSnapshot(resetSource);
|
|
4498
|
-
const resetResponse = computeBaselineResponse(st.schema,
|
|
4435
|
+
const resetResponse = computeBaselineResponse(st.schema, resetSource);
|
|
4499
4436
|
const next = resetResponse.data;
|
|
4500
4437
|
rebuildAuthoredPaths(st, resetSource, next);
|
|
4501
4438
|
applyFormReplacement(st, next);
|
|
@@ -4512,16 +4449,14 @@ function reset(st, nextDefaultValues) {
|
|
|
4512
4449
|
st.blankPaths.add(key);
|
|
4513
4450
|
}
|
|
4514
4451
|
st.errorCells.clear();
|
|
4515
|
-
if (
|
|
4452
|
+
if (!resetResponse.success) {
|
|
4516
4453
|
replaceErrorChannel(st, "schema", resetResponse.errors);
|
|
4517
4454
|
}
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
applySchemaErrorsForSubtree(st, [], syncResult.errors);
|
|
4522
|
-
}
|
|
4455
|
+
const syncResult = st.schema.validateAtPath(st.form.value, void 0, { sync: true });
|
|
4456
|
+
if (!(syncResult instanceof Promise) && !syncResult.success) {
|
|
4457
|
+
applySchemaErrorsForSubtree(st, [], syncResult.errors);
|
|
4523
4458
|
}
|
|
4524
|
-
st.firstValidationDone.value = initialFirstValidationGate(st.schema
|
|
4459
|
+
st.firstValidationDone.value = initialFirstValidationGate(st.schema);
|
|
4525
4460
|
queueInitialAsyncValidation(st);
|
|
4526
4461
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4527
4462
|
for (const [pathKey, record] of st.fields) {
|
|
@@ -4565,7 +4500,7 @@ function resetField(st, path) {
|
|
|
4565
4500
|
const wrote = setValueAtPath(st, targetSegments, leafEntry.value);
|
|
4566
4501
|
if (!wrote) {
|
|
4567
4502
|
console.error(
|
|
4568
|
-
`[attaform] resetField: leaf write rejected for path '${targetKey}'
|
|
4503
|
+
`[attaform] resetField: leaf write rejected for path '${targetKey}'. Originals contain a value that doesn't satisfy the slim primitive shape. This is a bug in the construction pipeline.`
|
|
4569
4504
|
);
|
|
4570
4505
|
}
|
|
4571
4506
|
} else {
|
|
@@ -4586,7 +4521,7 @@ function resetField(st, path) {
|
|
|
4586
4521
|
const wroteSubtree = setValueAtPath(st, targetSegments, subtree);
|
|
4587
4522
|
if (!wroteSubtree) {
|
|
4588
4523
|
console.error(
|
|
4589
|
-
`[attaform] resetField: subtree write rejected at path '${targetKey}'
|
|
4524
|
+
`[attaform] resetField: subtree write rejected at path '${targetKey}'. Originals contain values that don't satisfy the slim primitive shape. This is a bug in the construction pipeline.`
|
|
4590
4525
|
);
|
|
4591
4526
|
}
|
|
4592
4527
|
}
|
|
@@ -4611,10 +4546,6 @@ function clearFieldRecordFlags(st, pathKey) {
|
|
|
4611
4546
|
if (record === void 0) return;
|
|
4612
4547
|
st.fields.set(pathKey, withClearedHistoryFlags(record, (/* @__PURE__ */ new Date()).toISOString()));
|
|
4613
4548
|
}
|
|
4614
|
-
function isPristineAtPath(st, path) {
|
|
4615
|
-
const { key, segments } = canonicalizePath(path);
|
|
4616
|
-
return isPristineAtPathByKey(st, key, segments);
|
|
4617
|
-
}
|
|
4618
4549
|
function isPristineAtPathByKey(st, key, segments) {
|
|
4619
4550
|
if (st.blankPaths.has(key) !== st.originalBlankPaths.has(key)) return false;
|
|
4620
4551
|
const entry = st.originals.get(key);
|
|
@@ -4648,12 +4579,8 @@ function getFieldRecord(st, path) {
|
|
|
4648
4579
|
const { key } = canonicalizePath(path);
|
|
4649
4580
|
return st.fields.get(key);
|
|
4650
4581
|
}
|
|
4651
|
-
function getOriginalAtPath(st, path) {
|
|
4652
|
-
const { key } = canonicalizePath(path);
|
|
4653
|
-
return st.originals.get(key)?.value;
|
|
4654
|
-
}
|
|
4655
4582
|
function createFormStore(options) {
|
|
4656
|
-
const { formKey, schema, defaultValues,
|
|
4583
|
+
const { formKey, schema, defaultValues, hydration } = options;
|
|
4657
4584
|
const ssr = options.ssr === true;
|
|
4658
4585
|
const ssrPrefetch = options.ssrPrefetch;
|
|
4659
4586
|
const rememberVariants = options.rememberVariants !== false;
|
|
@@ -4661,22 +4588,16 @@ function createFormStore(options) {
|
|
|
4661
4588
|
const fieldValidationDebounceMs = normalizeNumericOption({
|
|
4662
4589
|
value: options.debounceMs ?? DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS,
|
|
4663
4590
|
source: "useForm.debounceMs",
|
|
4664
|
-
allowInfinity: false,
|
|
4665
4591
|
min: 0,
|
|
4666
4592
|
defaultValue: DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS
|
|
4667
4593
|
});
|
|
4668
|
-
const
|
|
4669
|
-
const resolvedGetDisplayState = resolveGetDisplayState(options.getDisplayState);
|
|
4594
|
+
const coerceEnabled = resolveCoerceEnabled(options.coerce);
|
|
4670
4595
|
const cleanupHooks = [];
|
|
4671
4596
|
const modules = /* @__PURE__ */ new Map();
|
|
4672
4597
|
const fieldValidatingSince = reactive(/* @__PURE__ */ new Map());
|
|
4673
4598
|
const displayEngine = createDisplayEngine(ssr);
|
|
4674
4599
|
cleanupHooks.push(() => displayEngine.dispose());
|
|
4675
|
-
const schemaResponse = computeBaselineResponse(
|
|
4676
|
-
schema,
|
|
4677
|
-
strict,
|
|
4678
|
-
defaultValues
|
|
4679
|
-
);
|
|
4600
|
+
const schemaResponse = computeBaselineResponse(schema, defaultValues);
|
|
4680
4601
|
const schemaInitialData = schemaResponse.data;
|
|
4681
4602
|
const authoredPaths = /* @__PURE__ */ new Set();
|
|
4682
4603
|
const initialData = hydration !== void 0 ? hydration.form : structuralSnapshot(schemaInitialData);
|
|
@@ -4689,16 +4610,15 @@ function createFormStore(options) {
|
|
|
4689
4610
|
const v = getAtPath(toRaw(form.value), arraySegs);
|
|
4690
4611
|
return Array.isArray(v) ? v.length : 0;
|
|
4691
4612
|
});
|
|
4692
|
-
const fields =
|
|
4613
|
+
const fields = shallowReactive(/* @__PURE__ */ new Map());
|
|
4693
4614
|
const domBinding = shallowRef(null);
|
|
4694
|
-
const errorCells =
|
|
4695
|
-
const originals =
|
|
4615
|
+
const errorCells = shallowReactive(/* @__PURE__ */ new Map());
|
|
4616
|
+
const originals = shallowReactive(/* @__PURE__ */ new Map());
|
|
4696
4617
|
const removedSubtrees = /* @__PURE__ */ new Set();
|
|
4697
|
-
const initialTransientList = hydration?.blankPaths ?? options.initialBlankPaths ?? [];
|
|
4698
4618
|
const blankPaths = reactive(/* @__PURE__ */ new Set());
|
|
4699
4619
|
const originalBlankPaths = /* @__PURE__ */ new Set();
|
|
4700
|
-
|
|
4701
|
-
|
|
4620
|
+
const seededBlankPaths = hydration !== void 0 ? (hydration.blankPaths ?? []).map((dotted) => canonicalizePath(dotted).key) : options.initialBlankPaths ?? [];
|
|
4621
|
+
for (const key of seededBlankPaths) {
|
|
4702
4622
|
blankPaths.add(key);
|
|
4703
4623
|
originalBlankPaths.add(key);
|
|
4704
4624
|
}
|
|
@@ -4715,6 +4635,9 @@ function createFormStore(options) {
|
|
|
4715
4635
|
}
|
|
4716
4636
|
return result;
|
|
4717
4637
|
});
|
|
4638
|
+
const errorPathIndex = computed(
|
|
4639
|
+
() => buildErrorPathIndex(errorCells, derivedBlankErrors.value)
|
|
4640
|
+
);
|
|
4718
4641
|
const submitting = ref(false);
|
|
4719
4642
|
const activeSubmissions = ref(0);
|
|
4720
4643
|
const submissionAttempts = ref(0);
|
|
@@ -4747,7 +4670,7 @@ function createFormStore(options) {
|
|
|
4747
4670
|
const defaultsResolved = ref(false);
|
|
4748
4671
|
const activated = ref(false);
|
|
4749
4672
|
const activationPromise = ref(void 0);
|
|
4750
|
-
const firstValidationDone = ref(initialFirstValidationGate(schema
|
|
4673
|
+
const firstValidationDone = ref(initialFirstValidationGate(schema));
|
|
4751
4674
|
watch(activeValidations, (now, prev) => {
|
|
4752
4675
|
if (prev > 0 && now === 0) {
|
|
4753
4676
|
firstValidationDone.value = true;
|
|
@@ -4765,6 +4688,33 @@ function createFormStore(options) {
|
|
|
4765
4688
|
const activeTransforms = ref(0);
|
|
4766
4689
|
const transformRuns = /* @__PURE__ */ new Map();
|
|
4767
4690
|
const transformWaiters = [];
|
|
4691
|
+
const errorWindows = /* @__PURE__ */ new Map();
|
|
4692
|
+
const pathSweep = createDynamicPathSweep({
|
|
4693
|
+
onFormChange: (listener) => {
|
|
4694
|
+
formChangeListeners.add(listener);
|
|
4695
|
+
},
|
|
4696
|
+
isFixedObjectAtPath: (path) => schema.isFixedObjectAtPath(path)
|
|
4697
|
+
});
|
|
4698
|
+
pathSweep.onEvict((key) => {
|
|
4699
|
+
fields.delete(key);
|
|
4700
|
+
fieldValidationState.delete(key);
|
|
4701
|
+
authoredPaths.delete(key);
|
|
4702
|
+
if (originals.get(key)?.value === void 0) originals.delete(key);
|
|
4703
|
+
errorWindows.delete(key);
|
|
4704
|
+
});
|
|
4705
|
+
const errorWindowAt = (prefix, prefixKey) => {
|
|
4706
|
+
let cached = errorWindows.get(prefixKey);
|
|
4707
|
+
if (cached === void 0) {
|
|
4708
|
+
const frozen = [...prefix];
|
|
4709
|
+
cached = computed((prev) => {
|
|
4710
|
+
const next = windowUnder(errorPathIndex.value, frozen);
|
|
4711
|
+
return prev !== void 0 && isSameWindow(prev, next) ? prev : next;
|
|
4712
|
+
});
|
|
4713
|
+
errorWindows.set(prefixKey, cached);
|
|
4714
|
+
pathSweep.track(frozen, prefixKey);
|
|
4715
|
+
}
|
|
4716
|
+
return cached.value;
|
|
4717
|
+
};
|
|
4768
4718
|
const arrayBookkeeping = createArrayBookkeeping({
|
|
4769
4719
|
form,
|
|
4770
4720
|
fields,
|
|
@@ -4782,6 +4732,7 @@ function createFormStore(options) {
|
|
|
4782
4732
|
touchFieldRecord: (pathKey, path, patch) => touchFieldRecord(st, pathKey, path, patch),
|
|
4783
4733
|
decFieldValidation: (key) => decFieldValidation(st, key)
|
|
4784
4734
|
});
|
|
4735
|
+
const bind = (fn) => (...args) => fn(st, ...args);
|
|
4785
4736
|
const st = {
|
|
4786
4737
|
// --- public data (the FormStore contract's state members) ---
|
|
4787
4738
|
formKey,
|
|
@@ -4789,10 +4740,11 @@ function createFormStore(options) {
|
|
|
4789
4740
|
fields,
|
|
4790
4741
|
errorCells,
|
|
4791
4742
|
derivedBlankErrors,
|
|
4743
|
+
errorWindowAt,
|
|
4792
4744
|
originals,
|
|
4745
|
+
pathSweep,
|
|
4793
4746
|
schema,
|
|
4794
4747
|
ssr,
|
|
4795
|
-
getDisplayState: resolvedGetDisplayState,
|
|
4796
4748
|
submitting,
|
|
4797
4749
|
activeSubmissions,
|
|
4798
4750
|
submissionAttempts,
|
|
@@ -4820,20 +4772,16 @@ function createFormStore(options) {
|
|
|
4820
4772
|
displayEngine,
|
|
4821
4773
|
domBinding,
|
|
4822
4774
|
modules,
|
|
4823
|
-
|
|
4775
|
+
coerceEnabled,
|
|
4824
4776
|
blankPaths,
|
|
4825
4777
|
originalBlankPaths,
|
|
4826
4778
|
// --- kernel-internal state ---
|
|
4827
|
-
|
|
4828
|
-
//
|
|
4829
|
-
// `
|
|
4830
|
-
//
|
|
4831
|
-
//
|
|
4832
|
-
//
|
|
4833
|
-
// it should not depend on it. The copy also means a consumer who
|
|
4834
|
-
// keeps a reference to the literal they passed cannot mutate the
|
|
4835
|
-
// form's defaults from outside. `reset()` snapshots for a harder
|
|
4836
|
-
// reason (see there) where an alias IS reachable.
|
|
4779
|
+
// A defensive copy. `getDefaultValues` building a fresh tree is an adapter
|
|
4780
|
+
// implementation detail, not a contract, and this field is durable state
|
|
4781
|
+
// that every `reset()` reads, so it must not depend on one. The copy also
|
|
4782
|
+
// stops a consumer holding a reference to the literal they passed from
|
|
4783
|
+
// mutating the form's defaults from outside. `reset()` snapshots for a
|
|
4784
|
+
// harder reason, where an alias IS reachable.
|
|
4837
4785
|
defaultValues: structuralSnapshot(defaultValues),
|
|
4838
4786
|
ssrPrefetch,
|
|
4839
4787
|
rememberVariants,
|
|
@@ -4861,17 +4809,20 @@ function createFormStore(options) {
|
|
|
4861
4809
|
transformTokenSeq: 0,
|
|
4862
4810
|
warnedDisabledWrite: false,
|
|
4863
4811
|
// --- methods: thin per-instance skins over the module kernel ---
|
|
4864
|
-
rehydrate: (
|
|
4865
|
-
activate: (
|
|
4866
|
-
adoptResolvedDefaults: (
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4812
|
+
rehydrate: bind(rehydrate),
|
|
4813
|
+
activate: bind(activate),
|
|
4814
|
+
adoptResolvedDefaults: bind(adoptResolvedDefaults),
|
|
4815
|
+
pathHasAsyncValidationByKey: bind(pathHasAsyncValidationByKey),
|
|
4816
|
+
applyFormReplacement: bind(applyFormReplacement),
|
|
4817
|
+
setValueAtPath: bind(setValueAtPath),
|
|
4818
|
+
getValueAtPath: bind(getValueAtPath),
|
|
4819
|
+
arrayElementKey: bind(arrayElementKey),
|
|
4820
|
+
reset: bind(reset),
|
|
4821
|
+
resetField: bind(resetField),
|
|
4822
|
+
// The schema/user pair below reads as a fold waiting to happen. Folding
|
|
4823
|
+
// them into a `setErrorsForPathIn(channel)` factory measures 10 B LARGER:
|
|
4824
|
+
// gzip has already collected the rent on two adjacent copies, and the
|
|
4825
|
+
// helper adds a name the original does not need.
|
|
4875
4826
|
setSchemaErrorsForPath: (path, entries) => setErrorChannelForKey(
|
|
4876
4827
|
st,
|
|
4877
4828
|
canonicalizePath(path).key,
|
|
@@ -4880,7 +4831,7 @@ function createFormStore(options) {
|
|
|
4880
4831
|
),
|
|
4881
4832
|
setAllSchemaErrors: (entries) => replaceErrorChannel(st, "schema", entries),
|
|
4882
4833
|
clearSchemaErrors: (path) => clearErrorChannel(st, "schema", path),
|
|
4883
|
-
applySchemaErrorsForSubtree: (
|
|
4834
|
+
applySchemaErrorsForSubtree: bind(applySchemaErrorsForSubtree),
|
|
4884
4835
|
setAllUserErrors: (entries) => replaceErrorChannel(st, "user", entries),
|
|
4885
4836
|
setUserErrorsForPath: (path, entries) => setErrorChannelForKey(
|
|
4886
4837
|
st,
|
|
@@ -4889,37 +4840,34 @@ function createFormStore(options) {
|
|
|
4889
4840
|
entries.length === 0 ? NO_ERRORS : [...entries]
|
|
4890
4841
|
),
|
|
4891
4842
|
clearUserErrors: (path) => clearErrorChannel(st, "user", path),
|
|
4892
|
-
restoreErrorCells: (
|
|
4893
|
-
getErrorsForPath: (
|
|
4894
|
-
ensurePathOrdinal: (
|
|
4895
|
-
noteDomConnected: (
|
|
4896
|
-
noteDomDisconnected: (
|
|
4897
|
-
markFocused: (
|
|
4898
|
-
markInteracted: (
|
|
4899
|
-
touchAtPath: (
|
|
4900
|
-
interactAtPath: (
|
|
4901
|
-
markConnectedOptimistically: (
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
emitSubmitSuccess: () => emitSubmitSuccess(st),
|
|
4921
|
-
registerCleanup: (fn) => registerCleanup(st, fn),
|
|
4922
|
-
dispose: () => dispose(st)
|
|
4843
|
+
restoreErrorCells: bind(restoreErrorCells),
|
|
4844
|
+
getErrorsForPath: bind(getErrorsForPath),
|
|
4845
|
+
ensurePathOrdinal: bind(ensurePathOrdinal),
|
|
4846
|
+
noteDomConnected: bind(noteDomConnected),
|
|
4847
|
+
noteDomDisconnected: bind(noteDomDisconnected),
|
|
4848
|
+
markFocused: bind(markFocused),
|
|
4849
|
+
markInteracted: bind(markInteracted),
|
|
4850
|
+
touchAtPath: bind(touchAtPath),
|
|
4851
|
+
interactAtPath: bind(interactAtPath),
|
|
4852
|
+
markConnectedOptimistically: bind(markConnectedOptimistically),
|
|
4853
|
+
isPristineAtPathByKey: bind(isPristineAtPathByKey),
|
|
4854
|
+
hasStructuralChangeUnder: bind(hasStructuralChangeUnder),
|
|
4855
|
+
hasRemovedSubtreeUnder: bind(hasRemovedSubtreeUnder),
|
|
4856
|
+
getFieldRecord: bind(getFieldRecord),
|
|
4857
|
+
cancelFieldValidation: bind(cancelFieldValidation),
|
|
4858
|
+
beginTransform: bind(beginTransform),
|
|
4859
|
+
isCurrentTransform: bind(isCurrentTransform),
|
|
4860
|
+
endTransform: bind(endTransform),
|
|
4861
|
+
setTransformError: bind(setTransformError),
|
|
4862
|
+
cancelTransformsUnder: bind(cancelTransformsUnder),
|
|
4863
|
+
settleTransforms: bind(settleTransforms),
|
|
4864
|
+
scheduleFieldValidation: bind(scheduleFieldValidation),
|
|
4865
|
+
onFormChange: bind(onFormChange),
|
|
4866
|
+
onSubmitSuccess: bind(onSubmitSuccess),
|
|
4867
|
+
onReset: bind(onReset),
|
|
4868
|
+
emitSubmitSuccess: bind(emitSubmitSuccess),
|
|
4869
|
+
registerCleanup: bind(registerCleanup),
|
|
4870
|
+
dispose: bind(dispose)
|
|
4923
4871
|
};
|
|
4924
4872
|
rebuildAuthoredPaths(st, defaultValues, schemaInitialData);
|
|
4925
4873
|
seedOriginalsFromBaseline(st, schemaInitialData, true);
|
|
@@ -4961,7 +4909,7 @@ function createFormStore(options) {
|
|
|
4961
4909
|
blurredAfterInteraction: false
|
|
4962
4910
|
});
|
|
4963
4911
|
});
|
|
4964
|
-
if (
|
|
4912
|
+
if (!schemaResponse.success) {
|
|
4965
4913
|
replaceErrorChannel(st, "schema", schemaResponse.errors);
|
|
4966
4914
|
}
|
|
4967
4915
|
}
|
|
@@ -4992,24 +4940,23 @@ function useAbstractForm(configuration, options) {
|
|
|
4992
4940
|
const resolvedDefaults = resolveTrichotomy(configuration.defaultValues);
|
|
4993
4941
|
const materialisedDefaults = resolvedDefaults.kind === "sync" ? resolvedDefaults.value : void 0;
|
|
4994
4942
|
const { defaultValues: _droppedDefaults, ...configWithoutDefaults } = configuration;
|
|
4995
|
-
const
|
|
4996
|
-
const merged = mergeWithDefaults(registry.defaults, trichotomyOverride);
|
|
4997
|
-
const maxRecursionDepth = normalizeNumericOption({
|
|
4998
|
-
value: merged.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH,
|
|
4999
|
-
source: "useForm.maxRecursionDepth",
|
|
5000
|
-
allowInfinity: true,
|
|
5001
|
-
min: 0,
|
|
5002
|
-
defaultValue: DEFAULT_MAX_RECURSION_DEPTH
|
|
5003
|
-
});
|
|
5004
|
-
const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
|
|
4943
|
+
const materialisedConfiguration = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
|
|
5005
4944
|
const existing = registry.forms.get(key);
|
|
4945
|
+
const resolvedSchema = getComputedSchema(key, configuration.schema, {
|
|
4946
|
+
maxRecursionDepth: DEFAULT_MAX_RECURSION_DEPTH
|
|
4947
|
+
}) ;
|
|
5006
4948
|
if (existing !== void 0) {
|
|
5007
4949
|
void import('../chunks/dev-key-collision-warnings.mjs').then((m) => {
|
|
5008
4950
|
void m.warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
|
|
5009
4951
|
});
|
|
5010
4952
|
}
|
|
5011
4953
|
const hadPendingHydration = registry.pendingHydration.has(key);
|
|
5012
|
-
const state = existing ?? buildFreshState(
|
|
4954
|
+
const state = existing ?? buildFreshState(
|
|
4955
|
+
key,
|
|
4956
|
+
resolvedSchema,
|
|
4957
|
+
materialisedConfiguration,
|
|
4958
|
+
registry
|
|
4959
|
+
);
|
|
5013
4960
|
if (existing !== void 0) ; else if (resolvedDefaults.kind === "sync") {
|
|
5014
4961
|
state.defaultsResolved.value = true;
|
|
5015
4962
|
}
|
|
@@ -5034,8 +4981,8 @@ function useAbstractForm(configuration, options) {
|
|
|
5034
4981
|
const releaseConsumer = registry.trackConsumer(key);
|
|
5035
4982
|
onScopeDispose(releaseConsumer);
|
|
5036
4983
|
}
|
|
5037
|
-
if (existing === void 0 &&
|
|
5038
|
-
const historyModule =
|
|
4984
|
+
if (existing === void 0 && materialisedConfiguration.history !== void 0) {
|
|
4985
|
+
const historyModule = materialisedConfiguration.history.attach(state);
|
|
5039
4986
|
state.modules.set(HISTORY_MODULE_KEY, historyModule);
|
|
5040
4987
|
state.registerCleanup(() => historyModule.dispose());
|
|
5041
4988
|
}
|
|
@@ -5048,36 +4995,16 @@ function useAbstractForm(configuration, options) {
|
|
|
5048
4995
|
provide(kFormInstanceId, formInstanceId);
|
|
5049
4996
|
}
|
|
5050
4997
|
const apiOptions = pickDefined({
|
|
5051
|
-
|
|
4998
|
+
focusOnInvalidSubmit: materialisedConfiguration.focusOnInvalidSubmit,
|
|
5052
4999
|
history: state.modules.get(HISTORY_MODULE_KEY),
|
|
5053
|
-
validateOn:
|
|
5054
|
-
debounceMs:
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
rememberVariants: merged.rememberVariants,
|
|
5058
|
-
autoAria: merged.autoAria
|
|
5000
|
+
validateOn: materialisedConfiguration.validateOn,
|
|
5001
|
+
debounceMs: materialisedConfiguration.debounceMs,
|
|
5002
|
+
coerce: materialisedConfiguration.coerce,
|
|
5003
|
+
rememberVariants: materialisedConfiguration.rememberVariants
|
|
5059
5004
|
});
|
|
5060
5005
|
const api = buildFormApi(state, formInstanceId, apiOptions);
|
|
5061
5006
|
return api;
|
|
5062
5007
|
}
|
|
5063
|
-
function mergeWithDefaults(defaults, configuration) {
|
|
5064
|
-
return {
|
|
5065
|
-
...configuration,
|
|
5066
|
-
...pickDefined({
|
|
5067
|
-
strict: configuration.strict ?? defaults.strict,
|
|
5068
|
-
onInvalidSubmit: configuration.onInvalidSubmit ?? defaults.onInvalidSubmit,
|
|
5069
|
-
history: configuration.history ?? defaults.history,
|
|
5070
|
-
rememberVariants: configuration.rememberVariants ?? defaults.rememberVariants,
|
|
5071
|
-
disabled: configuration.disabled ?? defaults.disabled,
|
|
5072
|
-
coerce: configuration.coerce ?? defaults.coerce,
|
|
5073
|
-
validateOn: configuration.validateOn ?? defaults.validateOn,
|
|
5074
|
-
debounceMs: configuration.debounceMs ?? defaults.debounceMs,
|
|
5075
|
-
getDisplayState: configuration.getDisplayState ?? defaults.getDisplayState,
|
|
5076
|
-
maxRecursionDepth: configuration.maxRecursionDepth ?? defaults.maxRecursionDepth,
|
|
5077
|
-
autoAria: configuration.autoAria ?? defaults.autoAria
|
|
5078
|
-
})
|
|
5079
|
-
};
|
|
5080
|
-
}
|
|
5081
5008
|
const HISTORY_MODULE_KEY = "history";
|
|
5082
5009
|
function buildFreshState(key, schema, configuration, registry) {
|
|
5083
5010
|
const pending = registry.pendingHydration.get(key);
|
|
@@ -5097,21 +5024,17 @@ function buildFreshState(key, schema, configuration, registry) {
|
|
|
5097
5024
|
hydration: pending,
|
|
5098
5025
|
ssr: registry.ssr,
|
|
5099
5026
|
...pickDefined({
|
|
5100
|
-
strict: configuration.strict,
|
|
5101
5027
|
validateOn: configuration.validateOn,
|
|
5102
5028
|
debounceMs: configuration.debounceMs,
|
|
5103
5029
|
rememberVariants: configuration.rememberVariants,
|
|
5104
5030
|
disabled: configuration.disabled,
|
|
5105
5031
|
coerce: configuration.coerce,
|
|
5106
|
-
getDisplayState: configuration.getDisplayState,
|
|
5107
5032
|
initialBlankPaths
|
|
5108
5033
|
}),
|
|
5109
|
-
// Server
|
|
5110
|
-
//
|
|
5111
|
-
//
|
|
5112
|
-
//
|
|
5113
|
-
// wizard explicitly skipped this key — even an explicit
|
|
5114
|
-
// `form.activate()` defers to the wizard's render-efficiency
|
|
5034
|
+
// Server only. `enqueue` records intent on every `state.activate()`
|
|
5035
|
+
// so a wizard skip-list override has a consistent set to diff
|
|
5036
|
+
// against; `shouldFire` lets the activate path bail on a key the
|
|
5037
|
+
// wizard skipped. Even an explicit `form.activate()` defers to that
|
|
5115
5038
|
// skip-list on the server.
|
|
5116
5039
|
...registry.ssr ? {
|
|
5117
5040
|
ssrPrefetch: {
|
|
@@ -5324,7 +5247,7 @@ function warnOutsideSetup() {
|
|
|
5324
5247
|
warnedOutsideSetup = true;
|
|
5325
5248
|
const frame = captureUserCallSite();
|
|
5326
5249
|
console.warn(
|
|
5327
|
-
`[attaform] useRegister() called outside a component setup; returning an unbound RegisterValue proxy. Fix: call it inside <script setup> or a setup() function
|
|
5250
|
+
`[attaform] useRegister() called outside a component setup; returning an unbound RegisterValue proxy. Fix: call it inside <script setup> or a setup() function, not from an event handler or async callback.` + (frame !== void 0 ? ` ${frame}` : "")
|
|
5328
5251
|
);
|
|
5329
5252
|
}
|
|
5330
5253
|
function warnNoParentRV(instance) {
|
|
@@ -5417,25 +5340,18 @@ function createWizardHistory(param) {
|
|
|
5417
5340
|
};
|
|
5418
5341
|
}
|
|
5419
5342
|
|
|
5420
|
-
const NOOP_FINGERPRINT = "attaform:wizard-noop";
|
|
5421
5343
|
const EMPTY_SLIM_KINDS = /* @__PURE__ */ new Set();
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
errors: void 0,
|
|
5434
|
-
formKey
|
|
5435
|
-
};
|
|
5436
|
-
return {
|
|
5437
|
-
fingerprint: () => Promise.resolve(NOOP_FINGERPRINT),
|
|
5438
|
-
getDefaultValues: () => defaultsResponse,
|
|
5344
|
+
const EMPTY_VALUE = {};
|
|
5345
|
+
const NOOP_SUCCESS = {
|
|
5346
|
+
success: true,
|
|
5347
|
+
data: EMPTY_VALUE,
|
|
5348
|
+
errors: void 0
|
|
5349
|
+
};
|
|
5350
|
+
const NOOP_DEFAULTS = NOOP_SUCCESS;
|
|
5351
|
+
let NOOP_SCHEMA;
|
|
5352
|
+
function buildNoopWizardSchema() {
|
|
5353
|
+
NOOP_SCHEMA ?? (NOOP_SCHEMA = {
|
|
5354
|
+
getDefaultValues: () => NOOP_DEFAULTS,
|
|
5439
5355
|
getDefaultAtPath: () => void 0,
|
|
5440
5356
|
getEmptyValueAtPath: () => void 0,
|
|
5441
5357
|
isPreprocessOrCoerceLeaf: () => false,
|
|
@@ -5446,13 +5362,14 @@ function buildNoopWizardSchema(formKey) {
|
|
|
5446
5362
|
// addressable, root included.
|
|
5447
5363
|
entryKeyKindAtPath: () => void 0,
|
|
5448
5364
|
getSchemasAtPath: () => [],
|
|
5449
|
-
validateAtPath: () =>
|
|
5365
|
+
validateAtPath: () => NOOP_SUCCESS,
|
|
5450
5366
|
getSlimPrimitiveTypesAtPath: () => new Set(EMPTY_SLIM_KINDS),
|
|
5451
5367
|
isLeafAtPath: () => false,
|
|
5452
5368
|
isRequiredAtPath: () => false,
|
|
5453
5369
|
getUnionDiscriminatorAtPath: () => void 0,
|
|
5454
5370
|
hasDiscriminatedUnions: () => false
|
|
5455
|
-
};
|
|
5371
|
+
});
|
|
5372
|
+
return NOOP_SCHEMA;
|
|
5456
5373
|
}
|
|
5457
5374
|
|
|
5458
5375
|
function buildCallableReadonlySnapshotProxy(opts) {
|
|
@@ -5561,7 +5478,7 @@ function useWizard(options) {
|
|
|
5561
5478
|
const rawSteps = Array.isArray(options.steps) ? options.steps : [];
|
|
5562
5479
|
if (rawSteps.length === 0 && true) {
|
|
5563
5480
|
console.error(
|
|
5564
|
-
"[attaform] useWizard({ steps }): expected a non-empty array of step slots. Continuing with an empty step list
|
|
5481
|
+
"[attaform] useWizard({ steps }): expected a non-empty array of step slots. Continuing with an empty step list: wizard.currentStep reads as undefined, navigation refuses, handleSubmit no-ops."
|
|
5565
5482
|
);
|
|
5566
5483
|
}
|
|
5567
5484
|
const registry = useRegistry();
|
|
@@ -5571,7 +5488,7 @@ function useWizard(options) {
|
|
|
5571
5488
|
if (typeof slot !== "string") continue;
|
|
5572
5489
|
if (noopForms.has(slot)) continue;
|
|
5573
5490
|
const noop = useAbstractForm({
|
|
5574
|
-
schema: buildNoopWizardSchema(
|
|
5491
|
+
schema: buildNoopWizardSchema(),
|
|
5575
5492
|
key: slot
|
|
5576
5493
|
});
|
|
5577
5494
|
noopForms.set(slot, noop);
|
|
@@ -5582,7 +5499,7 @@ function useWizard(options) {
|
|
|
5582
5499
|
const noop = lazyNoopScope.run(
|
|
5583
5500
|
() => useAbstractForm(
|
|
5584
5501
|
{
|
|
5585
|
-
schema: buildNoopWizardSchema(
|
|
5502
|
+
schema: buildNoopWizardSchema(),
|
|
5586
5503
|
key
|
|
5587
5504
|
},
|
|
5588
5505
|
{ registry }
|
|
@@ -5650,33 +5567,45 @@ function useWizard(options) {
|
|
|
5650
5567
|
return activeKey.value === "" ? void 0 : activeKey.value;
|
|
5651
5568
|
}
|
|
5652
5569
|
};
|
|
5653
|
-
function lazyComputedFor(marker) {
|
|
5570
|
+
function lazyComputedFor(marker, index) {
|
|
5654
5571
|
const cached = lazyComputeds.get(marker);
|
|
5655
5572
|
if (cached !== void 0) return cached;
|
|
5656
5573
|
const c = computed(() => {
|
|
5657
5574
|
void lazyEpoch.value;
|
|
5658
|
-
return
|
|
5575
|
+
return callConsumerFn(
|
|
5576
|
+
() => marker.resolve(slotCtx),
|
|
5577
|
+
void 0,
|
|
5578
|
+
"wizard-lazy-slot",
|
|
5579
|
+
`steps[${index}]`
|
|
5580
|
+
);
|
|
5659
5581
|
});
|
|
5660
5582
|
lazyComputeds.set(marker, c);
|
|
5661
5583
|
return c;
|
|
5662
5584
|
}
|
|
5663
5585
|
const MAX_SLOT_DEPTH = 32;
|
|
5664
|
-
function normalizeSlot(slot, ctx, gated, depth) {
|
|
5586
|
+
function normalizeSlot(slot, ctx, gated, depth, index) {
|
|
5665
5587
|
if (depth > MAX_SLOT_DEPTH) {
|
|
5666
5588
|
{
|
|
5667
5589
|
console.warn(
|
|
5668
|
-
`[attaform] useWizard:
|
|
5590
|
+
`[attaform] useWizard: steps[${index}] nested past ${MAX_SLOT_DEPTH} levels (gate / lazy / function); dropping it. Check for a resolver that returns itself.`
|
|
5669
5591
|
);
|
|
5670
5592
|
}
|
|
5671
5593
|
return void 0;
|
|
5672
5594
|
}
|
|
5673
5595
|
if (slot === void 0 || slot === null) return void 0;
|
|
5674
5596
|
if (typeof slot === "string") return { form: getOrBuildNoop(slot), gated };
|
|
5675
|
-
if (isGateMarker(slot)) return normalizeSlot(slot.inner, ctx, true, depth + 1);
|
|
5676
|
-
if (isLazyMarker(slot))
|
|
5597
|
+
if (isGateMarker(slot)) return normalizeSlot(slot.inner, ctx, true, depth + 1, index);
|
|
5598
|
+
if (isLazyMarker(slot)) {
|
|
5599
|
+
return normalizeSlot(lazyComputedFor(slot, index).value, ctx, gated, depth + 1, index);
|
|
5600
|
+
}
|
|
5677
5601
|
if (typeof slot === "function") {
|
|
5678
|
-
const result =
|
|
5679
|
-
|
|
5602
|
+
const result = callConsumerFn(
|
|
5603
|
+
() => slot(ctx),
|
|
5604
|
+
void 0,
|
|
5605
|
+
"wizard-slot",
|
|
5606
|
+
`steps[${index}]`
|
|
5607
|
+
);
|
|
5608
|
+
return normalizeSlot(result, ctx, gated, depth + 1, index);
|
|
5680
5609
|
}
|
|
5681
5610
|
if (isAnyForm(slot)) return { form: slot, gated };
|
|
5682
5611
|
return void 0;
|
|
@@ -5685,7 +5614,7 @@ function useWizard(options) {
|
|
|
5685
5614
|
const out = [];
|
|
5686
5615
|
const seen = /* @__PURE__ */ new Set();
|
|
5687
5616
|
for (let i = 0; i < rawSteps.length; i++) {
|
|
5688
|
-
const norm = normalizeSlot(rawSteps[i], slotCtx, false, 0);
|
|
5617
|
+
const norm = normalizeSlot(rawSteps[i], slotCtx, false, 0, i);
|
|
5689
5618
|
if (norm === void 0) continue;
|
|
5690
5619
|
const { form, gated } = norm;
|
|
5691
5620
|
if (seen.has(form.key)) {
|
|
@@ -5998,13 +5927,15 @@ function useWizard(options) {
|
|
|
5998
5927
|
seedRef.value = resolved.value;
|
|
5999
5928
|
applyGateSeed(resolved.value);
|
|
6000
5929
|
} else {
|
|
6001
|
-
const eager = resolved.factory();
|
|
5930
|
+
const eager = callConsumerFn(() => resolved.factory(), void 0, "wizard-default-statuses");
|
|
6002
5931
|
if (eager instanceof Promise) {
|
|
6003
5932
|
void eager.then((value) => {
|
|
6004
5933
|
seedRef.value = value;
|
|
6005
5934
|
applyGateSeed(value);
|
|
5935
|
+
}).catch((err) => {
|
|
5936
|
+
reportConsumerThrow("wizard-default-statuses", void 0, err);
|
|
6006
5937
|
});
|
|
6007
|
-
} else {
|
|
5938
|
+
} else if (eager !== void 0) {
|
|
6008
5939
|
seedRef.value = eager;
|
|
6009
5940
|
applyGateSeed(eager);
|
|
6010
5941
|
}
|
|
@@ -6090,10 +6021,15 @@ function useWizard(options) {
|
|
|
6090
6021
|
}
|
|
6091
6022
|
const progressOverride = options.progress;
|
|
6092
6023
|
const progress = computed(() => {
|
|
6024
|
+
const list = compiledSteps.value;
|
|
6093
6025
|
if (progressOverride !== void 0) {
|
|
6094
|
-
|
|
6026
|
+
const override = callConsumerFn(
|
|
6027
|
+
() => progressOverride(list),
|
|
6028
|
+
null,
|
|
6029
|
+
"wizard-progress"
|
|
6030
|
+
);
|
|
6031
|
+
if (override !== null) return override;
|
|
6095
6032
|
}
|
|
6096
|
-
const list = compiledSteps.value;
|
|
6097
6033
|
if (list.length === 0) return 0;
|
|
6098
6034
|
let valid = 0;
|
|
6099
6035
|
for (const step of list) {
|
|
@@ -6121,11 +6057,15 @@ function useWizard(options) {
|
|
|
6121
6057
|
historyHandle.subscribe((value) => {
|
|
6122
6058
|
urlMirror.value = value;
|
|
6123
6059
|
});
|
|
6124
|
-
const
|
|
6060
|
+
const consumerRestore = options.restore;
|
|
6061
|
+
const restoreCallback = consumerRestore === false ? void 0 : consumerRestore !== void 0 ? () => callConsumerFn(() => consumerRestore(), void 0, "wizard-restore") : () => {
|
|
6125
6062
|
const value = urlMirror.value;
|
|
6126
6063
|
return value === void 0 ? void 0 : { step: value };
|
|
6127
6064
|
};
|
|
6128
|
-
const
|
|
6065
|
+
const consumerPersist = options.persist;
|
|
6066
|
+
const persistCallback = consumerPersist === false ? void 0 : consumerPersist !== void 0 ? (state) => {
|
|
6067
|
+
callConsumerFn(() => consumerPersist(state), void 0, "wizard-persist");
|
|
6068
|
+
} : (state) => {
|
|
6129
6069
|
if (state.step === void 0) return;
|
|
6130
6070
|
const current = historyHandle.read();
|
|
6131
6071
|
const effectiveCurrent = current !== void 0 && isCompiledKey(current) ? current : firstKey();
|
|
@@ -6806,4 +6746,4 @@ function escapeForInlineScript(json) {
|
|
|
6806
6746
|
});
|
|
6807
6747
|
}
|
|
6808
6748
|
|
|
6809
|
-
export { AttaformErrorCode as A,
|
|
6749
|
+
export { AttaformErrorCode as A, injectWizard as a, isUnset as b, useAbstractForm as c, useRegister as d, escapeForInlineScript as e, useWizard as f, gate as g, getAtPath as h, injectForm as i, humanize as j, isPlainRecord as k, lazy as l, slimKindOf as s, unset as u };
|