/** * The strict-vs-collect decision. * * Layer 1. Every diagnostic edfcore emits is built here, which is what makes two invariants * structural rather than conventional: * * - `strict: true` throws `EdfFormatError` carrying the first would-be diagnostic whose severity * is not `info`; `info` notes are exempt and are still collected, which is why a strict parse * of a conforming file can still return a `diagnostics` array; * - a code whose disposition is `fatal` throws whether or not `strict` is set, because * proceeding would require inventing data. * * The caller never passes a severity — it is derived from the code by `severityOf`, so one code * cannot acquire two severities in two call sites. */ import { EdfFormatError } from '../errors.js'; import type { EdfDiagnostic, ParseOptions } from '../types.js'; import { type EdfDiagnosticCode } from './codes.js'; /** * Everything a diagnostic may carry except its severity. Optional here, required-or-undefined * on `EdfDiagnostic`: `createDiagnostic` normalises between the two, which is what lets the * result type stay exhaustive under `exactOptionalPropertyTypes`. */ export interface DiagnosticInit { readonly code: EdfDiagnosticCode; /** Must name the field, the raw bytes as written, the rule, and an actionable next step. */ readonly message: string; readonly field?: string; readonly byteOffset?: number; readonly byteLength?: number; readonly rawBytes?: Uint8Array; readonly raw?: string; readonly expected?: string; readonly actual?: string; readonly signalIndex?: number; readonly recordIndex?: number; /** A spec clause, e.g. 'EDF+ additional specification 5'. */ readonly specReference?: string; } /** * Appends every diagnostic in `source` to `target`, without an argument spread. * * `target.push(...source)` passes each element as a separate call argument, and V8 throws * `RangeError: Maximum call stack size exceeded` past roughly 125,000 of them. Several * diagnostics are deliberately reported once per record — `TIMEKEEPING_TAL_MISSING` names the * record whose onset it had to derive — so a long recording with a systematically damaged * annotation section reaches that count honestly. A 32 MiB file with 130,000 records is enough. * * The failure was worse than a crash: the thrown value is a bare `RangeError` about the call * stack, so it is neither an `EdfError` nor a caller mistake, and it lands in the one function * whose whole purpose is to survive being pointed at an untrusted file. */ export declare function appendDiagnostics(target: EdfDiagnostic[], source: readonly EdfDiagnostic[]): void; /** * `appendDiagnostics` for a caller folding one `decodeAnnotations` call per scan chunk. * * `seen` is the fold's own state and must live across the whole sweep, not across one chunk — * that is the entire point. */ export declare function appendChunkDiagnostics(target: EdfDiagnostic[], source: readonly EdfDiagnostic[], seen: Set): void; /** * Builds a diagnostic with its severity derived rather than supplied. A caller choosing its own * severity is how one code comes to mean two different things, so the code is the only input and * `severityOf` decides — every diagnostic with the same code ranks the same everywhere. */ export declare function createDiagnostic(init: DiagnosticInit): EdfDiagnostic; /** * The one way to turn a diagnostic into the error that carries it. `EdfFormatError` re-derives * `field`/`byteOffset`/`signalIndex`/`recordIndex` from the diagnostic, so they are not repeated. */ export declare function toFormatError(diagnostic: EdfDiagnostic, cause?: unknown, collected?: readonly EdfDiagnostic[]): EdfFormatError; /** * `toFormatError(createDiagnostic(init))`, for the paths that throw without a sink — an always * fatal code detected before one exists, or where the type system needs the `throw` to be * visible at the call site. */ export declare function fatalError(init: DiagnosticInit, cause?: unknown): EdfFormatError; /** * The OPTIONS object, in the one family whose option changes what a parse DOES. * * 0.6.130 and 0.6.140 refused a bare value where an options object belongs in the three * formatters and in `cachedSource`, and made the argument for it: every option in this package is * a field on an object, so the value a caller means IS the option, and `formatAnnotations(list, 20)` * is what gets written. `openEdf(source, true)` is the same sentence for the flag a reader is * likeliest to be holding one of, since `strict` is the only option in the package that is a * boolean. * * It read as `undefined`, so the parse was lenient: a file with a would-be diagnostic came back * as a header carrying a list, from a caller who asked to receive no such file at all. `types.ts` * puts it exactly that way — `strict` is for "callers who would rather not receive a file at all * than inspect it" — and they received one, with nothing saying the flag had been dropped. * * Here rather than at each entry point, because this constructor is where `strict` is read, and * `parseHeader`, `readHeader`, `openEdf`, `decodeAnnotations` and `buildTimeline` all reach it. * * `null` and `undefined` still mean "no options", which is what they already meant. */ export declare function assertParseOptions(options: unknown): void; /** * The one place `strict` is turned into a decision. Every module that finds a departure reports * it here rather than choosing between collecting and throwing itself, which is what keeps the * rule — and the `info` exemption — from being reimplemented slightly differently per caller. */ export declare class DiagnosticSink { #private; /** * Readable so a caller can skip building a message it is about to throw away. Never so a * caller can re-implement the decision in `report`. */ readonly strict: boolean; constructor(options?: ParseOptions); get size(): number; /** * Records the diagnostic, or throws `EdfFormatError` when the code is always fatal, or when * `strict` is set and the code describes an actual defect. There is no severity parameter * by design. * * `info` codes are exempt from `strict`. They exist to explain something that is correct but * surprising — a spec-sanctioned negative gain, a pre-stimulus onset, the mandated two-digit * year rule — so throwing on one would contradict what the severity means, and would make * `strict` reject conforming files. Every `info` note is still collected and readable. */ /** * The fatal a caller raises directly, carrying what this sink has already found. * * `fatalError` is the sinkless version and stays that way. Where a sink DOES exist, throwing * through it is what lets `inspectEdf` report the defects the parse had already accumulated * rather than only the one that stopped it (added in 0.3.18). */ fatal(init: DiagnosticInit, cause?: unknown): EdfFormatError; report(init: DiagnosticInit): void; /** A frozen copy: an array already attached to a result must not grow if reporting continues. */ get diagnostics(): readonly EdfDiagnostic[]; /** `diagnostics`, then reset — for a sink reused across records. */ drain(): readonly EdfDiagnostic[]; } //# sourceMappingURL=collector.d.ts.map