@suss/adapter-typescript 0.35.0 → 0.36.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
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  GENERIC_TERMINALS: () => GENERIC_TERMINALS,
35
35
  NO_BARRIERS: () => NO_BARRIERS,
36
36
  ResolutionStore: () => ResolutionStore,
37
+ SKIP_DIRECTORIES: () => SKIP_DIRECTORIES,
37
38
  SOURCE_SUFFIXES: () => SOURCE_SUFFIXES,
38
39
  TSCONFIG_NAMES: () => TSCONFIG_NAMES,
39
40
  TypeScriptWhySession: () => TypeScriptWhySession,
@@ -53,6 +54,7 @@ __export(index_exports, {
53
54
  createTypeScriptAdapter: () => createTypeScriptAdapter,
54
55
  declarationsBehind: () => declarationsBehind,
55
56
  discoverUnits: () => discoverUnits,
57
+ dottedPathOf: () => dottedPathOf,
56
58
  exportedDeclarationsOf: () => exportedDeclarationsOf,
57
59
  extractCodeStructure: () => extractCodeStructure,
58
60
  extractRawBranches: () => extractRawBranches,
@@ -109,11 +111,11 @@ module.exports = __toCommonJS(index_exports);
109
111
 
110
112
  // src/adapter.ts
111
113
  var import_node_path15 = __toESM(require("path"), 1);
112
- var import_ts_morph85 = require("ts-morph");
114
+ var import_ts_morph87 = require("ts-morph");
113
115
  var import_behavioral_ir16 = require("@suss/behavioral-ir");
114
116
  var import_datalog5 = require("@suss/datalog");
115
117
  var import_extractor20 = require("@suss/extractor");
116
- var import_values14 = require("@suss/values");
118
+ var import_values15 = require("@suss/values");
117
119
 
118
120
  // src/assembly.ts
119
121
  var import_ts_morph69 = require("ts-morph");
@@ -1149,11 +1151,11 @@ function settledDepthsFor(project) {
1149
1151
  }
1150
1152
  var depthsByProject = /* @__PURE__ */ new WeakMap();
1151
1153
  function readSpecifierPaths(project, filePath, options, host) {
1152
- const text2 = readFileText(project, filePath);
1153
- if (text2 === null) {
1154
+ const text3 = readFileText(project, filePath);
1155
+ if (text3 === null) {
1154
1156
  return [];
1155
1157
  }
1156
- const scanned = import_ts_morph4.ts.preProcessFile(text2, true, true);
1158
+ const scanned = import_ts_morph4.ts.preProcessFile(text3, true, true);
1157
1159
  const cache = resolutionCacheFor(project, options);
1158
1160
  const paths = [];
1159
1161
  for (const imported of scanned.importedFiles) {
@@ -1325,12 +1327,12 @@ async function readImportsConcurrently(paths, marks) {
1325
1327
  }
1326
1328
  const p = paths[i];
1327
1329
  try {
1328
- const text2 = await import_promises.default.readFile(p, "utf-8");
1329
- const pre = import_ts_morph4.ts.preProcessFile(text2, true, false);
1330
+ const text3 = await import_promises.default.readFile(p, "utf-8");
1331
+ const pre = import_ts_morph4.ts.preProcessFile(text3, true, false);
1330
1332
  results[i] = {
1331
1333
  path: p,
1332
1334
  importedModules: pre.importedFiles.map((f) => f.fileName),
1333
- marked: marks.some((mark) => text2.includes(mark))
1335
+ marked: marks.some((mark) => text3.includes(mark))
1334
1336
  };
1335
1337
  } catch {
1336
1338
  results[i] = { path: p, importedModules: [], marked: false };
@@ -2323,8 +2325,8 @@ function spellingsHandedOn(paths, sourceFile) {
2323
2325
  if (paths.length === 0) {
2324
2326
  return [];
2325
2327
  }
2326
- const text2 = sourceFile.getFullText();
2327
- if (!paths.some((path17) => text2.includes(path17))) {
2328
+ const text3 = sourceFile.getFullText();
2329
+ if (!paths.some((path17) => text3.includes(path17))) {
2328
2330
  return [];
2329
2331
  }
2330
2332
  return sourceFile.getDescendantsOfKind(import_ts_morph8.SyntaxKind.PropertyAccessExpression).filter((node) => spellsOneOf(paths, node) && flowsOnward(node));
@@ -2343,6 +2345,10 @@ function dottedPathOf(expression) {
2343
2345
  if (import_ts_morph8.Node.isIdentifier(expression)) {
2344
2346
  return expression.getText();
2345
2347
  }
2348
+ if (import_ts_morph8.Node.isMetaProperty(expression)) {
2349
+ const keyword = import_ts_morph8.ts.tokenToString(expression.getKeywordToken());
2350
+ return `${keyword}.${expression.getName()}`;
2351
+ }
2346
2352
  if (!import_ts_morph8.Node.isPropertyAccessExpression(expression)) {
2347
2353
  return null;
2348
2354
  }
@@ -4375,8 +4381,8 @@ var ResolutionStore = class _ResolutionStore {
4375
4381
  if (one.isInNodeModules()) {
4376
4382
  return false;
4377
4383
  }
4378
- const text2 = one.getFullText();
4379
- return spellings.some((spelling) => text2.includes(spelling));
4384
+ const text3 = one.getFullText();
4385
+ return spellings.some((spelling) => text3.includes(spelling));
4380
4386
  });
4381
4387
  if (siteFiles.length === 0) {
4382
4388
  return [];
@@ -5771,10 +5777,13 @@ function parametersOf(parameter, position) {
5771
5777
  }
5772
5778
  ];
5773
5779
  }
5774
- if (!import_ts_morph13.Node.isObjectBindingPattern(nameNode) || parameter.getInitializer() !== void 0) {
5780
+ if (!import_ts_morph13.Node.isObjectBindingPattern(nameNode)) {
5775
5781
  return [];
5776
5782
  }
5777
- return destructuredNames(nameNode, position, []);
5783
+ const patternDefault = parameter.getInitializer();
5784
+ return destructuredNames(nameNode, position, []).map(
5785
+ (entry) => patternDefault === void 0 ? entry : { ...entry, patternDefault }
5786
+ );
5778
5787
  }
5779
5788
  function destructuredNames(pattern, position, prefix) {
5780
5789
  return pattern.getElements().flatMap((element) => {
@@ -6063,6 +6072,18 @@ function evaluatedValue(node, resolution, site) {
6063
6072
  function evaluatedValueUnder(node, resolution, bindings) {
6064
6073
  return bindings.size === 0 ? evaluatedValue(node, resolution) : (0, import_values3.force)(evaluatorFor(node, resolution).evaluate(node, { bindings }));
6065
6074
  }
6075
+ function evaluatedValueThrough(node, calls, resolution, given) {
6076
+ const evaluator = evaluatorFor(node, resolution);
6077
+ let bindings = /* @__PURE__ */ new Map();
6078
+ for (const call of [...calls].reverse()) {
6079
+ const bound = evaluator.argumentsAt(call, { bindings, given });
6080
+ if (bound === null) {
6081
+ return null;
6082
+ }
6083
+ bindings = bound;
6084
+ }
6085
+ return evaluator.evaluate(node, { bindings, given });
6086
+ }
6066
6087
 
6067
6088
  // src/resolve/routePath.ts
6068
6089
  function pathFromArgument(arg, resolution, site) {
@@ -7478,8 +7499,8 @@ function declaredChannelOf(source, decorator, resolution) {
7478
7499
  if (arg === void 0) {
7479
7500
  return null;
7480
7501
  }
7481
- const text2 = stringValueOf(arg, resolution);
7482
- return text2 === null || text2 === "" ? null : text2;
7502
+ const text3 = stringValueOf(arg, resolution);
7503
+ return text3 === null || text3 === "" ? null : text3;
7483
7504
  }
7484
7505
  function statusCodeDecoratedOn(routeDecorator, statusDecorators, constants, resolution) {
7485
7506
  const member = routeDecorator.getParent();
@@ -7929,28 +7950,28 @@ function assembledTemplateText(template, assembly) {
7929
7950
  if (!import_ts_morph25.Node.isTemplateExpression(template)) {
7930
7951
  return null;
7931
7952
  }
7932
- let text2 = template.getHead().getLiteralText();
7953
+ let text3 = template.getHead().getLiteralText();
7933
7954
  for (const span of template.getTemplateSpans()) {
7934
7955
  const spliced = interpolatedDocumentText(span.getExpression(), assembly);
7935
7956
  if (spliced === null) {
7936
7957
  assembly.unresolvedInterpolations.push(
7937
7958
  singleLine(span.getExpression().getText())
7938
7959
  );
7939
- if (openBraceDepth(text2) > 0) {
7960
+ if (openBraceDepth(text3) > 0) {
7940
7961
  assembly.unresolvedInsideSelection = true;
7941
7962
  }
7942
7963
  } else {
7943
- text2 += `
7964
+ text3 += `
7944
7965
  ${spliced}
7945
7966
  `;
7946
7967
  }
7947
- text2 += span.getLiteral().getLiteralText();
7968
+ text3 += span.getLiteral().getLiteralText();
7948
7969
  }
7949
- return text2;
7970
+ return text3;
7950
7971
  }
7951
- function openBraceDepth(text2) {
7972
+ function openBraceDepth(text3) {
7952
7973
  let depth = 0;
7953
- for (const char of text2) {
7974
+ for (const char of text3) {
7954
7975
  if (char === "{") {
7955
7976
  depth += 1;
7956
7977
  } else if (char === "}") {
@@ -7982,16 +8003,16 @@ function interpolatedDocumentText(expr, assembly) {
7982
8003
  function resolveGraphqlDocument(arg, resolution) {
7983
8004
  const assembly = startAssembly(resolution);
7984
8005
  const stripped = peelSyntax(arg);
7985
- const text2 = documentTextFromExpression(stripped, assembly) ?? resolveGqlTemplateText(stripped, assembly) ?? resolveTypedDocumentSource(stripped) ?? resolveThroughFacts(stripped, assembly);
7986
- if (text2 !== null) {
7987
- return assembledDocumentResolution(text2, stripped, assembly);
8006
+ const text3 = documentTextFromExpression(stripped, assembly) ?? resolveGqlTemplateText(stripped, assembly) ?? resolveTypedDocumentSource(stripped) ?? resolveThroughFacts(stripped, assembly);
8007
+ if (text3 !== null) {
8008
+ return assembledDocumentResolution(text3, stripped, assembly);
7988
8009
  }
7989
8010
  return resolveTypedDocumentHeader(stripped);
7990
8011
  }
7991
- function assembledDocumentResolution(text2, arg, assembly) {
8012
+ function assembledDocumentResolution(text3, arg, assembly) {
7992
8013
  const interpolations = assembly.unresolvedInterpolations;
7993
8014
  if (assembly.unresolvedInsideSelection) {
7994
- const header = parseGraphqlOperation(text2);
8015
+ const header = parseGraphqlOperation(text3);
7995
8016
  return {
7996
8017
  ...header !== null ? { operationType: header.operationType } : {},
7997
8018
  ...header?.operationName !== void 0 ? { operationName: header.operationName } : {},
@@ -8001,10 +8022,10 @@ function assembledDocumentResolution(text2, arg, assembly) {
8001
8022
  }
8002
8023
  };
8003
8024
  }
8004
- const spreads = fragmentSpreadsIn(text2);
8025
+ const spreads = fragmentSpreadsIn(text3);
8005
8026
  if (spreads === null) {
8006
8027
  if (interpolations.length === 0) {
8007
- return { document: text2 };
8028
+ return { document: text3 };
8008
8029
  }
8009
8030
  return {
8010
8031
  unresolved: {
@@ -8013,7 +8034,7 @@ function assembledDocumentResolution(text2, arg, assembly) {
8013
8034
  }
8014
8035
  };
8015
8036
  }
8016
- const filled = fillDanglingSpreads(text2, spreads, arg, assembly);
8037
+ const filled = fillDanglingSpreads(text3, spreads, arg, assembly);
8017
8038
  if (filled.unresolved.length > 0) {
8018
8039
  return {
8019
8040
  document: filled.text,
@@ -8032,15 +8053,15 @@ function assembledDocumentResolution(text2, arg, assembly) {
8032
8053
  }
8033
8054
  return { document: filled.text };
8034
8055
  }
8035
- function fillDanglingSpreads(text2, spreads, arg, assembly) {
8056
+ function fillDanglingSpreads(text3, spreads, arg, assembly) {
8036
8057
  if (spreads.dangling.length === 0 || assembly.tagOrigin !== "project") {
8037
- return { text: text2, unresolved: spreads.dangling, ambiguous: [] };
8058
+ return { text: text3, unresolved: spreads.dangling, ambiguous: [] };
8038
8059
  }
8039
8060
  const definitions = projectFragmentDefinitions(arg.getProject());
8040
8061
  const settled3 = new Set(spreads.defined);
8041
8062
  const unresolved = /* @__PURE__ */ new Set();
8042
8063
  const ambiguous = /* @__PURE__ */ new Set();
8043
- let filled = text2;
8064
+ let filled = text3;
8044
8065
  let pending = spreads.dangling;
8045
8066
  while (pending.length > 0) {
8046
8067
  const brought = [];
@@ -8104,12 +8125,12 @@ function buildFragmentIndex(project) {
8104
8125
  if (!FRAGMENT_DEFINITION_TEXT.test(sourceFile.getFullText())) {
8105
8126
  continue;
8106
8127
  }
8107
- for (const text2 of documentTextsIn(sourceFile)) {
8108
- recordFragmentDefinitions(text2, definitions);
8128
+ for (const text3 of documentTextsIn(sourceFile)) {
8129
+ recordFragmentDefinitions(text3, definitions);
8109
8130
  }
8110
8131
  }
8111
- for (const text2 of graphqlFileTextsNear(sourceFiles)) {
8112
- recordFragmentDefinitions(text2, definitions);
8132
+ for (const text3 of graphqlFileTextsNear(sourceFiles)) {
8133
+ recordFragmentDefinitions(text3, definitions);
8113
8134
  }
8114
8135
  return definitions;
8115
8136
  }
@@ -8146,17 +8167,17 @@ function documentTextsIn(sourceFile) {
8146
8167
  if (!import_ts_morph25.Node.isTaggedTemplateExpression(node) && !import_ts_morph25.Node.isCallExpression(node)) {
8147
8168
  return;
8148
8169
  }
8149
- const text2 = documentTextFromExpression(node, startAssembly(void 0));
8150
- if (text2 !== null && text2 !== "") {
8151
- texts.push(text2);
8170
+ const text3 = documentTextFromExpression(node, startAssembly(void 0));
8171
+ if (text3 !== null && text3 !== "") {
8172
+ texts.push(text3);
8152
8173
  }
8153
8174
  });
8154
8175
  return texts;
8155
8176
  }
8156
- function recordFragmentDefinitions(text2, definitions) {
8177
+ function recordFragmentDefinitions(text3, definitions) {
8157
8178
  let doc;
8158
8179
  try {
8159
- doc = (0, import_graphql.parse)(text2);
8180
+ doc = (0, import_graphql.parse)(text3);
8160
8181
  } catch {
8161
8182
  return;
8162
8183
  }
@@ -8180,13 +8201,13 @@ function recordFragmentDefinitions(text2, definitions) {
8180
8201
  }
8181
8202
  var NO_NAMES = /* @__PURE__ */ new Set();
8182
8203
  function describeInterpolations(interpolations) {
8183
- const quoted = interpolations.map((text2) => `\`${text2}\``).join(", ");
8204
+ const quoted = interpolations.map((text3) => `\`${text3}\``).join(", ");
8184
8205
  return interpolations.length === 1 ? `expression ${quoted}` : `expressions ${quoted}`;
8185
8206
  }
8186
- function fragmentSpreadsIn(text2) {
8207
+ function fragmentSpreadsIn(text3) {
8187
8208
  let doc;
8188
8209
  try {
8189
- doc = (0, import_graphql.parse)(text2);
8210
+ doc = (0, import_graphql.parse)(text3);
8190
8211
  } catch {
8191
8212
  return null;
8192
8213
  }
@@ -8338,9 +8359,9 @@ function resolveThroughFacts(arg, assembly) {
8338
8359
  if (written === null) {
8339
8360
  return null;
8340
8361
  }
8341
- const text2 = documentTextFromExpression(written, assembly);
8342
- if (text2 !== null) {
8343
- return text2;
8362
+ const text3 = documentTextFromExpression(written, assembly);
8363
+ if (text3 !== null) {
8364
+ return text3;
8344
8365
  }
8345
8366
  return typedDocumentSourceOf(written);
8346
8367
  }
@@ -8376,8 +8397,8 @@ function unreadableDocument(arg, reason) {
8376
8397
  }
8377
8398
  };
8378
8399
  }
8379
- function singleLine(text2) {
8380
- const collapsed = text2.replace(/\s+/g, " ").trim();
8400
+ function singleLine(text3) {
8401
+ const collapsed = text3.replace(/\s+/g, " ").trim();
8381
8402
  return collapsed.length > 80 ? `${collapsed.slice(0, 79)}\u2026` : collapsed;
8382
8403
  }
8383
8404
  function importedVariableInitializers(identifier) {
@@ -8421,9 +8442,9 @@ function resolveGqlTemplateText(arg, assembly) {
8421
8442
  }
8422
8443
  }
8423
8444
  for (const init of importedVariableInitializers(arg)) {
8424
- const text2 = documentTextFromExpression(peelSyntax(init), assembly);
8425
- if (text2 !== null) {
8426
- return text2;
8445
+ const text3 = documentTextFromExpression(peelSyntax(init), assembly);
8446
+ if (text3 !== null) {
8447
+ return text3;
8427
8448
  }
8428
8449
  }
8429
8450
  return null;
@@ -9026,7 +9047,8 @@ function expandDocumentParameterCallers(documentArgument, libraryCall, context)
9026
9047
  context.store.returnsCall(wrapper, libraryCall),
9027
9048
  0,
9028
9049
  /* @__PURE__ */ new Set([parameter]),
9029
- context
9050
+ context,
9051
+ { library: libraryCall, through: [] }
9030
9052
  );
9031
9053
  return {
9032
9054
  units: expanded.units,
@@ -9039,7 +9061,7 @@ function documentBehindWrapper(value, store, wrapperReason) {
9039
9061
  }
9040
9062
  return resolveGraphqlDocument(value, store) ?? unreadableDocument(value);
9041
9063
  }
9042
- function expandParameter(parameter, forwardsResult, depth, expanded, context) {
9064
+ function expandParameter(parameter, forwardsResult, depth, expanded, context, chain) {
9043
9065
  const passed = context.store.argumentsPassedTo(parameter);
9044
9066
  if (passed.length === 0) {
9045
9067
  return { units: [], unfollowed: 1 };
@@ -9052,6 +9074,7 @@ function expandParameter(parameter, forwardsResult, depth, expanded, context) {
9052
9074
  unfollowed++;
9053
9075
  continue;
9054
9076
  }
9077
+ const outward = { ...chain, through: [...chain.through, call] };
9055
9078
  const nested = parameterBehind(argument, caller);
9056
9079
  if (nested !== null) {
9057
9080
  const deeper = expandNested(
@@ -9059,13 +9082,21 @@ function expandParameter(parameter, forwardsResult, depth, expanded, context) {
9059
9082
  forwardsResult && context.store.returnsCall(caller, call),
9060
9083
  depth,
9061
9084
  expanded,
9062
- context
9085
+ context,
9086
+ outward
9063
9087
  );
9064
9088
  units.push(...deeper.units);
9065
9089
  unfollowed += deeper.unfollowed;
9066
9090
  continue;
9067
9091
  }
9068
- const unit = callerUnit(caller, call, argument, forwardsResult, context);
9092
+ const unit = callerUnit(
9093
+ caller,
9094
+ call,
9095
+ argument,
9096
+ forwardsResult,
9097
+ context,
9098
+ outward
9099
+ );
9069
9100
  if (unit === null) {
9070
9101
  unfollowed++;
9071
9102
  continue;
@@ -9074,7 +9105,7 @@ function expandParameter(parameter, forwardsResult, depth, expanded, context) {
9074
9105
  }
9075
9106
  return { units, unfollowed };
9076
9107
  }
9077
- function expandNested(parameter, forwardsResult, depth, expanded, context) {
9108
+ function expandNested(parameter, forwardsResult, depth, expanded, context, chain) {
9078
9109
  if (depth + 1 >= MAX_WRAPPER_DEPTH || expanded.has(parameter)) {
9079
9110
  return { units: [], unfollowed: 1 };
9080
9111
  }
@@ -9083,10 +9114,11 @@ function expandNested(parameter, forwardsResult, depth, expanded, context) {
9083
9114
  forwardsResult,
9084
9115
  depth + 1,
9085
9116
  /* @__PURE__ */ new Set([...expanded, parameter]),
9086
- context
9117
+ context,
9118
+ chain
9087
9119
  );
9088
9120
  }
9089
- function callerUnit(caller, call, argument, forwardsResult, context) {
9121
+ function callerUnit(caller, call, argument, forwardsResult, context, chain) {
9090
9122
  const document = resolveGraphqlDocument(argument, context.store) ?? unreadableDocument(argument);
9091
9123
  const operationInfo = operationInfoFromResolution(
9092
9124
  document,
@@ -9103,7 +9135,14 @@ function callerUnit(caller, call, argument, forwardsResult, context) {
9103
9135
  // A wrapper handing back something of its own leaves the caller
9104
9136
  // none of the library's result to read.
9105
9137
  ...forwardsResult ? { callSite: { callExpression: call, methodName: context.methodName } } : {},
9106
- operationInfo
9138
+ operationInfo,
9139
+ ...context.client === void 0 ? {} : {
9140
+ graphqlCall: {
9141
+ call: chain.library,
9142
+ through: chain.through,
9143
+ client: context.client
9144
+ }
9145
+ }
9107
9146
  };
9108
9147
  }
9109
9148
  function unfollowedReason(followed, unfollowed) {
@@ -9205,7 +9244,8 @@ function discoverGraphqlHookCalls(sourceFile, match, kind, resolution) {
9205
9244
  kind,
9206
9245
  operationType: spec.operationType,
9207
9246
  methodName: spec.canonical,
9208
- store
9247
+ store,
9248
+ ...match.clientOption === void 0 ? {} : { client: match.clientOption }
9209
9249
  });
9210
9250
  if (expansion !== null) {
9211
9251
  results.push(...expansion.units);
@@ -9235,7 +9275,14 @@ function discoverGraphqlHookCalls(sourceFile, match, kind, resolution) {
9235
9275
  kind,
9236
9276
  name: `${functionNameOrAnon(enclosing)}.${nameToken}`,
9237
9277
  callSite: { callExpression: node, methodName: spec.canonical },
9238
- operationInfo
9278
+ operationInfo,
9279
+ ...match.clientOption === void 0 ? {} : {
9280
+ graphqlCall: {
9281
+ call: node,
9282
+ through: [],
9283
+ client: match.clientOption
9284
+ }
9285
+ }
9239
9286
  });
9240
9287
  }
9241
9288
  return results;
@@ -9286,7 +9333,8 @@ function discoverGraphqlImperativeCalls(sourceFile, match, kind, resolution) {
9286
9333
  kind,
9287
9334
  operationType: spec.operationType,
9288
9335
  methodName,
9289
- store: resolution
9336
+ store: resolution,
9337
+ client: "receiver"
9290
9338
  });
9291
9339
  if (expansion !== null) {
9292
9340
  results.push(...expansion.units);
@@ -9316,7 +9364,8 @@ function discoverGraphqlImperativeCalls(sourceFile, match, kind, resolution) {
9316
9364
  kind,
9317
9365
  name: `${functionNameOrAnon(enclosing)}.${nameToken}`,
9318
9366
  callSite: { callExpression: node, methodName },
9319
- operationInfo
9367
+ operationInfo,
9368
+ graphqlCall: { call: node, through: [], client: "receiver" }
9320
9369
  });
9321
9370
  });
9322
9371
  return results;
@@ -10327,6 +10376,7 @@ function discoverRegistrationCalls(sourceFile, match, kind, bindingExtraction, r
10327
10376
  if (registrationSubjects.size === 0 && !storeCanFindSubjects(sourceFile, match, resolution, registrationMethods)) {
10328
10377
  return results;
10329
10378
  }
10379
+ const anonymousLabels = /* @__PURE__ */ new Map();
10330
10380
  sourceFile.forEachDescendant((node) => {
10331
10381
  if (!import_ts_morph37.Node.isCallExpression(node)) {
10332
10382
  return;
@@ -10428,7 +10478,8 @@ function discoverRegistrationCalls(sourceFile, match, kind, bindingExtraction, r
10428
10478
  routeWrapperPattern,
10429
10479
  methodName,
10430
10480
  routeInfo,
10431
- resolution
10481
+ resolution,
10482
+ anonymousLabels
10432
10483
  );
10433
10484
  results.push({
10434
10485
  func: handler,
@@ -10489,9 +10540,8 @@ function declinedRegistration(call, callee, methodName, kind, receiver, resoluti
10489
10540
  }
10490
10541
  ];
10491
10542
  }
10492
- function routeChainOf(listed, pattern, methodName, routeInfo, resolution) {
10543
+ function routeChainOf(listed, pattern, methodName, routeInfo, resolution, anonymousLabels) {
10493
10544
  const chain = { units: [], references: [], unfollowed: [] };
10494
- const anonymousLabels = /* @__PURE__ */ new Map();
10495
10545
  for (const arg of listed) {
10496
10546
  const elements = import_ts_morph37.Node.isArrayLiteralExpression(arg) ? arg.getElements() : [arg];
10497
10547
  for (const element of elements) {
@@ -10505,12 +10555,12 @@ function routeChainOf(listed, pattern, methodName, routeInfo, resolution) {
10505
10555
  }
10506
10556
  const ownName = functionNameOrAnon(target);
10507
10557
  const named2 = ownName !== "<anon>";
10508
- const label = factoryNameOf(element) ?? methodName;
10558
+ const label = `${factoryNameOf(element) ?? methodName}@${routeWhere(routeInfo)}`;
10509
10559
  const seen = anonymousLabels.get(label) ?? 0;
10510
10560
  if (!named2) {
10511
10561
  anonymousLabels.set(label, seen + 1);
10512
10562
  }
10513
- const name = named2 ? ownName : routeWrapperLabel(label, routeInfo, element, seen);
10563
+ const name = named2 ? ownName : numberedLabel(label, seen);
10514
10564
  chain.units.push({
10515
10565
  func: target,
10516
10566
  kind: pattern.kind,
@@ -10526,9 +10576,11 @@ function routeChainOf(listed, pattern, methodName, routeInfo, resolution) {
10526
10576
  }
10527
10577
  return chain;
10528
10578
  }
10529
- function routeWrapperLabel(label, routeInfo, element, seen) {
10530
- const where = routeInfo === null ? `L${element.getStartLineNumber()}` : `${routeInfo.method} ${routeInfo.path}`;
10531
- return seen === 0 ? `${label}@${where}` : `${label}@${where}#${seen + 1}`;
10579
+ function routeWhere(routeInfo) {
10580
+ return routeInfo === null ? "?" : `${routeInfo.method} ${routeInfo.path}`;
10581
+ }
10582
+ function numberedLabel(label, seen) {
10583
+ return seen === 0 ? label : `${label}#${seen + 1}`;
10532
10584
  }
10533
10585
  function withMountPrefix(routeInfo, subjectNode, mountPrefixes) {
10534
10586
  if (routeInfo === null || mountPrefixes === void 0) {
@@ -11466,8 +11518,8 @@ function mockOf(call, match, sourceFile, resolution) {
11466
11518
  const module2 = import_ts_morph41.Node.isIdentifier(first) ? moduleImportedWholeAs(sourceFile, first.getText()) : null;
11467
11519
  return { ...module2 !== null ? { module: module2 } : {}, name, written };
11468
11520
  }
11469
- function oneLine(text2) {
11470
- const flat = text2.replace(/\s*\n\s*/g, "");
11521
+ function oneLine(text3) {
11522
+ const flat = text3.replace(/\s*\n\s*/g, "");
11471
11523
  const factory = flat.search(/,\s*(async\s*)?\(/);
11472
11524
  return factory === -1 ? flat : `${flat.slice(0, factory)})`;
11473
11525
  }
@@ -12042,8 +12094,8 @@ function receiverIsPromiseTyped(receiver) {
12042
12094
  if (symbolName === "Promise") {
12043
12095
  return true;
12044
12096
  }
12045
- const text2 = type.getText();
12046
- return text2 === "Promise" || text2.startsWith("Promise<");
12097
+ const text3 = type.getText();
12098
+ return text3 === "Promise" || text3.startsWith("Promise<");
12047
12099
  }
12048
12100
  function callbackReturnExpression(node) {
12049
12101
  if (node === void 0 || !(import_ts_morph44.Node.isArrowFunction(node) || import_ts_morph44.Node.isFunctionExpression(node))) {
@@ -12726,9 +12778,9 @@ function getLoopBody(stmt) {
12726
12778
  return void 0;
12727
12779
  }
12728
12780
  var loopHeaderText = (stmt) => {
12729
- const text2 = stmt.getText();
12730
- const bodyStart = text2.indexOf("{");
12731
- const header = bodyStart === -1 ? text2 : text2.slice(0, bodyStart);
12781
+ const text3 = stmt.getText();
12782
+ const bodyStart = text3.indexOf("{");
12783
+ const header = bodyStart === -1 ? text3 : text3.slice(0, bodyStart);
12732
12784
  return header.trim();
12733
12785
  };
12734
12786
  var conditionOf = (expr) => ({
@@ -12839,7 +12891,8 @@ function buildStructured(stmt, ctx) {
12839
12891
  kind: "loop",
12840
12892
  condition: { sourceText: loopHeaderText(stmt), expression: null },
12841
12893
  body: lowerList(statementsOf(getLoopBody(stmt)), ctx),
12842
- exitKind
12894
+ exitKind,
12895
+ lines: lineRangeOf(stmt)
12843
12896
  };
12844
12897
  }
12845
12898
  if (import_ts_morph48.Node.isTryStatement(stmt)) {
@@ -12847,6 +12900,7 @@ function buildStructured(stmt, ctx) {
12847
12900
  const finallyBlock = stmt.getFinallyBlock();
12848
12901
  return {
12849
12902
  kind: "try",
12903
+ lines: lineRangeOf(stmt),
12850
12904
  tryBody: lowerList(stmt.getTryBlock().getStatements(), ctx),
12851
12905
  catchBody: catchClause === void 0 ? null : lowerList(catchClause.getBlock().getStatements(), ctx),
12852
12906
  finallyBody: finallyBlock === void 0 ? null : lowerList(finallyBlock.getStatements(), ctx),
@@ -13007,13 +13061,14 @@ function loweredExpressionBody(body, terminalNodes, func, barriers) {
13007
13061
  function computePathConditions(func, terminalNodes, barriers = NO_BARRIERS, endings = NO_ENDINGS, keepsArms) {
13008
13062
  const body = func.getBody();
13009
13063
  if (body === void 0) {
13010
- return { byTerminal: /* @__PURE__ */ new Map(), fallthrough: [] };
13064
+ return { byTerminal: /* @__PURE__ */ new Map(), fallthrough: [], placement: /* @__PURE__ */ new Map() };
13011
13065
  }
13012
13066
  if (containsUnmodeledFlow(body, func, barriers)) {
13013
13067
  return degradedResult(func, terminalNodes, "labeled statement");
13014
13068
  }
13069
+ let lowered;
13015
13070
  try {
13016
- const lowered = import_ts_morph49.Node.isBlock(body) ? lowerFunctionBody(body, terminalNodes, func, barriers, endings) : loweredExpressionBody(body, terminalNodes, func, barriers);
13071
+ lowered = import_ts_morph49.Node.isBlock(body) ? lowerFunctionBody(body, terminalNodes, func, barriers, endings) : loweredExpressionBody(body, terminalNodes, func, barriers);
13017
13072
  const result = (0, import_extractor4.enumerateStructuredPaths)({
13018
13073
  statements: lowered.statements,
13019
13074
  terminalsByStmt: lowered.terminalsByStmt,
@@ -13021,15 +13076,17 @@ function computePathConditions(func, terminalNodes, barriers = NO_BARRIERS, endi
13021
13076
  });
13022
13077
  return composeExpressionLevel(func, result, lowered.terminalHomeRaw);
13023
13078
  } catch (error) {
13079
+ const placement = lowered === void 0 ? /* @__PURE__ */ new Map() : (0, import_extractor4.placeTerminals)(lowered);
13024
13080
  if (error instanceof import_extractor4.PathBudgetExceeded) {
13025
13081
  return degradedResult(
13026
13082
  func,
13027
13083
  terminalNodes,
13028
- error.message === "" ? "path budget exceeded" : error.message
13084
+ error.message === "" ? "path budget exceeded" : error.message,
13085
+ placement
13029
13086
  );
13030
13087
  }
13031
13088
  if (error instanceof import_extractor4.UnmodeledFlow) {
13032
- return degradedResult(func, terminalNodes, error.message);
13089
+ return degradedResult(func, terminalNodes, error.message, placement);
13033
13090
  }
13034
13091
  throw error;
13035
13092
  }
@@ -13044,9 +13101,13 @@ function composeExpressionLevel(func, result, terminalHomeRaw) {
13044
13101
  paths.map((statementLevel) => [...statementLevel, ...expressionLevel])
13045
13102
  );
13046
13103
  }
13047
- return { byTerminal, fallthrough: result.fallthrough };
13104
+ return {
13105
+ byTerminal,
13106
+ fallthrough: result.fallthrough,
13107
+ placement: result.placement
13108
+ };
13048
13109
  }
13049
- function degradedResult(func, terminalNodes, reason) {
13110
+ function degradedResult(func, terminalNodes, reason, placement = /* @__PURE__ */ new Map()) {
13050
13111
  const marker = {
13051
13112
  sourceText: `unmodeled control flow (${reason})`,
13052
13113
  polarity: "positive",
@@ -13063,7 +13124,7 @@ function degradedResult(func, terminalNodes, reason) {
13063
13124
  [...collectAncestorConditionInfosBelow(terminal, func), marker]
13064
13125
  ]);
13065
13126
  }
13066
- return { byTerminal, fallthrough: [[marker]] };
13127
+ return { byTerminal, fallthrough: [[marker]], placement };
13067
13128
  }
13068
13129
 
13069
13130
  // src/provenance.ts
@@ -13190,7 +13251,7 @@ function stableTypeText(type, reader, enclosing, flags) {
13190
13251
  return withoutImportQualifiers(printed(ordered, file));
13191
13252
  }
13192
13253
  var IMPORT_QUALIFIER = /import\("[^"]*"\)\./g;
13193
- var withoutImportQualifiers = (text2) => text2.replace(IMPORT_QUALIFIER, "");
13254
+ var withoutImportQualifiers = (text3) => text3.replace(IMPORT_QUALIFIER, "");
13194
13255
  function printed(node, file) {
13195
13256
  return PRINTER.printNode(import_ts_morph51.ts.EmitHint.Unspecified, node, file).replace(
13196
13257
  /\n/g,
@@ -13232,11 +13293,11 @@ function inStableOrder(members, file) {
13232
13293
  function compareMembers(a, b) {
13233
13294
  return nullishRank(a) - nullishRank(b) || compareText(a, b);
13234
13295
  }
13235
- function nullishRank(text2) {
13236
- if (text2 === "null") {
13296
+ function nullishRank(text3) {
13297
+ if (text3 === "null") {
13237
13298
  return 1;
13238
13299
  }
13239
- if (text2 === "undefined") {
13300
+ if (text3 === "undefined") {
13240
13301
  return 2;
13241
13302
  }
13242
13303
  return 0;
@@ -14231,11 +14292,12 @@ function invocationAt(call, func) {
14231
14292
  callee: call.getExpression().getText(),
14232
14293
  args: extractArgs(call),
14233
14294
  async: isAwaited(call),
14234
- ...preconditions.length > 0 ? { preconditions } : {}
14295
+ ...preconditions.length > 0 ? { preconditions } : {},
14296
+ location: lineRangeOf(call)
14235
14297
  },
14236
14298
  node: call,
14237
14299
  line: enclosingStatementLine(call),
14238
- alwaysRuns: runsOnEveryPath(call, func)
14300
+ cleansUpFrom: cleanupStartLine(call, func)
14239
14301
  };
14240
14302
  }
14241
14303
  function isAwaited(call) {
@@ -14246,7 +14308,7 @@ function runInvocationRecognizers(func, recognizers, barriers = NO_BARRIERS, res
14246
14308
  if (recognizers.length === 0) {
14247
14309
  return [];
14248
14310
  }
14249
- const out = [];
14311
+ const sites = [];
14250
14312
  const sourceFile = func.getSourceFile();
14251
14313
  const calls = [];
14252
14314
  func.forEachDescendant((node, traversal) => {
@@ -14271,7 +14333,8 @@ function runInvocationRecognizers(func, recognizers, barriers = NO_BARRIERS, res
14271
14333
  );
14272
14334
  const line = enclosingStatementLine(node);
14273
14335
  const preconditions = collectPreconditions(node, func);
14274
- const alwaysRuns = runsOnEveryPath(node, func);
14336
+ const cleansUpFrom = cleanupStartLine(node, func);
14337
+ const site = [];
14275
14338
  for (const recognizer of recognizers) {
14276
14339
  let emitted = null;
14277
14340
  try {
@@ -14289,17 +14352,18 @@ function runInvocationRecognizers(func, recognizers, barriers = NO_BARRIERS, res
14289
14352
  continue;
14290
14353
  }
14291
14354
  for (const eff of emitted) {
14292
- out.push({
14293
- effect: withPreconditions(eff, preconditions),
14355
+ site.push({
14356
+ effect: placedAt(withPreconditions(eff, preconditions), node),
14294
14357
  line,
14295
14358
  preconditions,
14296
- alwaysRuns,
14359
+ cleansUpFrom,
14297
14360
  ...stated?.of(eff)
14298
14361
  });
14299
14362
  }
14300
14363
  }
14364
+ sites.push(site);
14301
14365
  }
14302
- return out;
14366
+ return (0, import_extractor6.numberRepeatedCallSites)(sites).flat();
14303
14367
  }
14304
14368
  function runAccessRecognizers(func, recognizers, barriers = NO_BARRIERS, resolution, originatesFrom, anchorCallsOf) {
14305
14369
  return dispatchAccessRecognizers(
@@ -14384,7 +14448,7 @@ function dispatchAccessRecognizers(root, recognizers, isStop, resolution, origin
14384
14448
  );
14385
14449
  const line = enclosingStatementLine(node);
14386
14450
  const preconditions = collectPreconditions(node, root);
14387
- const alwaysRuns = runsOnEveryPath(node, root);
14451
+ const cleansUpFrom = cleanupStartLine(node, root);
14388
14452
  for (const recognizer of recognizers) {
14389
14453
  let emitted = null;
14390
14454
  try {
@@ -14409,10 +14473,10 @@ function dispatchAccessRecognizers(root, recognizers, isStop, resolution, origin
14409
14473
  }
14410
14474
  seenEffects.add(key);
14411
14475
  out.push({
14412
- effect,
14476
+ effect: placedAt(effect, node),
14413
14477
  line,
14414
14478
  preconditions,
14415
- alwaysRuns,
14479
+ cleansUpFrom,
14416
14480
  ...stated?.of(eff)
14417
14481
  });
14418
14482
  }
@@ -14446,6 +14510,12 @@ function extractArg(node, depth) {
14446
14510
  if (import_ts_morph56.Node.isFalseLiteral(node)) {
14447
14511
  return { kind: "boolean", value: false };
14448
14512
  }
14513
+ if (import_ts_morph56.Node.isNullLiteral(node)) {
14514
+ return { kind: "null" };
14515
+ }
14516
+ if (import_ts_morph56.Node.isIdentifier(node) && node.getText() === "undefined") {
14517
+ return { kind: "undefined" };
14518
+ }
14449
14519
  if (import_ts_morph56.Node.isTemplateExpression(node)) {
14450
14520
  return { kind: "template", sourceText: node.getText() };
14451
14521
  }
@@ -14537,16 +14607,20 @@ function collectPreconditions(node, root) {
14537
14607
  conditionInfoToRawCondition
14538
14608
  );
14539
14609
  }
14540
- function runsOnEveryPath(node, root) {
14610
+ function cleanupStartLine(node, root) {
14611
+ let found;
14541
14612
  let current2 = node;
14542
14613
  while (current2 !== void 0 && current2 !== root) {
14543
14614
  const parent = current2.getParent();
14544
14615
  if (parent !== void 0 && import_ts_morph56.Node.isTryStatement(parent) && parent.getFinallyBlock() === current2) {
14545
- return true;
14616
+ found = startLineOf(parent);
14546
14617
  }
14547
14618
  current2 = parent;
14548
14619
  }
14549
- return false;
14620
+ return found;
14621
+ }
14622
+ function placedAt(effect, node) {
14623
+ return { ...effect, location: lineRangeOf(node) };
14550
14624
  }
14551
14625
  function withPreconditions(effect, preconditions) {
14552
14626
  if (preconditions.length === 0) {
@@ -14578,6 +14652,9 @@ function inlineModuleBinding(ident, depth) {
14578
14652
  if (!isModuleScoped(decl)) {
14579
14653
  continue;
14580
14654
  }
14655
+ if (decl.getVariableStatement()?.getDeclarationKind() !== import_ts_morph56.VariableDeclarationKind.Const) {
14656
+ continue;
14657
+ }
14581
14658
  const init = decl.getInitializer();
14582
14659
  if (init === void 0) {
14583
14660
  continue;
@@ -15287,8 +15364,8 @@ function typeToShape(type, ctx) {
15287
15364
  return { type: "number" };
15288
15365
  }
15289
15366
  if (type.isBooleanLiteral()) {
15290
- const text3 = type.getText(ctx.enclosing);
15291
- return { type: "literal", value: text3 === "true" };
15367
+ const text4 = type.getText(ctx.enclosing);
15368
+ return { type: "literal", value: text4 === "true" };
15292
15369
  }
15293
15370
  if (type.isString()) {
15294
15371
  return { type: "text" };
@@ -15352,9 +15429,9 @@ function typeToShape(type, ctx) {
15352
15429
  if (type.isEnum() || type.isEnumLiteral()) {
15353
15430
  return enumToShape(type, ctx);
15354
15431
  }
15355
- const text2 = printedTypeName(type, ctx);
15356
- if (text2 && text2.length > 0 && text2 !== "__type") {
15357
- return { type: "ref", name: text2 };
15432
+ const text3 = printedTypeName(type, ctx);
15433
+ if (text3 && text3.length > 0 && text3 !== "__type") {
15434
+ return { type: "ref", name: text3 };
15358
15435
  }
15359
15436
  return null;
15360
15437
  }
@@ -15497,8 +15574,8 @@ function opaqueNamedRef(type, ctx) {
15497
15574
  if (!OPAQUE_NAMED_TYPES.has(name)) {
15498
15575
  return null;
15499
15576
  }
15500
- const text2 = printedTypeName(type, ctx);
15501
- return { type: "ref", name: text2 && text2.length > 0 ? text2 : name };
15577
+ const text3 = printedTypeName(type, ctx);
15578
+ return { type: "ref", name: text3 && text3.length > 0 ? text3 : name };
15502
15579
  }
15503
15580
  function outsideProjectRef(type, ctx) {
15504
15581
  const symbol = type.getSymbol();
@@ -15564,8 +15641,8 @@ function definitionKeyFor(type, name, reader) {
15564
15641
  const written = stableTypeText(type, reader);
15565
15642
  return `${name}@${shortHash(written)}`;
15566
15643
  }
15567
- function shortHash(text2) {
15568
- return (0, import_node_crypto.createHash)("sha1").update(text2).digest("hex").slice(0, 12);
15644
+ function shortHash(text3) {
15645
+ return (0, import_node_crypto.createHash)("sha1").update(text3).digest("hex").slice(0, 12);
15569
15646
  }
15570
15647
  function refFromType(type, ctx) {
15571
15648
  const symbol = type.getAliasSymbol() ?? type.getSymbol();
@@ -16247,11 +16324,11 @@ function collectJsxAttributes(opening) {
16247
16324
  }
16248
16325
  function jsxChildToRenderNode(child) {
16249
16326
  if (import_ts_morph63.Node.isJsxText(child)) {
16250
- const text2 = child.getLiteralText().trim();
16251
- if (text2.length === 0) {
16327
+ const text3 = child.getLiteralText().trim();
16328
+ if (text3.length === 0) {
16252
16329
  return null;
16253
16330
  }
16254
- return { type: "text", value: text2 };
16331
+ return { type: "text", value: text3 };
16255
16332
  }
16256
16333
  if (import_ts_morph63.Node.isJsxExpression(child)) {
16257
16334
  const inner = child.getExpression();
@@ -17248,11 +17325,12 @@ function makeFallthroughTerminal(func, pattern) {
17248
17325
 
17249
17326
  // src/assembly.ts
17250
17327
  var preconditionsOf = (effect) => effect.type === "invocation" ? effect.preconditions : void 0;
17251
- function firesOn(site, branch) {
17252
- if (!(0, import_extractor8.guardsHoldOn)(site.preconditions, branch.conditions)) {
17253
- return false;
17254
- }
17255
- return site.alwaysRuns || (0, import_extractor8.runsBefore)(site.line, branch.terminal.location.end);
17328
+ function invocationSite(call) {
17329
+ return {
17330
+ line: call.line,
17331
+ preconditions: preconditionsOf(call.effect),
17332
+ cleansUpFrom: call.cleansUpFrom
17333
+ };
17256
17334
  }
17257
17335
  function writesTerminal(call, terminalNodes) {
17258
17336
  let current2 = call;
@@ -17350,7 +17428,7 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
17350
17428
  const endings = new Set(
17351
17429
  terminals.filter(({ terminal }) => terminal.kind === "exit").map(({ node }) => node)
17352
17430
  );
17353
- const { byTerminal } = computePathConditions(
17431
+ const { byTerminal, placement } = computePathConditions(
17354
17432
  func,
17355
17433
  terminals.map(({ node }) => node),
17356
17434
  barriers,
@@ -17383,6 +17461,7 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
17383
17461
  const rawBranches = terminals.flatMap(
17384
17462
  ({ node, terminal, whenAlso }) => {
17385
17463
  const conditionLists = byTerminal.get(node) ?? [];
17464
+ const loopAround = placement.get(node)?.loopAround;
17386
17465
  return conditionLists.map((infos) => {
17387
17466
  const conditions = [
17388
17467
  ...infos.map(conditionInfoToRawCondition),
@@ -17393,6 +17472,7 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
17393
17472
  terminal,
17394
17473
  effects: [],
17395
17474
  location: terminal.location,
17475
+ ...loopAround === void 0 ? {} : { loopAround },
17396
17476
  isDefault: isDefaultConditionList(infos)
17397
17477
  };
17398
17478
  });
@@ -17419,14 +17499,12 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
17419
17499
  (i) => !writesTerminal(i.node, terminalNodes)
17420
17500
  );
17421
17501
  for (const branch of distinctBranches) {
17422
- branch.effects = sideEffects.filter(
17423
- (i) => firesOn({ ...i, preconditions: preconditionsOf(i.effect) }, branch)
17424
- ).map((i) => i.effect);
17502
+ branch.effects = sideEffects.filter((i) => (0, import_extractor8.firesOn)(invocationSite(i), branch)).map((i) => i.effect);
17425
17503
  }
17426
17504
  }
17427
17505
  const firing = /* @__PURE__ */ new Map();
17428
17506
  for (const branch of distinctBranches) {
17429
- const located = recognized.filter((r) => firesOn(r, branch));
17507
+ const located = recognized.filter((r) => (0, import_extractor8.firesOn)(r, branch));
17430
17508
  const extra = located.map((r) => r.effect);
17431
17509
  if (extra.length > 0) {
17432
17510
  branch.extraEffects = [...branch.extraEffects ?? [], ...extra];
@@ -17466,11 +17544,9 @@ function handOffSites(invocations, terminalNodes, handOff, reading) {
17466
17544
  continue;
17467
17545
  }
17468
17546
  sites.push({
17547
+ ...invocationSite(site),
17469
17548
  helper,
17470
- preconditions: preconditionsOf(site.effect),
17471
- line: site.line,
17472
- at: lineRangeOf(site.node),
17473
- alwaysRuns: site.alwaysRuns
17549
+ at: lineRangeOf(site.node)
17474
17550
  });
17475
17551
  }
17476
17552
  return sites;
@@ -17496,16 +17572,12 @@ function accountFor(site, terminalNodes, branches) {
17496
17572
  if (writesTerminal(node, terminalNodes)) {
17497
17573
  return { node, callee, line, outcome: "terminal" };
17498
17574
  }
17499
- const call = {
17500
- line,
17501
- alwaysRuns: site.alwaysRuns,
17502
- preconditions: preconditionsOf(site.effect)
17503
- };
17504
- if (branches.some((branch) => firesOn(call, branch))) {
17575
+ const call = invocationSite(site);
17576
+ if (branches.some((branch) => (0, import_extractor8.firesOn)(call, branch))) {
17505
17577
  return { node, callee, line, outcome: "invocation" };
17506
17578
  }
17507
17579
  const reachable = branches.some(
17508
- (branch) => site.alwaysRuns || (0, import_extractor8.runsBefore)(line, branch.terminal.location.end)
17580
+ (branch) => (0, import_extractor8.runsBefore)(call.cleansUpFrom ?? line, branch.terminal.location.end)
17509
17581
  );
17510
17582
  return {
17511
17583
  node,
@@ -18182,39 +18254,132 @@ function parameterName(segment, convention) {
18182
18254
  return null;
18183
18255
  }
18184
18256
 
18257
+ // src/discovery/graphqlCallClient.ts
18258
+ var import_ts_morph74 = require("ts-morph");
18259
+ var import_values12 = require("@suss/values");
18260
+
18185
18261
  // src/discovery/graphqlClientConstruction.ts
18186
18262
  var import_picomatch2 = __toESM(require("picomatch"), 1);
18187
18263
  var import_ts_morph73 = require("ts-morph");
18188
18264
  var import_behavioral_ir12 = require("@suss/behavioral-ir");
18189
18265
  function stampGraphqlClientRefs(summaries, sourceFiles, packs, resolution) {
18190
- const scopes = compileOperationScopes(packs);
18266
+ const constructions = graphqlClientConstructions(
18267
+ sourceFiles,
18268
+ packs.flatMap((pack) => pack.graphqlClients ?? []),
18269
+ resolution
18270
+ );
18191
18271
  const sole = soleGraphqlClientRef(
18192
- collectGraphqlClientRefs(sourceFiles, packs, resolution)
18272
+ constructionRefs(constructions, resolution)
18193
18273
  );
18194
- const workspace = sole !== null ? boundWorkspaceFor(sole, packs) : null;
18195
- const soleClient = sole !== null ? workspace !== null ? { ...sole, workspace } : sole : null;
18274
+ const grounding = {
18275
+ packs,
18276
+ scopes: compileOperationScopes(packs),
18277
+ sole,
18278
+ soleClient: withBoundWorkspace(sole, packs),
18279
+ clientsBuilt: constructions.filter(
18280
+ (construction) => construction.spec.linkProperty !== void 0
18281
+ ).length,
18282
+ pairWithAll: packs.some(
18283
+ (pack) => pack.graphqlUnboundClients === "pairWithAll"
18284
+ )
18285
+ };
18196
18286
  let registry = null;
18197
18287
  for (const summary of summaries) {
18198
18288
  if (!(0, import_behavioral_ir12.isGraphqlOperationBinding)(summary.identity.boundaryBinding)) {
18199
18289
  continue;
18200
18290
  }
18201
18291
  const existing = (0, import_behavioral_ir12.readGraphqlMetadata)(summary) ?? {};
18202
- const scoped = scopedWorkspaceFor(summary.location.file, scopes);
18203
- const client = scoped !== null ? { uri: null, uriRef: null, workspace: scoped } : soleClient;
18292
+ const decided = clientFor(summary, existing, grounding);
18204
18293
  const dangling = (existing.unresolvedFragments?.length ?? 0) > 0;
18205
- if (client === null && !dangling) {
18294
+ if (decided === null && !dangling) {
18206
18295
  continue;
18207
18296
  }
18208
18297
  if (dangling && registry === null) {
18209
18298
  registry = fragmentRegistryStatus(sourceFiles, packs, resolution);
18210
18299
  }
18211
18300
  summary.metadata = (0, import_behavioral_ir12.withGraphqlMetadata)(summary.metadata, {
18212
- ...existing,
18213
- ...client !== null ? { client } : {},
18301
+ ...decided === null ? existing : withoutClient(existing),
18302
+ ...decided ?? {},
18214
18303
  ...dangling && registry !== null ? { fragmentRegistry: registry } : {}
18215
18304
  });
18216
18305
  }
18217
18306
  }
18307
+ function withBoundWorkspace(sole, packs) {
18308
+ if (sole === null) {
18309
+ return null;
18310
+ }
18311
+ const workspace = boundWorkspaceFor(sole, packs);
18312
+ return workspace === null ? sole : { ...sole, workspace };
18313
+ }
18314
+ function underUnboundSetting(unresolved, grounding) {
18315
+ return grounding.pairWithAll ? { ...unresolved, pairWithAll: true } : unresolved;
18316
+ }
18317
+ function clientFor(summary, existing, grounding) {
18318
+ const scoped = scopedWorkspaceFor(summary.location.file, grounding.scopes);
18319
+ const chosen = existing.client?.chosenAtCall === true ? existing.client : null;
18320
+ if (chosen === null) {
18321
+ const open = existing.unresolvedClient !== void 0;
18322
+ if (existing.unresolvedClient !== void 0 && !oneEndpointToChoose(grounding)) {
18323
+ return grounding.pairWithAll ? {
18324
+ unresolvedClient: {
18325
+ ...existing.unresolvedClient,
18326
+ pairWithAll: true
18327
+ }
18328
+ } : null;
18329
+ }
18330
+ const client = scoped !== null ? { uri: null, uriRef: null, workspace: scoped } : grounding.soleClient;
18331
+ if (client === null) {
18332
+ return open ? {} : null;
18333
+ }
18334
+ return { client };
18335
+ }
18336
+ const passed = {
18337
+ uri: chosen.uri,
18338
+ uriRef: chosen.uriRef,
18339
+ chosenAtCall: true
18340
+ };
18341
+ const bound = boundWorkspaceFor(passed, grounding.packs) ?? scoped;
18342
+ if (bound !== null) {
18343
+ return { client: { ...passed, workspace: bound } };
18344
+ }
18345
+ if (isTheAppsClient(passed, grounding)) {
18346
+ return { client: passed };
18347
+ }
18348
+ return {
18349
+ client: passed,
18350
+ unresolvedClient: underUnboundSetting(unboundClient(passed), grounding)
18351
+ };
18352
+ }
18353
+ function isTheAppsClient(passed, grounding) {
18354
+ if (grounding.clientsBuilt === 1) {
18355
+ return true;
18356
+ }
18357
+ const key = endpointKeyOf(passed);
18358
+ return key !== null && grounding.sole !== null && key === endpointKeyOf(grounding.sole);
18359
+ }
18360
+ function oneEndpointToChoose(grounding) {
18361
+ return grounding.clientsBuilt <= 1 || grounding.sole !== null;
18362
+ }
18363
+ function unboundClient(passed) {
18364
+ const key = endpointKeyOf(passed);
18365
+ if (key === null) {
18366
+ return {
18367
+ reference: "the client it passes",
18368
+ reason: "it passes a client whose endpoint suss could not read, and the project builds more than one client, so suss cannot tell which service this one talks to"
18369
+ };
18370
+ }
18371
+ return {
18372
+ reference: key,
18373
+ reason: `it passes the client built with ${key}, the project builds more than one client, and no \`clients\` entry in the pack options says which service ${key} talks to`
18374
+ };
18375
+ }
18376
+ function endpointKeyOf(ref) {
18377
+ return ref.uri ?? ref.uriRef;
18378
+ }
18379
+ function withoutClient(existing) {
18380
+ const { client: _client, unresolvedClient: _unresolved, ...rest } = existing;
18381
+ return rest;
18382
+ }
18218
18383
  function compileOperationScopes(packs) {
18219
18384
  return packs.flatMap((pack) => pack.graphqlOperationScopes ?? []).map((scope) => {
18220
18385
  const matchers2 = scope.files.map(
@@ -18247,13 +18412,22 @@ function boundWorkspaceFor(ref, packs) {
18247
18412
  }
18248
18413
  return null;
18249
18414
  }
18250
- function collectGraphqlClientRefs(sourceFiles, packs, resolution) {
18251
- const specs = packs.flatMap((pack) => pack.graphqlClients ?? []);
18415
+ function constructionRefs(constructions, resolution) {
18416
+ return constructions.flatMap((construction) => {
18417
+ const found = constructionRef(
18418
+ construction.node,
18419
+ construction.spec.uriProperty,
18420
+ resolution
18421
+ );
18422
+ return found === null ? [] : [found];
18423
+ });
18424
+ }
18425
+ function graphqlClientConstructions(sourceFiles, specs, resolution) {
18252
18426
  if (specs.length === 0) {
18253
18427
  return [];
18254
18428
  }
18255
18429
  const store = resolution ?? new ResolutionStore();
18256
- const refs = [];
18430
+ const found = [];
18257
18431
  for (const sourceFile of sourceFiles) {
18258
18432
  for (const spec of specs) {
18259
18433
  const constructions = callsByOriginName(
@@ -18263,14 +18437,27 @@ function collectGraphqlClientRefs(sourceFiles, packs, resolution) {
18263
18437
  /* @__PURE__ */ new Set([spec.importName])
18264
18438
  );
18265
18439
  for (const node of constructions.keys()) {
18266
- const found = constructionRef(node, spec.uriProperty, resolution);
18267
- if (found !== null) {
18268
- refs.push(found);
18269
- }
18440
+ found.push({ node, spec });
18270
18441
  }
18271
18442
  }
18272
18443
  }
18273
- return refs;
18444
+ return found;
18445
+ }
18446
+ function constructionEndpoint(construction, transports, resolution) {
18447
+ const own = constructionRef(
18448
+ construction.node,
18449
+ construction.spec.uriProperty,
18450
+ resolution
18451
+ );
18452
+ const linkProperty = construction.spec.linkProperty;
18453
+ if (own !== null || linkProperty === void 0) {
18454
+ return own;
18455
+ }
18456
+ const options = firstArgumentObject(construction.node, resolution);
18457
+ const link = options === null ? null : propertyOf(options, linkProperty, resolution);
18458
+ const transport = link === null ? null : writtenNodeOf(link, resolution);
18459
+ const built = transports.find((one) => one.node === transport);
18460
+ return built === void 0 ? null : constructionRef(built.node, built.spec.uriProperty, resolution);
18274
18461
  }
18275
18462
  function soleGraphqlClientRef(refs) {
18276
18463
  const distinct = /* @__PURE__ */ new Map();
@@ -18393,11 +18580,153 @@ function constructionRef(node, uriProperty, resolution) {
18393
18580
  return { uri: null, uriRef: value.getText() };
18394
18581
  }
18395
18582
 
18583
+ // src/discovery/graphqlCallClient.ts
18584
+ var REFERENCE_LIMIT = 80;
18585
+ function clientChosenAt(site, specs, resolution) {
18586
+ const store = resolution ?? new ResolutionStore();
18587
+ const written = writtenClientOf(site, store);
18588
+ if (written === null) {
18589
+ return null;
18590
+ }
18591
+ const constructions = constructionsInProject(
18592
+ site.call.getSourceFile(),
18593
+ specs,
18594
+ store
18595
+ );
18596
+ const tokens = new Map(
18597
+ constructions.map((construction, at) => [
18598
+ construction.node,
18599
+ (0, import_values12.text)(`suss:client#${at}`)
18600
+ ])
18601
+ );
18602
+ const value = evaluatedValueThrough(
18603
+ written.node,
18604
+ site.through,
18605
+ store,
18606
+ tokens
18607
+ );
18608
+ const chosen = value === null ? unreadChain(written, store) : clientValueOf(value, written.property);
18609
+ if (chosen === void 0) {
18610
+ return null;
18611
+ }
18612
+ const picked = tokensIn(chosen, constructions.length);
18613
+ if (picked !== null && picked.length === 1) {
18614
+ const construction = constructions[picked[0]];
18615
+ const endpoint = construction === void 0 ? null : constructionEndpoint(construction, constructions, store);
18616
+ return {
18617
+ kind: "settled",
18618
+ client: endpoint ?? { uri: null, uriRef: null }
18619
+ };
18620
+ }
18621
+ if (picked === null && written.property === null) {
18622
+ return null;
18623
+ }
18624
+ const reference = referenceOf(written.node, written.property, store);
18625
+ const passes = written.property === null ? "the client it is called on" : "the client it passes";
18626
+ if (picked !== null && picked.length > 1) {
18627
+ return {
18628
+ kind: "unresolved",
18629
+ reference,
18630
+ reason: `${passes}, ${reference}, can be any of ${picked.length} clients, and nothing the call passes settles which`
18631
+ };
18632
+ }
18633
+ return {
18634
+ kind: "unresolved",
18635
+ reference,
18636
+ reason: `${passes}, ${reference}, does not come out as one client construction suss can read`
18637
+ };
18638
+ }
18639
+ function writtenClientOf(site, store) {
18640
+ if (site.client === "receiver") {
18641
+ const callee = site.call.getExpression();
18642
+ return import_ts_morph74.Node.isPropertyAccessExpression(callee) ? { node: callee.getExpression(), property: null } : null;
18643
+ }
18644
+ const options = site.call.getArguments()[site.client.argument];
18645
+ if (options === void 0) {
18646
+ return null;
18647
+ }
18648
+ const direct = site.through.length === 0;
18649
+ if (direct && optionWritten(options, site.client.property, store) === null) {
18650
+ return null;
18651
+ }
18652
+ return { node: options, property: site.client.property };
18653
+ }
18654
+ function clientValueOf(value, property) {
18655
+ const forced = (0, import_values12.force)(value);
18656
+ if (property === null) {
18657
+ return isUndefined(forced) ? void 0 : forced;
18658
+ }
18659
+ if (forced.kind !== "record") {
18660
+ return void 0;
18661
+ }
18662
+ const field = forced.fields.get(property);
18663
+ if (field === void 0 || isUndefined((0, import_values12.force)(field.value))) {
18664
+ return void 0;
18665
+ }
18666
+ return (0, import_values12.force)(field.value);
18667
+ }
18668
+ function unreadChain(written, store) {
18669
+ if (written.property === null) {
18670
+ return (0, import_values12.hole)("client");
18671
+ }
18672
+ return optionWritten(written.node, written.property, store) === null ? void 0 : (0, import_values12.hole)("client");
18673
+ }
18674
+ function isUndefined(value) {
18675
+ return value.kind === "constant" && value.options.every((option) => option === void 0);
18676
+ }
18677
+ function tokensIn(value, count) {
18678
+ if (value.kind !== "string" || value.pieces.length !== 1) {
18679
+ return null;
18680
+ }
18681
+ const piece = value.pieces[0];
18682
+ if (piece?.kind !== "text") {
18683
+ return null;
18684
+ }
18685
+ const picked = [];
18686
+ for (const option of piece.options) {
18687
+ const at = /^suss:client#(\d+)$/.exec(option)?.[1];
18688
+ if (at === void 0 || Number(at) >= count) {
18689
+ return null;
18690
+ }
18691
+ picked.push(Number(at));
18692
+ }
18693
+ return picked;
18694
+ }
18695
+ function referenceOf(node, property, store) {
18696
+ const written = property === null ? node : optionWritten(node, property, store);
18697
+ const shown = written === null ? `${node.getText()}.${property}` : writtenText(written, store);
18698
+ return shown.length > REFERENCE_LIMIT ? `${shown.slice(0, REFERENCE_LIMIT - 3)}...` : shown;
18699
+ }
18700
+ function optionWritten(options, property, store) {
18701
+ const literal = objectLiteralOf(options, store);
18702
+ return literal === null ? null : propertyOf(literal, property, store);
18703
+ }
18704
+ function writtenText(node, store) {
18705
+ if (!import_ts_morph74.Node.isIdentifier(node)) {
18706
+ return node.getText();
18707
+ }
18708
+ const value = store.resolveWrittenValue(node);
18709
+ return value === null ? node.getText() : value.getText();
18710
+ }
18711
+ var constructionsByStore = /* @__PURE__ */ new WeakMap();
18712
+ function constructionsInProject(sourceFile, specs, store) {
18713
+ const bySpecs = constructionsByStore.get(store) ?? /* @__PURE__ */ new WeakMap();
18714
+ constructionsByStore.set(store, bySpecs);
18715
+ const known = bySpecs.get(specs);
18716
+ if (known !== void 0) {
18717
+ return known;
18718
+ }
18719
+ const files = sourceFile.getProject().getSourceFiles().filter((file) => !file.isDeclarationFile() && !file.isInNodeModules());
18720
+ const found = graphqlClientConstructions(files, specs, store);
18721
+ bySpecs.set(specs, found);
18722
+ return found;
18723
+ }
18724
+
18396
18725
  // src/discovery/helperIndex.ts
18397
- var import_ts_morph75 = require("ts-morph");
18726
+ var import_ts_morph76 = require("ts-morph");
18398
18727
 
18399
18728
  // src/discovery/helperReading.ts
18400
- var import_ts_morph74 = require("ts-morph");
18729
+ var import_ts_morph75 = require("ts-morph");
18401
18730
  var UNREAD = { as: "unread" };
18402
18731
  function readHelperSinks(helper) {
18403
18732
  const positions = parameterPositions(helper);
@@ -18407,11 +18736,11 @@ function readHelperSinks(helper) {
18407
18736
  return sinks;
18408
18737
  }
18409
18738
  body.forEachDescendant((node) => {
18410
- if (!import_ts_morph74.Node.isCallExpression(node)) {
18739
+ if (!import_ts_morph75.Node.isCallExpression(node)) {
18411
18740
  return;
18412
18741
  }
18413
18742
  const callee = node.getExpression();
18414
- const [method, receiver] = import_ts_morph74.Node.isPropertyAccessExpression(callee) ? [callee.getName(), readValue(callee.getExpression(), positions)] : [null, UNREAD];
18743
+ const [method, receiver] = import_ts_morph75.Node.isPropertyAccessExpression(callee) ? [callee.getName(), readValue(callee.getExpression(), positions)] : [null, UNREAD];
18415
18744
  sinks.push({
18416
18745
  method,
18417
18746
  receiver,
@@ -18431,25 +18760,25 @@ function parameterPositions(helper) {
18431
18760
  return positions;
18432
18761
  }
18433
18762
  function readValue(node, positions) {
18434
- if (import_ts_morph74.Node.isStringLiteral(node) || import_ts_morph74.Node.isNoSubstitutionTemplateLiteral(node)) {
18763
+ if (import_ts_morph75.Node.isStringLiteral(node) || import_ts_morph75.Node.isNoSubstitutionTemplateLiteral(node)) {
18435
18764
  return { as: "text", text: node.getLiteralValue() };
18436
18765
  }
18437
- if (import_ts_morph74.Node.isTemplateExpression(node)) {
18766
+ if (import_ts_morph75.Node.isTemplateExpression(node)) {
18438
18767
  return templateText(node, positions);
18439
18768
  }
18440
- if (import_ts_morph74.Node.isIdentifier(node)) {
18769
+ if (import_ts_morph75.Node.isIdentifier(node)) {
18441
18770
  const position = positions.get(node.getText());
18442
18771
  return position === void 0 ? UNREAD : { as: "parameter", position };
18443
18772
  }
18444
- if (import_ts_morph74.Node.isPropertyAccessExpression(node)) {
18773
+ if (import_ts_morph75.Node.isPropertyAccessExpression(node)) {
18445
18774
  const target = node.getExpression();
18446
- const position = import_ts_morph74.Node.isIdentifier(target) ? positions.get(target.getText()) : void 0;
18775
+ const position = import_ts_morph75.Node.isIdentifier(target) ? positions.get(target.getText()) : void 0;
18447
18776
  return position === void 0 ? UNREAD : { as: "parameter", position, property: node.getName() };
18448
18777
  }
18449
- if (import_ts_morph74.Node.isObjectLiteralExpression(node)) {
18778
+ if (import_ts_morph75.Node.isObjectLiteralExpression(node)) {
18450
18779
  return objectValue(node, positions);
18451
18780
  }
18452
- if (import_ts_morph74.Node.isCallExpression(node)) {
18781
+ if (import_ts_morph75.Node.isCallExpression(node)) {
18453
18782
  return {
18454
18783
  as: "call",
18455
18784
  callee: node.getExpression().getText(),
@@ -18459,27 +18788,27 @@ function readValue(node, positions) {
18459
18788
  return UNREAD;
18460
18789
  }
18461
18790
  function templateText(node, positions) {
18462
- if (!import_ts_morph74.Node.isTemplateExpression(node)) {
18791
+ if (!import_ts_morph75.Node.isTemplateExpression(node)) {
18463
18792
  return UNREAD;
18464
18793
  }
18465
- let text2 = node.getHead().getLiteralText();
18794
+ let text3 = node.getHead().getLiteralText();
18466
18795
  for (const span of node.getTemplateSpans()) {
18467
18796
  const expression = span.getExpression();
18468
- const position = import_ts_morph74.Node.isIdentifier(expression) ? positions.get(expression.getText()) : void 0;
18797
+ const position = import_ts_morph75.Node.isIdentifier(expression) ? positions.get(expression.getText()) : void 0;
18469
18798
  if (position === void 0) {
18470
18799
  return UNREAD;
18471
18800
  }
18472
- text2 += `{${position}}${span.getLiteral().getLiteralText()}`;
18801
+ text3 += `{${position}}${span.getLiteral().getLiteralText()}`;
18473
18802
  }
18474
- return { as: "text", text: text2 };
18803
+ return { as: "text", text: text3 };
18475
18804
  }
18476
18805
  function objectValue(node, positions) {
18477
- if (!import_ts_morph74.Node.isObjectLiteralExpression(node)) {
18806
+ if (!import_ts_morph75.Node.isObjectLiteralExpression(node)) {
18478
18807
  return UNREAD;
18479
18808
  }
18480
18809
  const properties = {};
18481
18810
  for (const property of node.getProperties()) {
18482
- if (!import_ts_morph74.Node.isPropertyAssignment(property)) {
18811
+ if (!import_ts_morph75.Node.isPropertyAssignment(property)) {
18483
18812
  continue;
18484
18813
  }
18485
18814
  const initializer = property.getInitializer();
@@ -18494,7 +18823,7 @@ function objectValue(node, positions) {
18494
18823
  return { as: "object", properties };
18495
18824
  }
18496
18825
  function propertyKey(name) {
18497
- return import_ts_morph74.Node.isStringLiteral(name) ? name.getLiteralValue() : name.getText();
18826
+ return import_ts_morph75.Node.isStringLiteral(name) ? name.getLiteralValue() : name.getText();
18498
18827
  }
18499
18828
 
18500
18829
  // src/discovery/helperIndex.ts
@@ -18578,8 +18907,8 @@ function noteExpanded(expanded, readings, discovery) {
18578
18907
  }
18579
18908
  }
18580
18909
  function mentionsAnyOf(file, written) {
18581
- const text2 = file.getFullText();
18582
- return written.some((word) => text2.includes(word));
18910
+ const text3 = file.getFullText();
18911
+ return written.some((word) => text3.includes(word));
18583
18912
  }
18584
18913
  function readSubjectHelpers(pack, packsByFile, resolution) {
18585
18914
  const matches = registrationMatchesOf(pack);
@@ -18624,11 +18953,11 @@ function registrationMatchesOf(pack) {
18624
18953
  }
18625
18954
  function collectHandOffs(sourceFile, subjects, match, resolution, found) {
18626
18955
  sourceFile.forEachDescendant((node) => {
18627
- if (!import_ts_morph75.Node.isCallExpression(node)) {
18956
+ if (!import_ts_morph76.Node.isCallExpression(node)) {
18628
18957
  return;
18629
18958
  }
18630
18959
  const callee = node.getExpression();
18631
- if (!import_ts_morph75.Node.isIdentifier(callee)) {
18960
+ if (!import_ts_morph76.Node.isIdentifier(callee)) {
18632
18961
  return;
18633
18962
  }
18634
18963
  const handedOver = subjectArgument(node, subjects, match, resolution);
@@ -18643,12 +18972,12 @@ function collectHandOffs(sourceFile, subjects, match, resolution, found) {
18643
18972
  });
18644
18973
  }
18645
18974
  function subjectArgument(call, subjects, match, resolution) {
18646
- if (!import_ts_morph75.Node.isCallExpression(call)) {
18975
+ if (!import_ts_morph76.Node.isCallExpression(call)) {
18647
18976
  return -1;
18648
18977
  }
18649
18978
  const args = call.getArguments();
18650
18979
  for (const [position, argument] of args.entries()) {
18651
- if (!import_ts_morph75.Node.isIdentifier(argument) && !import_ts_morph75.Node.isPropertyAccessExpression(argument)) {
18980
+ if (!import_ts_morph76.Node.isIdentifier(argument) && !import_ts_morph76.Node.isPropertyAccessExpression(argument)) {
18652
18981
  continue;
18653
18982
  }
18654
18983
  if (subjectNodeFor(argument, subjects, match, resolution) !== void 0) {
@@ -18680,8 +19009,8 @@ function record(found, helper, name, subjectParameter) {
18680
19009
  function readTextHelpers(sourceFiles, contains) {
18681
19010
  const readings = [];
18682
19011
  for (const sourceFile of sourceFiles) {
18683
- const text2 = sourceFile.getFullText();
18684
- if (!contains.some((mark) => text2.includes(mark))) {
19012
+ const text3 = sourceFile.getFullText();
19013
+ if (!contains.some((mark) => text3.includes(mark))) {
18685
19014
  continue;
18686
19015
  }
18687
19016
  for (const [name, declaration] of declaredFunctions(sourceFile)) {
@@ -18709,7 +19038,7 @@ function declaredFunctions(sourceFile) {
18709
19038
  }
18710
19039
  for (const variable3 of sourceFile.getVariableDeclarations()) {
18711
19040
  const initializer = variable3.getInitializer();
18712
- if (initializer !== void 0 && (import_ts_morph75.Node.isArrowFunction(initializer) || import_ts_morph75.Node.isFunctionExpression(initializer))) {
19041
+ if (initializer !== void 0 && (import_ts_morph76.Node.isArrowFunction(initializer) || import_ts_morph76.Node.isFunctionExpression(initializer))) {
18713
19042
  found.push([variable3.getName(), initializer]);
18714
19043
  }
18715
19044
  }
@@ -18878,8 +19207,8 @@ function recordEdge(edgesByChild, candidate) {
18878
19207
  }
18879
19208
 
18880
19209
  // src/discovery/restWrapper.ts
18881
- var import_ts_morph76 = require("ts-morph");
18882
- var import_values12 = require("@suss/values");
19210
+ var import_ts_morph77 = require("ts-morph");
19211
+ var import_values13 = require("@suss/values");
18883
19212
  var MAX_WRAPPER_DEPTH2 = 3;
18884
19213
  function expandRestWrapperCallers(wrapper, readSink, store) {
18885
19214
  return expandFrom(
@@ -18895,8 +19224,8 @@ function parametersAsHoles(func) {
18895
19224
  const bindings = /* @__PURE__ */ new Map();
18896
19225
  for (const parameter of func.getParameters()) {
18897
19226
  const nameNode = parameter.getNameNode();
18898
- if (import_ts_morph76.Node.isIdentifier(nameNode)) {
18899
- bindings.set(nameNode.getText(), (0, import_values12.hole)(nameNode.getText()));
19227
+ if (import_ts_morph77.Node.isIdentifier(nameNode)) {
19228
+ bindings.set(nameNode.getText(), (0, import_values13.hole)(nameNode.getText()));
18900
19229
  }
18901
19230
  }
18902
19231
  return bindings;
@@ -18935,11 +19264,11 @@ function callSitesOf(func, store) {
18935
19264
  const byCall = /* @__PURE__ */ new Map();
18936
19265
  for (const parameter of func.getParameters()) {
18937
19266
  const nameNode = parameter.getNameNode();
18938
- if (!import_ts_morph76.Node.isIdentifier(nameNode)) {
19267
+ if (!import_ts_morph77.Node.isIdentifier(nameNode)) {
18939
19268
  continue;
18940
19269
  }
18941
19270
  for (const passed of store.argumentsPassedTo(parameter)) {
18942
- if (!import_ts_morph76.Node.isCallExpression(passed.call)) {
19271
+ if (!import_ts_morph77.Node.isCallExpression(passed.call)) {
18943
19272
  continue;
18944
19273
  }
18945
19274
  const key = siteKey(passed.call);
@@ -18965,7 +19294,7 @@ function valuesAt(site, bindings, store) {
18965
19294
  }
18966
19295
 
18967
19296
  // src/discovery/wrapperIndex.ts
18968
- var import_ts_morph77 = require("ts-morph");
19297
+ var import_ts_morph78 = require("ts-morph");
18969
19298
  var import_resolution9 = require("@suss/resolution");
18970
19299
  var NO_WRAPPERS = {
18971
19300
  wrappersFor: () => [],
@@ -19089,11 +19418,11 @@ function discoverWrapperRegistrations(sourceFile, pattern, match, wraps, resolut
19089
19418
  const candidates = [];
19090
19419
  const unresolved = [];
19091
19420
  sourceFile.forEachDescendant((node) => {
19092
- if (!import_ts_morph77.Node.isCallExpression(node)) {
19421
+ if (!import_ts_morph78.Node.isCallExpression(node)) {
19093
19422
  return;
19094
19423
  }
19095
19424
  const callee = node.getExpression();
19096
- if (!import_ts_morph77.Node.isPropertyAccessExpression(callee) || callee.getName() !== wraps.method) {
19425
+ if (!import_ts_morph78.Node.isPropertyAccessExpression(callee) || callee.getName() !== wraps.method) {
19097
19426
  return;
19098
19427
  }
19099
19428
  const subjectNode = subjectNodeFor(
@@ -19188,7 +19517,7 @@ function discoverConstructorOptions(sourceFile, pattern, wraps, subjects, resolu
19188
19517
  return { candidates, unresolved };
19189
19518
  }
19190
19519
  function constructorOptionOf(construction, wraps, resolution) {
19191
- if (!(import_ts_morph77.Node.isNewExpression(construction) || import_ts_morph77.Node.isCallExpression(construction))) {
19520
+ if (!(import_ts_morph78.Node.isNewExpression(construction) || import_ts_morph78.Node.isCallExpression(construction))) {
19192
19521
  return void 0;
19193
19522
  }
19194
19523
  const optionsArg = construction.getArguments()[wraps.targetPosition];
@@ -19243,7 +19572,7 @@ function scopeOf(args, wraps, resolution) {
19243
19572
  }
19244
19573
 
19245
19574
  // src/discoveryContext.ts
19246
- var import_ts_morph78 = require("ts-morph");
19575
+ var import_ts_morph79 = require("ts-morph");
19247
19576
  function createTsDiscoveryContext(resolution) {
19248
19577
  const store = resolution ?? new ResolutionStore();
19249
19578
  return {
@@ -19279,7 +19608,7 @@ function exportedFunctions(sourceFile, resolution) {
19279
19608
  return out;
19280
19609
  }
19281
19610
  function exportedFunction(decl, resolution) {
19282
- const writtenAgain = import_ts_morph78.Node.isVariableDeclaration(decl) && isWrittenAgain(decl);
19611
+ const writtenAgain = import_ts_morph79.Node.isVariableDeclaration(decl) && isWrittenAgain(decl);
19283
19612
  if (!writtenAgain) {
19284
19613
  const declared = resolveDeclarationToFunction(decl);
19285
19614
  if (declared !== null) {
@@ -19300,7 +19629,7 @@ function valueToAskAbout(decl, writtenAgain) {
19300
19629
  if (writtenAgain) {
19301
19630
  return decl;
19302
19631
  }
19303
- const value = import_ts_morph78.Node.isVariableDeclaration(decl) ? decl.getInitializer() ?? decl : decl;
19632
+ const value = import_ts_morph79.Node.isVariableDeclaration(decl) ? decl.getInitializer() ?? decl : decl;
19304
19633
  return couldStillNameAFunction(value) ? value : null;
19305
19634
  }
19306
19635
  function resolveDeclarationToFunction(decl) {
@@ -19308,18 +19637,18 @@ function resolveDeclarationToFunction(decl) {
19308
19637
  if (fn !== null) {
19309
19638
  return fn;
19310
19639
  }
19311
- if (import_ts_morph78.Node.isVariableDeclaration(decl)) {
19640
+ if (import_ts_morph79.Node.isVariableDeclaration(decl)) {
19312
19641
  const init = decl.getInitializer();
19313
- if (init !== void 0 && (import_ts_morph78.Node.isArrowFunction(init) || import_ts_morph78.Node.isFunctionExpression(init))) {
19642
+ if (init !== void 0 && (import_ts_morph79.Node.isArrowFunction(init) || import_ts_morph79.Node.isFunctionExpression(init))) {
19314
19643
  return init;
19315
19644
  }
19316
19645
  }
19317
19646
  return null;
19318
19647
  }
19319
19648
  function hasJsxReturn(func) {
19320
- if (import_ts_morph78.Node.isArrowFunction(func)) {
19649
+ if (import_ts_morph79.Node.isArrowFunction(func)) {
19321
19650
  const body2 = func.getBody();
19322
- if (import_ts_morph78.Node.isExpression(body2) && isJsxOrFragment(body2)) {
19651
+ if (import_ts_morph79.Node.isExpression(body2) && isJsxOrFragment(body2)) {
19323
19652
  return true;
19324
19653
  }
19325
19654
  }
@@ -19333,11 +19662,11 @@ function hasJsxReturn(func) {
19333
19662
  traversal.stop();
19334
19663
  return;
19335
19664
  }
19336
- if (node !== func && (import_ts_morph78.Node.isFunctionDeclaration(node) || import_ts_morph78.Node.isFunctionExpression(node) || import_ts_morph78.Node.isArrowFunction(node) || import_ts_morph78.Node.isMethodDeclaration(node))) {
19665
+ if (node !== func && (import_ts_morph79.Node.isFunctionDeclaration(node) || import_ts_morph79.Node.isFunctionExpression(node) || import_ts_morph79.Node.isArrowFunction(node) || import_ts_morph79.Node.isMethodDeclaration(node))) {
19337
19666
  traversal.skip();
19338
19667
  return;
19339
19668
  }
19340
- if (import_ts_morph78.Node.isReturnStatement(node)) {
19669
+ if (import_ts_morph79.Node.isReturnStatement(node)) {
19341
19670
  const expr = node.getExpression();
19342
19671
  if (expr !== void 0 && isJsxOrFragment(expr)) {
19343
19672
  found = true;
@@ -19348,7 +19677,7 @@ function hasJsxReturn(func) {
19348
19677
  }
19349
19678
  function isJsxOrFragment(node) {
19350
19679
  const current2 = peelParens(node);
19351
- return import_ts_morph78.Node.isJsxElement(current2) || import_ts_morph78.Node.isJsxSelfClosingElement(current2) || import_ts_morph78.Node.isJsxFragment(current2);
19680
+ return import_ts_morph79.Node.isJsxElement(current2) || import_ts_morph79.Node.isJsxSelfClosingElement(current2) || import_ts_morph79.Node.isJsxFragment(current2);
19352
19681
  }
19353
19682
 
19354
19683
  // src/everyUnit.ts
@@ -19375,7 +19704,7 @@ function stampExitCodeFrom(summaries, project, resolution) {
19375
19704
  }
19376
19705
 
19377
19706
  // src/flagReads.ts
19378
- var import_ts_morph79 = require("ts-morph");
19707
+ var import_ts_morph80 = require("ts-morph");
19379
19708
  function flagReads(func, parameterNames2, parsers, resolution, barriers = NO_BARRIERS) {
19380
19709
  if (parsers.length === 0 || parameterNames2.length === 0 || resolution === void 0) {
19381
19710
  return [];
@@ -19408,11 +19737,11 @@ function namedCallsIn(func, barriers) {
19408
19737
  traversal.skip();
19409
19738
  return;
19410
19739
  }
19411
- if (!import_ts_morph79.Node.isCallExpression(node)) {
19740
+ if (!import_ts_morph80.Node.isCallExpression(node)) {
19412
19741
  return;
19413
19742
  }
19414
19743
  const callee = node.getExpression();
19415
- if (import_ts_morph79.Node.isIdentifier(callee) || import_ts_morph79.Node.isPropertyAccessExpression(callee)) {
19744
+ if (import_ts_morph80.Node.isIdentifier(callee) || import_ts_morph80.Node.isPropertyAccessExpression(callee)) {
19416
19745
  calls.push(node);
19417
19746
  }
19418
19747
  });
@@ -19630,7 +19959,7 @@ function createReferenceIndex(sourceFiles) {
19630
19959
  }
19631
19960
 
19632
19961
  // src/resolve/clientBasePath.ts
19633
- var import_ts_morph80 = require("ts-morph");
19962
+ var import_ts_morph81 = require("ts-morph");
19634
19963
  var import_behavioral_ir13 = require("@suss/behavioral-ir");
19635
19964
  function clientBasePath(call, match, resolution, site) {
19636
19965
  if (match?.type !== "clientCall" || match.basePathOption === void 0) {
@@ -19672,13 +20001,14 @@ function clientConstructionConfig(call, match, resolution) {
19672
20001
  }
19673
20002
  function receiverOf2(call) {
19674
20003
  const callee = call.getExpression();
19675
- if (import_ts_morph80.Node.isPropertyAccessExpression(callee)) {
20004
+ if (import_ts_morph81.Node.isPropertyAccessExpression(callee)) {
19676
20005
  return callee.getExpression();
19677
20006
  }
19678
- return import_ts_morph80.Node.isIdentifier(callee) ? callee : null;
20007
+ return import_ts_morph81.Node.isIdentifier(callee) ? callee : null;
19679
20008
  }
19680
20009
 
19681
20010
  // src/resolve/closureRecords.ts
20011
+ var import_resolution10 = require("@suss/resolution");
19682
20012
  function fromScanRecord(unit) {
19683
20013
  const files = [unit.file, ...unit.deps];
19684
20014
  const placeOf = ([file, start, end]) => {
@@ -19746,7 +20076,7 @@ function closureUnitRecords(scans, stillValid, references) {
19746
20076
  continue;
19747
20077
  }
19748
20078
  const file = fileOfOffsetKey(key);
19749
- if (file === null) {
20079
+ if (file === null || reachesASubclass(key, scan.findings)) {
19750
20080
  continue;
19751
20081
  }
19752
20082
  const deps = depsOf(file, scan.findings, scan.read, references);
@@ -19762,6 +20092,9 @@ function closureUnitRecords(scans, stillValid, references) {
19762
20092
  }
19763
20093
  return units;
19764
20094
  }
20095
+ function reachesASubclass(key, findings) {
20096
+ return key !== (0, import_resolution10.plainKeyOf)(key) || findings.calls.some((call) => call.key !== (0, import_resolution10.plainKeyOf)(call.key));
20097
+ }
19765
20098
  function filesNamedBy(findings) {
19766
20099
  const files = [];
19767
20100
  for (const call of findings.calls) {
@@ -19850,10 +20183,102 @@ function placeOfKey(key) {
19850
20183
  }
19851
20184
 
19852
20185
  // src/resolve/reachableClosure.ts
19853
- var import_ts_morph81 = require("ts-morph");
20186
+ var import_ts_morph83 = require("ts-morph");
19854
20187
  var import_behavioral_ir14 = require("@suss/behavioral-ir");
19855
20188
  var import_datalog3 = require("@suss/datalog");
19856
20189
  var import_extractor15 = require("@suss/extractor");
20190
+ var import_resolution11 = require("@suss/resolution");
20191
+
20192
+ // src/resolve/receiverClass.ts
20193
+ var import_ts_morph82 = require("ts-morph");
20194
+ function candidateOnReceiver(callee, checked, scan) {
20195
+ if (!import_ts_morph82.Node.isPropertyAccessExpression(callee)) {
20196
+ return null;
20197
+ }
20198
+ const receiver = callee.getExpression();
20199
+ const name = callee.getName();
20200
+ const { scanning } = scan;
20201
+ const own = scanning === void 0 ? null : ownForm(receiver, scanning, scan.resolution, 0);
20202
+ const selfClass = scan.runsOn ?? (scanning === void 0 ? null : classOf(scanning));
20203
+ if (own !== null && selfClass !== null) {
20204
+ return memberOn(selfClass, name, own);
20205
+ }
20206
+ if (checked === null || !import_ts_morph82.Node.isClassDeclaration(checked.func.getParent())) {
20207
+ return null;
20208
+ }
20209
+ const sentTo = classSentTo(receiver);
20210
+ return sentTo === null ? null : runningOn(checked, sentTo);
20211
+ }
20212
+ function runningOn(candidate, cls) {
20213
+ const owner = candidate.func.getParent();
20214
+ if (!import_ts_morph82.Node.isClassDeclaration(owner) || owner === cls.node) {
20215
+ return candidate;
20216
+ }
20217
+ return { ...candidate, receiverClass: cls };
20218
+ }
20219
+ function subclassPlace(cls) {
20220
+ return { name: cls.name, file: cls.file };
20221
+ }
20222
+ function ownThis(node, scanning) {
20223
+ const owner = node.getFirstAncestor(
20224
+ (at) => import_ts_morph82.Node.isFunctionDeclaration(at) || import_ts_morph82.Node.isFunctionExpression(at) || import_ts_morph82.Node.isMethodDeclaration(at) || import_ts_morph82.Node.isConstructorDeclaration(at)
20225
+ );
20226
+ return owner === scanning;
20227
+ }
20228
+ function ownForm(value, scanning, resolution, depth) {
20229
+ const written = peelSyntax(value);
20230
+ if (import_ts_morph82.Node.isThisExpression(written)) {
20231
+ if (!ownThis(written, scanning) || !import_ts_morph82.Node.isMethodDeclaration(scanning)) {
20232
+ return null;
20233
+ }
20234
+ return scanning.isStatic() ? "class" : "instance";
20235
+ }
20236
+ if (import_ts_morph82.Node.isNewExpression(written)) {
20237
+ return ownForm(written.getExpression(), scanning, resolution, depth) === "class" ? "instance" : null;
20238
+ }
20239
+ if (!import_ts_morph82.Node.isIdentifier(written) || depth > 0) {
20240
+ return null;
20241
+ }
20242
+ const behind = writtenNodeOf(written, resolution);
20243
+ return behind === null || behind === written ? null : ownForm(behind, scanning, resolution, depth + 1);
20244
+ }
20245
+ function classOf(scanning) {
20246
+ const owner = scanning.getParent();
20247
+ return import_ts_morph82.Node.isClassDeclaration(owner) ? subclassOf(owner) : null;
20248
+ }
20249
+ function subclassOf(node) {
20250
+ const name = node.getName();
20251
+ return name === void 0 ? null : { node, name, file: node.getSourceFile().getFilePath() };
20252
+ }
20253
+ function classSentTo(receiver) {
20254
+ const written = peelSyntax(receiver);
20255
+ const named2 = classDeclarationOf(declarationsBehind(written.getSymbol()));
20256
+ if (named2 !== null) {
20257
+ return subclassOf(named2);
20258
+ }
20259
+ const typed = classDeclarationOf(
20260
+ written.getType().getSymbol()?.getDeclarations() ?? []
20261
+ );
20262
+ return typed === null ? null : subclassOf(typed);
20263
+ }
20264
+ function classDeclarationOf(declarations) {
20265
+ const classes = declarations.filter((one) => import_ts_morph82.Node.isClassDeclaration(one));
20266
+ const [only] = classes;
20267
+ return classes.length === 1 && only !== void 0 ? only : null;
20268
+ }
20269
+ function memberOn(cls, name, form) {
20270
+ const member = typeOf(cls, form).getProperty(name);
20271
+ const found = member === void 0 ? null : functionAmong(member.getDeclarations(), name);
20272
+ return found === null ? null : runningOn(found, cls);
20273
+ }
20274
+ function typeOf(cls, form) {
20275
+ if (form === "instance") {
20276
+ return cls.node.getType();
20277
+ }
20278
+ return cls.node.getProject().getTypeChecker().getTypeOfSymbolAtLocation(cls.node.getSymbolOrThrow(), cls.node);
20279
+ }
20280
+
20281
+ // src/resolve/reachableClosure.ts
19857
20282
  var GENERIC_TERMINALS = [
19858
20283
  { kind: "return", match: { type: "returnStatement" }, extraction: {} },
19859
20284
  { kind: "throw", match: { type: "throwExpression" }, extraction: {} },
@@ -19888,19 +20313,35 @@ function targetOf(node) {
19888
20313
  span: { start: node.getStart(), end: node.getEnd() }
19889
20314
  };
19890
20315
  }
20316
+ function placeOfCandidate(candidate) {
20317
+ const place = targetOf(candidate.func);
20318
+ return candidate.receiverClass === void 0 ? place : { ...place, receiverClass: subclassPlace(candidate.receiverClass) };
20319
+ }
20320
+ function candidateKey(candidate) {
20321
+ const key = nodeKey(candidate.func);
20322
+ return candidate.receiverClass === void 0 ? key : (0, import_resolution11.variantKey)(key, subclassPlace(candidate.receiverClass));
20323
+ }
19891
20324
  function resolveCallee(call, calleeName, scan) {
19892
- if (!import_ts_morph81.Node.isCallExpression(call)) {
20325
+ if (!import_ts_morph83.Node.isCallExpression(call)) {
19893
20326
  return null;
19894
20327
  }
19895
20328
  const callee = call.getExpression();
19896
20329
  const symbol = callee.getSymbol();
19897
20330
  const declarations = declarationsBehind(symbol);
19898
20331
  const direct = functionAmong(declarations, calleeName);
20332
+ const onReceiver = candidateOnReceiver(callee, direct, {
20333
+ scanning: scan.scanning,
20334
+ runsOn: scan.receiverClass,
20335
+ resolution: scan.resolution
20336
+ });
20337
+ if (onReceiver !== null) {
20338
+ return { kind: "followed", candidate: onReceiver };
20339
+ }
19899
20340
  if (direct !== null) {
19900
20341
  return { kind: "followed", candidate: direct };
19901
20342
  }
19902
20343
  const resolveSources = scan.resolveCallableSources;
19903
- if (resolveSources !== void 0 && import_ts_morph81.Node.isPropertyAccessExpression(callee) && declarations.some(isDeclaredShape)) {
20344
+ if (resolveSources !== void 0 && import_ts_morph83.Node.isPropertyAccessExpression(callee) && declarations.some(isDeclaredShape)) {
19904
20345
  if (scan.sourcesAsked !== void 0) {
19905
20346
  scan.sourcesAsked.asked = true;
19906
20347
  }
@@ -19929,7 +20370,7 @@ function resolveCallee(call, calleeName, scan) {
19929
20370
  );
19930
20371
  }
19931
20372
  function stopFor(callee, declarations, stop, scan, parameterIndex) {
19932
- const asks = scan.resolution !== void 0 && (stop.reason === "unsettledValue" || import_ts_morph81.Node.isElementAccessExpression(callee));
20373
+ const asks = scan.resolution !== void 0 && (stop.reason === "unsettledValue" || import_ts_morph83.Node.isElementAccessExpression(callee));
19933
20374
  return {
19934
20375
  kind: "stopped",
19935
20376
  stop,
@@ -19939,7 +20380,7 @@ function stopFor(callee, declarations, stop, scan, parameterIndex) {
19939
20380
  };
19940
20381
  }
19941
20382
  function storeKeyOf(callee) {
19942
- const declarations = import_ts_morph81.Node.isIdentifier(callee) ? declarationsBehind(callee.getSymbol()) : [];
20383
+ const declarations = import_ts_morph83.Node.isIdentifier(callee) ? declarationsBehind(callee.getSymbol()) : [];
19943
20384
  return declarations.length === 1 ? declarations[0] : callee;
19944
20385
  }
19945
20386
  function settleThroughStore(outcomes, scan) {
@@ -19998,7 +20439,7 @@ function declarationToReport(declarations, calleeName, scan) {
19998
20439
  }
19999
20440
  function declaredAtOf(outcome) {
20000
20441
  if (outcome.kind === "followed") {
20001
- return targetOf(outcome.candidate.func);
20442
+ return placeOfCandidate(outcome.candidate);
20002
20443
  }
20003
20444
  if (outcome.kind === "possible") {
20004
20445
  return null;
@@ -20024,7 +20465,7 @@ function mayDispatch(one) {
20024
20465
  if (one.outcome.askStore !== void 0) {
20025
20466
  return true;
20026
20467
  }
20027
- return import_ts_morph81.Node.isCallExpression(one.node) && import_ts_morph81.Node.isCallExpression(one.node.getExpression());
20468
+ return import_ts_morph83.Node.isCallExpression(one.node) && import_ts_morph83.Node.isCallExpression(one.node.getExpression());
20028
20469
  }
20029
20470
  function dispatchOutcome(answer, stopped) {
20030
20471
  const { callee } = stopped.stop;
@@ -20047,18 +20488,18 @@ function dispatchOutcome(answer, stopped) {
20047
20488
  }
20048
20489
  function entryNameOf(func) {
20049
20490
  const property = func.getParent();
20050
- const key = property === void 0 || !import_ts_morph81.Node.isPropertyAssignment(property) ? null : propertyNameOf(property);
20491
+ const key = property === void 0 || !import_ts_morph83.Node.isPropertyAssignment(property) ? null : propertyNameOf(property);
20051
20492
  if (property === void 0 || key === null) {
20052
20493
  return null;
20053
20494
  }
20054
20495
  const holder = climbSyntax(property.getParentOrThrow()).getParent();
20055
- return holder !== void 0 && import_ts_morph81.Node.isVariableDeclaration(holder) ? `${holder.getName()}.${key}` : key;
20496
+ return holder !== void 0 && import_ts_morph83.Node.isVariableDeclaration(holder) ? `${holder.getName()}.${key}` : key;
20056
20497
  }
20057
20498
  function placeCallee(placements, calleeText, call, outcome) {
20058
20499
  if (outcome.kind === "possible") {
20059
20500
  placements.placePossible(
20060
20501
  calleeText,
20061
- outcome.candidates.map((candidate) => targetOf(candidate.func))
20502
+ outcome.candidates.map(placeOfCandidate)
20062
20503
  );
20063
20504
  return;
20064
20505
  }
@@ -20072,24 +20513,24 @@ function placeCallee(placements, calleeText, call, outcome) {
20072
20513
  }
20073
20514
  }
20074
20515
  function isCallOnOtherValue(call) {
20075
- if (!import_ts_morph81.Node.isCallExpression(call)) {
20516
+ if (!import_ts_morph83.Node.isCallExpression(call)) {
20076
20517
  return false;
20077
20518
  }
20078
20519
  const callee = call.getExpression();
20079
- if (!import_ts_morph81.Node.isPropertyAccessExpression(callee) && !import_ts_morph81.Node.isElementAccessExpression(callee)) {
20520
+ if (!import_ts_morph83.Node.isPropertyAccessExpression(callee) && !import_ts_morph83.Node.isElementAccessExpression(callee)) {
20080
20521
  return false;
20081
20522
  }
20082
- return !import_ts_morph81.Node.isThisExpression(callee.getExpression());
20523
+ return !import_ts_morph83.Node.isThisExpression(callee.getExpression());
20083
20524
  }
20084
20525
  function passedPositionKey(target, position) {
20085
20526
  return `${offsetKeyFor(target.file, target.span)}#${position}`;
20086
20527
  }
20087
20528
  function resolveJsxReference(node, scan) {
20088
- if (!import_ts_morph81.Node.isJsxOpeningElement(node) && !import_ts_morph81.Node.isJsxSelfClosingElement(node)) {
20529
+ if (!import_ts_morph83.Node.isJsxOpeningElement(node) && !import_ts_morph83.Node.isJsxSelfClosingElement(node)) {
20089
20530
  return null;
20090
20531
  }
20091
20532
  const tag = node.getTagNameNode();
20092
- if (!import_ts_morph81.Node.isIdentifier(tag) || !/^[A-Z]/.test(tag.getText())) {
20533
+ if (!import_ts_morph83.Node.isIdentifier(tag) || !/^[A-Z]/.test(tag.getText())) {
20093
20534
  return null;
20094
20535
  }
20095
20536
  const name = tag.getText();
@@ -20111,7 +20552,7 @@ function resolveJsxReference(node, scan) {
20111
20552
  };
20112
20553
  }
20113
20554
  function collectReachable(root, scan) {
20114
- const atModuleScope = import_ts_morph81.Node.isSourceFile(root);
20555
+ const atModuleScope = import_ts_morph83.Node.isSourceFile(root);
20115
20556
  const inFunc = atModuleScope ? { ...scan } : { ...scan, scanning: root };
20116
20557
  const calls = [];
20117
20558
  const stops = [];
@@ -20128,7 +20569,7 @@ function collectReachable(root, scan) {
20128
20569
  }
20129
20570
  };
20130
20571
  const recordFollowed = (candidate) => {
20131
- const key = nodeKey(candidate.func);
20572
+ const key = candidateKey(candidate);
20132
20573
  if (seen.has(key)) {
20133
20574
  return;
20134
20575
  }
@@ -20150,18 +20591,18 @@ function collectReachable(root, scan) {
20150
20591
  recordFollowed(outcome.candidate);
20151
20592
  };
20152
20593
  const recordPassedArgs = (call, calleeText, calleeOutcome) => {
20153
- if (!import_ts_morph81.Node.isCallExpression(call)) {
20594
+ if (!import_ts_morph83.Node.isCallExpression(call)) {
20154
20595
  return;
20155
20596
  }
20156
20597
  const calleeTarget = declaredAtOf(calleeOutcome);
20157
20598
  call.getArguments().forEach((arg, position) => {
20158
- if (import_ts_morph81.Node.isArrowFunction(arg) || import_ts_morph81.Node.isFunctionExpression(arg)) {
20599
+ if (import_ts_morph83.Node.isArrowFunction(arg) || import_ts_morph83.Node.isFunctionExpression(arg)) {
20159
20600
  if (calleeTarget !== null) {
20160
20601
  passedPositions.add(passedPositionKey(calleeTarget, position));
20161
20602
  }
20162
20603
  return;
20163
20604
  }
20164
- if (!import_ts_morph81.Node.isIdentifier(arg)) {
20605
+ if (!import_ts_morph83.Node.isIdentifier(arg)) {
20165
20606
  return;
20166
20607
  }
20167
20608
  const resolved = functionTargetOf(arg);
@@ -20186,7 +20627,7 @@ function collectReachable(root, scan) {
20186
20627
  found.push({ node, outcome: jsx.outcome });
20187
20628
  return;
20188
20629
  }
20189
- if (!import_ts_morph81.Node.isCallExpression(node)) {
20630
+ if (!import_ts_morph83.Node.isCallExpression(node)) {
20190
20631
  return;
20191
20632
  }
20192
20633
  const calleeText = (0, import_behavioral_ir14.normalizeCalleeText)(node.getExpression().getText());
@@ -20290,7 +20731,7 @@ function recognizerOnlyRoots(packsByFile) {
20290
20731
  }
20291
20732
  for (const declaration of variableStatement.getDeclarations()) {
20292
20733
  const init = declaration.getInitializer();
20293
- if (init !== void 0 && (import_ts_morph81.Node.isArrowFunction(init) || import_ts_morph81.Node.isFunctionExpression(init)) && hasBody(init)) {
20734
+ if (init !== void 0 && (import_ts_morph83.Node.isArrowFunction(init) || import_ts_morph83.Node.isFunctionExpression(init)) && hasBody(init)) {
20294
20735
  roots.push({ func: init, name: declaration.getName() });
20295
20736
  }
20296
20737
  }
@@ -20320,6 +20761,8 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20320
20761
  const passedPositions = /* @__PURE__ */ new Set();
20321
20762
  const summariesByKey = /* @__PURE__ */ new Map();
20322
20763
  const reachedFrom = /* @__PURE__ */ new Map();
20764
+ const receiverByKey = /* @__PURE__ */ new Map();
20765
+ const callsByKey = /* @__PURE__ */ new Map();
20323
20766
  const finding = {
20324
20767
  project,
20325
20768
  lookup,
@@ -20328,7 +20771,8 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20328
20771
  storeAnswers: /* @__PURE__ */ new Map(),
20329
20772
  funcByKey,
20330
20773
  moduleByKey,
20331
- reachedFrom
20774
+ reachedFrom,
20775
+ receiverByKey
20332
20776
  };
20333
20777
  for (const seed of seeds) {
20334
20778
  const func = lookup.functionAt(seed.location);
@@ -20386,25 +20830,53 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20386
20830
  for (const position of found.passedPositions) {
20387
20831
  passedPositions.add(position);
20388
20832
  }
20389
- const callerFile = fileOfOffsetKey(key);
20390
- for (const call of found.calls) {
20833
+ const callerFile = fileOfOffsetKey((0, import_resolution11.plainKeyOf)(key));
20834
+ const calledHere = found.calls.flatMap(
20835
+ (call) => call.receiverClass === void 0 ? [call] : [
20836
+ { ...call, key: (0, import_resolution11.plainKeyOf)(call.key), receiverClass: void 0 },
20837
+ call
20838
+ ]
20839
+ );
20840
+ const called = [];
20841
+ for (const call of calledHere) {
20391
20842
  if (!nameByKey.has(call.key)) {
20392
20843
  nameByKey.set(call.key, call.name);
20393
20844
  }
20394
20845
  if (call.func !== void 0 && !funcByKey.has(call.key)) {
20395
20846
  funcByKey.set(call.key, call.func);
20396
20847
  }
20848
+ if (call.receiverClass !== void 0) {
20849
+ receiverByKey.set(call.key, call.receiverClass);
20850
+ }
20397
20851
  if (callerFile !== null && !reachedFrom.has(call.key)) {
20398
20852
  reachedFrom.set(call.key, callerFile);
20399
20853
  }
20400
20854
  db.add("calls", [key, call.key]);
20855
+ called.push(call.key);
20401
20856
  }
20857
+ callsByKey.set(key, called);
20402
20858
  }
20403
20859
  }
20860
+ const placementsOf = (key) => ({
20861
+ targets: targetsByKey.get(key),
20862
+ argTargets: argTargetsByKey.get(key),
20863
+ possibleTargets: possibleTargetsByKey.get(key)
20864
+ });
20865
+ const onSubclass = (0, import_resolution11.keptVariants)(
20866
+ [...receiverByKey.keys()].map((key) => ({
20867
+ key,
20868
+ differs: !(0, import_behavioral_ir14.sameDeclarations)(
20869
+ placementsOf(key),
20870
+ placementsOf((0, import_resolution11.plainKeyOf)(key))
20871
+ ),
20872
+ calls: callsByKey.get(key) ?? []
20873
+ }))
20874
+ );
20404
20875
  const reached = [];
20405
20876
  for (const [keyAtom] of db.facts("reachable")) {
20406
20877
  const key = String(keyAtom);
20407
- if (known.has(key) || seedKeys.has(key)) {
20878
+ const receiverClass = receiverByKey.get(key);
20879
+ if (known.has(key) || seedKeys.has(key) || receiverClass !== void 0 && !onSubclass.has(key)) {
20408
20880
  continue;
20409
20881
  }
20410
20882
  const name = nameByKey.get(key);
@@ -20415,7 +20887,7 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20415
20887
  if (projectFileSet !== void 0) {
20416
20888
  lazyAddSourceFile(project, projectFileSet, file);
20417
20889
  }
20418
- const func = funcByKey.get(key) ?? functionAtKey(key, lookup);
20890
+ const func = funcByKey.get(key) ?? functionAtKey((0, import_resolution11.plainKeyOf)(key), lookup);
20419
20891
  if (func === void 0) {
20420
20892
  continue;
20421
20893
  }
@@ -20425,6 +20897,10 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20425
20897
  facts,
20426
20898
  () => extractReachableSummary(candidate, options, recognizers)
20427
20899
  );
20900
+ if (receiverClass !== void 0) {
20901
+ summary.receiverClass = subclassPlace(receiverClass);
20902
+ summary.identity.exportPath = [receiverClass.name, name];
20903
+ }
20428
20904
  facts?.unitKeyBySummary.set(summary, key);
20429
20905
  rememberSummary(summariesByKey, key, summary);
20430
20906
  reached.push(summary);
@@ -20481,14 +20957,15 @@ function knownUnits(summaries, lookup, facts) {
20481
20957
  }
20482
20958
  function findingsFor(key, ctx) {
20483
20959
  const cameFrom = ctx.reachedFrom.get(key);
20484
- const stored = ctx.facts?.previousScans?.get(key);
20960
+ const receiverClass = ctx.receiverByKey.get(key);
20961
+ const stored = receiverClass === void 0 ? ctx.facts?.previousScans?.get(key) : void 0;
20485
20962
  const recorded = stored !== void 0 && recordValidFrom(stored.data, cameFrom) ? fromScanRecord(stored) : null;
20486
20963
  if (stored !== void 0 && recorded !== null) {
20487
20964
  ctx.facts?.scans?.set(key, { kind: "reused", unit: stored });
20488
20965
  noteFilesRead(key, ctx.facts, stored.deps);
20489
20966
  return recorded;
20490
20967
  }
20491
- const root = ctx.funcByKey.get(key) ?? ctx.moduleByKey.get(key) ?? functionAtKey(key, ctx.lookup);
20968
+ const root = ctx.funcByKey.get(key) ?? ctx.moduleByKey.get(key) ?? functionAtKey((0, import_resolution11.plainKeyOf)(key), ctx.lookup);
20492
20969
  if (root === void 0) {
20493
20970
  return null;
20494
20971
  }
@@ -20499,7 +20976,8 @@ function findingsFor(key, ctx) {
20499
20976
  ...resolution === void 0 ? {} : { resolution, storeAnswers: ctx.storeAnswers },
20500
20977
  ...resolveCallableSources === void 0 ? {} : { resolveCallableSources, sourcesAsked },
20501
20978
  ...sourceDeclarationsBehind2 === void 0 ? {} : { sourceDeclarationsBehind: sourceDeclarationsBehind2 },
20502
- ...reachedFrom === void 0 ? {} : { reachedFrom }
20979
+ ...reachedFrom === void 0 ? {} : { reachedFrom },
20980
+ ...receiverClass === void 0 ? {} : { receiverClass }
20503
20981
  };
20504
20982
  const scanned = scanOrReport(key, ctx.facts, root, scan);
20505
20983
  if (scanned === null) {
@@ -20519,7 +20997,7 @@ function scanOrReport(key, facts, root, scan) {
20519
20997
  return scanWithRecording(key, facts, () => collectReachable(root, scan));
20520
20998
  } catch (error) {
20521
20999
  const message = error instanceof Error ? error.message : String(error);
20522
- const where = import_ts_morph81.Node.isSourceFile(root) ? root.getFilePath() : `${root.getSourceFile().getFilePath()}:${root.getStartLineNumber()}`;
21000
+ const where = import_ts_morph83.Node.isSourceFile(root) ? root.getFilePath() : `${root.getSourceFile().getFilePath()}:${root.getStartLineNumber()}`;
20523
21001
  process.stderr.write(
20524
21002
  `[suss] could not scan the function at ${where} for the calls it makes, so nothing it calls is followed from it: ${message}
20525
21003
  `
@@ -20643,7 +21121,7 @@ function recordParameterGaps(parameterCallsByKey, summariesByKey, passedPosition
20643
21121
  }
20644
21122
  for (const [key, parameterCalls] of parameterCallsByKey) {
20645
21123
  const unbound = parameterCalls.filter(
20646
- ({ parameterIndex }) => !passedPositions.has(`${key}#${parameterIndex}`)
21124
+ ({ parameterIndex }) => !passedPositions.has(`${(0, import_resolution11.plainKeyOf)(key)}#${parameterIndex}`)
20647
21125
  );
20648
21126
  if (unbound.length === 0) {
20649
21127
  continue;
@@ -20660,7 +21138,7 @@ function recordParameterGaps(parameterCallsByKey, summariesByKey, passedPosition
20660
21138
 
20661
21139
  // src/resolve/redirectDelivery.ts
20662
21140
  var import_extractor16 = require("@suss/extractor");
20663
- var import_values13 = require("@suss/values");
21141
+ var import_values14 = require("@suss/values");
20664
21142
  function settingOf(object, name, resolution, site) {
20665
21143
  if (object === void 0) {
20666
21144
  return { kind: "absent" };
@@ -20676,7 +21154,7 @@ function settingOf(object, name, resolution, site) {
20676
21154
  if (field.presence !== "one") {
20677
21155
  return { kind: "unknown" };
20678
21156
  }
20679
- const settled3 = (0, import_values13.scalarOf)((0, import_values13.force)(field.value));
21157
+ const settled3 = (0, import_values14.scalarOf)((0, import_values14.force)(field.value));
20680
21158
  return settled3 === null ? { kind: "unknown" } : { kind: "settled", value: settled3 };
20681
21159
  }
20682
21160
  function redirectDeliveryAtCall(callSite, pattern, pack, resolution) {
@@ -20701,7 +21179,7 @@ function redirectDeliveryAtCall(callSite, pattern, pack, resolution) {
20701
21179
  }
20702
21180
 
20703
21181
  // src/resolve/rethrowEnrichment.ts
20704
- var import_ts_morph82 = require("ts-morph");
21182
+ var import_ts_morph84 = require("ts-morph");
20705
21183
  var import_datalog4 = require("@suss/datalog");
20706
21184
  var RETHROW_RULES = [
20707
21185
  (0, import_datalog4.rule)(
@@ -20838,7 +21316,7 @@ function findBareRethrow(func, location) {
20838
21316
  traversal.stop();
20839
21317
  return;
20840
21318
  }
20841
- if (!import_ts_morph82.Node.isThrowStatement(node)) {
21319
+ if (!import_ts_morph84.Node.isThrowStatement(node)) {
20842
21320
  return;
20843
21321
  }
20844
21322
  if (startLineOf(node) !== location.start) {
@@ -20848,7 +21326,7 @@ function findBareRethrow(func, location) {
20848
21326
  return;
20849
21327
  }
20850
21328
  const thrown = node.getExpression();
20851
- if (thrown !== void 0 && import_ts_morph82.Node.isIdentifier(thrown)) {
21329
+ if (thrown !== void 0 && import_ts_morph84.Node.isIdentifier(thrown)) {
20852
21330
  found = node;
20853
21331
  }
20854
21332
  });
@@ -20858,13 +21336,13 @@ function enclosingTry(throwStmt) {
20858
21336
  let current2 = throwStmt.getParent();
20859
21337
  let sawCatchClause = false;
20860
21338
  while (current2 !== void 0) {
20861
- if (import_ts_morph82.Node.isCatchClause(current2)) {
21339
+ if (import_ts_morph84.Node.isCatchClause(current2)) {
20862
21340
  sawCatchClause = true;
20863
21341
  }
20864
- if (import_ts_morph82.Node.isTryStatement(current2)) {
21342
+ if (import_ts_morph84.Node.isTryStatement(current2)) {
20865
21343
  return sawCatchClause ? current2 : null;
20866
21344
  }
20867
- if (import_ts_morph82.Node.isFunctionDeclaration(current2) || import_ts_morph82.Node.isFunctionExpression(current2) || import_ts_morph82.Node.isArrowFunction(current2) || import_ts_morph82.Node.isMethodDeclaration(current2)) {
21345
+ if (import_ts_morph84.Node.isFunctionDeclaration(current2) || import_ts_morph84.Node.isFunctionExpression(current2) || import_ts_morph84.Node.isArrowFunction(current2) || import_ts_morph84.Node.isMethodDeclaration(current2)) {
20868
21346
  return null;
20869
21347
  }
20870
21348
  current2 = current2.getParent();
@@ -20872,14 +21350,14 @@ function enclosingTry(throwStmt) {
20872
21350
  return null;
20873
21351
  }
20874
21352
  function collectTryBodyCallees(tryStmt, index, keyFor) {
20875
- if (!import_ts_morph82.Node.isTryStatement(tryStmt)) {
21353
+ if (!import_ts_morph84.Node.isTryStatement(tryStmt)) {
20876
21354
  return [];
20877
21355
  }
20878
21356
  const tryBlock = tryStmt.getTryBlock();
20879
21357
  const callees = [];
20880
21358
  const seen = /* @__PURE__ */ new Set();
20881
21359
  tryBlock.forEachDescendant((node) => {
20882
- if (!import_ts_morph82.Node.isCallExpression(node)) {
21360
+ if (!import_ts_morph84.Node.isCallExpression(node)) {
20883
21361
  return;
20884
21362
  }
20885
21363
  const calleeSummary = resolveCalleeSummary(node, index);
@@ -20896,7 +21374,7 @@ function collectTryBodyCallees(tryStmt, index, keyFor) {
20896
21374
  return callees;
20897
21375
  }
20898
21376
  function resolveCalleeSummary(call, index) {
20899
- if (!import_ts_morph82.Node.isCallExpression(call)) {
21377
+ if (!import_ts_morph84.Node.isCallExpression(call)) {
20900
21378
  return null;
20901
21379
  }
20902
21380
  const callee = call.getExpression();
@@ -20928,7 +21406,7 @@ function resolveCalleeSummary(call, index) {
20928
21406
  }
20929
21407
 
20930
21408
  // src/subUnitContext.ts
20931
- var import_ts_morph83 = require("ts-morph");
21409
+ var import_ts_morph85 = require("ts-morph");
20932
21410
  function createTsSubUnitContext() {
20933
21411
  return {
20934
21412
  findJsxAttributes,
@@ -20955,18 +21433,18 @@ function findJsxAttributes(parent) {
20955
21433
  if (skip(node, traversal)) {
20956
21434
  return;
20957
21435
  }
20958
- if (!import_ts_morph83.Node.isJsxOpeningElement(node) && !import_ts_morph83.Node.isJsxSelfClosingElement(node)) {
21436
+ if (!import_ts_morph85.Node.isJsxOpeningElement(node) && !import_ts_morph85.Node.isJsxSelfClosingElement(node)) {
20959
21437
  return;
20960
21438
  }
20961
21439
  const tag = node.getTagNameNode().getText();
20962
21440
  for (const attr of node.getAttributes()) {
20963
- if (!import_ts_morph83.Node.isJsxAttribute(attr)) {
21441
+ if (!import_ts_morph85.Node.isJsxAttribute(attr)) {
20964
21442
  continue;
20965
21443
  }
20966
21444
  const initializer = attr.getInitializer();
20967
21445
  let valueExpression = null;
20968
21446
  if (initializer !== void 0) {
20969
- if (import_ts_morph83.Node.isJsxExpression(initializer)) {
21447
+ if (import_ts_morph85.Node.isJsxExpression(initializer)) {
20970
21448
  const inner = initializer.getExpression();
20971
21449
  valueExpression = inner ?? null;
20972
21450
  } else {
@@ -20989,11 +21467,11 @@ function findCallExpressionsByName(parent, name) {
20989
21467
  if (skip(node, traversal)) {
20990
21468
  return;
20991
21469
  }
20992
- if (!import_ts_morph83.Node.isCallExpression(node)) {
21470
+ if (!import_ts_morph85.Node.isCallExpression(node)) {
20993
21471
  return;
20994
21472
  }
20995
21473
  const callee = node.getExpression();
20996
- if (import_ts_morph83.Node.isIdentifier(callee) && callee.getText() === name) {
21474
+ if (import_ts_morph85.Node.isIdentifier(callee) && callee.getText() === name) {
20997
21475
  out.push(node);
20998
21476
  }
20999
21477
  });
@@ -21004,10 +21482,10 @@ function resolveAttributeValueFunction(attr, parent) {
21004
21482
  if (expr === null) {
21005
21483
  return null;
21006
21484
  }
21007
- if (import_ts_morph83.Node.isArrowFunction(expr) || import_ts_morph83.Node.isFunctionExpression(expr)) {
21485
+ if (import_ts_morph85.Node.isArrowFunction(expr) || import_ts_morph85.Node.isFunctionExpression(expr)) {
21008
21486
  return { func: expr, localName: null };
21009
21487
  }
21010
- if (import_ts_morph83.Node.isIdentifier(expr)) {
21488
+ if (import_ts_morph85.Node.isIdentifier(expr)) {
21011
21489
  const symbol = expr.getSymbol();
21012
21490
  if (symbol === void 0) {
21013
21491
  return null;
@@ -21040,7 +21518,7 @@ function getCallArgumentFunction(call, position) {
21040
21518
  if (arg === void 0) {
21041
21519
  return null;
21042
21520
  }
21043
- if (import_ts_morph83.Node.isArrowFunction(arg) || import_ts_morph83.Node.isFunctionExpression(arg)) {
21521
+ if (import_ts_morph85.Node.isArrowFunction(arg) || import_ts_morph85.Node.isFunctionExpression(arg)) {
21044
21522
  return arg;
21045
21523
  }
21046
21524
  return null;
@@ -21052,7 +21530,7 @@ function readArrayLiteralText(node) {
21052
21530
  if (node === null) {
21053
21531
  return null;
21054
21532
  }
21055
- if (import_ts_morph83.Node.isArrayLiteralExpression(node)) {
21533
+ if (import_ts_morph85.Node.isArrayLiteralExpression(node)) {
21056
21534
  return node.getElements().map((e) => e.getText());
21057
21535
  }
21058
21536
  return [node.getText()];
@@ -21066,7 +21544,7 @@ var import_files2 = require("@suss/behavioral-ir/files");
21066
21544
  // src/bootstrap/noTsconfigProject.ts
21067
21545
  var import_node_fs5 = __toESM(require("fs"), 1);
21068
21546
  var import_node_path12 = __toESM(require("path"), 1);
21069
- var import_ts_morph84 = require("ts-morph");
21547
+ var import_ts_morph86 = require("ts-morph");
21070
21548
  var SOURCE_SUFFIXES = [
21071
21549
  ".ts",
21072
21550
  ".tsx",
@@ -21088,7 +21566,12 @@ var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
21088
21566
  ".turbo",
21089
21567
  ".next",
21090
21568
  ".suss",
21091
- "vendor"
21569
+ "vendor",
21570
+ "__pycache__",
21571
+ ".venv",
21572
+ "venv",
21573
+ ".tox",
21574
+ "tmp"
21092
21575
  ]);
21093
21576
  function findSourceFiles(rootDir, depth = 0) {
21094
21577
  if (depth > 12) {
@@ -21115,12 +21598,12 @@ function findSourceFiles(rootDir, depth = 0) {
21115
21598
  return found;
21116
21599
  }
21117
21600
  function createProjectWithoutTsconfig(rootDir) {
21118
- const project = new import_ts_morph84.Project({
21601
+ const project = new import_ts_morph86.Project({
21119
21602
  skipAddingFilesFromTsConfig: true,
21120
21603
  compilerOptions: {
21121
- target: import_ts_morph84.ScriptTarget.ES2022,
21122
- module: import_ts_morph84.ModuleKind.ESNext,
21123
- moduleResolution: import_ts_morph84.ModuleResolutionKind.Bundler,
21604
+ target: import_ts_morph86.ScriptTarget.ES2022,
21605
+ module: import_ts_morph86.ModuleKind.ESNext,
21606
+ moduleResolution: import_ts_morph86.ModuleResolutionKind.Bundler,
21124
21607
  allowJs: true,
21125
21608
  checkJs: false,
21126
21609
  noEmit: true,
@@ -21444,13 +21927,13 @@ var raise = (msg) => {
21444
21927
  throw new Error(msg);
21445
21928
  };
21446
21929
  function bindingPatternNames(nameNode) {
21447
- if (import_ts_morph85.Node.isObjectBindingPattern(nameNode)) {
21930
+ if (import_ts_morph87.Node.isObjectBindingPattern(nameNode)) {
21448
21931
  return nameNode.getElements().map((e) => e.getName());
21449
21932
  }
21450
- if (import_ts_morph85.Node.isArrayBindingPattern(nameNode)) {
21933
+ if (import_ts_morph87.Node.isArrayBindingPattern(nameNode)) {
21451
21934
  const names = [];
21452
21935
  for (const element of nameNode.getElements()) {
21453
- if (import_ts_morph85.Node.isOmittedExpression(element)) {
21936
+ if (import_ts_morph87.Node.isOmittedExpression(element)) {
21454
21937
  continue;
21455
21938
  }
21456
21939
  names.push(element.getName());
@@ -21480,7 +21963,7 @@ function objectParamInputs(param, position, mapping) {
21480
21963
  }
21481
21964
  function componentPropsParameters(param, mapping) {
21482
21965
  const nameNode = param.getNameNode();
21483
- if (!import_ts_morph85.Node.isObjectBindingPattern(nameNode)) {
21966
+ if (!import_ts_morph87.Node.isObjectBindingPattern(nameNode)) {
21484
21967
  const type = param.getType();
21485
21968
  return [
21486
21969
  {
@@ -21507,15 +21990,15 @@ function componentPropsParameters(param, mapping) {
21507
21990
  }
21508
21991
  function namesBoundUnder(element, field) {
21509
21992
  const inner = element.getNameNode();
21510
- if (import_ts_morph85.Node.isObjectBindingPattern(inner)) {
21993
+ if (import_ts_morph87.Node.isObjectBindingPattern(inner)) {
21511
21994
  return inner.getElements().flatMap((nested) => {
21512
21995
  const property = nested.getDotDotDotToken() === void 0 ? bindingRole(nested, nested.getName()) : void 0;
21513
21996
  return namesBoundUnder(nested, field ?? property);
21514
21997
  });
21515
21998
  }
21516
- if (import_ts_morph85.Node.isArrayBindingPattern(inner)) {
21999
+ if (import_ts_morph87.Node.isArrayBindingPattern(inner)) {
21517
22000
  return inner.getElements().flatMap(
21518
- (nested) => import_ts_morph85.Node.isBindingElement(nested) ? namesBoundUnder(nested, field) : []
22001
+ (nested) => import_ts_morph87.Node.isBindingElement(nested) ? namesBoundUnder(nested, field) : []
21519
22002
  );
21520
22003
  }
21521
22004
  return [{ binding: element, field }];
@@ -21528,7 +22011,7 @@ function bindingRole(element, name) {
21528
22011
  if (property === void 0) {
21529
22012
  return name;
21530
22013
  }
21531
- if (import_ts_morph85.Node.isStringLiteral(property)) {
22014
+ if (import_ts_morph87.Node.isStringLiteral(property)) {
21532
22015
  return property.getLiteralValue();
21533
22016
  }
21534
22017
  return property.getText();
@@ -21560,7 +22043,7 @@ function extractParameters(func, inputMapping) {
21560
22043
  const param = params[i];
21561
22044
  const nameNode = param.getNameNode();
21562
22045
  const boundNames = bindingPatternNames(nameNode);
21563
- if (import_ts_morph85.Node.isObjectBindingPattern(nameNode)) {
22046
+ if (import_ts_morph87.Node.isObjectBindingPattern(nameNode)) {
21564
22047
  for (const element of nameNode.getElements()) {
21565
22048
  result.push({
21566
22049
  name: element.getName(),
@@ -21627,7 +22110,7 @@ function extractDependencyCalls(func, barriers = NO_BARRIERS) {
21627
22110
  traversal.skip();
21628
22111
  return;
21629
22112
  }
21630
- if (!import_ts_morph85.Node.isVariableDeclaration(node)) {
22113
+ if (!import_ts_morph87.Node.isVariableDeclaration(node)) {
21631
22114
  return;
21632
22115
  }
21633
22116
  const init = node.getInitializer();
@@ -21636,19 +22119,19 @@ function extractDependencyCalls(func, barriers = NO_BARRIERS) {
21636
22119
  }
21637
22120
  let isAsync = false;
21638
22121
  let callExpr = init;
21639
- if (import_ts_morph85.Node.isAwaitExpression(init)) {
22122
+ if (import_ts_morph87.Node.isAwaitExpression(init)) {
21640
22123
  isAsync = true;
21641
22124
  callExpr = init.getExpression();
21642
22125
  }
21643
- if (!import_ts_morph85.Node.isCallExpression(callExpr)) {
22126
+ if (!import_ts_morph87.Node.isCallExpression(callExpr)) {
21644
22127
  return;
21645
22128
  }
21646
22129
  const calleeName = callExpr.getExpression().getText();
21647
22130
  const nameNode = node.getNameNode();
21648
- const assignedTo = import_ts_morph85.Node.isIdentifier(nameNode) ? nameNode.getText() : null;
22131
+ const assignedTo = import_ts_morph87.Node.isIdentifier(nameNode) ? nameNode.getText() : null;
21649
22132
  const declList = node.getParent();
21650
22133
  const varStmt = declList?.getParent();
21651
- const locationNode = varStmt !== void 0 && import_ts_morph85.Node.isVariableStatement(varStmt) ? varStmt : node;
22134
+ const locationNode = varStmt !== void 0 && import_ts_morph87.Node.isVariableStatement(varStmt) ? varStmt : node;
21652
22135
  results.push({
21653
22136
  name: calleeName,
21654
22137
  assignedTo,
@@ -22389,6 +22872,7 @@ function extractFromSourceFile(sourceFile, frameworks, claimedUnits, options, ta
22389
22872
  if (unit.operationInfo.unresolved !== void 0) {
22390
22873
  raw.graphqlUnresolvedDocument = unit.operationInfo.unresolved;
22391
22874
  }
22875
+ stampClientChosenAtCall(raw, unit, pack, resolution);
22392
22876
  for (const v2 of unit.operationInfo.variables) {
22393
22877
  raw.parameters.push({
22394
22878
  name: v2.name,
@@ -22429,17 +22913,18 @@ function extractFromSourceFile(sourceFile, frameworks, claimedUnits, options, ta
22429
22913
  if (binding.semantics.name === "rest" && binding.semantics.method !== null) {
22430
22914
  const defaultBranch = raw.branches.find((b) => b.isDefault);
22431
22915
  if (defaultBranch !== void 0) {
22432
- const calleeText = unit.callSite.callExpression.getExpression().getText();
22916
+ const call = unit.callSite.callExpression;
22433
22917
  defaultBranch.extraEffects = [
22434
22918
  ...defaultBranch.extraEffects ?? [],
22435
22919
  {
22436
22920
  type: "interaction",
22437
22921
  binding,
22438
- callee: calleeText,
22922
+ callee: call.getExpression().getText(),
22439
22923
  interaction: {
22440
22924
  class: "service-call",
22441
22925
  method: binding.semantics.method
22442
- }
22926
+ },
22927
+ location: lineRangeOf(call)
22443
22928
  }
22444
22929
  ];
22445
22930
  }
@@ -22563,6 +23048,23 @@ function stampRequestSpelling(raw, pack) {
22563
23048
  }
22564
23049
  raw.requestSpelling = pack.requestSpelling;
22565
23050
  }
23051
+ function stampClientChosenAtCall(raw, unit, pack, resolution) {
23052
+ const specs = pack.graphqlClients ?? [];
23053
+ if (unit.graphqlCall === void 0 || specs.length === 0) {
23054
+ return;
23055
+ }
23056
+ const chosen = clientChosenAt(unit.graphqlCall, specs, resolution);
23057
+ if (chosen?.kind === "settled") {
23058
+ raw.graphqlClient = { ...chosen.client, chosenAtCall: true };
23059
+ return;
23060
+ }
23061
+ if (chosen?.kind === "unresolved") {
23062
+ raw.graphqlUnresolvedClient = {
23063
+ reference: chosen.reference,
23064
+ reason: chosen.reason
23065
+ };
23066
+ }
23067
+ }
22566
23068
  function unfollowedStopsOf(unit, wrappers) {
22567
23069
  const stops = [
22568
23070
  ...unit.unfollowed === void 0 ? [] : [unit.unfollowed],
@@ -22580,7 +23082,7 @@ function emitLibraryEnvReadMarkers(summaries, project, frameworks) {
22580
23082
  if (importer === void 0) {
22581
23083
  continue;
22582
23084
  }
22583
- summaries.push(libraryEnvReadMarker(pack, declared, importer));
23085
+ summaries.push((0, import_extractor20.libraryEnvReadMarker)(pack.name, declared, importer));
22584
23086
  }
22585
23087
  }
22586
23088
  }
@@ -22628,31 +23130,6 @@ function schemaDocumentSummary(label, schemaSdl) {
22628
23130
  )
22629
23131
  };
22630
23132
  }
22631
- function libraryEnvReadMarker(pack, declared, importer) {
22632
- return {
22633
- kind: "library",
22634
- location: { file: importer, range: { start: 0, end: 0 }, exportName: null },
22635
- identity: {
22636
- name: `${declared.module} env reads`,
22637
- exportPath: null,
22638
- boundaryBinding: (0, import_behavioral_ir16.functionCallBinding)({
22639
- transport: "in-process",
22640
- recognition: pack.name
22641
- })
22642
- },
22643
- inputs: [],
22644
- transitions: [],
22645
- gaps: [],
22646
- confidence: { source: "declared", level: "high" },
22647
- metadata: {
22648
- libraryEnvReads: {
22649
- module: declared.module,
22650
- ...declared.prefixes !== void 0 ? { prefixes: declared.prefixes } : {},
22651
- ...declared.names !== void 0 ? { names: declared.names } : {}
22652
- }
22653
- }
22654
- };
22655
- }
22656
23133
  function internalImportsOf(project, file) {
22657
23134
  const sourceFile = project.getSourceFile(file);
22658
23135
  if (sourceFile === void 0) {
@@ -22748,12 +23225,12 @@ function sinkIn(func, pack, summary, discovered, resolution) {
22748
23225
  }
22749
23226
  function sinkReading(sink, bindings, resolution) {
22750
23227
  const url = sinkUrl(sink, bindings, resolution);
22751
- const path17 = url === void 0 ? void 0 : statedPath((0, import_values14.pathOf)(url));
23228
+ const path17 = url === void 0 ? void 0 : statedPath((0, import_values15.pathOf)(url));
22752
23229
  if (url === void 0 || path17 === void 0) {
22753
23230
  return null;
22754
23231
  }
22755
23232
  const method = sinkMethod(sink, bindings, resolution);
22756
- const host = (0, import_values14.hostOf)(url);
23233
+ const host = (0, import_values15.hostOf)(url);
22757
23234
  return method === void 0 ? null : { path: path17, method, ...host === void 0 ? {} : { host } };
22758
23235
  }
22759
23236
  function sinkArgumentValue(sink, position, bindings, resolution) {
@@ -22765,7 +23242,7 @@ function fieldValue2(record2, name) {
22765
23242
  return void 0;
22766
23243
  }
22767
23244
  const field = record2.fields.get(name);
22768
- return field === void 0 ? void 0 : (0, import_values14.force)(field.value);
23245
+ return field === void 0 ? void 0 : (0, import_values15.force)(field.value);
22769
23246
  }
22770
23247
  function sinkUrl(sink, bindings, resolution) {
22771
23248
  const p = sink.extraction.path;
@@ -22797,7 +23274,7 @@ function sinkMethod(sink, bindings, resolution) {
22797
23274
  if (value === void 0) {
22798
23275
  return record2.open ? void 0 : m.default;
22799
23276
  }
22800
- return (0, import_values14.literalOf)(value) ?? void 0;
23277
+ return (0, import_values15.literalOf)(value) ?? void 0;
22801
23278
  }
22802
23279
  function statedPath(path17) {
22803
23280
  if (path17 === void 0) {
@@ -22825,17 +23302,17 @@ function synthesizeCallerSummaries(wrapper, resolution, options) {
22825
23302
  return out;
22826
23303
  }
22827
23304
  function callerName(func) {
22828
- if (import_ts_morph85.Node.isFunctionDeclaration(func) || import_ts_morph85.Node.isMethodDeclaration(func)) {
23305
+ if (import_ts_morph87.Node.isFunctionDeclaration(func) || import_ts_morph87.Node.isMethodDeclaration(func)) {
22829
23306
  return func.getName() ?? "anonymous";
22830
23307
  }
22831
23308
  const parent = func.getParent();
22832
- if (parent !== void 0 && import_ts_morph85.Node.isVariableDeclaration(parent)) {
23309
+ if (parent !== void 0 && import_ts_morph87.Node.isVariableDeclaration(parent)) {
22833
23310
  const nameNode = parent.getNameNode();
22834
- if (import_ts_morph85.Node.isIdentifier(nameNode)) {
23311
+ if (import_ts_morph87.Node.isIdentifier(nameNode)) {
22835
23312
  return nameNode.getText();
22836
23313
  }
22837
23314
  }
22838
- if (parent !== void 0 && import_ts_morph85.Node.isPropertyAssignment(parent)) {
23315
+ if (parent !== void 0 && import_ts_morph87.Node.isPropertyAssignment(parent)) {
22839
23316
  return parent.getName();
22840
23317
  }
22841
23318
  return "anonymous";
@@ -22958,7 +23435,7 @@ function createTypeScriptAdapter(suppliedConfig) {
22958
23435
  ]
22959
23436
  };
22960
23437
  const newProject = () => {
22961
- const made = new import_ts_morph85.Project(
23438
+ const made = new import_ts_morph87.Project(
22962
23439
  config.tsConfigFilePath !== void 0 ? {
22963
23440
  tsConfigFilePath: config.tsConfigFilePath,
22964
23441
  skipAddingFilesFromTsConfig: true
@@ -23875,8 +24352,8 @@ function buildSubUnitSummary(subUnit, parent, parentFunc, recognizers, everyUnit
23875
24352
 
23876
24353
  // src/facts/why.ts
23877
24354
  var import_node_path16 = __toESM(require("path"), 1);
23878
- var import_ts_morph86 = require("ts-morph");
23879
- var import_resolution10 = require("@suss/resolution");
24355
+ var import_ts_morph88 = require("ts-morph");
24356
+ var import_resolution12 = require("@suss/resolution");
23880
24357
  var TypeScriptWhySession = class {
23881
24358
  project;
23882
24359
  store;
@@ -23884,7 +24361,7 @@ var TypeScriptWhySession = class {
23884
24361
  constructor(options) {
23885
24362
  this.root = import_node_path16.default.resolve(options.dir);
23886
24363
  const tsconfig = findNearestTsconfig(this.root);
23887
- this.project = tsconfig === null ? createProjectWithoutTsconfig(this.root).project : new import_ts_morph86.Project({ tsConfigFilePath: tsconfig });
24364
+ this.project = tsconfig === null ? createProjectWithoutTsconfig(this.root).project : new import_ts_morph88.Project({ tsConfigFilePath: tsconfig });
23888
24365
  this.store = ResolutionStore.forPacks(options.packs ?? []);
23889
24366
  }
23890
24367
  /**
@@ -23892,14 +24369,14 @@ var TypeScriptWhySession = class {
23892
24369
  * `text`, or null. Spelled from the person's side: the name as
23893
24370
  * written, not a node id.
23894
24371
  */
23895
- findExpression(file, line, text2) {
24372
+ findExpression(file, line, text3) {
23896
24373
  const sourceFile = this.sourceFileAt(file);
23897
24374
  if (sourceFile === null) {
23898
24375
  return null;
23899
24376
  }
23900
24377
  let found = null;
23901
24378
  sourceFile.forEachDescendant((node) => {
23902
- if (import_ts_morph86.Node.isExpression(node) && node.getStartLineNumber() === line && node.getText() === text2 && (found === null || node.getWidth() <= found.getWidth())) {
24379
+ if (import_ts_morph88.Node.isExpression(node) && node.getStartLineNumber() === line && node.getText() === text3 && (found === null || node.getWidth() <= found.getWidth())) {
23903
24380
  found = node;
23904
24381
  }
23905
24382
  });
@@ -23917,7 +24394,7 @@ var TypeScriptWhySession = class {
23917
24394
  return null;
23918
24395
  }
23919
24396
  for (const call of sourceFile.getDescendantsOfKind(
23920
- import_ts_morph86.SyntaxKind.CallExpression
24397
+ import_ts_morph88.SyntaxKind.CallExpression
23921
24398
  )) {
23922
24399
  const line = call.getStartLineNumber();
23923
24400
  if (line < startLine || line > endLine) {
@@ -23947,14 +24424,14 @@ var TypeScriptWhySession = class {
23947
24424
  const at = this.locate(node);
23948
24425
  return `${at.name} (${at.file}:${at.line})`;
23949
24426
  };
23950
- const explanation = (0, import_resolution10.explainResolutionProof)(explained.proof, { describe });
24427
+ const explanation = (0, import_resolution12.explainResolutionProof)(explained.proof, { describe });
23951
24428
  if (explanation === null) {
23952
24429
  return null;
23953
24430
  }
23954
24431
  return {
23955
24432
  explanation,
23956
24433
  chain: explanation.atoms.map((atom) => describe(atom)),
23957
- lines: (0, import_resolution10.renderExplanation)(explanation, describe),
24434
+ lines: (0, import_resolution12.renderExplanation)(explanation, describe),
23958
24435
  target: this.locate(explained.target),
23959
24436
  stats: explained.stats
23960
24437
  };
@@ -23988,31 +24465,31 @@ var TypeScriptWhySession = class {
23988
24465
  }
23989
24466
  };
23990
24467
  function displayNameOf(node) {
23991
- if (import_ts_morph86.Node.isIdentifier(node) || import_ts_morph86.Node.isPropertyAccessExpression(node)) {
24468
+ if (import_ts_morph88.Node.isIdentifier(node) || import_ts_morph88.Node.isPropertyAccessExpression(node)) {
23992
24469
  return node.getText();
23993
24470
  }
23994
- if (import_ts_morph86.Node.isCallExpression(node) || import_ts_morph86.Node.isNewExpression(node)) {
24471
+ if (import_ts_morph88.Node.isCallExpression(node) || import_ts_morph88.Node.isNewExpression(node)) {
23995
24472
  return `${node.getExpression()?.getText() ?? "call"}(...)`;
23996
24473
  }
23997
- if (import_ts_morph86.Node.isFunctionDeclaration(node) || import_ts_morph86.Node.isClassDeclaration(node) || import_ts_morph86.Node.isMethodDeclaration(node) || import_ts_morph86.Node.isVariableDeclaration(node)) {
24474
+ if (import_ts_morph88.Node.isFunctionDeclaration(node) || import_ts_morph88.Node.isClassDeclaration(node) || import_ts_morph88.Node.isMethodDeclaration(node) || import_ts_morph88.Node.isVariableDeclaration(node)) {
23998
24475
  const name = node.getName();
23999
24476
  if (name !== void 0) {
24000
24477
  return name;
24001
24478
  }
24002
24479
  }
24003
- if (import_ts_morph86.Node.isArrowFunction(node) || import_ts_morph86.Node.isFunctionExpression(node)) {
24480
+ if (import_ts_morph88.Node.isArrowFunction(node) || import_ts_morph88.Node.isFunctionExpression(node)) {
24004
24481
  const parent = node.getParent();
24005
- if (parent !== void 0 && import_ts_morph86.Node.isVariableDeclaration(parent)) {
24482
+ if (parent !== void 0 && import_ts_morph88.Node.isVariableDeclaration(parent)) {
24006
24483
  return parent.getName();
24007
24484
  }
24008
24485
  }
24009
- const text2 = node.getText().split("\n")[0].trim();
24010
- return text2.length > 40 ? `${text2.slice(0, 40)}...` : text2;
24486
+ const text3 = node.getText().split("\n")[0].trim();
24487
+ return text3.length > 40 ? `${text3.slice(0, 40)}...` : text3;
24011
24488
  }
24012
24489
 
24013
24490
  // src/stubEvidence.ts
24014
24491
  var import_node_path17 = __toESM(require("path"), 1);
24015
- var import_ts_morph87 = require("ts-morph");
24492
+ var import_ts_morph89 = require("ts-morph");
24016
24493
  function stubEvidenceIn(project, packageName, root) {
24017
24494
  const resolution = new ResolutionStore();
24018
24495
  const byExport = /* @__PURE__ */ new Map();
@@ -24039,7 +24516,7 @@ function stubEvidenceIn(project, packageName, root) {
24039
24516
  );
24040
24517
  }
24041
24518
  function stubEvidence(options) {
24042
- const project = new import_ts_morph87.Project(
24519
+ const project = new import_ts_morph89.Project(
24043
24520
  options.tsConfigFilePath !== void 0 ? { tsConfigFilePath: options.tsConfigFilePath } : { compilerOptions: { allowJs: true } }
24044
24521
  );
24045
24522
  const root = options.tsConfigFilePath !== void 0 ? import_node_path17.default.dirname(options.tsConfigFilePath) : import_node_path17.default.resolve(options.directory ?? process.cwd());
@@ -24053,9 +24530,9 @@ function stubEvidence(options) {
24053
24530
  }
24054
24531
 
24055
24532
  // src/walk/defaulted.ts
24056
- var import_ts_morph88 = require("ts-morph");
24533
+ var import_ts_morph90 = require("ts-morph");
24057
24534
  function isDefaultedAt(node) {
24058
- if (!import_ts_morph88.Node.isBindingElement(node)) {
24535
+ if (!import_ts_morph90.Node.isBindingElement(node)) {
24059
24536
  const read2 = climbSyntax(node);
24060
24537
  const inner = peelSyntax(read2);
24061
24538
  const subject = {
@@ -24072,7 +24549,7 @@ function isDefaultedAt(node) {
24072
24549
  }
24073
24550
  function isSubject(subject, expression) {
24074
24551
  if (subject.kind === "local") {
24075
- return import_ts_morph88.Node.isIdentifier(expression) && refersTo(expression, subject.local);
24552
+ return import_ts_morph90.Node.isIdentifier(expression) && refersTo(expression, subject.local);
24076
24553
  }
24077
24554
  return expression.getText() === subject.text;
24078
24555
  }
@@ -24086,7 +24563,7 @@ function hasFallbackOperand(node) {
24086
24563
  let child = climbSyntax(node);
24087
24564
  let parent = child.getParent();
24088
24565
  while (parent !== void 0) {
24089
- if (!import_ts_morph88.Node.isBinaryExpression(parent)) {
24566
+ if (!import_ts_morph90.Node.isBinaryExpression(parent)) {
24090
24567
  return false;
24091
24568
  }
24092
24569
  const operator = parent.getOperatorToken().getText();
@@ -24107,8 +24584,8 @@ function isPresenceTest(node, subject) {
24107
24584
  let throughLogical = false;
24108
24585
  let parent = child.getParent();
24109
24586
  while (parent !== void 0) {
24110
- if (!import_ts_morph88.Node.isBinaryExpression(parent)) {
24111
- if (negatedOperand(parent) === null && !import_ts_morph88.Node.isTypeOfExpression(parent)) {
24587
+ if (!import_ts_morph90.Node.isBinaryExpression(parent)) {
24588
+ if (negatedOperand(parent) === null && !import_ts_morph90.Node.isTypeOfExpression(parent)) {
24112
24589
  return isTestedAt(parent, child, subject, { tested, throughLogical });
24113
24590
  }
24114
24591
  tested = true;
@@ -24134,22 +24611,22 @@ function logicalStep(parent, child) {
24134
24611
  return null;
24135
24612
  }
24136
24613
  const operand2 = peelSyntax(child);
24137
- return import_ts_morph88.Node.isTypeOfExpression(operand2) || missingSideOf(parent) === operand2 ? "tested" : null;
24614
+ return import_ts_morph90.Node.isTypeOfExpression(operand2) || missingSideOf(parent) === operand2 ? "tested" : null;
24138
24615
  }
24139
24616
  function isTestedAt(parent, child, subject, seen) {
24140
24617
  if (conditionOf2(parent) === child) {
24141
24618
  return !absentPathThrows(parent, subject);
24142
24619
  }
24143
- if (seen.throughLogical && import_ts_morph88.Node.isExpressionStatement(parent)) {
24620
+ if (seen.throughLogical && import_ts_morph90.Node.isExpressionStatement(parent)) {
24144
24621
  return !continuationThrows(parent);
24145
24622
  }
24146
24623
  return seen.tested;
24147
24624
  }
24148
24625
  function conditionOf2(node) {
24149
- if (import_ts_morph88.Node.isIfStatement(node) || import_ts_morph88.Node.isWhileStatement(node) || import_ts_morph88.Node.isDoStatement(node)) {
24626
+ if (import_ts_morph90.Node.isIfStatement(node) || import_ts_morph90.Node.isWhileStatement(node) || import_ts_morph90.Node.isDoStatement(node)) {
24150
24627
  return node.getExpression();
24151
24628
  }
24152
- if (import_ts_morph88.Node.isConditionalExpression(node) || import_ts_morph88.Node.isForStatement(node)) {
24629
+ if (import_ts_morph90.Node.isConditionalExpression(node) || import_ts_morph90.Node.isForStatement(node)) {
24153
24630
  return node.getCondition();
24154
24631
  }
24155
24632
  return void 0;
@@ -24167,24 +24644,24 @@ function isPresentAt(node, subject) {
24167
24644
  return false;
24168
24645
  }
24169
24646
  function isBranchWherePresent(parent, child, subject) {
24170
- if (import_ts_morph88.Node.isIfStatement(parent)) {
24647
+ if (import_ts_morph90.Node.isIfStatement(parent)) {
24171
24648
  return isBranchTaken(parent.getExpression(), child, subject, {
24172
24649
  whenTrue: parent.getThenStatement(),
24173
24650
  whenFalse: parent.getElseStatement()
24174
24651
  });
24175
24652
  }
24176
- if (import_ts_morph88.Node.isConditionalExpression(parent)) {
24653
+ if (import_ts_morph90.Node.isConditionalExpression(parent)) {
24177
24654
  return isBranchTaken(parent.getCondition(), child, subject, {
24178
24655
  whenTrue: parent.getWhenTrue(),
24179
24656
  whenFalse: parent.getWhenFalse()
24180
24657
  });
24181
24658
  }
24182
- if (import_ts_morph88.Node.isWhileStatement(parent)) {
24659
+ if (import_ts_morph90.Node.isWhileStatement(parent)) {
24183
24660
  return isBranchTaken(parent.getExpression(), child, subject, {
24184
24661
  whenTrue: parent.getStatement()
24185
24662
  });
24186
24663
  }
24187
- if (!import_ts_morph88.Node.isBinaryExpression(parent) || parent.getRight() !== child) {
24664
+ if (!import_ts_morph90.Node.isBinaryExpression(parent) || parent.getRight() !== child) {
24188
24665
  return false;
24189
24666
  }
24190
24667
  const operator = parent.getOperatorToken().getText();
@@ -24197,16 +24674,16 @@ function isBranchTaken(test, child, subject, branches) {
24197
24674
  return child === branches.whenFalse && isPresentWhen(test, false, subject);
24198
24675
  }
24199
24676
  function followsExitWhenAbsent(parent, child, subject) {
24200
- if (!(import_ts_morph88.Node.isBlock(parent) || import_ts_morph88.Node.isSourceFile(parent))) {
24677
+ if (!(import_ts_morph90.Node.isBlock(parent) || import_ts_morph90.Node.isSourceFile(parent))) {
24201
24678
  return false;
24202
24679
  }
24203
24680
  const statements = parent.getStatements();
24204
24681
  return statements.slice(0, statements.indexOf(child)).some(
24205
- (statement) => import_ts_morph88.Node.isIfStatement(statement) && exitOf(statement.getThenStatement()) === "leave" && isPresentWhen(statement.getExpression(), false, subject)
24682
+ (statement) => import_ts_morph90.Node.isIfStatement(statement) && exitOf(statement.getThenStatement()) === "leave" && isPresentWhen(statement.getExpression(), false, subject)
24206
24683
  );
24207
24684
  }
24208
24685
  function absentPathThrows(owner, subject) {
24209
- if (!import_ts_morph88.Node.isIfStatement(owner)) {
24686
+ if (!import_ts_morph90.Node.isIfStatement(owner)) {
24210
24687
  return continuationThrows(owner);
24211
24688
  }
24212
24689
  const test = owner.getExpression();
@@ -24220,7 +24697,7 @@ function absentPathThrows(owner, subject) {
24220
24697
  return arms.some((arm) => armThrows(arm, owner, subject));
24221
24698
  }
24222
24699
  function armThrows(arm, owner, subject) {
24223
- if (arm !== void 0 && import_ts_morph88.Node.isIfStatement(arm)) {
24700
+ if (arm !== void 0 && import_ts_morph90.Node.isIfStatement(arm)) {
24224
24701
  return absentPathThrows(arm, subject);
24225
24702
  }
24226
24703
  const exit = arm === void 0 ? null : exitOf(arm);
@@ -24230,7 +24707,7 @@ function continuationThrows(from) {
24230
24707
  let current2 = from;
24231
24708
  let parent = current2.getParent();
24232
24709
  while (parent !== void 0) {
24233
- if (import_ts_morph88.Node.isBlock(parent) || import_ts_morph88.Node.isSourceFile(parent)) {
24710
+ if (import_ts_morph90.Node.isBlock(parent) || import_ts_morph90.Node.isSourceFile(parent)) {
24234
24711
  const statements = parent.getStatements();
24235
24712
  const after = statements.slice(
24236
24713
  statements.indexOf(current2) + 1
@@ -24249,7 +24726,7 @@ function continuationThrows(from) {
24249
24726
  return false;
24250
24727
  }
24251
24728
  function endsThePath(node) {
24252
- return isFunctionRoot(node) || import_ts_morph88.Node.isIterationStatement(node) || import_ts_morph88.Node.isReturnStatement(node);
24729
+ return isFunctionRoot(node) || import_ts_morph90.Node.isIterationStatement(node) || import_ts_morph90.Node.isReturnStatement(node);
24253
24730
  }
24254
24731
  function firstExit(statements) {
24255
24732
  for (const statement of statements) {
@@ -24261,13 +24738,13 @@ function firstExit(statements) {
24261
24738
  return null;
24262
24739
  }
24263
24740
  function exitOf(statement) {
24264
- if (import_ts_morph88.Node.isThrowStatement(statement)) {
24741
+ if (import_ts_morph90.Node.isThrowStatement(statement)) {
24265
24742
  return "throw";
24266
24743
  }
24267
- if (import_ts_morph88.Node.isReturnStatement(statement) || import_ts_morph88.Node.isContinueStatement(statement) || import_ts_morph88.Node.isBreakStatement(statement)) {
24744
+ if (import_ts_morph90.Node.isReturnStatement(statement) || import_ts_morph90.Node.isContinueStatement(statement) || import_ts_morph90.Node.isBreakStatement(statement)) {
24268
24745
  return "leave";
24269
24746
  }
24270
- return import_ts_morph88.Node.isBlock(statement) ? firstExit(statement.getStatements()) : null;
24747
+ return import_ts_morph90.Node.isBlock(statement) ? firstExit(statement.getStatements()) : null;
24271
24748
  }
24272
24749
  function isPresentWhen(test, outcome, subject) {
24273
24750
  const expression = peelSyntax(test);
@@ -24278,7 +24755,7 @@ function isPresentWhen(test, outcome, subject) {
24278
24755
  if (negated2 !== null) {
24279
24756
  return isPresentWhen(negated2, !outcome, subject);
24280
24757
  }
24281
- if (!import_ts_morph88.Node.isBinaryExpression(expression)) {
24758
+ if (!import_ts_morph90.Node.isBinaryExpression(expression)) {
24282
24759
  return false;
24283
24760
  }
24284
24761
  const operator = expression.getOperatorToken().getText();
@@ -24309,24 +24786,24 @@ function missingSideOf(comparison) {
24309
24786
  return comparedValue(left, right, loose) ?? comparedValue(right, left, loose);
24310
24787
  }
24311
24788
  function comparedValue(operand2, other, loose) {
24312
- if (import_ts_morph88.Node.isTypeOfExpression(operand2)) {
24313
- return import_ts_morph88.Node.isStringLiteral(other) && other.getLiteralValue() === "undefined" ? peelSyntax(operand2.getExpression()) : null;
24789
+ if (import_ts_morph90.Node.isTypeOfExpression(operand2)) {
24790
+ return import_ts_morph90.Node.isStringLiteral(other) && other.getLiteralValue() === "undefined" ? peelSyntax(operand2.getExpression()) : null;
24314
24791
  }
24315
- const missing = import_ts_morph88.Node.isIdentifier(other) && other.getText() === "undefined" || loose && import_ts_morph88.Node.isNullLiteral(other);
24792
+ const missing = import_ts_morph90.Node.isIdentifier(other) && other.getText() === "undefined" || loose && import_ts_morph90.Node.isNullLiteral(other);
24316
24793
  return missing ? operand2 : null;
24317
24794
  }
24318
24795
  function negatedOperand(node) {
24319
- if (import_ts_morph88.Node.isPrefixUnaryExpression(node) && node.getOperatorToken() === import_ts_morph88.SyntaxKind.ExclamationToken) {
24796
+ if (import_ts_morph90.Node.isPrefixUnaryExpression(node) && node.getOperatorToken() === import_ts_morph90.SyntaxKind.ExclamationToken) {
24320
24797
  return node.getOperand();
24321
24798
  }
24322
24799
  return null;
24323
24800
  }
24324
24801
  function isKeyOfRead(read2, key, container) {
24325
- return (import_ts_morph88.Node.isPropertyAccessExpression(read2) || import_ts_morph88.Node.isElementAccessExpression(read2)) && read2.getExpression().getText() === container.getText() && isKeyReadBy(read2, key);
24802
+ return (import_ts_morph90.Node.isPropertyAccessExpression(read2) || import_ts_morph90.Node.isElementAccessExpression(read2)) && read2.getExpression().getText() === container.getText() && isKeyReadBy(read2, key);
24326
24803
  }
24327
24804
  function isKeyReadBy(read2, key) {
24328
- if (import_ts_morph88.Node.isPropertyAccessExpression(read2)) {
24329
- return import_ts_morph88.Node.isStringLiteral(key) && key.getLiteralValue() === read2.getName();
24805
+ if (import_ts_morph90.Node.isPropertyAccessExpression(read2)) {
24806
+ return import_ts_morph90.Node.isStringLiteral(key) && key.getLiteralValue() === read2.getName();
24330
24807
  }
24331
24808
  return read2.getArgumentExpression()?.getText() === key.getText();
24332
24809
  }
@@ -24340,25 +24817,25 @@ function localNamedBy(node) {
24340
24817
  }
24341
24818
  const name = declaration.getNameNode();
24342
24819
  const scope = findEnclosingFunction(declaration);
24343
- if (!import_ts_morph88.Node.isIdentifier(name) || scope === null) {
24820
+ if (!import_ts_morph90.Node.isIdentifier(name) || scope === null) {
24344
24821
  return null;
24345
24822
  }
24346
24823
  const symbol = symbolBehind(name);
24347
24824
  return symbol === void 0 ? null : { name, symbol, scope };
24348
24825
  }
24349
24826
  function declarationInitializedBy(node) {
24350
- if (import_ts_morph88.Node.isBindingElement(node)) {
24827
+ if (import_ts_morph90.Node.isBindingElement(node)) {
24351
24828
  return node;
24352
24829
  }
24353
24830
  const read2 = climbSyntax(node);
24354
24831
  const parent = read2.getParent();
24355
- if (parent !== void 0 && import_ts_morph88.Node.isVariableDeclaration(parent) && parent.getInitializer() === read2) {
24832
+ if (parent !== void 0 && import_ts_morph90.Node.isVariableDeclaration(parent) && parent.getInitializer() === read2) {
24356
24833
  return parent;
24357
24834
  }
24358
24835
  return null;
24359
24836
  }
24360
24837
  function usesOf(local) {
24361
- return local.scope.getDescendantsOfKind(import_ts_morph88.SyntaxKind.Identifier).filter(
24838
+ return local.scope.getDescendantsOfKind(import_ts_morph90.SyntaxKind.Identifier).filter(
24362
24839
  (identifier) => identifier !== local.name && refersTo(identifier, local)
24363
24840
  );
24364
24841
  }
@@ -24368,6 +24845,7 @@ function usesOf(local) {
24368
24845
  GENERIC_TERMINALS,
24369
24846
  NO_BARRIERS,
24370
24847
  ResolutionStore,
24848
+ SKIP_DIRECTORIES,
24371
24849
  SOURCE_SUFFIXES,
24372
24850
  TSCONFIG_NAMES,
24373
24851
  TypeScriptWhySession,
@@ -24387,6 +24865,7 @@ function usesOf(local) {
24387
24865
  createTypeScriptAdapter,
24388
24866
  declarationsBehind,
24389
24867
  discoverUnits,
24868
+ dottedPathOf,
24390
24869
  exportedDeclarationsOf,
24391
24870
  extractCodeStructure,
24392
24871
  extractRawBranches,