@formspec/build 0.1.0-alpha.31 → 0.1.0-alpha.33

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.
@@ -805,7 +805,7 @@ function createFormSpecTSDocConfig(extensionTagNames = []) {
805
805
  })
806
806
  );
807
807
  }
808
- for (const tagName of ["displayName", "format", "placeholder"]) {
808
+ for (const tagName of ["apiName", "displayName", "format", "placeholder"]) {
809
809
  config.addTagDefinition(
810
810
  new import_tsdoc.TSDocTagDefinition({
811
811
  tagName: "@" + tagName,
@@ -839,6 +839,16 @@ function sharedTagValueOptions(options) {
839
839
  };
840
840
  }
841
841
  var SYNTHETIC_TYPE_FORMAT_FLAGS = ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope;
842
+ function getExtensionTypeNames(registry) {
843
+ if (registry === void 0) {
844
+ return /* @__PURE__ */ new Set();
845
+ }
846
+ return new Set(
847
+ registry.extensions.flatMap(
848
+ (ext) => (ext.types ?? []).flatMap((t) => t.tsTypeNames ?? [t.typeName])
849
+ )
850
+ );
851
+ }
842
852
  function collectImportedNames(sourceFile) {
843
853
  const importedNames = /* @__PURE__ */ new Set();
844
854
  for (const statement of sourceFile.statements) {
@@ -878,6 +888,9 @@ function isNonReferenceIdentifier(node) {
878
888
  return false;
879
889
  }
880
890
  function statementReferencesImportedName(statement, importedNames) {
891
+ if (importedNames.size === 0) {
892
+ return false;
893
+ }
881
894
  let referencesImportedName = false;
882
895
  const visit = (node) => {
883
896
  if (referencesImportedName) {
@@ -892,14 +905,17 @@ function statementReferencesImportedName(statement, importedNames) {
892
905
  visit(statement);
893
906
  return referencesImportedName;
894
907
  }
895
- function buildSupportingDeclarations(sourceFile) {
908
+ function buildSupportingDeclarations(sourceFile, extensionTypeNames) {
896
909
  const importedNames = collectImportedNames(sourceFile);
910
+ const importedNamesToSkip = new Set(
911
+ [...importedNames].filter((name) => !extensionTypeNames.has(name))
912
+ );
897
913
  return sourceFile.statements.filter((statement) => {
898
914
  if (ts.isImportDeclaration(statement)) return false;
899
915
  if (ts.isImportEqualsDeclaration(statement)) return false;
900
916
  if (ts.isExportDeclaration(statement) && statement.moduleSpecifier !== void 0)
901
917
  return false;
902
- if (importedNames.size > 0 && statementReferencesImportedName(statement, importedNames)) {
918
+ if (statementReferencesImportedName(statement, importedNamesToSkip)) {
903
919
  return false;
904
920
  }
905
921
  return true;
@@ -1156,6 +1172,14 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
1156
1172
  extensionId: extension.extensionId,
1157
1173
  ...extension.constraintTags !== void 0 ? {
1158
1174
  constraintTags: extension.constraintTags.map((tag) => ({ tagName: tag.tagName }))
1175
+ } : {},
1176
+ ...extension.metadataSlots !== void 0 ? {
1177
+ metadataSlots: extension.metadataSlots
1178
+ } : {},
1179
+ ...extension.types !== void 0 ? {
1180
+ customTypes: extension.types.map((t) => ({
1181
+ tsTypeNames: t.tsTypeNames ?? [t.typeName]
1182
+ }))
1159
1183
  } : {}
1160
1184
  }))
1161
1185
  } : {}
@@ -1177,7 +1201,10 @@ var parseResultCache = /* @__PURE__ */ new Map();
1177
1201
  function getParser(options) {
1178
1202
  const extensionTagNames = [
1179
1203
  ...options?.extensionRegistry?.extensions.flatMap(
1180
- (extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
1204
+ (extension) => (extension.constraintTags ?? []).map((tag) => (0, import_internal.normalizeFormSpecTagName)(tag.tagName))
1205
+ ) ?? [],
1206
+ ...options?.extensionRegistry?.extensions.flatMap(
1207
+ (extension) => (extension.metadataSlots ?? []).map((slot) => (0, import_internal.normalizeFormSpecTagName)(slot.tagName))
1181
1208
  ) ?? []
1182
1209
  ].sort();
1183
1210
  const cacheKey = extensionTagNames.join("|");
@@ -1197,7 +1224,16 @@ function getExtensionRegistryCacheKey(registry) {
1197
1224
  (extension) => JSON.stringify({
1198
1225
  extensionId: extension.extensionId,
1199
1226
  typeNames: extension.types?.map((type) => type.typeName) ?? [],
1200
- constraintTags: extension.constraintTags?.map((tag) => tag.tagName) ?? []
1227
+ constraintTags: extension.constraintTags?.map((tag) => (0, import_internal.normalizeFormSpecTagName)(tag.tagName)) ?? [],
1228
+ metadataSlots: extension.metadataSlots?.map((slot) => ({
1229
+ tagName: (0, import_internal.normalizeFormSpecTagName)(slot.tagName),
1230
+ declarationKinds: [...slot.declarationKinds].sort(),
1231
+ allowBare: slot.allowBare !== false,
1232
+ qualifiers: (slot.qualifiers ?? []).map((qualifier) => ({
1233
+ qualifier: qualifier.qualifier,
1234
+ ...qualifier.sourceQualifier !== void 0 ? { sourceQualifier: qualifier.sourceQualifier } : {}
1235
+ })).sort((left, right) => left.qualifier.localeCompare(right.qualifier))
1236
+ })) ?? []
1201
1237
  })
1202
1238
  ).join("|");
1203
1239
  }
@@ -1232,7 +1268,8 @@ function parseTSDocTags(node, file = "", options) {
1232
1268
  const rawTextTags = [];
1233
1269
  const sourceFile = node.getSourceFile();
1234
1270
  const sourceText = sourceFile.getFullText();
1235
- const supportingDeclarations = buildSupportingDeclarations(sourceFile);
1271
+ const extensionTypeNames = getExtensionTypeNames(options?.extensionRegistry);
1272
+ const supportingDeclarations = buildSupportingDeclarations(sourceFile, extensionTypeNames);
1236
1273
  const commentRanges = ts.getLeadingCommentRanges(sourceText, node.getFullStart());
1237
1274
  const rawTextFallbacks = collectRawTextFallbacks(node, file);
1238
1275
  if (commentRanges) {
@@ -1660,76 +1697,50 @@ function makeParseOptions(extensionRegistry, fieldType, checker, subjectType, ho
1660
1697
  ...hostType !== void 0 && { hostType }
1661
1698
  };
1662
1699
  }
1663
- function makeExplicitScalarMetadata(value) {
1664
- return value === void 0 || value === "" ? void 0 : { value, source: "explicit" };
1665
- }
1666
- function extractExplicitMetadata(node) {
1667
- let apiName;
1668
- let displayName;
1669
- let apiNamePlural;
1670
- let displayNamePlural;
1671
- for (const tag of getLeadingParsedTags(node)) {
1672
- const value = tag.argumentText.trim();
1673
- if (value === "") {
1674
- continue;
1675
- }
1676
- if (tag.normalizedTagName === "apiName") {
1677
- if (tag.target === null) {
1678
- apiName ??= value;
1679
- } else if (tag.target.kind === "variant") {
1680
- if (tag.target.rawText === "singular") {
1681
- apiName ??= value;
1682
- } else if (tag.target.rawText === "plural") {
1683
- apiNamePlural ??= value;
1684
- }
1685
- }
1686
- continue;
1687
- }
1688
- if (tag.normalizedTagName === "displayName") {
1689
- if (tag.target === null) {
1690
- displayName ??= value;
1691
- } else if (tag.target.kind === "variant") {
1692
- if (tag.target.rawText === "singular") {
1693
- displayName ??= value;
1694
- } else if (tag.target.rawText === "plural") {
1695
- displayNamePlural ??= value;
1696
- }
1697
- }
1698
- }
1699
- }
1700
- const resolvedApiName = makeExplicitScalarMetadata(apiName);
1701
- const resolvedDisplayName = makeExplicitScalarMetadata(displayName);
1702
- const resolvedApiNamePlural = makeExplicitScalarMetadata(apiNamePlural);
1703
- const resolvedDisplayNamePlural = makeExplicitScalarMetadata(displayNamePlural);
1704
- const metadata = {
1705
- ...resolvedApiName !== void 0 && { apiName: resolvedApiName },
1706
- ...resolvedDisplayName !== void 0 && { displayName: resolvedDisplayName },
1707
- ...resolvedApiNamePlural !== void 0 && { apiNamePlural: resolvedApiNamePlural },
1708
- ...resolvedDisplayNamePlural !== void 0 && {
1709
- displayNamePlural: resolvedDisplayNamePlural
1710
- }
1700
+ function createAnalyzerMetadataPolicy(input) {
1701
+ return {
1702
+ raw: input,
1703
+ normalized: normalizeMetadataPolicy(input)
1711
1704
  };
1712
- return Object.keys(metadata).length === 0 ? void 0 : metadata;
1713
1705
  }
1714
- function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, buildContext) {
1715
- const explicit = extractExplicitMetadata(node);
1716
- return resolveMetadata(
1717
- {
1718
- ...explicit?.apiName !== void 0 && { apiName: explicit.apiName.value },
1719
- ...explicit?.displayName !== void 0 && { displayName: explicit.displayName.value },
1720
- ...explicit?.apiNamePlural !== void 0 && {
1721
- apiNamePlural: explicit.apiNamePlural.value
1722
- },
1723
- ...explicit?.displayNamePlural !== void 0 && {
1724
- displayNamePlural: explicit.displayNamePlural.value
1725
- }
1726
- },
1727
- getDeclarationMetadataPolicy(metadataPolicy, declarationKind),
1728
- makeMetadataContext("tsdoc", declarationKind, logicalName, buildContext)
1706
+ function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, checker, extensionRegistry, buildContext) {
1707
+ const analysis = (0, import_internal2.analyzeMetadataForNodeWithChecker)({
1708
+ checker,
1709
+ node,
1710
+ logicalName,
1711
+ metadata: metadataPolicy.raw,
1712
+ extensions: extensionRegistry?.extensions,
1713
+ ...buildContext !== void 0 && { buildContext }
1714
+ });
1715
+ const resolvedMetadata = analysis?.resolvedMetadata;
1716
+ const declarationPolicy = getDeclarationMetadataPolicy(
1717
+ metadataPolicy.normalized,
1718
+ declarationKind
1729
1719
  );
1720
+ if (resolvedMetadata?.apiName === void 0 && declarationPolicy.apiName.mode === "require-explicit") {
1721
+ throw new Error(
1722
+ `Metadata policy requires explicit apiName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
1723
+ );
1724
+ }
1725
+ if (resolvedMetadata?.displayName === void 0 && declarationPolicy.displayName.mode === "require-explicit") {
1726
+ throw new Error(
1727
+ `Metadata policy requires explicit displayName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
1728
+ );
1729
+ }
1730
+ if (resolvedMetadata?.apiNamePlural === void 0 && declarationPolicy.apiName.pluralization.mode === "require-explicit") {
1731
+ throw new Error(
1732
+ `Metadata policy requires explicit apiNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
1733
+ );
1734
+ }
1735
+ if (resolvedMetadata?.displayNamePlural === void 0 && declarationPolicy.displayName.pluralization.mode === "require-explicit") {
1736
+ throw new Error(
1737
+ `Metadata policy requires explicit displayNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
1738
+ );
1739
+ }
1740
+ return resolvedMetadata;
1730
1741
  }
1731
1742
  function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy) {
1732
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
1743
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
1733
1744
  const name = classDecl.name?.text ?? "AnonymousClass";
1734
1745
  const fields = [];
1735
1746
  const fieldLayouts = [];
@@ -1784,12 +1795,20 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
1784
1795
  diagnostics,
1785
1796
  normalizedMetadataPolicy
1786
1797
  );
1787
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, classDecl, {
1798
+ const metadata = resolveNodeMetadata(
1799
+ normalizedMetadataPolicy,
1800
+ "type",
1801
+ name,
1802
+ classDecl,
1788
1803
  checker,
1789
- declaration: classDecl,
1790
- subjectType: classType,
1791
- hostType: classType
1792
- });
1804
+ extensionRegistry,
1805
+ {
1806
+ checker,
1807
+ declaration: classDecl,
1808
+ subjectType: classType,
1809
+ hostType: classType
1810
+ }
1811
+ );
1793
1812
  return {
1794
1813
  name,
1795
1814
  ...metadata !== void 0 && { metadata },
@@ -1803,7 +1822,7 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
1803
1822
  };
1804
1823
  }
1805
1824
  function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy) {
1806
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
1825
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
1807
1826
  const name = interfaceDecl.name.text;
1808
1827
  const fields = [];
1809
1828
  const typeRegistry = {};
@@ -1845,12 +1864,20 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
1845
1864
  normalizedMetadataPolicy
1846
1865
  );
1847
1866
  const fieldLayouts = specializedFields.map(() => ({}));
1848
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, interfaceDecl, {
1867
+ const metadata = resolveNodeMetadata(
1868
+ normalizedMetadataPolicy,
1869
+ "type",
1870
+ name,
1871
+ interfaceDecl,
1849
1872
  checker,
1850
- declaration: interfaceDecl,
1851
- subjectType: interfaceType,
1852
- hostType: interfaceType
1853
- });
1873
+ extensionRegistry,
1874
+ {
1875
+ checker,
1876
+ declaration: interfaceDecl,
1877
+ subjectType: interfaceType,
1878
+ hostType: interfaceType
1879
+ }
1880
+ );
1854
1881
  return {
1855
1882
  name,
1856
1883
  ...metadata !== void 0 && { metadata },
@@ -1874,7 +1901,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
1874
1901
  };
1875
1902
  }
1876
1903
  const typeLiteral = typeAlias.type;
1877
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
1904
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
1878
1905
  const name = typeAlias.name.text;
1879
1906
  const fields = [];
1880
1907
  const typeRegistry = {};
@@ -1915,12 +1942,20 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
1915
1942
  diagnostics,
1916
1943
  normalizedMetadataPolicy
1917
1944
  );
1918
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, typeAlias, {
1945
+ const metadata = resolveNodeMetadata(
1946
+ normalizedMetadataPolicy,
1947
+ "type",
1948
+ name,
1949
+ typeAlias,
1919
1950
  checker,
1920
- declaration: typeAlias,
1921
- subjectType: aliasType,
1922
- hostType: aliasType
1923
- });
1951
+ extensionRegistry,
1952
+ {
1953
+ checker,
1954
+ declaration: typeAlias,
1955
+ subjectType: aliasType,
1956
+ hostType: aliasType
1957
+ }
1958
+ );
1924
1959
  return {
1925
1960
  ok: true,
1926
1961
  analysis: {
@@ -2142,10 +2177,7 @@ function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker,
2142
2177
  if (resolvedAnchorNode === null) {
2143
2178
  return void 0;
2144
2179
  }
2145
- const propertyType = checker.getTypeOfSymbolAtLocation(
2146
- propertySymbol,
2147
- resolvedAnchorNode
2148
- );
2180
+ const propertyType = checker.getTypeOfSymbolAtLocation(propertySymbol, resolvedAnchorNode);
2149
2181
  if (propertyType.isStringLiteral()) {
2150
2182
  return propertyType.value;
2151
2183
  }
@@ -2176,6 +2208,8 @@ function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
2176
2208
  "type",
2177
2209
  getDiscriminatorLogicalName(boundType, declaration, checker),
2178
2210
  declaration,
2211
+ checker,
2212
+ void 0,
2179
2213
  {
2180
2214
  checker,
2181
2215
  declaration,
@@ -2412,12 +2446,20 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
2412
2446
  annotations.push(defaultAnnotation);
2413
2447
  }
2414
2448
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
2415
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
2449
+ const metadata = resolveNodeMetadata(
2450
+ metadataPolicy,
2451
+ "field",
2452
+ name,
2453
+ prop,
2416
2454
  checker,
2417
- declaration: prop,
2418
- subjectType: tsType,
2419
- hostType
2420
- });
2455
+ extensionRegistry,
2456
+ {
2457
+ checker,
2458
+ declaration: prop,
2459
+ subjectType: tsType,
2460
+ hostType
2461
+ }
2462
+ );
2421
2463
  return {
2422
2464
  kind: "field",
2423
2465
  name,
@@ -2464,12 +2506,20 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
2464
2506
  let annotations = [];
2465
2507
  annotations.push(...docResult.annotations);
2466
2508
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
2467
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
2509
+ const metadata = resolveNodeMetadata(
2510
+ metadataPolicy,
2511
+ "field",
2512
+ name,
2513
+ prop,
2468
2514
  checker,
2469
- declaration: prop,
2470
- subjectType: tsType,
2471
- hostType
2472
- });
2515
+ extensionRegistry,
2516
+ {
2517
+ checker,
2518
+ declaration: prop,
2519
+ subjectType: tsType,
2520
+ hostType
2521
+ }
2522
+ );
2473
2523
  return {
2474
2524
  kind: "field",
2475
2525
  name,
@@ -2598,7 +2648,7 @@ function getTypeNodeRegistrationName(typeNode) {
2598
2648
  }
2599
2649
  return null;
2600
2650
  }
2601
- function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2651
+ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2602
2652
  const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
2603
2653
  if (customType) {
2604
2654
  return customType;
@@ -2688,7 +2738,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
2688
2738
  }
2689
2739
  return { kind: "primitive", primitiveKind: "string" };
2690
2740
  }
2691
- function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2741
+ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2692
2742
  if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
2693
2743
  return null;
2694
2744
  }
@@ -2708,11 +2758,19 @@ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiti
2708
2758
  file,
2709
2759
  makeParseOptions(extensionRegistry)
2710
2760
  );
2711
- const metadata = resolveNodeMetadata(metadataPolicy, "type", aliasName, aliasDecl, {
2761
+ const metadata = resolveNodeMetadata(
2762
+ metadataPolicy,
2763
+ "type",
2764
+ aliasName,
2765
+ aliasDecl,
2712
2766
  checker,
2713
- declaration: aliasDecl,
2714
- subjectType: aliasType
2715
- });
2767
+ extensionRegistry,
2768
+ {
2769
+ checker,
2770
+ declaration: aliasDecl,
2771
+ subjectType: aliasType
2772
+ }
2773
+ );
2716
2774
  typeRegistry[aliasName] = {
2717
2775
  name: aliasName,
2718
2776
  ...metadata !== void 0 && { metadata },
@@ -2751,7 +2809,7 @@ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
2751
2809
  const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
2752
2810
  return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
2753
2811
  }
2754
- function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2812
+ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2755
2813
  const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
2756
2814
  if (nestedAliasDecl !== void 0) {
2757
2815
  return resolveAliasedPrimitiveTarget(
@@ -2777,7 +2835,7 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
2777
2835
  diagnostics
2778
2836
  );
2779
2837
  }
2780
- function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2838
+ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2781
2839
  const typeName = getNamedTypeName(type);
2782
2840
  const namedDecl = getNamedTypeDeclaration(type);
2783
2841
  if (typeName && typeName in typeRegistry) {
@@ -2812,11 +2870,19 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2812
2870
  return result;
2813
2871
  }
2814
2872
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2815
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", typeName, namedDecl, {
2873
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
2874
+ metadataPolicy,
2875
+ "type",
2876
+ typeName,
2877
+ namedDecl,
2816
2878
  checker,
2817
- declaration: namedDecl,
2818
- subjectType: type
2819
- }) : void 0;
2879
+ extensionRegistry,
2880
+ {
2881
+ checker,
2882
+ declaration: namedDecl,
2883
+ subjectType: type
2884
+ }
2885
+ ) : void 0;
2820
2886
  typeRegistry[typeName] = {
2821
2887
  name: typeName,
2822
2888
  ...metadata !== void 0 && { metadata },
@@ -2901,7 +2967,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2901
2967
  }
