@emeryld/rrroutes-contract 2.7.6 → 2.7.8

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.mjs CHANGED
@@ -688,18 +688,18 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
688
688
  <title>Finalized Leaves Viewer</title>
689
689
  <style>
690
690
  :root {
691
- --bg: #f5f7fb;
692
- --surface: #ffffff;
693
- --border: #d6ddea;
694
- --text: #172033;
695
- --muted: #5b6680;
696
- --accent: #1858c6;
691
+ --bg: #212121;
692
+ --surface: #2a2a2a;
693
+ --border: #4a4a4a;
694
+ --text: #fffafa;
695
+ --muted: #c8c2c2;
696
+ --accent: #a764d3;
697
697
  }
698
698
  body {
699
699
  margin: 0;
700
700
  font-family: 'Iosevka Web', 'SFMono-Regular', Menlo, Consolas, monospace;
701
701
  color: var(--text);
702
- background: linear-gradient(180deg, var(--bg), #eef2fa);
702
+ background: var(--bg);
703
703
  }
704
704
  .wrap { max-width: 1100px; margin: 0 auto; padding: 20px; }
705
705
  .card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 14px; }
@@ -707,7 +707,7 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
707
707
  #results { margin-top: 12px; display: grid; gap: 8px; }
708
708
  details { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px; }
709
709
  summary { cursor: pointer; font-weight: 700; color: var(--accent); }
