@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/cli.cjs CHANGED
@@ -829,7 +829,12 @@ function applyCustomConstraint(schema, constraint, ctx) {
829
829
  `Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
830
830
  );
831
831
  }
832
- Object.assign(schema, registration.toJsonSchema(constraint.payload, ctx.vendorPrefix));
832
+ assignVendorPrefixedExtensionKeywords(
833
+ schema,
834
+ registration.toJsonSchema(constraint.payload, ctx.vendorPrefix),
835
+ ctx.vendorPrefix,
836
+ `custom constraint "${constraint.constraintId}"`
837
+ );
833
838
  }
834
839
  function applyCustomAnnotation(schema, annotation, ctx) {
835
840
  const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
@@ -841,7 +846,22 @@ function applyCustomAnnotation(schema, annotation, ctx) {
841
846
  if (registration.toJsonSchema === void 0) {
842
847
  return;
843
848
  }
844
- Object.assign(schema, registration.toJsonSchema(annotation.value, ctx.vendorPrefix));
849
+ assignVendorPrefixedExtensionKeywords(
850
+ schema,
851
+ registration.toJsonSchema(annotation.value, ctx.vendorPrefix),
852
+ ctx.vendorPrefix,
853
+ `custom annotation "${annotation.annotationId}"`
854
+ );
855
+ }
856
+ function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPrefix, source) {
857
+ for (const [key, value] of Object.entries(extensionSchema)) {
858
+ if (!key.startsWith(`${vendorPrefix}-`)) {
859
+ throw new Error(
860
+ `Cannot apply ${source}: extension hooks may only emit "${vendorPrefix}-*" JSON Schema keywords`
861
+ );
862
+ }
863
+ schema[key] = value;
864
+ }
845
865
  }
846
866
  var init_ir_generator = __esm({
847
867
  "src/json-schema/ir-generator.ts"() {
@@ -1127,7 +1147,10 @@ var init_types = __esm({
1127
1147
  // src/extensions/registry.ts
1128
1148
  function createExtensionRegistry(extensions) {
1129
1149
  const typeMap = /* @__PURE__ */ new Map();
1150
+ const typeNameMap = /* @__PURE__ */ new Map();
1130
1151
  const constraintMap = /* @__PURE__ */ new Map();
1152
+ const constraintTagMap = /* @__PURE__ */ new Map();
1153
+ const builtinBroadeningMap = /* @__PURE__ */ new Map();
1131
1154
  const annotationMap = /* @__PURE__ */ new Map();
1132
1155
  for (const ext of extensions) {
1133
1156
  if (ext.types !== void 0) {
@@ -1137,6 +1160,27 @@ function createExtensionRegistry(extensions) {
1137
1160
  throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
1138
1161
  }
1139
1162
  typeMap.set(qualifiedId, type);
1163
+ for (const sourceTypeName of type.tsTypeNames ?? [type.typeName]) {
1164
+ if (typeNameMap.has(sourceTypeName)) {
1165
+ throw new Error(`Duplicate custom type source name: "${sourceTypeName}"`);
1166
+ }
1167
+ typeNameMap.set(sourceTypeName, {
1168
+ extensionId: ext.extensionId,
1169
+ registration: type
1170
+ });
1171
+ }
1172
+ if (type.builtinConstraintBroadenings !== void 0) {
1173
+ for (const broadening of type.builtinConstraintBroadenings) {
1174
+ const key = `${qualifiedId}:${broadening.tagName}`;
1175
+ if (builtinBroadeningMap.has(key)) {
1176
+ throw new Error(`Duplicate built-in constraint broadening: "${key}"`);
1177
+ }
1178
+ builtinBroadeningMap.set(key, {
1179
+ extensionId: ext.extensionId,
1180
+ registration: broadening
1181
+ });
1182
+ }
1183
+ }
1140
1184
  }
1141
1185
  }
1142
1186
  if (ext.constraints !== void 0) {
@@ -1148,6 +1192,17 @@ function createExtensionRegistry(extensions) {
1148
1192
  constraintMap.set(qualifiedId, constraint);
1149
1193
  }
1150
1194
  }
1195
+ if (ext.constraintTags !== void 0) {
1196
+ for (const tag of ext.constraintTags) {
1197
+ if (constraintTagMap.has(tag.tagName)) {
1198
+ throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
1199
+ }
1200
+ constraintTagMap.set(tag.tagName, {
1201
+ extensionId: ext.extensionId,
1202
+ registration: tag
1203
+ });
1204
+ }
1205
+ }
1151
1206
  if (ext.annotations !== void 0) {
1152
1207
  for (const annotation of ext.annotations) {
1153
1208
  const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
@@ -1161,7 +1216,10 @@ function createExtensionRegistry(extensions) {
1161
1216
  return {
1162
1217
  extensions,
1163
1218
  findType: (typeId) => typeMap.get(typeId),
1219
+ findTypeByName: (typeName) => typeNameMap.get(typeName),
1164
1220
  findConstraint: (constraintId) => constraintMap.get(constraintId),
1221
+ findConstraintTag: (tagName) => constraintTagMap.get(tagName),
1222
+ findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
1165
1223
  findAnnotation: (annotationId) => annotationMap.get(annotationId)
1166
1224
  };
1167
1225
  }
@@ -1340,7 +1398,7 @@ var init_json_utils = __esm({
1340
1398
  });
1341
1399
 
1342
1400
  // src/analyzer/tsdoc-parser.ts
1343
- function createFormSpecTSDocConfig() {
1401
+ function createFormSpecTSDocConfig(extensionTagNames = []) {
1344
1402
  const config = new import_tsdoc.TSDocConfiguration();
1345
1403
  for (const tagName of Object.keys(import_core3.BUILTIN_CONSTRAINT_DEFINITIONS)) {
1346
1404
  config.addTagDefinition(
@@ -1360,13 +1418,33 @@ function createFormSpecTSDocConfig() {
1360
1418
  })
1361
1419
  );
1362
1420
  }
1421
+ for (const tagName of extensionTagNames) {
1422
+ config.addTagDefinition(
1423
+ new import_tsdoc.TSDocTagDefinition({
1424
+ tagName: "@" + tagName,
1425
+ syntaxKind: import_tsdoc.TSDocTagSyntaxKind.BlockTag,
1426
+ allowMultiple: true
1427
+ })
1428
+ );
1429
+ }
1363
1430
  return config;
1364
1431
  }
1365
- function getParser() {
1366
- sharedParser ??= new import_tsdoc.TSDocParser(createFormSpecTSDocConfig());
1367
- return sharedParser;
1368
- }
1369
- function parseTSDocTags(node, file = "") {
1432
+ function getParser(options) {
1433
+ const extensionTagNames = [
1434
+ ...options?.extensionRegistry?.extensions.flatMap(
1435
+ (extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
1436
+ ) ?? []
1437
+ ].sort();
1438
+ const cacheKey = extensionTagNames.join("|");
1439
+ const existing = parserCache.get(cacheKey);
1440
+ if (existing) {
1441
+ return existing;
1442
+ }
1443
+ const parser = new import_tsdoc.TSDocParser(createFormSpecTSDocConfig(extensionTagNames));
1444
+ parserCache.set(cacheKey, parser);
1445
+ return parser;
1446
+ }
1447
+ function parseTSDocTags(node, file = "", options) {
1370
1448
  const constraints = [];
1371
1449
  const annotations = [];
1372
1450
  let displayName;
@@ -1387,7 +1465,7 @@ function parseTSDocTags(node, file = "") {
1387
1465
  if (!commentText.startsWith("/**")) {
1388
1466
  continue;
1389
1467
  }
1390
- const parser = getParser();
1468
+ const parser = getParser(options);
1391
1469
  const parserContext = parser.parseRange(
1392
1470
  import_tsdoc.TextRange.fromStringRange(sourceText, range.pos, range.end)
1393
1471
  );
@@ -1426,7 +1504,7 @@ function parseTSDocTags(node, file = "") {
1426
1504
  const expectedType = (0, import_core3.isBuiltinConstraintName)(tagName) ? import_core3.BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
1427
1505
  if (text === "" && expectedType !== "boolean") continue;
1428
1506
  const provenance = provenanceForComment(range, sourceFile, file, tagName);
1429
- const constraintNode = parseConstraintValue(tagName, text, provenance);
1507
+ const constraintNode = parseConstraintValue(tagName, text, provenance, options);
1430
1508
  if (constraintNode) {
1431
1509
  constraints.push(constraintNode);
1432
1510
  }
@@ -1486,7 +1564,7 @@ function parseTSDocTags(node, file = "") {
1486
1564
  annotations.push(defaultValueNode);
1487
1565
  continue;
1488
1566
  }
1489
- const constraintNode = parseConstraintValue(tagName, text, provenance);
1567
+ const constraintNode = parseConstraintValue(tagName, text, provenance, options);
1490
1568
  if (constraintNode) {
1491
1569
  constraints.push(constraintNode);
1492
1570
  }
@@ -1542,7 +1620,11 @@ function extractPlainText(node) {
1542
1620
  }
1543
1621
  return result;
1544
1622
  }
1545
- function parseConstraintValue(tagName, text, provenance) {
1623
+ function parseConstraintValue(tagName, text, provenance, options) {
1624
+ const customConstraint = parseExtensionConstraintValue(tagName, text, provenance, options);
1625
+ if (customConstraint) {
1626
+ return customConstraint;
1627
+ }
1546
1628
  if (!(0, import_core3.isBuiltinConstraintName)(tagName)) {
1547
1629
  return null;
1548
1630
  }
@@ -1647,6 +1729,83 @@ function parseConstraintValue(tagName, text, provenance) {
1647
1729
  provenance
1648
1730
  };
1649
1731
  }
1732
+ function parseExtensionConstraintValue(tagName, text, provenance, options) {
1733
+ const pathResult = extractPathTarget(text);
1734
+ const effectiveText = pathResult ? pathResult.remainingText : text;
1735
+ const path4 = pathResult?.path;
1736
+ const registry = options?.extensionRegistry;
1737
+ if (registry === void 0) {
1738
+ return null;
1739
+ }
1740
+ const directTag = registry.findConstraintTag(tagName);
1741
+ if (directTag !== void 0) {
1742
+ return makeCustomConstraintNode(
1743
+ directTag.extensionId,
1744
+ directTag.registration.constraintName,
1745
+ directTag.registration.parseValue(effectiveText),
1746
+ provenance,
1747
+ path4,
1748
+ registry
1749
+ );
1750
+ }
1751
+ if (!(0, import_core3.isBuiltinConstraintName)(tagName)) {
1752
+ return null;
1753
+ }
1754
+ const broadenedTypeId = getBroadenedCustomTypeId(options?.fieldType);
1755
+ if (broadenedTypeId === void 0) {
1756
+ return null;
1757
+ }
1758
+ const broadened = registry.findBuiltinConstraintBroadening(broadenedTypeId, tagName);
1759
+ if (broadened === void 0) {
1760
+ return null;
1761
+ }
1762
+ return makeCustomConstraintNode(
1763
+ broadened.extensionId,
1764
+ broadened.registration.constraintName,
1765
+ broadened.registration.parseValue(effectiveText),
1766
+ provenance,
1767
+ path4,
1768
+ registry
1769
+ );
1770
+ }
1771
+ function getBroadenedCustomTypeId(fieldType) {
1772
+ if (fieldType?.kind === "custom") {
1773
+ return fieldType.typeId;
1774
+ }
1775
+ if (fieldType?.kind !== "union") {
1776
+ return void 0;
1777
+ }
1778
+ const customMembers = fieldType.members.filter(
1779
+ (member) => member.kind === "custom"
1780
+ );
1781
+ if (customMembers.length !== 1) {
1782
+ return void 0;
1783
+ }
1784
+ const nonCustomMembers = fieldType.members.filter((member) => member.kind !== "custom");
1785
+ const allOtherMembersAreNull = nonCustomMembers.every(
1786
+ (member) => member.kind === "primitive" && member.primitiveKind === "null"
1787
+ );
1788
+ const customMember = customMembers[0];
1789
+ return allOtherMembersAreNull && customMember !== void 0 ? customMember.typeId : void 0;
1790
+ }
1791
+ function makeCustomConstraintNode(extensionId, constraintName, payload, provenance, path4, registry) {
1792
+ const constraintId = `${extensionId}/${constraintName}`;
1793
+ const registration = registry.findConstraint(constraintId);
1794
+ if (registration === void 0) {
1795
+ throw new Error(
1796
+ `Custom TSDoc tag resolved to unregistered constraint "${constraintId}". Register the constraint before using its tag.`
1797
+ );
1798
+ }
1799
+ return {
1800
+ kind: "constraint",
1801
+ constraintKind: "custom",
1802
+ constraintId,
1803
+ payload,
1804
+ compositionRule: registration.compositionRule,
1805
+ ...path4 && { path: path4 },
1806
+ provenance
1807
+ };
1808
+ }
1650
1809
  function parseDefaultValueValue(text, provenance) {
1651
1810
  const trimmed = text.trim();
1652
1811
  let value;
@@ -1705,7 +1864,7 @@ function getTagCommentText(tag) {
1705
1864
  }
1706
1865
  return ts2.getTextOfJSDocComment(tag.comment);
1707
1866
  }
1708
- var ts2, import_tsdoc, import_core3, NUMERIC_CONSTRAINT_MAP, LENGTH_CONSTRAINT_MAP, TAGS_REQUIRING_RAW_TEXT, sharedParser;
1867
+ var ts2, import_tsdoc, import_core3, NUMERIC_CONSTRAINT_MAP, LENGTH_CONSTRAINT_MAP, TAGS_REQUIRING_RAW_TEXT, parserCache;
1709
1868
  var init_tsdoc_parser = __esm({
1710
1869
  "src/analyzer/tsdoc-parser.ts"() {
1711
1870
  "use strict";
@@ -1727,16 +1886,17 @@ var init_tsdoc_parser = __esm({
1727
1886
  maxItems: "maxItems"
1728
1887
  };
1729
1888
  TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions", "defaultValue"]);
1889
+ parserCache = /* @__PURE__ */ new Map();
1730
1890
  }
1731
1891
  });
1732
1892
 
1733
1893
  // src/analyzer/jsdoc-constraints.ts
1734
- function extractJSDocConstraintNodes(node, file = "") {
1735
- const result = parseTSDocTags(node, file);
1894
+ function extractJSDocConstraintNodes(node, file = "", options) {
1895
+ const result = parseTSDocTags(node, file, options);
1736
1896
  return [...result.constraints];
1737
1897
  }
1738
- function extractJSDocAnnotationNodes(node, file = "") {
1739
- const result = parseTSDocTags(node, file);
1898
+ function extractJSDocAnnotationNodes(node, file = "", options) {
1899
+ const result = parseTSDocTags(node, file, options);
1740
1900
  return [...result.annotations];
1741
1901
  }
1742
1902
  function extractDefaultValueAnnotation(initializer, file = "") {
@@ -1788,18 +1948,38 @@ function isObjectType(type) {
1788
1948
  function isTypeReference(type) {
1789
1949
  return !!(type.flags & ts4.TypeFlags.Object) && !!(type.objectFlags & ts4.ObjectFlags.Reference);
1790
1950
  }
1791
- function analyzeClassToIR(classDecl, checker, file = "") {
1951
+ function makeParseOptions(extensionRegistry, fieldType) {
1952
+ if (extensionRegistry === void 0 && fieldType === void 0) {
1953
+ return void 0;
1954
+ }
1955
+ return {
1956
+ ...extensionRegistry !== void 0 && { extensionRegistry },
1957
+ ...fieldType !== void 0 && { fieldType }
1958
+ };
1959
+ }
1960
+ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
1792
1961
  const name = classDecl.name?.text ?? "AnonymousClass";
1793
1962
  const fields = [];
1794
1963
  const fieldLayouts = [];
1795
1964
  const typeRegistry = {};
1796
- const annotations = extractJSDocAnnotationNodes(classDecl, file);
1965
+ const annotations = extractJSDocAnnotationNodes(
1966
+ classDecl,
1967
+ file,
1968
+ makeParseOptions(extensionRegistry)
1969
+ );
1797
1970
  const visiting = /* @__PURE__ */ new Set();
1798
1971
  const instanceMethods = [];
1799
1972
  const staticMethods = [];
1800
1973
  for (const member of classDecl.members) {
1801
1974
  if (ts4.isPropertyDeclaration(member)) {
1802
- const fieldNode = analyzeFieldToIR(member, checker, file, typeRegistry, visiting);
1975
+ const fieldNode = analyzeFieldToIR(
1976
+ member,
1977
+ checker,
1978
+ file,
1979
+ typeRegistry,
1980
+ visiting,
1981
+ extensionRegistry
1982
+ );
1803
1983
  if (fieldNode) {
1804
1984
  fields.push(fieldNode);
1805
1985
  fieldLayouts.push({});
@@ -1826,15 +2006,26 @@ function analyzeClassToIR(classDecl, checker, file = "") {
1826
2006
  staticMethods
1827
2007
  };
1828
2008
  }
1829
- function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
2009
+ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry) {
1830
2010
  const name = interfaceDecl.name.text;
1831
2011
  const fields = [];
1832
2012
  const typeRegistry = {};
1833
- const annotations = extractJSDocAnnotationNodes(interfaceDecl, file);
2013
+ const annotations = extractJSDocAnnotationNodes(
2014
+ interfaceDecl,
2015
+ file,
2016
+ makeParseOptions(extensionRegistry)
2017
+ );
1834
2018
  const visiting = /* @__PURE__ */ new Set();
1835
2019
  for (const member of interfaceDecl.members) {
1836
2020
  if (ts4.isPropertySignature(member)) {
1837
- const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
2021
+ const fieldNode = analyzeInterfacePropertyToIR(
2022
+ member,
2023
+ checker,
2024
+ file,
2025
+ typeRegistry,
2026
+ visiting,
2027
+ extensionRegistry
2028
+ );
1838
2029
  if (fieldNode) {
1839
2030
  fields.push(fieldNode);
1840
2031
  }
@@ -1851,7 +2042,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
1851
2042
  staticMethods: []
1852
2043
  };
1853
2044
  }
1854
- function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
2045
+ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
1855
2046
  if (!ts4.isTypeLiteralNode(typeAlias.type)) {
1856
2047
  const sourceFile = typeAlias.getSourceFile();
1857
2048
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
@@ -1864,11 +2055,22 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
1864
2055
  const name = typeAlias.name.text;
1865
2056
  const fields = [];
1866
2057
  const typeRegistry = {};
1867
- const annotations = extractJSDocAnnotationNodes(typeAlias, file);
2058
+ const annotations = extractJSDocAnnotationNodes(
2059
+ typeAlias,
2060
+ file,
2061
+ makeParseOptions(extensionRegistry)
2062
+ );
1868
2063
  const visiting = /* @__PURE__ */ new Set();
1869
2064
  for (const member of typeAlias.type.members) {
1870
2065
  if (ts4.isPropertySignature(member)) {
1871
- const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
2066
+ const fieldNode = analyzeInterfacePropertyToIR(
2067
+ member,
2068
+ checker,
2069
+ file,
2070
+ typeRegistry,
2071
+ visiting,
2072
+ extensionRegistry
2073
+ );
1872
2074
  if (fieldNode) {
1873
2075
  fields.push(fieldNode);
1874
2076
  }
@@ -1887,7 +2089,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
1887
2089
  }
1888
2090
  };
1889
2091
  }
1890
- function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
2092
+ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
1891
2093
  if (!ts4.isIdentifier(prop.name)) {
1892
2094
  return null;
1893
2095
  }
@@ -1895,14 +2097,26 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
1895
2097
  const tsType = checker.getTypeAtLocation(prop);
1896
2098
  const optional = prop.questionToken !== void 0;
1897
2099
  const provenance = provenanceForNode(prop, file);
1898
- let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
2100
+ let type = resolveTypeNode(
2101
+ tsType,
2102
+ checker,
2103
+ file,
2104
+ typeRegistry,
2105
+ visiting,
2106
+ prop,
2107
+ extensionRegistry
2108
+ );
1899
2109
  const constraints = [];
1900
2110
  if (prop.type) {
1901
- constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
2111
+ constraints.push(
2112
+ ...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
2113
+ );
1902
2114
  }
1903
- constraints.push(...extractJSDocConstraintNodes(prop, file));
2115
+ constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
1904
2116
  let annotations = [];
1905
- annotations.push(...extractJSDocAnnotationNodes(prop, file));
2117
+ annotations.push(
2118
+ ...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
2119
+ );
1906
2120
  const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
1907
2121
  if (defaultAnnotation && !annotations.some((a) => a.annotationKind === "defaultValue")) {
1908
2122
  annotations.push(defaultAnnotation);
@@ -1918,7 +2132,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
1918
2132
  provenance
1919
2133
  };
1920
2134
  }
1921
- function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting) {
2135
+ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
1922
2136
  if (!ts4.isIdentifier(prop.name)) {
1923
2137
  return null;
1924
2138
  }
@@ -1926,14 +2140,26 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
1926
2140
  const tsType = checker.getTypeAtLocation(prop);
1927
2141
  const optional = prop.questionToken !== void 0;
1928
2142
  const provenance = provenanceForNode(prop, file);
1929
- let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
2143
+ let type = resolveTypeNode(
2144
+ tsType,
2145
+ checker,
2146
+ file,
2147
+ typeRegistry,
2148
+ visiting,
2149
+ prop,
2150
+ extensionRegistry
2151
+ );
1930
2152
  const constraints = [];
1931
2153
  if (prop.type) {
1932
- constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
2154
+ constraints.push(
2155
+ ...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
2156
+ );
1933
2157
  }
1934
- constraints.push(...extractJSDocConstraintNodes(prop, file));
2158
+ constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
1935
2159
  let annotations = [];
1936
- annotations.push(...extractJSDocAnnotationNodes(prop, file));
2160
+ annotations.push(
2161
+ ...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
2162
+ );
1937
2163
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
1938
2164
  return {
1939
2165
  kind: "field",
@@ -2007,7 +2233,66 @@ function parseEnumMemberDisplayName(value) {
2007
2233
  if (label === "") return null;
2008
2234
  return { value: match[1], label };
2009
2235
  }
2010
- function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode) {
2236
+ function resolveRegisteredCustomType(sourceNode, extensionRegistry, checker) {
2237
+ if (sourceNode === void 0 || extensionRegistry === void 0) {
2238
+ return null;
2239
+ }
2240
+ const typeNode = extractTypeNodeFromSource(sourceNode);
2241
+ if (typeNode === void 0) {
2242
+ return null;
2243
+ }
2244
+ return resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker);
2245
+ }
2246
+ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker) {
2247
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
2248
+ return resolveRegisteredCustomTypeFromTypeNode(typeNode.type, extensionRegistry, checker);
2249
+ }
2250
+ const typeName = getTypeNodeRegistrationName(typeNode);
2251
+ if (typeName === null) {
2252
+ return null;
2253
+ }
2254
+ const registration = extensionRegistry.findTypeByName(typeName);
2255
+ if (registration !== void 0) {
2256
+ return {
2257
+ kind: "custom",
2258
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
2259
+ payload: null
2260
+ };
2261
+ }
2262
+ if (ts4.isTypeReferenceNode(typeNode) && ts4.isIdentifier(typeNode.typeName)) {
2263
+ const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts4.isTypeAliasDeclaration);
2264
+ if (aliasDecl !== void 0) {
2265
+ return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
2266
+ }
2267
+ }
2268
+ return null;
2269
+ }
2270
+ function extractTypeNodeFromSource(sourceNode) {
2271
+ if (ts4.isPropertyDeclaration(sourceNode) || ts4.isPropertySignature(sourceNode) || ts4.isParameter(sourceNode) || ts4.isTypeAliasDeclaration(sourceNode)) {
2272
+ return sourceNode.type;
2273
+ }
2274
+ if (ts4.isTypeNode(sourceNode)) {
2275
+ return sourceNode;
2276
+ }
2277
+ return void 0;
2278
+ }
2279
+ function getTypeNodeRegistrationName(typeNode) {
2280
+ if (ts4.isTypeReferenceNode(typeNode)) {
2281
+ return ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
2282
+ }
2283
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
2284
+ return getTypeNodeRegistrationName(typeNode.type);
2285
+ }
2286
+ if (typeNode.kind === ts4.SyntaxKind.BigIntKeyword || typeNode.kind === ts4.SyntaxKind.StringKeyword || typeNode.kind === ts4.SyntaxKind.NumberKeyword || typeNode.kind === ts4.SyntaxKind.BooleanKeyword) {
2287
+ return typeNode.getText();
2288
+ }
2289
+ return null;
2290
+ }
2291
+ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
2292
+ const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
2293
+ if (customType) {
2294
+ return customType;
2295
+ }
2011
2296
  if (type.flags & ts4.TypeFlags.String) {
2012
2297
  return { kind: "primitive", primitiveKind: "string" };
2013
2298
  }
@@ -2036,26 +2321,50 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
2036
2321
  };
2037
2322
  }
2038
2323
  if (type.isUnion()) {
2039
- return resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode);
2324
+ return resolveUnionType(
2325
+ type,
2326
+ checker,
2327
+ file,
2328
+ typeRegistry,
2329
+ visiting,
2330
+ sourceNode,
2331
+ extensionRegistry
2332
+ );
2040
2333
  }
2041
2334
  if (checker.isArrayType(type)) {
2042
- return resolveArrayType(type, checker, file, typeRegistry, visiting);
2335
+ return resolveArrayType(
2336
+ type,
2337
+ checker,
2338
+ file,
2339
+ typeRegistry,
2340
+ visiting,
2341
+ sourceNode,
2342
+ extensionRegistry
2343
+ );
2043
2344
  }
2044
2345
  if (isObjectType(type)) {
2045
- return resolveObjectType(type, checker, file, typeRegistry, visiting);
2346
+ return resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry);
2046
2347
  }
2047
2348
  return { kind: "primitive", primitiveKind: "string" };
2048
2349
  }
2049
- function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode) {
2350
+ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
2050
2351
  const typeName = getNamedTypeName(type);
2051
2352
  const namedDecl = getNamedTypeDeclaration(type);
2052
2353
  if (typeName && typeName in typeRegistry) {
2053
2354
  return { kind: "reference", name: typeName, typeArguments: [] };
2054
2355
  }
2055
2356
  const allTypes = type.types;
2357
+ const unionMemberTypeNodes = extractUnionMemberTypeNodes(sourceNode, checker);
2358
+ const nonNullSourceNodes = unionMemberTypeNodes.filter(
2359
+ (memberTypeNode) => !isNullishTypeNode(resolveAliasedTypeNode(memberTypeNode, checker))
2360
+ );
2056
2361
  const nonNullTypes = allTypes.filter(
2057
- (t) => !(t.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
2362
+ (memberType) => !(memberType.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
2058
2363
  );
2364
+ const nonNullMembers = nonNullTypes.map((memberType, index) => ({
2365
+ memberType,
2366
+ sourceNode: nonNullSourceNodes.length === nonNullTypes.length ? nonNullSourceNodes[index] : void 0
2367
+ }));
2059
2368
  const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
2060
2369
  const memberDisplayNames = /* @__PURE__ */ new Map();
2061
2370
  if (namedDecl) {
@@ -2072,7 +2381,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2072
2381
  if (!typeName) {
2073
2382
  return result;
2074
2383
  }
2075
- const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
2384
+ const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2076
2385
  typeRegistry[typeName] = {
2077
2386
  name: typeName,
2078
2387
  type: result,
@@ -2120,14 +2429,15 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2120
2429
  } : enumNode;
2121
2430
  return registerNamed(result);
2122
2431
  }
2123
- if (nonNullTypes.length === 1 && nonNullTypes[0]) {
2432
+ if (nonNullMembers.length === 1 && nonNullMembers[0]) {
2124
2433
  const inner = resolveTypeNode(
2125
- nonNullTypes[0],
2434
+ nonNullMembers[0].memberType,
2126
2435
  checker,
2127
2436
  file,
2128
2437
  typeRegistry,
2129
2438
  visiting,
2130
- sourceNode
2439
+ nonNullMembers[0].sourceNode ?? sourceNode,
2440
+ extensionRegistry
2131
2441
  );
2132
2442
  const result = hasNull ? {
2133
2443
  kind: "union",
@@ -2135,21 +2445,38 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2135
2445
  } : inner;
2136
2446
  return registerNamed(result);
2137
2447
  }
2138
- const members = nonNullTypes.map(
2139
- (t) => resolveTypeNode(t, checker, file, typeRegistry, visiting, sourceNode)
2448
+ const members = nonNullMembers.map(
2449
+ ({ memberType, sourceNode: memberSourceNode }) => resolveTypeNode(
2450
+ memberType,
2451
+ checker,
2452
+ file,
2453
+ typeRegistry,
2454
+ visiting,
2455
+ memberSourceNode ?? sourceNode,
2456
+ extensionRegistry
2457
+ )
2140
2458
  );
2141
2459
  if (hasNull) {
2142
2460
  members.push({ kind: "primitive", primitiveKind: "null" });
2143
2461
  }
2144
2462
  return registerNamed({ kind: "union", members });
2145
2463
  }
2146
- function resolveArrayType(type, checker, file, typeRegistry, visiting) {
2464
+ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
2147
2465
  const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
2148
2466
  const elementType = typeArgs?.[0];
2149
- const items = elementType ? resolveTypeNode(elementType, checker, file, typeRegistry, visiting) : { kind: "primitive", primitiveKind: "string" };
2467
+ const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
2468
+ const items = elementType ? resolveTypeNode(
2469
+ elementType,
2470
+ checker,
2471
+ file,
2472
+ typeRegistry,
2473
+ visiting,
2474
+ elementSourceNode,
2475
+ extensionRegistry
2476
+ ) : { kind: "primitive", primitiveKind: "string" };
2150
2477
  return { kind: "array", items };
2151
2478
  }
2152
- function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
2479
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
2153
2480
  if (type.getProperties().length > 0) {
2154
2481
  return null;
2155
2482
  }
@@ -2157,7 +2484,15 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
2157
2484
  if (!indexInfo) {
2158
2485
  return null;
2159
2486
  }
2160
- const valueType = resolveTypeNode(indexInfo.type, checker, file, typeRegistry, visiting);
2487
+ const valueType = resolveTypeNode(
2488
+ indexInfo.type,
2489
+ checker,
2490
+ file,
2491
+ typeRegistry,
2492
+ visiting,
2493
+ void 0,
2494
+ extensionRegistry
2495
+ );
2161
2496
  return { kind: "record", valueType };
2162
2497
  }
2163
2498
  function typeNodeContainsReference(type, targetName) {
@@ -2185,7 +2520,7 @@ function typeNodeContainsReference(type, targetName) {
2185
2520
  }
2186
2521
  }
2187
2522
  }
2188
- function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2523
+ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
2189
2524
  const typeName = getNamedTypeName(type);
2190
2525
  const namedTypeName = typeName ?? void 0;
2191
2526
  const namedDecl = getNamedTypeDeclaration(type);
@@ -2216,7 +2551,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2216
2551
  return { kind: "reference", name: namedTypeName, typeArguments: [] };
2217
2552
  }
2218
2553
  }
2219
- const recordNode = tryResolveRecordType(type, checker, file, typeRegistry, visiting);
2554
+ const recordNode = tryResolveRecordType(
2555
+ type,
2556
+ checker,
2557
+ file,
2558
+ typeRegistry,
2559
+ visiting,
2560
+ extensionRegistry
2561
+ );
2220
2562
  if (recordNode) {
2221
2563
  visiting.delete(type);
2222
2564
  if (namedTypeName !== void 0 && shouldRegisterNamedType) {
@@ -2225,7 +2567,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2225
2567
  clearNamedTypeRegistration();
2226
2568
  return recordNode;
2227
2569
  }
2228
- const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
2570
+ const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2229
2571
  typeRegistry[namedTypeName] = {
2230
2572
  name: namedTypeName,
2231
2573
  type: recordNode,
@@ -2237,7 +2579,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2237
2579
  return recordNode;
2238
2580
  }
2239
2581
  const properties = [];
2240
- const fieldInfoMap = getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting);
2582
+ const fieldInfoMap = getNamedTypeFieldNodeInfoMap(
2583
+ type,
2584
+ checker,
2585
+ file,
2586
+ typeRegistry,
2587
+ visiting,
2588
+ extensionRegistry
2589
+ );
2241
2590
  for (const prop of type.getProperties()) {
2242
2591
  const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
2243
2592
  if (!declaration) continue;
@@ -2249,7 +2598,8 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2249
2598
  file,
2250
2599
  typeRegistry,
2251
2600
  visiting,
2252
- declaration
2601
+ declaration,
2602
+ extensionRegistry
2253
2603
  );
2254
2604
  const fieldNodeInfo = fieldInfoMap?.get(prop.name);
2255
2605
  properties.push({
@@ -2268,7 +2618,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2268
2618
  additionalProperties: true
2269
2619
  };
2270
2620
  if (namedTypeName !== void 0 && shouldRegisterNamedType) {
2271
- const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
2621
+ const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2272
2622
  typeRegistry[namedTypeName] = {
2273
2623
  name: namedTypeName,
2274
2624
  type: objectNode,
@@ -2279,7 +2629,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2279
2629
  }
2280
2630
  return objectNode;
2281
2631
  }
2282
- function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting) {
2632
+ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, extensionRegistry) {
2283
2633
  const symbols = [type.getSymbol(), type.aliasSymbol].filter(
2284
2634
  (s) => s?.declarations != null && s.declarations.length > 0
2285
2635
  );
@@ -2291,7 +2641,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2291
2641
  const map = /* @__PURE__ */ new Map();
2292
2642
  for (const member of classDecl.members) {
2293
2643
  if (ts4.isPropertyDeclaration(member) && ts4.isIdentifier(member.name)) {
2294
- const fieldNode = analyzeFieldToIR(member, checker, file, typeRegistry, visiting);
2644
+ const fieldNode = analyzeFieldToIR(
2645
+ member,
2646
+ checker,
2647
+ file,
2648
+ typeRegistry,
2649
+ visiting,
2650
+ extensionRegistry
2651
+ );
2295
2652
  if (fieldNode) {
2296
2653
  map.set(fieldNode.name, {
2297
2654
  constraints: [...fieldNode.constraints],
@@ -2305,7 +2662,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2305
2662
  }
2306
2663
  const interfaceDecl = declarations.find(ts4.isInterfaceDeclaration);
2307
2664
  if (interfaceDecl) {
2308
- return buildFieldNodeInfoMap(interfaceDecl.members, checker, file, typeRegistry, visiting);
2665
+ return buildFieldNodeInfoMap(
2666
+ interfaceDecl.members,
2667
+ checker,
2668
+ file,
2669
+ typeRegistry,
2670
+ visiting,
2671
+ extensionRegistry
2672
+ );
2309
2673
  }
2310
2674
  const typeAliasDecl = declarations.find(ts4.isTypeAliasDeclaration);
2311
2675
  if (typeAliasDecl && ts4.isTypeLiteralNode(typeAliasDecl.type)) {
@@ -2314,17 +2678,68 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2314
2678
  checker,
2315
2679
  file,
2316
2680
  typeRegistry,
2317
- visiting
2681
+ visiting,
2682
+ extensionRegistry
2318
2683
  );
2319
2684
  }
2320
2685
  }
2321
2686
  return null;
2322
2687
  }
2323
- function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
2688
+ function extractArrayElementTypeNode(sourceNode, checker) {
2689
+ const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
2690
+ if (typeNode === void 0) {
2691
+ return void 0;
2692
+ }
2693
+ const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
2694
+ if (ts4.isArrayTypeNode(resolvedTypeNode)) {
2695
+ return resolvedTypeNode.elementType;
2696
+ }
2697
+ if (ts4.isTypeReferenceNode(resolvedTypeNode) && ts4.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
2698
+ return resolvedTypeNode.typeArguments[0];
2699
+ }
2700
+ return void 0;
2701
+ }
2702
+ function extractUnionMemberTypeNodes(sourceNode, checker) {
2703
+ const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
2704
+ if (!typeNode) {
2705
+ return [];
2706
+ }
2707
+ const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
2708
+ return ts4.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
2709
+ }
2710
+ function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
2711
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
2712
+ return resolveAliasedTypeNode(typeNode.type, checker, visited);
2713
+ }
2714
+ if (!ts4.isTypeReferenceNode(typeNode) || !ts4.isIdentifier(typeNode.typeName)) {
2715
+ return typeNode;
2716
+ }
2717
+ const symbol = checker.getSymbolAtLocation(typeNode.typeName);
2718
+ const aliasDecl = symbol?.declarations?.find(ts4.isTypeAliasDeclaration);
2719
+ if (aliasDecl === void 0 || visited.has(aliasDecl)) {
2720
+ return typeNode;
2721
+ }
2722
+ visited.add(aliasDecl);
2723
+ return resolveAliasedTypeNode(aliasDecl.type, checker, visited);
2724
+ }
2725
+ function isNullishTypeNode(typeNode) {
2726
+ if (typeNode.kind === ts4.SyntaxKind.NullKeyword || typeNode.kind === ts4.SyntaxKind.UndefinedKeyword) {
2727
+ return true;
2728
+ }
2729
+ return ts4.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts4.SyntaxKind.NullKeyword || typeNode.literal.kind === ts4.SyntaxKind.UndefinedKeyword);
2730
+ }
2731
+ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, extensionRegistry) {
2324
2732
  const map = /* @__PURE__ */ new Map();
2325
2733
  for (const member of members) {
2326
2734
  if (ts4.isPropertySignature(member)) {
2327
- const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
2735
+ const fieldNode = analyzeInterfacePropertyToIR(
2736
+ member,
2737
+ checker,
2738
+ file,
2739
+ typeRegistry,
2740
+ visiting,
2741
+ extensionRegistry
2742
+ );
2328
2743
  if (fieldNode) {
2329
2744
  map.set(fieldNode.name, {
2330
2745
  constraints: [...fieldNode.constraints],
@@ -2336,7 +2751,7 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
2336
2751
  }
2337
2752
  return map;
2338
2753
  }
2339
- function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
2754
+ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegistry, depth = 0) {
2340
2755
  if (!ts4.isTypeReferenceNode(typeNode)) return [];
2341
2756
  if (depth >= MAX_ALIAS_CHAIN_DEPTH) {
2342
2757
  const aliasName = typeNode.typeName.getText();
@@ -2349,8 +2764,29 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
2349
2764
  const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
2350
2765
  if (!aliasDecl) return [];
2351
2766
  if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
2352
- const constraints = extractJSDocConstraintNodes(aliasDecl, file);
2353
- constraints.push(...extractTypeAliasConstraintNodes(aliasDecl.type, checker, file, depth + 1));
2767
+ const aliasFieldType = resolveTypeNode(
2768
+ checker.getTypeAtLocation(aliasDecl.type),
2769
+ checker,
2770
+ file,
2771
+ {},
2772
+ /* @__PURE__ */ new Set(),
2773
+ aliasDecl.type,
2774
+ extensionRegistry
2775
+ );
2776
+ const constraints = extractJSDocConstraintNodes(
2777
+ aliasDecl,
2778
+ file,
2779
+ makeParseOptions(extensionRegistry, aliasFieldType)
2780
+ );
2781
+ constraints.push(
2782
+ ...extractTypeAliasConstraintNodes(
2783
+ aliasDecl.type,
2784
+ checker,
2785
+ file,
2786
+ extensionRegistry,
2787
+ depth + 1
2788
+ )
2789
+ );
2354
2790
  return constraints;
2355
2791
  }
2356
2792
  function provenanceForNode(node, file) {
@@ -2461,10 +2897,10 @@ var init_class_analyzer = __esm({
2461
2897
  });
2462
2898
 
2463
2899
  // src/generators/class-schema.ts
2464
- function generateClassSchemas(analysis, source) {
2900
+ function generateClassSchemas(analysis, source, options) {
2465
2901
  const ir = canonicalizeTSDoc(analysis, source);
2466
2902
  return {
2467
- jsonSchema: generateJsonSchemaFromIR(ir),
2903
+ jsonSchema: generateJsonSchemaFromIR(ir, options),
2468
2904
  uiSchema: generateUiSchemaFromIR(ir)
2469
2905
  };
2470
2906
  }
@@ -2474,27 +2910,54 @@ function generateSchemasFromClass(options) {
2474
2910
  if (!classDecl) {
2475
2911
  throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
2476
2912
  }
2477
- const analysis = analyzeClassToIR(classDecl, ctx.checker, options.filePath);
2478
- return generateClassSchemas(analysis, { file: options.filePath });
2913
+ const analysis = analyzeClassToIR(
2914
+ classDecl,
2915
+ ctx.checker,
2916
+ options.filePath,
2917
+ options.extensionRegistry
2918
+ );
2919
+ return generateClassSchemas(
2920
+ analysis,
2921
+ { file: options.filePath },
2922
+ {
2923
+ extensionRegistry: options.extensionRegistry,
2924
+ vendorPrefix: options.vendorPrefix
2925
+ }
2926
+ );
2479
2927
  }
2480
2928
  function generateSchemas(options) {
2481
2929
  const ctx = createProgramContext(options.filePath);
2482
2930
  const source = { file: options.filePath };
2483
2931
  const classDecl = findClassByName(ctx.sourceFile, options.typeName);
2484
2932
  if (classDecl) {
2485
- const analysis = analyzeClassToIR(classDecl, ctx.checker, options.filePath);
2486
- return generateClassSchemas(analysis, source);
2933
+ const analysis = analyzeClassToIR(
2934
+ classDecl,
2935
+ ctx.checker,
2936
+ options.filePath,
2937
+ options.extensionRegistry
2938
+ );
2939
+ return generateClassSchemas(analysis, source, options);
2487
2940
  }
2488
2941
  const interfaceDecl = findInterfaceByName(ctx.sourceFile, options.typeName);
2489
2942
  if (interfaceDecl) {
2490
- const analysis = analyzeInterfaceToIR(interfaceDecl, ctx.checker, options.filePath);
2491
- return generateClassSchemas(analysis, source);
2943
+ const analysis = analyzeInterfaceToIR(
2944
+ interfaceDecl,
2945
+ ctx.checker,
2946
+ options.filePath,
2947
+ options.extensionRegistry
2948
+ );
2949
+ return generateClassSchemas(analysis, source, options);
2492
2950
  }
2493
2951
  const typeAlias = findTypeAliasByName(ctx.sourceFile, options.typeName);
2494
2952
  if (typeAlias) {
2495
- const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, options.filePath);
2953
+ const result = analyzeTypeAliasToIR(
2954
+ typeAlias,
2955
+ ctx.checker,
2956
+ options.filePath,
2957
+ options.extensionRegistry
2958
+ );
2496
2959
  if (result.ok) {
2497
- return generateClassSchemas(result.analysis, source);
2960
+ return generateClassSchemas(result.analysis, source, options);
2498
2961
  }
2499
2962
  throw new Error(result.error);
2500
2963
  }
@@ -2516,7 +2979,7 @@ var init_class_schema = __esm({
2516
2979
  // src/generators/mixed-authoring.ts
2517
2980
  function buildMixedAuthoringSchemas(options) {
2518
2981
  const { filePath, typeName, overlays, ...schemaOptions } = options;
2519
- const analysis = analyzeNamedType(filePath, typeName);
2982
+ const analysis = analyzeNamedType(filePath, typeName, schemaOptions.extensionRegistry);
2520
2983
  const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays);
2521
2984
  const ir = canonicalizeTSDoc(composedAnalysis, { file: filePath });
2522
2985
  return {
@@ -2524,20 +2987,20 @@ function buildMixedAuthoringSchemas(options) {
2524
2987
  uiSchema: generateUiSchemaFromIR(ir)
2525
2988
  };
2526
2989
  }
2527
- function analyzeNamedType(filePath, typeName) {
2990
+ function analyzeNamedType(filePath, typeName, extensionRegistry) {
2528
2991
  const ctx = createProgramContext(filePath);
2529
2992
  const source = { file: filePath };
2530
2993
  const classDecl = findClassByName(ctx.sourceFile, typeName);
2531
2994
  if (classDecl !== null) {
2532
- return analyzeClassToIR(classDecl, ctx.checker, source.file);
2995
+ return analyzeClassToIR(classDecl, ctx.checker, source.file, extensionRegistry);
2533
2996
  }
2534
2997
  const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
2535
2998
  if (interfaceDecl !== null) {
2536
- return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file);
2999
+ return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file, extensionRegistry);
2537
3000
  }
2538
3001
  const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
2539
3002
  if (typeAlias !== null) {
2540
- const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file);
3003
+ const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file, extensionRegistry);
2541
3004
  if (result.ok) {
2542
3005
  return result.analysis;
2543
3006
  }
@@ -2616,7 +3079,7 @@ function assertSupportedOverlayField(baseField, overlayField) {
2616
3079
  `Mixed-authoring overlay for "${baseField.name}" cannot define constraints; keep constraints on the static model`
2617
3080
  );
2618
3081
  }
2619
- if (overlayField.required) {
3082
+ if (overlayField.required && !baseField.required) {
2620
3083
  throw new Error(
2621
3084
  `Mixed-authoring overlay for "${baseField.name}" cannot change requiredness; keep requiredness on the static model`
2622
3085
  );
@@ -2706,7 +3169,7 @@ function mergeAnnotations(baseAnnotations, overlayAnnotations) {
2706
3169
  const overlayOnly = overlayAnnotations.filter(
2707
3170
  (annotation) => !baseKeys.has(annotationKey(annotation))
2708
3171
  );
2709
- return [...overlayOnly, ...baseAnnotations];
3172
+ return [...baseAnnotations, ...overlayOnly];
2710
3173
  }
2711
3174
  function annotationKey(annotation) {
2712
3175
  return annotation.annotationKind === "custom" ? `${annotation.annotationKind}:${annotation.annotationId}` : annotation.annotationKind;