@vielzeug/forge 1.3.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -53
- package/dist/_utils.cjs +1 -2
- package/dist/_utils.d.ts +1 -10
- package/dist/_utils.d.ts.map +1 -1
- package/dist/_utils.js +2 -32
- package/dist/adapters/form-data.cjs +1 -1
- package/dist/adapters/form-data.cjs.map +1 -1
- package/dist/adapters/form-data.d.ts.map +1 -1
- package/dist/adapters/form-data.js +5 -5
- package/dist/adapters/form-data.js.map +1 -1
- package/dist/core/path.cjs +2 -0
- package/dist/core/path.cjs.map +1 -0
- package/dist/core/path.d.ts +22 -0
- package/dist/core/path.d.ts.map +1 -0
- package/dist/core/path.js +125 -0
- package/dist/core/path.js.map +1 -0
- package/dist/devtools.cjs +1 -1
- package/dist/devtools.cjs.map +1 -1
- package/dist/devtools.d.ts +3 -36
- package/dist/devtools.d.ts.map +1 -1
- package/dist/devtools.js +6 -24
- package/dist/devtools.js.map +1 -1
- package/dist/dom.cjs +2 -0
- package/dist/dom.cjs.map +1 -0
- package/dist/dom.d.ts +9 -0
- package/dist/dom.d.ts.map +1 -0
- package/dist/dom.js +24 -0
- package/dist/dom.js.map +1 -0
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +4 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -7
- 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 +3 -5
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +225 -71
- package/dist/form.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +3 -4
- package/dist/spell.cjs +2 -0
- package/dist/spell.cjs.map +1 -0
- package/dist/spell.d.ts +5 -0
- package/dist/spell.d.ts.map +1 -0
- package/dist/spell.js +57 -0
- package/dist/spell.js.map +1 -0
- package/dist/types.d.ts +70 -375
- package/dist/types.d.ts.map +1 -1
- package/dist/vault.cjs +2 -0
- package/dist/vault.cjs.map +1 -0
- package/dist/vault.d.ts +10 -0
- package/dist/vault.d.ts.map +1 -0
- package/dist/vault.js +14 -0
- package/dist/vault.js.map +1 -0
- package/package.json +36 -11
- package/dist/_dev.cjs +0 -2
- package/dist/_dev.cjs.map +0 -1
- package/dist/_dev.js +0 -12
- package/dist/_dev.js.map +0 -1
- package/dist/_utils.cjs.map +0 -1
- package/dist/_utils.js.map +0 -1
- package/dist/adapters/validators.cjs +0 -2
- package/dist/adapters/validators.cjs.map +0 -1
- package/dist/adapters/validators.d.ts +0 -44
- package/dist/adapters/validators.d.ts.map +0 -1
- package/dist/adapters/validators.js +0 -20
- package/dist/adapters/validators.js.map +0 -1
- package/dist/core/array.cjs +0 -2
- package/dist/core/array.cjs.map +0 -1
- package/dist/core/array.d.ts +0 -10
- package/dist/core/array.d.ts.map +0 -1
- package/dist/core/array.js +0 -46
- package/dist/core/array.js.map +0 -1
- package/dist/core/asyncQueue.cjs +0 -2
- package/dist/core/asyncQueue.cjs.map +0 -1
- package/dist/core/asyncQueue.d.ts +0 -22
- package/dist/core/asyncQueue.d.ts.map +0 -1
- package/dist/core/asyncQueue.js +0 -67
- package/dist/core/asyncQueue.js.map +0 -1
- package/dist/core/context.cjs +0 -2
- package/dist/core/context.cjs.map +0 -1
- package/dist/core/context.d.ts +0 -10
- package/dist/core/context.d.ts.map +0 -1
- package/dist/core/context.js +0 -176
- package/dist/core/context.js.map +0 -1
- package/dist/core/lifecycle.cjs +0 -2
- package/dist/core/lifecycle.cjs.map +0 -1
- package/dist/core/lifecycle.d.ts +0 -13
- package/dist/core/lifecycle.d.ts.map +0 -1
- package/dist/core/lifecycle.js +0 -42
- package/dist/core/lifecycle.js.map +0 -1
- package/dist/core/observe.cjs +0 -2
- package/dist/core/observe.cjs.map +0 -1
- package/dist/core/observe.d.ts +0 -18
- package/dist/core/observe.d.ts.map +0 -1
- package/dist/core/observe.js +0 -90
- package/dist/core/observe.js.map +0 -1
- package/dist/core/scope.cjs +0 -2
- package/dist/core/scope.cjs.map +0 -1
- package/dist/core/scope.d.ts +0 -8
- package/dist/core/scope.d.ts.map +0 -1
- package/dist/core/scope.js +0 -286
- package/dist/core/scope.js.map +0 -1
- 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
- package/dist/types.cjs +0 -2
- package/dist/types.cjs.map +0 -1
- package/dist/types.js +0 -21
- package/dist/types.js.map +0 -1
package/dist/form.js
CHANGED
|
@@ -1,89 +1,243 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { createFormContext as n } from "./core/context.js";
|
|
4
|
-
import { createLifecycleOps as r } from "./core/lifecycle.js";
|
|
5
|
-
import { createObserveOps as i } from "./core/observe.js";
|
|
6
|
-
import { createScopedForm as a } from "./core/scope.js";
|
|
7
|
-
import { createValidationOps as o } from "./core/validation.js";
|
|
8
|
-
import { createValueOps as s } from "./core/values.js";
|
|
1
|
+
import { ForgeConfigError as e, ForgeDisposedError as t, ForgeSubmitError as n, ForgeValidationError as r } from "./errors.js";
|
|
2
|
+
import { assertSafeKey as i, hasAtPath as a, immutable as o, isRecord as s, normalizeErrors as c, readAtPath as l, readError as u, readMeta as d, resetAtPath as f, touchAll as p, writeAtPath as m, writeMeta as h } from "./core/path.js";
|
|
9
3
|
//#region src/form.ts
|
|
10
|
-
function
|
|
11
|
-
let
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
4
|
+
function g(e, t) {
|
|
5
|
+
let n = e, r = /* @__PURE__ */ new Set();
|
|
6
|
+
return {
|
|
7
|
+
dispose() {
|
|
8
|
+
r.clear();
|
|
9
|
+
},
|
|
10
|
+
read: () => n,
|
|
11
|
+
subscribe(e) {
|
|
12
|
+
return r.add(e), () => r.delete(e);
|
|
13
|
+
},
|
|
14
|
+
write(e) {
|
|
15
|
+
if (e !== n) {
|
|
16
|
+
n = e;
|
|
17
|
+
for (let e of [...r]) try {
|
|
18
|
+
e();
|
|
19
|
+
} catch (e) {
|
|
20
|
+
t(e);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function _(e) {
|
|
27
|
+
queueMicrotask(() => {
|
|
28
|
+
throw e;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function v(e, t) {
|
|
32
|
+
let n = l(e.value, t), r = l(e.baseline, t);
|
|
33
|
+
return Object.freeze({
|
|
34
|
+
dirty: n !== r || a(e.value, t) !== a(e.baseline, t),
|
|
35
|
+
error: u(e.errors, t),
|
|
36
|
+
touched: d(e.touched, t),
|
|
37
|
+
value: n
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function y(e) {
|
|
41
|
+
return Object.freeze({
|
|
42
|
+
error: e.error,
|
|
43
|
+
errors: e.errors,
|
|
44
|
+
submitCount: e.submitCount,
|
|
45
|
+
submitting: e.isSubmitting,
|
|
46
|
+
touched: Object.keys(e.touched).length > 0,
|
|
47
|
+
valid: e.error === void 0 && e.errors === void 0,
|
|
48
|
+
validating: e.isValidating
|
|
23
49
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
50
|
+
}
|
|
51
|
+
function b(a) {
|
|
52
|
+
let u = o(a.initialValues);
|
|
53
|
+
function d(e) {
|
|
54
|
+
if (!a.onSubscriberError) {
|
|
55
|
+
_(e);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
a.onSubscriberError(e);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
_(e);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
let b = g({
|
|
65
|
+
baseline: u,
|
|
66
|
+
error: void 0,
|
|
67
|
+
errors: void 0,
|
|
68
|
+
isSubmitting: !1,
|
|
69
|
+
isValidating: !1,
|
|
70
|
+
submitCount: 0,
|
|
71
|
+
touched: {},
|
|
72
|
+
value: u
|
|
73
|
+
}, d), x = new AbortController(), S = !1, C;
|
|
74
|
+
function w(e) {
|
|
75
|
+
if (S) throw new t(e);
|
|
76
|
+
}
|
|
77
|
+
function T(e) {
|
|
78
|
+
b.write(Object.freeze(e(b.read())));
|
|
79
|
+
}
|
|
80
|
+
async function E(e) {
|
|
81
|
+
w("validate"), C?.abort();
|
|
82
|
+
let t = new AbortController(), n = e ? AbortSignal.any([t.signal, e]) : t.signal;
|
|
83
|
+
C = t, T((e) => ({
|
|
84
|
+
...e,
|
|
85
|
+
isValidating: !0
|
|
86
|
+
}));
|
|
87
|
+
try {
|
|
88
|
+
let e = a.validate ? await a.validate(b.read().value, n) : void 0;
|
|
89
|
+
if (n.aborted || C !== t) return Object.freeze({ status: "aborted" });
|
|
90
|
+
let r = e?.fields ? c(o(e.fields)) : void 0, i = e?.formError;
|
|
91
|
+
return T((e) => ({
|
|
92
|
+
...e,
|
|
93
|
+
error: i,
|
|
94
|
+
errors: r
|
|
95
|
+
})), Object.freeze(r === void 0 && i === void 0 ? { status: "valid" } : {
|
|
96
|
+
errors: r,
|
|
97
|
+
formError: i,
|
|
98
|
+
status: "invalid"
|
|
99
|
+
});
|
|
100
|
+
} catch (e) {
|
|
101
|
+
if (n.aborted || C !== t) return Object.freeze({ status: "aborted" });
|
|
102
|
+
throw new r("Form validation failed.", { cause: e });
|
|
103
|
+
} finally {
|
|
104
|
+
C === t && (C = void 0, S || T((e) => ({
|
|
105
|
+
...e,
|
|
106
|
+
isValidating: !1
|
|
107
|
+
})));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function D(t) {
|
|
111
|
+
return Object.assign({
|
|
112
|
+
get dirty() {
|
|
113
|
+
return v(b.read(), t).dirty;
|
|
114
|
+
},
|
|
115
|
+
get error() {
|
|
116
|
+
return v(b.read(), t).error;
|
|
117
|
+
},
|
|
118
|
+
reset() {
|
|
119
|
+
w("field().reset"), C?.abort(), T((e) => ({
|
|
120
|
+
...e,
|
|
121
|
+
touched: h(e.touched, t, !1),
|
|
122
|
+
value: f(e.value, e.baseline, t)
|
|
123
|
+
}));
|
|
124
|
+
},
|
|
125
|
+
set(e) {
|
|
126
|
+
w("field().set"), C?.abort();
|
|
127
|
+
let n = l(b.read().value, t), r = typeof e == "function" ? e(n) : e;
|
|
128
|
+
T((e) => ({
|
|
129
|
+
...e,
|
|
130
|
+
value: m(e.value, t, r)
|
|
131
|
+
}));
|
|
132
|
+
},
|
|
133
|
+
subscribe(e, n = {}) {
|
|
134
|
+
w("field().subscribe");
|
|
135
|
+
let r = v(b.read(), t);
|
|
136
|
+
return n.immediate && e(r), b.subscribe(() => {
|
|
137
|
+
let n = v(b.read(), t);
|
|
138
|
+
(n.value !== r.value || n.error !== r.error || n.touched !== r.touched || n.dirty !== r.dirty) && (r = n, e(n));
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
touch() {
|
|
142
|
+
w("field().touch"), T((e) => ({
|
|
143
|
+
...e,
|
|
144
|
+
touched: h(e.touched, t, !0)
|
|
145
|
+
}));
|
|
146
|
+
},
|
|
147
|
+
get touched() {
|
|
148
|
+
return v(b.read(), t).touched;
|
|
149
|
+
},
|
|
150
|
+
get value() {
|
|
151
|
+
return v(b.read(), t).value;
|
|
152
|
+
}
|
|
153
|
+
}, { field(n) {
|
|
154
|
+
i(n);
|
|
155
|
+
let r = l(b.read().value, t);
|
|
156
|
+
if (r !== void 0 && !s(r)) throw new e(`Cannot select '${n}' because the current field value is not an object.`);
|
|
157
|
+
return D([...t, n]);
|
|
158
|
+
} });
|
|
159
|
+
}
|
|
160
|
+
let O = {
|
|
30
161
|
get disposalSignal() {
|
|
31
|
-
return
|
|
162
|
+
return x.signal;
|
|
32
163
|
},
|
|
33
|
-
|
|
34
|
-
|
|
164
|
+
dispose() {
|
|
165
|
+
S || (S = !0, x.abort(), C?.abort(), b.dispose());
|
|
35
166
|
},
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
register: h,
|
|
39
|
-
remove: g,
|
|
40
|
-
setValidator: _
|
|
167
|
+
get disposed() {
|
|
168
|
+
return S;
|
|
41
169
|
},
|
|
42
|
-
|
|
43
|
-
return
|
|
170
|
+
field(e) {
|
|
171
|
+
return i(e), D([e]);
|
|
44
172
|
},
|
|
45
|
-
|
|
46
|
-
|
|
173
|
+
reset(e) {
|
|
174
|
+
w("reset"), C?.abort();
|
|
175
|
+
let t = e === void 0 ? b.read().baseline : o(e);
|
|
176
|
+
T((e) => ({
|
|
177
|
+
...e,
|
|
178
|
+
baseline: t,
|
|
179
|
+
error: void 0,
|
|
180
|
+
errors: void 0,
|
|
181
|
+
touched: {},
|
|
182
|
+
value: t
|
|
183
|
+
}));
|
|
47
184
|
},
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
getStateSnapshot: l.getStateSnapshot,
|
|
58
|
-
incrementSubmitCount: () => {
|
|
59
|
-
l.submitCount++;
|
|
60
|
-
},
|
|
61
|
-
invalidateErrors: l.invalidateErrors,
|
|
62
|
-
isDisposed: () => l.disposed,
|
|
63
|
-
isSubmitting: () => l.isSubmittingState,
|
|
64
|
-
requestNotify: l.requestNotify,
|
|
65
|
-
root: x,
|
|
66
|
-
runValidationCore: y,
|
|
67
|
-
setSubmitting: (e) => {
|
|
68
|
-
l.isSubmittingState = e;
|
|
69
|
-
},
|
|
70
|
-
store: l.store,
|
|
71
|
-
touched: l.touched,
|
|
72
|
-
validators: l.validators
|
|
73
|
-
}, e);
|
|
74
|
-
return l.scopeCache.set(t, r), r;
|
|
185
|
+
set(e) {
|
|
186
|
+
w("set"), C?.abort();
|
|
187
|
+
let t = typeof e == "function" ? e(O.value) : e;
|
|
188
|
+
T((e) => ({
|
|
189
|
+
...e,
|
|
190
|
+
error: void 0,
|
|
191
|
+
errors: void 0,
|
|
192
|
+
value: o(t)
|
|
193
|
+
}));
|
|
75
194
|
},
|
|
76
195
|
get state() {
|
|
77
|
-
return
|
|
196
|
+
return y(b.read());
|
|
197
|
+
},
|
|
198
|
+
async submit(e) {
|
|
199
|
+
if (w("submit"), b.read().isSubmitting) throw new n("submit() called while a submission is already in progress");
|
|
200
|
+
T((e) => ({
|
|
201
|
+
...e,
|
|
202
|
+
isSubmitting: !0,
|
|
203
|
+
submitCount: e.submitCount + 1,
|
|
204
|
+
touched: p(e.value)
|
|
205
|
+
}));
|
|
206
|
+
try {
|
|
207
|
+
let t = await E();
|
|
208
|
+
return t.status === "aborted" ? Object.freeze({
|
|
209
|
+
ok: !1,
|
|
210
|
+
type: "aborted"
|
|
211
|
+
}) : t.status === "invalid" ? Object.freeze({
|
|
212
|
+
errors: t.errors,
|
|
213
|
+
formError: t.formError,
|
|
214
|
+
ok: !1,
|
|
215
|
+
type: "validation"
|
|
216
|
+
}) : Object.freeze({
|
|
217
|
+
ok: !0,
|
|
218
|
+
value: await e(O.value)
|
|
219
|
+
});
|
|
220
|
+
} finally {
|
|
221
|
+
S || T((e) => ({
|
|
222
|
+
...e,
|
|
223
|
+
isSubmitting: !1
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
subscribe(e, t = {}) {
|
|
228
|
+
return w("subscribe"), t.immediate && e(y(b.read())), b.subscribe(() => e(y(b.read())));
|
|
78
229
|
},
|
|
79
|
-
subscribeScoped: f.subscribe,
|
|
80
230
|
[Symbol.dispose]() {
|
|
81
|
-
|
|
231
|
+
O.dispose();
|
|
232
|
+
},
|
|
233
|
+
validate: E,
|
|
234
|
+
get value() {
|
|
235
|
+
return b.read().value;
|
|
82
236
|
}
|
|
83
237
|
};
|
|
84
|
-
return
|
|
238
|
+
return O;
|
|
85
239
|
}
|
|
86
240
|
//#endregion
|
|
87
|
-
export {
|
|
241
|
+
export { b as createForm };
|
|
88
242
|
|
|
89
243
|
//# sourceMappingURL=form.js.map
|
package/dist/form.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.js","names":[],"sources":["../src/form.ts"],"sourcesContent":["import { warn } from './_dev';\nimport { sanitizeForLog } from './_utils';\nimport { createFormContext } from './core/context';\nimport { createLifecycleOps } from './core/lifecycle';\nimport { createObserveOps } from './core/observe';\nimport { createScopedForm, type ScopeContext } from './core/scope';\nimport { createValidationOps } from './core/validation';\nimport { createValueOps } from './core/values';\nimport { type FlatKeyOf, type Form, type FormOptions, type ScopedValues } from './types';\n\n/* -------------------- createForm -------------------- */\n\nexport function createForm<TInit extends object>(\n init: Omit<FormOptions<TInit & Record<string, unknown>>, 'defaultValues'> & {\n defaultValues: TInit | (() => Promise<TInit>);\n },\n): Form<TInit & Record<string, unknown>>;\nexport function createForm<TValues extends Record<string, unknown> = Record<string, unknown>>(\n init?: FormOptions<TValues>,\n): Form<TValues>;\nexport function createForm<TValues extends Record<string, unknown> = Record<string, unknown>>(\n init: FormOptions<TValues> = {},\n): Form<TValues> {\n const ctx = createFormContext<TValues>(init);\n const valueOps = createValueOps(ctx);\n const validationOps = createValidationOps(ctx, { touchAll: valueOps.touchAll, values: valueOps.values });\n const observeOps = createObserveOps(ctx, {\n set: valueOps.set,\n touch: valueOps.touch,\n validateFields: validationOps.validateFields,\n });\n const lifecycleOps = createLifecycleOps(ctx);\n\n /* ---- Async defaultValues ---- */\n\n if (typeof init.defaultValues === 'function') {\n void init\n .defaultValues()\n .then((resolved) => {\n ctx.loadingState = false;\n\n if (ctx.disposed) return;\n\n valueOps.replace(resolved);\n })\n .catch((err: unknown) => {\n ctx.loadingState = false;\n\n if (ctx.disposed) return;\n\n warn(`defaultValues factory rejected. Form will be empty. Error: ${sanitizeForLog(String(err))}`);\n init.onLoadError?.(err);\n ctx.requestNotify();\n });\n }\n\n /* ======== Public form object ======== */\n\n // registerField/removeField/setValidator/listFields are nested under `fields`, not top-level.\n const { listFields, registerField, removeField, setValidator, ...publicValueOps } = valueOps;\n // runValidationCore is internal-only — needed by scope(), not part of the public Form<T> surface.\n const { runValidationCore, ...publicValidationOps } = validationOps;\n\n // `scope` is defined as a method on publicForm so that `const publicForm` can capture itself\n // via closure without a forward-reference lint disable.\n const publicForm: Form<TValues> = {\n ...publicValueOps,\n ...publicValidationOps,\n ...observeOps,\n ...lifecycleOps,\n batch: ctx.batch,\n get disposalSignal() {\n return ctx.disposeController.signal;\n },\n get disposed() {\n return ctx.disposed;\n },\n fields: {\n list: listFields,\n register: registerField,\n remove: removeField,\n setValidator,\n },\n get isLoading() {\n return ctx.loadingState;\n },\n get isSubmitting() {\n return ctx.isSubmittingState;\n },\n scope<P extends FlatKeyOf<TValues>>(prefix: P): Form<ScopedValues<TValues, P>> {\n const key = prefix as string;\n const cached = ctx.scopeCache.get(key);\n\n if (cached) return cached as Form<ScopedValues<TValues, P>>;\n\n const scopeCtx: ScopeContext<TValues> = {\n baseline: ctx.baseline,\n dirty: ctx.dirty,\n ensureNotDisposed: ctx.ensureNotDisposed,\n fieldCtrls: ctx.fieldCtrls,\n fieldErrors: ctx.fieldErrors,\n getStateSnapshot: ctx.getStateSnapshot,\n incrementSubmitCount: () => {\n ctx.submitCount++;\n },\n invalidateErrors: ctx.invalidateErrors,\n isDisposed: () => ctx.disposed,\n isSubmitting: () => ctx.isSubmittingState,\n requestNotify: ctx.requestNotify,\n root: publicForm,\n runValidationCore,\n setSubmitting: (v) => {\n ctx.isSubmittingState = v;\n },\n store: ctx.store,\n touched: ctx.touched,\n validators: ctx.validators,\n };\n\n const scoped = createScopedForm<TValues, P>(scopeCtx, prefix) as Form<ScopedValues<TValues, P>>;\n\n ctx.scopeCache.set(key, scoped as Form<never>);\n\n return scoped;\n },\n get state() {\n return ctx.getStateSnapshot();\n },\n // On a root form, subscribeScoped behaves identically to subscribe — no prefix filtering.\n subscribeScoped: observeOps.subscribe,\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n\n return publicForm;\n}\n"],"mappings":";;;;;;;;;AAoBA,SAAgB,EACd,IAA6B,CAAC,GACf;CACf,IAAM,IAAM,EAA2B,CAAI,GACrC,IAAW,EAAe,CAAG,GAC7B,IAAgB,EAAoB,GAAK;EAAE,UAAU,EAAS;EAAU,QAAQ,EAAS;CAAO,CAAC,GACjG,IAAa,EAAiB,GAAK;EACvC,KAAK,EAAS;EACd,OAAO,EAAS;EAChB,gBAAgB,EAAc;CAChC,CAAC,GACK,IAAe,EAAmB,CAAG;CAI3C,AAAI,OAAO,EAAK,iBAAkB,cAChC,EACG,cAAc,CAAC,CACf,MAAM,MAAa;EAClB,EAAI,eAAe,IAEf,GAAI,YAER,EAAS,QAAQ,CAAQ;CAC3B,CAAC,CAAC,CACD,OAAO,MAAiB;EACvB,EAAI,eAAe,IAEf,GAAI,aAER,EAAK,8DAA8D,EAAe,OAAO,CAAG,CAAC,GAAG,GAChG,EAAK,cAAc,CAAG,GACtB,EAAI,cAAc;CACpB,CAAC;CAML,IAAM,EAAE,eAAY,kBAAe,gBAAa,iBAAc,GAAG,MAAmB,GAE9E,EAAE,sBAAmB,GAAG,MAAwB,GAIhD,IAA4B;EAChC,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,OAAO,EAAI;EACX,IAAI,iBAAiB;GACnB,OAAO,EAAI,kBAAkB;EAC/B;EACA,IAAI,WAAW;GACb,OAAO,EAAI;EACb;EACA,QAAQ;GACN,MAAM;GACN,UAAU;GACV,QAAQ;GACR;EACF;EACA,IAAI,YAAY;GACd,OAAO,EAAI;EACb;EACA,IAAI,eAAe;GACjB,OAAO,EAAI;EACb;EACA,MAAoC,GAA2C;GAC7E,IAAM,IAAM,GACN,IAAS,EAAI,WAAW,IAAI,CAAG;GAErC,IAAI,GAAQ,OAAO;GA0BnB,IAAM,IAAS,EAA6B;IAvB1C,UAAU,EAAI;IACd,OAAO,EAAI;IACX,mBAAmB,EAAI;IACvB,YAAY,EAAI;IAChB,aAAa,EAAI;IACjB,kBAAkB,EAAI;IACtB,4BAA4B;KAC1B,EAAI;IACN;IACA,kBAAkB,EAAI;IACtB,kBAAkB,EAAI;IACtB,oBAAoB,EAAI;IACxB,eAAe,EAAI;IACnB,MAAM;IACN;IACA,gBAAgB,MAAM;KACpB,EAAI,oBAAoB;IAC1B;IACA,OAAO,EAAI;IACX,SAAS,EAAI;IACb,YAAY,EAAI;GAG0B,GAAU,CAAM;GAI5D,OAFA,EAAI,WAAW,IAAI,GAAK,CAAqB,GAEtC;EACT;EACA,IAAI,QAAQ;GACV,OAAO,EAAI,iBAAiB;EAC9B;EAEA,iBAAiB,EAAW;EAC5B,CAAC,OAAO,WAAW;GACjB,KAAK,QAAQ;EACf;CACF;CAEA,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"form.js","names":[],"sources":["../src/form.ts"],"sourcesContent":["import type {\n Field,\n FieldState,\n Form,\n FormErrors,\n FormOptions,\n FormState,\n MaybePromise,\n ReadonlyDeep,\n SubmitResult,\n SubscribeOptions,\n Unsubscribe,\n ValidationErrors,\n ValidationResult,\n} from './types';\n\nimport {\n assertSafeKey,\n hasAtPath,\n immutable,\n isRecord,\n normalizeErrors,\n readAtPath,\n readError,\n readMeta,\n resetAtPath,\n type MetaRoot,\n touchAll,\n writeAtPath,\n writeMeta,\n} from './core/path';\nimport { ForgeConfigError, ForgeDisposedError, ForgeSubmitError, ForgeValidationError } from './errors';\n\ntype InternalState<TValues extends Record<string, unknown>> = Readonly<{\n baseline: TValues;\n error: string | undefined;\n errors: FormErrors<TValues> | undefined;\n isSubmitting: boolean;\n isValidating: boolean;\n submitCount: number;\n touched: MetaRoot;\n value: TValues;\n}>;\n\ntype Store<T> = {\n dispose(): void;\n read(): T;\n subscribe(listener: () => void): Unsubscribe;\n write(next: T): void;\n};\n\nfunction createStore<T>(initial: T, onListenerError: (error: unknown) => void): Store<T> {\n let current = initial;\n const listeners = new Set<() => void>();\n\n return {\n dispose() {\n listeners.clear();\n },\n read: () => current,\n subscribe(listener) {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n write(next) {\n if (next === current) return;\n\n current = next;\n\n for (const listener of [...listeners]) {\n try {\n listener();\n } catch (error) {\n onListenerError(error);\n }\n }\n },\n };\n}\n\nfunction rethrowAsync(error: unknown): void {\n queueMicrotask(() => {\n throw error;\n });\n}\n\nfunction makeFieldState<V>(state: InternalState<Record<string, unknown>>, path: readonly string[]): FieldState<V> {\n const value = readAtPath<V>(state.value, path);\n const baseline = readAtPath<V>(state.baseline, path);\n\n return Object.freeze({\n dirty: value !== baseline || hasAtPath(state.value, path) !== hasAtPath(state.baseline, path),\n error: readError(state.errors, path),\n touched: readMeta(state.touched, path),\n value: value as ReadonlyDeep<V>,\n });\n}\n\nfunction makeFormState<TValues extends Record<string, unknown>>(current: InternalState<TValues>): FormState<TValues> {\n return Object.freeze({\n error: current.error,\n errors: current.errors,\n submitCount: current.submitCount,\n submitting: current.isSubmitting,\n touched: Object.keys(current.touched).length > 0,\n valid: current.error === undefined && current.errors === undefined,\n validating: current.isValidating,\n });\n}\n\n/** One immutable value tree and one explicit full-form validator keep form behavior locally understandable. */\nexport function createForm<TValues extends Record<string, unknown>>(options: FormOptions<TValues>): Form<TValues> {\n const initial = immutable(options.initialValues);\n\n function reportListenerError(error: unknown): void {\n if (!options.onSubscriberError) {\n rethrowAsync(error);\n\n return;\n }\n\n try {\n options.onSubscriberError(error);\n } catch (reporterError) {\n rethrowAsync(reporterError);\n }\n }\n\n const store = createStore<InternalState<TValues>>(\n {\n baseline: initial,\n error: undefined,\n errors: undefined,\n isSubmitting: false,\n isValidating: false,\n submitCount: 0,\n touched: {},\n value: initial,\n },\n reportListenerError,\n );\n const disposalController = new AbortController();\n let disposed = false;\n let validationController: AbortController | undefined;\n\n function ensureActive(operation: string): void {\n if (disposed) throw new ForgeDisposedError(operation);\n }\n\n function write(update: (current: InternalState<TValues>) => InternalState<TValues>): void {\n store.write(Object.freeze(update(store.read())));\n }\n\n async function validate(externalSignal?: AbortSignal): Promise<ValidationResult<TValues>> {\n ensureActive('validate');\n validationController?.abort();\n\n const controller = new AbortController();\n const signal = externalSignal ? AbortSignal.any([controller.signal, externalSignal]) : controller.signal;\n\n validationController = controller;\n write((current) => ({ ...current, isValidating: true }));\n\n try {\n const result: ValidationErrors<TValues> | undefined = options.validate\n ? await options.validate(store.read().value as ReadonlyDeep<TValues>, signal)\n : undefined;\n\n if (signal.aborted || validationController !== controller) return Object.freeze({ status: 'aborted' });\n\n const errors = result?.fields\n ? (normalizeErrors(immutable(result.fields)) as FormErrors<TValues> | undefined)\n : undefined;\n const formError = result?.formError;\n\n write((current) => ({ ...current, error: formError, errors }));\n\n return errors === undefined && formError === undefined\n ? Object.freeze({ status: 'valid' })\n : Object.freeze({ errors, formError, status: 'invalid' });\n } catch (error) {\n if (signal.aborted || validationController !== controller) return Object.freeze({ status: 'aborted' });\n\n throw new ForgeValidationError('Form validation failed.', { cause: error });\n } finally {\n if (validationController === controller) {\n validationController = undefined;\n\n if (!disposed) write((current) => ({ ...current, isValidating: false }));\n }\n }\n }\n\n function createField<V>(path: readonly string[]): Field<V> {\n const common = {\n get dirty() {\n return makeFieldState<V>(store.read() as InternalState<Record<string, unknown>>, path).dirty;\n },\n get error() {\n return makeFieldState<V>(store.read() as InternalState<Record<string, unknown>>, path).error;\n },\n reset() {\n ensureActive('field().reset');\n validationController?.abort();\n write((current) => ({\n ...current,\n touched: writeMeta(current.touched, path, false),\n value: resetAtPath(current.value, current.baseline, path),\n }));\n },\n set(next: V | ((previous: ReadonlyDeep<V>) => V)) {\n ensureActive('field().set');\n validationController?.abort();\n\n const previous = readAtPath<V>(store.read().value, path) as ReadonlyDeep<V>;\n const value = typeof next === 'function' ? (next as (current: ReadonlyDeep<V>) => V)(previous) : next;\n\n write((current) => ({ ...current, value: writeAtPath(current.value, path, value) }));\n },\n subscribe(listener: (state: FieldState<V>) => void, subscribeOptions: SubscribeOptions = {}): Unsubscribe {\n ensureActive('field().subscribe');\n\n let previous = makeFieldState<V>(store.read() as InternalState<Record<string, unknown>>, path);\n\n if (subscribeOptions.immediate) listener(previous);\n\n return store.subscribe(() => {\n const next = makeFieldState<V>(store.read() as InternalState<Record<string, unknown>>, path);\n\n if (\n next.value === previous.value &&\n next.error === previous.error &&\n next.touched === previous.touched &&\n next.dirty === previous.dirty\n ) {\n return;\n }\n\n previous = next;\n listener(next);\n });\n },\n touch() {\n ensureActive('field().touch');\n write((current) => ({ ...current, touched: writeMeta(current.touched, path, true) }));\n },\n get touched() {\n return makeFieldState<V>(store.read() as InternalState<Record<string, unknown>>, path).touched;\n },\n get value() {\n return makeFieldState<V>(store.read() as InternalState<Record<string, unknown>>, path).value;\n },\n };\n\n return Object.assign(common, {\n field<K extends keyof NonNullable<V> & string>(key: K): Field<NonNullable<V>[K]> {\n assertSafeKey(key);\n\n const current = readAtPath(store.read().value, path);\n\n if (current !== undefined && !isRecord(current)) {\n throw new ForgeConfigError(`Cannot select '${key}' because the current field value is not an object.`);\n }\n\n return createField<NonNullable<V>[K]>([...path, key]);\n },\n }) as Field<V>;\n }\n\n const form: Form<TValues> = {\n get disposalSignal() {\n return disposalController.signal;\n },\n dispose() {\n if (disposed) return;\n\n disposed = true;\n disposalController.abort();\n validationController?.abort();\n store.dispose();\n },\n get disposed() {\n return disposed;\n },\n field(key) {\n assertSafeKey(key);\n\n return createField<TValues[typeof key]>([key]);\n },\n reset(next) {\n ensureActive('reset');\n validationController?.abort();\n\n const baseline = next === undefined ? store.read().baseline : immutable(next);\n\n write((current) => ({\n ...current,\n baseline,\n error: undefined,\n errors: undefined,\n touched: {},\n value: baseline,\n }));\n },\n set(next) {\n ensureActive('set');\n validationController?.abort();\n\n const value =\n typeof next === 'function' ? (next as (current: ReadonlyDeep<TValues>) => TValues)(form.value) : next;\n\n write((current) => ({ ...current, error: undefined, errors: undefined, value: immutable(value) }));\n },\n get state() {\n return makeFormState(store.read());\n },\n async submit<TResult = void>(\n handler: (values: ReadonlyDeep<TValues>) => MaybePromise<TResult>,\n ): Promise<SubmitResult<TResult, TValues>> {\n ensureActive('submit');\n\n if (store.read().isSubmitting)\n throw new ForgeSubmitError('submit() called while a submission is already in progress');\n\n write((current) => ({\n ...current,\n isSubmitting: true,\n submitCount: current.submitCount + 1,\n touched: touchAll(current.value),\n }));\n\n try {\n const result = await validate();\n\n if (result.status === 'aborted') return Object.freeze({ ok: false, type: 'aborted' });\n\n if (result.status === 'invalid') {\n return Object.freeze({\n errors: result.errors,\n formError: result.formError,\n ok: false,\n type: 'validation',\n });\n }\n\n return Object.freeze({ ok: true, value: await handler(form.value) });\n } finally {\n if (!disposed) write((current) => ({ ...current, isSubmitting: false }));\n }\n },\n subscribe(listener, subscribeOptions: SubscribeOptions = {}): Unsubscribe {\n ensureActive('subscribe');\n\n if (subscribeOptions.immediate) listener(makeFormState(store.read()));\n\n return store.subscribe(() => listener(makeFormState(store.read())));\n },\n [Symbol.dispose]() {\n form.dispose();\n },\n validate,\n get value() {\n return store.read().value as ReadonlyDeep<TValues>;\n },\n };\n\n return form;\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./form.cjs"),n=require("./adapters/form-data.cjs");exports.ForgeConfigError=e.ForgeConfigError,exports.ForgeDisposedError=e.ForgeDisposedError,exports.ForgeError=e.ForgeError,exports.ForgeSubmitError=e.ForgeSubmitError,exports.ForgeValidationError=e.ForgeValidationError,exports.createForm=t.createForm,exports.toFormData=n.toFormData;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ForgeConfigError as e, ForgeDisposedError as t, ForgeError as n, ForgeSubmitError as r, ForgeValidationError as i } from "./errors.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
export { a as FORM_ERROR, e as ForgeConfigError, t as ForgeDisposedError, n as ForgeError, r as ForgeSubmitError, i as ForgeValidationError, o as ValidationModes, s as createForm, c as toFormData };
|
|
2
|
+
import { createForm as a } from "./form.js";
|
|
3
|
+
import { toFormData as o } from "./adapters/form-data.js";
|
|
4
|
+
export { e as ForgeConfigError, t as ForgeDisposedError, n as ForgeError, r as ForgeSubmitError, i as ForgeValidationError, a as createForm, o as toFormData };
|
package/dist/spell.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./core/path.cjs");require("@vielzeug/spell");function t(e){return e.code===`invalid_union`}function n(e,t){return t.aborted?Promise.resolve(void 0):new Promise((n,r)=>{let i=()=>n(void 0);t.addEventListener(`abort`,i,{once:!0}),e.then(e=>{t.removeEventListener(`abort`,i),n(e)},e=>{t.removeEventListener(`abort`,i),r(e)})})}function r(e){let t=[];for(let n of e){if(typeof n==`number`)break;t.push(n)}return t}function i(e,t){return t.map(t=>({...t,path:[...e,...t.path]}))}function a(a){return async(o,s)=>{let c=await n(a.safeParseAsync(o),s);if(!c||c.success)return;let l=c.error.issues.flatMap(e=>{if(!t(e))return[e];let n=c.error.bestMatch(e);return n?i(e.path,n):[e]}),u={},d;for(let t of l){if(t.path.length===0){d??=t.message;continue}u=e.writeError(u,r(t.path),t.message)}let f={fields:Object.keys(u).length===0?void 0:u,formError:d};return f.fields===void 0&&f.formError===void 0?void 0:f}}exports.customValidator=a;
|
|
2
|
+
//# sourceMappingURL=spell.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spell.cjs","names":[],"sources":["../src/spell.ts"],"sourcesContent":["import { Schema, type Issue, type SchemaMode } from '@vielzeug/spell';\n\nimport type { FormErrors, FormValidator, ValidationErrors } from './types';\n\nimport { writeError } from './core/path';\n\ntype ErrorTree = Record<string, unknown>;\ntype UnionIssue = Extract<Issue, { code: 'invalid_union' }>;\n\nfunction isUnionIssue(issue: Issue): issue is UnionIssue {\n return issue.code === 'invalid_union';\n}\n\nfunction abortable<T>(promise: Promise<T>, signal: AbortSignal): Promise<T | undefined> {\n if (signal.aborted) return Promise.resolve(undefined);\n\n return new Promise((resolve, reject) => {\n const abort = () => resolve(undefined);\n\n signal.addEventListener('abort', abort, { once: true });\n void promise.then(\n (value) => {\n signal.removeEventListener('abort', abort);\n resolve(value);\n },\n (error: unknown) => {\n signal.removeEventListener('abort', abort);\n reject(error);\n },\n );\n });\n}\n\nfunction fieldPath(path: readonly (string | number)[]): string[] {\n const segments: string[] = [];\n\n for (const segment of path) {\n if (typeof segment === 'number') break;\n\n segments.push(segment);\n }\n\n return segments;\n}\n\nfunction prefixIssues(path: readonly (string | number)[], issues: readonly Issue[]): Issue[] {\n return issues.map((issue) => ({ ...issue, path: [...path, ...issue.path] }));\n}\n\n/** Adapts a Spell Schema into Forge's full-form validator without transforming form values. */\nexport function customValidator<TValues extends Record<string, unknown>>(\n schema: Schema<unknown, TValues, SchemaMode>,\n): FormValidator<TValues> {\n return async (values, signal) => {\n const result = await abortable(schema.safeParseAsync(values), signal);\n\n if (!result || result.success) return undefined;\n\n const issues = result.error.issues.flatMap((issue) => {\n if (!isUnionIssue(issue)) return [issue];\n\n const bestMatch = result.error.bestMatch(issue);\n\n return bestMatch ? prefixIssues(issue.path, bestMatch) : [issue];\n });\n let fields: ErrorTree = {};\n let formError: string | undefined;\n\n for (const issue of issues) {\n if (issue.path.length === 0) {\n formError ??= issue.message;\n continue;\n }\n\n fields = writeError(fields, fieldPath(issue.path), issue.message);\n }\n\n const errors: ValidationErrors<TValues> = {\n fields: Object.keys(fields).length === 0 ? undefined : (fields as FormErrors<TValues>),\n formError,\n };\n\n return errors.fields === undefined && errors.formError === undefined ? undefined : errors;\n };\n}\n"],"mappings":"iIASA,SAAS,EAAa,EAAmC,CACvD,OAAO,EAAM,OAAS,eACxB,CAEA,SAAS,EAAa,EAAqB,EAA6C,CAGtF,OAFI,EAAO,QAAgB,QAAQ,QAAQ,IAAA,EAAS,EAE7C,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,MAAc,EAAQ,IAAA,EAAS,EAErC,EAAO,iBAAiB,QAAS,EAAO,CAAE,KAAM,EAAK,CAAC,EACtD,EAAa,KACV,GAAU,CACT,EAAO,oBAAoB,QAAS,CAAK,EACzC,EAAQ,CAAK,CACf,EACC,GAAmB,CAClB,EAAO,oBAAoB,QAAS,CAAK,EACzC,EAAO,CAAK,CACd,CACF,CACF,CAAC,CACH,CAEA,SAAS,EAAU,EAA8C,CAC/D,IAAM,EAAqB,CAAC,EAE5B,IAAK,IAAM,KAAW,EAAM,CAC1B,GAAI,OAAO,GAAY,SAAU,MAEjC,EAAS,KAAK,CAAO,CACvB,CAEA,OAAO,CACT,CAEA,SAAS,EAAa,EAAoC,EAAmC,CAC3F,OAAO,EAAO,IAAK,IAAW,CAAE,GAAG,EAAO,KAAM,CAAC,GAAG,EAAM,GAAG,EAAM,IAAI,CAAE,EAAE,CAC7E,CAGA,SAAgB,EACd,EACwB,CACxB,OAAO,MAAO,EAAQ,IAAW,CAC/B,IAAM,EAAS,MAAM,EAAU,EAAO,eAAe,CAAM,EAAG,CAAM,EAEpE,GAAI,CAAC,GAAU,EAAO,QAAS,OAE/B,IAAM,EAAS,EAAO,MAAM,OAAO,QAAS,GAAU,CACpD,GAAI,CAAC,EAAa,CAAK,EAAG,MAAO,CAAC,CAAK,EAEvC,IAAM,EAAY,EAAO,MAAM,UAAU,CAAK,EAE9C,OAAO,EAAY,EAAa,EAAM,KAAM,CAAS,EAAI,CAAC,CAAK,CACjE,CAAC,EACG,EAAoB,CAAC,EACrB,EAEJ,IAAK,IAAM,KAAS,EAAQ,CAC1B,GAAI,EAAM,KAAK,SAAW,EAAG,CAC3B,IAAc,EAAM,QACpB,QACF,CAEA,EAAS,EAAA,WAAW,EAAQ,EAAU,EAAM,IAAI,EAAG,EAAM,OAAO,CAClE,CAEA,IAAM,EAAoC,CACxC,OAAQ,OAAO,KAAK,CAAM,CAAC,CAAC,SAAW,EAAI,IAAA,GAAa,EACxD,WACF,EAEA,OAAO,EAAO,SAAW,IAAA,IAAa,EAAO,YAAc,IAAA,GAAY,IAAA,GAAY,CACrF,CACF"}
|
package/dist/spell.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Schema, type SchemaMode } from '@vielzeug/spell';
|
|
2
|
+
import type { FormValidator } from './types';
|
|
3
|
+
/** Adapts a Spell Schema into Forge's full-form validator without transforming form values. */
|
|
4
|
+
export declare function customValidator<TValues extends Record<string, unknown>>(schema: Schema<unknown, TValues, SchemaMode>): FormValidator<TValues>;
|
|
5
|
+
//# sourceMappingURL=spell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spell.d.ts","sourceRoot":"","sources":["../src/spell.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,KAAK,EAAc,aAAa,EAAoB,MAAM,SAAS,CAAC;AA+C3E,+FAA+F;AAC/F,wBAAgB,eAAe,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,GAC3C,aAAa,CAAC,OAAO,CAAC,CAgCxB"}
|
package/dist/spell.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { writeError as e } from "./core/path.js";
|
|
2
|
+
import "@vielzeug/spell";
|
|
3
|
+
//#region src/spell.ts
|
|
4
|
+
function t(e) {
|
|
5
|
+
return e.code === "invalid_union";
|
|
6
|
+
}
|
|
7
|
+
function n(e, t) {
|
|
8
|
+
return t.aborted ? Promise.resolve(void 0) : new Promise((n, r) => {
|
|
9
|
+
let i = () => n(void 0);
|
|
10
|
+
t.addEventListener("abort", i, { once: !0 }), e.then((e) => {
|
|
11
|
+
t.removeEventListener("abort", i), n(e);
|
|
12
|
+
}, (e) => {
|
|
13
|
+
t.removeEventListener("abort", i), r(e);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function r(e) {
|
|
18
|
+
let t = [];
|
|
19
|
+
for (let n of e) {
|
|
20
|
+
if (typeof n == "number") break;
|
|
21
|
+
t.push(n);
|
|
22
|
+
}
|
|
23
|
+
return t;
|
|
24
|
+
}
|
|
25
|
+
function i(e, t) {
|
|
26
|
+
return t.map((t) => ({
|
|
27
|
+
...t,
|
|
28
|
+
path: [...e, ...t.path]
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
function a(a) {
|
|
32
|
+
return async (o, s) => {
|
|
33
|
+
let c = await n(a.safeParseAsync(o), s);
|
|
34
|
+
if (!c || c.success) return;
|
|
35
|
+
let l = c.error.issues.flatMap((e) => {
|
|
36
|
+
if (!t(e)) return [e];
|
|
37
|
+
let n = c.error.bestMatch(e);
|
|
38
|
+
return n ? i(e.path, n) : [e];
|
|
39
|
+
}), u = {}, d;
|
|
40
|
+
for (let t of l) {
|
|
41
|
+
if (t.path.length === 0) {
|
|
42
|
+
d ??= t.message;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
u = e(u, r(t.path), t.message);
|
|
46
|
+
}
|
|
47
|
+
let f = {
|
|
48
|
+
fields: Object.keys(u).length === 0 ? void 0 : u,
|
|
49
|
+
formError: d
|
|
50
|
+
};
|
|
51
|
+
return f.fields === void 0 && f.formError === void 0 ? void 0 : f;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { a as customValidator };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=spell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spell.js","names":[],"sources":["../src/spell.ts"],"sourcesContent":["import { Schema, type Issue, type SchemaMode } from '@vielzeug/spell';\n\nimport type { FormErrors, FormValidator, ValidationErrors } from './types';\n\nimport { writeError } from './core/path';\n\ntype ErrorTree = Record<string, unknown>;\ntype UnionIssue = Extract<Issue, { code: 'invalid_union' }>;\n\nfunction isUnionIssue(issue: Issue): issue is UnionIssue {\n return issue.code === 'invalid_union';\n}\n\nfunction abortable<T>(promise: Promise<T>, signal: AbortSignal): Promise<T | undefined> {\n if (signal.aborted) return Promise.resolve(undefined);\n\n return new Promise((resolve, reject) => {\n const abort = () => resolve(undefined);\n\n signal.addEventListener('abort', abort, { once: true });\n void promise.then(\n (value) => {\n signal.removeEventListener('abort', abort);\n resolve(value);\n },\n (error: unknown) => {\n signal.removeEventListener('abort', abort);\n reject(error);\n },\n );\n });\n}\n\nfunction fieldPath(path: readonly (string | number)[]): string[] {\n const segments: string[] = [];\n\n for (const segment of path) {\n if (typeof segment === 'number') break;\n\n segments.push(segment);\n }\n\n return segments;\n}\n\nfunction prefixIssues(path: readonly (string | number)[], issues: readonly Issue[]): Issue[] {\n return issues.map((issue) => ({ ...issue, path: [...path, ...issue.path] }));\n}\n\n/** Adapts a Spell Schema into Forge's full-form validator without transforming form values. */\nexport function customValidator<TValues extends Record<string, unknown>>(\n schema: Schema<unknown, TValues, SchemaMode>,\n): FormValidator<TValues> {\n return async (values, signal) => {\n const result = await abortable(schema.safeParseAsync(values), signal);\n\n if (!result || result.success) return undefined;\n\n const issues = result.error.issues.flatMap((issue) => {\n if (!isUnionIssue(issue)) return [issue];\n\n const bestMatch = result.error.bestMatch(issue);\n\n return bestMatch ? prefixIssues(issue.path, bestMatch) : [issue];\n });\n let fields: ErrorTree = {};\n let formError: string | undefined;\n\n for (const issue of issues) {\n if (issue.path.length === 0) {\n formError ??= issue.message;\n continue;\n }\n\n fields = writeError(fields, fieldPath(issue.path), issue.message);\n }\n\n const errors: ValidationErrors<TValues> = {\n fields: Object.keys(fields).length === 0 ? undefined : (fields as FormErrors<TValues>),\n formError,\n };\n\n return errors.fields === undefined && errors.formError === undefined ? undefined : errors;\n };\n}\n"],"mappings":";;;AASA,SAAS,EAAa,GAAmC;CACvD,OAAO,EAAM,SAAS;AACxB;AAEA,SAAS,EAAa,GAAqB,GAA6C;CAGtF,OAFI,EAAO,UAAgB,QAAQ,QAAQ,KAAA,CAAS,IAE7C,IAAI,SAAS,GAAS,MAAW;EACtC,IAAM,UAAc,EAAQ,KAAA,CAAS;EAGrC,AADA,EAAO,iBAAiB,SAAS,GAAO,EAAE,MAAM,GAAK,CAAC,GACtD,EAAa,MACV,MAAU;GAET,AADA,EAAO,oBAAoB,SAAS,CAAK,GACzC,EAAQ,CAAK;EACf,IACC,MAAmB;GAElB,AADA,EAAO,oBAAoB,SAAS,CAAK,GACzC,EAAO,CAAK;EACd,CACF;CACF,CAAC;AACH;AAEA,SAAS,EAAU,GAA8C;CAC/D,IAAM,IAAqB,CAAC;CAE5B,KAAK,IAAM,KAAW,GAAM;EAC1B,IAAI,OAAO,KAAY,UAAU;EAEjC,EAAS,KAAK,CAAO;CACvB;CAEA,OAAO;AACT;AAEA,SAAS,EAAa,GAAoC,GAAmC;CAC3F,OAAO,EAAO,KAAK,OAAW;EAAE,GAAG;EAAO,MAAM,CAAC,GAAG,GAAM,GAAG,EAAM,IAAI;CAAE,EAAE;AAC7E;AAGA,SAAgB,EACd,GACwB;CACxB,OAAO,OAAO,GAAQ,MAAW;EAC/B,IAAM,IAAS,MAAM,EAAU,EAAO,eAAe,CAAM,GAAG,CAAM;EAEpE,IAAI,CAAC,KAAU,EAAO,SAAS;EAE/B,IAAM,IAAS,EAAO,MAAM,OAAO,SAAS,MAAU;GACpD,IAAI,CAAC,EAAa,CAAK,GAAG,OAAO,CAAC,CAAK;GAEvC,IAAM,IAAY,EAAO,MAAM,UAAU,CAAK;GAE9C,OAAO,IAAY,EAAa,EAAM,MAAM,CAAS,IAAI,CAAC,CAAK;EACjE,CAAC,GACG,IAAoB,CAAC,GACrB;EAEJ,KAAK,IAAM,KAAS,GAAQ;GAC1B,IAAI,EAAM,KAAK,WAAW,GAAG;IAC3B,MAAc,EAAM;IACpB;GACF;GAEA,IAAS,EAAW,GAAQ,EAAU,EAAM,IAAI,GAAG,EAAM,OAAO;EAClE;EAEA,IAAM,IAAoC;GACxC,QAAQ,OAAO,KAAK,CAAM,CAAC,CAAC,WAAW,IAAI,KAAA,IAAa;GACxD;EACF;EAEA,OAAO,EAAO,WAAW,KAAA,KAAa,EAAO,cAAc,KAAA,IAAY,KAAA,IAAY;CACrF;AACF"}
|