@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/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
- const properties = {};
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) return {
631
- oneOf: d.type.items.map(build$1),
632
- discriminator: {
633
- propertyName: disc.propertyName,
634
- mapping: disc.mapping
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
- return build$1(type);
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) throw new Error("$ref is not supported by fromJsonSchema. Dereference the schema first.");
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
- const properties = {};
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) return {
630
- oneOf: d.type.items.map(build$1),
631
- discriminator: {
632
- propertyName: disc.propertyName,
633
- mapping: disc.mapping
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
- return build$1(type);
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) throw new Error("$ref is not supported by fromJsonSchema. Dereference the schema first.");
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 };