@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.js CHANGED
@@ -1854,13 +1854,34 @@ var init_generator2 = __esm({
1854
1854
  });
1855
1855
 
1856
1856
  // src/extensions/registry.ts
1857
+ import {
1858
+ BUILTIN_CONSTRAINT_DEFINITIONS,
1859
+ normalizeConstraintTagName
1860
+ } from "@formspec/core/internals";
1861
+ import {
1862
+ getTagDefinition,
1863
+ normalizeFormSpecTagName
1864
+ } from "@formspec/analysis/internal";
1865
+ function buildConstraintTagSources(extensions) {
1866
+ return extensions.map((extension) => ({
1867
+ extensionId: extension.extensionId,
1868
+ ...extension.constraintTags !== void 0 ? {
1869
+ constraintTags: extension.constraintTags.map((tag) => ({
1870
+ tagName: normalizeFormSpecTagName(tag.tagName)
1871
+ }))
1872
+ } : {}
1873
+ }));
1874
+ }
1857
1875
  function createExtensionRegistry(extensions) {
1876
+ const reservedTagSources = buildConstraintTagSources(extensions);
1858
1877
  const typeMap = /* @__PURE__ */ new Map();
1859
1878
  const typeNameMap = /* @__PURE__ */ new Map();
1860
1879
  const constraintMap = /* @__PURE__ */ new Map();
1861
1880
  const constraintTagMap = /* @__PURE__ */ new Map();
1862
1881
  const builtinBroadeningMap = /* @__PURE__ */ new Map();
1863
1882
  const annotationMap = /* @__PURE__ */ new Map();
1883
+ const metadataSlotMap = /* @__PURE__ */ new Map();
1884
+ const metadataTagMap = /* @__PURE__ */ new Map();
1864
1885
  for (const ext of extensions) {
1865
1886
  if (ext.types !== void 0) {
1866
1887
  for (const type of ext.types) {
@@ -1903,10 +1924,11 @@ function createExtensionRegistry(extensions) {
1903
1924
  }
1904
1925
  if (ext.constraintTags !== void 0) {
1905
1926
  for (const tag of ext.constraintTags) {
1906
- if (constraintTagMap.has(tag.tagName)) {
1907
- throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
1927
+ const canonicalTagName = normalizeFormSpecTagName(tag.tagName);
1928
+ if (constraintTagMap.has(canonicalTagName)) {
1929
+ throw new Error(`Duplicate custom constraint tag: "@${canonicalTagName}"`);
1908
1930
  }
1909
- constraintTagMap.set(tag.tagName, {
1931
+ constraintTagMap.set(canonicalTagName, {
1910
1932
  extensionId: ext.extensionId,
1911
1933
  registration: tag
1912
1934
  });
@@ -1921,20 +1943,63 @@ function createExtensionRegistry(extensions) {
1921
1943
  annotationMap.set(qualifiedId, annotation);
1922
1944
  }
1923
1945
  }
1946
+ if (ext.metadataSlots !== void 0) {
1947
+ for (const slot of ext.metadataSlots) {
1948
+ if (metadataSlotMap.has(slot.slotId)) {
1949
+ throw new Error(`Duplicate metadata slot ID: "${slot.slotId}"`);
1950
+ }
1951
+ metadataSlotMap.set(slot.slotId, true);
1952
+ const canonicalTagName = normalizeFormSpecTagName(slot.tagName);
1953
+ if (slot.allowBare === false && (slot.qualifiers?.length ?? 0) === 0) {
1954
+ throw new Error(
1955
+ `Metadata tag "@${canonicalTagName}" must allow bare usage or declare at least one qualifier.`
1956
+ );
1957
+ }
1958
+ if (metadataTagMap.has(canonicalTagName)) {
1959
+ throw new Error(`Duplicate metadata tag: "@${canonicalTagName}"`);
1960
+ }
1961
+ if (BUILTIN_METADATA_TAGS.has(canonicalTagName)) {
1962
+ throw new Error(
1963
+ `Metadata tag "@${canonicalTagName}" conflicts with built-in metadata tags.`
1964
+ );
1965
+ }
1966
+ if (constraintTagMap.has(canonicalTagName)) {
1967
+ throw new Error(
1968
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${canonicalTagName}".`
1969
+ );
1970
+ }
1971
+ if (Object.hasOwn(BUILTIN_CONSTRAINT_DEFINITIONS, normalizeConstraintTagName(canonicalTagName))) {
1972
+ throw new Error(
1973
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${normalizeConstraintTagName(canonicalTagName)}".`
1974
+ );
1975
+ }
1976
+ const existingTag = getTagDefinition(canonicalTagName, reservedTagSources);
1977
+ if (existingTag !== null) {
1978
+ throw BUILTIN_METADATA_TAGS.has(existingTag.canonicalName) ? new Error(
1979
+ `Metadata tag "@${canonicalTagName}" conflicts with built-in metadata tags.`
1980
+ ) : new Error(
1981
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${existingTag.canonicalName}".`
1982
+ );
1983
+ }
1984
+ metadataTagMap.set(canonicalTagName, true);
1985
+ }
1986
+ }
1924
1987
  }
1925
1988
  return {
1926
1989
  extensions,
1927
1990
  findType: (typeId) => typeMap.get(typeId),
1928
1991
  findTypeByName: (typeName) => typeNameMap.get(typeName),
1929
1992
  findConstraint: (constraintId) => constraintMap.get(constraintId),
1930
- findConstraintTag: (tagName) => constraintTagMap.get(tagName),
1993
+ findConstraintTag: (tagName) => constraintTagMap.get(normalizeFormSpecTagName(tagName)),
1931
1994
  findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
1932
1995
  findAnnotation: (annotationId) => annotationMap.get(annotationId)
1933
1996
  };
1934
1997
  }
1998
+ var BUILTIN_METADATA_TAGS;
1935
1999
  var init_registry = __esm({
1936
2000
  "src/extensions/registry.ts"() {
1937
2001
  "use strict";
2002
+ BUILTIN_METADATA_TAGS = /* @__PURE__ */ new Set(["apiName", "displayName"]);
1938
2003
  }
1939
2004
  });
1940
2005
 
@@ -2019,8 +2084,9 @@ import * as ts from "typescript";
2019
2084
  import {
2020
2085
  checkSyntheticTagApplication,
2021
2086
  extractPathTarget as extractSharedPathTarget,
2022
- getTagDefinition,
2087
+ getTagDefinition as getTagDefinition2,
2023
2088
  hasTypeSemanticCapability,
2089
+ normalizeFormSpecTagName as normalizeFormSpecTagName2,
2024
2090
  parseConstraintTagValue,
2025
2091
  parseDefaultValueTagValue,
2026
2092
  resolveDeclarationPlacement,
@@ -2040,14 +2106,14 @@ import {
2040
2106
  TextRange
2041
2107
  } from "@microsoft/tsdoc";
2042
2108
  import {
2043
- BUILTIN_CONSTRAINT_DEFINITIONS,
2044
- normalizeConstraintTagName,
2109
+ BUILTIN_CONSTRAINT_DEFINITIONS as BUILTIN_CONSTRAINT_DEFINITIONS2,
2110
+ normalizeConstraintTagName as normalizeConstraintTagName2,
2045
2111
  isBuiltinConstraintName
2046
2112
  } from "@formspec/core/internals";
2047
2113
  import "@formspec/core/internals";
2048
2114
  function createFormSpecTSDocConfig(extensionTagNames = []) {
2049
2115
  const config = new TSDocConfiguration();
2050
- for (const tagName of Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS)) {
2116
+ for (const tagName of Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS2)) {
2051
2117
  config.addTagDefinition(
2052
2118
  new TSDocTagDefinition({
2053
2119
  tagName: "@" + tagName,
@@ -2056,7 +2122,7 @@ function createFormSpecTSDocConfig(extensionTagNames = []) {
2056
2122
  })
2057
2123
  );
2058
2124
  }
2059
- for (const tagName of ["displayName", "format", "placeholder"]) {
2125
+ for (const tagName of ["apiName", "displayName", "format", "placeholder"]) {
2060
2126
  config.addTagDefinition(
2061
2127
  new TSDocTagDefinition({
2062
2128
  tagName: "@" + tagName,
@@ -2089,6 +2155,16 @@ function sharedTagValueOptions(options) {
2089
2155
  ...options?.fieldType !== void 0 ? { fieldType: options.fieldType } : {}
2090
2156
  };
2091
2157
  }
2158
+ function getExtensionTypeNames(registry) {
2159
+ if (registry === void 0) {
2160
+ return /* @__PURE__ */ new Set();
2161
+ }
2162
+ return new Set(
2163
+ registry.extensions.flatMap(
2164
+ (ext) => (ext.types ?? []).flatMap((t) => t.tsTypeNames ?? [t.typeName])
2165
+ )
2166
+ );
2167
+ }
2092
2168
  function collectImportedNames(sourceFile) {
2093
2169
  const importedNames = /* @__PURE__ */ new Set();
2094
2170
  for (const statement of sourceFile.statements) {
@@ -2128,6 +2204,9 @@ function isNonReferenceIdentifier(node) {
2128
2204
  return false;
2129
2205
  }
2130
2206
  function statementReferencesImportedName(statement, importedNames) {
2207
+ if (importedNames.size === 0) {
2208
+ return false;
2209
+ }
2131
2210
  let referencesImportedName = false;
2132
2211
  const visit = (node) => {
2133
2212
  if (referencesImportedName) {
@@ -2142,14 +2221,17 @@ function statementReferencesImportedName(statement, importedNames) {
2142
2221
  visit(statement);
2143
2222
  return referencesImportedName;
2144
2223
  }
2145
- function buildSupportingDeclarations(sourceFile) {
2224
+ function buildSupportingDeclarations(sourceFile, extensionTypeNames) {
2146
2225
  const importedNames = collectImportedNames(sourceFile);
2226
+ const importedNamesToSkip = new Set(
2227
+ [...importedNames].filter((name) => !extensionTypeNames.has(name))
2228
+ );
2147
2229
  return sourceFile.statements.filter((statement) => {
2148
2230
  if (ts.isImportDeclaration(statement)) return false;
2149
2231
  if (ts.isImportEqualsDeclaration(statement)) return false;
2150
2232
  if (ts.isExportDeclaration(statement) && statement.moduleSpecifier !== void 0)
2151
2233
  return false;
2152
- if (importedNames.size > 0 && statementReferencesImportedName(statement, importedNames)) {
2234
+ if (statementReferencesImportedName(statement, importedNamesToSkip)) {
2153
2235
  return false;
2154
2236
  }
2155
2237
  return true;
@@ -2302,7 +2384,7 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
2302
2384
  if (placement === null) {
2303
2385
  return [];
2304
2386
  }
2305
- const definition = getTagDefinition(tagName, options?.extensionRegistry?.extensions);
2387
+ const definition = getTagDefinition2(tagName, options?.extensionRegistry?.extensions);
2306
2388
  if (definition === null) {
2307
2389
  return [];
2308
2390
  }
@@ -2406,6 +2488,14 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
2406
2488
  extensionId: extension.extensionId,
2407
2489
  ...extension.constraintTags !== void 0 ? {
2408
2490
  constraintTags: extension.constraintTags.map((tag) => ({ tagName: tag.tagName }))
2491
+ } : {},
2492
+ ...extension.metadataSlots !== void 0 ? {
2493
+ metadataSlots: extension.metadataSlots
2494
+ } : {},
2495
+ ...extension.types !== void 0 ? {
2496
+ customTypes: extension.types.map((t) => ({
2497
+ tsTypeNames: t.tsTypeNames ?? [t.typeName]
2498
+ }))
2409
2499
  } : {}
2410
2500
  }))
2411
2501
  } : {}
@@ -2425,7 +2515,10 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
2425
2515
  function getParser(options) {
2426
2516
  const extensionTagNames = [
2427
2517
  ...options?.extensionRegistry?.extensions.flatMap(
2428
- (extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
2518
+ (extension) => (extension.constraintTags ?? []).map((tag) => normalizeFormSpecTagName2(tag.tagName))
2519
+ ) ?? [],
2520
+ ...options?.extensionRegistry?.extensions.flatMap(
2521
+ (extension) => (extension.metadataSlots ?? []).map((slot) => normalizeFormSpecTagName2(slot.tagName))
2429
2522
  ) ?? []
2430
2523
  ].sort();
2431
2524
  const cacheKey = extensionTagNames.join("|");
@@ -2445,7 +2538,16 @@ function getExtensionRegistryCacheKey(registry) {
2445
2538
  (extension) => JSON.stringify({
2446
2539
  extensionId: extension.extensionId,
2447
2540
  typeNames: extension.types?.map((type) => type.typeName) ?? [],
2448
- constraintTags: extension.constraintTags?.map((tag) => tag.tagName) ?? []
2541
+ constraintTags: extension.constraintTags?.map((tag) => normalizeFormSpecTagName2(tag.tagName)) ?? [],
2542
+ metadataSlots: extension.metadataSlots?.map((slot) => ({
2543
+ tagName: normalizeFormSpecTagName2(slot.tagName),
2544
+ declarationKinds: [...slot.declarationKinds].sort(),
2545
+ allowBare: slot.allowBare !== false,
2546
+ qualifiers: (slot.qualifiers ?? []).map((qualifier) => ({
2547
+ qualifier: qualifier.qualifier,
2548
+ ...qualifier.sourceQualifier !== void 0 ? { sourceQualifier: qualifier.sourceQualifier } : {}
2549
+ })).sort((left, right) => left.qualifier.localeCompare(right.qualifier))
2550
+ })) ?? []
2449
2551
  })
2450
2552
  ).join("|");
2451
2553
  }
@@ -2480,7 +2582,8 @@ function parseTSDocTags(node, file = "", options) {
2480
2582
  const rawTextTags = [];
2481
2583
  const sourceFile = node.getSourceFile();
2482
2584
  const sourceText = sourceFile.getFullText();
2483
- const supportingDeclarations = buildSupportingDeclarations(sourceFile);
2585
+ const extensionTypeNames = getExtensionTypeNames(options?.extensionRegistry);
2586
+ const supportingDeclarations = buildSupportingDeclarations(sourceFile, extensionTypeNames);
2484
2587
  const commentRanges = ts.getLeadingCommentRanges(sourceText, node.getFullStart());
2485
2588
  const rawTextFallbacks = collectRawTextFallbacks(node, file);
2486
2589
  if (commentRanges) {
@@ -2518,7 +2621,7 @@ function parseTSDocTags(node, file = "", options) {
2518
2621
  }
2519
2622
  }
2520
2623
  for (const block of docComment.customBlocks) {
2521
- const tagName = normalizeConstraintTagName(block.blockTag.tagName.substring(1));
2624
+ const tagName = normalizeConstraintTagName2(block.blockTag.tagName.substring(1));
2522
2625
  const parsedTag = nextParsedTag(tagName);
2523
2626
  if (tagName === "displayName" || tagName === "format" || tagName === "placeholder") {
2524
2627
  const text2 = getBestBlockPayloadText(parsedTag, commentText, range.pos, block);
@@ -2550,7 +2653,7 @@ function parseTSDocTags(node, file = "", options) {
2550
2653
  }
2551
2654
  if (TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
2552
2655
  const text = getBestBlockPayloadText(parsedTag, commentText, range.pos, block);
2553
- const expectedType = isBuiltinConstraintName(tagName) ? BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
2656
+ const expectedType = isBuiltinConstraintName(tagName) ? BUILTIN_CONSTRAINT_DEFINITIONS2[tagName] : void 0;
2554
2657
  if (text === "" && expectedType !== "boolean") continue;
2555
2658
  const provenance = parsedTag !== null ? provenanceForParsedTag(parsedTag, sourceFile, file) : provenanceForComment(range, sourceFile, file, tagName);
2556
2659
  const compilerDiagnostics = buildCompilerBackedConstraintDiagnostics(
@@ -2781,7 +2884,7 @@ function getBestBlockPayloadText(tag, commentText, commentOffset, block) {
2781
2884
  function collectRawTextFallbacks(node, file) {
2782
2885
  const fallbacks = /* @__PURE__ */ new Map();
2783
2886
  for (const tag of ts.getJSDocTags(node)) {
2784
- const tagName = normalizeConstraintTagName(tag.tagName.text);
2887
+ const tagName = normalizeConstraintTagName2(tag.tagName.text);
2785
2888
  if (!TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
2786
2889
  const commentText = getTagCommentText(tag)?.trim() ?? "";
2787
2890
  if (commentText === "") continue;
@@ -2904,11 +3007,15 @@ var init_jsdoc_constraints = __esm({
2904
3007
  // src/analyzer/class-analyzer.ts
2905
3008
  import * as ts3 from "typescript";
2906
3009
  import {
3010
+ analyzeMetadataForNodeWithChecker,
2907
3011
  parseCommentBlock as parseCommentBlock2
2908
3012
  } from "@formspec/analysis/internal";
2909
3013
  function isObjectType(type) {
2910
3014
  return !!(type.flags & ts3.TypeFlags.Object);
2911
3015
  }
3016
+ function isIntersectionType(type) {
3017
+ return !!(type.flags & ts3.TypeFlags.Intersection);
3018
+ }
2912
3019
  function isTypeReference(type) {
2913
3020
  return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
2914
3021
  }
@@ -2924,76 +3031,51 @@ function makeParseOptions(extensionRegistry, fieldType, checker, subjectType, ho
2924
3031
  ...hostType !== void 0 && { hostType }
2925
3032
  };
2926
3033
  }
2927
- function makeExplicitScalarMetadata(value) {
2928
- return value === void 0 || value === "" ? void 0 : { value, source: "explicit" };
2929
- }
2930
- function extractExplicitMetadata(node) {
2931
- let apiName;
2932
- let displayName;
2933
- let apiNamePlural;
2934
- let displayNamePlural;
2935
- for (const tag of getLeadingParsedTags(node)) {
2936
- const value = tag.argumentText.trim();
2937
- if (value === "") {
2938
- continue;
2939
- }
2940
- if (tag.normalizedTagName === "apiName") {
2941
- if (tag.target === null) {
2942
- apiName ??= value;
2943
- } else if (tag.target.kind === "variant") {
2944
- if (tag.target.rawText === "singular") {
2945
- apiName ??= value;
2946
- } else if (tag.target.rawText === "plural") {
2947
- apiNamePlural ??= value;
2948
- }
2949
- }
2950
- continue;
2951
- }
2952
- if (tag.normalizedTagName === "displayName") {
2953
- if (tag.target === null) {
2954
- displayName ??= value;
2955
- } else if (tag.target.kind === "variant") {
2956
- if (tag.target.rawText === "singular") {
2957
- displayName ??= value;
2958
- } else if (tag.target.rawText === "plural") {
2959
- displayNamePlural ??= value;
2960
- }
2961
- }
2962
- }
2963
- }
2964
- const resolvedApiName = makeExplicitScalarMetadata(apiName);
2965
- const resolvedDisplayName = makeExplicitScalarMetadata(displayName);
2966
- const resolvedApiNamePlural = makeExplicitScalarMetadata(apiNamePlural);
2967
- const resolvedDisplayNamePlural = makeExplicitScalarMetadata(displayNamePlural);
2968
- const metadata = {
2969
- ...resolvedApiName !== void 0 && { apiName: resolvedApiName },
2970
- ...resolvedDisplayName !== void 0 && { displayName: resolvedDisplayName },
2971
- ...resolvedApiNamePlural !== void 0 && { apiNamePlural: resolvedApiNamePlural },
2972
- ...resolvedDisplayNamePlural !== void 0 && {
2973
- displayNamePlural: resolvedDisplayNamePlural
2974
- }
3034
+ function createAnalyzerMetadataPolicy(input, discriminator) {
3035
+ return {
3036
+ raw: input,
3037
+ normalized: normalizeMetadataPolicy(input),
3038
+ discriminator
2975
3039
  };
2976
- return Object.keys(metadata).length === 0 ? void 0 : metadata;
2977
3040
  }
2978
- function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, buildContext) {
2979
- const explicit = extractExplicitMetadata(node);
2980
- return resolveMetadata(
2981
- {
2982
- ...explicit?.apiName !== void 0 && { apiName: explicit.apiName.value },
2983
- ...explicit?.displayName !== void 0 && { displayName: explicit.displayName.value },
2984
- ...explicit?.apiNamePlural !== void 0 && {
2985
- apiNamePlural: explicit.apiNamePlural.value
2986
- },
2987
- ...explicit?.displayNamePlural !== void 0 && {
2988
- displayNamePlural: explicit.displayNamePlural.value
2989
- }
2990
- },
2991
- getDeclarationMetadataPolicy(metadataPolicy, declarationKind),
2992
- makeMetadataContext("tsdoc", declarationKind, logicalName, buildContext)
3041
+ function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, checker, extensionRegistry, buildContext) {
3042
+ const analysis = analyzeMetadataForNodeWithChecker({
3043
+ checker,
3044
+ node,
3045
+ logicalName,
3046
+ metadata: metadataPolicy.raw,
3047
+ extensions: extensionRegistry?.extensions,
3048
+ ...buildContext !== void 0 && { buildContext }
3049
+ });
3050
+ const resolvedMetadata = analysis?.resolvedMetadata;
3051
+ const declarationPolicy = getDeclarationMetadataPolicy(
3052
+ metadataPolicy.normalized,
3053
+ declarationKind
2993
3054
  );
3055
+ if (resolvedMetadata?.apiName === void 0 && declarationPolicy.apiName.mode === "require-explicit") {
3056
+ throw new Error(
3057
+ `Metadata policy requires explicit apiName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
3058
+ );
3059
+ }
3060
+ if (resolvedMetadata?.displayName === void 0 && declarationPolicy.displayName.mode === "require-explicit") {
3061
+ throw new Error(
3062
+ `Metadata policy requires explicit displayName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
3063
+ );
3064
+ }
3065
+ if (resolvedMetadata?.apiNamePlural === void 0 && declarationPolicy.apiName.pluralization.mode === "require-explicit") {
3066
+ throw new Error(
3067
+ `Metadata policy requires explicit apiNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
3068
+ );
3069
+ }
3070
+ if (resolvedMetadata?.displayNamePlural === void 0 && declarationPolicy.displayName.pluralization.mode === "require-explicit") {
3071
+ throw new Error(
3072
+ `Metadata policy requires explicit displayNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
3073
+ );
3074
+ }
3075
+ return resolvedMetadata;
2994
3076
  }
2995
3077
  function analyzeDeclarationRootInfo(declaration, checker, file = "", extensionRegistry, metadataPolicy) {
2996
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3078
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
2997
3079
  const declarationType = checker.getTypeAtLocation(declaration);
2998
3080
  const logicalName = ts3.isClassDeclaration(declaration) ? declaration.name?.text ?? "AnonymousClass" : declaration.name.text;
2999
3081
  const docResult = extractJSDocParseResult(
@@ -3001,20 +3083,31 @@ function analyzeDeclarationRootInfo(declaration, checker, file = "", extensionRe
3001
3083
  file,
3002
3084
  makeParseOptions(extensionRegistry, void 0, checker, declarationType, declarationType)
3003
3085
  );
3004
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", logicalName, declaration, {
3005
- checker,
3086
+ const metadata = resolveNodeMetadata(
3087
+ normalizedMetadataPolicy,
3088
+ "type",
3089
+ logicalName,
3006
3090
  declaration,
3007
- subjectType: declarationType,
3008
- hostType: declarationType
3009
- });
3091
+ checker,
3092
+ extensionRegistry,
3093
+ {
3094
+ checker,
3095
+ declaration,
3096
+ subjectType: declarationType,
3097
+ hostType: declarationType
3098
+ }
3099
+ );
3010
3100
  return {
3011
3101
  ...metadata !== void 0 && { metadata },
3012
3102
  annotations: docResult.annotations,
3013
3103
  diagnostics: docResult.diagnostics
3014
3104
  };
3015
3105
  }
3016
- function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy) {
3017
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3106
+ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
3107
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
3108
+ metadataPolicy,
3109
+ discriminatorOptions
3110
+ );
3018
3111
  const name = classDecl.name?.text ?? "AnonymousClass";
3019
3112
  const fields = [];
3020
3113
  const fieldLayouts = [];
@@ -3069,12 +3162,20 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
3069
3162
  diagnostics,
3070
3163
  normalizedMetadataPolicy
3071
3164
  );
3072
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, classDecl, {
3165
+ const metadata = resolveNodeMetadata(
3166
+ normalizedMetadataPolicy,
3167
+ "type",
3168
+ name,
3169
+ classDecl,
3073
3170
  checker,
3074
- declaration: classDecl,
3075
- subjectType: classType,
3076
- hostType: classType
3077
- });
3171
+ extensionRegistry,
3172
+ {
3173
+ checker,
3174
+ declaration: classDecl,
3175
+ subjectType: classType,
3176
+ hostType: classType
3177
+ }
3178
+ );
3078
3179
  return {
3079
3180
  name,
3080
3181
  ...metadata !== void 0 && { metadata },
@@ -3087,8 +3188,11 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
3087
3188
  staticMethods
3088
3189
  };
3089
3190
  }
3090
- function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy) {
3091
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3191
+ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
3192
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
3193
+ metadataPolicy,
3194
+ discriminatorOptions
3195
+ );
3092
3196
  const name = interfaceDecl.name.text;
3093
3197
  const fields = [];
3094
3198
  const typeRegistry = {};
@@ -3130,12 +3234,20 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
3130
3234
  normalizedMetadataPolicy
3131
3235
  );
3132
3236
  const fieldLayouts = specializedFields.map(() => ({}));
3133
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, interfaceDecl, {
3237
+ const metadata = resolveNodeMetadata(
3238
+ normalizedMetadataPolicy,
3239
+ "type",
3240
+ name,
3241
+ interfaceDecl,
3134
3242
  checker,
3135
- declaration: interfaceDecl,
3136
- subjectType: interfaceType,
3137
- hostType: interfaceType
3138
- });
3243
+ extensionRegistry,
3244
+ {
3245
+ checker,
3246
+ declaration: interfaceDecl,
3247
+ subjectType: interfaceType,
3248
+ hostType: interfaceType
3249
+ }
3250
+ );
3139
3251
  return {
3140
3252
  name,
3141
3253
  ...metadata !== void 0 && { metadata },
@@ -3148,19 +3260,31 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
3148
3260
  staticMethods: []
3149
3261
  };
3150
3262
  }
3151
- function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry, metadataPolicy) {
3152
- if (!ts3.isTypeLiteralNode(typeAlias.type)) {
3263
+ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
3264
+ const members = getObjectLikeTypeAliasMembers(typeAlias.type);
3265
+ if (members === null) {
3153
3266
  const sourceFile = typeAlias.getSourceFile();
3154
3267
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
3155
3268
  const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
3156
3269
  return {
3157
3270
  ok: false,
3158
- error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
3271
+ error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
3159
3272
  };
3160
3273
  }
3161
- const typeLiteral = typeAlias.type;
3162
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
3274
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
3275
+ metadataPolicy,
3276
+ discriminatorOptions
3277
+ );
3163
3278
  const name = typeAlias.name.text;
3279
+ const duplicatePropertyNames = findDuplicateObjectLikeTypeAliasPropertyNames(members);
3280
+ if (duplicatePropertyNames.length > 0) {
3281
+ const sourceFile = typeAlias.getSourceFile();
3282
+ const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
3283
+ return {
3284
+ ok: false,
3285
+ error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
3286
+ };
3287
+ }
3164
3288
  const fields = [];
3165
3289
  const typeRegistry = {};
3166
3290
  const diagnostics = [];
@@ -3173,7 +3297,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3173
3297
  const annotations = [...typeAliasDoc.annotations];
3174
3298
  diagnostics.push(...typeAliasDoc.diagnostics);
3175
3299
  const visiting = /* @__PURE__ */ new Set();
3176
- for (const member of typeLiteral.members) {
3300
+ for (const member of members) {
3177
3301
  if (ts3.isPropertySignature(member)) {
3178
3302
  const fieldNode = analyzeInterfacePropertyToIR(
3179
3303
  member,
@@ -3200,12 +3324,20 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3200
3324
  diagnostics,
3201
3325
  normalizedMetadataPolicy
3202
3326
  );
3203
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, typeAlias, {
3327
+ const metadata = resolveNodeMetadata(
3328
+ normalizedMetadataPolicy,
3329
+ "type",
3330
+ name,
3331
+ typeAlias,
3204
3332
  checker,
3205
- declaration: typeAlias,
3206
- subjectType: aliasType,
3207
- hostType: aliasType
3208
- });
3333
+ extensionRegistry,
3334
+ {
3335
+ checker,
3336
+ declaration: typeAlias,
3337
+ subjectType: aliasType,
3338
+ hostType: aliasType
3339
+ }
3340
+ );
3209
3341
  return {
3210
3342
  ok: true,
3211
3343
  analysis: {
@@ -3274,15 +3406,43 @@ function isNullishSemanticType(type) {
3274
3406
  }
3275
3407
  return type.isUnion() && type.types.some((member) => isNullishSemanticType(member));
3276
3408
  }
3277
- function isStringLikeSemanticType(type) {
3409
+ function isStringLikeSemanticType(type, checker, seen = /* @__PURE__ */ new Set()) {
3410
+ if (seen.has(type)) {
3411
+ return false;
3412
+ }
3413
+ seen.add(type);
3278
3414
  if (type.flags & ts3.TypeFlags.StringLike) {
3279
3415
  return true;
3280
3416
  }
3281
3417
  if (type.isUnion()) {
3282
- return type.types.length > 0 && type.types.every((member) => isStringLikeSemanticType(member));
3418
+ return type.types.length > 0 && type.types.every((member) => isStringLikeSemanticType(member, checker, seen));
3419
+ }
3420
+ const baseConstraint = checker.getBaseConstraintOfType(type);
3421
+ if (baseConstraint !== void 0 && baseConstraint !== type) {
3422
+ return isStringLikeSemanticType(baseConstraint, checker, seen);
3283
3423
  }
3284
3424
  return false;
3285
3425
  }
3426
+ function getObjectLikeTypeAliasMembers(typeNode) {
3427
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
3428
+ return getObjectLikeTypeAliasMembers(typeNode.type);
3429
+ }
3430
+ if (ts3.isTypeLiteralNode(typeNode)) {
3431
+ return [...typeNode.members];
3432
+ }
3433
+ if (ts3.isIntersectionTypeNode(typeNode)) {
3434
+ const members = [];
3435
+ for (const intersectionMember of typeNode.types) {
3436
+ const resolvedMembers = getObjectLikeTypeAliasMembers(intersectionMember);
3437
+ if (resolvedMembers === null) {
3438
+ return null;
3439
+ }
3440
+ members.push(...resolvedMembers);
3441
+ }
3442
+ return members;
3443
+ }
3444
+ return null;
3445
+ }
3286
3446
  function extractDiscriminatorDirective(node, file, diagnostics) {
3287
3447
  const discriminatorTags = getLeadingParsedTags(node).filter(
3288
3448
  (tag) => tag.normalizedTagName === "discriminator"
@@ -3389,7 +3549,7 @@ function validateDiscriminatorDirective(node, checker, file, diagnostics) {
3389
3549
  );
3390
3550
  return null;
3391
3551
  }
3392
- if (!isStringLikeSemanticType(property.type)) {
3552
+ if (!isStringLikeSemanticType(property.type, checker)) {
3393
3553
  diagnostics.push(
3394
3554
  makeAnalysisDiagnostic(
3395
3555
  "TYPE_MISMATCH",
@@ -3416,8 +3576,8 @@ function getConcreteTypeArgumentForDiscriminator(node, subjectType, checker, typ
3416
3576
  const localTypeParameter = node.typeParameters?.[typeParameterIndex];
3417
3577
  return localTypeParameter === void 0 ? null : checker.getTypeAtLocation(localTypeParameter);
3418
3578
  }
3419
- function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker, provenance, diagnostics) {
3420
- const propertySymbol = boundType.getProperty(fieldName);
3579
+ function resolveLiteralDiscriminatorPropertyValue(boundType, propertyName, checker, provenance, diagnostics) {
3580
+ const propertySymbol = boundType.getProperty(propertyName);
3421
3581
  if (propertySymbol === void 0) {
3422
3582
  return void 0;
3423
3583
  }
@@ -3448,6 +3608,9 @@ function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker,
3448
3608
  }
3449
3609
  return void 0;
3450
3610
  }
3611
+ function getDiscriminatorIdentityPropertyNames(fieldName) {
3612
+ return fieldName === "object" ? ["object"] : [fieldName, "object"];
3613
+ }
3451
3614
  function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
3452
3615
  const declaration = resolveNamedDiscriminatorDeclaration(boundType, checker);
3453
3616
  if (declaration === null) {
@@ -3458,6 +3621,8 @@ function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
3458
3621
  "type",
3459
3622
  getDiscriminatorLogicalName(boundType, declaration, checker),
3460
3623
  declaration,
3624
+ checker,
3625
+ void 0,
3461
3626
  {
3462
3627
  checker,
3463
3628
  declaration,
@@ -3466,6 +3631,10 @@ function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
3466
3631
  );
3467
3632
  return metadata?.apiName;
3468
3633
  }
3634
+ function applyDiscriminatorApiNamePrefix(value, discriminatorOptions) {
3635
+ const prefix = discriminatorOptions?.apiNamePrefix;
3636
+ return prefix === void 0 || prefix === "" ? value : `${prefix}${value}`;
3637
+ }
3469
3638
  function resolveNamedDiscriminatorDeclaration(type, checker, seen = /* @__PURE__ */ new Set()) {
3470
3639
  if (seen.has(type)) {
3471
3640
  return null;
@@ -3520,22 +3689,27 @@ function resolveDiscriminatorValue(boundType, fieldName, checker, provenance, di
3520
3689
  return null;
3521
3690
  }
3522
3691
  }
3523
- const literalIdentityValue = resolveLiteralDiscriminatorPropertyValue(
3524
- boundType,
3525
- fieldName,
3526
- checker,
3527
- provenance,
3528
- diagnostics
3529
- );
3530
- if (literalIdentityValue !== void 0) {
3531
- return literalIdentityValue;
3692
+ for (const identityPropertyName of getDiscriminatorIdentityPropertyNames(fieldName)) {
3693
+ const literalIdentityValue = resolveLiteralDiscriminatorPropertyValue(
3694
+ boundType,
3695
+ identityPropertyName,
3696
+ checker,
3697
+ provenance,
3698
+ diagnostics
3699
+ );
3700
+ if (literalIdentityValue === null) {
3701
+ return null;
3702
+ }
3703
+ if (literalIdentityValue !== void 0) {
3704
+ return literalIdentityValue;
3705
+ }
3532
3706
  }
3533
3707
  const apiName = resolveDiscriminatorApiName(boundType, checker, metadataPolicy);
3534
3708
  if (apiName?.source === "explicit") {
3535
- return apiName.value;
3709
+ return applyDiscriminatorApiNamePrefix(apiName.value, metadataPolicy.discriminator);
3536
3710
  }
3537
3711
  if (apiName?.source === "inferred") {
3538
- return apiName.value;
3712
+ return applyDiscriminatorApiNamePrefix(apiName.value, metadataPolicy.discriminator);
3539
3713
  }
3540
3714
  diagnostics.push(
3541
3715
  makeAnalysisDiagnostic(
@@ -3598,15 +3772,20 @@ function buildInstantiatedReferenceName(baseName, typeArguments, checker) {
3598
3772
  return renderedArguments.length === 0 ? baseName : `${baseName}__${renderedArguments.join("__")}`;
3599
3773
  }
3600
3774
  function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy, extensionRegistry, diagnostics) {
3601
- const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
3602
- if (typeNode === void 0) {
3775
+ const sourceTypeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
3776
+ if (sourceTypeNode === void 0) {
3603
3777
  return [];
3604
3778
  }
3605
- const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
3606
- if (!ts3.isTypeReferenceNode(resolvedTypeNode) || resolvedTypeNode.typeArguments === void 0) {
3779
+ const unwrapParentheses = (typeNode) => ts3.isParenthesizedTypeNode(typeNode) ? unwrapParentheses(typeNode.type) : typeNode;
3780
+ const directTypeNode = unwrapParentheses(sourceTypeNode);
3781
+ const referenceTypeNode = ts3.isTypeReferenceNode(directTypeNode) ? directTypeNode : (() => {
3782
+ const resolvedTypeNode = resolveAliasedTypeNode(directTypeNode, checker);
3783
+ return ts3.isTypeReferenceNode(resolvedTypeNode) ? resolvedTypeNode : null;
3784
+ })();
3785
+ if (referenceTypeNode?.typeArguments === void 0) {
3607
3786
  return [];
3608
3787
  }
3609
- return resolvedTypeNode.typeArguments.map((argumentNode) => {
3788
+ return referenceTypeNode.typeArguments.map((argumentNode) => {
3610
3789
  const argumentType = checker.getTypeFromTypeNode(argumentNode);
3611
3790
  return {
3612
3791
  tsType: argumentType,
@@ -3694,12 +3873,20 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
3694
3873
  annotations.push(defaultAnnotation);
3695
3874
  }
3696
3875
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
3697
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
3876
+ const metadata = resolveNodeMetadata(
3877
+ metadataPolicy,
3878
+ "field",
3879
+ name,
3880
+ prop,
3698
3881
  checker,
3699
- declaration: prop,
3700
- subjectType: tsType,
3701
- hostType
3702
- });
3882
+ extensionRegistry,
3883
+ {
3884
+ checker,
3885
+ declaration: prop,
3886
+ subjectType: tsType,
3887
+ hostType
3888
+ }
3889
+ );
3703
3890
  return {
3704
3891
  kind: "field",
3705
3892
  name,
@@ -3712,10 +3899,10 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
3712
3899
  };
3713
3900
  }
3714
3901
  function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
3715
- if (!ts3.isIdentifier(prop.name)) {
3902
+ const name = getAnalyzableObjectLikePropertyName(prop.name);
3903
+ if (name === null) {
3716
3904
  return null;
3717
3905
  }
3718
- const name = prop.name.text;
3719
3906
  const tsType = checker.getTypeAtLocation(prop);
3720
3907
  const optional = prop.questionToken !== void 0;
3721
3908
  const provenance = provenanceForNode(prop, file);
@@ -3746,12 +3933,20 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
3746
3933
  let annotations = [];
3747
3934
  annotations.push(...docResult.annotations);
3748
3935
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
3749
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
3936
+ const metadata = resolveNodeMetadata(
3937
+ metadataPolicy,
3938
+ "field",
3939
+ name,
3940
+ prop,
3750
3941
  checker,
3751
- declaration: prop,
3752
- subjectType: tsType,
3753
- hostType
3754
- });
3942
+ extensionRegistry,
3943
+ {
3944
+ checker,
3945
+ declaration: prop,
3946
+ subjectType: tsType,
3947
+ hostType
3948
+ }
3949
+ );
3755
3950
  return {
3756
3951
  kind: "field",
3757
3952
  name,
@@ -3763,6 +3958,31 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
3763
3958
  provenance
3764
3959
  };
3765
3960
  }
3961
+ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
3962
+ const seen = /* @__PURE__ */ new Set();
3963
+ const duplicates = /* @__PURE__ */ new Set();
3964
+ for (const member of members) {
3965
+ if (!ts3.isPropertySignature(member)) {
3966
+ continue;
3967
+ }
3968
+ const name = getAnalyzableObjectLikePropertyName(member.name);
3969
+ if (name === null) {
3970
+ continue;
3971
+ }
3972
+ if (seen.has(name)) {
3973
+ duplicates.add(name);
3974
+ continue;
3975
+ }
3976
+ seen.add(name);
3977
+ }
3978
+ return [...duplicates].sort();
3979
+ }
3980
+ function getAnalyzableObjectLikePropertyName(name) {
3981
+ if (!ts3.isIdentifier(name)) {
3982
+ return null;
3983
+ }
3984
+ return name.text;
3985
+ }
3766
3986
  function applyEnumMemberDisplayNames(type, annotations) {
3767
3987
  if (!annotations.some(
3768
3988
  (annotation) => annotation.annotationKind === "displayName" && annotation.value.trim().startsWith(":")
@@ -3880,7 +4100,7 @@ function getTypeNodeRegistrationName(typeNode) {
3880
4100
  }
3881
4101
  return null;
3882
4102
  }
3883
- function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4103
+ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3884
4104
  const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
3885
4105
  if (customType) {
3886
4106
  return customType;
@@ -3955,6 +4175,23 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
3955
4175
  diagnostics
3956
4176
  );
3957
4177
  }
4178
+ if (isIntersectionType(type)) {
4179
+ const sourceTypeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
4180
+ const resolvedSourceTypeNode = sourceTypeNode === void 0 ? void 0 : resolveAliasedTypeNode(sourceTypeNode, checker);
4181
+ if (resolvedSourceTypeNode !== void 0 && getObjectLikeTypeAliasMembers(resolvedSourceTypeNode) !== null) {
4182
+ return resolveObjectType(
4183
+ type,
4184
+ checker,
4185
+ file,
4186
+ typeRegistry,
4187
+ visiting,
4188
+ sourceNode,
4189
+ metadataPolicy,
4190
+ extensionRegistry,
4191
+ diagnostics
4192
+ );
4193
+ }
4194
+ }
3958
4195
  if (isObjectType(type)) {
3959
4196
  return resolveObjectType(
3960
4197
  type,
@@ -3970,7 +4207,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
3970
4207
  }
3971
4208
  return { kind: "primitive", primitiveKind: "string" };
3972
4209
  }
3973
- function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4210
+ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3974
4211
  if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
3975
4212
  return null;
3976
4213
  }
@@ -3990,11 +4227,19 @@ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiti
3990
4227
  file,
3991
4228
  makeParseOptions(extensionRegistry)
3992
4229
  );
3993
- const metadata = resolveNodeMetadata(metadataPolicy, "type", aliasName, aliasDecl, {
4230
+ const metadata = resolveNodeMetadata(
4231
+ metadataPolicy,
4232
+ "type",
4233
+ aliasName,
4234
+ aliasDecl,
3994
4235
  checker,
3995
- declaration: aliasDecl,
3996
- subjectType: aliasType
3997
- });
4236
+ extensionRegistry,
4237
+ {
4238
+ checker,
4239
+ declaration: aliasDecl,
4240
+ subjectType: aliasType
4241
+ }
4242
+ );
3998
4243
  typeRegistry[aliasName] = {
3999
4244
  name: aliasName,
4000
4245
  ...metadata !== void 0 && { metadata },
@@ -4033,7 +4278,7 @@ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
4033
4278
  const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
4034
4279
  return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
4035
4280
  }
4036
- function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4281
+ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4037
4282
  const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
4038
4283
  if (nestedAliasDecl !== void 0) {
4039
4284
  return resolveAliasedPrimitiveTarget(
@@ -4059,7 +4304,7 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
4059
4304
  diagnostics
4060
4305
  );
4061
4306
  }
4062
- function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4307
+ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4063
4308
  const typeName = getNamedTypeName(type);
4064
4309
  const namedDecl = getNamedTypeDeclaration(type);
4065
4310
  if (typeName && typeName in typeRegistry) {
@@ -4094,11 +4339,19 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
4094
4339
  return result;
4095
4340
  }
4096
4341
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
4097
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", typeName, namedDecl, {
4342
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
4343
+ metadataPolicy,
4344
+ "type",
4345
+ typeName,
4346
+ namedDecl,
4098
4347
  checker,
4099
- declaration: namedDecl,
4100
- subjectType: type
4101
- }) : void 0;
4348
+ extensionRegistry,
4349
+ {
4350
+ checker,
4351
+ declaration: namedDecl,
4352
+ subjectType: type
4353
+ }
4354
+ ) : void 0;
4102
4355
  typeRegistry[typeName] = {
4103
4356
  name: typeName,
4104
4357
  ...metadata !== void 0 && { metadata },
@@ -4183,7 +4436,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
4183
4436
  }
4184
4437
  return registerNamed({ kind: "union", members });
4185
4438
  }
4186
- function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4439
+ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4187
4440
  const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
4188
4441
  const elementType = typeArgs?.[0];
4189
4442
  const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
@@ -4200,7 +4453,7 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNod
4200
4453
  ) : { kind: "primitive", primitiveKind: "string" };
4201
4454
  return { kind: "array", items };
4202
4455
  }
4203
- function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4456
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4204
4457
  if (type.getProperties().length > 0) {
4205
4458
  return null;
4206
4459
  }
@@ -4261,7 +4514,7 @@ function shouldEmitResolvedObjectProperty(property, declaration) {
4261
4514
  }
4262
4515
  return true;
4263
4516
  }
4264
- function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4517
+ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4265
4518
  const collectedDiagnostics = diagnostics ?? [];
4266
4519
  const typeName = getNamedTypeName(type);
4267
4520
  const namedTypeName = typeName ?? void 0;
@@ -4318,7 +4571,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4318
4571
  };
4319
4572
  }
4320
4573
  }
4321
- const recordNode = tryResolveRecordType(
4574
+ const recordNode = isObjectType(type) ? tryResolveRecordType(
4322
4575
  type,
4323
4576
  checker,
4324
4577
  file,
@@ -4327,7 +4580,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4327
4580
  metadataPolicy,
4328
4581
  extensionRegistry,
4329
4582
  collectedDiagnostics
4330
- );
4583
+ ) : null;
4331
4584
  if (recordNode) {
4332
4585
  visiting.delete(type);
4333
4586
  if (registryTypeName !== void 0 && shouldRegisterNamedType) {
@@ -4337,11 +4590,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4337
4590
  return recordNode;
4338
4591
  }
4339
4592
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
4340
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
4593
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
4594
+ metadataPolicy,
4595
+ "type",
4596
+ registryTypeName,
4597
+ namedDecl,
4341
4598
  checker,
4342
- declaration: namedDecl,
4343
- subjectType: type
4344
- }) : void 0;
4599
+ extensionRegistry,
4600
+ {
4601
+ checker,
4602
+ declaration: namedDecl,
4603
+ subjectType: type
4604
+ }
4605
+ ) : void 0;
4345
4606
  typeRegistry[registryTypeName] = {
4346
4607
  name: registryTypeName,
4347
4608
  ...metadata !== void 0 && { metadata },
@@ -4437,11 +4698,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
4437
4698
  };
4438
4699
  if (registryTypeName !== void 0 && shouldRegisterNamedType) {
4439
4700
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
4440
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
4701
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
4702
+ metadataPolicy,
4703
+ "type",
4704
+ registryTypeName,
4705
+ namedDecl,
4441
4706
  checker,
4442
- declaration: namedDecl,
4443
- subjectType: type
4444
- }) : void 0;
4707
+ extensionRegistry,
4708
+ {
4709
+ checker,
4710
+ declaration: namedDecl,
4711
+ subjectType: type
4712
+ }
4713
+ ) : void 0;
4445
4714
  typeRegistry[registryTypeName] = {
4446
4715
  name: registryTypeName,
4447
4716
  ...metadata !== void 0 && { metadata },
@@ -4508,9 +4777,10 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
4508
4777
  );
4509
4778
  }
4510
4779
  const typeAliasDecl = declarations.find(ts3.isTypeAliasDeclaration);
4511
- if (typeAliasDecl && ts3.isTypeLiteralNode(typeAliasDecl.type)) {
4780
+ const typeAliasMembers = typeAliasDecl === void 0 ? null : getObjectLikeTypeAliasMembers(typeAliasDecl.type);
4781
+ if (typeAliasDecl && typeAliasMembers !== null) {
4512
4782
  return buildFieldNodeInfoMap(
4513
- typeAliasDecl.type.members,
4783
+ typeAliasMembers,
4514
4784
  checker,
4515
4785
  file,
4516
4786
  typeRegistry,
@@ -4817,17 +5087,18 @@ function findInterfaceByName(sourceFile, interfaceName) {
4817
5087
  function findTypeAliasByName(sourceFile, aliasName) {
4818
5088
  return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
4819
5089
  }
4820
- function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy) {
5090
+ function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
4821
5091
  const ctx = createProgramContext(filePath);
4822
5092
  return analyzeNamedTypeToIRFromProgramContext(
4823
5093
  ctx,
4824
5094
  filePath,
4825
5095
  typeName,
4826
5096
  extensionRegistry,
4827
- metadataPolicy
5097
+ metadataPolicy,
5098
+ discriminatorOptions
4828
5099
  );
4829
5100
  }
4830
- function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy) {
5101
+ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
4831
5102
  const analysisFilePath = path.resolve(filePath);
4832
5103
  const classDecl = findClassByName(ctx.sourceFile, typeName);
4833
5104
  if (classDecl !== null) {
@@ -4836,7 +5107,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
4836
5107
  ctx.checker,
4837
5108
  analysisFilePath,
4838
5109
  extensionRegistry,
4839
- metadataPolicy
5110
+ metadataPolicy,
5111
+ discriminatorOptions
4840
5112
  );
4841
5113
  }
4842
5114
  const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
@@ -4846,7 +5118,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
4846
5118
  ctx.checker,
4847
5119
  analysisFilePath,
4848
5120
  extensionRegistry,
4849
- metadataPolicy
5121
+ metadataPolicy,
5122
+ discriminatorOptions
4850
5123
  );
4851
5124
  }
4852
5125
  const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
@@ -4856,7 +5129,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
4856
5129
  ctx.checker,
4857
5130
  analysisFilePath,
4858
5131
  extensionRegistry,
4859
- metadataPolicy
5132
+ metadataPolicy,
5133
+ discriminatorOptions
4860
5134
  );
4861
5135
  if (result.ok) {
4862
5136
  return result.analysis;
@@ -5013,7 +5287,8 @@ function generateSchemasFromClass(options) {
5013
5287
  ctx.checker,
5014
5288
  options.filePath,
5015
5289
  options.extensionRegistry,
5016
- options.metadata
5290
+ options.metadata,
5291
+ options.discriminator
5017
5292
  );
5018
5293
  return generateClassSchemas(
5019
5294
  analysis,
@@ -5039,7 +5314,8 @@ function generateSchemasFromProgram(options) {
5039
5314
  options.filePath,
5040
5315
  options.typeName,
5041
5316
  options.extensionRegistry,
5042
- options.metadata
5317
+ options.metadata,
5318
+ options.discriminator
5043
5319
  );
5044
5320
  return generateClassSchemas(
5045
5321
  analysis,
@@ -5275,7 +5551,7 @@ function generateSchemasFromResolvedType(options, skipNamedDeclaration = false,
5275
5551
  typeRegistry,
5276
5552
  /* @__PURE__ */ new Set(),
5277
5553
  options.sourceNode,
5278
- normalizeMetadataPolicy(options.metadata),
5554
+ createAnalyzerMetadataPolicy(options.metadata, options.discriminator),
5279
5555
  options.extensionRegistry,
5280
5556
  diagnostics
5281
5557
  );
@@ -5324,7 +5600,8 @@ function generateSchemasFromDeclaration(options) {
5324
5600
  options.context.checker,
5325
5601
  filePath,
5326
5602
  options.extensionRegistry,
5327
- options.metadata
5603
+ options.metadata,
5604
+ options.discriminator
5328
5605
  ),
5329
5606
  filePath,
5330
5607
  options
@@ -5337,7 +5614,8 @@ function generateSchemasFromDeclaration(options) {
5337
5614
  options.context.checker,
5338
5615
  filePath,
5339
5616
  options.extensionRegistry,
5340
- options.metadata
5617
+ options.metadata,
5618
+ options.discriminator
5341
5619
  ),
5342
5620
  filePath,
5343
5621
  options
@@ -5349,7 +5627,8 @@ function generateSchemasFromDeclaration(options) {
5349
5627
  options.context.checker,
5350
5628
  filePath,
5351
5629
  options.extensionRegistry,
5352
- options.metadata
5630
+ options.metadata,
5631
+ options.discriminator
5353
5632
  );
5354
5633
  if (analyzedAlias.ok) {
5355
5634
  return generateSchemasFromAnalysis(analyzedAlias.analysis, filePath, options);
@@ -5424,7 +5703,8 @@ function buildMixedAuthoringSchemas(options) {
5424
5703
  filePath,
5425
5704
  typeName,
5426
5705
  schemaOptions.extensionRegistry,
5427
- schemaOptions.metadata
5706
+ schemaOptions.metadata,
5707
+ schemaOptions.discriminator
5428
5708
  );
5429
5709
  const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays, schemaOptions.metadata);
5430
5710
  const ir = canonicalizeTSDoc(