@suss/adapter-typescript 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  // src/adapter.ts
2
2
  import path9 from "path";
3
3
  import {
4
- Node as Node53,
5
- Project
4
+ Node as Node54,
5
+ Project,
6
+ SyntaxKind as SyntaxKind11
6
7
  } from "ts-morph";
7
8
  import {
8
9
  functionCallBinding as functionCallBinding2,
@@ -10,7 +11,11 @@ import {
10
11
  graphqlResolverBinding,
11
12
  messageBusBinding,
12
13
  packageExportBinding,
13
- restBinding as restBinding2
14
+ readGraphqlMetadata as readGraphqlMetadata2,
15
+ readSourceDocumentMetadata,
16
+ restBinding as restBinding2,
17
+ withGraphqlMetadata as withGraphqlMetadata2,
18
+ withSourceDocumentMetadata
14
19
  } from "@suss/behavioral-ir";
15
20
  import { Database as Database5 } from "@suss/datalog";
16
21
  import {
@@ -1029,32 +1034,72 @@ function collectFallthroughCaseLabels(caseClause) {
1029
1034
  }
1030
1035
 
1031
1036
  // src/paths/pathConditions.ts
1032
- import { Node as Node8 } from "ts-morph";
1037
+ import { Node as Node9 } from "ts-morph";
1033
1038
  import {
1034
1039
  enumerateStructuredPaths,
1035
1040
  PathBudgetExceeded,
1036
1041
  UnmodeledFlow as UnmodeledFlow2
1037
1042
  } from "@suss/extractor";
1038
1043
 
1044
+ // src/walk/descent.ts
1045
+ import { Node as Node7 } from "ts-morph";
1046
+ var NO_BARRIERS = /* @__PURE__ */ new Set();
1047
+ function isDescentStop(node, func, barriers = NO_BARRIERS) {
1048
+ if (node === func) {
1049
+ return false;
1050
+ }
1051
+ if (Node7.isFunctionDeclaration(node) || Node7.isMethodDeclaration(node)) {
1052
+ return true;
1053
+ }
1054
+ if (Node7.isArrowFunction(node) || Node7.isFunctionExpression(node)) {
1055
+ return barriers.has(node);
1056
+ }
1057
+ return false;
1058
+ }
1059
+ function isInlineCallback(node, func, barriers = NO_BARRIERS) {
1060
+ if (node === func) {
1061
+ return false;
1062
+ }
1063
+ if (!(Node7.isArrowFunction(node) || Node7.isFunctionExpression(node))) {
1064
+ return false;
1065
+ }
1066
+ return !barriers.has(node);
1067
+ }
1068
+ function isModuleScopeStop(node) {
1069
+ return Node7.isFunctionDeclaration(node) || Node7.isMethodDeclaration(node) || Node7.isArrowFunction(node) || Node7.isFunctionExpression(node) || Node7.isClassDeclaration(node) || Node7.isClassExpression(node);
1070
+ }
1071
+ function crossesNestedFunctionScope(node, func) {
1072
+ let current2 = node;
1073
+ while (current2 !== void 0 && current2 !== func) {
1074
+ if (startsItsOwnScope(current2)) {
1075
+ return true;
1076
+ }
1077
+ current2 = current2.getParent();
1078
+ }
1079
+ return false;
1080
+ }
1081
+ function startsItsOwnScope(node) {
1082
+ return Node7.isArrowFunction(node) || Node7.isFunctionExpression(node) || Node7.isFunctionDeclaration(node) || Node7.isMethodDeclaration(node) || Node7.isGetAccessorDeclaration(node) || Node7.isSetAccessorDeclaration(node) || Node7.isConstructorDeclaration(node);
1083
+ }
1084
+
1039
1085
  // src/paths/lowering.ts
1040
1086
  import {
1041
- Node as Node7
1087
+ Node as Node8
1042
1088
  } from "ts-morph";
1043
1089
  import {
1044
1090
  UnmodeledFlow
1045
1091
  } from "@suss/extractor";
1046
- var isFunctionBoundary = (node) => Node7.isFunctionDeclaration(node) || Node7.isFunctionExpression(node) || Node7.isArrowFunction(node) || Node7.isMethodDeclaration(node);
1047
1092
  function exitKindOf(stmt) {
1048
1093
  let sawReturn = false;
1049
1094
  let sawThrow = false;
1050
1095
  const visit = (node) => {
1051
- if (node !== stmt && isFunctionBoundary(node)) {
1096
+ if (node !== stmt && startsItsOwnScope(node)) {
1052
1097
  return;
1053
1098
  }
1054
- if (Node7.isReturnStatement(node)) {
1099
+ if (Node8.isReturnStatement(node)) {
1055
1100
  sawReturn = true;
1056
1101
  }
1057
- if (Node7.isThrowStatement(node)) {
1102
+ if (Node8.isThrowStatement(node)) {
1058
1103
  sawThrow = true;
1059
1104
  }
1060
1105
  for (const child of node.getChildren()) {
@@ -1071,14 +1116,14 @@ var statementsOf = (stmt) => {
1071
1116
  if (stmt === void 0) {
1072
1117
  return [];
1073
1118
  }
1074
- if (Node7.isBlock(stmt)) {
1119
+ if (Node8.isBlock(stmt)) {
1075
1120
  return stmt.getStatements();
1076
1121
  }
1077
1122
  return [stmt];
1078
1123
  };
1079
- var isLoop = (stmt) => Node7.isForOfStatement(stmt) || Node7.isForInStatement(stmt) || Node7.isForStatement(stmt) || Node7.isWhileStatement(stmt) || Node7.isDoStatement(stmt);
1124
+ var isLoop = (stmt) => Node8.isForOfStatement(stmt) || Node8.isForInStatement(stmt) || Node8.isForStatement(stmt) || Node8.isWhileStatement(stmt) || Node8.isDoStatement(stmt);
1080
1125
  function getLoopBody(stmt) {
1081
- if (Node7.isForOfStatement(stmt) || Node7.isForInStatement(stmt) || Node7.isForStatement(stmt) || Node7.isWhileStatement(stmt) || Node7.isDoStatement(stmt)) {
1126
+ if (Node8.isForOfStatement(stmt) || Node8.isForInStatement(stmt) || Node8.isForStatement(stmt) || Node8.isWhileStatement(stmt) || Node8.isDoStatement(stmt)) {
1082
1127
  return stmt.getStatement();
1083
1128
  }
1084
1129
  return void 0;
@@ -1093,37 +1138,97 @@ var conditionOf = (expr) => ({
1093
1138
  sourceText: expr.getText(),
1094
1139
  expression: expr
1095
1140
  });
1096
- function lowerStatement(stmt, rawToStructured) {
1097
- const structured = buildStructured(stmt, rawToStructured);
1098
- rawToStructured.set(stmt, structured);
1141
+ function lowerStatement(stmt, ctx) {
1142
+ const structured = {
1143
+ ...buildStructured(stmt, ctx),
1144
+ callbacks: lowerCallbacksIn(ownExpressionsOf(stmt), ctx)
1145
+ };
1146
+ ctx.rawToStructured.set(stmt, structured);
1099
1147
  return structured;
1100
1148
  }
1101
- function lowerList(stmts, rawToStructured) {
1102
- return spliceBlocks(stmts).map((s) => lowerStatement(s, rawToStructured));
1149
+ function lowerList(stmts, ctx) {
1150
+ return spliceBlocks(stmts).map((s) => lowerStatement(s, ctx));
1151
+ }
1152
+ function ownExpressionsOf(stmt) {
1153
+ if (Node8.isIfStatement(stmt) || Node8.isSwitchStatement(stmt)) {
1154
+ return [stmt.getExpression()];
1155
+ }
1156
+ if (Node8.isWhileStatement(stmt) || Node8.isDoStatement(stmt)) {
1157
+ return [stmt.getExpression()];
1158
+ }
1159
+ if (Node8.isForOfStatement(stmt) || Node8.isForInStatement(stmt)) {
1160
+ return [stmt.getInitializer(), stmt.getExpression()];
1161
+ }
1162
+ if (Node8.isForStatement(stmt)) {
1163
+ return [
1164
+ stmt.getInitializer(),
1165
+ stmt.getCondition(),
1166
+ stmt.getIncrementor()
1167
+ ].filter((n) => n !== void 0);
1168
+ }
1169
+ if (Node8.isTryStatement(stmt)) {
1170
+ return [];
1171
+ }
1172
+ if (Node8.isReturnStatement(stmt) || Node8.isThrowStatement(stmt)) {
1173
+ const value = stmt.getExpression();
1174
+ return value === void 0 ? [] : [value];
1175
+ }
1176
+ if (Node8.isBreakStatement(stmt) || Node8.isContinueStatement(stmt)) {
1177
+ return [];
1178
+ }
1179
+ return [stmt];
1180
+ }
1181
+ function lowerCallbacksIn(parts, ctx) {
1182
+ const bodies = [];
1183
+ const visit = (node, root) => {
1184
+ if (node !== root && isDescentStop(node, ctx.func, ctx.barriers)) {
1185
+ return;
1186
+ }
1187
+ if (node !== root && (Node8.isArrowFunction(node) || Node8.isFunctionExpression(node)) && isInlineCallback(node, ctx.func, ctx.barriers)) {
1188
+ const body = node.getBody();
1189
+ if (body !== void 0 && Node8.isBlock(body)) {
1190
+ bodies.push(lowerList(body.getStatements(), ctx));
1191
+ return;
1192
+ }
1193
+ } else if (node !== root && startsItsOwnScope(node)) {
1194
+ return;
1195
+ }
1196
+ for (const child of node.getChildren()) {
1197
+ visit(child, root);
1198
+ }
1199
+ };
1200
+ for (const part of parts) {
1201
+ visit(part, part);
1202
+ }
1203
+ return bodies;
1204
+ }
1205
+ function lowerExpressionBodyCallbacks(expression, func, barriers = NO_BARRIERS) {
1206
+ return lowerCallbacksIn([expression], {
1207
+ func,
1208
+ barriers,
1209
+ rawToStructured: /* @__PURE__ */ new Map()
1210
+ });
1103
1211
  }
1104
1212
  function spliceBlocks(stmts) {
1105
1213
  return stmts.flatMap(
1106
- (s) => Node7.isBlock(s) ? spliceBlocks(s.getStatements()) : [s]
1214
+ (s) => Node8.isBlock(s) ? spliceBlocks(s.getStatements()) : [s]
1107
1215
  );
1108
1216
  }
1109
- function buildStructured(stmt, rawToStructured) {
1110
- if (Node7.isIfStatement(stmt)) {
1217
+ function buildStructured(stmt, ctx) {
1218
+ if (Node8.isIfStatement(stmt)) {
1111
1219
  const elseBranch = stmt.getElseStatement();
1112
1220
  return {
1113
1221
  kind: "if",
1114
1222
  condition: conditionOf(stmt.getExpression()),
1115
- thenBody: lowerList(
1116
- statementsOf(stmt.getThenStatement()),
1117
- rawToStructured
1118
- ),
1119
- elseBody: elseBranch === void 0 ? null : lowerList(statementsOf(elseBranch), rawToStructured),
1223
+ thenBody: lowerList(statementsOf(stmt.getThenStatement()), ctx),
1224
+ elseBody: elseBranch === void 0 ? null : lowerList(statementsOf(elseBranch), ctx),
1120
1225
  exitKind: exitKindOf(stmt)
1121
1226
  };
1122
1227
  }
1123
- if (Node7.isSwitchStatement(stmt)) {
1228
+ if (Node8.isSwitchStatement(stmt)) {
1124
1229
  return {
1125
1230
  kind: "switch",
1126
- groups: lowerSwitchGroups(stmt, rawToStructured),
1231
+ groups: lowerSwitchGroups(stmt, ctx),
1127
1232
  exitKind: exitKindOf(stmt)
1128
1233
  };
1129
1234
  }
@@ -1131,36 +1236,36 @@ function buildStructured(stmt, rawToStructured) {
1131
1236
  return {
1132
1237
  kind: "loop",
1133
1238
  condition: { sourceText: loopHeaderText(stmt), expression: null },
1134
- body: lowerList(statementsOf(getLoopBody(stmt)), rawToStructured),
1239
+ body: lowerList(statementsOf(getLoopBody(stmt)), ctx),
1135
1240
  exitKind: exitKindOf(stmt)
1136
1241
  };
1137
1242
  }
1138
- if (Node7.isTryStatement(stmt)) {
1243
+ if (Node8.isTryStatement(stmt)) {
1139
1244
  const catchClause = stmt.getCatchClause();
1140
1245
  const finallyBlock = stmt.getFinallyBlock();
1141
1246
  return {
1142
1247
  kind: "try",
1143
- tryBody: lowerList(stmt.getTryBlock().getStatements(), rawToStructured),
1144
- catchBody: catchClause === void 0 ? null : lowerList(catchClause.getBlock().getStatements(), rawToStructured),
1145
- finallyBody: finallyBlock === void 0 ? null : lowerList(finallyBlock.getStatements(), rawToStructured),
1248
+ tryBody: lowerList(stmt.getTryBlock().getStatements(), ctx),
1249
+ catchBody: catchClause === void 0 ? null : lowerList(catchClause.getBlock().getStatements(), ctx),
1250
+ finallyBody: finallyBlock === void 0 ? null : lowerList(finallyBlock.getStatements(), ctx),
1146
1251
  exitKind: exitKindOf(stmt)
1147
1252
  };
1148
1253
  }
1149
- if (Node7.isReturnStatement(stmt)) {
1254
+ if (Node8.isReturnStatement(stmt)) {
1150
1255
  return { kind: "exit", exit: "return", exitKind: exitKindOf(stmt) };
1151
1256
  }
1152
- if (Node7.isThrowStatement(stmt)) {
1257
+ if (Node8.isThrowStatement(stmt)) {
1153
1258
  return { kind: "exit", exit: "throw", exitKind: exitKindOf(stmt) };
1154
1259
  }
1155
- if (Node7.isBreakStatement(stmt)) {
1260
+ if (Node8.isBreakStatement(stmt)) {
1156
1261
  return { kind: "exit", exit: "break", exitKind: exitKindOf(stmt) };
1157
1262
  }
1158
- if (Node7.isContinueStatement(stmt)) {
1263
+ if (Node8.isContinueStatement(stmt)) {
1159
1264
  return { kind: "exit", exit: "continue", exitKind: exitKindOf(stmt) };
1160
1265
  }
1161
1266
  return { kind: "opaque", exitKind: exitKindOf(stmt) };
1162
1267
  }
1163
- function lowerSwitchGroups(stmt, rawToStructured) {
1268
+ function lowerSwitchGroups(stmt, ctx) {
1164
1269
  const switchText = stmt.getExpression().getText();
1165
1270
  const groups = [];
1166
1271
  let pendingLabels = [];
@@ -1169,7 +1274,7 @@ function lowerSwitchGroups(stmt, rawToStructured) {
1169
1274
  if (sawDefault) {
1170
1275
  throw new UnmodeledFlow("default clause is not last");
1171
1276
  }
1172
- if (Node7.isDefaultClause(clause)) {
1277
+ if (Node8.isDefaultClause(clause)) {
1173
1278
  sawDefault = true;
1174
1279
  const stmts2 = clause.getStatements();
1175
1280
  if (stmts2.length === 0) {
@@ -1177,11 +1282,11 @@ function lowerSwitchGroups(stmt, rawToStructured) {
1177
1282
  }
1178
1283
  groups.push({
1179
1284
  condition: null,
1180
- ...lowerGroupBody(stmts2, rawToStructured)
1285
+ ...lowerGroupBody(stmts2, ctx)
1181
1286
  });
1182
1287
  continue;
1183
1288
  }
1184
- if (!Node7.isCaseClause(clause)) {
1289
+ if (!Node8.isCaseClause(clause)) {
1185
1290
  continue;
1186
1291
  }
1187
1292
  pendingLabels.push(clause.getExpression().getText());
@@ -1194,7 +1299,7 @@ function lowerSwitchGroups(stmt, rawToStructured) {
1194
1299
  const sourceText = labels.map((l) => `${switchText} === ${l}`).join(" || ");
1195
1300
  groups.push({
1196
1301
  condition: { sourceText, expression: null },
1197
- ...lowerGroupBody(stmts, rawToStructured)
1302
+ ...lowerGroupBody(stmts, ctx)
1198
1303
  });
1199
1304
  }
1200
1305
  return groups;
@@ -1202,41 +1307,42 @@ function lowerSwitchGroups(stmt, rawToStructured) {
1202
1307
  function validateClauseBreaks(stmts) {
1203
1308
  const last = stmts[stmts.length - 1];
1204
1309
  for (const stmt of stmts) {
1205
- if (stmt === last && Node7.isBreakStatement(stmt)) {
1310
+ if (stmt === last && Node8.isBreakStatement(stmt)) {
1206
1311
  continue;
1207
1312
  }
1208
- if (Node7.isSwitchStatement(stmt) || isLoop(stmt)) {
1313
+ if (Node8.isSwitchStatement(stmt) || isLoop(stmt)) {
1209
1314
  continue;
1210
1315
  }
1211
1316
  stmt.forEachDescendant((node, traversal) => {
1212
- if (isFunctionBoundary(node) || Node7.isSwitchStatement(node) || isLoop(node)) {
1317
+ if (startsItsOwnScope(node) || Node8.isSwitchStatement(node) || isLoop(node)) {
1213
1318
  traversal.skip();
1214
1319
  return;
1215
1320
  }
1216
- if (Node7.isBreakStatement(node)) {
1321
+ if (Node8.isBreakStatement(node)) {
1217
1322
  throw new UnmodeledFlow("non-trailing break in switch clause");
1218
1323
  }
1219
1324
  });
1220
- if (Node7.isBreakStatement(stmt)) {
1325
+ if (Node8.isBreakStatement(stmt)) {
1221
1326
  throw new UnmodeledFlow("non-trailing break in switch clause");
1222
1327
  }
1223
1328
  }
1224
1329
  }
1225
- function lowerGroupBody(rawStmts, rawToStructured) {
1330
+ function lowerGroupBody(rawStmts, ctx) {
1226
1331
  const stmts = spliceBlocks(rawStmts);
1227
1332
  validateClauseBreaks(stmts);
1228
1333
  const last = stmts[stmts.length - 1];
1229
- const hasTrailingBreak = last !== void 0 && Node7.isBreakStatement(last);
1334
+ const hasTrailingBreak = last !== void 0 && Node8.isBreakStatement(last);
1230
1335
  const kept = hasTrailingBreak ? stmts.slice(0, -1) : stmts;
1231
- const body = lowerList(kept, rawToStructured);
1336
+ const body = lowerList(kept, ctx);
1232
1337
  if (hasTrailingBreak && last !== void 0) {
1233
- lowerStatement(last, rawToStructured);
1338
+ lowerStatement(last, ctx);
1234
1339
  }
1235
1340
  return { hasTrailingBreak, body };
1236
1341
  }
1237
- function lowerFunctionBody(body, terminalNodes) {
1342
+ function lowerFunctionBody(body, terminalNodes, func = body, barriers = NO_BARRIERS) {
1238
1343
  const rawToStructured = /* @__PURE__ */ new Map();
1239
- const statements = lowerList(body.getStatements(), rawToStructured);
1344
+ const ctx = { func, barriers, rawToStructured };
1345
+ const statements = lowerList(body.getStatements(), ctx);
1240
1346
  const terminalsByStmt = /* @__PURE__ */ new Map();
1241
1347
  const terminalHomeRaw = /* @__PURE__ */ new Map();
1242
1348
  for (const terminal of terminalNodes) {
@@ -1258,7 +1364,7 @@ function lowerFunctionBody(body, terminalNodes) {
1258
1364
  function enclosingLoweredStatement(terminal, body, rawToStructured) {
1259
1365
  let current2 = terminal;
1260
1366
  while (current2 !== void 0 && current2 !== body) {
1261
- if (Node7.isStatement(current2) && rawToStructured.has(current2)) {
1367
+ if (Node8.isStatement(current2) && rawToStructured.has(current2)) {
1262
1368
  return current2;
1263
1369
  }
1264
1370
  current2 = current2.getParent();
@@ -1267,39 +1373,42 @@ function enclosingLoweredStatement(terminal, body, rawToStructured) {
1267
1373
  }
1268
1374
 
1269
1375
  // src/paths/pathConditions.ts
1270
- function containsUnmodeledFlow(body) {
1376
+ function containsUnmodeledFlow(body, func, barriers) {
1271
1377
  let found = false;
1272
1378
  body.forEachDescendant((node, traversal) => {
1273
- if (isFunctionBoundary(node)) {
1379
+ if (isDescentStop(node, func, barriers)) {
1274
1380
  traversal.skip();
1275
1381
  return;
1276
1382
  }
1277
- if (Node8.isLabeledStatement(node)) {
1383
+ if (Node9.isLabeledStatement(node)) {
1278
1384
  found = true;
1279
1385
  traversal.stop();
1280
1386
  }
1281
1387
  });
1282
1388
  return found;
1283
1389
  }
1284
- function computePathConditions(func, terminalNodes) {
1390
+ function loweredExpressionBody(body, terminalNodes, func, barriers) {
1391
+ const statement = {
1392
+ kind: "opaque",
1393
+ exitKind: null,
1394
+ callbacks: lowerExpressionBodyCallbacks(body, func, barriers)
1395
+ };
1396
+ return {
1397
+ statements: [statement],
1398
+ terminalsByStmt: /* @__PURE__ */ new Map([[statement, terminalNodes]]),
1399
+ terminalHomeRaw: /* @__PURE__ */ new Map()
1400
+ };
1401
+ }
1402
+ function computePathConditions(func, terminalNodes, barriers = NO_BARRIERS) {
1285
1403
  const body = func.getBody();
1286
1404
  if (body === void 0) {
1287
1405
  return { byTerminal: /* @__PURE__ */ new Map(), fallthrough: [] };
1288
1406
  }
1289
- if (!Node8.isBlock(body)) {
1290
- const byTerminal = /* @__PURE__ */ new Map();
1291
- for (const terminal of terminalNodes) {
1292
- byTerminal.set(terminal, [
1293
- collectAncestorConditionInfosBelow(terminal, func)
1294
- ]);
1295
- }
1296
- return { byTerminal, fallthrough: [] };
1297
- }
1298
- if (containsUnmodeledFlow(body)) {
1407
+ if (containsUnmodeledFlow(body, func, barriers)) {
1299
1408
  return degradedResult(func, terminalNodes, "labeled statement");
1300
1409
  }
1301
1410
  try {
1302
- const lowered = lowerFunctionBody(body, terminalNodes);
1411
+ const lowered = Node9.isBlock(body) ? lowerFunctionBody(body, terminalNodes, func, barriers) : loweredExpressionBody(body, terminalNodes, func, barriers);
1303
1412
  const result = enumerateStructuredPaths({
1304
1413
  statements: lowered.statements,
1305
1414
  terminalsByStmt: lowered.terminalsByStmt
@@ -1366,7 +1475,7 @@ function lineAt(node, pos) {
1366
1475
  }
1367
1476
 
1368
1477
  // src/moduleExports.ts
1369
- import { Node as Node9 } from "ts-morph";
1478
+ import { Node as Node10 } from "ts-morph";
1370
1479
 
1371
1480
  // src/bootstrap/lazyProjectInit.ts
1372
1481
  import fs from "fs/promises";
@@ -1945,6 +2054,30 @@ async function readImportsConcurrently(paths) {
1945
2054
  return results;
1946
2055
  }
1947
2056
 
2057
+ // src/depTracking.ts
2058
+ function createDependencySink() {
2059
+ return { files: /* @__PURE__ */ new Set(), mountPrefixes: /* @__PURE__ */ new Map(), claims: [] };
2060
+ }
2061
+ var activeSink = null;
2062
+ function withDependencySink(sink, fn) {
2063
+ const previous = activeSink;
2064
+ activeSink = sink;
2065
+ try {
2066
+ return fn();
2067
+ } finally {
2068
+ activeSink = previous;
2069
+ }
2070
+ }
2071
+ function recordFileDependency(filePath) {
2072
+ activeSink?.files.add(filePath);
2073
+ }
2074
+ function recordMountPrefix(childId, prefix) {
2075
+ activeSink?.mountPrefixes.set(childId, prefix);
2076
+ }
2077
+ function recordUnitClaim(key, pack) {
2078
+ activeSink?.claims.push({ key, pack });
2079
+ }
2080
+
1948
2081
  // src/perFileCache.ts
1949
2082
  function createPerFileCache() {
1950
2083
  const byParse = /* @__PURE__ */ new WeakMap();
@@ -1960,6 +2093,7 @@ function createPerFileCache() {
1960
2093
 
1961
2094
  // src/moduleExports.ts
1962
2095
  function exportedDeclarationsOf(sourceFile) {
2096
+ recordFileDependency(sourceFile.getFilePath());
1963
2097
  const cached = exportsByFile.get(sourceFile);
1964
2098
  if (cached !== void 0) {
1965
2099
  return cached;
@@ -1974,7 +2108,14 @@ function resolveAliasedSymbol(symbol) {
1974
2108
  warmReExportChains(declaration.getSourceFile(), "every import");
1975
2109
  }
1976
2110
  try {
1977
- return symbol.getAliasedSymbol();
2111
+ const aliased = symbol.getAliasedSymbol();
2112
+ for (const declaration of aliased?.getDeclarations() ?? []) {
2113
+ const file = declaration.getSourceFile();
2114
+ if (!file.isInNodeModules()) {
2115
+ recordFileDependency(file.getFilePath());
2116
+ }
2117
+ }
2118
+ return aliased;
1978
2119
  } catch (error) {
1979
2120
  if (!(error instanceof RangeError)) {
1980
2121
  throw error;
@@ -2089,7 +2230,7 @@ function reExportShapeOf(file, reach) {
2089
2230
  for (const assignment of file.getExportAssignments()) {
2090
2231
  exportNodes.push(assignment);
2091
2232
  const expression = assignment.getExpression();
2092
- if (Node9.isIdentifier(expression)) {
2233
+ if (Node10.isIdentifier(expression)) {
2093
2234
  localNames.add(expression.getText());
2094
2235
  }
2095
2236
  }
@@ -2165,38 +2306,6 @@ function reportUnreadableExports(file) {
2165
2306
  );
2166
2307
  }
2167
2308
 
2168
- // src/walk/descent.ts
2169
- import { Node as Node10 } from "ts-morph";
2170
- var NO_BARRIERS = /* @__PURE__ */ new Set();
2171
- function isDescentStop(node, func, barriers = NO_BARRIERS) {
2172
- if (node === func) {
2173
- return false;
2174
- }
2175
- if (Node10.isFunctionDeclaration(node) || Node10.isMethodDeclaration(node)) {
2176
- return true;
2177
- }
2178
- if (Node10.isArrowFunction(node) || Node10.isFunctionExpression(node)) {
2179
- return barriers.has(node);
2180
- }
2181
- return false;
2182
- }
2183
- function isModuleScopeStop(node) {
2184
- return Node10.isFunctionDeclaration(node) || Node10.isMethodDeclaration(node) || Node10.isArrowFunction(node) || Node10.isFunctionExpression(node) || Node10.isClassDeclaration(node) || Node10.isClassExpression(node);
2185
- }
2186
- function crossesNestedFunctionScope(node, func) {
2187
- let current2 = node;
2188
- while (current2 !== void 0 && current2 !== func) {
2189
- if (startsItsOwnScope(current2)) {
2190
- return true;
2191
- }
2192
- current2 = current2.getParent();
2193
- }
2194
- return false;
2195
- }
2196
- function startsItsOwnScope(node) {
2197
- return Node10.isArrowFunction(node) || Node10.isFunctionExpression(node) || Node10.isFunctionDeclaration(node) || Node10.isMethodDeclaration(node) || Node10.isGetAccessorDeclaration(node) || Node10.isSetAccessorDeclaration(node) || Node10.isConstructorDeclaration(node);
2198
- }
2199
-
2200
2309
  // src/resolve/invocationEffects.ts
2201
2310
  function isImportedFrom(identifier, expectedModule) {
2202
2311
  if (!Node11.isIdentifier(identifier)) {
@@ -4908,7 +5017,8 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
4908
5017
  ];
4909
5018
  const { byTerminal } = computePathConditions(
4910
5019
  func,
4911
- terminals.map(({ node }) => node)
5020
+ terminals.map(({ node }) => node),
5021
+ barriers
4912
5022
  );
4913
5023
  const wantsFallthrough = terminalPatterns.some(
4914
5024
  (p) => p.match.type === "functionFallthrough"
@@ -4920,7 +5030,11 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
4920
5030
  if (!hasDefaultTerminal) {
4921
5031
  const synthetic = makeFallthroughTerminal(func);
4922
5032
  terminals.push(synthetic);
4923
- const pathResult = computePathConditions(func, [synthetic.node]);
5033
+ const pathResult = computePathConditions(
5034
+ func,
5035
+ [synthetic.node],
5036
+ barriers
5037
+ );
4924
5038
  byTerminal.set(
4925
5039
  synthetic.node,
4926
5040
  pathResult.fallthrough.length > 0 ? pathResult.fallthrough : [[]]
@@ -5051,7 +5165,7 @@ function indexFunctions(sf) {
5051
5165
  import { createHash as createHash2 } from "crypto";
5052
5166
  import fs2 from "fs/promises";
5053
5167
  import path2 from "path";
5054
- var SCHEMA_VERSION = "5";
5168
+ var SCHEMA_VERSION = "6";
5055
5169
  var MAX_ENTRIES = 2;
5056
5170
  var ENTRY_PREFIX = "key-";
5057
5171
  var ENTRY_DIR_NAME = new RegExp(`^${ENTRY_PREFIX}[0-9a-f]{16}$`);
@@ -5063,6 +5177,7 @@ function createCacheLayer(cacheDir) {
5063
5177
  kind: "miss",
5064
5178
  diagnostic: { kind: "miss", missReason: "no-manifest" }
5065
5179
  }),
5180
+ plan: async () => null,
5066
5181
  write: async () => {
5067
5182
  }
5068
5183
  };
@@ -5090,20 +5205,36 @@ function createCacheLayer(cacheDir) {
5090
5205
  return {
5091
5206
  kind: "hit",
5092
5207
  summaries: manifest.summaries,
5208
+ ...manifest.projectRoot === void 0 ? {} : { projectRoot: manifest.projectRoot },
5093
5209
  diagnostic: { kind: "hit" }
5094
5210
  };
5095
5211
  },
5096
- async write(input, summaries) {
5212
+ async plan(input) {
5213
+ const entryDir = entryDirFor(cacheDir, input);
5214
+ const manifest = await readManifest(path2.join(entryDir, "manifest.json"));
5215
+ if (manifest === null || manifest.roots === void 0 || manifest.rootMeta === void 0 || manifest.owners === void 0) {
5216
+ return null;
5217
+ }
5218
+ const currentTsconfigStamp = await stampTsconfig(input.tsconfigPath);
5219
+ if (!fileStampEquals(manifest.tsconfigStamp, currentTsconfigStamp)) {
5220
+ return null;
5221
+ }
5222
+ await markUsed(entryDir);
5223
+ return buildPlan(manifest, await resolveFileStamps(input));
5224
+ },
5225
+ async write(input, summaries, attribution) {
5226
+ const entryDir = entryDirFor(cacheDir, input);
5227
+ const previous = await readManifest(path2.join(entryDir, "manifest.json"));
5097
5228
  const tsconfigStamp = await stampTsconfig(input.tsconfigPath);
5098
- const files = await resolveFileStamps(input);
5229
+ const files = await hashStamps(await resolveFileStamps(input), previous);
5099
5230
  const manifest = {
5100
5231
  schemaVersion: SCHEMA_VERSION,
5101
5232
  adapterPacksDigest: input.adapterPacksDigest,
5102
5233
  tsconfigStamp,
5103
5234
  files,
5104
- summaries
5235
+ summaries,
5236
+ ...attribution === void 0 ? {} : encodeAttribution(attribution)
5105
5237
  };
5106
- const entryDir = entryDirFor(cacheDir, input);
5107
5238
  await fs2.mkdir(entryDir, { recursive: true });
5108
5239
  await fs2.writeFile(
5109
5240
  path2.join(entryDir, "manifest.json"),
@@ -5113,6 +5244,171 @@ function createCacheLayer(cacheDir) {
5113
5244
  }
5114
5245
  };
5115
5246
  }
5247
+ async function buildPlan(manifest, currentStamps) {
5248
+ const stored = new Map(manifest.files.map((f) => [f.path, f]));
5249
+ const current2 = new Map(currentStamps.map((f) => [f.path, f]));
5250
+ const changed = /* @__PURE__ */ new Set();
5251
+ const removed = /* @__PURE__ */ new Set();
5252
+ for (const p of stored.keys()) {
5253
+ if (!current2.has(p)) {
5254
+ removed.add(p);
5255
+ }
5256
+ }
5257
+ const toVerify = [];
5258
+ for (const [p, stamp] of current2) {
5259
+ const before = stored.get(p);
5260
+ if (before === void 0) {
5261
+ changed.add(p);
5262
+ } else if (!fileStampEquals(before, stamp)) {
5263
+ if (before.contentHash === void 0) {
5264
+ changed.add(p);
5265
+ } else {
5266
+ toVerify.push(p);
5267
+ }
5268
+ }
5269
+ }
5270
+ await Promise.all(
5271
+ toVerify.map(async (p) => {
5272
+ const hash = await hashFile(p);
5273
+ if (hash === null || hash !== stored.get(p)?.contentHash) {
5274
+ changed.add(p);
5275
+ }
5276
+ })
5277
+ );
5278
+ const roots = /* @__PURE__ */ new Map();
5279
+ const rootNames = manifest.roots ?? [];
5280
+ const depPaths = manifest.depPaths ?? [];
5281
+ let rootsDeclined = 0;
5282
+ rootNames.forEach((rootPath, i) => {
5283
+ const meta = manifest.rootMeta?.[i];
5284
+ if (meta === void 0) {
5285
+ return;
5286
+ }
5287
+ if (!meta.cacheable) {
5288
+ rootsDeclined += 1;
5289
+ }
5290
+ roots.set(rootPath, {
5291
+ path: rootPath,
5292
+ cacheable: meta.cacheable,
5293
+ deps: meta.deps.flatMap((d) => {
5294
+ const p = depPaths[d];
5295
+ return p === void 0 ? [] : [p];
5296
+ }),
5297
+ claims: meta.claims,
5298
+ mountPrefixes: meta.mountPrefixes,
5299
+ packs: meta.packs
5300
+ });
5301
+ });
5302
+ const validRoots = /* @__PURE__ */ new Set();
5303
+ for (const [rootPath, record] of roots) {
5304
+ if (!record.cacheable || !current2.has(rootPath)) {
5305
+ continue;
5306
+ }
5307
+ if (changed.has(rootPath)) {
5308
+ continue;
5309
+ }
5310
+ const depMoved = record.deps.some((d) => changed.has(d) || removed.has(d));
5311
+ if (!depMoved) {
5312
+ validRoots.add(rootPath);
5313
+ }
5314
+ }
5315
+ const owners = manifest.owners ?? [];
5316
+ return {
5317
+ changed,
5318
+ removed,
5319
+ roots,
5320
+ validRoots,
5321
+ rootsDeclined,
5322
+ storedProjectRoot: manifest.projectRoot ?? null,
5323
+ reuse(valid) {
5324
+ const summaries = [];
5325
+ const reusedOwners = [];
5326
+ manifest.summaries.forEach((summary, i) => {
5327
+ const ownerPaths = (owners[i] ?? []).flatMap((o) => {
5328
+ const p = rootNames[o];
5329
+ return p === void 0 ? [] : [p];
5330
+ });
5331
+ if (ownerPaths.some((p) => valid.has(p))) {
5332
+ summaries.push(summary);
5333
+ reusedOwners.push(ownerPaths.filter((p) => valid.has(p)));
5334
+ }
5335
+ });
5336
+ return { summaries, owners: reusedOwners };
5337
+ },
5338
+ allSummaries() {
5339
+ return manifest.summaries;
5340
+ },
5341
+ attribution() {
5342
+ return {
5343
+ roots: [...roots.values()],
5344
+ owners: owners.map(
5345
+ (ownerIds) => ownerIds.flatMap((o) => {
5346
+ const p = rootNames[o];
5347
+ return p === void 0 ? [] : [p];
5348
+ })
5349
+ ),
5350
+ ...manifest.projectRoot === void 0 ? {} : { projectRoot: manifest.projectRoot }
5351
+ };
5352
+ }
5353
+ };
5354
+ }
5355
+ function encodeAttribution(attribution) {
5356
+ const roots = attribution.roots.map((r) => r.path);
5357
+ const rootIndex = new Map(roots.map((p, i) => [p, i]));
5358
+ const depIndex = /* @__PURE__ */ new Map();
5359
+ const depPaths = [];
5360
+ const depIdOf = (p) => {
5361
+ const existing = depIndex.get(p);
5362
+ if (existing !== void 0) {
5363
+ return existing;
5364
+ }
5365
+ const id = depPaths.length;
5366
+ depPaths.push(p);
5367
+ depIndex.set(p, id);
5368
+ return id;
5369
+ };
5370
+ const rootMeta = attribution.roots.map((r) => ({
5371
+ cacheable: r.cacheable,
5372
+ deps: r.deps.map(depIdOf),
5373
+ claims: r.claims,
5374
+ mountPrefixes: r.mountPrefixes,
5375
+ packs: r.packs
5376
+ }));
5377
+ const owners = attribution.owners.map(
5378
+ (ownerPaths) => ownerPaths.flatMap((p) => {
5379
+ const i = rootIndex.get(p);
5380
+ return i === void 0 ? [] : [i];
5381
+ })
5382
+ );
5383
+ return {
5384
+ roots,
5385
+ rootMeta,
5386
+ depPaths,
5387
+ owners,
5388
+ ...attribution.projectRoot === void 0 ? {} : { projectRoot: attribution.projectRoot }
5389
+ };
5390
+ }
5391
+ async function hashStamps(stamps, previous) {
5392
+ const before = new Map((previous?.files ?? []).map((f) => [f.path, f]));
5393
+ return Promise.all(
5394
+ stamps.map(async (stamp) => {
5395
+ const prior = before.get(stamp.path);
5396
+ if (prior !== void 0 && prior.contentHash !== void 0 && fileStampEquals(prior, stamp)) {
5397
+ return { ...stamp, contentHash: prior.contentHash };
5398
+ }
5399
+ const contentHash = await hashFile(stamp.path);
5400
+ return contentHash === null ? stamp : { ...stamp, contentHash };
5401
+ })
5402
+ );
5403
+ }
5404
+ async function hashFile(filePath) {
5405
+ try {
5406
+ const content = await fs2.readFile(filePath);
5407
+ return createHash2("sha256").update(content).digest("hex").slice(0, 16);
5408
+ } catch {
5409
+ return null;
5410
+ }
5411
+ }
5116
5412
  async function describeAbsentEntry(cacheDir) {
5117
5413
  try {
5118
5414
  const entries = await fs2.readdir(cacheDir, { withFileTypes: true });
@@ -5126,7 +5422,7 @@ function entryDirFor(cacheDir, input) {
5126
5422
  SCHEMA_VERSION,
5127
5423
  input.adapterPacksDigest,
5128
5424
  input.tsconfigPath ?? ""
5129
- ].join("\0");
5425
+ ].join(" ");
5130
5426
  const name = createHash2("sha256").update(key).digest("hex").slice(0, 16);
5131
5427
  return path2.join(cacheDir, `${ENTRY_PREFIX}${name}`);
5132
5428
  }
@@ -6985,7 +7281,7 @@ function calleeOf(decorator) {
6985
7281
 
6986
7282
  // src/discovery/decoratedMethod.ts
6987
7283
  function resolverTypeName(args) {
6988
- return args.classTypeName ?? args.typeMap[args.decoratorName] ?? null;
7284
+ return args.typeMap[args.decoratorName] ?? args.classTypeName ?? null;
6989
7285
  }
6990
7286
  function resolveResolverClassTypeName(decoratorArg) {
6991
7287
  if (Node32.isArrowFunction(decoratorArg)) {
@@ -9549,7 +9845,10 @@ function discoverResolverMaps(sourceFile, match, kind, resolution) {
9549
9845
  resolverInfo: {
9550
9846
  typeName,
9551
9847
  fieldName,
9552
- ...schemaSdl !== null ? { schemaSdl } : {}
9848
+ ...schemaSdl !== null ? {
9849
+ schemaSdl,
9850
+ schemaDocument: config.getSourceFile().getFilePath()
9851
+ } : {}
9553
9852
  }
9554
9853
  });
9555
9854
  }
@@ -9746,7 +10045,15 @@ function discoverUnits(sourceFile, patterns, resolution, mountPrefixes) {
9746
10045
  }
9747
10046
 
9748
10047
  // src/discovery/mountPrefix.ts
9749
- var NO_MOUNTS = { effectivePrefixFor: () => "" };
10048
+ var NO_MOUNTS = {
10049
+ // Recorded even with no mounts anywhere: a mount added later in some
10050
+ // other file changes this router's prefix without touching its file.
10051
+ effectivePrefixFor: (routerNode) => {
10052
+ recordMountPrefix(nodeId(routerNode), "");
10053
+ return "";
10054
+ },
10055
+ prefixForId: () => ""
10056
+ };
9750
10057
  function buildMountPrefixIndex(packsByFile, resolution) {
9751
10058
  const subjectIdsByPack = /* @__PURE__ */ new Map();
9752
10059
  const mountWorkByPack = /* @__PURE__ */ new Map();
@@ -9803,10 +10110,15 @@ function buildMountPrefixIndex(packsByFile, resolution) {
9803
10110
  return NO_MOUNTS;
9804
10111
  }
9805
10112
  const memo = /* @__PURE__ */ new Map();
10113
+ const byId = (childId) => resolvePrefix(edgesByChild, memo, childId, /* @__PURE__ */ new Set()) ?? "";
9806
10114
  return {
9807
10115
  effectivePrefixFor(routerNode) {
9808
- return resolvePrefix(edgesByChild, memo, nodeId(routerNode), /* @__PURE__ */ new Set()) ?? "";
9809
- }
10116
+ const childId = nodeId(routerNode);
10117
+ const prefix = byId(childId);
10118
+ recordMountPrefix(childId, prefix);
10119
+ return prefix;
10120
+ },
10121
+ prefixForId: byId
9810
10122
  };
9811
10123
  }
9812
10124
  function recordEdge(edgesByChild, candidate) {
@@ -10058,14 +10370,16 @@ import {
10058
10370
  import {
10059
10371
  ANSWER_RELATIONS,
10060
10372
  RESOLUTION_QUESTIONS,
10061
- RESOLUTION_RULES as SHARED_RULES
10373
+ RESOLUTION_RULES as SHARED_RULES,
10374
+ VALUE_STEP
10062
10375
  } from "@suss/resolution";
10063
10376
  var JS_RULES = [
10064
- // f.bind(...) resolves to whatever f resolves to.
10377
+ // f.bind(...) leads wherever f leads. Stated as a step, so the
10378
+ // questions other than `comesTo` follow it too.
10065
10379
  rule2(
10066
- "comesTo",
10067
- [v2("r"), v2("h")],
10068
- [lit2("bindCall", v2("r"), v2("t")), lit2("comesTo", v2("t"), v2("h"))]
10380
+ "stepsTo",
10381
+ [v2("r"), v2("t"), VALUE_STEP],
10382
+ [lit2("bindCall", v2("r"), v2("t"))]
10069
10383
  )
10070
10384
  ];
10071
10385
  var RESOLUTION_PROGRAM = process.env.SUSS_RESOLUTION_ON_DEMAND === "0" ? {
@@ -10083,6 +10397,8 @@ var ResolutionStore = class {
10083
10397
  fullyExtracted = /* @__PURE__ */ new Set();
10084
10398
  seededValues = /* @__PURE__ */ new Set();
10085
10399
  importedNames = /* @__PURE__ */ new Map();
10400
+ /** Files the most recent wave walk entered, for the memo to keep. */
10401
+ lastQueryWalked = [];
10086
10402
  declarations = /* @__PURE__ */ new Map();
10087
10403
  graph = new ModuleGraph();
10088
10404
  stale = true;
@@ -10143,7 +10459,10 @@ var ResolutionStore = class {
10143
10459
  const declaration = `${nodeId(refersTo)}|${modules.join(",")}`;
10144
10460
  const cached = this.importedNames.get(declaration);
10145
10461
  if (cached !== void 0) {
10146
- return cached;
10462
+ for (const walkedPath of cached.walked) {
10463
+ recordFileDependency(walkedPath);
10464
+ }
10465
+ return cached.names;
10147
10466
  }
10148
10467
  const target = factKeyOf(value);
10149
10468
  const found = this.resolveByWaves(
@@ -10151,7 +10470,10 @@ var ResolutionStore = class {
10151
10470
  "wantedOrigin",
10152
10471
  () => this.lookupImportedNames(target, modules)
10153
10472
  ) ?? [];
10154
- this.importedNames.set(declaration, found);
10473
+ this.importedNames.set(declaration, {
10474
+ names: found,
10475
+ walked: [...this.lastQueryWalked]
10476
+ });
10155
10477
  return found;
10156
10478
  }
10157
10479
  /**
@@ -10185,6 +10507,7 @@ var ResolutionStore = class {
10185
10507
  this.wantValue(question, value);
10186
10508
  this.seedValue(value);
10187
10509
  const walked = /* @__PURE__ */ new Set();
10510
+ this.lastQueryWalked = [];
10188
10511
  let frontier = alsoFrom === void 0 ? [value.getSourceFile()] : [value.getSourceFile(), alsoFrom];
10189
10512
  for (let hop = 0; hop <= MAX_MODULE_HOPS; hop++) {
10190
10513
  const next = [];
@@ -10193,6 +10516,8 @@ var ResolutionStore = class {
10193
10516
  continue;
10194
10517
  }
10195
10518
  walked.add(sourceFile.getFilePath());
10519
+ this.lastQueryWalked.push(sourceFile.getFilePath());
10520
+ recordFileDependency(sourceFile.getFilePath());
10196
10521
  this.extractFile(sourceFile);
10197
10522
  next.push(...this.graph.importedFilesOf(sourceFile));
10198
10523
  }
@@ -10492,6 +10817,27 @@ function moduleInitSummary(sourceFile, effects, options) {
10492
10817
  return assembleSummary(raw, options);
10493
10818
  }
10494
10819
 
10820
+ // src/referencedFiles.ts
10821
+ function createReferenceIndex(sourceFiles) {
10822
+ const direct = /* @__PURE__ */ new Map();
10823
+ for (const sourceFile of sourceFiles) {
10824
+ const referenced = /* @__PURE__ */ new Set();
10825
+ for (const target of sourceFile.getReferencedSourceFiles()) {
10826
+ if (target.isInNodeModules() || target.isDeclarationFile()) {
10827
+ continue;
10828
+ }
10829
+ referenced.add(target.getFilePath());
10830
+ }
10831
+ direct.set(sourceFile.getFilePath(), referenced);
10832
+ }
10833
+ const none = /* @__PURE__ */ new Set();
10834
+ return {
10835
+ directOf(filePath) {
10836
+ return direct.get(filePath) ?? none;
10837
+ }
10838
+ };
10839
+ }
10840
+
10495
10841
  // src/resolve/boundaryEffects.ts
10496
10842
  import { evaluate as evaluate3, lit as lit3, rule as rule3, variable } from "@suss/datalog";
10497
10843
  var EFFECT_RULES = [
@@ -10537,7 +10883,10 @@ function bindingLabel(binding) {
10537
10883
  if (semantics.name === "graphql-operation") {
10538
10884
  return `${semantics.operationType} ${semantics.operationName ?? ""}`.trim();
10539
10885
  }
10540
- return semantics.exportName ?? semantics.module ?? binding.recognition;
10886
+ if ("exportName" in semantics || "module" in semantics) {
10887
+ return semantics.exportName ?? semantics.module ?? binding.recognition;
10888
+ }
10889
+ return binding.recognition;
10541
10890
  }
10542
10891
  function effectFact(effect) {
10543
10892
  if (effect.type === "invocation") {
@@ -10609,10 +10958,108 @@ function deriveBoundaryEffects(summaries, facts) {
10609
10958
  }
10610
10959
 
10611
10960
  // src/resolve/reachableClosure.ts
10612
- import { Node as Node49 } from "ts-morph";
10961
+ import { Node as Node50 } from "ts-morph";
10613
10962
  import { functionCallBinding } from "@suss/behavioral-ir";
10614
10963
  import { Database as Database3, evaluate as evaluate4, lit as lit4, rule as rule4, variable as variable2 } from "@suss/datalog";
10615
10964
  import { assembleSummary as assembleSummary2 } from "@suss/extractor";
10965
+
10966
+ // src/resolve/unfollowedCall.ts
10967
+ import {
10968
+ Node as Node49,
10969
+ SyntaxKind as SyntaxKind10
10970
+ } from "ts-morph";
10971
+ function isInExternalCode(sourceFile) {
10972
+ if (sourceFile.isDeclarationFile()) {
10973
+ return true;
10974
+ }
10975
+ return sourceFile.getFilePath().includes("/node_modules/");
10976
+ }
10977
+ function hasBody(fn) {
10978
+ if (Node49.isFunctionDeclaration(fn) || Node49.isMethodDeclaration(fn) || Node49.isFunctionExpression(fn)) {
10979
+ return fn.getBody?.() !== void 0;
10980
+ }
10981
+ return true;
10982
+ }
10983
+ function isDeclaredShape(declaration) {
10984
+ return (Node49.isMethodSignature(declaration) || Node49.isPropertySignature(declaration)) && !isInExternalCode(declaration.getSourceFile());
10985
+ }
10986
+ function isExternalDeclaration(declaration) {
10987
+ if (isInExternalCode(declaration.getSourceFile())) {
10988
+ return true;
10989
+ }
10990
+ if (Node49.isImportSpecifier(declaration) || Node49.isImportClause(declaration) || Node49.isNamespaceImport(declaration)) {
10991
+ const target = declaration.getFirstAncestorByKind(SyntaxKind10.ImportDeclaration)?.getModuleSpecifierSourceFile();
10992
+ return target === void 0 || isInExternalCode(target);
10993
+ }
10994
+ return declaresAnotherModule(declaration);
10995
+ }
10996
+ function declaresAnotherModule(declaration) {
10997
+ const module = declaration.getFirstAncestorByKind(
10998
+ SyntaxKind10.ModuleDeclaration
10999
+ );
11000
+ return module !== void 0 && Node49.isStringLiteral(module.getNameNode());
11001
+ }
11002
+ function statesShapeOnly(declaration) {
11003
+ if (isExternalDeclaration(declaration)) {
11004
+ return false;
11005
+ }
11006
+ if (Node49.isMethodSignature(declaration) || Node49.isPropertySignature(declaration)) {
11007
+ return true;
11008
+ }
11009
+ if (Node49.isFunctionDeclaration(declaration) || Node49.isMethodDeclaration(declaration)) {
11010
+ return declaration.getBody() === void 0;
11011
+ }
11012
+ return false;
11013
+ }
11014
+ function declarationsBehind(symbol) {
11015
+ if (symbol === void 0) {
11016
+ return [];
11017
+ }
11018
+ const behind = symbol.getAliasedSymbol()?.getDeclarations() ?? [];
11019
+ return behind.length > 0 ? behind : symbol.getDeclarations();
11020
+ }
11021
+ function classifyStop(declarations) {
11022
+ if (declarations.length === 0) {
11023
+ return "noDeclaration";
11024
+ }
11025
+ for (const declaration of declarations) {
11026
+ if (!isInExternalCode(declaration.getSourceFile())) {
11027
+ recordFileDependency(declaration.getSourceFile().getFilePath());
11028
+ }
11029
+ }
11030
+ if (declarations.every(isExternalDeclaration)) {
11031
+ return "outsideRun";
11032
+ }
11033
+ if (declarations.some(statesShapeOnly)) {
11034
+ return "noBody";
11035
+ }
11036
+ return "unsettledValue";
11037
+ }
11038
+ var RECORDED = {
11039
+ noBody: true,
11040
+ unsettledValue: true,
11041
+ outsideRun: false,
11042
+ noDeclaration: false
11043
+ };
11044
+ function worthRecording(reason) {
11045
+ return RECORDED[reason];
11046
+ }
11047
+ var STOP_SENTENCE = {
11048
+ noBody: (callee) => `The call to ${callee} lands on a declaration with no body, so whatever runs there is missing from this summary`,
11049
+ unsettledValue: (callee) => `The call to ${callee} goes through a value this run could not settle, so whatever runs there is missing from this summary`,
11050
+ outsideRun: (callee) => `The call to ${callee} lands in a package whose source is not in this run, so whatever runs there is missing from this summary`,
11051
+ noDeclaration: (callee) => `The call to ${callee} has no declaration this run could find, so whatever runs there is missing from this summary`
11052
+ };
11053
+ function unfollowedCallGap(stop) {
11054
+ return {
11055
+ type: "unfollowedCall",
11056
+ conditions: [],
11057
+ consequence: "unknown",
11058
+ description: STOP_SENTENCE[stop.reason](stop.callee)
11059
+ };
11060
+ }
11061
+
11062
+ // src/resolve/reachableClosure.ts
10616
11063
  var reachablePack = {
10617
11064
  name: "reachable",
10618
11065
  languages: ["typescript"],
@@ -10635,23 +11082,6 @@ function nodeKey2(func) {
10635
11082
  const sf = func.getSourceFile();
10636
11083
  return `${sf.getFilePath()}:${func.getStart()}-${func.getEnd()}`;
10637
11084
  }
10638
- function isInExternalCode(sourceFile) {
10639
- if (sourceFile.isDeclarationFile()) {
10640
- return true;
10641
- }
10642
- const filePath = sourceFile.getFilePath();
10643
- if (filePath.includes("/node_modules/")) {
10644
- return true;
10645
- }
10646
- return false;
10647
- }
10648
- function hasBody(fn) {
10649
- if (Node49.isFunctionDeclaration(fn) || Node49.isMethodDeclaration(fn) || Node49.isFunctionExpression(fn)) {
10650
- const body = fn.getBody?.();
10651
- return body !== void 0;
10652
- }
10653
- return true;
10654
- }
10655
11085
  function resolveDecl(decl, calleeName) {
10656
11086
  if (isInExternalCode(decl.getSourceFile())) {
10657
11087
  return null;
@@ -10663,14 +11093,14 @@ function resolveDecl(decl, calleeName) {
10663
11093
  }
10664
11094
  return { func: fn, name: declName(decl) ?? calleeName };
10665
11095
  }
10666
- if (Node49.isVariableDeclaration(decl)) {
11096
+ if (Node50.isVariableDeclaration(decl)) {
10667
11097
  const init = decl.getInitializer();
10668
- if (init !== void 0 && (Node49.isArrowFunction(init) || Node49.isFunctionExpression(init))) {
11098
+ if (init !== void 0 && (Node50.isArrowFunction(init) || Node50.isFunctionExpression(init))) {
10669
11099
  return { func: init, name: decl.getName() };
10670
11100
  }
10671
11101
  return null;
10672
11102
  }
10673
- if (Node49.isImportSpecifier(decl)) {
11103
+ if (Node50.isImportSpecifier(decl)) {
10674
11104
  const importDecl = decl.getImportDeclaration();
10675
11105
  const sf = importDecl.getModuleSpecifierSourceFile();
10676
11106
  if (sf === void 0 || isInExternalCode(sf)) {
@@ -10691,78 +11121,85 @@ function resolveDecl(decl, calleeName) {
10691
11121
  return null;
10692
11122
  }
10693
11123
  function declName(decl) {
10694
- if (Node49.isFunctionDeclaration(decl) || Node49.isMethodDeclaration(decl)) {
11124
+ if (Node50.isFunctionDeclaration(decl) || Node50.isMethodDeclaration(decl)) {
10695
11125
  const n = decl.getName?.();
10696
11126
  if (typeof n === "string" && n.length > 0) {
10697
11127
  return n;
10698
11128
  }
10699
11129
  }
10700
- if (Node49.isFunctionExpression(decl)) {
11130
+ if (Node50.isFunctionExpression(decl)) {
10701
11131
  const n = decl.getName();
10702
11132
  if (typeof n === "string" && n.length > 0) {
10703
11133
  return n;
10704
11134
  }
10705
11135
  }
10706
11136
  const parent = decl.getParent();
10707
- if (parent !== void 0 && Node49.isVariableDeclaration(parent)) {
11137
+ if (parent !== void 0 && Node50.isVariableDeclaration(parent)) {
10708
11138
  return parent.getName();
10709
11139
  }
10710
11140
  return null;
10711
11141
  }
10712
11142
  function resolveCallee(call, calleeName, scan) {
10713
- if (!Node49.isCallExpression(call)) {
11143
+ if (!Node50.isCallExpression(call)) {
10714
11144
  return null;
10715
11145
  }
10716
11146
  const callee = call.getExpression();
10717
- const declarations = callee.getSymbol()?.getDeclarations() ?? [];
11147
+ const symbol = callee.getSymbol();
11148
+ const declarations = symbol?.getDeclarations() ?? [];
10718
11149
  for (const decl of declarations) {
10719
11150
  const resolved = resolveDecl(decl, calleeName);
10720
11151
  if (resolved !== null) {
10721
- return resolved;
11152
+ return { kind: "followed", candidate: resolved };
10722
11153
  }
10723
11154
  }
10724
- if (scan.resolveCallable === void 0 || !readsAField(callee)) {
10725
- return null;
10726
- }
10727
- if (!declarations.some(isDeclaredShape)) {
10728
- return null;
10729
- }
10730
- const value = scan.resolveCallable(callee, scan.reachedFrom);
10731
- return value === null ? null : resolveDecl(value, calleeName);
10732
- }
10733
- function readsAField(callee) {
10734
- if (!Node49.isPropertyAccessExpression(callee)) {
10735
- return false;
10736
- }
10737
- let receiver = callee.getExpression();
10738
- while (Node49.isPropertyAccessExpression(receiver)) {
10739
- receiver = receiver.getExpression();
11155
+ const resolveCallable = scan.resolveCallable;
11156
+ if (resolveCallable !== void 0 && Node50.isPropertyAccessExpression(callee) && declarations.some(isDeclaredShape)) {
11157
+ const value = resolveCallable(callee, scan.reachedFrom);
11158
+ const resolved = value === null ? null : resolveDecl(value, calleeName);
11159
+ if (resolved !== null) {
11160
+ return { kind: "followed", candidate: resolved };
11161
+ }
10740
11162
  }
10741
- return Node49.isThisExpression(receiver);
10742
- }
10743
- function isDeclaredShape(declaration) {
10744
- return (Node49.isMethodSignature(declaration) || Node49.isPropertySignature(declaration)) && !isInExternalCode(declaration.getSourceFile());
11163
+ return {
11164
+ kind: "stopped",
11165
+ stop: {
11166
+ callee: calleeName,
11167
+ reason: classifyStop(declarationsBehind(symbol))
11168
+ }
11169
+ };
10745
11170
  }
10746
11171
  function collectReachable(func, scan) {
10747
- const found = [];
11172
+ const candidates = [];
11173
+ const stops = [];
10748
11174
  const seen = /* @__PURE__ */ new Set();
10749
11175
  func.forEachDescendant((node) => {
10750
- if (!Node49.isCallExpression(node)) {
11176
+ if (!Node50.isCallExpression(node)) {
10751
11177
  return;
10752
11178
  }
10753
- const calleeText = node.getExpression().getText();
10754
- const resolved = resolveCallee(node, calleeText, scan);
10755
- if (resolved === null) {
11179
+ const calleeText = normalizeCallee(node.getExpression().getText());
11180
+ const outcome = resolveCallee(node, calleeText, scan);
11181
+ if (outcome === null) {
10756
11182
  return;
10757
11183
  }
10758
- const key = nodeKey2(resolved.func);
11184
+ if (outcome.kind === "stopped") {
11185
+ const stopKey = `${outcome.stop.reason}:${outcome.stop.callee}`;
11186
+ if (!seen.has(stopKey) && worthRecording(outcome.stop.reason)) {
11187
+ seen.add(stopKey);
11188
+ stops.push(outcome.stop);
11189
+ }
11190
+ return;
11191
+ }
11192
+ const key = nodeKey2(outcome.candidate.func);
10759
11193
  if (seen.has(key)) {
10760
11194
  return;
10761
11195
  }
10762
11196
  seen.add(key);
10763
- found.push(resolved);
11197
+ candidates.push(outcome.candidate);
10764
11198
  });
10765
- return found;
11199
+ return { candidates, stops };
11200
+ }
11201
+ function normalizeCallee(text) {
11202
+ return text.replace(/\s+/g, " ").trim();
10766
11203
  }
10767
11204
  function extractReachableSummary(candidate, options, recognizers) {
10768
11205
  const unit = {
@@ -10796,12 +11233,14 @@ var REACHABLE_RULES = [
10796
11233
  )
10797
11234
  ];
10798
11235
  var NO_RECOGNIZERS = { invocation: [], access: [] };
10799
- function expandReachableClosure(seeds, project, options, projectFileSet, facts, recognizers = NO_RECOGNIZERS) {
11236
+ function expandReachableClosure(seeds, project, options, projectFileSet, facts, recognizers = NO_RECOGNIZERS, alreadySummarized = []) {
10800
11237
  const lookup = createSourceFileLookup(project);
10801
11238
  const db = facts?.db ?? new Database3();
10802
11239
  const functionByKey = /* @__PURE__ */ new Map();
10803
11240
  const seedKeys = /* @__PURE__ */ new Set();
10804
11241
  const scanned = /* @__PURE__ */ new Set();
11242
+ const stopsByKey = /* @__PURE__ */ new Map();
11243
+ const summariesByKey = /* @__PURE__ */ new Map();
10805
11244
  const reachedFrom = /* @__PURE__ */ new Map();
10806
11245
  for (const seed of seeds) {
10807
11246
  const func = lookup.functionAt(seed.location);
@@ -10810,9 +11249,19 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
10810
11249
  seedKeys.add(key);
10811
11250
  functionByKey.set(key, { func, name: seed.identity.name });
10812
11251
  facts?.unitKeyBySummary.set(seed, key);
11252
+ rememberSummary(summariesByKey, key, seed);
10813
11253
  db.add("entry", [key]);
10814
11254
  }
10815
11255
  }
11256
+ const knownKeys = /* @__PURE__ */ new Set();
11257
+ for (const summary of alreadySummarized) {
11258
+ const func = lookup.functionAt(summary.location);
11259
+ if (func !== null) {
11260
+ const key = nodeKey2(func);
11261
+ knownKeys.add(key);
11262
+ facts?.unitKeyBySummary.set(summary, key);
11263
+ }
11264
+ }
10816
11265
  for (; ; ) {
10817
11266
  evaluate4(db, REACHABLE_RULES);
10818
11267
  const frontier = db.facts("reachable").map(([key]) => String(key)).filter((key) => !scanned.has(key));
@@ -10830,7 +11279,15 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
10830
11279
  ...recognizers.resolveCallable === void 0 ? {} : { resolveCallable: recognizers.resolveCallable },
10831
11280
  ...cameFrom === void 0 ? {} : { reachedFrom: cameFrom }
10832
11281
  };
10833
- for (const candidate of collectReachable(source.func, scan)) {
11282
+ const { candidates, stops } = scanWithRecording(
11283
+ key,
11284
+ facts,
11285
+ () => collectReachable(source.func, scan)
11286
+ );
11287
+ if (stops.length > 0) {
11288
+ stopsByKey.set(key, stops);
11289
+ }
11290
+ for (const candidate of candidates) {
10834
11291
  const calleeKey = nodeKey2(candidate.func);
10835
11292
  if (!functionByKey.has(calleeKey)) {
10836
11293
  functionByKey.set(calleeKey, candidate);
@@ -10845,7 +11302,7 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
10845
11302
  const reached = [];
10846
11303
  for (const [keyAtom] of db.facts("reachable")) {
10847
11304
  const key = String(keyAtom);
10848
- if (seedKeys.has(key)) {
11305
+ if (seedKeys.has(key) || knownKeys.has(key)) {
10849
11306
  continue;
10850
11307
  }
10851
11308
  const candidate = functionByKey.get(key);
@@ -10859,15 +11316,50 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
10859
11316
  candidate.func.getSourceFile().getFilePath()
10860
11317
  );
10861
11318
  }
10862
- const summary = extractReachableSummary(candidate, options, recognizers);
11319
+ const summary = scanWithRecording(
11320
+ key,
11321
+ facts,
11322
+ () => extractReachableSummary(candidate, options, recognizers)
11323
+ );
10863
11324
  facts?.unitKeyBySummary.set(summary, key);
11325
+ rememberSummary(summariesByKey, key, summary);
10864
11326
  reached.push(summary);
10865
11327
  }
11328
+ recordStops(stopsByKey, summariesByKey, options);
10866
11329
  return [...seeds, ...reached];
10867
11330
  }
11331
+ function scanWithRecording(key, facts, fn) {
11332
+ const filesByKey = facts?.filesByKey;
11333
+ if (filesByKey === void 0) {
11334
+ return fn();
11335
+ }
11336
+ const sink = createDependencySink();
11337
+ const result = withDependencySink(sink, fn);
11338
+ const bucket = filesByKey.get(key) ?? /* @__PURE__ */ new Set();
11339
+ for (const filePath of sink.files) {
11340
+ bucket.add(filePath);
11341
+ }
11342
+ filesByKey.set(key, bucket);
11343
+ return result;
11344
+ }
11345
+ function rememberSummary(summariesByKey, key, summary) {
11346
+ const bucket = summariesByKey.get(key) ?? [];
11347
+ bucket.push(summary);
11348
+ summariesByKey.set(key, bucket);
11349
+ }
11350
+ function recordStops(stopsByKey, summariesByKey, options) {
11351
+ if (options?.gapHandling === "silent") {
11352
+ return;
11353
+ }
11354
+ for (const [key, stops] of stopsByKey) {
11355
+ for (const summary of summariesByKey.get(key) ?? []) {
11356
+ summary.gaps.push(...stops.map(unfollowedCallGap));
11357
+ }
11358
+ }
11359
+ }
10868
11360
 
10869
11361
  // src/resolve/rethrowEnrichment.ts
10870
- import { Node as Node50 } from "ts-morph";
11362
+ import { Node as Node51 } from "ts-morph";
10871
11363
  import { Database as Database4, evaluate as evaluate5, lit as lit5, rule as rule5, variable as variable3 } from "@suss/datalog";
10872
11364
  var RETHROW_RULES = [
10873
11365
  rule5(
@@ -11007,7 +11499,7 @@ function findBareRethrow(func, location) {
11007
11499
  traversal.stop();
11008
11500
  return;
11009
11501
  }
11010
- if (!Node50.isThrowStatement(node)) {
11502
+ if (!Node51.isThrowStatement(node)) {
11011
11503
  return;
11012
11504
  }
11013
11505
  if (startLineOf(node) !== location.start) {
@@ -11017,7 +11509,7 @@ function findBareRethrow(func, location) {
11017
11509
  return;
11018
11510
  }
11019
11511
  const thrown = node.getExpression();
11020
- if (thrown !== void 0 && Node50.isIdentifier(thrown)) {
11512
+ if (thrown !== void 0 && Node51.isIdentifier(thrown)) {
11021
11513
  found = node;
11022
11514
  }
11023
11515
  });
@@ -11027,13 +11519,13 @@ function enclosingTry(throwStmt) {
11027
11519
  let current2 = throwStmt.getParent();
11028
11520
  let sawCatchClause = false;
11029
11521
  while (current2 !== void 0) {
11030
- if (Node50.isCatchClause(current2)) {
11522
+ if (Node51.isCatchClause(current2)) {
11031
11523
  sawCatchClause = true;
11032
11524
  }
11033
- if (Node50.isTryStatement(current2)) {
11525
+ if (Node51.isTryStatement(current2)) {
11034
11526
  return sawCatchClause ? current2 : null;
11035
11527
  }
11036
- if (Node50.isFunctionDeclaration(current2) || Node50.isFunctionExpression(current2) || Node50.isArrowFunction(current2) || Node50.isMethodDeclaration(current2)) {
11528
+ if (Node51.isFunctionDeclaration(current2) || Node51.isFunctionExpression(current2) || Node51.isArrowFunction(current2) || Node51.isMethodDeclaration(current2)) {
11037
11529
  return null;
11038
11530
  }
11039
11531
  current2 = current2.getParent();
@@ -11041,14 +11533,14 @@ function enclosingTry(throwStmt) {
11041
11533
  return null;
11042
11534
  }
11043
11535
  function collectTryBodyCallees(tryStmt, index, keyFor) {
11044
- if (!Node50.isTryStatement(tryStmt)) {
11536
+ if (!Node51.isTryStatement(tryStmt)) {
11045
11537
  return [];
11046
11538
  }
11047
11539
  const tryBlock = tryStmt.getTryBlock();
11048
11540
  const callees = [];
11049
11541
  const seen = /* @__PURE__ */ new Set();
11050
11542
  tryBlock.forEachDescendant((node) => {
11051
- if (!Node50.isCallExpression(node)) {
11543
+ if (!Node51.isCallExpression(node)) {
11052
11544
  return;
11053
11545
  }
11054
11546
  const calleeSummary = resolveCalleeSummary(node, index);
@@ -11065,7 +11557,7 @@ function collectTryBodyCallees(tryStmt, index, keyFor) {
11065
11557
  return callees;
11066
11558
  }
11067
11559
  function resolveCalleeSummary(call, index) {
11068
- if (!Node50.isCallExpression(call)) {
11560
+ if (!Node51.isCallExpression(call)) {
11069
11561
  return null;
11070
11562
  }
11071
11563
  const callee = call.getExpression();
@@ -11091,12 +11583,12 @@ function resolveCalleeSummary(call, index) {
11091
11583
  return null;
11092
11584
  }
11093
11585
  function functionFromDecl(decl) {
11094
- if (Node50.isFunctionDeclaration(decl) || Node50.isFunctionExpression(decl) || Node50.isArrowFunction(decl) || Node50.isMethodDeclaration(decl)) {
11586
+ if (Node51.isFunctionDeclaration(decl) || Node51.isFunctionExpression(decl) || Node51.isArrowFunction(decl) || Node51.isMethodDeclaration(decl)) {
11095
11587
  return decl;
11096
11588
  }
11097
- if (Node50.isVariableDeclaration(decl)) {
11589
+ if (Node51.isVariableDeclaration(decl)) {
11098
11590
  const init = decl.getInitializer();
11099
- if (init !== void 0 && (Node50.isArrowFunction(init) || Node50.isFunctionExpression(init))) {
11591
+ if (init !== void 0 && (Node51.isArrowFunction(init) || Node51.isFunctionExpression(init))) {
11100
11592
  return init;
11101
11593
  }
11102
11594
  }
@@ -11105,23 +11597,23 @@ function functionFromDecl(decl) {
11105
11597
 
11106
11598
  // src/shapes/fieldAccesses.ts
11107
11599
  import {
11108
- Node as Node51
11600
+ Node as Node52
11109
11601
  } from "ts-morph";
11110
11602
  function findResponseAccessor(callExpr) {
11111
11603
  let current2 = callExpr;
11112
11604
  const parent1 = current2.getParent();
11113
- if (parent1 !== void 0 && Node51.isAwaitExpression(parent1)) {
11605
+ if (parent1 !== void 0 && Node52.isAwaitExpression(parent1)) {
11114
11606
  current2 = parent1;
11115
11607
  }
11116
11608
  const parent2 = current2.getParent();
11117
- if (parent2 === void 0 || !Node51.isVariableDeclaration(parent2)) {
11609
+ if (parent2 === void 0 || !Node52.isVariableDeclaration(parent2)) {
11118
11610
  return null;
11119
11611
  }
11120
11612
  const nameNode = parent2.getNameNode();
11121
- if (Node51.isIdentifier(nameNode)) {
11613
+ if (Node52.isIdentifier(nameNode)) {
11122
11614
  return { kind: "identifier", name: nameNode.getText() };
11123
11615
  }
11124
- if (Node51.isObjectBindingPattern(nameNode)) {
11616
+ if (Node52.isObjectBindingPattern(nameNode)) {
11125
11617
  const bindings = /* @__PURE__ */ new Map();
11126
11618
  for (const element of nameNode.getElements()) {
11127
11619
  if (element.getDotDotDotToken() !== void 0) {
@@ -11142,11 +11634,11 @@ function findResponseAccessor(callExpr) {
11142
11634
  function buildThenChainAccessor(callExpr, func) {
11143
11635
  const bindings = /* @__PURE__ */ new Map();
11144
11636
  func.forEachDescendant((node) => {
11145
- if (!Node51.isParameterDeclaration(node)) {
11637
+ if (!Node52.isParameterDeclaration(node)) {
11146
11638
  return;
11147
11639
  }
11148
11640
  const nameNode = node.getNameNode();
11149
- if (!Node51.isIdentifier(nameNode)) {
11641
+ if (!Node52.isIdentifier(nameNode)) {
11150
11642
  return;
11151
11643
  }
11152
11644
  const prefix = prefixOfThenParam(node, callExpr, 0);
@@ -11178,7 +11670,7 @@ function prefixOfExpr(expr, responseCall, depth) {
11178
11670
  if (node === responseCall) {
11179
11671
  return [];
11180
11672
  }
11181
- if (Node51.isCallExpression(node)) {
11673
+ if (Node52.isCallExpression(node)) {
11182
11674
  const hop = thenLikeCall(node);
11183
11675
  if (hop !== null) {
11184
11676
  if (hop.method === "catch") {
@@ -11210,14 +11702,14 @@ function prefixOfMemberExpr(expr, responseCall, depth) {
11210
11702
  return null;
11211
11703
  }
11212
11704
  const decl = chain.root.getSymbol()?.getDeclarations()?.[0];
11213
- if (decl === void 0 || !Node51.isParameterDeclaration(decl)) {
11705
+ if (decl === void 0 || !Node52.isParameterDeclaration(decl)) {
11214
11706
  return null;
11215
11707
  }
11216
11708
  const base = prefixOfThenParam(decl, responseCall, depth + 1);
11217
11709
  return base === null ? null : [...base, ...chain.segments];
11218
11710
  }
11219
11711
  function firstCallbackParamName(callback) {
11220
- if (callback === void 0 || !(Node51.isArrowFunction(callback) || Node51.isFunctionExpression(callback))) {
11712
+ if (callback === void 0 || !(Node52.isArrowFunction(callback) || Node52.isFunctionExpression(callback))) {
11221
11713
  return null;
11222
11714
  }
11223
11715
  const param = callback.getParameters()[0];
@@ -11225,11 +11717,11 @@ function firstCallbackParamName(callback) {
11225
11717
  return null;
11226
11718
  }
11227
11719
  const nameNode = param.getNameNode();
11228
- return Node51.isIdentifier(nameNode) ? nameNode.getText() : null;
11720
+ return Node52.isIdentifier(nameNode) ? nameNode.getText() : null;
11229
11721
  }
11230
11722
  function unwrap2(expr) {
11231
11723
  let node = expr;
11232
- while (Node51.isAwaitExpression(node) || Node51.isParenthesizedExpression(node) || Node51.isAsExpression(node)) {
11724
+ while (Node52.isAwaitExpression(node) || Node52.isParenthesizedExpression(node) || Node52.isAsExpression(node)) {
11233
11725
  node = node.getExpression();
11234
11726
  }
11235
11727
  return node;
@@ -11239,15 +11731,15 @@ function chainRootedAt(expr, rootName) {
11239
11731
  if (chain === null) {
11240
11732
  return null;
11241
11733
  }
11242
- return Node51.isIdentifier(chain.root) && chain.root.getText() === rootName ? chain.segments : null;
11734
+ return Node52.isIdentifier(chain.root) && chain.root.getText() === rootName ? chain.segments : null;
11243
11735
  }
11244
11736
  function memberChain(expr) {
11245
- let node = Node51.isExpression(expr) ? unwrap2(expr) : expr;
11246
- if (Node51.isCallExpression(node)) {
11737
+ let node = Node52.isExpression(expr) ? unwrap2(expr) : expr;
11738
+ if (Node52.isCallExpression(node)) {
11247
11739
  node = node.getExpression();
11248
11740
  }
11249
11741
  const segments = [];
11250
- while (Node51.isPropertyAccessExpression(node)) {
11742
+ while (Node52.isPropertyAccessExpression(node)) {
11251
11743
  segments.unshift(node.getName());
11252
11744
  node = node.getExpression();
11253
11745
  }
@@ -11266,16 +11758,16 @@ function collectPropertyAccesses(subtree, accessor) {
11266
11758
  if (accessor.kind === "identifier") {
11267
11759
  const varName = accessor.name;
11268
11760
  subtree.forEachDescendant((node) => {
11269
- if (!Node51.isPropertyAccessExpression(node)) {
11761
+ if (!Node52.isPropertyAccessExpression(node)) {
11270
11762
  return;
11271
11763
  }
11272
11764
  const chain = [];
11273
11765
  let current2 = node;
11274
- while (Node51.isPropertyAccessExpression(current2)) {
11766
+ while (Node52.isPropertyAccessExpression(current2)) {
11275
11767
  chain.unshift(current2.getName());
11276
11768
  current2 = current2.getExpression();
11277
11769
  }
11278
- if (Node51.isIdentifier(current2) && current2.getText() === varName) {
11770
+ if (Node52.isIdentifier(current2) && current2.getText() === varName) {
11279
11771
  pushChain(chain);
11280
11772
  }
11281
11773
  });
@@ -11286,7 +11778,7 @@ function collectPropertyAccesses(subtree, accessor) {
11286
11778
  return source === void 0 ? void 0 : [source];
11287
11779
  } : (name) => accessor.bindings.get(name);
11288
11780
  subtree.forEachDescendant((node) => {
11289
- if (!Node51.isIdentifier(node)) {
11781
+ if (!Node52.isIdentifier(node)) {
11290
11782
  return;
11291
11783
  }
11292
11784
  const prefix = prefixOf(node.getText());
@@ -11298,13 +11790,13 @@ function collectPropertyAccesses(subtree, accessor) {
11298
11790
  }
11299
11791
  let chainTop = node;
11300
11792
  let parent = chainTop.getParent();
11301
- while (parent !== void 0 && Node51.isPropertyAccessExpression(parent) && parent.getExpression() === chainTop) {
11793
+ while (parent !== void 0 && Node52.isPropertyAccessExpression(parent) && parent.getExpression() === chainTop) {
11302
11794
  chainTop = parent;
11303
11795
  parent = chainTop.getParent();
11304
11796
  }
11305
11797
  const segments = [];
11306
11798
  let current2 = chainTop;
11307
- while (Node51.isPropertyAccessExpression(current2)) {
11799
+ while (Node52.isPropertyAccessExpression(current2)) {
11308
11800
  segments.push(current2.getName());
11309
11801
  current2 = current2.getExpression();
11310
11802
  }
@@ -11318,15 +11810,15 @@ function isBindingDeclarationSite(node) {
11318
11810
  return true;
11319
11811
  }
11320
11812
  const parent = node.getParent();
11321
- return parent !== void 0 && Node51.isParameterDeclaration(parent) && parent.getNameNode() === node;
11813
+ return parent !== void 0 && Node52.isParameterDeclaration(parent) && parent.getNameNode() === node;
11322
11814
  }
11323
11815
  function isInsideObjectBindingPattern(node) {
11324
11816
  let current2 = node.getParent();
11325
11817
  while (current2 !== void 0) {
11326
- if (Node51.isObjectBindingPattern(current2)) {
11818
+ if (Node52.isObjectBindingPattern(current2)) {
11327
11819
  return true;
11328
11820
  }
11329
- if (Node51.isFunctionLikeDeclaration(current2) || Node51.isVariableStatement(current2)) {
11821
+ if (Node52.isFunctionLikeDeclaration(current2) || Node52.isVariableStatement(current2)) {
11330
11822
  break;
11331
11823
  }
11332
11824
  current2 = current2.getParent();
@@ -11389,7 +11881,7 @@ function findBranchSubtree(func, byLineRange, terminalStartLine, terminalEndLine
11389
11881
  if (parent === void 0) {
11390
11882
  break;
11391
11883
  }
11392
- if (Node51.isIfStatement(parent)) {
11884
+ if (Node52.isIfStatement(parent)) {
11393
11885
  const ifStmt = parent;
11394
11886
  const thenBlock = ifStmt.getThenStatement();
11395
11887
  const elseBlock = ifStmt.getElseStatement();
@@ -11452,7 +11944,7 @@ function buildNonBodyPropertySet(semantics) {
11452
11944
 
11453
11945
  // src/subUnitContext.ts
11454
11946
  import {
11455
- Node as Node52
11947
+ Node as Node53
11456
11948
  } from "ts-morph";
11457
11949
  function createTsSubUnitContext() {
11458
11950
  return {
@@ -11466,7 +11958,7 @@ function createTsSubUnitContext() {
11466
11958
  }
11467
11959
  function skipNestedFunctions(parent) {
11468
11960
  return (node, traversal) => {
11469
- if (node !== parent && (Node52.isFunctionDeclaration(node) || Node52.isFunctionExpression(node) || Node52.isArrowFunction(node) || Node52.isMethodDeclaration(node))) {
11961
+ if (node !== parent && (Node53.isFunctionDeclaration(node) || Node53.isFunctionExpression(node) || Node53.isArrowFunction(node) || Node53.isMethodDeclaration(node))) {
11470
11962
  traversal.skip();
11471
11963
  return true;
11472
11964
  }
@@ -11480,18 +11972,18 @@ function findJsxAttributes(parent) {
11480
11972
  if (skip(node, traversal)) {
11481
11973
  return;
11482
11974
  }
11483
- if (!Node52.isJsxOpeningElement(node) && !Node52.isJsxSelfClosingElement(node)) {
11975
+ if (!Node53.isJsxOpeningElement(node) && !Node53.isJsxSelfClosingElement(node)) {
11484
11976
  return;
11485
11977
  }
11486
11978
  const tag = node.getTagNameNode().getText();
11487
11979
  for (const attr of node.getAttributes()) {
11488
- if (!Node52.isJsxAttribute(attr)) {
11980
+ if (!Node53.isJsxAttribute(attr)) {
11489
11981
  continue;
11490
11982
  }
11491
11983
  const initializer = attr.getInitializer();
11492
11984
  let valueExpression = null;
11493
11985
  if (initializer !== void 0) {
11494
- if (Node52.isJsxExpression(initializer)) {
11986
+ if (Node53.isJsxExpression(initializer)) {
11495
11987
  const inner = initializer.getExpression();
11496
11988
  valueExpression = inner ?? null;
11497
11989
  } else {
@@ -11514,11 +12006,11 @@ function findCallExpressionsByName(parent, name) {
11514
12006
  if (skip(node, traversal)) {
11515
12007
  return;
11516
12008
  }
11517
- if (!Node52.isCallExpression(node)) {
12009
+ if (!Node53.isCallExpression(node)) {
11518
12010
  return;
11519
12011
  }
11520
12012
  const callee = node.getExpression();
11521
- if (Node52.isIdentifier(callee) && callee.getText() === name) {
12013
+ if (Node53.isIdentifier(callee) && callee.getText() === name) {
11522
12014
  out.push(node);
11523
12015
  }
11524
12016
  });
@@ -11529,10 +12021,10 @@ function resolveAttributeValueFunction(attr, parent) {
11529
12021
  if (expr === null) {
11530
12022
  return null;
11531
12023
  }
11532
- if (Node52.isArrowFunction(expr) || Node52.isFunctionExpression(expr)) {
12024
+ if (Node53.isArrowFunction(expr) || Node53.isFunctionExpression(expr)) {
11533
12025
  return { func: expr, localName: null };
11534
12026
  }
11535
- if (Node52.isIdentifier(expr)) {
12027
+ if (Node53.isIdentifier(expr)) {
11536
12028
  const symbol = expr.getSymbol();
11537
12029
  if (symbol === void 0) {
11538
12030
  return null;
@@ -11551,15 +12043,15 @@ function resolveAttributeValueFunction(attr, parent) {
11551
12043
  return null;
11552
12044
  }
11553
12045
  function tryExtractFunction(decl) {
11554
- if (Node52.isFunctionDeclaration(decl)) {
12046
+ if (Node53.isFunctionDeclaration(decl)) {
11555
12047
  return decl;
11556
12048
  }
11557
- if (Node52.isVariableDeclaration(decl)) {
12049
+ if (Node53.isVariableDeclaration(decl)) {
11558
12050
  const init = decl.getInitializer();
11559
12051
  if (init === void 0) {
11560
12052
  return null;
11561
12053
  }
11562
- if (Node52.isArrowFunction(init) || Node52.isFunctionExpression(init)) {
12054
+ if (Node53.isArrowFunction(init) || Node53.isFunctionExpression(init)) {
11563
12055
  return init;
11564
12056
  }
11565
12057
  }
@@ -11580,7 +12072,7 @@ function getCallArgumentFunction(call, position) {
11580
12072
  if (arg === void 0) {
11581
12073
  return null;
11582
12074
  }
11583
- if (Node52.isArrowFunction(arg) || Node52.isFunctionExpression(arg)) {
12075
+ if (Node53.isArrowFunction(arg) || Node53.isFunctionExpression(arg)) {
11584
12076
  return arg;
11585
12077
  }
11586
12078
  return null;
@@ -11592,7 +12084,7 @@ function readArrayLiteralText(node) {
11592
12084
  if (node === null) {
11593
12085
  return null;
11594
12086
  }
11595
- if (Node52.isArrayLiteralExpression(node)) {
12087
+ if (Node53.isArrayLiteralExpression(node)) {
11596
12088
  return node.getElements().map((e) => e.getText());
11597
12089
  }
11598
12090
  return [node.getText()];
@@ -11817,13 +12309,13 @@ var raise = (msg) => {
11817
12309
  throw new Error(msg);
11818
12310
  };
11819
12311
  function bindingPatternNames(nameNode) {
11820
- if (Node53.isObjectBindingPattern(nameNode)) {
12312
+ if (Node54.isObjectBindingPattern(nameNode)) {
11821
12313
  return nameNode.getElements().map((e) => e.getName());
11822
12314
  }
11823
- if (Node53.isArrayBindingPattern(nameNode)) {
12315
+ if (Node54.isArrayBindingPattern(nameNode)) {
11824
12316
  const names = [];
11825
12317
  for (const element of nameNode.getElements()) {
11826
- if (Node53.isOmittedExpression(element)) {
12318
+ if (Node54.isOmittedExpression(element)) {
11827
12319
  continue;
11828
12320
  }
11829
12321
  names.push(element.getName());
@@ -11834,7 +12326,7 @@ function bindingPatternNames(nameNode) {
11834
12326
  }
11835
12327
  function componentPropsParameters(param, mapping) {
11836
12328
  const nameNode = param.getNameNode();
11837
- if (!Node53.isObjectBindingPattern(nameNode)) {
12329
+ if (!Node54.isObjectBindingPattern(nameNode)) {
11838
12330
  const type = param.getType();
11839
12331
  return [
11840
12332
  {
@@ -11960,11 +12452,11 @@ function extractParameters(func, inputMapping) {
11960
12452
  function extractDependencyCalls(func) {
11961
12453
  const results = [];
11962
12454
  func.forEachDescendant((node, traversal) => {
11963
- if (node !== func && (Node53.isFunctionDeclaration(node) || Node53.isFunctionExpression(node) || Node53.isArrowFunction(node) || Node53.isMethodDeclaration(node))) {
12455
+ if (node !== func && (Node54.isFunctionDeclaration(node) || Node54.isFunctionExpression(node) || Node54.isArrowFunction(node) || Node54.isMethodDeclaration(node))) {
11964
12456
  traversal.skip();
11965
12457
  return;
11966
12458
  }
11967
- if (!Node53.isVariableDeclaration(node)) {
12459
+ if (!Node54.isVariableDeclaration(node)) {
11968
12460
  return;
11969
12461
  }
11970
12462
  const init = node.getInitializer();
@@ -11973,19 +12465,19 @@ function extractDependencyCalls(func) {
11973
12465
  }
11974
12466
  let isAsync = false;
11975
12467
  let callExpr = init;
11976
- if (Node53.isAwaitExpression(init)) {
12468
+ if (Node54.isAwaitExpression(init)) {
11977
12469
  isAsync = true;
11978
12470
  callExpr = init.getExpression();
11979
12471
  }
11980
- if (!Node53.isCallExpression(callExpr)) {
12472
+ if (!Node54.isCallExpression(callExpr)) {
11981
12473
  return;
11982
12474
  }
11983
12475
  const calleeName = callExpr.getExpression().getText();
11984
12476
  const nameNode = node.getNameNode();
11985
- const assignedTo = Node53.isIdentifier(nameNode) ? nameNode.getText() : null;
12477
+ const assignedTo = Node54.isIdentifier(nameNode) ? nameNode.getText() : null;
11986
12478
  const declList = node.getParent();
11987
12479
  const varStmt = declList?.getParent();
11988
- const locationNode = varStmt !== void 0 && Node53.isVariableStatement(varStmt) ? varStmt : node;
12480
+ const locationNode = varStmt !== void 0 && Node54.isVariableStatement(varStmt) ? varStmt : node;
11989
12481
  results.push({
11990
12482
  name: calleeName,
11991
12483
  assignedTo,
@@ -12111,6 +12603,36 @@ function extractCodeStructure(unit, pack, invocationRecognizers = [], accessReco
12111
12603
  function responseAccessorNames(pack, kind) {
12112
12604
  return pack.responseSemantics?.filter((m) => m.semantics.type === kind).map((m) => m.name);
12113
12605
  }
12606
+ var ACCESSOR_FIELD_SEMANTICS = {
12607
+ bodyAccessors: "body",
12608
+ statusAccessors: "statusCode",
12609
+ successAccessors: "statusRange"
12610
+ };
12611
+ function responseAccessors(pack) {
12612
+ const out = {};
12613
+ for (const [field, kind] of Object.entries(ACCESSOR_FIELD_SEMANTICS)) {
12614
+ const names = responseAccessorNames(pack, kind);
12615
+ if (names !== void 0 && names.length > 0) {
12616
+ out[field] = names;
12617
+ }
12618
+ }
12619
+ return out;
12620
+ }
12621
+ var FUNCTION_FALLTHROUGH_TERMINAL = {
12622
+ kind: "return",
12623
+ match: { type: "functionFallthrough" },
12624
+ extraction: {}
12625
+ };
12626
+ function terminalsFor(unit, pack) {
12627
+ const declared = unit.terminals ?? pack.terminals;
12628
+ if (unit.callSite === void 0) {
12629
+ return declared;
12630
+ }
12631
+ if (declared.some((p) => p.match.type === "functionFallthrough")) {
12632
+ return declared;
12633
+ }
12634
+ return [...declared, FUNCTION_FALLTHROUGH_TERMINAL];
12635
+ }
12114
12636
  function readCodeStructure(unit, pack, invocationRecognizers, accessRecognizers, barriers, resolveWrittenValue) {
12115
12637
  const { func, kind, name } = unit;
12116
12638
  if (func === null) {
@@ -12122,7 +12644,7 @@ function readCodeStructure(unit, pack, invocationRecognizers, accessRecognizers,
12122
12644
  );
12123
12645
  const extracted = extractRawBranches(
12124
12646
  func,
12125
- unit.terminals ?? pack.terminals,
12647
+ terminalsFor(unit, pack),
12126
12648
  invocationRecognizers,
12127
12649
  accessRecognizers,
12128
12650
  barriers,
@@ -12158,8 +12680,7 @@ function readCodeStructure(unit, pack, invocationRecognizers, accessRecognizers,
12158
12680
  }
12159
12681
  }
12160
12682
  }
12161
- const bodyAccessors = unit.callSite !== void 0 ? responseAccessorNames(pack, "body") : void 0;
12162
- const statusAccessors = unit.callSite !== void 0 ? responseAccessorNames(pack, "statusCode") : void 0;
12683
+ const accessors = unit.callSite !== void 0 ? responseAccessors(pack) : {};
12163
12684
  return {
12164
12685
  identity: {
12165
12686
  name,
@@ -12183,8 +12704,8 @@ function readCodeStructure(unit, pack, invocationRecognizers, accessRecognizers,
12183
12704
  bodyContent: bodyContentOf(func),
12184
12705
  dependencyCalls: depCalls,
12185
12706
  declaredContract: null,
12186
- ...bodyAccessors !== void 0 && bodyAccessors.length > 0 ? { bodyAccessors } : {},
12187
- ...statusAccessors !== void 0 && statusAccessors.length > 0 ? { statusAccessors } : {}
12707
+ ...accessors,
12708
+ ...unit.callSite !== void 0 && pack.failureDelivery !== void 0 ? { failureDelivery: pack.failureDelivery } : {}
12188
12709
  };
12189
12710
  }
12190
12711
  function extractConsumerBinding(unit, pattern, pack, resolution) {
@@ -12241,9 +12762,9 @@ function extractBindingMethod(binding, callSite, pack, resolution) {
12241
12762
  if (m.type === "fromArgumentProperty") {
12242
12763
  const args = callSite.callExpression.getArguments();
12243
12764
  const arg = args[m.position];
12244
- if (arg !== void 0 && Node53.isObjectLiteralExpression(arg)) {
12765
+ if (arg !== void 0 && Node54.isObjectLiteralExpression(arg)) {
12245
12766
  const prop = arg.getProperty(m.property);
12246
- if (prop !== void 0 && Node53.isPropertyAssignment(prop)) {
12767
+ if (prop !== void 0 && Node54.isPropertyAssignment(prop)) {
12247
12768
  const init = prop.getInitializer();
12248
12769
  if (init !== void 0) {
12249
12770
  const value = stringValueOf(init, resolution);
@@ -12372,22 +12893,22 @@ function extractBindingPath(binding, callSite, pack, resolution) {
12372
12893
  return void 0;
12373
12894
  }
12374
12895
  function pathFromUrlNode(node) {
12375
- if (Node53.isStringLiteral(node)) {
12896
+ if (Node54.isStringLiteral(node)) {
12376
12897
  return pathFromLiteralUrl(node.getLiteralValue());
12377
12898
  }
12378
- if (Node53.isNoSubstitutionTemplateLiteral(node)) {
12899
+ if (Node54.isNoSubstitutionTemplateLiteral(node)) {
12379
12900
  return pathFromLiteralUrl(node.getLiteralValue());
12380
12901
  }
12381
- if (Node53.isTemplateExpression(node)) {
12902
+ if (Node54.isTemplateExpression(node)) {
12382
12903
  return pathFromTemplateUrl(node);
12383
12904
  }
12384
12905
  return void 0;
12385
12906
  }
12386
12907
  function placeholderName(expr) {
12387
- if (Node53.isIdentifier(expr)) {
12908
+ if (Node54.isIdentifier(expr)) {
12388
12909
  return expr.getText();
12389
12910
  }
12390
- if (Node53.isPropertyAccessExpression(expr)) {
12911
+ if (Node54.isPropertyAccessExpression(expr)) {
12391
12912
  return expr.getName();
12392
12913
  }
12393
12914
  return "param";
@@ -12538,12 +13059,20 @@ function extractFromSourceFile(sourceFile, frameworks, claimedUnits, options, ta
12538
13059
  if (claimant.pack === pack.name && claimant.file === sourceFile.getFilePath() && tally !== void 0) {
12539
13060
  tally.selfCollisions += 1;
12540
13061
  }
13062
+ if (claimant.file !== sourceFile.getFilePath()) {
13063
+ recordFileDependency(claimant.file);
13064
+ }
12541
13065
  continue;
12542
13066
  }
12543
13067
  claimed.set(claimKey, {
12544
13068
  pack: pack.name,
12545
13069
  file: sourceFile.getFilePath()
12546
13070
  });
13071
+ recordUnitClaim(claimKey, pack.name);
13072
+ const declaringFile = unit.func?.getSourceFile().getFilePath();
13073
+ if (declaringFile !== void 0 && declaringFile !== sourceFile.getFilePath()) {
13074
+ recordFileDependency(declaringFile);
13075
+ }
12547
13076
  unitsWalkedHere += 1;
12548
13077
  if (tally !== void 0) {
12549
13078
  tally.unitsClaimed += 1;
@@ -12582,6 +13111,9 @@ function extractFromSourceFile(sourceFile, frameworks, claimedUnits, options, ta
12582
13111
  }
12583
13112
  if (unit.resolverInfo.schemaSdl !== void 0) {
12584
13113
  raw.graphqlSchemaSdl = unit.resolverInfo.schemaSdl;
13114
+ if (unit.resolverInfo.schemaDocument !== void 0) {
13115
+ raw.sourceDocumentLabel = unit.resolverInfo.schemaDocument;
13116
+ }
12585
13117
  if (unit.resolverInfo.typeName !== null) {
12586
13118
  const contract = deriveGraphqlContract(
12587
13119
  unit.resolverInfo.schemaSdl,
@@ -12770,6 +13302,37 @@ function fileImporting(project, modulePrefix) {
12770
13302
  }
12771
13303
  return void 0;
12772
13304
  }
13305
+ function liftSchemasOntoDocuments(summaries) {
13306
+ const sdlByDocument = /* @__PURE__ */ new Map();
13307
+ for (const summary of summaries) {
13308
+ const label = readSourceDocumentMetadata(summary)?.label;
13309
+ const graphql = readGraphqlMetadata2(summary);
13310
+ if (label === void 0 || graphql?.schemaSdl === void 0) {
13311
+ continue;
13312
+ }
13313
+ sdlByDocument.set(label, graphql.schemaSdl);
13314
+ const { schemaSdl: _movedToTheDocument, ...rest } = graphql;
13315
+ summary.metadata = withGraphqlMetadata2(summary.metadata, rest);
13316
+ }
13317
+ for (const [label, schemaSdl] of sdlByDocument) {
13318
+ summaries.push(schemaDocumentSummary(label, schemaSdl));
13319
+ }
13320
+ }
13321
+ function schemaDocumentSummary(label, schemaSdl) {
13322
+ return {
13323
+ kind: "library",
13324
+ location: { file: label, range: { start: 0, end: 0 }, exportName: null },
13325
+ identity: { name: label, exportPath: null, boundaryBinding: null },
13326
+ inputs: [],
13327
+ transitions: [],
13328
+ gaps: [],
13329
+ confidence: { source: "declared", level: "high" },
13330
+ metadata: withGraphqlMetadata2(
13331
+ withSourceDocumentMetadata(void 0, { label }),
13332
+ { schemaSdl }
13333
+ )
13334
+ };
13335
+ }
12773
13336
  function libraryEnvReadMarker(pack, declared, importer) {
12774
13337
  return {
12775
13338
  kind: "library",
@@ -12822,7 +13385,7 @@ function expandWrapperCallers(summaries, project, options) {
12822
13385
  continue;
12823
13386
  }
12824
13387
  const binding = s.identity.boundaryBinding;
12825
- if (binding === null || binding.semantics.name !== "rest" || binding.semantics.method === null || binding.semantics.path !== null) {
13388
+ if (binding === null || binding.semantics.name !== "rest" || binding.semantics.method === null) {
12826
13389
  continue;
12827
13390
  }
12828
13391
  lookup ??= createSourceFileLookup(project);
@@ -12850,11 +13413,21 @@ function findWrapperPathParam(summary, lookup) {
12850
13413
  if (func === null) {
12851
13414
  return null;
12852
13415
  }
12853
- const params = func.getParameters();
12854
- if (params.length === 0) {
13416
+ const names = func.getParameters().map((p) => p.getName());
13417
+ if (names.length === 0) {
12855
13418
  return null;
12856
13419
  }
12857
- return { func, pathParamPosition: 0 };
13420
+ for (const call of func.getDescendantsOfKind(SyntaxKind11.CallExpression)) {
13421
+ const first = call.getArguments()[0];
13422
+ if (first === void 0 || !Node54.isIdentifier(first)) {
13423
+ continue;
13424
+ }
13425
+ const at = names.indexOf(first.getText());
13426
+ if (at !== -1) {
13427
+ return { func, pathParamPosition: at };
13428
+ }
13429
+ }
13430
+ return null;
12858
13431
  }
12859
13432
  function synthesizeCallerSummaries(wrapper, _project, options) {
12860
13433
  const nameNode = wrapperNameNode(wrapper.func);
@@ -12895,16 +13468,16 @@ function synthesizeCallerSummaries(wrapper, _project, options) {
12895
13468
  return out;
12896
13469
  }
12897
13470
  function wrapperNameNode(func) {
12898
- if (Node53.isFunctionDeclaration(func) || Node53.isMethodDeclaration(func)) {
13471
+ if (Node54.isFunctionDeclaration(func) || Node54.isMethodDeclaration(func)) {
12899
13472
  const name = func.getNameNode();
12900
- if (name !== void 0 && Node53.isIdentifier(name)) {
13473
+ if (name !== void 0 && Node54.isIdentifier(name)) {
12901
13474
  return name;
12902
13475
  }
12903
13476
  }
12904
13477
  const parent = func.getParent();
12905
- if (parent !== void 0 && Node53.isVariableDeclaration(parent)) {
13478
+ if (parent !== void 0 && Node54.isVariableDeclaration(parent)) {
12906
13479
  const nameNode = parent.getNameNode();
12907
- if (Node53.isIdentifier(nameNode)) {
13480
+ if (Node54.isIdentifier(nameNode)) {
12908
13481
  return nameNode;
12909
13482
  }
12910
13483
  }
@@ -12913,10 +13486,10 @@ function wrapperNameNode(func) {
12913
13486
  function enclosingCall(node) {
12914
13487
  let current2 = node.getParent();
12915
13488
  while (current2 !== void 0) {
12916
- if (Node53.isCallExpression(current2)) {
13489
+ if (Node54.isCallExpression(current2)) {
12917
13490
  return current2;
12918
13491
  }
12919
- if (Node53.isPropertyAccessExpression(current2)) {
13492
+ if (Node54.isPropertyAccessExpression(current2)) {
12920
13493
  current2 = current2.getParent();
12921
13494
  continue;
12922
13495
  }
@@ -12927,7 +13500,7 @@ function enclosingCall(node) {
12927
13500
  function enclosingFunction(node) {
12928
13501
  let current2 = node.getParent();
12929
13502
  while (current2 !== void 0) {
12930
- if (Node53.isFunctionDeclaration(current2) || Node53.isFunctionExpression(current2) || Node53.isArrowFunction(current2) || Node53.isMethodDeclaration(current2)) {
13503
+ if (Node54.isFunctionDeclaration(current2) || Node54.isFunctionExpression(current2) || Node54.isArrowFunction(current2) || Node54.isMethodDeclaration(current2)) {
12931
13504
  return current2;
12932
13505
  }
12933
13506
  current2 = current2.getParent();
@@ -12935,13 +13508,13 @@ function enclosingFunction(node) {
12935
13508
  return null;
12936
13509
  }
12937
13510
  function literalOrTemplate(arg) {
12938
- if (Node53.isStringLiteral(arg)) {
13511
+ if (Node54.isStringLiteral(arg)) {
12939
13512
  return arg.getLiteralValue();
12940
13513
  }
12941
- if (Node53.isNoSubstitutionTemplateLiteral(arg)) {
13514
+ if (Node54.isNoSubstitutionTemplateLiteral(arg)) {
12942
13515
  return arg.getLiteralValue();
12943
13516
  }
12944
- if (Node53.isTemplateExpression(arg)) {
13517
+ if (Node54.isTemplateExpression(arg)) {
12945
13518
  let path11 = arg.getHead().getLiteralText();
12946
13519
  for (const span of arg.getTemplateSpans()) {
12947
13520
  path11 += `{${placeholderName(span.getExpression())}}`;
@@ -12952,13 +13525,13 @@ function literalOrTemplate(arg) {
12952
13525
  return void 0;
12953
13526
  }
12954
13527
  function callerName(func) {
12955
- if (Node53.isFunctionDeclaration(func) || Node53.isMethodDeclaration(func)) {
13528
+ if (Node54.isFunctionDeclaration(func) || Node54.isMethodDeclaration(func)) {
12956
13529
  return func.getName() ?? "anonymous";
12957
13530
  }
12958
13531
  const parent = func.getParent();
12959
- if (parent !== void 0 && Node53.isVariableDeclaration(parent)) {
13532
+ if (parent !== void 0 && Node54.isVariableDeclaration(parent)) {
12960
13533
  const nameNode = parent.getNameNode();
12961
- if (Node53.isIdentifier(nameNode)) {
13534
+ if (Node54.isIdentifier(nameNode)) {
12962
13535
  return nameNode.getText();
12963
13536
  }
12964
13537
  }
@@ -13010,8 +13583,8 @@ function buildCallerSummary(wrapper, callerFunc, callExpr, path11, options) {
13010
13583
  summary.confidence = { source: "inferred_static", level: "low" };
13011
13584
  return summary;
13012
13585
  }
13013
- function named(summaries, workspace) {
13014
- const projectRoot = commonDirectoryOf(summaries.map((s) => s.location.file));
13586
+ function named(summaries, workspace, storedProjectRoot) {
13587
+ const projectRoot = storedProjectRoot ?? commonDirectoryOf(summaries.map((s) => s.location.file));
13015
13588
  nameSummaries(summaries, {
13016
13589
  workspace: workspace ?? workspaceNameFor(projectRoot),
13017
13590
  projectRoot
@@ -13142,7 +13715,29 @@ function createTypeScriptAdapter(config) {
13142
13715
  if (config.onTiming !== void 0) {
13143
13716
  config.onTiming(timer.report());
13144
13717
  }
13145
- return named(lookup.summaries, config.workspace);
13718
+ return named(lookup.summaries, config.workspace, lookup.projectRoot);
13719
+ }
13720
+ const plan = lookup.diagnostic.missReason === "files-changed" ? await timer.timeAsync("cache.plan", () => cache.plan(cacheInput)) : null;
13721
+ if (plan !== null && plan.changed.size === 0 && plan.removed.size === 0) {
13722
+ try {
13723
+ await cache.write(
13724
+ cacheInput,
13725
+ plan.allSummaries(),
13726
+ plan.attribution()
13727
+ );
13728
+ } catch {
13729
+ }
13730
+ if (config.onCacheDiagnostic !== void 0) {
13731
+ config.onCacheDiagnostic({ kind: "hit" });
13732
+ }
13733
+ if (config.onTiming !== void 0) {
13734
+ config.onTiming(timer.report());
13735
+ }
13736
+ return named(
13737
+ plan.allSummaries(),
13738
+ config.workspace,
13739
+ plan.storedProjectRoot ?? void 0
13740
+ );
13146
13741
  }
13147
13742
  let candidatePaths = null;
13148
13743
  if (lazyEligible) {
@@ -13173,16 +13768,58 @@ function createTypeScriptAdapter(config) {
13173
13768
  "mountPrefix",
13174
13769
  () => buildMountPrefixIndex(packsByFile, resolution)
13175
13770
  );
13771
+ const caching = cacheDir !== null;
13772
+ const validRoots = /* @__PURE__ */ new Set();
13773
+ if (plan !== null) {
13774
+ const packNamesByPath = /* @__PURE__ */ new Map();
13775
+ for (const [sf, packs] of packsByFile) {
13776
+ packNamesByPath.set(
13777
+ sf.getFilePath(),
13778
+ packs.map((p) => p.name).sort().join(",")
13779
+ );
13780
+ }
13781
+ for (const rootPath of plan.validRoots) {
13782
+ const record = plan.roots.get(rootPath);
13783
+ if (record === void 0) {
13784
+ continue;
13785
+ }
13786
+ if (!mountAssumptionsAgree(record, mountPrefixes)) {
13787
+ continue;
13788
+ }
13789
+ const stored = [...record.packs].sort().join(",");
13790
+ if ((packNamesByPath.get(rootPath) ?? "") !== stored) {
13791
+ continue;
13792
+ }
13793
+ validRoots.add(rootPath);
13794
+ }
13795
+ }
13796
+ const reused = plan !== null && validRoots.size > 0 ? plan.reuse(validRoots) : null;
13176
13797
  const claimedUnits = /* @__PURE__ */ new Map();
13798
+ if (reused !== null && plan !== null) {
13799
+ for (const rootPath of validRoots) {
13800
+ for (const claim of plan.roots.get(rootPath)?.claims ?? []) {
13801
+ claimedUnits.set(claim.key, { pack: claim.pack, file: rootPath });
13802
+ }
13803
+ }
13804
+ }
13805
+ const walkList = reused === null ? sourceFiles : sourceFiles.filter((sf) => !validRoots.has(sf.getFilePath()));
13806
+ const sinkByRoot = /* @__PURE__ */ new Map();
13807
+ const ownersBySummary = /* @__PURE__ */ new Map();
13177
13808
  timer.time("extract per-file", () => {
13178
- for (const sourceFile of sourceFiles) {
13809
+ for (const sourceFile of walkList) {
13810
+ const rootPath = sourceFile.getFilePath();
13811
+ const sink = createDependencySink();
13812
+ if (caching) {
13813
+ sinkByRoot.set(rootPath, sink);
13814
+ }
13179
13815
  const applicablePacks = packsByFile.get(sourceFile);
13180
13816
  if (applicablePacks === void 0) {
13181
13817
  continue;
13182
13818
  }
13183
13819
  try {
13184
- summaries.push(
13185
- ...extractFromSourceFile(
13820
+ const walked = withDependencySink(
13821
+ sink,
13822
+ () => extractFromSourceFile(
13186
13823
  sourceFile,
13187
13824
  applicablePacks,
13188
13825
  claimedUnits,
@@ -13192,6 +13829,10 @@ function createTypeScriptAdapter(config) {
13192
13829
  mountPrefixes
13193
13830
  )
13194
13831
  );
13832
+ for (const summary of walked) {
13833
+ ownersBySummary.set(summary, /* @__PURE__ */ new Set([rootPath]));
13834
+ }
13835
+ summaries.push(...walked);
13195
13836
  } catch (error) {
13196
13837
  if (!(error instanceof RangeError)) {
13197
13838
  throw error;
@@ -13200,23 +13841,32 @@ function createTypeScriptAdapter(config) {
13200
13841
  }
13201
13842
  }
13202
13843
  });
13844
+ const wrapperInput = reused === null ? summaries : [...summaries, ...reused.summaries];
13203
13845
  const withWrappers = timer.time(
13204
13846
  "expandWrapperCallers",
13205
- () => expandWrapperCallers(summaries, project, config.extractorOptions)
13847
+ () => expandWrapperCallers(wrapperInput, project, config.extractorOptions)
13206
13848
  );
13849
+ const pipeline = reused === null ? withWrappers : [...summaries, ...withWrappers.slice(wrapperInput.length)];
13207
13850
  const withSubUnits = timer.time(
13208
13851
  "synthesizeSubUnits",
13209
13852
  () => synthesizeSubUnits(
13210
- withWrappers,
13853
+ pipeline,
13211
13854
  project,
13212
13855
  config.frameworks,
13213
13856
  config.extractorOptions,
13214
- tallies
13857
+ tallies,
13858
+ (created, parent) => {
13859
+ const parentOwners = ownersBySummary.get(parent);
13860
+ if (parentOwners !== void 0) {
13861
+ ownersBySummary.set(created, new Set(parentOwners));
13862
+ }
13863
+ }
13215
13864
  )
13216
13865
  );
13217
13866
  const closureFacts = {
13218
13867
  db: new Database5(),
13219
- unitKeyBySummary: /* @__PURE__ */ new Map()
13868
+ unitKeyBySummary: /* @__PURE__ */ new Map(),
13869
+ ...caching ? { filesByKey: /* @__PURE__ */ new Map() } : {}
13220
13870
  };
13221
13871
  const withClosure = config.includeReachable !== false ? timer.time(
13222
13872
  "expandReachableClosure",
@@ -13234,12 +13884,28 @@ function createTypeScriptAdapter(config) {
13234
13884
  access: collectAccessRecognizers(config.frameworks, tallies),
13235
13885
  resolveWrittenValue: (value) => resolution.resolveWrittenValue(value),
13236
13886
  resolveCallable: (value, alsoFrom) => resolution.resolveCallable(value, alsoFrom)
13237
- }
13887
+ },
13888
+ // Reached units the cache already serves emit nothing,
13889
+ // the way a cold run's seeds do not.
13890
+ reused?.summaries ?? []
13238
13891
  )
13239
13892
  ) : withSubUnits;
13893
+ const closureOwnership = caching && config.includeReachable !== false ? timer.time(
13894
+ "cache.attributeClosure",
13895
+ () => attributeReachedSummaries(
13896
+ withSubUnits,
13897
+ withClosure.slice(withSubUnits.length),
13898
+ closureFacts,
13899
+ ownersBySummary
13900
+ )
13901
+ ) : null;
13902
+ const merged = timer.time(
13903
+ "cache.merge",
13904
+ () => mergeWithReused(reused, withClosure, ownersBySummary)
13905
+ );
13240
13906
  const enriched = timer.time(
13241
13907
  "enrichRethrows",
13242
- () => enrichRethrows(withClosure, project, closureFacts)
13908
+ () => enrichRethrows(merged.summaries, project, closureFacts)
13243
13909
  );
13244
13910
  if (config.includeReachable !== false) {
13245
13911
  timer.time(
@@ -13255,6 +13921,10 @@ function createTypeScriptAdapter(config) {
13255
13921
  "emitLibraryEnvReadMarkers",
13256
13922
  () => emitLibraryEnvReadMarkers(enriched, project, config.frameworks)
13257
13923
  );
13924
+ timer.time(
13925
+ "liftSchemasOntoDocuments",
13926
+ () => liftSchemasOntoDocuments(enriched)
13927
+ );
13258
13928
  timer.time(
13259
13929
  "stampGraphqlClientRefs",
13260
13930
  () => stampGraphqlClientRefs(
@@ -13264,12 +13934,45 @@ function createTypeScriptAdapter(config) {
13264
13934
  resolution
13265
13935
  )
13266
13936
  );
13937
+ if (config.onCacheDiagnostic !== void 0 && plan !== null) {
13938
+ config.onCacheDiagnostic({
13939
+ kind: "partial",
13940
+ partial: {
13941
+ filesChanged: plan.changed.size,
13942
+ filesRemoved: plan.removed.size,
13943
+ rootsReused: validRoots.size,
13944
+ rootsReextracted: walkList.length,
13945
+ rootsDeclined: plan.rootsDeclined,
13946
+ summariesReused: merged.reusedKept.length
13947
+ }
13948
+ });
13949
+ }
13950
+ const runProjectRoot = commonDirectoryOf(
13951
+ sourceFiles.map((f) => f.getFilePath())
13952
+ );
13267
13953
  await timer.timeAsync("cache.write", async () => {
13268
- if (enriched.length === 0) {
13954
+ if (!caching || enriched.length === 0) {
13269
13955
  return;
13270
13956
  }
13271
13957
  try {
13272
- await cache.write(cacheInput, enriched);
13958
+ const attribution = buildCacheAttribution({
13959
+ project,
13960
+ plan,
13961
+ validRoots,
13962
+ sinkByRoot,
13963
+ ownersBySummary,
13964
+ closureOwnership,
13965
+ unitKeyBySummary: closureFacts.unitKeyBySummary,
13966
+ filesByKey: closureFacts.filesByKey,
13967
+ packsByFile,
13968
+ reusedKept: merged.reusedKept,
13969
+ reusedOwners: merged.reusedOwners,
13970
+ // Everything after the reused prefix, the passes' own
13971
+ // additions (markers, schema documents) included.
13972
+ fresh: enriched.slice(merged.reusedKept.length),
13973
+ projectRoot: runProjectRoot
13974
+ });
13975
+ await cache.write(cacheInput, enriched, attribution);
13273
13976
  } catch {
13274
13977
  }
13275
13978
  });
@@ -13282,7 +13985,7 @@ function createTypeScriptAdapter(config) {
13282
13985
  packs: config.frameworks,
13283
13986
  tallies,
13284
13987
  filesInProject: tsconfigFileList?.length ?? null,
13285
- filesWalked: sourceFiles.length,
13988
+ filesWalked: walkList.length,
13286
13989
  summaries: enriched,
13287
13990
  tsConfigFilePath: config.tsConfigFilePath,
13288
13991
  projectRoot: commonDirectoryOf(
@@ -13302,7 +14005,200 @@ function createTypeScriptAdapter(config) {
13302
14005
  }
13303
14006
  };
13304
14007
  }
13305
- function synthesizeSubUnits(summaries, project, frameworks, options, tallies) {
14008
+ function mountAssumptionsAgree(record, index) {
14009
+ return Object.entries(record.mountPrefixes).every(
14010
+ ([childId, prefix]) => (index.prefixForId?.(childId) ?? "") === prefix
14011
+ );
14012
+ }
14013
+ function attributeReachedSummaries(seeds, reached, facts, owners) {
14014
+ const adjacency = /* @__PURE__ */ new Map();
14015
+ for (const [from, to] of facts.db.facts("calls")) {
14016
+ const key = String(from);
14017
+ const bucket = adjacency.get(key) ?? [];
14018
+ bucket.push(String(to));
14019
+ adjacency.set(key, bucket);
14020
+ }
14021
+ const seedKeysByRoot = /* @__PURE__ */ new Map();
14022
+ for (const seed of seeds) {
14023
+ const key = facts.unitKeyBySummary.get(seed);
14024
+ if (key === void 0) {
14025
+ continue;
14026
+ }
14027
+ for (const root of owners.get(seed) ?? []) {
14028
+ const bucket = seedKeysByRoot.get(root) ?? [];
14029
+ bucket.push(key);
14030
+ seedKeysByRoot.set(root, bucket);
14031
+ }
14032
+ }
14033
+ const reachableByRoot = /* @__PURE__ */ new Map();
14034
+ const rootsByKey = /* @__PURE__ */ new Map();
14035
+ for (const [root, seedKeys] of seedKeysByRoot) {
14036
+ const seen = new Set(seedKeys);
14037
+ const queue = [...seedKeys];
14038
+ while (queue.length > 0) {
14039
+ const key = queue.pop();
14040
+ if (key === void 0) {
14041
+ continue;
14042
+ }
14043
+ for (const next of adjacency.get(key) ?? []) {
14044
+ if (!seen.has(next)) {
14045
+ seen.add(next);
14046
+ queue.push(next);
14047
+ }
14048
+ }
14049
+ const keyRoots = rootsByKey.get(key) ?? /* @__PURE__ */ new Set();
14050
+ keyRoots.add(root);
14051
+ rootsByKey.set(key, keyRoots);
14052
+ }
14053
+ reachableByRoot.set(root, seen);
14054
+ }
14055
+ for (const summary of reached) {
14056
+ const key = facts.unitKeyBySummary.get(summary);
14057
+ if (key === void 0) {
14058
+ continue;
14059
+ }
14060
+ const keyRoots = rootsByKey.get(key);
14061
+ if (keyRoots !== void 0 && keyRoots.size > 0) {
14062
+ owners.set(summary, new Set(keyRoots));
14063
+ }
14064
+ }
14065
+ return { reachableByRoot, rootsByKey };
14066
+ }
14067
+ function fileOfNodeKey(key) {
14068
+ const cut = key.lastIndexOf(":");
14069
+ return cut <= 0 ? null : key.slice(0, cut);
14070
+ }
14071
+ function summaryMergeKey(summary) {
14072
+ return JSON.stringify([
14073
+ summary.location.file,
14074
+ summary.location.range.start,
14075
+ summary.location.range.end,
14076
+ summary.kind,
14077
+ summary.identity.name,
14078
+ summary.identity.exportPath,
14079
+ summary.identity.boundaryBinding
14080
+ ]);
14081
+ }
14082
+ function mergeWithReused(reused, fresh, ownersBySummary) {
14083
+ if (reused === null) {
14084
+ return { summaries: fresh, reusedKept: [], reusedOwners: [] };
14085
+ }
14086
+ const freshByKey = /* @__PURE__ */ new Map();
14087
+ for (const summary of fresh) {
14088
+ freshByKey.set(summaryMergeKey(summary), summary);
14089
+ }
14090
+ const reusedKept = [];
14091
+ const reusedOwners = [];
14092
+ reused.summaries.forEach((summary, i) => {
14093
+ const owners = reused.owners[i] ?? [];
14094
+ const supersededBy = freshByKey.get(summaryMergeKey(summary));
14095
+ if (supersededBy !== void 0) {
14096
+ const freshOwners = ownersBySummary.get(supersededBy) ?? /* @__PURE__ */ new Set();
14097
+ for (const owner of owners) {
14098
+ freshOwners.add(owner);
14099
+ }
14100
+ ownersBySummary.set(supersededBy, freshOwners);
14101
+ return;
14102
+ }
14103
+ reusedKept.push(summary);
14104
+ reusedOwners.push(owners);
14105
+ });
14106
+ return { summaries: [...reusedKept, ...fresh], reusedKept, reusedOwners };
14107
+ }
14108
+ function readsRunLevelJoins(summary) {
14109
+ if (readSourceDocumentMetadata(summary) !== void 0) {
14110
+ return true;
14111
+ }
14112
+ if (readGraphqlMetadata2(summary)?.client !== void 0) {
14113
+ return true;
14114
+ }
14115
+ const semantics = summary.identity.boundaryBinding?.semantics;
14116
+ return semantics !== void 0 && "operationType" in semantics;
14117
+ }
14118
+ function buildCacheAttribution(args) {
14119
+ const references = createReferenceIndex(
14120
+ args.project.getSourceFiles().filter((sf) => !sf.isDeclarationFile())
14121
+ );
14122
+ const packNamesByPath = /* @__PURE__ */ new Map();
14123
+ for (const [sf, packs] of args.packsByFile) {
14124
+ packNamesByPath.set(
14125
+ sf.getFilePath(),
14126
+ packs.map((p) => p.name)
14127
+ );
14128
+ }
14129
+ const summariesByRoot = /* @__PURE__ */ new Map();
14130
+ for (const summary of args.fresh) {
14131
+ for (const root of args.ownersBySummary.get(summary) ?? []) {
14132
+ const bucket = summariesByRoot.get(root) ?? [];
14133
+ bucket.push(summary);
14134
+ summariesByRoot.set(root, bucket);
14135
+ }
14136
+ }
14137
+ const roots = [];
14138
+ for (const [rootPath, sink] of args.sinkByRoot) {
14139
+ const deps = new Set(sink.files);
14140
+ for (const dep of references.directOf(rootPath)) {
14141
+ deps.add(dep);
14142
+ }
14143
+ let cacheable = true;
14144
+ for (const summary of summariesByRoot.get(rootPath) ?? []) {
14145
+ deps.add(summary.location.file);
14146
+ if (readsRunLevelJoins(summary)) {
14147
+ cacheable = false;
14148
+ }
14149
+ }
14150
+ const reachable = args.closureOwnership?.reachableByRoot.get(rootPath) ?? [];
14151
+ for (const key of reachable) {
14152
+ const keyFile = fileOfNodeKey(key);
14153
+ if (keyFile !== null) {
14154
+ deps.add(keyFile);
14155
+ for (const hop of references.directOf(keyFile)) {
14156
+ deps.add(hop);
14157
+ }
14158
+ }
14159
+ for (const filePath of args.filesByKey?.get(key) ?? []) {
14160
+ deps.add(filePath);
14161
+ }
14162
+ }
14163
+ deps.delete(rootPath);
14164
+ roots.push({
14165
+ path: rootPath,
14166
+ cacheable,
14167
+ deps: [...deps].sort(),
14168
+ claims: sink.claims,
14169
+ mountPrefixes: Object.fromEntries(sink.mountPrefixes),
14170
+ packs: packNamesByPath.get(rootPath) ?? []
14171
+ });
14172
+ }
14173
+ if (args.plan !== null) {
14174
+ for (const rootPath of args.validRoots) {
14175
+ const record = args.plan.roots.get(rootPath);
14176
+ if (record !== void 0) {
14177
+ roots.push(record);
14178
+ }
14179
+ }
14180
+ }
14181
+ const reusedOwners = args.reusedKept.map((summary, i) => {
14182
+ const combined = new Set(args.reusedOwners[i] ?? []);
14183
+ const key = args.unitKeyBySummary.get(summary);
14184
+ if (key !== void 0) {
14185
+ for (const root of args.closureOwnership?.rootsByKey.get(key) ?? []) {
14186
+ combined.add(root);
14187
+ }
14188
+ }
14189
+ return [...combined];
14190
+ });
14191
+ const owners = [
14192
+ ...reusedOwners,
14193
+ ...args.fresh.map((s) => [...args.ownersBySummary.get(s) ?? []])
14194
+ ];
14195
+ return {
14196
+ roots,
14197
+ owners,
14198
+ ...args.projectRoot === void 0 ? {} : { projectRoot: args.projectRoot }
14199
+ };
14200
+ }
14201
+ function synthesizeSubUnits(summaries, project, frameworks, options, tallies, onCreated) {
13306
14202
  const packByRecognition = /* @__PURE__ */ new Map();
13307
14203
  for (const pack of frameworks) {
13308
14204
  packByRecognition.set(pack.name, pack);
@@ -13345,6 +14241,7 @@ function synthesizeSubUnits(summaries, project, frameworks, options, tallies) {
13345
14241
  );
13346
14242
  if (summary !== null) {
13347
14243
  synthesized.push(summary);
14244
+ onCreated?.(summary, parent);
13348
14245
  }
13349
14246
  }
13350
14247
  }
@@ -13353,14 +14250,7 @@ function synthesizeSubUnits(summaries, project, frameworks, options, tallies) {
13353
14250
  var DEFAULT_SUB_UNIT_TERMINALS = [
13354
14251
  { kind: "return", match: { type: "returnStatement" }, extraction: {} },
13355
14252
  { kind: "throw", match: { type: "throwExpression" }, extraction: {} },
13356
- // A callback routinely falls off the end returning `undefined`, so
13357
- // fall-through gives it a default transition. HTTP packs do not opt in:
13358
- // a handler that sends no response should come out with none.
13359
- {
13360
- kind: "return",
13361
- match: { type: "functionFallthrough" },
13362
- extraction: {}
13363
- }
14253
+ FUNCTION_FALLTHROUGH_TERMINAL
13364
14254
  ];
13365
14255
  var DEFAULT_SUB_UNIT_INPUT_MAPPING = {
13366
14256
  type: "positionalParams",
@@ -13504,10 +14394,10 @@ function findNearestTsconfig(startDir) {
13504
14394
  }
13505
14395
 
13506
14396
  // src/configuredCall.ts
13507
- import { Node as Node54 } from "ts-morph";
14397
+ import { Node as Node55 } from "ts-morph";
13508
14398
  function readConfiguredCall(call, ctx, spec) {
13509
14399
  const callee = call.getExpression();
13510
- if (!Node54.isPropertyAccessExpression(callee)) {
14400
+ if (!Node55.isPropertyAccessExpression(callee)) {
13511
14401
  return null;
13512
14402
  }
13513
14403
  if (callee.getName() !== spec.method) {
@@ -13552,10 +14442,10 @@ function readStringArg(arg) {
13552
14442
  }
13553
14443
  function rootIdentifier(node) {
13554
14444
  let current2 = node;
13555
- while (Node54.isPropertyAccessExpression(current2)) {
14445
+ while (Node55.isPropertyAccessExpression(current2)) {
13556
14446
  current2 = current2.getExpression();
13557
14447
  }
13558
- return Node54.isIdentifier(current2) ? current2 : null;
14448
+ return Node55.isIdentifier(current2) ? current2 : null;
13559
14449
  }
13560
14450
 
13561
14451
  // src/packHealth.ts
@@ -13671,7 +14561,8 @@ function formatPackHealth(checks, audiences) {
13671
14561
  }
13672
14562
 
13673
14563
  // src/resolve/readName.ts
13674
- import { Node as Node55, SyntaxKind as SyntaxKind10 } from "ts-morph";
14564
+ import { Node as Node56, SyntaxKind as SyntaxKind12 } from "ts-morph";
14565
+ import { patternHole, referenceName } from "@suss/behavioral-ir";
13675
14566
  var MAX_HELPER_HOPS = 2;
13676
14567
  var UNNAMED_HOLE = "param";
13677
14568
  function readName(expr, options = {}) {
@@ -13685,6 +14576,14 @@ function readName(expr, options = {}) {
13685
14576
  if (name !== null || options.unsettled !== "reference") {
13686
14577
  return name;
13687
14578
  }
14579
+ const reference = referenceIn(expr);
14580
+ return reference === null ? null : referenceName(reference);
14581
+ }
14582
+ function referenceIn(expr) {
14583
+ const fromParameter = parameterPath(expr);
14584
+ if (fromParameter !== null) {
14585
+ return fromParameter;
14586
+ }
13688
14587
  const asked = holeName(expr, {
13689
14588
  resolve: () => null,
13690
14589
  bindings: /* @__PURE__ */ new Map(),
@@ -13692,7 +14591,25 @@ function readName(expr, options = {}) {
13692
14591
  seen: /* @__PURE__ */ new Set(),
13693
14592
  insideHole: true
13694
14593
  });
13695
- return asked === UNNAMED_HOLE ? null : `{${asked}}`;
14594
+ return asked === UNNAMED_HOLE ? null : { root: asked, fields: [] };
14595
+ }
14596
+ function parameterPath(expr) {
14597
+ const fields = [];
14598
+ let step = expr;
14599
+ while (Node56.isPropertyAccessExpression(step)) {
14600
+ fields.unshift(step.getName());
14601
+ step = step.getExpression();
14602
+ }
14603
+ if (!Node56.isIdentifier(step) || !isParameter(step)) {
14604
+ return null;
14605
+ }
14606
+ return { root: step.getText(), fields };
14607
+ }
14608
+ function isParameter(identifier) {
14609
+ const declarations = identifier.getSymbol()?.getDeclarations() ?? [];
14610
+ return declarations.some(
14611
+ (declaration) => Node56.isParameterDeclaration(declaration)
14612
+ );
13696
14613
  }
13697
14614
  function read(expr, ctx) {
13698
14615
  if (ctx.seen.has(expr)) {
@@ -13700,25 +14617,25 @@ function read(expr, ctx) {
13700
14617
  }
13701
14618
  const seen = new Set(ctx.seen).add(expr);
13702
14619
  const inner = { ...ctx, seen };
13703
- if (Node55.isStringLiteral(expr) || Node55.isNoSubstitutionTemplateLiteral(expr)) {
14620
+ if (Node56.isStringLiteral(expr) || Node56.isNoSubstitutionTemplateLiteral(expr)) {
13704
14621
  return expr.getLiteralValue();
13705
14622
  }
13706
- if (Node55.isNumericLiteral(expr)) {
14623
+ if (Node56.isNumericLiteral(expr)) {
13707
14624
  return expr.getLiteralText();
13708
14625
  }
13709
- if (Node55.isTemplateExpression(expr)) {
14626
+ if (Node56.isTemplateExpression(expr)) {
13710
14627
  return fromTemplate(expr, inner);
13711
14628
  }
13712
- if (Node55.isParenthesizedExpression(expr)) {
14629
+ if (Node56.isParenthesizedExpression(expr)) {
13713
14630
  return read(expr.getExpression(), inner);
13714
14631
  }
13715
- if (Node55.isBinaryExpression(expr)) {
14632
+ if (Node56.isBinaryExpression(expr)) {
13716
14633
  return fromBinary(expr, inner);
13717
14634
  }
13718
- if (Node55.isCallExpression(expr)) {
14635
+ if (Node56.isCallExpression(expr)) {
13719
14636
  return fromCall(expr, inner);
13720
14637
  }
13721
- if (Node55.isIdentifier(expr)) {
14638
+ if (Node56.isIdentifier(expr)) {
13722
14639
  const only = boundOnce(expr, ctx);
13723
14640
  if (only !== null) {
13724
14641
  return read(only.node, { ...inner, bindings: only.scope });
@@ -13728,7 +14645,7 @@ function read(expr, ctx) {
13728
14645
  return written === null || written === expr ? null : read(written, inner);
13729
14646
  }
13730
14647
  function fromTemplate(expr, ctx) {
13731
- if (!Node55.isTemplateExpression(expr)) {
14648
+ if (!Node56.isTemplateExpression(expr)) {
13732
14649
  return null;
13733
14650
  }
13734
14651
  let name = expr.getHead().getLiteralText();
@@ -13739,28 +14656,28 @@ function fromTemplate(expr, ctx) {
13739
14656
  return name;
13740
14657
  }
13741
14658
  function fromBinary(expr, ctx) {
13742
- if (!Node55.isBinaryExpression(expr)) {
14659
+ if (!Node56.isBinaryExpression(expr)) {
13743
14660
  return null;
13744
14661
  }
13745
14662
  const operator = expr.getOperatorToken().getKind();
13746
- if (operator === SyntaxKind10.BarBarToken || operator === SyntaxKind10.QuestionQuestionToken) {
14663
+ if (operator === SyntaxKind12.BarBarToken || operator === SyntaxKind12.QuestionQuestionToken) {
13747
14664
  return ctx.insideHole ? null : read(expr.getRight(), ctx);
13748
14665
  }
13749
- if (operator !== SyntaxKind10.PlusToken) {
14666
+ if (operator !== SyntaxKind12.PlusToken) {
13750
14667
  return null;
13751
14668
  }
13752
14669
  const parts = { ...ctx, insideHole: true };
13753
14670
  return readOrHole(expr.getLeft(), parts) + readOrHole(expr.getRight(), parts);
13754
14671
  }
13755
14672
  function fromCall(expr, ctx) {
13756
- if (!Node55.isCallExpression(expr)) {
14673
+ if (!Node56.isCallExpression(expr)) {
13757
14674
  return null;
13758
14675
  }
13759
14676
  const callee = expr.getExpression();
13760
- if (Node55.isPropertyAccessExpression(callee) && callee.getName() === "join") {
14677
+ if (Node56.isPropertyAccessExpression(callee) && callee.getName() === "join") {
13761
14678
  return fromJoin(callee.getExpression(), expr.getArguments(), ctx);
13762
14679
  }
13763
- if (Node55.isIdentifier(callee) && callee.getText() === "String") {
14680
+ if (Node56.isIdentifier(callee) && callee.getText() === "String") {
13764
14681
  const only = expr.getArguments()[0];
13765
14682
  return only === void 0 ? null : readOrHole(only, ctx);
13766
14683
  }
@@ -13779,7 +14696,7 @@ function fromCall(expr, ctx) {
13779
14696
  }
13780
14697
  function fromJoin(subject, args, ctx) {
13781
14698
  const separator = args[0];
13782
- if (separator === void 0 || !Node55.isStringLiteral(separator)) {
14699
+ if (separator === void 0 || !Node56.isStringLiteral(separator)) {
13783
14700
  return null;
13784
14701
  }
13785
14702
  const parts = elementsOf(subject, ctx);
@@ -13795,17 +14712,17 @@ function fromJoin(subject, args, ctx) {
13795
14712
  ).join(separator.getLiteralValue());
13796
14713
  }
13797
14714
  function elementsOf(subject, ctx) {
13798
- if (Node55.isArrayLiteralExpression(subject)) {
14715
+ if (Node56.isArrayLiteralExpression(subject)) {
13799
14716
  return subject.getElements().map((element) => here(element, ctx));
13800
14717
  }
13801
- if (Node55.isCallExpression(subject)) {
14718
+ if (Node56.isCallExpression(subject)) {
13802
14719
  const callee = subject.getExpression();
13803
- if (Node55.isPropertyAccessExpression(callee) && callee.getName() === "map") {
14720
+ if (Node56.isPropertyAccessExpression(callee) && callee.getName() === "map") {
13804
14721
  return elementsOf(callee.getExpression(), ctx);
13805
14722
  }
13806
14723
  return null;
13807
14724
  }
13808
- if (!Node55.isIdentifier(subject)) {
14725
+ if (!Node56.isIdentifier(subject)) {
13809
14726
  return null;
13810
14727
  }
13811
14728
  const bound = ctx.bindings.get(subject.getText());
@@ -13813,7 +14730,7 @@ function elementsOf(subject, ctx) {
13813
14730
  return null;
13814
14731
  }
13815
14732
  const only = bound.length === 1 ? bound[0] : void 0;
13816
- return only !== void 0 && Node55.isArrayLiteralExpression(only.node) ? only.node.getElements().map((element) => here(element, ctx)) : bound;
14733
+ return only !== void 0 && Node56.isArrayLiteralExpression(only.node) ? only.node.getElements().map((element) => here(element, ctx)) : bound;
13817
14734
  }
13818
14735
  function singleReturnBody(callee, ctx) {
13819
14736
  const declaration = declarationOf2(callee, ctx);
@@ -13828,22 +14745,22 @@ function singleReturnBody(callee, ctx) {
13828
14745
  if (body === void 0) {
13829
14746
  return null;
13830
14747
  }
13831
- if (Node55.isExpression(body)) {
14748
+ if (Node56.isExpression(body)) {
13832
14749
  return { parameters, returned: body };
13833
14750
  }
13834
- if (!Node55.isBlock(body)) {
14751
+ if (!Node56.isBlock(body)) {
13835
14752
  return null;
13836
14753
  }
13837
14754
  const statements = body.getStatements();
13838
14755
  const only = statements[0];
13839
- if (statements.length !== 1 || only === void 0 || !Node55.isReturnStatement(only)) {
14756
+ if (statements.length !== 1 || only === void 0 || !Node56.isReturnStatement(only)) {
13840
14757
  return null;
13841
14758
  }
13842
14759
  const returned = only.getExpression();
13843
14760
  return returned === void 0 ? null : { parameters, returned };
13844
14761
  }
13845
14762
  function declarationOf2(callee, ctx) {
13846
- const symbol = Node55.isPropertyAccessExpression(callee) ? callee.getNameNode().getSymbol() : callee.getSymbol();
14763
+ const symbol = Node56.isPropertyAccessExpression(callee) ? callee.getNameNode().getSymbol() : callee.getSymbol();
13847
14764
  const declarations = symbol?.getAliasedSymbol()?.getDeclarations() ?? symbol?.getDeclarations() ?? [];
13848
14765
  for (const declaration of declarations) {
13849
14766
  const callable = asCallable(declaration);
@@ -13855,14 +14772,14 @@ function declarationOf2(callee, ctx) {
13855
14772
  return written === null || written === callee ? null : declarationOf2(written, { ...ctx, resolve: () => null });
13856
14773
  }
13857
14774
  function asCallable(declaration) {
13858
- if (Node55.isFunctionDeclaration(declaration) || Node55.isMethodDeclaration(declaration) || Node55.isArrowFunction(declaration) || Node55.isFunctionExpression(declaration)) {
14775
+ if (Node56.isFunctionDeclaration(declaration) || Node56.isMethodDeclaration(declaration) || Node56.isArrowFunction(declaration) || Node56.isFunctionExpression(declaration)) {
13859
14776
  return declaration;
13860
14777
  }
13861
- if (!Node55.isVariableDeclaration(declaration)) {
14778
+ if (!Node56.isVariableDeclaration(declaration)) {
13862
14779
  return null;
13863
14780
  }
13864
14781
  const initializer = declaration.getInitializer();
13865
- if (initializer !== void 0 && (Node55.isArrowFunction(initializer) || Node55.isFunctionExpression(initializer))) {
14782
+ if (initializer !== void 0 && (Node56.isArrowFunction(initializer) || Node56.isFunctionExpression(initializer))) {
13866
14783
  return initializer;
13867
14784
  }
13868
14785
  return null;
@@ -13884,22 +14801,22 @@ function here(node, ctx) {
13884
14801
  return { node, scope: ctx.bindings };
13885
14802
  }
13886
14803
  function boundOnce(expr, ctx) {
13887
- const bound = Node55.isIdentifier(expr) ? ctx.bindings.get(expr.getText()) : void 0;
14804
+ const bound = Node56.isIdentifier(expr) ? ctx.bindings.get(expr.getText()) : void 0;
13888
14805
  return bound !== void 0 && bound.length === 1 ? bound[0] : null;
13889
14806
  }
13890
14807
  function readOrHole(expr, ctx) {
13891
14808
  const name = read(expr, ctx);
13892
- return name ?? `{${holeName(expr, ctx)}}`;
14809
+ return name ?? patternHole(holeName(expr, ctx));
13893
14810
  }
13894
14811
  function holeName(expr, ctx) {
13895
14812
  const bound = boundOnce(expr, ctx);
13896
14813
  if (bound !== null) {
13897
14814
  return holeName(bound.node, { ...ctx, bindings: bound.scope });
13898
14815
  }
13899
- if (Node55.isIdentifier(expr)) {
14816
+ if (Node56.isIdentifier(expr)) {
13900
14817
  return expr.getText();
13901
14818
  }
13902
- if (Node55.isPropertyAccessExpression(expr)) {
14819
+ if (Node56.isPropertyAccessExpression(expr)) {
13903
14820
  return expr.getName();
13904
14821
  }
13905
14822
  return UNNAMED_HOLE;