/** * Error and diagnostic types. * * The distinction that matters here: an EdfError means we cannot produce * trustworthy output and must stop. A Diagnostic with severity 'warning' means * we can continue, but the user needs to know something about their data that * they would not otherwise see. */ export type DiagnosticCode = 'MIXED_SAMPLING_RATES' | 'DISCONTINUOUS' | 'RECORD_COUNT_UNKNOWN' | 'RECORD_COUNT_MISMATCH' | 'TRAILING_BYTES' | 'DEGENERATE_DIGITAL_RANGE' | 'DEGENERATE_PHYSICAL_RANGE' | 'UNUSABLE_PHYSICAL_RANGE' | 'INVERTED_PHYSICAL_RANGE' | 'DUPLICATE_LABEL' | 'EMPTY_LABEL' | 'NO_ANNOTATIONS' | 'ANNOTATION_DECODE_FAILED' | 'COMMA_DECIMAL' | 'LARGE_OUTPUT' | 'NO_SIGNAL_CHANNELS' | 'NO_SAMPLES' | 'STALE_OUTPUT' | 'INPUT_CHANGED' | 'EMPTY_WINDOW' | 'EMPTY_RATE_WINDOW' | 'TIME_RESOLUTION' | 'VALUE_RESOLUTION' | 'HEADER_BYTES_MISMATCH' | 'NONPRINTABLE_LABEL' /** * Header text that a spreadsheet will run instead of read. * * Excel, LibreOffice and Google Sheets treat a cell beginning `=`, `+` or `@` as a formula, * whatever file it arrived in. EDF labels, units, transducer and prefiltering fields are * free text out of the header, and this tool writes them through unchanged on purpose — so * a channel labelled `=1+1` becomes a column header that computes 2, and one labelled * `=HYPERLINK(...)` becomes a link the reader did not write. The README says the output * opens in Excel and SECURITY.md already treats these fields as attacker-controlled; this * is the one place they reach a program that executes text. * * A warning rather than a rewrite. Prefixing the cell with a quote is the usual mitigation * and it would mean writing something the header does not say, which is the one thing this * tool does not do — the same answer NONPRINTABLE_LABEL gives for control bytes. */ | 'FORMULA_LABEL' /** * The header's start date or time is not a date or a time. * * Every other unusable header field reports itself. This one did not, so a recording whose * timestamp cannot be read converted in silence, passed `--strict`, and left * `start_datetime_local` null in metadata.json with nothing saying why — on the field the * documented recipe for an absolute instant depends on. */ | 'START_TIME_UNREADABLE' /** * The header's start time names the sixtieth second of a minute. * * A leap second is a real instant and `23.59.60` is how UTC writes it, but no calendar date * has a sixtieth second and `Date` has no way to hold one: asking for it rolls over into the * next minute. So the instant is kept as `:59`, one second earlier — which was done silently, * and `start_datetime_local` then named a moment the header does not, on the field the * documented recipe for an absolute instant depends on. */ | 'LEAP_SECOND_START' /** * An EDF+ recording identification field whose `Startdate` is not the header's start date. * * EDF+ requires the two to be the same date, and the four-digit year in the recording ID is * what lets a file say which century it belongs to. When they disagree about the day or the * month, or about a year that is not the same year at all, neither is corroborated and the * one number naming when the recording happened cannot be trusted — which nothing said. */ | 'START_DATE_MISMATCH' /** * An annotation channel with a non-zero origin, in a file marked neither EDF+C nor EDF+D. * * The marker decides whether the origin is applied, and the annotation channel is found by * label instead. So a file carrying one without the marker got samples timed from zero and events * timed from the origin, and the two CSVs came out on clocks seconds apart. */ | 'MISSING_EDF_PLUS_MARKER' /** * `--info --stdout` on a recording `--stdout` would refuse. * * Only `--info` raises it. A conversion refuses outright instead, with the same words — * this is that refusal shown ahead of time, which is what `--info` is for. */ | 'STDOUT_UNSUPPORTED'; export interface Diagnostic { code: DiagnosticCode; /** * Always `'warning'`. One field, one value, because there has only ever been one. * * It was `'warning' | 'info'`, and nothing in this codebase has ever built an `'info'`. * A published union is a promise about what a caller may receive, so the two pages that * print this interface told them to expect a second value and handle it — a branch that * cannot run, in the type a `--json` consumer reads `severity` out of. The terminal was * worse: `formatDiagnostics` labelled anything that was not `'warning'` as `note:`, so the * one thing the type said could arrive would have arrived under a third name again. * * Kept as a field rather than dropped: `metadata.json`, both JSON streams and this * interface all carry it, and removing it from a document people have archived is a * different and worse change than narrowing what it can say. */ severity: 'warning'; message: string; /** What the user can do about it. Omitted when there is nothing useful to say. */ hint?: string; } export type EdfErrorCode = 'FILE_TOO_SMALL' | 'BAD_HEADER_FIELD' | 'NO_DATA_RECORDS' | 'INVALID_SIGNAL_COUNT' | 'INVALID_RECORD_DURATION' | 'NO_SAMPLES' | 'UNREADABLE'; /** * A fatal problem with the recording itself. Carries a stable `code` so the CLI * can map it to an exit status, and a `hint` so the user is not left guessing. */ export declare class EdfError extends Error { readonly code: EdfErrorCode; readonly hint: string | undefined; constructor(code: EdfErrorCode, message: string, hint?: string); }