import type { Inv1Config } from "./config.js"; /** * INV-1 write-site scanner (Proposal 006 A6). A PURE function of * `(filePath, content, config)` — no file I/O, no git, no TS type-checking. * It parses `content` with `ts.createSourceFile` (a pure in-memory syntax * parse) and walks the AST for drizzle guarded-column write expressions. * * Purity is load-bearing: it is the precondition for the B3 machine-level * cache, whose key is `hash(adapter_version, blob_oid, config_hash)` — same * bytes + same config ⇒ same result, so config is an explicit parameter, never * read from disk inside here. * * Why AST, not regex (acceptance (a) = no false-negatives): a regex can only * find the forms it anticipates, so an unrecognized guarded write becomes a * SILENT miss. Walking the AST classifies every guarded-table write chain, so * anything not confidently analyzable falls into `unanalyzable` by * construction — the conservative, human-review bucket. Chain SHAPE also * disambiguates drizzle `db.update(runs).set({...})` from lookalikes like * `hash.update(x).digest()` (no `.set`) without needing types. * * Recognized guarded-table transition-write forms: * - `update(TABLE).set(...)` * - `insert(TABLE)…onConflictDoUpdate({ set: {...} })` (an upsert IS a * transition, so it is classified, not silently skipped) * TABLE is resolved from a bare identifier via: config.guardedTables → * config.aliases → file-local `const X = ` aliases (pre-scanned). * * Documented residual false-negatives (no data-flow/type analysis, on purpose, * to keep the parse pure and within T0's <5s budget): a guarded table reached * through a *renamed method* (`const u = db.update; u(runs)`) or a *reassigned / * parameter-passed* table binding is not resolved. Pure `insert(TABLE).values` * (row creation, i.e. INITIAL state, not a transition) is deliberately out of * INV-1's transition scope. None of these forms occur in the current target today (verified); * recorded here rather than left silent. */ export type WriteVerdict = "allowed" | "violation" | "unanalyzable"; export interface WritePoint { filePath: string; /** 1-based line, for a human reading the report — deliberately NOT used as a stable anchor. */ line: number; /** Resolved guarded table identifier, or null for a dynamic (non-identifier) table expression. */ table: string | null; columns: string[] | "opaque"; verdict: WriteVerdict; reason: string; snippet: string; } export declare function scanFileForGuardedWrites(filePath: string, content: string, config: Inv1Config): WritePoint[]; //# sourceMappingURL=scan.d.ts.map