attaform 0.16.3 → 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 +27 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -8
- package/dist/index.d.mts +80 -8
- package/dist/index.d.ts +80 -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.KrNw10aW.cjs → attaform.0Wg7UEeX.cjs} +60 -20
- package/dist/shared/attaform.0Wg7UEeX.cjs.map +1 -0
- package/dist/shared/attaform.AOgGyRoI.d.cts +65 -0
- package/dist/shared/{attaform.lFNwBcA3.d.ts → attaform.B0zue7zt.d.ts} +1 -1
- package/dist/shared/{attaform.c_NzdRyc.cjs → attaform.BBM2muQ9.cjs} +7 -3
- package/dist/shared/attaform.BBM2muQ9.cjs.map +1 -0
- package/dist/shared/{attaform.C9Ph2SMx.cjs → attaform.BFumZXY2.cjs} +1514 -391
- package/dist/shared/attaform.BFumZXY2.cjs.map +1 -0
- package/dist/shared/attaform.BQ-iGGWd.d.mts +65 -0
- package/dist/shared/{attaform.DILbdvfo.mjs → attaform.BT55rDNN.mjs} +1514 -393
- package/dist/shared/attaform.BT55rDNN.mjs.map +1 -0
- package/dist/shared/{attaform._EqYNPYF.d.mts → attaform.BYbsV2Wv.d.cts} +738 -138
- package/dist/shared/{attaform._EqYNPYF.d.ts → attaform.BYbsV2Wv.d.mts} +738 -138
- package/dist/shared/{attaform._EqYNPYF.d.cts → attaform.BYbsV2Wv.d.ts} +738 -138
- package/dist/shared/{attaform.DGuGGNg9.cjs → attaform.C6_zOf8x.cjs} +232 -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.XYOMTvuO.mjs → attaform.Cj0pCNVn.mjs} +232 -113
- package/dist/shared/attaform.Cj0pCNVn.mjs.map +1 -0
- package/dist/shared/{attaform.DLnKT7wk.d.cts → attaform.ClfCi1i2.d.mts} +1 -1
- package/dist/shared/{attaform.CFA6y0KF.mjs → attaform.D6Q5ZP8L.mjs} +60 -20
- package/dist/shared/attaform.D6Q5ZP8L.mjs.map +1 -0
- package/dist/shared/{attaform.Bls_kFR6.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.jrxE_xZw.mjs → attaform.h1sq3BFu.mjs} +6 -4
- package/dist/shared/attaform.h1sq3BFu.mjs.map +1 -0
- 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.C9Ph2SMx.cjs.map +0 -1
- package/dist/shared/attaform.CFA6y0KF.mjs.map +0 -1
- package/dist/shared/attaform.DGuGGNg9.cjs.map +0 -1
- package/dist/shared/attaform.DILbdvfo.mjs.map +0 -1
- package/dist/shared/attaform.KrNw10aW.cjs.map +0 -1
- package/dist/shared/attaform.XYOMTvuO.mjs.map +0 -1
- package/dist/shared/attaform.c_NzdRyc.cjs.map +0 -1
- package/dist/shared/attaform.jrxE_xZw.mjs.map +0 -1
- package/dist/shared/attaform.rIRYSUI1.cjs.map +0 -1
|
@@ -1,175 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const vue = require('vue');
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
function isDescendable(value) {
|
|
8
|
-
if (value === null || typeof value !== "object") return false;
|
|
9
|
-
if (Array.isArray(value)) return true;
|
|
10
|
-
const proto = Object.getPrototypeOf(value);
|
|
11
|
-
return proto === null || proto === Object.prototype;
|
|
12
|
-
}
|
|
13
|
-
function appendSegment(prefix, segment) {
|
|
14
|
-
const next = new Array(prefix.length + 1);
|
|
15
|
-
for (let i = 0; i < prefix.length; i++) {
|
|
16
|
-
const s = prefix[i];
|
|
17
|
-
next[i] = s;
|
|
18
|
-
}
|
|
19
|
-
next[prefix.length] = segment;
|
|
20
|
-
return next;
|
|
21
|
-
}
|
|
22
|
-
function diffAndApply(oldValue, newValue, prefix, visit) {
|
|
23
|
-
if (Object.is(oldValue, newValue)) return;
|
|
24
|
-
const oldIsDescendable = isDescendable(oldValue);
|
|
25
|
-
const newIsDescendable = isDescendable(newValue);
|
|
26
|
-
if (oldValue === void 0 && newIsDescendable) {
|
|
27
|
-
if (Array.isArray(newValue)) {
|
|
28
|
-
for (let i = 0; i < newValue.length; i++) {
|
|
29
|
-
diffAndApply(void 0, newValue[i], appendSegment(prefix, i), visit);
|
|
30
|
-
}
|
|
31
|
-
} else {
|
|
32
|
-
const rec = newValue;
|
|
33
|
-
for (const k of Object.keys(rec)) {
|
|
34
|
-
diffAndApply(void 0, rec[k], appendSegment(prefix, k), visit);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
if (oldIsDescendable && newValue === void 0) {
|
|
40
|
-
if (Array.isArray(oldValue)) {
|
|
41
|
-
for (let i = 0; i < oldValue.length; i++) {
|
|
42
|
-
diffAndApply(oldValue[i], void 0, appendSegment(prefix, i), visit);
|
|
43
|
-
}
|
|
44
|
-
} else {
|
|
45
|
-
const rec = oldValue;
|
|
46
|
-
for (const k of Object.keys(rec)) {
|
|
47
|
-
diffAndApply(rec[k], void 0, appendSegment(prefix, k), visit);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
if (oldIsDescendable && newIsDescendable) {
|
|
53
|
-
const oldIsArray = Array.isArray(oldValue);
|
|
54
|
-
const newIsArray = Array.isArray(newValue);
|
|
55
|
-
if (oldIsArray && newIsArray) {
|
|
56
|
-
const oldArr = oldValue;
|
|
57
|
-
const newArr = newValue;
|
|
58
|
-
const max = Math.max(oldArr.length, newArr.length);
|
|
59
|
-
for (let i = 0; i < max; i++) {
|
|
60
|
-
diffAndApply(oldArr[i], newArr[i], appendSegment(prefix, i), visit);
|
|
61
|
-
}
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
if (!oldIsArray && !newIsArray) {
|
|
65
|
-
const oldRec = oldValue;
|
|
66
|
-
const newRec = newValue;
|
|
67
|
-
const seen = /* @__PURE__ */ new Set();
|
|
68
|
-
for (const k of Object.keys(oldRec)) {
|
|
69
|
-
seen.add(k);
|
|
70
|
-
diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
|
|
71
|
-
}
|
|
72
|
-
for (const k of Object.keys(newRec)) {
|
|
73
|
-
if (seen.has(k)) continue;
|
|
74
|
-
diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
|
|
75
|
-
}
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
if (oldIsDescendable && !newIsDescendable) {
|
|
82
|
-
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
if (!oldIsDescendable && newIsDescendable) {
|
|
86
|
-
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
if (oldValue === void 0) {
|
|
90
|
-
visit({ kind: "added", path: prefix, newValue });
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
if (newValue === void 0) {
|
|
94
|
-
visit({ kind: "removed", path: prefix, oldValue });
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
98
|
-
}
|
|
99
|
-
function applyChangedKeys(target, source) {
|
|
100
|
-
if (!isDescendable(target) || !isDescendable(source)) return false;
|
|
101
|
-
const targetIsArray = Array.isArray(target);
|
|
102
|
-
const sourceIsArray = Array.isArray(source);
|
|
103
|
-
if (targetIsArray !== sourceIsArray) return false;
|
|
104
|
-
const ROOT_SENTINEL = Symbol.for("attaform.applyChangedKeys.rootMismatch");
|
|
105
|
-
const changedFirstSegments = /* @__PURE__ */ new Set();
|
|
106
|
-
diffAndApply(target, source, [], (patch) => {
|
|
107
|
-
if (patch.path.length === 0) {
|
|
108
|
-
changedFirstSegments.add(ROOT_SENTINEL);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
changedFirstSegments.add(patch.path[0]);
|
|
112
|
-
});
|
|
113
|
-
if (changedFirstSegments.has(ROOT_SENTINEL)) return false;
|
|
114
|
-
if (targetIsArray) {
|
|
115
|
-
const t = target;
|
|
116
|
-
const s = source;
|
|
117
|
-
if (t.length > s.length) t.length = s.length;
|
|
118
|
-
for (const idx of changedFirstSegments) {
|
|
119
|
-
if (typeof idx === "symbol") continue;
|
|
120
|
-
const i = typeof idx === "number" ? idx : Number(idx);
|
|
121
|
-
t[i] = s[i];
|
|
122
|
-
}
|
|
123
|
-
} else {
|
|
124
|
-
const t = target;
|
|
125
|
-
const s = source;
|
|
126
|
-
const sourceKeys = new Set(Object.keys(s));
|
|
127
|
-
for (const k of Object.keys(t)) {
|
|
128
|
-
if (!sourceKeys.has(k)) delete t[k];
|
|
129
|
-
}
|
|
130
|
-
for (const k of changedFirstSegments) {
|
|
131
|
-
if (typeof k === "symbol") continue;
|
|
132
|
-
t[String(k)] = s[String(k)];
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return true;
|
|
136
|
-
}
|
|
137
|
-
function structuralSnapshot(value) {
|
|
138
|
-
if (!isDescendable(value)) return value;
|
|
139
|
-
if (Array.isArray(value)) {
|
|
140
|
-
const out2 = new Array(value.length);
|
|
141
|
-
for (let i = 0; i < value.length; i++) {
|
|
142
|
-
out2[i] = structuralSnapshot(value[i]);
|
|
143
|
-
}
|
|
144
|
-
return out2;
|
|
145
|
-
}
|
|
146
|
-
const src = value;
|
|
147
|
-
const out = {};
|
|
148
|
-
for (const k of Object.keys(src)) {
|
|
149
|
-
out[k] = structuralSnapshot(src[k]);
|
|
150
|
-
}
|
|
151
|
-
return out;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const EMPTY_RESOLVED_FIELD_META = Object.freeze({
|
|
155
|
-
label: "",
|
|
156
|
-
description: void 0,
|
|
157
|
-
placeholder: void 0,
|
|
158
|
-
meta: Object.freeze({})
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
function humanize(segment) {
|
|
162
|
-
if (typeof segment === "number") return "";
|
|
163
|
-
const str = String(segment);
|
|
164
|
-
if (str.length === 0) return "";
|
|
165
|
-
if (/^\d+$/.test(str)) return "";
|
|
166
|
-
const tokens = str.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().split(" ").filter((part) => part.length > 0);
|
|
167
|
-
if (tokens.length === 0) return "";
|
|
168
|
-
return tokens.map((part) => {
|
|
169
|
-
const head = part[0];
|
|
170
|
-
return head === void 0 ? part : head.toUpperCase() + part.slice(1).toLowerCase();
|
|
171
|
-
}).join(" ");
|
|
172
|
-
}
|
|
4
|
+
const plugin = require('./attaform.Dee2rU1P.cjs');
|
|
5
|
+
const paths = require('./attaform.BBM2muQ9.cjs');
|
|
173
6
|
|
|
174
7
|
const NOT_FOUND = Symbol("NOT_FOUND");
|
|
175
8
|
function descendStep(value, segment) {
|
|
@@ -415,20 +248,236 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
|
|
|
415
248
|
return rec;
|
|
416
249
|
}
|
|
417
250
|
|
|
418
|
-
function
|
|
251
|
+
function isDescendable(value) {
|
|
252
|
+
if (value === null || typeof value !== "object") return false;
|
|
253
|
+
if (Array.isArray(value)) return true;
|
|
254
|
+
const proto = Object.getPrototypeOf(value);
|
|
255
|
+
return proto === null || proto === Object.prototype;
|
|
256
|
+
}
|
|
257
|
+
function appendSegment(prefix, segment) {
|
|
258
|
+
const next = new Array(prefix.length + 1);
|
|
259
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
260
|
+
const s = prefix[i];
|
|
261
|
+
next[i] = s;
|
|
262
|
+
}
|
|
263
|
+
next[prefix.length] = segment;
|
|
264
|
+
return next;
|
|
265
|
+
}
|
|
266
|
+
function diffAndApply(oldValue, newValue, prefix, visit) {
|
|
267
|
+
if (Object.is(oldValue, newValue)) return;
|
|
268
|
+
const oldIsDescendable = isDescendable(oldValue);
|
|
269
|
+
const newIsDescendable = isDescendable(newValue);
|
|
270
|
+
if (oldValue === void 0 && newIsDescendable) {
|
|
271
|
+
if (Array.isArray(newValue)) {
|
|
272
|
+
for (let i = 0; i < newValue.length; i++) {
|
|
273
|
+
diffAndApply(void 0, newValue[i], appendSegment(prefix, i), visit);
|
|
274
|
+
}
|
|
275
|
+
} else {
|
|
276
|
+
const rec = newValue;
|
|
277
|
+
for (const k of Object.keys(rec)) {
|
|
278
|
+
diffAndApply(void 0, rec[k], appendSegment(prefix, k), visit);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (oldIsDescendable && newValue === void 0) {
|
|
284
|
+
if (Array.isArray(oldValue)) {
|
|
285
|
+
for (let i = 0; i < oldValue.length; i++) {
|
|
286
|
+
diffAndApply(oldValue[i], void 0, appendSegment(prefix, i), visit);
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
const rec = oldValue;
|
|
290
|
+
for (const k of Object.keys(rec)) {
|
|
291
|
+
diffAndApply(rec[k], void 0, appendSegment(prefix, k), visit);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (oldIsDescendable && newIsDescendable) {
|
|
297
|
+
const oldIsArray = Array.isArray(oldValue);
|
|
298
|
+
const newIsArray = Array.isArray(newValue);
|
|
299
|
+
if (oldIsArray && newIsArray) {
|
|
300
|
+
const oldArr = oldValue;
|
|
301
|
+
const newArr = newValue;
|
|
302
|
+
const max = Math.max(oldArr.length, newArr.length);
|
|
303
|
+
for (let i = 0; i < max; i++) {
|
|
304
|
+
diffAndApply(oldArr[i], newArr[i], appendSegment(prefix, i), visit);
|
|
305
|
+
}
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (!oldIsArray && !newIsArray) {
|
|
309
|
+
const oldRec = oldValue;
|
|
310
|
+
const newRec = newValue;
|
|
311
|
+
const seen = /* @__PURE__ */ new Set();
|
|
312
|
+
for (const k of Object.keys(oldRec)) {
|
|
313
|
+
seen.add(k);
|
|
314
|
+
diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
|
|
315
|
+
}
|
|
316
|
+
for (const k of Object.keys(newRec)) {
|
|
317
|
+
if (seen.has(k)) continue;
|
|
318
|
+
diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
|
|
319
|
+
}
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (oldIsDescendable && !newIsDescendable) {
|
|
326
|
+
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (!oldIsDescendable && newIsDescendable) {
|
|
330
|
+
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (oldValue === void 0) {
|
|
334
|
+
visit({ kind: "added", path: prefix, newValue });
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (newValue === void 0) {
|
|
338
|
+
visit({ kind: "removed", path: prefix, oldValue });
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
visit({ kind: "changed", path: prefix, oldValue, newValue });
|
|
342
|
+
}
|
|
343
|
+
function applyChangedKeys(target, source) {
|
|
344
|
+
if (!isDescendable(target) || !isDescendable(source)) return false;
|
|
345
|
+
const targetIsArray = Array.isArray(target);
|
|
346
|
+
const sourceIsArray = Array.isArray(source);
|
|
347
|
+
if (targetIsArray !== sourceIsArray) return false;
|
|
348
|
+
const ROOT_SENTINEL = Symbol.for("attaform.applyChangedKeys.rootMismatch");
|
|
349
|
+
const changedFirstSegments = /* @__PURE__ */ new Set();
|
|
350
|
+
diffAndApply(target, source, [], (patch) => {
|
|
351
|
+
if (patch.path.length === 0) {
|
|
352
|
+
changedFirstSegments.add(ROOT_SENTINEL);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
changedFirstSegments.add(patch.path[0]);
|
|
356
|
+
});
|
|
357
|
+
if (changedFirstSegments.has(ROOT_SENTINEL)) return false;
|
|
358
|
+
if (targetIsArray) {
|
|
359
|
+
const t = target;
|
|
360
|
+
const s = source;
|
|
361
|
+
if (t.length > s.length) t.length = s.length;
|
|
362
|
+
for (const idx of changedFirstSegments) {
|
|
363
|
+
if (typeof idx === "symbol") continue;
|
|
364
|
+
const i = typeof idx === "number" ? idx : Number(idx);
|
|
365
|
+
t[i] = s[i];
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
const t = target;
|
|
369
|
+
const s = source;
|
|
370
|
+
const sourceKeys = new Set(Object.keys(s));
|
|
371
|
+
for (const k of Object.keys(t)) {
|
|
372
|
+
if (!sourceKeys.has(k)) delete t[k];
|
|
373
|
+
}
|
|
374
|
+
for (const k of changedFirstSegments) {
|
|
375
|
+
if (typeof k === "symbol") continue;
|
|
376
|
+
t[String(k)] = s[String(k)];
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
function applyPatchesForward(root, patches) {
|
|
382
|
+
let current = root;
|
|
383
|
+
for (const patch of patches) {
|
|
384
|
+
if (patch.path.length === 0) {
|
|
385
|
+
current = patch.kind === "removed" ? void 0 : patch.newValue;
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
if (patch.kind === "removed") {
|
|
389
|
+
current = deleteAtPath(current, patch.path);
|
|
390
|
+
} else {
|
|
391
|
+
current = setAtPath(current, patch.path, patch.newValue);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return current;
|
|
395
|
+
}
|
|
396
|
+
function applyPatchesInverse(root, patches) {
|
|
397
|
+
let current = root;
|
|
398
|
+
for (let i = patches.length - 1; i >= 0; i--) {
|
|
399
|
+
const patch = patches[i];
|
|
400
|
+
if (patch.path.length === 0) {
|
|
401
|
+
if (patch.kind === "added") {
|
|
402
|
+
current = void 0;
|
|
403
|
+
} else {
|
|
404
|
+
current = patch.oldValue;
|
|
405
|
+
}
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
if (patch.kind === "added") {
|
|
409
|
+
current = deleteAtPath(current, patch.path);
|
|
410
|
+
} else {
|
|
411
|
+
current = setAtPath(current, patch.path, patch.oldValue);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return current;
|
|
415
|
+
}
|
|
416
|
+
function structuralSnapshot(value) {
|
|
417
|
+
if (!isDescendable(value)) return value;
|
|
418
|
+
if (Array.isArray(value)) {
|
|
419
|
+
const out2 = new Array(value.length);
|
|
420
|
+
for (let i = 0; i < value.length; i++) {
|
|
421
|
+
out2[i] = structuralSnapshot(value[i]);
|
|
422
|
+
}
|
|
423
|
+
return out2;
|
|
424
|
+
}
|
|
425
|
+
const src = value;
|
|
426
|
+
const out = {};
|
|
427
|
+
for (const k of Object.keys(src)) {
|
|
428
|
+
out[k] = structuralSnapshot(src[k]);
|
|
429
|
+
}
|
|
430
|
+
return out;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const EMPTY_RESOLVED_FIELD_META = Object.freeze({
|
|
434
|
+
label: "",
|
|
435
|
+
description: void 0,
|
|
436
|
+
placeholder: void 0,
|
|
437
|
+
meta: Object.freeze({})
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
function humanize(segment) {
|
|
441
|
+
if (typeof segment === "number") return "";
|
|
442
|
+
const str = String(segment);
|
|
443
|
+
if (str.length === 0) return "";
|
|
444
|
+
if (/^\d+$/.test(str)) return "";
|
|
445
|
+
const tokens = str.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().split(" ").filter((part) => part.length > 0);
|
|
446
|
+
if (tokens.length === 0) return "";
|
|
447
|
+
return tokens.map((part) => {
|
|
448
|
+
const head = part[0];
|
|
449
|
+
return head === void 0 ? part : head.toUpperCase() + part.slice(1).toLowerCase();
|
|
450
|
+
}).join(" ");
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function isUnderStubAncestor(state, segments) {
|
|
454
|
+
for (let i = 0; i < segments.length; i++) {
|
|
455
|
+
const ancestorPath = segments.slice(0, i);
|
|
456
|
+
const du = state.schema.getUnionDiscriminatorAtPath(ancestorPath);
|
|
457
|
+
if (du === void 0) continue;
|
|
458
|
+
const ancestorValue = getAtPath(state.form.value, ancestorPath);
|
|
459
|
+
if (ancestorValue === null || typeof ancestorValue !== "object") continue;
|
|
460
|
+
const discValue = ancestorValue[du.discriminatorKey];
|
|
461
|
+
if (discValue === void 0) return true;
|
|
462
|
+
if (!du.isVariantSelected(discValue)) return true;
|
|
463
|
+
}
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
function buildFieldStateAccessor(state, getFormMetaBase, options) {
|
|
419
467
|
const cache = /* @__PURE__ */ new Map();
|
|
468
|
+
const predicate = options?.shouldShowErrors;
|
|
420
469
|
return function getFieldState(pathInput) {
|
|
421
470
|
const { segments, key } = paths.canonicalizePath(pathInput);
|
|
422
471
|
const cached = cache.get(key);
|
|
423
472
|
if (cached !== void 0) return cached;
|
|
424
473
|
const c = vue.computed(
|
|
425
|
-
() => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key) : buildContainerFieldState(state, segments)
|
|
474
|
+
() => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, getFormMetaBase, predicate) : buildContainerFieldState(state, segments, key, getFormMetaBase, predicate)
|
|
426
475
|
);
|
|
427
476
|
cache.set(key, c);
|
|
428
477
|
return c;
|
|
429
478
|
};
|
|
430
479
|
}
|
|
431
|
-
function
|
|
480
|
+
function buildLeafFieldStateBase(state, segments, key) {
|
|
432
481
|
const record = state.fields.get(key);
|
|
433
482
|
const value = state.getValueAtPath(segments);
|
|
434
483
|
const original = state.originals.get(key)?.value;
|
|
@@ -442,7 +491,8 @@ function buildLeafFieldState(state, segments, key) {
|
|
|
442
491
|
if (userForKey !== void 0) errors.push(...userForKey);
|
|
443
492
|
const validating = (state.fieldValidationCounts.get(key) ?? 0) > 0;
|
|
444
493
|
const gated = state.pathHasAsyncValidation(segments) && !state.firstValidationDone.value;
|
|
445
|
-
const
|
|
494
|
+
const isOrphan = segments.length > 0 && !hasAtPath(state.form.value, segments) && isUnderStubAncestor(state, segments);
|
|
495
|
+
const valid = !gated && errors.length === 0 && !validating && !isOrphan;
|
|
446
496
|
const elementRecord = state.elements.get(key);
|
|
447
497
|
const elementsArr = elementRecord ? Object.freeze([...elementRecord.elements]) : EMPTY_ELEMENTS;
|
|
448
498
|
const firstElement = elementsArr[0] ?? null;
|
|
@@ -472,7 +522,11 @@ function buildLeafFieldState(state, segments, key) {
|
|
|
472
522
|
meta: resolved.meta
|
|
473
523
|
};
|
|
474
524
|
}
|
|
475
|
-
function
|
|
525
|
+
function buildLeafFieldState(state, segments, key, getFormMetaBase, shouldShowErrors) {
|
|
526
|
+
const base = buildLeafFieldStateBase(state, segments, key);
|
|
527
|
+
return decorateWithDerivedProps(base, state, getFormMetaBase, shouldShowErrors);
|
|
528
|
+
}
|
|
529
|
+
function buildContainerFieldStateBase(state, segments, _key) {
|
|
476
530
|
const formValue = state.form.value;
|
|
477
531
|
const value = state.getValueAtPath(segments);
|
|
478
532
|
const original = state.originals.get(paths.canonicalizePath(segments).key)?.value;
|
|
@@ -538,6 +592,16 @@ function buildContainerFieldState(state, segments, _key) {
|
|
|
538
592
|
meta: resolved.meta
|
|
539
593
|
};
|
|
540
594
|
}
|
|
595
|
+
function buildContainerFieldState(state, segments, key, getFormMetaBase, shouldShowErrors) {
|
|
596
|
+
const base = buildContainerFieldStateBase(state, segments);
|
|
597
|
+
return decorateWithDerivedProps(base, state, getFormMetaBase, shouldShowErrors);
|
|
598
|
+
}
|
|
599
|
+
function decorateWithDerivedProps(base, state, getFormMetaBase, shouldShowErrors) {
|
|
600
|
+
const firstError = base.errors[0];
|
|
601
|
+
const predicate = shouldShowErrors ?? state.shouldShowErrors;
|
|
602
|
+
const showErrors = base.errors.length > 0 && predicate(base, getFormMetaBase());
|
|
603
|
+
return { ...base, showErrors, firstError };
|
|
604
|
+
}
|
|
541
605
|
function aggregateErrorsAt(state, prefix) {
|
|
542
606
|
const formValue = state.form.value;
|
|
543
607
|
const buckets = /* @__PURE__ */ new Map();
|
|
@@ -547,7 +611,7 @@ function aggregateErrorsAt(state, prefix) {
|
|
|
547
611
|
const segs = paths.segmentsForPathKey(pathKey);
|
|
548
612
|
if (segs === null) continue;
|
|
549
613
|
if (!paths.isPathPrefix(prefix, segs)) continue;
|
|
550
|
-
if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
|
|
614
|
+
if (pathKey === paths.FORM_ERRORS_PATH_KEY) ; else if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
|
|
551
615
|
const ordinal = state.ensurePathOrdinal(pathKey);
|
|
552
616
|
const existing = buckets.get(ordinal);
|
|
553
617
|
if (existing === void 0) buckets.set(ordinal, [...list]);
|
|
@@ -720,14 +784,16 @@ function buildErrorsProxy(state) {
|
|
|
720
784
|
return buildSurfaceProxy({
|
|
721
785
|
schema: state.schema,
|
|
722
786
|
resolveLeaf: (path) => {
|
|
723
|
-
if (!hasAtPath(state.form.value, path)) return void 0;
|
|
724
787
|
const { key } = paths.canonicalizePath(path);
|
|
725
|
-
const schemaForKey = state.schemaErrors.get(key);
|
|
726
|
-
const blankForKey = state.derivedBlankErrors.value.get(key);
|
|
727
788
|
const userForKey = state.userErrors.get(key);
|
|
789
|
+
const isActive = hasAtPath(state.form.value, path);
|
|
728
790
|
const merged = [];
|
|
729
|
-
if (
|
|
730
|
-
|
|
791
|
+
if (isActive) {
|
|
792
|
+
const schemaForKey = state.schemaErrors.get(key);
|
|
793
|
+
const blankForKey = state.derivedBlankErrors.value.get(key);
|
|
794
|
+
if (schemaForKey !== void 0) merged.push(...schemaForKey);
|
|
795
|
+
if (blankForKey !== void 0) merged.push(...blankForKey);
|
|
796
|
+
}
|
|
731
797
|
if (userForKey !== void 0) merged.push(...userForKey);
|
|
732
798
|
return merged.length === 0 ? void 0 : merged;
|
|
733
799
|
},
|
|
@@ -752,7 +818,7 @@ function buildErrorsProxy(state) {
|
|
|
752
818
|
function materializeErrors(state, containerSegments) {
|
|
753
819
|
const liveContainer = getAtPath(state.form.value, containerSegments);
|
|
754
820
|
const tree = Array.isArray(liveContainer) ? [] : {};
|
|
755
|
-
const collect = (store) => {
|
|
821
|
+
const collect = (store, applyActivePathFilter) => {
|
|
756
822
|
entries: for (const [pathKey, errors] of store) {
|
|
757
823
|
if (errors.length === 0) continue;
|
|
758
824
|
const fullPath = paths.segmentsForPathKey(pathKey);
|
|
@@ -761,13 +827,13 @@ function materializeErrors(state, containerSegments) {
|
|
|
761
827
|
for (let i = 0; i < containerSegments.length; i++) {
|
|
762
828
|
if (fullPath[i] !== containerSegments[i]) continue entries;
|
|
763
829
|
}
|
|
764
|
-
if (!hasAtPath(state.form.value, fullPath)) continue;
|
|
830
|
+
if (applyActivePathFilter && !hasAtPath(state.form.value, fullPath)) continue;
|
|
765
831
|
placeAt(tree, fullPath.slice(containerSegments.length), errors);
|
|
766
832
|
}
|
|
767
833
|
};
|
|
768
|
-
collect(state.schemaErrors);
|
|
769
|
-
collect(state.derivedBlankErrors.value);
|
|
770
|
-
collect(state.userErrors);
|
|
834
|
+
collect(state.schemaErrors, true);
|
|
835
|
+
collect(state.derivedBlankErrors.value, true);
|
|
836
|
+
collect(state.userErrors, false);
|
|
771
837
|
return tree;
|
|
772
838
|
}
|
|
773
839
|
function placeAt(tree, path, errors) {
|
|
@@ -798,11 +864,13 @@ function buildFieldArrayApi(state) {
|
|
|
798
864
|
const current = state.getValueAtPath(segments);
|
|
799
865
|
return Array.isArray(current) ? current.slice() : [];
|
|
800
866
|
}
|
|
801
|
-
function writeArray(path, next) {
|
|
867
|
+
function writeArray(path, next, arrayOp) {
|
|
802
868
|
const { segments, key } = paths.canonicalizePath(path);
|
|
803
|
-
|
|
804
|
-
persist: state.persistOptIns.hasAnyOptInForPath(key)
|
|
805
|
-
|
|
869
|
+
const meta = {
|
|
870
|
+
persist: state.persistOptIns.hasAnyOptInForPath(key),
|
|
871
|
+
...arrayOp !== void 0 ? { arrayOp } : {}
|
|
872
|
+
};
|
|
873
|
+
return state.setValueAtPath(segments, next, meta);
|
|
806
874
|
}
|
|
807
875
|
return {
|
|
808
876
|
append(path, value) {
|
|
@@ -813,18 +881,19 @@ function buildFieldArrayApi(state) {
|
|
|
813
881
|
prepend(path, value) {
|
|
814
882
|
const next = readArray(path);
|
|
815
883
|
next.unshift(value);
|
|
816
|
-
return writeArray(path, next);
|
|
884
|
+
return writeArray(path, next, { kind: "shift-from", index: 0 });
|
|
817
885
|
},
|
|
818
886
|
insert(path, index, value) {
|
|
819
887
|
const next = readArray(path);
|
|
820
888
|
next.splice(index, 0, value);
|
|
821
|
-
|
|
889
|
+
const clampedIndex = Math.max(0, Math.min(index, next.length));
|
|
890
|
+
return writeArray(path, next, { kind: "shift-from", index: clampedIndex });
|
|
822
891
|
},
|
|
823
892
|
remove(path, index) {
|
|
824
893
|
const next = readArray(path);
|
|
825
894
|
if (index < 0 || index >= next.length) return false;
|
|
826
895
|
next.splice(index, 1);
|
|
827
|
-
return writeArray(path, next);
|
|
896
|
+
return writeArray(path, next, { kind: "shift-from", index });
|
|
828
897
|
},
|
|
829
898
|
swap(path, a, b) {
|
|
830
899
|
const next = readArray(path);
|
|
@@ -834,7 +903,7 @@ function buildFieldArrayApi(state) {
|
|
|
834
903
|
const tmp = next[a];
|
|
835
904
|
next[a] = next[b];
|
|
836
905
|
next[b] = tmp;
|
|
837
|
-
return writeArray(path, next);
|
|
906
|
+
return writeArray(path, next, { kind: "swap", a, b });
|
|
838
907
|
},
|
|
839
908
|
move(path, from, to) {
|
|
840
909
|
const next = readArray(path);
|
|
@@ -842,13 +911,17 @@ function buildFieldArrayApi(state) {
|
|
|
842
911
|
const [item] = next.splice(from, 1);
|
|
843
912
|
const clampedTo = Math.max(0, Math.min(to, next.length));
|
|
844
913
|
next.splice(clampedTo, 0, item);
|
|
845
|
-
return writeArray(path, next
|
|
914
|
+
return writeArray(path, next, {
|
|
915
|
+
kind: "shift-range",
|
|
916
|
+
fromIndex: Math.min(from, clampedTo),
|
|
917
|
+
toIndex: Math.max(from, clampedTo)
|
|
918
|
+
});
|
|
846
919
|
},
|
|
847
920
|
replace(path, index, value) {
|
|
848
921
|
const next = readArray(path);
|
|
849
922
|
if (index < 0 || index >= next.length) return false;
|
|
850
923
|
next[index] = value;
|
|
851
|
-
return writeArray(path, next);
|
|
924
|
+
return writeArray(path, next, { kind: "replace-at", index });
|
|
852
925
|
}
|
|
853
926
|
};
|
|
854
927
|
}
|
|
@@ -868,6 +941,8 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
|
|
|
868
941
|
"errors",
|
|
869
942
|
"validating",
|
|
870
943
|
"valid",
|
|
944
|
+
"showErrors",
|
|
945
|
+
"firstError",
|
|
871
946
|
"path",
|
|
872
947
|
"blank",
|
|
873
948
|
"label",
|
|
@@ -875,8 +950,12 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
|
|
|
875
950
|
"placeholder",
|
|
876
951
|
"meta"
|
|
877
952
|
]);
|
|
878
|
-
function buildFieldStateProxy(state) {
|
|
879
|
-
const getFieldStateAt = buildFieldStateAccessor(
|
|
953
|
+
function buildFieldStateProxy(state, getFormMetaBase, options) {
|
|
954
|
+
const getFieldStateAt = buildFieldStateAccessor(
|
|
955
|
+
state,
|
|
956
|
+
getFormMetaBase,
|
|
957
|
+
options?.shouldShowErrors !== void 0 ? { shouldShowErrors: options.shouldShowErrors } : void 0
|
|
958
|
+
);
|
|
880
959
|
const snapshotFieldStateAt = (path) => {
|
|
881
960
|
const view = getFieldStateAt(path).value;
|
|
882
961
|
const snapshot = {};
|
|
@@ -967,10 +1046,25 @@ function walk$2(value, basePath, schema, snapshotFieldStateAt) {
|
|
|
967
1046
|
|
|
968
1047
|
const DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS = 0;
|
|
969
1048
|
const DEFAULT_PERSISTENCE_DEBOUNCE_MS = 300;
|
|
970
|
-
const DEFAULT_HISTORY_MAX_SNAPSHOTS =
|
|
1049
|
+
const DEFAULT_HISTORY_MAX_SNAPSHOTS = 128;
|
|
971
1050
|
const PERSISTENCE_KEY_PREFIX = "attaform:";
|
|
972
1051
|
const RESERVED_KEY_PREFIX = "__atta:";
|
|
973
1052
|
const ANONYMOUS_FORM_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon:`;
|
|
1053
|
+
const DEFAULT_MAX_RECURSION_DEPTH = 64;
|
|
1054
|
+
function normalizeNumericOption(config) {
|
|
1055
|
+
const { value, source, allowInfinity, min, defaultValue } = config;
|
|
1056
|
+
if (allowInfinity && value === Infinity) return Infinity;
|
|
1057
|
+
if (typeof value !== "number" || Number.isNaN(value) || value === Infinity || value === -Infinity) {
|
|
1058
|
+
if (plugin.__DEV__) {
|
|
1059
|
+
const acceptedDescription = allowInfinity ? "a non-negative integer or Infinity" : "a non-negative finite integer";
|
|
1060
|
+
console.warn(
|
|
1061
|
+
`[attaform] ${source} must be ${acceptedDescription}; got ${String(value)}. Falling back to ${String(defaultValue)}.`
|
|
1062
|
+
);
|
|
1063
|
+
}
|
|
1064
|
+
return defaultValue;
|
|
1065
|
+
}
|
|
1066
|
+
return Math.max(min, Math.floor(value));
|
|
1067
|
+
}
|
|
974
1068
|
|
|
975
1069
|
const PERSISTENCE_MODULE_KEY = "persistence";
|
|
976
1070
|
async function getStorageAdapter(storage) {
|
|
@@ -1145,20 +1239,29 @@ function mergeDeep(target, source, path, schema) {
|
|
|
1145
1239
|
if (source === null || typeof source !== "object") return source;
|
|
1146
1240
|
if (Array.isArray(source)) return source;
|
|
1147
1241
|
if (!isPlainRecord(source)) return source;
|
|
1148
|
-
let mergeTarget = target;
|
|
1149
1242
|
if (schema !== void 0) {
|
|
1150
1243
|
const du = schema.getUnionDiscriminatorAtPath(path);
|
|
1151
1244
|
if (du !== void 0) {
|
|
1152
|
-
const
|
|
1153
|
-
const
|
|
1154
|
-
if (sourceDisc !== void 0 && !
|
|
1245
|
+
const sourceRecord = source;
|
|
1246
|
+
const sourceDisc = sourceRecord[du.discriminatorKey];
|
|
1247
|
+
if (sourceDisc !== void 0 && !du.isVariantSelected(sourceDisc)) {
|
|
1248
|
+
return { [du.discriminatorKey]: sourceDisc };
|
|
1249
|
+
}
|
|
1250
|
+
if (sourceDisc !== void 0) {
|
|
1155
1251
|
const variantDefault = du.getVariantDefault(sourceDisc);
|
|
1156
1252
|
if (isPlainRecord(variantDefault)) {
|
|
1157
|
-
|
|
1253
|
+
const out2 = { ...variantDefault };
|
|
1254
|
+
for (const key of Object.keys(sourceRecord)) {
|
|
1255
|
+
if (!(key in variantDefault) && key !== du.discriminatorKey) continue;
|
|
1256
|
+
out2[key] = mergeDeep(out2[key], sourceRecord[key], [...path, key], schema);
|
|
1257
|
+
}
|
|
1258
|
+
return out2;
|
|
1158
1259
|
}
|
|
1159
1260
|
}
|
|
1261
|
+
return {};
|
|
1160
1262
|
}
|
|
1161
1263
|
}
|
|
1264
|
+
const mergeTarget = target;
|
|
1162
1265
|
const out = isPlainRecord(mergeTarget) ? { ...mergeTarget } : {};
|
|
1163
1266
|
for (const key of Object.keys(source)) {
|
|
1164
1267
|
out[key] = mergeDeep(out[key], source[key], [...path, key], schema);
|
|
@@ -1187,14 +1290,14 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1187
1290
|
});
|
|
1188
1291
|
let gen = 0;
|
|
1189
1292
|
async function kickoff(data, path, captured) {
|
|
1190
|
-
state.activeValidations.value += 1;
|
|
1191
|
-
result.value = {
|
|
1192
|
-
pending: true,
|
|
1193
|
-
errors: void 0,
|
|
1194
|
-
success: false,
|
|
1195
|
-
formKey: state.formKey
|
|
1196
|
-
};
|
|
1197
1293
|
try {
|
|
1294
|
+
state.activeValidations.value += 1;
|
|
1295
|
+
result.value = {
|
|
1296
|
+
pending: true,
|
|
1297
|
+
errors: void 0,
|
|
1298
|
+
success: false,
|
|
1299
|
+
formKey: state.formKey
|
|
1300
|
+
};
|
|
1198
1301
|
const refinement = await runRefinementValidation(data, path);
|
|
1199
1302
|
if (captured !== gen) return;
|
|
1200
1303
|
result.value = settled(composeWithDerivedBlank(refinement, path));
|
|
@@ -1238,14 +1341,44 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1238
1341
|
async function validateAsync(pathInput) {
|
|
1239
1342
|
const segments = pathInput === void 0 ? void 0 : toSegments(pathInput);
|
|
1240
1343
|
const dataAtPath = segments === void 0 ? state.form.value : state.getValueAtPath(segments);
|
|
1241
|
-
state.activeValidations.value += 1;
|
|
1242
1344
|
try {
|
|
1345
|
+
state.activeValidations.value += 1;
|
|
1243
1346
|
const refinement = await runRefinementValidation(dataAtPath, segments);
|
|
1244
1347
|
return stripData(composeWithDerivedBlank(refinement, segments));
|
|
1348
|
+
} catch (err) {
|
|
1349
|
+
return adapterThrowResponse(err);
|
|
1350
|
+
} finally {
|
|
1351
|
+
state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
async function process(pathInput) {
|
|
1355
|
+
const segments = pathInput === void 0 ? void 0 : toSegments(pathInput);
|
|
1356
|
+
const dataAtPath = segments === void 0 ? state.form.value : state.getValueAtPath(segments);
|
|
1357
|
+
try {
|
|
1358
|
+
state.activeValidations.value += 1;
|
|
1359
|
+
const refinement = await runRefinementValidation(dataAtPath, segments);
|
|
1360
|
+
return composeWithDerivedBlank(refinement, segments);
|
|
1361
|
+
} catch (err) {
|
|
1362
|
+
return adapterThrowResponse(err);
|
|
1245
1363
|
} finally {
|
|
1246
1364
|
state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
|
|
1247
1365
|
}
|
|
1248
1366
|
}
|
|
1367
|
+
function adapterThrowResponse(err) {
|
|
1368
|
+
return {
|
|
1369
|
+
success: false,
|
|
1370
|
+
data: void 0,
|
|
1371
|
+
errors: [
|
|
1372
|
+
{
|
|
1373
|
+
message: adapterThrowMessage(err),
|
|
1374
|
+
path: [],
|
|
1375
|
+
formKey: state.formKey,
|
|
1376
|
+
code: AttaformErrorCode.AdapterThrew
|
|
1377
|
+
}
|
|
1378
|
+
],
|
|
1379
|
+
formKey: state.formKey
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1249
1382
|
async function runRefinementValidation(data, path) {
|
|
1250
1383
|
return await state.schema.validateAtPath(data, path);
|
|
1251
1384
|
}
|
|
@@ -1267,14 +1400,17 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1267
1400
|
if (event !== void 0 && "preventDefault" in event && typeof event.preventDefault === "function") {
|
|
1268
1401
|
event.preventDefault();
|
|
1269
1402
|
}
|
|
1403
|
+
if (state.activeSubmissions.value > 0) {
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1270
1406
|
const genAtEntry = state.submissionGeneration.value;
|
|
1271
|
-
state.activeSubmissions.value += 1;
|
|
1272
|
-
state.submitting.value = true;
|
|
1273
|
-
state.submitError.value = null;
|
|
1274
|
-
state.cancelFieldValidation();
|
|
1275
|
-
state.activeValidations.value += 1;
|
|
1276
1407
|
let validationSettled = false;
|
|
1277
1408
|
try {
|
|
1409
|
+
state.activeSubmissions.value += 1;
|
|
1410
|
+
state.submitting.value = true;
|
|
1411
|
+
state.submitError.value = null;
|
|
1412
|
+
state.cancelFieldValidation();
|
|
1413
|
+
state.activeValidations.value += 1;
|
|
1278
1414
|
const refinement = await runRefinementValidation(state.form.value, void 0);
|
|
1279
1415
|
const merged = composeWithDerivedBlank(refinement, void 0);
|
|
1280
1416
|
state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
|
|
@@ -1323,7 +1459,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
|
|
|
1323
1459
|
};
|
|
1324
1460
|
return submitHandler;
|
|
1325
1461
|
};
|
|
1326
|
-
return { validate, validateAsync, handleSubmit };
|
|
1462
|
+
return { validate, validateAsync, process, handleSubmit };
|
|
1327
1463
|
}
|
|
1328
1464
|
function toSegments(pathInput) {
|
|
1329
1465
|
return paths.canonicalizePath(pathInput).segments;
|
|
@@ -1661,11 +1797,12 @@ function coerceValue(value, accepted, elementAccepted, index) {
|
|
|
1661
1797
|
const EMPTY_TRANSFORMS = Object.freeze([]);
|
|
1662
1798
|
const INTERACTIVE_TAG_NAMES = /* @__PURE__ */ new Set(["INPUT", "SELECT", "TEXTAREA"]);
|
|
1663
1799
|
const attaformListenersSymbol = Symbol.for("attaform:focus-listeners");
|
|
1664
|
-
function attachFocusListeners(state, segments, element) {
|
|
1800
|
+
function attachFocusListeners(state, segments, element, instanceMeta) {
|
|
1665
1801
|
const target = element;
|
|
1666
1802
|
if (target[attaformListenersSymbol] !== void 0) return;
|
|
1667
|
-
const
|
|
1668
|
-
const
|
|
1803
|
+
const focusMeta = instanceMeta !== void 0 ? { instance: instanceMeta } : void 0;
|
|
1804
|
+
const handleFocus = () => state.markFocused(segments, true, focusMeta);
|
|
1805
|
+
const handleBlur = () => state.markFocused(segments, false, focusMeta);
|
|
1669
1806
|
element.addEventListener("focus", handleFocus);
|
|
1670
1807
|
element.addEventListener("blur", handleBlur);
|
|
1671
1808
|
target[attaformListenersSymbol] = { handleFocus, handleBlur };
|
|
@@ -1678,7 +1815,13 @@ function detachFocusListeners(element) {
|
|
|
1678
1815
|
element.removeEventListener("blur", listeners.handleBlur);
|
|
1679
1816
|
delete target[attaformListenersSymbol];
|
|
1680
1817
|
}
|
|
1681
|
-
function buildRegister(state, formInstanceId) {
|
|
1818
|
+
function buildRegister(state, formInstanceId, instanceConfig) {
|
|
1819
|
+
const coerceIndex = instanceConfig?.coerce !== void 0 ? resolveCoercionIndex(instanceConfig.coerce) : state.coerceIndex;
|
|
1820
|
+
const instanceMeta = instanceConfig?.instanceMeta;
|
|
1821
|
+
const withInstanceMeta = (meta) => {
|
|
1822
|
+
if (instanceMeta === void 0) return meta;
|
|
1823
|
+
return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
|
|
1824
|
+
};
|
|
1682
1825
|
const lastTypedFormByPath = /* @__PURE__ */ new Map();
|
|
1683
1826
|
return function register(pathInput, options) {
|
|
1684
1827
|
const { segments, key: pathKey } = paths.canonicalizePath(pathInput);
|
|
@@ -1701,16 +1844,23 @@ function buildRegister(state, formInstanceId) {
|
|
|
1701
1844
|
const slimDefault = state.schema.getDefaultAtPath(segments);
|
|
1702
1845
|
const persist = options?.persist === true;
|
|
1703
1846
|
const acknowledgeSensitive = options?.acknowledgeSensitive === true;
|
|
1847
|
+
const multiTab = options?.multiTab !== false;
|
|
1704
1848
|
const transforms = options?.transforms ?? EMPTY_TRANSFORMS;
|
|
1849
|
+
const markNoSync = !multiTab ? () => {
|
|
1850
|
+
state.incrementNoSyncOptOut(pathKey);
|
|
1851
|
+
} : void 0;
|
|
1852
|
+
const unmarkNoSync = !multiTab ? () => {
|
|
1853
|
+
state.decrementNoSyncOptOut(pathKey);
|
|
1854
|
+
} : void 0;
|
|
1705
1855
|
const coerce = buildCoerceFn(
|
|
1706
1856
|
state.schema,
|
|
1707
1857
|
segments,
|
|
1708
|
-
|
|
1858
|
+
coerceIndex
|
|
1709
1859
|
);
|
|
1710
1860
|
const coerceElement = buildElementCoerceFn(
|
|
1711
1861
|
state.schema,
|
|
1712
1862
|
segments,
|
|
1713
|
-
|
|
1863
|
+
coerceIndex
|
|
1714
1864
|
);
|
|
1715
1865
|
if (persist && !state.ssr && !state.modules.has(PERSISTENCE_MODULE_KEY)) {
|
|
1716
1866
|
throw new plugin.AnonPersistError({
|
|
@@ -1724,22 +1874,26 @@ function buildRegister(state, formInstanceId) {
|
|
|
1724
1874
|
displayValue,
|
|
1725
1875
|
lastTypedForm,
|
|
1726
1876
|
markBlank: () => {
|
|
1727
|
-
return state.setValueAtPath(
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1877
|
+
return state.setValueAtPath(
|
|
1878
|
+
segments,
|
|
1879
|
+
slimDefault,
|
|
1880
|
+
withInstanceMeta({
|
|
1881
|
+
blank: true,
|
|
1882
|
+
persist
|
|
1883
|
+
})
|
|
1884
|
+
);
|
|
1731
1885
|
},
|
|
1732
1886
|
registerElement: (element) => {
|
|
1733
1887
|
if (!INTERACTIVE_TAG_NAMES.has(element.tagName)) return;
|
|
1734
1888
|
const added = state.registerElement(segments, element, formInstanceId);
|
|
1735
|
-
if (added) attachFocusListeners(state, segments, element);
|
|
1889
|
+
if (added) attachFocusListeners(state, segments, element, instanceMeta);
|
|
1736
1890
|
},
|
|
1737
1891
|
deregisterElement: (element) => {
|
|
1738
1892
|
detachFocusListeners(element);
|
|
1739
1893
|
state.deregisterElement(segments, element);
|
|
1740
1894
|
},
|
|
1741
1895
|
setValueWithInternalPath: (value, meta) => {
|
|
1742
|
-
return state.setValueAtPath(segments, value, meta);
|
|
1896
|
+
return state.setValueAtPath(segments, value, withInstanceMeta(meta));
|
|
1743
1897
|
},
|
|
1744
1898
|
// Called by the `vRegisterHint` compile-time transform's wrapping
|
|
1745
1899
|
// IIFE on every server-side render of `<element v-register="…">`.
|
|
@@ -1765,6 +1919,10 @@ function buildRegister(state, formInstanceId) {
|
|
|
1765
1919
|
persist,
|
|
1766
1920
|
acknowledgeSensitive,
|
|
1767
1921
|
persistOptIns: state.persistOptIns,
|
|
1922
|
+
isSensitivePath: state.isSensitivePath,
|
|
1923
|
+
multiTab,
|
|
1924
|
+
...markNoSync !== void 0 ? { markNoSync } : {},
|
|
1925
|
+
...unmarkNoSync !== void 0 ? { unmarkNoSync } : {},
|
|
1768
1926
|
transforms,
|
|
1769
1927
|
coerce,
|
|
1770
1928
|
...coerceElement !== void 0 ? { coerceElement } : {}
|
|
@@ -1922,6 +2080,8 @@ function buildValuesProxy(form) {
|
|
|
1922
2080
|
const inner = vue.computed(() => vue.readonly(form.value));
|
|
1923
2081
|
const target = (() => {
|
|
1924
2082
|
});
|
|
2083
|
+
const valuesToString = () => JSON.stringify(inner.value);
|
|
2084
|
+
const valuesToPrimitive = (hint) => hint === "number" ? NaN : valuesToString();
|
|
1925
2085
|
return new Proxy(target, {
|
|
1926
2086
|
apply(_, __, args) {
|
|
1927
2087
|
const arg = args[0];
|
|
@@ -1935,8 +2095,16 @@ function buildValuesProxy(form) {
|
|
|
1935
2095
|
return cursor;
|
|
1936
2096
|
},
|
|
1937
2097
|
get(_, key) {
|
|
1938
|
-
if (typeof key === "symbol")
|
|
2098
|
+
if (typeof key === "symbol") {
|
|
2099
|
+
if (key === Symbol.toPrimitive) return valuesToPrimitive;
|
|
2100
|
+
return Reflect.get(target, key);
|
|
2101
|
+
}
|
|
1939
2102
|
if (key === "toJSON") return () => inner.value;
|
|
2103
|
+
if (key === "toString") return valuesToString;
|
|
2104
|
+
if (key === "valueOf")
|
|
2105
|
+
return function() {
|
|
2106
|
+
return this;
|
|
2107
|
+
};
|
|
1940
2108
|
return inner.value[key];
|
|
1941
2109
|
},
|
|
1942
2110
|
has(_, key) {
|
|
@@ -1975,6 +2143,14 @@ function buildValuesProxy(form) {
|
|
|
1975
2143
|
});
|
|
1976
2144
|
}
|
|
1977
2145
|
|
|
2146
|
+
function blankForKind(slimDefault) {
|
|
2147
|
+
if (typeof slimDefault === "string") return "";
|
|
2148
|
+
if (typeof slimDefault === "number") return 0;
|
|
2149
|
+
if (typeof slimDefault === "bigint") return 0n;
|
|
2150
|
+
if (typeof slimDefault === "boolean") return false;
|
|
2151
|
+
if (slimDefault === null) return null;
|
|
2152
|
+
return void 0;
|
|
2153
|
+
}
|
|
1978
2154
|
function readonlySetSnapshot(source) {
|
|
1979
2155
|
const snapshot = new Set(source);
|
|
1980
2156
|
return new Proxy(snapshot, {
|
|
@@ -1990,15 +2166,36 @@ function readonlySetSnapshot(source) {
|
|
|
1990
2166
|
});
|
|
1991
2167
|
}
|
|
1992
2168
|
function buildFormApi(state, formInstanceId, options = {}) {
|
|
1993
|
-
const
|
|
2169
|
+
const instanceMeta = (() => {
|
|
2170
|
+
const bag = {};
|
|
2171
|
+
if (options.validateOn !== void 0) bag.validateOn = options.validateOn;
|
|
2172
|
+
if (options.debounceMs !== void 0) bag.debounceMs = options.debounceMs;
|
|
2173
|
+
if (options.rememberVariants !== void 0) bag.rememberVariants = options.rememberVariants;
|
|
2174
|
+
return Object.keys(bag).length > 0 ? bag : void 0;
|
|
2175
|
+
})();
|
|
2176
|
+
const withInstanceMeta = (meta) => {
|
|
2177
|
+
if (instanceMeta === void 0) return meta;
|
|
2178
|
+
return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
|
|
2179
|
+
};
|
|
2180
|
+
const registerConfig = {
|
|
2181
|
+
...instanceMeta !== void 0 ? { instanceMeta } : {},
|
|
2182
|
+
...options.coerce !== void 0 ? { coerce: options.coerce } : {}
|
|
2183
|
+
};
|
|
2184
|
+
const register = buildRegister(
|
|
2185
|
+
state,
|
|
2186
|
+
formInstanceId,
|
|
2187
|
+
Object.keys(registerConfig).length > 0 ? registerConfig : void 0
|
|
2188
|
+
);
|
|
1994
2189
|
const processOptions = options.onInvalidSubmit !== void 0 ? { onInvalidSubmit: options.onInvalidSubmit } : {};
|
|
1995
2190
|
const {
|
|
1996
2191
|
validate: validateBuilt,
|
|
1997
2192
|
validateAsync: validateAsyncBuilt,
|
|
2193
|
+
process: processBuilt,
|
|
1998
2194
|
handleSubmit
|
|
1999
2195
|
} = buildProcessForm(state, formInstanceId, processOptions);
|
|
2000
2196
|
const validate = (pathInput) => validateBuilt(pathInput);
|
|
2001
2197
|
const validateAsync = (pathInput) => validateAsyncBuilt(pathInput);
|
|
2198
|
+
const process = (pathInput) => processBuilt(pathInput);
|
|
2002
2199
|
function pathToRef(pathInput) {
|
|
2003
2200
|
const segments = paths.canonicalizePath(pathInput).segments;
|
|
2004
2201
|
return vue.computed(() => getAtPath(state.form.value, segments));
|
|
@@ -2010,22 +2207,36 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2010
2207
|
next,
|
|
2011
2208
|
state.schema
|
|
2012
2209
|
);
|
|
2013
|
-
const ok2 = state.setValueAtPath([], walked2.cleanedValues);
|
|
2210
|
+
const ok2 = state.setValueAtPath([], walked2.cleanedValues, withInstanceMeta());
|
|
2014
2211
|
if (!ok2) return false;
|
|
2015
2212
|
for (const pathKey of walked2.paths) {
|
|
2016
2213
|
const segments2 = paths.segmentsForPathKey(pathKey);
|
|
2017
2214
|
if (segments2 === null) continue;
|
|
2018
|
-
state.setValueAtPath(
|
|
2019
|
-
|
|
2020
|
-
|
|
2215
|
+
state.setValueAtPath(
|
|
2216
|
+
segments2,
|
|
2217
|
+
state.schema.getDefaultAtPath(segments2),
|
|
2218
|
+
withInstanceMeta({ blank: true })
|
|
2219
|
+
);
|
|
2021
2220
|
}
|
|
2022
2221
|
return true;
|
|
2023
2222
|
}
|
|
2024
2223
|
const segments = paths.canonicalizePath(pathOrValue).segments;
|
|
2025
2224
|
if (isUnset(maybeValue)) {
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2225
|
+
const last = segments.length > 0 ? segments[segments.length - 1] : void 0;
|
|
2226
|
+
if (typeof last === "string") {
|
|
2227
|
+
const parent = segments.slice(0, -1);
|
|
2228
|
+
const parentDU = state.schema.getUnionDiscriminatorAtPath(parent);
|
|
2229
|
+
if (parentDU?.discriminatorKey === last) {
|
|
2230
|
+
const slimDefault = state.schema.getDefaultAtPath(segments);
|
|
2231
|
+
const blank = blankForKind(slimDefault);
|
|
2232
|
+
return state.setValueAtPath(segments, blank, withInstanceMeta({ blank: true }));
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
return state.setValueAtPath(
|
|
2236
|
+
segments,
|
|
2237
|
+
state.schema.getDefaultAtPath(segments),
|
|
2238
|
+
withInstanceMeta({ blank: true })
|
|
2239
|
+
);
|
|
2029
2240
|
}
|
|
2030
2241
|
let resolvedValue;
|
|
2031
2242
|
if (typeof maybeValue === "function") {
|
|
@@ -2033,9 +2244,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2033
2244
|
const prev = current === void 0 ? state.schema.getDefaultAtPath(segments) : current;
|
|
2034
2245
|
resolvedValue = maybeValue(prev);
|
|
2035
2246
|
if (isUnset(resolvedValue)) {
|
|
2036
|
-
return state.setValueAtPath(
|
|
2037
|
-
|
|
2038
|
-
|
|
2247
|
+
return state.setValueAtPath(
|
|
2248
|
+
segments,
|
|
2249
|
+
state.schema.getDefaultAtPath(segments),
|
|
2250
|
+
withInstanceMeta({ blank: true })
|
|
2251
|
+
);
|
|
2039
2252
|
}
|
|
2040
2253
|
} else {
|
|
2041
2254
|
resolvedValue = maybeValue;
|
|
@@ -2045,28 +2258,52 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2045
2258
|
segments,
|
|
2046
2259
|
state.schema
|
|
2047
2260
|
);
|
|
2048
|
-
const ok = state.setValueAtPath(segments, walked.cleanedValues);
|
|
2261
|
+
const ok = state.setValueAtPath(segments, walked.cleanedValues, withInstanceMeta());
|
|
2049
2262
|
if (!ok) return false;
|
|
2050
2263
|
for (const pathKey of walked.paths) {
|
|
2051
2264
|
const blankSegments = paths.segmentsForPathKey(pathKey);
|
|
2052
2265
|
if (blankSegments === null) continue;
|
|
2053
|
-
state.setValueAtPath(
|
|
2054
|
-
|
|
2055
|
-
|
|
2266
|
+
state.setValueAtPath(
|
|
2267
|
+
blankSegments,
|
|
2268
|
+
state.schema.getDefaultAtPath(blankSegments),
|
|
2269
|
+
withInstanceMeta({ blank: true })
|
|
2270
|
+
);
|
|
2056
2271
|
}
|
|
2057
2272
|
return true;
|
|
2058
2273
|
}
|
|
2059
2274
|
const errorsProxy = buildErrorsProxy(state);
|
|
2275
|
+
function filterToOwnFormKey(errors, op) {
|
|
2276
|
+
const own = [];
|
|
2277
|
+
let dropped = 0;
|
|
2278
|
+
for (const e of errors) {
|
|
2279
|
+
if (e.formKey === state.formKey) own.push(e);
|
|
2280
|
+
else dropped++;
|
|
2281
|
+
}
|
|
2282
|
+
if (plugin.__DEV__ && dropped > 0) {
|
|
2283
|
+
console.warn(
|
|
2284
|
+
`[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.`
|
|
2285
|
+
);
|
|
2286
|
+
}
|
|
2287
|
+
return own;
|
|
2288
|
+
}
|
|
2060
2289
|
function setFieldErrors(errors) {
|
|
2061
|
-
state.
|
|
2290
|
+
const preserved = state.userErrors.get(paths.FORM_ERRORS_PATH_KEY);
|
|
2291
|
+
state.setAllUserErrors(filterToOwnFormKey(errors, "setFieldErrors"));
|
|
2292
|
+
if (preserved !== void 0 && preserved.length > 0) {
|
|
2293
|
+
state.userErrors.set(paths.FORM_ERRORS_PATH_KEY, preserved);
|
|
2294
|
+
}
|
|
2062
2295
|
}
|
|
2063
2296
|
function addFieldErrors(errors) {
|
|
2064
|
-
state.addUserErrors(errors);
|
|
2297
|
+
state.addUserErrors(filterToOwnFormKey(errors, "addFieldErrors"));
|
|
2065
2298
|
}
|
|
2066
2299
|
function clearFieldErrors(path) {
|
|
2067
2300
|
if (path === void 0) {
|
|
2301
|
+
const preserved = state.userErrors.get(paths.FORM_ERRORS_PATH_KEY);
|
|
2068
2302
|
state.clearSchemaErrors();
|
|
2069
2303
|
state.clearUserErrors();
|
|
2304
|
+
if (preserved !== void 0 && preserved.length > 0) {
|
|
2305
|
+
state.userErrors.set(paths.FORM_ERRORS_PATH_KEY, preserved);
|
|
2306
|
+
}
|
|
2070
2307
|
return;
|
|
2071
2308
|
}
|
|
2072
2309
|
const segments = paths.canonicalizePath(path).segments;
|
|
@@ -2075,13 +2312,13 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2075
2312
|
}
|
|
2076
2313
|
function setFormErrors(errors) {
|
|
2077
2314
|
if (errors.length === 0) {
|
|
2078
|
-
state.userErrors.delete(paths.
|
|
2315
|
+
state.userErrors.delete(paths.FORM_ERRORS_PATH_KEY);
|
|
2079
2316
|
return;
|
|
2080
2317
|
}
|
|
2081
2318
|
state.userErrors.set(
|
|
2082
|
-
paths.
|
|
2319
|
+
paths.FORM_ERRORS_PATH_KEY,
|
|
2083
2320
|
errors.map((e) => ({
|
|
2084
|
-
path: [],
|
|
2321
|
+
path: [...paths.FORM_ERRORS_PATH],
|
|
2085
2322
|
message: e.message,
|
|
2086
2323
|
formKey: state.formKey,
|
|
2087
2324
|
code: e.code ?? "atta:form-error"
|
|
@@ -2089,7 +2326,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2089
2326
|
);
|
|
2090
2327
|
}
|
|
2091
2328
|
function clearFormErrors() {
|
|
2092
|
-
state.userErrors.delete(paths.
|
|
2329
|
+
state.userErrors.delete(paths.FORM_ERRORS_PATH_KEY);
|
|
2093
2330
|
}
|
|
2094
2331
|
const submitting = vue.computed(() => state.submitting.value);
|
|
2095
2332
|
const submitCount = vue.computed(() => state.submitCount.value);
|
|
@@ -2099,15 +2336,36 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2099
2336
|
() => state.firstValidationDone.value && state.schemaErrors.size === 0 && state.userErrors.size === 0 && state.derivedBlankErrors.value.size === 0 && !validating.value
|
|
2100
2337
|
);
|
|
2101
2338
|
const history = options.history;
|
|
2102
|
-
const
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2339
|
+
const formHistory = vue.readonly(
|
|
2340
|
+
vue.reactive({
|
|
2341
|
+
undo: history?.undo ?? (() => false),
|
|
2342
|
+
redo: history?.redo ?? (() => false),
|
|
2343
|
+
clear: history?.clear ?? (() => {
|
|
2344
|
+
}),
|
|
2345
|
+
canUndo: history?.canUndo ?? vue.computed(() => false),
|
|
2346
|
+
canRedo: history?.canRedo ?? vue.computed(() => false),
|
|
2347
|
+
size: history?.historySize ?? vue.computed(() => 0)
|
|
2348
|
+
})
|
|
2349
|
+
);
|
|
2107
2350
|
const metaErrors = vue.computed(
|
|
2108
2351
|
() => aggregateErrorsAt(state, [])
|
|
2109
2352
|
);
|
|
2110
|
-
const
|
|
2353
|
+
const getFormMetaBase = () => {
|
|
2354
|
+
const rootBase = buildContainerFieldStateBase(state, paths.ROOT_PATH);
|
|
2355
|
+
return {
|
|
2356
|
+
...rootBase,
|
|
2357
|
+
submitting: state.submitting.value,
|
|
2358
|
+
submitCount: state.submitCount.value,
|
|
2359
|
+
submitError: state.submitError.value,
|
|
2360
|
+
instanceId: formInstanceId
|
|
2361
|
+
};
|
|
2362
|
+
};
|
|
2363
|
+
const fieldStateAccessorOptions = options.shouldShowErrors !== void 0 ? { shouldShowErrors: options.shouldShowErrors } : void 0;
|
|
2364
|
+
const getRootFieldStateAt = buildFieldStateAccessor(
|
|
2365
|
+
state,
|
|
2366
|
+
getFormMetaBase,
|
|
2367
|
+
fieldStateAccessorOptions
|
|
2368
|
+
);
|
|
2111
2369
|
const rootFieldState = getRootFieldStateAt([]);
|
|
2112
2370
|
const formMeta = vue.readonly(
|
|
2113
2371
|
vue.reactive({
|
|
@@ -2143,6 +2401,13 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2143
2401
|
// keep the explicit form-level computation for the gate.
|
|
2144
2402
|
valid,
|
|
2145
2403
|
errors: metaErrors,
|
|
2404
|
+
// `showErrors` / `firstError` flow through the same root
|
|
2405
|
+
// field-state computed as the rest of the FieldState surface,
|
|
2406
|
+
// so `form.meta.showErrors` matches `form.fields().showErrors`
|
|
2407
|
+
// exactly — the predicate runs once at the root and the result
|
|
2408
|
+
// is shared.
|
|
2409
|
+
showErrors: vue.computed(() => rootFieldState.value.showErrors),
|
|
2410
|
+
firstError: vue.computed(() => rootFieldState.value.firstError),
|
|
2146
2411
|
path: vue.computed(() => rootFieldState.value.path),
|
|
2147
2412
|
blank: vue.computed(() => rootFieldState.value.blank),
|
|
2148
2413
|
label: vue.computed(() => rootFieldState.value.label),
|
|
@@ -2153,9 +2418,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2153
2418
|
submitting,
|
|
2154
2419
|
submitCount,
|
|
2155
2420
|
submitError,
|
|
2156
|
-
canUndo,
|
|
2157
|
-
canRedo,
|
|
2158
|
-
historySize,
|
|
2159
2421
|
// Per-`useForm()`-call identity. Stable for one mount; new on
|
|
2160
2422
|
// re-mount; orthogonal to `form.key` (which is the user-supplied
|
|
2161
2423
|
// shared identifier). Useful for devtools panels disambiguating
|
|
@@ -2177,9 +2439,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2177
2439
|
for (const pathKey of walked.paths) {
|
|
2178
2440
|
const segments = paths.segmentsForPathKey(pathKey);
|
|
2179
2441
|
if (segments === null) continue;
|
|
2180
|
-
state.setValueAtPath(
|
|
2181
|
-
|
|
2182
|
-
|
|
2442
|
+
state.setValueAtPath(
|
|
2443
|
+
segments,
|
|
2444
|
+
state.schema.getDefaultAtPath(segments),
|
|
2445
|
+
withInstanceMeta({ blank: true })
|
|
2446
|
+
);
|
|
2183
2447
|
state.originalBlankPaths.add(pathKey);
|
|
2184
2448
|
}
|
|
2185
2449
|
}
|
|
@@ -2196,7 +2460,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2196
2460
|
};
|
|
2197
2461
|
const persist = async (pathInput, options2) => {
|
|
2198
2462
|
const segments = paths.canonicalizePath(pathInput).segments;
|
|
2199
|
-
plugin.enforceSensitiveCheck(segments, options2?.acknowledgeSensitive === true);
|
|
2463
|
+
plugin.enforceSensitiveCheck(segments, options2?.acknowledgeSensitive === true, state.isSensitivePath);
|
|
2200
2464
|
if (persistence === void 0) return;
|
|
2201
2465
|
await persistence.writePathImmediately(segments);
|
|
2202
2466
|
};
|
|
@@ -2209,6 +2473,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2209
2473
|
const segments = paths.canonicalizePath(pathInput).segments;
|
|
2210
2474
|
await persistence.clearPersistedDraft(segments);
|
|
2211
2475
|
};
|
|
2476
|
+
function touch(pathInput) {
|
|
2477
|
+
const segments = pathInput === void 0 ? paths.ROOT_PATH : paths.canonicalizePath(pathInput).segments;
|
|
2478
|
+
state.touchAtPath(segments);
|
|
2479
|
+
}
|
|
2212
2480
|
const focusFirstError = (options2) => {
|
|
2213
2481
|
const target = state.getFirstErrorElement(formInstanceId);
|
|
2214
2482
|
if (target === null) return false;
|
|
@@ -2226,7 +2494,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2226
2494
|
return readonlySetSnapshot(state.blankPaths);
|
|
2227
2495
|
});
|
|
2228
2496
|
const valuesProxy = buildValuesProxy(state.form);
|
|
2229
|
-
const fieldStateProxy = buildFieldStateProxy(state);
|
|
2497
|
+
const fieldStateProxy = buildFieldStateProxy(state, getFormMetaBase, fieldStateAccessorOptions);
|
|
2230
2498
|
return {
|
|
2231
2499
|
handleSubmit,
|
|
2232
2500
|
// `values` is the callable readonly Proxy. Each `get` trap reads
|
|
@@ -2239,6 +2507,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2239
2507
|
setValue: setValueImpl,
|
|
2240
2508
|
validate,
|
|
2241
2509
|
validateAsync,
|
|
2510
|
+
process,
|
|
2242
2511
|
register,
|
|
2243
2512
|
key: state.formKey,
|
|
2244
2513
|
errors: errorsProxy,
|
|
@@ -2255,8 +2524,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2255
2524
|
clearPersistedDraft,
|
|
2256
2525
|
focusFirstError,
|
|
2257
2526
|
scrollToFirstError,
|
|
2258
|
-
|
|
2259
|
-
|
|
2527
|
+
touch,
|
|
2528
|
+
history: formHistory,
|
|
2260
2529
|
append: fieldArrays.append,
|
|
2261
2530
|
prepend: fieldArrays.prepend,
|
|
2262
2531
|
insert: fieldArrays.insert,
|
|
@@ -2268,6 +2537,16 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
2268
2537
|
};
|
|
2269
2538
|
}
|
|
2270
2539
|
|
|
2540
|
+
const defaultShouldShowErrors = (field, formMeta) => formMeta.submitCount > 0 || field.touched === true && field.dirty;
|
|
2541
|
+
const SHOW_ALWAYS = () => true;
|
|
2542
|
+
const SHOW_NEVER = () => false;
|
|
2543
|
+
function resolveShouldShowErrors(config) {
|
|
2544
|
+
if (config === void 0) return defaultShouldShowErrors;
|
|
2545
|
+
if (config === true) return SHOW_ALWAYS;
|
|
2546
|
+
if (config === false) return SHOW_NEVER;
|
|
2547
|
+
return config;
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2271
2550
|
function isHydratedFieldRecord(value) {
|
|
2272
2551
|
if (typeof value !== "object" || value === null) return false;
|
|
2273
2552
|
const r = value;
|
|
@@ -2291,6 +2570,42 @@ function warnMalformedHydration(formKey, kind, rawKey) {
|
|
|
2291
2570
|
`[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).`
|
|
2292
2571
|
);
|
|
2293
2572
|
}
|
|
2573
|
+
function applyDuStubs(schema, data, options = {}) {
|
|
2574
|
+
const warned = options.warn === true ? /* @__PURE__ */ new Set() : void 0;
|
|
2575
|
+
return walkDuStubs(schema, data, options.basePath ?? [], warned);
|
|
2576
|
+
}
|
|
2577
|
+
function walkDuStubs(schema, value, path, warned) {
|
|
2578
|
+
if (value === null || value === void 0 || typeof value !== "object") return value;
|
|
2579
|
+
if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || typeof value === "function") {
|
|
2580
|
+
return value;
|
|
2581
|
+
}
|
|
2582
|
+
if (Array.isArray(value)) {
|
|
2583
|
+
return value.map((item, i) => walkDuStubs(schema, item, [...path, i], warned));
|
|
2584
|
+
}
|
|
2585
|
+
const rec = value;
|
|
2586
|
+
const du = schema.getUnionDiscriminatorAtPath(path);
|
|
2587
|
+
if (du !== void 0) {
|
|
2588
|
+
const discValue = rec[du.discriminatorKey];
|
|
2589
|
+
if (discValue !== void 0 && !du.isVariantSelected(discValue)) {
|
|
2590
|
+
if (warned !== void 0 && plugin.__DEV__) {
|
|
2591
|
+
const dotted = path.map((s) => String(s)).join(".") || "(root)";
|
|
2592
|
+
const key = `${dotted}::${String(discValue)}`;
|
|
2593
|
+
if (!warned.has(key)) {
|
|
2594
|
+
warned.add(key);
|
|
2595
|
+
console.warn(
|
|
2596
|
+
`[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.`
|
|
2597
|
+
);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
return { [du.discriminatorKey]: discValue };
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
const out = {};
|
|
2604
|
+
for (const k of Object.keys(rec)) {
|
|
2605
|
+
out[k] = walkDuStubs(schema, rec[k], [...path, k], warned);
|
|
2606
|
+
}
|
|
2607
|
+
return out;
|
|
2608
|
+
}
|
|
2294
2609
|
function isPathKeyUnder(existingKey, parentPath) {
|
|
2295
2610
|
const parsed = paths.segmentsForPathKey(existingKey);
|
|
2296
2611
|
if (parsed === null) return false;
|
|
@@ -2300,18 +2615,96 @@ function isPathKeyUnder(existingKey, parentPath) {
|
|
|
2300
2615
|
}
|
|
2301
2616
|
return true;
|
|
2302
2617
|
}
|
|
2618
|
+
function stripSymbolsDeep(value) {
|
|
2619
|
+
if (value === null || typeof value !== "object") return value;
|
|
2620
|
+
if (Array.isArray(value)) {
|
|
2621
|
+
let mutated2 = false;
|
|
2622
|
+
const out2 = new Array(value.length);
|
|
2623
|
+
for (let i = 0; i < value.length; i++) {
|
|
2624
|
+
const cleaned = stripSymbolsDeep(value[i]);
|
|
2625
|
+
out2[i] = cleaned;
|
|
2626
|
+
if (cleaned !== value[i]) mutated2 = true;
|
|
2627
|
+
}
|
|
2628
|
+
return mutated2 ? out2 : value;
|
|
2629
|
+
}
|
|
2630
|
+
const proto = Object.getPrototypeOf(value);
|
|
2631
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
2632
|
+
const symKeys = Object.getOwnPropertySymbols(value);
|
|
2633
|
+
const stringKeys = Object.keys(value);
|
|
2634
|
+
let mutated = symKeys.length > 0;
|
|
2635
|
+
const out = {};
|
|
2636
|
+
const src = value;
|
|
2637
|
+
for (const k of stringKeys) {
|
|
2638
|
+
const cleaned = stripSymbolsDeep(src[k]);
|
|
2639
|
+
out[k] = cleaned;
|
|
2640
|
+
if (cleaned !== src[k]) mutated = true;
|
|
2641
|
+
}
|
|
2642
|
+
return mutated ? out : value;
|
|
2643
|
+
}
|
|
2644
|
+
function cloneVariantSnapshot(value) {
|
|
2645
|
+
if (value === null || typeof value !== "object") return value;
|
|
2646
|
+
const raw = vue.toRaw(value);
|
|
2647
|
+
if (raw instanceof Date) return new Date(raw.getTime());
|
|
2648
|
+
if (raw instanceof Map) {
|
|
2649
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
2650
|
+
for (const [k, v] of raw.entries()) out2.set(cloneVariantSnapshot(k), cloneVariantSnapshot(v));
|
|
2651
|
+
return out2;
|
|
2652
|
+
}
|
|
2653
|
+
if (raw instanceof Set) {
|
|
2654
|
+
const out2 = /* @__PURE__ */ new Set();
|
|
2655
|
+
for (const v of raw) out2.add(cloneVariantSnapshot(v));
|
|
2656
|
+
return out2;
|
|
2657
|
+
}
|
|
2658
|
+
if (raw instanceof RegExp) return new RegExp(raw.source, raw.flags);
|
|
2659
|
+
if (Array.isArray(raw)) {
|
|
2660
|
+
const out2 = new Array(raw.length);
|
|
2661
|
+
for (let i = 0; i < raw.length; i++) out2[i] = cloneVariantSnapshot(raw[i]);
|
|
2662
|
+
return out2;
|
|
2663
|
+
}
|
|
2664
|
+
const src = raw;
|
|
2665
|
+
const out = {};
|
|
2666
|
+
for (const k of Object.keys(src)) out[k] = cloneVariantSnapshot(src[k]);
|
|
2667
|
+
return out;
|
|
2668
|
+
}
|
|
2303
2669
|
function createFormStore(options) {
|
|
2304
2670
|
const { formKey, schema, defaultValues, strict = true, hydration } = options;
|
|
2305
2671
|
const ssr = options.ssr === true;
|
|
2306
2672
|
const rememberVariants = options.rememberVariants !== false;
|
|
2307
2673
|
const fieldValidationMode = options.validateOn ?? "change";
|
|
2308
|
-
const fieldValidationDebounceMs =
|
|
2674
|
+
const fieldValidationDebounceMs = normalizeNumericOption({
|
|
2675
|
+
value: options.debounceMs ?? DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS,
|
|
2676
|
+
source: "useForm.debounceMs",
|
|
2677
|
+
allowInfinity: false,
|
|
2678
|
+
min: 0,
|
|
2679
|
+
defaultValue: DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS
|
|
2680
|
+
});
|
|
2309
2681
|
const fieldValidationState = /* @__PURE__ */ new Map();
|
|
2310
2682
|
const formChangeListeners = /* @__PURE__ */ new Set();
|
|
2311
2683
|
const submitSuccessListeners = /* @__PURE__ */ new Set();
|
|
2312
2684
|
const resetListeners = /* @__PURE__ */ new Set();
|
|
2313
2685
|
const persistOptIns = plugin.createPersistOptInRegistry();
|
|
2686
|
+
const noSyncPaths = /* @__PURE__ */ new Set();
|
|
2687
|
+
const noSyncPathCounts = /* @__PURE__ */ new Map();
|
|
2688
|
+
function incrementNoSyncOptOut(path) {
|
|
2689
|
+
const next = (noSyncPathCounts.get(path) ?? 0) + 1;
|
|
2690
|
+
noSyncPathCounts.set(path, next);
|
|
2691
|
+
if (next === 1) noSyncPaths.add(path);
|
|
2692
|
+
}
|
|
2693
|
+
function decrementNoSyncOptOut(path) {
|
|
2694
|
+
const current = noSyncPathCounts.get(path) ?? 0;
|
|
2695
|
+
if (current <= 1) {
|
|
2696
|
+
noSyncPathCounts.delete(path);
|
|
2697
|
+
noSyncPaths.delete(path);
|
|
2698
|
+
return;
|
|
2699
|
+
}
|
|
2700
|
+
noSyncPathCounts.set(path, current - 1);
|
|
2701
|
+
}
|
|
2314
2702
|
const coerceIndex = resolveCoercionIndex(options.coerce);
|
|
2703
|
+
const resolvedShouldShowErrors = resolveShouldShowErrors(
|
|
2704
|
+
options.shouldShowErrors
|
|
2705
|
+
);
|
|
2706
|
+
const resolvedIsSensitivePath = options.isSensitivePath ?? plugin.isSensitivePath;
|
|
2707
|
+
const resolvedSegmentMatchesSensitive = options.segmentMatchesSensitive ?? plugin.segmentMatchesSensitive;
|
|
2315
2708
|
const cleanupHooks = [];
|
|
2316
2709
|
const modules = /* @__PURE__ */ new Map();
|
|
2317
2710
|
const completedConstraints = defaultValues === void 0 ? void 0 : mergeStructural(schema, [], defaultValues);
|
|
@@ -2322,7 +2715,10 @@ function createFormStore(options) {
|
|
|
2322
2715
|
});
|
|
2323
2716
|
const schemaInitialData = schemaResponse.data;
|
|
2324
2717
|
const initialData = hydration !== void 0 ? hydration.form : structuralSnapshot(schemaInitialData);
|
|
2325
|
-
const
|
|
2718
|
+
const stubbedInitialData = applyDuStubs(schema, initialData, {
|
|
2719
|
+
warn: true
|
|
2720
|
+
});
|
|
2721
|
+
const form = vue.ref(stubbedInitialData);
|
|
2326
2722
|
const fields = vue.reactive(/* @__PURE__ */ new Map());
|
|
2327
2723
|
const elements = vue.reactive(/* @__PURE__ */ new Map());
|
|
2328
2724
|
const elementToFormInstance = /* @__PURE__ */ new WeakMap();
|
|
@@ -2338,6 +2734,40 @@ function createFormStore(options) {
|
|
|
2338
2734
|
originalBlankPaths.add(raw);
|
|
2339
2735
|
}
|
|
2340
2736
|
const variantMemory = /* @__PURE__ */ new Map();
|
|
2737
|
+
function clearVariantMemoryUnderPath(arrayPath) {
|
|
2738
|
+
for (const memKey of [...variantMemory.keys()]) {
|
|
2739
|
+
const segs = paths.segmentsForPathKey(memKey);
|
|
2740
|
+
if (segs === null) continue;
|
|
2741
|
+
if (isPathPrefix(arrayPath, segs)) variantMemory.delete(memKey);
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
function clearVariantMemoryAtArrayIndices(arrayPath, indexFilter) {
|
|
2745
|
+
for (const memKey of [...variantMemory.keys()]) {
|
|
2746
|
+
const segs = paths.segmentsForPathKey(memKey);
|
|
2747
|
+
if (segs === null) continue;
|
|
2748
|
+
if (!isPathPrefix(arrayPath, segs)) continue;
|
|
2749
|
+
if (segs.length <= arrayPath.length) continue;
|
|
2750
|
+
const idxSeg = segs[arrayPath.length];
|
|
2751
|
+
if (typeof idxSeg !== "number") continue;
|
|
2752
|
+
if (indexFilter(idxSeg)) variantMemory.delete(memKey);
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
function applyArrayOpToMemory(arrayPath, op) {
|
|
2756
|
+
switch (op.kind) {
|
|
2757
|
+
case "shift-from":
|
|
2758
|
+
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i >= op.index);
|
|
2759
|
+
return;
|
|
2760
|
+
case "shift-range":
|
|
2761
|
+
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i >= op.fromIndex && i <= op.toIndex);
|
|
2762
|
+
return;
|
|
2763
|
+
case "swap":
|
|
2764
|
+
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i === op.a || i === op.b);
|
|
2765
|
+
return;
|
|
2766
|
+
case "replace-at":
|
|
2767
|
+
clearVariantMemoryAtArrayIndices(arrayPath, (i) => i === op.index);
|
|
2768
|
+
return;
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2341
2771
|
const pathOrdinals = /* @__PURE__ */ new Map();
|
|
2342
2772
|
let nextOrdinal = 0;
|
|
2343
2773
|
function ensurePathOrdinal(key) {
|
|
@@ -2489,9 +2919,35 @@ function createFormStore(options) {
|
|
|
2489
2919
|
}
|
|
2490
2920
|
}
|
|
2491
2921
|
function setValueAtPath(path, value, meta) {
|
|
2922
|
+
value = stripSymbolsDeep(value);
|
|
2923
|
+
value = schema.normalizeWriteValueAtPath(value, path);
|
|
2492
2924
|
if (!isSlimPrimitiveValid(schema, form, path, value)) {
|
|
2493
2925
|
return false;
|
|
2494
2926
|
}
|
|
2927
|
+
if (path.length >= 2) {
|
|
2928
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
2929
|
+
const ancestorPath = path.slice(0, i + 1);
|
|
2930
|
+
const du = schema.getUnionDiscriminatorAtPath(ancestorPath);
|
|
2931
|
+
if (du === void 0) continue;
|
|
2932
|
+
const nextSeg = path[i + 1];
|
|
2933
|
+
if (nextSeg === du.discriminatorKey) continue;
|
|
2934
|
+
const ancestorValue = getAtPath(form.value, ancestorPath);
|
|
2935
|
+
if (!isPlainRecord(ancestorValue)) continue;
|
|
2936
|
+
const discValue = ancestorValue[du.discriminatorKey];
|
|
2937
|
+
if (discValue === void 0) {
|
|
2938
|
+
return false;
|
|
2939
|
+
}
|
|
2940
|
+
if (!du.isVariantSelected(discValue)) {
|
|
2941
|
+
return false;
|
|
2942
|
+
}
|
|
2943
|
+
const variantDefault = du.getVariantDefault(discValue);
|
|
2944
|
+
if (!isPlainRecord(variantDefault)) continue;
|
|
2945
|
+
if (typeof nextSeg !== "string") continue;
|
|
2946
|
+
if (!(nextSeg in variantDefault)) {
|
|
2947
|
+
return false;
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2495
2951
|
if (meta?.skipDiscriminatorReshape !== true) {
|
|
2496
2952
|
if (path.length > 0) {
|
|
2497
2953
|
const last = path[path.length - 1];
|
|
@@ -2512,6 +2968,14 @@ function createFormStore(options) {
|
|
|
2512
2968
|
meta
|
|
2513
2969
|
);
|
|
2514
2970
|
}
|
|
2971
|
+
return reshapeUnionVariant(
|
|
2972
|
+
parentPath,
|
|
2973
|
+
oldValue,
|
|
2974
|
+
value,
|
|
2975
|
+
{ [last]: value },
|
|
2976
|
+
void 0,
|
|
2977
|
+
meta
|
|
2978
|
+
);
|
|
2515
2979
|
}
|
|
2516
2980
|
}
|
|
2517
2981
|
}
|
|
@@ -2520,12 +2984,13 @@ function createFormStore(options) {
|
|
|
2520
2984
|
const selfDU = schema.getUnionDiscriminatorAtPath(path);
|
|
2521
2985
|
if (selfDU !== void 0) {
|
|
2522
2986
|
const valueRecord = value;
|
|
2523
|
-
const
|
|
2987
|
+
const discKey = selfDU.discriminatorKey;
|
|
2988
|
+
const discValue = valueRecord[discKey];
|
|
2989
|
+
const currentUnionValue = getAtPath(form.value, path);
|
|
2990
|
+
const oldDiscValue = isPlainRecord(currentUnionValue) ? currentUnionValue[discKey] : void 0;
|
|
2524
2991
|
if (discValue !== void 0) {
|
|
2525
2992
|
const variantDefault = selfDU.getVariantDefault(discValue);
|
|
2526
2993
|
if (variantDefault !== void 0 && isPlainRecord(variantDefault)) {
|
|
2527
|
-
const currentUnionValue = getAtPath(form.value, path);
|
|
2528
|
-
const oldDiscValue = isPlainRecord(currentUnionValue) ? currentUnionValue[selfDU.discriminatorKey] : void 0;
|
|
2529
2994
|
return reshapeUnionVariant(
|
|
2530
2995
|
path,
|
|
2531
2996
|
oldDiscValue,
|
|
@@ -2535,7 +3000,16 @@ function createFormStore(options) {
|
|
|
2535
3000
|
meta
|
|
2536
3001
|
);
|
|
2537
3002
|
}
|
|
3003
|
+
return reshapeUnionVariant(
|
|
3004
|
+
path,
|
|
3005
|
+
oldDiscValue,
|
|
3006
|
+
discValue,
|
|
3007
|
+
{ [discKey]: discValue },
|
|
3008
|
+
void 0,
|
|
3009
|
+
meta
|
|
3010
|
+
);
|
|
2538
3011
|
}
|
|
3012
|
+
return reshapeUnionVariant(path, oldDiscValue, void 0, {}, void 0, meta);
|
|
2539
3013
|
}
|
|
2540
3014
|
}
|
|
2541
3015
|
}
|
|
@@ -2552,12 +3026,17 @@ function createFormStore(options) {
|
|
|
2552
3026
|
}
|
|
2553
3027
|
const nextForm = setAtPathWithSchemaFill(form.value, schema, path, completedValue);
|
|
2554
3028
|
applyFormReplacement(nextForm, meta);
|
|
2555
|
-
if (
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
3029
|
+
if (meta?.arrayOp !== void 0) {
|
|
3030
|
+
applyArrayOpToMemory(path, meta.arrayOp);
|
|
3031
|
+
} else if (Array.isArray(value) && Array.isArray(currentValue)) {
|
|
3032
|
+
clearVariantMemoryUnderPath(path);
|
|
3033
|
+
}
|
|
3034
|
+
const effectiveModeAfterWrite = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
3035
|
+
if (effectiveModeAfterWrite === "change") {
|
|
3036
|
+
scheduleFieldValidation(path, false, {
|
|
3037
|
+
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
3038
|
+
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
3039
|
+
});
|
|
2561
3040
|
}
|
|
2562
3041
|
return true;
|
|
2563
3042
|
}
|
|
@@ -2566,9 +3045,10 @@ function createFormStore(options) {
|
|
|
2566
3045
|
const parentKey = paths.canonicalizePath(parentPath).key;
|
|
2567
3046
|
let baseline = variantDefault;
|
|
2568
3047
|
let restoredBlanks;
|
|
2569
|
-
|
|
3048
|
+
const effectiveRemember = meta?.instance?.rememberVariants ?? rememberVariants;
|
|
3049
|
+
if (effectiveRemember && !sameDisc) {
|
|
2570
3050
|
if (oldDiscValue !== void 0) {
|
|
2571
|
-
const currentValue2 =
|
|
3051
|
+
const currentValue2 = cloneVariantSnapshot(getAtPath(form.value, parentPath));
|
|
2572
3052
|
const outgoingBlanks = [];
|
|
2573
3053
|
for (const k of blankPaths) {
|
|
2574
3054
|
if (isPathKeyUnder(k, parentPath)) outgoingBlanks.push(k);
|
|
@@ -2590,7 +3070,10 @@ function createFormStore(options) {
|
|
|
2590
3070
|
restoredBlanks = [...restored.blankPaths];
|
|
2591
3071
|
}
|
|
2592
3072
|
}
|
|
2593
|
-
const
|
|
3073
|
+
const layered = consumerOverrides !== void 0 ? { ...baseline, ...consumerOverrides } : baseline;
|
|
3074
|
+
const finalValue = applyDuStubs(schema, layered, {
|
|
3075
|
+
basePath: parentPath
|
|
3076
|
+
});
|
|
2594
3077
|
let newBlankPaths;
|
|
2595
3078
|
if (restoredBlanks !== void 0) {
|
|
2596
3079
|
newBlankPaths = restoredBlanks;
|
|
@@ -2609,9 +3092,10 @@ function createFormStore(options) {
|
|
|
2609
3092
|
for (const k of newBlankPaths) blankPaths.add(k);
|
|
2610
3093
|
return true;
|
|
2611
3094
|
}
|
|
2612
|
-
const nextForm = parentPath.length === 0 ? finalValue :
|
|
3095
|
+
const nextForm = parentPath.length === 0 ? finalValue : setAtPathWithSchemaFill(form.value, schema, parentPath, finalValue);
|
|
2613
3096
|
let appliedSync = false;
|
|
2614
|
-
|
|
3097
|
+
const reshapeMode = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
3098
|
+
if (reshapeMode === "change") {
|
|
2615
3099
|
const syncOrPromise = schema.validateAtPath(finalValue, parentPath, { sync: true });
|
|
2616
3100
|
if (!(syncOrPromise instanceof Promise)) {
|
|
2617
3101
|
const reStamped = syncOrPromise.success ? [] : syncOrPromise.errors.map((err) => ({
|
|
@@ -2631,17 +3115,18 @@ function createFormStore(options) {
|
|
|
2631
3115
|
}
|
|
2632
3116
|
applyFormReplacement(nextForm, meta);
|
|
2633
3117
|
for (const k of newBlankPaths) blankPaths.add(k);
|
|
2634
|
-
if (
|
|
2635
|
-
scheduleFieldValidation(
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
);
|
|
3118
|
+
if (reshapeMode === "change" && !appliedSync) {
|
|
3119
|
+
scheduleFieldValidation(parentPath, false, {
|
|
3120
|
+
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
3121
|
+
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
3122
|
+
});
|
|
2640
3123
|
}
|
|
2641
3124
|
return true;
|
|
2642
3125
|
}
|
|
2643
|
-
function scheduleFieldValidation(path, immediate) {
|
|
2644
|
-
|
|
3126
|
+
function scheduleFieldValidation(path, immediate, override) {
|
|
3127
|
+
const effectiveMode = override?.mode ?? fieldValidationMode;
|
|
3128
|
+
if (effectiveMode === "submit") return;
|
|
3129
|
+
const effectiveDebounce = override?.debounceMs ?? fieldValidationDebounceMs;
|
|
2645
3130
|
const { key } = paths.canonicalizePath(path);
|
|
2646
3131
|
const prev = fieldValidationState.get(key);
|
|
2647
3132
|
if (prev !== void 0) {
|
|
@@ -2655,8 +3140,17 @@ function createFormStore(options) {
|
|
|
2655
3140
|
fresh.timer = null;
|
|
2656
3141
|
if (controller.signal.aborted) return;
|
|
2657
3142
|
const data = getAtPath(form.value, path);
|
|
2658
|
-
|
|
2659
|
-
|
|
3143
|
+
let activeIncremented = false;
|
|
3144
|
+
try {
|
|
3145
|
+
activeValidations.value += 1;
|
|
3146
|
+
activeIncremented = true;
|
|
3147
|
+
incFieldValidation(key);
|
|
3148
|
+
} catch (err) {
|
|
3149
|
+
if (activeIncremented) {
|
|
3150
|
+
activeValidations.value = Math.max(0, activeValidations.value - 1);
|
|
3151
|
+
}
|
|
3152
|
+
throw err;
|
|
3153
|
+
}
|
|
2660
3154
|
void Promise.resolve().then(() => schema.validateAtPath(data, path)).then((response) => {
|
|
2661
3155
|
if (controller.signal.aborted) return;
|
|
2662
3156
|
const reStamped = response.success ? [] : response.errors.map((err) => ({
|
|
@@ -2670,10 +3164,10 @@ function createFormStore(options) {
|
|
|
2670
3164
|
decFieldValidation(key);
|
|
2671
3165
|
});
|
|
2672
3166
|
};
|
|
2673
|
-
if (immediate ||
|
|
3167
|
+
if (immediate || effectiveDebounce === 0) {
|
|
2674
3168
|
run();
|
|
2675
3169
|
} else {
|
|
2676
|
-
fresh.timer = setTimeout(run,
|
|
3170
|
+
fresh.timer = setTimeout(run, effectiveDebounce);
|
|
2677
3171
|
}
|
|
2678
3172
|
}
|
|
2679
3173
|
function cancelFieldValidation() {
|
|
@@ -2737,13 +3231,26 @@ function createFormStore(options) {
|
|
|
2737
3231
|
submitSuccessListeners.clear();
|
|
2738
3232
|
resetListeners.clear();
|
|
2739
3233
|
persistOptIns.clear();
|
|
3234
|
+
noSyncPaths.clear();
|
|
3235
|
+
noSyncPathCounts.clear();
|
|
2740
3236
|
}
|
|
2741
3237
|
function getValueAtPath(path) {
|
|
2742
3238
|
return getAtPath(form.value, path);
|
|
2743
3239
|
}
|
|
3240
|
+
function rerouteFormLevelEntry(err) {
|
|
3241
|
+
if (err.path.length === 0) {
|
|
3242
|
+
return { ...err, path: [...paths.FORM_ERRORS_PATH] };
|
|
3243
|
+
}
|
|
3244
|
+
return err;
|
|
3245
|
+
}
|
|
3246
|
+
function pathKeyForEntry(err) {
|
|
3247
|
+
if (err.path.length === 0) return paths.FORM_ERRORS_PATH_KEY;
|
|
3248
|
+
return paths.canonicalizePath(err.path).key;
|
|
3249
|
+
}
|
|
2744
3250
|
function appendErrorsTo(map, entries) {
|
|
2745
|
-
for (const
|
|
2746
|
-
const
|
|
3251
|
+
for (const raw of entries) {
|
|
3252
|
+
const err = rerouteFormLevelEntry(raw);
|
|
3253
|
+
const key = pathKeyForEntry(err);
|
|
2747
3254
|
const current = map.get(key);
|
|
2748
3255
|
if (current === void 0) {
|
|
2749
3256
|
map.set(key, [err]);
|
|
@@ -2773,16 +3280,18 @@ function createFormStore(options) {
|
|
|
2773
3280
|
schemaErrors.set(key, [...entries]);
|
|
2774
3281
|
}
|
|
2775
3282
|
function applySchemaErrorsForSubtree(path, entries) {
|
|
2776
|
-
const
|
|
3283
|
+
const parentKey = path.length === 0 ? paths.FORM_ERRORS_PATH_KEY : paths.canonicalizePath(path).key;
|
|
2777
3284
|
const grouped = /* @__PURE__ */ new Map();
|
|
2778
|
-
for (const
|
|
2779
|
-
const
|
|
3285
|
+
for (const raw of entries) {
|
|
3286
|
+
const err = rerouteFormLevelEntry(raw);
|
|
3287
|
+
const key = pathKeyForEntry(err);
|
|
2780
3288
|
const list = grouped.get(key);
|
|
2781
3289
|
if (list === void 0) grouped.set(key, [err]);
|
|
2782
3290
|
else list.push(err);
|
|
2783
3291
|
}
|
|
2784
3292
|
if (!grouped.has(parentKey)) schemaErrors.delete(parentKey);
|
|
2785
3293
|
for (const existingKey of [...schemaErrors.keys()]) {
|
|
3294
|
+
if (existingKey === parentKey) continue;
|
|
2786
3295
|
if (isPathKeyUnder(existingKey, path) && !grouped.has(existingKey)) {
|
|
2787
3296
|
schemaErrors.delete(existingKey);
|
|
2788
3297
|
}
|
|
@@ -2858,7 +3367,7 @@ function createFormStore(options) {
|
|
|
2858
3367
|
if (current?.connected === true) return;
|
|
2859
3368
|
touchFieldRecord(key, path, { connected: true });
|
|
2860
3369
|
}
|
|
2861
|
-
function markFocused(path, focused) {
|
|
3370
|
+
function markFocused(path, focused, meta) {
|
|
2862
3371
|
const { key } = paths.canonicalizePath(path);
|
|
2863
3372
|
touchFieldRecord(key, path, {
|
|
2864
3373
|
focused,
|
|
@@ -2867,24 +3376,45 @@ function createFormStore(options) {
|
|
|
2867
3376
|
// a field is currently focused we keep whatever value it held.
|
|
2868
3377
|
touched: focused ? fields.get(key)?.touched ?? null : true
|
|
2869
3378
|
});
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
);
|
|
3379
|
+
const focusMode = meta?.instance?.validateOn ?? fieldValidationMode;
|
|
3380
|
+
if (!focused && focusMode === "blur") {
|
|
3381
|
+
scheduleFieldValidation(path, true, {
|
|
3382
|
+
...meta?.instance?.validateOn !== void 0 ? { mode: meta.instance.validateOn } : {},
|
|
3383
|
+
...meta?.instance?.debounceMs !== void 0 ? { debounceMs: meta.instance.debounceMs } : {}
|
|
3384
|
+
});
|
|
2876
3385
|
}
|
|
2877
3386
|
}
|
|
2878
3387
|
function markTouched(path) {
|
|
2879
3388
|
const { key } = paths.canonicalizePath(path);
|
|
2880
3389
|
touchFieldRecord(key, path, { touched: true });
|
|
2881
3390
|
}
|
|
3391
|
+
function touchAtPath(segments) {
|
|
3392
|
+
const formValue = form.value;
|
|
3393
|
+
let touchedAny = false;
|
|
3394
|
+
for (const [, entry] of originals) {
|
|
3395
|
+
if (!isPathPrefix(segments, entry.segments)) continue;
|
|
3396
|
+
if (!hasAtPath(formValue, entry.segments)) continue;
|
|
3397
|
+
touchedAny = true;
|
|
3398
|
+
const leafKey = paths.canonicalizePath(entry.segments).key;
|
|
3399
|
+
const current = fields.get(leafKey);
|
|
3400
|
+
if (current?.touched === true) continue;
|
|
3401
|
+
touchFieldRecord(leafKey, entry.segments, { touched: true });
|
|
3402
|
+
}
|
|
3403
|
+
if (!touchedAny && plugin.__DEV__) {
|
|
3404
|
+
console.warn(
|
|
3405
|
+
`[attaform] form.touch(): no fields resolved at path ${JSON.stringify(segments)}. Check the path matches an existing field or container.`
|
|
3406
|
+
);
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
2882
3409
|
function reset(nextDefaultValues) {
|
|
2883
|
-
const
|
|
3410
|
+
const resetSource = nextDefaultValues ?? defaultValues;
|
|
3411
|
+
const completedResetConstraints = resetSource === void 0 ? void 0 : mergeStructural(schema, [], resetSource);
|
|
3412
|
+
const resetResponse = schema.getDefaultValues({
|
|
2884
3413
|
useDefaultSchemaValues: true,
|
|
2885
|
-
constraints:
|
|
3414
|
+
constraints: completedResetConstraints,
|
|
2886
3415
|
strict
|
|
2887
|
-
})
|
|
3416
|
+
});
|
|
3417
|
+
const next = resetResponse.data;
|
|
2888
3418
|
applyFormReplacement(next);
|
|
2889
3419
|
originals.clear();
|
|
2890
3420
|
diffAndApply({}, next, [], (patch) => {
|
|
@@ -2903,6 +3433,24 @@ function createFormStore(options) {
|
|
|
2903
3433
|
}
|
|
2904
3434
|
schemaErrors.clear();
|
|
2905
3435
|
userErrors.clear();
|
|
3436
|
+
if (strict && !resetResponse.success) {
|
|
3437
|
+
setAllSchemaErrors(resetResponse.errors);
|
|
3438
|
+
}
|
|
3439
|
+
if (strict) {
|
|
3440
|
+
const syncResult = schema.validateAtPath(form.value, void 0, { sync: true });
|
|
3441
|
+
if (!(syncResult instanceof Promise) && !syncResult.success) {
|
|
3442
|
+
applySchemaErrorsForSubtree([], syncResult.errors);
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
firstValidationDone.value = !strict || schema.needsAsyncValidation?.() !== true;
|
|
3446
|
+
const needsAsync = !ssr && strict && schema.needsAsyncValidation?.() === true;
|
|
3447
|
+
if (needsAsync) {
|
|
3448
|
+
queueMicrotask(() => scheduleFieldValidation(
|
|
3449
|
+
[],
|
|
3450
|
+
true
|
|
3451
|
+
/* immediate */
|
|
3452
|
+
));
|
|
3453
|
+
}
|
|
2906
3454
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2907
3455
|
for (const [pathKey, record] of fields) {
|
|
2908
3456
|
fields.set(pathKey, {
|
|
@@ -2946,30 +3494,28 @@ function createFormStore(options) {
|
|
|
2946
3494
|
`[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.`
|
|
2947
3495
|
);
|
|
2948
3496
|
}
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
`[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.`
|
|
2972
|
-
);
|
|
3497
|
+
} else {
|
|
3498
|
+
let subtree = void 0;
|
|
3499
|
+
let anyMatch = false;
|
|
3500
|
+
for (const [, entry] of originals) {
|
|
3501
|
+
const leafSegments = entry.segments;
|
|
3502
|
+
if (!isPathPrefix(targetSegments, leafSegments)) continue;
|
|
3503
|
+
if (leafSegments.length === targetSegments.length) continue;
|
|
3504
|
+
anyMatch = true;
|
|
3505
|
+
const relative = leafSegments.slice(targetSegments.length);
|
|
3506
|
+
if (subtree === void 0) {
|
|
3507
|
+
subtree = typeof relative[0] === "number" ? [] : {};
|
|
3508
|
+
}
|
|
3509
|
+
subtree = setAtPath(subtree, relative, entry.value);
|
|
3510
|
+
}
|
|
3511
|
+
if (anyMatch) {
|
|
3512
|
+
const wroteSubtree = setValueAtPath(targetSegments, subtree);
|
|
3513
|
+
if (!wroteSubtree) {
|
|
3514
|
+
console.error(
|
|
3515
|
+
`[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.`
|
|
3516
|
+
);
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
2973
3519
|
}
|
|
2974
3520
|
deleteErrorsUnderPrefix(schemaErrors, targetSegments);
|
|
2975
3521
|
deleteErrorsUnderPrefix(userErrors, targetSegments);
|
|
@@ -3055,6 +3601,7 @@ function createFormStore(options) {
|
|
|
3055
3601
|
originals,
|
|
3056
3602
|
schema,
|
|
3057
3603
|
ssr,
|
|
3604
|
+
shouldShowErrors: resolvedShouldShowErrors,
|
|
3058
3605
|
submitting,
|
|
3059
3606
|
activeSubmissions,
|
|
3060
3607
|
submitCount,
|
|
@@ -3081,6 +3628,7 @@ function createFormStore(options) {
|
|
|
3081
3628
|
deregisterElement,
|
|
3082
3629
|
markFocused,
|
|
3083
3630
|
markTouched,
|
|
3631
|
+
touchAtPath,
|
|
3084
3632
|
markConnectedOptimistically,
|
|
3085
3633
|
isPristineAtPath,
|
|
3086
3634
|
getFieldRecord,
|
|
@@ -3097,6 +3645,11 @@ function createFormStore(options) {
|
|
|
3097
3645
|
awaitPendingWrites,
|
|
3098
3646
|
modules,
|
|
3099
3647
|
persistOptIns,
|
|
3648
|
+
isSensitivePath: resolvedIsSensitivePath,
|
|
3649
|
+
segmentMatchesSensitive: resolvedSegmentMatchesSensitive,
|
|
3650
|
+
noSyncPaths,
|
|
3651
|
+
incrementNoSyncOptOut,
|
|
3652
|
+
decrementNoSyncOptOut,
|
|
3100
3653
|
coerceIndex,
|
|
3101
3654
|
blankPaths,
|
|
3102
3655
|
originalBlankPaths,
|
|
@@ -3104,43 +3657,140 @@ function createFormStore(options) {
|
|
|
3104
3657
|
};
|
|
3105
3658
|
}
|
|
3106
3659
|
|
|
3107
|
-
function getComputedSchema(formKey, schemaOrCallback) {
|
|
3108
|
-
if (typeof schemaOrCallback === "function") return schemaOrCallback(formKey);
|
|
3660
|
+
function getComputedSchema(formKey, schemaOrCallback, options) {
|
|
3661
|
+
if (typeof schemaOrCallback === "function") return schemaOrCallback(formKey, options);
|
|
3109
3662
|
return schemaOrCallback;
|
|
3110
3663
|
}
|
|
3111
3664
|
|
|
3665
|
+
function captureErrorEntries(map) {
|
|
3666
|
+
const out = [];
|
|
3667
|
+
for (const [k, v] of map) out.push([k, [...v]]);
|
|
3668
|
+
return out;
|
|
3669
|
+
}
|
|
3670
|
+
function errorsEqual(a, b) {
|
|
3671
|
+
if (a.length !== b.length) return false;
|
|
3672
|
+
const bMap = /* @__PURE__ */ new Map();
|
|
3673
|
+
for (const [k, v] of b) bMap.set(k, v);
|
|
3674
|
+
for (const [k, v] of a) {
|
|
3675
|
+
const bv = bMap.get(k);
|
|
3676
|
+
if (bv === void 0) return false;
|
|
3677
|
+
if (v.length !== bv.length) return false;
|
|
3678
|
+
for (let i = 0; i < v.length; i++) {
|
|
3679
|
+
const av = v[i];
|
|
3680
|
+
const bvi = bv[i];
|
|
3681
|
+
if (av === bvi) continue;
|
|
3682
|
+
if (av.message !== bvi.message) return false;
|
|
3683
|
+
if (av.code !== bvi.code) return false;
|
|
3684
|
+
if (av.formKey !== bvi.formKey) return false;
|
|
3685
|
+
if (av.path !== bvi.path) {
|
|
3686
|
+
if (av.path.length !== bvi.path.length) return false;
|
|
3687
|
+
for (let j = 0; j < av.path.length; j++) {
|
|
3688
|
+
if (av.path[j] !== bvi.path[j]) return false;
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
return true;
|
|
3694
|
+
}
|
|
3695
|
+
function diffBlankPaths$1(prev, curr) {
|
|
3696
|
+
const added = [];
|
|
3697
|
+
const removed = [];
|
|
3698
|
+
for (const k of curr) if (!prev.has(k)) added.push(k);
|
|
3699
|
+
for (const k of prev) if (!curr.has(k)) removed.push(k);
|
|
3700
|
+
return { added, removed };
|
|
3701
|
+
}
|
|
3702
|
+
function applyDeltaForward(snap, d) {
|
|
3703
|
+
const nextForm = applyPatchesForward(snap.form, d.formPatches);
|
|
3704
|
+
const nextBlank = new Set(snap.blankPaths);
|
|
3705
|
+
for (const k of d.blankPathsRemoved) nextBlank.delete(k);
|
|
3706
|
+
for (const k of d.blankPathsAdded) nextBlank.add(k);
|
|
3707
|
+
return {
|
|
3708
|
+
form: nextForm,
|
|
3709
|
+
blankPaths: [...nextBlank],
|
|
3710
|
+
schemaErrors: d.schemaErrors !== void 0 ? d.schemaErrors.after : snap.schemaErrors,
|
|
3711
|
+
userErrors: d.userErrors !== void 0 ? d.userErrors.after : snap.userErrors
|
|
3712
|
+
};
|
|
3713
|
+
}
|
|
3714
|
+
function applyDeltaInverse(snap, d) {
|
|
3715
|
+
const prevForm = applyPatchesInverse(snap.form, d.formPatches);
|
|
3716
|
+
const prevBlank = new Set(snap.blankPaths);
|
|
3717
|
+
for (const k of d.blankPathsAdded) prevBlank.delete(k);
|
|
3718
|
+
for (const k of d.blankPathsRemoved) prevBlank.add(k);
|
|
3719
|
+
return {
|
|
3720
|
+
form: prevForm,
|
|
3721
|
+
blankPaths: [...prevBlank],
|
|
3722
|
+
schemaErrors: d.schemaErrors !== void 0 ? d.schemaErrors.before : snap.schemaErrors,
|
|
3723
|
+
userErrors: d.userErrors !== void 0 ? d.userErrors.before : snap.userErrors
|
|
3724
|
+
};
|
|
3725
|
+
}
|
|
3112
3726
|
function createHistoryModule(state, config) {
|
|
3113
|
-
const max =
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3727
|
+
const max = normalizeNumericOption({
|
|
3728
|
+
value: typeof config === "object" ? config.max ?? DEFAULT_HISTORY_MAX_SNAPSHOTS : DEFAULT_HISTORY_MAX_SNAPSHOTS,
|
|
3729
|
+
source: "useForm.history.max",
|
|
3730
|
+
allowInfinity: false,
|
|
3731
|
+
min: 0,
|
|
3732
|
+
defaultValue: DEFAULT_HISTORY_MAX_SNAPSHOTS
|
|
3733
|
+
});
|
|
3117
3734
|
function captureSnapshot() {
|
|
3118
3735
|
return {
|
|
3119
3736
|
form: structuralSnapshot(state.form.value),
|
|
3120
3737
|
blankPaths: [...state.blankPaths],
|
|
3121
|
-
schemaErrors:
|
|
3122
|
-
userErrors:
|
|
3738
|
+
schemaErrors: captureErrorEntries(state.schemaErrors),
|
|
3739
|
+
userErrors: captureErrorEntries(state.userErrors)
|
|
3123
3740
|
};
|
|
3124
3741
|
}
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3742
|
+
const initial = captureSnapshot();
|
|
3743
|
+
const base = vue.shallowRef(initial);
|
|
3744
|
+
const currentSnapshot = vue.shallowRef(initial);
|
|
3745
|
+
const undoDeltas = vue.shallowRef([]);
|
|
3746
|
+
const redoDeltas = vue.shallowRef([]);
|
|
3747
|
+
let suppressNext = false;
|
|
3748
|
+
function appendDelta(delta, newCurrent) {
|
|
3749
|
+
if (max === 0) {
|
|
3750
|
+
base.value = newCurrent;
|
|
3751
|
+
currentSnapshot.value = newCurrent;
|
|
3752
|
+
redoDeltas.value = [];
|
|
3753
|
+
return;
|
|
3754
|
+
}
|
|
3755
|
+
undoDeltas.value = [...undoDeltas.value, delta];
|
|
3756
|
+
redoDeltas.value = [];
|
|
3757
|
+
currentSnapshot.value = newCurrent;
|
|
3758
|
+
while (1 + undoDeltas.value.length > max && undoDeltas.value.length > 0) {
|
|
3759
|
+
const oldest = undoDeltas.value[0];
|
|
3760
|
+
base.value = applyDeltaForward(base.value, oldest);
|
|
3761
|
+
undoDeltas.value = undoDeltas.value.slice(1);
|
|
3762
|
+
}
|
|
3129
3763
|
}
|
|
3130
|
-
|
|
3131
|
-
const unsubscribeChange = state.onFormChange(() => {
|
|
3764
|
+
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
3132
3765
|
if (suppressNext) {
|
|
3133
3766
|
suppressNext = false;
|
|
3134
3767
|
return;
|
|
3135
3768
|
}
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3769
|
+
if (meta?.hydration === true) {
|
|
3770
|
+
clear();
|
|
3771
|
+
return;
|
|
3772
|
+
}
|
|
3773
|
+
if (meta?.crossTab === true) {
|
|
3774
|
+
currentSnapshot.value = captureSnapshot();
|
|
3775
|
+
return;
|
|
3776
|
+
}
|
|
3777
|
+
const newSnap = captureSnapshot();
|
|
3778
|
+
const prevSnap = currentSnapshot.value;
|
|
3779
|
+
const formPatches = [];
|
|
3780
|
+
diffAndApply(prevSnap.form, newSnap.form, [], (p) => formPatches.push(p));
|
|
3781
|
+
const prevBlankSet = new Set(prevSnap.blankPaths);
|
|
3782
|
+
const currBlankSet = new Set(newSnap.blankPaths);
|
|
3783
|
+
const blankDiff = diffBlankPaths$1(prevBlankSet, currBlankSet);
|
|
3784
|
+
const delta = {
|
|
3785
|
+
formPatches,
|
|
3786
|
+
blankPathsAdded: blankDiff.added,
|
|
3787
|
+
blankPathsRemoved: blankDiff.removed,
|
|
3788
|
+
...errorsEqual(prevSnap.schemaErrors, newSnap.schemaErrors) ? {} : { schemaErrors: { before: prevSnap.schemaErrors, after: newSnap.schemaErrors } },
|
|
3789
|
+
...errorsEqual(prevSnap.userErrors, newSnap.userErrors) ? {} : { userErrors: { before: prevSnap.userErrors, after: newSnap.userErrors } }
|
|
3790
|
+
};
|
|
3791
|
+
appendDelta(delta, newSnap);
|
|
3142
3792
|
});
|
|
3143
|
-
function
|
|
3793
|
+
function restoreCurrent(snap) {
|
|
3144
3794
|
suppressNext = true;
|
|
3145
3795
|
state.blankPaths.clear();
|
|
3146
3796
|
for (const key of snap.blankPaths) state.blankPaths.add(key);
|
|
@@ -3153,38 +3803,46 @@ function createHistoryModule(state, config) {
|
|
|
3153
3803
|
state.setAllUserErrors(userFlat);
|
|
3154
3804
|
}
|
|
3155
3805
|
function undo() {
|
|
3156
|
-
if (
|
|
3157
|
-
const
|
|
3158
|
-
const
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3806
|
+
if (undoDeltas.value.length === 0) return false;
|
|
3807
|
+
const d = undoDeltas.value[undoDeltas.value.length - 1];
|
|
3808
|
+
const restored = applyDeltaInverse(currentSnapshot.value, d);
|
|
3809
|
+
redoDeltas.value = [...redoDeltas.value, d];
|
|
3810
|
+
undoDeltas.value = undoDeltas.value.slice(0, -1);
|
|
3811
|
+
currentSnapshot.value = restored;
|
|
3812
|
+
restoreCurrent(restored);
|
|
3163
3813
|
return true;
|
|
3164
3814
|
}
|
|
3165
3815
|
function redo() {
|
|
3166
|
-
if (
|
|
3167
|
-
const
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3816
|
+
if (redoDeltas.value.length === 0) return false;
|
|
3817
|
+
const d = redoDeltas.value[redoDeltas.value.length - 1];
|
|
3818
|
+
const next = applyDeltaForward(currentSnapshot.value, d);
|
|
3819
|
+
undoDeltas.value = [...undoDeltas.value, d];
|
|
3820
|
+
redoDeltas.value = redoDeltas.value.slice(0, -1);
|
|
3821
|
+
currentSnapshot.value = next;
|
|
3822
|
+
restoreCurrent(next);
|
|
3172
3823
|
return true;
|
|
3173
3824
|
}
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3825
|
+
function clear() {
|
|
3826
|
+
const fresh = captureSnapshot();
|
|
3827
|
+
base.value = fresh;
|
|
3828
|
+
currentSnapshot.value = fresh;
|
|
3829
|
+
undoDeltas.value = [];
|
|
3830
|
+
redoDeltas.value = [];
|
|
3831
|
+
}
|
|
3832
|
+
const canUndo = vue.computed(() => undoDeltas.value.length > 0);
|
|
3833
|
+
const canRedo = vue.computed(() => redoDeltas.value.length > 0);
|
|
3834
|
+
const historySize = vue.computed(() => 1 + undoDeltas.value.length + redoDeltas.value.length);
|
|
3177
3835
|
return {
|
|
3178
3836
|
undo,
|
|
3179
3837
|
redo,
|
|
3838
|
+
clear,
|
|
3180
3839
|
canUndo,
|
|
3181
3840
|
canRedo,
|
|
3182
3841
|
historySize,
|
|
3183
3842
|
dispose() {
|
|
3184
3843
|
unsubscribeChange();
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
redoStack.value = [];
|
|
3844
|
+
undoDeltas.value = [];
|
|
3845
|
+
redoDeltas.value = [];
|
|
3188
3846
|
}
|
|
3189
3847
|
};
|
|
3190
3848
|
}
|
|
@@ -3202,12 +3860,368 @@ function hashStableString(input, seed = 0) {
|
|
|
3202
3860
|
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36).padStart(11, "0");
|
|
3203
3861
|
}
|
|
3204
3862
|
|
|
3863
|
+
const PROTOCOL_VERSION = 1;
|
|
3864
|
+
const JOIN_COLLECTION_WINDOW_MS = 50;
|
|
3865
|
+
const SNAPSHOT_TIMEOUT_MS = 200;
|
|
3866
|
+
const MAX_LEADER_ATTEMPTS = 3;
|
|
3867
|
+
function isDangerousSegment(s) {
|
|
3868
|
+
return s === "__proto__" || s === "constructor" || s === "prototype";
|
|
3869
|
+
}
|
|
3870
|
+
function pathContainsDangerousSegment(path) {
|
|
3871
|
+
for (let i = 0; i < path.length; i++) {
|
|
3872
|
+
if (isDangerousSegment(path[i])) return true;
|
|
3873
|
+
}
|
|
3874
|
+
return false;
|
|
3875
|
+
}
|
|
3876
|
+
function diffBlankPaths(prev, curr) {
|
|
3877
|
+
const added = [];
|
|
3878
|
+
const removed = [];
|
|
3879
|
+
const prevSet = new Set(prev);
|
|
3880
|
+
for (const k of curr) if (!prevSet.has(k)) added.push(k);
|
|
3881
|
+
for (const k of prev) if (!curr.has(k)) removed.push(k);
|
|
3882
|
+
return { added, removed };
|
|
3883
|
+
}
|
|
3884
|
+
function snapshotForm(form) {
|
|
3885
|
+
return structuralSnapshot(form);
|
|
3886
|
+
}
|
|
3887
|
+
function stripSensitivePathsDeep(value, pathSoFar, isSensitivePath) {
|
|
3888
|
+
if (value === null || typeof value !== "object") return value;
|
|
3889
|
+
if (Array.isArray(value)) {
|
|
3890
|
+
return value.map((item, i) => stripSensitivePathsDeep(item, [...pathSoFar, i], isSensitivePath));
|
|
3891
|
+
}
|
|
3892
|
+
const proto = Object.getPrototypeOf(value);
|
|
3893
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
3894
|
+
const out = {};
|
|
3895
|
+
const src = value;
|
|
3896
|
+
for (const key of Object.keys(src)) {
|
|
3897
|
+
const childPath = [...pathSoFar, key];
|
|
3898
|
+
if (isSensitivePath(childPath)) {
|
|
3899
|
+
out[key] = void 0;
|
|
3900
|
+
continue;
|
|
3901
|
+
}
|
|
3902
|
+
out[key] = stripSensitivePathsDeep(src[key], childPath, isSensitivePath);
|
|
3903
|
+
}
|
|
3904
|
+
return out;
|
|
3905
|
+
}
|
|
3906
|
+
function isValidSyncMessage(data) {
|
|
3907
|
+
if (data === null || typeof data !== "object") return false;
|
|
3908
|
+
const m = data;
|
|
3909
|
+
if (m["v"] !== PROTOCOL_VERSION) return false;
|
|
3910
|
+
if (typeof m["senderId"] !== "string") return false;
|
|
3911
|
+
if (typeof m["kind"] !== "string") return false;
|
|
3912
|
+
switch (m["kind"]) {
|
|
3913
|
+
case "hello":
|
|
3914
|
+
case "announce":
|
|
3915
|
+
return true;
|
|
3916
|
+
case "requestSnapshot":
|
|
3917
|
+
return typeof m["targetId"] === "string";
|
|
3918
|
+
case "snapshot":
|
|
3919
|
+
return Array.isArray(m["blankPaths"]) && "form" in m;
|
|
3920
|
+
case "patches":
|
|
3921
|
+
return Array.isArray(m["formPatches"]) && Array.isArray(m["blankPathsAdded"]) && Array.isArray(m["blankPathsRemoved"]);
|
|
3922
|
+
default:
|
|
3923
|
+
return false;
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
function generateSenderId() {
|
|
3927
|
+
try {
|
|
3928
|
+
return globalThis.crypto.randomUUID();
|
|
3929
|
+
} catch {
|
|
3930
|
+
return `atta-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
function createMultiTabSyncModule(state, channelName, options) {
|
|
3934
|
+
if (typeof BroadcastChannel === "undefined") {
|
|
3935
|
+
return {
|
|
3936
|
+
dispose: () => void 0,
|
|
3937
|
+
lifecycle: () => "established",
|
|
3938
|
+
senderId: "",
|
|
3939
|
+
channelName
|
|
3940
|
+
};
|
|
3941
|
+
}
|
|
3942
|
+
let channel;
|
|
3943
|
+
try {
|
|
3944
|
+
channel = new BroadcastChannel(channelName);
|
|
3945
|
+
} catch {
|
|
3946
|
+
return {
|
|
3947
|
+
dispose: () => void 0,
|
|
3948
|
+
lifecycle: () => "established",
|
|
3949
|
+
senderId: "",
|
|
3950
|
+
channelName
|
|
3951
|
+
};
|
|
3952
|
+
}
|
|
3953
|
+
const senderId = generateSenderId();
|
|
3954
|
+
let lifecycle = "joining";
|
|
3955
|
+
let disposed = false;
|
|
3956
|
+
const peerIds = /* @__PURE__ */ new Set();
|
|
3957
|
+
let joinCollectionTimer = null;
|
|
3958
|
+
let snapshotTimeoutTimer = null;
|
|
3959
|
+
let leaderAttempts = 0;
|
|
3960
|
+
let prior = {
|
|
3961
|
+
form: snapshotForm(state.form.value),
|
|
3962
|
+
blankPathsSnapshot: [...state.blankPaths]
|
|
3963
|
+
};
|
|
3964
|
+
function safePost(msg) {
|
|
3965
|
+
if (disposed) return;
|
|
3966
|
+
try {
|
|
3967
|
+
channel.postMessage(msg);
|
|
3968
|
+
} catch {
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
function refreshPrior() {
|
|
3972
|
+
prior = {
|
|
3973
|
+
form: snapshotForm(state.form.value),
|
|
3974
|
+
blankPathsSnapshot: [...state.blankPaths]
|
|
3975
|
+
};
|
|
3976
|
+
}
|
|
3977
|
+
function isPathLocallySuppressed(path) {
|
|
3978
|
+
if (pathContainsDangerousSegment(path)) return true;
|
|
3979
|
+
if (options.isSensitivePath(path)) return true;
|
|
3980
|
+
const { key } = paths.canonicalizePath([...path]);
|
|
3981
|
+
if (options.noSyncPaths.has(key)) return true;
|
|
3982
|
+
return false;
|
|
3983
|
+
}
|
|
3984
|
+
function postPatches() {
|
|
3985
|
+
if (lifecycle !== "established") return;
|
|
3986
|
+
const next = snapshotForm(state.form.value);
|
|
3987
|
+
const rawPatches = [];
|
|
3988
|
+
diffAndApply(prior.form, next, [], (p) => rawPatches.push(p));
|
|
3989
|
+
const safePatches = [];
|
|
3990
|
+
for (const p of rawPatches) {
|
|
3991
|
+
if (isPathLocallySuppressed(p.path)) continue;
|
|
3992
|
+
safePatches.push(p);
|
|
3993
|
+
}
|
|
3994
|
+
const { added, removed } = diffBlankPaths(prior.blankPathsSnapshot, state.blankPaths);
|
|
3995
|
+
if (safePatches.length === 0 && added.length === 0 && removed.length === 0) {
|
|
3996
|
+
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
3997
|
+
return;
|
|
3998
|
+
}
|
|
3999
|
+
safePost({
|
|
4000
|
+
v: PROTOCOL_VERSION,
|
|
4001
|
+
kind: "patches",
|
|
4002
|
+
senderId,
|
|
4003
|
+
formPatches: safePatches,
|
|
4004
|
+
blankPathsAdded: added,
|
|
4005
|
+
blankPathsRemoved: removed
|
|
4006
|
+
});
|
|
4007
|
+
prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
|
|
4008
|
+
}
|
|
4009
|
+
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
4010
|
+
if (disposed) return;
|
|
4011
|
+
if (lifecycle !== "established") return;
|
|
4012
|
+
if (meta?.crossTab === true) return;
|
|
4013
|
+
if (meta?.hydration === true) {
|
|
4014
|
+
refreshPrior();
|
|
4015
|
+
return;
|
|
4016
|
+
}
|
|
4017
|
+
postPatches();
|
|
4018
|
+
});
|
|
4019
|
+
function applyIncomingForm(form, blankPaths) {
|
|
4020
|
+
state.blankPaths.clear();
|
|
4021
|
+
for (const k of blankPaths) state.blankPaths.add(k);
|
|
4022
|
+
state.applyFormReplacement(form, { crossTab: true, persist: false });
|
|
4023
|
+
refreshPrior();
|
|
4024
|
+
}
|
|
4025
|
+
function handlePatches(msg) {
|
|
4026
|
+
if (lifecycle !== "established") return;
|
|
4027
|
+
const safePatches = [];
|
|
4028
|
+
for (const p of msg.formPatches) {
|
|
4029
|
+
if (!Array.isArray(p.path)) continue;
|
|
4030
|
+
if (isPathLocallySuppressed(p.path)) continue;
|
|
4031
|
+
safePatches.push(p);
|
|
4032
|
+
}
|
|
4033
|
+
const safeBlankAdded = [];
|
|
4034
|
+
for (const k of msg.blankPathsAdded) {
|
|
4035
|
+
const segs = paths.canonicalizePath(k).segments;
|
|
4036
|
+
if (isPathLocallySuppressed(segs)) continue;
|
|
4037
|
+
safeBlankAdded.push(k);
|
|
4038
|
+
}
|
|
4039
|
+
const safeBlankRemoved = [];
|
|
4040
|
+
for (const k of msg.blankPathsRemoved) {
|
|
4041
|
+
const segs = paths.canonicalizePath(k).segments;
|
|
4042
|
+
if (isPathLocallySuppressed(segs)) continue;
|
|
4043
|
+
safeBlankRemoved.push(k);
|
|
4044
|
+
}
|
|
4045
|
+
if (safePatches.length === 0 && safeBlankAdded.length === 0 && safeBlankRemoved.length === 0) {
|
|
4046
|
+
return;
|
|
4047
|
+
}
|
|
4048
|
+
const candidate = applyPatchesForward(state.form.value, safePatches);
|
|
4049
|
+
try {
|
|
4050
|
+
options.validateForm(state.form.value);
|
|
4051
|
+
try {
|
|
4052
|
+
options.validateForm(candidate);
|
|
4053
|
+
} catch {
|
|
4054
|
+
return;
|
|
4055
|
+
}
|
|
4056
|
+
} catch {
|
|
4057
|
+
}
|
|
4058
|
+
const nextBlankPaths = new Set(state.blankPaths);
|
|
4059
|
+
for (const k of safeBlankRemoved) nextBlankPaths.delete(k);
|
|
4060
|
+
for (const k of safeBlankAdded) nextBlankPaths.add(k);
|
|
4061
|
+
applyIncomingForm(candidate, [...nextBlankPaths]);
|
|
4062
|
+
}
|
|
4063
|
+
function handleSnapshot(msg) {
|
|
4064
|
+
if (lifecycle !== "joining") return;
|
|
4065
|
+
try {
|
|
4066
|
+
options.validateForm(msg.form);
|
|
4067
|
+
} catch {
|
|
4068
|
+
return;
|
|
4069
|
+
}
|
|
4070
|
+
if (snapshotTimeoutTimer !== null) {
|
|
4071
|
+
clearTimeout(snapshotTimeoutTimer);
|
|
4072
|
+
snapshotTimeoutTimer = null;
|
|
4073
|
+
}
|
|
4074
|
+
if (joinCollectionTimer !== null) {
|
|
4075
|
+
clearTimeout(joinCollectionTimer);
|
|
4076
|
+
joinCollectionTimer = null;
|
|
4077
|
+
}
|
|
4078
|
+
applyIncomingForm(msg.form, msg.blankPaths);
|
|
4079
|
+
lifecycle = "established";
|
|
4080
|
+
peerIds.clear();
|
|
4081
|
+
}
|
|
4082
|
+
function respondToHello() {
|
|
4083
|
+
safePost({ v: PROTOCOL_VERSION, kind: "announce", senderId });
|
|
4084
|
+
}
|
|
4085
|
+
function respondToSnapshotRequest() {
|
|
4086
|
+
const scrubbedForm = stripSensitivePathsDeep(state.form.value, [], options.isSensitivePath);
|
|
4087
|
+
safePost({
|
|
4088
|
+
v: PROTOCOL_VERSION,
|
|
4089
|
+
kind: "snapshot",
|
|
4090
|
+
senderId,
|
|
4091
|
+
form: scrubbedForm,
|
|
4092
|
+
blankPaths: [...state.blankPaths]
|
|
4093
|
+
});
|
|
4094
|
+
}
|
|
4095
|
+
channel.onmessage = (event) => {
|
|
4096
|
+
if (disposed) return;
|
|
4097
|
+
const data = event.data;
|
|
4098
|
+
if (!isValidSyncMessage(data)) return;
|
|
4099
|
+
const msg = data;
|
|
4100
|
+
if (msg.senderId === senderId) return;
|
|
4101
|
+
switch (msg.kind) {
|
|
4102
|
+
case "hello":
|
|
4103
|
+
if (lifecycle !== "established") return;
|
|
4104
|
+
respondToHello();
|
|
4105
|
+
break;
|
|
4106
|
+
case "announce":
|
|
4107
|
+
if (lifecycle === "joining") peerIds.add(msg.senderId);
|
|
4108
|
+
break;
|
|
4109
|
+
case "requestSnapshot":
|
|
4110
|
+
if (lifecycle !== "established") return;
|
|
4111
|
+
if (msg.targetId !== senderId) return;
|
|
4112
|
+
respondToSnapshotRequest();
|
|
4113
|
+
break;
|
|
4114
|
+
case "snapshot":
|
|
4115
|
+
handleSnapshot(msg);
|
|
4116
|
+
break;
|
|
4117
|
+
case "patches":
|
|
4118
|
+
handlePatches(msg);
|
|
4119
|
+
break;
|
|
4120
|
+
}
|
|
4121
|
+
};
|
|
4122
|
+
function electLeaderAndRequest() {
|
|
4123
|
+
if (disposed) return;
|
|
4124
|
+
if (peerIds.size === 0) {
|
|
4125
|
+
lifecycle = "established";
|
|
4126
|
+
refreshPrior();
|
|
4127
|
+
return;
|
|
4128
|
+
}
|
|
4129
|
+
const sorted = [...peerIds].sort();
|
|
4130
|
+
const leaderId = sorted[0];
|
|
4131
|
+
peerIds.delete(leaderId);
|
|
4132
|
+
leaderAttempts++;
|
|
4133
|
+
safePost({
|
|
4134
|
+
v: PROTOCOL_VERSION,
|
|
4135
|
+
kind: "requestSnapshot",
|
|
4136
|
+
senderId,
|
|
4137
|
+
targetId: leaderId
|
|
4138
|
+
});
|
|
4139
|
+
snapshotTimeoutTimer = setTimeout(() => {
|
|
4140
|
+
snapshotTimeoutTimer = null;
|
|
4141
|
+
if (disposed) return;
|
|
4142
|
+
if (lifecycle === "established") return;
|
|
4143
|
+
if (leaderAttempts >= MAX_LEADER_ATTEMPTS || peerIds.size === 0) {
|
|
4144
|
+
lifecycle = "established";
|
|
4145
|
+
refreshPrior();
|
|
4146
|
+
return;
|
|
4147
|
+
}
|
|
4148
|
+
electLeaderAndRequest();
|
|
4149
|
+
}, SNAPSHOT_TIMEOUT_MS);
|
|
4150
|
+
}
|
|
4151
|
+
function joinFlow() {
|
|
4152
|
+
safePost({ v: PROTOCOL_VERSION, kind: "hello", senderId });
|
|
4153
|
+
joinCollectionTimer = setTimeout(() => {
|
|
4154
|
+
joinCollectionTimer = null;
|
|
4155
|
+
if (disposed) return;
|
|
4156
|
+
if (lifecycle === "established") return;
|
|
4157
|
+
electLeaderAndRequest();
|
|
4158
|
+
}, JOIN_COLLECTION_WINDOW_MS);
|
|
4159
|
+
}
|
|
4160
|
+
joinFlow();
|
|
4161
|
+
return {
|
|
4162
|
+
dispose: () => {
|
|
4163
|
+
if (disposed) return;
|
|
4164
|
+
disposed = true;
|
|
4165
|
+
if (joinCollectionTimer !== null) {
|
|
4166
|
+
clearTimeout(joinCollectionTimer);
|
|
4167
|
+
joinCollectionTimer = null;
|
|
4168
|
+
}
|
|
4169
|
+
if (snapshotTimeoutTimer !== null) {
|
|
4170
|
+
clearTimeout(snapshotTimeoutTimer);
|
|
4171
|
+
snapshotTimeoutTimer = null;
|
|
4172
|
+
}
|
|
4173
|
+
unsubscribeChange();
|
|
4174
|
+
try {
|
|
4175
|
+
channel.close();
|
|
4176
|
+
} catch {
|
|
4177
|
+
}
|
|
4178
|
+
},
|
|
4179
|
+
lifecycle: () => lifecycle,
|
|
4180
|
+
senderId,
|
|
4181
|
+
channelName
|
|
4182
|
+
};
|
|
4183
|
+
}
|
|
4184
|
+
const MULTI_TAB_SYNC_MODULE_KEY = "multiTabSync";
|
|
4185
|
+
|
|
4186
|
+
const warned = /* @__PURE__ */ new Set();
|
|
4187
|
+
function warnOnceInsecureContext(feature) {
|
|
4188
|
+
if (!plugin.__DEV__) return;
|
|
4189
|
+
if (warned.has(feature)) return;
|
|
4190
|
+
warned.add(feature);
|
|
4191
|
+
const message = featureMessage(feature);
|
|
4192
|
+
console.warn(`[attaform] ${message}`);
|
|
4193
|
+
}
|
|
4194
|
+
function featureMessage(feature) {
|
|
4195
|
+
switch (feature) {
|
|
4196
|
+
case "multiTab":
|
|
4197
|
+
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.";
|
|
4198
|
+
case "persist:local":
|
|
4199
|
+
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.";
|
|
4200
|
+
case "persist:session":
|
|
4201
|
+
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.";
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
function isSecureContext() {
|
|
4205
|
+
return typeof window !== "undefined" && window.isSecureContext === true;
|
|
4206
|
+
}
|
|
4207
|
+
|
|
3205
4208
|
function useAbstractForm(configuration) {
|
|
3206
4209
|
if (configuration === void 0 || configuration === null || configuration.schema === void 0) {
|
|
3207
4210
|
throw new plugin.InvalidUseFormConfigError();
|
|
3208
4211
|
}
|
|
3209
4212
|
const key = resolveFormKey(configuration.key);
|
|
3210
|
-
const
|
|
4213
|
+
const instance = vue.getCurrentInstance();
|
|
4214
|
+
if (instance !== null) plugin.ensureAttaformInstalled(instance.appContext.app);
|
|
4215
|
+
const registry = plugin.useRegistry();
|
|
4216
|
+
const merged = mergeWithDefaults(registry.defaults, configuration);
|
|
4217
|
+
const maxRecursionDepth = normalizeNumericOption({
|
|
4218
|
+
value: merged.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH,
|
|
4219
|
+
source: "useForm.maxRecursionDepth",
|
|
4220
|
+
allowInfinity: true,
|
|
4221
|
+
min: 0,
|
|
4222
|
+
defaultValue: DEFAULT_MAX_RECURSION_DEPTH
|
|
4223
|
+
});
|
|
4224
|
+
const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
|
|
3211
4225
|
if (configuration.persist !== void 0 && configuration.key === void 0) {
|
|
3212
4226
|
throw new plugin.AnonPersistError({
|
|
3213
4227
|
cause: "no-key",
|
|
@@ -3215,13 +4229,10 @@ function useAbstractForm(configuration) {
|
|
|
3215
4229
|
callSite: plugin.captureUserCallSite()
|
|
3216
4230
|
});
|
|
3217
4231
|
}
|
|
3218
|
-
const instance = vue.getCurrentInstance();
|
|
3219
|
-
if (instance !== null) plugin.ensureAttaformInstalled(instance.appContext.app);
|
|
3220
|
-
const registry = plugin.useRegistry();
|
|
3221
|
-
const merged = mergeWithDefaults(registry.defaults, configuration);
|
|
3222
4232
|
const existing = registry.forms.get(key);
|
|
3223
4233
|
if (plugin.__DEV__ && existing !== void 0) {
|
|
3224
4234
|
warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
|
|
4235
|
+
warnOnPersistDivergence(key, existing, configuration.persist);
|
|
3225
4236
|
}
|
|
3226
4237
|
const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
|
|
3227
4238
|
if (vue.getCurrentScope() !== void 0) {
|
|
@@ -3232,16 +4243,54 @@ function useAbstractForm(configuration) {
|
|
|
3232
4243
|
if (existing === void 0 && !registry.ssr) {
|
|
3233
4244
|
if (merged.persist !== void 0 && !persistDisabledByAnonRule) {
|
|
3234
4245
|
const resolvedPersist = normalizePersistConfig(merged.persist);
|
|
3235
|
-
const
|
|
3236
|
-
|
|
3237
|
-
const
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
4246
|
+
const storageKind = resolvedPersist.storage;
|
|
4247
|
+
const isBuiltinStorage = typeof storageKind === "string";
|
|
4248
|
+
const secureContextOk = !isBuiltinStorage || isSecureContext();
|
|
4249
|
+
if (!secureContextOk) {
|
|
4250
|
+
const feature = storageKind === "session" ? "persist:session" : "persist:local";
|
|
4251
|
+
warnOnceInsecureContext(feature);
|
|
4252
|
+
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
4253
|
+
} else {
|
|
4254
|
+
const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
|
|
4255
|
+
void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
|
|
4256
|
+
const persistenceModule = wirePersistence(state, resolvedPersist);
|
|
4257
|
+
state.modules.set(PERSISTENCE_MODULE_KEY, persistenceModule);
|
|
4258
|
+
state.registerDrain(() => persistenceModule.awaitPendingWrites());
|
|
4259
|
+
state.registerCleanup(() => persistenceModule.dispose());
|
|
4260
|
+
}
|
|
3241
4261
|
} else {
|
|
3242
4262
|
void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
|
|
3243
4263
|
}
|
|
3244
4264
|
}
|
|
4265
|
+
if (existing === void 0 && merged.multiTab !== false && configuration.key !== void 0 && !registry.ssr) {
|
|
4266
|
+
const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
|
|
4267
|
+
const secureContext = isSecureContext();
|
|
4268
|
+
if (hasBroadcastChannel && secureContext) {
|
|
4269
|
+
let channelName;
|
|
4270
|
+
try {
|
|
4271
|
+
channelName = `attaform:sync:${state.formKey}:${hashStableString(state.schema.fingerprint())}`;
|
|
4272
|
+
} catch {
|
|
4273
|
+
channelName = null;
|
|
4274
|
+
}
|
|
4275
|
+
if (channelName !== null) {
|
|
4276
|
+
const syncModule = createMultiTabSyncModule(state, channelName, {
|
|
4277
|
+
isSensitivePath: state.isSensitivePath,
|
|
4278
|
+
noSyncPaths: state.noSyncPaths,
|
|
4279
|
+
validateForm: (form) => {
|
|
4280
|
+
const result = state.schema.validateAtPath(form, void 0, { sync: true });
|
|
4281
|
+
if (result instanceof Promise) return;
|
|
4282
|
+
if (!result.success) {
|
|
4283
|
+
throw new Error("attaform multi-tab sync: post-apply schema validation failed");
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
});
|
|
4287
|
+
state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
|
|
4288
|
+
state.registerCleanup(() => syncModule.dispose());
|
|
4289
|
+
}
|
|
4290
|
+
} else if (hasBroadcastChannel && !secureContext) {
|
|
4291
|
+
warnOnceInsecureContext("multiTab");
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
3245
4294
|
if (existing === void 0 && merged.history !== void 0) {
|
|
3246
4295
|
const historyModule = createHistoryModule(state, merged.history);
|
|
3247
4296
|
state.modules.set(HISTORY_MODULE_KEY, historyModule);
|
|
@@ -3263,6 +4312,22 @@ function useAbstractForm(configuration) {
|
|
|
3263
4312
|
if (history !== void 0) {
|
|
3264
4313
|
apiOptions.history = history;
|
|
3265
4314
|
}
|
|
4315
|
+
if (merged.validateOn !== void 0) {
|
|
4316
|
+
apiOptions.validateOn = merged.validateOn;
|
|
4317
|
+
}
|
|
4318
|
+
const mergedDebounceMs = merged.debounceMs;
|
|
4319
|
+
if (mergedDebounceMs !== void 0) {
|
|
4320
|
+
apiOptions.debounceMs = mergedDebounceMs;
|
|
4321
|
+
}
|
|
4322
|
+
if (merged.shouldShowErrors !== void 0) {
|
|
4323
|
+
apiOptions.shouldShowErrors = resolveShouldShowErrors(merged.shouldShowErrors);
|
|
4324
|
+
}
|
|
4325
|
+
if (merged.coerce !== void 0) {
|
|
4326
|
+
apiOptions.coerce = merged.coerce;
|
|
4327
|
+
}
|
|
4328
|
+
if (merged.rememberVariants !== void 0) {
|
|
4329
|
+
apiOptions.rememberVariants = merged.rememberVariants;
|
|
4330
|
+
}
|
|
3266
4331
|
return buildFormApi(state, formInstanceId, apiOptions);
|
|
3267
4332
|
}
|
|
3268
4333
|
function mergeWithDefaults(defaults, configuration) {
|
|
@@ -3273,6 +4338,10 @@ function mergeWithDefaults(defaults, configuration) {
|
|
|
3273
4338
|
const coerce = configuration.coerce ?? defaults.coerce;
|
|
3274
4339
|
const validateOn = configuration.validateOn ?? defaults.validateOn;
|
|
3275
4340
|
const debounceMs = configuration.debounceMs ?? defaults.debounceMs;
|
|
4341
|
+
const shouldShowErrors = configuration.shouldShowErrors ?? defaults.shouldShowErrors;
|
|
4342
|
+
const maxRecursionDepth = configuration.maxRecursionDepth ?? defaults.maxRecursionDepth;
|
|
4343
|
+
const sensitiveNames = configuration.sensitiveNames ?? defaults.sensitiveNames;
|
|
4344
|
+
const multiTab = configuration.multiTab ?? defaults.multiTab;
|
|
3276
4345
|
return {
|
|
3277
4346
|
...configuration,
|
|
3278
4347
|
...strict === void 0 ? {} : { strict },
|
|
@@ -3281,7 +4350,11 @@ function mergeWithDefaults(defaults, configuration) {
|
|
|
3281
4350
|
...rememberVariants === void 0 ? {} : { rememberVariants },
|
|
3282
4351
|
...coerce === void 0 ? {} : { coerce },
|
|
3283
4352
|
...validateOn === void 0 ? {} : { validateOn },
|
|
3284
|
-
...debounceMs === void 0 ? {} : { debounceMs }
|
|
4353
|
+
...debounceMs === void 0 ? {} : { debounceMs },
|
|
4354
|
+
...shouldShowErrors === void 0 ? {} : { shouldShowErrors },
|
|
4355
|
+
...maxRecursionDepth === void 0 ? {} : { maxRecursionDepth },
|
|
4356
|
+
...sensitiveNames === void 0 ? {} : { sensitiveNames },
|
|
4357
|
+
...multiTab === void 0 ? {} : { multiTab }
|
|
3285
4358
|
};
|
|
3286
4359
|
}
|
|
3287
4360
|
const HISTORY_MODULE_KEY = "history";
|
|
@@ -3293,6 +4366,9 @@ function buildFreshState(key, schema, configuration, registry) {
|
|
|
3293
4366
|
schema
|
|
3294
4367
|
);
|
|
3295
4368
|
const initialBlankPaths = pending === void 0 ? walked.paths : void 0;
|
|
4369
|
+
const resolvedSensitiveNames = configuration.sensitiveNames;
|
|
4370
|
+
const resolvedIsSensitivePath = resolvedSensitiveNames === void 0 ? void 0 : plugin.createIsSensitivePath(resolvedSensitiveNames);
|
|
4371
|
+
const resolvedSegmentMatchesSensitive = resolvedSensitiveNames === void 0 ? void 0 : plugin.createSegmentMatchesSensitive(resolvedSensitiveNames);
|
|
3296
4372
|
const createOptions = {
|
|
3297
4373
|
formKey: key,
|
|
3298
4374
|
schema,
|
|
@@ -3304,7 +4380,10 @@ function buildFreshState(key, schema, configuration, registry) {
|
|
|
3304
4380
|
ssr: registry.ssr,
|
|
3305
4381
|
...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
|
|
3306
4382
|
...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
|
|
3307
|
-
...
|
|
4383
|
+
...configuration.shouldShowErrors !== void 0 ? { shouldShowErrors: configuration.shouldShowErrors } : {},
|
|
4384
|
+
...initialBlankPaths !== void 0 ? { initialBlankPaths } : {},
|
|
4385
|
+
...resolvedIsSensitivePath !== void 0 ? { isSensitivePath: resolvedIsSensitivePath } : {},
|
|
4386
|
+
...resolvedSegmentMatchesSensitive !== void 0 ? { segmentMatchesSensitive: resolvedSegmentMatchesSensitive } : {}
|
|
3308
4387
|
};
|
|
3309
4388
|
const state = createFormStore(createOptions);
|
|
3310
4389
|
registry.forms.set(
|
|
@@ -3363,11 +4442,47 @@ function warnOnSchemaFingerprintMismatch(key, existing, incoming) {
|
|
|
3363
4442
|
incoming: ${incomingFp}`
|
|
3364
4443
|
);
|
|
3365
4444
|
}
|
|
4445
|
+
function warnOnPersistDivergence(key, existing, incomingPersist) {
|
|
4446
|
+
if (incomingPersist === void 0) return;
|
|
4447
|
+
const wired = existing.modules.get(PERSISTENCE_MODULE_KEY);
|
|
4448
|
+
const incomingNormalized = normalizePersistConfig(incomingPersist);
|
|
4449
|
+
if (wired === void 0) {
|
|
4450
|
+
console.warn(
|
|
4451
|
+
`[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.`
|
|
4452
|
+
);
|
|
4453
|
+
return;
|
|
4454
|
+
}
|
|
4455
|
+
if (persistConfigsEquivalent(wired.wiredConfig, incomingNormalized)) return;
|
|
4456
|
+
console.warn(
|
|
4457
|
+
`[attaform] useForm({ key: "${key}" }) passed a persist config that differs from the first useForm({ key }) call's; first wins, this one is ignored.
|
|
4458
|
+
wired: ${describePersist(wired.wiredConfig)}
|
|
4459
|
+
incoming: ${describePersist(incomingNormalized)}`
|
|
4460
|
+
);
|
|
4461
|
+
}
|
|
4462
|
+
function persistConfigsEquivalent(a, b) {
|
|
4463
|
+
if (a.storage !== b.storage) return false;
|
|
4464
|
+
if ((a.key ?? void 0) !== (b.key ?? void 0)) return false;
|
|
4465
|
+
if ((a.debounceMs ?? void 0) !== (b.debounceMs ?? void 0)) return false;
|
|
4466
|
+
return true;
|
|
4467
|
+
}
|
|
4468
|
+
function describePersist(config) {
|
|
4469
|
+
const storage = typeof config.storage === "string" ? config.storage : "custom-adapter";
|
|
4470
|
+
const parts = [`storage=${storage}`];
|
|
4471
|
+
if (config.key !== void 0) parts.push(`key=${config.key}`);
|
|
4472
|
+
if (config.debounceMs !== void 0) parts.push(`debounceMs=${config.debounceMs}`);
|
|
4473
|
+
return `{ ${parts.join(", ")} }`;
|
|
4474
|
+
}
|
|
3366
4475
|
function wirePersistence(state, config) {
|
|
3367
4476
|
const fingerprint = hashStableString(state.schema.fingerprint());
|
|
3368
4477
|
const base = resolveStorageKeyBase(config, state.formKey);
|
|
3369
4478
|
const key = `${base}:${fingerprint}`;
|
|
3370
|
-
const debounceMs =
|
|
4479
|
+
const debounceMs = normalizeNumericOption({
|
|
4480
|
+
value: config.debounceMs ?? DEFAULT_PERSISTENCE_DEBOUNCE_MS,
|
|
4481
|
+
source: "useForm.persist.debounceMs",
|
|
4482
|
+
allowInfinity: false,
|
|
4483
|
+
min: 0,
|
|
4484
|
+
defaultValue: DEFAULT_PERSISTENCE_DEBOUNCE_MS
|
|
4485
|
+
});
|
|
3371
4486
|
const include = config.include ?? "form";
|
|
3372
4487
|
const clearOnSubmitSuccess = config.clearOnSubmitSuccess ?? true;
|
|
3373
4488
|
const adapterPromise = getStorageAdapter(config.storage);
|
|
@@ -3402,6 +4517,7 @@ function wirePersistence(state, config) {
|
|
|
3402
4517
|
}, debounceMs);
|
|
3403
4518
|
const unsubscribeChange = state.onFormChange((_next, meta) => {
|
|
3404
4519
|
if (disposed || inFlightFinalFlush !== null) return;
|
|
4520
|
+
if (meta?.crossTab === true) return;
|
|
3405
4521
|
if (meta?.persist !== true) return;
|
|
3406
4522
|
pendingOptedInPaths = new Set(state.persistOptIns.optedInPaths());
|
|
3407
4523
|
writer.schedule();
|
|
@@ -3435,7 +4551,7 @@ function wirePersistence(state, config) {
|
|
|
3435
4551
|
payload.data.form,
|
|
3436
4552
|
state.schema
|
|
3437
4553
|
);
|
|
3438
|
-
state.applyFormReplacement(merged);
|
|
4554
|
+
state.applyFormReplacement(merged, { hydration: true });
|
|
3439
4555
|
const persistedLeafPaths = collectPersistedLeafPaths(payload.data.form);
|
|
3440
4556
|
for (const k of persistedLeafPaths) {
|
|
3441
4557
|
state.blankPaths.delete(k);
|
|
@@ -3566,6 +4682,7 @@ function wirePersistence(state, config) {
|
|
|
3566
4682
|
});
|
|
3567
4683
|
}
|
|
3568
4684
|
return {
|
|
4685
|
+
wiredConfig: config,
|
|
3569
4686
|
writePathImmediately,
|
|
3570
4687
|
clearPersistedDraft,
|
|
3571
4688
|
awaitPendingWrites,
|
|
@@ -3640,7 +4757,11 @@ function injectForm(key) {
|
|
|
3640
4757
|
}
|
|
3641
4758
|
const ambientInstanceId = vue.getCurrentInstance() !== null ? vue.inject(plugin.kFormInstanceId, null) : null;
|
|
3642
4759
|
const formInstanceId = ambientInstanceId ?? (vue.getCurrentInstance() !== null ? vue.useId() : `atta:form-instance-injected:${injectedInstanceCounter++}`);
|
|
3643
|
-
return buildFormApi(
|
|
4760
|
+
return buildFormApi(
|
|
4761
|
+
state,
|
|
4762
|
+
formInstanceId,
|
|
4763
|
+
apiOptions
|
|
4764
|
+
);
|
|
3644
4765
|
}
|
|
3645
4766
|
function resolveState(key, registry) {
|
|
3646
4767
|
if (key !== void 0) {
|
|
@@ -3686,14 +4807,16 @@ function warnIfAmbientProviderHadDuplicates() {
|
|
|
3686
4807
|
|
|
3687
4808
|
exports.AttaformErrorCode = AttaformErrorCode;
|
|
3688
4809
|
exports.defaultCoercionRules = defaultCoercionRules;
|
|
4810
|
+
exports.defaultShouldShowErrors = defaultShouldShowErrors;
|
|
3689
4811
|
exports.defineCoercion = defineCoercion;
|
|
3690
4812
|
exports.getAtPath = getAtPath;
|
|
3691
4813
|
exports.humanize = humanize;
|
|
3692
4814
|
exports.injectForm = injectForm;
|
|
3693
4815
|
exports.isPlainRecord = isPlainRecord;
|
|
3694
4816
|
exports.isUnset = isUnset;
|
|
4817
|
+
exports.normalizeNumericOption = normalizeNumericOption;
|
|
3695
4818
|
exports.setAtPath = setAtPath;
|
|
3696
4819
|
exports.slimKindOf = slimKindOf;
|
|
3697
4820
|
exports.unset = unset;
|
|
3698
4821
|
exports.useAbstractForm = useAbstractForm;
|
|
3699
|
-
//# sourceMappingURL=attaform.
|
|
4822
|
+
//# sourceMappingURL=attaform.BFumZXY2.cjs.map
|