@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/index.js CHANGED
@@ -1765,13 +1765,35 @@ import * as fs from "fs";
1765
1765
  import * as path2 from "path";
1766
1766
 
1767
1767
  // src/extensions/registry.ts
1768
+ import {
1769
+ BUILTIN_CONSTRAINT_DEFINITIONS,
1770
+ normalizeConstraintTagName
1771
+ } from "@formspec/core/internals";
1772
+ import {
1773
+ getTagDefinition,
1774
+ normalizeFormSpecTagName
1775
+ } from "@formspec/analysis/internal";
1776
+ var BUILTIN_METADATA_TAGS = /* @__PURE__ */ new Set(["apiName", "displayName"]);
1777
+ function buildConstraintTagSources(extensions) {
1778
+ return extensions.map((extension) => ({
1779
+ extensionId: extension.extensionId,
1780
+ ...extension.constraintTags !== void 0 ? {
1781
+ constraintTags: extension.constraintTags.map((tag) => ({
1782
+ tagName: normalizeFormSpecTagName(tag.tagName)
1783
+ }))
1784
+ } : {}
1785
+ }));
1786
+ }
1768
1787
  function createExtensionRegistry(extensions) {
1788
+ const reservedTagSources = buildConstraintTagSources(extensions);
1769
1789
  const typeMap = /* @__PURE__ */ new Map();
1770
1790
  const typeNameMap = /* @__PURE__ */ new Map();
1771
1791
  const constraintMap = /* @__PURE__ */ new Map();
1772
1792
  const constraintTagMap = /* @__PURE__ */ new Map();
1773
1793
  const builtinBroadeningMap = /* @__PURE__ */ new Map();
1774
1794
  const annotationMap = /* @__PURE__ */ new Map();
1795
+ const metadataSlotMap = /* @__PURE__ */ new Map();
1796
+ const metadataTagMap = /* @__PURE__ */ new Map();
1775
1797
  for (const ext of extensions) {
1776
1798
  if (ext.types !== void 0) {
1777
1799
  for (const type of ext.types) {
@@ -1814,10 +1836,11 @@ function createExtensionRegistry(extensions) {
1814
1836
  }
1815
1837
  if (ext.constraintTags !== void 0) {
1816
1838
  for (const tag of ext.constraintTags) {
1817
- if (constraintTagMap.has(tag.tagName)) {
1818
- throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
1839
+ const canonicalTagName = normalizeFormSpecTagName(tag.tagName);
1840
+ if (constraintTagMap.has(canonicalTagName)) {
1841
+ throw new Error(`Duplicate custom constraint tag: "@${canonicalTagName}"`);
1819
1842
  }
1820
- constraintTagMap.set(tag.tagName, {
1843
+ constraintTagMap.set(canonicalTagName, {
1821
1844
  extensionId: ext.extensionId,
1822
1845
  registration: tag
1823
1846
  });
@@ -1832,13 +1855,54 @@ function createExtensionRegistry(extensions) {
1832
1855
  annotationMap.set(qualifiedId, annotation);
1833
1856
  }
1834
1857
  }
1858
+ if (ext.metadataSlots !== void 0) {
1859
+ for (const slot of ext.metadataSlots) {
1860
+ if (metadataSlotMap.has(slot.slotId)) {
1861
+ throw new Error(`Duplicate metadata slot ID: "${slot.slotId}"`);
1862
+ }
1863
+ metadataSlotMap.set(slot.slotId, true);
1864
+ const canonicalTagName = normalizeFormSpecTagName(slot.tagName);
1865
+ if (slot.allowBare === false && (slot.qualifiers?.length ?? 0) === 0) {
1866
+ throw new Error(
1867
+ `Metadata tag "@${canonicalTagName}" must allow bare usage or declare at least one qualifier.`
1868
+ );
1869
+ }
1870
+ if (metadataTagMap.has(canonicalTagName)) {
1871
+ throw new Error(`Duplicate metadata tag: "@${canonicalTagName}"`);
1872
+ }
1873
+ if (BUILTIN_METADATA_TAGS.has(canonicalTagName)) {
1874
+ throw new Error(
1875
+ `Metadata tag "@${canonicalTagName}" conflicts with built-in metadata tags.`
1876
+ );
1877
+ }
1878
+ if (constraintTagMap.has(canonicalTagName)) {
1879
+ throw new Error(
1880
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${canonicalTagName}".`
1881
+ );
1882
+ }
1883
+ if (Object.hasOwn(BUILTIN_CONSTRAINT_DEFINITIONS, normalizeConstraintTagName(canonicalTagName))) {
1884
+ throw new Error(
1885
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${normalizeConstraintTagName(canonicalTagName)}".`
1886
+ );
1887
+ }
1888
+ const existingTag = getTagDefinition(canonicalTagName, reservedTagSources);
1889
+ if (existingTag !== null) {
1890
+ throw BUILTIN_METADATA_TAGS.has(existingTag.canonicalName) ? new Error(
1891
+ `Metadata tag "@${canonicalTagName}" conflicts with built-in metadata tags.`
1892
+ ) : new Error(
1893
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${existingTag.canonicalName}".`
1894
+ );
1895
+ }
1896
+ metadataTagMap.set(canonicalTagName, true);
1897
+ }
1898
+ }
1835
1899
  }
1836
1900
  return {
1837
1901
  extensions,
1838
1902
  findType: (typeId) => typeMap.get(typeId),
1839
1903
  findTypeByName: (typeName) => typeNameMap.get(typeName),
1840
1904
  findConstraint: (constraintId) => constraintMap.get(constraintId),
1841
- findConstraintTag: (tagName) => constraintTagMap.get(tagName),
1905
+ findConstraintTag: (tagName) => constraintTagMap.get(normalizeFormSpecTagName(tagName)),
1842
1906
  findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
1843
1907
  findAnnotation: (annotationId) => annotationMap.get(annotationId)
1844
1908
  };
@@ -1916,6 +1980,7 @@ import * as path from "path";
1916
1980
  // src/analyzer/class-analyzer.ts
1917
1981
  import * as ts3 from "typescript";
1918
1982
  import {
1983
+ analyzeMetadataForNodeWithChecker,
1919
1984
  parseCommentBlock as parseCommentBlock2
1920
1985
  } from "@formspec/analysis/internal";
1921
1986
 
@@ -1927,8 +1992,9 @@ import * as ts from "typescript";
1927
1992
  import {
1928
1993
  checkSyntheticTagApplication,
1929
1994
  extractPathTarget as extractSharedPathTarget,
1930
- getTagDefinition,
1995
+ getTagDefinition as getTagDefinition2,
1931
1996
  hasTypeSemanticCapability,
1997
+ normalizeFormSpecTagName as normalizeFormSpecTagName2,
1932
1998
  parseConstraintTagValue,
1933
1999
  parseDefaultValueTagValue,
1934
2000
  resolveDeclarationPlacement,
@@ -1948,15 +2014,15 @@ import {
1948
2014
  TextRange
1949
2015
  } from "@microsoft/tsdoc";
1950
2016
  import {
1951
- BUILTIN_CONSTRAINT_DEFINITIONS,
1952
- normalizeConstraintTagName,
2017
+ BUILTIN_CONSTRAINT_DEFINITIONS as BUILTIN_CONSTRAINT_DEFINITIONS2,
2018
+ normalizeConstraintTagName as normalizeConstraintTagName2,
1953
2019
  isBuiltinConstraintName
1954
2020
  } from "@formspec/core/internals";
1955
2021
  import "@formspec/core/internals";
1956
2022
  var TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions", "defaultValue"]);
1957
2023
  function createFormSpecTSDocConfig(extensionTagNames = []) {
1958
2024
  const config = new TSDocConfiguration();
1959
- for (const tagName of Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS)) {
2025
+ for (const tagName of Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS2)) {
1960
2026
  config.addTagDefinition(
1961
2027
  new TSDocTagDefinition({
1962
2028
  tagName: "@" + tagName,
@@ -1965,7 +2031,7 @@ function createFormSpecTSDocConfig(extensionTagNames = []) {
1965
2031
  })
1966
2032
  );
1967
2033
  }
1968
- for (const tagName of ["displayName", "format", "placeholder"]) {
2034
+ for (const tagName of ["apiName", "displayName", "format", "placeholder"]) {
1969
2035
  config.addTagDefinition(
1970
2036
  new TSDocTagDefinition({
1971
2037
  tagName: "@" + tagName,
@@ -1999,6 +2065,16 @@ function sharedTagValueOptions(options) {
1999
2065
  };
2000
2066
  }
2001
2067
  var SYNTHETIC_TYPE_FORMAT_FLAGS = ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope;
2068
+ function getExtensionTypeNames(registry) {
2069
+ if (registry === void 0) {
2070
+ return /* @__PURE__ */ new Set();
2071
+ }
2072
+ return new Set(
2073
+ registry.extensions.flatMap(
2074
+ (ext) => (ext.types ?? []).flatMap((t) => t.tsTypeNames ?? [t.typeName])
2075
+ )
2076
+ );
2077
+ }
2002
2078
  function collectImportedNames(sourceFile) {
2003
2079
  const importedNames = /* @__PURE__ */ new Set();
2004
2080
  for (const statement of sourceFile.statements) {
@@ -2038,6 +2114,9 @@ function isNonReferenceIdentifier(node) {
2038
2114
  return false;
2039
2115
  }
2040
2116
  function statementReferencesImportedName(statement, importedNames) {
2117
+ if (importedNames.size === 0) {
2118
+ return false;
2119
+ }
2041
2120
  let referencesImportedName = false;
2042
2121
  const visit = (node) => {
2043
2122
  if (referencesImportedName) {
@@ -2052,14 +2131,17 @@ function statementReferencesImportedName(statement, importedNames) {
2052
2131
  visit(statement);
2053
2132
  return referencesImportedName;
2054
2133
  }
2055
- function buildSupportingDeclarations(sourceFile) {
2134
+ function buildSupportingDeclarations(sourceFile, extensionTypeNames) {
2056
2135
  const importedNames = collectImportedNames(sourceFile);
2136
+ const importedNamesToSkip = new Set(
2137
+ [...importedNames].filter((name) => !extensionTypeNames.has(name))
2138
+ );
2057
2139
  return sourceFile.statements.filter((statement) => {
2058
2140
  if (ts.isImportDeclaration(statement)) return false;
2059
2141
  if (ts.isImportEqualsDeclaration(statement)) return false;
2060
2142
  if (ts.isExportDeclaration(statement) && statement.moduleSpecifier !== void 0)
2061
2143
  return false;
2062
- if (importedNames.size > 0 && statementReferencesImportedName(statement, importedNames)) {
2144
+ if (statementReferencesImportedName(statement, importedNamesToSkip)) {
2063
2145
  return false;
2064
2146
  }
2065
2147
  return true;
@@ -2212,7 +2294,7 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
2212
2294
  if (placement === null) {
2213
2295
  return [];
2214
2296
  }
2215
- const definition = getTagDefinition(tagName, options?.extensionRegistry?.extensions);
2297
+ const definition = getTagDefinition2(tagName, options?.extensionRegistry?.extensions);
2216
2298
  if (definition === null) {
2217
2299
  return [];
2218
2300
  }
@@ -2316,6 +2398,14 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
2316
2398
  extensionId: extension.extensionId,
2317
2399
  ...extension.constraintTags !== void 0 ? {
2318
2400
  constraintTags: extension.constraintTags.map((tag) => ({ tagName: tag.tagName }))
2401
+ } : {},
2402
+ ...extension.metadataSlots !== void 0 ? {
2403
+ metadataSlots: extension.metadataSlots
2404
+ } : {},
2405
+ ...extension.types !== void 0 ? {
2406
+ customTypes: extension.types.map((t) => ({
2407
+ tsTypeNames: t.tsTypeNames ?? [t.typeName]
2408
+ }))
2319
2409
  } : {}
2320
2410
  }))
2321
2411
  } : {}
@@ -2337,7 +2427,10 @@ var parseResultCache = /* @__PURE__ */ new Map();
2337
2427
  function getParser(options) {
2338
2428
  const extensionTagNames = [
2339
2429
  ...options?.extensionRegistry?.extensions.flatMap(
2340
- (extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
2430
+ (extension) => (extension.constraintTags ?? []).map((tag) => normalizeFormSpecTagName2(tag.tagName))
2431
+ ) ?? [],
2432
+ ...options?.extensionRegistry?.extensions.flatMap(
2433
+ (extension) => (extension.metadataSlots ?? []).map((slot) => normalizeFormSpecTagName2(slot.tagName))
2341
2434
  ) ?? []
2342
2435
  ].sort();
2343
2436
  const cacheKey = extensionTagNames.join("|");
@@ -2357,7 +2450,16 @@ function getExtensionRegistryCacheKey(registry) {
2357
2450
  (extension) => JSON.stringify({
2358
2451
  extensionId: extension.extensionId,
2359
2452
  typeNames: extension.types?.map((type) => type.typeName) ?? [],
2360
- constraintTags: extension.constraintTags?.map((tag) => tag.tagName) ?? []
2453
+ constraintTags: extension.constraintTags?.map((tag) => normalizeFormSpecTagName2(tag.tagName)) ?? [],
2454
+ metadataSlots: extension.metadataSlots?.map((slot) => ({
2455
+ tagName: normalizeFormSpecTagName2(slot.tagName),
2456
+ declarationKinds: [...slot.declarationKinds].sort(),
2457
+ allowBare: slot.allowBare !== false,
2458
+ qualifiers: (slot.qualifiers ?? []).map((qualifier) => ({
2459
+ qualifier: qualifier.qualifier,
2460
+ ...qualifier.sourceQualifier !== void 0 ? { sourceQualifier: qualifier.sourceQualifier } : {}
2461
+ })).sort((left, right) => left.qualifier.localeCompare(right.qualifier))
2462
+ })) ?? []
2361
2463
  })
2362
2464
  ).join("|");
2363
2465
  }
@@ -2392,7 +2494,8 @@ function parseTSDocTags(node, file = "", options) {
2392
2494
  const rawTextTags = [];
2393
2495
  const sourceFile = node.getSourceFile();
2394
2496
  const sourceText = sourceFile.getFullText();
2395
- const supportingDeclarations = buildSupportingDeclarations(sourceFile);
2497
+ const extensionTypeNames = getExtensionTypeNames(options?.extensionRegistry);
2498
+ const supportingDeclarations = buildSupportingDeclarations(sourceFile, extensionTypeNames);
2396
2499
  const commentRanges = ts.getLeadingCommentRanges(sourceText, node.getFullStart());
2397
2500
  const rawTextFallbacks = collectRawTextFallbacks(node, file);
2398
2501
  if (commentRanges) {
@@ -2430,7 +2533,7 @@ function parseTSDocTags(node, file = "", options) {
2430
2533
  }
2431
2534
  }
2432
2535
  for (const block of docComment.customBlocks) {
2433
- const tagName = normalizeConstraintTagName(block.blockTag.tagName.substring(1));
2536
+ const tagName = normalizeConstraintTagName2(block.blockTag.tagName.substring(1));
2434
2537
  const parsedTag = nextParsedTag(tagName);
2435
2538
  if (tagName === "displayName" || tagName === "format" || tagName === "placeholder") {
2436
2539
  const text2 = getBestBlockPayloadText(parsedTag, commentText, range.pos, block);
@@ -2462,7 +2565,7 @@ function parseTSDocTags(node, file = "", options) {
2462
2565
  }
2463
2566
  if (TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
2464
2567
  const text = getBestBlockPayloadText(parsedTag, commentText, range.pos, block);
2465
- const expectedType = isBuiltinConstraintName(tagName) ? BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
2568
+ const expectedType = isBuiltinConstraintName(tagName) ? BUILTIN_CONSTRAINT_DEFINITIONS2[tagName] : void 0;
2466
2569
  if (text === "" && expectedType !== "boolean") continue;
2467
2570
  const provenance = parsedTag !== null ? provenanceForParsedTag(parsedTag, sourceFile, file) : provenanceForComment(range, sourceFile, file, tagName);
2468
2571
  const compilerDiagnostics = buildCompilerBackedConstraintDiagnostics(
@@ -2693,7 +2796,7 @@ function getBestBlockPayloadText(tag, commentText, commentOffset, block) {
2693
2796
  function collectRawTextFallbacks(node, file) {
2694
2797
  const fallbacks = /* @__PURE__ */ new Map();
2695
2798
  for (const tag of ts.getJSDocTags(node)) {
2696
- const tagName = normalizeConstraintTagName(tag.tagName.text);
2799
+ const tagName = normalizeConstraintTagName2(tag.tagName.text);
2697
2800
  if (!TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
2698
2801
  const commentText = getTagCommentText(tag)?.trim() ?? "";
2699
2802
  if (commentText === "") continue;
@@ -2800,6 +2903,9 @@ function extractDefaultValueAnnotation(initializer, file = "") {
2800
2903
  function isObjectType(type) {
2801
2904
  return !!(type.flags & ts3.TypeFlags.Object);
2802
2905
  }
2906
+ function isIntersectionType(type) {
2907
+ return !!(type.flags & ts3.TypeFlags.Intersection);
2908
+ }
2803
2909
  function isTypeReference(type) {
2804
2910
  return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
2805
2911
  }
@@ -2820,76 +2926,51 @@ function makeParseOptions(extensionRegistry, fieldType, checker, subjectType, ho
2820
2926
  ...hostType !== void 0 && { hostType }
2821
2927
  };
2822
2928
  }
2823
- function makeExplicitScalarMetadata(value) {
2824
- return value === void 0 || value === "" ? void 0 : { value, source: "explicit" };
2825
- }
2826
- function extractExplicitMetadata(node) {
2827
- let apiName;
2828
- let displayName;
2829
- let apiNamePlural;
2830
- let displayNamePlural;
2831
- for (const tag of getLeadingParsedTags(node)) {
2832
- const value = tag.argumentText.trim();
2833
- if (value === "") {
2834
- continue;
2835
- }
2836
- if (tag.normalizedTagName === "apiName") {
2837
- if (tag.target === null) {
2838
- apiName ??= value;
2839
- } else if (tag.target.kind === "variant") {
2840
- if (tag.target.rawText === "singular") {
2841
- apiName ??= value;
2842
- } else if (tag.target.rawText === "plural") {
2843
- apiNamePlural ??= value;
2844
- }
2845
- }
2846
- continue;
2847
- }
2848
- if (tag.normalizedTagName === "displayName") {
2849
- if (tag.target === null) {
2850
- displayName ??= value;
2851
- } else if (tag.target.kind === "variant") {
2852
- if (tag.target.rawText === "singular") {
2853
- displayName ??= value;
2854
- } else if (tag.target.rawText === "plural") {
2855
- displayNamePlural ??= value;
2856
- }
2857
- }
2858
- }
2859
- }
2860
- const resolvedApiName = makeExplicitScalarMetadata(apiName);
2861
- const resolvedDisplayName = makeExplicitScalarMetadata(displayName);
2862
- const resolvedApiNamePlural = makeExplicitScalarMetadata(apiNamePlural);
2863
- const resolvedDisplayNamePlural = makeExplicitScalarMetadata(displayNamePlural);
2864
- const metadata = {
2865
- ...resolvedApiName !== void 0 && { apiName: resolvedApiName },
2866
- ...resolvedDisplayName !== void 0 && { displayName: resolvedDisplayName },
2867
- ...resolvedApiNamePlural !== void 0 && { apiNamePlural: resolvedApiNamePlural },
2868
- ...resolvedDisplayNamePlural !== void 0 && {
2869
- displayNamePlural: resolvedDisplayNamePlural
2870
- }
2929
+ function createAnalyzerMetadataPolicy(input, discriminator) {
2930
+ return {
2931
+ raw: input,
2932
+ normalized: normalizeMetadataPolicy(input),
2933
+ discriminator
2871
2934
  };
2872
- return Object.keys(metadata).length === 0 ? void 0 : metadata;
2873
2935
  }
2874
- function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, buildContext) {
2875
- const explicit = extractExplicitMetadata(node);
2876
- return resolveMetadata(
2877
- {
2878
- ...explicit?.apiName !== void 0 && { apiName: explicit.apiName.value },
2879
- ...explicit?.displayName !== void 0 && { displayName: explicit.displayName.value },
2880
- ...explicit?.apiNamePlural !== void 0 && {
2881
- apiNamePlural: explicit.apiNamePlural.value
2882
- },
2883
- ...explicit?.displayNamePlural !== void 0 && {
2884
- displayNamePlural: explicit.displayNamePlural.value
2885
- }
2886
- },
2887
- getDeclarationMetadataPolicy(metadataPolicy, declarationKind),
2888
- makeMetadataContext("tsdoc", declarationKind, logicalName, buildContext)
2936
+ function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, checker, extensionRegistry, buildContext) {
2937
+ const analysis = analyzeMetadataForNodeWithChecker({
2938
+ checker,
2939
+ node,
2940
+ logicalName,
2941
+ metadata: metadataPolicy.raw,
2942
+ extensions: extensionRegistry?.extensions,
2943
+ ...buildContext !== void 0 && { buildContext }
2944
+ });
2945
+ const resolvedMetadata = analysis?.resolvedMetadata;
2946
+ const declarationPolicy = getDeclarationMetadataPolicy(
2947
+ metadataPolicy.normalized,
2948
+ declarationKind
2889
2949
  );
2950
+ if (resolvedMetadata?.apiName === void 0 && declarationPolicy.apiName.mode === "require-explicit") {
2951
+ throw new Error(
2952
+ `Metadata policy requires explicit apiName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
2953
+ );
2954
+ }
2955
+ if (resolvedMetadata?.displayName === void 0 && declarationPolicy.displayName.mode === "require-explicit") {
2956
+ throw new Error(
2957
+ `Metadata policy requires explicit displayName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
2958
+ );
2959
+ }
2960
+ if (resolvedMetadata?.apiNamePlural === void 0 && declarationPolicy.apiName.pluralization.mode === "require-explicit") {
2961
+ throw new Error(
2962
+ `Metadata policy requires explicit apiNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
2963
+ );
2964
+ }
2965
+ if (resolvedMetadata?.displayNamePlural === void 0 && declarationPolicy.displayName.pluralization.mode === "require-explicit") {
2966
+ throw new Error(
2967
+ `Metadata policy requires explicit displayNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
2968
+ );
2969
+ }
2970
+ return resolvedMetadata;
2890
2971
  }
2891
2972
  function analyzeDeclarationRootInfo(declaration, checker, file = "", extensionRegistry, metadataPolicy) {
2892
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
2973
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
2893
2974
  const declarationType = checker.getTypeAtLocation(declaration);
2894
2975
  const logicalName = ts3.isClassDeclaration(declaration) ? declaration.name?.text ?? "AnonymousClass" : declaration.name.text;
2895
2976
  const docResult = extractJSDocParseResult(
@@ -2897,20 +2978,31 @@ function analyzeDeclarationRootInfo(declaration, checker, file = "", extensionRe
2897
2978
  file,
2898
2979
  makeParseOptions(extensionRegistry, void 0, checker, declarationType, declarationType)
2899
2980
  );
2900
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", logicalName, declaration, {
2901
- checker,
2981
+ const metadata = resolveNodeMetadata(
2982
+ normalizedMetadataPolicy,
2983
+ "type",
2984
+ logicalName,
2902
2985
  declaration,
2903
- subjectType: declarationType,
2904
- hostType: declarationType
2905
- });
2986
+ checker,
2987
+ extensionRegistry,
2988
+ {
2989
+ checker,
2990
+ declaration,
2991
+ subjectType: declarationType,
2992
+ hostType: declarationType
2993
+ }
2994
+ );
2906
2995
  return {
2907
2996
  ...metadata !== void 0 && { metadata },
2908
2997
  annotations: docResult.annotations,
2909
2998
  diagnostics: docResult.diagnostics
2910
2999
  };
2911
3000
  }
2912
- function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy) {
2913
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3001
+ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
3002
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
3003
+ metadataPolicy,
3004
+ discriminatorOptions
3005
+ );
2914
3006
  const name = classDecl.name?.text ?? "AnonymousClass";
2915
3007
  const fields = [];
2916
3008
  const fieldLayouts = [];
@@ -2965,12 +3057,20 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
2965
3057
  diagnostics,
2966
3058
  normalizedMetadataPolicy
2967
3059
  );
2968
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, classDecl, {
3060
+ const metadata = resolveNodeMetadata(
3061
+ normalizedMetadataPolicy,
3062
+ "type",
3063
+ name,
3064
+ classDecl,
2969
3065
  checker,
2970
- declaration: classDecl,
2971
- subjectType: classType,
2972
- hostType: classType
2973
- });
3066
+ extensionRegistry,
3067
+ {
3068
+ checker,
3069
+ declaration: classDecl,
3070
+ subjectType: classType,
3071
+ hostType: classType
3072
+ }
3073
+ );
2974
3074
  return {
2975
3075
  name,
2976
3076
  ...metadata !== void 0 && { metadata },
@@ -2983,8 +3083,11 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
2983
3083
  staticMethods
2984
3084
  };
2985
3085
  }
2986
- function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy) {
2987
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3086
+ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
3087
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
3088
+ metadataPolicy,
3089
+ discriminatorOptions
3090
+ );
2988
3091
  const name = interfaceDecl.name.text;
2989
3092
  const fields = [];
2990
3093
  const typeRegistry = {};
@@ -3026,12 +3129,20 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
3026
3129
  normalizedMetadataPolicy
3027
3130
  );
3028
3131
  const fieldLayouts = specializedFields.map(() => ({}));
3029
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, interfaceDecl, {
3132
+ const metadata = resolveNodeMetadata(
3133
+ normalizedMetadataPolicy,
3134
+ "type",
3135
+ name,
3136
+ interfaceDecl,
3030
3137
  checker,
3031
- declaration: interfaceDecl,
3032
- subjectType: interfaceType,
3033
- hostType: interfaceType
3034
- });
3138
+ extensionRegistry,
3139
+ {
3140
+ checker,
3141
+ declaration: interfaceDecl,
3142
+ subjectType: interfaceType,
3143
+ hostType: interfaceType
3144
+ }
3145
+ );
3035
3146
  return {
3036
3147
  name,
3037
3148
  ...metadata !== void 0 && { metadata },
@@ -3044,19 +3155,31 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
3044
3155
  staticMethods: []
3045
3156
  };
3046
3157
  }
3047
- function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry, metadataPolicy) {
3048
- if (!ts3.isTypeLiteralNode(typeAlias.type)) {
3158
+ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
3159
+ const members = getObjectLikeTypeAliasMembers(typeAlias.type);
3160
+ if (members === null) {
3049
3161
  const sourceFile = typeAlias.getSourceFile();
3050
3162
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
3051
3163
  const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
3052
3164
  return {
3053
3165
  ok: false,
3054
- error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
3166
+ error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
3055
3167
  };
3056
3168
  }
3057
- const typeLiteral = typeAlias.type;
3058
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3169
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
3170
+ metadataPolicy,
3171
+ discriminatorOptions
3172
+ );
3059
3173
  const name = typeAlias.name.text;
3174
+ const duplicatePropertyNames = findDuplicateObjectLikeTypeAliasPropertyNames(members);
3175
+ if (duplicatePropertyNames.length > 0) {
3176
+ const sourceFile = typeAlias.getSourceFile();
3177
+ const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
3178
+ return {
3179
+ ok: false,
3180
+ error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
3181
+ };
3182
+ }
3060
3183
  const fields = [];
3061
3184
  const typeRegistry = {};
3062
3185
  const diagnostics = [];
@@ -3069,7 +3192,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3069
3192
  const annotations = [...typeAliasDoc.annotations];
3070
3193
  diagnostics.push(...typeAliasDoc.diagnostics);
3071
3194
  const visiting = /* @__PURE__ */ new Set();
3072
- for (const member of typeLiteral.members) {
3195
+ for (const member of members) {
3073
3196
  if (ts3.isPropertySignature(member)) {
3074
3197
  const fieldNode = analyzeInterfacePropertyToIR(
3075
3198
  member,
@@ -3096,12 +3219,20 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3096
3219
  diagnostics,
3097
3220
  normalizedMetadataPolicy
3098
3221
  );
3099
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, typeAlias, {
3222
+ const metadata = resolveNodeMetadata(
3223
+ normalizedMetadataPolicy,
3224
+ "type",
3225
+ name,
3226
+ typeAlias,
3100
3227
  checker,
3101
- declaration: typeAlias,
3102
- subjectType: aliasType,
3103
- hostType: aliasType
3104
- });
3228
+ extensionRegistry,
3229
+ {
3230
+ checker,
3231
+ declaration: typeAlias,
3232
+ subjectType: aliasType,
3233
+ hostType: aliasType
3234
+ }
3235
+ );
3105
3236
  return {
3106
3237
  ok: true,
3107
3238
  analysis: {
@@ -3170,15 +3301,43 @@ function isNullishSemanticType(type) {
3170
3301
  }
3171
3302
  return type.isUnion() && type.types.some((member) => isNullishSemanticType(member));
3172
3303
  }
3173
- function isStringLikeSemanticType(type) {
3304
+ function isStringLikeSemanticType(type, checker, seen = /* @__PURE__ */ new Set()) {
3305
+ if (seen.has(type)) {
3306
+ return false;
3307
+ }
3308
+ seen.add(type);
3174
3309
  if (type.flags & ts3.TypeFlags.StringLike) {
3175
3310
  return true;
3176
3311
  }
3177
3312
  if (type.isUnion()) {
3178
- return type.types.length > 0 && type.types.every((member) => isStringLikeSemanticType(member));
3313
+ return type.types.length > 0 && type.types.every((member) => isStringLikeSemanticType(member, checker, seen));
3314
+ }
3315
+ const baseConstraint = checker.getBaseConstraintOfType(type);
3316
+ if (baseConstraint !== void 0 && baseConstraint !== type) {
3317
+ return isStringLikeSemanticType(baseConstraint, checker, seen);
3179
3318
  }
3180
3319
  return false;
3181
3320
  }
3321
+ function getObjectLikeTypeAliasMembers(typeNode) {
3322
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
3323
+ return getObjectLikeTypeAliasMembers(typeNode.type);
3324
+ }
3325
+ if (ts3.isTypeLiteralNode(typeNode)) {
3326
+ return [...typeNode.members];
3327
+ }
3328
+ if (ts3.isIntersectionTypeNode(typeNode)) {
3329
+ const members = [];
3330
+ for (const intersectionMember of typeNode.types) {
3331
+ const resolvedMembers = getObjectLikeTypeAliasMembers(intersectionMember);
3332
+ if (resolvedMembers === null) {
3333
+ return null;
3334
+ }
3335
+ members.push(...resolvedMembers);
3336
+ }
3337
+ return members;
3338
+ }
3339
+ return null;
3340
+ }
3182
3341
  function extractDiscriminatorDirective(node, file, diagnostics) {
3183
3342
  const discriminatorTags = getLeadingParsedTags(node).filter(
3184
3343
  (tag) => tag.normalizedTagName === "discriminator"
@@ -3285,7 +3444,7 @@ function validateDiscriminatorDirective(node, checker, file, diagnostics) {
3285
3444
  );
3286
3445
  return null;
3287
3446
  }
3288
- if (!isStringLikeSemanticType(property.type)) {
3447
+ if (!isStringLikeSemanticType(property.type, checker)) {
3289
3448
  diagnostics.push(
3290
3449
  makeAnalysisDiagnostic(
3291
3450
  "TYPE_MISMATCH",
@@ -3312,8 +3471,8 @@ function getConcreteTypeArgumentForDiscriminator(node, subjectType, checker, typ
3312
3471
  const localTypeParameter = node.typeParameters?.[typeParameterIndex];
3313
3472
  return localTypeParameter === void 0 ? null : checker.getTypeAtLocation(localTypeParameter);
3314
3473
  }
3315
- function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker, provenance, diagnostics) {
3316
- const propertySymbol = boundType.getProperty(fieldName);
3474
+ function resolveLiteralDiscriminatorPropertyValue(boundType, propertyName, checker, provenance, diagnostics) {
3475
+ const propertySymbol = boundType.getProperty(propertyName);
3317
3476
  if (propertySymbol === void 0) {
3318
3477
  return void 0;
3319
3478
  }
@@ -3344,6 +3503,9 @@ function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker,
3344
3503
  }
3345
3504
  return void 0;
3346
3505
  }
3506
+ function getDiscriminatorIdentityPropertyNames(fieldName) {
3507
+ return fieldName === "object" ? ["object"] : [fieldName, "object"];
3508
+ }
3347
3509
  function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
3348
3510
  const declaration = resolveNamedDiscriminatorDeclaration(boundType, checker);
3349
3511
  if (declaration === null) {
@@ -3354,6 +3516,8 @@ function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
3354
3516
  "type",
3355
3517
  getDiscriminatorLogicalName(boundType, declaration, checker),
3356
3518
  declaration,
3519
+ checker,
3520
+ void 0,
3357
3521
  {
3358
3522
  checker,
3359
3523
  declaration,
@@ -3362,6 +3526,10 @@ function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
3362
3526
  );
3363
3527
  return metadata?.apiName;
3364
3528
  }
3529
+ function applyDiscriminatorApiNamePrefix(value, discriminatorOptions) {
3530
+ const prefix = discriminatorOptions?.apiNamePrefix;
3531
+ return prefix === void 0 || prefix === "" ? value : `${prefix}${value}`;
3532
+ }
3365
3533
  function resolveNamedDiscriminatorDeclaration(type, checker, seen = /* @__PURE__ */ new Set()) {
3366
3534
  if (seen.has(type)) {
3367
3535
  return null;
@@ -3416,22 +3584,27 @@ function resolveDiscriminatorValue(boundType, fieldName, checker, provenance, di
3416
3584
  return null;
3417
3585
  }
3418
3586
  }
3419
- const literalIdentityValue = resolveLiteralDiscriminatorPropertyValue(
3420
- boundType,
3421
- fieldName,
3422
- checker,
3423
- provenance,
3424
- diagnostics
3425
- );
3426
- if (literalIdentityValue !== void 0) {
3427
- return literalIdentityValue;
3587
+ for (const identityPropertyName of getDiscriminatorIdentityPropertyNames(fieldName)) {
3588
+ const literalIdentityValue = resolveLiteralDiscriminatorPropertyValue(
3589
+ boundType,
3590
+ identityPropertyName,
3591
+ checker,
3592
+ provenance,
3593
+ diagnostics
3594
+ );
3595
+ if (literalIdentityValue === null) {
3596
+ return null;
3597
+ }
3598
+ if (literalIdentityValue !== void 0) {
3599
+ return literalIdentityValue;
3600
+ }
3428
3601
  }
3429
3602
  const apiName = resolveDiscriminatorApiName(boundType, checker, metadataPolicy);
3430
3603
  if (apiName?.source === "explicit") {
3431
- return apiName.value;
3604
+ return applyDiscriminatorApiNamePrefix(apiName.value, metadataPolicy.discriminator);
3432
3605
  }
3433
3606
  if (apiName?.source === "inferred") {
3434
- return apiName.value;
3607
+ return applyDiscriminatorApiNamePrefix(apiName.value, metadataPolicy.discriminator);
3435
3608
  }
3436
3609
  diagnostics.push(
3437
3610
  makeAnalysisDiagnostic(
@@ -3494,15 +3667,20 @@ function buildInstantiatedReferenceName(baseName, typeArguments, checker) {
3494
3667
  return renderedArguments.length === 0 ? baseName : `${baseName}__${renderedArguments.join("__")}`;
3495
3668
  }
3496
3669
  function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy, extensionRegistry, diagnostics) {
3497
- const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
3498
- if (typeNode === void 0) {
3670
+ const sourceTypeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
3671
+ if (sourceTypeNode === void 0) {
3499
3672
  return [];
3500
3673
  }
3501
- const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
3502
- if (!ts3.isTypeReferenceNode(resolvedTypeNode) || resolvedTypeNode.typeArguments === void 0) {
3674
+ const unwrapParentheses = (typeNode) => ts3.isParenthesizedTypeNode(typeNode) ? unwrapParentheses(typeNode.type) : typeNode;
3675
+ const directTypeNode = unwrapParentheses(sourceTypeNode);
3676
+ const referenceTypeNode = ts3.isTypeReferenceNode(directTypeNode) ? directTypeNode : (() => {
3677
+ const resolvedTypeNode = resolveAliasedTypeNode(directTypeNode, checker);
3678
+ return ts3.isTypeReferenceNode(resolvedTypeNode) ? resolvedTypeNode : null;
3679
+ })();
3680
+ if (referenceTypeNode?.typeArguments === void 0) {
3503
3681
  return [];
3504
3682
  }
3505
- return resolvedTypeNode.typeArguments.map((argumentNode) => {
3683
+ return referenceTypeNode.typeArguments.map((argumentNode) => {
3506
3684
  const argumentType = checker.getTypeFromTypeNode(argumentNode);
3507
3685
  return {
3508
3686
  tsType: argumentType,
@@ -3590,12 +3768,20 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
3590
3768
  annotations.push(defaultAnnotation);
3591
3769
  }
3592
3770
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
3593
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
3771
+ const metadata = resolveNodeMetadata(
3772
+ metadataPolicy,
3773
+ "field",
3774
+ name,
3775
+ prop,
3594
3776
  checker,
3595
- declaration: prop,
3596
- subjectType: tsType,
3597
- hostType
3598
- });
3777
+ extensionRegistry,
3778
+ {
3779
+ checker,
3780
+ declaration: prop,
3781
+ subjectType: tsType,
3782
+ hostType
3783
+ }
3784
+ );
3599
3785
  return {
3600
3786
  kind: "field",
3601
3787
  name,
@@ -3608,10 +3794,10 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
3608
3794
  };
3609
3795
  }
3610
3796
  function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
3611
- if (!ts3.isIdentifier(prop.name)) {
3797
+ const name = getAnalyzableObjectLikePropertyName(prop.name);
3798
+ if (name === null) {
3612
3799
  return null;
3613
3800
  }
3614
- const name = prop.name.text;
3615
3801
  const tsType = checker.getTypeAtLocation(prop);
3616
3802
  const optional = prop.questionToken !== void 0;
3617
3803
  const provenance = provenanceForNode(prop, file);
@@ -3642,12 +3828,20 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
3642
3828
  let annotations = [];
3643
3829
  annotations.push(...docResult.annotations);
3644
3830
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
3645
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
3831
+ const metadata = resolveNodeMetadata(
3832
+ metadataPolicy,
3833
+ "field",
3834
+ name,
3835
+ prop,
3646
3836
  checker,
3647
- declaration: prop,
3648
- subjectType: tsType,
3649
- hostType
3650
- });
3837
+ extensionRegistry,
3838
+ {
3839
+ checker,
3840
+ declaration: prop,
3841
+ subjectType: tsType,
3842
+ hostType
3843
+ }
3844
+ );
3651
3845
  return {
3652
3846
  kind: "field",
3653
3847
  name,
@@ -3659,6 +3853,31 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
3659
3853
  provenance
3660
3854
  };
3661
3855
  }
3856
+ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
3857
+ const seen = /* @__PURE__ */ new Set();
3858
+ const duplicates = /* @__PURE__ */ new Set();
3859
+ for (const member of members) {
3860
+ if (!ts3.isPropertySignature(member)) {
3861
+ continue;
3862
+ }
3863
+ const name = getAnalyzableObjectLikePropertyName(member.name);
3864
+ if (name === null) {
3865
+ continue;
3866
+ }
3867
+ if (seen.has(name)) {
3868
+ duplicates.add(name);
3869
+ continue;
3870
+ }
3871
+ seen.add(name);
3872
+ }
3873
+ return [...duplicates].sort();
3874
+ }
3875
+ function getAnalyzableObjectLikePropertyName(name) {
3876
+ if (!ts3.isIdentifier(name)) {
3877
+ return null;
3878
+ }
3879
+ return name.text;
3880
+ }
3662
3881
  function applyEnumMemberDisplayNames(type, annotations) {
3663
3882
  if (!annotations.some(
3664
3883
  (annotation) => annotation.annotationKind === "displayName" && annotation.value.trim().startsWith(":")
@@ -3776,7 +3995,7 @@ function getTypeNodeRegistrationName(typeNode) {
3776
3995
  }
3777
3996
  return null;
3778
3997
  }
3779
- 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) {
3780
3999
  const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
3781
4000
  if (customType) {
3782
4001
  return customType;
@@ -3851,6 +4070,23 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
3851
4070
  diagnostics
3852
4071
  );
3853
4072
  }
4073
+ if (isIntersectionType(type)) {
4074
+ const sourceTypeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
4075
+ const resolvedSourceTypeNode = sourceTypeNode === void 0 ? void 0 : resolveAliasedTypeNode(sourceTypeNode, checker);
4076
+ if (resolvedSourceTypeNode !== void 0 && getObjectLikeTypeAliasMembers(resolvedSourceTypeNode) !== null) {
4077
+ return resolveObjectType(
4078
+ type,
4079
+ checker,
4080
+ file,
4081
+ typeRegistry,
4082
+ visiting,
4083
+ sourceNode,
4084
+ metadataPolicy,
4085
+ extensionRegistry,
4086
+ diagnostics
4087
+ );
4088
+ }
4089
+ }
3854
4090
  if (isObjectType(type)) {
3855
4091
  return resolveObjectType(
3856
4092
  type,
@@ -3866,7 +4102,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
3866
4102
  }
3867
4103
  return { kind: "primitive", primitiveKind: "string" };
3868
4104
  }
3869
- function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4105
+ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3870
4106
  if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
3871
4107
  return null;
3872
4108
  }
@@ -3886,11 +4122,19 @@ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiti
3886
4122
  file,
3887
4123
  makeParseOptions(extensionRegistry)
3888
4124
  );
3889
- const metadata = resolveNodeMetadata(metadataPolicy, "type", aliasName, aliasDecl, {
4125
+ const metadata = resolveNodeMetadata(
4126
+ metadataPolicy,
4127
+ "type",
4128
+ aliasName,
4129
+ aliasDecl,
3890
4130
  checker,
3891
- declaration: aliasDecl,
3892
- subjectType: aliasType
3893
- });
4131
+ extensionRegistry,
4132
+ {
4133
+ checker,
4134
+ declaration: aliasDecl,
4135
+ subjectType: aliasType
4136
+ }
4137
+ );
3894
4138
  typeRegistry[aliasName] = {
3895
4139
  name: aliasName,
3896
4140
  ...metadata !== void 0 && { metadata },
@@ -3929,7 +4173,7 @@ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
3929
4173
  const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
3930
4174
  return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
3931
4175
  }
3932
- function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4176
+ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3933
4177
  const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
3934
4178
  if (nestedAliasDecl !== void 0) {
3935
4179
  return resolveAliasedPrimitiveTarget(
@@ -3955,7 +4199,7 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
3955
4199
  diagnostics
3956
4200
  );
3957
4201
  }
3958
- function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4202
+ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3959
4203
  const typeName = getNamedTypeName(type);
3960
4204
  const namedDecl = getNamedTypeDeclaration(type);
3961
4205
  if (typeName && typeName in typeRegistry) {
@@ -3990,11 +4234,19 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
3990
4234
  return result;
3991
4235
  }
3992
4236
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
3993
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", typeName, namedDecl, {
4237
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
4238
+ metadataPolicy,
4239
+ "type",
4240
+ typeName,
4241
+ namedDecl,
3994
4242
  checker,
3995
- declaration: namedDecl,
3996
- subjectType: type
3997
- }) : void 0;
4243
+ extensionRegistry,
4244
+ {
4245
+ checker,
4246
+ declaration: namedDecl,
4247
+ subjectType: type
4248
+ }
4249
+ ) : void 0;
3998
4250
  typeRegistry[typeName] = {
3999
4251
  name: typeName,
4000
4252
  ...metadata !== void 0 && { metadata },
@@ -4079,7 +4331,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
4079
4331
  }
4080
4332
  return registerNamed({ kind: "union", members });
4081
4333
  }
4082
- function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4334
+ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4083
4335
  const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
4084
4336
  const elementType = typeArgs?.[0];
4085
4337
  const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
@@ -4096,7 +4348,7 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNod
4096
4348
  ) : { kind: "primitive", primitiveKind: "string" };
4097
4349
  return { kind: "array", items };
4098
4350
  }
4099
- function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4351
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4100
4352
  if (type.getProperties().length > 0) {
4101
4353
  return null;
4102
4354
  }
@@ -4157,7 +4409,7 @@ function shouldEmitResolvedObjectProperty(property, declaration) {
4157
4409
  }
4158
4410
  return true;
4159
4411
  }
4160
- function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4412
+ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4161
4413
  const collectedDiagnostics = diagnostics ?? [];
4162
4414
  const typeName = getNamedTypeName(type);
4163
4415
  const namedTypeName = typeName ?? void 0;
@@ -4214,7 +4466,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4214
4466
  };
4215
4467
  }
4216
4468
  }
4217
- const recordNode = tryResolveRecordType(
4469
+ const recordNode = isObjectType(type) ? tryResolveRecordType(
4218
4470
  type,
4219
4471
  checker,
4220
4472
  file,
@@ -4223,7 +4475,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4223
4475
  metadataPolicy,
4224
4476
  extensionRegistry,
4225
4477
  collectedDiagnostics
4226
- );
4478
+ ) : null;
4227
4479
  if (recordNode) {
4228
4480
  visiting.delete(type);
4229
4481
  if (registryTypeName !== void 0 && shouldRegisterNamedType) {
@@ -4233,11 +4485,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4233
4485
  return recordNode;
4234
4486
  }
4235
4487
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
4236
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
4488
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
4489
+ metadataPolicy,
4490
+ "type",
4491
+ registryTypeName,
4492
+ namedDecl,
4237
4493
  checker,
4238
- declaration: namedDecl,
4239
- subjectType: type
4240
- }) : void 0;
4494
+ extensionRegistry,
4495
+ {
4496
+ checker,
4497
+ declaration: namedDecl,
4498
+ subjectType: type
4499
+ }
4500
+ ) : void 0;
4241
4501
  typeRegistry[registryTypeName] = {
4242
4502
  name: registryTypeName,
4243
4503
  ...metadata !== void 0 && { metadata },
@@ -4333,11 +4593,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4333
4593
  };
4334
4594
  if (registryTypeName !== void 0 && shouldRegisterNamedType) {
4335
4595
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
4336
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
4596
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
4597
+ metadataPolicy,
4598
+ "type",
4599
+ registryTypeName,
4600
+ namedDecl,
4337
4601
  checker,
4338
- declaration: namedDecl,
4339
- subjectType: type
4340
- }) : void 0;
4602
+ extensionRegistry,
4603
+ {
4604
+ checker,
4605
+ declaration: namedDecl,
4606
+ subjectType: type
4607
+ }
4608
+ ) : void 0;
4341
4609
  typeRegistry[registryTypeName] = {
4342
4610
  name: registryTypeName,
4343
4611
  ...metadata !== void 0 && { metadata },
@@ -4404,9 +4672,10 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
4404
4672
  );
4405
4673
  }
4406
4674
  const typeAliasDecl = declarations.find(ts3.isTypeAliasDeclaration);
4407
- if (typeAliasDecl && ts3.isTypeLiteralNode(typeAliasDecl.type)) {
4675
+ const typeAliasMembers = typeAliasDecl === void 0 ? null : getObjectLikeTypeAliasMembers(typeAliasDecl.type);
4676
+ if (typeAliasDecl && typeAliasMembers !== null) {
4408
4677
  return buildFieldNodeInfoMap(
4409
- typeAliasDecl.type.members,
4678
+ typeAliasMembers,
4410
4679
  checker,
4411
4680
  file,
4412
4681
  typeRegistry,
@@ -4697,17 +4966,18 @@ function findInterfaceByName(sourceFile, interfaceName) {
4697
4966
  function findTypeAliasByName(sourceFile, aliasName) {
4698
4967
  return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
4699
4968
  }
4700
- function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy) {
4969
+ function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
4701
4970
  const ctx = createProgramContext(filePath);
4702
4971
  return analyzeNamedTypeToIRFromProgramContext(
4703
4972
  ctx,
4704
4973
  filePath,
4705
4974
  typeName,
4706
4975
  extensionRegistry,
4707
- metadataPolicy
4976
+ metadataPolicy,
4977
+ discriminatorOptions
4708
4978
  );
4709
4979
  }
4710
- function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy) {
4980
+ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
4711
4981
  const analysisFilePath = path.resolve(filePath);
4712
4982
  const classDecl = findClassByName(ctx.sourceFile, typeName);
4713
4983
  if (classDecl !== null) {
@@ -4716,7 +4986,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
4716
4986
  ctx.checker,
4717
4987
  analysisFilePath,
4718
4988
  extensionRegistry,
4719
- metadataPolicy
4989
+ metadataPolicy,
4990
+ discriminatorOptions
4720
4991
  );
4721
4992
  }
4722
4993
  const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
@@ -4726,7 +4997,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
4726
4997
  ctx.checker,
4727
4998
  analysisFilePath,
4728
4999
  extensionRegistry,
4729
- metadataPolicy
5000
+ metadataPolicy,
5001
+ discriminatorOptions
4730
5002
  );
4731
5003
  }
4732
5004
  const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
@@ -4736,7 +5008,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
4736
5008
  ctx.checker,
4737
5009
  analysisFilePath,
4738
5010
  extensionRegistry,
4739
- metadataPolicy
5011
+ metadataPolicy,
5012
+ discriminatorOptions
4740
5013
  );
4741
5014
  if (result.ok) {
4742
5015
  return result.analysis;
@@ -4873,7 +5146,8 @@ function generateSchemasFromClass(options) {
4873
5146
  ctx.checker,
4874
5147
  options.filePath,
4875
5148
  options.extensionRegistry,
4876
- options.metadata
5149
+ options.metadata,
5150
+ options.discriminator
4877
5151
  );
4878
5152
  return generateClassSchemas(
4879
5153
  analysis,
@@ -4899,7 +5173,8 @@ function generateSchemasFromProgram(options) {
4899
5173
  options.filePath,
4900
5174
  options.typeName,
4901
5175
  options.extensionRegistry,
4902
- options.metadata
5176
+ options.metadata,
5177
+ options.discriminator
4903
5178
  );
4904
5179
  return generateClassSchemas(
4905
5180
  analysis,
@@ -5118,7 +5393,7 @@ function generateSchemasFromResolvedType(options, skipNamedDeclaration = false,
5118
5393
  typeRegistry,
5119
5394
  /* @__PURE__ */ new Set(),
5120
5395
  options.sourceNode,
5121
- normalizeMetadataPolicy(options.metadata),
5396
+ createAnalyzerMetadataPolicy(options.metadata, options.discriminator),
5122
5397
  options.extensionRegistry,
5123
5398
  diagnostics
5124
5399
  );
@@ -5167,7 +5442,8 @@ function generateSchemasFromDeclaration(options) {
5167
5442
  options.context.checker,
5168
5443
  filePath,
5169
5444
  options.extensionRegistry,
5170
- options.metadata
5445
+ options.metadata,
5446
+ options.discriminator
5171
5447
  ),
5172
5448
  filePath,
5173
5449
  options
@@ -5180,7 +5456,8 @@ function generateSchemasFromDeclaration(options) {
5180
5456
  options.context.checker,
5181
5457
  filePath,
5182
5458
  options.extensionRegistry,
5183
- options.metadata
5459
+ options.metadata,
5460
+ options.discriminator
5184
5461
  ),
5185
5462
  filePath,
5186
5463
  options
@@ -5192,7 +5469,8 @@ function generateSchemasFromDeclaration(options) {
5192
5469
  options.context.checker,
5193
5470
  filePath,
5194
5471
  options.extensionRegistry,
5195
- options.metadata
5472
+ options.metadata,
5473
+ options.discriminator
5196
5474
  );
5197
5475
  if (analyzedAlias.ok) {
5198
5476
  return generateSchemasFromAnalysis(analyzedAlias.analysis, filePath, options);
@@ -5258,7 +5536,8 @@ function buildMixedAuthoringSchemas(options) {
5258
5536
  filePath,
5259
5537
  typeName,
5260
5538
  schemaOptions.extensionRegistry,
5261
- schemaOptions.metadata
5539
+ schemaOptions.metadata,
5540
+ schemaOptions.discriminator
5262
5541
  );
5263
5542
  const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays, schemaOptions.metadata);
5264
5543
  const ir = canonicalizeTSDoc(