@axeom/schema 0.1.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 +21 -0
- package/dist/index.cjs +347 -0
- package/dist/index.d.cts +223 -0
- package/dist/index.d.ts +223 -0
- package/dist/index.js +321 -0
- package/package.json +26 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Axeom
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
18
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
19
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
20
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
|
21
|
+
OR OTHER DEALINGS IN THE SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AxeomSchema: () => AxeomSchema,
|
|
24
|
+
default: () => index_default,
|
|
25
|
+
s: () => s
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
var AxeomSchema = class {
|
|
29
|
+
_output;
|
|
30
|
+
_isOptional;
|
|
31
|
+
_isOptionalVal = false;
|
|
32
|
+
_isNullable = false;
|
|
33
|
+
_defaultValue;
|
|
34
|
+
_transforms = [];
|
|
35
|
+
/**
|
|
36
|
+
* Validates and parses the input data, applying all transforms and refinements.
|
|
37
|
+
* Throws an error if validation fails.
|
|
38
|
+
*/
|
|
39
|
+
async parse(data) {
|
|
40
|
+
if (data === void 0) {
|
|
41
|
+
if (this._defaultValue !== void 0) data = this._defaultValue;
|
|
42
|
+
else if (this._isOptionalVal) return void 0;
|
|
43
|
+
else throw new Error("Required field is missing");
|
|
44
|
+
}
|
|
45
|
+
if (data === null) {
|
|
46
|
+
if (this._isNullable) return null;
|
|
47
|
+
throw new Error("Field cannot be null");
|
|
48
|
+
}
|
|
49
|
+
const result = this._validate(data);
|
|
50
|
+
if (!result.success) throw new Error(result.error);
|
|
51
|
+
let finalData = result.data;
|
|
52
|
+
for (const transform of this._transforms) {
|
|
53
|
+
finalData = await transform(finalData);
|
|
54
|
+
}
|
|
55
|
+
return finalData;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Marks the field as optional, allowing 'undefined' inputs.
|
|
59
|
+
*/
|
|
60
|
+
optional() {
|
|
61
|
+
this._isOptionalVal = true;
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Marks the field as nullable, allowing 'null' inputs.
|
|
66
|
+
*/
|
|
67
|
+
nullable() {
|
|
68
|
+
this._isNullable = true;
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Sets a default value if the input is missing.
|
|
73
|
+
*/
|
|
74
|
+
default(val) {
|
|
75
|
+
this._defaultValue = val;
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Transforms the validated output into a new value or type.
|
|
80
|
+
*/
|
|
81
|
+
transform(fn) {
|
|
82
|
+
this._transforms.push(fn);
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Adds a custom validation check. Throws if the function returns false.
|
|
87
|
+
*/
|
|
88
|
+
refine(fn, message = "Invalid value") {
|
|
89
|
+
return this.transform(async (data) => {
|
|
90
|
+
if (!await fn(data)) throw new Error(message);
|
|
91
|
+
return data;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var StringSchema = class extends AxeomSchema {
|
|
96
|
+
_validate(data) {
|
|
97
|
+
return typeof data === "string" ? { success: true, data } : { success: false, error: `Expected string, got ${typeof data}` };
|
|
98
|
+
}
|
|
99
|
+
toJSONSchema() {
|
|
100
|
+
return {
|
|
101
|
+
type: "string",
|
|
102
|
+
...this._isOptionalVal ? { optional: true } : {},
|
|
103
|
+
...this._isNullable ? { nullable: true } : {},
|
|
104
|
+
...this._defaultValue !== void 0 ? { default: this._defaultValue } : {}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
min(n) {
|
|
108
|
+
this.refine((v) => v.length >= n, `Too short (min ${n} characters)`);
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
max(n) {
|
|
112
|
+
this.refine((v) => v.length <= n, `Too long (max ${n} characters)`);
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
length(n) {
|
|
116
|
+
this.refine((v) => v.length === n, `Must be exactly ${n} characters`);
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
email() {
|
|
120
|
+
this.refine((v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v), "Invalid email address");
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
url() {
|
|
124
|
+
this.refine((v) => {
|
|
125
|
+
try {
|
|
126
|
+
new URL(v);
|
|
127
|
+
return true;
|
|
128
|
+
} catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}, "Invalid URL");
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
regex(re, message = "Does not match pattern") {
|
|
135
|
+
this.refine((v) => re.test(v), message);
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
trim() {
|
|
139
|
+
this.transform((v) => v.trim());
|
|
140
|
+
return this;
|
|
141
|
+
}
|
|
142
|
+
toLowerCase() {
|
|
143
|
+
this.transform((v) => v.toLowerCase());
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
toUpperCase() {
|
|
147
|
+
this.transform((v) => v.toUpperCase());
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
var NumberSchema = class extends AxeomSchema {
|
|
152
|
+
_validate(data) {
|
|
153
|
+
return typeof data === "number" ? { success: true, data } : { success: false, error: `Expected number, got ${typeof data}` };
|
|
154
|
+
}
|
|
155
|
+
toJSONSchema() {
|
|
156
|
+
return {
|
|
157
|
+
type: "number",
|
|
158
|
+
...this._isOptionalVal ? { optional: true } : {},
|
|
159
|
+
...this._isNullable ? { nullable: true } : {},
|
|
160
|
+
...this._defaultValue !== void 0 ? { default: this._defaultValue } : {}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
min(n) {
|
|
164
|
+
this.refine((v) => v >= n, `Must be greater than or equal to ${n}`);
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
max(n) {
|
|
168
|
+
this.refine((v) => v <= n, `Must be less than or equal to ${n}`);
|
|
169
|
+
return this;
|
|
170
|
+
}
|
|
171
|
+
int() {
|
|
172
|
+
this.refine((v) => Number.isInteger(v), "Must be an integer");
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
positive() {
|
|
176
|
+
return this.min(0);
|
|
177
|
+
}
|
|
178
|
+
negative() {
|
|
179
|
+
return this.max(0);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var BooleanSchema = class extends AxeomSchema {
|
|
183
|
+
_validate(data) {
|
|
184
|
+
return typeof data === "boolean" ? { success: true, data } : { success: false, error: `Expected boolean, got ${typeof data}` };
|
|
185
|
+
}
|
|
186
|
+
toJSONSchema() {
|
|
187
|
+
return {
|
|
188
|
+
type: "boolean",
|
|
189
|
+
...this._isOptionalVal ? { optional: true } : {},
|
|
190
|
+
...this._isNullable ? { nullable: true } : {},
|
|
191
|
+
...this._defaultValue !== void 0 ? { default: this._defaultValue } : {}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
var AnySchema = class extends AxeomSchema {
|
|
196
|
+
_validate(data) {
|
|
197
|
+
return { success: true, data };
|
|
198
|
+
}
|
|
199
|
+
toJSONSchema() {
|
|
200
|
+
return { type: "any" };
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
var EnumSchema = class extends AxeomSchema {
|
|
204
|
+
constructor(values) {
|
|
205
|
+
super();
|
|
206
|
+
this.values = values;
|
|
207
|
+
}
|
|
208
|
+
_validate(data) {
|
|
209
|
+
return typeof data === "string" && this.values.includes(data) ? { success: true, data } : {
|
|
210
|
+
success: false,
|
|
211
|
+
error: `Invalid value. Expected one of: ${this.values.join(", ")}`
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
toJSONSchema() {
|
|
215
|
+
return {
|
|
216
|
+
type: "string",
|
|
217
|
+
enum: this.values
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
var ObjectSchema = class extends AxeomSchema {
|
|
222
|
+
constructor(shape) {
|
|
223
|
+
super();
|
|
224
|
+
this.shape = shape;
|
|
225
|
+
}
|
|
226
|
+
_validate(data) {
|
|
227
|
+
return typeof data === "object" && data !== null ? { success: true, data } : { success: false, error: "Expected object" };
|
|
228
|
+
}
|
|
229
|
+
toJSONSchema() {
|
|
230
|
+
const properties = {};
|
|
231
|
+
for (const key in this.shape) {
|
|
232
|
+
properties[key] = this.shape[key].toJSONSchema();
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
type: "object",
|
|
236
|
+
properties,
|
|
237
|
+
required: Object.keys(this.shape).filter((key) => !this.shape[key]._isOptionalVal)
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
async parse(data) {
|
|
241
|
+
const base = await super.parse(data);
|
|
242
|
+
const result = {};
|
|
243
|
+
const errors = [];
|
|
244
|
+
for (const key in this.shape) {
|
|
245
|
+
try {
|
|
246
|
+
result[key] = await this.shape[key].parse(base[key]);
|
|
247
|
+
} catch (e) {
|
|
248
|
+
errors.push(`${key}: ${e.message}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (errors.length > 0) {
|
|
252
|
+
throw new Error(errors.join(", "));
|
|
253
|
+
}
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
var ArraySchema = class extends AxeomSchema {
|
|
258
|
+
constructor(itemSchema) {
|
|
259
|
+
super();
|
|
260
|
+
this.itemSchema = itemSchema;
|
|
261
|
+
}
|
|
262
|
+
_validate(data) {
|
|
263
|
+
return Array.isArray(data) ? { success: true, data } : { success: false, error: "Expected array" };
|
|
264
|
+
}
|
|
265
|
+
toJSONSchema() {
|
|
266
|
+
return {
|
|
267
|
+
type: "array",
|
|
268
|
+
items: this.itemSchema.toJSONSchema()
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
min(n) {
|
|
272
|
+
this.refine((v) => v.length >= n, `Must have at least ${n} items`);
|
|
273
|
+
return this;
|
|
274
|
+
}
|
|
275
|
+
max(n) {
|
|
276
|
+
this.refine((v) => v.length <= n, `Must have at most ${n} items`);
|
|
277
|
+
return this;
|
|
278
|
+
}
|
|
279
|
+
length(n) {
|
|
280
|
+
this.refine((v) => v.length === n, `Must have exactly ${n} items`);
|
|
281
|
+
return this;
|
|
282
|
+
}
|
|
283
|
+
async parse(data) {
|
|
284
|
+
const base = await super.parse(data);
|
|
285
|
+
const result = await Promise.all(
|
|
286
|
+
base.map(
|
|
287
|
+
(item, i) => this.itemSchema.parse(item).catch((e) => {
|
|
288
|
+
throw new Error(`[${i}]: ${e.message}`);
|
|
289
|
+
})
|
|
290
|
+
)
|
|
291
|
+
);
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
var FileSchema = class extends AxeomSchema {
|
|
296
|
+
_validate(data) {
|
|
297
|
+
return data instanceof File || data instanceof Blob ? { success: true, data } : { success: false, error: "Expected File or Blob" };
|
|
298
|
+
}
|
|
299
|
+
toJSONSchema() {
|
|
300
|
+
return {
|
|
301
|
+
type: "string",
|
|
302
|
+
format: "binary"
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
max(sizeInBytes, message) {
|
|
306
|
+
return this.refine(
|
|
307
|
+
(v) => v.size <= sizeInBytes,
|
|
308
|
+
message || `File is too large (max ${sizeInBytes} bytes)`
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
min(sizeInBytes, message) {
|
|
312
|
+
return this.refine(
|
|
313
|
+
(v) => v.size >= sizeInBytes,
|
|
314
|
+
message || `File is too small (min ${sizeInBytes} bytes)`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
type(mimeType, message) {
|
|
318
|
+
return this.refine(
|
|
319
|
+
(v) => v.type.startsWith(mimeType),
|
|
320
|
+
message || `Invalid file type (expected ${mimeType})`
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
var s = {
|
|
325
|
+
/** Create a string validator */
|
|
326
|
+
string: () => new StringSchema(),
|
|
327
|
+
/** Create a number validator */
|
|
328
|
+
number: () => new NumberSchema(),
|
|
329
|
+
/** Create a boolean validator */
|
|
330
|
+
boolean: () => new BooleanSchema(),
|
|
331
|
+
/** Create a validator that allows any input */
|
|
332
|
+
any: () => new AnySchema(),
|
|
333
|
+
/** Create an enum validator from an array of strings */
|
|
334
|
+
enum: (values) => new EnumSchema(values),
|
|
335
|
+
/** Create an object validator from a shape record */
|
|
336
|
+
object: (shape) => new ObjectSchema(shape),
|
|
337
|
+
/** Create an array validator for a specific item type */
|
|
338
|
+
array: (item) => new ArraySchema(item),
|
|
339
|
+
/** Create a file/blob validator */
|
|
340
|
+
file: () => new FileSchema()
|
|
341
|
+
};
|
|
342
|
+
var index_default = s;
|
|
343
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
344
|
+
0 && (module.exports = {
|
|
345
|
+
AxeomSchema,
|
|
346
|
+
s
|
|
347
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Axeom Schema
|
|
3
|
+
* A zero-dependency, ultra-lightweight validation library.
|
|
4
|
+
*/
|
|
5
|
+
type ValidationResult<T> = {
|
|
6
|
+
success: true;
|
|
7
|
+
data: T;
|
|
8
|
+
} | {
|
|
9
|
+
success: false;
|
|
10
|
+
error: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Standard Validator interface for Axeom.
|
|
14
|
+
*/
|
|
15
|
+
interface Validator<T = any> {
|
|
16
|
+
readonly _output: T;
|
|
17
|
+
readonly _isOptional?: boolean;
|
|
18
|
+
parse: (data: unknown) => T | Promise<T>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Infer the type of an Axeom Schema.
|
|
22
|
+
*/
|
|
23
|
+
type Infer<S> = S extends Validator<infer T> ? T : never;
|
|
24
|
+
/**
|
|
25
|
+
* Base Schema class providing chainable modifiers for all schema types.
|
|
26
|
+
*/
|
|
27
|
+
declare abstract class AxeomSchema<T> implements Validator<T> {
|
|
28
|
+
readonly _output: T;
|
|
29
|
+
readonly _isOptional: boolean;
|
|
30
|
+
protected _isOptionalVal: boolean;
|
|
31
|
+
protected _isNullable: boolean;
|
|
32
|
+
protected _defaultValue?: T;
|
|
33
|
+
protected _transforms: Array<(data: any) => any>;
|
|
34
|
+
/**
|
|
35
|
+
* Generates a standard JSON Schema representation of the validator.
|
|
36
|
+
*/
|
|
37
|
+
abstract toJSONSchema(): any;
|
|
38
|
+
protected abstract _validate(data: unknown): ValidationResult<T>;
|
|
39
|
+
/**
|
|
40
|
+
* Validates and parses the input data, applying all transforms and refinements.
|
|
41
|
+
* Throws an error if validation fails.
|
|
42
|
+
*/
|
|
43
|
+
parse(data: unknown): Promise<T>;
|
|
44
|
+
/**
|
|
45
|
+
* Marks the field as optional, allowing 'undefined' inputs.
|
|
46
|
+
*/
|
|
47
|
+
optional(): this & {
|
|
48
|
+
readonly _isOptional: true;
|
|
49
|
+
readonly _output: T | undefined;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Marks the field as nullable, allowing 'null' inputs.
|
|
53
|
+
*/
|
|
54
|
+
nullable(): this & {
|
|
55
|
+
readonly _output: T | null;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Sets a default value if the input is missing.
|
|
59
|
+
*/
|
|
60
|
+
default(val: T): this;
|
|
61
|
+
/**
|
|
62
|
+
* Transforms the validated output into a new value or type.
|
|
63
|
+
*/
|
|
64
|
+
transform<U>(fn: (data: T) => U | Promise<U>): AxeomSchema<U> & this;
|
|
65
|
+
/**
|
|
66
|
+
* Adds a custom validation check. Throws if the function returns false.
|
|
67
|
+
*/
|
|
68
|
+
refine(fn: (data: T) => boolean | Promise<boolean>, message?: string): this;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* String Schema
|
|
72
|
+
*/
|
|
73
|
+
declare class StringSchema extends AxeomSchema<string> {
|
|
74
|
+
protected _validate(data: unknown): ValidationResult<string>;
|
|
75
|
+
toJSONSchema(): {
|
|
76
|
+
default?: string | undefined;
|
|
77
|
+
nullable?: boolean | undefined;
|
|
78
|
+
optional?: boolean | undefined;
|
|
79
|
+
type: string;
|
|
80
|
+
};
|
|
81
|
+
min(n: number): this;
|
|
82
|
+
max(n: number): this;
|
|
83
|
+
length(n: number): this;
|
|
84
|
+
email(): this;
|
|
85
|
+
url(): this;
|
|
86
|
+
regex(re: RegExp, message?: string): this;
|
|
87
|
+
trim(): this;
|
|
88
|
+
toLowerCase(): this;
|
|
89
|
+
toUpperCase(): this;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Number Schema
|
|
93
|
+
*/
|
|
94
|
+
declare class NumberSchema extends AxeomSchema<number> {
|
|
95
|
+
protected _validate(data: unknown): ValidationResult<number>;
|
|
96
|
+
toJSONSchema(): {
|
|
97
|
+
default?: number | undefined;
|
|
98
|
+
nullable?: boolean | undefined;
|
|
99
|
+
optional?: boolean | undefined;
|
|
100
|
+
type: string;
|
|
101
|
+
};
|
|
102
|
+
min(n: number): this;
|
|
103
|
+
max(n: number): this;
|
|
104
|
+
int(): this;
|
|
105
|
+
positive(): this;
|
|
106
|
+
negative(): this;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Boolean Schema
|
|
110
|
+
*/
|
|
111
|
+
declare class BooleanSchema extends AxeomSchema<boolean> {
|
|
112
|
+
protected _validate(data: unknown): ValidationResult<boolean>;
|
|
113
|
+
toJSONSchema(): {
|
|
114
|
+
default?: boolean | undefined;
|
|
115
|
+
nullable?: boolean | undefined;
|
|
116
|
+
optional?: boolean | undefined;
|
|
117
|
+
type: string;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Any Schema
|
|
122
|
+
*/
|
|
123
|
+
declare class AnySchema extends AxeomSchema<any> {
|
|
124
|
+
protected _validate(data: unknown): ValidationResult<any>;
|
|
125
|
+
toJSONSchema(): {
|
|
126
|
+
type: string;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Enum Schema - Validates that the input is one of a specific set of strings.
|
|
131
|
+
*/
|
|
132
|
+
declare class EnumSchema<T extends string> extends AxeomSchema<T> {
|
|
133
|
+
private values;
|
|
134
|
+
constructor(values: T[]);
|
|
135
|
+
protected _validate(data: unknown): ValidationResult<T>;
|
|
136
|
+
toJSONSchema(): {
|
|
137
|
+
type: string;
|
|
138
|
+
enum: T[];
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Type to correctly infer optional vs required properties in an object schema.
|
|
143
|
+
*/
|
|
144
|
+
type InferObject<T extends Record<string, Validator>> = {
|
|
145
|
+
[K in keyof T as T[K] extends {
|
|
146
|
+
_isOptional: true;
|
|
147
|
+
} ? never : K]: T[K]["_output"];
|
|
148
|
+
} & {
|
|
149
|
+
[K in keyof T as T[K] extends {
|
|
150
|
+
_isOptional: true;
|
|
151
|
+
} ? K : never]?: T[K]["_output"];
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Object Schema - Handles recursive validation of properties
|
|
155
|
+
*/
|
|
156
|
+
declare class ObjectSchema<T extends Record<string, Validator>> extends AxeomSchema<InferObject<T>> {
|
|
157
|
+
private shape;
|
|
158
|
+
constructor(shape: T);
|
|
159
|
+
protected _validate(data: unknown): ValidationResult<InferObject<T>>;
|
|
160
|
+
toJSONSchema(): {
|
|
161
|
+
type: string;
|
|
162
|
+
properties: any;
|
|
163
|
+
required: string[];
|
|
164
|
+
};
|
|
165
|
+
parse(data: unknown): Promise<InferObject<T>>;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Array Schema - Handles recursive validation of items
|
|
169
|
+
*/
|
|
170
|
+
declare class ArraySchema<T extends Validator> extends AxeomSchema<T["_output"][]> {
|
|
171
|
+
private itemSchema;
|
|
172
|
+
constructor(itemSchema: T);
|
|
173
|
+
protected _validate(data: unknown): ValidationResult<T["_output"][]>;
|
|
174
|
+
toJSONSchema(): {
|
|
175
|
+
type: string;
|
|
176
|
+
items: any;
|
|
177
|
+
};
|
|
178
|
+
min(n: number): this;
|
|
179
|
+
max(n: number): this;
|
|
180
|
+
length(n: number): this;
|
|
181
|
+
parse(data: unknown): Promise<T["_output"][]>;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* File Schema - Validates that the input is a standard File or Blob.
|
|
185
|
+
*/
|
|
186
|
+
declare class FileSchema extends AxeomSchema<File> {
|
|
187
|
+
protected _validate(data: unknown): ValidationResult<File>;
|
|
188
|
+
toJSONSchema(): {
|
|
189
|
+
type: string;
|
|
190
|
+
format: string;
|
|
191
|
+
};
|
|
192
|
+
max(sizeInBytes: number, message?: string): this;
|
|
193
|
+
min(sizeInBytes: number, message?: string): this;
|
|
194
|
+
type(mimeType: MimeType, message?: string): this;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Common Mime Types for autocomplete
|
|
198
|
+
*/
|
|
199
|
+
type MimeType = "image/jpeg" | "image/png" | "image/gif" | "image/webp" | "image/svg+xml" | "application/pdf" | "application/json" | "application/xml" | "application/zip" | "application/x-zip-compressed" | "application/octet-stream" | "text/plain" | "text/html" | "text/css" | "text/javascript" | "video/mp4" | "video/mpeg" | "audio/mpeg" | "audio/wav" | "image/" | "video/" | "audio/" | "text/" | "application/" | (string & {});
|
|
200
|
+
/**
|
|
201
|
+
* The main library interface.
|
|
202
|
+
* Use this to create new schema validators.
|
|
203
|
+
*/
|
|
204
|
+
declare const s: {
|
|
205
|
+
/** Create a string validator */
|
|
206
|
+
string: () => StringSchema;
|
|
207
|
+
/** Create a number validator */
|
|
208
|
+
number: () => NumberSchema;
|
|
209
|
+
/** Create a boolean validator */
|
|
210
|
+
boolean: () => BooleanSchema;
|
|
211
|
+
/** Create a validator that allows any input */
|
|
212
|
+
any: () => AnySchema;
|
|
213
|
+
/** Create an enum validator from an array of strings */
|
|
214
|
+
enum: <T extends string>(values: T[]) => EnumSchema<T>;
|
|
215
|
+
/** Create an object validator from a shape record */
|
|
216
|
+
object: <T extends Record<string, Validator>>(shape: T) => ObjectSchema<T>;
|
|
217
|
+
/** Create an array validator for a specific item type */
|
|
218
|
+
array: <T extends Validator>(item: T) => ArraySchema<T>;
|
|
219
|
+
/** Create a file/blob validator */
|
|
220
|
+
file: () => FileSchema;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export { AxeomSchema, type Infer, type InferObject, type MimeType, type Validator, s as default, s };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Axeom Schema
|
|
3
|
+
* A zero-dependency, ultra-lightweight validation library.
|
|
4
|
+
*/
|
|
5
|
+
type ValidationResult<T> = {
|
|
6
|
+
success: true;
|
|
7
|
+
data: T;
|
|
8
|
+
} | {
|
|
9
|
+
success: false;
|
|
10
|
+
error: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Standard Validator interface for Axeom.
|
|
14
|
+
*/
|
|
15
|
+
interface Validator<T = any> {
|
|
16
|
+
readonly _output: T;
|
|
17
|
+
readonly _isOptional?: boolean;
|
|
18
|
+
parse: (data: unknown) => T | Promise<T>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Infer the type of an Axeom Schema.
|
|
22
|
+
*/
|
|
23
|
+
type Infer<S> = S extends Validator<infer T> ? T : never;
|
|
24
|
+
/**
|
|
25
|
+
* Base Schema class providing chainable modifiers for all schema types.
|
|
26
|
+
*/
|
|
27
|
+
declare abstract class AxeomSchema<T> implements Validator<T> {
|
|
28
|
+
readonly _output: T;
|
|
29
|
+
readonly _isOptional: boolean;
|
|
30
|
+
protected _isOptionalVal: boolean;
|
|
31
|
+
protected _isNullable: boolean;
|
|
32
|
+
protected _defaultValue?: T;
|
|
33
|
+
protected _transforms: Array<(data: any) => any>;
|
|
34
|
+
/**
|
|
35
|
+
* Generates a standard JSON Schema representation of the validator.
|
|
36
|
+
*/
|
|
37
|
+
abstract toJSONSchema(): any;
|
|
38
|
+
protected abstract _validate(data: unknown): ValidationResult<T>;
|
|
39
|
+
/**
|
|
40
|
+
* Validates and parses the input data, applying all transforms and refinements.
|
|
41
|
+
* Throws an error if validation fails.
|
|
42
|
+
*/
|
|
43
|
+
parse(data: unknown): Promise<T>;
|
|
44
|
+
/**
|
|
45
|
+
* Marks the field as optional, allowing 'undefined' inputs.
|
|
46
|
+
*/
|
|
47
|
+
optional(): this & {
|
|
48
|
+
readonly _isOptional: true;
|
|
49
|
+
readonly _output: T | undefined;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Marks the field as nullable, allowing 'null' inputs.
|
|
53
|
+
*/
|
|
54
|
+
nullable(): this & {
|
|
55
|
+
readonly _output: T | null;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Sets a default value if the input is missing.
|
|
59
|
+
*/
|
|
60
|
+
default(val: T): this;
|
|
61
|
+
/**
|
|
62
|
+
* Transforms the validated output into a new value or type.
|
|
63
|
+
*/
|
|
64
|
+
transform<U>(fn: (data: T) => U | Promise<U>): AxeomSchema<U> & this;
|
|
65
|
+
/**
|
|
66
|
+
* Adds a custom validation check. Throws if the function returns false.
|
|
67
|
+
*/
|
|
68
|
+
refine(fn: (data: T) => boolean | Promise<boolean>, message?: string): this;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* String Schema
|
|
72
|
+
*/
|
|
73
|
+
declare class StringSchema extends AxeomSchema<string> {
|
|
74
|
+
protected _validate(data: unknown): ValidationResult<string>;
|
|
75
|
+
toJSONSchema(): {
|
|
76
|
+
default?: string | undefined;
|
|
77
|
+
nullable?: boolean | undefined;
|
|
78
|
+
optional?: boolean | undefined;
|
|
79
|
+
type: string;
|
|
80
|
+
};
|
|
81
|
+
min(n: number): this;
|
|
82
|
+
max(n: number): this;
|
|
83
|
+
length(n: number): this;
|
|
84
|
+
email(): this;
|
|
85
|
+
url(): this;
|
|
86
|
+
regex(re: RegExp, message?: string): this;
|
|
87
|
+
trim(): this;
|
|
88
|
+
toLowerCase(): this;
|
|
89
|
+
toUpperCase(): this;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Number Schema
|
|
93
|
+
*/
|
|
94
|
+
declare class NumberSchema extends AxeomSchema<number> {
|
|
95
|
+
protected _validate(data: unknown): ValidationResult<number>;
|
|
96
|
+
toJSONSchema(): {
|
|
97
|
+
default?: number | undefined;
|
|
98
|
+
nullable?: boolean | undefined;
|
|
99
|
+
optional?: boolean | undefined;
|
|
100
|
+
type: string;
|
|
101
|
+
};
|
|
102
|
+
min(n: number): this;
|
|
103
|
+
max(n: number): this;
|
|
104
|
+
int(): this;
|
|
105
|
+
positive(): this;
|
|
106
|
+
negative(): this;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Boolean Schema
|
|
110
|
+
*/
|
|
111
|
+
declare class BooleanSchema extends AxeomSchema<boolean> {
|
|
112
|
+
protected _validate(data: unknown): ValidationResult<boolean>;
|
|
113
|
+
toJSONSchema(): {
|
|
114
|
+
default?: boolean | undefined;
|
|
115
|
+
nullable?: boolean | undefined;
|
|
116
|
+
optional?: boolean | undefined;
|
|
117
|
+
type: string;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Any Schema
|
|
122
|
+
*/
|
|
123
|
+
declare class AnySchema extends AxeomSchema<any> {
|
|
124
|
+
protected _validate(data: unknown): ValidationResult<any>;
|
|
125
|
+
toJSONSchema(): {
|
|
126
|
+
type: string;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Enum Schema - Validates that the input is one of a specific set of strings.
|
|
131
|
+
*/
|
|
132
|
+
declare class EnumSchema<T extends string> extends AxeomSchema<T> {
|
|
133
|
+
private values;
|
|
134
|
+
constructor(values: T[]);
|
|
135
|
+
protected _validate(data: unknown): ValidationResult<T>;
|
|
136
|
+
toJSONSchema(): {
|
|
137
|
+
type: string;
|
|
138
|
+
enum: T[];
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Type to correctly infer optional vs required properties in an object schema.
|
|
143
|
+
*/
|
|
144
|
+
type InferObject<T extends Record<string, Validator>> = {
|
|
145
|
+
[K in keyof T as T[K] extends {
|
|
146
|
+
_isOptional: true;
|
|
147
|
+
} ? never : K]: T[K]["_output"];
|
|
148
|
+
} & {
|
|
149
|
+
[K in keyof T as T[K] extends {
|
|
150
|
+
_isOptional: true;
|
|
151
|
+
} ? K : never]?: T[K]["_output"];
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Object Schema - Handles recursive validation of properties
|
|
155
|
+
*/
|
|
156
|
+
declare class ObjectSchema<T extends Record<string, Validator>> extends AxeomSchema<InferObject<T>> {
|
|
157
|
+
private shape;
|
|
158
|
+
constructor(shape: T);
|
|
159
|
+
protected _validate(data: unknown): ValidationResult<InferObject<T>>;
|
|
160
|
+
toJSONSchema(): {
|
|
161
|
+
type: string;
|
|
162
|
+
properties: any;
|
|
163
|
+
required: string[];
|
|
164
|
+
};
|
|
165
|
+
parse(data: unknown): Promise<InferObject<T>>;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Array Schema - Handles recursive validation of items
|
|
169
|
+
*/
|
|
170
|
+
declare class ArraySchema<T extends Validator> extends AxeomSchema<T["_output"][]> {
|
|
171
|
+
private itemSchema;
|
|
172
|
+
constructor(itemSchema: T);
|
|
173
|
+
protected _validate(data: unknown): ValidationResult<T["_output"][]>;
|
|
174
|
+
toJSONSchema(): {
|
|
175
|
+
type: string;
|
|
176
|
+
items: any;
|
|
177
|
+
};
|
|
178
|
+
min(n: number): this;
|
|
179
|
+
max(n: number): this;
|
|
180
|
+
length(n: number): this;
|
|
181
|
+
parse(data: unknown): Promise<T["_output"][]>;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* File Schema - Validates that the input is a standard File or Blob.
|
|
185
|
+
*/
|
|
186
|
+
declare class FileSchema extends AxeomSchema<File> {
|
|
187
|
+
protected _validate(data: unknown): ValidationResult<File>;
|
|
188
|
+
toJSONSchema(): {
|
|
189
|
+
type: string;
|
|
190
|
+
format: string;
|
|
191
|
+
};
|
|
192
|
+
max(sizeInBytes: number, message?: string): this;
|
|
193
|
+
min(sizeInBytes: number, message?: string): this;
|
|
194
|
+
type(mimeType: MimeType, message?: string): this;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Common Mime Types for autocomplete
|
|
198
|
+
*/
|
|
199
|
+
type MimeType = "image/jpeg" | "image/png" | "image/gif" | "image/webp" | "image/svg+xml" | "application/pdf" | "application/json" | "application/xml" | "application/zip" | "application/x-zip-compressed" | "application/octet-stream" | "text/plain" | "text/html" | "text/css" | "text/javascript" | "video/mp4" | "video/mpeg" | "audio/mpeg" | "audio/wav" | "image/" | "video/" | "audio/" | "text/" | "application/" | (string & {});
|
|
200
|
+
/**
|
|
201
|
+
* The main library interface.
|
|
202
|
+
* Use this to create new schema validators.
|
|
203
|
+
*/
|
|
204
|
+
declare const s: {
|
|
205
|
+
/** Create a string validator */
|
|
206
|
+
string: () => StringSchema;
|
|
207
|
+
/** Create a number validator */
|
|
208
|
+
number: () => NumberSchema;
|
|
209
|
+
/** Create a boolean validator */
|
|
210
|
+
boolean: () => BooleanSchema;
|
|
211
|
+
/** Create a validator that allows any input */
|
|
212
|
+
any: () => AnySchema;
|
|
213
|
+
/** Create an enum validator from an array of strings */
|
|
214
|
+
enum: <T extends string>(values: T[]) => EnumSchema<T>;
|
|
215
|
+
/** Create an object validator from a shape record */
|
|
216
|
+
object: <T extends Record<string, Validator>>(shape: T) => ObjectSchema<T>;
|
|
217
|
+
/** Create an array validator for a specific item type */
|
|
218
|
+
array: <T extends Validator>(item: T) => ArraySchema<T>;
|
|
219
|
+
/** Create a file/blob validator */
|
|
220
|
+
file: () => FileSchema;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export { AxeomSchema, type Infer, type InferObject, type MimeType, type Validator, s as default, s };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var AxeomSchema = class {
|
|
3
|
+
_output;
|
|
4
|
+
_isOptional;
|
|
5
|
+
_isOptionalVal = false;
|
|
6
|
+
_isNullable = false;
|
|
7
|
+
_defaultValue;
|
|
8
|
+
_transforms = [];
|
|
9
|
+
/**
|
|
10
|
+
* Validates and parses the input data, applying all transforms and refinements.
|
|
11
|
+
* Throws an error if validation fails.
|
|
12
|
+
*/
|
|
13
|
+
async parse(data) {
|
|
14
|
+
if (data === void 0) {
|
|
15
|
+
if (this._defaultValue !== void 0) data = this._defaultValue;
|
|
16
|
+
else if (this._isOptionalVal) return void 0;
|
|
17
|
+
else throw new Error("Required field is missing");
|
|
18
|
+
}
|
|
19
|
+
if (data === null) {
|
|
20
|
+
if (this._isNullable) return null;
|
|
21
|
+
throw new Error("Field cannot be null");
|
|
22
|
+
}
|
|
23
|
+
const result = this._validate(data);
|
|
24
|
+
if (!result.success) throw new Error(result.error);
|
|
25
|
+
let finalData = result.data;
|
|
26
|
+
for (const transform of this._transforms) {
|
|
27
|
+
finalData = await transform(finalData);
|
|
28
|
+
}
|
|
29
|
+
return finalData;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Marks the field as optional, allowing 'undefined' inputs.
|
|
33
|
+
*/
|
|
34
|
+
optional() {
|
|
35
|
+
this._isOptionalVal = true;
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Marks the field as nullable, allowing 'null' inputs.
|
|
40
|
+
*/
|
|
41
|
+
nullable() {
|
|
42
|
+
this._isNullable = true;
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Sets a default value if the input is missing.
|
|
47
|
+
*/
|
|
48
|
+
default(val) {
|
|
49
|
+
this._defaultValue = val;
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Transforms the validated output into a new value or type.
|
|
54
|
+
*/
|
|
55
|
+
transform(fn) {
|
|
56
|
+
this._transforms.push(fn);
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Adds a custom validation check. Throws if the function returns false.
|
|
61
|
+
*/
|
|
62
|
+
refine(fn, message = "Invalid value") {
|
|
63
|
+
return this.transform(async (data) => {
|
|
64
|
+
if (!await fn(data)) throw new Error(message);
|
|
65
|
+
return data;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var StringSchema = class extends AxeomSchema {
|
|
70
|
+
_validate(data) {
|
|
71
|
+
return typeof data === "string" ? { success: true, data } : { success: false, error: `Expected string, got ${typeof data}` };
|
|
72
|
+
}
|
|
73
|
+
toJSONSchema() {
|
|
74
|
+
return {
|
|
75
|
+
type: "string",
|
|
76
|
+
...this._isOptionalVal ? { optional: true } : {},
|
|
77
|
+
...this._isNullable ? { nullable: true } : {},
|
|
78
|
+
...this._defaultValue !== void 0 ? { default: this._defaultValue } : {}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
min(n) {
|
|
82
|
+
this.refine((v) => v.length >= n, `Too short (min ${n} characters)`);
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
max(n) {
|
|
86
|
+
this.refine((v) => v.length <= n, `Too long (max ${n} characters)`);
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
length(n) {
|
|
90
|
+
this.refine((v) => v.length === n, `Must be exactly ${n} characters`);
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
email() {
|
|
94
|
+
this.refine((v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v), "Invalid email address");
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
url() {
|
|
98
|
+
this.refine((v) => {
|
|
99
|
+
try {
|
|
100
|
+
new URL(v);
|
|
101
|
+
return true;
|
|
102
|
+
} catch {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}, "Invalid URL");
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
regex(re, message = "Does not match pattern") {
|
|
109
|
+
this.refine((v) => re.test(v), message);
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
trim() {
|
|
113
|
+
this.transform((v) => v.trim());
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
toLowerCase() {
|
|
117
|
+
this.transform((v) => v.toLowerCase());
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
toUpperCase() {
|
|
121
|
+
this.transform((v) => v.toUpperCase());
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
var NumberSchema = class extends AxeomSchema {
|
|
126
|
+
_validate(data) {
|
|
127
|
+
return typeof data === "number" ? { success: true, data } : { success: false, error: `Expected number, got ${typeof data}` };
|
|
128
|
+
}
|
|
129
|
+
toJSONSchema() {
|
|
130
|
+
return {
|
|
131
|
+
type: "number",
|
|
132
|
+
...this._isOptionalVal ? { optional: true } : {},
|
|
133
|
+
...this._isNullable ? { nullable: true } : {},
|
|
134
|
+
...this._defaultValue !== void 0 ? { default: this._defaultValue } : {}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
min(n) {
|
|
138
|
+
this.refine((v) => v >= n, `Must be greater than or equal to ${n}`);
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
141
|
+
max(n) {
|
|
142
|
+
this.refine((v) => v <= n, `Must be less than or equal to ${n}`);
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
int() {
|
|
146
|
+
this.refine((v) => Number.isInteger(v), "Must be an integer");
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
positive() {
|
|
150
|
+
return this.min(0);
|
|
151
|
+
}
|
|
152
|
+
negative() {
|
|
153
|
+
return this.max(0);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
var BooleanSchema = class extends AxeomSchema {
|
|
157
|
+
_validate(data) {
|
|
158
|
+
return typeof data === "boolean" ? { success: true, data } : { success: false, error: `Expected boolean, got ${typeof data}` };
|
|
159
|
+
}
|
|
160
|
+
toJSONSchema() {
|
|
161
|
+
return {
|
|
162
|
+
type: "boolean",
|
|
163
|
+
...this._isOptionalVal ? { optional: true } : {},
|
|
164
|
+
...this._isNullable ? { nullable: true } : {},
|
|
165
|
+
...this._defaultValue !== void 0 ? { default: this._defaultValue } : {}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
var AnySchema = class extends AxeomSchema {
|
|
170
|
+
_validate(data) {
|
|
171
|
+
return { success: true, data };
|
|
172
|
+
}
|
|
173
|
+
toJSONSchema() {
|
|
174
|
+
return { type: "any" };
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
var EnumSchema = class extends AxeomSchema {
|
|
178
|
+
constructor(values) {
|
|
179
|
+
super();
|
|
180
|
+
this.values = values;
|
|
181
|
+
}
|
|
182
|
+
_validate(data) {
|
|
183
|
+
return typeof data === "string" && this.values.includes(data) ? { success: true, data } : {
|
|
184
|
+
success: false,
|
|
185
|
+
error: `Invalid value. Expected one of: ${this.values.join(", ")}`
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
toJSONSchema() {
|
|
189
|
+
return {
|
|
190
|
+
type: "string",
|
|
191
|
+
enum: this.values
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
var ObjectSchema = class extends AxeomSchema {
|
|
196
|
+
constructor(shape) {
|
|
197
|
+
super();
|
|
198
|
+
this.shape = shape;
|
|
199
|
+
}
|
|
200
|
+
_validate(data) {
|
|
201
|
+
return typeof data === "object" && data !== null ? { success: true, data } : { success: false, error: "Expected object" };
|
|
202
|
+
}
|
|
203
|
+
toJSONSchema() {
|
|
204
|
+
const properties = {};
|
|
205
|
+
for (const key in this.shape) {
|
|
206
|
+
properties[key] = this.shape[key].toJSONSchema();
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
type: "object",
|
|
210
|
+
properties,
|
|
211
|
+
required: Object.keys(this.shape).filter((key) => !this.shape[key]._isOptionalVal)
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
async parse(data) {
|
|
215
|
+
const base = await super.parse(data);
|
|
216
|
+
const result = {};
|
|
217
|
+
const errors = [];
|
|
218
|
+
for (const key in this.shape) {
|
|
219
|
+
try {
|
|
220
|
+
result[key] = await this.shape[key].parse(base[key]);
|
|
221
|
+
} catch (e) {
|
|
222
|
+
errors.push(`${key}: ${e.message}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (errors.length > 0) {
|
|
226
|
+
throw new Error(errors.join(", "));
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
var ArraySchema = class extends AxeomSchema {
|
|
232
|
+
constructor(itemSchema) {
|
|
233
|
+
super();
|
|
234
|
+
this.itemSchema = itemSchema;
|
|
235
|
+
}
|
|
236
|
+
_validate(data) {
|
|
237
|
+
return Array.isArray(data) ? { success: true, data } : { success: false, error: "Expected array" };
|
|
238
|
+
}
|
|
239
|
+
toJSONSchema() {
|
|
240
|
+
return {
|
|
241
|
+
type: "array",
|
|
242
|
+
items: this.itemSchema.toJSONSchema()
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
min(n) {
|
|
246
|
+
this.refine((v) => v.length >= n, `Must have at least ${n} items`);
|
|
247
|
+
return this;
|
|
248
|
+
}
|
|
249
|
+
max(n) {
|
|
250
|
+
this.refine((v) => v.length <= n, `Must have at most ${n} items`);
|
|
251
|
+
return this;
|
|
252
|
+
}
|
|
253
|
+
length(n) {
|
|
254
|
+
this.refine((v) => v.length === n, `Must have exactly ${n} items`);
|
|
255
|
+
return this;
|
|
256
|
+
}
|
|
257
|
+
async parse(data) {
|
|
258
|
+
const base = await super.parse(data);
|
|
259
|
+
const result = await Promise.all(
|
|
260
|
+
base.map(
|
|
261
|
+
(item, i) => this.itemSchema.parse(item).catch((e) => {
|
|
262
|
+
throw new Error(`[${i}]: ${e.message}`);
|
|
263
|
+
})
|
|
264
|
+
)
|
|
265
|
+
);
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
var FileSchema = class extends AxeomSchema {
|
|
270
|
+
_validate(data) {
|
|
271
|
+
return data instanceof File || data instanceof Blob ? { success: true, data } : { success: false, error: "Expected File or Blob" };
|
|
272
|
+
}
|
|
273
|
+
toJSONSchema() {
|
|
274
|
+
return {
|
|
275
|
+
type: "string",
|
|
276
|
+
format: "binary"
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
max(sizeInBytes, message) {
|
|
280
|
+
return this.refine(
|
|
281
|
+
(v) => v.size <= sizeInBytes,
|
|
282
|
+
message || `File is too large (max ${sizeInBytes} bytes)`
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
min(sizeInBytes, message) {
|
|
286
|
+
return this.refine(
|
|
287
|
+
(v) => v.size >= sizeInBytes,
|
|
288
|
+
message || `File is too small (min ${sizeInBytes} bytes)`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
type(mimeType, message) {
|
|
292
|
+
return this.refine(
|
|
293
|
+
(v) => v.type.startsWith(mimeType),
|
|
294
|
+
message || `Invalid file type (expected ${mimeType})`
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
var s = {
|
|
299
|
+
/** Create a string validator */
|
|
300
|
+
string: () => new StringSchema(),
|
|
301
|
+
/** Create a number validator */
|
|
302
|
+
number: () => new NumberSchema(),
|
|
303
|
+
/** Create a boolean validator */
|
|
304
|
+
boolean: () => new BooleanSchema(),
|
|
305
|
+
/** Create a validator that allows any input */
|
|
306
|
+
any: () => new AnySchema(),
|
|
307
|
+
/** Create an enum validator from an array of strings */
|
|
308
|
+
enum: (values) => new EnumSchema(values),
|
|
309
|
+
/** Create an object validator from a shape record */
|
|
310
|
+
object: (shape) => new ObjectSchema(shape),
|
|
311
|
+
/** Create an array validator for a specific item type */
|
|
312
|
+
array: (item) => new ArraySchema(item),
|
|
313
|
+
/** Create a file/blob validator */
|
|
314
|
+
file: () => new FileSchema()
|
|
315
|
+
};
|
|
316
|
+
var index_default = s;
|
|
317
|
+
export {
|
|
318
|
+
AxeomSchema,
|
|
319
|
+
index_default as default,
|
|
320
|
+
s
|
|
321
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@axeom/schema",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"main": "./dist/index.cjs",
|
|
5
|
+
"module": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"devDependencies": {
|
|
10
|
+
"tsup": "^8.0.2"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "tsup --watch"
|
|
25
|
+
}
|
|
26
|
+
}
|