@sinclair/typebox 0.29.6 → 0.30.0-dev-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/compiler/compiler.d.ts +7 -4
- package/compiler/compiler.js +208 -194
- package/errors/errors.d.ts +65 -60
- package/errors/errors.js +515 -492
- package/package.json +14 -2
- package/readme.md +161 -169
- package/system/system.js +0 -6
- package/typebox.d.ts +99 -52
- package/typebox.js +496 -573
- package/value/cast.d.ts +5 -4
- package/value/cast.js +255 -260
- package/value/check.d.ts +4 -3
- package/value/check.js +412 -397
- package/value/clone.d.ts +2 -3
- package/value/clone.js +62 -42
- package/value/convert.d.ts +5 -4
- package/value/convert.js +305 -318
- package/value/create.d.ts +6 -6
- package/value/create.js +388 -376
- package/value/delta.d.ts +2 -4
- package/value/delta.js +121 -130
- package/value/equal.d.ts +2 -3
- package/value/equal.js +48 -51
- package/value/guard.d.ts +44 -0
- package/value/guard.js +146 -0
- package/value/hash.d.ts +14 -3
- package/value/hash.js +124 -166
- package/value/index.d.ts +3 -4
- package/value/index.js +6 -20
- package/value/mutate.d.ts +2 -4
- package/value/mutate.js +75 -67
- package/value/pointer.js +8 -2
- package/value/value.d.ts +15 -15
- package/value/value.js +27 -27
- package/value/is.d.ts +0 -11
- package/value/is.js +0 -53
package/compiler/compiler.js
CHANGED
|
@@ -28,10 +28,11 @@ THE SOFTWARE.
|
|
|
28
28
|
---------------------------------------------------------------------------*/
|
|
29
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
30
|
exports.TypeCompiler = exports.TypeCompilerTypeGuardError = exports.TypeCompilerDereferenceError = exports.TypeCompilerUnknownTypeError = exports.TypeCheck = void 0;
|
|
31
|
+
const index_1 = require("../system/index");
|
|
31
32
|
const Types = require("../typebox");
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
33
|
+
const ValueErrors = require("../errors/index");
|
|
34
|
+
const ValueHash = require("../value/hash");
|
|
35
|
+
const ValueGuard = require("../value/guard");
|
|
35
36
|
// -------------------------------------------------------------------
|
|
36
37
|
// TypeCheck
|
|
37
38
|
// -------------------------------------------------------------------
|
|
@@ -48,7 +49,7 @@ class TypeCheck {
|
|
|
48
49
|
}
|
|
49
50
|
/** Returns an iterator for each error in this value. */
|
|
50
51
|
Errors(value) {
|
|
51
|
-
return
|
|
52
|
+
return ValueErrors.Errors(this.schema, this.references, value);
|
|
52
53
|
}
|
|
53
54
|
/** Returns true if the value matches the compiled type. */
|
|
54
55
|
Check(value) {
|
|
@@ -128,7 +129,7 @@ var Identifier;
|
|
|
128
129
|
Identifier.Encode = Encode;
|
|
129
130
|
})(Identifier || (Identifier = {}));
|
|
130
131
|
// -------------------------------------------------------------------
|
|
131
|
-
//
|
|
132
|
+
// Errors
|
|
132
133
|
// -------------------------------------------------------------------
|
|
133
134
|
class TypeCompilerUnknownTypeError extends Error {
|
|
134
135
|
constructor(schema) {
|
|
@@ -154,20 +155,8 @@ exports.TypeCompilerTypeGuardError = TypeCompilerTypeGuardError;
|
|
|
154
155
|
/** Compiles Types for Runtime Type Checking */
|
|
155
156
|
var TypeCompiler;
|
|
156
157
|
(function (TypeCompiler) {
|
|
157
|
-
// -------------------------------------------------------------------
|
|
158
|
-
// Guards
|
|
159
|
-
// -------------------------------------------------------------------
|
|
160
|
-
function IsBigInt(value) {
|
|
161
|
-
return typeof value === 'bigint';
|
|
162
|
-
}
|
|
163
|
-
function IsNumber(value) {
|
|
164
|
-
return typeof value === 'number' && globalThis.Number.isFinite(value);
|
|
165
|
-
}
|
|
166
|
-
function IsString(value) {
|
|
167
|
-
return typeof value === 'string';
|
|
168
|
-
}
|
|
169
158
|
// ----------------------------------------------------------------------
|
|
170
|
-
//
|
|
159
|
+
// Guards
|
|
171
160
|
// ----------------------------------------------------------------------
|
|
172
161
|
function IsAnyOrUnknown(schema) {
|
|
173
162
|
return schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown';
|
|
@@ -176,95 +165,110 @@ var TypeCompiler;
|
|
|
176
165
|
// Polices
|
|
177
166
|
// -------------------------------------------------------------------
|
|
178
167
|
function IsExactOptionalProperty(value, key, expression) {
|
|
179
|
-
return
|
|
168
|
+
return index_1.TypeSystem.ExactOptionalPropertyTypes ? `('${key}' in ${value} ? ${expression} : true)` : `(${MemberExpression.Encode(value, key)} !== undefined ? ${expression} : true)`;
|
|
180
169
|
}
|
|
181
170
|
function IsObjectCheck(value) {
|
|
182
|
-
return !
|
|
171
|
+
return !index_1.TypeSystem.AllowArrayObjects ? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` : `(typeof ${value} === 'object' && ${value} !== null)`;
|
|
183
172
|
}
|
|
184
173
|
function IsRecordCheck(value) {
|
|
185
|
-
return !
|
|
174
|
+
return !index_1.TypeSystem.AllowArrayObjects
|
|
186
175
|
? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}) && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))`
|
|
187
176
|
: `(typeof ${value} === 'object' && ${value} !== null && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))`;
|
|
188
177
|
}
|
|
189
178
|
function IsNumberCheck(value) {
|
|
190
|
-
return !
|
|
179
|
+
return !index_1.TypeSystem.AllowNaN ? `(typeof ${value} === 'number' && Number.isFinite(${value}))` : `typeof ${value} === 'number'`;
|
|
191
180
|
}
|
|
192
181
|
function IsVoidCheck(value) {
|
|
193
|
-
return
|
|
182
|
+
return index_1.TypeSystem.AllowVoidNull ? `(${value} === undefined || ${value} === null)` : `${value} === undefined`;
|
|
194
183
|
}
|
|
195
184
|
// -------------------------------------------------------------------
|
|
196
185
|
// Types
|
|
197
186
|
// -------------------------------------------------------------------
|
|
198
|
-
function*
|
|
187
|
+
function* TAny(schema, references, value) {
|
|
199
188
|
yield 'true';
|
|
200
189
|
}
|
|
201
|
-
function*
|
|
190
|
+
function* TArray(schema, references, value) {
|
|
202
191
|
yield `Array.isArray(${value})`;
|
|
203
|
-
|
|
192
|
+
const [parameter, accumulator] = [CreateParameter('value', 'any'), CreateParameter('acc', 'number')];
|
|
193
|
+
if (ValueGuard.IsNumber(schema.minItems))
|
|
204
194
|
yield `${value}.length >= ${schema.minItems}`;
|
|
205
|
-
if (IsNumber(schema.maxItems))
|
|
195
|
+
if (ValueGuard.IsNumber(schema.maxItems))
|
|
206
196
|
yield `${value}.length <= ${schema.maxItems}`;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
197
|
+
const elementExpression = CreateExpression(schema.items, references, 'value');
|
|
198
|
+
yield `${value}.every((${parameter}) => ${elementExpression})`;
|
|
199
|
+
if (Types.TypeGuard.TSchema(schema.contains) || ValueGuard.IsNumber(schema.minContains) || ValueGuard.IsNumber(schema.maxContains)) {
|
|
200
|
+
const containsSchema = Types.TypeGuard.TSchema(schema.contains) ? schema.contains : Types.Type.Never();
|
|
201
|
+
const checkExpression = CreateExpression(containsSchema, references, 'value');
|
|
202
|
+
const checkMinContains = ValueGuard.IsNumber(schema.minContains) ? [`(count >= ${schema.minContains})`] : [];
|
|
203
|
+
const checkMaxContains = ValueGuard.IsNumber(schema.maxContains) ? [`(count <= ${schema.maxContains})`] : [];
|
|
204
|
+
const checkCount = `const count = ${value}.reduce((${accumulator}, ${parameter}) => ${checkExpression} ? acc + 1 : acc, 0)`;
|
|
205
|
+
const check = [`(count > 0)`, ...checkMinContains, ...checkMaxContains].join(' && ');
|
|
206
|
+
yield `((${parameter}) => { ${checkCount}; return ${check}})(${value})`;
|
|
207
|
+
}
|
|
208
|
+
if (schema.uniqueItems === true) {
|
|
209
|
+
const check = `const hashed = hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true`;
|
|
210
|
+
const block = `const set = new Set(); for(const element of value) { ${check} }`;
|
|
211
|
+
yield `((${parameter}) => { ${block} )(${value})`;
|
|
212
|
+
}
|
|
212
213
|
}
|
|
213
|
-
function*
|
|
214
|
+
function* TAsyncIterator(schema, references, value) {
|
|
215
|
+
yield `(typeof value === 'object' && Symbol.asyncIterator in ${value})`;
|
|
216
|
+
}
|
|
217
|
+
function* TBigInt(schema, references, value) {
|
|
214
218
|
yield `(typeof ${value} === 'bigint')`;
|
|
215
|
-
if (IsBigInt(schema.multipleOf))
|
|
219
|
+
if (ValueGuard.IsBigInt(schema.multipleOf))
|
|
216
220
|
yield `(${value} % BigInt(${schema.multipleOf})) === 0`;
|
|
217
|
-
if (IsBigInt(schema.exclusiveMinimum))
|
|
221
|
+
if (ValueGuard.IsBigInt(schema.exclusiveMinimum))
|
|
218
222
|
yield `${value} > BigInt(${schema.exclusiveMinimum})`;
|
|
219
|
-
if (IsBigInt(schema.exclusiveMaximum))
|
|
223
|
+
if (ValueGuard.IsBigInt(schema.exclusiveMaximum))
|
|
220
224
|
yield `${value} < BigInt(${schema.exclusiveMaximum})`;
|
|
221
|
-
if (IsBigInt(schema.minimum))
|
|
225
|
+
if (ValueGuard.IsBigInt(schema.minimum))
|
|
222
226
|
yield `${value} >= BigInt(${schema.minimum})`;
|
|
223
|
-
if (IsBigInt(schema.maximum))
|
|
227
|
+
if (ValueGuard.IsBigInt(schema.maximum))
|
|
224
228
|
yield `${value} <= BigInt(${schema.maximum})`;
|
|
225
229
|
}
|
|
226
|
-
function*
|
|
230
|
+
function* TBoolean(schema, references, value) {
|
|
227
231
|
yield `(typeof ${value} === 'boolean')`;
|
|
228
232
|
}
|
|
229
|
-
function*
|
|
233
|
+
function* TConstructor(schema, references, value) {
|
|
230
234
|
yield* Visit(schema.returns, references, `${value}.prototype`);
|
|
231
235
|
}
|
|
232
|
-
function*
|
|
236
|
+
function* TDate(schema, references, value) {
|
|
233
237
|
yield `(${value} instanceof Date) && Number.isFinite(${value}.getTime())`;
|
|
234
|
-
if (IsNumber(schema.exclusiveMinimumTimestamp))
|
|
238
|
+
if (ValueGuard.IsNumber(schema.exclusiveMinimumTimestamp))
|
|
235
239
|
yield `${value}.getTime() > ${schema.exclusiveMinimumTimestamp}`;
|
|
236
|
-
if (IsNumber(schema.exclusiveMaximumTimestamp))
|
|
240
|
+
if (ValueGuard.IsNumber(schema.exclusiveMaximumTimestamp))
|
|
237
241
|
yield `${value}.getTime() < ${schema.exclusiveMaximumTimestamp}`;
|
|
238
|
-
if (IsNumber(schema.minimumTimestamp))
|
|
242
|
+
if (ValueGuard.IsNumber(schema.minimumTimestamp))
|
|
239
243
|
yield `${value}.getTime() >= ${schema.minimumTimestamp}`;
|
|
240
|
-
if (IsNumber(schema.maximumTimestamp))
|
|
244
|
+
if (ValueGuard.IsNumber(schema.maximumTimestamp))
|
|
241
245
|
yield `${value}.getTime() <= ${schema.maximumTimestamp}`;
|
|
242
246
|
}
|
|
243
|
-
function*
|
|
247
|
+
function* TFunction(schema, references, value) {
|
|
244
248
|
yield `(typeof ${value} === 'function')`;
|
|
245
249
|
}
|
|
246
|
-
function*
|
|
250
|
+
function* TInteger(schema, references, value) {
|
|
247
251
|
yield `(typeof ${value} === 'number' && Number.isInteger(${value}))`;
|
|
248
|
-
if (IsNumber(schema.multipleOf))
|
|
252
|
+
if (ValueGuard.IsNumber(schema.multipleOf))
|
|
249
253
|
yield `(${value} % ${schema.multipleOf}) === 0`;
|
|
250
|
-
if (IsNumber(schema.exclusiveMinimum))
|
|
254
|
+
if (ValueGuard.IsNumber(schema.exclusiveMinimum))
|
|
251
255
|
yield `${value} > ${schema.exclusiveMinimum}`;
|
|
252
|
-
if (IsNumber(schema.exclusiveMaximum))
|
|
256
|
+
if (ValueGuard.IsNumber(schema.exclusiveMaximum))
|
|
253
257
|
yield `${value} < ${schema.exclusiveMaximum}`;
|
|
254
|
-
if (IsNumber(schema.minimum))
|
|
258
|
+
if (ValueGuard.IsNumber(schema.minimum))
|
|
255
259
|
yield `${value} >= ${schema.minimum}`;
|
|
256
|
-
if (IsNumber(schema.maximum))
|
|
260
|
+
if (ValueGuard.IsNumber(schema.maximum))
|
|
257
261
|
yield `${value} <= ${schema.maximum}`;
|
|
258
262
|
}
|
|
259
|
-
function*
|
|
263
|
+
function* TIntersect(schema, references, value) {
|
|
260
264
|
const check1 = schema.allOf.map((schema) => CreateExpression(schema, references, value)).join(' && ');
|
|
261
265
|
if (schema.unevaluatedProperties === false) {
|
|
262
|
-
const keyCheck =
|
|
266
|
+
const keyCheck = CreateVariable(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`);
|
|
263
267
|
const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key))`;
|
|
264
268
|
yield `(${check1} && ${check2})`;
|
|
265
269
|
}
|
|
266
270
|
else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) {
|
|
267
|
-
const keyCheck =
|
|
271
|
+
const keyCheck = CreateVariable(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`);
|
|
268
272
|
const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key) || ${CreateExpression(schema.unevaluatedProperties, references, `${value}[key]`)})`;
|
|
269
273
|
yield `(${check1} && ${check2})`;
|
|
270
274
|
}
|
|
@@ -272,7 +276,10 @@ var TypeCompiler;
|
|
|
272
276
|
yield `(${check1})`;
|
|
273
277
|
}
|
|
274
278
|
}
|
|
275
|
-
function*
|
|
279
|
+
function* TIterator(schema, references, value) {
|
|
280
|
+
yield `(typeof value === 'object' && Symbol.iterator in ${value})`;
|
|
281
|
+
}
|
|
282
|
+
function* TLiteral(schema, references, value) {
|
|
276
283
|
if (typeof schema.const === 'number' || typeof schema.const === 'boolean') {
|
|
277
284
|
yield `(${value} === ${schema.const})`;
|
|
278
285
|
}
|
|
@@ -280,36 +287,36 @@ var TypeCompiler;
|
|
|
280
287
|
yield `(${value} === '${schema.const}')`;
|
|
281
288
|
}
|
|
282
289
|
}
|
|
283
|
-
function*
|
|
290
|
+
function* TNever(schema, references, value) {
|
|
284
291
|
yield `false`;
|
|
285
292
|
}
|
|
286
|
-
function*
|
|
293
|
+
function* TNot(schema, references, value) {
|
|
287
294
|
const expression = CreateExpression(schema.not, references, value);
|
|
288
295
|
yield `(!${expression})`;
|
|
289
296
|
}
|
|
290
|
-
function*
|
|
297
|
+
function* TNull(schema, references, value) {
|
|
291
298
|
yield `(${value} === null)`;
|
|
292
299
|
}
|
|
293
|
-
function*
|
|
300
|
+
function* TNumber(schema, references, value) {
|
|
294
301
|
yield IsNumberCheck(value);
|
|
295
|
-
if (IsNumber(schema.multipleOf))
|
|
302
|
+
if (ValueGuard.IsNumber(schema.multipleOf))
|
|
296
303
|
yield `(${value} % ${schema.multipleOf}) === 0`;
|
|
297
|
-
if (IsNumber(schema.exclusiveMinimum))
|
|
304
|
+
if (ValueGuard.IsNumber(schema.exclusiveMinimum))
|
|
298
305
|
yield `${value} > ${schema.exclusiveMinimum}`;
|
|
299
|
-
if (IsNumber(schema.exclusiveMaximum))
|
|
306
|
+
if (ValueGuard.IsNumber(schema.exclusiveMaximum))
|
|
300
307
|
yield `${value} < ${schema.exclusiveMaximum}`;
|
|
301
|
-
if (IsNumber(schema.minimum))
|
|
308
|
+
if (ValueGuard.IsNumber(schema.minimum))
|
|
302
309
|
yield `${value} >= ${schema.minimum}`;
|
|
303
|
-
if (IsNumber(schema.maximum))
|
|
310
|
+
if (ValueGuard.IsNumber(schema.maximum))
|
|
304
311
|
yield `${value} <= ${schema.maximum}`;
|
|
305
312
|
}
|
|
306
|
-
function*
|
|
313
|
+
function* TObject(schema, references, value) {
|
|
307
314
|
yield IsObjectCheck(value);
|
|
308
|
-
if (IsNumber(schema.minProperties))
|
|
315
|
+
if (ValueGuard.IsNumber(schema.minProperties))
|
|
309
316
|
yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}`;
|
|
310
|
-
if (IsNumber(schema.maxProperties))
|
|
317
|
+
if (ValueGuard.IsNumber(schema.maxProperties))
|
|
311
318
|
yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}`;
|
|
312
|
-
const knownKeys =
|
|
319
|
+
const knownKeys = Object.getOwnPropertyNames(schema.properties);
|
|
313
320
|
for (const knownKey of knownKeys) {
|
|
314
321
|
const memberExpression = MemberExpression.Encode(value, knownKey);
|
|
315
322
|
const property = schema.properties[knownKey];
|
|
@@ -338,61 +345,60 @@ var TypeCompiler;
|
|
|
338
345
|
yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key) || ${expression}))`;
|
|
339
346
|
}
|
|
340
347
|
}
|
|
341
|
-
function*
|
|
348
|
+
function* TPromise(schema, references, value) {
|
|
342
349
|
yield `(typeof value === 'object' && typeof ${value}.then === 'function')`;
|
|
343
350
|
}
|
|
344
|
-
function*
|
|
351
|
+
function* TRecord(schema, references, value) {
|
|
345
352
|
yield IsRecordCheck(value);
|
|
346
|
-
if (IsNumber(schema.minProperties))
|
|
353
|
+
if (ValueGuard.IsNumber(schema.minProperties))
|
|
347
354
|
yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}`;
|
|
348
|
-
if (IsNumber(schema.maxProperties))
|
|
355
|
+
if (ValueGuard.IsNumber(schema.maxProperties))
|
|
349
356
|
yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}`;
|
|
350
|
-
const [patternKey, patternSchema] =
|
|
351
|
-
const
|
|
357
|
+
const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0];
|
|
358
|
+
const variable = CreateVariable(`new RegExp(/${patternKey}/)`);
|
|
352
359
|
const check1 = CreateExpression(patternSchema, references, 'value');
|
|
353
360
|
const check2 = Types.TypeGuard.TSchema(schema.additionalProperties) ? CreateExpression(schema.additionalProperties, references, value) : schema.additionalProperties === false ? 'false' : 'true';
|
|
354
|
-
const expression = `(${
|
|
361
|
+
const expression = `(${variable}.test(key) ? ${check1} : ${check2})`;
|
|
355
362
|
yield `(Object.entries(${value}).every(([key, value]) => ${expression}))`;
|
|
356
363
|
}
|
|
357
|
-
function*
|
|
364
|
+
function* TRef(schema, references, value) {
|
|
358
365
|
const index = references.findIndex((foreign) => foreign.$id === schema.$ref);
|
|
359
366
|
if (index === -1)
|
|
360
367
|
throw new TypeCompilerDereferenceError(schema);
|
|
361
368
|
const target = references[index];
|
|
362
|
-
// Reference: If we have seen this reference before we can just yield and
|
|
363
|
-
//
|
|
364
|
-
// generate and set the function for subsequent passes.
|
|
369
|
+
// Reference: If we have seen this reference before we can just yield and return the function call.
|
|
370
|
+
// If this isn't the case we defer to visit to generate and set the function for subsequent passes.
|
|
365
371
|
if (state.functions.has(schema.$ref))
|
|
366
372
|
return yield `${CreateFunctionName(schema.$ref)}(${value})`;
|
|
367
373
|
yield* Visit(target, references, value);
|
|
368
374
|
}
|
|
369
|
-
function*
|
|
375
|
+
function* TString(schema, references, value) {
|
|
370
376
|
yield `(typeof ${value} === 'string')`;
|
|
371
|
-
if (IsNumber(schema.minLength))
|
|
377
|
+
if (ValueGuard.IsNumber(schema.minLength))
|
|
372
378
|
yield `${value}.length >= ${schema.minLength}`;
|
|
373
|
-
if (IsNumber(schema.maxLength))
|
|
379
|
+
if (ValueGuard.IsNumber(schema.maxLength))
|
|
374
380
|
yield `${value}.length <= ${schema.maxLength}`;
|
|
375
381
|
if (schema.pattern !== undefined) {
|
|
376
|
-
const
|
|
377
|
-
yield `${
|
|
382
|
+
const variable = CreateVariable(`${new RegExp(schema.pattern)};`);
|
|
383
|
+
yield `${variable}.test(${value})`;
|
|
378
384
|
}
|
|
379
385
|
if (schema.format !== undefined) {
|
|
380
386
|
yield `format('${schema.format}', ${value})`;
|
|
381
387
|
}
|
|
382
388
|
}
|
|
383
|
-
function*
|
|
389
|
+
function* TSymbol(schema, references, value) {
|
|
384
390
|
yield `(typeof ${value} === 'symbol')`;
|
|
385
391
|
}
|
|
386
|
-
function*
|
|
392
|
+
function* TTemplateLiteral(schema, references, value) {
|
|
387
393
|
yield `(typeof ${value} === 'string')`;
|
|
388
|
-
const
|
|
389
|
-
yield `${
|
|
394
|
+
const variable = CreateVariable(`${new RegExp(schema.pattern)};`);
|
|
395
|
+
yield `${variable}.test(${value})`;
|
|
390
396
|
}
|
|
391
|
-
function*
|
|
397
|
+
function* TThis(schema, references, value) {
|
|
392
398
|
const func = CreateFunctionName(schema.$ref);
|
|
393
399
|
yield `${func}(${value})`;
|
|
394
400
|
}
|
|
395
|
-
function*
|
|
401
|
+
function* TTuple(schema, references, value) {
|
|
396
402
|
yield `Array.isArray(${value})`;
|
|
397
403
|
if (schema.items === undefined)
|
|
398
404
|
return yield `${value}.length === 0`;
|
|
@@ -402,112 +408,114 @@ var TypeCompiler;
|
|
|
402
408
|
yield `${expression}`;
|
|
403
409
|
}
|
|
404
410
|
}
|
|
405
|
-
function*
|
|
411
|
+
function* TUndefined(schema, references, value) {
|
|
406
412
|
yield `${value} === undefined`;
|
|
407
413
|
}
|
|
408
|
-
function*
|
|
414
|
+
function* TUnion(schema, references, value) {
|
|
409
415
|
const expressions = schema.anyOf.map((schema) => CreateExpression(schema, references, value));
|
|
410
416
|
yield `(${expressions.join(' || ')})`;
|
|
411
417
|
}
|
|
412
|
-
function*
|
|
418
|
+
function* TUint8Array(schema, references, value) {
|
|
413
419
|
yield `${value} instanceof Uint8Array`;
|
|
414
|
-
if (IsNumber(schema.maxByteLength))
|
|
420
|
+
if (ValueGuard.IsNumber(schema.maxByteLength))
|
|
415
421
|
yield `(${value}.length <= ${schema.maxByteLength})`;
|
|
416
|
-
if (IsNumber(schema.minByteLength))
|
|
422
|
+
if (ValueGuard.IsNumber(schema.minByteLength))
|
|
417
423
|
yield `(${value}.length >= ${schema.minByteLength})`;
|
|
418
424
|
}
|
|
419
|
-
function*
|
|
425
|
+
function* TUnknown(schema, references, value) {
|
|
420
426
|
yield 'true';
|
|
421
427
|
}
|
|
422
|
-
function*
|
|
428
|
+
function* TVoid(schema, references, value) {
|
|
423
429
|
yield IsVoidCheck(value);
|
|
424
430
|
}
|
|
425
|
-
function*
|
|
426
|
-
|
|
427
|
-
state.customs.set(schema_key, schema);
|
|
428
|
-
yield `custom('${schema[Types.Kind]}', '${schema_key}', ${value})`;
|
|
431
|
+
function* TKind(schema, references, value) {
|
|
432
|
+
yield `kind('${schema[Types.Kind]}', ${value})`;
|
|
429
433
|
}
|
|
430
|
-
function* Visit(schema, references, value,
|
|
431
|
-
const references_ = IsString(schema.$id) ? [...references, schema] : references;
|
|
434
|
+
function* Visit(schema, references, value, useHoisting = true) {
|
|
435
|
+
const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references;
|
|
432
436
|
const schema_ = schema;
|
|
433
|
-
//
|
|
434
|
-
//
|
|
435
|
-
//
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
state.functions.
|
|
444
|
-
|
|
445
|
-
PushFunction(body);
|
|
437
|
+
// ----------------------------------------------------------------------------------
|
|
438
|
+
// Hoisting
|
|
439
|
+
// ----------------------------------------------------------------------------------
|
|
440
|
+
if (useHoisting && ValueGuard.IsString(schema.$id)) {
|
|
441
|
+
const functionName = CreateFunctionName(schema.$id);
|
|
442
|
+
if (state.functions.has(functionName)) {
|
|
443
|
+
return yield `${functionName}(${value})`;
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
const functionCode = CreateFunction(functionName, schema, references, 'value', false);
|
|
447
|
+
state.functions.set(functionName, functionCode);
|
|
448
|
+
return yield `${functionName}(${value})`;
|
|
446
449
|
}
|
|
447
|
-
if (!root)
|
|
448
|
-
return yield `${name}(${value})`;
|
|
449
450
|
}
|
|
451
|
+
// ----------------------------------------------------------------------------------
|
|
452
|
+
// Types
|
|
453
|
+
// ----------------------------------------------------------------------------------
|
|
450
454
|
switch (schema_[Types.Kind]) {
|
|
451
455
|
case 'Any':
|
|
452
|
-
return yield*
|
|
456
|
+
return yield* TAny(schema_, references_, value);
|
|
453
457
|
case 'Array':
|
|
454
|
-
return yield*
|
|
458
|
+
return yield* TArray(schema_, references_, value);
|
|
459
|
+
case 'AsyncIterator':
|
|
460
|
+
return yield* TAsyncIterator(schema_, references_, value);
|
|
455
461
|
case 'BigInt':
|
|
456
|
-
return yield*
|
|
462
|
+
return yield* TBigInt(schema_, references_, value);
|
|
457
463
|
case 'Boolean':
|
|
458
|
-
return yield*
|
|
464
|
+
return yield* TBoolean(schema_, references_, value);
|
|
459
465
|
case 'Constructor':
|
|
460
|
-
return yield*
|
|
466
|
+
return yield* TConstructor(schema_, references_, value);
|
|
461
467
|
case 'Date':
|
|
462
|
-
return yield*
|
|
468
|
+
return yield* TDate(schema_, references_, value);
|
|
463
469
|
case 'Function':
|
|
464
|
-
return yield*
|
|
470
|
+
return yield* TFunction(schema_, references_, value);
|
|
465
471
|
case 'Integer':
|
|
466
|
-
return yield*
|
|
472
|
+
return yield* TInteger(schema_, references_, value);
|
|
467
473
|
case 'Intersect':
|
|
468
|
-
return yield*
|
|
474
|
+
return yield* TIntersect(schema_, references_, value);
|
|
475
|
+
case 'Iterator':
|
|
476
|
+
return yield* TIterator(schema_, references_, value);
|
|
469
477
|
case 'Literal':
|
|
470
|
-
return yield*
|
|
478
|
+
return yield* TLiteral(schema_, references_, value);
|
|
471
479
|
case 'Never':
|
|
472
|
-
return yield*
|
|
480
|
+
return yield* TNever(schema_, references_, value);
|
|
473
481
|
case 'Not':
|
|
474
|
-
return yield*
|
|
482
|
+
return yield* TNot(schema_, references_, value);
|
|
475
483
|
case 'Null':
|
|
476
|
-
return yield*
|
|
484
|
+
return yield* TNull(schema_, references_, value);
|
|
477
485
|
case 'Number':
|
|
478
|
-
return yield*
|
|
486
|
+
return yield* TNumber(schema_, references_, value);
|
|
479
487
|
case 'Object':
|
|
480
|
-
return yield*
|
|
488
|
+
return yield* TObject(schema_, references_, value);
|
|
481
489
|
case 'Promise':
|
|
482
|
-
return yield*
|
|
490
|
+
return yield* TPromise(schema_, references_, value);
|
|
483
491
|
case 'Record':
|
|
484
|
-
return yield*
|
|
492
|
+
return yield* TRecord(schema_, references_, value);
|
|
485
493
|
case 'Ref':
|
|
486
|
-
return yield*
|
|
494
|
+
return yield* TRef(schema_, references_, value);
|
|
487
495
|
case 'String':
|
|
488
|
-
return yield*
|
|
496
|
+
return yield* TString(schema_, references_, value);
|
|
489
497
|
case 'Symbol':
|
|
490
|
-
return yield*
|
|
498
|
+
return yield* TSymbol(schema_, references_, value);
|
|
491
499
|
case 'TemplateLiteral':
|
|
492
|
-
return yield*
|
|
500
|
+
return yield* TTemplateLiteral(schema_, references_, value);
|
|
493
501
|
case 'This':
|
|
494
|
-
return yield*
|
|
502
|
+
return yield* TThis(schema_, references_, value);
|
|
495
503
|
case 'Tuple':
|
|
496
|
-
return yield*
|
|
504
|
+
return yield* TTuple(schema_, references_, value);
|
|
497
505
|
case 'Undefined':
|
|
498
|
-
return yield*
|
|
506
|
+
return yield* TUndefined(schema_, references_, value);
|
|
499
507
|
case 'Union':
|
|
500
|
-
return yield*
|
|
508
|
+
return yield* TUnion(schema_, references_, value);
|
|
501
509
|
case 'Uint8Array':
|
|
502
|
-
return yield*
|
|
510
|
+
return yield* TUint8Array(schema_, references_, value);
|
|
503
511
|
case 'Unknown':
|
|
504
|
-
return yield*
|
|
512
|
+
return yield* TUnknown(schema_, references_, value);
|
|
505
513
|
case 'Void':
|
|
506
|
-
return yield*
|
|
514
|
+
return yield* TVoid(schema_, references_, value);
|
|
507
515
|
default:
|
|
508
516
|
if (!Types.TypeRegistry.Has(schema_[Types.Kind]))
|
|
509
517
|
throw new TypeCompilerUnknownTypeError(schema);
|
|
510
|
-
return yield*
|
|
518
|
+
return yield* TKind(schema_, references_, value);
|
|
511
519
|
}
|
|
512
520
|
}
|
|
513
521
|
// -------------------------------------------------------------------
|
|
@@ -516,15 +524,29 @@ var TypeCompiler;
|
|
|
516
524
|
// prettier-ignore
|
|
517
525
|
const state = {
|
|
518
526
|
language: 'javascript',
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
customs: new Map(), // custom type data
|
|
527
|
+
functions: new Map(),
|
|
528
|
+
variables: new Map(), // local variables
|
|
522
529
|
};
|
|
530
|
+
// -------------------------------------------------------------------
|
|
531
|
+
// Compiler Factory
|
|
532
|
+
// -------------------------------------------------------------------
|
|
533
|
+
function CreateExpression(schema, references, value, useHoisting = true) {
|
|
534
|
+
return `(${[...Visit(schema, references, value, useHoisting)].join(' && ')})`;
|
|
535
|
+
}
|
|
523
536
|
function CreateFunctionName($id) {
|
|
524
537
|
return `check_${Identifier.Encode($id)}`;
|
|
525
538
|
}
|
|
526
|
-
function
|
|
527
|
-
|
|
539
|
+
function CreateVariable(expression) {
|
|
540
|
+
const variableName = `local_${state.variables.size}`;
|
|
541
|
+
state.variables.set(variableName, `const ${variableName} = ${expression}`);
|
|
542
|
+
return variableName;
|
|
543
|
+
}
|
|
544
|
+
function CreateFunction(name, schema, references, value, useHoisting = true) {
|
|
545
|
+
const [newline, pad] = ['\n', (length) => ''.padStart(length, ' ')];
|
|
546
|
+
const parameter = CreateParameter('value', 'any');
|
|
547
|
+
const returns = CreateReturns('boolean');
|
|
548
|
+
const expression = [...Visit(schema, references, value, useHoisting)].map((expression) => `${pad(4)}${expression}`).join(` &&${newline}`);
|
|
549
|
+
return `function ${name}(${parameter})${returns} {${newline}${pad(2)}return (${newline}${expression}${newline}${pad(2)})\n}`;
|
|
528
550
|
}
|
|
529
551
|
function CreateParameter(name, type) {
|
|
530
552
|
const annotation = state.language === 'typescript' ? `: ${type}` : '';
|
|
@@ -533,71 +555,63 @@ var TypeCompiler;
|
|
|
533
555
|
function CreateReturns(type) {
|
|
534
556
|
return state.language === 'typescript' ? `: ${type}` : '';
|
|
535
557
|
}
|
|
536
|
-
function CreateFunction(name, schema, references, value) {
|
|
537
|
-
const expression = [...Visit(schema, references, value, true)].map((condition) => ` ${condition}`).join(' &&\n');
|
|
538
|
-
const parameter = CreateParameter('value', 'any');
|
|
539
|
-
const returns = CreateReturns('boolean');
|
|
540
|
-
return `function ${name}(${parameter})${returns} {\n return (\n${expression}\n )\n}`;
|
|
541
|
-
}
|
|
542
|
-
function PushFunction(functionBody) {
|
|
543
|
-
state.variables.add(functionBody);
|
|
544
|
-
}
|
|
545
|
-
function PushLocal(expression) {
|
|
546
|
-
const local = `local_${state.variables.size}`;
|
|
547
|
-
state.variables.add(`const ${local} = ${expression}`);
|
|
548
|
-
return local;
|
|
549
|
-
}
|
|
550
|
-
function GetLocals() {
|
|
551
|
-
return [...state.variables.values()];
|
|
552
|
-
}
|
|
553
558
|
// -------------------------------------------------------------------
|
|
554
559
|
// Compile
|
|
555
560
|
// -------------------------------------------------------------------
|
|
556
|
-
function Build(schema, references) {
|
|
557
|
-
const
|
|
558
|
-
const locals = GetLocals();
|
|
561
|
+
function Build(schema, references, options) {
|
|
562
|
+
const functionCode = CreateFunction('check', schema, references, 'value'); // will populate functions and variables
|
|
559
563
|
const parameter = CreateParameter('value', 'any');
|
|
560
564
|
const returns = CreateReturns('boolean');
|
|
565
|
+
const functions = [...state.functions.values()];
|
|
566
|
+
const variables = [...state.variables.values()];
|
|
561
567
|
// prettier-ignore
|
|
562
|
-
|
|
563
|
-
?
|
|
564
|
-
:
|
|
568
|
+
const checkFunction = ValueGuard.IsString(schema.$id) // ensure top level schemas with $id's are hoisted
|
|
569
|
+
? `return function check(${parameter})${returns} {\n return ${CreateFunctionName(schema.$id)}(value)\n}`
|
|
570
|
+
: `return ${functionCode}`;
|
|
571
|
+
return [...variables, ...functions, checkFunction].join('\n');
|
|
565
572
|
}
|
|
566
573
|
/** Returns the generated assertion code used to validate this type. */
|
|
567
|
-
function Code(
|
|
574
|
+
function Code(...args) {
|
|
575
|
+
const defaults = { language: 'javascript' };
|
|
576
|
+
// prettier-ignore
|
|
577
|
+
const [schema, references, options] = (args.length === 2 && ValueGuard.IsArray(args[1]) ? [args[0], args[1], defaults] :
|
|
578
|
+
args.length === 2 && !ValueGuard.IsArray(args[1]) ? [args[0], [], args[1]] :
|
|
579
|
+
args.length === 3 ? [args[0], args[1], args[2]] :
|
|
580
|
+
args.length === 1 ? [args[0], [], defaults] :
|
|
581
|
+
[null, [], defaults]);
|
|
568
582
|
// compiler-reset
|
|
569
583
|
state.language = options.language;
|
|
570
584
|
state.variables.clear();
|
|
571
585
|
state.functions.clear();
|
|
572
|
-
state.customs.clear();
|
|
573
586
|
if (!Types.TypeGuard.TSchema(schema))
|
|
574
587
|
throw new TypeCompilerTypeGuardError(schema);
|
|
575
588
|
for (const schema of references)
|
|
576
589
|
if (!Types.TypeGuard.TSchema(schema))
|
|
577
590
|
throw new TypeCompilerTypeGuardError(schema);
|
|
578
|
-
return Build(schema, references);
|
|
591
|
+
return Build(schema, references, options);
|
|
579
592
|
}
|
|
580
593
|
TypeCompiler.Code = Code;
|
|
581
594
|
/** Compiles the given type for runtime type checking. This compiler only accepts known TypeBox types non-inclusive of unsafe types. */
|
|
582
595
|
function Compile(schema, references = []) {
|
|
583
|
-
const
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
if (!Types.TypeRegistry.Has(kind) || !customs.has(schema_key))
|
|
596
|
+
const generatedCode = Code(schema, references, { language: 'javascript' });
|
|
597
|
+
const compiledFunction = globalThis.Function('kind', 'format', 'hash', generatedCode);
|
|
598
|
+
function typeRegistryFunction(kind, value) {
|
|
599
|
+
if (!Types.TypeRegistry.Has(kind))
|
|
588
600
|
return false;
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
601
|
+
const checkFunc = Types.TypeRegistry.Get(kind);
|
|
602
|
+
return checkFunc(schema, value);
|
|
603
|
+
}
|
|
604
|
+
function formatRegistryFunction(format, value) {
|
|
593
605
|
if (!Types.FormatRegistry.Has(format))
|
|
594
606
|
return false;
|
|
595
|
-
const
|
|
596
|
-
return
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
607
|
+
const checkFunc = Types.FormatRegistry.Get(format);
|
|
608
|
+
return checkFunc(value);
|
|
609
|
+
}
|
|
610
|
+
function valueHashFunction(value) {
|
|
611
|
+
return ValueHash.Hash(value);
|
|
612
|
+
}
|
|
613
|
+
const checkFunction = compiledFunction(typeRegistryFunction, formatRegistryFunction, valueHashFunction);
|
|
614
|
+
return new TypeCheck(schema, references, checkFunction, generatedCode);
|
|
601
615
|
}
|
|
602
616
|
TypeCompiler.Compile = Compile;
|
|
603
617
|
})(TypeCompiler || (exports.TypeCompiler = TypeCompiler = {}));
|