710
- pre { margin: 10px 0 0; overflow: auto; border: 1px solid var(--border); border-radius: 8px; padding: 10px; background: #fafcff; }
710
+ pre { margin: 10px 0 0; overflow: auto; border: 1px solid var(--border); border-radius: 8px; padding: 10px; background: #303030; color: var(--text); }
711
711
  </style>
712
712
  </head>
713
713
  <body>
@@ -738,6 +738,10 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
738
738
  return prefix + encodeURI(normalizedPath)
739
739
  }
740
740
 
741
+ const sourceDisplay = (source) => {
742
+ return ''
743
+ }
744
+
741
745
  payload.leaves.forEach((leaf) => {
742
746
  const details = document.createElement('details')
743
747
  const summary = document.createElement('summary')
@@ -759,13 +763,7 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
759
763
  link.href = definitionHref
760
764
  link.target = '_blank'
761
765
  link.rel = 'noopener noreferrer'
762
- link.textContent =
763
- 'definition: ' +
764
- source.definition.file +
765
- ':' +
766
- source.definition.line +
767
- ':' +
768
- source.definition.column
766
+ link.textContent = 'definition'
769
767
  label.appendChild(link)
770
768
  sourceWrap.appendChild(label)
771
769
  }
@@ -781,16 +779,7 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
781
779
  link.target = '_blank'
782
780
  link.rel = 'noopener noreferrer'
783
781
  link.textContent =
784
- name +
785
- ': ' +
786
- (schema.sourceName || schema.tag || '<anonymous>') +
787
- ' (' +
788
- schema.file +
789
- ':' +
790
- schema.line +
791
- ':' +
792
- schema.column +
793
- ')'
782
+ name + ': ' + (schema.sourceName || schema.tag || '<anonymous>')
794
783
  row.appendChild(link)
795
784
  } else {
796
785
  row.textContent = name + ': ' + (schema.sourceName || schema.tag || '<anonymous>')
@@ -824,6 +813,7 @@ var SCHEMA_KEYS = [
824
813
  "queryExtensionSchema"
825
814
  ];
826
815
  var HTTP_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete"]);
816
+ var MAX_RECURSION_DEPTH = 120;
827
817
  function toLocation(node) {
828
818
  const sourceFile = node.getSourceFile();
829
819
  const { line, character } = sourceFile.getLineAndCharacterOfPosition(
@@ -835,6 +825,10 @@ function toLocation(node) {
835
825
  column: character + 1
836
826
  };
837
827
  }
828
+ function markFile(ctx, sourceFile) {
829
+ if (!sourceFile) return;
830
+ ctx.visitedFilePaths.add(path.resolve(sourceFile.fileName));
831
+ }
838
832
  function trimPreview(text, max = 80) {
839
833
  const normalized = text.replace(/\s+/g, " ").trim();
840
834
  return normalized.length > max ? `${normalized.slice(0, max)}...` : normalized;
@@ -892,7 +886,7 @@ function findSourceFile(program, filePath) {
892
886
  const normalizedWanted = path.normalize(wanted);
893
887
  return program.getSourceFiles().find((file) => path.normalize(path.resolve(file.fileName)) === normalizedWanted);
894
888
  }
895
- function expressionFromDeclaration(declaration) {
889
+ function declarationToExpression(declaration) {
896
890
  if (!declaration) return void 0;
897
891
  if (ts.isVariableDeclaration(declaration)) {
898
892
  return declaration.initializer;
@@ -909,14 +903,95 @@ function expressionFromDeclaration(declaration) {
909
903
  if (ts.isBindingElement(declaration)) {
910
904
  return declaration.initializer;
911
905
  }
906
+ if (ts.isEnumMember(declaration)) {
907
+ return declaration.initializer;
908
+ }
912
909
  return void 0;
913
910
  }
914
- function resolveIdentifierExpression(identifier, ctx) {
915
- const symbol = ctx.checker.getSymbolAtLocation(identifier);
916
- if (!symbol) return void 0;
911
+ function symbolKey(symbol) {
912
+ const decl = symbol.declarations?.[0];
913
+ if (!decl) return `${symbol.getName()}#${symbol.flags}`;
914
+ const file = path.resolve(decl.getSourceFile().fileName);
915
+ return `${file}:${decl.getStart()}:${symbol.getName()}`;
916
+ }
917
+ function resolveSymbolFromNode(node, ctx) {
918
+ const symbol = ctx.checker.getSymbolAtLocation(node);
919
+ if (!symbol) {
920
+ ctx.unresolvedReferences += 1;
921
+ return void 0;
922
+ }
917
923
  const target = getAliasedSymbolIfNeeded(ctx.checker, symbol);
918
- const declaration = target.declarations?.[0];
919
- return expressionFromDeclaration(declaration);
924
+ const key = symbolKey(target);
925
+ ctx.visitedSymbolKeys.add(key);
926
+ return target;
927
+ }
928
+ function getExpressionFromSymbol(symbol, ctx, depth) {
929
+ const key = symbolKey(symbol);
930
+ if (ctx.activeSymbols.has(key)) return void 0;
931
+ if (depth > MAX_RECURSION_DEPTH) return void 0;
932
+ ctx.activeSymbols.add(key);
933
+ try {
934
+ const declaration = symbol.declarations?.[0];
935
+ markFile(ctx, declaration?.getSourceFile());
936
+ const direct = declarationToExpression(declaration);
937
+ if (direct) return direct;
938
+ if (declaration && ts.isImportSpecifier(declaration)) {
939
+ const target = getAliasedSymbolIfNeeded(ctx.checker, symbol);
940
+ if (target !== symbol) {
941
+ return getExpressionFromSymbol(target, ctx, depth + 1);
942
+ }
943
+ }
944
+ return void 0;
945
+ } finally {
946
+ ctx.activeSymbols.delete(key);
947
+ }
948
+ }
949
+ function resolveIdentifierExpression(identifier, ctx, depth) {
950
+ const symbol = resolveSymbolFromNode(identifier, ctx);
951
+ if (!symbol) return void 0;
952
+ return getExpressionFromSymbol(symbol, ctx, depth);
953
+ }
954
+ function resolvePropertyExpression(expression, ctx, depth) {
955
+ if (depth > MAX_RECURSION_DEPTH) return void 0;
956
+ if (ts.isPropertyAccessExpression(expression)) {
957
+ const symbol = resolveSymbolFromNode(expression.name, ctx);
958
+ if (symbol) {
959
+ const fromSymbol = getExpressionFromSymbol(symbol, ctx, depth + 1);
960
+ if (fromSymbol) return fromSymbol;
961
+ }
962
+ } else if (expression.argumentExpression) {
963
+ const symbol = resolveSymbolFromNode(expression.argumentExpression, ctx);
964
+ if (symbol) {
965
+ const fromSymbol = getExpressionFromSymbol(symbol, ctx, depth + 1);
966
+ if (fromSymbol) return fromSymbol;
967
+ }
968
+ }
969
+ const ownerExpr = evaluateExpressionReference(expression.expression, ctx, depth + 1);
970
+ const owner = ownerExpr ? maybeObjectLiteral(ownerExpr, ctx, depth + 1) : void 0;
971
+ if (!owner) return void 0;
972
+ const propName = ts.isPropertyAccessExpression(expression) ? expression.name.text : expression.argumentExpression && ts.isStringLiteralLike(expression.argumentExpression) ? expression.argumentExpression.text : void 0;
973
+ if (!propName) return void 0;
974
+ for (const property of owner.properties) {
975
+ if (ts.isPropertyAssignment(property) && getTextName(property.name) === propName) {
976
+ return property.initializer;
977
+ }
978
+ if (ts.isShorthandPropertyAssignment(property) && property.name.text === propName) {
979
+ return property.name;
980
+ }
981
+ }
982
+ return void 0;
983
+ }
984
+ function evaluateExpressionReference(expression, ctx, depth) {
985
+ const resolved = unwrapExpression(expression);
986
+ markFile(ctx, resolved.getSourceFile());
987
+ if (depth > MAX_RECURSION_DEPTH) return void 0;
988
+ if (ts.isIdentifier(resolved)) {
989
+ return resolveIdentifierExpression(resolved, ctx, depth + 1);
990
+ }
991
+ if (ts.isPropertyAccessExpression(resolved) || ts.isElementAccessExpression(resolved)) {
992
+ return resolvePropertyExpression(resolved, ctx, depth + 1);
993
+ }
994
+ return void 0;
920
995
  }
921
996
  function resolveExportExpression(sourceFile, exportName, checker) {
922
997
  const moduleSymbol = getModuleSymbol(checker, sourceFile);
@@ -925,12 +1000,12 @@ function resolveExportExpression(sourceFile, exportName, checker) {
925
1000
  const explicit = exports.find((entry) => entry.getName() === exportName);
926
1001
  if (explicit) {
927
1002
  const declaration = getAliasedSymbolIfNeeded(checker, explicit).declarations?.[0];
928
- return expressionFromDeclaration(declaration);
1003
+ return declarationToExpression(declaration);
929
1004
  }
930
1005
  const defaultExport = exports.find((entry) => entry.getName() === "default");
931
1006
  if (!defaultExport) return void 0;
932
1007
  const defaultDecl = getAliasedSymbolIfNeeded(checker, defaultExport).declarations?.[0];
933
- const defaultExpr = expressionFromDeclaration(defaultDecl);
1008
+ const defaultExpr = declarationToExpression(defaultDecl);
934
1009
  if (!defaultExpr) return void 0;
935
1010
  const resolved = unwrapExpression(defaultExpr);
936
1011
  if (!ts.isObjectLiteralExpression(resolved)) return void 0;
@@ -942,24 +1017,25 @@ function resolveExportExpression(sourceFile, exportName, checker) {
942
1017
  }
943
1018
  return void 0;
944
1019
  }
945
- function maybeObjectLiteral(expression, ctx) {
946
- if (!expression) return void 0;
1020
+ function maybeObjectLiteral(expression, ctx, depth = 0) {
1021
+ if (!expression || depth > MAX_RECURSION_DEPTH) return void 0;
947
1022
  const resolved = unwrapExpression(expression);
1023
+ markFile(ctx, resolved.getSourceFile());
948
1024
  if (ts.isObjectLiteralExpression(resolved)) return resolved;
949
- if (ts.isIdentifier(resolved)) {
950
- const target = resolveIdentifierExpression(resolved, ctx);
951
- if (!target) return void 0;
952
- return maybeObjectLiteral(target, ctx);
953
- }
954
- return void 0;
1025
+ const referenced = evaluateExpressionReference(resolved, ctx, depth + 1);
1026
+ if (!referenced) return void 0;
1027
+ return maybeObjectLiteral(referenced, ctx, depth + 1);
955
1028
  }
956
- function collectSchemaExpressionsFromObject(objectLiteral, ctx) {
1029
+ function collectSchemaExpressionsFromObject(objectLiteral, ctx, depth) {
957
1030
  const schemas = {};
958
1031
  for (const property of objectLiteral.properties) {
959
1032
  if (ts.isSpreadAssignment(property)) {
960
- const spreadObject = maybeObjectLiteral(property.expression, ctx);
1033
+ const spreadObject = maybeObjectLiteral(property.expression, ctx, depth + 1);
961
1034
  if (!spreadObject) continue;
962
- Object.assign(schemas, collectSchemaExpressionsFromObject(spreadObject, ctx));
1035
+ Object.assign(
1036
+ schemas,
1037
+ collectSchemaExpressionsFromObject(spreadObject, ctx, depth + 1)
1038
+ );
963
1039
  continue;
964
1040
  }
965
1041
  if (ts.isPropertyAssignment(property)) {
@@ -976,10 +1052,10 @@ function collectSchemaExpressionsFromObject(objectLiteral, ctx) {
976
1052
  }
977
1053
  return schemas;
978
1054
  }
979
- function extractSchemaExpressions(cfgExpression, ctx) {
980
- const objectLiteral = maybeObjectLiteral(cfgExpression, ctx);
1055
+ function extractSchemaExpressions(cfgExpression, ctx, depth) {
1056
+ const objectLiteral = maybeObjectLiteral(cfgExpression, ctx, depth);
981
1057
  if (!objectLiteral) return {};
982
- return collectSchemaExpressionsFromObject(objectLiteral, ctx);
1058
+ return collectSchemaExpressionsFromObject(objectLiteral, ctx, depth);
983
1059
  }
984
1060
  function getNearestVariableName(node) {
985
1061
  let current = node;
@@ -991,31 +1067,45 @@ function getNearestVariableName(node) {
991
1067
  }
992
1068
  return void 0;
993
1069
  }
994
- function evaluateBranchExpression(expression, ctx) {
1070
+ function isAllAccess(expression) {
1071
+ if (ts.isPropertyAccessExpression(expression)) {
1072
+ return expression.name.text === "all";
1073
+ }
1074
+ return Boolean(
1075
+ expression.argumentExpression && ts.isStringLiteralLike(expression.argumentExpression) && expression.argumentExpression.text === "all"
1076
+ );
1077
+ }
1078
+ function evaluateBranchExpression(expression, ctx, depth) {
995
1079
  const resolved = unwrapExpression(expression);
1080
+ markFile(ctx, resolved.getSourceFile());
1081
+ if (depth > MAX_RECURSION_DEPTH) return void 0;
996
1082
  if (ts.isIdentifier(resolved)) {
997
- const valueExpr = resolveIdentifierExpression(resolved, ctx);
1083
+ const valueExpr = resolveIdentifierExpression(resolved, ctx, depth + 1);
998
1084
  if (!valueExpr) return void 0;
999
- return evaluateBranchExpression(valueExpr, ctx);
1085
+ return evaluateBranchExpression(valueExpr, ctx, depth + 1);
1000
1086
  }
1001
1087
  if (!ts.isCallExpression(resolved)) return void 0;
1002
1088
  const call = resolved;
1003
1089
  if (ts.isIdentifier(call.expression) && call.expression.text === "resource") {
1004
1090
  const firstArg = call.arguments[0];
1005
- if (!firstArg || !ts.isStringLiteralLike(firstArg)) return void 0;
1091
+ if (!firstArg || !ts.isStringLiteralLike(firstArg)) {
1092
+ ctx.unsupportedShapeSeen = true;
1093
+ return void 0;
1094
+ }
1006
1095
  return { base: normalizeResourceBase(firstArg.text), leaves: [] };
1007
1096
  }
1008
- if (!ts.isPropertyAccessExpression(call.expression)) return void 0;
1097
+ if (!ts.isPropertyAccessExpression(call.expression)) {
1098
+ ctx.unsupportedShapeSeen = true;
1099
+ return void 0;
1100
+ }
1009
1101
  const owner = call.expression.expression;
1010
1102
  const method = call.expression.name.text;
1011
- const branch = evaluateBranchExpression(owner, ctx);
1103
+ const branch = evaluateBranchExpression(owner, ctx, depth + 1);
1012
1104
  if (!branch) return void 0;
1013
- if (method === "with") {
1014
- return branch;
1015
- }
1105
+ if (method === "with") return branch;
1016
1106
  if (HTTP_METHODS.has(method)) {
1017
1107
  const cfgExpression = call.arguments[0];
1018
- const schemas = extractSchemaExpressions(cfgExpression, ctx);
1108
+ const schemas = extractSchemaExpressions(cfgExpression, ctx, depth + 1);
1019
1109
  const nextLeaf = {
1020
1110
  method,
1021
1111
  path: branch.base,
@@ -1029,7 +1119,7 @@ function evaluateBranchExpression(expression, ctx) {
1029
1119
  }
1030
1120
  if (method === "sub") {
1031
1121
  const mountedLeaves = call.arguments.flatMap(
1032
- (arg) => evaluateLeavesFromExpression(arg, ctx)
1122
+ (arg) => evaluateLeavesFromExpression(arg, ctx, depth + 1)
1033
1123
  );
1034
1124
  const prefixed = mountedLeaves.map((leaf) => ({
1035
1125
  ...leaf,
@@ -1040,29 +1130,44 @@ function evaluateBranchExpression(expression, ctx) {
1040
1130
  leaves: [...branch.leaves, ...prefixed]
1041
1131
  };
1042
1132
  }
1133
+ ctx.unsupportedShapeSeen = true;
1043
1134
  return void 0;
1044
1135
  }
1045
- function evaluateLeavesFromExpression(expression, ctx) {
1136
+ function expressionKey(expression) {
1137
+ const source = expression.getSourceFile();
1138
+ return `${path.resolve(source.fileName)}:${expression.getStart(source)}:${expression.getEnd()}:${expression.kind}`;
1139
+ }
1140
+ function evaluateLeavesFromExpression(expression, ctx, depth = 0) {
1046
1141
  const resolved = unwrapExpression(expression);
1047
- const key = resolved.getStart(resolved.getSourceFile());
1048
- if (ctx.visitedExpressionStarts.has(key)) {
1049
- return [];
1050
- }
1051
- ctx.visitedExpressionStarts.add(key);
1142
+ markFile(ctx, resolved.getSourceFile());
1143
+ const key = expressionKey(resolved);
1144
+ if (ctx.activeExpressionKeys.has(key)) return [];
1145
+ if (depth > MAX_RECURSION_DEPTH) return [];
1146
+ ctx.activeExpressionKeys.add(key);
1052
1147
  try {
1053
1148
  if (ts.isIdentifier(resolved)) {
1054
- const valueExpr = resolveIdentifierExpression(resolved, ctx);
1149
+ const valueExpr = resolveIdentifierExpression(resolved, ctx, depth + 1);
1055
1150
  if (!valueExpr) return [];
1056
- return evaluateLeavesFromExpression(valueExpr, ctx);
1151
+ return evaluateLeavesFromExpression(valueExpr, ctx, depth + 1);
1152
+ }
1153
+ if (ts.isPropertyAccessExpression(resolved) || ts.isElementAccessExpression(resolved)) {
1154
+ if (isAllAccess(resolved)) {
1155
+ return evaluateLeavesFromExpression(resolved.expression, ctx, depth + 1);
1156
+ }
1157
+ const refExpr = resolvePropertyExpression(resolved, ctx, depth + 1);
1158
+ if (refExpr) {
1159
+ return evaluateLeavesFromExpression(refExpr, ctx, depth + 1);
1160
+ }
1161
+ return [];
1057
1162
  }
1058
1163
  if (ts.isArrayLiteralExpression(resolved)) {
1059
1164
  const leaves = [];
1060
1165
  for (const element of resolved.elements) {
1061
1166
  if (ts.isSpreadElement(element)) {
1062
- leaves.push(...evaluateLeavesFromExpression(element.expression, ctx));
1167
+ leaves.push(...evaluateLeavesFromExpression(element.expression, ctx, depth + 1));
1063
1168
  continue;
1064
1169
  }
1065
- leaves.push(...evaluateLeavesFromExpression(element, ctx));
1170
+ leaves.push(...evaluateLeavesFromExpression(element, ctx, depth + 1));
1066
1171
  }
1067
1172
  return leaves;
1068
1173
  }
@@ -1072,34 +1177,43 @@ function evaluateLeavesFromExpression(expression, ctx) {
1072
1177
  if (callName === "finalize") {
1073
1178
  const arg = resolved.arguments[0];
1074
1179
  if (!arg) return [];
1075
- return evaluateLeavesFromExpression(arg, ctx);
1180
+ return evaluateLeavesFromExpression(arg, ctx, depth + 1);
1076
1181
  }
1077
1182
  if (callName === "mergeArrays") {
1078
1183
  return resolved.arguments.flatMap(
1079
- (arg) => evaluateLeavesFromExpression(arg, ctx)
1184
+ (arg) => evaluateLeavesFromExpression(arg, ctx, depth + 1)
1080
1185
  );
1081
1186
  }
1082
1187
  }
1083
1188
  if (ts.isPropertyAccessExpression(resolved.expression)) {
1084
1189
  const prop = resolved.expression.name.text;
1085
1190
  if (prop === "done") {
1086
- const branch2 = evaluateBranchExpression(resolved.expression.expression, ctx);
1191
+ const branch2 = evaluateBranchExpression(
1192
+ resolved.expression.expression,
1193
+ ctx,
1194
+ depth + 1
1195
+ );
1087
1196
  return branch2?.leaves ?? [];
1088
1197
  }
1089
1198
  }
1199
+ const refExpr = evaluateExpressionReference(resolved, ctx, depth + 1);
1200
+ if (refExpr) return evaluateLeavesFromExpression(refExpr, ctx, depth + 1);
1201
+ ctx.unsupportedShapeSeen = true;
1202
+ return [];
1090
1203
  }
1091
- const branch = evaluateBranchExpression(resolved, ctx);
1092
- return branch?.leaves ?? [];
1204
+ const branch = evaluateBranchExpression(resolved, ctx, depth + 1);
1205
+ if (branch) return branch.leaves;
1206
+ ctx.unsupportedShapeSeen = true;
1207
+ return [];
1093
1208
  } finally {
1094
- ctx.visitedExpressionStarts.delete(key);
1209
+ ctx.activeExpressionKeys.delete(key);
1095
1210
  }
1096
1211
  }
1097
1212
  function resolveSchemaMetadata(expression, ctx) {
1098
1213
  const resolved = unwrapExpression(expression);
1099
1214
  if (ts.isIdentifier(resolved)) {
1100
- const symbol = ctx.checker.getSymbolAtLocation(resolved);
1101
- const target = symbol ? getAliasedSymbolIfNeeded(ctx.checker, symbol) : void 0;
1102
- const declaration = target?.declarations?.[0];
1215
+ const symbol = resolveSymbolFromNode(resolved, ctx);
1216
+ const declaration = symbol?.declarations?.[0];
1103
1217
  const locationNode = declaration ? ts.isVariableDeclaration(declaration) ? declaration.name : declaration : resolved;
1104
1218
  const sourceName = declaration && ts.isVariableDeclaration(declaration) && ts.isIdentifier(declaration.name) ? declaration.name.text : resolved.text;
1105
1219
  return {
@@ -1137,13 +1251,25 @@ function parseTsConfig(cwd, tsconfigPath) {
1137
1251
  void 0,
1138
1252
  resolvedTsconfig
1139
1253
  );
1140
- if (parsed.errors.length > 0) {
1254
+ const nonEmptyInputErrors = parsed.errors.filter((entry) => entry.code !== 18003);
1255
+ if (nonEmptyInputErrors.length > 0) {
1141
1256
  throw new Error(
1142
- parsed.errors.map((entry) => ts.flattenDiagnosticMessageText(entry.messageText, "\n")).join("\n")
1257
+ nonEmptyInputErrors.map((entry) => ts.flattenDiagnosticMessageText(entry.messageText, "\n")).join("\n")
1143
1258
  );
1144
1259
  }
1145
1260
  return { resolvedTsconfig, parsed };
1146
1261
  }
1262
+ function createProgramWithFallback(parsed, moduleFileAbs) {
1263
+ const base = ts.createProgram({
1264
+ rootNames: parsed.fileNames,
1265
+ options: parsed.options
1266
+ });
1267
+ if (findSourceFile(base, moduleFileAbs)) {
1268
+ return base;
1269
+ }
1270
+ const rootNames = Array.from(/* @__PURE__ */ new Set([...parsed.fileNames, moduleFileAbs]));
1271
+ return ts.createProgram({ rootNames, options: parsed.options });
1272
+ }
1147
1273
  function extractLeafSourceByAst({
1148
1274
  modulePath,
1149
1275
  exportName,
@@ -1152,32 +1278,43 @@ function extractLeafSourceByAst({
1152
1278
  }) {
1153
1279
  const parsedConfig = parseTsConfig(cwd, tsconfigPath);
1154
1280
  if (!parsedConfig) {
1155
- return { sourceByLeaf: {} };
1281
+ return {
1282
+ sourceByLeaf: {},
1283
+ reason: "module_not_in_program",
1284
+ stats: { visitedSymbols: 0, visitedFiles: 0, unresolvedReferences: 0 }
1285
+ };
1156
1286
  }
1157
- const program = ts.createProgram({
1158
- rootNames: parsedConfig.parsed.fileNames,
1159
- options: parsedConfig.parsed.options
1160
- });
1287
+ const moduleAbs = path.resolve(cwd, modulePath);
1288
+ const program = createProgramWithFallback(parsedConfig.parsed, moduleAbs);
1161
1289
  const checker = program.getTypeChecker();
1162
- const moduleFile = findSourceFile(program, path.resolve(cwd, modulePath));
1290
+ const moduleFile = findSourceFile(program, moduleAbs);
1163
1291
  if (!moduleFile) {
1164
1292
  return {
1165
1293
  sourceByLeaf: {},
1166
- tsconfigPath: parsedConfig.resolvedTsconfig
1294
+ tsconfigPath: parsedConfig.resolvedTsconfig,
1295
+ reason: "module_not_in_program",
1296
+ stats: { visitedSymbols: 0, visitedFiles: 0, unresolvedReferences: 0 }
1167
1297
  };
1168
1298
  }
1169
1299
  const exportedExpression = resolveExportExpression(moduleFile, exportName, checker);
1170
1300
  if (!exportedExpression) {
1171
1301
  return {
1172
1302
  sourceByLeaf: {},
1173
- tsconfigPath: parsedConfig.resolvedTsconfig
1303
+ tsconfigPath: parsedConfig.resolvedTsconfig,
1304
+ reason: "export_not_found",
1305
+ stats: { visitedSymbols: 0, visitedFiles: 1, unresolvedReferences: 0 }
1174
1306
  };
1175
1307
  }
1176
1308
  const ctx = {
1177
1309
  checker,
1178
- visitedExpressionStarts: /* @__PURE__ */ new Set()
1310
+ activeExpressionKeys: /* @__PURE__ */ new Set(),
1311
+ activeSymbols: /* @__PURE__ */ new Set(),
1312
+ visitedSymbolKeys: /* @__PURE__ */ new Set(),
1313
+ visitedFilePaths: /* @__PURE__ */ new Set([path.resolve(moduleFile.fileName)]),
1314
+ unresolvedReferences: 0,
1315
+ unsupportedShapeSeen: false
1179
1316
  };
1180
- const evaluatedLeaves = evaluateLeavesFromExpression(exportedExpression, ctx);
1317
+ const evaluatedLeaves = evaluateLeavesFromExpression(exportedExpression, ctx, 0);
1181
1318
  const sourceByLeaf = {};
1182
1319
  for (const leaf of evaluatedLeaves) {
1183
1320
  const key = buildLeafKey(leaf.method, leaf.path);
@@ -1185,17 +1322,27 @@ function extractLeafSourceByAst({
1185
1322
  ...toLocation(leaf.definitionNode),
1186
1323
  symbolName: getNearestVariableName(leaf.definitionNode)
1187
1324
  };
1325
+ ctx.visitedFilePaths.add(definition.file);
1188
1326
  const schemas = {};
1189
1327
  for (const schemaKey of SCHEMA_KEYS) {
1190
1328
  const schemaExpression = leaf.schemas[schemaKey];
1191
1329
  if (!schemaExpression) continue;
1192
- schemas[schemaKey] = resolveSchemaMetadata(schemaExpression, ctx);
1330
+ const schemaMeta = resolveSchemaMetadata(schemaExpression, ctx);
1331
+ ctx.visitedFilePaths.add(schemaMeta.file);
1332
+ schemas[schemaKey] = schemaMeta;
1193
1333
  }
1194
1334
  sourceByLeaf[key] = { definition, schemas };
1195
1335
  }
1336
+ const reason = Object.keys(sourceByLeaf).length > 0 ? void 0 : ctx.unsupportedShapeSeen ? "unsupported_expression_shape" : "resolved_zero_leaves";
1196
1337
  return {
1197
1338
  sourceByLeaf,
1198
- tsconfigPath: parsedConfig.resolvedTsconfig
1339
+ tsconfigPath: parsedConfig.resolvedTsconfig,
1340
+ reason,
1341
+ stats: {
1342
+ visitedSymbols: ctx.visitedSymbolKeys.size,
1343
+ visitedFiles: ctx.visitedFilePaths.size,
1344
+ unresolvedReferences: ctx.unresolvedReferences
1345
+ }
1199
1346
  };
1200
1347
  }
1201
1348
 
@@ -1378,7 +1525,9 @@ async function exportFinalizedLeaves(input, options = {}) {
1378
1525
  modulePath: path2.resolve(modulePath),
1379
1526
  exportName,
1380
1527
  tsconfigPath: extracted.tsconfigPath,
1381
- resolvedLeafCount: Object.keys(sourceByLeaf).length
1528
+ resolvedLeafCount: Object.keys(sourceByLeaf).length,
1529
+ reason: extracted.reason,
1530
+ stats: extracted.stats
1382
1531
  };
1383
1532
  } else {
1384
1533
  sourceExtraction = {
@@ -1386,7 +1535,13 @@ async function exportFinalizedLeaves(input, options = {}) {
1386
1535
  enabled: false,
1387
1536
  exportName,
1388
1537
  tsconfigPath: options.tsconfigPath ? path2.resolve(options.tsconfigPath) : void 0,
1389
- resolvedLeafCount: 0
1538
+ resolvedLeafCount: 0,
1539
+ reason: "resolved_zero_leaves",
1540
+ stats: {
1541
+ visitedSymbols: 0,
1542
+ visitedFiles: 0,
1543
+ unresolvedReferences: 0
1544
+ }
1390
1545
  };
1391
1546
  }
1392
1547
  }