/** * Degradation diagnostics — "parseman could not do the fast/complete thing here". * * A fallback that is CORRECT but slower (or less complete) and announces nothing is * indistinguishable from working properly. Every such path in the compiler and the * analysis APIs reports through this one channel so a consumer's build gate can * assert ZERO degradations, exactly the way jess's `check:macro` already asserts zero * `"falling back to runtime"` lines. * * Machine-greppable contract (stable across minor versions): * * [parseman] degraded [] : — ; otherwise * * Grep token is the literal `[parseman] degraded`. The `` is a stable kebab-case * identifier; new codes may be added, existing ones are not renamed without a note in * CHANGELOG.md. * * NOT for: user errors (those throw), un-taken optimizations that cost nothing, or * anything a grammar author cannot act on. A diagnostic that fires on every rule gets * filtered out and becomes the same silence we are trying to end — so a code that is * common and unavoidable AGGREGATES (see `DETAIL_CAP`) into one summary line with a * count instead of one line per rule. */ /** Off / print / throw. */ export type DegradationLevel = 'off' | 'warn' | 'error'; /** Stable, greppable code per degradation class. */ export type DegradationCode = /** A node build's formal parameter list could not be read → all capture tiers kept. */ 'build-arity-unconfirmed' /** A composed/carried piece is an opaque artifact → its rules were not analysed. */ | 'opaque-artifact' /** A coverage-definition request could not read the grammar → empty is NOT a zero. */ | 'coverage-definitions-unavailable'; /** * Severity. `warn` = a real, measurable cost the author can remove. `info` = the author * has nothing to act on (e.g. an imported reducer in someone else's package), but the * fact still has to be visible and countable. */ export type DegradationSeverity = 'warn' | 'info'; export type Degradation = { code: DegradationCode; severity: DegradationSeverity; /** Rule / node type / artifact the cost lands on. Never a bare "" if avoidable. */ where: string; /** The reducer, identifier, or input that could not be analysed. */ subject: string; /** What parseman did instead. */ fellBackTo: string; /** What it would have done had the input been analysable. */ otherwise: string; }; /** One line per finding, in the documented greppable shape. */ export declare function formatDegradation(d: Degradation): string; /** Group by code, cap the detail lines, and append a counted summary for the rest. */ export declare function formatDegradations(list: readonly Degradation[]): string[]; /** Begin collecting degradations instead of printing them. */ export declare function beginDegradationCapture(): void; /** Stop collecting and return the findings (deduped, insertion-ordered). */ export declare function endDegradationCapture(): Degradation[]; /** Open-sink depth, for a caller that must unwind exactly its own frames. */ export declare function degradationCaptureDepth(): number; /** * Close every sink opened above `depth` and return their findings, outermost first. * * The safety net for an ABORTED capture. `beginDegradationCapture()` and its matching * `end` used to sit on the straight-line path of a function that throws between them, so * one failed macro transform left the sink open for the REST OF THE PROCESS: every later * `recordDegradation` — including from an unrelated runtime `compile()` — went into a * dead Map and printed nothing. Calling this from a `finally` bounds the damage to the * frame that failed, and returns what it had collected so it can still be reported * rather than silently dropped. */ export declare function unwindDegradationCapture(depth: number): Degradation[]; /** * Resolve the level: explicit argument wins, else `PARSEMAN_DEGRADATION`, else * default-on `'warn'`. Default-on is the whole point — this exists because the * default was silence. */ export declare function resolveDegradationLevel(explicit?: DegradationLevel): DegradationLevel; /** * Record one degradation. Deduped on `code + where + subject`, so the four capture-tier * probes that all consult the same unreadable parameter list produce ONE finding, not * four. * * With a sink open (a macro transform) the finding is collected and the drain site * decides what to do with it. With NO sink open — a runtime `compile()` — this is the * only place that ever sees the finding, so it is also the only place that can honour * `'error'`. It therefore throws here. `docs/guide/degradation-diagnostics.md` documents * `PARSEMAN_DEGRADATION=error` as "fail the build" without qualification, and until this * throw existed that was false in library mode: `error` silently behaved as `warn`, * because `endDegradationCapture()` had exactly one call site, in the macro plugin. */ export declare function recordDegradation(d: Degradation): void; /** * Open a per-`compile()` drain and return the function that closes it. * * This channel stays LOUD. `compile()` no longer prints gating advice — that is a * deliberate diagnostic now (`diagnoseGrammar`) — but a degradation is not advice: it * is parseman reporting that it could not do the thing the caller asked for, and this * release exists to stop that happening silently. What changes here is only the SHAPE. * * Before, the sink-less path printed one ~500-character line per site as it went, with * no aggregation — 31 of them for a single code in one `pnpm perf:workloads` run, each * repeating the same advice. The macro drain has always aggregated (`formatDegradations`, * `DETAIL_CAP`); the runtime path simply had no drain to aggregate at. It has one now, * so both paths report the same way and a real count survives instead of a wall. * * Nested inside a macro transform this is a NO-OP: that transform's sink owns the whole * module's findings and returns them on the bundler's warning channel. A per-compile * drain underneath it would steal them and print them instead. * * @returns `drain(report)` — always unwinds the sink (so a `compile()` that throws * cannot leave it open for the rest of the process), and reports only when * `report` is true, so a failed compile does not mask its own error with a second one. */ export declare function beginCompileDegradationDrain(): (report: boolean) => void; /** Test-only: forget the sink-less dedup memo. */ export declare function resetDegradationMemo(): void; //# sourceMappingURL=degradation.d.ts.map