@formspec/build 0.1.0-alpha.32 → 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: {
@@ -2173,6 +2208,8 @@ function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
2173
2208
  "type",
2174
2209
  getDiscriminatorLogicalName(boundType, declaration, checker),
2175
2210
  declaration,
2211
+ checker,
2212
+ void 0,
2176
2213
  {
2177
2214
  checker,
2178
2215
  declaration,
@@ -2409,12 +2446,20 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
2409
2446
  annotations.push(defaultAnnotation);
2410
2447
  }
2411
2448
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
2412
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
2449
+ const metadata = resolveNodeMetadata(
2450
+ metadataPolicy,
2451
+ "field",
2452
+ name,
2453
+ prop,
2413
2454
  checker,
2414
- declaration: prop,
2415
- subjectType: tsType,
2416
- hostType
2417
- });
2455
+ extensionRegistry,
2456
+ {
2457
+ checker,
2458
+ declaration: prop,
2459
+ subjectType: tsType,
2460
+ hostType
2461
+ }
2462
+ );
2418
2463
  return {
2419
2464
  kind: "field",
2420
2465
  name,
@@ -2461,12 +2506,20 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
2461
2506
  let annotations = [];
2462
2507
  annotations.push(...docResult.annotations);
2463
2508
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
2464
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
2509
+ const metadata = resolveNodeMetadata(
2510
+ metadataPolicy,
2511
+ "field",
2512
+ name,
2513
+ prop,
2465
2514
  checker,
2466
- declaration: prop,
2467
- subjectType: tsType,
2468
- hostType
2469
- });
2515
+ extensionRegistry,
2516
+ {
2517
+ checker,
2518
+ declaration: prop,
2519
+ subjectType: tsType,
2520
+ hostType
2521
+ }
2522
+ );
2470
2523
  return {
2471
2524
  kind: "field",
2472
2525
  name,
@@ -2595,7 +2648,7 @@ function getTypeNodeRegistrationName(typeNode) {
2595
2648
  }
2596
2649
  return null;
2597
2650
  }
2598
- 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) {
2599
2652
  const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
2600
2653
  if (customType) {
2601
2654
  return customType;
@@ -2685,7 +2738,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
2685
2738
  }
2686
2739
  return { kind: "primitive", primitiveKind: "string" };
2687
2740
  }
2688
- 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) {
2689
2742
  if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
2690
2743
  return null;
2691
2744
  }
@@ -2705,11 +2758,19 @@ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiti
2705
2758
  file,
2706
2759
  makeParseOptions(extensionRegistry)
2707
2760
  );
2708
- const metadata = resolveNodeMetadata(metadataPolicy, "type", aliasName, aliasDecl, {
2761
+ const metadata = resolveNodeMetadata(
2762
+ metadataPolicy,
2763
+ "type",
2764
+ aliasName,
2765
+ aliasDecl,
2709
2766
  checker,
2710
- declaration: aliasDecl,
2711
- subjectType: aliasType
2712
- });
2767
+ extensionRegistry,
2768
+ {
2769
+ checker,
2770
+ declaration: aliasDecl,
2771
+ subjectType: aliasType
2772
+ }
2773
+ );
2713
2774
  typeRegistry[aliasName] = {
2714
2775
  name: aliasName,
2715
2776
  ...metadata !== void 0 && { metadata },
@@ -2748,7 +2809,7 @@ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
2748
2809
  const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
2749
2810
  return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
2750
2811
  }
2751
- 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) {
2752
2813
  const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
2753
2814
  if (nestedAliasDecl !== void 0) {
2754
2815
  return resolveAliasedPrimitiveTarget(
@@ -2774,7 +2835,7 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
2774
2835
  diagnostics
2775
2836
  );
2776
2837
  }
2777
- 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) {
2778
2839
  const typeName = getNamedTypeName(type);
2779
2840
  const namedDecl = getNamedTypeDeclaration(type);
2780
2841
  if (typeName && typeName in typeRegistry) {
@@ -2809,11 +2870,19 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2809
2870
  return result;
2810
2871
  }
2811
2872
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2812
- 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,
2813
2878
  checker,
2814
- declaration: namedDecl,
2815
- subjectType: type
2816
- }) : void 0;
2879
+ extensionRegistry,
2880
+ {
2881
+ checker,
2882
+ declaration: namedDecl,
2883
+ subjectType: type
2884
+ }
2885
+ ) : void 0;
2817
2886
  typeRegistry[typeName] = {
2818
2887
  name: typeName,
2819
2888
  ...metadata !== void 0 && { metadata },
@@ -2898,7 +2967,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2898
2967
  }
2899
2968
  return registerNamed({ kind: "union", members });
2900
2969
  }
2901
- 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) {
2902
2971
  const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
2903
2972
  const elementType = typeArgs?.[0];
2904
2973
  const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
@@ -2915,7 +2984,7 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNod
2915
2984
  ) : { kind: "primitive", primitiveKind: "string" };
2916
2985
  return { kind: "array", items };
2917
2986
  }
