@suss/adapter-typescript 0.3.2 → 0.5.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 +25 -3
- package/dist/index.cjs +6723 -4758
- package/dist/index.d.cts +267 -265
- package/dist/index.d.ts +267 -265
- package/dist/index.js +7962 -5990
- package/package.json +6 -5
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SourceFile, Node, FunctionDeclaration, FunctionExpression, ArrowFunction, MethodDeclaration, CallExpression, Project, Expression } from 'ts-morph';
|
|
2
|
-
import { TransparentWrapper, RawCondition, DiscoveryPattern, TerminalPattern, InputMappingPattern, PatternPack, ExtractorOptions, InvocationRecognizer, AccessRecognizer, RawCodeStructure, RawTerminal, RawBranch, EffectArg, RawDeclaredContract, ContractPattern, ResponsePropertyMapping } from '@suss/extractor';
|
|
1
|
+
import { SourceFile, Node, FunctionDeclaration, FunctionExpression, ArrowFunction, MethodDeclaration, CallExpression, Project, Symbol, Expression } from 'ts-morph';
|
|
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
4
|
|
|
5
5
|
/** Reported by `lookup`: what the cache decided, and why. */
|
|
@@ -15,7 +15,7 @@ interface CacheDiagnostic {
|
|
|
15
15
|
missReason?: "no-manifest" | "key-changed" | "tsconfig-changed" | "files-changed";
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Result of a cache lookup. A hit
|
|
18
|
+
* Result of a cache lookup. A hit gives back the whole summary set; a miss
|
|
19
19
|
* says why, so a caller can render the reason.
|
|
20
20
|
*/
|
|
21
21
|
type CacheLookup = {
|
|
@@ -33,18 +33,18 @@ interface PackFunnel {
|
|
|
33
33
|
/**
|
|
34
34
|
* What the pack calls this build of itself, or null when it declares
|
|
35
35
|
* nothing. The cache keys on it, so a pack that never changes its
|
|
36
|
-
* stamp can
|
|
36
|
+
* stamp can serve a later run with an earlier build's results.
|
|
37
37
|
*/
|
|
38
38
|
version: string | null;
|
|
39
39
|
/**
|
|
40
|
-
* Whether the pack looks for units of its own at all. A pack made
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
40
|
+
* Whether the pack looks for units of its own at all. A pack made only
|
|
41
|
+
* of recognisers contributes effects to units other packs found, so it
|
|
42
|
+
* always discovers nothing, and that tells you nothing about whether
|
|
43
|
+
* it is working.
|
|
44
44
|
*/
|
|
45
45
|
discovers: boolean;
|
|
46
46
|
/**
|
|
47
|
-
* Whether the pack
|
|
47
|
+
* Whether the pack has any recognisers. Those fire inside units other
|
|
48
48
|
* packs discovered, so a pack made only of them contributes effects
|
|
49
49
|
* and never a summary.
|
|
50
50
|
*/
|
|
@@ -70,8 +70,8 @@ interface PackFunnel {
|
|
|
70
70
|
* Unit bodies any pack walked in the files this pack's gate selected.
|
|
71
71
|
*
|
|
72
72
|
* This is what a recogniser pack had the chance to fire on. Its own
|
|
73
|
-
* discovery count
|
|
74
|
-
* and its candidate-file count
|
|
73
|
+
* discovery count tells you nothing, since it discovers nothing by
|
|
74
|
+
* design, and neither does its candidate-file count, because a
|
|
75
75
|
* recogniser only runs where some pack found a unit to walk.
|
|
76
76
|
*/
|
|
77
77
|
unitsInGatedFiles: number;
|
|
@@ -80,7 +80,7 @@ interface PackFunnel {
|
|
|
80
80
|
/**
|
|
81
81
|
* Units this pack kept. A unit an earlier pack already claimed is
|
|
82
82
|
* dropped here, so a pack can discover plenty and keep none when it
|
|
83
|
-
*
|
|
83
|
+
* comes after a pack that recognises the same code.
|
|
84
84
|
*/
|
|
85
85
|
unitsClaimed: number;
|
|
86
86
|
/**
|
|
@@ -92,19 +92,19 @@ interface PackFunnel {
|
|
|
92
92
|
selfCollisions: number;
|
|
93
93
|
/** Summaries built from those units. */
|
|
94
94
|
summariesProduced: number;
|
|
95
|
-
/** Summaries in the finished run that
|
|
95
|
+
/** Summaries in the finished run that credit this pack for finding them. */
|
|
96
96
|
summariesBound: number;
|
|
97
97
|
/**
|
|
98
98
|
* Bound summaries on the provider side of their boundary.
|
|
99
99
|
*
|
|
100
|
-
* Transitions say what a unit does with a request, which
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
100
|
+
* Transitions say what a unit does with a request, which only a
|
|
101
|
+
* provider can say. A consumer records what it reads back from the
|
|
102
|
+
* response instead, and that goes on the summary's metadata. Measuring
|
|
103
|
+
* a client pack against transitions would report every working one as
|
|
104
|
+
* having extracted nothing.
|
|
105
105
|
*/
|
|
106
106
|
providerSummaries: number;
|
|
107
|
-
/** Provider summaries
|
|
107
|
+
/** Provider summaries with at least one transition. */
|
|
108
108
|
summariesWithBehavior: number;
|
|
109
109
|
/**
|
|
110
110
|
* Where one of this pack's hooks threw. Every count above is a floor
|
|
@@ -119,6 +119,17 @@ interface ExtractionReport {
|
|
|
119
119
|
filesWalked: number;
|
|
120
120
|
packs: PackFunnel[];
|
|
121
121
|
summaries: number;
|
|
122
|
+
/**
|
|
123
|
+
* Files whose exports the checker could not follow, so the run read
|
|
124
|
+
* them as exporting nothing.
|
|
125
|
+
*
|
|
126
|
+
* Without this the artifact cannot tell the two apart: a module whose
|
|
127
|
+
* barrel chain outran the call stack and a module that really does
|
|
128
|
+
* export nothing both produce no summaries and exit 0.
|
|
129
|
+
* Anything reachable only through these files is missing, and every
|
|
130
|
+
* count below is a floor while this is non-empty.
|
|
131
|
+
*/
|
|
132
|
+
filesWithUnreadableExports: string[];
|
|
122
133
|
/**
|
|
123
134
|
* The first stage whose count was zero, when the run produced
|
|
124
135
|
* nothing. Null when the run produced summaries.
|
|
@@ -129,13 +140,13 @@ type EmptyStage = "tsconfig" | "gateResolution" | "candidateFiles" | "discovery"
|
|
|
129
140
|
/**
|
|
130
141
|
* A pack's hook throwing on one file.
|
|
131
142
|
*
|
|
132
|
-
* The run
|
|
143
|
+
* The run continues with the other files, so every count for that pack
|
|
133
144
|
* afterwards is a floor rather than a total. Somebody reading those
|
|
134
|
-
* counts has to be told, or a pack that broke
|
|
145
|
+
* counts has to be told, or a pack that broke looks the same as a pack
|
|
135
146
|
* that looked and found nothing.
|
|
136
147
|
*/
|
|
137
148
|
interface PackFailure {
|
|
138
|
-
/** The hook that threw,
|
|
149
|
+
/** The hook that threw, called what a pack author would call it. */
|
|
139
150
|
hook: string;
|
|
140
151
|
/** The file the pack was reading. */
|
|
141
152
|
file: string;
|
|
@@ -148,147 +159,98 @@ interface FileSetQuery {
|
|
|
148
159
|
packages: ReadonlyArray<string>;
|
|
149
160
|
}
|
|
150
161
|
|
|
162
|
+
/**
|
|
163
|
+
* The resolution store, and the three questions callers ask it.
|
|
164
|
+
*
|
|
165
|
+
* `resolveCallable` says which function a value comes down to, through
|
|
166
|
+
* any depth of aliasing, imports, re-export barrels, wrapper factories
|
|
167
|
+
* and `.bind`. `resolveWrittenValue` says which expression a value is
|
|
168
|
+
* written as, for callers chasing something that is neither a function
|
|
169
|
+
* nor an object. `filesImportingTransitively` says which of a set of
|
|
170
|
+
* files reach any of a set of packages, which a per-file import check
|
|
171
|
+
* misses whenever a local barrel re-exports the SDK.
|
|
172
|
+
*
|
|
173
|
+
* Facts are extracted per file on demand and only along the module
|
|
174
|
+
* edges a query follows, so cost tracks how indirect the code is.
|
|
175
|
+
*/
|
|
176
|
+
|
|
151
177
|
declare class ResolutionStore {
|
|
152
178
|
private readonly db;
|
|
153
179
|
private readonly table;
|
|
154
180
|
private readonly fullyExtracted;
|
|
155
181
|
private readonly seededValues;
|
|
156
182
|
private readonly importedNames;
|
|
183
|
+
private readonly declarations;
|
|
157
184
|
private readonly graph;
|
|
158
185
|
private stale;
|
|
159
186
|
constructor(wrappers?: TransparentWrapper[]);
|
|
160
|
-
/**
|
|
161
|
-
* The function `value` resolves to, or null when no chain reaches
|
|
162
|
-
* one. The result is a ts-morph node in whatever file the function
|
|
163
|
-
* actually lives.
|
|
164
|
-
*
|
|
165
|
-
* Facts come in waves: extract the file the value lives in, ask, and
|
|
166
|
-
* only widen to the files it imports when the answer is still
|
|
167
|
-
* missing. A value that resolves without leaving its own file costs
|
|
168
|
-
* one file of extraction, not the whole import closure.
|
|
169
|
-
*/
|
|
170
187
|
resolveCallable(value: Node): Node | null;
|
|
171
|
-
/**
|
|
172
|
-
* The object literal `value` ends up being, or null. The route a
|
|
173
|
-
* registration call names often lives on a shared object built in
|
|
174
|
-
* another file, and this is how discovery reads it back.
|
|
175
|
-
*/
|
|
176
188
|
resolveObject(value: Node): Node | null;
|
|
177
189
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* on, so it answers for values that are neither functions nor
|
|
181
|
-
* objects: a GraphQL document held in a named constant in another
|
|
182
|
-
* file comes back as the template literal or tag call that built it.
|
|
183
|
-
*
|
|
184
|
-
* A value the code computes has no written form to report, so the
|
|
185
|
-
* answer is the computing expression itself and the caller gets
|
|
186
|
-
* nothing it can use, which is the point.
|
|
190
|
+
* For a value that is neither a function nor an object, such as a
|
|
191
|
+
* GraphQL document kept in a constant in another file.
|
|
187
192
|
*/
|
|
188
193
|
resolveWrittenValue(value: Node): Node | null;
|
|
189
194
|
/**
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* - it is that name, under whatever local spelling. `import
|
|
195
|
-
* { Controller as Resource }` makes `Resource` the library's
|
|
196
|
-
* `Controller`.
|
|
197
|
-
* - calling it calls that name. A project decorator written as
|
|
198
|
-
* `(path) => Controller(path)` marks a controller, and so does
|
|
199
|
-
* one written as `applyDecorators(Controller(path))`.
|
|
200
|
-
*
|
|
201
|
-
* Several names is the normal answer rather than an ambiguity, since
|
|
202
|
-
* a wrapper composing two library decorators applies both. The caller
|
|
203
|
-
* asks whether the name it cares about is among them.
|
|
204
|
-
*
|
|
205
|
-
* One wrapper is applied across hundreds of files, so the answer is
|
|
206
|
-
* memoized against the declaration it was asked about. Without that,
|
|
207
|
-
* every use site seeds a fresh value and pays another fixpoint.
|
|
195
|
+
* Which names from `modules` this value comes down to, either by being
|
|
196
|
+
* one of them under a local name or by calling into one. Getting
|
|
197
|
+
* several back is normal, since a wrapper can compose two library
|
|
198
|
+
* decorators.
|
|
208
199
|
*/
|
|
209
200
|
importedNamesOf(value: Node, modules: string[]): string[];
|
|
210
201
|
/**
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* The distinction is what keeps the cost down. A value that has
|
|
215
|
-
* arrived at a package has nothing more to give, because a library's
|
|
216
|
-
* own body is not here to read, so a decorator from a library the
|
|
217
|
-
* caller did not ask about stops on the first wave rather than
|
|
218
|
-
* running out the import closure looking for a match it will never
|
|
219
|
-
* find. A value that has only arrived at another file in the project
|
|
220
|
-
* keeps going, since the wrapper it names is declared somewhere the
|
|
221
|
-
* walk has not reached yet.
|
|
202
|
+
* Null tells the walk to keep widening. A value that landed in a
|
|
203
|
+
* package returns empty instead, because a library's own body is not
|
|
204
|
+
* here to read and widening would never find anything.
|
|
222
205
|
*/
|
|
223
206
|
private lookupImportedNames;
|
|
224
207
|
private resolveByWaves;
|
|
225
208
|
private walkForAnswer;
|
|
226
209
|
/**
|
|
227
|
-
* The
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
* Nothing is owed after this. The rules have seen every fact the
|
|
232
|
-
* database holds and concluded what the demand still present asks
|
|
233
|
-
* for, which is none, so the next query starts from its own asking
|
|
234
|
-
* fact.
|
|
210
|
+
* The question and the chain that settled it are dropped. The files
|
|
211
|
+
* the query extracted stay, since the next query reads the same facts.
|
|
212
|
+
* Nothing is left half-done, so the store is not stale afterwards.
|
|
235
213
|
*/
|
|
236
214
|
private forgetQuery;
|
|
237
|
-
/**
|
|
238
|
-
* Somebody asked about this value, which is what the rules follow
|
|
239
|
-
* chains for. Separate from `seedValue`, which only speaks for values
|
|
240
|
-
* that are expressions; a caller can ask about a declaration too, and
|
|
241
|
-
* the answer is still owed.
|
|
242
|
-
*/
|
|
243
215
|
private wantValue;
|
|
244
216
|
/**
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
* a registration call passes `routes.provision` as an argument, and
|
|
248
|
-
* nothing else ever emits facts for that expression.
|
|
217
|
+
* File extraction only reaches values hanging off exports, and a query
|
|
218
|
+
* can be rooted anywhere, so the queried value gets its own facts.
|
|
249
219
|
*/
|
|
250
220
|
private seedValue;
|
|
251
221
|
private lookupObject;
|
|
252
222
|
/**
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
* itself is not an answer, or every identifier would answer with
|
|
257
|
-
* itself the moment its chain went nowhere.
|
|
223
|
+
* Two candidates give null, since ambiguity is nothing. The value
|
|
224
|
+
* itself is never a result either, or an identifier whose chain went
|
|
225
|
+
* nowhere would come back as itself.
|
|
258
226
|
*/
|
|
259
227
|
private lookupWritten;
|
|
260
228
|
/**
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
229
|
+
* Which of these files reach any of these packages, following
|
|
230
|
+
* project-local re-export chains. A barrel package that re-exports an
|
|
231
|
+
* SDK defeats a per-file import check and does not defeat this.
|
|
264
232
|
*/
|
|
265
233
|
filesImportingTransitively(fileSets: ReadonlyArray<FileSetQuery>): ReadonlyArray<ReadonlySet<SourceFile>>;
|
|
266
234
|
/**
|
|
267
|
-
*
|
|
268
|
-
* the
|
|
269
|
-
* functions means the rules cannot tell which one the value is, and
|
|
270
|
-
* picking whichever landed in the relation first would make the
|
|
271
|
-
* answer depend on the order facts arrived in. Ambiguity is nothing.
|
|
235
|
+
* Two candidates give null. Picking whichever arrived first would make
|
|
236
|
+
* the result depend on the order the facts came in.
|
|
272
237
|
*/
|
|
273
238
|
private lookup;
|
|
274
|
-
/** Run the rules to fixpoint. */
|
|
275
239
|
private derive;
|
|
240
|
+
private answersFor;
|
|
276
241
|
/**
|
|
277
|
-
* The
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
242
|
+
* The trailing pair of a three-column relation, joined so a Set dedupes
|
|
243
|
+
* on both halves. The join uses the database's own tuple encoding,
|
|
244
|
+
* since a specifier or an identifier can contain any separator
|
|
245
|
+
* character.
|
|
281
246
|
*/
|
|
282
|
-
private answersFor;
|
|
283
|
-
/** The trailing pair of a three-column answer relation, joined. */
|
|
284
247
|
private answerPairsFor;
|
|
285
|
-
/** Emit a file's facts, unless some earlier query already did. */
|
|
286
248
|
private extractFile;
|
|
287
249
|
}
|
|
288
250
|
|
|
289
251
|
type FunctionRoot = FunctionDeclaration | FunctionExpression | ArrowFunction | MethodDeclaration;
|
|
290
252
|
/**
|
|
291
|
-
* Public API
|
|
253
|
+
* Public API: returns RawCondition[] with structured: null.
|
|
292
254
|
* Use collectAncestorConditionInfos when you need the Expression nodes.
|
|
293
255
|
*/
|
|
294
256
|
declare function collectAncestorBranches(terminalNode: Node, functionRoot: FunctionRoot): RawCondition[];
|
|
@@ -313,6 +275,12 @@ interface DiscoveredUnit {
|
|
|
313
275
|
announcedAt?: Node;
|
|
314
276
|
kind: string;
|
|
315
277
|
name: string;
|
|
278
|
+
/**
|
|
279
|
+
* "label" when the name was coined for the reader rather than
|
|
280
|
+
* naming a binding other code can call, the way a registration
|
|
281
|
+
* verb identifies its handler. Absent means the name is a binding.
|
|
282
|
+
*/
|
|
283
|
+
nameKind?: "binding" | "label";
|
|
316
284
|
callSite?: ClientCallSite;
|
|
317
285
|
/** The discovery pattern that produced this unit. Set by discoverUnits. */
|
|
318
286
|
pattern?: DiscoveryPattern;
|
|
@@ -345,13 +313,14 @@ interface DiscoveredUnit {
|
|
|
345
313
|
* fields without needing a separate schema provenance.
|
|
346
314
|
*/
|
|
347
315
|
resolverInfo?: {
|
|
348
|
-
|
|
316
|
+
/** Null when the source never says which type the resolver attaches to. */
|
|
317
|
+
typeName: string | null;
|
|
349
318
|
fieldName: string;
|
|
350
319
|
schemaSdl?: string;
|
|
351
320
|
};
|
|
352
321
|
/**
|
|
353
322
|
* Populated by `graphqlHookCall` discovery (GraphQL consumer side).
|
|
354
|
-
*
|
|
323
|
+
* Has the operation shape the adapter uses to build a
|
|
355
324
|
* `graphql-operation` binding. `operationName` is absent for
|
|
356
325
|
* anonymous operations (`gql\`query { ... }\``, no identifier).
|
|
357
326
|
*
|
|
@@ -360,7 +329,7 @@ interface DiscoveredUnit {
|
|
|
360
329
|
* alongside the parsed shape so downstream tools can re-parse if
|
|
361
330
|
* they need additional detail beyond what we surface here. Absent
|
|
362
331
|
* when the document body wasn't statically readable and the header
|
|
363
|
-
* was recovered from `TypedDocumentNode` type arguments
|
|
332
|
+
* was recovered from `TypedDocumentNode` type arguments: a
|
|
364
333
|
* cross-module codegen reference to a document produced by a helper.
|
|
365
334
|
*
|
|
366
335
|
* `variables` list the `$name: Type` declarations at the operation
|
|
@@ -368,7 +337,7 @@ interface DiscoveredUnit {
|
|
|
368
337
|
* pairing layers can match against resolver args. Empty when the
|
|
369
338
|
* document body wasn't read.
|
|
370
339
|
*
|
|
371
|
-
* `rootFields` is the list of root-level selection names
|
|
340
|
+
* `rootFields` is the list of root-level selection names: the
|
|
372
341
|
* fields the operation actually selects under Query / Mutation /
|
|
373
342
|
* Subscription. Used by the checker's pairing pass. Empty when the
|
|
374
343
|
* document body wasn't read.
|
|
@@ -395,9 +364,9 @@ interface DiscoveredUnit {
|
|
|
395
364
|
};
|
|
396
365
|
};
|
|
397
366
|
/**
|
|
398
|
-
* Populated by `packageExports` discovery.
|
|
399
|
-
*
|
|
400
|
-
* with `package` + `exportPath` fields
|
|
367
|
+
* Populated by `packageExports` discovery. Has the package identity
|
|
368
|
+
* the adapter uses to build a `function-call` binding
|
|
369
|
+
* with `package` + `exportPath` fields: i.e. a provider summary
|
|
401
370
|
* for a publicly-exported library function.
|
|
402
371
|
*/
|
|
403
372
|
packageExportInfo?: {
|
|
@@ -409,7 +378,7 @@ interface DiscoveredUnit {
|
|
|
409
378
|
* controllers). The adapter uses it to build a `rest` binding
|
|
410
379
|
* with `(method, path)` directly, bypassing the
|
|
411
380
|
* `bindingExtraction` config used by Express / Fastify (which
|
|
412
|
-
* extract from `app.get(...)` registration calls
|
|
381
|
+
* extract from `app.get(...)` registration calls: the wrong
|
|
413
382
|
* shape for decorator-driven controllers).
|
|
414
383
|
*/
|
|
415
384
|
routeInfo?: {
|
|
@@ -418,8 +387,8 @@ interface DiscoveredUnit {
|
|
|
418
387
|
};
|
|
419
388
|
/**
|
|
420
389
|
* Populated by a pack's `discoverUnits` callback for a message-bus
|
|
421
|
-
* consumer whose channel the code
|
|
422
|
-
* config
|
|
390
|
+
* consumer whose channel the code gives (a handler factory whose
|
|
391
|
+
* config includes the expected subject). The adapter uses it to build
|
|
423
392
|
* a `message-bus` binding directly, which pairs with producers
|
|
424
393
|
* sending on the same channel.
|
|
425
394
|
*/
|
|
@@ -433,16 +402,41 @@ interface DiscoveredUnit {
|
|
|
433
402
|
* Metadata merged onto the assembled summary's `metadata` field.
|
|
434
403
|
* Populated when a pack's `discoverUnits` callback stamps provenance
|
|
435
404
|
* on the units it returns (the discovery-layer sibling of the
|
|
436
|
-
* per-sub-unit `metadata` the `subUnits` hook
|
|
405
|
+
* per-sub-unit `metadata` the `subUnits` hook returns).
|
|
437
406
|
*/
|
|
438
407
|
metadata?: Record<string, unknown>;
|
|
408
|
+
/**
|
|
409
|
+
* What a discovery handler could not read about the boundary this
|
|
410
|
+
* unit is on. A route whose path is computed is still a route
|
|
411
|
+
* somebody declared, so the unit goes out with no path claimed and
|
|
412
|
+
* this sentence saying which part went unread. It ends up on the
|
|
413
|
+
* summary as an `unreadOutcome` gap.
|
|
414
|
+
*/
|
|
415
|
+
unreadBinding?: string;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* What a route's path should be composed with once mount discovery has
|
|
420
|
+
* run: the prefix a router was mounted under, following however many
|
|
421
|
+
* routers it was mounted onto in turn. Built once per extraction by
|
|
422
|
+
* `buildMountPrefixIndex`, over every mount call the active packs'
|
|
423
|
+
* `DiscoveryPattern.mount` config recognizes.
|
|
424
|
+
*/
|
|
425
|
+
interface MountPrefixIndex {
|
|
426
|
+
/**
|
|
427
|
+
* The prefix routes on `routerNode` (a registration subject's own
|
|
428
|
+
* creation site, e.g. a `Router()` call) were mounted under. Empty
|
|
429
|
+
* when nothing mounts it, or when a mount call along the way
|
|
430
|
+
* couldn't be resolved to a literal prefix and a concrete router.
|
|
431
|
+
*/
|
|
432
|
+
effectivePrefixFor(routerNode: Node): string;
|
|
439
433
|
}
|
|
440
434
|
|
|
441
435
|
/**
|
|
442
436
|
* Discover code units in `sourceFile` by running all patterns.
|
|
443
437
|
* Deduplicates entries with the same function node and kind.
|
|
444
438
|
*/
|
|
445
|
-
declare function discoverUnits(sourceFile: SourceFile, patterns: DiscoveryPattern[], resolution?: ResolutionStore): DiscoveredUnit[];
|
|
439
|
+
declare function discoverUnits(sourceFile: SourceFile, patterns: DiscoveryPattern[], resolution?: ResolutionStore, mountPrefixes?: MountPrefixIndex): DiscoveredUnit[];
|
|
446
440
|
|
|
447
441
|
interface TimingReport {
|
|
448
442
|
totalMs: number;
|
|
@@ -455,7 +449,7 @@ interface TimingReport {
|
|
|
455
449
|
interface Timer {
|
|
456
450
|
/** Run `fn`, accumulate wall time under `label`, return its result. */
|
|
457
451
|
time<T>(label: string, fn: () => T): T;
|
|
458
|
-
/** Async variant
|
|
452
|
+
/** Async variant: same accumulation rule. */
|
|
459
453
|
timeAsync<T>(label: string, fn: () => Promise<T>): Promise<T>;
|
|
460
454
|
/** Snapshot of all accumulated phases, ordered by total time descending. */
|
|
461
455
|
report(): TimingReport;
|
|
@@ -468,82 +462,42 @@ interface Timer {
|
|
|
468
462
|
*/
|
|
469
463
|
type DescentBarriers = ReadonlySet<Node>;
|
|
470
464
|
|
|
471
|
-
|
|
465
|
+
/**
|
|
466
|
+
* The whole extraction, from a tsconfig to a set of behavioral
|
|
467
|
+
* summaries. `createTypeScriptAdapter` is what a caller uses;
|
|
468
|
+
* everything else here is a stage of the run it drives.
|
|
469
|
+
*
|
|
470
|
+
* A run loads the project, asks each pack which files it cares about,
|
|
471
|
+
* discovers units in those files, walks each unit's body for terminals
|
|
472
|
+
* and effects, works out the boundary the unit is on, and assembles a
|
|
473
|
+
* summary. Several passes then run over the finished set: mount
|
|
474
|
+
* prefixes, module-load behavior, the reachable closure, rethrow
|
|
475
|
+
* sources, wrapper expansion, sub-units, and naming. The adapter owns
|
|
476
|
+
* what TypeScript and ECMAScript define; what a call means to a
|
|
477
|
+
* framework or a runtime belongs to a pack.
|
|
478
|
+
*/
|
|
479
|
+
|
|
480
|
+
declare function extractCodeStructure(unit: DiscoveredUnit, pack: PatternPack, invocationRecognizers?: InvocationRecognizer[], accessRecognizers?: AccessRecognizer[], barriers?: DescentBarriers, resolveWrittenValue?: (value: Node) => Node | null): RawCodeStructure;
|
|
472
481
|
interface TypeScriptAdapterConfig {
|
|
473
482
|
tsConfigFilePath?: string;
|
|
474
|
-
/**
|
|
475
|
-
* What the project being read calls itself, so a summary can carry a
|
|
476
|
-
* name nothing else in a run shares.
|
|
477
|
-
*
|
|
478
|
-
* Two services in one repository both hold `src/handlers.ts`, and a
|
|
479
|
-
* summary named after the file and the export is the same on both
|
|
480
|
-
* sides until something says which project it came from. Worked out
|
|
481
|
-
* from the project's own package.json when nobody says.
|
|
482
|
-
*/
|
|
483
|
+
/** Separates two services in one repository whose file names match. */
|
|
483
484
|
workspace?: string;
|
|
484
485
|
project?: Project;
|
|
485
486
|
frameworks: PatternPack[];
|
|
486
487
|
extractorOptions?: ExtractorOptions;
|
|
487
|
-
/**
|
|
488
|
-
* Include library summaries for every function reachable through a
|
|
489
|
-
* static call edge from a pack-discovered unit. Defaults to `true` —
|
|
490
|
-
* internal orchestrators, helpers, and utilities that frameworks
|
|
491
|
-
* don't recognise become `library`-kind summaries with
|
|
492
|
-
* `recognition: "reachable"` so readers can see their behaviour.
|
|
493
|
-
* Set to `false` for pack-only extraction (smaller output; only the
|
|
494
|
-
* units packs explicitly claim).
|
|
495
|
-
*/
|
|
488
|
+
/** A `library` summary per function reachable by a static call edge. */
|
|
496
489
|
includeReachable?: boolean;
|
|
497
|
-
/**
|
|
498
|
-
* Called once at the end of each `extractAll` / `extractFromFiles`
|
|
499
|
-
* with the wall-clock breakdown of the major extraction phases.
|
|
500
|
-
* Always-on instrumentation cost is negligible (millisecond-scale
|
|
501
|
-
* `performance.now()` calls); the callback is the opt-in for
|
|
502
|
-
* surfacing it. CLI uses this to render the per-extract summary
|
|
503
|
-
* line and the `--timing` breakdown.
|
|
504
|
-
*/
|
|
505
490
|
onTiming?: (report: TimingReport) => void;
|
|
506
|
-
/**
|
|
507
|
-
* Called once per extract with the cache hit/miss decision and the
|
|
508
|
-
* partial-reuse diagnostic — counts of cached summaries that fine-
|
|
509
|
-
* grained invalidation would have salvaged on a coarse miss.
|
|
510
|
-
* Phase 4a uses this to validate the dep-tracking model before
|
|
511
|
-
* activating partial-reuse.
|
|
512
|
-
*/
|
|
513
491
|
onCacheDiagnostic?: (diagnostic: CacheDiagnostic) => void;
|
|
514
|
-
/**
|
|
515
|
-
* Called once per `extractAll` with the funnel: files in the
|
|
516
|
-
* tsconfig, files walked, and per-pack gate / candidate / unit /
|
|
517
|
-
* summary counts. The CLI renders it when a run produces nothing,
|
|
518
|
-
* so "wrote 0 summaries" can say which stage the count died at
|
|
519
|
-
* instead of leaving the user to guess.
|
|
520
|
-
*
|
|
521
|
-
* Not called on a cache hit, where no stage ran.
|
|
522
|
-
*/
|
|
492
|
+
/** Not called on a cache hit, where no stage ran. */
|
|
523
493
|
onExtractionReport?: (report: ExtractionReport) => void;
|
|
524
|
-
/**
|
|
525
|
-
* On-disk cache directory for the extraction cache. Pass an absolute
|
|
526
|
-
* path; defaults to `.suss/cache/` next to the tsconfig, and to no
|
|
527
|
-
* cache at all when no tsconfig path is supplied. Pass `null` to turn
|
|
528
|
-
* caching off. A warm run with nothing changed costs one stat per
|
|
529
|
-
* file and returns the previous run's summaries whole.
|
|
530
|
-
*
|
|
531
|
-
* A process that loaded the adapter from source gets no cache
|
|
532
|
-
* whatever this says, because a key made there cannot tell one build
|
|
533
|
-
* of the adapter from another.
|
|
534
|
-
*/
|
|
494
|
+
/** Absolute. `.suss/cache/` beside the tsconfig; `null` turns it off. */
|
|
535
495
|
cacheDir?: string | null;
|
|
536
496
|
}
|
|
537
|
-
interface TypeScriptAdapter {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
* Extract summaries from a specific list of files. Returns a
|
|
541
|
-
* Promise so the implementation can do concurrent I/O during
|
|
542
|
-
* the pre-filter / lazy-load phases without bottlenecking on
|
|
543
|
-
* sync `fs.readFileSync`.
|
|
544
|
-
*/
|
|
497
|
+
interface TypeScriptAdapter extends LanguageAdapter {
|
|
498
|
+
/** Kept off `LanguageAdapter`, so a caller needing ts-morph asks for it. */
|
|
499
|
+
readonly tsProject: Project;
|
|
545
500
|
extractFromFiles(filePaths: string[]): Promise<BehavioralSummary[]>;
|
|
546
|
-
/** Extract summaries from every (non-declaration) file the Project knows about. */
|
|
547
501
|
extractAll(): Promise<BehavioralSummary[]>;
|
|
548
502
|
}
|
|
549
503
|
declare function createTypeScriptAdapter(config: TypeScriptAdapterConfig): TypeScriptAdapter;
|
|
@@ -556,19 +510,18 @@ interface FoundTerminal {
|
|
|
556
510
|
* of an arrow that returns without writing `return`. Absent when the
|
|
557
511
|
* terminal is not a return at all, as a throw is not.
|
|
558
512
|
*
|
|
559
|
-
*
|
|
560
|
-
*
|
|
561
|
-
* Anything downstream that
|
|
513
|
+
* Only the matcher knows this, because `node` is wherever that
|
|
514
|
+
* matcher stopped looking, which differs from matcher to matcher.
|
|
515
|
+
* Anything downstream that tried to guess got it wrong.
|
|
562
516
|
*/
|
|
563
517
|
source?: Node;
|
|
564
518
|
/**
|
|
565
|
-
* A test that also has to
|
|
519
|
+
* A test that also has to be true for this terminal to be the one that
|
|
566
520
|
* fires, on top of whatever the path to it required.
|
|
567
521
|
*
|
|
568
|
-
* One call site
|
|
569
|
-
* written as a choice, and the arms
|
|
570
|
-
*
|
|
571
|
-
* conditions.
|
|
522
|
+
* One call site produces more than one status when the status is
|
|
523
|
+
* written as a choice, and nothing else tells the arms apart: they
|
|
524
|
+
* share a node, so the path engine gives them the same conditions.
|
|
572
525
|
*/
|
|
573
526
|
whenAlso?: RawCondition;
|
|
574
527
|
}
|
|
@@ -581,24 +534,20 @@ interface FoundTerminal {
|
|
|
581
534
|
* terminal output is produced inside a Promise executor or `.then`
|
|
582
535
|
* callback (`res.json(data)` on the unit's own `res` parameter) is still
|
|
583
536
|
* discovered. Inside a nested function only escaping terminals are
|
|
584
|
-
* matched
|
|
537
|
+
* matched: see `NESTED_ESCAPING_MATCH_TYPES`.
|
|
585
538
|
*/
|
|
586
539
|
declare function findTerminals(func: FunctionRoot, patterns: TerminalPattern[], barriers?: DescentBarriers): FoundTerminal[];
|
|
587
540
|
|
|
588
541
|
/**
|
|
589
|
-
*
|
|
590
|
-
* 1. findTerminals, to locate terminal nodes
|
|
591
|
-
* 2. computePathConditions, for the conditions on each path (CFG
|
|
592
|
-
* enumeration; declined shapes degrade to sound
|
|
593
|
-
* under-specification)
|
|
594
|
-
* 3. parseConditionExpression, turning an Expression into a Predicate
|
|
595
|
-
* 4. extractInvocationEffects, for bare expression-statement calls,
|
|
596
|
-
* attached to the default branch so handler and useEffect bodies
|
|
597
|
-
* carry their side-effect set
|
|
542
|
+
* Turns one function into the list of branches a summary is built from.
|
|
598
543
|
*
|
|
599
|
-
*
|
|
600
|
-
*
|
|
544
|
+
* Terminal matching, path-condition enumeration, predicate parsing and
|
|
545
|
+
* effect capture each live in their own module. This one runs them over
|
|
546
|
+
* the same function and lines the results up: every terminal gets the
|
|
547
|
+
* conditions on each path that reaches it, and the calls in the body get
|
|
548
|
+
* attached to the branch they fire on.
|
|
601
549
|
*/
|
|
550
|
+
|
|
602
551
|
/**
|
|
603
552
|
* The branches a function produces, alongside the terminals they came
|
|
604
553
|
* from. A caller that wants to know which returns went unclaimed needs
|
|
@@ -609,7 +558,14 @@ interface RawBranchResult {
|
|
|
609
558
|
branches: RawBranch[];
|
|
610
559
|
terminals: FoundTerminal[];
|
|
611
560
|
}
|
|
612
|
-
|
|
561
|
+
/**
|
|
562
|
+
* Every branch a function produces: one per path that reaches a
|
|
563
|
+
* terminal, with the conditions on that path and the effects that fire
|
|
564
|
+
* along it. A branch is `isDefault` when it has no conditions at all,
|
|
565
|
+
* or when every condition on it came from an early return or throw,
|
|
566
|
+
* since those are the paths that run when no guard fired.
|
|
567
|
+
*/
|
|
568
|
+
declare function extractRawBranches(func: FunctionRoot, terminalPatterns: TerminalPattern[], invocationRecognizers?: InvocationRecognizer[], accessRecognizers?: AccessRecognizer[], barriers?: DescentBarriers, resolveWrittenValue?: (value: Node) => Node | null): RawBranchResult;
|
|
613
569
|
|
|
614
570
|
/**
|
|
615
571
|
* A Project covering `rootDir`, for a codebase with no tsconfig to read.
|
|
@@ -623,14 +579,30 @@ declare function createProjectWithoutTsconfig(rootDir: string): {
|
|
|
623
579
|
declare function findNearestTsconfig(startDir: string): string | null;
|
|
624
580
|
|
|
625
581
|
/**
|
|
626
|
-
*
|
|
582
|
+
* Reads a method call whose receiver has a type the project's config
|
|
583
|
+
* points at.
|
|
627
584
|
*
|
|
628
|
-
*
|
|
629
|
-
*
|
|
630
|
-
*
|
|
585
|
+
* A pack recognizes library calls by their command class, so it only
|
|
586
|
+
* sees the libraries it was built with. A project that sends every
|
|
587
|
+
* message through a wrapper of its own writes no such call, and the pack
|
|
588
|
+
* sees nothing. This lets the project describe the wrapper instead:
|
|
589
|
+
* which module declares it, which type the receiver has, which method
|
|
590
|
+
* does the sending, and which arguments the subject and body are in.
|
|
591
|
+
*
|
|
592
|
+
* A call on a typed receiver is something TypeScript already tells us,
|
|
593
|
+
* so reading it belongs to the adapter. What the call means on the bus
|
|
594
|
+
* is the pack's judgment, so the pack builds the effect from this.
|
|
595
|
+
*/
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* A call the project has declared to be its own dispatcher.
|
|
599
|
+
*
|
|
600
|
+
* `receiver` is the type name rather than the variable: a service keeps
|
|
601
|
+
* its dispatcher in a field, a closure or a constructor parameter, and
|
|
602
|
+
* the type is the only thing stable across all three.
|
|
631
603
|
*/
|
|
632
604
|
interface ConfiguredCallSpec {
|
|
633
|
-
/** Module the receiver's type
|
|
605
|
+
/** Module that declares the receiver's type. */
|
|
634
606
|
module: string;
|
|
635
607
|
/** Type name of the receiver, as exported from that module. */
|
|
636
608
|
receiver: string;
|
|
@@ -639,14 +611,14 @@ interface ConfiguredCallSpec {
|
|
|
639
611
|
/** Argument index carrying the subject. */
|
|
640
612
|
subjectArg: number;
|
|
641
613
|
/**
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
*
|
|
614
|
+
* Which argument the message body is. Left out when the method has no
|
|
615
|
+
* single body argument, as a batch method taking a list of entries
|
|
616
|
+
* does, and then no body is reported.
|
|
645
617
|
*/
|
|
646
618
|
bodyArg?: number;
|
|
647
619
|
}
|
|
648
620
|
interface ConfiguredCallRead {
|
|
649
|
-
/** The subject the call
|
|
621
|
+
/** The subject the call sends to, always a literal string. */
|
|
650
622
|
subject: string;
|
|
651
623
|
/** The body argument's extracted shape, or null when none applies. */
|
|
652
624
|
body: EffectArg | null;
|
|
@@ -659,13 +631,12 @@ interface ConfiguredCallContext {
|
|
|
659
631
|
extractArgs(): EffectArg[];
|
|
660
632
|
}
|
|
661
633
|
/**
|
|
662
|
-
* Read `call` as the configured send, or
|
|
634
|
+
* Read `call` as the configured send, or return null.
|
|
663
635
|
*
|
|
664
|
-
* A subject that is not a string
|
|
665
|
-
*
|
|
666
|
-
*
|
|
667
|
-
*
|
|
668
|
-
* than not seeing the producer at all.
|
|
636
|
+
* A subject that is not a literal string (a value built at runtime, a
|
|
637
|
+
* template with substitutions, a computed key) returns null and the pack
|
|
638
|
+
* emits nothing. Guessing a channel we cannot read would pair a producer
|
|
639
|
+
* with the wrong consumer, which is worse than missing the producer.
|
|
669
640
|
*/
|
|
670
641
|
declare function readConfiguredCall(call: CallExpression, ctx: ConfiguredCallContext, spec: ConfiguredCallSpec): ConfiguredCallRead | null;
|
|
671
642
|
|
|
@@ -699,11 +670,11 @@ interface TsDiscoveryContext {
|
|
|
699
670
|
getFilePath(sourceFile: SourceFile): string;
|
|
700
671
|
/**
|
|
701
672
|
* Yield every export from the source file whose declaration is a
|
|
702
|
-
* function
|
|
673
|
+
* function: function declarations, arrow / function-expression
|
|
703
674
|
* variable initializers, and re-exports of either. Skips
|
|
704
675
|
* non-function exports (constants, classes, types).
|
|
705
676
|
*
|
|
706
|
-
* Each entry
|
|
677
|
+
* Each entry has the exported name (the binding the consumer
|
|
707
678
|
* uses), the function root, and whether the export is the file's
|
|
708
679
|
* default export. `default` is included; the pack decides whether
|
|
709
680
|
* to handle it (typically the data-driven `namedExport(["default"])`
|
|
@@ -715,24 +686,24 @@ interface TsDiscoveryContext {
|
|
|
715
686
|
isDefault: boolean;
|
|
716
687
|
}>;
|
|
717
688
|
/**
|
|
718
|
-
* For an export the project builds by calling a factory, the string
|
|
719
|
-
* property of that call's config object
|
|
689
|
+
* For an export the project builds by calling a factory, the string
|
|
690
|
+
* under a property of that call's config object:
|
|
720
691
|
* `export const handler = makeWidgetHandler({ subject: "a.b" }, ...)`
|
|
721
|
-
* with `{ property: "subject" }`
|
|
692
|
+
* with `{ property: "subject" }` gives back `"a.b"`.
|
|
722
693
|
*
|
|
723
|
-
*
|
|
724
|
-
*
|
|
725
|
-
*
|
|
726
|
-
*
|
|
694
|
+
* The caller does not have to say which function was called or which
|
|
695
|
+
* argument the config was in. Every object argument is read. Giving the
|
|
696
|
+
* callee or fixing the argument position narrows that down, for a
|
|
697
|
+
* project whose factories would otherwise collide.
|
|
727
698
|
*
|
|
728
|
-
* Two arguments
|
|
699
|
+
* Two arguments with the property set to different values give back
|
|
729
700
|
* null, since nothing says which one was meant.
|
|
730
701
|
*
|
|
731
702
|
* The config argument is usually an object literal at the call site;
|
|
732
703
|
* a variable or import is followed to the literal it resolves to.
|
|
733
704
|
* `as const` and parentheses around the property value are peeled.
|
|
734
705
|
* Anything but a string literal underneath (a computed subject, a
|
|
735
|
-
* template, a call)
|
|
706
|
+
* template, a call) gives back null, and the caller attaches nothing
|
|
736
707
|
* rather than guessing.
|
|
737
708
|
*/
|
|
738
709
|
exportedCallConfigString(sourceFile: SourceFile, exportName: string, spec: {
|
|
@@ -744,30 +715,61 @@ interface TsDiscoveryContext {
|
|
|
744
715
|
* Walk a function's body for return statements whose value is a
|
|
745
716
|
* JSX element / fragment / self-closing tag. Returns true on the
|
|
746
717
|
* first match; false otherwise. Skips into nested function bodies
|
|
747
|
-
|
|
718
|
+
*: nested arrow returning JSX is its own component, not part of
|
|
748
719
|
* this function's output.
|
|
749
720
|
*
|
|
750
|
-
* Concise-arrow bodies (`() => <X/>`) are handled
|
|
721
|
+
* Concise-arrow bodies (`() => <X/>`) are handled: the body IS
|
|
751
722
|
* the implicit return.
|
|
752
723
|
*/
|
|
753
724
|
hasJsxReturn(func: FunctionRoot): boolean;
|
|
754
725
|
}
|
|
755
726
|
declare function createTsDiscoveryContext(resolution?: ResolutionStore): TsDiscoveryContext;
|
|
756
727
|
|
|
757
|
-
|
|
728
|
+
type ExportedDeclarationMap = ReturnType<SourceFile["getExportedDeclarations"]>;
|
|
729
|
+
/**
|
|
730
|
+
* What a module exports, by exported name. Cached, because ts-morph
|
|
731
|
+
* rebuilds the map from scratch on every `getExportedDeclarations` call
|
|
732
|
+
* and callee resolution asks the same file the same thing once per
|
|
733
|
+
* import site.
|
|
734
|
+
*/
|
|
735
|
+
declare function exportedDeclarationsOf(sourceFile: SourceFile): ExportedDeclarationMap;
|
|
736
|
+
/**
|
|
737
|
+
* `Symbol.getAliasedSymbol`, with the re-export chains warmed first so
|
|
738
|
+
* that an import sitting at the top of a deep chain still resolves
|
|
739
|
+
* instead of running the call stack out.
|
|
740
|
+
*/
|
|
741
|
+
declare function resolveAliasedSymbol(symbol: Symbol): Symbol | undefined;
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Checks that tell you a pack is probably not working.
|
|
745
|
+
*
|
|
746
|
+
* The funnel shows where a run's counts dropped to zero. These checks
|
|
747
|
+
* ask something narrower of each pack on its own: did this pack lose
|
|
748
|
+
* everything at some stage, when the stage before it had something?
|
|
749
|
+
*
|
|
750
|
+
* A pack that finds nothing in a codebase that does not use its library
|
|
751
|
+
* is working correctly, so a count of zero on its own is never the
|
|
752
|
+
* signal. What makes zero a signal is the count before it. A pack whose
|
|
753
|
+
* import gate picked forty files and whose discovery then found no unit
|
|
754
|
+
* in any of them said "look here" and failed to look. Every later stage
|
|
755
|
+
* works the same way, which is why these checks are one comparison run
|
|
756
|
+
* over a list of pairs rather than one check written per stage.
|
|
757
|
+
*/
|
|
758
|
+
|
|
759
|
+
/** One thing that looks wrong, reported the way the dogfood invariants are. */
|
|
758
760
|
interface HealthViolation {
|
|
759
761
|
label: string;
|
|
760
762
|
detail: string;
|
|
761
763
|
}
|
|
762
764
|
interface HealthCheck {
|
|
763
|
-
/** The property, as something
|
|
765
|
+
/** The property being checked, as something either true or false. */
|
|
764
766
|
name: string;
|
|
765
767
|
/**
|
|
766
768
|
* The heading printed when this check finds something.
|
|
767
769
|
*
|
|
768
|
-
*
|
|
769
|
-
* violations
|
|
770
|
-
*
|
|
770
|
+
* `name` is written as an assertion, so printing it above the
|
|
771
|
+
* violations would say "no pack collides with itself" and then list a
|
|
772
|
+
* pack colliding with itself.
|
|
771
773
|
*/
|
|
772
774
|
whenBroken: string;
|
|
773
775
|
/**
|
|
@@ -775,10 +777,10 @@ interface HealthCheck {
|
|
|
775
777
|
*
|
|
776
778
|
* A `run` check found something about the code in front of it, and
|
|
777
779
|
* the person who started the run can do something about it: drop a
|
|
778
|
-
* pack, install a dependency, open an issue with the file that
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
*
|
|
780
|
+
* pack, install a dependency, open an issue with the file that broke.
|
|
781
|
+
* A `pack` check found something about how a pack was built, which
|
|
782
|
+
* only whoever ships that pack can fix. Printing the second kind on
|
|
783
|
+
* every run would teach people to skim past the first.
|
|
782
784
|
*/
|
|
783
785
|
audience: "run" | "pack";
|
|
784
786
|
violations: HealthViolation[];
|
|
@@ -791,10 +793,10 @@ declare function evaluatePackHealth(report: ExtractionReport): HealthCheck[];
|
|
|
791
793
|
/**
|
|
792
794
|
* The health checks that fired, as lines for a terminal.
|
|
793
795
|
*
|
|
794
|
-
* `audiences` is who the caller is printing for
|
|
795
|
-
*
|
|
796
|
-
*
|
|
797
|
-
*
|
|
796
|
+
* `audiences` is who the caller is printing for. It is required because
|
|
797
|
+
* no single choice suits every caller: a CLI run prints what the person
|
|
798
|
+
* who started it can act on, while a run a pack author is reading wants
|
|
799
|
+
* both kinds.
|
|
798
800
|
*/
|
|
799
801
|
declare function formatPackHealth(checks: ReadonlyArray<HealthCheck>, audiences: ReadonlyArray<HealthCheck["audience"]>): string;
|
|
800
802
|
|
|
@@ -813,7 +815,7 @@ declare function parseConditionExpression(expr: Expression, depth?: number): Pre
|
|
|
813
815
|
declare function isImportedFrom(identifier: Node, expectedModule: string): boolean;
|
|
814
816
|
|
|
815
817
|
interface BranchFieldAccesses {
|
|
816
|
-
/** Terminal location (start, end lines)
|
|
818
|
+
/** Terminal location (start, end lines): used to match back to transitions */
|
|
817
819
|
terminalLocation: {
|
|
818
820
|
start: number;
|
|
819
821
|
end: number;
|
|
@@ -828,7 +830,7 @@ interface BranchFieldAccesses {
|
|
|
828
830
|
* @param callExpr The API call expression (fetch(), client.getUser())
|
|
829
831
|
* @param func The enclosing function
|
|
830
832
|
* @param branchLocations Terminal locations from the extracted branches
|
|
831
|
-
* @param responseSemantics Optional response property mappings from the pack
|
|
833
|
+
* @param responseSemantics Optional response property mappings from the pack ,
|
|
832
834
|
* used to filter non-body properties instead of a hardcoded list
|
|
833
835
|
*/
|
|
834
836
|
declare function collectClientFieldAccesses(callExpr: CallExpression, func: Node, branchLocations: Array<{
|
|
@@ -838,7 +840,7 @@ declare function collectClientFieldAccesses(callExpr: CallExpression, func: Node
|
|
|
838
840
|
|
|
839
841
|
/**
|
|
840
842
|
* Resolve a ts-morph Expression node to a structured ValueRef.
|
|
841
|
-
* Uses only expr.getSymbol()?.getDeclarations()[0] for symbol lookup
|
|
843
|
+
* Uses only expr.getSymbol()?.getDeclarations()[0] for symbol lookup ,
|
|
842
844
|
* never findReferencesAsNodes() which is project-wide and quadratic.
|
|
843
845
|
*
|
|
844
846
|
* Follows intermediate variable assignments (const data = result.body)
|
|
@@ -854,7 +856,7 @@ declare function resolveSubject(expr: Expression, depth?: number): ValueRef;
|
|
|
854
856
|
* bodies and source text.
|
|
855
857
|
*
|
|
856
858
|
* All walks are scoped to the parent function and deliberately do NOT
|
|
857
|
-
* recurse into nested function bodies
|
|
859
|
+
* recurse into nested function bodies, sub-units of nested functions
|
|
858
860
|
* belong to those functions' summaries, not to the enclosing parent.
|
|
859
861
|
*/
|
|
860
862
|
interface TsSubUnitContext {
|
|
@@ -867,7 +869,7 @@ interface TsSubUnitContext {
|
|
|
867
869
|
findJsxAttributes(parent: FunctionRoot): TsJsxAttributeLocation[];
|
|
868
870
|
/**
|
|
869
871
|
* Yield every call expression matching `name` (bare identifier
|
|
870
|
-
* callee only
|
|
872
|
+
* callee only: e.g. `useEffect(...)`, not `React.useEffect(...)`).
|
|
871
873
|
* Nested function bodies are skipped.
|
|
872
874
|
*/
|
|
873
875
|
findCallExpressionsByName(parent: FunctionRoot, name: string): CallExpression[];
|
|
@@ -912,14 +914,14 @@ interface TsJsxAttributeLocation {
|
|
|
912
914
|
}
|
|
913
915
|
declare function createTsSubUnitContext(): TsSubUnitContext;
|
|
914
916
|
|
|
915
|
-
declare const ADAPTER_VERSION = "0.2.
|
|
917
|
+
declare const ADAPTER_VERSION = "0.2.1";
|
|
916
918
|
/**
|
|
917
919
|
* Content hash of the given files, in the order they arrive. Empty when
|
|
918
920
|
* the list is empty or a file cannot be read, so a caller that could not
|
|
919
|
-
*
|
|
921
|
+
* locate a file gets the same "no stamp" result as a run from source
|
|
920
922
|
* rather than a hash of a shorter list.
|
|
921
923
|
*
|
|
922
|
-
* The caller resolves the paths, because
|
|
924
|
+
* The caller resolves the paths, because where a specifier resolves to
|
|
923
925
|
* depends on which package is asking.
|
|
924
926
|
*/
|
|
925
927
|
declare function computeContentHash(paths: readonly string[]): string;
|
|
@@ -934,13 +936,13 @@ declare function computeContentHash(paths: readonly string[]): string;
|
|
|
934
936
|
* came from, so folding the config and the code into the stamp is the
|
|
935
937
|
* loader's job.
|
|
936
938
|
*
|
|
937
|
-
* A source run says
|
|
938
|
-
* out of it. Nothing writes under that digest today, and a key
|
|
939
|
-
*
|
|
939
|
+
* A source run says as much in the digest rather than leaving the
|
|
940
|
+
* adapter out of it. Nothing writes under that digest today, and a key
|
|
941
|
+
* that says "source" cannot be mistaken for one built from the code.
|
|
940
942
|
*/
|
|
941
943
|
declare function computeAdapterPacksDigest(packVersions: ReadonlyArray<{
|
|
942
944
|
name: string;
|
|
943
945
|
version?: string;
|
|
944
946
|
}>): string;
|
|
945
947
|
|
|
946
|
-
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 Timer, type TimingReport, type TsDiscoveryContext, type TsJsxAttributeLocation, type TsSubUnitContext, type TypeScriptAdapter, type TypeScriptAdapterConfig, collectAncestorBranches, collectClientFieldAccesses, computeAdapterPacksDigest, computeContentHash, createProjectWithoutTsconfig, createTsDiscoveryContext, createTsSubUnitContext, createTypeScriptAdapter, discoverUnits, evaluatePackHealth, extractCodeStructure, extractRawBranches, findNearestTsconfig, findTerminals, formatPackHealth, isImportedFrom, parseConditionExpression, readConfiguredCall, readContract, readContractForClientCall, resolveSubject };
|
|
948
|
+
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 };
|