@suss/adapter-typescript 0.35.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/adapter.ts
2
2
  import path14 from "path";
3
3
  import {
4
- Node as Node80,
4
+ Node as Node82,
5
5
  Project as Project2
6
6
  } from "ts-morph";
7
7
  import {
@@ -26,17 +26,18 @@ import {
26
26
  createCacheLayer,
27
27
  extractionCacheDirFor,
28
28
  extractionConfigStamp,
29
+ libraryEnvReadMarker,
29
30
  mergeReadShapes as mergeReadShapes2,
30
31
  runDigest,
31
32
  splitAtHandOffs as splitAtHandOffs2,
32
33
  stampModuleImports,
33
34
  stampModules
34
35
  } from "@suss/extractor";
35
- import { force as force7, hostOf as hostOf2, literalOf as literalOf6, pathOf as pathOf2 } from "@suss/values";
36
+ import { force as force8, hostOf as hostOf2, literalOf as literalOf6, pathOf as pathOf2 } from "@suss/values";
36
37
 
37
38
  // src/assembly.ts
38
39
  import { Node as Node67 } from "ts-morph";
39
- import { guardsHoldOn, runsBefore, splitAtHandOffs } from "@suss/extractor";
40
+ import { firesOn, runsBefore, splitAtHandOffs } from "@suss/extractor";
40
41
 
41
42
  // src/conditions.ts
42
43
  import {
@@ -1116,11 +1117,11 @@ function settledDepthsFor(project) {
1116
1117
  }
1117
1118
  var depthsByProject = /* @__PURE__ */ new WeakMap();
1118
1119
  function readSpecifierPaths(project, filePath, options, host) {
1119
- const text2 = readFileText(project, filePath);
1120
- if (text2 === null) {
1120
+ const text3 = readFileText(project, filePath);
1121
+ if (text3 === null) {
1121
1122
  return [];
1122
1123
  }
1123
- const scanned = ts.preProcessFile(text2, true, true);
1124
+ const scanned = ts.preProcessFile(text3, true, true);
1124
1125
  const cache = resolutionCacheFor(project, options);
1125
1126
  const paths = [];
1126
1127
  for (const imported of scanned.importedFiles) {
@@ -1292,12 +1293,12 @@ async function readImportsConcurrently(paths, marks) {
1292
1293
  }
1293
1294
  const p = paths[i];
1294
1295
  try {
1295
- const text2 = await fs2.readFile(p, "utf-8");
1296
- const pre = ts.preProcessFile(text2, true, false);
1296
+ const text3 = await fs2.readFile(p, "utf-8");
1297
+ const pre = ts.preProcessFile(text3, true, false);
1297
1298
  results[i] = {
1298
1299
  path: p,
1299
1300
  importedModules: pre.importedFiles.map((f) => f.fileName),
1300
- marked: marks.some((mark) => text2.includes(mark))
1301
+ marked: marks.some((mark) => text3.includes(mark))
1301
1302
  };
1302
1303
  } catch {
1303
1304
  results[i] = { path: p, importedModules: [], marked: false };
@@ -2290,8 +2291,8 @@ function spellingsHandedOn(paths, sourceFile) {
2290
2291
  if (paths.length === 0) {
2291
2292
  return [];
2292
2293
  }
2293
- const text2 = sourceFile.getFullText();
2294
- if (!paths.some((path17) => text2.includes(path17))) {
2294
+ const text3 = sourceFile.getFullText();
2295
+ if (!paths.some((path17) => text3.includes(path17))) {
2295
2296
  return [];
2296
2297
  }
2297
2298
  return sourceFile.getDescendantsOfKind(SyntaxKind2.PropertyAccessExpression).filter((node) => spellsOneOf(paths, node) && flowsOnward(node));
@@ -2310,6 +2311,10 @@ function dottedPathOf(expression) {
2310
2311
  if (Node7.isIdentifier(expression)) {
2311
2312
  return expression.getText();
2312
2313
  }
2314
+ if (Node7.isMetaProperty(expression)) {
2315
+ const keyword = ts2.tokenToString(expression.getKeywordToken());
2316
+ return `${keyword}.${expression.getName()}`;
2317
+ }
2313
2318
  if (!Node7.isPropertyAccessExpression(expression)) {
2314
2319
  return null;
2315
2320
  }
@@ -4342,8 +4347,8 @@ var ResolutionStore = class _ResolutionStore {
4342
4347
  if (one.isInNodeModules()) {
4343
4348
  return false;
4344
4349
  }
4345
- const text2 = one.getFullText();
4346
- return spellings.some((spelling) => text2.includes(spelling));
4350
+ const text3 = one.getFullText();
4351
+ return spellings.some((spelling) => text3.includes(spelling));
4347
4352
  });
4348
4353
  if (siteFiles.length === 0) {
4349
4354
  return [];
@@ -5750,10 +5755,13 @@ function parametersOf(parameter, position) {
5750
5755
  }
5751
5756
  ];
5752
5757
  }
5753
- if (!Node12.isObjectBindingPattern(nameNode) || parameter.getInitializer() !== void 0) {
5758
+ if (!Node12.isObjectBindingPattern(nameNode)) {
5754
5759
  return [];
5755
5760
  }
5756
- return destructuredNames(nameNode, position, []);
5761
+ const patternDefault = parameter.getInitializer();
5762
+ return destructuredNames(nameNode, position, []).map(
5763
+ (entry) => patternDefault === void 0 ? entry : { ...entry, patternDefault }
5764
+ );
5757
5765
  }
5758
5766
  function destructuredNames(pattern, position, prefix) {
5759
5767
  return pattern.getElements().flatMap((element) => {
@@ -6060,6 +6068,18 @@ function evaluatedValue(node, resolution, site) {
6060
6068
  function evaluatedValueUnder(node, resolution, bindings) {
6061
6069
  return bindings.size === 0 ? evaluatedValue(node, resolution) : force(evaluatorFor(node, resolution).evaluate(node, { bindings }));
6062
6070
  }
6071
+ function evaluatedValueThrough(node, calls, resolution, given) {
6072
+ const evaluator = evaluatorFor(node, resolution);
6073
+ let bindings = /* @__PURE__ */ new Map();
6074
+ for (const call of [...calls].reverse()) {
6075
+ const bound = evaluator.argumentsAt(call, { bindings, given });
6076
+ if (bound === null) {
6077
+ return null;
6078
+ }
6079
+ bindings = bound;
6080
+ }
6081
+ return evaluator.evaluate(node, { bindings, given });
6082
+ }
6063
6083
 
6064
6084
  // src/resolve/routePath.ts
6065
6085
  function pathFromArgument(arg, resolution, site) {
@@ -7475,8 +7495,8 @@ function declaredChannelOf(source, decorator, resolution) {
7475
7495
  if (arg === void 0) {
7476
7496
  return null;
7477
7497
  }
7478
- const text2 = stringValueOf(arg, resolution);
7479
- return text2 === null || text2 === "" ? null : text2;
7498
+ const text3 = stringValueOf(arg, resolution);
7499
+ return text3 === null || text3 === "" ? null : text3;
7480
7500
  }
7481
7501
  function statusCodeDecoratedOn(routeDecorator, statusDecorators, constants, resolution) {
7482
7502
  const member = routeDecorator.getParent();
@@ -7932,28 +7952,28 @@ function assembledTemplateText(template, assembly) {
7932
7952
  if (!Node24.isTemplateExpression(template)) {
7933
7953
  return null;
7934
7954
  }
7935
- let text2 = template.getHead().getLiteralText();
7955
+ let text3 = template.getHead().getLiteralText();
7936
7956
  for (const span of template.getTemplateSpans()) {
7937
7957
  const spliced = interpolatedDocumentText(span.getExpression(), assembly);
7938
7958
  if (spliced === null) {
7939
7959
  assembly.unresolvedInterpolations.push(
7940
7960
  singleLine(span.getExpression().getText())
7941
7961
  );
7942
- if (openBraceDepth(text2) > 0) {
7962
+ if (openBraceDepth(text3) > 0) {
7943
7963
  assembly.unresolvedInsideSelection = true;
7944
7964
  }
7945
7965
  } else {
7946
- text2 += `
7966
+ text3 += `
7947
7967
  ${spliced}
7948
7968
  `;
7949
7969
  }
7950
- text2 += span.getLiteral().getLiteralText();
7970
+ text3 += span.getLiteral().getLiteralText();
7951
7971
  }
7952
- return text2;
7972
+ return text3;
7953
7973
  }
7954
- function openBraceDepth(text2) {
7974
+ function openBraceDepth(text3) {
7955
7975
  let depth = 0;
7956
- for (const char of text2) {
7976
+ for (const char of text3) {
7957
7977
  if (char === "{") {
7958
7978
  depth += 1;
7959
7979
  } else if (char === "}") {
@@ -7985,16 +8005,16 @@ function interpolatedDocumentText(expr, assembly) {
7985
8005
  function resolveGraphqlDocument(arg, resolution) {
7986
8006
  const assembly = startAssembly(resolution);
7987
8007
  const stripped = peelSyntax(arg);
7988
- const text2 = documentTextFromExpression(stripped, assembly) ?? resolveGqlTemplateText(stripped, assembly) ?? resolveTypedDocumentSource(stripped) ?? resolveThroughFacts(stripped, assembly);
7989
- if (text2 !== null) {
7990
- return assembledDocumentResolution(text2, stripped, assembly);
8008
+ const text3 = documentTextFromExpression(stripped, assembly) ?? resolveGqlTemplateText(stripped, assembly) ?? resolveTypedDocumentSource(stripped) ?? resolveThroughFacts(stripped, assembly);
8009
+ if (text3 !== null) {
8010
+ return assembledDocumentResolution(text3, stripped, assembly);
7991
8011
  }
7992
8012
  return resolveTypedDocumentHeader(stripped);
7993
8013
  }
7994
- function assembledDocumentResolution(text2, arg, assembly) {
8014
+ function assembledDocumentResolution(text3, arg, assembly) {
7995
8015
  const interpolations = assembly.unresolvedInterpolations;
7996
8016
  if (assembly.unresolvedInsideSelection) {
7997
- const header = parseGraphqlOperation(text2);
8017
+ const header = parseGraphqlOperation(text3);
7998
8018
  return {
7999
8019
  ...header !== null ? { operationType: header.operationType } : {},
8000
8020
  ...header?.operationName !== void 0 ? { operationName: header.operationName } : {},
@@ -8004,10 +8024,10 @@ function assembledDocumentResolution(text2, arg, assembly) {
8004
8024
  }
8005
8025
  };
8006
8026
  }
8007
- const spreads = fragmentSpreadsIn(text2);
8027
+ const spreads = fragmentSpreadsIn(text3);
8008
8028
  if (spreads === null) {
8009
8029
  if (interpolations.length === 0) {
8010
- return { document: text2 };
8030
+ return { document: text3 };
8011
8031
  }
8012
8032
  return {
8013
8033
  unresolved: {
@@ -8016,7 +8036,7 @@ function assembledDocumentResolution(text2, arg, assembly) {
8016
8036
  }
8017
8037
  };
8018
8038
  }
8019
- const filled = fillDanglingSpreads(text2, spreads, arg, assembly);
8039
+ const filled = fillDanglingSpreads(text3, spreads, arg, assembly);
8020
8040
  if (filled.unresolved.length > 0) {
8021
8041
  return {
8022
8042
  document: filled.text,
@@ -8035,15 +8055,15 @@ function assembledDocumentResolution(text2, arg, assembly) {
8035
8055
  }
8036
8056
  return { document: filled.text };
8037
8057
  }
8038
- function fillDanglingSpreads(text2, spreads, arg, assembly) {
8058
+ function fillDanglingSpreads(text3, spreads, arg, assembly) {
8039
8059
  if (spreads.dangling.length === 0 || assembly.tagOrigin !== "project") {
8040
- return { text: text2, unresolved: spreads.dangling, ambiguous: [] };
8060
+ return { text: text3, unresolved: spreads.dangling, ambiguous: [] };
8041
8061
  }
8042
8062
  const definitions = projectFragmentDefinitions(arg.getProject());
8043
8063
  const settled3 = new Set(spreads.defined);
8044
8064
  const unresolved = /* @__PURE__ */ new Set();
8045
8065
  const ambiguous = /* @__PURE__ */ new Set();
8046
- let filled = text2;
8066
+ let filled = text3;
8047
8067
  let pending = spreads.dangling;
8048
8068
  while (pending.length > 0) {
8049
8069
  const brought = [];
@@ -8107,12 +8127,12 @@ function buildFragmentIndex(project) {
8107
8127
  if (!FRAGMENT_DEFINITION_TEXT.test(sourceFile.getFullText())) {
8108
8128
  continue;
8109
8129
  }
8110
- for (const text2 of documentTextsIn(sourceFile)) {
8111
- recordFragmentDefinitions(text2, definitions);
8130
+ for (const text3 of documentTextsIn(sourceFile)) {
8131
+ recordFragmentDefinitions(text3, definitions);
8112
8132
  }
8113
8133
  }
8114
- for (const text2 of graphqlFileTextsNear(sourceFiles)) {
8115
- recordFragmentDefinitions(text2, definitions);
8134
+ for (const text3 of graphqlFileTextsNear(sourceFiles)) {
8135
+ recordFragmentDefinitions(text3, definitions);
8116
8136
  }
8117
8137
  return definitions;
8118
8138
  }
@@ -8149,17 +8169,17 @@ function documentTextsIn(sourceFile) {
8149
8169
  if (!Node24.isTaggedTemplateExpression(node) && !Node24.isCallExpression(node)) {
8150
8170
  return;
8151
8171
  }
8152
- const text2 = documentTextFromExpression(node, startAssembly(void 0));
8153
- if (text2 !== null && text2 !== "") {
8154
- texts.push(text2);
8172
+ const text3 = documentTextFromExpression(node, startAssembly(void 0));
8173
+ if (text3 !== null && text3 !== "") {
8174
+ texts.push(text3);
8155
8175
  }
8156
8176
  });
8157
8177
  return texts;
8158
8178
  }
8159
- function recordFragmentDefinitions(text2, definitions) {
8179
+ function recordFragmentDefinitions(text3, definitions) {
8160
8180
  let doc;
8161
8181
  try {
8162
- doc = graphqlParse(text2);
8182
+ doc = graphqlParse(text3);
8163
8183
  } catch {
8164
8184
  return;
8165
8185
  }
@@ -8183,13 +8203,13 @@ function recordFragmentDefinitions(text2, definitions) {
8183
8203
  }
8184
8204
  var NO_NAMES = /* @__PURE__ */ new Set();
8185
8205
  function describeInterpolations(interpolations) {
8186
- const quoted = interpolations.map((text2) => `\`${text2}\``).join(", ");
8206
+ const quoted = interpolations.map((text3) => `\`${text3}\``).join(", ");
8187
8207
  return interpolations.length === 1 ? `expression ${quoted}` : `expressions ${quoted}`;
8188
8208
  }
8189
- function fragmentSpreadsIn(text2) {
8209
+ function fragmentSpreadsIn(text3) {
8190
8210
  let doc;
8191
8211
  try {
8192
- doc = graphqlParse(text2);
8212
+ doc = graphqlParse(text3);
8193
8213
  } catch {
8194
8214
  return null;
8195
8215
  }
@@ -8341,9 +8361,9 @@ function resolveThroughFacts(arg, assembly) {
8341
8361
  if (written === null) {
8342
8362
  return null;
8343
8363
  }
8344
- const text2 = documentTextFromExpression(written, assembly);
8345
- if (text2 !== null) {
8346
- return text2;
8364
+ const text3 = documentTextFromExpression(written, assembly);
8365
+ if (text3 !== null) {
8366
+ return text3;
8347
8367
  }
8348
8368
  return typedDocumentSourceOf(written);
8349
8369
  }
@@ -8379,8 +8399,8 @@ function unreadableDocument(arg, reason) {
8379
8399
  }
8380
8400
  };
8381
8401
  }
8382
- function singleLine(text2) {
8383
- const collapsed = text2.replace(/\s+/g, " ").trim();
8402
+ function singleLine(text3) {
8403
+ const collapsed = text3.replace(/\s+/g, " ").trim();
8384
8404
  return collapsed.length > 80 ? `${collapsed.slice(0, 79)}\u2026` : collapsed;
8385
8405
  }
8386
8406
  function importedVariableInitializers(identifier) {
@@ -8424,9 +8444,9 @@ function resolveGqlTemplateText(arg, assembly) {
8424
8444
  }
8425
8445
  }
8426
8446
  for (const init of importedVariableInitializers(arg)) {
8427
- const text2 = documentTextFromExpression(peelSyntax(init), assembly);
8428
- if (text2 !== null) {
8429
- return text2;
8447
+ const text3 = documentTextFromExpression(peelSyntax(init), assembly);
8448
+ if (text3 !== null) {
8449
+ return text3;
8430
8450
  }
8431
8451
  }
8432
8452
  return null;
@@ -9029,7 +9049,8 @@ function expandDocumentParameterCallers(documentArgument, libraryCall, context)
9029
9049
  context.store.returnsCall(wrapper, libraryCall),
9030
9050
  0,
9031
9051
  /* @__PURE__ */ new Set([parameter]),
9032
- context
9052
+ context,
9053
+ { library: libraryCall, through: [] }
9033
9054
  );
9034
9055
  return {
9035
9056
  units: expanded.units,
@@ -9042,7 +9063,7 @@ function documentBehindWrapper(value, store, wrapperReason) {
9042
9063
  }
9043
9064
  return resolveGraphqlDocument(value, store) ?? unreadableDocument(value);
9044
9065
  }
9045
- function expandParameter(parameter, forwardsResult, depth, expanded, context) {
9066
+ function expandParameter(parameter, forwardsResult, depth, expanded, context, chain) {
9046
9067
  const passed = context.store.argumentsPassedTo(parameter);
9047
9068
  if (passed.length === 0) {
9048
9069
  return { units: [], unfollowed: 1 };
@@ -9055,6 +9076,7 @@ function expandParameter(parameter, forwardsResult, depth, expanded, context) {
9055
9076
  unfollowed++;
9056
9077
  continue;
9057
9078
  }
9079
+ const outward = { ...chain, through: [...chain.through, call] };
9058
9080
  const nested = parameterBehind(argument, caller);
9059
9081
  if (nested !== null) {
9060
9082
  const deeper = expandNested(
@@ -9062,13 +9084,21 @@ function expandParameter(parameter, forwardsResult, depth, expanded, context) {
9062
9084
  forwardsResult && context.store.returnsCall(caller, call),
9063
9085
  depth,
9064
9086
  expanded,
9065
- context
9087
+ context,
9088
+ outward
9066
9089
  );
9067
9090
  units.push(...deeper.units);
9068
9091
  unfollowed += deeper.unfollowed;
9069
9092
  continue;
9070
9093
  }
9071
- const unit = callerUnit(caller, call, argument, forwardsResult, context);
9094
+ const unit = callerUnit(
9095
+ caller,
9096
+ call,
9097
+ argument,
9098
+ forwardsResult,
9099
+ context,
9100
+ outward
9101
+ );
9072
9102
  if (unit === null) {
9073
9103
  unfollowed++;
9074
9104
  continue;
@@ -9077,7 +9107,7 @@ function expandParameter(parameter, forwardsResult, depth, expanded, context) {
9077
9107
  }
9078
9108
  return { units, unfollowed };
9079
9109
  }
9080
- function expandNested(parameter, forwardsResult, depth, expanded, context) {
9110
+ function expandNested(parameter, forwardsResult, depth, expanded, context, chain) {
9081
9111
  if (depth + 1 >= MAX_WRAPPER_DEPTH || expanded.has(parameter)) {
9082
9112
  return { units: [], unfollowed: 1 };
9083
9113
  }
@@ -9086,10 +9116,11 @@ function expandNested(parameter, forwardsResult, depth, expanded, context) {
9086
9116
  forwardsResult,
9087
9117
  depth + 1,
9088
9118
  /* @__PURE__ */ new Set([...expanded, parameter]),
9089
- context
9119
+ context,
9120
+ chain
9090
9121
  );
9091
9122
  }
9092
- function callerUnit(caller, call, argument, forwardsResult, context) {
9123
+ function callerUnit(caller, call, argument, forwardsResult, context, chain) {
9093
9124
  const document = resolveGraphqlDocument(argument, context.store) ?? unreadableDocument(argument);
9094
9125
  const operationInfo = operationInfoFromResolution(
9095
9126
  document,
@@ -9106,7 +9137,14 @@ function callerUnit(caller, call, argument, forwardsResult, context) {
9106
9137
  // A wrapper handing back something of its own leaves the caller
9107
9138
  // none of the library's result to read.
9108
9139
  ...forwardsResult ? { callSite: { callExpression: call, methodName: context.methodName } } : {},
9109
- operationInfo
9140
+ operationInfo,
9141
+ ...context.client === void 0 ? {} : {
9142
+ graphqlCall: {
9143
+ call: chain.library,
9144
+ through: chain.through,
9145
+ client: context.client
9146
+ }
9147
+ }
9110
9148
  };
9111
9149
  }
9112
9150
  function unfollowedReason(followed, unfollowed) {
@@ -9208,7 +9246,8 @@ function discoverGraphqlHookCalls(sourceFile, match, kind, resolution) {
9208
9246
  kind,
9209
9247
  operationType: spec.operationType,
9210
9248
  methodName: spec.canonical,
9211
- store
9249
+ store,
9250
+ ...match.clientOption === void 0 ? {} : { client: match.clientOption }
9212
9251
  });
9213
9252
  if (expansion !== null) {
9214
9253
  results.push(...expansion.units);
@@ -9238,7 +9277,14 @@ function discoverGraphqlHookCalls(sourceFile, match, kind, resolution) {
9238
9277
  kind,
9239
9278
  name: `${functionNameOrAnon(enclosing)}.${nameToken}`,
9240
9279
  callSite: { callExpression: node, methodName: spec.canonical },
9241
- operationInfo
9280
+ operationInfo,
9281
+ ...match.clientOption === void 0 ? {} : {
9282
+ graphqlCall: {
9283
+ call: node,
9284
+ through: [],
9285
+ client: match.clientOption
9286
+ }
9287
+ }
9242
9288
  });
9243
9289
  }
9244
9290
  return results;
@@ -9289,7 +9335,8 @@ function discoverGraphqlImperativeCalls(sourceFile, match, kind, resolution) {
9289
9335
  kind,
9290
9336
  operationType: spec.operationType,
9291
9337
  methodName,
9292
- store: resolution
9338
+ store: resolution,
9339
+ client: "receiver"
9293
9340
  });
9294
9341
  if (expansion !== null) {
9295
9342
  results.push(...expansion.units);
@@ -9319,7 +9366,8 @@ function discoverGraphqlImperativeCalls(sourceFile, match, kind, resolution) {
9319
9366
  kind,
9320
9367
  name: `${functionNameOrAnon(enclosing)}.${nameToken}`,
9321
9368
  callSite: { callExpression: node, methodName },
9322
- operationInfo
9369
+ operationInfo,
9370
+ graphqlCall: { call: node, through: [], client: "receiver" }
9323
9371
  });
9324
9372
  });
9325
9373
  return results;
@@ -10332,6 +10380,7 @@ function discoverRegistrationCalls(sourceFile, match, kind, bindingExtraction, r
10332
10380
  if (registrationSubjects.size === 0 && !storeCanFindSubjects(sourceFile, match, resolution, registrationMethods)) {
10333
10381
  return results;
10334
10382
  }
10383
+ const anonymousLabels = /* @__PURE__ */ new Map();
10335
10384
  sourceFile.forEachDescendant((node) => {
10336
10385
  if (!Node36.isCallExpression(node)) {
10337
10386
  return;
@@ -10433,7 +10482,8 @@ function discoverRegistrationCalls(sourceFile, match, kind, bindingExtraction, r
10433
10482
  routeWrapperPattern,
10434
10483
  methodName,
10435
10484
  routeInfo,
10436
- resolution
10485
+ resolution,
10486
+ anonymousLabels
10437
10487
  );
10438
10488
  results.push({
10439
10489
  func: handler,
@@ -10494,9 +10544,8 @@ function declinedRegistration(call, callee, methodName, kind, receiver, resoluti
10494
10544
  }
10495
10545
  ];
10496
10546
  }
10497
- function routeChainOf(listed, pattern, methodName, routeInfo, resolution) {
10547
+ function routeChainOf(listed, pattern, methodName, routeInfo, resolution, anonymousLabels) {
10498
10548
  const chain = { units: [], references: [], unfollowed: [] };
10499
- const anonymousLabels = /* @__PURE__ */ new Map();
10500
10549
  for (const arg of listed) {
10501
10550
  const elements = Node36.isArrayLiteralExpression(arg) ? arg.getElements() : [arg];
10502
10551
  for (const element of elements) {
@@ -10510,12 +10559,12 @@ function routeChainOf(listed, pattern, methodName, routeInfo, resolution) {
10510
10559
  }
10511
10560
  const ownName = functionNameOrAnon(target);
10512
10561
  const named2 = ownName !== "<anon>";
10513
- const label = factoryNameOf(element) ?? methodName;
10562
+ const label = `${factoryNameOf(element) ?? methodName}@${routeWhere(routeInfo)}`;
10514
10563
  const seen = anonymousLabels.get(label) ?? 0;
10515
10564
  if (!named2) {
10516
10565
  anonymousLabels.set(label, seen + 1);
10517
10566
  }
10518
- const name = named2 ? ownName : routeWrapperLabel(label, routeInfo, element, seen);
10567
+ const name = named2 ? ownName : numberedLabel(label, seen);
10519
10568
  chain.units.push({
10520
10569
  func: target,
10521
10570
  kind: pattern.kind,
@@ -10531,9 +10580,11 @@ function routeChainOf(listed, pattern, methodName, routeInfo, resolution) {
10531
10580
  }
10532
10581
  return chain;
10533
10582
  }
10534
- function routeWrapperLabel(label, routeInfo, element, seen) {
10535
- const where = routeInfo === null ? `L${element.getStartLineNumber()}` : `${routeInfo.method} ${routeInfo.path}`;
10536
- return seen === 0 ? `${label}@${where}` : `${label}@${where}#${seen + 1}`;
10583
+ function routeWhere(routeInfo) {
10584
+ return routeInfo === null ? "?" : `${routeInfo.method} ${routeInfo.path}`;
10585
+ }
10586
+ function numberedLabel(label, seen) {
10587
+ return seen === 0 ? label : `${label}#${seen + 1}`;
10537
10588
  }
10538
10589
  function withMountPrefix(routeInfo, subjectNode, mountPrefixes) {
10539
10590
  if (routeInfo === null || mountPrefixes === void 0) {
@@ -11471,8 +11522,8 @@ function mockOf(call, match, sourceFile, resolution) {
11471
11522
  const module = Node40.isIdentifier(first) ? moduleImportedWholeAs(sourceFile, first.getText()) : null;
11472
11523
  return { ...module !== null ? { module } : {}, name, written };
11473
11524
  }
11474
- function oneLine(text2) {
11475
- const flat = text2.replace(/\s*\n\s*/g, "");
11525
+ function oneLine(text3) {
11526
+ const flat = text3.replace(/\s*\n\s*/g, "");
11476
11527
  const factory = flat.search(/,\s*(async\s*)?\(/);
11477
11528
  return factory === -1 ? flat : `${flat.slice(0, factory)})`;
11478
11529
  }
@@ -12049,8 +12100,8 @@ function receiverIsPromiseTyped(receiver) {
12049
12100
  if (symbolName === "Promise") {
12050
12101
  return true;
12051
12102
  }
12052
- const text2 = type.getText();
12053
- return text2 === "Promise" || text2.startsWith("Promise<");
12103
+ const text3 = type.getText();
12104
+ return text3 === "Promise" || text3.startsWith("Promise<");
12054
12105
  }
12055
12106
  function callbackReturnExpression(node) {
12056
12107
  if (node === void 0 || !(Node43.isArrowFunction(node) || Node43.isFunctionExpression(node))) {
@@ -12687,6 +12738,7 @@ import { Node as Node48 } from "ts-morph";
12687
12738
  import {
12688
12739
  enumerateStructuredPaths,
12689
12740
  PathBudgetExceeded,
12741
+ placeTerminals,
12690
12742
  UnmodeledFlow as UnmodeledFlow2
12691
12743
  } from "@suss/extractor";
12692
12744
 
@@ -12741,9 +12793,9 @@ function getLoopBody(stmt) {
12741
12793
  return void 0;
12742
12794
  }
12743
12795
  var loopHeaderText = (stmt) => {
12744
- const text2 = stmt.getText();
12745
- const bodyStart = text2.indexOf("{");
12746
- const header = bodyStart === -1 ? text2 : text2.slice(0, bodyStart);
12796
+ const text3 = stmt.getText();
12797
+ const bodyStart = text3.indexOf("{");
12798
+ const header = bodyStart === -1 ? text3 : text3.slice(0, bodyStart);
12747
12799
  return header.trim();
12748
12800
  };
12749
12801
  var conditionOf = (expr) => ({
@@ -12854,7 +12906,8 @@ function buildStructured(stmt, ctx) {
12854
12906
  kind: "loop",
12855
12907
  condition: { sourceText: loopHeaderText(stmt), expression: null },
12856
12908
  body: lowerList(statementsOf(getLoopBody(stmt)), ctx),
12857
- exitKind
12909
+ exitKind,
12910
+ lines: lineRangeOf(stmt)
12858
12911
  };
12859
12912
  }
12860
12913
  if (Node47.isTryStatement(stmt)) {
@@ -12862,6 +12915,7 @@ function buildStructured(stmt, ctx) {
12862
12915
  const finallyBlock = stmt.getFinallyBlock();
12863
12916
  return {
12864
12917
  kind: "try",
12918
+ lines: lineRangeOf(stmt),
12865
12919
  tryBody: lowerList(stmt.getTryBlock().getStatements(), ctx),
12866
12920
  catchBody: catchClause === void 0 ? null : lowerList(catchClause.getBlock().getStatements(), ctx),
12867
12921
  finallyBody: finallyBlock === void 0 ? null : lowerList(finallyBlock.getStatements(), ctx),
@@ -13022,13 +13076,14 @@ function loweredExpressionBody(body, terminalNodes, func, barriers) {
13022
13076
  function computePathConditions(func, terminalNodes, barriers = NO_BARRIERS, endings = NO_ENDINGS, keepsArms) {
13023
13077
  const body = func.getBody();
13024
13078
  if (body === void 0) {
13025
- return { byTerminal: /* @__PURE__ */ new Map(), fallthrough: [] };
13079
+ return { byTerminal: /* @__PURE__ */ new Map(), fallthrough: [], placement: /* @__PURE__ */ new Map() };
13026
13080
  }
13027
13081
  if (containsUnmodeledFlow(body, func, barriers)) {
13028
13082
  return degradedResult(func, terminalNodes, "labeled statement");
13029
13083
  }
13084
+ let lowered;
13030
13085
  try {
13031
- const lowered = Node48.isBlock(body) ? lowerFunctionBody(body, terminalNodes, func, barriers, endings) : loweredExpressionBody(body, terminalNodes, func, barriers);
13086
+ lowered = Node48.isBlock(body) ? lowerFunctionBody(body, terminalNodes, func, barriers, endings) : loweredExpressionBody(body, terminalNodes, func, barriers);
13032
13087
  const result = enumerateStructuredPaths({
13033
13088
  statements: lowered.statements,
13034
13089
  terminalsByStmt: lowered.terminalsByStmt,
@@ -13036,15 +13091,17 @@ function computePathConditions(func, terminalNodes, barriers = NO_BARRIERS, endi
13036
13091
  });
13037
13092
  return composeExpressionLevel(func, result, lowered.terminalHomeRaw);
13038
13093
  } catch (error) {
13094
+ const placement = lowered === void 0 ? /* @__PURE__ */ new Map() : placeTerminals(lowered);
13039
13095
  if (error instanceof PathBudgetExceeded) {
13040
13096
  return degradedResult(
13041
13097
  func,
13042
13098
  terminalNodes,
13043
- error.message === "" ? "path budget exceeded" : error.message
13099
+ error.message === "" ? "path budget exceeded" : error.message,
13100
+ placement
13044
13101
  );
13045
13102
  }
13046
13103
  if (error instanceof UnmodeledFlow2) {
13047
- return degradedResult(func, terminalNodes, error.message);
13104
+ return degradedResult(func, terminalNodes, error.message, placement);
13048
13105
  }
13049
13106
  throw error;
13050
13107
  }
@@ -13059,9 +13116,13 @@ function composeExpressionLevel(func, result, terminalHomeRaw) {
13059
13116
  paths.map((statementLevel) => [...statementLevel, ...expressionLevel])
13060
13117
  );
13061
13118
  }
13062
- return { byTerminal, fallthrough: result.fallthrough };
13119
+ return {
13120
+ byTerminal,
13121
+ fallthrough: result.fallthrough,
13122
+ placement: result.placement
13123
+ };
13063
13124
  }
13064
- function degradedResult(func, terminalNodes, reason) {
13125
+ function degradedResult(func, terminalNodes, reason, placement = /* @__PURE__ */ new Map()) {
13065
13126
  const marker = {
13066
13127
  sourceText: `unmodeled control flow (${reason})`,
13067
13128
  polarity: "positive",
@@ -13078,7 +13139,7 @@ function degradedResult(func, terminalNodes, reason) {
13078
13139
  [...collectAncestorConditionInfosBelow(terminal, func), marker]
13079
13140
  ]);
13080
13141
  }
13081
- return { byTerminal, fallthrough: [[marker]] };
13142
+ return { byTerminal, fallthrough: [[marker]], placement };
13082
13143
  }
13083
13144
 
13084
13145
  // src/provenance.ts
@@ -13161,9 +13222,13 @@ function literalAt(node, resolution) {
13161
13222
 
13162
13223
  // src/resolve/invocationEffects.ts
13163
13224
  import {
13164
- Node as Node54
13225
+ Node as Node54,
13226
+ VariableDeclarationKind as VariableDeclarationKind2
13165
13227
  } from "ts-morph";
13166
- import { rawConditionToPredicate } from "@suss/extractor";
13228
+ import {
13229
+ numberRepeatedCallSites,
13230
+ rawConditionToPredicate
13231
+ } from "@suss/extractor";
13167
13232
 
13168
13233
  // src/resolve/callOps.ts
13169
13234
  import { Node as Node53 } from "ts-morph";
@@ -13207,7 +13272,7 @@ function stableTypeText(type, reader, enclosing, flags) {
13207
13272
  return withoutImportQualifiers(printed(ordered, file));
13208
13273
  }
13209
13274
  var IMPORT_QUALIFIER = /import\("[^"]*"\)\./g;
13210
- var withoutImportQualifiers = (text2) => text2.replace(IMPORT_QUALIFIER, "");
13275
+ var withoutImportQualifiers = (text3) => text3.replace(IMPORT_QUALIFIER, "");
13211
13276
  function printed(node, file) {
13212
13277
  return PRINTER.printNode(ts3.EmitHint.Unspecified, node, file).replace(
13213
13278
  /\n/g,
@@ -13249,11 +13314,11 @@ function inStableOrder(members, file) {
13249
13314
  function compareMembers(a, b) {
13250
13315
  return nullishRank(a) - nullishRank(b) || compareText(a, b);
13251
13316
  }
13252
- function nullishRank(text2) {
13253
- if (text2 === "null") {
13317
+ function nullishRank(text3) {
13318
+ if (text3 === "null") {
13254
13319
  return 1;
13255
13320
  }
13256
- if (text2 === "undefined") {
13321
+ if (text3 === "undefined") {
13257
13322
  return 2;
13258
13323
  }
13259
13324
  return 0;
@@ -14248,11 +14313,12 @@ function invocationAt(call, func) {
14248
14313
  callee: call.getExpression().getText(),
14249
14314
  args: extractArgs(call),
14250
14315
  async: isAwaited(call),
14251
- ...preconditions.length > 0 ? { preconditions } : {}
14316
+ ...preconditions.length > 0 ? { preconditions } : {},
14317
+ location: lineRangeOf(call)
14252
14318
  },
14253
14319
  node: call,
14254
14320
  line: enclosingStatementLine(call),
14255
- alwaysRuns: runsOnEveryPath(call, func)
14321
+ cleansUpFrom: cleanupStartLine(call, func)
14256
14322
  };
14257
14323
  }
14258
14324
  function isAwaited(call) {
@@ -14263,7 +14329,7 @@ function runInvocationRecognizers(func, recognizers, barriers = NO_BARRIERS, res
14263
14329
  if (recognizers.length === 0) {
14264
14330
  return [];
14265
14331
  }
14266
- const out = [];
14332
+ const sites = [];
14267
14333
  const sourceFile = func.getSourceFile();
14268
14334
  const calls = [];
14269
14335
  func.forEachDescendant((node, traversal) => {
@@ -14288,7 +14354,8 @@ function runInvocationRecognizers(func, recognizers, barriers = NO_BARRIERS, res
14288
14354
  );
14289
14355
  const line = enclosingStatementLine(node);
14290
14356
  const preconditions = collectPreconditions(node, func);
14291
- const alwaysRuns = runsOnEveryPath(node, func);
14357
+ const cleansUpFrom = cleanupStartLine(node, func);
14358
+ const site = [];
14292
14359
  for (const recognizer of recognizers) {
14293
14360
  let emitted = null;
14294
14361
  try {
@@ -14306,17 +14373,18 @@ function runInvocationRecognizers(func, recognizers, barriers = NO_BARRIERS, res
14306
14373
  continue;
14307
14374
  }
14308
14375
  for (const eff of emitted) {
14309
- out.push({
14310
- effect: withPreconditions(eff, preconditions),
14376
+ site.push({
14377
+ effect: placedAt(withPreconditions(eff, preconditions), node),
14311
14378
  line,
14312
14379
  preconditions,
14313
- alwaysRuns,
14380
+ cleansUpFrom,
14314
14381
  ...stated?.of(eff)
14315
14382
  });
14316
14383
  }
14317
14384
  }
14385
+ sites.push(site);
14318
14386
  }
14319
- return out;
14387
+ return numberRepeatedCallSites(sites).flat();
14320
14388
  }
14321
14389
  function runAccessRecognizers(func, recognizers, barriers = NO_BARRIERS, resolution, originatesFrom, anchorCallsOf) {
14322
14390
  return dispatchAccessRecognizers(
@@ -14401,7 +14469,7 @@ function dispatchAccessRecognizers(root, recognizers, isStop, resolution, origin
14401
14469
  );
14402
14470
  const line = enclosingStatementLine(node);
14403
14471
  const preconditions = collectPreconditions(node, root);
14404
- const alwaysRuns = runsOnEveryPath(node, root);
14472
+ const cleansUpFrom = cleanupStartLine(node, root);
14405
14473
  for (const recognizer of recognizers) {
14406
14474
  let emitted = null;
14407
14475
  try {
@@ -14426,10 +14494,10 @@ function dispatchAccessRecognizers(root, recognizers, isStop, resolution, origin
14426
14494
  }
14427
14495
  seenEffects.add(key);
14428
14496
  out.push({
14429
- effect,
14497
+ effect: placedAt(effect, node),
14430
14498
  line,
14431
14499
  preconditions,
14432
- alwaysRuns,
14500
+ cleansUpFrom,
14433
14501
  ...stated?.of(eff)
14434
14502
  });
14435
14503
  }
@@ -14463,6 +14531,12 @@ function extractArg(node, depth) {
14463
14531
  if (Node54.isFalseLiteral(node)) {
14464
14532
  return { kind: "boolean", value: false };
14465
14533
  }
14534
+ if (Node54.isNullLiteral(node)) {
14535
+ return { kind: "null" };
14536
+ }
14537
+ if (Node54.isIdentifier(node) && node.getText() === "undefined") {
14538
+ return { kind: "undefined" };
14539
+ }
14466
14540
  if (Node54.isTemplateExpression(node)) {
14467
14541
  return { kind: "template", sourceText: node.getText() };
14468
14542
  }
@@ -14554,16 +14628,20 @@ function collectPreconditions(node, root) {
14554
14628
  conditionInfoToRawCondition
14555
14629
  );
14556
14630
  }
14557
- function runsOnEveryPath(node, root) {
14631
+ function cleanupStartLine(node, root) {
14632
+ let found;
14558
14633
  let current2 = node;
14559
14634
  while (current2 !== void 0 && current2 !== root) {
14560
14635
  const parent = current2.getParent();
14561
14636
  if (parent !== void 0 && Node54.isTryStatement(parent) && parent.getFinallyBlock() === current2) {
14562
- return true;
14637
+ found = startLineOf(parent);
14563
14638
  }
14564
14639
  current2 = parent;
14565
14640
  }
14566
- return false;
14641
+ return found;
14642
+ }
14643
+ function placedAt(effect, node) {
14644
+ return { ...effect, location: lineRangeOf(node) };
14567
14645
  }
14568
14646
  function withPreconditions(effect, preconditions) {
14569
14647
  if (preconditions.length === 0) {
@@ -14595,6 +14673,9 @@ function inlineModuleBinding(ident, depth) {
14595
14673
  if (!isModuleScoped(decl)) {
14596
14674
  continue;
14597
14675
  }
14676
+ if (decl.getVariableStatement()?.getDeclarationKind() !== VariableDeclarationKind2.Const) {
14677
+ continue;
14678
+ }
14598
14679
  const init = decl.getInitializer();
14599
14680
  if (init === void 0) {
14600
14681
  continue;
@@ -15311,8 +15392,8 @@ function typeToShape(type, ctx) {
15311
15392
  return { type: "number" };
15312
15393
  }
15313
15394
  if (type.isBooleanLiteral()) {
15314
- const text3 = type.getText(ctx.enclosing);
15315
- return { type: "literal", value: text3 === "true" };
15395
+ const text4 = type.getText(ctx.enclosing);
15396
+ return { type: "literal", value: text4 === "true" };
15316
15397
  }
15317
15398
  if (type.isString()) {
15318
15399
  return { type: "text" };
@@ -15376,9 +15457,9 @@ function typeToShape(type, ctx) {
15376
15457
  if (type.isEnum() || type.isEnumLiteral()) {
15377
15458
  return enumToShape(type, ctx);
15378
15459
  }
15379
- const text2 = printedTypeName(type, ctx);
15380
- if (text2 && text2.length > 0 && text2 !== "__type") {
15381
- return { type: "ref", name: text2 };
15460
+ const text3 = printedTypeName(type, ctx);
15461
+ if (text3 && text3.length > 0 && text3 !== "__type") {
15462
+ return { type: "ref", name: text3 };
15382
15463
  }
15383
15464
  return null;
15384
15465
  }
@@ -15521,8 +15602,8 @@ function opaqueNamedRef(type, ctx) {
15521
15602
  if (!OPAQUE_NAMED_TYPES.has(name)) {
15522
15603
  return null;
15523
15604
  }
15524
- const text2 = printedTypeName(type, ctx);
15525
- return { type: "ref", name: text2 && text2.length > 0 ? text2 : name };
15605
+ const text3 = printedTypeName(type, ctx);
15606
+ return { type: "ref", name: text3 && text3.length > 0 ? text3 : name };
15526
15607
  }
15527
15608
  function outsideProjectRef(type, ctx) {
15528
15609
  const symbol = type.getSymbol();
@@ -15588,8 +15669,8 @@ function definitionKeyFor(type, name, reader) {
15588
15669
  const written = stableTypeText(type, reader);
15589
15670
  return `${name}@${shortHash(written)}`;
15590
15671
  }
15591
- function shortHash(text2) {
15592
- return createHash("sha1").update(text2).digest("hex").slice(0, 12);
15672
+ function shortHash(text3) {
15673
+ return createHash("sha1").update(text3).digest("hex").slice(0, 12);
15593
15674
  }
15594
15675
  function refFromType(type, ctx) {
15595
15676
  const symbol = type.getAliasSymbol() ?? type.getSymbol();
@@ -16271,11 +16352,11 @@ function collectJsxAttributes(opening) {
16271
16352
  }
16272
16353
  function jsxChildToRenderNode(child) {
16273
16354
  if (Node61.isJsxText(child)) {
16274
- const text2 = child.getLiteralText().trim();
16275
- if (text2.length === 0) {
16355
+ const text3 = child.getLiteralText().trim();
16356
+ if (text3.length === 0) {
16276
16357
  return null;
16277
16358
  }
16278
- return { type: "text", value: text2 };
16359
+ return { type: "text", value: text3 };
16279
16360
  }
16280
16361
  if (Node61.isJsxExpression(child)) {
16281
16362
  const inner = child.getExpression();
@@ -17274,11 +17355,12 @@ function makeFallthroughTerminal(func, pattern) {
17274
17355
 
17275
17356
  // src/assembly.ts
17276
17357
  var preconditionsOf = (effect) => effect.type === "invocation" ? effect.preconditions : void 0;
17277
- function firesOn(site, branch) {
17278
- if (!guardsHoldOn(site.preconditions, branch.conditions)) {
17279
- return false;
17280
- }
17281
- return site.alwaysRuns || runsBefore(site.line, branch.terminal.location.end);
17358
+ function invocationSite(call) {
17359
+ return {
17360
+ line: call.line,
17361
+ preconditions: preconditionsOf(call.effect),
17362
+ cleansUpFrom: call.cleansUpFrom
17363
+ };
17282
17364
  }
17283
17365
  function writesTerminal(call, terminalNodes) {
17284
17366
  let current2 = call;
@@ -17376,7 +17458,7 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
17376
17458
  const endings = new Set(
17377
17459
  terminals.filter(({ terminal }) => terminal.kind === "exit").map(({ node }) => node)
17378
17460
  );
17379
- const { byTerminal } = computePathConditions(
17461
+ const { byTerminal, placement } = computePathConditions(
17380
17462
  func,
17381
17463
  terminals.map(({ node }) => node),
17382
17464
  barriers,
@@ -17409,6 +17491,7 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
17409
17491
  const rawBranches = terminals.flatMap(
17410
17492
  ({ node, terminal, whenAlso }) => {
17411
17493
  const conditionLists = byTerminal.get(node) ?? [];
17494
+ const loopAround = placement.get(node)?.loopAround;
17412
17495
  return conditionLists.map((infos) => {
17413
17496
  const conditions = [
17414
17497
  ...infos.map(conditionInfoToRawCondition),
@@ -17419,6 +17502,7 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
17419
17502
  terminal,
17420
17503
  effects: [],
17421
17504
  location: terminal.location,
17505
+ ...loopAround === void 0 ? {} : { loopAround },
17422
17506
  isDefault: isDefaultConditionList(infos)
17423
17507
  };
17424
17508
  });
@@ -17445,9 +17529,7 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
17445
17529
  (i) => !writesTerminal(i.node, terminalNodes)
17446
17530
  );
17447
17531
  for (const branch of distinctBranches) {
17448
- branch.effects = sideEffects.filter(
17449
- (i) => firesOn({ ...i, preconditions: preconditionsOf(i.effect) }, branch)
17450
- ).map((i) => i.effect);
17532
+ branch.effects = sideEffects.filter((i) => firesOn(invocationSite(i), branch)).map((i) => i.effect);
17451
17533
  }
17452
17534
  }
17453
17535
  const firing = /* @__PURE__ */ new Map();
@@ -17492,11 +17574,9 @@ function handOffSites(invocations, terminalNodes, handOff, reading) {
17492
17574
  continue;
17493
17575
  }
17494
17576
  sites.push({
17577
+ ...invocationSite(site),
17495
17578
  helper,
17496
- preconditions: preconditionsOf(site.effect),
17497
- line: site.line,
17498
- at: lineRangeOf(site.node),
17499
- alwaysRuns: site.alwaysRuns
17579
+ at: lineRangeOf(site.node)
17500
17580
  });
17501
17581
  }
17502
17582
  return sites;
@@ -17522,16 +17602,12 @@ function accountFor(site, terminalNodes, branches) {
17522
17602
  if (writesTerminal(node, terminalNodes)) {
17523
17603
  return { node, callee, line, outcome: "terminal" };
17524
17604
  }
17525
- const call = {
17526
- line,
17527
- alwaysRuns: site.alwaysRuns,
17528
- preconditions: preconditionsOf(site.effect)
17529
- };
17605
+ const call = invocationSite(site);
17530
17606
  if (branches.some((branch) => firesOn(call, branch))) {
17531
17607
  return { node, callee, line, outcome: "invocation" };
17532
17608
  }
17533
17609
  const reachable = branches.some(
17534
- (branch) => site.alwaysRuns || runsBefore(line, branch.terminal.location.end)
17610
+ (branch) => runsBefore(call.cleansUpFrom ?? line, branch.terminal.location.end)
17535
17611
  );
17536
17612
  return {
17537
17613
  node,
@@ -18220,6 +18296,10 @@ function parameterName(segment, convention) {
18220
18296
  return null;
18221
18297
  }
18222
18298
 
18299
+ // src/discovery/graphqlCallClient.ts
18300
+ import { Node as Node70 } from "ts-morph";
18301
+ import { force as force6, hole, text as text2 } from "@suss/values";
18302
+
18223
18303
  // src/discovery/graphqlClientConstruction.ts
18224
18304
  import picomatch2 from "picomatch";
18225
18305
  import {
@@ -18231,34 +18311,123 @@ import {
18231
18311
  withGraphqlMetadata
18232
18312
  } from "@suss/behavioral-ir";
18233
18313
  function stampGraphqlClientRefs(summaries, sourceFiles, packs, resolution) {
18234
- const scopes = compileOperationScopes(packs);
18314
+ const constructions = graphqlClientConstructions(
18315
+ sourceFiles,
18316
+ packs.flatMap((pack) => pack.graphqlClients ?? []),
18317
+ resolution
18318
+ );
18235
18319
  const sole = soleGraphqlClientRef(
18236
- collectGraphqlClientRefs(sourceFiles, packs, resolution)
18320
+ constructionRefs(constructions, resolution)
18237
18321
  );
18238
- const workspace = sole !== null ? boundWorkspaceFor(sole, packs) : null;
18239
- const soleClient = sole !== null ? workspace !== null ? { ...sole, workspace } : sole : null;
18322
+ const grounding = {
18323
+ packs,
18324
+ scopes: compileOperationScopes(packs),
18325
+ sole,
18326
+ soleClient: withBoundWorkspace(sole, packs),
18327
+ clientsBuilt: constructions.filter(
18328
+ (construction) => construction.spec.linkProperty !== void 0
18329
+ ).length,
18330
+ pairWithAll: packs.some(
18331
+ (pack) => pack.graphqlUnboundClients === "pairWithAll"
18332
+ )
18333
+ };
18240
18334
  let registry = null;
18241
18335
  for (const summary of summaries) {
18242
18336
  if (!isGraphqlOperationBinding(summary.identity.boundaryBinding)) {
18243
18337
  continue;
18244
18338
  }
18245
18339
  const existing = readGraphqlMetadata(summary) ?? {};
18246
- const scoped = scopedWorkspaceFor(summary.location.file, scopes);
18247
- const client = scoped !== null ? { uri: null, uriRef: null, workspace: scoped } : soleClient;
18340
+ const decided = clientFor(summary, existing, grounding);
18248
18341
  const dangling = (existing.unresolvedFragments?.length ?? 0) > 0;
18249
- if (client === null && !dangling) {
18342
+ if (decided === null && !dangling) {
18250
18343
  continue;
18251
18344
  }
18252
18345
  if (dangling && registry === null) {
18253
18346
  registry = fragmentRegistryStatus(sourceFiles, packs, resolution);
18254
18347
  }
18255
18348
  summary.metadata = withGraphqlMetadata(summary.metadata, {
18256
- ...existing,
18257
- ...client !== null ? { client } : {},
18349
+ ...decided === null ? existing : withoutClient(existing),
18350
+ ...decided ?? {},
18258
18351
  ...dangling && registry !== null ? { fragmentRegistry: registry } : {}
18259
18352
  });
18260
18353
  }
18261
18354
  }
18355
+ function withBoundWorkspace(sole, packs) {
18356
+ if (sole === null) {
18357
+ return null;
18358
+ }
18359
+ const workspace = boundWorkspaceFor(sole, packs);
18360
+ return workspace === null ? sole : { ...sole, workspace };
18361
+ }
18362
+ function underUnboundSetting(unresolved, grounding) {
18363
+ return grounding.pairWithAll ? { ...unresolved, pairWithAll: true } : unresolved;
18364
+ }
18365
+ function clientFor(summary, existing, grounding) {
18366
+ const scoped = scopedWorkspaceFor(summary.location.file, grounding.scopes);
18367
+ const chosen = existing.client?.chosenAtCall === true ? existing.client : null;
18368
+ if (chosen === null) {
18369
+ const open = existing.unresolvedClient !== void 0;
18370
+ if (existing.unresolvedClient !== void 0 && !oneEndpointToChoose(grounding)) {
18371
+ return grounding.pairWithAll ? {
18372
+ unresolvedClient: {
18373
+ ...existing.unresolvedClient,
18374
+ pairWithAll: true
18375
+ }
18376
+ } : null;
18377
+ }
18378
+ const client = scoped !== null ? { uri: null, uriRef: null, workspace: scoped } : grounding.soleClient;
18379
+ if (client === null) {
18380
+ return open ? {} : null;
18381
+ }
18382
+ return { client };
18383
+ }
18384
+ const passed = {
18385
+ uri: chosen.uri,
18386
+ uriRef: chosen.uriRef,
18387
+ chosenAtCall: true
18388
+ };
18389
+ const bound = boundWorkspaceFor(passed, grounding.packs) ?? scoped;
18390
+ if (bound !== null) {
18391
+ return { client: { ...passed, workspace: bound } };
18392
+ }
18393
+ if (isTheAppsClient(passed, grounding)) {
18394
+ return { client: passed };
18395
+ }
18396
+ return {
18397
+ client: passed,
18398
+ unresolvedClient: underUnboundSetting(unboundClient(passed), grounding)
18399
+ };
18400
+ }
18401
+ function isTheAppsClient(passed, grounding) {
18402
+ if (grounding.clientsBuilt === 1) {
18403
+ return true;
18404
+ }
18405
+ const key = endpointKeyOf(passed);
18406
+ return key !== null && grounding.sole !== null && key === endpointKeyOf(grounding.sole);
18407
+ }
18408
+ function oneEndpointToChoose(grounding) {
18409
+ return grounding.clientsBuilt <= 1 || grounding.sole !== null;
18410
+ }
18411
+ function unboundClient(passed) {
18412
+ const key = endpointKeyOf(passed);
18413
+ if (key === null) {
18414
+ return {
18415
+ reference: "the client it passes",
18416
+ 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"
18417
+ };
18418
+ }
18419
+ return {
18420
+ reference: key,
18421
+ 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`
18422
+ };
18423
+ }
18424
+ function endpointKeyOf(ref) {
18425
+ return ref.uri ?? ref.uriRef;
18426
+ }
18427
+ function withoutClient(existing) {
18428
+ const { client: _client, unresolvedClient: _unresolved, ...rest } = existing;
18429
+ return rest;
18430
+ }
18262
18431
  function compileOperationScopes(packs) {
18263
18432
  return packs.flatMap((pack) => pack.graphqlOperationScopes ?? []).map((scope) => {
18264
18433
  const matchers2 = scope.files.map(
@@ -18291,13 +18460,22 @@ function boundWorkspaceFor(ref, packs) {
18291
18460
  }
18292
18461
  return null;
18293
18462
  }
18294
- function collectGraphqlClientRefs(sourceFiles, packs, resolution) {
18295
- const specs = packs.flatMap((pack) => pack.graphqlClients ?? []);
18463
+ function constructionRefs(constructions, resolution) {
18464
+ return constructions.flatMap((construction) => {
18465
+ const found = constructionRef(
18466
+ construction.node,
18467
+ construction.spec.uriProperty,
18468
+ resolution
18469
+ );
18470
+ return found === null ? [] : [found];
18471
+ });
18472
+ }
18473
+ function graphqlClientConstructions(sourceFiles, specs, resolution) {
18296
18474
  if (specs.length === 0) {
18297
18475
  return [];
18298
18476
  }
18299
18477
  const store = resolution ?? new ResolutionStore();
18300
- const refs = [];
18478
+ const found = [];
18301
18479
  for (const sourceFile of sourceFiles) {
18302
18480
  for (const spec of specs) {
18303
18481
  const constructions = callsByOriginName(
@@ -18307,14 +18485,27 @@ function collectGraphqlClientRefs(sourceFiles, packs, resolution) {
18307
18485
  /* @__PURE__ */ new Set([spec.importName])
18308
18486
  );
18309
18487
  for (const node of constructions.keys()) {
18310
- const found = constructionRef(node, spec.uriProperty, resolution);
18311
- if (found !== null) {
18312
- refs.push(found);
18313
- }
18488
+ found.push({ node, spec });
18314
18489
  }
18315
18490
  }
18316
18491
  }
18317
- return refs;
18492
+ return found;
18493
+ }
18494
+ function constructionEndpoint(construction, transports, resolution) {
18495
+ const own = constructionRef(
18496
+ construction.node,
18497
+ construction.spec.uriProperty,
18498
+ resolution
18499
+ );
18500
+ const linkProperty = construction.spec.linkProperty;
18501
+ if (own !== null || linkProperty === void 0) {
18502
+ return own;
18503
+ }
18504
+ const options = firstArgumentObject(construction.node, resolution);
18505
+ const link = options === null ? null : propertyOf(options, linkProperty, resolution);
18506
+ const transport = link === null ? null : writtenNodeOf(link, resolution);
18507
+ const built = transports.find((one) => one.node === transport);
18508
+ return built === void 0 ? null : constructionRef(built.node, built.spec.uriProperty, resolution);
18318
18509
  }
18319
18510
  function soleGraphqlClientRef(refs) {
18320
18511
  const distinct = /* @__PURE__ */ new Map();
@@ -18437,11 +18628,153 @@ function constructionRef(node, uriProperty, resolution) {
18437
18628
  return { uri: null, uriRef: value.getText() };
18438
18629
  }
18439
18630
 
18631
+ // src/discovery/graphqlCallClient.ts
18632
+ var REFERENCE_LIMIT = 80;
18633
+ function clientChosenAt(site, specs, resolution) {
18634
+ const store = resolution ?? new ResolutionStore();
18635
+ const written = writtenClientOf(site, store);
18636
+ if (written === null) {
18637
+ return null;
18638
+ }
18639
+ const constructions = constructionsInProject(
18640
+ site.call.getSourceFile(),
18641
+ specs,
18642
+ store
18643
+ );
18644
+ const tokens = new Map(
18645
+ constructions.map((construction, at) => [
18646
+ construction.node,
18647
+ text2(`suss:client#${at}`)
18648
+ ])
18649
+ );
18650
+ const value = evaluatedValueThrough(
18651
+ written.node,
18652
+ site.through,
18653
+ store,
18654
+ tokens
18655
+ );
18656
+ const chosen = value === null ? unreadChain(written, store) : clientValueOf(value, written.property);
18657
+ if (chosen === void 0) {
18658
+ return null;
18659
+ }
18660
+ const picked = tokensIn(chosen, constructions.length);
18661
+ if (picked !== null && picked.length === 1) {
18662
+ const construction = constructions[picked[0]];
18663
+ const endpoint = construction === void 0 ? null : constructionEndpoint(construction, constructions, store);
18664
+ return {
18665
+ kind: "settled",
18666
+ client: endpoint ?? { uri: null, uriRef: null }
18667
+ };
18668
+ }
18669
+ if (picked === null && written.property === null) {
18670
+ return null;
18671
+ }
18672
+ const reference = referenceOf(written.node, written.property, store);
18673
+ const passes = written.property === null ? "the client it is called on" : "the client it passes";
18674
+ if (picked !== null && picked.length > 1) {
18675
+ return {
18676
+ kind: "unresolved",
18677
+ reference,
18678
+ reason: `${passes}, ${reference}, can be any of ${picked.length} clients, and nothing the call passes settles which`
18679
+ };
18680
+ }
18681
+ return {
18682
+ kind: "unresolved",
18683
+ reference,
18684
+ reason: `${passes}, ${reference}, does not come out as one client construction suss can read`
18685
+ };
18686
+ }
18687
+ function writtenClientOf(site, store) {
18688
+ if (site.client === "receiver") {
18689
+ const callee = site.call.getExpression();
18690
+ return Node70.isPropertyAccessExpression(callee) ? { node: callee.getExpression(), property: null } : null;
18691
+ }
18692
+ const options = site.call.getArguments()[site.client.argument];
18693
+ if (options === void 0) {
18694
+ return null;
18695
+ }
18696
+ const direct = site.through.length === 0;
18697
+ if (direct && optionWritten(options, site.client.property, store) === null) {
18698
+ return null;
18699
+ }
18700
+ return { node: options, property: site.client.property };
18701
+ }
18702
+ function clientValueOf(value, property) {
18703
+ const forced = force6(value);
18704
+ if (property === null) {
18705
+ return isUndefined(forced) ? void 0 : forced;
18706
+ }
18707
+ if (forced.kind !== "record") {
18708
+ return void 0;
18709
+ }
18710
+ const field = forced.fields.get(property);
18711
+ if (field === void 0 || isUndefined(force6(field.value))) {
18712
+ return void 0;
18713
+ }
18714
+ return force6(field.value);
18715
+ }
18716
+ function unreadChain(written, store) {
18717
+ if (written.property === null) {
18718
+ return hole("client");
18719
+ }
18720
+ return optionWritten(written.node, written.property, store) === null ? void 0 : hole("client");
18721
+ }
18722
+ function isUndefined(value) {
18723
+ return value.kind === "constant" && value.options.every((option) => option === void 0);
18724
+ }
18725
+ function tokensIn(value, count) {
18726
+ if (value.kind !== "string" || value.pieces.length !== 1) {
18727
+ return null;
18728
+ }
18729
+ const piece = value.pieces[0];
18730
+ if (piece?.kind !== "text") {
18731
+ return null;
18732
+ }
18733
+ const picked = [];
18734
+ for (const option of piece.options) {
18735
+ const at = /^suss:client#(\d+)$/.exec(option)?.[1];
18736
+ if (at === void 0 || Number(at) >= count) {
18737
+ return null;
18738
+ }
18739
+ picked.push(Number(at));
18740
+ }
18741
+ return picked;
18742
+ }
18743
+ function referenceOf(node, property, store) {
18744
+ const written = property === null ? node : optionWritten(node, property, store);
18745
+ const shown = written === null ? `${node.getText()}.${property}` : writtenText(written, store);
18746
+ return shown.length > REFERENCE_LIMIT ? `${shown.slice(0, REFERENCE_LIMIT - 3)}...` : shown;
18747
+ }
18748
+ function optionWritten(options, property, store) {
18749
+ const literal = objectLiteralOf(options, store);
18750
+ return literal === null ? null : propertyOf(literal, property, store);
18751
+ }
18752
+ function writtenText(node, store) {
18753
+ if (!Node70.isIdentifier(node)) {
18754
+ return node.getText();
18755
+ }
18756
+ const value = store.resolveWrittenValue(node);
18757
+ return value === null ? node.getText() : value.getText();
18758
+ }
18759
+ var constructionsByStore = /* @__PURE__ */ new WeakMap();
18760
+ function constructionsInProject(sourceFile, specs, store) {
18761
+ const bySpecs = constructionsByStore.get(store) ?? /* @__PURE__ */ new WeakMap();
18762
+ constructionsByStore.set(store, bySpecs);
18763
+ const known = bySpecs.get(specs);
18764
+ if (known !== void 0) {
18765
+ return known;
18766
+ }
18767
+ const files = sourceFile.getProject().getSourceFiles().filter((file) => !file.isDeclarationFile() && !file.isInNodeModules());
18768
+ const found = graphqlClientConstructions(files, specs, store);
18769
+ bySpecs.set(specs, found);
18770
+ return found;
18771
+ }
18772
+
18440
18773
  // src/discovery/helperIndex.ts
18441
- import { Node as Node71 } from "ts-morph";
18774
+ import { Node as Node72 } from "ts-morph";
18442
18775
 
18443
18776
  // src/discovery/helperReading.ts
18444
- import { Node as Node70 } from "ts-morph";
18777
+ import { Node as Node71 } from "ts-morph";
18445
18778
  var UNREAD = { as: "unread" };
18446
18779
  function readHelperSinks(helper) {
18447
18780
  const positions = parameterPositions(helper);
@@ -18451,11 +18784,11 @@ function readHelperSinks(helper) {
18451
18784
  return sinks;
18452
18785
  }
18453
18786
  body.forEachDescendant((node) => {
18454
- if (!Node70.isCallExpression(node)) {
18787
+ if (!Node71.isCallExpression(node)) {
18455
18788
  return;
18456
18789
  }
18457
18790
  const callee = node.getExpression();
18458
- const [method, receiver] = Node70.isPropertyAccessExpression(callee) ? [callee.getName(), readValue(callee.getExpression(), positions)] : [null, UNREAD];
18791
+ const [method, receiver] = Node71.isPropertyAccessExpression(callee) ? [callee.getName(), readValue(callee.getExpression(), positions)] : [null, UNREAD];
18459
18792
  sinks.push({
18460
18793
  method,
18461
18794
  receiver,
@@ -18475,25 +18808,25 @@ function parameterPositions(helper) {
18475
18808
  return positions;
18476
18809
  }
18477
18810
  function readValue(node, positions) {
18478
- if (Node70.isStringLiteral(node) || Node70.isNoSubstitutionTemplateLiteral(node)) {
18811
+ if (Node71.isStringLiteral(node) || Node71.isNoSubstitutionTemplateLiteral(node)) {
18479
18812
  return { as: "text", text: node.getLiteralValue() };
18480
18813
  }
18481
- if (Node70.isTemplateExpression(node)) {
18814
+ if (Node71.isTemplateExpression(node)) {
18482
18815
  return templateText(node, positions);
18483
18816
  }
18484
- if (Node70.isIdentifier(node)) {
18817
+ if (Node71.isIdentifier(node)) {
18485
18818
  const position = positions.get(node.getText());
18486
18819
  return position === void 0 ? UNREAD : { as: "parameter", position };
18487
18820
  }
18488
- if (Node70.isPropertyAccessExpression(node)) {
18821
+ if (Node71.isPropertyAccessExpression(node)) {
18489
18822
  const target = node.getExpression();
18490
- const position = Node70.isIdentifier(target) ? positions.get(target.getText()) : void 0;
18823
+ const position = Node71.isIdentifier(target) ? positions.get(target.getText()) : void 0;
18491
18824
  return position === void 0 ? UNREAD : { as: "parameter", position, property: node.getName() };
18492
18825
  }
18493
- if (Node70.isObjectLiteralExpression(node)) {
18826
+ if (Node71.isObjectLiteralExpression(node)) {
18494
18827
  return objectValue(node, positions);
18495
18828
  }
18496
- if (Node70.isCallExpression(node)) {
18829
+ if (Node71.isCallExpression(node)) {
18497
18830
  return {
18498
18831
  as: "call",
18499
18832
  callee: node.getExpression().getText(),
@@ -18503,27 +18836,27 @@ function readValue(node, positions) {
18503
18836
  return UNREAD;
18504
18837
  }
18505
18838
  function templateText(node, positions) {
18506
- if (!Node70.isTemplateExpression(node)) {
18839
+ if (!Node71.isTemplateExpression(node)) {
18507
18840
  return UNREAD;
18508
18841
  }
18509
- let text2 = node.getHead().getLiteralText();
18842
+ let text3 = node.getHead().getLiteralText();
18510
18843
  for (const span of node.getTemplateSpans()) {
18511
18844
  const expression = span.getExpression();
18512
- const position = Node70.isIdentifier(expression) ? positions.get(expression.getText()) : void 0;
18845
+ const position = Node71.isIdentifier(expression) ? positions.get(expression.getText()) : void 0;
18513
18846
  if (position === void 0) {
18514
18847
  return UNREAD;
18515
18848
  }
18516
- text2 += `{${position}}${span.getLiteral().getLiteralText()}`;
18849
+ text3 += `{${position}}${span.getLiteral().getLiteralText()}`;
18517
18850
  }
18518
- return { as: "text", text: text2 };
18851
+ return { as: "text", text: text3 };
18519
18852
  }
18520
18853
  function objectValue(node, positions) {
18521
- if (!Node70.isObjectLiteralExpression(node)) {
18854
+ if (!Node71.isObjectLiteralExpression(node)) {
18522
18855
  return UNREAD;
18523
18856
  }
18524
18857
  const properties = {};
18525
18858
  for (const property of node.getProperties()) {
18526
- if (!Node70.isPropertyAssignment(property)) {
18859
+ if (!Node71.isPropertyAssignment(property)) {
18527
18860
  continue;
18528
18861
  }
18529
18862
  const initializer = property.getInitializer();
@@ -18538,7 +18871,7 @@ function objectValue(node, positions) {
18538
18871
  return { as: "object", properties };
18539
18872
  }
18540
18873
  function propertyKey(name) {
18541
- return Node70.isStringLiteral(name) ? name.getLiteralValue() : name.getText();
18874
+ return Node71.isStringLiteral(name) ? name.getLiteralValue() : name.getText();
18542
18875
  }
18543
18876
 
18544
18877
  // src/discovery/helperIndex.ts
@@ -18622,8 +18955,8 @@ function noteExpanded(expanded, readings, discovery) {
18622
18955
  }
18623
18956
  }
18624
18957
  function mentionsAnyOf(file, written) {
18625
- const text2 = file.getFullText();
18626
- return written.some((word) => text2.includes(word));
18958
+ const text3 = file.getFullText();
18959
+ return written.some((word) => text3.includes(word));
18627
18960
  }
18628
18961
  function readSubjectHelpers(pack, packsByFile, resolution) {
18629
18962
  const matches = registrationMatchesOf(pack);
@@ -18668,11 +19001,11 @@ function registrationMatchesOf(pack) {
18668
19001
  }
18669
19002
  function collectHandOffs(sourceFile, subjects, match, resolution, found) {
18670
19003
  sourceFile.forEachDescendant((node) => {
18671
- if (!Node71.isCallExpression(node)) {
19004
+ if (!Node72.isCallExpression(node)) {
18672
19005
  return;
18673
19006
  }
18674
19007
  const callee = node.getExpression();
18675
- if (!Node71.isIdentifier(callee)) {
19008
+ if (!Node72.isIdentifier(callee)) {
18676
19009
  return;
18677
19010
  }
18678
19011
  const handedOver = subjectArgument(node, subjects, match, resolution);
@@ -18687,12 +19020,12 @@ function collectHandOffs(sourceFile, subjects, match, resolution, found) {
18687
19020
  });
18688
19021
  }
18689
19022
  function subjectArgument(call, subjects, match, resolution) {
18690
- if (!Node71.isCallExpression(call)) {
19023
+ if (!Node72.isCallExpression(call)) {
18691
19024
  return -1;
18692
19025
  }
18693
19026
  const args = call.getArguments();
18694
19027
  for (const [position, argument] of args.entries()) {
18695
- if (!Node71.isIdentifier(argument) && !Node71.isPropertyAccessExpression(argument)) {
19028
+ if (!Node72.isIdentifier(argument) && !Node72.isPropertyAccessExpression(argument)) {
18696
19029
  continue;
18697
19030
  }
18698
19031
  if (subjectNodeFor(argument, subjects, match, resolution) !== void 0) {
@@ -18724,8 +19057,8 @@ function record(found, helper, name, subjectParameter) {
18724
19057
  function readTextHelpers(sourceFiles, contains) {
18725
19058
  const readings = [];
18726
19059
  for (const sourceFile of sourceFiles) {
18727
- const text2 = sourceFile.getFullText();
18728
- if (!contains.some((mark) => text2.includes(mark))) {
19060
+ const text3 = sourceFile.getFullText();
19061
+ if (!contains.some((mark) => text3.includes(mark))) {
18729
19062
  continue;
18730
19063
  }
18731
19064
  for (const [name, declaration] of declaredFunctions(sourceFile)) {
@@ -18753,7 +19086,7 @@ function declaredFunctions(sourceFile) {
18753
19086
  }
18754
19087
  for (const variable3 of sourceFile.getVariableDeclarations()) {
18755
19088
  const initializer = variable3.getInitializer();
18756
- if (initializer !== void 0 && (Node71.isArrowFunction(initializer) || Node71.isFunctionExpression(initializer))) {
19089
+ if (initializer !== void 0 && (Node72.isArrowFunction(initializer) || Node72.isFunctionExpression(initializer))) {
18757
19090
  found.push([variable3.getName(), initializer]);
18758
19091
  }
18759
19092
  }
@@ -18924,8 +19257,8 @@ function recordEdge(edgesByChild, candidate) {
18924
19257
  }
18925
19258
 
18926
19259
  // src/discovery/restWrapper.ts
18927
- import { Node as Node72 } from "ts-morph";
18928
- import { hole } from "@suss/values";
19260
+ import { Node as Node73 } from "ts-morph";
19261
+ import { hole as hole2 } from "@suss/values";
18929
19262
  var MAX_WRAPPER_DEPTH2 = 3;
18930
19263
  function expandRestWrapperCallers(wrapper, readSink, store) {
18931
19264
  return expandFrom(
@@ -18941,8 +19274,8 @@ function parametersAsHoles(func) {
18941
19274
  const bindings = /* @__PURE__ */ new Map();
18942
19275
  for (const parameter of func.getParameters()) {
18943
19276
  const nameNode = parameter.getNameNode();
18944
- if (Node72.isIdentifier(nameNode)) {
18945
- bindings.set(nameNode.getText(), hole(nameNode.getText()));
19277
+ if (Node73.isIdentifier(nameNode)) {
19278
+ bindings.set(nameNode.getText(), hole2(nameNode.getText()));
18946
19279
  }
18947
19280
  }
18948
19281
  return bindings;
@@ -18981,11 +19314,11 @@ function callSitesOf(func, store) {
18981
19314
  const byCall = /* @__PURE__ */ new Map();
18982
19315
  for (const parameter of func.getParameters()) {
18983
19316
  const nameNode = parameter.getNameNode();
18984
- if (!Node72.isIdentifier(nameNode)) {
19317
+ if (!Node73.isIdentifier(nameNode)) {
18985
19318
  continue;
18986
19319
  }
18987
19320
  for (const passed of store.argumentsPassedTo(parameter)) {
18988
- if (!Node72.isCallExpression(passed.call)) {
19321
+ if (!Node73.isCallExpression(passed.call)) {
18989
19322
  continue;
18990
19323
  }
18991
19324
  const key = siteKey(passed.call);
@@ -19011,7 +19344,7 @@ function valuesAt(site, bindings, store) {
19011
19344
  }
19012
19345
 
19013
19346
  // src/discovery/wrapperIndex.ts
19014
- import { Node as Node73 } from "ts-morph";
19347
+ import { Node as Node74 } from "ts-morph";
19015
19348
  import { joinMountedPath as joinMountedPath3 } from "@suss/resolution";
19016
19349
  var NO_WRAPPERS = {
19017
19350
  wrappersFor: () => [],
@@ -19135,11 +19468,11 @@ function discoverWrapperRegistrations(sourceFile, pattern, match, wraps, resolut
19135
19468
  const candidates = [];
19136
19469
  const unresolved = [];
19137
19470
  sourceFile.forEachDescendant((node) => {
19138
- if (!Node73.isCallExpression(node)) {
19471
+ if (!Node74.isCallExpression(node)) {
19139
19472
  return;
19140
19473
  }
19141
19474
  const callee = node.getExpression();
19142
- if (!Node73.isPropertyAccessExpression(callee) || callee.getName() !== wraps.method) {
19475
+ if (!Node74.isPropertyAccessExpression(callee) || callee.getName() !== wraps.method) {
19143
19476
  return;
19144
19477
  }
19145
19478
  const subjectNode = subjectNodeFor(
@@ -19234,7 +19567,7 @@ function discoverConstructorOptions(sourceFile, pattern, wraps, subjects, resolu
19234
19567
  return { candidates, unresolved };
19235
19568
  }
19236
19569
  function constructorOptionOf(construction, wraps, resolution) {
19237
- if (!(Node73.isNewExpression(construction) || Node73.isCallExpression(construction))) {
19570
+ if (!(Node74.isNewExpression(construction) || Node74.isCallExpression(construction))) {
19238
19571
  return void 0;
19239
19572
  }
19240
19573
  const optionsArg = construction.getArguments()[wraps.targetPosition];
@@ -19289,7 +19622,7 @@ function scopeOf(args, wraps, resolution) {
19289
19622
  }
19290
19623
 
19291
19624
  // src/discoveryContext.ts
19292
- import { Node as Node74 } from "ts-morph";
19625
+ import { Node as Node75 } from "ts-morph";
19293
19626
  function createTsDiscoveryContext(resolution) {
19294
19627
  const store = resolution ?? new ResolutionStore();
19295
19628
  return {
@@ -19325,7 +19658,7 @@ function exportedFunctions(sourceFile, resolution) {
19325
19658
  return out;
19326
19659
  }
19327
19660
  function exportedFunction(decl, resolution) {
19328
- const writtenAgain = Node74.isVariableDeclaration(decl) && isWrittenAgain(decl);
19661
+ const writtenAgain = Node75.isVariableDeclaration(decl) && isWrittenAgain(decl);
19329
19662
  if (!writtenAgain) {
19330
19663
  const declared = resolveDeclarationToFunction(decl);
19331
19664
  if (declared !== null) {
@@ -19346,7 +19679,7 @@ function valueToAskAbout(decl, writtenAgain) {
19346
19679
  if (writtenAgain) {
19347
19680
  return decl;
19348
19681
  }
19349
- const value = Node74.isVariableDeclaration(decl) ? decl.getInitializer() ?? decl : decl;
19682
+ const value = Node75.isVariableDeclaration(decl) ? decl.getInitializer() ?? decl : decl;
19350
19683
  return couldStillNameAFunction(value) ? value : null;
19351
19684
  }
19352
19685
  function resolveDeclarationToFunction(decl) {
@@ -19354,18 +19687,18 @@ function resolveDeclarationToFunction(decl) {
19354
19687
  if (fn !== null) {
19355
19688
  return fn;
19356
19689
  }
19357
- if (Node74.isVariableDeclaration(decl)) {
19690
+ if (Node75.isVariableDeclaration(decl)) {
19358
19691
  const init = decl.getInitializer();
19359
- if (init !== void 0 && (Node74.isArrowFunction(init) || Node74.isFunctionExpression(init))) {
19692
+ if (init !== void 0 && (Node75.isArrowFunction(init) || Node75.isFunctionExpression(init))) {
19360
19693
  return init;
19361
19694
  }
19362
19695
  }
19363
19696
  return null;
19364
19697
  }
19365
19698
  function hasJsxReturn(func) {
19366
- if (Node74.isArrowFunction(func)) {
19699
+ if (Node75.isArrowFunction(func)) {
19367
19700
  const body2 = func.getBody();
19368
- if (Node74.isExpression(body2) && isJsxOrFragment(body2)) {
19701
+ if (Node75.isExpression(body2) && isJsxOrFragment(body2)) {
19369
19702
  return true;
19370
19703
  }
19371
19704
  }
@@ -19379,11 +19712,11 @@ function hasJsxReturn(func) {
19379
19712
  traversal.stop();
19380
19713
  return;
19381
19714
  }
19382
- if (node !== func && (Node74.isFunctionDeclaration(node) || Node74.isFunctionExpression(node) || Node74.isArrowFunction(node) || Node74.isMethodDeclaration(node))) {
19715
+ if (node !== func && (Node75.isFunctionDeclaration(node) || Node75.isFunctionExpression(node) || Node75.isArrowFunction(node) || Node75.isMethodDeclaration(node))) {
19383
19716
  traversal.skip();
19384
19717
  return;
19385
19718
  }
19386
- if (Node74.isReturnStatement(node)) {
19719
+ if (Node75.isReturnStatement(node)) {
19387
19720
  const expr = node.getExpression();
19388
19721
  if (expr !== void 0 && isJsxOrFragment(expr)) {
19389
19722
  found = true;
@@ -19394,7 +19727,7 @@ function hasJsxReturn(func) {
19394
19727
  }
19395
19728
  function isJsxOrFragment(node) {
19396
19729
  const current2 = peelParens(node);
19397
- return Node74.isJsxElement(current2) || Node74.isJsxSelfClosingElement(current2) || Node74.isJsxFragment(current2);
19730
+ return Node75.isJsxElement(current2) || Node75.isJsxSelfClosingElement(current2) || Node75.isJsxFragment(current2);
19398
19731
  }
19399
19732
 
19400
19733
  // src/everyUnit.ts
@@ -19421,7 +19754,7 @@ function stampExitCodeFrom(summaries, project, resolution) {
19421
19754
  }
19422
19755
 
19423
19756
  // src/flagReads.ts
19424
- import { Node as Node75 } from "ts-morph";
19757
+ import { Node as Node76 } from "ts-morph";
19425
19758
  function flagReads(func, parameterNames2, parsers, resolution, barriers = NO_BARRIERS) {
19426
19759
  if (parsers.length === 0 || parameterNames2.length === 0 || resolution === void 0) {
19427
19760
  return [];
@@ -19454,11 +19787,11 @@ function namedCallsIn(func, barriers) {
19454
19787
  traversal.skip();
19455
19788
  return;
19456
19789
  }
19457
- if (!Node75.isCallExpression(node)) {
19790
+ if (!Node76.isCallExpression(node)) {
19458
19791
  return;
19459
19792
  }
19460
19793
  const callee = node.getExpression();
19461
- if (Node75.isIdentifier(callee) || Node75.isPropertyAccessExpression(callee)) {
19794
+ if (Node76.isIdentifier(callee) || Node76.isPropertyAccessExpression(callee)) {
19462
19795
  calls.push(node);
19463
19796
  }
19464
19797
  });
@@ -19682,7 +20015,7 @@ function createReferenceIndex(sourceFiles) {
19682
20015
  }
19683
20016
 
19684
20017
  // src/resolve/clientBasePath.ts
19685
- import { Node as Node76 } from "ts-morph";
20018
+ import { Node as Node77 } from "ts-morph";
19686
20019
  import { hasNameHole as hasNameHole2 } from "@suss/behavioral-ir";
19687
20020
  function clientBasePath(call, match, resolution, site) {
19688
20021
  if (match?.type !== "clientCall" || match.basePathOption === void 0) {
@@ -19724,13 +20057,14 @@ function clientConstructionConfig(call, match, resolution) {
19724
20057
  }
19725
20058
  function receiverOf2(call) {
19726
20059
  const callee = call.getExpression();
19727
- if (Node76.isPropertyAccessExpression(callee)) {
20060
+ if (Node77.isPropertyAccessExpression(callee)) {
19728
20061
  return callee.getExpression();
19729
20062
  }
19730
- return Node76.isIdentifier(callee) ? callee : null;
20063
+ return Node77.isIdentifier(callee) ? callee : null;
19731
20064
  }
19732
20065
 
19733
20066
  // src/resolve/closureRecords.ts
20067
+ import { plainKeyOf } from "@suss/resolution";
19734
20068
  function fromScanRecord(unit) {
19735
20069
  const files = [unit.file, ...unit.deps];
19736
20070
  const placeOf = ([file, start, end]) => {
@@ -19798,7 +20132,7 @@ function closureUnitRecords(scans, stillValid, references) {
19798
20132
  continue;
19799
20133
  }
19800
20134
  const file = fileOfOffsetKey(key);
19801
- if (file === null) {
20135
+ if (file === null || reachesASubclass(key, scan.findings)) {
19802
20136
  continue;
19803
20137
  }
19804
20138
  const deps = depsOf(file, scan.findings, scan.read, references);
@@ -19814,6 +20148,9 @@ function closureUnitRecords(scans, stillValid, references) {
19814
20148
  }
19815
20149
  return units;
19816
20150
  }
20151
+ function reachesASubclass(key, findings) {
20152
+ return key !== plainKeyOf(key) || findings.calls.some((call) => call.key !== plainKeyOf(call.key));
20153
+ }
19817
20154
  function filesNamedBy(findings) {
19818
20155
  const files = [];
19819
20156
  for (const call of findings.calls) {
@@ -19902,15 +20239,108 @@ function placeOfKey(key) {
19902
20239
  }
19903
20240
 
19904
20241
  // src/resolve/reachableClosure.ts
19905
- import { Node as Node77 } from "ts-morph";
20242
+ import { Node as Node79 } from "ts-morph";
19906
20243
  import {
19907
20244
  functionCallBinding,
19908
20245
  normalizeCalleeText as normalizeCalleeText2,
19909
20246
  placePossibleCalls,
20247
+ sameDeclarations,
19910
20248
  TargetPlacements
19911
20249
  } from "@suss/behavioral-ir";
19912
20250
  import { Database as Database3, evaluate as evaluate3, lit as lit2, rule as rule2, variable } from "@suss/datalog";
19913
20251
  import { assembleSummary as assembleSummary2 } from "@suss/extractor";
20252
+ import { keptVariants, plainKeyOf as plainKeyOf2, variantKey } from "@suss/resolution";
20253
+
20254
+ // src/resolve/receiverClass.ts
20255
+ import { Node as Node78 } from "ts-morph";
20256
+ function candidateOnReceiver(callee, checked, scan) {
20257
+ if (!Node78.isPropertyAccessExpression(callee)) {
20258
+ return null;
20259
+ }
20260
+ const receiver = callee.getExpression();
20261
+ const name = callee.getName();
20262
+ const { scanning } = scan;
20263
+ const own = scanning === void 0 ? null : ownForm(receiver, scanning, scan.resolution, 0);
20264
+ const selfClass = scan.runsOn ?? (scanning === void 0 ? null : classOf(scanning));
20265
+ if (own !== null && selfClass !== null) {
20266
+ return memberOn(selfClass, name, own);
20267
+ }
20268
+ if (checked === null || !Node78.isClassDeclaration(checked.func.getParent())) {
20269
+ return null;
20270
+ }
20271
+ const sentTo = classSentTo(receiver);
20272
+ return sentTo === null ? null : runningOn(checked, sentTo);
20273
+ }
20274
+ function runningOn(candidate, cls) {
20275
+ const owner = candidate.func.getParent();
20276
+ if (!Node78.isClassDeclaration(owner) || owner === cls.node) {
20277
+ return candidate;
20278
+ }
20279
+ return { ...candidate, receiverClass: cls };
20280
+ }
20281
+ function subclassPlace(cls) {
20282
+ return { name: cls.name, file: cls.file };
20283
+ }
20284
+ function ownThis(node, scanning) {
20285
+ const owner = node.getFirstAncestor(
20286
+ (at) => Node78.isFunctionDeclaration(at) || Node78.isFunctionExpression(at) || Node78.isMethodDeclaration(at) || Node78.isConstructorDeclaration(at)
20287
+ );
20288
+ return owner === scanning;
20289
+ }
20290
+ function ownForm(value, scanning, resolution, depth) {
20291
+ const written = peelSyntax(value);
20292
+ if (Node78.isThisExpression(written)) {
20293
+ if (!ownThis(written, scanning) || !Node78.isMethodDeclaration(scanning)) {
20294
+ return null;
20295
+ }
20296
+ return scanning.isStatic() ? "class" : "instance";
20297
+ }
20298
+ if (Node78.isNewExpression(written)) {
20299
+ return ownForm(written.getExpression(), scanning, resolution, depth) === "class" ? "instance" : null;
20300
+ }
20301
+ if (!Node78.isIdentifier(written) || depth > 0) {
20302
+ return null;
20303
+ }
20304
+ const behind = writtenNodeOf(written, resolution);
20305
+ return behind === null || behind === written ? null : ownForm(behind, scanning, resolution, depth + 1);
20306
+ }
20307
+ function classOf(scanning) {
20308
+ const owner = scanning.getParent();
20309
+ return Node78.isClassDeclaration(owner) ? subclassOf(owner) : null;
20310
+ }
20311
+ function subclassOf(node) {
20312
+ const name = node.getName();
20313
+ return name === void 0 ? null : { node, name, file: node.getSourceFile().getFilePath() };
20314
+ }
20315
+ function classSentTo(receiver) {
20316
+ const written = peelSyntax(receiver);
20317
+ const named2 = classDeclarationOf(declarationsBehind(written.getSymbol()));
20318
+ if (named2 !== null) {
20319
+ return subclassOf(named2);
20320
+ }
20321
+ const typed = classDeclarationOf(
20322
+ written.getType().getSymbol()?.getDeclarations() ?? []
20323
+ );
20324
+ return typed === null ? null : subclassOf(typed);
20325
+ }
20326
+ function classDeclarationOf(declarations) {
20327
+ const classes = declarations.filter((one) => Node78.isClassDeclaration(one));
20328
+ const [only] = classes;
20329
+ return classes.length === 1 && only !== void 0 ? only : null;
20330
+ }
20331
+ function memberOn(cls, name, form) {
20332
+ const member = typeOf(cls, form).getProperty(name);
20333
+ const found = member === void 0 ? null : functionAmong(member.getDeclarations(), name);
20334
+ return found === null ? null : runningOn(found, cls);
20335
+ }
20336
+ function typeOf(cls, form) {
20337
+ if (form === "instance") {
20338
+ return cls.node.getType();
20339
+ }
20340
+ return cls.node.getProject().getTypeChecker().getTypeOfSymbolAtLocation(cls.node.getSymbolOrThrow(), cls.node);
20341
+ }
20342
+
20343
+ // src/resolve/reachableClosure.ts
19914
20344
  var GENERIC_TERMINALS = [
19915
20345
  { kind: "return", match: { type: "returnStatement" }, extraction: {} },
19916
20346
  { kind: "throw", match: { type: "throwExpression" }, extraction: {} },
@@ -19945,19 +20375,35 @@ function targetOf(node) {
19945
20375
  span: { start: node.getStart(), end: node.getEnd() }
19946
20376
  };
19947
20377
  }
20378
+ function placeOfCandidate(candidate) {
20379
+ const place = targetOf(candidate.func);
20380
+ return candidate.receiverClass === void 0 ? place : { ...place, receiverClass: subclassPlace(candidate.receiverClass) };
20381
+ }
20382
+ function candidateKey(candidate) {
20383
+ const key = nodeKey(candidate.func);
20384
+ return candidate.receiverClass === void 0 ? key : variantKey(key, subclassPlace(candidate.receiverClass));
20385
+ }
19948
20386
  function resolveCallee(call, calleeName, scan) {
19949
- if (!Node77.isCallExpression(call)) {
20387
+ if (!Node79.isCallExpression(call)) {
19950
20388
  return null;
19951
20389
  }
19952
20390
  const callee = call.getExpression();
19953
20391
  const symbol = callee.getSymbol();
19954
20392
  const declarations = declarationsBehind(symbol);
19955
20393
  const direct = functionAmong(declarations, calleeName);
20394
+ const onReceiver = candidateOnReceiver(callee, direct, {
20395
+ scanning: scan.scanning,
20396
+ runsOn: scan.receiverClass,
20397
+ resolution: scan.resolution
20398
+ });
20399
+ if (onReceiver !== null) {
20400
+ return { kind: "followed", candidate: onReceiver };
20401
+ }
19956
20402
  if (direct !== null) {
19957
20403
  return { kind: "followed", candidate: direct };
19958
20404
  }
19959
20405
  const resolveSources = scan.resolveCallableSources;
19960
- if (resolveSources !== void 0 && Node77.isPropertyAccessExpression(callee) && declarations.some(isDeclaredShape)) {
20406
+ if (resolveSources !== void 0 && Node79.isPropertyAccessExpression(callee) && declarations.some(isDeclaredShape)) {
19961
20407
  if (scan.sourcesAsked !== void 0) {
19962
20408
  scan.sourcesAsked.asked = true;
19963
20409
  }
@@ -19986,7 +20432,7 @@ function resolveCallee(call, calleeName, scan) {
19986
20432
  );
19987
20433
  }
19988
20434
  function stopFor(callee, declarations, stop, scan, parameterIndex) {
19989
- const asks = scan.resolution !== void 0 && (stop.reason === "unsettledValue" || Node77.isElementAccessExpression(callee));
20435
+ const asks = scan.resolution !== void 0 && (stop.reason === "unsettledValue" || Node79.isElementAccessExpression(callee));
19990
20436
  return {
19991
20437
  kind: "stopped",
19992
20438
  stop,
@@ -19996,7 +20442,7 @@ function stopFor(callee, declarations, stop, scan, parameterIndex) {
19996
20442
  };
19997
20443
  }
19998
20444
  function storeKeyOf(callee) {
19999
- const declarations = Node77.isIdentifier(callee) ? declarationsBehind(callee.getSymbol()) : [];
20445
+ const declarations = Node79.isIdentifier(callee) ? declarationsBehind(callee.getSymbol()) : [];
20000
20446
  return declarations.length === 1 ? declarations[0] : callee;
20001
20447
  }
20002
20448
  function settleThroughStore(outcomes, scan) {
@@ -20055,7 +20501,7 @@ function declarationToReport(declarations, calleeName, scan) {
20055
20501
  }
20056
20502
  function declaredAtOf(outcome) {
20057
20503
  if (outcome.kind === "followed") {
20058
- return targetOf(outcome.candidate.func);
20504
+ return placeOfCandidate(outcome.candidate);
20059
20505
  }
20060
20506
  if (outcome.kind === "possible") {
20061
20507
  return null;
@@ -20081,7 +20527,7 @@ function mayDispatch(one) {
20081
20527
  if (one.outcome.askStore !== void 0) {
20082
20528
  return true;
20083
20529
  }
20084
- return Node77.isCallExpression(one.node) && Node77.isCallExpression(one.node.getExpression());
20530
+ return Node79.isCallExpression(one.node) && Node79.isCallExpression(one.node.getExpression());
20085
20531
  }
20086
20532
  function dispatchOutcome(answer, stopped) {
20087
20533
  const { callee } = stopped.stop;
@@ -20104,18 +20550,18 @@ function dispatchOutcome(answer, stopped) {
20104
20550
  }
20105
20551
  function entryNameOf(func) {
20106
20552
  const property = func.getParent();
20107
- const key = property === void 0 || !Node77.isPropertyAssignment(property) ? null : propertyNameOf(property);
20553
+ const key = property === void 0 || !Node79.isPropertyAssignment(property) ? null : propertyNameOf(property);
20108
20554
  if (property === void 0 || key === null) {
20109
20555
  return null;
20110
20556
  }
20111
20557
  const holder = climbSyntax(property.getParentOrThrow()).getParent();
20112
- return holder !== void 0 && Node77.isVariableDeclaration(holder) ? `${holder.getName()}.${key}` : key;
20558
+ return holder !== void 0 && Node79.isVariableDeclaration(holder) ? `${holder.getName()}.${key}` : key;
20113
20559
  }
20114
20560
  function placeCallee(placements, calleeText, call, outcome) {
20115
20561
  if (outcome.kind === "possible") {
20116
20562
  placements.placePossible(
20117
20563
  calleeText,
20118
- outcome.candidates.map((candidate) => targetOf(candidate.func))
20564
+ outcome.candidates.map(placeOfCandidate)
20119
20565
  );
20120
20566
  return;
20121
20567
  }
@@ -20129,24 +20575,24 @@ function placeCallee(placements, calleeText, call, outcome) {
20129
20575
  }
20130
20576
  }
20131
20577
  function isCallOnOtherValue(call) {
20132
- if (!Node77.isCallExpression(call)) {
20578
+ if (!Node79.isCallExpression(call)) {
20133
20579
  return false;
20134
20580
  }
20135
20581
  const callee = call.getExpression();
20136
- if (!Node77.isPropertyAccessExpression(callee) && !Node77.isElementAccessExpression(callee)) {
20582
+ if (!Node79.isPropertyAccessExpression(callee) && !Node79.isElementAccessExpression(callee)) {
20137
20583
  return false;
20138
20584
  }
20139
- return !Node77.isThisExpression(callee.getExpression());
20585
+ return !Node79.isThisExpression(callee.getExpression());
20140
20586
  }
20141
20587
  function passedPositionKey(target, position) {
20142
20588
  return `${offsetKeyFor(target.file, target.span)}#${position}`;
20143
20589
  }
20144
20590
  function resolveJsxReference(node, scan) {
20145
- if (!Node77.isJsxOpeningElement(node) && !Node77.isJsxSelfClosingElement(node)) {
20591
+ if (!Node79.isJsxOpeningElement(node) && !Node79.isJsxSelfClosingElement(node)) {
20146
20592
  return null;
20147
20593
  }
20148
20594
  const tag = node.getTagNameNode();
20149
- if (!Node77.isIdentifier(tag) || !/^[A-Z]/.test(tag.getText())) {
20595
+ if (!Node79.isIdentifier(tag) || !/^[A-Z]/.test(tag.getText())) {
20150
20596
  return null;
20151
20597
  }
20152
20598
  const name = tag.getText();
@@ -20168,7 +20614,7 @@ function resolveJsxReference(node, scan) {
20168
20614
  };
20169
20615
  }
20170
20616
  function collectReachable(root, scan) {
20171
- const atModuleScope = Node77.isSourceFile(root);
20617
+ const atModuleScope = Node79.isSourceFile(root);
20172
20618
  const inFunc = atModuleScope ? { ...scan } : { ...scan, scanning: root };
20173
20619
  const calls = [];
20174
20620
  const stops = [];
@@ -20185,7 +20631,7 @@ function collectReachable(root, scan) {
20185
20631
  }
20186
20632
  };
20187
20633
  const recordFollowed = (candidate) => {
20188
- const key = nodeKey(candidate.func);
20634
+ const key = candidateKey(candidate);
20189
20635
  if (seen.has(key)) {
20190
20636
  return;
20191
20637
  }
@@ -20207,18 +20653,18 @@ function collectReachable(root, scan) {
20207
20653
  recordFollowed(outcome.candidate);
20208
20654
  };
20209
20655
  const recordPassedArgs = (call, calleeText, calleeOutcome) => {
20210
- if (!Node77.isCallExpression(call)) {
20656
+ if (!Node79.isCallExpression(call)) {
20211
20657
  return;
20212
20658
  }
20213
20659
  const calleeTarget = declaredAtOf(calleeOutcome);
20214
20660
  call.getArguments().forEach((arg, position) => {
20215
- if (Node77.isArrowFunction(arg) || Node77.isFunctionExpression(arg)) {
20661
+ if (Node79.isArrowFunction(arg) || Node79.isFunctionExpression(arg)) {
20216
20662
  if (calleeTarget !== null) {
20217
20663
  passedPositions.add(passedPositionKey(calleeTarget, position));
20218
20664
  }
20219
20665
  return;
20220
20666
  }
20221
- if (!Node77.isIdentifier(arg)) {
20667
+ if (!Node79.isIdentifier(arg)) {
20222
20668
  return;
20223
20669
  }
20224
20670
  const resolved = functionTargetOf(arg);
@@ -20243,7 +20689,7 @@ function collectReachable(root, scan) {
20243
20689
  found.push({ node, outcome: jsx.outcome });
20244
20690
  return;
20245
20691
  }
20246
- if (!Node77.isCallExpression(node)) {
20692
+ if (!Node79.isCallExpression(node)) {
20247
20693
  return;
20248
20694
  }
20249
20695
  const calleeText = normalizeCalleeText2(node.getExpression().getText());
@@ -20347,7 +20793,7 @@ function recognizerOnlyRoots(packsByFile) {
20347
20793
  }
20348
20794
  for (const declaration of variableStatement.getDeclarations()) {
20349
20795
  const init = declaration.getInitializer();
20350
- if (init !== void 0 && (Node77.isArrowFunction(init) || Node77.isFunctionExpression(init)) && hasBody(init)) {
20796
+ if (init !== void 0 && (Node79.isArrowFunction(init) || Node79.isFunctionExpression(init)) && hasBody(init)) {
20351
20797
  roots.push({ func: init, name: declaration.getName() });
20352
20798
  }
20353
20799
  }
@@ -20377,6 +20823,8 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20377
20823
  const passedPositions = /* @__PURE__ */ new Set();
20378
20824
  const summariesByKey = /* @__PURE__ */ new Map();
20379
20825
  const reachedFrom = /* @__PURE__ */ new Map();
20826
+ const receiverByKey = /* @__PURE__ */ new Map();
20827
+ const callsByKey = /* @__PURE__ */ new Map();
20380
20828
  const finding = {
20381
20829
  project,
20382
20830
  lookup,
@@ -20385,7 +20833,8 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20385
20833
  storeAnswers: /* @__PURE__ */ new Map(),
20386
20834
  funcByKey,
20387
20835
  moduleByKey,
20388
- reachedFrom
20836
+ reachedFrom,
20837
+ receiverByKey
20389
20838
  };
20390
20839
  for (const seed of seeds) {
20391
20840
  const func = lookup.functionAt(seed.location);
@@ -20443,25 +20892,53 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20443
20892
  for (const position of found.passedPositions) {
20444
20893
  passedPositions.add(position);
20445
20894
  }
20446
- const callerFile = fileOfOffsetKey(key);
20447
- for (const call of found.calls) {
20895
+ const callerFile = fileOfOffsetKey(plainKeyOf2(key));
20896
+ const calledHere = found.calls.flatMap(
20897
+ (call) => call.receiverClass === void 0 ? [call] : [
20898
+ { ...call, key: plainKeyOf2(call.key), receiverClass: void 0 },
20899
+ call
20900
+ ]
20901
+ );
20902
+ const called = [];
20903
+ for (const call of calledHere) {
20448
20904
  if (!nameByKey.has(call.key)) {
20449
20905
  nameByKey.set(call.key, call.name);
20450
20906
  }
20451
20907
  if (call.func !== void 0 && !funcByKey.has(call.key)) {
20452
20908
  funcByKey.set(call.key, call.func);
20453
20909
  }
20910
+ if (call.receiverClass !== void 0) {
20911
+ receiverByKey.set(call.key, call.receiverClass);
20912
+ }
20454
20913
  if (callerFile !== null && !reachedFrom.has(call.key)) {
20455
20914
  reachedFrom.set(call.key, callerFile);
20456
20915
  }
20457
20916
  db.add("calls", [key, call.key]);
20917
+ called.push(call.key);
20458
20918
  }
20919
+ callsByKey.set(key, called);
20459
20920
  }
20460
20921
  }
20922
+ const placementsOf = (key) => ({
20923
+ targets: targetsByKey.get(key),
20924
+ argTargets: argTargetsByKey.get(key),
20925
+ possibleTargets: possibleTargetsByKey.get(key)
20926
+ });
20927
+ const onSubclass = keptVariants(
20928
+ [...receiverByKey.keys()].map((key) => ({
20929
+ key,
20930
+ differs: !sameDeclarations(
20931
+ placementsOf(key),
20932
+ placementsOf(plainKeyOf2(key))
20933
+ ),
20934
+ calls: callsByKey.get(key) ?? []
20935
+ }))
20936
+ );
20461
20937
  const reached = [];
20462
20938
  for (const [keyAtom] of db.facts("reachable")) {
20463
20939
  const key = String(keyAtom);
20464
- if (known.has(key) || seedKeys.has(key)) {
20940
+ const receiverClass = receiverByKey.get(key);
20941
+ if (known.has(key) || seedKeys.has(key) || receiverClass !== void 0 && !onSubclass.has(key)) {
20465
20942
  continue;
20466
20943
  }
20467
20944
  const name = nameByKey.get(key);
@@ -20472,7 +20949,7 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20472
20949
  if (projectFileSet !== void 0) {
20473
20950
  lazyAddSourceFile(project, projectFileSet, file);
20474
20951
  }
20475
- const func = funcByKey.get(key) ?? functionAtKey(key, lookup);
20952
+ const func = funcByKey.get(key) ?? functionAtKey(plainKeyOf2(key), lookup);
20476
20953
  if (func === void 0) {
20477
20954
  continue;
20478
20955
  }
@@ -20482,6 +20959,10 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
20482
20959
  facts,
20483
20960
  () => extractReachableSummary(candidate, options, recognizers)
20484
20961
  );
20962
+ if (receiverClass !== void 0) {
20963
+ summary.receiverClass = subclassPlace(receiverClass);
20964
+ summary.identity.exportPath = [receiverClass.name, name];
20965
+ }
20485
20966
  facts?.unitKeyBySummary.set(summary, key);
20486
20967
  rememberSummary(summariesByKey, key, summary);
20487
20968
  reached.push(summary);
@@ -20538,14 +21019,15 @@ function knownUnits(summaries, lookup, facts) {
20538
21019
  }
20539
21020
  function findingsFor(key, ctx) {
20540
21021
  const cameFrom = ctx.reachedFrom.get(key);
20541
- const stored = ctx.facts?.previousScans?.get(key);
21022
+ const receiverClass = ctx.receiverByKey.get(key);
21023
+ const stored = receiverClass === void 0 ? ctx.facts?.previousScans?.get(key) : void 0;
20542
21024
  const recorded = stored !== void 0 && recordValidFrom(stored.data, cameFrom) ? fromScanRecord(stored) : null;
20543
21025
  if (stored !== void 0 && recorded !== null) {
20544
21026
  ctx.facts?.scans?.set(key, { kind: "reused", unit: stored });
20545
21027
  noteFilesRead(key, ctx.facts, stored.deps);
20546
21028
  return recorded;
20547
21029
  }
20548
- const root = ctx.funcByKey.get(key) ?? ctx.moduleByKey.get(key) ?? functionAtKey(key, ctx.lookup);
21030
+ const root = ctx.funcByKey.get(key) ?? ctx.moduleByKey.get(key) ?? functionAtKey(plainKeyOf2(key), ctx.lookup);
20549
21031
  if (root === void 0) {
20550
21032
  return null;
20551
21033
  }
@@ -20556,7 +21038,8 @@ function findingsFor(key, ctx) {
20556
21038
  ...resolution === void 0 ? {} : { resolution, storeAnswers: ctx.storeAnswers },
20557
21039
  ...resolveCallableSources === void 0 ? {} : { resolveCallableSources, sourcesAsked },
20558
21040
  ...sourceDeclarationsBehind2 === void 0 ? {} : { sourceDeclarationsBehind: sourceDeclarationsBehind2 },
20559
- ...reachedFrom === void 0 ? {} : { reachedFrom }
21041
+ ...reachedFrom === void 0 ? {} : { reachedFrom },
21042
+ ...receiverClass === void 0 ? {} : { receiverClass }
20560
21043
  };
20561
21044
  const scanned = scanOrReport(key, ctx.facts, root, scan);
20562
21045
  if (scanned === null) {
@@ -20576,7 +21059,7 @@ function scanOrReport(key, facts, root, scan) {
20576
21059
  return scanWithRecording(key, facts, () => collectReachable(root, scan));
20577
21060
  } catch (error) {
20578
21061
  const message = error instanceof Error ? error.message : String(error);
20579
- const where = Node77.isSourceFile(root) ? root.getFilePath() : `${root.getSourceFile().getFilePath()}:${root.getStartLineNumber()}`;
21062
+ const where = Node79.isSourceFile(root) ? root.getFilePath() : `${root.getSourceFile().getFilePath()}:${root.getStartLineNumber()}`;
20580
21063
  process.stderr.write(
20581
21064
  `[suss] could not scan the function at ${where} for the calls it makes, so nothing it calls is followed from it: ${message}
20582
21065
  `
@@ -20700,7 +21183,7 @@ function recordParameterGaps(parameterCallsByKey, summariesByKey, passedPosition
20700
21183
  }
20701
21184
  for (const [key, parameterCalls] of parameterCallsByKey) {
20702
21185
  const unbound = parameterCalls.filter(
20703
- ({ parameterIndex }) => !passedPositions.has(`${key}#${parameterIndex}`)
21186
+ ({ parameterIndex }) => !passedPositions.has(`${plainKeyOf2(key)}#${parameterIndex}`)
20704
21187
  );
20705
21188
  if (unbound.length === 0) {
20706
21189
  continue;
@@ -20717,7 +21200,7 @@ function recordParameterGaps(parameterCallsByKey, summariesByKey, passedPosition
20717
21200
 
20718
21201
  // src/resolve/redirectDelivery.ts
20719
21202
  import { redirectDeliveryWhenSet } from "@suss/extractor";
20720
- import { force as force6, scalarOf } from "@suss/values";
21203
+ import { force as force7, scalarOf } from "@suss/values";
20721
21204
  function settingOf(object, name, resolution, site) {
20722
21205
  if (object === void 0) {
20723
21206
  return { kind: "absent" };
@@ -20733,7 +21216,7 @@ function settingOf(object, name, resolution, site) {
20733
21216
  if (field.presence !== "one") {
20734
21217
  return { kind: "unknown" };
20735
21218
  }
20736
- const settled3 = scalarOf(force6(field.value));
21219
+ const settled3 = scalarOf(force7(field.value));
20737
21220
  return settled3 === null ? { kind: "unknown" } : { kind: "settled", value: settled3 };
20738
21221
  }
20739
21222
  function redirectDeliveryAtCall(callSite, pattern, pack, resolution) {
@@ -20758,7 +21241,7 @@ function redirectDeliveryAtCall(callSite, pattern, pack, resolution) {
20758
21241
  }
20759
21242
 
20760
21243
  // src/resolve/rethrowEnrichment.ts
20761
- import { Node as Node78 } from "ts-morph";
21244
+ import { Node as Node80 } from "ts-morph";
20762
21245
  import { Database as Database4, evaluate as evaluate4, lit as lit3, rule as rule3, variable as variable2 } from "@suss/datalog";
20763
21246
  var RETHROW_RULES = [
20764
21247
  rule3(
@@ -20895,7 +21378,7 @@ function findBareRethrow(func, location) {
20895
21378
  traversal.stop();
20896
21379
  return;
20897
21380
  }
20898
- if (!Node78.isThrowStatement(node)) {
21381
+ if (!Node80.isThrowStatement(node)) {
20899
21382
  return;
20900
21383
  }
20901
21384
  if (startLineOf(node) !== location.start) {
@@ -20905,7 +21388,7 @@ function findBareRethrow(func, location) {
20905
21388
  return;
20906
21389
  }
20907
21390
  const thrown = node.getExpression();
20908
- if (thrown !== void 0 && Node78.isIdentifier(thrown)) {
21391
+ if (thrown !== void 0 && Node80.isIdentifier(thrown)) {
20909
21392
  found = node;
20910
21393
  }
20911
21394
  });
@@ -20915,13 +21398,13 @@ function enclosingTry(throwStmt) {
20915
21398
  let current2 = throwStmt.getParent();
20916
21399
  let sawCatchClause = false;
20917
21400
  while (current2 !== void 0) {
20918
- if (Node78.isCatchClause(current2)) {
21401
+ if (Node80.isCatchClause(current2)) {
20919
21402
  sawCatchClause = true;
20920
21403
  }
20921
- if (Node78.isTryStatement(current2)) {
21404
+ if (Node80.isTryStatement(current2)) {
20922
21405
  return sawCatchClause ? current2 : null;
20923
21406
  }
20924
- if (Node78.isFunctionDeclaration(current2) || Node78.isFunctionExpression(current2) || Node78.isArrowFunction(current2) || Node78.isMethodDeclaration(current2)) {
21407
+ if (Node80.isFunctionDeclaration(current2) || Node80.isFunctionExpression(current2) || Node80.isArrowFunction(current2) || Node80.isMethodDeclaration(current2)) {
20925
21408
  return null;
20926
21409
  }
20927
21410
  current2 = current2.getParent();
@@ -20929,14 +21412,14 @@ function enclosingTry(throwStmt) {
20929
21412
  return null;
20930
21413
  }
20931
21414
  function collectTryBodyCallees(tryStmt, index, keyFor) {
20932
- if (!Node78.isTryStatement(tryStmt)) {
21415
+ if (!Node80.isTryStatement(tryStmt)) {
20933
21416
  return [];
20934
21417
  }
20935
21418
  const tryBlock = tryStmt.getTryBlock();
20936
21419
  const callees = [];
20937
21420
  const seen = /* @__PURE__ */ new Set();
20938
21421
  tryBlock.forEachDescendant((node) => {
20939
- if (!Node78.isCallExpression(node)) {
21422
+ if (!Node80.isCallExpression(node)) {
20940
21423
  return;
20941
21424
  }
20942
21425
  const calleeSummary = resolveCalleeSummary(node, index);
@@ -20953,7 +21436,7 @@ function collectTryBodyCallees(tryStmt, index, keyFor) {
20953
21436
  return callees;
20954
21437
  }
20955
21438
  function resolveCalleeSummary(call, index) {
20956
- if (!Node78.isCallExpression(call)) {
21439
+ if (!Node80.isCallExpression(call)) {
20957
21440
  return null;
20958
21441
  }
20959
21442
  const callee = call.getExpression();
@@ -20986,7 +21469,7 @@ function resolveCalleeSummary(call, index) {
20986
21469
 
20987
21470
  // src/subUnitContext.ts
20988
21471
  import {
20989
- Node as Node79
21472
+ Node as Node81
20990
21473
  } from "ts-morph";
20991
21474
  function createTsSubUnitContext() {
20992
21475
  return {
@@ -21014,18 +21497,18 @@ function findJsxAttributes(parent) {
21014
21497
  if (skip(node, traversal)) {
21015
21498
  return;
21016
21499
  }
21017
- if (!Node79.isJsxOpeningElement(node) && !Node79.isJsxSelfClosingElement(node)) {
21500
+ if (!Node81.isJsxOpeningElement(node) && !Node81.isJsxSelfClosingElement(node)) {
21018
21501
  return;
21019
21502
  }
21020
21503
  const tag = node.getTagNameNode().getText();
21021
21504
  for (const attr of node.getAttributes()) {
21022
- if (!Node79.isJsxAttribute(attr)) {
21505
+ if (!Node81.isJsxAttribute(attr)) {
21023
21506
  continue;
21024
21507
  }
21025
21508
  const initializer = attr.getInitializer();
21026
21509
  let valueExpression = null;
21027
21510
  if (initializer !== void 0) {
21028
- if (Node79.isJsxExpression(initializer)) {
21511
+ if (Node81.isJsxExpression(initializer)) {
21029
21512
  const inner = initializer.getExpression();
21030
21513
  valueExpression = inner ?? null;
21031
21514
  } else {
@@ -21048,11 +21531,11 @@ function findCallExpressionsByName(parent, name) {
21048
21531
  if (skip(node, traversal)) {
21049
21532
  return;
21050
21533
  }
21051
- if (!Node79.isCallExpression(node)) {
21534
+ if (!Node81.isCallExpression(node)) {
21052
21535
  return;
21053
21536
  }
21054
21537
  const callee = node.getExpression();
21055
- if (Node79.isIdentifier(callee) && callee.getText() === name) {
21538
+ if (Node81.isIdentifier(callee) && callee.getText() === name) {
21056
21539
  out.push(node);
21057
21540
  }
21058
21541
  });
@@ -21063,10 +21546,10 @@ function resolveAttributeValueFunction(attr, parent) {
21063
21546
  if (expr === null) {
21064
21547
  return null;
21065
21548
  }
21066
- if (Node79.isArrowFunction(expr) || Node79.isFunctionExpression(expr)) {
21549
+ if (Node81.isArrowFunction(expr) || Node81.isFunctionExpression(expr)) {
21067
21550
  return { func: expr, localName: null };
21068
21551
  }
21069
- if (Node79.isIdentifier(expr)) {
21552
+ if (Node81.isIdentifier(expr)) {
21070
21553
  const symbol = expr.getSymbol();
21071
21554
  if (symbol === void 0) {
21072
21555
  return null;
@@ -21099,7 +21582,7 @@ function getCallArgumentFunction(call, position) {
21099
21582
  if (arg === void 0) {
21100
21583
  return null;
21101
21584
  }
21102
- if (Node79.isArrowFunction(arg) || Node79.isFunctionExpression(arg)) {
21585
+ if (Node81.isArrowFunction(arg) || Node81.isFunctionExpression(arg)) {
21103
21586
  return arg;
21104
21587
  }
21105
21588
  return null;
@@ -21111,7 +21594,7 @@ function readArrayLiteralText(node) {
21111
21594
  if (node === null) {
21112
21595
  return null;
21113
21596
  }
21114
- if (Node79.isArrayLiteralExpression(node)) {
21597
+ if (Node81.isArrayLiteralExpression(node)) {
21115
21598
  return node.getElements().map((e) => e.getText());
21116
21599
  }
21117
21600
  return [node.getText()];
@@ -21163,7 +21646,12 @@ var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
21163
21646
  ".turbo",
21164
21647
  ".next",
21165
21648
  ".suss",
21166
- "vendor"
21649
+ "vendor",
21650
+ "__pycache__",
21651
+ ".venv",
21652
+ "venv",
21653
+ ".tox",
21654
+ "tmp"
21167
21655
  ]);
21168
21656
  function findSourceFiles(rootDir, depth = 0) {
21169
21657
  if (depth > 12) {
@@ -21522,13 +22010,13 @@ var raise = (msg) => {
21522
22010
  throw new Error(msg);
21523
22011
  };
21524
22012
  function bindingPatternNames(nameNode) {
21525
- if (Node80.isObjectBindingPattern(nameNode)) {
22013
+ if (Node82.isObjectBindingPattern(nameNode)) {
21526
22014
  return nameNode.getElements().map((e) => e.getName());
21527
22015
  }
21528
- if (Node80.isArrayBindingPattern(nameNode)) {
22016
+ if (Node82.isArrayBindingPattern(nameNode)) {
21529
22017
  const names = [];
21530
22018
  for (const element of nameNode.getElements()) {
21531
- if (Node80.isOmittedExpression(element)) {
22019
+ if (Node82.isOmittedExpression(element)) {
21532
22020
  continue;
21533
22021
  }
21534
22022
  names.push(element.getName());
@@ -21558,7 +22046,7 @@ function objectParamInputs(param, position, mapping) {
21558
22046
  }
21559
22047
  function componentPropsParameters(param, mapping) {
21560
22048
  const nameNode = param.getNameNode();
21561
- if (!Node80.isObjectBindingPattern(nameNode)) {
22049
+ if (!Node82.isObjectBindingPattern(nameNode)) {
21562
22050
  const type = param.getType();
21563
22051
  return [
21564
22052
  {
@@ -21585,15 +22073,15 @@ function componentPropsParameters(param, mapping) {
21585
22073
  }
21586
22074
  function namesBoundUnder(element, field) {
21587
22075
  const inner = element.getNameNode();
21588
- if (Node80.isObjectBindingPattern(inner)) {
22076
+ if (Node82.isObjectBindingPattern(inner)) {
21589
22077
  return inner.getElements().flatMap((nested) => {
21590
22078
  const property = nested.getDotDotDotToken() === void 0 ? bindingRole(nested, nested.getName()) : void 0;
21591
22079
  return namesBoundUnder(nested, field ?? property);
21592
22080
  });
21593
22081
  }
21594
- if (Node80.isArrayBindingPattern(inner)) {
22082
+ if (Node82.isArrayBindingPattern(inner)) {
21595
22083
  return inner.getElements().flatMap(
21596
- (nested) => Node80.isBindingElement(nested) ? namesBoundUnder(nested, field) : []
22084
+ (nested) => Node82.isBindingElement(nested) ? namesBoundUnder(nested, field) : []
21597
22085
  );
21598
22086
  }
21599
22087
  return [{ binding: element, field }];
@@ -21606,7 +22094,7 @@ function bindingRole(element, name) {
21606
22094
  if (property === void 0) {
21607
22095
  return name;
21608
22096
  }
21609
- if (Node80.isStringLiteral(property)) {
22097
+ if (Node82.isStringLiteral(property)) {
21610
22098
  return property.getLiteralValue();
21611
22099
  }
21612
22100
  return property.getText();
@@ -21638,7 +22126,7 @@ function extractParameters(func, inputMapping) {
21638
22126
  const param = params[i];
21639
22127
  const nameNode = param.getNameNode();
21640
22128
  const boundNames = bindingPatternNames(nameNode);
21641
- if (Node80.isObjectBindingPattern(nameNode)) {
22129
+ if (Node82.isObjectBindingPattern(nameNode)) {
21642
22130
  for (const element of nameNode.getElements()) {
21643
22131
  result.push({
21644
22132
  name: element.getName(),
@@ -21705,7 +22193,7 @@ function extractDependencyCalls(func, barriers = NO_BARRIERS) {
21705
22193
  traversal.skip();
21706
22194
  return;
21707
22195
  }
21708
- if (!Node80.isVariableDeclaration(node)) {
22196
+ if (!Node82.isVariableDeclaration(node)) {
21709
22197
  return;
21710
22198
  }
21711
22199
  const init = node.getInitializer();
@@ -21714,19 +22202,19 @@ function extractDependencyCalls(func, barriers = NO_BARRIERS) {
21714
22202
  }
21715
22203
  let isAsync = false;
21716
22204
  let callExpr = init;
21717
- if (Node80.isAwaitExpression(init)) {
22205
+ if (Node82.isAwaitExpression(init)) {
21718
22206
  isAsync = true;
21719
22207
  callExpr = init.getExpression();
21720
22208
  }
21721
- if (!Node80.isCallExpression(callExpr)) {
22209
+ if (!Node82.isCallExpression(callExpr)) {
21722
22210
  return;
21723
22211
  }
21724
22212
  const calleeName = callExpr.getExpression().getText();
21725
22213
  const nameNode = node.getNameNode();
21726
- const assignedTo = Node80.isIdentifier(nameNode) ? nameNode.getText() : null;
22214
+ const assignedTo = Node82.isIdentifier(nameNode) ? nameNode.getText() : null;
21727
22215
  const declList = node.getParent();
21728
22216
  const varStmt = declList?.getParent();
21729
- const locationNode = varStmt !== void 0 && Node80.isVariableStatement(varStmt) ? varStmt : node;
22217
+ const locationNode = varStmt !== void 0 && Node82.isVariableStatement(varStmt) ? varStmt : node;
21730
22218
  results.push({
21731
22219
  name: calleeName,
21732
22220
  assignedTo,
@@ -22467,6 +22955,7 @@ function extractFromSourceFile(sourceFile, frameworks, claimedUnits, options, ta
22467
22955
  if (unit.operationInfo.unresolved !== void 0) {
22468
22956
  raw.graphqlUnresolvedDocument = unit.operationInfo.unresolved;
22469
22957
  }
22958
+ stampClientChosenAtCall(raw, unit, pack, resolution);
22470
22959
  for (const v2 of unit.operationInfo.variables) {
22471
22960
  raw.parameters.push({
22472
22961
  name: v2.name,
@@ -22507,17 +22996,18 @@ function extractFromSourceFile(sourceFile, frameworks, claimedUnits, options, ta
22507
22996
  if (binding.semantics.name === "rest" && binding.semantics.method !== null) {
22508
22997
  const defaultBranch = raw.branches.find((b) => b.isDefault);
22509
22998
  if (defaultBranch !== void 0) {
22510
- const calleeText = unit.callSite.callExpression.getExpression().getText();
22999
+ const call = unit.callSite.callExpression;
22511
23000
  defaultBranch.extraEffects = [
22512
23001
  ...defaultBranch.extraEffects ?? [],
22513
23002
  {
22514
23003
  type: "interaction",
22515
23004
  binding,
22516
- callee: calleeText,
23005
+ callee: call.getExpression().getText(),
22517
23006
  interaction: {
22518
23007
  class: "service-call",
22519
23008
  method: binding.semantics.method
22520
- }
23009
+ },
23010
+ location: lineRangeOf(call)
22521
23011
  }
22522
23012
  ];
22523
23013
  }
@@ -22641,6 +23131,23 @@ function stampRequestSpelling(raw, pack) {
22641
23131
  }
22642
23132
  raw.requestSpelling = pack.requestSpelling;
22643
23133
  }
23134
+ function stampClientChosenAtCall(raw, unit, pack, resolution) {
23135
+ const specs = pack.graphqlClients ?? [];
23136
+ if (unit.graphqlCall === void 0 || specs.length === 0) {
23137
+ return;
23138
+ }
23139
+ const chosen = clientChosenAt(unit.graphqlCall, specs, resolution);
23140
+ if (chosen?.kind === "settled") {
23141
+ raw.graphqlClient = { ...chosen.client, chosenAtCall: true };
23142
+ return;
23143
+ }
23144
+ if (chosen?.kind === "unresolved") {
23145
+ raw.graphqlUnresolvedClient = {
23146
+ reference: chosen.reference,
23147
+ reason: chosen.reason
23148
+ };
23149
+ }
23150
+ }
22644
23151
  function unfollowedStopsOf(unit, wrappers) {
22645
23152
  const stops = [
22646
23153
  ...unit.unfollowed === void 0 ? [] : [unit.unfollowed],
@@ -22658,7 +23165,7 @@ function emitLibraryEnvReadMarkers(summaries, project, frameworks) {
22658
23165
  if (importer === void 0) {
22659
23166
  continue;
22660
23167
  }
22661
- summaries.push(libraryEnvReadMarker(pack, declared, importer));
23168
+ summaries.push(libraryEnvReadMarker(pack.name, declared, importer));
22662
23169
  }
22663
23170
  }
22664
23171
  }
@@ -22706,31 +23213,6 @@ function schemaDocumentSummary(label, schemaSdl) {
22706
23213
  )
22707
23214
  };
22708
23215
  }
22709
- function libraryEnvReadMarker(pack, declared, importer) {
22710
- return {
22711
- kind: "library",
22712
- location: { file: importer, range: { start: 0, end: 0 }, exportName: null },
22713
- identity: {
22714
- name: `${declared.module} env reads`,
22715
- exportPath: null,
22716
- boundaryBinding: functionCallBinding2({
22717
- transport: "in-process",
22718
- recognition: pack.name
22719
- })
22720
- },
22721
- inputs: [],
22722
- transitions: [],
22723
- gaps: [],
22724
- confidence: { source: "declared", level: "high" },
22725
- metadata: {
22726
- libraryEnvReads: {
22727
- module: declared.module,
22728
- ...declared.prefixes !== void 0 ? { prefixes: declared.prefixes } : {},
22729
- ...declared.names !== void 0 ? { names: declared.names } : {}
22730
- }
22731
- }
22732
- };
22733
- }
22734
23216
  function internalImportsOf(project, file) {
22735
23217
  const sourceFile = project.getSourceFile(file);
22736
23218
  if (sourceFile === void 0) {
@@ -22843,7 +23325,7 @@ function fieldValue2(record2, name) {
22843
23325
  return void 0;
22844
23326
  }
22845
23327
  const field = record2.fields.get(name);
22846
- return field === void 0 ? void 0 : force7(field.value);
23328
+ return field === void 0 ? void 0 : force8(field.value);
22847
23329
  }
22848
23330
  function sinkUrl(sink, bindings, resolution) {
22849
23331
  const p = sink.extraction.path;
@@ -22903,17 +23385,17 @@ function synthesizeCallerSummaries(wrapper, resolution, options) {
22903
23385
  return out;
22904
23386
  }
22905
23387
  function callerName(func) {
22906
- if (Node80.isFunctionDeclaration(func) || Node80.isMethodDeclaration(func)) {
23388
+ if (Node82.isFunctionDeclaration(func) || Node82.isMethodDeclaration(func)) {
22907
23389
  return func.getName() ?? "anonymous";
22908
23390
  }
22909
23391
  const parent = func.getParent();
22910
- if (parent !== void 0 && Node80.isVariableDeclaration(parent)) {
23392
+ if (parent !== void 0 && Node82.isVariableDeclaration(parent)) {
22911
23393
  const nameNode = parent.getNameNode();
22912
- if (Node80.isIdentifier(nameNode)) {
23394
+ if (Node82.isIdentifier(nameNode)) {
22913
23395
  return nameNode.getText();
22914
23396
  }
22915
23397
  }
22916
- if (parent !== void 0 && Node80.isPropertyAssignment(parent)) {
23398
+ if (parent !== void 0 && Node82.isPropertyAssignment(parent)) {
22917
23399
  return parent.getName();
22918
23400
  }
22919
23401
  return "anonymous";
@@ -23953,7 +24435,7 @@ function buildSubUnitSummary(subUnit, parent, parentFunc, recognizers, everyUnit
23953
24435
 
23954
24436
  // src/facts/why.ts
23955
24437
  import path15 from "path";
23956
- import { Node as Node81, Project as Project3, SyntaxKind as SyntaxKind15 } from "ts-morph";
24438
+ import { Node as Node83, Project as Project3, SyntaxKind as SyntaxKind15 } from "ts-morph";
23957
24439
  import { explainResolutionProof, renderExplanation } from "@suss/resolution";
23958
24440
  var TypeScriptWhySession = class {
23959
24441
  project;
@@ -23970,14 +24452,14 @@ var TypeScriptWhySession = class {
23970
24452
  * `text`, or null. Spelled from the person's side: the name as
23971
24453
  * written, not a node id.
23972
24454
  */
23973
- findExpression(file, line, text2) {
24455
+ findExpression(file, line, text3) {
23974
24456
  const sourceFile = this.sourceFileAt(file);
23975
24457
  if (sourceFile === null) {
23976
24458
  return null;
23977
24459
  }
23978
24460
  let found = null;
23979
24461
  sourceFile.forEachDescendant((node) => {
23980
- if (Node81.isExpression(node) && node.getStartLineNumber() === line && node.getText() === text2 && (found === null || node.getWidth() <= found.getWidth())) {
24462
+ if (Node83.isExpression(node) && node.getStartLineNumber() === line && node.getText() === text3 && (found === null || node.getWidth() <= found.getWidth())) {
23981
24463
  found = node;
23982
24464
  }
23983
24465
  });
@@ -24066,26 +24548,26 @@ var TypeScriptWhySession = class {
24066
24548
  }
24067
24549
  };
24068
24550
  function displayNameOf(node) {
24069
- if (Node81.isIdentifier(node) || Node81.isPropertyAccessExpression(node)) {
24551
+ if (Node83.isIdentifier(node) || Node83.isPropertyAccessExpression(node)) {
24070
24552
  return node.getText();
24071
24553
  }
24072
- if (Node81.isCallExpression(node) || Node81.isNewExpression(node)) {
24554
+ if (Node83.isCallExpression(node) || Node83.isNewExpression(node)) {
24073
24555
  return `${node.getExpression()?.getText() ?? "call"}(...)`;
24074
24556
  }
24075
- if (Node81.isFunctionDeclaration(node) || Node81.isClassDeclaration(node) || Node81.isMethodDeclaration(node) || Node81.isVariableDeclaration(node)) {
24557
+ if (Node83.isFunctionDeclaration(node) || Node83.isClassDeclaration(node) || Node83.isMethodDeclaration(node) || Node83.isVariableDeclaration(node)) {
24076
24558
  const name = node.getName();
24077
24559
  if (name !== void 0) {
24078
24560
  return name;
24079
24561
  }
24080
24562
  }
24081
- if (Node81.isArrowFunction(node) || Node81.isFunctionExpression(node)) {
24563
+ if (Node83.isArrowFunction(node) || Node83.isFunctionExpression(node)) {
24082
24564
  const parent = node.getParent();
24083
- if (parent !== void 0 && Node81.isVariableDeclaration(parent)) {
24565
+ if (parent !== void 0 && Node83.isVariableDeclaration(parent)) {
24084
24566
  return parent.getName();
24085
24567
  }
24086
24568
  }
24087
- const text2 = node.getText().split("\n")[0].trim();
24088
- return text2.length > 40 ? `${text2.slice(0, 40)}...` : text2;
24569
+ const text3 = node.getText().split("\n")[0].trim();
24570
+ return text3.length > 40 ? `${text3.slice(0, 40)}...` : text3;
24089
24571
  }
24090
24572
 
24091
24573
  // src/stubEvidence.ts
@@ -24131,9 +24613,9 @@ function stubEvidence(options) {
24131
24613
  }
24132
24614
 
24133
24615
  // src/walk/defaulted.ts
24134
- import { Node as Node82, SyntaxKind as SyntaxKind16 } from "ts-morph";
24616
+ import { Node as Node84, SyntaxKind as SyntaxKind16 } from "ts-morph";
24135
24617
  function isDefaultedAt(node) {
24136
- if (!Node82.isBindingElement(node)) {
24618
+ if (!Node84.isBindingElement(node)) {
24137
24619
  const read2 = climbSyntax(node);
24138
24620
  const inner = peelSyntax(read2);
24139
24621
  const subject = {
@@ -24150,7 +24632,7 @@ function isDefaultedAt(node) {
24150
24632
  }
24151
24633
  function isSubject(subject, expression) {
24152
24634
  if (subject.kind === "local") {
24153
- return Node82.isIdentifier(expression) && refersTo(expression, subject.local);
24635
+ return Node84.isIdentifier(expression) && refersTo(expression, subject.local);
24154
24636
  }
24155
24637
  return expression.getText() === subject.text;
24156
24638
  }
@@ -24164,7 +24646,7 @@ function hasFallbackOperand(node) {
24164
24646
  let child = climbSyntax(node);
24165
24647
  let parent = child.getParent();
24166
24648
  while (parent !== void 0) {
24167
- if (!Node82.isBinaryExpression(parent)) {
24649
+ if (!Node84.isBinaryExpression(parent)) {
24168
24650
  return false;
24169
24651
  }
24170
24652
  const operator = parent.getOperatorToken().getText();
@@ -24185,8 +24667,8 @@ function isPresenceTest(node, subject) {
24185
24667
  let throughLogical = false;
24186
24668
  let parent = child.getParent();
24187
24669
  while (parent !== void 0) {
24188
- if (!Node82.isBinaryExpression(parent)) {
24189
- if (negatedOperand(parent) === null && !Node82.isTypeOfExpression(parent)) {
24670
+ if (!Node84.isBinaryExpression(parent)) {
24671
+ if (negatedOperand(parent) === null && !Node84.isTypeOfExpression(parent)) {
24190
24672
  return isTestedAt(parent, child, subject, { tested, throughLogical });
24191
24673
  }
24192
24674
  tested = true;
@@ -24212,22 +24694,22 @@ function logicalStep(parent, child) {
24212
24694
  return null;
24213
24695
  }
24214
24696
  const operand2 = peelSyntax(child);
24215
- return Node82.isTypeOfExpression(operand2) || missingSideOf(parent) === operand2 ? "tested" : null;
24697
+ return Node84.isTypeOfExpression(operand2) || missingSideOf(parent) === operand2 ? "tested" : null;
24216
24698
  }
24217
24699
  function isTestedAt(parent, child, subject, seen) {
24218
24700
  if (conditionOf2(parent) === child) {
24219
24701
  return !absentPathThrows(parent, subject);
24220
24702
  }
24221
- if (seen.throughLogical && Node82.isExpressionStatement(parent)) {
24703
+ if (seen.throughLogical && Node84.isExpressionStatement(parent)) {
24222
24704
  return !continuationThrows(parent);
24223
24705
  }
24224
24706
  return seen.tested;
24225
24707
  }
24226
24708
  function conditionOf2(node) {
24227
- if (Node82.isIfStatement(node) || Node82.isWhileStatement(node) || Node82.isDoStatement(node)) {
24709
+ if (Node84.isIfStatement(node) || Node84.isWhileStatement(node) || Node84.isDoStatement(node)) {
24228
24710
  return node.getExpression();
24229
24711
  }
24230
- if (Node82.isConditionalExpression(node) || Node82.isForStatement(node)) {
24712
+ if (Node84.isConditionalExpression(node) || Node84.isForStatement(node)) {
24231
24713
  return node.getCondition();
24232
24714
  }
24233
24715
  return void 0;
@@ -24245,24 +24727,24 @@ function isPresentAt(node, subject) {
24245
24727
  return false;
24246
24728
  }
24247
24729
  function isBranchWherePresent(parent, child, subject) {
24248
- if (Node82.isIfStatement(parent)) {
24730
+ if (Node84.isIfStatement(parent)) {
24249
24731
  return isBranchTaken(parent.getExpression(), child, subject, {
24250
24732
  whenTrue: parent.getThenStatement(),
24251
24733
  whenFalse: parent.getElseStatement()
24252
24734
  });
24253
24735
  }
24254
- if (Node82.isConditionalExpression(parent)) {
24736
+ if (Node84.isConditionalExpression(parent)) {
24255
24737
  return isBranchTaken(parent.getCondition(), child, subject, {
24256
24738
  whenTrue: parent.getWhenTrue(),
24257
24739
  whenFalse: parent.getWhenFalse()
24258
24740
  });
24259
24741
  }
24260
- if (Node82.isWhileStatement(parent)) {
24742
+ if (Node84.isWhileStatement(parent)) {
24261
24743
  return isBranchTaken(parent.getExpression(), child, subject, {
24262
24744
  whenTrue: parent.getStatement()
24263
24745
  });
24264
24746
  }
24265
- if (!Node82.isBinaryExpression(parent) || parent.getRight() !== child) {
24747
+ if (!Node84.isBinaryExpression(parent) || parent.getRight() !== child) {
24266
24748
  return false;
24267
24749
  }
24268
24750
  const operator = parent.getOperatorToken().getText();
@@ -24275,16 +24757,16 @@ function isBranchTaken(test, child, subject, branches) {
24275
24757
  return child === branches.whenFalse && isPresentWhen(test, false, subject);
24276
24758
  }
24277
24759
  function followsExitWhenAbsent(parent, child, subject) {
24278
- if (!(Node82.isBlock(parent) || Node82.isSourceFile(parent))) {
24760
+ if (!(Node84.isBlock(parent) || Node84.isSourceFile(parent))) {
24279
24761
  return false;
24280
24762
  }
24281
24763
  const statements = parent.getStatements();
24282
24764
  return statements.slice(0, statements.indexOf(child)).some(
24283
- (statement) => Node82.isIfStatement(statement) && exitOf(statement.getThenStatement()) === "leave" && isPresentWhen(statement.getExpression(), false, subject)
24765
+ (statement) => Node84.isIfStatement(statement) && exitOf(statement.getThenStatement()) === "leave" && isPresentWhen(statement.getExpression(), false, subject)
24284
24766
  );
24285
24767
  }
24286
24768
  function absentPathThrows(owner, subject) {
24287
- if (!Node82.isIfStatement(owner)) {
24769
+ if (!Node84.isIfStatement(owner)) {
24288
24770
  return continuationThrows(owner);
24289
24771
  }
24290
24772
  const test = owner.getExpression();
@@ -24298,7 +24780,7 @@ function absentPathThrows(owner, subject) {
24298
24780
  return arms.some((arm) => armThrows(arm, owner, subject));
24299
24781
  }
24300
24782
  function armThrows(arm, owner, subject) {
24301
- if (arm !== void 0 && Node82.isIfStatement(arm)) {
24783
+ if (arm !== void 0 && Node84.isIfStatement(arm)) {
24302
24784
  return absentPathThrows(arm, subject);
24303
24785
  }
24304
24786
  const exit = arm === void 0 ? null : exitOf(arm);
@@ -24308,7 +24790,7 @@ function continuationThrows(from) {
24308
24790
  let current2 = from;
24309
24791
  let parent = current2.getParent();
24310
24792
  while (parent !== void 0) {
24311
- if (Node82.isBlock(parent) || Node82.isSourceFile(parent)) {
24793
+ if (Node84.isBlock(parent) || Node84.isSourceFile(parent)) {
24312
24794
  const statements = parent.getStatements();
24313
24795
  const after = statements.slice(
24314
24796
  statements.indexOf(current2) + 1
@@ -24327,7 +24809,7 @@ function continuationThrows(from) {
24327
24809
  return false;
24328
24810
  }
24329
24811
  function endsThePath(node) {
24330
- return isFunctionRoot(node) || Node82.isIterationStatement(node) || Node82.isReturnStatement(node);
24812
+ return isFunctionRoot(node) || Node84.isIterationStatement(node) || Node84.isReturnStatement(node);
24331
24813
  }
24332
24814
  function firstExit(statements) {
24333
24815
  for (const statement of statements) {
@@ -24339,13 +24821,13 @@ function firstExit(statements) {
24339
24821
  return null;
24340
24822
  }
24341
24823
  function exitOf(statement) {
24342
- if (Node82.isThrowStatement(statement)) {
24824
+ if (Node84.isThrowStatement(statement)) {
24343
24825
  return "throw";
24344
24826
  }
24345
- if (Node82.isReturnStatement(statement) || Node82.isContinueStatement(statement) || Node82.isBreakStatement(statement)) {
24827
+ if (Node84.isReturnStatement(statement) || Node84.isContinueStatement(statement) || Node84.isBreakStatement(statement)) {
24346
24828
  return "leave";
24347
24829
  }
24348
- return Node82.isBlock(statement) ? firstExit(statement.getStatements()) : null;
24830
+ return Node84.isBlock(statement) ? firstExit(statement.getStatements()) : null;
24349
24831
  }
24350
24832
  function isPresentWhen(test, outcome, subject) {
24351
24833
  const expression = peelSyntax(test);
@@ -24356,7 +24838,7 @@ function isPresentWhen(test, outcome, subject) {
24356
24838
  if (negated2 !== null) {
24357
24839
  return isPresentWhen(negated2, !outcome, subject);
24358
24840
  }
24359
- if (!Node82.isBinaryExpression(expression)) {
24841
+ if (!Node84.isBinaryExpression(expression)) {
24360
24842
  return false;
24361
24843
  }
24362
24844
  const operator = expression.getOperatorToken().getText();
@@ -24387,24 +24869,24 @@ function missingSideOf(comparison) {
24387
24869
  return comparedValue(left, right, loose) ?? comparedValue(right, left, loose);
24388
24870
  }
24389
24871
  function comparedValue(operand2, other, loose) {
24390
- if (Node82.isTypeOfExpression(operand2)) {
24391
- return Node82.isStringLiteral(other) && other.getLiteralValue() === "undefined" ? peelSyntax(operand2.getExpression()) : null;
24872
+ if (Node84.isTypeOfExpression(operand2)) {
24873
+ return Node84.isStringLiteral(other) && other.getLiteralValue() === "undefined" ? peelSyntax(operand2.getExpression()) : null;
24392
24874
  }
24393
- const missing = Node82.isIdentifier(other) && other.getText() === "undefined" || loose && Node82.isNullLiteral(other);
24875
+ const missing = Node84.isIdentifier(other) && other.getText() === "undefined" || loose && Node84.isNullLiteral(other);
24394
24876
  return missing ? operand2 : null;
24395
24877
  }
24396
24878
  function negatedOperand(node) {
24397
- if (Node82.isPrefixUnaryExpression(node) && node.getOperatorToken() === SyntaxKind16.ExclamationToken) {
24879
+ if (Node84.isPrefixUnaryExpression(node) && node.getOperatorToken() === SyntaxKind16.ExclamationToken) {
24398
24880
  return node.getOperand();
24399
24881
  }
24400
24882
  return null;
24401
24883
  }
24402
24884
  function isKeyOfRead(read2, key, container) {
24403
- return (Node82.isPropertyAccessExpression(read2) || Node82.isElementAccessExpression(read2)) && read2.getExpression().getText() === container.getText() && isKeyReadBy(read2, key);
24885
+ return (Node84.isPropertyAccessExpression(read2) || Node84.isElementAccessExpression(read2)) && read2.getExpression().getText() === container.getText() && isKeyReadBy(read2, key);
24404
24886
  }
24405
24887
  function isKeyReadBy(read2, key) {
24406
- if (Node82.isPropertyAccessExpression(read2)) {
24407
- return Node82.isStringLiteral(key) && key.getLiteralValue() === read2.getName();
24888
+ if (Node84.isPropertyAccessExpression(read2)) {
24889
+ return Node84.isStringLiteral(key) && key.getLiteralValue() === read2.getName();
24408
24890
  }
24409
24891
  return read2.getArgumentExpression()?.getText() === key.getText();
24410
24892
  }
@@ -24418,19 +24900,19 @@ function localNamedBy(node) {
24418
24900
  }
24419
24901
  const name = declaration.getNameNode();
24420
24902
  const scope = findEnclosingFunction(declaration);
24421
- if (!Node82.isIdentifier(name) || scope === null) {
24903
+ if (!Node84.isIdentifier(name) || scope === null) {
24422
24904
  return null;
24423
24905
  }
24424
24906
  const symbol = symbolBehind(name);
24425
24907
  return symbol === void 0 ? null : { name, symbol, scope };
24426
24908
  }
24427
24909
  function declarationInitializedBy(node) {
24428
- if (Node82.isBindingElement(node)) {
24910
+ if (Node84.isBindingElement(node)) {
24429
24911
  return node;
24430
24912
  }
24431
24913
  const read2 = climbSyntax(node);
24432
24914
  const parent = read2.getParent();
24433
- if (parent !== void 0 && Node82.isVariableDeclaration(parent) && parent.getInitializer() === read2) {
24915
+ if (parent !== void 0 && Node84.isVariableDeclaration(parent) && parent.getInitializer() === read2) {
24434
24916
  return parent;
24435
24917
  }
24436
24918
  return null;
@@ -24445,6 +24927,7 @@ export {
24445
24927
  GENERIC_TERMINALS,
24446
24928
  NO_BARRIERS,
24447
24929
  ResolutionStore,
24930
+ SKIP_DIRECTORIES,
24448
24931
  SOURCE_SUFFIXES,
24449
24932
  TSCONFIG_NAMES,
24450
24933
  TypeScriptWhySession,
@@ -24464,6 +24947,7 @@ export {
24464
24947
  createTypeScriptAdapter,
24465
24948
  declarationsBehind,
24466
24949
  discoverUnits,
24950
+ dottedPathOf,
24467
24951
  exportedDeclarationsOf,
24468
24952
  extractCodeStructure,
24469
24953
  extractRawBranches,