@vielzeug/forge 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_utils.cjs +1 -1
- package/dist/_utils.cjs.map +1 -1
- package/dist/_utils.d.ts.map +1 -1
- package/dist/_utils.js +1 -0
- package/dist/_utils.js.map +1 -1
- package/dist/core/array.cjs +1 -1
- package/dist/core/array.cjs.map +1 -1
- package/dist/core/array.d.ts.map +1 -1
- package/dist/core/array.js +63 -32
- package/dist/core/array.js.map +1 -1
- package/dist/core/asyncQueue.cjs.map +1 -1
- package/dist/core/asyncQueue.d.ts +2 -2
- package/dist/core/asyncQueue.js.map +1 -1
- package/dist/core/context.cjs +1 -1
- package/dist/core/context.cjs.map +1 -1
- package/dist/core/context.d.ts +2 -5
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +72 -109
- package/dist/core/context.js.map +1 -1
- package/dist/core/fields.cjs +2 -0
- package/dist/core/fields.cjs.map +1 -0
- package/dist/core/fields.d.ts +52 -0
- package/dist/core/fields.d.ts.map +1 -0
- package/dist/core/fields.js +310 -0
- package/dist/core/fields.js.map +1 -0
- package/dist/core/lifecycle.cjs +1 -1
- package/dist/core/lifecycle.cjs.map +1 -1
- package/dist/core/lifecycle.d.ts +3 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +10 -14
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/notifier.cjs +2 -0
- package/dist/core/notifier.cjs.map +1 -0
- package/dist/core/notifier.d.ts +33 -0
- package/dist/core/notifier.d.ts.map +1 -0
- package/dist/core/notifier.js +78 -0
- package/dist/core/notifier.js.map +1 -0
- package/dist/core/observe.cjs +1 -1
- package/dist/core/observe.cjs.map +1 -1
- package/dist/core/observe.d.ts +8 -6
- package/dist/core/observe.d.ts.map +1 -1
- package/dist/core/observe.js +34 -45
- package/dist/core/observe.js.map +1 -1
- package/dist/core/scope.cjs +1 -1
- package/dist/core/scope.cjs.map +1 -1
- package/dist/core/scope.d.ts +6 -3
- package/dist/core/scope.d.ts.map +1 -1
- package/dist/core/scope.js +105 -156
- package/dist/core/scope.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +3 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -2
- package/dist/errors.js.map +1 -1
- package/dist/forge.cjs +1 -1
- package/dist/forge.cjs.map +1 -1
- package/dist/forge.iife.js +1 -1
- package/dist/forge.iife.js.map +1 -1
- package/dist/forge.js +1 -1
- package/dist/forge.js.map +1 -1
- package/dist/form.cjs +1 -1
- package/dist/form.cjs.map +1 -1
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +45 -56
- package/dist/form.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +4 -4
- package/dist/types.cjs +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.ts +53 -36
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -2
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
- package/dist/core/validation.cjs +0 -2
- package/dist/core/validation.cjs.map +0 -1
- package/dist/core/validation.d.ts +0 -23
- package/dist/core/validation.d.ts.map +0 -1
- package/dist/core/validation.js +0 -173
- package/dist/core/validation.js.map +0 -1
- package/dist/core/values.cjs +0 -2
- package/dist/core/values.cjs.map +0 -1
- package/dist/core/values.d.ts +0 -31
- package/dist/core/values.d.ts.map +0 -1
- package/dist/core/values.js +0 -149
- package/dist/core/values.js.map +0 -1
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { warn as e } from "../_dev.js";
|
|
2
|
+
import { ForgeSubmitError as t, ForgeValidationError as n } from "../errors.js";
|
|
3
|
+
import { anySignal as r, assertSafeKey as i, flattenValues as a, isSafeKey as o, sanitizeForLog as s, unflattenValues as c } from "../_utils.js";
|
|
4
|
+
import { createArrayField as l } from "./array.js";
|
|
5
|
+
import { isAbortError as u } from "@vielzeug/arsenal";
|
|
6
|
+
//#region src/core/fields.ts
|
|
7
|
+
function d(d) {
|
|
8
|
+
function f(e) {
|
|
9
|
+
let t = e;
|
|
10
|
+
return i(t), d.store.get(t);
|
|
11
|
+
}
|
|
12
|
+
function p() {
|
|
13
|
+
return d.cachedValues ??= c(Object.fromEntries(d.store));
|
|
14
|
+
}
|
|
15
|
+
function m(e, t) {
|
|
16
|
+
d.baseline.get(e) === t ? d.dirty.delete(e) : d.dirty.add(e);
|
|
17
|
+
}
|
|
18
|
+
function h(e, t, n = {}) {
|
|
19
|
+
d.ensureNotDisposed("set");
|
|
20
|
+
let r = e;
|
|
21
|
+
i(r), d.store.set(r, t), m(r, t), n.touched && d.touched.add(r), d.requestNotify(r);
|
|
22
|
+
}
|
|
23
|
+
function g(e) {
|
|
24
|
+
let t = e;
|
|
25
|
+
return i(t), d.getFieldSnapshot(t);
|
|
26
|
+
}
|
|
27
|
+
function _(e, t) {
|
|
28
|
+
d.ensureNotDisposed("setError");
|
|
29
|
+
let n = e;
|
|
30
|
+
i(n), d.fieldErrors.set(n, t), d.invalidateErrors(), d.requestNotify(n);
|
|
31
|
+
}
|
|
32
|
+
function v(e) {
|
|
33
|
+
d.ensureNotDisposed("clearError");
|
|
34
|
+
let t = e;
|
|
35
|
+
i(t), d.fieldErrors.delete(t) && (d.invalidateErrors(), d.requestNotify(t));
|
|
36
|
+
}
|
|
37
|
+
function y(e, t, n) {
|
|
38
|
+
for (let t of [...d.fieldErrors.keys()]) e(t) && d.fieldErrors.delete(t);
|
|
39
|
+
for (let [e, r] of Object.entries(t)) {
|
|
40
|
+
let t = n(e);
|
|
41
|
+
i(t), typeof r == "string" && d.fieldErrors.set(t, r);
|
|
42
|
+
}
|
|
43
|
+
d.invalidateErrors();
|
|
44
|
+
}
|
|
45
|
+
function b(e = {}) {
|
|
46
|
+
d.ensureNotDisposed("resetErrors"), y(() => !0, e, (e) => e), d.requestNotify();
|
|
47
|
+
}
|
|
48
|
+
function x(t, n) {
|
|
49
|
+
/\.\d+(\.|$)/.test(t) && !d.store.has(t) && e(`${n}('${s(t, 80)}'): path looks like an array item key. Array items are stored as whole arrays — register on the parent key instead.`);
|
|
50
|
+
}
|
|
51
|
+
function S(e, t) {
|
|
52
|
+
d.ensureNotDisposed("setValidator");
|
|
53
|
+
let n = e;
|
|
54
|
+
i(n), x(n, "setValidator"), d.fieldCtrls.get(n)?.abort(), d.fieldCtrls.delete(n), t ? d.validators.set(n, t) : (d.validators.delete(n), d.fieldErrors.delete(n) && (d.invalidateErrors(), d.requestNotify(n)));
|
|
55
|
+
}
|
|
56
|
+
function C(e, t = {}) {
|
|
57
|
+
d.ensureNotDisposed("fields.register");
|
|
58
|
+
let n = e;
|
|
59
|
+
return i(n), x(n, "fields.register"), t.validator !== void 0 && S(e, t.validator), t.defaultValue !== void 0 && !d.store.has(n) && (d.store.set(n, t.defaultValue), d.baseline.set(n, t.defaultValue), d.requestNotify(n)), () => {
|
|
60
|
+
d.disposed || M(e);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function w() {
|
|
64
|
+
let e = /* @__PURE__ */ new Set();
|
|
65
|
+
for (let t of d.store.keys()) e.add(t);
|
|
66
|
+
for (let t of d.validators.keys()) e.add(t);
|
|
67
|
+
return Object.freeze([...e]);
|
|
68
|
+
}
|
|
69
|
+
function T(e) {
|
|
70
|
+
d.ensureNotDisposed("touch");
|
|
71
|
+
let t = e;
|
|
72
|
+
i(t), d.touched.add(t), d.requestNotify(t);
|
|
73
|
+
}
|
|
74
|
+
function E(e) {
|
|
75
|
+
d.ensureNotDisposed("untouch");
|
|
76
|
+
let t = e;
|
|
77
|
+
i(t), d.touched.delete(t) && d.requestNotify(t);
|
|
78
|
+
}
|
|
79
|
+
function D(e) {
|
|
80
|
+
for (let t of d.store.keys()) e(t) && d.touched.add(t);
|
|
81
|
+
for (let t of d.validators.keys()) e(t) && d.touched.add(t);
|
|
82
|
+
}
|
|
83
|
+
function O() {
|
|
84
|
+
d.ensureNotDisposed("touchAll"), D(() => !0), d.requestNotify();
|
|
85
|
+
}
|
|
86
|
+
function k(e) {
|
|
87
|
+
for (let t of [...d.touched]) e(t) && d.touched.delete(t);
|
|
88
|
+
}
|
|
89
|
+
function A() {
|
|
90
|
+
d.ensureNotDisposed("untouchAll"), k(() => !0), d.requestNotify();
|
|
91
|
+
}
|
|
92
|
+
function j(e) {
|
|
93
|
+
d.ensureNotDisposed("resetField");
|
|
94
|
+
let t = e;
|
|
95
|
+
i(t), d.fieldCtrls.get(t)?.abort(), d.fieldCtrls.delete(t), d.store.set(t, d.baseline.get(t)), d.dirty.delete(t), d.touched.delete(t), d.fieldErrors.delete(t) && d.invalidateErrors(), d.requestNotify(t);
|
|
96
|
+
}
|
|
97
|
+
function M(e) {
|
|
98
|
+
d.ensureNotDisposed("fields.remove");
|
|
99
|
+
let t = e;
|
|
100
|
+
i(t), d.store.delete(t), d.baseline.delete(t), d.dirty.delete(t), d.touched.delete(t), d.validators.delete(t), d.arrayCache.delete(t), d.fieldCtrls.get(t)?.abort(), d.fieldCtrls.delete(t), d.fieldErrors.delete(t) && d.invalidateErrors(), d.requestNotify(t);
|
|
101
|
+
}
|
|
102
|
+
function N() {
|
|
103
|
+
return /* @__PURE__ */ new Set([
|
|
104
|
+
...d.store.keys(),
|
|
105
|
+
...d.touched,
|
|
106
|
+
...d.fieldErrors.keys(),
|
|
107
|
+
...d.validators.keys()
|
|
108
|
+
]);
|
|
109
|
+
}
|
|
110
|
+
function P(e) {
|
|
111
|
+
for (let t of N()) e(t) && (d.fieldCtrls.get(t)?.abort(), d.fieldCtrls.delete(t), d.baseline.has(t) ? d.store.set(t, d.baseline.get(t)) : d.store.delete(t), d.dirty.delete(t), d.touched.delete(t), d.fieldErrors.delete(t) && d.invalidateErrors());
|
|
112
|
+
}
|
|
113
|
+
function F() {
|
|
114
|
+
d.ensureNotDisposed("reset");
|
|
115
|
+
for (let e of d.runCtrls) e.abort();
|
|
116
|
+
d.runCtrls.clear(), d.validatingRuns.clear(), P(() => !0), d.submitCount = 0, d.requestNotify();
|
|
117
|
+
}
|
|
118
|
+
function I(e, t, n) {
|
|
119
|
+
for (let t of N()) e(t) && (d.fieldCtrls.get(t)?.abort(), d.fieldCtrls.delete(t), d.store.delete(t), d.baseline.delete(t), d.dirty.delete(t), d.touched.delete(t), d.fieldErrors.delete(t) && d.invalidateErrors());
|
|
120
|
+
for (let [e, r] of Object.entries(t)) {
|
|
121
|
+
let t = n(e);
|
|
122
|
+
d.store.set(t, r), d.baseline.set(t, r);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function L(e) {
|
|
126
|
+
d.ensureNotDisposed("replace");
|
|
127
|
+
for (let e of d.runCtrls) e.abort();
|
|
128
|
+
d.runCtrls.clear(), d.validatingRuns.clear(), I(() => !0, a(e), (e) => e), d.submitCount = 0, d.requestNotify();
|
|
129
|
+
}
|
|
130
|
+
function R(e, t) {
|
|
131
|
+
let n = a(e), r = [];
|
|
132
|
+
for (let [e, i] of Object.entries(n)) {
|
|
133
|
+
let n = t(e);
|
|
134
|
+
d.baseline.set(n, i), d.store.set(n, i), d.dirty.delete(n), r.push(n);
|
|
135
|
+
}
|
|
136
|
+
return r;
|
|
137
|
+
}
|
|
138
|
+
function z(e) {
|
|
139
|
+
d.ensureNotDisposed("patch"), d.requestNotify(R(e, (e) => e));
|
|
140
|
+
}
|
|
141
|
+
function B(e) {
|
|
142
|
+
d.ensureNotDisposed("array");
|
|
143
|
+
let t = e;
|
|
144
|
+
i(t);
|
|
145
|
+
let n = d.arrayCache.get(t);
|
|
146
|
+
if (n) return n;
|
|
147
|
+
let r = l(e, d.store, h);
|
|
148
|
+
return d.arrayCache.set(t, r), r;
|
|
149
|
+
}
|
|
150
|
+
async function V(e, t) {
|
|
151
|
+
let n = d.validators.get(e);
|
|
152
|
+
if (!n) return;
|
|
153
|
+
if (t.aborted) throw t.reason;
|
|
154
|
+
let r = await n(d.store.get(e), t);
|
|
155
|
+
return typeof r == "string" ? r : void 0;
|
|
156
|
+
}
|
|
157
|
+
async function H(e) {
|
|
158
|
+
if (!d.formValidator) return {};
|
|
159
|
+
if (e.aborted) throw e.reason;
|
|
160
|
+
let t = await d.formValidator(p(), e), n = {};
|
|
161
|
+
if (t) for (let [e, r] of Object.entries(t)) o(e) && typeof r == "string" && (n[e] = r);
|
|
162
|
+
return n;
|
|
163
|
+
}
|
|
164
|
+
function U(e, t, n, r) {
|
|
165
|
+
if (r === "full") {
|
|
166
|
+
d.fieldErrors.clear();
|
|
167
|
+
for (let [e, r] of Object.entries(n)) t[e] === void 0 && (t[e] = r);
|
|
168
|
+
for (let [e, n] of Object.entries(t)) d.fieldErrors.set(e, n);
|
|
169
|
+
d.invalidateErrors(), d.requestNotify();
|
|
170
|
+
} else {
|
|
171
|
+
let n = /* @__PURE__ */ new Set();
|
|
172
|
+
for (let r of e) {
|
|
173
|
+
let e = t[r], i = d.fieldErrors.get(r);
|
|
174
|
+
e === void 0 ? d.fieldErrors.delete(r) && n.add(r) : (d.fieldErrors.set(r, e), i !== e && n.add(r));
|
|
175
|
+
}
|
|
176
|
+
n.size > 0 && d.invalidateErrors(), d.requestNotify(n);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async function W(e, t, n) {
|
|
180
|
+
let i = new Set(e), a = /* @__PURE__ */ new Map();
|
|
181
|
+
for (let e of i) {
|
|
182
|
+
d.fieldCtrls.get(e)?.abort();
|
|
183
|
+
let t = new AbortController();
|
|
184
|
+
d.fieldCtrls.set(e, t), a.set(e, t);
|
|
185
|
+
}
|
|
186
|
+
let o = new AbortController(), s = r(o.signal, n, d.disposeController.signal);
|
|
187
|
+
d.runCtrls.add(o);
|
|
188
|
+
let c = Symbol();
|
|
189
|
+
for (let e of i) {
|
|
190
|
+
let t = d.validatingRuns.get(e);
|
|
191
|
+
t || (t = /* @__PURE__ */ new Set(), d.validatingRuns.set(e, t)), t.add(c);
|
|
192
|
+
}
|
|
193
|
+
d.requestNotify();
|
|
194
|
+
try {
|
|
195
|
+
let e = await Promise.all([...i].map(async (e) => [e, await V(e, t === "partial" ? r(a.get(e).signal, s) : s)]));
|
|
196
|
+
if (s.aborted) return {
|
|
197
|
+
aborted: !0,
|
|
198
|
+
errors: Object.fromEntries(d.fieldErrors),
|
|
199
|
+
valid: d.fieldErrors.size === 0
|
|
200
|
+
};
|
|
201
|
+
let n = {};
|
|
202
|
+
for (let [t, r] of e) r !== void 0 && (n[t] = r);
|
|
203
|
+
return U(i, n, t === "full" ? await H(s) : {}, t), {
|
|
204
|
+
aborted: !1,
|
|
205
|
+
errors: Object.fromEntries(d.fieldErrors),
|
|
206
|
+
valid: d.fieldErrors.size === 0
|
|
207
|
+
};
|
|
208
|
+
} catch (e) {
|
|
209
|
+
if (u(e)) return {
|
|
210
|
+
aborted: !0,
|
|
211
|
+
errors: Object.fromEntries(d.fieldErrors),
|
|
212
|
+
valid: d.fieldErrors.size === 0
|
|
213
|
+
};
|
|
214
|
+
throw e;
|
|
215
|
+
} finally {
|
|
216
|
+
d.runCtrls.delete(o);
|
|
217
|
+
for (let e of i) {
|
|
218
|
+
let t = d.validatingRuns.get(e);
|
|
219
|
+
t && (t.delete(c), t.size === 0 && d.validatingRuns.delete(e));
|
|
220
|
+
}
|
|
221
|
+
d.requestNotify();
|
|
222
|
+
for (let [e, t] of a) d.fieldCtrls.get(e) === t && d.fieldCtrls.delete(e);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async function G(e, t) {
|
|
226
|
+
d.ensureNotDisposed("validate");
|
|
227
|
+
let n = await W(e, "partial", t);
|
|
228
|
+
return {
|
|
229
|
+
errors: n.errors,
|
|
230
|
+
valid: n.valid
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
async function K(e, t) {
|
|
234
|
+
if (d.ensureNotDisposed("validate"), e === void 0 || e instanceof AbortSignal) {
|
|
235
|
+
let t = await W([...d.validators.keys()], "full", e);
|
|
236
|
+
return {
|
|
237
|
+
errors: t.errors,
|
|
238
|
+
valid: t.valid
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (Array.isArray(e)) return G(e, t);
|
|
242
|
+
await G([e], t);
|
|
243
|
+
let n = e, r = d.fieldErrors.get(n);
|
|
244
|
+
return {
|
|
245
|
+
errors: r === void 0 ? {} : { [n]: r },
|
|
246
|
+
valid: r === void 0
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
async function q(e) {
|
|
250
|
+
let t = await J(e);
|
|
251
|
+
if (!t.ok) throw new n(t.errors);
|
|
252
|
+
return t.value;
|
|
253
|
+
}
|
|
254
|
+
async function J(e) {
|
|
255
|
+
if (d.ensureNotDisposed("submit"), d.isSubmittingState) throw new t("submit() called while a submission is already in progress");
|
|
256
|
+
d.batch(() => {
|
|
257
|
+
d.submitCount++, d.isSubmittingState = !0, O();
|
|
258
|
+
});
|
|
259
|
+
try {
|
|
260
|
+
let t = await W([...d.validators.keys()], "full");
|
|
261
|
+
return d.ensureNotDisposed("submit"), t.valid ? {
|
|
262
|
+
ok: !0,
|
|
263
|
+
value: await e(p())
|
|
264
|
+
} : {
|
|
265
|
+
errors: t.errors,
|
|
266
|
+
ok: !1,
|
|
267
|
+
type: "validation"
|
|
268
|
+
};
|
|
269
|
+
} finally {
|
|
270
|
+
d.isSubmittingState = !1, d.disposed || d.requestNotify();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
array: B,
|
|
275
|
+
clearError: v,
|
|
276
|
+
field: g,
|
|
277
|
+
get: f,
|
|
278
|
+
listFields: w,
|
|
279
|
+
patch: z,
|
|
280
|
+
patchKeys: R,
|
|
281
|
+
registerField: C,
|
|
282
|
+
removeField: M,
|
|
283
|
+
replace: L,
|
|
284
|
+
replaceKeys: I,
|
|
285
|
+
reset: F,
|
|
286
|
+
resetErrors: b,
|
|
287
|
+
resetErrorsKeys: y,
|
|
288
|
+
resetField: j,
|
|
289
|
+
resetKeys: P,
|
|
290
|
+
runValidationCore: W,
|
|
291
|
+
set: h,
|
|
292
|
+
setError: _,
|
|
293
|
+
setValidator: S,
|
|
294
|
+
submit: J,
|
|
295
|
+
submitOrThrow: q,
|
|
296
|
+
touch: T,
|
|
297
|
+
touchAll: O,
|
|
298
|
+
touchAllKeys: D,
|
|
299
|
+
untouch: E,
|
|
300
|
+
untouchAll: A,
|
|
301
|
+
untouchAllKeys: k,
|
|
302
|
+
validate: K,
|
|
303
|
+
validateFields: G,
|
|
304
|
+
values: p
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
//#endregion
|
|
308
|
+
export { d as createFieldOps };
|
|
309
|
+
|
|
310
|
+
//# sourceMappingURL=fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.js","names":[],"sources":["../../src/core/fields.ts"],"sourcesContent":["import { isAbortError } from '@vielzeug/arsenal';\n\nimport type { FormContext } from './context';\n\nimport { warn } from '../_dev';\nimport { anySignal, assertSafeKey, flattenValues, isSafeKey, sanitizeForLog, unflattenValues } from '../_utils';\nimport { ForgeSubmitError, ForgeValidationError } from '../errors';\nimport {\n type ArrayField,\n type ErrorKeyOf,\n type FieldState,\n type FieldValidator,\n type FlatKeyOf,\n type MaybePromise,\n type RegisterFieldOptions,\n type SetOptions,\n type SubmitResult,\n type TypeAtPath,\n type Unsubscribe,\n type ValidateResult,\n} from '../types';\nimport { createArrayField } from './array';\n\n/**\n * Field values, dirty/touch tracking, field-level errors, dynamic field registration, and\n * validation/submit — one module, not the two (`values.ts`/`validation.ts`) it used to be\n * split across. That split was drawn along \"which section of the original monolithic\n * `createForm()` this code used to live in,\" not along a real boundary: validating a field\n * needs the same store/fieldErrors/fieldCtrls state that setting one does, so the old split\n * required threading `deps: Pick<ValueOps, '...'>` between files just to call each other. One\n * closure removes that threading entirely.\n *\n * The six `*Keys(predicate)` functions below are the shared bulk-mutation primitives behind\n * `reset`/`replace`/`patch`/`touchAll`/`untouchAll`/`resetErrors`. `scope.ts` calls them with\n * an `isScopedKey` predicate instead of maintaining its own second copy of each loop.\n */\nexport function createFieldOps<TValues extends Record<string, unknown>>(ctx: FormContext<TValues>) {\n /* ======== Values ======== */\n\n function get<K extends FlatKeyOf<TValues>>(name: K): TypeAtPath<TValues, K> {\n const key = name as string;\n\n assertSafeKey(key);\n\n return ctx.store.get(key) as TypeAtPath<TValues, K>;\n }\n\n function values(): TValues {\n return (ctx.cachedValues ??= unflattenValues(Object.fromEntries(ctx.store)) as TValues);\n }\n\n function trackDirty(name: string, value: unknown): void {\n if (ctx.baseline.get(name) === value) ctx.dirty.delete(name);\n else ctx.dirty.add(name);\n }\n\n function set<K extends FlatKeyOf<TValues>>(name: K, value: TypeAtPath<TValues, K>, options: SetOptions = {}): void {\n ctx.ensureNotDisposed('set');\n\n const key = name as string;\n\n assertSafeKey(key);\n ctx.store.set(key, value);\n trackDirty(key, value);\n\n if (options.touched) ctx.touched.add(key);\n\n ctx.requestNotify(key);\n }\n\n function field<K extends FlatKeyOf<TValues>>(name: K): FieldState<TypeAtPath<TValues, K>> {\n const key = name as string;\n\n assertSafeKey(key);\n\n return ctx.getFieldSnapshot(key) as FieldState<TypeAtPath<TValues, K>>;\n }\n\n /* ======== Errors ======== */\n\n function setError(name: ErrorKeyOf<TValues>, message: string): void {\n ctx.ensureNotDisposed('setError');\n\n const key = name as string;\n\n assertSafeKey(key);\n ctx.fieldErrors.set(key, message);\n ctx.invalidateErrors();\n ctx.requestNotify(key);\n }\n\n function clearError(name: ErrorKeyOf<TValues>): void {\n ctx.ensureNotDisposed('clearError');\n\n const key = name as string;\n\n assertSafeKey(key);\n\n if (ctx.fieldErrors.delete(key)) {\n ctx.invalidateErrors();\n ctx.requestNotify(key);\n }\n }\n\n /**\n * Shared primitive behind `resetErrors()` (predicate `() => true`) and `scope.ts`'s scoped\n * `resetErrors()` (predicate `isScopedKey`, mapper `pre`). Clears every error matching\n * `predicate`, then applies `nextErrors` through `toFullKey`.\n */\n function resetErrorsKeys(\n predicate: (key: string) => boolean,\n nextErrors: Partial<Record<string, string | undefined>>,\n toFullKey: (key: string) => string,\n ): void {\n for (const key of [...ctx.fieldErrors.keys()]) {\n if (predicate(key)) ctx.fieldErrors.delete(key);\n }\n\n for (const [key, message] of Object.entries(nextErrors)) {\n const fullKey = toFullKey(key);\n\n assertSafeKey(fullKey);\n\n if (typeof message === 'string') ctx.fieldErrors.set(fullKey, message);\n }\n\n ctx.invalidateErrors();\n }\n\n function resetErrors(nextErrors: Partial<Record<ErrorKeyOf<TValues>, string | undefined>> = {}): void {\n ctx.ensureNotDisposed('resetErrors');\n resetErrorsKeys(\n () => true,\n nextErrors,\n (k) => k,\n );\n ctx.requestNotify();\n }\n\n /**\n * Warns when `key` looks like a dot-notation array item path (e.g. \"tags.0\") that isn't\n * already a real store entry — array items are stored as whole arrays, not individual keys.\n * Shared by `setValidator()` and `registerField()`.\n */\n function warnIfArrayItemKey(key: string, apiLabel: string): void {\n if (/\\.\\d+(\\.|$)/.test(key) && !ctx.store.has(key)) {\n const displayKey = sanitizeForLog(key, 80);\n\n warn(\n `${apiLabel}('${displayKey}'): path looks like an array item key. ` +\n `Array items are stored as whole arrays — register on the parent key instead.`,\n );\n }\n }\n\n function setValidator(name: FlatKeyOf<TValues>, validator?: FieldValidator<unknown>): void {\n ctx.ensureNotDisposed('setValidator');\n\n const key = name as string;\n\n assertSafeKey(key);\n warnIfArrayItemKey(key, 'setValidator');\n\n ctx.fieldCtrls.get(key)?.abort();\n ctx.fieldCtrls.delete(key);\n\n if (validator) {\n ctx.validators.set(key, validator);\n } else {\n ctx.validators.delete(key);\n\n if (ctx.fieldErrors.delete(key)) {\n ctx.invalidateErrors();\n ctx.requestNotify(key);\n }\n }\n }\n\n /* ======== Dynamic field registration ======== */\n\n function registerField<K extends FlatKeyOf<TValues>>(\n name: K,\n options: RegisterFieldOptions<TypeAtPath<TValues, K>> = {},\n ): Unsubscribe {\n ctx.ensureNotDisposed('fields.register');\n\n const key = name as string;\n\n assertSafeKey(key);\n warnIfArrayItemKey(key, 'fields.register');\n\n if (options.validator !== undefined) setValidator(name, options.validator as FieldValidator<unknown>);\n\n if (options.defaultValue !== undefined && !ctx.store.has(key)) {\n ctx.store.set(key, options.defaultValue);\n ctx.baseline.set(key, options.defaultValue);\n ctx.requestNotify(key);\n }\n\n return () => {\n if (!ctx.disposed) removeField(name);\n };\n }\n\n function listFields(): readonly string[] {\n const known = new Set<string>();\n\n for (const name of ctx.store.keys()) known.add(name);\n for (const name of ctx.validators.keys()) known.add(name);\n\n return Object.freeze([...known]);\n }\n\n /* ======== Touch ======== */\n\n function touch(name: FlatKeyOf<TValues>): void {\n ctx.ensureNotDisposed('touch');\n\n const key = name as string;\n\n assertSafeKey(key);\n ctx.touched.add(key);\n ctx.requestNotify(key);\n }\n\n function untouch(name: FlatKeyOf<TValues>): void {\n ctx.ensureNotDisposed('untouch');\n\n const key = name as string;\n\n assertSafeKey(key);\n\n if (ctx.touched.delete(key)) ctx.requestNotify(key);\n }\n\n /** Shared by `touchAll()` and scope.ts's scoped `touchAll()`. */\n function touchAllKeys(predicate: (key: string) => boolean): void {\n for (const name of ctx.store.keys()) if (predicate(name)) ctx.touched.add(name);\n for (const name of ctx.validators.keys()) if (predicate(name)) ctx.touched.add(name);\n }\n\n function touchAll(): void {\n ctx.ensureNotDisposed('touchAll');\n touchAllKeys(() => true);\n ctx.requestNotify();\n }\n\n /** Shared by `untouchAll()` and scope.ts's scoped `untouchAll()`. */\n function untouchAllKeys(predicate: (key: string) => boolean): void {\n for (const name of [...ctx.touched]) if (predicate(name)) ctx.touched.delete(name);\n }\n\n function untouchAll(): void {\n ctx.ensureNotDisposed('untouchAll');\n untouchAllKeys(() => true);\n ctx.requestNotify();\n }\n\n /* ======== Lifecycle: field operations ======== */\n\n function resetField(name: FlatKeyOf<TValues>): void {\n ctx.ensureNotDisposed('resetField');\n\n const key = name as string;\n\n assertSafeKey(key);\n ctx.fieldCtrls.get(key)?.abort();\n ctx.fieldCtrls.delete(key);\n ctx.store.set(key, ctx.baseline.get(key));\n ctx.dirty.delete(key);\n ctx.touched.delete(key);\n\n if (ctx.fieldErrors.delete(key)) ctx.invalidateErrors();\n\n ctx.requestNotify(key);\n }\n\n function removeField(name: FlatKeyOf<TValues>): void {\n ctx.ensureNotDisposed('fields.remove');\n\n const key = name as string;\n\n assertSafeKey(key);\n ctx.store.delete(key);\n ctx.baseline.delete(key);\n ctx.dirty.delete(key);\n ctx.touched.delete(key);\n ctx.validators.delete(key);\n ctx.arrayCache.delete(key);\n ctx.fieldCtrls.get(key)?.abort();\n ctx.fieldCtrls.delete(key);\n\n if (ctx.fieldErrors.delete(key)) ctx.invalidateErrors();\n\n ctx.requestNotify(key);\n }\n\n /**\n * Every key a bulk operation might need to touch — not just `store.keys()`. A validator-only\n * field (a validator with no matching store entry, e.g. a cross-field or virtual-name\n * validator) can still be `touched` or hold a `fieldErrors` entry, so `resetKeys`/\n * `replaceKeys` below must consider `touched`/`fieldErrors`/`validators` too, not just\n * `store` — deriving \"everything reset must clear\" from `store.keys()` alone silently leaves\n * such a field's touched/error state behind.\n */\n function allKnownKeys(): Set<string> {\n return new Set([...ctx.store.keys(), ...ctx.touched, ...ctx.fieldErrors.keys(), ...ctx.validators.keys()]);\n }\n\n /**\n * Shared primitive behind `reset()` (predicate `() => true`) and scope.ts's scoped\n * `reset()` (predicate `isScopedKey`). For each known key matching `predicate`: aborts its\n * in-flight field validation, restores its baseline value (or removes it entirely if it has\n * no baseline entry — e.g. a key written via `set()` alone, never through `patch()`), and\n * clears its dirty/touched/error state.\n */\n function resetKeys(predicate: (key: string) => boolean): void {\n for (const key of allKnownKeys()) {\n if (!predicate(key)) continue;\n\n ctx.fieldCtrls.get(key)?.abort();\n ctx.fieldCtrls.delete(key);\n\n // Both branches are no-ops for a validator-only key that was never in `store` to begin\n // with — `Map.delete()` on an absent key is a safe no-op, and `baseline.has(key)` is\n // false for it, so it falls through to `delete` rather than writing an `undefined` entry.\n if (ctx.baseline.has(key)) ctx.store.set(key, ctx.baseline.get(key));\n else ctx.store.delete(key);\n\n ctx.dirty.delete(key);\n ctx.touched.delete(key);\n\n if (ctx.fieldErrors.delete(key)) ctx.invalidateErrors();\n }\n }\n\n function reset(): void {\n ctx.ensureNotDisposed('reset');\n\n for (const ctrl of ctx.runCtrls) ctrl.abort();\n\n ctx.runCtrls.clear();\n ctx.validatingRuns.clear();\n resetKeys(() => true);\n ctx.submitCount = 0;\n ctx.requestNotify();\n }\n\n /**\n * Shared primitive behind `replace()` (predicate `() => true`, mapper `(k) => k`) and\n * scope.ts's scoped `replace()` (predicate `isScopedKey`, mapper `pre`). Removes every\n * store/baseline/touched/error entry matching `predicate` (see `allKnownKeys()` above for\n * why this isn't just `store.keys()`), then writes `flat` back in as both store and baseline\n * (a replace always establishes a new, clean baseline).\n */\n function replaceKeys(\n predicate: (key: string) => boolean,\n flat: Record<string, unknown>,\n toFullKey: (key: string) => string,\n ): void {\n for (const key of allKnownKeys()) {\n if (!predicate(key)) continue;\n\n ctx.fieldCtrls.get(key)?.abort();\n ctx.fieldCtrls.delete(key);\n ctx.store.delete(key);\n ctx.baseline.delete(key);\n ctx.dirty.delete(key);\n ctx.touched.delete(key);\n\n if (ctx.fieldErrors.delete(key)) ctx.invalidateErrors();\n }\n\n for (const [key, value] of Object.entries(flat)) {\n const fullKey = toFullKey(key);\n\n ctx.store.set(fullKey, value);\n ctx.baseline.set(fullKey, value);\n }\n }\n\n function replace(newValues: TValues): void {\n ctx.ensureNotDisposed('replace');\n\n for (const ctrl of ctx.runCtrls) ctrl.abort();\n\n ctx.runCtrls.clear();\n ctx.validatingRuns.clear();\n replaceKeys(\n () => true,\n flattenValues(newValues as Record<string, unknown>),\n (k) => k,\n );\n ctx.submitCount = 0;\n ctx.requestNotify();\n }\n\n /**\n * Shared primitive behind `patch()` and scope.ts's scoped `patch()` (mapper `pre`). Unlike\n * `resetKeys`/`replaceKeys`, `patch()` never needs a predicate — the partial object itself\n * already names exactly the keys to touch. Returns the full (prefixed) keys written, for\n * the caller to pass to `requestNotify()`.\n */\n function patchKeys(partial: Record<string, unknown>, toFullKey: (key: string) => string): string[] {\n const flat = flattenValues(partial);\n const changedKeys: string[] = [];\n\n for (const [key, value] of Object.entries(flat)) {\n const fullKey = toFullKey(key);\n\n ctx.baseline.set(fullKey, value);\n ctx.store.set(fullKey, value);\n ctx.dirty.delete(fullKey);\n changedKeys.push(fullKey);\n }\n\n return changedKeys;\n }\n\n function patch(partial: Record<string, unknown>): void {\n ctx.ensureNotDisposed('patch');\n ctx.requestNotify(patchKeys(partial, (k) => k));\n }\n\n /* ======== Array helpers ======== */\n\n function array(name: FlatKeyOf<TValues>): ArrayField {\n ctx.ensureNotDisposed('array');\n\n const key = name as string;\n\n assertSafeKey(key);\n\n const cached = ctx.arrayCache.get(key);\n\n if (cached) return cached;\n\n const helpers = createArrayField(name, ctx.store, set as (name: string, value: unknown) => void);\n\n ctx.arrayCache.set(key, helpers);\n\n return helpers;\n }\n\n /* ======== Validation ======== */\n\n async function runFieldValidator(name: string, signal: AbortSignal): Promise<string | undefined> {\n const validator = ctx.validators.get(name);\n\n if (!validator) return undefined;\n\n if (signal.aborted) throw signal.reason;\n\n const result = await validator(ctx.store.get(name), signal);\n\n return typeof result === 'string' ? result : undefined;\n }\n\n async function runFormValidator(signal: AbortSignal): Promise<Record<string, string>> {\n if (!ctx.formValidator) return {};\n\n if (signal.aborted) throw signal.reason;\n\n const result = await ctx.formValidator(values(), signal);\n const errors: Record<string, string> = {};\n\n if (result) {\n for (const [key, message] of Object.entries(result)) {\n // Security: a custom FormValidator function is caller-authored code — its returned\n // keys are just as untrusted as a schema's `issue.path`. Reject reserved segments.\n if (isSafeKey(key) && typeof message === 'string') errors[key] = message;\n }\n }\n\n return errors;\n }\n\n function applyFieldErrors(\n fieldSet: Set<string>,\n nextErrors: Record<string, string>,\n formErrors: Record<string, string>,\n scope: 'full' | 'partial',\n ): void {\n if (scope === 'full') {\n ctx.fieldErrors.clear();\n\n for (const [key, message] of Object.entries(formErrors)) {\n if (nextErrors[key] === undefined) nextErrors[key] = message;\n }\n\n for (const [key, message] of Object.entries(nextErrors)) ctx.fieldErrors.set(key, message);\n\n ctx.invalidateErrors();\n ctx.requestNotify();\n } else {\n const changedFields = new Set<string>();\n\n for (const name of fieldSet) {\n const next = nextErrors[name];\n const previous = ctx.fieldErrors.get(name);\n\n if (next !== undefined) {\n ctx.fieldErrors.set(name, next);\n\n if (previous !== next) changedFields.add(name);\n } else if (ctx.fieldErrors.delete(name)) {\n changedFields.add(name);\n }\n }\n\n if (changedFields.size > 0) ctx.invalidateErrors();\n\n ctx.requestNotify(changedFields);\n }\n }\n\n async function runValidationCore(\n fields: string[],\n scope: 'full' | 'partial',\n signal?: AbortSignal,\n ): Promise<ValidateResult & { aborted: boolean }> {\n const fieldSet = new Set(fields);\n const fieldRunCtrls = new Map<string, AbortController>();\n\n for (const name of fieldSet) {\n ctx.fieldCtrls.get(name)?.abort();\n\n const ctrl = new AbortController();\n\n ctx.fieldCtrls.set(name, ctrl);\n fieldRunCtrls.set(name, ctrl);\n }\n\n const runCtrl = new AbortController();\n const runSignal = anySignal(runCtrl.signal, signal, ctx.disposeController.signal)!;\n\n ctx.runCtrls.add(runCtrl);\n\n // Symbol-based ref-counting: each run gets a unique token per field.\n // The field leaves validatingFields only when its Set empties — impossible to miscount.\n const runId = Symbol();\n\n for (const name of fieldSet) {\n let runs = ctx.validatingRuns.get(name);\n\n if (!runs) {\n runs = new Set<symbol>();\n ctx.validatingRuns.set(name, runs);\n }\n\n runs.add(runId);\n }\n\n ctx.requestNotify();\n\n try {\n const results = await Promise.all(\n [...fieldSet].map(async (name) => {\n const fieldSignal = scope === 'partial' ? anySignal(fieldRunCtrls.get(name)!.signal, runSignal)! : runSignal;\n\n return [name, await runFieldValidator(name, fieldSignal)] as const;\n }),\n );\n\n if (runSignal.aborted) {\n return { aborted: true, errors: Object.fromEntries(ctx.fieldErrors), valid: ctx.fieldErrors.size === 0 };\n }\n\n const nextErrors: Record<string, string> = {};\n\n for (const [name, message] of results) {\n if (message !== undefined) nextErrors[name] = message;\n }\n\n const formErrors = scope === 'full' ? await runFormValidator(runSignal) : {};\n\n applyFieldErrors(fieldSet, nextErrors, formErrors, scope);\n\n return { aborted: false, errors: Object.fromEntries(ctx.fieldErrors), valid: ctx.fieldErrors.size === 0 };\n } catch (error) {\n if (isAbortError(error)) {\n return { aborted: true, errors: Object.fromEntries(ctx.fieldErrors), valid: ctx.fieldErrors.size === 0 };\n }\n\n throw error;\n } finally {\n ctx.runCtrls.delete(runCtrl);\n\n for (const name of fieldSet) {\n const runs = ctx.validatingRuns.get(name);\n\n if (runs) {\n runs.delete(runId);\n\n if (runs.size === 0) ctx.validatingRuns.delete(name);\n }\n }\n\n ctx.requestNotify();\n\n for (const [name, ctrl] of fieldRunCtrls) {\n if (ctx.fieldCtrls.get(name) === ctrl) ctx.fieldCtrls.delete(name);\n }\n }\n }\n\n async function validateFields(\n fields: FlatKeyOf<TValues>[] | string[],\n signal?: AbortSignal,\n ): Promise<ValidateResult> {\n ctx.ensureNotDisposed('validate');\n\n const result = await runValidationCore(fields as string[], 'partial', signal);\n\n return { errors: result.errors, valid: result.valid };\n }\n\n async function validate(\n nameOrFieldsOrSignal?: FlatKeyOf<TValues> | FlatKeyOf<TValues>[] | AbortSignal,\n signal?: AbortSignal,\n ): Promise<ValidateResult> {\n ctx.ensureNotDisposed('validate');\n\n if (nameOrFieldsOrSignal === undefined || nameOrFieldsOrSignal instanceof AbortSignal) {\n const result = await runValidationCore(\n [...ctx.validators.keys()],\n 'full',\n nameOrFieldsOrSignal as AbortSignal | undefined,\n );\n\n return { errors: result.errors, valid: result.valid };\n }\n\n if (Array.isArray(nameOrFieldsOrSignal)) {\n return validateFields(nameOrFieldsOrSignal, signal);\n }\n\n await validateFields([nameOrFieldsOrSignal], signal);\n\n const name = nameOrFieldsOrSignal as string;\n const error = ctx.fieldErrors.get(name);\n\n return { errors: error !== undefined ? { [name]: error } : {}, valid: error === undefined };\n }\n\n /* ======== Submit ======== */\n\n async function submitOrThrow<TResult = void>(handler: (values: TValues) => MaybePromise<TResult>): Promise<TResult> {\n const result = await submit(handler);\n\n if (!result.ok) throw new ForgeValidationError(result.errors as Record<string, string>);\n\n return result.value;\n }\n\n async function submit<TResult = void>(\n handler: (values: TValues) => MaybePromise<TResult>,\n ): Promise<SubmitResult<TResult>> {\n ctx.ensureNotDisposed('submit');\n\n if (ctx.isSubmittingState) throw new ForgeSubmitError('submit() called while a submission is already in progress');\n\n ctx.batch(() => {\n ctx.submitCount++;\n ctx.isSubmittingState = true;\n touchAll();\n });\n\n try {\n const validation = await runValidationCore([...ctx.validators.keys()], 'full');\n\n ctx.ensureNotDisposed('submit');\n\n if (!validation.valid) {\n return { errors: validation.errors, ok: false, type: 'validation' };\n }\n\n return { ok: true, value: await handler(values()) };\n } finally {\n ctx.isSubmittingState = false;\n\n if (!ctx.disposed) ctx.requestNotify();\n }\n }\n\n return {\n array,\n clearError,\n field,\n get,\n listFields,\n patch,\n patchKeys,\n registerField,\n removeField,\n replace,\n replaceKeys,\n reset,\n resetErrors,\n resetErrorsKeys,\n resetField,\n resetKeys,\n runValidationCore,\n set,\n setError,\n setValidator,\n submit,\n submitOrThrow,\n touch,\n touchAll,\n touchAllKeys,\n untouch,\n untouchAll,\n untouchAllKeys,\n validate,\n validateFields,\n values,\n };\n}\n\nexport type FieldOps<TValues extends Record<string, unknown>> = ReturnType<typeof createFieldOps<TValues>>;\n"],"mappings":"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"}
|
package/dist/core/lifecycle.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../_utils.cjs");function t(t){function
|
|
1
|
+
const e=require("../_utils.cjs");function t(t,n){function r(){t.dispose(),n.onDispose?.()}function i(){return t.ensureNotDisposed(`history.snapshot`),Object.freeze({baseline:Object.freeze(Object.fromEntries(t.baseline)),dirty:Object.freeze([...t.dirty]),errors:Object.freeze(Object.fromEntries(t.fieldErrors)),store:Object.freeze(Object.fromEntries(t.store)),submitCount:t.submitCount,touched:Object.freeze([...t.touched])})}function a(n){t.ensureNotDisposed(`history.restore`);for(let e of t.runCtrls)e.abort();for(let e of t.fieldCtrls.values())e.abort();t.runCtrls.clear(),t.fieldCtrls.clear(),t.validatingRuns.clear(),t.store.clear(),t.baseline.clear(),t.fieldErrors.clear(),t.touched.clear(),t.dirty.clear();for(let[r,i]of Object.entries(n.store))e.isSafeKey(r)&&t.store.set(r,i);for(let[r,i]of Object.entries(n.baseline))e.isSafeKey(r)&&t.baseline.set(r,i);for(let[r,i]of Object.entries(n.errors))e.isSafeKey(r)&&t.fieldErrors.set(r,i);for(let r of n.touched)e.isSafeKey(r)&&t.touched.add(r);for(let r of n.dirty)e.isSafeKey(r)&&t.dirty.add(r);t.submitCount=n.submitCount,t.invalidateErrors(),t.invalidateValues(),t.requestNotify()}return{dispose:r,restore:a,snapshot:i}}exports.createLifecycleOps=t;
|
|
2
2
|
//# sourceMappingURL=lifecycle.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.cjs","names":[],"sources":["../../src/core/lifecycle.ts"],"sourcesContent":["import type { FormSnapshot } from '../types';\nimport type { FormContext } from './context';\n\nimport { isSafeKey } from '../_utils';\n\n/**\n * Lifecycle operations: `dispose`, `snapshot`, `restore`. Moved verbatim from `createForm()`'s\n * \"Lifecycle: dispose\" and \"Snapshot / Restore\" sections.\n */\nexport function createLifecycleOps<TValues extends Record<string, unknown>>(ctx: FormContext<TValues
|
|
1
|
+
{"version":3,"file":"lifecycle.cjs","names":[],"sources":["../../src/core/lifecycle.ts"],"sourcesContent":["import type { FormSnapshot } from '../types';\nimport type { FormContext } from './context';\n\nimport { isSafeKey } from '../_utils';\n\n/**\n * Lifecycle operations: `dispose`, `snapshot`, `restore`. Moved verbatim from `createForm()`'s\n * \"Lifecycle: dispose\" and \"Snapshot / Restore\" sections.\n */\nexport function createLifecycleOps<TValues extends Record<string, unknown>>(\n ctx: FormContext<TValues>,\n deps: { onDispose?: () => void },\n) {\n function dispose(): void {\n ctx.dispose();\n deps.onDispose?.();\n }\n\n function snapshot(): FormSnapshot<TValues> {\n ctx.ensureNotDisposed('history.snapshot');\n\n // Cast is irreducible: baseline/store are typed Partial<Record<FlatKeyOf<TValues>, unknown>>, a\n // TValues-dependent mapped type TS cannot verify a plain Object.fromEntries() result against for\n // a generic TValues param -- no runtime behavior is affected, only static verification.\n return Object.freeze({\n baseline: Object.freeze(Object.fromEntries(ctx.baseline)),\n dirty: Object.freeze([...ctx.dirty]),\n errors: Object.freeze(Object.fromEntries(ctx.fieldErrors)),\n store: Object.freeze(Object.fromEntries(ctx.store)),\n submitCount: ctx.submitCount,\n touched: Object.freeze([...ctx.touched]),\n }) as FormSnapshot<TValues>;\n }\n\n function restore(snap: FormSnapshot<TValues>): void {\n ctx.ensureNotDisposed('history.restore');\n\n for (const ctrl of ctx.runCtrls) ctrl.abort();\n for (const ctrl of ctx.fieldCtrls.values()) ctrl.abort();\n\n ctx.runCtrls.clear();\n ctx.fieldCtrls.clear();\n ctx.validatingRuns.clear();\n ctx.store.clear();\n ctx.baseline.clear();\n ctx.fieldErrors.clear();\n ctx.touched.clear();\n ctx.dirty.clear();\n\n // Security: snap may be an externally-sourced blob (e.g. JSON.parse'd persisted state).\n // Unlike patch()/replace(), restore() writes flat keys directly into the internal Maps —\n // it must apply the same silent-drop defense-in-depth as flattenValues() does for those.\n for (const [k, v] of Object.entries(snap.store)) if (isSafeKey(k)) ctx.store.set(k, v);\n for (const [k, v] of Object.entries(snap.baseline)) if (isSafeKey(k)) ctx.baseline.set(k, v);\n for (const [k, v] of Object.entries(snap.errors)) if (isSafeKey(k)) ctx.fieldErrors.set(k, v);\n for (const t of snap.touched) if (isSafeKey(t)) ctx.touched.add(t);\n for (const d of snap.dirty) if (isSafeKey(d)) ctx.dirty.add(d);\n\n ctx.submitCount = snap.submitCount;\n\n ctx.invalidateErrors();\n ctx.invalidateValues();\n ctx.requestNotify();\n }\n\n return { dispose, restore, snapshot };\n}\n\nexport type LifecycleOps<TValues extends Record<string, unknown>> = ReturnType<typeof createLifecycleOps<TValues>>;\n"],"mappings":"iCASA,SAAgB,EACd,EACA,EACA,CACA,SAAS,GAAgB,CACvB,EAAI,QAAQ,EACZ,EAAK,YAAY,CACnB,CAEA,SAAS,GAAkC,CAMzC,OALA,EAAI,kBAAkB,kBAAkB,EAKjC,OAAO,OAAO,CACnB,SAAU,OAAO,OAAO,OAAO,YAAY,EAAI,QAAQ,CAAC,EACxD,MAAO,OAAO,OAAO,CAAC,GAAG,EAAI,KAAK,CAAC,EACnC,OAAQ,OAAO,OAAO,OAAO,YAAY,EAAI,WAAW,CAAC,EACzD,MAAO,OAAO,OAAO,OAAO,YAAY,EAAI,KAAK,CAAC,EAClD,YAAa,EAAI,YACjB,QAAS,OAAO,OAAO,CAAC,GAAG,EAAI,OAAO,CAAC,CACzC,CAAC,CACH,CAEA,SAAS,EAAQ,EAAmC,CAClD,EAAI,kBAAkB,iBAAiB,EAEvC,IAAK,IAAM,KAAQ,EAAI,SAAU,EAAK,MAAM,EAC5C,IAAK,IAAM,KAAQ,EAAI,WAAW,OAAO,EAAG,EAAK,MAAM,EAEvD,EAAI,SAAS,MAAM,EACnB,EAAI,WAAW,MAAM,EACrB,EAAI,eAAe,MAAM,EACzB,EAAI,MAAM,MAAM,EAChB,EAAI,SAAS,MAAM,EACnB,EAAI,YAAY,MAAM,EACtB,EAAI,QAAQ,MAAM,EAClB,EAAI,MAAM,MAAM,EAKhB,IAAK,GAAM,CAAC,EAAG,KAAM,OAAO,QAAQ,EAAK,KAAK,EAAO,EAAA,UAAU,CAAC,GAAG,EAAI,MAAM,IAAI,EAAG,CAAC,EACrF,IAAK,GAAM,CAAC,EAAG,KAAM,OAAO,QAAQ,EAAK,QAAQ,EAAO,EAAA,UAAU,CAAC,GAAG,EAAI,SAAS,IAAI,EAAG,CAAC,EAC3F,IAAK,GAAM,CAAC,EAAG,KAAM,OAAO,QAAQ,EAAK,MAAM,EAAO,EAAA,UAAU,CAAC,GAAG,EAAI,YAAY,IAAI,EAAG,CAAC,EAC5F,IAAK,IAAM,KAAK,EAAK,QAAa,EAAA,UAAU,CAAC,GAAG,EAAI,QAAQ,IAAI,CAAC,EACjE,IAAK,IAAM,KAAK,EAAK,MAAW,EAAA,UAAU,CAAC,GAAG,EAAI,MAAM,IAAI,CAAC,EAE7D,EAAI,YAAc,EAAK,YAEvB,EAAI,iBAAiB,EACrB,EAAI,iBAAiB,EACrB,EAAI,cAAc,CACpB,CAEA,MAAO,CAAE,UAAS,UAAS,UAAS,CACtC"}
|
package/dist/core/lifecycle.d.ts
CHANGED
|
@@ -4,7 +4,9 @@ import type { FormContext } from './context';
|
|
|
4
4
|
* Lifecycle operations: `dispose`, `snapshot`, `restore`. Moved verbatim from `createForm()`'s
|
|
5
5
|
* "Lifecycle: dispose" and "Snapshot / Restore" sections.
|
|
6
6
|
*/
|
|
7
|
-
export declare function createLifecycleOps<TValues extends Record<string, unknown>>(ctx: FormContext<TValues
|
|
7
|
+
export declare function createLifecycleOps<TValues extends Record<string, unknown>>(ctx: FormContext<TValues>, deps: {
|
|
8
|
+
onDispose?: () => void;
|
|
9
|
+
}): {
|
|
8
10
|
dispose: () => void;
|
|
9
11
|
restore: (snap: FormSnapshot<TValues>) => void;
|
|
10
12
|
snapshot: () => FormSnapshot<TValues>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/core/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAI7C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/core/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAI7C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxE,GAAG,EAAE,WAAW,CAAC,OAAO,CAAC,EACzB,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE;mBAEZ,IAAI;oBAqBD,YAAY,CAAC,OAAO,CAAC,KAAG,IAAI;oBAhB9B,YAAY,CAAC,OAAO,CAAC;EAgD3C;AAED,MAAM,MAAM,YAAY,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC"}
|
package/dist/core/lifecycle.js
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import { isSafeKey as e } from "../_utils.js";
|
|
2
2
|
//#region src/core/lifecycle.ts
|
|
3
|
-
function t(t) {
|
|
4
|
-
function n() {
|
|
5
|
-
t.disposed = !0, t.disposeController.abort();
|
|
6
|
-
for (let e of t.fieldCtrls.values()) e.abort();
|
|
7
|
-
t.fieldCtrls.clear(), t.runCtrls.clear(), t.arrayCache.clear(), t.scopeCache.clear();
|
|
8
|
-
for (let e of t.rippleSubs) e.dispose();
|
|
9
|
-
t.rippleSubs.clear(), t.fieldSignals.clear();
|
|
10
|
-
}
|
|
3
|
+
function t(t, n) {
|
|
11
4
|
function r() {
|
|
12
|
-
|
|
5
|
+
t.dispose(), n.onDispose?.();
|
|
6
|
+
}
|
|
7
|
+
function i() {
|
|
8
|
+
return t.ensureNotDisposed("history.snapshot"), Object.freeze({
|
|
13
9
|
baseline: Object.freeze(Object.fromEntries(t.baseline)),
|
|
14
10
|
dirty: Object.freeze([...t.dirty]),
|
|
15
11
|
errors: Object.freeze(Object.fromEntries(t.fieldErrors)),
|
|
@@ -18,8 +14,8 @@ function t(t) {
|
|
|
18
14
|
touched: Object.freeze([...t.touched])
|
|
19
15
|
});
|
|
20
16
|
}
|
|
21
|
-
function
|
|
22
|
-
t.ensureNotDisposed();
|
|
17
|
+
function a(n) {
|
|
18
|
+
t.ensureNotDisposed("history.restore");
|
|
23
19
|
for (let e of t.runCtrls) e.abort();
|
|
24
20
|
for (let e of t.fieldCtrls.values()) e.abort();
|
|
25
21
|
t.runCtrls.clear(), t.fieldCtrls.clear(), t.validatingRuns.clear(), t.store.clear(), t.baseline.clear(), t.fieldErrors.clear(), t.touched.clear(), t.dirty.clear();
|
|
@@ -31,9 +27,9 @@ function t(t) {
|
|
|
31
27
|
t.submitCount = n.submitCount, t.invalidateErrors(), t.invalidateValues(), t.requestNotify();
|
|
32
28
|
}
|
|
33
29
|
return {
|
|
34
|
-
dispose:
|
|
35
|
-
restore:
|
|
36
|
-
snapshot:
|
|
30
|
+
dispose: r,
|
|
31
|
+
restore: a,
|
|
32
|
+
snapshot: i
|
|
37
33
|
};
|
|
38
34
|
}
|
|
39
35
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.js","names":[],"sources":["../../src/core/lifecycle.ts"],"sourcesContent":["import type { FormSnapshot } from '../types';\nimport type { FormContext } from './context';\n\nimport { isSafeKey } from '../_utils';\n\n/**\n * Lifecycle operations: `dispose`, `snapshot`, `restore`. Moved verbatim from `createForm()`'s\n * \"Lifecycle: dispose\" and \"Snapshot / Restore\" sections.\n */\nexport function createLifecycleOps<TValues extends Record<string, unknown>>(ctx: FormContext<TValues
|
|
1
|
+
{"version":3,"file":"lifecycle.js","names":[],"sources":["../../src/core/lifecycle.ts"],"sourcesContent":["import type { FormSnapshot } from '../types';\nimport type { FormContext } from './context';\n\nimport { isSafeKey } from '../_utils';\n\n/**\n * Lifecycle operations: `dispose`, `snapshot`, `restore`. Moved verbatim from `createForm()`'s\n * \"Lifecycle: dispose\" and \"Snapshot / Restore\" sections.\n */\nexport function createLifecycleOps<TValues extends Record<string, unknown>>(\n ctx: FormContext<TValues>,\n deps: { onDispose?: () => void },\n) {\n function dispose(): void {\n ctx.dispose();\n deps.onDispose?.();\n }\n\n function snapshot(): FormSnapshot<TValues> {\n ctx.ensureNotDisposed('history.snapshot');\n\n // Cast is irreducible: baseline/store are typed Partial<Record<FlatKeyOf<TValues>, unknown>>, a\n // TValues-dependent mapped type TS cannot verify a plain Object.fromEntries() result against for\n // a generic TValues param -- no runtime behavior is affected, only static verification.\n return Object.freeze({\n baseline: Object.freeze(Object.fromEntries(ctx.baseline)),\n dirty: Object.freeze([...ctx.dirty]),\n errors: Object.freeze(Object.fromEntries(ctx.fieldErrors)),\n store: Object.freeze(Object.fromEntries(ctx.store)),\n submitCount: ctx.submitCount,\n touched: Object.freeze([...ctx.touched]),\n }) as FormSnapshot<TValues>;\n }\n\n function restore(snap: FormSnapshot<TValues>): void {\n ctx.ensureNotDisposed('history.restore');\n\n for (const ctrl of ctx.runCtrls) ctrl.abort();\n for (const ctrl of ctx.fieldCtrls.values()) ctrl.abort();\n\n ctx.runCtrls.clear();\n ctx.fieldCtrls.clear();\n ctx.validatingRuns.clear();\n ctx.store.clear();\n ctx.baseline.clear();\n ctx.fieldErrors.clear();\n ctx.touched.clear();\n ctx.dirty.clear();\n\n // Security: snap may be an externally-sourced blob (e.g. JSON.parse'd persisted state).\n // Unlike patch()/replace(), restore() writes flat keys directly into the internal Maps —\n // it must apply the same silent-drop defense-in-depth as flattenValues() does for those.\n for (const [k, v] of Object.entries(snap.store)) if (isSafeKey(k)) ctx.store.set(k, v);\n for (const [k, v] of Object.entries(snap.baseline)) if (isSafeKey(k)) ctx.baseline.set(k, v);\n for (const [k, v] of Object.entries(snap.errors)) if (isSafeKey(k)) ctx.fieldErrors.set(k, v);\n for (const t of snap.touched) if (isSafeKey(t)) ctx.touched.add(t);\n for (const d of snap.dirty) if (isSafeKey(d)) ctx.dirty.add(d);\n\n ctx.submitCount = snap.submitCount;\n\n ctx.invalidateErrors();\n ctx.invalidateValues();\n ctx.requestNotify();\n }\n\n return { dispose, restore, snapshot };\n}\n\nexport type LifecycleOps<TValues extends Record<string, unknown>> = ReturnType<typeof createLifecycleOps<TValues>>;\n"],"mappings":";;AASA,SAAgB,EACd,GACA,GACA;CACA,SAAS,IAAgB;EAEvB,AADA,EAAI,QAAQ,GACZ,EAAK,YAAY;CACnB;CAEA,SAAS,IAAkC;EAMzC,OALA,EAAI,kBAAkB,kBAAkB,GAKjC,OAAO,OAAO;GACnB,UAAU,OAAO,OAAO,OAAO,YAAY,EAAI,QAAQ,CAAC;GACxD,OAAO,OAAO,OAAO,CAAC,GAAG,EAAI,KAAK,CAAC;GACnC,QAAQ,OAAO,OAAO,OAAO,YAAY,EAAI,WAAW,CAAC;GACzD,OAAO,OAAO,OAAO,OAAO,YAAY,EAAI,KAAK,CAAC;GAClD,aAAa,EAAI;GACjB,SAAS,OAAO,OAAO,CAAC,GAAG,EAAI,OAAO,CAAC;EACzC,CAAC;CACH;CAEA,SAAS,EAAQ,GAAmC;EAClD,EAAI,kBAAkB,iBAAiB;EAEvC,KAAK,IAAM,KAAQ,EAAI,UAAU,EAAK,MAAM;EAC5C,KAAK,IAAM,KAAQ,EAAI,WAAW,OAAO,GAAG,EAAK,MAAM;EASvD,AAPA,EAAI,SAAS,MAAM,GACnB,EAAI,WAAW,MAAM,GACrB,EAAI,eAAe,MAAM,GACzB,EAAI,MAAM,MAAM,GAChB,EAAI,SAAS,MAAM,GACnB,EAAI,YAAY,MAAM,GACtB,EAAI,QAAQ,MAAM,GAClB,EAAI,MAAM,MAAM;EAKhB,KAAK,IAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,EAAK,KAAK,GAAG,AAAI,EAAU,CAAC,KAAG,EAAI,MAAM,IAAI,GAAG,CAAC;EACrF,KAAK,IAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,EAAK,QAAQ,GAAG,AAAI,EAAU,CAAC,KAAG,EAAI,SAAS,IAAI,GAAG,CAAC;EAC3F,KAAK,IAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,EAAK,MAAM,GAAG,AAAI,EAAU,CAAC,KAAG,EAAI,YAAY,IAAI,GAAG,CAAC;EAC5F,KAAK,IAAM,KAAK,EAAK,SAAS,AAAI,EAAU,CAAC,KAAG,EAAI,QAAQ,IAAI,CAAC;EACjE,KAAK,IAAM,KAAK,EAAK,OAAO,AAAI,EAAU,CAAC,KAAG,EAAI,MAAM,IAAI,CAAC;EAM7D,AAJA,EAAI,cAAc,EAAK,aAEvB,EAAI,iBAAiB,GACrB,EAAI,iBAAiB,GACrB,EAAI,cAAc;CACpB;CAEA,OAAO;EAAE;EAAS;EAAS;CAAS;AACtC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../_utils.cjs");let t=require("@vielzeug/ripple");function n(n){let r=(0,t.signal)(n.computeState()),i=new Map,a=new Map,o=new Set,s=!1;function c(e){let r=i.get(e);return r||(r=(0,t.signal)(n.buildFieldState(e),{equals:(e,t)=>e.value===t.value&&e.error===t.error&&e.hasError===t.hasError&&e.touched===t.touched&&e.dirty===t.dirty}),i.set(e,r)),r}function l(e){let t=a.get(e);if(t)return t;let r=Object.freeze(n.buildFieldState(e));return a.set(e,r),r}function u(){return r.value}function d(e){if(!s)if(n.invalidateCaches(),e===void 0)a.clear(),(0,t.batch)(()=>{r.value=n.computeState();for(let[e,t]of i)t.value=n.buildFieldState(e)});else if(typeof e==`string`)a.delete(e),(0,t.batch)(()=>{r.value=n.computeState();let t=i.get(e);t&&(t.value=n.buildFieldState(e))});else{let o=[...e];for(let e of o)a.delete(e);(0,t.batch)(()=>{r.value=n.computeState();for(let e of o){let t=i.get(e);t&&(t.value=n.buildFieldState(e))}})}}function f(e,n){if(s)return()=>{};let i=(0,t.watch)(r,t=>{e(t)});return o.add(i),n?.sync&&e(r.value),()=>{i.dispose(),o.delete(i)}}function p(n,r,i){if(s)return()=>{};e.assertSafeKey(n);let a=c(n),l=(0,t.watch)(a,e=>{r(e)});return o.add(l),i?.sync&&r(a.value),()=>{l.dispose(),o.delete(l)}}function m(){s=!0;for(let e of o)e.dispose();o.clear(),i.clear(),a.clear()}return{dispose:m,getFieldSnapshot:l,getOrCreateFieldSignal:c,getStateSnapshot:u,requestNotify:d,subscribe:f,subscribeField:p}}exports.createNotifier=n;
|
|
2
|
+
//# sourceMappingURL=notifier.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifier.cjs","names":[],"sources":["../../src/core/notifier.ts"],"sourcesContent":["import { batch as rippleBatch, signal, watch, type Signal } from '@vielzeug/ripple';\n\nimport type { FieldState, FormState, SubscribeOptions, Unsubscribe } from '../types';\n\nimport { assertSafeKey } from '../_utils';\n\n/**\n * Everything needed to (re)compute fresh state on demand. Supplied by whoever owns the\n * actual field/validator Maps (`core/context.ts`, backing `core/fields.ts`) so this module\n * never touches those Maps directly — it only knows \"how to get current state\" and \"who's\n * listening,\" never \"where the state lives.\"\n */\nexport type NotifierDeps = {\n buildFieldState(name: string): FieldState<unknown>;\n computeState(): FormState;\n /** Called once at the top of every requestNotify(), before state is recomputed — lets the\n * caller drop its own memoized derived values (e.g. `values()`'s cached unflattened object). */\n invalidateCaches(): void;\n};\n\n/**\n * Owns every signal, subscription, and derived-state cache a form needs to notify observers.\n * Extracted from the old shared `FormContext` god-object (see `core/context.ts`'s header\n * comment) — this is the one piece of that object with no real dependency on field values or\n * validators, only on \"what does current state look like\" (via `NotifierDeps`), so it's safe\n * to own its signals/caches privately instead of exposing them on a shared mutable bag.\n */\nexport function createNotifier(deps: NotifierDeps) {\n const formStateSignal: Signal<FormState> = signal(deps.computeState());\n const fieldSignals = new Map<string, Signal<FieldState<unknown>>>();\n const fieldStateCache = new Map<string, FieldState<unknown>>();\n const rippleSubs = new Set<{ dispose(): void }>();\n let disposed = false;\n\n function getOrCreateFieldSignal(key: string): Signal<FieldState<unknown>> {\n let sig = fieldSignals.get(key);\n\n if (!sig) {\n sig = signal<FieldState<unknown>>(deps.buildFieldState(key), {\n equals: (a, b) =>\n a.value === b.value &&\n a.error === b.error &&\n a.hasError === b.hasError &&\n a.touched === b.touched &&\n a.dirty === b.dirty,\n });\n fieldSignals.set(key, sig);\n }\n\n return sig;\n }\n\n function getFieldSnapshot(name: string): FieldState<unknown> {\n const cached = fieldStateCache.get(name);\n\n if (cached) return cached;\n\n const snap = Object.freeze(deps.buildFieldState(name));\n\n fieldStateCache.set(name, snap);\n\n return snap;\n }\n\n function getStateSnapshot(): FormState {\n return formStateSignal.value;\n }\n\n /**\n * Unified notification — a single write path for every mutation in the package.\n * - `undefined` → full-form refresh (all field signals + form signal)\n * - `string` → single field + form signal\n * - `Iterable` → targeted set of fields + form signal\n * `rippleBatch` deduplicates signal writes within the synchronous call stack.\n */\n function requestNotify(target?: string | Iterable<string>): void {\n if (disposed) return;\n\n deps.invalidateCaches();\n\n if (target === undefined) {\n fieldStateCache.clear();\n rippleBatch(() => {\n formStateSignal.value = deps.computeState();\n\n for (const [name, sig] of fieldSignals) sig.value = deps.buildFieldState(name);\n });\n } else if (typeof target === 'string') {\n fieldStateCache.delete(target);\n rippleBatch(() => {\n formStateSignal.value = deps.computeState();\n\n const sig = fieldSignals.get(target);\n\n if (sig) sig.value = deps.buildFieldState(target);\n });\n } else {\n // Materialize before rippleBatch so the iterable isn't consumed inside the callback.\n const fields = [...target];\n\n for (const field of fields) fieldStateCache.delete(field);\n\n rippleBatch(() => {\n formStateSignal.value = deps.computeState();\n\n for (const field of fields) {\n const sig = fieldSignals.get(field);\n\n if (sig) sig.value = deps.buildFieldState(field);\n }\n });\n }\n }\n\n function subscribe(listener: (state: FormState) => void, options?: SubscribeOptions): Unsubscribe {\n if (disposed) return () => {};\n\n // Wrap listener so it always returns void — ripple watch throws on non-function returns.\n const sub = watch(formStateSignal, (state) => {\n listener(state);\n });\n\n rippleSubs.add(sub);\n\n if (options?.sync) listener(formStateSignal.value);\n\n return () => {\n sub.dispose();\n rippleSubs.delete(sub);\n };\n }\n\n function subscribeField(\n name: string,\n listener: (state: FieldState<unknown>) => void,\n options?: SubscribeOptions,\n ): Unsubscribe {\n if (disposed) return () => {};\n\n assertSafeKey(name);\n\n const sig = getOrCreateFieldSignal(name);\n const sub = watch(sig, (state) => {\n listener(state);\n });\n\n rippleSubs.add(sub);\n\n if (options?.sync) listener(sig.value);\n\n return () => {\n sub.dispose();\n rippleSubs.delete(sub);\n };\n }\n\n function dispose(): void {\n disposed = true;\n\n for (const sub of rippleSubs) sub.dispose();\n\n rippleSubs.clear();\n fieldSignals.clear();\n fieldStateCache.clear();\n }\n\n return {\n dispose,\n getFieldSnapshot,\n getOrCreateFieldSignal,\n getStateSnapshot,\n requestNotify,\n subscribe,\n subscribeField,\n };\n}\n\nexport type Notifier = ReturnType<typeof createNotifier>;\n"],"mappings":"mEA2BA,SAAgB,EAAe,EAAoB,CACjD,IAAM,GAAA,EAAA,EAAA,OAAA,CAA4C,EAAK,aAAa,CAAC,EAC/D,EAAe,IAAI,IACnB,EAAkB,IAAI,IACtB,EAAa,IAAI,IACnB,EAAW,GAEf,SAAS,EAAuB,EAA0C,CACxE,IAAI,EAAM,EAAa,IAAI,CAAG,EAc9B,OAZK,IACH,GAAA,EAAA,EAAA,OAAA,CAAkC,EAAK,gBAAgB,CAAG,EAAG,CAC3D,QAAS,EAAG,IACV,EAAE,QAAU,EAAE,OACd,EAAE,QAAU,EAAE,OACd,EAAE,WAAa,EAAE,UACjB,EAAE,UAAY,EAAE,SAChB,EAAE,QAAU,EAAE,KAClB,CAAC,EACD,EAAa,IAAI,EAAK,CAAG,GAGpB,CACT,CAEA,SAAS,EAAiB,EAAmC,CAC3D,IAAM,EAAS,EAAgB,IAAI,CAAI,EAEvC,GAAI,EAAQ,OAAO,EAEnB,IAAM,EAAO,OAAO,OAAO,EAAK,gBAAgB,CAAI,CAAC,EAIrD,OAFA,EAAgB,IAAI,EAAM,CAAI,EAEvB,CACT,CAEA,SAAS,GAA8B,CACrC,OAAO,EAAgB,KACzB,CASA,SAAS,EAAc,EAA0C,CAC3D,MAIJ,GAFA,EAAK,iBAAiB,EAElB,IAAW,IAAA,GACb,EAAgB,MAAM,GACtB,EAAA,EAAA,MAAA,KAAkB,CAChB,EAAgB,MAAQ,EAAK,aAAa,EAE1C,IAAK,GAAM,CAAC,EAAM,KAAQ,EAAc,EAAI,MAAQ,EAAK,gBAAgB,CAAI,CAC/E,CAAC,OACI,GAAI,OAAO,GAAW,SAC3B,EAAgB,OAAO,CAAM,GAC7B,EAAA,EAAA,MAAA,KAAkB,CAChB,EAAgB,MAAQ,EAAK,aAAa,EAE1C,IAAM,EAAM,EAAa,IAAI,CAAM,EAE/B,IAAK,EAAI,MAAQ,EAAK,gBAAgB,CAAM,EAClD,CAAC,MACI,CAEL,IAAM,EAAS,CAAC,GAAG,CAAM,EAEzB,IAAK,IAAM,KAAS,EAAQ,EAAgB,OAAO,CAAK,GAExD,EAAA,EAAA,MAAA,KAAkB,CAChB,EAAgB,MAAQ,EAAK,aAAa,EAE1C,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAM,EAAa,IAAI,CAAK,EAE9B,IAAK,EAAI,MAAQ,EAAK,gBAAgB,CAAK,EACjD,CACF,CAAC,CACH,CACF,CAEA,SAAS,EAAU,EAAsC,EAAyC,CAChG,GAAI,EAAU,UAAa,CAAC,EAG5B,IAAM,GAAA,EAAA,EAAA,MAAA,CAAY,EAAkB,GAAU,CAC5C,EAAS,CAAK,CAChB,CAAC,EAMD,OAJA,EAAW,IAAI,CAAG,EAEd,GAAS,MAAM,EAAS,EAAgB,KAAK,MAEpC,CACX,EAAI,QAAQ,EACZ,EAAW,OAAO,CAAG,CACvB,CACF,CAEA,SAAS,EACP,EACA,EACA,EACa,CACb,GAAI,EAAU,UAAa,CAAC,EAE5B,EAAA,cAAc,CAAI,EAElB,IAAM,EAAM,EAAuB,CAAI,EACjC,GAAA,EAAA,EAAA,MAAA,CAAY,EAAM,GAAU,CAChC,EAAS,CAAK,CAChB,CAAC,EAMD,OAJA,EAAW,IAAI,CAAG,EAEd,GAAS,MAAM,EAAS,EAAI,KAAK,MAExB,CACX,EAAI,QAAQ,EACZ,EAAW,OAAO,CAAG,CACvB,CACF,CAEA,SAAS,GAAgB,CACvB,EAAW,GAEX,IAAK,IAAM,KAAO,EAAY,EAAI,QAAQ,EAE1C,EAAW,MAAM,EACjB,EAAa,MAAM,EACnB,EAAgB,MAAM,CACxB,CAEA,MAAO,CACL,UACA,mBACA,yBACA,mBACA,gBACA,YACA,gBACF,CACF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type Signal } from '@vielzeug/ripple';
|
|
2
|
+
import type { FieldState, FormState, SubscribeOptions, Unsubscribe } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Everything needed to (re)compute fresh state on demand. Supplied by whoever owns the
|
|
5
|
+
* actual field/validator Maps (`core/context.ts`, backing `core/fields.ts`) so this module
|
|
6
|
+
* never touches those Maps directly — it only knows "how to get current state" and "who's
|
|
7
|
+
* listening," never "where the state lives."
|
|
8
|
+
*/
|
|
9
|
+
export type NotifierDeps = {
|
|
10
|
+
buildFieldState(name: string): FieldState<unknown>;
|
|
11
|
+
computeState(): FormState;
|
|
12
|
+
/** Called once at the top of every requestNotify(), before state is recomputed — lets the
|
|
13
|
+
* caller drop its own memoized derived values (e.g. `values()`'s cached unflattened object). */
|
|
14
|
+
invalidateCaches(): void;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Owns every signal, subscription, and derived-state cache a form needs to notify observers.
|
|
18
|
+
* Extracted from the old shared `FormContext` god-object (see `core/context.ts`'s header
|
|
19
|
+
* comment) — this is the one piece of that object with no real dependency on field values or
|
|
20
|
+
* validators, only on "what does current state look like" (via `NotifierDeps`), so it's safe
|
|
21
|
+
* to own its signals/caches privately instead of exposing them on a shared mutable bag.
|
|
22
|
+
*/
|
|
23
|
+
export declare function createNotifier(deps: NotifierDeps): {
|
|
24
|
+
dispose: () => void;
|
|
25
|
+
getFieldSnapshot: (name: string) => FieldState<unknown>;
|
|
26
|
+
getOrCreateFieldSignal: (key: string) => Signal<FieldState<unknown>>;
|
|
27
|
+
getStateSnapshot: () => FormState;
|
|
28
|
+
requestNotify: (target?: string | Iterable<string>) => void;
|
|
29
|
+
subscribe: (listener: (state: FormState) => void, options?: SubscribeOptions) => Unsubscribe;
|
|
30
|
+
subscribeField: (name: string, listener: (state: FieldState<unknown>) => void, options?: SubscribeOptions) => Unsubscribe;
|
|
31
|
+
};
|
|
32
|
+
export type Notifier = ReturnType<typeof createNotifier>;
|
|
33
|
+
//# sourceMappingURL=notifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifier.d.ts","sourceRoot":"","sources":["../../src/core/notifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEpF,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAIrF;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACnD,YAAY,IAAI,SAAS,CAAC;IAC1B;oGACgG;IAChG,gBAAgB,IAAI,IAAI,CAAC;CAC1B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY;mBAiI3B,IAAI;6BAxGQ,MAAM,KAAG,UAAU,CAAC,OAAO,CAAC;kCAlBvB,MAAM,KAAG,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;4BA8B5C,SAAS;6BAWN,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAG,IAAI;0BAuCnC,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,YAAY,gBAAgB,KAAG,WAAW;2BAmBzF,MAAM,YACF,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,IAAI,YACpC,gBAAgB,KACzB,WAAW;EAuCf;AAED,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { assertSafeKey as e } from "../_utils.js";
|
|
2
|
+
import { batch as t, signal as n, watch as r } from "@vielzeug/ripple";
|
|
3
|
+
//#region src/core/notifier.ts
|
|
4
|
+
function i(i) {
|
|
5
|
+
let a = n(i.computeState()), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Set(), l = !1;
|
|
6
|
+
function u(e) {
|
|
7
|
+
let t = o.get(e);
|
|
8
|
+
return t || (t = n(i.buildFieldState(e), { equals: (e, t) => e.value === t.value && e.error === t.error && e.hasError === t.hasError && e.touched === t.touched && e.dirty === t.dirty }), o.set(e, t)), t;
|
|
9
|
+
}
|
|
10
|
+
function d(e) {
|
|
11
|
+
let t = s.get(e);
|
|
12
|
+
if (t) return t;
|
|
13
|
+
let n = Object.freeze(i.buildFieldState(e));
|
|
14
|
+
return s.set(e, n), n;
|
|
15
|
+
}
|
|
16
|
+
function f() {
|
|
17
|
+
return a.value;
|
|
18
|
+
}
|
|
19
|
+
function p(e) {
|
|
20
|
+
if (!l) if (i.invalidateCaches(), e === void 0) s.clear(), t(() => {
|
|
21
|
+
a.value = i.computeState();
|
|
22
|
+
for (let [e, t] of o) t.value = i.buildFieldState(e);
|
|
23
|
+
});
|
|
24
|
+
else if (typeof e == "string") s.delete(e), t(() => {
|
|
25
|
+
a.value = i.computeState();
|
|
26
|
+
let t = o.get(e);
|
|
27
|
+
t && (t.value = i.buildFieldState(e));
|
|
28
|
+
});
|
|
29
|
+
else {
|
|
30
|
+
let n = [...e];
|
|
31
|
+
for (let e of n) s.delete(e);
|
|
32
|
+
t(() => {
|
|
33
|
+
a.value = i.computeState();
|
|
34
|
+
for (let e of n) {
|
|
35
|
+
let t = o.get(e);
|
|
36
|
+
t && (t.value = i.buildFieldState(e));
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function m(e, t) {
|
|
42
|
+
if (l) return () => {};
|
|
43
|
+
let n = r(a, (t) => {
|
|
44
|
+
e(t);
|
|
45
|
+
});
|
|
46
|
+
return c.add(n), t?.sync && e(a.value), () => {
|
|
47
|
+
n.dispose(), c.delete(n);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function h(t, n, i) {
|
|
51
|
+
if (l) return () => {};
|
|
52
|
+
e(t);
|
|
53
|
+
let a = u(t), o = r(a, (e) => {
|
|
54
|
+
n(e);
|
|
55
|
+
});
|
|
56
|
+
return c.add(o), i?.sync && n(a.value), () => {
|
|
57
|
+
o.dispose(), c.delete(o);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function g() {
|
|
61
|
+
l = !0;
|
|
62
|
+
for (let e of c) e.dispose();
|
|
63
|
+
c.clear(), o.clear(), s.clear();
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
dispose: g,
|
|
67
|
+
getFieldSnapshot: d,
|
|
68
|
+
getOrCreateFieldSignal: u,
|
|
69
|
+
getStateSnapshot: f,
|
|
70
|
+
requestNotify: p,
|
|
71
|
+
subscribe: m,
|
|
72
|
+
subscribeField: h
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { i as createNotifier };
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=notifier.js.map
|