attaform 0.16.4 → 0.17.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/README.md +4 -2
- package/dist/chunks/devtools.cjs +19 -12
- package/dist/chunks/devtools.cjs.map +1 -1
- package/dist/chunks/devtools.mjs +19 -12
- 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 +26 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -8
- package/dist/index.d.mts +51 -8
- package/dist/index.d.ts +51 -8
- package/dist/index.mjs +28 -9
- package/dist/index.mjs.map +1 -1
- 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 +3 -3
- package/dist/runtime/plugins/attaform.cjs.map +1 -1
- package/dist/runtime/plugins/attaform.mjs +3 -3
- package/dist/runtime/plugins/attaform.mjs.map +1 -1
- package/dist/shared/{attaform.Dd_pWnmn.cjs → attaform.0Wg7UEeX.cjs} +51 -10
- package/dist/shared/attaform.0Wg7UEeX.cjs.map +1 -0
- package/dist/shared/attaform.AOgGyRoI.d.cts +65 -0
- package/dist/shared/{attaform.CCQkY4Ta.d.ts → attaform.B0zue7zt.d.ts} +1 -1
- package/dist/shared/{attaform.CIwZtbGV.cjs → attaform.BBM2muQ9.cjs} +2 -2
- package/dist/shared/{attaform.CIwZtbGV.cjs.map → attaform.BBM2muQ9.cjs.map} +1 -1
- package/dist/shared/{attaform.fegmBJaq.cjs → attaform.BFumZXY2.cjs} +1422 -389
- package/dist/shared/attaform.BFumZXY2.cjs.map +1 -0
- package/dist/shared/attaform.BQ-iGGWd.d.mts +65 -0
- package/dist/shared/{attaform.DyV1O4tI.mjs → attaform.BT55rDNN.mjs} +1423 -391
- package/dist/shared/attaform.BT55rDNN.mjs.map +1 -0
- package/dist/shared/{attaform.CU3JperC.d.cts → attaform.BYbsV2Wv.d.cts} +574 -132
- package/dist/shared/{attaform.CU3JperC.d.mts → attaform.BYbsV2Wv.d.mts} +574 -132
- package/dist/shared/{attaform.CU3JperC.d.ts → attaform.BYbsV2Wv.d.ts} +574 -132
- package/dist/shared/{attaform.keLBaHB6.cjs → attaform.C6_zOf8x.cjs} +228 -113
- package/dist/shared/attaform.C6_zOf8x.cjs.map +1 -0
- package/dist/shared/{attaform.CJttVxRj.cjs → attaform.C8LVFVVe.cjs} +2 -2
- package/dist/shared/{attaform.CJttVxRj.cjs.map → attaform.C8LVFVVe.cjs.map} +1 -1
- package/dist/shared/{attaform.BfMxsfmE.mjs → attaform.CIEQgJnM.mjs} +143 -78
- package/dist/shared/attaform.CIEQgJnM.mjs.map +1 -0
- package/dist/shared/attaform.CX9v2M8k.d.ts +65 -0
- package/dist/shared/{attaform.g7rfuXdz.mjs → attaform.Cj0pCNVn.mjs} +228 -113
- package/dist/shared/attaform.Cj0pCNVn.mjs.map +1 -0
- package/dist/shared/{attaform.CMRmwGDt.d.cts → attaform.ClfCi1i2.d.mts} +1 -1
- package/dist/shared/{attaform.UA19EF3J.mjs → attaform.D6Q5ZP8L.mjs} +51 -10
- package/dist/shared/attaform.D6Q5ZP8L.mjs.map +1 -0
- package/dist/shared/{attaform.CXMOheyZ.d.mts → attaform.D7lomopc.d.cts} +1 -1
- package/dist/shared/{attaform.rIRYSUI1.cjs → attaform.Dee2rU1P.cjs} +145 -77
- package/dist/shared/attaform.Dee2rU1P.cjs.map +1 -0
- package/dist/shared/{attaform.CINUMjPq.mjs → attaform.Vo-Kft0t.mjs} +2 -2
- package/dist/shared/{attaform.CINUMjPq.mjs.map → attaform.Vo-Kft0t.mjs.map} +1 -1
- package/dist/shared/{attaform.DZRj9s0s.mjs → attaform.h1sq3BFu.mjs} +2 -2
- package/dist/shared/{attaform.DZRj9s0s.mjs.map → attaform.h1sq3BFu.mjs.map} +1 -1
- package/dist/zod-v3.cjs +3 -3
- package/dist/zod-v3.d.cts +5 -5
- package/dist/zod-v3.d.mts +5 -5
- package/dist/zod-v3.d.ts +5 -5
- package/dist/zod-v3.mjs +3 -3
- package/dist/zod-v4.cjs +3 -3
- package/dist/zod-v4.d.cts +16 -42
- package/dist/zod-v4.d.mts +16 -42
- package/dist/zod-v4.d.ts +16 -42
- package/dist/zod-v4.mjs +3 -3
- package/dist/zod.cjs +4 -4
- package/dist/zod.cjs.map +1 -1
- package/dist/zod.d.cts +6 -5
- package/dist/zod.d.mts +6 -5
- package/dist/zod.d.ts +6 -5
- package/dist/zod.mjs +5 -5
- package/dist/zod.mjs.map +1 -1
- package/package.json +3 -8
- package/dist/shared/attaform.BfMxsfmE.mjs.map +0 -1
- package/dist/shared/attaform.Dd_pWnmn.cjs.map +0 -1
- package/dist/shared/attaform.DyV1O4tI.mjs.map +0 -1
- package/dist/shared/attaform.UA19EF3J.mjs.map +0 -1
- package/dist/shared/attaform.fegmBJaq.cjs.map +0 -1
- package/dist/shared/attaform.g7rfuXdz.mjs.map +0 -1
- package/dist/shared/attaform.keLBaHB6.cjs.map +0 -1
- package/dist/shared/attaform.rIRYSUI1.cjs.map +0 -1
|
@@ -1,173 +1,6 @@
|
|
|
1
|
-
import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, reactive, watch, markRaw, shallowRef, getCurrentInstance, provide, useId,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
|
-
}
|
|
1
|
+
import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, reactive, watch, markRaw, toRaw, shallowRef, getCurrentInstance, provide, useId, inject } from 'vue';
|
|
2
|
+
import { _ as __DEV__, e as SubmitErrorHandlerError, A as AnonPersistError, l as captureUserCallSite, m as enforceSensitiveCheck, s as segmentMatchesSensitive, n as isSensitivePath, o as createPersistOptInRegistry, b as InvalidUseFormConfigError, p as ensureAttaformInstalled, j as useRegistry, q as kFormContext, r as kFormInstanceId, d as ReservedFormKeyError, t as createIsSensitivePath, w as createSegmentMatchesSensitive } from './attaform.CIEQgJnM.mjs';
|
|
3
|
+
import { c as canonicalizePath, s as segmentsForPathKey, i as isPathPrefix, F as FORM_ERRORS_PATH_KEY, R as ROOT_PATH, b as FORM_ERRORS_PATH } from './attaform.h1sq3BFu.mjs';
|
|
171
4
|
|
|
172
5
|
const NOT_FOUND = Symbol("NOT_FOUND");
|
|
173
6
|
function descendStep(value, segment) {
|
|
@@ -413,14 +246,230 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
|
|
|
413
246
|
return rec;
|
|
414
247
|
}
|
|
415
248
|
|
|
416
|
-
function
|
|
249
|
+
function isDescendable(value) {
|
|
250
|
+
if (value === null || typeof value !== "object") return false;
|
|
251
|
+
if (Array.isArray(value)) return true;
|
|
252
|
+
const proto = Object.getPrototypeOf(value);
|
|
253
|
+
return proto === null || proto === Object.prototype;
|
|
254
|
+
}
|
|
255
|
+
function appendSegment(prefix, segment) {
|
|
256
|
+
const next = new Array(prefix.length + 1);
|
|
257
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
258
|
+
const s = prefix[i];
|
|
259
|
+
next[i] = s;
|
|
260
|
+
}
|
|
261
|
+
next[prefix.length] = segment;
|
|
262
|
+
return next;
|
|
263
|
+
}
|
|
264
|
+
function diffAndApply(oldValue, newValue, prefix, visit) {
|
|
265
|
+
if (Object.is(oldValue, newValue)) return;
|
|
266
|
+
const oldIsDescendable = isDescendable(oldValue);
|
|
267
|
+
const newIsDescendable = isDescendable(newValue);
|
|
268
|
+
if (oldValue === void 0 && newIsDescendable) {
|
|
269
|
+
if (Array.isArray(newValue)) {
|
|
270
|
+
for (let i = 0; i < newValue.length; i++) {
|
|
271
|
+
diffAndApply(void 0, newValue[i], appendSegment(prefix, i), visit);
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
const rec = newValue;
|
|
275
|
+
for (const k of Object.keys(rec)) {
|
|
276
|
+
diffAndApply(void 0, rec[k], appendSegment(prefix, k), visit);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (oldIsDescendable && newValue === void 0) {
|
|
282
|
+
if (Array.isArray(oldValue)) {
|
|
283
|
+
for (let i = 0; i < oldValue.length; i++) {
|
|
284
|
+
diffAndApply(oldValue[i], void 0, appendSegment(prefix, i), visit);
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
const rec = oldValue;
|
|
288
|
+
for (const k of Object.keys(rec)) {
|
|
289
|
+
diffAndApply(rec[k], void 0, appendSegment(prefix, k), visit);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (oldIsDescendable && newIsDescendable) {
|
|
295
|
+
const oldIsArray = Array.isArray(oldValue);
|
|
296
|
+
const newIsArray = Array.isArray(newValue);
|
|
297
|
+
if (oldIsArray && newIsArray) {
|
|
298
|
+
const oldArr = oldValue;
|
|
299
|
+
const newArr = newValue;
|
|
300
|
+
const max = Math.max(oldArr.length, newArr.length);
|
|
301
|
+
for (let i = 0; i < max; i++) {
|
|
302
|
+
diffAndApply(oldArr[i], newArr[i], appendSegment(prefix, i), visit);
|
|
303
|
+
}
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (!oldIsArray && !newIsArray) {
|
|
307
|
+
const oldRec = oldValue;
|
|
308
|
+
const newRec = newValue;
|
|
309
|
+
const seen = /* @__PURE__ */ new Set();
|
|
310
|
+
for (const k of Object.keys(oldRec)) {
|
|
311
|
+
seen.add(k);
|
|
312
|
+
diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
|
|
313
|
+
}
|
|
314
|
+
for (const k of Object.keys(newRec)) {
|
|
315
|
+
if (seen.has(k)) continue;
|
|
316
|
+
diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
|
|
317
|
+
}
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (oldIsDescendable && !newIsDescendable) {
|
|
324
|
+
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (!oldIsDescendable && newIsDescendable) {
|
|
328
|
+
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (oldValue === void 0) {
|
|
332
|
+
visit({ kind: "added", path: prefix, newValue });
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (newValue === void 0) {
|
|
336
|
+
visit({ kind: "removed", path: prefix, oldValue });
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
340
|
+
}
|
|
341
|
+
function applyChangedKeys(target, source) {
|
|
342
|
+
if (!isDescendable(target) || !isDescendable(source)) return false;
|
|
343
|
+
const targetIsArray = Array.isArray(target);
|
|
344
|
+
const sourceIsArray = Array.isArray(source);
|
|
345
|
+
if (targetIsArray !== sourceIsArray) return false;
|
|
346
|
+
const ROOT_SENTINEL = Symbol.for("attaform.applyChangedKeys.rootMismatch");
|
|
347
|
+
const changedFirstSegments = /* @__PURE__ */ new Set();
|
|
348
|
+
diffAndApply(target, source, [], (patch) => {
|
|
349
|
+
if (patch.path.length === 0) {
|
|
350
|
+
changedFirstSegments.add(ROOT_SENTINEL);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
changedFirstSegments.add(patch.path[0]);
|
|
354
|
+
});
|
|
355
|
+
if (changedFirstSegments.has(ROOT_SENTINEL)) return false;
|
|
356
|
+
if (targetIsArray) {
|
|
357
|
+
const t = target;
|
|
358
|
+
const s = source;
|
|
359
|
+
if (t.length > s.length) t.length = s.length;
|
|
360
|
+
for (const idx of changedFirstSegments) {
|
|
361
|
+
if (typeof idx === "symbol") continue;
|
|
362
|
+
const i = typeof idx === "number" ? idx : Number(idx);
|
|
363
|
+
t[i] = s[i];
|
|
364
|
+
}
|
|
365
|
+
} else {
|
|
366
|
+
const t = target;
|
|
367
|
+
const s = source;
|
|
368
|
+
const sourceKeys = new Set(Object.keys(s));
|
|
369
|
+
for (const k of Object.keys(t)) {
|
|
370
|
+
if (!sourceKeys.has(k)) delete t[k];
|
|
371
|
+
}
|
|
372
|
+
for (const k of changedFirstSegments) {
|
|
373
|
+
if (typeof k === "symbol") continue;
|
|
374
|
+
t[String(k)] = s[String(k)];
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
function applyPatchesForward(root, patches) {
|
|
380
|
+
let current = root;
|
|
381
|
+
for (const patch of patches) {
|
|
382
|
+
if (patch.path.length === 0) {
|
|
383
|
+
current = patch.kind === "removed" ? void 0 : patch.newValue;
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
if (patch.kind === "removed") {
|
|
387
|
+
current = deleteAtPath(current, patch.path);
|
|
388
|
+
} else {
|
|
389
|
+
current = setAtPath(current, patch.path, patch.newValue);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return current;
|
|
393
|
+
}
|
|
394
|
+
function applyPatchesInverse(root, patches) {
|
|
395
|
+
let current = root;
|
|
396
|
+
for (let i = patches.length - 1; i >= 0; i--) {
|
|
397
|
+
const patch = patches[i];
|
|
398
|
+
if (patch.path.length === 0) {
|
|
399
|
+
if (patch.kind === "added") {
|
|
400
|
+
current = void 0;
|
|
401
|
+
} else {
|
|
402
|
+
current = patch.oldValue;
|
|
403
|
+
}
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
if (patch.kind === "added") {
|
|
407
|
+
current = deleteAtPath(current, patch.path);
|
|
408
|
+
} else {
|
|
409
|
+
current = setAtPath(current, patch.path, patch.oldValue);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return current;
|
|
413
|
+
}
|
|
414
|
+
function structuralSnapshot(value) {
|
|
415
|
+
if (!isDescendable(value)) return value;
|
|
416
|
+
if (Array.isArray(value)) {
|
|
417
|
+
const out2 = new Array(value.length);
|
|
418
|
+
for (let i = 0; i < value.length; i++) {
|
|
419
|
+
out2[i] = structuralSnapshot(value[i]);
|
|
420
|
+
}
|
|
421
|
+
return out2;
|
|
422
|
+
}
|
|
423
|
+
const src = value;
|
|
424
|
+
const out = {};
|
|
425
|
+
for (const k of Object.keys(src)) {
|
|
426
|
+
out[k] = structuralSnapshot(src[k]);
|
|
427
|
+
}
|
|
428
|
+
return out;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const EMPTY_RESOLVED_FIELD_META = Object.freeze({
|
|
432
|
+
label: "",
|
|
433
|
+
description: void 0,
|
|
434
|
+
placeholder: void 0,
|
|
435
|
+
meta: Object.freeze({})
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
function humanize(segment) {
|
|
439
|
+
if (typeof segment === "number") return "";
|
|
440
|
+
const str = String(segment);
|
|
441
|
+
if (str.length === 0) return "";
|
|
442
|
+
if (/^\d+$/.test(str)) return "";
|
|
443
|
+
const tokens = str.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().split(" ").filter((part) => part.length > 0);
|
|
444
|
+
if (tokens.length === 0) return "";
|
|
445
|
+
return tokens.map((part) => {
|
|
446
|
+
const head = part[0];
|
|
447
|
+
return head === void 0 ? part : head.toUpperCase() + part.slice(1).toLowerCase();
|
|
448
|
+
}).join(" ");
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function isUnderStubAncestor(state, segments) {
|
|
452
|
+
for (let i = 0; i < segments.length; i++) {
|
|
453
|
+
const ancestorPath = segments.slice(0, i);
|
|
454
|
+
const du = state.schema.getUnionDiscriminatorAtPath(ancestorPath);
|
|
455
|
+
if (du === void 0) continue;
|
|
456
|
+
const ancestorValue = getAtPath(state.form.value, ancestorPath);
|
|
457
|
+
if (ancestorValue === null || typeof ancestorValue !== "object") continue;
|
|
458
|
+
const discValue = ancestorValue[du.discriminatorKey];
|
|
459
|
+
if (discValue === void 0) return true;
|
|
460
|
+
if (!du.isVariantSelected(discValue)) return true;
|
|
461
|
+
}
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
function buildFieldStateAccessor(state, getFormMetaBase, options) {
|
|
417
465
|
const cache = /* @__PURE__ */ new Map();
|
|
466
|
+
const predicate = options?.shouldShowErrors;
|
|
418
467
|
return function getFieldState(pathInput) {
|
|
419
468
|
const { segments, key } = canonicalizePath(pathInput);
|
|
420
469
|
const cached = cache.get(key);
|
|
421
470
|
if (cached !== void 0) return cached;
|
|
422
471
|
const c = computed(
|
|
423
|
-
() => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, getFormMetaBase) : buildContainerFieldState(state, segments, key, getFormMetaBase)
|
|
472
|
+
() => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, getFormMetaBase, predicate) : buildContainerFieldState(state, segments, key, getFormMetaBase, predicate)
|
|
424
473
|
);
|
|
425
474
|
cache.set(key, c);
|
|
426
475
|
return c;
|
|
@@ -440,7 +489,8 @@ function buildLeafFieldStateBase(state, segments, key) {
|
|
|
440
489
|
if (userForKey !== void 0) errors.push(...userForKey);
|
|
441
490
|
const validating = (state.fieldValidationCounts.get(key) ?? 0) > 0;
|
|
442
491
|
const gated = state.pathHasAsyncValidation(segments) && !state.firstValidationDone.value;
|
|
443
|
-
const
|
|
492
|
+
const isOrphan = segments.length > 0 && !hasAtPath(state.form.value, segments) && isUnderStubAncestor(state, segments);
|
|
493
|
+
const valid = !gated && errors.length === 0 && !validating && !isOrphan;
|
|
444
494
|
const elementRecord = state.elements.get(key);
|
|
445
495
|
const elementsArr = elementRecord ? Object.freeze([...elementRecord.elements]) : EMPTY_ELEMENTS;
|
|
446
496
|
const firstElement = elementsArr[0] ?? null;
|
|
@@ -470,9 +520,9 @@ function buildLeafFieldStateBase(state, segments, key) {
|
|
|
470
520
|
meta: resolved.meta
|
|
471
521
|
};
|
|
472
522
|
}
|
|
473
|
-
function buildLeafFieldState(state, segments, key, getFormMetaBase) {
|
|
523
|
+
function buildLeafFieldState(state, segments, key, getFormMetaBase, shouldShowErrors) {
|
|
474
524
|
const base = buildLeafFieldStateBase(state, segments, key);
|
|
475
|
-
return decorateWithDerivedProps(base, state, getFormMetaBase);
|
|
525
|
+
return decorateWithDerivedProps(base, state, getFormMetaBase, shouldShowErrors);
|
|
476
526
|
}
|
|
477
527
|
function buildContainerFieldStateBase(state, segments, _key) {
|
|
478
528
|
const formValue = state.form.value;
|
|
@@ -540,13 +590,14 @@ function buildContainerFieldStateBase(state, segments, _key) {
|
|
|
540
590
|
meta: resolved.meta
|
|
541
591
|
};
|
|
542
592
|
}
|
|
543
|
-
function buildContainerFieldState(state, segments, key, getFormMetaBase) {
|
|
593
|
+
function buildContainerFieldState(state, segments, key, getFormMetaBase, shouldShowErrors) {
|
|
544
594
|
const base = buildContainerFieldStateBase(state, segments);
|
|
545
|
-
return decorateWithDerivedProps(base, state, getFormMetaBase);
|
|
595
|
+
return decorateWithDerivedProps(base, state, getFormMetaBase, shouldShowErrors);
|
|
546
596
|
}
|
|
547
|
-
function decorateWithDerivedProps(base, state, getFormMetaBase) {
|
|
597
|
+
function decorateWithDerivedProps(base, state, getFormMetaBase, shouldShowErrors) {
|
|
548
598
|
const firstError = base.errors[0];
|
|
549
|
-
const
|
|
599
|
+
const predicate = shouldShowErrors ?? state.shouldShowErrors;
|
|
600
|
+
const showErrors = base.errors.length > 0 && predicate(base, getFormMetaBase());
|
|
550
601
|
return { ...base, showErrors, firstError };
|
|
551
602
|
}
|
|
552
603
|
function aggregateErrorsAt(state, prefix) {
|
|
@@ -731,14 +782,16 @@ function buildErrorsProxy(state) {
|
|
|
731
782
|
return buildSurfaceProxy({
|
|
732
783
|
schema: state.schema,
|
|
733
784
|
resolveLeaf: (path) => {
|
|
734
|
-
if (!hasAtPath(state.form.value, path)) return void 0;
|
|
735
785
|
const { key } = canonicalizePath(path);
|
|
736
|
-
const schemaForKey = state.schemaErrors.get(key);
|
|
737
|
-
const blankForKey = state.derivedBlankErrors.value.get(key);
|
|
738
786
|
const userForKey = state.userErrors.get(key);
|
|
787
|
+
const isActive = hasAtPath(state.form.value, path);
|
|
739
788
|
const merged = [];
|
|
740
|
-
if (
|
|
741
|
-
|
|
789
|
+
if (isActive) {
|
|
790
|
+
const schemaForKey = state.schemaErrors.get(key);
|
|
791
|
+
const blankForKey = state.derivedBlankErrors.value.get(key);
|
|
792
|
+
if (schemaForKey !== void 0) merged.push(...schemaForKey);
|
|
793
|
+
if (blankForKey !== void 0) merged.push(...blankForKey);
|
|
794
|
+
}
|
|
742
795
|
if (userForKey !== void 0) merged.push(...userForKey);
|
|
743
796
|
return merged.length === 0 ? void 0 : merged;
|
|
744
797
|
},
|
|
@@ -763,7 +816,7 @@ function buildErrorsProxy(state) {
|
|
|
763
816
|
function materializeErrors(state, containerSegments) {
|
|
764
817
|
const liveContainer = getAtPath(state.form.value, containerSegments);
|
|
765
818
|
const tree = Array.isArray(liveContainer) ? [] : {};
|
|
766
|
-
const collect = (store) => {
|
|
819
|
+
const collect = (store, applyActivePathFilter) => {
|
|
767
820
|
entries: for (const [pathKey, errors] of store) {
|
|
768
821
|
if (errors.length === 0) continue;
|
|
769
822
|
const fullPath = segmentsForPathKey(pathKey);
|
|
@@ -772,13 +825,13 @@ function materializeErrors(state, containerSegments) {
|
|
|
772
825
|
for (let i = 0; i < containerSegments.length; i++) {
|
|
773
826
|
if (fullPath[i] !== containerSegments[i]) continue entries;
|
|
774
827
|
}
|
|
775
|
-
if (!hasAtPath(state.form.value, fullPath)) continue;
|
|
828
|
+
if (applyActivePathFilter && !hasAtPath(state.form.value, fullPath)) continue;
|
|
776
829
|
placeAt(tree, fullPath.slice(containerSegments.length), errors);
|
|
777
830
|
}
|
|
778
831
|
};
|
|
779
|
-
collect(state.schemaErrors);
|
|
780
|
-
collect(state.derivedBlankErrors.value);
|
|
781
|
-
collect(state.userErrors);
|
|
832
|
+
collect(state.schemaErrors, true);
|
|
833
|
+
collect(state.derivedBlankErrors.value, true);
|
|
834
|
+
collect(state.userErrors, false);
|
|
782
835
|
return tree;
|
|
783
836
|
}
|
|
784
837
|
function placeAt(tree, path, errors) {
|
|
@@ -809,11 +862,13 @@ function buildFieldArrayApi(state) {
|
|
|
809
862
|
const current = state.getValueAtPath(segments);
|
|
810
863
|
return Array.isArray(current) ? current.slice() : [];
|
|
811
864
|
}
|
|
812
|
-
function writeArray(path, next) {
|
|
865
|
+
function writeArray(path, next, arrayOp) {
|
|
813
866
|
const { segments, key } = canonicalizePath(path);
|
|
814
|
-
|
|
815
|
-
persist: state.persistOptIns.hasAnyOptInForPath(key)
|
|
816
|
-
|
|
867
|
+
const meta = {
|
|
868
|
+
persist: state.persistOptIns.hasAnyOptInForPath(key),
|
|
869
|
+
...arrayOp !== void 0 ? { arrayOp } : {}
|
|
870
|
+
};
|
|
871
|
+
return state.setValueAtPath(segments, next, meta);
|
|
817
872
|
}
|
|
818
873
|
return {
|
|
819
874
|
append(path, value) {
|
|
@@ -824,18 +879,19 @@ function buildFieldArrayApi(state) {
|
|
|
824
879
|
prepend(path, value) {
|
|
825
880
|
const next = readArray(path);
|
|
826
881
|
next.unshift(value);
|
|
827
|
-
return writeArray(path, next);
|
|
882
|
+
return writeArray(path, next, { kind: "shift-from", index: 0 });
|
|
828
883
|
},
|
|
829
884
|
insert(path, index, value) {
|
|
830
885
|
const next = readArray(path);
|
|
831
886
|
next.splice(index, 0, value);
|
|
832
|
-
|
|
887
|
+
const clampedIndex = Math.max(0, Math.min(index, next.length));
|
|
888
|
+
return writeArray(path, next, { kind: "shift-from", index: clampedIndex });
|
|
833
889
|
},
|
|
834
890
|
remove(path, index) {
|
|
835
891
|
const next = readArray(path);
|
|
836
892
|
if (index < 0 || index >= next.length) return false;
|
|
837
893
|
next.splice(index, 1);
|
|
838
|
-
return writeArray(path, next);
|
|
894
|
+
return writeArray(path, next, { kind: "shift-from", index });
|
|
839
895
|
},
|
|
840
896
|
swap(path, a, b) {
|
|
841
897
|
const next = readArray(path);
|
|
@@ -845,7 +901,7 @@ function buildFieldArrayApi(state) {
|
|
|
845
901
|
const tmp = next[a];
|
|
846
902
|
next[a] = next[b];
|
|
847
903
|
next[b] = tmp;
|
|
848
|
-
return writeArray(path, next);
|
|
904
|
+
return writeArray(path, next, { kind: "swap", a, b });
|
|
849
905
|
},
|
|
850
906
|
move(path, from, to) {
|
|
851
907
|
const next = readArray(path);
|
|
@@ -853,13 +909,17 @@ function buildFieldArrayApi(state) {
|
|
|
853
909
|
const [item] = next.splice(from, 1);
|
|
854
910
|
const clampedTo = Math.max(0, Math.min(to, next.length));
|
|
855
911
|
next.splice(clampedTo, 0, item);
|
|
856
|
-
return writeArray(path, next
|
|
912
|
+
return writeArray(path, next, {
|
|
913
|
+
kind: "shift-range",
|
|
914
|
+
fromIndex: Math.min(from, clampedTo),
|
|
915
|
+
toIndex: Math.max(from, clampedTo)
|
|
916
|
+
});
|
|
857
917
|
},
|
|
858
918
|
replace(path, index, value) {
|
|
859
919
|
const next = readArray(path);
|
|
860
920
|
if (index < 0 || index >= next.length) return false;
|
|
861
921
|
next[index] = value;
|
|
862
|
-
return writeArray(path, next);
|
|
922
|
+
return writeArray(path, next, { kind: "replace-at", index });
|
|
863
923
|
}
|
|
864
924
|
};
|
|
865
925
|
}
|
|
@@ -888,8 +948,12 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
|
|
|
888
948
|
"placeholder",
|
|
889
949
|
"meta"
|
|
890
950
|
]);
|
|
891
|
-
function buildFieldStateProxy(state, getFormMetaBase) {
|
|
892
|
-
const getFieldStateAt = buildFieldStateAccessor(
|
|
951
|
+
function buildFieldStateProxy(state, getFormMetaBase, options) {
|
|
952
|
+
const getFieldStateAt = buildFieldStateAccessor(
|
|
953
|
+
state,
|
|
954
|
+
getFormMetaBase,
|
|
955
|
+
options?.shouldShowErrors !== void 0 ? { shouldShowErrors: options.shouldShowErrors } : void 0
|
|
956
|
+
);
|
|
893
957
|
const snapshotFieldStateAt = (path) => {
|
|
894
958
|
const view = getFieldStateAt(path).value;
|
|
895
959
|
const snapshot = {};
|
|
@@ -980,10 +1044,25 @@ function walk$2(value, basePath, schema, snapshotFieldStateAt) {
|
|
|
980
1044
|
|
|
981
1045
|
const DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS = 0;
|
|
982
1046
|
const DEFAULT_PERSISTENCE_DEBOUNCE_MS = 300;
|
|
983
|
-
const DEFAULT_HISTORY_MAX_SNAPSHOTS =
|
|
1047
|
+
const DEFAULT_HISTORY_MAX_SNAPSHOTS = 128;
|
|
984
1048
|
const PERSISTENCE_KEY_PREFIX = "attaform:";
|
|
985
1049
|
const RESERVED_KEY_PREFIX = "__atta:";
|
|
986
1050
|
const ANONYMOUS_FORM_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon:`;
|
|
1051
|
+
const DEFAULT_MAX_RECURSION_DEPTH = 64;
|
|
1052
|
+
function normalizeNumericOption(config) {
|
|
1053
|
+
const { value, source, allowInfinity, min, defaultValue } = config;
|
|
1054
|
+
if (allowInfinity && value === Infinity) return Infinity;
|
|
1055
|
+
if (typeof value !== "number" || Number.isNaN(value) || value === Infinity || value === -Infinity) {
|
|
1056
|
+
if (__DEV__) {
|
|
1057
|
+
const acceptedDescription = allowInfinity ? "a non-negative integer or Infinity" : "a non-negative finite integer";
|
|
1058
|
+
console.warn(
|
|
1059
|
+
`[attaform] ${source} must be ${acceptedDescription}; got ${String(value)}. Falling back to ${String(defaultValue)}.`
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
return defaultValue;
|
|
1063
|
+
}
|
|
1064
|
+
return Math.max(min, Math.floor(value));
|
|
1065
|
+
}
|
|
987
1066
|
|
|
988
1067
|
const PERSISTENCE_MODULE_KEY = "persistence";
|
|
989
1068
|
async function getStorageAdapter(storage) {
|
|
@@ -1158,20 +1237,29 @@ function mergeDeep(target, source, path, schema) {
|
|
|
1158
1237
|
if (source === null || typeof source !== "object") return source;
|
|
1159
1238
|
if (Array.isArray(source)) return source;
|
|
1160
1239
|
if (!isPlainRecord(source)) return source;
|
|
1161
|
-
let mergeTarget = target;
|
|
1162
1240
|
if (schema !== void 0) {
|
|
1163
1241
|
const du = schema.getUnionDiscriminatorAtPath(path);
|
|
1164
1242
|
if (du !== void 0) {
|
|
1165
|
-
const
|
|
1166
|
-
const
|
|
1167
|
-
if (sourceDisc !== void 0 && !
|
|
1243
|
+
const sourceRecord = source;
|
|
1244
|
+
const sourceDisc = sourceRecord[du.discriminatorKey];
|
|
1245
|
+
if (sourceDisc !== void 0 && !du.isVariantSelected(sourceDisc)) {
|
|
1246
|
+
return { [du.discriminatorKey]: sourceDisc };
|
|
1247
|
+
}
|
|
1248
|
+
if (sourceDisc !== void 0) {
|
|
1168
1249
|
const variantDefault = du.getVariantDefault(sourceDisc);
|
|
1169
1250
|
if (isPlainRecord(variantDefault)) {
|
|
1170
|
-
|
|
1251
|
+
const out2 = { ...variantDefault };
|
|
1252
|
+
for (const key of Object.keys(sourceRecord)) {
|
|
1253
|
+
if (!(key in variantDefault) && key !== du.discriminatorKey) continue;
|
|
1254
|
+
out2[key] = mergeDeep(out2[key], sourceRecord[key], [...path, key], schema);
|
|
1255
|
+
}
|
|
1256
|
+
return out2;
|
|
1171
1257
|
}
|
|
1172
1258
|
}
|
|
1259
|
+
return {};
|
|
1173
1260
|
}
|
|
1174
1261
|
}
|
|
1262
|
+
const mergeTarget = target;
|
|
1175
1263
|
const out = isPlainRecord(mergeTarget) ? { ...mergeTarget } : {};
|
|
1176
1264
|
for (const key of Object.keys(source)) {
|
|
1177
1265
|
out[key] = mergeDeep(out[key], source[key], [...path, key], schema);
|
|
@@ -1200,14 +1288,14 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1200
1288
|
});
|
|
1201
1289
|
let gen = 0;
|
|
1202
1290
|
async function kickoff(data, path, captured) {
|
|
1203
|
-
state.activeValidations.value += 1;
|
|
1204
|
-
result.value = {
|
|
1205
|
-
pending: true,
|
|
1206
|
-
errors: void 0,
|
|
1207
|
-
success: false,
|
|
1208
|
-
formKey: state.formKey
|
|
1209
|
-
};
|
|
1210
1291
|
try {
|
|
1292
|
+
state.activeValidations.value += 1;
|
|
1293
|
+
result.value = {
|
|
1294
|
+
pending: true,
|
|
1295
|
+
errors: void 0,
|
|
1296
|
+
success: false,
|
|
1297
|
+
formKey: state.formKey
|
|
1298
|
+
};
|
|
1211
1299
|
const refinement = await runRefinementValidation(data, path);
|
|
1212
1300
|
if (captured !== gen) return;
|
|
1213
1301
|
result.value = settled(composeWithDerivedBlank(refinement, path));
|
|
@@ -1251,14 +1339,44 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1251
1339
|
async function validateAsync(pathInput) {
|
|
1252
1340
|
const segments = pathInput === void 0 ? void 0 : toSegments(pathInput);
|
|
1253
1341
|
const dataAtPath = segments === void 0 ? state.form.value : state.getValueAtPath(segments);
|
|
1254
|
-
state.activeValidations.value += 1;
|
|
1255
1342
|
try {
|
|
1343
|
+
state.activeValidations.value += 1;
|
|
1256
1344
|
const refinement = await runRefinementValidation(dataAtPath, segments);
|
|
1257
1345
|
return stripData(composeWithDerivedBlank(refinement, segments));
|
|
1346
|
+
} catch (err) {
|
|
1347
|
+
return adapterThrowResponse(err);
|
|
1348
|
+
} finally {
|
|
1349
|
+
state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
async function process(pathInput) {
|
|
1353
|
+
const segments = pathInput === void 0 ? void 0 : toSegments(pathInput);
|
|
1354
|
+
const dataAtPath = segments === void 0 ? state.form.value : state.getValueAtPath(segments);
|
|
1355
|
+
try {
|
|
1356
|
+
state.activeValidations.value += 1;
|
|
1357
|
+
const refinement = await runRefinementValidation(dataAtPath, segments);
|
|
1358
|
+
return composeWithDerivedBlank(refinement, segments);
|
|
1359
|
+
} catch (err) {
|
|
1360
|
+
return adapterThrowResponse(err);
|
|
1258
1361
|
} finally {
|
|
1259
1362
|
state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
|
|
1260
1363
|
}
|
|
1261
1364
|
}
|
|
1365
|
+
function adapterThrowResponse(err) {
|
|
1366
|
+
return {
|
|
1367
|
+
success: false,
|
|
1368
|
+
data: void 0,
|
|
1369
|
+
errors: [
|
|
1370
|
+
{
|
|
1371
|
+
message: adapterThrowMessage(err),
|
|
1372
|
+
path: [],
|
|
1373
|
+
formKey: state.formKey,
|
|
1374
|
+
code: AttaformErrorCode.AdapterThrew
|
|
1375
|
+
}
|
|
1376
|
+
],
|
|
1377
|
+
formKey: state.formKey
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1262
1380
|
async function runRefinementValidation(data, path) {
|
|
1263
1381
|
return await state.schema.validateAtPath(data, path);
|
|
1264
1382
|
}
|
|
@@ -1280,14 +1398,17 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1280
1398
|
if (event !== void 0 && "preventDefault" in event && typeof event.preventDefault === "function") {
|
|
1281
1399
|
event.preventDefault();
|
|
1282
1400
|
}
|
|
1401
|
+
if (state.activeSubmissions.value > 0) {
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1283
1404
|
const genAtEntry = state.submissionGeneration.value;
|
|
1284
|
-
state.activeSubmissions.value += 1;
|
|
1285
|
-
state.submitting.value = true;
|
|
1286
|
-
state.submitError.value = null;
|
|
1287
|
-
state.cancelFieldValidation();
|
|
1288
|
-
state.activeValidations.value += 1;
|
|
1289
1405
|
let validationSettled = false;
|
|
1290
1406
|
try {
|
|
1407
|
+
state.activeSubmissions.value += 1;
|
|
1408
|
+
state.submitting.value = true;
|
|
1409
|
+
state.submitError.value = null;
|
|
1410
|
+
state.cancelFieldValidation();
|
|
1411
|
+
state.activeValidations.value += 1;
|
|
1291
1412
|
const refinement = await runRefinementValidation(state.form.value, void 0);
|
|
1292
1413
|
const merged = composeWithDerivedBlank(refinement, void 0);
|
|
1293
1414
|
state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
|
|
@@ -1336,7 +1457,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1336
1457
|
};
|
|
1337
1458
|
return submitHandler;
|
|
1338
1459
|
};
|
|
1339
|
-
return { validate, validateAsync, handleSubmit };
|
|
1460
|
+
return { validate, validateAsync, process, handleSubmit };
|
|
1340
1461
|
}
|
|
1341
1462
|
function toSegments(pathInput) {
|
|
1342
1463
|
return canonicalizePath(pathInput).segments;
|
|
@@ -1674,11 +1795,12 @@ function coerceValue(value, accepted, elementAccepted, index) {
|
|
|
1674
1795
|
const EMPTY_TRANSFORMS = Object.freeze([]);
|
|
1675
1796
|
const INTERACTIVE_TAG_NAMES = /* @__PURE__ */ new Set(["INPUT", "SELECT", "TEXTAREA"]);
|
|
1676
1797
|
const attaformListenersSymbol = Symbol.for("attaform:focus-listeners");
|
|
1677
|
-
function attachFocusListeners(state, segments, element) {
|
|
1798
|
+
function attachFocusListeners(state, segments, element, instanceMeta) {
|
|
1678
1799
|
const target = element;
|
|
1679
1800
|
if (target[attaformListenersSymbol] !== void 0) return;
|
|
1680
|
-
const
|
|
1681
|
-
const
|
|
1801
|
+
const focusMeta = instanceMeta !== void 0 ? { instance: instanceMeta } : void 0;
|
|
1802
|
+
const handleFocus = () => state.markFocused(segments, true, focusMeta);
|
|
1803
|
+
const handleBlur = () => state.markFocused(segments, false, focusMeta);
|
|
1682
1804
|
element.addEventListener("focus", handleFocus);
|
|
1683
1805
|
element.addEventListener("blur", handleBlur);
|
|
1684
1806
|
target[attaformListenersSymbol] = { handleFocus, handleBlur };
|
|
@@ -1691,7 +1813,13 @@ function detachFocusListeners(element) {
|
|
|
1691
1813
|
element.removeEventListener("blur", listeners.handleBlur);
|
|
1692
1814
|
delete target[attaformListenersSymbol];
|
|
1693
1815
|
}
|
|
1694
|
-
function buildRegister(state, formInstanceId) {
|
|
1816
|
+
function buildRegister(state, formInstanceId, instanceConfig) {
|
|
1817
|
+
const coerceIndex = instanceConfig?.coerce !== void 0 ? resolveCoercionIndex(instanceConfig.coerce) : state.coerceIndex;
|
|
1818
|
+
const instanceMeta = instanceConfig?.instanceMeta;
|
|
1819
|
+
const withInstanceMeta = (meta) => {
|
|
1820
|
+
if (instanceMeta === void 0) return meta;
|
|
1821
|
+
return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
|
|
1822
|
+
};
|
|
1695
1823
|
const lastTypedFormByPath = /* @__PURE__ */ new Map();
|
|
1696
1824
|
return function register(pathInput, options) {
|
|
1697
1825
|
const { segments, key: pathKey } = canonicalizePath(pathInput);
|
|
@@ -1714,16 +1842,23 @@ function buildRegister(state, formInstanceId) {
|
|
|
1714
1842
|
const slimDefault = state.schema.getDefaultAtPath(segments);
|
|
1715
1843
|
const persist = options?.persist === true;
|
|
1716
1844
|
const acknowledgeSensitive = options?.acknowledgeSensitive === true;
|
|
1845
|
+
const multiTab = options?.multiTab !== false;
|
|
1717
1846
|
const transforms = options?.transforms ?? EMPTY_TRANSFORMS;
|
|
1847
|
+
const markNoSync = !multiTab ? () => {
|
|
1848
|
+
state.incrementNoSyncOptOut(pathKey);
|
|
1849
|
+
} : void 0;
|
|
1850
|
+
const unmarkNoSync = !multiTab ? () => {
|
|
1851
|
+
state.decrementNoSyncOptOut(pathKey);
|
|
1852
|
+
} : void 0;
|
|
1718
1853
|
const coerce = buildCoerceFn(
|
|
1719
1854
|
state.schema,
|
|
1720
1855
|
segments,
|
|
1721
|
-
|
|
1856
|
+
coerceIndex
|
|
1722
1857
|
);
|
|
1723
1858
|
const coerceElement = buildElementCoerceFn(
|
|
1724
1859
|
state.schema,
|
|
1725
1860
|
segments,
|
|
1726
|
-
|
|
1861
|
+
coerceIndex
|
|
1727
1862
|
);
|
|
1728
1863
|
if (persist && !state.ssr && !state.modules.has(PERSISTENCE_MODULE_KEY)) {
|
|
1729
1864
|
throw new AnonPersistError({
|
|
@@ -1737,22 +1872,26 @@ function buildRegister(state, formInstanceId) {
|
|
|
1737
1872
|
displayValue,
|
|
1738
1873
|
lastTypedForm,
|
|
1739
1874
|
markBlank: () => {
|
|
1740
|
-
return state.setValueAtPath(
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1875
|
+
return state.setValueAtPath(
|
|
1876
|
+
segments,
|
|
1877
|
+
slimDefault,
|
|
1878
|
+
withInstanceMeta({
|
|
1879
|
+
blank: true,
|
|
1880
|
+
persist
|
|
1881
|
+
})
|
|
1882
|
+
);
|
|
1744
1883
|
},
|
|
1745
1884
|
registerElement: (element) => {
|
|
1746
1885
|
if (!INTERACTIVE_TAG_NAMES.has(element.tagName)) return;
|
|
1747
1886
|
const added = state.registerElement(segments, element, formInstanceId);
|
|
1748
|
-
if (added) attachFocusListeners(state, segments, element);
|
|
1887
|
+
if (added) attachFocusListeners(state, segments, element, instanceMeta);
|
|
1749
1888
|
},
|
|
1750
1889
|
deregisterElement: (element) => {
|
|
1751
1890
|
detachFocusListeners(element);
|
|
1752
1891
|
state.deregisterElement(segments, element);
|
|
1753
1892
|
},
|
|
1754
1893
|
setValueWithInternalPath: (value, meta) => {
|
|
1755
|
-
return state.setValueAtPath(segments, value, meta);
|
|
1894
|
+
return state.setValueAtPath(segments, value, withInstanceMeta(meta));
|
|
1756
1895
|
},
|
|
1757
1896
|
// Called by the `vRegisterHint` compile-time transform's wrapping
|
|
1758
1897
|
// IIFE on every server-side render of `<element v-register="…">`.
|
|
@@ -1778,6 +1917,10 @@ function buildRegister(state, formInstanceId) {
|
|
|
1778
1917
|
persist,
|
|
1779
1918
|
acknowledgeSensitive,
|
|
1780
1919
|
persistOptIns: state.persistOptIns,
|
|
1920
|
+
isSensitivePath: state.isSensitivePath,
|
|
1921
|
+
multiTab,
|
|
1922
|
+
...markNoSync !== void 0 ? { markNoSync } : {},
|
|
1923
|
+
...unmarkNoSync !== void 0 ? { unmarkNoSync } : {},
|
|
1781
1924
|
transforms,
|
|
1782
1925
|
coerce,
|
|
1783
1926
|
...coerceElement !== void 0 ? { coerceElement } : {}
|
|
@@ -1935,6 +2078,8 @@ function buildValuesProxy(form) {
|
|
|
1935
2078
|
const inner = computed(() => readonly(form.value));
|
|
1936
2079
|
const target = (() => {
|
|
1937
2080
|
});
|
|
2081
|
+
const valuesToString = () => JSON.stringify(inner.value);
|
|
2082
|
+
const valuesToPrimitive = (hint) => hint === "number" ? NaN : valuesToString();
|
|
1938
2083
|
return new Proxy(target, {
|
|
1939
2084
|
apply(_, __, args) {
|
|
1940
2085
|
const arg = args[0];
|
|
@@ -1948,8 +2093,16 @@ function buildValuesProxy(form) {
|
|
|
1948
2093
|
return cursor;
|
|
1949
2094
|
},
|
|
1950
2095
|
get(_, key) {
|
|
1951
|
-
if (typeof key === "symbol")
|
|
2096
|
+
if (typeof key === "symbol") {
|
|
2097
|
+
if (key === Symbol.toPrimitive) return valuesToPrimitive;
|
|
2098
|
+
return Reflect.get(target, key);
|
|
2099
|
+
}
|
|
1952
2100
|
if (key === "toJSON") return () => inner.value;
|
|
2101
|
+
if (key === "toString") return valuesToString;
|
|
2102
|
+
if (key === "valueOf")
|
|
2103
|
+
return function() {
|
|
2104
|
+
return this;
|
|
2105
|
+
};
|
|
1953
2106
|
return inner.value[key];
|
|
1954
2107
|
},
|
|
1955
2108
|
has(_, key) {
|
|
@@ -1988,6 +2141,14 @@ function buildValuesProxy(form) {
|
|
|
1988
2141
|
});
|
|
1989
2142
|
}
|
|
1990
2143
|
|
|
2144
|
+
function blankForKind(slimDefault) {
|
|
2145
|
+
if (typeof slimDefault === "string") return "";
|
|
2146
|
+
if (typeof slimDefault === "number") return 0;
|
|
2147
|
+
if (typeof slimDefault === "bigint") return 0n;
|
|
2148
|
+
if (typeof slimDefault === "boolean") return false;
|
|
2149
|
+
if (slimDefault === null) return null;
|
|
2150
|
+
return void 0;
|
|
2151
|
+
}
|
|
1991
2152
|
function readonlySetSnapshot(source) {
|
|
1992
2153
|
const snapshot = new Set(source);
|
|
1993
2154
|
return new Proxy(snapshot, {
|
|
@@ -2003,15 +2164,36 @@ function readonlySetSnapshot(source) {
|
|
|
2003
2164
|
});
|
|
2004
2165
|
}
|
|
2005
2166
|
function buildFormApi(state, formInstanceId, options = {}) {
|
|
2006
|
-
const
|
|
2167
|
+
const instanceMeta = (() => {
|
|
2168
|
+
const bag = {};
|
|
2169
|
+
if (options.validateOn !== void 0) bag.validateOn = options.validateOn;
|
|
2170
|
+
if (options.debounceMs !== void 0) bag.debounceMs = options.debounceMs;
|
|
2171
|
+
if (options.rememberVariants !== void 0) bag.rememberVariants = options.rememberVariants;
|
|
2172
|
+
return Object.keys(bag).length > 0 ? bag : void 0;
|
|
2173
|
+
})();
|
|
2174
|
+
const withInstanceMeta = (meta) => {
|
|
2175
|
+
if (instanceMeta === void 0) return meta;
|
|
2176
|
+
return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
|
|
2177
|
+
};
|
|
2178
|
+
const registerConfig = {
|
|
2179
|
+
...instanceMeta !== void 0 ? { instanceMeta } : {},
|
|
2180
|
+
...options.coerce !== void 0 ? { coerce: options.coerce } : {}
|
|
2181
|
+
};
|
|
2182
|
+
const register = buildRegister(
|
|
2183
|
+
state,
|
|
2184
|
+
formInstanceId,
|
|
2185
|
+
Object.keys(registerConfig).length > 0 ? registerConfig : void 0
|
|
2186
|
+
);
|
|
2007
2187
|
const processOptions = options.onInvalidSubmit !== void 0 ? { onInvalidSubmit: options.onInvalidSubmit } : {};
|
|
2008
2188
|
const {
|
|
2009
2189
|
validate: validateBuilt,
|
|
2010
2190
|
validateAsync: validateAsyncBuilt,
|
|
2191
|
+
process: processBuilt,
|
|
2011
2192
|
handleSubmit
|
|
2012
2193
|
} = buildProcessForm(state, formInstanceId, processOptions);
|
|
2013
2194
|
const validate = (pathInput) => validateBuilt(pathInput);
|
|
2014
2195
|
const validateAsync = (pathInput) => validateAsyncBuilt(pathInput);
|
|
2196
|
+
const process = (pathInput) => processBuilt(pathInput);
|
|
2015
2197
|
function pathToRef(pathInput) {
|
|
2016
2198
|
const segments = canonicalizePath(pathInput).segments;
|
|
2017
2199
|
return computed(() => getAtPath(state.form.value, segments));
|
|
@@ -2023,22 +2205,36 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2023
2205
|
next,
|
|
2024
2206
|
state.schema
|
|
2025
2207
|
);
|
|
2026
|
-
const ok2 = state.setValueAtPath([], walked2.cleanedValues);
|
|
2208
|
+
const ok2 = state.setValueAtPath([], walked2.cleanedValues, withInstanceMeta());
|
|
2027
2209
|
if (!ok2) return false;
|
|
2028
2210
|
for (const pathKey of walked2.paths) {
|
|
2029
2211
|
const segments2 = segmentsForPathKey(pathKey);
|
|
2030
2212
|
if (segments2 === null) continue;
|
|
2031
|
-
state.setValueAtPath(
|
|
2032
|
-
|
|
2033
|
-
|
|
2213
|
+
state.setValueAtPath(
|
|
2214
|
+
segments2,
|
|
2215
|
+
state.schema.getDefaultAtPath(segments2),
|
|
2216
|
+
withInstanceMeta({ blank: true })
|
|
2217
|
+
);
|
|
2034
2218
|
}
|
|
2035
2219
|
return true;
|
|
2036
2220
|
}
|
|
2037
2221
|
const segments = canonicalizePath(pathOrValue).segments;
|
|
2038
2222
|
if (isUnset(maybeValue)) {
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2223
|
+
const last = segments.length > 0 ? segments[segments.length - 1] : void 0;
|
|
2224
|
+
if (typeof last === "string") {
|
|
2225
|
+
const parent = segments.slice(0, -1);
|
|
2226
|
+
const parentDU = state.schema.getUnionDiscriminatorAtPath(parent);
|
|
2227
|
+
if (parentDU?.discriminatorKey === last) {
|
|
2228
|
+
const slimDefault = state.schema.getDefaultAtPath(segments);
|
|
2229
|
+
const blank = blankForKind(slimDefault);
|
|
2230
|
+
return state.setValueAtPath(segments, blank, withInstanceMeta({ blank: true }));
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
return state.setValueAtPath(
|
|
2234
|
+
segments,
|
|
2235
|
+
state.schema.getDefaultAtPath(segments),
|
|
2236
|
+
withInstanceMeta({ blank: true })
|
|
2237
|
+
);
|
|
2042
2238
|
}
|
|
2043
2239
|
let resolvedValue;
|
|
2044
2240
|
if (typeof maybeValue === "function") {
|
|
@@ -2046,9 +2242,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2046
2242
|
const prev = current === void 0 ? state.schema.getDefaultAtPath(segments) : current;
|
|
2047
2243
|
resolvedValue = maybeValue(prev);
|
|
2048
2244
|
if (isUnset(resolvedValue)) {
|
|
2049
|
-
return state.setValueAtPath(
|
|
2050
|
-
|
|
2051
|
-
|
|
2245
|
+
return state.setValueAtPath(
|
|
2246
|
+
segments,
|
|
2247
|
+
state.schema.getDefaultAtPath(segments),
|
|
2248
|
+
withInstanceMeta({ blank: true })
|
|
2249
|
+
);
|
|
2052
2250
|
}
|
|
2053
2251
|
} else {
|
|
2054
2252
|
resolvedValue = maybeValue;
|
|
@@ -2058,28 +2256,52 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2058
2256
|
segments,
|
|
2059
2257
|
state.schema
|
|
2060
2258
|
);
|
|
2061
|
-
const ok = state.setValueAtPath(segments, walked.cleanedValues);
|
|
2259
|
+
const ok = state.setValueAtPath(segments, walked.cleanedValues, withInstanceMeta());
|
|
2062
2260
|
if (!ok) return false;
|
|
2063
2261
|
for (const pathKey of walked.paths) {
|
|
2064
2262
|
const blankSegments = segmentsForPathKey(pathKey);
|
|
2065
2263
|
if (blankSegments === null) continue;
|
|
2066
|
-
state.setValueAtPath(
|
|
2067
|
-
|
|
2068
|
-
|
|
2264
|
+
state.setValueAtPath(
|
|
2265
|
+
blankSegments,
|
|
2266
|
+
state.schema.getDefaultAtPath(blankSegments),
|
|
2267
|
+
withInstanceMeta({ blank: true })
|
|
2268
|
+
);
|
|
2069
2269
|
}
|
|
2070
2270
|
return true;
|
|
2071
2271
|
}
|
|
2072
2272
|
const errorsProxy = buildErrorsProxy(state);
|
|
2273
|
+
function filterToOwnFormKey(errors, op) {
|
|
2274
|
+
const own = [];
|
|
2275
|
+
let dropped = 0;
|
|
2276
|
+
for (const e of errors) {
|
|
2277
|
+
if (e.formKey === state.formKey) own.push(e);
|
|
2278
|
+
else dropped++;
|
|
2279
|
+
}
|
|
2280
|
+
if (__DEV__ && dropped > 0) {
|
|
2281
|
+
console.warn(
|
|
2282
|
+
`[attaform] ${op}: dropped ${dropped} error(s) with non-matching formKey (this form's key is "${String(state.formKey)}"). Errors are scoped to the form that produced them \u2014 pass them to the matching form instance.`
|
|
2283
|
+
);
|
|
2284
|
+
}
|
|
2285
|
+
return own;
|
|
2286
|
+
}
|
|
2073
2287
|
function setFieldErrors(errors) {
|
|
2074
|
-
state.
|
|
2288
|
+
const preserved = state.userErrors.get(FORM_ERRORS_PATH_KEY);
|
|
2289
|
+
state.setAllUserErrors(filterToOwnFormKey(errors, "setFieldErrors"));
|
|
2290
|
+
if (preserved !== void 0 && preserved.length > 0) {
|
|
2291
|
+
state.userErrors.set(FORM_ERRORS_PATH_KEY, preserved);
|
|
2292
|
+
}
|
|
2075
2293
|
}
|
|
2076
2294
|
function addFieldErrors(errors) {
|
|
2077
|
-
state.addUserErrors(errors);
|
|
2295
|
+
state.addUserErrors(filterToOwnFormKey(errors, "addFieldErrors"));
|
|
2078
2296
|
}
|
|
2079
2297
|
function clearFieldErrors(path) {
|
|
2080
2298
|
if (path === void 0) {
|
|
2299
|
+
const preserved = state.userErrors.get(FORM_ERRORS_PATH_KEY);
|
|
2081
2300
|
state.clearSchemaErrors();
|
|
2082
2301
|
state.clearUserErrors();
|
|
2302
|
+
if (preserved !== void 0 && preserved.length > 0) {
|
|
2303
|
+
state.userErrors.set(FORM_ERRORS_PATH_KEY, preserved);
|
|
2304
|
+
}
|
|
2083
2305
|
return;
|
|
2084
2306
|
}
|
|
2085
2307
|
const segments = canonicalizePath(path).segments;
|
|
@@ -2112,11 +2334,17 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2112
2334
|
() => state.firstValidationDone.value && state.schemaErrors.size === 0 && state.userErrors.size === 0 && state.derivedBlankErrors.value.size === 0 && !validating.value
|
|
2113
2335
|
);
|
|
2114
2336
|
const history = options.history;
|
|
2115
|
-
const
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2337
|
+
const formHistory = readonly(
|
|
2338
|
+
reactive({
|
|
2339
|
+
undo: history?.undo ?? (() => false),
|
|
2340
|
+
redo: history?.redo ?? (() => false),
|
|
2341
|
+
clear: history?.clear ?? (() => {
|
|
2342
|
+
}),
|
|
2343
|
+
canUndo: history?.canUndo ?? computed(() => false),
|
|
2344
|
+
canRedo: history?.canRedo ?? computed(() => false),
|
|
2345
|
+
size: history?.historySize ?? computed(() => 0)
|
|
2346
|
+
})
|
|
2347
|
+
);
|
|
2120
2348
|
const metaErrors = computed(
|
|
2121
2349
|
() => aggregateErrorsAt(state, [])
|
|
2122
2350
|
);
|
|
@@ -2127,13 +2355,15 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2127
2355
|
submitting: state.submitting.value,
|
|
2128
2356
|
submitCount: state.submitCount.value,
|
|
2129
2357
|
submitError: state.submitError.value,
|
|
2130
|
-
canUndo: canUndo.value,
|
|
2131
|
-
canRedo: canRedo.value,
|
|
2132
|
-
historySize: historySize.value,
|
|
2133
2358
|
instanceId: formInstanceId
|
|
2134
2359
|
};
|
|
2135
2360
|
};
|
|
2136
|
-
const
|
|
2361
|
+
const fieldStateAccessorOptions = options.shouldShowErrors !== void 0 ? { shouldShowErrors: options.shouldShowErrors } : void 0;
|
|
2362
|
+
const getRootFieldStateAt = buildFieldStateAccessor(
|
|
2363
|
+
state,
|
|
2364
|
+
getFormMetaBase,
|
|
2365
|
+
fieldStateAccessorOptions
|
|
2366
|
+
);
|
|
2137
2367
|
const rootFieldState = getRootFieldStateAt([]);
|
|
2138
2368
|
const formMeta = readonly(
|
|
2139
2369
|
reactive({
|
|
@@ -2186,9 +2416,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2186
2416
|
submitting,
|
|
2187
2417
|
submitCount,
|
|
2188
2418
|
submitError,
|
|
2189
|
-
canUndo,
|
|
2190
|
-
canRedo,
|
|
2191
|
-
historySize,
|
|
2192
2419
|
// Per-`useForm()`-call identity. Stable for one mount; new on
|
|
2193
2420
|
// re-mount; orthogonal to `form.key` (which is the user-supplied
|
|
2194
2421
|
// shared identifier). Useful for devtools panels disambiguating
|
|
@@ -2210,9 +2437,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2210
2437
|
for (const pathKey of walked.paths) {
|
|
2211
2438
|
const segments = segmentsForPathKey(pathKey);
|
|
2212
2439
|
if (segments === null) continue;
|
|
2213
|
-
state.setValueAtPath(
|
|
2214
|
-
|
|
2215
|
-
|
|
2440
|
+
state.setValueAtPath(
|
|
2441
|
+
segments,
|
|
2442
|
+
state.schema.getDefaultAtPath(segments),
|
|
2443
|
+
withInstanceMeta({ blank: true })
|
|
2444
|
+
);
|
|
2216
2445
|
state.originalBlankPaths.add(pathKey);
|
|
2217
2446
|
}
|
|
2218
2447
|
}
|
|
@@ -2229,7 +2458,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2229
2458
|
};
|
|
2230
2459
|
const persist = async (pathInput, options2) => {
|
|
2231
2460
|
const segments = canonicalizePath(pathInput).segments;
|
|
2232
|
-
enforceSensitiveCheck(segments, options2?.acknowledgeSensitive === true);
|
|
2461
|
+
enforceSensitiveCheck(segments, options2?.acknowledgeSensitive === true, state.isSensitivePath);
|
|
2233
2462
|
if (persistence === void 0) return;
|
|
2234
2463
|
await persistence.writePathImmediately(segments);
|
|
2235
2464
|
};
|
|
@@ -2263,7 +2492,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2263
2492
|
return readonlySetSnapshot(state.blankPaths);
|
|
2264
2493
|
});
|
|
2265
2494
|
const valuesProxy = buildValuesProxy(state.form);
|
|
2266
|
-
const fieldStateProxy = buildFieldStateProxy(state, getFormMetaBase);
|
|
2495
|
+
const fieldStateProxy = buildFieldStateProxy(state, getFormMetaBase, fieldStateAccessorOptions);
|
|
2267
2496
|
return {
|
|
2268
2497
|
handleSubmit,
|
|
2269
2498
|
// `values` is the callable readonly Proxy. Each `get` trap reads
|
|
@@ -2276,6 +2505,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2276
2505
|
setValue: setValueImpl,
|
|
2277
2506
|
validate,
|
|
2278
2507
|
validateAsync,
|
|
2508
|
+
process,
|
|
2279
2509
|
register,
|
|
2280
2510
|
key: state.formKey,
|
|
2281
2511
|
errors: errorsProxy,
|
|
@@ -2292,9 +2522,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2292
2522
|
clearPersistedDraft,
|
|
2293
2523
|
focusFirstError,
|
|
2294
2524
|
scrollToFirstError,
|
|
2295
|
-
touch,
|
|
2296
|
-
|
|
2297
|
-
redo,
|
|
2525
|
+
touch,
|
|
2526
|
+
history: formHistory,
|
|
2298
2527
|
append: fieldArrays.append,
|
|
2299
2528
|
prepend: fieldArrays.prepend,
|
|
2300
2529
|
insert: fieldArrays.insert,
|
|
@@ -2339,6 +2568,42 @@ function warnMalformedHydration(formKey, kind, rawKey) {
|
|
|
2339
2568
|
`[attaform] hydration: skipping malformed ${kind} entry at key '${rawKey}' on form '${formKey}'. This usually means the SSR bundle is on a different version than the client (rolling deploy / stale cache).`
|
|
2340
2569
|
);
|
|
2341
2570
|
}
|
|
2571
|
+
function applyDuStubs(schema, data, options = {}) {
|
|
2572
|
+
const warned = options.warn === true ? /* @__PURE__ */ new Set() : void 0;
|
|
2573
|
+
return walkDuStubs(schema, data, options.basePath ?? [], warned);
|
|
2574
|
+
}
|
|
2575
|
+
function walkDuStubs(schema, value, path, warned) {
|
|
2576
|
+
if (value === null || value === void 0 || typeof value !== "object") return value;
|
|
2577
|
+
if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || typeof value === "function") {
|
|
2578
|
+
return value;
|
|
2579
|
+
}
|
|
2580
|
+
if (Array.isArray(value)) {
|
|
2581
|
+
return value.map((item, i) => walkDuStubs(schema, item, [...path, i], warned));
|
|
2582
|
+
}
|
|
2583
|
+
const rec = value;
|
|
2584
|
+
const du = schema.getUnionDiscriminatorAtPath(path);
|
|
2585
|
+
if (du !== void 0) {
|
|
2586
|
+
const discValue = rec[du.discriminatorKey];
|
|
2587
|
+
if (discValue !== void 0 && !du.isVariantSelected(discValue)) {
|
|
2588
|
+
if (warned !== void 0 && __DEV__) {
|
|
2589
|
+
const dotted = path.map((s) => String(s)).join(".") || "(root)";
|
|
2590
|
+
const key = `${dotted}::${String(discValue)}`;
|
|
2591
|
+
if (!warned.has(key)) {
|
|
2592
|
+
warned.add(key);
|
|
2593
|
+
console.warn(
|
|
2594
|
+
`[attaform] defaultValues at '${dotted}' carries discriminator '${du.discriminatorKey}=${JSON.stringify(discValue)}' which isn't a known variant. Form mounts in a stub holding only the discriminator key. Validation will surface the mismatch.`
|
|
2595
|
+
);
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
return { [du.discriminatorKey]: discValue };
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
const out = {};
|
|
2602
|
+
for (const k of Object.keys(rec)) {
|
|
2603
|
+
out[k] = walkDuStubs(schema, rec[k], [...path, k], warned);
|
|
2604
|
+
}
|
|
2605
|
+
return out;
|
|
2606
|
+
}
|
|
2342
2607
|
function isPathKeyUnder(existingKey, parentPath) {
|
|
2343
2608
|
const parsed = segmentsForPathKey(existingKey);
|
|
2344
2609
|
if (parsed === null) return false;
|
|
@@ -2348,21 +2613,96 @@ function isPathKeyUnder(existingKey, parentPath) {
|
|
|
2348
2613
|
}
|
|
2349
2614
|
return true;
|
|
2350
2615
|
}
|
|
2616
|
+
function stripSymbolsDeep(value) {
|
|
2617
|
+
if (value === null || typeof value !== "object") return value;
|
|
2618
|
+
if (Array.isArray(value)) {
|
|
2619
|
+
let mutated2 = false;
|
|
2620
|
+
const out2 = new Array(value.length);
|
|
2621
|
+
for (let i = 0; i < value.length; i++) {
|
|
2622
|
+
const cleaned = stripSymbolsDeep(value[i]);
|
|
2623
|
+
out2[i] = cleaned;
|
|
2624
|
+
if (cleaned !== value[i]) mutated2 = true;
|
|
2625
|
+
}
|
|
2626
|
+
return mutated2 ? out2 : value;
|
|
2627
|
+
}
|
|
2628
|
+
const proto = Object.getPrototypeOf(value);
|
|
2629
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
2630
|
+
const symKeys = Object.getOwnPropertySymbols(value);
|
|
2631
|
+
const stringKeys = Object.keys(value);
|
|
2632
|
+
let mutated = symKeys.length > 0;
|
|
2633
|
+
const out = {};
|
|
2634
|
+
const src = value;
|
|
2635
|
+
for (const k of stringKeys) {
|
|
2636
|
+
const cleaned = stripSymbolsDeep(src[k]);
|
|
2637
|
+
out[k] = cleaned;
|
|
2638
|
+
if (cleaned !== src[k]) mutated = true;
|
|
2639
|
+
}
|
|
2640
|
+
return mutated ? out : value;
|
|
2641
|
+
}
|
|
2642
|
+
function cloneVariantSnapshot(value) {
|
|
2643
|
+
if (value === null || typeof value !== "object") return value;
|
|
2644
|
+
const raw = toRaw(value);
|
|
2645
|
+
if (raw instanceof Date) return new Date(raw.getTime());
|
|
2646
|
+
if (raw instanceof Map) {
|
|
2647
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
2648
|
+
for (const [k, v] of raw.entries()) out2.set(cloneVariantSnapshot(k), cloneVariantSnapshot(v));
|
|
2649
|
+
return out2;
|
|
2650
|
+
}
|
|
2651
|
+
if (raw instanceof Set) {
|
|
2652
|
+
const out2 = /* @__PURE__ */ new Set();
|
|
2653
|
+
for (const v of raw) out2.add(cloneVariantSnapshot(v));
|
|
2654
|
+
return out2;
|
|
2655
|
+
}
|
|
2656
|
+
if (raw instanceof RegExp) return new RegExp(raw.source, raw.flags);
|
|
2657
|
+
if (Array.isArray(raw)) {
|
|
2658
|
+
const out2 = new Array(raw.length);
|
|
2659
|
+
for (let i = 0; i < raw.length; i++) out2[i] = cloneVariantSnapshot(raw[i]);
|
|
2660
|
+
return out2;
|
|
2661
|
+
}
|
|
2662
|
+
const src = raw;
|
|
2663
|
+
const out = {};
|
|
2664
|
+
for (const k of Object.keys(src)) out[k] = cloneVariantSnapshot(src[k]);
|
|
2665
|
+
return out;
|
|
2666
|
+
}
|
|
2351
2667
|
function createFormStore(options) {
|
|
2352
2668
|
const { formKey, schema, defaultValues, strict = true, hydration } = options;
|
|
2353
2669
|
const ssr = options.ssr === true;
|
|
2354
2670
|
const rememberVariants = options.rememberVariants !== false;
|
|
2355
2671
|
const fieldValidationMode = options.validateOn ?? "change";
|
|
2356
|
-
const fieldValidationDebounceMs =
|
|
2672
|
+
const fieldValidationDebounceMs = normalizeNumericOption({
|
|
2673
|
+
value: options.debounceMs ?? DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS,
|
|
2674
|
+
source: "useForm.debounceMs",
|
|
2675
|
+
allowInfinity: false,
|
|
2676
|
+
min: 0,
|
|
2677
|
+
defaultValue: DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS
|
|
2678
|
+
});
|
|
2357
2679
|
const fieldValidationState = /* @__PURE__ */ new Map();
|
|
2358
2680
|
const formChangeListeners = /* @__PURE__ */ new Set();
|
|
2359
2681
|
const submitSuccessListeners = /* @__PURE__ */ new Set();
|
|
2360
2682
|
const resetListeners = /* @__PURE__ */ new Set();
|
|
2361
2683
|
const persistOptIns = createPersistOptInRegistry();
|
|
2684
|
+
const noSyncPaths = /* @__PURE__ */ new Set();
|
|
2685
|
+
const noSyncPathCounts = /* @__PURE__ */ new Map();
|
|
2686
|
+
function incrementNoSyncOptOut(path) {
|
|
2687
|
+
const next = (noSyncPathCounts.get(path) ?? 0) + 1;
|
|
2688
|
+
noSyncPathCounts.set(path, next);
|
|
2689
|
+
if (next === 1) noSyncPaths.add(path);
|
|
2690
|
+
}
|
|
2691
|
+
function decrementNoSyncOptOut(path) {
|
|
2692
|
+
const current = noSyncPathCounts.get(path) ?? 0;
|
|
2693
|
+
if (current <= 1) {
|
|
2694
|
+
noSyncPathCounts.delete(path);
|
|
2695
|
+
noSyncPaths.delete(path);
|
|
2696
|
+
return;
|
|
2697
|
+
}
|
|
2698
|
+
noSyncPathCounts.set(path, current - 1);
|
|
2699
|
+
}
|
|
2362
2700
|
const coerceIndex = resolveCoercionIndex(options.coerce);
|
|
2363
2701
|
const resolvedShouldShowErrors = resolveShouldShowErrors(
|
|
2364
2702
|
options.shouldShowErrors
|
|
2365
2703
|
);
|
|
2704
|
+
const resolvedIsSensitivePath = options.isSensitivePath ?? isSensitivePath;
|
|
2705
|
+
const resolvedSegmentMatchesSensitive = options.segmentMatchesSensitive ?? segmentMatchesSensitive;
|
|
2366
2706
|
const cleanupHooks = [];
|
|
2367
2707
|
const modules = /* @__PURE__ */ new Map();
|
|
2368
2708
|
const completedConstraints = defaultValues === void 0 ? void 0 : mergeStructural(schema, [], defaultValues);
|
|
@@ -2373,7 +2713,10 @@ function createFormStore(options) {
|
|
|
2373
2713
|
});
|
|
2374
2714
|
const schemaInitialData = schemaResponse.data;
|
|
2375
2715
|
const initialData = hydration !== void 0 ? hydration.form : structuralSnapshot(schemaInitialData);
|
|
2376
|
-
const
|
|
2716
|
+
const stubbedInitialData = applyDuStubs(schema, initialData, {
|
|
2717
|
+
warn: true
|
|
2718
|
+
});
|
|
2719
|
+
const form = ref(stubbedInitialData);
|
|
2377
2720
|
const fields = reactive(/* @__PURE__ */ new Map());
|
|
2378
2721
|
const elements = reactive(/* @__PURE__ */ new Map());
|
|
2379
2722
|
const elementToFormInstance = /* @__PURE__ */ new WeakMap();
|
|
@@ -2389,6 +2732,40 @@ function createFormStore(options) {
|
|
|
2389
2732
|
originalBlankPaths.add(raw);
|
|
2390
2733
|
}
|
|
2391
2734
|
const variantMemory = /* @__PURE__ */ new Map();
|
|
2735
|
+
function clearVariantMemoryUnderPath(arrayPath) {
|
|
2736
|
+
for (const memKey of [...variantMemory.keys()]) {
|
|
2737
|
+
const segs = segmentsForPathKey(memKey);
|
|
2738
|
+
if (segs === null) continue;
|
|
2739
|
+
if (isPathPrefix(arrayPath, segs)) variantMemory.delete(memKey);
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
function clearVariantMemoryAtArrayIndices(arrayPath, indexFilter) {
|
|
2743
|
+
for (const memKey of [...variantMemory.keys()]) {
|
|
2744
|
+
const segs = segmentsForPathKey(memKey);
|
|
2745
|
+
if (segs === null) continue;
|
|
2746
|
+
if (!isPathPrefix(arrayPath, segs)) continue;
|
|
2747
|
+
if (segs.length <= arrayPath.length) continue;
|
|
2748
|
+
const idxSeg = segs[arrayPath.length];
|
|
2749
|
+
if (typeof idxSeg !== "number") continue;
|
|
2750
|
+
if (indexFilter(idxSeg)) variantMemory.delete(memKey);
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
function applyArrayOpToMemory(arrayPath, op) {
|
|
2754
|
+
switch (op.kind) {
|
|
2755
|
+
case "shift-from":
|
|
2756
|
+
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i >= op.index);
|
|
2757
|
+
return;
|
|
2758
|
+
case "shift-range":
|
|
2759
|
+
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i >= op.fromIndex && i <= op.toIndex);
|
|
2760
|
+
return;
|
|
2761
|
+
case "swap":
|
|
2762
|
+
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i === op.a || i === op.b);
|
|
2763
|
+
return;
|
|
2764
|
+
case "replace-at":
|
|
2765
|
+
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i === op.index);
|
|
2766
|
+
return;
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2392
2769
|
const pathOrdinals = /* @__PURE__ */ new Map();
|
|
2393
2770
|
let nextOrdinal = 0;
|
|
2394
2771
|
function ensurePathOrdinal(key) {
|
|
@@ -2540,9 +2917,35 @@ function createFormStore(options) {
|
|
|
2540
2917
|
}
|
|
2541
2918
|
}
|
|
2542
2919
|
function setValueAtPath(path, value, meta) {
|
|
2920
|
+
value = stripSymbolsDeep(value);
|
|
2921
|
+
value = schema.normalizeWriteValueAtPath(value, path);
|
|
2543
2922
|
if (!isSlimPrimitiveValid(schema, form, path, value)) {
|
|
2544
2923
|
return false;
|
|
2545
2924
|
}
|
|
2925
|
+
if (path.length >= 2) {
|
|
2926
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
2927
|
+
const ancestorPath = path.slice(0, i + 1);
|
|
2928
|
+
const du = schema.getUnionDiscriminatorAtPath(ancestorPath);
|
|
2929
|
+
if (du === void 0) continue;
|
|
2930
|
+
const nextSeg = path[i + 1];
|
|
2931
|
+
if (nextSeg === du.discriminatorKey) continue;
|
|
2932
|
+
const ancestorValue = getAtPath(form.value, ancestorPath);
|
|
2933
|
+
if (!isPlainRecord(ancestorValue)) continue;
|
|
2934
|
+
const discValue = ancestorValue[du.discriminatorKey];
|
|
2935
|
+
if (discValue === void 0) {
|
|
2936
|
+
return false;
|
|
2937
|
+
}
|
|
2938
|
+
if (!du.isVariantSelected(discValue)) {
|
|
2939
|
+
return false;
|
|
2940
|
+
}
|
|
2941
|
+
const variantDefault = du.getVariantDefault(discValue);
|
|
2942
|
+
if (!isPlainRecord(variantDefault)) continue;
|
|
2943
|
+
if (typeof nextSeg !== "string") continue;
|
|
2944
|
+
if (!(nextSeg in variantDefault)) {
|
|
2945
|
+
return false;
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2546
2949
|
if (meta?.skipDiscriminatorReshape !== true) {
|
|
2547
2950
|
if (path.length > 0) {
|
|
2548
2951
|
const last = path[path.length - 1];
|
|
@@ -2563,6 +2966,14 @@ function createFormStore(options) {
|
|
|
2563
2966
|
meta
|
|
2564
2967
|
);
|
|
2565
2968
|
}
|
|
2969
|
+
return reshapeUnionVariant(
|
|
2970
|
+
parentPath,
|
|
2971
|
+
oldValue,
|
|
2972
|
+
value,
|
|
2973
|
+
{ [last]: value },
|
|
2974
|
+
void 0,
|
|
2975
|
+
meta
|
|
2976
|
+
);
|
|
2566
2977
|
}
|
|
2567
2978
|
}
|
|
2568
2979
|
}
|
|
@@ -2571,12 +2982,13 @@ function createFormStore(options) {
|
|
|
2571
2982
|
const selfDU = schema.getUnionDiscriminatorAtPath(path);
|
|
2572
2983
|
if (selfDU !== void 0) {
|
|
2573
2984
|
const valueRecord = value;
|
|
2574
|
-
const
|
|
2985
|
+
const discKey = selfDU.discriminatorKey;
|
|
2986
|
+
const discValue = valueRecord[discKey];
|
|
2987
|
+
const currentUnionValue = getAtPath(form.value, path);
|
|
2988
|
+
const oldDiscValue = isPlainRecord(currentUnionValue) ? currentUnionValue[discKey] : void 0;
|
|
2575
2989
|
if (discValue !== void 0) {
|
|
2576
2990
|
const variantDefault = selfDU.getVariantDefault(discValue);
|
|
2577
2991
|
if (variantDefault !== void 0 && isPlainRecord(variantDefault)) {
|
|
2578
|
-
const currentUnionValue = getAtPath(form.value, path);
|
|
2579
|
-
const oldDiscValue = isPlainRecord(currentUnionValue) ? currentUnionValue[selfDU.discriminatorKey] : void 0;
|
|
2580
2992
|
return reshapeUnionVariant(
|
|
2581
2993
|
path,
|
|
2582
2994
|
oldDiscValue,
|
|
@@ -2586,7 +2998,16 @@ function createFormStore(options) {
|
|
|
2586
2998
|
meta
|
|
2587
2999
|
);
|
|
2588
3000
|
}
|
|
3001
|
+
return reshapeUnionVariant(
|
|
3002
|
+
path,
|
|
3003
|
+
oldDiscValue,
|
|
3004
|
+
discValue,
|
|
3005
|
+
{ [discKey]: discValue },
|
|
3006
|
+
void 0,
|
|
3007
|
+
meta
|
|
3008
|
+
);
|
|
2589
3009
|
}
|
|
3010
|
+
return reshapeUnionVariant(path, oldDiscValue, void 0, {}, void 0, meta);
|
|
2590
3011
|
}
|
|
2591
3012
|
}
|
|
2592
3013
|
}
|
|
@@ -2603,12 +3024,17 @@ function createFormStore(options) {
|
|
|
2603
3024
|
}
|
|
2604
3025
|
const nextForm = setAtPathWithSchemaFill(form.value, schema, path, completedValue);
|
|
2605
3026
|
applyFormReplacement(nextForm, meta);
|
|
2606
|
-
if (
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
3027
|
+
if (meta?.arrayOp !== void 0) {
|
|
3028
|
+
applyArrayOpToMemory(path, meta.arrayOp);
|
|
3029
|
+
} else if (Array.isArray(value) && Array.isArray(currentValue)) {
|
|
3030
|
+
clearVariantMemoryUnderPath(path);
|
|
3031
|
+
}
|
|
3032
|
+
const effectiveModeAfterWrite = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
3033
|
+
if (effectiveModeAfterWrite === "change") {
|
|
3034
|
+
scheduleFieldValidation(path, false, {
|
|
3035
|
+
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
3036
|
+
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
3037
|
+
});
|
|
2612
3038
|
}
|
|
2613
3039
|
return true;
|
|
2614
3040
|
}
|
|
@@ -2617,9 +3043,10 @@ function createFormStore(options) {
|
|
|
2617
3043
|
const parentKey = canonicalizePath(parentPath).key;
|
|
2618
3044
|
let baseline = variantDefault;
|
|
2619
3045
|
let restoredBlanks;
|
|
2620
|
-
|
|
3046
|
+
const effectiveRemember = meta?.instance?.rememberVariants ?? rememberVariants;
|
|
3047
|
+
if (effectiveRemember && !sameDisc) {
|
|
2621
3048
|
if (oldDiscValue !== void 0) {
|
|
2622
|
-
const currentValue2 =
|
|
3049
|
+
const currentValue2 = cloneVariantSnapshot(getAtPath(form.value, parentPath));
|
|
2623
3050
|
const outgoingBlanks = [];
|
|
2624
3051
|
for (const k of blankPaths) {
|
|
2625
3052
|
if (isPathKeyUnder(k, parentPath)) outgoingBlanks.push(k);
|
|
@@ -2641,7 +3068,10 @@ function createFormStore(options) {
|
|
|
2641
3068
|
restoredBlanks = [...restored.blankPaths];
|
|
2642
3069
|
}
|
|
2643
3070
|
}
|
|
2644
|
-
const
|
|
3071
|
+
const layered = consumerOverrides !== void 0 ? { ...baseline, ...consumerOverrides } : baseline;
|
|
3072
|
+
const finalValue = applyDuStubs(schema, layered, {
|
|
3073
|
+
basePath: parentPath
|
|
3074
|
+
});
|
|
2645
3075
|
let newBlankPaths;
|
|
2646
3076
|
if (restoredBlanks !== void 0) {
|
|
2647
3077
|
newBlankPaths = restoredBlanks;
|
|
@@ -2660,9 +3090,10 @@ function createFormStore(options) {
|
|
|
2660
3090
|
for (const k of newBlankPaths) blankPaths.add(k);
|
|
2661
3091
|
return true;
|
|
2662
3092
|
}
|
|
2663
|
-
const nextForm = parentPath.length === 0 ? finalValue :
|
|
3093
|
+
const nextForm = parentPath.length === 0 ? finalValue : setAtPathWithSchemaFill(form.value, schema, parentPath, finalValue);
|
|
2664
3094
|
let appliedSync = false;
|
|
2665
|
-
|
|
3095
|
+
const reshapeMode = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
3096
|
+
if (reshapeMode === "change") {
|
|
2666
3097
|
const syncOrPromise = schema.validateAtPath(finalValue, parentPath, { sync: true });
|
|
2667
3098
|
if (!(syncOrPromise instanceof Promise)) {
|
|
2668
3099
|
const reStamped = syncOrPromise.success ? [] : syncOrPromise.errors.map((err) => ({
|
|
@@ -2682,17 +3113,18 @@ function createFormStore(options) {
|
|
|
2682
3113
|
}
|
|
2683
3114
|
applyFormReplacement(nextForm, meta);
|
|
2684
3115
|
for (const k of newBlankPaths) blankPaths.add(k);
|
|
2685
|
-
if (
|
|
2686
|
-
scheduleFieldValidation(
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
);
|
|
3116
|
+
if (reshapeMode === "change" && !appliedSync) {
|
|
3117
|
+
scheduleFieldValidation(parentPath, false, {
|
|
3118
|
+
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
3119
|
+
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
3120
|
+
});
|
|
2691
3121
|
}
|
|
2692
3122
|
return true;
|
|
2693
3123
|
}
|
|
2694
|
-
function scheduleFieldValidation(path, immediate) {
|
|
2695
|
-
|
|
3124
|
+
function scheduleFieldValidation(path, immediate, override) {
|
|
3125
|
+
const effectiveMode = override?.mode ?? fieldValidationMode;
|
|
3126
|
+
if (effectiveMode === "submit") return;
|
|
3127
|
+
const effectiveDebounce = override?.debounceMs ?? fieldValidationDebounceMs;
|
|
2696
3128
|
const { key } = canonicalizePath(path);
|
|
2697
3129
|
const prev = fieldValidationState.get(key);
|
|
2698
3130
|
if (prev !== void 0) {
|
|
@@ -2706,8 +3138,17 @@ function createFormStore(options) {
|
|
|
2706
3138
|
fresh.timer = null;
|
|
2707
3139
|
if (controller.signal.aborted) return;
|
|
2708
3140
|
const data = getAtPath(form.value, path);
|
|
2709
|
-
|
|
2710
|
-
|
|
3141
|
+
let activeIncremented = false;
|
|
3142
|
+
try {
|
|
3143
|
+
activeValidations.value += 1;
|
|
3144
|
+
activeIncremented = true;
|
|
3145
|
+
incFieldValidation(key);
|
|
3146
|
+
} catch (err) {
|
|
3147
|
+
if (activeIncremented) {
|
|
3148
|
+
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
3149
|
+
}
|
|
3150
|
+
throw err;
|
|
3151
|
+
}
|
|
2711
3152
|
void Promise.resolve().then(() => schema.validateAtPath(data, path)).then((response) => {
|
|
2712
3153
|
if (controller.signal.aborted) return;
|
|
2713
3154
|
const reStamped = response.success ? [] : response.errors.map((err) => ({
|
|
@@ -2721,10 +3162,10 @@ function createFormStore(options) {
|
|
|
2721
3162
|
decFieldValidation(key);
|
|
2722
3163
|
});
|
|
2723
3164
|
};
|
|
2724
|
-
if (immediate ||
|
|
3165
|
+
if (immediate || effectiveDebounce === 0) {
|
|
2725
3166
|
run();
|
|
2726
3167
|
} else {
|
|
2727
|
-
fresh.timer = setTimeout(run,
|
|
3168
|
+
fresh.timer = setTimeout(run, effectiveDebounce);
|
|
2728
3169
|
}
|
|
2729
3170
|
}
|
|
2730
3171
|
function cancelFieldValidation() {
|
|
@@ -2788,6 +3229,8 @@ function createFormStore(options) {
|
|
|
2788
3229
|
submitSuccessListeners.clear();
|
|
2789
3230
|
resetListeners.clear();
|
|
2790
3231
|
persistOptIns.clear();
|
|
3232
|
+
noSyncPaths.clear();
|
|
3233
|
+
noSyncPathCounts.clear();
|
|
2791
3234
|
}
|
|
2792
3235
|
function getValueAtPath(path) {
|
|
2793
3236
|
return getAtPath(form.value, path);
|
|
@@ -2922,7 +3365,7 @@ function createFormStore(options) {
|
|
|
2922
3365
|
if (current?.connected === true) return;
|
|
2923
3366
|
touchFieldRecord(key, path, { connected: true });
|
|
2924
3367
|
}
|
|
2925
|
-
function markFocused(path, focused) {
|
|
3368
|
+
function markFocused(path, focused, meta) {
|
|
2926
3369
|
const { key } = canonicalizePath(path);
|
|
2927
3370
|
touchFieldRecord(key, path, {
|
|
2928
3371
|
focused,
|
|
@@ -2931,12 +3374,12 @@ function createFormStore(options) {
|
|
|
2931
3374
|
// a field is currently focused we keep whatever value it held.
|
|
2932
3375
|
touched: focused ? fields.get(key)?.touched ?? null : true
|
|
2933
3376
|
});
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
);
|
|
3377
|
+
const focusMode = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
3378
|
+
if (!focused && focusMode === "blur") {
|
|
3379
|
+
scheduleFieldValidation(path, true, {
|
|
3380
|
+
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
3381
|
+
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
3382
|
+
});
|
|
2940
3383
|
}
|
|
2941
3384
|
}
|
|
2942
3385
|
function markTouched(path) {
|
|
@@ -2962,11 +3405,14 @@ function createFormStore(options) {
|
|
|
2962
3405
|
}
|
|
2963
3406
|
}
|
|
2964
3407
|
function reset(nextDefaultValues) {
|
|
2965
|
-
const
|
|
3408
|
+
const resetSource = nextDefaultValues ?? defaultValues;
|
|
3409
|
+
const completedResetConstraints = resetSource === void 0 ? void 0 : mergeStructural(schema, [], resetSource);
|
|
3410
|
+
const resetResponse = schema.getDefaultValues({
|
|
2966
3411
|
useDefaultSchemaValues: true,
|
|
2967
|
-
constraints:
|
|
3412
|
+
constraints: completedResetConstraints,
|
|
2968
3413
|
strict
|
|
2969
|
-
})
|
|
3414
|
+
});
|
|
3415
|
+
const next = resetResponse.data;
|
|
2970
3416
|
applyFormReplacement(next);
|
|
2971
3417
|
originals.clear();
|
|
2972
3418
|
diffAndApply({}, next, [], (patch) => {
|
|
@@ -2985,6 +3431,24 @@ function createFormStore(options) {
|
|
|
2985
3431
|
}
|
|
2986
3432
|
schemaErrors.clear();
|
|
2987
3433
|
userErrors.clear();
|
|
3434
|
+
if (strict && !resetResponse.success) {
|
|
3435
|
+
setAllSchemaErrors(resetResponse.errors);
|
|
3436
|
+
}
|
|
3437
|
+
if (strict) {
|
|
3438
|
+
const syncResult = schema.validateAtPath(form.value, void 0, { sync: true });
|
|
3439
|
+
if (!(syncResult instanceof Promise) && !syncResult.success) {
|
|
3440
|
+
applySchemaErrorsForSubtree([], syncResult.errors);
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
firstValidationDone.value = !strict || schema.needsAsyncValidation?.() !== true;
|
|
3444
|
+
const needsAsync = !ssr && strict && schema.needsAsyncValidation?.() === true;
|
|
3445
|
+
if (needsAsync) {
|
|
3446
|
+
queueMicrotask(() => scheduleFieldValidation(
|
|
3447
|
+
[],
|
|
3448
|
+
true
|
|
3449
|
+
/* immediate */
|
|
3450
|
+
));
|
|
3451
|
+
}
|
|
2988
3452
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2989
3453
|
for (const [pathKey, record] of fields) {
|
|
2990
3454
|
fields.set(pathKey, {
|
|
@@ -3028,30 +3492,28 @@ function createFormStore(options) {
|
|
|
3028
3492
|
`[attaform] resetField: leaf write rejected for path '${targetKey}' \u2014 originals contain a value that doesn't satisfy the slim primitive shape. This is a bug in the construction pipeline.`
|
|
3029
3493
|
);
|
|
3030
3494
|
}
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
`[attaform] resetField: subtree write rejected at path '${targetKey}' \u2014 originals contain values that don't satisfy the slim primitive shape. This is a bug in the construction pipeline.`
|
|
3054
|
-
);
|
|
3495
|
+
} else {
|
|
3496
|
+
let subtree = void 0;
|
|
3497
|
+
let anyMatch = false;
|
|
3498
|
+
for (const [, entry] of originals) {
|
|
3499
|
+
const leafSegments = entry.segments;
|
|
3500
|
+
if (!isPathPrefix(targetSegments, leafSegments)) continue;
|
|
3501
|
+
if (leafSegments.length === targetSegments.length) continue;
|
|
3502
|
+
anyMatch = true;
|
|
3503
|
+
const relative = leafSegments.slice(targetSegments.length);
|
|
3504
|
+
if (subtree === void 0) {
|
|
3505
|
+
subtree = typeof relative[0] === "number" ? [] : {};
|
|
3506
|
+
}
|
|
3507
|
+
subtree = setAtPath(subtree, relative, entry.value);
|
|
3508
|
+
}
|
|
3509
|
+
if (anyMatch) {
|
|
3510
|
+
const wroteSubtree = setValueAtPath(targetSegments, subtree);
|
|
3511
|
+
if (!wroteSubtree) {
|
|
3512
|
+
console.error(
|
|
3513
|
+
`[attaform] resetField: subtree write rejected at path '${targetKey}' \u2014 originals contain values that don't satisfy the slim primitive shape. This is a bug in the construction pipeline.`
|
|
3514
|
+
);
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3055
3517
|
}
|
|
3056
3518
|
deleteErrorsUnderPrefix(schemaErrors, targetSegments);
|
|
3057
3519
|
deleteErrorsUnderPrefix(userErrors, targetSegments);
|
|
@@ -3181,6 +3643,11 @@ function createFormStore(options) {
|
|
|
3181
3643
|
awaitPendingWrites,
|
|
3182
3644
|
modules,
|
|
3183
3645
|
persistOptIns,
|
|
3646
|
+
isSensitivePath: resolvedIsSensitivePath,
|
|
3647
|
+
segmentMatchesSensitive: resolvedSegmentMatchesSensitive,
|
|
3648
|
+
noSyncPaths,
|
|
3649
|
+
incrementNoSyncOptOut,
|
|
3650
|
+
decrementNoSyncOptOut,
|
|
3184
3651
|
coerceIndex,
|
|
3185
3652
|
blankPaths,
|
|
3186
3653
|
originalBlankPaths,
|
|
@@ -3188,43 +3655,140 @@ function createFormStore(options) {
|
|
|
3188
3655
|
};
|
|
3189
3656
|
}
|
|
3190
3657
|
|
|
3191
|
-
function getComputedSchema(formKey, schemaOrCallback) {
|
|
3192
|
-
if (typeof schemaOrCallback === "function") return schemaOrCallback(formKey);
|
|
3658
|
+
function getComputedSchema(formKey, schemaOrCallback, options) {
|
|
3659
|
+
if (typeof schemaOrCallback === "function") return schemaOrCallback(formKey, options);
|
|
3193
3660
|
return schemaOrCallback;
|
|
3194
3661
|
}
|
|
3195
3662
|
|
|
3663
|
+
function captureErrorEntries(map) {
|
|
3664
|
+
const out = [];
|
|
3665
|
+
for (const [k, v] of map) out.push([k, [...v]]);
|
|
3666
|
+
return out;
|
|
3667
|
+
}
|
|
3668
|
+
function errorsEqual(a, b) {
|
|
3669
|
+
if (a.length !== b.length) return false;
|
|
3670
|
+
const bMap = /* @__PURE__ */ new Map();
|
|
3671
|
+
for (const [k, v] of b) bMap.set(k, v);
|
|
3672
|
+
for (const [k, v] of a) {
|
|
3673
|
+
const bv = bMap.get(k);
|
|
3674
|
+
if (bv === void 0) return false;
|
|
3675
|
+
if (v.length !== bv.length) return false;
|
|
3676
|
+
for (let i = 0; i < v.length; i++) {
|
|
3677
|
+
const av = v[i];
|
|
3678
|
+
const bvi = bv[i];
|
|
3679
|
+
if (av === bvi) continue;
|
|
3680
|
+
if (av.message !== bvi.message) return false;
|
|
3681
|
+
if (av.code !== bvi.code) return false;
|
|
3682
|
+
if (av.formKey !== bvi.formKey) return false;
|
|
3683
|
+
if (av.path !== bvi.path) {
|
|
3684
|
+
if (av.path.length !== bvi.path.length) return false;
|
|
3685
|
+
for (let j = 0; j < av.path.length; j++) {
|
|
3686
|
+
if (av.path[j] !== bvi.path[j]) return false;
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
return true;
|
|
3692
|
+
}
|
|
3693
|
+
function diffBlankPaths$1(prev, curr) {
|
|
3694
|
+
const added = [];
|
|
3695
|
+
const removed = [];
|
|
3696
|
+
for (const k of curr) if (!prev.has(k)) added.push(k);
|
|
3697
|
+
for (const k of prev) if (!curr.has(k)) removed.push(k);
|
|
3698
|
+
return { added, removed };
|
|
3699
|
+
}
|
|
3700
|
+
function applyDeltaForward(snap, d) {
|
|
3701
|
+
const nextForm = applyPatchesForward(snap.form, d.formPatches);
|
|
3702
|
+
const nextBlank = new Set(snap.blankPaths);
|
|
3703
|
+
for (const k of d.blankPathsRemoved) nextBlank.delete(k);
|
|
3704
|
+
for (const k of d.blankPathsAdded) nextBlank.add(k);
|
|
3705
|
+
return {
|
|
3706
|
+
form: nextForm,
|
|
3707
|
+
blankPaths: [...nextBlank],
|
|
3708
|
+
schemaErrors: d.schemaErrors !== void 0 ? d.schemaErrors.after : snap.schemaErrors,
|
|
3709
|
+
userErrors: d.userErrors !== void 0 ? d.userErrors.after : snap.userErrors
|
|
3710
|
+
};
|
|
3711
|
+
}
|
|
3712
|
+
function applyDeltaInverse(snap, d) {
|
|
3713
|
+
const prevForm = applyPatchesInverse(snap.form, d.formPatches);
|
|
3714
|
+
const prevBlank = new Set(snap.blankPaths);
|
|
3715
|
+
for (const k of d.blankPathsAdded) prevBlank.delete(k);
|
|
3716
|
+
for (const k of d.blankPathsRemoved) prevBlank.add(k);
|
|
3717
|
+
return {
|
|
3718
|
+
form: prevForm,
|
|
3719
|
+
blankPaths: [...prevBlank],
|
|
3720
|
+
schemaErrors: d.schemaErrors !== void 0 ? d.schemaErrors.before : snap.schemaErrors,
|
|
3721
|
+
userErrors: d.userErrors !== void 0 ? d.userErrors.before : snap.userErrors
|
|
3722
|
+
};
|
|
3723
|
+
}
|
|
3196
3724
|
function createHistoryModule(state, config) {
|
|
3197
|
-
const max =
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3725
|
+
const max = normalizeNumericOption({
|
|
3726
|
+
value: typeof config === "object" ? config.max ?? DEFAULT_HISTORY_MAX_SNAPSHOTS : DEFAULT_HISTORY_MAX_SNAPSHOTS,
|
|
3727
|
+
source: "useForm.history.max",
|
|
3728
|
+
allowInfinity: false,
|
|
3729
|
+
min: 0,
|
|
3730
|
+
defaultValue: DEFAULT_HISTORY_MAX_SNAPSHOTS
|
|
3731
|
+
});
|
|
3201
3732
|
function captureSnapshot() {
|
|
3202
3733
|
return {
|
|
3203
3734
|
form: structuralSnapshot(state.form.value),
|
|
3204
3735
|
blankPaths: [...state.blankPaths],
|
|
3205
|
-
schemaErrors:
|
|
3206
|
-
userErrors:
|
|
3736
|
+
schemaErrors: captureErrorEntries(state.schemaErrors),
|
|
3737
|
+
userErrors: captureErrorEntries(state.userErrors)
|
|
3207
3738
|
};
|
|
3208
3739
|
}
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3740
|
+
const initial = captureSnapshot();
|
|
3741
|
+
const base = shallowRef(initial);
|
|
3742
|
+
const currentSnapshot = shallowRef(initial);
|
|
3743
|
+
const undoDeltas = shallowRef([]);
|
|
3744
|
+
const redoDeltas = shallowRef([]);
|
|
3745
|
+
let suppressNext = false;
|
|
3746
|
+
function appendDelta(delta, newCurrent) {
|
|
3747
|
+
if (max === 0) {
|
|
3748
|
+
base.value = newCurrent;
|
|
3749
|
+
currentSnapshot.value = newCurrent;
|
|
3750
|
+
redoDeltas.value = [];
|
|
3751
|
+
return;
|
|
3752
|
+
}
|
|
3753
|
+
undoDeltas.value = [...undoDeltas.value, delta];
|
|
3754
|
+
redoDeltas.value = [];
|
|
3755
|
+
currentSnapshot.value = newCurrent;
|
|
3756
|
+
while (1 + undoDeltas.value.length > max && undoDeltas.value.length > 0) {
|
|
3757
|
+
const oldest = undoDeltas.value[0];
|
|
3758
|
+
base.value = applyDeltaForward(base.value, oldest);
|
|
3759
|
+
undoDeltas.value = undoDeltas.value.slice(1);
|
|
3760
|
+
}
|
|
3213
3761
|
}
|
|
3214
|
-
|
|
3215
|
-
const unsubscribeChange = state.onFormChange(() => {
|
|
3762
|
+
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
3216
3763
|
if (suppressNext) {
|
|
3217
3764
|
suppressNext = false;
|
|
3218
3765
|
return;
|
|
3219
3766
|
}
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3767
|
+
if (meta?.hydration === true) {
|
|
3768
|
+
clear();
|
|
3769
|
+
return;
|
|
3770
|
+
}
|
|
3771
|
+
if (meta?.crossTab === true) {
|
|
3772
|
+
currentSnapshot.value = captureSnapshot();
|
|
3773
|
+
return;
|
|
3774
|
+
}
|
|
3775
|
+
const newSnap = captureSnapshot();
|
|
3776
|
+
const prevSnap = currentSnapshot.value;
|
|
3777
|
+
const formPatches = [];
|
|
3778
|
+
diffAndApply(prevSnap.form, newSnap.form, [], (p) => formPatches.push(p));
|
|
3779
|
+
const prevBlankSet = new Set(prevSnap.blankPaths);
|
|
3780
|
+
const currBlankSet = new Set(newSnap.blankPaths);
|
|
3781
|
+
const blankDiff = diffBlankPaths$1(prevBlankSet, currBlankSet);
|
|
3782
|
+
const delta = {
|
|
3783
|
+
formPatches,
|
|
3784
|
+
blankPathsAdded: blankDiff.added,
|
|
3785
|
+
blankPathsRemoved: blankDiff.removed,
|
|
3786
|
+
...errorsEqual(prevSnap.schemaErrors, newSnap.schemaErrors) ? {} : { schemaErrors: { before: prevSnap.schemaErrors, after: newSnap.schemaErrors } },
|
|
3787
|
+
...errorsEqual(prevSnap.userErrors, newSnap.userErrors) ? {} : { userErrors: { before: prevSnap.userErrors, after: newSnap.userErrors } }
|
|
3788
|
+
};
|
|
3789
|
+
appendDelta(delta, newSnap);
|
|
3226
3790
|
});
|
|
3227
|
-
function
|
|
3791
|
+
function restoreCurrent(snap) {
|
|
3228
3792
|
suppressNext = true;
|
|
3229
3793
|
state.blankPaths.clear();
|
|
3230
3794
|
for (const key of snap.blankPaths) state.blankPaths.add(key);
|
|
@@ -3237,38 +3801,46 @@ function createHistoryModule(state, config) {
|
|
|
3237
3801
|
state.setAllUserErrors(userFlat);
|
|
3238
3802
|
}
|
|
3239
3803
|
function undo() {
|
|
3240
|
-
if (
|
|
3241
|
-
const
|
|
3242
|
-
const
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3804
|
+
if (undoDeltas.value.length === 0) return false;
|
|
3805
|
+
const d = undoDeltas.value[undoDeltas.value.length - 1];
|
|
3806
|
+
const restored = applyDeltaInverse(currentSnapshot.value, d);
|
|
3807
|
+
redoDeltas.value = [...redoDeltas.value, d];
|
|
3808
|
+
undoDeltas.value = undoDeltas.value.slice(0, -1);
|
|
3809
|
+
currentSnapshot.value = restored;
|
|
3810
|
+
restoreCurrent(restored);
|
|
3247
3811
|
return true;
|
|
3248
3812
|
}
|
|
3249
3813
|
function redo() {
|
|
3250
|
-
if (
|
|
3251
|
-
const
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3814
|
+
if (redoDeltas.value.length === 0) return false;
|
|
3815
|
+
const d = redoDeltas.value[redoDeltas.value.length - 1];
|
|
3816
|
+
const next = applyDeltaForward(currentSnapshot.value, d);
|
|
3817
|
+
undoDeltas.value = [...undoDeltas.value, d];
|
|
3818
|
+
redoDeltas.value = redoDeltas.value.slice(0, -1);
|
|
3819
|
+
currentSnapshot.value = next;
|
|
3820
|
+
restoreCurrent(next);
|
|
3256
3821
|
return true;
|
|
3257
3822
|
}
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3823
|
+
function clear() {
|
|
3824
|
+
const fresh = captureSnapshot();
|
|
3825
|
+
base.value = fresh;
|
|
3826
|
+
currentSnapshot.value = fresh;
|
|
3827
|
+
undoDeltas.value = [];
|
|
3828
|
+
redoDeltas.value = [];
|
|
3829
|
+
}
|
|
3830
|
+
const canUndo = computed(() => undoDeltas.value.length > 0);
|
|
3831
|
+
const canRedo = computed(() => redoDeltas.value.length > 0);
|
|
3832
|
+
const historySize = computed(() => 1 + undoDeltas.value.length + redoDeltas.value.length);
|
|
3261
3833
|
return {
|
|
3262
3834
|
undo,
|
|
3263
3835
|
redo,
|
|
3836
|
+
clear,
|
|
3264
3837
|
canUndo,
|
|
3265
3838
|
canRedo,
|
|
3266
3839
|
historySize,
|
|
3267
3840
|
dispose() {
|
|
3268
3841
|
unsubscribeChange();
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
redoStack.value = [];
|
|
3842
|
+
undoDeltas.value = [];
|
|
3843
|
+
redoDeltas.value = [];
|
|
3272
3844
|
}
|
|
3273
3845
|
};
|
|
3274
3846
|
}
|
|
@@ -3286,12 +3858,368 @@ function hashStableString(input, seed = 0) {
|
|
|
3286
3858
|
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36).padStart(11, "0");
|
|
3287
3859
|
}
|
|
3288
3860
|
|
|
3861
|
+
const PROTOCOL_VERSION = 1;
|
|
3862
|
+
const JOIN_COLLECTION_WINDOW_MS = 50;
|
|
3863
|
+
const SNAPSHOT_TIMEOUT_MS = 200;
|
|
3864
|
+
const MAX_LEADER_ATTEMPTS = 3;
|
|
3865
|
+
function isDangerousSegment(s) {
|
|
3866
|
+
return s === "__proto__" || s === "constructor" || s === "prototype";
|
|
3867
|
+
}
|
|
3868
|
+
function pathContainsDangerousSegment(path) {
|
|
3869
|
+
for (let i = 0; i < path.length; i++) {
|
|
3870
|
+
if (isDangerousSegment(path[i])) return true;
|
|
3871
|
+
}
|
|
3872
|
+
return false;
|
|
3873
|
+
}
|
|
3874
|
+
function diffBlankPaths(prev, curr) {
|
|
3875
|
+
const added = [];
|
|
3876
|
+
const removed = [];
|
|
3877
|
+
const prevSet = new Set(prev);
|
|
3878
|
+
for (const k of curr) if (!prevSet.has(k)) added.push(k);
|
|
3879
|
+
for (const k of prev) if (!curr.has(k)) removed.push(k);
|
|
3880
|
+
return { added, removed };
|
|
3881
|
+
}
|
|
3882
|
+
function snapshotForm(form) {
|
|
3883
|
+
return structuralSnapshot(form);
|
|
3884
|
+
}
|
|
3885
|
+
function stripSensitivePathsDeep(value, pathSoFar, isSensitivePath) {
|
|
3886
|
+
if (value === null || typeof value !== "object") return value;
|
|
3887
|
+
if (Array.isArray(value)) {
|
|
3888
|
+
return value.map((item, i) => stripSensitivePathsDeep(item, [...pathSoFar, i], isSensitivePath));
|
|
3889
|
+
}
|
|
3890
|
+
const proto = Object.getPrototypeOf(value);
|
|
3891
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
3892
|
+
const out = {};
|
|
3893
|
+
const src = value;
|
|
3894
|
+
for (const key of Object.keys(src)) {
|
|
3895
|
+
const childPath = [...pathSoFar, key];
|
|
3896
|
+
if (isSensitivePath(childPath)) {
|
|
3897
|
+
out[key] = void 0;
|
|
3898
|
+
continue;
|
|
3899
|
+
}
|
|
3900
|
+
out[key] = stripSensitivePathsDeep(src[key], childPath, isSensitivePath);
|
|
3901
|
+
}
|
|
3902
|
+
return out;
|
|
3903
|
+
}
|
|
3904
|
+
function isValidSyncMessage(data) {
|
|
3905
|
+
if (data === null || typeof data !== "object") return false;
|
|
3906
|
+
const m = data;
|
|
3907
|
+
if (m["v"] !== PROTOCOL_VERSION) return false;
|
|
3908
|
+
if (typeof m["senderId"] !== "string") return false;
|
|
3909
|
+
if (typeof m["kind"] !== "string") return false;
|
|
3910
|
+
switch (m["kind"]) {
|
|
3911
|
+
case "hello":
|
|
3912
|
+
case "announce":
|
|
3913
|
+
return true;
|
|
3914
|
+
case "requestSnapshot":
|
|
3915
|
+
return typeof m["targetId"] === "string";
|
|
3916
|
+
case "snapshot":
|
|
3917
|
+
return Array.isArray(m["blankPaths"]) && "form" in m;
|
|
3918
|
+
case "patches":
|
|
3919
|
+
return Array.isArray(m["formPatches"]) && Array.isArray(m["blankPathsAdded"]) && Array.isArray(m["blankPathsRemoved"]);
|
|
3920
|
+
default:
|
|
3921
|
+
return false;
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
function generateSenderId() {
|
|
3925
|
+
try {
|
|
3926
|
+
return globalThis.crypto.randomUUID();
|
|
3927
|
+
} catch {
|
|
3928
|
+
return `atta-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
function createMultiTabSyncModule(state, channelName, options) {
|
|
3932
|
+
if (typeof BroadcastChannel === "undefined") {
|
|
3933
|
+
return {
|
|
3934
|
+
dispose: () => void 0,
|
|
3935
|
+
lifecycle: () => "established",
|
|
3936
|
+
senderId: "",
|
|
3937
|
+
channelName
|
|
3938
|
+
};
|
|
3939
|
+
}
|
|
3940
|
+
let channel;
|
|
3941
|
+
try {
|
|
3942
|
+
channel = new BroadcastChannel(channelName);
|
|
3943
|
+
} catch {
|
|
3944
|
+
return {
|
|
3945
|
+
dispose: () => void 0,
|
|
3946
|
+
lifecycle: () => "established",
|
|
3947
|
+
senderId: "",
|
|
3948
|
+
channelName
|
|
3949
|
+
};
|
|
3950
|
+
}
|
|
3951
|
+
const senderId = generateSenderId();
|
|
3952
|
+
let lifecycle = "joining";
|
|
3953
|
+
let disposed = false;
|
|
3954
|
+
const peerIds = /* @__PURE__ */ new Set();
|
|
3955
|
+
let joinCollectionTimer = null;
|
|
3956
|
+
let snapshotTimeoutTimer = null;
|
|
3957
|
+
let leaderAttempts = 0;
|
|
3958
|
+
let prior = {
|
|
3959
|
+
form: snapshotForm(state.form.value),
|
|
3960
|
+
blankPathsSnapshot: [...state.blankPaths]
|
|
3961
|
+
};
|
|
3962
|
+
function safePost(msg) {
|
|
3963
|
+
if (disposed) return;
|
|
3964
|
+
try {
|
|
3965
|
+
channel.postMessage(msg);
|
|
3966
|
+
} catch {
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
function refreshPrior() {
|
|
3970
|
+
prior = {
|
|
3971
|
+
form: snapshotForm(state.form.value),
|
|
3972
|
+
blankPathsSnapshot: [...state.blankPaths]
|
|
3973
|
+
};
|
|
3974
|
+
}
|
|
3975
|
+
function isPathLocallySuppressed(path) {
|
|
3976
|
+
if (pathContainsDangerousSegment(path)) return true;
|
|
3977
|
+
if (options.isSensitivePath(path)) return true;
|
|
3978
|
+
const { key } = canonicalizePath([...path]);
|
|
3979
|
+
if (options.noSyncPaths.has(key)) return true;
|
|
3980
|
+
return false;
|
|
3981
|
+
}
|
|
3982
|
+
function postPatches() {
|
|
3983
|
+
if (lifecycle !== "established") return;
|
|
3984
|
+
const next = snapshotForm(state.form.value);
|
|
3985
|
+
const rawPatches = [];
|
|
3986
|
+
diffAndApply(prior.form, next, [], (p) => rawPatches.push(p));
|
|
3987
|
+
const safePatches = [];
|
|
3988
|
+
for (const p of rawPatches) {
|
|
3989
|
+
if (isPathLocallySuppressed(p.path)) continue;
|
|
3990
|
+
safePatches.push(p);
|
|
3991
|
+
}
|
|
3992
|
+
const { added, removed } = diffBlankPaths(prior.blankPathsSnapshot, state.blankPaths);
|
|
3993
|
+
if (safePatches.length === 0 && added.length === 0 && removed.length === 0) {
|
|
3994
|
+
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
3995
|
+
return;
|
|
3996
|
+
}
|
|
3997
|
+
safePost({
|
|
3998
|
+
v: PROTOCOL_VERSION,
|
|
3999
|
+
kind: "patches",
|
|
4000
|
+
senderId,
|
|
4001
|
+
formPatches: safePatches,
|
|
4002
|
+
blankPathsAdded: added,
|
|
4003
|
+
blankPathsRemoved: removed
|
|
4004
|
+
});
|
|
4005
|
+
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
4006
|
+
}
|
|
4007
|
+
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
4008
|
+
if (disposed) return;
|
|
4009
|
+
if (lifecycle !== "established") return;
|
|
4010
|
+
if (meta?.crossTab === true) return;
|
|
4011
|
+
if (meta?.hydration === true) {
|
|
4012
|
+
refreshPrior();
|
|
4013
|
+
return;
|
|
4014
|
+
}
|
|
4015
|
+
postPatches();
|
|
4016
|
+
});
|
|
4017
|
+
function applyIncomingForm(form, blankPaths) {
|
|
4018
|
+
state.blankPaths.clear();
|
|
4019
|
+
for (const k of blankPaths) state.blankPaths.add(k);
|
|
4020
|
+
state.applyFormReplacement(form, { crossTab: true, persist: false });
|
|
4021
|
+
refreshPrior();
|
|
4022
|
+
}
|
|
4023
|
+
function handlePatches(msg) {
|
|
4024
|
+
if (lifecycle !== "established") return;
|
|
4025
|
+
const safePatches = [];
|
|
4026
|
+
for (const p of msg.formPatches) {
|
|
4027
|
+
if (!Array.isArray(p.path)) continue;
|
|
4028
|
+
if (isPathLocallySuppressed(p.path)) continue;
|
|
4029
|
+
safePatches.push(p);
|
|
4030
|
+
}
|
|
4031
|
+
const safeBlankAdded = [];
|
|
4032
|
+
for (const k of msg.blankPathsAdded) {
|
|
4033
|
+
const segs = canonicalizePath(k).segments;
|
|
4034
|
+
if (isPathLocallySuppressed(segs)) continue;
|
|
4035
|
+
safeBlankAdded.push(k);
|
|
4036
|
+
}
|
|
4037
|
+
const safeBlankRemoved = [];
|
|
4038
|
+
for (const k of msg.blankPathsRemoved) {
|
|
4039
|
+
const segs = canonicalizePath(k).segments;
|
|
4040
|
+
if (isPathLocallySuppressed(segs)) continue;
|
|
4041
|
+
safeBlankRemoved.push(k);
|
|
4042
|
+
}
|
|
4043
|
+
if (safePatches.length === 0 && safeBlankAdded.length === 0 && safeBlankRemoved.length === 0) {
|
|
4044
|
+
return;
|
|
4045
|
+
}
|
|
4046
|
+
const candidate = applyPatchesForward(state.form.value, safePatches);
|
|
4047
|
+
try {
|
|
4048
|
+
options.validateForm(state.form.value);
|
|
4049
|
+
try {
|
|
4050
|
+
options.validateForm(candidate);
|
|
4051
|
+
} catch {
|
|
4052
|
+
return;
|
|
4053
|
+
}
|
|
4054
|
+
} catch {
|
|
4055
|
+
}
|
|
4056
|
+
const nextBlankPaths = new Set(state.blankPaths);
|
|
4057
|
+
for (const k of safeBlankRemoved) nextBlankPaths.delete(k);
|
|
4058
|
+
for (const k of safeBlankAdded) nextBlankPaths.add(k);
|
|
4059
|
+
applyIncomingForm(candidate, [...nextBlankPaths]);
|
|
4060
|
+
}
|
|
4061
|
+
function handleSnapshot(msg) {
|
|
4062
|
+
if (lifecycle !== "joining") return;
|
|
4063
|
+
try {
|
|
4064
|
+
options.validateForm(msg.form);
|
|
4065
|
+
} catch {
|
|
4066
|
+
return;
|
|
4067
|
+
}
|
|
4068
|
+
if (snapshotTimeoutTimer !== null) {
|
|
4069
|
+
clearTimeout(snapshotTimeoutTimer);
|
|
4070
|
+
snapshotTimeoutTimer = null;
|
|
4071
|
+
}
|
|
4072
|
+
if (joinCollectionTimer !== null) {
|
|
4073
|
+
clearTimeout(joinCollectionTimer);
|
|
4074
|
+
joinCollectionTimer = null;
|
|
4075
|
+
}
|
|
4076
|
+
applyIncomingForm(msg.form, msg.blankPaths);
|
|
4077
|
+
lifecycle = "established";
|
|
4078
|
+
peerIds.clear();
|
|
4079
|
+
}
|
|
4080
|
+
function respondToHello() {
|
|
4081
|
+
safePost({ v: PROTOCOL_VERSION, kind: "announce", senderId });
|
|
4082
|
+
}
|
|
4083
|
+
function respondToSnapshotRequest() {
|
|
4084
|
+
const scrubbedForm = stripSensitivePathsDeep(state.form.value, [], options.isSensitivePath);
|
|
4085
|
+
safePost({
|
|
4086
|
+
v: PROTOCOL_VERSION,
|
|
4087
|
+
kind: "snapshot",
|
|
4088
|
+
senderId,
|
|
4089
|
+
form: scrubbedForm,
|
|
4090
|
+
blankPaths: [...state.blankPaths]
|
|
4091
|
+
});
|
|
4092
|
+
}
|
|
4093
|
+
channel.onmessage = (event) => {
|
|
4094
|
+
if (disposed) return;
|
|
4095
|
+
const data = event.data;
|
|
4096
|
+
if (!isValidSyncMessage(data)) return;
|
|
4097
|
+
const msg = data;
|
|
4098
|
+
if (msg.senderId === senderId) return;
|
|
4099
|
+
switch (msg.kind) {
|
|
4100
|
+
case "hello":
|
|
4101
|
+
if (lifecycle !== "established") return;
|
|
4102
|
+
respondToHello();
|
|
4103
|
+
break;
|
|
4104
|
+
case "announce":
|
|
4105
|
+
if (lifecycle === "joining") peerIds.add(msg.senderId);
|
|
4106
|
+
break;
|
|
4107
|
+
case "requestSnapshot":
|
|
4108
|
+
if (lifecycle !== "established") return;
|
|
4109
|
+
if (msg.targetId !== senderId) return;
|
|
4110
|
+
respondToSnapshotRequest();
|
|
4111
|
+
break;
|
|
4112
|
+
case "snapshot":
|
|
4113
|
+
handleSnapshot(msg);
|
|
4114
|
+
break;
|
|
4115
|
+
case "patches":
|
|
4116
|
+
handlePatches(msg);
|
|
4117
|
+
break;
|
|
4118
|
+
}
|
|
4119
|
+
};
|
|
4120
|
+
function electLeaderAndRequest() {
|
|
4121
|
+
if (disposed) return;
|
|
4122
|
+
if (peerIds.size === 0) {
|
|
4123
|
+
lifecycle = "established";
|
|
4124
|
+
refreshPrior();
|
|
4125
|
+
return;
|
|
4126
|
+
}
|
|
4127
|
+
const sorted = [...peerIds].sort();
|
|
4128
|
+
const leaderId = sorted[0];
|
|
4129
|
+
peerIds.delete(leaderId);
|
|
4130
|
+
leaderAttempts++;
|
|
4131
|
+
safePost({
|
|
4132
|
+
v: PROTOCOL_VERSION,
|
|
4133
|
+
kind: "requestSnapshot",
|
|
4134
|
+
senderId,
|
|
4135
|
+
targetId: leaderId
|
|
4136
|
+
});
|
|
4137
|
+
snapshotTimeoutTimer = setTimeout(() => {
|
|
4138
|
+
snapshotTimeoutTimer = null;
|
|
4139
|
+
if (disposed) return;
|
|
4140
|
+
if (lifecycle === "established") return;
|
|
4141
|
+
if (leaderAttempts >= MAX_LEADER_ATTEMPTS || peerIds.size === 0) {
|
|
4142
|
+
lifecycle = "established";
|
|
4143
|
+
refreshPrior();
|
|
4144
|
+
return;
|
|
4145
|
+
}
|
|
4146
|
+
electLeaderAndRequest();
|
|
4147
|
+
}, SNAPSHOT_TIMEOUT_MS);
|
|
4148
|
+
}
|
|
4149
|
+
function joinFlow() {
|
|
4150
|
+
safePost({ v: PROTOCOL_VERSION, kind: "hello", senderId });
|
|
4151
|
+
joinCollectionTimer = setTimeout(() => {
|
|
4152
|
+
joinCollectionTimer = null;
|
|
4153
|
+
if (disposed) return;
|
|
4154
|
+
if (lifecycle === "established") return;
|
|
4155
|
+
electLeaderAndRequest();
|
|
4156
|
+
}, JOIN_COLLECTION_WINDOW_MS);
|
|
4157
|
+
}
|
|
4158
|
+
joinFlow();
|
|
4159
|
+
return {
|
|
4160
|
+
dispose: () => {
|
|
4161
|
+
if (disposed) return;
|
|
4162
|
+
disposed = true;
|
|
4163
|
+
if (joinCollectionTimer !== null) {
|
|
4164
|
+
clearTimeout(joinCollectionTimer);
|
|
4165
|
+
joinCollectionTimer = null;
|
|
4166
|
+
}
|
|
4167
|
+
if (snapshotTimeoutTimer !== null) {
|
|
4168
|
+
clearTimeout(snapshotTimeoutTimer);
|
|
4169
|
+
snapshotTimeoutTimer = null;
|
|
4170
|
+
}
|
|
4171
|
+
unsubscribeChange();
|
|
4172
|
+
try {
|
|
4173
|
+
channel.close();
|
|
4174
|
+
} catch {
|
|
4175
|
+
}
|
|
4176
|
+
},
|
|
4177
|
+
lifecycle: () => lifecycle,
|
|
4178
|
+
senderId,
|
|
4179
|
+
channelName
|
|
4180
|
+
};
|
|
4181
|
+
}
|
|
4182
|
+
const MULTI_TAB_SYNC_MODULE_KEY = "multiTabSync";
|
|
4183
|
+
|
|
4184
|
+
const warned = /* @__PURE__ */ new Set();
|
|
4185
|
+
function warnOnceInsecureContext(feature) {
|
|
4186
|
+
if (!__DEV__) return;
|
|
4187
|
+
if (warned.has(feature)) return;
|
|
4188
|
+
warned.add(feature);
|
|
4189
|
+
const message = featureMessage(feature);
|
|
4190
|
+
console.warn(`[attaform] ${message}`);
|
|
4191
|
+
}
|
|
4192
|
+
function featureMessage(feature) {
|
|
4193
|
+
switch (feature) {
|
|
4194
|
+
case "multiTab":
|
|
4195
|
+
return "Multi-tab sync requires a secure context (HTTPS or localhost). Plain HTTP on a real hostname is interceptable by network observers, so the sync module is disabled. Serve over HTTPS in production (or develop on `localhost`) to enable cross-tab synchronisation. Use `multiTab: false` on `useForm` to silence this warning.";
|
|
4196
|
+
case "persist:local":
|
|
4197
|
+
return "Built-in `persist: 'local'` storage requires a secure context (HTTPS or localhost). Plain HTTP on a real hostname is MITM-interceptable, so the persistence layer is disabled. Serve over HTTPS to enable localStorage persistence, or pass a custom storage adapter to opt out of the secure-context gate.";
|
|
4198
|
+
case "persist:session":
|
|
4199
|
+
return "Built-in `persist: 'session'` storage requires a secure context (HTTPS or localhost). Plain HTTP on a real hostname is MITM-interceptable, so the persistence layer is disabled. Serve over HTTPS to enable sessionStorage persistence, or pass a custom storage adapter to opt out of the secure-context gate.";
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
function isSecureContext() {
|
|
4203
|
+
return typeof window !== "undefined" && window.isSecureContext === true;
|
|
4204
|
+
}
|
|
4205
|
+
|
|
3289
4206
|
function useAbstractForm(configuration) {
|
|
3290
4207
|
if (configuration === void 0 || configuration === null || configuration.schema === void 0) {
|
|
3291
4208
|
throw new InvalidUseFormConfigError();
|
|
3292
4209
|
}
|
|
3293
4210
|
const key = resolveFormKey(configuration.key);
|
|
3294
|
-
const
|
|
4211
|
+
const instance = getCurrentInstance();
|
|
4212
|
+
if (instance !== null) ensureAttaformInstalled(instance.appContext.app);
|
|
4213
|
+
const registry = useRegistry();
|
|
4214
|
+
const merged = mergeWithDefaults(registry.defaults, configuration);
|
|
4215
|
+
const maxRecursionDepth = normalizeNumericOption({
|
|
4216
|
+
value: merged.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH,
|
|
4217
|
+
source: "useForm.maxRecursionDepth",
|
|
4218
|
+
allowInfinity: true,
|
|
4219
|
+
min: 0,
|
|
4220
|
+
defaultValue: DEFAULT_MAX_RECURSION_DEPTH
|
|
4221
|
+
});
|
|
4222
|
+
const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
|
|
3295
4223
|
if (configuration.persist !== void 0 && configuration.key === void 0) {
|
|
3296
4224
|
throw new AnonPersistError({
|
|
3297
4225
|
cause: "no-key",
|
|
@@ -3299,13 +4227,10 @@ function useAbstractForm(configuration) {
|
|
|
3299
4227
|
callSite: captureUserCallSite()
|
|
3300
4228
|
});
|
|
3301
4229
|
}
|
|
3302
|
-
const instance = getCurrentInstance();
|
|
3303
|
-
if (instance !== null) ensureAttaformInstalled(instance.appContext.app);
|
|
3304
|
-
const registry = useRegistry();
|
|
3305
|
-
const merged = mergeWithDefaults(registry.defaults, configuration);
|
|
3306
4230
|
const existing = registry.forms.get(key);
|
|
3307
4231
|
if (__DEV__ && existing !== void 0) {
|
|
3308
4232
|
warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
|
|
4233
|
+
warnOnPersistDivergence(key, existing, configuration.persist);
|
|
3309
4234
|
}
|
|
3310
4235
|
const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
|
|
3311
4236
|
if (getCurrentScope() !== void 0) {
|
|
@@ -3316,16 +4241,54 @@ function useAbstractForm(configuration) {
|
|
|
3316
4241
|
if (existing === void 0 && !registry.ssr) {
|
|
3317
4242
|
if (merged.persist !== void 0 && !persistDisabledByAnonRule) {
|
|
3318
4243
|
const resolvedPersist = normalizePersistConfig(merged.persist);
|
|
3319
|
-
const
|
|
3320
|
-
|
|
3321
|
-
const
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
4244
|
+
const storageKind = resolvedPersist.storage;
|
|
4245
|
+
const isBuiltinStorage = typeof storageKind === "string";
|
|
4246
|
+
const secureContextOk = !isBuiltinStorage || isSecureContext();
|
|
4247
|
+
if (!secureContextOk) {
|
|
4248
|
+
const feature = storageKind === "session" ? "persist:session" : "persist:local";
|
|
4249
|
+
warnOnceInsecureContext(feature);
|
|
4250
|
+
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
4251
|
+
} else {
|
|
4252
|
+
const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
|
|
4253
|
+
void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
|
|
4254
|
+
const persistenceModule = wirePersistence(state, resolvedPersist);
|
|
4255
|
+
state.modules.set(PERSISTENCE_MODULE_KEY, persistenceModule);
|
|
4256
|
+
state.registerDrain(() => persistenceModule.awaitPendingWrites());
|
|
4257
|
+
state.registerCleanup(() => persistenceModule.dispose());
|
|
4258
|
+
}
|
|
3325
4259
|
} else {
|
|
3326
4260
|
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
3327
4261
|
}
|
|
3328
4262
|
}
|
|
4263
|
+
if (existing === void 0 && merged.multiTab !== false && configuration.key !== void 0 && !registry.ssr) {
|
|
4264
|
+
const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
|
|
4265
|
+
const secureContext = isSecureContext();
|
|
4266
|
+
if (hasBroadcastChannel && secureContext) {
|
|
4267
|
+
let channelName;
|
|
4268
|
+
try {
|
|
4269
|
+
channelName = `attaform:sync:${state.formKey}:${hashStableString(state.schema.fingerprint())}`;
|
|
4270
|
+
} catch {
|
|
4271
|
+
channelName = null;
|
|
4272
|
+
}
|
|
4273
|
+
if (channelName !== null) {
|
|
4274
|
+
const syncModule = createMultiTabSyncModule(state, channelName, {
|
|
4275
|
+
isSensitivePath: state.isSensitivePath,
|
|
4276
|
+
noSyncPaths: state.noSyncPaths,
|
|
4277
|
+
validateForm: (form) => {
|
|
4278
|
+
const result = state.schema.validateAtPath(form, void 0, { sync: true });
|
|
4279
|
+
if (result instanceof Promise) return;
|
|
4280
|
+
if (!result.success) {
|
|
4281
|
+
throw new Error("attaform multi-tab sync: post-apply schema validation failed");
|
|
4282
|
+
}
|
|
4283
|
+
}
|
|
4284
|
+
});
|
|
4285
|
+
state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
|
|
4286
|
+
state.registerCleanup(() => syncModule.dispose());
|
|
4287
|
+
}
|
|
4288
|
+
} else if (hasBroadcastChannel && !secureContext) {
|
|
4289
|
+
warnOnceInsecureContext("multiTab");
|
|
4290
|
+
}
|
|
4291
|
+
}
|
|
3329
4292
|
if (existing === void 0 && merged.history !== void 0) {
|
|
3330
4293
|
const historyModule = createHistoryModule(state, merged.history);
|
|
3331
4294
|
state.modules.set(HISTORY_MODULE_KEY, historyModule);
|
|
@@ -3347,6 +4310,22 @@ function useAbstractForm(configuration) {
|
|
|
3347
4310
|
if (history !== void 0) {
|
|
3348
4311
|
apiOptions.history = history;
|
|
3349
4312
|
}
|
|
4313
|
+
if (merged.validateOn !== void 0) {
|
|
4314
|
+
apiOptions.validateOn = merged.validateOn;
|
|
4315
|
+
}
|
|
4316
|
+
const mergedDebounceMs = merged.debounceMs;
|
|
4317
|
+
if (mergedDebounceMs !== void 0) {
|
|
4318
|
+
apiOptions.debounceMs = mergedDebounceMs;
|
|
4319
|
+
}
|
|
4320
|
+
if (merged.shouldShowErrors !== void 0) {
|
|
4321
|
+
apiOptions.shouldShowErrors = resolveShouldShowErrors(merged.shouldShowErrors);
|
|
4322
|
+
}
|
|
4323
|
+
if (merged.coerce !== void 0) {
|
|
4324
|
+
apiOptions.coerce = merged.coerce;
|
|
4325
|
+
}
|
|
4326
|
+
if (merged.rememberVariants !== void 0) {
|
|
4327
|
+
apiOptions.rememberVariants = merged.rememberVariants;
|
|
4328
|
+
}
|
|
3350
4329
|
return buildFormApi(state, formInstanceId, apiOptions);
|
|
3351
4330
|
}
|
|
3352
4331
|
function mergeWithDefaults(defaults, configuration) {
|
|
@@ -3358,6 +4337,9 @@ function mergeWithDefaults(defaults, configuration) {
|
|
|
3358
4337
|
const validateOn = configuration.validateOn ?? defaults.validateOn;
|
|
3359
4338
|
const debounceMs = configuration.debounceMs ?? defaults.debounceMs;
|
|
3360
4339
|
const shouldShowErrors = configuration.shouldShowErrors ?? defaults.shouldShowErrors;
|
|
4340
|
+
const maxRecursionDepth = configuration.maxRecursionDepth ?? defaults.maxRecursionDepth;
|
|
4341
|
+
const sensitiveNames = configuration.sensitiveNames ?? defaults.sensitiveNames;
|
|
4342
|
+
const multiTab = configuration.multiTab ?? defaults.multiTab;
|
|
3361
4343
|
return {
|
|
3362
4344
|
...configuration,
|
|
3363
4345
|
...strict === void 0 ? {} : { strict },
|
|
@@ -3367,7 +4349,10 @@ function mergeWithDefaults(defaults, configuration) {
|
|
|
3367
4349
|
...coerce === void 0 ? {} : { coerce },
|
|
3368
4350
|
...validateOn === void 0 ? {} : { validateOn },
|
|
3369
4351
|
...debounceMs === void 0 ? {} : { debounceMs },
|
|
3370
|
-
...shouldShowErrors === void 0 ? {} : { shouldShowErrors }
|
|
4352
|
+
...shouldShowErrors === void 0 ? {} : { shouldShowErrors },
|
|
4353
|
+
...maxRecursionDepth === void 0 ? {} : { maxRecursionDepth },
|
|
4354
|
+
...sensitiveNames === void 0 ? {} : { sensitiveNames },
|
|
4355
|
+
...multiTab === void 0 ? {} : { multiTab }
|
|
3371
4356
|
};
|
|
3372
4357
|
}
|
|
3373
4358
|
const HISTORY_MODULE_KEY = "history";
|
|
@@ -3379,6 +4364,9 @@ function buildFreshState(key, schema, configuration, registry) {
|
|
|
3379
4364
|
schema
|
|
3380
4365
|
);
|
|
3381
4366
|
const initialBlankPaths = pending === void 0 ? walked.paths : void 0;
|
|
4367
|
+
const resolvedSensitiveNames = configuration.sensitiveNames;
|
|
4368
|
+
const resolvedIsSensitivePath = resolvedSensitiveNames === void 0 ? void 0 : createIsSensitivePath(resolvedSensitiveNames);
|
|
4369
|
+
const resolvedSegmentMatchesSensitive = resolvedSensitiveNames === void 0 ? void 0 : createSegmentMatchesSensitive(resolvedSensitiveNames);
|
|
3382
4370
|
const createOptions = {
|
|
3383
4371
|
formKey: key,
|
|
3384
4372
|
schema,
|
|
@@ -3391,7 +4379,9 @@ function buildFreshState(key, schema, configuration, registry) {
|
|
|
3391
4379
|
...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
|
|
3392
4380
|
...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
|
|
3393
4381
|
...configuration.shouldShowErrors !== void 0 ? { shouldShowErrors: configuration.shouldShowErrors } : {},
|
|
3394
|
-
...initialBlankPaths !== void 0 ? { initialBlankPaths } : {}
|
|
4382
|
+
...initialBlankPaths !== void 0 ? { initialBlankPaths } : {},
|
|
4383
|
+
...resolvedIsSensitivePath !== void 0 ? { isSensitivePath: resolvedIsSensitivePath } : {},
|
|
4384
|
+
...resolvedSegmentMatchesSensitive !== void 0 ? { segmentMatchesSensitive: resolvedSegmentMatchesSensitive } : {}
|
|
3395
4385
|
};
|
|
3396
4386
|
const state = createFormStore(createOptions);
|
|
3397
4387
|
registry.forms.set(
|
|
@@ -3450,11 +4440,47 @@ function warnOnSchemaFingerprintMismatch(key, existing, incoming) {
|
|
|
3450
4440
|
incoming: ${incomingFp}`
|
|
3451
4441
|
);
|
|
3452
4442
|
}
|
|
4443
|
+
function warnOnPersistDivergence(key, existing, incomingPersist) {
|
|
4444
|
+
if (incomingPersist === void 0) return;
|
|
4445
|
+
const wired = existing.modules.get(PERSISTENCE_MODULE_KEY);
|
|
4446
|
+
const incomingNormalized = normalizePersistConfig(incomingPersist);
|
|
4447
|
+
if (wired === void 0) {
|
|
4448
|
+
console.warn(
|
|
4449
|
+
`[attaform] useForm({ key: "${key}" }) passed a persist config but the first useForm({ key }) call didn't wire persistence; the new config is silently dropped. Pass persist on the first call, or remove persist here to make the inheritance explicit.`
|
|
4450
|
+
);
|
|
4451
|
+
return;
|
|
4452
|
+
}
|
|
4453
|
+
if (persistConfigsEquivalent(wired.wiredConfig, incomingNormalized)) return;
|
|
4454
|
+
console.warn(
|
|
4455
|
+
`[attaform] useForm({ key: "${key}" }) passed a persist config that differs from the first useForm({ key }) call's; first wins, this one is ignored.
|
|
4456
|
+
wired: ${describePersist(wired.wiredConfig)}
|
|
4457
|
+
incoming: ${describePersist(incomingNormalized)}`
|
|
4458
|
+
);
|
|
4459
|
+
}
|
|
4460
|
+
function persistConfigsEquivalent(a, b) {
|
|
4461
|
+
if (a.storage !== b.storage) return false;
|
|
4462
|
+
if ((a.key ?? void 0) !== (b.key ?? void 0)) return false;
|
|
4463
|
+
if ((a.debounceMs ?? void 0) !== (b.debounceMs ?? void 0)) return false;
|
|
4464
|
+
return true;
|
|
4465
|
+
}
|
|
4466
|
+
function describePersist(config) {
|
|
4467
|
+
const storage = typeof config.storage === "string" ? config.storage : "custom-adapter";
|
|
4468
|
+
const parts = [`storage=${storage}`];
|
|
4469
|
+
if (config.key !== void 0) parts.push(`key=${config.key}`);
|
|
4470
|
+
if (config.debounceMs !== void 0) parts.push(`debounceMs=${config.debounceMs}`);
|
|
4471
|
+
return `{ ${parts.join(", ")} }`;
|
|
4472
|
+
}
|
|
3453
4473
|
function wirePersistence(state, config) {
|
|
3454
4474
|
const fingerprint = hashStableString(state.schema.fingerprint());
|
|
3455
4475
|
const base = resolveStorageKeyBase(config, state.formKey);
|
|
3456
4476
|
const key = `${base}:${fingerprint}`;
|
|
3457
|
-
const debounceMs =
|
|
4477
|
+
const debounceMs = normalizeNumericOption({
|
|
4478
|
+
value: config.debounceMs ?? DEFAULT_PERSISTENCE_DEBOUNCE_MS,
|
|
4479
|
+
source: "useForm.persist.debounceMs",
|
|
4480
|
+
allowInfinity: false,
|
|
4481
|
+
min: 0,
|
|
4482
|
+
defaultValue: DEFAULT_PERSISTENCE_DEBOUNCE_MS
|
|
4483
|
+
});
|
|
3458
4484
|
const include = config.include ?? "form";
|
|
3459
4485
|
const clearOnSubmitSuccess = config.clearOnSubmitSuccess ?? true;
|
|
3460
4486
|
const adapterPromise = getStorageAdapter(config.storage);
|
|
@@ -3489,6 +4515,7 @@ function wirePersistence(state, config) {
|
|
|
3489
4515
|
}, debounceMs);
|
|
3490
4516
|
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
3491
4517
|
if (disposed || inFlightFinalFlush !== null) return;
|
|
4518
|
+
if (meta?.crossTab === true) return;
|
|
3492
4519
|
if (meta?.persist !== true) return;
|
|
3493
4520
|
pendingOptedInPaths = new Set(state.persistOptIns.optedInPaths());
|
|
3494
4521
|
writer.schedule();
|
|
@@ -3522,7 +4549,7 @@ function wirePersistence(state, config) {
|
|
|
3522
4549
|
payload.data.form,
|
|
3523
4550
|
state.schema
|
|
3524
4551
|
);
|
|
3525
|
-
state.applyFormReplacement(merged);
|
|
4552
|
+
state.applyFormReplacement(merged, { hydration: true });
|
|
3526
4553
|
const persistedLeafPaths = collectPersistedLeafPaths(payload.data.form);
|
|
3527
4554
|
for (const k of persistedLeafPaths) {
|
|
3528
4555
|
state.blankPaths.delete(k);
|
|
@@ -3653,6 +4680,7 @@ function wirePersistence(state, config) {
|
|
|
3653
4680
|
});
|
|
3654
4681
|
}
|
|
3655
4682
|
return {
|
|
4683
|
+
wiredConfig: config,
|
|
3656
4684
|
writePathImmediately,
|
|
3657
4685
|
clearPersistedDraft,
|
|
3658
4686
|
awaitPendingWrites,
|
|
@@ -3727,7 +4755,11 @@ function injectForm(key) {
|
|
|
3727
4755
|
}
|
|
3728
4756
|
const ambientInstanceId = getCurrentInstance() !== null ? inject(kFormInstanceId, null) : null;
|
|
3729
4757
|
const formInstanceId = ambientInstanceId ?? (getCurrentInstance() !== null ? useId() : `atta:form-instance-injected:${injectedInstanceCounter++}`);
|
|
3730
|
-
return buildFormApi(
|
|
4758
|
+
return buildFormApi(
|
|
4759
|
+
state,
|
|
4760
|
+
formInstanceId,
|
|
4761
|
+
apiOptions
|
|
4762
|
+
);
|
|
3731
4763
|
}
|
|
3732
4764
|
function resolveState(key, registry) {
|
|
3733
4765
|
if (key !== void 0) {
|
|
@@ -3771,5 +4803,5 @@ function warnIfAmbientProviderHadDuplicates() {
|
|
|
3771
4803
|
}
|
|
3772
4804
|
}
|
|
3773
4805
|
|
|
3774
|
-
export { AttaformErrorCode as A, isUnset as a, defaultShouldShowErrors as b, defineCoercion as c, defaultCoercionRules as d, useAbstractForm as e, setAtPath as f, getAtPath as g, humanize as h, injectForm as i, isPlainRecord as j, slimKindOf as s, unset as u };
|
|
3775
|
-
//# sourceMappingURL=attaform.
|
|
4806
|
+
export { AttaformErrorCode as A, isUnset as a, defaultShouldShowErrors as b, defineCoercion as c, defaultCoercionRules as d, useAbstractForm as e, setAtPath as f, getAtPath as g, humanize as h, injectForm as i, isPlainRecord as j, normalizeNumericOption as n, slimKindOf as s, unset as u };
|
|
4807
|
+
//# sourceMappingURL=attaform.BT55rDNN.mjs.map
|