@atscript/typescript 0.1.2 → 0.1.3
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/dist/cli.cjs +90 -51
- package/dist/index.cjs +90 -51
- package/dist/index.d.ts +8 -2
- package/dist/index.mjs +90 -51
- package/dist/utils.cjs +306 -41
- package/dist/utils.d.ts +273 -11
- package/dist/utils.mjs +301 -41
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -207,7 +207,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
207
207
|
this.writeln(" * Do not edit this file!");
|
|
208
208
|
this.writeln(" */");
|
|
209
209
|
this.writeln();
|
|
210
|
-
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator,
|
|
210
|
+
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TAtscriptAnnotatedType, TMetadataMap, Validator, TValidatorOptions } from \"@atscript/typescript/utils\"");
|
|
211
211
|
}
|
|
212
212
|
post() {
|
|
213
213
|
this.writeln("// prettier-ignore-end");
|
|
@@ -296,9 +296,10 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
296
296
|
}
|
|
297
297
|
if (asClass) {
|
|
298
298
|
this.writeln("static __is_atscript_annotated_type: true");
|
|
299
|
-
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
|
|
299
|
+
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}, ${asClass}>`);
|
|
300
300
|
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
301
|
-
this.writeln(`static validator:
|
|
301
|
+
this.writeln(`static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${asClass}>`);
|
|
302
|
+
if (resolveJsonSchemaMode(this.opts) === false) this.writeln("/** @deprecated JSON Schema support is disabled. Calling this method will throw a runtime error. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add `@emit.jsonSchema` annotation to individual interfaces. */");
|
|
302
303
|
this.writeln("static toJsonSchema: () => any");
|
|
303
304
|
}
|
|
304
305
|
this.pop();
|
|
@@ -313,6 +314,12 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
313
314
|
if (struct?.entity === "structure") this.renderStructure(struct, node.id);
|
|
314
315
|
else this.writeln("{}");
|
|
315
316
|
this.writeln();
|
|
317
|
+
const nsPrefix = exported ? "export declare" : "declare";
|
|
318
|
+
this.write(`${nsPrefix} namespace ${node.id} `);
|
|
319
|
+
this.blockln("{}");
|
|
320
|
+
this.writeln(`type DataType = ${node.id}`);
|
|
321
|
+
this.popln();
|
|
322
|
+
this.writeln();
|
|
316
323
|
}
|
|
317
324
|
renderType(node) {
|
|
318
325
|
this.writeln();
|
|
@@ -339,6 +346,11 @@ else this.writeln("{}");
|
|
|
339
346
|
} else {
|
|
340
347
|
this.write(exported ? "export declare " : "declare ");
|
|
341
348
|
this.writeln(`class ${node.id} extends ${targetName} {}`);
|
|
349
|
+
const nsPrefix = exported ? "export declare" : "declare";
|
|
350
|
+
this.write(`${nsPrefix} namespace ${node.id} `);
|
|
351
|
+
this.blockln("{}");
|
|
352
|
+
this.writeln(`type DataType = ${node.id}`);
|
|
353
|
+
this.popln();
|
|
342
354
|
this.writeln();
|
|
343
355
|
}
|
|
344
356
|
}
|
|
@@ -353,15 +365,17 @@ else this.writeln("{}");
|
|
|
353
365
|
let realDef = inputDef;
|
|
354
366
|
if (isRef(inputDef)) realDef = this.doc.unwindType(inputDef.id, inputDef.chain)?.def || realDef;
|
|
355
367
|
realDef = this.doc.mergeIntersection(realDef);
|
|
356
|
-
if (isStructure(realDef) || isInterface(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}>`;
|
|
357
|
-
else if (isGroup(realDef)) typeDef =
|
|
358
|
-
else if (isArray(realDef)) typeDef =
|
|
359
|
-
else if (isPrimitive(realDef)) typeDef =
|
|
368
|
+
if (isStructure(realDef) || isInterface(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}, ${name}>`;
|
|
369
|
+
else if (isGroup(realDef)) typeDef = `TAtscriptTypeComplex<${name}>`;
|
|
370
|
+
else if (isArray(realDef)) typeDef = `TAtscriptTypeArray<${name}>`;
|
|
371
|
+
else if (isPrimitive(realDef)) typeDef = `TAtscriptTypeFinal<${name}>`;
|
|
360
372
|
}
|
|
373
|
+
this.writeln(`type DataType = ${name}`);
|
|
361
374
|
this.writeln(`const __is_atscript_annotated_type: true`);
|
|
362
375
|
this.writeln(`const type: ${typeDef}`);
|
|
363
376
|
this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
|
|
364
|
-
this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<
|
|
377
|
+
this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<TAtscriptAnnotatedType, ${name}>`);
|
|
378
|
+
if (resolveJsonSchemaMode(this.opts) === false) this.writeln("/** @deprecated JSON Schema support is disabled. Calling this method will throw a runtime error. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add `@emit.jsonSchema` annotation to individual interfaces. */");
|
|
365
379
|
this.writeln("const toJsonSchema: () => any");
|
|
366
380
|
this.popln();
|
|
367
381
|
}
|
|
@@ -403,6 +417,20 @@ function renderPrimitiveTypeDef(def) {
|
|
|
403
417
|
}
|
|
404
418
|
}
|
|
405
419
|
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region packages/typescript/src/traverse.ts
|
|
422
|
+
function forAnnotatedType(def, handlers) {
|
|
423
|
+
switch (def.type.kind) {
|
|
424
|
+
case "": return handlers.final(def);
|
|
425
|
+
case "object": return handlers.object(def);
|
|
426
|
+
case "array": return handlers.array(def);
|
|
427
|
+
case "union": return handlers.union(def);
|
|
428
|
+
case "intersection": return handlers.intersection(def);
|
|
429
|
+
case "tuple": return handlers.tuple(def);
|
|
430
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
406
434
|
//#endregion
|
|
407
435
|
//#region packages/typescript/src/validator.ts
|
|
408
436
|
function _define_property$1(obj, key, value) {
|
|
@@ -448,7 +476,17 @@ var Validator = class {
|
|
|
448
476
|
throw() {
|
|
449
477
|
throw new ValidatorError(this.errors);
|
|
450
478
|
}
|
|
451
|
-
|
|
479
|
+
/**
|
|
480
|
+
* Validates a value against the type definition.
|
|
481
|
+
*
|
|
482
|
+
* Acts as a TypeScript type guard — when it returns `true`, the value
|
|
483
|
+
* is narrowed to `DataType`.
|
|
484
|
+
*
|
|
485
|
+
* @param value - The value to validate.
|
|
486
|
+
* @param safe - If `true`, returns `false` on failure instead of throwing.
|
|
487
|
+
* @returns `true` if the value matches the type definition.
|
|
488
|
+
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
489
|
+
*/ validate(value, safe) {
|
|
452
490
|
this.push("");
|
|
453
491
|
this.errors = [];
|
|
454
492
|
this.stackErrors = [];
|
|
@@ -475,15 +513,14 @@ var Validator = class {
|
|
|
475
513
|
return this.stackPath.slice(1).join(".");
|
|
476
514
|
}
|
|
477
515
|
validateAnnotatedType(def, value) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
516
|
+
return forAnnotatedType(def, {
|
|
517
|
+
final: (d) => this.validatePrimitive(d, value),
|
|
518
|
+
object: (d) => this.validateObject(d, value),
|
|
519
|
+
array: (d) => this.validateArray(d, value),
|
|
520
|
+
union: (d) => this.validateUnion(d, value),
|
|
521
|
+
intersection: (d) => this.validateIntersection(d, value),
|
|
522
|
+
tuple: (d) => this.validateTuple(d, value)
|
|
523
|
+
});
|
|
487
524
|
}
|
|
488
525
|
validateUnion(def, value) {
|
|
489
526
|
let i = 0;
|
|
@@ -715,7 +752,7 @@ else {
|
|
|
715
752
|
constructor(def, opts) {
|
|
716
753
|
_define_property$1(this, "def", void 0);
|
|
717
754
|
_define_property$1(this, "opts", void 0);
|
|
718
|
-
_define_property$1(this, "errors", void 0);
|
|
755
|
+
/** Validation errors collected during the last {@link validate} call. */ _define_property$1(this, "errors", void 0);
|
|
719
756
|
_define_property$1(this, "stackErrors", void 0);
|
|
720
757
|
_define_property$1(this, "stackPath", void 0);
|
|
721
758
|
this.def = def;
|
|
@@ -861,14 +898,12 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
861
898
|
//#region packages/typescript/src/json-schema.ts
|
|
862
899
|
function buildJsonSchema(type) {
|
|
863
900
|
const build = (def) => {
|
|
864
|
-
const t = def.type;
|
|
865
901
|
const meta = def.metadata;
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
const obj = t;
|
|
902
|
+
return forAnnotatedType(def, {
|
|
903
|
+
object(d) {
|
|
869
904
|
const properties = {};
|
|
870
905
|
const required = [];
|
|
871
|
-
for (const [key, val] of
|
|
906
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
872
907
|
properties[key] = build(val);
|
|
873
908
|
if (!val.optional) required.push(key);
|
|
874
909
|
}
|
|
@@ -878,41 +913,36 @@ function buildJsonSchema(type) {
|
|
|
878
913
|
};
|
|
879
914
|
if (required.length) schema.required = required;
|
|
880
915
|
return schema;
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
const arr = t;
|
|
916
|
+
},
|
|
917
|
+
array(d) {
|
|
884
918
|
const schema = {
|
|
885
919
|
type: "array",
|
|
886
|
-
items: build(
|
|
920
|
+
items: build(d.type.of)
|
|
887
921
|
};
|
|
888
922
|
const minLength = meta.get("expect.minLength");
|
|
889
923
|
if (typeof minLength === "number") schema.minItems = minLength;
|
|
890
924
|
const maxLength = meta.get("expect.maxLength");
|
|
891
925
|
if (typeof maxLength === "number") schema.maxItems = maxLength;
|
|
892
926
|
return schema;
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
}
|
|
902
|
-
case "tuple": {
|
|
903
|
-
const grp = t;
|
|
927
|
+
},
|
|
928
|
+
union(d) {
|
|
929
|
+
return { anyOf: d.type.items.map(build) };
|
|
930
|
+
},
|
|
931
|
+
intersection(d) {
|
|
932
|
+
return { allOf: d.type.items.map(build) };
|
|
933
|
+
},
|
|
934
|
+
tuple(d) {
|
|
904
935
|
return {
|
|
905
936
|
type: "array",
|
|
906
|
-
items:
|
|
937
|
+
items: d.type.items.map(build),
|
|
907
938
|
additionalItems: false
|
|
908
939
|
};
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
const fin = t;
|
|
940
|
+
},
|
|
941
|
+
final(d) {
|
|
912
942
|
const schema = {};
|
|
913
|
-
if (
|
|
914
|
-
if (
|
|
915
|
-
schema.type =
|
|
943
|
+
if (d.type.value !== undefined) schema.const = d.type.value;
|
|
944
|
+
if (d.type.designType && d.type.designType !== "any") {
|
|
945
|
+
schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
916
946
|
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
917
947
|
}
|
|
918
948
|
if (schema.type === "string") {
|
|
@@ -932,8 +962,7 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
|
|
|
932
962
|
}
|
|
933
963
|
return schema;
|
|
934
964
|
}
|
|
935
|
-
|
|
936
|
-
}
|
|
965
|
+
});
|
|
937
966
|
};
|
|
938
967
|
return build(type);
|
|
939
968
|
}
|
|
@@ -955,7 +984,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
955
984
|
this.writeln("// prettier-ignore-start");
|
|
956
985
|
this.writeln("/* eslint-disable */");
|
|
957
986
|
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
958
|
-
if (
|
|
987
|
+
if (resolveJsonSchemaMode(this.opts) === "lazy") imports.push("buildJsonSchema as $$");
|
|
959
988
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
960
989
|
}
|
|
961
990
|
buildAdHocMap(annotateNodes) {
|
|
@@ -1043,15 +1072,21 @@ else {
|
|
|
1043
1072
|
this.writeln();
|
|
1044
1073
|
}
|
|
1045
1074
|
renderJsonSchemaMethod(node) {
|
|
1046
|
-
|
|
1075
|
+
const mode = resolveJsonSchemaMode(this.opts);
|
|
1076
|
+
const hasAnnotation = node.countAnnotations("emit.jsonSchema") > 0;
|
|
1077
|
+
if (hasAnnotation || mode === "bundle") {
|
|
1047
1078
|
const schema = JSON.stringify(buildJsonSchema(this.toAnnotatedType(node)));
|
|
1048
1079
|
this.writeln("static toJsonSchema() {");
|
|
1049
1080
|
this.indent().writeln(`return ${schema}`).unindent();
|
|
1050
1081
|
this.writeln("}");
|
|
1051
|
-
} else {
|
|
1082
|
+
} else if (mode === "lazy") {
|
|
1052
1083
|
this.writeln("static toJsonSchema() {");
|
|
1053
1084
|
this.indent().writeln("return this._jsonSchema ?? (this._jsonSchema = $$(this))").unindent();
|
|
1054
1085
|
this.writeln("}");
|
|
1086
|
+
} else {
|
|
1087
|
+
this.writeln("static toJsonSchema() {");
|
|
1088
|
+
this.indent().writeln("throw new Error(\"JSON Schema support is disabled. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add @emit.jsonSchema annotation to individual interfaces.\")").unindent();
|
|
1089
|
+
this.writeln("}");
|
|
1055
1090
|
}
|
|
1056
1091
|
}
|
|
1057
1092
|
toAnnotatedType(node) {
|
|
@@ -1494,6 +1529,10 @@ else {
|
|
|
1494
1529
|
|
|
1495
1530
|
//#endregion
|
|
1496
1531
|
//#region packages/typescript/src/plugin.ts
|
|
1532
|
+
function resolveJsonSchemaMode(opts) {
|
|
1533
|
+
if (opts?.jsonSchema !== undefined) return opts.jsonSchema;
|
|
1534
|
+
return false;
|
|
1535
|
+
}
|
|
1497
1536
|
const tsPlugin = (opts) => {
|
|
1498
1537
|
return {
|
|
1499
1538
|
name: "typesccript",
|
package/dist/utils.cjs
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
//#region packages/typescript/src/traverse.ts
|
|
4
|
+
function forAnnotatedType(def, handlers) {
|
|
5
|
+
switch (def.type.kind) {
|
|
6
|
+
case "": return handlers.final(def);
|
|
7
|
+
case "object": return handlers.object(def);
|
|
8
|
+
case "array": return handlers.array(def);
|
|
9
|
+
case "union": return handlers.union(def);
|
|
10
|
+
case "intersection": return handlers.intersection(def);
|
|
11
|
+
case "tuple": return handlers.tuple(def);
|
|
12
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
3
17
|
//#region packages/typescript/src/validator.ts
|
|
4
18
|
function _define_property(obj, key, value) {
|
|
5
19
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -44,7 +58,17 @@ var Validator = class {
|
|
|
44
58
|
throw() {
|
|
45
59
|
throw new ValidatorError(this.errors);
|
|
46
60
|
}
|
|
47
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Validates a value against the type definition.
|
|
63
|
+
*
|
|
64
|
+
* Acts as a TypeScript type guard — when it returns `true`, the value
|
|
65
|
+
* is narrowed to `DataType`.
|
|
66
|
+
*
|
|
67
|
+
* @param value - The value to validate.
|
|
68
|
+
* @param safe - If `true`, returns `false` on failure instead of throwing.
|
|
69
|
+
* @returns `true` if the value matches the type definition.
|
|
70
|
+
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
71
|
+
*/ validate(value, safe) {
|
|
48
72
|
this.push("");
|
|
49
73
|
this.errors = [];
|
|
50
74
|
this.stackErrors = [];
|
|
@@ -71,15 +95,14 @@ var Validator = class {
|
|
|
71
95
|
return this.stackPath.slice(1).join(".");
|
|
72
96
|
}
|
|
73
97
|
validateAnnotatedType(def, value) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
98
|
+
return forAnnotatedType(def, {
|
|
99
|
+
final: (d) => this.validatePrimitive(d, value),
|
|
100
|
+
object: (d) => this.validateObject(d, value),
|
|
101
|
+
array: (d) => this.validateArray(d, value),
|
|
102
|
+
union: (d) => this.validateUnion(d, value),
|
|
103
|
+
intersection: (d) => this.validateIntersection(d, value),
|
|
104
|
+
tuple: (d) => this.validateTuple(d, value)
|
|
105
|
+
});
|
|
83
106
|
}
|
|
84
107
|
validateUnion(def, value) {
|
|
85
108
|
let i = 0;
|
|
@@ -311,7 +334,7 @@ else {
|
|
|
311
334
|
constructor(def, opts) {
|
|
312
335
|
_define_property(this, "def", void 0);
|
|
313
336
|
_define_property(this, "opts", void 0);
|
|
314
|
-
_define_property(this, "errors", void 0);
|
|
337
|
+
/** Validation errors collected during the last {@link validate} call. */ _define_property(this, "errors", void 0);
|
|
315
338
|
_define_property(this, "stackErrors", void 0);
|
|
316
339
|
_define_property(this, "stackPath", void 0);
|
|
317
340
|
this.def = def;
|
|
@@ -470,14 +493,12 @@ function isAnnotatedTypeOfPrimitive(t) {
|
|
|
470
493
|
//#region packages/typescript/src/json-schema.ts
|
|
471
494
|
function buildJsonSchema(type) {
|
|
472
495
|
const build = (def) => {
|
|
473
|
-
const t = def.type;
|
|
474
496
|
const meta = def.metadata;
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
const obj = t;
|
|
497
|
+
return forAnnotatedType(def, {
|
|
498
|
+
object(d) {
|
|
478
499
|
const properties = {};
|
|
479
500
|
const required = [];
|
|
480
|
-
for (const [key, val] of
|
|
501
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
481
502
|
properties[key] = build(val);
|
|
482
503
|
if (!val.optional) required.push(key);
|
|
483
504
|
}
|
|
@@ -487,41 +508,36 @@ function buildJsonSchema(type) {
|
|
|
487
508
|
};
|
|
488
509
|
if (required.length) schema.required = required;
|
|
489
510
|
return schema;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const arr = t;
|
|
511
|
+
},
|
|
512
|
+
array(d) {
|
|
493
513
|
const schema = {
|
|
494
514
|
type: "array",
|
|
495
|
-
items: build(
|
|
515
|
+
items: build(d.type.of)
|
|
496
516
|
};
|
|
497
517
|
const minLength = meta.get("expect.minLength");
|
|
498
518
|
if (typeof minLength === "number") schema.minItems = minLength;
|
|
499
519
|
const maxLength = meta.get("expect.maxLength");
|
|
500
520
|
if (typeof maxLength === "number") schema.maxItems = maxLength;
|
|
501
521
|
return schema;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
}
|
|
511
|
-
case "tuple": {
|
|
512
|
-
const grp = t;
|
|
522
|
+
},
|
|
523
|
+
union(d) {
|
|
524
|
+
return { anyOf: d.type.items.map(build) };
|
|
525
|
+
},
|
|
526
|
+
intersection(d) {
|
|
527
|
+
return { allOf: d.type.items.map(build) };
|
|
528
|
+
},
|
|
529
|
+
tuple(d) {
|
|
513
530
|
return {
|
|
514
531
|
type: "array",
|
|
515
|
-
items:
|
|
532
|
+
items: d.type.items.map(build),
|
|
516
533
|
additionalItems: false
|
|
517
534
|
};
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
const fin = t;
|
|
535
|
+
},
|
|
536
|
+
final(d) {
|
|
521
537
|
const schema = {};
|
|
522
|
-
if (
|
|
523
|
-
if (
|
|
524
|
-
schema.type =
|
|
538
|
+
if (d.type.value !== undefined) schema.const = d.type.value;
|
|
539
|
+
if (d.type.designType && d.type.designType !== "any") {
|
|
540
|
+
schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
525
541
|
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
526
542
|
}
|
|
527
543
|
if (schema.type === "string") {
|
|
@@ -541,17 +557,266 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
|
|
|
541
557
|
}
|
|
542
558
|
return schema;
|
|
543
559
|
}
|
|
544
|
-
|
|
545
|
-
}
|
|
560
|
+
});
|
|
546
561
|
};
|
|
547
562
|
return build(type);
|
|
548
563
|
}
|
|
564
|
+
function fromJsonSchema(schema) {
|
|
565
|
+
const convert = (s) => {
|
|
566
|
+
if (!s || Object.keys(s).length === 0) return defineAnnotatedType().designType("any").$type;
|
|
567
|
+
if (s.$ref) throw new Error("$ref is not supported by fromJsonSchema. Dereference the schema first.");
|
|
568
|
+
if ("const" in s) {
|
|
569
|
+
const val = s.const;
|
|
570
|
+
const dt = val === null ? "null" : typeof val;
|
|
571
|
+
return defineAnnotatedType().designType(dt).value(val).$type;
|
|
572
|
+
}
|
|
573
|
+
if (s.enum) {
|
|
574
|
+
const handle = defineAnnotatedType("union");
|
|
575
|
+
for (const val of s.enum) {
|
|
576
|
+
const dt = val === null ? "null" : typeof val;
|
|
577
|
+
handle.item(defineAnnotatedType().designType(dt).value(val).$type);
|
|
578
|
+
}
|
|
579
|
+
return handle.$type;
|
|
580
|
+
}
|
|
581
|
+
if (s.anyOf) {
|
|
582
|
+
const handle = defineAnnotatedType("union");
|
|
583
|
+
for (const item of s.anyOf) handle.item(convert(item));
|
|
584
|
+
return handle.$type;
|
|
585
|
+
}
|
|
586
|
+
if (s.oneOf) {
|
|
587
|
+
const handle = defineAnnotatedType("union");
|
|
588
|
+
for (const item of s.oneOf) handle.item(convert(item));
|
|
589
|
+
return handle.$type;
|
|
590
|
+
}
|
|
591
|
+
if (s.allOf && !s.type) {
|
|
592
|
+
const handle = defineAnnotatedType("intersection");
|
|
593
|
+
for (const item of s.allOf) handle.item(convert(item));
|
|
594
|
+
return handle.$type;
|
|
595
|
+
}
|
|
596
|
+
if (Array.isArray(s.type)) {
|
|
597
|
+
const handle = defineAnnotatedType("union");
|
|
598
|
+
for (const t of s.type) handle.item(convert({
|
|
599
|
+
...s,
|
|
600
|
+
type: t
|
|
601
|
+
}));
|
|
602
|
+
return handle.$type;
|
|
603
|
+
}
|
|
604
|
+
if (s.type === "object") {
|
|
605
|
+
const handle = defineAnnotatedType("object");
|
|
606
|
+
const required = new Set(s.required || []);
|
|
607
|
+
if (s.properties) for (const [key, propSchema] of Object.entries(s.properties)) {
|
|
608
|
+
const propType = convert(propSchema);
|
|
609
|
+
if (!required.has(key)) propType.optional = true;
|
|
610
|
+
handle.prop(key, propType);
|
|
611
|
+
}
|
|
612
|
+
return handle.$type;
|
|
613
|
+
}
|
|
614
|
+
if (s.type === "array") {
|
|
615
|
+
if (Array.isArray(s.items)) {
|
|
616
|
+
const handle$1 = defineAnnotatedType("tuple");
|
|
617
|
+
for (const item of s.items) handle$1.item(convert(item));
|
|
618
|
+
return handle$1.$type;
|
|
619
|
+
}
|
|
620
|
+
const itemType = s.items ? convert(s.items) : defineAnnotatedType().designType("any").$type;
|
|
621
|
+
const handle = defineAnnotatedType("array").of(itemType);
|
|
622
|
+
if (typeof s.minItems === "number") handle.annotate("expect.minLength", s.minItems);
|
|
623
|
+
if (typeof s.maxItems === "number") handle.annotate("expect.maxLength", s.maxItems);
|
|
624
|
+
return handle.$type;
|
|
625
|
+
}
|
|
626
|
+
if (s.type === "string") {
|
|
627
|
+
const handle = defineAnnotatedType().designType("string").tags("string");
|
|
628
|
+
if (typeof s.minLength === "number") handle.annotate("expect.minLength", s.minLength);
|
|
629
|
+
if (typeof s.maxLength === "number") handle.annotate("expect.maxLength", s.maxLength);
|
|
630
|
+
if (s.pattern) handle.annotate("expect.pattern", { pattern: s.pattern }, true);
|
|
631
|
+
if (s.allOf) {
|
|
632
|
+
for (const item of s.allOf) if (item.pattern) handle.annotate("expect.pattern", { pattern: item.pattern }, true);
|
|
633
|
+
}
|
|
634
|
+
return handle.$type;
|
|
635
|
+
}
|
|
636
|
+
if (s.type === "integer") {
|
|
637
|
+
const handle = defineAnnotatedType().designType("number").tags("number");
|
|
638
|
+
handle.annotate("expect.int", true);
|
|
639
|
+
if (typeof s.minimum === "number") handle.annotate("expect.min", s.minimum);
|
|
640
|
+
if (typeof s.maximum === "number") handle.annotate("expect.max", s.maximum);
|
|
641
|
+
return handle.$type;
|
|
642
|
+
}
|
|
643
|
+
if (s.type === "number") {
|
|
644
|
+
const handle = defineAnnotatedType().designType("number").tags("number");
|
|
645
|
+
if (typeof s.minimum === "number") handle.annotate("expect.min", s.minimum);
|
|
646
|
+
if (typeof s.maximum === "number") handle.annotate("expect.max", s.maximum);
|
|
647
|
+
return handle.$type;
|
|
648
|
+
}
|
|
649
|
+
if (s.type === "boolean") return defineAnnotatedType().designType("boolean").tags("boolean").$type;
|
|
650
|
+
if (s.type === "null") return defineAnnotatedType().designType("null").tags("null").$type;
|
|
651
|
+
return defineAnnotatedType().designType("any").$type;
|
|
652
|
+
};
|
|
653
|
+
return convert(schema);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
//#endregion
|
|
657
|
+
//#region packages/typescript/src/serialize.ts
|
|
658
|
+
const SERIALIZE_VERSION = 1;
|
|
659
|
+
function serializeAnnotatedType(type, options) {
|
|
660
|
+
const result = serializeNode(type, [], options);
|
|
661
|
+
result.$v = SERIALIZE_VERSION;
|
|
662
|
+
return result;
|
|
663
|
+
}
|
|
664
|
+
function serializeNode(def, path, options) {
|
|
665
|
+
const result = {
|
|
666
|
+
type: serializeTypeDef(def, path, options),
|
|
667
|
+
metadata: serializeMetadata(def.metadata, path, def.type.kind, options)
|
|
668
|
+
};
|
|
669
|
+
if (def.optional) result.optional = true;
|
|
670
|
+
return result;
|
|
671
|
+
}
|
|
672
|
+
function serializeTypeDef(def, path, options) {
|
|
673
|
+
return forAnnotatedType(def, {
|
|
674
|
+
final(d) {
|
|
675
|
+
const result = {
|
|
676
|
+
kind: "",
|
|
677
|
+
designType: d.type.designType,
|
|
678
|
+
tags: Array.from(d.type.tags)
|
|
679
|
+
};
|
|
680
|
+
if (d.type.value !== undefined) result.value = d.type.value;
|
|
681
|
+
return result;
|
|
682
|
+
},
|
|
683
|
+
object(d) {
|
|
684
|
+
const props = {};
|
|
685
|
+
for (const [key, val] of d.type.props.entries()) props[key] = serializeNode(val, [...path, key], options);
|
|
686
|
+
const propsPatterns = d.type.propsPatterns.map((pp) => ({
|
|
687
|
+
pattern: {
|
|
688
|
+
source: pp.pattern.source,
|
|
689
|
+
flags: pp.pattern.flags
|
|
690
|
+
},
|
|
691
|
+
def: serializeNode(pp.def, path, options)
|
|
692
|
+
}));
|
|
693
|
+
return {
|
|
694
|
+
kind: "object",
|
|
695
|
+
props,
|
|
696
|
+
propsPatterns,
|
|
697
|
+
tags: Array.from(d.type.tags)
|
|
698
|
+
};
|
|
699
|
+
},
|
|
700
|
+
array(d) {
|
|
701
|
+
return {
|
|
702
|
+
kind: "array",
|
|
703
|
+
of: serializeNode(d.type.of, path, options),
|
|
704
|
+
tags: Array.from(d.type.tags)
|
|
705
|
+
};
|
|
706
|
+
},
|
|
707
|
+
union(d) {
|
|
708
|
+
return {
|
|
709
|
+
kind: "union",
|
|
710
|
+
items: d.type.items.map((item) => serializeNode(item, path, options)),
|
|
711
|
+
tags: Array.from(d.type.tags)
|
|
712
|
+
};
|
|
713
|
+
},
|
|
714
|
+
intersection(d) {
|
|
715
|
+
return {
|
|
716
|
+
kind: "intersection",
|
|
717
|
+
items: d.type.items.map((item) => serializeNode(item, path, options)),
|
|
718
|
+
tags: Array.from(d.type.tags)
|
|
719
|
+
};
|
|
720
|
+
},
|
|
721
|
+
tuple(d) {
|
|
722
|
+
return {
|
|
723
|
+
kind: "tuple",
|
|
724
|
+
items: d.type.items.map((item) => serializeNode(item, path, options)),
|
|
725
|
+
tags: Array.from(d.type.tags)
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
function serializeMetadata(metadata, path, kind, options) {
|
|
731
|
+
const result = {};
|
|
732
|
+
const ignoreSet = options?.ignoreAnnotations ? new Set(options.ignoreAnnotations) : undefined;
|
|
733
|
+
for (const [key, value] of metadata.entries()) {
|
|
734
|
+
if (ignoreSet?.has(key)) continue;
|
|
735
|
+
if (options?.processAnnotation) {
|
|
736
|
+
const processed = options.processAnnotation({
|
|
737
|
+
key,
|
|
738
|
+
value,
|
|
739
|
+
path,
|
|
740
|
+
kind
|
|
741
|
+
});
|
|
742
|
+
if (processed === undefined || processed === null) continue;
|
|
743
|
+
result[processed.key] = processed.value;
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
result[key] = value;
|
|
747
|
+
}
|
|
748
|
+
return result;
|
|
749
|
+
}
|
|
750
|
+
function deserializeAnnotatedType(data) {
|
|
751
|
+
if (data.$v !== SERIALIZE_VERSION) throw new Error(`Unsupported serialized type version: ${data.$v} (expected ${SERIALIZE_VERSION})`);
|
|
752
|
+
return deserializeNode(data);
|
|
753
|
+
}
|
|
754
|
+
function deserializeNode(data) {
|
|
755
|
+
const metadata = new Map(Object.entries(data.metadata));
|
|
756
|
+
const type = deserializeTypeDef(data.type);
|
|
757
|
+
const result = {
|
|
758
|
+
__is_atscript_annotated_type: true,
|
|
759
|
+
type,
|
|
760
|
+
metadata,
|
|
761
|
+
validator(opts) {
|
|
762
|
+
return new Validator(this, opts);
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
if (data.optional) result.optional = true;
|
|
766
|
+
return result;
|
|
767
|
+
}
|
|
768
|
+
function deserializeTypeDef(t) {
|
|
769
|
+
const tags = new Set(t.tags);
|
|
770
|
+
switch (t.kind) {
|
|
771
|
+
case "": {
|
|
772
|
+
const result = {
|
|
773
|
+
kind: "",
|
|
774
|
+
designType: t.designType,
|
|
775
|
+
tags
|
|
776
|
+
};
|
|
777
|
+
if (t.value !== undefined) result.value = t.value;
|
|
778
|
+
return result;
|
|
779
|
+
}
|
|
780
|
+
case "object": {
|
|
781
|
+
const props = new Map();
|
|
782
|
+
for (const [key, val] of Object.entries(t.props)) props.set(key, deserializeNode(val));
|
|
783
|
+
const propsPatterns = t.propsPatterns.map((pp) => ({
|
|
784
|
+
pattern: new RegExp(pp.pattern.source, pp.pattern.flags),
|
|
785
|
+
def: deserializeNode(pp.def)
|
|
786
|
+
}));
|
|
787
|
+
return {
|
|
788
|
+
kind: "object",
|
|
789
|
+
props,
|
|
790
|
+
propsPatterns,
|
|
791
|
+
tags
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
case "array": return {
|
|
795
|
+
kind: "array",
|
|
796
|
+
of: deserializeNode(t.of),
|
|
797
|
+
tags
|
|
798
|
+
};
|
|
799
|
+
case "union":
|
|
800
|
+
case "intersection":
|
|
801
|
+
case "tuple": return {
|
|
802
|
+
kind: t.kind,
|
|
803
|
+
items: t.items.map((item) => deserializeNode(item)),
|
|
804
|
+
tags
|
|
805
|
+
};
|
|
806
|
+
default: throw new Error(`Unknown serialized type kind "${t.kind}"`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
549
809
|
|
|
550
810
|
//#endregion
|
|
811
|
+
exports.SERIALIZE_VERSION = SERIALIZE_VERSION
|
|
551
812
|
exports.Validator = Validator
|
|
552
813
|
exports.ValidatorError = ValidatorError
|
|
553
814
|
exports.annotate = annotate
|
|
554
815
|
exports.buildJsonSchema = buildJsonSchema
|
|
555
816
|
exports.defineAnnotatedType = defineAnnotatedType
|
|
817
|
+
exports.deserializeAnnotatedType = deserializeAnnotatedType
|
|
818
|
+
exports.forAnnotatedType = forAnnotatedType
|
|
819
|
+
exports.fromJsonSchema = fromJsonSchema
|
|
556
820
|
exports.isAnnotatedType = isAnnotatedType
|
|
557
|
-
exports.isAnnotatedTypeOfPrimitive = isAnnotatedTypeOfPrimitive
|
|
821
|
+
exports.isAnnotatedTypeOfPrimitive = isAnnotatedTypeOfPrimitive
|
|
822
|
+
exports.serializeAnnotatedType = serializeAnnotatedType
|