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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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();
@@ -2904,6 +3000,9 @@ var init_jsdoc_constraints = __esm({
2904
3000
  function isObjectType(type) {
2905
3001
  return !!(type.flags & ts3.TypeFlags.Object);
2906
3002
  }
3003
+ function isIntersectionType(type) {
3004
+ return !!(type.flags & ts3.TypeFlags.Intersection);
3005
+ }
2907
3006
  function isTypeReference(type) {
2908
3007
  return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
2909
3008
  }
@@ -2919,76 +3018,51 @@ function makeParseOptions(extensionRegistry, fieldType, checker, subjectType, ho
2919
3018
  ...hostType !== void 0 && { hostType }
2920
3019
  };
2921
3020
  }
2922
- function makeExplicitScalarMetadata(value) {
2923
- return value === void 0 || value === "" ? void 0 : { value, source: "explicit" };
2924
- }
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
- }
2958
- }
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
- }
3021
+ function createAnalyzerMetadataPolicy(input, discriminator) {
3022
+ return {
3023
+ raw: input,
3024
+ normalized: normalizeMetadataPolicy(input),
3025
+ discriminator
2970
3026
  };
2971
- return Object.keys(metadata).length === 0 ? void 0 : metadata;
2972
3027
  }
2973
- function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, buildContext) {
2974
- const explicit = extractExplicitMetadata(node);
2975
- return resolveMetadata(
2976
- {
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)
3028
+ function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, checker, extensionRegistry, buildContext) {
3029
+ const analysis = (0, import_internal3.analyzeMetadataForNodeWithChecker)({
3030
+ checker,
3031
+ node,
3032
+ logicalName,
3033
+ metadata: metadataPolicy.raw,
3034
+ extensions: extensionRegistry?.extensions,
3035
+ ...buildContext !== void 0 && { buildContext }
3036
+ });
3037
+ const resolvedMetadata = analysis?.resolvedMetadata;
3038
+ const declarationPolicy = getDeclarationMetadataPolicy(
3039
+ metadataPolicy.normalized,
3040
+ declarationKind
2988
3041
  );
3042
+ if (resolvedMetadata?.apiName === void 0 && declarationPolicy.apiName.mode === "require-explicit") {
3043
+ throw new Error(
3044
+ `Metadata policy requires explicit apiName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
3045
+ );
3046
+ }
3047
+ if (resolvedMetadata?.displayName === void 0 && declarationPolicy.displayName.mode === "require-explicit") {
3048
+ throw new Error(
3049
+ `Metadata policy requires explicit displayName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
3050
+ );
3051
+ }
3052
+ if (resolvedMetadata?.apiNamePlural === void 0 && declarationPolicy.apiName.pluralization.mode === "require-explicit") {
3053
+ throw new Error(
3054
+ `Metadata policy requires explicit apiNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
3055
+ );
3056
+ }
3057
+ if (resolvedMetadata?.displayNamePlural === void 0 && declarationPolicy.displayName.pluralization.mode === "require-explicit") {
3058
+ throw new Error(
3059
+ `Metadata policy requires explicit displayNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
3060
+ );
3061
+ }
3062
+ return resolvedMetadata;
2989
3063
  }
2990
3064
  function analyzeDeclarationRootInfo(declaration, checker, file = "", extensionRegistry, metadataPolicy) {
2991
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3065
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
2992
3066
  const declarationType = checker.getTypeAtLocation(declaration);
2993
3067
  const logicalName = ts3.isClassDeclaration(declaration) ? declaration.name?.text ?? "AnonymousClass" : declaration.name.text;
2994
3068
  const docResult = extractJSDocParseResult(
@@ -2996,20 +3070,31 @@ function analyzeDeclarationRootInfo(declaration, checker, file = "", extensionRe
2996
3070
  file,
2997
3071
  makeParseOptions(extensionRegistry, void 0, checker, declarationType, declarationType)
2998
3072
  );
2999
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", logicalName, declaration, {
3000
- checker,
3073
+ const metadata = resolveNodeMetadata(
3074
+ normalizedMetadataPolicy,
3075
+ "type",
3076
+ logicalName,
3001
3077
  declaration,
3002
- subjectType: declarationType,
3003
- hostType: declarationType
3004
- });
3078
+ checker,
3079
+ extensionRegistry,
3080
+ {
3081
+ checker,
3082
+ declaration,
3083
+ subjectType: declarationType,
3084
+ hostType: declarationType
3085
+ }
3086
+ );
3005
3087
  return {
3006
3088
  ...metadata !== void 0 && { metadata },
3007
3089
  annotations: docResult.annotations,
3008
3090
  diagnostics: docResult.diagnostics
3009
3091
  };
3010
3092
  }
3011
- function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy) {
3012
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3093
+ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
3094
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
3095
+ metadataPolicy,
3096
+ discriminatorOptions
3097
+ );
3013
3098
  const name = classDecl.name?.text ?? "AnonymousClass";
