@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.cjs CHANGED
@@ -63,7 +63,7 @@ module.exports = __toCommonJS(index_exports);
63
63
 
64
64
  // src/adapter.ts
65
65
  var import_node_path9 = __toESM(require("path"), 1);
66
- var import_ts_morph55 = require("ts-morph");
66
+ var import_ts_morph56 = require("ts-morph");
67
67
  var import_behavioral_ir6 = require("@suss/behavioral-ir");
68
68
  var import_datalog6 = require("@suss/datalog");
69
69
  var import_extractor5 = require("@suss/extractor");
@@ -1076,24 +1076,64 @@ function collectFallthroughCaseLabels(caseClause) {
1076
1076
  }
1077
1077
 
1078
1078
  // src/paths/pathConditions.ts
1079
- var import_ts_morph8 = require("ts-morph");
1079
+ var import_ts_morph9 = require("ts-morph");
1080
1080
  var import_extractor2 = require("@suss/extractor");
1081
1081
 
1082
- // src/paths/lowering.ts
1082
+ // src/walk/descent.ts
1083
1083
  var import_ts_morph7 = require("ts-morph");
1084
+ var NO_BARRIERS = /* @__PURE__ */ new Set();
1085
+ function isDescentStop(node, func, barriers = NO_BARRIERS) {
1086
+ if (node === func) {
1087
+ return false;
1088
+ }
1089
+ if (import_ts_morph7.Node.isFunctionDeclaration(node) || import_ts_morph7.Node.isMethodDeclaration(node)) {
1090
+ return true;
1091
+ }
1092
+ if (import_ts_morph7.Node.isArrowFunction(node) || import_ts_morph7.Node.isFunctionExpression(node)) {
1093
+ return barriers.has(node);
1094
+ }
1095
+ return false;
1096
+ }
1097
+ function isInlineCallback(node, func, barriers = NO_BARRIERS) {
1098
+ if (node === func) {
1099
+ return false;
1100
+ }
1101
+ if (!(import_ts_morph7.Node.isArrowFunction(node) || import_ts_morph7.Node.isFunctionExpression(node))) {
1102
+ return false;
1103
+ }
1104
+ return !barriers.has(node);
1105
+ }
1106
+ function isModuleScopeStop(node) {
1107
+ return import_ts_morph7.Node.isFunctionDeclaration(node) || import_ts_morph7.Node.isMethodDeclaration(node) || import_ts_morph7.Node.isArrowFunction(node) || import_ts_morph7.Node.isFunctionExpression(node) || import_ts_morph7.Node.isClassDeclaration(node) || import_ts_morph7.Node.isClassExpression(node);
1108
+ }
1109
+ function crossesNestedFunctionScope(node, func) {
1110
+ let current2 = node;
1111
+ while (current2 !== void 0 && current2 !== func) {
1112
+ if (startsItsOwnScope(current2)) {
1113
+ return true;
1114
+ }
1115
+ current2 = current2.getParent();
1116
+ }
1117
+ return false;
1118
+ }
1119
+ function startsItsOwnScope(node) {
1120
+ return import_ts_morph7.Node.isArrowFunction(node) || import_ts_morph7.Node.isFunctionExpression(node) || import_ts_morph7.Node.isFunctionDeclaration(node) || import_ts_morph7.Node.isMethodDeclaration(node) || import_ts_morph7.Node.isGetAccessorDeclaration(node) || import_ts_morph7.Node.isSetAccessorDeclaration(node) || import_ts_morph7.Node.isConstructorDeclaration(node);
1121
+ }
1122
+
1123
+ // src/paths/lowering.ts
1124
+ var import_ts_morph8 = require("ts-morph");
1084
1125
  var import_extractor = require("@suss/extractor");
