/** * Per-file parse budget (change: fix-analyze-native-abort-and-file-cost-budget). * * Extraction of one file used to be unbounded in wall-clock time. Measured on a 300 KB file of * a repeated unterminated block-comment opener: 84 s inside `parser.parse()` alone, yielding a * 100,002-deep tree. A minified bundle or a generated client in an ordinary repository reaches * the same path, so this is not only a hostile-input problem. * * ## Why the bound is in-band, not a timer * * `parser.parse()` is one synchronous call into a native binding. A `setTimeout` cannot preempt * it — the event loop does not run until it returns — and terminating the worker that holds it * is precisely what turns a slow parse into a process-level `abort()` (V8 can only interrupt a * thread at a JS boundary; an interrupt delivered inside a native frame surfaces as a C++ * exception with nowhere to go). tree-sitter's own deadline is the only bound that can actually * stop the work: it is checked inside the parse loop, and on expiry `parse()` simply returns no * tree. Nothing is killed, and the parser stays reusable for the next file. * * ## Honesty * * A budgeted result is a LOWER BOUND and reads as one. An abandoned file is never silently * dropped: {@link ParseBudgetExceededError} carries the elapsed time, the caller records the * file with reason `budget-exceeded`, and conclusions built over it disclose the boundary. The * bound is disabled by setting `OPENLORE_PARSE_BUDGET_MS=0`, which reproduces the previous * behavior exactly — the escape hatch for anyone who would rather wait than be told. * * A binding whose `setTimeoutMicros` is unavailable or unusable (older native builds, and the * web-tree-sitter WASM lane) simply parses unbounded, as it did before — {@link parseBudgetSupported} * reports which. That is a real limit of the bound, not a covered case: those languages keep the * pre-change behaviour, including the unbounded cost. It is called out in the change's spec rather * than papered over here. */ /** * Stable prefix on {@link ParseBudgetExceededError}'s message. * * The message is the ONLY channel that survives an extraction worker: a throw crossing the * `worker_threads` boundary is structured-cloned, so its class, prototype and properties are * gone by the time the parent sees it. Keying the reason off a message prefix is what makes the * pooled and serial lanes classify the same failure identically — an `instanceof` check would * behave differently on the two lanes, which is the bug this prefix exists to avoid. */ export declare const PARSE_BUDGET_MESSAGE_PREFIX = "openlore:parse-budget-exceeded"; /** Thrown when tree-sitter abandoned a parse at the deadline. Carries the bound and what was spent. */ export declare class ParseBudgetExceededError extends Error { readonly elapsedMs: number; readonly budgetMs: number; constructor(elapsedMs: number, budgetMs: number); } /** * Read `message` as a budget-exceeded signal, returning the BUDGET it names. * * Message-based on purpose — see {@link PARSE_BUDGET_MESSAGE_PREFIX}. Returns `undefined` for * any other failure, so an ordinary parse failure is never re-labelled as a budget overrun. * * The budget rather than the elapsed time, because the caller records it in a persisted artifact * that must be byte-identical across re-analyses of a fixed repository state — see * `FileParseHealth.budgetMs`. */ export declare function parseBudgetOverrunMs(message: string | undefined): number | undefined; /** * The active budget in milliseconds, or `0` when disabled. * * Read per call rather than cached at module load: extraction workers inherit `process.env`, and * tests set the override around a single build. A value that is not a finite, non-negative number * is ignored in favour of the default — a typo must not silently disable the bound. */ export declare function parseBudgetMs(): number; /** The minimum shape {@link parseWithBudget} needs. Narrow so tests can supply a plain object. */ export interface BudgetableParser { parse(content: string): TTree | null | undefined; setTimeoutMicros?(micros: number): void; /** * Discard partial parse state. Load-bearing after a deadline fires — see * {@link parseWithBudget}. Optional so a test double need not supply it. */ reset?(): void; } /** Can this parser actually enforce a deadline? `false` means it parses unbounded, as before. */ export declare function parseBudgetSupported(parser: object): boolean; /** * Parse `content` under the active per-file budget. * * Returns the tree on success. Throws {@link ParseBudgetExceededError} when the deadline fired, * so the existing per-file `try`/`catch` in the builder — which already records a file that * contributed nothing — classifies and discloses it without every extractor growing a new return * shape. * * The deadline is re-applied before each parse rather than once at parser construction: these * parsers are per-language singletons shared across a whole build (and reused by the watcher * across builds), so a budget set once could be left stale by any other caller. Setting it is a * single native field write. */ export declare function parseWithBudget(parser: BudgetableParser, content: string): TTree; //# sourceMappingURL=parse-budget.d.ts.map