3014
3099
  const fields = [];
3015
3100
  const fieldLayouts = [];
@@ -3064,12 +3149,20 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
3064
3149
  diagnostics,
3065
3150
  normalizedMetadataPolicy
3066
3151
  );
3067
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, classDecl, {
3152
+ const metadata = resolveNodeMetadata(
3153
+ normalizedMetadataPolicy,
3154
+ "type",
3155
+ name,
3156
+ classDecl,
3068
3157
  checker,
3069
- declaration: classDecl,
3070
- subjectType: classType,
3071
- hostType: classType
3072
- });
3158
+ extensionRegistry,
3159
+ {
3160
+ checker,
3161
+ declaration: classDecl,
3162
+ subjectType: classType,
3163
+ hostType: classType
3164
+ }
3165
+ );
3073
3166
  return {
3074
3167
  name,
3075
3168
  ...metadata !== void 0 && { metadata },
@@ -3082,8 +3175,11 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
3082
3175
  staticMethods
3083
3176
  };
3084
3177
  }
3085
- function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy) {
3086
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3178
+ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
3179
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
3180
+ metadataPolicy,
3181
+ discriminatorOptions
3182
+ );
3087
3183
  const name = interfaceDecl.name.text;
3088
3184
  const fields = [];
3089
3185
  const typeRegistry = {};
@@ -3125,12 +3221,20 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
3125
3221
  normalizedMetadataPolicy
3126
3222
  );
3127
3223
  const fieldLayouts = specializedFields.map(() => ({}));
3128
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, interfaceDecl, {
3224
+ const metadata = resolveNodeMetadata(
3225
+ normalizedMetadataPolicy,
3226
+ "type",
3227
+ name,
3228
+ interfaceDecl,
3129
3229
  checker,
3130
- declaration: interfaceDecl,
3131
- subjectType: interfaceType,
3132
- hostType: interfaceType
3133
- });
3230
+ extensionRegistry,
3231
+ {
3232
+ checker,
3233
+ declaration: interfaceDecl,
3234
+ subjectType: interfaceType,
3235
+ hostType: interfaceType
3236
+ }
3237
+ );
3134
3238
  return {
3135
3239
  name,
3136
3240
  ...metadata !== void 0 && { metadata },
@@ -3143,19 +3247,31 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
3143
3247
  staticMethods: []
3144
3248
  };
3145
3249
  }
