/** * The convenience layer: composition over the primitives, and no behaviour of its own. * * Layer 6. Everything here is a few lines of arrangement around `io/read.ts`, `decode/` and * `time/`. That is the point — a facade that made its own decisions would be a second place where * the library's rules live, and the rules are what edfcore is. * * Three shapes are load-bearing: * * - `readRecords` returns exactly ONE chunk and costs exactly one read. The caller named the * records, so a gap inside them cannot surprise anyone. * - `readWindow` ALWAYS returns an array, one chunk per contiguous run, and on a continuous file a * window that selects any records is a single element. If two shapes existed, consumers would * write against the easy one and misbehave on EDF+D. A window entirely inside a gap, past the * end, or of non-positive duration returns `[]` — the last two on a continuous file too, which * is why the sentence above is about windows that select records and not about windows. * Nothing is ever filled in: there is no gap-fill and no gap-fill option. * - Chunks stay RECORD-ALIGNED and are therefore usually wider than the window asked for. The * exact per-signal narrowing is `trimToWindow`, which is pure and testable without I/O. * * Every chunk carries the onsets of the records it contains, verified from the bytes that were * already read — annotation regions live inside those bytes, so this costs no extra I/O and makes * a sparsely indexed file safe for the data you actually received. */ import { EdfChannelNotFoundError } from './errors.js'; import type { ByteSource, DecodeAnnotationsOptions, EdfAnnotationsResult, EdfChunk, EdfGap, EdfHeader, EdfRecordIndex, EdfRecording, EdfSignal, OpenOptions, ReadOptions, RecordRange, RecordSelection, WindowSelection } from './types.js'; /** * Open a recording: the header, then the timeline. * * Never scans the file. On a plain EDF or BDF this is two reads in total; on an EDF+ or BDF+ file * it is two more, probing the first and last records for their timekeeping onsets. */ export declare function openEdf(source: ByteSource, options?: OpenOptions): Promise; /** * The SELECTION itself, before any field of it is read. * * `assertSignalIndices` below makes this argument already — "TypeScript is not the only way in. A * selection built from JSON, from a config file, from a JavaScript call site…" — and it was made * one level too deep. Reaching that guard means dereferencing `selection`, so a caller who * omitted the whole object got `Cannot read properties of undefined (reading 'signalIndices')` * from V8 instead: a `TypeError` with no `Next:` clause, naming an internal field rather than the * argument, from a package whose plain `RangeError` is what a caller mistake is supposed to look * like. `readWindow`, `readRecords`, `readEnvelope`, `streamRecords` and `readTriggers` all did * it, each naming whichever field it happened to read first (fixed in 0.6.79). * * `shape` is the call's own selection spelled out, so the message names what to pass rather than * what was missing. */ export declare function assertSelection(selection: unknown, call: string, shape: string, recordsForm?: string): void; /** * The RECORDING itself, before any field of it is read. * * `assertSelection` above checks the second argument; the first one was never checked at all, and * the mistake it invites is the one every async API invites. A forgotten `await` passes the * pending Promise `openEdf` returns, and it reached `recording.header.signals` and threw V8's * `Cannot read properties of undefined (reading 'signals')` — a `TypeError` with no `Next:` * clause, naming an internal field rather than the argument, and saying nothing about the one * keyword that fixes it (fixed in 0.6.89). */ export declare function assertRecording(recording: unknown, call: string): asserts recording is EdfRecording; /** * The one required option with no default, refused in edfcore's own words. * * `reading-signals.md` explains why there is no "all signals" default: so that the whole of a * 256-channel file is never read because an argument was omitted. Omitting it was a caller * mistake the type system catches — and TypeScript is not the only way in. A selection built from * JSON, from a config file, from a JavaScript call site, or from an object spread that dropped a * key arrives at run time, and until 0.4.442 it produced `TypeError: signalIndices is not * iterable`: no `Next:` clause, no mention of edfcore, and nothing naming the option. Every other * bad argument on this path already says what to pass instead (fixed in 0.4.442). * * No caller prefix, for the reason `resolveSignals` below carries none: it is shared by * `readWindow`, `readRecords`, `streamRecords` and the envelope calls, and a hard-coded name is * wrong for all but one of them. `envelope.test.ts` records that exact mistake being made by three * functions sharing two helpers (0.3.35). The option's own name is what a caller needs. */ export declare function assertSignalIndices(signalIndices: unknown): void; /** * The refusal for a signal index the file does not have, in ONE place. * * `envelope.ts` keeps its own copy of this loop and its comment says why: "The same refusal * `resolveSignals` gives, from the resolver the envelope path uses instead", and, of this error * specifically, that the identical mistake once "threw a typed error carrying `selector` and * `availableLabels` from `readWindow` and a bare `RangeError` from here, so `isEdfError` answered * differently depending on which read the caller had reached for". * * The class was made to match. The MESSAGE was not: the envelope's copy ends "Next: pass an index * from header.dataSignalIndices" and stops there, without the clause naming the function that * takes a label. A label is the commonest way to arrive here — `getSignal(header, selector)` * accepts one, so naming channels is the habit the rest of the package teaches — and the half of * the advice that fixes it was the half `readEnvelope` withheld. * * Two copies of a sentence have to be kept in agreement and one does not, which is the argument * 0.6.121 makes for `isByteArray` (fixed in 0.6.136). */ export declare function channelNotFound(header: EdfHeader, signalIndex: number): EdfChannelNotFoundError; export declare function resolveSignals(header: EdfHeader, signalIndices: readonly number[]): readonly EdfSignal[]; /** * The gap immediately before `recordStart`, when the index knows where the gaps are. * * `undefined` for a probed index, and that is not a claim that there is no gap — it is the honest * answer that nobody has read the onsets in between. `buildRecordIndex()` is what turns the * question into an answerable one. * * Exported for `envelope.ts`, so an envelope chunk reports a gap exactly as a read chunk does. */ export declare function gapBefore(index: EdfRecordIndex, recordStart: number): EdfGap | undefined; /** Exactly one chunk and exactly one read: you named the records, so gaps cannot surprise you. */ export declare function readRecords(recording: EdfRecording, selection: RecordSelection, options?: ReadOptions): Promise; /** * A time window, as one chunk per contiguous run of records. * * Always an array. `[]` means the window is entirely inside a gap or entirely outside the * recording — never that the read failed. Chunks are record-aligned and may be wider than asked * for; `trimToWindow(header, chunkSignal, startSeconds, durationSeconds)` narrows them exactly. * * Runs are read one after another rather than concurrently, so the read pattern a caller observes * is the one this function issued, in order, with no burst it did not ask for. Concurrency over a * `ByteSource` belongs to the source — `httpSource` has `maxConcurrency` — not here. * * On a discontinuous file a probed index cannot map seconds to records; `resolveTimeWindow` * refuses rather than guessing. Build a complete index and rebuild the recording around it: * `const index = await buildRecordIndex(rec); await readWindow({ ...rec, index }, selection)`. */ export declare function readWindow(recording: EdfRecording, selection: WindowSelection, options?: ReadOptions): Promise; /** * The annotations in a record range, in one read. * * `records` is required and has no default. A full-file annotation scan is a legitimate thing to * want and an expensive thing to do by accident, so it is always visible in the caller's source * as `{ start: 0, count: recording.header.recordCount }`. */ export declare function readAnnotations(recording: EdfRecording, records: RecordRange, options?: DecodeAnnotationsOptions & ReadOptions): Promise; //# sourceMappingURL=recording.d.ts.map