@suss/adapter-typescript 0.9.0 → 0.11.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/README.md +36 -0
- package/dist/index.cjs +1735 -364
- package/dist/index.d.cts +198 -15
- package/dist/index.d.ts +198 -15
- package/dist/index.js +1726 -351
- package/package.json +5 -5
package/dist/index.d.cts
CHANGED
|
@@ -1,18 +1,44 @@
|
|
|
1
1
|
import { SourceFile, Node, FunctionDeclaration, FunctionExpression, ArrowFunction, MethodDeclaration, CallExpression, Project, Symbol, Expression } from 'ts-morph';
|
|
2
2
|
import { TransparentWrapper, RawCondition, DiscoveryPattern, TerminalPattern, InputMappingPattern, LanguageAdapter, PatternPack, ExtractorOptions, InvocationRecognizer, AccessRecognizer, RawCodeStructure, RawTerminal, RawBranch, EffectArg, RawDeclaredContract, ContractPattern, ResponsePropertyMapping } from '@suss/extractor';
|
|
3
3
|
import { BehavioralSummary, MessageBusSemantics, DeployableUnit, BoundaryBinding, Predicate, TypeShape, ValueRef } from '@suss/behavioral-ir';
|
|
4
|
+
import { Proof, Atom } from '@suss/datalog';
|
|
5
|
+
import { ResolutionExplanation } from '@suss/resolution';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* cache.ts: the on-disk extraction cache.
|
|
9
|
+
*
|
|
10
|
+
* A run reuses the previous one's summaries whole when nothing
|
|
11
|
+
* changed, and per file when some files did. The entry directory is
|
|
12
|
+
* named after everything that has to agree before any reuse is sound:
|
|
13
|
+
* schema version, adapter version and code hash, pack versions, the
|
|
14
|
+
* extraction config stamp and the tsconfig path. Inside it, the
|
|
15
|
+
* manifest records a stamp and content hash per file, each summary's
|
|
16
|
+
* owning files, and per owning file the other files its walk read, so
|
|
17
|
+
* a change to any of those re-extracts the owner instead of serving a
|
|
18
|
+
* stale answer. The full design, including what per-file reuse can
|
|
19
|
+
* and cannot promise, is in this package's README.
|
|
20
|
+
*/
|
|
4
21
|
|
|
5
22
|
/** Reported by `lookup`: what the cache decided, and why. */
|
|
6
23
|
interface CacheDiagnostic {
|
|
7
|
-
kind: "hit" | "miss";
|
|
24
|
+
kind: "hit" | "miss" | "partial";
|
|
8
25
|
/**
|
|
9
26
|
* Reason the lookup missed (only set when kind === "miss").
|
|
10
|
-
* `key-changed` means the cache directory
|
|
27
|
+
* `key-changed` means the cache directory contains entries, but none
|
|
11
28
|
* under this run's schema, adapter, packs and tsconfig path.
|
|
12
29
|
* `files-changed` means the include set is not the one the entry
|
|
13
30
|
* was written from.
|
|
14
31
|
*/
|
|
15
32
|
missReason?: "no-manifest" | "key-changed" | "tsconfig-changed" | "files-changed";
|
|
33
|
+
/** Set when kind === "partial". */
|
|
34
|
+
partial?: {
|
|
35
|
+
filesChanged: number;
|
|
36
|
+
filesRemoved: number;
|
|
37
|
+
rootsReused: number;
|
|
38
|
+
rootsReextracted: number;
|
|
39
|
+
rootsDeclined: number;
|
|
40
|
+
summariesReused: number;
|
|
41
|
+
};
|
|
16
42
|
}
|
|
17
43
|
/**
|
|
18
44
|
* Result of a cache lookup. A hit gives back the whole summary set; a miss
|
|
@@ -174,12 +200,36 @@ interface FileSetQuery {
|
|
|
174
200
|
* edges a query follows, so cost tracks how indirect the code is.
|
|
175
201
|
*/
|
|
176
202
|
|
|
203
|
+
interface ExplainCallableOptions {
|
|
204
|
+
/** A second file to walk out from; see `resolveCallable`. */
|
|
205
|
+
alsoFrom?: SourceFile;
|
|
206
|
+
/** How many derived nodes deep the proof walk goes; see `proofOf`. */
|
|
207
|
+
maxDepth?: number;
|
|
208
|
+
}
|
|
209
|
+
/** What one witness re-evaluation cost, said rather than hidden. */
|
|
210
|
+
interface ExplainStats {
|
|
211
|
+
/** Facts the demand walk had extracted, which the proof pass reran. */
|
|
212
|
+
baseFacts: number;
|
|
213
|
+
/** Facts the exhaustive pass derived on top of those. */
|
|
214
|
+
derivedFacts: number;
|
|
215
|
+
evaluateMs: number;
|
|
216
|
+
}
|
|
217
|
+
interface ExplainedResolution {
|
|
218
|
+
/** The node the value comes down to. */
|
|
219
|
+
target: Node;
|
|
220
|
+
proof: Proof;
|
|
221
|
+
/** The source node behind a proof atom, when the atom is a node id. */
|
|
222
|
+
nodeFor: (atom: Atom) => Node | undefined;
|
|
223
|
+
stats: ExplainStats;
|
|
224
|
+
}
|
|
177
225
|
declare class ResolutionStore {
|
|
178
226
|
private readonly db;
|
|
179
227
|
private readonly table;
|
|
180
228
|
private readonly fullyExtracted;
|
|
181
229
|
private readonly seededValues;
|
|
182
230
|
private readonly importedNames;
|
|
231
|
+
/** Files the most recent wave walk entered, for the memo to keep. */
|
|
232
|
+
private lastQueryWalked;
|
|
183
233
|
private readonly declarations;
|
|
184
234
|
private readonly graph;
|
|
185
235
|
private stale;
|
|
@@ -192,6 +242,21 @@ declare class ResolutionStore {
|
|
|
192
242
|
* the walk never reaches on its own.
|
|
193
243
|
*/
|
|
194
244
|
resolveCallable(value: Node, alsoFrom?: SourceFile): Node | null;
|
|
245
|
+
/**
|
|
246
|
+
* Every function the value comes down to. One is the answer
|
|
247
|
+
* `resolveCallable` gives; several is a value with more than one
|
|
248
|
+
* possible source, which a caller says at the site rather than
|
|
249
|
+
* folding into the same nothing as a chain that went nowhere.
|
|
250
|
+
*/
|
|
251
|
+
resolveCallableSources(value: Node, alsoFrom?: SourceFile): Node[];
|
|
252
|
+
/**
|
|
253
|
+
* The witness proof behind `resolveCallable`'s answer. Resolves the
|
|
254
|
+
* value first, which walks files and extracts facts in the usual
|
|
255
|
+
* waves, then re-evaluates the rules over those base facts under the
|
|
256
|
+
* witness algebra and rebuilds the proof of the answer. Null when
|
|
257
|
+
* the value does not resolve at all.
|
|
258
|
+
*/
|
|
259
|
+
explainCallable(value: Node, options?: ExplainCallableOptions): ExplainedResolution | null;
|
|
195
260
|
resolveObject(value: Node): Node | null;
|
|
196
261
|
/**
|
|
197
262
|
* For a value that is neither a function nor an object, such as a
|
|
@@ -239,10 +304,12 @@ declare class ResolutionStore {
|
|
|
239
304
|
*/
|
|
240
305
|
filesImportingTransitively(fileSets: ReadonlyArray<FileSetQuery>): ReadonlyArray<ReadonlySet<SourceFile>>;
|
|
241
306
|
/**
|
|
242
|
-
*
|
|
243
|
-
*
|
|
307
|
+
* Null keeps the wave walk widening; any candidate at all ends it,
|
|
308
|
+
* since more files can only add candidates, never take one away. The
|
|
309
|
+
* single-answer policy stays with the callers: picking one of two
|
|
310
|
+
* would make the result depend on the order the facts came in.
|
|
244
311
|
*/
|
|
245
|
-
private
|
|
312
|
+
private lookupSources;
|
|
246
313
|
private derive;
|
|
247
314
|
private answersFor;
|
|
248
315
|
/**
|
|
@@ -313,17 +380,19 @@ interface DiscoveredUnit {
|
|
|
313
380
|
* path.
|
|
314
381
|
*
|
|
315
382
|
* `schemaSdl` is the typeDefs string captured from the same
|
|
316
|
-
* ApolloServer config object the resolver map came from, when
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
383
|
+
* ApolloServer config object the resolver map came from, when we can
|
|
384
|
+
* statically resolve it (string literal or gql-tagged template,
|
|
385
|
+
* inline or const-bound). The adapter puts it on one summary
|
|
386
|
+
* standing for that schema, and `schemaDocument` is the label
|
|
387
|
+
* pointing every resolver at it.
|
|
321
388
|
*/
|
|
322
389
|
resolverInfo?: {
|
|
323
390
|
/** Null when the source never says which type the resolver attaches to. */
|
|
324
391
|
typeName: string | null;
|
|
325
392
|
fieldName: string;
|
|
326
393
|
schemaSdl?: string;
|
|
394
|
+
/** The file whose server config declares the schema. */
|
|
395
|
+
schemaDocument?: string;
|
|
327
396
|
};
|
|
328
397
|
/**
|
|
329
398
|
* Populated by `graphqlHookCall` discovery (GraphQL consumer side).
|
|
@@ -365,6 +434,8 @@ interface DiscoveredUnit {
|
|
|
365
434
|
required: boolean;
|
|
366
435
|
}>;
|
|
367
436
|
rootFields: string[];
|
|
437
|
+
/** Spreads in `document` with no definition in it: read partially. */
|
|
438
|
+
unresolvedFragments?: string[];
|
|
368
439
|
unresolved?: {
|
|
369
440
|
reference: string;
|
|
370
441
|
reason: string;
|
|
@@ -438,6 +509,12 @@ interface MountPrefixIndex {
|
|
|
438
509
|
* couldn't be resolved to a literal prefix and a concrete router.
|
|
439
510
|
*/
|
|
440
511
|
effectivePrefixFor(routerNode: Node): string;
|
|
512
|
+
/**
|
|
513
|
+
* The same resolution by a router's node id, for callers replaying a
|
|
514
|
+
* recorded consumption without the node in hand (the per-file cache
|
|
515
|
+
* re-checks a stored prefix against a rebuilt index this way).
|
|
516
|
+
*/
|
|
517
|
+
prefixForId?(childId: string): string;
|
|
441
518
|
}
|
|
442
519
|
|
|
443
520
|
/**
|
|
@@ -488,6 +565,11 @@ type DescentBarriers = ReadonlySet<Node>;
|
|
|
488
565
|
declare function extractCodeStructure(unit: DiscoveredUnit, pack: PatternPack, invocationRecognizers?: InvocationRecognizer[], accessRecognizers?: AccessRecognizer[], barriers?: DescentBarriers, resolveWrittenValue?: (value: Node) => Node | null): RawCodeStructure;
|
|
489
566
|
interface TypeScriptAdapterConfig {
|
|
490
567
|
tsConfigFilePath?: string;
|
|
568
|
+
/**
|
|
569
|
+
* Absolute. Every id's file path is relative to this directory. When
|
|
570
|
+
* absent, settled by workspaceRootFor from the tsconfig's directory.
|
|
571
|
+
*/
|
|
572
|
+
projectRoot?: string;
|
|
491
573
|
/** Separates two services in one repository whose file names match. */
|
|
492
574
|
workspace?: string;
|
|
493
575
|
project?: Project;
|
|
@@ -739,6 +821,71 @@ interface TsDiscoveryContext {
|
|
|
739
821
|
}
|
|
740
822
|
declare function createTsDiscoveryContext(resolution?: ResolutionStore): TsDiscoveryContext;
|
|
741
823
|
|
|
824
|
+
/**
|
|
825
|
+
* The session behind `suss ask why`: a ts-morph project over the
|
|
826
|
+
* asked-about source, a way to point at the expression somebody
|
|
827
|
+
* spelled, and the witness proof of what it resolved to, rendered
|
|
828
|
+
* through `@suss/resolution`'s phrases.
|
|
829
|
+
*
|
|
830
|
+
* The CLI points at values with a file, a line, and the text as
|
|
831
|
+
* written, because that is what a person has in hand. Everything
|
|
832
|
+
* ts-morph stays behind this class; callers treat the found nodes as
|
|
833
|
+
* opaque handles to pass back in.
|
|
834
|
+
*/
|
|
835
|
+
|
|
836
|
+
interface WhySessionOptions {
|
|
837
|
+
/** The project root, which paths in every answer come out relative to. */
|
|
838
|
+
dir: string;
|
|
839
|
+
}
|
|
840
|
+
/** A value or function said the way an answer prints it. */
|
|
841
|
+
interface ValueLocation {
|
|
842
|
+
name: string;
|
|
843
|
+
/** Relative to the session root when under it, absolute otherwise. */
|
|
844
|
+
file: string;
|
|
845
|
+
line: number;
|
|
846
|
+
}
|
|
847
|
+
interface WhyExplained {
|
|
848
|
+
explanation: ResolutionExplanation;
|
|
849
|
+
/** The chain's atoms, each said in source terms. */
|
|
850
|
+
chain: string[];
|
|
851
|
+
/** The chain and its reasons as printable lines. */
|
|
852
|
+
lines: string[];
|
|
853
|
+
target: ValueLocation;
|
|
854
|
+
stats: ExplainStats;
|
|
855
|
+
}
|
|
856
|
+
declare class WhySession {
|
|
857
|
+
private readonly project;
|
|
858
|
+
private readonly store;
|
|
859
|
+
private readonly root;
|
|
860
|
+
constructor(options: WhySessionOptions);
|
|
861
|
+
/**
|
|
862
|
+
* The smallest expression on `line` of `file` whose text is exactly
|
|
863
|
+
* `text`, or null. Spelled from the person's side: the name as
|
|
864
|
+
* written, not a node id.
|
|
865
|
+
*/
|
|
866
|
+
findExpression(file: string, line: number, text: string): Node | null;
|
|
867
|
+
/**
|
|
868
|
+
* The callee of the call written as `calleeText` between `startLine`
|
|
869
|
+
* and `endLine` of `file`, or null. This is how a summary's recorded
|
|
870
|
+
* callee gets back to its node: the summary keeps source text and a
|
|
871
|
+
* range, not ids.
|
|
872
|
+
*/
|
|
873
|
+
findCallee(file: string, startLine: number, endLine: number, calleeText: string): Node | null;
|
|
874
|
+
/**
|
|
875
|
+
* Why `value` resolves to what it does: the witness proof, flattened
|
|
876
|
+
* to the chain and rendered. Null when the value does not resolve,
|
|
877
|
+
* which the caller says in its own words.
|
|
878
|
+
*/
|
|
879
|
+
explain(value: Node, options?: {
|
|
880
|
+
maxDepth?: number;
|
|
881
|
+
}): WhyExplained | null;
|
|
882
|
+
/** Where a found node is, in the terms an answer prints. */
|
|
883
|
+
locate(node: Node): ValueLocation;
|
|
884
|
+
private sourceFileAt;
|
|
885
|
+
/** A module key: a file path said relative to the root, or a package name. */
|
|
886
|
+
private displayPath;
|
|
887
|
+
}
|
|
888
|
+
|
|
742
889
|
type ExportedDeclarationMap = ReturnType<SourceFile["getExportedDeclarations"]>;
|
|
743
890
|
/**
|
|
744
891
|
* What a module exports, by exported name. Cached, because ts-morph
|
|
@@ -832,8 +979,14 @@ declare function isImportedFrom(identifier: Node, expectedModule: string): boole
|
|
|
832
979
|
* asks this when the receiver came from somewhere the source does not
|
|
833
980
|
* spell out: `const redis = await this.getClient()` says nothing about
|
|
834
981
|
* ioredis, and the type of `redis.get` says everything.
|
|
982
|
+
*
|
|
983
|
+
* A client cached on an untyped global, `global.x || new Redis()`,
|
|
984
|
+
* types as `any`, so the method has no symbol. With `resolve` supplied,
|
|
985
|
+
* what the receiver was written as still says which library: the value
|
|
986
|
+
* comes down to a construction or a factory call of something imported
|
|
987
|
+
* from it.
|
|
835
988
|
*/
|
|
836
|
-
declare function methodDeclaredIn(callee: Node, expectedModule: string): boolean;
|
|
989
|
+
declare function methodDeclaredIn(callee: Node, expectedModule: string, resolve?: (value: Node) => Node | null): boolean;
|
|
837
990
|
|
|
838
991
|
/**
|
|
839
992
|
* What a name-valued expression says, as fixed text with a hole for
|
|
@@ -846,9 +999,8 @@ declare function methodDeclaredIn(callee: Node, expectedModule: string): boolean
|
|
|
846
999
|
interface ReadNameOptions {
|
|
847
1000
|
/**
|
|
848
1001
|
* What to say about a name nothing here settles. `"nothing"` gives
|
|
849
|
-
* null. `"reference"` gives
|
|
850
|
-
*
|
|
851
|
-
* about rather than which bucket this is.
|
|
1002
|
+
* null. `"reference"` gives `{location.bucket}`, which says which
|
|
1003
|
+
* value to go and ask about rather than which bucket this is.
|
|
852
1004
|
*/
|
|
853
1005
|
unsettled?: "nothing" | "reference";
|
|
854
1006
|
/**
|
|
@@ -961,6 +1113,37 @@ interface TsJsxAttributeLocation {
|
|
|
961
1113
|
}
|
|
962
1114
|
declare function createTsSubUnitContext(): TsSubUnitContext;
|
|
963
1115
|
|
|
1116
|
+
/**
|
|
1117
|
+
* Naming a summary, so that something else can point at it.
|
|
1118
|
+
*
|
|
1119
|
+
* A bare function name is not enough. Reading suss's own source turns up
|
|
1120
|
+
* 404 summaries that share a name with another one, and a large
|
|
1121
|
+
* repository is worse: two services with the same `src/handlers.ts` give
|
|
1122
|
+
* every summary in one an exact twin in the other. Following a call by
|
|
1123
|
+
* matching names was always a guess about which one was meant.
|
|
1124
|
+
*
|
|
1125
|
+
* So the name includes the project the summary came from, the file it is
|
|
1126
|
+
* in, and the path its export is reached by. That is unique across
|
|
1127
|
+
* everything one run can see, and it still looks like something a person
|
|
1128
|
+
* would have written down.
|
|
1129
|
+
*/
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* The one root a run measures its file paths from: the nearest
|
|
1133
|
+
* directory at or above the run's anchor whose package.json has a
|
|
1134
|
+
* name. The anchor is the tsconfig's directory, or the directory a
|
|
1135
|
+
* run without one was pointed at. When no package.json declares a
|
|
1136
|
+
* name above the anchor, the anchor itself is the root.
|
|
1137
|
+
*
|
|
1138
|
+
* The same directory supplies the workspace segment of an id, so both
|
|
1139
|
+
* parts of an id are measured from one place. The walk starts from the
|
|
1140
|
+
* run's configuration, not from whichever files the run loaded, so a
|
|
1141
|
+
* run over one nested tsconfig spells a file the same way a run over
|
|
1142
|
+
* the whole workspace does, and the same command spells it the same
|
|
1143
|
+
* way twice.
|
|
1144
|
+
*/
|
|
1145
|
+
declare function workspaceRootFor(anchor: string): string;
|
|
1146
|
+
|
|
964
1147
|
declare const ADAPTER_VERSION = "0.2.1";
|
|
965
1148
|
/**
|
|
966
1149
|
* Content hash of the given files, in the order they arrive. Empty when
|
|
@@ -992,4 +1175,4 @@ declare function computeAdapterPacksDigest(packVersions: ReadonlyArray<{
|
|
|
992
1175
|
version?: string;
|
|
993
1176
|
}>): string;
|
|
994
1177
|
|
|
995
|
-
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 };
|
|
1178
|
+
export { ADAPTER_VERSION, type CacheDiagnostic, type CacheLookup, type ClientCallSite, type ConfiguredCallContext, type ConfiguredCallRead, type ConfiguredCallSpec, type ContractReadResult, type DiscoveredUnit, type EmptyStage, type ExplainCallableOptions, type ExplainStats, type ExplainedResolution, 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, type ValueLocation, type WhyExplained, WhySession, type WhySessionOptions, 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, workspaceRootFor };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,44 @@
|
|
|
1
1
|
import { SourceFile, Node, FunctionDeclaration, FunctionExpression, ArrowFunction, MethodDeclaration, CallExpression, Project, Symbol, Expression } from 'ts-morph';
|
|
2
2
|
import { TransparentWrapper, RawCondition, DiscoveryPattern, TerminalPattern, InputMappingPattern, LanguageAdapter, PatternPack, ExtractorOptions, InvocationRecognizer, AccessRecognizer, RawCodeStructure, RawTerminal, RawBranch, EffectArg, RawDeclaredContract, ContractPattern, ResponsePropertyMapping } from '@suss/extractor';
|
|
3
3
|
import { BehavioralSummary, MessageBusSemantics, DeployableUnit, BoundaryBinding, Predicate, TypeShape, ValueRef } from '@suss/behavioral-ir';
|
|
4
|
+
import { Proof, Atom } from '@suss/datalog';
|
|
5
|
+
import { ResolutionExplanation } from '@suss/resolution';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* cache.ts: the on-disk extraction cache.
|
|
9
|
+
*
|
|
10
|
+
* A run reuses the previous one's summaries whole when nothing
|
|
11
|
+
* changed, and per file when some files did. The entry directory is
|
|
12
|
+
* named after everything that has to agree before any reuse is sound:
|
|
13
|
+
* schema version, adapter version and code hash, pack versions, the
|
|
14
|
+
* extraction config stamp and the tsconfig path. Inside it, the
|
|
15
|
+
* manifest records a stamp and content hash per file, each summary's
|
|
16
|
+
* owning files, and per owning file the other files its walk read, so
|
|
17
|
+
* a change to any of those re-extracts the owner instead of serving a
|
|
18
|
+
* stale answer. The full design, including what per-file reuse can
|
|
19
|
+
* and cannot promise, is in this package's README.
|
|
20
|
+
*/
|
|
4
21
|
|
|
5
22
|
/** Reported by `lookup`: what the cache decided, and why. */
|
|
6
23
|
interface CacheDiagnostic {
|
|
7
|
-
kind: "hit" | "miss";
|
|
24
|
+
kind: "hit" | "miss" | "partial";
|
|
8
25
|
/**
|
|
9
26
|
* Reason the lookup missed (only set when kind === "miss").
|
|
10
|
-
* `key-changed` means the cache directory
|
|
27
|
+
* `key-changed` means the cache directory contains entries, but none
|
|
11
28
|
* under this run's schema, adapter, packs and tsconfig path.
|
|
12
29
|
* `files-changed` means the include set is not the one the entry
|
|
13
30
|
* was written from.
|
|
14
31
|
*/
|
|
15
32
|
missReason?: "no-manifest" | "key-changed" | "tsconfig-changed" | "files-changed";
|
|
33
|
+
/** Set when kind === "partial". */
|
|
34
|
+
partial?: {
|
|
35
|
+
filesChanged: number;
|
|
36
|
+
filesRemoved: number;
|
|
37
|
+
rootsReused: number;
|
|
38
|
+
rootsReextracted: number;
|
|
39
|
+
rootsDeclined: number;
|
|
40
|
+
summariesReused: number;
|
|
41
|
+
};
|
|
16
42
|
}
|
|
17
43
|
/**
|
|
18
44
|
* Result of a cache lookup. A hit gives back the whole summary set; a miss
|
|
@@ -174,12 +200,36 @@ interface FileSetQuery {
|
|
|
174
200
|
* edges a query follows, so cost tracks how indirect the code is.
|
|
175
201
|
*/
|
|
176
202
|
|
|
203
|
+
interface ExplainCallableOptions {
|
|
204
|
+
/** A second file to walk out from; see `resolveCallable`. */
|
|
205
|
+
alsoFrom?: SourceFile;
|
|
206
|
+
/** How many derived nodes deep the proof walk goes; see `proofOf`. */
|
|
207
|
+
maxDepth?: number;
|
|
208
|
+
}
|
|
209
|
+
/** What one witness re-evaluation cost, said rather than hidden. */
|
|
210
|
+
interface ExplainStats {
|
|
211
|
+
/** Facts the demand walk had extracted, which the proof pass reran. */
|
|
212
|
+
baseFacts: number;
|
|
213
|
+
/** Facts the exhaustive pass derived on top of those. */
|
|
214
|
+
derivedFacts: number;
|
|
215
|
+
evaluateMs: number;
|
|
216
|
+
}
|
|
217
|
+
interface ExplainedResolution {
|
|
218
|
+
/** The node the value comes down to. */
|
|
219
|
+
target: Node;
|
|
220
|
+
proof: Proof;
|
|
221
|
+
/** The source node behind a proof atom, when the atom is a node id. */
|
|
222
|
+
nodeFor: (atom: Atom) => Node | undefined;
|
|
223
|
+
stats: ExplainStats;
|
|
224
|
+
}
|
|
177
225
|
declare class ResolutionStore {
|
|
178
226
|
private readonly db;
|
|
179
227
|
private readonly table;
|
|
180
228
|
private readonly fullyExtracted;
|
|
181
229
|
private readonly seededValues;
|
|
182
230
|
private readonly importedNames;
|
|
231
|
+
/** Files the most recent wave walk entered, for the memo to keep. */
|
|
232
|
+
private lastQueryWalked;
|
|
183
233
|
private readonly declarations;
|
|
184
234
|
private readonly graph;
|
|
185
235
|
private stale;
|
|
@@ -192,6 +242,21 @@ declare class ResolutionStore {
|
|
|
192
242
|
* the walk never reaches on its own.
|
|
193
243
|
*/
|
|
194
244
|
resolveCallable(value: Node, alsoFrom?: SourceFile): Node | null;
|
|
245
|
+
/**
|
|
246
|
+
* Every function the value comes down to. One is the answer
|
|
247
|
+
* `resolveCallable` gives; several is a value with more than one
|
|
248
|
+
* possible source, which a caller says at the site rather than
|
|
249
|
+
* folding into the same nothing as a chain that went nowhere.
|
|
250
|
+
*/
|
|
251
|
+
resolveCallableSources(value: Node, alsoFrom?: SourceFile): Node[];
|
|
252
|
+
/**
|
|
253
|
+
* The witness proof behind `resolveCallable`'s answer. Resolves the
|
|
254
|
+
* value first, which walks files and extracts facts in the usual
|
|
255
|
+
* waves, then re-evaluates the rules over those base facts under the
|
|
256
|
+
* witness algebra and rebuilds the proof of the answer. Null when
|
|
257
|
+
* the value does not resolve at all.
|
|
258
|
+
*/
|
|
259
|
+
explainCallable(value: Node, options?: ExplainCallableOptions): ExplainedResolution | null;
|
|
195
260
|
resolveObject(value: Node): Node | null;
|
|
196
261
|
/**
|
|
197
262
|
* For a value that is neither a function nor an object, such as a
|
|
@@ -239,10 +304,12 @@ declare class ResolutionStore {
|
|
|
239
304
|
*/
|
|
240
305
|
filesImportingTransitively(fileSets: ReadonlyArray<FileSetQuery>): ReadonlyArray<ReadonlySet<SourceFile>>;
|
|
241
306
|
/**
|
|
242
|
-
*
|
|
243
|
-
*
|
|
307
|
+
* Null keeps the wave walk widening; any candidate at all ends it,
|
|
308
|
+
* since more files can only add candidates, never take one away. The
|
|
309
|
+
* single-answer policy stays with the callers: picking one of two
|
|
310
|
+
* would make the result depend on the order the facts came in.
|
|
244
311
|
*/
|
|
245
|
-
private
|
|
312
|
+
private lookupSources;
|
|
246
313
|
private derive;
|
|
247
314
|
private answersFor;
|
|
248
315
|
/**
|
|
@@ -313,17 +380,19 @@ interface DiscoveredUnit {
|
|
|
313
380
|
* path.
|
|
314
381
|
*
|
|
315
382
|
* `schemaSdl` is the typeDefs string captured from the same
|
|
316
|
-
* ApolloServer config object the resolver map came from, when
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
383
|
+
* ApolloServer config object the resolver map came from, when we can
|
|
384
|
+
* statically resolve it (string literal or gql-tagged template,
|
|
385
|
+
* inline or const-bound). The adapter puts it on one summary
|
|
386
|
+
* standing for that schema, and `schemaDocument` is the label
|
|
387
|
+
* pointing every resolver at it.
|
|
321
388
|
*/
|
|
322
389
|
resolverInfo?: {
|
|
323
390
|
/** Null when the source never says which type the resolver attaches to. */
|
|
324
391
|
typeName: string | null;
|
|
325
392
|
fieldName: string;
|
|
326
393
|
schemaSdl?: string;
|
|
394
|
+
/** The file whose server config declares the schema. */
|
|
395
|
+
schemaDocument?: string;
|
|
327
396
|
};
|
|
328
397
|
/**
|
|
329
398
|
* Populated by `graphqlHookCall` discovery (GraphQL consumer side).
|
|
@@ -365,6 +434,8 @@ interface DiscoveredUnit {
|
|
|
365
434
|
required: boolean;
|
|
366
435
|
}>;
|
|
367
436
|
rootFields: string[];
|
|
437
|
+
/** Spreads in `document` with no definition in it: read partially. */
|
|
438
|
+
unresolvedFragments?: string[];
|
|
368
439
|
unresolved?: {
|
|
369
440
|
reference: string;
|
|
370
441
|
reason: string;
|
|
@@ -438,6 +509,12 @@ interface MountPrefixIndex {
|
|
|
438
509
|
* couldn't be resolved to a literal prefix and a concrete router.
|
|
439
510
|
*/
|
|
440
511
|
effectivePrefixFor(routerNode: Node): string;
|
|
512
|
+
/**
|
|
513
|
+
* The same resolution by a router's node id, for callers replaying a
|
|
514
|
+
* recorded consumption without the node in hand (the per-file cache
|
|
515
|
+
* re-checks a stored prefix against a rebuilt index this way).
|
|
516
|
+
*/
|
|
517
|
+
prefixForId?(childId: string): string;
|
|
441
518
|
}
|
|
442
519
|
|
|
443
520
|
/**
|
|
@@ -488,6 +565,11 @@ type DescentBarriers = ReadonlySet<Node>;
|
|
|
488
565
|
declare function extractCodeStructure(unit: DiscoveredUnit, pack: PatternPack, invocationRecognizers?: InvocationRecognizer[], accessRecognizers?: AccessRecognizer[], barriers?: DescentBarriers, resolveWrittenValue?: (value: Node) => Node | null): RawCodeStructure;
|
|
489
566
|
interface TypeScriptAdapterConfig {
|
|
490
567
|
tsConfigFilePath?: string;
|
|
568
|
+
/**
|
|
569
|
+
* Absolute. Every id's file path is relative to this directory. When
|
|
570
|
+
* absent, settled by workspaceRootFor from the tsconfig's directory.
|
|
571
|
+
*/
|
|
572
|
+
projectRoot?: string;
|
|
491
573
|
/** Separates two services in one repository whose file names match. */
|
|
492
574
|
workspace?: string;
|
|
493
575
|
project?: Project;
|
|
@@ -739,6 +821,71 @@ interface TsDiscoveryContext {
|
|
|
739
821
|
}
|
|
740
822
|
declare function createTsDiscoveryContext(resolution?: ResolutionStore): TsDiscoveryContext;
|
|
741
823
|
|
|
824
|
+
/**
|
|
825
|
+
* The session behind `suss ask why`: a ts-morph project over the
|
|
826
|
+
* asked-about source, a way to point at the expression somebody
|
|
827
|
+
* spelled, and the witness proof of what it resolved to, rendered
|
|
828
|
+
* through `@suss/resolution`'s phrases.
|
|
829
|
+
*
|
|
830
|
+
* The CLI points at values with a file, a line, and the text as
|
|
831
|
+
* written, because that is what a person has in hand. Everything
|
|
832
|
+
* ts-morph stays behind this class; callers treat the found nodes as
|
|
833
|
+
* opaque handles to pass back in.
|
|
834
|
+
*/
|
|
835
|
+
|
|
836
|
+
interface WhySessionOptions {
|
|
837
|
+
/** The project root, which paths in every answer come out relative to. */
|
|
838
|
+
dir: string;
|
|
839
|
+
}
|
|
840
|
+
/** A value or function said the way an answer prints it. */
|
|
841
|
+
interface ValueLocation {
|
|
842
|
+
name: string;
|
|
843
|
+
/** Relative to the session root when under it, absolute otherwise. */
|
|
844
|
+
file: string;
|
|
845
|
+
line: number;
|
|
846
|
+
}
|
|
847
|
+
interface WhyExplained {
|
|
848
|
+
explanation: ResolutionExplanation;
|
|
849
|
+
/** The chain's atoms, each said in source terms. */
|
|
850
|
+
chain: string[];
|
|
851
|
+
/** The chain and its reasons as printable lines. */
|
|
852
|
+
lines: string[];
|
|
853
|
+
target: ValueLocation;
|
|
854
|
+
stats: ExplainStats;
|
|
855
|
+
}
|
|
856
|
+
declare class WhySession {
|
|
857
|
+
private readonly project;
|
|
858
|
+
private readonly store;
|
|
859
|
+
private readonly root;
|
|
860
|
+
constructor(options: WhySessionOptions);
|
|
861
|
+
/**
|
|
862
|
+
* The smallest expression on `line` of `file` whose text is exactly
|
|
863
|
+
* `text`, or null. Spelled from the person's side: the name as
|
|
864
|
+
* written, not a node id.
|
|
865
|
+
*/
|
|
866
|
+
findExpression(file: string, line: number, text: string): Node | null;
|
|
867
|
+
/**
|
|
868
|
+
* The callee of the call written as `calleeText` between `startLine`
|
|
869
|
+
* and `endLine` of `file`, or null. This is how a summary's recorded
|
|
870
|
+
* callee gets back to its node: the summary keeps source text and a
|
|
871
|
+
* range, not ids.
|
|
872
|
+
*/
|
|
873
|
+
findCallee(file: string, startLine: number, endLine: number, calleeText: string): Node | null;
|
|
874
|
+
/**
|
|
875
|
+
* Why `value` resolves to what it does: the witness proof, flattened
|
|
876
|
+
* to the chain and rendered. Null when the value does not resolve,
|
|
877
|
+
* which the caller says in its own words.
|
|
878
|
+
*/
|
|
879
|
+
explain(value: Node, options?: {
|
|
880
|
+
maxDepth?: number;
|
|
881
|
+
}): WhyExplained | null;
|
|
882
|
+
/** Where a found node is, in the terms an answer prints. */
|
|
883
|
+
locate(node: Node): ValueLocation;
|
|
884
|
+
private sourceFileAt;
|
|
885
|
+
/** A module key: a file path said relative to the root, or a package name. */
|
|
886
|
+
private displayPath;
|
|
887
|
+
}
|
|
888
|
+
|
|
742
889
|
type ExportedDeclarationMap = ReturnType<SourceFile["getExportedDeclarations"]>;
|
|
743
890
|
/**
|
|
744
891
|
* What a module exports, by exported name. Cached, because ts-morph
|
|
@@ -832,8 +979,14 @@ declare function isImportedFrom(identifier: Node, expectedModule: string): boole
|
|
|
832
979
|
* asks this when the receiver came from somewhere the source does not
|
|
833
980
|
* spell out: `const redis = await this.getClient()` says nothing about
|
|
834
981
|
* ioredis, and the type of `redis.get` says everything.
|
|
982
|
+
*
|
|
983
|
+
* A client cached on an untyped global, `global.x || new Redis()`,
|
|
984
|
+
* types as `any`, so the method has no symbol. With `resolve` supplied,
|
|
985
|
+
* what the receiver was written as still says which library: the value
|
|
986
|
+
* comes down to a construction or a factory call of something imported
|
|
987
|
+
* from it.
|
|
835
988
|
*/
|
|
836
|
-
declare function methodDeclaredIn(callee: Node, expectedModule: string): boolean;
|
|
989
|
+
declare function methodDeclaredIn(callee: Node, expectedModule: string, resolve?: (value: Node) => Node | null): boolean;
|
|
837
990
|
|
|
838
991
|
/**
|
|
839
992
|
* What a name-valued expression says, as fixed text with a hole for
|
|
@@ -846,9 +999,8 @@ declare function methodDeclaredIn(callee: Node, expectedModule: string): boolean
|
|
|
846
999
|
interface ReadNameOptions {
|
|
847
1000
|
/**
|
|
848
1001
|
* What to say about a name nothing here settles. `"nothing"` gives
|
|
849
|
-
* null. `"reference"` gives
|
|
850
|
-
*
|
|
851
|
-
* about rather than which bucket this is.
|
|
1002
|
+
* null. `"reference"` gives `{location.bucket}`, which says which
|
|
1003
|
+
* value to go and ask about rather than which bucket this is.
|
|
852
1004
|
*/
|
|
853
1005
|
unsettled?: "nothing" | "reference";
|
|
854
1006
|
/**
|
|
@@ -961,6 +1113,37 @@ interface TsJsxAttributeLocation {
|
|
|
961
1113
|
}
|
|
962
1114
|
declare function createTsSubUnitContext(): TsSubUnitContext;
|
|
963
1115
|
|
|
1116
|
+
/**
|
|
1117
|
+
* Naming a summary, so that something else can point at it.
|
|
1118
|
+
*
|
|
1119
|
+
* A bare function name is not enough. Reading suss's own source turns up
|
|
1120
|
+
* 404 summaries that share a name with another one, and a large
|
|
1121
|
+
* repository is worse: two services with the same `src/handlers.ts` give
|
|
1122
|
+
* every summary in one an exact twin in the other. Following a call by
|
|
1123
|
+
* matching names was always a guess about which one was meant.
|
|
1124
|
+
*
|
|
1125
|
+
* So the name includes the project the summary came from, the file it is
|
|
1126
|
+
* in, and the path its export is reached by. That is unique across
|
|
1127
|
+
* everything one run can see, and it still looks like something a person
|
|
1128
|
+
* would have written down.
|
|
1129
|
+
*/
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* The one root a run measures its file paths from: the nearest
|
|
1133
|
+
* directory at or above the run's anchor whose package.json has a
|
|
1134
|
+
* name. The anchor is the tsconfig's directory, or the directory a
|
|
1135
|
+
* run without one was pointed at. When no package.json declares a
|
|
1136
|
+
* name above the anchor, the anchor itself is the root.
|
|
1137
|
+
*
|
|
1138
|
+
* The same directory supplies the workspace segment of an id, so both
|
|
1139
|
+
* parts of an id are measured from one place. The walk starts from the
|
|
1140
|
+
* run's configuration, not from whichever files the run loaded, so a
|
|
1141
|
+
* run over one nested tsconfig spells a file the same way a run over
|
|
1142
|
+
* the whole workspace does, and the same command spells it the same
|
|
1143
|
+
* way twice.
|
|
1144
|
+
*/
|
|
1145
|
+
declare function workspaceRootFor(anchor: string): string;
|
|
1146
|
+
|
|
964
1147
|
declare const ADAPTER_VERSION = "0.2.1";
|
|
965
1148
|
/**
|
|
966
1149
|
* Content hash of the given files, in the order they arrive. Empty when
|
|
@@ -992,4 +1175,4 @@ declare function computeAdapterPacksDigest(packVersions: ReadonlyArray<{
|
|
|
992
1175
|
version?: string;
|
|
993
1176
|
}>): string;
|
|
994
1177
|
|
|
995
|
-
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 };
|
|
1178
|
+
export { ADAPTER_VERSION, type CacheDiagnostic, type CacheLookup, type ClientCallSite, type ConfiguredCallContext, type ConfiguredCallRead, type ConfiguredCallSpec, type ContractReadResult, type DiscoveredUnit, type EmptyStage, type ExplainCallableOptions, type ExplainStats, type ExplainedResolution, 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, type ValueLocation, type WhyExplained, WhySession, type WhySessionOptions, 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, workspaceRootFor };
|