3146
- function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry, metadataPolicy) {
3147
- if (!ts3.isTypeLiteralNode(typeAlias.type)) {
3250
+ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
3251
+ const members = getObjectLikeTypeAliasMembers(typeAlias.type);
3252
+ if (members === null) {
3148
3253
  const sourceFile = typeAlias.getSourceFile();
3149
3254
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
3150
3255
  const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
3151
3256
  return {
3152
3257
  ok: false,
3153
- error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
3258
+ error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
3154
3259
  };
3155
3260
  }
3156
- const typeLiteral = typeAlias.type;
3157
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3261
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
3262
+ metadataPolicy,
3263
+ discriminatorOptions
3264
+ );
3158
3265
  const name = typeAlias.name.text;
3266
+ const duplicatePropertyNames = findDuplicateObjectLikeTypeAliasPropertyNames(members);
3267
+ if (duplicatePropertyNames.length > 0) {
3268
+ const sourceFile = typeAlias.getSourceFile();
3269
+ const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
3270
+ return {
3271
+ ok: false,
3272
+ error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
3273
+ };
3274
+ }
3159
3275
  const fields = [];
3160
3276
  const typeRegistry = {};
3161
3277
  const diagnostics = [];
@@ -3168,7 +3284,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3168
3284
  const annotations = [...typeAliasDoc.annotations];
3169
3285
  diagnostics.push(...typeAliasDoc.diagnostics);
3170
3286
  const visiting = /* @__PURE__ */ new Set();
3171
- for (const member of typeLiteral.members) {
3287
+ for (const member of members) {
3172
3288
  if (ts3.isPropertySignature(member)) {
3173
3289
  const fieldNode = analyzeInterfacePropertyToIR(
3174
3290
  member,
@@ -3195,12 +3311,20 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3195
3311
  diagnostics,
3196
3312
  normalizedMetadataPolicy
3197
3313
  );
3198
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, typeAlias, {
3314
+ const metadata = resolveNodeMetadata(
3315
+ normalizedMetadataPolicy,
3316
+ "type",
3317
+ name,
3318
+ typeAlias,
3199
3319
  checker,
3200
- declaration: typeAlias,
3201
- subjectType: aliasType,
3202
- hostType: aliasType
3203
- });
3320
+ extensionRegistry,
3321
+ {
3322
+ checker,
3323
+ declaration: typeAlias,
3324
+ subjectType: aliasType,
3325
+ hostType: aliasType
3326
+ }
3327
+ );
3204
3328
  return {
3205
3329
  ok: true,
3206
3330
  analysis: {
@@ -3241,7 +3365,7 @@ function getLeadingParsedTags(node) {
3241
3365
  if (!commentText.startsWith("/**")) {
3242
3366
  continue;
3243
3367
  }
3244
- parsedTags.push(...(0, import_internal2.parseCommentBlock)(commentText, { offset: range.pos }).tags);
3368
+ parsedTags.push(...(0, import_internal3.parseCommentBlock)(commentText, { offset: range.pos }).tags);
3245
3369
  }
3246
3370
  return parsedTags;
3247
3371
  }
@@ -3269,15 +3393,43 @@ function isNullishSemanticType(type) {
3269
3393
  }
3270
3394
  return type.isUnion() && type.types.some((member) => isNullishSemanticType(member));
3271
3395
  }
3272
- function isStringLikeSemanticType(type) {
3396
+ function isStringLikeSemanticType(type, checker, seen = /* @__PURE__ */ new Set()) {
3397
+ if (seen.has(type)) {
3398
+ return false;
3399
+ }
3400
+ seen.add(type);
3273
3401
  if (type.flags & ts3.TypeFlags.StringLike) {
3274
3402
  return true;
3275
3403
  }
3276
3404
  if (type.isUnion()) {
3277
- return type.types.length > 0 && type.types.every((member) => isStringLikeSemanticType(member));
3405
+ return type.types.length > 0 && type.types.every((member) => isStringLikeSemanticType(member, checker, seen));
3406
+ }
3407
+ const baseConstraint = checker.getBaseConstraintOfType(type);
3408
+ if (baseConstraint !== void 0 && baseConstraint !== type) {
3409
+ return isStringLikeSemanticType(baseConstraint, checker, seen);
3278
3410
  }
3279
3411
  return false;
3280
3412
  }
3413
+ function getObjectLikeTypeAliasMembers(typeNode) {
3414
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
3415
+ return getObjectLikeTypeAliasMembers(typeNode.type);
3416
+ }
3417
+ if (ts3.isTypeLiteralNode(typeNode)) {
3418
+ return [...typeNode.members];
3419
+ }
3420
+ if (ts3.isIntersectionTypeNode(typeNode)) {
3421
+ const members = [];
3422
+ for (const intersectionMember of typeNode.types) {
3423
+ const resolvedMembers = getObjectLikeTypeAliasMembers(intersectionMember);
3424
+ if (resolvedMembers === null) {
3425
+ return null;
3426
+ }
3427
+ members.push(...resolvedMembers);
3428
+ }
3429
+ return members;
3430
+ }
3431
+ return null;
3432
+ }
3281
3433
  function extractDiscriminatorDirective(node, file, diagnostics) {
3282
3434
  const discriminatorTags = getLeadingParsedTags(node).filter(
3283
3435
  (tag) => tag.normalizedTagName === "discriminator"
@@ -3384,7 +3536,7 @@ function validateDiscriminatorDirective(node, checker, file, diagnostics) {
3384
3536
  );
3385
3537
  return null;
3386
3538
  }
3387
- if (!isStringLikeSemanticType(property.type)) {
3539
+ if (!isStringLikeSemanticType(property.type, checker)) {
3388
3540
  diagnostics.push(
3389
3541
  makeAnalysisDiagnostic(
3390
3542
  "TYPE_MISMATCH",
@@ -3411,8 +3563,8 @@ function getConcreteTypeArgumentForDiscriminator(node, subjectType, checker, typ
3411
3563
  const localTypeParameter = node.typeParameters?.[typeParameterIndex];
3412
3564
  return localTypeParameter === void 0 ? null : checker.getTypeAtLocation(localTypeParameter);
3413
3565
  }
3414
- function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker, provenance, diagnostics) {
3415
- const propertySymbol = boundType.getProperty(fieldName);
3566
+ function resolveLiteralDiscriminatorPropertyValue(boundType, propertyName, checker, provenance, diagnostics) {
3567
+ const propertySymbol = boundType.getProperty(propertyName);
3416
3568
  if (propertySymbol === void 0) {
3417
3569
  return void 0;
3418
3570
  }
@@ -3443,6 +3595,9 @@ function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker,
3443
3595
  }
3444
3596
  return void 0;
3445
3597
  }
3598
+ function getDiscriminatorIdentityPropertyNames(fieldName) {
3599
+ return fieldName === "object" ? ["object"] : [fieldName, "object"];
3600
+ }
3446
3601
  function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
3447
3602
  const declaration = resolveNamedDiscriminatorDeclaration(boundType, checker);
3448
3603
  if (declaration === null) {
@@ -3453,6 +3608,8 @@ function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
3453
3608
  "type",
3454
3609
  getDiscriminatorLogicalName(boundType, declaration, checker),
3455
3610
  declaration,
3611
+ checker,
3612
+ void 0,
3456
3613
  {
3457
3614
  checker,
3458
3615
  declaration,
@@ -3461,6 +3618,10 @@ function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
3461
3618
  );
3462
3619
  return metadata?.apiName;
3463
3620
  }
3621
+ function applyDiscriminatorApiNamePrefix(value, discriminatorOptions) {
3622
+ const prefix = discriminatorOptions?.apiNamePrefix;
3623
+ return prefix === void 0 || prefix === "" ? value : `${prefix}${value}`;
3624
+ }
3464
3625
  function resolveNamedDiscriminatorDeclaration(type, checker, seen = /* @__PURE__ */ new Set()) {
3465
3626
  if (seen.has(type)) {
3466
3627
  return null;
@@ -3515,22 +3676,27 @@ function resolveDiscriminatorValue(boundType, fieldName, checker, provenance, di
3515
3676
  return null;
3516
3677
  }
3517
3678
  }
3518
- const literalIdentityValue = resolveLiteralDiscriminatorPropertyValue(
3519
- boundType,
3520
- fieldName,
3521
- checker,
3522
- provenance,
3523
- diagnostics
3524
- );
3525
- if (literalIdentityValue !== void 0) {
3526
- return literalIdentityValue;
3679
+ for (const identityPropertyName of getDiscriminatorIdentityPropertyNames(fieldName)) {
3680
+ const literalIdentityValue = resolveLiteralDiscriminatorPropertyValue(
3681
+ boundType,
3682
+ identityPropertyName,
3683
+ checker,
3684
+ provenance,
3685
+ diagnostics
3686
+ );
3687
+ if (literalIdentityValue === null) {
3688
+ return null;
3689
+ }
3690
+ if (literalIdentityValue !== void 0) {
3691
+ return literalIdentityValue;
3692
+ }
3527
3693
  }
3528
3694
  const apiName = resolveDiscriminatorApiName(boundType, checker, metadataPolicy);
3529
3695
  if (apiName?.source === "explicit") {
3530
- return apiName.value;
3696
+ return applyDiscriminatorApiNamePrefix(apiName.value, metadataPolicy.discriminator);
3531
3697
  }
3532
3698
  if (apiName?.source === "inferred") {
3533
- return apiName.value;
3699
+ return applyDiscriminatorApiNamePrefix(apiName.value, metadataPolicy.discriminator);
3534
3700
  }
3535
3701
  diagnostics.push(
3536
3702
  makeAnalysisDiagnostic(
@@ -3593,15 +3759,20 @@ function buildInstantiatedReferenceName(baseName, typeArguments, checker) {
3593
3759
  return renderedArguments.length === 0 ? baseName : `${baseName}__${renderedArguments.join("__")}`;
3594
3760
  }
3595
3761
  function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy, extensionRegistry, diagnostics) {
3596
- const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
3597
- if (typeNode === void 0) {
3762
+ const sourceTypeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
3763
+ if (sourceTypeNode === void 0) {
3598
3764
  return [];
3599
3765
  }
3600
- const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
3601
- if (!ts3.isTypeReferenceNode(resolvedTypeNode) || resolvedTypeNode.typeArguments === void 0) {
3766
+ const unwrapParentheses = (typeNode) => ts3.isParenthesizedTypeNode(typeNode) ? unwrapParentheses(typeNode.type) : typeNode;
3767
+ const directTypeNode = unwrapParentheses(sourceTypeNode);
3768
+ const referenceTypeNode = ts3.isTypeReferenceNode(directTypeNode) ? directTypeNode : (() => {
3769
+ const resolvedTypeNode = resolveAliasedTypeNode(directTypeNode, checker);
3770
+ return ts3.isTypeReferenceNode(resolvedTypeNode) ? resolvedTypeNode : null;
3771
+ })();
3772
+ if (referenceTypeNode?.typeArguments === void 0) {
3602
3773
  return [];
3603
3774
  }
3604
- return resolvedTypeNode.typeArguments.map((argumentNode) => {
3775
+ return referenceTypeNode.typeArguments.map((argumentNode) => {
3605
3776
  const argumentType = checker.getTypeFromTypeNode(argumentNode);
3606
3777
  return {
3607
3778
  tsType: argumentType,
@@ -3689,12 +3860,20 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
3689
3860
  annotations.push(defaultAnnotation);
3690
3861
  }
3691
3862
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
3692
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
3863
+ const metadata = resolveNodeMetadata(
3864
+ metadataPolicy,
3865
+ "field",
3866
+ name,
3867
+ prop,
3693
3868
  checker,
3694
- declaration: prop,
3695
- subjectType: tsType,
3696
- hostType
3697
- });
3869
+ extensionRegistry,
3870
+ {
3871
+ checker,
3872
+ declaration: prop,
3873
+ subjectType: tsType,
3874
+ hostType
3875
+ }
3876
+ );
3698
3877
  return {
3699
3878
  kind: "field",
3700
3879
  name,
@@ -3707,10 +3886,10 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
3707
3886
  };
3708
3887
  }
3709
3888
  function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
3710
- if (!ts3.isIdentifier(prop.name)) {
3889
+ const name = getAnalyzableObjectLikePropertyName(prop.name);
3890
+ if (name === null) {
3711
3891
  return null;
3712
3892
  }
3713
- const name = prop.name.text;
3714
3893
  const tsType = checker.getTypeAtLocation(prop);
3715
3894
  const optional = prop.questionToken !== void 0;
3716
3895
  const provenance = provenanceForNode(prop, file);
@@ -3741,12 +3920,20 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
3741
3920
  let annotations = [];
3742
3921
  annotations.push(...docResult.annotations);
3743
3922
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
3744
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
3923
+ const metadata = resolveNodeMetadata(
3924
+ metadataPolicy,
3925
+ "field",
3926
+ name,
3927
+ prop,
3745
3928
  checker,
3746
- declaration: prop,
3747
- subjectType: tsType,
3748
- hostType
3749
- });
3929
+ extensionRegistry,
3930
+ {
3931
+ checker,
3932
+ declaration: prop,
3933
+ subjectType: tsType,
3934
+ hostType
3935
+ }
3936
+ );
3750
3937
  return {
3751
3938
  kind: "field",
3752
3939
  name,
@@ -3758,6 +3945,31 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
3758
3945
  provenance
3759
3946
  };
3760
3947
  }
3948
+ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
3949
+ const seen = /* @__PURE__ */ new Set();
3950
+ const duplicates = /* @__PURE__ */ new Set();
3951
+ for (const member of members) {
3952
+ if (!ts3.isPropertySignature(member)) {
3953
+ continue;
3954
+ }
3955
+ const name = getAnalyzableObjectLikePropertyName(member.name);
3956
+ if (name === null) {
3957
+ continue;
3958
+ }
3959
+ if (seen.has(name)) {
3960
+ duplicates.add(name);
3961
+ continue;
3962
+ }
3963
+ seen.add(name);
3964
+ }
3965
+ return [...duplicates].sort();
3966
+ }
3967
+ function getAnalyzableObjectLikePropertyName(name) {
3968
+ if (!ts3.isIdentifier(name)) {
3969
+ return null;
3970
+ }
3971
+ return name.text;
3972
+ }
3761
3973
  function applyEnumMemberDisplayNames(type, annotations) {
3762
3974
  if (!annotations.some(
3763
3975
  (annotation) => annotation.annotationKind === "displayName" && annotation.value.trim().startsWith(":")
@@ -3875,7 +4087,7 @@ function getTypeNodeRegistrationName(typeNode) {
3875
4087
  }
3876
4088
  return null;
3877
4089
  }
3878
- function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4090
+ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3879
4091
  const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
3880
4092
  if (customType) {
3881
4093
  return customType;
@@ -3950,6 +4162,23 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
3950
4162
  diagnostics
3951
4163
  );
3952
4164
  }
4165
+ if (isIntersectionType(type)) {
4166
+ const sourceTypeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
4167
+ const resolvedSourceTypeNode = sourceTypeNode === void 0 ? void 0 : resolveAliasedTypeNode(sourceTypeNode, checker);
4168
+ if (resolvedSourceTypeNode !== void 0 && getObjectLikeTypeAliasMembers(resolvedSourceTypeNode) !== null) {
4169
+ return resolveObjectType(
4170
+ type,
4171
+ checker,
4172
+ file,
4173
+ typeRegistry,
4174
+ visiting,
4175
+ sourceNode,
4176
+ metadataPolicy,
4177
+ extensionRegistry,
4178
+ diagnostics
4179
+ );
4180
+ }
4181
+ }
3953
4182
  if (isObjectType(type)) {
3954
4183
  return resolveObjectType(
3955
4184
  type,
@@ -3965,7 +4194,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
3965
4194
  }
3966
4195
  return { kind: "primitive", primitiveKind: "string" };
3967
4196
  }
3968
- function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4197
+ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3969
4198
  if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
3970
4199
  return null;
3971
4200
  }
@@ -3985,11 +4214,19 @@ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiti
3985
4214
  file,
3986
4215
  makeParseOptions(extensionRegistry)
3987
4216
  );
3988
- const metadata = resolveNodeMetadata(metadataPolicy, "type", aliasName, aliasDecl, {
4217
+ const metadata = resolveNodeMetadata(
4218
+ metadataPolicy,
4219
+ "type",
4220
+ aliasName,
4221
+ aliasDecl,
3989
4222
  checker,
3990
- declaration: aliasDecl,
3991
- subjectType: aliasType
3992
- });
4223
+ extensionRegistry,
4224
+ {
4225
+ checker,
4226
+ declaration: aliasDecl,
4227
+ subjectType: aliasType
4228
+ }
4229
+ );
3993
4230
  typeRegistry[aliasName] = {
3994
4231
  name: aliasName,
3995
4232
  ...metadata !== void 0 && { metadata },
@@ -4028,7 +4265,7 @@ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
4028
4265
  const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
4029
4266
  return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
4030
4267
  }
4031
- function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4268
+ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4032
4269
  const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
4033
4270
  if (nestedAliasDecl !== void 0) {
4034
4271
  return resolveAliasedPrimitiveTarget(
@@ -4054,7 +4291,7 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
4054
4291
  diagnostics
4055
4292
  );
4056
4293
  }
4057
- function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4294
+ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4058
4295
  const typeName = getNamedTypeName(type);
4059
4296
  const namedDecl = getNamedTypeDeclaration(type);
4060
4297
  if (typeName && typeName in typeRegistry) {
@@ -4089,11 +4326,19 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
4089
4326
  return result;
4090
4327
  }
4091
4328
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
4092
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", typeName, namedDecl, {
4329
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
4330
+ metadataPolicy,
4331
+ "type",
4332
+ typeName,
4333
+ namedDecl,
4093
4334
  checker,
4094
- declaration: namedDecl,
4095
- subjectType: type
4096
- }) : void 0;
4335
+ extensionRegistry,
4336
+ {
4337
+ checker,
4338
+ declaration: namedDecl,
4339
+ subjectType: type
4340
+ }
4341
+ ) : void 0;
4097
4342
  typeRegistry[typeName] = {
4098
4343
  name: typeName,
4099
4344
  ...metadata !== void 0 && { metadata },
@@ -4178,7 +4423,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
4178
4423
  }
4179
4424
  return registerNamed({ kind: "union", members });
4180
4425
  }
4181
- function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4426
+ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4182
4427
  const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
4183
4428
  const elementType = typeArgs?.[0];
4184
4429
  const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
@@ -4195,7 +4440,7 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNod
4195
4440
  ) : { kind: "primitive", primitiveKind: "string" };
4196
4441
  return { kind: "array", items };
4197
4442
  }
4198
- function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4443
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4199
4444
  if (type.getProperties().length > 0) {
4200
4445
  return null;
4201
4446
  }
@@ -4256,7 +4501,7 @@ function shouldEmitResolvedObjectProperty(property, declaration) {
4256
4501
  }
4257
4502
  return true;
4258
4503
  }
4259
- function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4504
+ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4260
4505
  const collectedDiagnostics = diagnostics ?? [];
4261
4506
  const typeName = getNamedTypeName(type);
4262
4507
  const namedTypeName = typeName ?? void 0;
@@ -4313,7 +4558,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4313
4558
  };
4314
4559
  }
4315
4560
  }
