@atscript/typescript 0.1.88 → 0.1.89

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.
@@ -1,4 +1,3 @@
1
-
2
1
  //#region packages/typescript/src/runtime/validator.ts
3
2
  function _define_property(obj, key, value) {
4
3
  if (key in obj) Object.defineProperty(obj, key, {
@@ -7,11 +6,31 @@ function _define_property(obj, key, value) {
7
6
  configurable: true,
8
7
  writable: true
9
8
  });
10
- else obj[key] = value;
9
+ else obj[key] = value;
11
10
  return obj;
12
11
  }
13
- const regexCache = new Map();
14
- var Validator = class {
12
+ const regexCache = /* @__PURE__ */ new Map();
13
+ /**
14
+ * Validates values against an {@link TAtscriptAnnotatedType} definition.
15
+ *
16
+ * `DataType` is automatically inferred from the type definition's phantom generic,
17
+ * enabling the {@link validate} method to act as a type guard.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * // From a generated interface class:
22
+ * const validator = new Validator(MyInterface)
23
+ * if (validator.validate(data, true)) {
24
+ * data // narrowed to MyInterface
25
+ * }
26
+ *
27
+ * // Or use the built-in factory:
28
+ * MyInterface.validator().validate(data)
29
+ * ```
30
+ *
31
+ * @typeParam T - The annotated type definition.
32
+ * @typeParam DataType - The TypeScript type that `validate` narrows to (auto-inferred).
33
+ */ var Validator = class {
15
34
  buildPath() {
16
35
  if (this.depth <= 0) return "";
17
36
  let path = this.pathSegments[0];
@@ -26,7 +45,7 @@ var Validator = class {
26
45
  pop(saveErrors) {
27
46
  this.depth--;
28
47
  const popped = this.stackErrors.pop();
29
- if (saveErrors && popped !== null && popped !== undefined && popped.length > 0) for (const err of popped) this.error(err.message, err.path, err.details);
48
+ if (saveErrors && popped !== null && popped !== void 0 && popped.length > 0) for (const err of popped) this.error(err.message, err.path, err.details);
30
49
  return popped;
31
50
  }
32
51
  clear() {
@@ -67,7 +86,7 @@ var Validator = class {
67
86
  this.limitExceeded = false;
68
87
  this.context = context;
69
88
  const passed = this.validateSafe(this.def, value);
70
- this.context = undefined;
89
+ this.context = void 0;
71
90
  if (!passed) {
72
91
  if (safe) return false;
73
92
  this.throw();
@@ -78,13 +97,13 @@ var Validator = class {
78
97
  if (this.limitExceeded) return false;
79
98
  if (this.hasReplace) {
80
99
  let replaced = this.replaceCache.get(def);
81
- if (replaced === undefined) {
100
+ if (replaced === void 0) {
82
101
  replaced = this.opts.replace(def, this.buildPath());
83
102
  this.replaceCache.set(def, replaced);
84
103
  }
85
104
  def = replaced;
86
105
  }
87
- if (def.optional && (value === undefined || value === null)) return true;
106
+ if (def.optional && (value === void 0 || value === null)) return true;
88
107
  if (this.hasPlugins) for (const plugin of this.opts.plugins) {
89
108
  const result = plugin(this, def, value);
90
109
  if (result === false || result === true) return result;
@@ -96,10 +115,9 @@ var Validator = class {
96
115
  }
97
116
  validateAnnotatedType(def, value) {
98
117
  switch (def.type.kind) {
99
- case "": {
118
+ case "":
100
119
  if (def.type.designType === "phantom") return true;
101
120
  return this.validatePrimitive(def, value);
102
- }
103
121
  case "object": return this.validateObject(def, value);
104
122
  case "array": return this.validateArray(def, value);
105
123
  case "union": return this.validateUnion(def, value);
@@ -120,10 +138,10 @@ var Validator = class {
120
138
  const branchErrors = this.stackErrors.pop();
121
139
  if (this.limitExceeded) this.limitExceeded = false;
122
140
  if (branchErrors) if (details) for (const err of branchErrors) details.push(err);
123
- else details = branchErrors;
141
+ else details = branchErrors;
124
142
  }
125
143
  const expected = items.map((item, i) => `[${item.type.kind || item.type.designType}(${i})]`).join(", ");
126
- this.error(`Value does not match any of the allowed types: ${expected}`, undefined, details);
144
+ this.error(`Value does not match any of the allowed types: ${expected}`, void 0, details);
127
145
  return false;
128
146
  }
129
147
  validateIntersection(def, value) {
@@ -173,8 +191,8 @@ else details = branchErrors;
173
191
  const uniqueItems = def.metadata.get("expect.array.uniqueItems");
174
192
  if (uniqueItems) {
175
193
  const separator = "▼↩";
176
- const seen = new Set();
177
- const keyProps = new Set();
194
+ const seen = /* @__PURE__ */ new Set();
195
+ const keyProps = /* @__PURE__ */ new Set();
178
196
  if (def.type.of.type.kind === "object") {
179
197
  for (const [key, val] of def.type.of.type.props.entries()) if (val.metadata.get("expect.array.key")) keyProps.add(key);
180
198
  }
@@ -219,7 +237,7 @@ else details = branchErrors;
219
237
  const path = this.depth > 0 ? `${this.buildPath()}.` : "";
220
238
  for (const item of this.opts.skipList) if (item.startsWith(path)) {
221
239
  const key = item.slice(path.length);
222
- if (!skipList) skipList = new Set();
240
+ if (!skipList) skipList = /* @__PURE__ */ new Set();
223
241
  skipList.add(key);
224
242
  }
225
243
  }
@@ -227,12 +245,12 @@ else details = branchErrors;
227
245
  if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.buildPath());
228
246
  for (const [key, item] of def.type.props.entries()) {
229
247
  if (skipList && skipList.has(key) || isPhantomType(item)) continue;
230
- if (value[key] === undefined) {
248
+ if (value[key] === void 0) {
231
249
  if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.depth === 0) continue;
232
250
  }
233
251
  this.push(key);
234
252
  if (this.validateSafe(item, value[key])) this.pop(false);
235
- else {
253
+ else {
236
254
  passed = false;
237
255
  this.pop(true);
238
256
  if (this.limitExceeded) return false;
@@ -282,7 +300,7 @@ else {
282
300
  return passed;
283
301
  }
284
302
  validatePrimitive(def, value) {
285
- if (def.type.value !== undefined) {
303
+ if (def.type.value !== void 0) {
286
304
  if (value !== def.type.value) {
287
305
  this.error(`Expected ${def.type.value}, got ${value}`);
288
306
  return false;
@@ -291,47 +309,41 @@ else {
291
309
  }
292
310
  const typeOfValue = Array.isArray(value) ? "array" : typeof value;
293
311
  switch (def.type.designType) {
294
- case "never": {
312
+ case "never":
295
313
  this.error(`This type is impossible, must be an internal problem`);
296
314
  return false;
297
- }
298
315
  case "any": return true;
299
- case "string": {
316
+ case "string":
300
317
  if (typeOfValue !== def.type.designType) {
301
318
  this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
302
319
  return false;
303
320
  }
304
321
  return this.validateString(def, value);
305
- }
306
- case "number": {
322
+ case "number":
307
323
  if (typeOfValue !== def.type.designType) {
308
324
  this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
309
325
  return false;
310
326
  }
311
327
  return this.validateNumber(def, value);
312
- }
313
- case "boolean": {
328
+ case "boolean":
314
329
  if (typeOfValue !== def.type.designType) {
315
330
  this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
316
331
  return false;
317
332
  }
318
333
  return this.validateBoolean(def, value);
319
- }
320
- case "undefined": {
321
- if (value !== undefined) {
334
+ case "undefined":
335
+ if (value !== void 0) {
322
336
  this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
323
337
  return false;
324
338
  }
325
339
  return true;
326
- }
327
- case "null": {
340
+ case "null":
328
341
  if (value !== null) {
329
342
  this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
330
343
  return false;
331
344
  }
332
345
  return true;
333
- }
334
- case "decimal": {
346
+ case "decimal":
335
347
  if (typeOfValue !== "string") {
336
348
  this.error(`Expected string (decimal), got ${typeOfValue}`);
337
349
  return false;
@@ -341,7 +353,6 @@ else {
341
353
  return false;
342
354
  }
343
355
  return true;
344
- }
345
356
  default: throw new Error(`Unknown type "${def.type.designType}"`);
346
357
  }
347
358
  }
@@ -456,10 +467,10 @@ else {
456
467
  };
457
468
  this.hasPlugins = this.opts.plugins.length > 0;
458
469
  this.hasReplace = typeof this.opts.replace === "function";
459
- if (this.hasReplace) this.replaceCache = new WeakMap();
470
+ if (this.hasReplace) this.replaceCache = /* @__PURE__ */ new WeakMap();
460
471
  }
461
472
  };
462
- var ValidatorError = class extends Error {
473
+ /** Error thrown by {@link Validator.validate} when validation fails. Contains structured error details. */ var ValidatorError = class extends Error {
463
474
  constructor(errors) {
464
475
  super(`${errors[0].path ? errors[0].path + ": " : ""}${errors[0].message}`), _define_property(this, "errors", void 0), _define_property(this, "name", void 0), this.errors = errors, this.name = "Validation Error";
465
476
  }
@@ -476,7 +487,10 @@ const NON_PRIMITIVE_KINDS = new Set(["array", "object"]);
476
487
  /** Shared validator method reused by all annotated type nodes. */ function validatorMethod(opts) {
477
488
  return new Validator(this, opts);
478
489
  }
479
- function createAnnotatedTypeNode(type, metadata, opts) {
490
+ /**
491
+ * Creates a minimal annotated type node from a type def and metadata map.
492
+ * Centralises the shape so callers never duplicate the boilerplate.
493
+ */ function createAnnotatedTypeNode(type, metadata, opts) {
480
494
  return {
481
495
  __is_atscript_annotated_type: true,
482
496
  type,
@@ -487,19 +501,27 @@ function createAnnotatedTypeNode(type, metadata, opts) {
487
501
  ref: opts?.ref
488
502
  };
489
503
  }
490
- function isAnnotatedType(type) {
504
+ /**
505
+ * Type Guard to check if a type is atscript-annotated
506
+ */ function isAnnotatedType(type) {
491
507
  return type && type.__is_atscript_annotated_type;
492
508
  }
493
- function annotate(metadata, key, value, asArray) {
509
+ /**
510
+ * Standalone annotate function that handles both replace and append (array) strategies.
511
+ * Used by the handle's .annotate() method and by generated mutation statements.
512
+ */ function annotate(metadata, key, value, asArray) {
494
513
  if (!metadata) return;
495
514
  if (asArray) if (metadata.has(key)) {
496
515
  const a = metadata.get(key);
497
516
  if (Array.isArray(a)) a.push(value);
498
- else metadata.set(key, [a, value]);
517
+ else metadata.set(key, [a, value]);
499
518
  } else metadata.set(key, [value]);
500
- else metadata.set(key, value);
519
+ else metadata.set(key, value);
501
520
  }
502
- function cloneRefProp(parentType, propName) {
521
+ /**
522
+ * Clones a property's type tree in-place so mutations don't leak to shared refs.
523
+ * Used by mutating annotate codegen when paths cross ref boundaries.
524
+ */ function cloneRefProp(parentType, propName) {
503
525
  if (parentType.kind !== "object") return;
504
526
  const objType = parentType;
505
527
  const existing = objType.props.get(propName);
@@ -513,7 +535,7 @@ function cloneRefProp(parentType, propName) {
513
535
  function cloneTypeDef(type) {
514
536
  if (type.kind === "object") {
515
537
  const obj = type;
516
- const props = new Map();
538
+ const props = /* @__PURE__ */ new Map();
517
539
  for (const [k, v] of obj.props) props.set(k, createAnnotatedTypeNode(v.type, new Map(v.metadata), {
518
540
  id: v.id,
519
541
  optional: v.optional
@@ -546,18 +568,36 @@ function cloneTypeDef(type) {
546
568
  tags: new Set(type.tags)
547
569
  };
548
570
  }
549
- function defineAnnotatedType(_kind, base) {
571
+ /**
572
+ * Creates a builder handle for constructing a {@link TAtscriptAnnotatedType} at runtime.
573
+ *
574
+ * This is primarily used by generated `.as.js` code. The returned handle provides
575
+ * a fluent API for setting the type definition, metadata, and properties.
576
+ *
577
+ * @example
578
+ * ```ts
579
+ * const handle = defineAnnotatedType('object')
580
+ * .prop('name', defineAnnotatedType().designType('string').$type)
581
+ * .prop('age', defineAnnotatedType().designType('number').$type)
582
+ *
583
+ * handle.$type // the resulting TAtscriptAnnotatedType
584
+ * ```
585
+ *
586
+ * @param _kind - The kind of type to create (e.g. `'object'`, `'array'`, `'union'`). Defaults to `''` (primitive/final).
587
+ * @param base - Optional existing object to augment with annotated type fields.
588
+ * @returns A builder handle for fluent type construction.
589
+ */ function defineAnnotatedType(_kind, base) {
550
590
  const kind = _kind || "";
551
- const baseProvided = base !== undefined;
591
+ const baseProvided = base !== void 0;
552
592
  const type = base?.type || {};
553
593
  type.kind = kind;
554
594
  if (COMPLEX_KINDS.has(kind)) type.items = [];
555
595
  if (kind === "object") {
556
- type.props = new Map();
596
+ type.props = /* @__PURE__ */ new Map();
557
597
  type.propsPatterns = [];
558
598
  }
559
- type.tags = new Set();
560
- const metadata = base?.metadata || new Map();
599
+ type.tags = /* @__PURE__ */ new Set();
600
+ const metadata = base?.metadata || /* @__PURE__ */ new Map();
561
601
  const payload = {
562
602
  __is_atscript_annotated_type: true,
563
603
  metadata,
@@ -565,7 +605,7 @@ function defineAnnotatedType(_kind, base) {
565
605
  validator: validatorMethod
566
606
  };
567
607
  base = base ? Object.assign(base, payload) : payload;
568
- const handle = {
608
+ return {
569
609
  $type: base,
570
610
  $def: type,
571
611
  $metadata: metadata,
@@ -608,15 +648,15 @@ function defineAnnotatedType(_kind, base) {
608
648
  for (const [key, value] of fromMetadata.entries()) if (!ignore || !ignore.has(key)) this.$metadata.set(key, value);
609
649
  return this;
610
650
  },
611
- refTo(type$1, chain) {
651
+ refTo(type, chain) {
612
652
  const node = this.$type;
613
- if (isAnnotatedType(type$1)) {
614
- let newBase = type$1;
615
- const typeName = type$1.name || "Unknown";
653
+ if (isAnnotatedType(type)) {
654
+ let newBase = type;
655
+ const typeName = type.name || "Unknown";
616
656
  if (chain) for (let i = 0; i < chain.length; i++) {
617
657
  const c = chain[i];
618
658
  if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
619
- else {
659
+ else {
620
660
  const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
621
661
  throw new Error(`Can't find prop ${typeName}${keys}`);
622
662
  }
@@ -624,11 +664,11 @@ else {
624
664
  node.type = baseProvided ? cloneTypeDef(newBase.type) : newBase.type;
625
665
  node.id = node.id ?? newBase.id;
626
666
  if (chain && chain.length > 0) node.ref = {
627
- type: () => type$1,
667
+ type: () => type,
628
668
  field: chain.join(".")
629
669
  };
630
- } else if (typeof type$1 === "function") {
631
- const lazyType = type$1;
670
+ } else if (typeof type === "function") {
671
+ const lazyType = type;
632
672
  const ownId = node.id;
633
673
  node.ref = {
634
674
  type: lazyType,
@@ -648,7 +688,7 @@ else {
648
688
  }
649
689
  let target = t;
650
690
  if (chain) for (const c of chain) if (target.type.kind === "object" && target.type.props.has(c)) target = target.type.props.get(c);
651
- else return t.type;
691
+ else return t.type;
652
692
  node.id = ownId ?? target.id;
653
693
  const resolved = baseProvided ? cloneTypeDef(target.type) : target.type;
654
694
  Object.defineProperty(node, "type", {
@@ -660,7 +700,7 @@ else return t.type;
660
700
  },
661
701
  configurable: true
662
702
  });
663
- } else throw new TypeError(`${type$1} is not annotated type`);
703
+ } else throw new TypeError(`${type} is not annotated type`);
664
704
  return this;
665
705
  },
666
706
  annotate(key, value, asArray) {
@@ -672,12 +712,21 @@ else return t.type;
672
712
  return this;
673
713
  }
674
714
  };
675
- return handle;
676
715
  }
677
- function isPhantomType(def) {
716
+ /**
717
+ * Checks whether an annotated type is a phantom type.
718
+ *
719
+ * Phantom types do not affect the data type, validation, or schema,
720
+ * but are discoverable via runtime type traversal.
721
+ */ function isPhantomType(def) {
678
722
  return def.type.kind === "" && def.type.designType === "phantom";
679
723
  }
680
- function isAnnotatedTypeOfPrimitive(t) {
724
+ /**
725
+ * Checks whether an annotated type resolves to a primitive (non-object, non-array) shape.
726
+ *
727
+ * Returns `true` for final types and for unions/intersections/tuples
728
+ * whose members are all primitives.
729
+ */ function isAnnotatedTypeOfPrimitive(t) {
681
730
  if (NON_PRIMITIVE_KINDS.has(t.type.kind)) return false;
682
731
  if (!t.type.kind) return true;
683
732
  if (COMPLEX_KINDS.has(t.type.kind)) {
@@ -689,7 +738,17 @@ function isAnnotatedTypeOfPrimitive(t) {
689
738
 
690
739
  //#endregion
691
740
  //#region packages/typescript/src/runtime/traverse.ts
692
- function forAnnotatedType(def, handlers) {
741
+ /**
742
+ * Type-safe dispatch over `TAtscriptAnnotatedType` by its `type.kind`.
743
+ *
744
+ * Provides the common `switch (def.type.kind)` pattern used by
745
+ * the validator, JSON-schema builder, and serializer.
746
+ * Each caller supplies its own handlers that control recursion.
747
+ *
748
+ * When a `phantom` handler is provided, phantom types (`designType === 'phantom'`)
749
+ * are dispatched to it instead of `final`. This allows consumers to skip or
750
+ * handle phantom props without polluting their `final` handler.
751
+ */ function forAnnotatedType(def, handlers) {
693
752
  switch (def.type.kind) {
694
753
  case "": {
695
754
  const typed = def;
@@ -707,21 +766,27 @@ function forAnnotatedType(def, handlers) {
707
766
 
708
767
  //#endregion
709
768
  //#region packages/typescript/src/runtime/json-schema.ts
710
- function detectDiscriminator(items) {
769
+ /**
770
+ * Detects a discriminator property across union items.
771
+ *
772
+ * Scans all items for object-typed members that share a common property
773
+ * with distinct const/literal values. If exactly one such property exists,
774
+ * it is returned as the discriminator. Returns `null` when no qualifying
775
+ * property exists, or when more than one does (ambiguous).
776
+ */ function detectDiscriminator(items) {
711
777
  if (items.length < 2) return null;
712
778
  for (const item of items) if (item.type.kind !== "object") return null;
713
779
  const firstObj = items[0].type;
714
780
  const candidates = [];
715
- for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
781
+ for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== void 0) candidates.push(propName);
716
782
  let result = null;
717
783
  for (const candidate of candidates) {
718
- const values = new Set();
784
+ const values = /* @__PURE__ */ new Set();
719
785
  const indexMapping = {};
720
786
  let valid = true;
721
787
  for (let i = 0; i < items.length; i++) {
722
- const obj = items[i].type;
723
- const prop = obj.props.get(candidate);
724
- if (!prop || prop.type.kind !== "" || prop.type.value === undefined) {
788
+ const prop = items[i].type.props.get(candidate);
789
+ if (!prop || prop.type.kind !== "" || prop.type.value === void 0) {
725
790
  valid = false;
726
791
  break;
727
792
  }
@@ -743,7 +808,23 @@ function detectDiscriminator(items) {
743
808
  }
744
809
  return result;
745
810
  }
746
- function buildJsonSchema(type) {
811
+ /**
812
+ * Builds a JSON Schema from an {@link TAtscriptAnnotatedType}.
813
+ *
814
+ * Translates the atscript type structure and validation metadata
815
+ * (min/max, patterns, integer constraints, etc.) into a standard JSON Schema.
816
+ *
817
+ * @example
818
+ * ```ts
819
+ * import { buildJsonSchema } from '@atscript/typescript'
820
+ *
821
+ * const schema = buildJsonSchema(MyInterface)
822
+ * // { type: 'object', properties: { ... }, required: [...] }
823
+ * ```
824
+ *
825
+ * @param type - The annotated type to convert.
826
+ * @returns A JSON Schema object.
827
+ */ function buildJsonSchema(type) {
747
828
  const defs = {};
748
829
  let hasDefs = false;
749
830
  const buildObject = (d) => {
@@ -754,12 +835,12 @@ function buildJsonSchema(type) {
754
835
  properties[key] = build(val);
755
836
  if (!val.optional) required.push(key);
756
837
  }
757
- const schema$1 = {
838
+ const schema = {
758
839
  type: "object",
759
840
  properties
760
841
  };
761
- if (required.length > 0) schema$1.required = required;
762
- return schema$1;
842
+ if (required.length > 0) schema.required = required;
843
+ return schema;
763
844
  };
764
845
  const build = (def) => {
765
846
  if (def.id && def.type.kind === "object" && def !== type) {
@@ -780,15 +861,15 @@ function buildJsonSchema(type) {
780
861
  return buildObject(d);
781
862
  },
782
863
  array(d) {
783
- const schema$1 = {
864
+ const schema = {
784
865
  type: "array",
785
866
  items: build(d.type.of)
786
867
  };
787
868
  const minLength = meta.get("expect.minLength");
788
- if (minLength) schema$1.minItems = typeof minLength === "number" ? minLength : minLength.length;
869
+ if (minLength) schema.minItems = typeof minLength === "number" ? minLength : minLength.length;
789
870
  const maxLength = meta.get("expect.maxLength");
790
- if (maxLength) schema$1.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
791
- return schema$1;
871
+ if (maxLength) schema.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
872
+ return schema;
792
873
  },
793
874
  union(d) {
794
875
  const disc = detectDiscriminator(d.type.items);
@@ -820,30 +901,30 @@ function buildJsonSchema(type) {
820
901
  };
821
902
  },
822
903
  final(d) {
823
- const schema$1 = {};
824
- if (d.type.value !== undefined) schema$1.const = d.type.value;
904
+ const schema = {};
905
+ if (d.type.value !== void 0) schema.const = d.type.value;
825
906
  if (d.type.designType && d.type.designType !== "any") {
826
907
  const dt = d.type.designType;
827
- schema$1.type = dt === "undefined" ? "null" : dt === "decimal" ? "string" : dt;
828
- if (schema$1.type === "number" && meta.get("expect.int")) schema$1.type = "integer";
908
+ schema.type = dt === "undefined" ? "null" : dt === "decimal" ? "string" : dt;
909
+ if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
829
910
  }
830
- if (schema$1.type === "string") {
831
- if (meta.get("meta.required")) schema$1.minLength = 1;
911
+ if (schema.type === "string") {
912
+ if (meta.get("meta.required")) schema.minLength = 1;
832
913
  const minLength = meta.get("expect.minLength");
833
- if (minLength) schema$1.minLength = typeof minLength === "number" ? minLength : minLength.length;
914
+ if (minLength) schema.minLength = typeof minLength === "number" ? minLength : minLength.length;
834
915
  const maxLength = meta.get("expect.maxLength");
835
- if (maxLength) schema$1.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
916
+ if (maxLength) schema.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
836
917
  const patterns = meta.get("expect.pattern");
837
- if (patterns?.length) if (patterns.length === 1) schema$1.pattern = patterns[0].pattern;
838
- else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
918
+ if (patterns?.length) if (patterns.length === 1) schema.pattern = patterns[0].pattern;
919
+ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
839
920
  }
840
- if (schema$1.type === "number" || schema$1.type === "integer") {
921
+ if (schema.type === "number" || schema.type === "integer") {
841
922
  const min = meta.get("expect.min");
842
- if (min) schema$1.minimum = typeof min === "number" ? min : min.minValue;
923
+ if (min) schema.minimum = typeof min === "number" ? min : min.minValue;
843
924
  const max = meta.get("expect.max");
844
- if (max) schema$1.maximum = typeof max === "number" ? max : max.maxValue;
925
+ if (max) schema.maximum = typeof max === "number" ? max : max.maxValue;
845
926
  }
846
- return schema$1;
927
+ return schema;
847
928
  }
848
929
  });
849
930
  };
@@ -854,9 +935,28 @@ else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ patte
854
935
  };
855
936
  return schema;
856
937
  }
857
- function fromJsonSchema(schema) {
938
+ /**
939
+ * Converts a JSON Schema object into a {@link TAtscriptAnnotatedType}.
940
+ *
941
+ * This is the inverse of {@link buildJsonSchema}. A round-trip
942
+ * `buildJsonSchema(fromJsonSchema(schema))` preserves structure and constraints.
943
+ *
944
+ * Supports the JSON Schema subset produced by `buildJsonSchema` plus
945
+ * common extensions like `oneOf` (treated as union) and `enum` (union of literals).
946
+ *
947
+ * @example
948
+ * ```ts
949
+ * import { fromJsonSchema } from '@atscript/typescript'
950
+ *
951
+ * const type = fromJsonSchema({ type: 'object', properties: { name: { type: 'string' } }, required: ['name'] })
952
+ * type.validator().validate({ name: 'Alice' }) // passes
953
+ * ```
954
+ *
955
+ * @param schema - A JSON Schema object.
956
+ * @returns An annotated type with full validator support.
957
+ */ function fromJsonSchema(schema) {
858
958
  const defsSource = schema.$defs || schema.definitions || {};
859
- const resolved = new Map();
959
+ const resolved = /* @__PURE__ */ new Map();
860
960
  const convert = (s) => {
861
961
  if (!s || Object.keys(s).length === 0) return defineAnnotatedType().designType("any").$type;
862
962
  if (s.$ref) {
@@ -919,9 +1019,9 @@ function fromJsonSchema(schema) {
919
1019
  }
920
1020
  if (s.type === "array") {
921
1021
  if (Array.isArray(s.items)) {
922
- const handle$1 = defineAnnotatedType("tuple");
923
- for (const item of s.items) handle$1.item(convert(item));
924
- return handle$1.$type;
1022
+ const handle = defineAnnotatedType("tuple");
1023
+ for (const item of s.items) handle.item(convert(item));
1024
+ return handle.$type;
925
1025
  }
926
1026
  const itemType = s.items ? convert(s.items) : defineAnnotatedType().designType("any").$type;
927
1027
  const handle = defineAnnotatedType("array").of(itemType);
@@ -958,7 +1058,16 @@ function fromJsonSchema(schema) {
958
1058
  };
959
1059
  return convert(schema);
960
1060
  }
961
- function mergeJsonSchemas(types) {
1061
+ /**
1062
+ * Merges multiple annotated types into a combined schema map with shared `$defs`.
1063
+ *
1064
+ * Each type must have an `id`. The returned `schemas` object contains individual
1065
+ * schemas keyed by type id, and `$defs` contains all shared type definitions
1066
+ * deduplicated across schemas.
1067
+ *
1068
+ * @param types - Array of annotated types, each with an `id`.
1069
+ * @returns An object with `schemas` (keyed by id) and shared `$defs`.
1070
+ */ function mergeJsonSchemas(types) {
962
1071
  const mergedDefs = {};
963
1072
  const schemas = {};
964
1073
  for (const type of types) {
@@ -967,7 +1076,7 @@ function mergeJsonSchemas(types) {
967
1076
  const schema = buildJsonSchema(type);
968
1077
  if (schema.$defs) {
969
1078
  for (const [defName, defSchema] of Object.entries(schema.$defs)) if (!mergedDefs[defName]) mergedDefs[defName] = defSchema;
970
- const { $defs: _,...rest } = schema;
1079
+ const { $defs: _, ...rest } = schema;
971
1080
  schemas[name] = rest;
972
1081
  } else schemas[name] = schema;
973
1082
  }
@@ -978,4 +1087,4 @@ function mergeJsonSchemas(types) {
978
1087
  }
979
1088
 
980
1089
  //#endregion
981
- export { Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createAnnotatedTypeNode, defineAnnotatedType, detectDiscriminator, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas };
1090
+ export { forAnnotatedType as a, createAnnotatedTypeNode as c, isAnnotatedTypeOfPrimitive as d, isPhantomType as f, mergeJsonSchemas as i, defineAnnotatedType as l, ValidatorError as m, detectDiscriminator as n, annotate as o, Validator as p, fromJsonSchema as r, cloneRefProp as s, buildJsonSchema as t, isAnnotatedType as u };