@suss/adapter-typescript 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 +29 -0
- package/dist/index.cjs +8685 -0
- package/dist/index.d.cts +537 -0
- package/dist/index.d.ts +537 -0
- package/dist/index.js +8678 -0
- package/package.json +63 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
import { FunctionDeclaration, FunctionExpression, ArrowFunction, MethodDeclaration, Node, CallExpression, SourceFile, Project, Expression } from 'ts-morph';
|
|
2
|
+
import { RawCondition, DiscoveryPattern, PatternPack, ExtractorOptions, InvocationRecognizer, AccessRecognizer, RawCodeStructure, TerminalPattern, RawBranch, RawDeclaredContract, ContractPattern, ResponsePropertyMapping, RawTerminal } from '@suss/extractor';
|
|
3
|
+
import { BehavioralSummary, BoundaryBinding, Predicate, TypeShape, ValueRef } from '@suss/behavioral-ir';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Reported by `lookup`. Surfaces what the cache decided and, for
|
|
7
|
+
* partial-hits, the file-churn breakdown so callers can render a
|
|
8
|
+
* diagnostic.
|
|
9
|
+
*/
|
|
10
|
+
interface CacheDiagnostic {
|
|
11
|
+
kind: "hit" | "partial-hit" | "miss";
|
|
12
|
+
/** Reason the lookup missed (only set when kind === "miss"). */
|
|
13
|
+
missReason?: "no-manifest" | "schema-mismatch" | "packs-changed" | "tsconfig-changed";
|
|
14
|
+
/**
|
|
15
|
+
* Counts describing the file churn that produced a partial hit.
|
|
16
|
+
* Only populated when kind === "partial-hit".
|
|
17
|
+
*/
|
|
18
|
+
partial?: {
|
|
19
|
+
/** Cached summaries whose location.file is in the unchanged set. */
|
|
20
|
+
reusedSummaries: number;
|
|
21
|
+
/** Files the adapter needs to re-extract per-file from. */
|
|
22
|
+
filesToReExtract: number;
|
|
23
|
+
/** Files added to the include set since the cached run. */
|
|
24
|
+
addedFiles: number;
|
|
25
|
+
/** Files removed from the include set since the cached run. */
|
|
26
|
+
removedFiles: number;
|
|
27
|
+
/** Files whose mtime/size differs (excludes added/removed). */
|
|
28
|
+
changedFiles: number;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Result of a cache lookup. The adapter dispatches on `kind`:
|
|
33
|
+
*
|
|
34
|
+
* - "hit" — the entire summary set is valid; return it
|
|
35
|
+
* verbatim, skip extraction.
|
|
36
|
+
* - "partial-hit" — `kept` is correct as far as their location.file
|
|
37
|
+
* went; re-extract `filesToExtract` per-file and
|
|
38
|
+
* re-run the closure / rethrow passes over the
|
|
39
|
+
* merged set.
|
|
40
|
+
* - "miss" — manifest absent or coarse keys (schema, packs,
|
|
41
|
+
* tsconfig) changed; full re-extract.
|
|
42
|
+
*/
|
|
43
|
+
type CacheLookup = {
|
|
44
|
+
kind: "hit";
|
|
45
|
+
summaries: BehavioralSummary[];
|
|
46
|
+
diagnostic: CacheDiagnostic;
|
|
47
|
+
} | {
|
|
48
|
+
kind: "partial-hit";
|
|
49
|
+
kept: BehavioralSummary[];
|
|
50
|
+
filesToExtract: string[];
|
|
51
|
+
diagnostic: CacheDiagnostic;
|
|
52
|
+
} | {
|
|
53
|
+
kind: "miss";
|
|
54
|
+
diagnostic: CacheDiagnostic;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** One pack's path through the funnel. */
|
|
58
|
+
interface PackFunnel {
|
|
59
|
+
pack: string;
|
|
60
|
+
/**
|
|
61
|
+
* Import specifiers that make a file relevant to this pack. Empty
|
|
62
|
+
* for an ungated pack, which walks every file.
|
|
63
|
+
*/
|
|
64
|
+
gates: string[];
|
|
65
|
+
/**
|
|
66
|
+
* Gate specifiers that do not resolve from the tsconfig. Non-empty
|
|
67
|
+
* here usually means the target project's dependencies are not
|
|
68
|
+
* installed, which stops symbol-resolution packs while leaving
|
|
69
|
+
* textual-gate packs working, so the run half-succeeds in a way
|
|
70
|
+
* nothing else surfaces.
|
|
71
|
+
*/
|
|
72
|
+
unresolvedGates: string[];
|
|
73
|
+
/** Files the pre-filter selected for this pack. */
|
|
74
|
+
candidateFiles: number;
|
|
75
|
+
/** Units discovered across those files. */
|
|
76
|
+
unitsDiscovered: number;
|
|
77
|
+
/** Summaries built from those units. */
|
|
78
|
+
summariesProduced: number;
|
|
79
|
+
}
|
|
80
|
+
interface ExtractionReport {
|
|
81
|
+
/** Files in the tsconfig include set, or null when the caller supplied a Project. */
|
|
82
|
+
filesInProject: number | null;
|
|
83
|
+
/** Files the bootstrap loaded and the extract walked. */
|
|
84
|
+
filesWalked: number;
|
|
85
|
+
packs: PackFunnel[];
|
|
86
|
+
summaries: number;
|
|
87
|
+
/**
|
|
88
|
+
* The first stage whose count was zero, when the run produced
|
|
89
|
+
* nothing. Null when the run produced summaries.
|
|
90
|
+
*/
|
|
91
|
+
emptyStage: EmptyStage | null;
|
|
92
|
+
}
|
|
93
|
+
type EmptyStage = "tsconfig" | "gateResolution" | "candidateFiles" | "discovery" | "assembly";
|
|
94
|
+
|
|
95
|
+
type FunctionRoot = FunctionDeclaration | FunctionExpression | ArrowFunction | MethodDeclaration;
|
|
96
|
+
/**
|
|
97
|
+
* Public API — returns RawCondition[] with structured: null.
|
|
98
|
+
* Use collectAncestorConditionInfos when you need the Expression nodes.
|
|
99
|
+
*/
|
|
100
|
+
declare function collectAncestorBranches(terminalNode: Node, functionRoot: FunctionRoot): RawCondition[];
|
|
101
|
+
/**
|
|
102
|
+
* Public API — returns RawCondition[] with structured: null.
|
|
103
|
+
* Use collectEarlyReturnConditionInfos when you need the Expression nodes.
|
|
104
|
+
*/
|
|
105
|
+
declare function collectEarlyReturns(terminalNode: Node, functionRoot: FunctionRoot): RawCondition[];
|
|
106
|
+
|
|
107
|
+
interface ClientCallSite {
|
|
108
|
+
callExpression: CallExpression;
|
|
109
|
+
/** Method name on the client object (e.g. "getUser"), null for bare calls like fetch() */
|
|
110
|
+
methodName: string | null;
|
|
111
|
+
}
|
|
112
|
+
interface DiscoveredUnit {
|
|
113
|
+
func: FunctionRoot;
|
|
114
|
+
kind: string;
|
|
115
|
+
name: string;
|
|
116
|
+
callSite?: ClientCallSite;
|
|
117
|
+
/** The discovery pattern that produced this unit. Set by discoverUnits. */
|
|
118
|
+
pattern?: DiscoveryPattern;
|
|
119
|
+
/**
|
|
120
|
+
* Populated by `resolverMap`-style discovery (GraphQL code-first).
|
|
121
|
+
* The adapter uses it to build a `graphql-resolver` binding directly,
|
|
122
|
+
* without running this through the REST-shaped bindingExtraction
|
|
123
|
+
* path.
|
|
124
|
+
*
|
|
125
|
+
* `schemaSdl` is the typeDefs string captured from the same
|
|
126
|
+
* ApolloServer config object the resolver map came from, when
|
|
127
|
+
* we can statically resolve it (string literal or gql-tagged
|
|
128
|
+
* template, inline or const-bound). Surfaced on the summary so
|
|
129
|
+
* the checker's nested-selection pairing can look up return-type
|
|
130
|
+
* fields without needing a separate schema provenance.
|
|
131
|
+
*/
|
|
132
|
+
resolverInfo?: {
|
|
133
|
+
typeName: string;
|
|
134
|
+
fieldName: string;
|
|
135
|
+
schemaSdl?: string;
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Populated by `graphqlHookCall` discovery (GraphQL consumer side).
|
|
139
|
+
* Carries the operation shape the adapter uses to build a
|
|
140
|
+
* `graphql-operation` binding. `operationName` is absent for
|
|
141
|
+
* anonymous operations (`gql\`query { ... }\``, no identifier).
|
|
142
|
+
*
|
|
143
|
+
* `document` is the raw GraphQL document source (the inner text of
|
|
144
|
+
* the gql-tagged template literal, backticks stripped). Kept
|
|
145
|
+
* alongside the parsed shape so downstream tools can re-parse if
|
|
146
|
+
* they need additional detail beyond what we surface here. Absent
|
|
147
|
+
* when the document body wasn't statically readable and the header
|
|
148
|
+
* was recovered from `TypedDocumentNode` type arguments — a
|
|
149
|
+
* cross-module codegen reference to a document produced by a helper.
|
|
150
|
+
*
|
|
151
|
+
* `variables` list the `$name: Type` declarations at the operation
|
|
152
|
+
* header. Each becomes an `Input` on the resulting summary so
|
|
153
|
+
* pairing layers can match against resolver args. Empty when the
|
|
154
|
+
* document body wasn't read.
|
|
155
|
+
*
|
|
156
|
+
* `rootFields` is the list of root-level selection names — the
|
|
157
|
+
* fields the operation actually selects under Query / Mutation /
|
|
158
|
+
* Subscription. Used by the checker's pairing pass. Empty when the
|
|
159
|
+
* document body wasn't read.
|
|
160
|
+
*
|
|
161
|
+
* `unresolved` is set when the argument was recognized as a GraphQL
|
|
162
|
+
* document reference but the header couldn't be fully read. The unit
|
|
163
|
+
* is still emitted (operation type comes from the call shape) and the
|
|
164
|
+
* gap surfaces on the summary as `metadata.graphql.unresolvedDocument`
|
|
165
|
+
* so nothing is silently dropped.
|
|
166
|
+
*/
|
|
167
|
+
operationInfo?: {
|
|
168
|
+
operationType: "query" | "mutation" | "subscription";
|
|
169
|
+
operationName?: string;
|
|
170
|
+
document?: string;
|
|
171
|
+
variables: Array<{
|
|
172
|
+
name: string;
|
|
173
|
+
type: string;
|
|
174
|
+
required: boolean;
|
|
175
|
+
}>;
|
|
176
|
+
rootFields: string[];
|
|
177
|
+
unresolved?: {
|
|
178
|
+
reference: string;
|
|
179
|
+
reason: string;
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Populated by `packageExports` discovery. Carries the package
|
|
184
|
+
* identity the adapter uses to build a `function-call` binding
|
|
185
|
+
* with `package` + `exportPath` fields — i.e. a provider summary
|
|
186
|
+
* for a publicly-exported library function.
|
|
187
|
+
*/
|
|
188
|
+
packageExportInfo?: {
|
|
189
|
+
packageName: string;
|
|
190
|
+
exportPath: string[];
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Populated by `decoratedRoute` discovery (NestJS-style REST
|
|
194
|
+
* controllers). The adapter uses it to build a `rest` binding
|
|
195
|
+
* with `(method, path)` directly, bypassing the
|
|
196
|
+
* `bindingExtraction` config used by Express / Fastify (which
|
|
197
|
+
* extract from `app.get(...)` registration calls — the wrong
|
|
198
|
+
* shape for decorator-driven controllers).
|
|
199
|
+
*/
|
|
200
|
+
routeInfo?: {
|
|
201
|
+
method: string;
|
|
202
|
+
path: string;
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Metadata merged onto the assembled summary's `metadata` field.
|
|
206
|
+
* Populated when a pack's `discoverUnits` callback stamps provenance
|
|
207
|
+
* on the units it returns (the discovery-layer sibling of the
|
|
208
|
+
* per-sub-unit `metadata` the `subUnits` hook carries).
|
|
209
|
+
*/
|
|
210
|
+
metadata?: Record<string, unknown>;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Discover code units in `sourceFile` by running all patterns.
|
|
215
|
+
* Deduplicates entries with the same function node and kind.
|
|
216
|
+
*/
|
|
217
|
+
declare function discoverUnits(sourceFile: SourceFile, patterns: DiscoveryPattern[]): DiscoveredUnit[];
|
|
218
|
+
|
|
219
|
+
interface TimingReport {
|
|
220
|
+
totalMs: number;
|
|
221
|
+
phases: Array<{
|
|
222
|
+
label: string;
|
|
223
|
+
durationMs: number;
|
|
224
|
+
calls: number;
|
|
225
|
+
}>;
|
|
226
|
+
}
|
|
227
|
+
interface Timer {
|
|
228
|
+
/** Run `fn`, accumulate wall time under `label`, return its result. */
|
|
229
|
+
time<T>(label: string, fn: () => T): T;
|
|
230
|
+
/** Async variant — same accumulation rule. */
|
|
231
|
+
timeAsync<T>(label: string, fn: () => Promise<T>): Promise<T>;
|
|
232
|
+
/** Snapshot of all accumulated phases, ordered by total time descending. */
|
|
233
|
+
report(): TimingReport;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Nested function nodes a pack claimed as sub-units. The unit-body
|
|
238
|
+
* walkers stop at these so the sub-unit's behavior isn't double-counted
|
|
239
|
+
* on the parent.
|
|
240
|
+
*/
|
|
241
|
+
type DescentBarriers = ReadonlySet<Node>;
|
|
242
|
+
|
|
243
|
+
declare function extractCodeStructure(unit: DiscoveredUnit, pack: PatternPack, filePath: string, invocationRecognizers?: InvocationRecognizer[], accessRecognizers?: AccessRecognizer[], barriers?: DescentBarriers): RawCodeStructure;
|
|
244
|
+
interface TypeScriptAdapterConfig {
|
|
245
|
+
tsConfigFilePath?: string;
|
|
246
|
+
project?: Project;
|
|
247
|
+
frameworks: PatternPack[];
|
|
248
|
+
extractorOptions?: ExtractorOptions;
|
|
249
|
+
/**
|
|
250
|
+
* Include library summaries for every function reachable through a
|
|
251
|
+
* static call edge from a pack-discovered unit. Defaults to `true` —
|
|
252
|
+
* internal orchestrators, helpers, and utilities that frameworks
|
|
253
|
+
* don't recognise become `library`-kind summaries with
|
|
254
|
+
* `recognition: "reachable"` so readers can see their behaviour.
|
|
255
|
+
* Set to `false` for pack-only extraction (smaller output; only the
|
|
256
|
+
* units packs explicitly claim).
|
|
257
|
+
*/
|
|
258
|
+
includeReachable?: boolean;
|
|
259
|
+
/**
|
|
260
|
+
* Called once at the end of each `extractAll` / `extractFromFiles`
|
|
261
|
+
* with the wall-clock breakdown of the major extraction phases.
|
|
262
|
+
* Always-on instrumentation cost is negligible (millisecond-scale
|
|
263
|
+
* `performance.now()` calls); the callback is the opt-in for
|
|
264
|
+
* surfacing it. CLI uses this to render the per-extract summary
|
|
265
|
+
* line and the `--timing` breakdown.
|
|
266
|
+
*/
|
|
267
|
+
onTiming?: (report: TimingReport) => void;
|
|
268
|
+
/**
|
|
269
|
+
* Called once per extract with the cache hit/miss decision and the
|
|
270
|
+
* partial-reuse diagnostic — counts of cached summaries that fine-
|
|
271
|
+
* grained invalidation would have salvaged on a coarse miss.
|
|
272
|
+
* Phase 4a uses this to validate the dep-tracking model before
|
|
273
|
+
* activating partial-reuse.
|
|
274
|
+
*/
|
|
275
|
+
onCacheDiagnostic?: (diagnostic: CacheDiagnostic) => void;
|
|
276
|
+
/**
|
|
277
|
+
* Called once per `extractAll` with the funnel: files in the
|
|
278
|
+
* tsconfig, files walked, and per-pack gate / candidate / unit /
|
|
279
|
+
* summary counts. The CLI renders it when a run produces nothing,
|
|
280
|
+
* so "wrote 0 summaries" can say which stage the count died at
|
|
281
|
+
* instead of leaving the user to guess.
|
|
282
|
+
*
|
|
283
|
+
* Not called on a cache hit, where no stage ran.
|
|
284
|
+
*/
|
|
285
|
+
onExtractionReport?: (report: ExtractionReport) => void;
|
|
286
|
+
/**
|
|
287
|
+
* On-disk cache directory for the coarse-key extraction cache.
|
|
288
|
+
* Pass an absolute path; defaults to `.suss/cache/` next to the
|
|
289
|
+
* tsconfig (or under cwd when no tsconfig path is supplied).
|
|
290
|
+
* Pass `null` to disable caching entirely. The cache stores one
|
|
291
|
+
* `manifest.json` keyed against (adapter version, pack versions,
|
|
292
|
+
* sorted file mtime/size stamps); on a warm run with no changes
|
|
293
|
+
* the lookup is stat-only and skips extraction.
|
|
294
|
+
*/
|
|
295
|
+
cacheDir?: string | null;
|
|
296
|
+
}
|
|
297
|
+
interface TypeScriptAdapter {
|
|
298
|
+
project: Project;
|
|
299
|
+
/**
|
|
300
|
+
* Extract summaries from a specific list of files. Returns a
|
|
301
|
+
* Promise so the implementation can do concurrent I/O during
|
|
302
|
+
* the pre-filter / lazy-load phases without bottlenecking on
|
|
303
|
+
* sync `fs.readFileSync`.
|
|
304
|
+
*/
|
|
305
|
+
extractFromFiles(filePaths: string[]): Promise<BehavioralSummary[]>;
|
|
306
|
+
/** Extract summaries from every (non-declaration) file the Project knows about. */
|
|
307
|
+
extractAll(): Promise<BehavioralSummary[]>;
|
|
308
|
+
}
|
|
309
|
+
declare function createTypeScriptAdapter(config: TypeScriptAdapterConfig): TypeScriptAdapter;
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Extract all raw branches from a function, composing:
|
|
313
|
+
* 1. findTerminals — locate terminal nodes
|
|
314
|
+
* 2. collectEarlyReturnConditionInfos — prior guard clauses
|
|
315
|
+
* 3. collectAncestorConditionInfos — enclosing branch conditions
|
|
316
|
+
* 4. parseConditionExpression — Expression → Predicate
|
|
317
|
+
* 5. extractInvocationEffects — bare expression-statement calls
|
|
318
|
+
* (Phase 1.5b — attaches to the default branch so handler /
|
|
319
|
+
* useEffect bodies carry their side-effect set)
|
|
320
|
+
*
|
|
321
|
+
* `isDefault` is true when the branch has no conditions, or all
|
|
322
|
+
* conditions come from early returns/throws.
|
|
323
|
+
*/
|
|
324
|
+
declare function extractRawBranches(func: FunctionRoot, terminalPatterns: TerminalPattern[], invocationRecognizers?: InvocationRecognizer[], accessRecognizers?: AccessRecognizer[], barriers?: DescentBarriers): RawBranch[];
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* A Project covering `rootDir`, for a codebase with no tsconfig to read.
|
|
328
|
+
* Returns the project and the files it loaded.
|
|
329
|
+
*/
|
|
330
|
+
declare function createProjectWithoutTsconfig(rootDir: string): {
|
|
331
|
+
project: Project;
|
|
332
|
+
files: string[];
|
|
333
|
+
};
|
|
334
|
+
/** The nearest tsconfig or jsconfig at or above `startDir`, or null. */
|
|
335
|
+
declare function findNearestTsconfig(startDir: string): string | null;
|
|
336
|
+
|
|
337
|
+
interface ContractReadResult {
|
|
338
|
+
declaredContract: RawDeclaredContract;
|
|
339
|
+
boundaryBinding: BoundaryBinding | null;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Read the declared contract for a discovered handler unit.
|
|
343
|
+
*
|
|
344
|
+
* Traces from the handler's registration site (e.g., `s.router(contract, { ... })`)
|
|
345
|
+
* back to the contract definition, then extracts the declared responses for
|
|
346
|
+
* this specific endpoint.
|
|
347
|
+
*
|
|
348
|
+
* Returns null if the handler wasn't registered with a contract or the contract
|
|
349
|
+
* can't be resolved.
|
|
350
|
+
*/
|
|
351
|
+
declare function readContract(unit: DiscoveredUnit, pattern: ContractPattern): ContractReadResult | null;
|
|
352
|
+
/**
|
|
353
|
+
* Resolve the contract for a consumer call site.
|
|
354
|
+
*
|
|
355
|
+
* Given `client.getUser(...)`, traces back to the `initClient(contract, ...)`
|
|
356
|
+
* call to find the contract, then extracts the endpoint definition for the
|
|
357
|
+
* matched method name (e.g. "getUser").
|
|
358
|
+
*/
|
|
359
|
+
declare function readContractForClientCall(callExpression: Node, methodName: string, pattern: ContractPattern): ContractReadResult | null;
|
|
360
|
+
|
|
361
|
+
interface TsDiscoveryContext {
|
|
362
|
+
/** Full filesystem path of the source file. Useful for excluding
|
|
363
|
+
* test / story files via the pack's own regex. */
|
|
364
|
+
getFilePath(sourceFile: SourceFile): string;
|
|
365
|
+
/**
|
|
366
|
+
* Yield every export from the source file whose declaration is a
|
|
367
|
+
* function — function declarations, arrow / function-expression
|
|
368
|
+
* variable initializers, and re-exports of either. Skips
|
|
369
|
+
* non-function exports (constants, classes, types).
|
|
370
|
+
*
|
|
371
|
+
* Each entry carries the exported name (the binding the consumer
|
|
372
|
+
* uses), the function root, and whether the export is the file's
|
|
373
|
+
* default export. `default` is included; the pack decides whether
|
|
374
|
+
* to handle it (typically the data-driven `namedExport(["default"])`
|
|
375
|
+
* already does, and the callback skips to avoid duplicates).
|
|
376
|
+
*/
|
|
377
|
+
exportedFunctions(sourceFile: SourceFile): Array<{
|
|
378
|
+
name: string;
|
|
379
|
+
func: FunctionRoot;
|
|
380
|
+
isDefault: boolean;
|
|
381
|
+
}>;
|
|
382
|
+
/**
|
|
383
|
+
* Walk a function's body for return statements whose value is a
|
|
384
|
+
* JSX element / fragment / self-closing tag. Returns true on the
|
|
385
|
+
* first match; false otherwise. Skips into nested function bodies
|
|
386
|
+
* — nested arrow returning JSX is its own component, not part of
|
|
387
|
+
* this function's output.
|
|
388
|
+
*
|
|
389
|
+
* Concise-arrow bodies (`() => <X/>`) are handled — the body IS
|
|
390
|
+
* the implicit return.
|
|
391
|
+
*/
|
|
392
|
+
hasJsxReturn(func: FunctionRoot): boolean;
|
|
393
|
+
}
|
|
394
|
+
declare function createTsDiscoveryContext(): TsDiscoveryContext;
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Parse a ts-morph Expression into a structured Predicate.
|
|
398
|
+
* Returns null when the expression can't be decomposed cleanly.
|
|
399
|
+
* The caller wraps null into { type: "opaque", sourceText, reason: "complexExpression" }.
|
|
400
|
+
*
|
|
401
|
+
* Call expressions are resolved recursively: if the callee is a local
|
|
402
|
+
* function with a single-expression body, the body is parsed as a
|
|
403
|
+
* predicate and parameter references are substituted with the call-site
|
|
404
|
+
* arguments. This reduces opaqueness for patterns like `if (isActive(user))`.
|
|
405
|
+
*/
|
|
406
|
+
declare function parseConditionExpression(expr: Expression, depth?: number): Predicate | null;
|
|
407
|
+
|
|
408
|
+
interface BranchFieldAccesses {
|
|
409
|
+
/** Terminal location (start, end lines) — used to match back to transitions */
|
|
410
|
+
terminalLocation: {
|
|
411
|
+
start: number;
|
|
412
|
+
end: number;
|
|
413
|
+
};
|
|
414
|
+
/** The shape of fields the consumer reads from the response in this branch */
|
|
415
|
+
expectedInput: TypeShape | null;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* For a client function, collect the fields accessed on the response variable
|
|
419
|
+
* within each branch. Returns one entry per branch (matched by terminal location).
|
|
420
|
+
*
|
|
421
|
+
* @param callExpr The API call expression (fetch(), client.getUser())
|
|
422
|
+
* @param func The enclosing function
|
|
423
|
+
* @param branchLocations Terminal locations from the extracted branches
|
|
424
|
+
* @param responseSemantics Optional response property mappings from the pack —
|
|
425
|
+
* used to filter non-body properties instead of a hardcoded list
|
|
426
|
+
*/
|
|
427
|
+
declare function collectClientFieldAccesses(callExpr: CallExpression, func: Node, branchLocations: Array<{
|
|
428
|
+
start: number;
|
|
429
|
+
end: number;
|
|
430
|
+
}>, responseSemantics?: ResponsePropertyMapping[]): BranchFieldAccesses[];
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Resolve a ts-morph Expression node to a structured ValueRef.
|
|
434
|
+
* Uses only expr.getSymbol()?.getDeclarations()[0] for symbol lookup —
|
|
435
|
+
* never findReferencesAsNodes() which is project-wide and quadratic.
|
|
436
|
+
*
|
|
437
|
+
* Follows intermediate variable assignments (const data = result.body)
|
|
438
|
+
* so that property chains through temporaries resolve to their origin.
|
|
439
|
+
* Depth-bounded to prevent infinite recursion on cyclic references.
|
|
440
|
+
*/
|
|
441
|
+
declare function resolveSubject(expr: Expression, depth?: number): ValueRef;
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* The context the TypeScript adapter passes to a pack's `subUnits`
|
|
445
|
+
* hook. Covers JSX-attribute-walking and call-expression-walking plus
|
|
446
|
+
* the resolution helpers packs need to map AST nodes back to function
|
|
447
|
+
* bodies and source text.
|
|
448
|
+
*
|
|
449
|
+
* All walks are scoped to the parent function and deliberately do NOT
|
|
450
|
+
* recurse into nested function bodies — sub-units of nested functions
|
|
451
|
+
* belong to those functions' summaries, not to the enclosing parent.
|
|
452
|
+
*/
|
|
453
|
+
interface TsSubUnitContext {
|
|
454
|
+
/**
|
|
455
|
+
* Yield every JSX attribute on every JSX element / self-closing
|
|
456
|
+
* element in the parent's body, excluding any inside nested
|
|
457
|
+
* function bodies. The tag is the source text of the element's
|
|
458
|
+
* tag-name node.
|
|
459
|
+
*/
|
|
460
|
+
findJsxAttributes(parent: FunctionRoot): TsJsxAttributeLocation[];
|
|
461
|
+
/**
|
|
462
|
+
* Yield every call expression matching `name` (bare identifier
|
|
463
|
+
* callee only — e.g. `useEffect(...)`, not `React.useEffect(...)`).
|
|
464
|
+
* Nested function bodies are skipped.
|
|
465
|
+
*/
|
|
466
|
+
findCallExpressionsByName(parent: FunctionRoot, name: string): CallExpression[];
|
|
467
|
+
/**
|
|
468
|
+
* Resolve a JSX attribute's value to a function body, if the value
|
|
469
|
+
* is (a) an inline arrow or function expression or (b) an
|
|
470
|
+
* identifier referring to a function declared inside the parent.
|
|
471
|
+
* Returns null for prop-delegating references (`props.onClick`),
|
|
472
|
+
* identifiers that resolve outside the parent's scope, or any
|
|
473
|
+
* non-function expression.
|
|
474
|
+
*/
|
|
475
|
+
resolveAttributeValueFunction(attr: TsJsxAttributeLocation, parent: FunctionRoot): {
|
|
476
|
+
func: FunctionRoot;
|
|
477
|
+
localName: string | null;
|
|
478
|
+
} | null;
|
|
479
|
+
/**
|
|
480
|
+
* Return the Nth argument of a call as a function body, if it's an
|
|
481
|
+
* inline arrow or function expression. Returns null for identifier
|
|
482
|
+
* references, property accesses, or non-function values.
|
|
483
|
+
*/
|
|
484
|
+
getCallArgumentFunction(call: CallExpression, position: number): FunctionRoot | null;
|
|
485
|
+
/**
|
|
486
|
+
* Return the Nth argument raw node (or null if absent).
|
|
487
|
+
*/
|
|
488
|
+
getCallArgument(call: CallExpression, position: number): Node | null;
|
|
489
|
+
/**
|
|
490
|
+
* If `node` is an array literal expression, return each element's
|
|
491
|
+
* source text. If it's some other expression, return a single entry
|
|
492
|
+
* with the node's full text. Null means no deps argument was given.
|
|
493
|
+
*/
|
|
494
|
+
readArrayLiteralText(node: Node | null): string[] | null;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* A located JSX attribute: which element it's on, its name, and the
|
|
498
|
+
* expression node supplying its value (null for boolean-shorthand
|
|
499
|
+
* attributes like `<input disabled>`).
|
|
500
|
+
*/
|
|
501
|
+
interface TsJsxAttributeLocation {
|
|
502
|
+
tag: string;
|
|
503
|
+
name: string;
|
|
504
|
+
valueExpression: Node | null;
|
|
505
|
+
}
|
|
506
|
+
declare function createTsSubUnitContext(): TsSubUnitContext;
|
|
507
|
+
|
|
508
|
+
interface FoundTerminal {
|
|
509
|
+
node: Node;
|
|
510
|
+
terminal: RawTerminal;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Walk every descendant of `func` and try each pattern in order (first match
|
|
515
|
+
* wins per node). Returns all matched terminals across the whole function.
|
|
516
|
+
*
|
|
517
|
+
* Descends into nested function expressions / arrows so a unit whose
|
|
518
|
+
* terminal output is produced inside a Promise executor or `.then`
|
|
519
|
+
* callback (`res.json(data)` on the unit's own `res` parameter) is still
|
|
520
|
+
* discovered. Inside a nested function only escaping terminals are
|
|
521
|
+
* matched — see `NESTED_ESCAPING_MATCH_TYPES`.
|
|
522
|
+
*/
|
|
523
|
+
declare function findTerminals(func: FunctionRoot, patterns: TerminalPattern[], barriers?: DescentBarriers): FoundTerminal[];
|
|
524
|
+
|
|
525
|
+
declare const ADAPTER_VERSION = "0.1.0";
|
|
526
|
+
/**
|
|
527
|
+
* Compute a cache-friendly identity for an adapter+packs combination.
|
|
528
|
+
* Stable across processes given the same inputs; bumps when any pack
|
|
529
|
+
* declares a new version, the adapter version changes, or the loaded
|
|
530
|
+
* adapter dist file changes (dev-mode rebuild auto-invalidation).
|
|
531
|
+
*/
|
|
532
|
+
declare function computeAdapterPacksDigest(packVersions: ReadonlyArray<{
|
|
533
|
+
name: string;
|
|
534
|
+
version?: string;
|
|
535
|
+
}>): string;
|
|
536
|
+
|
|
537
|
+
export { ADAPTER_VERSION, type CacheDiagnostic, type CacheLookup, type ClientCallSite, type ContractReadResult, type DiscoveredUnit, type EmptyStage, type ExtractionReport, type FoundTerminal, type FunctionRoot, type PackFunnel, type Timer, type TimingReport, type TsDiscoveryContext, type TsJsxAttributeLocation, type TsSubUnitContext, type TypeScriptAdapter, type TypeScriptAdapterConfig, collectAncestorBranches, collectClientFieldAccesses, collectEarlyReturns, computeAdapterPacksDigest, createProjectWithoutTsconfig, createTsDiscoveryContext, createTsSubUnitContext, createTypeScriptAdapter, discoverUnits, extractCodeStructure, extractRawBranches, findNearestTsconfig, findTerminals, parseConditionExpression, readContract, readContractForClientCall, resolveSubject };
|