@suss/extractor 0.33.1 → 0.34.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 +3 -3
- package/dist/index.cjs +664 -193
- package/dist/index.d.cts +1114 -933
- package/dist/index.d.ts +1114 -933
- package/dist/index.js +641 -180
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { Effect, MessageBusSemantics, DeployableUnit, CodeUnitKind, RequestSpellingMetadata, BehavioralSummary, Predicate, TypeShape, RenderNode, BoundaryBinding, WrapperReference, GraphqlDeclaredContract, ConfidenceInfo, Transition, Gap, Input, Output } from '@suss/behavioral-ir';
|
|
1
|
+
import { Effect, MessageBusSemantics, DeployableUnit, CodeUnitKind, RequestSpellingMetadata, BehavioralSummary, Predicate, ValueRef, TypeShape, RenderNode, ProvenanceSlot, BoundaryBinding, WrapperReference, TestMetadata, GraphqlDeclaredContract, ConfidenceInfo, Transition, Gap, Input, Output } from '@suss/behavioral-ir';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* What a unit reads out of
|
|
5
|
+
* What a unit reads out of its inputs.
|
|
6
6
|
*
|
|
7
7
|
* A summary already describes this, but only as a chain of `derived` nodes
|
|
8
8
|
* nested one inside the next, which anyone who wants the list has to walk
|
|
9
9
|
* themselves. The `path` on an input reference is empty, so querying the
|
|
10
10
|
* field that looks like the answer gives nothing back.
|
|
11
11
|
*
|
|
12
|
-
* This module flattens
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* This module flattens those chains into a list of everything a unit reads
|
|
13
|
+
* through its inputs, once each, sorted by input and then by path. The list
|
|
14
|
+
* goes on the summary as `inputReads`.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
/** One thing a unit read, and the way it reached it. */
|
|
@@ -31,10 +31,10 @@ interface InputRead {
|
|
|
31
31
|
* is a parameter. The call site has the literals and no library; the
|
|
32
32
|
* helper body has the library and no literals.
|
|
33
33
|
*
|
|
34
|
-
* A pack asks
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
34
|
+
* A pack asks for the helper to be read once, over the whole project,
|
|
35
|
+
* before any file is walked. What comes back says what the body does in
|
|
36
|
+
* terms of the helper's own parameters, and the pack turns that into
|
|
37
|
+
* discovery patterns and invocation recognizers.
|
|
38
38
|
*/
|
|
39
39
|
|
|
40
40
|
/**
|
|
@@ -45,8 +45,8 @@ interface InputRead {
|
|
|
45
45
|
*/
|
|
46
46
|
type HelperValue =
|
|
47
47
|
/**
|
|
48
|
-
* A string, with `{N}`
|
|
49
|
-
*
|
|
48
|
+
* A string, with `{N}` wherever parameter N was interpolated. A
|
|
49
|
+
* literal with no interpolation comes back unchanged.
|
|
50
50
|
*/
|
|
51
51
|
{
|
|
52
52
|
as: "text";
|
|
@@ -83,11 +83,11 @@ interface HelperSink {
|
|
|
83
83
|
}
|
|
84
84
|
/** A function the project declares, as the index read it. */
|
|
85
85
|
interface ProjectHelper {
|
|
86
|
-
/**
|
|
86
|
+
/** The declared name, which is the name call sites use. */
|
|
87
87
|
name: string;
|
|
88
88
|
/** Absolute path of the file declaring it. */
|
|
89
89
|
file: string;
|
|
90
|
-
/**
|
|
90
|
+
/** Parameter names in declaration order. */
|
|
91
91
|
parameters: string[];
|
|
92
92
|
/**
|
|
93
93
|
* Parameters some caller handed this pack's own value to. Empty when
|
|
@@ -100,11 +100,11 @@ interface ProjectHelper {
|
|
|
100
100
|
/**
|
|
101
101
|
* How the index picks out which of the project's functions to read.
|
|
102
102
|
*
|
|
103
|
-
* `subject` starts at a call site: a function the project
|
|
104
|
-
* this pack's own values to is
|
|
103
|
+
* `subject` starts at a call site: a function the project passes one of
|
|
104
|
+
* this pack's own values to is one of the pack's helpers, whatever the
|
|
105
105
|
* helper's file imports. `text` starts at the body, for a pack whose
|
|
106
106
|
* library is reached over the wire and has no import to look for. Both
|
|
107
|
-
*
|
|
107
|
+
* searches use only facts about the library, as `requiresImport` does.
|
|
108
108
|
*/
|
|
109
109
|
type HelperSearch = {
|
|
110
110
|
by: "subject";
|
|
@@ -117,30 +117,30 @@ interface HelperDeclarations {
|
|
|
117
117
|
discovery?: DiscoveryPattern[];
|
|
118
118
|
invocationRecognizers?: InvocationRecognizer[];
|
|
119
119
|
}
|
|
120
|
-
/** A pack's
|
|
120
|
+
/** A pack's request to have the project's helpers read before extraction. */
|
|
121
121
|
interface ProjectHelpers {
|
|
122
122
|
find: HelperSearch;
|
|
123
123
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
124
|
+
* Called once, before the first file is walked, to turn the helpers
|
|
125
|
+
* into patterns and recognizers. It receives data and never an AST, so
|
|
126
|
+
* a pack that declares it runs on any adapter that reads helpers.
|
|
127
127
|
*/
|
|
128
128
|
declare(helpers: readonly ProjectHelper[]): HelperDeclarations;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
/**
|
|
132
|
-
* The `PatternPack` interface
|
|
133
|
-
*
|
|
134
|
-
*
|
|
132
|
+
* The `PatternPack` interface a framework pack hands to a language adapter.
|
|
133
|
+
* The pack describes what to look for (an import, a call, a decorator), and
|
|
134
|
+
* the adapter finds it in its own language's syntax tree.
|
|
135
135
|
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
136
|
+
* Most of a pack is data, so one pack description works with any adapter
|
|
137
|
+
* that reads these patterns. When a pack needs to compute something, the
|
|
138
|
+
* usual fix is another declarative field here. The function hooks
|
|
139
|
+
* (`subUnits`, `discoverUnits` and the recognizers) cover what data cannot.
|
|
139
140
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* semantics say which property is the body and which is the status.
|
|
141
|
+
* Sections follow the order an adapter uses them. The pack patterns
|
|
142
|
+
* reference at https://suss.sh/packs/patterns shows each variant with the
|
|
143
|
+
* code it matches.
|
|
144
144
|
*/
|
|
145
145
|
|
|
146
146
|
type DiscoveryMatch = {
|
|
@@ -165,21 +165,18 @@ type DiscoveryMatch = {
|
|
|
165
165
|
* Unset means any method call (or bare call for globals). */
|
|
166
166
|
methodFilter?: string[];
|
|
167
167
|
/**
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* treat `api.get(...)` (where `api = axios.create(...)`) the same as
|
|
173
|
-
* `axios.get(...)`.
|
|
168
|
+
* Methods on the import that build a client instance. A variable
|
|
169
|
+
* initialized from one of these calls is a client too, so with
|
|
170
|
+
* `factoryMethods: ["create"]`, `api.get(...)` after
|
|
171
|
+
* `api = axios.create(...)` matches the same as `axios.get(...)`.
|
|
174
172
|
*/
|
|
175
173
|
factoryMethods?: string[];
|
|
176
174
|
/**
|
|
177
|
-
* The
|
|
178
|
-
* request through the instance
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* provider's paths
|
|
182
|
-
* consumer's.
|
|
175
|
+
* The option on the factory call's config that sets a base path for
|
|
176
|
+
* every request through the instance, `baseURL` for axios. The
|
|
177
|
+
* adapter puts it in front of each call's path, so the consumer's
|
|
178
|
+
* route lines up with a spec whose `servers[0].url` adds the same
|
|
179
|
+
* prefix to the provider's paths.
|
|
183
180
|
*/
|
|
184
181
|
basePathOption?: string;
|
|
185
182
|
/**
|
|
@@ -190,88 +187,54 @@ type DiscoveryMatch = {
|
|
|
190
187
|
callable?: boolean;
|
|
191
188
|
} | {
|
|
192
189
|
/**
|
|
193
|
-
* A constructor or factory call
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* GraphQL type names (`Query`, `Mutation`, `Subscription`, or
|
|
197
|
-
* object-type names like `User`), and inner keys are field names
|
|
198
|
-
* whose values are resolver functions.
|
|
190
|
+
* A constructor or factory call whose config object contains a
|
|
191
|
+
* two-level resolver map, the usual way to write a code-first GraphQL
|
|
192
|
+
* server. Outer keys are type names and inner keys are field names:
|
|
199
193
|
*
|
|
200
|
-
* Example (Apollo Server v4):
|
|
201
194
|
* ```ts
|
|
202
|
-
* new ApolloServer({
|
|
203
|
-
* typeDefs,
|
|
204
|
-
* resolvers: {
|
|
205
|
-
* Query: { users: async () => {...} },
|
|
206
|
-
* Mutation: { createUser: async (_, {input}) => {...} },
|
|
207
|
-
* User: { fullName: (parent) => `${parent.first} ${parent.last}` },
|
|
208
|
-
* },
|
|
209
|
-
* });
|
|
195
|
+
* new ApolloServer({ typeDefs, resolvers: { Query: { users: async () => {...} } } });
|
|
210
196
|
* ```
|
|
211
197
|
*
|
|
212
|
-
* Each inner function becomes one
|
|
213
|
-
*
|
|
214
|
-
* `
|
|
215
|
-
*
|
|
198
|
+
* Each inner function becomes one unit bound as
|
|
199
|
+
* `graphql-resolver(typeName, fieldName)`. Both `new Ctor(cfg)` and
|
|
200
|
+
* `ctor(cfg)` match, since Apollo's standalone server uses `new` and
|
|
201
|
+
* yoga uses a bare call.
|
|
216
202
|
*/
|
|
217
203
|
type: "resolverMap";
|
|
218
204
|
importModule: string;
|
|
219
205
|
importName: string;
|
|
220
206
|
/**
|
|
221
|
-
* The
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
* spell it `"resolvers"`.
|
|
207
|
+
* The config property that contains the resolver map. The key belongs
|
|
208
|
+
* to the library, so the adapter has no default. Apollo, yoga and
|
|
209
|
+
* graphql-tools all use `"resolvers"`.
|
|
225
210
|
*/
|
|
226
211
|
mapProperty: string;
|
|
227
212
|
/**
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
* differently later. Leave it unset to discover every type.
|
|
213
|
+
* Types whose fields are not discovered as resolvers, such as
|
|
214
|
+
* `Subscription`. Leave it unset to discover every type.
|
|
231
215
|
*/
|
|
232
216
|
excludeTypes?: string[];
|
|
233
217
|
} | {
|
|
234
218
|
/**
|
|
235
219
|
* A consumer-side GraphQL hook call, the way Apollo Client and urql
|
|
236
|
-
* are normally used. Each call to
|
|
237
|
-
*
|
|
238
|
-
* `graphql-operation(operationType, operationName?)`.
|
|
220
|
+
* are normally used. Each call to a listed hook becomes a `client`
|
|
221
|
+
* unit bound as `graphql-operation(operationType, operationName?)`.
|
|
239
222
|
*
|
|
240
|
-
* The document
|
|
241
|
-
*
|
|
242
|
-
* from
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
* summary as `metadata.graphql.unresolvedDocument`, so the boundary is
|
|
248
|
-
* kept rather than dropped.
|
|
249
|
-
*
|
|
250
|
-
* Example:
|
|
251
|
-
* ```ts
|
|
252
|
-
* import { gql, useQuery } from "@apollo/client";
|
|
253
|
-
* const GET_USER = gql`query GetUser($id: ID!) { user(id: $id) { id } }`;
|
|
254
|
-
* function UserPage({ id }) {
|
|
255
|
-
* const { data } = useQuery(GET_USER, { variables: { id } });
|
|
256
|
-
* ...
|
|
257
|
-
* }
|
|
258
|
-
* ```
|
|
259
|
-
*
|
|
260
|
-
* The adapter records the operation name and type on the
|
|
261
|
-
* DiscoveredUnit's `operationInfo`, and binding construction uses that
|
|
262
|
-
* to emit `graphql-operation(...)`. The per-hook `operationType`
|
|
263
|
-
* wins when the document header cannot be read, the same way
|
|
264
|
-
* `graphqlImperativeCall.methods` does.
|
|
223
|
+
* The document can be an inline `gql` template, a const in this
|
|
224
|
+
* module or an imported one, a `.graphql` or `.gql` file import, or a
|
|
225
|
+
* `TypedDocumentNode` from graphql-codegen. When the body cannot be
|
|
226
|
+
* read, the operation header comes from the `TypedDocumentNode` type
|
|
227
|
+
* arguments. A document that still cannot be resolved is kept on the
|
|
228
|
+
* summary as `metadata.graphql.unresolvedDocument`, so the boundary
|
|
229
|
+
* stays.
|
|
265
230
|
*/
|
|
266
231
|
type: "graphqlHookCall";
|
|
267
232
|
importModule: string;
|
|
268
233
|
/**
|
|
269
|
-
* Hooks
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
* Each hook is reported as `kind = "client"` unless a pack overrides
|
|
274
|
-
* that through the enclosing `DiscoveryPattern.kind`.
|
|
234
|
+
* Hooks on that import, each with the operation type it performs
|
|
235
|
+
* (`useQuery` performs a query). The type is used when the document's
|
|
236
|
+
* header cannot be read. A matched call is a `client` unit unless the
|
|
237
|
+
* enclosing `DiscoveryPattern.kind` says otherwise.
|
|
275
238
|
*/
|
|
276
239
|
hooks: Array<{
|
|
277
240
|
hookName: string;
|
|
@@ -279,21 +242,17 @@ type DiscoveryMatch = {
|
|
|
279
242
|
}>;
|
|
280
243
|
} | {
|
|
281
244
|
/**
|
|
282
|
-
* An imperative Apollo
|
|
283
|
-
* `client.mutate({ mutation })
|
|
284
|
-
*
|
|
285
|
-
*
|
|
245
|
+
* An imperative Apollo Client call: `client.query({ query })`,
|
|
246
|
+
* `client.mutate({ mutation })` or `client.subscribe({ query })`. The
|
|
247
|
+
* document is on a config property instead of the first argument,
|
|
248
|
+
* which is why this is separate from `graphqlHookCall`.
|
|
286
249
|
*
|
|
287
|
-
* Discovery only
|
|
288
|
-
* `ApolloClient`) is imported,
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
* property that contains the gql document
|
|
294
|
-
* (`"query"`, `"mutation"`, and `"query"` respectively). The method
|
|
295
|
-
* name decides the operation type when the gql document's header is
|
|
296
|
-
* anonymous. When the document has a name, its header wins.
|
|
250
|
+
* Discovery fires only when the named constructor (usually
|
|
251
|
+
* `ApolloClient`) is imported, since otherwise any object with a
|
|
252
|
+
* `query` method would match. Each `methods` entry gives the method,
|
|
253
|
+
* the config property with the document on it, and the operation type
|
|
254
|
+
* to use when the document's header is anonymous. A named header takes
|
|
255
|
+
* precedence.
|
|
297
256
|
*/
|
|
298
257
|
type: "graphqlImperativeCall";
|
|
299
258
|
importModule: string;
|
|
@@ -305,41 +264,29 @@ type DiscoveryMatch = {
|
|
|
305
264
|
}>;
|
|
306
265
|
} | {
|
|
307
266
|
/**
|
|
308
|
-
*
|
|
309
|
-
* boundary. The adapter reads
|
|
310
|
-
* resolves each
|
|
311
|
-
* `exports`), follows barrel re-exports, and emits one
|
|
312
|
-
* unit per exported function.
|
|
313
|
-
* an in-process `function-call` boundary.
|
|
314
|
-
*
|
|
315
|
-
* The bindings this produces have the identity
|
|
316
|
-
* `{ transport: "in-process",
|
|
317
|
-
* semantics: { name: "function-call",
|
|
318
|
-
* package: <pkg.name>,
|
|
319
|
-
* exportPath: [...] },
|
|
320
|
-
* recognition: <pack.name> }`.
|
|
321
|
-
*
|
|
322
|
-
* A sub-path export is identified as, for example,
|
|
323
|
-
* `@suss/behavioral-ir/schemas::BehavioralSummarySchema`, giving
|
|
324
|
-
* `exportPath = ["schemas", "BehavioralSummarySchema"]`. A root export
|
|
325
|
-
* leaves the sub-path segment out.
|
|
267
|
+
* A TypeScript package's public exports, as the provider side of an
|
|
268
|
+
* in-process `function-call` boundary. The adapter reads
|
|
269
|
+
* `package.json`, resolves each entry point (the root `.` and any
|
|
270
|
+
* sub-path in `exports`), follows barrel re-exports, and emits one
|
|
271
|
+
* unit per exported function.
|
|
326
272
|
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
* `
|
|
273
|
+
* Each binding is keyed by package and export path, so
|
|
274
|
+
* `@suss/behavioral-ir/schemas::BehavioralSummarySchema` has
|
|
275
|
+
* `exportPath = ["schemas", "BehavioralSummarySchema"]`. The `types`,
|
|
276
|
+
* `default` and `import` conditions are resolved, with `types`, `main`
|
|
277
|
+
* and `module` as the fallback when there is no `exports` field.
|
|
278
|
+
* Pattern exports (`./utils/*`) and `development` conditions are not.
|
|
331
279
|
*/
|
|
332
280
|
type: "packageExports";
|
|
333
281
|
/**
|
|
334
282
|
* Absolute path to the package's `package.json`. Left out when
|
|
335
|
-
* `workspaces` is set, since the workspace
|
|
283
|
+
* `workspaces` is set, since the workspace manifest lists the packages.
|
|
336
284
|
*/
|
|
337
285
|
packageJsonPath?: string;
|
|
338
286
|
/**
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
* manifest and applies this pattern once per package it finds.
|
|
287
|
+
* Apply the pattern to every package the workspace declares. Those
|
|
288
|
+
* packages belong to the project, so the pack cannot list them; the
|
|
289
|
+
* adapter reads the workspace manifest and applies the pattern to each.
|
|
343
290
|
*/
|
|
344
291
|
workspaces?: true;
|
|
345
292
|
/**
|
|
@@ -355,113 +302,113 @@ type DiscoveryMatch = {
|
|
|
355
302
|
excludeNames?: string[];
|
|
356
303
|
} | {
|
|
357
304
|
/**
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
305
|
+
* The public exports of one module the project lists in `suss.json`,
|
|
306
|
+
* read the way `packageExports` reads a package's entry points: one
|
|
307
|
+
* unit per exported function, class methods surfaced, barrels
|
|
308
|
+
* followed. Each binding is keyed `fn:<module>::<exportName>`. The
|
|
309
|
+
* adapter writes these patterns from the module list, so no pack
|
|
310
|
+
* declares one.
|
|
311
|
+
*/
|
|
312
|
+
type: "moduleSurface";
|
|
313
|
+
/** The module's name, as `suss.json` gives it. */
|
|
314
|
+
module: string;
|
|
315
|
+
/** Absolute paths of the files whose exports are the surface. */
|
|
316
|
+
publicFiles: string[];
|
|
317
|
+
} | {
|
|
318
|
+
/**
|
|
319
|
+
* Methods with one of `methodDecorators` on a class with one of
|
|
320
|
+
* `classDecorators`, the way NestJS declares resolvers. At least one
|
|
321
|
+
* method decorator has to be imported from `importModule`, so a
|
|
322
|
+
* project decorator that only shares a name does not match. A NestJS
|
|
323
|
+
* GraphQL pack sets `classDecorators: ["Resolver"]` and
|
|
324
|
+
* `methodDecorators: ["Query", "Mutation", "ResolveField", "Subscription"]`.
|
|
372
325
|
*
|
|
373
|
-
* The
|
|
374
|
-
* comes
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
* decorator's
|
|
378
|
-
* `"User"`). `fieldName` comes from the method decorator's `{ name }`
|
|
379
|
-
* option when that is set, and otherwise from the method name.
|
|
326
|
+
* The binding comes out as `graphql-resolver(typeName, fieldName)`.
|
|
327
|
+
* `typeName` comes from `methodDecoratorTypeMap` when the method
|
|
328
|
+
* decorator is in it, and otherwise from the class decorator's first
|
|
329
|
+
* argument (`@Resolver(() => User)` gives `"User"`). `fieldName` comes
|
|
330
|
+
* from the method decorator's `{ name }` option, or the method name.
|
|
380
331
|
*/
|
|
381
332
|
type: "decoratedMethod";
|
|
382
333
|
/**
|
|
383
|
-
* The
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
* those. Pass an array of acceptable modules and any one of them
|
|
387
|
-
* matching is enough.
|
|
334
|
+
* The modules a decorator may be imported from. A project that wraps
|
|
335
|
+
* a framework decorator and re-exports it needs its own module listed
|
|
336
|
+
* too. Any one module in the list matching is enough.
|
|
388
337
|
*/
|
|
389
338
|
importModule: string | string[];
|
|
390
339
|
/**
|
|
391
|
-
* Class decorators to
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
* its options instead.
|
|
340
|
+
* Class decorators to match. The first one found on a class supplies
|
|
341
|
+
* `typeName`, and the rest are fallbacks for projects with several
|
|
342
|
+
* wrapper styles. A pack lists only its own framework's decorators and
|
|
343
|
+
* takes a project's wrappers through its options.
|
|
396
344
|
*/
|
|
397
345
|
classDecorators: string[];
|
|
398
346
|
methodDecorators: string[];
|
|
399
347
|
/**
|
|
400
|
-
* Maps a method decorator to the type its field belongs to
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
* says, so an entry here wins over the class decorator's argument.
|
|
348
|
+
* Maps a method decorator to the type its field belongs to. NestJS
|
|
349
|
+
* puts `@Query` on the root `Query` type whatever the class says, so an
|
|
350
|
+
* entry here takes precedence over the class decorator's argument.
|
|
404
351
|
*
|
|
405
|
-
* When the map
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
* of claiming a field the schema does not have.
|
|
352
|
+
* When neither the map nor the class decorator gives a type, the
|
|
353
|
+
* binding goes out with no type and pairs with nothing. That is safer
|
|
354
|
+
* than claiming a field the schema does not have.
|
|
409
355
|
*/
|
|
410
356
|
methodDecoratorTypeMap: Record<string, string>;
|
|
411
357
|
} | {
|
|
412
358
|
/**
|
|
413
|
-
* NestJS-style REST controller
|
|
414
|
-
* `@Controller(
|
|
415
|
-
* `@
|
|
416
|
-
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
* argument, and both of those are optional.
|
|
359
|
+
* A NestJS-style REST controller: a class decorated with
|
|
360
|
+
* `@Controller(prefix?)` and methods decorated with `@Get(subpath?)`,
|
|
361
|
+
* `@Post` and so on. The method decorator's name gives the HTTP method
|
|
362
|
+
* through `methodDecoratorRouteMap`. The path is the class decorator's
|
|
363
|
+
* first argument joined to the method decorator's, and both are
|
|
364
|
+
* optional. The binding comes out as `rest(method, path)`.
|
|
420
365
|
*
|
|
421
|
-
*
|
|
422
|
-
* `
|
|
423
|
-
*
|
|
424
|
-
* are matched by name alone, so a project's own wrapper around the
|
|
425
|
-
* framework's decorator matches once the project lists it in the
|
|
426
|
-
* pack's options.
|
|
427
|
-
*
|
|
428
|
-
* The adapter fills in `DiscoveredUnit.routeInfo` so the binding comes
|
|
429
|
-
* out as `rest(method, path)`.
|
|
366
|
+
* At least one route decorator on a method has to be imported from
|
|
367
|
+
* `importModule`. Class decorators match by name alone, so a project's
|
|
368
|
+
* own wrapper matches once the project lists it in the pack's options.
|
|
430
369
|
*/
|
|
431
370
|
type: "decoratedRoute";
|
|
432
371
|
importModule: string | string[];
|
|
433
372
|
classDecorators: string[];
|
|
434
373
|
/**
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
* become the `method` field on the REST binding, and `"*"` is fine for
|
|
439
|
-
* a catch-all decorator.
|
|
374
|
+
* Decorator name to HTTP method, one entry per verb decorator. The
|
|
375
|
+
* value becomes the REST binding's `method`; use `"*"` for a
|
|
376
|
+
* catch-all decorator such as NestJS's `@All`.
|
|
440
377
|
*/
|
|
441
378
|
methodDecoratorRouteMap: Record<string, string>;
|
|
379
|
+
/**
|
|
380
|
+
* The status the framework sends when a handler returns without
|
|
381
|
+
* stating one, keyed by method decorator, for the decorators whose
|
|
382
|
+
* status differs from the response terminals' `defaultStatusCode`.
|
|
383
|
+
* NestJS sends 201 for `@Post`.
|
|
384
|
+
*/
|
|
385
|
+
defaultStatusCodes?: Record<string, number>;
|
|
386
|
+
/**
|
|
387
|
+
* A method decorator, imported from `importModule`, whose first
|
|
388
|
+
* argument sets the status of every response the handler returns
|
|
389
|
+
* without stating its own, NestJS's `@HttpCode(204)`. It wins over
|
|
390
|
+
* `defaultStatusCodes`.
|
|
391
|
+
*/
|
|
392
|
+
statusCodeDecorator?: string;
|
|
393
|
+
/**
|
|
394
|
+
* The call that puts one path in front of every route the
|
|
395
|
+
* application serves, NestJS's `app.setGlobalPrefix("api")`.
|
|
396
|
+
*/
|
|
397
|
+
globalPrefix?: GlobalPrefixCall;
|
|
442
398
|
} | {
|
|
443
399
|
/**
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
* registration. Used for patterns like:
|
|
400
|
+
* A `for-of` loop over a literal array of route specs, read as one
|
|
401
|
+
* inline registration per element:
|
|
447
402
|
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
* for (const r of routes) app[r.method](r.path, r.handler);
|
|
453
|
-
*
|
|
454
|
-
* `elementShape` declares which keys on each element give the method,
|
|
455
|
-
* the path, and the handler. The loop body has to contain at least
|
|
456
|
-
* one call expression that references the
|
|
457
|
-
* loop variable, which filters out unrelated loops. Nothing else
|
|
458
|
-
* about that call is checked.
|
|
459
|
-
*
|
|
460
|
-
* An iterable that resolves to an `ArrayLiteralExpression`, inline or
|
|
461
|
-
* bound to a `const` one hop away, gets expanded. Cross-file and
|
|
462
|
-
* computed iterables are outside v0.
|
|
403
|
+
* ```ts
|
|
404
|
+
* const routes = [{ method: "get", path: "/users", handler: getUsers }];
|
|
405
|
+
* for (const r of routes) app[r.method](r.path, r.handler);
|
|
406
|
+
* ```
|
|
463
407
|
*
|
|
464
|
-
*
|
|
408
|
+
* `elementShape` says which keys give the method, path and handler.
|
|
409
|
+
* The loop body has to contain a call that uses the loop variable,
|
|
410
|
+
* which filters out unrelated loops. The array can be inline or a
|
|
411
|
+
* `const` one hop away. An array from another file is not expanded.
|
|
465
412
|
*/
|
|
466
413
|
type: "registrationLoop";
|
|
467
414
|
elementShape: {
|
|
@@ -483,33 +430,26 @@ type DiscoveryMatch = {
|
|
|
483
430
|
};
|
|
484
431
|
} | {
|
|
485
432
|
/**
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
* `registrationCall` discovery cannot see today.
|
|
491
|
-
*
|
|
492
|
-
* Each entry in `registrations` describes one virtual route
|
|
493
|
-
* the helper produces. `pathTemplate` and `handlerArg` use
|
|
494
|
-
* `{N}` placeholders that resolve to the call's positional
|
|
495
|
-
* arguments. `{N}` substitutes the argument's literal value
|
|
496
|
-
* (for string-literal args) or its source text (for
|
|
497
|
-
* non-literal args, with the slot marked opaque). `{N}.prop`
|
|
498
|
-
* reads `prop` from the argument's resolved object.
|
|
499
|
-
*
|
|
500
|
-
* `importModule` optionally narrows matches to helpers imported from
|
|
501
|
-
* one specific module, which helps when two packages happen to export
|
|
502
|
-
* a function with the same name.
|
|
433
|
+
* A helper call read as several inline registrations, with the call's
|
|
434
|
+
* arguments substituted into a template for each. It covers helpers
|
|
435
|
+
* like `registerCrud(app, "users", userHandlers)`, which
|
|
436
|
+
* `registrationCall` cannot see.
|
|
503
437
|
*
|
|
504
|
-
*
|
|
438
|
+
* Each `registrations` entry is one route the helper produces.
|
|
439
|
+
* `pathTemplate` and `handlerArg` use `{N}` for the call's Nth
|
|
440
|
+
* argument: its value when it is a string literal, and otherwise its
|
|
441
|
+
* source text with the slot marked opaque. `{N}.prop` reads `prop` from
|
|
442
|
+
* the argument's resolved object. `importModule` narrows matches to a
|
|
443
|
+
* helper imported from that module, for when two packages export the
|
|
444
|
+
* same name.
|
|
505
445
|
*/
|
|
506
446
|
type: "registrationTemplate";
|
|
507
447
|
helperName: string;
|
|
508
448
|
importModule?: string;
|
|
509
449
|
/**
|
|
510
|
-
* Which argument is the routable
|
|
511
|
-
*
|
|
512
|
-
* middleware registered
|
|
450
|
+
* Which argument is the routable. A route the helper registers then
|
|
451
|
+
* belongs to the same app as a route written beside the call, and the
|
|
452
|
+
* middleware registered on that app applies to it too.
|
|
513
453
|
*/
|
|
514
454
|
subject?: {
|
|
515
455
|
argument: number;
|
|
@@ -523,32 +463,23 @@ type DiscoveryMatch = {
|
|
|
523
463
|
}>;
|
|
524
464
|
} | {
|
|
525
465
|
/**
|
|
526
|
-
* Routes declared as JSX elements, the way client-side routers
|
|
527
|
-
*
|
|
528
|
-
*
|
|
529
|
-
*
|
|
530
|
-
*
|
|
531
|
-
*
|
|
532
|
-
* factory call whose first argument is an array of route
|
|
533
|
-
* objects using the same property names).
|
|
466
|
+
* Routes declared as JSX elements, the way client-side routers write
|
|
467
|
+
* them. It reads the tree form, where a child path joins its parent's
|
|
468
|
+
* and an index route takes its parent's path, and the object form,
|
|
469
|
+
* where a factory call takes an array of route objects with the same
|
|
470
|
+
* property names. The pack supplies every name, and the adapter only
|
|
471
|
+
* walks JSX and arrays.
|
|
534
472
|
*
|
|
535
|
-
*
|
|
536
|
-
*
|
|
537
|
-
*
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
* Each route with a readable path becomes one unit whose target
|
|
541
|
-
* is the component the element attribute references, resolved
|
|
542
|
-
* only when the reference is a single identifier. A route whose
|
|
543
|
-
* component cannot be read is still reported, as a boundary with
|
|
544
|
-
* nothing behind it. A route whose path cannot be read gets no path
|
|
545
|
-
* and reports that in a gap instead of guessing at one.
|
|
473
|
+
* Each route with a readable path becomes one unit whose target is the
|
|
474
|
+
* component its element attribute references, when that reference is
|
|
475
|
+
* a single identifier. A route whose component cannot be read is still
|
|
476
|
+
* reported, as a boundary with nothing behind it. A route whose path
|
|
477
|
+
* cannot be read gets no path and a gap saying why.
|
|
546
478
|
*/
|
|
547
479
|
type: "jsxElementRoute";
|
|
548
480
|
/**
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
* import declarations; aliased imports are followed.
|
|
481
|
+
* Modules the route element and factories must be imported from,
|
|
482
|
+
* matched exactly. An aliased import still matches.
|
|
552
483
|
*/
|
|
553
484
|
importModule: string | string[];
|
|
554
485
|
/** The route element's exported name. */
|
|
@@ -578,12 +509,10 @@ type DiscoveryMatch = {
|
|
|
578
509
|
*/
|
|
579
510
|
routeObjectFactories?: string[];
|
|
580
511
|
/**
|
|
581
|
-
* Factory functions that turn JSX route elements into
|
|
582
|
-
*
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
* here is what stops that case from being reported as an
|
|
586
|
-
* unreadable route array.
|
|
512
|
+
* Factory functions that turn JSX route elements into route objects.
|
|
513
|
+
* The JSX walk already reads those elements wherever they appear.
|
|
514
|
+
* Listing the factory keeps a route-object factory that is handed one
|
|
515
|
+
* of these calls from being reported as an unreadable route array.
|
|
587
516
|
*/
|
|
588
517
|
elementsFactories?: string[];
|
|
589
518
|
/**
|
|
@@ -594,23 +523,17 @@ type DiscoveryMatch = {
|
|
|
594
523
|
method: string;
|
|
595
524
|
} | {
|
|
596
525
|
/**
|
|
597
|
-
*
|
|
598
|
-
* imports of the listed packages
|
|
599
|
-
*
|
|
600
|
-
* function
|
|
601
|
-
* `
|
|
602
|
-
* summaries `packageExports` produces.
|
|
603
|
-
*
|
|
604
|
-
* `packages` lists exact package names to track imports of, possibly
|
|
605
|
-
* with a sub-path such as `"@suss/behavioral-ir/schemas"`. Pass
|
|
606
|
-
* several package names to track a family at
|
|
607
|
-
* once. Imports of any other package are ignored.
|
|
526
|
+
* The consumer side of the package-export boundary. The adapter finds
|
|
527
|
+
* imports of the listed packages, records every call site, and emits
|
|
528
|
+
* one `caller` unit per enclosing function, bound as
|
|
529
|
+
* `function-call { package, exportPath }` to pair with the providers
|
|
530
|
+
* `packageExports` finds.
|
|
608
531
|
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
*
|
|
532
|
+
* `packages` takes exact package names, with a sub-path where needed,
|
|
533
|
+
* such as `"@suss/behavioral-ir/schemas"`. Named and default imports
|
|
534
|
+
* are tracked. Namespace imports (`import * as X`) are not yet. When a
|
|
535
|
+
* file in the consumer's repo re-exports the package and another file
|
|
536
|
+
* imports it from there, the unit binds to that intermediate file.
|
|
614
537
|
*/
|
|
615
538
|
type: "packageImport";
|
|
616
539
|
/** Left out when `workspaces` is set. */
|
|
@@ -621,7 +544,61 @@ type DiscoveryMatch = {
|
|
|
621
544
|
* side of the package-export boundary, whichever two they are.
|
|
622
545
|
*/
|
|
623
546
|
workspaces?: true;
|
|
624
|
-
};
|
|
547
|
+
} | TestCaseMatch;
|
|
548
|
+
/**
|
|
549
|
+
* Test cases written as nested calls, the way vitest, jest and mocha
|
|
550
|
+
* write them: `describe("orders", () => { it("cancels", () => ...) })`.
|
|
551
|
+
* Each case becomes a unit named by its title path, the suite titles
|
|
552
|
+
* then its own, joined with ` > `. A title the adapter cannot read as a
|
|
553
|
+
* string keeps its source text and is recorded as unresolved.
|
|
554
|
+
*/
|
|
555
|
+
interface TestCaseMatch {
|
|
556
|
+
type: "testCase";
|
|
557
|
+
style: "block";
|
|
558
|
+
/** The module the runner's functions are imported from, e.g. "vitest". */
|
|
559
|
+
importModule: string;
|
|
560
|
+
/** The functions that open a group of cases, e.g. ["describe", "suite"]. */
|
|
561
|
+
suiteNames: string[];
|
|
562
|
+
/** The functions that declare one case, e.g. ["it", "test"]. */
|
|
563
|
+
caseNames: string[];
|
|
564
|
+
/**
|
|
565
|
+
* Modifiers that mean the case does not run, written after a suite
|
|
566
|
+
* or case name: `it.skip`, `describe.todo`. A case under a skipped
|
|
567
|
+
* suite is skipped too.
|
|
568
|
+
*/
|
|
569
|
+
skipModifiers: string[];
|
|
570
|
+
/**
|
|
571
|
+
* Modifiers that take arguments of their own and return the function
|
|
572
|
+
* to call, such as `it.skipIf(condition)`. The case called through one
|
|
573
|
+
* is read like any other.
|
|
574
|
+
*/
|
|
575
|
+
argumentModifiers: string[];
|
|
576
|
+
/**
|
|
577
|
+
* Modifiers that take a table and declare one case per row, such as
|
|
578
|
+
* `it.each(rows)`. The runner fills the title in per row, so the case
|
|
579
|
+
* is read once, under the title as written, and that title is
|
|
580
|
+
* recorded as unresolved.
|
|
581
|
+
*/
|
|
582
|
+
rowModifiers: string[];
|
|
583
|
+
/**
|
|
584
|
+
* How the runner replaces something before a case runs. `object` is
|
|
585
|
+
* the runner's helper object, imported from `importModule`.
|
|
586
|
+
* `moduleMethods` replace a whole module given its specifier
|
|
587
|
+
* (`vi.mock("./orders.js")`), and `memberMethods` replace one member
|
|
588
|
+
* given the object and the member's name (`vi.spyOn(orders, "cancel")`).
|
|
589
|
+
*/
|
|
590
|
+
mocks?: {
|
|
591
|
+
object: string;
|
|
592
|
+
moduleMethods: string[];
|
|
593
|
+
memberMethods: string[];
|
|
594
|
+
};
|
|
595
|
+
/**
|
|
596
|
+
* Walk only these files, matched on whole path segments from the end,
|
|
597
|
+
* so `src/orders.test.ts` matches `/repo/pkg/src/orders.test.ts`. Left
|
|
598
|
+
* out, every file that imports the runner is walked.
|
|
599
|
+
*/
|
|
600
|
+
files?: string[];
|
|
601
|
+
}
|
|
625
602
|
type BindingExtraction = {
|
|
626
603
|
method: {
|
|
627
604
|
type: "fromRegistration";
|
|
@@ -657,11 +634,9 @@ type BindingExtraction = {
|
|
|
657
634
|
property: string;
|
|
658
635
|
} | {
|
|
659
636
|
/**
|
|
660
|
-
* The route path comes from where the file is on disk,
|
|
661
|
-
* Next.js and React Router describe
|
|
662
|
-
*
|
|
663
|
-
* about files and the pack knows what the framework does with their
|
|
664
|
-
* names.
|
|
637
|
+
* The route path comes from where the file is on disk, the way
|
|
638
|
+
* Next.js and React Router describe routes. The pack gives the
|
|
639
|
+
* framework's filename convention, and the adapter applies it.
|
|
665
640
|
*
|
|
666
641
|
* `app/api/orders/[id]/route.ts` under `{ root: "app",
|
|
667
642
|
* dropBasenames: ["route"], dynamic: "brackets" }` comes out as
|
|
@@ -707,8 +682,8 @@ type BindingExtraction = {
|
|
|
707
682
|
* `decoratorArgument` reads the argument off the same decorator the
|
|
708
683
|
* match selected the handler by, so `@EventPattern("order.placed")`
|
|
709
684
|
* gives "order.placed". `literal` is for a wire whose channel the
|
|
710
|
-
* library fixes. `unstated`
|
|
711
|
-
* not,
|
|
685
|
+
* library fixes. `unstated` means the wire is known and the channel is
|
|
686
|
+
* not, and it pairs the same as a null channel.
|
|
712
687
|
*/
|
|
713
688
|
type ChannelSource = {
|
|
714
689
|
from: "decoratorArgument";
|
|
@@ -721,15 +696,10 @@ type ChannelSource = {
|
|
|
721
696
|
};
|
|
722
697
|
/**
|
|
723
698
|
* A binding the pattern states outright, for a boundary the match
|
|
724
|
-
* cannot read from the source.
|
|
725
|
-
*
|
|
726
|
-
*
|
|
727
|
-
*
|
|
728
|
-
* was pushed into a callback, which pack health then reports as an
|
|
729
|
-
* ast-link. This is the same vocabulary `DiscoveredCustomUnit` already
|
|
730
|
-
* has, declared instead of returned. Message bus only for now; the
|
|
731
|
-
* design note in the proposals directory says what comes next and why
|
|
732
|
-
* the rest stays put.
|
|
699
|
+
* cannot read from the source. `bindingExtraction` only covers REST, so
|
|
700
|
+
* a declarative pack for a queue or a topic states its binding here
|
|
701
|
+
* instead of writing a callback. The fields are the ones a
|
|
702
|
+
* `DiscoveredCustomUnit` returns. Only the message bus is supported.
|
|
733
703
|
*/
|
|
734
704
|
type DeclaredBinding = {
|
|
735
705
|
semantics: "message-bus";
|
|
@@ -789,6 +759,40 @@ interface WrapperMethodRegistration extends WrapperFunctionShape {
|
|
|
789
759
|
*/
|
|
790
760
|
arity?: number;
|
|
791
761
|
}
|
|
762
|
+
/**
|
|
763
|
+
* A call on the application object that puts one path in front of
|
|
764
|
+
* every route, with the path as its first argument. The adapter reads
|
|
765
|
+
* it in whichever file makes it, and applies the prefix to every route
|
|
766
|
+
* the pattern finds when all such calls in the run agree on it.
|
|
767
|
+
*/
|
|
768
|
+
interface GlobalPrefixCall {
|
|
769
|
+
/** The method on the application object, NestJS's `setGlobalPrefix`. */
|
|
770
|
+
method: string;
|
|
771
|
+
/**
|
|
772
|
+
* How the application object is made: `NestFactory.create(...)`, where
|
|
773
|
+
* `NestFactory` is imported from `@nestjs/core`. A call on anything
|
|
774
|
+
* else is not read, including a parameter some caller passes anything
|
|
775
|
+
* else to.
|
|
776
|
+
*/
|
|
777
|
+
application: {
|
|
778
|
+
importModule: string;
|
|
779
|
+
importName: string;
|
|
780
|
+
factory: string;
|
|
781
|
+
};
|
|
782
|
+
/**
|
|
783
|
+
* The routes the prefix leaves out, listed under `option` on the
|
|
784
|
+
* object the call takes second. An entry is a path, or an object with
|
|
785
|
+
* the path under `pathKey` and the method under `methodKey`, written
|
|
786
|
+
* as a member of the library's method enum. `methods` maps each
|
|
787
|
+
* member's number to its verb, with `*` for every verb.
|
|
788
|
+
*/
|
|
789
|
+
exclude?: {
|
|
790
|
+
option: string;
|
|
791
|
+
pathKey: string;
|
|
792
|
+
methodKey: string;
|
|
793
|
+
methods: Record<number, string>;
|
|
794
|
+
};
|
|
795
|
+
}
|
|
792
796
|
/** A wrapper handed to the routable's constructor as an option. */
|
|
793
797
|
interface WrapperOptionRegistration extends WrapperFunctionShape {
|
|
794
798
|
/** The option the wrapper is under, e.g. "defaultHook". */
|
|
@@ -804,22 +808,17 @@ interface DiscoveryPattern {
|
|
|
804
808
|
/** A binding the pattern states outright. See `DeclaredBinding`. */
|
|
805
809
|
binding?: DeclaredBinding;
|
|
806
810
|
/**
|
|
807
|
-
* How the routable this pattern discovers (Express's `Router()`,
|
|
808
|
-
*
|
|
809
|
-
*
|
|
810
|
-
* `
|
|
811
|
-
*
|
|
812
|
-
* because mount discovery reuses that match's `importModule` and
|
|
813
|
-
* `importName` to work out which variables in a file are the routable
|
|
814
|
-
* that a mount call is being made on.
|
|
811
|
+
* How the routable this pattern discovers (Express's `Router()`, Hono's
|
|
812
|
+
* `new Hono()`) is mounted on another under a path prefix, as in
|
|
813
|
+
* `app.use(prefix, router)` or `app.route(prefix, sub)`. It only applies
|
|
814
|
+
* when `match.type` is `"registrationCall"`, since mount discovery uses
|
|
815
|
+
* that match's `importModule` and `importName` to find the routables.
|
|
815
816
|
*
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
* declared in the mounting file or, by following the mounted value
|
|
819
|
-
* through an import, in whichever file declares it. A mount whose
|
|
817
|
+
* The adapter adds the prefix to every route on the mounted value, in
|
|
818
|
+
* the mounting file or in the file an import leads to. A mount whose
|
|
820
819
|
* prefix is not a string literal, or whose target the resolution store
|
|
821
|
-
* cannot follow
|
|
822
|
-
*
|
|
820
|
+
* cannot follow, adds nothing, and its routes keep the path they were
|
|
821
|
+
* written with.
|
|
823
822
|
*/
|
|
824
823
|
mount?: {
|
|
825
824
|
/** Method name that registers a sub-router at a prefix, e.g. "use" or "route". */
|
|
@@ -844,22 +843,14 @@ interface DiscoveryPattern {
|
|
|
844
843
|
*/
|
|
845
844
|
wraps?: WrapperRegistration;
|
|
846
845
|
/**
|
|
847
|
-
*
|
|
848
|
-
* specifiers
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
*
|
|
852
|
-
* "match every file" choice, usually because the pattern keys on
|
|
853
|
-
* something other than imports. The fetch runtime does that, since it
|
|
854
|
-
* matches global `fetch(...)` calls.
|
|
855
|
-
*
|
|
856
|
-
* Matching is by prefix on the import module specifier. An entry of
|
|
857
|
-
* `"@nestjs/graphql"` matches `from "@nestjs/graphql"` and
|
|
858
|
-
* `from "@nestjs/graphql/dist/foo"` and any other sub-path.
|
|
846
|
+
* Run this pattern only on files that import one of these module
|
|
847
|
+
* specifiers or a sub-path of one, so `"@nestjs/graphql"` also matches
|
|
848
|
+
* `"@nestjs/graphql/dist/foo"`. An empty array runs it on every file, the
|
|
849
|
+
* same as leaving it unset. Write `[]` out when that is deliberate, as
|
|
850
|
+
* the fetch runtime does for global `fetch(...)` calls.
|
|
859
851
|
*
|
|
860
|
-
*
|
|
861
|
-
*
|
|
862
|
-
* resolution.
|
|
852
|
+
* The gate only saves time. The closure walk and the passes after
|
|
853
|
+
* discovery can still reach every loaded file through symbol resolution.
|
|
863
854
|
*/
|
|
864
855
|
requiresImport?: string[];
|
|
865
856
|
}
|
|
@@ -869,13 +860,10 @@ type TerminalMatch = {
|
|
|
869
860
|
} | {
|
|
870
861
|
type: "returnStatement";
|
|
871
862
|
/**
|
|
872
|
-
* Skip
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
*
|
|
876
|
-
* terminals, one from the wrapping returnStatement and one from
|
|
877
|
-
* the inner method-call chain. Bare returns (`return user`,
|
|
878
|
-
* `return { id }`, `return await fn()`) still match.
|
|
863
|
+
* Skip a return whose value is a call or `new` expression. Set it when
|
|
864
|
+
* a `parameterMethodCall` terminal already matches the inner call, so
|
|
865
|
+
* `return reply.send(...)` produces one terminal instead of two.
|
|
866
|
+
* Returns like `return user` and `return await fn()` still match.
|
|
879
867
|
*/
|
|
880
868
|
excludeCallReturns?: boolean;
|
|
881
869
|
} | {
|
|
@@ -896,22 +884,16 @@ type TerminalMatch = {
|
|
|
896
884
|
type: "functionCall";
|
|
897
885
|
functionName: string;
|
|
898
886
|
/**
|
|
899
|
-
* Only match when the name was imported from one of these modules
|
|
900
|
-
*
|
|
901
|
-
* prefix: "react-router" also matches "react-router/server".
|
|
887
|
+
* Only match when the name was imported from one of these modules,
|
|
888
|
+
* by prefix, the same as `DiscoveryPattern.requiresImport`.
|
|
902
889
|
*
|
|
903
|
-
* Set it whenever the function belongs to a library
|
|
904
|
-
*
|
|
905
|
-
*
|
|
906
|
-
*
|
|
907
|
-
*
|
|
908
|
-
*
|
|
909
|
-
*
|
|
910
|
-
* A pack should generally not target a project's own helper. Declare
|
|
911
|
-
* the envelope structure instead, with a `returnShape` terminal, and
|
|
912
|
-
* the adapter follows a returned call into the project and reads
|
|
913
|
-
* the helper's parameters. That covers a helper whatever it is called
|
|
914
|
-
* and whatever order its arguments come in.
|
|
890
|
+
* Set it whenever the function belongs to a library. A bare name also
|
|
891
|
+
* matches the project's own functions, and `json` is a common name
|
|
892
|
+
* for a project's response helper. Reading a library's argument order
|
|
893
|
+
* into one of those gives a confident wrong answer. To cover a project
|
|
894
|
+
* helper, declare the envelope with a `returnShape` terminal instead:
|
|
895
|
+
* the adapter follows a returned call into the project and reads the
|
|
896
|
+
* helper's parameters, whatever the helper is called.
|
|
915
897
|
*/
|
|
916
898
|
requiresImport?: string[];
|
|
917
899
|
} | {
|
|
@@ -924,26 +906,21 @@ type TerminalMatch = {
|
|
|
924
906
|
type: "jsxReturn";
|
|
925
907
|
} | {
|
|
926
908
|
/**
|
|
927
|
-
*
|
|
928
|
-
*
|
|
929
|
-
*
|
|
930
|
-
*
|
|
931
|
-
*
|
|
932
|
-
* handler
|
|
933
|
-
* `findTerminals` has nothing to match. A pack that always expects
|
|
934
|
-
* explicit returns (HTTP handlers) should leave this out of
|
|
935
|
-
* its terminals. A pack for callback bodies (React handlers,
|
|
936
|
-
* `useEffect` bodies, Node `.on(...)` callbacks) should include it.
|
|
909
|
+
* The implicit fall-through at the end of a function body. It fires
|
|
910
|
+
* when the last statement is neither a return nor a throw, which is
|
|
911
|
+
* how most event handlers and effect bodies end. Without it, such a
|
|
912
|
+
* summary comes out with no transitions. Packs for callback bodies
|
|
913
|
+
* (React handlers, `useEffect`, Node `.on(...)`) include it, and HTTP
|
|
914
|
+
* handler packs that expect explicit returns leave it out.
|
|
937
915
|
*/
|
|
938
916
|
type: "functionFallthrough";
|
|
939
917
|
} | {
|
|
940
918
|
/**
|
|
941
919
|
* A call to the parameter at this position, `next()` inside a
|
|
942
|
-
* middleware.
|
|
943
|
-
*
|
|
944
|
-
*
|
|
945
|
-
*
|
|
946
|
-
* composition tells the two apart.
|
|
920
|
+
* middleware. Packs do not declare it; the adapter builds it from
|
|
921
|
+
* `DiscoveryPattern.wraps.continuationParam`. A wrapper path that
|
|
922
|
+
* hands control on then ends in a `delegate` output, and composition
|
|
923
|
+
* uses that to tell it from a path that responds first.
|
|
947
924
|
*/
|
|
948
925
|
type: "parameterCall";
|
|
949
926
|
parameterPosition: number;
|
|
@@ -981,10 +958,21 @@ interface TerminalExtraction {
|
|
|
981
958
|
defaultStatusCode?: number;
|
|
982
959
|
}
|
|
983
960
|
interface TerminalPattern {
|
|
984
|
-
/**
|
|
985
|
-
|
|
961
|
+
/**
|
|
962
|
+
* What kind of output this terminal produces. An `exit` terminal ends
|
|
963
|
+
* the process, so a path that reaches one goes no further, and the
|
|
964
|
+
* status code it extracts is the exit code.
|
|
965
|
+
*/
|
|
966
|
+
kind: "response" | "throw" | "return" | "render" | "delegate" | "exit";
|
|
986
967
|
match: TerminalMatch;
|
|
987
968
|
extraction: TerminalExtraction;
|
|
969
|
+
/**
|
|
970
|
+
* Read in every unit, whichever pack discovered it, the way an
|
|
971
|
+
* invocation recognizer is. A runtime's own way of ending the process,
|
|
972
|
+
* `process.exit(n)`, ends a route handler and a helper alike, and the
|
|
973
|
+
* pack that declares it discovers no units of its own.
|
|
974
|
+
*/
|
|
975
|
+
inEveryUnit?: boolean;
|
|
988
976
|
/**
|
|
989
977
|
* On a throw terminal: the framework turns the thrown status into
|
|
990
978
|
* the wire response, so a resolved status makes the output a
|
|
@@ -1007,9 +995,8 @@ interface ContractPattern {
|
|
|
1007
995
|
};
|
|
1008
996
|
/**
|
|
1009
997
|
* The properties an endpoint states its HTTP method and path under.
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
1012
|
-
* and the adapter reads whatever it is told.
|
|
998
|
+
* The names belong to the library, so the pack supplies them. ts-rest
|
|
999
|
+
* and zod-openapi both use `method` and `path`.
|
|
1013
1000
|
*/
|
|
1014
1001
|
methodProperty: string;
|
|
1015
1002
|
pathProperty: string;
|
|
@@ -1073,33 +1060,23 @@ type InputMappingPattern = {
|
|
|
1073
1060
|
wholeParamRole?: string;
|
|
1074
1061
|
} | {
|
|
1075
1062
|
/**
|
|
1076
|
-
*
|
|
1077
|
-
* parameter's name as its role
|
|
1078
|
-
* reachable-closure pass for internal library functions,
|
|
1079
|
-
*
|
|
1080
|
-
*
|
|
1081
|
-
* same way `objectParam` captures them, so `(ctx, { userId })`
|
|
1082
|
-
* gives two inputs, `ctx` and `userId`.
|
|
1063
|
+
* One input per declared parameter, in source order, with the
|
|
1064
|
+
* parameter's name as its role unless `defaultRole` is set. The
|
|
1065
|
+
* reachable-closure pass uses it for internal library functions,
|
|
1066
|
+
* where no framework assigns roles. Destructured parameters work as
|
|
1067
|
+
* in `objectParam`, so `(ctx, { userId })` gives `ctx` and `userId`.
|
|
1083
1068
|
*/
|
|
1084
1069
|
type: "allPositional";
|
|
1085
1070
|
defaultRole?: string;
|
|
1086
1071
|
} | {
|
|
1087
1072
|
/**
|
|
1088
|
-
*
|
|
1089
|
-
* parameter
|
|
1090
|
-
*
|
|
1091
|
-
*
|
|
1092
|
-
*
|
|
1093
|
-
* `
|
|
1094
|
-
*
|
|
1095
|
-
* For `@nestjs/graphql` resolvers:
|
|
1096
|
-
* `{ "Args": "args", "Parent": "parent",
|
|
1097
|
-
* "Context": "context", "Info": "info" }`.
|
|
1098
|
-
*
|
|
1099
|
-
* Decorators are matched by name alone, so if several frameworks
|
|
1100
|
-
* define `@Args`, all of them map. Packs that need to
|
|
1101
|
-
* tell them apart by import module can add that later, once there
|
|
1102
|
-
* is a use case worth the cost.
|
|
1073
|
+
* NestJS-style parameter decorators. The adapter looks up each
|
|
1074
|
+
* parameter's first decorator in `decoratorRoleMap`. A match gives the
|
|
1075
|
+
* role, and a parameter that matches nothing gets `defaultRole` or is
|
|
1076
|
+
* skipped. For `@nestjs/graphql` resolvers the map is
|
|
1077
|
+
* `{ Args: "args", Parent: "parent", Context: "context", Info: "info" }`.
|
|
1078
|
+
* Decorators match by name alone, so every framework's `@Args` maps
|
|
1079
|
+
* the same way.
|
|
1103
1080
|
*/
|
|
1104
1081
|
type: "decoratedParams";
|
|
1105
1082
|
decoratorRoleMap: Record<string, string>;
|
|
@@ -1138,31 +1115,57 @@ interface ResponsePropertyMapping {
|
|
|
1138
1115
|
* say so.
|
|
1139
1116
|
*/
|
|
1140
1117
|
interface TransparentWrapper {
|
|
1141
|
-
/** Callee text as written, e.g. "Sentry.wrapHandler". */
|
|
1142
|
-
callee: string;
|
|
1143
|
-
/** Which argument the wrapped function is passed as. */
|
|
1144
|
-
argument: number;
|
|
1145
1118
|
/**
|
|
1146
|
-
* The module
|
|
1147
|
-
*
|
|
1119
|
+
* The module that exports the wrapper, e.g. "@sentry/aws-serverless".
|
|
1120
|
+
* A call matches whatever the project calls the import, and a local
|
|
1121
|
+
* function spelled the same way does not match.
|
|
1148
1122
|
*/
|
|
1149
1123
|
module: string;
|
|
1124
|
+
/** The name the module exports the wrapper under, e.g. "wrapHandler". */
|
|
1125
|
+
name: string;
|
|
1126
|
+
/** Which argument the wrapped function is passed as. */
|
|
1127
|
+
argument: number;
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* A library decorator that fills the parameter it decorates. The library
|
|
1131
|
+
* builds the value, so nothing in the project describes it, and the
|
|
1132
|
+
* decorator call is the only thing that says what the parameter is.
|
|
1133
|
+
*/
|
|
1134
|
+
interface ParameterSupplier {
|
|
1135
|
+
/** The module that exports the decorator, e.g. "@nestjs/typeorm". */
|
|
1136
|
+
module: string;
|
|
1137
|
+
/** The name the module exports the decorator under. */
|
|
1138
|
+
name: string;
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* A command-line parser a runtime or library provides, and where its
|
|
1142
|
+
* settings say which flags a command takes. `parseArgs({ args, options })`
|
|
1143
|
+
* from `node:util` is one: its options object lists every flag, and
|
|
1144
|
+
* nothing else in the command's body ever spells them.
|
|
1145
|
+
*/
|
|
1146
|
+
interface ArgumentParser {
|
|
1147
|
+
/** The module that exports the parser, e.g. `"node:util"`. */
|
|
1148
|
+
module: string;
|
|
1149
|
+
/** The name the module exports it under, e.g. `"parseArgs"`. */
|
|
1150
|
+
name: string;
|
|
1151
|
+
/** Which argument the settings object is passed as. */
|
|
1152
|
+
argument: number;
|
|
1153
|
+
/** The settings key the command-line arguments are passed under, e.g. `"args"`. */
|
|
1154
|
+
argsKey: string;
|
|
1155
|
+
/** The settings key whose object has one key per flag, e.g. `"options"`. */
|
|
1156
|
+
optionsKey: string;
|
|
1150
1157
|
}
|
|
1151
1158
|
interface PatternPack {
|
|
1152
1159
|
name: string;
|
|
1153
1160
|
/**
|
|
1154
|
-
*
|
|
1155
|
-
*
|
|
1156
|
-
* Format is opaque to the adapter, so semver or a content hash both
|
|
1157
|
-
* work.
|
|
1161
|
+
* The pack's version, part of the cache key. Change it whenever a change
|
|
1162
|
+
* affects discovered units or summaries; any string works.
|
|
1158
1163
|
*
|
|
1159
|
-
*
|
|
1160
|
-
*
|
|
1161
|
-
*
|
|
1162
|
-
*
|
|
1163
|
-
*
|
|
1164
|
-
* A pack with nothing to stamp comes out as `"unset"`, and a warm cache
|
|
1165
|
-
* will then serve results for code that has since changed.
|
|
1164
|
+
* It is optional because the CLI hashes the pack file it loaded and the
|
|
1165
|
+
* config it passed into the key, so an edit invalidates the cache
|
|
1166
|
+
* whether or not the version moved. A host that loads packs another way
|
|
1167
|
+
* has to do that itself. A pack with nothing to stamp is keyed as
|
|
1168
|
+
* `"unset"`, and a warm cache then serves stale results after an edit.
|
|
1166
1169
|
*/
|
|
1167
1170
|
version?: string;
|
|
1168
1171
|
/**
|
|
@@ -1196,18 +1199,12 @@ interface PatternPack {
|
|
|
1196
1199
|
*/
|
|
1197
1200
|
requestSpelling?: RequestSpellingMetadata;
|
|
1198
1201
|
/**
|
|
1199
|
-
*
|
|
1200
|
-
*
|
|
1201
|
-
*
|
|
1202
|
-
* does
|
|
1203
|
-
*
|
|
1204
|
-
*
|
|
1205
|
-
* that does not fit it.
|
|
1206
|
-
*
|
|
1207
|
-
* The pack's `name` separately fills in `BoundaryBinding.recognition`
|
|
1208
|
-
* on the summaries, so `{ transport, recognition }` come from
|
|
1209
|
-
* the pack directly and the adapter derives `semantics` from the
|
|
1210
|
-
* discovery pattern's binding-extraction rules.
|
|
1202
|
+
* The transport written into `BoundaryBinding.transport` on discovered
|
|
1203
|
+
* units, such as `"http"` or `"in-process"`. It is required so a pack
|
|
1204
|
+
* for React, queues or Lambda invocation never inherits an HTTP default
|
|
1205
|
+
* that does not fit it. The pack's `name` fills in
|
|
1206
|
+
* `BoundaryBinding.recognition`, and the adapter derives `semantics`
|
|
1207
|
+
* from the discovery pattern's binding extraction.
|
|
1211
1208
|
*/
|
|
1212
1209
|
protocol: string;
|
|
1213
1210
|
/**
|
|
@@ -1225,137 +1222,57 @@ interface PatternPack {
|
|
|
1225
1222
|
*/
|
|
1226
1223
|
failureDelivery?: FailureDelivery;
|
|
1227
1224
|
/**
|
|
1228
|
-
*
|
|
1229
|
-
*
|
|
1230
|
-
*
|
|
1231
|
-
*
|
|
1232
|
-
*
|
|
1233
|
-
*
|
|
1234
|
-
* methods, and similar.
|
|
1235
|
-
*
|
|
1236
|
-
* `ctx` is typed `unknown` here because the extractor has no
|
|
1237
|
-
* knowledge of which adapter is driving it; each language adapter
|
|
1238
|
-
* defines its own context type (`TsSubUnitContext` in
|
|
1239
|
-
* `@suss/adapter-typescript`, say) with the primitives a pack needs to
|
|
1240
|
-
* walk the parent's AST. Packs import and cast to the adapter
|
|
1241
|
-
* context they were written against, and that cast is how a pack says
|
|
1242
|
-
* out loud that it requires the TypeScript adapter.
|
|
1225
|
+
* Build extra code units from a parent unit's body, for callbacks the
|
|
1226
|
+
* runtime schedules that are not top-level declarations: React event
|
|
1227
|
+
* handlers on JSX elements, `useEffect` bodies, Node `emitter.on(...)`
|
|
1228
|
+
* handlers. Each returned unit goes through the same extraction as a
|
|
1229
|
+
* discovered one and gets its own summary. Set `terminals` or
|
|
1230
|
+
* `inputMapping` on a sub-unit when it differs from the pack's defaults.
|
|
1243
1231
|
*
|
|
1244
|
-
*
|
|
1245
|
-
*
|
|
1246
|
-
*
|
|
1247
|
-
*
|
|
1248
|
-
* the parent pack's defaults.
|
|
1232
|
+
* `ctx` is `unknown` because each adapter defines its own context type
|
|
1233
|
+
* (`TsSubUnitContext` in `@suss/adapter-typescript`) with the primitives
|
|
1234
|
+
* for walking the parent's syntax tree. A pack casts to the context it
|
|
1235
|
+
* was written against, which ties the pack to that adapter.
|
|
1249
1236
|
*/
|
|
1250
1237
|
subUnits?: (parent: DiscoveredSubUnitParent, ctx: unknown) => DiscoveredSubUnit[];
|
|
1251
1238
|
/**
|
|
1252
|
-
*
|
|
1253
|
-
*
|
|
1254
|
-
*
|
|
1255
|
-
*
|
|
1256
|
-
*
|
|
1257
|
-
*
|
|
1239
|
+
* The pack's own top-level discovery, for a convention none of the
|
|
1240
|
+
* `DiscoveryMatch` variants fits, such as React's component-export
|
|
1241
|
+
* heuristic or Storybook's `.stories.tsx` files. Keeping those in the
|
|
1242
|
+
* pack leaves the `DiscoveryMatch` union to patterns any pack can share.
|
|
1243
|
+
* The adapter calls it once per source file beside the data-driven
|
|
1244
|
+
* discovery, and the units it returns go through the normal pipeline
|
|
1245
|
+
* with the same per-unit overrides.
|
|
1258
1246
|
*
|
|
1259
|
-
*
|
|
1260
|
-
*
|
|
1261
|
-
*
|
|
1262
|
-
*
|
|
1263
|
-
* but baking each one into the central `DiscoveryMatch` union forces
|
|
1264
|
-
* every unrelated pack to know about them. Callbacks leave the central
|
|
1265
|
-
* union for the generic primitives and let each pack own its own
|
|
1266
|
-
* conventions.
|
|
1267
|
-
*
|
|
1268
|
-
* `ctx` is typed `unknown` for the same reason as in `subUnits`: each
|
|
1269
|
-
* adapter ships its own context primitive (`TsDiscoveryContext` in
|
|
1270
|
-
* `@suss/adapter-typescript`) and a pack casts to whichever one it was
|
|
1271
|
-
* written against. That cast is how the pack says it requires the TS
|
|
1272
|
-
* adapter.
|
|
1273
|
-
*
|
|
1274
|
-
* The units you return go through the adapter's normal pipeline. They
|
|
1275
|
-
* get their terminals and effects extracted, sub-units synthesized, and
|
|
1276
|
-
* summaries assembled exactly as units from data-driven discovery
|
|
1277
|
-
* do. Per-unit `terminals` and `inputMapping` overrides on
|
|
1278
|
-
* `DiscoveredUnit` work the same way too.
|
|
1279
|
-
*
|
|
1280
|
-
* **Cross-pack dedup.** When this callback discovers a unit at the
|
|
1281
|
-
* same `(func, kind)` as a unit from another pack's data-driven
|
|
1282
|
-
* discovery, the adapter's cross-pack claim dedup keeps whichever
|
|
1283
|
-
* claimed it first. The order packs appear in the framework list is
|
|
1284
|
-
* what decides precedence.
|
|
1247
|
+
* `ctx` is `unknown` for the same reason as in `subUnits`
|
|
1248
|
+
* (`TsDiscoveryContext` in `@suss/adapter-typescript`). When this finds
|
|
1249
|
+
* a unit at the same function and kind as another pack's discovery, the
|
|
1250
|
+
* pack that comes first in the framework list claims it.
|
|
1285
1251
|
*/
|
|
1286
1252
|
discoverUnits?: (sourceFile: unknown, ctx: unknown) => DiscoveredCustomUnit[];
|
|
1287
1253
|
/**
|
|
1288
|
-
* Per-call
|
|
1289
|
-
*
|
|
1290
|
-
*
|
|
1291
|
-
*
|
|
1292
|
-
*
|
|
1293
|
-
* for each call. Walking skips nested function bodies (those are
|
|
1294
|
-
* their own units with their own recognizer dispatch). The walk is
|
|
1295
|
-
* INDEPENDENT of the existing invocation-effect walker, which is
|
|
1296
|
-
* deliberately narrow (it only captures
|
|
1297
|
-
* `invocation` effects from bare expression statements and container
|
|
1298
|
-
* composition, to avoid double-counting calls that already become
|
|
1299
|
-
* terminals). Recognizers do not have that problem, so they fire on
|
|
1300
|
-
* every call regardless of position, including
|
|
1301
|
-
* `const x = await fn(...)` initializers and nested call args
|
|
1302
|
-
* (which the invocation walker skips). This independence means
|
|
1303
|
-
* recognizer authors can rely on seeing every call in scope.
|
|
1304
|
-
*
|
|
1305
|
-
* **Cross-pack visibility.** Recognizers fire regardless of which pack
|
|
1306
|
-
* discovered the enclosing function, so
|
|
1307
|
-
* `@suss/framework-prisma`'s recognizer can fire on Prisma calls
|
|
1308
|
-
* inside an `@suss/framework-express` handler. Pack authors don't
|
|
1309
|
-
* need to coordinate.
|
|
1310
|
-
*
|
|
1311
|
-
* **Emission contract.** Returning effects ADDS them to the enclosing
|
|
1312
|
-
* default-branch transition, and the generic `invocation` effect is
|
|
1313
|
-
* kept either way (typed effects live alongside the raw
|
|
1314
|
-
* call capture, so inspect can still render the callee text and
|
|
1315
|
-
* arguments while the checker pairs on the typed form). Return `null`
|
|
1316
|
-
* or `[]` for no match.
|
|
1254
|
+
* Per-call recognizers that emit typed `Effect`s beside the generic
|
|
1255
|
+
* `invocation` effect. The adapter calls each one on every call in the
|
|
1256
|
+
* function body, whichever pack discovered the function, and skips
|
|
1257
|
+
* nested functions, which are units of their own. Calls the invocation
|
|
1258
|
+
* walk skips, such as `const x = await fn(...)` initializers, are included.
|
|
1317
1259
|
*
|
|
1318
|
-
*
|
|
1319
|
-
*
|
|
1320
|
-
*
|
|
1321
|
-
*
|
|
1322
|
-
*
|
|
1323
|
-
*
|
|
1324
|
-
* logged to stderr with the file path and line number, and is skipped
|
|
1325
|
-
* for that one call while the extraction carries on. A buggy
|
|
1326
|
-
* recognizer will not crash the run.
|
|
1327
|
-
*
|
|
1328
|
-
* `call` is the language adapter's call-expression handle (opaque
|
|
1329
|
-
* here; ts-morph `CallExpression` in `@suss/adapter-typescript`).
|
|
1330
|
-
* `ctx` is the adapter's recognizer context (source file, an
|
|
1331
|
-
* `extractArgs()` helper that reuses the adapter's own EffectArg
|
|
1332
|
-
* builder). A recognizer casts both to the adapter context it was
|
|
1333
|
-
* written against, which is the same way `subUnits` says a pack
|
|
1334
|
-
* requires the TypeScript adapter.
|
|
1260
|
+
* Returned effects are added to the enclosing default transition, and
|
|
1261
|
+
* the `invocation` effect stays. Return `null` or `[]` for no match.
|
|
1262
|
+
* Nothing dedupes across calls, so a recognizer that should fire once
|
|
1263
|
+
* per identifier tracks that itself. One that throws is logged with its
|
|
1264
|
+
* file and line and skipped for that call. `call` and `ctx` are the
|
|
1265
|
+
* adapter's own types, which a recognizer casts to as `subUnits` does.
|
|
1335
1266
|
*/
|
|
1336
1267
|
invocationRecognizers?: InvocationRecognizer[];
|
|
1337
1268
|
/**
|
|
1338
|
-
*
|
|
1339
|
-
*
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
1342
|
-
* and
|
|
1343
|
-
*
|
|
1344
|
-
*
|
|
1345
|
-
* `@suss/framework-aws-sqs` declares `["@aws-sdk/client-sqs"]`,
|
|
1346
|
-
* `@suss/framework-prisma` declares `["@prisma/client"]`. Without
|
|
1347
|
-
* a gate, a recognizer-only pack walks every file in the project. That
|
|
1348
|
-
* is correct but wasteful in a large monorepo where most files never
|
|
1349
|
-
* import the library.
|
|
1350
|
-
*
|
|
1351
|
-
* A discovery-pattern pack already has a per-pattern `requiresImport`
|
|
1352
|
-
* on `DiscoveryPattern`. This is the pack-level version of that, for a
|
|
1353
|
-
* pack whose ONLY mechanism is recognizers and which has no discovery.
|
|
1354
|
-
*
|
|
1355
|
-
* Empty or undefined means no gate, so the pack walks every file (the
|
|
1356
|
-
* default for universal recognizers like `@suss/runtime-node`'s
|
|
1357
|
-
* process-surface and env-var recognizers, since `process.*` is
|
|
1358
|
-
* available without importing anything).
|
|
1269
|
+
* A pack-level import gate: the pack applies only to files that import
|
|
1270
|
+
* one of these modules, matched by prefix. A pack with only recognizers
|
|
1271
|
+
* uses it the way a discovery pattern uses its own `requiresImport`;
|
|
1272
|
+
* `@suss/framework-prisma` declares `["@prisma/client"]`. Without a gate
|
|
1273
|
+
* the pack walks every file, which gives the same result and is slow in
|
|
1274
|
+
* a large monorepo. Leave it empty for recognizers that apply
|
|
1275
|
+
* everywhere, such as `process.env` reads, which need no import.
|
|
1359
1276
|
*/
|
|
1360
1277
|
requiresImport?: string[];
|
|
1361
1278
|
/**
|
|
@@ -1368,6 +1285,16 @@ interface PatternPack {
|
|
|
1368
1285
|
* ordinary import would.
|
|
1369
1286
|
*/
|
|
1370
1287
|
generatedModuleMarkers?: string[];
|
|
1288
|
+
/**
|
|
1289
|
+
* The directories this library's code generator writes to, as the
|
|
1290
|
+
* project's own configuration says, for a generator that leaves no
|
|
1291
|
+
* marker file. Given the directory of a file that imports by relative
|
|
1292
|
+
* path, it returns the output directories the configuration governing
|
|
1293
|
+
* that file gives. An import into one counts as the gated package, the
|
|
1294
|
+
* same as a directory with a marker in it, even before the generator
|
|
1295
|
+
* has run.
|
|
1296
|
+
*/
|
|
1297
|
+
generatedModuleDirs?: (fromDir: string) => readonly string[];
|
|
1371
1298
|
/**
|
|
1372
1299
|
* Functions the project itself wrote in front of this library, read
|
|
1373
1300
|
* once across the whole project before any file is walked. What the
|
|
@@ -1376,14 +1303,13 @@ interface PatternPack {
|
|
|
1376
1303
|
*/
|
|
1377
1304
|
projectHelpers?: ProjectHelpers;
|
|
1378
1305
|
/**
|
|
1379
|
-
* Environment variables the
|
|
1380
|
-
*
|
|
1381
|
-
*
|
|
1382
|
-
*
|
|
1383
|
-
*
|
|
1384
|
-
*
|
|
1385
|
-
*
|
|
1386
|
-
* scoped family like `@aws-lambda-powertools/`.
|
|
1306
|
+
* Environment variables the library reads from inside node_modules,
|
|
1307
|
+
* where no walk looks. Without them the checker would report a variable
|
|
1308
|
+
* in a template as unused when the library reads it on every invocation.
|
|
1309
|
+
* The adapter emits one marker summary per entry whose `module` some
|
|
1310
|
+
* project file imports, and runtime-config pairing checks the markers
|
|
1311
|
+
* before reporting. `module` matches as a prefix, so one entry covers a
|
|
1312
|
+
* family like `@aws-lambda-powertools/`.
|
|
1387
1313
|
*/
|
|
1388
1314
|
libraryEnvVars?: Array<{
|
|
1389
1315
|
/** Module-specifier prefix the library's imports start with. */
|
|
@@ -1394,16 +1320,18 @@ interface PatternPack {
|
|
|
1394
1320
|
names?: string[];
|
|
1395
1321
|
}>;
|
|
1396
1322
|
/**
|
|
1397
|
-
* Library wrappers that return the function they
|
|
1398
|
-
*
|
|
1399
|
-
*
|
|
1400
|
-
* pack has to say it: a call to `callee` resolves to its
|
|
1401
|
-
* `argument`-th argument.
|
|
1402
|
-
*
|
|
1403
|
-
* `callee` matches the call expression text as written, e.g.
|
|
1404
|
-
* `"Sentry.wrapHandler"`.
|
|
1323
|
+
* Library wrappers that return the function they were handed. A call to
|
|
1324
|
+
* the `name` that `module` exports resolves to its `argument`-th
|
|
1325
|
+
* argument. See `TransparentWrapper`.
|
|
1405
1326
|
*/
|
|
1406
1327
|
transparentWrappers?: TransparentWrapper[];
|
|
1328
|
+
/**
|
|
1329
|
+
* Parameter decorators whose call is the value a container passes
|
|
1330
|
+
* in. A constructor parameter written `@InjectRepository(User) repo`
|
|
1331
|
+
* then reads as that call, and a pack can match `this.repo.find()` by
|
|
1332
|
+
* the decorator it came from.
|
|
1333
|
+
*/
|
|
1334
|
+
parameterSuppliers?: ParameterSupplier[];
|
|
1407
1335
|
/**
|
|
1408
1336
|
* Objects whose properties are the process environment, written as
|
|
1409
1337
|
* the dotted path the code spells, e.g. `"process.env"`. The adapter
|
|
@@ -1412,6 +1340,27 @@ interface PatternPack {
|
|
|
1412
1340
|
* parameter gets its reads reported at the calls that named them.
|
|
1413
1341
|
*/
|
|
1414
1342
|
environmentObjects?: string[];
|
|
1343
|
+
/**
|
|
1344
|
+
* The process's output streams, written the same way, e.g.
|
|
1345
|
+
* `"process.stdout"`. The adapter follows each one into the helpers
|
|
1346
|
+
* it is passed to, so a write through a stream parameter can say
|
|
1347
|
+
* which stream it goes to.
|
|
1348
|
+
*/
|
|
1349
|
+
streamObjects?: string[];
|
|
1350
|
+
/**
|
|
1351
|
+
* Dotted paths a program assigns its exit code to without ending
|
|
1352
|
+
* there, e.g. `"process.exitCode"`. The value assigned, and the
|
|
1353
|
+
* argument of a call an `exit` terminal matches, are where the adapter
|
|
1354
|
+
* starts when it works out which functions' returns become the code.
|
|
1355
|
+
*/
|
|
1356
|
+
exitCodeWrites?: string[];
|
|
1357
|
+
/**
|
|
1358
|
+
* Command-line parsers, read in every unit whichever pack found it.
|
|
1359
|
+
* Each flag a call to one declares becomes an input read of `--flag`
|
|
1360
|
+
* off the parameter the call hands its arguments from, so a command's
|
|
1361
|
+
* intent can list its flags under `receives`.
|
|
1362
|
+
*/
|
|
1363
|
+
argumentParsers?: ArgumentParser[];
|
|
1415
1364
|
/**
|
|
1416
1365
|
* How this library's client object is constructed, so an operation
|
|
1417
1366
|
* summary can say which endpoint its calls go to. Each entry is a
|
|
@@ -1466,41 +1415,26 @@ interface PatternPack {
|
|
|
1466
1415
|
workspace: string;
|
|
1467
1416
|
}>;
|
|
1468
1417
|
/**
|
|
1469
|
-
* Per-
|
|
1470
|
-
*
|
|
1471
|
-
*
|
|
1472
|
-
*
|
|
1473
|
-
*
|
|
1474
|
-
*
|
|
1475
|
-
* A recognizer here is handed a property access, a call, or a tagged
|
|
1476
|
-
* template, and guards its own shapes. The tagged template is there
|
|
1477
|
-
* for a library that takes its whole argument as one, the way
|
|
1478
|
-
* `prisma.$queryRaw` and `gql` do.
|
|
1479
|
-
*
|
|
1480
|
-
* The scope rules are the same as for invocationRecognizers: it fires
|
|
1481
|
-
* on every such node in the function body and skips nested function
|
|
1482
|
-
* bodies. The emission contract is the same, so effects land on the
|
|
1483
|
-
* enclosing default-branch transition.
|
|
1484
|
-
*
|
|
1485
|
-
* The arguments are opaque here and narrowed by the adapter, for the
|
|
1486
|
-
* same reason as in invocationRecognizers.
|
|
1418
|
+
* Per-access recognizers, the counterpart to `invocationRecognizers` for
|
|
1419
|
+
* values read without a call, such as `process.env.X`. A recognizer is
|
|
1420
|
+
* handed a property access, a call or a tagged template, and checks the
|
|
1421
|
+
* node itself. The tagged template covers libraries that take a whole
|
|
1422
|
+
* template as one argument, like `prisma.$queryRaw` and `gql`. Scope,
|
|
1423
|
+
* emission and argument types follow the `invocationRecognizers` rules.
|
|
1487
1424
|
*/
|
|
1488
1425
|
accessRecognizers?: AccessRecognizer[];
|
|
1489
1426
|
/**
|
|
1490
|
-
* What the pack wrote as data
|
|
1491
|
-
*
|
|
1492
|
-
* itself the thing the report says.
|
|
1427
|
+
* What the pack wrote as data, for the pack health report. A pack
|
|
1428
|
+
* written as a hand-rolled walk leaves it out, and the report says so.
|
|
1493
1429
|
*/
|
|
1494
1430
|
declarations?: PackDeclarations;
|
|
1495
1431
|
}
|
|
1496
1432
|
/**
|
|
1497
|
-
*
|
|
1498
|
-
*
|
|
1499
|
-
*
|
|
1500
|
-
*
|
|
1501
|
-
*
|
|
1502
|
-
* answered with a function is code that only its own language runs.
|
|
1503
|
-
* Both are allowed, and the report says which is which.
|
|
1433
|
+
* How much of what a pack matches is declared as data and how much is
|
|
1434
|
+
* code, counted for the pack health report. A link given as data can be
|
|
1435
|
+
* inspected, serialized and run by any adapter. A link given as a
|
|
1436
|
+
* function only runs in its own language. Both are allowed, and the
|
|
1437
|
+
* report counts each kind.
|
|
1504
1438
|
*/
|
|
1505
1439
|
interface PackDeclarations {
|
|
1506
1440
|
declarations: DeclaredMatch[];
|
|
@@ -1523,22 +1457,19 @@ interface DeclaredMatch {
|
|
|
1523
1457
|
example: string | null;
|
|
1524
1458
|
}
|
|
1525
1459
|
/**
|
|
1526
|
-
* Per-call
|
|
1527
|
-
*
|
|
1460
|
+
* Per-call recognizer hook. `PatternPack.invocationRecognizers` says when
|
|
1461
|
+
* it is called and what it returns.
|
|
1528
1462
|
*/
|
|
1529
1463
|
type InvocationRecognizer<TCtx = unknown> = (call: unknown, ctx: TCtx) => Effect[] | null;
|
|
1530
1464
|
/**
|
|
1531
|
-
* Per-
|
|
1532
|
-
*
|
|
1465
|
+
* Per-access recognizer hook. `PatternPack.accessRecognizers` says when
|
|
1466
|
+
* it is called and what it returns.
|
|
1533
1467
|
*/
|
|
1534
1468
|
type AccessRecognizer<TCtx = unknown> = (access: unknown, ctx: TCtx) => Effect[] | null;
|
|
1535
1469
|
/**
|
|
1536
|
-
* The
|
|
1537
|
-
*
|
|
1538
|
-
*
|
|
1539
|
-
* the extractor only so `PatternPack` can refer to it, and the adapter-level
|
|
1540
|
-
* context types like `TsSubUnitContext` narrow `func` to a concrete AST
|
|
1541
|
-
* handle.
|
|
1470
|
+
* The parent unit a `subUnits` hook works inside. `func` is opaque because
|
|
1471
|
+
* each adapter has its own function-root type; adapter context types such
|
|
1472
|
+
* as `TsSubUnitContext` narrow it.
|
|
1542
1473
|
*/
|
|
1543
1474
|
interface DiscoveredSubUnitParent {
|
|
1544
1475
|
/** Handle to the parent's function body. Opaque at extractor level. */
|
|
@@ -1549,15 +1480,11 @@ interface DiscoveredSubUnitParent {
|
|
|
1549
1480
|
kind: string;
|
|
1550
1481
|
}
|
|
1551
1482
|
/**
|
|
1552
|
-
*
|
|
1553
|
-
*
|
|
1554
|
-
*
|
|
1555
|
-
*
|
|
1556
|
-
*
|
|
1557
|
-
*
|
|
1558
|
-
* Pack authors only ever see opaque handles: `func` is whatever the adapter's
|
|
1559
|
-
* primitive returned, and the adapter narrows it to its concrete
|
|
1560
|
-
* function-root type (`FunctionRoot` in `@suss/adapter-typescript`).
|
|
1483
|
+
* One top-level unit a pack's `discoverUnits` hook found. The adapter
|
|
1484
|
+
* widens it into its own internal unit type and runs it through the
|
|
1485
|
+
* normal extraction pipeline. `func` is whatever the adapter's primitive
|
|
1486
|
+
* returned, and the adapter narrows it to its function-root type
|
|
1487
|
+
* (`FunctionRoot` in `@suss/adapter-typescript`).
|
|
1561
1488
|
*/
|
|
1562
1489
|
interface DiscoveredCustomUnit {
|
|
1563
1490
|
/** Function body handle, opaque here. */
|
|
@@ -1567,10 +1494,9 @@ interface DiscoveredCustomUnit {
|
|
|
1567
1494
|
/** Discovered name (e.g. "UserCard"). */
|
|
1568
1495
|
name: string;
|
|
1569
1496
|
/**
|
|
1570
|
-
* The unit
|
|
1571
|
-
*
|
|
1572
|
-
*
|
|
1573
|
-
* adapter puts both on the unit's function-call binding.
|
|
1497
|
+
* The module the unit is exported from and its export name, when the
|
|
1498
|
+
* pack states them. Server actions use this so intent and keyed pairing
|
|
1499
|
+
* can refer to them. The adapter puts both on the function-call binding.
|
|
1574
1500
|
*/
|
|
1575
1501
|
functionCallInfo?: {
|
|
1576
1502
|
module: string;
|
|
@@ -1587,19 +1513,15 @@ interface DiscoveredCustomUnit {
|
|
|
1587
1513
|
*/
|
|
1588
1514
|
inputMapping?: InputMappingPattern;
|
|
1589
1515
|
/**
|
|
1590
|
-
* REST route
|
|
1591
|
-
*
|
|
1592
|
-
*
|
|
1593
|
-
*
|
|
1594
|
-
*
|
|
1595
|
-
* `routeInfo`, and the discoverUnits callback never has to reach into
|
|
1596
|
-
* the adapter's binding machinery. Either half is null when the
|
|
1597
|
-
* source does not state it, and a binding missing one pairs with
|
|
1598
|
-
* nothing.
|
|
1516
|
+
* The REST route for a unit found through an external manifest, such as
|
|
1517
|
+
* a SAM template's `Events` block, instead of an in-code registration.
|
|
1518
|
+
* The adapter builds a `rest` binding from it, the same one a NestJS
|
|
1519
|
+
* controller gets. Leave either half null when the source does not
|
|
1520
|
+
* state it; a binding missing one pairs with nothing.
|
|
1599
1521
|
*
|
|
1600
|
-
*
|
|
1601
|
-
* per
|
|
1602
|
-
*
|
|
1522
|
+
* A function bound to several routes returns one unit per route. The
|
|
1523
|
+
* per-file claim dedup keys on function, kind, method and path, so all
|
|
1524
|
+
* of them are kept.
|
|
1603
1525
|
*/
|
|
1604
1526
|
routeInfo?: {
|
|
1605
1527
|
method: string | null;
|
|
@@ -1645,9 +1567,8 @@ interface DiscoveredCustomUnit {
|
|
|
1645
1567
|
metadata?: Record<string, unknown>;
|
|
1646
1568
|
}
|
|
1647
1569
|
/**
|
|
1648
|
-
*
|
|
1649
|
-
*
|
|
1650
|
-
* pipeline used for top-level-discovered units.
|
|
1570
|
+
* One child unit a pack's `subUnits` hook built. The adapter extracts and
|
|
1571
|
+
* assembles it the same way as a top-level unit.
|
|
1651
1572
|
*/
|
|
1652
1573
|
interface DiscoveredSubUnit {
|
|
1653
1574
|
/** Function body handle, opaque here. */
|
|
@@ -1681,8 +1602,8 @@ interface DiscoveredSubUnit {
|
|
|
1681
1602
|
* again.
|
|
1682
1603
|
*
|
|
1683
1604
|
* The `Cond` type parameter is the language's own handle for a condition
|
|
1684
|
-
* expression (a ts-morph Expression
|
|
1685
|
-
*
|
|
1605
|
+
* expression (a ts-morph Expression in TypeScript, a tree-sitter node in
|
|
1606
|
+
* Python and Ruby). The engine passes it through untouched for a caller
|
|
1686
1607
|
* to parse afterwards, and never looks inside it.
|
|
1687
1608
|
*/
|
|
1688
1609
|
/** Where a condition came from, in terms of what that branch's body does. */
|
|
@@ -1723,18 +1644,18 @@ interface CaseGroup<Cond> {
|
|
|
1723
1644
|
readonly body: StatementBlock<Cond>;
|
|
1724
1645
|
}
|
|
1725
1646
|
/**
|
|
1726
|
-
*
|
|
1647
|
+
* The fields every lowered statement has, whichever construct it is.
|
|
1727
1648
|
*
|
|
1728
1649
|
* The lowering works `exitKind` out by scanning the statement's own
|
|
1729
1650
|
* subtree for a return or a throw, skipping every nested function body.
|
|
1730
1651
|
*
|
|
1731
1652
|
* `callbacks` is the bodies of the functions this statement passes to
|
|
1732
|
-
* calls it makes,
|
|
1653
|
+
* calls it makes, when the language treats those as running for the
|
|
1733
1654
|
* enclosing unit. The engine walks them on the same path as the
|
|
1734
1655
|
* statement, so their branches are the unit's branches. Their `return`
|
|
1735
|
-
*
|
|
1736
|
-
* statement.
|
|
1737
|
-
*
|
|
1656
|
+
* does not leave the unit, so a path that ends inside one continues past
|
|
1657
|
+
* the statement. When a lowering leaves the field out, no callback bodies
|
|
1658
|
+
* are walked.
|
|
1738
1659
|
*/
|
|
1739
1660
|
interface LoweredStatementParts<Cond> {
|
|
1740
1661
|
readonly exitKind: ExitKind;
|
|
@@ -1765,7 +1686,7 @@ type StructuredStatement<Cond = unknown> = (LoweredStatementParts<Cond> & {
|
|
|
1765
1686
|
readonly kind: "try";
|
|
1766
1687
|
readonly tryBody: StatementBlock<Cond>;
|
|
1767
1688
|
readonly catchBody: StatementBlock<Cond> | null;
|
|
1768
|
-
/**
|
|
1689
|
+
/** Kept only for validation. The engine does not model a finally that exits or contains one of the caller's terminals, and never enumerates its conditions. */
|
|
1769
1690
|
readonly finallyBody: StatementBlock<Cond> | null;
|
|
1770
1691
|
}) | (LoweredStatementParts<Cond> & {
|
|
1771
1692
|
readonly kind: "exit";
|
|
@@ -1780,14 +1701,14 @@ type StructuredStatement<Cond = unknown> = (LoweredStatementParts<Cond> & {
|
|
|
1780
1701
|
* value in.
|
|
1781
1702
|
*
|
|
1782
1703
|
* A reader that returns `T | null` makes null mean three things at once: the
|
|
1783
|
-
* source left the value out, the source
|
|
1704
|
+
* source left the value out, the source wrote it and the reader could not
|
|
1784
1705
|
* evaluate it, or several candidates matched and the reader picked none.
|
|
1785
|
-
* Each needs
|
|
1786
|
-
*
|
|
1787
|
-
*
|
|
1706
|
+
* Each needs a different note on the summary, and `T | null` does not show
|
|
1707
|
+
* which case happened, so every new reader has to choose again and some
|
|
1708
|
+
* choose wrong.
|
|
1788
1709
|
*
|
|
1789
1710
|
* The rule that turns a reading into a claim, a library default, or a gap
|
|
1790
|
-
*
|
|
1711
|
+
* is in the summary builder in this package and is not exported.
|
|
1791
1712
|
*/
|
|
1792
1713
|
/** Where in a file a value is written, as byte offsets into that file. */
|
|
1793
1714
|
interface SourceRange {
|
|
@@ -1830,9 +1751,9 @@ declare const absentReading: Reading<never>;
|
|
|
1830
1751
|
*/
|
|
1831
1752
|
declare function unreadableReading<T>(reason: string, range: SourceRange): Reading<T>;
|
|
1832
1753
|
/**
|
|
1833
|
-
* Several values could be right and the reader picked none.
|
|
1834
|
-
*
|
|
1835
|
-
*
|
|
1754
|
+
* Several values could be right and the reader picked none. The candidates
|
|
1755
|
+
* are kept through every later step, so code that can choose among them
|
|
1756
|
+
* still has them.
|
|
1836
1757
|
*
|
|
1837
1758
|
* The range is where somebody should look to see the ambiguity. An ambiguity
|
|
1838
1759
|
* often spans more than one place (two mounts of one router, in two files),
|
|
@@ -1843,8 +1764,7 @@ declare function ambiguousReading<T>(candidates: readonly T[], reason: string, r
|
|
|
1843
1764
|
/**
|
|
1844
1765
|
* A reading paired with what the library does when the source says nothing.
|
|
1845
1766
|
* Only a pack may supply that default, so the value a summary claims for an
|
|
1846
|
-
* unstated field
|
|
1847
|
-
* everything else the pack already declares.
|
|
1767
|
+
* unstated field always comes from a pack's declaration about the library.
|
|
1848
1768
|
*/
|
|
1849
1769
|
interface DefaultedReading<T> {
|
|
1850
1770
|
/** What the source said. */
|
|
@@ -1871,9 +1791,8 @@ declare function mapReading<T, U>(reading: Reading<T>, f: (value: T) => U): Read
|
|
|
1871
1791
|
*
|
|
1872
1792
|
* `f` runs on each of an ambiguous reading's candidates too, and the ones
|
|
1873
1793
|
* that do read come back as the candidates of an ambiguous reading with the
|
|
1874
|
-
* same reason.
|
|
1875
|
-
*
|
|
1876
|
-
* that reads further.
|
|
1794
|
+
* same reason. The alternatives reach the summary instead of being dropped
|
|
1795
|
+
* at the first step that reads further.
|
|
1877
1796
|
*/
|
|
1878
1797
|
declare function andThenReading<T, U>(reading: Reading<T>, f: (value: T, range: SourceRange) => Reading<U>): Reading<U>;
|
|
1879
1798
|
/**
|
|
@@ -1888,10 +1807,10 @@ interface ChosenReading<T> {
|
|
|
1888
1807
|
*/
|
|
1889
1808
|
reading: Reading<T>;
|
|
1890
1809
|
/**
|
|
1891
|
-
* The readings the choice passed over
|
|
1892
|
-
* reading
|
|
1893
|
-
*
|
|
1894
|
-
*
|
|
1810
|
+
* The unreadable or ambiguous readings the choice passed over. A later
|
|
1811
|
+
* reading that supplies the value does not settle an earlier one that
|
|
1812
|
+
* could not be read, so these still go to the builder and their reasons
|
|
1813
|
+
* reach the summary.
|
|
1895
1814
|
*/
|
|
1896
1815
|
passedOver: readonly Reading<T>[];
|
|
1897
1816
|
}
|
|
@@ -1909,32 +1828,29 @@ declare function firstWrittenReading<T>(readings: readonly Reading<T>[]): Chosen
|
|
|
1909
1828
|
* parameters that decide what each of the handler's parameters is, and
|
|
1910
1829
|
* that has to be settled before there is a summary field to fill in.
|
|
1911
1830
|
*
|
|
1912
|
-
* This applies no default and gives no reason, so most
|
|
1913
|
-
*
|
|
1914
|
-
*
|
|
1915
|
-
* review can see it.
|
|
1831
|
+
* This applies no default and gives no reason, so most summary fields must
|
|
1832
|
+
* not be written from it. Pass the reading to the builder instead, which
|
|
1833
|
+
* applies the one rule for readings in a place a reviewer can find.
|
|
1916
1834
|
*
|
|
1917
1835
|
* The identity fields of a boundary binding are the exception, and the path
|
|
1918
|
-
*
|
|
1919
|
-
*
|
|
1920
|
-
*
|
|
1921
|
-
* put there is the value this gives back. Hand the reading over as well and
|
|
1922
|
-
* the reason still becomes a gap.
|
|
1836
|
+
* is one of them. No pack declares a default for what a boundary is called,
|
|
1837
|
+
* so the builder would write the same value this returns. Pass the reading
|
|
1838
|
+
* to the builder as well, so its reason still becomes a gap.
|
|
1923
1839
|
*/
|
|
1924
1840
|
declare function valueToReadFurtherFrom<T>(reading: Reading<T>): T | null;
|
|
1925
1841
|
|
|
1926
1842
|
/**
|
|
1927
|
-
*
|
|
1928
|
-
*
|
|
1843
|
+
* Folds the code registered around a unit, such as middleware, into what
|
|
1844
|
+
* the unit does.
|
|
1929
1845
|
*
|
|
1930
1846
|
* A wrapper is a meta-function: it takes a unit and returns a unit. The
|
|
1931
1847
|
* call to its continuation comes through as a `delegate` transition, so
|
|
1932
1848
|
* a path that ends before that call is a response the caller gets
|
|
1933
1849
|
* instead of the unit's own, and a path that reaches it hands the
|
|
1934
|
-
* request on without responding. The responses
|
|
1935
|
-
* outcomes
|
|
1936
|
-
* outcomes already say what happens on them.
|
|
1937
|
-
*
|
|
1850
|
+
* request on without responding. The responses are added beside the
|
|
1851
|
+
* unit's own outcomes. The pass-throughs are dropped, because the unit's
|
|
1852
|
+
* outcomes already say what happens on them. This package's DESIGN.md
|
|
1853
|
+
* works through an example and lists what composition does not read.
|
|
1938
1854
|
*/
|
|
1939
1855
|
|
|
1940
1856
|
/**
|
|
@@ -1951,6 +1867,57 @@ interface ComposeOptions {
|
|
|
1951
1867
|
*/
|
|
1952
1868
|
declare function composeWrappers(summaries: readonly BehavioralSummary[], options?: ComposeOptions): BehavioralSummary[];
|
|
1953
1869
|
|
|
1870
|
+
/**
|
|
1871
|
+
* The modules a project lists in `suss.json`, as every adapter reads them.
|
|
1872
|
+
*
|
|
1873
|
+
* A module is a name, a root folder, and the files whose exports other
|
|
1874
|
+
* modules are meant to call. The CLI reads the list and hands it to the
|
|
1875
|
+
* adapter with absolute paths. Which file is public when the list does not
|
|
1876
|
+
* say is a fact about the language, so each adapter settles that first.
|
|
1877
|
+
* The rest is the same in all three adapters: which module a file belongs
|
|
1878
|
+
* to, the module stamped on each summary, and the part of the cache key
|
|
1879
|
+
* that makes an edit to the list run discovery again.
|
|
1880
|
+
*/
|
|
1881
|
+
|
|
1882
|
+
/** The recognition on every module export's binding, in each language. */
|
|
1883
|
+
declare const MODULE_SURFACE_RECOGNITION = "modules";
|
|
1884
|
+
interface DeclaredModule {
|
|
1885
|
+
readonly name: string;
|
|
1886
|
+
/** Absolute. Every file under it belongs to the module. */
|
|
1887
|
+
readonly root: string;
|
|
1888
|
+
/**
|
|
1889
|
+
* Absolute. The files whose exports are the module's public surface.
|
|
1890
|
+
* Left out when `suss.json` does not say, and then the adapter's
|
|
1891
|
+
* convention for its language decides.
|
|
1892
|
+
*/
|
|
1893
|
+
readonly public?: readonly string[];
|
|
1894
|
+
}
|
|
1895
|
+
/** A module whose public files are settled. An empty list means it has none. */
|
|
1896
|
+
interface SettledModule extends DeclaredModule {
|
|
1897
|
+
readonly public: readonly string[];
|
|
1898
|
+
}
|
|
1899
|
+
/**
|
|
1900
|
+
* Each module with its public files. A module `suss.json` gave files for
|
|
1901
|
+
* keeps them. Otherwise the language's candidates are tried, and only the
|
|
1902
|
+
* ones on disk are kept. A candidate is a path relative to the module's
|
|
1903
|
+
* root, with `{name}` standing for the module's name, as in `index.ts`
|
|
1904
|
+
* or `../{name}.rb`.
|
|
1905
|
+
*/
|
|
1906
|
+
declare function settleModules(modules: readonly DeclaredModule[] | undefined, candidates: readonly string[]): SettledModule[];
|
|
1907
|
+
/**
|
|
1908
|
+
* The module a file belongs to. A public file belongs to its module even
|
|
1909
|
+
* when it is outside the root, as a Ruby `lib/billing.rb` is next to
|
|
1910
|
+
* `lib/billing/`. Otherwise the deepest root that contains the file
|
|
1911
|
+
* wins, so a module nested inside another's folder keeps its own files.
|
|
1912
|
+
*/
|
|
1913
|
+
declare function moduleOfFile(modules: readonly SettledModule[], file: string): SettledModule | undefined;
|
|
1914
|
+
/**
|
|
1915
|
+
* Writes each summary's module onto its location. `root` is the directory
|
|
1916
|
+
* a shortened `location.file` is relative to, when the adapter has
|
|
1917
|
+
* already shortened it.
|
|
1918
|
+
*/
|
|
1919
|
+
declare function stampModules(summaries: readonly BehavioralSummary[], modules: readonly SettledModule[], root: string | undefined): void;
|
|
1920
|
+
|
|
1954
1921
|
/**
|
|
1955
1922
|
* Which branch an effect belongs to.
|
|
1956
1923
|
*
|
|
@@ -1961,8 +1928,8 @@ declare function composeWrappers(summaries: readonly BehavioralSummary[], option
|
|
|
1961
1928
|
* attributes effects to branches reads from here, so they all decide
|
|
1962
1929
|
* it the same way.
|
|
1963
1930
|
*
|
|
1964
|
-
* Neither test is enough alone
|
|
1965
|
-
*
|
|
1931
|
+
* Neither test is enough alone. This package's DESIGN.md shows the two
|
|
1932
|
+
* cases where one test on its own gets the branch wrong.
|
|
1966
1933
|
*/
|
|
1967
1934
|
|
|
1968
1935
|
/**
|
|
@@ -1982,7 +1949,26 @@ declare function guardsHoldOn(preconditions: readonly RawCondition[] | undefined
|
|
|
1982
1949
|
declare function runsBefore(effectLine: number, terminalEndLine: number): boolean;
|
|
1983
1950
|
|
|
1984
1951
|
/**
|
|
1985
|
-
*
|
|
1952
|
+
* Marks the summaries whose return becomes the process's exit code, as
|
|
1953
|
+
* `metadata.process.exitCodeFrom: "return"`, and clears the mark from
|
|
1954
|
+
* every other one.
|
|
1955
|
+
*
|
|
1956
|
+
* Every adapter works the functions out once per run and calls this over
|
|
1957
|
+
* every summary the run hands back, reused ones included. The chain runs
|
|
1958
|
+
* from the file that sets the code into files that never record that
|
|
1959
|
+
* file as a dependency, so a mark a cache kept could otherwise outlive an
|
|
1960
|
+
* edit to the entry file.
|
|
1961
|
+
*/
|
|
1962
|
+
|
|
1963
|
+
/**
|
|
1964
|
+
* `returnsTheCode` contains function keys, the absolute file and the
|
|
1965
|
+
* function's span as `declarationKey` writes them. A summary whose file
|
|
1966
|
+
* was shortened is resolved against `workspaceRoot` first.
|
|
1967
|
+
*/
|
|
1968
|
+
declare function markReturnsAsExitCode(summaries: readonly BehavioralSummary[], returnsTheCode: ReadonlySet<string>, workspaceRoot?: string): void;
|
|
1969
|
+
|
|
1970
|
+
/**
|
|
1971
|
+
* Writes `metadata.moduleImports` on a set of summaries.
|
|
1986
1972
|
*
|
|
1987
1973
|
* Every adapter records the project files a summary's own file depends
|
|
1988
1974
|
* on, and a checker rebuilds the module graph from that field to find
|
|
@@ -1999,15 +1985,14 @@ declare function runsBefore(effectLine: number, terminalEndLine: number): boolea
|
|
|
1999
1985
|
declare function stampModuleImports(summaries: BehavioralSummary[], importsOf: (file: string) => Iterable<string> | undefined): void;
|
|
2000
1986
|
|
|
2001
1987
|
/**
|
|
2002
|
-
*
|
|
1988
|
+
* Builds the raw structure for what a module does when it loads.
|
|
2003
1989
|
*
|
|
2004
1990
|
* A module's top-level statements run once, when it is first imported,
|
|
2005
|
-
* and a service
|
|
2006
|
-
*
|
|
2007
|
-
*
|
|
2008
|
-
*
|
|
2009
|
-
*
|
|
2010
|
-
* closure can reach the functions behind them.
|
|
1991
|
+
* and a service often reads its configuration there. Walking unit bodies
|
|
1992
|
+
* never sees that read, so the read gets a unit of its own: one per file,
|
|
1993
|
+
* named after the file, with no boundary. Attributing it to each handler
|
|
1994
|
+
* would report one read as several. The calls those statements make go on
|
|
1995
|
+
* the same unit, so the closure can reach the functions behind them.
|
|
2011
1996
|
*
|
|
2012
1997
|
* The one default branch has a `void` terminal, because module
|
|
2013
1998
|
* initialization returns to nobody. Every adapter builds it the same way.
|
|
@@ -2028,12 +2013,9 @@ interface ModuleInitOptions {
|
|
|
2028
2013
|
declare function moduleInitStructure(options: ModuleInitOptions): RawCodeStructure;
|
|
2029
2014
|
|
|
2030
2015
|
/**
|
|
2031
|
-
*
|
|
2032
|
-
*
|
|
2033
|
-
*
|
|
2034
|
-
* object the adapter interprets, and most differences between frameworks are
|
|
2035
|
-
* best expressed that way. A few patterns, though, repeat word for word
|
|
2036
|
-
* across packs, and this module collects those so they are written once.
|
|
2016
|
+
* Builders for the pack entries that several packs would otherwise copy
|
|
2017
|
+
* word for word. A pack is still a data object the adapter interprets;
|
|
2018
|
+
* these functions only write that data out.
|
|
2037
2019
|
*/
|
|
2038
2020
|
|
|
2039
2021
|
/**
|
|
@@ -2042,15 +2024,14 @@ declare function moduleInitStructure(options: ModuleInitOptions): RawCodeStructu
|
|
|
2042
2024
|
* like.
|
|
2043
2025
|
*
|
|
2044
2026
|
* Each `importNames` entry produces one DiscoveryPattern, because a library
|
|
2045
|
-
* usually
|
|
2046
|
-
*
|
|
2047
|
-
* `
|
|
2048
|
-
*
|
|
2049
|
-
* the same for every HTTP server framework we support.
|
|
2027
|
+
* usually exports more than one function that makes the routable instance
|
|
2028
|
+
* (Express has `express()` and `Router()`, Fastify has `fastify()` and
|
|
2029
|
+
* `Fastify`). The binding extraction, method from the registration and
|
|
2030
|
+
* path from position 0, is the same for every supported HTTP framework.
|
|
2050
2031
|
*
|
|
2051
|
-
* Callers
|
|
2052
|
-
*
|
|
2053
|
-
*
|
|
2032
|
+
* Callers pass the `methods` list themselves, because frameworks accept
|
|
2033
|
+
* different verbs: Fastify has `.head` and `.options`, and Express does
|
|
2034
|
+
* not by default.
|
|
2054
2035
|
*
|
|
2055
2036
|
* @example
|
|
2056
2037
|
* discovery: httpRouteDiscovery({
|
|
@@ -2077,7 +2058,7 @@ declare function httpRouteDiscovery(opts: {
|
|
|
2077
2058
|
* handlers: middleware, error handlers, validation hooks.
|
|
2078
2059
|
*
|
|
2079
2060
|
* A wrapper becomes a unit of its own, summarized like any other, so
|
|
2080
|
-
* these entries
|
|
2061
|
+
* these entries have no `bindingExtraction` and no registration chain.
|
|
2081
2062
|
* Their `match` is there for the import and the imported name, which is
|
|
2082
2063
|
* how wrapper discovery works out which variables in a file are the
|
|
2083
2064
|
* routable. One entry per (import name, wrapper shape) pair, since a
|
|
@@ -2095,7 +2076,7 @@ declare function wrapperDiscovery(opts: {
|
|
|
2095
2076
|
* placeholders for the call's positional arguments.
|
|
2096
2077
|
*/
|
|
2097
2078
|
interface RegistrationHelper {
|
|
2098
|
-
/**
|
|
2079
|
+
/** The helper's name as the project's code writes it. */
|
|
2099
2080
|
helperName: string;
|
|
2100
2081
|
/** The file declaring it, so a same-named function elsewhere is left alone. */
|
|
2101
2082
|
importModule?: string;
|
|
@@ -2122,9 +2103,8 @@ interface RegistrationHelper {
|
|
|
2122
2103
|
*/
|
|
2123
2104
|
declare function registrationHelperDiscovery(helpers: readonly RegistrationHelper[], kind?: string): DiscoveryPattern[];
|
|
2124
2105
|
/**
|
|
2125
|
-
*
|
|
2126
|
-
*
|
|
2127
|
-
* code rather than something the project restates in config.
|
|
2106
|
+
* Asks for the project's own route helpers to be read, so what each one
|
|
2107
|
+
* registers comes from the code instead of being restated in config.
|
|
2128
2108
|
*/
|
|
2129
2109
|
declare function routeHelperIndex(opts: {
|
|
2130
2110
|
importModule: string;
|
|
@@ -2136,8 +2116,8 @@ declare function routeHelperIndex(opts: {
|
|
|
2136
2116
|
/**
|
|
2137
2117
|
* The payload behind a `JSON.stringify(...)` call, or the argument
|
|
2138
2118
|
* unchanged when it is anything else. A producer serializes its message
|
|
2139
|
-
* before sending it,
|
|
2140
|
-
*
|
|
2119
|
+
* before sending it, so the value compared across the boundary is the
|
|
2120
|
+
* object that went into the string.
|
|
2141
2121
|
*/
|
|
2142
2122
|
declare function unwrapJsonStringify(body: EffectArg | null): EffectArg | null;
|
|
2143
2123
|
|
|
@@ -2145,13 +2125,12 @@ declare function unwrapJsonStringify(body: EffectArg | null): EffectArg | null;
|
|
|
2145
2125
|
* Option declarations more than one pack takes.
|
|
2146
2126
|
*
|
|
2147
2127
|
* A pack exports `optionsSchema` beside its factory, and the CLI parses
|
|
2148
|
-
* a `-f pack=config.json` file against it before the factory runs, so
|
|
2149
|
-
* key
|
|
2128
|
+
* a `-f pack=config.json` file against it before the factory runs, so an
|
|
2129
|
+
* undeclared key is refused by name instead of being silently ignored.
|
|
2150
2130
|
*
|
|
2151
|
-
* Four packs accept `storageSystem` and three accept `scope
|
|
2152
|
-
*
|
|
2153
|
-
*
|
|
2154
|
-
* `"postgres"` and the union allowed only `"postgresql"`.
|
|
2131
|
+
* Four packs accept `storageSystem` and three accept `scope`, so each
|
|
2132
|
+
* option is declared once here. With a copy per pack, one pack can end up
|
|
2133
|
+
* accepting a value the others reject.
|
|
2155
2134
|
*/
|
|
2156
2135
|
|
|
2157
2136
|
/**
|
|
@@ -2175,9 +2154,9 @@ declare const scopeOption: z.ZodString;
|
|
|
2175
2154
|
* A call the project has declared to be its own dispatcher, which both
|
|
2176
2155
|
* message-bus packs take under `producers`.
|
|
2177
2156
|
*
|
|
2178
|
-
* `receiver` is
|
|
2179
|
-
*
|
|
2180
|
-
*
|
|
2157
|
+
* `receiver` is a type name because a service may keep its dispatcher in
|
|
2158
|
+
* a field, a closure or a constructor parameter, and the type is the same
|
|
2159
|
+
* in all three.
|
|
2181
2160
|
*/
|
|
2182
2161
|
declare const configuredCallOption: z.ZodObject<{
|
|
2183
2162
|
module: z.ZodString;
|
|
@@ -2197,14 +2176,14 @@ declare const configuredCallOption: z.ZodObject<{
|
|
|
2197
2176
|
* Nothing here touches a language-specific AST node. A language adapter
|
|
2198
2177
|
* lowers its own tree into `StructuredStatement` and hands that over.
|
|
2199
2178
|
*
|
|
2200
|
-
*
|
|
2201
|
-
* `
|
|
2179
|
+
* On a construct it does not model, or on too many paths, the engine throws
|
|
2180
|
+
* `UnmodeledFlow` or `PathBudgetExceeded`, and the caller falls back to
|
|
2202
2181
|
* enclosure conditions plus an opaque conjunct instead of guessing.
|
|
2203
2182
|
*/
|
|
2204
2183
|
|
|
2205
2184
|
/** The cap on how many paths to enumerate. Past it the caller falls back to degraded conditions. */
|
|
2206
2185
|
declare const MAX_PATHS = 256;
|
|
2207
|
-
/** A construct the
|
|
2186
|
+
/** A construct the engine does not model, such as a stray break in a switch clause or a finally that exits. Callers catch this and degrade. */
|
|
2208
2187
|
declare class UnmodeledFlow extends Error {
|
|
2209
2188
|
}
|
|
2210
2189
|
/** The path budget was exceeded. Callers catch this and degrade. */
|
|
@@ -2215,11 +2194,11 @@ interface StructuredPathConditionsInput<Cond, Terminal> {
|
|
|
2215
2194
|
statements: StatementBlock<Cond>;
|
|
2216
2195
|
/**
|
|
2217
2196
|
* Every StructuredStatement reachable from `statements`, mapped to the
|
|
2218
|
-
*
|
|
2219
|
-
*
|
|
2220
|
-
*
|
|
2221
|
-
*
|
|
2222
|
-
*
|
|
2197
|
+
* caller's terminals found directly at that node: at its own position
|
|
2198
|
+
* (a return, throw, or opaque leaf) or, for a branching construct, in
|
|
2199
|
+
* its header or test expression. A lowering builds this at the same
|
|
2200
|
+
* time as the tree, so the engine never walks a raw source AST to find
|
|
2201
|
+
* where a terminal is.
|
|
2223
2202
|
*/
|
|
2224
2203
|
terminalsByStmt: ReadonlyMap<StructuredStatement<Cond>, readonly Terminal[]>;
|
|
2225
2204
|
}
|
|
@@ -2239,11 +2218,10 @@ interface StructuredPathConditionsResult<Cond, Terminal> {
|
|
|
2239
2218
|
*/
|
|
2240
2219
|
declare function enumerateStructuredPaths<Cond, Terminal>(input: StructuredPathConditionsInput<Cond, Terminal>): StructuredPathConditionsResult<Cond, Terminal>;
|
|
2241
2220
|
/**
|
|
2242
|
-
* Enumerate, and when the engine gives up
|
|
2243
|
-
* terminal comes back reachable under one
|
|
2244
|
-
*
|
|
2245
|
-
*
|
|
2246
|
-
* enclose it, should.
|
|
2221
|
+
* Enumerate, and when the engine gives up, report why instead of throwing.
|
|
2222
|
+
* Each terminal comes back reachable under one opaque marker condition, so
|
|
2223
|
+
* no terminal is lost. A caller that can walk a terminal's own ancestors
|
|
2224
|
+
* for the conditions that enclose it should use those instead.
|
|
2247
2225
|
*/
|
|
2248
2226
|
declare function enumerateOrDegrade<Cond, Terminal>(input: StructuredPathConditionsInput<Cond, Terminal>, terminals: Iterable<Terminal>): StructuredPathConditionsResult<Cond, Terminal> & {
|
|
2249
2227
|
/** Why the engine gave up, or null when it read the whole body. */
|
|
@@ -2256,12 +2234,10 @@ declare function enumerateOrDegrade<Cond, Terminal>(input: StructuredPathConditi
|
|
|
2256
2234
|
* for a path without it.
|
|
2257
2235
|
*
|
|
2258
2236
|
* `readPredicate` turns the language's own expression handle into a
|
|
2259
|
-
* predicate. Without
|
|
2260
|
-
* caller that has no reader gets.
|
|
2237
|
+
* predicate. Without it every condition comes back with `structured: null`.
|
|
2261
2238
|
*/
|
|
2262
2239
|
declare function sharedGatingConditions<Cond>(paths: readonly (readonly ConditionInfo<Cond>[])[] | undefined, readPredicate?: (expression: Cond) => Predicate): RawCondition[];
|
|
2263
2240
|
|
|
2264
|
-
/** Anything with a stable id, which is what these key on. */
|
|
2265
2241
|
interface Identified {
|
|
2266
2242
|
readonly id: number;
|
|
2267
2243
|
}
|
|
@@ -2271,7 +2247,7 @@ declare class IdSet<T extends Identified> implements Iterable<T> {
|
|
|
2271
2247
|
constructor(nodes?: Iterable<T>);
|
|
2272
2248
|
add(node: T): this;
|
|
2273
2249
|
has(node: T): boolean;
|
|
2274
|
-
/** The
|
|
2250
|
+
/** The wrapper that was added, which may be a different object from `node`. */
|
|
2275
2251
|
get(node: T): T | undefined;
|
|
2276
2252
|
get size(): number;
|
|
2277
2253
|
[Symbol.iterator](): Iterator<T>;
|
|
@@ -2287,23 +2263,59 @@ declare class IdMap<T extends Identified, V> implements Iterable<[T, V]> {
|
|
|
2287
2263
|
}
|
|
2288
2264
|
|
|
2289
2265
|
/**
|
|
2290
|
-
*
|
|
2291
|
-
*
|
|
2266
|
+
* The enclosing definition of a tree-sitter node, kept for as long as
|
|
2267
|
+
* its parse is.
|
|
2268
|
+
*
|
|
2269
|
+
* A fact key includes the function a node is written in, so an adapter
|
|
2270
|
+
* looks it up for every read it keys, and each step up the tree is a call
|
|
2271
|
+
* into tree-sitter's wasm module that builds a fresh wrapper. A parse tree
|
|
2272
|
+
* does not change once it is built, so each result is kept in a WeakMap
|
|
2273
|
+
* keyed on the tree. The results go when the tree does, and a parse made
|
|
2274
|
+
* in a later run starts with nothing kept.
|
|
2292
2275
|
*
|
|
2293
|
-
*
|
|
2294
|
-
*
|
|
2295
|
-
* does not list the containers it descends into. It walks every named
|
|
2296
|
-
* child and says only which nodes keep their own body, because that set
|
|
2297
|
-
* is short and a grammar's own vocabulary already names it.
|
|
2276
|
+
* Only this read is kept. Keeping each node's children and fields too
|
|
2277
|
+
* cost more memory than it saved time.
|
|
2298
2278
|
*/
|
|
2299
|
-
/**
|
|
2279
|
+
/** What the reader needs from a parse tree node. */
|
|
2280
|
+
interface ParsedNode<T> {
|
|
2281
|
+
readonly id: number;
|
|
2282
|
+
readonly type: string;
|
|
2283
|
+
readonly tree: object;
|
|
2284
|
+
readonly parent: T | null;
|
|
2285
|
+
}
|
|
2286
|
+
/** The nearest ancestor of each node whose type is one of `types`, read once per parse. */
|
|
2287
|
+
declare class EnclosingNodes<T extends ParsedNode<T>> {
|
|
2288
|
+
private readonly types;
|
|
2289
|
+
private readonly byTree;
|
|
2290
|
+
constructor(types: ReadonlySet<string>);
|
|
2291
|
+
/**
|
|
2292
|
+
* The nearest ancestor of one of the types, or null when there is none.
|
|
2293
|
+
* Every node passed on the way up has the same result, so each one is
|
|
2294
|
+
* kept, and a later read from a sibling stops one step up.
|
|
2295
|
+
*/
|
|
2296
|
+
of(node: T): T | null;
|
|
2297
|
+
private keptFor;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
/**
|
|
2301
|
+
* The recursion the adapters use to find a declaration wherever the
|
|
2302
|
+
* language allows one to be written.
|
|
2303
|
+
*
|
|
2304
|
+
* A declaration can go anywhere a statement can, and every language has
|
|
2305
|
+
* more places for a statement than anyone remembers to list. So an adapter
|
|
2306
|
+
* does not list the containers to descend into. The walk visits every
|
|
2307
|
+
* named child, and the adapter only lists the nodes whose body belongs to
|
|
2308
|
+
* what they declare, because that list is short and the grammar already
|
|
2309
|
+
* has a node type for each.
|
|
2310
|
+
*/
|
|
2311
|
+
/** The one thing the walk needs from a parse tree node. */
|
|
2300
2312
|
interface WalkableNode<T> {
|
|
2301
2313
|
readonly namedChildren: ReadonlyArray<T | null>;
|
|
2302
2314
|
}
|
|
2303
2315
|
/**
|
|
2304
2316
|
* Returned by `into` for a node whose children the walk should leave
|
|
2305
|
-
* unread, such as one whose body belongs to what it declares.
|
|
2306
|
-
* so it cannot collide with
|
|
2317
|
+
* unread, such as one whose body belongs to what it declares. It is a
|
|
2318
|
+
* symbol so it cannot collide with any value a caller passes down.
|
|
2307
2319
|
*/
|
|
2308
2320
|
declare const SKIP_CHILDREN: unique symbol;
|
|
2309
2321
|
/** What a walk does at each node it reaches. */
|
|
@@ -2330,6 +2342,60 @@ declare function walkDescendants<T extends WalkableNode<T>, C>(root: T, carried:
|
|
|
2330
2342
|
/** The deepest directory containing every file, or undefined when they share no absolute root. */
|
|
2331
2343
|
declare function commonDirectoryOf(files: ReadonlyArray<string>): string | undefined;
|
|
2332
2344
|
|
|
2345
|
+
/**
|
|
2346
|
+
* Which files a test pack reads. `suss extract --intent` hands a test
|
|
2347
|
+
* pack the files the PRDs list under `coveredBy`, written the way the
|
|
2348
|
+
* author wrote them, usually from the repository root. An adapter walks
|
|
2349
|
+
* absolute paths, so an entry matches a file on whole path segments from
|
|
2350
|
+
* the end: `tests/test_orders.py` matches `/repo/api/tests/test_orders.py`
|
|
2351
|
+
* and not `/repo/api/tests/other_test_orders.py`. Every adapter's test
|
|
2352
|
+
* discovery goes through this, so the three languages agree on it.
|
|
2353
|
+
*/
|
|
2354
|
+
/** Whether the file is on the list. Every file is when there is no list. */
|
|
2355
|
+
declare function isListedTestFile(file: string, listed: readonly string[] | undefined): boolean;
|
|
2356
|
+
/**
|
|
2357
|
+
* Whether a file's base name matches one of the runner's patterns, where
|
|
2358
|
+
* `*` matches any run of characters: `test_*.py` or `*_spec.rb`.
|
|
2359
|
+
*/
|
|
2360
|
+
declare function matchesTestFileName(file: string, patterns: readonly string[]): boolean;
|
|
2361
|
+
|
|
2362
|
+
/**
|
|
2363
|
+
* Turns the places a value's walk ended into the value references a
|
|
2364
|
+
* transition records, the same way for every adapter.
|
|
2365
|
+
*
|
|
2366
|
+
* The walk itself is in `@suss/resolution`. What an input is depends on
|
|
2367
|
+
* the language and the framework, and so does reading a literal or
|
|
2368
|
+
* quoting the source, so the adapter says those three things. The order
|
|
2369
|
+
* they are tried in, and what a place that is none of them becomes, is
|
|
2370
|
+
* decided here.
|
|
2371
|
+
*/
|
|
2372
|
+
|
|
2373
|
+
/** One place a walk ended, as much of it as this needs. */
|
|
2374
|
+
interface WalkEnd {
|
|
2375
|
+
/** The key of the member the walk ended at. */
|
|
2376
|
+
readonly key: string;
|
|
2377
|
+
/** The properties read off it on the way to the value, outermost first. */
|
|
2378
|
+
readonly path: readonly string[];
|
|
2379
|
+
/** A read at a key the source computes on the way, or null for none. */
|
|
2380
|
+
readonly computedAt: string | null;
|
|
2381
|
+
/** The language's own conversions on the way to the value, outermost first. */
|
|
2382
|
+
readonly conversions: readonly string[];
|
|
2383
|
+
readonly end: {
|
|
2384
|
+
readonly is: string;
|
|
2385
|
+
};
|
|
2386
|
+
}
|
|
2387
|
+
/** What only the adapter can say about a place a walk ended. */
|
|
2388
|
+
interface SourceSpelling<L extends WalkEnd> {
|
|
2389
|
+
/** The input the place is, or null when it is not one. */
|
|
2390
|
+
inputOf(leaf: L): ValueRef | null;
|
|
2391
|
+
/** The literal the written value at a key is, or undefined for anything else. */
|
|
2392
|
+
literalAt(key: string): string | number | boolean | null | undefined;
|
|
2393
|
+
/** The source written at a key, or null when the key is not a node. */
|
|
2394
|
+
textAt(key: string): string | null;
|
|
2395
|
+
}
|
|
2396
|
+
/** Every place a value's walk ended, as distinct value references. */
|
|
2397
|
+
declare function sourceRefsOf<L extends WalkEnd>(leaves: readonly L[], spelling: SourceSpelling<L>): ValueRef[];
|
|
2398
|
+
|
|
2333
2399
|
/**
|
|
2334
2400
|
* The small surface every language adapter implements, whatever that
|
|
2335
2401
|
* language's own tooling looks like underneath.
|
|
@@ -2342,9 +2408,8 @@ declare function commonDirectoryOf(files: ReadonlyArray<string>): string | undef
|
|
|
2342
2408
|
|
|
2343
2409
|
interface LanguageAdapter {
|
|
2344
2410
|
/**
|
|
2345
|
-
*
|
|
2346
|
-
*
|
|
2347
|
-
* discovery without bottlenecking on synchronous reads.
|
|
2411
|
+
* Summaries for the listed files. It is async so an implementation can
|
|
2412
|
+
* read files concurrently during discovery.
|
|
2348
2413
|
*/
|
|
2349
2414
|
extractFromFiles(filePaths: string[]): Promise<BehavioralSummary[]>;
|
|
2350
2415
|
/** Extract summaries from every source file the adapter's project knows about. */
|
|
@@ -2352,23 +2417,22 @@ interface LanguageAdapter {
|
|
|
2352
2417
|
}
|
|
2353
2418
|
|
|
2354
2419
|
/**
|
|
2355
|
-
*
|
|
2420
|
+
* The language-neutral half of an adapter's cache key.
|
|
2356
2421
|
*
|
|
2357
|
-
*
|
|
2422
|
+
* Each adapter calls `createAdapterStamp` once, with its own
|
|
2358
2423
|
* `import.meta.url` and its hand-bumped version, and keeps the result for
|
|
2359
|
-
* the life of the process. The stamp hashes the adapter's
|
|
2360
|
-
*
|
|
2361
|
-
* `@suss/
|
|
2362
|
-
*
|
|
2363
|
-
*
|
|
2364
|
-
*
|
|
2365
|
-
* to cache. The extraction cache's design is in this package's README.
|
|
2424
|
+
* the life of the process. The stamp hashes the adapter's dist file and
|
|
2425
|
+
* every analysis package shipped with it (this package, `@suss/resolution`,
|
|
2426
|
+
* `@suss/datalog`, `@suss/behavioral-ir`). A release that changes any of
|
|
2427
|
+
* them invalidates an older cache, and so does a local rebuild, with no
|
|
2428
|
+
* version bump. Running from source leaves no dist file to hash, so that
|
|
2429
|
+
* mode turns the cache off. This package's DESIGN.md describes the cache.
|
|
2366
2430
|
*/
|
|
2431
|
+
|
|
2367
2432
|
/**
|
|
2368
2433
|
* Whether this process can see the adapter's own code. `bundle` includes
|
|
2369
|
-
* a hash that changes whenever the code does. `source` means
|
|
2370
|
-
*
|
|
2371
|
-
* about the code that will produce the results.
|
|
2434
|
+
* a hash that changes whenever the code does. `source` means no bundle was
|
|
2435
|
+
* found, so a cache key built from this stamp cannot tell two builds apart.
|
|
2372
2436
|
*/
|
|
2373
2437
|
type AdapterCodeStamp = {
|
|
2374
2438
|
kind: "bundle";
|
|
@@ -2376,24 +2440,22 @@ type AdapterCodeStamp = {
|
|
|
2376
2440
|
} | {
|
|
2377
2441
|
kind: "source";
|
|
2378
2442
|
};
|
|
2379
|
-
/** What `createAdapterStamp` hands back to an adapter's own version.ts. */
|
|
2380
2443
|
interface AdapterStamp {
|
|
2381
2444
|
/** The stamp for the running adapter, computed once per process. */
|
|
2382
2445
|
codeStamp(): AdapterCodeStamp;
|
|
2383
2446
|
/**
|
|
2384
|
-
*
|
|
2385
|
-
*
|
|
2386
|
-
*
|
|
2387
|
-
* loaded adapter dist file changes (dev-mode rebuild auto-invalidation).
|
|
2447
|
+
* A cache key for this adapter and its packs. It is the same across
|
|
2448
|
+
* processes for the same inputs, and changes when a pack's version, the
|
|
2449
|
+
* adapter's version, or the adapter's dist file changes.
|
|
2388
2450
|
*/
|
|
2389
2451
|
packsDigest(packVersions: ReadonlyArray<{
|
|
2390
2452
|
name: string;
|
|
2391
2453
|
version?: string;
|
|
2392
2454
|
}>): string;
|
|
2393
2455
|
/**
|
|
2394
|
-
* `cacheDir
|
|
2395
|
-
*
|
|
2396
|
-
* null
|
|
2456
|
+
* Returns `cacheDir`, or null when this process loaded the adapter from
|
|
2457
|
+
* source, since then one build cannot be told from another. The first
|
|
2458
|
+
* null in a process also prints the reason to stderr.
|
|
2397
2459
|
*/
|
|
2398
2460
|
declineWhenRunFromSource(cacheDir: string | null): string | null;
|
|
2399
2461
|
}
|
|
@@ -2410,7 +2472,7 @@ declare function createAdapterStamp(config: {
|
|
|
2410
2472
|
/**
|
|
2411
2473
|
* The hash for a bundle directory: the bundle itself plus every analysis
|
|
2412
2474
|
* package that can be located. Empty when the directory has no bundle in
|
|
2413
|
-
* it,
|
|
2475
|
+
* it, as happens when running from source.
|
|
2414
2476
|
*/
|
|
2415
2477
|
declare function computeDistHashFrom(dir: string): string;
|
|
2416
2478
|
/**
|
|
@@ -2429,19 +2491,40 @@ declare function computeContentHash(paths: readonly string[]): string;
|
|
|
2429
2491
|
* content go in, so a file that moves counts as a change even when
|
|
2430
2492
|
* every byte in it stayed the same.
|
|
2431
2493
|
*
|
|
2432
|
-
* A file that cannot be read
|
|
2433
|
-
*
|
|
2434
|
-
*
|
|
2435
|
-
* being gone is a fact about the project the next run should notice,
|
|
2436
|
-
* not a reason to stop telling runs apart.
|
|
2494
|
+
* A file that cannot be read is hashed as absent, where
|
|
2495
|
+
* `computeContentHash` would return an empty stamp. These files belong
|
|
2496
|
+
* to the project, so a missing one is a change the next run should see.
|
|
2437
2497
|
*/
|
|
2438
2498
|
declare function projectFileStamp(paths: readonly string[]): string;
|
|
2499
|
+
/**
|
|
2500
|
+
* The run options that change what an extraction writes. An adapter passes
|
|
2501
|
+
* every one it takes, and leaves out the ones it does not have.
|
|
2502
|
+
*/
|
|
2503
|
+
interface ExtractionConfig {
|
|
2504
|
+
gapHandling?: "strict" | "permissive" | "silent" | undefined;
|
|
2505
|
+
/** Whether a summary is written for each function a unit reaches. */
|
|
2506
|
+
includeReachable?: boolean | undefined;
|
|
2507
|
+
/** The directory a stored summary's `location.file` is relative to. */
|
|
2508
|
+
workspaceRoot?: string | undefined;
|
|
2509
|
+
/** The directory a stored summary's id measures its file from. */
|
|
2510
|
+
projectRoot?: string | undefined;
|
|
2511
|
+
/** The directories an absolute import was resolved against. */
|
|
2512
|
+
importRoots?: readonly string[] | undefined;
|
|
2513
|
+
/** The modules the project lists, which decide which units discovery finds. */
|
|
2514
|
+
modules?: readonly SettledModule[] | undefined;
|
|
2515
|
+
}
|
|
2516
|
+
/**
|
|
2517
|
+
* The run options as one part of the cache key, so a run never reads an
|
|
2518
|
+
* entry that a run with other options wrote. Every adapter builds this part
|
|
2519
|
+
* here, which keeps the three keys from drifting apart.
|
|
2520
|
+
*/
|
|
2521
|
+
declare function extractionConfigStamp(config: ExtractionConfig): string;
|
|
2439
2522
|
/**
|
|
2440
2523
|
* The digest a run looks its cache entry up under. A pack may read
|
|
2441
2524
|
* project files that are not among the ones a run walks, a SAM template
|
|
2442
2525
|
* that decides which handlers exist for instance, so those belong in
|
|
2443
2526
|
* the key next to the pack's own config. Which files they are depends
|
|
2444
|
-
* on the files this run walks, so the digest is
|
|
2527
|
+
* on the files this run walks, so the digest is computed per run rather
|
|
2445
2528
|
* than once per adapter.
|
|
2446
2529
|
*/
|
|
2447
2530
|
declare function runDigest(packsDigest: string, packs: ReadonlyArray<{
|
|
@@ -2451,36 +2534,33 @@ declare function runDigest(packsDigest: string, packs: ReadonlyArray<{
|
|
|
2451
2534
|
/**
|
|
2452
2535
|
* The on-disk extraction cache, shared by every language adapter.
|
|
2453
2536
|
*
|
|
2454
|
-
* A run reuses the previous
|
|
2455
|
-
*
|
|
2456
|
-
*
|
|
2457
|
-
*
|
|
2458
|
-
*
|
|
2459
|
-
*
|
|
2460
|
-
*
|
|
2461
|
-
*
|
|
2462
|
-
*
|
|
2463
|
-
* full design is in this package's README.
|
|
2537
|
+
* A run reuses the previous run's summaries whole when nothing changed,
|
|
2538
|
+
* and per file when some files did. The entry directory's name is a hash
|
|
2539
|
+
* of the schema version, the adapter and packs digest, and the config
|
|
2540
|
+
* path, so two builds that disagree on any of them never read each
|
|
2541
|
+
* other's entry. The manifest inside records a stamp and content hash per
|
|
2542
|
+
* file, the files each summary belongs to, and for each of those files the
|
|
2543
|
+
* other files its walk read. An adapter can store its own data on a file's
|
|
2544
|
+
* record through `meta`, which the cache never reads. The package's
|
|
2545
|
+
* design notes describe the cache in full.
|
|
2464
2546
|
*/
|
|
2465
2547
|
|
|
2466
2548
|
/**
|
|
2467
|
-
* How many
|
|
2468
|
-
*
|
|
2469
|
-
*
|
|
2470
|
-
* twice one manifest.
|
|
2549
|
+
* How many entries a cache directory keeps. Two lets a pair of builds
|
|
2550
|
+
* alternate, as on a branch switch or an adapter rebuild and revert,
|
|
2551
|
+
* without either losing its entry, and keeps a directory to two manifests.
|
|
2471
2552
|
*/
|
|
2472
2553
|
declare const MAX_ENTRIES = 2;
|
|
2473
2554
|
/**
|
|
2474
2555
|
* What one walked file contributed to the run, and what its walk read.
|
|
2475
2556
|
* `deps` are the other files whose content went into this file's
|
|
2476
|
-
* summaries
|
|
2477
|
-
* are the units this file's walk claimed
|
|
2478
|
-
*
|
|
2479
|
-
* whatever else the adapter needs to
|
|
2480
|
-
* partial run
|
|
2481
|
-
*
|
|
2482
|
-
*
|
|
2483
|
-
* every partial run.
|
|
2557
|
+
* summaries, and a change to any of them re-extracts this file. `claims`
|
|
2558
|
+
* are the units this file's walk claimed. A partial run replays them
|
|
2559
|
+
* before walking anything, so the same pack wins each unit as before.
|
|
2560
|
+
* `meta` is whatever else the adapter needs to check this file again on
|
|
2561
|
+
* a partial run, such as a route's mount prefixes. A file marked
|
|
2562
|
+
* `cacheable: false` depends on something the cache cannot tie to files,
|
|
2563
|
+
* and is re-extracted on every partial run.
|
|
2484
2564
|
*/
|
|
2485
2565
|
interface RootRecord<Meta = unknown> {
|
|
2486
2566
|
path: string;
|
|
@@ -2493,15 +2573,31 @@ interface RootRecord<Meta = unknown> {
|
|
|
2493
2573
|
meta: Meta;
|
|
2494
2574
|
/** Packs that applied to the file, re-checked on a partial run. */
|
|
2495
2575
|
packs: string[];
|
|
2576
|
+
/** The files this file imports, in the order the adapter's loader found them. */
|
|
2577
|
+
imports?: string[];
|
|
2496
2578
|
}
|
|
2497
2579
|
/**
|
|
2498
|
-
*
|
|
2499
|
-
*
|
|
2500
|
-
*
|
|
2580
|
+
* What an adapter worked out about one unit of its own, such as the calls
|
|
2581
|
+
* one function body makes. `file` is the file the unit is in and `deps`
|
|
2582
|
+
* the other files that went into `data`. A partial run hands the record
|
|
2583
|
+
* back while `file` and every one of `deps` hash the same, so the adapter
|
|
2584
|
+
* can skip the work that produced it. The cache never reads `data`.
|
|
2501
2585
|
*/
|
|
2502
|
-
interface
|
|
2586
|
+
interface UnitRecord<Data = unknown> {
|
|
2587
|
+
key: string;
|
|
2588
|
+
file: string;
|
|
2589
|
+
deps: string[];
|
|
2590
|
+
data: Data;
|
|
2591
|
+
}
|
|
2592
|
+
/**
|
|
2593
|
+
* Which files each summary belongs to. `summaries[i]` is reused while at
|
|
2594
|
+
* least one file in `owners[i]` is. An empty list marks a summary built
|
|
2595
|
+
* over the whole run, which a partial run always recomputes.
|
|
2596
|
+
*/
|
|
2597
|
+
interface CacheAttribution<Meta = unknown, UnitData = unknown> {
|
|
2503
2598
|
roots: RootRecord<Meta>[];
|
|
2504
2599
|
owners: string[][];
|
|
2600
|
+
units?: UnitRecord<UnitData>[];
|
|
2505
2601
|
}
|
|
2506
2602
|
/** Reported by `lookup`: what the cache decided, and why. */
|
|
2507
2603
|
interface CacheDiagnostic {
|
|
@@ -2537,34 +2633,42 @@ type CacheLookup = {
|
|
|
2537
2633
|
diagnostic: CacheDiagnostic;
|
|
2538
2634
|
};
|
|
2539
2635
|
/**
|
|
2540
|
-
* What a `files-changed` miss can still reuse. `validRoots`
|
|
2541
|
-
*
|
|
2542
|
-
*
|
|
2543
|
-
* before calling `reuse`. `reuse` returns the summaries
|
|
2544
|
-
* least one
|
|
2545
|
-
*
|
|
2636
|
+
* What a `files-changed` miss can still reuse. `validRoots` are the files
|
|
2637
|
+
* whose hashes and recorded dependencies are unchanged. The caller can
|
|
2638
|
+
* drop more of them, such as a file whose mount prefix no longer matches,
|
|
2639
|
+
* before calling `reuse`. `reuse` returns the summaries that belong to at
|
|
2640
|
+
* least one remaining file, in stored order, with their owners, so the
|
|
2641
|
+
* caller can merge them and write the result back.
|
|
2546
2642
|
*/
|
|
2547
|
-
interface PartialPlan<Meta = unknown> {
|
|
2643
|
+
interface PartialPlan<Meta = unknown, UnitData = unknown> {
|
|
2548
2644
|
/** Paths whose content hash differs, plus paths new to the set. */
|
|
2549
2645
|
changed: Set<string>;
|
|
2550
2646
|
removed: Set<string>;
|
|
2647
|
+
/**
|
|
2648
|
+
* The stored import list of every file whose content is unchanged, when
|
|
2649
|
+
* no file joined or left the set, since either can move where an import
|
|
2650
|
+
* resolves. Empty otherwise.
|
|
2651
|
+
*/
|
|
2652
|
+
resolvedImports: Map<string, string[]>;
|
|
2551
2653
|
roots: Map<string, RootRecord<Meta>>;
|
|
2552
2654
|
validRoots: Set<string>;
|
|
2553
2655
|
rootsDeclined: number;
|
|
2656
|
+
/** Unit records whose file and deps are all unchanged, by key. */
|
|
2657
|
+
validUnits: Map<string, UnitRecord<UnitData>>;
|
|
2554
2658
|
reuse(valid: ReadonlySet<string>): {
|
|
2555
2659
|
summaries: BehavioralSummary[];
|
|
2556
2660
|
owners: string[][];
|
|
2557
2661
|
};
|
|
2558
2662
|
allSummaries(): BehavioralSummary[];
|
|
2559
2663
|
/** The stored attribution decoded, for a write that changes nothing. */
|
|
2560
|
-
attribution(): CacheAttribution<Meta>;
|
|
2664
|
+
attribution(): CacheAttribution<Meta, UnitData>;
|
|
2561
2665
|
}
|
|
2562
|
-
interface CacheLayer<Meta = unknown> {
|
|
2666
|
+
interface CacheLayer<Meta = unknown, UnitData = unknown> {
|
|
2563
2667
|
/** The summary list on a hit, null on a miss. */
|
|
2564
2668
|
tryHit(input: CacheInput): Promise<BehavioralSummary[] | null>;
|
|
2565
2669
|
/**
|
|
2566
|
-
* The lookup behind `tryHit`, with the reason a miss
|
|
2567
|
-
*
|
|
2670
|
+
* The lookup behind `tryHit`, with the reason for a miss. It stats the
|
|
2671
|
+
* files, and reads only those changed shortly before, to hash them.
|
|
2568
2672
|
*/
|
|
2569
2673
|
lookup(input: CacheInput): Promise<CacheLookup>;
|
|
2570
2674
|
/**
|
|
@@ -2572,33 +2676,39 @@ interface CacheLayer<Meta = unknown> {
|
|
|
2572
2676
|
* work out which files' summaries survive. Null when the entry has
|
|
2573
2677
|
* no per-file layer to reuse, or no entry matches the key at all.
|
|
2574
2678
|
*/
|
|
2575
|
-
plan(input: CacheInput): Promise<PartialPlan<Meta> | null>;
|
|
2679
|
+
plan(input: CacheInput): Promise<PartialPlan<Meta, UnitData> | null>;
|
|
2576
2680
|
/**
|
|
2577
|
-
*
|
|
2578
|
-
*
|
|
2579
|
-
*
|
|
2580
|
-
* ever be reused whole.
|
|
2681
|
+
* Save a fresh extraction's summaries against this file list, so a later
|
|
2682
|
+
* `lookup` with the same files returns them. Without `attribution` the
|
|
2683
|
+
* entry can only be reused whole.
|
|
2581
2684
|
*/
|
|
2582
|
-
write(input: CacheInput, summaries: BehavioralSummary[], attribution?: CacheAttribution<Meta>): Promise<void>;
|
|
2685
|
+
write(input: CacheInput, summaries: BehavioralSummary[], attribution?: CacheAttribution<Meta, UnitData>): Promise<void>;
|
|
2583
2686
|
}
|
|
2584
2687
|
interface CacheInput {
|
|
2585
2688
|
/** Absolute paths of every file the run walks. */
|
|
2586
2689
|
files: ReadonlyArray<string>;
|
|
2587
2690
|
adapterPacksDigest: string;
|
|
2588
2691
|
/**
|
|
2589
|
-
*
|
|
2590
|
-
*
|
|
2591
|
-
* not every adapter has one.
|
|
2692
|
+
* A file whose change invalidates the whole entry, such as a tsconfig or
|
|
2693
|
+
* a project manifest. Not every adapter has one.
|
|
2592
2694
|
*/
|
|
2593
2695
|
configPath?: string;
|
|
2594
2696
|
}
|
|
2595
2697
|
/**
|
|
2596
|
-
*
|
|
2597
|
-
*
|
|
2598
|
-
*
|
|
2599
|
-
|
|
2698
|
+
* A cache layer rooted at `cacheDir`. With `null`, every lookup misses and
|
|
2699
|
+
* `write` does nothing, for a one-shot extract where the cache would only
|
|
2700
|
+
* add time.
|
|
2701
|
+
*/
|
|
2702
|
+
declare function createCacheLayer<Meta = unknown, UnitData = unknown>(cacheDir: string | null): CacheLayer<Meta, UnitData>;
|
|
2703
|
+
|
|
2704
|
+
/**
|
|
2705
|
+
* Whether a write after `statAt` could leave this file's mtime and size
|
|
2706
|
+
* where the stat saw them, because its last change was that recent.
|
|
2600
2707
|
*/
|
|
2601
|
-
declare function
|
|
2708
|
+
declare function stampMayMissAWrite(stat: {
|
|
2709
|
+
mtimeMs: number;
|
|
2710
|
+
ctimeMs: number;
|
|
2711
|
+
}, statAt: number): boolean;
|
|
2602
2712
|
|
|
2603
2713
|
/**
|
|
2604
2714
|
* The extraction funnel, shared across languages.
|
|
@@ -2606,10 +2716,9 @@ declare function createCacheLayer<Meta = unknown>(cacheDir: string | null): Cach
|
|
|
2606
2716
|
* "Why did this run produce nothing" is always "at which stage did the
|
|
2607
2717
|
* count reach zero", so the report is a funnel: files in the tsconfig,
|
|
2608
2718
|
* files the import gates selected, units discovered, summaries built.
|
|
2609
|
-
* Each
|
|
2610
|
-
*
|
|
2611
|
-
*
|
|
2612
|
-
* is what made an entire pack family extract nothing in silence.
|
|
2719
|
+
* Each stage records its own row, so the CLI never works the counts out
|
|
2720
|
+
* from a second copy of the pre-filter's logic that could drift from the
|
|
2721
|
+
* first.
|
|
2613
2722
|
*
|
|
2614
2723
|
* TypeScript, Python and Ruby all build one of these, though only
|
|
2615
2724
|
* TypeScript gates files by import specifier today. A pack with no gate
|
|
@@ -2620,9 +2729,9 @@ declare function createCacheLayer<Meta = unknown>(cacheDir: string | null): Cach
|
|
|
2620
2729
|
interface PackFunnel {
|
|
2621
2730
|
pack: string;
|
|
2622
2731
|
/**
|
|
2623
|
-
*
|
|
2624
|
-
*
|
|
2625
|
-
*
|
|
2732
|
+
* The version stamp the pack declares, or null when it declares none.
|
|
2733
|
+
* The cache keys on it, so a pack that never changes its stamp can
|
|
2734
|
+
* serve a later run with an earlier build's results.
|
|
2626
2735
|
*/
|
|
2627
2736
|
version: string | null;
|
|
2628
2737
|
/**
|
|
@@ -2644,11 +2753,10 @@ interface PackFunnel {
|
|
|
2644
2753
|
*/
|
|
2645
2754
|
gates: string[];
|
|
2646
2755
|
/**
|
|
2647
|
-
* Gate specifiers that do not resolve from the project.
|
|
2648
|
-
*
|
|
2649
|
-
* installed
|
|
2650
|
-
* textual
|
|
2651
|
-
* nothing else surfaces.
|
|
2756
|
+
* Gate specifiers that do not resolve from the project. A non-empty
|
|
2757
|
+
* list usually means the target project's dependencies are not
|
|
2758
|
+
* installed. Packs that resolve symbols then stop working while packs
|
|
2759
|
+
* with a textual gate still run, and nothing else in the report shows it.
|
|
2652
2760
|
*/
|
|
2653
2761
|
unresolvedGates: string[];
|
|
2654
2762
|
/** Files the pre-filter selected for this pack. */
|
|
@@ -2707,9 +2815,9 @@ interface PackFunnel {
|
|
|
2707
2815
|
/**
|
|
2708
2816
|
* What the pack wrote as data rather than as code, or null for a pack
|
|
2709
2817
|
* written as a hand-rolled walk, or for a language whose packs have
|
|
2710
|
-
* no declared-pattern system at all.
|
|
2711
|
-
*
|
|
2712
|
-
*
|
|
2818
|
+
* no declared-pattern system at all. Unlike the counts, this comes from
|
|
2819
|
+
* the pack's definition and not from the run. It is reported so the
|
|
2820
|
+
* move of packs onto declared patterns can be measured.
|
|
2713
2821
|
*/
|
|
2714
2822
|
declarations: PackDeclarations | null;
|
|
2715
2823
|
}
|
|
@@ -2724,11 +2832,10 @@ interface ExtractionReport {
|
|
|
2724
2832
|
* Files whose exports the checker could not follow, so the run read
|
|
2725
2833
|
* them as exporting nothing.
|
|
2726
2834
|
*
|
|
2727
|
-
* Without this
|
|
2728
|
-
*
|
|
2729
|
-
*
|
|
2730
|
-
*
|
|
2731
|
-
* count below is a floor while this is non-empty.
|
|
2835
|
+
* Without this list, a module whose barrel chain overflowed the call
|
|
2836
|
+
* stack looks the same as a module that exports nothing: both produce
|
|
2837
|
+
* no summaries and exit 0. Anything reachable only through these files
|
|
2838
|
+
* is missing, and every count below is a floor while this is non-empty.
|
|
2732
2839
|
*/
|
|
2733
2840
|
filesWithUnreadableExports: string[];
|
|
2734
2841
|
/**
|
|
@@ -2737,10 +2844,9 @@ interface ExtractionReport {
|
|
|
2737
2844
|
*/
|
|
2738
2845
|
emptyStage: EmptyStage | null;
|
|
2739
2846
|
/**
|
|
2740
|
-
* Reassigned names the run stated
|
|
2741
|
-
* decides which write a reader sees.
|
|
2742
|
-
*
|
|
2743
|
-
* reaching definitions is worth writing.
|
|
2847
|
+
* Reassigned names the run stated no value for, because control flow
|
|
2848
|
+
* decides which write a reader sees. Across a large corpus, the count
|
|
2849
|
+
* shows how much a scoped reaching-definitions analysis would recover.
|
|
2744
2850
|
*/
|
|
2745
2851
|
reassignedNamesUnstated: number;
|
|
2746
2852
|
}
|
|
@@ -2754,7 +2860,7 @@ type EmptyStage = "tsconfig" | "gateResolution" | "candidateFiles" | "discovery"
|
|
|
2754
2860
|
* that looked and found nothing.
|
|
2755
2861
|
*/
|
|
2756
2862
|
interface PackFailure {
|
|
2757
|
-
/** The hook that threw,
|
|
2863
|
+
/** The hook that threw, by the name a pack author uses for it. */
|
|
2758
2864
|
hook: string;
|
|
2759
2865
|
/** The file the pack was reading. */
|
|
2760
2866
|
file: string;
|
|
@@ -2774,9 +2880,9 @@ interface PackTally {
|
|
|
2774
2880
|
}
|
|
2775
2881
|
declare const emptyTally: () => PackTally;
|
|
2776
2882
|
/**
|
|
2777
|
-
* Record that a pack's hook threw, and
|
|
2778
|
-
*
|
|
2779
|
-
*
|
|
2883
|
+
* Record that a pack's hook threw, and return one line a caller can print.
|
|
2884
|
+
* The failure also goes on the tally, so the pack's counts do not look
|
|
2885
|
+
* like those of a pack that found nothing.
|
|
2780
2886
|
*/
|
|
2781
2887
|
declare function recordPackFailure(tally: PackTally | undefined, failure: {
|
|
2782
2888
|
pack: string;
|
|
@@ -2796,8 +2902,8 @@ declare function tallyUnit(tallies: Map<string, PackTally>, recognition: string
|
|
|
2796
2902
|
* it, because the summaries a pack is responsible for are not all built
|
|
2797
2903
|
* where the pack is in scope: wrapper expansion and sub-unit synthesis
|
|
2798
2904
|
* both add summaries after discovery has moved on. Every summary
|
|
2799
|
-
* records
|
|
2800
|
-
*
|
|
2905
|
+
* records the pack that recognised it, so grouping on that credits each
|
|
2906
|
+
* summary to its pack however late it was built.
|
|
2801
2907
|
*/
|
|
2802
2908
|
interface SummaryCounts {
|
|
2803
2909
|
bound: number;
|
|
@@ -2822,6 +2928,25 @@ declare function buildUngatedExtractionReport(args: {
|
|
|
2822
2928
|
summaries: ReadonlyArray<BehavioralSummary>;
|
|
2823
2929
|
}): ExtractionReport;
|
|
2824
2930
|
|
|
2931
|
+
/**
|
|
2932
|
+
* Parses a process keeps between runs of one project, by file.
|
|
2933
|
+
*
|
|
2934
|
+
* The Python and Ruby adapters parse every file on every run, because
|
|
2935
|
+
* facts are emitted over the whole project. A process that reads the same
|
|
2936
|
+
* project after every edit hands a run one of these, and a file whose text
|
|
2937
|
+
* is the same as last time gets last time's tree back. The trees are
|
|
2938
|
+
* WASM memory the garbage collector cannot see, so a tree that is replaced
|
|
2939
|
+
* or whose file went away is handed to `release`.
|
|
2940
|
+
*/
|
|
2941
|
+
declare class KeptParses<T> {
|
|
2942
|
+
private readonly release;
|
|
2943
|
+
private readonly byFile;
|
|
2944
|
+
constructor(release?: (value: T) => void);
|
|
2945
|
+
parse(file: string, source: string, make: (source: string) => Promise<T>): Promise<T>;
|
|
2946
|
+
/** Lets go of every file not in this run. */
|
|
2947
|
+
keepOnly(files: Iterable<string>): void;
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2825
2950
|
/**
|
|
2826
2951
|
* Checks that tell you a pack is probably not working.
|
|
2827
2952
|
*
|
|
@@ -2834,8 +2959,8 @@ declare function buildUngatedExtractionReport(args: {
|
|
|
2834
2959
|
* signal. What makes zero a signal is the count before it. A pack whose
|
|
2835
2960
|
* import gate picked forty files and whose discovery then found no unit
|
|
2836
2961
|
* in any of them said "look here" and failed to look. Every later stage
|
|
2837
|
-
* works the same way,
|
|
2838
|
-
*
|
|
2962
|
+
* works the same way, so the check is one comparison run over a list of
|
|
2963
|
+
* stage pairs instead of one check written per stage.
|
|
2839
2964
|
*/
|
|
2840
2965
|
|
|
2841
2966
|
/** One thing that looks wrong, reported the way the dogfood invariants are. */
|
|
@@ -2869,13 +2994,13 @@ interface HealthCheck {
|
|
|
2869
2994
|
violations: HealthViolation[];
|
|
2870
2995
|
}
|
|
2871
2996
|
/**
|
|
2872
|
-
*
|
|
2997
|
+
* How much of one pack is data and how much is code (#542).
|
|
2873
2998
|
*
|
|
2874
|
-
*
|
|
2875
|
-
*
|
|
2876
|
-
*
|
|
2877
|
-
*
|
|
2878
|
-
*
|
|
2999
|
+
* A pack with every link written as data runs on any adapter with the
|
|
3000
|
+
* executor ops. A link written as a function runs only where its own
|
|
3001
|
+
* language does, and one that reads the syntax tree runs only on the
|
|
3002
|
+
* adapter whose tree it reads. All three are allowed, and this counts
|
|
3003
|
+
* each kind.
|
|
2879
3004
|
*/
|
|
2880
3005
|
interface PackGradient {
|
|
2881
3006
|
pack: string;
|
|
@@ -2906,16 +3031,13 @@ declare function evaluatePackHealth(report: ExtractionReport): HealthCheck[];
|
|
|
2906
3031
|
declare function formatPackHealth(checks: ReadonlyArray<HealthCheck>, audiences: ReadonlyArray<HealthCheck["audience"]>): string;
|
|
2907
3032
|
|
|
2908
3033
|
/**
|
|
2909
|
-
*
|
|
2910
|
-
*
|
|
2911
|
-
*
|
|
2912
|
-
*
|
|
2913
|
-
* reachable closure" without engaging a profiler. Cheap enough to
|
|
2914
|
-
* always be on; a phase that isn't entered contributes zero.
|
|
3034
|
+
* Wall time per named phase of an adapter run, so the CLI can print
|
|
3035
|
+
* "extract took 17s, of which 11s was in the reachable closure" without
|
|
3036
|
+
* a profiler. It is cheap enough to leave on, and a phase that never
|
|
3037
|
+
* runs adds nothing.
|
|
2915
3038
|
*
|
|
2916
|
-
*
|
|
2917
|
-
*
|
|
2918
|
-
* long-running daemon (LSP / file-watcher), revisit then.
|
|
3039
|
+
* Everything stays in one process and goes to stdout, so there is no
|
|
3040
|
+
* OpenTelemetry dependency for consumers to inherit.
|
|
2919
3041
|
*/
|
|
2920
3042
|
interface TimingReport {
|
|
2921
3043
|
totalMs: number;
|
|
@@ -2928,48 +3050,34 @@ interface TimingReport {
|
|
|
2928
3050
|
interface Timer {
|
|
2929
3051
|
/** Run `fn`, accumulate wall time under `label`, return its result. */
|
|
2930
3052
|
time<T>(label: string, fn: () => T): T;
|
|
2931
|
-
/**
|
|
3053
|
+
/** The same as `time`, for an async `fn`. */
|
|
2932
3054
|
timeAsync<T>(label: string, fn: () => Promise<T>): Promise<T>;
|
|
2933
3055
|
/** Snapshot of all accumulated phases, ordered by total time descending. */
|
|
2934
3056
|
report(): TimingReport;
|
|
2935
3057
|
}
|
|
2936
|
-
/**
|
|
2937
|
-
* Build a fresh timer. Each adapter run gets its own, keeps
|
|
2938
|
-
* concurrent extracts independent (irrelevant today, will matter if
|
|
2939
|
-
* we add a server mode).
|
|
2940
|
-
*/
|
|
3058
|
+
/** Each adapter run gets its own timer, so two runs at once never mix their phases. */
|
|
2941
3059
|
declare function createTimer(): Timer;
|
|
2942
|
-
/**
|
|
3060
|
+
/** A timer that runs `fn` and records nothing, for callers that do not want timing. */
|
|
2943
3061
|
declare function noopTimer(): Timer;
|
|
2944
3062
|
|
|
2945
3063
|
/**
|
|
2946
|
-
* The vocabulary a language adapter implements so that any declared
|
|
2947
|
-
*
|
|
3064
|
+
* The vocabulary a language adapter implements so that any declared pack
|
|
3065
|
+
* runs on it. A pack is data, so the adapter has to turn "did this module
|
|
3066
|
+
* declare the method" into a yes or no about a particular program, and
|
|
3067
|
+
* these interfaces are everything a pack can ask of it.
|
|
2948
3068
|
*
|
|
2949
|
-
*
|
|
2950
|
-
*
|
|
2951
|
-
*
|
|
2952
|
-
* ask for. An adapter implements them once and every pack runs there,
|
|
2953
|
-
* which is what lets one pack drive several languages.
|
|
2954
|
-
*
|
|
2955
|
-
* They live here rather than beside the builders that produce them,
|
|
2956
|
-
* because an adapter needs this and none of the rest: the TypeScript
|
|
2957
|
-
* adapter imports these types and no runtime code from
|
|
2958
|
-
* `@suss/recognize`. Python and Ruby will want the same.
|
|
3069
|
+
* They are in this package rather than beside the builders in
|
|
3070
|
+
* `@suss/recognize`, so an adapter imports the types without that
|
|
3071
|
+
* package's runtime code. Only the TypeScript adapter implements them.
|
|
2959
3072
|
*
|
|
2960
3073
|
* Nothing here mentions a syntax tree. A pack that needs the tree goes
|
|
2961
3074
|
* through `@suss/recognize/ast`, a separate import so that reaching for
|
|
2962
3075
|
* it shows up in a diff and in the pack health report.
|
|
2963
3076
|
*/
|
|
2964
3077
|
/**
|
|
2965
|
-
* How a pack pins down the receiver a call is on.
|
|
2966
|
-
*
|
|
2967
|
-
*
|
|
2968
|
-
* express()`), `imported`, `anchored` (a chain read from its anchor
|
|
2969
|
-
* call), `inherits` (a Ruby or Python receiver matched by ancestry) and
|
|
2970
|
-
* `global` (`process.env`, bare `fetch`). Each arrives as a member here
|
|
2971
|
-
* with an entry in every adapter's dispatch table. The README beside
|
|
2972
|
-
* this file says which starts are not receiver-shaped at all.
|
|
3078
|
+
* How a pack pins down the receiver a call is on. The recognize package's
|
|
3079
|
+
* DESIGN.md lists the origins not built yet (#542) and the places a match
|
|
3080
|
+
* can start that are not receivers.
|
|
2973
3081
|
*/
|
|
2974
3082
|
type ReceiverOrigin = DeclaredBy | ConstructedFrom;
|
|
2975
3083
|
/**
|
|
@@ -3003,29 +3111,41 @@ interface ConstructedFrom {
|
|
|
3003
3111
|
readonly named?: readonly string[];
|
|
3004
3112
|
}
|
|
3005
3113
|
|
|
3114
|
+
/**
|
|
3115
|
+
* What a reader returns for a name nothing in the source settles.
|
|
3116
|
+
* `"nothing"` returns null, and `"reference"` returns the name of the
|
|
3117
|
+
* value to look up instead.
|
|
3118
|
+
*/
|
|
3006
3119
|
type UnsettledName = "nothing" | "reference";
|
|
3007
3120
|
/**
|
|
3008
3121
|
* One value a call states, as the questions a pack can ask about it.
|
|
3009
3122
|
*
|
|
3010
|
-
* `CallOps` reaches
|
|
3011
|
-
* values that are not calls. A library that takes one request object
|
|
3012
|
-
*
|
|
3013
|
-
*
|
|
3014
|
-
*
|
|
3015
|
-
* node. So the rule it writes runs wherever the ops do.
|
|
3123
|
+
* `CallOps` reaches the calls around the one in hand, and this reaches the
|
|
3124
|
+
* values that are not calls. A library that takes one request object puts
|
|
3125
|
+
* everything the call does inside it, as a list or as a string in the
|
|
3126
|
+
* library's own query language. A pack that reads those gets this instead
|
|
3127
|
+
* of the adapter's node, so its rule runs on any adapter with the ops.
|
|
3016
3128
|
*/
|
|
3017
3129
|
interface ValueOps {
|
|
3018
3130
|
/** The text of the string the source wrote, or null for anything else. */
|
|
3019
3131
|
text(): string | null;
|
|
3020
3132
|
/**
|
|
3021
|
-
*
|
|
3022
|
-
* writing it out. A queue URL is nearly always
|
|
3133
|
+
* The name of this value, when the source refers to it by name instead
|
|
3134
|
+
* of writing it out. A queue URL is nearly always
|
|
3023
3135
|
* `process.env.ORDERS_QUEUE_URL`, so the value only exists at deploy
|
|
3024
|
-
* time and the env var's name
|
|
3025
|
-
*
|
|
3026
|
-
*
|
|
3136
|
+
* time and both sides of the boundary use the env var's name.
|
|
3137
|
+
* `"reference"` returns that name, and `"nothing"` returns null for
|
|
3138
|
+
* anything the source does not settle.
|
|
3027
3139
|
*/
|
|
3028
3140
|
name(unsettled: UnsettledName): string | null;
|
|
3141
|
+
/**
|
|
3142
|
+
* Every string this value can be, when the source limits it to a
|
|
3143
|
+
* few. `` `record.${op}` `` with `op` typed `"a" | "b"` is
|
|
3144
|
+
* `record.a` and `record.b`. Null when part of it could be anything,
|
|
3145
|
+
* or when there are more than `cap`. An adapter that has not
|
|
3146
|
+
* implemented this leaves it out, and a reader has only `name`.
|
|
3147
|
+
*/
|
|
3148
|
+
names?(cap: number): readonly string[] | null;
|
|
3029
3149
|
/**
|
|
3030
3150
|
* The yes or no the source wrote here, or null for anything else. A
|
|
3031
3151
|
* library that asks which fields a call wants states them as a map of
|
|
@@ -3061,16 +3181,17 @@ interface ValueOps {
|
|
|
3061
3181
|
* a call is null rather than being dropped, so the two lists stay in
|
|
3062
3182
|
* step.
|
|
3063
3183
|
*
|
|
3064
|
-
* What a hole
|
|
3065
|
-
* there, so a query that
|
|
3184
|
+
* What a hole means is up to the pack. This only reports what was
|
|
3185
|
+
* written there, so a query that interpolates a table object can still
|
|
3186
|
+
* be read.
|
|
3066
3187
|
*/
|
|
3067
3188
|
holes(): readonly (CallOps | null)[];
|
|
3068
3189
|
/**
|
|
3069
3190
|
* The same holes as values, in the same order, for a reader that
|
|
3070
3191
|
* wants what the source settled a hole to rather than what call was
|
|
3071
|
-
* written there. A table kept in a module constant
|
|
3072
|
-
* hole is a name,
|
|
3073
|
-
*
|
|
3192
|
+
* written there. A table name kept in a module constant needs this:
|
|
3193
|
+
* the hole is a name, so `holes` gives null for it and this gives the
|
|
3194
|
+
* constant.
|
|
3074
3195
|
*
|
|
3075
3196
|
* An adapter that has not implemented this leaves it out, and a
|
|
3076
3197
|
* reader then has only the calls.
|
|
@@ -3097,7 +3218,7 @@ interface ValueEntry {
|
|
|
3097
3218
|
*
|
|
3098
3219
|
* Two members give back another `CallOps`, which is how these questions
|
|
3099
3220
|
* reach a call next to this one. A chain of calls and a command object
|
|
3100
|
-
* are both these same questions asked one step along, so neither
|
|
3221
|
+
* are both these same questions asked one step along, so neither needs
|
|
3101
3222
|
* a question of its own.
|
|
3102
3223
|
*/
|
|
3103
3224
|
interface CallOps {
|
|
@@ -3106,11 +3227,9 @@ interface CallOps {
|
|
|
3106
3227
|
/** Whether the receiver came from where the origin says. */
|
|
3107
3228
|
receiverIsFrom(origin: ReceiverOrigin): boolean;
|
|
3108
3229
|
/**
|
|
3109
|
-
* Whether the
|
|
3110
|
-
*
|
|
3111
|
-
*
|
|
3112
|
-
* an argument asks this of it before reading anything, so it never
|
|
3113
|
-
* reads an argument that was never the one.
|
|
3230
|
+
* Whether the callee itself came from where the origin says. A pack
|
|
3231
|
+
* that steps to an argument such as `new GetCommand(...)` asks this
|
|
3232
|
+
* first, so it never reads a command from some other library.
|
|
3114
3233
|
*/
|
|
3115
3234
|
isFrom(origin: ReceiverOrigin): boolean;
|
|
3116
3235
|
/** How many arguments the call passes. */
|
|
@@ -3132,6 +3251,23 @@ interface CallOps {
|
|
|
3132
3251
|
* class, the same as any other call.
|
|
3133
3252
|
*/
|
|
3134
3253
|
argument(index: number): CallOps | null;
|
|
3254
|
+
/**
|
|
3255
|
+
* Said by a chain that steps to the arguments of every call it sees,
|
|
3256
|
+
* before it reads the first one: at every call the walk visits, it
|
|
3257
|
+
* will read the arguments from `first` through `last`, or to the end
|
|
3258
|
+
* when `last` is left out. An adapter that asks a separate question
|
|
3259
|
+
* for each read can ask about all of them at once. Leaving it out
|
|
3260
|
+
* changes how fast a run is and nothing else.
|
|
3261
|
+
*/
|
|
3262
|
+
readingArguments?(first: number, last?: number): void;
|
|
3263
|
+
/**
|
|
3264
|
+
* Said by a chain before it asks what made a receiver: at every call
|
|
3265
|
+
* the walk visits whose method is one of `methods`, spelled as the
|
|
3266
|
+
* source spells it, it will ask about the receiver. An adapter can
|
|
3267
|
+
* then ask about all of them at once, and leaving it out changes only
|
|
3268
|
+
* how fast a run is.
|
|
3269
|
+
*/
|
|
3270
|
+
readingReceivers?(methods: ReadonlySet<string>): void;
|
|
3135
3271
|
/**
|
|
3136
3272
|
* The call the callee itself was written as, or null when nothing
|
|
3137
3273
|
* wrote it as one. A class a factory made is the case: `new
|
|
@@ -3154,7 +3290,7 @@ interface CallOps {
|
|
|
3154
3290
|
*/
|
|
3155
3291
|
parameterReadsAt?(index: number): readonly string[] | null;
|
|
3156
3292
|
/**
|
|
3157
|
-
*
|
|
3293
|
+
* One property of the object literal passed at this position. A
|
|
3158
3294
|
* property bag is not a call, so nothing else here reaches into one.
|
|
3159
3295
|
*/
|
|
3160
3296
|
propertyAt(index: number, property: string, unsettled: UnsettledName): string | null;
|
|
@@ -3167,13 +3303,42 @@ interface CallOps {
|
|
|
3167
3303
|
valueAt(index: number): ValueOps | null;
|
|
3168
3304
|
/**
|
|
3169
3305
|
* The one call behind the receiver the origin accepts, however many
|
|
3170
|
-
* name, construction, or query hops separate them
|
|
3171
|
-
* `model(...)` behind a document
|
|
3306
|
+
* name, construction, or query hops separate them, such as Mongoose's
|
|
3307
|
+
* `model(...)` behind a document. Null when nothing
|
|
3172
3308
|
* behind the receiver matches, when two distinct calls do (picking
|
|
3173
3309
|
* one would depend on the order facts arrived), and for an origin
|
|
3174
3310
|
* kind with no construction to hand back.
|
|
3175
3311
|
*/
|
|
3176
3312
|
anchorCall?(origin: ReceiverOrigin): CallOps | null;
|
|
3313
|
+
/**
|
|
3314
|
+
* The class the argument in this position refers to, or null when it
|
|
3315
|
+
* refers to none the run declares. An ORM that takes an entity class,
|
|
3316
|
+
* `InjectRepository(Order)`, keeps the table on the class itself.
|
|
3317
|
+
*/
|
|
3318
|
+
classAt?(index: number): ClassOps | null;
|
|
3319
|
+
/**
|
|
3320
|
+
* Said by a recognizer about an effect it built from this call: the
|
|
3321
|
+
* value the call states for each column the effect writes or picks
|
|
3322
|
+
* rows by. The adapter asks where each value came from and records it
|
|
3323
|
+
* on the transition. Leaving it out changes only that record.
|
|
3324
|
+
*/
|
|
3325
|
+
statesSlots?(effect: Effect, slots: readonly StatedSlot[]): void;
|
|
3326
|
+
}
|
|
3327
|
+
/** A column an effect writes or picks rows by, and the value stated for it. */
|
|
3328
|
+
interface StatedSlot {
|
|
3329
|
+
readonly slot: "field" | "selector";
|
|
3330
|
+
readonly name: string;
|
|
3331
|
+
readonly value: ValueOps;
|
|
3332
|
+
}
|
|
3333
|
+
/** One class a call refers to, as the questions a pack can ask about it. */
|
|
3334
|
+
interface ClassOps {
|
|
3335
|
+
/** The name the class is declared under, or null when it has none. */
|
|
3336
|
+
name(): string | null;
|
|
3337
|
+
/**
|
|
3338
|
+
* The one decorator call on the class that the origin accepts, such
|
|
3339
|
+
* as `@Entity("orders")`. Null when none does, or when two do.
|
|
3340
|
+
*/
|
|
3341
|
+
decorator(origin: ReceiverOrigin): CallOps | null;
|
|
3177
3342
|
}
|
|
3178
3343
|
/**
|
|
3179
3344
|
* The property an adapter puts its ops on, in the context it hands a
|
|
@@ -3194,26 +3359,27 @@ interface AstCapableOps extends CallOps {
|
|
|
3194
3359
|
}
|
|
3195
3360
|
|
|
3196
3361
|
/**
|
|
3197
|
-
* The assembly engine
|
|
3198
|
-
*
|
|
3199
|
-
* fits in the pipeline.
|
|
3362
|
+
* The assembly engine. A language adapter hands over a `RawCodeStructure`,
|
|
3363
|
+
* and `assembleSummary` turns it into a `BehavioralSummary`. The package
|
|
3364
|
+
* README explains where that step fits in the pipeline.
|
|
3200
3365
|
*
|
|
3201
|
-
*
|
|
3202
|
-
*
|
|
3203
|
-
*
|
|
3204
|
-
*
|
|
3205
|
-
*
|
|
3366
|
+
* `RawCodeStructure` and the raw types around it are the contract every
|
|
3367
|
+
* adapter and pack implements, so adding a field here changes that
|
|
3368
|
+
* contract. This is also the only module that turns a `Reading` into a
|
|
3369
|
+
* claim on a summary. Adapters pass readings over uncollapsed, and the rule
|
|
3370
|
+
* for collapsing them is below.
|
|
3206
3371
|
*/
|
|
3207
3372
|
|
|
3208
3373
|
interface RawParameter {
|
|
3209
3374
|
name: string;
|
|
3210
3375
|
position: number;
|
|
3211
3376
|
/**
|
|
3212
|
-
* What the parameter is for, in the library's own vocabulary.
|
|
3213
|
-
* adapter could not tell,
|
|
3214
|
-
* goes in `readings`.
|
|
3377
|
+
* What the parameter is for, in the library's own vocabulary. Null when
|
|
3378
|
+
* the adapter could not tell, with the reason in `readings`.
|
|
3215
3379
|
*/
|
|
3216
3380
|
role: string | null;
|
|
3381
|
+
/** Which field of the role's value the parameter is, when the library hands over one field. */
|
|
3382
|
+
field?: string;
|
|
3217
3383
|
typeText: string | null;
|
|
3218
3384
|
}
|
|
3219
3385
|
interface RawCondition {
|
|
@@ -3223,7 +3389,7 @@ interface RawCondition {
|
|
|
3223
3389
|
source: ConditionSource;
|
|
3224
3390
|
}
|
|
3225
3391
|
interface RawTerminal {
|
|
3226
|
-
kind: "response" | "throw" | "return" | "render" | "delegate" | "emit" | "void";
|
|
3392
|
+
kind: "response" | "throw" | "return" | "render" | "delegate" | "emit" | "void" | "exit";
|
|
3227
3393
|
statusCode: {
|
|
3228
3394
|
type: "literal";
|
|
3229
3395
|
value: number;
|
|
@@ -3302,7 +3468,7 @@ type RawEffect = {
|
|
|
3302
3468
|
callee: string;
|
|
3303
3469
|
args: EffectArg[];
|
|
3304
3470
|
async: boolean;
|
|
3305
|
-
/**
|
|
3471
|
+
/** An empty list means the call is not under any guard, so it always fires. */
|
|
3306
3472
|
preconditions?: RawCondition[];
|
|
3307
3473
|
} | {
|
|
3308
3474
|
type: "emission";
|
|
@@ -3320,6 +3486,8 @@ interface RawBranch {
|
|
|
3320
3486
|
* `RawEffect` conversion that `effects` goes through.
|
|
3321
3487
|
*/
|
|
3322
3488
|
extraEffects?: Effect[];
|
|
3489
|
+
/** Where the values in the slots of `extraEffects` came from. */
|
|
3490
|
+
provenance?: RawProvenance[];
|
|
3323
3491
|
location: {
|
|
3324
3492
|
start: number;
|
|
3325
3493
|
end: number;
|
|
@@ -3340,6 +3508,17 @@ interface RawBranch {
|
|
|
3340
3508
|
*/
|
|
3341
3509
|
bodyShapeReading?: DefaultedReading<TypeShape>;
|
|
3342
3510
|
}
|
|
3511
|
+
/**
|
|
3512
|
+
* Where the value in one slot came from, keyed by the effect object an
|
|
3513
|
+
* adapter put in `extraEffects`. Assembly folds repeated effects, so it
|
|
3514
|
+
* works out each effect's position only once the list is final.
|
|
3515
|
+
*/
|
|
3516
|
+
interface RawProvenance {
|
|
3517
|
+
effect: Effect;
|
|
3518
|
+
slot: ProvenanceSlot["slot"];
|
|
3519
|
+
name: string;
|
|
3520
|
+
from: ValueRef[];
|
|
3521
|
+
}
|
|
3343
3522
|
interface RawDependencyCall {
|
|
3344
3523
|
name: string;
|
|
3345
3524
|
assignedTo: string | null;
|
|
@@ -3420,14 +3599,14 @@ interface RawCodeStructure {
|
|
|
3420
3599
|
bodyContent?: BodyContent;
|
|
3421
3600
|
dependencyCalls: RawDependencyCall[];
|
|
3422
3601
|
/**
|
|
3423
|
-
* Reads the adapter saw in the body that never flow into a condition
|
|
3424
|
-
*
|
|
3425
|
-
* `inputReads`
|
|
3602
|
+
* Reads the adapter saw in the body that never flow into a condition or
|
|
3603
|
+
* an output value, such as a render tree's `props.title`. They are merged
|
|
3604
|
+
* into `inputReads` with the ones derived from conditions and outputs.
|
|
3426
3605
|
*/
|
|
3427
3606
|
extraInputReads?: InputRead[];
|
|
3428
3607
|
declaredContract: RawDeclaredContract | null;
|
|
3429
|
-
/** The property a consumer
|
|
3430
|
-
* axios
|
|
3608
|
+
/** The property a consumer reads the body through, such as `data` for
|
|
3609
|
+
* axios, so the checker can unwrap it without a rule per pack. */
|
|
3431
3610
|
bodyAccessors?: string[];
|
|
3432
3611
|
/** The same, for the status: `status` for fetch and for axios. */
|
|
3433
3612
|
statusAccessors?: string[];
|
|
@@ -3451,6 +3630,8 @@ interface RawCodeStructure {
|
|
|
3451
3630
|
wrappers?: WrapperReference[];
|
|
3452
3631
|
/** Where a REST pack's handlers read each part of the request, from the pack. */
|
|
3453
3632
|
requestSpelling?: RequestSpellingMetadata;
|
|
3633
|
+
/** What a test pack found about a `test` unit: whether it is skipped, and what it replaces with a mock. */
|
|
3634
|
+
test?: TestMetadata;
|
|
3454
3635
|
/** The extractor cannot derive this. An adapter that has the SDL and knows
|
|
3455
3636
|
* which field the resolver serves fills it in. */
|
|
3456
3637
|
graphqlDeclaredContract?: GraphqlDeclaredContract;
|
|
@@ -3508,4 +3689,4 @@ declare function rawConditionToPredicate(c: RawCondition): Predicate;
|
|
|
3508
3689
|
declare function effectToIR(effect: RawEffect): Effect;
|
|
3509
3690
|
declare function paramToInput(param: RawParameter): Input;
|
|
3510
3691
|
|
|
3511
|
-
export { type AccessRecognizer, type AdapterCodeStamp, type AdapterStamp, type AstCapableOps, type BindingExtraction, type BodyContent, type CacheAttribution, type CacheDiagnostic, type CacheInput, type CacheLayer, type CacheLookup, type CallOps, type CaseGroup, type ChannelSource, type ChosenReading, type ConditionHandle, type ConditionInfo, type ConditionSource, type ConstructedFrom, type ContractPattern, type DeclaredBinding, type DeclaredBy, type DeclaredMatch, type DefaultedReading, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type EmptyStage, type ExitKind, type ExtractionReport, type ExtractorOptions, type FailureDelivery, type HealthCheck, type HealthViolation, type HelperDeclarations, type HelperSearch, type HelperSink, type HelperValue, IdMap, IdSet, type Identified, type InputMappingPattern, type InputRead, type InvocationRecognizer, type LanguageAdapter, type LoweredStatementParts, MAX_ENTRIES, MAX_PATHS, type ModuleInitOptions, type NodeVisitor, type OpsCarrier, type PackDeclarations, type PackFailure, type PackFunnel, type PackGradient, type PackTally, type PartialPlan, PathBudgetExceeded, type PatternPack, type ProjectHelper, type ProjectHelpers, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type Reading, type ReceiverOrigin, type RegistrationHelper, type ResponsePropertyMapping, type ResponsePropertyMeaning, type RootRecord, SKIP_CHILDREN, type SourceRange, type StatementBlock, type StructuredPathConditionsInput, type StructuredPathConditionsResult, type StructuredStatement, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type Timer, type TimingReport, type TransparentWrapper, UnmodeledFlow, type UnsettledName, type ValueEntry, type ValueOps, type WalkableNode, type WrapperMethodRegistration, type WrapperOptionRegistration, type WrapperRegistration, absentReading, ambiguousReading, andThenReading, assembleSummary, assessConfidence, buildUngatedExtractionReport, commonDirectoryOf, composeWrappers, computeContentHash, computeDistHashFrom, configuredCallOption, createAdapterStamp, createCacheLayer, createPackTallies, createTimer, detectGaps, effectToIR, emptyTally, enumerateOrDegrade, enumerateStructuredPaths, evaluatePackHealth, firstWrittenReading, formatPackHealth, guardsHoldOn, httpRouteDiscovery, makeTransitionId, mapReading, moduleInitStructure, noopTimer, packGradients, paramToInput, projectFileStamp, rawConditionToPredicate, recordPackFailure, registrationHelperDiscovery, routeHelperIndex, runDigest, runsBefore, scopeOption, sharedGatingConditions, stampModuleImports, storageSystemOption, summaryCountsByPack, tallyUnit, terminalToOutput, unreadableReading, unwrapJsonStringify, valueToReadFurtherFrom, walkDescendants, wrapperDiscovery, writtenReading };
|
|
3692
|
+
export { type AccessRecognizer, type AdapterCodeStamp, type AdapterStamp, type ArgumentParser, type AstCapableOps, type BindingExtraction, type BodyContent, type CacheAttribution, type CacheDiagnostic, type CacheInput, type CacheLayer, type CacheLookup, type CallOps, type CaseGroup, type ChannelSource, type ChosenReading, type ClassOps, type ConditionHandle, type ConditionInfo, type ConditionSource, type ConstructedFrom, type ContractPattern, type DeclaredBinding, type DeclaredBy, type DeclaredMatch, type DeclaredModule, type DefaultedReading, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type EmptyStage, EnclosingNodes, type ExitKind, type ExtractionConfig, type ExtractionReport, type ExtractorOptions, type FailureDelivery, type GlobalPrefixCall, type HealthCheck, type HealthViolation, type HelperDeclarations, type HelperSearch, type HelperSink, type HelperValue, IdMap, IdSet, type Identified, type InputMappingPattern, type InputRead, type InvocationRecognizer, KeptParses, type LanguageAdapter, type LoweredStatementParts, MAX_ENTRIES, MAX_PATHS, MODULE_SURFACE_RECOGNITION, type ModuleInitOptions, type NodeVisitor, type OpsCarrier, type PackDeclarations, type PackFailure, type PackFunnel, type PackGradient, type PackTally, type ParameterSupplier, type ParsedNode, type PartialPlan, PathBudgetExceeded, type PatternPack, type ProjectHelper, type ProjectHelpers, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawProvenance, type RawTerminal, type Reading, type ReceiverOrigin, type RegistrationHelper, type ResponsePropertyMapping, type ResponsePropertyMeaning, type RootRecord, SKIP_CHILDREN, type SettledModule, type SourceRange, type SourceSpelling, type StatedSlot, type StatementBlock, type StructuredPathConditionsInput, type StructuredPathConditionsResult, type StructuredStatement, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type TestCaseMatch, type Timer, type TimingReport, type TransparentWrapper, type UnitRecord, UnmodeledFlow, type UnsettledName, type ValueEntry, type ValueOps, type WalkEnd, type WalkableNode, type WrapperMethodRegistration, type WrapperOptionRegistration, type WrapperRegistration, absentReading, ambiguousReading, andThenReading, assembleSummary, assessConfidence, buildUngatedExtractionReport, commonDirectoryOf, composeWrappers, computeContentHash, computeDistHashFrom, configuredCallOption, createAdapterStamp, createCacheLayer, createPackTallies, createTimer, detectGaps, effectToIR, emptyTally, enumerateOrDegrade, enumerateStructuredPaths, evaluatePackHealth, extractionConfigStamp, firstWrittenReading, formatPackHealth, guardsHoldOn, httpRouteDiscovery, isListedTestFile, makeTransitionId, mapReading, markReturnsAsExitCode, matchesTestFileName, moduleInitStructure, moduleOfFile, noopTimer, packGradients, paramToInput, projectFileStamp, rawConditionToPredicate, recordPackFailure, registrationHelperDiscovery, routeHelperIndex, runDigest, runsBefore, scopeOption, settleModules, sharedGatingConditions, sourceRefsOf, stampMayMissAWrite, stampModuleImports, stampModules, storageSystemOption, summaryCountsByPack, tallyUnit, terminalToOutput, unreadableReading, unwrapJsonStringify, valueToReadFurtherFrom, walkDescendants, wrapperDiscovery, writtenReading };
|