4316
- const recordNode = tryResolveRecordType(
4561
+ const recordNode = isObjectType(type) ? tryResolveRecordType(
4317
4562
  type,
4318
4563
  checker,
4319
4564
  file,
@@ -4322,7 +4567,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4322
4567
  metadataPolicy,
4323
4568
  extensionRegistry,
4324
4569
  collectedDiagnostics
4325
- );
4570
+ ) : null;
4326
4571
  if (recordNode) {
4327
4572
  visiting.delete(type);
4328
4573
  if (registryTypeName !== void 0 && shouldRegisterNamedType) {
@@ -4332,11 +4577,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4332
4577
  return recordNode;
4333
4578
  }
4334
4579
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
4335
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
4580
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
4581
+ metadataPolicy,
4582
+ "type",
4583
+ registryTypeName,
4584
+ namedDecl,
4336
4585
  checker,
4337
- declaration: namedDecl,
4338
- subjectType: type
4339
- }) : void 0;
4586
+ extensionRegistry,
4587
+ {
4588
+ checker,
4589
+ declaration: namedDecl,
4590
+ subjectType: type
4591
+ }
4592
+ ) : void 0;
4340
4593
  typeRegistry[registryTypeName] = {
4341
4594
  name: registryTypeName,
4342
4595
  ...metadata !== void 0 && { metadata },
@@ -4432,11 +4685,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4432
4685
  };