2918
- 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) {
2919
2988
  if (type.getProperties().length > 0) {
2920
2989
  return null;
2921
2990
  }
@@ -2976,7 +3045,7 @@ function shouldEmitResolvedObjectProperty(property, declaration) {
2976
3045
  }
2977
3046
  return true;
2978
3047
  }
2979
- 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) {
2980
3049
  const collectedDiagnostics = diagnostics ?? [];
2981
3050
  const typeName = getNamedTypeName(type);
2982
3051
  const namedTypeName = typeName ?? void 0;
@@ -3052,11 +3121,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3052
3121
  return recordNode;
3053
3122
  }
3054
3123
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
3055
- 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,
3056
3129
  checker,
3057
- declaration: namedDecl,
3058
- subjectType: type
3059
- }) : void 0;
3130
+ extensionRegistry,
3131
+ {
3132
+ checker,
3133
+ declaration: namedDecl,
3134
+ subjectType: type
3135
+ }
3136
+ ) : void 0;
3060
3137
  typeRegistry[registryTypeName] = {
3061
3138
  name: registryTypeName,
3062
3139
  ...metadata !== void 0 && { metadata },
@@ -3152,11 +3229,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3152
3229
  };
3153
3230
  if (registryTypeName !== void 0 && shouldRegisterNamedType) {
3154
3231
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
3155
- 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,
3156
3237
  checker,
3157
- declaration: namedDecl,
3158
- subjectType: type
3159
- }) : void 0;
3238
+ extensionRegistry,
3239
+ {
3240
+ checker,
3241
+ declaration: namedDecl,
3242
+ subjectType: type
3243
+ }
3244
+ ) : void 0;
3160
3245
  typeRegistry[registryTypeName] = {
3161
3246
  name: registryTypeName,
3162
3247
  ...metadata !== void 0 && { metadata },
@@ -4659,13 +4744,29 @@ function formatLocation(location) {
4659
4744
  }
4660
4745
 
4661
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
+ }
4662
4760
  function createExtensionRegistry(extensions) {
4761
+ const reservedTagSources = buildConstraintTagSources(extensions);
4663
4762
  const typeMap = /* @__PURE__ */ new Map();
4664
4763
  const typeNameMap = /* @__PURE__ */ new Map();
4665
4764
  const constraintMap = /* @__PURE__ */ new Map();
4666
4765
  const constraintTagMap = /* @__PURE__ */ new Map();
4667
4766
  const builtinBroadeningMap = /* @__PURE__ */ new Map();
4668
4767
  const annotationMap = /* @__PURE__ */ new Map();
4768
+ const metadataSlotMap = /* @__PURE__ */ new Map();
4769
+ const metadataTagMap = /* @__PURE__ */ new Map();
4669
4770
  for (const ext of extensions) {
4670
4771
  if (ext.types !== void 0) {
4671
4772
  for (const type of ext.types) {
@@ -4708,10 +4809,11 @@ function createExtensionRegistry(extensions) {
4708
4809
  }
4709
4810
  if (ext.constraintTags !== void 0) {
4710
4811
  for (const tag of ext.constraintTags) {
4711
- if (constraintTagMap.has(tag.tagName)) {
4712
- 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}"`);
4713
4815
  }
4714
- constraintTagMap.set(tag.tagName, {
4816
+ constraintTagMap.set(canonicalTagName, {
4715
4817
  extensionId: ext.extensionId,
4716
4818
  registration: tag
4717
4819
  });
@@ -4726,20 +4828,61 @@ function createExtensionRegistry(extensions) {
4726
4828
  annotationMap.set(qualifiedId, annotation);
4727
4829
  }
4728
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
+ }
4729
4872
  }
4730
4873
  return {
4731
4874
  extensions,
4732
4875
  findType: (typeId) => typeMap.get(typeId),
4733
4876
  findTypeByName: (typeName) => typeNameMap.get(typeName),
4734
4877
  findConstraint: (constraintId) => constraintMap.get(constraintId),
4735
- findConstraintTag: (tagName) => constraintTagMap.get(tagName),
4878
+ findConstraintTag: (tagName) => constraintTagMap.get((0, import_internal4.normalizeFormSpecTagName)(tagName)),
4736
4879
  findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
4737
4880
  findAnnotation: (annotationId) => annotationMap.get(annotationId)
4738
4881
  };
4739
4882
  }
4740
4883
 
4741
4884
  // src/generators/method-schema.ts
4742
- var import_internals5 = require("@formspec/core/internals");
4885
+ var import_internals6 = require("@formspec/core/internals");
4743
4886
  function typeToJsonSchema(type, checker) {
4744
4887
  const typeRegistry = {};
4745
4888
  const visiting = /* @__PURE__ */ new Set();
@@ -4764,7 +4907,7 @@ function typeToJsonSchema(type, checker) {
4764
4907
  const fieldProvenance = { surface: "tsdoc", file: "", line: 0, column: 0 };
4765
4908
  const ir = {
4766
4909
  kind: "form-ir",
4767
- irVersion: import_internals5.IR_VERSION,
4910
+ irVersion: import_internals6.IR_VERSION,
4768
4911
  elements: [
4769
4912
  {
4770
4913
  kind: "field",