Skip to content

Commit b4d4f3c

Browse files
authored
support v3 stores consolidated_metadata (#652)
* updates for zarrita v0.7, supports now consolidated_metadata in v3 stores * suggestions
1 parent 949845b commit b4d4f3c

7 files changed

Lines changed: 39 additions & 32 deletions

File tree

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
"input-otp": "^1.4.2",
8080
"js-colormaps-es": "^0.0.5",
8181
"lucide-react": "^0.562.0",
82-
"next": "^16.2.0",
82+
"next": "^16.2.7",
8383
"next-themes": "^0.4.6",
8484
"radix-ui": "1.4.3",
8585
"react": "^19.0.0",
@@ -95,7 +95,7 @@
9595
"three-stdlib": "^2.36.0",
9696
"vaul": "^1.1.2",
9797
"webgpu-utils": "^1.11.0",
98-
"zarrita": "^0.6.2",
98+
"zarrita": "^0.7.3",
9999
"zod": "^4.3.6",
100100
"zustand": "^5.0.8"
101101
},
@@ -109,7 +109,7 @@
109109
"@types/react": "^19",
110110
"@types/react-dom": "^19",
111111
"@vitejs/plugin-react": "^4.4.1",
112-
"@vitest/coverage-v8": "^3.2.4",
112+
"@vitest/coverage-v8": "^4.1.8",
113113
"eslint": "^9",
114114
"eslint-config-next": "^16.2.0",
115115
"eslint-plugin-next": "^0.0.0",
@@ -119,6 +119,6 @@
119119
"tw-animate-css": "^1.3.4",
120120
"typescript": "^5",
121121
"vite-tsconfig-paths": "^5.1.4",
122-
"vitest": "^3.1.2"
122+
"vitest": "^4.1.8"
123123
}
124124
}

src/components/ui/MainPanel/LocalZarr.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,16 @@ const LocalZarr = ({setShowLocal, setOpenVariables, setInitStore}:LocalZarrType)
3737
}
3838

