@lunora/values 0.0.0 → 1.0.0-alpha.1
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/LICENSE.md +105 -0
- package/README.md +109 -9
- package/__assets__/package-og.svg +14 -0
- package/dist/index.d.mts +417 -0
- package/dist/index.d.ts +417 -0
- package/dist/index.mjs +9 -0
- package/dist/packem_shared/ValidationError-DWWcFe37.mjs +60 -0
- package/dist/packem_shared/argsToJsonSchema-DdHmmamC.mjs +22 -0
- package/dist/packem_shared/isOrWrapsFromValidator-DJMAE0l9.mjs +412 -0
- package/dist/packem_shared/jsonSchemaFromNode-weszUOQG.mjs +82 -0
- package/dist/packem_shared/parseValidatorMap-CCjevE5Z.mjs +30 -0
- package/package.json +37 -17
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import { ValidationError, formatPath, describeValue } from './ValidationError-DWWcFe37.mjs';
|
|
2
|
+
|
|
3
|
+
function fail(context, expected, received) {
|
|
4
|
+
const path = [...context.path];
|
|
5
|
+
const receivedDescription = describeValue(received);
|
|
6
|
+
throw new ValidationError(`Expected ${expected} at ${formatPath(path)}, received ${receivedDescription}`, {
|
|
7
|
+
expected,
|
|
8
|
+
path,
|
|
9
|
+
received: receivedDescription
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
const mergeConstraints = (meta, fragment) => {
|
|
13
|
+
if (fragment === void 0) {
|
|
14
|
+
return meta;
|
|
15
|
+
}
|
|
16
|
+
const previous = meta?.constraints;
|
|
17
|
+
return { ...meta, constraints: { ...previous, ...fragment } };
|
|
18
|
+
};
|
|
19
|
+
const createValidator = (kind, parser, meta) => {
|
|
20
|
+
const column = meta?.column ?? { notNull: true };
|
|
21
|
+
const validator = {
|
|
22
|
+
__type: void 0,
|
|
23
|
+
// The Standard Schema v1 surface (https://standardschema.dev). `validate`
|
|
24
|
+
// delegates to `safeParse`: on success it returns `{ value }`, on a
|
|
25
|
+
// ValidationError it maps to `{ issues: [...] }`. Lunora paths are already
|
|
26
|
+
// `(string | number)[]`, a subset of Standard Schema's
|
|
27
|
+
// `ReadonlyArray<PropertyKey | PathSegment>`, so they pass through
|
|
28
|
+
// verbatim — no per-segment wrapping needed (same as Zod/Valibot, which
|
|
29
|
+
// also emit bare keys). Synchronous validate is permitted by the spec.
|
|
30
|
+
"~standard": {
|
|
31
|
+
/** @returns A Standard Schema result object — either `{ value }` on success or `{ issues }` on failure. */
|
|
32
|
+
validate(value) {
|
|
33
|
+
const result = validator.safeParse(value);
|
|
34
|
+
if (result.ok) {
|
|
35
|
+
return { value: result.value };
|
|
36
|
+
}
|
|
37
|
+
return { issues: [{ message: result.error.message, path: result.error.path }] };
|
|
38
|
+
},
|
|
39
|
+
vendor: "lunora",
|
|
40
|
+
version: 1
|
|
41
|
+
},
|
|
42
|
+
_meta: { ...meta, column },
|
|
43
|
+
_parse(value, context) {
|
|
44
|
+
return parser(value, context);
|
|
45
|
+
},
|
|
46
|
+
kind,
|
|
47
|
+
parse(value) {
|
|
48
|
+
return parser(value, { path: [] });
|
|
49
|
+
},
|
|
50
|
+
/** @returns `{ ok: true, value }` on success or `{ ok: false, error }` on a validation failure. */
|
|
51
|
+
safeParse(value) {
|
|
52
|
+
try {
|
|
53
|
+
return { ok: true, value: parser(value, { path: [] }) };
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (error instanceof ValidationError) {
|
|
56
|
+
return { error, ok: false };
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const rebuild = (patch) => createValidator(kind, parser, { ...meta, column: { ...column, ...patch } });
|
|
63
|
+
validator.default = (value) => rebuild({ defaultValue: value });
|
|
64
|
+
validator.defaultNow = () => rebuild({ defaultFn: () => Date.now() });
|
|
65
|
+
validator.unique = () => rebuild({ unique: true });
|
|
66
|
+
validator.$defaultFn = (function_) => rebuild({ defaultFn: function_ });
|
|
67
|
+
validator.$onUpdateFn = (function_) => rebuild({ onUpdateFn: function_ });
|
|
68
|
+
validator.serverDefault = (function_) => rebuild({ serverDefault: function_ });
|
|
69
|
+
validator.$type = (() => rebuild({}));
|
|
70
|
+
validator.nullable = () => {
|
|
71
|
+
const nullableParser = (value, context) => value === null ? null : parser(value, context);
|
|
72
|
+
return createValidator(kind, nullableParser, { ...meta, column: { ...column, notNull: false } });
|
|
73
|
+
};
|
|
74
|
+
validator.check = (predicate, options) => {
|
|
75
|
+
const message = typeof options === "string" ? options : options?.message;
|
|
76
|
+
const fragment = typeof options === "string" ? void 0 : options?.schema;
|
|
77
|
+
const refinedParser = (value, context) => {
|
|
78
|
+
const parsed = parser(value, context);
|
|
79
|
+
if (!predicate(parsed)) {
|
|
80
|
+
fail(context, message ?? "value matching refinement", parsed);
|
|
81
|
+
}
|
|
82
|
+
return parsed;
|
|
83
|
+
};
|
|
84
|
+
return createValidator(kind, refinedParser, mergeConstraints(meta, fragment));
|
|
85
|
+
};
|
|
86
|
+
validator.meta = (options) => {
|
|
87
|
+
const fragment = options.description === void 0 ? options.schema : { description: options.description, ...options.schema };
|
|
88
|
+
return createValidator(kind, parser, mergeConstraints(meta, fragment));
|
|
89
|
+
};
|
|
90
|
+
return validator;
|
|
91
|
+
};
|
|
92
|
+
const toInternal = (validator) => validator;
|
|
93
|
+
const asColumn = (validator) => validator;
|
|
94
|
+
const string = () => asColumn(
|
|
95
|
+
createValidator("string", (value, context) => {
|
|
96
|
+
if (typeof value !== "string") {
|
|
97
|
+
fail(context, "string", value);
|
|
98
|
+
}
|
|
99
|
+
return value;
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
const number = () => asColumn(
|
|
103
|
+
createValidator("number", (value, context) => {
|
|
104
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
105
|
+
fail(context, "number", value);
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
const parseEpochMillis = (value, context) => {
|
|
111
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
112
|
+
fail(context, "number", value);
|
|
113
|
+
}
|
|
114
|
+
return value;
|
|
115
|
+
};
|
|
116
|
+
const timestamp = () => createValidator("timestamp", parseEpochMillis);
|
|
117
|
+
const date = () => createValidator("date", parseEpochMillis);
|
|
118
|
+
const boolean = () => asColumn(
|
|
119
|
+
createValidator("boolean", (value, context) => {
|
|
120
|
+
if (typeof value !== "boolean") {
|
|
121
|
+
fail(context, "boolean", value);
|
|
122
|
+
}
|
|
123
|
+
return value;
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
const bigintValidator = () => asColumn(
|
|
127
|
+
createValidator("bigint", (value, context) => {
|
|
128
|
+
if (typeof value !== "bigint") {
|
|
129
|
+
fail(context, "bigint", value);
|
|
130
|
+
}
|
|
131
|
+
return value;
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
const nullValidator = () => asColumn(
|
|
135
|
+
createValidator("null", (value, context) => {
|
|
136
|
+
if (value !== null) {
|
|
137
|
+
fail(context, "null", value);
|
|
138
|
+
}
|
|
139
|
+
return value;
|
|
140
|
+
})
|
|
141
|
+
);
|
|
142
|
+
const bytes = () => asColumn(
|
|
143
|
+
createValidator("bytes", (value, context) => {
|
|
144
|
+
if (!(value instanceof ArrayBuffer)) {
|
|
145
|
+
fail(context, "ArrayBuffer", value);
|
|
146
|
+
}
|
|
147
|
+
return value;
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
const id = (tableName) => asColumn(
|
|
151
|
+
createValidator(
|
|
152
|
+
"id",
|
|
153
|
+
(value, context) => {
|
|
154
|
+
if (typeof value !== "string") {
|
|
155
|
+
fail(context, `Id<"${tableName}">`, value);
|
|
156
|
+
}
|
|
157
|
+
return value;
|
|
158
|
+
},
|
|
159
|
+
{ tableName }
|
|
160
|
+
)
|
|
161
|
+
);
|
|
162
|
+
const storage = (bucket) => asColumn(
|
|
163
|
+
createValidator(
|
|
164
|
+
"storage",
|
|
165
|
+
(value, context) => {
|
|
166
|
+
if (typeof value !== "string") {
|
|
167
|
+
fail(context, "storage object key (string)", value);
|
|
168
|
+
}
|
|
169
|
+
return value;
|
|
170
|
+
},
|
|
171
|
+
bucket === void 0 ? void 0 : { bucket }
|
|
172
|
+
)
|
|
173
|
+
);
|
|
174
|
+
const literal = (literalValue) => asColumn(
|
|
175
|
+
createValidator(
|
|
176
|
+
"literal",
|
|
177
|
+
(value, context) => {
|
|
178
|
+
if (value !== literalValue) {
|
|
179
|
+
fail(context, `literal(${String(literalValue)})`, value);
|
|
180
|
+
}
|
|
181
|
+
return value;
|
|
182
|
+
},
|
|
183
|
+
{ value: literalValue }
|
|
184
|
+
)
|
|
185
|
+
);
|
|
186
|
+
const array = (inner) => {
|
|
187
|
+
const innerInternal = toInternal(inner);
|
|
188
|
+
return asColumn(
|
|
189
|
+
createValidator(
|
|
190
|
+
"array",
|
|
191
|
+
(value, context) => {
|
|
192
|
+
if (!Array.isArray(value)) {
|
|
193
|
+
fail(context, "array", value);
|
|
194
|
+
}
|
|
195
|
+
const { length } = value;
|
|
196
|
+
const out = Array.from({ length });
|
|
197
|
+
const { path } = context;
|
|
198
|
+
for (let index = 0; index < length; index += 1) {
|
|
199
|
+
path.push(index);
|
|
200
|
+
out[index] = innerInternal._parse(value[index], context);
|
|
201
|
+
path.pop();
|
|
202
|
+
}
|
|
203
|
+
return out;
|
|
204
|
+
},
|
|
205
|
+
{ inner }
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
};
|
|
209
|
+
const objectValidator = (shape) => {
|
|
210
|
+
const entries = Object.keys(shape).map((key) => {
|
|
211
|
+
const child = toInternal(shape[key]);
|
|
212
|
+
return { child, isOptional: child.kind === "optional", key };
|
|
213
|
+
});
|
|
214
|
+
return asColumn(
|
|
215
|
+
createValidator(
|
|
216
|
+
"object",
|
|
217
|
+
(value, context) => {
|
|
218
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
219
|
+
fail(context, "object", value);
|
|
220
|
+
}
|
|
221
|
+
const input = value;
|
|
222
|
+
const out = {};
|
|
223
|
+
const { path } = context;
|
|
224
|
+
for (const { child, isOptional, key } of entries) {
|
|
225
|
+
const fieldValue = input[key];
|
|
226
|
+
if (fieldValue === void 0 && isOptional) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
path.push(key);
|
|
230
|
+
out[key] = child._parse(fieldValue, context);
|
|
231
|
+
path.pop();
|
|
232
|
+
}
|
|
233
|
+
return out;
|
|
234
|
+
},
|
|
235
|
+
{ shape }
|
|
236
|
+
)
|
|
237
|
+
);
|
|
238
|
+
};
|
|
239
|
+
const record = (keyValidator, valueValidator) => {
|
|
240
|
+
const keyInternal = toInternal(keyValidator);
|
|
241
|
+
const valueInternal = toInternal(valueValidator);
|
|
242
|
+
return asColumn(
|
|
243
|
+
createValidator(
|
|
244
|
+
"record",
|
|
245
|
+
(value, context) => {
|
|
246
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
247
|
+
fail(context, "record", value);
|
|
248
|
+
}
|
|
249
|
+
const input = value;
|
|
250
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
251
|
+
const { path } = context;
|
|
252
|
+
for (const key of Object.keys(input)) {
|
|
253
|
+
path.push(key);
|
|
254
|
+
const parsedKey = keyInternal._parse(key, context);
|
|
255
|
+
const parsedValue = valueInternal._parse(input[key], context);
|
|
256
|
+
path.pop();
|
|
257
|
+
out[parsedKey] = parsedValue;
|
|
258
|
+
}
|
|
259
|
+
return out;
|
|
260
|
+
},
|
|
261
|
+
{ keyValidator, valueValidator }
|
|
262
|
+
)
|
|
263
|
+
);
|
|
264
|
+
};
|
|
265
|
+
const union = (...members) => {
|
|
266
|
+
if (members.length === 0) {
|
|
267
|
+
throw new Error("v.union requires at least one member");
|
|
268
|
+
}
|
|
269
|
+
const memberInternals = members.map((member) => toInternal(member));
|
|
270
|
+
return asColumn(
|
|
271
|
+
createValidator(
|
|
272
|
+
"union",
|
|
273
|
+
(value, context) => {
|
|
274
|
+
let deepestError;
|
|
275
|
+
const { path } = context;
|
|
276
|
+
const baseDepth = path.length;
|
|
277
|
+
for (const member of memberInternals) {
|
|
278
|
+
try {
|
|
279
|
+
return member._parse(value, context);
|
|
280
|
+
} catch (error) {
|
|
281
|
+
if (!(error instanceof ValidationError)) {
|
|
282
|
+
throw error;
|
|
283
|
+
}
|
|
284
|
+
path.length = baseDepth;
|
|
285
|
+
if (deepestError === void 0 || error.path.length > deepestError.path.length) {
|
|
286
|
+
deepestError = error;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (memberInternals.length === 1 && deepestError !== void 0) {
|
|
291
|
+
throw deepestError;
|
|
292
|
+
}
|
|
293
|
+
const detail = deepestError === void 0 ? "" : ` (closest: expected ${deepestError.expected} at ${formatPath(deepestError.path)})`;
|
|
294
|
+
return fail(context, `union of ${String(members.length)} member(s)${detail}`, value);
|
|
295
|
+
},
|
|
296
|
+
{ members }
|
|
297
|
+
)
|
|
298
|
+
);
|
|
299
|
+
};
|
|
300
|
+
const optional = (inner) => {
|
|
301
|
+
const innerInternal = toInternal(inner);
|
|
302
|
+
return asColumn(
|
|
303
|
+
createValidator(
|
|
304
|
+
"optional",
|
|
305
|
+
(value, context) => value === void 0 ? void 0 : innerInternal._parse(value, context),
|
|
306
|
+
{ inner }
|
|
307
|
+
)
|
|
308
|
+
);
|
|
309
|
+
};
|
|
310
|
+
const any = () => asColumn(createValidator("any", (value) => value));
|
|
311
|
+
const standardIssuePath = (path) => {
|
|
312
|
+
const issuePath = [];
|
|
313
|
+
if (!path) {
|
|
314
|
+
return issuePath;
|
|
315
|
+
}
|
|
316
|
+
for (const segment of path) {
|
|
317
|
+
const key = typeof segment === "object" && segment !== null && "key" in segment ? segment.key : segment;
|
|
318
|
+
if (typeof key === "string" || typeof key === "number") {
|
|
319
|
+
issuePath.push(key);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return issuePath;
|
|
323
|
+
};
|
|
324
|
+
const from = (schema) => {
|
|
325
|
+
const props = schema["~standard"];
|
|
326
|
+
if (props?.version !== 1 || typeof props.validate !== "function") {
|
|
327
|
+
throw new Error('@lunora/values: v.from() expects a Standard Schema v1 object (missing or invalid "~standard")');
|
|
328
|
+
}
|
|
329
|
+
const validate = props.validate;
|
|
330
|
+
return asColumn(
|
|
331
|
+
createValidator("from", (value, context) => {
|
|
332
|
+
const result = validate(value);
|
|
333
|
+
if (result instanceof Promise || typeof result?.then === "function") {
|
|
334
|
+
throw new ValidationError("v.from(): async Standard Schema validators are not supported in args", {
|
|
335
|
+
expected: "sync Standard Schema result",
|
|
336
|
+
path: [...context.path],
|
|
337
|
+
received: "Promise"
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const looseResult = result;
|
|
341
|
+
if (looseResult === null || typeof looseResult !== "object") {
|
|
342
|
+
throw new ValidationError("v.from(): Standard Schema validator returned a non-object result", {
|
|
343
|
+
expected: "Standard Schema result object",
|
|
344
|
+
path: [...context.path],
|
|
345
|
+
received: describeValue(looseResult)
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
const syncResult = result;
|
|
349
|
+
const syncResultObject = syncResult;
|
|
350
|
+
if ("issues" in syncResultObject && syncResult.issues !== void 0 && syncResult.issues.length > 0) {
|
|
351
|
+
const first = syncResult.issues[0];
|
|
352
|
+
const message = first?.message ?? "Standard Schema validation failed";
|
|
353
|
+
throw new ValidationError(message, {
|
|
354
|
+
expected: "valid value",
|
|
355
|
+
path: [...context.path, ...standardIssuePath(first?.path)],
|
|
356
|
+
received: describeValue(value)
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
return syncResult.value;
|
|
360
|
+
})
|
|
361
|
+
);
|
|
362
|
+
};
|
|
363
|
+
const isOrWrapsFromValidator = (validator) => {
|
|
364
|
+
if (validator.kind === "from") {
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
const meta = validator._meta;
|
|
368
|
+
if (!meta) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
const children = [meta.inner, meta.keyValidator, meta.valueValidator];
|
|
372
|
+
if (Array.isArray(meta.members)) {
|
|
373
|
+
children.push(...meta.members);
|
|
374
|
+
}
|
|
375
|
+
if (meta.shape !== null && typeof meta.shape === "object") {
|
|
376
|
+
children.push(...Object.values(meta.shape));
|
|
377
|
+
}
|
|
378
|
+
for (const child of children) {
|
|
379
|
+
if (child !== null && typeof child === "object" && "kind" in child && isOrWrapsFromValidator(child)) {
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return false;
|
|
384
|
+
};
|
|
385
|
+
const optionalInner = (validator) => {
|
|
386
|
+
if (validator.kind !== "optional") {
|
|
387
|
+
return void 0;
|
|
388
|
+
}
|
|
389
|
+
return validator._meta?.inner;
|
|
390
|
+
};
|
|
391
|
+
const v = {
|
|
392
|
+
any,
|
|
393
|
+
array,
|
|
394
|
+
bigint: bigintValidator,
|
|
395
|
+
boolean,
|
|
396
|
+
bytes,
|
|
397
|
+
date,
|
|
398
|
+
from,
|
|
399
|
+
id,
|
|
400
|
+
literal,
|
|
401
|
+
null: nullValidator,
|
|
402
|
+
number,
|
|
403
|
+
object: objectValidator,
|
|
404
|
+
optional,
|
|
405
|
+
record,
|
|
406
|
+
storage,
|
|
407
|
+
string,
|
|
408
|
+
timestamp,
|
|
409
|
+
union
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
export { isOrWrapsFromValidator, optionalInner, v };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const jsonSchemaFromNode = (node, reader) => {
|
|
2
|
+
const base = (() => {
|
|
3
|
+
switch (reader.kind(node)) {
|
|
4
|
+
case "any": {
|
|
5
|
+
return {};
|
|
6
|
+
}
|
|
7
|
+
case "array": {
|
|
8
|
+
const inner = reader.inner(node);
|
|
9
|
+
return { items: inner === void 0 ? {} : jsonSchemaFromNode(inner, reader), type: "array" };
|
|
10
|
+
}
|
|
11
|
+
case "bigint": {
|
|
12
|
+
return { format: "int64", type: "integer" };
|
|
13
|
+
}
|
|
14
|
+
case "boolean": {
|
|
15
|
+
return { type: "boolean" };
|
|
16
|
+
}
|
|
17
|
+
case "bytes": {
|
|
18
|
+
return { contentEncoding: "base64", description: "binary (ArrayBuffer)", type: "string" };
|
|
19
|
+
}
|
|
20
|
+
case "date": {
|
|
21
|
+
return { description: "epoch milliseconds (date)", type: "integer" };
|
|
22
|
+
}
|
|
23
|
+
case "id": {
|
|
24
|
+
return { description: `Id<"${String(reader.tableName(node))}">`, type: "string", "x-lunora-table": reader.tableName(node) };
|
|
25
|
+
}
|
|
26
|
+
case "literal": {
|
|
27
|
+
return reader.literalSchema(node);
|
|
28
|
+
}
|
|
29
|
+
case "null": {
|
|
30
|
+
return { type: "null" };
|
|
31
|
+
}
|
|
32
|
+
case "number": {
|
|
33
|
+
return { type: "number" };
|
|
34
|
+
}
|
|
35
|
+
case "object": {
|
|
36
|
+
return objectSchemaFromNodes(reader.shape(node), reader);
|
|
37
|
+
}
|
|
38
|
+
case "optional": {
|
|
39
|
+
const inner = reader.inner(node);
|
|
40
|
+
return inner === void 0 ? {} : jsonSchemaFromNode(inner, reader);
|
|
41
|
+
}
|
|
42
|
+
case "record": {
|
|
43
|
+
const value = reader.valueChild(node);
|
|
44
|
+
return { additionalProperties: value === void 0 ? {} : jsonSchemaFromNode(value, reader), type: "object" };
|
|
45
|
+
}
|
|
46
|
+
case "storage": {
|
|
47
|
+
return { description: "storage object key", type: "string", "x-lunora-storage": true };
|
|
48
|
+
}
|
|
49
|
+
case "string": {
|
|
50
|
+
return { type: "string" };
|
|
51
|
+
}
|
|
52
|
+
case "timestamp": {
|
|
53
|
+
return { description: "epoch milliseconds (timestamp)", type: "integer" };
|
|
54
|
+
}
|
|
55
|
+
case "union": {
|
|
56
|
+
return { anyOf: reader.members(node).map((member) => jsonSchemaFromNode(member, reader)) };
|
|
57
|
+
}
|
|
58
|
+
default: {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
})();
|
|
63
|
+
const constraints = reader.constraints(node);
|
|
64
|
+
const withConstraints = constraints === void 0 ? base : { ...base, ...constraints };
|
|
65
|
+
if (reader.isNullable(node)) {
|
|
66
|
+
return { anyOf: [withConstraints, { type: "null" }] };
|
|
67
|
+
}
|
|
68
|
+
return withConstraints;
|
|
69
|
+
};
|
|
70
|
+
const objectSchemaFromNodes = (shape, reader) => {
|
|
71
|
+
const properties = {};
|
|
72
|
+
const required = [];
|
|
73
|
+
for (const [key, child] of Object.entries(shape)) {
|
|
74
|
+
properties[key] = jsonSchemaFromNode(child, reader);
|
|
75
|
+
if (reader.kind(child) !== "optional") {
|
|
76
|
+
required.push(key);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { additionalProperties: false, properties, required, type: "object" };
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export { jsonSchemaFromNode, objectSchemaFromNodes };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ValidationError } from './ValidationError-DWWcFe37.mjs';
|
|
2
|
+
|
|
3
|
+
const parseValidatorMap = (validators, source, label) => {
|
|
4
|
+
const out = {};
|
|
5
|
+
for (const key of Object.keys(validators)) {
|
|
6
|
+
const validator = validators[key];
|
|
7
|
+
if (!validator) {
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
const candidate = source[key];
|
|
11
|
+
if (candidate === void 0 && validator.kind === "optional") {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
out[key] = validator.parse(candidate);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
if (error instanceof ValidationError) {
|
|
18
|
+
throw new ValidationError(`${label}.${key}: ${error.message}`, {
|
|
19
|
+
expected: error.expected,
|
|
20
|
+
path: [key, ...error.path],
|
|
21
|
+
received: error.received
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export { parseValidatorMap };
|
package/package.json
CHANGED
|
@@ -1,31 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/values",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "1.0.0-alpha.1",
|
|
4
4
|
"description": "Validators for Lunora: the v.* validator suite with end-to-end return-type inference",
|
|
5
|
-
"
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cloudflare",
|
|
7
|
+
"durable-objects",
|
|
8
|
+
"lunora",
|
|
9
|
+
"schema",
|
|
10
|
+
"standard-schema",
|
|
11
|
+
"type-inference",
|
|
12
|
+
"validation",
|
|
13
|
+
"workers"
|
|
14
|
+
],
|
|
6
15
|
"homepage": "https://lunora.sh",
|
|
16
|
+
"bugs": "https://github.com/anolilab/lunora/issues",
|
|
17
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
18
|
+
"author": {
|
|
19
|
+
"name": "Daniel Bannert",
|
|
20
|
+
"email": "d.bannert@anolilab.de"
|
|
21
|
+
},
|
|
7
22
|
"repository": {
|
|
8
23
|
"type": "git",
|
|
9
24
|
"url": "git+https://github.com/anolilab/lunora.git",
|
|
10
25
|
"directory": "packages/values"
|
|
11
26
|
},
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
"cloudflare",
|
|
18
|
-
"workers",
|
|
19
|
-
"durable-objects",
|
|
20
|
-
"validation",
|
|
21
|
-
"schema",
|
|
22
|
-
"type-inference",
|
|
23
|
-
"standard-schema"
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE.md",
|
|
31
|
+
"__assets__"
|
|
24
32
|
],
|
|
33
|
+
"type": "module",
|
|
34
|
+
"sideEffects": false,
|
|
35
|
+
"main": "./dist/index.mjs",
|
|
36
|
+
"module": "./dist/index.mjs",
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"import": "./dist/index.mjs"
|
|
42
|
+
},
|
|
43
|
+
"./package.json": "./package.json"
|
|
44
|
+
},
|
|
25
45
|
"publishConfig": {
|
|
26
46
|
"access": "public"
|
|
27
47
|
},
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": "^22.15.0 || >=24.11.0"
|
|
50
|
+
}
|
|
31
51
|
}
|