d9fc1b2Demonstrate lazy reading of remote HDF5/NWB files (direct h5wasm + LINDI)magland 1import { useEffect, useState } from "react";
2import { RemoteH5File, RemoteH5FileLindi } from "../remote-h5-file";
3import type { RemoteH5FileX } from "../remote-h5-file";
4import {
5 extractAssetId,
6 lindiIndexExists,
7 lindiUrlForAsset,
8 resolveDandiDownloadUrl,
9} from "./dandi";
11export type BackendMode = "direct" | "lindi";
13export type BackendState = {
14 file?: RemoteH5FileX;
15 resolvedUrl?: string;
16 openMs?: number;
17 status: "idle" | "opening" | "ready" | "error";
18 error?: string;
19};
21// Opens the remote NWB file using one of the two strategies:
22// - "direct": resolve the DANDI redirect, then read the raw HDF5 lazily via
23// the h5wasm worker (HTTP range requests).
24// - "lindi": locate the precomputed LINDI JSON index and read metadata from
25// it, range-reading only chunk data from the original HDF5.
26export const useBackend = (
27 downloadUrl: string,
28 dandisetId: string,
29 mode: BackendMode,
30): BackendState => {
31 const [state, setState] = useState<BackendState>({ status: "idle" });
33 useEffect(() => {
34 let canceled = false;
35 const open = async () => {
36 setState({ status: "opening" });
37 const t0 = performance.now();
38 try {
39 let file: RemoteH5FileX;
40 let resolvedUrl: string;
41 if (mode === "direct") {
42 resolvedUrl = await resolveDandiDownloadUrl(downloadUrl);
43 file = new RemoteH5File(resolvedUrl, {});
44 } else {
45 const assetId = extractAssetId(downloadUrl);
46 if (!assetId) throw new Error("Could not extract asset id from URL");
47 resolvedUrl = lindiUrlForAsset(dandisetId, assetId);
48 if (!(await lindiIndexExists(resolvedUrl))) {
49 throw new Error(
50 "No LINDI index is available for this asset. neurosift would " +
51 "fall back to direct HDF5 reading in this case.",
52 );
53 }
54 file = await RemoteH5FileLindi.create(resolvedUrl);
55 }
56 // Touch the root group so "ready" means metadata is actually reachable.
57 await file.getGroup("/");
58 if (canceled) return;
59 setState({
60 file,
61 resolvedUrl,
62 openMs: performance.now() - t0,
63 status: "ready",
64 });
65 } catch (err: unknown) {
66 if (canceled) return;
67 setState({
68 status: "error",
69 error: err instanceof Error ? err.message : String(err),
70 });
71 }
72 };
73 open();
74 return () => {
75 canceled = true;
76 };
77 }, [downloadUrl, dandisetId, mode]);
79 return state;
80};