4433
4686
  if (registryTypeName !== void 0 && shouldRegisterNamedType) {
4434
4687
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
4435
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
4688
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
4689
+ metadataPolicy,
4690
+ "type",
4691
+ registryTypeName,
4692
+ namedDecl,
4436
4693
  checker,
4437
- declaration: namedDecl,
4438
- subjectType: type
4439
- }) : void 0;
4694
+ extensionRegistry,
4695
+ {
4696
+ checker,
4697
+ declaration: namedDecl,
4698
+ subjectType: type
4699
+ }
4700
+ ) : void 0;
4440
4701
  typeRegistry[registryTypeName] = {
4441
4702
  name: registryTypeName,
4442
4703
  ...metadata !== void 0 && { metadata },
@@ -4503,9 +4764,10 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
4503
4764
  );
4504
4765
  }
4505
4766
  const typeAliasDecl = declarations.find(ts3.isTypeAliasDeclaration);
4506
- if (typeAliasDecl && ts3.isTypeLiteralNode(typeAliasDecl.type)) {
4767
+ const typeAliasMembers = typeAliasDecl === void 0 ? null : getObjectLikeTypeAliasMembers(typeAliasDecl.type);
4768
+ if (typeAliasDecl && typeAliasMembers !== null) {
4507
4769
  return buildFieldNodeInfoMap(
4508
- typeAliasDecl.type.members,
4770
+ typeAliasMembers,
4509
4771
  checker,
4510
4772
  file,
4511
4773
  typeRegistry,
@@ -4713,12 +4975,12 @@ function detectFormSpecReference(typeNode) {
4713
4975
  }
4714
4976
  return null;
4715
4977
  }
4716
- var ts3, import_internal2, RESOLVING_TYPE_PLACEHOLDER, MAX_ALIAS_CHAIN_DEPTH;
4978
+ var ts3, import_internal3, RESOLVING_TYPE_PLACEHOLDER, MAX_ALIAS_CHAIN_DEPTH;
4717
4979
  var init_class_analyzer = __esm({
4718
4980
  "src/analyzer/class-analyzer.ts"() {
4719
4981
  "use strict";
4720
4982
  ts3 = __toESM(require("typescript"), 1);
4721
- import_internal2 = require("@formspec/analysis/internal");
4983
+ import_internal3 = require("@formspec/analysis/internal");
4722
4984
  init_jsdoc_constraints();
4723
4985
  init_tsdoc_parser();
4724
4986
  init_metadata();
@@ -4812,17 +5074,18 @@ function findInterfaceByName(sourceFile, interfaceName) {
4812
5074
  function findTypeAliasByName(sourceFile, aliasName) {
4813
5075
  return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
4814
5076
  }
4815
- function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy) {
5077
+ function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
4816
5078
  const ctx = createProgramContext(filePath);
4817
5079
  return analyzeNamedTypeToIRFromProgramContext(
4818
5080
  ctx,
4819
5081
  filePath,
4820
5082
  typeName,
4821
5083
  extensionRegistry,
4822
- metadataPolicy
5084
+ metadataPolicy,
5085
+ discriminatorOptions
4823
5086
  );
4824
5087
  }
4825
- function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy) {
5088
+ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
4826
5089
  const analysisFilePath = path.resolve(filePath);
4827
5090
  const classDecl = findClassByName(ctx.sourceFile, typeName);
4828
5091
  if (classDecl !== null) {
@@ -4831,7 +5094,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
4831
5094
  ctx.checker,
4832
5095
  analysisFilePath,
4833
5096
  extensionRegistry,
4834
- metadataPolicy
5097
+ metadataPolicy,
5098
+ discriminatorOptions
4835
5099
  );
4836
5100
  }
4837
5101
  const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
@@ -4841,7 +5105,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
4841
5105
  ctx.checker,
4842
5106
  analysisFilePath,
4843
5107
  extensionRegistry,
4844
- metadataPolicy
5108
+ metadataPolicy,
5109
+ discriminatorOptions
4845
5110
  );
4846
5111
  }
4847
5112
  const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
@@ -4851,7 +5116,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
4851
5116
  ctx.checker,
4852
5117
  analysisFilePath,
4853
5118
  extensionRegistry,
4854
- metadataPolicy
5119
+ metadataPolicy,
5120
+ discriminatorOptions
4855
5121
  );
4856
5122
  if (result.ok) {
4857
5123
  return result.analysis;
@@ -4874,7 +5140,7 @@ var init_program = __esm({
4874
5140
 
4875
5141
  // src/validate/constraint-validator.ts
4876
5142
  function validateFieldNode(ctx, field) {
4877
- const analysis = (0, import_internal3.analyzeConstraintTargets)(
5143
+ const analysis = (0, import_internal4.analyzeConstraintTargets)(
4878
5144
  field.name,
4879
5145
  field.type,
4880
5146
  field.constraints,
@@ -4892,7 +5158,7 @@ function validateFieldNode(ctx, field) {
4892
5158
  }
4893
5159
  function validateObjectProperty(ctx, parentName, property) {
4894
5160
  const qualifiedName = `${parentName}.${property.name}`;
4895
- const analysis = (0, import_internal3.analyzeConstraintTargets)(
5161
+ const analysis = (0, import_internal4.analyzeConstraintTargets)(
4896
5162
  qualifiedName,
4897
5163
  property.type,
4898
5164
  property.constraints,
@@ -4943,11 +5209,11 @@ function validateIR(ir, options) {
4943
5209
  valid: ctx.diagnostics.every((diagnostic) => diagnostic.severity !== "error")
4944
5210
  };
4945
5211
  }
4946
- var import_internal3;
5212
+ var import_internal4;
4947
5213
  var init_constraint_validator = __esm({
4948
5214
  "src/validate/constraint-validator.ts"() {
4949
5215
  "use strict";
4950
- import_internal3 = require("@formspec/analysis/internal");
5216
+ import_internal4 = require("@formspec/analysis/internal");
4951
5217
  }
4952
5218
  });
4953
5219
 
@@ -5009,7 +5275,8 @@ function generateSchemasFromClass(options) {
5009
5275
  ctx.checker,
5010
5276
  options.filePath,
5011
5277
  options.extensionRegistry,
5012
- options.metadata
5278
+ options.metadata,
5279
+ options.discriminator
5013
5280
  );
5014
5281
  return generateClassSchemas(
5015
5282
  analysis,
@@ -5035,7 +5302,8 @@ function generateSchemasFromProgram(options) {
5035
5302
  options.filePath,
5036
5303
  options.typeName,
5037
5304
  options.extensionRegistry,
5038
- options.metadata
5305
+ options.metadata,
5306
+ options.discriminator
5039
5307
  );
5040
5308
  return generateClassSchemas(
5041
5309
  analysis,
@@ -5213,7 +5481,7 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
5213
5481
  {
5214
5482
  kind: "form-ir",
5215
5483
  name: root.name,
5216
- irVersion: import_internals5.IR_VERSION,
5484
+ irVersion: import_internals6.IR_VERSION,
5217
5485
  elements: [syntheticField],
5218
5486
  ...root.metadata !== void 0 && { metadata: root.metadata },
5219
5487
  ...root.annotations !== void 0 && root.annotations.length > 0 && { rootAnnotations: root.annotations },
@@ -5272,7 +5540,7 @@ function generateSchemasFromResolvedType(options, skipNamedDeclaration = false,
5272
5540
  typeRegistry,
5273
5541
  /* @__PURE__ */ new Set(),
5274
5542
  options.sourceNode,
5275
- normalizeMetadataPolicy(options.metadata),
5543
+ createAnalyzerMetadataPolicy(options.metadata, options.discriminator),
5276
5544
  options.extensionRegistry,
5277
5545
  diagnostics
5278
5546
  );
@@ -5321,7 +5589,8 @@ function generateSchemasFromDeclaration(options) {
5321
5589
  options.context.checker,
5322
5590
  filePath,
5323
5591
  options.extensionRegistry,
5324
- options.metadata
5592
+ options.metadata,
5593
+ options.discriminator
5325
5594
  ),
5326
5595
  filePath,
5327
5596
  options
@@ -5334,7 +5603,8 @@ function generateSchemasFromDeclaration(options) {
5334
5603
  options.context.checker,
5335
5604
  filePath,
5336
5605
  options.extensionRegistry,
5337
- options.metadata
5606
+ options.metadata,
5607
+ options.discriminator
5338
5608
  ),
5339
5609
  filePath,
5340
5610
  options
@@ -5346,7 +5616,8 @@ function generateSchemasFromDeclaration(options) {
5346
5616
  options.context.checker,
5347
5617
  filePath,
5348
5618
  options.extensionRegistry,
5349
- options.metadata
5619
+ options.metadata,
5620
+ options.discriminator
5350
5621
  );
5351
5622
  if (analyzedAlias.ok) {
5352
5623
  return generateSchemasFromAnalysis(analyzedAlias.analysis, filePath, options);
@@ -5404,7 +5675,7 @@ function generateSchemasFromReturnType(options) {
5404
5675
  name: fallbackName
5405
5676
  });
5406
5677
  }
5407
- var ts7, import_internals5;
5678
+ var ts7, import_internals6;
5408
5679
  var init_discovered_schema = __esm({
5409
5680
  "src/generators/discovered-schema.ts"() {
5410
5681
  "use strict";
@@ -5412,7 +5683,7 @@ var init_discovered_schema = __esm({
5412
5683
  init_class_analyzer();
5413
5684
  init_class_schema();
5414
5685
  init_ir_generator();
5415
- import_internals5 = require("@formspec/core/internals");
5686
+ import_internals6 = require("@formspec/core/internals");
5416
5687
  init_metadata();
5417
5688
  }
5418
5689
  });
@@ -5424,7 +5695,8 @@ function buildMixedAuthoringSchemas(options) {
5424
5695
  filePath,
5425
5696
  typeName,
5426
5697
  schemaOptions.extensionRegistry,
5427
- schemaOptions.metadata
5698
+ schemaOptions.metadata,
5699
+ schemaOptions.discriminator
5428
5700
  );
5429
5701
  const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays, schemaOptions.metadata);
5430
5702
  const ir = canonicalizeTSDoc(