@lucania/schema 1.0.8 → 2.0.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/README.md +47 -90
- package/build/builder.d.ts +48 -0
- package/build/error/ValidationError.d.ts +5 -0
- package/build/error/ValidationPass.d.ts +17 -0
- package/build/index.d.ts +17 -100
- package/build/index.js +607 -314
- package/build/schema/AnySchema.d.ts +8 -0
- package/build/schema/ArraySchema.d.ts +13 -0
- package/build/schema/BaseSchema.d.ts +32 -0
- package/build/schema/BooleanSchema.d.ts +9 -0
- package/build/schema/DateSchema.d.ts +22 -0
- package/build/schema/DynamicObjectSchema.d.ts +17 -0
- package/build/schema/EnumerationSchema.d.ts +10 -0
- package/build/schema/NumberSchema.d.ts +15 -0
- package/build/schema/ObjectSchema.d.ts +25 -0
- package/build/schema/OrSetSchema.d.ts +17 -0
- package/build/schema/StringSchema.d.ts +13 -0
- package/build/typing/extended.d.ts +3 -0
- package/build/typing/toolbox.d.ts +37 -0
- package/package.json +8 -8
package/build/index.js
CHANGED
|
@@ -1,352 +1,645 @@
|
|
|
1
|
-
(function (factory) {
|
|
2
|
-
typeof
|
|
3
|
-
factory
|
|
4
|
-
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.LucaniaSchema = {}));
|
|
5
|
+
})(this, (function (exports) { 'use strict';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (Number.isNaN(numberValue)) {
|
|
41
|
-
Schema.assert(value.length !== 0, `Failed to convert empty string to number.`);
|
|
42
|
-
throw new Schema.Error(`Failed to convert "${value}" to number.`);
|
|
43
|
-
}
|
|
44
|
-
return numberValue;
|
|
45
|
-
},
|
|
46
|
-
bigint: (value) => BigInt(value),
|
|
47
|
-
any: (value) => JSON.parse(value),
|
|
48
|
-
Date: (value) => new Date(value)
|
|
49
|
-
},
|
|
50
|
-
any: {
|
|
51
|
-
string: (value) => JSON.stringify(value)
|
|
52
|
-
},
|
|
53
|
-
Date: {
|
|
54
|
-
number: (value) => value.getTime(),
|
|
55
|
-
bigint: (value) => BigInt(value.getTime()),
|
|
56
|
-
string: (value) => value.toISOString()
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
const untypedConverters = converters;
|
|
60
|
-
function registerConverter(fromTypeName, toTypeName, converter) {
|
|
61
|
-
if (!(fromTypeName in converters)) {
|
|
62
|
-
untypedConverters[fromTypeName] = [];
|
|
63
|
-
}
|
|
64
|
-
untypedConverters[fromTypeName][toTypeName] = converter;
|
|
65
|
-
}
|
|
66
|
-
Schema.registerConverter = registerConverter;
|
|
67
|
-
function registerPrimitive(typeName) {
|
|
68
|
-
primitives.push(typeName);
|
|
69
|
-
}
|
|
70
|
-
Schema.registerPrimitive = registerPrimitive;
|
|
71
|
-
function isSchemaPrimitive(value) {
|
|
72
|
-
return typeof value === "string" && primitives.includes(value);
|
|
73
|
-
}
|
|
74
|
-
Schema.isSchemaPrimitive = isSchemaPrimitive;
|
|
75
|
-
function isSchemaOrCompound(value) {
|
|
76
|
-
return Array.isArray(value) && value.length === 3 && isSchema(value[0]) && value[1] === "or" && isSchema(value[2]);
|
|
77
|
-
}
|
|
78
|
-
Schema.isSchemaOrCompound = isSchemaOrCompound;
|
|
79
|
-
function isSchemaAndCompound(value) {
|
|
80
|
-
return Array.isArray(value) && value.length === 3 && isSchema(value[0]) && value[1] === "and" && isSchema(value[2]);
|
|
81
|
-
}
|
|
82
|
-
Schema.isSchemaAndCompound = isSchemaAndCompound;
|
|
83
|
-
function isSchemaMeta(value) {
|
|
84
|
-
return typeof value === "object" && value !== null && isSchema(value.type) && typeof value.required === "boolean";
|
|
85
|
-
}
|
|
86
|
-
Schema.isSchemaMeta = isSchemaMeta;
|
|
87
|
-
function isSchemaDynamic(value) {
|
|
88
|
-
return typeof value === "object" && value !== null && isSchema(value.$);
|
|
89
|
-
}
|
|
90
|
-
Schema.isSchemaDynamic = isSchemaDynamic;
|
|
91
|
-
function isSchemaArray(value) {
|
|
92
|
-
return Array.isArray(value) && value.length > 0 && value.every(isSchema);
|
|
93
|
-
}
|
|
94
|
-
Schema.isSchemaArray = isSchemaArray;
|
|
95
|
-
function isSchemaHierarchy(value) {
|
|
96
|
-
return typeof value === "object" && value !== null && Object.values(value).every(isSchema);
|
|
97
|
-
}
|
|
98
|
-
Schema.isSchemaHierarchy = isSchemaHierarchy;
|
|
99
|
-
function isSchema(value) {
|
|
100
|
-
return (isSchemaPrimitive(value) ||
|
|
101
|
-
isSchemaOrCompound(value) ||
|
|
102
|
-
isSchemaAndCompound(value) ||
|
|
103
|
-
isSchemaDynamic(value) ||
|
|
104
|
-
isSchemaMeta(value) ||
|
|
105
|
-
isSchemaArray(value) ||
|
|
106
|
-
isSchemaHierarchy(value));
|
|
107
|
-
}
|
|
108
|
-
Schema.isSchema = isSchema;
|
|
109
|
-
function getType(value) {
|
|
110
|
-
return typeof value === "object" ? value === null ? "null" : value.constructor.name : typeof value;
|
|
111
|
-
}
|
|
112
|
-
Schema.getType = getType;
|
|
113
|
-
function build(schema) {
|
|
114
|
-
return schema;
|
|
115
|
-
}
|
|
116
|
-
Schema.build = build;
|
|
117
|
-
function validate(schema, source) {
|
|
118
|
-
const result = _validate(schema, source, [], schema, source, !isSchemaOrCompound(schema) && !isSchemaAndCompound(schema));
|
|
119
|
-
if (result instanceof ValidationError) {
|
|
120
|
-
throw result;
|
|
7
|
+
class ValidationError extends Error {
|
|
8
|
+
pass;
|
|
9
|
+
constructor(pass, message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.pass = pass;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class ValidationPass {
|
|
16
|
+
originalSchema;
|
|
17
|
+
originalSource;
|
|
18
|
+
_path;
|
|
19
|
+
_schema;
|
|
20
|
+
_source;
|
|
21
|
+
constructor(originalSchema, originalSource) {
|
|
22
|
+
this.originalSchema = originalSchema;
|
|
23
|
+
this.originalSource = originalSource;
|
|
24
|
+
this._path = [];
|
|
25
|
+
this._schema = originalSchema;
|
|
26
|
+
this._source = originalSource;
|
|
27
|
+
}
|
|
28
|
+
get path() { return this._path; }
|
|
29
|
+
get schema() { return this._schema; }
|
|
30
|
+
get source() { return this._source; }
|
|
31
|
+
next(path, schema, source) {
|
|
32
|
+
const nextPass = new ValidationPass(this.originalSchema, this.originalSource);
|
|
33
|
+
nextPass._path = path;
|
|
34
|
+
nextPass._source = source;
|
|
35
|
+
nextPass._schema = schema;
|
|
36
|
+
return nextPass;
|
|
37
|
+
}
|
|
38
|
+
assert(condition, message) {
|
|
39
|
+
if (!condition) {
|
|
40
|
+
throw this.getError(message);
|
|
121
41
|
}
|
|
122
|
-
return result;
|
|
123
42
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
43
|
+
getError(message) {
|
|
44
|
+
if (message === undefined) {
|
|
45
|
+
return this.getError(`Validation failed.`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
return new ValidationError(this, message);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class BaseSchema {
|
|
54
|
+
type;
|
|
55
|
+
_required;
|
|
56
|
+
_default;
|
|
57
|
+
_additionalValidationPasses;
|
|
58
|
+
constructor(type, required, defaultValue, additionalValidationPasses) {
|
|
59
|
+
this.type = type;
|
|
60
|
+
this._required = required;
|
|
61
|
+
this._default = defaultValue;
|
|
62
|
+
this._additionalValidationPasses = additionalValidationPasses === undefined ? {
|
|
63
|
+
beforeAll: [],
|
|
64
|
+
beforeDefault: [],
|
|
65
|
+
afterDefault: [],
|
|
66
|
+
beforeConversion: [],
|
|
67
|
+
afterConversion: [],
|
|
68
|
+
afterAll: []
|
|
69
|
+
} : additionalValidationPasses;
|
|
70
|
+
}
|
|
71
|
+
validate(source, pass) {
|
|
72
|
+
pass = this._ensurePass(source, pass);
|
|
73
|
+
let result = source;
|
|
74
|
+
result = this._executeAdditionalValidator(result, pass, "beforeAll");
|
|
75
|
+
result = this._executeAdditionalValidator(result, pass, "beforeDefault");
|
|
76
|
+
if (!BaseSchema.isPresent(result) && this.hasDefault()) {
|
|
77
|
+
result = this.getDefault(pass);
|
|
78
|
+
}
|
|
79
|
+
result = this._executeAdditionalValidator(result, pass, "afterDefault");
|
|
80
|
+
result = this._executeAdditionalValidator(result, pass, "beforeConversion");
|
|
81
|
+
if (BaseSchema.isPresent(result)) {
|
|
82
|
+
if (BaseSchema.getType(result) !== this.type) {
|
|
83
|
+
result = this.convert(result, pass);
|
|
133
84
|
}
|
|
134
|
-
|
|
135
|
-
|
|
85
|
+
result = this._executeAdditionalValidator(result, pass, "afterConversion");
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
result = this._executeAdditionalValidator(result, pass, "afterConversion");
|
|
89
|
+
if (this._required) {
|
|
90
|
+
throw pass.getError(pass.path.length > 0 ? `Missing required value at "${pass.path.join(".")}".` : "Missing required value.");
|
|
136
91
|
}
|
|
137
|
-
else
|
|
138
|
-
|
|
92
|
+
else {
|
|
93
|
+
result = undefined;
|
|
139
94
|
}
|
|
140
|
-
const errorType = source === undefined || source === null ? "missing" : "incorrectType";
|
|
141
|
-
return new ValidationError(errorType, schema, source, path, originalSchema, originalSource);
|
|
142
95
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (result instanceof ValidationError) {
|
|
146
|
-
return _validate(schema[2], source, path, originalSchema, originalSource, false);
|
|
147
|
-
}
|
|
148
|
-
return result;
|
|
96
|
+
if (this._required || result !== undefined) {
|
|
97
|
+
result = this._executeAdditionalValidator(result, pass, "afterAll");
|
|
149
98
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
custom(additionalValidator, type = "afterAll") {
|
|
102
|
+
this._additionalValidationPasses[type].push(additionalValidator);
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
ensure(ensureValidator, message) {
|
|
106
|
+
this.custom((value, pass) => {
|
|
107
|
+
pass.assert(ensureValidator(value, pass), message === undefined ? `Failed to ensure value.` : message);
|
|
108
|
+
return value;
|
|
109
|
+
});
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
hasDefault() {
|
|
113
|
+
return this._default !== undefined && this._default !== null;
|
|
114
|
+
}
|
|
115
|
+
isDefaultRuntimeEvaluated() {
|
|
116
|
+
return typeof this._default === "function";
|
|
117
|
+
}
|
|
118
|
+
getDefault(pass) {
|
|
119
|
+
if (this.isDefaultRuntimeEvaluated()) {
|
|
120
|
+
return this._default(pass);
|
|
153
121
|
}
|
|
154
|
-
else if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
122
|
+
else if (this.hasDefault()) {
|
|
123
|
+
return this._default;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
throw pass.getError(`Failed to get default. Invalid default value.`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
_getValueDisplay(value) {
|
|
130
|
+
return String(value);
|
|
131
|
+
}
|
|
132
|
+
_getJsonSchemaDescription() {
|
|
133
|
+
const pass = new ValidationPass(this, this._default);
|
|
134
|
+
const descriptionPieces = [];
|
|
135
|
+
descriptionPieces.push(`A ${this._required ? "required" : "optional"} ${this.type}`);
|
|
136
|
+
if (this.hasDefault()) {
|
|
137
|
+
let defaultValue = this.getDefault(pass);
|
|
138
|
+
if (BaseSchema.getType(defaultValue) !== this.type) {
|
|
139
|
+
defaultValue = this.convert(defaultValue, pass);
|
|
167
140
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (!schema.required && source === undefined) {
|
|
171
|
-
return undefined;
|
|
172
|
-
}
|
|
173
|
-
if ("default" in schema) {
|
|
174
|
-
if (typeof schema.default === "function") {
|
|
175
|
-
return _validate(schema.type, schema.default(), path, originalSchema, originalSource);
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
return _validate(schema.type, schema.default, path, originalSchema, originalSource);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return result;
|
|
141
|
+
if (Array.isArray(defaultValue)) {
|
|
142
|
+
defaultValue = `[${defaultValue.join(", ")}]`;
|
|
182
143
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
else if (isSchemaDynamic(schema)) {
|
|
186
|
-
const validated = {};
|
|
187
|
-
for (const key in source) {
|
|
188
|
-
const result = _validate(schema.$, source[key], path, originalSchema, originalSource);
|
|
189
|
-
if (result instanceof ValidationError) {
|
|
190
|
-
return result;
|
|
191
|
-
}
|
|
192
|
-
validated[key] = result;
|
|
144
|
+
if (this.isDefaultRuntimeEvaluated()) {
|
|
145
|
+
descriptionPieces.push(` that defaults to a run-time evaluated value (i.e. ${defaultValue})`);
|
|
193
146
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
else if (isSchemaArray(schema)) {
|
|
197
|
-
if (Array.isArray(source)) {
|
|
198
|
-
[schema] = schema;
|
|
199
|
-
const validated = [];
|
|
200
|
-
for (let i = 0; i < source.length; i++) {
|
|
201
|
-
const result = _validate(schema, source[i], path, originalSchema, originalSource);
|
|
202
|
-
if (result instanceof ValidationError) {
|
|
203
|
-
return result;
|
|
204
|
-
}
|
|
205
|
-
validated[i] = result;
|
|
206
|
-
}
|
|
207
|
-
return validated;
|
|
147
|
+
else {
|
|
148
|
+
descriptionPieces.push(` that defaults to ${defaultValue}`);
|
|
208
149
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
150
|
+
}
|
|
151
|
+
descriptionPieces.push(".");
|
|
152
|
+
return descriptionPieces.join("");
|
|
153
|
+
}
|
|
154
|
+
_ensurePass(source, pass) {
|
|
155
|
+
if (pass === undefined) {
|
|
156
|
+
pass = new ValidationPass(this, source);
|
|
157
|
+
}
|
|
158
|
+
return pass;
|
|
159
|
+
}
|
|
160
|
+
_executeAdditionalValidator(source, pass, type) {
|
|
161
|
+
for (const additionalValidationPass of this._additionalValidationPasses[type]) {
|
|
162
|
+
source = additionalValidationPass(source, pass);
|
|
163
|
+
}
|
|
164
|
+
return source;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Checks to see if a value is present. (Not null or undefined)
|
|
168
|
+
* @param value The value to check the presence of.
|
|
169
|
+
* @returns true if value is not null or undefined, false otherwise.
|
|
170
|
+
*/
|
|
171
|
+
static isPresent(value) {
|
|
172
|
+
return value !== undefined && value !== null;
|
|
173
|
+
}
|
|
174
|
+
static getType(value) {
|
|
175
|
+
if (value === null) {
|
|
176
|
+
return "null";
|
|
177
|
+
}
|
|
178
|
+
const type = typeof value;
|
|
179
|
+
if (type === "object") {
|
|
180
|
+
const constructor = value.constructor.name;
|
|
181
|
+
if (constructor === "Object") {
|
|
182
|
+
return type;
|
|
183
|
+
}
|
|
184
|
+
else if (constructor === "Array") {
|
|
185
|
+
return "array";
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
return constructor;
|
|
223
189
|
}
|
|
224
|
-
const errorType = source === undefined || source === null ? "missing" : "incorrectType";
|
|
225
|
-
return new ValidationError(errorType, schema, source, path, originalSchema, originalSource);
|
|
226
190
|
}
|
|
227
191
|
else {
|
|
228
|
-
return
|
|
192
|
+
return type;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
class AnySchema extends BaseSchema {
|
|
198
|
+
constructor(required, defaultValue) {
|
|
199
|
+
super("any", required, defaultValue);
|
|
200
|
+
}
|
|
201
|
+
convert(value, pass) {
|
|
202
|
+
return value;
|
|
203
|
+
}
|
|
204
|
+
getJsonSchema() {
|
|
205
|
+
return { description: this._getJsonSchemaDescription() };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
class ArraySchema extends BaseSchema {
|
|
210
|
+
subschema;
|
|
211
|
+
constructor(subschema, required, defaultValue) {
|
|
212
|
+
super("array", required, defaultValue);
|
|
213
|
+
this.subschema = subschema;
|
|
214
|
+
}
|
|
215
|
+
validate(source, pass) {
|
|
216
|
+
pass = this._ensurePass(source, pass);
|
|
217
|
+
const result = super.validate(source, pass);
|
|
218
|
+
if (result !== undefined) {
|
|
219
|
+
for (const key in result) {
|
|
220
|
+
const nestedValue = result[key];
|
|
221
|
+
result[key] = this.subschema.validate(result[key], pass.next([...pass.path, key], this.subschema, nestedValue));
|
|
222
|
+
}
|
|
229
223
|
}
|
|
224
|
+
return result;
|
|
230
225
|
}
|
|
231
|
-
|
|
232
|
-
if (
|
|
233
|
-
return
|
|
226
|
+
convert(value, pass) {
|
|
227
|
+
if (Array.isArray(value)) {
|
|
228
|
+
return value;
|
|
234
229
|
}
|
|
235
|
-
else if (
|
|
236
|
-
|
|
237
|
-
return [clone(a), "or", clone(b)];
|
|
230
|
+
else if (typeof value === "string") {
|
|
231
|
+
return [value];
|
|
238
232
|
}
|
|
239
|
-
else
|
|
240
|
-
|
|
241
|
-
return [clone(a), "and", clone(b)];
|
|
233
|
+
else {
|
|
234
|
+
throw pass.getError(`Unable to convert ${BaseSchema.getType(value)} to array.`);
|
|
242
235
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
236
|
+
}
|
|
237
|
+
getJsonSchema() {
|
|
238
|
+
return {
|
|
239
|
+
type: "array",
|
|
240
|
+
description: this._getJsonSchemaDescription(),
|
|
241
|
+
items: this.subschema.getJsonSchema()
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
class BooleanSchema extends BaseSchema {
|
|
247
|
+
constructor(required, defaultValue) {
|
|
248
|
+
super("boolean", required, defaultValue);
|
|
249
|
+
}
|
|
250
|
+
convert(value, pass) {
|
|
251
|
+
if (typeof value === "number") {
|
|
252
|
+
return value !== 0;
|
|
253
|
+
}
|
|
254
|
+
else if (typeof value === "string") {
|
|
255
|
+
if (value === "false" || value === "no" || value === "off") {
|
|
256
|
+
return false;
|
|
250
257
|
}
|
|
251
|
-
|
|
252
|
-
|
|
258
|
+
else {
|
|
259
|
+
return value.length > 0;
|
|
253
260
|
}
|
|
254
|
-
return meta;
|
|
255
261
|
}
|
|
256
|
-
else if (
|
|
257
|
-
return
|
|
262
|
+
else if (value === undefined || value === null) {
|
|
263
|
+
return false;
|
|
258
264
|
}
|
|
259
|
-
else
|
|
260
|
-
|
|
265
|
+
else {
|
|
266
|
+
throw pass.getError(`Unable to convert ${BaseSchema.getType(value)} to boolean.`);
|
|
261
267
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
+
}
|
|
269
|
+
getJsonSchema() {
|
|
270
|
+
return {
|
|
271
|
+
type: "boolean",
|
|
272
|
+
description: this._getJsonSchemaDescription(),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const StandardDate = globalThis.Date;
|
|
278
|
+
class DateSchema extends BaseSchema {
|
|
279
|
+
constructor(required, defaultValue) {
|
|
280
|
+
super("Date", required, defaultValue);
|
|
281
|
+
}
|
|
282
|
+
convert(value, pass) {
|
|
283
|
+
if (typeof value === "string") {
|
|
284
|
+
return value === "now" ? new StandardDate() : new StandardDate(value);
|
|
285
|
+
}
|
|
286
|
+
if (typeof value === "number") {
|
|
287
|
+
return new StandardDate(value);
|
|
268
288
|
}
|
|
269
289
|
else {
|
|
270
|
-
throw
|
|
290
|
+
throw pass.getError(`Unable to convert ${BaseSchema.getType(value)} to Date.`);
|
|
271
291
|
}
|
|
272
292
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
293
|
+
before(date, message) {
|
|
294
|
+
return this.custom((model, pass) => {
|
|
295
|
+
pass.assert(date.getTime() < model.getTime(), message === undefined ?
|
|
296
|
+
`Date "${model.toLocaleString()}" failed check. (Must be before ${date.toLocaleString()})` : message);
|
|
297
|
+
return model;
|
|
298
|
+
}, "afterAll");
|
|
299
|
+
}
|
|
300
|
+
after(date, message) {
|
|
301
|
+
return this.custom((model, pass) => {
|
|
302
|
+
pass.assert(date.getTime() > model.getTime(), message === undefined ?
|
|
303
|
+
`Date "${model.toLocaleString()}" failed check. (Must be after ${date.toLocaleString()})` : message);
|
|
304
|
+
return model;
|
|
305
|
+
}, "afterAll");
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* @param duration milliseconds
|
|
309
|
+
*/
|
|
310
|
+
moreThanAgo(duration, message) {
|
|
311
|
+
return this.custom((model, pass) => {
|
|
312
|
+
pass.assert(Date.now() - model.getTime() > duration, message === undefined ?
|
|
313
|
+
`Date "${model.toLocaleString()}" failed check. (Must be more than ${duration} millisecond(s) ago)` : message);
|
|
314
|
+
return model;
|
|
315
|
+
}, "afterAll");
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* @param duration milliseconds
|
|
319
|
+
*/
|
|
320
|
+
lessThanAgo(duration, message) {
|
|
321
|
+
return this.custom((model, pass) => {
|
|
322
|
+
pass.assert(Date.now() - model.getTime() < duration, message === undefined ?
|
|
323
|
+
`Date "${model.toLocaleString()}" failed check. (Must be less than ${duration} millisecond(s) ago)` : message);
|
|
324
|
+
return model;
|
|
325
|
+
}, "afterAll");
|
|
326
|
+
}
|
|
327
|
+
getJsonSchema() {
|
|
328
|
+
return {
|
|
329
|
+
type: "string",
|
|
330
|
+
description: this._getJsonSchemaDescription(),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
class DynamicObjectSchema extends BaseSchema {
|
|
336
|
+
subschema;
|
|
337
|
+
constructor(subschema, required, defaultValue) {
|
|
338
|
+
super("object", required, defaultValue);
|
|
339
|
+
this.subschema = subschema;
|
|
340
|
+
}
|
|
341
|
+
validate(source, pass) {
|
|
342
|
+
pass = this._ensurePass(source, pass);
|
|
343
|
+
const result = super.validate(source, pass);
|
|
344
|
+
if (result !== undefined) {
|
|
345
|
+
for (const key in result) {
|
|
346
|
+
const nestedValue = result[key];
|
|
347
|
+
result[key] = this.subschema.validate(result[key], pass.next([...pass.path, key], this.subschema, nestedValue));
|
|
323
348
|
}
|
|
324
|
-
return "unfamiliar object";
|
|
325
349
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
350
|
+
return result;
|
|
351
|
+
}
|
|
352
|
+
convert(source, pass) {
|
|
353
|
+
const model = {};
|
|
354
|
+
for (const key in source) {
|
|
355
|
+
const nestedValue = source[key];
|
|
356
|
+
model[key] = this.subschema.convert(nestedValue, pass.next([...pass.path, key], this.subschema, nestedValue));
|
|
357
|
+
}
|
|
358
|
+
return model;
|
|
359
|
+
}
|
|
360
|
+
getJsonSchema() {
|
|
361
|
+
return {
|
|
362
|
+
type: "object",
|
|
363
|
+
description: this._getJsonSchemaDescription(),
|
|
364
|
+
additionalProperties: this.subschema.getJsonSchema()
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
class EnumerationSchema extends BaseSchema {
|
|
370
|
+
members;
|
|
371
|
+
constructor(members, required, defaultValue) {
|
|
372
|
+
super("string", required, defaultValue);
|
|
373
|
+
this.members = members;
|
|
374
|
+
}
|
|
375
|
+
validate(source, pass) {
|
|
376
|
+
pass = this._ensurePass(source, pass);
|
|
377
|
+
const result = super.validate(source, pass);
|
|
378
|
+
pass.assert(this.members.includes(result), `"${result}" is not a valid enumeration value (Expected: ${this.members.join(", ")}).`);
|
|
379
|
+
return result;
|
|
380
|
+
}
|
|
381
|
+
convert(value, pass) {
|
|
382
|
+
return value;
|
|
383
|
+
}
|
|
384
|
+
getJsonSchema() {
|
|
385
|
+
return {
|
|
386
|
+
type: "string",
|
|
387
|
+
description: this._getJsonSchemaDescription(),
|
|
388
|
+
enum: this.members
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
class NumberSchema extends BaseSchema {
|
|
394
|
+
constructor(required, defaultValue) {
|
|
395
|
+
super("number", required, defaultValue);
|
|
396
|
+
}
|
|
397
|
+
convert(value, pass) {
|
|
398
|
+
if (typeof value === "bigint") {
|
|
399
|
+
return globalThis.Number(value);
|
|
400
|
+
}
|
|
401
|
+
if (typeof value === "string") {
|
|
402
|
+
return parseFloat(value);
|
|
403
|
+
}
|
|
404
|
+
else if (typeof value === "boolean") {
|
|
405
|
+
return value ? 1 : 0;
|
|
406
|
+
}
|
|
407
|
+
else if (value === undefined || value === null) {
|
|
408
|
+
return 0;
|
|
409
|
+
}
|
|
410
|
+
else if (value instanceof Date) {
|
|
411
|
+
return value.getTime();
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
throw pass.getError(`Unable to convert ${BaseSchema.getType(value)} to number.`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
min(minimum, message) {
|
|
418
|
+
return this.custom((model, pass) => {
|
|
419
|
+
pass.assert(model >= minimum, message === undefined ? `Number ${model} failed minimum check. (${minimum})` : message);
|
|
420
|
+
return model;
|
|
421
|
+
}, "afterAll");
|
|
422
|
+
}
|
|
423
|
+
max(maximum, message) {
|
|
424
|
+
return this.custom((model, pass) => {
|
|
425
|
+
pass.assert(model <= maximum, message === undefined ? `Number ${model} failed maximum check. (${maximum})` : message);
|
|
426
|
+
return model;
|
|
427
|
+
}, "afterAll");
|
|
428
|
+
}
|
|
429
|
+
clamp(minimum, maximum, messageA, messageB) {
|
|
430
|
+
return this.min(minimum, messageA).max(maximum, messageB === undefined ? messageA : messageB);
|
|
431
|
+
}
|
|
432
|
+
validNumber(notANumber = false, message) {
|
|
433
|
+
return this.custom((model, pass) => {
|
|
434
|
+
pass.assert(isNaN(model) === notANumber, message === undefined ? `Number ${model} failed not a number check. (${notANumber ? "Requires NaN" : "Requires valid number"})` : message);
|
|
435
|
+
return model;
|
|
436
|
+
}, "afterAll");
|
|
437
|
+
}
|
|
438
|
+
getJsonSchema() {
|
|
439
|
+
return {
|
|
440
|
+
type: "number",
|
|
441
|
+
description: this._getJsonSchemaDescription(),
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
class ObjectSchema extends BaseSchema {
|
|
447
|
+
subschema;
|
|
448
|
+
constructor(subschema, required, defaultValue) {
|
|
449
|
+
super("object", required, defaultValue);
|
|
450
|
+
this.subschema = subschema;
|
|
451
|
+
}
|
|
452
|
+
validate(source, pass) {
|
|
453
|
+
pass = this._ensurePass(source, pass);
|
|
454
|
+
const result = super.validate(source, pass);
|
|
455
|
+
if (result !== undefined) {
|
|
456
|
+
for (const key in this.subschema) {
|
|
457
|
+
const nestedSchema = this.subschema[key];
|
|
458
|
+
const nestedValue = result[key];
|
|
459
|
+
result[key] = this.subschema[key].validate(result[key], pass.next([...pass.path, key], nestedSchema, nestedValue));
|
|
330
460
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
461
|
+
}
|
|
462
|
+
return result;
|
|
463
|
+
}
|
|
464
|
+
convert(value, pass) {
|
|
465
|
+
const model = {};
|
|
466
|
+
for (const key in this.subschema) {
|
|
467
|
+
const nestedSchema = this.subschema[key];
|
|
468
|
+
const nestedValue = value[key];
|
|
469
|
+
model[key] = nestedSchema.convert(nestedValue, pass.next([...pass.path, key], nestedSchema, nestedValue));
|
|
470
|
+
}
|
|
471
|
+
return model;
|
|
472
|
+
}
|
|
473
|
+
extend(schema) {
|
|
474
|
+
let defaultValue = undefined;
|
|
475
|
+
if (this.hasDefault() !== schema.hasDefault()) {
|
|
476
|
+
throw new Error("Both or neither default values must be specified in order to extend a schema!");
|
|
477
|
+
}
|
|
478
|
+
if (this.hasDefault() && schema.hasDefault()) {
|
|
479
|
+
defaultValue = (pass) => {
|
|
480
|
+
return {
|
|
481
|
+
...this.getDefault(pass),
|
|
482
|
+
...schema.getDefault(pass)
|
|
483
|
+
};
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
return new ObjectSchema({ ...this.subschema, ...schema.subschema }, this._required, defaultValue);
|
|
487
|
+
}
|
|
488
|
+
getJsonSchema() {
|
|
489
|
+
const properties = {};
|
|
490
|
+
for (const key in this.subschema) {
|
|
491
|
+
properties[key] = this.subschema[key].getJsonSchema();
|
|
492
|
+
}
|
|
493
|
+
return {
|
|
494
|
+
type: "object",
|
|
495
|
+
description: this._getJsonSchemaDescription(),
|
|
496
|
+
properties
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
class OrSetSchema extends BaseSchema {
|
|
502
|
+
schemas;
|
|
503
|
+
constructor(schemas, required, defaultValue) {
|
|
504
|
+
super("string", required, defaultValue);
|
|
505
|
+
this.schemas = schemas;
|
|
506
|
+
}
|
|
507
|
+
validate(source, pass) {
|
|
508
|
+
pass = this._ensurePass(source, pass);
|
|
509
|
+
let result = super.validate(source, pass);
|
|
510
|
+
if (result !== undefined) {
|
|
511
|
+
let done = false;
|
|
512
|
+
const failureMessages = [];
|
|
513
|
+
for (let i = 0; i < this.schemas.length && !done; i++) {
|
|
514
|
+
const schema = this.schemas[i];
|
|
515
|
+
try {
|
|
516
|
+
result = schema.validate(result, pass);
|
|
517
|
+
done = true;
|
|
518
|
+
}
|
|
519
|
+
catch (error) {
|
|
520
|
+
if (error instanceof Error) {
|
|
521
|
+
failureMessages.push(`Schema #${i + 1}: ${error.message}`);
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
failureMessages.push(`Schema #${i + 1}: ${String(error)}`);
|
|
525
|
+
}
|
|
526
|
+
pass.assert(failureMessages.length !== this.schemas.length, `Supplied value didn't match any schemas in or-set.\n${failureMessages.join("\n")}`);
|
|
527
|
+
}
|
|
346
528
|
}
|
|
347
529
|
}
|
|
530
|
+
return result;
|
|
531
|
+
}
|
|
532
|
+
convert(value, pass) {
|
|
533
|
+
return value;
|
|
534
|
+
}
|
|
535
|
+
getJsonSchema() {
|
|
536
|
+
return { oneOf: this.schemas.map((schema) => schema.getJsonSchema()) };
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
class StringSchema extends BaseSchema {
|
|
541
|
+
constructor(required, defaultValue, additionalValidationPasses) {
|
|
542
|
+
super("string", required, defaultValue, additionalValidationPasses);
|
|
543
|
+
}
|
|
544
|
+
// 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.
|
|
545
|
+
// public required() { return new StringSchema(true, this._default, this._additionalValidationPasses); }
|
|
546
|
+
// public optional() { return new StringSchema(false, this._default, this._additionalValidationPasses); }
|
|
547
|
+
// public default<Default extends DefaultValue<StringSource>>(defaultValue: Default) { return new StringSchema(this._required, defaultValue, this._additionalValidationPasses); }
|
|
548
|
+
convert(value, pass) {
|
|
549
|
+
if (value instanceof Date) {
|
|
550
|
+
return value.toISOString();
|
|
551
|
+
}
|
|
552
|
+
else if (value === null) {
|
|
553
|
+
return "null";
|
|
554
|
+
}
|
|
555
|
+
else if (value === undefined) {
|
|
556
|
+
return "undefined";
|
|
557
|
+
}
|
|
558
|
+
return value.toString();
|
|
559
|
+
}
|
|
560
|
+
length(minimum, maximum, messageA, messageB) {
|
|
561
|
+
return this.custom((model, pass) => {
|
|
562
|
+
messageB = messageB === undefined ? messageA : messageB;
|
|
563
|
+
pass.assert(model.length > minimum, messageA === undefined ? `String "${model}: failed minimum length check. (${minimum})` : messageA);
|
|
564
|
+
pass.assert(model.length < maximum, messageB === undefined ? `String "${model}: failed maximum length check. (${maximum})` : messageB);
|
|
565
|
+
return model;
|
|
566
|
+
}, "afterAll");
|
|
567
|
+
}
|
|
568
|
+
regex(expression, message) {
|
|
569
|
+
return this.custom((model, pass) => {
|
|
570
|
+
pass.assert(expression.test(model), message === undefined ? `String "${model}" failed regular expression check. (${expression})` : message);
|
|
571
|
+
return model;
|
|
572
|
+
}, "afterAll");
|
|
573
|
+
}
|
|
574
|
+
getJsonSchema() {
|
|
575
|
+
return {
|
|
576
|
+
type: "string",
|
|
577
|
+
description: this._getJsonSchemaDescription(),
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
exports.Schema = void 0;
|
|
583
|
+
(function (Schema) {
|
|
584
|
+
function String(required = true, defaultValue = undefined) {
|
|
585
|
+
return new StringSchema(required, defaultValue);
|
|
586
|
+
}
|
|
587
|
+
Schema.String = String;
|
|
588
|
+
function Number(required = true, defaultValue = undefined) {
|
|
589
|
+
return new NumberSchema(required, defaultValue);
|
|
348
590
|
}
|
|
349
|
-
Schema.
|
|
350
|
-
|
|
591
|
+
Schema.Number = Number;
|
|
592
|
+
function Boolean(required = true, defaultValue = undefined) {
|
|
593
|
+
return new BooleanSchema(required, defaultValue);
|
|
594
|
+
}
|
|
595
|
+
Schema.Boolean = Boolean;
|
|
596
|
+
function Date(required = true, defaultValue = undefined) {
|
|
597
|
+
return new DateSchema(required, defaultValue);
|
|
598
|
+
}
|
|
599
|
+
Schema.Date = Date;
|
|
600
|
+
function Any(required = true, defaultValue = undefined) {
|
|
601
|
+
return new AnySchema(required, defaultValue);
|
|
602
|
+
}
|
|
603
|
+
Schema.Any = Any;
|
|
604
|
+
function Object(subschema, required = true, defaultValue = undefined) {
|
|
605
|
+
return new ObjectSchema(subschema, required, defaultValue);
|
|
606
|
+
}
|
|
607
|
+
Schema.Object = Object;
|
|
608
|
+
function Array(subschema, required = true, defaultValue = undefined) {
|
|
609
|
+
return new ArraySchema(subschema, required, defaultValue);
|
|
610
|
+
}
|
|
611
|
+
Schema.Array = Array;
|
|
612
|
+
function Enumeration(members, required = true, defaultValue = undefined) {
|
|
613
|
+
return new EnumerationSchema(members.data, required, defaultValue);
|
|
614
|
+
}
|
|
615
|
+
Schema.Enumeration = Enumeration;
|
|
616
|
+
function DynamicObject(subschema, required = true, defaultValue = undefined) {
|
|
617
|
+
return new DynamicObjectSchema(subschema, required, defaultValue);
|
|
618
|
+
}
|
|
619
|
+
Schema.DynamicObject = DynamicObject;
|
|
620
|
+
function OrSet(members, required = true, defaultValue = undefined) {
|
|
621
|
+
return new OrSetSchema(members.data, required, defaultValue);
|
|
622
|
+
}
|
|
623
|
+
Schema.OrSet = OrSet;
|
|
624
|
+
function Members(...members) {
|
|
625
|
+
return { data: members };
|
|
626
|
+
}
|
|
627
|
+
Schema.Members = Members;
|
|
628
|
+
})(exports.Schema || (exports.Schema = {}));
|
|
629
|
+
|
|
630
|
+
exports.$ = exports.Schema;
|
|
631
|
+
exports.AnySchema = AnySchema;
|
|
632
|
+
exports.ArraySchema = ArraySchema;
|
|
633
|
+
exports.BaseSchema = BaseSchema;
|
|
634
|
+
exports.BooleanSchema = BooleanSchema;
|
|
635
|
+
exports.DateSchema = DateSchema;
|
|
636
|
+
exports.DynamicObjectSchema = DynamicObjectSchema;
|
|
637
|
+
exports.EnumerationSchema = EnumerationSchema;
|
|
638
|
+
exports.NumberSchema = NumberSchema;
|
|
639
|
+
exports.ObjectSchema = ObjectSchema;
|
|
640
|
+
exports.OrSetSchema = OrSetSchema;
|
|
641
|
+
exports.StringSchema = StringSchema;
|
|
642
|
+
exports.ValidationError = ValidationError;
|
|
643
|
+
exports.ValidationPass = ValidationPass;
|
|
351
644
|
|
|
352
645
|
}));
|