@vielzeug/forge 1.1.2
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 +70 -0
- package/dist/_dev.cjs +2 -0
- package/dist/_dev.cjs.map +1 -0
- package/dist/_dev.d.ts +2 -0
- package/dist/_dev.d.ts.map +1 -0
- package/dist/_dev.js +9 -0
- package/dist/_dev.js.map +1 -0
- package/dist/_utils.cjs +2 -0
- package/dist/_utils.cjs.map +1 -0
- package/dist/_utils.d.ts +12 -0
- package/dist/_utils.d.ts.map +1 -0
- package/dist/_utils.js +25 -0
- package/dist/_utils.js.map +1 -0
- package/dist/adapters/form-data.cjs +2 -0
- package/dist/adapters/form-data.cjs.map +1 -0
- package/dist/adapters/form-data.d.ts +8 -0
- package/dist/adapters/form-data.d.ts.map +1 -0
- package/dist/adapters/form-data.js +14 -0
- package/dist/adapters/form-data.js.map +1 -0
- package/dist/adapters/validators.cjs +2 -0
- package/dist/adapters/validators.cjs.map +1 -0
- package/dist/adapters/validators.d.ts +44 -0
- package/dist/adapters/validators.d.ts.map +1 -0
- package/dist/adapters/validators.js +20 -0
- package/dist/adapters/validators.js.map +1 -0
- package/dist/core/array.cjs +2 -0
- package/dist/core/array.cjs.map +1 -0
- package/dist/core/array.d.ts +10 -0
- package/dist/core/array.d.ts.map +1 -0
- package/dist/core/array.js +46 -0
- package/dist/core/array.js.map +1 -0
- package/dist/core/asyncQueue.cjs +2 -0
- package/dist/core/asyncQueue.cjs.map +1 -0
- package/dist/core/asyncQueue.d.ts +22 -0
- package/dist/core/asyncQueue.d.ts.map +1 -0
- package/dist/core/asyncQueue.js +67 -0
- package/dist/core/asyncQueue.js.map +1 -0
- package/dist/core/context.cjs +2 -0
- package/dist/core/context.cjs.map +1 -0
- package/dist/core/context.d.ts +10 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +176 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/lifecycle.cjs +2 -0
- package/dist/core/lifecycle.cjs.map +1 -0
- package/dist/core/lifecycle.d.ts +13 -0
- package/dist/core/lifecycle.d.ts.map +1 -0
- package/dist/core/lifecycle.js +42 -0
- package/dist/core/lifecycle.js.map +1 -0
- package/dist/core/observe.cjs +2 -0
- package/dist/core/observe.cjs.map +1 -0
- package/dist/core/observe.d.ts +18 -0
- package/dist/core/observe.d.ts.map +1 -0
- package/dist/core/observe.js +90 -0
- package/dist/core/observe.js.map +1 -0
- package/dist/core/scope.cjs +2 -0
- package/dist/core/scope.cjs.map +1 -0
- package/dist/core/scope.d.ts +8 -0
- package/dist/core/scope.d.ts.map +1 -0
- package/dist/core/scope.js +239 -0
- package/dist/core/scope.js.map +1 -0
- package/dist/core/validation.cjs +2 -0
- package/dist/core/validation.cjs.map +1 -0
- package/dist/core/validation.d.ts +23 -0
- package/dist/core/validation.d.ts.map +1 -0
- package/dist/core/validation.js +173 -0
- package/dist/core/validation.js.map +1 -0
- package/dist/core/values.cjs +2 -0
- package/dist/core/values.cjs.map +1 -0
- package/dist/core/values.d.ts +31 -0
- package/dist/core/values.d.ts.map +1 -0
- package/dist/core/values.js +149 -0
- package/dist/core/values.js.map +1 -0
- package/dist/devtools.cjs +2 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.ts +40 -0
- package/dist/devtools.d.ts.map +1 -0
- package/dist/devtools.js +29 -0
- package/dist/devtools.js.map +1 -0
- package/dist/errors.cjs +2 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.ts +21 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +22 -0
- package/dist/errors.js.map +1 -0
- package/dist/forge.cjs +2 -0
- package/dist/forge.cjs.map +1 -0
- package/dist/forge.iife.js +2 -0
- package/dist/forge.iife.js.map +1 -0
- package/dist/forge.js +2 -0
- package/dist/forge.js.map +1 -0
- package/dist/form.cjs +2 -0
- package/dist/form.cjs.map +1 -0
- package/dist/form.d.ts +6 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +89 -0
- package/dist/form.js.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/types.cjs +2 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.ts +391 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { ForgeSubmitError as e, ForgeValidationError as t } from "../errors.js";
|
|
2
|
+
import { flattenValues as n, unflattenValues as r } from "../_utils.js";
|
|
3
|
+
//#region src/core/scope.ts
|
|
4
|
+
function i(a, o) {
|
|
5
|
+
let s = `${o}.`;
|
|
6
|
+
function c(e) {
|
|
7
|
+
return `${o}.${e}`;
|
|
8
|
+
}
|
|
9
|
+
function l(e) {
|
|
10
|
+
return e === o || e.startsWith(s);
|
|
11
|
+
}
|
|
12
|
+
function u(e) {
|
|
13
|
+
return e.startsWith(s) ? e.slice(s.length) : e;
|
|
14
|
+
}
|
|
15
|
+
function d() {
|
|
16
|
+
let e = a.getStateSnapshot(), t = Object.fromEntries(Object.entries(e.errors).filter(([e]) => l(e)).map(([e, t]) => [u(e), t])), n = e.touchedFields.filter(l).map(u), r = e.validatingFields.filter(l).map(u), i = Object.keys(t).length === 0, o = [...a.dirty].some(l), s = [...a.touched].some(l), c = r.length > 0;
|
|
17
|
+
return Object.freeze({
|
|
18
|
+
errors: Object.freeze(t),
|
|
19
|
+
isDirty: o,
|
|
20
|
+
isLoading: e.isLoading,
|
|
21
|
+
isSubmitting: e.isSubmitting,
|
|
22
|
+
isTouched: s,
|
|
23
|
+
isValid: i,
|
|
24
|
+
isValidating: c,
|
|
25
|
+
submitCount: e.submitCount,
|
|
26
|
+
touchedFields: Object.freeze(n),
|
|
27
|
+
validatingFields: Object.freeze(r)
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function f() {
|
|
31
|
+
let e = {};
|
|
32
|
+
for (let [t, n] of a.store) t.startsWith(s) && (e[t.slice(s.length)] = n);
|
|
33
|
+
return r(e);
|
|
34
|
+
}
|
|
35
|
+
function p() {
|
|
36
|
+
let e = /* @__PURE__ */ new Set();
|
|
37
|
+
for (let t of a.store.keys()) l(t) && e.add(u(t));
|
|
38
|
+
for (let t of a.validators.keys()) l(t) && e.add(u(t));
|
|
39
|
+
return Object.freeze([...e]);
|
|
40
|
+
}
|
|
41
|
+
function m() {
|
|
42
|
+
a.ensureNotDisposed();
|
|
43
|
+
for (let e of a.store.keys()) l(e) && a.touched.add(e);
|
|
44
|
+
for (let e of a.validators.keys()) l(e) && a.touched.add(e);
|
|
45
|
+
a.requestNotify();
|
|
46
|
+
}
|
|
47
|
+
function h() {
|
|
48
|
+
a.ensureNotDisposed();
|
|
49
|
+
for (let e of [...a.touched]) l(e) && a.touched.delete(e);
|
|
50
|
+
a.requestNotify();
|
|
51
|
+
}
|
|
52
|
+
function g() {
|
|
53
|
+
a.ensureNotDisposed();
|
|
54
|
+
for (let e of [...a.store.keys()]) l(e) && (a.fieldCtrls.get(e)?.abort(), a.fieldCtrls.delete(e), a.store.set(e, a.baseline.get(e)), a.dirty.delete(e), a.touched.delete(e), a.fieldErrors.delete(e) && a.invalidateErrors());
|
|
55
|
+
a.requestNotify();
|
|
56
|
+
}
|
|
57
|
+
function _(e) {
|
|
58
|
+
a.ensureNotDisposed();
|
|
59
|
+
for (let e of [...a.fieldCtrls.keys()]) l(e) && (a.fieldCtrls.get(e)?.abort(), a.fieldCtrls.delete(e));
|
|
60
|
+
let t = n(e);
|
|
61
|
+
for (let e of [...a.store.keys()]) l(e) && (a.store.delete(e), a.baseline.delete(e), a.dirty.delete(e), a.touched.delete(e), a.fieldErrors.delete(e));
|
|
62
|
+
a.invalidateErrors();
|
|
63
|
+
for (let [e, n] of Object.entries(t)) {
|
|
64
|
+
let t = c(e);
|
|
65
|
+
a.store.set(t, n), a.baseline.set(t, n);
|
|
66
|
+
}
|
|
67
|
+
a.requestNotify();
|
|
68
|
+
}
|
|
69
|
+
function v(e) {
|
|
70
|
+
a.ensureNotDisposed();
|
|
71
|
+
let t = n(e), r = [];
|
|
72
|
+
for (let [e, n] of Object.entries(t)) {
|
|
73
|
+
let t = c(e);
|
|
74
|
+
a.baseline.set(t, n), a.store.set(t, n), a.dirty.delete(t), r.push(t);
|
|
75
|
+
}
|
|
76
|
+
a.requestNotify(r);
|
|
77
|
+
}
|
|
78
|
+
function y(e) {
|
|
79
|
+
a.ensureNotDisposed();
|
|
80
|
+
for (let e of [...a.fieldErrors.keys()]) l(e) && a.fieldErrors.delete(e);
|
|
81
|
+
if (e) for (let [t, n] of Object.entries(e)) typeof n == "string" && a.fieldErrors.set(c(t), n);
|
|
82
|
+
a.invalidateErrors(), a.requestNotify();
|
|
83
|
+
}
|
|
84
|
+
async function b(e, t) {
|
|
85
|
+
if (a.ensureNotDisposed(), e !== void 0 && !Array.isArray(e) && !(e instanceof AbortSignal)) {
|
|
86
|
+
let n = c(e);
|
|
87
|
+
await a.runValidationCore([n], "partial", t);
|
|
88
|
+
let r = a.fieldErrors.get(n);
|
|
89
|
+
return {
|
|
90
|
+
errors: r === void 0 ? {} : { [e]: r },
|
|
91
|
+
valid: r === void 0
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (Array.isArray(e)) {
|
|
95
|
+
let n = e.map((e) => c(e));
|
|
96
|
+
await a.runValidationCore(n, "partial", t);
|
|
97
|
+
let r = {};
|
|
98
|
+
for (let e of n) {
|
|
99
|
+
let t = a.fieldErrors.get(e);
|
|
100
|
+
t !== void 0 && (r[u(e)] = t);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
errors: r,
|
|
104
|
+
valid: Object.keys(r).length === 0
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
let n = e, r = [...a.validators.keys()].filter(l);
|
|
108
|
+
await a.runValidationCore(r, "partial", n);
|
|
109
|
+
let i = {};
|
|
110
|
+
for (let [e, t] of a.fieldErrors) l(e) && (i[u(e)] = t);
|
|
111
|
+
return {
|
|
112
|
+
errors: i,
|
|
113
|
+
valid: Object.keys(i).length === 0
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
async function x(t) {
|
|
117
|
+
if (a.ensureNotDisposed(), a.isSubmitting()) throw new e("submit() called while a submission is already in progress");
|
|
118
|
+
a.root.batch(() => {
|
|
119
|
+
a.incrementSubmitCount(), a.setSubmitting(!0), m();
|
|
120
|
+
});
|
|
121
|
+
try {
|
|
122
|
+
let e = [...a.validators.keys()].filter(l);
|
|
123
|
+
await a.runValidationCore(e, "partial"), a.ensureNotDisposed();
|
|
124
|
+
let n = {};
|
|
125
|
+
for (let [e, t] of a.fieldErrors) l(e) && (n[u(e)] = t);
|
|
126
|
+
return Object.keys(n).length > 0 ? {
|
|
127
|
+
errors: n,
|
|
128
|
+
ok: !1,
|
|
129
|
+
type: "validation"
|
|
130
|
+
} : {
|
|
131
|
+
ok: !0,
|
|
132
|
+
value: await t(f())
|
|
133
|
+
};
|
|
134
|
+
} finally {
|
|
135
|
+
a.setSubmitting(!1), a.isDisposed() || a.requestNotify();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function S(e, t) {
|
|
139
|
+
let n = null;
|
|
140
|
+
return a.root.subscribe(() => {
|
|
141
|
+
let t = d();
|
|
142
|
+
if (n) {
|
|
143
|
+
let e = n, r = Object.keys(t.errors), i = Object.keys(e.errors), a = r.length === i.length && r.every((n) => t.errors[n] === e.errors[n]), o = t.touchedFields.length === e.touchedFields.length && t.touchedFields.every((t, n) => t === e.touchedFields[n]), s = t.validatingFields.length === e.validatingFields.length && t.validatingFields.every((t, n) => t === e.validatingFields[n]);
|
|
144
|
+
if (t.isDirty === e.isDirty && t.isLoading === e.isLoading && t.isSubmitting === e.isSubmitting && t.isTouched === e.isTouched && t.isValid === e.isValid && t.isValidating === e.isValidating && t.submitCount === e.submitCount && a && o && s) return;
|
|
145
|
+
}
|
|
146
|
+
n = t, e(t);
|
|
147
|
+
}, t);
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
array: (e) => a.root.array(c(e)),
|
|
151
|
+
batch: (e) => a.root.batch(e),
|
|
152
|
+
clearError: (e) => a.root.clearError(c(e)),
|
|
153
|
+
connect: (e, t) => a.root.connect(c(e), t),
|
|
154
|
+
get disposalSignal() {
|
|
155
|
+
return a.root.disposalSignal;
|
|
156
|
+
},
|
|
157
|
+
dispose: () => {},
|
|
158
|
+
get disposed() {
|
|
159
|
+
return a.isDisposed();
|
|
160
|
+
},
|
|
161
|
+
field: (e) => a.root.field(c(e)),
|
|
162
|
+
fields: {
|
|
163
|
+
list: p,
|
|
164
|
+
register: (e, t) => a.root.fields.register(c(e), t),
|
|
165
|
+
remove: (e) => a.root.fields.remove(c(e)),
|
|
166
|
+
setValidator: (e, t) => a.root.fields.setValidator(c(e), t)
|
|
167
|
+
},
|
|
168
|
+
get: (e) => a.root.get(c(e)),
|
|
169
|
+
get isLoading() {
|
|
170
|
+
return a.root.isLoading;
|
|
171
|
+
},
|
|
172
|
+
get isSubmitting() {
|
|
173
|
+
return a.root.isSubmitting;
|
|
174
|
+
},
|
|
175
|
+
patch: v,
|
|
176
|
+
replace: _,
|
|
177
|
+
reset: g,
|
|
178
|
+
resetErrors: y,
|
|
179
|
+
resetField: (e) => a.root.resetField(c(e)),
|
|
180
|
+
restore: (e) => a.root.restore(e),
|
|
181
|
+
scope: (e) => i(a, c(e)),
|
|
182
|
+
set: (e, t, n) => a.root.set(c(e), t, n),
|
|
183
|
+
setError: (e, t) => a.root.setError(c(e), t),
|
|
184
|
+
snapshot: () => a.root.snapshot(),
|
|
185
|
+
get state() {
|
|
186
|
+
return d();
|
|
187
|
+
},
|
|
188
|
+
submit: x,
|
|
189
|
+
submitOrThrow: async (e) => {
|
|
190
|
+
let n = await x(e);
|
|
191
|
+
if (!n.ok) throw new t(n.errors);
|
|
192
|
+
return n.value;
|
|
193
|
+
},
|
|
194
|
+
subscribe: (e, t) => a.root.subscribe(e, t),
|
|
195
|
+
subscribeField: (e, t, n) => a.root.subscribeField(c(e), t, n),
|
|
196
|
+
subscribeScoped: S,
|
|
197
|
+
[Symbol.asyncIterator]: () => a.root[Symbol.asyncIterator](),
|
|
198
|
+
[Symbol.dispose]() {
|
|
199
|
+
this.dispose();
|
|
200
|
+
},
|
|
201
|
+
touch: (e) => a.root.touch(c(e)),
|
|
202
|
+
touchAll: m,
|
|
203
|
+
untouch: (e) => a.root.untouch(c(e)),
|
|
204
|
+
untouchAll: h,
|
|
205
|
+
validate: b,
|
|
206
|
+
validateStream(e) {
|
|
207
|
+
let t = a.root.validateStream(e);
|
|
208
|
+
return {
|
|
209
|
+
async next() {
|
|
210
|
+
for (;;) {
|
|
211
|
+
let e = await t.next();
|
|
212
|
+
if (e.done) return e;
|
|
213
|
+
if (l(e.value.field)) return {
|
|
214
|
+
done: !1,
|
|
215
|
+
value: {
|
|
216
|
+
error: e.value.error,
|
|
217
|
+
field: u(e.value.field)
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
return() {
|
|
223
|
+
return t.return ? t.return() : Promise.resolve({
|
|
224
|
+
done: !0,
|
|
225
|
+
value: void 0
|
|
226
|
+
});
|
|
227
|
+
},
|
|
228
|
+
[Symbol.asyncIterator]() {
|
|
229
|
+
return this;
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
},
|
|
233
|
+
values: f
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
//#endregion
|
|
237
|
+
export { i as createScopedForm };
|
|
238
|
+
|
|
239
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","names":[],"sources":["../../src/core/scope.ts"],"sourcesContent":["import type {\n ArrayField,\n ConnectionResult,\n ErrorKeyOf,\n FieldState,\n FieldValidator,\n FlatKeyOf,\n Form,\n FormSnapshot,\n FormState,\n MaybePromise,\n RegisterFieldOptions,\n ScopedValues,\n SetOptions,\n SubmitResult,\n SubscribeOptions,\n TypeAtPath,\n Unsubscribe,\n ValidateResult,\n} from '../types';\n\nimport { flattenValues, unflattenValues } from '../_utils';\nimport { ForgeSubmitError, ForgeValidationError } from '../errors';\n\n/**\n * R3: Slim ScopeContext — delegates simple operations through `root: Form<TValues>`.\n * Only the Maps/Sets needed for bulk scoped operations are passed directly.\n * @internal\n */\nexport interface ScopeContext<TValues extends Record<string, unknown> = Record<string, unknown>> {\n /** The fully-featured root form — used for all simple one-field delegations. */\n root: Form<TValues>;\n\n /* ---- Shared mutable state (passed by reference, needed by bulk scoped ops) ---- */\n store: Map<string, unknown>;\n baseline: Map<string, unknown>;\n validators: Map<string, FieldValidator<unknown>>;\n fieldErrors: Map<string, string>;\n touched: Set<string>;\n dirty: Set<string>;\n fieldCtrls: Map<string, AbortController>;\n\n /* ---- Scalar state accessors ---- */\n isSubmitting(): boolean;\n setSubmitting(value: boolean): void;\n incrementSubmitCount(): void;\n isDisposed(): boolean;\n\n /* ---- Core helpers ---- */\n ensureNotDisposed(): void;\n invalidateErrors(): void;\n requestNotify(target?: string | Iterable<string>): void;\n getStateSnapshot(): FormState;\n\n /* ---- Validation core (needed for scoped validate/submit) ---- */\n runValidationCore(\n fields: string[],\n scope: 'full' | 'partial',\n signal?: AbortSignal,\n ): Promise<ValidateResult & { aborted: boolean }>;\n}\n\n/**\n * Creates a scoped sub-form whose field paths are relative to `pfx`.\n * All simple single-field operations delegate to `ctx.root`.\n * Only prefix-aware bulk operations (submit, validate, replace, reset, etc.) contain custom code.\n */\nexport function createScopedForm<TValues extends Record<string, unknown>, P extends string>(\n ctx: ScopeContext<TValues>,\n pfx: P,\n): Form<ScopedValues<TValues, P>> {\n const pfxDot = `${pfx}.`;\n\n function pre(name: string): string {\n return `${pfx}.${name}`;\n }\n\n function isScopedKey(key: string): boolean {\n return key === pfx || key.startsWith(pfxDot);\n }\n\n function unscope(key: string): string {\n return key.startsWith(pfxDot) ? key.slice(pfxDot.length) : key;\n }\n\n /* ---- Non-trivial scoped implementations ---- */\n\n /**\n * Compute a scoped projection of FormState from current raw Maps.\n * Errors, touchedFields, validatingFields, isValid, isDirty, isTouched reflect only\n * fields within the prefix. isSubmitting, isLoading, isValidating, submitCount are full-form.\n */\n function getScopedState(): FormState {\n const rootState = ctx.getStateSnapshot();\n const errors = Object.fromEntries(\n Object.entries(rootState.errors)\n .filter(([k]) => isScopedKey(k))\n .map(([k, v]) => [unscope(k), v]),\n );\n const touchedFields = rootState.touchedFields.filter(isScopedKey).map(unscope);\n const validatingFields = rootState.validatingFields.filter(isScopedKey).map(unscope);\n const isValid = Object.keys(errors).length === 0;\n const isDirty = [...ctx.dirty].some(isScopedKey);\n const isTouched = [...ctx.touched].some(isScopedKey);\n const isValidating = validatingFields.length > 0;\n\n return Object.freeze({\n errors: Object.freeze(errors) as FormState['errors'],\n isDirty,\n isLoading: rootState.isLoading,\n isSubmitting: rootState.isSubmitting,\n isTouched,\n isValid,\n isValidating,\n submitCount: rootState.submitCount,\n touchedFields: Object.freeze(touchedFields) as readonly string[],\n validatingFields: Object.freeze(validatingFields) as readonly string[],\n });\n }\n\n function scopedValues(): ScopedValues<TValues, P> {\n const sub: Record<string, unknown> = {};\n\n for (const [key, value] of ctx.store) {\n if (key.startsWith(pfxDot)) sub[key.slice(pfxDot.length)] = value;\n }\n\n return unflattenValues(sub) as ScopedValues<TValues, P>;\n }\n\n /**\n * Enumerate field paths within this scope's prefix, stripped to relative paths —\n * matching `scope.state`'s relative-path convention, not `state.touchedFields`'s\n * absolute-path convention. Same \"known field\" definition `scopedTouchAll()` uses.\n */\n function scopedListFields(): readonly string[] {\n const known = new Set<string>();\n\n for (const key of ctx.store.keys()) {\n if (isScopedKey(key)) known.add(unscope(key));\n }\n\n for (const key of ctx.validators.keys()) {\n if (isScopedKey(key)) known.add(unscope(key));\n }\n\n return Object.freeze([...known]);\n }\n\n function scopedTouchAll(): void {\n ctx.ensureNotDisposed();\n\n for (const name of ctx.store.keys()) {\n if (isScopedKey(name)) ctx.touched.add(name);\n }\n\n for (const name of ctx.validators.keys()) {\n if (isScopedKey(name)) ctx.touched.add(name);\n }\n\n ctx.requestNotify();\n }\n\n function scopedUntouchAll(): void {\n ctx.ensureNotDisposed();\n\n for (const name of [...ctx.touched]) {\n if (isScopedKey(name)) ctx.touched.delete(name);\n }\n\n ctx.requestNotify();\n }\n\n function scopedReset(): void {\n ctx.ensureNotDisposed();\n\n for (const key of [...ctx.store.keys()]) {\n if (!isScopedKey(key)) continue;\n\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\n ctx.requestNotify();\n }\n\n function scopedReplace(newValues: ScopedValues<TValues, P>): void {\n ctx.ensureNotDisposed();\n\n for (const key of [...ctx.fieldCtrls.keys()]) {\n if (isScopedKey(key)) {\n ctx.fieldCtrls.get(key)?.abort();\n ctx.fieldCtrls.delete(key);\n }\n }\n\n const flat = flattenValues(newValues as Record<string, unknown>);\n\n for (const key of [...ctx.store.keys()]) {\n if (!isScopedKey(key)) continue;\n\n ctx.store.delete(key);\n ctx.baseline.delete(key);\n ctx.dirty.delete(key);\n ctx.touched.delete(key);\n ctx.fieldErrors.delete(key);\n }\n\n ctx.invalidateErrors();\n\n for (const [key, value] of Object.entries(flat)) {\n const full = pre(key);\n\n ctx.store.set(full, value);\n ctx.baseline.set(full, value);\n }\n\n ctx.requestNotify();\n }\n\n function scopedPatch(partial: Record<string, unknown>): void {\n ctx.ensureNotDisposed();\n\n const flat = flattenValues(partial);\n const changedKeys: string[] = [];\n\n for (const [key, value] of Object.entries(flat)) {\n const full = pre(key);\n\n ctx.baseline.set(full, value);\n ctx.store.set(full, value);\n ctx.dirty.delete(full);\n changedKeys.push(full);\n }\n\n ctx.requestNotify(changedKeys);\n }\n\n function scopedResetErrors(\n nextErrors?: Partial<Record<ErrorKeyOf<ScopedValues<TValues, P>>, string | undefined>>,\n ): void {\n ctx.ensureNotDisposed();\n\n for (const key of [...ctx.fieldErrors.keys()]) {\n if (isScopedKey(key)) ctx.fieldErrors.delete(key);\n }\n\n if (nextErrors) {\n for (const [key, message] of Object.entries(nextErrors)) {\n if (typeof message === 'string') ctx.fieldErrors.set(pre(key), message);\n }\n }\n\n ctx.invalidateErrors();\n ctx.requestNotify();\n }\n\n async function scopedValidate(\n nameOrFieldsOrSignal?: FlatKeyOf<ScopedValues<TValues, P>> | FlatKeyOf<ScopedValues<TValues, P>>[] | AbortSignal,\n signal?: AbortSignal,\n ): Promise<ValidateResult> {\n ctx.ensureNotDisposed();\n\n if (\n nameOrFieldsOrSignal !== undefined &&\n !Array.isArray(nameOrFieldsOrSignal) &&\n !(nameOrFieldsOrSignal instanceof AbortSignal)\n ) {\n // validate(name) — single field\n const prefixedName = pre(nameOrFieldsOrSignal as string) as FlatKeyOf<TValues>;\n\n await ctx.runValidationCore([prefixedName as string], 'partial', signal);\n\n const error = ctx.fieldErrors.get(prefixedName as string);\n\n return {\n errors: error !== undefined ? { [nameOrFieldsOrSignal as string]: error } : {},\n valid: error === undefined,\n };\n }\n\n if (Array.isArray(nameOrFieldsOrSignal)) {\n // validate(fields[]) — specific subset\n const prefixedFields = nameOrFieldsOrSignal.map((f) => pre(f as string)) as string[];\n\n await ctx.runValidationCore(prefixedFields, 'partial', signal);\n\n const errors: Record<string, string> = {};\n\n for (const pf of prefixedFields) {\n const msg = ctx.fieldErrors.get(pf);\n\n if (msg !== undefined) errors[unscope(pf)] = msg;\n }\n\n return { errors, valid: Object.keys(errors).length === 0 };\n }\n\n // validate(signal?) — all scoped fields\n const sig = nameOrFieldsOrSignal as AbortSignal | undefined;\n const fields = [...ctx.validators.keys()].filter(isScopedKey);\n\n await ctx.runValidationCore(fields, 'partial', sig);\n\n const errors: Record<string, string> = {};\n\n for (const [key, message] of ctx.fieldErrors) {\n if (isScopedKey(key)) errors[unscope(key)] = message;\n }\n\n return { errors, valid: Object.keys(errors).length === 0 };\n }\n\n async function scopedSubmit<TResult>(\n handler: (values: ScopedValues<TValues, P>) => MaybePromise<TResult>,\n ): Promise<SubmitResult<TResult>> {\n ctx.ensureNotDisposed();\n\n if (ctx.isSubmitting()) throw new ForgeSubmitError('submit() called while a submission is already in progress');\n\n ctx.root.batch(() => {\n ctx.incrementSubmitCount();\n ctx.setSubmitting(true);\n scopedTouchAll();\n });\n\n try {\n const fields = [...ctx.validators.keys()].filter(isScopedKey);\n\n await ctx.runValidationCore(fields, 'partial');\n\n ctx.ensureNotDisposed();\n\n const errors: Record<string, string> = {};\n\n for (const [key, message] of ctx.fieldErrors) {\n if (isScopedKey(key)) errors[unscope(key)] = message;\n }\n\n if (Object.keys(errors).length > 0) {\n return { errors, ok: false, type: 'validation' as const };\n }\n\n return { ok: true as const, value: await handler(scopedValues()) };\n } finally {\n ctx.setSubmitting(false);\n\n if (!ctx.isDisposed()) ctx.requestNotify();\n }\n }\n\n /**\n * Subscribe filtered to scoped fields only.\n * Errors, touchedFields, and validatingFields are remapped to relative paths.\n * `isValid`, `isDirty`, and `isTouched` reflect only fields within this scope's prefix.\n * `isSubmitting`, `isLoading`, `isValidating`, and `submitCount` reflect the full form.\n *\n * The listener fires only when the scoped projection changes — mutations outside this\n * prefix do not fire the listener.\n */\n function subscribeScoped(listener: (state: FormState) => void, options?: SubscribeOptions): Unsubscribe {\n // Track the previous scoped projection for equality comparison.\n let prevState: FormState | null = null;\n\n return ctx.root.subscribe(() => {\n const next = getScopedState();\n\n // Skip if no scoped-relevant state has changed.\n if (prevState) {\n const p = prevState;\n const errKeys = Object.keys(next.errors);\n const prevErrKeys = Object.keys(p.errors);\n const errorsMatch =\n errKeys.length === prevErrKeys.length && errKeys.every((k) => next.errors[k] === p.errors[k]);\n\n const touchedMatch =\n next.touchedFields.length === p.touchedFields.length &&\n next.touchedFields.every((v, i) => v === p.touchedFields[i]);\n\n const validatingMatch =\n next.validatingFields.length === p.validatingFields.length &&\n next.validatingFields.every((v, i) => v === p.validatingFields[i]);\n\n const flagsMatch =\n next.isDirty === p.isDirty &&\n next.isLoading === p.isLoading &&\n next.isSubmitting === p.isSubmitting &&\n next.isTouched === p.isTouched &&\n next.isValid === p.isValid &&\n next.isValidating === p.isValidating &&\n next.submitCount === p.submitCount;\n\n if (flagsMatch && errorsMatch && touchedMatch && validatingMatch) return;\n }\n\n prevState = next;\n listener(next);\n }, options);\n }\n\n /* ---- Public scoped form object ---- */\n\n type S = ScopedValues<TValues, P>;\n\n return {\n array: (name) =>\n ctx.root.array(pre(name as string) as FlatKeyOf<TValues>) as ArrayField<\n TypeAtPath<S, typeof name> extends readonly (infer E)[] ? E : unknown\n >,\n batch: (fn) => ctx.root.batch(fn),\n clearError: (name) => ctx.root.clearError(pre(name as string) as ErrorKeyOf<TValues>),\n connect: (name, config?) =>\n ctx.root.connect(pre(name as string) as FlatKeyOf<TValues>, config) as ConnectionResult<\n TypeAtPath<S, typeof name>\n >,\n get disposalSignal() {\n return ctx.root.disposalSignal;\n },\n dispose: () => {\n /* Scoped forms share lifecycle with parent — call parentForm.dispose() to tear down */\n },\n get disposed() {\n return ctx.isDisposed();\n },\n field: (name) =>\n ctx.root.field(pre(name as string) as FlatKeyOf<TValues>) as FieldState<TypeAtPath<S, typeof name>>,\n fields: {\n list: scopedListFields,\n register: (name, options?) =>\n ctx.root.fields.register(\n pre(name as string) as FlatKeyOf<TValues>,\n options as RegisterFieldOptions<TypeAtPath<TValues, FlatKeyOf<TValues>>>,\n ),\n remove: (name) => ctx.root.fields.remove(pre(name as string) as FlatKeyOf<TValues>),\n setValidator: (name, validator?) =>\n ctx.root.fields.setValidator(pre(name as string) as FlatKeyOf<TValues>, validator),\n },\n get: (name) => ctx.root.get(pre(name as string) as FlatKeyOf<TValues>) as TypeAtPath<S, typeof name>,\n get isLoading() {\n return ctx.root.isLoading;\n },\n get isSubmitting() {\n return ctx.root.isSubmitting;\n },\n patch: scopedPatch as Form<S>['patch'],\n replace: scopedReplace as Form<S>['replace'],\n reset: scopedReset,\n resetErrors: scopedResetErrors as Form<S>['resetErrors'],\n resetField: (name) => ctx.root.resetField(pre(name as string) as FlatKeyOf<TValues>),\n // Cast is irreducible: FormSnapshot<ScopedValues<TValues,P>> and FormSnapshot<TValues> are\n // structurally unrelated generic instantiations -- TS cannot verify one against the other.\n restore: (snap) => ctx.root.restore(snap as FormSnapshot<TValues>),\n scope: (subPrefix) => createScopedForm(ctx, pre(subPrefix as string)) as Form<ScopedValues<S, typeof subPrefix>>,\n set: (name, value, options?: SetOptions) =>\n ctx.root.set(\n pre(name as string) as FlatKeyOf<TValues>,\n value as TypeAtPath<TValues, FlatKeyOf<TValues>>,\n options,\n ),\n setError: (name, message) => ctx.root.setError(pre(name as string) as ErrorKeyOf<TValues>, message),\n // Cast is irreducible: FormSnapshot<TValues> and FormSnapshot<ScopedValues<TValues,P>> are\n // structurally unrelated generic instantiations -- TS cannot verify one against the other.\n snapshot: () => ctx.root.snapshot() as FormSnapshot<S>,\n get state() {\n return getScopedState();\n },\n submit: scopedSubmit as Form<S>['submit'],\n submitOrThrow: async (handler) => {\n const result = await scopedSubmit(handler);\n\n if (!result.ok) throw new ForgeValidationError(result.errors as Record<string, string>);\n\n return result.value;\n },\n subscribe: (listener, options?) => ctx.root.subscribe(listener, options),\n subscribeField: (name, listener, options?) =>\n ctx.root.subscribeField(\n pre(name as string) as FlatKeyOf<TValues>,\n listener as (state: FieldState<TypeAtPath<TValues, FlatKeyOf<TValues>>>) => void,\n options,\n ),\n subscribeScoped,\n [Symbol.asyncIterator]: () => ctx.root[Symbol.asyncIterator](),\n [Symbol.dispose]() {\n this.dispose();\n },\n touch: (name) => ctx.root.touch(pre(name as string) as FlatKeyOf<TValues>),\n touchAll: scopedTouchAll,\n untouch: (name) => ctx.root.untouch(pre(name as string) as FlatKeyOf<TValues>),\n untouchAll: scopedUntouchAll,\n validate: scopedValidate as Form<S>['validate'],\n validateStream(signal?) {\n const rootIter = ctx.root.validateStream(signal);\n\n const iter: AsyncIterableIterator<{ error: string | undefined; field: string }> = {\n async next() {\n for (;;) {\n const item = await rootIter.next();\n\n if (item.done) return item;\n\n if (isScopedKey(item.value.field))\n return { done: false, value: { error: item.value.error, field: unscope(item.value.field) } };\n }\n },\n return() {\n return rootIter.return ? rootIter.return() : Promise.resolve({ done: true as const, value: undefined });\n },\n [Symbol.asyncIterator]() {\n return this;\n },\n };\n\n return iter;\n },\n values: scopedValues,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../errors.cjs"),t=require("../_utils.cjs"),n=require("../types.cjs"),r=require("./asyncQueue.cjs");let i=require("@vielzeug/arsenal");function a(a,o){async function s(e,t){let n=a.validators.get(e);if(!n)return;if(t.aborted)throw t.reason;let r=await n(a.store.get(e),t);return typeof r==`string`?r:void 0}async function c(e){if(!a.formValidator)return{};if(e.aborted)throw e.reason;let n=await a.formValidator(o.values(),e),r={};if(n)for(let[e,i]of Object.entries(n))t.isSafeKey(e)&&typeof i==`string`&&(r[e]=i);return r}function l(e,t,n,r){if(r===`full`){a.fieldErrors.clear();for(let[e,r]of Object.entries(n))t[e]===void 0&&(t[e]=r);for(let[e,n]of Object.entries(t))a.fieldErrors.set(e,n);a.invalidateErrors(),a.requestNotify()}else{let n=new Set;for(let r of e){let e=t[r],i=a.fieldErrors.get(r);e===void 0?a.fieldErrors.delete(r)&&n.add(r):(a.fieldErrors.set(r,e),i!==e&&n.add(r))}n.size>0&&a.invalidateErrors(),a.requestNotify(n)}}async function u(e,n,r){let o=new Set(e),u=new Map;for(let e of o){a.fieldCtrls.get(e)?.abort();let t=new AbortController;a.fieldCtrls.set(e,t),u.set(e,t)}let d=new AbortController,f=t.anySignal(d.signal,r,a.disposeController.signal);a.runCtrls.add(d);let p=Symbol();for(let e of o){let t=a.validatingRuns.get(e);t||(t=new Set,a.validatingRuns.set(e,t)),t.add(p)}a.requestNotify();try{let e=await Promise.all([...o].map(async e=>[e,await s(e,n===`partial`?t.anySignal(u.get(e).signal,f):f)]));if(f.aborted)return{aborted:!0,errors:Object.fromEntries(a.fieldErrors),valid:a.fieldErrors.size===0};let r={};for(let[t,n]of e)n!==void 0&&(r[t]=n);return l(o,r,n===`full`?await c(f):{},n),{aborted:!1,errors:Object.fromEntries(a.fieldErrors),valid:a.fieldErrors.size===0}}catch(e){if((0,i.isAbortError)(e))return{aborted:!0,errors:Object.fromEntries(a.fieldErrors),valid:a.fieldErrors.size===0};throw e}finally{a.runCtrls.delete(d);for(let e of o){let t=a.validatingRuns.get(e);t&&(t.delete(p),t.size===0&&a.validatingRuns.delete(e))}a.requestNotify();for(let[e,t]of u)a.fieldCtrls.get(e)===t&&a.fieldCtrls.delete(e)}}async function d(e,t){a.ensureNotDisposed();let n=await u(e,`partial`,t);return{errors:n.errors,valid:n.valid}}async function f(e,t){if(a.ensureNotDisposed(),e===void 0||e instanceof AbortSignal){let t=await u([...a.validators.keys()],`full`,e);return{errors:t.errors,valid:t.valid}}if(Array.isArray(e))return d(e,t);await d([e],t);let n=e,r=a.fieldErrors.get(n);return{errors:r===void 0?{}:{[n]:r},valid:r===void 0}}async function p(t){let n=await m(t);if(!n.ok)throw new e.ForgeValidationError(n.errors);return n.value}async function m(t){if(a.ensureNotDisposed(),a.isSubmittingState)throw new e.ForgeSubmitError(`submit() called while a submission is already in progress`);a.batch(()=>{a.submitCount++,a.isSubmittingState=!0,o.touchAll()});try{let e=await u([...a.validators.keys()],`full`);return a.ensureNotDisposed(),e.valid?{ok:!0,value:await t(o.values())}:{errors:e.errors,ok:!1,type:`validation`}}finally{a.isSubmittingState=!1,a.disposed||a.requestNotify()}}function h(e){a.ensureNotDisposed();let o=[...a.validators.keys()],l=new AbortController,u=e?t.anySignal(l.signal,e,a.disposeController.signal):t.anySignal(l.signal,a.disposeController.signal),d=r.createAsyncQueue(()=>l.abort());return Promise.all(o.map(async e=>{if(!u.aborted)try{let t=await s(e,u);u.aborted||d.push({error:t,field:e})}catch(e){if(!(0,i.isAbortError)(e))throw e}})).then(async()=>{if(a.formValidator&&!u.aborted)try{let e=await c(u);if(!u.aborted){let t=new Set([n.FORM_ERROR,...Object.keys(e)]);for(let n of t)d.push({error:e[n],field:n})}}catch{}}).catch(e=>{(0,i.isAbortError)(e)||(l.abort(),d.fail(e))}).finally(()=>{l.abort(),d.finish()}),d.iterator}return{runValidationCore:u,submit:m,submitOrThrow:p,validate:f,validateFields:d,validateStream:h}}exports.createValidationOps=a;
|
|
2
|
+
//# sourceMappingURL=validation.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.cjs","names":[],"sources":["../../src/core/validation.ts"],"sourcesContent":["import { isAbortError } from '@vielzeug/arsenal';\n\nimport type { FormContext } from './context';\nimport type { ValueOps } from './values';\n\nimport { anySignal, isSafeKey } from '../_utils';\nimport { ForgeSubmitError, ForgeValidationError } from '../errors';\nimport { type FlatKeyOf, FORM_ERROR, type MaybePromise, type SubmitResult, type ValidateResult } from '../types';\nimport { createAsyncQueue } from './asyncQueue';\n\n/**\n * Validation + submit operations: field/form validator execution, error application,\n * `validate()`/`validateFields()`/`validateStream()`, and `submit()`/`submitOrThrow()`.\n * Moved verbatim from `createForm()`'s \"Validation\" and \"Submit\" sections.\n */\nexport function createValidationOps<TValues extends Record<string, unknown>>(\n ctx: FormContext<TValues>,\n deps: Pick<ValueOps<TValues>, 'touchAll' | 'values'>,\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(deps.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();\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();\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();\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 deps.touchAll();\n });\n\n try {\n const validation = await runValidationCore([...ctx.validators.keys()], 'full');\n\n ctx.ensureNotDisposed();\n\n if (!validation.valid) {\n return { errors: validation.errors, ok: false, type: 'validation' };\n }\n\n return { ok: true, value: await handler(deps.values()) };\n } finally {\n ctx.isSubmittingState = false;\n\n if (!ctx.disposed) ctx.requestNotify();\n }\n }\n\n /* ======== validateStream ======== */\n\n function validateStream(signal?: AbortSignal): AsyncIterableIterator<{ error: string | undefined; field: string }> {\n ctx.ensureNotDisposed();\n\n const fields = [...ctx.validators.keys()];\n const ctrl = new AbortController();\n const combined = signal\n ? anySignal(ctrl.signal, signal, ctx.disposeController.signal)!\n : anySignal(ctrl.signal, ctx.disposeController.signal)!;\n\n type Item = { error: string | undefined; field: string };\n\n const queue = createAsyncQueue<Item>(() => ctrl.abort());\n\n // Run validators read-only — does NOT write to fieldErrors or fire requestNotify.\n // Each result is yielded as it settles; the form-level validator is yielded last.\n Promise.all(\n fields.map(async (f) => {\n if (combined.aborted) return;\n\n try {\n const error = await runFieldValidator(f, combined);\n\n if (!combined.aborted) queue.push({ error, field: f });\n } catch (err) {\n if (!isAbortError(err)) throw err;\n\n // aborted or disposed — skip this field\n }\n }),\n )\n .then(async () => {\n // Yield the form-level validator result last (F4: streams _form key too)\n if (ctx.formValidator && !combined.aborted) {\n try {\n const formErrors = await runFormValidator(combined);\n\n if (!combined.aborted) {\n const allKeys = new Set([FORM_ERROR, ...Object.keys(formErrors)]);\n\n for (const key of allKeys) {\n queue.push({ error: formErrors[key], field: key });\n }\n }\n } catch {\n // aborted\n }\n }\n })\n .catch((err) => {\n if (!isAbortError(err)) {\n ctrl.abort();\n queue.fail(err);\n }\n })\n .finally(() => {\n ctrl.abort();\n queue.finish();\n });\n\n return queue.iterator;\n }\n\n return { runValidationCore, submit, submitOrThrow, validate, validateFields, validateStream };\n}\n\nexport type ValidationOps<TValues extends Record<string, unknown>> = ReturnType<typeof createValidationOps<TValues>>;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { FormContext } from './context';
|
|
2
|
+
import type { ValueOps } from './values';
|
|
3
|
+
import { type FlatKeyOf, type MaybePromise, type SubmitResult, type ValidateResult } from '../types';
|
|
4
|
+
/**
|
|
5
|
+
* Validation + submit operations: field/form validator execution, error application,
|
|
6
|
+
* `validate()`/`validateFields()`/`validateStream()`, and `submit()`/`submitOrThrow()`.
|
|
7
|
+
* Moved verbatim from `createForm()`'s "Validation" and "Submit" sections.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createValidationOps<TValues extends Record<string, unknown>>(ctx: FormContext<TValues>, deps: Pick<ValueOps<TValues>, 'touchAll' | 'values'>): {
|
|
10
|
+
runValidationCore: (fields: string[], scope: "full" | "partial", signal?: AbortSignal) => Promise<ValidateResult & {
|
|
11
|
+
aborted: boolean;
|
|
12
|
+
}>;
|
|
13
|
+
submit: <TResult = void>(handler: (values: TValues) => MaybePromise<TResult>) => Promise<SubmitResult<TResult>>;
|
|
14
|
+
submitOrThrow: <TResult = void>(handler: (values: TValues) => MaybePromise<TResult>) => Promise<TResult>;
|
|
15
|
+
validate: (nameOrFieldsOrSignal?: FlatKeyOf<TValues> | FlatKeyOf<TValues>[] | AbortSignal, signal?: AbortSignal) => Promise<ValidateResult>;
|
|
16
|
+
validateFields: (fields: FlatKeyOf<TValues>[] | string[], signal?: AbortSignal) => Promise<ValidateResult>;
|
|
17
|
+
validateStream: (signal?: AbortSignal) => AsyncIterableIterator<{
|
|
18
|
+
error: string | undefined;
|
|
19
|
+
field: string;
|
|
20
|
+
}>;
|
|
21
|
+
};
|
|
22
|
+
export type ValidationOps<TValues extends Record<string, unknown>> = ReturnType<typeof createValidationOps<TValues>>;
|
|
23
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/core/validation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAIzC,OAAO,EAAE,KAAK,SAAS,EAAc,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAGjH;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzE,GAAG,EAAE,WAAW,CAAC,OAAO,CAAC,EACzB,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAC;gCAyE1C,MAAM,EAAE,SACT,MAAM,GAAG,SAAS,WAChB,WAAW,KACnB,OAAO,CAAC,cAAc,GAAG;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;aAuI3B,OAAO,kBAClB,CAAC,MAAM,EAAE,OAAO,KAAK,YAAY,CAAC,OAAO,CAAC,KAClD,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;oBAVJ,OAAO,kBAAkB,CAAC,MAAM,EAAE,OAAO,KAAK,YAAY,CAAC,OAAO,CAAC,KAAG,OAAO,CAAC,OAAO,CAAC;sCA7B1F,SAAS,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,EAAE,GAAG,WAAW,WACrE,WAAW,KACnB,OAAO,CAAC,cAAc,CAAC;6BAbhB,SAAS,CAAC,OAAO,CAAC,EAAE,GAAG,MAAM,EAAE,WAC9B,WAAW,KACnB,OAAO,CAAC,cAAc,CAAC;8BA8EO,WAAW,KAAG,qBAAqB,CAAC;QAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;EA+DnH;AAED,MAAM,MAAM,aAAa,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { ForgeSubmitError as e, ForgeValidationError as t } from "../errors.js";
|
|
2
|
+
import { anySignal as n, isSafeKey as r } from "../_utils.js";
|
|
3
|
+
import { FORM_ERROR as i } from "../types.js";
|
|
4
|
+
import { createAsyncQueue as a } from "./asyncQueue.js";
|
|
5
|
+
import { isAbortError as o } from "@vielzeug/arsenal";
|
|
6
|
+
//#region src/core/validation.ts
|
|
7
|
+
function s(s, c) {
|
|
8
|
+
async function l(e, t) {
|
|
9
|
+
let n = s.validators.get(e);
|
|
10
|
+
if (!n) return;
|
|
11
|
+
if (t.aborted) throw t.reason;
|
|
12
|
+
let r = await n(s.store.get(e), t);
|
|
13
|
+
return typeof r == "string" ? r : void 0;
|
|
14
|
+
}
|
|
15
|
+
async function u(e) {
|
|
16
|
+
if (!s.formValidator) return {};
|
|
17
|
+
if (e.aborted) throw e.reason;
|
|
18
|
+
let t = await s.formValidator(c.values(), e), n = {};
|
|
19
|
+
if (t) for (let [e, i] of Object.entries(t)) r(e) && typeof i == "string" && (n[e] = i);
|
|
20
|
+
return n;
|
|
21
|
+
}
|
|
22
|
+
function d(e, t, n, r) {
|
|
23
|
+
if (r === "full") {
|
|
24
|
+
s.fieldErrors.clear();
|
|
25
|
+
for (let [e, r] of Object.entries(n)) t[e] === void 0 && (t[e] = r);
|
|
26
|
+
for (let [e, n] of Object.entries(t)) s.fieldErrors.set(e, n);
|
|
27
|
+
s.invalidateErrors(), s.requestNotify();
|
|
28
|
+
} else {
|
|
29
|
+
let n = /* @__PURE__ */ new Set();
|
|
30
|
+
for (let r of e) {
|
|
31
|
+
let e = t[r], i = s.fieldErrors.get(r);
|
|
32
|
+
e === void 0 ? s.fieldErrors.delete(r) && n.add(r) : (s.fieldErrors.set(r, e), i !== e && n.add(r));
|
|
33
|
+
}
|
|
34
|
+
n.size > 0 && s.invalidateErrors(), s.requestNotify(n);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function f(e, t, r) {
|
|
38
|
+
let i = new Set(e), a = /* @__PURE__ */ new Map();
|
|
39
|
+
for (let e of i) {
|
|
40
|
+
s.fieldCtrls.get(e)?.abort();
|
|
41
|
+
let t = new AbortController();
|
|
42
|
+
s.fieldCtrls.set(e, t), a.set(e, t);
|
|
43
|
+
}
|
|
44
|
+
let c = new AbortController(), f = n(c.signal, r, s.disposeController.signal);
|
|
45
|
+
s.runCtrls.add(c);
|
|
46
|
+
let p = Symbol();
|
|
47
|
+
for (let e of i) {
|
|
48
|
+
let t = s.validatingRuns.get(e);
|
|
49
|
+
t || (t = /* @__PURE__ */ new Set(), s.validatingRuns.set(e, t)), t.add(p);
|
|
50
|
+
}
|
|
51
|
+
s.requestNotify();
|
|
52
|
+
try {
|
|
53
|
+
let e = await Promise.all([...i].map(async (e) => [e, await l(e, t === "partial" ? n(a.get(e).signal, f) : f)]));
|
|
54
|
+
if (f.aborted) return {
|
|
55
|
+
aborted: !0,
|
|
56
|
+
errors: Object.fromEntries(s.fieldErrors),
|
|
57
|
+
valid: s.fieldErrors.size === 0
|
|
58
|
+
};
|
|
59
|
+
let r = {};
|
|
60
|
+
for (let [t, n] of e) n !== void 0 && (r[t] = n);
|
|
61
|
+
return d(i, r, t === "full" ? await u(f) : {}, t), {
|
|
62
|
+
aborted: !1,
|
|
63
|
+
errors: Object.fromEntries(s.fieldErrors),
|
|
64
|
+
valid: s.fieldErrors.size === 0
|
|
65
|
+
};
|
|
66
|
+
} catch (e) {
|
|
67
|
+
if (o(e)) return {
|
|
68
|
+
aborted: !0,
|
|
69
|
+
errors: Object.fromEntries(s.fieldErrors),
|
|
70
|
+
valid: s.fieldErrors.size === 0
|
|
71
|
+
};
|
|
72
|
+
throw e;
|
|
73
|
+
} finally {
|
|
74
|
+
s.runCtrls.delete(c);
|
|
75
|
+
for (let e of i) {
|
|
76
|
+
let t = s.validatingRuns.get(e);
|
|
77
|
+
t && (t.delete(p), t.size === 0 && s.validatingRuns.delete(e));
|
|
78
|
+
}
|
|
79
|
+
s.requestNotify();
|
|
80
|
+
for (let [e, t] of a) s.fieldCtrls.get(e) === t && s.fieldCtrls.delete(e);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function p(e, t) {
|
|
84
|
+
s.ensureNotDisposed();
|
|
85
|
+
let n = await f(e, "partial", t);
|
|
86
|
+
return {
|
|
87
|
+
errors: n.errors,
|
|
88
|
+
valid: n.valid
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async function m(e, t) {
|
|
92
|
+
if (s.ensureNotDisposed(), e === void 0 || e instanceof AbortSignal) {
|
|
93
|
+
let t = await f([...s.validators.keys()], "full", e);
|
|
94
|
+
return {
|
|
95
|
+
errors: t.errors,
|
|
96
|
+
valid: t.valid
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (Array.isArray(e)) return p(e, t);
|
|
100
|
+
await p([e], t);
|
|
101
|
+
let n = e, r = s.fieldErrors.get(n);
|
|
102
|
+
return {
|
|
103
|
+
errors: r === void 0 ? {} : { [n]: r },
|
|
104
|
+
valid: r === void 0
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
async function h(e) {
|
|
108
|
+
let n = await g(e);
|
|
109
|
+
if (!n.ok) throw new t(n.errors);
|
|
110
|
+
return n.value;
|
|
111
|
+
}
|
|
112
|
+
async function g(t) {
|
|
113
|
+
if (s.ensureNotDisposed(), s.isSubmittingState) throw new e("submit() called while a submission is already in progress");
|
|
114
|
+
s.batch(() => {
|
|
115
|
+
s.submitCount++, s.isSubmittingState = !0, c.touchAll();
|
|
116
|
+
});
|
|
117
|
+
try {
|
|
118
|
+
let e = await f([...s.validators.keys()], "full");
|
|
119
|
+
return s.ensureNotDisposed(), e.valid ? {
|
|
120
|
+
ok: !0,
|
|
121
|
+
value: await t(c.values())
|
|
122
|
+
} : {
|
|
123
|
+
errors: e.errors,
|
|
124
|
+
ok: !1,
|
|
125
|
+
type: "validation"
|
|
126
|
+
};
|
|
127
|
+
} finally {
|
|
128
|
+
s.isSubmittingState = !1, s.disposed || s.requestNotify();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function _(e) {
|
|
132
|
+
s.ensureNotDisposed();
|
|
133
|
+
let t = [...s.validators.keys()], r = new AbortController(), c = e ? n(r.signal, e, s.disposeController.signal) : n(r.signal, s.disposeController.signal), d = a(() => r.abort());
|
|
134
|
+
return Promise.all(t.map(async (e) => {
|
|
135
|
+
if (!c.aborted) try {
|
|
136
|
+
let t = await l(e, c);
|
|
137
|
+
c.aborted || d.push({
|
|
138
|
+
error: t,
|
|
139
|
+
field: e
|
|
140
|
+
});
|
|
141
|
+
} catch (e) {
|
|
142
|
+
if (!o(e)) throw e;
|
|
143
|
+
}
|
|
144
|
+
})).then(async () => {
|
|
145
|
+
if (s.formValidator && !c.aborted) try {
|
|
146
|
+
let e = await u(c);
|
|
147
|
+
if (!c.aborted) {
|
|
148
|
+
let t = /* @__PURE__ */ new Set([i, ...Object.keys(e)]);
|
|
149
|
+
for (let n of t) d.push({
|
|
150
|
+
error: e[n],
|
|
151
|
+
field: n
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
} catch {}
|
|
155
|
+
}).catch((e) => {
|
|
156
|
+
o(e) || (r.abort(), d.fail(e));
|
|
157
|
+
}).finally(() => {
|
|
158
|
+
r.abort(), d.finish();
|
|
159
|
+
}), d.iterator;
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
runValidationCore: f,
|
|
163
|
+
submit: g,
|
|
164
|
+
submitOrThrow: h,
|
|
165
|
+
validate: m,
|
|
166
|
+
validateFields: p,
|
|
167
|
+
validateStream: _
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
//#endregion
|
|
171
|
+
export { s as createValidationOps };
|
|
172
|
+
|
|
173
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","names":[],"sources":["../../src/core/validation.ts"],"sourcesContent":["import { isAbortError } from '@vielzeug/arsenal';\n\nimport type { FormContext } from './context';\nimport type { ValueOps } from './values';\n\nimport { anySignal, isSafeKey } from '../_utils';\nimport { ForgeSubmitError, ForgeValidationError } from '../errors';\nimport { type FlatKeyOf, FORM_ERROR, type MaybePromise, type SubmitResult, type ValidateResult } from '../types';\nimport { createAsyncQueue } from './asyncQueue';\n\n/**\n * Validation + submit operations: field/form validator execution, error application,\n * `validate()`/`validateFields()`/`validateStream()`, and `submit()`/`submitOrThrow()`.\n * Moved verbatim from `createForm()`'s \"Validation\" and \"Submit\" sections.\n */\nexport function createValidationOps<TValues extends Record<string, unknown>>(\n ctx: FormContext<TValues>,\n deps: Pick<ValueOps<TValues>, 'touchAll' | 'values'>,\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(deps.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();\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();\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();\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 deps.touchAll();\n });\n\n try {\n const validation = await runValidationCore([...ctx.validators.keys()], 'full');\n\n ctx.ensureNotDisposed();\n\n if (!validation.valid) {\n return { errors: validation.errors, ok: false, type: 'validation' };\n }\n\n return { ok: true, value: await handler(deps.values()) };\n } finally {\n ctx.isSubmittingState = false;\n\n if (!ctx.disposed) ctx.requestNotify();\n }\n }\n\n /* ======== validateStream ======== */\n\n function validateStream(signal?: AbortSignal): AsyncIterableIterator<{ error: string | undefined; field: string }> {\n ctx.ensureNotDisposed();\n\n const fields = [...ctx.validators.keys()];\n const ctrl = new AbortController();\n const combined = signal\n ? anySignal(ctrl.signal, signal, ctx.disposeController.signal)!\n : anySignal(ctrl.signal, ctx.disposeController.signal)!;\n\n type Item = { error: string | undefined; field: string };\n\n const queue = createAsyncQueue<Item>(() => ctrl.abort());\n\n // Run validators read-only — does NOT write to fieldErrors or fire requestNotify.\n // Each result is yielded as it settles; the form-level validator is yielded last.\n Promise.all(\n fields.map(async (f) => {\n if (combined.aborted) return;\n\n try {\n const error = await runFieldValidator(f, combined);\n\n if (!combined.aborted) queue.push({ error, field: f });\n } catch (err) {\n if (!isAbortError(err)) throw err;\n\n // aborted or disposed — skip this field\n }\n }),\n )\n .then(async () => {\n // Yield the form-level validator result last (F4: streams _form key too)\n if (ctx.formValidator && !combined.aborted) {\n try {\n const formErrors = await runFormValidator(combined);\n\n if (!combined.aborted) {\n const allKeys = new Set([FORM_ERROR, ...Object.keys(formErrors)]);\n\n for (const key of allKeys) {\n queue.push({ error: formErrors[key], field: key });\n }\n }\n } catch {\n // aborted\n }\n }\n })\n .catch((err) => {\n if (!isAbortError(err)) {\n ctrl.abort();\n queue.fail(err);\n }\n })\n .finally(() => {\n ctrl.abort();\n queue.finish();\n });\n\n return queue.iterator;\n }\n\n return { runValidationCore, submit, submitOrThrow, validate, validateFields, validateStream };\n}\n\nexport type ValidationOps<TValues extends Record<string, unknown>> = ReturnType<typeof createValidationOps<TValues>>;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../_utils.cjs"),t=require("../_dev.cjs"),n=require("./array.cjs");let r=require("@vielzeug/arsenal");function i(i){function a(t){let n=t;return e.assertSafeKey(n),i.store.get(n)}function o(){return i.cachedValues??=(0,r.unflattenPaths)(Object.fromEntries(i.store))}function s(e,t){i.baseline.get(e)===t?i.dirty.delete(e):i.dirty.add(e)}function c(t,n,r={}){i.ensureNotDisposed();let a=t;e.assertSafeKey(a),i.store.set(a,n),s(a,n),r.touched&&i.touched.add(a),i.requestNotify(a)}function l(t){let n=t;return e.assertSafeKey(n),i.getFieldSnapshot(n)}function u(t,n){i.ensureNotDisposed();let r=t;e.assertSafeKey(r),i.fieldErrors.set(r,n),i.invalidateErrors(),i.requestNotify(r)}function d(t){i.ensureNotDisposed();let n=t;e.assertSafeKey(n),i.fieldErrors.delete(n)&&(i.invalidateErrors(),i.requestNotify(n))}function f(t={}){i.ensureNotDisposed(),i.fieldErrors.clear();for(let[n,r]of Object.entries(t))e.assertSafeKey(n),typeof r==`string`&&i.fieldErrors.set(n,r);i.invalidateErrors(),i.requestNotify()}function p(n,r){/\.\d+(\.|$)/.test(n)&&!i.store.has(n)&&t.warn(`${r}('${e.sanitizeForLog(n,80)}'): path looks like an array item key. Array items are stored as whole arrays — register on the parent key instead.`)}function m(t,n){i.ensureNotDisposed();let r=t;e.assertSafeKey(r),p(r,`setValidator`),i.fieldCtrls.get(r)?.abort(),i.fieldCtrls.delete(r),n?i.validators.set(r,n):(i.validators.delete(r),i.fieldErrors.delete(r)&&(i.invalidateErrors(),i.requestNotify(r)))}function h(t,n={}){i.ensureNotDisposed();let r=t;return e.assertSafeKey(r),p(r,`fields.register`),n.validator!==void 0&&m(t,n.validator),n.defaultValue!==void 0&&!i.store.has(r)&&(i.store.set(r,n.defaultValue),i.baseline.set(r,n.defaultValue),i.requestNotify(r)),()=>{i.disposed||S(t)}}function g(){let e=new Set;for(let t of i.store.keys())e.add(t);for(let t of i.validators.keys())e.add(t);return Object.freeze([...e])}function _(t){i.ensureNotDisposed();let n=t;e.assertSafeKey(n),i.touched.add(n),i.requestNotify(n)}function v(t){i.ensureNotDisposed();let n=t;e.assertSafeKey(n),i.touched.delete(n)&&i.requestNotify(n)}function y(){i.ensureNotDisposed();for(let e of i.store.keys())i.touched.add(e);for(let e of i.validators.keys())i.touched.add(e);i.requestNotify()}function b(){i.ensureNotDisposed(),i.touched.clear(),i.requestNotify()}function x(t){i.ensureNotDisposed();let n=t;e.assertSafeKey(n),i.fieldCtrls.get(n)?.abort(),i.fieldCtrls.delete(n),i.store.set(n,i.baseline.get(n)),i.dirty.delete(n),i.touched.delete(n),i.fieldErrors.delete(n)&&i.invalidateErrors(),i.requestNotify(n)}function S(t){i.ensureNotDisposed();let n=t;e.assertSafeKey(n),i.store.delete(n),i.baseline.delete(n),i.dirty.delete(n),i.touched.delete(n),i.validators.delete(n),i.arrayCache.delete(n),i.fieldCtrls.get(n)?.abort(),i.fieldCtrls.delete(n),i.fieldErrors.delete(n)&&i.invalidateErrors(),i.requestNotify(n)}function C(){i.ensureNotDisposed();for(let e of i.runCtrls)e.abort();for(let e of i.fieldCtrls.values())e.abort();i.runCtrls.clear(),i.fieldCtrls.clear(),i.validatingRuns.clear(),i.store.clear(),i.fieldErrors.clear(),i.touched.clear(),i.dirty.clear(),i.submitCount=0,i.invalidateErrors();for(let[e,t]of i.baseline)i.store.set(e,t);i.requestNotify()}function w(e){i.ensureNotDisposed();for(let e of i.runCtrls)e.abort();for(let e of i.fieldCtrls.values())e.abort();i.runCtrls.clear(),i.fieldCtrls.clear(),i.validatingRuns.clear();let t=(0,r.flattenPaths)(e);i.store.clear(),i.baseline.clear(),i.fieldErrors.clear(),i.touched.clear(),i.dirty.clear(),i.submitCount=0,i.invalidateErrors();for(let[e,n]of Object.entries(t))i.store.set(e,n),i.baseline.set(e,n);i.requestNotify()}function T(e){i.ensureNotDisposed();let t=(0,r.flattenPaths)(e);for(let[e,n]of Object.entries(t))i.baseline.set(e,n),i.store.set(e,n),i.dirty.delete(e);i.requestNotify(Object.keys(t))}function E(t){i.ensureNotDisposed();let r=t;e.assertSafeKey(r);let a=i.arrayCache.get(r);if(a)return a;let o=n.createArrayField(t,i.store,c);return i.arrayCache.set(r,o),o}return{array:E,clearError:d,field:l,get:a,listFields:g,patch:T,registerField:h,removeField:S,replace:w,reset:C,resetErrors:f,resetField:x,set:c,setError:u,setValidator:m,touch:_,touchAll:y,untouch:v,untouchAll:b,values:o}}exports.createValueOps=i;
|
|
2
|
+
//# sourceMappingURL=values.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"values.cjs","names":[],"sources":["../../src/core/values.ts"],"sourcesContent":["import type { FormContext } from './context';\n\nimport { warn } from '../_dev';\nimport { assertSafeKey, flattenValues, sanitizeForLog, unflattenValues } from '../_utils';\nimport {\n type ArrayField,\n type ErrorKeyOf,\n type FieldState,\n type FieldValidator,\n type FlatKeyOf,\n type RegisterFieldOptions,\n type SetOptions,\n type TypeAtPath,\n type Unsubscribe,\n} from '../types';\nimport { createArrayField } from './array';\n\n/**\n * Value/field-mutation operations: get/set/values, dirty tracking, errors, dynamic field\n * registration, touch, and reset/replace/patch/array. Moved verbatim from `createForm()`'s\n * \"Value access\" through \"Lifecycle: field operations\" sections.\n */\nexport function createValueOps<TValues extends Record<string, unknown>>(ctx: FormContext<TValues>) {\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 /* ======== Dirty tracking ======== */\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 /* ======== Field mutation ======== */\n\n function set<K extends FlatKeyOf<TValues>>(name: K, value: TypeAtPath<TValues, K>, options: SetOptions = {}): void {\n ctx.ensureNotDisposed();\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 function setError(name: ErrorKeyOf<TValues>, message: string): void {\n ctx.ensureNotDisposed();\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();\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 function resetErrors(nextErrors: Partial<Record<ErrorKeyOf<TValues>, string | undefined>> = {}): void {\n ctx.ensureNotDisposed();\n\n ctx.fieldErrors.clear();\n\n for (const [key, message] of Object.entries(nextErrors)) {\n assertSafeKey(key);\n\n if (typeof message === 'string') ctx.fieldErrors.set(key, message);\n }\n\n ctx.invalidateErrors();\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();\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();\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 /* ======== Field enumeration ======== */\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();\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();\n\n const key = name as string;\n\n assertSafeKey(key);\n\n if (ctx.touched.delete(key)) ctx.requestNotify(key);\n }\n\n function touchAll(): void {\n ctx.ensureNotDisposed();\n\n for (const name of ctx.store.keys()) ctx.touched.add(name);\n for (const name of ctx.validators.keys()) ctx.touched.add(name);\n\n ctx.requestNotify();\n }\n\n function untouchAll(): void {\n ctx.ensureNotDisposed();\n ctx.touched.clear();\n ctx.requestNotify();\n }\n\n /* ======== Lifecycle: field operations ======== */\n\n function resetField(name: FlatKeyOf<TValues>): void {\n ctx.ensureNotDisposed();\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();\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 function reset(): void {\n ctx.ensureNotDisposed();\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.fieldErrors.clear();\n ctx.touched.clear();\n ctx.dirty.clear();\n ctx.submitCount = 0;\n ctx.invalidateErrors();\n\n for (const [name, value] of ctx.baseline) ctx.store.set(name, value);\n\n ctx.requestNotify();\n }\n\n function replace(newValues: TValues): void {\n ctx.ensureNotDisposed();\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\n const flat = flattenValues(newValues as Record<string, unknown>);\n\n ctx.store.clear();\n ctx.baseline.clear();\n ctx.fieldErrors.clear();\n ctx.touched.clear();\n ctx.dirty.clear();\n ctx.submitCount = 0;\n ctx.invalidateErrors();\n\n for (const [name, value] of Object.entries(flat)) {\n ctx.store.set(name, value);\n ctx.baseline.set(name, value);\n }\n\n ctx.requestNotify();\n }\n\n function patch(partial: Record<string, unknown>): void {\n ctx.ensureNotDisposed();\n\n const flat = flattenValues(partial);\n\n for (const [key, value] of Object.entries(flat)) {\n ctx.baseline.set(key, value);\n ctx.store.set(key, value);\n ctx.dirty.delete(key);\n }\n\n ctx.requestNotify(Object.keys(flat));\n }\n\n /* ======== Array helpers ======== */\n\n function array(name: FlatKeyOf<TValues>): ArrayField {\n ctx.ensureNotDisposed();\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 return {\n array,\n clearError,\n field,\n get,\n listFields,\n patch,\n registerField,\n removeField,\n replace,\n reset,\n resetErrors,\n resetField,\n set,\n setError,\n setValidator,\n touch,\n touchAll,\n untouch,\n untouchAll,\n values,\n };\n}\n\nexport type ValueOps<TValues extends Record<string, unknown>> = ReturnType<typeof createValueOps<TValues>>;\n"],"mappings":"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"}
|