@suss/extractor 0.3.1 → 0.4.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 +2 -2
- package/dist/index.cjs +626 -17
- package/dist/index.d.cts +962 -571
- package/dist/index.d.ts +962 -571
- package/dist/index.js +614 -16
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,260 @@
|
|
|
1
|
-
import { Effect, MessageBusSemantics, DeployableUnit, Predicate, TypeShape, RenderNode, CodeUnitKind, BoundaryBinding,
|
|
1
|
+
import { Effect, MessageBusSemantics, DeployableUnit, Predicate, BehavioralSummary, TypeShape, RenderNode, CodeUnitKind, BoundaryBinding, GraphqlDeclaredContract, ConfidenceInfo, Transition, Gap, Input, Output } from '@suss/behavioral-ir';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The language-neutral form the path engine walks: statement kind, condition
|
|
5
|
+
* handle, children, and exit kind. Each language's adapter lowers its own AST
|
|
6
|
+
* into this, and the path enumeration never touches a language-specific node
|
|
7
|
+
* again.
|
|
8
|
+
*
|
|
9
|
+
* The `Cond` type parameter is the language's own handle for a condition
|
|
10
|
+
* expression (a ts-morph Expression for TypeScript, a tree-sitter node for a
|
|
11
|
+
* language added later). The engine passes it through untouched for a caller
|
|
12
|
+
* to parse afterwards, and never looks inside it.
|
|
13
|
+
*/
|
|
14
|
+
/** Where a condition came from, in terms of what that branch's body does. */
|
|
15
|
+
type ConditionSource = "explicit" | "earlyReturn" | "earlyThrow" | "catchBlock";
|
|
16
|
+
/** Whether a statement's own subtree leaves the unit by returning or throwing. A throw anywhere beats a return anywhere. Never set for break or continue. */
|
|
17
|
+
type ExitKind = "return" | "throw" | null;
|
|
18
|
+
/**
|
|
19
|
+
* A test the engine passes through without looking inside: its display text
|
|
20
|
+
* plus the language's own expression handle. The handle is null for a
|
|
21
|
+
* synthetic condition, such as a loop's "some iteration of" marker or a
|
|
22
|
+
* switch group's disjunction text.
|
|
23
|
+
*/
|
|
24
|
+
interface ConditionHandle<Cond> {
|
|
25
|
+
readonly sourceText: string;
|
|
26
|
+
readonly expression: Cond | null;
|
|
27
|
+
}
|
|
28
|
+
/** One condition attached to a caller-visible transition. */
|
|
29
|
+
interface ConditionInfo<Cond> {
|
|
30
|
+
readonly sourceText: string;
|
|
31
|
+
readonly polarity: "positive" | "negative";
|
|
32
|
+
readonly source: ConditionSource;
|
|
33
|
+
readonly expression: Cond | null;
|
|
34
|
+
}
|
|
35
|
+
/** A statement list: an if arm, a loop body, a try/catch/finally block. */
|
|
36
|
+
type StatementBlock<Cond> = readonly StructuredStatement<Cond>[];
|
|
37
|
+
/**
|
|
38
|
+
* One switch or match case group, already merged out of the language's own
|
|
39
|
+
* grammar (TypeScript stacks several empty-bodied labels on top of the
|
|
40
|
+
* clause that finally has a body, and the lowering step folds that
|
|
41
|
+
* into one group with a joined condition). `condition` is null for the
|
|
42
|
+
* default or wildcard group. `body` has any language-specific
|
|
43
|
+
* statement that ends a fallthrough (a trailing break) already stripped
|
|
44
|
+
* out, and `hasTrailingBreak` records whether one was there.
|
|
45
|
+
*/
|
|
46
|
+
interface CaseGroup<Cond> {
|
|
47
|
+
readonly condition: ConditionHandle<Cond> | null;
|
|
48
|
+
readonly hasTrailingBreak: boolean;
|
|
49
|
+
readonly body: StatementBlock<Cond>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* One statement in the unit's control flow, already lowered out of the
|
|
53
|
+
* source language. Every variant has the same four things on it:
|
|
54
|
+
* `kind` says which construct it is, `condition` (where
|
|
55
|
+
* one applies) is the opaque test, the block and group fields are its
|
|
56
|
+
* children, and `exitKind` is worked out by the lowering step (a deep
|
|
57
|
+
* scan of the statement's own subtree for a return or throw, skipping
|
|
58
|
+
* nested function bodies, the same scan every language needs in order to
|
|
59
|
+
* tell an early return from an early throw).
|
|
60
|
+
*/
|
|
61
|
+
type StructuredStatement<Cond = unknown> = {
|
|
62
|
+
readonly kind: "if";
|
|
63
|
+
readonly condition: ConditionHandle<Cond>;
|
|
64
|
+
readonly thenBody: StatementBlock<Cond>;
|
|
65
|
+
/** null when the source has no else/elif tail at all. */
|
|
66
|
+
readonly elseBody: StatementBlock<Cond> | null;
|
|
67
|
+
readonly exitKind: ExitKind;
|
|
68
|
+
} | {
|
|
69
|
+
readonly kind: "switch";
|
|
70
|
+
/** Source order; at most one group has a null (default) condition. */
|
|
71
|
+
readonly groups: readonly CaseGroup<Cond>[];
|
|
72
|
+
readonly exitKind: ExitKind;
|
|
73
|
+
} | {
|
|
74
|
+
readonly kind: "loop";
|
|
75
|
+
/** Display text for the loop header. The engine builds two synthetic conditions out of it: "some iteration of: ..." and "loop exited via ...: ...". */
|
|
76
|
+
readonly condition: ConditionHandle<Cond>;
|
|
77
|
+
readonly body: StatementBlock<Cond>;
|
|
78
|
+
readonly exitKind: ExitKind;
|
|
79
|
+
} | {
|
|
80
|
+
readonly kind: "try";
|
|
81
|
+
readonly tryBody: StatementBlock<Cond>;
|
|
82
|
+
readonly catchBody: StatementBlock<Cond> | null;
|
|
83
|
+
/** Here only so it can be validated. A finally that exits, or that contains a terminal the caller gave us, is not modeled, and its own conditions are never enumerated. */
|
|
84
|
+
readonly finallyBody: StatementBlock<Cond> | null;
|
|
85
|
+
readonly exitKind: ExitKind;
|
|
86
|
+
} | {
|
|
87
|
+
readonly kind: "exit";
|
|
88
|
+
readonly exit: "return" | "throw" | "break" | "continue";
|
|
89
|
+
readonly exitKind: ExitKind;
|
|
90
|
+
} | {
|
|
91
|
+
/** Anything else: expression statements, declarations, and any statement the enumeration does not branch on. */
|
|
92
|
+
readonly kind: "opaque";
|
|
93
|
+
readonly exitKind: ExitKind;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* `Reading<T>`: what a reader found, in the four states a source can leave a
|
|
98
|
+
* value in.
|
|
99
|
+
*
|
|
100
|
+
* A reader that returns `T | null` makes null mean three things at once: the
|
|
101
|
+
* source left the value out, the source states it and the reader could not
|
|
102
|
+
* evaluate it, or several candidates matched and the reader picked none.
|
|
103
|
+
* Each needs something different said about it on the summary, and the type
|
|
104
|
+
* does not show which the reader meant, so every new reader decides it again
|
|
105
|
+
* and some of them decide wrong.
|
|
106
|
+
*
|
|
107
|
+
* The rule that turns a reading into a claim, a library default, or a gap
|
|
108
|
+
* lives in the summary builder in this package and is not exported.
|
|
109
|
+
*/
|
|
110
|
+
/** Where in a file a value is written, as byte offsets into that file. */
|
|
111
|
+
interface SourceRange {
|
|
112
|
+
start: number;
|
|
113
|
+
end: number;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* What a reader found where a value could be. `written` is the only state
|
|
117
|
+
* with a value a summary may claim, and the other three each say something
|
|
118
|
+
* different about why there is no claim to make.
|
|
119
|
+
*/
|
|
120
|
+
type Reading<T> = {
|
|
121
|
+
kind: "written";
|
|
122
|
+
value: T;
|
|
123
|
+
range: SourceRange;
|
|
124
|
+
} | {
|
|
125
|
+
kind: "absent";
|
|
126
|
+
} | {
|
|
127
|
+
kind: "unreadable";
|
|
128
|
+
reason: string;
|
|
129
|
+
range: SourceRange;
|
|
130
|
+
} | {
|
|
131
|
+
kind: "ambiguous";
|
|
132
|
+
candidates: readonly T[];
|
|
133
|
+
reason: string;
|
|
134
|
+
range: SourceRange;
|
|
135
|
+
};
|
|
136
|
+
/** The source gives this value, here, and the reader read it. */
|
|
137
|
+
declare function writtenReading<T>(value: T, range: SourceRange): Reading<T>;
|
|
138
|
+
/**
|
|
139
|
+
* The source says nothing where this value could be. A library that defines
|
|
140
|
+
* a default for it may still supply one, and the summary builder applies
|
|
141
|
+
* that default only when a pack declares it as data.
|
|
142
|
+
*/
|
|
143
|
+
declare const absentReading: Reading<never>;
|
|
144
|
+
/**
|
|
145
|
+
* The source gives the value and the reader could not evaluate it. The
|
|
146
|
+
* reason is the sentence somebody reading the summary will see, so write it
|
|
147
|
+
* about what could not be read rather than about the code.
|
|
148
|
+
*/
|
|
149
|
+
declare function unreadableReading<T>(reason: string, range: SourceRange): Reading<T>;
|
|
150
|
+
/**
|
|
151
|
+
* Several values could be right and the reader picked none. Keeping the
|
|
152
|
+
* candidates leaves what was found available to whoever later teaches the
|
|
153
|
+
* reader how to choose, and every step afterwards keeps them too.
|
|
154
|
+
*
|
|
155
|
+
* The range is where somebody should look to see the ambiguity. An ambiguity
|
|
156
|
+
* often spans more than one place (two mounts of one router, in two files),
|
|
157
|
+
* so give the site the reading was taken at, the same one an `unreadable`
|
|
158
|
+
* reading here would have given.
|
|
159
|
+
*/
|
|
160
|
+
declare function ambiguousReading<T>(candidates: readonly T[], reason: string, range: SourceRange): Reading<T>;
|
|
161
|
+
/**
|
|
162
|
+
* A reading paired with what the library does when the source says nothing.
|
|
163
|
+
* Only a pack may supply that default, so the value a summary claims for an
|
|
164
|
+
* unstated field is library-defined and lives in the pack alongside
|
|
165
|
+
* everything else the pack already declares.
|
|
166
|
+
*/
|
|
167
|
+
interface DefaultedReading<T> {
|
|
168
|
+
/** What the source said. */
|
|
169
|
+
reading: Reading<T>;
|
|
170
|
+
/**
|
|
171
|
+
* The value the library applies when the source gives none, as the pack
|
|
172
|
+
* declared it. Leave it out when the library defines no default, and then
|
|
173
|
+
* a source that says nothing gets no claim.
|
|
174
|
+
*/
|
|
175
|
+
libraryDefault?: T;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The same reading with its value in another form. A written value goes
|
|
179
|
+
* through `f` and keeps its range. An ambiguous reading's candidates go
|
|
180
|
+
* through `f` too, so the alternatives end up in the same form as the
|
|
181
|
+
* chosen value would. Absent and unreadable readings pass through
|
|
182
|
+
* unchanged, since neither one has a value to convert.
|
|
183
|
+
*/
|
|
184
|
+
declare function mapReading<T, U>(reading: Reading<T>, f: (value: T) => U): Reading<U>;
|
|
185
|
+
/**
|
|
186
|
+
* Read further from what this reading found. `f` runs on a written
|
|
187
|
+
* value with the range it was written at, so a step that turns out to
|
|
188
|
+
* be unreadable can say so against the same syntax.
|
|
189
|
+
*
|
|
190
|
+
* `f` runs on each of an ambiguous reading's candidates too, and the ones
|
|
191
|
+
* that do read come back as the candidates of an ambiguous reading with the
|
|
192
|
+
* same reason. That way the alternatives stay next to the chosen value all
|
|
193
|
+
* the way to the summary, instead of being dropped at the first step
|
|
194
|
+
* that reads further.
|
|
195
|
+
*/
|
|
196
|
+
declare function andThenReading<T, U>(reading: Reading<T>, f: (value: T, range: SourceRange) => Reading<U>): Reading<U>;
|
|
197
|
+
/**
|
|
198
|
+
* Which of several readings a claim comes from, for a value a library lets
|
|
199
|
+
* a source give in more than one place.
|
|
200
|
+
*/
|
|
201
|
+
interface ChosenReading<T> {
|
|
202
|
+
/**
|
|
203
|
+
* The reading a claim comes from: the first that was written, or if none
|
|
204
|
+
* was, the first that failed to read, or absent when all of them were
|
|
205
|
+
* absent.
|
|
206
|
+
*/
|
|
207
|
+
reading: Reading<T>;
|
|
208
|
+
/**
|
|
209
|
+
* The readings the choice passed over that nobody could resolve. A later
|
|
210
|
+
* reading supplying the value does not settle what an earlier one said
|
|
211
|
+
* and could not be read, so these still go to the
|
|
212
|
+
* builder and their reasons still reach the summary.
|
|
213
|
+
*/
|
|
214
|
+
passedOver: readonly Reading<T>[];
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Choose among readings of the same value, taking the first that was
|
|
218
|
+
* written. Nothing is thrown away: whatever the choice passed over and
|
|
219
|
+
* could not read comes back next to it in `passedOver`, so a
|
|
220
|
+
* `response_model` nobody could read still lands as a gap even when the
|
|
221
|
+
* return annotation after it supplies the type.
|
|
222
|
+
*/
|
|
223
|
+
declare function firstWrittenReading<T>(readings: readonly Reading<T>[]): ChosenReading<T>;
|
|
224
|
+
/**
|
|
225
|
+
* What a written reading found, for a reader that has to read further from
|
|
226
|
+
* it before anything is claimed. A route's path template gives the
|
|
227
|
+
* parameters that decide what each of the handler's parameters is, and
|
|
228
|
+
* that has to be settled before there is a summary field to fill in.
|
|
229
|
+
*
|
|
230
|
+
* This applies no default and gives no reason, so most of what a summary
|
|
231
|
+
* says must not be written from it. Hand the reading to the
|
|
232
|
+
* builder instead, and the fixed rule gets applied to it once, somewhere
|
|
233
|
+
* review can see it.
|
|
234
|
+
*
|
|
235
|
+
* The identity fields of a boundary binding are the exception, and the path
|
|
236
|
+
* this reads is one of them. A binding either says where a unit is or says
|
|
237
|
+
* nothing and pairs with nothing, and no pack declares a
|
|
238
|
+
* default for what a boundary is called, so the value the builder would
|
|
239
|
+
* put there is the value this gives back. Hand the reading over as well and
|
|
240
|
+
* the reason still becomes a gap.
|
|
241
|
+
*/
|
|
242
|
+
declare function valueToReadFurtherFrom<T>(reading: Reading<T>): T | null;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The `PatternPack` interface, which is what a framework pack gives a language
|
|
246
|
+
* adapter. The pack says WHAT to look for (which import, which call, which
|
|
247
|
+
* decorator); the adapter knows HOW to find that in its language's AST.
|
|
248
|
+
*
|
|
249
|
+
* Everything here is data, never code. A pack describes a library once and any
|
|
250
|
+
* adapter that understands these patterns can apply it. If you want a pack to
|
|
251
|
+
* compute something, the answer is usually another declarative field here.
|
|
252
|
+
*
|
|
253
|
+
* The sections run in the order an adapter uses them: discovery finds candidate
|
|
254
|
+
* code units, terminals describe how a unit finishes, contract reading and
|
|
255
|
+
* input mapping describe what it declares and takes in, and response property
|
|
256
|
+
* semantics say which property is the body and which is the status.
|
|
257
|
+
*/
|
|
2
258
|
|
|
3
259
|
type DiscoveryMatch = {
|
|
4
260
|
type: "namedExport";
|
|
@@ -20,7 +276,7 @@ type DiscoveryMatch = {
|
|
|
20
276
|
type: "clientCall";
|
|
21
277
|
/** Module the client is imported from, or "global" for built-ins like fetch */
|
|
22
278
|
importModule: string;
|
|
23
|
-
/** Named export or identifier
|
|
279
|
+
/** Named export or identifier, for example "initClient" or "fetch" */
|
|
24
280
|
importName: string;
|
|
25
281
|
/** If set, only match calls to these methods on the client (e.g. ["getUser"]).
|
|
26
282
|
* Unset means any method call (or bare call for globals). */
|
|
@@ -37,11 +293,11 @@ type DiscoveryMatch = {
|
|
|
37
293
|
} | {
|
|
38
294
|
/**
|
|
39
295
|
* A constructor or factory call that takes a configuration object
|
|
40
|
-
* containing a resolver map
|
|
41
|
-
*
|
|
42
|
-
* names (`Query`, `Mutation`, `Subscription`, or
|
|
43
|
-
* names like `User`)
|
|
44
|
-
* are resolver functions.
|
|
296
|
+
* containing a resolver map, which is how code-first GraphQL servers
|
|
297
|
+
* are usually written. The map is two levels deep: outer keys are
|
|
298
|
+
* GraphQL type names (`Query`, `Mutation`, `Subscription`, or
|
|
299
|
+
* object-type names like `User`), and inner keys are field names
|
|
300
|
+
* whose values are resolver functions.
|
|
45
301
|
*
|
|
46
302
|
* Example (Apollo Server v4):
|
|
47
303
|
* ```ts
|
|
@@ -56,41 +312,42 @@ type DiscoveryMatch = {
|
|
|
56
312
|
* ```
|
|
57
313
|
*
|
|
58
314
|
* Each inner function becomes one discovered unit whose binding
|
|
59
|
-
* semantics is `graphql-resolver(typeName, fieldName)`.
|
|
60
|
-
*
|
|
61
|
-
* server uses `new
|
|
315
|
+
* semantics is `graphql-resolver(typeName, fieldName)`. Both
|
|
316
|
+
* `new Ctor(cfg)` and `ctor(cfg)` match, because Apollo's standalone
|
|
317
|
+
* server uses `new` and yoga uses a bare call.
|
|
62
318
|
*/
|
|
63
319
|
type: "resolverMap";
|
|
64
320
|
importModule: string;
|
|
65
321
|
importName: string;
|
|
66
322
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* Apollo
|
|
323
|
+
* The property on the config object that contains the resolver map.
|
|
324
|
+
* This is the library's own config key, so the pack has to give it and
|
|
325
|
+
* the adapter ships no default. Apollo, yoga, and graphql-tools all
|
|
326
|
+
* spell it `"resolvers"`.
|
|
70
327
|
*/
|
|
71
|
-
mapProperty
|
|
328
|
+
mapProperty: string;
|
|
72
329
|
/**
|
|
73
|
-
* GraphQL
|
|
74
|
-
* opt-out for meta-types like `Subscription` that we may want
|
|
75
|
-
*
|
|
330
|
+
* GraphQL types whose fields we DON'T treat as resolvers. This is the
|
|
331
|
+
* opt-out for meta-types like `Subscription` that we may want to handle
|
|
332
|
+
* differently later. Leave it unset to discover every type.
|
|
76
333
|
*/
|
|
77
334
|
excludeTypes?: string[];
|
|
78
335
|
} | {
|
|
79
336
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
337
|
+
* A consumer-side GraphQL hook call, the way Apollo Client and urql
|
|
338
|
+
* are normally used. Each call to one of the listed hooks becomes
|
|
82
339
|
* a `client`-kind code unit whose binding semantics is
|
|
83
340
|
* `graphql-operation(operationType, operationName?)`.
|
|
84
341
|
*
|
|
85
|
-
* The document argument
|
|
86
|
-
* `gql`-tagged template, a const binding (
|
|
87
|
-
* from another
|
|
342
|
+
* The document argument can be written several ways: an inline
|
|
343
|
+
* `gql`-tagged template, a const binding (in this module or imported
|
|
344
|
+
* from another one), a `.graphql` or `.gql` file import, or a
|
|
88
345
|
* generated `TypedDocumentNode` object literal from graphql-codegen
|
|
89
|
-
* client-preset. When the document body
|
|
346
|
+
* client-preset. When the document body cannot be read statically,
|
|
90
347
|
* the operation header falls back to the `TypedDocumentNode` type
|
|
91
|
-
* arguments
|
|
92
|
-
* as `metadata.graphql.unresolvedDocument
|
|
93
|
-
*
|
|
348
|
+
* arguments. A document that still cannot be resolved shows up on the
|
|
349
|
+
* summary as `metadata.graphql.unresolvedDocument`, so the boundary is
|
|
350
|
+
* kept rather than dropped.
|
|
94
351
|
*
|
|
95
352
|
* Example:
|
|
96
353
|
* ```ts
|
|
@@ -102,21 +359,21 @@ type DiscoveryMatch = {
|
|
|
102
359
|
* }
|
|
103
360
|
* ```
|
|
104
361
|
*
|
|
105
|
-
* The adapter records the operation name
|
|
106
|
-
* DiscoveredUnit's `operationInfo
|
|
362
|
+
* The adapter records the operation name and type on the
|
|
363
|
+
* DiscoveredUnit's `operationInfo`, and binding construction uses that
|
|
107
364
|
* to emit `graphql-operation(...)`. The per-hook `operationType`
|
|
108
|
-
*
|
|
109
|
-
* `graphqlImperativeCall.methods`
|
|
365
|
+
* wins when the document header cannot be read, the same way
|
|
366
|
+
* `graphqlImperativeCall.methods` does.
|
|
110
367
|
*/
|
|
111
368
|
type: "graphqlHookCall";
|
|
112
369
|
importModule: string;
|
|
113
370
|
/**
|
|
114
|
-
* Hooks to match on that import, each mapped to the operation
|
|
115
|
-
*
|
|
116
|
-
* `useSubscription`
|
|
117
|
-
* operation type when the document body
|
|
118
|
-
* Each hook is reported as `kind = "client"`
|
|
119
|
-
*
|
|
371
|
+
* Hooks to match on that import, each mapped to the operation type it
|
|
372
|
+
* performs (`useQuery` to query, `useMutation` to mutation,
|
|
373
|
+
* `useSubscription` to subscription). That mapping supplies the
|
|
374
|
+
* operation type when the document body cannot be read statically.
|
|
375
|
+
* Each hook is reported as `kind = "client"` unless a pack overrides
|
|
376
|
+
* that through the enclosing `DiscoveryPattern.kind`.
|
|
120
377
|
*/
|
|
121
378
|
hooks: Array<{
|
|
122
379
|
hookName: string;
|
|
@@ -124,22 +381,21 @@ type DiscoveryMatch = {
|
|
|
124
381
|
}>;
|
|
125
382
|
} | {
|
|
126
383
|
/**
|
|
127
|
-
*
|
|
128
|
-
* `client.mutate({ mutation })`, `client.subscribe({ query })`.
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
384
|
+
* An imperative Apollo-Client-style call: `client.query({ query })`,
|
|
385
|
+
* `client.mutate({ mutation })`, `client.subscribe({ query })`. This is
|
|
386
|
+
* separate from hook calls because the document is on a config-object
|
|
387
|
+
* property rather than the first positional argument.
|
|
388
|
+
*
|
|
389
|
+
* Discovery only fires when the named constructor (usually
|
|
390
|
+
* `ApolloClient`) is imported, because otherwise any object at all
|
|
391
|
+
* with a `query` method would look like a match.
|
|
135
392
|
*
|
|
136
|
-
* Each entry in `methods` specifies the method
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* (`"query"
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* a named document's header wins otherwise.
|
|
393
|
+
* Each entry in `methods` specifies the method called on the client
|
|
394
|
+
* (`"query"`, `"mutate"`, or `"subscribe"`) and the config-object
|
|
395
|
+
* property that contains the gql document
|
|
396
|
+
* (`"query"`, `"mutation"`, and `"query"` respectively). The method
|
|
397
|
+
* name decides the operation type when the gql document's header is
|
|
398
|
+
* anonymous. When the document has a name, its header wins.
|
|
143
399
|
*/
|
|
144
400
|
type: "graphqlImperativeCall";
|
|
145
401
|
importModule: string;
|
|
@@ -152,55 +408,53 @@ type DiscoveryMatch = {
|
|
|
152
408
|
} | {
|
|
153
409
|
/**
|
|
154
410
|
* Treats a TypeScript package's public export surface as a
|
|
155
|
-
* boundary. The adapter reads `package.json` at
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* boundary.
|
|
411
|
+
* boundary. The adapter reads `package.json` at `packageJsonPath`,
|
|
412
|
+
* resolves each reachable entry point (root `.` and any sub-path
|
|
413
|
+
* `exports`), follows barrel re-exports, and emits one discovered
|
|
414
|
+
* unit per exported function. Those units are the provider side of
|
|
415
|
+
* an in-process `function-call` boundary.
|
|
161
416
|
*
|
|
162
|
-
*
|
|
417
|
+
* The bindings this produces have the identity
|
|
163
418
|
* `{ transport: "in-process",
|
|
164
419
|
* semantics: { name: "function-call",
|
|
165
420
|
* package: <pkg.name>,
|
|
166
421
|
* exportPath: [...] },
|
|
167
422
|
* recognition: <pack.name> }`.
|
|
168
423
|
*
|
|
169
|
-
*
|
|
170
|
-
* `@suss/behavioral-ir/schemas::BehavioralSummarySchema
|
|
171
|
-
* `exportPath = ["schemas", "BehavioralSummarySchema"]`.
|
|
172
|
-
*
|
|
424
|
+
* A sub-path export is identified as, for example,
|
|
425
|
+
* `@suss/behavioral-ir/schemas::BehavioralSummarySchema`, giving
|
|
426
|
+
* `exportPath = ["schemas", "BehavioralSummarySchema"]`. A root export
|
|
427
|
+
* leaves the sub-path segment out.
|
|
173
428
|
*
|
|
174
|
-
* v0
|
|
175
|
-
* on `exports`, falls back to `types
|
|
176
|
-
* when no `exports` field
|
|
177
|
-
*
|
|
429
|
+
* As of v0 this resolves the `types`, `default`, and `import`
|
|
430
|
+
* conditions on `exports`, and falls back to `types`, `main`, `module`
|
|
431
|
+
* when there is no `exports` field. Pattern exports (`./utils/*`) and
|
|
432
|
+
* `development` conditions are not handled yet.
|
|
178
433
|
*/
|
|
179
434
|
type: "packageExports";
|
|
180
435
|
/** Absolute path to the package's `package.json`. */
|
|
181
436
|
packageJsonPath: string;
|
|
182
437
|
/**
|
|
183
|
-
* Restrict to these `exports` keys (without leading `./`).
|
|
184
|
-
*
|
|
185
|
-
*
|
|
438
|
+
* Restrict to these `exports` keys (without the leading `./`). The
|
|
439
|
+
* root export is keyed `"."`. Leave it unset for every sub-path that
|
|
440
|
+
* resolves.
|
|
186
441
|
*/
|
|
187
442
|
subPaths?: string[];
|
|
188
443
|
/**
|
|
189
|
-
* Export names to skip
|
|
190
|
-
*
|
|
444
|
+
* Export names to skip, usually `["default"]` when a pack wants to
|
|
445
|
+
* treat default exports separately or ignore them.
|
|
191
446
|
*/
|
|
192
447
|
excludeNames?: string[];
|
|
193
448
|
} | {
|
|
194
449
|
/**
|
|
195
|
-
* Class methods
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
* object literal.
|
|
450
|
+
* Class methods with a particular decorator, on classes with a
|
|
451
|
+
* particular class-level decorator. NestJS-style frameworks work this
|
|
452
|
+
* way: resolvers, handlers, and controllers are declared by decorator
|
|
453
|
+
* rather than by registering a function in an object literal.
|
|
200
454
|
*
|
|
201
|
-
* Discovery
|
|
202
|
-
*
|
|
203
|
-
*
|
|
455
|
+
* Discovery only fires when `classDecorator` and each
|
|
456
|
+
* `methodDecorators` entry come from `importModule`, so a user-defined
|
|
457
|
+
* decorator that happens to share a name will not match.
|
|
204
458
|
*
|
|
205
459
|
* For a NestJS GraphQL pack:
|
|
206
460
|
* `{ importModule: "@nestjs/graphql",
|
|
@@ -208,75 +462,73 @@ type DiscoveryMatch = {
|
|
|
208
462
|
* methodDecorators: ["Query", "Mutation", "ResolveField",
|
|
209
463
|
* "Subscription"] }`
|
|
210
464
|
*
|
|
211
|
-
* The adapter
|
|
212
|
-
*
|
|
465
|
+
* The adapter fills in `DiscoveredUnit.resolverInfo` so the binding
|
|
466
|
+
* comes out as `graphql-resolver(typeName, fieldName)`.
|
|
213
467
|
* `typeName` resolves from the class decorator's first argument
|
|
214
|
-
* (`@Resolver(() => User)`
|
|
468
|
+
* (`@Resolver(() => User)` gives `"User"`), or from
|
|
215
469
|
* `methodDecoratorTypeMap` when the class decorator has no
|
|
216
|
-
* argument. `fieldName`
|
|
217
|
-
* option
|
|
470
|
+
* argument. `fieldName` comes from the method decorator's `{ name }`
|
|
471
|
+
* option when that is set, and otherwise from the method name.
|
|
218
472
|
*/
|
|
219
473
|
type: "decoratedMethod";
|
|
220
474
|
/**
|
|
221
|
-
* The module a
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
* modules and any one match suffices.
|
|
475
|
+
* The module a decorator has to be imported from before discovery will
|
|
476
|
+
* fire. Codebases sometimes re-export a framework decorator wrapped
|
|
477
|
+
* with extra metadata of their own, and checking one module would miss
|
|
478
|
+
* those. Pass an array of acceptable modules and any one of them
|
|
479
|
+
* matching is enough.
|
|
227
480
|
*/
|
|
228
481
|
importModule: string | string[];
|
|
229
482
|
/**
|
|
230
|
-
* Class
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
483
|
+
* Class decorators to recognise. The first one that appears on a class
|
|
484
|
+
* is the one typeName is read from; the rest are fallbacks for
|
|
485
|
+
* codebases with several wrapper styles. A pack ships only what its own
|
|
486
|
+
* framework declares here, and takes a project's own wrappers through
|
|
487
|
+
* its options instead.
|
|
235
488
|
*/
|
|
236
489
|
classDecorators: string[];
|
|
237
490
|
methodDecorators: string[];
|
|
238
491
|
/**
|
|
239
|
-
*
|
|
240
|
-
* class decorator
|
|
241
|
-
* `Query` type and `@Mutation` on `Mutation`
|
|
242
|
-
* says, so those two
|
|
492
|
+
* Maps a method decorator to the type its field belongs to when the
|
|
493
|
+
* class decorator does not say. NestJS puts `@Query` on the root
|
|
494
|
+
* `Query` type and `@Mutation` on `Mutation` no matter what the class
|
|
495
|
+
* says, so those two settle it themselves.
|
|
243
496
|
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
* binding then
|
|
247
|
-
* claiming a field the schema does not have.
|
|
497
|
+
* When the map leaves a decorator out and the class decorator gives no
|
|
498
|
+
* type either, nothing here works out which type owns the field. The
|
|
499
|
+
* binding then goes out with no type and pairs with nothing, instead
|
|
500
|
+
* of claiming a field the schema does not have.
|
|
248
501
|
*/
|
|
249
502
|
methodDecoratorTypeMap: Record<string, string>;
|
|
250
503
|
} | {
|
|
251
504
|
/**
|
|
252
|
-
* NestJS-style REST controller discovery
|
|
253
|
-
* `@Controller(pathPrefix?)
|
|
254
|
-
* `@Get(subpath?)
|
|
255
|
-
* decorator NAME determines the HTTP method
|
|
256
|
-
* `methodDecoratorRouteMap
|
|
257
|
-
*
|
|
258
|
-
*
|
|
505
|
+
* NestJS-style REST controller discovery: a class decorated with
|
|
506
|
+
* `@Controller(pathPrefix?)`, and methods decorated with
|
|
507
|
+
* `@Get(subpath?)`, `@Post`, `@Put`, `@Delete`, and so on. The
|
|
508
|
+
* decorator's NAME is what determines the HTTP method, through
|
|
509
|
+
* `methodDecoratorRouteMap`. The route path is the class decorator's
|
|
510
|
+
* first argument joined with a slash to the method decorator's first
|
|
511
|
+
* argument, and both of those are optional.
|
|
259
512
|
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
513
|
+
* Wrapper decorators are tolerated the same way as in
|
|
514
|
+
* `decoratedMethod`: at least one method-route decorator has to come
|
|
515
|
+
* from the framework module, but class decorators
|
|
516
|
+
* are matched by name alone, so a project's own wrapper around the
|
|
517
|
+
* framework's decorator matches once the project lists it in the
|
|
518
|
+
* pack's options.
|
|
266
519
|
*
|
|
267
|
-
* The adapter
|
|
268
|
-
*
|
|
520
|
+
* The adapter fills in `DiscoveredUnit.routeInfo` so the binding comes
|
|
521
|
+
* out as `rest(method, path)`.
|
|
269
522
|
*/
|
|
270
523
|
type: "decoratedRoute";
|
|
271
524
|
importModule: string | string[];
|
|
272
525
|
classDecorators: string[];
|
|
273
526
|
/**
|
|
274
|
-
*
|
|
275
|
-
* verb (`@Get
|
|
276
|
-
* `@
|
|
277
|
-
* the
|
|
278
|
-
*
|
|
279
|
-
* decorators.
|
|
527
|
+
* Maps a decorator to an HTTP method. NestJS uses one decorator per
|
|
528
|
+
* verb (`@Get`, `@Post`, `@Put`, `@Delete`, `@Patch`, `@Options`,
|
|
529
|
+
* `@Head`, `@All`), and other frameworks may do the same. The values
|
|
530
|
+
* become the `method` field on the REST binding, and `"*"` is fine for
|
|
531
|
+
* a catch-all decorator.
|
|
280
532
|
*/
|
|
281
533
|
methodDecoratorRouteMap: Record<string, string>;
|
|
282
534
|
} | {
|
|
@@ -291,15 +543,15 @@ type DiscoveryMatch = {
|
|
|
291
543
|
* ];
|
|
292
544
|
* for (const r of routes) app[r.method](r.path, r.handler);
|
|
293
545
|
*
|
|
294
|
-
* `elementShape` declares which keys on each element
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
* loop variable
|
|
298
|
-
*
|
|
546
|
+
* `elementShape` declares which keys on each element give the method,
|
|
547
|
+
* the path, and the handler. The loop body has to contain at least
|
|
548
|
+
* one call expression that references the
|
|
549
|
+
* loop variable, which filters out unrelated loops. Nothing else
|
|
550
|
+
* about that call is checked.
|
|
299
551
|
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
* computed iterables are
|
|
552
|
+
* An iterable that resolves to an `ArrayLiteralExpression`, inline or
|
|
553
|
+
* bound to a `const` one hop away, gets expanded. Cross-file and
|
|
554
|
+
* computed iterables are outside v0.
|
|
303
555
|
*
|
|
304
556
|
* Pack-author docs: `docs/internal/proposals/dynamic-registration.md`.
|
|
305
557
|
*/
|
|
@@ -311,11 +563,11 @@ type DiscoveryMatch = {
|
|
|
311
563
|
};
|
|
312
564
|
} | {
|
|
313
565
|
/**
|
|
314
|
-
* Helper-call expansion:
|
|
315
|
-
*
|
|
316
|
-
* arguments substituted into per
|
|
317
|
-
* for
|
|
318
|
-
*
|
|
566
|
+
* Helper-call expansion: one function call at the user's site is
|
|
567
|
+
* treated as if it were N inline registrations, with the call's
|
|
568
|
+
* arguments substituted into a template per registration. Used
|
|
569
|
+
* for calls like `registerCrud(app, 'users', userHandlers)` that
|
|
570
|
+
* `registrationCall` discovery cannot see today.
|
|
319
571
|
*
|
|
320
572
|
* Each entry in `registrations` describes one virtual route
|
|
321
573
|
* the helper produces. `pathTemplate` and `handlerArg` use
|
|
@@ -325,9 +577,9 @@ type DiscoveryMatch = {
|
|
|
325
577
|
* non-literal args, with the slot marked opaque). `{N}.prop`
|
|
326
578
|
* reads `prop` from the argument's resolved object.
|
|
327
579
|
*
|
|
328
|
-
* `importModule` optionally narrows matches to helpers
|
|
329
|
-
*
|
|
330
|
-
*
|
|
580
|
+
* `importModule` optionally narrows matches to helpers imported from
|
|
581
|
+
* one specific module, which helps when two packages happen to export
|
|
582
|
+
* a function with the same name.
|
|
331
583
|
*
|
|
332
584
|
* Pack-author docs: `docs/internal/proposals/dynamic-registration.md`.
|
|
333
585
|
*/
|
|
@@ -341,23 +593,94 @@ type DiscoveryMatch = {
|
|
|
341
593
|
}>;
|
|
342
594
|
} | {
|
|
343
595
|
/**
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
596
|
+
* Routes declared as JSX elements, the way client-side routers
|
|
597
|
+
* write them: an element imported from the router library whose
|
|
598
|
+
* attributes give a URL path pattern and the element it
|
|
599
|
+
* renders. Covers the tree form (route elements nested inside
|
|
600
|
+
* one another, child paths joining the parent's, index routes
|
|
601
|
+
* taking the parent's path) and the object-array form (a
|
|
602
|
+
* factory call whose first argument is an array of route
|
|
603
|
+
* objects using the same property names).
|
|
604
|
+
*
|
|
605
|
+
* The pack says what its library exports: the route element, the
|
|
606
|
+
* path, element, and index attributes, and any factories that
|
|
607
|
+
* take an array of route objects. The adapter walks JSX and arrays,
|
|
608
|
+
* and knows none of those names itself.
|
|
609
|
+
*
|
|
610
|
+
* Each route with a readable path becomes one unit whose target
|
|
611
|
+
* is the component the element attribute references, resolved
|
|
612
|
+
* only when the reference is a single identifier. A route whose
|
|
613
|
+
* component cannot be read is still reported, as a boundary with
|
|
614
|
+
* nothing behind it. A route whose path cannot be read gets no path
|
|
615
|
+
* and reports that in a gap instead of guessing at one.
|
|
616
|
+
*/
|
|
617
|
+
type: "jsxElementRoute";
|
|
618
|
+
/**
|
|
619
|
+
* Module(s) the route element and factories must be imported
|
|
620
|
+
* from. Exact module specifiers, matched against the file's
|
|
621
|
+
* import declarations; aliased imports are followed.
|
|
622
|
+
*/
|
|
623
|
+
importModule: string | string[];
|
|
624
|
+
/** The route element's exported name. */
|
|
625
|
+
routeElement: string;
|
|
626
|
+
/** The attribute with the route's path pattern on it. */
|
|
627
|
+
pathAttribute: string;
|
|
628
|
+
/** The attribute with the JSX the route renders on it. */
|
|
629
|
+
elementAttribute: string;
|
|
630
|
+
/**
|
|
631
|
+
* The attribute that marks an index route, which renders at its
|
|
632
|
+
* parent's path. Leave it unset if the library has no index routes.
|
|
633
|
+
*/
|
|
634
|
+
indexAttribute?: string;
|
|
635
|
+
/**
|
|
636
|
+
* The property with the routes nested under a route object on it,
|
|
637
|
+
* which is how the object form expresses what the JSX form expresses
|
|
638
|
+
* by nesting. Paths compose the same way in both. Leave it unset if
|
|
639
|
+
* the library's route objects do not nest.
|
|
640
|
+
*/
|
|
641
|
+
childrenAttribute?: string;
|
|
642
|
+
/**
|
|
643
|
+
* Factory functions whose first argument is an array of route
|
|
644
|
+
* objects keyed by the same three attribute names. The array is read
|
|
645
|
+
* where it is written, or through the same value resolution the rest
|
|
646
|
+
* of discovery uses, so a `const` binding one hop away works the
|
|
647
|
+
* same as an inline literal.
|
|
648
|
+
*/
|
|
649
|
+
routeObjectFactories?: string[];
|
|
650
|
+
/**
|
|
651
|
+
* Factory functions that turn JSX route elements into the route
|
|
652
|
+
* objects the library consumes. The elements themselves are read
|
|
653
|
+
* by the JSX walk wherever they appear, so a route-object factory
|
|
654
|
+
* handed one of these calls has nothing more to add. Listing them
|
|
655
|
+
* here is what stops that case from being reported as an
|
|
656
|
+
* unreadable route array.
|
|
657
|
+
*/
|
|
658
|
+
elementsFactories?: string[];
|
|
659
|
+
/**
|
|
660
|
+
* The HTTP method recorded on each route binding this produces. A page
|
|
661
|
+
* route serves navigations, and the pack has to say which method those
|
|
662
|
+
* use rather than the adapter assuming one.
|
|
663
|
+
*/
|
|
664
|
+
method: string;
|
|
665
|
+
} | {
|
|
666
|
+
/**
|
|
667
|
+
* Consumer side of the package-export boundary. Scans source files for
|
|
668
|
+
* imports of the listed packages and records every call site,
|
|
669
|
+
* emitting one `caller`-kind unit per enclosing
|
|
670
|
+
* function. The bindings this produces are
|
|
671
|
+
* `function-call { package, exportPath }`, which match the provider
|
|
672
|
+
* summaries `packageExports` produces.
|
|
350
673
|
*
|
|
351
|
-
* `packages`
|
|
352
|
-
* sub-path
|
|
353
|
-
*
|
|
674
|
+
* `packages` lists exact package names to track imports of, possibly
|
|
675
|
+
* with a sub-path such as `"@suss/behavioral-ir/schemas"`. Pass
|
|
676
|
+
* several package names to track a family at
|
|
354
677
|
* once. Imports of any other package are ignored.
|
|
355
678
|
*
|
|
356
|
-
* v0
|
|
357
|
-
* (`import * as X from`) are not yet
|
|
679
|
+
* As of v0 this covers named and default imports. Namespace imports
|
|
680
|
+
* (`import * as X from`) are not tracked yet. Re-imports within
|
|
358
681
|
* the consumer repo (consumer A imports from consumer B which
|
|
359
|
-
* re-exports from pkg) produce units against the intermediate
|
|
360
|
-
*
|
|
682
|
+
* re-exports from pkg) produce units against the intermediate rather
|
|
683
|
+
* than the original, because full symbol resolution is not built yet.
|
|
361
684
|
*/
|
|
362
685
|
type: "packageImport";
|
|
363
686
|
packages: string[];
|
|
@@ -366,6 +689,13 @@ type BindingExtraction = {
|
|
|
366
689
|
method: {
|
|
367
690
|
type: "fromRegistration";
|
|
368
691
|
position: "methodName" | number;
|
|
692
|
+
/**
|
|
693
|
+
* Registrations whose recorded method is something other than their
|
|
694
|
+
* own name uppercased, the way `.all` registers every method and is
|
|
695
|
+
* recorded as `"*"`. Anything missing from the map is recorded as
|
|
696
|
+
* its own name uppercased.
|
|
697
|
+
*/
|
|
698
|
+
nameMap?: Record<string, string>;
|
|
369
699
|
} | {
|
|
370
700
|
type: "fromExportName";
|
|
371
701
|
} | {
|
|
@@ -390,11 +720,11 @@ type BindingExtraction = {
|
|
|
390
720
|
property: string;
|
|
391
721
|
} | {
|
|
392
722
|
/**
|
|
393
|
-
* The route path comes from where the file
|
|
394
|
-
* Next.js and React Router describe their routes. The pack
|
|
395
|
-
*
|
|
396
|
-
* about files and the pack knows what the framework does with
|
|
397
|
-
*
|
|
723
|
+
* The route path comes from where the file is on disk, which is how
|
|
724
|
+
* Next.js and React Router describe their routes. The pack spells
|
|
725
|
+
* out its own convention here, because the adapter knows
|
|
726
|
+
* about files and the pack knows what the framework does with their
|
|
727
|
+
* names.
|
|
398
728
|
*
|
|
399
729
|
* `app/api/orders/[id]/route.ts` under `{ root: "app",
|
|
400
730
|
* dropBasenames: ["route"], dynamic: "brackets" }` comes out as
|
|
@@ -403,14 +733,14 @@ type BindingExtraction = {
|
|
|
403
733
|
*/
|
|
404
734
|
type: "fromFilename";
|
|
405
735
|
/**
|
|
406
|
-
* Where a route path starts. The directories below it become
|
|
407
|
-
*
|
|
408
|
-
* and
|
|
736
|
+
* Where a route path starts. The directories below it become the
|
|
737
|
+
* path, and everything above it belongs to the project's own layout
|
|
738
|
+
* and gets dropped.
|
|
409
739
|
*/
|
|
410
740
|
root: string;
|
|
411
741
|
/**
|
|
412
|
-
* Filenames that say what
|
|
413
|
-
* segment: `route`, `page`, `index`, `_index`.
|
|
742
|
+
* Filenames that say what kind of file it is rather than adding a
|
|
743
|
+
* path segment: `route`, `page`, `index`, `_index`.
|
|
414
744
|
*/
|
|
415
745
|
dropBasenames?: string[];
|
|
416
746
|
/**
|
|
@@ -424,8 +754,8 @@ type BindingExtraction = {
|
|
|
424
754
|
*/
|
|
425
755
|
dropParenthesized?: boolean;
|
|
426
756
|
/**
|
|
427
|
-
* Whether one filename
|
|
428
|
-
*
|
|
757
|
+
* Whether one filename contains the whole path with dots between the
|
|
758
|
+
* segments, the way `routes/orders.$id.tsx` does.
|
|
429
759
|
*/
|
|
430
760
|
flat?: boolean;
|
|
431
761
|
} | {
|
|
@@ -443,22 +773,48 @@ interface DiscoveryPattern {
|
|
|
443
773
|
match: DiscoveryMatch;
|
|
444
774
|
bindingExtraction?: BindingExtraction;
|
|
445
775
|
/**
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
*
|
|
776
|
+
* How the routable this pattern discovers (Express's `Router()`,
|
|
777
|
+
* Hono's `new Hono()`, and similar) can itself be mounted onto
|
|
778
|
+
* another one under a path prefix, as in Express's
|
|
779
|
+
* `app.use(prefix, router)` or Hono's `app.route(prefix, sub)`.
|
|
780
|
+
* This only means anything when `match.type` is `"registrationCall"`,
|
|
781
|
+
* because mount discovery reuses that match's `importModule` and
|
|
782
|
+
* `importName` to work out which variables in a file are the routable
|
|
783
|
+
* that a mount call is being made on.
|
|
454
784
|
*
|
|
455
|
-
*
|
|
456
|
-
*
|
|
457
|
-
*
|
|
785
|
+
* When set, the adapter composes the mount's prefix into the path
|
|
786
|
+
* of every route discovered on the mounted value, whether it is
|
|
787
|
+
* declared in the mounting file or, by following the mounted value
|
|
788
|
+
* through an import, in whichever file declares it. A mount whose
|
|
789
|
+
* prefix is not a string literal, or whose target the resolution store
|
|
790
|
+
* cannot follow to a concrete value, contributes nothing, and the routes
|
|
791
|
+
* under it keep the path they were written with.
|
|
792
|
+
*/
|
|
793
|
+
mount?: {
|
|
794
|
+
/** Method name that registers a sub-router at a prefix, e.g. "use" or "route". */
|
|
795
|
+
method: string;
|
|
796
|
+
/** Argument position of the prefix string. */
|
|
797
|
+
prefixPosition: number;
|
|
798
|
+
/** Argument position of the mounted router/sub-app value. */
|
|
799
|
+
targetPosition: number;
|
|
800
|
+
};
|
|
801
|
+
/**
|
|
802
|
+
* This pattern only runs against files that import one of these module
|
|
803
|
+
* specifiers, or a sub-path of one. An empty array means no gate at all
|
|
804
|
+
* (the pattern is dispatched against
|
|
805
|
+
* every file). Leaving it undefined does the same, but pack authors
|
|
806
|
+
* SHOULD write it out, because `[]` is the deliberate
|
|
807
|
+
* "match every file" choice, usually because the pattern keys on
|
|
808
|
+
* something other than imports. The fetch runtime does that, since it
|
|
809
|
+
* matches global `fetch(...)` calls.
|
|
458
810
|
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
811
|
+
* Matching is by prefix on the import module specifier. An entry of
|
|
812
|
+
* `"@nestjs/graphql"` matches `from "@nestjs/graphql"` and
|
|
813
|
+
* `from "@nestjs/graphql/dist/foo"` and any other sub-path.
|
|
814
|
+
*
|
|
815
|
+
* This pre-filter is only there for speed. The closure walk and the other
|
|
816
|
+
* post-passes can still reach every loaded file through symbol
|
|
817
|
+
* resolution.
|
|
462
818
|
*/
|
|
463
819
|
requiresImport?: string[];
|
|
464
820
|
}
|
|
@@ -471,8 +827,8 @@ type TerminalMatch = {
|
|
|
471
827
|
* Skip ReturnStatements whose returned expression is a CallExpression
|
|
472
828
|
* (or NewExpression). For frameworks where `return reply.send(...)`
|
|
473
829
|
* also lands as a `parameterMethodCall` match on the inner call,
|
|
474
|
-
* this
|
|
475
|
-
*
|
|
830
|
+
* this stops the same `return reply.send(...)` producing two
|
|
831
|
+
* terminals, one from the wrapping returnStatement and one from
|
|
476
832
|
* the inner method-call chain. Bare returns (`return user`,
|
|
477
833
|
* `return { id }`, `return await fn()`) still match.
|
|
478
834
|
*/
|
|
@@ -489,43 +845,43 @@ type TerminalMatch = {
|
|
|
489
845
|
functionName: string;
|
|
490
846
|
/**
|
|
491
847
|
* Only match when the name was imported from one of these modules.
|
|
492
|
-
*
|
|
493
|
-
*
|
|
848
|
+
* The field works exactly like a DiscoveryPattern's gate, matching by
|
|
849
|
+
* prefix: "react-router" also matches "react-router/server".
|
|
494
850
|
*
|
|
495
|
-
* Set it whenever the function belongs to a library, because
|
|
496
|
-
*
|
|
851
|
+
* Set it whenever the function belongs to a library, because matching
|
|
852
|
+
* on a bare name picks up every function with that name in the
|
|
497
853
|
* user's project too. `json` is a common name for a project's own
|
|
498
854
|
* response helper, and reading a library's argument order into one
|
|
499
|
-
* of those
|
|
855
|
+
* of those gives you a confident wrong answer.
|
|
500
856
|
*
|
|
501
|
-
* Leave it unset only when the function
|
|
502
|
-
* A pack should generally not
|
|
503
|
-
* the envelope
|
|
857
|
+
* Leave it unset only when the function belongs to no library at all.
|
|
858
|
+
* A pack should generally not target a project's own helper. Declare
|
|
859
|
+
* the envelope structure instead, with a `returnShape` terminal, and
|
|
504
860
|
* the adapter follows a returned call into the project and reads
|
|
505
|
-
* the helper's parameters. That covers
|
|
506
|
-
*
|
|
861
|
+
* the helper's parameters. That covers a helper whatever it is called
|
|
862
|
+
* and whatever order its arguments come in.
|
|
507
863
|
*/
|
|
508
864
|
requiresImport?: string[];
|
|
509
865
|
} | {
|
|
510
866
|
/**
|
|
511
|
-
*
|
|
512
|
-
* root element
|
|
513
|
-
*
|
|
514
|
-
* classify component
|
|
867
|
+
* A return statement whose value is a JSX element or fragment. The
|
|
868
|
+
* root element or component name is recorded in
|
|
869
|
+
* `RawTerminal.component`. React, and any other JSX-based framework
|
|
870
|
+
* pack, uses this to classify component output as a `render` terminal.
|
|
515
871
|
*/
|
|
516
872
|
type: "jsxReturn";
|
|
517
873
|
} | {
|
|
518
874
|
/**
|
|
519
|
-
*
|
|
520
|
-
* function body.
|
|
521
|
-
* `ReturnStatement`
|
|
522
|
-
* covers the common case of handler
|
|
523
|
-
* side
|
|
875
|
+
* A synthetic terminal for the implicit fall-through at the end of a
|
|
876
|
+
* function body. It fires when the function's last statement is
|
|
877
|
+
* neither a `ReturnStatement` nor a `ThrowStatement`, which
|
|
878
|
+
* covers the common case of handler and effect bodies that run
|
|
879
|
+
* side effects and return `undefined` implicitly. Without this,
|
|
524
880
|
* handler summaries come out with `transitions: []` because
|
|
525
|
-
* `findTerminals` has nothing to match.
|
|
526
|
-
* explicit returns (HTTP handlers)
|
|
527
|
-
*
|
|
528
|
-
* `useEffect` bodies, Node `.on(...)` callbacks) should.
|
|
881
|
+
* `findTerminals` has nothing to match. A pack that always expects
|
|
882
|
+
* explicit returns (HTTP handlers) should leave this out of
|
|
883
|
+
* its terminals. A pack for callback bodies (React handlers,
|
|
884
|
+
* `useEffect` bodies, Node `.on(...)` callbacks) should include it.
|
|
529
885
|
*/
|
|
530
886
|
type: "functionFallthrough";
|
|
531
887
|
};
|
|
@@ -568,15 +924,15 @@ interface TerminalPattern {
|
|
|
568
924
|
extraction: TerminalExtraction;
|
|
569
925
|
}
|
|
570
926
|
interface ContractPattern {
|
|
571
|
-
/** How to find the contract object.
|
|
572
|
-
* so this
|
|
927
|
+
/** How to find the contract object. A contract is a data structure rather
|
|
928
|
+
* than a code unit, so this needs less than a DiscoveryPattern does. */
|
|
573
929
|
discovery: {
|
|
574
930
|
importModule: string;
|
|
575
931
|
importName: string;
|
|
576
932
|
registrationChain: string[];
|
|
577
933
|
};
|
|
578
934
|
responseExtraction: {
|
|
579
|
-
/**
|
|
935
|
+
/** The property on the contract object with the responses map on it */
|
|
580
936
|
property: string;
|
|
581
937
|
};
|
|
582
938
|
paramsExtraction?: {
|
|
@@ -606,14 +962,14 @@ type InputMappingPattern = {
|
|
|
606
962
|
* Component props, React / Vue / Svelte-style: one parameter that
|
|
607
963
|
* the caller destructures at will, with prop names only visible at
|
|
608
964
|
* the call site. When the parameter is destructured, each bound
|
|
609
|
-
* name becomes its own Input with
|
|
610
|
-
*
|
|
611
|
-
*
|
|
965
|
+
* name becomes its own Input with the name as its role. When it is
|
|
966
|
+
* not destructured (`function X(props) {...}`), a single Input comes
|
|
967
|
+
* out with `wholeParamRole`, which defaults to `"props"`.
|
|
612
968
|
*
|
|
613
|
-
*
|
|
614
|
-
*
|
|
615
|
-
* author wrote.
|
|
616
|
-
* destructuring pattern is
|
|
969
|
+
* This differs from `destructuredObject` because the pack does not
|
|
970
|
+
* declare the prop names up front; they are whatever the component
|
|
971
|
+
* author wrote. It differs from `singleObjectParam` because the
|
|
972
|
+
* destructuring pattern is respected when there is one.
|
|
617
973
|
*/
|
|
618
974
|
type: "componentProps";
|
|
619
975
|
paramPosition: number;
|
|
@@ -621,41 +977,42 @@ type InputMappingPattern = {
|
|
|
621
977
|
wholeParamRole?: string;
|
|
622
978
|
} | {
|
|
623
979
|
/**
|
|
624
|
-
* Emit one `Input` per declared parameter, in source order,
|
|
625
|
-
*
|
|
626
|
-
* reachable-closure pass for internal library functions
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
-
* same way `destructuredObject`
|
|
630
|
-
*
|
|
980
|
+
* Emit one `Input` per declared parameter, in source order, using the
|
|
981
|
+
* parameter's name as its role, or `defaultRole` when set. Used by the
|
|
982
|
+
* reachable-closure pass for internal library functions, where no
|
|
983
|
+
* framework declares a set of roles, so the name a caller sees IS
|
|
984
|
+
* the role. Destructured parameters are captured the
|
|
985
|
+
* same way `destructuredObject` captures them, so `(ctx, { userId })`
|
|
986
|
+
* gives two inputs, `ctx` and `userId`.
|
|
631
987
|
*/
|
|
632
988
|
type: "allPositional";
|
|
633
989
|
defaultRole?: string;
|
|
634
990
|
} | {
|
|
635
991
|
/**
|
|
636
|
-
* Decorator-driven parameter mapping
|
|
637
|
-
*
|
|
638
|
-
*
|
|
639
|
-
*
|
|
640
|
-
*
|
|
992
|
+
* Decorator-driven parameter mapping, NestJS-style. For each declared
|
|
993
|
+
* parameter, the adapter reads the parameter's first decorator and
|
|
994
|
+
* looks its name up in `decoratorRoleMap`.
|
|
995
|
+
* A decorator that matches gives the parameter that role. One that
|
|
996
|
+
* matches nothing falls back to `defaultRole`, or is skipped when
|
|
997
|
+
* `defaultRole` is unset.
|
|
641
998
|
*
|
|
642
999
|
* For `@nestjs/graphql` resolvers:
|
|
643
1000
|
* `{ "Args": "args", "Parent": "parent",
|
|
644
1001
|
* "Context": "context", "Info": "info" }`.
|
|
645
1002
|
*
|
|
646
|
-
*
|
|
647
|
-
*
|
|
648
|
-
*
|
|
649
|
-
*
|
|
1003
|
+
* Decorators are matched by name alone, so if several frameworks
|
|
1004
|
+
* define `@Args`, all of them map. Packs that need to
|
|
1005
|
+
* tell them apart by import module can add that later, once there
|
|
1006
|
+
* is a use case worth the cost.
|
|
650
1007
|
*/
|
|
651
1008
|
type: "decoratedParams";
|
|
652
1009
|
decoratorRoleMap: Record<string, string>;
|
|
653
1010
|
defaultRole?: string;
|
|
654
1011
|
};
|
|
655
1012
|
/**
|
|
656
|
-
* What a property on the API response object
|
|
657
|
-
*
|
|
658
|
-
*
|
|
1013
|
+
* What a property on the API response object means. The pack declares this so
|
|
1014
|
+
* the adapter can work out a derived property at extraction time, the way
|
|
1015
|
+
* `.ok` means a status somewhere in 200 to 299.
|
|
659
1016
|
*/
|
|
660
1017
|
type ResponsePropertyMeaning = {
|
|
661
1018
|
type: "statusCode";
|
|
@@ -673,19 +1030,19 @@ interface ResponsePropertyMapping {
|
|
|
673
1030
|
name: string;
|
|
674
1031
|
/** How this member is accessed: property read or method call */
|
|
675
1032
|
access: "property" | "method";
|
|
676
|
-
/** What the value
|
|
1033
|
+
/** What the value means */
|
|
677
1034
|
semantics: ResponsePropertyMeaning;
|
|
678
1035
|
}
|
|
679
1036
|
/**
|
|
680
|
-
* A library wrapper
|
|
681
|
-
* adapter works this out on its own
|
|
682
|
-
*
|
|
683
|
-
* so
|
|
1037
|
+
* A library wrapper that returns the function it was handed. For a factory
|
|
1038
|
+
* declared inside the project, the adapter works this out on its own by
|
|
1039
|
+
* reading the body. A library's body is not there to read, so the pack has to
|
|
1040
|
+
* say so.
|
|
684
1041
|
*/
|
|
685
1042
|
interface TransparentWrapper {
|
|
686
1043
|
/** Callee text as written, e.g. "Sentry.wrapHandler". */
|
|
687
1044
|
callee: string;
|
|
688
|
-
/** Which argument
|
|
1045
|
+
/** Which argument the wrapped function is passed as. */
|
|
689
1046
|
argument: number;
|
|
690
1047
|
/**
|
|
691
1048
|
* The module the callee has to have been imported from. Without it a
|
|
@@ -705,9 +1062,9 @@ interface PatternPack {
|
|
|
705
1062
|
* the pack does. The CLI folds a hash of the file it loaded and of
|
|
706
1063
|
* the config it passed into this stamp, so a pack run through the CLI
|
|
707
1064
|
* invalidates on an edit whether or not it declares a version. A host
|
|
708
|
-
* that builds packs some other way
|
|
709
|
-
*
|
|
710
|
-
*
|
|
1065
|
+
* that builds packs some other way takes on that responsibility itself.
|
|
1066
|
+
* A pack with nothing to stamp comes out as `"unset"`, and a warm cache
|
|
1067
|
+
* will then serve results for code that has since changed.
|
|
711
1068
|
*/
|
|
712
1069
|
version?: string;
|
|
713
1070
|
languages: string[];
|
|
@@ -717,29 +1074,29 @@ interface PatternPack {
|
|
|
717
1074
|
inputMapping: InputMappingPattern;
|
|
718
1075
|
/**
|
|
719
1076
|
* Transport (wire protocol) used in the `BoundaryBinding.transport`
|
|
720
|
-
* of discovered units. Every pack
|
|
721
|
-
* rather than
|
|
1077
|
+
* of discovered units. Every pack has to say what its transport is
|
|
1078
|
+
* rather than falling back on a hardcoded HTTP default. "What transport
|
|
722
1079
|
* does this pack cover?" is a question every pack should have to
|
|
723
|
-
* answer, and
|
|
724
|
-
*
|
|
725
|
-
*
|
|
1080
|
+
* answer, and requiring the field stops a later pack (React, GraphQL,
|
|
1081
|
+
* Lambda-invoke, queues) from quietly inheriting an HTTP-shaped default
|
|
1082
|
+
* that does not fit it.
|
|
726
1083
|
*
|
|
727
|
-
* The pack's `name` separately
|
|
728
|
-
* on
|
|
729
|
-
* the pack directly
|
|
730
|
-
*
|
|
1084
|
+
* The pack's `name` separately fills in `BoundaryBinding.recognition`
|
|
1085
|
+
* on the summaries, so `{ transport, recognition }` come from
|
|
1086
|
+
* the pack directly and the adapter derives `semantics` from the
|
|
1087
|
+
* discovery pattern's binding-extraction rules.
|
|
731
1088
|
*/
|
|
732
1089
|
protocol: string;
|
|
733
1090
|
/**
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
* `.json()`
|
|
1091
|
+
* What the properties on the API response object mean, consumer side.
|
|
1092
|
+
* This tells the adapter how to turn a derived property like `.ok` or
|
|
1093
|
+
* `.json()` into a structured IR construct instead of leaving it opaque.
|
|
737
1094
|
*/
|
|
738
1095
|
responseSemantics?: ResponsePropertyMapping[];
|
|
739
1096
|
/**
|
|
740
|
-
* Synthesize
|
|
741
|
-
*
|
|
742
|
-
*
|
|
1097
|
+
* Synthesize extra code units out of a parent unit's body, for when one
|
|
1098
|
+
* construct the user wrote implicitly spawns several units the runtime
|
|
1099
|
+
* schedules. Used when a framework's runtime
|
|
743
1100
|
* schedules callbacks that aren't visible as top-level declarations:
|
|
744
1101
|
* React event handlers on JSX elements, React `useEffect` bodies,
|
|
745
1102
|
* Node `emitter.on("event", handler)`, class-component lifecycle
|
|
@@ -747,53 +1104,53 @@ interface PatternPack {
|
|
|
747
1104
|
*
|
|
748
1105
|
* `ctx` is typed `unknown` here because the extractor has no
|
|
749
1106
|
* knowledge of which adapter is driving it; each language adapter
|
|
750
|
-
* defines its own context
|
|
751
|
-
* `@suss/adapter-typescript
|
|
1107
|
+
* defines its own context type (`TsSubUnitContext` in
|
|
1108
|
+
* `@suss/adapter-typescript`, say) with the primitives a pack needs to
|
|
752
1109
|
* walk the parent's AST. Packs import and cast to the adapter
|
|
753
|
-
* context they
|
|
754
|
-
*
|
|
1110
|
+
* context they were written against, and that cast is how a pack says
|
|
1111
|
+
* out loud that it requires the TypeScript adapter.
|
|
755
1112
|
*
|
|
756
1113
|
* Returned units are fed through the adapter's extraction pipeline
|
|
757
1114
|
* the same way top-level discovered units are, so each becomes its
|
|
758
|
-
* own `BehavioralSummary`.
|
|
759
|
-
*
|
|
760
|
-
*
|
|
1115
|
+
* own `BehavioralSummary`. Put per-unit `terminals` and `inputMapping`
|
|
1116
|
+
* on the `DiscoveredUnit` when a sub-unit is written differently from
|
|
1117
|
+
* the parent pack's defaults.
|
|
761
1118
|
*/
|
|
762
1119
|
subUnits?: (parent: DiscoveredSubUnitParent, ctx: unknown) => DiscoveredSubUnit[];
|
|
763
1120
|
/**
|
|
764
|
-
*
|
|
765
|
-
*
|
|
766
|
-
*
|
|
1121
|
+
* A top-level discovery callback the pack supplies. It is to discovery
|
|
1122
|
+
* what `subUnits` is to sub-units: when a framework's convention does
|
|
1123
|
+
* not fit one of the data-driven `DiscoveryMatch` variants (REST
|
|
767
1124
|
* registration, decorator-based controllers, named-export shapes,
|
|
768
1125
|
* etc.), the pack ships its own walker here. The adapter calls it
|
|
769
1126
|
* once per source file alongside the data-driven dispatch.
|
|
770
1127
|
*
|
|
771
|
-
* Use this for framework-specific
|
|
772
|
-
* React's component-export heuristic (PascalCase
|
|
1128
|
+
* Use this for framework-specific patterns that do not generalize:
|
|
1129
|
+
* React's component-export heuristic (PascalCase plus a JSX return),
|
|
773
1130
|
* Vue's `.vue` SFC slots, Solid's component conventions, Storybook's
|
|
774
|
-
* `.stories.tsx` file convention.
|
|
775
|
-
* baking each into the central `DiscoveryMatch` union forces
|
|
776
|
-
* unrelated pack to know about them. Callbacks
|
|
777
|
-
* union for the generic primitives and let
|
|
1131
|
+
* `.stories.tsx` file convention. Those are all legitimate conventions,
|
|
1132
|
+
* but baking each one into the central `DiscoveryMatch` union forces
|
|
1133
|
+
* every unrelated pack to know about them. Callbacks leave the central
|
|
1134
|
+
* union for the generic primitives and let each pack own its own
|
|
778
1135
|
* conventions.
|
|
779
1136
|
*
|
|
780
|
-
* `ctx` is typed `unknown` for the same reason as `subUnits
|
|
1137
|
+
* `ctx` is typed `unknown` for the same reason as in `subUnits`: each
|
|
781
1138
|
* adapter ships its own context primitive (`TsDiscoveryContext` in
|
|
782
|
-
* `@suss/adapter-typescript`) and
|
|
783
|
-
* written against.
|
|
784
|
-
*
|
|
1139
|
+
* `@suss/adapter-typescript`) and a pack casts to whichever one it was
|
|
1140
|
+
* written against. That cast is how the pack says it requires the TS
|
|
1141
|
+
* adapter.
|
|
785
1142
|
*
|
|
786
|
-
*
|
|
787
|
-
* get terminals
|
|
1143
|
+
* The units you return go through the adapter's normal pipeline. They
|
|
1144
|
+
* get their terminals and effects extracted, sub-units synthesized, and
|
|
788
1145
|
* summaries assembled exactly as units from data-driven discovery
|
|
789
|
-
* do. Per-unit `terminals`
|
|
790
|
-
* `DiscoveredUnit` work the same way.
|
|
1146
|
+
* do. Per-unit `terminals` and `inputMapping` overrides on
|
|
1147
|
+
* `DiscoveredUnit` work the same way too.
|
|
791
1148
|
*
|
|
792
1149
|
* **Cross-pack dedup.** When this callback discovers a unit at the
|
|
793
1150
|
* same `(func, kind)` as a unit from another pack's data-driven
|
|
794
|
-
* discovery, the cross-pack claim dedup
|
|
795
|
-
* first
|
|
796
|
-
* precedence
|
|
1151
|
+
* discovery, the adapter's cross-pack claim dedup keeps whichever
|
|
1152
|
+
* claimed it first. The order packs appear in the framework list is
|
|
1153
|
+
* what decides precedence.
|
|
797
1154
|
*/
|
|
798
1155
|
discoverUnits?: (sourceFile: unknown, ctx: unknown) => DiscoveredCustomUnit[];
|
|
799
1156
|
/**
|
|
@@ -804,77 +1161,77 @@ interface PatternPack {
|
|
|
804
1161
|
* the function body and dispatches to every registered recognizer
|
|
805
1162
|
* for each call. Walking skips nested function bodies (those are
|
|
806
1163
|
* their own units with their own recognizer dispatch). The walk is
|
|
807
|
-
* INDEPENDENT of the existing invocation-effect walker
|
|
808
|
-
*
|
|
809
|
-
* `invocation` effects from bare expression statements
|
|
1164
|
+
* INDEPENDENT of the existing invocation-effect walker, which is
|
|
1165
|
+
* deliberately narrow (it only captures
|
|
1166
|
+
* `invocation` effects from bare expression statements and container
|
|
810
1167
|
* composition, to avoid double-counting calls that already become
|
|
811
|
-
* terminals). Recognizers
|
|
1168
|
+
* terminals). Recognizers do not have that problem, so they fire on
|
|
812
1169
|
* every call regardless of position, including
|
|
813
1170
|
* `const x = await fn(...)` initializers and nested call args
|
|
814
1171
|
* (which the invocation walker skips). This independence means
|
|
815
1172
|
* recognizer authors can rely on seeing every call in scope.
|
|
816
1173
|
*
|
|
817
|
-
* **Cross-pack visibility.** Recognizers fire regardless of which
|
|
818
|
-
*
|
|
1174
|
+
* **Cross-pack visibility.** Recognizers fire regardless of which pack
|
|
1175
|
+
* discovered the enclosing function, so
|
|
819
1176
|
* `@suss/framework-prisma`'s recognizer can fire on Prisma calls
|
|
820
1177
|
* inside an `@suss/framework-express` handler. Pack authors don't
|
|
821
1178
|
* need to coordinate.
|
|
822
1179
|
*
|
|
823
|
-
* **Emission contract.** Returning effects ADDS them to the
|
|
824
|
-
*
|
|
825
|
-
*
|
|
826
|
-
* call capture so inspect
|
|
827
|
-
* the checker pairs on the typed
|
|
828
|
-
*
|
|
1180
|
+
* **Emission contract.** Returning effects ADDS them to the enclosing
|
|
1181
|
+
* default-branch transition, and the generic `invocation` effect is
|
|
1182
|
+
* kept either way (typed effects live alongside the raw
|
|
1183
|
+
* call capture, so inspect can still render the callee text and
|
|
1184
|
+
* arguments while the checker pairs on the typed form). Return `null`
|
|
1185
|
+
* or `[]` for no match.
|
|
829
1186
|
*
|
|
830
|
-
* **Dedup is the recognizer's responsibility.** The dispatcher
|
|
831
|
-
*
|
|
832
|
-
* once per identifier
|
|
833
|
-
*
|
|
1187
|
+
* **Dedup is the recognizer's responsibility.** The dispatcher does
|
|
1188
|
+
* not dedupe across calls. A recognizer that wants to fire
|
|
1189
|
+
* once per identifier, to collapse reads bound to a const used N
|
|
1190
|
+
* times, has to track that state itself across invocations.
|
|
834
1191
|
*
|
|
835
|
-
* **Exceptions are caught and logged.** A recognizer that throws
|
|
836
|
-
*
|
|
837
|
-
* for that call
|
|
838
|
-
*
|
|
1192
|
+
* **Exceptions are caught and logged.** A recognizer that throws gets
|
|
1193
|
+
* logged to stderr with the file path and line number, and is skipped
|
|
1194
|
+
* for that one call while the extraction carries on. A buggy
|
|
1195
|
+
* recognizer will not crash the run.
|
|
839
1196
|
*
|
|
840
1197
|
* `call` is the language adapter's call-expression handle (opaque
|
|
841
1198
|
* here; ts-morph `CallExpression` in `@suss/adapter-typescript`).
|
|
842
1199
|
* `ctx` is the adapter's recognizer context (source file, an
|
|
843
1200
|
* `extractArgs()` helper that reuses the adapter's own EffectArg
|
|
844
|
-
* builder).
|
|
845
|
-
* written against
|
|
846
|
-
*
|
|
1201
|
+
* builder). A recognizer casts both to the adapter context it was
|
|
1202
|
+
* written against, which is the same way `subUnits` says a pack
|
|
1203
|
+
* requires the TypeScript adapter.
|
|
847
1204
|
*/
|
|
848
1205
|
invocationRecognizers?: InvocationRecognizer[];
|
|
849
1206
|
/**
|
|
850
1207
|
* Optional pack-level import gate. When set, the adapter's
|
|
851
1208
|
* pre-filter only considers this pack applicable to source files
|
|
852
1209
|
* whose imports include at least one of the listed modules
|
|
853
|
-
* (prefix
|
|
854
|
-
*
|
|
1210
|
+
* (matched by prefix, so `"@aws-sdk/client-sqs"` matches that module
|
|
1211
|
+
* and any `"@aws-sdk/client-sqs/sub-path"`).
|
|
855
1212
|
*
|
|
856
1213
|
* Useful for recognizer-only packs that target a specific library:
|
|
857
1214
|
* `@suss/framework-aws-sqs` declares `["@aws-sdk/client-sqs"]`,
|
|
858
1215
|
* `@suss/framework-prisma` declares `["@prisma/client"]`. Without
|
|
859
|
-
* a gate, recognizer-only
|
|
860
|
-
* correct but
|
|
861
|
-
*
|
|
1216
|
+
* a gate, a recognizer-only pack walks every file in the project. That
|
|
1217
|
+
* is correct but wasteful in a large monorepo where most files never
|
|
1218
|
+
* import the library.
|
|
862
1219
|
*
|
|
863
|
-
*
|
|
864
|
-
* on `DiscoveryPattern
|
|
865
|
-
*
|
|
1220
|
+
* A discovery-pattern pack already has a per-pattern `requiresImport`
|
|
1221
|
+
* on `DiscoveryPattern`. This is the pack-level version of that, for a
|
|
1222
|
+
* pack whose ONLY mechanism is recognizers and which has no discovery.
|
|
866
1223
|
*
|
|
867
|
-
* Empty
|
|
868
|
-
* default for
|
|
869
|
-
* process-surface
|
|
870
|
-
* available without
|
|
1224
|
+
* Empty or undefined means no gate, so the pack walks every file (the
|
|
1225
|
+
* default for universal recognizers like `@suss/runtime-node`'s
|
|
1226
|
+
* process-surface and env-var recognizers, since `process.*` is
|
|
1227
|
+
* available without importing anything).
|
|
871
1228
|
*/
|
|
872
1229
|
requiresImport?: string[];
|
|
873
1230
|
/**
|
|
874
|
-
* Library wrappers
|
|
875
|
-
*
|
|
876
|
-
*
|
|
877
|
-
* pack
|
|
1231
|
+
* Library wrappers that return the function they wrapped. The adapter
|
|
1232
|
+
* works this out on its own for a factory inside the project by reading
|
|
1233
|
+
* its body. A library wrapper's body is not there to read, so the
|
|
1234
|
+
* pack has to say it: a call to `callee` resolves to its
|
|
878
1235
|
* `argument`-th argument.
|
|
879
1236
|
*
|
|
880
1237
|
* `callee` matches the call expression text as written, e.g.
|
|
@@ -882,21 +1239,21 @@ interface PatternPack {
|
|
|
882
1239
|
*/
|
|
883
1240
|
transparentWrappers?: TransparentWrapper[];
|
|
884
1241
|
/**
|
|
885
|
-
* Per-property-access recognizers
|
|
886
|
-
* `invocationRecognizers
|
|
1242
|
+
* Per-property-access recognizers, the counterpart to
|
|
1243
|
+
* `invocationRecognizers`, firing on `PropertyAccessExpression`
|
|
887
1244
|
* nodes rather than `CallExpression` nodes. Use these for patterns
|
|
888
1245
|
* that read a value through property access without invoking it:
|
|
889
1246
|
* `process.env.X` env-var reads, `Date.now()`-style time reads
|
|
890
1247
|
* (which is actually a call, see invocationRecognizers), bare
|
|
891
1248
|
* `module.constant` reads, etc.
|
|
892
1249
|
*
|
|
893
|
-
*
|
|
894
|
-
* PropertyAccessExpression in the function body
|
|
895
|
-
* function bodies.
|
|
1250
|
+
* The scope rules are the same as for invocationRecognizers: it fires
|
|
1251
|
+
* on every PropertyAccessExpression in the function body and skips
|
|
1252
|
+
* nested function bodies. The emission contract is the same, so effects
|
|
896
1253
|
* on the enclosing default-branch transition.
|
|
897
1254
|
*
|
|
898
|
-
*
|
|
899
|
-
*
|
|
1255
|
+
* The arguments are opaque here and narrowed by the adapter, for the
|
|
1256
|
+
* same reason as in invocationRecognizers.
|
|
900
1257
|
*/
|
|
901
1258
|
accessRecognizers?: AccessRecognizer[];
|
|
902
1259
|
}
|
|
@@ -911,12 +1268,12 @@ type InvocationRecognizer<TCtx = unknown> = (call: unknown, ctx: TCtx) => Effect
|
|
|
911
1268
|
*/
|
|
912
1269
|
type AccessRecognizer<TCtx = unknown> = (access: unknown, ctx: TCtx) => Effect[] | null;
|
|
913
1270
|
/**
|
|
914
|
-
*
|
|
915
|
-
*
|
|
916
|
-
* language adapter brands its own FunctionRoot type. This interface
|
|
917
|
-
*
|
|
918
|
-
*
|
|
919
|
-
*
|
|
1271
|
+
* The bare minimum a `subUnits` hook needs to know about the parent code
|
|
1272
|
+
* unit it is working inside. `func` is left opaque here because each
|
|
1273
|
+
* language adapter brands its own FunctionRoot type. This interface lives in
|
|
1274
|
+
* the extractor only so `PatternPack` can refer to it, and the adapter-level
|
|
1275
|
+
* context types like `TsSubUnitContext` narrow `func` to a concrete AST
|
|
1276
|
+
* handle.
|
|
920
1277
|
*/
|
|
921
1278
|
interface DiscoveredSubUnitParent {
|
|
922
1279
|
/** Handle to the parent's function body. Opaque at extractor level. */
|
|
@@ -927,17 +1284,15 @@ interface DiscoveredSubUnitParent {
|
|
|
927
1284
|
kind: string;
|
|
928
1285
|
}
|
|
929
1286
|
/**
|
|
930
|
-
* What a pack's `discoverUnits` hook returns
|
|
931
|
-
*
|
|
932
|
-
* adapter widens these into its own internal `DiscoveredUnit` type
|
|
933
|
-
*
|
|
934
|
-
*
|
|
935
|
-
* extraction pipeline.
|
|
1287
|
+
* What a pack's `discoverUnits` hook returns for each top-level unit it
|
|
1288
|
+
* finds. It is to discovery what `DiscoveredSubUnit` is to sub-units. The
|
|
1289
|
+
* adapter widens these into its own internal `DiscoveredUnit` type, which has
|
|
1290
|
+
* adapter-specific fields like `routeInfo` and `packageExportInfo` on it, and
|
|
1291
|
+
* then runs them through the normal extraction pipeline.
|
|
936
1292
|
*
|
|
937
|
-
* Pack authors
|
|
938
|
-
*
|
|
939
|
-
* function-root type (
|
|
940
|
-
* `@suss/adapter-typescript`).
|
|
1293
|
+
* Pack authors only ever see opaque handles: `func` is whatever the adapter's
|
|
1294
|
+
* primitive returned, and the adapter narrows it to its concrete
|
|
1295
|
+
* function-root type (`FunctionRoot` in `@suss/adapter-typescript`).
|
|
941
1296
|
*/
|
|
942
1297
|
interface DiscoveredCustomUnit {
|
|
943
1298
|
/** Function body handle, opaque here. */
|
|
@@ -959,15 +1314,15 @@ interface DiscoveredCustomUnit {
|
|
|
959
1314
|
/**
|
|
960
1315
|
* REST route identity for units a callback discovers against an
|
|
961
1316
|
* external manifest (a SAM/CFN template's `Events` block, an infra
|
|
962
|
-
* routing declaration,
|
|
963
|
-
* When set, the adapter builds a `rest` binding from `(method, path)
|
|
964
|
-
*
|
|
965
|
-
* `routeInfo`,
|
|
966
|
-
*
|
|
1317
|
+
* routing declaration, and so on) rather than an in-code registration.
|
|
1318
|
+
* When set, the adapter builds a `rest` binding from `(method, path)`,
|
|
1319
|
+
* the same binding a NestJS controller gets from decorator-derived
|
|
1320
|
+
* `routeInfo`, and the discoverUnits callback never has to reach into
|
|
1321
|
+
* the adapter's binding machinery.
|
|
967
1322
|
*
|
|
968
1323
|
* One function bound to several routes emits one DiscoveredCustomUnit
|
|
969
|
-
* per route
|
|
970
|
-
* `(func, kind, method, path)
|
|
1324
|
+
* per route. The adapter's per-file claim dedup keys on
|
|
1325
|
+
* `(func, kind, method, path)`, so all of those variants survive.
|
|
971
1326
|
*/
|
|
972
1327
|
routeInfo?: {
|
|
973
1328
|
method: string;
|
|
@@ -986,9 +1341,9 @@ interface DiscoveredCustomUnit {
|
|
|
986
1341
|
fieldName: string;
|
|
987
1342
|
};
|
|
988
1343
|
/**
|
|
989
|
-
* Message-bus channel identity for consumer units a callback
|
|
990
|
-
*
|
|
991
|
-
*
|
|
1344
|
+
* Message-bus channel identity for consumer units a callback discovers
|
|
1345
|
+
* against a subject the code itself gives (a handler factory whose
|
|
1346
|
+
* config states the subject it expects). When set, the
|
|
992
1347
|
* adapter builds a `message-bus` binding from `(messageBus, channel)`,
|
|
993
1348
|
* which pairs with producers sending on the same channel.
|
|
994
1349
|
*/
|
|
@@ -1016,13 +1371,13 @@ interface DiscoveredSubUnit {
|
|
|
1016
1371
|
/** Qualified name (e.g. "Counter.button.onClick"). */
|
|
1017
1372
|
name: string;
|
|
1018
1373
|
/**
|
|
1019
|
-
* Terminal patterns to extract from this sub-unit's body.
|
|
1020
|
-
* to `return`
|
|
1374
|
+
* Terminal patterns to extract from this sub-unit's body. When unset it
|
|
1375
|
+
* defaults to `return` and `throw`, which suits handlers and effects.
|
|
1021
1376
|
*/
|
|
1022
1377
|
terminals?: TerminalPattern[];
|
|
1023
1378
|
/**
|
|
1024
|
-
* Input mapping for this sub-unit.
|
|
1025
|
-
* mapping
|
|
1379
|
+
* Input mapping for this sub-unit. When unset it defaults to an empty
|
|
1380
|
+
* positional mapping, so an event handler with one argument should pass
|
|
1026
1381
|
* `{ type: "positionalParams", params: [{ position: 0, role: "event" }] }`.
|
|
1027
1382
|
*/
|
|
1028
1383
|
inputMapping?: InputMappingPattern;
|
|
@@ -1034,20 +1389,29 @@ interface DiscoveredSubUnit {
|
|
|
1034
1389
|
}
|
|
1035
1390
|
|
|
1036
1391
|
/**
|
|
1037
|
-
*
|
|
1038
|
-
*
|
|
1039
|
-
*
|
|
1392
|
+
* Small helpers for pattern packs that are built the same way.
|
|
1393
|
+
*
|
|
1394
|
+
* The pack interface is deliberately declarative: a PatternPack is a data
|
|
1395
|
+
* object the adapter interprets, and most differences between frameworks are
|
|
1396
|
+
* best expressed that way. A few patterns, though, repeat word for word
|
|
1397
|
+
* across packs, and this module collects those so they are written once.
|
|
1398
|
+
*/
|
|
1399
|
+
|
|
1400
|
+
/**
|
|
1401
|
+
* Build the `discovery` entries for an HTTP-server framework whose handlers
|
|
1402
|
+
* are registered with `app.get(path, handler)`, `router.post(...)`, and the
|
|
1403
|
+
* like.
|
|
1040
1404
|
*
|
|
1041
|
-
* Each `importNames` entry produces one DiscoveryPattern
|
|
1042
|
-
*
|
|
1043
|
-
*
|
|
1044
|
-
* `Router()
|
|
1045
|
-
* extraction
|
|
1046
|
-
*
|
|
1405
|
+
* Each `importNames` entry produces one DiscoveryPattern, because a library
|
|
1406
|
+
* usually exposes both a default export and a named export that each produce
|
|
1407
|
+
* the routable instance (Express has `express()` and
|
|
1408
|
+
* `Router()`, Fastify has `fastify()` and `Fastify`). The binding
|
|
1409
|
+
* extraction, method from the registration and path from position 0, is
|
|
1410
|
+
* the same for every HTTP server framework we support.
|
|
1047
1411
|
*
|
|
1048
|
-
* Callers still
|
|
1049
|
-
*
|
|
1050
|
-
*
|
|
1412
|
+
* Callers still pass the `methods` list themselves, because frameworks
|
|
1413
|
+
* support different HTTP verbs. Fastify includes `.head` and `.options`;
|
|
1414
|
+
* Express historically does not by default.
|
|
1051
1415
|
*
|
|
1052
1416
|
* @example
|
|
1053
1417
|
* discovery: httpRouteDiscovery({
|
|
@@ -1062,19 +1426,160 @@ declare function httpRouteDiscovery(opts: {
|
|
|
1062
1426
|
methods: readonly string[];
|
|
1063
1427
|
/** Defaults to "handler". Override for packs that want a different kind. */
|
|
1064
1428
|
kind?: string;
|
|
1429
|
+
/**
|
|
1430
|
+
* How this framework's routable can itself be mounted onto another
|
|
1431
|
+
* one under a path prefix, so a route declared on the mounted value gets
|
|
1432
|
+
* summarized with the prefix built into its path. See `DiscoveryPattern`.
|
|
1433
|
+
*/
|
|
1434
|
+
mount?: DiscoveryPattern["mount"];
|
|
1065
1435
|
}): DiscoveryPattern[];
|
|
1066
1436
|
|
|
1437
|
+
/**
|
|
1438
|
+
* The path engine, written against `StructuredStatement` rather than any one
|
|
1439
|
+
* language. Every control-flow path from entry to a terminal contributes its
|
|
1440
|
+
* own conjunction of conditions, so a terminal you can reach along several
|
|
1441
|
+
* paths becomes several entries instead of one branch with an invented
|
|
1442
|
+
* conjunction, or none at all.
|
|
1443
|
+
*
|
|
1444
|
+
* Nothing here touches a language-specific AST node. A language adapter
|
|
1445
|
+
* lowers its own tree into `StructuredStatement` and hands that over.
|
|
1446
|
+
*
|
|
1447
|
+
* Constructs a lowering declines to model come back as thrown
|
|
1448
|
+
* `PathBudgetExceeded` or `UnmodeledFlow`, and the caller falls back to
|
|
1449
|
+
* enclosure conditions plus an opaque conjunct instead of guessing.
|
|
1450
|
+
*/
|
|
1451
|
+
|
|
1452
|
+
/** A construct the lowering step will not model safely, such as switch and case rules the enumeration does not cover. Callers catch this and degrade. */
|
|
1453
|
+
declare class UnmodeledFlow extends Error {
|
|
1454
|
+
}
|
|
1455
|
+
/** The path budget was exceeded. Callers catch this and degrade. */
|
|
1456
|
+
declare class PathBudgetExceeded extends Error {
|
|
1457
|
+
}
|
|
1458
|
+
interface StructuredPathConditionsInput<Cond, Terminal> {
|
|
1459
|
+
/** The unit's top-level statements, in source order. */
|
|
1460
|
+
statements: StatementBlock<Cond>;
|
|
1461
|
+
/**
|
|
1462
|
+
* Every StructuredStatement reachable from `statements`, mapped to the
|
|
1463
|
+
* terminals the caller gave us that sit directly at that node: its own
|
|
1464
|
+
* position (a return, throw, or opaque leaf) or, for a branching
|
|
1465
|
+
* construct, its header or test expression. A lowering builds this at the
|
|
1466
|
+
* same time as the tree, so the engine never walks a raw source AST to
|
|
1467
|
+
* find where a terminal lives.
|
|
1468
|
+
*/
|
|
1469
|
+
terminalsByStmt: ReadonlyMap<StructuredStatement<Cond>, readonly Terminal[]>;
|
|
1470
|
+
}
|
|
1471
|
+
interface StructuredPathConditionsResult<Cond, Terminal> {
|
|
1472
|
+
/** The paths to each terminal, keyed by the caller's own terminal handle. */
|
|
1473
|
+
byTerminal: Map<Terminal, ConditionInfo<Cond>[][]>;
|
|
1474
|
+
/** Condition lists for paths that fall through the end of the body. */
|
|
1475
|
+
fallthrough: ConditionInfo<Cond>[][];
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Work out the conditions on each path, for every StructuredStatement
|
|
1479
|
+
* reachable from `input.statements`. Throws `PathBudgetExceeded` when the
|
|
1480
|
+
* path count crosses the cap, or `UnmodeledFlow` when a switch or match
|
|
1481
|
+
* turns up that the enumeration does not cover (a stray break, an unsafe
|
|
1482
|
+
* fallthrough). The caller catches both and degrades to enclosure conditions
|
|
1483
|
+
* plus an opaque conjunct, rather than claiming something it did not read.
|
|
1484
|
+
*/
|
|
1485
|
+
declare function enumerateStructuredPaths<Cond, Terminal>(input: StructuredPathConditionsInput<Cond, Terminal>): StructuredPathConditionsResult<Cond, Terminal>;
|
|
1486
|
+
/**
|
|
1487
|
+
* Enumerate, and when the engine gives up say so instead of throwing. Each
|
|
1488
|
+
* terminal comes back reachable under one condition nobody can read, which
|
|
1489
|
+
* keeps everything the caller already knew about it. A caller that can do
|
|
1490
|
+
* better, by walking the terminal's own ancestors for the conditions that
|
|
1491
|
+
* enclose it, should.
|
|
1492
|
+
*/
|
|
1493
|
+
declare function enumerateOrDegrade<Cond, Terminal>(input: StructuredPathConditionsInput<Cond, Terminal>, terminals: Iterable<Terminal>): StructuredPathConditionsResult<Cond, Terminal> & {
|
|
1494
|
+
/** Why the engine gave up, or null when it read the whole body. */
|
|
1495
|
+
degraded: string | null;
|
|
1496
|
+
};
|
|
1497
|
+
|
|
1498
|
+
/**
|
|
1499
|
+
* The conditions every path to a terminal agrees on. A terminal reached more
|
|
1500
|
+
* than one way keeps only the shared ones, so a condition is never claimed
|
|
1501
|
+
* for a path without it.
|
|
1502
|
+
*
|
|
1503
|
+
* `readPredicate` turns the language's own expression handle into a
|
|
1504
|
+
* predicate. Without one every condition stays opaque, which is what a
|
|
1505
|
+
* caller that has no reader gets.
|
|
1506
|
+
*/
|
|
1507
|
+
declare function sharedGatingConditions<Cond>(paths: readonly (readonly ConditionInfo<Cond>[])[] | undefined, readPredicate?: (expression: Cond) => Predicate): RawCondition[];
|
|
1508
|
+
|
|
1509
|
+
/** Anything with a stable id, which is what these key on. */
|
|
1510
|
+
interface Identified {
|
|
1511
|
+
readonly id: number;
|
|
1512
|
+
}
|
|
1513
|
+
/** A set of nodes, compared by id. */
|
|
1514
|
+
declare class IdSet<T extends Identified> implements Iterable<T> {
|
|
1515
|
+
private readonly byKey;
|
|
1516
|
+
constructor(nodes?: Iterable<T>);
|
|
1517
|
+
add(node: T): this;
|
|
1518
|
+
has(node: T): boolean;
|
|
1519
|
+
/** The node this set was built with, which is the caller's own handle for it. */
|
|
1520
|
+
get(node: T): T | undefined;
|
|
1521
|
+
get size(): number;
|
|
1522
|
+
[Symbol.iterator](): Iterator<T>;
|
|
1523
|
+
}
|
|
1524
|
+
/** A map keyed by node, compared by id. */
|
|
1525
|
+
declare class IdMap<T extends Identified, V> implements Iterable<[T, V]> {
|
|
1526
|
+
private readonly entries;
|
|
1527
|
+
set(node: T, value: V): this;
|
|
1528
|
+
get(node: T): V | undefined;
|
|
1529
|
+
has(node: T): boolean;
|
|
1530
|
+
get size(): number;
|
|
1531
|
+
[Symbol.iterator](): Iterator<[T, V]>;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
/**
|
|
1535
|
+
* The small surface every language adapter implements, whatever that
|
|
1536
|
+
* language's own tooling looks like underneath.
|
|
1537
|
+
*
|
|
1538
|
+
* Anything TypeScript-specific, such as the ts-morph Project a caller needs
|
|
1539
|
+
* for corroborate's sandboxed execution, belongs on that adapter's own type
|
|
1540
|
+
* rather than here. This interface never gets a field that mentions one
|
|
1541
|
+
* particular language's tooling.
|
|
1542
|
+
*/
|
|
1543
|
+
|
|
1544
|
+
interface LanguageAdapter {
|
|
1545
|
+
/**
|
|
1546
|
+
* Extract summaries from a specific list of files. Returns a
|
|
1547
|
+
* Promise so an implementation can do concurrent I/O during
|
|
1548
|
+
* discovery without bottlenecking on synchronous reads.
|
|
1549
|
+
*/
|
|
1550
|
+
extractFromFiles(filePaths: string[]): Promise<BehavioralSummary[]>;
|
|
1551
|
+
/** Extract summaries from every source file the adapter's project knows about. */
|
|
1552
|
+
extractAll(): Promise<BehavioralSummary[]>;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/**
|
|
1556
|
+
* The assembly engine: a `RawCodeStructure` from a language adapter goes in,
|
|
1557
|
+
* a `BehavioralSummary` comes out. The package README explains where that step
|
|
1558
|
+
* fits in the pipeline.
|
|
1559
|
+
*
|
|
1560
|
+
* Two things surprise people reading this file. First, `RawCodeStructure` and
|
|
1561
|
+
* the raw types around it are the contract every adapter and pack implements,
|
|
1562
|
+
* so a field added here is a change to that contract. Second, this is the only
|
|
1563
|
+
* module allowed to turn a `Reading` into a claim on a summary; adapters hand
|
|
1564
|
+
* readings over uncollapsed and the rule for collapsing them lives here.
|
|
1565
|
+
*/
|
|
1566
|
+
|
|
1067
1567
|
interface RawParameter {
|
|
1068
1568
|
name: string;
|
|
1069
1569
|
position: number;
|
|
1070
|
-
|
|
1570
|
+
/**
|
|
1571
|
+
* What the parameter is for, in the library's own vocabulary. When the
|
|
1572
|
+
* adapter could not tell, this is null rather than a guess, and the reason
|
|
1573
|
+
* goes in `readings`.
|
|
1574
|
+
*/
|
|
1575
|
+
role: string | null;
|
|
1071
1576
|
typeText: string | null;
|
|
1072
1577
|
}
|
|
1073
1578
|
interface RawCondition {
|
|
1074
1579
|
sourceText: string;
|
|
1075
1580
|
structured: Predicate | null;
|
|
1076
1581
|
polarity: "positive" | "negative";
|
|
1077
|
-
source:
|
|
1582
|
+
source: ConditionSource;
|
|
1078
1583
|
}
|
|
1079
1584
|
interface RawTerminal {
|
|
1080
1585
|
kind: "response" | "throw" | "return" | "render" | "delegate" | "emit" | "void";
|
|
@@ -1091,18 +1596,10 @@ interface RawTerminal {
|
|
|
1091
1596
|
} | null;
|
|
1092
1597
|
exceptionType: string | null;
|
|
1093
1598
|
message: string | null;
|
|
1094
|
-
/** For render terminals: the component being rendered */
|
|
1095
1599
|
component: string | null;
|
|
1096
|
-
/**
|
|
1097
|
-
* For render terminals (JSX / component-tree output): the nested
|
|
1098
|
-
* render tree if the pack's extractor can produce one. Flows
|
|
1099
|
-
* through to `Output.render.root`. Absent when the extractor sees
|
|
1100
|
-
* only the root element name or opted out of tree extraction.
|
|
1101
|
-
*/
|
|
1600
|
+
/** Null when the pack read only the root element name, not the tree under it. */
|
|
1102
1601
|
renderTree: RenderNode | null;
|
|
1103
|
-
/** For delegate terminals: where control is passed */
|
|
1104
1602
|
delegateTarget: string | null;
|
|
1105
|
-
/** For emit terminals: the event/channel name */
|
|
1106
1603
|
emitEvent: string | null;
|
|
1107
1604
|
location: {
|
|
1108
1605
|
start: number;
|
|
@@ -1110,22 +1607,9 @@ interface RawTerminal {
|
|
|
1110
1607
|
};
|
|
1111
1608
|
}
|
|
1112
1609
|
/**
|
|
1113
|
-
*
|
|
1114
|
-
*
|
|
1115
|
-
*
|
|
1116
|
-
* as structured variants so readers can see *what* was passed even when
|
|
1117
|
-
* the runtime value isn't statically resolvable. `null` is reserved for
|
|
1118
|
-
* the rare case where the argument shape doesn't match any variant
|
|
1119
|
-
* (type assertions with computed operands, arithmetic, etc.) — the
|
|
1120
|
-
* caller still knows how many arguments the call had.
|
|
1121
|
-
*
|
|
1122
|
-
* Useful for recognising literal-string discriminators like
|
|
1123
|
-
* `findings.push({ kind: "scenarioCoverageGap" })` or
|
|
1124
|
-
* `dispatch({ type: "USER_LOGGED_IN" })` at the summary level, and for
|
|
1125
|
-
* preserving argument *shape* (`{ userId, count }`, `getUser(id)`) so
|
|
1126
|
-
* downstream consumers (AI agents, error-taxonomy tooling,
|
|
1127
|
-
* release-note generators) can reason about composition without
|
|
1128
|
-
* re-reading source.
|
|
1610
|
+
* An invocation argument as the adapter read it. An argument that fits none of
|
|
1611
|
+
* these variants, arithmetic for instance, comes through as null instead of
|
|
1612
|
+
* being dropped, so a reader still knows how many arguments the call had.
|
|
1129
1613
|
*/
|
|
1130
1614
|
type EffectArg = {
|
|
1131
1615
|
kind: "string";
|
|
@@ -1142,39 +1626,19 @@ type EffectArg = {
|
|
|
1142
1626
|
} | {
|
|
1143
1627
|
kind: "array";
|
|
1144
1628
|
items: EffectArg[];
|
|
1145
|
-
}
|
|
1146
|
-
/**
|
|
1147
|
-
* Template literal with interpolation (`` `Error: ${e.message}` ``).
|
|
1148
|
-
* The resolved runtime value is unknown, but the source text is
|
|
1149
|
-
* preserved so readers can see which variables compose it — useful
|
|
1150
|
-
* for log messages, computed keys, and dedup-style key builders.
|
|
1151
|
-
*/
|
|
1152
|
-
| {
|
|
1629
|
+
} | {
|
|
1153
1630
|
kind: "template";
|
|
1154
1631
|
sourceText: string;
|
|
1155
1632
|
}
|
|
1156
1633
|
/**
|
|
1157
|
-
*
|
|
1158
|
-
*
|
|
1159
|
-
*
|
|
1160
|
-
* source text so readers can tell which binding flowed into the call
|
|
1161
|
-
* without inferring it from context. Identifiers that resolve to a
|
|
1162
|
-
* module-level `const` with a simple initializer (literal, process.env
|
|
1163
|
-
* access, etc.) are inlined to the initializer's EffectArg form so
|
|
1164
|
-
* closure-over-constants doesn't hide the actual value at call sites.
|
|
1634
|
+
* A bare variable or a whole access chain, written out as it appears. An
|
|
1635
|
+
* identifier bound to a module-level const with a simple initializer is
|
|
1636
|
+
* replaced by that initializer, so a constant does not hide the value.
|
|
1165
1637
|
*/
|
|
1166
1638
|
| {
|
|
1167
1639
|
kind: "identifier";
|
|
1168
1640
|
name: string;
|
|
1169
|
-
}
|
|
1170
|
-
/**
|
|
1171
|
-
* Nested call expression passed as an argument — `log(formatError(e))`,
|
|
1172
|
-
* `enqueue(buildPayload(ctx))`, inline composition. `callee` is the
|
|
1173
|
-
* source text of the call target; `args` recurses with bounded depth,
|
|
1174
|
-
* so `formatError(e)` reads as `{ callee: "formatError", args: [...] }`
|
|
1175
|
-
* rather than an opaque null.
|
|
1176
|
-
*/
|
|
1177
|
-
| {
|
|
1641
|
+
} | {
|
|
1178
1642
|
kind: "call";
|
|
1179
1643
|
callee: string;
|
|
1180
1644
|
args: EffectArg[];
|
|
@@ -1188,13 +1652,7 @@ type RawEffect = {
|
|
|
1188
1652
|
callee: string;
|
|
1189
1653
|
args: EffectArg[];
|
|
1190
1654
|
async: boolean;
|
|
1191
|
-
/**
|
|
1192
|
-
* Ancestor if/switch/ternary conditions that gate reaching this
|
|
1193
|
-
* call. Populated for calls nested inside conditional blocks or
|
|
1194
|
-
* loop bodies; empty for top-level (always-fires) calls.
|
|
1195
|
-
* Converts to `Effect.preconditions: Predicate[]` in the IR —
|
|
1196
|
-
* same RawCondition → Predicate pipeline transitions use.
|
|
1197
|
-
*/
|
|
1655
|
+
/** Empty means the call always fires, not that nobody looked. */
|
|
1198
1656
|
preconditions?: RawCondition[];
|
|
1199
1657
|
} | {
|
|
1200
1658
|
type: "emission";
|
|
@@ -1208,14 +1666,8 @@ interface RawBranch {
|
|
|
1208
1666
|
terminal: RawTerminal;
|
|
1209
1667
|
effects: RawEffect[];
|
|
1210
1668
|
/**
|
|
1211
|
-
*
|
|
1212
|
-
*
|
|
1213
|
-
* through — recognizers have full structural knowledge of what
|
|
1214
|
-
* they're emitting (e.g. `interaction(class: "storage-access")` with
|
|
1215
|
-
* binding identity + per-class fields/selector)
|
|
1216
|
-
* and there's no narrower extractor-side representation worth
|
|
1217
|
-
* round-tripping through. Concatenated with converted `effects` at
|
|
1218
|
-
* `assembleSummary` time.
|
|
1669
|
+
* Effects a recognizer built itself, already in IR form, so these skip the
|
|
1670
|
+
* `RawEffect` conversion that `effects` goes through.
|
|
1219
1671
|
*/
|
|
1220
1672
|
extraEffects?: Effect[];
|
|
1221
1673
|
location: {
|
|
@@ -1223,14 +1675,20 @@ interface RawBranch {
|
|
|
1223
1675
|
end: number;
|
|
1224
1676
|
};
|
|
1225
1677
|
isDefault: boolean;
|
|
1678
|
+
/** The fields a consumer reads off a response inside this branch. */
|
|
1679
|
+
expectedInput?: TypeShape | null;
|
|
1226
1680
|
/**
|
|
1227
|
-
*
|
|
1228
|
-
*
|
|
1229
|
-
*
|
|
1230
|
-
* the response body — those accesses are collected into a TypeShape.
|
|
1231
|
-
* The extractor copies this through to Transition.expectedInput.
|
|
1681
|
+
* An adapter that passes its reading along uncollapsed sets this instead of
|
|
1682
|
+
* `terminal.statusCode`. A status nobody wrote then becomes a claim only
|
|
1683
|
+
* where a pack declared the default.
|
|
1232
1684
|
*/
|
|
1233
|
-
|
|
1685
|
+
statusCodeReading?: DefaultedReading<number>;
|
|
1686
|
+
/**
|
|
1687
|
+
* Set instead of `terminal.body.shape`, on the same terms.
|
|
1688
|
+
* `terminal.body.typeText` is left alone, so a pack that gives the type as
|
|
1689
|
+
* text and also reads its structure can do both.
|
|
1690
|
+
*/
|
|
1691
|
+
bodyShapeReading?: DefaultedReading<TypeShape>;
|
|
1234
1692
|
}
|
|
1235
1693
|
interface RawDependencyCall {
|
|
1236
1694
|
name: string;
|
|
@@ -1246,12 +1704,8 @@ interface RawDeclaredContract {
|
|
|
1246
1704
|
framework: string;
|
|
1247
1705
|
responses: Array<{
|
|
1248
1706
|
statusCode: number;
|
|
1249
|
-
/**
|
|
1250
|
-
*
|
|
1251
|
-
* Populated when the adapter can statically resolve the response schema
|
|
1252
|
-
* (e.g. `c.type<T>()`'s type argument). Null when the contract exists
|
|
1253
|
-
* but no body was declared or the schema form isn't yet supported.
|
|
1254
|
-
*/
|
|
1707
|
+
/** Null means either the contract declared no body or suss could not read
|
|
1708
|
+
* the schema form, and nothing here tells the two apart. */
|
|
1255
1709
|
body?: TypeShape | null;
|
|
1256
1710
|
}>;
|
|
1257
1711
|
params?: Record<string, {
|
|
@@ -1259,54 +1713,33 @@ interface RawDeclaredContract {
|
|
|
1259
1713
|
required: boolean;
|
|
1260
1714
|
}>;
|
|
1261
1715
|
/**
|
|
1262
|
-
*
|
|
1263
|
-
*
|
|
1264
|
-
*
|
|
1265
|
-
*
|
|
1266
|
-
* comparing them against each other is tautological. Example: an
|
|
1267
|
-
* OpenAPI stub's contract and its transitions both come from the
|
|
1268
|
-
* operation's `responses` block. The cross-boundary checker
|
|
1269
|
-
* skips per-summary contract-consistency for these.
|
|
1270
|
-
*
|
|
1271
|
-
* - "independent": the contract is a separate statement from the
|
|
1272
|
-
* transitions. Example: a ts-rest handler whose router declares
|
|
1273
|
-
* `responses` and whose implementation is a separate function;
|
|
1274
|
-
* a CFN stub whose `MethodResponses` and integration config are
|
|
1275
|
-
* independent template fields. Contract-consistency comparison
|
|
1276
|
-
* is meaningful.
|
|
1277
|
-
*
|
|
1278
|
-
* Defaults to "independent" when a pack doesn't say — safer to
|
|
1279
|
-
* surface a spurious-but-investigable finding than to silently drop
|
|
1280
|
-
* a real one.
|
|
1716
|
+
* "derived" means the contract and the transitions both come from the same
|
|
1717
|
+
* source, so comparing them proves nothing and the checker skips it. A pack
|
|
1718
|
+
* that says nothing gets "independent", which risks a spurious finding
|
|
1719
|
+
* rather than dropping a valid one.
|
|
1281
1720
|
*/
|
|
1282
1721
|
provenance?: "derived" | "independent";
|
|
1283
1722
|
}
|
|
1284
1723
|
/**
|
|
1285
|
-
* What
|
|
1724
|
+
* What the adapter found where a unit's body should be. Downstream this is the
|
|
1725
|
+
* difference between a summary that says nothing because there was nothing to
|
|
1726
|
+
* say and one that says nothing because nobody could read the body.
|
|
1286
1727
|
*
|
|
1287
|
-
*
|
|
1288
|
-
*
|
|
1289
|
-
*
|
|
1290
|
-
*
|
|
1291
|
-
*
|
|
1292
|
-
*
|
|
1293
|
-
* arrow's expression. A summary that says nothing about it has
|
|
1294
|
-
* described none of it.
|
|
1295
|
-
* - "elsewhere": the unit's body is not in what this run read. A
|
|
1296
|
-
* route registered with a handler the caller supplies announces a
|
|
1297
|
-
* boundary and names no function to look in, and the boundary is
|
|
1298
|
-
* worth reporting with nothing behind it.
|
|
1728
|
+
* "absent" is a declaration with no body at all, such as an overload
|
|
1729
|
+
* signature. "empty" is a body with nothing in it, which an empty summary
|
|
1730
|
+
* describes completely. "statements" is a body with work in it, which an empty
|
|
1731
|
+
* summary describes none of. "elsewhere" is a body this run did not read: a
|
|
1732
|
+
* route registered with a handler the caller supplies points at no function to
|
|
1733
|
+
* go look in.
|
|
1299
1734
|
*/
|
|
1300
1735
|
type BodyContent = "absent" | "empty" | "statements" | "elsewhere";
|
|
1301
1736
|
interface RawCodeStructure {
|
|
1302
|
-
/**
|
|
1303
|
-
*
|
|
1304
|
-
* onto the summary as `definitions`, so a reader following a name has
|
|
1305
|
-
* somewhere to look.
|
|
1306
|
-
*/
|
|
1737
|
+
/** Types the unit's shapes refer to by name instead of spelling out, so a
|
|
1738
|
+
* reader who follows one of those names has somewhere to look. */
|
|
1307
1739
|
definitions?: Record<string, TypeShape> | null;
|
|
1308
1740
|
identity: {
|
|
1309
1741
|
name: string;
|
|
1742
|
+
nameKind?: "binding" | "label";
|
|
1310
1743
|
kind: CodeUnitKind;
|
|
1311
1744
|
file: string;
|
|
1312
1745
|
range: {
|
|
@@ -1316,113 +1749,71 @@ interface RawCodeStructure {
|
|
|
1316
1749
|
exportName: string | null;
|
|
1317
1750
|
exportPath: string[] | null;
|
|
1318
1751
|
};
|
|
1319
|
-
/**
|
|
1320
|
-
*
|
|
1321
|
-
* `recognition`). See `@suss/behavioral-ir`'s `BoundaryBinding`.
|
|
1322
|
-
* Null when the unit participates in no cross-unit boundary —
|
|
1323
|
-
* helpers, pure utilities, and anything the adapter can't place.
|
|
1324
|
-
*/
|
|
1752
|
+
/** Null when the unit is not on any cross-unit boundary, which is the
|
|
1753
|
+
* ordinary case for helpers and pure utilities. */
|
|
1325
1754
|
boundaryBinding: BoundaryBinding | null;
|
|
1326
|
-
/** The thing that gets deployed and runs this code, when known. */
|
|
1327
1755
|
deployableUnit?: DeployableUnit;
|
|
1328
1756
|
parameters: RawParameter[];
|
|
1329
1757
|
branches: RawBranch[];
|
|
1330
|
-
/**
|
|
1331
|
-
*
|
|
1332
|
-
* matched. A handler shaped in a way the pack does not describe still
|
|
1333
|
-
* returns something, and saying nothing about it reads the same as
|
|
1334
|
-
* saying it does nothing.
|
|
1335
|
-
*/
|
|
1758
|
+
/** Return statements no terminal in the pack matched. Leave this at zero and
|
|
1759
|
+
* a handler the pack cannot describe looks like one that returns nothing. */
|
|
1336
1760
|
unmatchedReturns?: number;
|
|
1337
1761
|
/**
|
|
1338
|
-
*
|
|
1339
|
-
*
|
|
1340
|
-
* adapter can say which: a body nobody could read, or a body with
|
|
1341
|
-
* nothing in it to read. Adapters that do not say leave this unset,
|
|
1342
|
-
* and everything downstream treats the summary as it did before.
|
|
1762
|
+
* Only the adapter can tell a body nobody could read from a body with
|
|
1763
|
+
* nothing in it, and an empty summary looks the same either way.
|
|
1343
1764
|
*/
|
|
1344
1765
|
bodyContent?: BodyContent;
|
|
1345
1766
|
dependencyCalls: RawDependencyCall[];
|
|
1346
1767
|
declaredContract: RawDeclaredContract | null;
|
|
1347
|
-
/**
|
|
1348
|
-
*
|
|
1349
|
-
* For client units this records the accessor a consumer uses to reach the
|
|
1350
|
-
* body (e.g. ["data"] for axios, ["body","json","text"] for fetch). Carried
|
|
1351
|
-
* through to `summary.metadata.http.bodyAccessors` so the cross-boundary
|
|
1352
|
-
* checker can unwrap consumer expectedInput correctly without knowing each
|
|
1353
|
-
* pack. Flat here because this is the adapter→extractor plumbing contract;
|
|
1354
|
-
* nesting under `http` happens when the summary is assembled.
|
|
1355
|
-
*/
|
|
1768
|
+
/** The property a consumer goes through to get at the body, `data` for
|
|
1769
|
+
* axios say, so the checker can unwrap it without knowing each pack. */
|
|
1356
1770
|
bodyAccessors?: string[];
|
|
1357
|
-
/**
|
|
1358
|
-
* Names of pack-declared response properties whose semantics is
|
|
1359
|
-
* `statusCode`. Same shape as `bodyAccessors` but for the status side —
|
|
1360
|
-
* lets the checker recognise pack-specific names (e.g. fetch's `status`,
|
|
1361
|
-
* axios's `status`, hypothetical `responseStatus`) when matching consumer
|
|
1362
|
-
* branch conditions to provider transitions. Carried through to
|
|
1363
|
-
* `summary.metadata.http.statusAccessors`.
|
|
1364
|
-
*/
|
|
1771
|
+
/** The same, for the status: `status` for fetch and for axios. */
|
|
1365
1772
|
statusAccessors?: string[];
|
|
1366
|
-
/**
|
|
1367
|
-
*
|
|
1368
|
-
* summaries (the inner text of a `useQuery(gql\`...\`)` call).
|
|
1369
|
-
* Assembled into `summary.metadata.graphql.document` for the
|
|
1370
|
-
* checker's pairing layer to parse. Deliberately untouched here —
|
|
1371
|
-
* the extractor has no dependency on graphql-js; parsing happens
|
|
1372
|
-
* at check time.
|
|
1373
|
-
*/
|
|
1773
|
+
/** Left exactly as written, because the extractor does not depend on
|
|
1774
|
+
* graphql-js. The parsing happens at check time. */
|
|
1374
1775
|
graphqlDocument?: string;
|
|
1375
|
-
/**
|
|
1376
|
-
* Raw GraphQL schema SDL attached to provider-side resolver
|
|
1377
|
-
* summaries. For schema-first stubs (AppSync), the SDL IS the
|
|
1378
|
-
* schema. For code-first frameworks (Apollo Server), the SDL
|
|
1379
|
-
* comes from the `typeDefs` config option when statically
|
|
1380
|
-
* resolvable. Surfaced as `summary.metadata.graphql.schemaSdl`
|
|
1381
|
-
* so the checker can use the return-type field set when pairing
|
|
1382
|
-
* nested consumer selections.
|
|
1383
|
-
*/
|
|
1384
1776
|
graphqlSchemaSdl?: string;
|
|
1385
|
-
/**
|
|
1386
|
-
*
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
*
|
|
1390
|
-
* sources declaring a contract for the same boundary.
|
|
1391
|
-
*
|
|
1392
|
-
* The extractor doesn't derive this — adapters that have the SDL
|
|
1393
|
-
* AND know the (typeName, fieldName) of the resolver populate it.
|
|
1394
|
-
* Today: the TS adapter does this for Apollo-style resolverMap
|
|
1395
|
-
* discovery. NestJS GraphQL (decorator-based) is a follow-up.
|
|
1396
|
-
*/
|
|
1397
|
-
graphqlDeclaredContract?: Record<string, unknown>;
|
|
1398
|
-
/**
|
|
1399
|
-
* Set on a consumer-side GraphQL summary whose document reference was
|
|
1400
|
-
* recognized (an imported `TypedDocumentNode` from graphql-codegen,
|
|
1401
|
-
* say) but whose body couldn't be read statically. Surfaced as
|
|
1402
|
-
* `summary.metadata.graphql.unresolvedDocument` so the unreadable
|
|
1403
|
-
* document is accounted for rather than silently dropped. `reference`
|
|
1404
|
-
* is the identifier passed to the hook / imperative call; `reason`
|
|
1405
|
-
* explains what defeated static resolution.
|
|
1406
|
-
*/
|
|
1777
|
+
/** The extractor cannot derive this. An adapter that has the SDL and knows
|
|
1778
|
+
* which field the resolver serves fills it in. */
|
|
1779
|
+
graphqlDeclaredContract?: GraphqlDeclaredContract;
|
|
1780
|
+
/** Set when a document reference was recognized but its body could not be
|
|
1781
|
+
* read, so an unreadable document is accounted for instead of dropped. */
|
|
1407
1782
|
graphqlUnresolvedDocument?: {
|
|
1408
1783
|
reference: string;
|
|
1409
1784
|
reason: string;
|
|
1410
1785
|
};
|
|
1411
1786
|
/**
|
|
1412
|
-
*
|
|
1413
|
-
*
|
|
1414
|
-
*
|
|
1415
|
-
*
|
|
1416
|
-
*
|
|
1417
|
-
* A statement about the reading rather than about the code, so it
|
|
1418
|
-
* lands as an `unreadOutcome` gap and no checker holds it against the
|
|
1419
|
-
* unit.
|
|
1787
|
+
* Which part of the boundary the source does not state, and why. The binding
|
|
1788
|
+
* still goes out with that part empty, so the unit pairs with nothing rather
|
|
1789
|
+
* than with whatever a guess would have supplied. It comes out as an
|
|
1790
|
+
* `unreadOutcome` gap, so no checker counts it against the unit.
|
|
1420
1791
|
*/
|
|
1421
1792
|
unreadBinding?: string;
|
|
1793
|
+
/**
|
|
1794
|
+
* Readings the adapter passed along without collapsing. This module writes
|
|
1795
|
+
* the reason for any that came back unreadable or ambiguous. Written and
|
|
1796
|
+
* absent readings contribute nothing here, since what they found is already
|
|
1797
|
+
* on the summary.
|
|
1798
|
+
*/
|
|
1799
|
+
readings?: readonly Reading<unknown>[];
|
|
1422
1800
|
}
|
|
1423
1801
|
interface ExtractorOptions {
|
|
1424
1802
|
gapHandling: "strict" | "permissive" | "silent";
|
|
1425
1803
|
}
|
|
1804
|
+
/**
|
|
1805
|
+
* The id for one branch's transition, built so that editing a handler does not
|
|
1806
|
+
* churn it. Reordering branches, or adding an unrelated one, must leave the
|
|
1807
|
+
* existing ids alone, otherwise `diffSummaries` reports "everything changed"
|
|
1808
|
+
* every time somebody shuffles a handler around.
|
|
1809
|
+
*
|
|
1810
|
+
* The id combines the enclosing function name, the terminal kind, the status
|
|
1811
|
+
* code (a literal value, the source text of a dynamic one, or "none"), and a
|
|
1812
|
+
* short hash of the condition chain's source texts. Editing a branch's body
|
|
1813
|
+
* without touching its guards or its status leaves the id alone, so a diff
|
|
1814
|
+
* reports one changed transition instead of an add plus a remove. Change any
|
|
1815
|
+
* of those signals and you get a new id.
|
|
1816
|
+
*/
|
|
1426
1817
|
declare function makeTransitionId(functionName: string, branch: RawBranch): string;
|
|
1427
1818
|
declare function assembleSummary(raw: RawCodeStructure, options?: ExtractorOptions): BehavioralSummary;
|
|
1428
1819
|
declare function detectGaps(raw: RawCodeStructure, transitions: Transition[], options: ExtractorOptions): Gap[];
|
|
@@ -1431,4 +1822,4 @@ declare function terminalToOutput(terminal: RawTerminal): Output;
|
|
|
1431
1822
|
declare function effectToIR(effect: RawEffect): Effect;
|
|
1432
1823
|
declare function paramToInput(param: RawParameter): Input;
|
|
1433
1824
|
|
|
1434
|
-
export { type AccessRecognizer, type BindingExtraction, type BodyContent, type ContractPattern, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type ExtractorOptions, type InputMappingPattern, type InvocationRecognizer, type PatternPack, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type ResponsePropertyMapping, type ResponsePropertyMeaning, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type TransparentWrapper, assembleSummary, assessConfidence, detectGaps, effectToIR, httpRouteDiscovery, makeTransitionId, paramToInput, terminalToOutput };
|
|
1825
|
+
export { type AccessRecognizer, type BindingExtraction, type BodyContent, type CaseGroup, type ChosenReading, type ConditionHandle, type ConditionInfo, type ConditionSource, type ContractPattern, type DefaultedReading, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type ExitKind, type ExtractorOptions, IdMap, IdSet, type Identified, type InputMappingPattern, type InvocationRecognizer, type LanguageAdapter, PathBudgetExceeded, type PatternPack, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type Reading, type ResponsePropertyMapping, type ResponsePropertyMeaning, type SourceRange, type StatementBlock, type StructuredPathConditionsInput, type StructuredPathConditionsResult, type StructuredStatement, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type TransparentWrapper, UnmodeledFlow, absentReading, ambiguousReading, andThenReading, assembleSummary, assessConfidence, detectGaps, effectToIR, enumerateOrDegrade, enumerateStructuredPaths, firstWrittenReading, httpRouteDiscovery, makeTransitionId, mapReading, paramToInput, sharedGatingConditions, terminalToOutput, unreadableReading, valueToReadFurtherFrom, writtenReading };
|