2902
2968
  return registerNamed({ kind: "union", members });
2903
2969
  }
2904
- function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2970
+ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2905
2971
  const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
2906
2972
  const elementType = typeArgs?.[0];
2907
2973
  const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
@@ -2918,7 +2984,7 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNod
2918
2984
  ) : { kind: "primitive", primitiveKind: "string" };
2919
2985
  return { kind: "array", items };
2920
2986
  }
2921
- function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2987
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2922
2988
  if (type.getProperties().length > 0) {
2923
2989
  return null;
2924
2990
  }
@@ -2979,7 +3045,7 @@ function shouldEmitResolvedObjectProperty(property, declaration) {
2979
3045
  }
2980
3046
  return true;
2981
3047
  }
2982
- function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3048
+ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2983
3049
  const collectedDiagnostics = diagnostics ?? [];
2984
3050
  const typeName = getNamedTypeName(type);
2985
3051
  const namedTypeName = typeName ?? void 0;
@@ -3055,11 +3121,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3055
3121
  return recordNode;
3056
3122
  }
3057
3123
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
3058
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
3124
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
3125
+ metadataPolicy,
3126
+ "type",
3127
+ registryTypeName,
3128
+ namedDecl,
3059
3129
  checker,
3060
- declaration: namedDecl,
3061
- subjectType: type
3062
- }) : void 0;
3130
+ extensionRegistry,
3131
+ {
3132
+ checker,
3133
+ declaration: namedDecl,
3134
+ subjectType: type
3135
+ }
3136
+ ) : void 0;
3063
3137
  typeRegistry[registryTypeName] = {
3064
3138
  name: registryTypeName,
3065
3139
  ...metadata !== void 0 && { metadata },
@@ -3104,14 +3178,39 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3104
3178
  collectedDiagnostics
3105
3179
  );