3939
// Create a custom zarrita store from the Map
40-
const customStore: zarr.AsyncReadable<any> = {
41-
async get(key: string) {
40+
const customStore: zarr.AsyncReadable = {
41+
async get(key: string): Promise<Uint8Array | undefined> {
4242
const file = fileMap.get(key)
4343
const buffer = await file?.arrayBuffer();
4444
return buffer ? new Uint8Array(buffer) : undefined;
4545
}
4646
};
4747
try {
4848
// Open the Zarr store using the custom store
49-
let store = await zarr.tryWithConsolidated(customStore);
49+
let store = await zarr.withMaybeConsolidatedMetadata(customStore);
5050
if (!('contents' in store)){
5151
// Metadata is missing. We will need to parse variables here.
5252
store = await ZarrParser(files, customStore)

src/components/ui/MainPanel/RemoteZarr.tsx

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ type Props = {
3636
};
3737

3838
const RemoteZarr = ({ initStore, setInitStore, onOpenDescription }: Props) => {
39-
const [showOverrides, setShowOverrides] = useState(false);
39+
const [showFetchOptions, setShowFetchOptions] = useState(false);
4040
const [preset, setPreset] = useState<AuthPreset>('none');
4141
const [presetValue, setPresetValue] = useState('');
4242
const [headers, setHeaders] = useState<HeaderRow[]>([{ key: '', value: '' }]);
@@ -47,7 +47,7 @@ const RemoteZarr = ({ initStore, setInitStore, onOpenDescription }: Props) => {
4747
const updateHeader = (i: number, field: 'key' | 'value', val: string) =>
4848
setHeaders(h => h.map((row, idx) => idx === i ? { ...row, [field]: val } : row));
4949

50-
const buildOverrides = (): RequestInit | undefined => {
50+
const buildFetchHandler = (): ((request: Request) => Promise<Response>) | undefined => {
5151
const builtHeaders: Record<string, string> = {};
5252

5353
// Preset auth header
@@ -60,9 +60,12 @@ const RemoteZarr = ({ initStore, setInitStore, onOpenDescription }: Props) => {
6060
.filter(r => r.key.trim())
6161
.forEach(r => { builtHeaders[r.key.trim()] = r.value.trim(); });
6262

63-
return Object.keys(builtHeaders).length > 0
64-
? { headers: builtHeaders }
65-
: undefined;
63+
if (Object.keys(builtHeaders).length === 0) return undefined;
64+
65+
return (request: Request) => {
66+
Object.entries(builtHeaders).forEach(([k, v]) => request.headers.set(k, v));
67+
return fetch(request);
68+
};
6669
};
6770

6871
return (
@@ -74,18 +77,18 @@ const RemoteZarr = ({ initStore, setInitStore, onOpenDescription }: Props) => {
7477
const url = input.value;
7578
if (!url) return;
7679

77-
const overrides = buildOverrides();
78-
if (overrides && url.startsWith('http://')) {
80+
const fetchHandler = buildFetchHandler();
81+
if (fetchHandler && url.startsWith('http://')) {
7982
useGlobalStore.getState().setStatus('Error: Cannot send auth headers over plain HTTP — use HTTPS.');
8083
return;
8184
}
8285
const fetchOptions = {
83-
...(overrides && { overrides }),
86+
...(fetchHandler && { fetch: fetchHandler}),
8487
};
8588

8689
useZarrStore.getState().setIcechunkOptions(null);
8790
useZarrStore.getState().setFetchOptions(
88-
Object.keys(fetchOptions).length > 0 ? fetchOptions : null
91+
fetchOptions.fetch !== undefined ? fetchOptions : null
8992
);
9093
useGlobalStore.getState().setStatus('Fetching...');
9194

@@ -102,19 +105,19 @@ const RemoteZarr = ({ initStore, setInitStore, onOpenDescription }: Props) => {
102105
</Button>
103106
</div>
104107

105-
{/* Overrides */}
108+
{/* FetchOptions */}
106109
<div>
107110
<Button
108111
type="button"
109112
variant="ghost"
110113
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
111-
onClick={() => setShowOverrides(v => !v)}
114+
onClick={() => setShowFetchOptions(v => !v)}
112115
>
113-
{showOverrides ? <ChevronUp size={12} /> : <ChevronDown size={12} />}
114-
Overrides
116+
{showFetchOptions ? <ChevronUp size={12} /> : <ChevronDown size={12} />}
117+
fetchOptions
115118
</Button>
116119

117-
{showOverrides && (
120+
{showFetchOptions && (
118121
<div className="flex flex-col gap-3 mt-2">
119122

120123
{/* Auth preset selector */}

src/components/zarr/Interfaces.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { FetchClient } from "icechunk-js";
22

33
export interface FetchStoreOptions {
4-
overrides?: RequestInit;
4+
fetch?: (request: Request) => Promise<Response>;
55
maxRetries?: number;
66
retryDelay?: number;
77
}

src/components/zarr/ZarrGetters.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export async function GetStore(storePath: string): Promise<zarr.Group<zarr.Fetch
100100
const {setStatus} = useGlobalStore.getState();
101101
for (let attempt = 0; attempt <= maxRetries; attempt++) {
102102
try {
103-
const d_store = zarr.tryWithConsolidated(
103+
const d_store = zarr.withMaybeConsolidatedMetadata(
104104
new zarr.FetchStore(storePath)
105105
);
106106
const gs = await d_store.then(store => zarr.open(store, {kind: 'group'}));

src/components/zarr/ZarrParser.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as zarr from 'zarrita'
2-
import { json_decode_object, json_encode_object } from 'node_modules/zarrita/dist/src/util';
2+
import { jsonDecodeObject, jsonEncodeObject } from 'node_modules/zarrita/dist/src/util';
33

44
function is_meta_key(key: string) {
55
return (key.endsWith(".zarray") ||
@@ -26,7 +26,7 @@ async function ZarrParser(files: any, store: any){
2626
}
2727
for (const variable of vars){
2828
const decoded = await store.get(variable)
29-
metadata[variable.slice(1)] = json_decode_object(decoded)
29+
metadata[variable.slice(1)] = jsonDecodeObject(decoded)
3030
}
3131
const v2_meta = {metadata, zarr_consolidated_format: 1}
3232
const known_meta: { [key: string]: any } = {};
@@ -36,11 +36,11 @@ async function ZarrParser(files: any, store: any){
3636
return {
3737
async get(key: string, opts?: any): Promise<any> {
3838
if (known_meta[key]) {
39-
return json_encode_object(known_meta[key]);
39+
return jsonEncodeObject(known_meta[key]);
4040
}
4141
const maybe_bytes = await store.get(key, opts);
4242
if (is_meta_key(key) && maybe_bytes) {
43-
const meta = json_decode_object(maybe_bytes);
43+
const meta = jsonDecodeObject(maybe_bytes);
4444
known_meta[key] = meta;
4545
}
4646
return maybe_bytes;

src/components/zarr/zarrita-store.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ export async function getFetchStore(
1414

1515
for (let attempt = 0; attempt <= maxRetries; attempt++) {
1616
try {
17-
const d_store = zarr.tryWithConsolidated(
18-
new zarr.FetchStore(storePath, { overrides: fetchOptions?.overrides })
17+
const d_store = zarr.withMaybeConsolidatedMetadata(
18+
new zarr.FetchStore(storePath, { fetch: fetchOptions?.fetch })
1919
);
2020
const gs = await d_store.then(store => zarr.open(store, { kind: 'group' }));
2121
useGlobalStore.setState({ status: null });
@@ -92,7 +92,10 @@ export async function getFetchStoreAttributes(
9292
cacheName: string
9393
) {
9494
const outVar = await zarr.open(group.resolve(variable), { kind: 'array' });
95-
const meta = outVar.attrs;
95+
const meta = {
96+
...outVar.attrs as Record<string, unknown>,
97+
shape: outVar.shape,
98+
};
9699
useCacheStore.getState().cache.set(cacheName, meta);
97100
return meta;
98101
}
@@ -111,8 +114,9 @@ export async function getFetchStoreDims(
111114
meta.shape = outVar.shape;
112115
cache.set(`${initStore}_${variable}_meta`, meta);
113116

114-
const dimNames = meta._ARRAY_DIMENSIONS as string[] | undefined;
115-
117+
const dimNames = (meta._ARRAY_DIMENSIONS as string[] | undefined)
118+
?? (outVar.dimensionNames as string[] | undefined);
119+
116120
if (dimNames) {
117121
for (const dim of dimNames) {
118122
const dimArray = await zarr.open(group.resolve(dim), { kind: 'array' })

0 commit comments

Comments
 (0)