attaform 0.14.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/devtools.cjs +3 -3
- package/dist/chunks/devtools.cjs.map +1 -1
- package/dist/chunks/devtools.mjs +3 -3
- package/dist/chunks/devtools.mjs.map +1 -1
- package/dist/chunks/indexeddb.cjs +1 -1
- package/dist/chunks/indexeddb.mjs +1 -1
- package/dist/chunks/local-storage.cjs +1 -1
- package/dist/chunks/local-storage.mjs +1 -1
- package/dist/chunks/session-storage.cjs +1 -1
- package/dist/chunks/session-storage.mjs +1 -1
- package/dist/index.cjs +5 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.mjs +6 -6
- package/dist/nuxt.d.cts +1 -1
- package/dist/nuxt.d.mts +1 -1
- package/dist/nuxt.d.ts +1 -1
- package/dist/runtime/plugins/attaform.cjs +1 -1
- package/dist/runtime/plugins/attaform.mjs +1 -1
- package/dist/shared/{attaform.DDXrY-1Q.d.mts → attaform.0Gxd_OOx.d.cts} +558 -174
- package/dist/shared/{attaform.DDXrY-1Q.d.ts → attaform.0Gxd_OOx.d.mts} +558 -174
- package/dist/shared/{attaform.DDXrY-1Q.d.cts → attaform.0Gxd_OOx.d.ts} +558 -174
- package/dist/shared/{attaform.xKWYHMdq.cjs → attaform.BOi138GE.cjs} +10 -2
- package/dist/shared/{attaform.xKWYHMdq.cjs.map → attaform.BOi138GE.cjs.map} +1 -1
- package/dist/shared/{attaform.CRgix6_n.cjs → attaform.BgYBU8gV.cjs} +18 -17
- package/dist/shared/attaform.BgYBU8gV.cjs.map +1 -0
- package/dist/shared/attaform.Bubm_slq.cjs.map +1 -1
- package/dist/shared/{attaform.CNJO3mME.cjs → attaform.CDJVeoJU.cjs} +633 -236
- package/dist/shared/attaform.CDJVeoJU.cjs.map +1 -0
- package/dist/shared/{attaform.DlgKK10S.mjs → attaform.CRk8NhlD.mjs} +18 -17
- package/dist/shared/attaform.CRk8NhlD.mjs.map +1 -0
- package/dist/shared/{attaform.CXZgUECn.d.cts → attaform.CVv9Oh0a.d.mts} +41 -9
- package/dist/shared/{attaform.BYc9kugA.d.ts → attaform.CWCx2r0x.d.ts} +41 -9
- package/dist/shared/attaform.CXpzmj38.mjs.map +1 -1
- package/dist/shared/{attaform.Cc93zNzD.mjs → attaform.DXye3JKf.mjs} +10 -3
- package/dist/shared/{attaform.Cc93zNzD.mjs.map → attaform.DXye3JKf.mjs.map} +1 -1
- package/dist/shared/{attaform.DOKOyb3Y.d.mts → attaform.Dq5BabH1.d.cts} +41 -9
- package/dist/shared/{attaform.B5GWYl76.cjs → attaform.RypIkgVy.cjs} +38 -7
- package/dist/shared/attaform.RypIkgVy.cjs.map +1 -0
- package/dist/shared/{attaform.al_rpt7_.mjs → attaform.a99dQV7Q.mjs} +39 -8
- package/dist/shared/attaform.a99dQV7Q.mjs.map +1 -0
- package/dist/shared/{attaform.BRTxpA3q.mjs → attaform.qxyip_aN.mjs} +634 -238
- package/dist/shared/attaform.qxyip_aN.mjs.map +1 -0
- package/dist/transforms.d.cts +2 -2
- package/dist/transforms.d.mts +2 -2
- package/dist/transforms.d.ts +2 -2
- package/dist/zod-v3.cjs +55 -3
- package/dist/zod-v3.cjs.map +1 -1
- package/dist/zod-v3.d.cts +77 -4
- package/dist/zod-v3.d.mts +77 -4
- package/dist/zod-v3.d.ts +77 -4
- package/dist/zod-v3.mjs +56 -6
- package/dist/zod-v3.mjs.map +1 -1
- package/dist/zod.cjs +372 -5
- package/dist/zod.cjs.map +1 -1
- package/dist/zod.d.cts +120 -4
- package/dist/zod.d.mts +120 -4
- package/dist/zod.d.ts +120 -4
- package/dist/zod.mjs +371 -8
- package/dist/zod.mjs.map +1 -1
- package/package.json +3 -1
- package/dist/shared/attaform.B5GWYl76.cjs.map +0 -1
- package/dist/shared/attaform.BRTxpA3q.mjs.map +0 -1
- package/dist/shared/attaform.CNJO3mME.cjs.map +0 -1
- package/dist/shared/attaform.CRgix6_n.cjs.map +0 -1
- package/dist/shared/attaform.DlgKK10S.mjs.map +0 -1
- package/dist/shared/attaform.al_rpt7_.mjs.map +0 -1
|
@@ -1,6 +1,173 @@
|
|
|
1
|
-
import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, readonly, reactive, shallowRef, provide, getCurrentInstance, useId, toRaw, inject } from 'vue';
|
|
2
|
-
import { c as canonicalizePath,
|
|
3
|
-
import { _ as __DEV__, c as SubmitErrorHandlerError, A as AnonPersistError, m as captureUserCallSite, j as enforceSensitiveCheck, n as createPersistOptInRegistry, e as useRegistry, o as kFormContext, p as kFormInstanceId, b as ReservedFormKeyError } from './attaform.
|
|
1
|
+
import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, reactive, watch, markRaw, shallowRef, provide, getCurrentInstance, useId, toRaw, inject } from 'vue';
|
|
2
|
+
import { s as segmentsForPathKey, i as isPathPrefix, c as canonicalizePath, a as ROOT_PATH_KEY } from './attaform.DXye3JKf.mjs';
|
|
3
|
+
import { _ as __DEV__, c as SubmitErrorHandlerError, A as AnonPersistError, m as captureUserCallSite, j as enforceSensitiveCheck, n as createPersistOptInRegistry, e as useRegistry, o as kFormContext, p as kFormInstanceId, b as ReservedFormKeyError } from './attaform.a99dQV7Q.mjs';
|
|
4
|
+
|
|
5
|
+
function isDescendable(value) {
|
|
6
|
+
if (value === null || typeof value !== "object") return false;
|
|
7
|
+
if (Array.isArray(value)) return true;
|
|
8
|
+
const proto = Object.getPrototypeOf(value);
|
|
9
|
+
return proto === null || proto === Object.prototype;
|
|
10
|
+
}
|
|
11
|
+
function appendSegment(prefix, segment) {
|
|
12
|
+
const next = new Array(prefix.length + 1);
|
|
13
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
14
|
+
const s = prefix[i];
|
|
15
|
+
next[i] = s;
|
|
16
|
+
}
|
|
17
|
+
next[prefix.length] = segment;
|
|
18
|
+
return next;
|
|
19
|
+
}
|
|
20
|
+
function diffAndApply(oldValue, newValue, prefix, visit) {
|
|
21
|
+
if (Object.is(oldValue, newValue)) return;
|
|
22
|
+
const oldIsDescendable = isDescendable(oldValue);
|
|
23
|
+
const newIsDescendable = isDescendable(newValue);
|
|
24
|
+
if (oldValue === void 0 && newIsDescendable) {
|
|
25
|
+
if (Array.isArray(newValue)) {
|
|
26
|
+
for (let i = 0; i < newValue.length; i++) {
|
|
27
|
+
diffAndApply(void 0, newValue[i], appendSegment(prefix, i), visit);
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
const rec = newValue;
|
|
31
|
+
for (const k of Object.keys(rec)) {
|
|
32
|
+
diffAndApply(void 0, rec[k], appendSegment(prefix, k), visit);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (oldIsDescendable && newValue === void 0) {
|
|
38
|
+
if (Array.isArray(oldValue)) {
|
|
39
|
+
for (let i = 0; i < oldValue.length; i++) {
|
|
40
|
+
diffAndApply(oldValue[i], void 0, appendSegment(prefix, i), visit);
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
const rec = oldValue;
|
|
44
|
+
for (const k of Object.keys(rec)) {
|
|
45
|
+
diffAndApply(rec[k], void 0, appendSegment(prefix, k), visit);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (oldIsDescendable && newIsDescendable) {
|
|
51
|
+
const oldIsArray = Array.isArray(oldValue);
|
|
52
|
+
const newIsArray = Array.isArray(newValue);
|
|
53
|
+
if (oldIsArray && newIsArray) {
|
|
54
|
+
const oldArr = oldValue;
|
|
55
|
+
const newArr = newValue;
|
|
56
|
+
const max = Math.max(oldArr.length, newArr.length);
|
|
57
|
+
for (let i = 0; i < max; i++) {
|
|
58
|
+
diffAndApply(oldArr[i], newArr[i], appendSegment(prefix, i), visit);
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!oldIsArray && !newIsArray) {
|
|
63
|
+
const oldRec = oldValue;
|
|
64
|
+
const newRec = newValue;
|
|
65
|
+
const seen = /* @__PURE__ */ new Set();
|
|
66
|
+
for (const k of Object.keys(oldRec)) {
|
|
67
|
+
seen.add(k);
|
|
68
|
+
diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
|
|
69
|
+
}
|
|
70
|
+
for (const k of Object.keys(newRec)) {
|
|
71
|
+
if (seen.has(k)) continue;
|
|
72
|
+
diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (oldIsDescendable && !newIsDescendable) {
|
|
80
|
+
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!oldIsDescendable && newIsDescendable) {
|
|
84
|
+
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (oldValue === void 0) {
|
|
88
|
+
visit({ kind: "added", path: prefix, newValue });
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (newValue === void 0) {
|
|
92
|
+
visit({ kind: "removed", path: prefix, oldValue });
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
96
|
+
}
|
|
97
|
+
function applyChangedKeys(target, source) {
|
|
98
|
+
if (!isDescendable(target) || !isDescendable(source)) return false;
|
|
99
|
+
const targetIsArray = Array.isArray(target);
|
|
100
|
+
const sourceIsArray = Array.isArray(source);
|
|
101
|
+
if (targetIsArray !== sourceIsArray) return false;
|
|
102
|
+
const ROOT_SENTINEL = Symbol.for("attaform.applyChangedKeys.rootMismatch");
|
|
103
|
+
const changedFirstSegments = /* @__PURE__ */ new Set();
|
|
104
|
+
diffAndApply(target, source, [], (patch) => {
|
|
105
|
+
if (patch.path.length === 0) {
|
|
106
|
+
changedFirstSegments.add(ROOT_SENTINEL);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
changedFirstSegments.add(patch.path[0]);
|
|
110
|
+
});
|
|
111
|
+
if (changedFirstSegments.has(ROOT_SENTINEL)) return false;
|
|
112
|
+
if (targetIsArray) {
|
|
113
|
+
const t = target;
|
|
114
|
+
const s = source;
|
|
115
|
+
if (t.length > s.length) t.length = s.length;
|
|
116
|
+
for (const idx of changedFirstSegments) {
|
|
117
|
+
if (typeof idx === "symbol") continue;
|
|
118
|
+
const i = typeof idx === "number" ? idx : Number(idx);
|
|
119
|
+
t[i] = s[i];
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
const t = target;
|
|
123
|
+
const s = source;
|
|
124
|
+
const sourceKeys = new Set(Object.keys(s));
|
|
125
|
+
for (const k of Object.keys(t)) {
|
|
126
|
+
if (!sourceKeys.has(k)) delete t[k];
|
|
127
|
+
}
|
|
128
|
+
for (const k of changedFirstSegments) {
|
|
129
|
+
if (typeof k === "symbol") continue;
|
|
130
|
+
t[String(k)] = s[String(k)];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
function structuralSnapshot(value) {
|
|
136
|
+
if (!isDescendable(value)) return value;
|
|
137
|
+
if (Array.isArray(value)) {
|
|
138
|
+
const out2 = new Array(value.length);
|
|
139
|
+
for (let i = 0; i < value.length; i++) {
|
|
140
|
+
out2[i] = structuralSnapshot(value[i]);
|
|
141
|
+
}
|
|
142
|
+
return out2;
|
|
143
|
+
}
|
|
144
|
+
const src = value;
|
|
145
|
+
const out = {};
|
|
146
|
+
for (const k of Object.keys(src)) {
|
|
147
|
+
out[k] = structuralSnapshot(src[k]);
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const EMPTY_RESOLVED_FIELD_META = Object.freeze({
|
|
153
|
+
label: "",
|
|
154
|
+
description: void 0,
|
|
155
|
+
placeholder: void 0,
|
|
156
|
+
meta: Object.freeze({})
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
function humanize(segment) {
|
|
160
|
+
if (typeof segment === "number") return "";
|
|
161
|
+
const str = String(segment);
|
|
162
|
+
if (str.length === 0) return "";
|
|
163
|
+
if (/^\d+$/.test(str)) return "";
|
|
164
|
+
const tokens = str.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().split(" ").filter((part) => part.length > 0);
|
|
165
|
+
if (tokens.length === 0) return "";
|
|
166
|
+
return tokens.map((part) => {
|
|
167
|
+
const head = part[0];
|
|
168
|
+
return head === void 0 ? part : head.toUpperCase() + part.slice(1).toLowerCase();
|
|
169
|
+
}).join(" ");
|
|
170
|
+
}
|
|
4
171
|
|
|
5
172
|
const NOT_FOUND = Symbol("NOT_FOUND");
|
|
6
173
|
function descendStep(value, segment) {
|
|
@@ -246,6 +413,153 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
|
|
|
246
413
|
return rec;
|
|
247
414
|
}
|
|
248
415
|
|
|
416
|
+
function buildFieldStateAccessor(state) {
|
|
417
|
+
const cache = /* @__PURE__ */ new Map();
|
|
418
|
+
return function getFieldState(pathInput) {
|
|
419
|
+
const { segments, key } = canonicalizePath(pathInput);
|
|
420
|
+
const cached = cache.get(key);
|
|
421
|
+
if (cached !== void 0) return cached;
|
|
422
|
+
const c = computed(
|
|
423
|
+
() => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key) : buildContainerFieldState(state, segments)
|
|
424
|
+
);
|
|
425
|
+
cache.set(key, c);
|
|
426
|
+
return c;
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
function buildLeafFieldState(state, segments, key) {
|
|
430
|
+
const record = state.fields.get(key);
|
|
431
|
+
const value = state.getValueAtPath(segments);
|
|
432
|
+
const original = state.originals.get(key)?.value;
|
|
433
|
+
const pristine = state.isPristineAtPath(segments);
|
|
434
|
+
const schemaForKey = state.schemaErrors.get(key);
|
|
435
|
+
const blankForKey = state.derivedBlankErrors.value.get(key);
|
|
436
|
+
const userForKey = state.userErrors.get(key);
|
|
437
|
+
const errors = [];
|
|
438
|
+
if (schemaForKey !== void 0) errors.push(...schemaForKey);
|
|
439
|
+
if (blankForKey !== void 0) errors.push(...blankForKey);
|
|
440
|
+
if (userForKey !== void 0) errors.push(...userForKey);
|
|
441
|
+
const validating = (state.fieldValidationCounts.get(key) ?? 0) > 0;
|
|
442
|
+
const gated = state.pathHasAsyncValidation(segments) && !state.firstValidationDone.value;
|
|
443
|
+
const valid = !gated && errors.length === 0 && !validating;
|
|
444
|
+
const elementRecord = state.elements.get(key);
|
|
445
|
+
const elementsArr = elementRecord ? Object.freeze([...elementRecord.elements]) : EMPTY_ELEMENTS;
|
|
446
|
+
const firstElement = elementsArr[0] ?? null;
|
|
447
|
+
const resolved = state.schema.getFieldMetaAtPath ? state.schema.getFieldMetaAtPath(segments) : EMPTY_RESOLVED_FIELD_META;
|
|
448
|
+
const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
|
|
449
|
+
const label = resolved.label || humanize(lastSegment);
|
|
450
|
+
return {
|
|
451
|
+
value,
|
|
452
|
+
original,
|
|
453
|
+
pristine,
|
|
454
|
+
dirty: !pristine,
|
|
455
|
+
focused: record?.focused ?? null,
|
|
456
|
+
blurred: record?.blurred ?? null,
|
|
457
|
+
touched: record?.touched ?? null,
|
|
458
|
+
connected: record?.connected ?? false,
|
|
459
|
+
element: firstElement,
|
|
460
|
+
elements: elementsArr,
|
|
461
|
+
updatedAt: record?.updatedAt ?? null,
|
|
462
|
+
errors,
|
|
463
|
+
validating,
|
|
464
|
+
valid,
|
|
465
|
+
path: segments,
|
|
466
|
+
blank: state.blankPaths.has(key),
|
|
467
|
+
label,
|
|
468
|
+
description: resolved.description,
|
|
469
|
+
placeholder: resolved.placeholder,
|
|
470
|
+
meta: resolved.meta
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function buildContainerFieldState(state, segments, _key) {
|
|
474
|
+
const formValue = state.form.value;
|
|
475
|
+
const value = state.getValueAtPath(segments);
|
|
476
|
+
const original = state.originals.get(canonicalizePath(segments).key)?.value;
|
|
477
|
+
let pristine = true;
|
|
478
|
+
let blank = true;
|
|
479
|
+
let dirty = false;
|
|
480
|
+
let focused = false;
|
|
481
|
+
let blurred = false;
|
|
482
|
+
let touched = false;
|
|
483
|
+
let connected = false;
|
|
484
|
+
let validating = false;
|
|
485
|
+
let updatedAt = null;
|
|
486
|
+
let asyncPending = false;
|
|
487
|
+
for (const [, entry] of state.originals) {
|
|
488
|
+
if (!isPathPrefix(segments, entry.segments)) continue;
|
|
489
|
+
if (segments.length === entry.segments.length) continue;
|
|
490
|
+
if (!hasAtPath(formValue, entry.segments)) continue;
|
|
491
|
+
const leafKey = canonicalizePath(entry.segments).key;
|
|
492
|
+
const leafRecord = state.fields.get(leafKey);
|
|
493
|
+
if (!state.isPristineAtPath(entry.segments)) {
|
|
494
|
+
pristine = false;
|
|
495
|
+
dirty = true;
|
|
496
|
+
}
|
|
497
|
+
if (!state.blankPaths.has(leafKey)) blank = false;
|
|
498
|
+
if (leafRecord?.focused === true) focused = true;
|
|
499
|
+
if (leafRecord?.blurred === true) blurred = true;
|
|
500
|
+
if (leafRecord?.touched === true) touched = true;
|
|
501
|
+
if (leafRecord?.connected === true) connected = true;
|
|
502
|
+
if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0) validating = true;
|
|
503
|
+
if (state.pathHasAsyncValidation(entry.segments)) asyncPending = true;
|
|
504
|
+
const ts = leafRecord?.updatedAt;
|
|
505
|
+
if (ts !== void 0 && ts !== null) {
|
|
506
|
+
if (updatedAt === null || ts > updatedAt) updatedAt = ts;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const errors = aggregateErrorsAt(state, segments);
|
|
510
|
+
if (!asyncPending && state.pathHasAsyncValidation(segments)) asyncPending = true;
|
|
511
|
+
const gated = asyncPending && !state.firstValidationDone.value;
|
|
512
|
+
const valid = !gated && errors.length === 0 && !validating;
|
|
513
|
+
const resolved = state.schema.getFieldMetaAtPath ? state.schema.getFieldMetaAtPath(segments) : EMPTY_RESOLVED_FIELD_META;
|
|
514
|
+
const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
|
|
515
|
+
const label = resolved.label || humanize(lastSegment);
|
|
516
|
+
return {
|
|
517
|
+
value,
|
|
518
|
+
original,
|
|
519
|
+
pristine,
|
|
520
|
+
dirty,
|
|
521
|
+
focused,
|
|
522
|
+
blurred,
|
|
523
|
+
touched,
|
|
524
|
+
connected,
|
|
525
|
+
element: null,
|
|
526
|
+
elements: EMPTY_ELEMENTS,
|
|
527
|
+
updatedAt,
|
|
528
|
+
errors,
|
|
529
|
+
validating,
|
|
530
|
+
valid,
|
|
531
|
+
path: segments,
|
|
532
|
+
blank,
|
|
533
|
+
label,
|
|
534
|
+
description: resolved.description,
|
|
535
|
+
placeholder: resolved.placeholder,
|
|
536
|
+
meta: resolved.meta
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
function aggregateErrorsAt(state, prefix) {
|
|
540
|
+
const formValue = state.form.value;
|
|
541
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
542
|
+
const collect = (errs) => {
|
|
543
|
+
for (const [pathKey, list] of errs) {
|
|
544
|
+
if (list.length === 0) continue;
|
|
545
|
+
const segs = segmentsForPathKey(pathKey);
|
|
546
|
+
if (segs === null) continue;
|
|
547
|
+
if (!isPathPrefix(prefix, segs)) continue;
|
|
548
|
+
if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
|
|
549
|
+
const ordinal = state.ensurePathOrdinal(pathKey);
|
|
550
|
+
const existing = buckets.get(ordinal);
|
|
551
|
+
if (existing === void 0) buckets.set(ordinal, [...list]);
|
|
552
|
+
else existing.push(...list);
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
collect(state.schemaErrors);
|
|
556
|
+
collect(state.derivedBlankErrors.value);
|
|
557
|
+
collect(state.userErrors);
|
|
558
|
+
if (buckets.size === 0) return [];
|
|
559
|
+
return [...buckets.entries()].sort(([a], [b]) => a - b).flatMap(([, errs]) => errs);
|
|
560
|
+
}
|
|
561
|
+
const EMPTY_ELEMENTS = Object.freeze([]);
|
|
562
|
+
|
|
249
563
|
const INTEGER_SEGMENT = /^(?:0|[1-9]\d*)$/;
|
|
250
564
|
function keyToSegment(key) {
|
|
251
565
|
return INTEGER_SEGMENT.test(key) ? Number(key) : key;
|
|
@@ -270,11 +584,6 @@ function buildSurfaceProxy(opts) {
|
|
|
270
584
|
}
|
|
271
585
|
return containerProxyAt(segs);
|
|
272
586
|
}
|
|
273
|
-
function navigateTo(input) {
|
|
274
|
-
if (input === void 0) return rootProxy;
|
|
275
|
-
const { segments } = canonicalizePath(input);
|
|
276
|
-
return descendOrTerminate(segments);
|
|
277
|
-
}
|
|
278
587
|
function containerProxyAt(segments) {
|
|
279
588
|
const cacheKey = JSON.stringify(segments);
|
|
280
589
|
const existing = containerCache.get(cacheKey);
|
|
@@ -291,8 +600,9 @@ function buildSurfaceProxy(opts) {
|
|
|
291
600
|
const proxy = new Proxy(target, {
|
|
292
601
|
apply(_, __, args) {
|
|
293
602
|
const arg = args[0];
|
|
294
|
-
if (arg === void 0) return
|
|
295
|
-
|
|
603
|
+
if (arg === void 0) return opts.resolveCallTarget(segments);
|
|
604
|
+
const { segments: argSegs } = canonicalizePath(arg);
|
|
605
|
+
return opts.resolveCallTarget(argSegs);
|
|
296
606
|
},
|
|
297
607
|
get(_, key) {
|
|
298
608
|
if (typeof key === "symbol") {
|
|
@@ -354,8 +664,9 @@ function buildSurfaceProxy(opts) {
|
|
|
354
664
|
const proxy = new Proxy(target, {
|
|
355
665
|
apply(_, __, args) {
|
|
356
666
|
const arg = args[0];
|
|
357
|
-
if (arg === void 0) return opts.
|
|
358
|
-
|
|
667
|
+
if (arg === void 0) return opts.resolveCallTarget(segments);
|
|
668
|
+
const { segments: argSegs } = canonicalizePath(arg);
|
|
669
|
+
return opts.resolveCallTarget(argSegs);
|
|
359
670
|
},
|
|
360
671
|
get(_, key) {
|
|
361
672
|
if (typeof key === "symbol") {
|
|
@@ -378,9 +689,8 @@ function buildSurfaceProxy(opts) {
|
|
|
378
689
|
return true;
|
|
379
690
|
},
|
|
380
691
|
// Iteration: leaf-views expose the leaf-key set so
|
|
381
|
-
// `JSON.stringify(form.fields.email)` produces
|
|
382
|
-
//
|
|
383
|
-
// `JSON.stringify(form.getFieldState('email').value)` shape).
|
|
692
|
+
// `JSON.stringify(form.fields.email)` produces a FieldState
|
|
693
|
+
// snapshot rather than the function-target placeholder.
|
|
384
694
|
ownKeys: () => Array.from(leafKeys),
|
|
385
695
|
getOwnPropertyDescriptor(_, key) {
|
|
386
696
|
if (typeof key !== "string") return void 0;
|
|
@@ -421,7 +731,20 @@ function buildErrorsProxy(state) {
|
|
|
421
731
|
},
|
|
422
732
|
// No leafKeys — at a leaf, the resolved value (the merged array or
|
|
423
733
|
// undefined) IS the terminal.
|
|
424
|
-
materializeContainer: (segments) => materializeErrors(state, segments)
|
|
734
|
+
materializeContainer: (segments) => materializeErrors(state, segments),
|
|
735
|
+
// Call-form aggregates: `form.errors(path)` returns a single
|
|
736
|
+
// `ValidationError[]` for any depth (leaf or container) — same
|
|
737
|
+
// shared `aggregateErrorsAt` helper that `form.meta.errors` and
|
|
738
|
+
// `form.fields(path).errors` use, so the three surfaces never
|
|
739
|
+
// drift. Empty results return `undefined`, matching the leaf
|
|
740
|
+
// proxy's pre-existing semantic (`form.errors.email === undefined`
|
|
741
|
+
// when valid) so consumer code that branches on truthiness keeps
|
|
742
|
+
// working — the call-form just extends that semantic to
|
|
743
|
+
// containers and dynamic paths.
|
|
744
|
+
resolveCallTarget: (path) => {
|
|
745
|
+
const errs = aggregateErrorsAt(state, path);
|
|
746
|
+
return errs.length === 0 ? void 0 : errs;
|
|
747
|
+
}
|
|
425
748
|
});
|
|
426
749
|
}
|
|
427
750
|
function materializeErrors(state, containerSegments) {
|
|
@@ -528,39 +851,6 @@ function buildFieldArrayApi(state) {
|
|
|
528
851
|
};
|
|
529
852
|
}
|
|
530
853
|
|
|
531
|
-
function buildFieldStateAccessor(state) {
|
|
532
|
-
return function getFieldState(pathInput) {
|
|
533
|
-
const { segments, key } = canonicalizePath(pathInput);
|
|
534
|
-
return computed(() => {
|
|
535
|
-
const record = state.fields.get(key);
|
|
536
|
-
const value = state.getValueAtPath(segments);
|
|
537
|
-
const original = state.originals.get(key)?.value;
|
|
538
|
-
const pristine = state.isPristineAtPath(segments);
|
|
539
|
-
const schemaForKey = state.schemaErrors.get(key);
|
|
540
|
-
const blankForKey = state.derivedBlankErrors.value.get(key);
|
|
541
|
-
const userForKey = state.userErrors.get(key);
|
|
542
|
-
const errors = [];
|
|
543
|
-
if (schemaForKey !== void 0) errors.push(...schemaForKey);
|
|
544
|
-
if (blankForKey !== void 0) errors.push(...blankForKey);
|
|
545
|
-
if (userForKey !== void 0) errors.push(...userForKey);
|
|
546
|
-
return {
|
|
547
|
-
value,
|
|
548
|
-
original,
|
|
549
|
-
pristine,
|
|
550
|
-
dirty: !pristine,
|
|
551
|
-
focused: record?.focused ?? null,
|
|
552
|
-
blurred: record?.blurred ?? null,
|
|
553
|
-
touched: record?.touched ?? null,
|
|
554
|
-
isConnected: record?.isConnected ?? false,
|
|
555
|
-
updatedAt: record?.updatedAt ?? null,
|
|
556
|
-
errors,
|
|
557
|
-
path: segments,
|
|
558
|
-
blank: state.blankPaths.has(key)
|
|
559
|
-
};
|
|
560
|
-
});
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
|
|
564
854
|
const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
|
|
565
855
|
"value",
|
|
566
856
|
"original",
|
|
@@ -569,11 +859,19 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
|
|
|
569
859
|
"focused",
|
|
570
860
|
"blurred",
|
|
571
861
|
"touched",
|
|
572
|
-
"
|
|
862
|
+
"connected",
|
|
863
|
+
"element",
|
|
864
|
+
"elements",
|
|
573
865
|
"updatedAt",
|
|
574
866
|
"errors",
|
|
867
|
+
"validating",
|
|
868
|
+
"valid",
|
|
575
869
|
"path",
|
|
576
|
-
"blank"
|
|
870
|
+
"blank",
|
|
871
|
+
"label",
|
|
872
|
+
"description",
|
|
873
|
+
"placeholder",
|
|
874
|
+
"meta"
|
|
577
875
|
]);
|
|
578
876
|
function buildFieldStateProxy(state) {
|
|
579
877
|
const getFieldStateAt = buildFieldStateAccessor(state);
|
|
@@ -583,12 +881,61 @@ function buildFieldStateProxy(state) {
|
|
|
583
881
|
for (const k of FIELD_STATE_KEYS) snapshot[k] = view[k];
|
|
584
882
|
return snapshot;
|
|
585
883
|
};
|
|
884
|
+
const terminalCache = /* @__PURE__ */ new Map();
|
|
885
|
+
function fieldStateTerminalAt(segments) {
|
|
886
|
+
const cacheKey = JSON.stringify(segments);
|
|
887
|
+
const existing = terminalCache.get(cacheKey);
|
|
888
|
+
if (existing !== void 0) return existing;
|
|
889
|
+
const target = (() => {
|
|
890
|
+
});
|
|
891
|
+
const proxy = new Proxy(target, {
|
|
892
|
+
get(_, key) {
|
|
893
|
+
if (typeof key === "symbol") {
|
|
894
|
+
if (key === Symbol.toPrimitive) {
|
|
895
|
+
return (hint) => hint === "number" ? NaN : JSON.stringify(snapshotFieldStateAt(segments));
|
|
896
|
+
}
|
|
897
|
+
return Reflect.get(target, key);
|
|
898
|
+
}
|
|
899
|
+
if (typeof key !== "string") return void 0;
|
|
900
|
+
if (key === "toJSON") return () => snapshotFieldStateAt(segments);
|
|
901
|
+
if (key === "toString") return () => JSON.stringify(snapshotFieldStateAt(segments));
|
|
902
|
+
if (key === "valueOf")
|
|
903
|
+
return function() {
|
|
904
|
+
return this;
|
|
905
|
+
};
|
|
906
|
+
if (FIELD_STATE_KEYS.has(key)) {
|
|
907
|
+
const computed = getFieldStateAt(segments);
|
|
908
|
+
return computed.value[key];
|
|
909
|
+
}
|
|
910
|
+
return void 0;
|
|
911
|
+
},
|
|
912
|
+
has: (_, key) => typeof key === "string" && FIELD_STATE_KEYS.has(key),
|
|
913
|
+
ownKeys: () => Array.from(FIELD_STATE_KEYS),
|
|
914
|
+
getOwnPropertyDescriptor(_, key) {
|
|
915
|
+
if (typeof key !== "string") return void 0;
|
|
916
|
+
if (!FIELD_STATE_KEYS.has(key)) return void 0;
|
|
917
|
+
const computed = getFieldStateAt(segments);
|
|
918
|
+
return {
|
|
919
|
+
configurable: true,
|
|
920
|
+
enumerable: true,
|
|
921
|
+
value: computed.value[key],
|
|
922
|
+
writable: false
|
|
923
|
+
};
|
|
924
|
+
},
|
|
925
|
+
set: () => false,
|
|
926
|
+
deleteProperty: () => false,
|
|
927
|
+
defineProperty: () => false
|
|
928
|
+
});
|
|
929
|
+
terminalCache.set(cacheKey, proxy);
|
|
930
|
+
return proxy;
|
|
931
|
+
}
|
|
586
932
|
return buildSurfaceProxy({
|
|
587
933
|
schema: state.schema,
|
|
588
934
|
resolveLeaf: (path) => getFieldStateAt(path),
|
|
589
935
|
leafKeys: FIELD_STATE_KEYS,
|
|
590
936
|
readLeafKey: (computed, key) => computed.value[key],
|
|
591
|
-
materializeContainer: (segments) => materializeFields(state, segments, snapshotFieldStateAt)
|
|
937
|
+
materializeContainer: (segments) => materializeFields(state, segments, snapshotFieldStateAt),
|
|
938
|
+
resolveCallTarget: (path) => fieldStateTerminalAt(path)
|
|
592
939
|
});
|
|
593
940
|
}
|
|
594
941
|
function materializeFields(state, containerSegments, snapshotFieldStateAt) {
|
|
@@ -920,7 +1267,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
920
1267
|
}
|
|
921
1268
|
const genAtEntry = state.submissionGeneration.value;
|
|
922
1269
|
state.activeSubmissions.value += 1;
|
|
923
|
-
state.
|
|
1270
|
+
state.submitting.value = true;
|
|
924
1271
|
state.submitError.value = null;
|
|
925
1272
|
state.cancelFieldValidation();
|
|
926
1273
|
state.activeValidations.value += 1;
|
|
@@ -967,7 +1314,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
967
1314
|
}
|
|
968
1315
|
state.activeSubmissions.value = Math.max(0, state.activeSubmissions.value - 1);
|
|
969
1316
|
if (state.submissionGeneration.value === genAtEntry) {
|
|
970
|
-
state.
|
|
1317
|
+
state.submitting.value = state.activeSubmissions.value > 0;
|
|
971
1318
|
state.submitCount.value += 1;
|
|
972
1319
|
}
|
|
973
1320
|
}
|
|
@@ -1363,14 +1710,14 @@ function buildRegister(state, formInstanceId) {
|
|
|
1363
1710
|
segments,
|
|
1364
1711
|
state.coerceIndex
|
|
1365
1712
|
);
|
|
1366
|
-
if (persist && !state.
|
|
1713
|
+
if (persist && !state.ssr && !state.modules.has(PERSISTENCE_MODULE_KEY)) {
|
|
1367
1714
|
throw new AnonPersistError({
|
|
1368
1715
|
cause: "register-without-config",
|
|
1369
1716
|
schemaFields: extractSchemaFields(state.schema),
|
|
1370
1717
|
callSite: captureUserCallSite()
|
|
1371
1718
|
});
|
|
1372
1719
|
}
|
|
1373
|
-
|
|
1720
|
+
const internalRv = {
|
|
1374
1721
|
innerRef,
|
|
1375
1722
|
displayValue,
|
|
1376
1723
|
lastTypedForm,
|
|
@@ -1394,18 +1741,25 @@ function buildRegister(state, formInstanceId) {
|
|
|
1394
1741
|
},
|
|
1395
1742
|
// Called by the `vRegisterHint` compile-time transform's wrapping
|
|
1396
1743
|
// IIFE on every server-side render of `<element v-register="…">`.
|
|
1397
|
-
// Without it, every SSR'd FieldState serialises `
|
|
1744
|
+
// Without it, every SSR'd FieldState serialises `connected: false`
|
|
1398
1745
|
// (because Vue skips directive lifecycle during SSR) and the client
|
|
1399
1746
|
// briefly shows that stale flag until hydration runs the directive's
|
|
1400
|
-
// `created` hook. The mark only takes effect when `state.
|
|
1747
|
+
// `created` hook. The mark only takes effect when `state.ssr` is
|
|
1401
1748
|
// true; on the client this is a no-op so the directive lifecycle
|
|
1402
1749
|
// remains the source of truth.
|
|
1403
1750
|
markConnectedOptimistically: () => {
|
|
1404
1751
|
state.markConnectedOptimistically(segments);
|
|
1405
1752
|
},
|
|
1753
|
+
path: pathKey,
|
|
1754
|
+
// Frozen so a wrapper component can pass `rv.segments` directly
|
|
1755
|
+
// to `form.fields(...)` without defensive copying — and so test
|
|
1756
|
+
// fixtures or downstream code can't mutate the canonical
|
|
1757
|
+
// segment list out from under the directive.
|
|
1758
|
+
segments: Object.freeze(segments.slice()),
|
|
1759
|
+
formKey: state.formKey,
|
|
1760
|
+
formInstanceId,
|
|
1406
1761
|
// --- Persistence opt-in (internal; the directive is the only
|
|
1407
1762
|
// legitimate consumer) ---
|
|
1408
|
-
path: pathKey,
|
|
1409
1763
|
persist,
|
|
1410
1764
|
acknowledgeSensitive,
|
|
1411
1765
|
persistOptIns: state.persistOptIns,
|
|
@@ -1413,6 +1767,7 @@ function buildRegister(state, formInstanceId) {
|
|
|
1413
1767
|
coerce,
|
|
1414
1768
|
...coerceElement !== void 0 ? { coerceElement } : {}
|
|
1415
1769
|
};
|
|
1770
|
+
return shallowReadonly(internalRv);
|
|
1416
1771
|
};
|
|
1417
1772
|
}
|
|
1418
1773
|
|
|
@@ -1451,22 +1806,30 @@ function walk(input, segments, schema, paths) {
|
|
|
1451
1806
|
}
|
|
1452
1807
|
if (Array.isArray(input)) {
|
|
1453
1808
|
const out = new Array(input.length);
|
|
1809
|
+
let mutated = false;
|
|
1454
1810
|
for (let i = 0; i < input.length; i++) {
|
|
1455
|
-
|
|
1811
|
+
const walked = walk(input[i], [...segments, i], schema, paths);
|
|
1812
|
+
out[i] = walked;
|
|
1813
|
+
if (walked !== input[i]) mutated = true;
|
|
1456
1814
|
}
|
|
1457
|
-
return out;
|
|
1815
|
+
return mutated ? out : input;
|
|
1458
1816
|
}
|
|
1459
1817
|
if (typeof input === "object") {
|
|
1460
1818
|
const slim = schema.getDefaultAtPath(segments);
|
|
1461
|
-
const
|
|
1819
|
+
const inputKeys = Object.keys(input);
|
|
1820
|
+
const allKeys = new Set(inputKeys);
|
|
1462
1821
|
if (slim !== null && slim !== void 0 && typeof slim === "object" && !Array.isArray(slim) && !(slim instanceof Date) && !(slim instanceof RegExp) && !(slim instanceof Map) && !(slim instanceof Set)) {
|
|
1463
1822
|
for (const k of Object.keys(slim)) allKeys.add(k);
|
|
1464
1823
|
}
|
|
1465
1824
|
const out = {};
|
|
1825
|
+
let mutated = allKeys.size !== inputKeys.length;
|
|
1466
1826
|
for (const key of allKeys) {
|
|
1467
|
-
|
|
1827
|
+
const orig = input[key];
|
|
1828
|
+
const walked = walk(orig, [...segments, key], schema, paths);
|
|
1829
|
+
out[key] = walked;
|
|
1830
|
+
if (walked !== orig) mutated = true;
|
|
1468
1831
|
}
|
|
1469
|
-
return out;
|
|
1832
|
+
return mutated ? out : input;
|
|
1470
1833
|
}
|
|
1471
1834
|
return input;
|
|
1472
1835
|
}
|
|
@@ -1490,6 +1853,48 @@ function walkUnspecified(slim, segments, paths) {
|
|
|
1490
1853
|
}
|
|
1491
1854
|
return slim;
|
|
1492
1855
|
}
|
|
1856
|
+
function substituteUnsetSentinels(value, prefix, schema) {
|
|
1857
|
+
const paths = [];
|
|
1858
|
+
const cleaned = substitute(value, [...prefix], schema, paths);
|
|
1859
|
+
return { cleanedValues: cleaned, paths };
|
|
1860
|
+
}
|
|
1861
|
+
function substitute(input, segments, schema, paths) {
|
|
1862
|
+
if (isUnset(input)) {
|
|
1863
|
+
const slim = schema.getDefaultAtPath(segments);
|
|
1864
|
+
if (!isPrimitiveOrEmpty(slim)) {
|
|
1865
|
+
warnNonPrimitiveLeaf(segments, slim);
|
|
1866
|
+
return slim;
|
|
1867
|
+
}
|
|
1868
|
+
paths.push(canonicalizePath(segments).key);
|
|
1869
|
+
return slim;
|
|
1870
|
+
}
|
|
1871
|
+
if (input === void 0 || input === null) return input;
|
|
1872
|
+
if (input instanceof Date || input instanceof RegExp || input instanceof Map || input instanceof Set || typeof input === "function") {
|
|
1873
|
+
return input;
|
|
1874
|
+
}
|
|
1875
|
+
if (Array.isArray(input)) {
|
|
1876
|
+
let mutated = false;
|
|
1877
|
+
const out = new Array(input.length);
|
|
1878
|
+
for (let i = 0; i < input.length; i++) {
|
|
1879
|
+
const walked = substitute(input[i], [...segments, i], schema, paths);
|
|
1880
|
+
out[i] = walked;
|
|
1881
|
+
if (walked !== input[i]) mutated = true;
|
|
1882
|
+
}
|
|
1883
|
+
return mutated ? out : input;
|
|
1884
|
+
}
|
|
1885
|
+
if (typeof input === "object") {
|
|
1886
|
+
let mutated = false;
|
|
1887
|
+
const out = {};
|
|
1888
|
+
for (const key of Object.keys(input)) {
|
|
1889
|
+
const orig = input[key];
|
|
1890
|
+
const walked = substitute(orig, [...segments, key], schema, paths);
|
|
1891
|
+
out[key] = walked;
|
|
1892
|
+
if (walked !== orig) mutated = true;
|
|
1893
|
+
}
|
|
1894
|
+
return mutated ? out : input;
|
|
1895
|
+
}
|
|
1896
|
+
return input;
|
|
1897
|
+
}
|
|
1493
1898
|
function isPrimitiveOrEmpty(value) {
|
|
1494
1899
|
if (value === null || value === void 0) return true;
|
|
1495
1900
|
const t = typeof value;
|
|
@@ -1598,14 +2003,14 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
1598
2003
|
}
|
|
1599
2004
|
function setValueImpl(pathOrValue, maybeValue) {
|
|
1600
2005
|
if (arguments.length === 1) {
|
|
1601
|
-
const next = typeof pathOrValue === "function" ? pathOrValue(state.form.value) : pathOrValue;
|
|
1602
|
-
const
|
|
2006
|
+
const next = typeof pathOrValue === "function" ? pathOrValue(structuralSnapshot(state.form.value)) : pathOrValue;
|
|
2007
|
+
const walked2 = walkUnsetSentinels(
|
|
1603
2008
|
next,
|
|
1604
2009
|
state.schema
|
|
1605
2010
|
);
|
|
1606
|
-
const
|
|
1607
|
-
if (!
|
|
1608
|
-
for (const pathKey of
|
|
2011
|
+
const ok2 = state.setValueAtPath([], walked2.cleanedValues);
|
|
2012
|
+
if (!ok2) return false;
|
|
2013
|
+
for (const pathKey of walked2.paths) {
|
|
1609
2014
|
const segments2 = segmentsForPathKey(pathKey);
|
|
1610
2015
|
if (segments2 === null) continue;
|
|
1611
2016
|
state.setValueAtPath(segments2, state.schema.getDefaultAtPath(segments2), {
|
|
@@ -1633,7 +2038,21 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
1633
2038
|
} else {
|
|
1634
2039
|
resolvedValue = maybeValue;
|
|
1635
2040
|
}
|
|
1636
|
-
|
|
2041
|
+
const walked = substituteUnsetSentinels(
|
|
2042
|
+
resolvedValue,
|
|
2043
|
+
segments,
|
|
2044
|
+
state.schema
|
|
2045
|
+
);
|
|
2046
|
+
const ok = state.setValueAtPath(segments, walked.cleanedValues);
|
|
2047
|
+
if (!ok) return false;
|
|
2048
|
+
for (const pathKey of walked.paths) {
|
|
2049
|
+
const blankSegments = segmentsForPathKey(pathKey);
|
|
2050
|
+
if (blankSegments === null) continue;
|
|
2051
|
+
state.setValueAtPath(blankSegments, state.schema.getDefaultAtPath(blankSegments), {
|
|
2052
|
+
blank: true
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
2055
|
+
return true;
|
|
1637
2056
|
}
|
|
1638
2057
|
const errorsProxy = buildErrorsProxy(state);
|
|
1639
2058
|
function setFieldErrors(errors) {
|
|
@@ -1652,58 +2071,89 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
1652
2071
|
state.clearSchemaErrors(segments);
|
|
1653
2072
|
state.clearUserErrors(segments);
|
|
1654
2073
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
if (state.blankPaths.size !== state.originalBlankPaths.size) return true;
|
|
1660
|
-
for (const key of state.blankPaths) {
|
|
1661
|
-
if (!state.originalBlankPaths.has(key)) return true;
|
|
2074
|
+
function setFormErrors(errors) {
|
|
2075
|
+
if (errors.length === 0) {
|
|
2076
|
+
state.userErrors.delete(ROOT_PATH_KEY);
|
|
2077
|
+
return;
|
|
1662
2078
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
2079
|
+
state.userErrors.set(
|
|
2080
|
+
ROOT_PATH_KEY,
|
|
2081
|
+
errors.map((e) => ({
|
|
2082
|
+
path: [],
|
|
2083
|
+
message: e.message,
|
|
2084
|
+
formKey: state.formKey,
|
|
2085
|
+
code: e.code ?? "atta:form-error"
|
|
2086
|
+
}))
|
|
2087
|
+
);
|
|
2088
|
+
}
|
|
2089
|
+
function clearFormErrors() {
|
|
2090
|
+
state.userErrors.delete(ROOT_PATH_KEY);
|
|
2091
|
+
}
|
|
2092
|
+
const submitting = computed(() => state.submitting.value);
|
|
1669
2093
|
const submitCount = computed(() => state.submitCount.value);
|
|
1670
2094
|
const submitError = computed(() => state.submitError.value);
|
|
1671
|
-
const
|
|
2095
|
+
const validating = computed(() => state.activeValidations.value > 0);
|
|
2096
|
+
const valid = computed(
|
|
2097
|
+
() => state.firstValidationDone.value && state.schemaErrors.size === 0 && state.userErrors.size === 0 && state.derivedBlankErrors.value.size === 0 && !validating.value
|
|
2098
|
+
);
|
|
1672
2099
|
const history = options.history;
|
|
1673
2100
|
const undo = history?.undo ?? (() => false);
|
|
1674
2101
|
const redo = history?.redo ?? (() => false);
|
|
1675
2102
|
const canUndo = history?.canUndo ?? computed(() => false);
|
|
1676
2103
|
const canRedo = history?.canRedo ?? computed(() => false);
|
|
1677
2104
|
const historySize = history?.historySize ?? computed(() => 0);
|
|
1678
|
-
const metaErrors = computed(
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
const ordinal = state.ensurePathOrdinal(pathKey);
|
|
1684
|
-
const existing = buckets.get(ordinal);
|
|
1685
|
-
if (existing === void 0) buckets.set(ordinal, [...list]);
|
|
1686
|
-
else existing.push(...list);
|
|
1687
|
-
}
|
|
1688
|
-
};
|
|
1689
|
-
collect(state.schemaErrors);
|
|
1690
|
-
collect(state.derivedBlankErrors.value);
|
|
1691
|
-
collect(state.userErrors);
|
|
1692
|
-
if (buckets.size === 0) return [];
|
|
1693
|
-
return [...buckets.entries()].sort(([a], [b]) => a - b).flatMap(([, errs]) => errs);
|
|
1694
|
-
});
|
|
2105
|
+
const metaErrors = computed(
|
|
2106
|
+
() => aggregateErrorsAt(state, [])
|
|
2107
|
+
);
|
|
2108
|
+
const getRootFieldStateAt = buildFieldStateAccessor(state);
|
|
2109
|
+
const rootFieldState = getRootFieldStateAt([]);
|
|
1695
2110
|
const formMeta = readonly(
|
|
1696
2111
|
reactive({
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
2112
|
+
// FieldState fields — read through one shared root computed. Each
|
|
2113
|
+
// property accesses `rootFieldState.value[X]`, so any descendant
|
|
2114
|
+
// change re-evaluates the root computed once (Vue's reactive
|
|
2115
|
+
// graph dedupes the dependent re-renders).
|
|
2116
|
+
value: computed(() => rootFieldState.value.value),
|
|
2117
|
+
original: computed(() => rootFieldState.value.original),
|
|
2118
|
+
pristine: computed(() => rootFieldState.value.pristine),
|
|
2119
|
+
dirty: computed(() => rootFieldState.value.dirty),
|
|
2120
|
+
focused: computed(() => rootFieldState.value.focused),
|
|
2121
|
+
blurred: computed(() => rootFieldState.value.blurred),
|
|
2122
|
+
touched: computed(() => rootFieldState.value.touched),
|
|
2123
|
+
connected: computed(() => rootFieldState.value.connected),
|
|
2124
|
+
element: computed(() => rootFieldState.value.element),
|
|
2125
|
+
elements: computed(() => rootFieldState.value.elements),
|
|
2126
|
+
updatedAt: computed(() => rootFieldState.value.updatedAt),
|
|
2127
|
+
// Whole-form validating mirrors the LIFECYCLE counter
|
|
2128
|
+
// (`state.activeValidations`) ORed with any per-leaf validation
|
|
2129
|
+
// in flight (via `rootFieldState.validating`). A submit-time
|
|
2130
|
+
// validate run shows up as activeValidations; per-field
|
|
2131
|
+
// debounced validators show up as fieldValidationCounts. Either
|
|
2132
|
+
// flips the flag.
|
|
2133
|
+
validating: computed(
|
|
2134
|
+
() => state.activeValidations.value > 0 || rootFieldState.value.validating
|
|
2135
|
+
),
|
|
2136
|
+
// Whole-form valid keeps the original `firstValidationDone`
|
|
2137
|
+
// mount gate so the surface doesn't lie about a yet-to-arrive
|
|
2138
|
+
// verdict at construction time. The shared `aggregateErrorsAt`
|
|
2139
|
+
// ensures `form.meta.errors` and `rootFieldState.errors` match,
|
|
2140
|
+
// so `errors.length === 0` here would agree with `valid` —
|
|
2141
|
+
// keep the explicit form-level computation for the gate.
|
|
2142
|
+
valid,
|
|
2143
|
+
errors: metaErrors,
|
|
2144
|
+
path: computed(() => rootFieldState.value.path),
|
|
2145
|
+
blank: computed(() => rootFieldState.value.blank),
|
|
2146
|
+
label: computed(() => rootFieldState.value.label),
|
|
2147
|
+
description: computed(() => rootFieldState.value.description),
|
|
2148
|
+
placeholder: computed(() => rootFieldState.value.placeholder),
|
|
2149
|
+
meta: computed(() => rootFieldState.value.meta),
|
|
2150
|
+
// Lifecycle (form-level only — not on FieldState).
|
|
2151
|
+
submitting,
|
|
1701
2152
|
submitCount,
|
|
1702
2153
|
submitError,
|
|
1703
2154
|
canUndo,
|
|
1704
2155
|
canRedo,
|
|
1705
2156
|
historySize,
|
|
1706
|
-
errors: metaErrors,
|
|
1707
2157
|
// Per-`useForm()`-call identity. Stable for one mount; new on
|
|
1708
2158
|
// re-mount; orthogonal to `form.key` (which is the user-supplied
|
|
1709
2159
|
// shared identifier). Useful for devtools panels disambiguating
|
|
@@ -1794,6 +2244,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
1794
2244
|
setFieldErrors,
|
|
1795
2245
|
addFieldErrors,
|
|
1796
2246
|
clearFieldErrors,
|
|
2247
|
+
setFormErrors,
|
|
2248
|
+
clearFormErrors,
|
|
1797
2249
|
meta: formMeta,
|
|
1798
2250
|
reset,
|
|
1799
2251
|
resetField,
|
|
@@ -1814,103 +2266,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
1814
2266
|
};
|
|
1815
2267
|
}
|
|
1816
2268
|
|
|
1817
|
-
function isDescendable(value) {
|
|
1818
|
-
if (value === null || typeof value !== "object") return false;
|
|
1819
|
-
if (Array.isArray(value)) return true;
|
|
1820
|
-
const proto = Object.getPrototypeOf(value);
|
|
1821
|
-
return proto === null || proto === Object.prototype;
|
|
1822
|
-
}
|
|
1823
|
-
function appendSegment(prefix, segment) {
|
|
1824
|
-
const next = new Array(prefix.length + 1);
|
|
1825
|
-
for (let i = 0; i < prefix.length; i++) {
|
|
1826
|
-
const s = prefix[i];
|
|
1827
|
-
next[i] = s;
|
|
1828
|
-
}
|
|
1829
|
-
next[prefix.length] = segment;
|
|
1830
|
-
return next;
|
|
1831
|
-
}
|
|
1832
|
-
function diffAndApply(oldValue, newValue, prefix, visit) {
|
|
1833
|
-
if (Object.is(oldValue, newValue)) return;
|
|
1834
|
-
const oldIsDescendable = isDescendable(oldValue);
|
|
1835
|
-
const newIsDescendable = isDescendable(newValue);
|
|
1836
|
-
if (oldValue === void 0 && newIsDescendable) {
|
|
1837
|
-
if (Array.isArray(newValue)) {
|
|
1838
|
-
for (let i = 0; i < newValue.length; i++) {
|
|
1839
|
-
diffAndApply(void 0, newValue[i], appendSegment(prefix, i), visit);
|
|
1840
|
-
}
|
|
1841
|
-
} else {
|
|
1842
|
-
const rec = newValue;
|
|
1843
|
-
for (const k of Object.keys(rec)) {
|
|
1844
|
-
diffAndApply(void 0, rec[k], appendSegment(prefix, k), visit);
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
return;
|
|
1848
|
-
}
|
|
1849
|
-
if (oldIsDescendable && newValue === void 0) {
|
|
1850
|
-
if (Array.isArray(oldValue)) {
|
|
1851
|
-
for (let i = 0; i < oldValue.length; i++) {
|
|
1852
|
-
diffAndApply(oldValue[i], void 0, appendSegment(prefix, i), visit);
|
|
1853
|
-
}
|
|
1854
|
-
} else {
|
|
1855
|
-
const rec = oldValue;
|
|
1856
|
-
for (const k of Object.keys(rec)) {
|
|
1857
|
-
diffAndApply(rec[k], void 0, appendSegment(prefix, k), visit);
|
|
1858
|
-
}
|
|
1859
|
-
}
|
|
1860
|
-
return;
|
|
1861
|
-
}
|
|
1862
|
-
if (oldIsDescendable && newIsDescendable) {
|
|
1863
|
-
const oldIsArray = Array.isArray(oldValue);
|
|
1864
|
-
const newIsArray = Array.isArray(newValue);
|
|
1865
|
-
if (oldIsArray && newIsArray) {
|
|
1866
|
-
const oldArr = oldValue;
|
|
1867
|
-
const newArr = newValue;
|
|
1868
|
-
const max = Math.max(oldArr.length, newArr.length);
|
|
1869
|
-
for (let i = 0; i < max; i++) {
|
|
1870
|
-
diffAndApply(oldArr[i], newArr[i], appendSegment(prefix, i), visit);
|
|
1871
|
-
}
|
|
1872
|
-
return;
|
|
1873
|
-
}
|
|
1874
|
-
if (!oldIsArray && !newIsArray) {
|
|
1875
|
-
const oldRec = oldValue;
|
|
1876
|
-
const newRec = newValue;
|
|
1877
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1878
|
-
for (const k of Object.keys(oldRec)) {
|
|
1879
|
-
seen.add(k);
|
|
1880
|
-
diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
|
|
1881
|
-
}
|
|
1882
|
-
for (const k of Object.keys(newRec)) {
|
|
1883
|
-
if (seen.has(k)) continue;
|
|
1884
|
-
diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
|
|
1885
|
-
}
|
|
1886
|
-
return;
|
|
1887
|
-
}
|
|
1888
|
-
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
1889
|
-
return;
|
|
1890
|
-
}
|
|
1891
|
-
if (oldIsDescendable && !newIsDescendable) {
|
|
1892
|
-
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
1893
|
-
return;
|
|
1894
|
-
}
|
|
1895
|
-
if (!oldIsDescendable && newIsDescendable) {
|
|
1896
|
-
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
1897
|
-
return;
|
|
1898
|
-
}
|
|
1899
|
-
if (oldValue === void 0) {
|
|
1900
|
-
visit({ kind: "added", path: prefix, newValue });
|
|
1901
|
-
return;
|
|
1902
|
-
}
|
|
1903
|
-
if (newValue === void 0) {
|
|
1904
|
-
visit({ kind: "removed", path: prefix, oldValue });
|
|
1905
|
-
return;
|
|
1906
|
-
}
|
|
1907
|
-
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
1908
|
-
}
|
|
1909
|
-
|
|
1910
2269
|
function isHydratedFieldRecord(value) {
|
|
1911
2270
|
if (typeof value !== "object" || value === null) return false;
|
|
1912
2271
|
const r = value;
|
|
1913
|
-
return Array.isArray(r.path) && (typeof r.updatedAt === "string" || r.updatedAt === null) && typeof r.
|
|
2272
|
+
return Array.isArray(r.path) && (typeof r.updatedAt === "string" || r.updatedAt === null) && typeof r.connected === "boolean" && (typeof r.focused === "boolean" || r.focused === null) && (typeof r.blurred === "boolean" || r.blurred === null) && (typeof r.touched === "boolean" || r.touched === null);
|
|
1914
2273
|
}
|
|
1915
2274
|
function isHydratedValidationErrorArray(value) {
|
|
1916
2275
|
if (!Array.isArray(value)) return false;
|
|
@@ -1941,7 +2300,7 @@ function isPathKeyUnder(existingKey, parentPath) {
|
|
|
1941
2300
|
}
|
|
1942
2301
|
function createFormStore(options) {
|
|
1943
2302
|
const { formKey, schema, defaultValues, strict = true, hydration } = options;
|
|
1944
|
-
const
|
|
2303
|
+
const ssr = options.ssr === true;
|
|
1945
2304
|
const rememberVariants = options.rememberVariants !== false;
|
|
1946
2305
|
const fieldValidationMode = options.validateOn ?? "change";
|
|
1947
2306
|
const fieldValidationDebounceMs = options.debounceMs ?? DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS;
|
|
@@ -1960,7 +2319,7 @@ function createFormStore(options) {
|
|
|
1960
2319
|
strict
|
|
1961
2320
|
});
|
|
1962
2321
|
const schemaInitialData = schemaResponse.data;
|
|
1963
|
-
const initialData = hydration !== void 0 ? hydration.form : schemaInitialData;
|
|
2322
|
+
const initialData = hydration !== void 0 ? hydration.form : structuralSnapshot(schemaInitialData);
|
|
1964
2323
|
const form = ref(initialData);
|
|
1965
2324
|
const fields = reactive(/* @__PURE__ */ new Map());
|
|
1966
2325
|
const elements = reactive(/* @__PURE__ */ new Map());
|
|
@@ -1968,7 +2327,7 @@ function createFormStore(options) {
|
|
|
1968
2327
|
let sortedRegistrationsCache = null;
|
|
1969
2328
|
const schemaErrors = reactive(/* @__PURE__ */ new Map());
|
|
1970
2329
|
const userErrors = reactive(/* @__PURE__ */ new Map());
|
|
1971
|
-
const originals = /* @__PURE__ */ new Map();
|
|
2330
|
+
const originals = reactive(/* @__PURE__ */ new Map());
|
|
1972
2331
|
const initialTransientList = hydration?.blankPaths ?? options.initialBlankPaths ?? [];
|
|
1973
2332
|
const blankPaths = reactive(/* @__PURE__ */ new Set());
|
|
1974
2333
|
const originalBlankPaths = /* @__PURE__ */ new Set();
|
|
@@ -2006,12 +2365,37 @@ function createFormStore(options) {
|
|
|
2006
2365
|
}
|
|
2007
2366
|
return result;
|
|
2008
2367
|
});
|
|
2009
|
-
const
|
|
2368
|
+
const submitting = ref(false);
|
|
2010
2369
|
const activeSubmissions = ref(0);
|
|
2011
2370
|
const submitCount = ref(0);
|
|
2012
2371
|
const submitError = ref(null);
|
|
2013
2372
|
const submissionGeneration = ref(0);
|
|
2014
2373
|
const activeValidations = ref(0);
|
|
2374
|
+
const firstValidationDone = ref(!strict || schema.needsAsyncValidation?.() !== true);
|
|
2375
|
+
watch(activeValidations, (now, prev) => {
|
|
2376
|
+
if (prev > 0 && now === 0) {
|
|
2377
|
+
firstValidationDone.value = true;
|
|
2378
|
+
}
|
|
2379
|
+
});
|
|
2380
|
+
const pathAsyncCache = /* @__PURE__ */ new Map();
|
|
2381
|
+
function pathHasAsyncValidation(path) {
|
|
2382
|
+
const { key } = canonicalizePath(path);
|
|
2383
|
+
const cached = pathAsyncCache.get(key);
|
|
2384
|
+
if (cached !== void 0) return cached;
|
|
2385
|
+
const candidates = schema.getSchemasAtPath(path);
|
|
2386
|
+
const hasAsync = candidates.some((sub) => sub.needsAsyncValidation?.() === true);
|
|
2387
|
+
pathAsyncCache.set(key, hasAsync);
|
|
2388
|
+
return hasAsync;
|
|
2389
|
+
}
|
|
2390
|
+
const fieldValidationCounts = reactive(/* @__PURE__ */ new Map());
|
|
2391
|
+
function incFieldValidation(key) {
|
|
2392
|
+
fieldValidationCounts.set(key, (fieldValidationCounts.get(key) ?? 0) + 1);
|
|
2393
|
+
}
|
|
2394
|
+
function decFieldValidation(key) {
|
|
2395
|
+
const next = (fieldValidationCounts.get(key) ?? 0) - 1;
|
|
2396
|
+
if (next <= 0) fieldValidationCounts.delete(key);
|
|
2397
|
+
else fieldValidationCounts.set(key, next);
|
|
2398
|
+
}
|
|
2015
2399
|
const initStamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2016
2400
|
diffAndApply({}, schemaInitialData, [], (patch) => {
|
|
2017
2401
|
if (patch.kind !== "added") return;
|
|
@@ -2048,7 +2432,7 @@ function createFormStore(options) {
|
|
|
2048
2432
|
fields.set(key, {
|
|
2049
2433
|
path: patch.path,
|
|
2050
2434
|
updatedAt: initStamp,
|
|
2051
|
-
|
|
2435
|
+
connected: false,
|
|
2052
2436
|
focused: null,
|
|
2053
2437
|
blurred: null,
|
|
2054
2438
|
touched: null
|
|
@@ -2058,7 +2442,7 @@ function createFormStore(options) {
|
|
|
2058
2442
|
setAllSchemaErrors(schemaResponse.errors);
|
|
2059
2443
|
}
|
|
2060
2444
|
}
|
|
2061
|
-
if (!
|
|
2445
|
+
if (!ssr && strict && schema.needsAsyncValidation?.() === true) {
|
|
2062
2446
|
queueMicrotask(() => scheduleFieldValidation(
|
|
2063
2447
|
[],
|
|
2064
2448
|
true
|
|
@@ -2070,7 +2454,7 @@ function createFormStore(options) {
|
|
|
2070
2454
|
fields.set(pathKey, {
|
|
2071
2455
|
path,
|
|
2072
2456
|
updatedAt: patch.updatedAt ?? current?.updatedAt ?? null,
|
|
2073
|
-
|
|
2457
|
+
connected: patch.connected ?? current?.connected ?? false,
|
|
2074
2458
|
focused: patch.focused ?? current?.focused ?? null,
|
|
2075
2459
|
blurred: patch.blurred ?? current?.blurred ?? null,
|
|
2076
2460
|
touched: patch.touched ?? current?.touched ?? null
|
|
@@ -2079,18 +2463,24 @@ function createFormStore(options) {
|
|
|
2079
2463
|
function applyFormReplacement(next, meta) {
|
|
2080
2464
|
const prev = form.value;
|
|
2081
2465
|
if (Object.is(prev, next)) return;
|
|
2082
|
-
form.value = next;
|
|
2083
2466
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2467
|
+
const patches = [];
|
|
2084
2468
|
diffAndApply(prev, next, [], (patch) => {
|
|
2469
|
+
patches.push(patch);
|
|
2470
|
+
});
|
|
2471
|
+
if (!applyChangedKeys(prev, next)) {
|
|
2472
|
+
form.value = next;
|
|
2473
|
+
}
|
|
2474
|
+
for (const patch of patches) {
|
|
2085
2475
|
const { key } = canonicalizePath(patch.path);
|
|
2086
2476
|
if (patch.kind === "added" && !originals.has(key)) {
|
|
2087
2477
|
originals.set(key, { segments: patch.path, value: void 0 });
|
|
2088
2478
|
}
|
|
2089
2479
|
touchFieldRecord(key, patch.path, { updatedAt: now });
|
|
2090
|
-
}
|
|
2480
|
+
}
|
|
2091
2481
|
for (const listener of formChangeListeners) {
|
|
2092
2482
|
try {
|
|
2093
|
-
listener(
|
|
2483
|
+
listener(form.value, meta);
|
|
2094
2484
|
} catch (err) {
|
|
2095
2485
|
console.error("[attaform] onFormChange threw:", err);
|
|
2096
2486
|
}
|
|
@@ -2264,6 +2654,7 @@ function createFormStore(options) {
|
|
|
2264
2654
|
if (controller.signal.aborted) return;
|
|
2265
2655
|
const data = getAtPath(form.value, path);
|
|
2266
2656
|
activeValidations.value += 1;
|
|
2657
|
+
incFieldValidation(key);
|
|
2267
2658
|
void Promise.resolve().then(() => schema.validateAtPath(data, path)).then((response) => {
|
|
2268
2659
|
if (controller.signal.aborted) return;
|
|
2269
2660
|
const reStamped = response.success ? [] : response.errors.map((err) => ({
|
|
@@ -2274,6 +2665,7 @@ function createFormStore(options) {
|
|
|
2274
2665
|
}).catch(() => {
|
|
2275
2666
|
}).finally(() => {
|
|
2276
2667
|
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
2668
|
+
decFieldValidation(key);
|
|
2277
2669
|
});
|
|
2278
2670
|
};
|
|
2279
2671
|
if (immediate || fieldValidationDebounceMs === 0) {
|
|
@@ -2429,15 +2821,16 @@ function createFormStore(options) {
|
|
|
2429
2821
|
function registerElement(path, element, formInstanceId) {
|
|
2430
2822
|
const { key } = canonicalizePath(path);
|
|
2431
2823
|
const record = elements.get(key);
|
|
2824
|
+
const raw = markRaw(element);
|
|
2432
2825
|
if (record === void 0) {
|
|
2433
|
-
elements.set(key, { path, elements: /* @__PURE__ */ new Set([
|
|
2826
|
+
elements.set(key, { path, elements: reactive(/* @__PURE__ */ new Set([raw])) });
|
|
2434
2827
|
} else {
|
|
2435
|
-
if (record.elements.has(
|
|
2436
|
-
record.elements.add(
|
|
2828
|
+
if (record.elements.has(raw)) return false;
|
|
2829
|
+
record.elements.add(raw);
|
|
2437
2830
|
}
|
|
2438
2831
|
elementToFormInstance.set(element, formInstanceId);
|
|
2439
2832
|
sortedRegistrationsCache = null;
|
|
2440
|
-
touchFieldRecord(key, path, {
|
|
2833
|
+
touchFieldRecord(key, path, { connected: true });
|
|
2441
2834
|
return true;
|
|
2442
2835
|
}
|
|
2443
2836
|
function deregisterElement(path, element) {
|
|
@@ -2452,16 +2845,16 @@ function createFormStore(options) {
|
|
|
2452
2845
|
const remaining = record.elements.size;
|
|
2453
2846
|
if (remaining === 0) {
|
|
2454
2847
|
elements.delete(key);
|
|
2455
|
-
touchFieldRecord(key, path, {
|
|
2848
|
+
touchFieldRecord(key, path, { connected: false });
|
|
2456
2849
|
}
|
|
2457
2850
|
return remaining;
|
|
2458
2851
|
}
|
|
2459
2852
|
function markConnectedOptimistically(path) {
|
|
2460
|
-
if (!
|
|
2853
|
+
if (!ssr) return;
|
|
2461
2854
|
const { key } = canonicalizePath(path);
|
|
2462
2855
|
const current = fields.get(key);
|
|
2463
|
-
if (current?.
|
|
2464
|
-
touchFieldRecord(key, path, {
|
|
2856
|
+
if (current?.connected === true) return;
|
|
2857
|
+
touchFieldRecord(key, path, { connected: true });
|
|
2465
2858
|
}
|
|
2466
2859
|
function markFocused(path, focused) {
|
|
2467
2860
|
const { key } = canonicalizePath(path);
|
|
@@ -2513,14 +2906,14 @@ function createFormStore(options) {
|
|
|
2513
2906
|
fields.set(pathKey, {
|
|
2514
2907
|
path: record.path,
|
|
2515
2908
|
updatedAt: now,
|
|
2516
|
-
|
|
2909
|
+
connected: record.connected,
|
|
2517
2910
|
focused: null,
|
|
2518
2911
|
blurred: null,
|
|
2519
2912
|
touched: null
|
|
2520
2913
|
});
|
|
2521
2914
|
}
|
|
2522
2915
|
submissionGeneration.value += 1;
|
|
2523
|
-
|
|
2916
|
+
submitting.value = false;
|
|
2524
2917
|
activeSubmissions.value = 0;
|
|
2525
2918
|
submitCount.value = 0;
|
|
2526
2919
|
submitError.value = null;
|
|
@@ -2597,7 +2990,7 @@ function createFormStore(options) {
|
|
|
2597
2990
|
fields.set(pathKey, {
|
|
2598
2991
|
path: record.path,
|
|
2599
2992
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2600
|
-
|
|
2993
|
+
connected: record.connected,
|
|
2601
2994
|
focused: null,
|
|
2602
2995
|
blurred: null,
|
|
2603
2996
|
touched: null
|
|
@@ -2659,13 +3052,16 @@ function createFormStore(options) {
|
|
|
2659
3052
|
derivedBlankErrors,
|
|
2660
3053
|
originals,
|
|
2661
3054
|
schema,
|
|
2662
|
-
|
|
2663
|
-
|
|
3055
|
+
ssr,
|
|
3056
|
+
submitting,
|
|
2664
3057
|
activeSubmissions,
|
|
2665
3058
|
submitCount,
|
|
2666
3059
|
submitError,
|
|
2667
3060
|
submissionGeneration,
|
|
2668
3061
|
activeValidations,
|
|
3062
|
+
firstValidationDone,
|
|
3063
|
+
pathHasAsyncValidation,
|
|
3064
|
+
fieldValidationCounts,
|
|
2669
3065
|
applyFormReplacement,
|
|
2670
3066
|
setValueAtPath,
|
|
2671
3067
|
getValueAtPath,
|
|
@@ -2718,7 +3114,7 @@ function createHistoryModule(state, config) {
|
|
|
2718
3114
|
let suppressNext = false;
|
|
2719
3115
|
function captureSnapshot() {
|
|
2720
3116
|
return {
|
|
2721
|
-
form: state.form.value,
|
|
3117
|
+
form: structuralSnapshot(state.form.value),
|
|
2722
3118
|
blankPaths: [...state.blankPaths],
|
|
2723
3119
|
schemaErrors: [...state.schemaErrors.entries()].map(([k, v]) => [k, [...v]]),
|
|
2724
3120
|
userErrors: [...state.userErrors.entries()].map(([k, v]) => [k, [...v]])
|
|
@@ -2825,8 +3221,8 @@ function useAbstractForm(configuration) {
|
|
|
2825
3221
|
const releaseConsumer = registry.trackConsumer(key);
|
|
2826
3222
|
onScopeDispose(releaseConsumer);
|
|
2827
3223
|
}
|
|
2828
|
-
const persistDisabledByAnonRule = merged.persist !== void 0 && enforceAnonPersistRule(state.formKey, registry.
|
|
2829
|
-
if (existing === void 0 && !registry.
|
|
3224
|
+
const persistDisabledByAnonRule = merged.persist !== void 0 && enforceAnonPersistRule(state.formKey, registry.ssr);
|
|
3225
|
+
if (existing === void 0 && !registry.ssr) {
|
|
2830
3226
|
if (merged.persist !== void 0 && !persistDisabledByAnonRule) {
|
|
2831
3227
|
const resolvedPersist = normalizePersistConfig(merged.persist);
|
|
2832
3228
|
const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
|
|
@@ -2845,7 +3241,7 @@ function useAbstractForm(configuration) {
|
|
|
2845
3241
|
state.registerCleanup(() => historyModule.dispose());
|
|
2846
3242
|
}
|
|
2847
3243
|
if (configuration.key === void 0) {
|
|
2848
|
-
recordAmbientProvide(registry.
|
|
3244
|
+
recordAmbientProvide(registry.ssr);
|
|
2849
3245
|
provide(kFormContext, state);
|
|
2850
3246
|
}
|
|
2851
3247
|
const formInstanceId = getCurrentInstance() !== null ? useId() : `atta:form-instance:${formInstanceCounter++}`;
|
|
@@ -2898,7 +3294,7 @@ function buildFreshState(key, schema, configuration, registry) {
|
|
|
2898
3294
|
hydration: pending,
|
|
2899
3295
|
...configuration.validateOn !== void 0 ? { validateOn: configuration.validateOn } : {},
|
|
2900
3296
|
...configuration.debounceMs !== void 0 ? { debounceMs: configuration.debounceMs } : {},
|
|
2901
|
-
|
|
3297
|
+
ssr: registry.ssr,
|
|
2902
3298
|
...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
|
|
2903
3299
|
...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
|
|
2904
3300
|
...initialBlankPaths !== void 0 ? { initialBlankPaths } : {}
|
|
@@ -2913,8 +3309,8 @@ function buildFreshState(key, schema, configuration, registry) {
|
|
|
2913
3309
|
let anonCounter = 0;
|
|
2914
3310
|
let formInstanceCounter = 0;
|
|
2915
3311
|
const ambientProvideHistory = __DEV__ ? /* @__PURE__ */ new WeakMap() : null;
|
|
2916
|
-
function recordAmbientProvide(
|
|
2917
|
-
if (!__DEV__ ||
|
|
3312
|
+
function recordAmbientProvide(ssr) {
|
|
3313
|
+
if (!__DEV__ || ssr || ambientProvideHistory === null) return;
|
|
2918
3314
|
const instance = getCurrentInstance();
|
|
2919
3315
|
if (instance === null) return;
|
|
2920
3316
|
const instanceKey = instance;
|
|
@@ -3176,14 +3572,14 @@ function isEmptyContainer(value) {
|
|
|
3176
3572
|
return false;
|
|
3177
3573
|
}
|
|
3178
3574
|
const warnedAnonPersistKeys = /* @__PURE__ */ new Set();
|
|
3179
|
-
function enforceAnonPersistRule(formKey,
|
|
3575
|
+
function enforceAnonPersistRule(formKey, ssr) {
|
|
3180
3576
|
if (!formKey.startsWith(ANONYMOUS_FORM_KEY_PREFIX)) return false;
|
|
3181
3577
|
if (__DEV__)
|
|
3182
3578
|
throw new AnonPersistError({
|
|
3183
3579
|
cause: "no-key",
|
|
3184
3580
|
callSite: captureUserCallSite()
|
|
3185
3581
|
});
|
|
3186
|
-
if (!
|
|
3582
|
+
if (!ssr && !warnedAnonPersistKeys.has(formKey)) {
|
|
3187
3583
|
warnedAnonPersistKeys.add(formKey);
|
|
3188
3584
|
console.warn(
|
|
3189
3585
|
"[attaform] persist: ignored \u2014 anonymous useForm() can't safely persist (key drift + cross-form collision risk).\n Persistence is disabled for this form; the app keeps working.\n Fix: useForm({ schema, key: 'login', persist: '...' })"
|
|
@@ -3241,21 +3637,21 @@ function resolveState(key, registry) {
|
|
|
3241
3637
|
if (key !== void 0) {
|
|
3242
3638
|
const stored = registry.forms.get(key);
|
|
3243
3639
|
if (stored === void 0) {
|
|
3244
|
-
warnMiss(`no form registered for key '${key}'`, registry.
|
|
3640
|
+
warnMiss(`no form registered for key '${key}'`, registry.ssr);
|
|
3245
3641
|
return null;
|
|
3246
3642
|
}
|
|
3247
3643
|
return stored;
|
|
3248
3644
|
}
|
|
3249
3645
|
const ambient = inject(kFormContext, null);
|
|
3250
3646
|
if (ambient === null) {
|
|
3251
|
-
warnMiss("no ambient form context", registry.
|
|
3647
|
+
warnMiss("no ambient form context", registry.ssr);
|
|
3252
3648
|
return null;
|
|
3253
3649
|
}
|
|
3254
3650
|
warnIfAmbientProviderHadDuplicates();
|
|
3255
3651
|
return ambient;
|
|
3256
3652
|
}
|
|
3257
|
-
function warnMiss(detail,
|
|
3258
|
-
if (!__DEV__ ||
|
|
3653
|
+
function warnMiss(detail, ssr) {
|
|
3654
|
+
if (!__DEV__ || ssr) return;
|
|
3259
3655
|
const frame = captureUserCallSite();
|
|
3260
3656
|
console.warn(
|
|
3261
3657
|
`[attaform] injectForm: ${detail}. Returning null.` + (frame !== void 0 ? ` ${frame}` : "")
|
|
@@ -3279,5 +3675,5 @@ function warnIfAmbientProviderHadDuplicates() {
|
|
|
3279
3675
|
}
|
|
3280
3676
|
}
|
|
3281
3677
|
|
|
3282
|
-
export { AttaformErrorCode as A, defineCoercion as a, isUnset as b, useAbstractForm as c, defaultCoercionRules as d, setAtPath as e, isPlainRecord as f, getAtPath as g, injectForm as i, slimKindOf as s, unset as u };
|
|
3283
|
-
//# sourceMappingURL=attaform.
|
|
3678
|
+
export { AttaformErrorCode as A, defineCoercion as a, isUnset as b, useAbstractForm as c, defaultCoercionRules as d, setAtPath as e, isPlainRecord as f, getAtPath as g, humanize as h, injectForm as i, slimKindOf as s, unset as u };
|
|
3679
|
+
//# sourceMappingURL=attaform.qxyip_aN.mjs.map
|