@emeryld/rrroutes-contract 2.7.6 → 2.7.7

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
@@ -759,14 +759,18 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
759
759
  link.href = definitionHref
760
760
  link.target = '_blank'
761
761
  link.rel = 'noopener noreferrer'
762
- link.textContent =
763
- 'definition: ' +
762
+ link.textContent = 'definition'
763
+ label.appendChild(link)
764
+ const location = document.createElement('span')
765
+ location.textContent =
766
+ ' (' +
764
767
  source.definition.file +
765
768
  ':' +
766
769
  source.definition.line +
767
770
  ':' +
768
- source.definition.column
769
- label.appendChild(link)
771
+ source.definition.column +
772
+ ')'
773
+ label.appendChild(location)
770
774
  sourceWrap.appendChild(label)
771
775
  }
772
776
 
@@ -781,17 +785,12 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
781
785
  link.target = '_blank'
782
786
  link.rel = 'noopener noreferrer'
783
787
  link.textContent =
784
- name +
785
- ': ' +
786
- (schema.sourceName || schema.tag || '<anonymous>') +
787
- ' (' +
788
- schema.file +
789
- ':' +
790
- schema.line +
791
- ':' +
792
- schema.column +
793
- ')'
788
+ name + ': ' + (schema.sourceName || schema.tag || '<anonymous>')
794
789
  row.appendChild(link)
790
+ const location = document.createElement('span')
791
+ location.textContent =
792
+ ' (' + schema.file + ':' + schema.line + ':' + schema.column + ')'
793
+ row.appendChild(location)
795
794
  } else {
796
795
  row.textContent = name + ': ' + (schema.sourceName || schema.tag || '<anonymous>')
797
796
  }
@@ -824,6 +823,7 @@ var SCHEMA_KEYS = [
824
823
  "queryExtensionSchema"
825
824
  ];
826
825
  var HTTP_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete"]);
