@suss/adapter-typescript 0.6.0 → 0.7.1

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;
@@ -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
  }
@@ -4151,9 +4162,9 @@ function statementOf(node) {
4151
4162
  function isReadBefore(declaration, position) {
4152
4163
  const name = declaration.getName();
4153
4164
  const sourceFile = declaration.getSourceFile();
4154
- let read = false;
4165
+ let read2 = false;
4155
4166
  sourceFile.forEachDescendant((node, traversal) => {
4156
- if (read) {
4167
+ if (read2) {
4157
4168
  traversal.stop();
4158
4169
  return;
4159
4170
  }
@@ -4172,10 +4183,10 @@ function isReadBefore(declaration, position) {
4172
4183
  return;
4173
4184
  }
4174
4185
  if (declarationsOf(node).includes(declaration)) {
4175
- read = true;
4186
+ read2 = true;
4176
4187
  }
4177
4188
  });
4178
- return read;
4189
+ return read2;
4179
4190
  }
4180
4191
  function startsItsOwnBody(node) {
4181
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);
@@ -6465,9 +6476,9 @@ function propertyValueOf(property) {
6465
6476
  }
6466
6477
  function functionValueOf(value, resolution) {
6467
6478
  const written = factKeyOf(value);
6468
- const here = toFunctionRoot(written);
6469
- if (here !== null) {
6470
- return here;
6479
+ const here2 = toFunctionRoot(written);
6480
+ if (here2 !== null) {
6481
+ return here2;
6471
6482
  }
6472
6483
  if (resolution === void 0 || !couldNameAValue(written)) {
6473
6484
  return null;
@@ -8223,7 +8234,7 @@ function unitsOfArrayElements(array, match, kind, resolution, prefix, ancestry)
8223
8234
  }
8224
8235
  function unitsForRouteObject(object, match, kind, resolution, prefix, ancestry) {
8225
8236
  const own = ownPathOfObject(object, match);
8226
- const here = unitForRouteObject(object, match, kind, resolution, prefix, own);
8237
+ const here2 = unitForRouteObject(object, match, kind, resolution, prefix, own);
8227
8238
  const nested = unitsUnderRouteObject(
8228
8239
  object,
8229
8240
  match,
@@ -8232,7 +8243,7 @@ function unitsForRouteObject(object, match, kind, resolution, prefix, ancestry)
8232
8243
  prefixUnder(prefix, own),
8233
8244
  ancestry
8234
8245
  );
8235
- return here === null ? nested : [here, ...nested];
8246
+ return here2 === null ? nested : [here2, ...nested];
8236
8247
  }
8237
8248
  function unitForRouteObject(object, match, kind, resolution, prefix, own) {
8238
8249
  const target = elementTargetOfValue(
@@ -12001,7 +12012,7 @@ function announcedBoundaryStructure(unit) {
12001
12012
  };
12002
12013
  }
12003
12014
  function extractCodeStructure(unit, pack, invocationRecognizers = [], accessRecognizers = [], barriers = NO_BARRIERS, resolveWrittenValue) {
12004
- const read = withDefinitions(
12015
+ const read2 = withDefinitions(
12005
12016
  () => readCodeStructure(
12006
12017
  unit,
12007
12018
  pack,
@@ -12011,7 +12022,7 @@ function extractCodeStructure(unit, pack, invocationRecognizers = [], accessReco
12011
12022
  resolveWrittenValue
12012
12023
  )
12013
12024
  );
12014
- return read.definitions === null ? read.value : { ...read.value, definitions: read.definitions };
12025
+ return read2.definitions === null ? read2.value : { ...read2.value, definitions: read2.definitions };
12015
12026
  }
12016
12027
  function responseAccessorNames(pack, kind) {
12017
12028
  return pack.responseSemantics?.filter((m) => m.semantics.type === kind).map((m) => m.name);
@@ -13516,7 +13527,7 @@ function threwWhileReading(packs) {
13516
13527
  return packs.filter((funnel) => funnel.failures.length > 0).map((funnel) => {
13517
13528
  const first = funnel.failures[0];
13518
13529
  const rest = funnel.failures.length - 1;
13519
- 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"}` : "";
13520
13531
  return {
13521
13532
  label: funnel.pack,
13522
13533
  detail: `threw from ${first.hook} on ${first.file}${alsoIn}, so its counts below are a floor: ${first.message}`
@@ -13568,6 +13579,241 @@ function formatPackHealth(checks, audiences) {
13568
13579
  return `${lines.join("\n")}
13569
13580
  `;
13570
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
+ }
13571
13817
  // Annotate the CommonJS export names for ESM import in node:
13572
13818
  0 && (module.exports = {
13573
13819
  ADAPTER_VERSION,
@@ -13589,10 +13835,12 @@ function formatPackHealth(checks, audiences) {
13589
13835
  findTerminals,
13590
13836
  formatPackHealth,
13591
13837
  isImportedFrom,
13838
+ methodDeclaredIn,
13592
13839
  parseConditionExpression,
13593
13840
  readConfiguredCall,
13594
13841
  readContract,
13595
13842
  readContractForClientCall,
13843
+ readName,
13596
13844
  resolveAliasedSymbol,
13597
13845
  resolveSubject,
13598
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;
@@ -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
  }
@@ -4121,9 +4130,9 @@ function statementOf(node) {
4121
4130
  function isReadBefore(declaration, position) {
4122
4131
  const name = declaration.getName();
4123
4132
  const sourceFile = declaration.getSourceFile();
4124
- let read = false;
4133
+ let read2 = false;
4125
4134
  sourceFile.forEachDescendant((node, traversal) => {
4126
- if (read) {
4135
+ if (read2) {
4127
4136
  traversal.stop();
4128
4137
  return;
4129
4138
  }
@@ -4142,10 +4151,10 @@ function isReadBefore(declaration, position) {
4142
4151
  return;
4143
4152
  }
4144
4153
  if (declarationsOf(node).includes(declaration)) {
4145
- read = true;
4154
+ read2 = true;
4146
4155
  }
4147
4156
  });
4148
- return read;
4157
+ return read2;
4149
4158
  }
4150
4159
  function startsItsOwnBody(node) {
4151
4160
  return Node17.isFunctionDeclaration(node) || Node17.isFunctionExpression(node) || Node17.isArrowFunction(node) || Node17.isMethodDeclaration(node) || Node17.isClassDeclaration(node);
@@ -6442,9 +6451,9 @@ function propertyValueOf(property) {
6442
6451
  }
6443
6452
  function functionValueOf(value, resolution) {
6444
6453
  const written = factKeyOf(value);
6445
- const here = toFunctionRoot(written);
6446
- if (here !== null) {
6447
- return here;
6454
+ const here2 = toFunctionRoot(written);
6455
+ if (here2 !== null) {
6456
+ return here2;
6448
6457
  }
6449
6458
  if (resolution === void 0 || !couldNameAValue(written)) {
6450
6459
  return null;
@@ -8206,7 +8215,7 @@ function unitsOfArrayElements(array, match, kind, resolution, prefix, ancestry)
8206
8215
  }
8207
8216
  function unitsForRouteObject(object, match, kind, resolution, prefix, ancestry) {
8208
8217
  const own = ownPathOfObject(object, match);
8209
- const here = unitForRouteObject(object, match, kind, resolution, prefix, own);
8218
+ const here2 = unitForRouteObject(object, match, kind, resolution, prefix, own);
8210
8219
  const nested = unitsUnderRouteObject(
8211
8220
  object,
8212
8221
  match,
@@ -8215,7 +8224,7 @@ function unitsForRouteObject(object, match, kind, resolution, prefix, ancestry)
8215
8224
  prefixUnder(prefix, own),
8216
8225
  ancestry
8217
8226
  );
8218
- return here === null ? nested : [here, ...nested];
8227
+ return here2 === null ? nested : [here2, ...nested];
8219
8228
  }
8220
8229
  function unitForRouteObject(object, match, kind, resolution, prefix, own) {
8221
8230
  const target = elementTargetOfValue(
@@ -12007,7 +12016,7 @@ function announcedBoundaryStructure(unit) {
12007
12016
  };
12008
12017
  }
12009
12018
  function extractCodeStructure(unit, pack, invocationRecognizers = [], accessRecognizers = [], barriers = NO_BARRIERS, resolveWrittenValue) {
12010
- const read = withDefinitions(
12019
+ const read2 = withDefinitions(
12011
12020
  () => readCodeStructure(
12012
12021
  unit,
12013
12022
  pack,
@@ -12017,7 +12026,7 @@ function extractCodeStructure(unit, pack, invocationRecognizers = [], accessReco
12017
12026
  resolveWrittenValue
12018
12027
  )
12019
12028
  );
12020
- return read.definitions === null ? read.value : { ...read.value, definitions: read.definitions };
12029
+ return read2.definitions === null ? read2.value : { ...read2.value, definitions: read2.definitions };
12021
12030
  }
12022
12031
  function responseAccessorNames(pack, kind) {
12023
12032
  return pack.responseSemantics?.filter((m) => m.semantics.type === kind).map((m) => m.name);
@@ -13527,7 +13536,7 @@ function threwWhileReading(packs) {
13527
13536
  return packs.filter((funnel) => funnel.failures.length > 0).map((funnel) => {
13528
13537
  const first = funnel.failures[0];
13529
13538
  const rest = funnel.failures.length - 1;
13530
- 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"}` : "";
13531
13540
  return {
13532
13541
  label: funnel.pack,
13533
13542
  detail: `threw from ${first.hook} on ${first.file}${alsoIn}, so its counts below are a floor: ${first.message}`
@@ -13579,6 +13588,241 @@ function formatPackHealth(checks, audiences) {
13579
13588
  return `${lines.join("\n")}
13580
13589
  `;
13581
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
+ }
13582
13826
  export {
13583
13827
  ADAPTER_VERSION,
13584
13828
  ResolutionStore,
@@ -13599,10 +13843,12 @@ export {
13599
13843
  findTerminals,
13600
13844
  formatPackHealth,
13601
13845
  isImportedFrom,
13846
+ methodDeclaredIn,
13602
13847
  parseConditionExpression,
13603
13848
  readConfiguredCall,
13604
13849
  readContract,
13605
13850
  readContractForClientCall,
13851
+ readName,
13606
13852
  resolveAliasedSymbol,
13607
13853
  resolveSubject,
13608
13854
  rootIdentifier
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/adapter-typescript",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
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.6.0",
50
- "@suss/datalog": "0.6.0",
51
- "@suss/extractor": "0.6.0",
52
- "@suss/resolution": "0.6.0",
49
+ "@suss/behavioral-ir": "0.7.1",
50
+ "@suss/datalog": "0.7.1",
51
+ "@suss/extractor": "0.7.1",
52
+ "@suss/resolution": "0.7.1",
53
53
  "graphql": "^16.8.0",
54
54
  "picomatch": "^4.0.4",
55
55
  "ts-morph": "^23.0.0"