@formspec/build 0.1.0-alpha.17 → 0.1.0-alpha.19

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.
Files changed (55) hide show
  1. package/README.md +74 -128
  2. package/dist/__tests__/class-schema.test.d.ts +2 -0
  3. package/dist/__tests__/class-schema.test.d.ts.map +1 -0
  4. package/dist/__tests__/date-extension.integration.test.d.ts +2 -0
  5. package/dist/__tests__/date-extension.integration.test.d.ts.map +1 -0
  6. package/dist/__tests__/fixtures/class-schema-regressions.d.ts +83 -0
  7. package/dist/__tests__/fixtures/class-schema-regressions.d.ts.map +1 -0
  8. package/dist/__tests__/fixtures/example-date-extension.d.ts +12 -0
  9. package/dist/__tests__/fixtures/example-date-extension.d.ts.map +1 -0
  10. package/dist/__tests__/fixtures/extension-forms.d.ts +7 -0
  11. package/dist/__tests__/fixtures/extension-forms.d.ts.map +1 -0
  12. package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts.map +1 -1
  13. package/dist/__tests__/fixtures/named-primitive-aliases.d.ts +15 -0
  14. package/dist/__tests__/fixtures/named-primitive-aliases.d.ts.map +1 -0
  15. package/dist/__tests__/fixtures/nested-array-path-constraints.d.ts +14 -0
  16. package/dist/__tests__/fixtures/nested-array-path-constraints.d.ts.map +1 -0
  17. package/dist/__tests__/fixtures/sample-forms.d.ts +10 -0
  18. package/dist/__tests__/fixtures/sample-forms.d.ts.map +1 -1
  19. package/dist/__tests__/generate-schemas.test.d.ts +2 -0
  20. package/dist/__tests__/generate-schemas.test.d.ts.map +1 -0
  21. package/dist/__tests__/parity/parity.test.d.ts +6 -2
  22. package/dist/__tests__/parity/parity.test.d.ts.map +1 -1
  23. package/dist/__tests__/parity/utils.d.ts +9 -4
  24. package/dist/__tests__/parity/utils.d.ts.map +1 -1
  25. package/dist/analyzer/class-analyzer.d.ts.map +1 -1
  26. package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
  27. package/dist/analyzer/program.d.ts +15 -0
  28. package/dist/analyzer/program.d.ts.map +1 -1
  29. package/dist/analyzer/tsdoc-parser.d.ts +5 -0
  30. package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
  31. package/dist/browser.cjs +73 -10
  32. package/dist/browser.cjs.map +1 -1
  33. package/dist/browser.js +73 -10
  34. package/dist/browser.js.map +1 -1
  35. package/dist/canonicalize/chain-dsl-canonicalizer.d.ts.map +1 -1
  36. package/dist/canonicalize/tsdoc-canonicalizer.d.ts.map +1 -1
  37. package/dist/cli.cjs +1147 -252
  38. package/dist/cli.cjs.map +1 -1
  39. package/dist/cli.js +1142 -248
  40. package/dist/cli.js.map +1 -1
  41. package/dist/extensions/registry.d.ts.map +1 -1
  42. package/dist/generators/class-schema.d.ts.map +1 -1
  43. package/dist/generators/method-schema.d.ts.map +1 -1
  44. package/dist/generators/mixed-authoring.d.ts.map +1 -1
  45. package/dist/index.cjs +1121 -239
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.js +1121 -239
  48. package/dist/index.js.map +1 -1
  49. package/dist/internals.cjs +377 -195
  50. package/dist/internals.cjs.map +1 -1
  51. package/dist/internals.js +377 -195
  52. package/dist/internals.js.map +1 -1
  53. package/dist/json-schema/ir-generator.d.ts.map +1 -1
  54. package/dist/validate/constraint-validator.d.ts.map +1 -1
  55. package/package.json +3 -3
package/dist/internals.js CHANGED
@@ -20,6 +20,7 @@ function canonicalizeChainDSL(form) {
20
20
  kind: "form-ir",
21
21
  irVersion: IR_VERSION,
22
22
  elements: canonicalizeElements(form.elements),
23
+ rootAnnotations: [],
23
24
  typeRegistry: {},
24
25
  provenance: CHAIN_DSL_PROVENANCE
25
26
  };
@@ -325,6 +326,7 @@ function canonicalizeTSDoc(analysis, source) {
325
326
  irVersion: IR_VERSION2,
326
327
  elements,
327
328
  typeRegistry: analysis.typeRegistry,
329
+ ...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { rootAnnotations: analysis.annotations },
328
330
  ...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { annotations: analysis.annotations },
329
331
  provenance
330
332
  };
@@ -385,85 +387,17 @@ function wrapInConditional(field, layout, provenance) {
385
387
  }
386
388
 
387
389
  // src/analyzer/program.ts
388
- import * as ts from "typescript";
390
+ import * as ts4 from "typescript";
389
391
  import * as path from "path";
