@pennsieve/timeseries-zarr-reader
v0.6.0
Published
Reads pyramid Zarr v3 time-series bundles in the browser or Node.js and yields per-channel segments for rendering.
Readme
timeseries-zarr-reader
A framework-agnostic TypeScript library that reads pyramid Zarr v3 bundles of electrophysiology time series in the browser or Node, and produces per-channel segments sized for canvas rendering.
The library takes a Zarr Store and yields Segment and EventBatch async iterables.
Level selection, min/max resampling to the pixel grid, bipolar montages, Butterworth
filtering, and spike reads all run client-side. A server only has to serve bytes with HTTP
Range support.
The bundle format is written and specified by
timeseries-zarr-py.
Installation
pnpm add @pennsieve/timeseries-zarr-readerESM only, Node 20 or later. In the browser, use any bundler that resolves the exports
map.
Quick start
import { StreamingClient, openBundle } from "@pennsieve/timeseries-zarr-reader";
const client = new StreamingClient({
store: await openBundle("https://example.org/recording.zarr"),
});
const channels = await client.channelInfo();
const continuous = channels.filter((c) => c.kind === "continuous");
const [first] = continuous;
if (!first) throw new Error("bundle has no continuous channels");
// One Segment per channel: raw samples at fine pixel widths, interleaved
// [min, max, ...] envelope pairs at coarse ones.
for await (const segment of client.query({
channels: continuous.map((c) => c.id),
startUs: first.startUs,
endUs: first.startUs + 60_000_000,
pixelWidthUs: 50_000, // 60 s across 1200 pixels
})) {
draw(segment);
}What a caller needs to know
- All times are UTC microseconds. Sample values stay in each channel's recorded physical unit.
- The reader picks the coarsest pyramid level that fits the requested pixel width. A
montage, a filter, or
raw: trueforces a raw read instead. - Reads of the raw level are capped at 15 MB per query and reject with
RawReadTooLargeErrorbefore fetching, whether a filter, a montage, orraw: trueforced the raw level or the pixel width selected it. - Segments are delivered on bin boundaries, so a segment can start before
startUsand run pastendUs. - Filters carry state across consecutive windows, so a trace read window by window matches the same trace read whole.
query()reads continuous channels. Unit channels are read withqueryUnits().
The full reference is in docs/api.md, including custom stores for authentication and caching.
Development
pnpm, Vitest with v8 coverage, TypeScript strict with ESM and nodenext resolution,
ESLint and Prettier.
pnpm check # the gate: eslint + prettier --check + tsc --noEmit + vitest + build
pnpm test # vitest only
pnpm build # tsc -p tsconfig.build.json -> dist/
pnpm typecheck # tsc --noEmit
pnpm lint # eslint --fix + prettier --write (rewrites files)
pnpm format:check # prettier --check (read-only)src/index.ts re-exports the public API and src/client.ts holds StreamingClient.
Tests sit beside the module they cover, as src/<module>.test.ts. The acceptance tests
read test-data/sample.zarr, a small bundle committed to the repository;
scripts/generate-test-bundle.py documents its contents and rewrites it from a
timeseries-zarr-py checkout.
License
Apache-2.0. See LICENSE.
