import ts from 'typescript'; import type { SpiEdge, SpiSymbol } from './types.js'; export type DetectExpressFrameworkContext = { sourceFile: ts.SourceFile; fileId: string; symbolsByFile: Map; /** Slice 1c-ii.e pushes synthesized inline-handler SpiSymbols into * this flat list so they survive into the SPI's symbol set. The * per-file index (symbolsByFile) is updated in lockstep — both * references point to the same symbol objects, but only `symbols` * is what buildSpi sorts/returns. */ symbols: SpiSymbol[]; /** Slice 1c-ii.c writes route_handler SpiEdges into this array when a * named handler is resolved from a `.(...)` call. */ edges: SpiEdge[]; /** Type checker for the program — used to resolve callee/handler * identifiers to their *declarations* so lexical shadows don't * produce false-positive route tags (CodeRabbit catch on slice * 1c-ii.c). When omitted, the detector falls back to bare-name * matching, which is correct only when no inner scope shadows the * receiver or handler identifier. */ checker?: ts.TypeChecker; /** Map from a source file's POSIX-normalized fileName to its SpiFile * id. Used by slice 1c-ii.h's cross-file mount resolution to look up * an imported router's SpiSymbol when the mount call lives in a * different file from the router's declaration. Optional — falls * back to current-file-only resolution if absent. */ pathToFileId?: Map; }; export declare function detectExpressFramework(ctx: DetectExpressFrameworkContext): void; /** * Slice 1c-ii.g — workspace-level finalizer that applies mounted-router * prefixes to the route_path on each route handler. Called once by * buildSpi after all per-file detectors have run. * * **Same-file only in this slice.** The mount-call detection itself * (in emitMiddlewareForCall) resolves the second-positional argument * via the current file's symbol list, so a mount call that imports * the router from another file does not get a mount_path recorded on * the imported router's SpiSymbol. As a result, the finalizer below * has nothing to apply for cross-file mounts. Cross-file resolution * (plumbing pathToFileId through the detector context and following * the type checker across boundaries) is slice 1c-ii.h. * * Mechanism: * 1. Index every express_router symbol by id; capture its mount_path * (set by emitMiddlewareForCall when the binding appeared as the * second arg of an `app.use('/prefix', router)` call in the SAME * file as the router's declaration). * 2. Walk every route_handler edge in the SPI. For each edge whose * from-symbol is a router with a mount_path, look up the to-symbol * and prepend the mount_path to its route_path. * * The finalizer runs exactly once per buildSpi invocation — it does * NOT guard against being re-run with idempotence checks because: * (a) such checks confuse legitimate Express semantics where a route * path can match its mount prefix (e.g., usersRouter.get('/api') * mounted at '/api' must resolve to '/api/api', not '/api'), and * (b) the single-run invariant is enforced by build.ts. */ export declare function finalizeExpressMountPrefixes(opts: { symbols: SpiSymbol[]; edges: SpiEdge[]; }): void;