@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.
package/dist/cli.cjs CHANGED
@@ -1877,13 +1877,26 @@ var init_generator2 = __esm({
1877
1877
  });
1878
1878
 
1879
1879
  // src/extensions/registry.ts
1880
+ function buildConstraintTagSources(extensions) {
1881
+ return extensions.map((extension) => ({
1882
+ extensionId: extension.extensionId,
1883
+ ...extension.constraintTags !== void 0 ? {
1884
+ constraintTags: extension.constraintTags.map((tag) => ({
1885
+ tagName: (0, import_internal.normalizeFormSpecTagName)(tag.tagName)
1886
+ }))
1887
+ } : {}
1888
+ }));
1889
+ }
1880
1890
  function createExtensionRegistry(extensions) {
1891
+ const reservedTagSources = buildConstraintTagSources(extensions);
1881
1892
  const typeMap = /* @__PURE__ */ new Map();
1882
1893
  const typeNameMap = /* @__PURE__ */ new Map();
1883
1894
  const constraintMap = /* @__PURE__ */ new Map();
1884
1895
  const constraintTagMap = /* @__PURE__ */ new Map();
1885
1896
  const builtinBroadeningMap = /* @__PURE__ */ new Map();
1886
1897
  const annotationMap = /* @__PURE__ */ new Map();
1898
+ const metadataSlotMap = /* @__PURE__ */ new Map();
1899
+ const metadataTagMap = /* @__PURE__ */ new Map();
1887
1900
  for (const ext of extensions) {
1888
1901
  if (ext.types !== void 0) {
1889
1902
  for (const type of ext.types) {
@@ -1926,10 +1939,11 @@ function createExtensionRegistry(extensions) {
1926
1939
  }
1927
1940
  if (ext.constraintTags !== void 0) {
1928
1941
  for (const tag of ext.constraintTags) {
1929
- if (constraintTagMap.has(tag.tagName)) {
1930
- throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
1942
+ const canonicalTagName = (0, import_internal.normalizeFormSpecTagName)(tag.tagName);
1943
+ if (constraintTagMap.has(canonicalTagName)) {
1944
+ throw new Error(`Duplicate custom constraint tag: "@${canonicalTagName}"`);
1931
1945
  }
1932
- constraintTagMap.set(tag.tagName, {
1946
+ constraintTagMap.set(canonicalTagName, {
1933
1947
  extensionId: ext.extensionId,
1934
1948
  registration: tag
1935
1949
  });
@@ -1944,20 +1958,65 @@ function createExtensionRegistry(extensions) {
1944
1958
  annotationMap.set(qualifiedId, annotation);
1945
1959
  }
1946
1960
  }
1961
+ if (ext.metadataSlots !== void 0) {
1962
+ for (const slot of ext.metadataSlots) {
1963
+ if (metadataSlotMap.has(slot.slotId)) {
1964
+ throw new Error(`Duplicate metadata slot ID: "${slot.slotId}"`);
1965
+ }
1966
+ metadataSlotMap.set(slot.slotId, true);
1967
+ const canonicalTagName = (0, import_internal.normalizeFormSpecTagName)(slot.tagName);
1968
+ if (slot.allowBare === false && (slot.qualifiers?.length ?? 0) === 0) {
1969
+ throw new Error(
1970
+ `Metadata tag "@${canonicalTagName}" must allow bare usage or declare at least one qualifier.`
1971
+ );
1972
+ }
1973
+ if (metadataTagMap.has(canonicalTagName)) {
1974
+ throw new Error(`Duplicate metadata tag: "@${canonicalTagName}"`);
1975
+ }
1976
+ if (BUILTIN_METADATA_TAGS.has(canonicalTagName)) {
1977
+ throw new Error(
1978
+ `Metadata tag "@${canonicalTagName}" conflicts with built-in metadata tags.`
1979
+ );
1980
+ }
1981
+ if (constraintTagMap.has(canonicalTagName)) {
1982
+ throw new Error(
1983
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${canonicalTagName}".`
1984
+ );
1985
+ }
1986
+ if (Object.hasOwn(import_internals3.BUILTIN_CONSTRAINT_DEFINITIONS, (0, import_internals3.normalizeConstraintTagName)(canonicalTagName))) {
1987
+ throw new Error(
1988
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${(0, import_internals3.normalizeConstraintTagName)(canonicalTagName)}".`
1989
+ );
1990
+ }
1991
+ const existingTag = (0, import_internal.getTagDefinition)(canonicalTagName, reservedTagSources);
1992
+ if (existingTag !== null) {
1993
+ throw BUILTIN_METADATA_TAGS.has(existingTag.canonicalName) ? new Error(
1994
+ `Metadata tag "@${canonicalTagName}" conflicts with built-in metadata tags.`
1995
+ ) : new Error(
1996
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${existingTag.canonicalName}".`
1997
+ );
1998
+ }
1999
+ metadataTagMap.set(canonicalTagName, true);
2000
+ }
2001
+ }
1947
2002
  }
1948
2003
  return {
1949
2004
  extensions,
1950
2005
  findType: (typeId) => typeMap.get(typeId),
1951
2006
  findTypeByName: (typeName) => typeNameMap.get(typeName),
1952
2007
  findConstraint: (constraintId) => constraintMap.get(constraintId),
1953
- findConstraintTag: (tagName) => constraintTagMap.get(tagName),
2008
+ findConstraintTag: (tagName) => constraintTagMap.get((0, import_internal.normalizeFormSpecTagName)(tagName)),
1954
2009
  findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
1955
2010
  findAnnotation: (annotationId) => annotationMap.get(annotationId)
1956
2011
  };
1957
2012
  }
2013
+ var import_internals3, import_internal, BUILTIN_METADATA_TAGS;
1958
2014
  var init_registry = __esm({
1959
2015
  "src/extensions/registry.ts"() {
1960
2016
  "use strict";
2017
+ import_internals3 = require("@formspec/core/internals");
2018
+ import_internal = require("@formspec/analysis/internal");
2019
+ BUILTIN_METADATA_TAGS = /* @__PURE__ */ new Set(["apiName", "displayName"]);
1961
2020
  }
1962
2021
  });
1963
2022
 
@@ -2040,7 +2099,7 @@ var init_schema2 = __esm({
2040
2099
  // src/analyzer/tsdoc-parser.ts
2041
2100
  function createFormSpecTSDocConfig(extensionTagNames = []) {
2042
2101
  const config = new import_tsdoc.TSDocConfiguration();
2043
- for (const tagName of Object.keys(import_internals3.BUILTIN_CONSTRAINT_DEFINITIONS)) {
2102
+ for (const tagName of Object.keys(import_internals4.BUILTIN_CONSTRAINT_DEFINITIONS)) {
2044
2103
  config.addTagDefinition(
2045
2104
  new import_tsdoc.TSDocTagDefinition({
2046
2105
  tagName: "@" + tagName,
@@ -2049,7 +2108,7 @@ function createFormSpecTSDocConfig(extensionTagNames = []) {
2049
2108
  })
2050
2109
  );
2051
2110
  }
2052
- for (const tagName of ["displayName", "format", "placeholder"]) {
2111
+ for (const tagName of ["apiName", "displayName", "format", "placeholder"]) {
2053
2112
  config.addTagDefinition(
2054
2113
  new import_tsdoc.TSDocTagDefinition({
2055
2114
  tagName: "@" + tagName,
@@ -2082,6 +2141,16 @@ function sharedTagValueOptions(options) {
2082
2141
  ...options?.fieldType !== void 0 ? { fieldType: options.fieldType } : {}
2083
2142
  };
2084
2143
  }
2144
+ function getExtensionTypeNames(registry) {
2145
+ if (registry === void 0) {
2146
+ return /* @__PURE__ */ new Set();
2147
+ }
2148
+ return new Set(
2149
+ registry.extensions.flatMap(
2150
+ (ext) => (ext.types ?? []).flatMap((t) => t.tsTypeNames ?? [t.typeName])
2151
+ )
2152
+ );
2153
+ }
2085
2154
  function collectImportedNames(sourceFile) {
2086
2155
  const importedNames = /* @__PURE__ */ new Set();
2087
2156
  for (const statement of sourceFile.statements) {
@@ -2121,6 +2190,9 @@ function isNonReferenceIdentifier(node) {
2121
2190
  return false;
2122
2191
  }
2123
2192
  function statementReferencesImportedName(statement, importedNames) {
2193
+ if (importedNames.size === 0) {
2194
+ return false;
2195
+ }
2124
2196
  let referencesImportedName = false;
2125
2197
  const visit = (node) => {
2126
2198
  if (referencesImportedName) {
@@ -2135,14 +2207,17 @@ function statementReferencesImportedName(statement, importedNames) {
2135
2207
  visit(statement);
2136
2208
  return referencesImportedName;
2137
2209
  }
2138
- function buildSupportingDeclarations(sourceFile) {
2210
+ function buildSupportingDeclarations(sourceFile, extensionTypeNames) {
2139
2211
  const importedNames = collectImportedNames(sourceFile);
2212
+ const importedNamesToSkip = new Set(
2213
+ [...importedNames].filter((name) => !extensionTypeNames.has(name))
2214
+ );
2140
2215
  return sourceFile.statements.filter((statement) => {
2141
2216
  if (ts.isImportDeclaration(statement)) return false;
2142
2217
  if (ts.isImportEqualsDeclaration(statement)) return false;
2143
2218
  if (ts.isExportDeclaration(statement) && statement.moduleSpecifier !== void 0)
2144
2219
  return false;
2145
- if (importedNames.size > 0 && statementReferencesImportedName(statement, importedNames)) {
2220
+ if (statementReferencesImportedName(statement, importedNamesToSkip)) {
2146
2221
  return false;
2147
2222
  }
2148
2223
  return true;
@@ -2188,12 +2263,12 @@ function supportsConstraintCapability(type, checker, capability) {
2188
2263
  if (capability === void 0) {
2189
2264
  return true;
2190
2265
  }
2191
- if ((0, import_internal.hasTypeSemanticCapability)(type, checker, capability)) {
2266
+ if ((0, import_internal2.hasTypeSemanticCapability)(type, checker, capability)) {
2192
2267
  return true;
2193
2268
  }
2194
2269
  if (capability === "string-like") {
2195
2270
  const itemType = getArrayElementType(type, checker);
2196
- return itemType !== null && (0, import_internal.hasTypeSemanticCapability)(itemType, checker, capability);
2271
+ return itemType !== null && (0, import_internal2.hasTypeSemanticCapability)(itemType, checker, capability);
2197
2272
  }
2198
2273
  return false;
2199
2274
  }
@@ -2283,7 +2358,7 @@ function hasBuiltinConstraintBroadening(tagName, options) {
2283
2358
  return broadenedTypeId !== void 0 && options?.extensionRegistry?.findBuiltinConstraintBroadening(broadenedTypeId, tagName) !== void 0;
2284
2359
  }
2285
2360
  function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, parsedTag, provenance, supportingDeclarations, options) {
2286
- if (!(0, import_internals3.isBuiltinConstraintName)(tagName)) {
2361
+ if (!(0, import_internals4.isBuiltinConstraintName)(tagName)) {
2287
2362
  return [];
2288
2363
  }
2289
2364
  const checker = options?.checker;
@@ -2291,11 +2366,11 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
2291
2366
  if (checker === void 0 || subjectType === void 0) {
2292
2367
  return [];
2293
2368
  }
2294
- const placement = (0, import_internal.resolveDeclarationPlacement)(node);
2369
+ const placement = (0, import_internal2.resolveDeclarationPlacement)(node);
2295
2370
  if (placement === null) {
2296
2371
  return [];
2297
2372
  }
2298
- const definition = (0, import_internal.getTagDefinition)(tagName, options?.extensionRegistry?.extensions);
2373
+ const definition = (0, import_internal2.getTagDefinition)(tagName, options?.extensionRegistry?.extensions);
2299
2374
  if (definition === null) {
2300
2375
  return [];
2301
2376
  }
@@ -2329,7 +2404,7 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
2329
2404
  )
2330
2405
  ];
2331
2406
  }
2332
- const resolution = (0, import_internal.resolvePathTargetType)(subjectType, checker, target.path.segments);
2407
+ const resolution = (0, import_internal2.resolvePathTargetType)(subjectType, checker, target.path.segments);
2333
2408
  if (resolution.kind === "missing-property") {
2334
2409
  return [
2335
2410
  makeDiagnostic(
@@ -2386,7 +2461,7 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
2386
2461
  const subjectTypeText = checker.typeToString(subjectType, node, SYNTHETIC_TYPE_FORMAT_FLAGS);
2387
2462
  const hostType = options?.hostType ?? subjectType;
2388
2463
  const hostTypeText = checker.typeToString(hostType, node, SYNTHETIC_TYPE_FORMAT_FLAGS);
2389
- const result = (0, import_internal.checkSyntheticTagApplication)({
2464
+ const result = (0, import_internal2.checkSyntheticTagApplication)({
2390
2465
  tagName,
2391
2466
  placement,
2392
2467
  hostType: hostTypeText,
@@ -2399,6 +2474,14 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
2399
2474
  extensionId: extension.extensionId,
2400
2475
  ...extension.constraintTags !== void 0 ? {
2401
2476
  constraintTags: extension.constraintTags.map((tag) => ({ tagName: tag.tagName }))
2477
+ } : {},
2478
+ ...extension.metadataSlots !== void 0 ? {
2479
+ metadataSlots: extension.metadataSlots
2480
+ } : {},
2481
+ ...extension.types !== void 0 ? {
2482
+ customTypes: extension.types.map((t) => ({
2483
+ tsTypeNames: t.tsTypeNames ?? [t.typeName]
2484
+ }))
2402
2485
  } : {}
2403
2486
  }))
2404
2487
  } : {}
@@ -2418,7 +2501,10 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
2418
2501
  function getParser(options) {
2419
2502
  const extensionTagNames = [
2420
2503
  ...options?.extensionRegistry?.extensions.flatMap(
2421
- (extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
2504
+ (extension) => (extension.constraintTags ?? []).map((tag) => (0, import_internal2.normalizeFormSpecTagName)(tag.tagName))
2505
+ ) ?? [],
2506
+ ...options?.extensionRegistry?.extensions.flatMap(
2507
+ (extension) => (extension.metadataSlots ?? []).map((slot) => (0, import_internal2.normalizeFormSpecTagName)(slot.tagName))
2422
2508
  ) ?? []
2423
2509
  ].sort();
2424
2510
  const cacheKey = extensionTagNames.join("|");
@@ -2438,7 +2524,16 @@ function getExtensionRegistryCacheKey(registry) {
2438
2524
  (extension) => JSON.stringify({
2439
2525
  extensionId: extension.extensionId,
2440
2526
  typeNames: extension.types?.map((type) => type.typeName) ?? [],
2441
- constraintTags: extension.constraintTags?.map((tag) => tag.tagName) ?? []
2527
+ constraintTags: extension.constraintTags?.map((tag) => (0, import_internal2.normalizeFormSpecTagName)(tag.tagName)) ?? [],
2528
+ metadataSlots: extension.metadataSlots?.map((slot) => ({
2529
+ tagName: (0, import_internal2.normalizeFormSpecTagName)(slot.tagName),
2530
+ declarationKinds: [...slot.declarationKinds].sort(),
2531
+ allowBare: slot.allowBare !== false,
2532
+ qualifiers: (slot.qualifiers ?? []).map((qualifier) => ({
2533
+ qualifier: qualifier.qualifier,
2534
+ ...qualifier.sourceQualifier !== void 0 ? { sourceQualifier: qualifier.sourceQualifier } : {}
2535
+ })).sort((left, right) => left.qualifier.localeCompare(right.qualifier))
2536
+ })) ?? []
2442
2537
  })
2443
2538
  ).join("|");
2444
2539
  }
@@ -2473,7 +2568,8 @@ function parseTSDocTags(node, file = "", options) {
2473
2568
  const rawTextTags = [];
2474
2569
  const sourceFile = node.getSourceFile();
2475
2570
  const sourceText = sourceFile.getFullText();
2476
- const supportingDeclarations = buildSupportingDeclarations(sourceFile);
2571
+ const extensionTypeNames = getExtensionTypeNames(options?.extensionRegistry);
2572
+ const supportingDeclarations = buildSupportingDeclarations(sourceFile, extensionTypeNames);
2477
2573
  const commentRanges = ts.getLeadingCommentRanges(sourceText, node.getFullStart());
2478
2574
  const rawTextFallbacks = collectRawTextFallbacks(node, file);
2479
2575
  if (commentRanges) {
@@ -2490,7 +2586,7 @@ function parseTSDocTags(node, file = "", options) {
2490
2586
  import_tsdoc.TextRange.fromStringRange(sourceText, range.pos, range.end)
2491
2587
  );
2492
2588
  const docComment = parserContext.docComment;
2493
- const parsedComment = (0, import_internal.parseCommentBlock)(
2589
+ const parsedComment = (0, import_internal2.parseCommentBlock)(
2494
2590
  commentText,
2495
2591
  sharedCommentSyntaxOptions(options, range.pos)
2496
2592
  );
@@ -2511,7 +2607,7 @@ function parseTSDocTags(node, file = "", options) {
2511
2607
  }
2512
2608
  }
2513
2609
  for (const block of docComment.customBlocks) {
2514
- const tagName = (0, import_internals3.normalizeConstraintTagName)(block.blockTag.tagName.substring(1));
2610
+ const tagName = (0, import_internals4.normalizeConstraintTagName)(block.blockTag.tagName.substring(1));
2515
2611
  const parsedTag = nextParsedTag(tagName);
2516
2612
  if (tagName === "displayName" || tagName === "format" || tagName === "placeholder") {
2517
2613
  const text2 = getBestBlockPayloadText(parsedTag, commentText, range.pos, block);
@@ -2543,7 +2639,7 @@ function parseTSDocTags(node, file = "", options) {
2543
2639
  }
2544
2640
  if (TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
2545
2641
  const text = getBestBlockPayloadText(parsedTag, commentText, range.pos, block);
2546
- const expectedType = (0, import_internals3.isBuiltinConstraintName)(tagName) ? import_internals3.BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
2642
+ const expectedType = (0, import_internals4.isBuiltinConstraintName)(tagName) ? import_internals4.BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
2547
2643
  if (text === "" && expectedType !== "boolean") continue;
2548
2644
  const provenance = parsedTag !== null ? provenanceForParsedTag(parsedTag, sourceFile, file) : provenanceForComment(range, sourceFile, file, tagName);
2549
2645
  const compilerDiagnostics = buildCompilerBackedConstraintDiagnostics(
@@ -2559,7 +2655,7 @@ function parseTSDocTags(node, file = "", options) {
2559
2655
  diagnostics.push(...compilerDiagnostics);
2560
2656
  continue;
2561
2657
  }
2562
- const constraintNode = (0, import_internal.parseConstraintTagValue)(
2658
+ const constraintNode = (0, import_internal2.parseConstraintTagValue)(
2563
2659
  tagName,
2564
2660
  text,
2565
2661
  provenance,
@@ -2629,7 +2725,7 @@ function parseTSDocTags(node, file = "", options) {
2629
2725
  if (text === "") continue;
2630
2726
  const provenance = provenanceForParsedTag(rawTextTag.tag, sourceFile, file);
2631
2727
  if (rawTextTag.tag.normalizedTagName === "defaultValue") {
2632
- const defaultValueNode = (0, import_internal.parseDefaultValueTagValue)(text, provenance);
2728
+ const defaultValueNode = (0, import_internal2.parseDefaultValueTagValue)(text, provenance);
2633
2729
  annotations.push(defaultValueNode);
2634
2730
  continue;
2635
2731
  }
@@ -2646,7 +2742,7 @@ function parseTSDocTags(node, file = "", options) {
2646
2742
  diagnostics.push(...compilerDiagnostics);
2647
2743
  continue;
2648
2744
  }
2649
- const constraintNode = (0, import_internal.parseConstraintTagValue)(
2745
+ const constraintNode = (0, import_internal2.parseConstraintTagValue)(
2650
2746
  rawTextTag.tag.normalizedTagName,
2651
2747
  text,
2652
2748
  provenance,
@@ -2663,7 +2759,7 @@ function parseTSDocTags(node, file = "", options) {
2663
2759
  if (text === "") continue;
2664
2760
  const provenance = fallback.provenance;
2665
2761
  if (tagName === "defaultValue") {
2666
- const defaultValueNode = (0, import_internal.parseDefaultValueTagValue)(text, provenance);
2762
+ const defaultValueNode = (0, import_internal2.parseDefaultValueTagValue)(text, provenance);
2667
2763
  annotations.push(defaultValueNode);
2668
2764
  continue;
2669
2765
  }
@@ -2680,7 +2776,7 @@ function parseTSDocTags(node, file = "", options) {
2680
2776
  diagnostics.push(...compilerDiagnostics);
2681
2777
  continue;
2682
2778
  }
2683
- const constraintNode = (0, import_internal.parseConstraintTagValue)(
2779
+ const constraintNode = (0, import_internal2.parseConstraintTagValue)(
2684
2780
  tagName,
2685
2781
  text,
2686
2782
  provenance,
@@ -2706,7 +2802,7 @@ function extractDisplayNameMetadata(node) {
2706
2802
  if (range.kind !== ts.SyntaxKind.MultiLineCommentTrivia) continue;
2707
2803
  const commentText = sourceText.substring(range.pos, range.end);
2708
2804
  if (!commentText.startsWith("/**")) continue;
2709
- const parsed = (0, import_internal.parseCommentBlock)(commentText);
2805
+ const parsed = (0, import_internal2.parseCommentBlock)(commentText);
2710
2806
  for (const tag of parsed.tags) {
2711
2807
  if (tag.normalizedTagName !== "displayName") {
2712
2808
  continue;
@@ -2762,7 +2858,7 @@ function getSharedPayloadText(tag, commentText, commentOffset) {
2762
2858
  if (tag.payloadSpan === null) {
2763
2859
  return "";
2764
2860
  }
2765
- return (0, import_internal.sliceCommentSpan)(commentText, tag.payloadSpan, {
2861
+ return (0, import_internal2.sliceCommentSpan)(commentText, tag.payloadSpan, {
2766
2862
  offset: commentOffset
2767
2863
  }).trim();
2768
2864
  }
@@ -2774,7 +2870,7 @@ function getBestBlockPayloadText(tag, commentText, commentOffset, block) {
2774
2870
  function collectRawTextFallbacks(node, file) {
2775
2871
  const fallbacks = /* @__PURE__ */ new Map();
2776
2872
  for (const tag of ts.getJSDocTags(node)) {
2777
- const tagName = (0, import_internals3.normalizeConstraintTagName)(tag.tagName.text);
2873
+ const tagName = (0, import_internals4.normalizeConstraintTagName)(tag.tagName.text);
2778
2874
  if (!TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
2779
2875
  const commentText = getTagCommentText(tag)?.trim() ?? "";
2780
2876
  if (commentText === "") continue;
@@ -2788,7 +2884,7 @@ function collectRawTextFallbacks(node, file) {
2788
2884
  return fallbacks;
2789
2885
  }
2790
2886
  function isMemberTargetDisplayName(text) {
2791
- return (0, import_internal.parseTagSyntax)("displayName", text).target !== null;
2887
+ return (0, import_internal2.parseTagSyntax)("displayName", text).target !== null;
2792
2888
  }
2793
2889
  function provenanceForComment(range, sourceFile, file, tagName) {
2794
2890
  const { line, character } = sourceFile.getLineAndCharacterOfPosition(range.pos);
@@ -2830,15 +2926,15 @@ function getTagCommentText(tag) {
2830
2926
  }
2831
2927
  return ts.getTextOfJSDocComment(tag.comment);
2832
2928
  }
2833
- var ts, import_internal, import_tsdoc, import_internals3, import_internals4, TAGS_REQUIRING_RAW_TEXT, SYNTHETIC_TYPE_FORMAT_FLAGS, parserCache, parseResultCache;
2929
+ var ts, import_internal2, import_tsdoc, import_internals4, import_internals5, TAGS_REQUIRING_RAW_TEXT, SYNTHETIC_TYPE_FORMAT_FLAGS, parserCache, parseResultCache;
2834
2930
  var init_tsdoc_parser = __esm({
2835
2931
  "src/analyzer/tsdoc-parser.ts"() {
2836
2932
  "use strict";
2837
2933
  ts = __toESM(require("typescript"), 1);
2838
- import_internal = require("@formspec/analysis/internal");
2934
+ import_internal2 = require("@formspec/analysis/internal");
2839
2935
  import_tsdoc = require("@microsoft/tsdoc");
2840
- import_internals3 = require("@formspec/core/internals");
2841
2936
  import_internals4 = require("@formspec/core/internals");
2937
+ import_internals5 = require("@formspec/core/internals");
2842
2938
  TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions", "defaultValue"]);
2843
2939
  SYNTHETIC_TYPE_FORMAT_FLAGS = ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope;
2844
2940
  parserCache = /* @__PURE__ */ new Map();
@@ -2919,76 +3015,79 @@ function makeParseOptions(extensionRegistry, fieldType, checker, subjectType, ho
2919
3015
  ...hostType !== void 0 && { hostType }
2920
3016
  };
2921
3017
  }
2922
- function makeExplicitScalarMetadata(value) {
2923
- return value === void 0 || value === "" ? void 0 : { value, source: "explicit" };
3018
+ function createAnalyzerMetadataPolicy(input) {
3019
+ return {
3020
+ raw: input,
3021
+ normalized: normalizeMetadataPolicy(input)
3022
+ };
2924
3023
  }
2925
- function extractExplicitMetadata(node) {
2926
- let apiName;
2927
- let displayName;
2928
- let apiNamePlural;
2929
- let displayNamePlural;
2930
- for (const tag of getLeadingParsedTags(node)) {
2931
- const value = tag.argumentText.trim();
2932
- if (value === "") {
2933
- continue;
2934
- }
2935
- if (tag.normalizedTagName === "apiName") {
2936
- if (tag.target === null) {
2937
- apiName ??= value;
2938
- } else if (tag.target.kind === "variant") {
2939
- if (tag.target.rawText === "singular") {
2940
- apiName ??= value;
2941
- } else if (tag.target.rawText === "plural") {
2942
- apiNamePlural ??= value;
2943
- }
2944
- }
2945
- continue;
2946
- }
2947
- if (tag.normalizedTagName === "displayName") {
2948
- if (tag.target === null) {
2949
- displayName ??= value;
2950
- } else if (tag.target.kind === "variant") {
2951
- if (tag.target.rawText === "singular") {
2952
- displayName ??= value;
2953
- } else if (tag.target.rawText === "plural") {
2954
- displayNamePlural ??= value;
2955
- }
2956
- }
2957
- }
3024
+ function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, checker, extensionRegistry, buildContext) {
3025
+ const analysis = (0, import_internal3.analyzeMetadataForNodeWithChecker)({
3026
+ checker,
3027
+ node,
3028
+ logicalName,
3029
+ metadata: metadataPolicy.raw,
3030
+ extensions: extensionRegistry?.extensions,
3031
+ ...buildContext !== void 0 && { buildContext }
3032
+ });
3033
+ const resolvedMetadata = analysis?.resolvedMetadata;
3034
+ const declarationPolicy = getDeclarationMetadataPolicy(
3035
+ metadataPolicy.normalized,
3036
+ declarationKind
3037
+ );
3038
+ if (resolvedMetadata?.apiName === void 0 && declarationPolicy.apiName.mode === "require-explicit") {
3039
+ throw new Error(
3040
+ `Metadata policy requires explicit apiName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
3041
+ );
2958
3042
  }
2959
- const resolvedApiName = makeExplicitScalarMetadata(apiName);
2960
- const resolvedDisplayName = makeExplicitScalarMetadata(displayName);
2961
- const resolvedApiNamePlural = makeExplicitScalarMetadata(apiNamePlural);
2962
- const resolvedDisplayNamePlural = makeExplicitScalarMetadata(displayNamePlural);
2963
- const metadata = {
2964
- ...resolvedApiName !== void 0 && { apiName: resolvedApiName },
2965
- ...resolvedDisplayName !== void 0 && { displayName: resolvedDisplayName },
2966
- ...resolvedApiNamePlural !== void 0 && { apiNamePlural: resolvedApiNamePlural },
2967
- ...resolvedDisplayNamePlural !== void 0 && {
2968
- displayNamePlural: resolvedDisplayNamePlural
2969
- }
2970
- };
2971
- return Object.keys(metadata).length === 0 ? void 0 : metadata;
3043
+ if (resolvedMetadata?.displayName === void 0 && declarationPolicy.displayName.mode === "require-explicit") {
3044
+ throw new Error(
3045
+ `Metadata policy requires explicit displayName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
3046
+ );
3047
+ }
3048
+ if (resolvedMetadata?.apiNamePlural === void 0 && declarationPolicy.apiName.pluralization.mode === "require-explicit") {
3049
+ throw new Error(
3050
+ `Metadata policy requires explicit apiNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
3051
+ );
3052
+ }
3053
+ if (resolvedMetadata?.displayNamePlural === void 0 && declarationPolicy.displayName.pluralization.mode === "require-explicit") {
3054
+ throw new Error(
3055
+ `Metadata policy requires explicit displayNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
3056
+ );
3057
+ }
3058
+ return resolvedMetadata;
2972
3059
  }
2973
- function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, buildContext) {
2974
- const explicit = extractExplicitMetadata(node);
2975
- return resolveMetadata(
3060
+ function analyzeDeclarationRootInfo(declaration, checker, file = "", extensionRegistry, metadataPolicy) {
3061
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
3062
+ const declarationType = checker.getTypeAtLocation(declaration);
3063
+ const logicalName = ts3.isClassDeclaration(declaration) ? declaration.name?.text ?? "AnonymousClass" : declaration.name.text;
3064
+ const docResult = extractJSDocParseResult(
3065
+ declaration,
3066
+ file,
3067
+ makeParseOptions(extensionRegistry, void 0, checker, declarationType, declarationType)
3068
+ );
3069
+ const metadata = resolveNodeMetadata(
3070
+ normalizedMetadataPolicy,
3071
+ "type",
3072
+ logicalName,
3073
+ declaration,
3074
+ checker,
3075
+ extensionRegistry,
2976
3076
  {
2977
- ...explicit?.apiName !== void 0 && { apiName: explicit.apiName.value },
2978
- ...explicit?.displayName !== void 0 && { displayName: explicit.displayName.value },
2979
- ...explicit?.apiNamePlural !== void 0 && {
2980
- apiNamePlural: explicit.apiNamePlural.value
2981
- },
2982
- ...explicit?.displayNamePlural !== void 0 && {
2983
- displayNamePlural: explicit.displayNamePlural.value
2984
- }
2985
- },
2986
- getDeclarationMetadataPolicy(metadataPolicy, declarationKind),
2987
- makeMetadataContext("tsdoc", declarationKind, logicalName, buildContext)
3077
+ checker,
3078
+ declaration,
3079
+ subjectType: declarationType,
3080
+ hostType: declarationType
3081
+ }
2988
3082
  );
3083
+ return {
3084
+ ...metadata !== void 0 && { metadata },
3085
+ annotations: docResult.annotations,
3086
+ diagnostics: docResult.diagnostics
3087
+ };
2989
3088
  }
2990
3089
  function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy) {
2991
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3090
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
2992
3091
  const name = classDecl.name?.text ?? "AnonymousClass";
2993
3092
  const fields = [];
2994
3093
  const fieldLayouts = [];
@@ -3043,12 +3142,20 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
3043
3142
  diagnostics,
3044
3143
  normalizedMetadataPolicy
3045
3144
  );
3046
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, classDecl, {
3145
+ const metadata = resolveNodeMetadata(
3146
+ normalizedMetadataPolicy,
3147
+ "type",
3148
+ name,
3149
+ classDecl,
3047
3150
  checker,
3048
- declaration: classDecl,
3049
- subjectType: classType,
3050
- hostType: classType
3051
- });
3151
+ extensionRegistry,
3152
+ {
3153
+ checker,
3154
+ declaration: classDecl,
3155
+ subjectType: classType,
3156
+ hostType: classType
3157
+ }
3158
+ );
3052
3159
  return {
3053
3160
  name,
3054
3161
  ...metadata !== void 0 && { metadata },
@@ -3062,7 +3169,7 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
3062
3169
  };
3063
3170
  }
3064
3171
  function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy) {
3065
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3172
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
3066
3173
  const name = interfaceDecl.name.text;
3067
3174
  const fields = [];
3068
3175
  const typeRegistry = {};
@@ -3104,12 +3211,20 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
3104
3211
  normalizedMetadataPolicy
3105
3212
  );
3106
3213
  const fieldLayouts = specializedFields.map(() => ({}));
3107
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, interfaceDecl, {
3214
+ const metadata = resolveNodeMetadata(
3215
+ normalizedMetadataPolicy,
3216
+ "type",
3217
+ name,
3218
+ interfaceDecl,
3108
3219
  checker,
3109
- declaration: interfaceDecl,
3110
- subjectType: interfaceType,
3111
- hostType: interfaceType
3112
- });
3220
+ extensionRegistry,
3221
+ {
3222
+ checker,
3223
+ declaration: interfaceDecl,
3224
+ subjectType: interfaceType,
3225
+ hostType: interfaceType
3226
+ }
3227
+ );
3113
3228
  return {
3114
3229
  name,
3115
3230
  ...metadata !== void 0 && { metadata },
@@ -3133,7 +3248,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3133
3248
  };
3134
3249
  }
3135
3250
  const typeLiteral = typeAlias.type;
3136
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3251
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
3137
3252
  const name = typeAlias.name.text;
3138
3253
  const fields = [];
3139
3254
  const typeRegistry = {};
@@ -3174,12 +3289,20 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3174
3289
  diagnostics,
3175
3290
  normalizedMetadataPolicy
3176
3291
  );
3177
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, typeAlias, {
3292
+ const metadata = resolveNodeMetadata(
3293
+ normalizedMetadataPolicy,
3294
+ "type",
3295
+ name,
3296
+ typeAlias,
3178
3297
  checker,
3179
- declaration: typeAlias,
3180
- subjectType: aliasType,
3181
- hostType: aliasType
3182
- });
3298
+ extensionRegistry,
3299
+ {
3300
+ checker,
3301
+ declaration: typeAlias,
3302
+ subjectType: aliasType,
3303
+ hostType: aliasType
3304
+ }
3305
+ );
3183
3306
  return {
3184
3307
  ok: true,
3185
3308
  analysis: {
@@ -3220,7 +3343,7 @@ function getLeadingParsedTags(node) {
3220
3343
  if (!commentText.startsWith("/**")) {
3221
3344
  continue;
3222
3345
  }
3223
- parsedTags.push(...(0, import_internal2.parseCommentBlock)(commentText, { offset: range.pos }).tags);
3346
+ parsedTags.push(...(0, import_internal3.parseCommentBlock)(commentText, { offset: range.pos }).tags);
3224
3347
  }
3225
3348
  return parsedTags;
3226
3349
  }
@@ -3401,10 +3524,7 @@ function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker,
3401
3524
  if (resolvedAnchorNode === null) {
3402
3525
  return void 0;
3403
3526
  }
3404
- const propertyType = checker.getTypeOfSymbolAtLocation(
3405
- propertySymbol,
3406
- resolvedAnchorNode
3407
- );
3527
+ const propertyType = checker.getTypeOfSymbolAtLocation(propertySymbol, resolvedAnchorNode);
3408
3528
  if (propertyType.isStringLiteral()) {
3409
3529
  return propertyType.value;
3410
3530
  }
@@ -3435,6 +3555,8 @@ function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
3435
3555
  "type",
3436
3556
  getDiscriminatorLogicalName(boundType, declaration, checker),
3437
3557
  declaration,
3558
+ checker,
3559
+ void 0,
3438
3560
  {
3439
3561
  checker,
3440
3562
  declaration,
@@ -3671,12 +3793,20 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
3671
3793
  annotations.push(defaultAnnotation);
3672
3794
  }
3673
3795
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
3674
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
3796
+ const metadata = resolveNodeMetadata(
3797
+ metadataPolicy,
3798
+ "field",
3799
+ name,
3800
+ prop,
3675
3801
  checker,
3676
- declaration: prop,
3677
- subjectType: tsType,
3678
- hostType
3679
- });
3802
+ extensionRegistry,
3803
+ {
3804
+ checker,
3805
+ declaration: prop,
3806
+ subjectType: tsType,
3807
+ hostType
3808
+ }
3809
+ );
3680
3810
  return {
3681
3811
  kind: "field",
3682
3812
  name,
@@ -3723,12 +3853,20 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
3723
3853
  let annotations = [];
3724
3854
  annotations.push(...docResult.annotations);
3725
3855
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
3726
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
3856
+ const metadata = resolveNodeMetadata(
3857
+ metadataPolicy,
3858
+ "field",
3859
+ name,
3860
+ prop,
3727
3861
  checker,
3728
- declaration: prop,
3729
- subjectType: tsType,
3730
- hostType
3731
- });
3862
+ extensionRegistry,
3863
+ {
3864
+ checker,
3865
+ declaration: prop,
3866
+ subjectType: tsType,
3867
+ hostType
3868
+ }
3869
+ );
3732
3870
  return {
3733
3871
  kind: "field",
3734
3872
  name,
@@ -3857,7 +3995,7 @@ function getTypeNodeRegistrationName(typeNode) {
3857
3995
  }
3858
3996
  return null;
3859
3997
  }
3860
- function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3998
+ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3861
3999
  const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
3862
4000
  if (customType) {
3863
4001
  return customType;
@@ -3947,7 +4085,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
3947
4085
  }
3948
4086
  return { kind: "primitive", primitiveKind: "string" };
3949
4087
  }
3950
- function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4088
+ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3951
4089
  if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
3952
4090
  return null;
3953
4091
  }
@@ -3967,11 +4105,19 @@ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiti
3967
4105
  file,
3968
4106
  makeParseOptions(extensionRegistry)
3969
4107
  );
3970
- const metadata = resolveNodeMetadata(metadataPolicy, "type", aliasName, aliasDecl, {
4108
+ const metadata = resolveNodeMetadata(
4109
+ metadataPolicy,
4110
+ "type",
4111
+ aliasName,
4112
+ aliasDecl,
3971
4113
  checker,
3972
- declaration: aliasDecl,
3973
- subjectType: aliasType
3974
- });
4114
+ extensionRegistry,
4115
+ {
4116
+ checker,
4117
+ declaration: aliasDecl,
4118
+ subjectType: aliasType
4119
+ }
4120
+ );
3975
4121
  typeRegistry[aliasName] = {
3976
4122
  name: aliasName,
3977
4123
  ...metadata !== void 0 && { metadata },
@@ -4010,7 +4156,7 @@ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
4010
4156
  const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
4011
4157
  return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
4012
4158
  }
4013
- function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4159
+ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4014
4160
  const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
4015
4161
  if (nestedAliasDecl !== void 0) {
4016
4162
  return resolveAliasedPrimitiveTarget(
@@ -4036,7 +4182,7 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
4036
4182
  diagnostics
4037
4183
  );
4038
4184
  }
4039
- function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4185
+ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4040
4186
  const typeName = getNamedTypeName(type);
4041
4187
  const namedDecl = getNamedTypeDeclaration(type);
4042
4188
  if (typeName && typeName in typeRegistry) {
@@ -4071,11 +4217,19 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
4071
4217
  return result;
4072
4218
  }
4073
4219
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
4074
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", typeName, namedDecl, {
4220
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
4221
+ metadataPolicy,
4222
+ "type",
4223
+ typeName,
4224
+ namedDecl,
4075
4225
  checker,
4076
- declaration: namedDecl,
4077
- subjectType: type
4078
- }) : void 0;
4226
+ extensionRegistry,
4227
+ {
4228
+ checker,
4229
+ declaration: namedDecl,
4230
+ subjectType: type
4231
+ }
4232
+ ) : void 0;
4079
4233
  typeRegistry[typeName] = {
4080
4234
  name: typeName,
4081
4235
  ...metadata !== void 0 && { metadata },
@@ -4160,7 +4314,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
4160
4314
  }
4161
4315
  return registerNamed({ kind: "union", members });
4162
4316
  }
4163
- function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4317
+ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4164
4318
  const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
4165
4319
  const elementType = typeArgs?.[0];
4166
4320
  const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
@@ -4177,7 +4331,7 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNod
4177
4331
  ) : { kind: "primitive", primitiveKind: "string" };
4178
4332
  return { kind: "array", items };
4179
4333
  }
4180
- function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4334
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4181
4335
  if (type.getProperties().length > 0) {
4182
4336
  return null;
4183
4337
  }
@@ -4238,7 +4392,7 @@ function shouldEmitResolvedObjectProperty(property, declaration) {
4238
4392
  }
4239
4393
  return true;
4240
4394
  }
4241
- function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4395
+ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4242
4396
  const collectedDiagnostics = diagnostics ?? [];
4243
4397
  const typeName = getNamedTypeName(type);
4244
4398
  const namedTypeName = typeName ?? void 0;
@@ -4314,11 +4468,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4314
4468
  return recordNode;
4315
4469
  }
4316
4470
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
4317
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
4471
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
4472
+ metadataPolicy,
4473
+ "type",
4474
+ registryTypeName,
4475
+ namedDecl,
4318
4476
  checker,
4319
- declaration: namedDecl,
4320
- subjectType: type
4321
- }) : void 0;
4477
+ extensionRegistry,
4478
+ {
4479
+ checker,
4480
+ declaration: namedDecl,
4481
+ subjectType: type
4482
+ }
4483
+ ) : void 0;
4322
4484
  typeRegistry[registryTypeName] = {
4323
4485
  name: registryTypeName,
4324
4486
  ...metadata !== void 0 && { metadata },
@@ -4363,14 +4525,39 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4363
4525
  collectedDiagnostics
4364
4526
  );
4365
4527
  const fieldNodeInfo = fieldInfoMap?.get(prop.name);
4528
+ const inlineFieldNodeInfo = fieldNodeInfo === void 0 ? ts3.isPropertySignature(declaration) ? analyzeInterfacePropertyToIR(
4529
+ declaration,
4530
+ checker,
4531
+ file,
4532
+ typeRegistry,
4533
+ visiting,
4534
+ collectedDiagnostics,
4535
+ type,
4536
+ metadataPolicy,
4537
+ extensionRegistry
4538
+ ) : ts3.isPropertyDeclaration(declaration) ? analyzeFieldToIR(
4539
+ declaration,
4540
+ checker,
4541
+ file,
4542
+ typeRegistry,
4543
+ visiting,
4544
+ collectedDiagnostics,
4545
+ type,
4546
+ metadataPolicy,
4547
+ extensionRegistry
4548
+ ) : null : null;
4549
+ const resolvedFieldNodeInfo = fieldNodeInfo ?? inlineFieldNodeInfo;
4550
+ const resolvedPropertyType = inlineFieldNodeInfo?.type ?? propTypeNode;
4366
4551
  properties.push({
4367
4552
  name: prop.name,
4368
- ...fieldNodeInfo?.metadata !== void 0 && { metadata: fieldNodeInfo.metadata },
4369
- type: propTypeNode,
4553
+ ...resolvedFieldNodeInfo?.metadata !== void 0 && {
4554
+ metadata: resolvedFieldNodeInfo.metadata
4555
+ },
4556
+ type: resolvedPropertyType,
4370
4557
  optional,
4371
- constraints: fieldNodeInfo?.constraints ?? [],
4372
- annotations: fieldNodeInfo?.annotations ?? [],
4373
- provenance: fieldNodeInfo?.provenance ?? provenanceForFile(file)
4558
+ constraints: resolvedFieldNodeInfo?.constraints ?? [],
4559
+ annotations: resolvedFieldNodeInfo?.annotations ?? [],
4560
+ provenance: resolvedFieldNodeInfo?.provenance ?? provenanceForFile(file)
4374
4561
  });
4375
4562
  }
4376
4563
  visiting.delete(type);
@@ -4389,11 +4576,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4389
4576
  };
4390
4577
  if (registryTypeName !== void 0 && shouldRegisterNamedType) {
4391
4578
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
4392
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
4579
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
4580
+ metadataPolicy,
4581
+ "type",
4582
+ registryTypeName,
4583
+ namedDecl,
4393
4584
  checker,
4394
- declaration: namedDecl,
4395
- subjectType: type
4396
- }) : void 0;
4585
+ extensionRegistry,
4586
+ {
4587
+ checker,
4588
+ declaration: namedDecl,
4589
+ subjectType: type
4590
+ }
4591
+ ) : void 0;
4397
4592
  typeRegistry[registryTypeName] = {
4398
4593
  name: registryTypeName,
4399
4594
  ...metadata !== void 0 && { metadata },
@@ -4670,12 +4865,12 @@ function detectFormSpecReference(typeNode) {
4670
4865
  }
4671
4866
  return null;
4672
4867
  }
4673
- var ts3, import_internal2, RESOLVING_TYPE_PLACEHOLDER, MAX_ALIAS_CHAIN_DEPTH;
4868
+ var ts3, import_internal3, RESOLVING_TYPE_PLACEHOLDER, MAX_ALIAS_CHAIN_DEPTH;
4674
4869
  var init_class_analyzer = __esm({
4675
4870
  "src/analyzer/class-analyzer.ts"() {
4676
4871
  "use strict";
4677
4872
  ts3 = __toESM(require("typescript"), 1);
4678
- import_internal2 = require("@formspec/analysis/internal");
4873
+ import_internal3 = require("@formspec/analysis/internal");
4679
4874
  init_jsdoc_constraints();
4680
4875
  init_tsdoc_parser();
4681
4876
  init_metadata();
@@ -4831,7 +5026,7 @@ var init_program = __esm({
4831
5026
 
4832
5027
  // src/validate/constraint-validator.ts
4833
5028
  function validateFieldNode(ctx, field) {
4834
- const analysis = (0, import_internal3.analyzeConstraintTargets)(
5029
+ const analysis = (0, import_internal4.analyzeConstraintTargets)(
4835
5030
  field.name,
4836
5031
  field.type,
4837
5032
  field.constraints,
@@ -4849,7 +5044,7 @@ function validateFieldNode(ctx, field) {
4849
5044
  }
4850
5045
  function validateObjectProperty(ctx, parentName, property) {
4851
5046
  const qualifiedName = `${parentName}.${property.name}`;
4852
- const analysis = (0, import_internal3.analyzeConstraintTargets)(
5047
+ const analysis = (0, import_internal4.analyzeConstraintTargets)(
4853
5048
  qualifiedName,
4854
5049
  property.type,
4855
5050
  property.constraints,
@@ -4900,11 +5095,11 @@ function validateIR(ir, options) {
4900
5095
  valid: ctx.diagnostics.every((diagnostic) => diagnostic.severity !== "error")
4901
5096
  };
4902
5097
  }
4903
- var import_internal3;
5098
+ var import_internal4;
4904
5099
  var init_constraint_validator = __esm({
4905
5100
  "src/validate/constraint-validator.ts"() {
4906
5101
  "use strict";
4907
- import_internal3 = require("@formspec/analysis/internal");
5102
+ import_internal4 = require("@formspec/analysis/internal");
4908
5103
  }
4909
5104
  });
4910
5105
 
@@ -5018,6 +5213,362 @@ var init_class_schema = __esm({
5018
5213
  }
5019
5214
  });
5020
5215
 
5216
+ // src/static-build.ts
5217
+ function toStaticBuildContext(context) {
5218
+ return context;
5219
+ }
5220
+ function createStaticBuildContext(filePath) {
5221
+ return toStaticBuildContext(createProgramContext(filePath));
5222
+ }
5223
+ function createStaticBuildContextFromProgram(program, filePath) {
5224
+ return toStaticBuildContext(createProgramContextFromProgram(program, filePath));
5225
+ }
5226
+ function getModuleSymbol(context) {
5227
+ const sourceFileWithSymbol = context.sourceFile;
5228
+ return context.checker.getSymbolAtLocation(context.sourceFile) ?? sourceFileWithSymbol.symbol;
5229
+ }
5230
+ function isSchemaSourceDeclaration(declaration) {
5231
+ return ts6.isClassDeclaration(declaration) || ts6.isInterfaceDeclaration(declaration) || ts6.isTypeAliasDeclaration(declaration);
5232
+ }
5233
+ function resolveModuleExport(context, exportName = "default") {
5234
+ const moduleSymbol = getModuleSymbol(context);
5235
+ if (moduleSymbol === void 0) {
5236
+ return null;
5237
+ }
5238
+ const exportSymbol = context.checker.getExportsOfModule(moduleSymbol).find((candidate) => candidate.name === exportName) ?? null;
5239
+ if (exportSymbol === null) {
5240
+ return null;
5241
+ }
5242
+ return exportSymbol.flags & ts6.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
5243
+ }
5244
+ function resolveModuleExportDeclaration(context, exportName = "default") {
5245
+ return resolveModuleExport(context, exportName)?.declarations?.find(isSchemaSourceDeclaration) ?? null;
5246
+ }
5247
+ var ts6;
5248
+ var init_static_build = __esm({
5249
+ "src/static-build.ts"() {
5250
+ "use strict";
5251
+ ts6 = __toESM(require("typescript"), 1);
5252
+ init_program();
5253
+ }
5254
+ });
5255
+
5256
+ // src/generators/discovered-schema.ts
5257
+ function toDiscoveredTypeSchemas(result) {
5258
+ return result;
5259
+ }
5260
+ function isNamedTypeDeclaration(declaration) {
5261
+ return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
5262
+ }
5263
+ function hasConcreteTypeArguments(type, checker) {
5264
+ if ("aliasTypeArguments" in type && Array.isArray(type.aliasTypeArguments) && type.aliasTypeArguments.length > 0) {
5265
+ return true;
5266
+ }
5267
+ if ((type.flags & ts7.TypeFlags.Object) === 0) {
5268
+ return false;
5269
+ }
5270
+ const objectType = type;
5271
+ if ((objectType.objectFlags & ts7.ObjectFlags.Reference) === 0) {
5272
+ return false;
5273
+ }
5274
+ return checker.getTypeArguments(objectType).length > 0;
5275
+ }
5276
+ function getNamedTypeDeclaration2(type) {
5277
+ const symbol = type.getSymbol();
5278
+ if (symbol?.declarations !== void 0) {
5279
+ const declaration = symbol.declarations[0];
5280
+ if (declaration !== void 0 && isNamedTypeDeclaration(declaration)) {
5281
+ return declaration;
5282
+ }
5283
+ }
5284
+ const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts7.isTypeAliasDeclaration);
5285
+ return aliasDeclaration;
5286
+ }
5287
+ function getFallbackName(sourceNode, fallback = "AnonymousType") {
5288
+ if (sourceNode !== void 0 && "name" in sourceNode) {
5289
+ const namedNode = sourceNode;
5290
+ if (namedNode.name !== void 0 && ts7.isIdentifier(namedNode.name)) {
5291
+ return namedNode.name.text;
5292
+ }
5293
+ }
5294
+ return fallback;
5295
+ }
5296
+ function createObjectRootAnalysis(name, properties, typeRegistry, metadata, annotations) {
5297
+ const fields = properties.map((property) => ({
5298
+ kind: "field",
5299
+ name: property.name,
5300
+ ...property.metadata !== void 0 && { metadata: property.metadata },
5301
+ type: property.type,
5302
+ required: !property.optional,
5303
+ constraints: property.constraints,
5304
+ annotations: property.annotations,
5305
+ provenance: property.provenance
5306
+ }));
5307
+ return {
5308
+ name,
5309
+ ...metadata !== void 0 && { metadata },
5310
+ fields,
5311
+ fieldLayouts: fields.map(() => ({})),
5312
+ typeRegistry,
5313
+ ...annotations !== void 0 && annotations.length > 0 && { annotations },
5314
+ instanceMethods: [],
5315
+ staticMethods: [],
5316
+ diagnostics: []
5317
+ };
5318
+ }
5319
+ function omitApiName(metadata) {
5320
+ if (metadata?.apiName === void 0) {
5321
+ return metadata;
5322
+ }
5323
+ const { apiName: _apiName, ...rest } = metadata;
5324
+ return Object.keys(rest).length > 0 ? rest : void 0;
5325
+ }
5326
+ function describeRootType(rootType, typeRegistry, fallbackName) {
5327
+ if (rootType.kind !== "reference") {
5328
+ return {
5329
+ name: fallbackName,
5330
+ type: rootType
5331
+ };
5332
+ }
5333
+ const definition = typeRegistry[rootType.name];
5334
+ if (definition === void 0) {
5335
+ return {
5336
+ name: rootType.name,
5337
+ type: rootType
5338
+ };
5339
+ }
5340
+ return {
5341
+ name: definition.name,
5342
+ ...definition.metadata !== void 0 && { metadata: definition.metadata },
5343
+ ...definition.annotations !== void 0 && definition.annotations.length > 0 && { annotations: definition.annotations },
5344
+ type: definition.type
5345
+ };
5346
+ }
5347
+ function toStandaloneJsonSchema(root, typeRegistry, options) {
5348
+ const syntheticFieldMetadata = omitApiName(root.metadata);
5349
+ const syntheticField = {
5350
+ kind: "field",
5351
+ name: "__result",
5352
+ ...syntheticFieldMetadata !== void 0 && { metadata: syntheticFieldMetadata },
5353
+ type: root.type,
5354
+ required: true,
5355
+ constraints: [],
5356
+ annotations: [...root.annotations ?? []],
5357
+ provenance: {
5358
+ surface: "tsdoc",
5359
+ file: "",
5360
+ line: 1,
5361
+ column: 0
5362
+ }
5363
+ };
5364
+ const schema = generateJsonSchemaFromIR(
5365
+ {
5366
+ kind: "form-ir",
5367
+ name: root.name,
5368
+ irVersion: import_internals6.IR_VERSION,
5369
+ elements: [syntheticField],
5370
+ ...root.metadata !== void 0 && { metadata: root.metadata },
5371
+ ...root.annotations !== void 0 && root.annotations.length > 0 && { rootAnnotations: root.annotations },
5372
+ typeRegistry,
5373
+ provenance: syntheticField.provenance
5374
+ },
5375
+ {
5376
+ extensionRegistry: options?.extensionRegistry,
5377
+ vendorPrefix: options?.vendorPrefix
5378
+ }
5379
+ );
5380
+ const result = schema.properties?.["__result"];
5381
+ if (result === void 0) {
5382
+ throw new Error("FormSpec failed to extract the standalone schema root from the synthetic IR.");
5383
+ }
5384
+ if (schema.$defs === void 0 || Object.keys(schema.$defs).length === 0) {
5385
+ return {
5386
+ ...schema.$schema !== void 0 && { $schema: schema.$schema },
5387
+ ...result
5388
+ };
5389
+ }
5390
+ return {
5391
+ ...schema.$schema !== void 0 && { $schema: schema.$schema },
5392
+ ...result,
5393
+ $defs: schema.$defs
5394
+ };
5395
+ }
5396
+ function generateSchemasFromAnalysis(analysis, filePath, options) {
5397
+ return toDiscoveredTypeSchemas(
5398
+ generateClassSchemas(
5399
+ analysis,
5400
+ { file: filePath },
5401
+ {
5402
+ extensionRegistry: options?.extensionRegistry,
5403
+ metadata: options?.metadata,
5404
+ vendorPrefix: options?.vendorPrefix
5405
+ }
5406
+ )
5407
+ );
5408
+ }
5409
+ function generateSchemasFromResolvedType(options, skipNamedDeclaration = false, rootOverride) {
5410
+ const namedDeclaration = skipNamedDeclaration || hasConcreteTypeArguments(options.type, options.context.checker) ? void 0 : getNamedTypeDeclaration2(options.type);
5411
+ if (namedDeclaration !== void 0) {
5412
+ return generateSchemasFromDeclaration({
5413
+ ...options,
5414
+ declaration: namedDeclaration
5415
+ });
5416
+ }
5417
+ const filePath = options.sourceNode?.getSourceFile().fileName ?? options.context.sourceFile.fileName;
5418
+ const typeRegistry = {};
5419
+ const diagnostics = [];
5420
+ const rootType = resolveTypeNode(
5421
+ options.type,
5422
+ options.context.checker,
5423
+ filePath,
5424
+ typeRegistry,
5425
+ /* @__PURE__ */ new Set(),
5426
+ options.sourceNode,
5427
+ createAnalyzerMetadataPolicy(options.metadata),
5428
+ options.extensionRegistry,
5429
+ diagnostics
5430
+ );
5431
+ if (diagnostics.length > 0) {
5432
+ const diagnosticDetails = diagnostics.map((diagnostic) => `${diagnostic.code}: ${diagnostic.message}`).join("; ");
5433
+ throw new Error(
5434
+ `FormSpec validation failed while generating discovered type schemas. ${diagnosticDetails}`
5435
+ );
5436
+ }
5437
+ const describedRoot = describeRootType(
5438
+ rootType,
5439
+ typeRegistry,
5440
+ options.name ?? getFallbackName(options.sourceNode)
5441
+ );
5442
+ const mergedMetadata = mergeResolvedMetadata(describedRoot.metadata, rootOverride?.metadata);
5443
+ const root = {
5444
+ ...describedRoot,
5445
+ ...rootOverride?.name !== void 0 && { name: rootOverride.name },
5446
+ ...mergedMetadata !== void 0 && { metadata: mergedMetadata },
5447
+ ...rootOverride?.annotations !== void 0 && { annotations: rootOverride.annotations }
5448
+ };
5449
+ if (root.type.kind === "object") {
5450
+ return generateSchemasFromAnalysis(
5451
+ createObjectRootAnalysis(
5452
+ options.name ?? root.name,
5453
+ root.type.properties,
5454
+ typeRegistry,
5455
+ root.metadata,
5456
+ root.annotations
5457
+ ),
5458
+ filePath,
5459
+ options
5460
+ );
5461
+ }
5462
+ return {
5463
+ jsonSchema: toStandaloneJsonSchema(root, typeRegistry, options),
5464
+ uiSchema: null
5465
+ };
5466
+ }
5467
+ function generateSchemasFromDeclaration(options) {
5468
+ const filePath = options.declaration.getSourceFile().fileName;
5469
+ if (ts7.isClassDeclaration(options.declaration)) {
5470
+ return generateSchemasFromAnalysis(
5471
+ analyzeClassToIR(
5472
+ options.declaration,
5473
+ options.context.checker,
5474
+ filePath,
5475
+ options.extensionRegistry,
5476
+ options.metadata
5477
+ ),
5478
+ filePath,
5479
+ options
5480
+ );
5481
+ }
5482
+ if (ts7.isInterfaceDeclaration(options.declaration)) {
5483
+ return generateSchemasFromAnalysis(
5484
+ analyzeInterfaceToIR(
5485
+ options.declaration,
5486
+ options.context.checker,
5487
+ filePath,
5488
+ options.extensionRegistry,
5489
+ options.metadata
5490
+ ),
5491
+ filePath,
5492
+ options
5493
+ );
5494
+ }
5495
+ if (ts7.isTypeAliasDeclaration(options.declaration)) {
5496
+ const analyzedAlias = analyzeTypeAliasToIR(
5497
+ options.declaration,
5498
+ options.context.checker,
5499
+ filePath,
5500
+ options.extensionRegistry,
5501
+ options.metadata
5502
+ );
5503
+ if (analyzedAlias.ok) {
5504
+ return generateSchemasFromAnalysis(analyzedAlias.analysis, filePath, options);
5505
+ }
5506
+ const aliasRootInfo = analyzeDeclarationRootInfo(
5507
+ options.declaration,
5508
+ options.context.checker,
5509
+ filePath,
5510
+ options.extensionRegistry,
5511
+ options.metadata
5512
+ );
5513
+ if (aliasRootInfo.diagnostics.length > 0) {
5514
+ const diagnosticDetails = aliasRootInfo.diagnostics.map((diagnostic) => `${diagnostic.code}: ${diagnostic.message}`).join("; ");
5515
+ throw new Error(
5516
+ `FormSpec validation failed while generating discovered type schemas. ${diagnosticDetails}`
5517
+ );
5518
+ }
5519
+ return generateSchemasFromResolvedType(
5520
+ {
5521
+ ...options,
5522
+ type: options.context.checker.getTypeAtLocation(options.declaration),
5523
+ sourceNode: options.declaration,
5524
+ name: options.declaration.name.text
5525
+ },
5526
+ true,
5527
+ {
5528
+ name: options.declaration.name.text,
5529
+ ...aliasRootInfo.metadata !== void 0 && { metadata: aliasRootInfo.metadata },
5530
+ ...aliasRootInfo.annotations.length > 0 && { annotations: aliasRootInfo.annotations }
5531
+ }
5532
+ );
5533
+ }
5534
+ const _exhaustive = options.declaration;
5535
+ return _exhaustive;
5536
+ }
5537
+ function generateSchemasFromType(options) {
5538
+ return generateSchemasFromResolvedType(options);
5539
+ }
5540
+ function generateSchemasFromParameter(options) {
5541
+ return generateSchemasFromResolvedType({
5542
+ ...options,
5543
+ type: options.context.checker.getTypeAtLocation(options.parameter),
5544
+ sourceNode: options.parameter,
5545
+ name: getFallbackName(options.parameter, "Parameter")
5546
+ });
5547
+ }
5548
+ function generateSchemasFromReturnType(options) {
5549
+ const signature = options.context.checker.getSignatureFromDeclaration(options.declaration);
5550
+ const type = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
5551
+ const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
5552
+ return generateSchemasFromResolvedType({
5553
+ ...options,
5554
+ type,
5555
+ sourceNode: options.declaration.type ?? options.declaration,
5556
+ name: fallbackName
5557
+ });
5558
+ }
5559
+ var ts7, import_internals6;
5560
+ var init_discovered_schema = __esm({
5561
+ "src/generators/discovered-schema.ts"() {
5562
+ "use strict";
5563
+ ts7 = __toESM(require("typescript"), 1);
5564
+ init_class_analyzer();
5565
+ init_class_schema();
5566
+ init_ir_generator();
5567
+ import_internals6 = require("@formspec/core/internals");
5568
+ init_metadata();
5569
+ }
5570
+ });
5571
+
5021
5572
  // src/generators/mixed-authoring.ts
5022
5573
  function buildMixedAuthoringSchemas(options) {
5023
5574
  const { filePath, typeName, overlays, ...schemaOptions } = options;
@@ -5224,12 +5775,20 @@ __export(index_exports, {
5224
5775
  buildFormSchemas: () => buildFormSchemas,
5225
5776
  buildMixedAuthoringSchemas: () => buildMixedAuthoringSchemas,
5226
5777
  createExtensionRegistry: () => createExtensionRegistry,
5778
+ createStaticBuildContext: () => createStaticBuildContext,
5779
+ createStaticBuildContextFromProgram: () => createStaticBuildContextFromProgram,
5227
5780
  generateJsonSchema: () => generateJsonSchema,
5228
5781
  generateSchemas: () => generateSchemas,
5229
5782
  generateSchemasFromClass: () => generateSchemasFromClass,
5783
+ generateSchemasFromDeclaration: () => generateSchemasFromDeclaration,
5784
+ generateSchemasFromParameter: () => generateSchemasFromParameter,
5230
5785
  generateSchemasFromProgram: () => generateSchemasFromProgram,
5786
+ generateSchemasFromReturnType: () => generateSchemasFromReturnType,
5787
+ generateSchemasFromType: () => generateSchemasFromType,
5231
5788
  generateUiSchema: () => generateUiSchema,
5232
5789
  jsonSchema7Schema: () => jsonSchema7Schema,
5790
+ resolveModuleExport: () => resolveModuleExport,
5791
+ resolveModuleExportDeclaration: () => resolveModuleExportDeclaration,
5233
5792
  uiSchemaSchema: () => uiSchema,
5234
5793
  writeSchemas: () => writeSchemas
5235
5794
  });
@@ -5267,6 +5826,8 @@ var init_index = __esm({
5267
5826
  init_generator();
5268
5827
  init_generator2();
5269
5828
  init_class_schema();
5829
+ init_static_build();
5830
+ init_discovered_schema();
5270
5831
  init_mixed_authoring();
5271
5832
  }
5272
5833
  });