826
+ var MAX_RECURSION_DEPTH = 120;
827
827
  function toLocation(node) {
828
828
  const sourceFile = node.getSourceFile();
829
829
  const { line, character } = sourceFile.getLineAndCharacterOfPosition(
@@ -835,6 +835,10 @@ function toLocation(node) {
835
835
  column: character + 1
836
836
  };
837
837
  }
838
+ function markFile(ctx, sourceFile) {
839
+ if (!sourceFile) return;
840
+ ctx.visitedFilePaths.add(path.resolve(sourceFile.fileName));
841
+ }
838
842
  function trimPreview(text, max = 80) {
839
843
  const normalized = text.replace(/\s+/g, " ").trim();
840
844
  return normalized.length > max ? `${normalized.slice(0, max)}...` : normalized;
@@ -892,7 +896,7 @@ function findSourceFile(program, filePath) {
892
896
  const normalizedWanted = path.normalize(wanted);
893
897
  return program.getSourceFiles().find((file) => path.normalize(path.resolve(file.fileName)) === normalizedWanted);
894
898
  }
895
- function expressionFromDeclaration(declaration) {
899
+ function declarationToExpression(declaration) {
896
900
  if (!declaration) return void 0;
897
901
  if (ts.isVariableDeclaration(declaration)) {
898
902
  return declaration.initializer;
@@ -909,14 +913,95 @@ function expressionFromDeclaration(declaration) {
909
913
  if (ts.isBindingElement(declaration)) {
910
914
  return declaration.initializer;
911
915
  }
916
+ if (ts.isEnumMember(declaration)) {
917
+ return declaration.initializer;
918
+ }
912
919
  return void 0;
913
920
  }
914
- function resolveIdentifierExpression(identifier, ctx) {
915
- const symbol = ctx.checker.getSymbolAtLocation(identifier);
916
- if (!symbol) return void 0;
921
+ function symbolKey(symbol) {
922
+ const decl = symbol.declarations?.[0];
923
+ if (!decl) return `${symbol.getName()}#${symbol.flags}`;
924
+ const file = path.resolve(decl.getSourceFile().fileName);
925
+ return `${file}:${decl.getStart()}:${symbol.getName()}`;
926
+ }
927
+ function resolveSymbolFromNode(node, ctx) {
928
+ const symbol = ctx.checker.getSymbolAtLocation(node);
929
+ if (!symbol) {
930
+ ctx.unresolvedReferences += 1;
931
+ return void 0;
932
+ }
917
933
  const target = getAliasedSymbolIfNeeded(ctx.checker, symbol);
918
- const declaration = target.declarations?.[0];
919
- return expressionFromDeclaration(declaration);
934
+ const key = symbolKey(target);
935
+ ctx.visitedSymbolKeys.add(key);
936
+ return target;
937
+ }
938
+ function getExpressionFromSymbol(symbol, ctx, depth) {
939
+ const key = symbolKey(symbol);
940
+ if (ctx.activeSymbols.has(key)) return void 0;
941
+ if (depth > MAX_RECURSION_DEPTH) return void 0;
942
+ ctx.activeSymbols.add(key);
943
+ try {
944
+ const declaration = symbol.declarations?.[0];
945
+ markFile(ctx, declaration?.getSourceFile());
946
+ const direct = declarationToExpression(declaration);
947
+ if (direct) return direct;
948
+ if (declaration && ts.isImportSpecifier(declaration)) {
949
+ const target = getAliasedSymbolIfNeeded(ctx.checker, symbol);
950
+ if (target !== symbol) {
951
+ return getExpressionFromSymbol(target, ctx, depth + 1);
952
+ }
953
+ }
954
+ return void 0;
955
+ } finally {
956
+ ctx.activeSymbols.delete(key);
957
+ }
958
+ }
959
+ function resolveIdentifierExpression(identifier, ctx, depth) {
960
+ const symbol = resolveSymbolFromNode(identifier, ctx);
961
+ if (!symbol) return void 0;
962
+ return getExpressionFromSymbol(symbol, ctx, depth);
963
+ }
964
+ function resolvePropertyExpression(expression, ctx, depth) {
965
+ if (depth > MAX_RECURSION_DEPTH) return void 0;
966
+ if (ts.isPropertyAccessExpression(expression)) {
967
+ const symbol = resolveSymbolFromNode(expression.name, ctx);
968
+ if (symbol) {
969
+ const fromSymbol = getExpressionFromSymbol(symbol, ctx, depth + 1);
970
+ if (fromSymbol) return fromSymbol;
971
+ }
972
+ } else if (expression.argumentExpression) {
973
+ const symbol = resolveSymbolFromNode(expression.argumentExpression, ctx);
974
+ if (symbol) {
975
+ const fromSymbol = getExpressionFromSymbol(symbol, ctx, depth + 1);
976
+ if (fromSymbol) return fromSymbol;
977
+ }
978
+ }
979
+ const ownerExpr = evaluateExpressionReference(expression.expression, ctx, depth + 1);
980
+ const owner = ownerExpr ? maybeObjectLiteral(ownerExpr, ctx, depth + 1) : void 0;
981
+ if (!owner) return void 0;
982
+ const propName = ts.isPropertyAccessExpression(expression) ? expression.name.text : expression.argumentExpression && ts.isStringLiteralLike(expression.argumentExpression) ? expression.argumentExpression.text : void 0;
983
+ if (!propName) return void 0;
984
+ for (const property of owner.properties) {
985
+ if (ts.isPropertyAssignment(property) && getTextName(property.name) === propName) {
986
+ return property.initializer;
987
+ }
988
+ if (ts.isShorthandPropertyAssignment(property) && property.name.text === propName) {
989
+ return property.name;
990
+ }
991
+ }
992
+ return void 0;
993
+ }
994
+ function evaluateExpressionReference(expression, ctx, depth) {
995
+ const resolved = unwrapExpression(expression);
996
+ markFile(ctx, resolved.getSourceFile());
997
+ if (depth > MAX_RECURSION_DEPTH) return void 0;
998
+ if (ts.isIdentifier(resolved)) {
999
+ return resolveIdentifierExpression(resolved, ctx, depth + 1);
1000
+ }
1001
+ if (ts.isPropertyAccessExpression(resolved) || ts.isElementAccessExpression(resolved)) {
1002
+ return resolvePropertyExpression(resolved, ctx, depth + 1);
1003
+ }
1004
+ return void 0;
920
1005
  }
921
1006
  function resolveExportExpression(sourceFile, exportName, checker) {
922
1007
  const moduleSymbol = getModuleSymbol(checker, sourceFile);
@@ -925,12 +1010,12 @@ function resolveExportExpression(sourceFile, exportName, checker) {
925
1010
  const explicit = exports.find((entry) => entry.getName() === exportName);
926
1011
  if (explicit) {
927
1012
  const declaration = getAliasedSymbolIfNeeded(checker, explicit).declarations?.[0];
928
- return expressionFromDeclaration(declaration);
1013
+ return declarationToExpression(declaration);
929
1014
  }
930
1015
  const defaultExport = exports.find((entry) => entry.getName() === "default");
931
1016
  if (!defaultExport) return void 0;
932
1017
  const defaultDecl = getAliasedSymbolIfNeeded(checker, defaultExport).declarations?.[0];
933
- const defaultExpr = expressionFromDeclaration(defaultDecl);
1018
+ const defaultExpr = declarationToExpression(defaultDecl);
934
1019
  if (!defaultExpr) return void 0;
935
1020
  const resolved = unwrapExpression(defaultExpr);
936
1021
  if (!ts.isObjectLiteralExpression(resolved)) return void 0;
@@ -942,24 +1027,25 @@ function resolveExportExpression(sourceFile, exportName, checker) {
942
1027
  }
943
1028
  return void 0;
944
1029
  }
945
- function maybeObjectLiteral(expression, ctx) {
946
- if (!expression) return void 0;
1030
+ function maybeObjectLiteral(expression, ctx, depth = 0) {
1031
+ if (!expression || depth > MAX_RECURSION_DEPTH) return void 0;
947
1032
  const resolved = unwrapExpression(expression);
1033
+ markFile(ctx, resolved.getSourceFile());
948
1034
  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;
1035
+ const referenced = evaluateExpressionReference(resolved, ctx, depth + 1);
1036
+ if (!referenced) return void 0;
1037
+ return maybeObjectLiteral(referenced, ctx, depth + 1);
955
1038
  }
956
- function collectSchemaExpressionsFromObject(objectLiteral, ctx) {
1039
+ function collectSchemaExpressionsFromObject(objectLiteral, ctx, depth) {
957
1040
  const schemas = {};
958
1041
  for (const property of objectLiteral.properties) {
959
1042
  if (ts.isSpreadAssignment(property)) {
960
- const spreadObject = maybeObjectLiteral(property.expression, ctx);
1043
+ const spreadObject = maybeObjectLiteral(property.expression, ctx, depth + 1);
961
1044
  if (!spreadObject) continue;
962
- Object.assign(schemas, collectSchemaExpressionsFromObject(spreadObject, ctx));
1045
+ Object.assign(
1046
+ schemas,
1047
+ collectSchemaExpressionsFromObject(spreadObject, ctx, depth + 1)
1048
+ );
963
1049
  continue;
964
1050
  }
965
1051
  if (ts.isPropertyAssignment(property)) {
@@ -976,10 +1062,10 @@ function collectSchemaExpressionsFromObject(objectLiteral, ctx) {
976
1062
  }
977
1063
  return schemas;
978
1064
  }
979
- function extractSchemaExpressions(cfgExpression, ctx) {
980
- const objectLiteral = maybeObjectLiteral(cfgExpression, ctx);
1065
+ function extractSchemaExpressions(cfgExpression, ctx, depth) {
1066
+ const objectLiteral = maybeObjectLiteral(cfgExpression, ctx, depth);
981
1067
  if (!objectLiteral) return {};
982
- return collectSchemaExpressionsFromObject(objectLiteral, ctx);
1068
+ return collectSchemaExpressionsFromObject(objectLiteral, ctx, depth);
983
1069
  }
984
1070
  function getNearestVariableName(node) {
985
1071
  let current = node;
@@ -991,31 +1077,45 @@ function getNearestVariableName(node) {
991
1077
  }
992
1078
  return void 0;
993
1079
  }
994
- function evaluateBranchExpression(expression, ctx) {
1080
+ function isAllAccess(expression) {
1081
+ if (ts.isPropertyAccessExpression(expression)) {
1082
+ return expression.name.text === "all";
1083
+ }
1084
+ return Boolean(
1085
+ expression.argumentExpression && ts.isStringLiteralLike(expression.argumentExpression) && expression.argumentExpression.text === "all"
1086
+ );
1087
+ }
1088
+ function evaluateBranchExpression(expression, ctx, depth) {
995
1089
  const resolved = unwrapExpression(expression);
1090
+ markFile(ctx, resolved.getSourceFile());
1091
+ if (depth > MAX_RECURSION_DEPTH) return void 0;
996
1092
  if (ts.isIdentifier(resolved)) {
997
- const valueExpr = resolveIdentifierExpression(resolved, ctx);
1093
+ const valueExpr = resolveIdentifierExpression(resolved, ctx, depth + 1);
998
1094
  if (!valueExpr) return void 0;
999
- return evaluateBranchExpression(valueExpr, ctx);
1095
+ return evaluateBranchExpression(valueExpr, ctx, depth + 1);
1000
1096
  }
1001
1097
  if (!ts.isCallExpression(resolved)) return void 0;
1002
1098
  const call = resolved;
1003
1099
  if (ts.isIdentifier(call.expression) && call.expression.text === "resource") {
1004
1100
  const firstArg = call.arguments[0];
1005
- if (!firstArg || !ts.isStringLiteralLike(firstArg)) return void 0;
1101
+ if (!firstArg || !ts.isStringLiteralLike(firstArg)) {
1102
+ ctx.unsupportedShapeSeen = true;
1103
+ return void 0;
1104
+ }
1006
1105
  return { base: normalizeResourceBase(firstArg.text), leaves: [] };
1007
1106
  }
1008
- if (!ts.isPropertyAccessExpression(call.expression)) return void 0;
1107
+ if (!ts.isPropertyAccessExpression(call.expression)) {
1108
+ ctx.unsupportedShapeSeen = true;
1109
+ return void 0;
1110
+ }
1009
1111
  const owner = call.expression.expression;
1010
1112
  const method = call.expression.name.text;
1011
- const branch = evaluateBranchExpression(owner, ctx);
1113
+ const branch = evaluateBranchExpression(owner, ctx, depth + 1);
1012
1114
  if (!branch) return void 0;
1013
- if (method === "with") {
1014
- return branch;
1015
- }
1115
+ if (method === "with") return branch;
1016
1116
  if (HTTP_METHODS.has(method)) {
1017
1117
  const cfgExpression = call.arguments[0];
1018
- const schemas = extractSchemaExpressions(cfgExpression, ctx);
1118
+ const schemas = extractSchemaExpressions(cfgExpression, ctx, depth + 1);
1019
1119
  const nextLeaf = {
1020
1120
  method,
1021
1121
  path: branch.base,
@@ -1029,7 +1129,7 @@ function evaluateBranchExpression(expression, ctx) {
1029
1129
  }
1030
1130
  if (method === "sub") {
1031
1131
  const mountedLeaves = call.arguments.flatMap(
1032
- (arg) => evaluateLeavesFromExpression(arg, ctx)
1132
+ (arg) => evaluateLeavesFromExpression(arg, ctx, depth + 1)
1033
1133
  );
1034
1134
  const prefixed = mountedLeaves.map((leaf) => ({
1035
1135
  ...leaf,
@@ -1040,29 +1140,44 @@ function evaluateBranchExpression(expression, ctx) {
1040
1140
  leaves: [...branch.leaves, ...prefixed]
1041
1141
  };
1042
1142
  }
1143
+ ctx.unsupportedShapeSeen = true;
1043
1144
  return void 0;
1044
1145
  }
1045
- function evaluateLeavesFromExpression(expression, ctx) {
1146
+ function expressionKey(expression) {
1147
+ const source = expression.getSourceFile();
1148
+ return `${path.resolve(source.fileName)}:${expression.getStart(source)}:${expression.getEnd()}:${expression.kind}`;
1149
+ }
1150
+ function evaluateLeavesFromExpression(expression, ctx, depth = 0) {
1046
1151
  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);
1152
+ markFile(ctx, resolved.getSourceFile());
1153
+ const key = expressionKey(resolved);
1154
+ if (ctx.activeExpressionKeys.has(key)) return [];
1155
+ if (depth > MAX_RECURSION_DEPTH) return [];
1156
+ ctx.activeExpressionKeys.add(key);
1052
1157
  try {
1053
1158
  if (ts.isIdentifier(resolved)) {
1054
- const valueExpr = resolveIdentifierExpression(resolved, ctx);
1159
+ const valueExpr = resolveIdentifierExpression(resolved, ctx, depth + 1);
1055
1160
  if (!valueExpr) return [];
1056
- return evaluateLeavesFromExpression(valueExpr, ctx);
1161
+ return evaluateLeavesFromExpression(valueExpr, ctx, depth + 1);
1162
+ }
1163
+ if (ts.isPropertyAccessExpression(resolved) || ts.isElementAccessExpression(resolved)) {
1164
+ if (isAllAccess(resolved)) {
1165
+ return evaluateLeavesFromExpression(resolved.expression, ctx, depth + 1);
1166
+ }
1167
+ const refExpr = resolvePropertyExpression(resolved, ctx, depth + 1);
1168
+ if (refExpr) {
1169
+ return evaluateLeavesFromExpression(refExpr, ctx, depth + 1);
1170
+ }
1171
+ return [];
1057
1172
  }
1058
1173
  if (ts.isArrayLiteralExpression(resolved)) {
1059
1174
  const leaves = [];
1060
1175
  for (const element of resolved.elements) {
1061
1176
  if (ts.isSpreadElement(element)) {
1062
- leaves.push(...evaluateLeavesFromExpression(element.expression, ctx));
1177
+ leaves.push(...evaluateLeavesFromExpression(element.expression, ctx, depth + 1));
1063
1178
  continue;
1064
1179
  }
1065
- leaves.push(...evaluateLeavesFromExpression(element, ctx));
1180
+ leaves.push(...evaluateLeavesFromExpression(element, ctx, depth + 1));
1066
1181
  }
1067
1182
  return leaves;
1068
1183
  }
@@ -1072,34 +1187,43 @@ function evaluateLeavesFromExpression(expression, ctx) {
1072
1187
  if (callName === "finalize") {
1073
1188
  const arg = resolved.arguments[0];
1074
1189
  if (!arg) return [];
1075
- return evaluateLeavesFromExpression(arg, ctx);
1190
+ return evaluateLeavesFromExpression(arg, ctx, depth + 1);
1076
1191
  }
1077
1192
  if (callName === "mergeArrays") {
1078
1193
  return resolved.arguments.flatMap(
1079
- (arg) => evaluateLeavesFromExpression(arg, ctx)
1194
+ (arg) => evaluateLeavesFromExpression(arg, ctx, depth + 1)
1080
1195
  );
1081
1196
  }
1082
1197
  }
1083
1198
  if (ts.isPropertyAccessExpression(resolved.expression)) {
1084
1199
  const prop = resolved.expression.name.text;
1085
1200
  if (prop === "done") {
1086
- const branch2 = evaluateBranchExpression(resolved.expression.expression, ctx);
1201
+ const branch2 = evaluateBranchExpression(
1202
+ resolved.expression.expression,
1203
+ ctx,
1204
+ depth + 1
1205
+ );
1087
1206
  return branch2?.leaves ?? [];
1088
1207
  }
1089
1208
  }
1209
+ const refExpr = evaluateExpressionReference(resolved, ctx, depth + 1);
1210
+ if (refExpr) return evaluateLeavesFromExpression(refExpr, ctx, depth + 1);
1211
+ ctx.unsupportedShapeSeen = true;
1212
+ return [];
1090
1213
  }
1091
- const branch = evaluateBranchExpression(resolved, ctx);
1092
- return branch?.leaves ?? [];
1214
+ const branch = evaluateBranchExpression(resolved, ctx, depth + 1);
1215
+ if (branch) return branch.leaves;
1216
+ ctx.unsupportedShapeSeen = true;
1217
+ return [];
1093
1218
  } finally {
1094
- ctx.visitedExpressionStarts.delete(key);
1219
+ ctx.activeExpressionKeys.delete(key);
1095
1220
  }
1096
1221
  }
1097
1222
  function resolveSchemaMetadata(expression, ctx) {
1098
1223
  const resolved = unwrapExpression(expression);
1099
1224
  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];
1225
+ const symbol = resolveSymbolFromNode(resolved, ctx);
1226
+ const declaration = symbol?.declarations?.[0];
1103
1227
  const locationNode = declaration ? ts.isVariableDeclaration(declaration) ? declaration.name : declaration : resolved;
1104
1228
  const sourceName = declaration && ts.isVariableDeclaration(declaration) && ts.isIdentifier(declaration.name) ? declaration.name.text : resolved.text;
1105
1229
  return {
@@ -1137,13 +1261,25 @@ function parseTsConfig(cwd, tsconfigPath) {
1137
1261
  void 0,
1138
1262
  resolvedTsconfig
1139
1263
  );
1140
- if (parsed.errors.length > 0) {
1264
+ const nonEmptyInputErrors = parsed.errors.filter((entry) => entry.code !== 18003);
1265
+ if (nonEmptyInputErrors.length > 0) {
1141
1266
  throw new Error(
1142
- parsed.errors.map((entry) => ts.flattenDiagnosticMessageText(entry.messageText, "\n")).join("\n")
1267
+ nonEmptyInputErrors.map((entry) => ts.flattenDiagnosticMessageText(entry.messageText, "\n")).join("\n")
1143
1268
  );
1144
1269
  }
1145
1270
  return { resolvedTsconfig, parsed };
1146
1271
  }
1272
+ function createProgramWithFallback(parsed, moduleFileAbs) {
1273
+ const base = ts.createProgram({
1274
+ rootNames: parsed.fileNames,
1275
+ options: parsed.options
1276
+ });
1277
+ if (findSourceFile(base, moduleFileAbs)) {
1278
+ return base;
1279
+ }
1280
+ const rootNames = Array.from(/* @__PURE__ */ new Set([...parsed.fileNames, moduleFileAbs]));
1281
+ return ts.createProgram({ rootNames, options: parsed.options });
1282
+ }
1147
1283
  function extractLeafSourceByAst({
1148
1284
  modulePath,
1149
1285
  exportName,
@@ -1152,32 +1288,43 @@ function extractLeafSourceByAst({
1152
1288
  }) {
1153
1289
  const parsedConfig = parseTsConfig(cwd, tsconfigPath);
1154
1290
  if (!parsedConfig) {
1155
- return { sourceByLeaf: {} };
1291
+ return {
1292
+ sourceByLeaf: {},
1293
+ reason: "module_not_in_program",
1294
+ stats: { visitedSymbols: 0, visitedFiles: 0, unresolvedReferences: 0 }
1295
+ };
1156
1296
  }
1157
- const program = ts.createProgram({
1158
- rootNames: parsedConfig.parsed.fileNames,
1159
- options: parsedConfig.parsed.options
1160
- });
1297
+ const moduleAbs = path.resolve(cwd, modulePath);
1298
+ const program = createProgramWithFallback(parsedConfig.parsed, moduleAbs);
1161
1299
  const checker = program.getTypeChecker();
1162
- const moduleFile = findSourceFile(program, path.resolve(cwd, modulePath));
1300
+ const moduleFile = findSourceFile(program, moduleAbs);
1163
1301
  if (!moduleFile) {
1164
1302
  return {
1165
1303
  sourceByLeaf: {},
1166
- tsconfigPath: parsedConfig.resolvedTsconfig
1304
+ tsconfigPath: parsedConfig.resolvedTsconfig,
1305
+ reason: "module_not_in_program",
1306
+ stats: { visitedSymbols: 0, visitedFiles: 0, unresolvedReferences: 0 }
1167
1307
  };
1168
1308
  }
1169
1309
  const exportedExpression = resolveExportExpression(moduleFile, exportName, checker);
1170
1310
  if (!exportedExpression) {
1171
1311
  return {
1172
1312
  sourceByLeaf: {},
1173
- tsconfigPath: parsedConfig.resolvedTsconfig
1313
+ tsconfigPath: parsedConfig.resolvedTsconfig,
1314
+ reason: "export_not_found",
1315
+ stats: { visitedSymbols: 0, visitedFiles: 1, unresolvedReferences: 0 }
1174
1316
  };
1175
1317
  }
1176
1318
  const ctx = {
1177
1319
  checker,
1178
- visitedExpressionStarts: /* @__PURE__ */ new Set()
1320
+ activeExpressionKeys: /* @__PURE__ */ new Set(),
1321
+ activeSymbols: /* @__PURE__ */ new Set(),
1322
+ visitedSymbolKeys: /* @__PURE__ */ new Set(),
1323
+ visitedFilePaths: /* @__PURE__ */ new Set([path.resolve(moduleFile.fileName)]),
1324
+ unresolvedReferences: 0,
1325
+ unsupportedShapeSeen: false
1179
1326
  };
1180
- const evaluatedLeaves = evaluateLeavesFromExpression(exportedExpression, ctx);
1327
+ const evaluatedLeaves = evaluateLeavesFromExpression(exportedExpression, ctx, 0);
1181
1328
  const sourceByLeaf = {};
1182
1329
  for (const leaf of evaluatedLeaves) {
1183
1330
  const key = buildLeafKey(leaf.method, leaf.path);
@@ -1185,17 +1332,27 @@ function extractLeafSourceByAst({
1185
1332
  ...toLocation(leaf.definitionNode),
1186
1333
  symbolName: getNearestVariableName(leaf.definitionNode)
1187
1334
  };
1335
+ ctx.visitedFilePaths.add(definition.file);
1188
1336
  const schemas = {};
1189
1337
  for (const schemaKey of SCHEMA_KEYS) {
1190
1338
  const schemaExpression = leaf.schemas[schemaKey];
1191
1339
  if (!schemaExpression) continue;
1192
- schemas[schemaKey] = resolveSchemaMetadata(schemaExpression, ctx);
1340
+ const schemaMeta = resolveSchemaMetadata(schemaExpression, ctx);
1341
+ ctx.visitedFilePaths.add(schemaMeta.file);
1342
+ schemas[schemaKey] = schemaMeta;
1193
1343
  }
1194
1344
  sourceByLeaf[key] = { definition, schemas };
1195
1345
  }
1346
+ const reason = Object.keys(sourceByLeaf).length > 0 ? void 0 : ctx.unsupportedShapeSeen ? "unsupported_expression_shape" : "resolved_zero_leaves";
1196
1347
  return {
1197
1348
  sourceByLeaf,
1198
- tsconfigPath: parsedConfig.resolvedTsconfig
1349
+ tsconfigPath: parsedConfig.resolvedTsconfig,
1350
+ reason,
1351
+ stats: {
1352
+ visitedSymbols: ctx.visitedSymbolKeys.size,
1353
+ visitedFiles: ctx.visitedFilePaths.size,
1354
+ unresolvedReferences: ctx.unresolvedReferences
1355
+ }
1199
1356
  };
1200
1357
  }
1201
1358
 
@@ -1378,7 +1535,9 @@ async function exportFinalizedLeaves(input, options = {}) {
1378
1535
  modulePath: path2.resolve(modulePath),
1379
1536
  exportName,
1380
1537
  tsconfigPath: extracted.tsconfigPath,
1381
- resolvedLeafCount: Object.keys(sourceByLeaf).length
1538
+ resolvedLeafCount: Object.keys(sourceByLeaf).length,
1539
+ reason: extracted.reason,
1540
+ stats: extracted.stats
1382
1541
  };
1383
1542
  } else {
1384
1543
  sourceExtraction = {
@@ -1386,7 +1545,13 @@ async function exportFinalizedLeaves(input, options = {}) {
1386
1545
  enabled: false,
1387
1546
  exportName,
1388
1547
  tsconfigPath: options.tsconfigPath ? path2.resolve(options.tsconfigPath) : void 0,
1389
- resolvedLeafCount: 0
1548
+ resolvedLeafCount: 0,
1549
+ reason: "resolved_zero_leaves",
1550
+ stats: {
1551
+ visitedSymbols: 0,
1552
+ visitedFiles: 0,
1553
+ unresolvedReferences: 0
1554
+ }
1390
1555
  };
1391
1556
  }
1392
1557
  }