@sinclair/typebox 0.28.16 → 0.28.18
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 +50 -35
- package/package.json +1 -1
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
|
@@ -207,7 +207,8 @@ var TypeCompiler;
|
|
|
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
|
-
|
|
210
|
+
const parameter = CreateParameter('value', 'any');
|
|
211
|
+
yield `${value}.every((${parameter}) => ${expression})`;
|
|
211
212
|
}
|
|
212
213
|
function* BigInt(schema, references, value) {
|
|
213
214
|
yield `(typeof ${value} === 'bigint')`;
|
|
@@ -223,7 +224,7 @@ var TypeCompiler;
|
|
|
223
224
|
yield `${value} <= BigInt(${schema.maximum})`;
|
|
224
225
|
}
|
|
225
226
|
function* Boolean(schema, references, value) {
|
|
226
|
-
yield `typeof ${value} === 'boolean'`;
|
|
227
|
+
yield `(typeof ${value} === 'boolean')`;
|
|
227
228
|
}
|
|
228
229
|
function* Constructor(schema, references, value) {
|
|
229
230
|
yield* Visit(schema.returns, references, `${value}.prototype`);
|
|
@@ -240,7 +241,7 @@ var TypeCompiler;
|
|
|
240
241
|
yield `${value}.getTime() <= ${schema.maximumTimestamp}`;
|
|
241
242
|
}
|
|
242
243
|
function* Function(schema, references, value) {
|
|
243
|
-
yield `typeof ${value} === 'function'`;
|
|
244
|
+
yield `(typeof ${value} === 'function')`;
|
|
244
245
|
}
|
|
245
246
|
function* Integer(schema, references, value) {
|
|
246
247
|
yield `(typeof ${value} === 'number' && Number.isInteger(${value}))`;
|
|
@@ -260,23 +261,23 @@ var TypeCompiler;
|
|
|
260
261
|
if (schema.unevaluatedProperties === false) {
|
|
261
262
|
const keyCheck = PushLocal(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`);
|
|
262
263
|
const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key))`;
|
|
263
|
-
yield
|
|
264
|
+
yield `(${check1} && ${check2})`;
|
|
264
265
|
}
|
|
265
266
|
else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) {
|
|
266
267
|
const keyCheck = PushLocal(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`);
|
|
267
268
|
const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key) || ${CreateExpression(schema.unevaluatedProperties, references, `${value}[key]`)})`;
|
|
268
|
-
yield
|
|
269
|
+
yield `(${check1} && ${check2})`;
|
|
269
270
|
}
|
|
270
271
|
else {
|
|
271
|
-
yield
|
|
272
|
+
yield `(${check1})`;
|
|
272
273
|
}
|
|
273
274
|
}
|
|
274
275
|
function* Literal(schema, references, value) {
|
|
275
276
|
if (typeof schema.const === 'number' || typeof schema.const === 'boolean') {
|
|
276
|
-
yield
|
|
277
|
+
yield `(${value} === ${schema.const})`;
|
|
277
278
|
}
|
|
278
279
|
else {
|
|
279
|
-
yield
|
|
280
|
+
yield `(${value} === '${schema.const}')`;
|
|
280
281
|
}
|
|
281
282
|
}
|
|
282
283
|
function* Never(schema, references, value) {
|
|
@@ -288,7 +289,7 @@ var TypeCompiler;
|
|
|
288
289
|
yield `!${left} && ${right}`;
|
|
289
290
|
}
|
|
290
291
|
function* Null(schema, references, value) {
|
|
291
|
-
yield
|
|
292
|
+
yield `(${value} === null)`;
|
|
292
293
|
}
|
|
293
294
|
function* Number(schema, references, value) {
|
|
294
295
|
yield IsNumberCheck(value);
|
|
@@ -362,7 +363,7 @@ var TypeCompiler;
|
|
|
362
363
|
// Reference: If we have seen this reference before we can just yield and
|
|
363
364
|
// return the function call. If this isn't the case we defer to visit to
|
|
364
365
|
// generate and set the function for subsequent passes.
|
|
365
|
-
if (
|
|
366
|
+
if (state.functions.has(schema.$ref))
|
|
366
367
|
return yield `${CreateFunctionName(schema.$ref)}(${value})`;
|
|
367
368
|
yield* Visit(target, references, value);
|
|
368
369
|
}
|
|
@@ -423,8 +424,8 @@ var TypeCompiler;
|
|
|
423
424
|
yield IsVoidCheck(value);
|
|
424
425
|
}
|
|
425
426
|
function* UserDefined(schema, references, value) {
|
|
426
|
-
const schema_key = `schema_key_${
|
|
427
|
-
|
|
427
|
+
const schema_key = `schema_key_${state.customs.size}`;
|
|
428
|
+
state.customs.set(schema_key, schema);
|
|
428
429
|
yield `custom('${schema[Types.Kind]}', '${schema_key}', ${value})`;
|
|
429
430
|
}
|
|
430
431
|
function* Visit(schema, references, value, root = false) {
|
|
@@ -439,8 +440,8 @@ var TypeCompiler;
|
|
|
439
440
|
// by refactoring the logic below. Consider for review.
|
|
440
441
|
if (IsString(schema.$id)) {
|
|
441
442
|
const name = CreateFunctionName(schema.$id);
|
|
442
|
-
if (!
|
|
443
|
-
|
|
443
|
+
if (!state.functions.has(schema.$id)) {
|
|
444
|
+
state.functions.add(schema.$id);
|
|
444
445
|
const body = CreateFunction(name, schema, references, 'value');
|
|
445
446
|
PushFunction(body);
|
|
446
447
|
}
|
|
@@ -513,49 +514,63 @@ var TypeCompiler;
|
|
|
513
514
|
// -------------------------------------------------------------------
|
|
514
515
|
// Compiler State
|
|
515
516
|
// -------------------------------------------------------------------
|
|
516
|
-
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
+
};
|
|
524
|
+
function CreateFunctionName($id) {
|
|
525
|
+
return `check_${Identifier.Encode($id)}`;
|
|
523
526
|
}
|
|
524
527
|
function CreateExpression(schema, references, value) {
|
|
525
528
|
return `(${[...Visit(schema, references, value)].join(' && ')})`;
|
|
526
529
|
}
|
|
527
|
-
function
|
|
528
|
-
|
|
530
|
+
function CreateParameter(name, type) {
|
|
531
|
+
const annotation = state.language === 'typescript' ? `: ${type}` : '';
|
|
532
|
+
return `${name}${annotation}`;
|
|
533
|
+
}
|
|
534
|
+
function CreateReturns(type) {
|
|
535
|
+
return state.language === 'typescript' ? `: ${type}` : '';
|
|
529
536
|
}
|
|
530
537
|
function CreateFunction(name, schema, references, value) {
|
|
531
538
|
const expression = [...Visit(schema, references, value, true)].map((condition) => ` ${condition}`).join(' &&\n');
|
|
532
|
-
|
|
539
|
+
const parameter = CreateParameter('value', 'any');
|
|
540
|
+
const returns = CreateReturns('boolean');
|
|
541
|
+
return `function ${name}(${parameter})${returns} {\n return (\n${expression}\n )\n}`;
|
|
533
542
|
}
|
|
534
543
|
function PushFunction(functionBody) {
|
|
535
|
-
|
|
544
|
+
state.variables.add(functionBody);
|
|
536
545
|
}
|
|
537
546
|
function PushLocal(expression) {
|
|
538
|
-
const local = `local_${
|
|
539
|
-
|
|
547
|
+
const local = `local_${state.variables.size}`;
|
|
548
|
+
state.variables.add(`const ${local} = ${expression}`);
|
|
540
549
|
return local;
|
|
541
550
|
}
|
|
542
551
|
function GetLocals() {
|
|
543
|
-
return [...
|
|
552
|
+
return [...state.variables.values()];
|
|
544
553
|
}
|
|
545
554
|
// -------------------------------------------------------------------
|
|
546
555
|
// Compile
|
|
547
556
|
// -------------------------------------------------------------------
|
|
548
557
|
function Build(schema, references) {
|
|
549
|
-
ResetCompiler();
|
|
550
558
|
const check = CreateFunction('check', schema, references, 'value'); // interior visit
|
|
551
559
|
const locals = GetLocals();
|
|
560
|
+
const parameter = CreateParameter('value', 'any');
|
|
561
|
+
const returns = CreateReturns('boolean');
|
|
552
562
|
// prettier-ignore
|
|
553
563
|
return IsString(schema.$id) // ensure top level schemas with $id's are hoisted
|
|
554
|
-
? `${locals.join('\n')}\nreturn function check(
|
|
564
|
+
? `${locals.join('\n')}\nreturn function check(${parameter})${returns} {\n return ${CreateFunctionName(schema.$id)}(value)\n}`
|
|
555
565
|
: `${locals.join('\n')}\nreturn ${check}`;
|
|
556
566
|
}
|
|
557
567
|
/** Returns the generated assertion code used to validate this type. */
|
|
558
|
-
function Code(schema, references = []) {
|
|
568
|
+
function Code(schema, references = [], options = { language: 'javascript' }) {
|
|
569
|
+
// compiler-reset
|
|
570
|
+
state.language = options.language;
|
|
571
|
+
state.variables.clear();
|
|
572
|
+
state.functions.clear();
|
|
573
|
+
state.customs.clear();
|
|
559
574
|
if (!Types.TypeGuard.TSchema(schema))
|
|
560
575
|
throw new TypeCompilerTypeGuardError(schema);
|
|
561
576
|
for (const schema of references)
|
|
@@ -566,13 +581,13 @@ var TypeCompiler;
|
|
|
566
581
|
TypeCompiler.Code = Code;
|
|
567
582
|
/** Compiles the given type for runtime type checking. This compiler only accepts known TypeBox types non-inclusive of unsafe types. */
|
|
568
583
|
function Compile(schema, references = []) {
|
|
569
|
-
const code = Code(schema, references);
|
|
570
|
-
const
|
|
584
|
+
const code = Code(schema, references, { language: 'javascript' });
|
|
585
|
+
const customs = new Map(state.customs);
|
|
571
586
|
const compiledFunction = globalThis.Function('custom', 'format', 'hash', code);
|
|
572
587
|
const checkFunction = compiledFunction((kind, schema_key, value) => {
|
|
573
|
-
if (!Types.TypeRegistry.Has(kind) || !
|
|
588
|
+
if (!Types.TypeRegistry.Has(kind) || !customs.has(schema_key))
|
|
574
589
|
return false;
|
|
575
|
-
const schema =
|
|
590
|
+
const schema = customs.get(schema_key);
|
|
576
591
|
const func = Types.TypeRegistry.Get(kind);
|
|
577
592
|
return func(schema, value);
|
|
578
593
|
}, (format, value) => {
|