@sinclair/typebox 0.28.15 → 0.28.17
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 +4 -1
- package/compiler/compiler.js +57 -43
- package/package.json +1 -1
- package/readme.md +6 -6
package/compiler/compiler.d.ts
CHANGED
|
@@ -26,10 +26,13 @@ export declare class TypeCompilerTypeGuardError extends Error {
|
|
|
26
26
|
readonly schema: Types.TSchema;
|
|
27
27
|
constructor(schema: Types.TSchema);
|
|
28
28
|
}
|
|
29
|
+
export interface TypeCompilerOptions {
|
|
30
|
+
language: 'typescript' | 'javascript';
|
|
31
|
+
}
|
|
29
32
|
/** Compiles Types for Runtime Type Checking */
|
|
30
33
|
export declare namespace TypeCompiler {
|
|
31
34
|
/** Returns the generated assertion code used to validate this type. */
|
|
32
|
-
function Code<T extends Types.TSchema>(schema: T, references?: Types.TSchema[]): string;
|
|
35
|
+
function Code<T extends Types.TSchema>(schema: T, references?: Types.TSchema[], options?: TypeCompilerOptions): string;
|
|
33
36
|
/** Compiles the given type for runtime type checking. This compiler only accepts known TypeBox types non-inclusive of unsafe types. */
|
|
34
37
|
function Compile<T extends Types.TSchema>(schema: T, references?: Types.TSchema[]): TypeCheck<T>;
|
|
35
38
|
}
|
package/compiler/compiler.js
CHANGED
|
@@ -199,14 +199,16 @@ var TypeCompiler;
|
|
|
199
199
|
yield 'true';
|
|
200
200
|
}
|
|
201
201
|
function* Array(schema, references, value) {
|
|
202
|
-
|
|
202
|
+
yield `Array.isArray(${value})`;
|
|
203
203
|
if (IsNumber(schema.minItems))
|
|
204
204
|
yield `${value}.length >= ${schema.minItems}`;
|
|
205
205
|
if (IsNumber(schema.maxItems))
|
|
206
206
|
yield `${value}.length <= ${schema.maxItems}`;
|
|
207
207
|
if (schema.uniqueItems === true)
|
|
208
208
|
yield `((function() { const set = new Set(); for(const element of ${value}) { const hashed = hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())`;
|
|
209
|
-
|
|
209
|
+
const expression = CreateExpression(schema.items, references, 'value');
|
|
210
|
+
const parameter = CreateParameter('value');
|
|
211
|
+
yield `${value}.every((${parameter}) => ${expression})`;
|
|
210
212
|
}
|
|
211
213
|
function* BigInt(schema, references, value) {
|
|
212
214
|
yield `(typeof ${value} === 'bigint')`;
|
|
@@ -222,7 +224,7 @@ var TypeCompiler;
|
|
|
222
224
|
yield `${value} <= BigInt(${schema.maximum})`;
|
|
223
225
|
}
|
|
224
226
|
function* Boolean(schema, references, value) {
|
|
225
|
-
yield `typeof ${value} === 'boolean'`;
|
|
227
|
+
yield `(typeof ${value} === 'boolean')`;
|
|
226
228
|
}
|
|
227
229
|
function* Constructor(schema, references, value) {
|
|
228
230
|
yield* Visit(schema.returns, references, `${value}.prototype`);
|
|
@@ -239,7 +241,7 @@ var TypeCompiler;
|
|
|
239
241
|
yield `${value}.getTime() <= ${schema.maximumTimestamp}`;
|
|
240
242
|
}
|
|
241
243
|
function* Function(schema, references, value) {
|
|
242
|
-
yield `typeof ${value} === 'function'`;
|
|
244
|
+
yield `(typeof ${value} === 'function')`;
|
|
243
245
|
}
|
|
244
246
|
function* Integer(schema, references, value) {
|
|
245
247
|
yield `(typeof ${value} === 'number' && Number.isInteger(${value}))`;
|
|
@@ -259,23 +261,23 @@ var TypeCompiler;
|
|
|
259
261
|
if (schema.unevaluatedProperties === false) {
|
|
260
262
|
const keyCheck = PushLocal(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`);
|
|
261
263
|
const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key))`;
|
|
262
|
-
yield
|
|
264
|
+
yield `(${check1} && ${check2})`;
|
|
263
265
|
}
|
|
264
266
|
else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) {
|
|
265
267
|
const keyCheck = PushLocal(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`);
|
|
266
268
|
const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key) || ${CreateExpression(schema.unevaluatedProperties, references, `${value}[key]`)})`;
|
|
267
|
-
yield
|
|
269
|
+
yield `(${check1} && ${check2})`;
|
|
268
270
|
}
|
|
269
271
|
else {
|
|
270
|
-
yield
|
|
272
|
+
yield `(${check1})`;
|
|
271
273
|
}
|
|
272
274
|
}
|
|
273
275
|
function* Literal(schema, references, value) {
|
|
274
276
|
if (typeof schema.const === 'number' || typeof schema.const === 'boolean') {
|
|
275
|
-
yield
|
|
277
|
+
yield `(${value} === ${schema.const})`;
|
|
276
278
|
}
|
|
277
279
|
else {
|
|
278
|
-
yield
|
|
280
|
+
yield `(${value} === '${schema.const}')`;
|
|
279
281
|
}
|
|
280
282
|
}
|
|
281
283
|
function* Never(schema, references, value) {
|
|
@@ -287,7 +289,7 @@ var TypeCompiler;
|
|
|
287
289
|
yield `!${left} && ${right}`;
|
|
288
290
|
}
|
|
289
291
|
function* Null(schema, references, value) {
|
|
290
|
-
yield
|
|
292
|
+
yield `(${value} === null)`;
|
|
291
293
|
}
|
|
292
294
|
function* Number(schema, references, value) {
|
|
293
295
|
yield IsNumberCheck(value);
|
|
@@ -358,10 +360,10 @@ var TypeCompiler;
|
|
|
358
360
|
if (index === -1)
|
|
359
361
|
throw new TypeCompilerDereferenceError(schema);
|
|
360
362
|
const target = references[index];
|
|
361
|
-
// Reference: If we have seen this reference before we can just yield and
|
|
362
|
-
// the function call. If this isn't the case we defer to visit to
|
|
363
|
-
// set the function for subsequent passes.
|
|
364
|
-
if (
|
|
363
|
+
// Reference: If we have seen this reference before we can just yield and
|
|
364
|
+
// return the function call. If this isn't the case we defer to visit to
|
|
365
|
+
// generate and set the function for subsequent passes.
|
|
366
|
+
if (state.functions.has(schema.$ref))
|
|
365
367
|
return yield `${CreateFunctionName(schema.$ref)}(${value})`;
|
|
366
368
|
yield* Visit(target, references, value);
|
|
367
369
|
}
|
|
@@ -392,7 +394,7 @@ var TypeCompiler;
|
|
|
392
394
|
yield `${func}(${value})`;
|
|
393
395
|
}
|
|
394
396
|
function* Tuple(schema, references, value) {
|
|
395
|
-
yield `
|
|
397
|
+
yield `Array.isArray(${value})`;
|
|
396
398
|
if (schema.items === undefined)
|
|
397
399
|
return yield `${value}.length === 0`;
|
|
398
400
|
yield `(${value}.length === ${schema.maxItems})`;
|
|
@@ -422,21 +424,24 @@ var TypeCompiler;
|
|
|
422
424
|
yield IsVoidCheck(value);
|
|
423
425
|
}
|
|
424
426
|
function* UserDefined(schema, references, value) {
|
|
425
|
-
const schema_key = `schema_key_${
|
|
426
|
-
|
|
427
|
+
const schema_key = `schema_key_${state.customs.size}`;
|
|
428
|
+
state.customs.set(schema_key, schema);
|
|
427
429
|
yield `custom('${schema[Types.Kind]}', '${schema_key}', ${value})`;
|
|
428
430
|
}
|
|
429
431
|
function* Visit(schema, references, value, root = false) {
|
|
430
432
|
const references_ = IsString(schema.$id) ? [...references, schema] : references;
|
|
431
433
|
const schema_ = schema;
|
|
432
|
-
// Rule: Types with identifiers are hoisted into their own functions.
|
|
433
|
-
//
|
|
434
|
-
//
|
|
435
|
-
//
|
|
434
|
+
// Rule: Types with identifiers are hoisted into their own functions.
|
|
435
|
+
// The following will generate a function for the schema and yield the
|
|
436
|
+
// call to that function. This call is only made if NOT the root type
|
|
437
|
+
// which allows the generated function to yield its expression. The
|
|
438
|
+
// root argument is only true when making calls via CreateFunction().
|
|
439
|
+
// Note there is potential to omit the root argument and conditional
|
|
440
|
+
// by refactoring the logic below. Consider for review.
|
|
436
441
|
if (IsString(schema.$id)) {
|
|
437
442
|
const name = CreateFunctionName(schema.$id);
|
|
438
|
-
if (!
|
|
439
|
-
|
|
443
|
+
if (!state.functions.has(schema.$id)) {
|
|
444
|
+
state.functions.add(schema.$id);
|
|
440
445
|
const body = CreateFunction(name, schema, references, 'value');
|
|
441
446
|
PushFunction(body);
|
|
442
447
|
}
|
|
@@ -509,49 +514,58 @@ var TypeCompiler;
|
|
|
509
514
|
// -------------------------------------------------------------------
|
|
510
515
|
// Compiler State
|
|
511
516
|
// -------------------------------------------------------------------
|
|
512
|
-
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
}
|
|
517
|
+
// prettier-ignore
|
|
518
|
+
const state = {
|
|
519
|
+
language: 'javascript',
|
|
520
|
+
variables: new Set(),
|
|
521
|
+
functions: new Set(),
|
|
522
|
+
customs: new Map(), // custom type data
|
|
523
|
+
};
|
|
520
524
|
function CreateExpression(schema, references, value) {
|
|
521
525
|
return `(${[...Visit(schema, references, value)].join(' && ')})`;
|
|
522
526
|
}
|
|
523
527
|
function CreateFunctionName($id) {
|
|
524
528
|
return `check_${Identifier.Encode($id)}`;
|
|
525
529
|
}
|
|
530
|
+
function CreateParameter(name) {
|
|
531
|
+
const annotation = state.language === 'typescript' ? ': any' : '';
|
|
532
|
+
return `${name}${annotation}`;
|
|
533
|
+
}
|
|
526
534
|
function CreateFunction(name, schema, references, value) {
|
|
527
535
|
const expression = [...Visit(schema, references, value, true)].map((condition) => ` ${condition}`).join(' &&\n');
|
|
528
|
-
|
|
536
|
+
const parameter = CreateParameter('value');
|
|
537
|
+
return `function ${name}(${parameter}) {\n return (\n${expression}\n )\n}`;
|
|
529
538
|
}
|
|
530
539
|
function PushFunction(functionBody) {
|
|
531
|
-
|
|
540
|
+
state.variables.add(functionBody);
|
|
532
541
|
}
|
|
533
542
|
function PushLocal(expression) {
|
|
534
|
-
const local = `local_${
|
|
535
|
-
|
|
543
|
+
const local = `local_${state.variables.size}`;
|
|
544
|
+
state.variables.add(`const ${local} = ${expression}`);
|
|
536
545
|
return local;
|
|
537
546
|
}
|
|
538
547
|
function GetLocals() {
|
|
539
|
-
return [...
|
|
548
|
+
return [...state.variables.values()];
|
|
540
549
|
}
|
|
541
550
|
// -------------------------------------------------------------------
|
|
542
551
|
// Compile
|
|
543
552
|
// -------------------------------------------------------------------
|
|
544
553
|
function Build(schema, references) {
|
|
545
|
-
ResetCompiler();
|
|
546
554
|
const check = CreateFunction('check', schema, references, 'value'); // interior visit
|
|
547
555
|
const locals = GetLocals();
|
|
556
|
+
const parameter = CreateParameter('value');
|
|
548
557
|
// prettier-ignore
|
|
549
558
|
return IsString(schema.$id) // ensure top level schemas with $id's are hoisted
|
|
550
|
-
? `${locals.join('\n')}\nreturn function check(
|
|
559
|
+
? `${locals.join('\n')}\nreturn function check(${parameter}) {\n return ${CreateFunctionName(schema.$id)}(value)\n}`
|
|
551
560
|
: `${locals.join('\n')}\nreturn ${check}`;
|
|
552
561
|
}
|
|
553
562
|
/** Returns the generated assertion code used to validate this type. */
|
|
554
|
-
function Code(schema, references = []) {
|
|
563
|
+
function Code(schema, references = [], options = { language: 'javascript' }) {
|
|
564
|
+
// compiler-reset
|
|
565
|
+
state.language = options.language;
|
|
566
|
+
state.variables.clear();
|
|
567
|
+
state.functions.clear();
|
|
568
|
+
state.customs.clear();
|
|
555
569
|
if (!Types.TypeGuard.TSchema(schema))
|
|
556
570
|
throw new TypeCompilerTypeGuardError(schema);
|
|
557
571
|
for (const schema of references)
|
|
@@ -562,13 +576,13 @@ var TypeCompiler;
|
|
|
562
576
|
TypeCompiler.Code = Code;
|
|
563
577
|
/** Compiles the given type for runtime type checking. This compiler only accepts known TypeBox types non-inclusive of unsafe types. */
|
|
564
578
|
function Compile(schema, references = []) {
|
|
565
|
-
const code = Code(schema, references);
|
|
566
|
-
const
|
|
579
|
+
const code = Code(schema, references, { language: 'javascript' });
|
|
580
|
+
const customs = new Map(state.customs);
|
|
567
581
|
const compiledFunction = globalThis.Function('custom', 'format', 'hash', code);
|
|
568
582
|
const checkFunction = compiledFunction((kind, schema_key, value) => {
|
|
569
|
-
if (!Types.TypeRegistry.Has(kind) || !
|
|
583
|
+
if (!Types.TypeRegistry.Has(kind) || !customs.has(schema_key))
|
|
570
584
|
return false;
|
|
571
|
-
const schema =
|
|
585
|
+
const schema = customs.get(schema_key);
|
|
572
586
|
const func = Types.TypeRegistry.Get(kind);
|
|
573
587
|
return func(schema, value);
|
|
574
588
|
}, (format, value) => {
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -483,7 +483,7 @@ The following table lists the Standard TypeBox types. These types are fully comp
|
|
|
483
483
|
│ ]) │ │ ], │
|
|
484
484
|
│ const T = Type.Tuple([ │ │ additionalItems: false, │
|
|
485
485
|
| ...Type.Rest(A), │ │ minItems: 4, │
|
|
486
|
-
| ...Type.
|
|
486
|
+
| ...Type.Rest(B) │ │ maxItems: 4 │
|
|
487
487
|
│ ]) │ │ } │
|
|
488
488
|
│ │ │ │
|
|
489
489
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
@@ -870,7 +870,7 @@ const T = Type.Object({ // const T = {
|
|
|
870
870
|
z: Type.Boolean() // properties: {
|
|
871
871
|
}) // x: { type: 'number' },
|
|
872
872
|
// y: { type: 'string' },
|
|
873
|
-
// z: { type: 'string' }
|
|
873
|
+
// z: { type: 'string' }
|
|
874
874
|
// }
|
|
875
875
|
// }
|
|
876
876
|
|
|
@@ -896,7 +896,7 @@ const C = Type.Index(T, Type.KeyOf(T)) // const C = {
|
|
|
896
896
|
|
|
897
897
|
### Rest Types
|
|
898
898
|
|
|
899
|
-
Rest parameters are supported with `Type.Rest`. This function is used to extract interior type elements from tuples which enables them to compose with the JavaScript spread operator `...`. This type can be used for tuple
|
|
899
|
+
Rest parameters are supported with `Type.Rest`. This function is used to extract interior type elements from tuples which enables them to compose with the JavaScript spread operator `...`. This type can be used for tuple concatenation as well as for variadic functions.
|
|
900
900
|
|
|
901
901
|
```typescript
|
|
902
902
|
// TypeScript
|
|
@@ -908,14 +908,14 @@ type C = [...T, number] // type C = [number, number
|
|
|
908
908
|
type F = (...param: C) => void // type F = (
|
|
909
909
|
// param0: number,
|
|
910
910
|
// param1: number,
|
|
911
|
-
// param2: number
|
|
911
|
+
// param2: number
|
|
912
912
|
// ) => void
|
|
913
913
|
|
|
914
914
|
// TypeBox
|
|
915
915
|
|
|
916
916
|
const T = Type.Tuple([ // const T: TTuple<[
|
|
917
917
|
Type.Number(), // TNumber,
|
|
918
|
-
Type.Number() // TNumber
|
|
918
|
+
Type.Number() // TNumber
|
|
919
919
|
]) // ]>
|
|
920
920
|
|
|
921
921
|
const C = Type.Tuple([ // const C: TTuple<[
|
|
@@ -1335,7 +1335,7 @@ console.log(C.Code()) // return function check(va
|
|
|
1335
1335
|
|
|
1336
1336
|
## TypeSystem
|
|
1337
1337
|
|
|
1338
|
-
The TypeBox TypeSystem module provides functionality to define types above and beyond the Standard and Extended type sets as well as control various assertion
|
|
1338
|
+
The TypeBox TypeSystem module provides functionality to define types above and beyond the Standard and Extended type sets as well as control various assertion policies. Configurations made to the TypeSystem module are observed by both `TypeCompiler` and `Value` modules.
|
|
1339
1339
|
|
|
1340
1340
|
The TypeSystem module is provided as an optional import.
|
|
1341
1341
|
|