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,12 +1,12 @@
|
|
|
1
|
-
import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, reactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope, nextTick } from 'vue';
|
|
2
|
-
import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform.
|
|
3
|
-
import { c as armDomBinding } from './attaform.
|
|
1
|
+
import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, reactive, shallowReactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope, nextTick } from 'vue';
|
|
2
|
+
import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform.WiGdIqgx.mjs';
|
|
3
|
+
import { c as armDomBinding } from './attaform.D_f1WgYf.mjs';
|
|
4
4
|
import { R as REGISTER_OWNER_MARKER, b as V_REGISTER_MARKER } from './attaform.KvlEedlF.mjs';
|
|
5
|
-
import { A as ANONYMOUS_FORM_KEY_PREFIX,
|
|
6
|
-
import { i as isPathPrefix, e as canonicalizePath,
|
|
5
|
+
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 ANONYMOUS_WIZARD_KEY_PREFIX } from './attaform.8mfKZCwH.mjs';
|
|
6
|
+
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.CdZIEf7_.mjs';
|
|
7
7
|
|
|
8
8
|
const AttaformErrorCode = {
|
|
9
|
-
/** A required field is in the blank set
|
|
9
|
+
/** A required field is in the blank set: no value was supplied. */
|
|
10
10
|
NoValueSupplied: "atta:no-value-supplied",
|
|
11
11
|
/** The schema adapter's `validateAtPath` threw synchronously. */
|
|
12
12
|
AdapterThrew: "atta:adapter-threw",
|
|
@@ -78,11 +78,6 @@ function makeBlankRequiredError(segments) {
|
|
|
78
78
|
}
|
|
79
79
|
const NO_ERRORS = Object.freeze([]);
|
|
80
80
|
|
|
81
|
-
function crossCopyState(key, create) {
|
|
82
|
-
const carrier = globalThis;
|
|
83
|
-
return carrier[key] ?? (carrier[key] = create());
|
|
84
|
-
}
|
|
85
|
-
|
|
86
81
|
function isGateOpen(field, formMeta) {
|
|
87
82
|
return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
|
|
88
83
|
}
|
|
@@ -104,45 +99,32 @@ function earliestNonNull(a, b) {
|
|
|
104
99
|
return a < b ? a : b;
|
|
105
100
|
}
|
|
106
101
|
const DEFAULT_TIMINGS = { showDelay: 120, minVisible: 120 };
|
|
102
|
+
const { showDelay, minVisible } = DEFAULT_TIMINGS;
|
|
107
103
|
const FOCUS_OUT_GRACE = 16;
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
()
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (!isGateOpen(field, formMeta)) return { display: verdict };
|
|
122
|
-
const inFlightSince = earliestNonNull(validatingSince, transformingSince);
|
|
123
|
-
if (inFlightSince === null) {
|
|
124
|
-
if (prev.display === "pending") {
|
|
125
|
-
const shownAt = prev.pendingShownAt ?? now;
|
|
126
|
-
if (now < shownAt + minVisible)
|
|
127
|
-
return { display: "pending", pendingShownAt: shownAt, reviewAt: shownAt + minVisible };
|
|
128
|
-
}
|
|
129
|
-
return { display: verdict };
|
|
130
|
-
}
|
|
131
|
-
if (prev.display === "pending")
|
|
132
|
-
return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
|
|
133
|
-
const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
|
|
134
|
-
if (now - inFlightSince < window) {
|
|
135
|
-
return { display: prev.display, reviewAt: inFlightSince + window };
|
|
104
|
+
const defaultDisplayState = (prev, { field, formMeta, validatingSince, transformingSince, now }) => {
|
|
105
|
+
const verdict = computeVerdict(field, formMeta);
|
|
106
|
+
if (!isGateOpen(field, formMeta)) return { display: verdict };
|
|
107
|
+
const inFlightSince = earliestNonNull(validatingSince, transformingSince);
|
|
108
|
+
if (inFlightSince === null) {
|
|
109
|
+
if (prev.display === "pending") {
|
|
110
|
+
const shownAt = prev.pendingShownAt ?? now;
|
|
111
|
+
if (now < shownAt + minVisible)
|
|
112
|
+
return {
|
|
113
|
+
display: "pending",
|
|
114
|
+
pendingShownAt: shownAt,
|
|
115
|
+
reviewAt: shownAt + minVisible
|
|
116
|
+
};
|
|
136
117
|
}
|
|
137
|
-
return { display:
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
118
|
+
return { display: verdict };
|
|
119
|
+
}
|
|
120
|
+
if (prev.display === "pending")
|
|
121
|
+
return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
|
|
122
|
+
const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
|
|
123
|
+
if (now - inFlightSince < window) {
|
|
124
|
+
return { display: prev.display, reviewAt: inFlightSince + window };
|
|
125
|
+
}
|
|
126
|
+
return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
|
|
127
|
+
};
|
|
146
128
|
|
|
147
129
|
function hashStableString(input, seed = 0) {
|
|
148
130
|
let h1 = 3735928559 ^ seed;
|
|
@@ -230,16 +212,27 @@ function descendStep(value, segment) {
|
|
|
230
212
|
}
|
|
231
213
|
function getAtPath(root, path) {
|
|
232
214
|
if (path.length === 0) return root;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
215
|
+
try {
|
|
216
|
+
let current = root;
|
|
217
|
+
for (const segment of path) {
|
|
218
|
+
const next = descendStep(current, segment);
|
|
219
|
+
if (next === NOT_FOUND) return void 0;
|
|
220
|
+
current = next;
|
|
221
|
+
}
|
|
222
|
+
return current;
|
|
223
|
+
} catch {
|
|
224
|
+
return void 0;
|
|
238
225
|
}
|
|
239
|
-
return current;
|
|
240
226
|
}
|
|
241
227
|
function hasAtPath(root, path) {
|
|
242
228
|
if (path.length === 0) return true;
|
|
229
|
+
try {
|
|
230
|
+
return hasAtPathUnguarded(root, path);
|
|
231
|
+
} catch {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function hasAtPathUnguarded(root, path) {
|
|
243
236
|
let current = root;
|
|
244
237
|
for (let i = 0; i < path.length - 1; i++) {
|
|
245
238
|
const segment = path[i];
|
|
@@ -266,29 +259,15 @@ function isPlainRecord(value) {
|
|
|
266
259
|
const proto = Object.getPrototypeOf(value);
|
|
267
260
|
return proto === null || proto === Object.prototype;
|
|
268
261
|
}
|
|
262
|
+
const NO_SCHEMA_DEFAULTS = /* @__PURE__ */ new Set();
|
|
263
|
+
const NO_SCHEMA_FILL = {
|
|
264
|
+
getDefaultAtPath: () => void 0,
|
|
265
|
+
arrayShapeAtPath: () => null,
|
|
266
|
+
getSlimPrimitiveTypesAtPath: () => NO_SCHEMA_DEFAULTS,
|
|
267
|
+
entryKeyKindAtPath: () => void 0
|
|
268
|
+
};
|
|
269
269
|
function setAtPath(root, path, value) {
|
|
270
|
-
return
|
|
271
|
-
}
|
|
272
|
-
function setAtPathOffset(root, path, value, offset) {
|
|
273
|
-
if (offset >= path.length) return value;
|
|
274
|
-
const head = path[offset];
|
|
275
|
-
const nextOffset = offset + 1;
|
|
276
|
-
if (root instanceof Map) {
|
|
277
|
-
const next = new Map(root);
|
|
278
|
-
const key = mapKeyForSegment(root, head);
|
|
279
|
-
next.set(key, setAtPathOffset(root.get(key), path, value, nextOffset));
|
|
280
|
-
return next;
|
|
281
|
-
}
|
|
282
|
-
if (!isRebuildableContainer(root, head)) return root;
|
|
283
|
-
if (typeof head === "number") {
|
|
284
|
-
const arr = Array.isArray(root) ? [...root] : [];
|
|
285
|
-
while (arr.length <= head) arr.push(void 0);
|
|
286
|
-
arr[head] = setAtPathOffset(arr[head], path, value, nextOffset);
|
|
287
|
-
return arr;
|
|
288
|
-
}
|
|
289
|
-
const rec = isPlainRecord(root) ? { ...root } : {};
|
|
290
|
-
safeAssign(rec, head, setAtPathOffset(safeOwnRead(rec, head), path, value, nextOffset));
|
|
291
|
-
return rec;
|
|
270
|
+
return setAtPathWithSchemaFill(root, NO_SCHEMA_FILL, path, value);
|
|
292
271
|
}
|
|
293
272
|
const NO_IN_PLACE = { applied: false };
|
|
294
273
|
function tryInPlaceLeafWrite(root, path, value) {
|
|
@@ -494,10 +473,10 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
|
|
|
494
473
|
}
|
|
495
474
|
const rec = isPlainRecord(root) ? { ...root } : {};
|
|
496
475
|
if (isLeafStep) {
|
|
497
|
-
rec
|
|
476
|
+
safeAssign(rec, head, value);
|
|
498
477
|
return rec;
|
|
499
478
|
}
|
|
500
|
-
const existing = rec
|
|
479
|
+
const existing = safeOwnRead(rec, head);
|
|
501
480
|
let childRoot;
|
|
502
481
|
if (existing === void 0 || existing !== null && typeof existing !== "object") {
|
|
503
482
|
const intermPath = [...fullPath.slice(0, startIdx + 1)];
|
|
@@ -506,7 +485,11 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
|
|
|
506
485
|
} else {
|
|
507
486
|
childRoot = existing;
|
|
508
487
|
}
|
|
509
|
-
|
|
488
|
+
safeAssign(
|
|
489
|
+
rec,
|
|
490
|
+
head,
|
|
491
|
+
setAtPathWithSchemaFillImpl(childRoot, schema, fullPath, value, startIdx + 1)
|
|
492
|
+
);
|
|
510
493
|
return rec;
|
|
511
494
|
}
|
|
512
495
|
|
|
@@ -523,16 +506,15 @@ function isUnderStubAncestor(state, segments) {
|
|
|
523
506
|
}
|
|
524
507
|
return false;
|
|
525
508
|
}
|
|
526
|
-
function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, sweep
|
|
509
|
+
function buildFieldStateAccessor(state, formInstanceId, getFormMetaBase, sweep) {
|
|
527
510
|
const cache = /* @__PURE__ */ new Map();
|
|
528
|
-
const predicate = options?.getDisplayState;
|
|
529
511
|
sweep.onEvict((key) => cache.delete(key));
|
|
530
512
|
return function getFieldState(pathInput) {
|
|
531
513
|
const { segments, key } = canonicalizePath(pathInput);
|
|
532
514
|
const cached = cache.get(key);
|
|
533
515
|
if (cached !== void 0) return cached;
|
|
534
516
|
const c = computed(
|
|
535
|
-
() => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase
|
|
517
|
+
() => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase) : buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase)
|
|
536
518
|
);
|
|
537
519
|
cache.set(key, c);
|
|
538
520
|
sweep.track(segments, key);
|
|
@@ -549,7 +531,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
549
531
|
const record = state.fields.get(key);
|
|
550
532
|
const value = state.getValueAtPath(segments);
|
|
551
533
|
const original = state.originals.get(key)?.value;
|
|
552
|
-
const pristine = state.
|
|
534
|
+
const pristine = state.isPristineAtPathByKey(key, segments);
|
|
553
535
|
const cell = state.errorCells.get(key);
|
|
554
536
|
const schemaForKey = cell !== void 0 && cell.schema.length > 0 ? cell.schema : void 0;
|
|
555
537
|
const blankForKey = state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments) ? [makeBlankRequiredError(segments)] : void 0;
|
|
@@ -561,7 +543,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
561
543
|
const validating = (state.fieldValidationCounts.get(key) ?? 0) > 0;
|
|
562
544
|
const transforming = (state.fieldTransformCounts.get(key) ?? 0) > 0;
|
|
563
545
|
const transformError = state.transformErrors.get(key) ?? null;
|
|
564
|
-
const gated = state.
|
|
546
|
+
const gated = state.pathHasAsyncValidationByKey(key, segments) && !state.firstValidationDone.value;
|
|
565
547
|
const isOrphan = segments.length > 0 && !hasAtPath(state.form.value, segments) && isUnderStubAncestor(state, segments);
|
|
566
548
|
const valid = !gated && errors.length === 0 && !validating && !isOrphan;
|
|
567
549
|
const elementRecord = state.domBinding.value?.elements.get(key);
|
|
@@ -584,9 +566,8 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
584
566
|
updatedAt: record?.updatedAt ?? null,
|
|
585
567
|
errors,
|
|
586
568
|
// 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).
|
|
569
|
+
// errors. Reuse the array reference, which makes `ownErrors === errors`
|
|
570
|
+
// and avoids a second read of the whole-form `derivedBlankErrors` map.
|
|
590
571
|
ownErrors: errors,
|
|
591
572
|
validating,
|
|
592
573
|
valid,
|
|
@@ -604,7 +585,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
604
585
|
meta: resolved.meta
|
|
605
586
|
};
|
|
606
587
|
}
|
|
607
|
-
function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase
|
|
588
|
+
function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase) {
|
|
608
589
|
const base = buildLeafFieldStateBase(state, segments, key, formInstanceId);
|
|
609
590
|
const validatingSince = state.fieldValidatingSince.get(key) ?? null;
|
|
610
591
|
const transformingSince = state.fieldTransformingSince.get(key) ?? null;
|
|
@@ -617,9 +598,8 @@ function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBa
|
|
|
617
598
|
transformingSince,
|
|
618
599
|
false,
|
|
619
600
|
// revealedDescendantError: leaves have no descendants
|
|
620
|
-
false
|
|
601
|
+
false
|
|
621
602
|
// isRoot: a leaf is never the form root
|
|
622
|
-
getDisplayState
|
|
623
603
|
);
|
|
624
604
|
}
|
|
625
605
|
function visitActiveLeafPaths(value, base, visit) {
|
|
@@ -717,7 +697,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
717
697
|
pristine = false;
|
|
718
698
|
dirty = true;
|
|
719
699
|
}
|
|
720
|
-
const errors = aggregateErrorsAt(state, segments);
|
|
700
|
+
const errors = aggregateErrorsAt(state, segments, key);
|
|
721
701
|
const revealedDescendantError = errors.length > 0 && errors.some((e) => {
|
|
722
702
|
const ePath = e.path;
|
|
723
703
|
if (ePath.length === segments.length && ePath.every((s, i) => s === segments[i])) return false;
|
|
@@ -725,7 +705,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
725
705
|
if (ePath.length === 1 && ePath[0] === "") return blurredAfterInteraction;
|
|
726
706
|
return blurredLeafSegments.some((s) => isPathPrefix(ePath, s));
|
|
727
707
|
});
|
|
728
|
-
if (!asyncPending && state.
|
|
708
|
+
if (!asyncPending && state.pathHasAsyncValidationByKey(key, segments)) asyncPending = true;
|
|
729
709
|
if ((state.fieldValidationCounts.get(key) ?? 0) > 0) {
|
|
730
710
|
validating = true;
|
|
731
711
|
const since = state.fieldValidatingSince.get(key);
|
|
@@ -767,19 +747,19 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
767
747
|
elements: EMPTY_ELEMENTS,
|
|
768
748
|
updatedAt,
|
|
769
749
|
errors,
|
|
770
|
-
// The container's OWN bucket
|
|
771
|
-
// descendant
|
|
772
|
-
//
|
|
773
|
-
//
|
|
774
|
-
//
|
|
750
|
+
// The container's OWN bucket at this exact path, excluding every
|
|
751
|
+
// descendant `errors` rolls up: a cross-field `.refine()` here, or a
|
|
752
|
+
// `setErrors` pinned at this node. The same raw union the leaf builds
|
|
753
|
+
// from the three stores, and the container already depends on all three
|
|
754
|
+
// through `aggregateErrorsAt`, so it costs no new reactivity.
|
|
775
755
|
ownErrors: state.getErrorsForPath(segments),
|
|
776
756
|
validating,
|
|
777
757
|
valid,
|
|
778
758
|
transforming,
|
|
779
759
|
busy: transforming || validating,
|
|
780
|
-
// A container surfaces its OWN transform failure
|
|
781
|
-
// on the container path
|
|
782
|
-
// descendant
|
|
760
|
+
// A container surfaces its OWN transform failure, from a transform
|
|
761
|
+
// registered on the container path such as a file normalizer, and never
|
|
762
|
+
// rolls up a descendant's: that stays a per-field channel.
|
|
783
763
|
transformError: ownTransformError,
|
|
784
764
|
path: segments,
|
|
785
765
|
...computeFieldIdentity(formInstanceId, state.formKey, key),
|
|
@@ -796,7 +776,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
796
776
|
revealedDescendantError
|
|
797
777
|
};
|
|
798
778
|
}
|
|
799
|
-
function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase
|
|
779
|
+
function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase) {
|
|
800
780
|
const { base, validatingSince, transformingSince, revealedDescendantError } = buildContainerFieldStateBase(state, segments, key, formInstanceId);
|
|
801
781
|
return decorateWithDerivedProps(
|
|
802
782
|
base,
|
|
@@ -806,14 +786,12 @@ function buildContainerFieldState(state, segments, key, formInstanceId, getFormM
|
|
|
806
786
|
validatingSince,
|
|
807
787
|
transformingSince,
|
|
808
788
|
revealedDescendantError,
|
|
809
|
-
segments.length === 0
|
|
810
|
-
getDisplayState
|
|
789
|
+
segments.length === 0
|
|
811
790
|
);
|
|
812
791
|
}
|
|
813
|
-
function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, transformingSince, revealedDescendantError, isRoot
|
|
792
|
+
function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, transformingSince, revealedDescendantError, isRoot) {
|
|
814
793
|
const firstError = base.errors[0];
|
|
815
794
|
const firstOwnError = base.ownErrors[0];
|
|
816
|
-
const predicate = getDisplayState ?? state.getDisplayState;
|
|
817
795
|
const formMeta = getFormMetaBase();
|
|
818
796
|
const ctx = {
|
|
819
797
|
field: base,
|
|
@@ -824,16 +802,9 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
|
|
|
824
802
|
// flight) so the reducer returns the plain verdict and hydration matches.
|
|
825
803
|
now: state.ssr ? 0 : Date.now()
|
|
826
804
|
};
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
machine = state.displayEngine.resolve(key, ctx, predicate);
|
|
831
|
-
} catch (err) {
|
|
832
|
-
machine = state.displayEngine.resolve(key, ctx, defaultDisplayState);
|
|
833
|
-
rollupApplies = true;
|
|
834
|
-
}
|
|
835
|
-
const submitValidating = rollupApplies && isRoot && state.submitting.value && state.activeValidations.value > 0;
|
|
836
|
-
const resolvedDisplayState = machine.display === "pending" || submitValidating ? "pending" : rollupApplies && revealedDescendantError ? "error" : machine.display;
|
|
805
|
+
const machine = state.displayEngine.resolve(key, ctx, defaultDisplayState);
|
|
806
|
+
const submitValidating = isRoot && state.submitting.value && state.activeValidations.value > 0;
|
|
807
|
+
const resolvedDisplayState = machine.display === "pending" || submitValidating ? "pending" : revealedDescendantError ? "error" : machine.display;
|
|
837
808
|
const displayState = base.disabled === true ? "idle" : resolvedDisplayState;
|
|
838
809
|
return {
|
|
839
810
|
...base,
|
|
@@ -846,26 +817,33 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
|
|
|
846
817
|
firstOwnError
|
|
847
818
|
};
|
|
848
819
|
}
|
|
849
|
-
function aggregateErrorsAt(state, prefix) {
|
|
820
|
+
function aggregateErrorsAt(state, prefix, prefixKey) {
|
|
821
|
+
const candidates = state.errorWindowAt(prefix, prefixKey);
|
|
822
|
+
if (candidates.length === 0) return [];
|
|
850
823
|
const formValue = state.form.value;
|
|
824
|
+
const cells = state.errorCells;
|
|
851
825
|
const buckets = /* @__PURE__ */ new Map();
|
|
852
|
-
const
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
826
|
+
for (const { key, segments } of candidates) {
|
|
827
|
+
if (!isPathPrefix(prefix, segments)) continue;
|
|
828
|
+
if (segments.length > 0 && !hasAtPath(formValue, segments)) continue;
|
|
829
|
+
const cell = cells.get(key);
|
|
830
|
+
const blankList = state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments) ? [makeBlankRequiredError(segments)] : void 0;
|
|
831
|
+
const schemaList = cell?.schema;
|
|
832
|
+
const userList = cell?.user;
|
|
833
|
+
const total = (schemaList?.length ?? 0) + (blankList?.length ?? 0) + (userList?.length ?? 0);
|
|
834
|
+
if (total === 0) continue;
|
|
835
|
+
const ordinal = state.ensurePathOrdinal(key);
|
|
836
|
+
let bucket = buckets.get(ordinal);
|
|
837
|
+
if (bucket === void 0) {
|
|
838
|
+
bucket = [];
|
|
839
|
+
buckets.set(ordinal, bucket);
|
|
840
|
+
}
|
|
841
|
+
if (schemaList !== void 0) bucket.push(...schemaList);
|
|
842
|
+
if (blankList !== void 0) bucket.push(...blankList);
|
|
843
|
+
if (userList !== void 0) bucket.push(...userList);
|
|
844
|
+
}
|
|
868
845
|
if (buckets.size === 0) return [];
|
|
846
|
+
if (buckets.size === 1) return [...buckets.values()][0];
|
|
869
847
|
return [...buckets.entries()].sort(([a], [b]) => a - b).flatMap(([, errs]) => errs);
|
|
870
848
|
}
|
|
871
849
|
const EMPTY_ELEMENTS = Object.freeze([]);
|
|
@@ -957,7 +935,7 @@ function buildTree(spec) {
|
|
|
957
935
|
existsCache.delete(key);
|
|
958
936
|
});
|
|
959
937
|
function schemaHasPath(segs) {
|
|
960
|
-
const cacheKey =
|
|
938
|
+
const cacheKey = keyForSegments(segs).key;
|
|
961
939
|
const cached = existsCache.get(cacheKey);
|
|
962
940
|
if (cached !== void 0) return cached;
|
|
963
941
|
const result = spec.schema.getSlimPrimitiveTypesAtPath(segs).size > 0;
|
|
@@ -972,7 +950,7 @@ function buildTree(spec) {
|
|
|
972
950
|
}
|
|
973
951
|
function containerAt(segments) {
|
|
974
952
|
const isArrayLike = spec.isArrayAt(segments);
|
|
975
|
-
const pathKey =
|
|
953
|
+
const pathKey = keyForSegments(segments).key;
|
|
976
954
|
const cacheKey = `${pathKey}+${isArrayLike ? "A" : "O"}`;
|
|
977
955
|
const existing = containerCache.get(cacheKey);
|
|
978
956
|
if (existing !== void 0) return existing;
|
|
@@ -1072,7 +1050,7 @@ function buildErrorsSurface(state, sweep) {
|
|
|
1072
1050
|
const treeCache = /* @__PURE__ */ new Map();
|
|
1073
1051
|
sweep.onEvict((key) => treeCache.delete(key));
|
|
1074
1052
|
const materialize = (segments) => {
|
|
1075
|
-
const cacheKey =
|
|
1053
|
+
const cacheKey = keyForSegments(segments).key;
|
|
1076
1054
|
let tree = treeCache.get(cacheKey);
|
|
1077
1055
|
if (tree === void 0) {
|
|
1078
1056
|
const frozen = [...segments];
|
|
@@ -1084,28 +1062,21 @@ function buildErrorsSurface(state, sweep) {
|
|
|
1084
1062
|
};
|
|
1085
1063
|
const resolveLeaf = (path) => {
|
|
1086
1064
|
const isContainerSelfAccess = path.length > 1 && path[path.length - 1] === "";
|
|
1087
|
-
const collectAtKey = (key2, active2, into) => {
|
|
1088
|
-
const cell = state.errorCells.get(key2);
|
|
1089
|
-
if (active2) {
|
|
1090
|
-
const b = state.derivedBlankErrors.value.get(key2);
|
|
1091
|
-
if (cell !== void 0) into.push(...cell.schema);
|
|
1092
|
-
if (b !== void 0) into.push(...b);
|
|
1093
|
-
}
|
|
1094
|
-
if (cell !== void 0) into.push(...cell.user);
|
|
1095
|
-
};
|
|
1096
1065
|
const merged = [];
|
|
1097
1066
|
if (isContainerSelfAccess) {
|
|
1098
1067
|
const containerPath = path.slice(0, -1);
|
|
1099
|
-
const
|
|
1100
|
-
const
|
|
1068
|
+
const container = canonicalizePath(containerPath);
|
|
1069
|
+
const literal = canonicalizePath(path);
|
|
1101
1070
|
const active2 = hasAtPath(state.form.value, containerPath);
|
|
1102
|
-
|
|
1103
|
-
if (
|
|
1071
|
+
collectErrorsAt(state, container.key, container.segments, active2, merged);
|
|
1072
|
+
if (literal.key !== container.key) {
|
|
1073
|
+
collectErrorsAt(state, literal.key, literal.segments, active2, merged);
|
|
1074
|
+
}
|
|
1104
1075
|
return merged;
|
|
1105
1076
|
}
|
|
1106
|
-
const { key } = canonicalizePath(path);
|
|
1077
|
+
const { key, segments } = canonicalizePath(path);
|
|
1107
1078
|
const active = hasAtPath(state.form.value, path);
|
|
1108
|
-
|
|
1079
|
+
collectErrorsAt(state, key, segments, active, merged);
|
|
1109
1080
|
return merged;
|
|
1110
1081
|
};
|
|
1111
1082
|
return buildTree({
|
|
@@ -1120,68 +1091,83 @@ function buildErrorsSurface(state, sweep) {
|
|
|
1120
1091
|
// non-schema keys reachable while a genuinely-absent key stays out.
|
|
1121
1092
|
hasOwn: (segments, key) => liveContainerHasKey(state, segments, key) || errorAwareContainerKeys(state, segments).includes(key),
|
|
1122
1093
|
isArrayAt: (segments) => isArrayPath(state, segments),
|
|
1123
|
-
// `errors()
|
|
1124
|
-
// aggregate
|
|
1125
|
-
//
|
|
1126
|
-
call: (path) => aggregateErrorsAt(state, path),
|
|
1094
|
+
// `errors()`, `errors([])` and `errors(path)` are all the subtree
|
|
1095
|
+
// aggregate, through the helper `meta.errors` reads, so they cannot
|
|
1096
|
+
// drift.
|
|
1097
|
+
call: (path) => aggregateErrorsAt(state, path, keyForSegments(path).key),
|
|
1127
1098
|
surface: "form.errors",
|
|
1128
1099
|
sweep
|
|
1129
1100
|
});
|
|
1130
1101
|
}
|
|
1102
|
+
function collectErrorsAt(state, key, segments, active, into) {
|
|
1103
|
+
const cell = state.errorCells.get(key);
|
|
1104
|
+
if (active) {
|
|
1105
|
+
if (cell !== void 0) into.push(...cell.schema);
|
|
1106
|
+
if (state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments)) {
|
|
1107
|
+
into.push(makeBlankRequiredError(segments));
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
if (cell !== void 0) into.push(...cell.user);
|
|
1111
|
+
}
|
|
1131
1112
|
function errorAwareContainerKeys(state, segments) {
|
|
1132
1113
|
const keys = new Set(liveKeysAtPath(state, segments));
|
|
1133
1114
|
const formValue = state.form.value;
|
|
1134
|
-
const
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1115
|
+
const errors = [];
|
|
1116
|
+
const found = [];
|
|
1117
|
+
for (const { key, segments: decoded } of state.errorWindowAt(
|
|
1118
|
+
segments,
|
|
1119
|
+
keyForSegments(segments).key
|
|
1120
|
+
)) {
|
|
1121
|
+
if (decoded.length <= segments.length) continue;
|
|
1122
|
+
if (!isPathPrefix(segments, decoded)) continue;
|
|
1123
|
+
errors.length = 0;
|
|
1124
|
+
collectErrorsAt(state, key, decoded, hasAtPath(formValue, decoded), errors);
|
|
1125
|
+
if (errors.length === 0) continue;
|
|
1126
|
+
const nextSeg = decoded[segments.length];
|
|
1127
|
+
found.push({
|
|
1128
|
+
childKey: typeof nextSeg === "number" ? String(nextSeg) : nextSeg,
|
|
1129
|
+
ordinal: state.ensurePathOrdinal(key)
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
found.sort((a, b) => a.ordinal - b.ordinal);
|
|
1133
|
+
for (const { childKey } of found) keys.add(childKey);
|
|
1149
1134
|
return [...keys];
|
|
1150
1135
|
}
|
|
1151
1136
|
function materializeErrors(state, containerSegments) {
|
|
1152
|
-
const
|
|
1137
|
+
const formValue = state.form.value;
|
|
1138
|
+
const liveContainer = getAtPath(formValue, containerSegments);
|
|
1153
1139
|
const tree = Array.isArray(liveContainer) ? [] : {};
|
|
1154
|
-
const
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
for (let i = 0; i < containerSegments.length; i++) {
|
|
1165
|
-
if (fullPath[i] !== containerSegments[i]) continue entries;
|
|
1166
|
-
}
|
|
1167
|
-
if (applyActivePathFilter && !hasAtPath(state.form.value, fullPath)) continue;
|
|
1168
|
-
const relativePath = fullPath.slice(containerSegments.length);
|
|
1169
|
-
let placePath;
|
|
1170
|
-
if (relativePath.length === 0) {
|
|
1171
|
-
placePath = [""];
|
|
1172
|
-
} else if (state.schema.isLeafAtPath(fullPath)) {
|
|
1173
|
-
placePath = relativePath;
|
|
1174
|
-
} else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
|
|
1175
|
-
placePath = [...relativePath, ""];
|
|
1176
|
-
} else {
|
|
1177
|
-
placePath = relativePath;
|
|
1178
|
-
}
|
|
1179
|
-
placeAt(tree, placePath, errors);
|
|
1140
|
+
const placements = [];
|
|
1141
|
+
for (const { key, segments: fullPath } of state.errorWindowAt(
|
|
1142
|
+
containerSegments,
|
|
1143
|
+
keyForSegments(containerSegments).key
|
|
1144
|
+
)) {
|
|
1145
|
+
const isRoot = fullPath.length === 0;
|
|
1146
|
+
if (isRoot) {
|
|
1147
|
+
if (containerSegments.length !== 0) continue;
|
|
1148
|
+
} else if (!isPathPrefix(containerSegments, fullPath)) {
|
|
1149
|
+
continue;
|
|
1180
1150
|
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1151
|
+
const errors = [];
|
|
1152
|
+
collectErrorsAt(state, key, fullPath, isRoot || hasAtPath(formValue, fullPath), errors);
|
|
1153
|
+
if (errors.length === 0) continue;
|
|
1154
|
+
const relativePath = fullPath.slice(containerSegments.length);
|
|
1155
|
+
let placePath;
|
|
1156
|
+
if (isRoot) {
|
|
1157
|
+
placePath = [ROOT_PATH_KEY];
|
|
1158
|
+
} else if (relativePath.length === 0) {
|
|
1159
|
+
placePath = [""];
|
|
1160
|
+
} else if (state.schema.isLeafAtPath(fullPath)) {
|
|
1161
|
+
placePath = relativePath;
|
|
1162
|
+
} else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
|
|
1163
|
+
placePath = [...relativePath, ""];
|
|
1164
|
+
} else {
|
|
1165
|
+
placePath = relativePath;
|
|
1166
|
+
}
|
|
1167
|
+
placements.push({ placePath, errors, ordinal: state.ensurePathOrdinal(key) });
|
|
1168
|
+
}
|
|
1169
|
+
placements.sort((a, b) => a.ordinal - b.ordinal);
|
|
1170
|
+
for (const { placePath, errors } of placements) placeAt(tree, placePath, errors);
|
|
1185
1171
|
return tree;
|
|
1186
1172
|
}
|
|
1187
1173
|
function placeAt(tree, path, errors) {
|
|
@@ -1254,7 +1240,7 @@ function buildFieldsSurface(state, getFieldStateAt, sweep) {
|
|
|
1254
1240
|
const viewCache = /* @__PURE__ */ new Map();
|
|
1255
1241
|
sweep.onEvict((key) => viewCache.delete(key));
|
|
1256
1242
|
function viewAt(segments) {
|
|
1257
|
-
const cacheKey =
|
|
1243
|
+
const cacheKey = keyForSegments(segments).key;
|
|
1258
1244
|
const existing = viewCache.get(cacheKey);
|
|
1259
1245
|
if (existing !== void 0) return existing;
|
|
1260
1246
|
const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(
|
|
@@ -1816,45 +1802,6 @@ function buildFieldArrayApi(state) {
|
|
|
1816
1802
|
};
|
|
1817
1803
|
}
|
|
1818
1804
|
|
|
1819
|
-
const SWEEP_SLICE = 8;
|
|
1820
|
-
function createDynamicPathSweep(state) {
|
|
1821
|
-
const dynamicKeys = /* @__PURE__ */ new Set();
|
|
1822
|
-
const evictors = [];
|
|
1823
|
-
const isDynamicPath = (segments) => {
|
|
1824
|
-
for (let i = 0; i < segments.length; i++) {
|
|
1825
|
-
if (typeof segments[i] === "number") return true;
|
|
1826
|
-
if (!state.schema.isFixedObjectAtPath(segments.slice(0, i))) return true;
|
|
1827
|
-
}
|
|
1828
|
-
return false;
|
|
1829
|
-
};
|
|
1830
|
-
let sweepCursor = null;
|
|
1831
|
-
state.onFormChange((next) => {
|
|
1832
|
-
if (dynamicKeys.size === 0) return;
|
|
1833
|
-
const raw = toRaw(next);
|
|
1834
|
-
for (let i = 0; i < SWEEP_SLICE; i++) {
|
|
1835
|
-
sweepCursor ?? (sweepCursor = dynamicKeys.values());
|
|
1836
|
-
const step = sweepCursor.next();
|
|
1837
|
-
if (step.done === true) {
|
|
1838
|
-
sweepCursor = null;
|
|
1839
|
-
break;
|
|
1840
|
-
}
|
|
1841
|
-
const segments = segmentsForPathKey(step.value);
|
|
1842
|
-
if (segments !== null && hasAtPath(raw, segments)) continue;
|
|
1843
|
-
dynamicKeys.delete(step.value);
|
|
1844
|
-
for (const evict of evictors) evict(step.value);
|
|
1845
|
-
}
|
|
1846
|
-
});
|
|
1847
|
-
return {
|
|
1848
|
-
track(segments, key) {
|
|
1849
|
-
if (dynamicKeys.has(key)) return;
|
|
1850
|
-
if (isDynamicPath(segments)) dynamicKeys.add(key);
|
|
1851
|
-
},
|
|
1852
|
-
onEvict(evict) {
|
|
1853
|
-
evictors.push(evict);
|
|
1854
|
-
}
|
|
1855
|
-
};
|
|
1856
|
-
}
|
|
1857
|
-
|
|
1858
1805
|
function isErrorInputLike(value) {
|
|
1859
1806
|
if (value instanceof Error) return true;
|
|
1860
1807
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -1881,8 +1828,8 @@ function deriveSubmitErrors(err) {
|
|
|
1881
1828
|
messageless: true
|
|
1882
1829
|
};
|
|
1883
1830
|
}
|
|
1884
|
-
function buildProcessForm(state,
|
|
1885
|
-
const
|
|
1831
|
+
function buildProcessForm(state, options = {}) {
|
|
1832
|
+
const applyInvalidSubmit = options.applyInvalidSubmit;
|
|
1886
1833
|
function validate(pathInput) {
|
|
1887
1834
|
const result = ref({
|
|
1888
1835
|
pending: true,
|
|
@@ -1975,7 +1922,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1975
1922
|
};
|
|
1976
1923
|
}
|
|
1977
1924
|
async function runRefinementValidation(data, path) {
|
|
1978
|
-
return await state.schema.validateAtPath(data, path);
|
|
1925
|
+
return stampFormKey(await state.schema.validateAtPath(data, path), state.formKey);
|
|
1979
1926
|
}
|
|
1980
1927
|
function composeWithDerivedBlank(refinement, scope) {
|
|
1981
1928
|
const blankErrors = collectScopedBlankErrors(state, scope);
|
|
@@ -2021,7 +1968,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
2021
1968
|
}
|
|
2022
1969
|
}
|
|
2023
1970
|
if (generationStillValid) {
|
|
2024
|
-
|
|
1971
|
+
applyInvalidSubmit?.();
|
|
2025
1972
|
}
|
|
2026
1973
|
if (onError !== void 0) {
|
|
2027
1974
|
try {
|
|
@@ -2038,7 +1985,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
2038
1985
|
await onSubmit(merged.data);
|
|
2039
1986
|
if (hasUserErrorEntries(state)) {
|
|
2040
1987
|
if (state.submissionGeneration.value === genAtEntry) {
|
|
2041
|
-
|
|
1988
|
+
applyInvalidSubmit?.();
|
|
2042
1989
|
}
|
|
2043
1990
|
return;
|
|
2044
1991
|
}
|
|
@@ -2056,7 +2003,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
2056
2003
|
if (segments === null) continue;
|
|
2057
2004
|
state.setUserErrorsForPath([...segments], bucket);
|
|
2058
2005
|
}
|
|
2059
|
-
|
|
2006
|
+
applyInvalidSubmit?.();
|
|
2060
2007
|
}
|
|
2061
2008
|
}
|
|
2062
2009
|
} finally {
|
|
@@ -2071,6 +2018,10 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
2071
2018
|
};
|
|
2072
2019
|
return { validate, parse, handleSubmit };
|
|
2073
2020
|
}
|
|
2021
|
+
function stampFormKey(verdict, formKey) {
|
|
2022
|
+
if (verdict.success) return { ...verdict, formKey };
|
|
2023
|
+
return verdict.data === void 0 ? { data: void 0, errors: verdict.errors, success: false, formKey } : { data: verdict.data, errors: verdict.errors, success: false, formKey };
|
|
2024
|
+
}
|
|
2074
2025
|
function toSegments(pathInput) {
|
|
2075
2026
|
return canonicalizePath(pathInput).segments;
|
|
2076
2027
|
}
|
|
@@ -2104,21 +2055,6 @@ function collectScopedBlankErrors(state, scope) {
|
|
|
2104
2055
|
}
|
|
2105
2056
|
return errors;
|
|
2106
2057
|
}
|
|
2107
|
-
function applyInvalidSubmitPolicy(state, formInstanceId, policy) {
|
|
2108
|
-
if (policy === "none") return;
|
|
2109
|
-
const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
|
|
2110
|
-
if (target === null) return;
|
|
2111
|
-
if (policy === "scroll-to-first-error") {
|
|
2112
|
-
target.element.scrollIntoView();
|
|
2113
|
-
return;
|
|
2114
|
-
}
|
|
2115
|
-
if (policy === "focus-first-error") {
|
|
2116
|
-
target.element.focus({ focusVisible: true });
|
|
2117
|
-
return;
|
|
2118
|
-
}
|
|
2119
|
-
target.element.scrollIntoView();
|
|
2120
|
-
target.element.focus({ preventScroll: true, focusVisible: true });
|
|
2121
|
-
}
|
|
2122
2058
|
|
|
2123
2059
|
function slimKindOf(value) {
|
|
2124
2060
|
if (value === null) return "null";
|
|
@@ -2182,51 +2118,9 @@ function walk(schema, store, path, value) {
|
|
|
2182
2118
|
return true;
|
|
2183
2119
|
}
|
|
2184
2120
|
|
|
2185
|
-
function defineCoercion(entry) {
|
|
2186
|
-
return entry;
|
|
2187
|
-
}
|
|
2188
2121
|
const IDENTITY = (v) => v;
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
defineCoercion({
|
|
2192
|
-
input: "string",
|
|
2193
|
-
output: "number",
|
|
2194
|
-
transform: (s) => {
|
|
2195
|
-
const trimmed = s.trim();
|
|
2196
|
-
if (trimmed === "") return { coerced: false };
|
|
2197
|
-
const n = Number(trimmed);
|
|
2198
|
-
if (!Number.isFinite(n)) return { coerced: false };
|
|
2199
|
-
return { coerced: true, value: n };
|
|
2200
|
-
}
|
|
2201
|
-
}),
|
|
2202
|
-
defineCoercion({
|
|
2203
|
-
input: "string",
|
|
2204
|
-
output: "boolean",
|
|
2205
|
-
transform: (s) => {
|
|
2206
|
-
const normalized = s.trim().toLowerCase();
|
|
2207
|
-
if (normalized === "true") return { coerced: true, value: true };
|
|
2208
|
-
if (normalized === "false") return { coerced: true, value: false };
|
|
2209
|
-
return { coerced: false };
|
|
2210
|
-
}
|
|
2211
|
-
})
|
|
2212
|
-
];
|
|
2213
|
-
function resolveCoercionIndex(config) {
|
|
2214
|
-
if (config === false) return EMPTY_INDEX;
|
|
2215
|
-
const rules = config === void 0 || config === true ? defaultCoercionRules : config;
|
|
2216
|
-
return indexRules(rules);
|
|
2217
|
-
}
|
|
2218
|
-
function indexRules(rules) {
|
|
2219
|
-
const idx = /* @__PURE__ */ new Map();
|
|
2220
|
-
for (const entry of rules) {
|
|
2221
|
-
const candidate = entry;
|
|
2222
|
-
if (candidate === null || typeof candidate !== "object" || typeof candidate.transform !== "function") {
|
|
2223
|
-
continue;
|
|
2224
|
-
}
|
|
2225
|
-
const key = `${entry.input}->${entry.output}`;
|
|
2226
|
-
if (idx.has(key) && false) ;
|
|
2227
|
-
idx.set(key, entry);
|
|
2228
|
-
}
|
|
2229
|
-
return idx;
|
|
2122
|
+
function resolveCoerceEnabled(config) {
|
|
2123
|
+
return config !== false;
|
|
2230
2124
|
}
|
|
2231
2125
|
function memberSlimTypes(schema, segments, accepted) {
|
|
2232
2126
|
if (accepted.has("array")) return schema.getSlimPrimitiveTypesAtPath([...segments, 0]);
|
|
@@ -2235,89 +2129,83 @@ function memberSlimTypes(schema, segments, accepted) {
|
|
|
2235
2129
|
}
|
|
2236
2130
|
return void 0;
|
|
2237
2131
|
}
|
|
2238
|
-
function buildCoerceFn(schema, segments,
|
|
2239
|
-
if (
|
|
2240
|
-
if (index.size === 0) return IDENTITY;
|
|
2132
|
+
function buildCoerceFn(schema, segments, enabled) {
|
|
2133
|
+
if (!enabled) return IDENTITY;
|
|
2241
2134
|
const accepted = schema.getSlimPrimitiveTypesAtPath(segments);
|
|
2242
2135
|
const elementAccepted = memberSlimTypes(schema, segments, accepted);
|
|
2243
|
-
return (value) => coerceValue(value, accepted, elementAccepted
|
|
2136
|
+
return (value) => coerceValue(value, accepted, elementAccepted);
|
|
2244
2137
|
}
|
|
2245
|
-
function buildElementCoerceFn(schema, segments,
|
|
2246
|
-
if (
|
|
2247
|
-
if (index.size === 0) return void 0;
|
|
2138
|
+
function buildElementCoerceFn(schema, segments, enabled) {
|
|
2139
|
+
if (!enabled) return void 0;
|
|
2248
2140
|
const accepted = schema.getSlimPrimitiveTypesAtPath(segments);
|
|
2249
2141
|
const elementAccepted = memberSlimTypes(schema, segments, accepted);
|
|
2250
2142
|
if (elementAccepted === void 0) return void 0;
|
|
2251
|
-
return (value) => coerceScalar(value, elementAccepted
|
|
2143
|
+
return (value) => coerceScalar(value, elementAccepted);
|
|
2252
2144
|
}
|
|
2253
2145
|
function pickScalarTarget(accepted) {
|
|
2254
2146
|
if (accepted.has("string")) return null;
|
|
2255
2147
|
if (accepted.has("number")) return "number";
|
|
2256
2148
|
if (accepted.has("boolean")) return "boolean";
|
|
2257
|
-
if (accepted.has("bigint")) return "bigint";
|
|
2258
2149
|
return null;
|
|
2259
2150
|
}
|
|
2260
|
-
function
|
|
2151
|
+
function toNumber(source) {
|
|
2152
|
+
const trimmed = source.trim();
|
|
2153
|
+
if (trimmed === "") return source;
|
|
2154
|
+
const n = Number(trimmed);
|
|
2155
|
+
return Number.isFinite(n) ? n : source;
|
|
2156
|
+
}
|
|
2157
|
+
function toBoolean(source) {
|
|
2158
|
+
const normalized = source.trim().toLowerCase();
|
|
2159
|
+
if (normalized === "true") return true;
|
|
2160
|
+
if (normalized === "false") return false;
|
|
2161
|
+
return source;
|
|
2162
|
+
}
|
|
2163
|
+
function coerceScalar(value, accepted) {
|
|
2261
2164
|
if (accepted.size === 0) return value;
|
|
2262
2165
|
const sourceKind = slimKindOf(value);
|
|
2263
2166
|
if (accepted.has(sourceKind)) return value;
|
|
2167
|
+
if (sourceKind !== "string") return value;
|
|
2264
2168
|
const target = pickScalarTarget(accepted);
|
|
2265
|
-
if (target ===
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
let result;
|
|
2269
|
-
try {
|
|
2270
|
-
result = entry.transform(value);
|
|
2271
|
-
} catch (err) {
|
|
2272
|
-
return value;
|
|
2273
|
-
}
|
|
2274
|
-
if (!result.coerced) return value;
|
|
2275
|
-
const returnedKind = slimKindOf(result.value);
|
|
2276
|
-
if (returnedKind !== entry.output) {
|
|
2277
|
-
return value;
|
|
2278
|
-
}
|
|
2279
|
-
if (entry.output === "number" && !Number.isFinite(result.value)) {
|
|
2280
|
-
return value;
|
|
2281
|
-
}
|
|
2282
|
-
return result.value;
|
|
2169
|
+
if (target === "number") return toNumber(value);
|
|
2170
|
+
if (target === "boolean") return toBoolean(value);
|
|
2171
|
+
return value;
|
|
2283
2172
|
}
|
|
2284
|
-
function coerceArrayMembers(arr, elementAccepted
|
|
2173
|
+
function coerceArrayMembers(arr, elementAccepted) {
|
|
2285
2174
|
let changed = false;
|
|
2286
2175
|
const out = [];
|
|
2287
2176
|
for (const el of arr) {
|
|
2288
|
-
const next = coerceScalar(el, elementAccepted
|
|
2177
|
+
const next = coerceScalar(el, elementAccepted);
|
|
2289
2178
|
if (next !== el) changed = true;
|
|
2290
2179
|
out.push(next);
|
|
2291
2180
|
}
|
|
2292
2181
|
return changed ? out : arr;
|
|
2293
2182
|
}
|
|
2294
|
-
function coerceSetMembers(set, elementAccepted
|
|
2183
|
+
function coerceSetMembers(set, elementAccepted) {
|
|
2295
2184
|
let changed = false;
|
|
2296
2185
|
const out = [];
|
|
2297
2186
|
for (const el of set) {
|
|
2298
|
-
const next = coerceScalar(el, elementAccepted
|
|
2187
|
+
const next = coerceScalar(el, elementAccepted);
|
|
2299
2188
|
if (next !== el) changed = true;
|
|
2300
2189
|
out.push(next);
|
|
2301
2190
|
}
|
|
2302
2191
|
return changed ? new Set(out) : set;
|
|
2303
2192
|
}
|
|
2304
|
-
function coerceValue(value, accepted, elementAccepted
|
|
2193
|
+
function coerceValue(value, accepted, elementAccepted) {
|
|
2305
2194
|
if (Array.isArray(value)) {
|
|
2306
2195
|
if (!accepted.has("array") || elementAccepted === void 0) return value;
|
|
2307
|
-
return coerceArrayMembers(value, elementAccepted
|
|
2196
|
+
return coerceArrayMembers(value, elementAccepted);
|
|
2308
2197
|
}
|
|
2309
2198
|
if (value instanceof Set) {
|
|
2310
2199
|
if (!accepted.has("set") || elementAccepted === void 0) return value;
|
|
2311
|
-
return coerceSetMembers(value, elementAccepted
|
|
2200
|
+
return coerceSetMembers(value, elementAccepted);
|
|
2312
2201
|
}
|
|
2313
|
-
return coerceScalar(value, accepted
|
|
2202
|
+
return coerceScalar(value, accepted);
|
|
2314
2203
|
}
|
|
2315
2204
|
|
|
2316
2205
|
const EMPTY_TRANSFORMS = Object.freeze([]);
|
|
2317
2206
|
function buildRegister(state, formInstanceId, instanceConfig) {
|
|
2318
|
-
const
|
|
2207
|
+
const coerceEnabled = instanceConfig?.coerce !== void 0 ? resolveCoerceEnabled(instanceConfig.coerce) : state.coerceEnabled;
|
|
2319
2208
|
const instanceMeta = instanceConfig?.instanceMeta;
|
|
2320
|
-
const formAutoAria = instanceConfig?.autoAria ?? true;
|
|
2321
2209
|
const getDisplayStateAt = instanceConfig?.getDisplayStateAt;
|
|
2322
2210
|
const withInstanceMeta = (meta) => {
|
|
2323
2211
|
if (instanceMeta === void 0) return meta;
|
|
@@ -2357,16 +2245,15 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2357
2245
|
const coerce = buildCoerceFn(
|
|
2358
2246
|
state.schema,
|
|
2359
2247
|
segments,
|
|
2360
|
-
|
|
2248
|
+
coerceEnabled
|
|
2361
2249
|
);
|
|
2362
2250
|
const coerceElement = buildElementCoerceFn(
|
|
2363
2251
|
state.schema,
|
|
2364
2252
|
segments,
|
|
2365
|
-
|
|
2253
|
+
coerceEnabled
|
|
2366
2254
|
);
|
|
2367
2255
|
const { aria } = computeFieldIdentity(formInstanceId, state.formKey, pathKey);
|
|
2368
2256
|
const isRequired = state.schema.isRequiredAtPath(segments);
|
|
2369
|
-
const ariaEnabled = options?.autoAria ?? formAutoAria;
|
|
2370
2257
|
const ariaDisplayState = getDisplayStateAt !== void 0 ? computed(() => getDisplayStateAt(segments)) : void 0;
|
|
2371
2258
|
const markBlank = () => state.setValueAtPath(segments, slimDefault, withInstanceMeta({ blank: true }));
|
|
2372
2259
|
const internalRv = {
|
|
@@ -2406,7 +2293,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2406
2293
|
const accepted = isBlankSignal ? markBlank() : state.setValueAtPath(segments, value, withInstanceMeta(void 0));
|
|
2407
2294
|
return accepted;
|
|
2408
2295
|
},
|
|
2409
|
-
// Called by the `
|
|
2296
|
+
// Called by the `vRegisterHintTransform` compile-time transform's wrapping
|
|
2410
2297
|
// IIFE on every server-side render of `<element v-register="…">`.
|
|
2411
2298
|
// Without it, every SSR'd FieldState serialises `connected: false`
|
|
2412
2299
|
// (because Vue skips directive lifecycle during SSR) and the client
|
|
@@ -2435,12 +2322,12 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2435
2322
|
}
|
|
2436
2323
|
return false;
|
|
2437
2324
|
},
|
|
2438
|
-
// --- Async transform lifecycle (internal; the directive's
|
|
2439
|
-
//
|
|
2440
|
-
//
|
|
2441
|
-
//
|
|
2442
|
-
//
|
|
2443
|
-
//
|
|
2325
|
+
// --- Async transform lifecycle (internal; the directive's deferred
|
|
2326
|
+
// orchestrator is the only legitimate consumer). Thin path-bound
|
|
2327
|
+
// delegates to the store's per-path token and counter machinery,
|
|
2328
|
+
// the same pattern as `markBlank` and `setValueWithInternalPath`, so
|
|
2329
|
+
// the directive (which holds this RegisterValue and never the store)
|
|
2330
|
+
// can drive the busy / discard / error bookkeeping. ---
|
|
2444
2331
|
beginTransform: (holder) => state.beginTransform(pathKey, holder),
|
|
2445
2332
|
isCurrentTransform: (token) => state.isCurrentTransform(pathKey, token),
|
|
2446
2333
|
endTransform: (token) => state.endTransform(pathKey, token),
|
|
@@ -2453,10 +2340,10 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2453
2340
|
return (state.fieldTransformCounts.get(pathKey) ?? 0) > 0;
|
|
2454
2341
|
},
|
|
2455
2342
|
path: pathKey,
|
|
2456
|
-
// Frozen so a wrapper component can pass `rv.segments`
|
|
2457
|
-
//
|
|
2458
|
-
//
|
|
2459
|
-
//
|
|
2343
|
+
// Frozen so a wrapper component can pass `rv.segments` straight to
|
|
2344
|
+
// `form.fields(...)` with no defensive copy, and so neither a test
|
|
2345
|
+
// fixture nor downstream code can mutate the canonical segment list
|
|
2346
|
+
// out from under the directive.
|
|
2460
2347
|
segments: Object.freeze(segments.slice()),
|
|
2461
2348
|
formKey: state.formKey,
|
|
2462
2349
|
formInstanceId,
|
|
@@ -2468,7 +2355,6 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2468
2355
|
// --- Aria (internal; consumed by the directive) ---
|
|
2469
2356
|
aria,
|
|
2470
2357
|
isRequired,
|
|
2471
|
-
ariaEnabled,
|
|
2472
2358
|
...ariaDisplayState !== void 0 ? { ariaDisplayState } : {}
|
|
2473
2359
|
};
|
|
2474
2360
|
return shallowReadonly(internalRv);
|
|
@@ -2632,11 +2518,50 @@ const DERIVED_DISPLAY_KEYS = /* @__PURE__ */ new Set([
|
|
|
2632
2518
|
"firstError",
|
|
2633
2519
|
"firstOwnError"
|
|
2634
2520
|
]);
|
|
2635
|
-
const
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2521
|
+
const META_KEYS = [
|
|
2522
|
+
...FIELD_STATE_KEYS,
|
|
2523
|
+
"submitting",
|
|
2524
|
+
"submissionAttempts",
|
|
2525
|
+
"departAttempts",
|
|
2526
|
+
"submitError",
|
|
2527
|
+
"submitted",
|
|
2528
|
+
"instanceId",
|
|
2529
|
+
"errorCount"
|
|
2530
|
+
];
|
|
2531
|
+
const BASE_META_KEYS = META_KEYS.filter((k) => !DERIVED_DISPLAY_KEYS.has(k));
|
|
2532
|
+
let INERT_HISTORY;
|
|
2533
|
+
function inertHistory() {
|
|
2534
|
+
INERT_HISTORY ?? (INERT_HISTORY = readonly({
|
|
2535
|
+
undo: () => false,
|
|
2536
|
+
redo: () => false,
|
|
2537
|
+
clear: () => {
|
|
2538
|
+
},
|
|
2539
|
+
canUndo: false,
|
|
2540
|
+
canRedo: false,
|
|
2541
|
+
size: 0
|
|
2542
|
+
}));
|
|
2543
|
+
return INERT_HISTORY;
|
|
2544
|
+
}
|
|
2545
|
+
const denyWrite = () => false;
|
|
2546
|
+
function buildMetaProxy(keys, read) {
|
|
2547
|
+
const target = {};
|
|
2548
|
+
const owns = new Set(keys);
|
|
2549
|
+
return new Proxy(target, {
|
|
2550
|
+
get: (_, key) => owns.has(key) ? read(key) : Reflect.get(target, key),
|
|
2551
|
+
has: (_, key) => owns.has(key) || Reflect.has(target, key),
|
|
2552
|
+
// Safe to hand back the same array every time: the spec copies a trap's
|
|
2553
|
+
// key list before using it.
|
|
2554
|
+
ownKeys: () => keys,
|
|
2555
|
+
getOwnPropertyDescriptor(_, key) {
|
|
2556
|
+
if (!owns.has(key)) return Reflect.getOwnPropertyDescriptor(target, key);
|
|
2557
|
+
return { configurable: true, enumerable: true, value: read(key), writable: false };
|
|
2558
|
+
},
|
|
2559
|
+
// A getter with no setter throws on assignment under strict mode, which
|
|
2560
|
+
// every module here is; `false` keeps that.
|
|
2561
|
+
set: denyWrite,
|
|
2562
|
+
deleteProperty: denyWrite,
|
|
2563
|
+
defineProperty: denyWrite
|
|
2564
|
+
});
|
|
2640
2565
|
}
|
|
2641
2566
|
function buildFormApi(state, formInstanceId, options = {}) {
|
|
2642
2567
|
const instanceBag = pickDefined({
|
|
@@ -2663,32 +2588,26 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2663
2588
|
const rootBaseComputed = computed(
|
|
2664
2589
|
() => buildContainerFieldStateBase(state, ROOT_PATH, ROOT_PATH_KEY, formInstanceId).base
|
|
2665
2590
|
);
|
|
2666
|
-
const metaBase = {
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2591
|
+
const metaBase = buildMetaProxy(BASE_META_KEYS, (key) => {
|
|
2592
|
+
switch (key) {
|
|
2593
|
+
case "instanceId":
|
|
2594
|
+
return formInstanceId;
|
|
2595
|
+
case "errorCount":
|
|
2596
|
+
return rootBaseComputed.value.errors.length;
|
|
2597
|
+
case "submitting":
|
|
2598
|
+
return state.submitting.value;
|
|
2599
|
+
case "submissionAttempts":
|
|
2600
|
+
return state.submissionAttempts.value;
|
|
2601
|
+
case "departAttempts":
|
|
2602
|
+
return state.departAttempts.value;
|
|
2603
|
+
case "submitError":
|
|
2604
|
+
return state.submitError.value;
|
|
2605
|
+
case "submitted":
|
|
2606
|
+
return state.submitted.value;
|
|
2607
|
+
default:
|
|
2608
|
+
return rootBaseComputed.value[key];
|
|
2682
2609
|
}
|
|
2683
|
-
};
|
|
2684
|
-
for (const k of BASE_MIRROR_KEYS) {
|
|
2685
|
-
defineGetter(
|
|
2686
|
-
metaBase,
|
|
2687
|
-
k,
|
|
2688
|
-
() => rootBaseComputed.value[k]
|
|
2689
|
-
);
|
|
2690
|
-
}
|
|
2691
|
-
defineGetter(metaBase, "errorCount", () => rootBaseComputed.value.errors.length);
|
|
2610
|
+
});
|
|
2692
2611
|
const getFormMetaBase = () => {
|
|
2693
2612
|
void state.submitting.value;
|
|
2694
2613
|
void state.submissionAttempts.value;
|
|
@@ -2697,28 +2616,41 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2697
2616
|
void state.submitted.value;
|
|
2698
2617
|
return metaBase;
|
|
2699
2618
|
};
|
|
2700
|
-
const
|
|
2701
|
-
const pathSweep = createDynamicPathSweep(state);
|
|
2619
|
+
const pathSweep = state.pathSweep;
|
|
2702
2620
|
const getRootFieldStateAt = buildFieldStateAccessor(
|
|
2703
2621
|
state,
|
|
2704
2622
|
formInstanceId,
|
|
2705
2623
|
getFormMetaBase,
|
|
2706
|
-
pathSweep
|
|
2707
|
-
fieldStateAccessorOptions
|
|
2624
|
+
pathSweep
|
|
2708
2625
|
);
|
|
2709
2626
|
const getDisplayStateAt = (segments) => getRootFieldStateAt(segments).value.displayState;
|
|
2710
2627
|
const registerConfig = {
|
|
2711
|
-
...pickDefined({ instanceMeta, coerce: options.coerce
|
|
2628
|
+
...pickDefined({ instanceMeta, coerce: options.coerce }),
|
|
2712
2629
|
getDisplayStateAt
|
|
2713
2630
|
};
|
|
2714
2631
|
const register = buildRegister(state, formInstanceId, registerConfig);
|
|
2715
|
-
const
|
|
2716
|
-
|
|
2632
|
+
const focusFirstError = (options2) => {
|
|
2633
|
+
const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
|
|
2634
|
+
if (target === null) return false;
|
|
2635
|
+
target.element.focus({ focusVisible: true, ...options2 });
|
|
2636
|
+
return true;
|
|
2637
|
+
};
|
|
2638
|
+
const scrollToFirstError = (options2) => {
|
|
2639
|
+
const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
|
|
2640
|
+
if (target === null) return false;
|
|
2641
|
+
target.element.scrollIntoView(options2);
|
|
2642
|
+
return true;
|
|
2643
|
+
};
|
|
2644
|
+
const applyInvalidSubmitPolicyPublic = () => {
|
|
2645
|
+
if (options.focusOnInvalidSubmit !== false) focusFirstError();
|
|
2646
|
+
};
|
|
2717
2647
|
const {
|
|
2718
2648
|
validate: validateBuilt,
|
|
2719
2649
|
parse: parseBuilt,
|
|
2720
2650
|
handleSubmit
|
|
2721
|
-
} = buildProcessForm(state,
|
|
2651
|
+
} = buildProcessForm(state, {
|
|
2652
|
+
applyInvalidSubmit: applyInvalidSubmitPolicyPublic
|
|
2653
|
+
});
|
|
2722
2654
|
const validate = (pathInput) => validateBuilt(pathInput);
|
|
2723
2655
|
const parse = (arg1, arg2) => {
|
|
2724
2656
|
const isPathArg = typeof arg1 === "string" || Array.isArray(arg1);
|
|
@@ -2846,28 +2778,26 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2846
2778
|
() => state.firstValidationDone.value && state.errorCells.size === 0 && state.derivedBlankErrors.value.size === 0 && !validating.value
|
|
2847
2779
|
);
|
|
2848
2780
|
const history = options.history;
|
|
2849
|
-
const formHistory = readonly(
|
|
2781
|
+
const formHistory = history === void 0 ? inertHistory() : readonly(
|
|
2850
2782
|
reactive({
|
|
2851
|
-
undo: history
|
|
2852
|
-
redo: history
|
|
2853
|
-
clear: history
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
size: history?.historySize ?? computed(() => 0)
|
|
2783
|
+
undo: history.undo,
|
|
2784
|
+
redo: history.redo,
|
|
2785
|
+
clear: history.clear,
|
|
2786
|
+
canUndo: history.canUndo,
|
|
2787
|
+
canRedo: history.canRedo,
|
|
2788
|
+
size: history.historySize
|
|
2858
2789
|
})
|
|
2859
2790
|
);
|
|
2860
2791
|
const metaErrors = computed(
|
|
2861
|
-
() => aggregateErrorsAt(state, [])
|
|
2792
|
+
() => aggregateErrorsAt(state, [], ROOT_PATH_KEY)
|
|
2862
2793
|
);
|
|
2863
2794
|
const rootFieldState = getRootFieldStateAt([]);
|
|
2864
|
-
const
|
|
2865
|
-
// Whole-form work signals compose the LIFECYCLE counters with the
|
|
2866
|
-
//
|
|
2867
|
-
//
|
|
2868
|
-
//
|
|
2869
|
-
//
|
|
2870
|
-
// unfiltered whole-form aggregate.
|
|
2795
|
+
const metaOwn = {
|
|
2796
|
+
// Whole-form work signals compose the LIFECYCLE counters with the per-leaf
|
|
2797
|
+
// rollup: a submit-time validate shows up in `activeValidations` and a
|
|
2798
|
+
// per-field debounced validator in `fieldValidationCounts`, and either
|
|
2799
|
+
// flips the flag. `valid` keeps the form-level mount gate, and `errors` is
|
|
2800
|
+
// the unfiltered whole-form aggregate.
|
|
2871
2801
|
validating: computed(
|
|
2872
2802
|
() => state.activeValidations.value > 0 || rootFieldState.value.validating
|
|
2873
2803
|
),
|
|
@@ -2879,7 +2809,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2879
2809
|
busy: computed(
|
|
2880
2810
|
() => state.activeValidations.value > 0 || state.activeTransforms.value > 0 || rootFieldState.value.validating || rootFieldState.value.transforming
|
|
2881
2811
|
),
|
|
2882
|
-
// Lifecycle
|
|
2812
|
+
// Lifecycle, form-level only and never on FieldState.
|
|
2883
2813
|
submitting,
|
|
2884
2814
|
submissionAttempts,
|
|
2885
2815
|
departAttempts,
|
|
@@ -2890,14 +2820,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2890
2820
|
// identifier).
|
|
2891
2821
|
instanceId: formInstanceId
|
|
2892
2822
|
};
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
);
|
|
2899
|
-
}
|
|
2900
|
-
defineGetter(metaTarget, "errorCount", () => metaErrors.value.length);
|
|
2823
|
+
const metaTarget = buildMetaProxy(META_KEYS, (key) => {
|
|
2824
|
+
if (key === "errorCount") return metaErrors.value.length;
|
|
2825
|
+
if (Object.hasOwn(metaOwn, key)) return metaOwn[key];
|
|
2826
|
+
return rootFieldState.value[key];
|
|
2827
|
+
});
|
|
2901
2828
|
const formMeta = readonly(reactive(metaTarget));
|
|
2902
2829
|
const reset = (nextDefaultValues) => {
|
|
2903
2830
|
if (nextDefaultValues === void 0) {
|
|
@@ -2937,21 +2864,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2937
2864
|
} catch {
|
|
2938
2865
|
}
|
|
2939
2866
|
}
|
|
2940
|
-
const focusFirstError = (options2) => {
|
|
2941
|
-
const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
|
|
2942
|
-
if (target === null) return false;
|
|
2943
|
-
target.element.focus({ focusVisible: true, ...options2 });
|
|
2944
|
-
return true;
|
|
2945
|
-
};
|
|
2946
|
-
const scrollToFirstError = (options2) => {
|
|
2947
|
-
const target = state.domBinding.value?.getFirstErrorElement(formInstanceId) ?? null;
|
|
2948
|
-
if (target === null) return false;
|
|
2949
|
-
target.element.scrollIntoView(options2);
|
|
2950
|
-
return true;
|
|
2951
|
-
};
|
|
2952
|
-
const applyInvalidSubmitPolicyPublic = (policy) => {
|
|
2953
|
-
applyInvalidSubmitPolicy(state, formInstanceId, policy ?? defaultInvalidSubmitPolicy);
|
|
2954
|
-
};
|
|
2955
2867
|
const fieldArrays = buildFieldArrayApi(state);
|
|
2956
2868
|
const blankPathsView = computed(() => {
|
|
2957
2869
|
const keys = /* @__PURE__ */ new Set();
|
|
@@ -3014,10 +2926,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3014
2926
|
}
|
|
3015
2927
|
const api = {
|
|
3016
2928
|
handleSubmit: gated(handleSubmit),
|
|
3017
|
-
//
|
|
3018
|
-
// reactive containers (`meta`, `history`, `blankPaths`) are exposed
|
|
3019
|
-
//
|
|
3020
|
-
//
|
|
2929
|
+
// The callable readonly Proxies (`values`, `fields`, `errors`) and the
|
|
2930
|
+
// reactive containers (`meta`, `history`, `blankPaths`) are exposed through
|
|
2931
|
+
// getters, so reading one activates the form on first touch. Each
|
|
2932
|
+
// underlying object is identity-stable across reads.
|
|
3021
2933
|
get values() {
|
|
3022
2934
|
void state.activate();
|
|
3023
2935
|
return valuesProxy;
|
|
@@ -3034,7 +2946,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3034
2946
|
key: state.formKey,
|
|
3035
2947
|
// Auto-unwrapping views over the per-store async-defaults lifecycle
|
|
3036
2948
|
// refs (see FormStore.hydrating / hydrateError). Reading either
|
|
3037
|
-
// activates the form
|
|
2949
|
+
// activates the form: observing factory state implies use.
|
|
3038
2950
|
get hydrating() {
|
|
3039
2951
|
void state.activate();
|
|
3040
2952
|
return state.hydrating.value;
|
|
@@ -3043,20 +2955,18 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3043
2955
|
void state.activate();
|
|
3044
2956
|
return state.hydrateError.value;
|
|
3045
2957
|
},
|
|
3046
|
-
// Orthogonal to `hydrating` and `hydrateError
|
|
3047
|
-
//
|
|
3048
|
-
// resolved
|
|
3049
|
-
// refetches even when those
|
|
3050
|
-
//
|
|
3051
|
-
// `hydrateError` surfaces the refresh failure.
|
|
2958
|
+
// Orthogonal to `hydrating` and `hydrateError`. `ready` flips true once
|
|
2959
|
+
// defaults are applied, whether sync at construction or from a factory
|
|
2960
|
+
// that resolved, and it is a one-way latch: it stays true through later
|
|
2961
|
+
// refetches even when those fail, so a stale-while-revalidate UI keeps
|
|
2962
|
+
// rendering the prior values while `hydrateError` surfaces the failure.
|
|
3052
2963
|
get ready() {
|
|
3053
2964
|
void state.activate();
|
|
3054
2965
|
return state.defaultsResolved.value;
|
|
3055
2966
|
},
|
|
3056
|
-
// `rehydrate` and `activate` are
|
|
3057
|
-
//
|
|
3058
|
-
//
|
|
3059
|
-
// so they call `state` directly.
|
|
2967
|
+
// `rehydrate` and `activate` are activation entry points themselves and
|
|
2968
|
+
// fire the factory by design, so wrapping them in `gated` would
|
|
2969
|
+
// double-fire. They call `state` directly.
|
|
3060
2970
|
rehydrate: () => state.rehydrate(),
|
|
3061
2971
|
activate: () => state.activate(),
|
|
3062
2972
|
get errors() {
|
|
@@ -3099,6 +3009,45 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3099
3009
|
return api;
|
|
3100
3010
|
}
|
|
3101
3011
|
|
|
3012
|
+
const SWEEP_SLICE = 8;
|
|
3013
|
+
function createDynamicPathSweep(deps) {
|
|
3014
|
+
const dynamicKeys = /* @__PURE__ */ new Set();
|
|
3015
|
+
const evictors = [];
|
|
3016
|
+
const isDynamicPath = (segments) => {
|
|
3017
|
+
for (let i = 0; i < segments.length; i++) {
|
|
3018
|
+
if (typeof segments[i] === "number") return true;
|
|
3019
|
+
if (!deps.isFixedObjectAtPath(segments.slice(0, i))) return true;
|
|
3020
|
+
}
|
|
3021
|
+
return false;
|
|
3022
|
+
};
|
|
3023
|
+
let sweepCursor = null;
|
|
3024
|
+
deps.onFormChange((next) => {
|
|
3025
|
+
if (dynamicKeys.size === 0) return;
|
|
3026
|
+
const raw = toRaw(next);
|
|
3027
|
+
for (let i = 0; i < SWEEP_SLICE; i++) {
|
|
3028
|
+
sweepCursor ?? (sweepCursor = dynamicKeys.values());
|
|
3029
|
+
const step = sweepCursor.next();
|
|
3030
|
+
if (step.done === true) {
|
|
3031
|
+
sweepCursor = null;
|
|
3032
|
+
break;
|
|
3033
|
+
}
|
|
3034
|
+
const segments = segmentsForPathKey(step.value);
|
|
3035
|
+
if (segments !== null && hasAtPath(raw, segments)) continue;
|
|
3036
|
+
dynamicKeys.delete(step.value);
|
|
3037
|
+
for (const evict of evictors) evict(step.value);
|
|
3038
|
+
}
|
|
3039
|
+
});
|
|
3040
|
+
return {
|
|
3041
|
+
track(segments, key) {
|
|
3042
|
+
if (dynamicKeys.has(key)) return;
|
|
3043
|
+
if (isDynamicPath(segments)) dynamicKeys.add(key);
|
|
3044
|
+
},
|
|
3045
|
+
onEvict(evict) {
|
|
3046
|
+
evictors.push(evict);
|
|
3047
|
+
}
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3102
3051
|
const IDLE = Object.freeze({ display: "idle" });
|
|
3103
3052
|
const MAX_DELAY = 2147483647;
|
|
3104
3053
|
const PENDING_LIVENESS_MS = 250;
|
|
@@ -3187,6 +3136,58 @@ function createDisplayEngine(ssr) {
|
|
|
3187
3136
|
return engine;
|
|
3188
3137
|
}
|
|
3189
3138
|
|
|
3139
|
+
function buildErrorPathIndex(cells, blank) {
|
|
3140
|
+
const keys = /* @__PURE__ */ new Set();
|
|
3141
|
+
for (const [key, cell] of cells) {
|
|
3142
|
+
if (cell.schema.length > 0 || cell.user.length > 0) keys.add(key);
|
|
3143
|
+
}
|
|
3144
|
+
for (const [key, list] of blank) {
|
|
3145
|
+
if (list.length > 0) keys.add(key);
|
|
3146
|
+
}
|
|
3147
|
+
if (keys.size === 0) return EMPTY_INDEX;
|
|
3148
|
+
const entries = [];
|
|
3149
|
+
for (const key of keys) {
|
|
3150
|
+
const segments = segmentsForPathKey(key);
|
|
3151
|
+
if (segments !== null) entries.push({ key, segments });
|
|
3152
|
+
}
|
|
3153
|
+
entries.sort(compareByKey);
|
|
3154
|
+
return entries;
|
|
3155
|
+
}
|
|
3156
|
+
const EMPTY_INDEX = Object.freeze([]);
|
|
3157
|
+
function compareByKey(a, b) {
|
|
3158
|
+
return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
|
|
3159
|
+
}
|
|
3160
|
+
function windowUnder(index, prefix) {
|
|
3161
|
+
if (prefix.length === 0 || index.length === 0) return index;
|
|
3162
|
+
const prefixKey = keyForSegments(prefix).key;
|
|
3163
|
+
const marker = `${prefixKey.slice(0, -1)},`;
|
|
3164
|
+
const start = lowerBound(index, marker);
|
|
3165
|
+
if (start >= index.length) return EMPTY_INDEX;
|
|
3166
|
+
let end = start;
|
|
3167
|
+
for (; end < index.length; end += 1) {
|
|
3168
|
+
const entry = index[end];
|
|
3169
|
+
if (entry === void 0 || entry.key > prefixKey) break;
|
|
3170
|
+
}
|
|
3171
|
+
return start === 0 && end === index.length ? index : index.slice(start, end);
|
|
3172
|
+
}
|
|
3173
|
+
function isSameWindow(a, b) {
|
|
3174
|
+
if (a.length !== b.length) return false;
|
|
3175
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
3176
|
+
if (a[i]?.key !== b[i]?.key) return false;
|
|
3177
|
+
}
|
|
3178
|
+
return true;
|
|
3179
|
+
}
|
|
3180
|
+
function lowerBound(index, target) {
|
|
3181
|
+
let lo = 0;
|
|
3182
|
+
let hi = index.length;
|
|
3183
|
+
while (lo < hi) {
|
|
3184
|
+
const mid = lo + hi >>> 1;
|
|
3185
|
+
if ((index[mid]?.key ?? target) < target) lo = mid + 1;
|
|
3186
|
+
else hi = mid;
|
|
3187
|
+
}
|
|
3188
|
+
return lo;
|
|
3189
|
+
}
|
|
3190
|
+
|
|
3190
3191
|
function mergeSparseHydration(schemaDefaults, sparse, schema) {
|
|
3191
3192
|
return mergeDeep(schemaDefaults, sparse, [], schema);
|
|
3192
3193
|
}
|
|
@@ -3397,16 +3398,15 @@ function walkAuthoredFromSchemaDiff(withDefaults, withoutDefaults, prefix, out)
|
|
|
3397
3398
|
out.add(canonicalizePath(prefix).key);
|
|
3398
3399
|
}
|
|
3399
3400
|
}
|
|
3400
|
-
function computeBaselineResponse(schema,
|
|
3401
|
+
function computeBaselineResponse(schema, source) {
|
|
3401
3402
|
const completed = source === void 0 ? void 0 : mergeStructural(schema, [], source);
|
|
3402
3403
|
return schema.getDefaultValues({
|
|
3403
3404
|
useDefaultSchemaValues: true,
|
|
3404
|
-
constraints: completed
|
|
3405
|
-
strict
|
|
3405
|
+
constraints: completed
|
|
3406
3406
|
});
|
|
3407
3407
|
}
|
|
3408
|
-
function initialFirstValidationGate(schema
|
|
3409
|
-
return
|
|
3408
|
+
function initialFirstValidationGate(schema) {
|
|
3409
|
+
return schema.needsAsyncValidation?.() !== true;
|
|
3410
3410
|
}
|
|
3411
3411
|
function seedOriginalsFromBaseline(st, baseline, ensureOrdinals) {
|
|
3412
3412
|
st.originals.clear();
|
|
@@ -3418,7 +3418,7 @@ function seedOriginalsFromBaseline(st, baseline, ensureOrdinals) {
|
|
|
3418
3418
|
});
|
|
3419
3419
|
}
|
|
3420
3420
|
function queueInitialAsyncValidation(st) {
|
|
3421
|
-
if (!st.ssr && st.
|
|
3421
|
+
if (!st.ssr && st.schema.needsAsyncValidation?.() === true) {
|
|
3422
3422
|
queueMicrotask(() => scheduleFieldValidation(
|
|
3423
3423
|
st,
|
|
3424
3424
|
[],
|
|
@@ -3460,10 +3460,6 @@ function ensurePathOrdinal(st, key) {
|
|
|
3460
3460
|
}
|
|
3461
3461
|
return ordinal;
|
|
3462
3462
|
}
|
|
3463
|
-
function pathHasAsyncValidation(st, path) {
|
|
3464
|
-
const { key } = canonicalizePath(path);
|
|
3465
|
-
return pathHasAsyncValidationByKey(st, key, path);
|
|
3466
|
-
}
|
|
3467
3463
|
function pathHasAsyncValidationByKey(st, key, segments) {
|
|
3468
3464
|
const cached = st.pathAsyncCache.get(key);
|
|
3469
3465
|
if (cached !== void 0) return cached;
|
|
@@ -3585,19 +3581,16 @@ function touchFieldRecord(st, pathKey, path, patch) {
|
|
|
3585
3581
|
path,
|
|
3586
3582
|
updatedAt: patch.updatedAt ?? current?.updatedAt ?? null,
|
|
3587
3583
|
connected: patch.connected ?? current?.connected ?? false,
|
|
3588
|
-
// focused/blurred
|
|
3589
|
-
//
|
|
3590
|
-
//
|
|
3591
|
-
// `current`, losing the intent. `!== undefined` honours an
|
|
3592
|
-
// explicit null and preserves current only on absence.
|
|
3584
|
+
// `focused` / `blurred` need an explicit-undefined guard: a patch
|
|
3585
|
+
// legitimately carries `null` to mark a disconnect, and `??` would
|
|
3586
|
+
// short-circuit on it and fall through to `current`, losing the intent.
|
|
3593
3587
|
focused: patch.focused !== void 0 ? patch.focused : current?.focused ?? null,
|
|
3594
3588
|
blurred: patch.blurred !== void 0 ? patch.blurred : current?.blurred ?? null,
|
|
3595
|
-
// touched is plain
|
|
3596
|
-
// guard here because `false` is not nullish.
|
|
3589
|
+
// `touched` is a plain boolean, so `??` is equivalent to the guard above.
|
|
3597
3590
|
touched: patch.touched ?? current?.touched ?? false,
|
|
3598
|
-
// interacted is sticky-true
|
|
3599
|
-
//
|
|
3600
|
-
//
|
|
3591
|
+
// `interacted` is sticky-true and a merge patch only ever sets it, so `??`
|
|
3592
|
+
// preserves the current bit. Only the reset paths clear it, and they
|
|
3593
|
+
// reconstruct the record outright.
|
|
3601
3594
|
interacted: patch.interacted ?? current?.interacted ?? false,
|
|
3602
3595
|
blurredAfterInteraction: patch.blurredAfterInteraction ?? current?.blurredAfterInteraction ?? false
|
|
3603
3596
|
});
|
|
@@ -3606,10 +3599,15 @@ function commitWritePatches(st, patches, meta) {
|
|
|
3606
3599
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3607
3600
|
for (const patch of patches) {
|
|
3608
3601
|
const { key } = canonicalizePath(patch.path);
|
|
3609
|
-
if (
|
|
3602
|
+
if (!st.originals.has(key)) {
|
|
3610
3603
|
st.originals.set(key, { segments: patch.path, value: void 0 });
|
|
3611
3604
|
}
|
|
3612
3605
|
touchFieldRecord(st, key, patch.path, { updatedAt: now });
|
|
3606
|
+
st.pathSweep.track(patch.path, key);
|
|
3607
|
+
for (let i = 1; i < patch.path.length; i++) {
|
|
3608
|
+
const ancestor = patch.path.slice(0, i);
|
|
3609
|
+
st.pathSweep.track(ancestor, canonicalizePath(ancestor).key);
|
|
3610
|
+
}
|
|
3613
3611
|
}
|
|
3614
3612
|
for (const listener of st.formChangeListeners) {
|
|
3615
3613
|
try {
|
|
@@ -3814,10 +3812,7 @@ function setValueAtPath(st, path, value, meta) {
|
|
|
3814
3812
|
if (newlyAuthored && st.schema.isPreprocessOrCoerceLeaf(path)) {
|
|
3815
3813
|
const modeForAuthoringTransition = meta?.instance?.validateOn ?? st.fieldValidationMode;
|
|
3816
3814
|
if (modeForAuthoringTransition === "change") {
|
|
3817
|
-
scheduleFieldValidation(st, path, false,
|
|
3818
|
-
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
3819
|
-
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
3820
|
-
});
|
|
3815
|
+
scheduleFieldValidation(st, path, false, meta?.instance);
|
|
3821
3816
|
}
|
|
3822
3817
|
}
|
|
3823
3818
|
return true;
|
|
@@ -3833,15 +3828,12 @@ function setValueAtPath(st, path, value, meta) {
|
|
|
3833
3828
|
st.arrayIdentity.realign(path);
|
|
3834
3829
|
} else if (isContainer(currentValue) && !isContainer(value)) {
|
|
3835
3830
|
if (subtreeHadRealBaseline(st, path, currentValue)) {
|
|
3836
|
-
st.removedSubtrees.add(
|
|
3831
|
+
st.removedSubtrees.add(pathKey);
|
|
3837
3832
|
}
|
|
3838
3833
|
}
|
|
3839
3834
|
const effectiveModeAfterWrite = meta?.instance?.validateOn ?? st.fieldValidationMode;
|
|
3840
3835
|
if (effectiveModeAfterWrite === "change") {
|
|
3841
|
-
scheduleFieldValidation(st, path, false,
|
|
3842
|
-
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
3843
|
-
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
3844
|
-
});
|
|
3836
|
+
scheduleFieldValidation(st, path, false, meta?.instance);
|
|
3845
3837
|
}
|
|
3846
3838
|
return true;
|
|
3847
3839
|
}
|
|
@@ -3902,12 +3894,11 @@ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variant
|
|
|
3902
3894
|
path: [...parentPath, ...err.path]
|
|
3903
3895
|
}));
|
|
3904
3896
|
applySchemaErrorsForSubtree(st, parentPath, reStamped);
|
|
3905
|
-
const
|
|
3906
|
-
const prevValidation = st.fieldValidationState.get(parentKey2);
|
|
3897
|
+
const prevValidation = st.fieldValidationState.get(parentKey);
|
|
3907
3898
|
if (prevValidation !== void 0) {
|
|
3908
3899
|
if (prevValidation.timer !== null) clearTimeout(prevValidation.timer);
|
|
3909
3900
|
prevValidation.aborted = true;
|
|
3910
|
-
st.fieldValidationState.delete(
|
|
3901
|
+
st.fieldValidationState.delete(parentKey);
|
|
3911
3902
|
}
|
|
3912
3903
|
appliedSync = true;
|
|
3913
3904
|
}
|
|
@@ -3915,17 +3906,14 @@ function reshapeUnionVariant(st, parentPath, oldDiscValue, newDiscValue, variant
|
|
|
3915
3906
|
applyFormReplacement(st, nextForm, meta);
|
|
3916
3907
|
for (const k of newBlankPaths) st.blankPaths.add(k);
|
|
3917
3908
|
if (reshapeMode === "change" && !appliedSync) {
|
|
3918
|
-
scheduleFieldValidation(st, parentPath, false,
|
|
3919
|
-
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
3920
|
-
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
3921
|
-
});
|
|
3909
|
+
scheduleFieldValidation(st, parentPath, false, meta?.instance);
|
|
3922
3910
|
}
|
|
3923
3911
|
return true;
|
|
3924
3912
|
}
|
|
3925
|
-
function scheduleFieldValidation(st, path, immediate,
|
|
3926
|
-
const effectiveMode =
|
|
3913
|
+
function scheduleFieldValidation(st, path, immediate, instance) {
|
|
3914
|
+
const effectiveMode = instance?.validateOn ?? st.fieldValidationMode;
|
|
3927
3915
|
if (effectiveMode === "submit") return;
|
|
3928
|
-
const effectiveDebounce =
|
|
3916
|
+
const effectiveDebounce = instance?.debounceMs ?? st.fieldValidationDebounceMs;
|
|
3929
3917
|
const { key } = canonicalizePath(path);
|
|
3930
3918
|
const prev = st.fieldValidationState.get(key);
|
|
3931
3919
|
if (prev !== void 0) {
|
|
@@ -4165,13 +4153,13 @@ function markFocused(st, path, focused, meta) {
|
|
|
4165
4153
|
touchFieldRecord(st, key, path, {
|
|
4166
4154
|
focused,
|
|
4167
4155
|
blurred: !focused,
|
|
4168
|
-
// `touched` flips
|
|
4169
|
-
//
|
|
4156
|
+
// `touched` flips true on blur and stays; a focused field keeps whatever
|
|
4157
|
+
// it held.
|
|
4170
4158
|
touched: focused ? current?.touched ?? false : true,
|
|
4171
|
-
// `blurredAfterInteraction` flips true on the first blur
|
|
4172
|
-
//
|
|
4173
|
-
//
|
|
4174
|
-
//
|
|
4159
|
+
// `blurredAfterInteraction` flips true on the first blur after a value
|
|
4160
|
+
// edit and stays. A tab-through blur before any edit leaves it false,
|
|
4161
|
+
// since `interacted` is still false, which is what keeps a clean
|
|
4162
|
+
// tab-through from arming the gate.
|
|
4175
4163
|
blurredAfterInteraction: !focused && current?.interacted === true ? true : current?.blurredAfterInteraction ?? false
|
|
4176
4164
|
});
|
|
4177
4165
|
const focusMode = meta?.instance?.validateOn ?? st.fieldValidationMode;
|
|
@@ -4199,10 +4187,7 @@ function markFocused(st, path, focused, meta) {
|
|
|
4199
4187
|
});
|
|
4200
4188
|
}
|
|
4201
4189
|
if (changed) {
|
|
4202
|
-
scheduleFieldValidation(st, path, true,
|
|
4203
|
-
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
4204
|
-
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
4205
|
-
});
|
|
4190
|
+
scheduleFieldValidation(st, path, true, meta?.instance);
|
|
4206
4191
|
}
|
|
4207
4192
|
}
|
|
4208
4193
|
}
|
|
@@ -4214,10 +4199,9 @@ function markInteracted(st, path) {
|
|
|
4214
4199
|
}
|
|
4215
4200
|
function touchAtPath(st, segments) {
|
|
4216
4201
|
const formValue = st.form.value;
|
|
4217
|
-
for (const [, entry] of st.originals) {
|
|
4202
|
+
for (const [leafKey, entry] of st.originals) {
|
|
4218
4203
|
if (!isPathPrefix(segments, entry.segments)) continue;
|
|
4219
4204
|
if (!hasAtPath(formValue, entry.segments)) continue;
|
|
4220
|
-
const leafKey = canonicalizePath(entry.segments).key;
|
|
4221
4205
|
const current = st.fields.get(leafKey);
|
|
4222
4206
|
if (current?.touched === true) continue;
|
|
4223
4207
|
touchFieldRecord(st, leafKey, entry.segments, { touched: true });
|
|
@@ -4227,11 +4211,10 @@ function interactAtPath(st, segments) {
|
|
|
4227
4211
|
if (st.effectiveDisabled.value) return false;
|
|
4228
4212
|
const formValue = st.form.value;
|
|
4229
4213
|
let interactedAny = false;
|
|
4230
|
-
for (const [, entry] of st.originals) {
|
|
4214
|
+
for (const [leafKey, entry] of st.originals) {
|
|
4231
4215
|
if (!isPathPrefix(segments, entry.segments)) continue;
|
|
4232
4216
|
if (!hasAtPath(formValue, entry.segments)) continue;
|
|
4233
4217
|
interactedAny = true;
|
|
4234
|
-
const leafKey = canonicalizePath(entry.segments).key;
|
|
4235
4218
|
const current = st.fields.get(leafKey);
|
|
4236
4219
|
if (current?.touched === true && current.interacted === true && current.blurredAfterInteraction === true) {
|
|
4237
4220
|
continue;
|
|
@@ -4283,11 +4266,7 @@ function adoptResolvedDefaults(st, value) {
|
|
|
4283
4266
|
st.schema
|
|
4284
4267
|
)
|
|
4285
4268
|
);
|
|
4286
|
-
seedOriginalsFromBaseline(
|
|
4287
|
-
st,
|
|
4288
|
-
computeBaselineResponse(st.schema, st.strict, st.defaultValues).data,
|
|
4289
|
-
false
|
|
4290
|
-
);
|
|
4269
|
+
seedOriginalsFromBaseline(st, computeBaselineResponse(st.schema, st.defaultValues).data, false);
|
|
4291
4270
|
}
|
|
4292
4271
|
function reset(st, nextDefaultValues) {
|
|
4293
4272
|
const resetSource = nextDefaultValues === void 0 ? st.defaultValues : mergeSparseHydration(
|
|
@@ -4296,7 +4275,7 @@ function reset(st, nextDefaultValues) {
|
|
|
4296
4275
|
st.schema
|
|
4297
4276
|
);
|
|
4298
4277
|
st.defaultValues = structuralSnapshot(resetSource);
|
|
4299
|
-
const resetResponse = computeBaselineResponse(st.schema,
|
|
4278
|
+
const resetResponse = computeBaselineResponse(st.schema, resetSource);
|
|
4300
4279
|
const next = resetResponse.data;
|
|
4301
4280
|
rebuildAuthoredPaths(st, resetSource, next);
|
|
4302
4281
|
applyFormReplacement(st, next);
|
|
@@ -4313,16 +4292,14 @@ function reset(st, nextDefaultValues) {
|
|
|
4313
4292
|
st.blankPaths.add(key);
|
|
4314
4293
|
}
|
|
4315
4294
|
st.errorCells.clear();
|
|
4316
|
-
if (
|
|
4295
|
+
if (!resetResponse.success) {
|
|
4317
4296
|
replaceErrorChannel(st, "schema", resetResponse.errors);
|
|
4318
4297
|
}
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
applySchemaErrorsForSubtree(st, [], syncResult.errors);
|
|
4323
|
-
}
|
|
4298
|
+
const syncResult = st.schema.validateAtPath(st.form.value, void 0, { sync: true });
|
|
4299
|
+
if (!(syncResult instanceof Promise) && !syncResult.success) {
|
|
4300
|
+
applySchemaErrorsForSubtree(st, [], syncResult.errors);
|
|
4324
4301
|
}
|
|
4325
|
-
st.firstValidationDone.value = initialFirstValidationGate(st.schema
|
|
4302
|
+
st.firstValidationDone.value = initialFirstValidationGate(st.schema);
|
|
4326
4303
|
queueInitialAsyncValidation(st);
|
|
4327
4304
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4328
4305
|
for (const [pathKey, record] of st.fields) {
|
|
@@ -4412,10 +4389,6 @@ function clearFieldRecordFlags(st, pathKey) {
|
|
|
4412
4389
|
if (record === void 0) return;
|
|
4413
4390
|
st.fields.set(pathKey, withClearedHistoryFlags(record, (/* @__PURE__ */ new Date()).toISOString()));
|
|
4414
4391
|
}
|
|
4415
|
-
function isPristineAtPath(st, path) {
|
|
4416
|
-
const { key, segments } = canonicalizePath(path);
|
|
4417
|
-
return isPristineAtPathByKey(st, key, segments);
|
|
4418
|
-
}
|
|
4419
4392
|
function isPristineAtPathByKey(st, key, segments) {
|
|
4420
4393
|
if (st.blankPaths.has(key) !== st.originalBlankPaths.has(key)) return false;
|
|
4421
4394
|
const entry = st.originals.get(key);
|
|
@@ -4449,12 +4422,8 @@ function getFieldRecord(st, path) {
|
|
|
4449
4422
|
const { key } = canonicalizePath(path);
|
|
4450
4423
|
return st.fields.get(key);
|
|
4451
4424
|
}
|
|
4452
|
-
function getOriginalAtPath(st, path) {
|
|
4453
|
-
const { key } = canonicalizePath(path);
|
|
4454
|
-
return st.originals.get(key)?.value;
|
|
4455
|
-
}
|
|
4456
4425
|
function createFormStore(options) {
|
|
4457
|
-
const { formKey, schema, defaultValues,
|
|
4426
|
+
const { formKey, schema, defaultValues, hydration } = options;
|
|
4458
4427
|
const ssr = options.ssr === true;
|
|
4459
4428
|
const ssrPrefetch = options.ssrPrefetch;
|
|
4460
4429
|
const rememberVariants = options.rememberVariants !== false;
|
|
@@ -4462,22 +4431,16 @@ function createFormStore(options) {
|
|
|
4462
4431
|
const fieldValidationDebounceMs = normalizeNumericOption({
|
|
4463
4432
|
value: options.debounceMs ?? DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS,
|
|
4464
4433
|
source: "useForm.debounceMs",
|
|
4465
|
-
allowInfinity: false,
|
|
4466
4434
|
min: 0,
|
|
4467
4435
|
defaultValue: DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS
|
|
4468
4436
|
});
|
|
4469
|
-
const
|
|
4470
|
-
const resolvedGetDisplayState = resolveGetDisplayState(options.getDisplayState);
|
|
4437
|
+
const coerceEnabled = resolveCoerceEnabled(options.coerce);
|
|
4471
4438
|
const cleanupHooks = [];
|
|
4472
4439
|
const modules = /* @__PURE__ */ new Map();
|
|
4473
4440
|
const fieldValidatingSince = reactive(/* @__PURE__ */ new Map());
|
|
4474
4441
|
const displayEngine = createDisplayEngine(ssr);
|
|
4475
4442
|
cleanupHooks.push(() => displayEngine.dispose());
|
|
4476
|
-
const schemaResponse = computeBaselineResponse(
|
|
4477
|
-
schema,
|
|
4478
|
-
strict,
|
|
4479
|
-
defaultValues
|
|
4480
|
-
);
|
|
4443
|
+
const schemaResponse = computeBaselineResponse(schema, defaultValues);
|
|
4481
4444
|
const schemaInitialData = schemaResponse.data;
|
|
4482
4445
|
const authoredPaths = /* @__PURE__ */ new Set();
|
|
4483
4446
|
const initialData = hydration !== void 0 ? hydration.form : structuralSnapshot(schemaInitialData);
|
|
@@ -4490,16 +4453,15 @@ function createFormStore(options) {
|
|
|
4490
4453
|
const v = getAtPath(toRaw(form.value), arraySegs);
|
|
4491
4454
|
return Array.isArray(v) ? v.length : 0;
|
|
4492
4455
|
});
|
|
4493
|
-
const fields =
|
|
4456
|
+
const fields = shallowReactive(/* @__PURE__ */ new Map());
|
|
4494
4457
|
const domBinding = shallowRef(null);
|
|
4495
|
-
const errorCells =
|
|
4496
|
-
const originals =
|
|
4458
|
+
const errorCells = shallowReactive(/* @__PURE__ */ new Map());
|
|
4459
|
+
const originals = shallowReactive(/* @__PURE__ */ new Map());
|
|
4497
4460
|
const removedSubtrees = /* @__PURE__ */ new Set();
|
|
4498
|
-
const initialTransientList = hydration?.blankPaths ?? options.initialBlankPaths ?? [];
|
|
4499
4461
|
const blankPaths = reactive(/* @__PURE__ */ new Set());
|
|
4500
4462
|
const originalBlankPaths = /* @__PURE__ */ new Set();
|
|
4501
|
-
|
|
4502
|
-
|
|
4463
|
+
const seededBlankPaths = hydration !== void 0 ? (hydration.blankPaths ?? []).map((dotted) => canonicalizePath(dotted).key) : options.initialBlankPaths ?? [];
|
|
4464
|
+
for (const key of seededBlankPaths) {
|
|
4503
4465
|
blankPaths.add(key);
|
|
4504
4466
|
originalBlankPaths.add(key);
|
|
4505
4467
|
}
|
|
@@ -4516,6 +4478,9 @@ function createFormStore(options) {
|
|
|
4516
4478
|
}
|
|
4517
4479
|
return result;
|
|
4518
4480
|
});
|
|
4481
|
+
const errorPathIndex = computed(
|
|
4482
|
+
() => buildErrorPathIndex(errorCells, derivedBlankErrors.value)
|
|
4483
|
+
);
|
|
4519
4484
|
const submitting = ref(false);
|
|
4520
4485
|
const activeSubmissions = ref(0);
|
|
4521
4486
|
const submissionAttempts = ref(0);
|
|
@@ -4540,7 +4505,7 @@ function createFormStore(options) {
|
|
|
4540
4505
|
const defaultsResolved = ref(false);
|
|
4541
4506
|
const activated = ref(false);
|
|
4542
4507
|
const activationPromise = ref(void 0);
|
|
4543
|
-
const firstValidationDone = ref(initialFirstValidationGate(schema
|
|
4508
|
+
const firstValidationDone = ref(initialFirstValidationGate(schema));
|
|
4544
4509
|
watch(activeValidations, (now, prev) => {
|
|
4545
4510
|
if (prev > 0 && now === 0) {
|
|
4546
4511
|
firstValidationDone.value = true;
|
|
@@ -4558,6 +4523,33 @@ function createFormStore(options) {
|
|
|
4558
4523
|
const activeTransforms = ref(0);
|
|
4559
4524
|
const transformRuns = /* @__PURE__ */ new Map();
|
|
4560
4525
|
const transformWaiters = [];
|
|
4526
|
+
const errorWindows = /* @__PURE__ */ new Map();
|
|
4527
|
+
const pathSweep = createDynamicPathSweep({
|
|
4528
|
+
onFormChange: (listener) => {
|
|
4529
|
+
formChangeListeners.add(listener);
|
|
4530
|
+
},
|
|
4531
|
+
isFixedObjectAtPath: (path) => schema.isFixedObjectAtPath(path)
|
|
4532
|
+
});
|
|
4533
|
+
pathSweep.onEvict((key) => {
|
|
4534
|
+
fields.delete(key);
|
|
4535
|
+
fieldValidationState.delete(key);
|
|
4536
|
+
authoredPaths.delete(key);
|
|
4537
|
+
if (originals.get(key)?.value === void 0) originals.delete(key);
|
|
4538
|
+
errorWindows.delete(key);
|
|
4539
|
+
});
|
|
4540
|
+
const errorWindowAt = (prefix, prefixKey) => {
|
|
4541
|
+
let cached = errorWindows.get(prefixKey);
|
|
4542
|
+
if (cached === void 0) {
|
|
4543
|
+
const frozen = [...prefix];
|
|
4544
|
+
cached = computed((prev) => {
|
|
4545
|
+
const next = windowUnder(errorPathIndex.value, frozen);
|
|
4546
|
+
return prev !== void 0 && isSameWindow(prev, next) ? prev : next;
|
|
4547
|
+
});
|
|
4548
|
+
errorWindows.set(prefixKey, cached);
|
|
4549
|
+
pathSweep.track(frozen, prefixKey);
|
|
4550
|
+
}
|
|
4551
|
+
return cached.value;
|
|
4552
|
+
};
|
|
4561
4553
|
const arrayBookkeeping = createArrayBookkeeping({
|
|
4562
4554
|
form,
|
|
4563
4555
|
fields,
|
|
@@ -4575,6 +4567,7 @@ function createFormStore(options) {
|
|
|
4575
4567
|
touchFieldRecord: (pathKey, path, patch) => touchFieldRecord(st, pathKey, path, patch),
|
|
4576
4568
|
decFieldValidation: (key) => decFieldValidation(st, key)
|
|
4577
4569
|
});
|
|
4570
|
+
const bind = (fn) => (...args) => fn(st, ...args);
|
|
4578
4571
|
const st = {
|
|
4579
4572
|
// --- public data (the FormStore contract's state members) ---
|
|
4580
4573
|
formKey,
|
|
@@ -4582,10 +4575,11 @@ function createFormStore(options) {
|
|
|
4582
4575
|
fields,
|
|
4583
4576
|
errorCells,
|
|
4584
4577
|
derivedBlankErrors,
|
|
4578
|
+
errorWindowAt,
|
|
4585
4579
|
originals,
|
|
4580
|
+
pathSweep,
|
|
4586
4581
|
schema,
|
|
4587
4582
|
ssr,
|
|
4588
|
-
getDisplayState: resolvedGetDisplayState,
|
|
4589
4583
|
submitting,
|
|
4590
4584
|
activeSubmissions,
|
|
4591
4585
|
submissionAttempts,
|
|
@@ -4613,20 +4607,16 @@ function createFormStore(options) {
|
|
|
4613
4607
|
displayEngine,
|
|
4614
4608
|
domBinding,
|
|
4615
4609
|
modules,
|
|
4616
|
-
|
|
4610
|
+
coerceEnabled,
|
|
4617
4611
|
blankPaths,
|
|
4618
4612
|
originalBlankPaths,
|
|
4619
4613
|
// --- kernel-internal state ---
|
|
4620
|
-
|
|
4621
|
-
//
|
|
4622
|
-
// `
|
|
4623
|
-
//
|
|
4624
|
-
//
|
|
4625
|
-
//
|
|
4626
|
-
// it should not depend on it. The copy also means a consumer who
|
|
4627
|
-
// keeps a reference to the literal they passed cannot mutate the
|
|
4628
|
-
// form's defaults from outside. `reset()` snapshots for a harder
|
|
4629
|
-
// reason (see there) where an alias IS reachable.
|
|
4614
|
+
// A defensive copy. `getDefaultValues` building a fresh tree is an adapter
|
|
4615
|
+
// implementation detail, not a contract, and this field is durable state
|
|
4616
|
+
// that every `reset()` reads, so it must not depend on one. The copy also
|
|
4617
|
+
// stops a consumer holding a reference to the literal they passed from
|
|
4618
|
+
// mutating the form's defaults from outside. `reset()` snapshots for a
|
|
4619
|
+
// harder reason, where an alias IS reachable.
|
|
4630
4620
|
defaultValues: structuralSnapshot(defaultValues),
|
|
4631
4621
|
ssrPrefetch,
|
|
4632
4622
|
rememberVariants,
|
|
@@ -4654,17 +4644,20 @@ function createFormStore(options) {
|
|
|
4654
4644
|
transformTokenSeq: 0,
|
|
4655
4645
|
warnedDisabledWrite: false,
|
|
4656
4646
|
// --- methods: thin per-instance skins over the module kernel ---
|
|
4657
|
-
rehydrate: (
|
|
4658
|
-
activate: (
|
|
4659
|
-
adoptResolvedDefaults: (
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4647
|
+
rehydrate: bind(rehydrate),
|
|
4648
|
+
activate: bind(activate),
|
|
4649
|
+
adoptResolvedDefaults: bind(adoptResolvedDefaults),
|
|
4650
|
+
pathHasAsyncValidationByKey: bind(pathHasAsyncValidationByKey),
|
|
4651
|
+
applyFormReplacement: bind(applyFormReplacement),
|
|
4652
|
+
setValueAtPath: bind(setValueAtPath),
|
|
4653
|
+
getValueAtPath: bind(getValueAtPath),
|
|
4654
|
+
arrayElementKey: bind(arrayElementKey),
|
|
4655
|
+
reset: bind(reset),
|
|
4656
|
+
resetField: bind(resetField),
|
|
4657
|
+
// The schema/user pair below reads as a fold waiting to happen. Folding
|
|
4658
|
+
// them into a `setErrorsForPathIn(channel)` factory measures 10 B LARGER:
|
|
4659
|
+
// gzip has already collected the rent on two adjacent copies, and the
|
|
4660
|
+
// helper adds a name the original does not need.
|
|
4668
4661
|
setSchemaErrorsForPath: (path, entries) => setErrorChannelForKey(
|
|
4669
4662
|
st,
|
|
4670
4663
|
canonicalizePath(path).key,
|
|
@@ -4673,7 +4666,7 @@ function createFormStore(options) {
|
|
|
4673
4666
|
),
|
|
4674
4667
|
setAllSchemaErrors: (entries) => replaceErrorChannel(st, "schema", entries),
|
|
4675
4668
|
clearSchemaErrors: (path) => clearErrorChannel(st, "schema", path),
|
|
4676
|
-
applySchemaErrorsForSubtree: (
|
|
4669
|
+
applySchemaErrorsForSubtree: bind(applySchemaErrorsForSubtree),
|
|
4677
4670
|
setAllUserErrors: (entries) => replaceErrorChannel(st, "user", entries),
|
|
4678
4671
|
setUserErrorsForPath: (path, entries) => setErrorChannelForKey(
|
|
4679
4672
|
st,
|
|
@@ -4682,37 +4675,34 @@ function createFormStore(options) {
|
|
|
4682
4675
|
entries.length === 0 ? NO_ERRORS : [...entries]
|
|
4683
4676
|
),
|
|
4684
4677
|
clearUserErrors: (path) => clearErrorChannel(st, "user", path),
|
|
4685
|
-
restoreErrorCells: (
|
|
4686
|
-
getErrorsForPath: (
|
|
4687
|
-
ensurePathOrdinal: (
|
|
4688
|
-
noteDomConnected: (
|
|
4689
|
-
noteDomDisconnected: (
|
|
4690
|
-
markFocused: (
|
|
4691
|
-
markInteracted: (
|
|
4692
|
-
touchAtPath: (
|
|
4693
|
-
interactAtPath: (
|
|
4694
|
-
markConnectedOptimistically: (
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
emitSubmitSuccess: () => emitSubmitSuccess(st),
|
|
4714
|
-
registerCleanup: (fn) => registerCleanup(st, fn),
|
|
4715
|
-
dispose: () => dispose(st)
|
|
4678
|
+
restoreErrorCells: bind(restoreErrorCells),
|
|
4679
|
+
getErrorsForPath: bind(getErrorsForPath),
|
|
4680
|
+
ensurePathOrdinal: bind(ensurePathOrdinal),
|
|
4681
|
+
noteDomConnected: bind(noteDomConnected),
|
|
4682
|
+
noteDomDisconnected: bind(noteDomDisconnected),
|
|
4683
|
+
markFocused: bind(markFocused),
|
|
4684
|
+
markInteracted: bind(markInteracted),
|
|
4685
|
+
touchAtPath: bind(touchAtPath),
|
|
4686
|
+
interactAtPath: bind(interactAtPath),
|
|
4687
|
+
markConnectedOptimistically: bind(markConnectedOptimistically),
|
|
4688
|
+
isPristineAtPathByKey: bind(isPristineAtPathByKey),
|
|
4689
|
+
hasStructuralChangeUnder: bind(hasStructuralChangeUnder),
|
|
4690
|
+
hasRemovedSubtreeUnder: bind(hasRemovedSubtreeUnder),
|
|
4691
|
+
getFieldRecord: bind(getFieldRecord),
|
|
4692
|
+
cancelFieldValidation: bind(cancelFieldValidation),
|
|
4693
|
+
beginTransform: bind(beginTransform),
|
|
4694
|
+
isCurrentTransform: bind(isCurrentTransform),
|
|
4695
|
+
endTransform: bind(endTransform),
|
|
4696
|
+
setTransformError: bind(setTransformError),
|
|
4697
|
+
cancelTransformsUnder: bind(cancelTransformsUnder),
|
|
4698
|
+
settleTransforms: bind(settleTransforms),
|
|
4699
|
+
scheduleFieldValidation: bind(scheduleFieldValidation),
|
|
4700
|
+
onFormChange: bind(onFormChange),
|
|
4701
|
+
onSubmitSuccess: bind(onSubmitSuccess),
|
|
4702
|
+
onReset: bind(onReset),
|
|
4703
|
+
emitSubmitSuccess: bind(emitSubmitSuccess),
|
|
4704
|
+
registerCleanup: bind(registerCleanup),
|
|
4705
|
+
dispose: bind(dispose)
|
|
4716
4706
|
};
|
|
4717
4707
|
rebuildAuthoredPaths(st, defaultValues, schemaInitialData);
|
|
4718
4708
|
seedOriginalsFromBaseline(st, schemaInitialData, true);
|
|
@@ -4751,7 +4741,7 @@ function createFormStore(options) {
|
|
|
4751
4741
|
blurredAfterInteraction: false
|
|
4752
4742
|
});
|
|
4753
4743
|
});
|
|
4754
|
-
if (
|
|
4744
|
+
if (!schemaResponse.success) {
|
|
4755
4745
|
replaceErrorChannel(st, "schema", schemaResponse.errors);
|
|
4756
4746
|
}
|
|
4757
4747
|
}
|
|
@@ -4782,19 +4772,18 @@ function useAbstractForm(configuration, options) {
|
|
|
4782
4772
|
const resolvedDefaults = resolveTrichotomy(configuration.defaultValues);
|
|
4783
4773
|
const materialisedDefaults = resolvedDefaults.kind === "sync" ? resolvedDefaults.value : void 0;
|
|
4784
4774
|
const { defaultValues: _droppedDefaults, ...configWithoutDefaults } = configuration;
|
|
4785
|
-
const
|
|
4786
|
-
const merged = mergeWithDefaults(registry.defaults, trichotomyOverride);
|
|
4787
|
-
const maxRecursionDepth = normalizeNumericOption({
|
|
4788
|
-
value: merged.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH,
|
|
4789
|
-
source: "useForm.maxRecursionDepth",
|
|
4790
|
-
allowInfinity: true,
|
|
4791
|
-
min: 0,
|
|
4792
|
-
defaultValue: DEFAULT_MAX_RECURSION_DEPTH
|
|
4793
|
-
});
|
|
4794
|
-
const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
|
|
4775
|
+
const materialisedConfiguration = materialisedDefaults === void 0 ? configWithoutDefaults : { ...configWithoutDefaults, defaultValues: materialisedDefaults };
|
|
4795
4776
|
const existing = registry.forms.get(key);
|
|
4777
|
+
const resolvedSchema = existing === void 0 || false ? getComputedSchema(key, configuration.schema, {
|
|
4778
|
+
maxRecursionDepth: DEFAULT_MAX_RECURSION_DEPTH
|
|
4779
|
+
}) : existing.schema;
|
|
4796
4780
|
const hadPendingHydration = registry.pendingHydration.has(key);
|
|
4797
|
-
const state = existing ?? buildFreshState(
|
|
4781
|
+
const state = existing ?? buildFreshState(
|
|
4782
|
+
key,
|
|
4783
|
+
resolvedSchema,
|
|
4784
|
+
materialisedConfiguration,
|
|
4785
|
+
registry
|
|
4786
|
+
);
|
|
4798
4787
|
if (existing !== void 0) ; else if (resolvedDefaults.kind === "sync") {
|
|
4799
4788
|
state.defaultsResolved.value = true;
|
|
4800
4789
|
}
|
|
@@ -4819,8 +4808,8 @@ function useAbstractForm(configuration, options) {
|
|
|
4819
4808
|
const releaseConsumer = registry.trackConsumer(key);
|
|
4820
4809
|
onScopeDispose(releaseConsumer);
|
|
4821
4810
|
}
|
|
4822
|
-
if (existing === void 0 &&
|
|
4823
|
-
const historyModule =
|
|
4811
|
+
if (existing === void 0 && materialisedConfiguration.history !== void 0) {
|
|
4812
|
+
const historyModule = materialisedConfiguration.history.attach(state);
|
|
4824
4813
|
state.modules.set(HISTORY_MODULE_KEY, historyModule);
|
|
4825
4814
|
state.registerCleanup(() => historyModule.dispose());
|
|
4826
4815
|
}
|
|
@@ -4833,36 +4822,16 @@ function useAbstractForm(configuration, options) {
|
|
|
4833
4822
|
provide(kFormInstanceId, formInstanceId);
|
|
4834
4823
|
}
|
|
4835
4824
|
const apiOptions = pickDefined({
|
|
4836
|
-
|
|
4825
|
+
focusOnInvalidSubmit: materialisedConfiguration.focusOnInvalidSubmit,
|
|
4837
4826
|
history: state.modules.get(HISTORY_MODULE_KEY),
|
|
4838
|
-
validateOn:
|
|
4839
|
-
debounceMs:
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
rememberVariants: merged.rememberVariants,
|
|
4843
|
-
autoAria: merged.autoAria
|
|
4827
|
+
validateOn: materialisedConfiguration.validateOn,
|
|
4828
|
+
debounceMs: materialisedConfiguration.debounceMs,
|
|
4829
|
+
coerce: materialisedConfiguration.coerce,
|
|
4830
|
+
rememberVariants: materialisedConfiguration.rememberVariants
|
|
4844
4831
|
});
|
|
4845
4832
|
const api = buildFormApi(state, formInstanceId, apiOptions);
|
|
4846
4833
|
return api;
|
|
4847
4834
|
}
|
|
4848
|
-
function mergeWithDefaults(defaults, configuration) {
|
|
4849
|
-
return {
|
|
4850
|
-
...configuration,
|
|
4851
|
-
...pickDefined({
|
|
4852
|
-
strict: configuration.strict ?? defaults.strict,
|
|
4853
|
-
onInvalidSubmit: configuration.onInvalidSubmit ?? defaults.onInvalidSubmit,
|
|
4854
|
-
history: configuration.history ?? defaults.history,
|
|
4855
|
-
rememberVariants: configuration.rememberVariants ?? defaults.rememberVariants,
|
|
4856
|
-
disabled: configuration.disabled ?? defaults.disabled,
|
|
4857
|
-
coerce: configuration.coerce ?? defaults.coerce,
|
|
4858
|
-
validateOn: configuration.validateOn ?? defaults.validateOn,
|
|
4859
|
-
debounceMs: configuration.debounceMs ?? defaults.debounceMs,
|
|
4860
|
-
getDisplayState: configuration.getDisplayState ?? defaults.getDisplayState,
|
|
4861
|
-
maxRecursionDepth: configuration.maxRecursionDepth ?? defaults.maxRecursionDepth,
|
|
4862
|
-
autoAria: configuration.autoAria ?? defaults.autoAria
|
|
4863
|
-
})
|
|
4864
|
-
};
|
|
4865
|
-
}
|
|
4866
4835
|
const HISTORY_MODULE_KEY = "history";
|
|
4867
4836
|
function buildFreshState(key, schema, configuration, registry) {
|
|
4868
4837
|
const pending = registry.pendingHydration.get(key);
|
|
@@ -4882,21 +4851,17 @@ function buildFreshState(key, schema, configuration, registry) {
|
|
|
4882
4851
|
hydration: pending,
|
|
4883
4852
|
ssr: registry.ssr,
|
|
4884
4853
|
...pickDefined({
|
|
4885
|
-
strict: configuration.strict,
|
|
4886
4854
|
validateOn: configuration.validateOn,
|
|
4887
4855
|
debounceMs: configuration.debounceMs,
|
|
4888
4856
|
rememberVariants: configuration.rememberVariants,
|
|
4889
4857
|
disabled: configuration.disabled,
|
|
4890
4858
|
coerce: configuration.coerce,
|
|
4891
|
-
getDisplayState: configuration.getDisplayState,
|
|
4892
4859
|
initialBlankPaths
|
|
4893
4860
|
}),
|
|
4894
|
-
// Server
|
|
4895
|
-
//
|
|
4896
|
-
//
|
|
4897
|
-
//
|
|
4898
|
-
// wizard explicitly skipped this key — even an explicit
|
|
4899
|
-
// `form.activate()` defers to the wizard's render-efficiency
|
|
4861
|
+
// Server only. `enqueue` records intent on every `state.activate()`
|
|
4862
|
+
// so a wizard skip-list override has a consistent set to diff
|
|
4863
|
+
// against; `shouldFire` lets the activate path bail on a key the
|
|
4864
|
+
// wizard skipped. Even an explicit `form.activate()` defers to that
|
|
4900
4865
|
// skip-list on the server.
|
|
4901
4866
|
...registry.ssr ? {
|
|
4902
4867
|
ssrPrefetch: {
|
|
@@ -5135,25 +5100,18 @@ function createWizardHistory(param) {
|
|
|
5135
5100
|
};
|
|
5136
5101
|
}
|
|
5137
5102
|
|
|
5138
|
-
const NOOP_FINGERPRINT = "attaform:wizard-noop";
|
|
5139
5103
|
const EMPTY_SLIM_KINDS = /* @__PURE__ */ new Set();
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
errors: void 0,
|
|
5152
|
-
formKey
|
|
5153
|
-
};
|
|
5154
|
-
return {
|
|
5155
|
-
fingerprint: () => Promise.resolve(NOOP_FINGERPRINT),
|
|
5156
|
-
getDefaultValues: () => defaultsResponse,
|
|
5104
|
+
const EMPTY_VALUE = {};
|
|
5105
|
+
const NOOP_SUCCESS = {
|
|
5106
|
+
success: true,
|
|
5107
|
+
data: EMPTY_VALUE,
|
|
5108
|
+
errors: void 0
|
|
5109
|
+
};
|
|
5110
|
+
const NOOP_DEFAULTS = NOOP_SUCCESS;
|
|
5111
|
+
let NOOP_SCHEMA;
|
|
5112
|
+
function buildNoopWizardSchema() {
|
|
5113
|
+
NOOP_SCHEMA ?? (NOOP_SCHEMA = {
|
|
5114
|
+
getDefaultValues: () => NOOP_DEFAULTS,
|
|
5157
5115
|
getDefaultAtPath: () => void 0,
|
|
5158
5116
|
getEmptyValueAtPath: () => void 0,
|
|
5159
5117
|
isPreprocessOrCoerceLeaf: () => false,
|
|
@@ -5164,13 +5122,14 @@ function buildNoopWizardSchema(formKey) {
|
|
|
5164
5122
|
// addressable, root included.
|
|
5165
5123
|
entryKeyKindAtPath: () => void 0,
|
|
5166
5124
|
getSchemasAtPath: () => [],
|
|
5167
|
-
validateAtPath: () =>
|
|
5125
|
+
validateAtPath: () => NOOP_SUCCESS,
|
|
5168
5126
|
getSlimPrimitiveTypesAtPath: () => new Set(EMPTY_SLIM_KINDS),
|
|
5169
5127
|
isLeafAtPath: () => false,
|
|
5170
5128
|
isRequiredAtPath: () => false,
|
|
5171
5129
|
getUnionDiscriminatorAtPath: () => void 0,
|
|
5172
5130
|
hasDiscriminatedUnions: () => false
|
|
5173
|
-
};
|
|
5131
|
+
});
|
|
5132
|
+
return NOOP_SCHEMA;
|
|
5174
5133
|
}
|
|
5175
5134
|
|
|
5176
5135
|
function buildCallableReadonlySnapshotProxy(opts) {
|
|
@@ -5285,7 +5244,7 @@ function useWizard(options) {
|
|
|
5285
5244
|
if (typeof slot !== "string") continue;
|
|
5286
5245
|
if (noopForms.has(slot)) continue;
|
|
5287
5246
|
const noop = useAbstractForm({
|
|
5288
|
-
schema: buildNoopWizardSchema(
|
|
5247
|
+
schema: buildNoopWizardSchema(),
|
|
5289
5248
|
key: slot
|
|
5290
5249
|
});
|
|
5291
5250
|
noopForms.set(slot, noop);
|
|
@@ -5296,7 +5255,7 @@ function useWizard(options) {
|
|
|
5296
5255
|
const noop = lazyNoopScope.run(
|
|
5297
5256
|
() => useAbstractForm(
|
|
5298
5257
|
{
|
|
5299
|
-
schema: buildNoopWizardSchema(
|
|
5258
|
+
schema: buildNoopWizardSchema(),
|
|
5300
5259
|
key
|
|
5301
5260
|
},
|
|
5302
5261
|
{ registry }
|
|
@@ -5364,27 +5323,33 @@ function useWizard(options) {
|
|
|
5364
5323
|
return activeKey.value === "" ? void 0 : activeKey.value;
|
|
5365
5324
|
}
|
|
5366
5325
|
};
|
|
5367
|
-
function lazyComputedFor(marker) {
|
|
5326
|
+
function lazyComputedFor(marker, index) {
|
|
5368
5327
|
const cached = lazyComputeds.get(marker);
|
|
5369
5328
|
if (cached !== void 0) return cached;
|
|
5370
5329
|
const c = computed(() => {
|
|
5371
5330
|
void lazyEpoch.value;
|
|
5372
|
-
return
|
|
5331
|
+
return callConsumerFn(
|
|
5332
|
+
() => marker.resolve(slotCtx),
|
|
5333
|
+
void 0);
|
|
5373
5334
|
});
|
|
5374
5335
|
lazyComputeds.set(marker, c);
|
|
5375
5336
|
return c;
|
|
5376
5337
|
}
|
|
5377
5338
|
const MAX_SLOT_DEPTH = 32;
|
|
5378
|
-
function normalizeSlot(slot, ctx, gated, depth) {
|
|
5339
|
+
function normalizeSlot(slot, ctx, gated, depth, index) {
|
|
5379
5340
|
if (depth > MAX_SLOT_DEPTH) {
|
|
5380
5341
|
return void 0;
|
|
5381
5342
|
}
|
|
5382
5343
|
if (slot === void 0 || slot === null) return void 0;
|
|
5383
5344
|
if (typeof slot === "string") return { form: getOrBuildNoop(slot), gated };
|
|
5384
5345
|
if (isGateMarker(slot)) return normalizeSlot(slot.inner, ctx, true, depth + 1);
|
|
5385
|
-
if (isLazyMarker(slot))
|
|
5346
|
+
if (isLazyMarker(slot)) {
|
|
5347
|
+
return normalizeSlot(lazyComputedFor(slot).value, ctx, gated, depth + 1);
|
|
5348
|
+
}
|
|
5386
5349
|
if (typeof slot === "function") {
|
|
5387
|
-
const result =
|
|
5350
|
+
const result = callConsumerFn(
|
|
5351
|
+
() => slot(ctx),
|
|
5352
|
+
void 0);
|
|
5388
5353
|
return normalizeSlot(result, ctx, gated, depth + 1);
|
|
5389
5354
|
}
|
|
5390
5355
|
if (isAnyForm(slot)) return { form: slot, gated };
|
|
@@ -5702,13 +5667,14 @@ function useWizard(options) {
|
|
|
5702
5667
|
seedRef.value = resolved.value;
|
|
5703
5668
|
applyGateSeed(resolved.value);
|
|
5704
5669
|
} else {
|
|
5705
|
-
const eager = resolved.factory();
|
|
5670
|
+
const eager = callConsumerFn(() => resolved.factory(), void 0);
|
|
5706
5671
|
if (eager instanceof Promise) {
|
|
5707
5672
|
void eager.then((value) => {
|
|
5708
5673
|
seedRef.value = value;
|
|
5709
5674
|
applyGateSeed(value);
|
|
5675
|
+
}).catch((err) => {
|
|
5710
5676
|
});
|
|
5711
|
-
} else {
|
|
5677
|
+
} else if (eager !== void 0) {
|
|
5712
5678
|
seedRef.value = eager;
|
|
5713
5679
|
applyGateSeed(eager);
|
|
5714
5680
|
}
|
|
@@ -5781,10 +5747,13 @@ function useWizard(options) {
|
|
|
5781
5747
|
const statuses = buildWizardStatusesProxy(statusesRecord);
|
|
5782
5748
|
const progressOverride = options.progress;
|
|
5783
5749
|
const progress = computed(() => {
|
|
5750
|
+
const list = compiledSteps.value;
|
|
5784
5751
|
if (progressOverride !== void 0) {
|
|
5785
|
-
|
|
5752
|
+
const override = callConsumerFn(
|
|
5753
|
+
() => progressOverride(list),
|
|
5754
|
+
null);
|
|
5755
|
+
if (override !== null) return override;
|
|
5786
5756
|
}
|
|
5787
|
-
const list = compiledSteps.value;
|
|
5788
5757
|
if (list.length === 0) return 0;
|
|
5789
5758
|
let valid = 0;
|
|
5790
5759
|
for (const step of list) {
|
|
@@ -5812,11 +5781,15 @@ function useWizard(options) {
|
|
|
5812
5781
|
historyHandle.subscribe((value) => {
|
|
5813
5782
|
urlMirror.value = value;
|
|
5814
5783
|
});
|
|
5815
|
-
const
|
|
5784
|
+
const consumerRestore = options.restore;
|
|
5785
|
+
const restoreCallback = consumerRestore === false ? void 0 : consumerRestore !== void 0 ? () => callConsumerFn(() => consumerRestore(), void 0) : () => {
|
|
5816
5786
|
const value = urlMirror.value;
|
|
5817
5787
|
return value === void 0 ? void 0 : { step: value };
|
|
5818
5788
|
};
|
|
5819
|
-
const
|
|
5789
|
+
const consumerPersist = options.persist;
|
|
5790
|
+
const persistCallback = consumerPersist === false ? void 0 : consumerPersist !== void 0 ? (state) => {
|
|
5791
|
+
callConsumerFn(() => consumerPersist(state), void 0);
|
|
5792
|
+
} : (state) => {
|
|
5820
5793
|
if (state.step === void 0) return;
|
|
5821
5794
|
const current = historyHandle.read();
|
|
5822
5795
|
const effectiveCurrent = current !== void 0 && isCompiledKey(current) ? current : firstKey();
|
|
@@ -6380,4 +6353,4 @@ function escapeForInlineScript(json) {
|
|
|
6380
6353
|
});
|
|
6381
6354
|
}
|
|
6382
6355
|
|
|
6383
|
-
export { AttaformErrorCode as A,
|
|
6356
|
+
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 };
|