/** * Stage 1+2 unified walk — Phase 4 of * local planning notes * * Legacy pipeline walked every source file twice: once for Stage 1 * (emit FunctionOccurrence records) and once for Stage 2 (locate + * resolve call sites). The two walks descend in identical order and * the only data flowing between them is each function-shape's * bodyHash — which Stage 1 already computes. Stage 2 was *re-hashing* * every function-shape to look it up in `fnByHash`. * * Phase 4 fuses both passes into one descent per file. The walker * emits both: * - `occurrences` — what Stage 1 emitted (function/method/arrow/etc. * records, plus a synthesized module-init per file). * - `callSites` — flat list of nodes that Stage 2's resolvers care * about, paired with the bodyHash that owns them. Resolution * happens *outside* the walk, against this flat list. * * The orchestrator (`cli/orchestrate.ts`) drives the pipeline * end-to-end. `edges.ts` exposes `resolveEdgesFromRecords` as the * Stage 2 entry point, consuming the `callSites` this walk emits. */ import ts from 'typescript'; import type { DependencyForm, DependencyRole, FunctionOccurrence, ParseError } from '@opensip-cli/graph'; /** * A node the unified walk identified as a candidate Stage 2 resolver * target — pre-paired with the bodyHash of its enclosing function-shape * occurrence so the resolver dispatcher doesn't need to re-walk the * AST or re-hash to find ownership. */ export interface CallSiteRecord { readonly node: ts.Node; readonly sourceFile: ts.SourceFile; /** * The function occurrence that owns this site, identified by its * `bodyHash`. Top-level (module-init) sites carry the synthesized * module-init occurrence's hash. */ readonly ownerHash: string; /** * The owning occurrence's declaration position — its 1-based `line` and * 0-based `column`, byte-identical to the owner `FunctionOccurrence`. Paired * with `ownerHash` so edges bucket by FULL occurrence identity * (`ownerEdgeKey(ownerHash, filePath, ownerLine, ownerColumn)`) and same-file * body-twins never union their edges (ADR-0136). */ readonly ownerLine: number; readonly ownerColumn: number; /** * 'call' for resolver dispatch (call/new/jsx/identifier-ref/ * shorthand). 'creation' for parent → nested-callable creation * edges (arrows, function-expressions, methods, accessors, * constructors); the resolver pass emits a static high-confidence * edge for these without consulting any resolver. */ readonly kind: 'call' | 'creation'; /** * For 'creation' kind, the bodyHash of the nested callable. */ readonly childHash?: string; } /** * One module-level import site discovered by the walker. Resolved to * a target module-init bodyHash by the resolver (Phase 4 of opensip's * substrate consolidation — DEC-498). * * Covers `ImportDeclaration` and `ImportEqualsDeclaration` with a * string moduleSpecifier. Re-exports (`ExportDeclaration` with * `moduleSpecifier`) and dynamic imports (`import('…')` expressions) * are out of scope at v1 — they can be added in a follow-up if * dispatch grouping shows they matter. */ export interface DependencySiteRecord { readonly node: ts.Node; readonly sourceFile: ts.SourceFile; /** bodyHash of the file's synthesized module-init occurrence. */ readonly ownerHash: string; /** The module-init occurrence's declaration position (1-based line / 0-based * column, byte-identical to its `FunctionOccurrence`) — paired with * `ownerHash` so dependency edges bucket by full occurrence identity via * `ownerEdgeKey` (ADR-0136). Module-init is always at line 1, column 0. */ readonly ownerLine: number; readonly ownerColumn: number; /** Raw import specifier — `'./foo'`, `'@opensip/core'`, etc. */ readonly specifier: string; /** 1-based line of the import statement. */ readonly line: number; /** 0-based column. */ readonly column: number; /** How the dependency statement is written (P2 Phase 0). The TS walk always * sets form+role; typed optional only to match the polyglot contract. */ readonly form?: DependencyForm; /** The executable role of the dependency (P2 Phase 0). */ readonly role?: DependencyRole; } /** * One re-export the file declares, normalized to the data the engine's * export index needs to make a re-exported name resolvable under the * RE-EXPORTING package. Two TS forms produce these: * * 1. `export { a, b as c } from './y' | '@scope/pkg'` — an * `ExportDeclaration` WITH a `moduleSpecifier`. * 2. `export { a, b }` (NO `from`) where `a`/`b` are bindings IMPORTED at * the top of the file — TS's import-then-re-export idiom (e.g. * `import { childrenOf } from '@opensip-cli/tree-sitter'; export * { childrenOf }`). Correlated against the file's named imports. * * A plain `export { localDef }` of a name DEFINED in this file is NOT a * re-export — it is already an `'exported'` occurrence in the catalog — so it * produces no record. */ export interface ReExportRecord { /** Re-exporting file, project-relative POSIX (→ `packageOf` gives the group). */ readonly fromFile: string; /** The name as exposed BY this module. `'*'` for `export * from`. */ readonly exportedName: string; /** The name in the SOURCE module (== `exportedName` unless aliased via * `export { x as y }`; `'*'` for star). */ readonly sourceName: string; /** The source module specifier — relative (`'./x'`) or workspace (`'@scope/pkg'`). */ readonly specifier: string; } export interface WalkInput { /** * The project's source files to walk. Supplied by the adapter from * either tier: exact mode passes `program.getSourceFiles()`; fast mode * passes the standalone source-file map's values. The walk is purely * structural — it needs `ts.SourceFile`s with parent pointers (which * both tiers provide) and nothing from the type checker — so it is * mode-agnostic and never sees a `ts.Program`. */ readonly sourceFiles: Iterable; readonly files: readonly string[]; readonly projectDirAbs: string; } export interface WalkOutput { readonly functions: Record; readonly callSites: readonly CallSiteRecord[]; readonly dependencySites: readonly DependencySiteRecord[]; readonly reExports: readonly ReExportRecord[]; readonly parseErrors: readonly ParseError[]; } export declare function walkProgram(input: WalkInput): WalkOutput; //# sourceMappingURL=walk.d.ts.map