@lucania/schema 2.0.2 → 2.0.4
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 +67 -17
- package/build/builder.d.ts +15 -9
- package/build/index.d.ts +1 -1
- package/build/index.js +230 -54
- package/build/schema/AnySchema.d.ts +4 -2
- package/build/schema/ArraySchema.d.ts +6 -3
- package/build/schema/BaseSchema.d.ts +26 -3
- package/build/schema/BooleanSchema.d.ts +4 -2
- package/build/schema/DateSchema.d.ts +4 -2
- package/build/schema/DynamicObjectSchema.d.ts +6 -3
- package/build/schema/EnumerationSchema.d.ts +8 -6
- package/build/schema/NumberSchema.d.ts +4 -2
- package/build/schema/ObjectSchema.d.ts +6 -3
- package/build/schema/OrSetSchema.d.ts +10 -12
- package/build/schema/StringSchema.d.ts +32 -2
- package/build/typing/toolbox.d.ts +17 -5
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
# Schema
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
|
2
|
+
|
|
3
|
+
| TECHNICAL | |
|
|
4
|
+
| :-------: | -------------------------------------------------------------------------- |
|
|
5
|
+
| _noun_ | _a representation of a plan or theory in the form of an outline or model._ |
|
|
5
6
|
|
|
6
7
|
This library allows you to specify the schema for your data, and get compile time typings _(help from your IDE)_ and runtime validation _(throw errors if your data isn't in the right format)_.
|
|
7
8
|
|
|
8
9
|
With this library, you create objects that serve as a blueprint for what your data should look like. These objects are called `Schema`s.
|
|
9
10
|
|
|
10
|
-
|_Schema Type_|Description|Usage|Example Data|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
| _Schema Type_ | Description | Usage | Example Data |
|
|
12
|
+
| :-------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| `StringSchema` | Used to represent a string. | `$.String(required?: boolean, default?: StringSource)` | `"Moaaz"`, `"The cow jumped over the moon."` |
|
|
14
|
+
| `NumberSchema` | Used to represent a number. | `$.Number(required?: boolean, default?: NumberSource)` | `-30`, `0`, `10` |
|
|
15
|
+
| `BooleanSchema` | Used to represent a boolean. | `$.Boolean(required?: boolean, default?: BooleanSource)` | `true`, `false` |
|
|
16
|
+
| `DateSchema` | Used to represent a Date. | `$.Date(required?: boolean, default?: DateSource)` | `new Date(2000, 9, 29)`, `new Date("1998-09-04")` |
|
|
17
|
+
| `ObjectSchema` | Used to represent an Object. | `$.Object(subschema: { <key>: Schema }, required?: boolean, default?: ObjectSource)` | `<depends on subschema>`, `{ name: "Jeremy", age: 23 }`, `{ make: "Toyota", model: "Sienna", year: 2011 }` |
|
|
18
|
+
| `ArraySchema` | Used to represent an array | `$.Array(subschema: Schema, required?: boolean, default?: ArraySource)` | `<depends on subschema>`, `[]`, `[1, 2, 3]`, `["Ben", "Amit", "Dean"]` |
|
|
19
|
+
| `EnumerationSchema` | Used to represent an enumeration value, otherwise known as a set of possible strings values. | `$.Enumeration(members: Members, required?: boolean, default?: EnumerationSource)` | `<depends on members>`, `"MAGENTA"`, `"CA"`, `"male"` |
|
|
20
|
+
| `OrSetSchema` | Used to represent a value that should be validated by one of many possible schemas. This is used to represent a value that is allowed to be multiple types. | `$.OrSet(members: Members, required?: boolean, default?: OrSetSource)` | `<depends on members>` |
|
|
21
|
+
| `DynamicObjectSchema` | Used to represent an object that could have many different keys. | `$.DynamicObject(subschema: Schema, required?: boolean, default?: DynamicObjectSource)` | `{ <any key>: <depends on subschema> }` |
|
|
22
|
+
| `AnySchema` | Used to represent a value that could be any type. | `$.Any(required?: boolean, default?: AnySource)` | `1`, `"Omar"`, `false`, `{}`, `[]`, `<any type>` |
|
|
22
23
|
|
|
23
24
|
## Validation
|
|
25
|
+
|
|
24
26
|
The simplest possible validation:
|
|
27
|
+
|
|
25
28
|
```typescript
|
|
26
29
|
import { $ } from "@lucania/schema";
|
|
27
30
|
|
|
@@ -38,7 +41,9 @@ const dataOfNumberType = numberSchema.validate(dataOfUnknownType);
|
|
|
38
41
|
With this collection of Schemas, we can now start building more complex data structures. There are multiple way to represent different scenarios. This is done with the following constructs.
|
|
39
42
|
|
|
40
43
|
## Creating Hierarchical Schema
|
|
44
|
+
|
|
41
45
|
These schemas can come together to allow you to define a blueprint for more complex data structures.
|
|
46
|
+
|
|
42
47
|
```typescript
|
|
43
48
|
import { $ } from "@lucania/schema";
|
|
44
49
|
|
|
@@ -58,7 +63,6 @@ const PersonSchema = $.Object({
|
|
|
58
63
|
age: $.Number(true).min(16, "You must be at least 16 years old!").max(100, "You must be at most 100 years old!"),
|
|
59
64
|
weapon: WeaponSchema
|
|
60
65
|
});
|
|
61
|
-
|
|
62
66
|
```
|
|
63
67
|
|
|
64
68
|
## Using Schema
|
|
@@ -71,6 +75,52 @@ import fs from "fs";
|
|
|
71
75
|
const personData = JSON.parse(fs.readFileSync("person.json", "utf8"));
|
|
72
76
|
const person = PersonSchema.validate(personData);
|
|
73
77
|
```
|
|
78
|
+
|
|
74
79
|
`person` now has the following compile-time type annotation based on [`PersonSchema`](#creating-hierarchical-schema).
|
|
75
80
|
|
|
76
|
-
At runtime, the object parsed from the `person.json` file will be validated to match this generated typing. If it does not match, a `Schema.ValidationError` will be thrown.
|
|
81
|
+
At runtime, the object parsed from the `person.json` file will be validated to match this generated typing. If it does not match, a `Schema.ValidationError` will be thrown.
|
|
82
|
+
|
|
83
|
+
## Additional Validation Passes
|
|
84
|
+
|
|
85
|
+
Sometimes it's necessary to validate not only a type, but also specifics about the value. I.E. a value is a `$.Number` _and_ is between 0 and 100. You can add additional type-specific validation passes (I.E. `.clamp(...)`) or custom ones (`.custom(...)`):
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const numberData: any = ...;
|
|
89
|
+
|
|
90
|
+
const ScoreSchema = $.Number().clamp(0, 100).custom((data, pass) => {
|
|
91
|
+
pass.assert(data % 2 === 0, "Your score must be even!");
|
|
92
|
+
return data;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// The above custom() validation pass can alternately be defined using the .ensure() shorthand.
|
|
96
|
+
|
|
97
|
+
const ScoreSchema = $.Number().clamp(0, 100).ensure((data, pass) => data % 2 === 0, "Your score must be even!");
|
|
98
|
+
|
|
99
|
+
const number: number = ScoreSchema.validate(numberData);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Custom Schema Types
|
|
103
|
+
|
|
104
|
+
You can define your own Schema types by creating a subclass of `BaseSchema`. But first, `BaseSchema` relies on 4 generic parameters for TypeScript's type checker to understand your Schema at compile time. These parameters are as follows:
|
|
105
|
+
|
|
106
|
+
- Source - Represents the typing for a source input to your schema.
|
|
107
|
+
- Model - Represents the typing for an output value from your schema.
|
|
108
|
+
- Required - Represents the presence optionality of your schema's Source/Model. (Is `undefined` a valid Source and Model)
|
|
109
|
+
- Default - Represents the typing for default values.
|
|
110
|
+
|
|
111
|
+
Typically, when developing your own Schema types, you'll hardcode Source and Model for your specific Schema's requirements, but pass over `Required` and `Default` generics to `BaseSchema` to allow them to be inferred from your Schema definitions.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
export type CowModel = { name: string, numberOfSpots: number };
|
|
115
|
+
|
|
116
|
+
export type CowSource = string | CowModel;
|
|
117
|
+
|
|
118
|
+
export class CowSchema<
|
|
119
|
+
Required extends boolean, // Taking in "Required" generic.
|
|
120
|
+
Default extends DefaultValue<CowSource> // Taking in "Default" generic.
|
|
121
|
+
|
|
122
|
+
// Handing over hardcoded Source, Model and CowSchema generics to BaseSchema.
|
|
123
|
+
> extends BaseSchema<CowSource, CowModel, Required, Default> { ... }
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
These declarations are enough to have the TypeScript type checker understand the compile-time typing for `CowSchema`. Next, lets implement our runtime checks.
|
package/build/builder.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseSchemaAny } from "./typing/extended";
|
|
2
|
-
import { DefaultValue, ModelValue
|
|
2
|
+
import { DefaultValue, ModelValue } from "./typing/toolbox";
|
|
3
3
|
import { AnySchema } from "./schema/AnySchema";
|
|
4
4
|
import { ArraySchema, ArraySource } from "./schema/ArraySchema";
|
|
5
5
|
import { BooleanSchema, BooleanSource } from "./schema/BooleanSchema";
|
|
@@ -33,17 +33,23 @@ export declare namespace Schema {
|
|
|
33
33
|
function Array<Subschema extends BaseSchemaAny>(subschema: Subschema): ArraySchema<Subschema, true, undefined>;
|
|
34
34
|
function Array<Subschema extends BaseSchemaAny, Required extends boolean>(subschema: Subschema, required: Required): ArraySchema<Subschema, Required, undefined>;
|
|
35
35
|
function Array<Subschema extends BaseSchemaAny, Required extends boolean, Default extends DefaultValue<ArraySource<Subschema>>>(subschema: Subschema, required: Required, defaultValue: Default): ArraySchema<Subschema, Required, Default>;
|
|
36
|
-
function Enumeration<
|
|
37
|
-
function Enumeration<
|
|
38
|
-
function Enumeration<
|
|
36
|
+
function Enumeration<Member extends string>(subschema: TypedMembers<Member>): EnumerationSchema<Member, true, undefined>;
|
|
37
|
+
function Enumeration<Member extends string, Required extends boolean>(subschema: TypedMembers<Member>, required: Required): EnumerationSchema<Member, Required, undefined>;
|
|
38
|
+
function Enumeration<Member extends string, Required extends boolean, Default extends DefaultValue<Member>>(subschema: TypedMembers<Member>, required: Required, defaultValue: Default): EnumerationSchema<Member, Required, Default>;
|
|
39
39
|
function DynamicObject<Subschema extends BaseSchemaAny>(subschema: Subschema): DynamicObjectSchema<Subschema, true, undefined>;
|
|
40
40
|
function DynamicObject<Subschema extends BaseSchemaAny, Required extends boolean>(subschema: Subschema, required: Required): DynamicObjectSchema<Subschema, Required, undefined>;
|
|
41
41
|
function DynamicObject<Subschema extends BaseSchemaAny, Required extends boolean, Default extends DefaultValue<DynamicObjectSource<Subschema>>>(subschema: Subschema, required: Required, defaultValue: Default): DynamicObjectSchema<Subschema, Required, Default>;
|
|
42
|
-
function OrSet<
|
|
43
|
-
function OrSet<
|
|
44
|
-
function OrSet<
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
function OrSet<MemberSchema extends BaseSchemaAny>(subschema: TypedMembers<MemberSchema>): OrSetSchema<MemberSchema, true, undefined>;
|
|
43
|
+
function OrSet<MemberSchema extends BaseSchemaAny, Required extends boolean>(subschema: TypedMembers<MemberSchema>, required: Required): OrSetSchema<MemberSchema, Required, undefined>;
|
|
44
|
+
function OrSet<MemberSchema extends BaseSchemaAny, Required extends boolean, Default extends DefaultValue<OrSetSchemaSource<MemberSchema>>>(subschema: TypedMembers<MemberSchema>, required: Required, defaultValue: Default): OrSetSchema<MemberSchema, Required, Default>;
|
|
45
|
+
type TypedMembers<Member extends any> = {
|
|
46
|
+
$members: Member[];
|
|
47
|
+
};
|
|
48
|
+
function Members<Member extends string[]>(...members: Member): TypedMembers<Member[number]>;
|
|
49
|
+
function Members<Member extends number[]>(...members: Member): TypedMembers<Member[number]>;
|
|
50
|
+
function Members<Member extends any[]>(...members: Member): TypedMembers<Member[number]>;
|
|
51
|
+
function Keys<Object extends object>(object: Object): (keyof Object)[];
|
|
52
|
+
function Values<Object extends object>(object: Object): (Object[keyof Object])[];
|
|
47
53
|
type Model<Schema extends BaseSchemaAny> = Schema extends BaseSchema<infer Source, infer Model, infer Require, infer Default> ? (ModelValue<Source, Model, Require, Default>) : never;
|
|
48
54
|
type Source<Schema extends BaseSchemaAny> = Schema extends BaseSchema<infer Source, any, infer Require, infer Default> ? (SourceValue<Source, Require, Default>) : never;
|
|
49
55
|
}
|
package/build/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export * from "./builder";
|
|
2
|
-
export { Schema as $ } from "./builder";
|
|
3
2
|
export * from "./error/ValidationError";
|
|
4
3
|
export * from "./error/ValidationPass";
|
|
5
4
|
export * from "./schema/AnySchema";
|
|
@@ -15,3 +14,4 @@ export * from "./schema/OrSetSchema";
|
|
|
15
14
|
export * from "./schema/StringSchema";
|
|
16
15
|
export * from "./typing/extended";
|
|
17
16
|
export * from "./typing/toolbox";
|
|
17
|
+
export { Schema as $ } from "./builder";
|
package/build/index.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
pass;
|
|
9
9
|
constructor(pass, message) {
|
|
10
10
|
super(message);
|
|
11
|
+
this.name = ValidationError.name;
|
|
11
12
|
this.pass = pass;
|
|
12
13
|
}
|
|
13
14
|
}
|
|
@@ -51,12 +52,10 @@
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
class BaseSchema {
|
|
54
|
-
type;
|
|
55
55
|
_required;
|
|
56
56
|
_default;
|
|
57
57
|
_additionalValidationPasses;
|
|
58
|
-
constructor(
|
|
59
|
-
this.type = type;
|
|
58
|
+
constructor(required, defaultValue, additionalValidationPasses) {
|
|
60
59
|
this._required = required;
|
|
61
60
|
this._default = defaultValue;
|
|
62
61
|
this._additionalValidationPasses = additionalValidationPasses === undefined ? {
|
|
@@ -82,16 +81,18 @@
|
|
|
82
81
|
if (BaseSchema.getType(result) !== this.type) {
|
|
83
82
|
result = this.convert(result, pass);
|
|
84
83
|
}
|
|
84
|
+
result = this._validate(result, pass);
|
|
85
85
|
result = this._executeAdditionalValidator(result, pass, "afterConversion");
|
|
86
86
|
}
|
|
87
87
|
else {
|
|
88
|
-
result = this._executeAdditionalValidator(result, pass, "afterConversion");
|
|
89
88
|
if (this._required) {
|
|
90
89
|
throw pass.getError(pass.path.length > 0 ? `Missing required value at "${pass.path.join(".")}".` : "Missing required value.");
|
|
91
90
|
}
|
|
92
91
|
else {
|
|
93
92
|
result = undefined;
|
|
94
93
|
}
|
|
94
|
+
result = this._validate(result, pass);
|
|
95
|
+
result = this._executeAdditionalValidator(result, pass, "afterConversion");
|
|
95
96
|
}
|
|
96
97
|
if (this._required || result !== undefined) {
|
|
97
98
|
result = this._executeAdditionalValidator(result, pass, "afterAll");
|
|
@@ -102,6 +103,15 @@
|
|
|
102
103
|
this._additionalValidationPasses[type].push(additionalValidator);
|
|
103
104
|
return this;
|
|
104
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Ensures a condition is true during the associated schema's validation pass.
|
|
108
|
+
*
|
|
109
|
+
* @note See {@link EnsureValidator EnsureValidator}.
|
|
110
|
+
*
|
|
111
|
+
* @param ensureValidator A validator to ensure a condition is passed.
|
|
112
|
+
* @param message The message to use for a thrown {@link ValidationError ValidationError} if ensureValidator fails.
|
|
113
|
+
* @returns The schema instance for chaining.
|
|
114
|
+
*/
|
|
105
115
|
ensure(ensureValidator, message) {
|
|
106
116
|
this.custom((value, pass) => {
|
|
107
117
|
pass.assert(ensureValidator(value, pass), message === undefined ? `Failed to ensure value.` : message);
|
|
@@ -132,6 +142,13 @@
|
|
|
132
142
|
_getValueDisplay(value) {
|
|
133
143
|
return String(value);
|
|
134
144
|
}
|
|
145
|
+
getJsonSchema() {
|
|
146
|
+
console.warn(`"getJsonSchema" is not implemented in the "${this.constructor.name}" schema class!`);
|
|
147
|
+
return {
|
|
148
|
+
type: "unknown",
|
|
149
|
+
description: `"getJsonSchema" is not implemented in the "${this.constructor.name}" schema class!`,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
135
152
|
_getJsonSchemaDescription() {
|
|
136
153
|
const pass = new ValidationPass(this, this._default);
|
|
137
154
|
const descriptionPieces = [];
|
|
@@ -166,6 +183,29 @@
|
|
|
166
183
|
}
|
|
167
184
|
return source;
|
|
168
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Converts this schema to a informational string representing this schema.
|
|
188
|
+
*
|
|
189
|
+
* @note Details about this schema will be displayed in [square brackets].
|
|
190
|
+
* * [R] Indicates this schema is required.
|
|
191
|
+
* * [D] Indicates this schema has a default value.
|
|
192
|
+
*
|
|
193
|
+
* @param level The nesting level. You needn't specify this externally.
|
|
194
|
+
* @returns A string representation of this schema.
|
|
195
|
+
*/
|
|
196
|
+
toString(level = 0) {
|
|
197
|
+
const modifiers = [];
|
|
198
|
+
if (this._required)
|
|
199
|
+
modifiers.push("R");
|
|
200
|
+
if (this._default !== undefined)
|
|
201
|
+
modifiers.push("D");
|
|
202
|
+
if (modifiers.length > 0) {
|
|
203
|
+
return `${this.constructor.name}[${modifiers.join("")}]`;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
return this.constructor.name;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
169
209
|
/**
|
|
170
210
|
* Checks to see if a value is present. (Not null or undefined)
|
|
171
211
|
* @param value The value to check the presence of.
|
|
@@ -198,8 +238,9 @@
|
|
|
198
238
|
}
|
|
199
239
|
|
|
200
240
|
class AnySchema extends BaseSchema {
|
|
201
|
-
|
|
202
|
-
|
|
241
|
+
get type() { return "any"; }
|
|
242
|
+
_validate(source, pass) {
|
|
243
|
+
return source;
|
|
203
244
|
}
|
|
204
245
|
convert(value, pass) {
|
|
205
246
|
return value;
|
|
@@ -207,21 +248,36 @@
|
|
|
207
248
|
getJsonSchema() {
|
|
208
249
|
return { description: this._getJsonSchemaDescription() };
|
|
209
250
|
}
|
|
251
|
+
clone() {
|
|
252
|
+
return new AnySchema(this._required, this._default, this._additionalValidationPasses);
|
|
253
|
+
}
|
|
210
254
|
}
|
|
211
255
|
|
|
212
256
|
class ArraySchema extends BaseSchema {
|
|
213
257
|
subschema;
|
|
214
|
-
constructor(subschema, required, defaultValue) {
|
|
215
|
-
super(
|
|
258
|
+
constructor(subschema, required, defaultValue, additionalValidationPasses) {
|
|
259
|
+
super(required, defaultValue, additionalValidationPasses);
|
|
216
260
|
this.subschema = subschema;
|
|
217
261
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
262
|
+
get type() { return "array"; }
|
|
263
|
+
// public validate(source: SourceValue<ArraySource<Subschema>, Required, Default>, pass?: ValidationPass):
|
|
264
|
+
// ModelValue<ArraySource<Subschema>, ArrayModel<Subschema>, Required, Default> {
|
|
265
|
+
// pass = this._ensurePass(source, pass);
|
|
266
|
+
// const result: any = super.validate(source, pass);
|
|
267
|
+
// if (result !== undefined) {
|
|
268
|
+
// for (const key in result) {
|
|
269
|
+
// const nestedValue = result[key];
|
|
270
|
+
// result[key] = this.subschema.validate(result[key], pass.next([...pass.path, key], this.subschema, nestedValue));
|
|
271
|
+
// }
|
|
272
|
+
// }
|
|
273
|
+
// return result;
|
|
274
|
+
// }
|
|
275
|
+
_validate(source, pass) {
|
|
276
|
+
const result = [];
|
|
277
|
+
if (source !== undefined) {
|
|
278
|
+
for (const key in source) {
|
|
279
|
+
const nestedValue = source[key];
|
|
280
|
+
result[key] = this.subschema.validate(source[key], pass.next([...pass.path, key], this.subschema, nestedValue));
|
|
225
281
|
}
|
|
226
282
|
}
|
|
227
283
|
return result;
|
|
@@ -244,11 +300,18 @@
|
|
|
244
300
|
items: this.subschema.getJsonSchema()
|
|
245
301
|
};
|
|
246
302
|
}
|
|
303
|
+
clone() {
|
|
304
|
+
return new ArraySchema(this.subschema.clone(), this._required, this._default, this._additionalValidationPasses);
|
|
305
|
+
}
|
|
306
|
+
toString(level = 0) {
|
|
307
|
+
return `${super.toString(level)}(${this.subschema.toString(level)}[])`;
|
|
308
|
+
}
|
|
247
309
|
}
|
|
248
310
|
|
|
249
311
|
class BooleanSchema extends BaseSchema {
|
|
250
|
-
|
|
251
|
-
|
|
312
|
+
get type() { return "boolean"; }
|
|
313
|
+
_validate(source, pass) {
|
|
314
|
+
return source;
|
|
252
315
|
}
|
|
253
316
|
convert(value, pass) {
|
|
254
317
|
if (typeof value === "number") {
|
|
@@ -269,6 +332,9 @@
|
|
|
269
332
|
throw pass.getError(`Unable to convert ${BaseSchema.getType(value)} to boolean.`);
|
|
270
333
|
}
|
|
271
334
|
}
|
|
335
|
+
clone() {
|
|
336
|
+
return new BooleanSchema(this._required, this._default, this._additionalValidationPasses);
|
|
337
|
+
}
|
|
272
338
|
getJsonSchema() {
|
|
273
339
|
return {
|
|
274
340
|
type: "boolean",
|
|
@@ -279,8 +345,9 @@
|
|
|
279
345
|
|
|
280
346
|
const StandardDate = globalThis.Date;
|
|
281
347
|
class DateSchema extends BaseSchema {
|
|
282
|
-
|
|
283
|
-
|
|
348
|
+
get type() { return "Date"; }
|
|
349
|
+
_validate(source, pass) {
|
|
350
|
+
return source;
|
|
284
351
|
}
|
|
285
352
|
convert(value, pass) {
|
|
286
353
|
if (typeof value === "string") {
|
|
@@ -327,6 +394,9 @@
|
|
|
327
394
|
return model;
|
|
328
395
|
}, "afterAll");
|
|
329
396
|
}
|
|
397
|
+
clone() {
|
|
398
|
+
return new DateSchema(this._required, this._default, this._additionalValidationPasses);
|
|
399
|
+
}
|
|
330
400
|
getJsonSchema() {
|
|
331
401
|
return {
|
|
332
402
|
type: "string",
|
|
@@ -337,13 +407,25 @@
|
|
|
337
407
|
|
|
338
408
|
class DynamicObjectSchema extends BaseSchema {
|
|
339
409
|
subschema;
|
|
340
|
-
constructor(subschema, required, defaultValue) {
|
|
341
|
-
super(
|
|
410
|
+
constructor(subschema, required, defaultValue, additionalValidationPasses) {
|
|
411
|
+
super(required, defaultValue, additionalValidationPasses);
|
|
342
412
|
this.subschema = subschema;
|
|
343
413
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
414
|
+
get type() { return "object"; }
|
|
415
|
+
// public validate(source: SourceValue<DynamicObjectSource<Subschema>, Required, Default>, pass?: ValidationPass):
|
|
416
|
+
// ModelValue<DynamicObjectSource<Subschema>, DynamicObjectModel<Subschema>, Required, Default> {
|
|
417
|
+
// pass = this._ensurePass(source, pass);
|
|
418
|
+
// const result: any = super.validate(source, pass);
|
|
419
|
+
// if (result !== undefined) {
|
|
420
|
+
// for (const key in result) {
|
|
421
|
+
// const nestedValue = result[key];
|
|
422
|
+
// result[key] = this.subschema.validate(result[key], pass.next([...pass.path, key], this.subschema, nestedValue));
|
|
423
|
+
// }
|
|
424
|
+
// }
|
|
425
|
+
// return result;
|
|
426
|
+
// }
|
|
427
|
+
_validate(source, pass) {
|
|
428
|
+
const result = source;
|
|
347
429
|
if (result !== undefined) {
|
|
348
430
|
for (const key in result) {
|
|
349
431
|
const nestedValue = result[key];
|
|
@@ -367,23 +449,36 @@
|
|
|
367
449
|
additionalProperties: this.subschema.getJsonSchema()
|
|
368
450
|
};
|
|
369
451
|
}
|
|
452
|
+
clone() {
|
|
453
|
+
return new DynamicObjectSchema(this.subschema.clone(), this._required, this._default, this._additionalValidationPasses);
|
|
454
|
+
}
|
|
455
|
+
toString(level = 0) {
|
|
456
|
+
const indent = " ";
|
|
457
|
+
const prefix = indent.repeat(level);
|
|
458
|
+
return `${super.toString(level)}({\n${prefix}${indent}[string]: ${this.subschema.toString(level)}\n${prefix}})`;
|
|
459
|
+
}
|
|
370
460
|
}
|
|
371
461
|
|
|
372
462
|
class EnumerationSchema extends BaseSchema {
|
|
373
463
|
members;
|
|
374
|
-
constructor(members, required, defaultValue) {
|
|
375
|
-
super(
|
|
464
|
+
constructor(members, required, defaultValue, additionalValidationPasses) {
|
|
465
|
+
super(required, defaultValue, additionalValidationPasses);
|
|
376
466
|
this.members = members;
|
|
377
467
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const result =
|
|
381
|
-
|
|
468
|
+
get type() { return "string"; }
|
|
469
|
+
_validate(source, pass) {
|
|
470
|
+
const result = source;
|
|
471
|
+
if (this._required) {
|
|
472
|
+
pass.assert(this.members.includes(result), `"${result}" is not a valid enumeration value (Expected: ${this.members.join(", ")}).`);
|
|
473
|
+
}
|
|
382
474
|
return result;
|
|
383
475
|
}
|
|
384
476
|
convert(value, pass) {
|
|
385
477
|
return value;
|
|
386
478
|
}
|
|
479
|
+
clone() {
|
|
480
|
+
return new EnumerationSchema([...this.members], this._required, this._default, this._additionalValidationPasses);
|
|
481
|
+
}
|
|
387
482
|
getJsonSchema() {
|
|
388
483
|
return {
|
|
389
484
|
type: "string",
|
|
@@ -394,8 +489,9 @@
|
|
|
394
489
|
}
|
|
395
490
|
|
|
396
491
|
class NumberSchema extends BaseSchema {
|
|
397
|
-
|
|
398
|
-
|
|
492
|
+
get type() { return "number"; }
|
|
493
|
+
_validate(source, pass) {
|
|
494
|
+
return source;
|
|
399
495
|
}
|
|
400
496
|
convert(value, pass) {
|
|
401
497
|
if (typeof value === "bigint") {
|
|
@@ -438,6 +534,9 @@
|
|
|
438
534
|
return model;
|
|
439
535
|
}, "afterAll");
|
|
440
536
|
}
|
|
537
|
+
clone() {
|
|
538
|
+
return new NumberSchema(this._required, this._default, this._additionalValidationPasses);
|
|
539
|
+
}
|
|
441
540
|
getJsonSchema() {
|
|
442
541
|
return {
|
|
443
542
|
type: "number",
|
|
@@ -448,23 +547,26 @@
|
|
|
448
547
|
|
|
449
548
|
class ObjectSchema extends BaseSchema {
|
|
450
549
|
subschema;
|
|
451
|
-
constructor(subschema, required, defaultValue) {
|
|
452
|
-
super(
|
|
550
|
+
constructor(subschema, required, defaultValue, additionalValidationPasses) {
|
|
551
|
+
super(required, defaultValue, additionalValidationPasses);
|
|
453
552
|
this.subschema = subschema;
|
|
454
553
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const
|
|
458
|
-
|
|
554
|
+
get type() { return "object"; }
|
|
555
|
+
_validate(source, pass) {
|
|
556
|
+
const input = source;
|
|
557
|
+
let output = input;
|
|
558
|
+
if (typeof input === "object" && input !== null) {
|
|
559
|
+
output = {};
|
|
459
560
|
for (const key in this.subschema) {
|
|
460
561
|
const nestedSchema = this.subschema[key];
|
|
461
|
-
const nestedValue =
|
|
462
|
-
|
|
562
|
+
const nestedValue = input[key];
|
|
563
|
+
output[key] = this.subschema[key].validate(input[key], pass.next([...pass.path, key], nestedSchema, nestedValue));
|
|
463
564
|
}
|
|
464
565
|
}
|
|
465
|
-
return
|
|
566
|
+
return output;
|
|
466
567
|
}
|
|
467
568
|
convert(value, pass) {
|
|
569
|
+
pass.assert(typeof value === "object", `Unable to convert ${ObjectSchema.getType(value)} to object.`);
|
|
468
570
|
const model = {};
|
|
469
571
|
for (const key in this.subschema) {
|
|
470
572
|
const nestedSchema = this.subschema[key];
|
|
@@ -499,25 +601,48 @@
|
|
|
499
601
|
properties
|
|
500
602
|
};
|
|
501
603
|
}
|
|
604
|
+
clone() {
|
|
605
|
+
const subschema = {};
|
|
606
|
+
for (const key in this.subschema) {
|
|
607
|
+
subschema[key] = this.subschema[key].clone();
|
|
608
|
+
}
|
|
609
|
+
return new ObjectSchema(subschema, this._required, this._default, this._additionalValidationPasses);
|
|
610
|
+
}
|
|
611
|
+
toString(level = 0) {
|
|
612
|
+
const indent = " ";
|
|
613
|
+
const prefix = indent.repeat(level);
|
|
614
|
+
const pieces = [];
|
|
615
|
+
pieces.push(`${super.toString()}({\n`);
|
|
616
|
+
for (const schemaKey in this.subschema) {
|
|
617
|
+
const subschema = this.subschema[schemaKey];
|
|
618
|
+
pieces.push(`${prefix}${indent}${schemaKey}: ${subschema.toString(level + 1)}\n`);
|
|
619
|
+
}
|
|
620
|
+
pieces.push(`${prefix}})`);
|
|
621
|
+
return pieces.join("");
|
|
622
|
+
}
|
|
502
623
|
}
|
|
503
624
|
|
|
504
625
|
class OrSetSchema extends BaseSchema {
|
|
505
626
|
schemas;
|
|
506
|
-
constructor(schemas, required, defaultValue) {
|
|
507
|
-
super(
|
|
627
|
+
constructor(schemas, required, defaultValue, additionalValidationPasses) {
|
|
628
|
+
super(required, defaultValue, additionalValidationPasses);
|
|
508
629
|
this.schemas = schemas;
|
|
509
630
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
631
|
+
get type() {
|
|
632
|
+
return "string";
|
|
633
|
+
}
|
|
634
|
+
_validate(source, pass) {
|
|
635
|
+
let result = source;
|
|
513
636
|
if (result !== undefined) {
|
|
514
637
|
let done = false;
|
|
515
638
|
const failureMessages = [];
|
|
516
639
|
for (let i = 0; i < this.schemas.length && !done; i++) {
|
|
517
640
|
const schema = this.schemas[i];
|
|
518
641
|
try {
|
|
519
|
-
result
|
|
520
|
-
|
|
642
|
+
if (BaseSchema.getType(result) === schema.type) {
|
|
643
|
+
result = schema.validate(result, pass);
|
|
644
|
+
done = true;
|
|
645
|
+
}
|
|
521
646
|
}
|
|
522
647
|
catch (error) {
|
|
523
648
|
if (error instanceof Error) {
|
|
@@ -526,28 +651,36 @@
|
|
|
526
651
|
else {
|
|
527
652
|
failureMessages.push(`Schema #${i + 1}: ${String(error)}`);
|
|
528
653
|
}
|
|
529
|
-
pass.assert(failureMessages.length !== this.schemas.length, `Supplied value didn't match any schemas in or-set.\n${failureMessages.join("\n")}`);
|
|
530
654
|
}
|
|
531
655
|
}
|
|
656
|
+
if (!done) {
|
|
657
|
+
failureMessages.push(`Conversions for schemas in an OrSet are disabled.`);
|
|
658
|
+
}
|
|
659
|
+
pass.assert(failureMessages.length === 0, `Provided value (${BaseSchema.getType(result)}) matched no schemas ` +
|
|
660
|
+
`(${this.schemas.map((schema) => schema.type).join(", ")}).\n${failureMessages.join("\n")}`);
|
|
532
661
|
}
|
|
533
662
|
return result;
|
|
534
663
|
}
|
|
535
664
|
convert(value, pass) {
|
|
536
665
|
return value;
|
|
537
666
|
}
|
|
667
|
+
clone() {
|
|
668
|
+
return new OrSetSchema(this.schemas.map((schema) => schema.clone()), this._required, this._default, this._additionalValidationPasses);
|
|
669
|
+
}
|
|
538
670
|
getJsonSchema() {
|
|
539
671
|
return { oneOf: this.schemas.map((schema) => schema.getJsonSchema()) };
|
|
540
672
|
}
|
|
541
673
|
}
|
|
542
674
|
|
|
543
675
|
class StringSchema extends BaseSchema {
|
|
544
|
-
|
|
545
|
-
super("string", required, defaultValue, additionalValidationPasses);
|
|
546
|
-
}
|
|
676
|
+
get type() { return "string"; }
|
|
547
677
|
// Not sure if these will be needed with constructor option. If they are required, it adds a lot of boilerplate to all of the Schema classes.
|
|
548
678
|
// public required() { return new StringSchema(true, this._default, this._additionalValidationPasses); }
|
|
549
679
|
// public optional() { return new StringSchema(false, this._default, this._additionalValidationPasses); }
|
|
550
680
|
// public default<Default extends DefaultValue<StringSource>>(defaultValue: Default) { return new StringSchema(this._required, defaultValue, this._additionalValidationPasses); }
|
|
681
|
+
_validate(source, pass) {
|
|
682
|
+
return source;
|
|
683
|
+
}
|
|
551
684
|
convert(value, pass) {
|
|
552
685
|
if (value instanceof Date) {
|
|
553
686
|
return value.toISOString();
|
|
@@ -560,20 +693,46 @@
|
|
|
560
693
|
}
|
|
561
694
|
return value.toString();
|
|
562
695
|
}
|
|
696
|
+
/**
|
|
697
|
+
* Trims strings validated by this schema.
|
|
698
|
+
* @see {@link String.trim}
|
|
699
|
+
*/
|
|
700
|
+
trim() {
|
|
701
|
+
return this.custom((model) => model.trim());
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Makes strings validated by this schema lowercase.
|
|
705
|
+
*/
|
|
706
|
+
lower() {
|
|
707
|
+
return this.custom((model) => model.toLowerCase());
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Makes strings validated by this schema uppercase.
|
|
711
|
+
*/
|
|
712
|
+
upper() {
|
|
713
|
+
return this.custom((model) => model.toUpperCase());
|
|
714
|
+
}
|
|
563
715
|
length(minimum, maximum, messageA, messageB) {
|
|
564
716
|
return this.custom((model, pass) => {
|
|
565
717
|
messageB = messageB === undefined ? messageA : messageB;
|
|
566
|
-
pass.assert(model.length
|
|
567
|
-
pass.assert(model.length
|
|
718
|
+
pass.assert(model.length >= minimum, messageA === undefined ? `String "${model}: failed minimum length check. (${minimum})` : messageA);
|
|
719
|
+
pass.assert(model.length <= maximum, messageB === undefined ? `String "${model}: failed maximum length check. (${maximum})` : messageB);
|
|
568
720
|
return model;
|
|
569
721
|
}, "afterAll");
|
|
570
722
|
}
|
|
723
|
+
/**
|
|
724
|
+
* Ensures a string validated by this schema matches a given regular expression.
|
|
725
|
+
* With an optional custom error message.
|
|
726
|
+
*/
|
|
571
727
|
regex(expression, message) {
|
|
572
728
|
return this.custom((model, pass) => {
|
|
573
729
|
pass.assert(expression.test(model), message === undefined ? `String "${model}" failed regular expression check. (${expression})` : message);
|
|
574
730
|
return model;
|
|
575
731
|
}, "afterAll");
|
|
576
732
|
}
|
|
733
|
+
clone() {
|
|
734
|
+
return new StringSchema(this._required, this._default, this._additionalValidationPasses);
|
|
735
|
+
}
|
|
577
736
|
getJsonSchema() {
|
|
578
737
|
return {
|
|
579
738
|
type: "string",
|
|
@@ -625,9 +784,26 @@
|
|
|
625
784
|
}
|
|
626
785
|
Schema.OrSet = OrSet;
|
|
627
786
|
function Members(...members) {
|
|
787
|
+
/*
|
|
788
|
+
* HACK START: The hermes JS engine doesn't use globalThis.Array when interpreting `...members`
|
|
789
|
+
* It uses `Array`, which is already defined in this namespace.
|
|
790
|
+
*/
|
|
791
|
+
if (!globalThis.Array.isArray(members)) {
|
|
792
|
+
const validArrayEntries = globalThis.Object.entries(members).filter(([key]) => !isNaN(key));
|
|
793
|
+
members = validArrayEntries.map(([_, value]) => value);
|
|
794
|
+
}
|
|
795
|
+
/* HACK END */
|
|
628
796
|
return { $members: members };
|
|
629
797
|
}
|
|
630
798
|
Schema.Members = Members;
|
|
799
|
+
function Keys(object) {
|
|
800
|
+
return globalThis.Object.keys(object);
|
|
801
|
+
}
|
|
802
|
+
Schema.Keys = Keys;
|
|
803
|
+
function Values(object) {
|
|
804
|
+
return globalThis.Object.values(object);
|
|
805
|
+
}
|
|
806
|
+
Schema.Values = Values;
|
|
631
807
|
})(exports.Schema || (exports.Schema = {}));
|
|
632
808
|
|
|
633
809
|
exports.$ = exports.Schema;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { DefaultValue } from "../typing/toolbox";
|
|
2
1
|
import { ValidationPass } from "../error/ValidationPass";
|
|
2
|
+
import { DefaultValue, ModelValue } from "../typing/toolbox";
|
|
3
3
|
import { BaseSchema } from "./BaseSchema";
|
|
4
4
|
export declare class AnySchema<Required extends boolean, Default extends DefaultValue<any>> extends BaseSchema<any, any, Required, Default> {
|
|
5
|
-
|
|
5
|
+
get type(): string;
|
|
6
|
+
protected _validate(source: ModelValue<any, any, Required, Default>, pass: ValidationPass): ModelValue<any, any, Required, Default>;
|
|
6
7
|
convert(value: any, pass: ValidationPass): any;
|
|
7
8
|
getJsonSchema(): object;
|
|
9
|
+
clone(): AnySchema<Required, Default>;
|
|
8
10
|
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { ValidationPass } from "../error/ValidationPass";
|
|
2
2
|
import { BaseSchemaAny } from "../typing/extended";
|
|
3
|
-
import type { DefaultValue, ModelValue, SourceValue } from "../typing/toolbox";
|
|
3
|
+
import type { AdditionalValidationPasses, DefaultValue, ModelValue, SourceValue } from "../typing/toolbox";
|
|
4
4
|
import { BaseSchema } from "./BaseSchema";
|
|
5
5
|
export type ArraySource<Subschema extends BaseSchemaAny> = (Subschema extends BaseSchema<infer Source, any, infer Required, infer Default> ? (SourceValue<Source, Required, Default>[]) : never);
|
|
6
6
|
export type ArrayModel<Subschema extends BaseSchemaAny> = (Subschema extends BaseSchema<infer Source, infer Model, infer Required, infer Default> ? (ModelValue<Source, Model, Required, Default>[]) : never);
|
|
7
7
|
export declare class ArraySchema<Subschema extends BaseSchemaAny, Required extends boolean, Default extends DefaultValue<ArraySource<Subschema>>> extends BaseSchema<ArraySource<Subschema>, ArrayModel<Subschema>, Required, Default> {
|
|
8
8
|
readonly subschema: Subschema;
|
|
9
|
-
constructor(subschema: Subschema, required: Required, defaultValue: Default);
|
|
10
|
-
|
|
9
|
+
constructor(subschema: Subschema, required: Required, defaultValue: Default, additionalValidationPasses?: AdditionalValidationPasses<ArraySource<Subschema>, ArrayModel<Subschema>>);
|
|
10
|
+
get type(): string;
|
|
11
|
+
protected _validate(source: ModelValue<ArraySource<Subschema>, ArrayModel<Subschema>, Required, Default>, pass: ValidationPass): ModelValue<ArraySource<Subschema>, ArrayModel<Subschema>, Required, Default>;
|
|
11
12
|
convert(value: ArraySource<Subschema>, pass: ValidationPass): ArrayModel<Subschema>;
|
|
12
13
|
getJsonSchema(): object;
|
|
14
|
+
clone(): ArraySchema<Subschema, Required, Default>;
|
|
15
|
+
toString(level?: number): string;
|
|
13
16
|
}
|
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
import { ValidationPass } from "../error/ValidationPass";
|
|
2
|
+
import { BaseSchemaAny } from "../typing/extended";
|
|
2
3
|
import type { AdditionalValidationPasses, AdditionalValidator, AdditionalValidatorAfterType, AdditionalValidatorBeforeType, DefaultValue, EnsureValidator, ModelValue, SourceValue } from "../typing/toolbox";
|
|
3
4
|
export declare abstract class BaseSchema<Source, Model, Required extends boolean, Default extends DefaultValue<Source>> {
|
|
4
|
-
readonly type: string;
|
|
5
5
|
protected readonly _required: Required;
|
|
6
6
|
protected readonly _default: Default;
|
|
7
7
|
protected readonly _additionalValidationPasses: AdditionalValidationPasses<Source, Model>;
|
|
8
|
-
constructor(
|
|
8
|
+
constructor(required: Required, defaultValue: Default, additionalValidationPasses?: AdditionalValidationPasses<Source, Model>);
|
|
9
|
+
abstract get type(): string;
|
|
9
10
|
validate(source: SourceValue<Source, Required, Default>, pass?: ValidationPass): ModelValue<Source, Model, Required, Default>;
|
|
11
|
+
protected abstract _validate(source: ModelValue<Source, Model, Required, Default>, pass: ValidationPass): ModelValue<Source, Model, Required, Default>;
|
|
10
12
|
abstract convert(value: Source, pass: ValidationPass): Model;
|
|
11
13
|
custom(additionalValidator: AdditionalValidator<SourceValue<Source, Required, Default>>, type: AdditionalValidatorBeforeType): this;
|
|
12
14
|
custom(additionalValidator: AdditionalValidator<Model>, type: AdditionalValidatorAfterType): this;
|
|
13
15
|
custom(additionalValidator: AdditionalValidator<Model>): this;
|
|
16
|
+
/**
|
|
17
|
+
* Ensures a condition is true during the associated schema's validation pass.
|
|
18
|
+
*
|
|
19
|
+
* @note See {@link EnsureValidator EnsureValidator}.
|
|
20
|
+
*
|
|
21
|
+
* @param ensureValidator A validator to ensure a condition is passed.
|
|
22
|
+
* @param message The message to use for a thrown {@link ValidationError ValidationError} if ensureValidator fails.
|
|
23
|
+
* @returns The schema instance for chaining.
|
|
24
|
+
*/
|
|
14
25
|
ensure(ensureValidator: EnsureValidator<Model>, message?: string): this;
|
|
15
26
|
isRequired(): Required;
|
|
16
27
|
hasDefault(): boolean;
|
|
@@ -19,10 +30,22 @@ export declare abstract class BaseSchema<Source, Model, Required extends boolean
|
|
|
19
30
|
};
|
|
20
31
|
getDefault(pass: ValidationPass): Source;
|
|
21
32
|
protected _getValueDisplay(value: Model): string;
|
|
22
|
-
|
|
33
|
+
getJsonSchema(): object;
|
|
23
34
|
protected _getJsonSchemaDescription(): string;
|
|
24
35
|
protected _ensurePass(source: SourceValue<Source, Required, Default>, pass?: ValidationPass): ValidationPass;
|
|
25
36
|
private _executeAdditionalValidator;
|
|
37
|
+
abstract clone(): BaseSchemaAny;
|
|
38
|
+
/**
|
|
39
|
+
* Converts this schema to a informational string representing this schema.
|
|
40
|
+
*
|
|
41
|
+
* @note Details about this schema will be displayed in [square brackets].
|
|
42
|
+
* * [R] Indicates this schema is required.
|
|
43
|
+
* * [D] Indicates this schema has a default value.
|
|
44
|
+
*
|
|
45
|
+
* @param level The nesting level. You needn't specify this externally.
|
|
46
|
+
* @returns A string representation of this schema.
|
|
47
|
+
*/
|
|
48
|
+
toString(level?: number): string;
|
|
26
49
|
/**
|
|
27
50
|
* Checks to see if a value is present. (Not null or undefined)
|
|
28
51
|
* @param value The value to check the presence of.
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { DefaultValue } from "../typing/toolbox";
|
|
2
1
|
import { ValidationPass } from "../error/ValidationPass";
|
|
2
|
+
import { DefaultValue, ModelValue } from "../typing/toolbox";
|
|
3
3
|
import { BaseSchema } from "./BaseSchema";
|
|
4
4
|
export type BooleanSource = boolean | number | string | null | undefined;
|
|
5
5
|
export declare class BooleanSchema<Required extends boolean, Default extends DefaultValue<BooleanSource>> extends BaseSchema<BooleanSource, boolean, Required, Default> {
|
|
6
|
-
|
|
6
|
+
get type(): string;
|
|
7
|
+
protected _validate(source: ModelValue<BooleanSource, boolean, Required, Default>, pass: ValidationPass): ModelValue<BooleanSource, boolean, Required, Default>;
|
|
7
8
|
convert(value: BooleanSource, pass: ValidationPass): boolean;
|
|
9
|
+
clone(): BooleanSchema<Required, Default>;
|
|
8
10
|
getJsonSchema(): object;
|
|
9
11
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { DefaultValue } from "../typing/toolbox";
|
|
2
1
|
import { ValidationPass } from "../error/ValidationPass";
|
|
2
|
+
import { DefaultValue, ModelValue } from "../typing/toolbox";
|
|
3
3
|
import { BaseSchema } from "./BaseSchema";
|
|
4
4
|
type StandardDate = globalThis.Date;
|
|
5
5
|
declare const StandardDate: DateConstructor;
|
|
6
6
|
export type DateSource = string | number | StandardDate;
|
|
7
7
|
export declare class DateSchema<Required extends boolean, Default extends DefaultValue<DateSource>> extends BaseSchema<DateSource, StandardDate, Required, Default> {
|
|
8
|
-
|
|
8
|
+
get type(): string;
|
|
9
|
+
protected _validate(source: ModelValue<DateSource, Date, Required, Default>, pass: ValidationPass): ModelValue<DateSource, Date, Required, Default>;
|
|
9
10
|
convert(value: DateSource, pass: ValidationPass): StandardDate;
|
|
10
11
|
before(date: Date, message?: string): this;
|
|
11
12
|
after(date: Date, message?: string): this;
|
|
@@ -17,6 +18,7 @@ export declare class DateSchema<Required extends boolean, Default extends Defaul
|
|
|
17
18
|
* @param duration milliseconds
|
|
18
19
|
*/
|
|
19
20
|
lessThanAgo(duration: number, message?: string): this;
|
|
21
|
+
clone(): DateSchema<Required, Default>;
|
|
20
22
|
getJsonSchema(): object;
|
|
21
23
|
}
|
|
22
24
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { DefaultValue, ModelValue, SourceValue } from "../typing/toolbox";
|
|
2
1
|
import { ValidationPass } from "../error/ValidationPass";
|
|
3
2
|
import { BaseSchemaAny } from "../typing/extended";
|
|
3
|
+
import type { AdditionalValidationPasses, DefaultValue, ModelValue, SourceValue } from "../typing/toolbox";
|
|
4
4
|
import { BaseSchema } from "./BaseSchema";
|
|
5
5
|
export type DynamicObjectSource<Subschema extends BaseSchemaAny> = ({
|
|
6
6
|
[Key: string]: (Subschema extends BaseSchema<infer Source, any, infer Required, infer Default> ? (SourceValue<Source, Required, Default>) : never);
|
|
@@ -10,8 +10,11 @@ export type DynamicObjectModel<Subschema extends BaseSchemaAny> = ({
|
|
|
10
10
|
});
|
|
11
11
|
export declare class DynamicObjectSchema<Subschema extends BaseSchemaAny, Required extends boolean, Default extends DefaultValue<DynamicObjectSource<Subschema>>> extends BaseSchema<DynamicObjectSource<Subschema>, DynamicObjectModel<Subschema>, Required, Default> {
|
|
12
12
|
readonly subschema: Subschema;
|
|
13
|
-
constructor(subschema: Subschema, required: Required, defaultValue: Default);
|
|
14
|
-
|
|
13
|
+
constructor(subschema: Subschema, required: Required, defaultValue: Default, additionalValidationPasses?: AdditionalValidationPasses<DynamicObjectSource<Subschema>, DynamicObjectModel<Subschema>>);
|
|
14
|
+
get type(): string;
|
|
15
|
+
protected _validate(source: ModelValue<DynamicObjectSource<Subschema>, DynamicObjectModel<Subschema>, Required, Default>, pass: ValidationPass): ModelValue<DynamicObjectSource<Subschema>, DynamicObjectModel<Subschema>, Required, Default>;
|
|
15
16
|
convert(source: DynamicObjectSource<Subschema>, pass: ValidationPass): DynamicObjectModel<Subschema>;
|
|
16
17
|
getJsonSchema(): object;
|
|
18
|
+
clone(): DynamicObjectSchema<Subschema, Required, Default>;
|
|
19
|
+
toString(level?: number): string;
|
|
17
20
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { DefaultValue, ModelValue, SourceValue } from "../typing/toolbox";
|
|
2
1
|
import { ValidationPass } from "../error/ValidationPass";
|
|
2
|
+
import { AdditionalValidationPasses, DefaultValue, ModelValue } from "../typing/toolbox";
|
|
3
3
|
import { BaseSchema } from "./BaseSchema";
|
|
4
|
-
export declare class EnumerationSchema<Members extends string
|
|
5
|
-
readonly members: Members;
|
|
6
|
-
constructor(members: Members, required: Required, defaultValue: Default);
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
export declare class EnumerationSchema<Members extends string, Required extends boolean, Default extends DefaultValue<Members>> extends BaseSchema<Members, Members, Required, Default> {
|
|
5
|
+
readonly members: Members[];
|
|
6
|
+
constructor(members: Members[], required: Required, defaultValue: Default, additionalValidationPasses?: AdditionalValidationPasses<Members, Members>);
|
|
7
|
+
get type(): string;
|
|
8
|
+
protected _validate(source: ModelValue<Members, Members, Required, Default>, pass: ValidationPass): ModelValue<Members, Members, Required, Default>;
|
|
9
|
+
convert(value: Members, pass: ValidationPass): Members;
|
|
10
|
+
clone(): EnumerationSchema<Members, Required, Default>;
|
|
9
11
|
getJsonSchema(): object;
|
|
10
12
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { DefaultValue } from "../typing/toolbox";
|
|
2
1
|
import { ValidationPass } from "../error/ValidationPass";
|
|
2
|
+
import { DefaultValue, ModelValue } from "../typing/toolbox";
|
|
3
3
|
import { BaseSchema } from "./BaseSchema";
|
|
4
4
|
export type NumberSource = number | bigint | string | boolean | null | undefined | Date;
|
|
5
5
|
export declare class NumberSchema<Required extends boolean, Default extends DefaultValue<NumberSource>> extends BaseSchema<NumberSource, number, Required, Default> {
|
|
6
|
-
|
|
6
|
+
get type(): string;
|
|
7
|
+
protected _validate(source: ModelValue<NumberSource, number, Required, Default>, pass: ValidationPass): ModelValue<NumberSource, number, Required, Default>;
|
|
7
8
|
convert(value: NumberSource, pass: ValidationPass): number;
|
|
8
9
|
min(minimum: number, message?: string): this;
|
|
9
10
|
max(maximum: number, message?: string): this;
|
|
@@ -11,5 +12,6 @@ export declare class NumberSchema<Required extends boolean, Default extends Defa
|
|
|
11
12
|
clamp(minimum: number, maximum: number, message: string): this;
|
|
12
13
|
clamp(minimum: number, maximum: number, tooShortMessage: string, tooLongMessage: string): this;
|
|
13
14
|
validNumber(notANumber?: boolean, message?: string): this;
|
|
15
|
+
clone(): NumberSchema<Required, Default>;
|
|
14
16
|
getJsonSchema(): object;
|
|
15
17
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { DefaultValue, Merge, ModelRequirement, ModelValue, SourceRequirement, SourceValue } from "../typing/toolbox";
|
|
2
1
|
import { ValidationPass } from "../error/ValidationPass";
|
|
3
2
|
import { BaseSchemaAny } from "../typing/extended";
|
|
3
|
+
import type { AdditionalValidationPasses, DefaultValue, Merge, ModelRequirement, ModelValue, SourceRequirement, SourceValue } from "../typing/toolbox";
|
|
4
4
|
import { BaseSchema } from "./BaseSchema";
|
|
5
5
|
export type ObjectSubschema = {
|
|
6
6
|
[Key: string]: BaseSchemaAny;
|
|
@@ -17,9 +17,12 @@ export type ObjectModel<Subschema extends ObjectSubschema> = (Merge<{
|
|
|
17
17
|
}>);
|
|
18
18
|
export declare class ObjectSchema<Subschema extends ObjectSubschema, Required extends boolean, Default extends DefaultValue<ObjectSource<Subschema>>> extends BaseSchema<ObjectSource<Subschema>, ObjectModel<Subschema>, Required, Default> {
|
|
19
19
|
readonly subschema: Subschema;
|
|
20
|
-
constructor(subschema: Subschema, required: Required, defaultValue: Default);
|
|
21
|
-
|
|
20
|
+
constructor(subschema: Subschema, required: Required, defaultValue: Default, additionalValidationPasses?: AdditionalValidationPasses<ObjectSource<Subschema>, ObjectModel<Subschema>>);
|
|
21
|
+
get type(): string;
|
|
22
|
+
protected _validate(source: ModelValue<ObjectSource<Subschema>, ObjectModel<Subschema>, Required, Default>, pass: ValidationPass): ModelValue<ObjectSource<Subschema>, ObjectModel<Subschema>, Required, Default>;
|
|
22
23
|
convert(value: ObjectSource<Subschema>, pass: ValidationPass): ObjectModel<Subschema>;
|
|
23
24
|
extend<ExtensionSubschema extends ObjectSubschema, ExtensionDefault extends DefaultValue<ObjectSource<ExtensionSubschema>>>(schema: ObjectSchema<ExtensionSubschema, Required, ExtensionDefault>): ObjectSchema<Subschema & ExtensionSubschema, Required, any>;
|
|
24
25
|
getJsonSchema(): object;
|
|
26
|
+
clone(): ObjectSchema<Subschema, Required, Default>;
|
|
27
|
+
toString(level?: number): string;
|
|
25
28
|
}
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { ValidationPass } from "../error/ValidationPass";
|
|
2
2
|
import { BaseSchemaAny } from "../typing/extended";
|
|
3
|
-
import { DefaultValue, ModelValue, SourceValue } from "../typing/toolbox";
|
|
3
|
+
import { AdditionalValidationPasses, DefaultValue, ModelValue, SourceValue } from "../typing/toolbox";
|
|
4
4
|
import { BaseSchema } from "./BaseSchema";
|
|
5
|
-
export type OrSetSchemaSource<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
validate(source: SourceValue<OrSetSchemaSource<MemberSchemas>, Required, Default>, pass?: ValidationPass): ModelValue<OrSetSchemaSource<MemberSchemas>, OrSetSchemaModel<MemberSchemas>, Required, Default>;
|
|
15
|
-
convert(value: OrSetSchemaSource<MemberSchemas>, pass: ValidationPass): OrSetSchemaModel<MemberSchemas>;
|
|
5
|
+
export type OrSetSchemaSource<MemberSchema extends BaseSchemaAny> = (MemberSchema extends BaseSchema<infer Source, any, infer Required, infer Default> ? SourceValue<Source, Required, Default> : never);
|
|
6
|
+
export type OrSetSchemaModel<MemberSchema extends BaseSchemaAny> = (MemberSchema extends BaseSchema<infer Source, infer Model, infer Required, infer Default> ? ModelValue<Source, Model, Required, Default> : never);
|
|
7
|
+
export declare class OrSetSchema<MemberSchema extends BaseSchemaAny, Required extends boolean, Default extends DefaultValue<OrSetSchemaSource<MemberSchema>>> extends BaseSchema<OrSetSchemaSource<MemberSchema>, OrSetSchemaModel<MemberSchema>, Required, Default> {
|
|
8
|
+
readonly schemas: MemberSchema[];
|
|
9
|
+
constructor(schemas: MemberSchema[], required: Required, defaultValue: Default, additionalValidationPasses?: AdditionalValidationPasses<OrSetSchemaSource<MemberSchema>, OrSetSchemaModel<MemberSchema>>);
|
|
10
|
+
get type(): string;
|
|
11
|
+
protected _validate(source: ModelValue<OrSetSchemaSource<MemberSchema>, OrSetSchemaModel<MemberSchema>, Required, Default>, pass: ValidationPass): ModelValue<OrSetSchemaSource<MemberSchema>, OrSetSchemaModel<MemberSchema>, Required, Default>;
|
|
12
|
+
convert(value: OrSetSchemaSource<MemberSchema>, pass: ValidationPass): OrSetSchemaModel<MemberSchema>;
|
|
13
|
+
clone(): OrSetSchema<MemberSchema, Required, Default>;
|
|
16
14
|
getJsonSchema(): object;
|
|
17
15
|
}
|
|
@@ -1,13 +1,43 @@
|
|
|
1
1
|
import { ValidationPass } from "../error/ValidationPass";
|
|
2
|
-
import {
|
|
2
|
+
import { DefaultValue, ModelValue } from "../typing/toolbox";
|
|
3
3
|
import { BaseSchema } from "./BaseSchema";
|
|
4
4
|
export type StringSource = string | number | boolean | null | undefined | Date;
|
|
5
5
|
export declare class StringSchema<Required extends boolean, Default extends DefaultValue<StringSource>> extends BaseSchema<StringSource, string, Required, Default> {
|
|
6
|
-
|
|
6
|
+
get type(): string;
|
|
7
|
+
protected _validate(source: ModelValue<StringSource, string, Required, Default>, pass: ValidationPass): ModelValue<StringSource, string, Required, Default>;
|
|
7
8
|
convert(value: StringSource, pass: ValidationPass): string;
|
|
9
|
+
/**
|
|
10
|
+
* Trims strings validated by this schema.
|
|
11
|
+
* @see {@link String.trim}
|
|
12
|
+
*/
|
|
13
|
+
trim(): this;
|
|
14
|
+
/**
|
|
15
|
+
* Makes strings validated by this schema lowercase.
|
|
16
|
+
*/
|
|
17
|
+
lower(): this;
|
|
18
|
+
/**
|
|
19
|
+
* Makes strings validated by this schema uppercase.
|
|
20
|
+
*/
|
|
21
|
+
upper(): this;
|
|
22
|
+
/**
|
|
23
|
+
* Ensures that a string validated by this schema has a minimum (inclusive) and maximum (inclusive) length.
|
|
24
|
+
*/
|
|
8
25
|
length(minimum: number, maximum: number): this;
|
|
26
|
+
/**
|
|
27
|
+
* Ensures that a string validated by this schema has a minimum (inclusive) and maximum (inclusive) length.
|
|
28
|
+
* With a custom error message.
|
|
29
|
+
*/
|
|
9
30
|
length(minimum: number, maximum: number, message: string): this;
|
|
31
|
+
/**
|
|
32
|
+
* Ensures that a string validated by this schema has a minimum (inclusive) and maximum (inclusive) length.
|
|
33
|
+
* With custom error messages for too long, and too short.
|
|
34
|
+
*/
|
|
10
35
|
length(minimum: number, maximum: number, tooShortMessage: string, tooLongMessage: string): this;
|
|
36
|
+
/**
|
|
37
|
+
* Ensures a string validated by this schema matches a given regular expression.
|
|
38
|
+
* With an optional custom error message.
|
|
39
|
+
*/
|
|
11
40
|
regex(expression: RegExp, message?: string): this;
|
|
41
|
+
clone(): StringSchema<Required, Default>;
|
|
12
42
|
getJsonSchema(): object;
|
|
13
43
|
}
|
|
@@ -6,9 +6,6 @@ export type SourceValue<Source, Required extends boolean, Default extends Defaul
|
|
|
6
6
|
export type ModelRequirement<Layout extends BaseSchemaAny> = (Layout extends BaseSchema<any, any, infer Required, infer Default> ? (Required extends true ? (true) : (Default extends undefined ? false : true)) : never);
|
|
7
7
|
export type ModelValue<Source, Model, Required extends boolean, Default extends DefaultValue<Source>> = (Required extends true ? Model : Default extends undefined ? Model | undefined : Model);
|
|
8
8
|
export type DefaultValue<Type> = undefined | Type | ((pass: ValidationPass) => Type);
|
|
9
|
-
export type TypedMembers<Members> = {
|
|
10
|
-
$members: Members;
|
|
11
|
-
};
|
|
12
9
|
export type AdditionalValidatorBeforeType = ("beforeAll" | "beforeDefault" | "afterDefault");
|
|
13
10
|
export type AdditionalValidatorAfterType = ("beforeConversion" | "afterConversion" | "afterAll");
|
|
14
11
|
/**
|
|
@@ -21,9 +18,21 @@ export type AdditionalValidatorAfterType = ("beforeConversion" | "afterConversio
|
|
|
21
18
|
* - afterAll: Validator executed after "afterConversion" and as the last task in the validation pipeline.
|
|
22
19
|
*/
|
|
23
20
|
export type AdditionalValidatorType = AdditionalValidatorBeforeType | AdditionalValidatorAfterType;
|
|
21
|
+
/**
|
|
22
|
+
* Represents a custom pass allowing any additional value specificity code, or value augmentations.
|
|
23
|
+
*
|
|
24
|
+
* @note Use "{@link ValidationPass pass}.assert" and/or "throw {@link ValidationPass pass}.getError" to build your value specificity checks.
|
|
25
|
+
*
|
|
26
|
+
* @param {Type} data The data being validated.
|
|
27
|
+
* @param {ValidationPass} pass A reference to the current validation pass. Used to interact with the validation pass.
|
|
28
|
+
* @returns `data` with any desired augmentations.
|
|
29
|
+
*/
|
|
24
30
|
export type AdditionalValidator<Type> = (data: Type, pass: ValidationPass) => Type;
|
|
25
31
|
/**
|
|
26
|
-
* Represents a pass to ensure that your data meets
|
|
32
|
+
* Represents a pass to ensure that your data meets conditions.
|
|
33
|
+
* @param data The data being validated.
|
|
34
|
+
* @param pass A reference to the current validation pass. Used to interact with the validation pass.
|
|
35
|
+
* @returns `true` if your data meets the desired conditions, `false` otherwise.
|
|
27
36
|
*/
|
|
28
37
|
export type EnsureValidator<Type> = (data: Type, pass: ValidationPass) => boolean;
|
|
29
38
|
export type AdditionalValidationPasses<Source, Model> = {
|
|
@@ -34,4 +43,7 @@ export type AdditionalValidationPasses<Source, Model> = {
|
|
|
34
43
|
afterConversion: AdditionalValidator<Model>[];
|
|
35
44
|
afterAll: AdditionalValidator<Model>[];
|
|
36
45
|
};
|
|
37
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Intersects two types.
|
|
48
|
+
*/
|
|
49
|
+
export type Merge<A, B> = (keyof A extends never ? B : keyof B extends never ? A : A & B);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lucania/schema",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "A schema module for compile-time and runtime type checking.",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://github.com/lucania-software/schema#readme",
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@babel/preset-env": "^7.
|
|
33
|
-
"@rollup/plugin-typescript": "^11.1.
|
|
34
|
-
"@types/node": "^20.
|
|
35
|
-
"nodemon": "^3.
|
|
36
|
-
"rollup": "^4.
|
|
37
|
-
"tslib": "^2.6.
|
|
38
|
-
"typescript": "^5.
|
|
32
|
+
"@babel/preset-env": "^7.24.7",
|
|
33
|
+
"@rollup/plugin-typescript": "^11.1.6",
|
|
34
|
+
"@types/node": "^20.14.2",
|
|
35
|
+
"nodemon": "^3.1.3",
|
|
36
|
+
"rollup": "^4.18.0",
|
|
37
|
+
"tslib": "^2.6.3",
|
|
38
|
+
"typescript": "^5.4.5"
|
|
39
39
|
}
|
|
40
40
|
}
|