@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/index.cjs CHANGED
@@ -82,6 +82,7 @@ function canonicalizeChainDSL(form) {
82
82
  kind: "form-ir",
83
83
  irVersion: import_core.IR_VERSION,
84
84
  elements: canonicalizeElements(form.elements),
85
+ rootAnnotations: [],
85
86
  typeRegistry: {},
86
87
  provenance: CHAIN_DSL_PROVENANCE
87
88
  };
@@ -387,6 +388,7 @@ function canonicalizeTSDoc(analysis, source) {
387
388
  irVersion: import_core2.IR_VERSION,
388
389
  elements,
389
390
  typeRegistry: analysis.typeRegistry,
391
+ ...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { rootAnnotations: analysis.annotations },
390
392
  ...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { annotations: analysis.annotations },
391
393
  provenance
392
394
  };
@@ -464,6 +466,9 @@ function generateJsonSchemaFromIR(ir, options) {
464
466
  const ctx = makeContext(options);
465
467
  for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
466
468
  ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
469
+ if (typeDef.constraints && typeDef.constraints.length > 0) {
470
+ applyConstraints(ctx.defs[name], typeDef.constraints, ctx);
471
+ }
467
472
  if (typeDef.annotations && typeDef.annotations.length > 0) {
468
473
  applyAnnotations(ctx.defs[name], typeDef.annotations, ctx);
469
474
  }
@@ -632,7 +637,9 @@ function generateTypeNode(type, ctx) {
632
637
  }
633
638
  }
634
639
  function generatePrimitiveType(type) {
635
- return { type: type.primitiveKind };
640
+ return {
641
+ type: type.primitiveKind === "integer" || type.primitiveKind === "bigint" ? "integer" : type.primitiveKind
642
+ };
636
643
  }
