@suss/adapter-typescript 0.35.1 → 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
@@ -54,6 +54,7 @@ __export(index_exports, {
54
54
  createTypeScriptAdapter: () => createTypeScriptAdapter,
55
55
  declarationsBehind: () => declarationsBehind,
56
56
  discoverUnits: () => discoverUnits,
57
+ dottedPathOf: () => dottedPathOf,
57
58
  exportedDeclarationsOf: () => exportedDeclarationsOf,
58
59
  extractCodeStructure: () => extractCodeStructure,
59
60
  extractRawBranches: () => extractRawBranches,
@@ -110,11 +111,11 @@ module.exports = __toCommonJS(index_exports);
110
111
 
111
112
  // src/adapter.ts
112
113
  var import_node_path15 = __toESM(require("path"), 1);
113
- var import_ts_morph85 = require("ts-morph");
114
+ var import_ts_morph87 = require("ts-morph");
114
115
  var import_behavioral_ir16 = require("@suss/behavioral-ir");
115
116
  var import_datalog5 = require("@suss/datalog");
116
117
  var import_extractor20 = require("@suss/extractor");
117
- var import_values14 = require("@suss/values");
118
+ var import_values15 = require("@suss/values");
118
119
 
119
120
  // src/assembly.ts
120
121
  var import_ts_morph69 = require("ts-morph");
@@ -1150,11 +1151,11 @@ function settledDepthsFor(project) {
1150
1151
  }
1151
1152
  var depthsByProject = /* @__PURE__ */ new WeakMap();
1152
1153
  function readSpecifierPaths(project, filePath, options, host) {
1153
- const text2 = readFileText(project, filePath);
1154
- if (text2 === null) {
1154
+ const text3 = readFileText(project, filePath);
1155
+ if (text3 === null) {
1155
1156
  return [];
1156
1157
  }
1157
- const scanned = import_ts_morph4.ts.preProcessFile(text2, true, true);
1158
+ const scanned = import_ts_morph4.ts.preProcessFile(text3, true, true);
1158
1159
  const cache = resolutionCacheFor(project, options);
1159
1160
  const paths = [];
1160
1161
  for (const imported of scanned.importedFiles) {
@@ -1326,12 +1327,12 @@ async function readImportsConcurrently(paths, marks) {
1326
1327
  }
1327
1328
  const p = paths[i];
1328
1329
  try {
1329
- const text2 = await import_promises.default.readFile(p, "utf-8");
1330
- 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);
1331
1332
  results[i] = {
1332
1333
  path: p,
1333
1334
  importedModules: pre.importedFiles.map((f) => f.fileName),
1334
- marked: marks.some((mark) => text2.includes(mark))
1335
+ marked: marks.some((mark) => text3.includes(mark))
1335
1336
  };
1336
1337
  } catch {
1337
1338
  results[i] = { path: p, importedModules: [], marked: false };
@@ -2324,8 +2325,8 @@ function spellingsHandedOn(paths, sourceFile) {
2324
2325
  if (paths.length === 0) {
2325
2326
  return [];
2326
2327
  }
2327
- const text2 = sourceFile.getFullText();
2328
- if (!paths.some((path17) => text2.includes(path17))) {
2328
+ const text3 = sourceFile.getFullText();
2329
+ if (!paths.some((path17) => text3.includes(path17))) {
2329
2330
  return [];
2330
2331
  }
2331
2332
  return sourceFile.getDescendantsOfKind(import_ts_morph8.SyntaxKind.PropertyAccessExpression).filter((node) => spellsOneOf(paths, node) && flowsOnward(node));
@@ -2344,6 +2345,10 @@ function dottedPathOf(expression) {
2344
2345
  if (import_ts_morph8.Node.isIdentifier(expression)) {
2345
2346
  return expression.getText();
2346
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
+ }
2347
2352
  if (!import_ts_morph8.Node.isPropertyAccessExpression(expression)) {
2348
2353
  return null;
2349
2354
  }
@@ -4376,8 +4381,8 @@ var ResolutionStore = class _ResolutionStore {
4376
4381
  if (one.isInNodeModules()) {
4377
4382
  return false;
4378
4383
  }
4379
- const text2 = one.getFullText();
4380
- return spellings.some((spelling) => text2.includes(spelling));
4384
+ const text3 = one.getFullText();
4385
+ return spellings.some((spelling) => text3.includes(spelling));
4381
4386
  });
4382
4387
  if (siteFiles.length === 0) {
4383
4388
  return [];
@@ -5772,10 +5777,13 @@ function parametersOf(parameter, position) {
5772
5777
  }
5773
5778
  ];
5774
5779
  }
5775
- if (!import_ts_morph13.Node.isObjectBindingPattern(nameNode) || parameter.getInitializer() !== void 0) {
5780
+ if (!import_ts_morph13.Node.isObjectBindingPattern(nameNode)) {
5776
5781
  return [];
5777
5782
  }
5778
- return destructuredNames(nameNode, position, []);
5783
+ const patternDefault = parameter.getInitializer();
5784
+ return destructuredNames(nameNode, position, []).map(
5785
+ (entry) => patternDefault === void 0 ? entry : { ...entry, patternDefault }
5786
+ );
5779
5787
  }
5780
5788
  function destructuredNames(pattern, position, prefix) {
5781
5789
  return pattern.getElements().flatMap((element) => {
@@ -6064,6 +6072,18 @@ function evaluatedValue(node, resolution, site) {
6064
6072
  function evaluatedValueUnder(node, resolution, bindings) {
6065
6073
  return bindings.size === 0 ? evaluatedValue(node, resolution) : (0, import_values3.force)(evaluatorFor(node, resolution).evaluate(node, { bindings }));
6066
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
+ }
6067
6087
 
6068
6088
  // src/resolve/routePath.ts
6069
6089
  function pathFromArgument(arg, resolution, site) {
@@ -7479,8 +7499,8 @@ function declaredChannelOf(source, decorator, resolution) {
7479
7499
  if (arg === void 0) {
7480
7500
  return null;
7481
7501
  }
7482
- const text2 = stringValueOf(arg, resolution);
7483
- return text2 === null || text2 === "" ? null : text2;
7502
+ const text3 = stringValueOf(arg, resolution);
7503
+ return text3 === null || text3 === "" ? null : text3;
7484
7504
  }
7485
7505
  function statusCodeDecoratedOn(routeDecorator, statusDecorators, constants, resolution) {
7486
7506
  const member = routeDecorator.getParent();
@@ -7930,28 +7950,28 @@ function assembledTemplateText(template, assembly) {
7930
7950
  if (!import_ts_morph25.Node.isTemplateExpression(template)) {
7931
7951
  return null;
7932
7952
  }
7933
- let text2 = template.getHead().getLiteralText();
7953
+ let text3 = template.getHead().getLiteralText();
7934
7954
  for (const span of template.getTemplateSpans()) {
7935
7955
  const spliced = interpolatedDocumentText(span.getExpression(), assembly);
7936
7956
  if (spliced === null) {
7937
7957
  assembly.unresolvedInterpolations.push(
7938
7958
  singleLine(span.getExpression().getText())
7939
7959
  );
7940
- if (openBraceDepth(text2) > 0) {
7960
+ if (openBraceDepth(text3) > 0) {
7941
7961
  assembly.unresolvedInsideSelection = true;
7942
7962
  }
7943
7963
  } else {
7944
- text2 += `
7964
+ text3 += `
7945
7965
  ${spliced}
7946
7966
  `;
7947
7967
  }
7948
- text2 += span.getLiteral().getLiteralText();
7968
+ text3 += span.getLiteral().getLiteralText();
7949
7969
  }
7950
- return text2;
7970
+ return text3;
7951
7971
  }
7952
- function openBraceDepth(text2) {
7972
+ function openBraceDepth(text3) {
7953
7973
  let depth = 0;
7954
- for (const char of text2) {
7974
+ for (const char of text3) {
7955
7975
  if (char === "{") {
7956
7976
  depth += 1;
7957
7977
  } else if (char === "}") {
@@ -7983,16 +8003,16 @@ function interpolatedDocumentText(expr, assembly) {
7983
8003
  function resolveGraphqlDocument(arg, resolution) {
7984
8004
  const assembly = startAssembly(resolution);
7985
8005
  const stripped = peelSyntax(arg);
7986
- const text2 = documentTextFromExpression(stripped, assembly) ?? resolveGqlTemplateText(stripped, assembly) ?? resolveTypedDocumentSource(stripped) ?? resolveThroughFacts(stripped, assembly);
7987
- if (text2 !== null) {
7988
- 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);
7989
8009
  }
7990
8010
  return resolveTypedDocumentHeader(stripped);
7991
8011
  }
7992
- function assembledDocumentResolution(text2, arg, assembly) {
8012
+ function assembledDocumentResolution(text3, arg, assembly) {
7993
8013
  const interpolations = assembly.unresolvedInterpolations;
7994
8014
  if (assembly.unresolvedInsideSelection) {
7995
- const header = parseGraphqlOperation(text2);
8015
+ const header = parseGraphqlOperation(text3);
7996
8016
  return {
7997
8017
  ...header !== null ? { operationType: header.operationType } : {},
7998
8018
  ...header?.operationName !== void 0 ? { operationName: header.operationName } : {},
@@ -8002,10 +8022,10 @@ function assembledDocumentResolution(text2, arg, assembly) {
8002
8022
  }
8003
8023
  };
8004
8024
  }
8005
- const spreads = fragmentSpreadsIn(text2);
8025
+ const spreads = fragmentSpreadsIn(text3);
8006
8026
  if (spreads === null) {
8007
8027
  if (interpolations.length === 0) {
8008
- return { document: text2 };
8028
+ return { document: text3 };
8009
8029
  }
8010
8030
  return {
8011
8031
  unresolved: {
@@ -8014,7 +8034,7 @@ function assembledDocumentResolution(text2, arg, assembly) {
8014
8034
  }
8015
8035
  };
8016
8036
  }
8017
- const filled = fillDanglingSpreads(text2, spreads, arg, assembly);
8037
+ const filled = fillDanglingSpreads(text3, spreads, arg, assembly);
8018
8038
  if (filled.unresolved.length > 0) {
8019
8039
  return {
8020
8040
  document: filled.text,
@@ -8033,15 +8053,15 @@ function assembledDocumentResolution(text2, arg, assembly) {
8033
8053
  }
8034
8054
  return { document: filled.text };
8035
8055
  }
8036
- function fillDanglingSpreads(text2, spreads, arg, assembly) {
8056
+ function fillDanglingSpreads(text3, spreads, arg, assembly) {
8037
8057
  if (spreads.dangling.length === 0 || assembly.tagOrigin !== "project") {
8038
- return { text: text2, unresolved: spreads.dangling, ambiguous: [] };
8058
+ return { text: text3, unresolved: spreads.dangling, ambiguous: [] };
8039
8059
  }
8040
8060
  const definitions = projectFragmentDefinitions(arg.getProject());
8041
8061
  const settled3 = new Set(spreads.defined);
8042
8062
  const unresolved = /* @__PURE__ */ new Set();
8043
8063
  const ambiguous = /* @__PURE__ */ new Set();
8044
- let filled = text2;
8064
+ let filled = text3;
8045
8065
  let pending = spreads.dangling;
8046
8066
  while (pending.length > 0) {
8047
8067
  const brought = [];
@@ -8105,12 +8125,12 @@ function buildFragmentIndex(project) {
8105
8125
  if (!FRAGMENT_DEFINITION_TEXT.test(sourceFile.getFullText())) {
8106
8126
  continue;
8107
8127
  }
8108
- for (const text2 of documentTextsIn(sourceFile)) {
8109
- recordFragmentDefinitions(text2, definitions);
8128
+ for (const text3 of documentTextsIn(sourceFile)) {
8129
+ recordFragmentDefinitions(text3, definitions);
8110
8130
  }
8111
8131
  }
8112
- for (const text2 of graphqlFileTextsNear(sourceFiles)) {
8113
- recordFragmentDefinitions(text2, definitions);
8132
+ for (const text3 of graphqlFileTextsNear(sourceFiles)) {
8133
+ recordFragmentDefinitions(text3, definitions);
8114
8134
  }
8115
8135
  return definitions;
8116
8136
  }
@@ -8147,17 +8167,17 @@ function documentTextsIn(sourceFile) {
8147
8167
  if (!import_ts_morph25.Node.isTaggedTemplateExpression(node) && !import_ts_morph25.Node.isCallExpression(node)) {
8148
8168
  return;
8149
8169
  }
8150
- const text2 = documentTextFromExpression(node, startAssembly(void 0));
8151
- if (text2 !== null && text2 !== "") {
8152
- texts.push(text2);
8170
+ const text3 = documentTextFromExpression(node, startAssembly(void 0));
8171
+ if (text3 !== null && text3 !== "") {
8172
+ texts.push(text3);
8153
8173
  }
8154
8174
  });
8155
8175
  return texts;
8156
8176
  }
8157
- function recordFragmentDefinitions(text2, definitions) {
8177
+ function recordFragmentDefinitions(text3, definitions) {
8158
8178
  let doc;
8159
8179
  try {
8160
- doc = (0, import_graphql.parse)(text2);
8180
+ doc = (0, import_graphql.parse)(text3);
8161
8181
  } catch {
8162
8182
  return;
8163
8183
  }
@@ -8181,13 +8201,13 @@ function recordFragmentDefinitions(text2, definitions) {
8181
8201
  }
8182
8202
  var NO_NAMES = /* @__PURE__ */ new Set();
8183
8203
  function describeInterpolations(interpolations) {
8184
- const quoted = interpolations.map((text2) => `\`${text2}\``).join(", ");
8204
+ const quoted = interpolations.map((text3) => `\`${text3}\``).join(", ");
8185
8205
  return interpolations.length === 1 ? `expression ${quoted}` : `expressions ${quoted}`;
8186
8206
  }
8187
- function fragmentSpreadsIn(text2) {
8207
+ function fragmentSpreadsIn(text3) {
8188
8208
  let doc;
8189
8209
  try {
8190
- doc = (0, import_graphql.parse)(text2);
8210
+ doc = (0, import_graphql.parse)(text3);
8191
8211
  } catch {
8192
8212
  return null;
8193
8213
  }
@@ -8339,9 +8359,9 @@ function resolveThroughFacts(arg, assembly) {
8339
8359
  if (written === null) {
8340
8360
  return null;
8341
8361
  }
8342
- const text2 = documentTextFromExpression(written, assembly);
8343
- if (text2 !== null) {
8344
- return text2;
8362
+ const text3 = documentTextFromExpression(written, assembly);
8363
+ if (text3 !== null) {
8364
+ return text3;
8345
8365
  }
8346
8366
  return typedDocumentSourceOf(written);
8347
8367
  }
@@ -8377,8 +8397,8 @@ function unreadableDocument(arg, reason) {
8377
8397
  }
8378
8398
  };
8379
8399
  }
8380
- function singleLine(text2) {
8381
- const collapsed = text2.replace(/\s+/g, " ").trim();
8400
+ function singleLine(text3) {
8401
+ const collapsed = text3.replace(/\s+/g, " ").trim();
8382
8402
  return collapsed.length > 80 ? `${collapsed.slice(0, 79)}\u2026` : collapsed;
8383
8403
  }
8384
8404
  function importedVariableInitializers(identifier) {
@@ -8422,9 +8442,9 @@ function resolveGqlTemplateText(arg, assembly) {
8422
8442
  }
8423
8443
  }
8424
8444
  for (const init of importedVariableInitializers(arg)) {
8425
- const text2 = documentTextFromExpression(peelSyntax(init), assembly);
8426
- if (text2 !== null) {
8427
- return text2;
8445
+ const text3 = documentTextFromExpression(peelSyntax(init), assembly);
8446
+ if (text3 !== null) {
8447
+ return text3;
8428
8448
  }
8429
8449
  }
8430
8450
  return null;
@@ -9027,7 +9047,8 @@ function expandDocumentParameterCallers(documentArgument, libraryCall, context)
9027
9047
  context.store.returnsCall(wrapper, libraryCall),
9028
9048
  0,
9029
9049
  /* @__PURE__ */ new Set([parameter]),
9030
- context
9050
+ context,
9051
+ { library: libraryCall, through: [] }
9031
9052
  );
9032
9053
  return {
9033
9054
  units: expanded.units,
@@ -9040,7 +9061,7 @@ function documentBehindWrapper(value, store, wrapperReason) {
9040
9061
  }
9041
9062
  return resolveGraphqlDocument(value, store) ?? unreadableDocument(value);
9042
9063
  }
9043
- function expandParameter(parameter, forwardsResult, depth, expanded, context) {
9064
+ function expandParameter(parameter, forwardsResult, depth, expanded, context, chain) {
9044
9065
  const passed = context.store.argumentsPassedTo(parameter);
9045
9066
  if (passed.length === 0) {
9046
9067
  return { units: [], unfollowed: 1 };
@@ -9053,6 +9074,7 @@ function expandParameter(parameter, forwardsResult, depth, expanded, context) {
9053
9074
  unfollowed++;
9054
9075
  continue;
9055
9076
  }
9077
+ const outward = { ...chain, through: [...chain.through, call] };
9056
9078
  const nested = parameterBehind(argument, caller);
9057
9079
  if (nested !== null) {
9058
9080
  const deeper = expandNested(
@@ -9060,13 +9082,21 @@ function expandParameter(parameter, forwardsResult, depth, expanded, context) {
9060
9082
  forwardsResult && context.store.returnsCall(caller, call),
9061
9083
  depth,
9062
9084
  expanded,
9063
- context
9085
+ context,
9086
+ outward
9064
9087
  );
9065
9088
  units.push(...deeper.units);
9066
9089
  unfollowed += deeper.unfollowed;
9067
9090
  continue;
9068
9091
  }
9069
- 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
+ );
9070
9100
  if (unit === null) {
9071
9101
  unfollowed++;
9072
9102
  continue;
@@ -9075,7 +9105,7 @@ function expandParameter(parameter, forwardsResult, depth, expanded, context) {
9075
9105
  }
9076
9106
  return { units, unfollowed };
9077
9107
  }
9078
- function expandNested(parameter, forwardsResult, depth, expanded, context) {
9108
+ function expandNested(parameter, forwardsResult, depth, expanded, context, chain) {
9079
9109
  if (depth + 1 >= MAX_WRAPPER_DEPTH || expanded.has(parameter)) {
9080
9110
  return { units: [], unfollowed: 1 };
9081
9111
  }
@@ -9084,10 +9114,11 @@ function expandNested(parameter, forwardsResult, depth, expanded, context) {
9084
9114
  forwardsResult,
9085
9115
  depth + 1,
9086
9116
  /* @__PURE__ */ new Set([...expanded, parameter]),
9087
- context
9117
+ context,
9118
+ chain
9088
9119
  );
9089
9120
  }
9090
- function callerUnit(caller, call, argument, forwardsResult, context) {
9121
+ function callerUnit(caller, call, argument, forwardsResult, context, chain) {
9091
9122
  const document = resolveGraphqlDocument(argument, context.store) ?? unreadableDocument(argument);
9092
9123
  const operationInfo = operationInfoFromResolution(
9093
9124
  document,
@@ -9104,7 +9135,14 @@ function callerUnit(caller, call, argument, forwardsResult, context) {
9104
9135
  // A wrapper handing back something of its own leaves the caller
9105
9136
  // none of the library's result to read.
9106
9137
  ...forwardsResult ? { callSite: { callExpression: call, methodName: context.methodName } } : {},
9107
- operationInfo
9138
+ operationInfo,
9139
+ ...context.client === void 0 ? {} : {
9140
+ graphqlCall: {
9141
+ call: chain.library,
9142
+ through: chain.through,
9143
+ client: context.client
9144
+ }
9145
+ }
9108
9146
  };
9109
9147
  }
9110
9148
  function unfollowedReason(followed, unfollowed) {
@@ -9206,7 +9244,8 @@ function discoverGraphqlHookCalls(sourceFile, match, kind, resolution) {
9206
9244
  kind,
9207
9245
  operationType: spec.operationType,
9208
9246
  methodName: spec.canonical,
9209
- store
9247
+ store,
9248
+ ...match.clientOption === void 0 ? {} : { client: match.clientOption }
9210
9249
  });
9211
9250
  if (expansion !== null) {
9212
9251
  results.push(...expansion.units);
@@ -9236,7 +9275,14 @@ function discoverGraphqlHookCalls(sourceFile, match, kind, resolution) {
9236
9275
  kind,
9237
9276
  name: `${functionNameOrAnon(enclosing)}.${nameToken}`,
9238
9277
  callSite: { callExpression: node, methodName: spec.canonical },
9239
- operationInfo
9278
+ operationInfo,
9279
+ ...match.clientOption === void 0 ? {} : {
9280
+ graphqlCall: {
9281
+ call: node,
9282
+ through: [],
9283
+ client: match.clientOption
9284
+ }
9285
+ }
9240
9286
  });
9241
9287
  }
9242
9288
  return results;
@@ -9287,7 +9333,8 @@ function discoverGraphqlImperativeCalls(sourceFile, match, kind, resolution) {
9287
9333
  kind,
9288
9334
  operationType: spec.operationType,
9289
9335
  methodName,
9290
- store: resolution
9336
+ store: resolution,
9337
+ client: "receiver"
9291
9338
  });
9292
9339
  if (expansion !== null) {
9293
9340
  results.push(...expansion.units);
@@ -9317,7 +9364,8 @@ function discoverGraphqlImperativeCalls(sourceFile, match, kind, resolution) {
9317
9364
  kind,
9318
9365
  name: `${functionNameOrAnon(enclosing)}.${nameToken}`,
9319
9366
  callSite: { callExpression: node, methodName },
9320
- operationInfo
9367
+ operationInfo,
9368
+ graphqlCall: { call: node, through: [], client: "receiver" }
9321
9369
  });
9322
9370
  });
9323
9371
  return results;
@@ -10328,6 +10376,7 @@ function discoverRegistrationCalls(sourceFile, match, kind, bindingExtraction, r
10328
10376
  if (registrationSubjects.size === 0 && !storeCanFindSubjects(sourceFile, match, resolution, registrationMethods)) {
10329
10377
  return results;
10330
10378
  }
10379
+ const anonymousLabels = /* @__PURE__ */ new Map();
10331
10380
  sourceFile.forEachDescendant((node) => {
10332
10381
  if (!import_ts_morph37.Node.isCallExpression(node)) {
10333
10382
  return;
@@ -10429,7 +10478,8 @@ function discoverRegistrationCalls(sourceFile, match, kind, bindingExtraction, r
10429
10478
  routeWrapperPattern,
10430
10479
  methodName,
10431
10480
  routeInfo,
10432
- resolution
10481
+ resolution,
10482
+ anonymousLabels
10433
10483
  );
10434
10484
  results.push({
10435
10485
  func: handler,
@@ -10490,9 +10540,8 @@ function declinedRegistration(call, callee, methodName, kind, receiver, resoluti
10490
10540
  }
10491
10541
  ];
10492
10542
  }
10493
- function routeChainOf(listed, pattern, methodName, routeInfo, resolution) {
10543
+ function routeChainOf(listed, pattern, methodName, routeInfo, resolution, anonymousLabels) {
10494
10544
  const chain = { units: [], references: [], unfollowed: [] };
10495
- const anonymousLabels = /* @__PURE__ */ new Map();
10496
10545
  for (const arg of listed) {
10497
10546
  const elements = import_ts_morph37.Node.isArrayLiteralExpression(arg) ? arg.getElements() : [arg];
10498
10547
  for (const element of elements) {
@@ -10506,12 +10555,12 @@ function routeChainOf(listed, pattern, methodName, routeInfo, resolution) {
10506
10555
  }
10507
10556
  const ownName = functionNameOrAnon(target);
10508
10557
  const named2 = ownName !== "<anon>";
10509
- const label = factoryNameOf(element) ?? methodName;
10558
+ const label = `${factoryNameOf(element) ?? methodName}@${routeWhere(routeInfo)}`;
10510
10559
  const seen = anonymousLabels.get(label) ?? 0;
10511
10560
  if (!named2) {
10512
10561
  anonymousLabels.set(label, seen + 1);
10513
10562
  }
10514
- const name = named2 ? ownName : routeWrapperLabel(label, routeInfo, element, seen);
10563
+ const name = named2 ? ownName : numberedLabel(label, seen);
10515
10564
  chain.units.push({
10516
10565
  func: target,
10517
10566
  kind: pattern.kind,
@@ -10527,9 +10576,11 @@ function routeChainOf(listed, pattern, methodName, routeInfo, resolution) {
10527
10576
  }
10528
10577
  return chain;
10529
10578
  }
10530
- function routeWrapperLabel(label, routeInfo, element, seen) {
10531
- const where = routeInfo === null ? `L${element.getStartLineNumber()}` : `${routeInfo.method} ${routeInfo.path}`;
10532
- 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}`;
10533
10584
  }
10534
10585
  function withMountPrefix(routeInfo, subjectNode, mountPrefixes) {
10535
10586
  if (routeInfo === null || mountPrefixes === void 0) {
@@ -11467,8 +11518,8 @@ function mockOf(call, match, sourceFile, resolution) {
11467
11518
  const module2 = import_ts_morph41.Node.isIdentifier(first) ? moduleImportedWholeAs(sourceFile, first.getText()) : null;
11468
11519
  return { ...module2 !== null ? { module: module2 } : {}, name, written };
11469
11520
  }
11470
- function oneLine(text2) {
11471
- const flat = text2.replace(/\s*\n\s*/g, "");
11521
+ function oneLine(text3) {
11522
+ const flat = text3.replace(/\s*\n\s*/g, "");
11472
11523
  const factory = flat.search(/,\s*(async\s*)?\(/);
11473
11524
  return factory === -1 ? flat : `${flat.slice(0, factory)})`;
11474
11525
  }
@@ -12043,8 +12094,8 @@ function receiverIsPromiseTyped(receiver) {
12043
12094
  if (symbolName === "Promise") {
12044
12095
  return true;
12045
12096
  }
12046
- const text2 = type.getText();
12047
- return text2 === "Promise" || text2.startsWith("Promise<");
12097
+ const text3 = type.getText();
12098
+ return text3 === "Promise" || text3.startsWith("Promise<");
12048
12099
  }
12049
12100
  function callbackReturnExpression(node) {
12050
12101
  if (node === void 0 || !(import_ts_morph44.Node.isArrowFunction(node) || import_ts_morph44.Node.isFunctionExpression(node))) {
@@ -12727,9 +12778,9 @@ function getLoopBody(stmt) {
12727
12778
  return void 0;
12728
12779
  }
12729
12780
  var loopHeaderText = (stmt) => {
12730
- const text2 = stmt.getText();
12731
- const bodyStart = text2.indexOf("{");
12732
- 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);
12733
12784
  return header.trim();
12734
12785
  };
12735
12786
  var conditionOf = (expr) => ({
@@ -12840,7 +12891,8 @@ function buildStructured(stmt, ctx) {
12840
12891
  kind: "loop",
12841
12892
  condition: { sourceText: loopHeaderText(stmt), expression: null },
12842
12893
  body: lowerList(statementsOf(getLoopBody(stmt)), ctx),
12843
- exitKind
12894
+ exitKind,
12895
+ lines: lineRangeOf(stmt)
12844
12896
  };
12845
12897
  }
12846
12898
  if (import_ts_morph48.Node.isTryStatement(stmt)) {
@@ -12848,6 +12900,7 @@ function buildStructured(stmt, ctx) {
12848
12900
  const finallyBlock = stmt.getFinallyBlock();
12849
12901
  return {
12850
12902
  kind: "try",
12903
+ lines: lineRangeOf(stmt),
12851
12904
  tryBody: lowerList(stmt.getTryBlock().getStatements(), ctx),
12852
12905
  catchBody: catchClause === void 0 ? null : lowerList(catchClause.getBlock().getStatements(), ctx),
12853
12906
  finallyBody: finallyBlock === void 0 ? null : lowerList(finallyBlock.getStatements(), ctx),
@@ -13008,13 +13061,14 @@ function loweredExpressionBody(body, terminalNodes, func, barriers) {
13008
13061
  function computePathConditions(func, terminalNodes, barriers = NO_BARRIERS, endings = NO_ENDINGS, keepsArms) {
13009
13062
  const body = func.getBody();
13010
13063
  if (body === void 0) {
13011
- return { byTerminal: /* @__PURE__ */ new Map(), fallthrough: [] };
13064
+ return { byTerminal: /* @__PURE__ */ new Map(), fallthrough: [], placement: /* @__PURE__ */ new Map() };
13012
13065
  }
13013
13066
  if (containsUnmodeledFlow(body, func, barriers)) {
13014
13067
  return degradedResult(func, terminalNodes, "labeled statement");
13015
13068
  }
13069
+ let lowered;
13016
13070
  try {
13017
- 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);
13018
13072
  const result = (0, import_extractor4.enumerateStructuredPaths)({
13019
13073
  statements: lowered.statements,
13020
13074
  terminalsByStmt: lowered.terminalsByStmt,
@@ -13022,15 +13076,17 @@ function computePathConditions(func, terminalNodes, barriers = NO_BARRIERS, endi
13022
13076
  });
13023
13077
  return composeExpressionLevel(func, result, lowered.terminalHomeRaw);
13024
13078
  } catch (error) {
13079
+ const placement = lowered === void 0 ? /* @__PURE__ */ new Map() : (0, import_extractor4.placeTerminals)(lowered);
13025
13080
  if (error instanceof import_extractor4.PathBudgetExceeded) {
13026
13081
  return degradedResult(
13027
13082
  func,
13028
13083
  terminalNodes,
13029
- error.message === "" ? "path budget exceeded" : error.message
13084
+ error.message === "" ? "path budget exceeded" : error.message,
13085
+ placement
13030
13086
  );
13031
13087
  }
13032
13088
  if (error instanceof import_extractor4.UnmodeledFlow) {
13033
- return degradedResult(func, terminalNodes, error.message);
13089
+ return degradedResult(func, terminalNodes, error.message, placement);
13034
13090
  }
13035
13091
  throw error;
13036
13092
  }
@@ -13045,9 +13101,13 @@ function composeExpressionLevel(func, result, terminalHomeRaw) {
13045
13101
  paths.map((statementLevel) => [...statementLevel, ...expressionLevel])
13046
13102
  );
13047
13103
  }
13048
- return { byTerminal, fallthrough: result.fallthrough };
13104
+ return {
13105
+ byTerminal,
13106
+ fallthrough: result.fallthrough,
13107
+ placement: result.placement
13108
+ };
13049
13109
  }
13050
- function degradedResult(func, terminalNodes, reason) {
13110
+ function degradedResult(func, terminalNodes, reason, placement = /* @__PURE__ */ new Map()) {
13051
13111
  const marker = {
13052
13112
  sourceText: `unmodeled control flow (${reason})`,
13053
13113
  polarity: "positive",
@@ -13064,7 +13124,7 @@ function degradedResult(func, terminalNodes, reason) {
13064
13124
  [...collectAncestorConditionInfosBelow(terminal, func), marker]
13065
13125
  ]);
13066
13126
  }
13067
- return { byTerminal, fallthrough: [[marker]] };
13127
+ return { byTerminal, fallthrough: [[marker]], placement };
13068
13128
  }
13069
13129
 
13070
13130
  // src/provenance.ts
@@ -13191,7 +13251,7 @@ function stableTypeText(type, reader, enclosing, flags) {
13191
13251
  return withoutImportQualifiers(printed(ordered, file));
13192
13252
  }
13193
13253
  var IMPORT_QUALIFIER = /import\("[^"]*"\)\./g;
13194
- var withoutImportQualifiers = (text2) => text2.replace(IMPORT_QUALIFIER, "");
13254
+ var withoutImportQualifiers = (text3) => text3.replace(IMPORT_QUALIFIER, "");
13195
13255
  function printed(node, file) {
13196
13256
  return PRINTER.printNode(import_ts_morph51.ts.EmitHint.Unspecified, node, file).replace(
13197
13257
  /\n/g,
@@ -13233,11 +13293,11 @@ function inStableOrder(members, file) {
13233
13293
  function compareMembers(a, b) {
13234
13294
  return nullishRank(a) - nullishRank(b) || compareText(a, b);
13235
13295
  }
13236
- function nullishRank(text2) {
13237
- if (text2 === "null") {
13296
+ function nullishRank(text3) {
13297
+ if (text3 === "null") {
13238
13298
  return 1;
13239
13299
  }
13240
- if (text2 === "undefined") {
13300
+ if (text3 === "undefined") {
13241
13301
  return 2;
13242
13302
  }
13243
13303
  return 0;
@@ -14232,11 +14292,12 @@ function invocationAt(call, func) {
14232
14292
  callee: call.getExpression().getText(),
14233
14293
  args: extractArgs(call),
14234
14294
  async: isAwaited(call),
14235
- ...preconditions.length > 0 ? { preconditions } : {}
14295
+ ...preconditions.length > 0 ? { preconditions } : {},
14296
+ location: lineRangeOf(call)
14236
14297
  },
14237
14298
  node: call,
14238
14299
  line: enclosingStatementLine(call),
14239
- alwaysRuns: runsOnEveryPath(call, func)
14300
+ cleansUpFrom: cleanupStartLine(call, func)
14240
14301
  };
14241
14302
  }
14242
14303
  function isAwaited(call) {
@@ -14247,7 +14308,7 @@ function runInvocationRecognizers(func, recognizers, barriers = NO_BARRIERS, res
14247
14308
  if (recognizers.length === 0) {
14248
14309
  return [];
14249
14310
  }
14250
- const out = [];
14311
+ const sites = [];
14251
14312
  const sourceFile = func.getSourceFile();
14252
14313
  const calls = [];
14253
14314
  func.forEachDescendant((node, traversal) => {
@@ -14272,7 +14333,8 @@ function runInvocationRecognizers(func, recognizers, barriers = NO_BARRIERS, res
14272
14333
  );
14273
14334
  const line = enclosingStatementLine(node);
14274
14335
  const preconditions = collectPreconditions(node, func);
14275
- const alwaysRuns = runsOnEveryPath(node, func);
14336
+ const cleansUpFrom = cleanupStartLine(node, func);
14337
+ const site = [];
14276
14338
  for (const recognizer of recognizers) {
14277
14339
  let emitted = null;
14278
14340
  try {
@@ -14290,17 +14352,18 @@ function runInvocationRecognizers(func, recognizers, barriers = NO_BARRIERS, res
14290
14352
  continue;
14291
14353
  }
14292
14354
  for (const eff of emitted) {
14293
- out.push({
14294
- effect: withPreconditions(eff, preconditions),
14355
+ site.push({
14356
+ effect: placedAt(withPreconditions(eff, preconditions), node),
14295
14357
  line,
14296
14358
  preconditions,
14297
- alwaysRuns,
14359
+ cleansUpFrom,
14298
14360
  ...stated?.of(eff)
14299
14361
  });
14300
14362
  }
14301
14363
  }
14364
+ sites.push(site);
14302
14365
  }
14303
- return out;
14366
+ return (0, import_extractor6.numberRepeatedCallSites)(sites).flat();
14304
14367
  }
14305
14368
  function runAccessRecognizers(func, recognizers, barriers = NO_BARRIERS, resolution, originatesFrom, anchorCallsOf) {
14306
14369
  return dispatchAccessRecognizers(
@@ -14385,7 +14448,7 @@ function dispatchAccessRecognizers(root, recognizers, isStop, resolution, origin
14385
14448
  );
14386
14449
  const line = enclosingStatementLine(node);
14387
14450
  const preconditions = collectPreconditions(node, root);
14388
- const alwaysRuns = runsOnEveryPath(node, root);
14451
+ const cleansUpFrom = cleanupStartLine(node, root);
14389
14452
  for (const recognizer of recognizers) {
14390
14453
  let emitted = null;
14391
14454
  try {
@@ -14410,10 +14473,10 @@ function dispatchAccessRecognizers(root, recognizers, isStop, resolution, origin
14410
14473
  }
14411
14474
  seenEffects.add(key);
14412
14475
  out.push({
14413
- effect,
14476
+ effect: placedAt(effect, node),
14414
14477
  line,
14415
14478
  preconditions,
14416
- alwaysRuns,
14479
+ cleansUpFrom,
14417
14480
  ...stated?.of(eff)
14418
14481
  });
14419
14482
  }
@@ -14447,6 +14510,12 @@ function extractArg(node, depth) {
14447
14510
  if (import_ts_morph56.Node.isFalseLiteral(node)) {
14448
14511
  return { kind: "boolean", value: false };
14449
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
+ }
14450
14519
  if (import_ts_morph56.Node.isTemplateExpression(node)) {
14451
14520
  return { kind: "template", sourceText: node.getText() };
14452
14521
  }
@@ -14538,16 +14607,20 @@ function collectPreconditions(node, root) {
14538
14607
  conditionInfoToRawCondition
14539
14608
  );
14540
14609
  }
14541
- function runsOnEveryPath(node, root) {
14610
+ function cleanupStartLine(node, root) {
14611
+ let found;
14542
14612
  let current2 = node;
14543
14613
  while (current2 !== void 0 && current2 !== root) {
14544
14614
  const parent = current2.getParent();
14545
14615
  if (parent !== void 0 && import_ts_morph56.Node.isTryStatement(parent) && parent.getFinallyBlock() === current2) {
14546
- return true;
14616
+ found = startLineOf(parent);
14547
14617
  }
14548
14618
  current2 = parent;
14549
14619
  }
14550
- return false;
14620
+ return found;
14621
+ }
14622
+ function placedAt(effect, node) {
14623
+ return { ...effect, location: lineRangeOf(node) };
14551
14624
  }
14552
14625
  function withPreconditions(effect, preconditions) {
14553
14626
  if (preconditions.length === 0) {
@@ -14579,6 +14652,9 @@ function inlineModuleBinding(ident, depth) {
14579
14652
  if (!isModuleScoped(decl)) {
14580
14653
  continue;
14581
14654
  }
14655
+ if (decl.getVariableStatement()?.getDeclarationKind() !== import_ts_morph56.VariableDeclarationKind.Const) {
14656
+ continue;
14657
+ }
14582
14658
  const init = decl.getInitializer();
14583
14659
  if (init === void 0) {
14584
14660
  continue;
@@ -15288,8 +15364,8 @@ function typeToShape(type, ctx) {
15288
15364
  return { type: "number" };
15289
15365
  }
15290
15366
  if (type.isBooleanLiteral()) {
15291
- const text3 = type.getText(ctx.enclosing);
15292
- return { type: "literal", value: text3 === "true" };
15367
+ const text4 = type.getText(ctx.enclosing);
15368
+ return { type: "literal", value: text4 === "true" };
15293
15369
  }
15294
15370
  if (type.isString()) {
15295
15371
  return { type: "text" };
@@ -15353,9 +15429,9 @@ function typeToShape(type, ctx) {
15353
15429
  if (type.isEnum() || type.isEnumLiteral()) {
15354
15430
  return enumToShape(type, ctx);
15355
15431
  }
15356
- const text2 = printedTypeName(type, ctx);
15357
- if (text2 && text2.length > 0 && text2 !== "__type") {
15358
- 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 };
15359
15435
  }
15360
15436
  return null;
15361
15437
  }
@@ -15498,8 +15574,8 @@ function opaqueNamedRef(type, ctx) {
15498
15574
  if (!OPAQUE_NAMED_TYPES.has(name)) {
15499
15575
  return null;
15500
15576
  }
15501
- const text2 = printedTypeName(type, ctx);
15502
- 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 };
15503
15579
  }
15504
15580
  function outsideProjectRef(type, ctx) {
15505
15581
  const symbol = type.getSymbol();
@@ -15565,8 +15641,8 @@ function definitionKeyFor(type, name, reader) {
15565
15641
  const written = stableTypeText(type, reader);
15566
15642
  return `${name}@${shortHash(written)}`;
15567
15643
  }
15568
- function shortHash(text2) {
15569
- 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);
15570
15646
  }
15571
15647
  function refFromType(type, ctx) {
15572
15648
  const symbol = type.getAliasSymbol() ?? type.getSymbol();
@@ -16248,11 +16324,11 @@ function collectJsxAttributes(opening) {
16248
16324
  }
16249
16325
  function jsxChildToRenderNode(child) {
16250
16326
  if (import_ts_morph63.Node.isJsxText(child)) {
16251
- const text2 = child.getLiteralText().trim();
16252
- if (text2.length === 0) {
16327
+ const text3 = child.getLiteralText().trim();
16328
+ if (text3.length === 0) {
16253
16329
  return null;
16254
16330
  }
16255
- return { type: "text", value: text2 };
16331
+ return { type: "text", value: text3 };
16256
16332
  }
16257
16333
  if (import_ts_morph63.Node.isJsxExpression(child)) {
16258
16334
  const inner = child.getExpression();
@@ -17249,11 +17325,12 @@ function makeFallthroughTerminal(func, pattern) {
17249
17325
 
17250
17326
  // src/assembly.ts
17251
17327
  var preconditionsOf = (effect) => effect.type === "invocation" ? effect.preconditions : void 0;
17252
- function firesOn(site, branch) {
17253
- if (!(0, import_extractor8.guardsHoldOn)(site.preconditions, branch.conditions)) {
17254
- return false;
17255
- }
17256
- 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
+ };
17257
17334
  }
17258
17335
  function writesTerminal(call, terminalNodes) {
17259
17336
  let current2 = call;
@@ -17351,7 +17428,7 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
17351
17428
  const endings = new Set(
17352
17429
  terminals.filter(({ terminal }) => terminal.kind === "exit").map(({ node }) => node)
17353
17430
  );
17354
- const { byTerminal } = computePathConditions(
17431
+ const { byTerminal, placement } = computePathConditions(
17355
17432
  func,
17356
17433
  terminals.map(({ node }) => node),
17357
17434
  barriers,
@@ -17384,6 +17461,7 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
17384
17461
  const rawBranches = terminals.flatMap(
17385
17462
  ({ node, terminal, whenAlso }) => {
17386
17463
  const conditionLists = byTerminal.get(node) ?? [];
17464
+ const loopAround = placement.get(node)?.loopAround;
17387
17465
  return conditionLists.map((infos) => {
17388
17466
  const conditions = [
17389
17467
  ...infos.map(conditionInfoToRawCondition),
@@ -17394,6 +17472,7 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
17394
17472
  terminal,
17395
17473
  effects: [],
17396
17474
  location: terminal.location,
17475
+ ...loopAround === void 0 ? {} : { loopAround },
17397
17476
  isDefault: isDefaultConditionList(infos)
17398
17477
  };
17399
17478
  });
@@ -17420,14 +17499,12 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
17420
17499
  (i) => !writesTerminal(i.node, terminalNodes)
17421
17500
  );
17422
17501
  for (const branch of distinctBranches) {
17423
- branch.effects = sideEffects.filter(
17424
- (i) => firesOn({ ...i, preconditions: preconditionsOf(i.effect) }, branch)
17425
- ).map((i) => i.effect);
17502
+ branch.effects = sideEffects.filter((i) => (0, import_extractor8.firesOn)(invocationSite(i), branch)).map((i) => i.effect);
17426
17503
  }
17427
17504
  }
17428
17505
  const firing = /* @__PURE__ */ new Map();
17429
17506
  for (const branch of distinctBranches) {
17430
- const located = recognized.filter((r) => firesOn(r, branch));
17507
+ const located = recognized.filter((r) => (0, import_extractor8.firesOn)(r, branch));
17431
17508
  const extra = located.map((r) => r.effect);
17432
17509
  if (extra.length > 0) {
17433
17510
  branch.extraEffects = [...branch.extraEffects ?? [], ...extra];
@@ -17467,11 +17544,9 @@ function handOffSites(invocations, terminalNodes, handOff, reading) {
17467
17544
  continue;
17468
17545
  }
17469
17546
  sites.push({
17547
+ ...invocationSite(site),
17470
17548
  helper,
17471
- preconditions: preconditionsOf(site.effect),
17472
- line: site.line,
17473
- at: lineRangeOf(site.node),
17474
- alwaysRuns: site.alwaysRuns
17549
+ at: lineRangeOf(site.node)
17475
17550
  });
17476
17551
  }
17477
17552
  return sites;
@@ -17497,16 +17572,12 @@ function accountFor(site, terminalNodes, branches) {
17497
17572
  if (writesTerminal(node, terminalNodes)) {
17498
17573
  return { node, callee, line, outcome: "terminal" };
17499
17574
  }
17500
- const call = {
17501
- line,
17502
- alwaysRuns: site.alwaysRuns,
17503
- preconditions: preconditionsOf(site.effect)
17504
- };
17505
- if (branches.some((branch) => firesOn(call, branch))) {
17575
+ const call = invocationSite(site);
17576
+ if (branches.some((branch) => (0, import_extractor8.firesOn)(call, branch))) {
17506
17577
  return { node, callee, line, outcome: "invocation" };
17507
17578
  }
17508
17579
  const reachable = branches.some(
17509
- (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)
17510
17581
  );
17511
17582
  return {
17512
17583
  node,
@@ -18183,39 +18254,132 @@ function parameterName(segment, convention) {
18183
18254
  return null;
18184
18255
  }
18185
18256
 
18257
+ // src/discovery/graphqlCallClient.ts
18258
+ var import_ts_morph74 = require("ts-morph");
18259
+ var import_values12 = require("@suss/values");
18260
+
18186
18261
  // src/discovery/graphqlClientConstruction.ts
18187
18262
  var import_picomatch2 = __toESM(require("picomatch"), 1);
18188
18263
  var import_ts_morph73 = require("ts-morph");
18189
18264
  var import_behavioral_ir12 = require("@suss/behavioral-ir");
18190
18265
  function stampGraphqlClientRefs(summaries, sourceFiles, packs, resolution) {
18191
- const scopes = compileOperationScopes(packs);
18266
+ const constructions = graphqlClientConstructions(
18267
+ sourceFiles,
18268
+ packs.flatMap((pack) => pack.graphqlClients ?? []),
18269
+ resolution
18270
+ );
18192
18271
  const sole = soleGraphqlClientRef(
18193
- collectGraphqlClientRefs(sourceFiles, packs, resolution)
18272
+ constructionRefs(constructions, resolution)
18194
18273
  );
18195
- const workspace = sole !== null ? boundWorkspaceFor(sole, packs) : null;
18196
- 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
+ };
18197
18286
  let registry = null;
18198
18287
  for (const summary of summaries) {
18199
18288
  if (!(0, import_behavioral_ir12.isGraphqlOperationBinding)(summary.identity.boundaryBinding)) {
18200
18289
  continue;
18201
18290
  }
18202
18291
  const existing = (0, import_behavioral_ir12.readGraphqlMetadata)(summary) ?? {};
18203
- const scoped = scopedWorkspaceFor(summary.location.file, scopes);
18204
- const client = scoped !== null ? { uri: null, uriRef: null, workspace: scoped } : soleClient;
18292
+ const decided = clientFor(summary, existing, grounding);
18205
18293
  const dangling = (existing.unresolvedFragments?.length ?? 0) > 0;
18206
- if (client === null && !dangling) {
18294
+ if (decided === null && !dangling) {
18207
18295
  continue;
18208
18296
  }
18209
18297
  if (dangling && registry === null) {
18210
18298
  registry = fragmentRegistryStatus(sourceFiles, packs, resolution);
18211
18299
  }
18212
18300
  summary.metadata = (0, import_behavioral_ir12.withGraphqlMetadata)(summary.metadata, {
18213
- ...existing,
18214
- ...client !== null ? { client } : {},
18301
+ ...decided === null ? existing : withoutClient(existing),
18302
+ ...decided ?? {},
18215
18303
  ...dangling && registry !== null ? { fragmentRegistry: registry } : {}
18216
18304
  });
18217
18305
  }
18218
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
+ }
18219
18383
  function compileOperationScopes(packs) {
18220
18384
  return packs.flatMap((pack) => pack.graphqlOperationScopes ?? []).map((scope) => {
18221
18385
  const matchers2 = scope.files.map(
@@ -18248,13 +18412,22 @@ function boundWorkspaceFor(ref, packs) {
18248
18412
  }
18249
18413
  return null;
18250
18414
  }
18251
- function collectGraphqlClientRefs(sourceFiles, packs, resolution) {
18252
- 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) {
18253
18426
  if (specs.length === 0) {
18254
18427
  return [];
18255
18428
  }
18256
18429
  const store = resolution ?? new ResolutionStore();
18257
- const refs = [];
18430
+ const found = [];
18258
18431
  for (const sourceFile of sourceFiles) {
18259
18432
  for (const spec of specs) {
18260
18433
  const constructions = callsByOriginName(
@@ -18264,14 +18437,27 @@ function collectGraphqlClientRefs(sourceFiles, packs, resolution) {
18264
18437
  /* @__PURE__ */ new Set([spec.importName])
18265
18438
  );
18266
18439
  for (const node of constructions.keys()) {
18267
- const found = constructionRef(node, spec.uriProperty, resolution);
18268
- if (found !== null) {
18269
- refs.push(found);
18270
- }
18440
+ found.push({ node, spec });
18271
18441
  }
18272
18442
  }
18273
18443
  }
18274
- 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);
18275
18461
  }
18276
18462
  function soleGraphqlClientRef(refs) {
18277
18463
  const distinct = /* @__PURE__ */ new Map();
@@ -18394,11 +18580,153 @@ function constructionRef(node, uriProperty, resolution) {
18394
18580
  return { uri: null, uriRef: value.getText() };
18395
18581
  }
18396
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
+
18397
18725
  // src/discovery/helperIndex.ts
18398
- var import_ts_morph75 = require("ts-morph");
18726
+ var import_ts_morph76 = require("ts-morph");
18399
18727
 
18400
18728
  // src/discovery/helperReading.ts
18401
- var import_ts_morph74 = require("ts-morph");
18729
+ var import_ts_morph75 = require("ts-morph");
18402
18730
  var UNREAD = { as: "unread" };
18403
18731
  function readHelperSinks(helper) {
18404
18732
  const positions = parameterPositions(helper);
@@ -18408,11 +18736,11 @@ function readHelperSinks(helper) {
18408
18736
  return sinks;
18409
18737
  }
18410
18738
  body.forEachDescendant((node) => {
18411
- if (!import_ts_morph74.Node.isCallExpression(node)) {
18739
+ if (!import_ts_morph75.Node.isCallExpression(node)) {
18412
18740
  return;
18413
18741
  }
18414
18742
  const callee = node.getExpression();
18415
- 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];
18416
18744
  sinks.push({
18417
18745
  method,
18418
18746
  receiver,
@@ -18432,25 +18760,25 @@ function parameterPositions(helper) {
18432
18760
  return positions;
18433
18761
  }
18434
18762
  function readValue(node, positions) {
18435
- 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)) {
18436
18764
  return { as: "text", text: node.getLiteralValue() };
18437
18765
  }
18438
- if (import_ts_morph74.Node.isTemplateExpression(node)) {
18766
+ if (import_ts_morph75.Node.isTemplateExpression(node)) {
18439
18767
  return templateText(node, positions);
18440
18768
  }
18441
- if (import_ts_morph74.Node.isIdentifier(node)) {
18769
+ if (import_ts_morph75.Node.isIdentifier(node)) {
18442
18770
  const position = positions.get(node.getText());
18443
18771
  return position === void 0 ? UNREAD : { as: "parameter", position };
18444
18772
  }
18445
- if (import_ts_morph74.Node.isPropertyAccessExpression(node)) {
18773
+ if (import_ts_morph75.Node.isPropertyAccessExpression(node)) {
18446
18774
  const target = node.getExpression();
18447
- 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;
18448
18776
  return position === void 0 ? UNREAD : { as: "parameter", position, property: node.getName() };
18449
18777
  }
18450
- if (import_ts_morph74.Node.isObjectLiteralExpression(node)) {
18778
+ if (import_ts_morph75.Node.isObjectLiteralExpression(node)) {
18451
18779
  return objectValue(node, positions);
18452
18780
  }
18453
- if (import_ts_morph74.Node.isCallExpression(node)) {
18781
+ if (import_ts_morph75.Node.isCallExpression(node)) {
18454
18782
  return {
18455
18783
  as: "call",
18456
18784
  callee: node.getExpression().getText(),
@@ -18460,27 +18788,27 @@ function readValue(node, positions) {
18460
18788
  return UNREAD;
18461
18789
  }
18462
18790
  function templateText(node, positions) {
18463
- if (!import_ts_morph74.Node.isTemplateExpression(node)) {
18791
+ if (!import_ts_morph75.Node.isTemplateExpression(node)) {
18464
18792
  return UNREAD;
18465
18793
  }
18466
- let text2 = node.getHead().getLiteralText();
18794
+ let text3 = node.getHead().getLiteralText();
18467
18795
  for (const span of node.getTemplateSpans()) {
18468
18796
  const expression = span.getExpression();
18469
- 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;
18470
18798
  if (position === void 0) {
18471
18799
  return UNREAD;
18472
18800
  }
18473
- text2 += `{${position}}${span.getLiteral().getLiteralText()}`;
18801
+ text3 += `{${position}}${span.getLiteral().getLiteralText()}`;
18474
18802
  }
18475
- return { as: "text", text: text2 };
18803
+ return { as: "text", text: text3 };
18476
18804
  }
18477
18805
  function objectValue(node, positions) {
18478
- if (!import_ts_morph74.Node.isObjectLiteralExpression(node)) {
18806
+ if (!import_ts_morph75.Node.isObjectLiteralExpression(node)) {
18479
18807
  return UNREAD;
18480
18808
  }
18481
18809
  const properties = {};
18482
18810
  for (const property of node.getProperties()) {
18483
- if (!import_ts_morph74.Node.isPropertyAssignment(property)) {
18811
+ if (!import_ts_morph75.Node.isPropertyAssignment(property)) {
18484
18812
  continue;
18485
18813
  }
18486
18814
  const initializer = property.getInitializer();
@@ -18495,7 +18823,7 @@ function objectValue(node, positions) {
18495
18823
  return { as: "object", properties };
18496
18824
  }
18497
18825
  function propertyKey(name) {
18498
- return import_ts_morph74.Node.isStringLiteral(name) ? name.getLiteralValue() : name.getText();
18826
+ return import_ts_morph75.Node.isStringLiteral(name) ? name.getLiteralValue() : name.getText();
18499
18827
  }
18500
18828
 
18501
18829
  // src/discovery/helperIndex.ts
@@ -18579,8 +18907,8 @@ function noteExpanded(expanded, readings, discovery) {
18579
18907
  }
18580
18908
  }
18581
18909
  function mentionsAnyOf(file, written) {
18582
- const text2 = file.getFullText();
18583
- return written.some((word) => text2.includes(word));
18910
+ const text3 = file.getFullText();
18911
+ return written.some((word) => text3.includes(word));
18584
18912
  }
18585
18913
  function readSubjectHelpers(pack, packsByFile, resolution) {
18586
18914
  const matches = registrationMatchesOf(pack);
@@ -18625,11 +18953,11 @@ function registrationMatchesOf(pack) {
18625
18953
  }
18626
18954
  function collectHandOffs(sourceFile, subjects, match, resolution, found) {
18627
18955
  sourceFile.forEachDescendant((node) => {
18628
- if (!import_ts_morph75.Node.isCallExpression(node)) {
18956
+ if (!import_ts_morph76.Node.isCallExpression(node)) {
18629
18957
  return;
18630
18958
  }
18631
18959
  const callee = node.getExpression();
18632
- if (!import_ts_morph75.Node.isIdentifier(callee)) {
18960
+ if (!import_ts_morph76.Node.isIdentifier(callee)) {
18633
18961
  return;
18634
18962
  }
18635
18963
  const handedOver = subjectArgument(node, subjects, match, resolution);
@@ -18644,12 +18972,12 @@ function collectHandOffs(sourceFile, subjects, match, resolution, found) {
18644
18972
  });
18645
18973
  }
18646
18974
  function subjectArgument(call, subjects, match, resolution) {
18647
- if (!import_ts_morph75.Node.isCallExpression(call)) {
18975
+ if (!import_ts_morph76.Node.isCallExpression(call)) {
18648
18976
  return -1;
18649
18977
  }
18650
18978
  const args = call.getArguments();
18651
18979
  for (const [position, argument] of args.entries()) {
18652
- 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)) {
18653
18981
  continue;
18654
18982
  }
18655
18983
  if (subjectNodeFor(argument, subjects, match, resolution) !== void 0) {
@@ -18681,8 +19009,8 @@ function record(found, helper, name, subjectParameter) {
18681
19009
  function readTextHelpers(sourceFiles, contains) {
18682
19010
  const readings = [];
18683
19011
  for (const sourceFile of sourceFiles) {
18684
- const text2 = sourceFile.getFullText();
18685
- if (!contains.some((mark) => text2.includes(mark))) {
19012
+ const text3 = sourceFile.getFullText();
19013
+ if (!contains.some((mark) => text3.includes(mark))) {
18686
19014
  continue;
18687
19015
  }
18688
19016
  for (const [name, declaration] of declaredFunctions(sourceFile)) {
@@ -18710,7 +19038,7 @@ function declaredFunctions(sourceFile) {
18710
19038
  }
18711
19039
  for (const variable3 of sourceFile.getVariableDeclarations()) {
18712
19040
  const initializer = variable3.getInitializer();
18713
- 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))) {
18714
19042
  found.push([variable3.getName(), initializer]);
18715
19043
  }
18716
19044
  }
@@ -18879,8 +19207,8 @@ function recordEdge(edgesByChild, candidate) {
18879
19207
  }
18880
19208
 
18881
19209
  // src/discovery/restWrapper.ts
18882
- var import_ts_morph76 = require("ts-morph");
18883
- var import_values12 = require("@suss/values");
19210
+ var import_ts_morph77 = require("ts-morph");
19211
+ var import_values13 = require("@suss/values");
18884
19212
  var MAX_WRAPPER_DEPTH2 = 3;
18885
19213
  function expandRestWrapperCallers(wrapper, readSink, store) {
18886
19214
  return expandFrom(
@@ -18896,8 +19224,8 @@ function parametersAsHoles(func) {
18896
19224
  const bindings = /* @__PURE__ */ new Map();
18897
19225
  for (const parameter of func.getParameters()) {
18898
19226
  const nameNode = parameter.getNameNode();
18899
- if (import_ts_morph76.Node.isIdentifier(nameNode)) {
18900
- 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()));
18901
19229
  }
18902
19230
  }
18903
19231
  return bindings;
@@ -18936,11 +19264,11 @@ function callSitesOf(func, store) {
18936
19264
  const byCall = /* @__PURE__ */ new Map();
18937
19265
  for (const parameter of func.getParameters()) {
18938
19266
  const nameNode = parameter.getNameNode();
18939
- if (!import_ts_morph76.Node.isIdentifier(nameNode)) {
19267
+ if (!import_ts_morph77.Node.isIdentifier(nameNode)) {
18940
19268
  continue;
18941
19269
  }
18942
19270
  for (const passed of store.argumentsPassedTo(parameter)) {
18943
- if (!import_ts_morph76.Node.isCallExpression(passed.call)) {
19271
+ if (!import_ts_morph77.Node.isCallExpression(passed.call)) {
18944
19272
  continue;
18945
19273
  }
18946
19274
  const key = siteKey(passed.call);
@@ -18966,7 +19294,7 @@ function valuesAt(site, bindings, store) {
18966
19294
  }
18967
19295
 
18968
19296
  // src/discovery/wrapperIndex.ts
18969
- var import_ts_morph77 = require("ts-morph");
19297
+ var import_ts_morph78 = require("ts-morph");
18970
19298
  var import_resolution9 = require("@suss/resolution");
18971
19299
  var NO_WRAPPERS = {
18972
19300
  wrappersFor: () => [],
@@ -19090,11 +19418,11 @@ function discoverWrapperRegistrations(sourceFile, pattern, match, wraps, resolut
19090
19418
  const candidates = [];
19091
19419
  const unresolved = [];
19092
19420
  sourceFile.forEachDescendant((node) => {
19093
- if (!import_ts_morph77.Node.isCallExpression(node)) {
19421
+ if (!import_ts_morph78.Node.isCallExpression(node)) {
19094
19422
  return;
19095
19423
  }
19096
19424
  const callee = node.getExpression();
19097
- if (!import_ts_morph77.Node.isPropertyAccessExpression(callee) || callee.getName() !== wraps.method) {
19425
+ if (!import_ts_morph78.Node.isPropertyAccessExpression(callee) || callee.getName() !== wraps.method) {
19098
19426
  return;
19099
19427
  }
19100
19428
  const subjectNode = subjectNodeFor(
@@ -19189,7 +19517,7 @@ function discoverConstructorOptions(sourceFile, pattern, wraps, subjects, resolu
19189
19517
  return { candidates, unresolved };
19190
19518
  }
19191
19519
  function constructorOptionOf(construction, wraps, resolution) {
19192
- 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))) {
19193
19521
  return void 0;
19194
19522
  }
19195
19523
  const optionsArg = construction.getArguments()[wraps.targetPosition];
@@ -19244,7 +19572,7 @@ function scopeOf(args, wraps, resolution) {
19244
19572
  }
19245
19573
 
19246
19574
  // src/discoveryContext.ts
19247
- var import_ts_morph78 = require("ts-morph");
19575
+ var import_ts_morph79 = require("ts-morph");
19248
19576
  function createTsDiscoveryContext(resolution) {
19249
19577
  const store = resolution ?? new ResolutionStore();
19250
19578
  return {
@@ -19280,7 +19608,7 @@ function exportedFunctions(sourceFile, resolution) {
19280
19608
  return out;
19281
19609
  }
19282
19610
  function exportedFunction(decl, resolution) {
19283
- const writtenAgain = import_ts_morph78.Node.isVariableDeclaration(decl) && isWrittenAgain(decl);
19611
+ const writtenAgain = import_ts_morph79.Node.isVariableDeclaration(decl) && isWrittenAgain(decl);
19284
19612
  if (!writtenAgain) {
19285
19613
  const declared = resolveDeclarationToFunction(decl);
19286
19614
  if (declared !== null) {
@@ -19301,7 +19629,7 @@ function valueToAskAbout(decl, writtenAgain) {
19301
19629
  if (writtenAgain) {
19302
19630
  return decl;
19303
19631
  }
19304
- 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;
19305
19633
  return couldStillNameAFunction(value) ? value : null;
19306
19634
  }
19307
19635
  function resolveDeclarationToFunction(decl) {
@@ -19309,18 +19637,18 @@ function resolveDeclarationToFunction(decl) {
19309
19637
  if (fn !== null) {
19310
19638
  return fn;
19311
19639
  }
19312
- if (import_ts_morph78.Node.isVariableDeclaration(decl)) {
19640
+ if (import_ts_morph79.Node.isVariableDeclaration(decl)) {
19313
19641
  const init = decl.getInitializer();
19314
- 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))) {
19315
19643
  return init;
19316
19644
  }
19317
19645
  }
19318
19646
  return null;
19319
19647
  }
19320
19648
  function hasJsxReturn(func) {
19321
- if (import_ts_morph78.Node.isArrowFunction(func)) {
19649
+ if (import_ts_morph79.Node.isArrowFunction(func)) {
19322
19650
  const body2 = func.getBody();
19323
- if (import_ts_morph78.Node.isExpression(body2) && isJsxOrFragment(body2)) {
19651
+ if (import_ts_morph79.Node.isExpression(body2) && isJsxOrFragment(body2)) {
19324
19652
  return true;
19325
19653
  }
19326
19654
  }
@@ -19334,11 +19662,11 @@ function hasJsxReturn(func) {
19334
19662
  traversal.stop();
19335
19663
  return;
19336
19664
  }
19337
- 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))) {
19338
19666
  traversal.skip();
19339
19667
  return;
19340
19668
  }
19341
- if (import_ts_morph78.Node.isReturnStatement(node)) {
19669
+ if (import_ts_morph79.Node.isReturnStatement(node)) {
19342
19670
  const expr = node.getExpression();
19343
19671
  if (expr !== void 0 && isJsxOrFragment(expr)) {
19344
19672
  found = true;
@@ -19349,7 +19677,7 @@ function hasJsxReturn(func) {
19349
19677
  }
19350
19678
  function isJsxOrFragment(node) {
19351
19679
  const current2 = peelParens(node);
19352
- 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);
19353
19681
  }
19354
19682
 
19355
19683
  // src/everyUnit.ts
@@ -19376,7 +19704,7 @@ function stampExitCodeFrom(summaries, project, resolution) {
19376
19704
  }
19377
19705
 
19378
19706
  // src/flagReads.ts
19379
- var import_ts_morph79 = require("ts-morph");
19707
+ var import_ts_morph80 = require("ts-morph");
19380
19708
  function flagReads(func, parameterNames2, parsers, resolution, barriers = NO_BARRIERS) {
19381
19709
  if (parsers.length === 0 || parameterNames2.length === 0 || resolution === void 0) {
19382
19710
  return [];
@@ -19409,11 +19737,11 @@ function namedCallsIn(func, barriers) {
19409
19737
  traversal.skip();
19410
19738
  return;
19411
19739
  }
19412
- if (!import_ts_morph79.Node.isCallExpression(node)) {
19740
+ if (!import_ts_morph80.Node.isCallExpression(node)) {
19413
19741
  return;
19414
19742
  }
19415
19743
  const callee = node.getExpression();
19416
- 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)) {
19417
19745
  calls.push(node);
19418
19746
  }
19419
19747
  });
@@ -19631,7 +19959,7 @@ function createReferenceIndex(sourceFiles) {
19631
19959
  }
19632
19960
 
19633
19961
  // src/resolve/clientBasePath.ts
19634
- var import_ts_morph80 = require("ts-morph");
19962
+ var import_ts_morph81 = require("ts-morph");
19635
19963
  var import_behavioral_ir13 = require("@suss/behavioral-ir");
19636
19964
  function clientBasePath(call, match, resolution, site) {
19637
19965
  if (match?.type !== "clientCall" || match.basePathOption === void 0) {
@@ -19673,13 +20001,14 @@ function clientConstructionConfig(call, match, resolution) {
19673
20001
  }
19674
20002
  function receiverOf2(call) {
19675
20003
  const callee = call.getExpression();
19676
- if (import_ts_morph80.Node.isPropertyAccessExpression(callee)) {
20004
+ if (import_ts_morph81.Node.isPropertyAccessExpression(callee)) {
19677
20005
  return callee.getExpression();
19678
20006
  }
19679
- return import_ts_morph80.Node.isIdentifier(callee) ? callee : null;
20007
+ return import_ts_morph81.Node.isIdentifier(callee) ? callee : null;
19680
20008
  }
19681
20009
 
19682
20010
  // src/resolve/closureRecords.ts
20011
+ var import_resolution10 = require("@suss/resolution");
19683
20012
  function fromScanRecord(unit) {
19684
20013
  const files = [unit.file, ...unit.deps];
19685
20014
  const placeOf = ([file, start, end]) => {
@@ -19747,7 +20076,7 @@ function closureUnitRecords(scans, stillValid, references) {
19747
20076
  continue;
19748
20077
  }
19749
20078
  const file = fileOfOffsetKey(key);
19750
- if (file === null) {
20079
+ if (file === null || reachesASubclass(key, scan.findings)) {
19751
20080
  continue;
19752
20081
  }
19753
20082
  const deps = depsOf(file, scan.findings, scan.read, references);
@@ -19763,6 +20092,9 @@ function closureUnitRecords(scans, stillValid, references) {
19763
20092
  }
19764
20093
  return units;
19765
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
+ }
19766
20098
  function filesNamedBy(findings) {
19767
20099
  const files = [];
19768
20100
  for (const call of findings.calls) {
@@ -19851,10 +20183,102 @@ function placeOfKey(key) {
19851
20183
  }
19852
20184
 
19853
20185
  // src/resolve/reachableClosure.ts
19854
- var import_ts_morph81 = require("ts-morph");
20186
+ var import_ts_morph83 = require("ts-morph");
19855
20187
  var import_behavioral_ir14 = require("@suss/behavioral-ir");
19856
20188
  var import_datalog3 = require("@suss/datalog");
19857
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
19858
20282
  var GENERIC_TERMINALS = [
19859
20283
  { kind: "return", match: { type: "returnStatement" }, extraction: {} },
19860
20284
  { kind: "throw", match: { type: "throwExpression" }, extraction: {} },
@@ -19889,19 +20313,35 @@ function targetOf(node) {
19889
20313
  span: { start: node.getStart(), end: node.getEnd() }
19890
20314
  };
19891
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
+ }
19892
20324
  function resolveCallee(call, calleeName, scan) {
19893
- if (!import_ts_morph81.Node.isCallExpression(call)) {
20325
+ if (!import_ts_morph83.Node.isCallExpression(call)) {
19894
20326
  return null;
19895
20327
  }
19896
20328
  const callee = call.getExpression();
19897
20329
  const symbol = callee.getSymbol();
19898
20330
  const declarations = declarationsBehind(symbol);
19899
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
+ }
19900
20340
  if (direct !== null) {
19901
20341
  return { kind: "followed", candidate: direct };
19902
20342
  }
19903
20343
  const resolveSources = scan.resolveCallableSources;
19904
- 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)) {
19905
20345
  if (scan.sourcesAsked !== void 0) {
19906
20346
  scan.sourcesAsked.asked = true;
19907
20347
  }
@@ -19930,7 +20370,7 @@ function resolveCallee(call, calleeName, scan) {
19930
20370
  );
19931
20371
  }
19932
20372
  function stopFor(callee, declarations, stop, scan, parameterIndex) {
19933
- 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));
19934
20374
  return {
19935
20375
  kind: "stopped",
19936
20376
  stop,
@@ -19940,7 +20380,7 @@ function stopFor(callee, declarations, stop, scan, parameterIndex) {
19940
20380
  };
19941
20381
  }
19942
20382
  function storeKeyOf(callee) {
19943
- const declarations = import_ts_morph81.Node.isIdentifier(callee) ? declarationsBehind(callee.getSymbol()) : [];
20383
+ const declarations = import_ts_morph83.Node.isIdentifier(callee) ? declarationsBehind(callee.getSymbol()) : [];
19944
20384
  return declarations.length === 1 ? declarations[0] : callee;
19945
20385
  }
19946
20386
  function settleThroughStore(outcomes, scan) {
@@ -19999,7 +20439,7 @@ function declarationToReport(declarations, calleeName, scan) {
19999
20439
  }
20000
20440
  function declaredAtOf(outcome) {
20001
20441
  if (outcome.kind === "followed") {
20002
- return targetOf(outcome.candidate.func);
20442
+ return placeOfCandidate(outcome.candidate);
20003
20443
  }
20004
20444
  if (outcome.kind === "possible") {
20005
20445
  return null;
@@ -20025,7 +20465,7 @@ function mayDispatch(one) {
20025
20465
  if (one.outcome.askStore !== void 0) {
20026
20466
  return true;
20027
20467
  }
20028
- 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());
20029
20469
  }
20030
20470
  function dispatchOutcome(answer, stopped) {
20031
20471
  const { callee } = stopped.stop;
@@ -20048,18 +20488,18 @@ function dispatchOutcome(answer, stopped) {
20048
20488
  }
20049
20489
  function entryNameOf(func) {
20050
20490
  const property = func.getParent();
20051
- 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);
20052
20492
  if (property === void 0 || key === null) {
20053
20493
  return null;
20054
20494
  }
20055
20495
  const holder = climbSyntax(property.getParentOrThrow()).getParent();
20056
- 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;
20057
20497
  }
20058
20498
  function placeCallee(placements, calleeText, call, outcome) {
20059
20499
  if (outcome.kind === "possible") {
20060
20500
  placements.placePossible(
20061
20501
  calleeText,
20062
- outcome.candidates.map((candidate) => targetOf(candidate.func))
20502
+ outcome.candidates.map(placeOfCandidate)
20063
20503
  );
20064
20504
  return;
20065
20505
  }
@@ -20073,24 +20513,24 @@ function placeCallee(placements, calleeText, call, outcome) {
20073
20513
  }
20074
20514
  }
20075
20515
  function isCallOnOtherValue(call) {
20076
- if (!import_ts_morph81.Node.isCallExpression(call)) {
20516
+ if (!import_ts_morph83.Node.isCallExpression(call)) {
20077
20517
  return false;
20078
20518
  }
20079
20519
  const callee = call.getExpression();
20080
- 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)) {
20081
20521
  return false;
20082
20522
  }
20083
- return !import_ts_morph81.Node.isThisExpression(callee.getExpression());
20523
+ return !import_ts_morph83.Node.isThisExpression(callee.getExpression());
20084
20524
  }
20085
20525
  function passedPositionKey(target, position) {
20086
20526
  return `${offsetKeyFor(target.file, target.span)}#${position}`;
20087
20527
  }
20088
20528
  function resolveJsxReference(node, scan) {
20089
- 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)) {
20090
20530
  return null;
20091
20531
  }
20092
20532
  const tag = node.getTagNameNode();
20093
- 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())) {
20094
20534
  return null;
20095
20535
  }
20096
20536
  const name = tag.getText();
@@ -20112,7 +20552,7 @@ function resolveJsxReference(node, scan) {
20112
20552
  };
20113
20553
  }
20114
20554
  function collectReachable(root, scan) {
20115
- const atModuleScope = import_ts_morph81.Node.isSourceFile(root);
20555
+ const atModuleScope = import_ts_morph83.Node.isSourceFile(root);
20116
20556
  const inFunc = atModuleScope ? { ...scan } : { ...scan, scanning: root };
20117
20557
  const calls = [];
20118
20558
  const stops = [];
@@ -20129,7 +20569,7 @@ function collectReachable(root, scan) {
20129
20569
  }
20130
20570
  };
20131
20571
  const recordFollowed = (candidate) => {
20132
- const key = nodeKey(candidate.func);
20572
+ const key = candidateKey(candidate);
20133
20573
  if (seen.has(key)) {
20134
20574
  return;
20135
20575
  }
@@ -20151,18 +20591,18 @@ function collectReachable(root, scan) {
20151
20591
  recordFollowed(outcome.candidate);
20152
20592
  };
20153
20593
  const recordPassedArgs = (call, calleeText, calleeOutcome) => {
20154
- if (!import_ts_morph81.Node.isCallExpression(call)) {
20594
+ if (!import_ts_morph83.Node.isCallExpression(call)) {
20155
20595
  return;
20156
20596
  }
20157
20597
  const calleeTarget = declaredAtOf(calleeOutcome);
20158
20598
  call.getArguments().forEach((arg, position) => {
20159
- 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)) {
20160
20600
  if (calleeTarget !== null) {
20161
20601
  passedPositions.add(passedPositionKey(calleeTarget, position));
20162
20602
  }
20163
20603
  return;
20164
20604
  }
20165
- if (!import_ts_morph81.Node.isIdentifier(arg)) {
20605
+ if (!import_ts_morph83.Node.isIdentifier(arg)) {
20166
20606
  return;
20167
20607
  }
20168
20608
  const resolved = functionTargetOf(arg);
@@ -20187,7 +20627,7 @@ function collectReachable(root, scan) {
20187
20627
  found.push({ node, outcome: jsx.outcome });
20188
20628
  return;
20189
20629
  }
20190
- if (!import_ts_morph81.Node.isCallExpression(node)) {
20630
+ if (!import_ts_morph83.Node.isCallExpression(node)) {
20191
20631
  return;
20192
20632
  }
20193
20633
  const calleeText = (0, import_behavioral_ir14.normalizeCalleeText)(node.getExpression().getText());
@@ -20291,7 +20731,7 @@ function recognizerOnlyRoots(packsByFile) {
20291
20731
  }
20292
20732
  for (const declaration of variableStatement.getDeclarations()) {
20293
20733
  const init = declaration.getInitializer();
20294
- 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)) {
20295
20735
  roots.push({ func: init, name: declaration.getName() });
20296
20736
  }
20297
20737
  }
@@ -20321,6 +20761,8 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20321
20761
  const passedPositions = /* @__PURE__ */ new Set();
20322
20762
  const summariesByKey = /* @__PURE__ */ new Map();
20323
20763
  const reachedFrom = /* @__PURE__ */ new Map();
20764
+ const receiverByKey = /* @__PURE__ */ new Map();
20765
+ const callsByKey = /* @__PURE__ */ new Map();
20324
20766
  const finding = {
20325
20767
  project,
20326
20768
  lookup,
@@ -20329,7 +20771,8 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20329
20771
  storeAnswers: /* @__PURE__ */ new Map(),
20330
20772
  funcByKey,
20331
20773
  moduleByKey,
20332
- reachedFrom
20774
+ reachedFrom,
20775
+ receiverByKey
20333
20776
  };
20334
20777
  for (const seed of seeds) {
20335
20778
  const func = lookup.functionAt(seed.location);
@@ -20387,25 +20830,53 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20387
20830
  for (const position of found.passedPositions) {
20388
20831
  passedPositions.add(position);
20389
20832
  }
20390
- const callerFile = fileOfOffsetKey(key);
20391
- 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) {
20392
20842
  if (!nameByKey.has(call.key)) {
20393
20843
  nameByKey.set(call.key, call.name);
20394
20844
  }
20395
20845
  if (call.func !== void 0 && !funcByKey.has(call.key)) {
20396
20846
  funcByKey.set(call.key, call.func);
20397
20847
  }
20848
+ if (call.receiverClass !== void 0) {
20849
+ receiverByKey.set(call.key, call.receiverClass);
20850
+ }
20398
20851
  if (callerFile !== null && !reachedFrom.has(call.key)) {
20399
20852
  reachedFrom.set(call.key, callerFile);
20400
20853
  }
20401
20854
  db.add("calls", [key, call.key]);
20855
+ called.push(call.key);
20402
20856
  }
20857
+ callsByKey.set(key, called);
20403
20858
  }
20404
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
+ );
20405
20875
  const reached = [];
20406
20876
  for (const [keyAtom] of db.facts("reachable")) {
20407
20877
  const key = String(keyAtom);
20408
- 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)) {
20409
20880
  continue;
20410
20881
  }
20411
20882
  const name = nameByKey.get(key);
@@ -20416,7 +20887,7 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20416
20887
  if (projectFileSet !== void 0) {
20417
20888
  lazyAddSourceFile(project, projectFileSet, file);
20418
20889
  }
20419
- const func = funcByKey.get(key) ?? functionAtKey(key, lookup);
20890
+ const func = funcByKey.get(key) ?? functionAtKey((0, import_resolution11.plainKeyOf)(key), lookup);
20420
20891
  if (func === void 0) {
20421
20892
  continue;
20422
20893
  }
@@ -20426,6 +20897,10 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20426
20897
  facts,
20427
20898
  () => extractReachableSummary(candidate, options, recognizers)
20428
20899
  );
20900
+ if (receiverClass !== void 0) {
20901
+ summary.receiverClass = subclassPlace(receiverClass);
20902
+ summary.identity.exportPath = [receiverClass.name, name];
20903
+ }
20429
20904
  facts?.unitKeyBySummary.set(summary, key);
20430
20905
  rememberSummary(summariesByKey, key, summary);
20431
20906
  reached.push(summary);
@@ -20482,14 +20957,15 @@ function knownUnits(summaries, lookup, facts) {
20482
20957
  }
20483
20958
  function findingsFor(key, ctx) {
20484
20959
  const cameFrom = ctx.reachedFrom.get(key);
20485
- 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;
20486
20962
  const recorded = stored !== void 0 && recordValidFrom(stored.data, cameFrom) ? fromScanRecord(stored) : null;
20487
20963
  if (stored !== void 0 && recorded !== null) {
20488
20964
  ctx.facts?.scans?.set(key, { kind: "reused", unit: stored });
20489
20965
  noteFilesRead(key, ctx.facts, stored.deps);
20490
20966
  return recorded;
20491
20967
  }
20492
- 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);
20493
20969
  if (root === void 0) {
20494
20970
  return null;
20495
20971
  }
@@ -20500,7 +20976,8 @@ function findingsFor(key, ctx) {
20500
20976
  ...resolution === void 0 ? {} : { resolution, storeAnswers: ctx.storeAnswers },
20501
20977
  ...resolveCallableSources === void 0 ? {} : { resolveCallableSources, sourcesAsked },
20502
20978
  ...sourceDeclarationsBehind2 === void 0 ? {} : { sourceDeclarationsBehind: sourceDeclarationsBehind2 },
20503
- ...reachedFrom === void 0 ? {} : { reachedFrom }
20979
+ ...reachedFrom === void 0 ? {} : { reachedFrom },
20980
+ ...receiverClass === void 0 ? {} : { receiverClass }
20504
20981
  };
20505
20982
  const scanned = scanOrReport(key, ctx.facts, root, scan);
20506
20983
  if (scanned === null) {
@@ -20520,7 +20997,7 @@ function scanOrReport(key, facts, root, scan) {
20520
20997
  return scanWithRecording(key, facts, () => collectReachable(root, scan));
20521
20998
  } catch (error) {
20522
20999
  const message = error instanceof Error ? error.message : String(error);
20523
- 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()}`;
20524
21001
  process.stderr.write(
20525
21002
  `[suss] could not scan the function at ${where} for the calls it makes, so nothing it calls is followed from it: ${message}
20526
21003
  `
@@ -20644,7 +21121,7 @@ function recordParameterGaps(parameterCallsByKey, summariesByKey, passedPosition
20644
21121
  }
20645
21122
  for (const [key, parameterCalls] of parameterCallsByKey) {
20646
21123
  const unbound = parameterCalls.filter(
20647
- ({ parameterIndex }) => !passedPositions.has(`${key}#${parameterIndex}`)
21124
+ ({ parameterIndex }) => !passedPositions.has(`${(0, import_resolution11.plainKeyOf)(key)}#${parameterIndex}`)
20648
21125
  );
20649
21126
  if (unbound.length === 0) {
20650
21127
  continue;
@@ -20661,7 +21138,7 @@ function recordParameterGaps(parameterCallsByKey, summariesByKey, passedPosition
20661
21138
 
20662
21139
  // src/resolve/redirectDelivery.ts
20663
21140
  var import_extractor16 = require("@suss/extractor");
20664
- var import_values13 = require("@suss/values");
21141
+ var import_values14 = require("@suss/values");
20665
21142
  function settingOf(object, name, resolution, site) {
20666
21143
  if (object === void 0) {
20667
21144
  return { kind: "absent" };
@@ -20677,7 +21154,7 @@ function settingOf(object, name, resolution, site) {
20677
21154
  if (field.presence !== "one") {
20678
21155
  return { kind: "unknown" };
20679
21156
  }
20680
- 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));
20681
21158
  return settled3 === null ? { kind: "unknown" } : { kind: "settled", value: settled3 };
20682
21159
  }
20683
21160
  function redirectDeliveryAtCall(callSite, pattern, pack, resolution) {
@@ -20702,7 +21179,7 @@ function redirectDeliveryAtCall(callSite, pattern, pack, resolution) {
20702
21179
  }
20703
21180
 
20704
21181
  // src/resolve/rethrowEnrichment.ts
20705
- var import_ts_morph82 = require("ts-morph");
21182
+ var import_ts_morph84 = require("ts-morph");
20706
21183
  var import_datalog4 = require("@suss/datalog");
20707
21184
  var RETHROW_RULES = [
20708
21185
  (0, import_datalog4.rule)(
@@ -20839,7 +21316,7 @@ function findBareRethrow(func, location) {
20839
21316
  traversal.stop();
20840
21317
  return;
20841
21318
  }
20842
- if (!import_ts_morph82.Node.isThrowStatement(node)) {
21319
+ if (!import_ts_morph84.Node.isThrowStatement(node)) {
20843
21320
  return;
20844
21321
  }
20845
21322
  if (startLineOf(node) !== location.start) {
@@ -20849,7 +21326,7 @@ function findBareRethrow(func, location) {
20849
21326
  return;
20850
21327
  }
20851
21328
  const thrown = node.getExpression();
20852
- if (thrown !== void 0 && import_ts_morph82.Node.isIdentifier(thrown)) {
21329
+ if (thrown !== void 0 && import_ts_morph84.Node.isIdentifier(thrown)) {
20853
21330
  found = node;
20854
21331
  }
20855
21332
  });
@@ -20859,13 +21336,13 @@ function enclosingTry(throwStmt) {
20859
21336
  let current2 = throwStmt.getParent();
20860
21337
  let sawCatchClause = false;
20861
21338
  while (current2 !== void 0) {
20862
- if (import_ts_morph82.Node.isCatchClause(current2)) {
21339
+ if (import_ts_morph84.Node.isCatchClause(current2)) {
20863
21340
  sawCatchClause = true;
20864
21341
  }
20865
- if (import_ts_morph82.Node.isTryStatement(current2)) {
21342
+ if (import_ts_morph84.Node.isTryStatement(current2)) {
20866
21343
  return sawCatchClause ? current2 : null;
20867
21344
  }
20868
- 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)) {
20869
21346
  return null;
20870
21347
  }
20871
21348
  current2 = current2.getParent();
@@ -20873,14 +21350,14 @@ function enclosingTry(throwStmt) {
20873
21350
  return null;
20874
21351
  }
20875
21352
  function collectTryBodyCallees(tryStmt, index, keyFor) {
20876
- if (!import_ts_morph82.Node.isTryStatement(tryStmt)) {
21353
+ if (!import_ts_morph84.Node.isTryStatement(tryStmt)) {
20877
21354
  return [];
20878
21355
  }
20879
21356
  const tryBlock = tryStmt.getTryBlock();
20880
21357
  const callees = [];
20881
21358
  const seen = /* @__PURE__ */ new Set();
20882
21359
  tryBlock.forEachDescendant((node) => {
20883
- if (!import_ts_morph82.Node.isCallExpression(node)) {
21360
+ if (!import_ts_morph84.Node.isCallExpression(node)) {
20884
21361
  return;
20885
21362
  }
20886
21363
  const calleeSummary = resolveCalleeSummary(node, index);
@@ -20897,7 +21374,7 @@ function collectTryBodyCallees(tryStmt, index, keyFor) {
20897
21374
  return callees;
20898
21375
  }
20899
21376
  function resolveCalleeSummary(call, index) {
20900
- if (!import_ts_morph82.Node.isCallExpression(call)) {
21377
+ if (!import_ts_morph84.Node.isCallExpression(call)) {
20901
21378
  return null;
20902
21379
  }
20903
21380
  const callee = call.getExpression();
@@ -20929,7 +21406,7 @@ function resolveCalleeSummary(call, index) {
20929
21406
  }
20930
21407
 
20931
21408
  // src/subUnitContext.ts
20932
- var import_ts_morph83 = require("ts-morph");
21409
+ var import_ts_morph85 = require("ts-morph");
20933
21410
  function createTsSubUnitContext() {
20934
21411
  return {
20935
21412
  findJsxAttributes,
@@ -20956,18 +21433,18 @@ function findJsxAttributes(parent) {
20956
21433
  if (skip(node, traversal)) {
20957
21434
  return;
20958
21435
  }
20959
- 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)) {
20960
21437
  return;
20961
21438
  }
20962
21439
  const tag = node.getTagNameNode().getText();
20963
21440
  for (const attr of node.getAttributes()) {
20964
- if (!import_ts_morph83.Node.isJsxAttribute(attr)) {
21441
+ if (!import_ts_morph85.Node.isJsxAttribute(attr)) {
20965
21442
  continue;
20966
21443
  }
20967
21444
  const initializer = attr.getInitializer();
20968
21445
  let valueExpression = null;
20969
21446
  if (initializer !== void 0) {
20970
- if (import_ts_morph83.Node.isJsxExpression(initializer)) {
21447
+ if (import_ts_morph85.Node.isJsxExpression(initializer)) {
20971
21448
  const inner = initializer.getExpression();
20972
21449
  valueExpression = inner ?? null;
20973
21450
  } else {
@@ -20990,11 +21467,11 @@ function findCallExpressionsByName(parent, name) {
20990
21467
  if (skip(node, traversal)) {
20991
21468
  return;
20992
21469
  }
20993
- if (!import_ts_morph83.Node.isCallExpression(node)) {
21470
+ if (!import_ts_morph85.Node.isCallExpression(node)) {
20994
21471
  return;
20995
21472
  }
20996
21473
  const callee = node.getExpression();
20997
- if (import_ts_morph83.Node.isIdentifier(callee) && callee.getText() === name) {
21474
+ if (import_ts_morph85.Node.isIdentifier(callee) && callee.getText() === name) {
20998
21475
  out.push(node);
20999
21476
  }
21000
21477
  });
@@ -21005,10 +21482,10 @@ function resolveAttributeValueFunction(attr, parent) {
21005
21482
  if (expr === null) {
21006
21483
  return null;
21007
21484
  }
21008
- 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)) {
21009
21486
  return { func: expr, localName: null };
21010
21487
  }
21011
- if (import_ts_morph83.Node.isIdentifier(expr)) {
21488
+ if (import_ts_morph85.Node.isIdentifier(expr)) {
21012
21489
  const symbol = expr.getSymbol();
21013
21490
  if (symbol === void 0) {
21014
21491
  return null;
@@ -21041,7 +21518,7 @@ function getCallArgumentFunction(call, position) {
21041
21518
  if (arg === void 0) {
21042
21519
  return null;
21043
21520
  }
21044
- 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)) {
21045
21522
  return arg;
21046
21523
  }
21047
21524
  return null;
@@ -21053,7 +21530,7 @@ function readArrayLiteralText(node) {
21053
21530
  if (node === null) {
21054
21531
  return null;
21055
21532
  }
21056
- if (import_ts_morph83.Node.isArrayLiteralExpression(node)) {
21533
+ if (import_ts_morph85.Node.isArrayLiteralExpression(node)) {
21057
21534
  return node.getElements().map((e) => e.getText());
21058
21535
  }
21059
21536
  return [node.getText()];
@@ -21067,7 +21544,7 @@ var import_files2 = require("@suss/behavioral-ir/files");
21067
21544
  // src/bootstrap/noTsconfigProject.ts
21068
21545
  var import_node_fs5 = __toESM(require("fs"), 1);
21069
21546
  var import_node_path12 = __toESM(require("path"), 1);
21070
- var import_ts_morph84 = require("ts-morph");
21547
+ var import_ts_morph86 = require("ts-morph");
21071
21548
  var SOURCE_SUFFIXES = [
21072
21549
  ".ts",
21073
21550
  ".tsx",
@@ -21121,12 +21598,12 @@ function findSourceFiles(rootDir, depth = 0) {
21121
21598
  return found;
21122
21599
  }
21123
21600
  function createProjectWithoutTsconfig(rootDir) {
21124
- const project = new import_ts_morph84.Project({
21601
+ const project = new import_ts_morph86.Project({
21125
21602
  skipAddingFilesFromTsConfig: true,
21126
21603
  compilerOptions: {
21127
- target: import_ts_morph84.ScriptTarget.ES2022,
21128
- module: import_ts_morph84.ModuleKind.ESNext,
21129
- 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,
21130
21607
  allowJs: true,
21131
21608
  checkJs: false,
21132
21609
  noEmit: true,
@@ -21450,13 +21927,13 @@ var raise = (msg) => {
21450
21927
  throw new Error(msg);
21451
21928
  };
21452
21929
  function bindingPatternNames(nameNode) {
21453
- if (import_ts_morph85.Node.isObjectBindingPattern(nameNode)) {
21930
+ if (import_ts_morph87.Node.isObjectBindingPattern(nameNode)) {
21454
21931
  return nameNode.getElements().map((e) => e.getName());
21455
21932
  }
21456
- if (import_ts_morph85.Node.isArrayBindingPattern(nameNode)) {
21933
+ if (import_ts_morph87.Node.isArrayBindingPattern(nameNode)) {
21457
21934
  const names = [];
21458
21935
  for (const element of nameNode.getElements()) {
21459
- if (import_ts_morph85.Node.isOmittedExpression(element)) {
21936
+ if (import_ts_morph87.Node.isOmittedExpression(element)) {
21460
21937
  continue;
21461
21938
  }
21462
21939
  names.push(element.getName());
@@ -21486,7 +21963,7 @@ function objectParamInputs(param, position, mapping) {
21486
21963
  }
21487
21964
  function componentPropsParameters(param, mapping) {
21488
21965
  const nameNode = param.getNameNode();
21489
- if (!import_ts_morph85.Node.isObjectBindingPattern(nameNode)) {
21966
+ if (!import_ts_morph87.Node.isObjectBindingPattern(nameNode)) {
21490
21967
  const type = param.getType();
21491
21968
  return [
21492
21969
  {
@@ -21513,15 +21990,15 @@ function componentPropsParameters(param, mapping) {
21513
21990
  }
21514
21991
  function namesBoundUnder(element, field) {
21515
21992
  const inner = element.getNameNode();
21516
- if (import_ts_morph85.Node.isObjectBindingPattern(inner)) {
21993
+ if (import_ts_morph87.Node.isObjectBindingPattern(inner)) {
21517
21994
  return inner.getElements().flatMap((nested) => {
21518
21995
  const property = nested.getDotDotDotToken() === void 0 ? bindingRole(nested, nested.getName()) : void 0;
21519
21996
  return namesBoundUnder(nested, field ?? property);
21520
21997
  });
21521
21998
  }
21522
- if (import_ts_morph85.Node.isArrayBindingPattern(inner)) {
21999
+ if (import_ts_morph87.Node.isArrayBindingPattern(inner)) {
21523
22000
  return inner.getElements().flatMap(
21524
- (nested) => import_ts_morph85.Node.isBindingElement(nested) ? namesBoundUnder(nested, field) : []
22001
+ (nested) => import_ts_morph87.Node.isBindingElement(nested) ? namesBoundUnder(nested, field) : []
21525
22002
  );
21526
22003
  }
21527
22004
  return [{ binding: element, field }];
@@ -21534,7 +22011,7 @@ function bindingRole(element, name) {
21534
22011
  if (property === void 0) {
21535
22012
  return name;
21536
22013
  }
21537
- if (import_ts_morph85.Node.isStringLiteral(property)) {
22014
+ if (import_ts_morph87.Node.isStringLiteral(property)) {
21538
22015
  return property.getLiteralValue();
21539
22016
  }
21540
22017
  return property.getText();
@@ -21566,7 +22043,7 @@ function extractParameters(func, inputMapping) {
21566
22043
  const param = params[i];
21567
22044
  const nameNode = param.getNameNode();
21568
22045
  const boundNames = bindingPatternNames(nameNode);
21569
- if (import_ts_morph85.Node.isObjectBindingPattern(nameNode)) {
22046
+ if (import_ts_morph87.Node.isObjectBindingPattern(nameNode)) {
21570
22047
  for (const element of nameNode.getElements()) {
21571
22048
  result.push({
21572
22049
  name: element.getName(),
@@ -21633,7 +22110,7 @@ function extractDependencyCalls(func, barriers = NO_BARRIERS) {
21633
22110
  traversal.skip();
21634
22111
  return;
21635
22112
  }
21636
- if (!import_ts_morph85.Node.isVariableDeclaration(node)) {
22113
+ if (!import_ts_morph87.Node.isVariableDeclaration(node)) {
21637
22114
  return;
21638
22115
  }
21639
22116
  const init = node.getInitializer();
@@ -21642,19 +22119,19 @@ function extractDependencyCalls(func, barriers = NO_BARRIERS) {
21642
22119
  }
21643
22120
  let isAsync = false;
21644
22121
  let callExpr = init;
21645
- if (import_ts_morph85.Node.isAwaitExpression(init)) {
22122
+ if (import_ts_morph87.Node.isAwaitExpression(init)) {
21646
22123
  isAsync = true;
21647
22124
  callExpr = init.getExpression();
21648
22125
  }
21649
- if (!import_ts_morph85.Node.isCallExpression(callExpr)) {
22126
+ if (!import_ts_morph87.Node.isCallExpression(callExpr)) {
21650
22127
  return;
21651
22128
  }
21652
22129
  const calleeName = callExpr.getExpression().getText();
21653
22130
  const nameNode = node.getNameNode();
21654
- const assignedTo = import_ts_morph85.Node.isIdentifier(nameNode) ? nameNode.getText() : null;
22131
+ const assignedTo = import_ts_morph87.Node.isIdentifier(nameNode) ? nameNode.getText() : null;
21655
22132
  const declList = node.getParent();
21656
22133
  const varStmt = declList?.getParent();
21657
- 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;
21658
22135
  results.push({
21659
22136
  name: calleeName,
21660
22137
  assignedTo,
@@ -22395,6 +22872,7 @@ function extractFromSourceFile(sourceFile, frameworks, claimedUnits, options, ta
22395
22872
  if (unit.operationInfo.unresolved !== void 0) {
22396
22873
  raw.graphqlUnresolvedDocument = unit.operationInfo.unresolved;
22397
22874
  }
22875
+ stampClientChosenAtCall(raw, unit, pack, resolution);
22398
22876
  for (const v2 of unit.operationInfo.variables) {
22399
22877
  raw.parameters.push({
22400
22878
  name: v2.name,
@@ -22435,17 +22913,18 @@ function extractFromSourceFile(sourceFile, frameworks, claimedUnits, options, ta
22435
22913
  if (binding.semantics.name === "rest" && binding.semantics.method !== null) {
22436
22914
  const defaultBranch = raw.branches.find((b) => b.isDefault);
22437
22915
  if (defaultBranch !== void 0) {
22438
- const calleeText = unit.callSite.callExpression.getExpression().getText();
22916
+ const call = unit.callSite.callExpression;
22439
22917
  defaultBranch.extraEffects = [
22440
22918
  ...defaultBranch.extraEffects ?? [],
22441
22919
  {
22442
22920
  type: "interaction",
22443
22921
  binding,
22444
- callee: calleeText,
22922
+ callee: call.getExpression().getText(),
22445
22923
  interaction: {
22446
22924
  class: "service-call",
22447
22925
  method: binding.semantics.method
22448
- }
22926
+ },
22927
+ location: lineRangeOf(call)
22449
22928
  }
22450
22929
  ];
22451
22930
  }
@@ -22569,6 +23048,23 @@ function stampRequestSpelling(raw, pack) {
22569
23048
  }
22570
23049
  raw.requestSpelling = pack.requestSpelling;
22571
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
+ }
22572
23068
  function unfollowedStopsOf(unit, wrappers) {
22573
23069
  const stops = [
22574
23070
  ...unit.unfollowed === void 0 ? [] : [unit.unfollowed],
@@ -22586,7 +23082,7 @@ function emitLibraryEnvReadMarkers(summaries, project, frameworks) {
22586
23082
  if (importer === void 0) {
22587
23083
  continue;
22588
23084
  }
22589
- summaries.push(libraryEnvReadMarker(pack, declared, importer));
23085
+ summaries.push((0, import_extractor20.libraryEnvReadMarker)(pack.name, declared, importer));
22590
23086
  }
22591
23087
  }
22592
23088
  }
@@ -22634,31 +23130,6 @@ function schemaDocumentSummary(label, schemaSdl) {
22634
23130
  )
22635
23131
  };
22636
23132
  }
22637
- function libraryEnvReadMarker(pack, declared, importer) {
22638
- return {
22639
- kind: "library",
22640
- location: { file: importer, range: { start: 0, end: 0 }, exportName: null },
22641
- identity: {
22642
- name: `${declared.module} env reads`,
22643
- exportPath: null,
22644
- boundaryBinding: (0, import_behavioral_ir16.functionCallBinding)({
22645
- transport: "in-process",
22646
- recognition: pack.name
22647
- })
22648
- },
22649
- inputs: [],
22650
- transitions: [],
22651
- gaps: [],
22652
- confidence: { source: "declared", level: "high" },
22653
- metadata: {
22654
- libraryEnvReads: {
22655
- module: declared.module,
22656
- ...declared.prefixes !== void 0 ? { prefixes: declared.prefixes } : {},
22657
- ...declared.names !== void 0 ? { names: declared.names } : {}
22658
- }
22659
- }
22660
- };
22661
- }
22662
23133
  function internalImportsOf(project, file) {
22663
23134
  const sourceFile = project.getSourceFile(file);
22664
23135
  if (sourceFile === void 0) {
@@ -22754,12 +23225,12 @@ function sinkIn(func, pack, summary, discovered, resolution) {
22754
23225
  }
22755
23226
  function sinkReading(sink, bindings, resolution) {
22756
23227
  const url = sinkUrl(sink, bindings, resolution);
22757
- 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));
22758
23229
  if (url === void 0 || path17 === void 0) {
22759
23230
  return null;
22760
23231
  }
22761
23232
  const method = sinkMethod(sink, bindings, resolution);
22762
- const host = (0, import_values14.hostOf)(url);
23233
+ const host = (0, import_values15.hostOf)(url);
22763
23234
  return method === void 0 ? null : { path: path17, method, ...host === void 0 ? {} : { host } };
22764
23235
  }
22765
23236
  function sinkArgumentValue(sink, position, bindings, resolution) {
@@ -22771,7 +23242,7 @@ function fieldValue2(record2, name) {
22771
23242
  return void 0;
22772
23243
  }
22773
23244
  const field = record2.fields.get(name);
22774
- 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);
22775
23246
  }
22776
23247
  function sinkUrl(sink, bindings, resolution) {
22777
23248
  const p = sink.extraction.path;
@@ -22803,7 +23274,7 @@ function sinkMethod(sink, bindings, resolution) {
22803
23274
  if (value === void 0) {
22804
23275
  return record2.open ? void 0 : m.default;
22805
23276
  }
22806
- return (0, import_values14.literalOf)(value) ?? void 0;
23277
+ return (0, import_values15.literalOf)(value) ?? void 0;
22807
23278
  }
22808
23279
  function statedPath(path17) {
22809
23280
  if (path17 === void 0) {
@@ -22831,17 +23302,17 @@ function synthesizeCallerSummaries(wrapper, resolution, options) {
22831
23302
  return out;
22832
23303
  }
22833
23304
  function callerName(func) {
22834
- 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)) {
22835
23306
  return func.getName() ?? "anonymous";
22836
23307
  }
22837
23308
  const parent = func.getParent();
22838
- if (parent !== void 0 && import_ts_morph85.Node.isVariableDeclaration(parent)) {
23309
+ if (parent !== void 0 && import_ts_morph87.Node.isVariableDeclaration(parent)) {
22839
23310
  const nameNode = parent.getNameNode();
22840
- if (import_ts_morph85.Node.isIdentifier(nameNode)) {
23311
+ if (import_ts_morph87.Node.isIdentifier(nameNode)) {
22841
23312
  return nameNode.getText();
22842
23313
  }
22843
23314
  }
22844
- if (parent !== void 0 && import_ts_morph85.Node.isPropertyAssignment(parent)) {
23315
+ if (parent !== void 0 && import_ts_morph87.Node.isPropertyAssignment(parent)) {
22845
23316
  return parent.getName();
22846
23317
  }
22847
23318
  return "anonymous";
@@ -22964,7 +23435,7 @@ function createTypeScriptAdapter(suppliedConfig) {
22964
23435
  ]
22965
23436
  };
22966
23437
  const newProject = () => {
22967
- const made = new import_ts_morph85.Project(
23438
+ const made = new import_ts_morph87.Project(
22968
23439
  config.tsConfigFilePath !== void 0 ? {
22969
23440
  tsConfigFilePath: config.tsConfigFilePath,
22970
23441
  skipAddingFilesFromTsConfig: true
@@ -23881,8 +24352,8 @@ function buildSubUnitSummary(subUnit, parent, parentFunc, recognizers, everyUnit
23881
24352
 
23882
24353
  // src/facts/why.ts
23883
24354
  var import_node_path16 = __toESM(require("path"), 1);
23884
- var import_ts_morph86 = require("ts-morph");
23885
- var import_resolution10 = require("@suss/resolution");
24355
+ var import_ts_morph88 = require("ts-morph");
24356
+ var import_resolution12 = require("@suss/resolution");
23886
24357
  var TypeScriptWhySession = class {
23887
24358
  project;
23888
24359
  store;
@@ -23890,7 +24361,7 @@ var TypeScriptWhySession = class {
23890
24361
  constructor(options) {
23891
24362
  this.root = import_node_path16.default.resolve(options.dir);
23892
24363
  const tsconfig = findNearestTsconfig(this.root);
23893
- 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 });
23894
24365
  this.store = ResolutionStore.forPacks(options.packs ?? []);
23895
24366
  }
23896
24367
  /**
@@ -23898,14 +24369,14 @@ var TypeScriptWhySession = class {
23898
24369
  * `text`, or null. Spelled from the person's side: the name as
23899
24370
  * written, not a node id.
23900
24371
  */
23901
- findExpression(file, line, text2) {
24372
+ findExpression(file, line, text3) {
23902
24373
  const sourceFile = this.sourceFileAt(file);
23903
24374
  if (sourceFile === null) {
23904
24375
  return null;
23905
24376
  }
23906
24377
  let found = null;
23907
24378
  sourceFile.forEachDescendant((node) => {
23908
- 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())) {
23909
24380
  found = node;
23910
24381
  }
23911
24382
  });
@@ -23923,7 +24394,7 @@ var TypeScriptWhySession = class {
23923
24394
  return null;
23924
24395
  }
23925
24396
  for (const call of sourceFile.getDescendantsOfKind(
23926
- import_ts_morph86.SyntaxKind.CallExpression
24397
+ import_ts_morph88.SyntaxKind.CallExpression
23927
24398
  )) {
23928
24399
  const line = call.getStartLineNumber();
23929
24400
  if (line < startLine || line > endLine) {
@@ -23953,14 +24424,14 @@ var TypeScriptWhySession = class {
23953
24424
  const at = this.locate(node);
23954
24425
  return `${at.name} (${at.file}:${at.line})`;
23955
24426
  };
23956
- const explanation = (0, import_resolution10.explainResolutionProof)(explained.proof, { describe });
24427
+ const explanation = (0, import_resolution12.explainResolutionProof)(explained.proof, { describe });
23957
24428
  if (explanation === null) {
23958
24429
  return null;
23959
24430
  }
23960
24431
  return {
23961
24432
  explanation,
23962
24433
  chain: explanation.atoms.map((atom) => describe(atom)),
23963
- lines: (0, import_resolution10.renderExplanation)(explanation, describe),
24434
+ lines: (0, import_resolution12.renderExplanation)(explanation, describe),
23964
24435
  target: this.locate(explained.target),
23965
24436
  stats: explained.stats
23966
24437
  };
@@ -23994,31 +24465,31 @@ var TypeScriptWhySession = class {
23994
24465
  }
23995
24466
  };
23996
24467
  function displayNameOf(node) {
23997
- 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)) {
23998
24469
  return node.getText();
23999
24470
  }
24000
- 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)) {
24001
24472
  return `${node.getExpression()?.getText() ?? "call"}(...)`;
24002
24473
  }
24003
- 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)) {
24004
24475
  const name = node.getName();
24005
24476
  if (name !== void 0) {
24006
24477
  return name;
24007
24478
  }
24008
24479
  }
24009
- 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)) {
24010
24481
  const parent = node.getParent();
24011
- if (parent !== void 0 && import_ts_morph86.Node.isVariableDeclaration(parent)) {
24482
+ if (parent !== void 0 && import_ts_morph88.Node.isVariableDeclaration(parent)) {
24012
24483
  return parent.getName();
24013
24484
  }
24014
24485
  }
24015
- const text2 = node.getText().split("\n")[0].trim();
24016
- 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;
24017
24488
  }
24018
24489
 
24019
24490
  // src/stubEvidence.ts
24020
24491
  var import_node_path17 = __toESM(require("path"), 1);
24021
- var import_ts_morph87 = require("ts-morph");
24492
+ var import_ts_morph89 = require("ts-morph");
24022
24493
  function stubEvidenceIn(project, packageName, root) {
24023
24494
  const resolution = new ResolutionStore();
24024
24495
  const byExport = /* @__PURE__ */ new Map();
@@ -24045,7 +24516,7 @@ function stubEvidenceIn(project, packageName, root) {
24045
24516
  );
24046
24517
  }
24047
24518
  function stubEvidence(options) {
24048
- const project = new import_ts_morph87.Project(
24519
+ const project = new import_ts_morph89.Project(
24049
24520
  options.tsConfigFilePath !== void 0 ? { tsConfigFilePath: options.tsConfigFilePath } : { compilerOptions: { allowJs: true } }
24050
24521
  );
24051
24522
  const root = options.tsConfigFilePath !== void 0 ? import_node_path17.default.dirname(options.tsConfigFilePath) : import_node_path17.default.resolve(options.directory ?? process.cwd());
@@ -24059,9 +24530,9 @@ function stubEvidence(options) {
24059
24530
  }
24060
24531
 
24061
24532
  // src/walk/defaulted.ts
24062
- var import_ts_morph88 = require("ts-morph");
24533
+ var import_ts_morph90 = require("ts-morph");
24063
24534
  function isDefaultedAt(node) {
24064
- if (!import_ts_morph88.Node.isBindingElement(node)) {
24535
+ if (!import_ts_morph90.Node.isBindingElement(node)) {
24065
24536
  const read2 = climbSyntax(node);
24066
24537
  const inner = peelSyntax(read2);
24067
24538
  const subject = {
@@ -24078,7 +24549,7 @@ function isDefaultedAt(node) {
24078
24549
  }
24079
24550
  function isSubject(subject, expression) {
24080
24551
  if (subject.kind === "local") {
24081
- return import_ts_morph88.Node.isIdentifier(expression) && refersTo(expression, subject.local);
24552
+ return import_ts_morph90.Node.isIdentifier(expression) && refersTo(expression, subject.local);
24082
24553
  }
24083
24554
  return expression.getText() === subject.text;
24084
24555
  }
@@ -24092,7 +24563,7 @@ function hasFallbackOperand(node) {
24092
24563
  let child = climbSyntax(node);
24093
24564
  let parent = child.getParent();
24094
24565
  while (parent !== void 0) {
24095
- if (!import_ts_morph88.Node.isBinaryExpression(parent)) {
24566
+ if (!import_ts_morph90.Node.isBinaryExpression(parent)) {
24096
24567
  return false;
24097
24568
  }
24098
24569
  const operator = parent.getOperatorToken().getText();
@@ -24113,8 +24584,8 @@ function isPresenceTest(node, subject) {
24113
24584
  let throughLogical = false;
24114
24585
  let parent = child.getParent();
24115
24586
  while (parent !== void 0) {
24116
- if (!import_ts_morph88.Node.isBinaryExpression(parent)) {
24117
- 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)) {
24118
24589
  return isTestedAt(parent, child, subject, { tested, throughLogical });
24119
24590
  }
24120
24591
  tested = true;
@@ -24140,22 +24611,22 @@ function logicalStep(parent, child) {
24140
24611
  return null;
24141
24612
  }
24142
24613
  const operand2 = peelSyntax(child);
24143
- 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;
24144
24615
  }
24145
24616
  function isTestedAt(parent, child, subject, seen) {
24146
24617
  if (conditionOf2(parent) === child) {
24147
24618
  return !absentPathThrows(parent, subject);
24148
24619
  }
24149
- if (seen.throughLogical && import_ts_morph88.Node.isExpressionStatement(parent)) {
24620
+ if (seen.throughLogical && import_ts_morph90.Node.isExpressionStatement(parent)) {
24150
24621
  return !continuationThrows(parent);
24151
24622
  }
24152
24623
  return seen.tested;
24153
24624
  }
24154
24625
  function conditionOf2(node) {
24155
- 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)) {
24156
24627
  return node.getExpression();
24157
24628
  }
24158
- 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)) {
24159
24630
  return node.getCondition();
24160
24631
  }
24161
24632
  return void 0;
@@ -24173,24 +24644,24 @@ function isPresentAt(node, subject) {
24173
24644
  return false;
24174
24645
  }
24175
24646
  function isBranchWherePresent(parent, child, subject) {
24176
- if (import_ts_morph88.Node.isIfStatement(parent)) {
24647
+ if (import_ts_morph90.Node.isIfStatement(parent)) {
24177
24648
  return isBranchTaken(parent.getExpression(), child, subject, {
24178
24649
  whenTrue: parent.getThenStatement(),
24179
24650
  whenFalse: parent.getElseStatement()
24180
24651
  });
24181
24652
  }
24182
- if (import_ts_morph88.Node.isConditionalExpression(parent)) {
24653
+ if (import_ts_morph90.Node.isConditionalExpression(parent)) {
24183
24654
  return isBranchTaken(parent.getCondition(), child, subject, {
24184
24655
  whenTrue: parent.getWhenTrue(),
24185
24656
  whenFalse: parent.getWhenFalse()
24186
24657
  });
24187
24658
  }
24188
- if (import_ts_morph88.Node.isWhileStatement(parent)) {
24659
+ if (import_ts_morph90.Node.isWhileStatement(parent)) {
24189
24660
  return isBranchTaken(parent.getExpression(), child, subject, {
24190
24661
  whenTrue: parent.getStatement()
24191
24662
  });
24192
24663
  }
24193
- if (!import_ts_morph88.Node.isBinaryExpression(parent) || parent.getRight() !== child) {
24664
+ if (!import_ts_morph90.Node.isBinaryExpression(parent) || parent.getRight() !== child) {
24194
24665
  return false;
24195
24666
  }
24196
24667
  const operator = parent.getOperatorToken().getText();
@@ -24203,16 +24674,16 @@ function isBranchTaken(test, child, subject, branches) {
24203
24674
  return child === branches.whenFalse && isPresentWhen(test, false, subject);
24204
24675
  }
24205
24676
  function followsExitWhenAbsent(parent, child, subject) {
24206
- 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))) {
24207
24678
  return false;
24208
24679
  }
24209
24680
  const statements = parent.getStatements();
24210
24681
  return statements.slice(0, statements.indexOf(child)).some(
24211
- (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)
24212
24683
  );
24213
24684
  }
24214
24685
  function absentPathThrows(owner, subject) {
24215
- if (!import_ts_morph88.Node.isIfStatement(owner)) {
24686
+ if (!import_ts_morph90.Node.isIfStatement(owner)) {
24216
24687
  return continuationThrows(owner);
24217
24688
  }
24218
24689
  const test = owner.getExpression();
@@ -24226,7 +24697,7 @@ function absentPathThrows(owner, subject) {
24226
24697
  return arms.some((arm) => armThrows(arm, owner, subject));
24227
24698
  }
24228
24699
  function armThrows(arm, owner, subject) {
24229
- if (arm !== void 0 && import_ts_morph88.Node.isIfStatement(arm)) {
24700
+ if (arm !== void 0 && import_ts_morph90.Node.isIfStatement(arm)) {
24230
24701
  return absentPathThrows(arm, subject);
24231
24702
  }
24232
24703
  const exit = arm === void 0 ? null : exitOf(arm);
@@ -24236,7 +24707,7 @@ function continuationThrows(from) {
24236
24707
  let current2 = from;
24237
24708
  let parent = current2.getParent();
24238
24709
  while (parent !== void 0) {
24239
- 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)) {
24240
24711
  const statements = parent.getStatements();
24241
24712
  const after = statements.slice(
24242
24713
  statements.indexOf(current2) + 1
@@ -24255,7 +24726,7 @@ function continuationThrows(from) {
24255
24726
  return false;
24256
24727
  }
24257
24728
  function endsThePath(node) {
24258
- 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);
24259
24730
  }
24260
24731
  function firstExit(statements) {
24261
24732
  for (const statement of statements) {
@@ -24267,13 +24738,13 @@ function firstExit(statements) {
24267
24738
  return null;
24268
24739
  }
24269
24740
  function exitOf(statement) {
24270
- if (import_ts_morph88.Node.isThrowStatement(statement)) {
24741
+ if (import_ts_morph90.Node.isThrowStatement(statement)) {
24271
24742
  return "throw";
24272
24743
  }
24273
- 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)) {
24274
24745
  return "leave";
24275
24746
  }
24276
- return import_ts_morph88.Node.isBlock(statement) ? firstExit(statement.getStatements()) : null;
24747
+ return import_ts_morph90.Node.isBlock(statement) ? firstExit(statement.getStatements()) : null;
24277
24748
  }
24278
24749
  function isPresentWhen(test, outcome, subject) {
24279
24750
  const expression = peelSyntax(test);
@@ -24284,7 +24755,7 @@ function isPresentWhen(test, outcome, subject) {
24284
24755
  if (negated2 !== null) {
24285
24756
  return isPresentWhen(negated2, !outcome, subject);
24286
24757
  }
24287
- if (!import_ts_morph88.Node.isBinaryExpression(expression)) {
24758
+ if (!import_ts_morph90.Node.isBinaryExpression(expression)) {
24288
24759
  return false;
24289
24760
  }
24290
24761
  const operator = expression.getOperatorToken().getText();
@@ -24315,24 +24786,24 @@ function missingSideOf(comparison) {
24315
24786
  return comparedValue(left, right, loose) ?? comparedValue(right, left, loose);
24316
24787
  }
24317
24788
  function comparedValue(operand2, other, loose) {
24318
- if (import_ts_morph88.Node.isTypeOfExpression(operand2)) {
24319
- 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;
24320
24791
  }
24321
- 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);
24322
24793
  return missing ? operand2 : null;
24323
24794
  }
24324
24795
  function negatedOperand(node) {
24325
- 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) {
24326
24797
  return node.getOperand();
24327
24798
  }
24328
24799
  return null;
24329
24800
  }
24330
24801
  function isKeyOfRead(read2, key, container) {
24331
- 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);
24332
24803
  }
24333
24804
  function isKeyReadBy(read2, key) {
24334
- if (import_ts_morph88.Node.isPropertyAccessExpression(read2)) {
24335
- 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();
24336
24807
  }
24337
24808
  return read2.getArgumentExpression()?.getText() === key.getText();
24338
24809
  }
@@ -24346,25 +24817,25 @@ function localNamedBy(node) {
24346
24817
  }
24347
24818
  const name = declaration.getNameNode();
24348
24819
  const scope = findEnclosingFunction(declaration);
24349
- if (!import_ts_morph88.Node.isIdentifier(name) || scope === null) {
24820
+ if (!import_ts_morph90.Node.isIdentifier(name) || scope === null) {
24350
24821
  return null;
24351
24822
  }
24352
24823
  const symbol = symbolBehind(name);
24353
24824
  return symbol === void 0 ? null : { name, symbol, scope };
24354
24825
  }
24355
24826
  function declarationInitializedBy(node) {
24356
- if (import_ts_morph88.Node.isBindingElement(node)) {
24827
+ if (import_ts_morph90.Node.isBindingElement(node)) {
24357
24828
  return node;
24358
24829
  }
24359
24830
  const read2 = climbSyntax(node);
24360
24831
  const parent = read2.getParent();
24361
- 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) {
24362
24833
  return parent;
24363
24834
  }
24364
24835
  return null;
24365
24836
  }
24366
24837
  function usesOf(local) {
24367
- return local.scope.getDescendantsOfKind(import_ts_morph88.SyntaxKind.Identifier).filter(
24838
+ return local.scope.getDescendantsOfKind(import_ts_morph90.SyntaxKind.Identifier).filter(
24368
24839
  (identifier) => identifier !== local.name && refersTo(identifier, local)
24369
24840
  );
24370
24841
  }
@@ -24394,6 +24865,7 @@ function usesOf(local) {
24394
24865
  createTypeScriptAdapter,
24395
24866
  declarationsBehind,
24396
24867
  discoverUnits,
24868
+ dottedPathOf,
24397
24869
  exportedDeclarationsOf,
24398
24870
  extractCodeStructure,
24399
24871
  extractRawBranches,