@suss/extractor 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +205 -0
- package/README.md +71 -0
- package/dist/index.cjs +314 -0
- package/dist/index.d.cts +1269 -0
- package/dist/index.d.ts +1269 -0
- package/dist/index.js +282 -0
- package/package.json +58 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1269 @@
|
|
|
1
|
+
import { Effect, Predicate, TypeShape, RenderNode, CodeUnitKind, BoundaryBinding, BehavioralSummary, ConfidenceInfo, Transition, Gap, Input, Output } from '@suss/behavioral-ir';
|
|
2
|
+
|
|
3
|
+
type DiscoveryMatch = {
|
|
4
|
+
type: "namedExport";
|
|
5
|
+
names: string[];
|
|
6
|
+
} | {
|
|
7
|
+
type: "registrationCall";
|
|
8
|
+
importModule: string;
|
|
9
|
+
importName: string;
|
|
10
|
+
registrationChain: string[];
|
|
11
|
+
} | {
|
|
12
|
+
type: "decorator";
|
|
13
|
+
decoratorModule: string;
|
|
14
|
+
decoratorName: string;
|
|
15
|
+
} | {
|
|
16
|
+
type: "fileConvention";
|
|
17
|
+
filePattern: string;
|
|
18
|
+
exportNames: string[];
|
|
19
|
+
} | {
|
|
20
|
+
type: "clientCall";
|
|
21
|
+
/** Module the client is imported from, or "global" for built-ins like fetch */
|
|
22
|
+
importModule: string;
|
|
23
|
+
/** Named export or identifier — e.g. "initClient", "fetch" */
|
|
24
|
+
importName: string;
|
|
25
|
+
/** If set, only match calls to these methods on the client (e.g. ["getUser"]).
|
|
26
|
+
* Unset means any method call (or bare call for globals). */
|
|
27
|
+
methodFilter?: string[];
|
|
28
|
+
/**
|
|
29
|
+
* Method names on the import that produce a client-equivalent instance,
|
|
30
|
+
* so variables initialized from those calls also act as discovery
|
|
31
|
+
* subjects. axios uses `axios.create({...})` to build a baseURL-bound
|
|
32
|
+
* instance; declaring `factoryMethods: ["create"]` lets the adapter
|
|
33
|
+
* treat `api.get(...)` (where `api = axios.create(...)`) the same as
|
|
34
|
+
* `axios.get(...)`.
|
|
35
|
+
*/
|
|
36
|
+
factoryMethods?: string[];
|
|
37
|
+
} | {
|
|
38
|
+
/**
|
|
39
|
+
* A constructor or factory call that takes a configuration object
|
|
40
|
+
* containing a resolver map — the idiomatic GraphQL code-first
|
|
41
|
+
* shape. The map is two levels deep: outer keys are GraphQL type
|
|
42
|
+
* names (`Query`, `Mutation`, `Subscription`, or object-type
|
|
43
|
+
* names like `User`); inner keys are field names whose values
|
|
44
|
+
* are resolver functions.
|
|
45
|
+
*
|
|
46
|
+
* Example (Apollo Server v4):
|
|
47
|
+
* ```ts
|
|
48
|
+
* new ApolloServer({
|
|
49
|
+
* typeDefs,
|
|
50
|
+
* resolvers: {
|
|
51
|
+
* Query: { users: async () => {...} },
|
|
52
|
+
* Mutation: { createUser: async (_, {input}) => {...} },
|
|
53
|
+
* User: { fullName: (parent) => `${parent.first} ${parent.last}` },
|
|
54
|
+
* },
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* Each inner function becomes one discovered unit whose binding
|
|
59
|
+
* semantics is `graphql-resolver(typeName, fieldName)`. Matches
|
|
60
|
+
* both `new Ctor(cfg)` and `ctor(cfg)` — Apollo's standalone
|
|
61
|
+
* server uses `new`, yoga uses a bare call.
|
|
62
|
+
*/
|
|
63
|
+
type: "resolverMap";
|
|
64
|
+
importModule: string;
|
|
65
|
+
importName: string;
|
|
66
|
+
/**
|
|
67
|
+
* Name of the property on the config object that holds the
|
|
68
|
+
* resolver map. Defaults to `"resolvers"` when unset, matching
|
|
69
|
+
* Apollo / yoga / graphql-tools convention.
|
|
70
|
+
*/
|
|
71
|
+
mapProperty?: string;
|
|
72
|
+
/**
|
|
73
|
+
* GraphQL type names whose fields we DON'T treat as resolvers —
|
|
74
|
+
* opt-out for meta-types like `Subscription` that we may want
|
|
75
|
+
* to handle differently later. Unset means discover every type.
|
|
76
|
+
*/
|
|
77
|
+
excludeTypes?: string[];
|
|
78
|
+
} | {
|
|
79
|
+
/**
|
|
80
|
+
* Consumer-side GraphQL hook call — the canonical Apollo Client
|
|
81
|
+
* and urql shape. Each call to one of the listed hooks becomes
|
|
82
|
+
* a `client`-kind code unit whose binding semantics is
|
|
83
|
+
* `graphql-operation(operationType, operationName?)`.
|
|
84
|
+
*
|
|
85
|
+
* The document argument resolves through several shapes: an inline
|
|
86
|
+
* `gql`-tagged template, a const binding (same module or imported
|
|
87
|
+
* from another module), a `.graphql` / `.gql` file import, and a
|
|
88
|
+
* generated `TypedDocumentNode` object literal from graphql-codegen
|
|
89
|
+
* client-preset. When the document body isn't statically readable,
|
|
90
|
+
* the operation header falls back to the `TypedDocumentNode` type
|
|
91
|
+
* arguments; a still-unresolvable document surfaces on the summary
|
|
92
|
+
* as `metadata.graphql.unresolvedDocument` rather than dropping the
|
|
93
|
+
* boundary.
|
|
94
|
+
*
|
|
95
|
+
* Example:
|
|
96
|
+
* ```ts
|
|
97
|
+
* import { gql, useQuery } from "@apollo/client";
|
|
98
|
+
* const GET_USER = gql`query GetUser($id: ID!) { user(id: $id) { id } }`;
|
|
99
|
+
* function UserPage({ id }) {
|
|
100
|
+
* const { data } = useQuery(GET_USER, { variables: { id } });
|
|
101
|
+
* ...
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* The adapter records the operation name / type on the
|
|
106
|
+
* DiscoveredUnit's `operationInfo`; binding construction uses that
|
|
107
|
+
* to emit `graphql-operation(...)`. The per-hook `operationType`
|
|
108
|
+
* is authoritative when the document header can't be read (mirrors
|
|
109
|
+
* `graphqlImperativeCall.methods`).
|
|
110
|
+
*/
|
|
111
|
+
type: "graphqlHookCall";
|
|
112
|
+
importModule: string;
|
|
113
|
+
/**
|
|
114
|
+
* Hooks to match on that import, each mapped to the operation
|
|
115
|
+
* type it performs (`useQuery` → query, `useMutation` → mutation,
|
|
116
|
+
* `useSubscription` → subscription). The mapping supplies the
|
|
117
|
+
* operation type when the document body isn't statically readable.
|
|
118
|
+
* Each hook is reported as `kind = "client"` by default; packs can
|
|
119
|
+
* override via the enclosing `DiscoveryPattern.kind`.
|
|
120
|
+
*/
|
|
121
|
+
hooks: Array<{
|
|
122
|
+
hookName: string;
|
|
123
|
+
operationType: "query" | "mutation" | "subscription";
|
|
124
|
+
}>;
|
|
125
|
+
} | {
|
|
126
|
+
/**
|
|
127
|
+
* Imperative Apollo-Client-style call — `client.query({ query })`,
|
|
128
|
+
* `client.mutate({ mutation })`, `client.subscribe({ query })`.
|
|
129
|
+
* Distinct from hook calls because the document lives on a
|
|
130
|
+
* config-object property rather than the first positional arg.
|
|
131
|
+
* Discovery is gated on an import of the named constructor
|
|
132
|
+
* (typically `ApolloClient`) to reduce false positives — any
|
|
133
|
+
* method-named "query" on a random object could look like this
|
|
134
|
+
* shape.
|
|
135
|
+
*
|
|
136
|
+
* Each entry in `methods` specifies the method name that gets
|
|
137
|
+
* called on the client (`"query"` / `"mutate"` / `"subscribe"`)
|
|
138
|
+
* and the config-object property that holds the gql document
|
|
139
|
+
* (`"query"` / `"mutation"` / `"query"` respectively). The
|
|
140
|
+
* method name drives the operation type (query / mutation /
|
|
141
|
+
* subscription) when the gql document's header is anonymous;
|
|
142
|
+
* a named document's header wins otherwise.
|
|
143
|
+
*/
|
|
144
|
+
type: "graphqlImperativeCall";
|
|
145
|
+
importModule: string;
|
|
146
|
+
importName: string;
|
|
147
|
+
methods: Array<{
|
|
148
|
+
methodName: string;
|
|
149
|
+
documentKey: string;
|
|
150
|
+
operationType: "query" | "mutation" | "subscription";
|
|
151
|
+
}>;
|
|
152
|
+
} | {
|
|
153
|
+
/**
|
|
154
|
+
* Treats a TypeScript package's public export surface as a
|
|
155
|
+
* boundary. The adapter reads `package.json` at
|
|
156
|
+
* `packageJsonPath`, resolves each reachable entry point
|
|
157
|
+
* (root `.` and any sub-path `exports`), follows barrel
|
|
158
|
+
* re-exports, and emits one discovered unit per exported
|
|
159
|
+
* function — provider side of an in-process `function-call`
|
|
160
|
+
* boundary.
|
|
161
|
+
*
|
|
162
|
+
* Produced bindings carry identity
|
|
163
|
+
* `{ transport: "in-process",
|
|
164
|
+
* semantics: { name: "function-call",
|
|
165
|
+
* package: <pkg.name>,
|
|
166
|
+
* exportPath: [...] },
|
|
167
|
+
* recognition: <pack.name> }`.
|
|
168
|
+
*
|
|
169
|
+
* Sub-path exports identify as e.g.
|
|
170
|
+
* `@suss/behavioral-ir/schemas::BehavioralSummarySchema` —
|
|
171
|
+
* `exportPath = ["schemas", "BehavioralSummarySchema"]`.
|
|
172
|
+
* Root exports omit the sub-path segment.
|
|
173
|
+
*
|
|
174
|
+
* v0 scope: resolves `types` / `default` / `import` conditions
|
|
175
|
+
* on `exports`, falls back to `types` + `main` + `module`
|
|
176
|
+
* when no `exports` field is set. Pattern exports (`./utils/*`)
|
|
177
|
+
* and `development`-conditional resolution are deferred.
|
|
178
|
+
*/
|
|
179
|
+
type: "packageExports";
|
|
180
|
+
/** Absolute path to the package's `package.json`. */
|
|
181
|
+
packageJsonPath: string;
|
|
182
|
+
/**
|
|
183
|
+
* Restrict to these `exports` keys (without leading `./`).
|
|
184
|
+
* The root export is keyed `"."`. Unset means all resolvable
|
|
185
|
+
* sub-paths.
|
|
186
|
+
*/
|
|
187
|
+
subPaths?: string[];
|
|
188
|
+
/**
|
|
189
|
+
* Export names to skip — typically `["default"]` when a pack
|
|
190
|
+
* wants to treat default exports separately or not at all.
|
|
191
|
+
*/
|
|
192
|
+
excludeNames?: string[];
|
|
193
|
+
} | {
|
|
194
|
+
/**
|
|
195
|
+
* Class methods that carry a specific decorator, on classes that
|
|
196
|
+
* carry a specific class-level decorator. Used by NestJS-style
|
|
197
|
+
* frameworks where resolvers / handlers / controllers are
|
|
198
|
+
* declared by decorator, not by registering a function in an
|
|
199
|
+
* object literal.
|
|
200
|
+
*
|
|
201
|
+
* Discovery gates on an `importModule` of `classDecorator` and
|
|
202
|
+
* each `methodDecorators` entry — random user-defined decorators
|
|
203
|
+
* with the same names won't trip the matcher.
|
|
204
|
+
*
|
|
205
|
+
* For a NestJS GraphQL pack:
|
|
206
|
+
* `{ importModule: "@nestjs/graphql",
|
|
207
|
+
* classDecorator: "Resolver",
|
|
208
|
+
* methodDecorators: ["Query", "Mutation", "ResolveField",
|
|
209
|
+
* "Subscription"] }`
|
|
210
|
+
*
|
|
211
|
+
* The adapter populates `DiscoveredUnit.resolverInfo` so the
|
|
212
|
+
* produced binding carries `graphql-resolver(typeName, fieldName)`.
|
|
213
|
+
* `typeName` resolves from the class decorator's first argument
|
|
214
|
+
* (`@Resolver(() => User)` → `"User"`); when the class decorator
|
|
215
|
+
* has no argument it defaults to the operation kind (`"Query"`,
|
|
216
|
+
* `"Mutation"`, `"Subscription"`). `fieldName` reads the method
|
|
217
|
+
* decorator's `{ name }` option override when present, otherwise
|
|
218
|
+
* the method name.
|
|
219
|
+
*/
|
|
220
|
+
type: "decoratedMethod";
|
|
221
|
+
/**
|
|
222
|
+
* The module a pack-recognised decorator must be imported from
|
|
223
|
+
* for discovery to fire. Codebases sometimes wrap framework
|
|
224
|
+
* decorators in their own re-exports (NestJS apps frequently
|
|
225
|
+
* declare `MetadataResolver` / `CoreResolver` factories that
|
|
226
|
+
* compose `@Resolver()` with extra metadata). When that happens,
|
|
227
|
+
* the import-module gate would miss them; pass an array of
|
|
228
|
+
* accepted modules and any one match suffices.
|
|
229
|
+
*/
|
|
230
|
+
importModule: string | string[];
|
|
231
|
+
/**
|
|
232
|
+
* Class decorator names to recognise. The first entry that
|
|
233
|
+
* appears on a class wins for typeName extraction (the rest are
|
|
234
|
+
* fallbacks for codebases with multiple wrapper styles).
|
|
235
|
+
*/
|
|
236
|
+
classDecorators: string[];
|
|
237
|
+
methodDecorators: string[];
|
|
238
|
+
} | {
|
|
239
|
+
/**
|
|
240
|
+
* NestJS-style REST controller discovery. Class decorated with
|
|
241
|
+
* `@Controller(pathPrefix?)`; methods decorated with
|
|
242
|
+
* `@Get(subpath?)` / `@Post` / `@Put` / `@Delete` / etc. The
|
|
243
|
+
* decorator NAME determines the HTTP method via
|
|
244
|
+
* `methodDecoratorRouteMap`; the route path is the class
|
|
245
|
+
* decorator's first arg + the method decorator's first arg
|
|
246
|
+
* (slash-joined, both optional).
|
|
247
|
+
*
|
|
248
|
+
* Same wrapper-decorator tolerance as `decoratedMethod`: the
|
|
249
|
+
* import-module gate fires on at least one method-route
|
|
250
|
+
* decorator from the framework module, but class decorators
|
|
251
|
+
* match by name only so project-internal wrappers
|
|
252
|
+
* (`@PublicController` / `@AuthedController` factories
|
|
253
|
+
* composing `@Controller()`) work without per-project pack
|
|
254
|
+
* config.
|
|
255
|
+
*
|
|
256
|
+
* The adapter populates `DiscoveredUnit.routeInfo` so the
|
|
257
|
+
* produced binding carries `rest(method, path)`.
|
|
258
|
+
*/
|
|
259
|
+
type: "decoratedRoute";
|
|
260
|
+
importModule: string | string[];
|
|
261
|
+
classDecorators: string[];
|
|
262
|
+
/**
|
|
263
|
+
* Decorator name → HTTP method. NestJS uses one decorator per
|
|
264
|
+
* verb (`@Get` / `@Post` / `@Put` / `@Delete` / `@Patch` /
|
|
265
|
+
* `@Options` / `@Head` / `@All`); other frameworks may follow
|
|
266
|
+
* the same convention. The values become the `method` field on
|
|
267
|
+
* the produced REST binding; `"*"` is acceptable for catch-all
|
|
268
|
+
* decorators.
|
|
269
|
+
*/
|
|
270
|
+
methodDecoratorRouteMap: Record<string, string>;
|
|
271
|
+
} | {
|
|
272
|
+
/**
|
|
273
|
+
* Loop expansion: a `for-of` loop over a literal array of
|
|
274
|
+
* route specs is treated as if each element were an inline
|
|
275
|
+
* registration. Used for patterns like:
|
|
276
|
+
*
|
|
277
|
+
* const routes = [
|
|
278
|
+
* { method: "get", path: "/users", handler: getUsers },
|
|
279
|
+
* ...
|
|
280
|
+
* ];
|
|
281
|
+
* for (const r of routes) app[r.method](r.path, r.handler);
|
|
282
|
+
*
|
|
283
|
+
* `elementShape` declares which keys on each element carry the
|
|
284
|
+
* method, path, and handler. The loop body is required to
|
|
285
|
+
* contain at least one call expression that references the
|
|
286
|
+
* loop variable (filters out unrelated loops); the call's
|
|
287
|
+
* shape itself is not checked beyond that.
|
|
288
|
+
*
|
|
289
|
+
* Iterables that resolve to an `ArrayLiteralExpression` (inline
|
|
290
|
+
* or single-hop `const`-bound) are expanded; cross-file or
|
|
291
|
+
* computed iterables are out of v0 scope.
|
|
292
|
+
*
|
|
293
|
+
* Pack-author docs: `docs/internal/proposals/dynamic-registration.md`.
|
|
294
|
+
*/
|
|
295
|
+
type: "registrationLoop";
|
|
296
|
+
elementShape: {
|
|
297
|
+
methodKey: string;
|
|
298
|
+
pathKey: string;
|
|
299
|
+
handlerKey: string;
|
|
300
|
+
};
|
|
301
|
+
} | {
|
|
302
|
+
/**
|
|
303
|
+
* Helper-call expansion: a single function call at the user
|
|
304
|
+
* site is treated as if it were N inline registrations, with
|
|
305
|
+
* arguments substituted into per-registration templates. Used
|
|
306
|
+
* for patterns like `registerCrud(app, 'users', userHandlers)`
|
|
307
|
+
* that today's `registrationCall` discovery can't see.
|
|
308
|
+
*
|
|
309
|
+
* Each entry in `registrations` describes one virtual route
|
|
310
|
+
* the helper produces. `pathTemplate` and `handlerArg` use
|
|
311
|
+
* `{N}` placeholders that resolve to the call's positional
|
|
312
|
+
* arguments. `{N}` substitutes the argument's literal value
|
|
313
|
+
* (for string-literal args) or its source text (for
|
|
314
|
+
* non-literal args, with the slot marked opaque). `{N}.prop`
|
|
315
|
+
* reads `prop` from the argument's resolved object.
|
|
316
|
+
*
|
|
317
|
+
* `importModule` optionally narrows matches to helpers
|
|
318
|
+
* imported from a specific module — useful when two packages
|
|
319
|
+
* happen to export a function with the same name.
|
|
320
|
+
*
|
|
321
|
+
* Pack-author docs: `docs/internal/proposals/dynamic-registration.md`.
|
|
322
|
+
*/
|
|
323
|
+
type: "registrationTemplate";
|
|
324
|
+
helperName: string;
|
|
325
|
+
importModule?: string;
|
|
326
|
+
registrations: Array<{
|
|
327
|
+
method: string;
|
|
328
|
+
pathTemplate: string;
|
|
329
|
+
handlerArg: string;
|
|
330
|
+
}>;
|
|
331
|
+
} | {
|
|
332
|
+
/**
|
|
333
|
+
* Consumer side of the package-export boundary. Scans source
|
|
334
|
+
* files for imports of the named packages and records every
|
|
335
|
+
* call site, emitting one `caller`-kind unit per enclosing
|
|
336
|
+
* function. Produced bindings carry
|
|
337
|
+
* `function-call { package, exportPath }` matching the
|
|
338
|
+
* provider summaries from `packageExports`.
|
|
339
|
+
*
|
|
340
|
+
* `packages` is a list of exact package names (possibly with a
|
|
341
|
+
* sub-path, e.g. `"@suss/behavioral-ir/schemas"`) whose imports
|
|
342
|
+
* to track. Pass multiple package names to track a family at
|
|
343
|
+
* once. Imports of any other package are ignored.
|
|
344
|
+
*
|
|
345
|
+
* v0 scope: named imports + default imports. Namespace imports
|
|
346
|
+
* (`import * as X from`) are not yet tracked. Re-imports within
|
|
347
|
+
* the consumer repo (consumer A imports from consumer B which
|
|
348
|
+
* re-exports from pkg) produce units against the intermediate,
|
|
349
|
+
* not the original — full symbol resolution is deferred.
|
|
350
|
+
*/
|
|
351
|
+
type: "packageImport";
|
|
352
|
+
packages: string[];
|
|
353
|
+
};
|
|
354
|
+
type BindingExtraction = {
|
|
355
|
+
method: {
|
|
356
|
+
type: "fromRegistration";
|
|
357
|
+
position: "methodName" | number;
|
|
358
|
+
} | {
|
|
359
|
+
type: "fromExportName";
|
|
360
|
+
} | {
|
|
361
|
+
type: "fromContract";
|
|
362
|
+
} | {
|
|
363
|
+
type: "fromClientMethod";
|
|
364
|
+
} | {
|
|
365
|
+
type: "fromArgumentProperty";
|
|
366
|
+
position: number;
|
|
367
|
+
property: string;
|
|
368
|
+
default?: string;
|
|
369
|
+
} | {
|
|
370
|
+
type: "literal";
|
|
371
|
+
value: string;
|
|
372
|
+
};
|
|
373
|
+
path: {
|
|
374
|
+
type: "fromRegistration";
|
|
375
|
+
position: number;
|
|
376
|
+
} | {
|
|
377
|
+
type: "fromFilename";
|
|
378
|
+
} | {
|
|
379
|
+
type: "fromContract";
|
|
380
|
+
} | {
|
|
381
|
+
type: "fromClientMethod";
|
|
382
|
+
} | {
|
|
383
|
+
type: "fromArgumentLiteral";
|
|
384
|
+
position: number;
|
|
385
|
+
};
|
|
386
|
+
};
|
|
387
|
+
interface DiscoveryPattern {
|
|
388
|
+
/** The kind of code unit this discovers: "handler", "loader", "action", "component", etc. */
|
|
389
|
+
kind: string;
|
|
390
|
+
match: DiscoveryMatch;
|
|
391
|
+
bindingExtraction?: BindingExtraction;
|
|
392
|
+
/**
|
|
393
|
+
* Files whose import declarations include any of these module
|
|
394
|
+
* specifiers (or sub-paths of them) get this pattern's discovery
|
|
395
|
+
* dispatch. Empty array = no gate (pattern is dispatched against
|
|
396
|
+
* every file). Undefined = treated as no gate, but pack authors
|
|
397
|
+
* SHOULD declare it explicitly — the `[]` form is the deliberate
|
|
398
|
+
* "match every file" choice (typically because the pattern keys on
|
|
399
|
+
* something other than imports — e.g. the fetch runtime matching
|
|
400
|
+
* global `fetch(...)` calls).
|
|
401
|
+
*
|
|
402
|
+
* Match semantics: prefix on the import module specifier. An entry
|
|
403
|
+
* `"@nestjs/graphql"` matches `from "@nestjs/graphql"` AND
|
|
404
|
+
* `from "@nestjs/graphql/dist/foo"` AND any other sub-path.
|
|
405
|
+
*
|
|
406
|
+
* Pre-filter is purely a perf optimisation: the closure walk and
|
|
407
|
+
* other post-passes still have access to every loaded file via
|
|
408
|
+
* symbol resolution.
|
|
409
|
+
*/
|
|
410
|
+
requiresImport?: string[];
|
|
411
|
+
}
|
|
412
|
+
type TerminalMatch = {
|
|
413
|
+
type: "returnShape";
|
|
414
|
+
requiredProperties?: string[];
|
|
415
|
+
} | {
|
|
416
|
+
type: "returnStatement";
|
|
417
|
+
/**
|
|
418
|
+
* Skip ReturnStatements whose returned expression is a CallExpression
|
|
419
|
+
* (or NewExpression). For frameworks where `return reply.send(...)`
|
|
420
|
+
* also lands as a `parameterMethodCall` match on the inner call,
|
|
421
|
+
* this prevents the same `return reply.send(...)` from producing
|
|
422
|
+
* two terminals — one from the wrapping returnStatement, one from
|
|
423
|
+
* the inner method-call chain. Bare returns (`return user`,
|
|
424
|
+
* `return { id }`, `return await fn()`) still match.
|
|
425
|
+
*/
|
|
426
|
+
excludeCallReturns?: boolean;
|
|
427
|
+
} | {
|
|
428
|
+
type: "parameterMethodCall";
|
|
429
|
+
parameterPosition: number;
|
|
430
|
+
methodChain: string[];
|
|
431
|
+
} | {
|
|
432
|
+
type: "throwExpression";
|
|
433
|
+
constructorPattern?: string;
|
|
434
|
+
} | {
|
|
435
|
+
type: "functionCall";
|
|
436
|
+
functionName: string;
|
|
437
|
+
/**
|
|
438
|
+
* Only match when the name was imported from one of these modules.
|
|
439
|
+
* Same name, shape, and prefix semantics as a DiscoveryPattern's
|
|
440
|
+
* gate: "react-router" also matches "react-router/server".
|
|
441
|
+
*
|
|
442
|
+
* Set it whenever the function belongs to a library, because
|
|
443
|
+
* matching a bare name claims every same-named function in the
|
|
444
|
+
* user's project too. `json` is a common name for a project's own
|
|
445
|
+
* response helper, and reading a library's argument order into one
|
|
446
|
+
* of those produces a confident wrong answer.
|
|
447
|
+
*
|
|
448
|
+
* Leave it unset only when the function is not a library's at all.
|
|
449
|
+
* A pack should generally not name a project's own helper: declare
|
|
450
|
+
* the envelope shape instead, through a `returnShape` terminal, and
|
|
451
|
+
* the adapter follows a returned call into the project and reads
|
|
452
|
+
* the helper's parameters. That covers helpers whatever they are
|
|
453
|
+
* named and in whichever argument order they were written.
|
|
454
|
+
*/
|
|
455
|
+
requiresImport?: string[];
|
|
456
|
+
} | {
|
|
457
|
+
/**
|
|
458
|
+
* Return statement whose value is a JSX element or fragment. The
|
|
459
|
+
* root element/component name is recorded in RawTerminal.component.
|
|
460
|
+
* Used by React (and any other JSX-based framework pack) to
|
|
461
|
+
* classify component outputs as `render` terminals.
|
|
462
|
+
*/
|
|
463
|
+
type: "jsxReturn";
|
|
464
|
+
} | {
|
|
465
|
+
/**
|
|
466
|
+
* Synthetic terminal for the implicit fall-through at the end of a
|
|
467
|
+
* function body. Fires when the function has no explicit
|
|
468
|
+
* `ReturnStatement` / `ThrowStatement` as its last statement —
|
|
469
|
+
* covers the common case of handler / effect bodies that execute
|
|
470
|
+
* side-effects and return `undefined` implicitly. Without this,
|
|
471
|
+
* handler summaries come out with `transitions: []` because
|
|
472
|
+
* `findTerminals` has nothing to match. Packs that always expect
|
|
473
|
+
* explicit returns (HTTP handlers) shouldn't include this in
|
|
474
|
+
* their terminals; packs for callback bodies (React handlers,
|
|
475
|
+
* `useEffect` bodies, Node `.on(...)` callbacks) should.
|
|
476
|
+
*/
|
|
477
|
+
type: "functionFallthrough";
|
|
478
|
+
};
|
|
479
|
+
interface TerminalExtraction {
|
|
480
|
+
statusCode?: {
|
|
481
|
+
from: "property";
|
|
482
|
+
name: string;
|
|
483
|
+
} | {
|
|
484
|
+
from: "argument";
|
|
485
|
+
position: number;
|
|
486
|
+
minArgs?: number;
|
|
487
|
+
} | {
|
|
488
|
+
from: "constructor";
|
|
489
|
+
codes: Record<string, number>;
|
|
490
|
+
} | {
|
|
491
|
+
from: "argumentConstructor";
|
|
492
|
+
position: number;
|
|
493
|
+
codes: Record<string, number>;
|
|
494
|
+
};
|
|
495
|
+
body?: {
|
|
496
|
+
from: "property";
|
|
497
|
+
name: string;
|
|
498
|
+
unwrapJsonStringify?: boolean;
|
|
499
|
+
} | {
|
|
500
|
+
from: "argument";
|
|
501
|
+
position: number;
|
|
502
|
+
minArgs?: number;
|
|
503
|
+
};
|
|
504
|
+
/** Fallback status code when none is extracted. e.g. Express res.json() defaults to 200. */
|
|
505
|
+
defaultStatusCode?: number;
|
|
506
|
+
}
|
|
507
|
+
interface TerminalPattern {
|
|
508
|
+
/** What kind of output this terminal produces: "response", "throw", "return", "render" */
|
|
509
|
+
kind: "response" | "throw" | "return" | "render";
|
|
510
|
+
match: TerminalMatch;
|
|
511
|
+
extraction: TerminalExtraction;
|
|
512
|
+
}
|
|
513
|
+
interface ContractPattern {
|
|
514
|
+
/** How to find the contract object. Contracts are data structures, not code units,
|
|
515
|
+
* so this is a simpler shape than DiscoveryPattern. */
|
|
516
|
+
discovery: {
|
|
517
|
+
importModule: string;
|
|
518
|
+
importName: string;
|
|
519
|
+
registrationChain: string[];
|
|
520
|
+
};
|
|
521
|
+
responseExtraction: {
|
|
522
|
+
/** Property on the contract object that holds the responses map */
|
|
523
|
+
property: string;
|
|
524
|
+
};
|
|
525
|
+
paramsExtraction?: {
|
|
526
|
+
property: string;
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
type InputMappingPattern = {
|
|
530
|
+
/** Single object parameter, e.g. React Router LoaderFunctionArgs */
|
|
531
|
+
type: "singleObjectParam";
|
|
532
|
+
paramPosition: number;
|
|
533
|
+
/** Property name → role, e.g. { params: "pathParams", request: "request" } */
|
|
534
|
+
knownProperties: Record<string, string>;
|
|
535
|
+
} | {
|
|
536
|
+
/** Positional parameters, e.g. Express (req, res, next) */
|
|
537
|
+
type: "positionalParams";
|
|
538
|
+
params: Array<{
|
|
539
|
+
position: number;
|
|
540
|
+
role: string;
|
|
541
|
+
}>;
|
|
542
|
+
} | {
|
|
543
|
+
/** Destructured from framework call, e.g. ts-rest { params, body, query } */
|
|
544
|
+
type: "destructuredObject";
|
|
545
|
+
/** Property name → role, e.g. { params: "pathParams", body: "requestBody" } */
|
|
546
|
+
knownProperties: Record<string, string>;
|
|
547
|
+
} | {
|
|
548
|
+
/**
|
|
549
|
+
* Component props, React / Vue / Svelte-style: one parameter that
|
|
550
|
+
* the caller destructures at will, with prop names only visible at
|
|
551
|
+
* the call site. When the parameter is destructured, each bound
|
|
552
|
+
* name becomes its own Input with role equal to the name. When
|
|
553
|
+
* it's not destructured (e.g. `function X(props) {...}`), one
|
|
554
|
+
* Input is emitted with `wholeParamRole` (default `"props"`).
|
|
555
|
+
*
|
|
556
|
+
* Differs from `destructuredObject` in that prop names are not
|
|
557
|
+
* declared by the pack up-front — they are whatever the component
|
|
558
|
+
* author wrote. Differs from `singleObjectParam` in that the
|
|
559
|
+
* destructuring pattern is honored when present.
|
|
560
|
+
*/
|
|
561
|
+
type: "componentProps";
|
|
562
|
+
paramPosition: number;
|
|
563
|
+
/** Role for the single Input when the param is not destructured. Defaults to "props". */
|
|
564
|
+
wholeParamRole?: string;
|
|
565
|
+
} | {
|
|
566
|
+
/**
|
|
567
|
+
* Emit one `Input` per declared parameter, in source order, with
|
|
568
|
+
* `role = param name` (or `defaultRole` when set). Used by the
|
|
569
|
+
* reachable-closure pass for internal library functions — there
|
|
570
|
+
* is no framework-declared role space, so the caller-visible
|
|
571
|
+
* name IS the role. Captures destructured-parameter bindings the
|
|
572
|
+
* same way `destructuredObject` does, so `(ctx, { userId })`
|
|
573
|
+
* reads as two inputs: `ctx` and `userId`.
|
|
574
|
+
*/
|
|
575
|
+
type: "allPositional";
|
|
576
|
+
defaultRole?: string;
|
|
577
|
+
} | {
|
|
578
|
+
/**
|
|
579
|
+
* Decorator-driven parameter mapping (NestJS-style). For each
|
|
580
|
+
* declared parameter, the adapter reads the parameter's first
|
|
581
|
+
* decorator and looks up its name in `decoratorRoleMap` —
|
|
582
|
+
* matched decorators map the parameter to that role; unmatched
|
|
583
|
+
* parameters fall back to `defaultRole` (or skip when unset).
|
|
584
|
+
*
|
|
585
|
+
* For `@nestjs/graphql` resolvers:
|
|
586
|
+
* `{ "Args": "args", "Parent": "parent",
|
|
587
|
+
* "Context": "context", "Info": "info" }`.
|
|
588
|
+
*
|
|
589
|
+
* Decorator gating is by name only — multiple frameworks
|
|
590
|
+
* defining `@Args` would all map. Packs that need to
|
|
591
|
+
* disambiguate by import module add it later when the use case
|
|
592
|
+
* justifies the cost.
|
|
593
|
+
*/
|
|
594
|
+
type: "decoratedParams";
|
|
595
|
+
decoratorRoleMap: Record<string, string>;
|
|
596
|
+
defaultRole?: string;
|
|
597
|
+
};
|
|
598
|
+
/**
|
|
599
|
+
* What a property on the API response object semantically represents.
|
|
600
|
+
* Declared in the pack so the adapter can resolve derived properties
|
|
601
|
+
* (e.g. `.ok` → status range 200–299) at extraction time.
|
|
602
|
+
*/
|
|
603
|
+
type ResponsePropertyMeaning = {
|
|
604
|
+
type: "statusCode";
|
|
605
|
+
} | {
|
|
606
|
+
type: "statusRange";
|
|
607
|
+
min: number;
|
|
608
|
+
max: number;
|
|
609
|
+
} | {
|
|
610
|
+
type: "body";
|
|
611
|
+
} | {
|
|
612
|
+
type: "headers";
|
|
613
|
+
};
|
|
614
|
+
interface ResponsePropertyMapping {
|
|
615
|
+
/** Property or method name on the response (e.g. "ok", "status", "json") */
|
|
616
|
+
name: string;
|
|
617
|
+
/** How this member is accessed: property read or method call */
|
|
618
|
+
access: "property" | "method";
|
|
619
|
+
/** What the value semantically represents */
|
|
620
|
+
semantics: ResponsePropertyMeaning;
|
|
621
|
+
}
|
|
622
|
+
interface PatternPack {
|
|
623
|
+
name: string;
|
|
624
|
+
/**
|
|
625
|
+
* Pack version stamp — feeds the cache invalidation key. Bump on
|
|
626
|
+
* any change that affects discovered units / extracted summaries.
|
|
627
|
+
* Format is opaque to the adapter; semver is the obvious choice
|
|
628
|
+
* but a content hash, build SHA, or monotonic integer works too.
|
|
629
|
+
* Optional — packs that omit it use the literal string `"unset"`,
|
|
630
|
+
* which means cache entries from one process to the next aren't
|
|
631
|
+
* meaningfully versioned (suitable for development; production
|
|
632
|
+
* packs should declare a version).
|
|
633
|
+
*/
|
|
634
|
+
version?: string;
|
|
635
|
+
languages: string[];
|
|
636
|
+
discovery: DiscoveryPattern[];
|
|
637
|
+
terminals: TerminalPattern[];
|
|
638
|
+
contractReading?: ContractPattern;
|
|
639
|
+
inputMapping: InputMappingPattern;
|
|
640
|
+
/**
|
|
641
|
+
* Transport (wire protocol) used in the `BoundaryBinding.transport`
|
|
642
|
+
* of discovered units. Every pack states its transport explicitly
|
|
643
|
+
* rather than leaning on a hardcoded HTTP default — "what transport
|
|
644
|
+
* does this pack cover?" is a question every pack should have to
|
|
645
|
+
* answer, and making the field required keeps future packs (React,
|
|
646
|
+
* GraphQL, Lambda-invoke, queues) from silently inheriting an
|
|
647
|
+
* HTTP-shaped default that doesn't fit.
|
|
648
|
+
*
|
|
649
|
+
* The pack's `name` separately populates `BoundaryBinding.recognition`
|
|
650
|
+
* on produced summaries — so `{ transport, recognition }` come from
|
|
651
|
+
* the pack directly, and `semantics` is derived by the adapter from
|
|
652
|
+
* the discovery pattern's binding-extraction rules.
|
|
653
|
+
*/
|
|
654
|
+
protocol: string;
|
|
655
|
+
/**
|
|
656
|
+
* Semantics of properties on the API response object (consumer side).
|
|
657
|
+
* Tells the adapter how to resolve derived properties like `.ok` or
|
|
658
|
+
* `.json()` to structured IR constructs instead of leaving them opaque.
|
|
659
|
+
*/
|
|
660
|
+
responseSemantics?: ResponsePropertyMapping[];
|
|
661
|
+
/**
|
|
662
|
+
* Synthesize additional code units from a parent unit's body —
|
|
663
|
+
* "one user-authored construct implicitly spawns multiple
|
|
664
|
+
* runtime-scheduled units." Used when a framework's runtime
|
|
665
|
+
* schedules callbacks that aren't visible as top-level declarations:
|
|
666
|
+
* React event handlers on JSX elements, React `useEffect` bodies,
|
|
667
|
+
* Node `emitter.on("event", handler)`, class-component lifecycle
|
|
668
|
+
* methods, and similar.
|
|
669
|
+
*
|
|
670
|
+
* `ctx` is typed `unknown` here because the extractor has no
|
|
671
|
+
* knowledge of which adapter is driving it; each language adapter
|
|
672
|
+
* defines its own context shape (e.g. `TsSubUnitContext` in
|
|
673
|
+
* `@suss/adapter-typescript`) with the primitives packs need to
|
|
674
|
+
* walk the parent's AST. Packs import and cast to the adapter
|
|
675
|
+
* context they're written against — the cast is the explicit
|
|
676
|
+
* "this pack requires the TypeScript adapter" contract.
|
|
677
|
+
*
|
|
678
|
+
* Returned units are fed through the adapter's extraction pipeline
|
|
679
|
+
* the same way top-level discovered units are, so each becomes its
|
|
680
|
+
* own `BehavioralSummary`. Carry per-unit `terminals` and
|
|
681
|
+
* `inputMapping` on the `DiscoveredUnit` if the sub-unit's shape
|
|
682
|
+
* differs from the parent pack's defaults.
|
|
683
|
+
*/
|
|
684
|
+
subUnits?: (parent: DiscoveredSubUnitParent, ctx: unknown) => DiscoveredSubUnit[];
|
|
685
|
+
/**
|
|
686
|
+
* Pack-supplied top-level discovery callback. Sibling of `subUnits`
|
|
687
|
+
* for the discovery layer: when a framework's discovery convention
|
|
688
|
+
* doesn't fit one of the data-driven `DiscoveryMatch` variants (REST
|
|
689
|
+
* registration, decorator-based controllers, named-export shapes,
|
|
690
|
+
* etc.), the pack ships its own walker here. The adapter calls it
|
|
691
|
+
* once per source file alongside the data-driven dispatch.
|
|
692
|
+
*
|
|
693
|
+
* Use this for framework-specific shapes that don't generalize:
|
|
694
|
+
* React's component-export heuristic (PascalCase + JSX-return),
|
|
695
|
+
* Vue's `.vue` SFC slots, Solid's component conventions, Storybook's
|
|
696
|
+
* `.stories.tsx` file convention. These are real conventions but
|
|
697
|
+
* baking each into the central `DiscoveryMatch` union forces every
|
|
698
|
+
* unrelated pack to know about them. Callbacks keep the central
|
|
699
|
+
* union for the generic primitives and let packs own their own
|
|
700
|
+
* conventions.
|
|
701
|
+
*
|
|
702
|
+
* `ctx` is typed `unknown` for the same reason as `subUnits` — each
|
|
703
|
+
* adapter ships its own context primitive (`TsDiscoveryContext` in
|
|
704
|
+
* `@suss/adapter-typescript`) and packs cast to whichever they're
|
|
705
|
+
* written against. The cast is the "I require the TS adapter"
|
|
706
|
+
* contract.
|
|
707
|
+
*
|
|
708
|
+
* Returned units flow through the adapter's normal pipeline — they
|
|
709
|
+
* get terminals + effects extracted, sub-units synthesized, and
|
|
710
|
+
* summaries assembled exactly as units from data-driven discovery
|
|
711
|
+
* do. Per-unit `terminals` / `inputMapping` overrides on
|
|
712
|
+
* `DiscoveredUnit` work the same way.
|
|
713
|
+
*
|
|
714
|
+
* **Cross-pack dedup.** When this callback discovers a unit at the
|
|
715
|
+
* same `(func, kind)` as a unit from another pack's data-driven
|
|
716
|
+
* discovery, the cross-pack claim dedup in the adapter keeps the
|
|
717
|
+
* first claimant. Pack ordering in the framework list is the
|
|
718
|
+
* precedence signal.
|
|
719
|
+
*/
|
|
720
|
+
discoverUnits?: (sourceFile: unknown, ctx: unknown) => DiscoveredCustomUnit[];
|
|
721
|
+
/**
|
|
722
|
+
* Per-call-site recognizers that emit typed `Effect`s alongside the
|
|
723
|
+
* generic `invocation` effect the adapter already captures.
|
|
724
|
+
*
|
|
725
|
+
* **Scope contract.** The adapter walks every CallExpression in
|
|
726
|
+
* the function body and dispatches to every registered recognizer
|
|
727
|
+
* for each call. Walking skips nested function bodies (those are
|
|
728
|
+
* their own units with their own recognizer dispatch). The walk is
|
|
729
|
+
* INDEPENDENT of the existing invocation-effect walker — the
|
|
730
|
+
* existing walker is intentionally narrow (only captures
|
|
731
|
+
* `invocation` effects from bare expression statements + container
|
|
732
|
+
* composition, to avoid double-counting calls that already become
|
|
733
|
+
* terminals). Recognizers don't have that concern; they fire on
|
|
734
|
+
* every call regardless of position, including
|
|
735
|
+
* `const x = await fn(...)` initializers and nested call args
|
|
736
|
+
* (which the invocation walker skips). This independence means
|
|
737
|
+
* recognizer authors can rely on seeing every call in scope.
|
|
738
|
+
*
|
|
739
|
+
* **Cross-pack visibility.** Recognizers fire regardless of which
|
|
740
|
+
* pack discovered the enclosing function — so
|
|
741
|
+
* `@suss/framework-prisma`'s recognizer can fire on Prisma calls
|
|
742
|
+
* inside an `@suss/framework-express` handler. Pack authors don't
|
|
743
|
+
* need to coordinate.
|
|
744
|
+
*
|
|
745
|
+
* **Emission contract.** Returning effects ADDS them to the
|
|
746
|
+
* enclosing default-branch transition; the generic `invocation`
|
|
747
|
+
* effect is preserved either way (typed effects coexist with raw
|
|
748
|
+
* call capture so inspect rendering keeps callee text + args while
|
|
749
|
+
* the checker pairs on the typed shape). Returning `null` / `[]`
|
|
750
|
+
* is the no-match path.
|
|
751
|
+
*
|
|
752
|
+
* **Dedup is the recognizer's responsibility.** The dispatcher
|
|
753
|
+
* doesn't dedupe across calls. A recognizer that wants to fire
|
|
754
|
+
* once per identifier (e.g., to dedupe reads bound to a const
|
|
755
|
+
* that's used N times) tracks its own state across invocations.
|
|
756
|
+
*
|
|
757
|
+
* **Exceptions are caught and logged.** A recognizer that throws
|
|
758
|
+
* is logged to stderr with file path + line number and skipped
|
|
759
|
+
* for that call; the extraction continues. Buggy recognizers
|
|
760
|
+
* don't crash the run.
|
|
761
|
+
*
|
|
762
|
+
* `call` is the language adapter's call-expression handle (opaque
|
|
763
|
+
* here; ts-morph `CallExpression` in `@suss/adapter-typescript`).
|
|
764
|
+
* `ctx` is the adapter's recognizer context (source file, an
|
|
765
|
+
* `extractArgs()` helper that reuses the adapter's own EffectArg
|
|
766
|
+
* builder). Recognizers cast both to the adapter context they're
|
|
767
|
+
* written against — same "this pack requires the TypeScript
|
|
768
|
+
* adapter" contract `subUnits` uses.
|
|
769
|
+
*/
|
|
770
|
+
invocationRecognizers?: InvocationRecognizer[];
|
|
771
|
+
/**
|
|
772
|
+
* Optional pack-level import gate. When set, the adapter's
|
|
773
|
+
* pre-filter only considers this pack applicable to source files
|
|
774
|
+
* whose imports include at least one of the listed modules
|
|
775
|
+
* (prefix match — `"@aws-sdk/client-sqs"` matches that module
|
|
776
|
+
* AND any `"@aws-sdk/client-sqs/sub-path"`).
|
|
777
|
+
*
|
|
778
|
+
* Useful for recognizer-only packs that target a specific library:
|
|
779
|
+
* `@suss/framework-aws-sqs` declares `["@aws-sdk/client-sqs"]`,
|
|
780
|
+
* `@suss/framework-prisma` declares `["@prisma/client"]`. Without
|
|
781
|
+
* a gate, recognizer-only packs walk every file in the project —
|
|
782
|
+
* correct but wasted work on large monorepos where most files
|
|
783
|
+
* don't import the library.
|
|
784
|
+
*
|
|
785
|
+
* Discovery-pattern packs already have per-pattern `requiresImport`
|
|
786
|
+
* on `DiscoveryPattern`; this is the pack-level equivalent for
|
|
787
|
+
* packs whose ONLY mechanism is recognizers (no discovery).
|
|
788
|
+
*
|
|
789
|
+
* Empty / undefined means "no gate" — pack walks every file (the
|
|
790
|
+
* default for truly universal recognizers like `@suss/runtime-node`'s
|
|
791
|
+
* process-surface / env-var recognizers, since `process.*` is
|
|
792
|
+
* available without an import).
|
|
793
|
+
*/
|
|
794
|
+
requiresImport?: string[];
|
|
795
|
+
/**
|
|
796
|
+
* Per-property-access recognizers — sister to
|
|
797
|
+
* `invocationRecognizers` but firing on `PropertyAccessExpression`
|
|
798
|
+
* nodes rather than `CallExpression` nodes. Use these for patterns
|
|
799
|
+
* that read a value through property access without invoking it:
|
|
800
|
+
* `process.env.X` env-var reads, `Date.now()`-style time reads
|
|
801
|
+
* (which is actually a call, see invocationRecognizers), bare
|
|
802
|
+
* `module.constant` reads, etc.
|
|
803
|
+
*
|
|
804
|
+
* Same scope rules as invocationRecognizers — fires on every
|
|
805
|
+
* PropertyAccessExpression in the function body, skipping nested
|
|
806
|
+
* function bodies. Same emission contract: returned effects land
|
|
807
|
+
* on the enclosing default-branch transition.
|
|
808
|
+
*
|
|
809
|
+
* Same reasoning as invocationRecognizers for the opaque-arg /
|
|
810
|
+
* adapter-narrows pattern.
|
|
811
|
+
*/
|
|
812
|
+
accessRecognizers?: AccessRecognizer[];
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Per-call-site recognizer hook. See `PatternPack.invocationRecognizers`
|
|
816
|
+
* for the contract and threading model.
|
|
817
|
+
*/
|
|
818
|
+
type InvocationRecognizer<TCtx = unknown> = (call: unknown, ctx: TCtx) => Effect[] | null;
|
|
819
|
+
/**
|
|
820
|
+
* Per-property-access recognizer hook. See
|
|
821
|
+
* `PatternPack.accessRecognizers` for the contract and threading model.
|
|
822
|
+
*/
|
|
823
|
+
type AccessRecognizer<TCtx = unknown> = (access: unknown, ctx: TCtx) => Effect[] | null;
|
|
824
|
+
/**
|
|
825
|
+
* Minimal handle-shaped description of the parent code unit that
|
|
826
|
+
* `subUnits` operates within. `func` is left opaque here — the
|
|
827
|
+
* language adapter brands its own FunctionRoot type. This interface
|
|
828
|
+
* lives in the extractor only so `PatternPack` can name it;
|
|
829
|
+
* adapter-level context types (`TsSubUnitContext`) narrow `func` to
|
|
830
|
+
* a concrete AST handle.
|
|
831
|
+
*/
|
|
832
|
+
interface DiscoveredSubUnitParent {
|
|
833
|
+
/** Handle to the parent's function body. Opaque at extractor level. */
|
|
834
|
+
func: unknown;
|
|
835
|
+
/** Discovered name of the parent (e.g. "Counter"). */
|
|
836
|
+
name: string;
|
|
837
|
+
/** Kind of the parent (usually "component", "handler", etc.). */
|
|
838
|
+
kind: string;
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* What a pack's `discoverUnits` hook returns per discovered top-level
|
|
842
|
+
* unit. Mirrors `DiscoveredSubUnit` for the discovery layer — the
|
|
843
|
+
* adapter widens these into its own internal `DiscoveredUnit` type
|
|
844
|
+
* (which carries adapter-specific fields like `routeInfo`,
|
|
845
|
+
* `packageExportInfo`, etc.) and pipes them through the normal
|
|
846
|
+
* extraction pipeline.
|
|
847
|
+
*
|
|
848
|
+
* Pack authors stay in opaque-handle land — `func` is whatever the
|
|
849
|
+
* adapter's primitive returns. Adapter-narrows this to its concrete
|
|
850
|
+
* function-root type (e.g. `FunctionRoot` in
|
|
851
|
+
* `@suss/adapter-typescript`).
|
|
852
|
+
*/
|
|
853
|
+
interface DiscoveredCustomUnit {
|
|
854
|
+
/** Function body handle, opaque here. */
|
|
855
|
+
func: unknown;
|
|
856
|
+
/** IR code-unit kind (e.g. "component", "handler"). */
|
|
857
|
+
kind: string;
|
|
858
|
+
/** Discovered name (e.g. "UserCard"). */
|
|
859
|
+
name: string;
|
|
860
|
+
/**
|
|
861
|
+
* Terminal patterns to extract from this unit's body. Defaults to
|
|
862
|
+
* the pack-level `terminals` when unset.
|
|
863
|
+
*/
|
|
864
|
+
terminals?: TerminalPattern[];
|
|
865
|
+
/**
|
|
866
|
+
* Input mapping for this unit. Defaults to the pack-level
|
|
867
|
+
* `inputMapping` when unset.
|
|
868
|
+
*/
|
|
869
|
+
inputMapping?: InputMappingPattern;
|
|
870
|
+
/**
|
|
871
|
+
* REST route identity for units a callback discovers against an
|
|
872
|
+
* external manifest (a SAM/CFN template's `Events` block, an infra
|
|
873
|
+
* routing declaration, etc.) rather than an in-code registration.
|
|
874
|
+
* When set, the adapter builds a `rest` binding from `(method, path)`
|
|
875
|
+
* — the same binding NestJS controllers get via decorator-derived
|
|
876
|
+
* `routeInfo`, without the discoverUnits callback needing to reach
|
|
877
|
+
* into the adapter's binding machinery.
|
|
878
|
+
*
|
|
879
|
+
* One function bound to several routes emits one DiscoveredCustomUnit
|
|
880
|
+
* per route; the adapter's per-file claim dedup keys on
|
|
881
|
+
* `(func, kind, method, path)` so the variants survive.
|
|
882
|
+
*/
|
|
883
|
+
routeInfo?: {
|
|
884
|
+
method: string;
|
|
885
|
+
path: string;
|
|
886
|
+
};
|
|
887
|
+
/**
|
|
888
|
+
* Metadata merged onto the resulting summary's `metadata` field.
|
|
889
|
+
*/
|
|
890
|
+
metadata?: Record<string, unknown>;
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* What a pack's `subUnits` hook returns per synthesized child. The
|
|
894
|
+
* adapter pipes each of these through the same extraction + assembly
|
|
895
|
+
* pipeline used for top-level-discovered units.
|
|
896
|
+
*/
|
|
897
|
+
interface DiscoveredSubUnit {
|
|
898
|
+
/** Function body handle, opaque here. */
|
|
899
|
+
func: unknown;
|
|
900
|
+
/** IR code-unit kind (e.g. "handler"). */
|
|
901
|
+
kind: string;
|
|
902
|
+
/** Qualified name (e.g. "Counter.button.onClick"). */
|
|
903
|
+
name: string;
|
|
904
|
+
/**
|
|
905
|
+
* Terminal patterns to extract from this sub-unit's body. Defaults
|
|
906
|
+
* to `return` + `throw` when unset — fits handlers / effects cleanly.
|
|
907
|
+
*/
|
|
908
|
+
terminals?: TerminalPattern[];
|
|
909
|
+
/**
|
|
910
|
+
* Input mapping for this sub-unit. Defaults to an empty positional
|
|
911
|
+
* mapping when unset — event handlers with one arg should pass
|
|
912
|
+
* `{ type: "positionalParams", params: [{ position: 0, role: "event" }] }`.
|
|
913
|
+
*/
|
|
914
|
+
inputMapping?: InputMappingPattern;
|
|
915
|
+
/**
|
|
916
|
+
* Metadata merged onto the resulting summary's `metadata` field.
|
|
917
|
+
* Packs use this to stamp provenance (`metadata.react = { kind: "handler", ... }`).
|
|
918
|
+
*/
|
|
919
|
+
metadata?: Record<string, unknown>;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Build the `discovery` entries for an HTTP-server framework whose
|
|
924
|
+
* handlers are registered via `app.get(path, handler)` /
|
|
925
|
+
* `router.post(...)` / etc.
|
|
926
|
+
*
|
|
927
|
+
* Each `importNames` entry produces one DiscoveryPattern — libraries
|
|
928
|
+
* typically expose both a default export and a named export that both
|
|
929
|
+
* produce the routable instance (e.g. Express has `express()` and
|
|
930
|
+
* `Router()`; Fastify has `fastify()` and `Fastify`). The binding
|
|
931
|
+
* extraction (method from registration, path from position 0) is
|
|
932
|
+
* identical across every HTTP server framework we support.
|
|
933
|
+
*
|
|
934
|
+
* Callers still supply the `methods` list directly: different
|
|
935
|
+
* frameworks support different HTTP verbs (Fastify includes `.head` /
|
|
936
|
+
* `.options`, Express historically does not by default).
|
|
937
|
+
*
|
|
938
|
+
* @example
|
|
939
|
+
* discovery: httpRouteDiscovery({
|
|
940
|
+
* importModule: "express",
|
|
941
|
+
* importNames: ["Router", "express"],
|
|
942
|
+
* methods: [".get", ".post", ".put", ".delete", ".patch"],
|
|
943
|
+
* })
|
|
944
|
+
*/
|
|
945
|
+
declare function httpRouteDiscovery(opts: {
|
|
946
|
+
importModule: string;
|
|
947
|
+
importNames: readonly string[];
|
|
948
|
+
methods: readonly string[];
|
|
949
|
+
/** Defaults to "handler". Override for packs that want a different kind. */
|
|
950
|
+
kind?: string;
|
|
951
|
+
}): DiscoveryPattern[];
|
|
952
|
+
|
|
953
|
+
interface RawParameter {
|
|
954
|
+
name: string;
|
|
955
|
+
position: number;
|
|
956
|
+
role: string;
|
|
957
|
+
typeText: string | null;
|
|
958
|
+
}
|
|
959
|
+
interface RawCondition {
|
|
960
|
+
sourceText: string;
|
|
961
|
+
structured: Predicate | null;
|
|
962
|
+
polarity: "positive" | "negative";
|
|
963
|
+
source: "explicit" | "earlyReturn" | "earlyThrow" | "catchBlock";
|
|
964
|
+
}
|
|
965
|
+
interface RawTerminal {
|
|
966
|
+
kind: "response" | "throw" | "return" | "render" | "delegate" | "emit" | "void";
|
|
967
|
+
statusCode: {
|
|
968
|
+
type: "literal";
|
|
969
|
+
value: number;
|
|
970
|
+
} | {
|
|
971
|
+
type: "dynamic";
|
|
972
|
+
sourceText: string;
|
|
973
|
+
} | null;
|
|
974
|
+
body: {
|
|
975
|
+
typeText: string | null;
|
|
976
|
+
shape: TypeShape | null;
|
|
977
|
+
} | null;
|
|
978
|
+
exceptionType: string | null;
|
|
979
|
+
message: string | null;
|
|
980
|
+
/** For render terminals: the component being rendered */
|
|
981
|
+
component: string | null;
|
|
982
|
+
/**
|
|
983
|
+
* For render terminals (JSX / component-tree output): the nested
|
|
984
|
+
* render tree if the pack's extractor can produce one. Flows
|
|
985
|
+
* through to `Output.render.root`. Absent when the extractor sees
|
|
986
|
+
* only the root element name or opted out of tree extraction.
|
|
987
|
+
*/
|
|
988
|
+
renderTree: RenderNode | null;
|
|
989
|
+
/** For delegate terminals: where control is passed */
|
|
990
|
+
delegateTarget: string | null;
|
|
991
|
+
/** For emit terminals: the event/channel name */
|
|
992
|
+
emitEvent: string | null;
|
|
993
|
+
location: {
|
|
994
|
+
start: number;
|
|
995
|
+
end: number;
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* A structured capture of an invocation argument. Literal values
|
|
1000
|
+
* (string, number, boolean), object/array literals, template literals,
|
|
1001
|
+
* identifier references, and nested call expressions are all captured
|
|
1002
|
+
* as structured variants so readers can see *what* was passed even when
|
|
1003
|
+
* the runtime value isn't statically resolvable. `null` is reserved for
|
|
1004
|
+
* the rare case where the argument shape doesn't match any variant
|
|
1005
|
+
* (type assertions with computed operands, arithmetic, etc.) — the
|
|
1006
|
+
* caller still knows how many arguments the call had.
|
|
1007
|
+
*
|
|
1008
|
+
* Useful for recognising literal-string discriminators like
|
|
1009
|
+
* `findings.push({ kind: "scenarioCoverageGap" })` or
|
|
1010
|
+
* `dispatch({ type: "USER_LOGGED_IN" })` at the summary level, and for
|
|
1011
|
+
* preserving argument *shape* (`{ userId, count }`, `getUser(id)`) so
|
|
1012
|
+
* downstream consumers (AI agents, error-taxonomy tooling,
|
|
1013
|
+
* release-note generators) can reason about composition without
|
|
1014
|
+
* re-reading source.
|
|
1015
|
+
*/
|
|
1016
|
+
type EffectArg = {
|
|
1017
|
+
kind: "string";
|
|
1018
|
+
value: string;
|
|
1019
|
+
} | {
|
|
1020
|
+
kind: "number";
|
|
1021
|
+
value: number;
|
|
1022
|
+
} | {
|
|
1023
|
+
kind: "boolean";
|
|
1024
|
+
value: boolean;
|
|
1025
|
+
} | {
|
|
1026
|
+
kind: "object";
|
|
1027
|
+
fields: Record<string, EffectArg>;
|
|
1028
|
+
} | {
|
|
1029
|
+
kind: "array";
|
|
1030
|
+
items: EffectArg[];
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Template literal with interpolation (`` `Error: ${e.message}` ``).
|
|
1034
|
+
* The resolved runtime value is unknown, but the source text is
|
|
1035
|
+
* preserved so readers can see which variables compose it — useful
|
|
1036
|
+
* for log messages, computed keys, and dedup-style key builders.
|
|
1037
|
+
*/
|
|
1038
|
+
| {
|
|
1039
|
+
kind: "template";
|
|
1040
|
+
sourceText: string;
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Identifier reference — a bare variable (`userId`), a property-access
|
|
1044
|
+
* chain (`user.profile.email`, `process.env.DATABASE_URL`), or an
|
|
1045
|
+
* element-access chain (`config["host"]`). `name` holds the full
|
|
1046
|
+
* source text so readers can tell which binding flowed into the call
|
|
1047
|
+
* without inferring it from context. Identifiers that resolve to a
|
|
1048
|
+
* module-level `const` with a simple initializer (literal, process.env
|
|
1049
|
+
* access, etc.) are inlined to the initializer's EffectArg form so
|
|
1050
|
+
* closure-over-constants doesn't hide the actual value at call sites.
|
|
1051
|
+
*/
|
|
1052
|
+
| {
|
|
1053
|
+
kind: "identifier";
|
|
1054
|
+
name: string;
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Nested call expression passed as an argument — `log(formatError(e))`,
|
|
1058
|
+
* `enqueue(buildPayload(ctx))`, inline composition. `callee` is the
|
|
1059
|
+
* source text of the call target; `args` recurses with bounded depth,
|
|
1060
|
+
* so `formatError(e)` reads as `{ callee: "formatError", args: [...] }`
|
|
1061
|
+
* rather than an opaque null.
|
|
1062
|
+
*/
|
|
1063
|
+
| {
|
|
1064
|
+
kind: "call";
|
|
1065
|
+
callee: string;
|
|
1066
|
+
args: EffectArg[];
|
|
1067
|
+
} | null;
|
|
1068
|
+
type RawEffect = {
|
|
1069
|
+
type: "mutation";
|
|
1070
|
+
target: string;
|
|
1071
|
+
operation: "create" | "update" | "delete";
|
|
1072
|
+
} | {
|
|
1073
|
+
type: "invocation";
|
|
1074
|
+
callee: string;
|
|
1075
|
+
args: EffectArg[];
|
|
1076
|
+
async: boolean;
|
|
1077
|
+
/**
|
|
1078
|
+
* Ancestor if/switch/ternary conditions that gate reaching this
|
|
1079
|
+
* call. Populated for calls nested inside conditional blocks or
|
|
1080
|
+
* loop bodies; empty for top-level (always-fires) calls.
|
|
1081
|
+
* Converts to `Effect.preconditions: Predicate[]` in the IR —
|
|
1082
|
+
* same RawCondition → Predicate pipeline transitions use.
|
|
1083
|
+
*/
|
|
1084
|
+
preconditions?: RawCondition[];
|
|
1085
|
+
} | {
|
|
1086
|
+
type: "emission";
|
|
1087
|
+
event: string;
|
|
1088
|
+
} | {
|
|
1089
|
+
type: "stateChange";
|
|
1090
|
+
variable: string;
|
|
1091
|
+
};
|
|
1092
|
+
interface RawBranch {
|
|
1093
|
+
conditions: RawCondition[];
|
|
1094
|
+
terminal: RawTerminal;
|
|
1095
|
+
effects: RawEffect[];
|
|
1096
|
+
/**
|
|
1097
|
+
* Pre-typed `Effect`s emitted by `PatternPack.invocationRecognizers`.
|
|
1098
|
+
* Bypass the `RawEffect → Effect` conversion that `effects` runs
|
|
1099
|
+
* through — recognizers have full structural knowledge of what
|
|
1100
|
+
* they're emitting (e.g. `interaction(class: "storage-access")` with
|
|
1101
|
+
* binding identity + per-class fields/selector)
|
|
1102
|
+
* and there's no narrower extractor-side representation worth
|
|
1103
|
+
* round-tripping through. Concatenated with converted `effects` at
|
|
1104
|
+
* `assembleSummary` time.
|
|
1105
|
+
*/
|
|
1106
|
+
extraEffects?: Effect[];
|
|
1107
|
+
location: {
|
|
1108
|
+
start: number;
|
|
1109
|
+
end: number;
|
|
1110
|
+
};
|
|
1111
|
+
isDefault: boolean;
|
|
1112
|
+
/**
|
|
1113
|
+
* Shape of upstream data the code unit reads within this branch.
|
|
1114
|
+
* Populated by the adapter for client/consumer code units: after
|
|
1115
|
+
* branching on a response status, the consumer accesses fields on
|
|
1116
|
+
* the response body — those accesses are collected into a TypeShape.
|
|
1117
|
+
* The extractor copies this through to Transition.expectedInput.
|
|
1118
|
+
*/
|
|
1119
|
+
expectedInput?: TypeShape | null;
|
|
1120
|
+
}
|
|
1121
|
+
interface RawDependencyCall {
|
|
1122
|
+
name: string;
|
|
1123
|
+
assignedTo: string | null;
|
|
1124
|
+
async: boolean;
|
|
1125
|
+
returnType: string | null;
|
|
1126
|
+
location: {
|
|
1127
|
+
start: number;
|
|
1128
|
+
end: number;
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
interface RawDeclaredContract {
|
|
1132
|
+
framework: string;
|
|
1133
|
+
responses: Array<{
|
|
1134
|
+
statusCode: number;
|
|
1135
|
+
/**
|
|
1136
|
+
* Structured body schema the contract declares for this status code.
|
|
1137
|
+
* Populated when the adapter can statically resolve the response schema
|
|
1138
|
+
* (e.g. `c.type<T>()`'s type argument). Null when the contract exists
|
|
1139
|
+
* but no body was declared or the schema form isn't yet supported.
|
|
1140
|
+
*/
|
|
1141
|
+
body?: TypeShape | null;
|
|
1142
|
+
}>;
|
|
1143
|
+
params?: Record<string, {
|
|
1144
|
+
type: string;
|
|
1145
|
+
required: boolean;
|
|
1146
|
+
}>;
|
|
1147
|
+
/**
|
|
1148
|
+
* Relationship between this declared contract and the `transitions[]`
|
|
1149
|
+
* on the same summary:
|
|
1150
|
+
*
|
|
1151
|
+
* - "derived": both are extracted from the same source data, so
|
|
1152
|
+
* comparing them against each other is tautological. Example: an
|
|
1153
|
+
* OpenAPI stub's contract and its transitions both come from the
|
|
1154
|
+
* operation's `responses` block. The cross-boundary checker
|
|
1155
|
+
* skips per-summary contract-consistency for these.
|
|
1156
|
+
*
|
|
1157
|
+
* - "independent": the contract is a separate statement from the
|
|
1158
|
+
* transitions. Example: a ts-rest handler whose router declares
|
|
1159
|
+
* `responses` and whose implementation is a separate function;
|
|
1160
|
+
* a CFN stub whose `MethodResponses` and integration config are
|
|
1161
|
+
* independent template fields. Contract-consistency comparison
|
|
1162
|
+
* is meaningful.
|
|
1163
|
+
*
|
|
1164
|
+
* Defaults to "independent" when a pack doesn't say — safer to
|
|
1165
|
+
* surface a spurious-but-investigable finding than to silently drop
|
|
1166
|
+
* a real one.
|
|
1167
|
+
*/
|
|
1168
|
+
provenance?: "derived" | "independent";
|
|
1169
|
+
}
|
|
1170
|
+
interface RawCodeStructure {
|
|
1171
|
+
identity: {
|
|
1172
|
+
name: string;
|
|
1173
|
+
kind: CodeUnitKind;
|
|
1174
|
+
file: string;
|
|
1175
|
+
range: {
|
|
1176
|
+
start: number;
|
|
1177
|
+
end: number;
|
|
1178
|
+
};
|
|
1179
|
+
exportName: string | null;
|
|
1180
|
+
exportPath: string[] | null;
|
|
1181
|
+
};
|
|
1182
|
+
/**
|
|
1183
|
+
* Three-layer boundary description (`transport`, `semantics`,
|
|
1184
|
+
* `recognition`). See `@suss/behavioral-ir`'s `BoundaryBinding`.
|
|
1185
|
+
* Null when the unit participates in no cross-unit boundary —
|
|
1186
|
+
* helpers, pure utilities, and anything the adapter can't place.
|
|
1187
|
+
*/
|
|
1188
|
+
boundaryBinding: BoundaryBinding | null;
|
|
1189
|
+
parameters: RawParameter[];
|
|
1190
|
+
branches: RawBranch[];
|
|
1191
|
+
dependencyCalls: RawDependencyCall[];
|
|
1192
|
+
declaredContract: RawDeclaredContract | null;
|
|
1193
|
+
/**
|
|
1194
|
+
* Names of pack-declared response properties whose semantics is `body`.
|
|
1195
|
+
* For client units this records the accessor a consumer uses to reach the
|
|
1196
|
+
* body (e.g. ["data"] for axios, ["body","json","text"] for fetch). Carried
|
|
1197
|
+
* through to `summary.metadata.http.bodyAccessors` so the cross-boundary
|
|
1198
|
+
* checker can unwrap consumer expectedInput correctly without knowing each
|
|
1199
|
+
* pack. Flat here because this is the adapter→extractor plumbing contract;
|
|
1200
|
+
* nesting under `http` happens when the summary is assembled.
|
|
1201
|
+
*/
|
|
1202
|
+
bodyAccessors?: string[];
|
|
1203
|
+
/**
|
|
1204
|
+
* Names of pack-declared response properties whose semantics is
|
|
1205
|
+
* `statusCode`. Same shape as `bodyAccessors` but for the status side —
|
|
1206
|
+
* lets the checker recognise pack-specific names (e.g. fetch's `status`,
|
|
1207
|
+
* axios's `status`, hypothetical `responseStatus`) when matching consumer
|
|
1208
|
+
* branch conditions to provider transitions. Carried through to
|
|
1209
|
+
* `summary.metadata.http.statusAccessors`.
|
|
1210
|
+
*/
|
|
1211
|
+
statusAccessors?: string[];
|
|
1212
|
+
/**
|
|
1213
|
+
* Raw GraphQL document source attached to consumer-side graphql
|
|
1214
|
+
* summaries (the inner text of a `useQuery(gql\`...\`)` call).
|
|
1215
|
+
* Assembled into `summary.metadata.graphql.document` for the
|
|
1216
|
+
* checker's pairing layer to parse. Deliberately untouched here —
|
|
1217
|
+
* the extractor has no dependency on graphql-js; parsing happens
|
|
1218
|
+
* at check time.
|
|
1219
|
+
*/
|
|
1220
|
+
graphqlDocument?: string;
|
|
1221
|
+
/**
|
|
1222
|
+
* Raw GraphQL schema SDL attached to provider-side resolver
|
|
1223
|
+
* summaries. For schema-first stubs (AppSync), the SDL IS the
|
|
1224
|
+
* schema. For code-first frameworks (Apollo Server), the SDL
|
|
1225
|
+
* comes from the `typeDefs` config option when statically
|
|
1226
|
+
* resolvable. Surfaced as `summary.metadata.graphql.schemaSdl`
|
|
1227
|
+
* so the checker can use the return-type field set when pairing
|
|
1228
|
+
* nested consumer selections.
|
|
1229
|
+
*/
|
|
1230
|
+
graphqlSchemaSdl?: string;
|
|
1231
|
+
/**
|
|
1232
|
+
* Declared contract derived from the SDL field for this resolver:
|
|
1233
|
+
* `{ returnType, args, provenance, framework }`. Surfaced as
|
|
1234
|
+
* `summary.metadata.graphql.declaredContract` so
|
|
1235
|
+
* `checkGraphqlContractAgreement` can pair it against other
|
|
1236
|
+
* sources declaring a contract for the same boundary.
|
|
1237
|
+
*
|
|
1238
|
+
* The extractor doesn't derive this — adapters that have the SDL
|
|
1239
|
+
* AND know the (typeName, fieldName) of the resolver populate it.
|
|
1240
|
+
* Today: the TS adapter does this for Apollo-style resolverMap
|
|
1241
|
+
* discovery. NestJS GraphQL (decorator-based) is a follow-up.
|
|
1242
|
+
*/
|
|
1243
|
+
graphqlDeclaredContract?: Record<string, unknown>;
|
|
1244
|
+
/**
|
|
1245
|
+
* Set on a consumer-side GraphQL summary whose document reference was
|
|
1246
|
+
* recognized (an imported `TypedDocumentNode` from graphql-codegen,
|
|
1247
|
+
* say) but whose body couldn't be read statically. Surfaced as
|
|
1248
|
+
* `summary.metadata.graphql.unresolvedDocument` so the unreadable
|
|
1249
|
+
* document is accounted for rather than silently dropped. `reference`
|
|
1250
|
+
* is the identifier passed to the hook / imperative call; `reason`
|
|
1251
|
+
* explains what defeated static resolution.
|
|
1252
|
+
*/
|
|
1253
|
+
graphqlUnresolvedDocument?: {
|
|
1254
|
+
reference: string;
|
|
1255
|
+
reason: string;
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
interface ExtractorOptions {
|
|
1259
|
+
gapHandling: "strict" | "permissive" | "silent";
|
|
1260
|
+
}
|
|
1261
|
+
declare function makeTransitionId(functionName: string, branch: RawBranch): string;
|
|
1262
|
+
declare function assembleSummary(raw: RawCodeStructure, options?: ExtractorOptions): BehavioralSummary;
|
|
1263
|
+
declare function detectGaps(raw: RawCodeStructure, transitions: Transition[], options: ExtractorOptions): Gap[];
|
|
1264
|
+
declare function assessConfidence(raw: RawCodeStructure): ConfidenceInfo;
|
|
1265
|
+
declare function terminalToOutput(terminal: RawTerminal): Output;
|
|
1266
|
+
declare function effectToIR(effect: RawEffect): Effect;
|
|
1267
|
+
declare function paramToInput(param: RawParameter): Input;
|
|
1268
|
+
|
|
1269
|
+
export { type AccessRecognizer, type BindingExtraction, 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, assembleSummary, assessConfidence, detectGaps, effectToIR, httpRouteDiscovery, makeTransitionId, paramToInput, terminalToOutput };
|