637
644
  function generateEnumType(type) {
638
645
  const hasDisplayNames = type.members.some((m) => m.displayName !== void 0);
@@ -805,7 +812,7 @@ function applyAnnotations(schema, annotations, ctx) {
805
812
  case "deprecated":
806
813
  schema.deprecated = true;
807
814
  if (annotation.message !== void 0 && annotation.message !== "") {
808
- schema["x-formspec-deprecation-description"] = annotation.message;
815
+ schema[`${ctx.vendorPrefix}-deprecation-description`] = annotation.message;
809
816
  }
810
817
  break;
811
818
  case "placeholder":
@@ -1263,85 +1270,17 @@ var jsonSchema7Schema = import_zod3.z.lazy(
1263
1270
  );
1264
1271
 
1265
1272
  // src/analyzer/program.ts
1266
- var ts = __toESM(require("typescript"), 1);
1273
+ var ts4 = __toESM(require("typescript"), 1);
1267
1274
  var path = __toESM(require("path"), 1);
1268
- function createProgramContext(filePath) {
1269
- const absolutePath = path.resolve(filePath);
1270
- const fileDir = path.dirname(absolutePath);
1271
- const configPath = ts.findConfigFile(fileDir, ts.sys.fileExists.bind(ts.sys), "tsconfig.json");
1272
- let compilerOptions;
1273
- let fileNames;
1274
- if (configPath) {
1275
- const configFile = ts.readConfigFile(configPath, ts.sys.readFile.bind(ts.sys));
1276
- if (configFile.error) {
1277
- throw new Error(
1278
- `Error reading tsconfig.json: ${ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`
1279
- );
1280
- }
1281
- const parsed = ts.parseJsonConfigFileContent(
1282
- configFile.config,
1283
- ts.sys,
1284
- path.dirname(configPath)
1285
- );
1286
- if (parsed.errors.length > 0) {
1287
- const errorMessages = parsed.errors.map((e) => ts.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
1288
- throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
1289
- }
1290
- compilerOptions = parsed.options;
1291
- fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
1292
- } else {
1293
- compilerOptions = {
1294
- target: ts.ScriptTarget.ES2022,
1295
- module: ts.ModuleKind.NodeNext,
1296
- moduleResolution: ts.ModuleResolutionKind.NodeNext,
1297
- strict: true,
1298
- skipLibCheck: true,
1299
- declaration: true
1300
- };
1301
- fileNames = [absolutePath];
1302
- }
1303
- const program = ts.createProgram(fileNames, compilerOptions);
1304
- const sourceFile = program.getSourceFile(absolutePath);
1305
- if (!sourceFile) {
1306
- throw new Error(`Could not find source file: ${absolutePath}`);
1307
- }
1308
- return {
1309
- program,
1310
- checker: program.getTypeChecker(),
1311
- sourceFile
1312
- };
1313
- }
1314
- function findNodeByName(sourceFile, name, predicate, getName) {
1315
- let result = null;
1316
- function visit(node) {
1317
- if (result) return;
1318
- if (predicate(node) && getName(node) === name) {
1319
- result = node;
1320
- return;
1321
- }
1322
- ts.forEachChild(node, visit);
1323
- }
1324
- visit(sourceFile);
1325
- return result;
1326
- }
1327
- function findClassByName(sourceFile, className) {
1328
- return findNodeByName(sourceFile, className, ts.isClassDeclaration, (n) => n.name?.text);
1329
- }
1330
- function findInterfaceByName(sourceFile, interfaceName) {
1331
- return findNodeByName(sourceFile, interfaceName, ts.isInterfaceDeclaration, (n) => n.name.text);
1332
- }
1333
- function findTypeAliasByName(sourceFile, aliasName) {
1334
- return findNodeByName(sourceFile, aliasName, ts.isTypeAliasDeclaration, (n) => n.name.text);
1335
- }
1336
1275
 
1337
1276
  // src/analyzer/class-analyzer.ts
1338
- var ts4 = __toESM(require("typescript"), 1);
1277
+ var ts3 = __toESM(require("typescript"), 1);
1339
1278
 
1340
1279
  // src/analyzer/jsdoc-constraints.ts
1341
- var ts3 = __toESM(require("typescript"), 1);
1280
+ var ts2 = __toESM(require("typescript"), 1);
1342
1281
 
1343
1282
  // src/analyzer/tsdoc-parser.ts
1344
- var ts2 = __toESM(require("typescript"), 1);
1283
+ var ts = __toESM(require("typescript"), 1);
1345
1284
  var import_tsdoc = require("@microsoft/tsdoc");
1346
1285
  var import_core3 = require("@formspec/core");
1347
1286
 
@@ -1427,10 +1366,10 @@ function parseTSDocTags(node, file = "", options) {
1427
1366
  let placeholderProvenance;
1428
1367
  const sourceFile = node.getSourceFile();
1429
1368
  const sourceText = sourceFile.getFullText();
1430
- const commentRanges = ts2.getLeadingCommentRanges(sourceText, node.getFullStart());
1369
+ const commentRanges = ts.getLeadingCommentRanges(sourceText, node.getFullStart());
1431
1370
  if (commentRanges) {
1432
1371
  for (const range of commentRanges) {
1433
- if (range.kind !== ts2.SyntaxKind.MultiLineCommentTrivia) {
1372
+ if (range.kind !== ts.SyntaxKind.MultiLineCommentTrivia) {
1434
1373
  continue;
1435
1374
  }
1436
1375
  const commentText = sourceText.substring(range.pos, range.end);
@@ -1448,26 +1387,31 @@ function parseTSDocTags(node, file = "", options) {
1448
1387
  const text2 = extractBlockText(block).trim();
1449
1388
  if (text2 === "") continue;
1450
1389
  const provenance2 = provenanceForComment(range, sourceFile, file, tagName);
1451
- if (tagName === "displayName") {
1452
- if (!isMemberTargetDisplayName(text2) && displayName === void 0) {
1453
- displayName = text2;
1454
- displayNameProvenance = provenance2;
1455
- }
1456
- } else if (tagName === "format") {
1457
- annotations.push({
1458
- kind: "annotation",
1459
- annotationKind: "format",
1460
- value: text2,
1461
- provenance: provenance2
1462
- });
1463
- } else {
1464
- if (tagName === "description" && description === void 0) {
1390
+ switch (tagName) {
1391
+ case "displayName":
1392
+ if (!isMemberTargetDisplayName(text2) && displayName === void 0) {
1393
+ displayName = text2;
1394
+ displayNameProvenance = provenance2;
1395
+ }
1396
+ break;
1397
+ case "format":
1398
+ annotations.push({
1399
+ kind: "annotation",
1400
+ annotationKind: "format",
1401
+ value: text2,
1402
+ provenance: provenance2
1403
+ });
1404
+ break;
1405
+ case "description":
1465
1406
  description = text2;
1466
1407
  descriptionProvenance = provenance2;
1467
- } else if (tagName === "placeholder" && placeholder === void 0) {
1468
- placeholder = text2;
1469
- placeholderProvenance = provenance2;
1470
- }
1408
+ break;
1409
+ case "placeholder":
1410
+ if (placeholder === void 0) {
1411
+ placeholder = text2;
1412
+ placeholderProvenance = provenance2;
1413
+ }
1414
+ break;
1471
1415
  }
1472
1416
  continue;
1473
1417
  }
@@ -1497,6 +1441,13 @@ function parseTSDocTags(node, file = "", options) {
1497
1441
  descriptionProvenance = provenanceForComment(range, sourceFile, file, "remarks");
1498
1442
  }
1499
1443
  }
1444
+ if (description === void 0) {
1445
+ const summary = extractPlainText(docComment.summarySection).trim();
1446
+ if (summary !== "") {
1447
+ description = summary;
1448
+ descriptionProvenance = provenanceForComment(range, sourceFile, file, "summary");
1449
+ }
1450
+ }
1500
1451
  }
1501
1452
  }
1502
1453
  if (displayName !== void 0 && displayNameProvenance !== void 0) {
@@ -1523,7 +1474,7 @@ function parseTSDocTags(node, file = "", options) {
1523
1474
  provenance: placeholderProvenance
1524
1475
  });
1525
1476
  }
1526
- const jsDocTagsAll = ts2.getJSDocTags(node);
1477
+ const jsDocTagsAll = ts.getJSDocTags(node);
1527
1478
  for (const tag of jsDocTagsAll) {
1528
1479
  const tagName = (0, import_core3.normalizeConstraintTagName)(tag.tagName.text);
1529
1480
  if (!TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
@@ -1546,7 +1497,7 @@ function parseTSDocTags(node, file = "", options) {
1546
1497
  function extractDisplayNameMetadata(node) {
1547
1498
  let displayName;
1548
1499
  const memberDisplayNames = /* @__PURE__ */ new Map();
1549
- for (const tag of ts2.getJSDocTags(node)) {
1500
+ for (const tag of ts.getJSDocTags(node)) {
1550
1501
  const tagName = (0, import_core3.normalizeConstraintTagName)(tag.tagName.text);
1551
1502
  if (tagName !== "displayName") continue;
1552
1503
  const commentText = getTagCommentText(tag);
@@ -1567,11 +1518,11 @@ function extractDisplayNameMetadata(node) {
1567
1518
  }
1568
1519
  function extractPathTarget(text) {
1569
1520
  const trimmed = text.trimStart();
1570
- const match = /^:([a-zA-Z_]\w*)\s+([\s\S]*)$/.exec(trimmed);
1571
- if (!match?.[1] || !match[2]) return null;
1521
+ const match = /^:([a-zA-Z_]\w*)(?:\s+([\s\S]*))?$/.exec(trimmed);
1522
+ if (!match?.[1]) return null;
1572
1523
  return {
1573
1524
  path: { segments: [match[1]] },
1574
- remainingText: match[2]
1525
+ remainingText: match[2] ?? ""
1575
1526
  };
1576
1527
  }
1577
1528
  function extractBlockText(block) {
@@ -1834,7 +1785,7 @@ function getTagCommentText(tag) {
1834
1785
  if (typeof tag.comment === "string") {
1835
1786
  return tag.comment;
1836
1787
  }
1837
- return ts2.getTextOfJSDocComment(tag.comment);
1788
+ return ts.getTextOfJSDocComment(tag.comment);
1838
1789
  }
1839
1790
 
1840
1791
  // src/analyzer/jsdoc-constraints.ts
@@ -1849,18 +1800,18 @@ function extractJSDocAnnotationNodes(node, file = "", options) {
1849
1800
  function extractDefaultValueAnnotation(initializer, file = "") {
1850
1801
  if (!initializer) return null;
1851
1802
  let value;
1852
- if (ts3.isStringLiteral(initializer)) {
1803
+ if (ts2.isStringLiteral(initializer)) {
1853
1804
  value = initializer.text;
1854
- } else if (ts3.isNumericLiteral(initializer)) {
1805
+ } else if (ts2.isNumericLiteral(initializer)) {
1855
1806
  value = Number(initializer.text);
1856
- } else if (initializer.kind === ts3.SyntaxKind.TrueKeyword) {
1807
+ } else if (initializer.kind === ts2.SyntaxKind.TrueKeyword) {
1857
1808
  value = true;
1858
- } else if (initializer.kind === ts3.SyntaxKind.FalseKeyword) {
1809
+ } else if (initializer.kind === ts2.SyntaxKind.FalseKeyword) {
1859
1810
  value = false;
1860
- } else if (initializer.kind === ts3.SyntaxKind.NullKeyword) {
1811
+ } else if (initializer.kind === ts2.SyntaxKind.NullKeyword) {
1861
1812
  value = null;
1862
- } else if (ts3.isPrefixUnaryExpression(initializer)) {
1863
- if (initializer.operator === ts3.SyntaxKind.MinusToken && ts3.isNumericLiteral(initializer.operand)) {
1813
+ } else if (ts2.isPrefixUnaryExpression(initializer)) {
1814
+ if (initializer.operator === ts2.SyntaxKind.MinusToken && ts2.isNumericLiteral(initializer.operand)) {
1864
1815
  value = -Number(initializer.operand.text);
1865
1816
  }
1866
1817
  }
@@ -1882,10 +1833,10 @@ function extractDefaultValueAnnotation(initializer, file = "") {
1882
1833
 
1883
1834
  // src/analyzer/class-analyzer.ts
1884
1835
  function isObjectType(type) {
1885
- return !!(type.flags & ts4.TypeFlags.Object);
1836
+ return !!(type.flags & ts3.TypeFlags.Object);
1886
1837
  }
1887
1838
  function isTypeReference(type) {
1888
- return !!(type.flags & ts4.TypeFlags.Object) && !!(type.objectFlags & ts4.ObjectFlags.Reference);
1839
+ return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
1889
1840
  }
1890
1841
  var RESOLVING_TYPE_PLACEHOLDER = {
1891
1842
  kind: "object",
@@ -1915,7 +1866,7 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
1915
1866
  const instanceMethods = [];
1916
1867
  const staticMethods = [];
1917
1868
  for (const member of classDecl.members) {
1918
- if (ts4.isPropertyDeclaration(member)) {
1869
+ if (ts3.isPropertyDeclaration(member)) {
1919
1870
  const fieldNode = analyzeFieldToIR(
1920
1871
  member,
1921
1872
  checker,
@@ -1928,10 +1879,10 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
1928
1879
  fields.push(fieldNode);
1929
1880
  fieldLayouts.push({});
1930
1881
  }
1931
- } else if (ts4.isMethodDeclaration(member)) {
1882
+ } else if (ts3.isMethodDeclaration(member)) {
1932
1883
  const methodInfo = analyzeMethod(member, checker);
1933
1884
  if (methodInfo) {
1934
- const isStatic = member.modifiers?.some((m) => m.kind === ts4.SyntaxKind.StaticKeyword);
1885
+ const isStatic = member.modifiers?.some((m) => m.kind === ts3.SyntaxKind.StaticKeyword);
1935
1886
  if (isStatic) {
1936
1887
  staticMethods.push(methodInfo);
1937
1888
  } else {
@@ -1961,7 +1912,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
1961
1912
  );
1962
1913
  const visiting = /* @__PURE__ */ new Set();
1963
1914
  for (const member of interfaceDecl.members) {
1964
- if (ts4.isPropertySignature(member)) {
1915
+ if (ts3.isPropertySignature(member)) {
1965
1916
  const fieldNode = analyzeInterfacePropertyToIR(
1966
1917
  member,
1967
1918
  checker,
@@ -1987,10 +1938,10 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
1987
1938
  };
1988
1939
  }
1989
1940
  function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
1990
- if (!ts4.isTypeLiteralNode(typeAlias.type)) {
1941
+ if (!ts3.isTypeLiteralNode(typeAlias.type)) {
1991
1942
  const sourceFile = typeAlias.getSourceFile();
1992
1943
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
1993
- const kindDesc = ts4.SyntaxKind[typeAlias.type.kind] ?? "unknown";
1944
+ const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
1994
1945
  return {
1995
1946
  ok: false,
1996
1947
  error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
@@ -2006,7 +1957,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
2006
1957
  );
2007
1958
  const visiting = /* @__PURE__ */ new Set();
2008
1959
  for (const member of typeAlias.type.members) {
2009
- if (ts4.isPropertySignature(member)) {
1960
+ if (ts3.isPropertySignature(member)) {
2010
1961
  const fieldNode = analyzeInterfacePropertyToIR(
2011
1962
  member,
2012
1963
  checker,
@@ -2034,7 +1985,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
2034
1985
  };
2035
1986
  }
2036
1987
  function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
2037
- if (!ts4.isIdentifier(prop.name)) {
1988
+ if (!ts3.isIdentifier(prop.name)) {
2038
1989
  return null;
2039
1990
  }
2040
1991
  const name = prop.name.text;
@@ -2051,12 +2002,14 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extension
2051
2002
  extensionRegistry
2052
2003
  );
2053
2004
  const constraints = [];
2054
- if (prop.type) {
2005
+ if (prop.type && !shouldEmitPrimitiveAliasDefinition(prop.type, checker)) {
2055
2006
  constraints.push(
2056
2007
  ...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
2057
2008
  );
2058
2009
  }
2059
- constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
2010
+ constraints.push(
2011
+ ...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type))
2012
+ );
2060
2013
  let annotations = [];
2061
2014
  annotations.push(
2062
2015
  ...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
@@ -2077,7 +2030,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extension
2077
2030
  };
2078
2031
  }
2079
2032
  function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
2080
- if (!ts4.isIdentifier(prop.name)) {
2033
+ if (!ts3.isIdentifier(prop.name)) {
2081
2034
  return null;
2082
2035
  }
2083
2036
  const name = prop.name.text;
@@ -2094,12 +2047,14 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
2094
2047
  extensionRegistry
2095
2048
  );
2096
2049
  const constraints = [];
2097
- if (prop.type) {
2050
+ if (prop.type && !shouldEmitPrimitiveAliasDefinition(prop.type, checker)) {
2098
2051
  constraints.push(
2099
2052
  ...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
2100
2053
  );
2101
2054
  }
2102
- constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
2055
+ constraints.push(
2056
+ ...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type))
2057
+ );
2103
2058
  let annotations = [];
2104
2059
  annotations.push(
2105
2060
  ...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
@@ -2188,7 +2143,7 @@ function resolveRegisteredCustomType(sourceNode, extensionRegistry, checker) {
2188
2143
  return resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker);
2189
2144
  }
2190
2145
  function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker) {
2191
- if (ts4.isParenthesizedTypeNode(typeNode)) {
2146
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
2192
2147
  return resolveRegisteredCustomTypeFromTypeNode(typeNode.type, extensionRegistry, checker);
2193
2148
  }
2194
2149
  const typeName = getTypeNodeRegistrationName(typeNode);
@@ -2203,8 +2158,8 @@ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, ch
2203
2158
  payload: null
2204
2159
  };
2205
2160
  }
2206
- if (ts4.isTypeReferenceNode(typeNode) && ts4.isIdentifier(typeNode.typeName)) {
2207
- const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts4.isTypeAliasDeclaration);
2161
+ if (ts3.isTypeReferenceNode(typeNode) && ts3.isIdentifier(typeNode.typeName)) {
2162
+ const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
2208
2163
  if (aliasDecl !== void 0) {
2209
2164
  return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
2210
2165
  }
@@ -2212,22 +2167,22 @@ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, ch
2212
2167
  return null;
2213
2168
  }
2214
2169
  function extractTypeNodeFromSource(sourceNode) {
2215
- if (ts4.isPropertyDeclaration(sourceNode) || ts4.isPropertySignature(sourceNode) || ts4.isParameter(sourceNode) || ts4.isTypeAliasDeclaration(sourceNode)) {
2170
+ if (ts3.isPropertyDeclaration(sourceNode) || ts3.isPropertySignature(sourceNode) || ts3.isParameter(sourceNode) || ts3.isTypeAliasDeclaration(sourceNode)) {
2216
2171
  return sourceNode.type;
2217
2172
  }
2218
- if (ts4.isTypeNode(sourceNode)) {
2173
+ if (ts3.isTypeNode(sourceNode)) {
2219
2174
  return sourceNode;
2220
2175
  }
2221
2176
  return void 0;
2222
2177
  }
2223
2178
  function getTypeNodeRegistrationName(typeNode) {
2224
- if (ts4.isTypeReferenceNode(typeNode)) {
2225
- return ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
2179
+ if (ts3.isTypeReferenceNode(typeNode)) {
2180
+ return ts3.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
2226
2181
  }
2227
- if (ts4.isParenthesizedTypeNode(typeNode)) {
2182
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
2228
2183
  return getTypeNodeRegistrationName(typeNode.type);
2229
2184
  }
2230
- if (typeNode.kind === ts4.SyntaxKind.BigIntKeyword || typeNode.kind === ts4.SyntaxKind.StringKeyword || typeNode.kind === ts4.SyntaxKind.NumberKeyword || typeNode.kind === ts4.SyntaxKind.BooleanKeyword) {
2185
+ if (typeNode.kind === ts3.SyntaxKind.BigIntKeyword || typeNode.kind === ts3.SyntaxKind.StringKeyword || typeNode.kind === ts3.SyntaxKind.NumberKeyword || typeNode.kind === ts3.SyntaxKind.BooleanKeyword) {
2231
2186
  return typeNode.getText();
2232
2187
  }
2233
2188
  return null;
@@ -2237,19 +2192,34 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
2237
2192
  if (customType) {
2238
2193
  return customType;
2239
2194
  }
2240
- if (type.flags & ts4.TypeFlags.String) {
2195
+ const primitiveAlias = tryResolveNamedPrimitiveAlias(
2196
+ type,
2197
+ checker,
2198
+ file,
2199
+ typeRegistry,
2200
+ visiting,
2201
+ sourceNode,
2202
+ extensionRegistry
2203
+ );
2204
+ if (primitiveAlias) {
2205
+ return primitiveAlias;
2206
+ }
2207
+ if (type.flags & ts3.TypeFlags.String) {
2241
2208
  return { kind: "primitive", primitiveKind: "string" };
2242
2209
  }
2243
- if (type.flags & ts4.TypeFlags.Number) {
2210
+ if (type.flags & ts3.TypeFlags.Number) {
2244
2211
  return { kind: "primitive", primitiveKind: "number" };
2245
2212
  }
2246
- if (type.flags & ts4.TypeFlags.Boolean) {
2213
+ if (type.flags & (ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral)) {
2214
+ return { kind: "primitive", primitiveKind: "bigint" };
2215
+ }
2216
+ if (type.flags & ts3.TypeFlags.Boolean) {
2247
2217
  return { kind: "primitive", primitiveKind: "boolean" };
2248
2218
  }
2249
- if (type.flags & ts4.TypeFlags.Null) {
2219
+ if (type.flags & ts3.TypeFlags.Null) {
2250
2220
  return { kind: "primitive", primitiveKind: "null" };
2251
2221
  }
2252
- if (type.flags & ts4.TypeFlags.Undefined) {
2222
+ if (type.flags & ts3.TypeFlags.Undefined) {
2253
2223
  return { kind: "primitive", primitiveKind: "null" };
2254
2224
  }
2255
2225
  if (type.isStringLiteral()) {
@@ -2291,6 +2261,75 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
2291
2261
  }
2292
2262
  return { kind: "primitive", primitiveKind: "string" };
2293
2263
  }
2264
+ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
2265
+ if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
2266
+ return null;
2267
+ }
2268
+ const aliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration) ?? getReferencedTypeAliasDeclaration(sourceNode, checker);
2269
+ if (!aliasDecl) {
2270
+ return null;
2271
+ }
2272
+ const aliasName = aliasDecl.name.text;
2273
+ if (!typeRegistry[aliasName]) {
2274
+ const aliasType = checker.getTypeFromTypeNode(aliasDecl.type);
2275
+ const constraints = [
2276
+ ...extractJSDocConstraintNodes(aliasDecl, file, makeParseOptions(extensionRegistry)),
2277
+ ...extractTypeAliasConstraintNodes(aliasDecl.type, checker, file, extensionRegistry)
2278
+ ];
2279
+ const annotations = extractJSDocAnnotationNodes(
2280
+ aliasDecl,
2281
+ file,
2282
+ makeParseOptions(extensionRegistry)
2283
+ );
2284
+ typeRegistry[aliasName] = {
2285
+ name: aliasName,
2286
+ type: resolveAliasedPrimitiveTarget(
2287
+ aliasType,
2288
+ checker,
2289
+ file,
2290
+ typeRegistry,
2291
+ visiting,
2292
+ extensionRegistry
2293
+ ),
2294
+ ...constraints.length > 0 && { constraints },
2295
+ ...annotations.length > 0 && { annotations },
2296
+ provenance: provenanceForDeclaration(aliasDecl, file)
2297
+ };
2298
+ }
2299
+ return { kind: "reference", name: aliasName, typeArguments: [] };
2300
+ }
2301
+ function getReferencedTypeAliasDeclaration(sourceNode, checker) {
2302
+ const typeNode = sourceNode && (ts3.isPropertyDeclaration(sourceNode) || ts3.isPropertySignature(sourceNode) || ts3.isParameter(sourceNode)) ? sourceNode.type : void 0;
2303
+ if (!typeNode || !ts3.isTypeReferenceNode(typeNode)) {
2304
+ return void 0;
2305
+ }
2306
+ return checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
2307
+ }
2308
+ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
2309
+ if (!ts3.isTypeReferenceNode(typeNode)) {
2310
+ return false;
2311
+ }
2312
+ const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
2313
+ if (!aliasDecl) {
2314
+ return false;
2315
+ }
2316
+ const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
2317
+ return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
2318
+ }
2319
+ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, extensionRegistry) {
2320
+ const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
2321
+ if (nestedAliasDecl !== void 0) {
2322
+ return resolveAliasedPrimitiveTarget(
2323
+ checker.getTypeFromTypeNode(nestedAliasDecl.type),
2324
+ checker,
2325
+ file,
2326
+ typeRegistry,
2327
+ visiting,
2328
+ extensionRegistry
2329
+ );
2330
+ }
2331
+ return resolveTypeNode(type, checker, file, typeRegistry, visiting, void 0, extensionRegistry);
2332
+ }
2294
2333
  function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
2295
2334
  const typeName = getNamedTypeName(type);
2296
2335
  const namedDecl = getNamedTypeDeclaration(type);
@@ -2303,13 +2342,13 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2303
2342
  (memberTypeNode) => !isNullishTypeNode(resolveAliasedTypeNode(memberTypeNode, checker))
2304
2343
  );
2305
2344
  const nonNullTypes = allTypes.filter(
2306
- (memberType) => !(memberType.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
2345
+ (memberType) => !(memberType.flags & (ts3.TypeFlags.Null | ts3.TypeFlags.Undefined))
2307
2346
  );
2308
2347
  const nonNullMembers = nonNullTypes.map((memberType, index) => ({
2309
2348
  memberType,
2310
2349
  sourceNode: nonNullSourceNodes.length === nonNullTypes.length ? nonNullSourceNodes[index] : void 0
2311
2350
  }));
2312
- const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
2351
+ const hasNull = allTypes.some((t) => t.flags & ts3.TypeFlags.Null);
2313
2352
  const memberDisplayNames = /* @__PURE__ */ new Map();
2314
2353
  if (namedDecl) {
2315
2354
  for (const [value, label] of extractDisplayNameMetadata(namedDecl).memberDisplayNames) {
@@ -2338,7 +2377,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2338
2377
  const displayName = memberDisplayNames.get(String(value));
2339
2378
  return displayName !== void 0 ? { value, displayName } : { value };
2340
2379
  });
2341
- const isBooleanUnion2 = nonNullTypes.length === 2 && nonNullTypes.every((t) => t.flags & ts4.TypeFlags.BooleanLiteral);
2380
+ const isBooleanUnion2 = nonNullTypes.length === 2 && nonNullTypes.every((t) => t.flags & ts3.TypeFlags.BooleanLiteral);
2342
2381
  if (isBooleanUnion2) {
2343
2382
  const boolNode = { kind: "primitive", primitiveKind: "boolean" };
2344
2383
  const result = hasNull ? {
@@ -2424,7 +2463,7 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, exten
2424
2463
  if (type.getProperties().length > 0) {
2425
2464
  return null;
2426
2465
  }
2427
- const indexInfo = checker.getIndexInfoOfType(type, ts4.IndexKind.String);
2466
+ const indexInfo = checker.getIndexInfoOfType(type, ts3.IndexKind.String);
2428
2467
  if (!indexInfo) {
2429
2468
  return null;
2430
2469
  }
@@ -2535,7 +2574,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensio
2535
2574
  const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
2536
2575
  if (!declaration) continue;
2537
2576
  const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
2538
- const optional = !!(prop.flags & ts4.SymbolFlags.Optional);
2577
+ const optional = !!(prop.flags & ts3.SymbolFlags.Optional);
2539
2578
  const propTypeNode = resolveTypeNode(
2540
2579
  propType,
2541
2580
  checker,
@@ -2580,11 +2619,11 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2580
2619
  for (const symbol of symbols) {
2581
2620
  const declarations = symbol.declarations;
2582
2621
  if (!declarations) continue;
2583
- const classDecl = declarations.find(ts4.isClassDeclaration);
2622
+ const classDecl = declarations.find(ts3.isClassDeclaration);
2584
2623
  if (classDecl) {
2585
2624
  const map = /* @__PURE__ */ new Map();
2586
2625
  for (const member of classDecl.members) {
2587
- if (ts4.isPropertyDeclaration(member) && ts4.isIdentifier(member.name)) {
2626
+ if (ts3.isPropertyDeclaration(member) && ts3.isIdentifier(member.name)) {
2588
2627
  const fieldNode = analyzeFieldToIR(
2589
2628
  member,
2590
2629
  checker,
@@ -2604,7 +2643,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2604
2643
  }
2605
2644
  return map;
2606
2645
  }
2607
- const interfaceDecl = declarations.find(ts4.isInterfaceDeclaration);
2646
+ const interfaceDecl = declarations.find(ts3.isInterfaceDeclaration);
2608
2647
  if (interfaceDecl) {
2609
2648
  return buildFieldNodeInfoMap(
2610
2649
  interfaceDecl.members,
@@ -2615,8 +2654,8 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2615
2654
  extensionRegistry
2616
2655
  );
2617
2656
  }
2618
- const typeAliasDecl = declarations.find(ts4.isTypeAliasDeclaration);
2619
- if (typeAliasDecl && ts4.isTypeLiteralNode(typeAliasDecl.type)) {
2657
+ const typeAliasDecl = declarations.find(ts3.isTypeAliasDeclaration);
2658
+ if (typeAliasDecl && ts3.isTypeLiteralNode(typeAliasDecl.type)) {
2620
2659
  return buildFieldNodeInfoMap(
2621
2660
  typeAliasDecl.type.members,
2622
2661
  checker,
@@ -2635,10 +2674,10 @@ function extractArrayElementTypeNode(sourceNode, checker) {
2635
2674
  return void 0;
2636
2675
  }
2637
2676
  const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
2638
- if (ts4.isArrayTypeNode(resolvedTypeNode)) {
2677
+ if (ts3.isArrayTypeNode(resolvedTypeNode)) {
2639
2678
  return resolvedTypeNode.elementType;
2640
2679
  }
2641
- if (ts4.isTypeReferenceNode(resolvedTypeNode) && ts4.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
2680
+ if (ts3.isTypeReferenceNode(resolvedTypeNode) && ts3.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
2642
2681
  return resolvedTypeNode.typeArguments[0];
2643
2682
  }
2644
2683
  return void 0;
@@ -2649,17 +2688,17 @@ function extractUnionMemberTypeNodes(sourceNode, checker) {
2649
2688
  return [];
2650
2689
  }
2651
2690
  const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
2652
- return ts4.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
2691
+ return ts3.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
2653
2692
  }
2654
2693
  function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
2655
- if (ts4.isParenthesizedTypeNode(typeNode)) {
2694
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
2656
2695
  return resolveAliasedTypeNode(typeNode.type, checker, visited);
2657
2696
  }
2658
- if (!ts4.isTypeReferenceNode(typeNode) || !ts4.isIdentifier(typeNode.typeName)) {
2697
+ if (!ts3.isTypeReferenceNode(typeNode) || !ts3.isIdentifier(typeNode.typeName)) {
2659
2698
  return typeNode;
2660
2699
  }
2661
2700
  const symbol = checker.getSymbolAtLocation(typeNode.typeName);
2662
- const aliasDecl = symbol?.declarations?.find(ts4.isTypeAliasDeclaration);
2701
+ const aliasDecl = symbol?.declarations?.find(ts3.isTypeAliasDeclaration);
2663
2702
  if (aliasDecl === void 0 || visited.has(aliasDecl)) {
2664
2703
  return typeNode;
2665
2704
  }
@@ -2667,15 +2706,15 @@ function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new
2667
2706
  return resolveAliasedTypeNode(aliasDecl.type, checker, visited);
2668
2707
  }
2669
2708
  function isNullishTypeNode(typeNode) {
2670
- if (typeNode.kind === ts4.SyntaxKind.NullKeyword || typeNode.kind === ts4.SyntaxKind.UndefinedKeyword) {
2709
+ if (typeNode.kind === ts3.SyntaxKind.NullKeyword || typeNode.kind === ts3.SyntaxKind.UndefinedKeyword) {
2671
2710
  return true;
2672
2711
  }
2673
- return ts4.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts4.SyntaxKind.NullKeyword || typeNode.literal.kind === ts4.SyntaxKind.UndefinedKeyword);
2712
+ return ts3.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts3.SyntaxKind.NullKeyword || typeNode.literal.kind === ts3.SyntaxKind.UndefinedKeyword);
2674
2713
  }
2675
2714
  function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, extensionRegistry) {
2676
2715
  const map = /* @__PURE__ */ new Map();
2677
2716
  for (const member of members) {
2678
- if (ts4.isPropertySignature(member)) {
2717
+ if (ts3.isPropertySignature(member)) {
2679
2718
  const fieldNode = analyzeInterfacePropertyToIR(
2680
2719
  member,
2681
2720
  checker,
@@ -2697,7 +2736,7 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, e
2697
2736
  }
2698
2737
  var MAX_ALIAS_CHAIN_DEPTH = 8;
2699
2738
  function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegistry, depth = 0) {
2700
- if (!ts4.isTypeReferenceNode(typeNode)) return [];
2739
+ if (!ts3.isTypeReferenceNode(typeNode)) return [];
2701
2740
  if (depth >= MAX_ALIAS_CHAIN_DEPTH) {
2702
2741
  const aliasName = typeNode.typeName.getText();
2703
2742
  throw new Error(
@@ -2706,9 +2745,9 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
2706
2745
  }
2707
2746
  const symbol = checker.getSymbolAtLocation(typeNode.typeName);
2708
2747
  if (!symbol?.declarations) return [];
2709
- const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
2748
+ const aliasDecl = symbol.declarations.find(ts3.isTypeAliasDeclaration);
2710
2749
  if (!aliasDecl) return [];
2711
- if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
2750
+ if (ts3.isTypeLiteralNode(aliasDecl.type)) return [];
2712
2751
  const aliasFieldType = resolveTypeNode(
2713
2752
  checker.getTypeAtLocation(aliasDecl.type),
2714
2753
  checker,
@@ -2724,13 +2763,7 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
2724
2763
  makeParseOptions(extensionRegistry, aliasFieldType)
2725
2764
  );
2726
2765
  constraints.push(
2727
- ...extractTypeAliasConstraintNodes(
2728
- aliasDecl.type,
2729
- checker,
2730
- file,
2731
- extensionRegistry,
2732
- depth + 1
2733
- )
2766
+ ...extractTypeAliasConstraintNodes(aliasDecl.type, checker, file, extensionRegistry, depth + 1)
2734
2767
  );
2735
2768
  return constraints;
2736
2769
  }
@@ -2757,14 +2790,14 @@ function getNamedTypeName(type) {
2757
2790
  const symbol = type.getSymbol();
2758
2791
  if (symbol?.declarations) {
2759
2792
  const decl = symbol.declarations[0];
2760
- if (decl && (ts4.isClassDeclaration(decl) || ts4.isInterfaceDeclaration(decl) || ts4.isTypeAliasDeclaration(decl))) {
2761
- const name = ts4.isClassDeclaration(decl) ? decl.name?.text : decl.name.text;
2793
+ if (decl && (ts3.isClassDeclaration(decl) || ts3.isInterfaceDeclaration(decl) || ts3.isTypeAliasDeclaration(decl))) {
2794
+ const name = ts3.isClassDeclaration(decl) ? decl.name?.text : decl.name.text;
2762
2795
  if (name) return name;
2763
2796
  }
2764
2797
  }
2765
2798
  const aliasSymbol = type.aliasSymbol;
2766
2799
  if (aliasSymbol?.declarations) {
2767
- const aliasDecl = aliasSymbol.declarations.find(ts4.isTypeAliasDeclaration);
2800
+ const aliasDecl = aliasSymbol.declarations.find(ts3.isTypeAliasDeclaration);
2768
2801
  if (aliasDecl) {
2769
2802
  return aliasDecl.name.text;
2770
2803
  }
@@ -2775,24 +2808,24 @@ function getNamedTypeDeclaration(type) {
2775
2808
  const symbol = type.getSymbol();
2776
2809
  if (symbol?.declarations) {
2777
2810
  const decl = symbol.declarations[0];
2778
- if (decl && (ts4.isClassDeclaration(decl) || ts4.isInterfaceDeclaration(decl) || ts4.isTypeAliasDeclaration(decl))) {
2811
+ if (decl && (ts3.isClassDeclaration(decl) || ts3.isInterfaceDeclaration(decl) || ts3.isTypeAliasDeclaration(decl))) {
2779
2812
  return decl;
2780
2813
  }
2781
2814
  }
2782
2815
  const aliasSymbol = type.aliasSymbol;
2783
2816
  if (aliasSymbol?.declarations) {
2784
- return aliasSymbol.declarations.find(ts4.isTypeAliasDeclaration);
2817
+ return aliasSymbol.declarations.find(ts3.isTypeAliasDeclaration);
2785
2818
  }
2786
2819
  return void 0;
2787
2820
  }
2788
2821
  function analyzeMethod(method, checker) {
2789
- if (!ts4.isIdentifier(method.name)) {
2822
+ if (!ts3.isIdentifier(method.name)) {
2790
2823
  return null;
2791
2824
  }
2792
2825
  const name = method.name.text;
2793
2826
  const parameters = [];
2794
2827
  for (const param of method.parameters) {
2795
- if (ts4.isIdentifier(param.name)) {
2828
+ if (ts3.isIdentifier(param.name)) {
2796
2829
  const paramInfo = analyzeParameter(param, checker);
2797
2830
  parameters.push(paramInfo);
2798
2831
  }
@@ -2803,7 +2836,7 @@ function analyzeMethod(method, checker) {
2803
2836
  return { name, parameters, returnTypeNode, returnType };
2804
2837
  }
2805
2838
  function analyzeParameter(param, checker) {
2806
- const name = ts4.isIdentifier(param.name) ? param.name.text : "param";
2839
+ const name = ts3.isIdentifier(param.name) ? param.name.text : "param";
2807
2840
  const typeNode = param.type;
2808
2841
  const type = checker.getTypeAtLocation(param);
2809
2842
  const formSpecExportName = detectFormSpecReference(typeNode);
@@ -2812,28 +2845,932 @@ function analyzeParameter(param, checker) {
2812
2845
  }
2813
2846
  function detectFormSpecReference(typeNode) {
2814
2847
  if (!typeNode) return null;
2815
- if (!ts4.isTypeReferenceNode(typeNode)) return null;
2816
- const typeName = ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : ts4.isQualifiedName(typeNode.typeName) ? typeNode.typeName.right.text : null;
2848
+ if (!ts3.isTypeReferenceNode(typeNode)) return null;
2849
+ const typeName = ts3.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : ts3.isQualifiedName(typeNode.typeName) ? typeNode.typeName.right.text : null;
2817
2850
  if (typeName !== "InferSchema" && typeName !== "InferFormSchema") return null;
2818
2851
  const typeArg = typeNode.typeArguments?.[0];
2819
- if (!typeArg || !ts4.isTypeQueryNode(typeArg)) return null;
2820
- if (ts4.isIdentifier(typeArg.exprName)) {
2852
+ if (!typeArg || !ts3.isTypeQueryNode(typeArg)) return null;
2853
+ if (ts3.isIdentifier(typeArg.exprName)) {
2821
2854
  return typeArg.exprName.text;
2822
2855
  }
2823
- if (ts4.isQualifiedName(typeArg.exprName)) {
2856
+ if (ts3.isQualifiedName(typeArg.exprName)) {
2824
2857
  return typeArg.exprName.right.text;
2825
2858
  }
2826
2859
  return null;
2827
2860
  }
2828
2861
 
2862
+ // src/analyzer/program.ts
2863
+ function createProgramContext(filePath) {
2864
+ const absolutePath = path.resolve(filePath);
2865
+ const fileDir = path.dirname(absolutePath);
2866
+ const configPath = ts4.findConfigFile(fileDir, ts4.sys.fileExists.bind(ts4.sys), "tsconfig.json");
2867
+ let compilerOptions;
2868
+ let fileNames;
2869
+ if (configPath) {
2870
+ const configFile = ts4.readConfigFile(configPath, ts4.sys.readFile.bind(ts4.sys));
2871
+ if (configFile.error) {
2872
+ throw new Error(
2873
+ `Error reading tsconfig.json: ${ts4.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`
2874
+ );
2875
+ }
2876
+ const parsed = ts4.parseJsonConfigFileContent(
2877
+ configFile.config,
2878
+ ts4.sys,
2879
+ path.dirname(configPath)
2880
+ );
2881
+ if (parsed.errors.length > 0) {
2882
+ const errorMessages = parsed.errors.map((e) => ts4.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
2883
+ throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
2884
+ }
2885
+ compilerOptions = parsed.options;
2886
+ fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
2887
+ } else {
2888
+ compilerOptions = {
2889
+ target: ts4.ScriptTarget.ES2022,
2890
+ module: ts4.ModuleKind.NodeNext,
2891
+ moduleResolution: ts4.ModuleResolutionKind.NodeNext,
2892
+ strict: true,
2893
+ skipLibCheck: true,
2894
+ declaration: true
2895
+ };
2896
+ fileNames = [absolutePath];
2897
+ }
2898
+ const program = ts4.createProgram(fileNames, compilerOptions);
2899
+ const sourceFile = program.getSourceFile(absolutePath);
2900
+ if (!sourceFile) {
2901
+ throw new Error(`Could not find source file: ${absolutePath}`);
2902
+ }
2903
+ return {
2904
+ program,
2905
+ checker: program.getTypeChecker(),
2906
+ sourceFile
2907
+ };
2908
+ }
2909
+ function findNodeByName(sourceFile, name, predicate, getName) {
2910
+ let result = null;
2911
+ function visit(node) {
2912
+ if (result) return;
2913
+ if (predicate(node) && getName(node) === name) {
2914
+ result = node;
2915
+ return;
2916
+ }
2917
+ ts4.forEachChild(node, visit);
2918
+ }
2919
+ visit(sourceFile);
2920
+ return result;
2921
+ }
2922
+ function findClassByName(sourceFile, className) {
2923
+ return findNodeByName(sourceFile, className, ts4.isClassDeclaration, (n) => n.name?.text);
2924
+ }
2925
+ function findInterfaceByName(sourceFile, interfaceName) {
2926
+ return findNodeByName(sourceFile, interfaceName, ts4.isInterfaceDeclaration, (n) => n.name.text);
2927
+ }
2928
+ function findTypeAliasByName(sourceFile, aliasName) {
2929
+ return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
2930
+ }
2931
+ function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry) {
2932
+ const ctx = createProgramContext(filePath);
2933
+ const classDecl = findClassByName(ctx.sourceFile, typeName);
2934
+ if (classDecl !== null) {
2935
+ return analyzeClassToIR(classDecl, ctx.checker, filePath, extensionRegistry);
2936
+ }
2937
+ const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
2938
+ if (interfaceDecl !== null) {
2939
+ return analyzeInterfaceToIR(interfaceDecl, ctx.checker, filePath, extensionRegistry);
2940
+ }
2941
+ const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
2942
+ if (typeAlias !== null) {
2943
+ const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, filePath, extensionRegistry);
2944
+ if (result.ok) {
2945
+ return result.analysis;
2946
+ }
2947
+ throw new Error(result.error);
2948
+ }
2949
+ throw new Error(
2950
+ `Type "${typeName}" not found as a class, interface, or type alias in ${filePath}`
2951
+ );
2952
+ }
2953
+
2954
+ // src/validate/constraint-validator.ts
2955
+ var import_core4 = require("@formspec/core");
2956
+ function addContradiction(ctx, message, primary, related) {
2957
+ ctx.diagnostics.push({
2958
+ code: "CONTRADICTING_CONSTRAINTS",
2959
+ message,
2960
+ severity: "error",
2961
+ primaryLocation: primary,
2962
+ relatedLocations: [related]
2963
+ });
2964
+ }
2965
+ function addTypeMismatch(ctx, message, primary) {
2966
+ ctx.diagnostics.push({
2967
+ code: "TYPE_MISMATCH",
2968
+ message,
2969
+ severity: "error",
2970
+ primaryLocation: primary,
2971
+ relatedLocations: []
2972
+ });
2973
+ }
2974
+ function addUnknownExtension(ctx, message, primary) {
2975
+ ctx.diagnostics.push({
2976
+ code: "UNKNOWN_EXTENSION",
2977
+ message,
2978
+ severity: "warning",
2979
+ primaryLocation: primary,
2980
+ relatedLocations: []
2981
+ });
2982
+ }
2983
+ function addUnknownPathTarget(ctx, message, primary) {
2984
+ ctx.diagnostics.push({
2985
+ code: "UNKNOWN_PATH_TARGET",
2986
+ message,
2987
+ severity: "error",
2988
+ primaryLocation: primary,
2989
+ relatedLocations: []
2990
+ });
2991
+ }
2992
+ function addConstraintBroadening(ctx, message, primary, related) {
2993
+ ctx.diagnostics.push({
2994
+ code: "CONSTRAINT_BROADENING",
2995
+ message,
2996
+ severity: "error",
2997
+ primaryLocation: primary,
2998
+ relatedLocations: [related]
2999
+ });
3000
+ }
3001
+ function getExtensionIdFromConstraintId(constraintId) {
3002
+ const separator = constraintId.lastIndexOf("/");
3003
+ if (separator <= 0) {
3004
+ return null;
3005
+ }
3006
+ return constraintId.slice(0, separator);
3007
+ }
3008
+ function findNumeric(constraints, constraintKind) {
3009
+ return constraints.find((c) => c.constraintKind === constraintKind);
3010
+ }
3011
+ function findLength(constraints, constraintKind) {
3012
+ return constraints.find((c) => c.constraintKind === constraintKind);
3013
+ }
3014
+ function findAllowedMembers(constraints) {
3015
+ return constraints.filter(
3016
+ (c) => c.constraintKind === "allowedMembers"
3017
+ );
3018
+ }
3019
+ function findConstConstraints(constraints) {
3020
+ return constraints.filter(
3021
+ (c) => c.constraintKind === "const"
3022
+ );
3023
+ }
3024
+ function jsonValueEquals(left, right) {
3025
+ if (left === right) {
3026
+ return true;
3027
+ }
3028
+ if (Array.isArray(left) || Array.isArray(right)) {
3029
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
3030
+ return false;
3031
+ }
3032
+ return left.every((item, index) => jsonValueEquals(item, right[index]));
3033
+ }
3034
+ if (isJsonObject(left) || isJsonObject(right)) {
3035
+ if (!isJsonObject(left) || !isJsonObject(right)) {
3036
+ return false;
3037
+ }
3038
+ const leftKeys = Object.keys(left).sort();
3039
+ const rightKeys = Object.keys(right).sort();
3040
+ if (leftKeys.length !== rightKeys.length) {
3041
+ return false;
3042
+ }
3043
+ return leftKeys.every((key, index) => {
3044
+ const rightKey = rightKeys[index];
3045
+ if (rightKey !== key) {
3046
+ return false;
3047
+ }
3048
+ const leftValue = left[key];
3049
+ const rightValue = right[rightKey];
3050
+ return leftValue !== void 0 && rightValue !== void 0 && jsonValueEquals(leftValue, rightValue);
3051
+ });
3052
+ }
3053
+ return false;
3054
+ }
3055
+ function isJsonObject(value) {
3056
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3057
+ }
3058
+ function isOrderedBoundConstraint(constraint) {
3059
+ return constraint.constraintKind === "minimum" || constraint.constraintKind === "exclusiveMinimum" || constraint.constraintKind === "minLength" || constraint.constraintKind === "minItems" || constraint.constraintKind === "maximum" || constraint.constraintKind === "exclusiveMaximum" || constraint.constraintKind === "maxLength" || constraint.constraintKind === "maxItems";
3060
+ }
3061
+ function pathKey(constraint) {
3062
+ return constraint.path?.segments.join(".") ?? "";
3063
+ }
3064
+ function orderedBoundFamily(kind) {
3065
+ switch (kind) {
3066
+ case "minimum":
3067
+ case "exclusiveMinimum":
3068
+ return "numeric-lower";
3069
+ case "maximum":
3070
+ case "exclusiveMaximum":
3071
+ return "numeric-upper";
3072
+ case "minLength":
3073
+ return "minLength";
3074
+ case "minItems":
3075
+ return "minItems";
3076
+ case "maxLength":
3077
+ return "maxLength";
3078
+ case "maxItems":
3079
+ return "maxItems";
3080
+ default: {
3081
+ const _exhaustive = kind;
3082
+ return _exhaustive;
3083
+ }
3084
+ }
3085
+ }
3086
+ function isNumericLowerKind(kind) {
3087
+ return kind === "minimum" || kind === "exclusiveMinimum";
3088
+ }
3089
+ function isNumericUpperKind(kind) {
3090
+ return kind === "maximum" || kind === "exclusiveMaximum";
3091
+ }
3092
+ function describeConstraintTag(constraint) {
3093
+ return `@${constraint.constraintKind}`;
3094
+ }
3095
+ function compareConstraintStrength(current, previous) {
3096
+ const family = orderedBoundFamily(current.constraintKind);
3097
+ if (family === "numeric-lower") {
3098
+ if (!isNumericLowerKind(current.constraintKind) || !isNumericLowerKind(previous.constraintKind)) {
3099
+ throw new Error("numeric-lower family received non-numeric lower-bound constraint");
3100
+ }
3101
+ if (current.value !== previous.value) {
3102
+ return current.value > previous.value ? 1 : -1;
3103
+ }
3104
+ if (current.constraintKind === "exclusiveMinimum" && previous.constraintKind === "minimum") {
3105
+ return 1;
3106
+ }
3107
+ if (current.constraintKind === "minimum" && previous.constraintKind === "exclusiveMinimum") {
3108
+ return -1;
3109
+ }
3110
+ return 0;
3111
+ }
3112
+ if (family === "numeric-upper") {
3113
+ if (!isNumericUpperKind(current.constraintKind) || !isNumericUpperKind(previous.constraintKind)) {
3114
+ throw new Error("numeric-upper family received non-numeric upper-bound constraint");
3115
+ }
3116
+ if (current.value !== previous.value) {
3117
+ return current.value < previous.value ? 1 : -1;
3118
+ }
3119
+ if (current.constraintKind === "exclusiveMaximum" && previous.constraintKind === "maximum") {
3120
+ return 1;
3121
+ }
3122
+ if (current.constraintKind === "maximum" && previous.constraintKind === "exclusiveMaximum") {
3123
+ return -1;
3124
+ }
3125
+ return 0;
3126
+ }
3127
+ switch (family) {
3128
+ case "minLength":
3129
+ case "minItems":
3130
+ if (current.value === previous.value) {
3131
+ return 0;
3132
+ }
3133
+ return current.value > previous.value ? 1 : -1;
3134
+ case "maxLength":
3135
+ case "maxItems":
3136
+ if (current.value === previous.value) {
3137
+ return 0;
3138
+ }
3139
+ return current.value < previous.value ? 1 : -1;
3140
+ default: {
3141
+ const _exhaustive = family;
3142
+ return _exhaustive;
3143
+ }
3144
+ }
3145
+ }
3146
+ function checkConstraintBroadening(ctx, fieldName, constraints) {
3147
+ const strongestByKey = /* @__PURE__ */ new Map();
3148
+ for (const constraint of constraints) {
3149
+ if (!isOrderedBoundConstraint(constraint)) {
3150
+ continue;
3151
+ }
3152
+ const key = `${orderedBoundFamily(constraint.constraintKind)}:${pathKey(constraint)}`;
3153
+ const previous = strongestByKey.get(key);
3154
+ if (previous === void 0) {
3155
+ strongestByKey.set(key, constraint);
3156
+ continue;
3157
+ }
3158
+ const strength = compareConstraintStrength(constraint, previous);
3159
+ if (strength < 0) {
3160
+ const displayFieldName = formatPathTargetFieldName(
3161
+ fieldName,
3162
+ constraint.path?.segments ?? []
3163
+ );
3164
+ addConstraintBroadening(
3165
+ ctx,
3166
+ `Field "${displayFieldName}": ${describeConstraintTag(constraint)} (${String(constraint.value)}) is broader than earlier ${describeConstraintTag(previous)} (${String(previous.value)}). Constraints can only narrow.`,
3167
+ constraint.provenance,
3168
+ previous.provenance
3169
+ );
3170
+ continue;
3171
+ }
3172
+ if (strength <= 0) {
3173
+ continue;
3174
+ }
3175
+ strongestByKey.set(key, constraint);
3176
+ }
3177
+ }
3178
+ function compareCustomConstraintStrength(current, previous) {
3179
+ const order = current.comparePayloads(current.constraint.payload, previous.constraint.payload);
3180
+ const equalPayloadTiebreaker = order === 0 ? compareSemanticInclusivity(current.role.inclusive, previous.role.inclusive) : order;
3181
+ switch (current.role.bound) {
3182
+ case "lower":
3183
+ return equalPayloadTiebreaker;
3184
+ case "upper":
3185
+ return equalPayloadTiebreaker === 0 ? 0 : -equalPayloadTiebreaker;
3186
+ case "exact":
3187
+ return order === 0 ? 0 : Number.NaN;
3188
+ default: {
3189
+ const _exhaustive = current.role.bound;
3190
+ return _exhaustive;
3191
+ }
3192
+ }
3193
+ }
3194
+ function compareSemanticInclusivity(currentInclusive, previousInclusive) {
3195
+ if (currentInclusive === previousInclusive) {
3196
+ return 0;
3197
+ }
3198
+ return currentInclusive ? -1 : 1;
3199
+ }
3200
+ function customConstraintsContradict(lower, upper) {
3201
+ const order = lower.comparePayloads(lower.constraint.payload, upper.constraint.payload);
3202
+ if (order > 0) {
3203
+ return true;
3204
+ }
3205
+ if (order < 0) {
3206
+ return false;
3207
+ }
3208
+ return !lower.role.inclusive || !upper.role.inclusive;
3209
+ }
3210
+ function describeCustomConstraintTag(constraint) {
3211
+ return constraint.provenance.tagName ?? constraint.constraintId;
3212
+ }
3213
+ function checkCustomConstraintSemantics(ctx, fieldName, constraints) {
3214
+ if (ctx.extensionRegistry === void 0) {
3215
+ return;
3216
+ }
3217
+ const strongestByKey = /* @__PURE__ */ new Map();
3218
+ const lowerByFamily = /* @__PURE__ */ new Map();
3219
+ const upperByFamily = /* @__PURE__ */ new Map();
3220
+ for (const constraint of constraints) {
3221
+ if (constraint.constraintKind !== "custom") {
3222
+ continue;
3223
+ }
3224
+ const registration = ctx.extensionRegistry.findConstraint(constraint.constraintId);
3225
+ if (registration?.comparePayloads === void 0 || registration.semanticRole === void 0) {
3226
+ continue;
3227
+ }
3228
+ const entry = {
3229
+ constraint,
3230
+ comparePayloads: registration.comparePayloads,
3231
+ role: registration.semanticRole
3232
+ };
3233
+ const familyKey = `${registration.semanticRole.family}:${pathKey(constraint)}`;
3234
+ const boundKey = `${familyKey}:${registration.semanticRole.bound}`;
3235
+ const previous = strongestByKey.get(boundKey);
3236
+ if (previous !== void 0) {
3237
+ const strength = compareCustomConstraintStrength(entry, previous);
3238
+ if (Number.isNaN(strength)) {
3239
+ addContradiction(
3240
+ ctx,
3241
+ `Field "${formatPathTargetFieldName(fieldName, constraint.path?.segments ?? [])}": ${describeCustomConstraintTag(constraint)} conflicts with ${describeCustomConstraintTag(previous.constraint)}`,
3242
+ constraint.provenance,
3243
+ previous.constraint.provenance
3244
+ );
3245
+ continue;
3246
+ }
3247
+ if (strength < 0) {
3248
+ addConstraintBroadening(
3249
+ ctx,
3250
+ `Field "${formatPathTargetFieldName(fieldName, constraint.path?.segments ?? [])}": ${describeCustomConstraintTag(constraint)} is broader than earlier ${describeCustomConstraintTag(previous.constraint)}. Constraints can only narrow.`,
3251
+ constraint.provenance,
3252
+ previous.constraint.provenance
3253
+ );
3254
+ continue;
3255
+ }
3256
+ if (strength > 0) {
3257
+ strongestByKey.set(boundKey, entry);
3258
+ }
3259
+ } else {
3260
+ strongestByKey.set(boundKey, entry);
3261
+ }
3262
+ if (registration.semanticRole.bound === "lower") {
3263
+ lowerByFamily.set(familyKey, strongestByKey.get(boundKey) ?? entry);
3264
+ } else if (registration.semanticRole.bound === "upper") {
3265
+ upperByFamily.set(familyKey, strongestByKey.get(boundKey) ?? entry);
3266
+ }
3267
+ }
3268
+ for (const [familyKey, lower] of lowerByFamily) {
3269
+ const upper = upperByFamily.get(familyKey);
3270
+ if (upper === void 0) {
3271
+ continue;
3272
+ }
3273
+ if (!customConstraintsContradict(lower, upper)) {
3274
+ continue;
3275
+ }
3276
+ addContradiction(
3277
+ ctx,
3278
+ `Field "${formatPathTargetFieldName(fieldName, lower.constraint.path?.segments ?? [])}": ${describeCustomConstraintTag(lower.constraint)} contradicts ${describeCustomConstraintTag(upper.constraint)}`,
3279
+ lower.constraint.provenance,
3280
+ upper.constraint.provenance
3281
+ );
3282
+ }
3283
+ }
3284
+ function checkNumericContradictions(ctx, fieldName, constraints) {
3285
+ const min = findNumeric(constraints, "minimum");
3286
+ const max = findNumeric(constraints, "maximum");
3287
+ const exMin = findNumeric(constraints, "exclusiveMinimum");
3288
+ const exMax = findNumeric(constraints, "exclusiveMaximum");
3289
+ if (min !== void 0 && max !== void 0 && min.value > max.value) {
3290
+ addContradiction(
3291
+ ctx,
3292
+ `Field "${fieldName}": minimum (${String(min.value)}) is greater than maximum (${String(max.value)})`,
3293
+ min.provenance,
3294
+ max.provenance
3295
+ );
3296
+ }
3297
+ if (exMin !== void 0 && max !== void 0 && exMin.value >= max.value) {
3298
+ addContradiction(
3299
+ ctx,
3300
+ `Field "${fieldName}": exclusiveMinimum (${String(exMin.value)}) is greater than or equal to maximum (${String(max.value)})`,
3301
+ exMin.provenance,
3302
+ max.provenance
3303
+ );
3304
+ }
3305
+ if (min !== void 0 && exMax !== void 0 && min.value >= exMax.value) {
3306
+ addContradiction(
3307
+ ctx,
3308
+ `Field "${fieldName}": minimum (${String(min.value)}) is greater than or equal to exclusiveMaximum (${String(exMax.value)})`,
3309
+ min.provenance,
3310
+ exMax.provenance
3311
+ );
3312
+ }
3313
+ if (exMin !== void 0 && exMax !== void 0 && exMin.value >= exMax.value) {
3314
+ addContradiction(
3315
+ ctx,
3316
+ `Field "${fieldName}": exclusiveMinimum (${String(exMin.value)}) is greater than or equal to exclusiveMaximum (${String(exMax.value)})`,
3317
+ exMin.provenance,
3318
+ exMax.provenance
3319
+ );
3320
+ }
3321
+ }
3322
+ function checkLengthContradictions(ctx, fieldName, constraints) {
3323
+ const minLen = findLength(constraints, "minLength");
3324
+ const maxLen = findLength(constraints, "maxLength");
3325
+ if (minLen !== void 0 && maxLen !== void 0 && minLen.value > maxLen.value) {
3326
+ addContradiction(
3327
+ ctx,
3328
+ `Field "${fieldName}": minLength (${String(minLen.value)}) is greater than maxLength (${String(maxLen.value)})`,
3329
+ minLen.provenance,
3330
+ maxLen.provenance
3331
+ );
3332
+ }
3333
+ const minItems = findLength(constraints, "minItems");
3334
+ const maxItems = findLength(constraints, "maxItems");
3335
+ if (minItems !== void 0 && maxItems !== void 0 && minItems.value > maxItems.value) {
3336
+ addContradiction(
3337
+ ctx,
3338
+ `Field "${fieldName}": minItems (${String(minItems.value)}) is greater than maxItems (${String(maxItems.value)})`,
3339
+ minItems.provenance,
3340
+ maxItems.provenance
3341
+ );
3342
+ }
3343
+ }
3344
+ function checkAllowedMembersContradiction(ctx, fieldName, constraints) {
3345
+ const members = findAllowedMembers(constraints);
3346
+ if (members.length < 2) return;
3347
+ const firstSet = new Set(members[0]?.members ?? []);
3348
+ for (let i = 1; i < members.length; i++) {
3349
+ const current = members[i];
3350
+ if (current === void 0) continue;
3351
+ for (const m of firstSet) {
3352
+ if (!current.members.includes(m)) {
3353
+ firstSet.delete(m);
3354
+ }
3355
+ }
3356
+ }
3357
+ if (firstSet.size === 0) {
3358
+ const first = members[0];
3359
+ const second = members[1];
3360
+ if (first !== void 0 && second !== void 0) {
3361
+ addContradiction(
3362
+ ctx,
3363
+ `Field "${fieldName}": allowedMembers constraints have an empty intersection (no valid values remain)`,
3364
+ first.provenance,
3365
+ second.provenance
3366
+ );
3367
+ }
3368
+ }
3369
+ }
3370
+ function checkConstContradictions(ctx, fieldName, constraints) {
3371
+ const constConstraints = findConstConstraints(constraints);
3372
+ if (constConstraints.length < 2) return;
3373
+ const first = constConstraints[0];
3374
+ if (first === void 0) return;
3375
+ for (let i = 1; i < constConstraints.length; i++) {
3376
+ const current = constConstraints[i];
3377
+ if (current === void 0) continue;
3378
+ if (jsonValueEquals(first.value, current.value)) {
3379
+ continue;
3380
+ }
3381
+ addContradiction(
3382
+ ctx,
3383
+ `Field "${fieldName}": conflicting @const constraints require both ${JSON.stringify(first.value)} and ${JSON.stringify(current.value)}`,
3384
+ first.provenance,
3385
+ current.provenance
3386
+ );
3387
+ }
3388
+ }
3389
+ function typeLabel(type) {
3390
+ switch (type.kind) {
3391
+ case "primitive":
3392
+ return type.primitiveKind;
3393
+ case "enum":
3394
+ return "enum";
3395
+ case "array":
3396
+ return "array";
3397
+ case "object":
3398
+ return "object";
3399
+ case "record":
3400
+ return "record";
3401
+ case "union":
3402
+ return "union";
3403
+ case "reference":
3404
+ return `reference(${type.name})`;
3405
+ case "dynamic":
3406
+ return `dynamic(${type.dynamicKind})`;
3407
+ case "custom":
3408
+ return `custom(${type.typeId})`;
3409
+ default: {
3410
+ const _exhaustive = type;
3411
+ return String(_exhaustive);
3412
+ }
3413
+ }
3414
+ }
3415
+ function dereferenceType(ctx, type) {
3416
+ let current = type;
3417
+ const seen = /* @__PURE__ */ new Set();
3418
+ while (current.kind === "reference") {
3419
+ if (seen.has(current.name)) {
3420
+ return current;
3421
+ }
3422
+ seen.add(current.name);
3423
+ const definition = ctx.typeRegistry[current.name];
3424
+ if (definition === void 0) {
3425
+ return current;
3426
+ }
3427
+ current = definition.type;
3428
+ }
3429
+ return current;
3430
+ }
3431
+ function collectReferencedTypeConstraints(ctx, type) {
3432
+ const collected = [];
3433
+ let current = type;
3434
+ const seen = /* @__PURE__ */ new Set();
3435
+ while (current.kind === "reference") {
3436
+ if (seen.has(current.name)) {
3437
+ break;
3438
+ }
3439
+ seen.add(current.name);
3440
+ const definition = ctx.typeRegistry[current.name];
3441
+ if (definition === void 0) {
3442
+ break;
3443
+ }
3444
+ if (definition.constraints !== void 0) {
3445
+ collected.push(...definition.constraints);
3446
+ }
3447
+ current = definition.type;
3448
+ }
3449
+ return collected;
3450
+ }
3451
+ function resolvePathTargetType(ctx, type, segments) {
3452
+ const effectiveType = dereferenceType(ctx, type);
3453
+ if (segments.length === 0) {
3454
+ return { kind: "resolved", type: effectiveType };
3455
+ }
3456
+ if (effectiveType.kind === "array") {
3457
+ return resolvePathTargetType(ctx, effectiveType.items, segments);
3458
+ }
3459
+ if (effectiveType.kind === "object") {
3460
+ const [segment, ...rest] = segments;
3461
+ if (segment === void 0) {
3462
+ throw new Error("Invariant violation: object path traversal requires a segment");
3463
+ }
3464
+ const property = effectiveType.properties.find((prop) => prop.name === segment);
3465
+ if (property === void 0) {
3466
+ return { kind: "missing-property", segment };
3467
+ }
3468
+ return resolvePathTargetType(ctx, property.type, rest);
3469
+ }
3470
+ return { kind: "unresolvable", type: effectiveType };
3471
+ }
3472
+ function isNullType(type) {
3473
+ return type.kind === "primitive" && type.primitiveKind === "null";
3474
+ }
3475
+ function collectCustomConstraintCandidateTypes(ctx, type) {
3476
+ const effectiveType = dereferenceType(ctx, type);
3477
+ const candidates = [effectiveType];
3478
+ if (effectiveType.kind === "array") {
3479
+ candidates.push(...collectCustomConstraintCandidateTypes(ctx, effectiveType.items));
3480
+ }
3481
+ if (effectiveType.kind === "union") {
3482
+ const memberTypes = effectiveType.members.map((member) => dereferenceType(ctx, member));
3483
+ const nonNullMembers = memberTypes.filter((member) => !isNullType(member));
3484
+ if (nonNullMembers.length === 1 && nonNullMembers.length < memberTypes.length) {
3485
+ const [nullableMember] = nonNullMembers;
3486
+ if (nullableMember !== void 0) {
3487
+ candidates.push(...collectCustomConstraintCandidateTypes(ctx, nullableMember));
3488
+ }
3489
+ }
3490
+ }
3491
+ return candidates;
3492
+ }
3493
+ function formatPathTargetFieldName(fieldName, path3) {
3494
+ return path3.length === 0 ? fieldName : `${fieldName}.${path3.join(".")}`;
3495
+ }
3496
+ function checkConstraintOnType(ctx, fieldName, type, constraint) {
3497
+ const effectiveType = dereferenceType(ctx, type);
3498
+ const isNumber = effectiveType.kind === "primitive" && ["number", "integer", "bigint"].includes(effectiveType.primitiveKind);
3499
+ const isString = effectiveType.kind === "primitive" && effectiveType.primitiveKind === "string";
3500
+ const isArray = effectiveType.kind === "array";
3501
+ const isEnum = effectiveType.kind === "enum";
3502
+ const arrayItemType = effectiveType.kind === "array" ? dereferenceType(ctx, effectiveType.items) : void 0;
3503
+ const isStringArray = arrayItemType?.kind === "primitive" && arrayItemType.primitiveKind === "string";
3504
+ const label = typeLabel(effectiveType);
3505
+ const ck = constraint.constraintKind;
3506
+ switch (ck) {
3507
+ case "minimum":
3508
+ case "maximum":
3509
+ case "exclusiveMinimum":
3510
+ case "exclusiveMaximum":
3511
+ case "multipleOf": {
3512
+ if (!isNumber) {
3513
+ addTypeMismatch(
3514
+ ctx,
3515
+ `Field "${fieldName}": constraint "${ck}" is only valid on number fields, but field type is "${label}"`,
3516
+ constraint.provenance
3517
+ );
3518
+ }
3519
+ break;
3520
+ }
3521
+ case "minLength":
3522
+ case "maxLength":
3523
+ case "pattern": {
3524
+ if (!isString && !isStringArray) {
3525
+ addTypeMismatch(
3526
+ ctx,
3527
+ `Field "${fieldName}": constraint "${ck}" is only valid on string fields or string array items, but field type is "${label}"`,
3528
+ constraint.provenance
3529
+ );
3530
+ }
3531
+ break;
3532
+ }
3533
+ case "minItems":
3534
+ case "maxItems":
3535
+ case "uniqueItems": {
3536
+ if (!isArray) {
3537
+ addTypeMismatch(
3538
+ ctx,
3539
+ `Field "${fieldName}": constraint "${ck}" is only valid on array fields, but field type is "${label}"`,
3540
+ constraint.provenance
3541
+ );
3542
+ }
3543
+ break;
3544
+ }
3545
+ case "allowedMembers": {
3546
+ if (!isEnum) {
3547
+ addTypeMismatch(
3548
+ ctx,
3549
+ `Field "${fieldName}": constraint "allowedMembers" is only valid on enum fields, but field type is "${label}"`,
3550
+ constraint.provenance
3551
+ );
3552
+ }
3553
+ break;
3554
+ }
3555
+ case "const": {
3556
+ const isPrimitiveConstType = effectiveType.kind === "primitive" && ["string", "number", "integer", "bigint", "boolean", "null"].includes(
3557
+ effectiveType.primitiveKind
3558
+ ) || effectiveType.kind === "enum";
3559
+ if (!isPrimitiveConstType) {
3560
+ addTypeMismatch(
3561
+ ctx,
3562
+ `Field "${fieldName}": constraint "const" is only valid on primitive or enum fields, but field type is "${label}"`,
3563
+ constraint.provenance
3564
+ );
3565
+ break;
3566
+ }
3567
+ if (effectiveType.kind === "primitive") {
3568
+ const valueType = constraint.value === null ? "null" : Array.isArray(constraint.value) ? "array" : typeof constraint.value;
3569
+ const expectedValueType = effectiveType.primitiveKind === "integer" || effectiveType.primitiveKind === "bigint" ? "number" : effectiveType.primitiveKind;
3570
+ if (valueType !== expectedValueType) {
3571
+ addTypeMismatch(
3572
+ ctx,
3573
+ `Field "${fieldName}": @const value type "${valueType}" is incompatible with field type "${effectiveType.primitiveKind}"`,
3574
+ constraint.provenance
3575
+ );
3576
+ }
3577
+ break;
3578
+ }
3579
+ const memberValues = effectiveType.members.map((member) => member.value);
3580
+ if (!memberValues.some((member) => jsonValueEquals(member, constraint.value))) {
3581
+ addTypeMismatch(
3582
+ ctx,
3583
+ `Field "${fieldName}": @const value ${JSON.stringify(constraint.value)} is not one of the enum members`,
3584
+ constraint.provenance
3585
+ );
3586
+ }
3587
+ break;
3588
+ }
3589
+ case "custom": {
3590
+ checkCustomConstraint(ctx, fieldName, effectiveType, constraint);
3591
+ break;
3592
+ }
3593
+ default: {
3594
+ const _exhaustive = constraint;
3595
+ throw new Error(
3596
+ `Unhandled constraint kind: ${_exhaustive.constraintKind}`
3597
+ );
3598
+ }
3599
+ }
3600
+ }
3601
+ function checkTypeApplicability(ctx, fieldName, type, constraints) {
3602
+ for (const constraint of constraints) {
3603
+ if (constraint.path) {
3604
+ const resolution = resolvePathTargetType(ctx, type, constraint.path.segments);
3605
+ const targetFieldName = formatPathTargetFieldName(fieldName, constraint.path.segments);
3606
+ if (resolution.kind === "missing-property") {
3607
+ addUnknownPathTarget(
3608
+ ctx,
3609
+ `Field "${targetFieldName}": path-targeted constraint "${constraint.constraintKind}" references unknown path segment "${resolution.segment}"`,
3610
+ constraint.provenance
3611
+ );
3612
+ continue;
3613
+ }
3614
+ if (resolution.kind === "unresolvable") {
3615
+ addTypeMismatch(
3616
+ ctx,
3617
+ `Field "${targetFieldName}": path-targeted constraint "${constraint.constraintKind}" is invalid because type "${typeLabel(resolution.type)}" cannot be traversed`,
3618
+ constraint.provenance
3619
+ );
3620
+ continue;
3621
+ }
3622
+ checkConstraintOnType(ctx, targetFieldName, resolution.type, constraint);
3623
+ continue;
3624
+ }
3625
+ checkConstraintOnType(ctx, fieldName, type, constraint);
3626
+ }
3627
+ }
3628
+ function checkCustomConstraint(ctx, fieldName, type, constraint) {
3629
+ if (ctx.extensionRegistry === void 0) return;
3630
+ const registration = ctx.extensionRegistry.findConstraint(constraint.constraintId);
3631
+ if (registration === void 0) {
3632
+ addUnknownExtension(
3633
+ ctx,
3634
+ `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not registered in the extension registry`,
3635
+ constraint.provenance
3636
+ );
3637
+ return;
3638
+ }
3639
+ const candidateTypes = collectCustomConstraintCandidateTypes(ctx, type);
3640
+ const normalizedTagName = constraint.provenance.tagName === void 0 ? void 0 : (0, import_core4.normalizeConstraintTagName)(constraint.provenance.tagName.replace(/^@/, ""));
3641
+ if (normalizedTagName !== void 0) {
3642
+ const tagRegistration = ctx.extensionRegistry.findConstraintTag(normalizedTagName);
3643
+ const extensionId = getExtensionIdFromConstraintId(constraint.constraintId);
3644
+ if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName && !candidateTypes.some(
3645
+ (candidateType) => tagRegistration.registration.isApplicableToType?.(candidateType) !== false
3646
+ )) {
3647
+ addTypeMismatch(
3648
+ ctx,
3649
+ `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
3650
+ constraint.provenance
3651
+ );
3652
+ return;
3653
+ }
3654
+ }
3655
+ if (registration.applicableTypes === null) {
3656
+ if (!candidateTypes.some((candidateType) => registration.isApplicableToType?.(candidateType) !== false)) {
3657
+ addTypeMismatch(
3658
+ ctx,
3659
+ `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
3660
+ constraint.provenance
3661
+ );
3662
+ }
3663
+ return;
3664
+ }
3665
+ const applicableTypes = registration.applicableTypes;
3666
+ const matchesApplicableType = candidateTypes.some(
3667
+ (candidateType) => applicableTypes.includes(candidateType.kind) && registration.isApplicableToType?.(candidateType) !== false
3668
+ );
3669
+ if (!matchesApplicableType) {
3670
+ addTypeMismatch(
3671
+ ctx,
3672
+ `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
3673
+ constraint.provenance
3674
+ );
3675
+ }
3676
+ }
3677
+ function validateFieldNode(ctx, field) {
3678
+ validateConstraints(ctx, field.name, field.type, [
3679
+ ...collectReferencedTypeConstraints(ctx, field.type),
3680
+ ...field.constraints
3681
+ ]);
3682
+ if (field.type.kind === "object") {
3683
+ for (const prop of field.type.properties) {
3684
+ validateObjectProperty(ctx, field.name, prop);
3685
+ }
3686
+ }
3687
+ }
3688
+ function validateObjectProperty(ctx, parentName, prop) {
3689
+ const qualifiedName = `${parentName}.${prop.name}`;
3690
+ validateConstraints(ctx, qualifiedName, prop.type, [
3691
+ ...collectReferencedTypeConstraints(ctx, prop.type),
3692
+ ...prop.constraints
3693
+ ]);
3694
+ if (prop.type.kind === "object") {
3695
+ for (const nestedProp of prop.type.properties) {
3696
+ validateObjectProperty(ctx, qualifiedName, nestedProp);
3697
+ }
3698
+ }
3699
+ }
3700
+ function validateConstraints(ctx, name, type, constraints) {
3701
+ checkNumericContradictions(ctx, name, constraints);
3702
+ checkLengthContradictions(ctx, name, constraints);
3703
+ checkAllowedMembersContradiction(ctx, name, constraints);
3704
+ checkConstContradictions(ctx, name, constraints);
3705
+ checkConstraintBroadening(ctx, name, constraints);
3706
+ checkCustomConstraintSemantics(ctx, name, constraints);
3707
+ checkTypeApplicability(ctx, name, type, constraints);
3708
+ }
3709
+ function validateElement(ctx, element) {
3710
+ switch (element.kind) {
3711
+ case "field":
3712
+ validateFieldNode(ctx, element);
3713
+ break;
3714
+ case "group":
3715
+ for (const child of element.elements) {
3716
+ validateElement(ctx, child);
3717
+ }
3718
+ break;
3719
+ case "conditional":
3720
+ for (const child of element.elements) {
3721
+ validateElement(ctx, child);
3722
+ }
3723
+ break;
3724
+ default: {
3725
+ const _exhaustive = element;
3726
+ throw new Error(`Unhandled element kind: ${_exhaustive.kind}`);
3727
+ }
3728
+ }
3729
+ }
3730
+ function validateIR(ir, options) {
3731
+ const ctx = {
3732
+ diagnostics: [],
3733
+ extensionRegistry: options?.extensionRegistry,
3734
+ typeRegistry: ir.typeRegistry
3735
+ };
3736
+ for (const element of ir.elements) {
3737
+ validateElement(ctx, element);
3738
+ }
3739
+ return {
3740
+ diagnostics: ctx.diagnostics,
3741
+ valid: ctx.diagnostics.every((d) => d.severity !== "error")
3742
+ };
3743
+ }
3744
+
2829
3745
  // src/generators/class-schema.ts
2830
3746
  function generateClassSchemas(analysis, source, options) {
2831
3747
  const ir = canonicalizeTSDoc(analysis, source);
3748
+ const validationResult = validateIR(ir, {
3749
+ ...options?.extensionRegistry !== void 0 && {
3750
+ extensionRegistry: options.extensionRegistry
3751
+ },
3752
+ ...options?.vendorPrefix !== void 0 && { vendorPrefix: options.vendorPrefix }
3753
+ });
3754
+ if (!validationResult.valid) {
3755
+ throw new Error(formatValidationError(validationResult.diagnostics));
3756
+ }
2832
3757
  return {
2833
3758
  jsonSchema: generateJsonSchemaFromIR(ir, options),
2834
3759
  uiSchema: generateUiSchemaFromIR(ir)
2835
3760
  };
2836
3761
  }
3762
+ function formatValidationError(diagnostics) {
3763
+ const lines = diagnostics.map((diagnostic) => {
3764
+ const primary = formatLocation(diagnostic.primaryLocation);
3765
+ const related = diagnostic.relatedLocations.length > 0 ? ` [related: ${diagnostic.relatedLocations.map(formatLocation).join(", ")}]` : "";
3766
+ return `${diagnostic.code}: ${diagnostic.message} (${primary})${related}`;
3767
+ });
3768
+ return `FormSpec validation failed:
3769
+ ${lines.map((line) => `- ${line}`).join("\n")}`;
3770
+ }
3771
+ function formatLocation(location) {
3772
+ return `${location.file}:${String(location.line)}:${String(location.column)}`;
3773
+ }
2837
3774
  function generateSchemasFromClass(options) {
2838
3775
  const ctx = createProgramContext(options.filePath);
2839
3776
  const classDecl = findClassByName(ctx.sourceFile, options.className);
@@ -2856,50 +3793,18 @@ function generateSchemasFromClass(options) {
2856
3793
  );
2857
3794
  }
2858
3795
  function generateSchemas(options) {
2859
- const ctx = createProgramContext(options.filePath);
2860
- const source = { file: options.filePath };
2861
- const classDecl = findClassByName(ctx.sourceFile, options.typeName);
2862
- if (classDecl) {
2863
- const analysis = analyzeClassToIR(
2864
- classDecl,
2865
- ctx.checker,
2866
- options.filePath,
2867
- options.extensionRegistry
2868
- );
2869
- return generateClassSchemas(analysis, source, options);
2870
- }
2871
- const interfaceDecl = findInterfaceByName(ctx.sourceFile, options.typeName);
2872
- if (interfaceDecl) {
2873
- const analysis = analyzeInterfaceToIR(
2874
- interfaceDecl,
2875
- ctx.checker,
2876
- options.filePath,
2877
- options.extensionRegistry
2878
- );
2879
- return generateClassSchemas(analysis, source, options);
2880
- }
2881
- const typeAlias = findTypeAliasByName(ctx.sourceFile, options.typeName);
2882
- if (typeAlias) {
2883
- const result = analyzeTypeAliasToIR(
2884
- typeAlias,
2885
- ctx.checker,
2886
- options.filePath,
2887
- options.extensionRegistry
2888
- );
2889
- if (result.ok) {
2890
- return generateClassSchemas(result.analysis, source, options);
2891
- }
2892
- throw new Error(result.error);
2893
- }
2894
- throw new Error(
2895
- `Type "${options.typeName}" not found as a class, interface, or type alias in ${options.filePath}`
3796
+ const analysis = analyzeNamedTypeToIR(
3797
+ options.filePath,
3798
+ options.typeName,
3799
+ options.extensionRegistry
2896
3800
  );
3801
+ return generateClassSchemas(analysis, { file: options.filePath }, options);
2897
3802
  }
2898
3803
 
2899
3804
  // src/generators/mixed-authoring.ts
2900
3805
  function buildMixedAuthoringSchemas(options) {
2901
3806
  const { filePath, typeName, overlays, ...schemaOptions } = options;
2902
- const analysis = analyzeNamedType(filePath, typeName, schemaOptions.extensionRegistry);
3807
+ const analysis = analyzeNamedTypeToIR(filePath, typeName, schemaOptions.extensionRegistry);
2903
3808
  const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays);
2904
3809
  const ir = canonicalizeTSDoc(composedAnalysis, { file: filePath });
2905
3810
  return {
@@ -2907,29 +3812,6 @@ function buildMixedAuthoringSchemas(options) {
2907
3812
  uiSchema: generateUiSchemaFromIR(ir)
2908
3813
  };
2909
3814
  }
2910
- function analyzeNamedType(filePath, typeName, extensionRegistry) {
2911
- const ctx = createProgramContext(filePath);
2912
- const source = { file: filePath };
2913
- const classDecl = findClassByName(ctx.sourceFile, typeName);
2914
- if (classDecl !== null) {
2915
- return analyzeClassToIR(classDecl, ctx.checker, source.file, extensionRegistry);
2916
- }
2917
- const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
2918
- if (interfaceDecl !== null) {
2919
- return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file, extensionRegistry);
2920
- }
2921
- const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
2922
- if (typeAlias !== null) {
2923
- const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file, extensionRegistry);
2924
- if (result.ok) {
2925
- return result.analysis;
2926
- }
2927
- throw new Error(result.error);
2928
- }
2929
- throw new Error(
2930
- `Type "${typeName}" not found as a class, interface, or type alias in ${filePath}`
2931
- );
2932
- }
2933
3815
  function composeAnalysisWithOverlays(analysis, overlays) {
2934
3816
  const overlayIR = canonicalizeChainDSL(overlays);
2935
3817
  const overlayFields = collectOverlayFields(overlayIR.elements);