@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.cjs CHANGED
@@ -838,7 +838,12 @@ function applyCustomConstraint(schema, constraint, ctx) {
838
838
  `Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
839
839
  );
840
840
  }
841
- Object.assign(schema, registration.toJsonSchema(constraint.payload, ctx.vendorPrefix));
841
+ assignVendorPrefixedExtensionKeywords(
842
+ schema,
843
+ registration.toJsonSchema(constraint.payload, ctx.vendorPrefix),
844
+ ctx.vendorPrefix,
845
+ `custom constraint "${constraint.constraintId}"`
846
+ );
842
847
  }
843
848
  function applyCustomAnnotation(schema, annotation, ctx) {
844
849
  const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
@@ -850,7 +855,22 @@ function applyCustomAnnotation(schema, annotation, ctx) {
850
855
  if (registration.toJsonSchema === void 0) {
851
856
  return;
852
857
  }
853
- Object.assign(schema, registration.toJsonSchema(annotation.value, ctx.vendorPrefix));
858
+ assignVendorPrefixedExtensionKeywords(
859
+ schema,
860
+ registration.toJsonSchema(annotation.value, ctx.vendorPrefix),
861
+ ctx.vendorPrefix,
862
+ `custom annotation "${annotation.annotationId}"`
863
+ );
864
+ }
865
+ function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPrefix, source) {
866
+ for (const [key, value] of Object.entries(extensionSchema)) {
867
+ if (!key.startsWith(`${vendorPrefix}-`)) {
868
+ throw new Error(
869
+ `Cannot apply ${source}: extension hooks may only emit "${vendorPrefix}-*" JSON Schema keywords`
870
+ );
871
+ }
872
+ schema[key] = value;
873
+ }
854
874
  }
855
875
 
856
876
  // src/json-schema/generator.ts
@@ -1103,7 +1123,10 @@ function getSchemaExtension(schema, key) {
1103
1123
  // src/extensions/registry.ts
1104
1124
  function createExtensionRegistry(extensions) {
1105
1125
  const typeMap = /* @__PURE__ */ new Map();
1126
+ const typeNameMap = /* @__PURE__ */ new Map();
1106
1127
  const constraintMap = /* @__PURE__ */ new Map();
1128
+ const constraintTagMap = /* @__PURE__ */ new Map();
1129
+ const builtinBroadeningMap = /* @__PURE__ */ new Map();
1107
1130
  const annotationMap = /* @__PURE__ */ new Map();
1108
1131
  for (const ext of extensions) {
1109
1132
  if (ext.types !== void 0) {
@@ -1113,6 +1136,27 @@ function createExtensionRegistry(extensions) {
1113
1136
  throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
1114
1137
  }
1115
1138
  typeMap.set(qualifiedId, type);
1139
+ for (const sourceTypeName of type.tsTypeNames ?? [type.typeName]) {
1140
+ if (typeNameMap.has(sourceTypeName)) {
1141
+ throw new Error(`Duplicate custom type source name: "${sourceTypeName}"`);
1142
+ }
1143
+ typeNameMap.set(sourceTypeName, {
1144
+ extensionId: ext.extensionId,
1145
+ registration: type
1146
+ });
1147
+ }
1148
+ if (type.builtinConstraintBroadenings !== void 0) {
1149
+ for (const broadening of type.builtinConstraintBroadenings) {
1150
+ const key = `${qualifiedId}:${broadening.tagName}`;
1151
+ if (builtinBroadeningMap.has(key)) {
1152
+ throw new Error(`Duplicate built-in constraint broadening: "${key}"`);
1153
+ }
1154
+ builtinBroadeningMap.set(key, {
1155
+ extensionId: ext.extensionId,
1156
+ registration: broadening
1157
+ });
1158
+ }
1159
+ }
1116
1160
  }
1117
1161
  }
1118
1162
  if (ext.constraints !== void 0) {
@@ -1124,6 +1168,17 @@ function createExtensionRegistry(extensions) {
1124
1168
  constraintMap.set(qualifiedId, constraint);
1125
1169
  }
1126
1170
  }
1171
+ if (ext.constraintTags !== void 0) {
1172
+ for (const tag of ext.constraintTags) {
1173
+ if (constraintTagMap.has(tag.tagName)) {
1174
+ throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
1175
+ }
1176
+ constraintTagMap.set(tag.tagName, {
1177
+ extensionId: ext.extensionId,
1178
+ registration: tag
1179
+ });
1180
+ }
1181
+ }
1127
1182
  if (ext.annotations !== void 0) {
1128
1183
  for (const annotation of ext.annotations) {
1129
1184
  const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
@@ -1137,7 +1192,10 @@ function createExtensionRegistry(extensions) {
1137
1192
  return {
1138
1193
  extensions,
1139
1194
  findType: (typeId) => typeMap.get(typeId),
1195
+ findTypeByName: (typeName) => typeNameMap.get(typeName),
1140
1196
  findConstraint: (constraintId) => constraintMap.get(constraintId),
1197
+ findConstraintTag: (tagName) => constraintTagMap.get(tagName),
1198
+ findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
1141
1199
  findAnnotation: (annotationId) => annotationMap.get(annotationId)
1142
1200
  };
1143
1201
  }
@@ -1311,7 +1369,7 @@ var LENGTH_CONSTRAINT_MAP = {
1311
1369
  maxItems: "maxItems"
1312
1370
  };
1313
1371
  var TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions", "defaultValue"]);
1314
- function createFormSpecTSDocConfig() {
1372
+ function createFormSpecTSDocConfig(extensionTagNames = []) {
1315
1373
  const config = new import_tsdoc.TSDocConfiguration();
1316
1374
  for (const tagName of Object.keys(import_core3.BUILTIN_CONSTRAINT_DEFINITIONS)) {
1317
1375
  config.addTagDefinition(
@@ -1331,14 +1389,34 @@ function createFormSpecTSDocConfig() {
1331
1389
  })
1332
1390
  );
1333
1391
  }
1392
+ for (const tagName of extensionTagNames) {
1393
+ config.addTagDefinition(
1394
+ new import_tsdoc.TSDocTagDefinition({
1395
+ tagName: "@" + tagName,
1396
+ syntaxKind: import_tsdoc.TSDocTagSyntaxKind.BlockTag,
1397
+ allowMultiple: true
1398
+ })
1399
+ );
1400
+ }
1334
1401
  return config;
1335
1402
  }
1336
- var sharedParser;
1337
- function getParser() {
1338
- sharedParser ??= new import_tsdoc.TSDocParser(createFormSpecTSDocConfig());
1339
- return sharedParser;
1340
- }
1341
- function parseTSDocTags(node, file = "") {
1403
+ var parserCache = /* @__PURE__ */ new Map();
1404
+ function getParser(options) {
1405
+ const extensionTagNames = [
1406
+ ...options?.extensionRegistry?.extensions.flatMap(
1407
+ (extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
1408
+ ) ?? []
1409
+ ].sort();
1410
+ const cacheKey = extensionTagNames.join("|");
1411
+ const existing = parserCache.get(cacheKey);
1412
+ if (existing) {
1413
+ return existing;
1414
+ }
1415
+ const parser = new import_tsdoc.TSDocParser(createFormSpecTSDocConfig(extensionTagNames));
1416
+ parserCache.set(cacheKey, parser);
1417
+ return parser;
1418
+ }
1419
+ function parseTSDocTags(node, file = "", options) {
1342
1420
  const constraints = [];
1343
1421
  const annotations = [];
1344
1422
  let displayName;
@@ -1359,7 +1437,7 @@ function parseTSDocTags(node, file = "") {
1359
1437
  if (!commentText.startsWith("/**")) {
1360
1438
  continue;
1361
1439
  }
1362
- const parser = getParser();
1440
+ const parser = getParser(options);
1363
1441
  const parserContext = parser.parseRange(
1364
1442
  import_tsdoc.TextRange.fromStringRange(sourceText, range.pos, range.end)
1365
1443
  );
@@ -1398,7 +1476,7 @@ function parseTSDocTags(node, file = "") {
1398
1476
  const expectedType = (0, import_core3.isBuiltinConstraintName)(tagName) ? import_core3.BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
1399
1477
  if (text === "" && expectedType !== "boolean") continue;
1400
1478
  const provenance = provenanceForComment(range, sourceFile, file, tagName);
1401
- const constraintNode = parseConstraintValue(tagName, text, provenance);
1479
+ const constraintNode = parseConstraintValue(tagName, text, provenance, options);
1402
1480
  if (constraintNode) {
1403
1481
  constraints.push(constraintNode);
1404
1482
  }
@@ -1458,7 +1536,7 @@ function parseTSDocTags(node, file = "") {
1458
1536
  annotations.push(defaultValueNode);
1459
1537
  continue;
1460
1538
  }
1461
- const constraintNode = parseConstraintValue(tagName, text, provenance);
1539
+ const constraintNode = parseConstraintValue(tagName, text, provenance, options);
1462
1540
  if (constraintNode) {
1463
1541
  constraints.push(constraintNode);
1464
1542
  }
@@ -1514,7 +1592,11 @@ function extractPlainText(node) {
1514
1592
  }
1515
1593
  return result;
1516
1594
  }
1517
- function parseConstraintValue(tagName, text, provenance) {
1595
+ function parseConstraintValue(tagName, text, provenance, options) {
1596
+ const customConstraint = parseExtensionConstraintValue(tagName, text, provenance, options);
1597
+ if (customConstraint) {
1598
+ return customConstraint;
1599
+ }
1518
1600
  if (!(0, import_core3.isBuiltinConstraintName)(tagName)) {
1519
1601
  return null;
1520
1602
  }
@@ -1619,6 +1701,83 @@ function parseConstraintValue(tagName, text, provenance) {
1619
1701
  provenance
1620
1702
  };
1621
1703
  }
1704
+ function parseExtensionConstraintValue(tagName, text, provenance, options) {
1705
+ const pathResult = extractPathTarget(text);
1706
+ const effectiveText = pathResult ? pathResult.remainingText : text;
1707
+ const path3 = pathResult?.path;
1708
+ const registry = options?.extensionRegistry;
1709
+ if (registry === void 0) {
1710
+ return null;
1711
+ }
1712
+ const directTag = registry.findConstraintTag(tagName);
1713
+ if (directTag !== void 0) {
1714
+ return makeCustomConstraintNode(
1715
+ directTag.extensionId,
1716
+ directTag.registration.constraintName,
1717
+ directTag.registration.parseValue(effectiveText),
1718
+ provenance,
1719
+ path3,
1720
+ registry
1721
+ );
1722
+ }
1723
+ if (!(0, import_core3.isBuiltinConstraintName)(tagName)) {
1724
+ return null;
1725
+ }
1726
+ const broadenedTypeId = getBroadenedCustomTypeId(options?.fieldType);
1727
+ if (broadenedTypeId === void 0) {
1728
+ return null;
1729
+ }
1730
+ const broadened = registry.findBuiltinConstraintBroadening(broadenedTypeId, tagName);
1731
+ if (broadened === void 0) {
1732
+ return null;
1733
+ }
1734
+ return makeCustomConstraintNode(
1735
+ broadened.extensionId,
1736
+ broadened.registration.constraintName,
1737
+ broadened.registration.parseValue(effectiveText),
1738
+ provenance,
1739
+ path3,
1740
+ registry
1741
+ );
1742
+ }
1743
+ function getBroadenedCustomTypeId(fieldType) {
1744
+ if (fieldType?.kind === "custom") {
1745
+ return fieldType.typeId;
1746
+ }
1747
+ if (fieldType?.kind !== "union") {
1748
+ return void 0;
1749
+ }
1750
+ const customMembers = fieldType.members.filter(
1751
+ (member) => member.kind === "custom"
1752
+ );
1753
+ if (customMembers.length !== 1) {
1754
+ return void 0;
1755
+ }
1756
+ const nonCustomMembers = fieldType.members.filter((member) => member.kind !== "custom");
1757
+ const allOtherMembersAreNull = nonCustomMembers.every(
1758
+ (member) => member.kind === "primitive" && member.primitiveKind === "null"
1759
+ );
1760
+ const customMember = customMembers[0];
1761
+ return allOtherMembersAreNull && customMember !== void 0 ? customMember.typeId : void 0;
1762
+ }
1763
+ function makeCustomConstraintNode(extensionId, constraintName, payload, provenance, path3, registry) {
1764
+ const constraintId = `${extensionId}/${constraintName}`;
1765
+ const registration = registry.findConstraint(constraintId);
1766
+ if (registration === void 0) {
1767
+ throw new Error(
1768
+ `Custom TSDoc tag resolved to unregistered constraint "${constraintId}". Register the constraint before using its tag.`
1769
+ );
1770
+ }
1771
+ return {
1772
+ kind: "constraint",
1773
+ constraintKind: "custom",
1774
+ constraintId,
1775
+ payload,
1776
+ compositionRule: registration.compositionRule,
1777
+ ...path3 && { path: path3 },
1778
+ provenance
1779
+ };
1780
+ }
1622
1781
  function parseDefaultValueValue(text, provenance) {
1623
1782
  const trimmed = text.trim();
1624
1783
  let value;
@@ -1679,12 +1838,12 @@ function getTagCommentText(tag) {
1679
1838
  }
1680
1839
 
1681
1840
  // src/analyzer/jsdoc-constraints.ts
1682
- function extractJSDocConstraintNodes(node, file = "") {
1683
- const result = parseTSDocTags(node, file);
1841
+ function extractJSDocConstraintNodes(node, file = "", options) {
1842
+ const result = parseTSDocTags(node, file, options);
1684
1843
  return [...result.constraints];
1685
1844
  }
1686
- function extractJSDocAnnotationNodes(node, file = "") {
1687
- const result = parseTSDocTags(node, file);
1845
+ function extractJSDocAnnotationNodes(node, file = "", options) {
1846
+ const result = parseTSDocTags(node, file, options);
1688
1847
  return [...result.annotations];
1689
1848
  }
1690
1849
  function extractDefaultValueAnnotation(initializer, file = "") {
@@ -1733,18 +1892,38 @@ var RESOLVING_TYPE_PLACEHOLDER = {
1733
1892
  properties: [],
1734
1893
  additionalProperties: true
1735
1894
  };
1736
- function analyzeClassToIR(classDecl, checker, file = "") {
1895
+ function makeParseOptions(extensionRegistry, fieldType) {
1896
+ if (extensionRegistry === void 0 && fieldType === void 0) {
1897
+ return void 0;
1898
+ }
1899
+ return {
1900
+ ...extensionRegistry !== void 0 && { extensionRegistry },
1901
+ ...fieldType !== void 0 && { fieldType }
1902
+ };
1903
+ }
1904
+ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
1737
1905
  const name = classDecl.name?.text ?? "AnonymousClass";
1738
1906
  const fields = [];
1739
1907
  const fieldLayouts = [];
1740
1908
  const typeRegistry = {};
1741
- const annotations = extractJSDocAnnotationNodes(classDecl, file);
1909
+ const annotations = extractJSDocAnnotationNodes(
1910
+ classDecl,
1911
+ file,
1912
+ makeParseOptions(extensionRegistry)
1913
+ );
1742
1914
  const visiting = /* @__PURE__ */ new Set();
1743
1915
  const instanceMethods = [];
1744
1916
  const staticMethods = [];
1745
1917
  for (const member of classDecl.members) {
1746
1918
  if (ts4.isPropertyDeclaration(member)) {
1747
- const fieldNode = analyzeFieldToIR(member, checker, file, typeRegistry, visiting);
1919
+ const fieldNode = analyzeFieldToIR(
1920
+ member,
1921
+ checker,
1922
+ file,
1923
+ typeRegistry,
1924
+ visiting,
1925
+ extensionRegistry
1926
+ );
1748
1927
  if (fieldNode) {
1749
1928
  fields.push(fieldNode);
1750
1929
  fieldLayouts.push({});
@@ -1771,15 +1950,26 @@ function analyzeClassToIR(classDecl, checker, file = "") {
1771
1950
  staticMethods
1772
1951
  };
1773
1952
  }
1774
- function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
1953
+ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry) {
1775
1954
  const name = interfaceDecl.name.text;
1776
1955
  const fields = [];
1777
1956
  const typeRegistry = {};
1778
- const annotations = extractJSDocAnnotationNodes(interfaceDecl, file);
1957
+ const annotations = extractJSDocAnnotationNodes(
1958
+ interfaceDecl,
1959
+ file,
1960
+ makeParseOptions(extensionRegistry)
1961
+ );
1779
1962
  const visiting = /* @__PURE__ */ new Set();
1780
1963
  for (const member of interfaceDecl.members) {
1781
1964
  if (ts4.isPropertySignature(member)) {
1782
- const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
1965
+ const fieldNode = analyzeInterfacePropertyToIR(
1966
+ member,
1967
+ checker,
1968
+ file,
1969
+ typeRegistry,
1970
+ visiting,
1971
+ extensionRegistry
1972
+ );
1783
1973
  if (fieldNode) {
1784
1974
  fields.push(fieldNode);
1785
1975
  }
@@ -1796,7 +1986,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
1796
1986
  staticMethods: []
1797
1987
  };
1798
1988
  }
1799
- function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
1989
+ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
1800
1990
  if (!ts4.isTypeLiteralNode(typeAlias.type)) {
1801
1991
  const sourceFile = typeAlias.getSourceFile();
1802
1992
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
@@ -1809,11 +1999,22 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
1809
1999
  const name = typeAlias.name.text;
1810
2000
  const fields = [];
1811
2001
  const typeRegistry = {};
1812
- const annotations = extractJSDocAnnotationNodes(typeAlias, file);
2002
+ const annotations = extractJSDocAnnotationNodes(
2003
+ typeAlias,
2004
+ file,
2005
+ makeParseOptions(extensionRegistry)
2006
+ );
1813
2007
  const visiting = /* @__PURE__ */ new Set();
1814
2008
  for (const member of typeAlias.type.members) {
1815
2009
  if (ts4.isPropertySignature(member)) {
1816
- const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
2010
+ const fieldNode = analyzeInterfacePropertyToIR(
2011
+ member,
2012
+ checker,
2013
+ file,
2014
+ typeRegistry,
2015
+ visiting,
2016
+ extensionRegistry
2017
+ );
1817
2018
  if (fieldNode) {
1818
2019
  fields.push(fieldNode);
1819
2020
  }
@@ -1832,7 +2033,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
1832
2033
  }
1833
2034
  };
1834
2035
  }
1835
- function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
2036
+ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
1836
2037
  if (!ts4.isIdentifier(prop.name)) {
1837
2038
  return null;
1838
2039
  }
@@ -1840,14 +2041,26 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
1840
2041
  const tsType = checker.getTypeAtLocation(prop);
1841
2042
  const optional = prop.questionToken !== void 0;
1842
2043
  const provenance = provenanceForNode(prop, file);
1843
- let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
2044
+ let type = resolveTypeNode(
2045
+ tsType,
2046
+ checker,
2047
+ file,
2048
+ typeRegistry,
2049
+ visiting,
2050
+ prop,
2051
+ extensionRegistry
2052
+ );
1844
2053
  const constraints = [];
1845
2054
  if (prop.type) {
1846
- constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
2055
+ constraints.push(
2056
+ ...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
2057
+ );
1847
2058
  }
1848
- constraints.push(...extractJSDocConstraintNodes(prop, file));
2059
+ constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
1849
2060
  let annotations = [];
1850
- annotations.push(...extractJSDocAnnotationNodes(prop, file));
2061
+ annotations.push(
2062
+ ...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
2063
+ );
1851
2064
  const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
1852
2065
  if (defaultAnnotation && !annotations.some((a) => a.annotationKind === "defaultValue")) {
1853
2066
  annotations.push(defaultAnnotation);
@@ -1863,7 +2076,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
1863
2076
  provenance
1864
2077
  };
1865
2078
  }
1866
- function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting) {
2079
+ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
1867
2080
  if (!ts4.isIdentifier(prop.name)) {
1868
2081
  return null;
1869
2082
  }
@@ -1871,14 +2084,26 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
1871
2084
  const tsType = checker.getTypeAtLocation(prop);
1872
2085
  const optional = prop.questionToken !== void 0;
1873
2086
  const provenance = provenanceForNode(prop, file);
1874
- let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
2087
+ let type = resolveTypeNode(
2088
+ tsType,
2089
+ checker,
2090
+ file,
2091
+ typeRegistry,
2092
+ visiting,
2093
+ prop,
2094
+ extensionRegistry
2095
+ );
1875
2096
  const constraints = [];
1876
2097
  if (prop.type) {
1877
- constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
2098
+ constraints.push(
2099
+ ...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
2100
+ );
1878
2101
  }
1879
- constraints.push(...extractJSDocConstraintNodes(prop, file));
2102
+ constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
1880
2103
  let annotations = [];
1881
- annotations.push(...extractJSDocAnnotationNodes(prop, file));
2104
+ annotations.push(
2105
+ ...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
2106
+ );
1882
2107
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
1883
2108
  return {
1884
2109
  kind: "field",
@@ -1952,7 +2177,66 @@ function parseEnumMemberDisplayName(value) {
1952
2177
  if (label === "") return null;
1953
2178
  return { value: match[1], label };
1954
2179
  }
1955
- function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode) {
2180
+ function resolveRegisteredCustomType(sourceNode, extensionRegistry, checker) {
2181
+ if (sourceNode === void 0 || extensionRegistry === void 0) {
2182
+ return null;
2183
+ }
2184
+ const typeNode = extractTypeNodeFromSource(sourceNode);
2185
+ if (typeNode === void 0) {
2186
+ return null;
2187
+ }
2188
+ return resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker);
2189
+ }
2190
+ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker) {
2191
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
2192
+ return resolveRegisteredCustomTypeFromTypeNode(typeNode.type, extensionRegistry, checker);
2193
+ }
2194
+ const typeName = getTypeNodeRegistrationName(typeNode);
2195
+ if (typeName === null) {
2196
+ return null;
2197
+ }
2198
+ const registration = extensionRegistry.findTypeByName(typeName);
2199
+ if (registration !== void 0) {
2200
+ return {
2201
+ kind: "custom",
2202
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
2203
+ payload: null
2204
+ };
2205
+ }
2206
+ if (ts4.isTypeReferenceNode(typeNode) && ts4.isIdentifier(typeNode.typeName)) {
2207
+ const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts4.isTypeAliasDeclaration);
2208
+ if (aliasDecl !== void 0) {
2209
+ return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
2210
+ }
2211
+ }
2212
+ return null;
2213
+ }
2214
+ function extractTypeNodeFromSource(sourceNode) {
2215
+ if (ts4.isPropertyDeclaration(sourceNode) || ts4.isPropertySignature(sourceNode) || ts4.isParameter(sourceNode) || ts4.isTypeAliasDeclaration(sourceNode)) {
2216
+ return sourceNode.type;
2217
+ }
2218
+ if (ts4.isTypeNode(sourceNode)) {
2219
+ return sourceNode;
2220
+ }
2221
+ return void 0;
2222
+ }
2223
+ function getTypeNodeRegistrationName(typeNode) {
2224
+ if (ts4.isTypeReferenceNode(typeNode)) {
2225
+ return ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
2226
+ }
2227
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
2228
+ return getTypeNodeRegistrationName(typeNode.type);
2229
+ }
2230
+ if (typeNode.kind === ts4.SyntaxKind.BigIntKeyword || typeNode.kind === ts4.SyntaxKind.StringKeyword || typeNode.kind === ts4.SyntaxKind.NumberKeyword || typeNode.kind === ts4.SyntaxKind.BooleanKeyword) {
2231
+ return typeNode.getText();
2232
+ }
2233
+ return null;
2234
+ }
2235
+ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
2236
+ const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
2237
+ if (customType) {
2238
+ return customType;
2239
+ }
1956
2240
  if (type.flags & ts4.TypeFlags.String) {
1957
2241
  return { kind: "primitive", primitiveKind: "string" };
1958
2242
  }
@@ -1981,26 +2265,50 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
1981
2265
  };
1982
2266
  }
1983
2267
  if (type.isUnion()) {
1984
- return resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode);
2268
+ return resolveUnionType(
2269
+ type,
2270
+ checker,
2271
+ file,
2272
+ typeRegistry,
2273
+ visiting,
2274
+ sourceNode,
2275
+ extensionRegistry
2276
+ );
1985
2277
  }
1986
2278
  if (checker.isArrayType(type)) {
1987
- return resolveArrayType(type, checker, file, typeRegistry, visiting);
2279
+ return resolveArrayType(
2280
+ type,
2281
+ checker,
2282
+ file,
2283
+ typeRegistry,
2284
+ visiting,
2285
+ sourceNode,
2286
+ extensionRegistry
2287
+ );
1988
2288
  }
1989
2289
  if (isObjectType(type)) {
1990
- return resolveObjectType(type, checker, file, typeRegistry, visiting);
2290
+ return resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry);
1991
2291
  }
1992
2292
  return { kind: "primitive", primitiveKind: "string" };
1993
2293
  }
1994
- function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode) {
2294
+ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
1995
2295
  const typeName = getNamedTypeName(type);
1996
2296
  const namedDecl = getNamedTypeDeclaration(type);
1997
2297
  if (typeName && typeName in typeRegistry) {
1998
2298
  return { kind: "reference", name: typeName, typeArguments: [] };
1999
2299
  }
2000
2300
  const allTypes = type.types;
2301
+ const unionMemberTypeNodes = extractUnionMemberTypeNodes(sourceNode, checker);
2302
+ const nonNullSourceNodes = unionMemberTypeNodes.filter(
2303
+ (memberTypeNode) => !isNullishTypeNode(resolveAliasedTypeNode(memberTypeNode, checker))
2304
+ );
2001
2305
  const nonNullTypes = allTypes.filter(
2002
- (t) => !(t.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
2306
+ (memberType) => !(memberType.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
2003
2307
  );
2308
+ const nonNullMembers = nonNullTypes.map((memberType, index) => ({
2309
+ memberType,
2310
+ sourceNode: nonNullSourceNodes.length === nonNullTypes.length ? nonNullSourceNodes[index] : void 0
2311
+ }));
2004
2312
  const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
2005
2313
  const memberDisplayNames = /* @__PURE__ */ new Map();
2006
2314
  if (namedDecl) {
@@ -2017,7 +2325,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2017
2325
  if (!typeName) {
2018
2326
  return result;
2019
2327
  }
2020
- const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
2328
+ const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2021
2329
  typeRegistry[typeName] = {
2022
2330
  name: typeName,
2023
2331
  type: result,
@@ -2065,14 +2373,15 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2065
2373
  } : enumNode;
2066
2374
  return registerNamed(result);
2067
2375
  }
2068
- if (nonNullTypes.length === 1 && nonNullTypes[0]) {
2376
+ if (nonNullMembers.length === 1 && nonNullMembers[0]) {
2069
2377
  const inner = resolveTypeNode(
2070
- nonNullTypes[0],
2378
+ nonNullMembers[0].memberType,
2071
2379
  checker,
2072
2380
  file,
2073
2381
  typeRegistry,
2074
2382
  visiting,
2075
- sourceNode
2383
+ nonNullMembers[0].sourceNode ?? sourceNode,
2384
+ extensionRegistry
2076
2385
  );
2077
2386
  const result = hasNull ? {
2078
2387
  kind: "union",
@@ -2080,21 +2389,38 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2080
2389
  } : inner;
2081
2390
  return registerNamed(result);
2082
2391
  }
2083
- const members = nonNullTypes.map(
2084
- (t) => resolveTypeNode(t, checker, file, typeRegistry, visiting, sourceNode)
2392
+ const members = nonNullMembers.map(
2393
+ ({ memberType, sourceNode: memberSourceNode }) => resolveTypeNode(
2394
+ memberType,
2395
+ checker,
2396
+ file,
2397
+ typeRegistry,
2398
+ visiting,
2399
+ memberSourceNode ?? sourceNode,
2400
+ extensionRegistry
2401
+ )
2085
2402
  );
2086
2403
  if (hasNull) {
2087
2404
  members.push({ kind: "primitive", primitiveKind: "null" });
2088
2405
  }
2089
2406
  return registerNamed({ kind: "union", members });
2090
2407
  }
2091
- function resolveArrayType(type, checker, file, typeRegistry, visiting) {
2408
+ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
2092
2409
  const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
2093
2410
  const elementType = typeArgs?.[0];
2094
- const items = elementType ? resolveTypeNode(elementType, checker, file, typeRegistry, visiting) : { kind: "primitive", primitiveKind: "string" };
2411
+ const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
2412
+ const items = elementType ? resolveTypeNode(
2413
+ elementType,
2414
+ checker,
2415
+ file,
2416
+ typeRegistry,
2417
+ visiting,
2418
+ elementSourceNode,
2419
+ extensionRegistry
2420
+ ) : { kind: "primitive", primitiveKind: "string" };
2095
2421
  return { kind: "array", items };
2096
2422
  }
2097
- function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
2423
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
2098
2424
  if (type.getProperties().length > 0) {
2099
2425
  return null;
2100
2426
  }
@@ -2102,7 +2428,15 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
2102
2428
  if (!indexInfo) {
2103
2429
  return null;
2104
2430
  }
2105
- const valueType = resolveTypeNode(indexInfo.type, checker, file, typeRegistry, visiting);
2431
+ const valueType = resolveTypeNode(
2432
+ indexInfo.type,
2433
+ checker,
2434
+ file,
2435
+ typeRegistry,
2436
+ visiting,
2437
+ void 0,
2438
+ extensionRegistry
2439
+ );
2106
2440
  return { kind: "record", valueType };
2107
2441
  }
2108
2442
  function typeNodeContainsReference(type, targetName) {
@@ -2130,7 +2464,7 @@ function typeNodeContainsReference(type, targetName) {
2130
2464
  }
2131
2465
  }
2132
2466
  }
2133
- function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2467
+ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
2134
2468
  const typeName = getNamedTypeName(type);
2135
2469
  const namedTypeName = typeName ?? void 0;
2136
2470
  const namedDecl = getNamedTypeDeclaration(type);
@@ -2161,7 +2495,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2161
2495
  return { kind: "reference", name: namedTypeName, typeArguments: [] };
2162
2496
  }
2163
2497
  }
2164
- const recordNode = tryResolveRecordType(type, checker, file, typeRegistry, visiting);
2498
+ const recordNode = tryResolveRecordType(
2499
+ type,
2500
+ checker,
2501
+ file,
2502
+ typeRegistry,
2503
+ visiting,
2504
+ extensionRegistry
2505
+ );
2165
2506
  if (recordNode) {
2166
2507
  visiting.delete(type);
2167
2508
  if (namedTypeName !== void 0 && shouldRegisterNamedType) {
@@ -2170,7 +2511,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2170
2511
  clearNamedTypeRegistration();
2171
2512
  return recordNode;
2172
2513
  }
2173
- const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
2514
+ const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2174
2515
  typeRegistry[namedTypeName] = {
2175
2516
  name: namedTypeName,
2176
2517
  type: recordNode,
@@ -2182,7 +2523,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2182
2523
  return recordNode;
2183
2524
  }
2184
2525
  const properties = [];
2185
- const fieldInfoMap = getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting);
2526
+ const fieldInfoMap = getNamedTypeFieldNodeInfoMap(
2527
+ type,
2528
+ checker,
2529
+ file,
2530
+ typeRegistry,
2531
+ visiting,
2532
+ extensionRegistry
2533
+ );
2186
2534
  for (const prop of type.getProperties()) {
2187
2535
  const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
2188
2536
  if (!declaration) continue;
@@ -2194,7 +2542,8 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2194
2542
  file,
2195
2543
  typeRegistry,
2196
2544
  visiting,
2197
- declaration
2545
+ declaration,
2546
+ extensionRegistry
2198
2547
  );
2199
2548
  const fieldNodeInfo = fieldInfoMap?.get(prop.name);
2200
2549
  properties.push({
@@ -2213,7 +2562,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2213
2562
  additionalProperties: true
2214
2563
  };
2215
2564
  if (namedTypeName !== void 0 && shouldRegisterNamedType) {
2216
- const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
2565
+ const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2217
2566
  typeRegistry[namedTypeName] = {
2218
2567
  name: namedTypeName,
2219
2568
  type: objectNode,
@@ -2224,7 +2573,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2224
2573
  }
2225
2574
  return objectNode;
2226
2575
  }
2227
- function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting) {
2576
+ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, extensionRegistry) {
2228
2577
  const symbols = [type.getSymbol(), type.aliasSymbol].filter(
2229
2578
  (s) => s?.declarations != null && s.declarations.length > 0
2230
2579
  );
@@ -2236,7 +2585,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2236
2585
  const map = /* @__PURE__ */ new Map();
2237
2586
  for (const member of classDecl.members) {
2238
2587
  if (ts4.isPropertyDeclaration(member) && ts4.isIdentifier(member.name)) {
2239
- const fieldNode = analyzeFieldToIR(member, checker, file, typeRegistry, visiting);
2588
+ const fieldNode = analyzeFieldToIR(
2589
+ member,
2590
+ checker,
2591
+ file,
2592
+ typeRegistry,
2593
+ visiting,
2594
+ extensionRegistry
2595
+ );
2240
2596
  if (fieldNode) {
2241
2597
  map.set(fieldNode.name, {
2242
2598
  constraints: [...fieldNode.constraints],
@@ -2250,7 +2606,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2250
2606
  }
2251
2607
  const interfaceDecl = declarations.find(ts4.isInterfaceDeclaration);
2252
2608
  if (interfaceDecl) {
2253
- return buildFieldNodeInfoMap(interfaceDecl.members, checker, file, typeRegistry, visiting);
2609
+ return buildFieldNodeInfoMap(
2610
+ interfaceDecl.members,
2611
+ checker,
2612
+ file,
2613
+ typeRegistry,
2614
+ visiting,
2615
+ extensionRegistry
2616
+ );
2254
2617
  }
2255
2618
  const typeAliasDecl = declarations.find(ts4.isTypeAliasDeclaration);
2256
2619
  if (typeAliasDecl && ts4.isTypeLiteralNode(typeAliasDecl.type)) {
@@ -2259,17 +2622,68 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2259
2622
  checker,
2260
2623
  file,
2261
2624
  typeRegistry,
2262
- visiting
2625
+ visiting,
2626
+ extensionRegistry
2263
2627
  );
2264
2628
  }
2265
2629
  }
2266
2630
  return null;
2267
2631
  }
2268
- function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
2632
+ function extractArrayElementTypeNode(sourceNode, checker) {
2633
+ const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
2634
+ if (typeNode === void 0) {
2635
+ return void 0;
2636
+ }
2637
+ const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
2638
+ if (ts4.isArrayTypeNode(resolvedTypeNode)) {
2639
+ return resolvedTypeNode.elementType;
2640
+ }
2641
+ if (ts4.isTypeReferenceNode(resolvedTypeNode) && ts4.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
2642
+ return resolvedTypeNode.typeArguments[0];
2643
+ }
2644
+ return void 0;
2645
+ }
2646
+ function extractUnionMemberTypeNodes(sourceNode, checker) {
2647
+ const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
2648
+ if (!typeNode) {
2649
+ return [];
2650
+ }
2651
+ const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
2652
+ return ts4.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
2653
+ }
2654
+ function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
2655
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
2656
+ return resolveAliasedTypeNode(typeNode.type, checker, visited);
2657
+ }
2658
+ if (!ts4.isTypeReferenceNode(typeNode) || !ts4.isIdentifier(typeNode.typeName)) {
2659
+ return typeNode;
2660
+ }
2661
+ const symbol = checker.getSymbolAtLocation(typeNode.typeName);
2662
+ const aliasDecl = symbol?.declarations?.find(ts4.isTypeAliasDeclaration);
2663
+ if (aliasDecl === void 0 || visited.has(aliasDecl)) {
2664
+ return typeNode;
2665
+ }
2666
+ visited.add(aliasDecl);
2667
+ return resolveAliasedTypeNode(aliasDecl.type, checker, visited);
2668
+ }
2669
+ function isNullishTypeNode(typeNode) {
2670
+ if (typeNode.kind === ts4.SyntaxKind.NullKeyword || typeNode.kind === ts4.SyntaxKind.UndefinedKeyword) {
2671
+ return true;
2672
+ }
2673
+ return ts4.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts4.SyntaxKind.NullKeyword || typeNode.literal.kind === ts4.SyntaxKind.UndefinedKeyword);
2674
+ }
2675
+ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, extensionRegistry) {
2269
2676
  const map = /* @__PURE__ */ new Map();
2270
2677
  for (const member of members) {
2271
2678
  if (ts4.isPropertySignature(member)) {
2272
- const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
2679
+ const fieldNode = analyzeInterfacePropertyToIR(
2680
+ member,
2681
+ checker,
2682
+ file,
2683
+ typeRegistry,
2684
+ visiting,
2685
+ extensionRegistry
2686
+ );
2273
2687
  if (fieldNode) {
2274
2688
  map.set(fieldNode.name, {
2275
2689
  constraints: [...fieldNode.constraints],
@@ -2282,7 +2696,7 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
2282
2696
  return map;
2283
2697
  }
2284
2698
  var MAX_ALIAS_CHAIN_DEPTH = 8;
2285
- function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
2699
+ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegistry, depth = 0) {
2286
2700
  if (!ts4.isTypeReferenceNode(typeNode)) return [];
2287
2701
  if (depth >= MAX_ALIAS_CHAIN_DEPTH) {
2288
2702
  const aliasName = typeNode.typeName.getText();
@@ -2295,8 +2709,29 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
2295
2709
  const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
2296
2710
  if (!aliasDecl) return [];
2297
2711
  if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
2298
- const constraints = extractJSDocConstraintNodes(aliasDecl, file);
2299
- constraints.push(...extractTypeAliasConstraintNodes(aliasDecl.type, checker, file, depth + 1));
2712
+ const aliasFieldType = resolveTypeNode(
2713
+ checker.getTypeAtLocation(aliasDecl.type),
2714
+ checker,
2715
+ file,
2716
+ {},
2717
+ /* @__PURE__ */ new Set(),
2718
+ aliasDecl.type,
2719
+ extensionRegistry
2720
+ );
2721
+ const constraints = extractJSDocConstraintNodes(
2722
+ aliasDecl,
2723
+ file,
2724
+ makeParseOptions(extensionRegistry, aliasFieldType)
2725
+ );
2726
+ constraints.push(
2727
+ ...extractTypeAliasConstraintNodes(
2728
+ aliasDecl.type,
2729
+ checker,
2730
+ file,
2731
+ extensionRegistry,
2732
+ depth + 1
2733
+ )
2734
+ );
2300
2735
  return constraints;
2301
2736
  }
2302
2737
  function provenanceForNode(node, file) {
@@ -2392,10 +2827,10 @@ function detectFormSpecReference(typeNode) {
2392
2827
  }
2393
2828
 
2394
2829
  // src/generators/class-schema.ts
2395
- function generateClassSchemas(analysis, source) {
2830
+ function generateClassSchemas(analysis, source, options) {
2396
2831
  const ir = canonicalizeTSDoc(analysis, source);
2397
2832
  return {
2398
- jsonSchema: generateJsonSchemaFromIR(ir),
2833
+ jsonSchema: generateJsonSchemaFromIR(ir, options),
2399
2834
  uiSchema: generateUiSchemaFromIR(ir)
2400
2835
  };
2401
2836
  }
@@ -2405,27 +2840,54 @@ function generateSchemasFromClass(options) {
2405
2840
  if (!classDecl) {
2406
2841
  throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
2407
2842
  }
2408
- const analysis = analyzeClassToIR(classDecl, ctx.checker, options.filePath);
2409
- return generateClassSchemas(analysis, { file: options.filePath });
2843
+ const analysis = analyzeClassToIR(
2844
+ classDecl,
2845
+ ctx.checker,
2846
+ options.filePath,
2847
+ options.extensionRegistry
2848
+ );
2849
+ return generateClassSchemas(
2850
+ analysis,
2851
+ { file: options.filePath },
2852
+ {
2853
+ extensionRegistry: options.extensionRegistry,
2854
+ vendorPrefix: options.vendorPrefix
2855
+ }
2856
+ );
2410
2857
  }
2411
2858
  function generateSchemas(options) {
2412
2859
  const ctx = createProgramContext(options.filePath);
2413
2860
  const source = { file: options.filePath };
2414
2861
  const classDecl = findClassByName(ctx.sourceFile, options.typeName);
2415
2862
  if (classDecl) {
2416
- const analysis = analyzeClassToIR(classDecl, ctx.checker, options.filePath);
2417
- return generateClassSchemas(analysis, source);
2863
+ const analysis = analyzeClassToIR(
2864
+ classDecl,
2865
+ ctx.checker,
2866
+ options.filePath,
2867
+ options.extensionRegistry
2868
+ );
2869
+ return generateClassSchemas(analysis, source, options);
2418
2870
  }
2419
2871
  const interfaceDecl = findInterfaceByName(ctx.sourceFile, options.typeName);
2420
2872
  if (interfaceDecl) {
2421
- const analysis = analyzeInterfaceToIR(interfaceDecl, ctx.checker, options.filePath);
2422
- return generateClassSchemas(analysis, source);
2873
+ const analysis = analyzeInterfaceToIR(
2874
+ interfaceDecl,
2875
+ ctx.checker,
2876
+ options.filePath,
2877
+ options.extensionRegistry
2878
+ );
2879
+ return generateClassSchemas(analysis, source, options);
2423
2880
  }
2424
2881
  const typeAlias = findTypeAliasByName(ctx.sourceFile, options.typeName);
2425
2882
  if (typeAlias) {
2426
- const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, options.filePath);
2883
+ const result = analyzeTypeAliasToIR(
2884
+ typeAlias,
2885
+ ctx.checker,
2886
+ options.filePath,
2887
+ options.extensionRegistry
2888
+ );
2427
2889
  if (result.ok) {
2428
- return generateClassSchemas(result.analysis, source);
2890
+ return generateClassSchemas(result.analysis, source, options);
2429
2891
  }
2430
2892
  throw new Error(result.error);
2431
2893
  }
@@ -2437,7 +2899,7 @@ function generateSchemas(options) {
2437
2899
  // src/generators/mixed-authoring.ts
2438
2900
  function buildMixedAuthoringSchemas(options) {
2439
2901
  const { filePath, typeName, overlays, ...schemaOptions } = options;
2440
- const analysis = analyzeNamedType(filePath, typeName);
2902
+ const analysis = analyzeNamedType(filePath, typeName, schemaOptions.extensionRegistry);
2441
2903
  const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays);
2442
2904
  const ir = canonicalizeTSDoc(composedAnalysis, { file: filePath });
2443
2905
  return {
@@ -2445,20 +2907,20 @@ function buildMixedAuthoringSchemas(options) {
2445
2907
  uiSchema: generateUiSchemaFromIR(ir)
2446
2908
  };
2447
2909
  }
2448
- function analyzeNamedType(filePath, typeName) {
2910
+ function analyzeNamedType(filePath, typeName, extensionRegistry) {
2449
2911
  const ctx = createProgramContext(filePath);
2450
2912
  const source = { file: filePath };
2451
2913
  const classDecl = findClassByName(ctx.sourceFile, typeName);
2452
2914
  if (classDecl !== null) {
2453
- return analyzeClassToIR(classDecl, ctx.checker, source.file);
2915
+ return analyzeClassToIR(classDecl, ctx.checker, source.file, extensionRegistry);
2454
2916
  }
2455
2917
  const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
2456
2918
  if (interfaceDecl !== null) {
2457
- return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file);
2919
+ return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file, extensionRegistry);
2458
2920
  }
2459
2921
  const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
2460
2922
  if (typeAlias !== null) {
2461
- const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file);
2923
+ const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file, extensionRegistry);
2462
2924
  if (result.ok) {
2463
2925
  return result.analysis;
2464
2926
  }
@@ -2537,7 +2999,7 @@ function assertSupportedOverlayField(baseField, overlayField) {
2537
2999
  `Mixed-authoring overlay for "${baseField.name}" cannot define constraints; keep constraints on the static model`
2538
3000
  );
2539
3001
  }
2540
- if (overlayField.required) {
3002
+ if (overlayField.required && !baseField.required) {
2541
3003
  throw new Error(
2542
3004
  `Mixed-authoring overlay for "${baseField.name}" cannot change requiredness; keep requiredness on the static model`
2543
3005
  );
@@ -2627,7 +3089,7 @@ function mergeAnnotations(baseAnnotations, overlayAnnotations) {
2627
3089
  const overlayOnly = overlayAnnotations.filter(
2628
3090
  (annotation) => !baseKeys.has(annotationKey(annotation))
2629
3091
  );
2630
- return [...overlayOnly, ...baseAnnotations];
3092
+ return [...baseAnnotations, ...overlayOnly];
2631
3093
  }
2632
3094
  function annotationKey(annotation) {
2633
3095
  return annotation.annotationKind === "custom" ? `${annotation.annotationKind}:${annotation.annotationId}` : annotation.annotationKind;