1085
- var isFunctionBoundary = (node) => import_ts_morph7.Node.isFunctionDeclaration(node) || import_ts_morph7.Node.isFunctionExpression(node) || import_ts_morph7.Node.isArrowFunction(node) || import_ts_morph7.Node.isMethodDeclaration(node);
1086
1126
  function exitKindOf(stmt) {
1087
1127
  let sawReturn = false;
1088
1128
  let sawThrow = false;
1089
1129
  const visit = (node) => {
1090
- if (node !== stmt && isFunctionBoundary(node)) {
1130
+ if (node !== stmt && startsItsOwnScope(node)) {
1091
1131
  return;
1092
1132
  }
1093
- if (import_ts_morph7.Node.isReturnStatement(node)) {
1133
+ if (import_ts_morph8.Node.isReturnStatement(node)) {
1094
1134
  sawReturn = true;
1095
1135
  }
1096
- if (import_ts_morph7.Node.isThrowStatement(node)) {
1136
+ if (import_ts_morph8.Node.isThrowStatement(node)) {
1097
1137
  sawThrow = true;
1098
1138
  }
1099
1139
  for (const child of node.getChildren()) {
@@ -1110,14 +1150,14 @@ var statementsOf = (stmt) => {
1110
1150
  if (stmt === void 0) {
1111
1151
  return [];
1112
1152
  }
1113
- if (import_ts_morph7.Node.isBlock(stmt)) {
1153
+ if (import_ts_morph8.Node.isBlock(stmt)) {
1114
1154
  return stmt.getStatements();
1115
1155
  }
1116
1156
  return [stmt];
1117
1157
  };
1118
- var isLoop = (stmt) => import_ts_morph7.Node.isForOfStatement(stmt) || import_ts_morph7.Node.isForInStatement(stmt) || import_ts_morph7.Node.isForStatement(stmt) || import_ts_morph7.Node.isWhileStatement(stmt) || import_ts_morph7.Node.isDoStatement(stmt);
1158
+ var isLoop = (stmt) => import_ts_morph8.Node.isForOfStatement(stmt) || import_ts_morph8.Node.isForInStatement(stmt) || import_ts_morph8.Node.isForStatement(stmt) || import_ts_morph8.Node.isWhileStatement(stmt) || import_ts_morph8.Node.isDoStatement(stmt);
1119
1159
  function getLoopBody(stmt) {
1120
- if (import_ts_morph7.Node.isForOfStatement(stmt) || import_ts_morph7.Node.isForInStatement(stmt) || import_ts_morph7.Node.isForStatement(stmt) || import_ts_morph7.Node.isWhileStatement(stmt) || import_ts_morph7.Node.isDoStatement(stmt)) {
1160
+ if (import_ts_morph8.Node.isForOfStatement(stmt) || import_ts_morph8.Node.isForInStatement(stmt) || import_ts_morph8.Node.isForStatement(stmt) || import_ts_morph8.Node.isWhileStatement(stmt) || import_ts_morph8.Node.isDoStatement(stmt)) {
1121
1161
  return stmt.getStatement();
1122
1162
  }
1123
1163
  return void 0;
@@ -1132,37 +1172,97 @@ var conditionOf = (expr) => ({
1132
1172
  sourceText: expr.getText(),
1133
1173
  expression: expr
1134
1174
  });
1135
- function lowerStatement(stmt, rawToStructured) {
1136
- const structured = buildStructured(stmt, rawToStructured);
1137
- rawToStructured.set(stmt, structured);
1175
+ function lowerStatement(stmt, ctx) {
1176
+ const structured = {
1177
+ ...buildStructured(stmt, ctx),
1178
+ callbacks: lowerCallbacksIn(ownExpressionsOf(stmt), ctx)
1179
+ };
1180
+ ctx.rawToStructured.set(stmt, structured);
1138
1181
  return structured;
1139
1182
  }
1140
- function lowerList(stmts, rawToStructured) {
1141
- return spliceBlocks(stmts).map((s) => lowerStatement(s, rawToStructured));
1183
+ function lowerList(stmts, ctx) {
1184
+ return spliceBlocks(stmts).map((s) => lowerStatement(s, ctx));
1185
+ }
1186
+ function ownExpressionsOf(stmt) {
1187
+ if (import_ts_morph8.Node.isIfStatement(stmt) || import_ts_morph8.Node.isSwitchStatement(stmt)) {
1188
+ return [stmt.getExpression()];
1189
+ }
1190
+ if (import_ts_morph8.Node.isWhileStatement(stmt) || import_ts_morph8.Node.isDoStatement(stmt)) {
1191
+ return [stmt.getExpression()];
1192
+ }
1193
+ if (import_ts_morph8.Node.isForOfStatement(stmt) || import_ts_morph8.Node.isForInStatement(stmt)) {
1194
+ return [stmt.getInitializer(), stmt.getExpression()];
1195
+ }
1196
+ if (import_ts_morph8.Node.isForStatement(stmt)) {
1197
+ return [
1198
+ stmt.getInitializer(),
1199
+ stmt.getCondition(),
1200
+ stmt.getIncrementor()
1201
+ ].filter((n) => n !== void 0);
1202
+ }
1203
+ if (import_ts_morph8.Node.isTryStatement(stmt)) {
1204
+ return [];
1205
+ }
1206
+ if (import_ts_morph8.Node.isReturnStatement(stmt) || import_ts_morph8.Node.isThrowStatement(stmt)) {
1207
+ const value = stmt.getExpression();
1208
+ return value === void 0 ? [] : [value];
1209
+ }
1210
+ if (import_ts_morph8.Node.isBreakStatement(stmt) || import_ts_morph8.Node.isContinueStatement(stmt)) {
1211
+ return [];
1212
+ }
1213
+ return [stmt];
1214
+ }
1215
+ function lowerCallbacksIn(parts, ctx) {
1216
+ const bodies = [];
1217
+ const visit = (node, root) => {
1218
+ if (node !== root && isDescentStop(node, ctx.func, ctx.barriers)) {
1219
+ return;
1220
+ }
1221
+ if (node !== root && (import_ts_morph8.Node.isArrowFunction(node) || import_ts_morph8.Node.isFunctionExpression(node)) && isInlineCallback(node, ctx.func, ctx.barriers)) {
1222
+ const body = node.getBody();
1223
+ if (body !== void 0 && import_ts_morph8.Node.isBlock(body)) {
1224
+ bodies.push(lowerList(body.getStatements(), ctx));
1225
+ return;
1226
+ }
1227
+ } else if (node !== root && startsItsOwnScope(node)) {
1228
+ return;
1229
+ }
1230
+ for (const child of node.getChildren()) {
1231
+ visit(child, root);
1232
+ }
1233
+ };
1234
+ for (const part of parts) {
1235
+ visit(part, part);
1236
+ }
1237
+ return bodies;
1238
+ }
1239
+ function lowerExpressionBodyCallbacks(expression, func, barriers = NO_BARRIERS) {
1240
+ return lowerCallbacksIn([expression], {
1241
+ func,
1242
+ barriers,
1243
+ rawToStructured: /* @__PURE__ */ new Map()
1244
+ });
1142
1245
  }
1143
1246
  function spliceBlocks(stmts) {
1144
1247
  return stmts.flatMap(
1145
- (s) => import_ts_morph7.Node.isBlock(s) ? spliceBlocks(s.getStatements()) : [s]
1248
+ (s) => import_ts_morph8.Node.isBlock(s) ? spliceBlocks(s.getStatements()) : [s]
1146
1249
  );
1147
1250
  }
1148
- function buildStructured(stmt, rawToStructured) {
1149
- if (import_ts_morph7.Node.isIfStatement(stmt)) {
1251
+ function buildStructured(stmt, ctx) {
1252
+ if (import_ts_morph8.Node.isIfStatement(stmt)) {
1150
1253
  const elseBranch = stmt.getElseStatement();
1151
1254
  return {
1152
1255
  kind: "if",
1153
1256
  condition: conditionOf(stmt.getExpression()),
1154
- thenBody: lowerList(
1155
- statementsOf(stmt.getThenStatement()),
1156
- rawToStructured
1157
- ),
1158
- elseBody: elseBranch === void 0 ? null : lowerList(statementsOf(elseBranch), rawToStructured),
1257
+ thenBody: lowerList(statementsOf(stmt.getThenStatement()), ctx),
1258
+ elseBody: elseBranch === void 0 ? null : lowerList(statementsOf(elseBranch), ctx),
1159
1259
  exitKind: exitKindOf(stmt)
1160
1260
  };
1161
1261
  }
1162
- if (import_ts_morph7.Node.isSwitchStatement(stmt)) {
1262
+ if (import_ts_morph8.Node.isSwitchStatement(stmt)) {
1163
1263
  return {
1164
1264
  kind: "switch",
1165
- groups: lowerSwitchGroups(stmt, rawToStructured),
1265
+ groups: lowerSwitchGroups(stmt, ctx),
1166
1266
  exitKind: exitKindOf(stmt)
1167
1267
  };
1168
1268
  }
@@ -1170,36 +1270,36 @@ function buildStructured(stmt, rawToStructured) {
1170
1270
  return {
1171
1271
  kind: "loop",
1172
1272
  condition: { sourceText: loopHeaderText(stmt), expression: null },
1173
- body: lowerList(statementsOf(getLoopBody(stmt)), rawToStructured),
1273
+ body: lowerList(statementsOf(getLoopBody(stmt)), ctx),
1174
1274
  exitKind: exitKindOf(stmt)
1175
1275
  };
1176
1276
  }
1177
- if (import_ts_morph7.Node.isTryStatement(stmt)) {
1277
+ if (import_ts_morph8.Node.isTryStatement(stmt)) {
1178
1278
  const catchClause = stmt.getCatchClause();
1179
1279
  const finallyBlock = stmt.getFinallyBlock();
1180
1280
  return {
1181
1281
  kind: "try",
1182
- tryBody: lowerList(stmt.getTryBlock().getStatements(), rawToStructured),
1183
- catchBody: catchClause === void 0 ? null : lowerList(catchClause.getBlock().getStatements(), rawToStructured),
1184
- finallyBody: finallyBlock === void 0 ? null : lowerList(finallyBlock.getStatements(), rawToStructured),
1282
+ tryBody: lowerList(stmt.getTryBlock().getStatements(), ctx),
1283
+ catchBody: catchClause === void 0 ? null : lowerList(catchClause.getBlock().getStatements(), ctx),
1284
+ finallyBody: finallyBlock === void 0 ? null : lowerList(finallyBlock.getStatements(), ctx),
1185
1285
  exitKind: exitKindOf(stmt)
1186
1286
  };
1187
1287
  }
1188
- if (import_ts_morph7.Node.isReturnStatement(stmt)) {
1288
+ if (import_ts_morph8.Node.isReturnStatement(stmt)) {
1189
1289
  return { kind: "exit", exit: "return", exitKind: exitKindOf(stmt) };
1190
1290
  }
1191
- if (import_ts_morph7.Node.isThrowStatement(stmt)) {
1291
+ if (import_ts_morph8.Node.isThrowStatement(stmt)) {
1192
1292
  return { kind: "exit", exit: "throw", exitKind: exitKindOf(stmt) };
1193
1293
  }
1194
- if (import_ts_morph7.Node.isBreakStatement(stmt)) {
1294
+ if (import_ts_morph8.Node.isBreakStatement(stmt)) {
1195
1295
  return { kind: "exit", exit: "break", exitKind: exitKindOf(stmt) };
1196
1296
  }
1197
- if (import_ts_morph7.Node.isContinueStatement(stmt)) {
1297
+ if (import_ts_morph8.Node.isContinueStatement(stmt)) {
1198
1298
  return { kind: "exit", exit: "continue", exitKind: exitKindOf(stmt) };
1199
1299
  }
1200
1300
  return { kind: "opaque", exitKind: exitKindOf(stmt) };
1201
1301
  }
1202
- function lowerSwitchGroups(stmt, rawToStructured) {
1302
+ function lowerSwitchGroups(stmt, ctx) {
1203
1303
  const switchText = stmt.getExpression().getText();
1204
1304
  const groups = [];
1205
1305
  let pendingLabels = [];
@@ -1208,7 +1308,7 @@ function lowerSwitchGroups(stmt, rawToStructured) {
1208
1308
  if (sawDefault) {
1209
1309
  throw new import_extractor.UnmodeledFlow("default clause is not last");
1210
1310
  }
1211
- if (import_ts_morph7.Node.isDefaultClause(clause)) {
1311
+ if (import_ts_morph8.Node.isDefaultClause(clause)) {
1212
1312
  sawDefault = true;
1213
1313
  const stmts2 = clause.getStatements();
1214
1314
  if (stmts2.length === 0) {
@@ -1216,11 +1316,11 @@ function lowerSwitchGroups(stmt, rawToStructured) {
1216
1316
  }
1217
1317
  groups.push({
1218
1318
  condition: null,
1219
- ...lowerGroupBody(stmts2, rawToStructured)
1319
+ ...lowerGroupBody(stmts2, ctx)
1220
1320
  });
1221
1321
  continue;
1222
1322
  }
1223
- if (!import_ts_morph7.Node.isCaseClause(clause)) {
1323
+ if (!import_ts_morph8.Node.isCaseClause(clause)) {
1224
1324
  continue;
1225
1325
  }
1226
1326
  pendingLabels.push(clause.getExpression().getText());
@@ -1233,7 +1333,7 @@ function lowerSwitchGroups(stmt, rawToStructured) {
1233
1333
  const sourceText = labels.map((l) => `${switchText} === ${l}`).join(" || ");
1234
1334
  groups.push({
1235
1335
  condition: { sourceText, expression: null },
1236
- ...lowerGroupBody(stmts, rawToStructured)
1336
+ ...lowerGroupBody(stmts, ctx)
1237
1337
  });
1238
1338
  }
1239
1339
  return groups;
@@ -1241,41 +1341,42 @@ function lowerSwitchGroups(stmt, rawToStructured) {
1241
1341
  function validateClauseBreaks(stmts) {
1242
1342
  const last = stmts[stmts.length - 1];
1243
1343
  for (const stmt of stmts) {
1244
- if (stmt === last && import_ts_morph7.Node.isBreakStatement(stmt)) {
1344
+ if (stmt === last && import_ts_morph8.Node.isBreakStatement(stmt)) {
1245
1345
  continue;
1246
1346
  }
1247
- if (import_ts_morph7.Node.isSwitchStatement(stmt) || isLoop(stmt)) {
1347
+ if (import_ts_morph8.Node.isSwitchStatement(stmt) || isLoop(stmt)) {
1248
1348
  continue;
1249
1349
  }
1250
1350
  stmt.forEachDescendant((node, traversal) => {
1251
- if (isFunctionBoundary(node) || import_ts_morph7.Node.isSwitchStatement(node) || isLoop(node)) {
1351
+ if (startsItsOwnScope(node) || import_ts_morph8.Node.isSwitchStatement(node) || isLoop(node)) {
1252
1352
  traversal.skip();
1253
1353
  return;
1254
1354
  }
1255
- if (import_ts_morph7.Node.isBreakStatement(node)) {
1355
+ if (import_ts_morph8.Node.isBreakStatement(node)) {
1256
1356
  throw new import_extractor.UnmodeledFlow("non-trailing break in switch clause");
1257
1357
  }
1258
1358
  });
1259
- if (import_ts_morph7.Node.isBreakStatement(stmt)) {
1359
+ if (import_ts_morph8.Node.isBreakStatement(stmt)) {
1260
1360
  throw new import_extractor.UnmodeledFlow("non-trailing break in switch clause");
1261
1361
  }
1262
1362
  }
1263
1363
  }
1264
- function lowerGroupBody(rawStmts, rawToStructured) {
1364
+ function lowerGroupBody(rawStmts, ctx) {
1265
1365
  const stmts = spliceBlocks(rawStmts);
1266
1366
  validateClauseBreaks(stmts);
1267
1367
  const last = stmts[stmts.length - 1];
1268
- const hasTrailingBreak = last !== void 0 && import_ts_morph7.Node.isBreakStatement(last);
1368
+ const hasTrailingBreak = last !== void 0 && import_ts_morph8.Node.isBreakStatement(last);
1269
1369
  const kept = hasTrailingBreak ? stmts.slice(0, -1) : stmts;
1270
- const body = lowerList(kept, rawToStructured);
1370
+ const body = lowerList(kept, ctx);
1271
1371
  if (hasTrailingBreak && last !== void 0) {
1272
- lowerStatement(last, rawToStructured);
1372
+ lowerStatement(last, ctx);
1273
1373
  }
1274
1374
  return { hasTrailingBreak, body };
1275
1375
  }
1276
- function lowerFunctionBody(body, terminalNodes) {
1376
+ function lowerFunctionBody(body, terminalNodes, func = body, barriers = NO_BARRIERS) {
1277
1377
  const rawToStructured = /* @__PURE__ */ new Map();
1278
- const statements = lowerList(body.getStatements(), rawToStructured);
1378
+ const ctx = { func, barriers, rawToStructured };
1379
+ const statements = lowerList(body.getStatements(), ctx);
1279
1380
  const terminalsByStmt = /* @__PURE__ */ new Map();
1280
1381
  const terminalHomeRaw = /* @__PURE__ */ new Map();
1281
1382
  for (const terminal of terminalNodes) {
@@ -1297,7 +1398,7 @@ function lowerFunctionBody(body, terminalNodes) {
1297
1398
  function enclosingLoweredStatement(terminal, body, rawToStructured) {
1298
1399
  let current2 = terminal;
1299
1400
  while (current2 !== void 0 && current2 !== body) {
1300
- if (import_ts_morph7.Node.isStatement(current2) && rawToStructured.has(current2)) {
1401
+ if (import_ts_morph8.Node.isStatement(current2) && rawToStructured.has(current2)) {
1301
1402
  return current2;
1302
1403
  }
1303
1404
  current2 = current2.getParent();
@@ -1306,39 +1407,42 @@ function enclosingLoweredStatement(terminal, body, rawToStructured) {
1306
1407
  }
1307
1408
 
1308
1409
  // src/paths/pathConditions.ts
1309
- function containsUnmodeledFlow(body) {
1410
+ function containsUnmodeledFlow(body, func, barriers) {
1310
1411
  let found = false;
1311
1412
  body.forEachDescendant((node, traversal) => {
1312
- if (isFunctionBoundary(node)) {
1413
+ if (isDescentStop(node, func, barriers)) {
1313
1414
  traversal.skip();
1314
1415
  return;
1315
1416
  }
1316
- if (import_ts_morph8.Node.isLabeledStatement(node)) {
1417
+ if (import_ts_morph9.Node.isLabeledStatement(node)) {
1317
1418
  found = true;
1318
1419
  traversal.stop();
1319
1420
  }
1320
1421
  });
1321
1422
  return found;
1322
1423
  }
1323
- function computePathConditions(func, terminalNodes) {
1424
+ function loweredExpressionBody(body, terminalNodes, func, barriers) {
1425
+ const statement = {
1426
+ kind: "opaque",
1427
+ exitKind: null,
1428
+ callbacks: lowerExpressionBodyCallbacks(body, func, barriers)
1429
+ };
1430
+ return {
1431
+ statements: [statement],
1432
+ terminalsByStmt: /* @__PURE__ */ new Map([[statement, terminalNodes]]),
1433
+ terminalHomeRaw: /* @__PURE__ */ new Map()
1434
+ };
1435
+ }
1436
+ function computePathConditions(func, terminalNodes, barriers = NO_BARRIERS) {
1324
1437
  const body = func.getBody();
1325
1438
  if (body === void 0) {
1326
1439
  return { byTerminal: /* @__PURE__ */ new Map(), fallthrough: [] };
1327
1440
  }
1328
- if (!import_ts_morph8.Node.isBlock(body)) {
1329
- const byTerminal = /* @__PURE__ */ new Map();
1330
- for (const terminal of terminalNodes) {
1331
- byTerminal.set(terminal, [
1332
- collectAncestorConditionInfosBelow(terminal, func)
1333
- ]);
1334
- }
1335
- return { byTerminal, fallthrough: [] };
1336
- }
1337
- if (containsUnmodeledFlow(body)) {
1441
+ if (containsUnmodeledFlow(body, func, barriers)) {
1338
1442
  return degradedResult(func, terminalNodes, "labeled statement");
1339
1443
  }
1340
1444
  try {
1341
- const lowered = lowerFunctionBody(body, terminalNodes);
1445
+ const lowered = import_ts_morph9.Node.isBlock(body) ? lowerFunctionBody(body, terminalNodes, func, barriers) : loweredExpressionBody(body, terminalNodes, func, barriers);
1342
1446
  const result = (0, import_extractor2.enumerateStructuredPaths)({
1343
1447
  statements: lowered.statements,
1344
1448
  terminalsByStmt: lowered.terminalsByStmt
@@ -1405,12 +1509,12 @@ function lineAt(node, pos) {
1405
1509
  }
1406
1510
 
1407
1511
  // src/moduleExports.ts
1408
- var import_ts_morph10 = require("ts-morph");
1512
+ var import_ts_morph11 = require("ts-morph");
1409
1513
 
1410
1514
  // src/bootstrap/lazyProjectInit.ts
1411
1515
  var import_promises = __toESM(require("fs/promises"), 1);
1412
1516
  var import_node_path = __toESM(require("path"), 1);
1413
- var import_ts_morph9 = require("ts-morph");
1517
+ var import_ts_morph10 = require("ts-morph");
1414
1518
 
1415
1519
  // src/facts/moduleGraph.ts
1416
1520
  var import_datalog = require("@suss/datalog");
@@ -1828,10 +1932,10 @@ function readSpecifierPaths(project, filePath, options, host) {
1828
1932
  if (text === null) {
1829
1933
  return [];
1830
1934
  }
1831
- const scanned = import_ts_morph9.ts.preProcessFile(text, true, true);
1935
+ const scanned = import_ts_morph10.ts.preProcessFile(text, true, true);
1832
1936
  const paths = [];
1833
1937
  for (const imported of scanned.importedFiles) {
1834
- const resolved = import_ts_morph9.ts.resolveModuleName(
1938
+ const resolved = import_ts_morph10.ts.resolveModuleName(
1835
1939
  imported.fileName,
1836
1940
  filePath,
1837
1941
  options,
@@ -1855,13 +1959,13 @@ function readFileText(project, filePath) {
1855
1959
  }
1856
1960
  }
1857
1961
  function parseTsconfig(tsConfigFilePath) {
1858
- const configFile = import_ts_morph9.ts.readConfigFile(tsConfigFilePath, import_ts_morph9.ts.sys.readFile);
1962
+ const configFile = import_ts_morph10.ts.readConfigFile(tsConfigFilePath, import_ts_morph10.ts.sys.readFile);
1859
1963
  if (configFile.error !== void 0) {
1860
1964
  return { fileNames: [], options: {} };
1861
1965
  }
1862
- const parsed = import_ts_morph9.ts.parseJsonConfigFileContent(
1966
+ const parsed = import_ts_morph10.ts.parseJsonConfigFileContent(
1863
1967
  configFile.config,
1864
- import_ts_morph9.ts.sys,
1968
+ import_ts_morph10.ts.sys,
1865
1969
  import_node_path.default.dirname(tsConfigFilePath),
1866
1970
  /*existingOptions*/
1867
1971
  void 0,
@@ -1921,17 +2025,17 @@ function addImportersOfMatches(fileImports, matched, options) {
1921
2025
  }
1922
2026
  function internalSpecifierResolver(files, options) {
1923
2027
  const fileSet = new Set(files);
1924
- const cache = import_ts_morph9.ts.createModuleResolutionCache(
1925
- import_ts_morph9.ts.sys.getCurrentDirectory(),
2028
+ const cache = import_ts_morph10.ts.createModuleResolutionCache(
2029
+ import_ts_morph10.ts.sys.getCurrentDirectory(),
1926
2030
  (name) => name,
1927
2031
  options
1928
2032
  );
1929
2033
  return (fromFile, spec) => {
1930
- const resolved = import_ts_morph9.ts.resolveModuleName(
2034
+ const resolved = import_ts_morph10.ts.resolveModuleName(
1931
2035
  spec,
1932
2036
  fromFile,
1933
2037
  options,
1934
- import_ts_morph9.ts.sys,
2038
+ import_ts_morph10.ts.sys,
1935
2039
  cache
1936
2040
  ).resolvedModule?.resolvedFileName;
1937
2041
  if (resolved === void 0) {
@@ -1940,7 +2044,7 @@ function internalSpecifierResolver(files, options) {
1940
2044
  if (fileSet.has(resolved)) {
1941
2045
  return resolved;
1942
2046
  }
1943
- const real = import_ts_morph9.ts.sys.realpath?.(resolved) ?? resolved;
2047
+ const real = import_ts_morph10.ts.sys.realpath?.(resolved) ?? resolved;
1944
2048
  return fileSet.has(real) ? real : null;
1945
2049
  };
1946
2050
  }
@@ -1966,7 +2070,7 @@ async function readImportsConcurrently(paths) {
1966
2070
  const p = paths[i];
1967
2071
  try {
1968
2072
  const text = await import_promises.default.readFile(p, "utf-8");
1969
- const pre = import_ts_morph9.ts.preProcessFile(text, true, false);
2073
+ const pre = import_ts_morph10.ts.preProcessFile(text, true, false);
1970
2074
  results[i] = {
1971
2075
  path: p,
1972
2076
  importedModules: pre.importedFiles.map((f) => f.fileName)
@@ -1984,6 +2088,30 @@ async function readImportsConcurrently(paths) {
1984
2088
  return results;
1985
2089
  }
1986
2090
 
2091
+ // src/depTracking.ts
2092
+ function createDependencySink() {
2093
+ return { files: /* @__PURE__ */ new Set(), mountPrefixes: /* @__PURE__ */ new Map(), claims: [] };
2094
+ }
2095
+ var activeSink = null;
2096
+ function withDependencySink(sink, fn) {
2097
+ const previous = activeSink;
2098
+ activeSink = sink;
2099
+ try {
2100
+ return fn();
2101
+ } finally {
2102
+ activeSink = previous;
2103
+ }
2104
+ }
2105
+ function recordFileDependency(filePath) {
2106
+ activeSink?.files.add(filePath);
2107
+ }
2108
+ function recordMountPrefix(childId, prefix) {
2109
+ activeSink?.mountPrefixes.set(childId, prefix);
2110
+ }
2111
+ function recordUnitClaim(key, pack) {
2112
+ activeSink?.claims.push({ key, pack });
2113
+ }
2114
+
1987
2115
  // src/perFileCache.ts
1988
2116
  function createPerFileCache() {
1989
2117
  const byParse = /* @__PURE__ */ new WeakMap();
@@ -1999,6 +2127,7 @@ function createPerFileCache() {
1999
2127
 
2000
2128
  // src/moduleExports.ts
2001
2129
  function exportedDeclarationsOf(sourceFile) {
2130
+ recordFileDependency(sourceFile.getFilePath());
2002
2131
  const cached = exportsByFile.get(sourceFile);
2003
2132
  if (cached !== void 0) {
2004
2133
  return cached;
@@ -2013,7 +2142,14 @@ function resolveAliasedSymbol(symbol) {
2013
2142
  warmReExportChains(declaration.getSourceFile(), "every import");
2014
2143
  }
2015
2144
  try {
2016
- return symbol.getAliasedSymbol();
2145
+ const aliased = symbol.getAliasedSymbol();
2146
+ for (const declaration of aliased?.getDeclarations() ?? []) {
2147
+ const file = declaration.getSourceFile();
2148
+ if (!file.isInNodeModules()) {
2149
+ recordFileDependency(file.getFilePath());
2150
+ }
2151
+ }
2152
+ return aliased;
2017
2153
  } catch (error) {
2018
2154
  if (!(error instanceof RangeError)) {
2019
2155
  throw error;
@@ -2128,7 +2264,7 @@ function reExportShapeOf(file, reach) {
2128
2264
  for (const assignment of file.getExportAssignments()) {
2129
2265
  exportNodes.push(assignment);
2130
2266
  const expression = assignment.getExpression();
2131
- if (import_ts_morph10.Node.isIdentifier(expression)) {
2267
+ if (import_ts_morph11.Node.isIdentifier(expression)) {
2132
2268
  localNames.add(expression.getText());
2133
2269
  }
2134
2270
  }
@@ -2204,38 +2340,6 @@ function reportUnreadableExports(file) {
2204
2340
  );
2205
2341
  }
2206
2342
 
2207
- // src/walk/descent.ts
2208
- var import_ts_morph11 = require("ts-morph");
2209
- var NO_BARRIERS = /* @__PURE__ */ new Set();
2210
- function isDescentStop(node, func, barriers = NO_BARRIERS) {
2211
- if (node === func) {
2212
- return false;
2213
- }
2214
- if (import_ts_morph11.Node.isFunctionDeclaration(node) || import_ts_morph11.Node.isMethodDeclaration(node)) {
2215
- return true;
2216
- }
2217
- if (import_ts_morph11.Node.isArrowFunction(node) || import_ts_morph11.Node.isFunctionExpression(node)) {
2218
- return barriers.has(node);
2219
- }
2220
- return false;
2221
- }
2222
- function isModuleScopeStop(node) {
2223
- return import_ts_morph11.Node.isFunctionDeclaration(node) || import_ts_morph11.Node.isMethodDeclaration(node) || import_ts_morph11.Node.isArrowFunction(node) || import_ts_morph11.Node.isFunctionExpression(node) || import_ts_morph11.Node.isClassDeclaration(node) || import_ts_morph11.Node.isClassExpression(node);
2224
- }
2225
- function crossesNestedFunctionScope(node, func) {
2226
- let current2 = node;
2227
- while (current2 !== void 0 && current2 !== func) {
2228
- if (startsItsOwnScope(current2)) {
2229
- return true;
2230
- }
2231
- current2 = current2.getParent();
2232
- }
2233
- return false;
2234
- }
2235
- function startsItsOwnScope(node) {
2236
- return import_ts_morph11.Node.isArrowFunction(node) || import_ts_morph11.Node.isFunctionExpression(node) || import_ts_morph11.Node.isFunctionDeclaration(node) || import_ts_morph11.Node.isMethodDeclaration(node) || import_ts_morph11.Node.isGetAccessorDeclaration(node) || import_ts_morph11.Node.isSetAccessorDeclaration(node) || import_ts_morph11.Node.isConstructorDeclaration(node);
2237
- }
2238
-
2239
2343
  // src/resolve/invocationEffects.ts
2240
2344
  function isImportedFrom(identifier, expectedModule) {
2241
2345
  if (!import_ts_morph12.Node.isIdentifier(identifier)) {
@@ -4940,7 +5044,8 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
4940
5044
  ];
4941
5045
  const { byTerminal } = computePathConditions(
4942
5046
  func,
4943
- terminals.map(({ node }) => node)
5047
+ terminals.map(({ node }) => node),
5048
+ barriers
4944
5049
  );
4945
5050
  const wantsFallthrough = terminalPatterns.some(
4946
5051
  (p) => p.match.type === "functionFallthrough"
@@ -4952,7 +5057,11 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
4952
5057
  if (!hasDefaultTerminal) {
4953
5058
  const synthetic = makeFallthroughTerminal(func);
4954
5059
  terminals.push(synthetic);
4955
- const pathResult = computePathConditions(func, [synthetic.node]);
5060
+ const pathResult = computePathConditions(
5061
+ func,
5062
+ [synthetic.node],
5063
+ barriers
5064
+ );
4956
5065
  byTerminal.set(
4957
5066
  synthetic.node,
4958
5067
  pathResult.fallthrough.length > 0 ? pathResult.fallthrough : [[]]
@@ -5083,7 +5192,7 @@ function indexFunctions(sf) {
5083
5192
  var import_node_crypto2 = require("crypto");
5084
5193
  var import_promises2 = __toESM(require("fs/promises"), 1);
5085
5194
  var import_node_path2 = __toESM(require("path"), 1);
5086
- var SCHEMA_VERSION = "5";
5195
+ var SCHEMA_VERSION = "6";
5087
5196
  var MAX_ENTRIES = 2;
5088
5197
  var ENTRY_PREFIX = "key-";
5089
5198
  var ENTRY_DIR_NAME = new RegExp(`^${ENTRY_PREFIX}[0-9a-f]{16}$`);
@@ -5095,6 +5204,7 @@ function createCacheLayer(cacheDir) {
5095
5204
  kind: "miss",
5096
5205
  diagnostic: { kind: "miss", missReason: "no-manifest" }
5097
5206
  }),
5207
+ plan: async () => null,
5098
5208
  write: async () => {
5099
5209
  }
5100
5210
  };
@@ -5122,20 +5232,36 @@ function createCacheLayer(cacheDir) {
5122
5232
  return {
5123
5233
  kind: "hit",
5124
5234
  summaries: manifest.summaries,
5235
+ ...manifest.projectRoot === void 0 ? {} : { projectRoot: manifest.projectRoot },
5125
5236
  diagnostic: { kind: "hit" }
5126
5237
  };
5127
5238
  },
5128
- async write(input, summaries) {
5239
+ async plan(input) {
5240
+ const entryDir = entryDirFor(cacheDir, input);
5241
+ const manifest = await readManifest(import_node_path2.default.join(entryDir, "manifest.json"));
5242
+ if (manifest === null || manifest.roots === void 0 || manifest.rootMeta === void 0 || manifest.owners === void 0) {
5243
+ return null;
5244
+ }
5245
+ const currentTsconfigStamp = await stampTsconfig(input.tsconfigPath);
5246
+ if (!fileStampEquals(manifest.tsconfigStamp, currentTsconfigStamp)) {
5247
+ return null;
5248
+ }
5249
+ await markUsed(entryDir);
5250
+ return buildPlan(manifest, await resolveFileStamps(input));
5251
+ },
5252
+ async write(input, summaries, attribution) {
5253
+ const entryDir = entryDirFor(cacheDir, input);
5254
+ const previous = await readManifest(import_node_path2.default.join(entryDir, "manifest.json"));
5129
5255
  const tsconfigStamp = await stampTsconfig(input.tsconfigPath);
5130
- const files = await resolveFileStamps(input);
5256
+ const files = await hashStamps(await resolveFileStamps(input), previous);
5131
5257
  const manifest = {
5132
5258
  schemaVersion: SCHEMA_VERSION,
5133
5259
  adapterPacksDigest: input.adapterPacksDigest,
5134
5260
  tsconfigStamp,
5135
5261
  files,
5136
- summaries
5262
+ summaries,
5263
+ ...attribution === void 0 ? {} : encodeAttribution(attribution)
5137
5264
  };
5138
- const entryDir = entryDirFor(cacheDir, input);
5139
5265
  await import_promises2.default.mkdir(entryDir, { recursive: true });
5140
5266
  await import_promises2.default.writeFile(
5141
5267
  import_node_path2.default.join(entryDir, "manifest.json"),
@@ -5145,6 +5271,171 @@ function createCacheLayer(cacheDir) {
5145
5271
  }
5146
5272
  };
5147
5273
  }
5274
+ async function buildPlan(manifest, currentStamps) {
5275
+ const stored = new Map(manifest.files.map((f) => [f.path, f]));
5276
+ const current2 = new Map(currentStamps.map((f) => [f.path, f]));
5277
+ const changed = /* @__PURE__ */ new Set();
5278
+ const removed = /* @__PURE__ */ new Set();
5279
+ for (const p of stored.keys()) {
5280
+ if (!current2.has(p)) {
5281
+ removed.add(p);
5282
+ }
5283
+ }
5284
+ const toVerify = [];
5285
+ for (const [p, stamp] of current2) {
5286
+ const before = stored.get(p);
5287
+ if (before === void 0) {
5288
+ changed.add(p);
5289
+ } else if (!fileStampEquals(before, stamp)) {
5290
+ if (before.contentHash === void 0) {
5291
+ changed.add(p);
5292
+ } else {
5293
+ toVerify.push(p);
5294
+ }
5295
+ }
5296
+ }
5297
+ await Promise.all(
5298
+ toVerify.map(async (p) => {
5299
+ const hash = await hashFile(p);
5300
+ if (hash === null || hash !== stored.get(p)?.contentHash) {
5301
+ changed.add(p);
5302
+ }
5303
+ })
5304
+ );
5305
+ const roots = /* @__PURE__ */ new Map();
5306
+ const rootNames = manifest.roots ?? [];
5307
+ const depPaths = manifest.depPaths ?? [];
5308
+ let rootsDeclined = 0;
5309
+ rootNames.forEach((rootPath, i) => {
5310
+ const meta = manifest.rootMeta?.[i];
5311
+ if (meta === void 0) {
5312
+ return;
5313
+ }
5314
+ if (!meta.cacheable) {
5315
+ rootsDeclined += 1;
5316
+ }
5317
+ roots.set(rootPath, {
5318
+ path: rootPath,
5319
+ cacheable: meta.cacheable,
5320
+ deps: meta.deps.flatMap((d) => {
5321
+ const p = depPaths[d];
5322
+ return p === void 0 ? [] : [p];
5323
+ }),
5324
+ claims: meta.claims,
5325
+ mountPrefixes: meta.mountPrefixes,
5326
+ packs: meta.packs
5327
+ });
5328
+ });
5329
+ const validRoots = /* @__PURE__ */ new Set();
5330
+ for (const [rootPath, record] of roots) {
5331
+ if (!record.cacheable || !current2.has(rootPath)) {
5332
+ continue;
5333
+ }
5334
+ if (changed.has(rootPath)) {
5335
+ continue;
5336
+ }
5337
+ const depMoved = record.deps.some((d) => changed.has(d) || removed.has(d));
5338
+ if (!depMoved) {
5339
+ validRoots.add(rootPath);
5340
+ }
5341
+ }
5342
+ const owners = manifest.owners ?? [];
5343
+ return {
5344
+ changed,
5345
+ removed,
5346
+ roots,
5347
+ validRoots,
5348
+ rootsDeclined,
5349
+ storedProjectRoot: manifest.projectRoot ?? null,
5350
+ reuse(valid) {
5351
+ const summaries = [];
5352
+ const reusedOwners = [];
5353
+ manifest.summaries.forEach((summary, i) => {
5354
+ const ownerPaths = (owners[i] ?? []).flatMap((o) => {
5355
+ const p = rootNames[o];
5356
+ return p === void 0 ? [] : [p];
5357
+ });
5358
+ if (ownerPaths.some((p) => valid.has(p))) {
5359
+ summaries.push(summary);
5360
+ reusedOwners.push(ownerPaths.filter((p) => valid.has(p)));
5361
+ }
5362
+ });
5363
+ return { summaries, owners: reusedOwners };
5364
+ },
5365
+ allSummaries() {
5366
+ return manifest.summaries;
5367
+ },
5368
+ attribution() {
5369
+ return {
5370
+ roots: [...roots.values()],
5371
+ owners: owners.map(
5372
+ (ownerIds) => ownerIds.flatMap((o) => {
5373
+ const p = rootNames[o];
5374
+ return p === void 0 ? [] : [p];
5375
+ })
5376
+ ),
5377
+ ...manifest.projectRoot === void 0 ? {} : { projectRoot: manifest.projectRoot }
5378
+ };
5379
+ }
5380
+ };
5381
+ }
5382
+ function encodeAttribution(attribution) {
5383
+ const roots = attribution.roots.map((r) => r.path);
5384
+ const rootIndex = new Map(roots.map((p, i) => [p, i]));
5385
+ const depIndex = /* @__PURE__ */ new Map();
5386
+ const depPaths = [];
5387
+ const depIdOf = (p) => {
5388
+ const existing = depIndex.get(p);
5389
+ if (existing !== void 0) {
5390
+ return existing;
5391
+ }
5392
+ const id = depPaths.length;
5393
+ depPaths.push(p);
5394
+ depIndex.set(p, id);
5395
+ return id;
5396
+ };
5397
+ const rootMeta = attribution.roots.map((r) => ({
5398
+ cacheable: r.cacheable,
5399
+ deps: r.deps.map(depIdOf),
5400
+ claims: r.claims,
5401
+ mountPrefixes: r.mountPrefixes,
5402
+ packs: r.packs
5403
+ }));
5404
+ const owners = attribution.owners.map(
5405
+ (ownerPaths) => ownerPaths.flatMap((p) => {
5406
+ const i = rootIndex.get(p);
5407
+ return i === void 0 ? [] : [i];
5408
+ })
5409
+ );
5410
+ return {
5411
+ roots,
5412
+ rootMeta,
5413
+ depPaths,
5414
+ owners,
5415
+ ...attribution.projectRoot === void 0 ? {} : { projectRoot: attribution.projectRoot }
5416
+ };
5417
+ }
5418
+ async function hashStamps(stamps, previous) {
5419
+ const before = new Map((previous?.files ?? []).map((f) => [f.path, f]));
5420
+ return Promise.all(
5421
+ stamps.map(async (stamp) => {
5422
+ const prior = before.get(stamp.path);
5423
+ if (prior !== void 0 && prior.contentHash !== void 0 && fileStampEquals(prior, stamp)) {
5424
+ return { ...stamp, contentHash: prior.contentHash };
5425
+ }
5426
+ const contentHash = await hashFile(stamp.path);
5427
+ return contentHash === null ? stamp : { ...stamp, contentHash };
5428
+ })
5429
+ );
5430
+ }
5431
+ async function hashFile(filePath) {
5432
+ try {
5433
+ const content = await import_promises2.default.readFile(filePath);
5434
+ return (0, import_node_crypto2.createHash)("sha256").update(content).digest("hex").slice(0, 16);
5435
+ } catch {
5436
+ return null;
5437
+ }
5438
+ }
5148
5439
  async function describeAbsentEntry(cacheDir) {
5149
5440
  try {
5150
5441
  const entries = await import_promises2.default.readdir(cacheDir, { withFileTypes: true });
@@ -5158,7 +5449,7 @@ function entryDirFor(cacheDir, input) {
5158
5449
  SCHEMA_VERSION,
5159
5450
  input.adapterPacksDigest,
5160
5451
  input.tsconfigPath ?? ""
5161
- ].join("\0");
5452
+ ].join(" ");
5162
5453
  const name = (0, import_node_crypto2.createHash)("sha256").update(key).digest("hex").slice(0, 16);
5163
5454
  return import_node_path2.default.join(cacheDir, `${ENTRY_PREFIX}${name}`);
5164
5455
  }
@@ -7010,7 +7301,7 @@ function calleeOf(decorator) {
7010
7301
 
7011
7302
  // src/discovery/decoratedMethod.ts
7012
7303
  function resolverTypeName(args) {
7013
- return args.classTypeName ?? args.typeMap[args.decoratorName] ?? null;
7304
+ return args.typeMap[args.decoratorName] ?? args.classTypeName ?? null;
7014
7305
  }
7015
7306
  function resolveResolverClassTypeName(decoratorArg) {
7016
7307
  if (import_ts_morph34.Node.isArrowFunction(decoratorArg)) {
@@ -9568,7 +9859,10 @@ function discoverResolverMaps(sourceFile, match, kind, resolution) {
9568
9859
  resolverInfo: {
9569
9860
  typeName,
9570
9861
  fieldName,
9571
- ...schemaSdl !== null ? { schemaSdl } : {}
9862
+ ...schemaSdl !== null ? {
9863
+ schemaSdl,
9864
+ schemaDocument: config.getSourceFile().getFilePath()
9865
+ } : {}
9572
9866
  }
9573
9867
  });
9574
9868
  }
@@ -9765,7 +10059,15 @@ function discoverUnits(sourceFile, patterns, resolution, mountPrefixes) {
9765
10059
  }
9766
10060
 
9767
10061
  // src/discovery/mountPrefix.ts
9768
- var NO_MOUNTS = { effectivePrefixFor: () => "" };
10062
+ var NO_MOUNTS = {
10063
+ // Recorded even with no mounts anywhere: a mount added later in some
10064
+ // other file changes this router's prefix without touching its file.
10065
+ effectivePrefixFor: (routerNode) => {
10066
+ recordMountPrefix(nodeId(routerNode), "");
10067
+ return "";
10068
+ },
10069
+ prefixForId: () => ""
10070
+ };
9769
10071
  function buildMountPrefixIndex(packsByFile, resolution) {
9770
10072
  const subjectIdsByPack = /* @__PURE__ */ new Map();
9771
10073
  const mountWorkByPack = /* @__PURE__ */ new Map();
@@ -9822,10 +10124,15 @@ function buildMountPrefixIndex(packsByFile, resolution) {
9822
10124
  return NO_MOUNTS;
9823
10125
  }
9824
10126
  const memo = /* @__PURE__ */ new Map();
10127
+ const byId = (childId) => resolvePrefix(edgesByChild, memo, childId, /* @__PURE__ */ new Set()) ?? "";
9825
10128
  return {
9826
10129
  effectivePrefixFor(routerNode) {
9827
- return resolvePrefix(edgesByChild, memo, nodeId(routerNode), /* @__PURE__ */ new Set()) ?? "";
9828
- }
10130
+ const childId = nodeId(routerNode);
10131
+ const prefix = byId(childId);
10132
+ recordMountPrefix(childId, prefix);
10133
+ return prefix;
10134
+ },
10135
+ prefixForId: byId
9829
10136
  };
9830
10137
  }
9831
10138
  function recordEdge(edgesByChild, candidate) {
@@ -10066,11 +10373,12 @@ var import_ts_morph50 = require("ts-morph");
10066
10373
  var import_datalog2 = require("@suss/datalog");
10067
10374
  var import_resolution = require("@suss/resolution");
10068
10375
  var JS_RULES = [
10069
- // f.bind(...) resolves to whatever f resolves to.
10376
+ // f.bind(...) leads wherever f leads. Stated as a step, so the
10377
+ // questions other than `comesTo` follow it too.
10070
10378
  (0, import_datalog2.rule)(
10071
- "comesTo",
10072
- [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("h")],
10073
- [(0, import_datalog2.lit)("bindCall", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("t")), (0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("t"), (0, import_datalog2.variable)("h"))]
10379
+ "stepsTo",
10380
+ [(0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("t"), import_resolution.VALUE_STEP],
10381
+ [(0, import_datalog2.lit)("bindCall", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("t"))]
10074
10382
  )
10075
10383
  ];
10076
10384
  var RESOLUTION_PROGRAM = process.env.SUSS_RESOLUTION_ON_DEMAND === "0" ? {
@@ -10088,6 +10396,8 @@ var ResolutionStore = class {
10088
10396
  fullyExtracted = /* @__PURE__ */ new Set();
10089
10397
  seededValues = /* @__PURE__ */ new Set();
10090
10398
  importedNames = /* @__PURE__ */ new Map();
10399
+ /** Files the most recent wave walk entered, for the memo to keep. */
10400
+ lastQueryWalked = [];
10091
10401
  declarations = /* @__PURE__ */ new Map();
10092
10402
  graph = new ModuleGraph();
10093
10403
  stale = true;
@@ -10148,7 +10458,10 @@ var ResolutionStore = class {
10148
10458
  const declaration = `${nodeId(refersTo)}|${modules.join(",")}`;
10149
10459
  const cached = this.importedNames.get(declaration);
10150
10460
  if (cached !== void 0) {
10151
- return cached;
10461
+ for (const walkedPath of cached.walked) {
10462
+ recordFileDependency(walkedPath);
10463
+ }
10464
+ return cached.names;
10152
10465
  }
10153
10466
  const target = factKeyOf(value);
10154
10467
  const found = this.resolveByWaves(
@@ -10156,7 +10469,10 @@ var ResolutionStore = class {
10156
10469
  "wantedOrigin",
10157
10470
  () => this.lookupImportedNames(target, modules)
10158
10471
  ) ?? [];
10159
- this.importedNames.set(declaration, found);
10472
+ this.importedNames.set(declaration, {
10473
+ names: found,
10474
+ walked: [...this.lastQueryWalked]
10475
+ });
10160
10476
  return found;
10161
10477
  }
10162
10478
  /**
@@ -10190,6 +10506,7 @@ var ResolutionStore = class {
10190
10506
  this.wantValue(question, value);
10191
10507
  this.seedValue(value);
10192
10508
  const walked = /* @__PURE__ */ new Set();
10509
+ this.lastQueryWalked = [];
10193
10510
  let frontier = alsoFrom === void 0 ? [value.getSourceFile()] : [value.getSourceFile(), alsoFrom];
10194
10511
  for (let hop = 0; hop <= MAX_MODULE_HOPS; hop++) {
10195
10512
  const next = [];
@@ -10198,6 +10515,8 @@ var ResolutionStore = class {
10198
10515
  continue;
10199
10516
  }
10200
10517
  walked.add(sourceFile.getFilePath());
10518
+ this.lastQueryWalked.push(sourceFile.getFilePath());
10519
+ recordFileDependency(sourceFile.getFilePath());
10201
10520
  this.extractFile(sourceFile);
10202
10521
  next.push(...this.graph.importedFilesOf(sourceFile));
10203
10522
  }
@@ -10494,6 +10813,27 @@ function moduleInitSummary(sourceFile, effects, options) {
10494
10813
  return (0, import_extractor3.assembleSummary)(raw, options);
10495
10814
  }
10496
10815
 
10816
+ // src/referencedFiles.ts
10817
+ function createReferenceIndex(sourceFiles) {
10818
+ const direct = /* @__PURE__ */ new Map();
10819
+ for (const sourceFile of sourceFiles) {
10820
+ const referenced = /* @__PURE__ */ new Set();
10821
+ for (const target of sourceFile.getReferencedSourceFiles()) {
10822
+ if (target.isInNodeModules() || target.isDeclarationFile()) {
10823
+ continue;
10824
+ }
10825
+ referenced.add(target.getFilePath());
10826
+ }
10827
+ direct.set(sourceFile.getFilePath(), referenced);
10828
+ }
10829
+ const none = /* @__PURE__ */ new Set();
10830
+ return {
10831
+ directOf(filePath) {
10832
+ return direct.get(filePath) ?? none;
10833
+ }
10834
+ };
10835
+ }
10836
+
10497
10837
  // src/resolve/boundaryEffects.ts
10498
10838
  var import_datalog3 = require("@suss/datalog");
10499
10839
  var EFFECT_RULES = [
@@ -10539,7 +10879,10 @@ function bindingLabel(binding) {
10539
10879
  if (semantics.name === "graphql-operation") {
10540
10880
  return `${semantics.operationType} ${semantics.operationName ?? ""}`.trim();
10541
10881
  }
10542
- return semantics.exportName ?? semantics.module ?? binding.recognition;
10882
+ if ("exportName" in semantics || "module" in semantics) {
10883
+ return semantics.exportName ?? semantics.module ?? binding.recognition;
10884
+ }
10885
+ return binding.recognition;
10543
10886
  }
10544
10887
  function effectFact(effect) {
10545
10888
  if (effect.type === "invocation") {
@@ -10611,10 +10954,105 @@ function deriveBoundaryEffects(summaries, facts) {
10611
10954
  }
10612
10955
 
10613
10956
  // src/resolve/reachableClosure.ts
10614
- var import_ts_morph51 = require("ts-morph");
10957
+ var import_ts_morph52 = require("ts-morph");
10615
10958
  var import_behavioral_ir4 = require("@suss/behavioral-ir");
10616
10959
  var import_datalog4 = require("@suss/datalog");
10617
10960
  var import_extractor4 = require("@suss/extractor");
10961
+
10962
+ // src/resolve/unfollowedCall.ts
10963
+ var import_ts_morph51 = require("ts-morph");
10964
+ function isInExternalCode(sourceFile) {
10965
+ if (sourceFile.isDeclarationFile()) {
10966
+ return true;
10967
+ }
10968
+ return sourceFile.getFilePath().includes("/node_modules/");
10969
+ }
10970
+ function hasBody(fn) {
10971
+ if (import_ts_morph51.Node.isFunctionDeclaration(fn) || import_ts_morph51.Node.isMethodDeclaration(fn) || import_ts_morph51.Node.isFunctionExpression(fn)) {
10972
+ return fn.getBody?.() !== void 0;
10973
+ }
10974
+ return true;
10975
+ }
10976
+ function isDeclaredShape(declaration) {
10977
+ return (import_ts_morph51.Node.isMethodSignature(declaration) || import_ts_morph51.Node.isPropertySignature(declaration)) && !isInExternalCode(declaration.getSourceFile());
10978
+ }
10979
+ function isExternalDeclaration(declaration) {
10980
+ if (isInExternalCode(declaration.getSourceFile())) {
10981
+ return true;
10982
+ }
10983
+ if (import_ts_morph51.Node.isImportSpecifier(declaration) || import_ts_morph51.Node.isImportClause(declaration) || import_ts_morph51.Node.isNamespaceImport(declaration)) {
10984
+ const target = declaration.getFirstAncestorByKind(import_ts_morph51.SyntaxKind.ImportDeclaration)?.getModuleSpecifierSourceFile();
10985
+ return target === void 0 || isInExternalCode(target);
10986
+ }
10987
+ return declaresAnotherModule(declaration);
10988
+ }
10989
+ function declaresAnotherModule(declaration) {
10990
+ const module2 = declaration.getFirstAncestorByKind(
10991
+ import_ts_morph51.SyntaxKind.ModuleDeclaration
10992
+ );
10993
+ return module2 !== void 0 && import_ts_morph51.Node.isStringLiteral(module2.getNameNode());
10994
+ }
10995
+ function statesShapeOnly(declaration) {
10996
+ if (isExternalDeclaration(declaration)) {
10997
+ return false;
10998
+ }
10999
+ if (import_ts_morph51.Node.isMethodSignature(declaration) || import_ts_morph51.Node.isPropertySignature(declaration)) {
11000
+ return true;
11001
+ }
11002
+ if (import_ts_morph51.Node.isFunctionDeclaration(declaration) || import_ts_morph51.Node.isMethodDeclaration(declaration)) {
11003
+ return declaration.getBody() === void 0;
11004
+ }
11005
+ return false;
11006
+ }
11007
+ function declarationsBehind(symbol) {
11008
+ if (symbol === void 0) {
11009
+ return [];
11010
+ }
11011
+ const behind = symbol.getAliasedSymbol()?.getDeclarations() ?? [];
11012
+ return behind.length > 0 ? behind : symbol.getDeclarations();
11013
+ }
11014
+ function classifyStop(declarations) {
11015
+ if (declarations.length === 0) {
11016
+ return "noDeclaration";
11017
+ }
11018
+ for (const declaration of declarations) {
11019
+ if (!isInExternalCode(declaration.getSourceFile())) {
11020
+ recordFileDependency(declaration.getSourceFile().getFilePath());
11021
+ }
11022
+ }
11023
+ if (declarations.every(isExternalDeclaration)) {
11024
+ return "outsideRun";
11025
+ }
11026
+ if (declarations.some(statesShapeOnly)) {
11027
+ return "noBody";
11028
+ }
11029
+ return "unsettledValue";
11030
+ }
11031
+ var RECORDED = {
11032
+ noBody: true,
11033
+ unsettledValue: true,
11034
+ outsideRun: false,
11035
+ noDeclaration: false
11036
+ };
11037
+ function worthRecording(reason) {
11038
+ return RECORDED[reason];
11039
+ }
11040
+ var STOP_SENTENCE = {
11041
+ noBody: (callee) => `The call to ${callee} lands on a declaration with no body, so whatever runs there is missing from this summary`,
11042
+ unsettledValue: (callee) => `The call to ${callee} goes through a value this run could not settle, so whatever runs there is missing from this summary`,
11043
+ 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`,
11044
+ noDeclaration: (callee) => `The call to ${callee} has no declaration this run could find, so whatever runs there is missing from this summary`
11045
+ };
11046
+ function unfollowedCallGap(stop) {
11047
+ return {
11048
+ type: "unfollowedCall",
11049
+ conditions: [],
11050
+ consequence: "unknown",
11051
+ description: STOP_SENTENCE[stop.reason](stop.callee)
11052
+ };
11053
+ }
11054
+
11055
+ // src/resolve/reachableClosure.ts
10618
11056
  var reachablePack = {
10619
11057
  name: "reachable",
10620
11058
  languages: ["typescript"],
@@ -10637,23 +11075,6 @@ function nodeKey2(func) {
10637
11075
  const sf = func.getSourceFile();
10638
11076
  return `${sf.getFilePath()}:${func.getStart()}-${func.getEnd()}`;
10639
11077
  }
10640
- function isInExternalCode(sourceFile) {
10641
- if (sourceFile.isDeclarationFile()) {
10642
- return true;
10643
- }
10644
- const filePath = sourceFile.getFilePath();
10645
- if (filePath.includes("/node_modules/")) {
10646
- return true;
10647
- }
10648
- return false;
10649
- }
10650
- function hasBody(fn) {
10651
- if (import_ts_morph51.Node.isFunctionDeclaration(fn) || import_ts_morph51.Node.isMethodDeclaration(fn) || import_ts_morph51.Node.isFunctionExpression(fn)) {
10652
- const body = fn.getBody?.();
10653
- return body !== void 0;
10654
- }
10655
- return true;
10656
- }
10657
11078
  function resolveDecl(decl, calleeName) {
10658
11079
  if (isInExternalCode(decl.getSourceFile())) {
10659
11080
  return null;
@@ -10665,14 +11086,14 @@ function resolveDecl(decl, calleeName) {
10665
11086
  }
10666
11087
  return { func: fn, name: declName(decl) ?? calleeName };
10667
11088
  }
10668
- if (import_ts_morph51.Node.isVariableDeclaration(decl)) {
11089
+ if (import_ts_morph52.Node.isVariableDeclaration(decl)) {
10669
11090
  const init = decl.getInitializer();
10670
- if (init !== void 0 && (import_ts_morph51.Node.isArrowFunction(init) || import_ts_morph51.Node.isFunctionExpression(init))) {
11091
+ if (init !== void 0 && (import_ts_morph52.Node.isArrowFunction(init) || import_ts_morph52.Node.isFunctionExpression(init))) {
10671
11092
  return { func: init, name: decl.getName() };
10672
11093
  }
10673
11094
  return null;
10674
11095
  }
10675
- if (import_ts_morph51.Node.isImportSpecifier(decl)) {
11096
+ if (import_ts_morph52.Node.isImportSpecifier(decl)) {
10676
11097
  const importDecl = decl.getImportDeclaration();
10677
11098
  const sf = importDecl.getModuleSpecifierSourceFile();
10678
11099
  if (sf === void 0 || isInExternalCode(sf)) {
@@ -10693,78 +11114,85 @@ function resolveDecl(decl, calleeName) {
10693
11114
  return null;
10694
11115
  }
10695
11116
  function declName(decl) {
10696
- if (import_ts_morph51.Node.isFunctionDeclaration(decl) || import_ts_morph51.Node.isMethodDeclaration(decl)) {
11117
+ if (import_ts_morph52.Node.isFunctionDeclaration(decl) || import_ts_morph52.Node.isMethodDeclaration(decl)) {
10697
11118
  const n = decl.getName?.();
10698
11119
  if (typeof n === "string" && n.length > 0) {
10699
11120
  return n;
10700
11121
  }
10701
11122
  }
10702
- if (import_ts_morph51.Node.isFunctionExpression(decl)) {
11123
+ if (import_ts_morph52.Node.isFunctionExpression(decl)) {
10703
11124
  const n = decl.getName();
10704
11125
  if (typeof n === "string" && n.length > 0) {
10705
11126
  return n;
10706
11127
  }
10707
11128
  }
10708
11129
  const parent = decl.getParent();
10709
- if (parent !== void 0 && import_ts_morph51.Node.isVariableDeclaration(parent)) {
11130
+ if (parent !== void 0 && import_ts_morph52.Node.isVariableDeclaration(parent)) {
10710
11131
  return parent.getName();
10711
11132
  }
10712
11133
  return null;
10713
11134
  }
10714
11135
  function resolveCallee(call, calleeName, scan) {
10715
- if (!import_ts_morph51.Node.isCallExpression(call)) {
11136
+ if (!import_ts_morph52.Node.isCallExpression(call)) {
10716
11137
  return null;
10717
11138
  }
10718
11139
  const callee = call.getExpression();
10719
- const declarations = callee.getSymbol()?.getDeclarations() ?? [];
11140
+ const symbol = callee.getSymbol();
11141
+ const declarations = symbol?.getDeclarations() ?? [];
10720
11142
  for (const decl of declarations) {
10721
11143
  const resolved = resolveDecl(decl, calleeName);
10722
11144
  if (resolved !== null) {
10723
- return resolved;
11145
+ return { kind: "followed", candidate: resolved };
10724
11146
  }
10725
11147
  }
10726
- if (scan.resolveCallable === void 0 || !readsAField(callee)) {
10727
- return null;
10728
- }
10729
- if (!declarations.some(isDeclaredShape)) {
10730
- return null;
10731
- }
10732
- const value = scan.resolveCallable(callee, scan.reachedFrom);
10733
- return value === null ? null : resolveDecl(value, calleeName);
10734
- }
10735
- function readsAField(callee) {
10736
- if (!import_ts_morph51.Node.isPropertyAccessExpression(callee)) {
10737
- return false;
10738
- }
10739
- let receiver = callee.getExpression();
10740
- while (import_ts_morph51.Node.isPropertyAccessExpression(receiver)) {
10741
- receiver = receiver.getExpression();
11148
+ const resolveCallable = scan.resolveCallable;
11149
+ if (resolveCallable !== void 0 && import_ts_morph52.Node.isPropertyAccessExpression(callee) && declarations.some(isDeclaredShape)) {
11150
+ const value = resolveCallable(callee, scan.reachedFrom);
11151
+ const resolved = value === null ? null : resolveDecl(value, calleeName);
11152
+ if (resolved !== null) {
11153
+ return { kind: "followed", candidate: resolved };
11154
+ }
10742
11155
  }
10743
- return import_ts_morph51.Node.isThisExpression(receiver);
10744
- }
10745
- function isDeclaredShape(declaration) {
10746
- return (import_ts_morph51.Node.isMethodSignature(declaration) || import_ts_morph51.Node.isPropertySignature(declaration)) && !isInExternalCode(declaration.getSourceFile());
11156
+ return {
11157
+ kind: "stopped",
11158
+ stop: {
11159
+ callee: calleeName,
11160
+ reason: classifyStop(declarationsBehind(symbol))
11161
+ }
11162
+ };
10747
11163
  }
10748
11164
  function collectReachable(func, scan) {
10749
- const found = [];
11165
+ const candidates = [];
11166
+ const stops = [];
10750
11167
  const seen = /* @__PURE__ */ new Set();
10751
11168
  func.forEachDescendant((node) => {
10752
- if (!import_ts_morph51.Node.isCallExpression(node)) {
11169
+ if (!import_ts_morph52.Node.isCallExpression(node)) {
10753
11170
  return;
10754
11171
  }
10755
- const calleeText = node.getExpression().getText();
10756
- const resolved = resolveCallee(node, calleeText, scan);
10757
- if (resolved === null) {
11172
+ const calleeText = normalizeCallee(node.getExpression().getText());
11173
+ const outcome = resolveCallee(node, calleeText, scan);
11174
+ if (outcome === null) {
11175
+ return;
11176
+ }
11177
+ if (outcome.kind === "stopped") {
11178
+ const stopKey = `${outcome.stop.reason}:${outcome.stop.callee}`;
11179
+ if (!seen.has(stopKey) && worthRecording(outcome.stop.reason)) {
11180
+ seen.add(stopKey);
11181
+ stops.push(outcome.stop);
11182
+ }
10758
11183
  return;
10759
11184
  }
10760
- const key = nodeKey2(resolved.func);
11185
+ const key = nodeKey2(outcome.candidate.func);
10761
11186
  if (seen.has(key)) {
10762
11187
  return;
10763
11188
  }
10764
11189
  seen.add(key);
10765
- found.push(resolved);
11190
+ candidates.push(outcome.candidate);
10766
11191
  });
10767
- return found;
11192
+ return { candidates, stops };
11193
+ }
11194
+ function normalizeCallee(text) {
11195
+ return text.replace(/\s+/g, " ").trim();
10768
11196
  }
10769
11197
  function extractReachableSummary(candidate, options, recognizers) {
10770
11198
  const unit = {
@@ -10798,12 +11226,14 @@ var REACHABLE_RULES = [
10798
11226
  )
10799
11227
  ];
10800
11228
  var NO_RECOGNIZERS = { invocation: [], access: [] };
10801
- function expandReachableClosure(seeds, project, options, projectFileSet, facts, recognizers = NO_RECOGNIZERS) {
11229
+ function expandReachableClosure(seeds, project, options, projectFileSet, facts, recognizers = NO_RECOGNIZERS, alreadySummarized = []) {
10802
11230
  const lookup = createSourceFileLookup(project);
10803
11231
  const db = facts?.db ?? new import_datalog4.Database();
10804
11232
  const functionByKey = /* @__PURE__ */ new Map();
10805
11233
  const seedKeys = /* @__PURE__ */ new Set();
10806
11234
  const scanned = /* @__PURE__ */ new Set();
11235
+ const stopsByKey = /* @__PURE__ */ new Map();
11236
+ const summariesByKey = /* @__PURE__ */ new Map();
10807
11237
  const reachedFrom = /* @__PURE__ */ new Map();
10808
11238
  for (const seed of seeds) {
10809
11239
  const func = lookup.functionAt(seed.location);
@@ -10812,9 +11242,19 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
10812
11242
  seedKeys.add(key);
10813
11243
  functionByKey.set(key, { func, name: seed.identity.name });
10814
11244
  facts?.unitKeyBySummary.set(seed, key);
11245
+ rememberSummary(summariesByKey, key, seed);
10815
11246
  db.add("entry", [key]);
10816
11247
  }
10817
11248
  }
11249
+ const knownKeys = /* @__PURE__ */ new Set();
11250
+ for (const summary of alreadySummarized) {
11251
+ const func = lookup.functionAt(summary.location);
11252
+ if (func !== null) {
11253
+ const key = nodeKey2(func);
11254
+ knownKeys.add(key);
11255
+ facts?.unitKeyBySummary.set(summary, key);
11256
+ }
11257
+ }
10818
11258
  for (; ; ) {
10819
11259
  (0, import_datalog4.evaluate)(db, REACHABLE_RULES);
10820
11260
  const frontier = db.facts("reachable").map(([key]) => String(key)).filter((key) => !scanned.has(key));
@@ -10832,7 +11272,15 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
10832
11272
  ...recognizers.resolveCallable === void 0 ? {} : { resolveCallable: recognizers.resolveCallable },
10833
11273
  ...cameFrom === void 0 ? {} : { reachedFrom: cameFrom }
10834
11274
  };
10835
- for (const candidate of collectReachable(source.func, scan)) {
11275
+ const { candidates, stops } = scanWithRecording(
11276
+ key,
11277
+ facts,
11278
+ () => collectReachable(source.func, scan)
11279
+ );
11280
+ if (stops.length > 0) {
11281
+ stopsByKey.set(key, stops);
11282
+ }
11283
+ for (const candidate of candidates) {
10836
11284
  const calleeKey = nodeKey2(candidate.func);
10837
11285
  if (!functionByKey.has(calleeKey)) {
10838
11286
  functionByKey.set(calleeKey, candidate);
@@ -10847,7 +11295,7 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
10847
11295
  const reached = [];
10848
11296
  for (const [keyAtom] of db.facts("reachable")) {
10849
11297
  const key = String(keyAtom);
10850
- if (seedKeys.has(key)) {
11298
+ if (seedKeys.has(key) || knownKeys.has(key)) {
10851
11299
  continue;
10852
11300
  }
10853
11301
  const candidate = functionByKey.get(key);
@@ -10861,15 +11309,50 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
10861
11309
  candidate.func.getSourceFile().getFilePath()
10862
11310
  );
10863
11311
  }
10864
- const summary = extractReachableSummary(candidate, options, recognizers);
11312
+ const summary = scanWithRecording(
11313
+ key,
11314
+ facts,
11315
+ () => extractReachableSummary(candidate, options, recognizers)
11316
+ );
10865
11317
  facts?.unitKeyBySummary.set(summary, key);
11318
+ rememberSummary(summariesByKey, key, summary);
10866
11319
  reached.push(summary);
10867
11320
  }
11321
+ recordStops(stopsByKey, summariesByKey, options);
10868
11322
  return [...seeds, ...reached];
10869
11323
  }
11324
+ function scanWithRecording(key, facts, fn) {
11325
+ const filesByKey = facts?.filesByKey;
11326
+ if (filesByKey === void 0) {
11327
+ return fn();
11328
+ }
11329
+ const sink = createDependencySink();
11330
+ const result = withDependencySink(sink, fn);
11331
+ const bucket = filesByKey.get(key) ?? /* @__PURE__ */ new Set();
11332
+ for (const filePath of sink.files) {
11333
+ bucket.add(filePath);
11334
+ }
11335
+ filesByKey.set(key, bucket);
11336
+ return result;
11337
+ }
11338
+ function rememberSummary(summariesByKey, key, summary) {
11339
+ const bucket = summariesByKey.get(key) ?? [];
11340
+ bucket.push(summary);
11341
+ summariesByKey.set(key, bucket);
11342
+ }
11343
+ function recordStops(stopsByKey, summariesByKey, options) {
11344
+ if (options?.gapHandling === "silent") {
11345
+ return;
11346
+ }
11347
+ for (const [key, stops] of stopsByKey) {
11348
+ for (const summary of summariesByKey.get(key) ?? []) {
11349
+ summary.gaps.push(...stops.map(unfollowedCallGap));
11350
+ }
11351
+ }
11352
+ }
10870
11353
 
10871
11354
  // src/resolve/rethrowEnrichment.ts
10872
- var import_ts_morph52 = require("ts-morph");
11355
+ var import_ts_morph53 = require("ts-morph");
10873
11356
  var import_datalog5 = require("@suss/datalog");
10874
11357
  var RETHROW_RULES = [
10875
11358
  (0, import_datalog5.rule)(
@@ -11009,7 +11492,7 @@ function findBareRethrow(func, location) {
11009
11492
  traversal.stop();
11010
11493
  return;
11011
11494
  }
11012
- if (!import_ts_morph52.Node.isThrowStatement(node)) {
11495
+ if (!import_ts_morph53.Node.isThrowStatement(node)) {
11013
11496
  return;
11014
11497
  }
11015
11498
  if (startLineOf(node) !== location.start) {
@@ -11019,7 +11502,7 @@ function findBareRethrow(func, location) {
11019
11502
  return;
11020
11503
  }
11021
11504
  const thrown = node.getExpression();
11022
- if (thrown !== void 0 && import_ts_morph52.Node.isIdentifier(thrown)) {
11505
+ if (thrown !== void 0 && import_ts_morph53.Node.isIdentifier(thrown)) {
11023
11506
  found = node;
11024
11507
  }
11025
11508
  });
@@ -11029,13 +11512,13 @@ function enclosingTry(throwStmt) {
11029
11512
  let current2 = throwStmt.getParent();
11030
11513
  let sawCatchClause = false;
11031
11514
  while (current2 !== void 0) {
11032
- if (import_ts_morph52.Node.isCatchClause(current2)) {
11515
+ if (import_ts_morph53.Node.isCatchClause(current2)) {
11033
11516
  sawCatchClause = true;
11034
11517
  }
11035
- if (import_ts_morph52.Node.isTryStatement(current2)) {
11518
+ if (import_ts_morph53.Node.isTryStatement(current2)) {
11036
11519
  return sawCatchClause ? current2 : null;
11037
11520
  }
11038
- if (import_ts_morph52.Node.isFunctionDeclaration(current2) || import_ts_morph52.Node.isFunctionExpression(current2) || import_ts_morph52.Node.isArrowFunction(current2) || import_ts_morph52.Node.isMethodDeclaration(current2)) {
11521
+ if (import_ts_morph53.Node.isFunctionDeclaration(current2) || import_ts_morph53.Node.isFunctionExpression(current2) || import_ts_morph53.Node.isArrowFunction(current2) || import_ts_morph53.Node.isMethodDeclaration(current2)) {
11039
11522
  return null;
11040
11523
  }
11041
11524
  current2 = current2.getParent();
@@ -11043,14 +11526,14 @@ function enclosingTry(throwStmt) {
11043
11526
  return null;
11044
11527
  }
11045
11528
  function collectTryBodyCallees(tryStmt, index, keyFor) {
11046
- if (!import_ts_morph52.Node.isTryStatement(tryStmt)) {
11529
+ if (!import_ts_morph53.Node.isTryStatement(tryStmt)) {
11047
11530
  return [];
11048
11531
  }
11049
11532
  const tryBlock = tryStmt.getTryBlock();
11050
11533
  const callees = [];
11051
11534
  const seen = /* @__PURE__ */ new Set();
11052
11535
  tryBlock.forEachDescendant((node) => {
11053
- if (!import_ts_morph52.Node.isCallExpression(node)) {
11536
+ if (!import_ts_morph53.Node.isCallExpression(node)) {
11054
11537
  return;
11055
11538
  }
11056
11539
  const calleeSummary = resolveCalleeSummary(node, index);
@@ -11067,7 +11550,7 @@ function collectTryBodyCallees(tryStmt, index, keyFor) {
11067
11550
  return callees;
11068
11551
  }
11069
11552
  function resolveCalleeSummary(call, index) {
11070
- if (!import_ts_morph52.Node.isCallExpression(call)) {
11553
+ if (!import_ts_morph53.Node.isCallExpression(call)) {
11071
11554
  return null;
11072
11555
  }
11073
11556
  const callee = call.getExpression();
@@ -11093,12 +11576,12 @@ function resolveCalleeSummary(call, index) {
11093
11576
  return null;
11094
11577
  }
11095
11578
  function functionFromDecl(decl) {
11096
- if (import_ts_morph52.Node.isFunctionDeclaration(decl) || import_ts_morph52.Node.isFunctionExpression(decl) || import_ts_morph52.Node.isArrowFunction(decl) || import_ts_morph52.Node.isMethodDeclaration(decl)) {
11579
+ if (import_ts_morph53.Node.isFunctionDeclaration(decl) || import_ts_morph53.Node.isFunctionExpression(decl) || import_ts_morph53.Node.isArrowFunction(decl) || import_ts_morph53.Node.isMethodDeclaration(decl)) {
11097
11580
  return decl;
11098
11581
  }
11099
- if (import_ts_morph52.Node.isVariableDeclaration(decl)) {
11582
+ if (import_ts_morph53.Node.isVariableDeclaration(decl)) {
11100
11583
  const init = decl.getInitializer();
11101
- if (init !== void 0 && (import_ts_morph52.Node.isArrowFunction(init) || import_ts_morph52.Node.isFunctionExpression(init))) {
11584
+ if (init !== void 0 && (import_ts_morph53.Node.isArrowFunction(init) || import_ts_morph53.Node.isFunctionExpression(init))) {
11102
11585
  return init;
11103
11586
  }
11104
11587
  }
@@ -11106,22 +11589,22 @@ function functionFromDecl(decl) {
11106
11589
  }
11107
11590
 
11108
11591
  // src/shapes/fieldAccesses.ts
11109
- var import_ts_morph53 = require("ts-morph");
11592
+ var import_ts_morph54 = require("ts-morph");
11110
11593
  function findResponseAccessor(callExpr) {
11111
11594
  let current2 = callExpr;
11112
11595
  const parent1 = current2.getParent();
11113
- if (parent1 !== void 0 && import_ts_morph53.Node.isAwaitExpression(parent1)) {
11596
+ if (parent1 !== void 0 && import_ts_morph54.Node.isAwaitExpression(parent1)) {
11114
11597
  current2 = parent1;
11115
11598
  }
11116
11599
  const parent2 = current2.getParent();
11117
- if (parent2 === void 0 || !import_ts_morph53.Node.isVariableDeclaration(parent2)) {
11600
+ if (parent2 === void 0 || !import_ts_morph54.Node.isVariableDeclaration(parent2)) {
11118
11601
  return null;
11119
11602
  }
11120
11603
  const nameNode = parent2.getNameNode();
11121
- if (import_ts_morph53.Node.isIdentifier(nameNode)) {
11604
+ if (import_ts_morph54.Node.isIdentifier(nameNode)) {
11122
11605
  return { kind: "identifier", name: nameNode.getText() };
11123
11606
  }
11124
- if (import_ts_morph53.Node.isObjectBindingPattern(nameNode)) {
11607
+ if (import_ts_morph54.Node.isObjectBindingPattern(nameNode)) {
11125
11608
  const bindings = /* @__PURE__ */ new Map();
11126
11609
  for (const element of nameNode.getElements()) {
11127
11610
  if (element.getDotDotDotToken() !== void 0) {
@@ -11142,11 +11625,11 @@ function findResponseAccessor(callExpr) {
11142
11625
  function buildThenChainAccessor(callExpr, func) {
11143
11626
  const bindings = /* @__PURE__ */ new Map();
11144
11627
  func.forEachDescendant((node) => {
11145
- if (!import_ts_morph53.Node.isParameterDeclaration(node)) {
11628
+ if (!import_ts_morph54.Node.isParameterDeclaration(node)) {
11146
11629
  return;
11147
11630
  }
11148
11631
  const nameNode = node.getNameNode();
11149
- if (!import_ts_morph53.Node.isIdentifier(nameNode)) {
11632
+ if (!import_ts_morph54.Node.isIdentifier(nameNode)) {
11150
11633
  return;
11151
11634
  }
11152
11635
  const prefix = prefixOfThenParam(node, callExpr, 0);
@@ -11178,7 +11661,7 @@ function prefixOfExpr(expr, responseCall, depth) {
11178
11661
  if (node === responseCall) {
11179
11662
  return [];
11180
11663
  }
11181
- if (import_ts_morph53.Node.isCallExpression(node)) {
11664
+ if (import_ts_morph54.Node.isCallExpression(node)) {
11182
11665
  const hop = thenLikeCall(node);
11183
11666
  if (hop !== null) {
11184
11667
  if (hop.method === "catch") {
@@ -11210,14 +11693,14 @@ function prefixOfMemberExpr(expr, responseCall, depth) {
11210
11693
  return null;
11211
11694
  }
11212
11695
  const decl = chain.root.getSymbol()?.getDeclarations()?.[0];
11213
- if (decl === void 0 || !import_ts_morph53.Node.isParameterDeclaration(decl)) {
11696
+ if (decl === void 0 || !import_ts_morph54.Node.isParameterDeclaration(decl)) {
11214
11697
  return null;
11215
11698
  }
11216
11699
  const base = prefixOfThenParam(decl, responseCall, depth + 1);
11217
11700
  return base === null ? null : [...base, ...chain.segments];
11218
11701
  }
11219
11702
  function firstCallbackParamName(callback) {
11220
- if (callback === void 0 || !(import_ts_morph53.Node.isArrowFunction(callback) || import_ts_morph53.Node.isFunctionExpression(callback))) {
11703
+ if (callback === void 0 || !(import_ts_morph54.Node.isArrowFunction(callback) || import_ts_morph54.Node.isFunctionExpression(callback))) {
11221
11704
  return null;
11222
11705
  }
11223
11706
  const param = callback.getParameters()[0];
@@ -11225,11 +11708,11 @@ function firstCallbackParamName(callback) {
11225
11708
  return null;
11226
11709
  }
11227
11710
  const nameNode = param.getNameNode();
11228
- return import_ts_morph53.Node.isIdentifier(nameNode) ? nameNode.getText() : null;
11711
+ return import_ts_morph54.Node.isIdentifier(nameNode) ? nameNode.getText() : null;
11229
11712
  }
11230
11713
  function unwrap2(expr) {
11231
11714
  let node = expr;
11232
- while (import_ts_morph53.Node.isAwaitExpression(node) || import_ts_morph53.Node.isParenthesizedExpression(node) || import_ts_morph53.Node.isAsExpression(node)) {
11715
+ while (import_ts_morph54.Node.isAwaitExpression(node) || import_ts_morph54.Node.isParenthesizedExpression(node) || import_ts_morph54.Node.isAsExpression(node)) {
11233
11716
  node = node.getExpression();
11234
11717
  }
11235
11718
  return node;
@@ -11239,15 +11722,15 @@ function chainRootedAt(expr, rootName) {
11239
11722
  if (chain === null) {
11240
11723
  return null;
11241
11724
  }
11242
- return import_ts_morph53.Node.isIdentifier(chain.root) && chain.root.getText() === rootName ? chain.segments : null;
11725
+ return import_ts_morph54.Node.isIdentifier(chain.root) && chain.root.getText() === rootName ? chain.segments : null;
11243
11726
  }
11244
11727
  function memberChain(expr) {
11245
- let node = import_ts_morph53.Node.isExpression(expr) ? unwrap2(expr) : expr;
11246
- if (import_ts_morph53.Node.isCallExpression(node)) {
11728
+ let node = import_ts_morph54.Node.isExpression(expr) ? unwrap2(expr) : expr;
11729
+ if (import_ts_morph54.Node.isCallExpression(node)) {
11247
11730
  node = node.getExpression();
11248
11731
  }
11249
11732
  const segments = [];
11250
- while (import_ts_morph53.Node.isPropertyAccessExpression(node)) {
11733
+ while (import_ts_morph54.Node.isPropertyAccessExpression(node)) {
11251
11734
  segments.unshift(node.getName());
11252
11735
  node = node.getExpression();
11253
11736
  }
@@ -11266,16 +11749,16 @@ function collectPropertyAccesses(subtree, accessor) {
11266
11749
  if (accessor.kind === "identifier") {
11267
11750
  const varName = accessor.name;
11268
11751
  subtree.forEachDescendant((node) => {
11269
- if (!import_ts_morph53.Node.isPropertyAccessExpression(node)) {
11752
+ if (!import_ts_morph54.Node.isPropertyAccessExpression(node)) {
11270
11753
  return;
11271
11754
  }
11272
11755
  const chain = [];
11273
11756
  let current2 = node;
11274
- while (import_ts_morph53.Node.isPropertyAccessExpression(current2)) {
11757
+ while (import_ts_morph54.Node.isPropertyAccessExpression(current2)) {
11275
11758
  chain.unshift(current2.getName());
11276
11759
  current2 = current2.getExpression();
11277
11760
  }
11278
- if (import_ts_morph53.Node.isIdentifier(current2) && current2.getText() === varName) {
11761
+ if (import_ts_morph54.Node.isIdentifier(current2) && current2.getText() === varName) {
11279
11762
  pushChain(chain);
11280
11763
  }
11281
11764
  });
@@ -11286,7 +11769,7 @@ function collectPropertyAccesses(subtree, accessor) {
11286
11769
  return source === void 0 ? void 0 : [source];
11287
11770
  } : (name) => accessor.bindings.get(name);
11288
11771
  subtree.forEachDescendant((node) => {
11289
- if (!import_ts_morph53.Node.isIdentifier(node)) {
11772
+ if (!import_ts_morph54.Node.isIdentifier(node)) {
11290
11773
  return;
11291
11774
  }
11292
11775
  const prefix = prefixOf(node.getText());
@@ -11298,13 +11781,13 @@ function collectPropertyAccesses(subtree, accessor) {
11298
11781
  }
11299
11782
  let chainTop = node;
11300
11783
  let parent = chainTop.getParent();
11301
- while (parent !== void 0 && import_ts_morph53.Node.isPropertyAccessExpression(parent) && parent.getExpression() === chainTop) {
11784
+ while (parent !== void 0 && import_ts_morph54.Node.isPropertyAccessExpression(parent) && parent.getExpression() === chainTop) {
11302
11785
  chainTop = parent;
11303
11786
  parent = chainTop.getParent();
11304
11787
  }
11305
11788
  const segments = [];
11306
11789
  let current2 = chainTop;
11307
- while (import_ts_morph53.Node.isPropertyAccessExpression(current2)) {
11790
+ while (import_ts_morph54.Node.isPropertyAccessExpression(current2)) {
11308
11791
  segments.push(current2.getName());
11309
11792
  current2 = current2.getExpression();
11310
11793
  }
@@ -11318,15 +11801,15 @@ function isBindingDeclarationSite(node) {
11318
11801
  return true;
11319
11802
  }
11320
11803
  const parent = node.getParent();
11321
- return parent !== void 0 && import_ts_morph53.Node.isParameterDeclaration(parent) && parent.getNameNode() === node;
11804
+ return parent !== void 0 && import_ts_morph54.Node.isParameterDeclaration(parent) && parent.getNameNode() === node;
11322
11805
  }
11323
11806
  function isInsideObjectBindingPattern(node) {
11324
11807
  let current2 = node.getParent();
11325
11808
  while (current2 !== void 0) {
11326
- if (import_ts_morph53.Node.isObjectBindingPattern(current2)) {
11809
+ if (import_ts_morph54.Node.isObjectBindingPattern(current2)) {
11327
11810
  return true;
11328
11811
  }
11329
- if (import_ts_morph53.Node.isFunctionLikeDeclaration(current2) || import_ts_morph53.Node.isVariableStatement(current2)) {
11812
+ if (import_ts_morph54.Node.isFunctionLikeDeclaration(current2) || import_ts_morph54.Node.isVariableStatement(current2)) {
11330
11813
  break;
11331
11814
  }
11332
11815
  current2 = current2.getParent();
@@ -11389,7 +11872,7 @@ function findBranchSubtree(func, byLineRange, terminalStartLine, terminalEndLine
11389
11872
  if (parent === void 0) {
11390
11873
  break;
11391
11874
  }
11392
- if (import_ts_morph53.Node.isIfStatement(parent)) {
11875
+ if (import_ts_morph54.Node.isIfStatement(parent)) {
11393
11876
  const ifStmt = parent;
11394
11877
  const thenBlock = ifStmt.getThenStatement();
11395
11878
  const elseBlock = ifStmt.getElseStatement();
@@ -11451,7 +11934,7 @@ function buildNonBodyPropertySet(semantics) {
11451
11934
  }
11452
11935
 
11453
11936
  // src/subUnitContext.ts
11454
- var import_ts_morph54 = require("ts-morph");
11937
+ var import_ts_morph55 = require("ts-morph");
11455
11938
  function createTsSubUnitContext() {
11456
11939
  return {
11457
11940
  findJsxAttributes,
@@ -11464,7 +11947,7 @@ function createTsSubUnitContext() {
11464
11947
  }
11465
11948
  function skipNestedFunctions(parent) {
11466
11949
  return (node, traversal) => {
11467
- if (node !== parent && (import_ts_morph54.Node.isFunctionDeclaration(node) || import_ts_morph54.Node.isFunctionExpression(node) || import_ts_morph54.Node.isArrowFunction(node) || import_ts_morph54.Node.isMethodDeclaration(node))) {
11950
+ if (node !== parent && (import_ts_morph55.Node.isFunctionDeclaration(node) || import_ts_morph55.Node.isFunctionExpression(node) || import_ts_morph55.Node.isArrowFunction(node) || import_ts_morph55.Node.isMethodDeclaration(node))) {
11468
11951
  traversal.skip();
11469
11952
  return true;
11470
11953
  }
@@ -11478,18 +11961,18 @@ function findJsxAttributes(parent) {
11478
11961
  if (skip(node, traversal)) {
11479
11962
  return;
11480
11963
  }
11481
- if (!import_ts_morph54.Node.isJsxOpeningElement(node) && !import_ts_morph54.Node.isJsxSelfClosingElement(node)) {
11964
+ if (!import_ts_morph55.Node.isJsxOpeningElement(node) && !import_ts_morph55.Node.isJsxSelfClosingElement(node)) {
11482
11965
  return;
11483
11966
  }
11484
11967
  const tag = node.getTagNameNode().getText();
11485
11968
  for (const attr of node.getAttributes()) {
11486
- if (!import_ts_morph54.Node.isJsxAttribute(attr)) {
11969
+ if (!import_ts_morph55.Node.isJsxAttribute(attr)) {
11487
11970
  continue;
11488
11971
  }
11489
11972
  const initializer = attr.getInitializer();
11490
11973
  let valueExpression = null;
11491
11974
  if (initializer !== void 0) {
11492
- if (import_ts_morph54.Node.isJsxExpression(initializer)) {
11975
+ if (import_ts_morph55.Node.isJsxExpression(initializer)) {
11493
11976
  const inner = initializer.getExpression();
11494
11977
  valueExpression = inner ?? null;
11495
11978
  } else {
@@ -11512,11 +11995,11 @@ function findCallExpressionsByName(parent, name) {
11512
11995
  if (skip(node, traversal)) {
11513
11996
  return;
11514
11997
  }
11515
- if (!import_ts_morph54.Node.isCallExpression(node)) {
11998
+ if (!import_ts_morph55.Node.isCallExpression(node)) {
11516
11999
  return;
11517
12000
  }
11518
12001
  const callee = node.getExpression();
11519
- if (import_ts_morph54.Node.isIdentifier(callee) && callee.getText() === name) {
12002
+ if (import_ts_morph55.Node.isIdentifier(callee) && callee.getText() === name) {
11520
12003
  out.push(node);
11521
12004
  }
11522
12005
  });
@@ -11527,10 +12010,10 @@ function resolveAttributeValueFunction(attr, parent) {
11527
12010
  if (expr === null) {
11528
12011
  return null;
11529
12012
  }
11530
- if (import_ts_morph54.Node.isArrowFunction(expr) || import_ts_morph54.Node.isFunctionExpression(expr)) {
12013
+ if (import_ts_morph55.Node.isArrowFunction(expr) || import_ts_morph55.Node.isFunctionExpression(expr)) {
11531
12014
  return { func: expr, localName: null };
11532
12015
  }
11533
- if (import_ts_morph54.Node.isIdentifier(expr)) {
12016
+ if (import_ts_morph55.Node.isIdentifier(expr)) {
11534
12017
  const symbol = expr.getSymbol();
11535
12018
  if (symbol === void 0) {
11536
12019
  return null;
@@ -11549,15 +12032,15 @@ function resolveAttributeValueFunction(attr, parent) {
11549
12032
  return null;
11550
12033
  }
11551
12034
  function tryExtractFunction(decl) {
11552
- if (import_ts_morph54.Node.isFunctionDeclaration(decl)) {
12035
+ if (import_ts_morph55.Node.isFunctionDeclaration(decl)) {
11553
12036
  return decl;
11554
12037
  }
11555
- if (import_ts_morph54.Node.isVariableDeclaration(decl)) {
12038
+ if (import_ts_morph55.Node.isVariableDeclaration(decl)) {
11556
12039
  const init = decl.getInitializer();
11557
12040
  if (init === void 0) {
11558
12041
  return null;
11559
12042
  }
11560
- if (import_ts_morph54.Node.isArrowFunction(init) || import_ts_morph54.Node.isFunctionExpression(init)) {
12043
+ if (import_ts_morph55.Node.isArrowFunction(init) || import_ts_morph55.Node.isFunctionExpression(init)) {
11561
12044
  return init;
11562
12045
  }
11563
12046
  }
@@ -11578,7 +12061,7 @@ function getCallArgumentFunction(call, position) {
11578
12061
  if (arg === void 0) {
11579
12062
  return null;
11580
12063
  }
11581
- if (import_ts_morph54.Node.isArrowFunction(arg) || import_ts_morph54.Node.isFunctionExpression(arg)) {
12064
+ if (import_ts_morph55.Node.isArrowFunction(arg) || import_ts_morph55.Node.isFunctionExpression(arg)) {
11582
12065
  return arg;
11583
12066
  }
11584
12067
  return null;
@@ -11590,7 +12073,7 @@ function readArrayLiteralText(node) {
11590
12073
  if (node === null) {
11591
12074
  return null;
11592
12075
  }
11593
- if (import_ts_morph54.Node.isArrayLiteralExpression(node)) {
12076
+ if (import_ts_morph55.Node.isArrayLiteralExpression(node)) {
11594
12077
  return node.getElements().map((e) => e.getText());
11595
12078
  }
11596
12079
  return [node.getText()];
@@ -11813,13 +12296,13 @@ var raise = (msg) => {
11813
12296
  throw new Error(msg);
11814
12297
  };
11815
12298
  function bindingPatternNames(nameNode) {
11816
- if (import_ts_morph55.Node.isObjectBindingPattern(nameNode)) {
12299
+ if (import_ts_morph56.Node.isObjectBindingPattern(nameNode)) {
11817
12300
  return nameNode.getElements().map((e) => e.getName());
11818
12301
  }
11819
- if (import_ts_morph55.Node.isArrayBindingPattern(nameNode)) {
12302
+ if (import_ts_morph56.Node.isArrayBindingPattern(nameNode)) {
11820
12303
  const names = [];
11821
12304
  for (const element of nameNode.getElements()) {
11822
- if (import_ts_morph55.Node.isOmittedExpression(element)) {
12305
+ if (import_ts_morph56.Node.isOmittedExpression(element)) {
11823
12306
  continue;
11824
12307
  }
11825
12308
  names.push(element.getName());
@@ -11830,7 +12313,7 @@ function bindingPatternNames(nameNode) {
11830
12313
  }
11831
12314
  function componentPropsParameters(param, mapping) {
11832
12315
  const nameNode = param.getNameNode();
11833
- if (!import_ts_morph55.Node.isObjectBindingPattern(nameNode)) {
12316
+ if (!import_ts_morph56.Node.isObjectBindingPattern(nameNode)) {
11834
12317
  const type = param.getType();
11835
12318
  return [
11836
12319
  {
@@ -11956,11 +12439,11 @@ function extractParameters(func, inputMapping) {
11956
12439
  function extractDependencyCalls(func) {
11957
12440
  const results = [];
11958
12441
  func.forEachDescendant((node, traversal) => {
11959
- if (node !== func && (import_ts_morph55.Node.isFunctionDeclaration(node) || import_ts_morph55.Node.isFunctionExpression(node) || import_ts_morph55.Node.isArrowFunction(node) || import_ts_morph55.Node.isMethodDeclaration(node))) {
12442
+ if (node !== func && (import_ts_morph56.Node.isFunctionDeclaration(node) || import_ts_morph56.Node.isFunctionExpression(node) || import_ts_morph56.Node.isArrowFunction(node) || import_ts_morph56.Node.isMethodDeclaration(node))) {
11960
12443
  traversal.skip();
11961
12444
  return;
11962
12445
  }
11963
- if (!import_ts_morph55.Node.isVariableDeclaration(node)) {
12446
+ if (!import_ts_morph56.Node.isVariableDeclaration(node)) {
11964
12447
  return;
11965
12448
  }
11966
12449
  const init = node.getInitializer();
@@ -11969,19 +12452,19 @@ function extractDependencyCalls(func) {
11969
12452
  }
11970
12453
  let isAsync = false;
11971
12454
  let callExpr = init;
11972
- if (import_ts_morph55.Node.isAwaitExpression(init)) {
12455
+ if (import_ts_morph56.Node.isAwaitExpression(init)) {
11973
12456
  isAsync = true;
11974
12457
  callExpr = init.getExpression();
11975
12458
  }
11976
- if (!import_ts_morph55.Node.isCallExpression(callExpr)) {
12459
+ if (!import_ts_morph56.Node.isCallExpression(callExpr)) {
11977
12460
  return;
11978
12461
  }
11979
12462
  const calleeName = callExpr.getExpression().getText();
11980
12463
  const nameNode = node.getNameNode();
11981
- const assignedTo = import_ts_morph55.Node.isIdentifier(nameNode) ? nameNode.getText() : null;
12464
+ const assignedTo = import_ts_morph56.Node.isIdentifier(nameNode) ? nameNode.getText() : null;
11982
12465
  const declList = node.getParent();
11983
12466
  const varStmt = declList?.getParent();
11984
- const locationNode = varStmt !== void 0 && import_ts_morph55.Node.isVariableStatement(varStmt) ? varStmt : node;
12467
+ const locationNode = varStmt !== void 0 && import_ts_morph56.Node.isVariableStatement(varStmt) ? varStmt : node;
11985
12468
  results.push({
11986
12469
  name: calleeName,
11987
12470
  assignedTo,
@@ -12107,6 +12590,36 @@ function extractCodeStructure(unit, pack, invocationRecognizers = [], accessReco
12107
12590
  function responseAccessorNames(pack, kind) {
12108
12591
  return pack.responseSemantics?.filter((m) => m.semantics.type === kind).map((m) => m.name);
12109
12592
  }
12593
+ var ACCESSOR_FIELD_SEMANTICS = {
12594
+ bodyAccessors: "body",
12595
+ statusAccessors: "statusCode",
12596
+ successAccessors: "statusRange"
12597
+ };
12598
+ function responseAccessors(pack) {
12599
+ const out = {};
12600
+ for (const [field, kind] of Object.entries(ACCESSOR_FIELD_SEMANTICS)) {
12601
+ const names = responseAccessorNames(pack, kind);
12602
+ if (names !== void 0 && names.length > 0) {
12603
+ out[field] = names;
12604
+ }
12605
+ }
12606
+ return out;
12607
+ }
12608
+ var FUNCTION_FALLTHROUGH_TERMINAL = {
12609
+ kind: "return",
12610
+ match: { type: "functionFallthrough" },
12611
+ extraction: {}
12612
+ };
12613
+ function terminalsFor(unit, pack) {
12614
+ const declared = unit.terminals ?? pack.terminals;
12615
+ if (unit.callSite === void 0) {
12616
+ return declared;
12617
+ }
12618
+ if (declared.some((p) => p.match.type === "functionFallthrough")) {
12619
+ return declared;
12620
+ }
12621
+ return [...declared, FUNCTION_FALLTHROUGH_TERMINAL];
12622
+ }
12110
12623
  function readCodeStructure(unit, pack, invocationRecognizers, accessRecognizers, barriers, resolveWrittenValue) {
12111
12624
  const { func, kind, name } = unit;
12112
12625
  if (func === null) {
@@ -12118,7 +12631,7 @@ function readCodeStructure(unit, pack, invocationRecognizers, accessRecognizers,
12118
12631
  );
12119
12632
  const extracted = extractRawBranches(
12120
12633
  func,
12121
- unit.terminals ?? pack.terminals,
12634
+ terminalsFor(unit, pack),
12122
12635
  invocationRecognizers,
12123
12636
  accessRecognizers,
12124
12637
  barriers,
@@ -12154,8 +12667,7 @@ function readCodeStructure(unit, pack, invocationRecognizers, accessRecognizers,
12154
12667
  }
12155
12668
  }
12156
12669
  }
12157
- const bodyAccessors = unit.callSite !== void 0 ? responseAccessorNames(pack, "body") : void 0;
12158
- const statusAccessors = unit.callSite !== void 0 ? responseAccessorNames(pack, "statusCode") : void 0;
12670
+ const accessors = unit.callSite !== void 0 ? responseAccessors(pack) : {};
12159
12671
  return {
12160
12672
  identity: {
12161
12673
  name,
@@ -12179,8 +12691,8 @@ function readCodeStructure(unit, pack, invocationRecognizers, accessRecognizers,
12179
12691
  bodyContent: bodyContentOf(func),
12180
12692
  dependencyCalls: depCalls,
12181
12693
  declaredContract: null,
12182
- ...bodyAccessors !== void 0 && bodyAccessors.length > 0 ? { bodyAccessors } : {},
12183
- ...statusAccessors !== void 0 && statusAccessors.length > 0 ? { statusAccessors } : {}
12694
+ ...accessors,
12695
+ ...unit.callSite !== void 0 && pack.failureDelivery !== void 0 ? { failureDelivery: pack.failureDelivery } : {}
12184
12696
  };
12185
12697
  }
12186
12698
  function extractConsumerBinding(unit, pattern, pack, resolution) {
@@ -12237,9 +12749,9 @@ function extractBindingMethod(binding, callSite, pack, resolution) {
12237
12749
  if (m.type === "fromArgumentProperty") {
12238
12750
  const args = callSite.callExpression.getArguments();
12239
12751
  const arg = args[m.position];
12240
- if (arg !== void 0 && import_ts_morph55.Node.isObjectLiteralExpression(arg)) {
12752
+ if (arg !== void 0 && import_ts_morph56.Node.isObjectLiteralExpression(arg)) {
12241
12753
  const prop = arg.getProperty(m.property);
12242
- if (prop !== void 0 && import_ts_morph55.Node.isPropertyAssignment(prop)) {
12754
+ if (prop !== void 0 && import_ts_morph56.Node.isPropertyAssignment(prop)) {
12243
12755
  const init = prop.getInitializer();
12244
12756
  if (init !== void 0) {
12245
12757
  const value = stringValueOf(init, resolution);
@@ -12368,22 +12880,22 @@ function extractBindingPath(binding, callSite, pack, resolution) {
12368
12880
  return void 0;
12369
12881
  }
12370
12882
  function pathFromUrlNode(node) {
12371
- if (import_ts_morph55.Node.isStringLiteral(node)) {
12883
+ if (import_ts_morph56.Node.isStringLiteral(node)) {
12372
12884
  return pathFromLiteralUrl(node.getLiteralValue());
12373
12885
  }
12374
- if (import_ts_morph55.Node.isNoSubstitutionTemplateLiteral(node)) {
12886
+ if (import_ts_morph56.Node.isNoSubstitutionTemplateLiteral(node)) {
12375
12887
  return pathFromLiteralUrl(node.getLiteralValue());
12376
12888
  }
12377
- if (import_ts_morph55.Node.isTemplateExpression(node)) {
12889
+ if (import_ts_morph56.Node.isTemplateExpression(node)) {
12378
12890
  return pathFromTemplateUrl(node);
12379
12891
  }
12380
12892
  return void 0;
12381
12893
  }
12382
12894
  function placeholderName(expr) {
12383
- if (import_ts_morph55.Node.isIdentifier(expr)) {
12895
+ if (import_ts_morph56.Node.isIdentifier(expr)) {
12384
12896
  return expr.getText();
12385
12897
  }
12386
- if (import_ts_morph55.Node.isPropertyAccessExpression(expr)) {
12898
+ if (import_ts_morph56.Node.isPropertyAccessExpression(expr)) {
12387
12899
  return expr.getName();
12388
12900
  }
12389
12901
  return "param";
@@ -12534,12 +13046,20 @@ function extractFromSourceFile(sourceFile, frameworks, claimedUnits, options, ta
12534
13046
  if (claimant.pack === pack.name && claimant.file === sourceFile.getFilePath() && tally !== void 0) {
12535
13047
  tally.selfCollisions += 1;
12536
13048
  }
13049
+ if (claimant.file !== sourceFile.getFilePath()) {
13050
+ recordFileDependency(claimant.file);
13051
+ }
12537
13052
  continue;
12538
13053
  }
12539
13054
  claimed.set(claimKey, {
12540
13055
  pack: pack.name,
12541
13056
  file: sourceFile.getFilePath()
12542
13057
  });
13058
+ recordUnitClaim(claimKey, pack.name);
13059
+ const declaringFile = unit.func?.getSourceFile().getFilePath();
13060
+ if (declaringFile !== void 0 && declaringFile !== sourceFile.getFilePath()) {
13061
+ recordFileDependency(declaringFile);
13062
+ }
12543
13063
  unitsWalkedHere += 1;
12544
13064
  if (tally !== void 0) {
12545
13065
  tally.unitsClaimed += 1;
@@ -12578,6 +13098,9 @@ function extractFromSourceFile(sourceFile, frameworks, claimedUnits, options, ta
12578
13098
  }
12579
13099
  if (unit.resolverInfo.schemaSdl !== void 0) {
12580
13100
  raw.graphqlSchemaSdl = unit.resolverInfo.schemaSdl;
13101
+ if (unit.resolverInfo.schemaDocument !== void 0) {
13102
+ raw.sourceDocumentLabel = unit.resolverInfo.schemaDocument;
13103
+ }
12581
13104
  if (unit.resolverInfo.typeName !== null) {
12582
13105
  const contract = deriveGraphqlContract(
12583
13106
  unit.resolverInfo.schemaSdl,
@@ -12766,6 +13289,37 @@ function fileImporting(project, modulePrefix) {
12766
13289
  }
12767
13290
  return void 0;
12768
13291
  }
13292
+ function liftSchemasOntoDocuments(summaries) {
13293
+ const sdlByDocument = /* @__PURE__ */ new Map();
13294
+ for (const summary of summaries) {
13295
+ const label = (0, import_behavioral_ir6.readSourceDocumentMetadata)(summary)?.label;
13296
+ const graphql = (0, import_behavioral_ir6.readGraphqlMetadata)(summary);
13297
+ if (label === void 0 || graphql?.schemaSdl === void 0) {
13298
+ continue;
13299
+ }
13300
+ sdlByDocument.set(label, graphql.schemaSdl);
13301
+ const { schemaSdl: _movedToTheDocument, ...rest } = graphql;
13302
+ summary.metadata = (0, import_behavioral_ir6.withGraphqlMetadata)(summary.metadata, rest);
13303
+ }
13304
+ for (const [label, schemaSdl] of sdlByDocument) {
13305
+ summaries.push(schemaDocumentSummary(label, schemaSdl));
13306
+ }
13307
+ }
13308
+ function schemaDocumentSummary(label, schemaSdl) {
13309
+ return {
13310
+ kind: "library",
13311
+ location: { file: label, range: { start: 0, end: 0 }, exportName: null },
13312
+ identity: { name: label, exportPath: null, boundaryBinding: null },
13313
+ inputs: [],
13314
+ transitions: [],
13315
+ gaps: [],
13316
+ confidence: { source: "declared", level: "high" },
13317
+ metadata: (0, import_behavioral_ir6.withGraphqlMetadata)(
13318
+ (0, import_behavioral_ir6.withSourceDocumentMetadata)(void 0, { label }),
13319
+ { schemaSdl }
13320
+ )
13321
+ };
13322
+ }
12769
13323
  function libraryEnvReadMarker(pack, declared, importer) {
12770
13324
  return {
12771
13325
  kind: "library",
@@ -12818,7 +13372,7 @@ function expandWrapperCallers(summaries, project, options) {
12818
13372
  continue;
12819
13373
  }
12820
13374
  const binding = s.identity.boundaryBinding;
12821
- if (binding === null || binding.semantics.name !== "rest" || binding.semantics.method === null || binding.semantics.path !== null) {
13375
+ if (binding === null || binding.semantics.name !== "rest" || binding.semantics.method === null) {
12822
13376
  continue;
12823
13377
  }
12824
13378
  lookup ??= createSourceFileLookup(project);
@@ -12846,11 +13400,21 @@ function findWrapperPathParam(summary, lookup) {
12846
13400
  if (func === null) {
12847
13401
  return null;
12848
13402
  }
12849
- const params = func.getParameters();
12850
- if (params.length === 0) {
13403
+ const names = func.getParameters().map((p) => p.getName());
13404
+ if (names.length === 0) {
12851
13405
  return null;
12852
13406
  }
12853
- return { func, pathParamPosition: 0 };
13407
+ for (const call of func.getDescendantsOfKind(import_ts_morph56.SyntaxKind.CallExpression)) {
13408
+ const first = call.getArguments()[0];
13409
+ if (first === void 0 || !import_ts_morph56.Node.isIdentifier(first)) {
13410
+ continue;
13411
+ }
13412
+ const at = names.indexOf(first.getText());
13413
+ if (at !== -1) {
13414
+ return { func, pathParamPosition: at };
13415
+ }
13416
+ }
13417
+ return null;
12854
13418
  }
12855
13419
  function synthesizeCallerSummaries(wrapper, _project, options) {
12856
13420
  const nameNode = wrapperNameNode(wrapper.func);
@@ -12891,16 +13455,16 @@ function synthesizeCallerSummaries(wrapper, _project, options) {
12891
13455
  return out;
12892
13456
  }
12893
13457
  function wrapperNameNode(func) {
12894
- if (import_ts_morph55.Node.isFunctionDeclaration(func) || import_ts_morph55.Node.isMethodDeclaration(func)) {
13458
+ if (import_ts_morph56.Node.isFunctionDeclaration(func) || import_ts_morph56.Node.isMethodDeclaration(func)) {
12895
13459
  const name = func.getNameNode();
12896
- if (name !== void 0 && import_ts_morph55.Node.isIdentifier(name)) {
13460
+ if (name !== void 0 && import_ts_morph56.Node.isIdentifier(name)) {
12897
13461
  return name;
12898
13462
  }
12899
13463
  }
12900
13464
  const parent = func.getParent();
12901
- if (parent !== void 0 && import_ts_morph55.Node.isVariableDeclaration(parent)) {
13465
+ if (parent !== void 0 && import_ts_morph56.Node.isVariableDeclaration(parent)) {
12902
13466
  const nameNode = parent.getNameNode();
12903
- if (import_ts_morph55.Node.isIdentifier(nameNode)) {
13467
+ if (import_ts_morph56.Node.isIdentifier(nameNode)) {
12904
13468
  return nameNode;
12905
13469
  }
12906
13470
  }
@@ -12909,10 +13473,10 @@ function wrapperNameNode(func) {
12909
13473
  function enclosingCall(node) {
12910
13474
  let current2 = node.getParent();
12911
13475
  while (current2 !== void 0) {
12912
- if (import_ts_morph55.Node.isCallExpression(current2)) {
13476
+ if (import_ts_morph56.Node.isCallExpression(current2)) {
12913
13477
  return current2;
12914
13478
  }
12915
- if (import_ts_morph55.Node.isPropertyAccessExpression(current2)) {
13479
+ if (import_ts_morph56.Node.isPropertyAccessExpression(current2)) {
12916
13480
  current2 = current2.getParent();
12917
13481
  continue;
12918
13482
  }
@@ -12923,7 +13487,7 @@ function enclosingCall(node) {
12923
13487
  function enclosingFunction(node) {
12924
13488
  let current2 = node.getParent();
12925
13489
  while (current2 !== void 0) {
12926
- if (import_ts_morph55.Node.isFunctionDeclaration(current2) || import_ts_morph55.Node.isFunctionExpression(current2) || import_ts_morph55.Node.isArrowFunction(current2) || import_ts_morph55.Node.isMethodDeclaration(current2)) {
13490
+ if (import_ts_morph56.Node.isFunctionDeclaration(current2) || import_ts_morph56.Node.isFunctionExpression(current2) || import_ts_morph56.Node.isArrowFunction(current2) || import_ts_morph56.Node.isMethodDeclaration(current2)) {
12927
13491
  return current2;
12928
13492
  }
12929
13493
  current2 = current2.getParent();
@@ -12931,13 +13495,13 @@ function enclosingFunction(node) {
12931
13495
  return null;
12932
13496
  }
12933
13497
  function literalOrTemplate(arg) {
12934
- if (import_ts_morph55.Node.isStringLiteral(arg)) {
13498
+ if (import_ts_morph56.Node.isStringLiteral(arg)) {
12935
13499
  return arg.getLiteralValue();
12936
13500
  }
12937
- if (import_ts_morph55.Node.isNoSubstitutionTemplateLiteral(arg)) {
13501
+ if (import_ts_morph56.Node.isNoSubstitutionTemplateLiteral(arg)) {
12938
13502
  return arg.getLiteralValue();
12939
13503
  }
12940
- if (import_ts_morph55.Node.isTemplateExpression(arg)) {
13504
+ if (import_ts_morph56.Node.isTemplateExpression(arg)) {
12941
13505
  let path11 = arg.getHead().getLiteralText();
12942
13506
  for (const span of arg.getTemplateSpans()) {
12943
13507
  path11 += `{${placeholderName(span.getExpression())}}`;
@@ -12948,13 +13512,13 @@ function literalOrTemplate(arg) {
12948
13512
  return void 0;
12949
13513
  }
12950
13514
  function callerName(func) {
12951
- if (import_ts_morph55.Node.isFunctionDeclaration(func) || import_ts_morph55.Node.isMethodDeclaration(func)) {
13515
+ if (import_ts_morph56.Node.isFunctionDeclaration(func) || import_ts_morph56.Node.isMethodDeclaration(func)) {
12952
13516
  return func.getName() ?? "anonymous";
12953
13517
  }
12954
13518
  const parent = func.getParent();
12955
- if (parent !== void 0 && import_ts_morph55.Node.isVariableDeclaration(parent)) {
13519
+ if (parent !== void 0 && import_ts_morph56.Node.isVariableDeclaration(parent)) {
12956
13520
  const nameNode = parent.getNameNode();
12957
- if (import_ts_morph55.Node.isIdentifier(nameNode)) {
13521
+ if (import_ts_morph56.Node.isIdentifier(nameNode)) {
12958
13522
  return nameNode.getText();
12959
13523
  }
12960
13524
  }
@@ -13006,8 +13570,8 @@ function buildCallerSummary(wrapper, callerFunc, callExpr, path11, options) {
13006
13570
  summary.confidence = { source: "inferred_static", level: "low" };
13007
13571
  return summary;
13008
13572
  }
13009
- function named(summaries, workspace) {
13010
- const projectRoot = commonDirectoryOf(summaries.map((s) => s.location.file));
13573
+ function named(summaries, workspace, storedProjectRoot) {
13574
+ const projectRoot = storedProjectRoot ?? commonDirectoryOf(summaries.map((s) => s.location.file));
13011
13575
  nameSummaries(summaries, {
13012
13576
  workspace: workspace ?? workspaceNameFor(projectRoot),
13013
13577
  projectRoot
@@ -13059,7 +13623,7 @@ function loadRunFiles(project, candidatePaths, timer) {
13059
13623
  return { sourceFiles, deep };
13060
13624
  }
13061
13625
  function createTypeScriptAdapter(config) {
13062
- const project = config.project ?? new import_ts_morph55.Project(
13626
+ const project = config.project ?? new import_ts_morph56.Project(
13063
13627
  config.tsConfigFilePath !== void 0 ? {
13064
13628
  tsConfigFilePath: config.tsConfigFilePath,
13065
13629
  skipAddingFilesFromTsConfig: true
@@ -13138,7 +13702,29 @@ function createTypeScriptAdapter(config) {
13138
13702
  if (config.onTiming !== void 0) {
13139
13703
  config.onTiming(timer.report());
13140
13704
  }
13141
- return named(lookup.summaries, config.workspace);
13705
+ return named(lookup.summaries, config.workspace, lookup.projectRoot);
13706
+ }
13707
+ const plan = lookup.diagnostic.missReason === "files-changed" ? await timer.timeAsync("cache.plan", () => cache.plan(cacheInput)) : null;
13708
+ if (plan !== null && plan.changed.size === 0 && plan.removed.size === 0) {
13709
+ try {
13710
+ await cache.write(
13711
+ cacheInput,
13712
+ plan.allSummaries(),
13713
+ plan.attribution()
13714
+ );
13715
+ } catch {
13716
+ }
13717
+ if (config.onCacheDiagnostic !== void 0) {
13718
+ config.onCacheDiagnostic({ kind: "hit" });
13719
+ }
13720
+ if (config.onTiming !== void 0) {
13721
+ config.onTiming(timer.report());
13722
+ }
13723
+ return named(
13724
+ plan.allSummaries(),
13725
+ config.workspace,
13726
+ plan.storedProjectRoot ?? void 0
13727
+ );
13142
13728
  }
13143
13729
  let candidatePaths = null;
13144
13730
  if (lazyEligible) {
@@ -13169,16 +13755,58 @@ function createTypeScriptAdapter(config) {
13169
13755
  "mountPrefix",
13170
13756
  () => buildMountPrefixIndex(packsByFile, resolution)
13171
13757
  );
13758
+ const caching = cacheDir !== null;
13759
+ const validRoots = /* @__PURE__ */ new Set();
13760
+ if (plan !== null) {
13761
+ const packNamesByPath = /* @__PURE__ */ new Map();
13762
+ for (const [sf, packs] of packsByFile) {
13763
+ packNamesByPath.set(
13764
+ sf.getFilePath(),
13765
+ packs.map((p) => p.name).sort().join(",")
13766
+ );
13767
+ }
13768
+ for (const rootPath of plan.validRoots) {
13769
+ const record = plan.roots.get(rootPath);
13770
+ if (record === void 0) {
13771
+ continue;
13772
+ }
13773
+ if (!mountAssumptionsAgree(record, mountPrefixes)) {
13774
+ continue;
13775
+ }
13776
+ const stored = [...record.packs].sort().join(",");
13777
+ if ((packNamesByPath.get(rootPath) ?? "") !== stored) {
13778
+ continue;
13779
+ }
13780
+ validRoots.add(rootPath);
13781
+ }
13782
+ }
13783
+ const reused = plan !== null && validRoots.size > 0 ? plan.reuse(validRoots) : null;
13172
13784
  const claimedUnits = /* @__PURE__ */ new Map();
13785
+ if (reused !== null && plan !== null) {
13786
+ for (const rootPath of validRoots) {
13787
+ for (const claim of plan.roots.get(rootPath)?.claims ?? []) {
13788
+ claimedUnits.set(claim.key, { pack: claim.pack, file: rootPath });
13789
+ }
13790
+ }
13791
+ }
13792
+ const walkList = reused === null ? sourceFiles : sourceFiles.filter((sf) => !validRoots.has(sf.getFilePath()));
13793
+ const sinkByRoot = /* @__PURE__ */ new Map();
13794
+ const ownersBySummary = /* @__PURE__ */ new Map();
13173
13795
  timer.time("extract per-file", () => {
13174
- for (const sourceFile of sourceFiles) {
13796
+ for (const sourceFile of walkList) {
13797
+ const rootPath = sourceFile.getFilePath();
13798
+ const sink = createDependencySink();
13799
+ if (caching) {
13800
+ sinkByRoot.set(rootPath, sink);
13801
+ }
13175
13802
  const applicablePacks = packsByFile.get(sourceFile);
13176
13803
  if (applicablePacks === void 0) {
13177
13804
  continue;
13178
13805
  }
13179
13806
  try {
13180
- summaries.push(
13181
- ...extractFromSourceFile(
13807
+ const walked = withDependencySink(
13808
+ sink,
13809
+ () => extractFromSourceFile(
13182
13810
  sourceFile,
13183
13811
  applicablePacks,
13184
13812
  claimedUnits,
@@ -13188,6 +13816,10 @@ function createTypeScriptAdapter(config) {
13188
13816
  mountPrefixes
13189
13817
  )
13190
13818
  );
13819
+ for (const summary of walked) {
13820
+ ownersBySummary.set(summary, /* @__PURE__ */ new Set([rootPath]));
13821
+ }
13822
+ summaries.push(...walked);
13191
13823
  } catch (error) {
13192
13824
  if (!(error instanceof RangeError)) {
13193
13825
  throw error;
@@ -13196,23 +13828,32 @@ function createTypeScriptAdapter(config) {
13196
13828
  }
13197
13829
  }
13198
13830
  });
13831
+ const wrapperInput = reused === null ? summaries : [...summaries, ...reused.summaries];
13199
13832
  const withWrappers = timer.time(
13200
13833
  "expandWrapperCallers",
13201
- () => expandWrapperCallers(summaries, project, config.extractorOptions)
13834
+ () => expandWrapperCallers(wrapperInput, project, config.extractorOptions)
13202
13835
  );
13836
+ const pipeline = reused === null ? withWrappers : [...summaries, ...withWrappers.slice(wrapperInput.length)];
13203
13837
  const withSubUnits = timer.time(
13204
13838
  "synthesizeSubUnits",
13205
13839
  () => synthesizeSubUnits(
13206
- withWrappers,
13840
+ pipeline,
13207
13841
  project,
13208
13842
  config.frameworks,
13209
13843
  config.extractorOptions,
13210
- tallies
13844
+ tallies,
13845
+ (created, parent) => {
13846
+ const parentOwners = ownersBySummary.get(parent);
13847
+ if (parentOwners !== void 0) {
13848
+ ownersBySummary.set(created, new Set(parentOwners));
13849
+ }
13850
+ }
13211
13851
  )
13212
13852
  );
13213
13853
  const closureFacts = {
13214
13854
  db: new import_datalog6.Database(),
13215
- unitKeyBySummary: /* @__PURE__ */ new Map()
13855
+ unitKeyBySummary: /* @__PURE__ */ new Map(),
13856
+ ...caching ? { filesByKey: /* @__PURE__ */ new Map() } : {}
13216
13857
  };
13217
13858
  const withClosure = config.includeReachable !== false ? timer.time(
13218
13859
  "expandReachableClosure",
@@ -13230,12 +13871,28 @@ function createTypeScriptAdapter(config) {
13230
13871
  access: collectAccessRecognizers(config.frameworks, tallies),
13231
13872
  resolveWrittenValue: (value) => resolution.resolveWrittenValue(value),
13232
13873
  resolveCallable: (value, alsoFrom) => resolution.resolveCallable(value, alsoFrom)
13233
- }
13874
+ },
13875
+ // Reached units the cache already serves emit nothing,
13876
+ // the way a cold run's seeds do not.
13877
+ reused?.summaries ?? []
13234
13878
  )
13235
13879
  ) : withSubUnits;
13880
+ const closureOwnership = caching && config.includeReachable !== false ? timer.time(
13881
+ "cache.attributeClosure",
13882
+ () => attributeReachedSummaries(
13883
+ withSubUnits,
13884
+ withClosure.slice(withSubUnits.length),
13885
+ closureFacts,
13886
+ ownersBySummary
13887
+ )
13888
+ ) : null;
13889
+ const merged = timer.time(
13890
+ "cache.merge",
13891
+ () => mergeWithReused(reused, withClosure, ownersBySummary)
13892
+ );
13236
13893
  const enriched = timer.time(
13237
13894
  "enrichRethrows",
13238
- () => enrichRethrows(withClosure, project, closureFacts)
13895
+ () => enrichRethrows(merged.summaries, project, closureFacts)
13239
13896
  );
13240
13897
  if (config.includeReachable !== false) {
13241
13898
  timer.time(
@@ -13251,6 +13908,10 @@ function createTypeScriptAdapter(config) {
13251
13908
  "emitLibraryEnvReadMarkers",
13252
13909
  () => emitLibraryEnvReadMarkers(enriched, project, config.frameworks)
13253
13910
  );
13911
+ timer.time(
13912
+ "liftSchemasOntoDocuments",
13913
+ () => liftSchemasOntoDocuments(enriched)
13914
+ );
13254
13915
  timer.time(
13255
13916
  "stampGraphqlClientRefs",
13256
13917
  () => stampGraphqlClientRefs(
@@ -13260,12 +13921,45 @@ function createTypeScriptAdapter(config) {
13260
13921
  resolution
13261
13922
  )
13262
13923
  );
13924
+ if (config.onCacheDiagnostic !== void 0 && plan !== null) {
13925
+ config.onCacheDiagnostic({
13926
+ kind: "partial",
13927
+ partial: {
13928
+ filesChanged: plan.changed.size,
13929
+ filesRemoved: plan.removed.size,
13930
+ rootsReused: validRoots.size,
13931
+ rootsReextracted: walkList.length,
13932
+ rootsDeclined: plan.rootsDeclined,
13933
+ summariesReused: merged.reusedKept.length
13934
+ }
13935
+ });
13936
+ }
13937
+ const runProjectRoot = commonDirectoryOf(
13938
+ sourceFiles.map((f) => f.getFilePath())
13939
+ );
13263
13940
  await timer.timeAsync("cache.write", async () => {
13264
- if (enriched.length === 0) {
13941
+ if (!caching || enriched.length === 0) {
13265
13942
  return;
13266
13943
  }
13267
13944
  try {
13268
- await cache.write(cacheInput, enriched);
13945
+ const attribution = buildCacheAttribution({
13946
+ project,
13947
+ plan,
13948
+ validRoots,
13949
+ sinkByRoot,
13950
+ ownersBySummary,
13951
+ closureOwnership,
13952
+ unitKeyBySummary: closureFacts.unitKeyBySummary,
13953
+ filesByKey: closureFacts.filesByKey,
13954
+ packsByFile,
13955
+ reusedKept: merged.reusedKept,
13956
+ reusedOwners: merged.reusedOwners,
13957
+ // Everything after the reused prefix, the passes' own
13958
+ // additions (markers, schema documents) included.
13959
+ fresh: enriched.slice(merged.reusedKept.length),
13960
+ projectRoot: runProjectRoot
13961
+ });
13962
+ await cache.write(cacheInput, enriched, attribution);
13269
13963
  } catch {
13270
13964
  }
13271
13965
  });
@@ -13278,7 +13972,7 @@ function createTypeScriptAdapter(config) {
13278
13972
  packs: config.frameworks,
13279
13973
  tallies,
13280
13974
  filesInProject: tsconfigFileList?.length ?? null,
13281
- filesWalked: sourceFiles.length,
13975
+ filesWalked: walkList.length,
13282
13976
  summaries: enriched,
13283
13977
  tsConfigFilePath: config.tsConfigFilePath,
13284
13978
  projectRoot: commonDirectoryOf(
@@ -13298,7 +13992,200 @@ function createTypeScriptAdapter(config) {
13298
13992
  }
13299
13993
  };
13300
13994
  }
13301
- function synthesizeSubUnits(summaries, project, frameworks, options, tallies) {
13995
+ function mountAssumptionsAgree(record, index) {
13996
+ return Object.entries(record.mountPrefixes).every(
13997
+ ([childId, prefix]) => (index.prefixForId?.(childId) ?? "") === prefix
13998
+ );
13999
+ }
14000
+ function attributeReachedSummaries(seeds, reached, facts, owners) {
14001
+ const adjacency = /* @__PURE__ */ new Map();
14002
+ for (const [from, to] of facts.db.facts("calls")) {
14003
+ const key = String(from);
14004
+ const bucket = adjacency.get(key) ?? [];
14005
+ bucket.push(String(to));
14006
+ adjacency.set(key, bucket);
14007
+ }
14008
+ const seedKeysByRoot = /* @__PURE__ */ new Map();
14009
+ for (const seed of seeds) {
14010
+ const key = facts.unitKeyBySummary.get(seed);
14011
+ if (key === void 0) {
14012
+ continue;
14013
+ }
14014
+ for (const root of owners.get(seed) ?? []) {
14015
+ const bucket = seedKeysByRoot.get(root) ?? [];
14016
+ bucket.push(key);
14017
+ seedKeysByRoot.set(root, bucket);
14018
+ }
14019
+ }
14020
+ const reachableByRoot = /* @__PURE__ */ new Map();
14021
+ const rootsByKey = /* @__PURE__ */ new Map();
14022
+ for (const [root, seedKeys] of seedKeysByRoot) {
14023
+ const seen = new Set(seedKeys);
14024
+ const queue = [...seedKeys];
14025
+ while (queue.length > 0) {
14026
+ const key = queue.pop();
14027
+ if (key === void 0) {
14028
+ continue;
14029
+ }
14030
+ for (const next of adjacency.get(key) ?? []) {
14031
+ if (!seen.has(next)) {
14032
+ seen.add(next);
14033
+ queue.push(next);
14034
+ }
14035
+ }
14036
+ const keyRoots = rootsByKey.get(key) ?? /* @__PURE__ */ new Set();
14037
+ keyRoots.add(root);
14038
+ rootsByKey.set(key, keyRoots);
14039
+ }
14040
+ reachableByRoot.set(root, seen);
14041
+ }
14042
+ for (const summary of reached) {
14043
+ const key = facts.unitKeyBySummary.get(summary);
14044
+ if (key === void 0) {
14045
+ continue;
14046
+ }
14047
+ const keyRoots = rootsByKey.get(key);
14048
+ if (keyRoots !== void 0 && keyRoots.size > 0) {
14049
+ owners.set(summary, new Set(keyRoots));
14050
+ }
14051
+ }
14052
+ return { reachableByRoot, rootsByKey };
14053
+ }
14054
+ function fileOfNodeKey(key) {
14055
+ const cut = key.lastIndexOf(":");
14056
+ return cut <= 0 ? null : key.slice(0, cut);
14057
+ }
14058
+ function summaryMergeKey(summary) {
14059
+ return JSON.stringify([
14060
+ summary.location.file,
14061
+ summary.location.range.start,
14062
+ summary.location.range.end,
14063
+ summary.kind,
14064
+ summary.identity.name,
14065
+ summary.identity.exportPath,
14066
+ summary.identity.boundaryBinding
14067
+ ]);
14068
+ }
14069
+ function mergeWithReused(reused, fresh, ownersBySummary) {
14070
+ if (reused === null) {
14071
+ return { summaries: fresh, reusedKept: [], reusedOwners: [] };
14072
+ }
14073
+ const freshByKey = /* @__PURE__ */ new Map();
14074
+ for (const summary of fresh) {
14075
+ freshByKey.set(summaryMergeKey(summary), summary);
14076
+ }
14077
+ const reusedKept = [];
14078
+ const reusedOwners = [];
14079
+ reused.summaries.forEach((summary, i) => {
14080
+ const owners = reused.owners[i] ?? [];
14081
+ const supersededBy = freshByKey.get(summaryMergeKey(summary));
14082
+ if (supersededBy !== void 0) {
14083
+ const freshOwners = ownersBySummary.get(supersededBy) ?? /* @__PURE__ */ new Set();
14084
+ for (const owner of owners) {
14085
+ freshOwners.add(owner);
14086
+ }
14087
+ ownersBySummary.set(supersededBy, freshOwners);
14088
+ return;
14089
+ }
14090
+ reusedKept.push(summary);
14091
+ reusedOwners.push(owners);
14092
+ });
14093
+ return { summaries: [...reusedKept, ...fresh], reusedKept, reusedOwners };
14094
+ }
14095
+ function readsRunLevelJoins(summary) {
14096
+ if ((0, import_behavioral_ir6.readSourceDocumentMetadata)(summary) !== void 0) {
14097
+ return true;
14098
+ }
14099
+ if ((0, import_behavioral_ir6.readGraphqlMetadata)(summary)?.client !== void 0) {
14100
+ return true;
14101
+ }
14102
+ const semantics = summary.identity.boundaryBinding?.semantics;
14103
+ return semantics !== void 0 && "operationType" in semantics;
14104
+ }
14105
+ function buildCacheAttribution(args) {
14106
+ const references = createReferenceIndex(
14107
+ args.project.getSourceFiles().filter((sf) => !sf.isDeclarationFile())
14108
+ );
14109
+ const packNamesByPath = /* @__PURE__ */ new Map();
14110
+ for (const [sf, packs] of args.packsByFile) {
14111
+ packNamesByPath.set(
14112
+ sf.getFilePath(),
14113
+ packs.map((p) => p.name)
14114
+ );
14115
+ }
14116
+ const summariesByRoot = /* @__PURE__ */ new Map();
14117
+ for (const summary of args.fresh) {
14118
+ for (const root of args.ownersBySummary.get(summary) ?? []) {
14119
+ const bucket = summariesByRoot.get(root) ?? [];
14120
+ bucket.push(summary);
14121
+ summariesByRoot.set(root, bucket);
14122
+ }
14123
+ }
14124
+ const roots = [];
14125
+ for (const [rootPath, sink] of args.sinkByRoot) {
14126
+ const deps = new Set(sink.files);
14127
+ for (const dep of references.directOf(rootPath)) {
14128
+ deps.add(dep);
14129
+ }
14130
+ let cacheable = true;
14131
+ for (const summary of summariesByRoot.get(rootPath) ?? []) {
14132
+ deps.add(summary.location.file);
14133
+ if (readsRunLevelJoins(summary)) {
14134
+ cacheable = false;
14135
+ }
14136
+ }
14137
+ const reachable = args.closureOwnership?.reachableByRoot.get(rootPath) ?? [];
14138
+ for (const key of reachable) {
14139
+ const keyFile = fileOfNodeKey(key);
14140
+ if (keyFile !== null) {
14141
+ deps.add(keyFile);
14142
+ for (const hop of references.directOf(keyFile)) {
14143
+ deps.add(hop);
14144
+ }
14145
+ }
14146
+ for (const filePath of args.filesByKey?.get(key) ?? []) {
14147
+ deps.add(filePath);
14148
+ }
14149
+ }
14150
+ deps.delete(rootPath);
14151
+ roots.push({
14152
+ path: rootPath,
14153
+ cacheable,
14154
+ deps: [...deps].sort(),
14155
+ claims: sink.claims,
14156
+ mountPrefixes: Object.fromEntries(sink.mountPrefixes),
14157
+ packs: packNamesByPath.get(rootPath) ?? []
14158
+ });
14159
+ }
14160
+ if (args.plan !== null) {
14161
+ for (const rootPath of args.validRoots) {
14162
+ const record = args.plan.roots.get(rootPath);
14163
+ if (record !== void 0) {
14164
+ roots.push(record);
14165
+ }
14166
+ }
14167
+ }
14168
+ const reusedOwners = args.reusedKept.map((summary, i) => {
14169
+ const combined = new Set(args.reusedOwners[i] ?? []);
14170
+ const key = args.unitKeyBySummary.get(summary);
14171
+ if (key !== void 0) {
14172
+ for (const root of args.closureOwnership?.rootsByKey.get(key) ?? []) {
14173
+ combined.add(root);
14174
+ }
14175
+ }
14176
+ return [...combined];
14177
+ });
14178
+ const owners = [
14179
+ ...reusedOwners,
14180
+ ...args.fresh.map((s) => [...args.ownersBySummary.get(s) ?? []])
14181
+ ];
14182
+ return {
14183
+ roots,
14184
+ owners,
14185
+ ...args.projectRoot === void 0 ? {} : { projectRoot: args.projectRoot }
14186
+ };
14187
+ }
14188
+ function synthesizeSubUnits(summaries, project, frameworks, options, tallies, onCreated) {
13302
14189
  const packByRecognition = /* @__PURE__ */ new Map();
13303
14190
  for (const pack of frameworks) {
13304
14191
  packByRecognition.set(pack.name, pack);
@@ -13341,6 +14228,7 @@ function synthesizeSubUnits(summaries, project, frameworks, options, tallies) {
13341
14228
  );
13342
14229
  if (summary !== null) {
13343
14230
  synthesized.push(summary);
14231
+ onCreated?.(summary, parent);
13344
14232
  }
13345
14233
  }
13346
14234
  }
@@ -13349,14 +14237,7 @@ function synthesizeSubUnits(summaries, project, frameworks, options, tallies) {
13349
14237
  var DEFAULT_SUB_UNIT_TERMINALS = [
13350
14238
  { kind: "return", match: { type: "returnStatement" }, extraction: {} },
13351
14239
  { kind: "throw", match: { type: "throwExpression" }, extraction: {} },
13352
- // A callback routinely falls off the end returning `undefined`, so
13353
- // fall-through gives it a default transition. HTTP packs do not opt in:
13354
- // a handler that sends no response should come out with none.
13355
- {
13356
- kind: "return",
13357
- match: { type: "functionFallthrough" },
13358
- extraction: {}
13359
- }
14240
+ FUNCTION_FALLTHROUGH_TERMINAL
13360
14241
  ];
13361
14242
  var DEFAULT_SUB_UNIT_INPUT_MAPPING = {
13362
14243
  type: "positionalParams",
@@ -13402,7 +14283,7 @@ function buildSubUnitSummary(subUnit, parent, invocationRecognizers, accessRecog
13402
14283
  // src/bootstrap/noTsconfigProject.ts
13403
14284
  var import_node_fs6 = __toESM(require("fs"), 1);
13404
14285
  var import_node_path10 = __toESM(require("path"), 1);
13405
- var import_ts_morph56 = require("ts-morph");
14286
+ var import_ts_morph57 = require("ts-morph");
13406
14287
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
13407
14288
  ".ts",
13408
14289
  ".tsx",
@@ -13450,12 +14331,12 @@ function findSourceFiles(rootDir, depth = 0) {
13450
14331
  return found;
13451
14332
  }
13452
14333
  function createProjectWithoutTsconfig(rootDir) {
13453
- const project = new import_ts_morph56.Project({
14334
+ const project = new import_ts_morph57.Project({
13454
14335
  skipAddingFilesFromTsConfig: true,
13455
14336
  compilerOptions: {
13456
- target: import_ts_morph56.ScriptTarget.ES2022,
13457
- module: import_ts_morph56.ModuleKind.ESNext,
13458
- moduleResolution: import_ts_morph56.ModuleResolutionKind.Bundler,
14337
+ target: import_ts_morph57.ScriptTarget.ES2022,
14338
+ module: import_ts_morph57.ModuleKind.ESNext,
14339
+ moduleResolution: import_ts_morph57.ModuleResolutionKind.Bundler,
13459
14340
  allowJs: true,
13460
14341
  checkJs: false,
13461
14342
  noEmit: true,
@@ -13495,10 +14376,10 @@ function findNearestTsconfig(startDir) {
13495
14376
  }
13496
14377
 
13497
14378
  // src/configuredCall.ts
13498
- var import_ts_morph57 = require("ts-morph");
14379
+ var import_ts_morph58 = require("ts-morph");
13499
14380
  function readConfiguredCall(call, ctx, spec) {
13500
14381
  const callee = call.getExpression();
13501
- if (!import_ts_morph57.Node.isPropertyAccessExpression(callee)) {
14382
+ if (!import_ts_morph58.Node.isPropertyAccessExpression(callee)) {
13502
14383
  return null;
13503
14384
  }
13504
14385
  if (callee.getName() !== spec.method) {
@@ -13543,10 +14424,10 @@ function readStringArg(arg) {
13543
14424
  }
13544
14425
  function rootIdentifier(node) {
13545
14426
  let current2 = node;
13546
- while (import_ts_morph57.Node.isPropertyAccessExpression(current2)) {
14427
+ while (import_ts_morph58.Node.isPropertyAccessExpression(current2)) {
13547
14428
  current2 = current2.getExpression();
13548
14429
  }
13549
- return import_ts_morph57.Node.isIdentifier(current2) ? current2 : null;
14430
+ return import_ts_morph58.Node.isIdentifier(current2) ? current2 : null;
13550
14431
  }
13551
14432
 
13552
14433
  // src/packHealth.ts
@@ -13662,7 +14543,8 @@ function formatPackHealth(checks, audiences) {
13662
14543
  }
13663
14544
 
13664
14545
  // src/resolve/readName.ts
13665
- var import_ts_morph58 = require("ts-morph");
14546
+ var import_ts_morph59 = require("ts-morph");
14547
+ var import_behavioral_ir7 = require("@suss/behavioral-ir");
13666
14548
  var MAX_HELPER_HOPS = 2;
13667
14549
  var UNNAMED_HOLE = "param";
13668
14550
  function readName(expr, options = {}) {
@@ -13676,6 +14558,14 @@ function readName(expr, options = {}) {
13676
14558
  if (name !== null || options.unsettled !== "reference") {
13677
14559
  return name;
13678
14560
  }
14561
+ const reference = referenceIn(expr);
14562
+ return reference === null ? null : (0, import_behavioral_ir7.referenceName)(reference);
14563
+ }
14564
+ function referenceIn(expr) {
14565
+ const fromParameter = parameterPath(expr);
14566
+ if (fromParameter !== null) {
14567
+ return fromParameter;
14568
+ }
13679
14569
  const asked = holeName(expr, {
13680
14570
  resolve: () => null,
13681
14571
  bindings: /* @__PURE__ */ new Map(),
@@ -13683,7 +14573,25 @@ function readName(expr, options = {}) {
13683
14573
  seen: /* @__PURE__ */ new Set(),
13684
14574
  insideHole: true
13685
14575
  });
13686
- return asked === UNNAMED_HOLE ? null : `{${asked}}`;
14576
+ return asked === UNNAMED_HOLE ? null : { root: asked, fields: [] };
14577
+ }
14578
+ function parameterPath(expr) {
14579
+ const fields = [];
14580
+ let step = expr;
14581
+ while (import_ts_morph59.Node.isPropertyAccessExpression(step)) {
14582
+ fields.unshift(step.getName());
14583
+ step = step.getExpression();
14584
+ }
14585
+ if (!import_ts_morph59.Node.isIdentifier(step) || !isParameter(step)) {
14586
+ return null;
14587
+ }
14588
+ return { root: step.getText(), fields };
14589
+ }
14590
+ function isParameter(identifier) {
14591
+ const declarations = identifier.getSymbol()?.getDeclarations() ?? [];
14592
+ return declarations.some(
14593
+ (declaration) => import_ts_morph59.Node.isParameterDeclaration(declaration)
14594
+ );
13687
14595
  }
13688
14596
  function read(expr, ctx) {
13689
14597
  if (ctx.seen.has(expr)) {
@@ -13691,25 +14599,25 @@ function read(expr, ctx) {
13691
14599
  }
13692
14600
  const seen = new Set(ctx.seen).add(expr);
13693
14601
  const inner = { ...ctx, seen };
13694
- if (import_ts_morph58.Node.isStringLiteral(expr) || import_ts_morph58.Node.isNoSubstitutionTemplateLiteral(expr)) {
14602
+ if (import_ts_morph59.Node.isStringLiteral(expr) || import_ts_morph59.Node.isNoSubstitutionTemplateLiteral(expr)) {
13695
14603
  return expr.getLiteralValue();
13696
14604
  }
13697
- if (import_ts_morph58.Node.isNumericLiteral(expr)) {
14605
+ if (import_ts_morph59.Node.isNumericLiteral(expr)) {
13698
14606
  return expr.getLiteralText();
13699
14607
  }
13700
- if (import_ts_morph58.Node.isTemplateExpression(expr)) {
14608
+ if (import_ts_morph59.Node.isTemplateExpression(expr)) {
13701
14609
  return fromTemplate(expr, inner);
13702
14610
  }
13703
- if (import_ts_morph58.Node.isParenthesizedExpression(expr)) {
14611
+ if (import_ts_morph59.Node.isParenthesizedExpression(expr)) {
13704
14612
  return read(expr.getExpression(), inner);
13705
14613
  }
13706
- if (import_ts_morph58.Node.isBinaryExpression(expr)) {
14614
+ if (import_ts_morph59.Node.isBinaryExpression(expr)) {
13707
14615
  return fromBinary(expr, inner);
13708
14616
  }
13709
- if (import_ts_morph58.Node.isCallExpression(expr)) {
14617
+ if (import_ts_morph59.Node.isCallExpression(expr)) {
13710
14618
  return fromCall(expr, inner);
13711
14619
  }
13712
- if (import_ts_morph58.Node.isIdentifier(expr)) {
14620
+ if (import_ts_morph59.Node.isIdentifier(expr)) {
13713
14621
  const only = boundOnce(expr, ctx);
13714
14622
  if (only !== null) {
13715
14623
  return read(only.node, { ...inner, bindings: only.scope });
@@ -13719,7 +14627,7 @@ function read(expr, ctx) {
13719
14627
  return written === null || written === expr ? null : read(written, inner);
13720
14628
  }
13721
14629
  function fromTemplate(expr, ctx) {
13722
- if (!import_ts_morph58.Node.isTemplateExpression(expr)) {
14630
+ if (!import_ts_morph59.Node.isTemplateExpression(expr)) {
13723
14631
  return null;
13724
14632
  }
13725
14633
  let name = expr.getHead().getLiteralText();
@@ -13730,28 +14638,28 @@ function fromTemplate(expr, ctx) {
13730
14638
  return name;
13731
14639
  }
13732
14640
  function fromBinary(expr, ctx) {
13733
- if (!import_ts_morph58.Node.isBinaryExpression(expr)) {
14641
+ if (!import_ts_morph59.Node.isBinaryExpression(expr)) {
13734
14642
  return null;
13735
14643
  }
13736
14644
  const operator = expr.getOperatorToken().getKind();
13737
- if (operator === import_ts_morph58.SyntaxKind.BarBarToken || operator === import_ts_morph58.SyntaxKind.QuestionQuestionToken) {
14645
+ if (operator === import_ts_morph59.SyntaxKind.BarBarToken || operator === import_ts_morph59.SyntaxKind.QuestionQuestionToken) {
13738
14646
  return ctx.insideHole ? null : read(expr.getRight(), ctx);
13739
14647
  }
13740
- if (operator !== import_ts_morph58.SyntaxKind.PlusToken) {
14648
+ if (operator !== import_ts_morph59.SyntaxKind.PlusToken) {
13741
14649
  return null;
13742
14650
  }
13743
14651
  const parts = { ...ctx, insideHole: true };
13744
14652
  return readOrHole(expr.getLeft(), parts) + readOrHole(expr.getRight(), parts);
13745
14653
  }
13746
14654
  function fromCall(expr, ctx) {
13747
- if (!import_ts_morph58.Node.isCallExpression(expr)) {
14655
+ if (!import_ts_morph59.Node.isCallExpression(expr)) {
13748
14656
  return null;
13749
14657
  }
13750
14658
  const callee = expr.getExpression();
13751
- if (import_ts_morph58.Node.isPropertyAccessExpression(callee) && callee.getName() === "join") {
14659
+ if (import_ts_morph59.Node.isPropertyAccessExpression(callee) && callee.getName() === "join") {
13752
14660
  return fromJoin(callee.getExpression(), expr.getArguments(), ctx);
13753
14661
  }
13754
- if (import_ts_morph58.Node.isIdentifier(callee) && callee.getText() === "String") {
14662
+ if (import_ts_morph59.Node.isIdentifier(callee) && callee.getText() === "String") {
13755
14663
  const only = expr.getArguments()[0];
13756
14664
  return only === void 0 ? null : readOrHole(only, ctx);
13757
14665
  }
@@ -13770,7 +14678,7 @@ function fromCall(expr, ctx) {
13770
14678
  }
13771
14679
  function fromJoin(subject, args, ctx) {
13772
14680
  const separator = args[0];
13773
- if (separator === void 0 || !import_ts_morph58.Node.isStringLiteral(separator)) {
14681
+ if (separator === void 0 || !import_ts_morph59.Node.isStringLiteral(separator)) {
13774
14682
  return null;
13775
14683
  }
13776
14684
  const parts = elementsOf(subject, ctx);
@@ -13786,17 +14694,17 @@ function fromJoin(subject, args, ctx) {
13786
14694
  ).join(separator.getLiteralValue());
13787
14695
  }
13788
14696
  function elementsOf(subject, ctx) {
13789
- if (import_ts_morph58.Node.isArrayLiteralExpression(subject)) {
14697
+ if (import_ts_morph59.Node.isArrayLiteralExpression(subject)) {
13790
14698
  return subject.getElements().map((element) => here(element, ctx));
13791
14699
  }
13792
- if (import_ts_morph58.Node.isCallExpression(subject)) {
14700
+ if (import_ts_morph59.Node.isCallExpression(subject)) {
13793
14701
  const callee = subject.getExpression();
13794
- if (import_ts_morph58.Node.isPropertyAccessExpression(callee) && callee.getName() === "map") {
14702
+ if (import_ts_morph59.Node.isPropertyAccessExpression(callee) && callee.getName() === "map") {
13795
14703
  return elementsOf(callee.getExpression(), ctx);
13796
14704
  }
13797
14705
  return null;
13798
14706
  }
13799
- if (!import_ts_morph58.Node.isIdentifier(subject)) {
14707
+ if (!import_ts_morph59.Node.isIdentifier(subject)) {
13800
14708
  return null;
13801
14709
  }
13802
14710
  const bound = ctx.bindings.get(subject.getText());
@@ -13804,7 +14712,7 @@ function elementsOf(subject, ctx) {
13804
14712
  return null;
13805
14713
  }
13806
14714
  const only = bound.length === 1 ? bound[0] : void 0;
13807
- return only !== void 0 && import_ts_morph58.Node.isArrayLiteralExpression(only.node) ? only.node.getElements().map((element) => here(element, ctx)) : bound;
14715
+ return only !== void 0 && import_ts_morph59.Node.isArrayLiteralExpression(only.node) ? only.node.getElements().map((element) => here(element, ctx)) : bound;
13808
14716
  }
13809
14717
  function singleReturnBody(callee, ctx) {
13810
14718
  const declaration = declarationOf2(callee, ctx);
@@ -13819,22 +14727,22 @@ function singleReturnBody(callee, ctx) {
13819
14727
  if (body === void 0) {
13820
14728
  return null;
13821
14729
  }
13822
- if (import_ts_morph58.Node.isExpression(body)) {
14730
+ if (import_ts_morph59.Node.isExpression(body)) {
13823
14731
  return { parameters, returned: body };
13824
14732
  }
13825
- if (!import_ts_morph58.Node.isBlock(body)) {
14733
+ if (!import_ts_morph59.Node.isBlock(body)) {
13826
14734
  return null;
13827
14735
  }
13828
14736
  const statements = body.getStatements();
13829
14737
  const only = statements[0];
13830
- if (statements.length !== 1 || only === void 0 || !import_ts_morph58.Node.isReturnStatement(only)) {
14738
+ if (statements.length !== 1 || only === void 0 || !import_ts_morph59.Node.isReturnStatement(only)) {
13831
14739
  return null;
13832
14740
  }
13833
14741
  const returned = only.getExpression();
13834
14742
  return returned === void 0 ? null : { parameters, returned };
13835
14743
  }
13836
14744
  function declarationOf2(callee, ctx) {
13837
- const symbol = import_ts_morph58.Node.isPropertyAccessExpression(callee) ? callee.getNameNode().getSymbol() : callee.getSymbol();
14745
+ const symbol = import_ts_morph59.Node.isPropertyAccessExpression(callee) ? callee.getNameNode().getSymbol() : callee.getSymbol();
13838
14746
  const declarations = symbol?.getAliasedSymbol()?.getDeclarations() ?? symbol?.getDeclarations() ?? [];
13839
14747
  for (const declaration of declarations) {
13840
14748
  const callable = asCallable(declaration);
@@ -13846,14 +14754,14 @@ function declarationOf2(callee, ctx) {
13846
14754
  return written === null || written === callee ? null : declarationOf2(written, { ...ctx, resolve: () => null });
13847
14755
  }
13848
14756
  function asCallable(declaration) {
13849
- if (import_ts_morph58.Node.isFunctionDeclaration(declaration) || import_ts_morph58.Node.isMethodDeclaration(declaration) || import_ts_morph58.Node.isArrowFunction(declaration) || import_ts_morph58.Node.isFunctionExpression(declaration)) {
14757
+ if (import_ts_morph59.Node.isFunctionDeclaration(declaration) || import_ts_morph59.Node.isMethodDeclaration(declaration) || import_ts_morph59.Node.isArrowFunction(declaration) || import_ts_morph59.Node.isFunctionExpression(declaration)) {
13850
14758
  return declaration;
13851
14759
  }
13852
- if (!import_ts_morph58.Node.isVariableDeclaration(declaration)) {
14760
+ if (!import_ts_morph59.Node.isVariableDeclaration(declaration)) {
13853
14761
  return null;
13854
14762
  }
13855
14763
  const initializer = declaration.getInitializer();
13856
- if (initializer !== void 0 && (import_ts_morph58.Node.isArrowFunction(initializer) || import_ts_morph58.Node.isFunctionExpression(initializer))) {
14764
+ if (initializer !== void 0 && (import_ts_morph59.Node.isArrowFunction(initializer) || import_ts_morph59.Node.isFunctionExpression(initializer))) {
13857
14765
  return initializer;
13858
14766
  }
13859
14767
  return null;
@@ -13875,22 +14783,22 @@ function here(node, ctx) {
13875
14783
  return { node, scope: ctx.bindings };
13876
14784
  }
13877
14785
  function boundOnce(expr, ctx) {
13878
- const bound = import_ts_morph58.Node.isIdentifier(expr) ? ctx.bindings.get(expr.getText()) : void 0;
14786
+ const bound = import_ts_morph59.Node.isIdentifier(expr) ? ctx.bindings.get(expr.getText()) : void 0;
13879
14787
  return bound !== void 0 && bound.length === 1 ? bound[0] : null;
13880
14788
  }
13881
14789
  function readOrHole(expr, ctx) {
13882
14790
  const name = read(expr, ctx);
13883
- return name ?? `{${holeName(expr, ctx)}}`;
14791
+ return name ?? (0, import_behavioral_ir7.patternHole)(holeName(expr, ctx));
13884
14792
  }
13885
14793
  function holeName(expr, ctx) {
13886
14794
  const bound = boundOnce(expr, ctx);
13887
14795
  if (bound !== null) {
13888
14796
  return holeName(bound.node, { ...ctx, bindings: bound.scope });
13889
14797
  }
13890
- if (import_ts_morph58.Node.isIdentifier(expr)) {
14798
+ if (import_ts_morph59.Node.isIdentifier(expr)) {
13891
14799
  return expr.getText();
13892
14800
  }
13893
- if (import_ts_morph58.Node.isPropertyAccessExpression(expr)) {
14801
+ if (import_ts_morph59.Node.isPropertyAccessExpression(expr)) {
13894
14802
  return expr.getName();
13895
14803
  }
13896
14804
  return UNNAMED_HOLE;