@formspec/build 0.1.0-alpha.32 → 0.1.0-alpha.34

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.
package/dist/internals.js CHANGED
@@ -730,6 +730,7 @@ import * as path from "path";
730
730
  // src/analyzer/class-analyzer.ts
731
731
  import * as ts3 from "typescript";
732
732
  import {
733
+ analyzeMetadataForNodeWithChecker,
733
734
  parseCommentBlock as parseCommentBlock2
734
735
  } from "@formspec/analysis/internal";
735
736
 
@@ -743,6 +744,7 @@ import {
743
744
  extractPathTarget as extractSharedPathTarget,
744
745
  getTagDefinition,
745
746
  hasTypeSemanticCapability,
747
+ normalizeFormSpecTagName,
746
748
  parseConstraintTagValue,
747
749
  parseDefaultValueTagValue,
748
750
  resolveDeclarationPlacement,
@@ -779,7 +781,7 @@ function createFormSpecTSDocConfig(extensionTagNames = []) {
779
781
  })
780
782
  );
781
783
  }
782
- for (const tagName of ["displayName", "format", "placeholder"]) {
784
+ for (const tagName of ["apiName", "displayName", "format", "placeholder"]) {
783
785
  config.addTagDefinition(
784
786
  new TSDocTagDefinition({
785
787
  tagName: "@" + tagName,
@@ -813,6 +815,16 @@ function sharedTagValueOptions(options) {
813
815
  };
814
816
  }
815
817
  var SYNTHETIC_TYPE_FORMAT_FLAGS = ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope;
818
+ function getExtensionTypeNames(registry) {
819
+ if (registry === void 0) {
820
+ return /* @__PURE__ */ new Set();
821
+ }
822
+ return new Set(
823
+ registry.extensions.flatMap(
824
+ (ext) => (ext.types ?? []).flatMap((t) => t.tsTypeNames ?? [t.typeName])
825
+ )
826
+ );
827
+ }
816
828
  function collectImportedNames(sourceFile) {
817
829
  const importedNames = /* @__PURE__ */ new Set();
818
830
  for (const statement of sourceFile.statements) {
@@ -852,6 +864,9 @@ function isNonReferenceIdentifier(node) {
852
864
  return false;
853
865
  }
854
866
  function statementReferencesImportedName(statement, importedNames) {
867
+ if (importedNames.size === 0) {
868
+ return false;
869
+ }
855
870
  let referencesImportedName = false;
856
871
  const visit = (node) => {
857
872
  if (referencesImportedName) {
@@ -866,14 +881,17 @@ function statementReferencesImportedName(statement, importedNames) {
866
881
  visit(statement);
867
882
  return referencesImportedName;
868
883
  }
869
- function buildSupportingDeclarations(sourceFile) {
884
+ function buildSupportingDeclarations(sourceFile, extensionTypeNames) {
870
885
  const importedNames = collectImportedNames(sourceFile);
886
+ const importedNamesToSkip = new Set(
887
+ [...importedNames].filter((name) => !extensionTypeNames.has(name))
888
+ );
871
889
  return sourceFile.statements.filter((statement) => {
872
890
  if (ts.isImportDeclaration(statement)) return false;
873
891
  if (ts.isImportEqualsDeclaration(statement)) return false;
874
892
  if (ts.isExportDeclaration(statement) && statement.moduleSpecifier !== void 0)
875
893
  return false;
876
- if (importedNames.size > 0 && statementReferencesImportedName(statement, importedNames)) {
894
+ if (statementReferencesImportedName(statement, importedNamesToSkip)) {
877
895
  return false;
878
896
  }
879
897
  return true;
@@ -1130,6 +1148,14 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
1130
1148
  extensionId: extension.extensionId,
1131
1149
  ...extension.constraintTags !== void 0 ? {
1132
1150
  constraintTags: extension.constraintTags.map((tag) => ({ tagName: tag.tagName }))
1151
+ } : {},
1152
+ ...extension.metadataSlots !== void 0 ? {
1153
+ metadataSlots: extension.metadataSlots
1154
+ } : {},
1155
+ ...extension.types !== void 0 ? {
1156
+ customTypes: extension.types.map((t) => ({
1157
+ tsTypeNames: t.tsTypeNames ?? [t.typeName]
1158
+ }))
1133
1159
  } : {}
1134
1160
  }))
1135
1161
  } : {}
@@ -1151,7 +1177,10 @@ var parseResultCache = /* @__PURE__ */ new Map();
1151
1177
  function getParser(options) {
1152
1178
  const extensionTagNames = [
1153
1179
  ...options?.extensionRegistry?.extensions.flatMap(
1154
- (extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
1180
+ (extension) => (extension.constraintTags ?? []).map((tag) => normalizeFormSpecTagName(tag.tagName))
1181
+ ) ?? [],
1182
+ ...options?.extensionRegistry?.extensions.flatMap(
1183
+ (extension) => (extension.metadataSlots ?? []).map((slot) => normalizeFormSpecTagName(slot.tagName))
1155
1184
  ) ?? []
1156
1185
  ].sort();
1157
1186
  const cacheKey = extensionTagNames.join("|");
@@ -1171,7 +1200,16 @@ function getExtensionRegistryCacheKey(registry) {
1171
1200
  (extension) => JSON.stringify({
1172
1201
  extensionId: extension.extensionId,
1173
1202
  typeNames: extension.types?.map((type) => type.typeName) ?? [],
1174
- constraintTags: extension.constraintTags?.map((tag) => tag.tagName) ?? []
1203
+ constraintTags: extension.constraintTags?.map((tag) => normalizeFormSpecTagName(tag.tagName)) ?? [],
1204
+ metadataSlots: extension.metadataSlots?.map((slot) => ({
1205
+ tagName: normalizeFormSpecTagName(slot.tagName),
1206
+ declarationKinds: [...slot.declarationKinds].sort(),
1207
+ allowBare: slot.allowBare !== false,
1208
+ qualifiers: (slot.qualifiers ?? []).map((qualifier) => ({
1209
+ qualifier: qualifier.qualifier,
1210
+ ...qualifier.sourceQualifier !== void 0 ? { sourceQualifier: qualifier.sourceQualifier } : {}
1211
+ })).sort((left, right) => left.qualifier.localeCompare(right.qualifier))
1212
+ })) ?? []
1175
1213
  })
1176
1214
  ).join("|");
1177
1215
  }
@@ -1206,7 +1244,8 @@ function parseTSDocTags(node, file = "", options) {
1206
1244
  const rawTextTags = [];
1207
1245
  const sourceFile = node.getSourceFile();
1208
1246
  const sourceText = sourceFile.getFullText();
1209
- const supportingDeclarations = buildSupportingDeclarations(sourceFile);
1247
+ const extensionTypeNames = getExtensionTypeNames(options?.extensionRegistry);
1248
+ const supportingDeclarations = buildSupportingDeclarations(sourceFile, extensionTypeNames);
1210
1249
  const commentRanges = ts.getLeadingCommentRanges(sourceText, node.getFullStart());
1211
1250
  const rawTextFallbacks = collectRawTextFallbacks(node, file);
1212
1251
  if (commentRanges) {
@@ -1614,6 +1653,9 @@ function extractDefaultValueAnnotation(initializer, file = "") {
1614
1653
  function isObjectType(type) {
1615
1654
  return !!(type.flags & ts3.TypeFlags.Object);
1616
1655
  }
1656
+ function isIntersectionType(type) {
1657
+ return !!(type.flags & ts3.TypeFlags.Intersection);
1658
+ }
1617
1659
  function isTypeReference(type) {
1618
1660
  return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
1619
1661
  }
@@ -1634,76 +1676,54 @@ function makeParseOptions(extensionRegistry, fieldType, checker, subjectType, ho
1634
1676
  ...hostType !== void 0 && { hostType }
1635
1677
  };
1636
1678
  }
1637
- function makeExplicitScalarMetadata(value) {
1638
- return value === void 0 || value === "" ? void 0 : { value, source: "explicit" };
1639
- }
1640
- function extractExplicitMetadata(node) {
1641
- let apiName;
1642
- let displayName;
1643
- let apiNamePlural;
1644
- let displayNamePlural;
1645
- for (const tag of getLeadingParsedTags(node)) {
1646
- const value = tag.argumentText.trim();
1647
- if (value === "") {
1648
- continue;
1649
- }
1650
- if (tag.normalizedTagName === "apiName") {
1651
- if (tag.target === null) {
1652
- apiName ??= value;
1653
- } else if (tag.target.kind === "variant") {
1654
- if (tag.target.rawText === "singular") {
1655
- apiName ??= value;
1656
- } else if (tag.target.rawText === "plural") {
1657
- apiNamePlural ??= value;
1658
- }
1659
- }
1660
- continue;
1661
- }
1662
- if (tag.normalizedTagName === "displayName") {
1663
- if (tag.target === null) {
1664
- displayName ??= value;
1665
- } else if (tag.target.kind === "variant") {
1666
- if (tag.target.rawText === "singular") {
1667
- displayName ??= value;
1668
- } else if (tag.target.rawText === "plural") {
1669
- displayNamePlural ??= value;
1670
- }
1671
- }
1672
- }
1673
- }
1674
- const resolvedApiName = makeExplicitScalarMetadata(apiName);
1675
- const resolvedDisplayName = makeExplicitScalarMetadata(displayName);
1676
- const resolvedApiNamePlural = makeExplicitScalarMetadata(apiNamePlural);
1677
- const resolvedDisplayNamePlural = makeExplicitScalarMetadata(displayNamePlural);
1678
- const metadata = {
1679
- ...resolvedApiName !== void 0 && { apiName: resolvedApiName },
1680
- ...resolvedDisplayName !== void 0 && { displayName: resolvedDisplayName },
1681
- ...resolvedApiNamePlural !== void 0 && { apiNamePlural: resolvedApiNamePlural },
1682
- ...resolvedDisplayNamePlural !== void 0 && {
1683
- displayNamePlural: resolvedDisplayNamePlural
1684
- }
1679
+ function createAnalyzerMetadataPolicy(input, discriminator) {
1680
+ return {
1681
+ raw: input,
1682
+ normalized: normalizeMetadataPolicy(input),
1683
+ discriminator
1685
1684
  };
1686
- return Object.keys(metadata).length === 0 ? void 0 : metadata;
1687
1685
  }
1688
- function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, buildContext) {
1689
- const explicit = extractExplicitMetadata(node);
1690
- return resolveMetadata(
1691
- {
1692
- ...explicit?.apiName !== void 0 && { apiName: explicit.apiName.value },
1693
- ...explicit?.displayName !== void 0 && { displayName: explicit.displayName.value },
1694
- ...explicit?.apiNamePlural !== void 0 && {
1695
- apiNamePlural: explicit.apiNamePlural.value
1696
- },
1697
- ...explicit?.displayNamePlural !== void 0 && {
1698
- displayNamePlural: explicit.displayNamePlural.value
1699
- }
1700
- },
1701
- getDeclarationMetadataPolicy(metadataPolicy, declarationKind),
1702
- makeMetadataContext("tsdoc", declarationKind, logicalName, buildContext)
1686
+ function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, checker, extensionRegistry, buildContext) {
1687
+ const analysis = analyzeMetadataForNodeWithChecker({
1688
+ checker,
1689
+ node,
1690
+ logicalName,
1691
+ metadata: metadataPolicy.raw,
1692
+ extensions: extensionRegistry?.extensions,
1693
+ ...buildContext !== void 0 && { buildContext }
1694
+ });
1695
+ const resolvedMetadata = analysis?.resolvedMetadata;
1696
+ const declarationPolicy = getDeclarationMetadataPolicy(
1697
+ metadataPolicy.normalized,
1698
+ declarationKind
1703
1699
  );
1700
+ if (resolvedMetadata?.apiName === void 0 && declarationPolicy.apiName.mode === "require-explicit") {
1701
+ throw new Error(
1702
+ `Metadata policy requires explicit apiName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
1703
+ );
1704
+ }
1705
+ if (resolvedMetadata?.displayName === void 0 && declarationPolicy.displayName.mode === "require-explicit") {
1706
+ throw new Error(
1707
+ `Metadata policy requires explicit displayName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
1708
+ );
1709
+ }
1710
+ if (resolvedMetadata?.apiNamePlural === void 0 && declarationPolicy.apiName.pluralization.mode === "require-explicit") {
1711
+ throw new Error(
1712
+ `Metadata policy requires explicit apiNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
1713
+ );
1714
+ }
1715
+ if (resolvedMetadata?.displayNamePlural === void 0 && declarationPolicy.displayName.pluralization.mode === "require-explicit") {
1716
+ throw new Error(
1717
+ `Metadata policy requires explicit displayNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
1718
+ );
1719
+ }
1720
+ return resolvedMetadata;
1704
1721
  }
1705
- function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy) {
1706
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
1722
+ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
1723
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
1724
+ metadataPolicy,
1725
+ discriminatorOptions
1726
+ );
1707
1727
  const name = classDecl.name?.text ?? "AnonymousClass";
1708
1728
  const fields = [];
1709
1729
  const fieldLayouts = [];
@@ -1758,12 +1778,20 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
1758
1778
  diagnostics,
1759
1779
  normalizedMetadataPolicy
1760
1780
  );
1761
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, classDecl, {
1781
+ const metadata = resolveNodeMetadata(
1782
+ normalizedMetadataPolicy,
1783
+ "type",
1784
+ name,
1785
+ classDecl,
1762
1786
  checker,
1763
- declaration: classDecl,
1764
- subjectType: classType,
1765
- hostType: classType
1766
- });
1787
+ extensionRegistry,
1788
+ {
1789
+ checker,
1790
+ declaration: classDecl,
1791
+ subjectType: classType,
1792
+ hostType: classType
1793
+ }
1794
+ );
1767
1795
  return {
1768
1796
  name,
1769
1797
  ...metadata !== void 0 && { metadata },
@@ -1776,8 +1804,11 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
1776
1804
  staticMethods
1777
1805
  };
1778
1806
  }
1779
- function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy) {
1780
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
1807
+ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
1808
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
1809
+ metadataPolicy,
1810
+ discriminatorOptions
1811
+ );
1781
1812
  const name = interfaceDecl.name.text;
1782
1813
  const fields = [];
1783
1814
  const typeRegistry = {};
@@ -1819,12 +1850,20 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
1819
1850
  normalizedMetadataPolicy
1820
1851
  );
1821
1852
  const fieldLayouts = specializedFields.map(() => ({}));
1822
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, interfaceDecl, {
1853
+ const metadata = resolveNodeMetadata(
1854
+ normalizedMetadataPolicy,
1855
+ "type",
1856
+ name,
1857
+ interfaceDecl,
1823
1858
  checker,
1824
- declaration: interfaceDecl,
1825
- subjectType: interfaceType,
1826
- hostType: interfaceType
1827
- });
1859
+ extensionRegistry,
1860
+ {
1861
+ checker,
1862
+ declaration: interfaceDecl,
1863
+ subjectType: interfaceType,
1864
+ hostType: interfaceType
1865
+ }
1866
+ );
1828
1867
  return {
1829
1868
  name,
1830
1869
  ...metadata !== void 0 && { metadata },
@@ -1837,19 +1876,31 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
1837
1876
  staticMethods: []
1838
1877
  };
1839
1878
  }
1840
- function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry, metadataPolicy) {
1841
- if (!ts3.isTypeLiteralNode(typeAlias.type)) {
1879
+ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
1880
+ const members = getObjectLikeTypeAliasMembers(typeAlias.type);
1881
+ if (members === null) {
1842
1882
  const sourceFile = typeAlias.getSourceFile();
1843
1883
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
1844
1884
  const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
1845
1885
  return {
1846
1886
  ok: false,
1847
- error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
1887
+ error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
1848
1888
  };
1849
1889
  }
1850
- const typeLiteral = typeAlias.type;
1851
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
1890
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
1891
+ metadataPolicy,
1892
+ discriminatorOptions
1893
+ );
1852
1894
  const name = typeAlias.name.text;
1895
+ const duplicatePropertyNames = findDuplicateObjectLikeTypeAliasPropertyNames(members);
1896
+ if (duplicatePropertyNames.length > 0) {
1897
+ const sourceFile = typeAlias.getSourceFile();
1898
+ const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
1899
+ return {
1900
+ ok: false,
1901
+ error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
1902
+ };
1903
+ }
1853
1904
  const fields = [];
1854
1905
  const typeRegistry = {};
1855
1906
  const diagnostics = [];
@@ -1862,7 +1913,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
1862
1913
  const annotations = [...typeAliasDoc.annotations];
1863
1914
  diagnostics.push(...typeAliasDoc.diagnostics);
1864
1915
  const visiting = /* @__PURE__ */ new Set();
1865
- for (const member of typeLiteral.members) {
1916
+ for (const member of members) {
1866
1917
  if (ts3.isPropertySignature(member)) {
1867
1918
  const fieldNode = analyzeInterfacePropertyToIR(
1868
1919
  member,
@@ -1889,12 +1940,20 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
1889
1940
  diagnostics,
1890
1941
  normalizedMetadataPolicy
1891
1942
  );
1892
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, typeAlias, {
1943
+ const metadata = resolveNodeMetadata(
1944
+ normalizedMetadataPolicy,
1945
+ "type",
1946
+ name,
1947
+ typeAlias,
1893
1948
  checker,
1894
- declaration: typeAlias,
1895
- subjectType: aliasType,
1896
- hostType: aliasType
1897
- });
1949
+ extensionRegistry,
1950
+ {
1951
+ checker,
1952
+ declaration: typeAlias,
1953
+ subjectType: aliasType,
1954
+ hostType: aliasType
1955
+ }
1956
+ );
1898
1957
  return {
1899
1958
  ok: true,
1900
1959
  analysis: {
@@ -1963,15 +2022,43 @@ function isNullishSemanticType(type) {
1963
2022
  }
1964
2023
  return type.isUnion() && type.types.some((member) => isNullishSemanticType(member));
1965
2024
  }
1966
- function isStringLikeSemanticType(type) {
2025
+ function isStringLikeSemanticType(type, checker, seen = /* @__PURE__ */ new Set()) {
2026
+ if (seen.has(type)) {
2027
+ return false;
2028
+ }
2029
+ seen.add(type);
1967
2030
  if (type.flags & ts3.TypeFlags.StringLike) {
1968
2031
  return true;
1969
2032
  }
1970
2033
  if (type.isUnion()) {
1971
- return type.types.length > 0 && type.types.every((member) => isStringLikeSemanticType(member));
2034
+ return type.types.length > 0 && type.types.every((member) => isStringLikeSemanticType(member, checker, seen));
2035
+ }
2036
+ const baseConstraint = checker.getBaseConstraintOfType(type);
2037
+ if (baseConstraint !== void 0 && baseConstraint !== type) {
2038
+ return isStringLikeSemanticType(baseConstraint, checker, seen);
1972
2039
  }
1973
2040
  return false;
1974
2041
  }
2042
+ function getObjectLikeTypeAliasMembers(typeNode) {
2043
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
2044
+ return getObjectLikeTypeAliasMembers(typeNode.type);
2045
+ }
2046
+ if (ts3.isTypeLiteralNode(typeNode)) {
2047
+ return [...typeNode.members];
2048
+ }
2049
+ if (ts3.isIntersectionTypeNode(typeNode)) {
2050
+ const members = [];
2051
+ for (const intersectionMember of typeNode.types) {
2052
+ const resolvedMembers = getObjectLikeTypeAliasMembers(intersectionMember);
2053
+ if (resolvedMembers === null) {
2054
+ return null;
2055
+ }
2056
+ members.push(...resolvedMembers);
2057
+ }
2058
+ return members;
2059
+ }
2060
+ return null;
2061
+ }
1975
2062
  function extractDiscriminatorDirective(node, file, diagnostics) {
1976
2063
  const discriminatorTags = getLeadingParsedTags(node).filter(
1977
2064
  (tag) => tag.normalizedTagName === "discriminator"
@@ -2078,7 +2165,7 @@ function validateDiscriminatorDirective(node, checker, file, diagnostics) {
2078
2165
  );
2079
2166
  return null;
2080
2167
  }
2081
- if (!isStringLikeSemanticType(property.type)) {
2168
+ if (!isStringLikeSemanticType(property.type, checker)) {
2082
2169
  diagnostics.push(
2083
2170
  makeAnalysisDiagnostic(
2084
2171
  "TYPE_MISMATCH",
@@ -2105,8 +2192,8 @@ function getConcreteTypeArgumentForDiscriminator(node, subjectType, checker, typ
2105
2192
  const localTypeParameter = node.typeParameters?.[typeParameterIndex];
2106
2193
  return localTypeParameter === void 0 ? null : checker.getTypeAtLocation(localTypeParameter);
2107
2194
  }
2108
- function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker, provenance, diagnostics) {
2109
- const propertySymbol = boundType.getProperty(fieldName);
2195
+ function resolveLiteralDiscriminatorPropertyValue(boundType, propertyName, checker, provenance, diagnostics) {
2196
+ const propertySymbol = boundType.getProperty(propertyName);
2110
2197
  if (propertySymbol === void 0) {
2111
2198
  return void 0;
2112
2199
  }
@@ -2137,6 +2224,9 @@ function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker,
2137
2224
  }
2138
2225
  return void 0;
2139
2226
  }
2227
+ function getDiscriminatorIdentityPropertyNames(fieldName) {
2228
+ return fieldName === "object" ? ["object"] : [fieldName, "object"];
2229
+ }
2140
2230
  function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
2141
2231
  const declaration = resolveNamedDiscriminatorDeclaration(boundType, checker);
2142
2232
  if (declaration === null) {
@@ -2147,6 +2237,8 @@ function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
2147
2237
  "type",
2148
2238
  getDiscriminatorLogicalName(boundType, declaration, checker),
2149
2239
  declaration,
2240
+ checker,
2241
+ void 0,
2150
2242
  {
2151
2243
  checker,
2152
2244
  declaration,
@@ -2155,6 +2247,10 @@ function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
2155
2247
  );
2156
2248
  return metadata?.apiName;
2157
2249
  }
2250
+ function applyDiscriminatorApiNamePrefix(value, discriminatorOptions) {
2251
+ const prefix = discriminatorOptions?.apiNamePrefix;
2252
+ return prefix === void 0 || prefix === "" ? value : `${prefix}${value}`;
2253
+ }
2158
2254
  function resolveNamedDiscriminatorDeclaration(type, checker, seen = /* @__PURE__ */ new Set()) {
2159
2255
  if (seen.has(type)) {
2160
2256
  return null;
@@ -2209,22 +2305,27 @@ function resolveDiscriminatorValue(boundType, fieldName, checker, provenance, di
2209
2305
  return null;
2210
2306
  }
2211
2307
  }
2212
- const literalIdentityValue = resolveLiteralDiscriminatorPropertyValue(
2213
- boundType,
2214
- fieldName,
2215
- checker,
2216
- provenance,
2217
- diagnostics
2218
- );
2219
- if (literalIdentityValue !== void 0) {
2220
- return literalIdentityValue;
2308
+ for (const identityPropertyName of getDiscriminatorIdentityPropertyNames(fieldName)) {
2309
+ const literalIdentityValue = resolveLiteralDiscriminatorPropertyValue(
2310
+ boundType,
2311
+ identityPropertyName,
2312
+ checker,
2313
+ provenance,
2314
+ diagnostics
2315
+ );
2316
+ if (literalIdentityValue === null) {
2317
+ return null;
2318
+ }
2319
+ if (literalIdentityValue !== void 0) {
2320
+ return literalIdentityValue;
2321
+ }
2221
2322
  }
2222
2323
  const apiName = resolveDiscriminatorApiName(boundType, checker, metadataPolicy);
2223
2324
  if (apiName?.source === "explicit") {
2224
- return apiName.value;
2325
+ return applyDiscriminatorApiNamePrefix(apiName.value, metadataPolicy.discriminator);
2225
2326
  }
2226
2327
  if (apiName?.source === "inferred") {
2227
- return apiName.value;
2328
+ return applyDiscriminatorApiNamePrefix(apiName.value, metadataPolicy.discriminator);
2228
2329
  }
2229
2330
  diagnostics.push(
2230
2331
  makeAnalysisDiagnostic(
@@ -2287,15 +2388,20 @@ function buildInstantiatedReferenceName(baseName, typeArguments, checker) {
2287
2388
  return renderedArguments.length === 0 ? baseName : `${baseName}__${renderedArguments.join("__")}`;
2288
2389
  }
2289
2390
  function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy, extensionRegistry, diagnostics) {
2290
- const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
2291
- if (typeNode === void 0) {
2391
+ const sourceTypeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
2392
+ if (sourceTypeNode === void 0) {
2292
2393
  return [];
2293
2394
  }
2294
- const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
2295
- if (!ts3.isTypeReferenceNode(resolvedTypeNode) || resolvedTypeNode.typeArguments === void 0) {
2395
+ const unwrapParentheses = (typeNode) => ts3.isParenthesizedTypeNode(typeNode) ? unwrapParentheses(typeNode.type) : typeNode;
2396
+ const directTypeNode = unwrapParentheses(sourceTypeNode);
2397
+ const referenceTypeNode = ts3.isTypeReferenceNode(directTypeNode) ? directTypeNode : (() => {
2398
+ const resolvedTypeNode = resolveAliasedTypeNode(directTypeNode, checker);
2399
+ return ts3.isTypeReferenceNode(resolvedTypeNode) ? resolvedTypeNode : null;
2400
+ })();
2401
+ if (referenceTypeNode?.typeArguments === void 0) {
2296
2402
  return [];
2297
2403
  }
2298
- return resolvedTypeNode.typeArguments.map((argumentNode) => {
2404
+ return referenceTypeNode.typeArguments.map((argumentNode) => {
2299
2405
  const argumentType = checker.getTypeFromTypeNode(argumentNode);
2300
2406
  return {
2301
2407
  tsType: argumentType,
@@ -2383,12 +2489,20 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
2383
2489
  annotations.push(defaultAnnotation);
2384
2490
  }
2385
2491
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
2386
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
2492
+ const metadata = resolveNodeMetadata(
2493
+ metadataPolicy,
2494
+ "field",
2495
+ name,
2496
+ prop,
2387
2497
  checker,
2388
- declaration: prop,
2389
- subjectType: tsType,
2390
- hostType
2391
- });
2498
+ extensionRegistry,
2499
+ {
2500
+ checker,
2501
+ declaration: prop,
2502
+ subjectType: tsType,
2503
+ hostType
2504
+ }
2505
+ );
2392
2506
  return {
2393
2507
  kind: "field",
2394
2508
  name,
@@ -2401,10 +2515,10 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
2401
2515
  };
2402
2516
  }
2403
2517
  function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
2404
- if (!ts3.isIdentifier(prop.name)) {
2518
+ const name = getAnalyzableObjectLikePropertyName(prop.name);
2519
+ if (name === null) {
2405
2520
  return null;
2406
2521
  }
2407
- const name = prop.name.text;
2408
2522
  const tsType = checker.getTypeAtLocation(prop);
2409
2523
  const optional = prop.questionToken !== void 0;
2410
2524
  const provenance = provenanceForNode(prop, file);
@@ -2435,12 +2549,20 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
2435
2549
  let annotations = [];
2436
2550
  annotations.push(...docResult.annotations);
2437
2551
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
2438
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
2552
+ const metadata = resolveNodeMetadata(
2553
+ metadataPolicy,
2554
+ "field",
2555
+ name,
2556
+ prop,
2439
2557
  checker,
2440
- declaration: prop,
2441
- subjectType: tsType,
2442
- hostType
2443
- });
2558
+ extensionRegistry,
2559
+ {
2560
+ checker,
2561
+ declaration: prop,
2562
+ subjectType: tsType,
2563
+ hostType
2564
+ }
2565
+ );
2444
2566
  return {
2445
2567
  kind: "field",
2446
2568
  name,
@@ -2452,6 +2574,31 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
2452
2574
  provenance
2453
2575
  };
2454
2576
  }
2577
+ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
2578
+ const seen = /* @__PURE__ */ new Set();
2579
+ const duplicates = /* @__PURE__ */ new Set();
2580
+ for (const member of members) {
2581
+ if (!ts3.isPropertySignature(member)) {
2582
+ continue;
2583
+ }
2584
+ const name = getAnalyzableObjectLikePropertyName(member.name);
2585
+ if (name === null) {
2586
+ continue;
2587
+ }
2588
+ if (seen.has(name)) {
2589
+ duplicates.add(name);
2590
+ continue;
2591
+ }
2592
+ seen.add(name);
2593
+ }
2594
+ return [...duplicates].sort();
2595
+ }
2596
+ function getAnalyzableObjectLikePropertyName(name) {
2597
+ if (!ts3.isIdentifier(name)) {
2598
+ return null;
2599
+ }
2600
+ return name.text;
2601
+ }
2455
2602
  function applyEnumMemberDisplayNames(type, annotations) {
2456
2603
  if (!annotations.some(
2457
2604
  (annotation) => annotation.annotationKind === "displayName" && annotation.value.trim().startsWith(":")
@@ -2569,7 +2716,7 @@ function getTypeNodeRegistrationName(typeNode) {
2569
2716
  }
2570
2717
  return null;
2571
2718
  }
2572
- function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2719
+ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2573
2720
  const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
2574
2721
  if (customType) {
2575
2722
  return customType;
@@ -2644,6 +2791,23 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
2644
2791
  diagnostics
2645
2792
  );
2646
2793
  }
2794
+ if (isIntersectionType(type)) {
2795
+ const sourceTypeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
2796
+ const resolvedSourceTypeNode = sourceTypeNode === void 0 ? void 0 : resolveAliasedTypeNode(sourceTypeNode, checker);
2797
+ if (resolvedSourceTypeNode !== void 0 && getObjectLikeTypeAliasMembers(resolvedSourceTypeNode) !== null) {
2798
+ return resolveObjectType(
2799
+ type,
2800
+ checker,
2801
+ file,
2802
+ typeRegistry,
2803
+ visiting,
2804
+ sourceNode,
2805
+ metadataPolicy,
2806
+ extensionRegistry,
2807
+ diagnostics
2808
+ );
2809
+ }
2810
+ }
2647
2811
  if (isObjectType(type)) {
2648
2812
  return resolveObjectType(
2649
2813
  type,
@@ -2659,7 +2823,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
2659
2823
  }
2660
2824
  return { kind: "primitive", primitiveKind: "string" };
2661
2825
  }
2662
- function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2826
+ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2663
2827
  if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
2664
2828
  return null;
2665
2829
  }
@@ -2679,11 +2843,19 @@ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiti
2679
2843
  file,
2680
2844
  makeParseOptions(extensionRegistry)
2681
2845
  );
2682
- const metadata = resolveNodeMetadata(metadataPolicy, "type", aliasName, aliasDecl, {
2846
+ const metadata = resolveNodeMetadata(
2847
+ metadataPolicy,
2848
+ "type",
2849
+ aliasName,
2850
+ aliasDecl,
2683
2851
  checker,
2684
- declaration: aliasDecl,
2685
- subjectType: aliasType
2686
- });
2852
+ extensionRegistry,
2853
+ {
2854
+ checker,
2855
+ declaration: aliasDecl,
2856
+ subjectType: aliasType
2857
+ }
2858
+ );
2687
2859
  typeRegistry[aliasName] = {
2688
2860
  name: aliasName,
2689
2861
  ...metadata !== void 0 && { metadata },
@@ -2722,7 +2894,7 @@ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
2722
2894
  const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
2723
2895
  return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
2724
2896
  }
2725
- function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2897
+ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2726
2898
  const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
2727
2899
  if (nestedAliasDecl !== void 0) {
2728
2900
  return resolveAliasedPrimitiveTarget(
@@ -2748,7 +2920,7 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
2748
2920
  diagnostics
2749
2921
  );
2750
2922
  }
2751
- function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2923
+ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2752
2924
  const typeName = getNamedTypeName(type);
2753
2925
  const namedDecl = getNamedTypeDeclaration(type);
2754
2926
  if (typeName && typeName in typeRegistry) {
@@ -2783,11 +2955,19 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2783
2955
  return result;
2784
2956
  }
2785
2957
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2786
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", typeName, namedDecl, {
2958
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
2959
+ metadataPolicy,
2960
+ "type",
2961
+ typeName,
2962
+ namedDecl,
2787
2963
  checker,
2788
- declaration: namedDecl,
2789
- subjectType: type
2790
- }) : void 0;
2964
+ extensionRegistry,
2965
+ {
2966
+ checker,
2967
+ declaration: namedDecl,
2968
+ subjectType: type
2969
+ }
2970
+ ) : void 0;
2791
2971
  typeRegistry[typeName] = {
2792
2972
  name: typeName,
2793
2973
  ...metadata !== void 0 && { metadata },
@@ -2872,7 +3052,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2872
3052
  }
2873
3053
  return registerNamed({ kind: "union", members });
2874
3054
  }
2875
- function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3055
+ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2876
3056
  const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
2877
3057
  const elementType = typeArgs?.[0];
2878
3058
  const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
@@ -2889,7 +3069,7 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNod
2889
3069
  ) : { kind: "primitive", primitiveKind: "string" };
2890
3070
  return { kind: "array", items };
2891
3071
  }
2892
- function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3072
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2893
3073
  if (type.getProperties().length > 0) {
2894
3074
  return null;
2895
3075
  }
@@ -2950,7 +3130,7 @@ function shouldEmitResolvedObjectProperty(property, declaration) {
2950
3130
  }
2951
3131
  return true;
2952
3132
  }
2953
- function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3133
+ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2954
3134
  const collectedDiagnostics = diagnostics ?? [];
2955
3135
  const typeName = getNamedTypeName(type);
2956
3136
  const namedTypeName = typeName ?? void 0;
@@ -3007,7 +3187,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3007
3187
  };
3008
3188
  }
3009
3189
  }
3010
- const recordNode = tryResolveRecordType(
3190
+ const recordNode = isObjectType(type) ? tryResolveRecordType(
3011
3191
  type,
3012
3192
  checker,
3013
3193
  file,
@@ -3016,7 +3196,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3016
3196
  metadataPolicy,
3017
3197
  extensionRegistry,
3018
3198
  collectedDiagnostics
3019
- );
3199
+ ) : null;
3020
3200
  if (recordNode) {
3021
3201
  visiting.delete(type);
3022
3202
  if (registryTypeName !== void 0 && shouldRegisterNamedType) {
@@ -3026,11 +3206,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3026
3206
  return recordNode;
3027
3207
  }
3028
3208
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
3029
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
3209
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
3210
+ metadataPolicy,
3211
+ "type",
3212
+ registryTypeName,
3213
+ namedDecl,
3030
3214
  checker,
3031
- declaration: namedDecl,
3032
- subjectType: type
3033
- }) : void 0;
3215
+ extensionRegistry,
3216
+ {
3217
+ checker,
3218
+ declaration: namedDecl,
3219
+ subjectType: type
3220
+ }
3221
+ ) : void 0;
3034
3222
  typeRegistry[registryTypeName] = {
3035
3223
  name: registryTypeName,
3036
3224
  ...metadata !== void 0 && { metadata },
@@ -3126,11 +3314,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3126
3314
  };
3127
3315
  if (registryTypeName !== void 0 && shouldRegisterNamedType) {
3128
3316
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
3129
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
3317
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
3318
+ metadataPolicy,
3319
+ "type",
3320
+ registryTypeName,
3321
+ namedDecl,
3130
3322
  checker,
3131
- declaration: namedDecl,
3132
- subjectType: type
3133
- }) : void 0;
3323
+ extensionRegistry,
3324
+ {
3325
+ checker,
3326
+ declaration: namedDecl,
3327
+ subjectType: type
3328
+ }
3329
+ ) : void 0;
3134
3330
  typeRegistry[registryTypeName] = {
3135
3331
  name: registryTypeName,
3136
3332
  ...metadata !== void 0 && { metadata },
@@ -3197,9 +3393,10 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
3197
3393
  );
3198
3394
  }
3199
3395
  const typeAliasDecl = declarations.find(ts3.isTypeAliasDeclaration);
3200
- if (typeAliasDecl && ts3.isTypeLiteralNode(typeAliasDecl.type)) {
3396
+ const typeAliasMembers = typeAliasDecl === void 0 ? null : getObjectLikeTypeAliasMembers(typeAliasDecl.type);
3397
+ if (typeAliasDecl && typeAliasMembers !== null) {
3201
3398
  return buildFieldNodeInfoMap(
3202
- typeAliasDecl.type.members,
3399
+ typeAliasMembers,
3203
3400
  checker,
3204
3401
  file,
3205
3402
  typeRegistry,
@@ -3490,7 +3687,7 @@ function findInterfaceByName(sourceFile, interfaceName) {
3490
3687
  function findTypeAliasByName(sourceFile, aliasName) {
3491
3688
  return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
3492
3689
  }
3493
- function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy) {
3690
+ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
3494
3691
  const analysisFilePath = path.resolve(filePath);
3495
3692
  const classDecl = findClassByName(ctx.sourceFile, typeName);
3496
3693
  if (classDecl !== null) {
@@ -3499,7 +3696,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
3499
3696
  ctx.checker,
3500
3697
  analysisFilePath,
3501
3698
  extensionRegistry,
3502
- metadataPolicy
3699
+ metadataPolicy,
3700
+ discriminatorOptions
3503
3701
  );
3504
3702
  }
3505
3703
  const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
@@ -3509,7 +3707,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
3509
3707
  ctx.checker,
3510
3708
  analysisFilePath,
3511
3709
  extensionRegistry,
3512
- metadataPolicy
3710
+ metadataPolicy,
3711
+ discriminatorOptions
3513
3712
  );
3514
3713
  }
3515
3714
  const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
@@ -3519,7 +3718,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
3519
3718
  ctx.checker,
3520
3719
  analysisFilePath,
3521
3720
  extensionRegistry,
3522
- metadataPolicy
3721
+ metadataPolicy,
3722
+ discriminatorOptions
3523
3723
  );
3524
3724
  if (result.ok) {
3525
3725
  return result.analysis;
@@ -4635,13 +4835,35 @@ function formatLocation(location) {
4635
4835
  }
4636
4836
 
4637
4837
  // src/extensions/registry.ts
4838
+ import {
4839
+ BUILTIN_CONSTRAINT_DEFINITIONS as BUILTIN_CONSTRAINT_DEFINITIONS2,
4840
+ normalizeConstraintTagName as normalizeConstraintTagName2
4841
+ } from "@formspec/core/internals";
4842
+ import {
4843
+ getTagDefinition as getTagDefinition2,
4844
+ normalizeFormSpecTagName as normalizeFormSpecTagName2
4845
+ } from "@formspec/analysis/internal";
4846
+ var BUILTIN_METADATA_TAGS = /* @__PURE__ */ new Set(["apiName", "displayName"]);
4847
+ function buildConstraintTagSources(extensions) {
4848
+ return extensions.map((extension) => ({
4849
+ extensionId: extension.extensionId,
4850
+ ...extension.constraintTags !== void 0 ? {
4851
+ constraintTags: extension.constraintTags.map((tag) => ({
4852
+ tagName: normalizeFormSpecTagName2(tag.tagName)
4853
+ }))
4854
+ } : {}
4855
+ }));
4856
+ }
4638
4857
  function createExtensionRegistry(extensions) {
4858
+ const reservedTagSources = buildConstraintTagSources(extensions);
4639
4859
  const typeMap = /* @__PURE__ */ new Map();
4640
4860
  const typeNameMap = /* @__PURE__ */ new Map();
4641
4861
  const constraintMap = /* @__PURE__ */ new Map();
4642
4862
  const constraintTagMap = /* @__PURE__ */ new Map();
4643
4863
  const builtinBroadeningMap = /* @__PURE__ */ new Map();
4644
4864
  const annotationMap = /* @__PURE__ */ new Map();
4865
+ const metadataSlotMap = /* @__PURE__ */ new Map();
4866
+ const metadataTagMap = /* @__PURE__ */ new Map();
4645
4867
  for (const ext of extensions) {
4646
4868
  if (ext.types !== void 0) {
4647
4869
  for (const type of ext.types) {
@@ -4684,10 +4906,11 @@ function createExtensionRegistry(extensions) {
4684
4906
  }
4685
4907
  if (ext.constraintTags !== void 0) {
4686
4908
  for (const tag of ext.constraintTags) {
4687
- if (constraintTagMap.has(tag.tagName)) {
4688
- throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
4909
+ const canonicalTagName = normalizeFormSpecTagName2(tag.tagName);
4910
+ if (constraintTagMap.has(canonicalTagName)) {
4911
+ throw new Error(`Duplicate custom constraint tag: "@${canonicalTagName}"`);
4689
4912
  }
4690
- constraintTagMap.set(tag.tagName, {
4913
+ constraintTagMap.set(canonicalTagName, {
4691
4914
  extensionId: ext.extensionId,
4692
4915
  registration: tag
4693
4916
  });
@@ -4702,13 +4925,54 @@ function createExtensionRegistry(extensions) {
4702
4925
  annotationMap.set(qualifiedId, annotation);
4703
4926
  }
4704
4927
  }
4928
+ if (ext.metadataSlots !== void 0) {
4929
+ for (const slot of ext.metadataSlots) {
4930
+ if (metadataSlotMap.has(slot.slotId)) {
4931
+ throw new Error(`Duplicate metadata slot ID: "${slot.slotId}"`);
4932
+ }
4933
+ metadataSlotMap.set(slot.slotId, true);
4934
+ const canonicalTagName = normalizeFormSpecTagName2(slot.tagName);
4935
+ if (slot.allowBare === false && (slot.qualifiers?.length ?? 0) === 0) {
4936
+ throw new Error(
4937
+ `Metadata tag "@${canonicalTagName}" must allow bare usage or declare at least one qualifier.`
4938
+ );
4939
+ }
4940
+ if (metadataTagMap.has(canonicalTagName)) {
4941
+ throw new Error(`Duplicate metadata tag: "@${canonicalTagName}"`);
4942
+ }
4943
+ if (BUILTIN_METADATA_TAGS.has(canonicalTagName)) {
4944
+ throw new Error(
4945
+ `Metadata tag "@${canonicalTagName}" conflicts with built-in metadata tags.`
4946
+ );
4947
+ }
4948
+ if (constraintTagMap.has(canonicalTagName)) {
4949
+ throw new Error(
4950
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${canonicalTagName}".`
4951
+ );
4952
+ }
4953
+ if (Object.hasOwn(BUILTIN_CONSTRAINT_DEFINITIONS2, normalizeConstraintTagName2(canonicalTagName))) {
4954
+ throw new Error(
4955
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${normalizeConstraintTagName2(canonicalTagName)}".`
4956
+ );
4957
+ }
4958
+ const existingTag = getTagDefinition2(canonicalTagName, reservedTagSources);
4959
+ if (existingTag !== null) {
4960
+ throw BUILTIN_METADATA_TAGS.has(existingTag.canonicalName) ? new Error(
4961
+ `Metadata tag "@${canonicalTagName}" conflicts with built-in metadata tags.`
4962
+ ) : new Error(
4963
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${existingTag.canonicalName}".`
4964
+ );
4965
+ }
4966
+ metadataTagMap.set(canonicalTagName, true);
4967
+ }
4968
+ }
4705
4969
  }
4706
4970
  return {
4707
4971
  extensions,
4708
4972
  findType: (typeId) => typeMap.get(typeId),
4709
4973
  findTypeByName: (typeName) => typeNameMap.get(typeName),
4710
4974
  findConstraint: (constraintId) => constraintMap.get(constraintId),
4711
- findConstraintTag: (tagName) => constraintTagMap.get(tagName),
4975
+ findConstraintTag: (tagName) => constraintTagMap.get(normalizeFormSpecTagName2(tagName)),
4712
4976
  findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
4713
4977
  findAnnotation: (annotationId) => annotationMap.get(annotationId)
4714
4978
  };