@atscript/typescript 0.1.25 → 0.1.27
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 +50 -0
- package/dist/cli.cjs +140 -46
- package/dist/index.cjs +143 -51
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +141 -51
- package/dist/utils.cjs +163 -39
- package/dist/utils.d.ts +24 -2
- package/dist/utils.mjs +162 -40
- package/package.json +5 -5
- package/scripts/setup-skills.js +23 -13
- package/skills/atscript-typescript/SKILL.md +22 -14
- package/skills/atscript-typescript/annotations.md +51 -50
- package/skills/atscript-typescript/codegen.md +15 -10
- package/skills/atscript-typescript/core.md +23 -21
- package/skills/atscript-typescript/runtime.md +66 -52
- package/skills/atscript-typescript/syntax.md +31 -31
- package/skills/atscript-typescript/utilities.md +137 -72
- package/skills/atscript-typescript/validation.md +39 -39
package/dist/utils.cjs
CHANGED
|
@@ -428,6 +428,63 @@ else metadata.set(key, [a, value]);
|
|
|
428
428
|
} else metadata.set(key, [value]);
|
|
429
429
|
else metadata.set(key, value);
|
|
430
430
|
}
|
|
431
|
+
function cloneRefProp(parentType, propName) {
|
|
432
|
+
if (parentType.kind !== "object") return;
|
|
433
|
+
const objType = parentType;
|
|
434
|
+
const existing = objType.props.get(propName);
|
|
435
|
+
if (!existing) return;
|
|
436
|
+
const clonedType = cloneTypeDef(existing.type);
|
|
437
|
+
objType.props.set(propName, {
|
|
438
|
+
__is_atscript_annotated_type: true,
|
|
439
|
+
type: clonedType,
|
|
440
|
+
metadata: new Map(existing.metadata),
|
|
441
|
+
id: existing.id,
|
|
442
|
+
optional: existing.optional,
|
|
443
|
+
validator(opts) {
|
|
444
|
+
return new Validator(this, opts);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
function cloneTypeDef(type) {
|
|
449
|
+
if (type.kind === "object") {
|
|
450
|
+
const obj = type;
|
|
451
|
+
return {
|
|
452
|
+
kind: "object",
|
|
453
|
+
props: new Map(Array.from(obj.props.entries()).map(([k, v]) => [k, {
|
|
454
|
+
__is_atscript_annotated_type: true,
|
|
455
|
+
type: v.type,
|
|
456
|
+
metadata: new Map(v.metadata),
|
|
457
|
+
id: v.id,
|
|
458
|
+
optional: v.optional,
|
|
459
|
+
validator(opts) {
|
|
460
|
+
return new Validator(this, opts);
|
|
461
|
+
}
|
|
462
|
+
}])),
|
|
463
|
+
propsPatterns: [...obj.propsPatterns],
|
|
464
|
+
tags: new Set(obj.tags)
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
if (type.kind === "array") {
|
|
468
|
+
const arr = type;
|
|
469
|
+
return {
|
|
470
|
+
kind: "array",
|
|
471
|
+
of: arr.of,
|
|
472
|
+
tags: new Set(arr.tags)
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
if (type.kind === "union" || type.kind === "intersection" || type.kind === "tuple") {
|
|
476
|
+
const complex = type;
|
|
477
|
+
return {
|
|
478
|
+
kind: type.kind,
|
|
479
|
+
items: [...complex.items],
|
|
480
|
+
tags: new Set(complex.tags)
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
return {
|
|
484
|
+
...type,
|
|
485
|
+
tags: new Set(type.tags)
|
|
486
|
+
};
|
|
487
|
+
}
|
|
431
488
|
function defineAnnotatedType(_kind, base) {
|
|
432
489
|
const kind = _kind || "";
|
|
433
490
|
const type = base?.type || {};
|
|
@@ -519,6 +576,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
519
576
|
__is_atscript_annotated_type: true,
|
|
520
577
|
type: newBase.type,
|
|
521
578
|
metadata,
|
|
579
|
+
id: newBase.id,
|
|
522
580
|
validator(opts) {
|
|
523
581
|
return new Validator(this, opts);
|
|
524
582
|
}
|
|
@@ -529,6 +587,10 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
529
587
|
annotate(key, value, asArray) {
|
|
530
588
|
annotate(this.$metadata, key, value, asArray);
|
|
531
589
|
return this;
|
|
590
|
+
},
|
|
591
|
+
id(value) {
|
|
592
|
+
this.$type.id = value;
|
|
593
|
+
return this;
|
|
532
594
|
}
|
|
533
595
|
};
|
|
534
596
|
return handle;
|
|
@@ -593,47 +655,71 @@ function isAnnotatedTypeOfPrimitive(t) {
|
|
|
593
655
|
return null;
|
|
594
656
|
}
|
|
595
657
|
function buildJsonSchema(type) {
|
|
658
|
+
const defs = {};
|
|
659
|
+
let isRoot = true;
|
|
660
|
+
const buildObject = (d) => {
|
|
661
|
+
const properties = {};
|
|
662
|
+
const required = [];
|
|
663
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
664
|
+
if (isPhantomType(val)) continue;
|
|
665
|
+
properties[key] = build$1(val);
|
|
666
|
+
if (!val.optional) required.push(key);
|
|
667
|
+
}
|
|
668
|
+
const schema$1 = {
|
|
669
|
+
type: "object",
|
|
670
|
+
properties
|
|
671
|
+
};
|
|
672
|
+
if (required.length > 0) schema$1.required = required;
|
|
673
|
+
return schema$1;
|
|
674
|
+
};
|
|
596
675
|
const build$1 = (def) => {
|
|
676
|
+
if (def.id && def.type.kind === "object" && !isRoot) {
|
|
677
|
+
const name = def.id;
|
|
678
|
+
if (!defs[name]) {
|
|
679
|
+
defs[name] = {};
|
|
680
|
+
defs[name] = buildObject(def);
|
|
681
|
+
}
|
|
682
|
+
return { $ref: `#/$defs/${name}` };
|
|
683
|
+
}
|
|
684
|
+
isRoot = false;
|
|
597
685
|
const meta = def.metadata;
|
|
598
686
|
return forAnnotatedType(def, {
|
|
599
687
|
phantom() {
|
|
600
688
|
return {};
|
|
601
689
|
},
|
|
602
690
|
object(d) {
|
|
603
|
-
|
|
604
|
-
const required = [];
|
|
605
|
-
for (const [key, val] of d.type.props.entries()) {
|
|
606
|
-
if (isPhantomType(val)) continue;
|
|
607
|
-
properties[key] = build$1(val);
|
|
608
|
-
if (!val.optional) required.push(key);
|
|
609
|
-
}
|
|
610
|
-
const schema = {
|
|
611
|
-
type: "object",
|
|
612
|
-
properties
|
|
613
|
-
};
|
|
614
|
-
if (required.length > 0) schema.required = required;
|
|
615
|
-
return schema;
|
|
691
|
+
return buildObject(d);
|
|
616
692
|
},
|
|
617
693
|
array(d) {
|
|
618
|
-
const schema = {
|
|
694
|
+
const schema$1 = {
|
|
619
695
|
type: "array",
|
|
620
696
|
items: build$1(d.type.of)
|
|
621
697
|
};
|
|
622
698
|
const minLength = meta.get("expect.minLength");
|
|
623
|
-
if (minLength) schema.minItems = typeof minLength === "number" ? minLength : minLength.length;
|
|
699
|
+
if (minLength) schema$1.minItems = typeof minLength === "number" ? minLength : minLength.length;
|
|
624
700
|
const maxLength = meta.get("expect.maxLength");
|
|
625
|
-
if (maxLength) schema.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
626
|
-
return schema;
|
|
701
|
+
if (maxLength) schema$1.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
702
|
+
return schema$1;
|
|
627
703
|
},
|
|
628
704
|
union(d) {
|
|
629
705
|
const disc = detectDiscriminator(d.type.items);
|
|
630
|
-
if (disc)
|
|
631
|
-
oneOf
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
706
|
+
if (disc) {
|
|
707
|
+
const oneOf = d.type.items.map(build$1);
|
|
708
|
+
const mapping = {};
|
|
709
|
+
for (const [val, origPath] of Object.entries(disc.mapping)) {
|
|
710
|
+
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
711
|
+
const item = d.type.items[idx];
|
|
712
|
+
if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
|
|
713
|
+
else mapping[val] = origPath;
|
|
635
714
|
}
|
|
636
|
-
|
|
715
|
+
return {
|
|
716
|
+
oneOf,
|
|
717
|
+
discriminator: {
|
|
718
|
+
propertyName: disc.propertyName,
|
|
719
|
+
mapping
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
}
|
|
637
723
|
return { anyOf: d.type.items.map(build$1) };
|
|
638
724
|
},
|
|
639
725
|
intersection(d) {
|
|
@@ -647,38 +733,54 @@ function buildJsonSchema(type) {
|
|
|
647
733
|
};
|
|
648
734
|
},
|
|
649
735
|
final(d) {
|
|
650
|
-
const schema = {};
|
|
651
|
-
if (d.type.value !== undefined) schema.const = d.type.value;
|
|
736
|
+
const schema$1 = {};
|
|
737
|
+
if (d.type.value !== undefined) schema$1.const = d.type.value;
|
|
652
738
|
if (d.type.designType && d.type.designType !== "any") {
|
|
653
|
-
schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
654
|
-
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
739
|
+
schema$1.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
740
|
+
if (schema$1.type === "number" && meta.get("expect.int")) schema$1.type = "integer";
|
|
655
741
|
}
|
|
656
|
-
if (schema.type === "string") {
|
|
657
|
-
if (meta.get("meta.required")) schema.minLength = 1;
|
|
742
|
+
if (schema$1.type === "string") {
|
|
743
|
+
if (meta.get("meta.required")) schema$1.minLength = 1;
|
|
658
744
|
const minLength = meta.get("expect.minLength");
|
|
659
|
-
if (minLength) schema.minLength = typeof minLength === "number" ? minLength : minLength.length;
|
|
745
|
+
if (minLength) schema$1.minLength = typeof minLength === "number" ? minLength : minLength.length;
|
|
660
746
|
const maxLength = meta.get("expect.maxLength");
|
|
661
|
-
if (maxLength) schema.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
747
|
+
if (maxLength) schema$1.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
662
748
|
const patterns = meta.get("expect.pattern");
|
|
663
|
-
if (patterns?.length) if (patterns.length === 1) schema.pattern = patterns[0].pattern;
|
|
664
|
-
else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
|
|
749
|
+
if (patterns?.length) if (patterns.length === 1) schema$1.pattern = patterns[0].pattern;
|
|
750
|
+
else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
|
|
665
751
|
}
|
|
666
|
-
if (schema.type === "number" || schema.type === "integer") {
|
|
752
|
+
if (schema$1.type === "number" || schema$1.type === "integer") {
|
|
667
753
|
const min = meta.get("expect.min");
|
|
668
|
-
if (min) schema.minimum = typeof min === "number" ? min : min.minValue;
|
|
754
|
+
if (min) schema$1.minimum = typeof min === "number" ? min : min.minValue;
|
|
669
755
|
const max = meta.get("expect.max");
|
|
670
|
-
if (max) schema.maximum = typeof max === "number" ? max : max.maxValue;
|
|
756
|
+
if (max) schema$1.maximum = typeof max === "number" ? max : max.maxValue;
|
|
671
757
|
}
|
|
672
|
-
return schema;
|
|
758
|
+
return schema$1;
|
|
673
759
|
}
|
|
674
760
|
});
|
|
675
761
|
};
|
|
676
|
-
|
|
762
|
+
const schema = build$1(type);
|
|
763
|
+
if (Object.keys(defs).length > 0) return {
|
|
764
|
+
...schema,
|
|
765
|
+
$defs: defs
|
|
766
|
+
};
|
|
767
|
+
return schema;
|
|
677
768
|
}
|
|
678
769
|
function fromJsonSchema(schema) {
|
|
770
|
+
const defsSource = schema.$defs || schema.definitions || {};
|
|
771
|
+
const resolved = new Map();
|
|
679
772
|
const convert = (s) => {
|
|
680
773
|
if (!s || Object.keys(s).length === 0) return defineAnnotatedType().designType("any").$type;
|
|
681
|
-
if (s.$ref)
|
|
774
|
+
if (s.$ref) {
|
|
775
|
+
const refName = s.$ref.replace(/^#\/(\$defs|definitions)\//, "");
|
|
776
|
+
if (resolved.has(refName)) return resolved.get(refName);
|
|
777
|
+
if (defsSource[refName]) {
|
|
778
|
+
const type = convert(defsSource[refName]);
|
|
779
|
+
resolved.set(refName, type);
|
|
780
|
+
return type;
|
|
781
|
+
}
|
|
782
|
+
throw new Error(`Unresolvable $ref: ${s.$ref}`);
|
|
783
|
+
}
|
|
682
784
|
if ("const" in s) {
|
|
683
785
|
const val = s.const;
|
|
684
786
|
const dt = val === null ? "null" : typeof val;
|
|
@@ -766,6 +868,24 @@ function fromJsonSchema(schema) {
|
|
|
766
868
|
};
|
|
767
869
|
return convert(schema);
|
|
768
870
|
}
|
|
871
|
+
function mergeJsonSchemas(types) {
|
|
872
|
+
const mergedDefs = {};
|
|
873
|
+
const schemas = {};
|
|
874
|
+
for (const type of types) {
|
|
875
|
+
const name = type.id;
|
|
876
|
+
if (!name) throw new Error("mergeJsonSchemas: all types must have an id");
|
|
877
|
+
const schema = buildJsonSchema(type);
|
|
878
|
+
if (schema.$defs) {
|
|
879
|
+
for (const [defName, defSchema] of Object.entries(schema.$defs)) if (!mergedDefs[defName]) mergedDefs[defName] = defSchema;
|
|
880
|
+
const { $defs: _,...rest } = schema;
|
|
881
|
+
schemas[name] = rest;
|
|
882
|
+
} else schemas[name] = schema;
|
|
883
|
+
}
|
|
884
|
+
return {
|
|
885
|
+
schemas,
|
|
886
|
+
$defs: mergedDefs
|
|
887
|
+
};
|
|
888
|
+
}
|
|
769
889
|
|
|
770
890
|
//#endregion
|
|
771
891
|
//#region packages/typescript/src/default-value.ts
|
|
@@ -959,6 +1079,7 @@ function serializeNode(def, path, options) {
|
|
|
959
1079
|
metadata: serializeMetadata(def.metadata, path, def.type.kind, options)
|
|
960
1080
|
};
|
|
961
1081
|
if (def.optional) result.optional = true;
|
|
1082
|
+
if (def.id) result.id = def.id;
|
|
962
1083
|
return result;
|
|
963
1084
|
}
|
|
964
1085
|
function serializeTypeDef(def, path, options) {
|
|
@@ -1062,6 +1183,7 @@ function deserializeNode(data) {
|
|
|
1062
1183
|
}
|
|
1063
1184
|
};
|
|
1064
1185
|
if (data.optional) result.optional = true;
|
|
1186
|
+
if (data.id) result.id = data.id;
|
|
1065
1187
|
return result;
|
|
1066
1188
|
}
|
|
1067
1189
|
function deserializeTypeDef(t) {
|
|
@@ -1112,6 +1234,7 @@ exports.Validator = Validator
|
|
|
1112
1234
|
exports.ValidatorError = ValidatorError
|
|
1113
1235
|
exports.annotate = annotate
|
|
1114
1236
|
exports.buildJsonSchema = buildJsonSchema
|
|
1237
|
+
exports.cloneRefProp = cloneRefProp
|
|
1115
1238
|
exports.createDataFromAnnotatedType = createDataFromAnnotatedType
|
|
1116
1239
|
exports.defineAnnotatedType = defineAnnotatedType
|
|
1117
1240
|
exports.deserializeAnnotatedType = deserializeAnnotatedType
|
|
@@ -1121,5 +1244,6 @@ exports.fromJsonSchema = fromJsonSchema
|
|
|
1121
1244
|
exports.isAnnotatedType = isAnnotatedType
|
|
1122
1245
|
exports.isAnnotatedTypeOfPrimitive = isAnnotatedTypeOfPrimitive
|
|
1123
1246
|
exports.isPhantomType = isPhantomType
|
|
1247
|
+
exports.mergeJsonSchemas = mergeJsonSchemas
|
|
1124
1248
|
exports.serializeAnnotatedType = serializeAnnotatedType
|
|
1125
1249
|
exports.throwFeatureDisabled = throwFeatureDisabled
|
package/dist/utils.d.ts
CHANGED
|
@@ -181,6 +181,7 @@ interface TAtscriptAnnotatedType<T extends TAtscriptTypeDef = TAtscriptTypeDef,
|
|
|
181
181
|
validator(opts?: Partial<TValidatorOptions>): Validator<this, DataType>;
|
|
182
182
|
metadata: TMetadataMap<AtscriptMetadata>;
|
|
183
183
|
optional?: boolean;
|
|
184
|
+
id?: string;
|
|
184
185
|
}
|
|
185
186
|
/** An annotated type that is also a class constructor (i.e. a generated interface class). */
|
|
186
187
|
type TAtscriptAnnotatedTypeConstructor = TAtscriptAnnotatedType & (new (...args: any[]) => any);
|
|
@@ -193,6 +194,11 @@ declare function isAnnotatedType(type: any): type is TAtscriptAnnotatedType;
|
|
|
193
194
|
* Used by the handle's .annotate() method and by generated mutation statements.
|
|
194
195
|
*/
|
|
195
196
|
declare function annotate<K extends keyof AtscriptMetadata>(metadata: TMetadataMap<AtscriptMetadata> | undefined, key: K, value: AtscriptMetadata[K] extends Array<infer E> ? E : AtscriptMetadata[K], asArray?: boolean): void;
|
|
197
|
+
/**
|
|
198
|
+
* Clones a property's type tree in-place so mutations don't leak to shared refs.
|
|
199
|
+
* Used by mutating annotate codegen when paths cross ref boundaries.
|
|
200
|
+
*/
|
|
201
|
+
declare function cloneRefProp(parentType: TAtscriptTypeDef, propName: string): void;
|
|
196
202
|
type TKind = '' | 'array' | 'object' | 'union' | 'intersection' | 'tuple';
|
|
197
203
|
/**
|
|
198
204
|
* Creates a builder handle for constructing a {@link TAtscriptAnnotatedType} at runtime.
|
|
@@ -242,6 +248,7 @@ interface TAnnotatedTypeHandle {
|
|
|
242
248
|
name?: string;
|
|
243
249
|
}, chain?: string[]): TAnnotatedTypeHandle;
|
|
244
250
|
annotate(key: keyof AtscriptMetadata, value: any, asArray?: boolean): TAnnotatedTypeHandle;
|
|
251
|
+
id(value: string): TAnnotatedTypeHandle;
|
|
245
252
|
}
|
|
246
253
|
/**
|
|
247
254
|
* Checks whether an annotated type is a phantom type.
|
|
@@ -299,6 +306,20 @@ declare function buildJsonSchema(type: TAtscriptAnnotatedType): TJsonSchema;
|
|
|
299
306
|
* @returns An annotated type with full validator support.
|
|
300
307
|
*/
|
|
301
308
|
declare function fromJsonSchema(schema: TJsonSchema): TAtscriptAnnotatedType;
|
|
309
|
+
/**
|
|
310
|
+
* Merges multiple annotated types into a combined schema map with shared `$defs`.
|
|
311
|
+
*
|
|
312
|
+
* Each type must have an `id`. The returned `schemas` object contains individual
|
|
313
|
+
* schemas keyed by type id, and `$defs` contains all shared type definitions
|
|
314
|
+
* deduplicated across schemas.
|
|
315
|
+
*
|
|
316
|
+
* @param types - Array of annotated types, each with an `id`.
|
|
317
|
+
* @returns An object with `schemas` (keyed by id) and shared `$defs`.
|
|
318
|
+
*/
|
|
319
|
+
declare function mergeJsonSchemas(types: TAtscriptAnnotatedType[]): {
|
|
320
|
+
schemas: Record<string, TJsonSchema>;
|
|
321
|
+
$defs: Record<string, TJsonSchema>;
|
|
322
|
+
};
|
|
302
323
|
|
|
303
324
|
/**
|
|
304
325
|
* Type-safe dispatch over `TAtscriptAnnotatedType` by its `type.kind`.
|
|
@@ -417,6 +438,7 @@ interface TSerializedAnnotatedTypeInner {
|
|
|
417
438
|
type: TSerializedTypeDef;
|
|
418
439
|
metadata: Record<string, unknown>;
|
|
419
440
|
optional?: boolean;
|
|
441
|
+
id?: string;
|
|
420
442
|
}
|
|
421
443
|
interface TSerializedTypeFinal {
|
|
422
444
|
kind: '';
|
|
@@ -511,5 +533,5 @@ declare function serializeAnnotatedType(type: TAtscriptAnnotatedType, options?:
|
|
|
511
533
|
*/
|
|
512
534
|
declare function deserializeAnnotatedType(data: TSerializedAnnotatedType): TAtscriptAnnotatedType;
|
|
513
535
|
|
|
514
|
-
export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, createDataFromAnnotatedType, defineAnnotatedType, deserializeAnnotatedType, flattenAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, serializeAnnotatedType, throwFeatureDisabled };
|
|
515
|
-
export type { InferDataType, TAnnotatedTypeHandle, TAtscriptAnnotatedType, TAtscriptAnnotatedTypeConstructor, TAtscriptDataType, TAtscriptTypeArray, TAtscriptTypeComplex, TAtscriptTypeDef, TAtscriptTypeFinal, TAtscriptTypeObject, TCreateDataOptions, TFlattenOptions, TMetadataMap, TProcessAnnotationContext, TSerializeOptions, TSerializedAnnotatedType, TSerializedAnnotatedTypeInner, TSerializedTypeArray, TSerializedTypeComplex, TSerializedTypeDef, TSerializedTypeFinal, TSerializedTypeObject, TValidatorOptions, TValidatorPlugin, TValidatorPluginContext, TValueResolver };
|
|
536
|
+
export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createDataFromAnnotatedType, defineAnnotatedType, deserializeAnnotatedType, flattenAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas, serializeAnnotatedType, throwFeatureDisabled };
|
|
537
|
+
export type { InferDataType, TAnnotatedTypeHandle, TAtscriptAnnotatedType, TAtscriptAnnotatedTypeConstructor, TAtscriptDataType, TAtscriptTypeArray, TAtscriptTypeComplex, TAtscriptTypeDef, TAtscriptTypeFinal, TAtscriptTypeObject, TCreateDataOptions, TFlattenOptions, TJsonSchema, TMetadataMap, TProcessAnnotationContext, TSerializeOptions, TSerializedAnnotatedType, TSerializedAnnotatedTypeInner, TSerializedTypeArray, TSerializedTypeComplex, TSerializedTypeDef, TSerializedTypeFinal, TSerializedTypeObject, TValidatorOptions, TValidatorPlugin, TValidatorPluginContext, TValueResolver };
|
package/dist/utils.mjs
CHANGED
|
@@ -427,6 +427,63 @@ else metadata.set(key, [a, value]);
|
|
|
427
427
|
} else metadata.set(key, [value]);
|
|
428
428
|
else metadata.set(key, value);
|
|
429
429
|
}
|
|
430
|
+
function cloneRefProp(parentType, propName) {
|
|
431
|
+
if (parentType.kind !== "object") return;
|
|
432
|
+
const objType = parentType;
|
|
433
|
+
const existing = objType.props.get(propName);
|
|
434
|
+
if (!existing) return;
|
|
435
|
+
const clonedType = cloneTypeDef(existing.type);
|
|
436
|
+
objType.props.set(propName, {
|
|
437
|
+
__is_atscript_annotated_type: true,
|
|
438
|
+
type: clonedType,
|
|
439
|
+
metadata: new Map(existing.metadata),
|
|
440
|
+
id: existing.id,
|
|
441
|
+
optional: existing.optional,
|
|
442
|
+
validator(opts) {
|
|
443
|
+
return new Validator(this, opts);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
function cloneTypeDef(type) {
|
|
448
|
+
if (type.kind === "object") {
|
|
449
|
+
const obj = type;
|
|
450
|
+
return {
|
|
451
|
+
kind: "object",
|
|
452
|
+
props: new Map(Array.from(obj.props.entries()).map(([k, v]) => [k, {
|
|
453
|
+
__is_atscript_annotated_type: true,
|
|
454
|
+
type: v.type,
|
|
455
|
+
metadata: new Map(v.metadata),
|
|
456
|
+
id: v.id,
|
|
457
|
+
optional: v.optional,
|
|
458
|
+
validator(opts) {
|
|
459
|
+
return new Validator(this, opts);
|
|
460
|
+
}
|
|
461
|
+
}])),
|
|
462
|
+
propsPatterns: [...obj.propsPatterns],
|
|
463
|
+
tags: new Set(obj.tags)
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
if (type.kind === "array") {
|
|
467
|
+
const arr = type;
|
|
468
|
+
return {
|
|
469
|
+
kind: "array",
|
|
470
|
+
of: arr.of,
|
|
471
|
+
tags: new Set(arr.tags)
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
if (type.kind === "union" || type.kind === "intersection" || type.kind === "tuple") {
|
|
475
|
+
const complex = type;
|
|
476
|
+
return {
|
|
477
|
+
kind: type.kind,
|
|
478
|
+
items: [...complex.items],
|
|
479
|
+
tags: new Set(complex.tags)
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
return {
|
|
483
|
+
...type,
|
|
484
|
+
tags: new Set(type.tags)
|
|
485
|
+
};
|
|
486
|
+
}
|
|
430
487
|
function defineAnnotatedType(_kind, base) {
|
|
431
488
|
const kind = _kind || "";
|
|
432
489
|
const type = base?.type || {};
|
|
@@ -518,6 +575,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
518
575
|
__is_atscript_annotated_type: true,
|
|
519
576
|
type: newBase.type,
|
|
520
577
|
metadata,
|
|
578
|
+
id: newBase.id,
|
|
521
579
|
validator(opts) {
|
|
522
580
|
return new Validator(this, opts);
|
|
523
581
|
}
|
|
@@ -528,6 +586,10 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
528
586
|
annotate(key, value, asArray) {
|
|
529
587
|
annotate(this.$metadata, key, value, asArray);
|
|
530
588
|
return this;
|
|
589
|
+
},
|
|
590
|
+
id(value) {
|
|
591
|
+
this.$type.id = value;
|
|
592
|
+
return this;
|
|
531
593
|
}
|
|
532
594
|
};
|
|
533
595
|
return handle;
|
|
@@ -592,47 +654,71 @@ function isAnnotatedTypeOfPrimitive(t) {
|
|
|
592
654
|
return null;
|
|
593
655
|
}
|
|
594
656
|
function buildJsonSchema(type) {
|
|
657
|
+
const defs = {};
|
|
658
|
+
let isRoot = true;
|
|
659
|
+
const buildObject = (d) => {
|
|
660
|
+
const properties = {};
|
|
661
|
+
const required = [];
|
|
662
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
663
|
+
if (isPhantomType(val)) continue;
|
|
664
|
+
properties[key] = build$1(val);
|
|
665
|
+
if (!val.optional) required.push(key);
|
|
666
|
+
}
|
|
667
|
+
const schema$1 = {
|
|
668
|
+
type: "object",
|
|
669
|
+
properties
|
|
670
|
+
};
|
|
671
|
+
if (required.length > 0) schema$1.required = required;
|
|
672
|
+
return schema$1;
|
|
673
|
+
};
|
|
595
674
|
const build$1 = (def) => {
|
|
675
|
+
if (def.id && def.type.kind === "object" && !isRoot) {
|
|
676
|
+
const name = def.id;
|
|
677
|
+
if (!defs[name]) {
|
|
678
|
+
defs[name] = {};
|
|
679
|
+
defs[name] = buildObject(def);
|
|
680
|
+
}
|
|
681
|
+
return { $ref: `#/$defs/${name}` };
|
|
682
|
+
}
|
|
683
|
+
isRoot = false;
|
|
596
684
|
const meta = def.metadata;
|
|
597
685
|
return forAnnotatedType(def, {
|
|
598
686
|
phantom() {
|
|
599
687
|
return {};
|
|
600
688
|
},
|
|
601
689
|
object(d) {
|
|
602
|
-
|
|
603
|
-
const required = [];
|
|
604
|
-
for (const [key, val] of d.type.props.entries()) {
|
|
605
|
-
if (isPhantomType(val)) continue;
|
|
606
|
-
properties[key] = build$1(val);
|
|
607
|
-
if (!val.optional) required.push(key);
|
|
608
|
-
}
|
|
609
|
-
const schema = {
|
|
610
|
-
type: "object",
|
|
611
|
-
properties
|
|
612
|
-
};
|
|
613
|
-
if (required.length > 0) schema.required = required;
|
|
614
|
-
return schema;
|
|
690
|
+
return buildObject(d);
|
|
615
691
|
},
|
|
616
692
|
array(d) {
|
|
617
|
-
const schema = {
|
|
693
|
+
const schema$1 = {
|
|
618
694
|
type: "array",
|
|
619
695
|
items: build$1(d.type.of)
|
|
620
696
|
};
|
|
621
697
|
const minLength = meta.get("expect.minLength");
|
|
622
|
-
if (minLength) schema.minItems = typeof minLength === "number" ? minLength : minLength.length;
|
|
698
|
+
if (minLength) schema$1.minItems = typeof minLength === "number" ? minLength : minLength.length;
|
|
623
699
|
const maxLength = meta.get("expect.maxLength");
|
|
624
|
-
if (maxLength) schema.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
625
|
-
return schema;
|
|
700
|
+
if (maxLength) schema$1.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
701
|
+
return schema$1;
|
|
626
702
|
},
|
|
627
703
|
union(d) {
|
|
628
704
|
const disc = detectDiscriminator(d.type.items);
|
|
629
|
-
if (disc)
|
|
630
|
-
oneOf
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
705
|
+
if (disc) {
|
|
706
|
+
const oneOf = d.type.items.map(build$1);
|
|
707
|
+
const mapping = {};
|
|
708
|
+
for (const [val, origPath] of Object.entries(disc.mapping)) {
|
|
709
|
+
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
710
|
+
const item = d.type.items[idx];
|
|
711
|
+
if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
|
|
712
|
+
else mapping[val] = origPath;
|
|
634
713
|
}
|
|
635
|
-
|
|
714
|
+
return {
|
|
715
|
+
oneOf,
|
|
716
|
+
discriminator: {
|
|
717
|
+
propertyName: disc.propertyName,
|
|
718
|
+
mapping
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
}
|
|
636
722
|
return { anyOf: d.type.items.map(build$1) };
|
|
637
723
|
},
|
|
638
724
|
intersection(d) {
|
|
@@ -646,38 +732,54 @@ function buildJsonSchema(type) {
|
|
|
646
732
|
};
|
|
647
733
|
},
|
|
648
734
|
final(d) {
|
|
649
|
-
const schema = {};
|
|
650
|
-
if (d.type.value !== undefined) schema.const = d.type.value;
|
|
735
|
+
const schema$1 = {};
|
|
736
|
+
if (d.type.value !== undefined) schema$1.const = d.type.value;
|
|
651
737
|
if (d.type.designType && d.type.designType !== "any") {
|
|
652
|
-
schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
653
|
-
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
738
|
+
schema$1.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
739
|
+
if (schema$1.type === "number" && meta.get("expect.int")) schema$1.type = "integer";
|
|
654
740
|
}
|
|
655
|
-
if (schema.type === "string") {
|
|
656
|
-
if (meta.get("meta.required")) schema.minLength = 1;
|
|
741
|
+
if (schema$1.type === "string") {
|
|
742
|
+
if (meta.get("meta.required")) schema$1.minLength = 1;
|
|
657
743
|
const minLength = meta.get("expect.minLength");
|
|
658
|
-
if (minLength) schema.minLength = typeof minLength === "number" ? minLength : minLength.length;
|
|
744
|
+
if (minLength) schema$1.minLength = typeof minLength === "number" ? minLength : minLength.length;
|
|
659
745
|
const maxLength = meta.get("expect.maxLength");
|
|
660
|
-
if (maxLength) schema.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
746
|
+
if (maxLength) schema$1.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
661
747
|
const patterns = meta.get("expect.pattern");
|
|
662
|
-
if (patterns?.length) if (patterns.length === 1) schema.pattern = patterns[0].pattern;
|
|
663
|
-
else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
|
|
748
|
+
if (patterns?.length) if (patterns.length === 1) schema$1.pattern = patterns[0].pattern;
|
|
749
|
+
else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
|
|
664
750
|
}
|
|
665
|
-
if (schema.type === "number" || schema.type === "integer") {
|
|
751
|
+
if (schema$1.type === "number" || schema$1.type === "integer") {
|
|
666
752
|
const min = meta.get("expect.min");
|
|
667
|
-
if (min) schema.minimum = typeof min === "number" ? min : min.minValue;
|
|
753
|
+
if (min) schema$1.minimum = typeof min === "number" ? min : min.minValue;
|
|
668
754
|
const max = meta.get("expect.max");
|
|
669
|
-
if (max) schema.maximum = typeof max === "number" ? max : max.maxValue;
|
|
755
|
+
if (max) schema$1.maximum = typeof max === "number" ? max : max.maxValue;
|
|
670
756
|
}
|
|
671
|
-
return schema;
|
|
757
|
+
return schema$1;
|
|
672
758
|
}
|
|
673
759
|
});
|
|
674
760
|
};
|
|
675
|
-
|
|
761
|
+
const schema = build$1(type);
|
|
762
|
+
if (Object.keys(defs).length > 0) return {
|
|
763
|
+
...schema,
|
|
764
|
+
$defs: defs
|
|
765
|
+
};
|
|
766
|
+
return schema;
|
|
676
767
|
}
|
|
677
768
|
function fromJsonSchema(schema) {
|
|
769
|
+
const defsSource = schema.$defs || schema.definitions || {};
|
|
770
|
+
const resolved = new Map();
|
|
678
771
|
const convert = (s) => {
|
|
679
772
|
if (!s || Object.keys(s).length === 0) return defineAnnotatedType().designType("any").$type;
|
|
680
|
-
if (s.$ref)
|
|
773
|
+
if (s.$ref) {
|
|
774
|
+
const refName = s.$ref.replace(/^#\/(\$defs|definitions)\//, "");
|
|
775
|
+
if (resolved.has(refName)) return resolved.get(refName);
|
|
776
|
+
if (defsSource[refName]) {
|
|
777
|
+
const type = convert(defsSource[refName]);
|
|
778
|
+
resolved.set(refName, type);
|
|
779
|
+
return type;
|
|
780
|
+
}
|
|
781
|
+
throw new Error(`Unresolvable $ref: ${s.$ref}`);
|
|
782
|
+
}
|
|
681
783
|
if ("const" in s) {
|
|
682
784
|
const val = s.const;
|
|
683
785
|
const dt = val === null ? "null" : typeof val;
|
|
@@ -765,6 +867,24 @@ function fromJsonSchema(schema) {
|
|
|
765
867
|
};
|
|
766
868
|
return convert(schema);
|
|
767
869
|
}
|
|
870
|
+
function mergeJsonSchemas(types) {
|
|
871
|
+
const mergedDefs = {};
|
|
872
|
+
const schemas = {};
|
|
873
|
+
for (const type of types) {
|
|
874
|
+
const name = type.id;
|
|
875
|
+
if (!name) throw new Error("mergeJsonSchemas: all types must have an id");
|
|
876
|
+
const schema = buildJsonSchema(type);
|
|
877
|
+
if (schema.$defs) {
|
|
878
|
+
for (const [defName, defSchema] of Object.entries(schema.$defs)) if (!mergedDefs[defName]) mergedDefs[defName] = defSchema;
|
|
879
|
+
const { $defs: _,...rest } = schema;
|
|
880
|
+
schemas[name] = rest;
|
|
881
|
+
} else schemas[name] = schema;
|
|
882
|
+
}
|
|
883
|
+
return {
|
|
884
|
+
schemas,
|
|
885
|
+
$defs: mergedDefs
|
|
886
|
+
};
|
|
887
|
+
}
|
|
768
888
|
|
|
769
889
|
//#endregion
|
|
770
890
|
//#region packages/typescript/src/default-value.ts
|
|
@@ -958,6 +1078,7 @@ function serializeNode(def, path, options) {
|
|
|
958
1078
|
metadata: serializeMetadata(def.metadata, path, def.type.kind, options)
|
|
959
1079
|
};
|
|
960
1080
|
if (def.optional) result.optional = true;
|
|
1081
|
+
if (def.id) result.id = def.id;
|
|
961
1082
|
return result;
|
|
962
1083
|
}
|
|
963
1084
|
function serializeTypeDef(def, path, options) {
|
|
@@ -1061,6 +1182,7 @@ function deserializeNode(data) {
|
|
|
1061
1182
|
}
|
|
1062
1183
|
};
|
|
1063
1184
|
if (data.optional) result.optional = true;
|
|
1185
|
+
if (data.id) result.id = data.id;
|
|
1064
1186
|
return result;
|
|
1065
1187
|
}
|
|
1066
1188
|
function deserializeTypeDef(t) {
|
|
@@ -1106,4 +1228,4 @@ function deserializeTypeDef(t) {
|
|
|
1106
1228
|
}
|
|
1107
1229
|
|
|
1108
1230
|
//#endregion
|
|
1109
|
-
export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, createDataFromAnnotatedType, defineAnnotatedType, deserializeAnnotatedType, flattenAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, serializeAnnotatedType, throwFeatureDisabled };
|
|
1231
|
+
export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createDataFromAnnotatedType, defineAnnotatedType, deserializeAnnotatedType, flattenAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas, serializeAnnotatedType, throwFeatureDisabled };
|