@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.js CHANGED
@@ -807,7 +807,12 @@ function applyCustomConstraint(schema, constraint, ctx) {
807
807
  `Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
808
808
  );
809
809
  }
810
- Object.assign(schema, registration.toJsonSchema(constraint.payload, ctx.vendorPrefix));
810
+ assignVendorPrefixedExtensionKeywords(
811
+ schema,
812
+ registration.toJsonSchema(constraint.payload, ctx.vendorPrefix),
813
+ ctx.vendorPrefix,
814
+ `custom constraint "${constraint.constraintId}"`
815
+ );
811
816
  }
812
817
  function applyCustomAnnotation(schema, annotation, ctx) {
813
818
  const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
@@ -819,7 +824,22 @@ function applyCustomAnnotation(schema, annotation, ctx) {
819
824
  if (registration.toJsonSchema === void 0) {
820
825
  return;
821
826
  }
822
- Object.assign(schema, registration.toJsonSchema(annotation.value, ctx.vendorPrefix));
827
+ assignVendorPrefixedExtensionKeywords(
828
+ schema,
829
+ registration.toJsonSchema(annotation.value, ctx.vendorPrefix),
830
+ ctx.vendorPrefix,
831
+ `custom annotation "${annotation.annotationId}"`
832
+ );
833
+ }
834
+ function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPrefix, source) {
835
+ for (const [key, value] of Object.entries(extensionSchema)) {
836
+ if (!key.startsWith(`${vendorPrefix}-`)) {
837
+ throw new Error(
838
+ `Cannot apply ${source}: extension hooks may only emit "${vendorPrefix}-*" JSON Schema keywords`
839
+ );
840
+ }
841
+ schema[key] = value;
842
+ }
823
843
  }
824
844
  var init_ir_generator = __esm({
825
845
  "src/json-schema/ir-generator.ts"() {
@@ -1104,7 +1124,10 @@ var init_types = __esm({
1104
1124
  // src/extensions/registry.ts
1105
1125
  function createExtensionRegistry(extensions) {
1106
1126
  const typeMap = /* @__PURE__ */ new Map();
1127
+ const typeNameMap = /* @__PURE__ */ new Map();
1107
1128
  const constraintMap = /* @__PURE__ */ new Map();
1129
+ const constraintTagMap = /* @__PURE__ */ new Map();
1130
+ const builtinBroadeningMap = /* @__PURE__ */ new Map();
1108
1131
  const annotationMap = /* @__PURE__ */ new Map();
1109
1132
  for (const ext of extensions) {
1110
1133
  if (ext.types !== void 0) {
@@ -1114,6 +1137,27 @@ function createExtensionRegistry(extensions) {
1114
1137
  throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
1115
1138
  }
1116
1139
  typeMap.set(qualifiedId, type);
1140
+ for (const sourceTypeName of type.tsTypeNames ?? [type.typeName]) {
1141
+ if (typeNameMap.has(sourceTypeName)) {
1142
+ throw new Error(`Duplicate custom type source name: "${sourceTypeName}"`);
1143
+ }
1144
+ typeNameMap.set(sourceTypeName, {
1145
+ extensionId: ext.extensionId,
1146
+ registration: type
1147
+ });
1148
+ }
1149
+ if (type.builtinConstraintBroadenings !== void 0) {
1150
+ for (const broadening of type.builtinConstraintBroadenings) {
1151
+ const key = `${qualifiedId}:${broadening.tagName}`;
1152
+ if (builtinBroadeningMap.has(key)) {
1153
+ throw new Error(`Duplicate built-in constraint broadening: "${key}"`);
1154
+ }
1155
+ builtinBroadeningMap.set(key, {
1156
+ extensionId: ext.extensionId,
1157
+ registration: broadening
1158
+ });
1159
+ }
1160
+ }
1117
1161
  }
1118
1162
  }
1119
1163
  if (ext.constraints !== void 0) {
@@ -1125,6 +1169,17 @@ function createExtensionRegistry(extensions) {
1125
1169
  constraintMap.set(qualifiedId, constraint);
1126
1170
  }
1127
1171
  }
1172
+ if (ext.constraintTags !== void 0) {
1173
+ for (const tag of ext.constraintTags) {
1174
+ if (constraintTagMap.has(tag.tagName)) {
1175
+ throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
1176
+ }
1177
+ constraintTagMap.set(tag.tagName, {
1178
+ extensionId: ext.extensionId,
1179
+ registration: tag
1180
+ });
1181
+ }
1182
+ }
1128
1183
  if (ext.annotations !== void 0) {
1129
1184
  for (const annotation of ext.annotations) {
1130
1185
  const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
@@ -1138,7 +1193,10 @@ function createExtensionRegistry(extensions) {
1138
1193
  return {
1139
1194
  extensions,
1140
1195
  findType: (typeId) => typeMap.get(typeId),
1196
+ findTypeByName: (typeName) => typeNameMap.get(typeName),
1141
1197
  findConstraint: (constraintId) => constraintMap.get(constraintId),
1198
+ findConstraintTag: (tagName) => constraintTagMap.get(tagName),
1199
+ findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
1142
1200
  findAnnotation: (annotationId) => annotationMap.get(annotationId)
1143
1201
  };
1144
1202
  }
@@ -1331,7 +1389,7 @@ import {
1331
1389
  normalizeConstraintTagName,
1332
1390
  isBuiltinConstraintName
1333
1391
  } from "@formspec/core";
1334
- function createFormSpecTSDocConfig() {
1392
+ function createFormSpecTSDocConfig(extensionTagNames = []) {
1335
1393
  const config = new TSDocConfiguration();
1336
1394
  for (const tagName of Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS)) {
1337
1395
  config.addTagDefinition(
@@ -1351,13 +1409,33 @@ function createFormSpecTSDocConfig() {
1351
1409
  })
1352
1410
  );
1353
1411
  }
1412
+ for (const tagName of extensionTagNames) {
1413
+ config.addTagDefinition(
1414
+ new TSDocTagDefinition({
1415
+ tagName: "@" + tagName,
1416
+ syntaxKind: TSDocTagSyntaxKind.BlockTag,
1417
+ allowMultiple: true
1418
+ })
1419
+ );
1420
+ }
1354
1421
  return config;
1355
1422
  }
1356
- function getParser() {
1357
- sharedParser ??= new TSDocParser(createFormSpecTSDocConfig());
1358
- return sharedParser;
1359
- }
1360
- function parseTSDocTags(node, file = "") {
1423
+ function getParser(options) {
1424
+ const extensionTagNames = [
1425
+ ...options?.extensionRegistry?.extensions.flatMap(
1426
+ (extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
1427
+ ) ?? []
1428
+ ].sort();
1429
+ const cacheKey = extensionTagNames.join("|");
1430
+ const existing = parserCache.get(cacheKey);
1431
+ if (existing) {
1432
+ return existing;
1433
+ }
1434
+ const parser = new TSDocParser(createFormSpecTSDocConfig(extensionTagNames));
1435
+ parserCache.set(cacheKey, parser);
1436
+ return parser;
1437
+ }
1438
+ function parseTSDocTags(node, file = "", options) {
1361
1439
  const constraints = [];
1362
1440
  const annotations = [];
1363
1441
  let displayName;
@@ -1378,7 +1456,7 @@ function parseTSDocTags(node, file = "") {
1378
1456
  if (!commentText.startsWith("/**")) {
1379
1457
  continue;
1380
1458
  }
1381
- const parser = getParser();
1459
+ const parser = getParser(options);
1382
1460
  const parserContext = parser.parseRange(
1383
1461
  TextRange.fromStringRange(sourceText, range.pos, range.end)
1384
1462
  );
@@ -1417,7 +1495,7 @@ function parseTSDocTags(node, file = "") {
1417
1495
  const expectedType = isBuiltinConstraintName(tagName) ? BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
1418
1496
  if (text === "" && expectedType !== "boolean") continue;
1419
1497
  const provenance = provenanceForComment(range, sourceFile, file, tagName);
1420
- const constraintNode = parseConstraintValue(tagName, text, provenance);
1498
+ const constraintNode = parseConstraintValue(tagName, text, provenance, options);
1421
1499
  if (constraintNode) {
1422
1500
  constraints.push(constraintNode);
1423
1501
  }
@@ -1477,7 +1555,7 @@ function parseTSDocTags(node, file = "") {
1477
1555
  annotations.push(defaultValueNode);
1478
1556
  continue;
1479
1557
  }
1480
- const constraintNode = parseConstraintValue(tagName, text, provenance);
1558
+ const constraintNode = parseConstraintValue(tagName, text, provenance, options);
1481
1559
  if (constraintNode) {
1482
1560
  constraints.push(constraintNode);
1483
1561
  }
@@ -1533,7 +1611,11 @@ function extractPlainText(node) {
1533
1611
  }
1534
1612
  return result;
1535
1613
  }
1536
- function parseConstraintValue(tagName, text, provenance) {
1614
+ function parseConstraintValue(tagName, text, provenance, options) {
1615
+ const customConstraint = parseExtensionConstraintValue(tagName, text, provenance, options);
1616
+ if (customConstraint) {
1617
+ return customConstraint;
1618
+ }
1537
1619
  if (!isBuiltinConstraintName(tagName)) {
1538
1620
  return null;
1539
1621
  }
@@ -1638,6 +1720,83 @@ function parseConstraintValue(tagName, text, provenance) {
1638
1720
  provenance
1639
1721
  };
1640
1722
  }
1723
+ function parseExtensionConstraintValue(tagName, text, provenance, options) {
1724
+ const pathResult = extractPathTarget(text);
1725
+ const effectiveText = pathResult ? pathResult.remainingText : text;
1726
+ const path4 = pathResult?.path;
1727
+ const registry = options?.extensionRegistry;
1728
+ if (registry === void 0) {
1729
+ return null;
1730
+ }
1731
+ const directTag = registry.findConstraintTag(tagName);
1732
+ if (directTag !== void 0) {
1733
+ return makeCustomConstraintNode(
1734
+ directTag.extensionId,
1735
+ directTag.registration.constraintName,
1736
+ directTag.registration.parseValue(effectiveText),
1737
+ provenance,
1738
+ path4,
1739
+ registry
1740
+ );
1741
+ }
1742
+ if (!isBuiltinConstraintName(tagName)) {
1743
+ return null;
1744
+ }
1745
+ const broadenedTypeId = getBroadenedCustomTypeId(options?.fieldType);
1746
+ if (broadenedTypeId === void 0) {
1747
+ return null;
1748
+ }
1749
+ const broadened = registry.findBuiltinConstraintBroadening(broadenedTypeId, tagName);
1750
+ if (broadened === void 0) {
1751
+ return null;
1752
+ }
1753
+ return makeCustomConstraintNode(
1754
+ broadened.extensionId,
1755
+ broadened.registration.constraintName,
1756
+ broadened.registration.parseValue(effectiveText),
1757
+ provenance,
1758
+ path4,
1759
+ registry
1760
+ );
1761
+ }
1762
+ function getBroadenedCustomTypeId(fieldType) {
1763
+ if (fieldType?.kind === "custom") {
1764
+ return fieldType.typeId;
1765
+ }
1766
+ if (fieldType?.kind !== "union") {
1767
+ return void 0;
1768
+ }
1769
+ const customMembers = fieldType.members.filter(
1770
+ (member) => member.kind === "custom"
1771
+ );
1772
+ if (customMembers.length !== 1) {
1773
+ return void 0;
1774
+ }
1775
+ const nonCustomMembers = fieldType.members.filter((member) => member.kind !== "custom");
1776
+ const allOtherMembersAreNull = nonCustomMembers.every(
1777
+ (member) => member.kind === "primitive" && member.primitiveKind === "null"
1778
+ );
1779
+ const customMember = customMembers[0];
1780
+ return allOtherMembersAreNull && customMember !== void 0 ? customMember.typeId : void 0;
1781
+ }
1782
+ function makeCustomConstraintNode(extensionId, constraintName, payload, provenance, path4, registry) {
1783
+ const constraintId = `${extensionId}/${constraintName}`;
1784
+ const registration = registry.findConstraint(constraintId);
1785
+ if (registration === void 0) {
1786
+ throw new Error(
1787
+ `Custom TSDoc tag resolved to unregistered constraint "${constraintId}". Register the constraint before using its tag.`
1788
+ );
1789
+ }
1790
+ return {
1791
+ kind: "constraint",
1792
+ constraintKind: "custom",
1793
+ constraintId,
1794
+ payload,
1795
+ compositionRule: registration.compositionRule,
1796
+ ...path4 && { path: path4 },
1797
+ provenance
1798
+ };
1799
+ }
1641
1800
  function parseDefaultValueValue(text, provenance) {
1642
1801
  const trimmed = text.trim();
1643
1802
  let value;
@@ -1696,7 +1855,7 @@ function getTagCommentText(tag) {
1696
1855
  }
1697
1856
  return ts2.getTextOfJSDocComment(tag.comment);
1698
1857
  }
1699
- var NUMERIC_CONSTRAINT_MAP, LENGTH_CONSTRAINT_MAP, TAGS_REQUIRING_RAW_TEXT, sharedParser;
1858
+ var NUMERIC_CONSTRAINT_MAP, LENGTH_CONSTRAINT_MAP, TAGS_REQUIRING_RAW_TEXT, parserCache;
1700
1859
  var init_tsdoc_parser = __esm({
1701
1860
  "src/analyzer/tsdoc-parser.ts"() {
1702
1861
  "use strict";
@@ -1715,17 +1874,18 @@ var init_tsdoc_parser = __esm({
1715
1874
  maxItems: "maxItems"
1716
1875
  };
1717
1876
  TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions", "defaultValue"]);
1877
+ parserCache = /* @__PURE__ */ new Map();
1718
1878
  }
1719
1879
  });
1720
1880
 
1721
1881
  // src/analyzer/jsdoc-constraints.ts
1722
1882
  import * as ts3 from "typescript";
1723
- function extractJSDocConstraintNodes(node, file = "") {
1724
- const result = parseTSDocTags(node, file);
1883
+ function extractJSDocConstraintNodes(node, file = "", options) {
1884
+ const result = parseTSDocTags(node, file, options);
1725
1885
  return [...result.constraints];
1726
1886
  }
1727
- function extractJSDocAnnotationNodes(node, file = "") {
1728
- const result = parseTSDocTags(node, file);
1887
+ function extractJSDocAnnotationNodes(node, file = "", options) {
1888
+ const result = parseTSDocTags(node, file, options);
1729
1889
  return [...result.annotations];
1730
1890
  }
1731
1891
  function extractDefaultValueAnnotation(initializer, file = "") {
@@ -1776,18 +1936,38 @@ function isObjectType(type) {
1776
1936
  function isTypeReference(type) {
1777
1937
  return !!(type.flags & ts4.TypeFlags.Object) && !!(type.objectFlags & ts4.ObjectFlags.Reference);
1778
1938
  }
1779
- function analyzeClassToIR(classDecl, checker, file = "") {
1939
+ function makeParseOptions(extensionRegistry, fieldType) {
1940
+ if (extensionRegistry === void 0 && fieldType === void 0) {
1941
+ return void 0;
1942
+ }
1943
+ return {
1944
+ ...extensionRegistry !== void 0 && { extensionRegistry },
1945
+ ...fieldType !== void 0 && { fieldType }
1946
+ };
1947
+ }
1948
+ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
1780
1949
  const name = classDecl.name?.text ?? "AnonymousClass";
1781
1950
  const fields = [];
1782
1951
  const fieldLayouts = [];
1783
1952
  const typeRegistry = {};
1784
- const annotations = extractJSDocAnnotationNodes(classDecl, file);
1953
+ const annotations = extractJSDocAnnotationNodes(
1954
+ classDecl,
1955
+ file,
1956
+ makeParseOptions(extensionRegistry)
1957
+ );
1785
1958
  const visiting = /* @__PURE__ */ new Set();
1786
1959
  const instanceMethods = [];
1787
1960
  const staticMethods = [];
1788
1961
  for (const member of classDecl.members) {
1789
1962
  if (ts4.isPropertyDeclaration(member)) {
1790
- const fieldNode = analyzeFieldToIR(member, checker, file, typeRegistry, visiting);
1963
+ const fieldNode = analyzeFieldToIR(
1964
+ member,
1965
+ checker,
1966
+ file,
1967
+ typeRegistry,
1968
+ visiting,
1969
+ extensionRegistry
1970
+ );
1791
1971
  if (fieldNode) {
1792
1972
  fields.push(fieldNode);
1793
1973
  fieldLayouts.push({});
@@ -1814,15 +1994,26 @@ function analyzeClassToIR(classDecl, checker, file = "") {
1814
1994
  staticMethods
1815
1995
  };
1816
1996
  }
1817
- function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
1997
+ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry) {
1818
1998
  const name = interfaceDecl.name.text;
1819
1999
  const fields = [];
1820
2000
  const typeRegistry = {};
1821
- const annotations = extractJSDocAnnotationNodes(interfaceDecl, file);
2001
+ const annotations = extractJSDocAnnotationNodes(
2002
+ interfaceDecl,
2003
+ file,
2004
+ makeParseOptions(extensionRegistry)
2005
+ );
1822
2006
  const visiting = /* @__PURE__ */ new Set();
1823
2007
  for (const member of interfaceDecl.members) {
1824
2008
  if (ts4.isPropertySignature(member)) {
1825
- const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
2009
+ const fieldNode = analyzeInterfacePropertyToIR(
2010
+ member,
2011
+ checker,
2012
+ file,
2013
+ typeRegistry,
2014
+ visiting,
2015
+ extensionRegistry
2016
+ );
1826
2017
  if (fieldNode) {
1827
2018
  fields.push(fieldNode);
1828
2019
  }
@@ -1839,7 +2030,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
1839
2030
  staticMethods: []
1840
2031
  };
1841
2032
  }
1842
- function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
2033
+ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
1843
2034
  if (!ts4.isTypeLiteralNode(typeAlias.type)) {
1844
2035
  const sourceFile = typeAlias.getSourceFile();
1845
2036
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
@@ -1852,11 +2043,22 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
1852
2043
  const name = typeAlias.name.text;
1853
2044
  const fields = [];
1854
2045
  const typeRegistry = {};
1855
- const annotations = extractJSDocAnnotationNodes(typeAlias, file);
2046
+ const annotations = extractJSDocAnnotationNodes(
2047
+ typeAlias,
2048
+ file,
2049
+ makeParseOptions(extensionRegistry)
2050
+ );
1856
2051
  const visiting = /* @__PURE__ */ new Set();
1857
2052
  for (const member of typeAlias.type.members) {
1858
2053
  if (ts4.isPropertySignature(member)) {
1859
- const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
2054
+ const fieldNode = analyzeInterfacePropertyToIR(
2055
+ member,
2056
+ checker,
2057
+ file,
2058
+ typeRegistry,
2059
+ visiting,
2060
+ extensionRegistry
2061
+ );
1860
2062
  if (fieldNode) {
1861
2063
  fields.push(fieldNode);
1862
2064
  }
@@ -1875,7 +2077,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
1875
2077
  }
1876
2078
  };
1877
2079
  }
1878
- function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
2080
+ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
1879
2081
  if (!ts4.isIdentifier(prop.name)) {
1880
2082
  return null;
1881
2083
  }
@@ -1883,14 +2085,26 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
1883
2085
  const tsType = checker.getTypeAtLocation(prop);
1884
2086
  const optional = prop.questionToken !== void 0;
1885
2087
  const provenance = provenanceForNode(prop, file);
1886
- let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
2088
+ let type = resolveTypeNode(
2089
+ tsType,
2090
+ checker,
2091
+ file,
2092
+ typeRegistry,
2093
+ visiting,
2094
+ prop,
2095
+ extensionRegistry
2096
+ );
1887
2097
  const constraints = [];
1888
2098
  if (prop.type) {
1889
- constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
2099
+ constraints.push(
2100
+ ...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
2101
+ );
1890
2102
  }
1891
- constraints.push(...extractJSDocConstraintNodes(prop, file));
2103
+ constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
1892
2104
  let annotations = [];
1893
- annotations.push(...extractJSDocAnnotationNodes(prop, file));
2105
+ annotations.push(
2106
+ ...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
2107
+ );
1894
2108
  const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
1895
2109
  if (defaultAnnotation && !annotations.some((a) => a.annotationKind === "defaultValue")) {
1896
2110
  annotations.push(defaultAnnotation);
@@ -1906,7 +2120,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
1906
2120
  provenance
1907
2121
  };
1908
2122
  }
1909
- function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting) {
2123
+ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
1910
2124
  if (!ts4.isIdentifier(prop.name)) {
1911
2125
  return null;
1912
2126
  }
@@ -1914,14 +2128,26 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
1914
2128
  const tsType = checker.getTypeAtLocation(prop);
1915
2129
  const optional = prop.questionToken !== void 0;
1916
2130
  const provenance = provenanceForNode(prop, file);
1917
- let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
2131
+ let type = resolveTypeNode(
2132
+ tsType,
2133
+ checker,
2134
+ file,
2135
+ typeRegistry,
2136
+ visiting,
2137
+ prop,
2138
+ extensionRegistry
2139
+ );
1918
2140
  const constraints = [];
1919
2141
  if (prop.type) {
1920
- constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
2142
+ constraints.push(
2143
+ ...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
2144
+ );
1921
2145
  }
1922
- constraints.push(...extractJSDocConstraintNodes(prop, file));
2146
+ constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
1923
2147
  let annotations = [];
1924
- annotations.push(...extractJSDocAnnotationNodes(prop, file));
2148
+ annotations.push(
2149
+ ...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
2150
+ );
1925
2151
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
1926
2152
  return {
1927
2153
  kind: "field",
@@ -1995,7 +2221,66 @@ function parseEnumMemberDisplayName(value) {
1995
2221
  if (label === "") return null;
1996
2222
  return { value: match[1], label };
1997
2223
  }
1998
- function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode) {
2224
+ function resolveRegisteredCustomType(sourceNode, extensionRegistry, checker) {
2225
+ if (sourceNode === void 0 || extensionRegistry === void 0) {
2226
+ return null;
2227
+ }
2228
+ const typeNode = extractTypeNodeFromSource(sourceNode);
2229
+ if (typeNode === void 0) {
2230
+ return null;
2231
+ }
2232
+ return resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker);
2233
+ }
2234
+ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker) {
2235
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
2236
+ return resolveRegisteredCustomTypeFromTypeNode(typeNode.type, extensionRegistry, checker);
2237
+ }
2238
+ const typeName = getTypeNodeRegistrationName(typeNode);
2239
+ if (typeName === null) {
2240
+ return null;
2241
+ }
2242
+ const registration = extensionRegistry.findTypeByName(typeName);
2243
+ if (registration !== void 0) {
2244
+ return {
2245
+ kind: "custom",
2246
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
2247
+ payload: null
2248
+ };
2249
+ }
2250
+ if (ts4.isTypeReferenceNode(typeNode) && ts4.isIdentifier(typeNode.typeName)) {
2251
+ const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts4.isTypeAliasDeclaration);
2252
+ if (aliasDecl !== void 0) {
2253
+ return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
2254
+ }
2255
+ }
2256
+ return null;
2257
+ }
2258
+ function extractTypeNodeFromSource(sourceNode) {
2259
+ if (ts4.isPropertyDeclaration(sourceNode) || ts4.isPropertySignature(sourceNode) || ts4.isParameter(sourceNode) || ts4.isTypeAliasDeclaration(sourceNode)) {
2260
+ return sourceNode.type;
2261
+ }
2262
+ if (ts4.isTypeNode(sourceNode)) {
2263
+ return sourceNode;
2264
+ }
2265
+ return void 0;
2266
+ }
2267
+ function getTypeNodeRegistrationName(typeNode) {
2268
+ if (ts4.isTypeReferenceNode(typeNode)) {
2269
+ return ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
2270
+ }
2271
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
2272
+ return getTypeNodeRegistrationName(typeNode.type);
2273
+ }
2274
+ if (typeNode.kind === ts4.SyntaxKind.BigIntKeyword || typeNode.kind === ts4.SyntaxKind.StringKeyword || typeNode.kind === ts4.SyntaxKind.NumberKeyword || typeNode.kind === ts4.SyntaxKind.BooleanKeyword) {
2275
+ return typeNode.getText();
2276
+ }
2277
+ return null;
2278
+ }
2279
+ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
2280
+ const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
2281
+ if (customType) {
2282
+ return customType;
2283
+ }
1999
2284
  if (type.flags & ts4.TypeFlags.String) {
2000
2285
  return { kind: "primitive", primitiveKind: "string" };
2001
2286
  }
@@ -2024,26 +2309,50 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
2024
2309
  };
2025
2310
  }
2026
2311
  if (type.isUnion()) {
2027
- return resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode);
2312
+ return resolveUnionType(
2313
+ type,
2314
+ checker,
2315
+ file,
2316
+ typeRegistry,
2317
+ visiting,
2318
+ sourceNode,
2319
+ extensionRegistry
2320
+ );
2028
2321
  }
2029
2322
  if (checker.isArrayType(type)) {
2030
- return resolveArrayType(type, checker, file, typeRegistry, visiting);
2323
+ return resolveArrayType(
2324
+ type,
2325
+ checker,
2326
+ file,
2327
+ typeRegistry,
2328
+ visiting,
2329
+ sourceNode,
2330
+ extensionRegistry
2331
+ );
2031
2332
  }
2032
2333
  if (isObjectType(type)) {
2033
- return resolveObjectType(type, checker, file, typeRegistry, visiting);
2334
+ return resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry);
2034
2335
  }
2035
2336
  return { kind: "primitive", primitiveKind: "string" };
2036
2337
  }
2037
- function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode) {
2338
+ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
2038
2339
  const typeName = getNamedTypeName(type);
2039
2340
  const namedDecl = getNamedTypeDeclaration(type);
2040
2341
  if (typeName && typeName in typeRegistry) {
2041
2342
  return { kind: "reference", name: typeName, typeArguments: [] };
2042
2343
  }
2043
2344
  const allTypes = type.types;
2345
+ const unionMemberTypeNodes = extractUnionMemberTypeNodes(sourceNode, checker);
2346
+ const nonNullSourceNodes = unionMemberTypeNodes.filter(
2347
+ (memberTypeNode) => !isNullishTypeNode(resolveAliasedTypeNode(memberTypeNode, checker))
2348
+ );
2044
2349
  const nonNullTypes = allTypes.filter(
2045
- (t) => !(t.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
2350
+ (memberType) => !(memberType.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
2046
2351
  );
2352
+ const nonNullMembers = nonNullTypes.map((memberType, index) => ({
2353
+ memberType,
2354
+ sourceNode: nonNullSourceNodes.length === nonNullTypes.length ? nonNullSourceNodes[index] : void 0
2355
+ }));
2047
2356
  const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
2048
2357
  const memberDisplayNames = /* @__PURE__ */ new Map();
2049
2358
  if (namedDecl) {
@@ -2060,7 +2369,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2060
2369
  if (!typeName) {
2061
2370
  return result;
2062
2371
  }
2063
- const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
2372
+ const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2064
2373
  typeRegistry[typeName] = {
2065
2374
  name: typeName,
2066
2375
  type: result,
@@ -2108,14 +2417,15 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2108
2417
  } : enumNode;
2109
2418
  return registerNamed(result);
2110
2419
  }
2111
- if (nonNullTypes.length === 1 && nonNullTypes[0]) {
2420
+ if (nonNullMembers.length === 1 && nonNullMembers[0]) {
2112
2421
  const inner = resolveTypeNode(
2113
- nonNullTypes[0],
2422
+ nonNullMembers[0].memberType,
2114
2423
  checker,
2115
2424
  file,
2116
2425
  typeRegistry,
2117
2426
  visiting,
2118
- sourceNode
2427
+ nonNullMembers[0].sourceNode ?? sourceNode,
2428
+ extensionRegistry
2119
2429
  );
2120
2430
  const result = hasNull ? {
2121
2431
  kind: "union",
@@ -2123,21 +2433,38 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2123
2433
  } : inner;
2124
2434
  return registerNamed(result);
2125
2435
  }
2126
- const members = nonNullTypes.map(
2127
- (t) => resolveTypeNode(t, checker, file, typeRegistry, visiting, sourceNode)
2436
+ const members = nonNullMembers.map(
2437
+ ({ memberType, sourceNode: memberSourceNode }) => resolveTypeNode(
2438
+ memberType,
2439
+ checker,
2440
+ file,
2441
+ typeRegistry,
2442
+ visiting,
2443
+ memberSourceNode ?? sourceNode,
2444
+ extensionRegistry
2445
+ )
2128
2446
  );
2129
2447
  if (hasNull) {
2130
2448
  members.push({ kind: "primitive", primitiveKind: "null" });
2131
2449
  }
2132
2450
  return registerNamed({ kind: "union", members });
2133
2451
  }
2134
- function resolveArrayType(type, checker, file, typeRegistry, visiting) {
2452
+ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
2135
2453
  const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
2136
2454
  const elementType = typeArgs?.[0];
2137
- const items = elementType ? resolveTypeNode(elementType, checker, file, typeRegistry, visiting) : { kind: "primitive", primitiveKind: "string" };
2455
+ const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
2456
+ const items = elementType ? resolveTypeNode(
2457
+ elementType,
2458
+ checker,
2459
+ file,
2460
+ typeRegistry,
2461
+ visiting,
2462
+ elementSourceNode,
2463
+ extensionRegistry
2464
+ ) : { kind: "primitive", primitiveKind: "string" };
2138
2465
  return { kind: "array", items };
2139
2466
  }
2140
- function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
2467
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
2141
2468
  if (type.getProperties().length > 0) {
2142
2469
  return null;
2143
2470
  }
@@ -2145,7 +2472,15 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
2145
2472
  if (!indexInfo) {
2146
2473
  return null;
2147
2474
  }
2148
- const valueType = resolveTypeNode(indexInfo.type, checker, file, typeRegistry, visiting);
2475
+ const valueType = resolveTypeNode(
2476
+ indexInfo.type,
2477
+ checker,
2478
+ file,
2479
+ typeRegistry,
2480
+ visiting,
2481
+ void 0,
2482
+ extensionRegistry
2483
+ );
2149
2484
  return { kind: "record", valueType };
2150
2485
  }
2151
2486
  function typeNodeContainsReference(type, targetName) {
@@ -2173,7 +2508,7 @@ function typeNodeContainsReference(type, targetName) {
2173
2508
  }
2174
2509
  }
2175
2510
  }
2176
- function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2511
+ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
2177
2512
  const typeName = getNamedTypeName(type);
2178
2513
  const namedTypeName = typeName ?? void 0;
2179
2514
  const namedDecl = getNamedTypeDeclaration(type);
@@ -2204,7 +2539,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2204
2539
  return { kind: "reference", name: namedTypeName, typeArguments: [] };
2205
2540
  }
2206
2541
  }
2207
- const recordNode = tryResolveRecordType(type, checker, file, typeRegistry, visiting);
2542
+ const recordNode = tryResolveRecordType(
2543
+ type,
2544
+ checker,
2545
+ file,
2546
+ typeRegistry,
2547
+ visiting,
2548
+ extensionRegistry
2549
+ );
2208
2550
  if (recordNode) {
2209
2551
  visiting.delete(type);
2210
2552
  if (namedTypeName !== void 0 && shouldRegisterNamedType) {
@@ -2213,7 +2555,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2213
2555
  clearNamedTypeRegistration();
2214
2556
  return recordNode;
2215
2557
  }
2216
- const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
2558
+ const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2217
2559
  typeRegistry[namedTypeName] = {
2218
2560
  name: namedTypeName,
2219
2561
  type: recordNode,
@@ -2225,7 +2567,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2225
2567
  return recordNode;
2226
2568
  }
2227
2569
  const properties = [];
2228
- const fieldInfoMap = getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting);
2570
+ const fieldInfoMap = getNamedTypeFieldNodeInfoMap(
2571
+ type,
2572
+ checker,
2573
+ file,
2574
+ typeRegistry,
2575
+ visiting,
2576
+ extensionRegistry
2577
+ );
2229
2578
  for (const prop of type.getProperties()) {
2230
2579
  const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
2231
2580
  if (!declaration) continue;
@@ -2237,7 +2586,8 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2237
2586
  file,
2238
2587
  typeRegistry,
2239
2588
  visiting,
2240
- declaration
2589
+ declaration,
2590
+ extensionRegistry
2241
2591
  );
2242
2592
  const fieldNodeInfo = fieldInfoMap?.get(prop.name);
2243
2593
  properties.push({
@@ -2256,7 +2606,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2256
2606
  additionalProperties: true
2257
2607
  };
2258
2608
  if (namedTypeName !== void 0 && shouldRegisterNamedType) {
2259
- const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
2609
+ const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2260
2610
  typeRegistry[namedTypeName] = {
2261
2611
  name: namedTypeName,
2262
2612
  type: objectNode,
@@ -2267,7 +2617,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
2267
2617
  }
2268
2618
  return objectNode;
2269
2619
  }
2270
- function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting) {
2620
+ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, extensionRegistry) {
2271
2621
  const symbols = [type.getSymbol(), type.aliasSymbol].filter(
2272
2622
  (s) => s?.declarations != null && s.declarations.length > 0
2273
2623
  );
@@ -2279,7 +2629,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2279
2629
  const map = /* @__PURE__ */ new Map();
2280
2630
  for (const member of classDecl.members) {
2281
2631
  if (ts4.isPropertyDeclaration(member) && ts4.isIdentifier(member.name)) {
2282
- const fieldNode = analyzeFieldToIR(member, checker, file, typeRegistry, visiting);
2632
+ const fieldNode = analyzeFieldToIR(
2633
+ member,
2634
+ checker,
2635
+ file,
2636
+ typeRegistry,
2637
+ visiting,
2638
+ extensionRegistry
2639
+ );
2283
2640
  if (fieldNode) {
2284
2641
  map.set(fieldNode.name, {
2285
2642
  constraints: [...fieldNode.constraints],
@@ -2293,7 +2650,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2293
2650
  }
2294
2651
  const interfaceDecl = declarations.find(ts4.isInterfaceDeclaration);
2295
2652
  if (interfaceDecl) {
2296
- return buildFieldNodeInfoMap(interfaceDecl.members, checker, file, typeRegistry, visiting);
2653
+ return buildFieldNodeInfoMap(
2654
+ interfaceDecl.members,
2655
+ checker,
2656
+ file,
2657
+ typeRegistry,
2658
+ visiting,
2659
+ extensionRegistry
2660
+ );
2297
2661
  }
2298
2662
  const typeAliasDecl = declarations.find(ts4.isTypeAliasDeclaration);
2299
2663
  if (typeAliasDecl && ts4.isTypeLiteralNode(typeAliasDecl.type)) {
@@ -2302,17 +2666,68 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
2302
2666
  checker,
2303
2667
  file,
2304
2668
  typeRegistry,
2305
- visiting
2669
+ visiting,
2670
+ extensionRegistry
2306
2671
  );
2307
2672
  }
2308
2673
  }
2309
2674
  return null;
2310
2675
  }
2311
- function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
2676
+ function extractArrayElementTypeNode(sourceNode, checker) {
2677
+ const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
2678
+ if (typeNode === void 0) {
2679
+ return void 0;
2680
+ }
2681
+ const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
2682
+ if (ts4.isArrayTypeNode(resolvedTypeNode)) {
2683
+ return resolvedTypeNode.elementType;
2684
+ }
2685
+ if (ts4.isTypeReferenceNode(resolvedTypeNode) && ts4.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
2686
+ return resolvedTypeNode.typeArguments[0];
2687
+ }
2688
+ return void 0;
2689
+ }
2690
+ function extractUnionMemberTypeNodes(sourceNode, checker) {
2691
+ const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
2692
+ if (!typeNode) {
2693
+ return [];
2694
+ }
2695
+ const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
2696
+ return ts4.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
2697
+ }
2698
+ function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
2699
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
2700
+ return resolveAliasedTypeNode(typeNode.type, checker, visited);
2701
+ }
2702
+ if (!ts4.isTypeReferenceNode(typeNode) || !ts4.isIdentifier(typeNode.typeName)) {
2703
+ return typeNode;
2704
+ }
2705
+ const symbol = checker.getSymbolAtLocation(typeNode.typeName);
2706
+ const aliasDecl = symbol?.declarations?.find(ts4.isTypeAliasDeclaration);
2707
+ if (aliasDecl === void 0 || visited.has(aliasDecl)) {
2708
+ return typeNode;
2709
+ }
2710
+ visited.add(aliasDecl);
2711
+ return resolveAliasedTypeNode(aliasDecl.type, checker, visited);
2712
+ }
2713
+ function isNullishTypeNode(typeNode) {
2714
+ if (typeNode.kind === ts4.SyntaxKind.NullKeyword || typeNode.kind === ts4.SyntaxKind.UndefinedKeyword) {
2715
+ return true;
2716
+ }
2717
+ return ts4.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts4.SyntaxKind.NullKeyword || typeNode.literal.kind === ts4.SyntaxKind.UndefinedKeyword);
2718
+ }
2719
+ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, extensionRegistry) {
2312
2720
  const map = /* @__PURE__ */ new Map();
2313
2721
  for (const member of members) {
2314
2722
  if (ts4.isPropertySignature(member)) {
2315
- const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
2723
+ const fieldNode = analyzeInterfacePropertyToIR(
2724
+ member,
2725
+ checker,
2726
+ file,
2727
+ typeRegistry,
2728
+ visiting,
2729
+ extensionRegistry
2730
+ );
2316
2731
  if (fieldNode) {
2317
2732
  map.set(fieldNode.name, {
2318
2733
  constraints: [...fieldNode.constraints],
@@ -2324,7 +2739,7 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
2324
2739
  }
2325
2740
  return map;
2326
2741
  }
2327
- function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
2742
+ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegistry, depth = 0) {
2328
2743
  if (!ts4.isTypeReferenceNode(typeNode)) return [];
2329
2744
  if (depth >= MAX_ALIAS_CHAIN_DEPTH) {
2330
2745
  const aliasName = typeNode.typeName.getText();
@@ -2337,8 +2752,29 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
2337
2752
  const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
2338
2753
  if (!aliasDecl) return [];
2339
2754
  if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
2340
- const constraints = extractJSDocConstraintNodes(aliasDecl, file);
2341
- constraints.push(...extractTypeAliasConstraintNodes(aliasDecl.type, checker, file, depth + 1));
2755
+ const aliasFieldType = resolveTypeNode(
2756
+ checker.getTypeAtLocation(aliasDecl.type),
2757
+ checker,
2758
+ file,
2759
+ {},
2760
+ /* @__PURE__ */ new Set(),
2761
+ aliasDecl.type,
2762
+ extensionRegistry
2763
+ );
2764
+ const constraints = extractJSDocConstraintNodes(
2765
+ aliasDecl,
2766
+ file,
2767
+ makeParseOptions(extensionRegistry, aliasFieldType)
2768
+ );
2769
+ constraints.push(
2770
+ ...extractTypeAliasConstraintNodes(
2771
+ aliasDecl.type,
2772
+ checker,
2773
+ file,
2774
+ extensionRegistry,
2775
+ depth + 1
2776
+ )
2777
+ );
2342
2778
  return constraints;
2343
2779
  }
2344
2780
  function provenanceForNode(node, file) {
@@ -2448,10 +2884,10 @@ var init_class_analyzer = __esm({
2448
2884
  });
2449
2885
 
2450
2886
  // src/generators/class-schema.ts
2451
- function generateClassSchemas(analysis, source) {
2887
+ function generateClassSchemas(analysis, source, options) {
2452
2888
  const ir = canonicalizeTSDoc(analysis, source);
2453
2889
  return {
2454
- jsonSchema: generateJsonSchemaFromIR(ir),
2890
+ jsonSchema: generateJsonSchemaFromIR(ir, options),
2455
2891
  uiSchema: generateUiSchemaFromIR(ir)
2456
2892
  };
2457
2893
  }
@@ -2461,27 +2897,54 @@ function generateSchemasFromClass(options) {
2461
2897
  if (!classDecl) {
2462
2898
  throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
2463
2899
  }
2464
- const analysis = analyzeClassToIR(classDecl, ctx.checker, options.filePath);
2465
- return generateClassSchemas(analysis, { file: options.filePath });
2900
+ const analysis = analyzeClassToIR(
2901
+ classDecl,
2902
+ ctx.checker,
2903
+ options.filePath,
2904
+ options.extensionRegistry
2905
+ );
2906
+ return generateClassSchemas(
2907
+ analysis,
2908
+ { file: options.filePath },
2909
+ {
2910
+ extensionRegistry: options.extensionRegistry,
2911
+ vendorPrefix: options.vendorPrefix
2912
+ }
2913
+ );
2466
2914
  }
2467
2915
  function generateSchemas(options) {
2468
2916
  const ctx = createProgramContext(options.filePath);
2469
2917
  const source = { file: options.filePath };
2470
2918
  const classDecl = findClassByName(ctx.sourceFile, options.typeName);
2471
2919
  if (classDecl) {
2472
- const analysis = analyzeClassToIR(classDecl, ctx.checker, options.filePath);
2473
- return generateClassSchemas(analysis, source);
2920
+ const analysis = analyzeClassToIR(
2921
+ classDecl,
2922
+ ctx.checker,
2923
+ options.filePath,
2924
+ options.extensionRegistry
2925
+ );
2926
+ return generateClassSchemas(analysis, source, options);
2474
2927
  }
2475
2928
  const interfaceDecl = findInterfaceByName(ctx.sourceFile, options.typeName);
2476
2929
  if (interfaceDecl) {
2477
- const analysis = analyzeInterfaceToIR(interfaceDecl, ctx.checker, options.filePath);
2478
- return generateClassSchemas(analysis, source);
2930
+ const analysis = analyzeInterfaceToIR(
2931
+ interfaceDecl,
2932
+ ctx.checker,
2933
+ options.filePath,
2934
+ options.extensionRegistry
2935
+ );
2936
+ return generateClassSchemas(analysis, source, options);
2479
2937
  }
2480
2938
  const typeAlias = findTypeAliasByName(ctx.sourceFile, options.typeName);
2481
2939
  if (typeAlias) {
2482
- const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, options.filePath);
2940
+ const result = analyzeTypeAliasToIR(
2941
+ typeAlias,
2942
+ ctx.checker,
2943
+ options.filePath,
2944
+ options.extensionRegistry
2945
+ );
2483
2946
  if (result.ok) {
2484
- return generateClassSchemas(result.analysis, source);
2947
+ return generateClassSchemas(result.analysis, source, options);
2485
2948
  }
2486
2949
  throw new Error(result.error);
2487
2950
  }
@@ -2503,7 +2966,7 @@ var init_class_schema = __esm({
2503
2966
  // src/generators/mixed-authoring.ts
2504
2967
  function buildMixedAuthoringSchemas(options) {
2505
2968
  const { filePath, typeName, overlays, ...schemaOptions } = options;
2506
- const analysis = analyzeNamedType(filePath, typeName);
2969
+ const analysis = analyzeNamedType(filePath, typeName, schemaOptions.extensionRegistry);
2507
2970
  const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays);
2508
2971
  const ir = canonicalizeTSDoc(composedAnalysis, { file: filePath });
2509
2972
  return {
@@ -2511,20 +2974,20 @@ function buildMixedAuthoringSchemas(options) {
2511
2974
  uiSchema: generateUiSchemaFromIR(ir)
2512
2975
  };
2513
2976
  }
2514
- function analyzeNamedType(filePath, typeName) {
2977
+ function analyzeNamedType(filePath, typeName, extensionRegistry) {
2515
2978
  const ctx = createProgramContext(filePath);
2516
2979
  const source = { file: filePath };
2517
2980
  const classDecl = findClassByName(ctx.sourceFile, typeName);
2518
2981
  if (classDecl !== null) {
2519
- return analyzeClassToIR(classDecl, ctx.checker, source.file);
2982
+ return analyzeClassToIR(classDecl, ctx.checker, source.file, extensionRegistry);
2520
2983
  }
2521
2984
  const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
2522
2985
  if (interfaceDecl !== null) {
2523
- return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file);
2986
+ return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file, extensionRegistry);
2524
2987
  }
2525
2988
  const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
2526
2989
  if (typeAlias !== null) {
2527
- const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file);
2990
+ const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file, extensionRegistry);
2528
2991
  if (result.ok) {
2529
2992
  return result.analysis;
2530
2993
  }
@@ -2603,7 +3066,7 @@ function assertSupportedOverlayField(baseField, overlayField) {
2603
3066
  `Mixed-authoring overlay for "${baseField.name}" cannot define constraints; keep constraints on the static model`
2604
3067
  );
2605
3068
  }
2606
- if (overlayField.required) {
3069
+ if (overlayField.required && !baseField.required) {
2607
3070
  throw new Error(
2608
3071
  `Mixed-authoring overlay for "${baseField.name}" cannot change requiredness; keep requiredness on the static model`
2609
3072
  );
@@ -2693,7 +3156,7 @@ function mergeAnnotations(baseAnnotations, overlayAnnotations) {
2693
3156
  const overlayOnly = overlayAnnotations.filter(
2694
3157
  (annotation) => !baseKeys.has(annotationKey(annotation))
2695
3158
  );
2696
- return [...overlayOnly, ...baseAnnotations];
3159
+ return [...baseAnnotations, ...overlayOnly];
2697
3160
  }
2698
3161
  function annotationKey(annotation) {
2699
3162
  return annotation.annotationKind === "custom" ? `${annotation.annotationKind}:${annotation.annotationId}` : annotation.annotationKind;