3106
3180
  const fieldNodeInfo = fieldInfoMap?.get(prop.name);
3181
+ const inlineFieldNodeInfo = fieldNodeInfo === void 0 ? ts3.isPropertySignature(declaration) ? analyzeInterfacePropertyToIR(
3182
+ declaration,
3183
+ checker,
3184
+ file,
3185
+ typeRegistry,
3186
+ visiting,
3187
+ collectedDiagnostics,
3188
+ type,
3189
+ metadataPolicy,
3190
+ extensionRegistry
3191
+ ) : ts3.isPropertyDeclaration(declaration) ? analyzeFieldToIR(
3192
+ declaration,
3193
+ checker,
3194
+ file,
3195
+ typeRegistry,
3196
+ visiting,
3197
+ collectedDiagnostics,
3198
+ type,
3199
+ metadataPolicy,
3200
+ extensionRegistry
3201
+ ) : null : null;
3202
+ const resolvedFieldNodeInfo = fieldNodeInfo ?? inlineFieldNodeInfo;
3203
+ const resolvedPropertyType = inlineFieldNodeInfo?.type ?? propTypeNode;
3107
3204
  properties.push({
3108
3205
  name: prop.name,
3109
- ...fieldNodeInfo?.metadata !== void 0 && { metadata: fieldNodeInfo.metadata },
3110
- type: propTypeNode,
3206
+ ...resolvedFieldNodeInfo?.metadata !== void 0 && {
3207
+ metadata: resolvedFieldNodeInfo.metadata
3208
+ },
3209
+ type: resolvedPropertyType,
3111
3210
  optional,
3112
- constraints: fieldNodeInfo?.constraints ?? [],
3113
- annotations: fieldNodeInfo?.annotations ?? [],
3114
- provenance: fieldNodeInfo?.provenance ?? provenanceForFile(file)
3211
+ constraints: resolvedFieldNodeInfo?.constraints ?? [],
3212
+ annotations: resolvedFieldNodeInfo?.annotations ?? [],
3213
+ provenance: resolvedFieldNodeInfo?.provenance ?? provenanceForFile(file)
3115
3214
  });
