@suss/adapter-typescript 0.5.11 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -49,10 +49,12 @@ __export(index_exports, {
49
49
  findTerminals: () => findTerminals,
50
50
  formatPackHealth: () => formatPackHealth,
51
51
  isImportedFrom: () => isImportedFrom,
52
+ methodDeclaredIn: () => methodDeclaredIn,
52
53
  parseConditionExpression: () => parseConditionExpression,
53
54
  readConfiguredCall: () => readConfiguredCall,
54
55
  readContract: () => readContract,
55
56
  readContractForClientCall: () => readContractForClientCall,
57
+ readName: () => readName,
56
58
  resolveAliasedSymbol: () => resolveAliasedSymbol,
57
59
  resolveSubject: () => resolveSubject,
58
60
  rootIdentifier: () => rootIdentifier
@@ -1488,9 +1490,9 @@ var ModuleGraph = class {
1488
1490
  if (known !== void 0) {
1489
1491
  return known;
1490
1492
  }
1491
- const read = readModuleSpecifiers(sourceFile);
1492
- this.specifiers.set(filePath, read);
1493
- return read;
1493
+ const read2 = readModuleSpecifiers(sourceFile);
1494
+ this.specifiers.set(filePath, read2);
1495
+ return read2;
1494
1496
  }
1495
1497
  /**
1496
1498
  * Collect what the rules need in order to settle this file and this
@@ -2253,6 +2255,15 @@ function isImportedFrom(identifier, expectedModule) {
2253
2255
  (decl) => decl.getSourceFile().getFilePath().includes(`/node_modules/${expectedModule}/`)
2254
2256
  );
2255
2257
  }
2258
+ function methodDeclaredIn(callee, expectedModule) {
2259
+ if (!import_ts_morph12.Node.isPropertyAccessExpression(callee)) {
2260
+ return false;
2261
+ }
2262
+ const declarations = callee.getNameNode().getSymbol()?.getDeclarations();
2263
+ return declarations !== void 0 && declarations.some(
2264
+ (declaration) => declaration.getSourceFile().getFilePath().includes(`/node_modules/${expectedModule}/`)
2265
+ );
2266
+ }
2256
2267
  function importSpecifierMatches(decl, expectedModule) {
2257
2268
  if (import_ts_morph12.Node.isImportSpecifier(decl)) {
2258
2269
  return decl.getImportDeclaration().getModuleSpecifierValue() === expectedModule;
@@ -2431,7 +2442,7 @@ function dispatchAccessRecognizers(root, recognizers, isStop, resolveWrittenValu
2431
2442
  traversal.skip();
2432
2443
  return;
2433
2444
  }
2434
- if (!import_ts_morph12.Node.isPropertyAccessExpression(node) && !import_ts_morph12.Node.isCallExpression(node)) {
2445
+ if (!import_ts_morph12.Node.isPropertyAccessExpression(node) && !import_ts_morph12.Node.isCallExpression(node) && !import_ts_morph12.Node.isTaggedTemplateExpression(node)) {
2435
2446
  return;
2436
2447
  }
2437
2448
  const ctx = {
@@ -2856,12 +2867,12 @@ function createDefinitionTable() {
2856
2867
  }
2857
2868
  };
2858
2869
  }
2859
- function withDefinitions(read) {
2870
+ function withDefinitions(read2) {
2860
2871
  const table = createDefinitionTable();
2861
2872
  const outer = current;
2862
2873
  current = table;
2863
2874
  try {
2864
- return { value: read(), definitions: table.collected() };
2875
+ return { value: read2(), definitions: table.collected() };
2865
2876
  } finally {
2866
2877
  current = outer;
2867
2878
  }
@@ -4005,10 +4016,59 @@ function writesToBindingUncached(declaration) {
4005
4016
  }
4006
4017
  return { values, inOrder: writesRunInOrder(declaration, writes) };
4007
4018
  }
4019
+ function writesToField(declaration) {
4020
+ const remembered = byDeclaration.get(declaration);
4021
+ if (remembered !== void 0) {
4022
+ return remembered;
4023
+ }
4024
+ const answer = writesToFieldUncached(declaration);
4025
+ byDeclaration.set(declaration, answer);
4026
+ return answer;
4027
+ }
4028
+ function writesToFieldUncached(declaration) {
4029
+ const initializer = declaration.getInitializer();
4030
+ const assignments = assignmentsTo(declaration);
4031
+ if (assignments.length === 0) {
4032
+ return {
4033
+ values: initializer === void 0 ? [] : [initializer],
4034
+ inOrder: true
4035
+ };
4036
+ }
4037
+ const values = [];
4038
+ for (const write of assignments) {
4039
+ if (write.value === null) {
4040
+ return { values: [], inOrder: false };
4041
+ }
4042
+ values.push(write.value);
4043
+ }
4044
+ const withInitializer = initializer === void 0 ? values : [initializer, ...values];
4045
+ return {
4046
+ values: withInitializer,
4047
+ inOrder: assignments.every(
4048
+ (write) => writeRunsInConstructor(declaration, write)
4049
+ )
4050
+ };
4051
+ }
4052
+ function writeRunsInConstructor(declaration, write) {
4053
+ const statement = statementOf(write.node);
4054
+ if (statement === null) {
4055
+ return false;
4056
+ }
4057
+ const body = statement.getParent();
4058
+ if (body === void 0 || !import_ts_morph18.Node.isBlock(body)) {
4059
+ return false;
4060
+ }
4061
+ const owner = body.getParent();
4062
+ return owner !== void 0 && import_ts_morph18.Node.isConstructorDeclaration(owner) && owner.getParent() === declaration.getParent();
4063
+ }
4008
4064
  function isWrittenAgain(declaration) {
4009
4065
  return assignmentsTo(declaration).length > 0;
4010
4066
  }
4011
4067
  function assignmentsTo(declaration) {
4068
+ if (import_ts_morph18.Node.isPropertyDeclaration(declaration)) {
4069
+ const sourceFile2 = declaration.getSourceFile();
4070
+ return sourceFile2.isDeclarationFile() ? [] : assignmentsInFile(sourceFile2).get(declaration) ?? [];
4071
+ }
4012
4072
  if (declaration.getVariableStatement()?.getDeclarationKind() === import_ts_morph18.VariableDeclarationKind.Const) {
4013
4073
  return [];
4014
4074
  }
@@ -4048,7 +4108,7 @@ function writeAt(node) {
4048
4108
  if (import_ts_morph18.Node.isBinaryExpression(node)) {
4049
4109
  const operator = node.getOperatorToken().getKind();
4050
4110
  const target = node.getLeft();
4051
- if (!isAssignmentOperator(operator) || !import_ts_morph18.Node.isIdentifier(target)) {
4111
+ if (!isAssignmentOperator(operator) || !(import_ts_morph18.Node.isIdentifier(target) || import_ts_morph18.Node.isPropertyAccessExpression(target))) {
4052
4112
  return null;
4053
4113
  }
4054
4114
  const value = operator === import_ts_morph18.SyntaxKind.EqualsToken ? node.getRight() : null;
@@ -4065,13 +4125,14 @@ function writeAt(node) {
4065
4125
  return null;
4066
4126
  }
4067
4127
  function declarationsOf(name) {
4068
- const symbol = name.getSymbol();
4128
+ const nameNode = import_ts_morph18.Node.isPropertyAccessExpression(name) ? name.getNameNode() : name;
4129
+ const symbol = nameNode.getSymbol();
4069
4130
  if (symbol === void 0) {
4070
4131
  return [];
4071
4132
  }
4072
4133
  const declarations = [];
4073
4134
  for (const declaration of symbol.getDeclarations()) {
4074
- if (import_ts_morph18.Node.isVariableDeclaration(declaration)) {
4135
+ if (import_ts_morph18.Node.isVariableDeclaration(declaration) || import_ts_morph18.Node.isPropertyDeclaration(declaration)) {
4075
4136
  declarations.push(declaration);
4076
4137
  }
4077
4138
  }
@@ -4101,9 +4162,9 @@ function statementOf(node) {
4101
4162
  function isReadBefore(declaration, position) {
4102
4163
  const name = declaration.getName();
4103
4164
  const sourceFile = declaration.getSourceFile();
4104
- let read = false;
4165
+ let read2 = false;
4105
4166
  sourceFile.forEachDescendant((node, traversal) => {
4106
- if (read) {
4167
+ if (read2) {
4107
4168
  traversal.stop();
4108
4169
  return;
4109
4170
  }
@@ -4122,10 +4183,10 @@ function isReadBefore(declaration, position) {
4122
4183
  return;
4123
4184
  }
4124
4185
  if (declarationsOf(node).includes(declaration)) {
4125
- read = true;
4186
+ read2 = true;
4126
4187
  }
4127
4188
  });
4128
- return read;
4189
+ return read2;
4129
4190
  }
4130
4191
  function startsItsOwnBody(node) {
4131
4192
  return import_ts_morph18.Node.isFunctionDeclaration(node) || import_ts_morph18.Node.isFunctionExpression(node) || import_ts_morph18.Node.isArrowFunction(node) || import_ts_morph18.Node.isMethodDeclaration(node) || import_ts_morph18.Node.isClassDeclaration(node);
@@ -6093,6 +6154,14 @@ function emitBindingValues(db, table, declaration) {
6093
6154
  }
6094
6155
  fact(db, "endsHolding", declarationId, emitValue(db, table, last));
6095
6156
  }
6157
+ function emitFieldValues(db, table, declaration) {
6158
+ const { values, inOrder } = writesToField(declaration);
6159
+ const last = values[values.length - 1];
6160
+ if (!inOrder || last === void 0) {
6161
+ return;
6162
+ }
6163
+ fact(db, "binds", nodeId(declaration), emitValue(db, table, last));
6164
+ }
6096
6165
  function emitBindingElementFacts(db, table, element) {
6097
6166
  const id = nodeId(element);
6098
6167
  table.byId.set(id, element);
@@ -6168,6 +6237,11 @@ function emitReferenceFacts(db, table, reference) {
6168
6237
  emitBindingValues(db, table, declaration);
6169
6238
  continue;
6170
6239
  }
6240
+ if (import_ts_morph28.Node.isPropertyDeclaration(declaration)) {
6241
+ fact(db, "binds", referenceId, declarationId);
6242
+ emitFieldValues(db, table, declaration);
6243
+ continue;
6244
+ }
6171
6245
  if (import_ts_morph28.Node.isParameterDeclaration(declaration)) {
6172
6246
  fact(db, "binds", referenceId, declarationId);
6173
6247
  continue;
@@ -6402,9 +6476,9 @@ function propertyValueOf(property) {
6402
6476
  }
6403
6477
  function functionValueOf(value, resolution) {
6404
6478
  const written = factKeyOf(value);
6405
- const here = toFunctionRoot(written);
6406
- if (here !== null) {
6407
- return here;
6479
+ const here2 = toFunctionRoot(written);
6480
+ if (here2 !== null) {
6481
+ return here2;
6408
6482
  }
6409
6483
  if (resolution === void 0 || !couldNameAValue(written)) {
6410
6484
  return null;
@@ -8160,7 +8234,7 @@ function unitsOfArrayElements(array, match, kind, resolution, prefix, ancestry)
8160
8234
  }
8161
8235
  function unitsForRouteObject(object, match, kind, resolution, prefix, ancestry) {
8162
8236
  const own = ownPathOfObject(object, match);
8163
- const here = unitForRouteObject(object, match, kind, resolution, prefix, own);
8237
+ const here2 = unitForRouteObject(object, match, kind, resolution, prefix, own);
8164
8238
  const nested = unitsUnderRouteObject(
8165
8239
  object,
8166
8240
  match,
@@ -8169,7 +8243,7 @@ function unitsForRouteObject(object, match, kind, resolution, prefix, ancestry)
8169
8243
  prefixUnder(prefix, own),
8170
8244
  ancestry
8171
8245
  );
8172
- return here === null ? nested : [here, ...nested];
8246
+ return here2 === null ? nested : [here2, ...nested];
8173
8247
  }
8174
8248
  function unitForRouteObject(object, match, kind, resolution, prefix, own) {
8175
8249
  const target = elementTargetOfValue(
@@ -8657,6 +8731,9 @@ function trackFactoryBindings(sourceFile, recognize) {
8657
8731
  }
8658
8732
 
8659
8733
  // src/discovery/packageImport.ts
8734
+ function isLanguageLib(file) {
8735
+ return /[\\/]typescript[\\/]lib[\\/]lib\..*\.d\.ts$/.test(file.getFilePath());
8736
+ }
8660
8737
  function splitPackageSpec(spec) {
8661
8738
  if (spec.startsWith("@")) {
8662
8739
  const parts2 = spec.split("/");
@@ -8774,6 +8851,9 @@ function discoverPackageImports(sourceFile, match, kind) {
8774
8851
  if (subjectProvenance === null) {
8775
8852
  return null;
8776
8853
  }
8854
+ if (!methodComesFromSource(callee)) {
8855
+ return subjectProvenance;
8856
+ }
8777
8857
  return {
8778
8858
  packageName: subjectProvenance.packageName,
8779
8859
  exportPath: [...subjectProvenance.exportPath, callee.getName()]
@@ -8781,6 +8861,15 @@ function discoverPackageImports(sourceFile, match, kind) {
8781
8861
  }
8782
8862
  return null;
8783
8863
  }
8864
+ function methodComesFromSource(callee) {
8865
+ const declarations = callee.getNameNode().getSymbol()?.getDeclarations();
8866
+ if (declarations === void 0 || declarations.length === 0) {
8867
+ return true;
8868
+ }
8869
+ return declarations.some(
8870
+ (declaration) => !isLanguageLib(declaration.getSourceFile())
8871
+ );
8872
+ }
8784
8873
  const results = [];
8785
8874
  const seen = /* @__PURE__ */ new Set();
8786
8875
  sourceFile.forEachDescendant((node) => {
@@ -10381,8 +10470,8 @@ function bindingLabel(binding) {
10381
10470
  if (semantics.name === "rest") {
10382
10471
  return `${semantics.method} ${semantics.path}`;
10383
10472
  }
10384
- if (semantics.name === "storage-relational") {
10385
- return `${semantics.storageSystem}:${semantics.table}`;
10473
+ if (semantics.name === "storage") {
10474
+ return `${semantics.storageSystem}:${semantics.container}`;
10386
10475
  }
10387
10476
  if (semantics.name === "message-bus") {
10388
10477
  return `${semantics.messageBus}:${semantics.channel}`;
@@ -10616,7 +10705,9 @@ function extractReachableSummary(candidate, options, recognizers) {
10616
10705
  unit,
10617
10706
  reachablePack,
10618
10707
  recognizers.invocation,
10619
- recognizers.access
10708
+ recognizers.access,
10709
+ void 0,
10710
+ recognizers.resolveWrittenValue
10620
10711
  );
10621
10712
  raw.boundaryBinding = (0, import_behavioral_ir4.functionCallBinding)({
10622
10713
  transport: "in-process",
@@ -11921,7 +12012,7 @@ function announcedBoundaryStructure(unit) {
11921
12012
  };
11922
12013
  }
11923
12014
  function extractCodeStructure(unit, pack, invocationRecognizers = [], accessRecognizers = [], barriers = NO_BARRIERS, resolveWrittenValue) {
11924
- const read = withDefinitions(
12015
+ const read2 = withDefinitions(
11925
12016
  () => readCodeStructure(
11926
12017
  unit,
11927
12018
  pack,
@@ -11931,7 +12022,7 @@ function extractCodeStructure(unit, pack, invocationRecognizers = [], accessReco
11931
12022
  resolveWrittenValue
11932
12023
  )
11933
12024
  );
11934
- return read.definitions === null ? read.value : { ...read.value, definitions: read.definitions };
12025
+ return read2.definitions === null ? read2.value : { ...read2.value, definitions: read2.definitions };
11935
12026
  }
11936
12027
  function responseAccessorNames(pack, kind) {
11937
12028
  return pack.responseSemantics?.filter((m) => m.semantics.type === kind).map((m) => m.name);
@@ -13056,7 +13147,8 @@ function createTypeScriptAdapter(config) {
13056
13147
  config.frameworks,
13057
13148
  tallies
13058
13149
  ),
13059
- access: collectAccessRecognizers(config.frameworks, tallies)
13150
+ access: collectAccessRecognizers(config.frameworks, tallies),
13151
+ resolveWrittenValue: (value) => resolution.resolveWrittenValue(value)
13060
13152
  }
13061
13153
  )
13062
13154
  ) : withSubUnits;
@@ -13435,7 +13527,7 @@ function threwWhileReading(packs) {
13435
13527
  return packs.filter((funnel) => funnel.failures.length > 0).map((funnel) => {
13436
13528
  const first = funnel.failures[0];
13437
13529
  const rest = funnel.failures.length - 1;
13438
- const alsoIn = rest > 0 ? ` and on ${rest} other ${rest === 1 ? "file" : "files"}` : "";
13530
+ const alsoIn = rest > 0 ? ` and on ${rest} ${rest === 1 ? "other file" : "other files"}` : "";
13439
13531
  return {
13440
13532
  label: funnel.pack,
13441
13533
  detail: `threw from ${first.hook} on ${first.file}${alsoIn}, so its counts below are a floor: ${first.message}`
@@ -13487,6 +13579,241 @@ function formatPackHealth(checks, audiences) {
13487
13579
  return `${lines.join("\n")}
13488
13580
  `;
13489
13581
  }
13582
+
13583
+ // src/resolve/readName.ts
13584
+ var import_ts_morph58 = require("ts-morph");
13585
+ var MAX_HELPER_HOPS = 2;
13586
+ var UNNAMED_HOLE = "param";
13587
+ function readName(expr, options = {}) {
13588
+ const name = read(expr, {
13589
+ resolve: options.resolve ?? (() => null),
13590
+ bindings: /* @__PURE__ */ new Map(),
13591
+ hops: 0,
13592
+ seen: /* @__PURE__ */ new Set(),
13593
+ insideHole: false
13594
+ });
13595
+ if (name !== null || options.unsettled !== "reference") {
13596
+ return name;
13597
+ }
13598
+ const asked = holeName(expr, {
13599
+ resolve: () => null,
13600
+ bindings: /* @__PURE__ */ new Map(),
13601
+ hops: 0,
13602
+ seen: /* @__PURE__ */ new Set(),
13603
+ insideHole: true
13604
+ });
13605
+ return asked === UNNAMED_HOLE ? null : `{${asked}}`;
13606
+ }
13607
+ function read(expr, ctx) {
13608
+ if (ctx.seen.has(expr)) {
13609
+ return null;
13610
+ }
13611
+ const seen = new Set(ctx.seen).add(expr);
13612
+ const inner = { ...ctx, seen };
13613
+ if (import_ts_morph58.Node.isStringLiteral(expr) || import_ts_morph58.Node.isNoSubstitutionTemplateLiteral(expr)) {
13614
+ return expr.getLiteralValue();
13615
+ }
13616
+ if (import_ts_morph58.Node.isNumericLiteral(expr)) {
13617
+ return expr.getLiteralText();
13618
+ }
13619
+ if (import_ts_morph58.Node.isTemplateExpression(expr)) {
13620
+ return fromTemplate(expr, inner);
13621
+ }
13622
+ if (import_ts_morph58.Node.isParenthesizedExpression(expr)) {
13623
+ return read(expr.getExpression(), inner);
13624
+ }
13625
+ if (import_ts_morph58.Node.isBinaryExpression(expr)) {
13626
+ return fromBinary(expr, inner);
13627
+ }
13628
+ if (import_ts_morph58.Node.isCallExpression(expr)) {
13629
+ return fromCall(expr, inner);
13630
+ }
13631
+ if (import_ts_morph58.Node.isIdentifier(expr)) {
13632
+ const only = boundOnce(expr, ctx);
13633
+ if (only !== null) {
13634
+ return read(only.node, { ...inner, bindings: only.scope });
13635
+ }
13636
+ }
13637
+ const written = ctx.resolve(expr);
13638
+ return written === null || written === expr ? null : read(written, inner);
13639
+ }
13640
+ function fromTemplate(expr, ctx) {
13641
+ if (!import_ts_morph58.Node.isTemplateExpression(expr)) {
13642
+ return null;
13643
+ }
13644
+ let name = expr.getHead().getLiteralText();
13645
+ for (const span of expr.getTemplateSpans()) {
13646
+ name += readOrHole(span.getExpression(), { ...ctx, insideHole: true });
13647
+ name += span.getLiteral().getLiteralText();
13648
+ }
13649
+ return name;
13650
+ }
13651
+ function fromBinary(expr, ctx) {
13652
+ if (!import_ts_morph58.Node.isBinaryExpression(expr)) {
13653
+ return null;
13654
+ }
13655
+ const operator = expr.getOperatorToken().getKind();
13656
+ if (operator === import_ts_morph58.SyntaxKind.BarBarToken || operator === import_ts_morph58.SyntaxKind.QuestionQuestionToken) {
13657
+ return ctx.insideHole ? null : read(expr.getRight(), ctx);
13658
+ }
13659
+ if (operator !== import_ts_morph58.SyntaxKind.PlusToken) {
13660
+ return null;
13661
+ }
13662
+ const parts = { ...ctx, insideHole: true };
13663
+ return readOrHole(expr.getLeft(), parts) + readOrHole(expr.getRight(), parts);
13664
+ }
13665
+ function fromCall(expr, ctx) {
13666
+ if (!import_ts_morph58.Node.isCallExpression(expr)) {
13667
+ return null;
13668
+ }
13669
+ const callee = expr.getExpression();
13670
+ if (import_ts_morph58.Node.isPropertyAccessExpression(callee) && callee.getName() === "join") {
13671
+ return fromJoin(callee.getExpression(), expr.getArguments(), ctx);
13672
+ }
13673
+ if (import_ts_morph58.Node.isIdentifier(callee) && callee.getText() === "String") {
13674
+ const only = expr.getArguments()[0];
13675
+ return only === void 0 ? null : readOrHole(only, ctx);
13676
+ }
13677
+ if (ctx.hops >= MAX_HELPER_HOPS) {
13678
+ return null;
13679
+ }
13680
+ const helper = singleReturnBody(callee, ctx);
13681
+ if (helper === null) {
13682
+ return null;
13683
+ }
13684
+ return read(helper.returned, {
13685
+ ...ctx,
13686
+ hops: ctx.hops + 1,
13687
+ bindings: bind(helper.parameters, expr.getArguments(), ctx.bindings)
13688
+ });
13689
+ }
13690
+ function fromJoin(subject, args, ctx) {
13691
+ const separator = args[0];
13692
+ if (separator === void 0 || !import_ts_morph58.Node.isStringLiteral(separator)) {
13693
+ return null;
13694
+ }
13695
+ const parts = elementsOf(subject, ctx);
13696
+ if (parts === null) {
13697
+ return null;
13698
+ }
13699
+ return parts.map(
13700
+ (part) => readOrHole(part.node, {
13701
+ ...ctx,
13702
+ bindings: part.scope,
13703
+ insideHole: true
13704
+ })
13705
+ ).join(separator.getLiteralValue());
13706
+ }
13707
+ function elementsOf(subject, ctx) {
13708
+ if (import_ts_morph58.Node.isArrayLiteralExpression(subject)) {
13709
+ return subject.getElements().map((element) => here(element, ctx));
13710
+ }
13711
+ if (import_ts_morph58.Node.isCallExpression(subject)) {
13712
+ const callee = subject.getExpression();
13713
+ if (import_ts_morph58.Node.isPropertyAccessExpression(callee) && callee.getName() === "map") {
13714
+ return elementsOf(callee.getExpression(), ctx);
13715
+ }
13716
+ return null;
13717
+ }
13718
+ if (!import_ts_morph58.Node.isIdentifier(subject)) {
13719
+ return null;
13720
+ }
13721
+ const bound = ctx.bindings.get(subject.getText());
13722
+ if (bound === void 0) {
13723
+ return null;
13724
+ }
13725
+ const only = bound.length === 1 ? bound[0] : void 0;
13726
+ return only !== void 0 && import_ts_morph58.Node.isArrayLiteralExpression(only.node) ? only.node.getElements().map((element) => here(element, ctx)) : bound;
13727
+ }
13728
+ function singleReturnBody(callee, ctx) {
13729
+ const declaration = declarationOf2(callee, ctx);
13730
+ if (declaration === null) {
13731
+ return null;
13732
+ }
13733
+ const parameters = declaration.getParameters().map((parameter) => ({
13734
+ name: parameter.getName(),
13735
+ rest: parameter.isRestParameter()
13736
+ }));
13737
+ const body = declaration.getBody();
13738
+ if (body === void 0) {
13739
+ return null;
13740
+ }
13741
+ if (import_ts_morph58.Node.isExpression(body)) {
13742
+ return { parameters, returned: body };
13743
+ }
13744
+ if (!import_ts_morph58.Node.isBlock(body)) {
13745
+ return null;
13746
+ }
13747
+ const statements = body.getStatements();
13748
+ const only = statements[0];
13749
+ if (statements.length !== 1 || only === void 0 || !import_ts_morph58.Node.isReturnStatement(only)) {
13750
+ return null;
13751
+ }
13752
+ const returned = only.getExpression();
13753
+ return returned === void 0 ? null : { parameters, returned };
13754
+ }
13755
+ function declarationOf2(callee, ctx) {
13756
+ const symbol = import_ts_morph58.Node.isPropertyAccessExpression(callee) ? callee.getNameNode().getSymbol() : callee.getSymbol();
13757
+ const declarations = symbol?.getAliasedSymbol()?.getDeclarations() ?? symbol?.getDeclarations() ?? [];
13758
+ for (const declaration of declarations) {
13759
+ const callable = asCallable(declaration);
13760
+ if (callable !== null) {
13761
+ return callable;
13762
+ }
13763
+ }
13764
+ const written = ctx.resolve(callee);
13765
+ return written === null || written === callee ? null : declarationOf2(written, { ...ctx, resolve: () => null });
13766
+ }
13767
+ function asCallable(declaration) {
13768
+ if (import_ts_morph58.Node.isFunctionDeclaration(declaration) || import_ts_morph58.Node.isMethodDeclaration(declaration) || import_ts_morph58.Node.isArrowFunction(declaration) || import_ts_morph58.Node.isFunctionExpression(declaration)) {
13769
+ return declaration;
13770
+ }
13771
+ if (!import_ts_morph58.Node.isVariableDeclaration(declaration)) {
13772
+ return null;
13773
+ }
13774
+ const initializer = declaration.getInitializer();
13775
+ if (initializer !== void 0 && (import_ts_morph58.Node.isArrowFunction(initializer) || import_ts_morph58.Node.isFunctionExpression(initializer))) {
13776
+ return initializer;
13777
+ }
13778
+ return null;
13779
+ }
13780
+ function bind(parameters, args, scope) {
13781
+ const bindings = /* @__PURE__ */ new Map();
13782
+ parameters.forEach((parameter, index) => {
13783
+ const passed = parameter.rest ? args.slice(index) : args.slice(index, index + 1);
13784
+ if (passed.length > 0) {
13785
+ bindings.set(
13786
+ parameter.name,
13787
+ passed.map((node) => ({ node, scope }))
13788
+ );
13789
+ }
13790
+ });
13791
+ return bindings;
13792
+ }
13793
+ function here(node, ctx) {
13794
+ return { node, scope: ctx.bindings };
13795
+ }
13796
+ function boundOnce(expr, ctx) {
13797
+ const bound = import_ts_morph58.Node.isIdentifier(expr) ? ctx.bindings.get(expr.getText()) : void 0;
13798
+ return bound !== void 0 && bound.length === 1 ? bound[0] : null;
13799
+ }
13800
+ function readOrHole(expr, ctx) {
13801
+ const name = read(expr, ctx);
13802
+ return name ?? `{${holeName(expr, ctx)}}`;
13803
+ }
13804
+ function holeName(expr, ctx) {
13805
+ const bound = boundOnce(expr, ctx);
13806
+ if (bound !== null) {
13807
+ return holeName(bound.node, { ...ctx, bindings: bound.scope });
13808
+ }
13809
+ if (import_ts_morph58.Node.isIdentifier(expr)) {
13810
+ return expr.getText();
13811
+ }
13812
+ if (import_ts_morph58.Node.isPropertyAccessExpression(expr)) {
13813
+ return expr.getName();
13814
+ }
13815
+ return UNNAMED_HOLE;
13816
+ }
13490
13817
  // Annotate the CommonJS export names for ESM import in node:
13491
13818
  0 && (module.exports = {
13492
13819
  ADAPTER_VERSION,
@@ -13508,10 +13835,12 @@ function formatPackHealth(checks, audiences) {
13508
13835
  findTerminals,
13509
13836
  formatPackHealth,
13510
13837
  isImportedFrom,
13838
+ methodDeclaredIn,
13511
13839
  parseConditionExpression,
13512
13840
  readConfiguredCall,
13513
13841
  readContract,
13514
13842
  readContractForClientCall,
13843
+ readName,
13515
13844
  resolveAliasedSymbol,
13516
13845
  resolveSubject,
13517
13846
  rootIdentifier
package/dist/index.d.cts CHANGED
@@ -820,6 +820,39 @@ declare function formatPackHealth(checks: ReadonlyArray<HealthCheck>, audiences:
820
820
  declare function parseConditionExpression(expr: Expression, depth?: number): Predicate | null;
821
821
 
822
822
  declare function isImportedFrom(identifier: Node, expectedModule: string): boolean;
823
+ /**
824
+ * Whether `expectedModule` declares the method a call goes to. A pack
825
+ * asks this when the receiver came from somewhere the source does not
826
+ * spell out: `const redis = await this.getClient()` says nothing about
827
+ * ioredis, and the type of `redis.get` says everything.
828
+ */
829
+ declare function methodDeclaredIn(callee: Node, expectedModule: string): boolean;
830
+
831
+ /**
832
+ * What a name-valued expression says, as fixed text with a hole for
833
+ * each part built at run time. A table name, a bucket, a queue URL and
834
+ * a cache key are all built the same handful of ways, and each storage
835
+ * pack had its own copy of this before it existed. The README beside
836
+ * this file says what each shape reads as.
837
+ */
838
+
839
+ interface ReadNameOptions {
840
+ /**
841
+ * What to say about a name nothing here settles. `"nothing"` gives
842
+ * null. `"reference"` gives a hole named after what the source asked
843
+ * for, `{location.bucket}`, which says which value to go and ask
844
+ * about rather than which bucket this is.
845
+ */
846
+ unsettled?: "nothing" | "reference";
847
+ /**
848
+ * The expression a value was written as, from the run's facts. A
849
+ * caller with no store passes nothing, and the read runs on what the
850
+ * source spells at the call site.
851
+ */
852
+ resolve?: (value: Node) => Node | null;
853
+ }
854
+ /** The name an expression states, or null when nothing settles it. */
855
+ declare function readName(expr: Node, options?: ReadNameOptions): string | null;
823
856
 
824
857
  interface BranchFieldAccesses {
825
858
  /** Terminal location (start, end lines): used to match back to transitions */
@@ -952,4 +985,4 @@ declare function computeAdapterPacksDigest(packVersions: ReadonlyArray<{
952
985
  version?: string;
953
986
  }>): string;
954
987
 
955
- export { ADAPTER_VERSION, type CacheDiagnostic, type CacheLookup, type ClientCallSite, type ConfiguredCallContext, type ConfiguredCallRead, type ConfiguredCallSpec, type ContractReadResult, type DiscoveredUnit, type EmptyStage, type ExtractionReport, type FoundTerminal, type FunctionRoot, type HealthCheck, type HealthViolation, type PackFunnel, ResolutionStore, type Timer, type TimingReport, type TsDiscoveryContext, type TsJsxAttributeLocation, type TsSubUnitContext, type TypeScriptAdapter, type TypeScriptAdapterConfig, collectAncestorBranches, collectClientFieldAccesses, computeAdapterPacksDigest, computeContentHash, createProjectWithoutTsconfig, createTsDiscoveryContext, createTsSubUnitContext, createTypeScriptAdapter, discoverUnits, evaluatePackHealth, exportedDeclarationsOf, extractCodeStructure, extractRawBranches, findNearestTsconfig, findTerminals, formatPackHealth, isImportedFrom, parseConditionExpression, readConfiguredCall, readContract, readContractForClientCall, resolveAliasedSymbol, resolveSubject, rootIdentifier };
988
+ export { ADAPTER_VERSION, type CacheDiagnostic, type CacheLookup, type ClientCallSite, type ConfiguredCallContext, type ConfiguredCallRead, type ConfiguredCallSpec, type ContractReadResult, type DiscoveredUnit, type EmptyStage, type ExtractionReport, type FoundTerminal, type FunctionRoot, type HealthCheck, type HealthViolation, type PackFunnel, type ReadNameOptions, ResolutionStore, type Timer, type TimingReport, type TsDiscoveryContext, type TsJsxAttributeLocation, type TsSubUnitContext, type TypeScriptAdapter, type TypeScriptAdapterConfig, collectAncestorBranches, collectClientFieldAccesses, computeAdapterPacksDigest, computeContentHash, createProjectWithoutTsconfig, createTsDiscoveryContext, createTsSubUnitContext, createTypeScriptAdapter, discoverUnits, evaluatePackHealth, exportedDeclarationsOf, extractCodeStructure, extractRawBranches, findNearestTsconfig, findTerminals, formatPackHealth, isImportedFrom, methodDeclaredIn, parseConditionExpression, readConfiguredCall, readContract, readContractForClientCall, readName, resolveAliasedSymbol, resolveSubject, rootIdentifier };
package/dist/index.d.ts CHANGED
@@ -820,6 +820,39 @@ declare function formatPackHealth(checks: ReadonlyArray<HealthCheck>, audiences:
820
820
  declare function parseConditionExpression(expr: Expression, depth?: number): Predicate | null;
821
821
 
822
822
  declare function isImportedFrom(identifier: Node, expectedModule: string): boolean;
823
+ /**
824
+ * Whether `expectedModule` declares the method a call goes to. A pack
825
+ * asks this when the receiver came from somewhere the source does not
826
+ * spell out: `const redis = await this.getClient()` says nothing about
827
+ * ioredis, and the type of `redis.get` says everything.
828
+ */
829
+ declare function methodDeclaredIn(callee: Node, expectedModule: string): boolean;
830
+
831
+ /**
832
+ * What a name-valued expression says, as fixed text with a hole for
833
+ * each part built at run time. A table name, a bucket, a queue URL and
834
+ * a cache key are all built the same handful of ways, and each storage
835
+ * pack had its own copy of this before it existed. The README beside
836
+ * this file says what each shape reads as.
837
+ */
838
+
839
+ interface ReadNameOptions {
840
+ /**
841
+ * What to say about a name nothing here settles. `"nothing"` gives
842
+ * null. `"reference"` gives a hole named after what the source asked
843
+ * for, `{location.bucket}`, which says which value to go and ask
844
+ * about rather than which bucket this is.
845
+ */
846
+ unsettled?: "nothing" | "reference";
847
+ /**
848
+ * The expression a value was written as, from the run's facts. A
849
+ * caller with no store passes nothing, and the read runs on what the
850
+ * source spells at the call site.
851
+ */
852
+ resolve?: (value: Node) => Node | null;
853
+ }
854
+ /** The name an expression states, or null when nothing settles it. */
855
+ declare function readName(expr: Node, options?: ReadNameOptions): string | null;
823
856
 
824
857
  interface BranchFieldAccesses {
825
858
  /** Terminal location (start, end lines): used to match back to transitions */
@@ -952,4 +985,4 @@ declare function computeAdapterPacksDigest(packVersions: ReadonlyArray<{
952
985
  version?: string;
953
986
  }>): string;
954
987
 
955
- export { ADAPTER_VERSION, type CacheDiagnostic, type CacheLookup, type ClientCallSite, type ConfiguredCallContext, type ConfiguredCallRead, type ConfiguredCallSpec, type ContractReadResult, type DiscoveredUnit, type EmptyStage, type ExtractionReport, type FoundTerminal, type FunctionRoot, type HealthCheck, type HealthViolation, type PackFunnel, ResolutionStore, type Timer, type TimingReport, type TsDiscoveryContext, type TsJsxAttributeLocation, type TsSubUnitContext, type TypeScriptAdapter, type TypeScriptAdapterConfig, collectAncestorBranches, collectClientFieldAccesses, computeAdapterPacksDigest, computeContentHash, createProjectWithoutTsconfig, createTsDiscoveryContext, createTsSubUnitContext, createTypeScriptAdapter, discoverUnits, evaluatePackHealth, exportedDeclarationsOf, extractCodeStructure, extractRawBranches, findNearestTsconfig, findTerminals, formatPackHealth, isImportedFrom, parseConditionExpression, readConfiguredCall, readContract, readContractForClientCall, resolveAliasedSymbol, resolveSubject, rootIdentifier };
988
+ export { ADAPTER_VERSION, type CacheDiagnostic, type CacheLookup, type ClientCallSite, type ConfiguredCallContext, type ConfiguredCallRead, type ConfiguredCallSpec, type ContractReadResult, type DiscoveredUnit, type EmptyStage, type ExtractionReport, type FoundTerminal, type FunctionRoot, type HealthCheck, type HealthViolation, type PackFunnel, type ReadNameOptions, ResolutionStore, type Timer, type TimingReport, type TsDiscoveryContext, type TsJsxAttributeLocation, type TsSubUnitContext, type TypeScriptAdapter, type TypeScriptAdapterConfig, collectAncestorBranches, collectClientFieldAccesses, computeAdapterPacksDigest, computeContentHash, createProjectWithoutTsconfig, createTsDiscoveryContext, createTsSubUnitContext, createTypeScriptAdapter, discoverUnits, evaluatePackHealth, exportedDeclarationsOf, extractCodeStructure, extractRawBranches, findNearestTsconfig, findTerminals, formatPackHealth, isImportedFrom, methodDeclaredIn, parseConditionExpression, readConfiguredCall, readContract, readContractForClientCall, readName, resolveAliasedSymbol, resolveSubject, rootIdentifier };
package/dist/index.js CHANGED
@@ -1451,9 +1451,9 @@ var ModuleGraph = class {
1451
1451
  if (known !== void 0) {
1452
1452
  return known;
1453
1453
  }
1454
- const read = readModuleSpecifiers(sourceFile);
1455
- this.specifiers.set(filePath, read);
1456
- return read;
1454
+ const read2 = readModuleSpecifiers(sourceFile);
1455
+ this.specifiers.set(filePath, read2);
1456
+ return read2;
1457
1457
  }
1458
1458
  /**
1459
1459
  * Collect what the rules need in order to settle this file and this
@@ -2216,6 +2216,15 @@ function isImportedFrom(identifier, expectedModule) {
2216
2216
  (decl) => decl.getSourceFile().getFilePath().includes(`/node_modules/${expectedModule}/`)
2217
2217
  );
2218
2218
  }
2219
+ function methodDeclaredIn(callee, expectedModule) {
2220
+ if (!Node11.isPropertyAccessExpression(callee)) {
2221
+ return false;
2222
+ }
2223
+ const declarations = callee.getNameNode().getSymbol()?.getDeclarations();
2224
+ return declarations !== void 0 && declarations.some(
2225
+ (declaration) => declaration.getSourceFile().getFilePath().includes(`/node_modules/${expectedModule}/`)
2226
+ );
2227
+ }
2219
2228
  function importSpecifierMatches(decl, expectedModule) {
2220
2229
  if (Node11.isImportSpecifier(decl)) {
2221
2230
  return decl.getImportDeclaration().getModuleSpecifierValue() === expectedModule;
@@ -2394,7 +2403,7 @@ function dispatchAccessRecognizers(root, recognizers, isStop, resolveWrittenValu
2394
2403
  traversal.skip();
2395
2404
  return;
2396
2405
  }
2397
- if (!Node11.isPropertyAccessExpression(node) && !Node11.isCallExpression(node)) {
2406
+ if (!Node11.isPropertyAccessExpression(node) && !Node11.isCallExpression(node) && !Node11.isTaggedTemplateExpression(node)) {
2398
2407
  return;
2399
2408
  }
2400
2409
  const ctx = {
@@ -2824,12 +2833,12 @@ function createDefinitionTable() {
2824
2833
  }
2825
2834
  };
2826
2835
  }
2827
- function withDefinitions(read) {
2836
+ function withDefinitions(read2) {
2828
2837
  const table = createDefinitionTable();
2829
2838
  const outer = current;
2830
2839
  current = table;
2831
2840
  try {
2832
- return { value: read(), definitions: table.collected() };
2841
+ return { value: read2(), definitions: table.collected() };
2833
2842
  } finally {
2834
2843
  current = outer;
2835
2844
  }
@@ -3975,10 +3984,59 @@ function writesToBindingUncached(declaration) {
3975
3984
  }
3976
3985
  return { values, inOrder: writesRunInOrder(declaration, writes) };
3977
3986
  }
3987
+ function writesToField(declaration) {
3988
+ const remembered = byDeclaration.get(declaration);
3989
+ if (remembered !== void 0) {
3990
+ return remembered;
3991
+ }
3992
+ const answer = writesToFieldUncached(declaration);
3993
+ byDeclaration.set(declaration, answer);
3994
+ return answer;
3995
+ }
3996
+ function writesToFieldUncached(declaration) {
3997
+ const initializer = declaration.getInitializer();
3998
+ const assignments = assignmentsTo(declaration);
3999
+ if (assignments.length === 0) {
4000
+ return {
4001
+ values: initializer === void 0 ? [] : [initializer],
4002
+ inOrder: true
4003
+ };
4004
+ }
4005
+ const values = [];
4006
+ for (const write of assignments) {
4007
+ if (write.value === null) {
4008
+ return { values: [], inOrder: false };
4009
+ }
4010
+ values.push(write.value);
4011
+ }
4012
+ const withInitializer = initializer === void 0 ? values : [initializer, ...values];
4013
+ return {
4014
+ values: withInitializer,
4015
+ inOrder: assignments.every(
4016
+ (write) => writeRunsInConstructor(declaration, write)
4017
+ )
4018
+ };
4019
+ }
4020
+ function writeRunsInConstructor(declaration, write) {
4021
+ const statement = statementOf(write.node);
4022
+ if (statement === null) {
4023
+ return false;
4024
+ }
4025
+ const body = statement.getParent();
4026
+ if (body === void 0 || !Node17.isBlock(body)) {
4027
+ return false;
4028
+ }
4029
+ const owner = body.getParent();
4030
+ return owner !== void 0 && Node17.isConstructorDeclaration(owner) && owner.getParent() === declaration.getParent();
4031
+ }
3978
4032
  function isWrittenAgain(declaration) {
3979
4033
  return assignmentsTo(declaration).length > 0;
3980
4034
  }
3981
4035
  function assignmentsTo(declaration) {
4036
+ if (Node17.isPropertyDeclaration(declaration)) {
4037
+ const sourceFile2 = declaration.getSourceFile();
4038
+ return sourceFile2.isDeclarationFile() ? [] : assignmentsInFile(sourceFile2).get(declaration) ?? [];
4039
+ }
3982
4040
  if (declaration.getVariableStatement()?.getDeclarationKind() === VariableDeclarationKind.Const) {
3983
4041
  return [];
3984
4042
  }
@@ -4018,7 +4076,7 @@ function writeAt(node) {
4018
4076
  if (Node17.isBinaryExpression(node)) {
4019
4077
  const operator = node.getOperatorToken().getKind();
4020
4078
  const target = node.getLeft();
4021
- if (!isAssignmentOperator(operator) || !Node17.isIdentifier(target)) {
4079
+ if (!isAssignmentOperator(operator) || !(Node17.isIdentifier(target) || Node17.isPropertyAccessExpression(target))) {
4022
4080
  return null;
4023
4081
  }
4024
4082
  const value = operator === SyntaxKind5.EqualsToken ? node.getRight() : null;
@@ -4035,13 +4093,14 @@ function writeAt(node) {
4035
4093
  return null;
4036
4094
  }
4037
4095
  function declarationsOf(name) {
4038
- const symbol = name.getSymbol();
4096
+ const nameNode = Node17.isPropertyAccessExpression(name) ? name.getNameNode() : name;
4097
+ const symbol = nameNode.getSymbol();
4039
4098
  if (symbol === void 0) {
4040
4099
  return [];
4041
4100
  }
4042
4101
  const declarations = [];
4043
4102
  for (const declaration of symbol.getDeclarations()) {
4044
- if (Node17.isVariableDeclaration(declaration)) {
4103
+ if (Node17.isVariableDeclaration(declaration) || Node17.isPropertyDeclaration(declaration)) {
4045
4104
  declarations.push(declaration);
4046
4105
  }
4047
4106
  }
@@ -4071,9 +4130,9 @@ function statementOf(node) {
4071
4130
  function isReadBefore(declaration, position) {
4072
4131
  const name = declaration.getName();
4073
4132
  const sourceFile = declaration.getSourceFile();
4074
- let read = false;
4133
+ let read2 = false;
4075
4134
  sourceFile.forEachDescendant((node, traversal) => {
4076
- if (read) {
4135
+ if (read2) {
4077
4136
  traversal.stop();
4078
4137
  return;
4079
4138
  }
@@ -4092,10 +4151,10 @@ function isReadBefore(declaration, position) {
4092
4151
  return;
4093
4152
  }
4094
4153
  if (declarationsOf(node).includes(declaration)) {
4095
- read = true;
4154
+ read2 = true;
4096
4155
  }
4097
4156
  });
4098
- return read;
4157
+ return read2;
4099
4158
  }
4100
4159
  function startsItsOwnBody(node) {
4101
4160
  return Node17.isFunctionDeclaration(node) || Node17.isFunctionExpression(node) || Node17.isArrowFunction(node) || Node17.isMethodDeclaration(node) || Node17.isClassDeclaration(node);
@@ -6070,6 +6129,14 @@ function emitBindingValues(db, table, declaration) {
6070
6129
  }
6071
6130
  fact(db, "endsHolding", declarationId, emitValue(db, table, last));
6072
6131
  }
6132
+ function emitFieldValues(db, table, declaration) {
6133
+ const { values, inOrder } = writesToField(declaration);
6134
+ const last = values[values.length - 1];
6135
+ if (!inOrder || last === void 0) {
6136
+ return;
6137
+ }
6138
+ fact(db, "binds", nodeId(declaration), emitValue(db, table, last));
6139
+ }
6073
6140
  function emitBindingElementFacts(db, table, element) {
6074
6141
  const id = nodeId(element);
6075
6142
  table.byId.set(id, element);
@@ -6145,6 +6212,11 @@ function emitReferenceFacts(db, table, reference) {
6145
6212
  emitBindingValues(db, table, declaration);
6146
6213
  continue;
6147
6214
  }
6215
+ if (Node26.isPropertyDeclaration(declaration)) {
6216
+ fact(db, "binds", referenceId, declarationId);
6217
+ emitFieldValues(db, table, declaration);
6218
+ continue;
6219
+ }
6148
6220
  if (Node26.isParameterDeclaration(declaration)) {
6149
6221
  fact(db, "binds", referenceId, declarationId);
6150
6222
  continue;
@@ -6379,9 +6451,9 @@ function propertyValueOf(property) {
6379
6451
  }
6380
6452
  function functionValueOf(value, resolution) {
6381
6453
  const written = factKeyOf(value);
6382
- const here = toFunctionRoot(written);
6383
- if (here !== null) {
6384
- return here;
6454
+ const here2 = toFunctionRoot(written);
6455
+ if (here2 !== null) {
6456
+ return here2;
6385
6457
  }
6386
6458
  if (resolution === void 0 || !couldNameAValue(written)) {
6387
6459
  return null;
@@ -8143,7 +8215,7 @@ function unitsOfArrayElements(array, match, kind, resolution, prefix, ancestry)
8143
8215
  }
8144
8216
  function unitsForRouteObject(object, match, kind, resolution, prefix, ancestry) {
8145
8217
  const own = ownPathOfObject(object, match);
8146
- const here = unitForRouteObject(object, match, kind, resolution, prefix, own);
8218
+ const here2 = unitForRouteObject(object, match, kind, resolution, prefix, own);
8147
8219
  const nested = unitsUnderRouteObject(
8148
8220
  object,
8149
8221
  match,
@@ -8152,7 +8224,7 @@ function unitsForRouteObject(object, match, kind, resolution, prefix, ancestry)
8152
8224
  prefixUnder(prefix, own),
8153
8225
  ancestry
8154
8226
  );
8155
- return here === null ? nested : [here, ...nested];
8227
+ return here2 === null ? nested : [here2, ...nested];
8156
8228
  }
8157
8229
  function unitForRouteObject(object, match, kind, resolution, prefix, own) {
8158
8230
  const target = elementTargetOfValue(
@@ -8640,6 +8712,9 @@ function trackFactoryBindings(sourceFile, recognize) {
8640
8712
  }
8641
8713
 
8642
8714
  // src/discovery/packageImport.ts
8715
+ function isLanguageLib(file) {
8716
+ return /[\\/]typescript[\\/]lib[\\/]lib\..*\.d\.ts$/.test(file.getFilePath());
8717
+ }
8643
8718
  function splitPackageSpec(spec) {
8644
8719
  if (spec.startsWith("@")) {
8645
8720
  const parts2 = spec.split("/");
@@ -8757,6 +8832,9 @@ function discoverPackageImports(sourceFile, match, kind) {
8757
8832
  if (subjectProvenance === null) {
8758
8833
  return null;
8759
8834
  }
8835
+ if (!methodComesFromSource(callee)) {
8836
+ return subjectProvenance;
8837
+ }
8760
8838
  return {
8761
8839
  packageName: subjectProvenance.packageName,
8762
8840
  exportPath: [...subjectProvenance.exportPath, callee.getName()]
@@ -8764,6 +8842,15 @@ function discoverPackageImports(sourceFile, match, kind) {
8764
8842
  }
8765
8843
  return null;
8766
8844
  }
8845
+ function methodComesFromSource(callee) {
8846
+ const declarations = callee.getNameNode().getSymbol()?.getDeclarations();
8847
+ if (declarations === void 0 || declarations.length === 0) {
8848
+ return true;
8849
+ }
8850
+ return declarations.some(
8851
+ (declaration) => !isLanguageLib(declaration.getSourceFile())
8852
+ );
8853
+ }
8767
8854
  const results = [];
8768
8855
  const seen = /* @__PURE__ */ new Set();
8769
8856
  sourceFile.forEachDescendant((node) => {
@@ -10381,8 +10468,8 @@ function bindingLabel(binding) {
10381
10468
  if (semantics.name === "rest") {
10382
10469
  return `${semantics.method} ${semantics.path}`;
10383
10470
  }
10384
- if (semantics.name === "storage-relational") {
10385
- return `${semantics.storageSystem}:${semantics.table}`;
10471
+ if (semantics.name === "storage") {
10472
+ return `${semantics.storageSystem}:${semantics.container}`;
10386
10473
  }
10387
10474
  if (semantics.name === "message-bus") {
10388
10475
  return `${semantics.messageBus}:${semantics.channel}`;
@@ -10616,7 +10703,9 @@ function extractReachableSummary(candidate, options, recognizers) {
10616
10703
  unit,
10617
10704
  reachablePack,
10618
10705
  recognizers.invocation,
10619
- recognizers.access
10706
+ recognizers.access,
10707
+ void 0,
10708
+ recognizers.resolveWrittenValue
10620
10709
  );
10621
10710
  raw.boundaryBinding = functionCallBinding({
10622
10711
  transport: "in-process",
@@ -11927,7 +12016,7 @@ function announcedBoundaryStructure(unit) {
11927
12016
  };
11928
12017
  }
11929
12018
  function extractCodeStructure(unit, pack, invocationRecognizers = [], accessRecognizers = [], barriers = NO_BARRIERS, resolveWrittenValue) {
11930
- const read = withDefinitions(
12019
+ const read2 = withDefinitions(
11931
12020
  () => readCodeStructure(
11932
12021
  unit,
11933
12022
  pack,
@@ -11937,7 +12026,7 @@ function extractCodeStructure(unit, pack, invocationRecognizers = [], accessReco
11937
12026
  resolveWrittenValue
11938
12027
  )
11939
12028
  );
11940
- return read.definitions === null ? read.value : { ...read.value, definitions: read.definitions };
12029
+ return read2.definitions === null ? read2.value : { ...read2.value, definitions: read2.definitions };
11941
12030
  }
11942
12031
  function responseAccessorNames(pack, kind) {
11943
12032
  return pack.responseSemantics?.filter((m) => m.semantics.type === kind).map((m) => m.name);
@@ -13062,7 +13151,8 @@ function createTypeScriptAdapter(config) {
13062
13151
  config.frameworks,
13063
13152
  tallies
13064
13153
  ),
13065
- access: collectAccessRecognizers(config.frameworks, tallies)
13154
+ access: collectAccessRecognizers(config.frameworks, tallies),
13155
+ resolveWrittenValue: (value) => resolution.resolveWrittenValue(value)
13066
13156
  }
13067
13157
  )
13068
13158
  ) : withSubUnits;
@@ -13446,7 +13536,7 @@ function threwWhileReading(packs) {
13446
13536
  return packs.filter((funnel) => funnel.failures.length > 0).map((funnel) => {
13447
13537
  const first = funnel.failures[0];
13448
13538
  const rest = funnel.failures.length - 1;
13449
- const alsoIn = rest > 0 ? ` and on ${rest} other ${rest === 1 ? "file" : "files"}` : "";
13539
+ const alsoIn = rest > 0 ? ` and on ${rest} ${rest === 1 ? "other file" : "other files"}` : "";
13450
13540
  return {
13451
13541
  label: funnel.pack,
13452
13542
  detail: `threw from ${first.hook} on ${first.file}${alsoIn}, so its counts below are a floor: ${first.message}`
@@ -13498,6 +13588,241 @@ function formatPackHealth(checks, audiences) {
13498
13588
  return `${lines.join("\n")}
13499
13589
  `;
13500
13590
  }
13591
+
13592
+ // src/resolve/readName.ts
13593
+ import { Node as Node55, SyntaxKind as SyntaxKind10 } from "ts-morph";
13594
+ var MAX_HELPER_HOPS = 2;
13595
+ var UNNAMED_HOLE = "param";
13596
+ function readName(expr, options = {}) {
13597
+ const name = read(expr, {
13598
+ resolve: options.resolve ?? (() => null),
13599
+ bindings: /* @__PURE__ */ new Map(),
13600
+ hops: 0,
13601
+ seen: /* @__PURE__ */ new Set(),
13602
+ insideHole: false
13603
+ });
13604
+ if (name !== null || options.unsettled !== "reference") {
13605
+ return name;
13606
+ }
13607
+ const asked = holeName(expr, {
13608
+ resolve: () => null,
13609
+ bindings: /* @__PURE__ */ new Map(),
13610
+ hops: 0,
13611
+ seen: /* @__PURE__ */ new Set(),
13612
+ insideHole: true
13613
+ });
13614
+ return asked === UNNAMED_HOLE ? null : `{${asked}}`;
13615
+ }
13616
+ function read(expr, ctx) {
13617
+ if (ctx.seen.has(expr)) {
13618
+ return null;
13619
+ }
13620
+ const seen = new Set(ctx.seen).add(expr);
13621
+ const inner = { ...ctx, seen };
13622
+ if (Node55.isStringLiteral(expr) || Node55.isNoSubstitutionTemplateLiteral(expr)) {
13623
+ return expr.getLiteralValue();
13624
+ }
13625
+ if (Node55.isNumericLiteral(expr)) {
13626
+ return expr.getLiteralText();
13627
+ }
13628
+ if (Node55.isTemplateExpression(expr)) {
13629
+ return fromTemplate(expr, inner);
13630
+ }
13631
+ if (Node55.isParenthesizedExpression(expr)) {
13632
+ return read(expr.getExpression(), inner);
13633
+ }
13634
+ if (Node55.isBinaryExpression(expr)) {
13635
+ return fromBinary(expr, inner);
13636
+ }
13637
+ if (Node55.isCallExpression(expr)) {
13638
+ return fromCall(expr, inner);
13639
+ }
13640
+ if (Node55.isIdentifier(expr)) {
13641
+ const only = boundOnce(expr, ctx);
13642
+ if (only !== null) {
13643
+ return read(only.node, { ...inner, bindings: only.scope });
13644
+ }
13645
+ }
13646
+ const written = ctx.resolve(expr);
13647
+ return written === null || written === expr ? null : read(written, inner);
13648
+ }
13649
+ function fromTemplate(expr, ctx) {
13650
+ if (!Node55.isTemplateExpression(expr)) {
13651
+ return null;
13652
+ }
13653
+ let name = expr.getHead().getLiteralText();
13654
+ for (const span of expr.getTemplateSpans()) {
13655
+ name += readOrHole(span.getExpression(), { ...ctx, insideHole: true });
13656
+ name += span.getLiteral().getLiteralText();
13657
+ }
13658
+ return name;
13659
+ }
13660
+ function fromBinary(expr, ctx) {
13661
+ if (!Node55.isBinaryExpression(expr)) {
13662
+ return null;
13663
+ }
13664
+ const operator = expr.getOperatorToken().getKind();
13665
+ if (operator === SyntaxKind10.BarBarToken || operator === SyntaxKind10.QuestionQuestionToken) {
13666
+ return ctx.insideHole ? null : read(expr.getRight(), ctx);
13667
+ }
13668
+ if (operator !== SyntaxKind10.PlusToken) {
13669
+ return null;
13670
+ }
13671
+ const parts = { ...ctx, insideHole: true };
13672
+ return readOrHole(expr.getLeft(), parts) + readOrHole(expr.getRight(), parts);
13673
+ }
13674
+ function fromCall(expr, ctx) {
13675
+ if (!Node55.isCallExpression(expr)) {
13676
+ return null;
13677
+ }
13678
+ const callee = expr.getExpression();
13679
+ if (Node55.isPropertyAccessExpression(callee) && callee.getName() === "join") {
13680
+ return fromJoin(callee.getExpression(), expr.getArguments(), ctx);
13681
+ }
13682
+ if (Node55.isIdentifier(callee) && callee.getText() === "String") {
13683
+ const only = expr.getArguments()[0];
13684
+ return only === void 0 ? null : readOrHole(only, ctx);
13685
+ }
13686
+ if (ctx.hops >= MAX_HELPER_HOPS) {
13687
+ return null;
13688
+ }
13689
+ const helper = singleReturnBody(callee, ctx);
13690
+ if (helper === null) {
13691
+ return null;
13692
+ }
13693
+ return read(helper.returned, {
13694
+ ...ctx,
13695
+ hops: ctx.hops + 1,
13696
+ bindings: bind(helper.parameters, expr.getArguments(), ctx.bindings)
13697
+ });
13698
+ }
13699
+ function fromJoin(subject, args, ctx) {
13700
+ const separator = args[0];
13701
+ if (separator === void 0 || !Node55.isStringLiteral(separator)) {
13702
+ return null;
13703
+ }
13704
+ const parts = elementsOf(subject, ctx);
13705
+ if (parts === null) {
13706
+ return null;
13707
+ }
13708
+ return parts.map(
13709
+ (part) => readOrHole(part.node, {
13710
+ ...ctx,
13711
+ bindings: part.scope,
13712
+ insideHole: true
13713
+ })
13714
+ ).join(separator.getLiteralValue());
13715
+ }
13716
+ function elementsOf(subject, ctx) {
13717
+ if (Node55.isArrayLiteralExpression(subject)) {
13718
+ return subject.getElements().map((element) => here(element, ctx));
13719
+ }
13720
+ if (Node55.isCallExpression(subject)) {
13721
+ const callee = subject.getExpression();
13722
+ if (Node55.isPropertyAccessExpression(callee) && callee.getName() === "map") {
13723
+ return elementsOf(callee.getExpression(), ctx);
13724
+ }
13725
+ return null;
13726
+ }
13727
+ if (!Node55.isIdentifier(subject)) {
13728
+ return null;
13729
+ }
13730
+ const bound = ctx.bindings.get(subject.getText());
13731
+ if (bound === void 0) {
13732
+ return null;
13733
+ }
13734
+ const only = bound.length === 1 ? bound[0] : void 0;
13735
+ return only !== void 0 && Node55.isArrayLiteralExpression(only.node) ? only.node.getElements().map((element) => here(element, ctx)) : bound;
13736
+ }
13737
+ function singleReturnBody(callee, ctx) {
13738
+ const declaration = declarationOf2(callee, ctx);
13739
+ if (declaration === null) {
13740
+ return null;
13741
+ }
13742
+ const parameters = declaration.getParameters().map((parameter) => ({
13743
+ name: parameter.getName(),
13744
+ rest: parameter.isRestParameter()
13745
+ }));
13746
+ const body = declaration.getBody();
13747
+ if (body === void 0) {
13748
+ return null;
13749
+ }
13750
+ if (Node55.isExpression(body)) {
13751
+ return { parameters, returned: body };
13752
+ }
13753
+ if (!Node55.isBlock(body)) {
13754
+ return null;
13755
+ }
13756
+ const statements = body.getStatements();
13757
+ const only = statements[0];
13758
+ if (statements.length !== 1 || only === void 0 || !Node55.isReturnStatement(only)) {
13759
+ return null;
13760
+ }
13761
+ const returned = only.getExpression();
13762
+ return returned === void 0 ? null : { parameters, returned };
13763
+ }
13764
+ function declarationOf2(callee, ctx) {
13765
+ const symbol = Node55.isPropertyAccessExpression(callee) ? callee.getNameNode().getSymbol() : callee.getSymbol();
13766
+ const declarations = symbol?.getAliasedSymbol()?.getDeclarations() ?? symbol?.getDeclarations() ?? [];
13767
+ for (const declaration of declarations) {
13768
+ const callable = asCallable(declaration);
13769
+ if (callable !== null) {
13770
+ return callable;
13771
+ }
13772
+ }
13773
+ const written = ctx.resolve(callee);
13774
+ return written === null || written === callee ? null : declarationOf2(written, { ...ctx, resolve: () => null });
13775
+ }
13776
+ function asCallable(declaration) {
13777
+ if (Node55.isFunctionDeclaration(declaration) || Node55.isMethodDeclaration(declaration) || Node55.isArrowFunction(declaration) || Node55.isFunctionExpression(declaration)) {
13778
+ return declaration;
13779
+ }
13780
+ if (!Node55.isVariableDeclaration(declaration)) {
13781
+ return null;
13782
+ }
13783
+ const initializer = declaration.getInitializer();
13784
+ if (initializer !== void 0 && (Node55.isArrowFunction(initializer) || Node55.isFunctionExpression(initializer))) {
13785
+ return initializer;
13786
+ }
13787
+ return null;
13788
+ }
13789
+ function bind(parameters, args, scope) {
13790
+ const bindings = /* @__PURE__ */ new Map();
13791
+ parameters.forEach((parameter, index) => {
13792
+ const passed = parameter.rest ? args.slice(index) : args.slice(index, index + 1);
13793
+ if (passed.length > 0) {
13794
+ bindings.set(
13795
+ parameter.name,
13796
+ passed.map((node) => ({ node, scope }))
13797
+ );
13798
+ }
13799
+ });
13800
+ return bindings;
13801
+ }
13802
+ function here(node, ctx) {
13803
+ return { node, scope: ctx.bindings };
13804
+ }
13805
+ function boundOnce(expr, ctx) {
13806
+ const bound = Node55.isIdentifier(expr) ? ctx.bindings.get(expr.getText()) : void 0;
13807
+ return bound !== void 0 && bound.length === 1 ? bound[0] : null;
13808
+ }
13809
+ function readOrHole(expr, ctx) {
13810
+ const name = read(expr, ctx);
13811
+ return name ?? `{${holeName(expr, ctx)}}`;
13812
+ }
13813
+ function holeName(expr, ctx) {
13814
+ const bound = boundOnce(expr, ctx);
13815
+ if (bound !== null) {
13816
+ return holeName(bound.node, { ...ctx, bindings: bound.scope });
13817
+ }
13818
+ if (Node55.isIdentifier(expr)) {
13819
+ return expr.getText();
13820
+ }
13821
+ if (Node55.isPropertyAccessExpression(expr)) {
13822
+ return expr.getName();
13823
+ }
13824
+ return UNNAMED_HOLE;
13825
+ }
13501
13826
  export {
13502
13827
  ADAPTER_VERSION,
13503
13828
  ResolutionStore,
@@ -13518,10 +13843,12 @@ export {
13518
13843
  findTerminals,
13519
13844
  formatPackHealth,
13520
13845
  isImportedFrom,
13846
+ methodDeclaredIn,
13521
13847
  parseConditionExpression,
13522
13848
  readConfiguredCall,
13523
13849
  readContract,
13524
13850
  readContractForClientCall,
13851
+ readName,
13525
13852
  resolveAliasedSymbol,
13526
13853
  resolveSubject,
13527
13854
  rootIdentifier
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/adapter-typescript",
3
- "version": "0.5.11",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "description": "TypeScript adapter for suss — walks source files with ts-morph and emits raw extractions for the suss pipeline.",
6
6
  "keywords": [
@@ -46,10 +46,10 @@
46
46
  "typecheck": "tsc --noEmit"
47
47
  },
48
48
  "dependencies": {
49
- "@suss/behavioral-ir": "0.5.11",
50
- "@suss/datalog": "0.5.11",
51
- "@suss/extractor": "0.5.11",
52
- "@suss/resolution": "0.5.11",
49
+ "@suss/behavioral-ir": "0.7.0",
50
+ "@suss/datalog": "0.7.0",
51
+ "@suss/extractor": "0.7.0",
52
+ "@suss/resolution": "0.7.0",
53
53
  "graphql": "^16.8.0",
54
54
  "picomatch": "^4.0.4",
55
55
  "ts-morph": "^23.0.0"