390
- function createProgramContext(filePath) {
391
- const absolutePath = path.resolve(filePath);
392
- const fileDir = path.dirname(absolutePath);
393
- const configPath = ts.findConfigFile(fileDir, ts.sys.fileExists.bind(ts.sys), "tsconfig.json");
394
- let compilerOptions;
395
- let fileNames;
396
- if (configPath) {
397
- const configFile = ts.readConfigFile(configPath, ts.sys.readFile.bind(ts.sys));
398
- if (configFile.error) {
399
- throw new Error(
400
- `Error reading tsconfig.json: ${ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`
401
- );
402
- }
403
- const parsed = ts.parseJsonConfigFileContent(
404
- configFile.config,
405
- ts.sys,
406
- path.dirname(configPath)
407
- );
408
- if (parsed.errors.length > 0) {
409
- const errorMessages = parsed.errors.map((e) => ts.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
410
- throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
411
- }
412
- compilerOptions = parsed.options;
413
- fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
414
- } else {
415
- compilerOptions = {
416
- target: ts.ScriptTarget.ES2022,
417
- module: ts.ModuleKind.NodeNext,
418
- moduleResolution: ts.ModuleResolutionKind.NodeNext,
419
- strict: true,
420
- skipLibCheck: true,
421
- declaration: true
422
- };
423
- fileNames = [absolutePath];
424
- }
425
- const program = ts.createProgram(fileNames, compilerOptions);
426
- const sourceFile = program.getSourceFile(absolutePath);
427
- if (!sourceFile) {
428
- throw new Error(`Could not find source file: ${absolutePath}`);
429
- }
430
- return {
431
- program,
432
- checker: program.getTypeChecker(),
433
- sourceFile
434
- };
435
- }
436
- function findNodeByName(sourceFile, name, predicate, getName) {
437
- let result = null;
438
- function visit(node) {
439
- if (result) return;
440
- if (predicate(node) && getName(node) === name) {
441
- result = node;
442
- return;
443
- }
444
- ts.forEachChild(node, visit);
445
- }
446
- visit(sourceFile);
447
- return result;
448
- }
449
- function findClassByName(sourceFile, className) {
450
- return findNodeByName(sourceFile, className, ts.isClassDeclaration, (n) => n.name?.text);
451
- }
452
- function findInterfaceByName(sourceFile, interfaceName) {
453
- return findNodeByName(sourceFile, interfaceName, ts.isInterfaceDeclaration, (n) => n.name.text);
454
- }
455
- function findTypeAliasByName(sourceFile, aliasName) {
456
- return findNodeByName(sourceFile, aliasName, ts.isTypeAliasDeclaration, (n) => n.name.text);
457
- }
458
392
 
459
393
  // src/analyzer/class-analyzer.ts
460
- import * as ts4 from "typescript";
394
+ import * as ts3 from "typescript";
461
395
 
462
396
  // src/analyzer/jsdoc-constraints.ts
463
- import * as ts3 from "typescript";
397
+ import * as ts2 from "typescript";
464
398
 
465
399
  // src/analyzer/tsdoc-parser.ts
466
- import * as ts2 from "typescript";
400
+ import * as ts from "typescript";
467
401
  import {
468
402
  TSDocParser,
469
403
  TSDocConfiguration,
@@ -561,10 +495,10 @@ function parseTSDocTags(node, file = "", options) {
561
495
  let placeholderProvenance;
562
496
  const sourceFile = node.getSourceFile();
563
497
  const sourceText = sourceFile.getFullText();
564
- const commentRanges = ts2.getLeadingCommentRanges(sourceText, node.getFullStart());
498
+ const commentRanges = ts.getLeadingCommentRanges(sourceText, node.getFullStart());
565
499
  if (commentRanges) {
566
500
  for (const range of commentRanges) {
567
- if (range.kind !== ts2.SyntaxKind.MultiLineCommentTrivia) {
501
+ if (range.kind !== ts.SyntaxKind.MultiLineCommentTrivia) {
568
502
  continue;
569
503
  }
570
504
  const commentText = sourceText.substring(range.pos, range.end);
@@ -582,26 +516,31 @@ function parseTSDocTags(node, file = "", options) {
582
516
  const text2 = extractBlockText(block).trim();
583
517
  if (text2 === "") continue;
584
518
  const provenance2 = provenanceForComment(range, sourceFile, file, tagName);
585
- if (tagName === "displayName") {
586
- if (!isMemberTargetDisplayName(text2) && displayName === void 0) {
587
- displayName = text2;
588
- displayNameProvenance = provenance2;
589
- }
590
- } else if (tagName === "format") {
591
- annotations.push({
592
- kind: "annotation",
593
- annotationKind: "format",
594
- value: text2,
595
- provenance: provenance2
596
- });
597
- } else {
598
- if (tagName === "description" && description === void 0) {
519
+ switch (tagName) {
520
+ case "displayName":
521
+ if (!isMemberTargetDisplayName(text2) && displayName === void 0) {
522
+ displayName = text2;
523
+ displayNameProvenance = provenance2;
524
+ }
525
+ break;
526
+ case "format":
527
+ annotations.push({
528
+ kind: "annotation",
529
+ annotationKind: "format",
530
+ value: text2,
531
+ provenance: provenance2
532
+ });
533
+ break;
534
+ case "description":
599
535
  description = text2;
600
536
  descriptionProvenance = provenance2;
601
- } else if (tagName === "placeholder" && placeholder === void 0) {
602
- placeholder = text2;
603
- placeholderProvenance = provenance2;
604
- }
537
+ break;
538
+ case "placeholder":
539
+ if (placeholder === void 0) {
540
+ placeholder = text2;
541
+ placeholderProvenance = provenance2;
542
+ }
543
+ break;
605
544
  }
606
545
  continue;
607
546
  }
@@ -631,6 +570,13 @@ function parseTSDocTags(node, file = "", options) {
631
570
  descriptionProvenance = provenanceForComment(range, sourceFile, file, "remarks");
632
571
  }
633
572
  }
573
+ if (description === void 0) {
574
+ const summary = extractPlainText(docComment.summarySection).trim();
575
+ if (summary !== "") {
576
+ description = summary;
577
+ descriptionProvenance = provenanceForComment(range, sourceFile, file, "summary");
578
+ }
579
+ }
634
580
  }
635
581
  }
636
582
  if (displayName !== void 0 && displayNameProvenance !== void 0) {
@@ -657,7 +603,7 @@ function parseTSDocTags(node, file = "", options) {
657
603
  provenance: placeholderProvenance
658
604
  });
659
605
  }
660
- const jsDocTagsAll = ts2.getJSDocTags(node);
606
+ const jsDocTagsAll = ts.getJSDocTags(node);
661
607
  for (const tag of jsDocTagsAll) {
662
608
  const tagName = normalizeConstraintTagName(tag.tagName.text);
663
609
  if (!TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
@@ -680,7 +626,7 @@ function parseTSDocTags(node, file = "", options) {
680
626
  function extractDisplayNameMetadata(node) {
681
627
  let displayName;
682
628
  const memberDisplayNames = /* @__PURE__ */ new Map();
683
- for (const tag of ts2.getJSDocTags(node)) {
629
+ for (const tag of ts.getJSDocTags(node)) {
684
630
  const tagName = normalizeConstraintTagName(tag.tagName.text);
685
631
  if (tagName !== "displayName") continue;
686
632
  const commentText = getTagCommentText(tag);
@@ -701,11 +647,11 @@ function extractDisplayNameMetadata(node) {
701
647
  }
702
648
  function extractPathTarget(text) {
703
649
  const trimmed = text.trimStart();
704
- const match = /^:([a-zA-Z_]\w*)\s+([\s\S]*)$/.exec(trimmed);
705
- if (!match?.[1] || !match[2]) return null;
650
+ const match = /^:([a-zA-Z_]\w*)(?:\s+([\s\S]*))?$/.exec(trimmed);
651
+ if (!match?.[1]) return null;
706
652
  return {
707
653
  path: { segments: [match[1]] },
708
- remainingText: match[2]
654
+ remainingText: match[2] ?? ""
709
655
  };
710
656
  }
711
657
  function extractBlockText(block) {
@@ -968,7 +914,7 @@ function getTagCommentText(tag) {
968
914
  if (typeof tag.comment === "string") {
969
915
  return tag.comment;
970
916
  }
971
- return ts2.getTextOfJSDocComment(tag.comment);
917
+ return ts.getTextOfJSDocComment(tag.comment);
972
918
  }
973
919
 
974
920
  // src/analyzer/jsdoc-constraints.ts
@@ -983,18 +929,18 @@ function extractJSDocAnnotationNodes(node, file = "", options) {
983
929
  function extractDefaultValueAnnotation(initializer, file = "") {
984
930
  if (!initializer) return null;
985
931
  let value;
986
- if (ts3.isStringLiteral(initializer)) {
932
+ if (ts2.isStringLiteral(initializer)) {
987
933
  value = initializer.text;
988
- } else if (ts3.isNumericLiteral(initializer)) {
934
+ } else if (ts2.isNumericLiteral(initializer)) {
989
935
  value = Number(initializer.text);
990
- } else if (initializer.kind === ts3.SyntaxKind.TrueKeyword) {
936
+ } else if (initializer.kind === ts2.SyntaxKind.TrueKeyword) {
991
937
  value = true;
992
- } else if (initializer.kind === ts3.SyntaxKind.FalseKeyword) {
938
+ } else if (initializer.kind === ts2.SyntaxKind.FalseKeyword) {
993
939
  value = false;
994
- } else if (initializer.kind === ts3.SyntaxKind.NullKeyword) {
940
+ } else if (initializer.kind === ts2.SyntaxKind.NullKeyword) {
995
941
  value = null;
996
- } else if (ts3.isPrefixUnaryExpression(initializer)) {
997
- if (initializer.operator === ts3.SyntaxKind.MinusToken && ts3.isNumericLiteral(initializer.operand)) {
942
+ } else if (ts2.isPrefixUnaryExpression(initializer)) {
943
+ if (initializer.operator === ts2.SyntaxKind.MinusToken && ts2.isNumericLiteral(initializer.operand)) {
998
944
  value = -Number(initializer.operand.text);
999
945
  }
1000
946
  }
@@ -1016,10 +962,10 @@ function extractDefaultValueAnnotation(initializer, file = "") {
1016
962
 
1017
963
  // src/analyzer/class-analyzer.ts
1018
964
  function isObjectType(type) {
1019
- return !!(type.flags & ts4.TypeFlags.Object);
965
+ return !!(type.flags & ts3.TypeFlags.Object);
1020
966
  }
1021
967
  function isTypeReference(type) {
1022
- return !!(type.flags & ts4.TypeFlags.Object) && !!(type.objectFlags & ts4.ObjectFlags.Reference);
968
+ return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
1023
969
  }
1024
970
  var RESOLVING_TYPE_PLACEHOLDER = {
1025
971
  kind: "object",
@@ -1049,7 +995,7 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
1049
995
  const instanceMethods = [];
1050
996
  const staticMethods = [];
1051
997
  for (const member of classDecl.members) {
1052
- if (ts4.isPropertyDeclaration(member)) {
998
+ if (ts3.isPropertyDeclaration(member)) {
1053
999
  const fieldNode = analyzeFieldToIR(
1054
1000
  member,
1055
1001
  checker,
@@ -1062,10 +1008,10 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
1062
1008
  fields.push(fieldNode);
1063
1009
  fieldLayouts.push({});
1064
1010
  }
1065
- } else if (ts4.isMethodDeclaration(member)) {
1011
+ } else if (ts3.isMethodDeclaration(member)) {
1066
1012
  const methodInfo = analyzeMethod(member, checker);
1067
1013
  if (methodInfo) {
1068
- const isStatic = member.modifiers?.some((m) => m.kind === ts4.SyntaxKind.StaticKeyword);
1014
+ const isStatic = member.modifiers?.some((m) => m.kind === ts3.SyntaxKind.StaticKeyword);
1069
1015
  if (isStatic) {
1070
1016
  staticMethods.push(methodInfo);
1071
1017
  } else {
@@ -1095,7 +1041,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
1095
1041
  );
1096
1042
  const visiting = /* @__PURE__ */ new Set();
1097
1043
  for (const member of interfaceDecl.members) {
1098
- if (ts4.isPropertySignature(member)) {
1044
+ if (ts3.isPropertySignature(member)) {
1099
1045
  const fieldNode = analyzeInterfacePropertyToIR(
1100
1046
  member,
1101
1047
  checker,
@@ -1121,10 +1067,10 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
1121
1067
  };
1122
1068
  }
1123
1069
  function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
1124
- if (!ts4.isTypeLiteralNode(typeAlias.type)) {
1070
+ if (!ts3.isTypeLiteralNode(typeAlias.type)) {
1125
1071
  const sourceFile = typeAlias.getSourceFile();
1126
1072
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
1127
- const kindDesc = ts4.SyntaxKind[typeAlias.type.kind] ?? "unknown";
1073
+ const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
1128
1074
  return {
1129
1075
  ok: false,
1130
1076
  error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
@@ -1140,7 +1086,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
1140
1086
  );
1141
1087
  const visiting = /* @__PURE__ */ new Set();
1142
1088
  for (const member of typeAlias.type.members) {
1143
- if (ts4.isPropertySignature(member)) {
1089
+ if (ts3.isPropertySignature(member)) {
1144
1090
  const fieldNode = analyzeInterfacePropertyToIR(
1145
1091
  member,
1146
1092
  checker,
@@ -1168,7 +1114,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
1168
1114
  };
1169
1115
  }
1170
1116
  function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
1171
- if (!ts4.isIdentifier(prop.name)) {
1117
+ if (!ts3.isIdentifier(prop.name)) {
1172
1118
  return null;
1173
1119
  }
1174
1120
  const name = prop.name.text;
@@ -1185,12 +1131,14 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extension
1185
1131
  extensionRegistry
1186
1132
  );
1187
1133
  const constraints = [];
1188
- if (prop.type) {
1134
+ if (prop.type && !shouldEmitPrimitiveAliasDefinition(prop.type, checker)) {
1189
1135
  constraints.push(
1190
1136
  ...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
1191
1137
  );
1192
1138
  }
1193
- constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
1139
+ constraints.push(
1140
+ ...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type))
1141
+ );
1194
1142
  let annotations = [];
1195
1143
  annotations.push(
1196
1144
  ...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
@@ -1211,7 +1159,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extension
1211
1159
  };
1212
1160
  }
1213
1161
  function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
1214
- if (!ts4.isIdentifier(prop.name)) {
1162
+ if (!ts3.isIdentifier(prop.name)) {
1215
1163
  return null;
1216
1164
  }
1217
1165
  const name = prop.name.text;
@@ -1228,12 +1176,14 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
1228
1176
  extensionRegistry
1229
1177
  );
1230
1178
  const constraints = [];
1231
- if (prop.type) {
1179
+ if (prop.type && !shouldEmitPrimitiveAliasDefinition(prop.type, checker)) {
1232
1180
  constraints.push(
1233
1181
  ...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
1234
1182
  );
1235
1183
  }
1236
- constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
1184
+ constraints.push(
1185
+ ...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type))
1186
+ );
1237
1187
  let annotations = [];
1238
1188
  annotations.push(
1239
1189
  ...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
@@ -1322,7 +1272,7 @@ function resolveRegisteredCustomType(sourceNode, extensionRegistry, checker) {
1322
1272
  return resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker);
1323
1273
  }
1324
1274
  function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker) {
1325
- if (ts4.isParenthesizedTypeNode(typeNode)) {
1275
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
1326
1276
  return resolveRegisteredCustomTypeFromTypeNode(typeNode.type, extensionRegistry, checker);
1327
1277
  }
1328
1278
  const typeName = getTypeNodeRegistrationName(typeNode);
@@ -1337,8 +1287,8 @@ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, ch
1337
1287
  payload: null
1338
1288
  };
1339
1289
  }
1340
- if (ts4.isTypeReferenceNode(typeNode) && ts4.isIdentifier(typeNode.typeName)) {
1341
- const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts4.isTypeAliasDeclaration);
1290
+ if (ts3.isTypeReferenceNode(typeNode) && ts3.isIdentifier(typeNode.typeName)) {
1291
+ const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
1342
1292
  if (aliasDecl !== void 0) {
1343
1293
  return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
1344
1294
  }
@@ -1346,22 +1296,22 @@ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, ch
1346
1296
  return null;
1347
1297
  }
1348
1298
  function extractTypeNodeFromSource(sourceNode) {
1349
- if (ts4.isPropertyDeclaration(sourceNode) || ts4.isPropertySignature(sourceNode) || ts4.isParameter(sourceNode) || ts4.isTypeAliasDeclaration(sourceNode)) {
1299
+ if (ts3.isPropertyDeclaration(sourceNode) || ts3.isPropertySignature(sourceNode) || ts3.isParameter(sourceNode) || ts3.isTypeAliasDeclaration(sourceNode)) {
1350
1300
  return sourceNode.type;
1351
1301
  }
1352
- if (ts4.isTypeNode(sourceNode)) {
1302
+ if (ts3.isTypeNode(sourceNode)) {
1353
1303
  return sourceNode;
1354
1304
  }
1355
1305
  return void 0;
1356
1306
  }
1357
1307
  function getTypeNodeRegistrationName(typeNode) {
1358
- if (ts4.isTypeReferenceNode(typeNode)) {
1359
- return ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
1308
+ if (ts3.isTypeReferenceNode(typeNode)) {
1309
+ return ts3.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
1360
1310
  }
1361
- if (ts4.isParenthesizedTypeNode(typeNode)) {
1311
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
1362
1312
  return getTypeNodeRegistrationName(typeNode.type);
1363
1313
  }
1364
- if (typeNode.kind === ts4.SyntaxKind.BigIntKeyword || typeNode.kind === ts4.SyntaxKind.StringKeyword || typeNode.kind === ts4.SyntaxKind.NumberKeyword || typeNode.kind === ts4.SyntaxKind.BooleanKeyword) {
1314
+ if (typeNode.kind === ts3.SyntaxKind.BigIntKeyword || typeNode.kind === ts3.SyntaxKind.StringKeyword || typeNode.kind === ts3.SyntaxKind.NumberKeyword || typeNode.kind === ts3.SyntaxKind.BooleanKeyword) {
1365
1315
  return typeNode.getText();
1366
1316
  }
1367
1317
  return null;
@@ -1371,19 +1321,34 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
1371
1321
  if (customType) {
1372
1322
  return customType;
1373
1323
  }
1374
- if (type.flags & ts4.TypeFlags.String) {
1324
+ const primitiveAlias = tryResolveNamedPrimitiveAlias(
1325
+ type,
1326
+ checker,
1327
+ file,
1328
+ typeRegistry,
1329
+ visiting,
1330
+ sourceNode,
1331
+ extensionRegistry
1332
+ );
1333
+ if (primitiveAlias) {
1334
+ return primitiveAlias;
1335
+ }
1336
+ if (type.flags & ts3.TypeFlags.String) {
1375
1337
  return { kind: "primitive", primitiveKind: "string" };
1376
1338
  }
1377
- if (type.flags & ts4.TypeFlags.Number) {
1339
+ if (type.flags & ts3.TypeFlags.Number) {
1378
1340
  return { kind: "primitive", primitiveKind: "number" };
1379
1341
  }
1380
- if (type.flags & ts4.TypeFlags.Boolean) {
1342
+ if (type.flags & (ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral)) {
1343
+ return { kind: "primitive", primitiveKind: "bigint" };
1344
+ }
1345
+ if (type.flags & ts3.TypeFlags.Boolean) {
1381
1346
  return { kind: "primitive", primitiveKind: "boolean" };
1382
1347
  }
1383
- if (type.flags & ts4.TypeFlags.Null) {
1348
+ if (type.flags & ts3.TypeFlags.Null) {
1384
1349
  return { kind: "primitive", primitiveKind: "null" };
1385
1350
  }
1386
- if (type.flags & ts4.TypeFlags.Undefined) {
1351
+ if (type.flags & ts3.TypeFlags.Undefined) {
1387
1352
  return { kind: "primitive", primitiveKind: "null" };
1388
1353
  }
1389
1354
  if (type.isStringLiteral()) {
@@ -1425,6 +1390,75 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
1425
1390
  }
1426
1391
  return { kind: "primitive", primitiveKind: "string" };
1427
1392
  }
1393
+ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
1394
+ if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
1395
+ return null;
1396
+ }
1397
+ const aliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration) ?? getReferencedTypeAliasDeclaration(sourceNode, checker);
1398
+ if (!aliasDecl) {
1399
+ return null;
1400
+ }
1401
+ const aliasName = aliasDecl.name.text;
1402
+ if (!typeRegistry[aliasName]) {
1403
+ const aliasType = checker.getTypeFromTypeNode(aliasDecl.type);
1404
+ const constraints = [
1405
+ ...extractJSDocConstraintNodes(aliasDecl, file, makeParseOptions(extensionRegistry)),
1406
+ ...extractTypeAliasConstraintNodes(aliasDecl.type, checker, file, extensionRegistry)
1407
+ ];
1408
+ const annotations = extractJSDocAnnotationNodes(
1409
+ aliasDecl,
1410
+ file,
1411
+ makeParseOptions(extensionRegistry)
1412
+ );
1413
+ typeRegistry[aliasName] = {
1414
+ name: aliasName,
1415
+ type: resolveAliasedPrimitiveTarget(
1416
+ aliasType,
1417
+ checker,
1418
+ file,
1419
+ typeRegistry,
1420
+ visiting,
1421
+ extensionRegistry
1422
+ ),
1423
+ ...constraints.length > 0 && { constraints },
1424
+ ...annotations.length > 0 && { annotations },
1425
+ provenance: provenanceForDeclaration(aliasDecl, file)
1426
+ };
1427
+ }
1428
+ return { kind: "reference", name: aliasName, typeArguments: [] };
1429
+ }
1430
+ function getReferencedTypeAliasDeclaration(sourceNode, checker) {
1431
+ const typeNode = sourceNode && (ts3.isPropertyDeclaration(sourceNode) || ts3.isPropertySignature(sourceNode) || ts3.isParameter(sourceNode)) ? sourceNode.type : void 0;
1432
+ if (!typeNode || !ts3.isTypeReferenceNode(typeNode)) {
1433
+ return void 0;
1434
+ }
1435
+ return checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
1436
+ }
1437
+ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
1438
+ if (!ts3.isTypeReferenceNode(typeNode)) {
1439
+ return false;
1440
+ }
1441
+ const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
1442
+ if (!aliasDecl) {
1443
+ return false;
1444
+ }
1445
+ const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
1446
+ return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
1447
+ }
1448
+ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, extensionRegistry) {
1449
+ const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
1450
+ if (nestedAliasDecl !== void 0) {
1451
+ return resolveAliasedPrimitiveTarget(
1452
+ checker.getTypeFromTypeNode(nestedAliasDecl.type),
1453
+ checker,
1454
+ file,
1455
+ typeRegistry,
1456
+ visiting,
1457
+ extensionRegistry
1458
+ );
1459
+ }
1460
+ return resolveTypeNode(type, checker, file, typeRegistry, visiting, void 0, extensionRegistry);
1461
+ }
1428
1462
  function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
1429
1463
  const typeName = getNamedTypeName(type);
1430
1464
  const namedDecl = getNamedTypeDeclaration(type);
@@ -1437,13 +1471,13 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
1437
1471
  (memberTypeNode) => !isNullishTypeNode(resolveAliasedTypeNode(memberTypeNode, checker))
1438
1472
  );
1439
1473
  const nonNullTypes = allTypes.filter(
1440
- (memberType) => !(memberType.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
1474
+ (memberType) => !(memberType.flags & (ts3.TypeFlags.Null | ts3.TypeFlags.Undefined))
1441
1475
  );
1442
1476
  const nonNullMembers = nonNullTypes.map((memberType, index) => ({
1443
1477
  memberType,
1444
1478
  sourceNode: nonNullSourceNodes.length === nonNullTypes.length ? nonNullSourceNodes[index] : void 0
1445
1479
  }));
1446
- const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
1480
+ const hasNull = allTypes.some((t) => t.flags & ts3.TypeFlags.Null);
1447
1481
  const memberDisplayNames = /* @__PURE__ */ new Map();
1448
1482
  if (namedDecl) {
1449
1483
  for (const [value, label] of extractDisplayNameMetadata(namedDecl).memberDisplayNames) {
@@ -1472,7 +1506,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
1472
1506
  const displayName = memberDisplayNames.get(String(value));
1473
1507
  return displayName !== void 0 ? { value, displayName } : { value };
1474
1508
  });
1475
- const isBooleanUnion2 = nonNullTypes.length === 2 && nonNullTypes.every((t) => t.flags & ts4.TypeFlags.BooleanLiteral);
1509
+ const isBooleanUnion2 = nonNullTypes.length === 2 && nonNullTypes.every((t) => t.flags & ts3.TypeFlags.BooleanLiteral);
1476
1510
  if (isBooleanUnion2) {
1477
1511
  const boolNode = { kind: "primitive", primitiveKind: "boolean" };
1478
1512
  const result = hasNull ? {
@@ -1558,7 +1592,7 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, exten
1558
1592
  if (type.getProperties().length > 0) {
1559
1593
  return null;
1560
1594
  }
1561
- const indexInfo = checker.getIndexInfoOfType(type, ts4.IndexKind.String);
1595
+ const indexInfo = checker.getIndexInfoOfType(type, ts3.IndexKind.String);
1562
1596
  if (!indexInfo) {
1563
1597
  return null;
1564
1598
  }
@@ -1669,7 +1703,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensio
1669
1703
  const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
1670
1704
  if (!declaration) continue;
1671
1705
  const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
1672
- const optional = !!(prop.flags & ts4.SymbolFlags.Optional);
1706
+ const optional = !!(prop.flags & ts3.SymbolFlags.Optional);
1673
1707
  const propTypeNode = resolveTypeNode(
1674
1708
  propType,
1675
1709
  checker,
@@ -1714,11 +1748,11 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
1714
1748
  for (const symbol of symbols) {
1715
1749
  const declarations = symbol.declarations;
1716
1750
  if (!declarations) continue;
1717
- const classDecl = declarations.find(ts4.isClassDeclaration);
1751
+ const classDecl = declarations.find(ts3.isClassDeclaration);
1718
1752
  if (classDecl) {
1719
1753
  const map = /* @__PURE__ */ new Map();
1720
1754
  for (const member of classDecl.members) {
1721
- if (ts4.isPropertyDeclaration(member) && ts4.isIdentifier(member.name)) {
1755
+ if (ts3.isPropertyDeclaration(member) && ts3.isIdentifier(member.name)) {
1722
1756
  const fieldNode = analyzeFieldToIR(
1723
1757
  member,
1724
1758
  checker,
@@ -1738,7 +1772,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
1738
1772
  }
1739
1773
  return map;
1740
1774
  }
1741
- const interfaceDecl = declarations.find(ts4.isInterfaceDeclaration);
1775
+ const interfaceDecl = declarations.find(ts3.isInterfaceDeclaration);
1742
1776
  if (interfaceDecl) {
1743
1777
  return buildFieldNodeInfoMap(
1744
1778
  interfaceDecl.members,
@@ -1749,8 +1783,8 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
1749
1783
  extensionRegistry
1750
1784
  );
1751
1785
  }
1752
- const typeAliasDecl = declarations.find(ts4.isTypeAliasDeclaration);
1753
- if (typeAliasDecl && ts4.isTypeLiteralNode(typeAliasDecl.type)) {
1786
+ const typeAliasDecl = declarations.find(ts3.isTypeAliasDeclaration);
1787
+ if (typeAliasDecl && ts3.isTypeLiteralNode(typeAliasDecl.type)) {
1754
1788
  return buildFieldNodeInfoMap(
1755
1789
  typeAliasDecl.type.members,
1756
1790
  checker,
@@ -1769,10 +1803,10 @@ function extractArrayElementTypeNode(sourceNode, checker) {
1769
1803
  return void 0;
1770
1804
  }
1771
1805
  const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
1772
- if (ts4.isArrayTypeNode(resolvedTypeNode)) {
1806
+ if (ts3.isArrayTypeNode(resolvedTypeNode)) {
1773
1807
  return resolvedTypeNode.elementType;
1774
1808
  }
1775
- if (ts4.isTypeReferenceNode(resolvedTypeNode) && ts4.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
1809
+ if (ts3.isTypeReferenceNode(resolvedTypeNode) && ts3.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
1776
1810
  return resolvedTypeNode.typeArguments[0];
1777
1811
  }
1778
1812
  return void 0;
@@ -1783,17 +1817,17 @@ function extractUnionMemberTypeNodes(sourceNode, checker) {
1783
1817
  return [];
1784
1818
  }
1785
1819
  const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
1786
- return ts4.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
1820
+ return ts3.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
1787
1821
  }
1788
1822
  function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
1789
- if (ts4.isParenthesizedTypeNode(typeNode)) {
1823
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
1790
1824
  return resolveAliasedTypeNode(typeNode.type, checker, visited);
1791
1825
  }
1792
- if (!ts4.isTypeReferenceNode(typeNode) || !ts4.isIdentifier(typeNode.typeName)) {
1826
+ if (!ts3.isTypeReferenceNode(typeNode) || !ts3.isIdentifier(typeNode.typeName)) {
1793
1827
  return typeNode;
1794
1828
  }
1795
1829
  const symbol = checker.getSymbolAtLocation(typeNode.typeName);
1796
- const aliasDecl = symbol?.declarations?.find(ts4.isTypeAliasDeclaration);
1830
+ const aliasDecl = symbol?.declarations?.find(ts3.isTypeAliasDeclaration);
1797
1831
  if (aliasDecl === void 0 || visited.has(aliasDecl)) {
1798
1832
  return typeNode;
1799
1833
  }
@@ -1801,15 +1835,15 @@ function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new
1801
1835
  return resolveAliasedTypeNode(aliasDecl.type, checker, visited);
1802
1836
  }
1803
1837
  function isNullishTypeNode(typeNode) {
1804
- if (typeNode.kind === ts4.SyntaxKind.NullKeyword || typeNode.kind === ts4.SyntaxKind.UndefinedKeyword) {
1838
+ if (typeNode.kind === ts3.SyntaxKind.NullKeyword || typeNode.kind === ts3.SyntaxKind.UndefinedKeyword) {
1805
1839
  return true;
1806
1840
  }
1807
- return ts4.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts4.SyntaxKind.NullKeyword || typeNode.literal.kind === ts4.SyntaxKind.UndefinedKeyword);
1841
+ return ts3.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts3.SyntaxKind.NullKeyword || typeNode.literal.kind === ts3.SyntaxKind.UndefinedKeyword);
1808
1842
  }
1809
1843
  function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, extensionRegistry) {
1810
1844
  const map = /* @__PURE__ */ new Map();
1811
1845
  for (const member of members) {
1812
- if (ts4.isPropertySignature(member)) {
1846
+ if (ts3.isPropertySignature(member)) {
1813
1847
  const fieldNode = analyzeInterfacePropertyToIR(
1814
1848
  member,
1815
1849
  checker,
@@ -1831,7 +1865,7 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, e
1831
1865
  }
1832
1866
  var MAX_ALIAS_CHAIN_DEPTH = 8;
1833
1867
  function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegistry, depth = 0) {
1834
- if (!ts4.isTypeReferenceNode(typeNode)) return [];
1868
+ if (!ts3.isTypeReferenceNode(typeNode)) return [];
1835
1869
  if (depth >= MAX_ALIAS_CHAIN_DEPTH) {
1836
1870
  const aliasName = typeNode.typeName.getText();
1837
1871
  throw new Error(
@@ -1840,9 +1874,9 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
1840
1874
  }
1841
1875
  const symbol = checker.getSymbolAtLocation(typeNode.typeName);
1842
1876
  if (!symbol?.declarations) return [];
1843
- const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
1877
+ const aliasDecl = symbol.declarations.find(ts3.isTypeAliasDeclaration);
1844
1878
  if (!aliasDecl) return [];
1845
- if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
1879
+ if (ts3.isTypeLiteralNode(aliasDecl.type)) return [];
1846
1880
  const aliasFieldType = resolveTypeNode(
1847
1881
  checker.getTypeAtLocation(aliasDecl.type),
1848
1882
  checker,
@@ -1858,13 +1892,7 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
1858
1892
  makeParseOptions(extensionRegistry, aliasFieldType)
1859
1893
  );
1860
1894
  constraints.push(
1861
- ...extractTypeAliasConstraintNodes(
1862
- aliasDecl.type,
1863
- checker,
1864
- file,
1865
- extensionRegistry,
1866
- depth + 1
1867
- )
1895
+ ...extractTypeAliasConstraintNodes(aliasDecl.type, checker, file, extensionRegistry, depth + 1)
1868
1896
  );
1869
1897
  return constraints;
1870
1898
  }
@@ -1891,14 +1919,14 @@ function getNamedTypeName(type) {
1891
1919
  const symbol = type.getSymbol();
1892
1920
  if (symbol?.declarations) {
1893
1921
  const decl = symbol.declarations[0];
1894
- if (decl && (ts4.isClassDeclaration(decl) || ts4.isInterfaceDeclaration(decl) || ts4.isTypeAliasDeclaration(decl))) {
1895
- const name = ts4.isClassDeclaration(decl) ? decl.name?.text : decl.name.text;
1922
+ if (decl && (ts3.isClassDeclaration(decl) || ts3.isInterfaceDeclaration(decl) || ts3.isTypeAliasDeclaration(decl))) {
1923
+ const name = ts3.isClassDeclaration(decl) ? decl.name?.text : decl.name.text;
1896
1924
  if (name) return name;
1897
1925
  }
1898
1926
  }
1899
1927
  const aliasSymbol = type.aliasSymbol;
1900
1928
  if (aliasSymbol?.declarations) {
1901
- const aliasDecl = aliasSymbol.declarations.find(ts4.isTypeAliasDeclaration);
1929
+ const aliasDecl = aliasSymbol.declarations.find(ts3.isTypeAliasDeclaration);
1902
1930
  if (aliasDecl) {
1903
1931
  return aliasDecl.name.text;
1904
1932
  }
@@ -1909,24 +1937,24 @@ function getNamedTypeDeclaration(type) {
1909
1937
  const symbol = type.getSymbol();
1910
1938
  if (symbol?.declarations) {
1911
1939
  const decl = symbol.declarations[0];
1912
- if (decl && (ts4.isClassDeclaration(decl) || ts4.isInterfaceDeclaration(decl) || ts4.isTypeAliasDeclaration(decl))) {
1940
+ if (decl && (ts3.isClassDeclaration(decl) || ts3.isInterfaceDeclaration(decl) || ts3.isTypeAliasDeclaration(decl))) {
1913
1941
  return decl;
1914
1942
  }
1915
1943
  }
1916
1944
  const aliasSymbol = type.aliasSymbol;
1917
1945
  if (aliasSymbol?.declarations) {
1918
- return aliasSymbol.declarations.find(ts4.isTypeAliasDeclaration);
1946
+ return aliasSymbol.declarations.find(ts3.isTypeAliasDeclaration);
1919
1947
  }
1920
1948
  return void 0;
1921
1949
  }
1922
1950
  function analyzeMethod(method, checker) {
1923
- if (!ts4.isIdentifier(method.name)) {
1951
+ if (!ts3.isIdentifier(method.name)) {
1924
1952
  return null;
1925
1953
  }
1926
1954
  const name = method.name.text;
1927
1955
  const parameters = [];
1928
1956
  for (const param of method.parameters) {
1929
- if (ts4.isIdentifier(param.name)) {
1957
+ if (ts3.isIdentifier(param.name)) {
1930
1958
  const paramInfo = analyzeParameter(param, checker);
1931
1959
  parameters.push(paramInfo);
1932
1960
  }
@@ -1937,7 +1965,7 @@ function analyzeMethod(method, checker) {
1937
1965
  return { name, parameters, returnTypeNode, returnType };
1938
1966
  }
1939
1967
  function analyzeParameter(param, checker) {
1940
- const name = ts4.isIdentifier(param.name) ? param.name.text : "param";
1968
+ const name = ts3.isIdentifier(param.name) ? param.name.text : "param";
1941
1969
  const typeNode = param.type;
1942
1970
  const type = checker.getTypeAtLocation(param);
1943
1971
  const formSpecExportName = detectFormSpecReference(typeNode);
@@ -1946,20 +1974,90 @@ function analyzeParameter(param, checker) {
1946
1974
  }
1947
1975
  function detectFormSpecReference(typeNode) {
1948
1976
  if (!typeNode) return null;
1949
- if (!ts4.isTypeReferenceNode(typeNode)) return null;
1950
- const typeName = ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : ts4.isQualifiedName(typeNode.typeName) ? typeNode.typeName.right.text : null;
1977
+ if (!ts3.isTypeReferenceNode(typeNode)) return null;
1978
+ const typeName = ts3.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : ts3.isQualifiedName(typeNode.typeName) ? typeNode.typeName.right.text : null;
1951
1979
  if (typeName !== "InferSchema" && typeName !== "InferFormSchema") return null;
1952
1980
  const typeArg = typeNode.typeArguments?.[0];
1953
- if (!typeArg || !ts4.isTypeQueryNode(typeArg)) return null;
1954
- if (ts4.isIdentifier(typeArg.exprName)) {
1981
+ if (!typeArg || !ts3.isTypeQueryNode(typeArg)) return null;
1982
+ if (ts3.isIdentifier(typeArg.exprName)) {
1955
1983
  return typeArg.exprName.text;
1956
1984
  }
1957
- if (ts4.isQualifiedName(typeArg.exprName)) {
1985
+ if (ts3.isQualifiedName(typeArg.exprName)) {
1958
1986
  return typeArg.exprName.right.text;
1959
1987
  }
1960
1988
  return null;
1961
1989
  }
1962
1990
 
1991
+ // src/analyzer/program.ts
1992
+ function createProgramContext(filePath) {
1993
+ const absolutePath = path.resolve(filePath);
1994
+ const fileDir = path.dirname(absolutePath);
1995
+ const configPath = ts4.findConfigFile(fileDir, ts4.sys.fileExists.bind(ts4.sys), "tsconfig.json");
1996
+ let compilerOptions;
1997
+ let fileNames;
1998
+ if (configPath) {
1999
+ const configFile = ts4.readConfigFile(configPath, ts4.sys.readFile.bind(ts4.sys));
2000
+ if (configFile.error) {
2001
+ throw new Error(
2002
+ `Error reading tsconfig.json: ${ts4.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`
2003
+ );
2004
+ }
2005
+ const parsed = ts4.parseJsonConfigFileContent(
2006
+ configFile.config,
2007
+ ts4.sys,
2008
+ path.dirname(configPath)
2009
+ );
2010
+ if (parsed.errors.length > 0) {
2011
+ const errorMessages = parsed.errors.map((e) => ts4.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
2012
+ throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
2013
+ }
2014
+ compilerOptions = parsed.options;
2015
+ fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
2016
+ } else {
2017
+ compilerOptions = {
2018
+ target: ts4.ScriptTarget.ES2022,
2019
+ module: ts4.ModuleKind.NodeNext,
2020
+ moduleResolution: ts4.ModuleResolutionKind.NodeNext,
2021
+ strict: true,
2022
+ skipLibCheck: true,
2023
+ declaration: true
2024
+ };
2025
+ fileNames = [absolutePath];
2026
+ }
2027
+ const program = ts4.createProgram(fileNames, compilerOptions);
2028
+ const sourceFile = program.getSourceFile(absolutePath);
2029
+ if (!sourceFile) {
2030
+ throw new Error(`Could not find source file: ${absolutePath}`);
2031
+ }
2032
+ return {
2033
+ program,
2034
+ checker: program.getTypeChecker(),
2035
+ sourceFile
2036
+ };
2037
+ }
2038
+ function findNodeByName(sourceFile, name, predicate, getName) {
2039
+ let result = null;
2040
+ function visit(node) {
2041
+ if (result) return;
2042
+ if (predicate(node) && getName(node) === name) {
2043
+ result = node;
2044
+ return;
2045
+ }
2046
+ ts4.forEachChild(node, visit);
2047
+ }
2048
+ visit(sourceFile);
2049
+ return result;
2050
+ }
2051
+ function findClassByName(sourceFile, className) {
2052
+ return findNodeByName(sourceFile, className, ts4.isClassDeclaration, (n) => n.name?.text);
2053
+ }
2054
+ function findInterfaceByName(sourceFile, interfaceName) {
2055
+ return findNodeByName(sourceFile, interfaceName, ts4.isInterfaceDeclaration, (n) => n.name.text);
2056
+ }
2057
+ function findTypeAliasByName(sourceFile, aliasName) {
2058
+ return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
2059
+ }
2060
+
1963
2061
  // src/json-schema/ir-generator.ts
1964
2062
  function makeContext(options) {
1965
2063
  const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
@@ -1978,6 +2076,9 @@ function generateJsonSchemaFromIR(ir, options) {
1978
2076
  const ctx = makeContext(options);
1979
2077
  for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
1980
2078
  ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
2079
+ if (typeDef.constraints && typeDef.constraints.length > 0) {
2080
+ applyConstraints(ctx.defs[name], typeDef.constraints, ctx);
2081
+ }
1981
2082
  if (typeDef.annotations && typeDef.annotations.length > 0) {
1982
2083
  applyAnnotations(ctx.defs[name], typeDef.annotations, ctx);
1983
2084
  }
@@ -2146,7 +2247,9 @@ function generateTypeNode(type, ctx) {
2146
2247
  }
2147
2248
  }
2148
2249
  function generatePrimitiveType(type) {
2149
- return { type: type.primitiveKind };
2250
+ return {
2251
+ type: type.primitiveKind === "integer" || type.primitiveKind === "bigint" ? "integer" : type.primitiveKind
2252
+ };
2150
2253
  }
2151
2254
  function generateEnumType(type) {
2152
2255
  const hasDisplayNames = type.members.some((m) => m.displayName !== void 0);
@@ -2319,7 +2422,7 @@ function applyAnnotations(schema, annotations, ctx) {
2319
2422
  case "deprecated":
2320
2423
  schema.deprecated = true;
2321
2424
  if (annotation.message !== void 0 && annotation.message !== "") {
2322
- schema["x-formspec-deprecation-description"] = annotation.message;
2425
+ schema[`${ctx.vendorPrefix}-deprecation-description`] = annotation.message;
2323
2426
  }
2324
2427
  break;
2325
2428
  case "placeholder":
@@ -2610,15 +2713,6 @@ function generateUiSchemaFromIR(ir) {
2610
2713
  return parseOrThrow(uiSchema, result, "UI Schema");
2611
2714
  }
2612
2715
 
2613
- // src/generators/class-schema.ts
2614
- function generateClassSchemas(analysis, source, options) {
2615
- const ir = canonicalizeTSDoc(analysis, source);
2616
- return {
2617
- jsonSchema: generateJsonSchemaFromIR(ir, options),
2618
- uiSchema: generateUiSchemaFromIR(ir)
2619
- };
2620
- }
2621
-
2622
2716
  // src/validate/constraint-validator.ts
2623
2717
  import { normalizeConstraintTagName as normalizeConstraintTagName2 } from "@formspec/core";
2624
2718
  function addContradiction(ctx, message, primary, related) {
@@ -3096,6 +3190,26 @@ function dereferenceType(ctx, type) {
3096
3190
  }
3097
3191
  return current;
3098
3192
  }
3193
+ function collectReferencedTypeConstraints(ctx, type) {
3194
+ const collected = [];
3195
+ let current = type;
3196
+ const seen = /* @__PURE__ */ new Set();
3197
+ while (current.kind === "reference") {
3198
+ if (seen.has(current.name)) {
3199
+ break;
3200
+ }
3201
+ seen.add(current.name);
3202
+ const definition = ctx.typeRegistry[current.name];
3203
+ if (definition === void 0) {
3204
+ break;
3205
+ }
3206
+ if (definition.constraints !== void 0) {
3207
+ collected.push(...definition.constraints);
3208
+ }
3209
+ current = definition.type;
3210
+ }
3211
+ return collected;
3212
+ }
3099
3213
  function resolvePathTargetType(ctx, type, segments) {
3100
3214
  const effectiveType = dereferenceType(ctx, type);
3101
3215
  if (segments.length === 0) {
@@ -3117,12 +3231,33 @@ function resolvePathTargetType(ctx, type, segments) {
3117
3231
  }
3118
3232
  return { kind: "unresolvable", type: effectiveType };
3119
3233
  }
3234
+ function isNullType(type) {
3235
+ return type.kind === "primitive" && type.primitiveKind === "null";
3236
+ }
3237
+ function collectCustomConstraintCandidateTypes(ctx, type) {
3238
+ const effectiveType = dereferenceType(ctx, type);
3239
+ const candidates = [effectiveType];
3240
+ if (effectiveType.kind === "array") {
3241
+ candidates.push(...collectCustomConstraintCandidateTypes(ctx, effectiveType.items));
3242
+ }
3243
+ if (effectiveType.kind === "union") {
3244
+ const memberTypes = effectiveType.members.map((member) => dereferenceType(ctx, member));
3245
+ const nonNullMembers = memberTypes.filter((member) => !isNullType(member));
3246
+ if (nonNullMembers.length === 1 && nonNullMembers.length < memberTypes.length) {
3247
+ const [nullableMember] = nonNullMembers;
3248
+ if (nullableMember !== void 0) {
3249
+ candidates.push(...collectCustomConstraintCandidateTypes(ctx, nullableMember));
3250
+ }
3251
+ }
3252
+ }
3253
+ return candidates;
3254
+ }
3120
3255
  function formatPathTargetFieldName(fieldName, path2) {
3121
3256
  return path2.length === 0 ? fieldName : `${fieldName}.${path2.join(".")}`;
3122
3257
  }
3123
3258
  function checkConstraintOnType(ctx, fieldName, type, constraint) {
3124
3259
  const effectiveType = dereferenceType(ctx, type);
3125
- const isNumber = effectiveType.kind === "primitive" && effectiveType.primitiveKind === "number";
3260
+ const isNumber = effectiveType.kind === "primitive" && ["number", "integer", "bigint"].includes(effectiveType.primitiveKind);
3126
3261
  const isString = effectiveType.kind === "primitive" && effectiveType.primitiveKind === "string";
3127
3262
  const isArray = effectiveType.kind === "array";
3128
3263
  const isEnum = effectiveType.kind === "enum";
@@ -3180,7 +3315,9 @@ function checkConstraintOnType(ctx, fieldName, type, constraint) {
3180
3315
  break;
3181
3316
  }
3182
3317
  case "const": {
3183
- const isPrimitiveConstType = effectiveType.kind === "primitive" && ["string", "number", "boolean", "null"].includes(effectiveType.primitiveKind) || effectiveType.kind === "enum";
3318
+ const isPrimitiveConstType = effectiveType.kind === "primitive" && ["string", "number", "integer", "bigint", "boolean", "null"].includes(
3319
+ effectiveType.primitiveKind
3320
+ ) || effectiveType.kind === "enum";
3184
3321
  if (!isPrimitiveConstType) {
3185
3322
  addTypeMismatch(
3186
3323
  ctx,
@@ -3191,7 +3328,8 @@ function checkConstraintOnType(ctx, fieldName, type, constraint) {
3191
3328
  }
3192
3329
  if (effectiveType.kind === "primitive") {
3193
3330
  const valueType = constraint.value === null ? "null" : Array.isArray(constraint.value) ? "array" : typeof constraint.value;
3194
- if (valueType !== effectiveType.primitiveKind) {
3331
+ const expectedValueType = effectiveType.primitiveKind === "integer" || effectiveType.primitiveKind === "bigint" ? "number" : effectiveType.primitiveKind;
3332
+ if (valueType !== expectedValueType) {
3195
3333
  addTypeMismatch(
3196
3334
  ctx,
3197
3335
  `Field "${fieldName}": @const value type "${valueType}" is incompatible with field type "${effectiveType.primitiveKind}"`,
@@ -3260,11 +3398,14 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
3260
3398
  );
3261
3399
  return;
3262
3400
  }
3401
+ const candidateTypes = collectCustomConstraintCandidateTypes(ctx, type);
3263
3402
  const normalizedTagName = constraint.provenance.tagName === void 0 ? void 0 : normalizeConstraintTagName2(constraint.provenance.tagName.replace(/^@/, ""));
3264
3403
  if (normalizedTagName !== void 0) {
3265
3404
  const tagRegistration = ctx.extensionRegistry.findConstraintTag(normalizedTagName);
3266
3405
  const extensionId = getExtensionIdFromConstraintId(constraint.constraintId);
3267
- if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName && tagRegistration.registration.isApplicableToType?.(type) === false) {
3406
+ if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName && !candidateTypes.some(
3407
+ (candidateType) => tagRegistration.registration.isApplicableToType?.(candidateType) !== false
3408
+ )) {
3268
3409
  addTypeMismatch(
3269
3410
  ctx,
3270
3411
  `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
@@ -3274,7 +3415,7 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
3274
3415
  }
3275
3416
  }
3276
3417
  if (registration.applicableTypes === null) {
3277
- if (registration.isApplicableToType?.(type) === false) {
3418
+ if (!candidateTypes.some((candidateType) => registration.isApplicableToType?.(candidateType) !== false)) {
3278
3419
  addTypeMismatch(
3279
3420
  ctx,
3280
3421
  `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
@@ -3283,7 +3424,11 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
3283
3424
  }
3284
3425
  return;
3285
3426
  }
3286
- if (!registration.applicableTypes.includes(type.kind) || registration.isApplicableToType?.(type) === false) {
3427
+ const applicableTypes = registration.applicableTypes;
3428
+ const matchesApplicableType = candidateTypes.some(
3429
+ (candidateType) => applicableTypes.includes(candidateType.kind) && registration.isApplicableToType?.(candidateType) !== false
3430
+ );
3431
+ if (!matchesApplicableType) {
3287
3432
  addTypeMismatch(
3288
3433
  ctx,
3289
3434
  `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
@@ -3292,7 +3437,10 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
3292
3437
  }
3293
3438
  }
3294
3439
  function validateFieldNode(ctx, field) {
3295
- validateConstraints(ctx, field.name, field.type, field.constraints);
3440
+ validateConstraints(ctx, field.name, field.type, [
3441
+ ...collectReferencedTypeConstraints(ctx, field.type),
3442
+ ...field.constraints
3443
+ ]);
3296
3444
  if (field.type.kind === "object") {
3297
3445
  for (const prop of field.type.properties) {
3298
3446
  validateObjectProperty(ctx, field.name, prop);
@@ -3301,7 +3449,10 @@ function validateFieldNode(ctx, field) {
3301
3449
  }
3302
3450
  function validateObjectProperty(ctx, parentName, prop) {
3303
3451
  const qualifiedName = `${parentName}.${prop.name}`;
3304
- validateConstraints(ctx, qualifiedName, prop.type, prop.constraints);
3452
+ validateConstraints(ctx, qualifiedName, prop.type, [
3453
+ ...collectReferencedTypeConstraints(ctx, prop.type),
3454
+ ...prop.constraints
3455
+ ]);
3305
3456
  if (prop.type.kind === "object") {
3306
3457
  for (const nestedProp of prop.type.properties) {
3307
3458
  validateObjectProperty(ctx, qualifiedName, nestedProp);
@@ -3353,6 +3504,36 @@ function validateIR(ir, options) {
3353
3504
  };
3354
3505
  }
3355
3506
 
3507
+ // src/generators/class-schema.ts
3508
+ function generateClassSchemas(analysis, source, options) {
3509
+ const ir = canonicalizeTSDoc(analysis, source);
3510
+ const validationResult = validateIR(ir, {
3511
+ ...options?.extensionRegistry !== void 0 && {
3512
+ extensionRegistry: options.extensionRegistry
3513
+ },
3514
+ ...options?.vendorPrefix !== void 0 && { vendorPrefix: options.vendorPrefix }
3515
+ });
3516
+ if (!validationResult.valid) {
3517
+ throw new Error(formatValidationError(validationResult.diagnostics));
3518
+ }
3519
+ return {
3520
+ jsonSchema: generateJsonSchemaFromIR(ir, options),
3521
+ uiSchema: generateUiSchemaFromIR(ir)
3522
+ };
3523
+ }
3524
+ function formatValidationError(diagnostics) {
3525
+ const lines = diagnostics.map((diagnostic) => {
3526
+ const primary = formatLocation(diagnostic.primaryLocation);
3527
+ const related = diagnostic.relatedLocations.length > 0 ? ` [related: ${diagnostic.relatedLocations.map(formatLocation).join(", ")}]` : "";
3528
+ return `${diagnostic.code}: ${diagnostic.message} (${primary})${related}`;
3529
+ });
3530
+ return `FormSpec validation failed:
3531
+ ${lines.map((line) => `- ${line}`).join("\n")}`;
3532
+ }
3533
+ function formatLocation(location) {
3534
+ return `${location.file}:${String(location.line)}:${String(location.column)}`;
3535
+ }
3536
+
3356
3537
  // src/extensions/registry.ts
3357
3538
  function createExtensionRegistry(extensions) {
3358
3539
  const typeMap = /* @__PURE__ */ new Map();
@@ -3454,6 +3635,7 @@ function typeToJsonSchema(type, checker) {
3454
3635
  provenance: fieldProvenance
3455
3636
  }
3456
3637
  ],
3638
+ rootAnnotations: [],
3457
3639
  typeRegistry,
3458
3640
  provenance: fieldProvenance
3459
3641
  };