@atscript/typescript 0.1.32 → 0.1.34

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.mjs CHANGED
@@ -435,6 +435,26 @@ var ValidatorError = class extends Error {
435
435
 
436
436
  //#endregion
437
437
  //#region packages/typescript/src/annotated-type.ts
438
+ const COMPLEX_KINDS = new Set([
439
+ "union",
440
+ "intersection",
441
+ "tuple"
442
+ ]);
443
+ const NON_PRIMITIVE_KINDS = new Set(["array", "object"]);
444
+ /** Shared validator method reused by all annotated type nodes. */ function validatorMethod(opts) {
445
+ return new Validator(this, opts);
446
+ }
447
+ function createAnnotatedTypeNode(type, metadata, opts) {
448
+ return {
449
+ __is_atscript_annotated_type: true,
450
+ type,
451
+ metadata,
452
+ validator: validatorMethod,
453
+ id: opts?.id,
454
+ optional: opts?.optional,
455
+ ref: opts?.ref
456
+ };
457
+ }
438
458
  function isAnnotatedType(type) {
439
459
  return type && type.__is_atscript_annotated_type;
440
460
  }
@@ -453,32 +473,22 @@ function cloneRefProp(parentType, propName) {
453
473
  const existing = objType.props.get(propName);
454
474
  if (!existing) return;
455
475
  const clonedType = cloneTypeDef(existing.type);
456
- objType.props.set(propName, {
457
- __is_atscript_annotated_type: true,
458
- type: clonedType,
459
- metadata: new Map(existing.metadata),
476
+ objType.props.set(propName, createAnnotatedTypeNode(clonedType, new Map(existing.metadata), {
460
477
  id: existing.id,
461
- optional: existing.optional,
462
- validator(opts) {
463
- return new Validator(this, opts);
464
- }
465
- });
478
+ optional: existing.optional
479
+ }));
466
480
  }
467
481
  function cloneTypeDef(type) {
468
482
  if (type.kind === "object") {
469
483
  const obj = type;
484
+ const props = new Map();
485
+ for (const [k, v] of obj.props) props.set(k, createAnnotatedTypeNode(v.type, new Map(v.metadata), {
486
+ id: v.id,
487
+ optional: v.optional
488
+ }));
470
489
  return {
471
490
  kind: "object",
472
- props: new Map(Array.from(obj.props.entries()).map(([k, v]) => [k, {
473
- __is_atscript_annotated_type: true,
474
- type: v.type,
475
- metadata: new Map(v.metadata),
476
- id: v.id,
477
- optional: v.optional,
478
- validator(opts) {
479
- return new Validator(this, opts);
480
- }
481
- }])),
491
+ props,
482
492
  propsPatterns: [...obj.propsPatterns],
483
493
  tags: new Set(obj.tags)
484
494
  };
@@ -508,38 +518,24 @@ function defineAnnotatedType(_kind, base) {
508
518
  const kind = _kind || "";
509
519
  const type = base?.type || {};
510
520
  type.kind = kind;
511
- if ([
512
- "union",
513
- "intersection",
514
- "tuple"
515
- ].includes(kind)) type.items = [];
521
+ if (COMPLEX_KINDS.has(kind)) type.items = [];
516
522
  if (kind === "object") {
517
523
  type.props = new Map();
518
524
  type.propsPatterns = [];
519
525
  }
520
526
  type.tags = new Set();
521
527
  const metadata = base?.metadata || new Map();
522
- if (base) Object.assign(base, {
523
- __is_atscript_annotated_type: true,
524
- metadata,
525
- type,
526
- validator(opts) {
527
- return new Validator(this, opts);
528
- }
529
- });
530
- else base = {
528
+ const payload = {
531
529
  __is_atscript_annotated_type: true,
532
530
  metadata,
533
531
  type,
534
- validator(opts) {
535
- return new Validator(this, opts);
536
- }
532
+ validator: validatorMethod
537
533
  };
534
+ base = base ? Object.assign(base, payload) : payload;
538
535
  const handle = {
539
536
  $type: base,
540
537
  $def: type,
541
538
  $metadata: metadata,
542
- _existingObject: undefined,
543
539
  tags(...tags) {
544
540
  for (const tag of tags) this.$def.tags.add(tag);
545
541
  return this;
@@ -580,26 +576,56 @@ else base = {
580
576
  return this;
581
577
  },
582
578
  refTo(type$1, chain) {
583
- let newBase = type$1;
584
- const typeName = type$1.name || "Unknown";
585
- if (isAnnotatedType(newBase)) {
586
- let keys = "";
587
- for (const c of chain || []) {
588
- keys += `["${c}"]`;
579
+ if (isAnnotatedType(type$1)) {
580
+ let newBase = type$1;
581
+ const typeName = type$1.name || "Unknown";
582
+ if (chain) for (let i = 0; i < chain.length; i++) {
583
+ const c = chain[i];
589
584
  if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
590
- else throw new Error(`Can't find prop ${typeName}${keys}`);
585
+ else {
586
+ const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
587
+ throw new Error(`Can't find prop ${typeName}${keys}`);
588
+ }
591
589
  }
592
- if (!newBase && keys) throw new Error(`Can't find prop ${typeName}${keys}`);
593
- else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
594
- this.$type = {
595
- __is_atscript_annotated_type: true,
596
- type: newBase.type,
597
- metadata,
590
+ this.$type = createAnnotatedTypeNode(newBase.type, metadata, {
598
591
  id: newBase.id,
599
- validator(opts) {
600
- return new Validator(this, opts);
601
- }
602
- };
592
+ ref: chain && chain.length > 0 ? {
593
+ type: () => type$1,
594
+ field: chain.join(".")
595
+ } : undefined
596
+ });
597
+ } else if (typeof type$1 === "function") {
598
+ const lazyType = type$1;
599
+ this.$type = createAnnotatedTypeNode({ kind: "" }, metadata, { ref: {
600
+ type: lazyType,
601
+ field: chain ? chain.join(".") : ""
602
+ } });
603
+ const node = this.$type;
604
+ const placeholder = node.type;
605
+ Object.defineProperty(node, "type", {
606
+ get() {
607
+ const t = lazyType();
608
+ if (!isAnnotatedType(t)) {
609
+ Object.defineProperty(node, "type", {
610
+ value: placeholder,
611
+ writable: false,
612
+ configurable: true
613
+ });
614
+ return placeholder;
615
+ }
616
+ let target = t;
617
+ if (chain) for (const c of chain) if (target.type.kind === "object" && target.type.props.has(c)) target = target.type.props.get(c);
618
+ else return t.type;
619
+ node.id = target.id || t.id;
620
+ Object.defineProperty(node, "type", {
621
+ value: target.type,
622
+ writable: false,
623
+ configurable: true
624
+ });
625
+ return target.type;
626
+ },
627
+ configurable: true
628
+ });
603
629
  } else throw new Error(`${type$1} is not annotated type`);
604
630
  return this;
605
631
  },
@@ -618,13 +644,9 @@ function isPhantomType(def) {
618
644
  return def.type.kind === "" && def.type.designType === "phantom";
619
645
  }
620
646
  function isAnnotatedTypeOfPrimitive(t) {
621
- if (["array", "object"].includes(t.type.kind)) return false;
647
+ if (NON_PRIMITIVE_KINDS.has(t.type.kind)) return false;
622
648
  if (!t.type.kind) return true;
623
- if ([
624
- "union",
625
- "tuple",
626
- "intersection"
627
- ].includes(t.type.kind)) {
649
+ if (COMPLEX_KINDS.has(t.type.kind)) {
628
650
  for (const item of t.type.items) if (!isAnnotatedTypeOfPrimitive(item)) return false;
629
651
  return true;
630
652
  }
@@ -663,10 +685,10 @@ function forAnnotatedType(def, handlers) {
663
685
  const firstObj = items[0].type;
664
686
  const candidates = [];
665
687
  for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
666
- const validCandidates = [];
688
+ let result = null;
667
689
  for (const candidate of candidates) {
668
690
  const values = new Set();
669
- const mapping = {};
691
+ const indexMapping = {};
670
692
  let valid = true;
671
693
  for (let i = 0; i < items.length; i++) {
672
694
  const obj = items[i].type;
@@ -681,19 +703,21 @@ function forAnnotatedType(def, handlers) {
681
703
  break;
682
704
  }
683
705
  values.add(val);
684
- mapping[String(val)] = `#/oneOf/${i}`;
706
+ indexMapping[String(val)] = i;
707
+ }
708
+ if (valid) {
709
+ if (result) return null;
710
+ result = {
711
+ propertyName: candidate,
712
+ indexMapping
713
+ };
685
714
  }
686
- if (valid) validCandidates.push({
687
- propertyName: candidate,
688
- mapping
689
- });
690
715
  }
691
- if (validCandidates.length === 1) return validCandidates[0];
692
- return null;
716
+ return result;
693
717
  }
694
718
  function buildJsonSchema(type) {
695
719
  const defs = {};
696
- let isRoot = true;
720
+ let hasDefs = false;
697
721
  const buildObject = (d) => {
698
722
  const properties = {};
699
723
  const required = [];
@@ -710,15 +734,15 @@ function buildJsonSchema(type) {
710
734
  return schema$1;
711
735
  };
712
736
  const build$1 = (def) => {
713
- if (def.id && def.type.kind === "object" && !isRoot) {
737
+ if (def.id && def.type.kind === "object" && def !== type) {
714
738
  const name = def.id;
715
739
  if (!defs[name]) {
740
+ hasDefs = true;
716
741
  defs[name] = {};
717
742
  defs[name] = buildObject(def);
718
743
  }
719
744
  return { $ref: `#/$defs/${name}` };
720
745
  }
721
- isRoot = false;
722
746
  const meta = def.metadata;
723
747
  return forAnnotatedType(def, {
724
748
  phantom() {
@@ -743,11 +767,9 @@ function buildJsonSchema(type) {
743
767
  if (disc) {
744
768
  const oneOf = d.type.items.map(build$1);
745
769
  const mapping = {};
746
- for (const [val, origPath] of Object.entries(disc.mapping)) {
747
- const idx = Number.parseInt(origPath.split("/").pop(), 10);
770
+ for (const [val, idx] of Object.entries(disc.indexMapping)) {
748
771
  const item = d.type.items[idx];
749
- if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
750
- else mapping[val] = origPath;
772
+ mapping[val] = item.id && defs[item.id] ? `#/$defs/${item.id}` : `#/oneOf/${idx}`;
751
773
  }
752
774
  return {
753
775
  oneOf,
@@ -797,7 +819,7 @@ else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ patte
797
819
  });
798
820
  };
799
821
  const schema = build$1(type);
800
- if (Object.keys(defs).length > 0) return {
822
+ if (hasDefs) return {
801
823
  ...schema,
802
824
  $defs: defs
803
825
  };
@@ -812,6 +834,8 @@ function fromJsonSchema(schema) {
812
834
  const refName = s.$ref.replace(/^#\/(\$defs|definitions)\//, "");
813
835
  if (resolved.has(refName)) return resolved.get(refName);
814
836
  if (defsSource[refName]) {
837
+ const placeholder = defineAnnotatedType().designType("any").$type;
838
+ resolved.set(refName, placeholder);
815
839
  const type = convert(defsSource[refName]);
816
840
  resolved.set(refName, type);
817
841
  return type;
@@ -1035,6 +1059,7 @@ function throwFeatureDisabled(feature, option, annotation) {
1035
1059
  function flattenAnnotatedType(type, options) {
1036
1060
  const flatMap = new Map();
1037
1061
  const skipPhantom = !!options?.excludePhantomTypes;
1062
+ const visitedIds = new Set();
1038
1063
  function addFieldToFlatMap(name, def) {
1039
1064
  const existing = flatMap.get(name);
1040
1065
  if (existing) {
@@ -1071,6 +1096,13 @@ else flatUnion.item(existing);
1071
1096
  }
1072
1097
  }
1073
1098
  function flattenType(def, prefix = "", inComplexTypeOrArray = false) {
1099
+ const typeId = def.id;
1100
+ if (typeId && visitedIds.has(typeId)) {
1101
+ addFieldToFlatMap(prefix || "", def);
1102
+ if (prefix) options?.onField?.(prefix, def, def.metadata);
1103
+ return;
1104
+ }
1105
+ if (typeId) visitedIds.add(typeId);
1074
1106
  switch (def.type.kind) {
1075
1107
  case "object": {
1076
1108
  addFieldToFlatMap(prefix || "", def);
@@ -1117,20 +1149,31 @@ else flatUnion.item(existing);
1117
1149
  //#region packages/typescript/src/serialize.ts
1118
1150
  const SERIALIZE_VERSION = 1;
1119
1151
  function serializeAnnotatedType(type, options) {
1120
- const result = serializeNode(type, [], options);
1152
+ const visited = new Set();
1153
+ const result = serializeNode(type, [], options, visited);
1121
1154
  result.$v = SERIALIZE_VERSION;
1122
1155
  return result;
1123
1156
  }
1124
- function serializeNode(def, path, options) {
1157
+ function serializeNode(def, path, options, visited) {
1158
+ if (def.id && visited.has(def.id)) return {
1159
+ type: {
1160
+ kind: "$ref",
1161
+ id: def.id
1162
+ },
1163
+ metadata: {},
1164
+ ...def.optional ? { optional: true } : {},
1165
+ id: def.id
1166
+ };
1167
+ if (def.id) visited.add(def.id);
1125
1168
  const result = {
1126
- type: serializeTypeDef(def, path, options),
1169
+ type: serializeTypeDef(def, path, options, visited),
1127
1170
  metadata: serializeMetadata(def.metadata, path, def.type.kind, options)
1128
1171
  };
1129
1172
  if (def.optional) result.optional = true;
1130
1173
  if (def.id) result.id = def.id;
1131
1174
  return result;
1132
1175
  }
1133
- function serializeTypeDef(def, path, options) {
1176
+ function serializeTypeDef(def, path, options, visited) {
1134
1177
  return forAnnotatedType(def, {
1135
1178
  phantom(d) {
1136
1179
  return {
@@ -1150,13 +1193,13 @@ function serializeTypeDef(def, path, options) {
1150
1193
  },
1151
1194
  object(d) {
1152
1195
  const props = {};
1153
- for (const [key, val] of d.type.props.entries()) props[key] = serializeNode(val, [...path, key], options);
1196
+ for (const [key, val] of d.type.props.entries()) props[key] = serializeNode(val, [...path, key], options, visited);
1154
1197
  const propsPatterns = d.type.propsPatterns.map((pp) => ({
1155
1198
  pattern: {
1156
1199
  source: pp.pattern.source,
1157
1200
  flags: pp.pattern.flags
1158
1201
  },
1159
- def: serializeNode(pp.def, path, options)
1202
+ def: serializeNode(pp.def, path, options, visited)
1160
1203
  }));
1161
1204
  return {
1162
1205
  kind: "object",
@@ -1168,28 +1211,28 @@ function serializeTypeDef(def, path, options) {
1168
1211
  array(d) {
1169
1212
  return {
1170
1213
  kind: "array",
1171
- of: serializeNode(d.type.of, path, options),
1214
+ of: serializeNode(d.type.of, path, options, visited),
1172
1215
  tags: Array.from(d.type.tags)
1173
1216
  };
1174
1217
  },
1175
1218
  union(d) {
1176
1219
  return {
1177
1220
  kind: "union",
1178
- items: d.type.items.map((item) => serializeNode(item, path, options)),
1221
+ items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
1179
1222
  tags: Array.from(d.type.tags)
1180
1223
  };
1181
1224
  },
1182
1225
  intersection(d) {
1183
1226
  return {
1184
1227
  kind: "intersection",
1185
- items: d.type.items.map((item) => serializeNode(item, path, options)),
1228
+ items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
1186
1229
  tags: Array.from(d.type.tags)
1187
1230
  };
1188
1231
  },
1189
1232
  tuple(d) {
1190
1233
  return {
1191
1234
  kind: "tuple",
1192
- items: d.type.items.map((item) => serializeNode(item, path, options)),
1235
+ items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
1193
1236
  tags: Array.from(d.type.tags)
1194
1237
  };
1195
1238
  }
@@ -1222,16 +1265,10 @@ function deserializeAnnotatedType(data) {
1222
1265
  function deserializeNode(data) {
1223
1266
  const metadata = new Map(Object.entries(data.metadata));
1224
1267
  const type = deserializeTypeDef(data.type);
1225
- const result = {
1226
- __is_atscript_annotated_type: true,
1227
- type,
1228
- metadata,
1229
- validator(opts) {
1230
- return new Validator(this, opts);
1231
- }
1232
- };
1233
- if (data.optional) result.optional = true;
1234
- if (data.id) result.id = data.id;
1268
+ const result = createAnnotatedTypeNode(type, metadata, {
1269
+ optional: data.optional || undefined,
1270
+ id: data.id || undefined
1271
+ });
1235
1272
  return result;
1236
1273
  }
1237
1274
  function deserializeTypeDef(t) {
@@ -1272,9 +1309,15 @@ function deserializeTypeDef(t) {
1272
1309
  items: t.items.map((item) => deserializeNode(item)),
1273
1310
  tags
1274
1311
  };
1312
+ case "$ref": return {
1313
+ kind: "object",
1314
+ props: new Map(),
1315
+ propsPatterns: [],
1316
+ tags
1317
+ };
1275
1318
  default: throw new Error(`Unknown serialized type kind "${t.kind}"`);
1276
1319
  }
1277
1320
  }
1278
1321
 
1279
1322
  //#endregion
1280
- export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createDataFromAnnotatedType, defineAnnotatedType, deserializeAnnotatedType, flattenAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas, serializeAnnotatedType, throwFeatureDisabled };
1323
+ export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createAnnotatedTypeNode, createDataFromAnnotatedType, defineAnnotatedType, deserializeAnnotatedType, flattenAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas, serializeAnnotatedType, throwFeatureDisabled };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/typescript",
3
- "version": "0.1.32",
3
+ "version": "0.1.34",
4
4
  "description": "Atscript: typescript-gen support.",
5
5
  "keywords": [
6
6
  "annotations",
@@ -64,7 +64,7 @@
64
64
  "vitest": "3.2.4"
65
65
  },
66
66
  "peerDependencies": {
67
- "@atscript/core": "^0.1.32"
67
+ "@atscript/core": "^0.1.34"
68
68
  },
69
69
  "build": [
70
70
  {},