attaform 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abstract.d.mts +15 -21
- package/dist/abstract.mjs +3 -3
- package/dist/dev/abstract.mjs +4 -4
- package/dist/dev/chunks/devtools.mjs +4 -4
- package/dist/dev/directive.mjs +1 -1
- package/dist/dev/history.mjs +2 -2
- package/dist/dev/index.mjs +5 -5
- package/dist/dev/runtime/plugins/attaform.mjs +1 -1
- package/dist/dev/shared/{attaform-dev.DUZckyV8.mjs → attaform-dev.B1q-x9R9.mjs} +4 -4
- package/dist/dev/shared/{attaform-dev.BcYT6tFh.mjs → attaform-dev.CZz4GYZ6.mjs} +21 -9
- package/dist/dev/shared/{attaform-dev.BisFstxh.mjs → attaform-dev.Caibt7sb.mjs} +16 -15
- package/dist/dev/shared/{attaform-dev.DTrHonpc.mjs → attaform-dev.CtyqGia7.mjs} +176 -260
- package/dist/dev/shared/{attaform-dev.BK6Ge2qR.mjs → attaform-dev.DJ3nsWw6.mjs} +86 -122
- package/dist/dev/shared/{attaform-dev.BcNtyOmf.mjs → attaform-dev.DlaT4xGd.mjs} +4 -4
- package/dist/dev/shared/{attaform-dev.D6CLvaQT.mjs → attaform-dev.k5X47GMD.mjs} +520 -395
- package/dist/dev/shared/{attaform-dev.CQ2rJiAX.mjs → attaform-dev.y9x9Vp9F.mjs} +324 -234
- package/dist/dev/shared/{attaform-dev.BQc5QV3Y.mjs → attaform-dev.zlvDr0WB.mjs} +2 -2
- 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 +2 -2
- package/dist/index.d.mts +67 -70
- package/dist/index.mjs +4 -4
- package/dist/nuxt.d.mts +4 -3
- package/dist/nuxt.mjs +3 -6
- 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 +15 -15
- package/dist/runtime/components/DevtoolsValueTree.vue +2 -2
- package/dist/runtime/plugins/attaform.mjs +1 -1
- package/dist/shared/{attaform.BRDDQeHV.mjs → attaform.8mfKZCwH.mjs} +2 -2
- 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.DhnPb6nK.mjs → attaform.BAf3UNn4.mjs} +1 -1
- package/dist/shared/{attaform.BXneBxEj.mjs → attaform.BWUn8Kjy.mjs} +85 -121
- package/dist/shared/{attaform.WiGdIqgx.mjs → attaform.C4shmwyJ.mjs} +2 -2
- package/dist/shared/{attaform.Rc9u4Vfw.d.mts → attaform.CRVe8s82.d.mts} +83 -95
- package/dist/shared/attaform.D9RqPgGu.d.mts +42 -0
- package/dist/shared/attaform.DAo0QBmS.d.mts +2173 -0
- package/dist/shared/{attaform.BK0cgu2K.mjs → attaform.DBCbxlyk.mjs} +62 -4
- package/dist/shared/{attaform.CUfrMK7t.mjs → attaform.DL2nl_QR.mjs} +15 -14
- package/dist/shared/{attaform.DNJOWjQw.mjs → attaform.DNPJPOO7.mjs} +299 -218
- package/dist/shared/attaform.DZnrDrNb.d.mts +4657 -0
- package/dist/shared/{attaform.B3resMQ1.mjs → attaform.DxKQsqFZ.mjs} +496 -371
- package/dist/shared/{attaform.ByWP6kSx.mjs → attaform.L011tHgJ.mjs} +3 -3
- package/dist/shared/{attaform.CJ8DpRGj.mjs → attaform.LUPC-2Qg.mjs} +175 -257
- package/dist/transforms.d.mts +153 -26
- package/dist/transforms.mjs +1 -1
- package/dist/vite.d.mts +29 -52
- package/dist/vite.mjs +3 -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 +5 -5
- package/dist/zod-v4.d.mts +120 -118
- package/dist/zod-v4.mjs +5 -5
- package/dist/zod.d.mts +5 -5
- package/dist/zod.mjs +4 -4
- package/package.json +7 -6
- package/skills/attaform/SKILL.md +1 -1
- package/skills/attaform/references/custom-components.md +4 -0
- package/dist/shared/attaform.Clz0ArIi.d.mts +0 -44
- package/dist/shared/attaform.UGyOK83T.d.mts +0 -2366
- package/dist/shared/attaform.bKLzMU0o.d.mts +0 -4761
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, nextTick, warn, reactive, shallowReactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted
|
|
2
|
-
import { A as ANONYMOUS_FORM_KEY_PREFIX, i as isShadowedKey, d as safeOwnHas, a as safeOwnRead, e as consumerHas, f as spreadConsumerRecord, g as copyConsumerArray, r as readConsumerIndex, b as safeAssign, h as consumerKeys, j as readConsumerProp, k as diffAndApply, p as pickDefined, s as structuralSnapshot, n as normalizeNumericOption, l as DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS, m as consumerSymbolKeys, o as applyChangedKeys, R as RESERVED_KEY_PREFIX, q as DEFAULT_MAX_RECURSION_DEPTH, t as ANONYMOUS_WIZARD_KEY_PREFIX } from './attaform-dev.
|
|
3
|
-
import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform-dev.
|
|
4
|
-
import { R as REGISTER_OWNER_MARKER, d as armDomBinding,
|
|
5
|
-
import { i as isPathPrefix, e as canonicalizePath, k as keyForSegments, m as mapSegmentKeys, b as ROOT_PATH_KEY, s as segmentsForPathKey, S as SubmitErrorHandlerError, t as toError, j as groupErrorsByKey, n as normalizeErrorInput, h as SET_MEMBER_SEGMENT, R as ROOT_PATH, l as normalizeErrorInputs, a as InvalidUseFormConfigError, d as ReservedFormKeyError } from './attaform-dev.
|
|
1
|
+
import { computed, readonly, toRaw, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, nextTick, warn, reactive, shallowReactive, shallowRef, toValue, watch, triggerRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, effectScope, onBeforeMount, onBeforeUpdate, onMounted } from 'vue';
|
|
2
|
+
import { A as ANONYMOUS_FORM_KEY_PREFIX, i as isShadowedKey, d as safeOwnHas, a as safeOwnRead, e as consumerHas, f as spreadConsumerRecord, g as copyConsumerArray, r as readConsumerIndex, b as safeAssign, h as consumerKeys, j as readConsumerProp, k as diffAndApply, p as pickDefined, s as structuralSnapshot, n as normalizeNumericOption, l as DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS, m as consumerSymbolKeys, o as applyChangedKeys, R as RESERVED_KEY_PREFIX, q as DEFAULT_MAX_RECURSION_DEPTH, c as callConsumerFn, t as reportConsumerThrow, u as ANONYMOUS_WIZARD_KEY_PREFIX } from './attaform-dev.CZz4GYZ6.mjs';
|
|
3
|
+
import { e as ensureAttaformInstalled, u as useRegistry, d as kFormContext, f as kFormInstanceId, b as kAttaformWizardActiveStepResolver, i as kAttaformAncestorWizard } from './attaform-dev.B1q-x9R9.mjs';
|
|
4
|
+
import { R as REGISTER_OWNER_MARKER, V as V_REGISTER_MARKER, d as armDomBinding, r as recordInheritedModifiers, w as withInheritedModifiers } from './attaform-dev.k5X47GMD.mjs';
|
|
5
|
+
import { i as isPathPrefix, e as canonicalizePath, k as keyForSegments, m as mapSegmentKeys, b as ROOT_PATH_KEY, s as segmentsForPathKey, S as SubmitErrorHandlerError, t as toError, j as groupErrorsByKey, n as normalizeErrorInput, h as SET_MEMBER_SEGMENT, R as ROOT_PATH, l as normalizeErrorInputs, a as InvalidUseFormConfigError, d as ReservedFormKeyError } from './attaform-dev.zlvDr0WB.mjs';
|
|
6
6
|
|
|
7
7
|
const AttaformErrorCode = {
|
|
8
|
-
/** A required field is in the blank set
|
|
8
|
+
/** A required field is in the blank set: no value was supplied. */
|
|
9
9
|
NoValueSupplied: "atta:no-value-supplied",
|
|
10
10
|
/** The schema adapter's `validateAtPath` threw synchronously. */
|
|
11
11
|
AdapterThrew: "atta:adapter-threw",
|
|
@@ -531,7 +531,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
531
531
|
const value = state.getValueAtPath(segments);
|
|
532
532
|
const original = state.originals.get(key)?.value;
|
|
533
533
|
const pristine = state.isPristineAtPathByKey(key, segments);
|
|
534
|
-
const cell = state.errorCells.get(key);
|
|
534
|
+
const cell = state.errorCells().get(key);
|
|
535
535
|
const schemaForKey = cell !== void 0 && cell.schema.length > 0 ? cell.schema : void 0;
|
|
536
536
|
const blankForKey = state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments) ? [makeBlankRequiredError(segments)] : void 0;
|
|
537
537
|
const userForKey = cell !== void 0 && cell.user.length > 0 ? cell.user : void 0;
|
|
@@ -565,9 +565,8 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
565
565
|
updatedAt: record?.updatedAt ?? null,
|
|
566
566
|
errors,
|
|
567
567
|
// A leaf has no descendants, so its own-bucket errors ARE its subtree
|
|
568
|
-
// errors. Reuse the
|
|
569
|
-
//
|
|
570
|
-
// map (preserves the P3 blank-reactivity optimization above).
|
|
568
|
+
// errors. Reuse the array reference, which makes `ownErrors === errors`
|
|
569
|
+
// and avoids a second read of the whole-form `derivedBlankErrors` map.
|
|
571
570
|
ownErrors: errors,
|
|
572
571
|
validating,
|
|
573
572
|
valid,
|
|
@@ -747,19 +746,19 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
|
|
|
747
746
|
elements: EMPTY_ELEMENTS,
|
|
748
747
|
updatedAt,
|
|
749
748
|
errors,
|
|
750
|
-
// The container's OWN bucket
|
|
751
|
-
// descendant
|
|
752
|
-
//
|
|
753
|
-
//
|
|
754
|
-
//
|
|
749
|
+
// The container's OWN bucket at this exact path, excluding every
|
|
750
|
+
// descendant `errors` rolls up: a cross-field `.refine()` here, or a
|
|
751
|
+
// `setErrors` pinned at this node. The same raw union the leaf builds
|
|
752
|
+
// from the three stores, and the container already depends on all three
|
|
753
|
+
// through `aggregateErrorsAt`, so it costs no new reactivity.
|
|
755
754
|
ownErrors: state.getErrorsForPath(segments),
|
|
756
755
|
validating,
|
|
757
756
|
valid,
|
|
758
757
|
transforming,
|
|
759
758
|
busy: transforming || validating,
|
|
760
|
-
// A container surfaces its OWN transform failure
|
|
761
|
-
// on the container path
|
|
762
|
-
// descendant
|
|
759
|
+
// A container surfaces its OWN transform failure, from a transform
|
|
760
|
+
// registered on the container path such as a file normalizer, and never
|
|
761
|
+
// rolls up a descendant's: that stays a per-field channel.
|
|
763
762
|
transformError: ownTransformError,
|
|
764
763
|
path: segments,
|
|
765
764
|
...computeFieldIdentity(formInstanceId, state.formKey, key),
|
|
@@ -821,7 +820,7 @@ function aggregateErrorsAt(state, prefix, prefixKey) {
|
|
|
821
820
|
const candidates = state.errorWindowAt(prefix, prefixKey);
|
|
822
821
|
if (candidates.length === 0) return [];
|
|
823
822
|
const formValue = state.form.value;
|
|
824
|
-
const cells = state.errorCells;
|
|
823
|
+
const cells = state.errorCells();
|
|
825
824
|
const buckets = /* @__PURE__ */ new Map();
|
|
826
825
|
for (const { key, segments } of candidates) {
|
|
827
826
|
if (!isPathPrefix(prefix, segments)) continue;
|
|
@@ -886,7 +885,7 @@ function isArrayPath(state, segments) {
|
|
|
886
885
|
function warnReadOnly(surface, action, key) {
|
|
887
886
|
const phrase = action === "write" ? `write to "${String(key)}"` : `${action} of "${String(key)}"`;
|
|
888
887
|
console.warn(
|
|
889
|
-
`[attaform] ${surface} is read-only
|
|
888
|
+
`[attaform] ${surface} is read-only; ${phrase} was ignored. Mutate the form via setValue / the directive / field-array helpers instead.`
|
|
890
889
|
);
|
|
891
890
|
}
|
|
892
891
|
function makeReadonlyCoercion(snapshot) {
|
|
@@ -1094,16 +1093,16 @@ function buildErrorsSurface(state, sweep) {
|
|
|
1094
1093
|
// non-schema keys reachable while a genuinely-absent key stays out.
|
|
1095
1094
|
hasOwn: (segments, key) => liveContainerHasKey(state, segments, key) || errorAwareContainerKeys(state, segments).includes(key),
|
|
1096
1095
|
isArrayAt: (segments) => isArrayPath(state, segments),
|
|
1097
|
-
// `errors()
|
|
1098
|
-
// aggregate
|
|
1099
|
-
//
|
|
1096
|
+
// `errors()`, `errors([])` and `errors(path)` are all the subtree
|
|
1097
|
+
// aggregate, through the helper `meta.errors` reads, so they cannot
|
|
1098
|
+
// drift.
|
|
1100
1099
|
call: (path) => aggregateErrorsAt(state, path, keyForSegments(path).key),
|
|
1101
1100
|
surface: "form.errors",
|
|
1102
1101
|
sweep
|
|
1103
1102
|
});
|
|
1104
1103
|
}
|
|
1105
1104
|
function collectErrorsAt(state, key, segments, active, into) {
|
|
1106
|
-
const cell = state.errorCells.get(key);
|
|
1105
|
+
const cell = state.errorCells().get(key);
|
|
1107
1106
|
if (active) {
|
|
1108
1107
|
if (cell !== void 0) into.push(...cell.schema);
|
|
1109
1108
|
if (state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments)) {
|
|
@@ -1690,7 +1689,7 @@ function createArrayBookkeeping(deps) {
|
|
|
1690
1689
|
...record,
|
|
1691
1690
|
path: segments
|
|
1692
1691
|
}));
|
|
1693
|
-
migrateMapSubtree(errorCells, arrayPath, remap, (cell, segments) => ({
|
|
1692
|
+
migrateMapSubtree(errorCells(), arrayPath, remap, (cell, segments) => ({
|
|
1694
1693
|
schema: NO_ERRORS,
|
|
1695
1694
|
user: cell.user.map((error) => ({ ...error, path: [...segments] }))
|
|
1696
1695
|
}));
|
|
@@ -1740,7 +1739,7 @@ function createArrayBookkeeping(deps) {
|
|
|
1740
1739
|
migrateElementState(arrayPath, remap);
|
|
1741
1740
|
for (const freshIndex of remap.fresh) seedFreshElement(arrayPath, freshIndex);
|
|
1742
1741
|
const changed = changedIndices(remap);
|
|
1743
|
-
evictSchemaVerdictsAtIndices(errorCells, arrayPath, changed);
|
|
1742
|
+
evictSchemaVerdictsAtIndices(errorCells(), arrayPath, changed);
|
|
1744
1743
|
abortValidationAtVacatedIndices(arrayPath, remap);
|
|
1745
1744
|
variantMemory.dropAtIndices(arrayPath, changed);
|
|
1746
1745
|
arrayIdentity.applyOp(arrayPath, remap);
|
|
@@ -2061,7 +2060,7 @@ function adapterThrowMessage(err) {
|
|
|
2061
2060
|
return "Adapter validateAtPath threw a non-Error value";
|
|
2062
2061
|
}
|
|
2063
2062
|
function hasUserErrorEntries(state) {
|
|
2064
|
-
for (const cell of state.errorCells.values()) {
|
|
2063
|
+
for (const cell of state.errorCells().values()) {
|
|
2065
2064
|
if (cell.user.length > 0) return true;
|
|
2066
2065
|
}
|
|
2067
2066
|
return false;
|
|
@@ -2161,9 +2160,9 @@ function reportRejection(store, path, kind, accepted) {
|
|
|
2161
2160
|
if (!shouldWarnOnce(store, key)) return;
|
|
2162
2161
|
if (accepted.size === 0) {
|
|
2163
2162
|
console.warn(
|
|
2164
|
-
`[attaform] Cannot write to '${dotted}'
|
|
2163
|
+
`[attaform] Cannot write to '${dotted}': this path is not in your schema.
|
|
2165
2164
|
Fix: check for a typo in register('${dotted}'); it should match a leaf key in your schema.
|
|
2166
|
-
(If the path resolves to a never-typed schema, it explicitly admits no values
|
|
2165
|
+
(If the path resolves to a never-typed schema, it explicitly admits no values; relax the schema if intentional.)
|
|
2167
2166
|
The write was a no-op.`
|
|
2168
2167
|
);
|
|
2169
2168
|
return;
|
|
@@ -2171,16 +2170,16 @@ function reportRejection(store, path, kind, accepted) {
|
|
|
2171
2170
|
const expected = formatExpectedKinds(accepted);
|
|
2172
2171
|
if (kind === "string" && accepted.has("number")) {
|
|
2173
2172
|
console.warn(
|
|
2174
|
-
`[attaform] Cannot write a string to '${dotted}'
|
|
2175
|
-
Fix:
|
|
2176
|
-
<input type="number" v-register="register('${dotted}')" />
|
|
2173
|
+
`[attaform] Cannot write a string to '${dotted}': the schema expects ${expected}.
|
|
2174
|
+
Fix: use the .number modifier on v-register, OR add type="number" to a native input:
|
|
2177
2175
|
<input v-register.number="register('${dotted}')" />
|
|
2176
|
+
<input type="number" v-register="register('${dotted}')" />
|
|
2178
2177
|
The write was a no-op.`
|
|
2179
2178
|
);
|
|
2180
2179
|
return;
|
|
2181
2180
|
}
|
|
2182
2181
|
console.warn(
|
|
2183
|
-
`[attaform] Cannot write a ${kind} to '${dotted}'
|
|
2182
|
+
`[attaform] Cannot write a ${kind} to '${dotted}': the schema expects ${expected}.
|
|
2184
2183
|
The write was a no-op.`
|
|
2185
2184
|
);
|
|
2186
2185
|
}
|
|
@@ -2330,6 +2329,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2330
2329
|
const isRequired = state.schema.isRequiredAtPath(segments);
|
|
2331
2330
|
const ariaDisplayState = getDisplayStateAt !== void 0 ? computed(() => getDisplayStateAt(segments)) : void 0;
|
|
2332
2331
|
const markBlank = () => state.setValueAtPath(segments, slimDefault, withInstanceMeta({ blank: true }));
|
|
2332
|
+
const commitFromHost = (value) => value === "" && !acceptsString || value === null && !slimTypes.has("null") || value === void 0 && !acceptsUndefined ? markBlank() : state.setValueAtPath(segments, value, withInstanceMeta(void 0));
|
|
2333
2333
|
const internalRv = {
|
|
2334
2334
|
innerRef,
|
|
2335
2335
|
displayValue,
|
|
@@ -2346,7 +2346,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2346
2346
|
},
|
|
2347
2347
|
ensureDomBinding: (factory) => {
|
|
2348
2348
|
var _a;
|
|
2349
|
-
(_a = state.domBinding).value ?? (_a.value = factory(state));
|
|
2349
|
+
return (_a = state.domBinding).value ?? (_a.value = factory(state));
|
|
2350
2350
|
},
|
|
2351
2351
|
registerElement: (element) => {
|
|
2352
2352
|
const dom = state.domBinding.value;
|
|
@@ -2366,10 +2366,10 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2366
2366
|
setValueWithInternalPath: (value, meta) => {
|
|
2367
2367
|
return state.setValueAtPath(segments, value, withInstanceMeta(meta));
|
|
2368
2368
|
},
|
|
2369
|
-
|
|
2369
|
+
commitFromHost,
|
|
2370
|
+
setValueFromHost: (value, modifiers) => {
|
|
2370
2371
|
state.markInteracted(segments);
|
|
2371
|
-
const
|
|
2372
|
-
const accepted = isBlankSignal ? markBlank() : state.setValueAtPath(segments, value, withInstanceMeta(void 0));
|
|
2372
|
+
const accepted = state.domBinding.value?.hostChannel.write(internalRv, value, modifiers) ?? commitFromHost(value);
|
|
2373
2373
|
{
|
|
2374
2374
|
const dedupeKey = `${formInstanceId}:${pathKey}`;
|
|
2375
2375
|
const isWired = () => (state.domBinding.value?.elements.get(pathKey)?.elements.size ?? 0) > 0 || state.getFieldRecord(segments)?.connected === true;
|
|
@@ -2385,7 +2385,8 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2385
2385
|
}
|
|
2386
2386
|
return accepted;
|
|
2387
2387
|
},
|
|
2388
|
-
|
|
2388
|
+
setValueFromHostInput: (payload, modifiers) => state.domBinding.value?.hostChannel.writeInput(internalRv, payload, modifiers) ?? false,
|
|
2389
|
+
// Called by the `vRegisterHintTransform` compile-time transform's wrapping
|
|
2389
2390
|
// IIFE on every server-side render of `<element v-register="…">`.
|
|
2390
2391
|
// Without it, every SSR'd FieldState serialises `connected: false`
|
|
2391
2392
|
// (because Vue skips directive lifecycle during SSR) and the client
|
|
@@ -2414,12 +2415,12 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2414
2415
|
}
|
|
2415
2416
|
return false;
|
|
2416
2417
|
},
|
|
2417
|
-
// --- Async transform lifecycle (internal; the directive's
|
|
2418
|
-
//
|
|
2419
|
-
//
|
|
2420
|
-
//
|
|
2421
|
-
//
|
|
2422
|
-
//
|
|
2418
|
+
// --- Async transform lifecycle (internal; the directive's deferred
|
|
2419
|
+
// orchestrator is the only legitimate consumer). Thin path-bound
|
|
2420
|
+
// delegates to the store's per-path token and counter machinery,
|
|
2421
|
+
// the same pattern as `markBlank` and `setValueWithInternalPath`, so
|
|
2422
|
+
// the directive (which holds this RegisterValue and never the store)
|
|
2423
|
+
// can drive the busy / discard / error bookkeeping. ---
|
|
2423
2424
|
beginTransform: (holder) => state.beginTransform(pathKey, holder),
|
|
2424
2425
|
isCurrentTransform: (token) => state.isCurrentTransform(pathKey, token),
|
|
2425
2426
|
endTransform: (token) => state.endTransform(pathKey, token),
|
|
@@ -2432,10 +2433,10 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2432
2433
|
return (state.fieldTransformCounts.get(pathKey) ?? 0) > 0;
|
|
2433
2434
|
},
|
|
2434
2435
|
path: pathKey,
|
|
2435
|
-
// Frozen so a wrapper component can pass `rv.segments`
|
|
2436
|
-
//
|
|
2437
|
-
//
|
|
2438
|
-
//
|
|
2436
|
+
// Frozen so a wrapper component can pass `rv.segments` straight to
|
|
2437
|
+
// `form.fields(...)` with no defensive copy, and so neither a test
|
|
2438
|
+
// fixture nor downstream code can mutate the canonical segment list
|
|
2439
|
+
// out from under the directive.
|
|
2439
2440
|
segments: Object.freeze(segments.slice()),
|
|
2440
2441
|
formKey: state.formKey,
|
|
2441
2442
|
formInstanceId,
|
|
@@ -2458,25 +2459,24 @@ function isUnset(value) {
|
|
|
2458
2459
|
return value === unset;
|
|
2459
2460
|
}
|
|
2460
2461
|
|
|
2461
|
-
function walkUnsetSentinels(values, schema) {
|
|
2462
|
+
function walkUnsetSentinels(values, schema, rootDefault) {
|
|
2462
2463
|
const paths = [];
|
|
2463
2464
|
if (values === void 0) {
|
|
2464
|
-
|
|
2465
|
-
walkUnspecified(rootSlim, [], paths);
|
|
2465
|
+
walkUnspecified(rootDefault, [], paths);
|
|
2466
2466
|
return { cleanedValues: void 0, paths };
|
|
2467
2467
|
}
|
|
2468
|
-
const cleaned = walkCore(values, [], schema, paths,
|
|
2468
|
+
const cleaned = walkCore(values, [], schema, paths, { rootDefault });
|
|
2469
2469
|
return { cleanedValues: cleaned, paths };
|
|
2470
2470
|
}
|
|
2471
2471
|
function isRecursable(value) {
|
|
2472
2472
|
return Array.isArray(value) || isPlainRecord(value);
|
|
2473
2473
|
}
|
|
2474
|
-
function walkCore(input, segments, schema, paths,
|
|
2474
|
+
function walkCore(input, segments, schema, paths, synthesis) {
|
|
2475
2475
|
if (isUnset(input)) {
|
|
2476
2476
|
return expandUnsetAt(segments, schema, paths);
|
|
2477
2477
|
}
|
|
2478
2478
|
if (input === void 0) {
|
|
2479
|
-
if (
|
|
2479
|
+
if (synthesis !== null) {
|
|
2480
2480
|
const slim = schema.getDefaultAtPath(segments);
|
|
2481
2481
|
return walkUnspecified(slim, segments, paths);
|
|
2482
2482
|
}
|
|
@@ -2489,7 +2489,7 @@ function walkCore(input, segments, schema, paths, synthesizeSchemaKeys) {
|
|
|
2489
2489
|
let mutated = false;
|
|
2490
2490
|
for (let i = 0; i < input.length; i++) {
|
|
2491
2491
|
const original = readConsumerIndex(input, i);
|
|
2492
|
-
const walked = walkCore(original, [...segments, i], schema, paths,
|
|
2492
|
+
const walked = walkCore(original, [...segments, i], schema, paths, synthesis);
|
|
2493
2493
|
out[i] = walked;
|
|
2494
2494
|
if (walked !== original) mutated = true;
|
|
2495
2495
|
}
|
|
@@ -2501,10 +2501,10 @@ function walkCore(input, segments, schema, paths, synthesizeSchemaKeys) {
|
|
|
2501
2501
|
let keys = inputKeys;
|
|
2502
2502
|
let mutated = false;
|
|
2503
2503
|
let inputKeysSet = null;
|
|
2504
|
-
if (
|
|
2504
|
+
if (synthesis !== null) {
|
|
2505
2505
|
inputKeysSet = new Set(inputKeys);
|
|
2506
2506
|
const allKeys = new Set(inputKeys);
|
|
2507
|
-
const slim = schema.getDefaultAtPath(segments);
|
|
2507
|
+
const slim = segments.length === 0 ? synthesis.rootDefault : schema.getDefaultAtPath(segments);
|
|
2508
2508
|
if (isPlainRecord(slim)) {
|
|
2509
2509
|
for (const k of Object.keys(slim)) allKeys.add(k);
|
|
2510
2510
|
}
|
|
@@ -2514,12 +2514,12 @@ function walkCore(input, segments, schema, paths, synthesizeSchemaKeys) {
|
|
|
2514
2514
|
const out = {};
|
|
2515
2515
|
for (const key of keys) {
|
|
2516
2516
|
const orig = readConsumerProp(obj, key);
|
|
2517
|
-
if (
|
|
2517
|
+
if (synthesis !== null && orig === void 0 && inputKeysSet?.has(key) === true) {
|
|
2518
2518
|
safeAssign(out, key, void 0);
|
|
2519
2519
|
mutated = true;
|
|
2520
2520
|
continue;
|
|
2521
2521
|
}
|
|
2522
|
-
const walked = walkCore(orig, [...segments, key], schema, paths,
|
|
2522
|
+
const walked = walkCore(orig, [...segments, key], schema, paths, synthesis);
|
|
2523
2523
|
safeAssign(out, key, walked);
|
|
2524
2524
|
if (walked !== orig) mutated = true;
|
|
2525
2525
|
}
|
|
@@ -2543,7 +2543,7 @@ function walkUnspecified(slim, segments, paths) {
|
|
|
2543
2543
|
}
|
|
2544
2544
|
function substituteUnsetSentinels(value, prefix, schema) {
|
|
2545
2545
|
const paths = [];
|
|
2546
|
-
const cleaned = walkCore(value, [...prefix], schema, paths,
|
|
2546
|
+
const cleaned = walkCore(value, [...prefix], schema, paths, null);
|
|
2547
2547
|
return { cleanedValues: cleaned, paths };
|
|
2548
2548
|
}
|
|
2549
2549
|
function isPrimitiveOrEmpty(value) {
|
|
@@ -2641,15 +2641,15 @@ function buildMetaProxy(keys, read) {
|
|
|
2641
2641
|
return new Proxy(target, {
|
|
2642
2642
|
get: (_, key) => owns.has(key) ? read(key) : Reflect.get(target, key),
|
|
2643
2643
|
has: (_, key) => owns.has(key) || Reflect.has(target, key),
|
|
2644
|
-
// Safe to hand back the same array every time: the spec copies a
|
|
2645
|
-
//
|
|
2644
|
+
// Safe to hand back the same array every time: the spec copies a trap's
|
|
2645
|
+
// key list before using it.
|
|
2646
2646
|
ownKeys: () => keys,
|
|
2647
2647
|
getOwnPropertyDescriptor(_, key) {
|
|
2648
2648
|
if (!owns.has(key)) return Reflect.getOwnPropertyDescriptor(target, key);
|
|
2649
2649
|
return { configurable: true, enumerable: true, value: read(key), writable: false };
|
|
2650
2650
|
},
|
|
2651
|
-
// A getter with no setter throws on assignment under strict mode,
|
|
2652
|
-
//
|
|
2651
|
+
// A getter with no setter throws on assignment under strict mode, which
|
|
2652
|
+
// every module here is; `false` keeps that.
|
|
2653
2653
|
set: denyWrite,
|
|
2654
2654
|
deleteProperty: denyWrite,
|
|
2655
2655
|
defineProperty: denyWrite
|
|
@@ -2708,7 +2708,18 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2708
2708
|
void state.submitted.value;
|
|
2709
2709
|
return metaBase;
|
|
2710
2710
|
};
|
|
2711
|
-
const
|
|
2711
|
+
const releases = [];
|
|
2712
|
+
const lease = (release) => {
|
|
2713
|
+
releases.push(release);
|
|
2714
|
+
return release;
|
|
2715
|
+
};
|
|
2716
|
+
onScopeDispose(() => {
|
|
2717
|
+
for (const release of releases) release();
|
|
2718
|
+
}, true);
|
|
2719
|
+
const pathSweep = {
|
|
2720
|
+
track: state.pathSweep.track,
|
|
2721
|
+
onEvict: (evict) => lease(state.pathSweep.onEvict(evict))
|
|
2722
|
+
};
|
|
2712
2723
|
const getRootFieldStateAt = buildFieldStateAccessor(
|
|
2713
2724
|
state,
|
|
2714
2725
|
formInstanceId,
|
|
@@ -2766,7 +2777,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2766
2777
|
const next = typeof pathOrValue === "function" ? pathOrValue(structuralSnapshot(state.form.value)) : pathOrValue;
|
|
2767
2778
|
const walked2 = walkUnsetSentinels(
|
|
2768
2779
|
next,
|
|
2769
|
-
state.schema
|
|
2780
|
+
state.schema,
|
|
2781
|
+
state.schema.getDefaultAtPath([])
|
|
2770
2782
|
);
|
|
2771
2783
|
const ok2 = state.setValueAtPath([], walked2.cleanedValues, writeMeta());
|
|
2772
2784
|
if (!ok2) return false;
|
|
@@ -2831,7 +2843,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2831
2843
|
const errorsProxy = buildErrorsSurface(state, pathSweep);
|
|
2832
2844
|
function flattenUserErrors() {
|
|
2833
2845
|
const all = [];
|
|
2834
|
-
for (const cell of state.errorCells.values()) all.push(...cell.user);
|
|
2846
|
+
for (const cell of state.errorCells().values()) all.push(...cell.user);
|
|
2835
2847
|
return all;
|
|
2836
2848
|
}
|
|
2837
2849
|
function setErrors(arg1, arg2) {
|
|
@@ -2839,7 +2851,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2839
2851
|
if (isScoped) {
|
|
2840
2852
|
const { segments, key } = canonicalizePath(arg1);
|
|
2841
2853
|
const input2 = arg2;
|
|
2842
|
-
const resolved2 = typeof input2 === "function" ? input2([...state.errorCells.get(key)?.user ?? []]) : input2;
|
|
2854
|
+
const resolved2 = typeof input2 === "function" ? input2([...state.errorCells().get(key)?.user ?? []]) : input2;
|
|
2843
2855
|
state.setUserErrorsForPath(
|
|
2844
2856
|
segments,
|
|
2845
2857
|
normalizeErrorInputs(resolved2, segments, AttaformErrorCode.UserError)
|
|
@@ -2867,7 +2879,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2867
2879
|
const departAttempts = computed(() => state.departAttempts.value);
|
|
2868
2880
|
const validating = computed(() => state.activeValidations.value > 0);
|
|
2869
2881
|
const valid = computed(
|
|
2870
|
-
() => state.firstValidationDone.value && state.errorCells.size === 0 && state.derivedBlankErrors.value.size === 0 && !validating.value
|
|
2882
|
+
() => state.firstValidationDone.value && state.errorCells().size === 0 && state.derivedBlankErrors.value.size === 0 && !validating.value
|
|
2871
2883
|
);
|
|
2872
2884
|
const history = options.history;
|
|
2873
2885
|
const formHistory = history === void 0 ? inertHistory() : readonly(
|
|
@@ -2885,12 +2897,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2885
2897
|
);
|
|
2886
2898
|
const rootFieldState = getRootFieldStateAt([]);
|
|
2887
2899
|
const metaOwn = {
|
|
2888
|
-
// Whole-form work signals compose the LIFECYCLE counters with the
|
|
2889
|
-
//
|
|
2890
|
-
//
|
|
2891
|
-
//
|
|
2892
|
-
//
|
|
2893
|
-
// unfiltered whole-form aggregate.
|
|
2900
|
+
// Whole-form work signals compose the LIFECYCLE counters with the per-leaf
|
|
2901
|
+
// rollup: a submit-time validate shows up in `activeValidations` and a
|
|
2902
|
+
// per-field debounced validator in `fieldValidationCounts`, and either
|
|
2903
|
+
// flips the flag. `valid` keeps the form-level mount gate, and `errors` is
|
|
2904
|
+
// the unfiltered whole-form aggregate.
|
|
2894
2905
|
validating: computed(
|
|
2895
2906
|
() => state.activeValidations.value > 0 || rootFieldState.value.validating
|
|
2896
2907
|
),
|
|
@@ -2902,7 +2913,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2902
2913
|
busy: computed(
|
|
2903
2914
|
() => state.activeValidations.value > 0 || state.activeTransforms.value > 0 || rootFieldState.value.validating || rootFieldState.value.transforming
|
|
2904
2915
|
),
|
|
2905
|
-
// Lifecycle
|
|
2916
|
+
// Lifecycle, form-level only and never on FieldState.
|
|
2906
2917
|
submitting,
|
|
2907
2918
|
submissionAttempts,
|
|
2908
2919
|
departAttempts,
|
|
@@ -2984,7 +2995,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2984
2995
|
};
|
|
2985
2996
|
return Object.freeze(view);
|
|
2986
2997
|
});
|
|
2987
|
-
const valuesProxy = buildValuesSurface(
|
|
2998
|
+
const valuesProxy = buildValuesSurface(
|
|
2999
|
+
state.form,
|
|
3000
|
+
(listener) => lease(state.onFormChange(listener))
|
|
3001
|
+
);
|
|
2988
3002
|
const fieldStateProxy = buildFieldsSurface(state, getRootFieldStateAt, pathSweep);
|
|
2989
3003
|
const needsLazyGate = state.defaultValuesFactory.value !== void 0 || state.hasSsrPrefetch;
|
|
2990
3004
|
function gated(fn) {
|
|
@@ -3019,10 +3033,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3019
3033
|
}
|
|
3020
3034
|
const api = {
|
|
3021
3035
|
handleSubmit: gated(handleSubmit),
|
|
3022
|
-
//
|
|
3023
|
-
// reactive containers (`meta`, `history`, `blankPaths`) are exposed
|
|
3024
|
-
//
|
|
3025
|
-
//
|
|
3036
|
+
// The callable readonly Proxies (`values`, `fields`, `errors`) and the
|
|
3037
|
+
// reactive containers (`meta`, `history`, `blankPaths`) are exposed through
|
|
3038
|
+
// getters, so reading one activates the form on first touch. Each
|
|
3039
|
+
// underlying object is identity-stable across reads.
|
|
3026
3040
|
get values() {
|
|
3027
3041
|
void state.activate();
|
|
3028
3042
|
return valuesProxy;
|
|
@@ -3039,7 +3053,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3039
3053
|
key: state.formKey,
|
|
3040
3054
|
// Auto-unwrapping views over the per-store async-defaults lifecycle
|
|
3041
3055
|
// refs (see FormStore.hydrating / hydrateError). Reading either
|
|
3042
|
-
// activates the form
|
|
3056
|
+
// activates the form: observing factory state implies use.
|
|
3043
3057
|
get hydrating() {
|
|
3044
3058
|
void state.activate();
|
|
3045
3059
|
return state.hydrating.value;
|
|
@@ -3048,20 +3062,18 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3048
3062
|
void state.activate();
|
|
3049
3063
|
return state.hydrateError.value;
|
|
3050
3064
|
},
|
|
3051
|
-
// Orthogonal to `hydrating` and `hydrateError
|
|
3052
|
-
//
|
|
3053
|
-
// resolved
|
|
3054
|
-
// refetches even when those
|
|
3055
|
-
//
|
|
3056
|
-
// `hydrateError` surfaces the refresh failure.
|
|
3065
|
+
// Orthogonal to `hydrating` and `hydrateError`. `ready` flips true once
|
|
3066
|
+
// defaults are applied, whether sync at construction or from a factory
|
|
3067
|
+
// that resolved, and it is a one-way latch: it stays true through later
|
|
3068
|
+
// refetches even when those fail, so a stale-while-revalidate UI keeps
|
|
3069
|
+
// rendering the prior values while `hydrateError` surfaces the failure.
|
|
3057
3070
|
get ready() {
|
|
3058
3071
|
void state.activate();
|
|
3059
3072
|
return state.defaultsResolved.value;
|
|
3060
3073
|
},
|
|
3061
|
-
// `rehydrate` and `activate` are
|
|
3062
|
-
//
|
|
3063
|
-
//
|
|
3064
|
-
// so they call `state` directly.
|
|
3074
|
+
// `rehydrate` and `activate` are activation entry points themselves and
|
|
3075
|
+
// fire the factory by design, so wrapping them in `gated` would
|
|
3076
|
+
// double-fire. They call `state` directly.
|
|
3065
3077
|
rehydrate: () => state.rehydrate(),
|
|
3066
3078
|
activate: () => state.activate(),
|
|
3067
3079
|
get errors() {
|
|
@@ -3134,7 +3146,7 @@ function shortenSourceFrame(frame) {
|
|
|
3134
3146
|
const SWEEP_SLICE = 8;
|
|
3135
3147
|
function createDynamicPathSweep(deps) {
|
|
3136
3148
|
const dynamicKeys = /* @__PURE__ */ new Set();
|
|
3137
|
-
const evictors =
|
|
3149
|
+
const evictors = /* @__PURE__ */ new Set();
|
|
3138
3150
|
const isDynamicPath = (segments) => {
|
|
3139
3151
|
for (let i = 0; i < segments.length; i++) {
|
|
3140
3152
|
if (typeof segments[i] === "number") return true;
|
|
@@ -3165,7 +3177,10 @@ function createDynamicPathSweep(deps) {
|
|
|
3165
3177
|
if (isDynamicPath(segments)) dynamicKeys.add(key);
|
|
3166
3178
|
},
|
|
3167
3179
|
onEvict(evict) {
|
|
3168
|
-
evictors.
|
|
3180
|
+
evictors.add(evict);
|
|
3181
|
+
return () => {
|
|
3182
|
+
evictors.delete(evict);
|
|
3183
|
+
};
|
|
3169
3184
|
}
|
|
3170
3185
|
};
|
|
3171
3186
|
}
|
|
@@ -3369,7 +3384,7 @@ function mergeObjectKeys(target, source, path, schema) {
|
|
|
3369
3384
|
async function runFactoryAndApply(st, factory) {
|
|
3370
3385
|
try {
|
|
3371
3386
|
const value = await factory();
|
|
3372
|
-
walkAuthoredFromConstraints(value, [], st.authoredPaths);
|
|
3387
|
+
if (st.hasPreprocessOrCoerce) walkAuthoredFromConstraints(value, [], st.authoredPaths);
|
|
3373
3388
|
const full = mergeSparseHydration(
|
|
3374
3389
|
toRaw(st.form.value),
|
|
3375
3390
|
value,
|
|
@@ -3393,7 +3408,7 @@ async function runFactoryAndApply(st, factory) {
|
|
|
3393
3408
|
}
|
|
3394
3409
|
}
|
|
3395
3410
|
function clearHydrationFailedEntry(st) {
|
|
3396
|
-
const existing = st.errorCells.get(ROOT_PATH_KEY)?.schema;
|
|
3411
|
+
const existing = st.errorCells().get(ROOT_PATH_KEY)?.schema;
|
|
3397
3412
|
if (existing === void 0 || existing.length === 0) return;
|
|
3398
3413
|
const filtered = existing.filter((e) => e.code !== AttaformErrorCode.HydrationFailed);
|
|
3399
3414
|
if (filtered.length !== existing.length) {
|
|
@@ -3407,7 +3422,7 @@ function appendHydrationFailedEntry(st, error) {
|
|
|
3407
3422
|
path: [...ROOT_PATH],
|
|
3408
3423
|
code: AttaformErrorCode.HydrationFailed
|
|
3409
3424
|
};
|
|
3410
|
-
const existing = st.errorCells.get(ROOT_PATH_KEY)?.schema ?? [];
|
|
3425
|
+
const existing = st.errorCells().get(ROOT_PATH_KEY)?.schema ?? [];
|
|
3411
3426
|
st.setSchemaErrorsForPath([...ROOT_PATH], [...existing, entry]);
|
|
3412
3427
|
return entry;
|
|
3413
3428
|
}
|
|
@@ -3541,23 +3556,24 @@ function walkAuthoredFromSchemaDiff(withDefaults, withoutDefaults, prefix, out)
|
|
|
3541
3556
|
out.add(canonicalizePath(prefix).key);
|
|
3542
3557
|
}
|
|
3543
3558
|
}
|
|
3544
|
-
function
|
|
3545
|
-
|
|
3559
|
+
function computeBaseline(schema, source, schemaDefaults) {
|
|
3560
|
+
if (source === void 0) return schemaDefaults;
|
|
3546
3561
|
return schema.getDefaultValues({
|
|
3547
3562
|
useDefaultSchemaValues: true,
|
|
3548
|
-
constraints:
|
|
3563
|
+
constraints: mergeStructural(schema, [], source, schemaDefaults)
|
|
3549
3564
|
});
|
|
3550
3565
|
}
|
|
3551
3566
|
function initialFirstValidationGate(schema) {
|
|
3552
3567
|
return schema.needsAsyncValidation?.() !== true;
|
|
3553
3568
|
}
|
|
3554
|
-
function seedOriginalsFromBaseline(st, baseline
|
|
3569
|
+
function seedOriginalsFromBaseline(st, baseline) {
|
|
3555
3570
|
st.originals.clear();
|
|
3556
3571
|
diffAndApply({}, baseline, [], (patch) => {
|
|
3557
3572
|
if (patch.kind !== "added") return;
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3573
|
+
st.originals.set(canonicalizePath(patch.path).key, {
|
|
3574
|
+
segments: patch.path,
|
|
3575
|
+
value: patch.newValue
|
|
3576
|
+
});
|
|
3561
3577
|
});
|
|
3562
3578
|
}
|
|
3563
3579
|
function queueInitialAsyncValidation(st) {
|
|
@@ -3571,6 +3587,7 @@ function queueInitialAsyncValidation(st) {
|
|
|
3571
3587
|
}
|
|
3572
3588
|
}
|
|
3573
3589
|
function rebuildAuthoredPaths(st, constraints, schemaWithDefaultsData) {
|
|
3590
|
+
if (!st.hasPreprocessOrCoerce) return;
|
|
3574
3591
|
st.authoredPaths.clear();
|
|
3575
3592
|
if (constraints !== void 0) {
|
|
3576
3593
|
walkAuthoredFromConstraints(constraints, [], st.authoredPaths);
|
|
@@ -3604,6 +3621,7 @@ function ensurePathOrdinal(st, key) {
|
|
|
3604
3621
|
return ordinal;
|
|
3605
3622
|
}
|
|
3606
3623
|
function pathHasAsyncValidationByKey(st, key, segments) {
|
|
3624
|
+
if (st.schema.needsAsyncValidation?.() === false) return false;
|
|
3607
3625
|
const cached = st.pathAsyncCache.get(key);
|
|
3608
3626
|
if (cached !== void 0) return cached;
|
|
3609
3627
|
const candidates = st.schema.getSchemasAtPath(segments);
|
|
@@ -3724,19 +3742,16 @@ function touchFieldRecord(st, pathKey, path, patch) {
|
|
|
3724
3742
|
path,
|
|
3725
3743
|
updatedAt: patch.updatedAt ?? current?.updatedAt ?? null,
|
|
3726
3744
|
connected: patch.connected ?? current?.connected ?? false,
|
|
3727
|
-
// focused/blurred
|
|
3728
|
-
//
|
|
3729
|
-
//
|
|
3730
|
-
// `current`, losing the intent. `!== undefined` honours an
|
|
3731
|
-
// explicit null and preserves current only on absence.
|
|
3745
|
+
// `focused` / `blurred` need an explicit-undefined guard: a patch
|
|
3746
|
+
// legitimately carries `null` to mark a disconnect, and `??` would
|
|
3747
|
+
// short-circuit on it and fall through to `current`, losing the intent.
|
|
3732
3748
|
focused: patch.focused !== void 0 ? patch.focused : current?.focused ?? null,
|
|
3733
3749
|
blurred: patch.blurred !== void 0 ? patch.blurred : current?.blurred ?? null,
|
|
3734
|
-
// touched is plain
|
|
3735
|
-
// guard here because `false` is not nullish.
|
|
3750
|
+
// `touched` is a plain boolean, so `??` is equivalent to the guard above.
|
|
3736
3751
|
touched: patch.touched ?? current?.touched ?? false,
|
|
3737
|
-
// interacted is sticky-true
|
|
3738
|
-
//
|
|
3739
|
-
//
|
|
3752
|
+
// `interacted` is sticky-true and a merge patch only ever sets it, so `??`
|
|
3753
|
+
// preserves the current bit. Only the reset paths clear it, and they
|
|
3754
|
+
// reconstruct the record outright.
|
|
3740
3755
|
interacted: patch.interacted ?? current?.interacted ?? false,
|
|
3741
3756
|
blurredAfterInteraction: patch.blurredAfterInteraction ?? current?.blurredAfterInteraction ?? false
|
|
3742
3757
|
});
|
|
@@ -3941,16 +3956,19 @@ function setValueAtPath(st, path, value, meta) {
|
|
|
3941
3956
|
}
|
|
3942
3957
|
}
|
|
3943
3958
|
}
|
|
3944
|
-
|
|
3945
|
-
if (
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3959
|
+
let newlyAuthored = false;
|
|
3960
|
+
if (st.hasPreprocessOrCoerce) {
|
|
3961
|
+
const wasAuthoredBefore = st.authoredPaths.has(pathKey);
|
|
3962
|
+
if (arrayOpRemap !== null && Array.isArray(value)) {
|
|
3963
|
+
if (path.length > 0) st.authoredPaths.add(pathKey);
|
|
3964
|
+
for (const idx of arrayOpRemap.fresh) {
|
|
3965
|
+
walkAuthoredFromConstraints(value[idx], [...path, idx], st.authoredPaths);
|
|
3966
|
+
}
|
|
3967
|
+
} else {
|
|
3968
|
+
walkAuthoredFromConstraints(value, path, st.authoredPaths);
|
|
3949
3969
|
}
|
|
3950
|
-
|
|
3951
|
-
walkAuthoredFromConstraints(value, path, st.authoredPaths);
|
|
3970
|
+
newlyAuthored = !wasAuthoredBefore && st.authoredPaths.has(pathKey);
|
|
3952
3971
|
}
|
|
3953
|
-
const newlyAuthored = !wasAuthoredBefore && st.authoredPaths.has(pathKey);
|
|
3954
3972
|
let completedValue;
|
|
3955
3973
|
if (arrayOpRemap !== null && Array.isArray(value)) {
|
|
3956
3974
|
for (const idx of arrayOpRemap.fresh) {
|
|
@@ -4207,22 +4225,38 @@ function getValueAtPath(st, path) {
|
|
|
4207
4225
|
return getAtPath(st.form.value, path);
|
|
4208
4226
|
}
|
|
4209
4227
|
const ERROR_SOURCES = ["schema", "user"];
|
|
4228
|
+
function createErrorCells(schema, pristine) {
|
|
4229
|
+
const raw = /* @__PURE__ */ new Map();
|
|
4230
|
+
const cells = shallowReactive(raw);
|
|
4231
|
+
return (seed) => {
|
|
4232
|
+
const data = pristine;
|
|
4233
|
+
if (data !== void 0) {
|
|
4234
|
+
pristine = void 0;
|
|
4235
|
+
if (seed) {
|
|
4236
|
+
for (const [key, list] of groupErrorsByKey(schema.validateSync(data))) {
|
|
4237
|
+
raw.set(key, { schema: list, user: NO_ERRORS });
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4241
|
+
return cells;
|
|
4242
|
+
};
|
|
4243
|
+
}
|
|
4210
4244
|
function setErrorChannelForKey(st, key, src, entries) {
|
|
4211
|
-
const current = st.errorCells.get(key);
|
|
4245
|
+
const current = st.errorCells().get(key);
|
|
4212
4246
|
const schema = src === "schema" ? entries : current?.schema ?? NO_ERRORS;
|
|
4213
4247
|
const user = src === "user" ? entries : current?.user ?? NO_ERRORS;
|
|
4214
4248
|
if (schema.length === 0 && user.length === 0) {
|
|
4215
|
-
if (current !== void 0) st.errorCells.delete(key);
|
|
4249
|
+
if (current !== void 0) st.errorCells().delete(key);
|
|
4216
4250
|
return;
|
|
4217
4251
|
}
|
|
4218
|
-
st.errorCells.set(key, { schema, user });
|
|
4252
|
+
st.errorCells().set(key, { schema, user });
|
|
4219
4253
|
}
|
|
4220
4254
|
function replaceErrorChannel(st, src, entries) {
|
|
4221
4255
|
const other = src === "schema" ? "user" : "schema";
|
|
4222
4256
|
const grouped = groupErrorsByKey(entries);
|
|
4223
|
-
for (const [key, cell] of st.errorCells) {
|
|
4257
|
+
for (const [key, cell] of st.errorCells()) {
|
|
4224
4258
|
if (cell[other].length === 0) {
|
|
4225
|
-
st.errorCells.delete(key);
|
|
4259
|
+
st.errorCells().delete(key);
|
|
4226
4260
|
continue;
|
|
4227
4261
|
}
|
|
4228
4262
|
const fresh = grouped.get(key);
|
|
@@ -4247,8 +4281,9 @@ function clearErrorChannel(st, src, path) {
|
|
|
4247
4281
|
function applySchemaErrorsForSubtree(st, path, entries) {
|
|
4248
4282
|
const parentKey = canonicalizePath(path).key;
|
|
4249
4283
|
const grouped = groupErrorsByKey(entries);
|
|
4284
|
+
if (path.length === 0) st.skipErrorSeed();
|
|
4250
4285
|
if (!grouped.has(parentKey)) setErrorChannelForKey(st, parentKey, "schema", NO_ERRORS);
|
|
4251
|
-
for (const [existingKey, cell] of st.errorCells) {
|
|
4286
|
+
for (const [existingKey, cell] of st.errorCells()) {
|
|
4252
4287
|
if (existingKey === parentKey) continue;
|
|
4253
4288
|
if (cell.schema.length === 0) continue;
|
|
4254
4289
|
if (isPathKeyUnder(existingKey, path) && !grouped.has(existingKey)) {
|
|
@@ -4260,15 +4295,15 @@ function applySchemaErrorsForSubtree(st, path, entries) {
|
|
|
4260
4295
|
}
|
|
4261
4296
|
}
|
|
4262
4297
|
function restoreErrorCells(st, entries) {
|
|
4263
|
-
st.
|
|
4298
|
+
st.skipErrorSeed().clear();
|
|
4264
4299
|
for (const [key, cell] of entries) {
|
|
4265
4300
|
if (cell.schema.length === 0 && cell.user.length === 0) continue;
|
|
4266
|
-
st.errorCells.set(key, { schema: [...cell.schema], user: [...cell.user] });
|
|
4301
|
+
st.errorCells().set(key, { schema: [...cell.schema], user: [...cell.user] });
|
|
4267
4302
|
}
|
|
4268
4303
|
}
|
|
4269
4304
|
function getErrorsForPath(st, path) {
|
|
4270
4305
|
const { key } = canonicalizePath(path);
|
|
4271
|
-
const cell = st.errorCells.get(key);
|
|
4306
|
+
const cell = st.errorCells().get(key);
|
|
4272
4307
|
const blankForKey = st.derivedBlankErrors.value.get(key);
|
|
4273
4308
|
if (cell === void 0 && blankForKey === void 0) {
|
|
4274
4309
|
return [];
|
|
@@ -4305,13 +4340,13 @@ function markFocused(st, path, focused, meta) {
|
|
|
4305
4340
|
touchFieldRecord(st, key, path, {
|
|
4306
4341
|
focused,
|
|
4307
4342
|
blurred: !focused,
|
|
4308
|
-
// `touched` flips
|
|
4309
|
-
//
|
|
4343
|
+
// `touched` flips true on blur and stays; a focused field keeps whatever
|
|
4344
|
+
// it held.
|
|
4310
4345
|
touched: focused ? current?.touched ?? false : true,
|
|
4311
|
-
// `blurredAfterInteraction` flips true on the first blur
|
|
4312
|
-
//
|
|
4313
|
-
//
|
|
4314
|
-
//
|
|
4346
|
+
// `blurredAfterInteraction` flips true on the first blur after a value
|
|
4347
|
+
// edit and stays. A tab-through blur before any edit leaves it false,
|
|
4348
|
+
// since `interacted` is still false, which is what keeps a clean
|
|
4349
|
+
// tab-through from arming the gate.
|
|
4315
4350
|
blurredAfterInteraction: !focused && current?.interacted === true ? true : current?.blurredAfterInteraction ?? false
|
|
4316
4351
|
});
|
|
4317
4352
|
const focusMode = meta?.instance?.validateOn ?? st.fieldValidationMode;
|
|
@@ -4430,7 +4465,14 @@ function adoptResolvedDefaults(st, value) {
|
|
|
4430
4465
|
st.schema
|
|
4431
4466
|
)
|
|
4432
4467
|
);
|
|
4433
|
-
seedOriginalsFromBaseline(
|
|
4468
|
+
seedOriginalsFromBaseline(
|
|
4469
|
+
st,
|
|
4470
|
+
computeBaseline(
|
|
4471
|
+
st.schema,
|
|
4472
|
+
st.defaultValues,
|
|
4473
|
+
st.schema.getDefaultValues({ useDefaultSchemaValues: true })
|
|
4474
|
+
)
|
|
4475
|
+
);
|
|
4434
4476
|
}
|
|
4435
4477
|
function reset(st, nextDefaultValues) {
|
|
4436
4478
|
const resetSource = nextDefaultValues === void 0 ? st.defaultValues : mergeSparseHydration(
|
|
@@ -4439,13 +4481,16 @@ function reset(st, nextDefaultValues) {
|
|
|
4439
4481
|
st.schema
|
|
4440
4482
|
);
|
|
4441
4483
|
st.defaultValues = structuralSnapshot(resetSource);
|
|
4442
|
-
const
|
|
4443
|
-
|
|
4484
|
+
const next = computeBaseline(
|
|
4485
|
+
st.schema,
|
|
4486
|
+
resetSource,
|
|
4487
|
+
st.schema.getDefaultValues({ useDefaultSchemaValues: true })
|
|
4488
|
+
);
|
|
4444
4489
|
rebuildAuthoredPaths(st, resetSource, next);
|
|
4445
4490
|
applyFormReplacement(st, next);
|
|
4446
4491
|
st.arrayIdentity.rebaselineAll();
|
|
4447
4492
|
st.removedSubtrees.clear();
|
|
4448
|
-
seedOriginalsFromBaseline(st, next
|
|
4493
|
+
seedOriginalsFromBaseline(st, next);
|
|
4449
4494
|
if (nextDefaultValues !== void 0) {
|
|
4450
4495
|
const mentioned = /* @__PURE__ */ new Set();
|
|
4451
4496
|
walkAuthoredFromConstraints(nextDefaultValues, [], mentioned);
|
|
@@ -4455,14 +4500,8 @@ function reset(st, nextDefaultValues) {
|
|
|
4455
4500
|
for (const key of st.originalBlankPaths) {
|
|
4456
4501
|
st.blankPaths.add(key);
|
|
4457
4502
|
}
|
|
4458
|
-
st.
|
|
4459
|
-
|
|
4460
|
-
replaceErrorChannel(st, "schema", resetResponse.errors);
|
|
4461
|
-
}
|
|
4462
|
-
const syncResult = st.schema.validateAtPath(st.form.value, void 0, { sync: true });
|
|
4463
|
-
if (!(syncResult instanceof Promise) && !syncResult.success) {
|
|
4464
|
-
applySchemaErrorsForSubtree(st, [], syncResult.errors);
|
|
4465
|
-
}
|
|
4503
|
+
st.skipErrorSeed().clear();
|
|
4504
|
+
replaceErrorChannel(st, "schema", st.schema.validateSync(next));
|
|
4466
4505
|
st.firstValidationDone.value = initialFirstValidationGate(st.schema);
|
|
4467
4506
|
queueInitialAsyncValidation(st);
|
|
4468
4507
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4507,7 +4546,7 @@ function resetField(st, path) {
|
|
|
4507
4546
|
const wrote = setValueAtPath(st, targetSegments, leafEntry.value);
|
|
4508
4547
|
if (!wrote) {
|
|
4509
4548
|
console.error(
|
|
4510
|
-
`[attaform] resetField: leaf write rejected for path '${targetKey}'
|
|
4549
|
+
`[attaform] resetField: leaf write rejected for path '${targetKey}'. Originals contain a value that doesn't satisfy the slim primitive shape. This is a bug in the construction pipeline.`
|
|
4511
4550
|
);
|
|
4512
4551
|
}
|
|
4513
4552
|
} else {
|
|
@@ -4528,7 +4567,7 @@ function resetField(st, path) {
|
|
|
4528
4567
|
const wroteSubtree = setValueAtPath(st, targetSegments, subtree);
|
|
4529
4568
|
if (!wroteSubtree) {
|
|
4530
4569
|
console.error(
|
|
4531
|
-
`[attaform] resetField: subtree write rejected at path '${targetKey}'
|
|
4570
|
+
`[attaform] resetField: subtree write rejected at path '${targetKey}'. Originals contain values that don't satisfy the slim primitive shape. This is a bug in the construction pipeline.`
|
|
4532
4571
|
);
|
|
4533
4572
|
}
|
|
4534
4573
|
}
|
|
@@ -4539,7 +4578,7 @@ function resetField(st, path) {
|
|
|
4539
4578
|
}
|
|
4540
4579
|
}
|
|
4541
4580
|
function deleteErrorCellsUnderPrefix(st, prefix) {
|
|
4542
|
-
for (const [errorKey, cell] of st.errorCells) {
|
|
4581
|
+
for (const [errorKey, cell] of st.errorCells()) {
|
|
4543
4582
|
for (const src of ERROR_SOURCES) {
|
|
4544
4583
|
const first = cell[src][0];
|
|
4545
4584
|
if (first !== void 0 && isPathPrefix(prefix, first.path)) {
|
|
@@ -4604,8 +4643,8 @@ function createFormStore(options) {
|
|
|
4604
4643
|
const fieldValidatingSince = reactive(/* @__PURE__ */ new Map());
|
|
4605
4644
|
const displayEngine = createDisplayEngine(ssr);
|
|
4606
4645
|
cleanupHooks.push(() => displayEngine.dispose());
|
|
4607
|
-
const
|
|
4608
|
-
const schemaInitialData =
|
|
4646
|
+
const ownDefaultValues = structuralSnapshot(defaultValues);
|
|
4647
|
+
const schemaInitialData = computeBaseline(schema, ownDefaultValues, options.schemaDefaults);
|
|
4609
4648
|
const authoredPaths = /* @__PURE__ */ new Set();
|
|
4610
4649
|
const initialData = hydration !== void 0 ? hydration.form : structuralSnapshot(schemaInitialData);
|
|
4611
4650
|
const hasDU = schema.hasDiscriminatedUnions?.() !== false;
|
|
@@ -4619,7 +4658,11 @@ function createFormStore(options) {
|
|
|
4619
4658
|
});
|
|
4620
4659
|
const fields = shallowReactive(/* @__PURE__ */ new Map());
|
|
4621
4660
|
const domBinding = shallowRef(null);
|
|
4622
|
-
const
|
|
4661
|
+
const errorStore = createErrorCells(
|
|
4662
|
+
schema,
|
|
4663
|
+
hydration === void 0 ? schemaInitialData : void 0
|
|
4664
|
+
);
|
|
4665
|
+
const errorCells = () => errorStore(true);
|
|
4623
4666
|
const originals = shallowReactive(/* @__PURE__ */ new Map());
|
|
4624
4667
|
const removedSubtrees = /* @__PURE__ */ new Set();
|
|
4625
4668
|
const blankPaths = reactive(/* @__PURE__ */ new Set());
|
|
@@ -4643,7 +4686,7 @@ function createFormStore(options) {
|
|
|
4643
4686
|
return result;
|
|
4644
4687
|
});
|
|
4645
4688
|
const errorPathIndex = computed(
|
|
4646
|
-
() => buildErrorPathIndex(errorCells, derivedBlankErrors.value)
|
|
4689
|
+
() => buildErrorPathIndex(errorCells(), derivedBlankErrors.value)
|
|
4647
4690
|
);
|
|
4648
4691
|
const submitting = ref(false);
|
|
4649
4692
|
const activeSubmissions = ref(0);
|
|
@@ -4783,21 +4826,16 @@ function createFormStore(options) {
|
|
|
4783
4826
|
blankPaths,
|
|
4784
4827
|
originalBlankPaths,
|
|
4785
4828
|
// --- kernel-internal state ---
|
|
4786
|
-
//
|
|
4787
|
-
//
|
|
4788
|
-
//
|
|
4789
|
-
|
|
4790
|
-
// and this field is now durable state that every `reset()` reads, so
|
|
4791
|
-
// it should not depend on it. The copy also means a consumer who
|
|
4792
|
-
// keeps a reference to the literal they passed cannot mutate the
|
|
4793
|
-
// form's defaults from outside. `reset()` snapshots for a harder
|
|
4794
|
-
// reason (see there) where an alias IS reachable.
|
|
4795
|
-
defaultValues: structuralSnapshot(defaultValues),
|
|
4829
|
+
// The copy taken above, which also stops a consumer holding a reference to
|
|
4830
|
+
// the literal they passed from mutating the form's defaults from outside.
|
|
4831
|
+
// `reset()` snapshots for a harder reason, where an alias IS reachable.
|
|
4832
|
+
defaultValues: ownDefaultValues,
|
|
4796
4833
|
ssrPrefetch,
|
|
4797
4834
|
rememberVariants,
|
|
4798
4835
|
fieldValidationMode,
|
|
4799
4836
|
fieldValidationDebounceMs,
|
|
4800
4837
|
hasDU,
|
|
4838
|
+
hasPreprocessOrCoerce: schema.hasPreprocessOrCoerce?.() !== false,
|
|
4801
4839
|
fieldValidationState,
|
|
4802
4840
|
formChangeListeners,
|
|
4803
4841
|
submitSuccessListeners,
|
|
@@ -4813,6 +4851,7 @@ function createFormStore(options) {
|
|
|
4813
4851
|
transformRuns,
|
|
4814
4852
|
transformWaiters,
|
|
4815
4853
|
arrayBookkeeping,
|
|
4854
|
+
skipErrorSeed: () => errorStore(false),
|
|
4816
4855
|
nextOrdinal: 0,
|
|
4817
4856
|
scheduleEpoch: 0,
|
|
4818
4857
|
lastCommittedEpoch: 0,
|
|
@@ -4829,10 +4868,10 @@ function createFormStore(options) {
|
|
|
4829
4868
|
arrayElementKey: bind(arrayElementKey),
|
|
4830
4869
|
reset: bind(reset),
|
|
4831
4870
|
resetField: bind(resetField),
|
|
4832
|
-
// The schema/user pair below reads as a fold waiting to happen.
|
|
4833
|
-
//
|
|
4834
|
-
//
|
|
4835
|
-
//
|
|
4871
|
+
// The schema/user pair below reads as a fold waiting to happen. Folding
|
|
4872
|
+
// them into a `setErrorsForPathIn(channel)` factory measures 10 B LARGER:
|
|
4873
|
+
// gzip has already collected the rent on two adjacent copies, and the
|
|
4874
|
+
// helper adds a name the original does not need.
|
|
4836
4875
|
setSchemaErrorsForPath: (path, entries) => setErrorChannelForKey(
|
|
4837
4876
|
st,
|
|
4838
4877
|
canonicalizePath(path).key,
|
|
@@ -4880,7 +4919,25 @@ function createFormStore(options) {
|
|
|
4880
4919
|
dispose: bind(dispose)
|
|
4881
4920
|
};
|
|
4882
4921
|
rebuildAuthoredPaths(st, defaultValues, schemaInitialData);
|
|
4883
|
-
|
|
4922
|
+
const initStamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
4923
|
+
diffAndApply({}, schemaInitialData, [], (patch) => {
|
|
4924
|
+
if (patch.kind !== "added") return;
|
|
4925
|
+
const { key } = canonicalizePath(patch.path);
|
|
4926
|
+
originals.set(key, { segments: patch.path, value: patch.newValue });
|
|
4927
|
+
ensurePathOrdinal(st, key);
|
|
4928
|
+
if (hydration === void 0) {
|
|
4929
|
+
fields.set(key, {
|
|
4930
|
+
path: patch.path,
|
|
4931
|
+
updatedAt: initStamp,
|
|
4932
|
+
connected: false,
|
|
4933
|
+
focused: null,
|
|
4934
|
+
blurred: null,
|
|
4935
|
+
touched: false,
|
|
4936
|
+
interacted: false,
|
|
4937
|
+
blurredAfterInteraction: false
|
|
4938
|
+
});
|
|
4939
|
+
}
|
|
4940
|
+
});
|
|
4884
4941
|
if (hydration !== void 0) {
|
|
4885
4942
|
for (const [rawKey, record] of hydration.fields) {
|
|
4886
4943
|
if (typeof rawKey !== "string" || !isHydratedFieldRecord(record)) {
|
|
@@ -4903,25 +4960,6 @@ function createFormStore(options) {
|
|
|
4903
4960
|
}
|
|
4904
4961
|
setErrorChannelForKey(st, rawKey, "user", errs);
|
|
4905
4962
|
}
|
|
4906
|
-
} else {
|
|
4907
|
-
const initStamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
4908
|
-
diffAndApply({}, initialData, [], (patch) => {
|
|
4909
|
-
if (patch.kind !== "added") return;
|
|
4910
|
-
const { key } = canonicalizePath(patch.path);
|
|
4911
|
-
fields.set(key, {
|
|
4912
|
-
path: patch.path,
|
|
4913
|
-
updatedAt: initStamp,
|
|
4914
|
-
connected: false,
|
|
4915
|
-
focused: null,
|
|
4916
|
-
blurred: null,
|
|
4917
|
-
touched: false,
|
|
4918
|
-
interacted: false,
|
|
4919
|
-
blurredAfterInteraction: false
|
|
4920
|
-
});
|
|
4921
|
-
});
|
|
4922
|
-
if (!schemaResponse.success) {
|
|
4923
|
-
replaceErrorChannel(st, "schema", schemaResponse.errors);
|
|
4924
|
-
}
|
|
4925
4963
|
}
|
|
4926
4964
|
queueInitialAsyncValidation(st);
|
|
4927
4965
|
return st;
|
|
@@ -5019,9 +5057,11 @@ const HISTORY_MODULE_KEY = "history";
|
|
|
5019
5057
|
function buildFreshState(key, schema, configuration, registry) {
|
|
5020
5058
|
const pending = registry.pendingHydration.get(key);
|
|
5021
5059
|
if (pending !== void 0) registry.pendingHydration.delete(key);
|
|
5060
|
+
const schemaDefaults = schema.getDefaultValues({ useDefaultSchemaValues: true });
|
|
5022
5061
|
const walked = walkUnsetSentinels(
|
|
5023
5062
|
configuration.defaultValues,
|
|
5024
|
-
schema
|
|
5063
|
+
schema,
|
|
5064
|
+
schemaDefaults
|
|
5025
5065
|
);
|
|
5026
5066
|
let initialBlankPaths;
|
|
5027
5067
|
if (pending === void 0) {
|
|
@@ -5031,6 +5071,7 @@ function buildFreshState(key, schema, configuration, registry) {
|
|
|
5031
5071
|
formKey: key,
|
|
5032
5072
|
schema,
|
|
5033
5073
|
defaultValues: walked.cleanedValues,
|
|
5074
|
+
schemaDefaults,
|
|
5034
5075
|
hydration: pending,
|
|
5035
5076
|
ssr: registry.ssr,
|
|
5036
5077
|
...pickDefined({
|
|
@@ -5041,12 +5082,10 @@ function buildFreshState(key, schema, configuration, registry) {
|
|
|
5041
5082
|
coerce: configuration.coerce,
|
|
5042
5083
|
initialBlankPaths
|
|
5043
5084
|
}),
|
|
5044
|
-
// Server
|
|
5045
|
-
//
|
|
5046
|
-
//
|
|
5047
|
-
//
|
|
5048
|
-
// wizard explicitly skipped this key — even an explicit
|
|
5049
|
-
// `form.activate()` defers to the wizard's render-efficiency
|
|
5085
|
+
// Server only. `enqueue` records intent on every `state.activate()`
|
|
5086
|
+
// so a wizard skip-list override has a consistent set to diff
|
|
5087
|
+
// against; `shouldFire` lets the activate path bail on a key the
|
|
5088
|
+
// wizard skipped. Even an explicit `form.activate()` defers to that
|
|
5050
5089
|
// skip-list on the server.
|
|
5051
5090
|
...registry.ssr ? {
|
|
5052
5091
|
ssrPrefetch: {
|
|
@@ -5096,6 +5135,7 @@ function resolveFormKey(key) {
|
|
|
5096
5135
|
}
|
|
5097
5136
|
|
|
5098
5137
|
let injectedInstanceCounter = 0;
|
|
5138
|
+
const sharedApis = /* @__PURE__ */ new WeakMap();
|
|
5099
5139
|
function injectForm(input) {
|
|
5100
5140
|
const key = typeof input === "string" ? input : input?.key;
|
|
5101
5141
|
const ssrAccessed = typeof input === "object" && input !== null ? input.__ssrAccessed === true : false;
|
|
@@ -5118,12 +5158,37 @@ function injectForm(input) {
|
|
|
5118
5158
|
apiOptions.history = history;
|
|
5119
5159
|
}
|
|
5120
5160
|
const ambientInstanceId = getCurrentInstance() !== null ? inject(kFormInstanceId, null) : null;
|
|
5121
|
-
const
|
|
5122
|
-
return buildFormApi(
|
|
5161
|
+
const api = ambientInstanceId !== null ? shareApi(state, ambientInstanceId, apiOptions) : buildFormApi(
|
|
5123
5162
|
state,
|
|
5124
|
-
|
|
5163
|
+
getCurrentInstance() !== null ? useId() : `atta:form-instance-injected:${injectedInstanceCounter++}`,
|
|
5125
5164
|
apiOptions
|
|
5126
5165
|
);
|
|
5166
|
+
return api;
|
|
5167
|
+
}
|
|
5168
|
+
function shareApi(state, formInstanceId, options) {
|
|
5169
|
+
let instances = sharedApis.get(state);
|
|
5170
|
+
if (instances === void 0) {
|
|
5171
|
+
instances = /* @__PURE__ */ new Map();
|
|
5172
|
+
sharedApis.set(state, instances);
|
|
5173
|
+
}
|
|
5174
|
+
const byInstance = instances;
|
|
5175
|
+
let shared = byInstance.get(formInstanceId);
|
|
5176
|
+
if (shared === void 0) {
|
|
5177
|
+
const scope = effectScope(true);
|
|
5178
|
+
const api = scope.run(() => buildFormApi(state, formInstanceId, options));
|
|
5179
|
+
if (api === void 0) return buildFormApi(state, formInstanceId, options);
|
|
5180
|
+
shared = { api, scope, users: 0 };
|
|
5181
|
+
byInstance.set(formInstanceId, shared);
|
|
5182
|
+
}
|
|
5183
|
+
const entry = shared;
|
|
5184
|
+
entry.users += 1;
|
|
5185
|
+
onScopeDispose(() => {
|
|
5186
|
+
entry.users -= 1;
|
|
5187
|
+
if (entry.users > 0) return;
|
|
5188
|
+
entry.scope.stop();
|
|
5189
|
+
byInstance.delete(formInstanceId);
|
|
5190
|
+
});
|
|
5191
|
+
return entry.api;
|
|
5127
5192
|
}
|
|
5128
5193
|
function resolveState(key, registry) {
|
|
5129
5194
|
if (key !== void 0) {
|
|
@@ -5218,22 +5283,23 @@ function useRegister(options) {
|
|
|
5218
5283
|
const capturedRegisterValue = shallowRef(void 0);
|
|
5219
5284
|
const refreshAndStripBridgeAttrs = () => {
|
|
5220
5285
|
const rawAttrs = instance.attrs;
|
|
5286
|
+
const parentBinding = instance.vnode.dirs?.find(
|
|
5287
|
+
(dir) => dir.dir?.[V_REGISTER_MARKER] === true
|
|
5288
|
+
);
|
|
5221
5289
|
if ("registerValue" in rawAttrs) {
|
|
5222
5290
|
capturedRegisterValue.value = rawAttrs["registerValue"];
|
|
5223
5291
|
delete rawAttrs["registerValue"];
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
}
|
|
5236
|
-
}
|
|
5292
|
+
} else if (parentBinding !== void 0) {
|
|
5293
|
+
capturedRegisterValue.value = parentBinding.value;
|
|
5294
|
+
}
|
|
5295
|
+
const captured = capturedRegisterValue.value;
|
|
5296
|
+
if (captured !== void 0) {
|
|
5297
|
+
armDomBinding(captured);
|
|
5298
|
+
if (parentBinding !== void 0) {
|
|
5299
|
+
recordInheritedModifiers(
|
|
5300
|
+
captured,
|
|
5301
|
+
withInheritedModifiers(parentBinding.value, parentBinding.modifiers)
|
|
5302
|
+
);
|
|
5237
5303
|
}
|
|
5238
5304
|
}
|
|
5239
5305
|
if ("value" in rawAttrs) delete rawAttrs["value"];
|
|
@@ -5259,7 +5325,7 @@ function warnOutsideSetup() {
|
|
|
5259
5325
|
warnedOutsideSetup = true;
|
|
5260
5326
|
const frame = captureUserCallSite();
|
|
5261
5327
|
console.warn(
|
|
5262
|
-
`[attaform] useRegister() called outside a component setup; returning an unbound RegisterValue proxy. Fix: call it inside <script setup> or a setup() function
|
|
5328
|
+
`[attaform] useRegister() called outside a component setup; returning an unbound RegisterValue proxy. Fix: call it inside <script setup> or a setup() function, not from an event handler or async callback.` + (frame !== void 0 ? ` ${frame}` : "")
|
|
5263
5329
|
);
|
|
5264
5330
|
}
|
|
5265
5331
|
function warnNoParentRV(instance) {
|
|
@@ -5359,11 +5425,11 @@ const NOOP_SUCCESS = {
|
|
|
5359
5425
|
data: EMPTY_VALUE,
|
|
5360
5426
|
errors: void 0
|
|
5361
5427
|
};
|
|
5362
|
-
const NOOP_DEFAULTS = NOOP_SUCCESS;
|
|
5363
5428
|
let NOOP_SCHEMA;
|
|
5364
5429
|
function buildNoopWizardSchema() {
|
|
5365
5430
|
NOOP_SCHEMA ?? (NOOP_SCHEMA = {
|
|
5366
|
-
getDefaultValues: () =>
|
|
5431
|
+
getDefaultValues: () => EMPTY_VALUE,
|
|
5432
|
+
validateSync: () => [],
|
|
5367
5433
|
getDefaultAtPath: () => void 0,
|
|
5368
5434
|
getEmptyValueAtPath: () => void 0,
|
|
5369
5435
|
isPreprocessOrCoerceLeaf: () => false,
|
|
@@ -5379,7 +5445,8 @@ function buildNoopWizardSchema() {
|
|
|
5379
5445
|
isLeafAtPath: () => false,
|
|
5380
5446
|
isRequiredAtPath: () => false,
|
|
5381
5447
|
getUnionDiscriminatorAtPath: () => void 0,
|
|
5382
|
-
hasDiscriminatedUnions: () => false
|
|
5448
|
+
hasDiscriminatedUnions: () => false,
|
|
5449
|
+
hasPreprocessOrCoerce: () => false
|
|
5383
5450
|
});
|
|
5384
5451
|
return NOOP_SCHEMA;
|
|
5385
5452
|
}
|
|
@@ -5490,7 +5557,7 @@ function useWizard(options) {
|
|
|
5490
5557
|
const rawSteps = Array.isArray(options.steps) ? options.steps : [];
|
|
5491
5558
|
if (rawSteps.length === 0 && true) {
|
|
5492
5559
|
console.error(
|
|
5493
|
-
"[attaform] useWizard({ steps }): expected a non-empty array of step slots. Continuing with an empty step list
|
|
5560
|
+
"[attaform] useWizard({ steps }): expected a non-empty array of step slots. Continuing with an empty step list: wizard.currentStep reads as undefined, navigation refuses, handleSubmit no-ops."
|
|
5494
5561
|
);
|
|
5495
5562
|
}
|
|
5496
5563
|
const registry = useRegistry();
|
|
@@ -5579,33 +5646,45 @@ function useWizard(options) {
|
|
|
5579
5646
|
return activeKey.value === "" ? void 0 : activeKey.value;
|
|
5580
5647
|
}
|
|
5581
5648
|
};
|
|
5582
|
-
function lazyComputedFor(marker) {
|
|
5649
|
+
function lazyComputedFor(marker, index) {
|
|
5583
5650
|
const cached = lazyComputeds.get(marker);
|
|
5584
5651
|
if (cached !== void 0) return cached;
|
|
5585
5652
|
const c = computed(() => {
|
|
5586
5653
|
void lazyEpoch.value;
|
|
5587
|
-
return
|
|
5654
|
+
return callConsumerFn(
|
|
5655
|
+
() => marker.resolve(slotCtx),
|
|
5656
|
+
void 0,
|
|
5657
|
+
"wizard-lazy-slot",
|
|
5658
|
+
`steps[${index}]`
|
|
5659
|
+
);
|
|
5588
5660
|
});
|
|
5589
5661
|
lazyComputeds.set(marker, c);
|
|
5590
5662
|
return c;
|
|
5591
5663
|
}
|
|
5592
5664
|
const MAX_SLOT_DEPTH = 32;
|
|
5593
|
-
function normalizeSlot(slot, ctx, gated, depth) {
|
|
5665
|
+
function normalizeSlot(slot, ctx, gated, depth, index) {
|
|
5594
5666
|
if (depth > MAX_SLOT_DEPTH) {
|
|
5595
5667
|
{
|
|
5596
5668
|
console.warn(
|
|
5597
|
-
`[attaform] useWizard:
|
|
5669
|
+
`[attaform] useWizard: steps[${index}] nested past ${MAX_SLOT_DEPTH} levels (gate / lazy / function); dropping it. Check for a resolver that returns itself.`
|
|
5598
5670
|
);
|
|
5599
5671
|
}
|
|
5600
5672
|
return void 0;
|
|
5601
5673
|
}
|
|
5602
5674
|
if (slot === void 0 || slot === null) return void 0;
|
|
5603
5675
|
if (typeof slot === "string") return { form: getOrBuildNoop(slot), gated };
|
|
5604
|
-
if (isGateMarker(slot)) return normalizeSlot(slot.inner, ctx, true, depth + 1);
|
|
5605
|
-
if (isLazyMarker(slot))
|
|
5676
|
+
if (isGateMarker(slot)) return normalizeSlot(slot.inner, ctx, true, depth + 1, index);
|
|
5677
|
+
if (isLazyMarker(slot)) {
|
|
5678
|
+
return normalizeSlot(lazyComputedFor(slot, index).value, ctx, gated, depth + 1, index);
|
|
5679
|
+
}
|
|
5606
5680
|
if (typeof slot === "function") {
|
|
5607
|
-
const result =
|
|
5608
|
-
|
|
5681
|
+
const result = callConsumerFn(
|
|
5682
|
+
() => slot(ctx),
|
|
5683
|
+
void 0,
|
|
5684
|
+
"wizard-slot",
|
|
5685
|
+
`steps[${index}]`
|
|
5686
|
+
);
|
|
5687
|
+
return normalizeSlot(result, ctx, gated, depth + 1, index);
|
|
5609
5688
|
}
|
|
5610
5689
|
if (isAnyForm(slot)) return { form: slot, gated };
|
|
5611
5690
|
return void 0;
|
|
@@ -5614,7 +5693,7 @@ function useWizard(options) {
|
|
|
5614
5693
|
const out = [];
|
|
5615
5694
|
const seen = /* @__PURE__ */ new Set();
|
|
5616
5695
|
for (let i = 0; i < rawSteps.length; i++) {
|
|
5617
|
-
const norm = normalizeSlot(rawSteps[i], slotCtx, false, 0);
|
|
5696
|
+
const norm = normalizeSlot(rawSteps[i], slotCtx, false, 0, i);
|
|
5618
5697
|
if (norm === void 0) continue;
|
|
5619
5698
|
const { form, gated } = norm;
|
|
5620
5699
|
if (seen.has(form.key)) {
|
|
@@ -5927,13 +6006,15 @@ function useWizard(options) {
|
|
|
5927
6006
|
seedRef.value = resolved.value;
|
|
5928
6007
|
applyGateSeed(resolved.value);
|
|
5929
6008
|
} else {
|
|
5930
|
-
const eager = resolved.factory();
|
|
6009
|
+
const eager = callConsumerFn(() => resolved.factory(), void 0, "wizard-default-statuses");
|
|
5931
6010
|
if (eager instanceof Promise) {
|
|
5932
6011
|
void eager.then((value) => {
|
|
5933
6012
|
seedRef.value = value;
|
|
5934
6013
|
applyGateSeed(value);
|
|
6014
|
+
}).catch((err) => {
|
|
6015
|
+
reportConsumerThrow("wizard-default-statuses", void 0, err);
|
|
5935
6016
|
});
|
|
5936
|
-
} else {
|
|
6017
|
+
} else if (eager !== void 0) {
|
|
5937
6018
|
seedRef.value = eager;
|
|
5938
6019
|
applyGateSeed(eager);
|
|
5939
6020
|
}
|
|
@@ -6019,10 +6100,15 @@ function useWizard(options) {
|
|
|
6019
6100
|
}
|
|
6020
6101
|
const progressOverride = options.progress;
|
|
6021
6102
|
const progress = computed(() => {
|
|
6103
|
+
const list = compiledSteps.value;
|
|
6022
6104
|
if (progressOverride !== void 0) {
|
|
6023
|
-
|
|
6105
|
+
const override = callConsumerFn(
|
|
6106
|
+
() => progressOverride(list),
|
|
6107
|
+
null,
|
|
6108
|
+
"wizard-progress"
|
|
6109
|
+
);
|
|
6110
|
+
if (override !== null) return override;
|
|
6024
6111
|
}
|
|
6025
|
-
const list = compiledSteps.value;
|
|
6026
6112
|
if (list.length === 0) return 0;
|
|
6027
6113
|
let valid = 0;
|
|
6028
6114
|
for (const step of list) {
|
|
@@ -6050,11 +6136,15 @@ function useWizard(options) {
|
|
|
6050
6136
|
historyHandle.subscribe((value) => {
|
|
6051
6137
|
urlMirror.value = value;
|
|
6052
6138
|
});
|
|
6053
|
-
const
|
|
6139
|
+
const consumerRestore = options.restore;
|
|
6140
|
+
const restoreCallback = consumerRestore === false ? void 0 : consumerRestore !== void 0 ? () => callConsumerFn(() => consumerRestore(), void 0, "wizard-restore") : () => {
|
|
6054
6141
|
const value = urlMirror.value;
|
|
6055
6142
|
return value === void 0 ? void 0 : { step: value };
|
|
6056
6143
|
};
|
|
6057
|
-
const
|
|
6144
|
+
const consumerPersist = options.persist;
|
|
6145
|
+
const persistCallback = consumerPersist === false ? void 0 : consumerPersist !== void 0 ? (state) => {
|
|
6146
|
+
callConsumerFn(() => consumerPersist(state), void 0, "wizard-persist");
|
|
6147
|
+
} : (state) => {
|
|
6058
6148
|
if (state.step === void 0) return;
|
|
6059
6149
|
const current = historyHandle.read();
|
|
6060
6150
|
const effectiveCurrent = current !== void 0 && isCompiledKey(current) ? current : firstKey();
|
|
@@ -6360,7 +6450,7 @@ function useWizard(options) {
|
|
|
6360
6450
|
for (const key of keys) {
|
|
6361
6451
|
const store = registry.forms.get(key);
|
|
6362
6452
|
if (store === void 0) continue;
|
|
6363
|
-
for (const cell of store.errorCells.values()) {
|
|
6453
|
+
for (const cell of store.errorCells().values()) {
|
|
6364
6454
|
for (const err of cell.user) out.push(toWizardAggregateError(err, key));
|
|
6365
6455
|
}
|
|
6366
6456
|
}
|