3116
3215
  }
3117
3216
  visiting.delete(type);
@@ -3130,11 +3229,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3130
3229
  };
3131
3230
  if (registryTypeName !== void 0 && shouldRegisterNamedType) {
3132
3231
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
3133
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
3232
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
3233
+ metadataPolicy,
3234
+ "type",
3235
+ registryTypeName,
3236
+ namedDecl,
3134
3237
  checker,
3135
- declaration: namedDecl,
3136
- subjectType: type
3137
- }) : void 0;
3238
+ extensionRegistry,
3239
+ {
3240
+ checker,
3241
+ declaration: namedDecl,
3242
+ subjectType: type
3243
+ }
3244
+ ) : void 0;
3138
3245
  typeRegistry[registryTypeName] = {
3139
3246
  name: registryTypeName,
3140
3247
  ...metadata !== void 0 && { metadata },
@@ -4637,13 +4744,29 @@ function formatLocation(location) {
4637
4744
  }
4638
4745
 
4639
4746
  // src/extensions/registry.ts
4747
+ var import_internals5 = require("@formspec/core/internals");
4748
+ var import_internal4 = require("@formspec/analysis/internal");
4749
+ var BUILTIN_METADATA_TAGS = /* @__PURE__ */ new Set(["apiName", "displayName"]);
4750
+ function buildConstraintTagSources(extensions) {
4751
+ return extensions.map((extension) => ({
4752
+ extensionId: extension.extensionId,
4753
+ ...extension.constraintTags !== void 0 ? {
4754
+ constraintTags: extension.constraintTags.map((tag) => ({
4755
+ tagName: (0, import_internal4.normalizeFormSpecTagName)(tag.tagName)
4756
+ }))
4757
+ } : {}
4758
+ }));
4759
+ }
4640
4760
  function createExtensionRegistry(extensions) {
4761
+ const reservedTagSources = buildConstraintTagSources(extensions);
4641
4762
  const typeMap = /* @__PURE__ */ new Map();
4642
4763
  const typeNameMap = /* @__PURE__ */ new Map();
4643
4764
  const constraintMap = /* @__PURE__ */ new Map();
4644
4765
  const constraintTagMap = /* @__PURE__ */ new Map();
4645
4766
  const builtinBroadeningMap = /* @__PURE__ */ new Map();
4646
4767
  const annotationMap = /* @__PURE__ */ new Map();
4768
+ const metadataSlotMap = /* @__PURE__ */ new Map();
4769
+ const metadataTagMap = /* @__PURE__ */ new Map();
4647
4770
  for (const ext of extensions) {
4648
4771
  if (ext.types !== void 0) {
4649
4772
  for (const type of ext.types) {
@@ -4686,10 +4809,11 @@ function createExtensionRegistry(extensions) {
4686
4809
  }
4687
4810
  if (ext.constraintTags !== void 0) {
4688
4811
  for (const tag of ext.constraintTags) {
4689
- if (constraintTagMap.has(tag.tagName)) {
4690
- throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
4812
+ const canonicalTagName = (0, import_internal4.normalizeFormSpecTagName)(tag.tagName);
4813
+ if (constraintTagMap.has(canonicalTagName)) {
4814
+ throw new Error(`Duplicate custom constraint tag: "@${canonicalTagName}"`);
4691
4815
  }
4692
- constraintTagMap.set(tag.tagName, {
4816
+ constraintTagMap.set(canonicalTagName, {
4693
4817
  extensionId: ext.extensionId,
4694
4818
  registration: tag
4695
4819
  });
@@ -4704,20 +4828,61 @@ function createExtensionRegistry(extensions) {
4704
4828
  annotationMap.set(qualifiedId, annotation);
4705
4829
  }
4706
4830
  }
4831
+ if (ext.metadataSlots !== void 0) {
4832
+ for (const slot of ext.metadataSlots) {
4833
+ if (metadataSlotMap.has(slot.slotId)) {
4834
+ throw new Error(`Duplicate metadata slot ID: "${slot.slotId}"`);
4835
+ }
4836
+ metadataSlotMap.set(slot.slotId, true);
4837
+ const canonicalTagName = (0, import_internal4.normalizeFormSpecTagName)(slot.tagName);
4838
+ if (slot.allowBare === false && (slot.qualifiers?.length ?? 0) === 0) {
4839
+ throw new Error(
4840
+ `Metadata tag "@${canonicalTagName}" must allow bare usage or declare at least one qualifier.`
4841
+ );
4842
+ }
4843
+ if (metadataTagMap.has(canonicalTagName)) {
4844
+ throw new Error(`Duplicate metadata tag: "@${canonicalTagName}"`);
4845
+ }
4846
+ if (BUILTIN_METADATA_TAGS.has(canonicalTagName)) {
4847
+ throw new Error(
4848
+ `Metadata tag "@${canonicalTagName}" conflicts with built-in metadata tags.`
4849
+ );
4850
+ }
4851
+ if (constraintTagMap.has(canonicalTagName)) {
4852
+ throw new Error(
4853
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${canonicalTagName}".`
4854
+ );
4855
+ }
4856
+ if (Object.hasOwn(import_internals5.BUILTIN_CONSTRAINT_DEFINITIONS, (0, import_internals5.normalizeConstraintTagName)(canonicalTagName))) {
4857
+ throw new Error(
4858
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${(0, import_internals5.normalizeConstraintTagName)(canonicalTagName)}".`
4859
+ );
4860
+ }
4861
+ const existingTag = (0, import_internal4.getTagDefinition)(canonicalTagName, reservedTagSources);
4862
+ if (existingTag !== null) {
4863
+ throw BUILTIN_METADATA_TAGS.has(existingTag.canonicalName) ? new Error(
4864
+ `Metadata tag "@${canonicalTagName}" conflicts with built-in metadata tags.`
4865
+ ) : new Error(
4866
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${existingTag.canonicalName}".`
4867
+ );
4868
+ }
4869
+ metadataTagMap.set(canonicalTagName, true);
4870
+ }
4871
+ }
4707
4872
  }
4708
4873
  return {
4709
4874
  extensions,
4710
4875
  findType: (typeId) => typeMap.get(typeId),
4711
4876
  findTypeByName: (typeName) => typeNameMap.get(typeName),
4712
4877
  findConstraint: (constraintId) => constraintMap.get(constraintId),
4713
- findConstraintTag: (tagName) => constraintTagMap.get(tagName),
4878
+ findConstraintTag: (tagName) => constraintTagMap.get((0, import_internal4.normalizeFormSpecTagName)(tagName)),
4714
4879
  findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
4715
4880
  findAnnotation: (annotationId) => annotationMap.get(annotationId)
4716
4881
  };
4717
4882
  }
4718
4883
 
4719
4884
  // src/generators/method-schema.ts
4720
- var import_internals5 = require("@formspec/core/internals");
4885
+ var import_internals6 = require("@formspec/core/internals");
4721
4886
  function typeToJsonSchema(type, checker) {
4722
4887
  const typeRegistry = {};
4723
4888
  const visiting = /* @__PURE__ */ new Set();
@@ -4742,7 +4907,7 @@ function typeToJsonSchema(type, checker) {
4742
4907
  const fieldProvenance = { surface: "tsdoc", file: "", line: 0, column: 0 };
4743
4908
  const ir = {
4744
4909
  kind: "form-ir",
4745
- irVersion: import_internals5.IR_VERSION,
4910
+ irVersion: import_internals6.IR_VERSION,
4746
4911
  elements: [
4747
4912
  {
4748
4913
  kind: "field",