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

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 (35) hide show
  1. package/dist/__tests__/fixtures/example-numeric-extension.d.ts +20 -0
  2. package/dist/__tests__/fixtures/example-numeric-extension.d.ts.map +1 -0
  3. package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts +1 -0
  4. package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts.map +1 -1
  5. package/dist/__tests__/numeric-extension.integration.test.d.ts +2 -0
  6. package/dist/__tests__/numeric-extension.integration.test.d.ts.map +1 -0
  7. package/dist/analyzer/class-analyzer.d.ts +5 -4
  8. package/dist/analyzer/class-analyzer.d.ts.map +1 -1
  9. package/dist/analyzer/jsdoc-constraints.d.ts +3 -2
  10. package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
  11. package/dist/analyzer/tsdoc-parser.d.ts +18 -2
  12. package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
  13. package/dist/browser.cjs +199 -4
  14. package/dist/browser.cjs.map +1 -1
  15. package/dist/browser.js +199 -4
  16. package/dist/browser.js.map +1 -1
  17. package/dist/build.d.ts +28 -2
  18. package/dist/cli.cjs +547 -84
  19. package/dist/cli.cjs.map +1 -1
  20. package/dist/cli.js +547 -84
  21. package/dist/cli.js.map +1 -1
  22. package/dist/extensions/registry.d.ts +25 -1
  23. package/dist/extensions/registry.d.ts.map +1 -1
  24. package/dist/generators/class-schema.d.ts +4 -4
  25. package/dist/generators/class-schema.d.ts.map +1 -1
  26. package/dist/index.cjs +546 -84
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.js +546 -84
  29. package/dist/index.js.map +1 -1
  30. package/dist/internals.cjs +645 -73
  31. package/dist/internals.cjs.map +1 -1
  32. package/dist/internals.js +643 -71
  33. package/dist/internals.js.map +1 -1
  34. package/dist/validate/constraint-validator.d.ts.map +1 -1
  35. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -776,7 +776,12 @@ function applyCustomConstraint(schema, constraint, ctx) {
776
776
  `Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
777
777
  );
778
778
  }
779
- Object.assign(schema, registration.toJsonSchema(constraint.payload, ctx.vendorPrefix));
779
+ assignVendorPrefixedExtensionKeywords(
780
+ schema,
781
+ registration.toJsonSchema(constraint.payload, ctx.vendorPrefix),
782
+ ctx.vendorPrefix,
783
+ `custom constraint "${constraint.constraintId}"`
784
+ );
780
785
  }
781
786
  function applyCustomAnnotation(schema, annotation, ctx) {
782
787
  const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
@@ -788,7 +793,22 @@ function applyCustomAnnotation(schema, annotation, ctx) {
788
793
  if (registration.toJsonSchema === void 0) {
789
794
  return;
790
795
  }
791
- Object.assign(schema, registration.toJsonSchema(annotation.value, ctx.vendorPrefix));
796
+ assignVendorPrefixedExtensionKeywords(
797
+ schema,
798
+ registration.toJsonSchema(annotation.value, ctx.vendorPrefix),
799
+ ctx.vendorPrefix,
800
+ `custom annotation "${annotation.annotationId}"`
801
+ );
802
+ }
803
+ function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPrefix, source) {
804
+ for (const [key, value] of Object.entries(extensionSchema)) {
805
+ if (!key.startsWith(`${vendorPrefix}-`)) {
806
+ throw new Error(
807
+ `Cannot apply ${source}: extension hooks may only emit "${vendorPrefix}-*" JSON Schema keywords`
808
+ );
809
+ }
810
+ schema[key] = value;
811
+ }
792
812
  }
793
813
 
794
814
  // src/json-schema/generator.ts
@@ -1041,7 +1061,10 @@ function getSchemaExtension(schema, key) {
1041
1061
  // src/extensions/registry.ts
1042
1062
  function createExtensionRegistry(extensions) {
1043
1063
  const typeMap = /* @__PURE__ */ new Map();
1064
+ const typeNameMap = /* @__PURE__ */ new Map();
1044
1065
  const constraintMap = /* @__PURE__ */ new Map();
1066
+ const constraintTagMap = /* @__PURE__ */ new Map();
1067
+ const builtinBroadeningMap = /* @__PURE__ */ new Map();
1045
1068
  const annotationMap = /* @__PURE__ */ new Map();
1046
1069
  for (const ext of extensions) {
1047
1070
  if (ext.types !== void 0) {
@@ -1051,6 +1074,27 @@ function createExtensionRegistry(extensions) {
1051
1074
  throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
1052
1075
  }
1053
1076
  typeMap.set(qualifiedId, type);
1077
+ for (const sourceTypeName of type.tsTypeNames ?? [type.typeName]) {
1078
+ if (typeNameMap.has(sourceTypeName)) {
1079
+ throw new Error(`Duplicate custom type source name: "${sourceTypeName}"`);
1080
+ }
1081
+ typeNameMap.set(sourceTypeName, {
1082
+ extensionId: ext.extensionId,
1083
+ registration: type
1084
+ });
1085
+ }
1086
+ if (type.builtinConstraintBroadenings !== void 0) {
1087
+ for (const broadening of type.builtinConstraintBroadenings) {
1088
+ const key = `${qualifiedId}:${broadening.tagName}`;
1089
+ if (builtinBroadeningMap.has(key)) {
1090
+ throw new Error(`Duplicate built-in constraint broadening: "${key}"`);
1091
+ }
1092
+ builtinBroadeningMap.set(key, {
1093
+ extensionId: ext.extensionId,
1094
+ registration: broadening
1095
+ });
1096
+ }
1097
+ }
1054
1098
  }
1055
1099
  }
1056
1100
  if (ext.constraints !== void 0) {
@@ -1062,6 +1106,17 @@ function createExtensionRegistry(extensions) {
1062
1106
  constraintMap.set(qualifiedId, constraint);
1063
1107
  }
1064
1108
  }
1109
+ if (ext.constraintTags !== void 0) {
1110
+ for (const tag of ext.constraintTags) {
1111
+ if (constraintTagMap.has(tag.tagName)) {
1112
+ throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
1113
+ }
1114
+ constraintTagMap.set(tag.tagName, {
1115
+ extensionId: ext.extensionId,
1116
+ registration: tag
1117
+ });
1118
+ }
1119
+ }
1065
1120
  if (ext.annotations !== void 0) {
1066
1121
  for (const annotation of ext.annotations) {
1067
1122
  const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
@@ -1075,7 +1130,10 @@ function createExtensionRegistry(extensions) {
1075
1130
  return {
1076
1131
  extensions,
1077
1132
  findType: (typeId) => typeMap.get(typeId),
1133
+ findTypeByName: (typeName) => typeNameMap.get(typeName),
1078
1134
  findConstraint: (constraintId) => constraintMap.get(constraintId),
1135
+ findConstraintTag: (tagName) => constraintTagMap.get(tagName),
1136
+ findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
1079
1137
  findAnnotation: (annotationId) => annotationMap.get(annotationId)
1080
1138
  };
1081
1139
  }
@@ -1261,7 +1319,7 @@ var LENGTH_CONSTRAINT_MAP = {
1261
1319
  maxItems: "maxItems"
1262
1320
  };
1263
1321
  var TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions", "defaultValue"]);
1264
- function createFormSpecTSDocConfig() {
1322
+ function createFormSpecTSDocConfig(extensionTagNames = []) {
1265
1323
  const config = new TSDocConfiguration();
1266
1324
  for (const tagName of Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS)) {
1267
1325
  config.addTagDefinition(
@@ -1281,14 +1339,34 @@ function createFormSpecTSDocConfig() {
1281
1339
  })
1282
1340
  );
1283
1341
  }
1342
+ for (const tagName of extensionTagNames) {
1343
+ config.addTagDefinition(
1344
+ new TSDocTagDefinition({
1345
+ tagName: "@" + tagName,
1346
+ syntaxKind: TSDocTagSyntaxKind.BlockTag,
1347
+ allowMultiple: true
1348
+ })
1349
+ );
1350
+ }
1284
1351
  return config;
1285
1352
  }
1286
- var sharedParser;
1287
- function getParser() {
1288
- sharedParser ??= new TSDocParser(createFormSpecTSDocConfig());
1289
- return sharedParser;
1290
- }
1291
- function parseTSDocTags(node, file = "") {
1353
+ var parserCache = /* @__PURE__ */ new Map();
1354
+ function getParser(options) {
1355
+ const extensionTagNames = [
1356
+ ...options?.extensionRegistry?.extensions.flatMap(
1357
+ (extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
1358
+ ) ?? []
1359
+ ].sort();
1360
+ const cacheKey = extensionTagNames.join("|");
1361
+ const existing = parserCache.get(cacheKey);
1362
+ if (existing) {
1363
+ return existing;
1364
+ }
1365
+ const parser = new TSDocParser(createFormSpecTSDocConfig(extensionTagNames));
1366
+ parserCache.set(cacheKey, parser);
1367
+ return parser;
1368
+ }
1369
+ function parseTSDocTags(node, file = "", options) {
1292
1370
  const constraints = [];
1293
1371
  const annotations = [];
1294
1372
  let displayName;
@@ -1309,7 +1387,7 @@ function parseTSDocTags(node, file = "") {
1309
1387
  if (!commentText.startsWith("/**")) {
1310
1388
  continue;
1311
1389
  }
1312
- const parser = getParser();
1390
+ const parser = getParser(options);
1313
1391
  const parserContext = parser.parseRange(
1314
1392
  TextRange.fromStringRange(sourceText, range.pos, range.end)
1315
1393
  );
@@ -1348,7 +1426,7 @@ function parseTSDocTags(node, file = "") {
1348
1426
  const expectedType = isBuiltinConstraintName(tagName) ? BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
1349
1427
  if (text === "" && expectedType !== "boolean") continue;
1350
1428
  const provenance = provenanceForComment(range, sourceFile, file, tagName);
1351
- const constraintNode = parseConstraintValue(tagName, text, provenance);
1429
+ const constraintNode = parseConstraintValue(tagName, text, provenance, options);
1352
1430
  if (constraintNode) {
1353
1431
  constraints.push(constraintNode);
1354
1432
  }
@@ -1408,7 +1486,7 @@ function parseTSDocTags(node, file = "") {
1408
1486
  annotations.push(defaultValueNode);
1409
1487
  continue;
1410
1488
  }
1411
- const constraintNode = parseConstraintValue(tagName, text, provenance);
1489
+ const constraintNode = parseConstraintValue(tagName, text, provenance, options);
1412
1490
  if (constraintNode) {
1413
1491
  constraints.push(constraintNode);
1414
1492
  }
@@ -1464,7 +1542,11 @@ function extractPlainText(node) {
1464
1542
  }
1465
1543
  return result;
1466
1544
  }
1467
- function parseConstraintValue(tagName, text, provenance) {
1545
+ function parseConstraintValue(tagName, text, provenance, options) {
1546
+ const customConstraint = parseExtensionConstraintValue(tagName, text, provenance, options);
1547
+ if (customConstraint) {
1548
+ return customConstraint;
1549
+ }
1468
1550
  if (!isBuiltinConstraintName(tagName)) {
1469
1551
  return null;
1470
1552
  }
@@ -1569,6 +1651,83 @@ function parseConstraintValue(tagName, text, provenance) {
1569
1651
  provenance
1570
1652
  };
1571
1653
  }
1654
+ function parseExtensionConstraintValue(tagName, text, provenance, options) {
1655
+ const pathResult = extractPathTarget(text);
1656
+ const effectiveText = pathResult ? pathResult.remainingText : text;
1657
+ const path3 = pathResult?.path;
1658
+ const registry = options?.extensionRegistry;
1659
+ if (registry === void 0) {
1660
+ return null;
1661
+ }
1662
+ const directTag = registry.findConstraintTag(tagName);
1663
+ if (directTag !== void 0) {
1664
+ return makeCustomConstraintNode(
1665
+ directTag.extensionId,
1666
+ directTag.registration.constraintName,
1667
+ directTag.registration.parseValue(effectiveText),
1668
+ provenance,
1669
+ path3,
1670
+ registry
1671
+ );
1672
+ }
1673
+ if (!isBuiltinConstraintName(tagName)) {
1674
+ return null;
1675
+ }
1676
+ const broadenedTypeId = getBroadenedCustomTypeId(options?.fieldType);
1677
+ if (broadenedTypeId === void 0) {
1678
+ return null;
1679
+ }
1680
+ const broadened = registry.findBuiltinConstraintBroadening(broadenedTypeId, tagName);
1681
+ if (broadened === void 0) {
1682
+ return null;
1683
+ }
1684
+ return makeCustomConstraintNode(
1685
+ broadened.extensionId,
1686
+ broadened.registration.constraintName,
1687
+ broadened.registration.parseValue(effectiveText),
1688
+ provenance,
1689
+ path3,
1690
+ registry
1691
+ );
1692
+ }
1693
+ function getBroadenedCustomTypeId(fieldType) {
1694
+ if (fieldType?.kind === "custom") {
1695
+ return fieldType.typeId;
1696
+ }
1697
+ if (fieldType?.kind !== "union") {
1698
+ return void 0;
1699
+ }
1700
+ const customMembers = fieldType.members.filter(
1701
+ (member) => member.kind === "custom"
1702
+ );
1703
+ if (customMembers.length !== 1) {
1704
+ return void 0;
1705
+ }
1706
+ const nonCustomMembers = fieldType.members.filter((member) => member.kind !== "custom");
1707
+ const allOtherMembersAreNull = nonCustomMembers.every(
1708
+ (member) => member.kind === "primitive" && member.primitiveKind === "null"
1709
+ );
1710
+ const customMember = customMembers[0];
1711
+ return allOtherMembersAreNull && customMember !== void 0 ? customMember.typeId : void 0;
1712
+ }
1713
+ function makeCustomConstraintNode(extensionId, constraintName, payload, provenance, path3, registry) {
1714
+ const constraintId = `${extensionId}/${constraintName}`;
1715
+ const registration = registry.findConstraint(constraintId);
1716
+ if (registration === void 0) {
1717
+ throw new Error(
1718
+ `Custom TSDoc tag resolved to unregistered constraint "${constraintId}". Register the constraint before using its tag.`
1719
+ );
1720
+ }
1721
+ return {
1722
+ kind: "constraint",
1723
+ constraintKind: "custom",
1724
+ constraintId,
1725
+ payload,
1726
+ compositionRule: registration.compositionRule,
1727
+ ...path3 && { path: path3 },
1728
+ provenance
1729
+ };
1730
+ }
1572
1731
  function parseDefaultValueValue(text, provenance) {
1573
1732
  const trimmed = text.trim();
1574
1733
  let value;
@@ -1629,12 +1788,12 @@ function getTagCommentText(tag) {
1629
1788
  }
1630
1789
 
1631
1790
  // src/analyzer/jsdoc-constraints.ts
1632
- function extractJSDocConstraintNodes(node, file = "") {
1633
- const result = parseTSDocTags(node, file);
1791
+ function extractJSDocConstraintNodes(node, file = "", options) {
1792
+ const result = parseTSDocTags(node, file, options);
1634
1793
  return [...result.constraints];
1635
1794
  }
1636
- function extractJSDocAnnotationNodes(node, file = "") {
1637
- const result = parseTSDocTags(node, file);
1795
+ function extractJSDocAnnotationNodes(node, file = "", options) {
1796
+ const result = parseTSDocTags(node, file, options);
1638
1797
  return [...result.annotations];
1639
1798
  }
1640
1799
  function extractDefaultValueAnnotation(initializer, file = "") {
@@ -1683,18 +1842,38 @@ var RESOLVING_TYPE_PLACEHOLDER = {
1683
1842
  properties: [],
1684
1843
  additionalProperties: true
1685
1844
  };
1686
- function analyzeClassToIR(classDecl, checker, file = "") {
1845
+ function makeParseOptions(extensionRegistry, fieldType) {
1846
+ if (extensionRegistry === void 0 && fieldType === void 0) {
1847
+ return void 0;
1848
+ }
1849
+ return {
1850
+ ...extensionRegistry !== void 0 && { extensionRegistry },
1851
+ ...fieldType !== void 0 && { fieldType }
1852
+ };
1853
+ }
1854
+ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
1687
1855
  const name = classDecl.name?.text ?? "AnonymousClass";
1688
1856
  const fields = [];
1689
1857
  const fieldLayouts = [];
1690
1858
  const typeRegistry = {};
1691
- const annotations = extractJSDocAnnotationNodes(classDecl, file);
1859
+ const annotations = extractJSDocAnnotationNodes(
1860
+ classDecl,
1861
+ file,
1862
+ makeParseOptions(extensionRegistry)
1863
+ );
1692
1864
  const visiting = /* @__PURE__ */ new Set();
1693
1865
  const instanceMethods = [];
1694
1866
  const staticMethods = [];
1695
1867
  for (const member of classDecl.members) {
1696
1868
  if (ts4.isPropertyDeclaration(member)) {
1697
- const fieldNode = analyzeFieldToIR(member, checker, file, typeRegistry, visiting);
1869
+ const fieldNode = analyzeFieldToIR(
1870
+ member,
1871
+ checker,
1872
+ file,
1873
+ typeRegistry,
1874
+ visiting,
1875
+ extensionRegistry
1876
+ );
1698
1877
  if (fieldNode) {
1699
1878
  fields.push(fieldNode);
1700
1879
  fieldLayouts.push({});
@@ -1721,15 +1900,26 @@ function analyzeClassToIR(classDecl, checker, file = "") {
1721
1900
  staticMethods
1722
1901
  };
1723
1902
  }
1724
- function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
1903
+ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry) {
1725
1904
  const name = interfaceDecl.name.text;
1726
1905
  const fields = [];
1727
1906
  const typeRegistry = {};
1728
- const annotations = extractJSDocAnnotationNodes(interfaceDecl, file);
1907
+ const annotations = extractJSDocAnnotationNodes(
1908
+ interfaceDecl,
1909
+ file,
1910
+ makeParseOptions(extensionRegistry)
1911
+ );
1729
1912
  const visiting = /* @__PURE__ */ new Set();
1730
1913
  for (const member of interfaceDecl.members) {
1731
1914
  if (ts4.isPropertySignature(member)) {
1732
- const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
1915
+ const fieldNode = analyzeInterfacePropertyToIR(
1916
+ member,
1917
+ checker,
1918
+ file,
1919
+ typeRegistry,
1920
+ visiting,
1921
+ extensionRegistry
1922
+ );
1733
1923
  if (fieldNode) {
1734
1924
  fields.push(fieldNode);
1735
1925
  }
@@ -1746,7 +1936,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
1746
1936
  staticMethods: []
1747
1937
  };
1748
1938
  }
1749
- function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
1939
+ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
1750
1940
  if (!ts4.isTypeLiteralNode(typeAlias.type)) {
1751
1941
  const sourceFile = typeAlias.getSourceFile();
1752
1942
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
@@ -1759,11 +1949,22 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
1759
1949
  const name = typeAlias.name.text;
1760
1950
  const fields = [];
1761
1951
  const typeRegistry = {};
1762
- const annotations = extractJSDocAnnotationNodes(typeAlias, file);
1952
+ const annotations = extractJSDocAnnotationNodes(
1953
+ typeAlias,
1954
+ file,
1955
+ makeParseOptions(extensionRegistry)
1956
+ );
1763
1957
  const visiting = /* @__PURE__ */ new Set();
1764
1958
  for (const member of typeAlias.type.members) {
1765
1959
  if (ts4.isPropertySignature(member)) {
1766
- const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
1960
+ const fieldNode = analyzeInterfacePropertyToIR(
1961
+ member,
1962
+ checker,
1963
+ file,
1964
+ typeRegistry,
1965
+ visiting,
1966
+ extensionRegistry
1967
+ );
1767
1968
  if (fieldNode) {
1768
1969
  fields.push(fieldNode);
1769
1970
  }
@@ -1782,7 +1983,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
1782
1983
  }
1783
1984
  };
1784
1985
  }
1785
- function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
1986
+ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
1786
1987
  if (!ts4.isIdentifier(prop.name)) {
1787
1988
  return null;
1788
1989
  }
@@ -1790,14 +1991,26 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
1790
1991
  const tsType = checker.getTypeAtLocation(prop);
1791
1992
  const optional = prop.questionToken !== void 0;
1792
1993
  const provenance = provenanceForNode(prop, file);
1793
- let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
1994
+ let type = resolveTypeNode(
1995
+ tsType,
1996
+ checker,
1997
+ file,
1998
+ typeRegistry,
1999
+ visiting,
2000
+ prop,
2001
+ extensionRegistry
2002
+ );
1794
2003
  const constraints = [];
1795
2004
  if (prop.type) {
1796
- constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
2005
+ constraints.push(
2006
+ ...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
2007
+ );
1797
2008
  }
1798
- constraints.push(...extractJSDocConstraintNodes(prop, file));
2009
+ constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
1799
2010
  let annotations = [];
1800
- annotations.push(...extractJSDocAnnotationNodes(prop, file));
2011
+ annotations.push(
2012
+ ...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
2013
+ );
1801
2014
  const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
1802
2015
  if (defaultAnnotation && !annotations.some((a) => a.annotationKind === "defaultValue")) {
1803
2016
  annotations.push(defaultAnnotation);
@@ -1813,7 +2026,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
1813
2026
  provenance
1814
2027
  };
1815
2028
  }
1816
- function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting) {
2029
+ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
1817
2030
  if (!ts4.isIdentifier(prop.name)) {
1818
2031
  return null;
1819
2032
  }
@@ -1821,14 +2034,26 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
1821
2034
  const tsType = checker.getTypeAtLocation(prop);
1822
2035
  const optional = prop.questionToken !== void 0;
1823
2036
  const provenance = provenanceForNode(prop, file);
1824
- let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
2037
+ let type = resolveTypeNode(
2038
+ tsType,
2039
+ checker,
2040
+ file,
2041
+ typeRegistry,
2042
+ visiting,
2043
+ prop,
2044
+ extensionRegistry
2045
+ );
1825
2046
  const constraints = [];
1826
2047
  if (prop.type) {
1827
- constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
2048
+ constraints.push(
2049
+ ...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
2050
+ );
1828
2051
  }
1829
- constraints.push(...extractJSDocConstraintNodes(prop, file));
2052
+ constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
1830
2053
  let annotations = [];
1831
- annotations.push(...extractJSDocAnnotationNodes(prop, file));
2054
+ annotations.push(
2055
+ ...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
2056
+ );
1832
2057
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
1833
2058
  return {
1834
2059
  kind: "field",
@@ -1902,7 +2127,66 @@ function parseEnumMemberDisplayName(value) {
1902
2127
  if (label === "") return null;
1903
2128
  return { value: match[1], label };
1904
2129
  }
1905
- function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode) {
2130
+ function resolveRegisteredCustomType(sourceNode, extensionRegistry, checker) {
2131
+ if (sourceNode === void 0 || extensionRegistry === void 0) {
2132
+ return null;
2133
+ }
2134
+ const typeNode = extractTypeNodeFromSource(sourceNode);
2135
+ if (typeNode === void 0) {
2136
+ return null;
2137
+ }
2138
+ return resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker);
2139
+ }
2140
+ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker) {
2141
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
2142
+ return resolveRegisteredCustomTypeFromTypeNode(typeNode.type, extensionRegistry, checker);
2143
+ }
2144
+ const typeName = getTypeNodeRegistrationName(typeNode);
2145
+ if (typeName === null) {
2146
+ return null;
2147
+ }
2148
+ const registration = extensionRegistry.findTypeByName(typeName);
2149
+ if (registration !== void 0) {
2150
+ return {
2151
+ kind: "custom",
2152
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
2153
+ payload: null
2154
+ };
2155
+ }
2156
+ if (ts4.isTypeReferenceNode(typeNode) && ts4.isIdentifier(typeNode.typeName)) {
2157
+ const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts4.isTypeAliasDeclaration);
2158
+ if (aliasDecl !== void 0) {
2159
+ return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
2160
+ }
2161
+ }
2162
+ return null;
2163
+ }
2164
+ function extractTypeNodeFromSource(sourceNode) {
2165
+ if (ts4.isPropertyDeclaration(sourceNode) || ts4.isPropertySignature(sourceNode) || ts4.isParameter(sourceNode) || ts4.isTypeAliasDeclaration(sourceNode)) {
2166
+ return sourceNode.type;
2167
+ }
2168
+ if (ts4.isTypeNode(sourceNode)) {
2169
+ return sourceNode;
2170
+ }
2171
+ return void 0;
2172
+ }
2173
+ function getTypeNodeRegistrationName(typeNode) {
2174
+ if (ts4.isTypeReferenceNode(typeNode)) {
2175
+ return ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
2176
+ }
2177
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
2178
+ return getTypeNodeRegistrationName(typeNode.type);
2179
+ }
2180
+ if (typeNode.kind === ts4.SyntaxKind.BigIntKeyword || typeNode.kind === ts4.SyntaxKind.StringKeyword || typeNode.kind === ts4.SyntaxKind.NumberKeyword || typeNode.kind === ts4.SyntaxKind.BooleanKeyword) {
2181
+ return typeNode.getText();
2182
+ }
2183
+ return null;
2184
+ }
2185
+ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
2186
+ const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
2187
+ if (customType) {
2188
+ return customType;
2189
+ }
1906
2190
  if (type.flags & ts4.TypeFlags.String) {
1907
2191
  return { kind: "primitive", primitiveKind: "string" };
1908
2192
  }
@@ -1931,26 +2215,50 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
1931
2215
  };
1932
2216
  }
1933
2217
  if (type.isUnion()) {
1934
- return resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode);
2218
+ return resolveUnionType(
2219
+ type,
2220
+ checker,
2221
+ file,
2222
+ typeRegistry,
2223
+ visiting,
2224
+ sourceNode,
2225
+ extensionRegistry
2226
+ );
1935
2227
  }
1936
2228
  if (checker.isArrayType(type)) {
1937
- return resolveArrayType(type, checker, file, typeRegistry, visiting);
2229
+ return resolveArrayType(
2230
+ type,
2231
+ checker,
2232
+ file,
2233
+ typeRegistry,
2234
+ visiting,
2235
+ sourceNode,
2236
+ extensionRegistry
2237
+ );
1938
2238
  }
1939
2239
  if (isObjectType(type)) {
1940
- return resolveObjectType(type, checker, file, typeRegistry, visiting);
2240
+ return resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry);
1941
2241
  }
1942
2242
  return { kind: "primitive", primitiveKind: "string" };
1943
2243
  }
1944
- function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode) {
2244
+ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
1945
2245
  const typeName = getNamedTypeName(type);
1946
2246
  const namedDecl = getNamedTypeDeclaration(type);
1947
2247
  if (typeName && typeName in typeRegistry) {
1948
2248
  return { kind: "reference", name: typeName, typeArguments: [] };
1949
2249
  }
1950
2250
  const allTypes = type.types;
2251
+ const unionMemberTypeNodes = extractUnionMemberTypeNodes(sourceNode, checker);
2252
+ const nonNullSourceNodes = unionMemberTypeNodes.filter(
2253
+ (memberTypeNode) => !isNullishTypeNode(resolveAliasedTypeNode(memberTypeNode, checker))
2254
+ );
1951
2255
  const nonNullTypes = allTypes.filter(
1952
- (t) => !(t.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
2256
+ (memberType) => !(memberType.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
1953
2257
  );
2258
+ const nonNullMembers = nonNullTypes.map((memberType, index) => ({
2259
+ memberType,
2260
+ sourceNode: nonNullSourceNodes.length === nonNullTypes.length ? nonNullSourceNodes[index] : void 0
2261
+ }));
1954
2262
  const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
1955
2263
  const memberDisplayNames = /* @__PURE__ */ new Map();
1956
2264
  if (namedDecl) {
@@ -1967,7 +2275,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
1967
2275
  if (!typeName) {
1968
2276
  return result;
1969
2277
  }
1970
- const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
2278
+ const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
1971
2279
  typeRegistry[typeName] = {
1972
2280
  name: typeName,
1973
2281
  type: result,
@@ -2015,14 +2323,15 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2015
2323
  } : enumNode;
2016
2324
  return registerNamed(result);
2017
2325
  }
2018
- if (nonNullTypes.length === 1 && nonNullTypes[0]) {
2326
+ if (nonNullMembers.length === 1 && nonNullMembers[0]) {
2019
2327
  const inner = resolveTypeNode(
2020
- nonNullTypes[0],
2328
+ nonNullMembers[0].memberType,
2021
2329
  checker,
2022
2330
  file,
2023
2331
  typeRegistry,
2024
2332
  visiting,
2025
- sourceNode
2333
+ nonNullMembers[0].sourceNode ?? sourceNode,
2334
+ extensionRegistry
2026
2335
  );
2027
2336
  const result = hasNull ? {
2028
2337
  kind: "union",
@@ -2030,21 +2339,38 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2030
2339
  } : inner;
2031
2340
  return registerNamed(result);
2032
2341
  }
2033
- const members = nonNullTypes.map(
2034
- (t) => resolveTypeNode(t, checker, file, typeRegistry, visiting, sourceNode)
2342
+ const members = nonNullMembers.map(
2343
+ ({ memberType, sourceNode: memberSourceNode }) => resolveTypeNode(
2344
+ memberType,
2345
+ checker,
2346
+ file,
2347
+ typeRegistry,
2348
+ visiting,
2349
+ memberSourceNode ?? sourceNode,
2350
+ extensionRegistry
2351
+ )
2035
2352
  );
2036
2353
  if (hasNull) {
2037
2354
  members.push({ kind: "primitive", primitiveKind: "null" });
2038
2355
  }
2039
2356
  return registerNamed({ kind: "union", members });
2040
2357
  }
2041
- function resolveArrayType(type, checker, file, typeRegistry, visiting) {
2358
+ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
2042
2359
  const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
2043
2360
  const elementType = typeArgs?.[0];
2044
- const items = elementType ? resolveTypeNode(elementType, checker, file, typeRegistry, visiting) : { kind: "primitive", primitiveKind: "string" };
2361
+ const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
2362
+ const items = elementType ? resolveTypeNode(
2363
+ elementType,
2364
+ checker,
2365
+ file,
2366
+ typeRegistry,
2367
+ visiting,
2368
+ elementSourceNode,
2369
+ extensionRegistry
2370
+ ) : { kind: "primitive", primitiveKind: "string" };
2045
2371
  return { kind: "array", items };
2046
2372
  }
2047
- function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
2373
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
2048
2374
  if (type.getProperties().length > 0) {
2049
2375
  return null;
2050
2376
  }
@@ -2052,7 +2378,15 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
2052
2378
  if (!indexInfo) {
2053
2379
  return null;
2054
2380
  }
2055
- const valueType = resolveTypeNode(indexInfo.type, checker, file, typeRegistry, visiting);
2381
+ const valueType = resolveTypeNode(
2382
+ indexInfo.type,
2383
+ checker,
2384
+ file,
2385
+ typeRegistry,
2386
+ visiting,
2387
+ void 0,
2388
+ extensionRegistry
2389
+ );
2056
2390
  return { kind: "record", valueType };
2057
2391
  }
2058
2392
  function typeNodeContainsReference(type, targetName) {
@@ -2080,7 +2414,7 @@ function typeNodeContainsReference(type, targetName) {
2080
2414
  }
2081
2415
  }
2082
2416
  }
2083
- function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2417
+ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
2084
2418
  const typeName = getNamedTypeName(type);
2085
2419
  const namedTypeName = typeName ?? void 0;
2086
2420
  const namedDecl = getNamedTypeDeclaration(type);
@@ -2111,7 +2445,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2111
2445
  return { kind: "reference", name: namedTypeName, typeArguments: [] };
2112
2446
  }
2113
2447
  }
2114
- const recordNode = tryResolveRecordType(type, checker, file, typeRegistry, visiting);
2448
+ const recordNode = tryResolveRecordType(
2449
+ type,
2450
+ checker,
2451
+ file,
2452
+ typeRegistry,
2453
+ visiting,
2454
+ extensionRegistry
2455
+ );
2115
2456
  if (recordNode) {
2116
2457
  visiting.delete(type);
2117
2458
  if (namedTypeName !== void 0 && shouldRegisterNamedType) {
@@ -2120,7 +2461,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2120
2461
  clearNamedTypeRegistration();
2121
2462
  return recordNode;
2122
2463
  }
2123
- const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
2464
+ const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2124
2465
  typeRegistry[namedTypeName] = {
2125
2466
  name: namedTypeName,
2126
2467
  type: recordNode,
@@ -2132,7 +2473,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2132
2473
  return recordNode;
2133
2474
  }
2134
2475
  const properties = [];
2135
- const fieldInfoMap = getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting);
2476
+ const fieldInfoMap = getNamedTypeFieldNodeInfoMap(
2477
+ type,
2478
+ checker,
2479
+ file,
2480
+ typeRegistry,
2481
+ visiting,
2482
+ extensionRegistry
2483
+ );
2136
2484
  for (const prop of type.getProperties()) {
2137
2485
  const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
2138
2486
  if (!declaration) continue;
@@ -2144,7 +2492,8 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2144
2492
  file,
2145
2493
  typeRegistry,
2146
2494
  visiting,
2147
- declaration
2495
+ declaration,
2496
+ extensionRegistry
2148
2497
  );
2149
2498
  const fieldNodeInfo = fieldInfoMap?.get(prop.name);
2150
2499
  properties.push({
@@ -2163,7 +2512,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2163
2512
  additionalProperties: true
2164
2513
  };
2165
2514
  if (namedTypeName !== void 0 && shouldRegisterNamedType) {
2166
- const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
2515
+ const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2167
2516
  typeRegistry[namedTypeName] = {
2168
2517
  name: namedTypeName,
2169
2518
  type: objectNode,
@@ -2174,7 +2523,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2174
2523
  }
2175
2524
  return objectNode;
2176
2525
  }
2177
- function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting) {
2526
+ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, extensionRegistry) {
2178
2527
  const symbols = [type.getSymbol(), type.aliasSymbol].filter(
2179
2528
  (s) => s?.declarations != null && s.declarations.length > 0
2180
2529
  );
@@ -2186,7 +2535,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2186
2535
  const map = /* @__PURE__ */ new Map();
2187
2536
  for (const member of classDecl.members) {
2188
2537
  if (ts4.isPropertyDeclaration(member) && ts4.isIdentifier(member.name)) {
2189
- const fieldNode = analyzeFieldToIR(member, checker, file, typeRegistry, visiting);
2538
+ const fieldNode = analyzeFieldToIR(
2539
+ member,
2540
+ checker,
2541
+ file,
2542
+ typeRegistry,
2543
+ visiting,
2544
+ extensionRegistry
2545
+ );
2190
2546
  if (fieldNode) {
2191
2547
  map.set(fieldNode.name, {
2192
2548
  constraints: [...fieldNode.constraints],
@@ -2200,7 +2556,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2200
2556
  }
2201
2557
  const interfaceDecl = declarations.find(ts4.isInterfaceDeclaration);
2202
2558
  if (interfaceDecl) {
2203
- return buildFieldNodeInfoMap(interfaceDecl.members, checker, file, typeRegistry, visiting);
2559
+ return buildFieldNodeInfoMap(
2560
+ interfaceDecl.members,
2561
+ checker,
2562
+ file,
2563
+ typeRegistry,
2564
+ visiting,
2565
+ extensionRegistry
2566
+ );
2204
2567
  }
2205
2568
  const typeAliasDecl = declarations.find(ts4.isTypeAliasDeclaration);
2206
2569
  if (typeAliasDecl && ts4.isTypeLiteralNode(typeAliasDecl.type)) {
@@ -2209,17 +2572,68 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2209
2572
  checker,
2210
2573
  file,
2211
2574
  typeRegistry,
2212
- visiting
2575
+ visiting,
2576
+ extensionRegistry
2213
2577
  );
2214
2578
  }
2215
2579
  }
2216
2580
  return null;
2217
2581
  }
2218
- function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
2582
+ function extractArrayElementTypeNode(sourceNode, checker) {
2583
+ const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
2584
+ if (typeNode === void 0) {
2585
+ return void 0;
2586
+ }
2587
+ const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
2588
+ if (ts4.isArrayTypeNode(resolvedTypeNode)) {
2589
+ return resolvedTypeNode.elementType;
2590
+ }
2591
+ if (ts4.isTypeReferenceNode(resolvedTypeNode) && ts4.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
2592
+ return resolvedTypeNode.typeArguments[0];
2593
+ }
2594
+ return void 0;
2595
+ }
2596
+ function extractUnionMemberTypeNodes(sourceNode, checker) {
2597
+ const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
2598
+ if (!typeNode) {
2599
+ return [];
2600
+ }
2601
+ const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
2602
+ return ts4.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
2603
+ }
2604
+ function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
2605
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
2606
+ return resolveAliasedTypeNode(typeNode.type, checker, visited);
2607
+ }
2608
+ if (!ts4.isTypeReferenceNode(typeNode) || !ts4.isIdentifier(typeNode.typeName)) {
2609
+ return typeNode;
2610
+ }
2611
+ const symbol = checker.getSymbolAtLocation(typeNode.typeName);
2612
+ const aliasDecl = symbol?.declarations?.find(ts4.isTypeAliasDeclaration);
2613
+ if (aliasDecl === void 0 || visited.has(aliasDecl)) {
2614
+ return typeNode;
2615
+ }
2616
+ visited.add(aliasDecl);
2617
+ return resolveAliasedTypeNode(aliasDecl.type, checker, visited);
2618
+ }
2619
+ function isNullishTypeNode(typeNode) {
2620
+ if (typeNode.kind === ts4.SyntaxKind.NullKeyword || typeNode.kind === ts4.SyntaxKind.UndefinedKeyword) {
2621
+ return true;
2622
+ }
2623
+ return ts4.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts4.SyntaxKind.NullKeyword || typeNode.literal.kind === ts4.SyntaxKind.UndefinedKeyword);
2624
+ }
2625
+ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, extensionRegistry) {
2219
2626
  const map = /* @__PURE__ */ new Map();
2220
2627
  for (const member of members) {
2221
2628
  if (ts4.isPropertySignature(member)) {
2222
- const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
2629
+ const fieldNode = analyzeInterfacePropertyToIR(
2630
+ member,
2631
+ checker,
2632
+ file,
2633
+ typeRegistry,
2634
+ visiting,
2635
+ extensionRegistry
2636
+ );
2223
2637
  if (fieldNode) {
2224
2638
  map.set(fieldNode.name, {
2225
2639
  constraints: [...fieldNode.constraints],
@@ -2232,7 +2646,7 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
2232
2646
  return map;
2233
2647
  }
2234
2648
  var MAX_ALIAS_CHAIN_DEPTH = 8;
2235
- function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
2649
+ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegistry, depth = 0) {
2236
2650
  if (!ts4.isTypeReferenceNode(typeNode)) return [];
2237
2651
  if (depth >= MAX_ALIAS_CHAIN_DEPTH) {
2238
2652
  const aliasName = typeNode.typeName.getText();
@@ -2245,8 +2659,29 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
2245
2659
  const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
2246
2660
  if (!aliasDecl) return [];
2247
2661
  if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
2248
- const constraints = extractJSDocConstraintNodes(aliasDecl, file);
2249
- constraints.push(...extractTypeAliasConstraintNodes(aliasDecl.type, checker, file, depth + 1));
2662
+ const aliasFieldType = resolveTypeNode(
2663
+ checker.getTypeAtLocation(aliasDecl.type),
2664
+ checker,
2665
+ file,
2666
+ {},
2667
+ /* @__PURE__ */ new Set(),
2668
+ aliasDecl.type,
2669
+ extensionRegistry
2670
+ );
2671
+ const constraints = extractJSDocConstraintNodes(
2672
+ aliasDecl,
2673
+ file,
2674
+ makeParseOptions(extensionRegistry, aliasFieldType)
2675
+ );
2676
+ constraints.push(
2677
+ ...extractTypeAliasConstraintNodes(
2678
+ aliasDecl.type,
2679
+ checker,
2680
+ file,
2681
+ extensionRegistry,
2682
+ depth + 1
2683
+ )
2684
+ );
2250
2685
  return constraints;
2251
2686
  }
2252
2687
  function provenanceForNode(node, file) {
@@ -2342,10 +2777,10 @@ function detectFormSpecReference(typeNode) {
2342
2777
  }
2343
2778
 
2344
2779
  // src/generators/class-schema.ts
2345
- function generateClassSchemas(analysis, source) {
2780
+ function generateClassSchemas(analysis, source, options) {
2346
2781
  const ir = canonicalizeTSDoc(analysis, source);
2347
2782
  return {
2348
- jsonSchema: generateJsonSchemaFromIR(ir),
2783
+ jsonSchema: generateJsonSchemaFromIR(ir, options),
2349
2784
  uiSchema: generateUiSchemaFromIR(ir)
2350
2785
  };
2351
2786
  }
@@ -2355,27 +2790,54 @@ function generateSchemasFromClass(options) {
2355
2790
  if (!classDecl) {
2356
2791
  throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
2357
2792
  }
2358
- const analysis = analyzeClassToIR(classDecl, ctx.checker, options.filePath);
2359
- return generateClassSchemas(analysis, { file: options.filePath });
2793
+ const analysis = analyzeClassToIR(
2794
+ classDecl,
2795
+ ctx.checker,
2796
+ options.filePath,
2797
+ options.extensionRegistry
2798
+ );
2799
+ return generateClassSchemas(
2800
+ analysis,
2801
+ { file: options.filePath },
2802
+ {
2803
+ extensionRegistry: options.extensionRegistry,
2804
+ vendorPrefix: options.vendorPrefix
2805
+ }
2806
+ );
2360
2807
  }
2361
2808
  function generateSchemas(options) {
2362
2809
  const ctx = createProgramContext(options.filePath);
2363
2810
  const source = { file: options.filePath };
2364
2811
  const classDecl = findClassByName(ctx.sourceFile, options.typeName);
2365
2812
  if (classDecl) {
2366
- const analysis = analyzeClassToIR(classDecl, ctx.checker, options.filePath);
2367
- return generateClassSchemas(analysis, source);
2813
+ const analysis = analyzeClassToIR(
2814
+ classDecl,
2815
+ ctx.checker,
2816
+ options.filePath,
2817
+ options.extensionRegistry
2818
+ );
2819
+ return generateClassSchemas(analysis, source, options);
2368
2820
  }
2369
2821
  const interfaceDecl = findInterfaceByName(ctx.sourceFile, options.typeName);
2370
2822
  if (interfaceDecl) {
2371
- const analysis = analyzeInterfaceToIR(interfaceDecl, ctx.checker, options.filePath);
2372
- return generateClassSchemas(analysis, source);
2823
+ const analysis = analyzeInterfaceToIR(
2824
+ interfaceDecl,
2825
+ ctx.checker,
2826
+ options.filePath,
2827
+ options.extensionRegistry
2828
+ );
2829
+ return generateClassSchemas(analysis, source, options);
2373
2830
  }
2374
2831
  const typeAlias = findTypeAliasByName(ctx.sourceFile, options.typeName);
2375
2832
  if (typeAlias) {
2376
- const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, options.filePath);
2833
+ const result = analyzeTypeAliasToIR(
2834
+ typeAlias,
2835
+ ctx.checker,
2836
+ options.filePath,
2837
+ options.extensionRegistry
2838
+ );
2377
2839
  if (result.ok) {
2378
- return generateClassSchemas(result.analysis, source);
2840
+ return generateClassSchemas(result.analysis, source, options);
2379
2841
  }
2380
2842
  throw new Error(result.error);
2381
2843
  }
@@ -2387,7 +2849,7 @@ function generateSchemas(options) {
2387
2849
  // src/generators/mixed-authoring.ts
2388
2850
  function buildMixedAuthoringSchemas(options) {
2389
2851
  const { filePath, typeName, overlays, ...schemaOptions } = options;
2390
- const analysis = analyzeNamedType(filePath, typeName);
2852
+ const analysis = analyzeNamedType(filePath, typeName, schemaOptions.extensionRegistry);
2391
2853
  const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays);
2392
2854
  const ir = canonicalizeTSDoc(composedAnalysis, { file: filePath });
2393
2855
  return {
@@ -2395,20 +2857,20 @@ function buildMixedAuthoringSchemas(options) {
2395
2857
  uiSchema: generateUiSchemaFromIR(ir)
2396
2858
  };
2397
2859
  }
2398
- function analyzeNamedType(filePath, typeName) {
2860
+ function analyzeNamedType(filePath, typeName, extensionRegistry) {
2399
2861
  const ctx = createProgramContext(filePath);
2400
2862
  const source = { file: filePath };
2401
2863
  const classDecl = findClassByName(ctx.sourceFile, typeName);
2402
2864
  if (classDecl !== null) {
2403
- return analyzeClassToIR(classDecl, ctx.checker, source.file);
2865
+ return analyzeClassToIR(classDecl, ctx.checker, source.file, extensionRegistry);
2404
2866
  }
2405
2867
  const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
2406
2868
  if (interfaceDecl !== null) {
2407
- return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file);
2869
+ return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file, extensionRegistry);
2408
2870
  }
2409
2871
  const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
2410
2872
  if (typeAlias !== null) {
2411
- const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file);
2873
+ const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file, extensionRegistry);
2412
2874
  if (result.ok) {
2413
2875
  return result.analysis;
2414
2876
  }
@@ -2487,7 +2949,7 @@ function assertSupportedOverlayField(baseField, overlayField) {
2487
2949
  `Mixed-authoring overlay for "${baseField.name}" cannot define constraints; keep constraints on the static model`
2488
2950
  );
2489
2951
  }
2490
- if (overlayField.required) {
2952
+ if (overlayField.required && !baseField.required) {
2491
2953
  throw new Error(
2492
2954
  `Mixed-authoring overlay for "${baseField.name}" cannot change requiredness; keep requiredness on the static model`
2493
2955
  );
@@ -2577,7 +3039,7 @@ function mergeAnnotations(baseAnnotations, overlayAnnotations) {
2577
3039
  const overlayOnly = overlayAnnotations.filter(
2578
3040
  (annotation) => !baseKeys.has(annotationKey(annotation))
2579
3041
  );
2580
- return [...overlayOnly, ...baseAnnotations];
3042
+ return [...baseAnnotations, ...overlayOnly];
2581
3043
  }
2582
3044
  function annotationKey(annotation) {
2583
3045
  return annotation.annotationKind === "custom" ? `${annotation.annotationKind}:${annotation.annotationId}` : annotation.annotationKind;