zarrextra 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Wellcome Centre of Human Genetics
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # zarrextra
2
+
3
+ Extra utilities for working with zarr stores using zarrita.
4
+
5
+ This package provides helper functions and types for:
6
+ - Parsing zarr store contents into a tree structure
7
+ - Working with consolidated metadata
8
+ - Serializing zarr tree structures
9
+ - Result type for explicit error handling
10
+
11
+ ## Result Type
12
+
13
+ This package includes a `Result<T, E>` type inspired by Rust for explicit error handling. This is a custom implementation for simplicity and to avoid dependencies. We may review using an existing Result library (such as `neverthrow`) in the future, but for now this provides a lightweight solution.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install zarrextra
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```typescript
24
+ import { openExtraConsolidated } from 'zarrextra';
25
+ import * as zarr from 'zarrita';
26
+
27
+ const result = await openExtraConsolidated('https://example.com/store.zarr');
28
+ // or:
29
+ // const result = await openExtraConsolidated(new zarr.FetchStore('https://example.com/store.zarr'));
30
+ if (result.ok) {
31
+ const { zarritaStore, tree } = result.value;
32
+ ...
33
+ profit();
34
+ } else {
35
+ alert(result.error);
36
+ }
37
+ ```
38
+
39
+ ## API
40
+
41
+ See the TypeScript definitions for full API documentation.
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`zarrita`);c=s(c,1);var l=Symbol(`attrs`),u=Symbol(`.zarray`),d=e=>({ok:!0,value:e}),f=e=>({ok:!1,error:e}),p=e=>e.ok,m=e=>!e.ok,h=e=>{if(e.ok)return e.value;throw e.error instanceof Error?e.error:Error(String(e.error))},g=(e,t)=>e.ok?e.value:t,_=new TextDecoder;function v(e){return typeof e!=`string`}function y(e){return`contents`in e&&typeof e.contents==`function`}function b(e){return e.replace(/\/+$/,``)}function x(e){return typeof e==`string`?e:`[store instance]`}function S(e,t){let n=e===`/`?``:e;return t===`array`?[`${n}/zarr.json`,`${n}/.zarray`]:[`${n}/zarr.json`,`${n}/.zgroup`]}async function C(e,t,n){for(let r of S(t,n)){let t=await e.get(r);if(t)return JSON.parse(_.decode(t))}}async function w(e,t,n){return t===`/`?e.attrs:(n===`array`?await c.open(e.resolve(t),{kind:`array`}):await c.open(e.resolve(t),{kind:`group`})).attrs}function T(e,t){return e.path.split(`/`).filter(Boolean).length-t.path.split(`/`).filter(Boolean).length}async function E(e){let t=await c.open(e,{kind:`group`}),n={[l]:t.attrs},r=e.contents().sort(T);for(let{path:i,kind:a}of r){if(i===`/`)continue;let r=i.split(`/`).filter(Boolean),o=n;for(let[n,i]of r.entries()){if(!(i in o))if(n!==r.length-1)o[i]={};else{let s=`/${r.slice(0,n+1).join(`/`)}`,d=await w(t,s,a);if(a===`array`){let n=await C(e,s,`array`);if(!n)throw Error(`Missing array metadata for '${s}'`);o[i]={[l]:d,[u]:n,get:()=>c.open(t.resolve(s),{kind:`array`})}}else o[i]={[l]:d}}o=o[i]}}return n}async function D(e){let t=v(e)?e:new c.FetchStore(b(e));if(y(t))return t;try{return await c.withConsolidatedMetadata(t)}catch(e){try{return await c.withConsolidatedMetadata(t,{format:`v2`,metadataKey:`zmetadata`})}catch{throw e}}}async function O(e){try{let t=await D(e);return d({zarritaStore:t,tree:await E(t)})}catch(t){let n=t instanceof Error?t.message:String(t);return f(Error(`Failed to open zarr store '${x(e)}': ${n}`))}}function k(e){if(typeof e!=`object`||!e)return e;let t={};l in e&&e[l]&&(t._attrs=e[l]),u in e&&e[u]&&(t._zarray=e[u]);for(let n in e)if(Object.prototype.hasOwnProperty.call(e,n)){let r=e[n];typeof r==`function`?t[n]=`<function>`:t[n]=k(r)}return t}exports.ATTRS_KEY=l,exports.Err=f,exports.Ok=d,exports.ZARRAY_KEY=u,exports.isErr=m,exports.isOk=p,exports.openExtraConsolidated=O,exports.serializeZarrTree=k,exports.unwrap=h,exports.unwrapOr=g;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/types.ts","../src/result.ts","../src/index.ts"],"sourcesContent":["import type * as zarr from 'zarrita';\n\n/**\n * Supported inputs for opening a store.\n */\nexport type StoreReference = string | zarr.Readable;\n\ntype Store = zarr.Readable;\n\n/**\n * Zarr attributes type - a record of string keys to unknown values\n */\nexport type ZAttrsAny = Record<string, unknown>;\n\n/**\n * Symbol key for storing zarr attributes in the tree structure\n */\nexport const ATTRS_KEY = Symbol('attrs');\n\n/**\n * Symbol key for storing zarr array metadata\n */\nexport const ZARRAY_KEY = Symbol('.zarray');\n\n/**\n * Lazy zarr array type - represents a zarr array with a `get()` method for loading it,\n * and `.zarray` from consolidated metadata.\n */\nexport type LazyZarrArray<T extends zarr.DataType> = {\n [ATTRS_KEY]?: ZAttrsAny;\n [ZARRAY_KEY]: ZAttrsAny;\n get: () => Promise<zarr.Array<T>>;\n};\n\n/**\n * Zarr tree type\n *\n * This is a tree of zarr arrays and groups, with the leaves being lazy arrays.\n * It is used to represent the structure of the zarr store.\n * Leaf type subject to change.\n */\nexport interface ZarrTree {\n [ATTRS_KEY]?: ZAttrsAny;\n [key: string]: ZarrTree | LazyZarrArray<zarr.DataType>;\n}\n\n/**\n * Zarr v3 array node metadata\n */\nexport type ZarrV3ArrayNode = {\n shape: number[];\n data_type: string;\n chunk_grid: {\n name: string;\n configuration: {\n chunk_shape: number[];\n };\n };\n chunk_key_encoding: {\n name: string;\n configuration: {\n separator: string;\n };\n };\n fill_value: number | string | boolean;\n codecs: Array<{\n name: string;\n configuration?: Record<string, unknown>;\n }>;\n attributes: Record<string, unknown>;\n dimension_names: string[];\n zarr_format: number;\n node_type: 'array';\n storage_transformers: unknown[];\n};\n\n/**\n * Zarr v3 group node metadata\n */\nexport type ZarrV3GroupNode = {\n attributes: Record<string, unknown>;\n zarr_format: number;\n consolidated_metadata: {\n kind: string;\n must_understand: boolean;\n metadata: Record<string, unknown>;\n };\n node_type: 'group';\n};\n\n/**\n * Zarr v3 consolidated metadata structure (zarr.json)\n * The actual structure has metadata nested under consolidated_metadata.metadata\n * with path keys like \"images/blobs_image\", \"labels/blobs_labels\", etc.\n * Each entry can be either a group node or an array node.\n */\nexport type ZarrV3Metadata = {\n attributes: Record<string, unknown>;\n zarr_format: number;\n consolidated_metadata: {\n kind: string;\n must_understand: boolean;\n metadata: Record<string, ZarrV3GroupNode | ZarrV3ArrayNode>;\n };\n node_type: 'group';\n};\n\n/**\n * This type is liable to change in future - for now, it has `zarritaStore` which is the `ListableStore` from `zarrita`,\n * and `tree: ZarrTree` which has the object hierarchy as described in the consolidated metadata as a mostly \"Plain Old Javascript Object\",\n * but with (weakly typed) `Symbol`-keyed `attrs` & `.zarray` properties where available, and a `get()` on leaf nodes\n * for requesting array data.\n *\n * The use of `Symbol('attrs')` is intended to make these properties easy to access, but not appear when using `Object.keys()` etc.\n */\nexport type ConsolidatedStore = {\n zarritaStore: zarr.Listable<Store>;\n tree: ZarrTree;\n};\n","/**\n * Result type for explicit error handling without exceptions.\n * Inspired by Rust's Result<T, E>.\n *\n * This type is useful for operations that can fail, especially in zarr operations\n * where errors should be handled explicitly rather than thrown as exceptions.\n *\n * Note: This is a custom implementation for simplicity. We may review using\n * an existing Result library (such as neverthrow) in the future,\n * but for now this provides a lightweight, dependency-free solution.\n */\n\n/**\n * A Result type for explicit error handling without exceptions.\n * Inspired by Rust's Result<T, E>.\n */\nexport type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };\n\n/** Create a successful Result */\nexport const Ok = <T>(value: T): Result<T, never> => ({ ok: true, value });\n\n/** Create a failed Result */\nexport const Err = <E>(error: E): Result<never, E> => ({ ok: false, error });\n\n/** Type guard for Ok results */\nexport const isOk = <T, E>(result: Result<T, E>): result is { ok: true; value: T } => result.ok;\n\n/** Type guard for Err results */\nexport const isErr = <T, E>(result: Result<T, E>): result is { ok: false; error: E } => !result.ok;\n\n/**\n * Unwrap a Result, throwing if it's an error.\n * Use when you want to convert back to exception-based error handling.\n */\nexport const unwrap = <T, E>(result: Result<T, E>): T => {\n if (result.ok) return result.value;\n throw result.error instanceof Error ? result.error : new Error(String(result.error));\n};\n\n/**\n * Unwrap a Result with a default value for errors.\n */\nexport const unwrapOr = <T, E>(result: Result<T, E>, defaultValue: T): T => {\n return result.ok ? result.value : defaultValue;\n};\n","import * as zarr from 'zarrita';\nimport type {\n ConsolidatedStore,\n LazyZarrArray,\n StoreReference,\n ZarrTree,\n ZAttrsAny,\n} from './types';\nimport { ATTRS_KEY, ZARRAY_KEY } from './types';\nimport { Err, Ok, type Result } from './result';\n\nconst decoder = new TextDecoder();\n\nfunction isReadableStore(source: StoreReference): source is zarr.Readable {\n return typeof source !== 'string';\n}\n\nfunction isListableStore(store: zarr.Readable): store is zarr.Listable<zarr.Readable> {\n return 'contents' in store && typeof store.contents === 'function';\n}\n\nfunction normalizeStringSource(source: string): string {\n return source.replace(/\\/+$/, '');\n}\n\nfunction describeSource(source: StoreReference): string {\n return typeof source === 'string' ? source : '[store instance]';\n}\n\nfunction metadataKeysForPath(\n path: zarr.AbsolutePath,\n kind: 'array' | 'group'\n): zarr.AbsolutePath[] {\n const basePath = path === '/' ? '' : path;\n if (kind === 'array') {\n return [`${basePath}/zarr.json`, `${basePath}/.zarray`] as zarr.AbsolutePath[];\n }\n return [`${basePath}/zarr.json`, `${basePath}/.zgroup`] as zarr.AbsolutePath[];\n}\n\nasync function readMetadataJson(\n store: zarr.Readable,\n path: zarr.AbsolutePath,\n kind: 'array' | 'group'\n): Promise<ZAttrsAny | undefined> {\n for (const metadataKey of metadataKeysForPath(path, kind)) {\n const bytes = await store.get(metadataKey);\n if (bytes) {\n return JSON.parse(decoder.decode(bytes)) as ZAttrsAny;\n }\n }\n return undefined;\n}\n\nasync function readNodeAttrs(\n root: zarr.Group<zarr.Readable>,\n path: zarr.AbsolutePath,\n kind: 'array' | 'group'\n): Promise<ZAttrsAny> {\n if (path === '/') {\n return root.attrs;\n }\n\n const node =\n kind === 'array'\n ? await zarr.open(root.resolve(path), { kind: 'array' })\n : await zarr.open(root.resolve(path), { kind: 'group' });\n return node.attrs;\n}\n\nfunction sortContentsByDepth(\n a: { path: zarr.AbsolutePath },\n b: { path: zarr.AbsolutePath }\n): number {\n const depthA = a.path.split('/').filter(Boolean).length;\n const depthB = b.path.split('/').filter(Boolean).length;\n return depthA - depthB;\n}\n\nasync function parseStoreContents(store: zarr.Listable<zarr.Readable>): Promise<ZarrTree> {\n const root = await zarr.open(store, { kind: 'group' });\n const tree: ZarrTree = {\n [ATTRS_KEY]: root.attrs,\n };\n const contents = store.contents().sort(sortContentsByDepth);\n\n for (const { path, kind } of contents) {\n if (path === '/') continue;\n\n const pathParts = path.split('/').filter(Boolean);\n let currentNode = tree;\n\n for (const [index, part] of pathParts.entries()) {\n if (!(part in currentNode)) {\n const isLeaf = index === pathParts.length - 1;\n\n if (!isLeaf) {\n currentNode[part] = {};\n } else {\n const absolutePath = `/${pathParts.slice(0, index + 1).join('/')}` as zarr.AbsolutePath;\n const attrs = await readNodeAttrs(root, absolutePath, kind);\n\n if (kind === 'array') {\n const arrayMetadata = await readMetadataJson(store, absolutePath, 'array');\n if (!arrayMetadata) {\n throw new Error(`Missing array metadata for '${absolutePath}'`);\n }\n\n const leafNode: LazyZarrArray<zarr.DataType> = {\n [ATTRS_KEY]: attrs,\n [ZARRAY_KEY]: arrayMetadata,\n get: () => zarr.open(root.resolve(absolutePath), { kind: 'array' }),\n };\n currentNode[part] = leafNode;\n } else {\n currentNode[part] = {\n [ATTRS_KEY]: attrs,\n };\n }\n }\n }\n\n currentNode = currentNode[part] as ZarrTree;\n }\n }\n\n return tree;\n}\n\nasync function resolveListableStore(source: StoreReference): Promise<zarr.Listable<zarr.Readable>> {\n const store = isReadableStore(source)\n ? source\n : new zarr.FetchStore(normalizeStringSource(source));\n\n if (isListableStore(store)) {\n return store;\n }\n\n try {\n return await zarr.withConsolidatedMetadata(store as zarr.AsyncReadable);\n } catch (defaultError) {\n try {\n return await zarr.withConsolidatedMetadata(store as zarr.AsyncReadable, {\n format: 'v2',\n metadataKey: 'zmetadata',\n });\n } catch {\n throw defaultError;\n }\n }\n}\n\n/**\n * Open a zarr store or store-backed source and return a parsed tree representation.\n */\nexport async function openExtraConsolidated(\n source: StoreReference\n): Promise<Result<ConsolidatedStore>> {\n try {\n const zarritaStore = await resolveListableStore(source);\n const tree = await parseStoreContents(zarritaStore);\n return Ok({ zarritaStore, tree });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return Err(new Error(`Failed to open zarr store '${describeSource(source)}': ${errorMessage}`));\n }\n}\n\n/**\n * Deep clone a ZarrTree, converting Symbol-keyed attrs to string keys for serialization/debugging\n */\nexport function serializeZarrTree(obj: ZarrTree | unknown): unknown {\n if (obj === null || typeof obj !== 'object') return obj;\n\n const result: Record<string, unknown> = {};\n\n if (ATTRS_KEY in obj && obj[ATTRS_KEY]) {\n result._attrs = obj[ATTRS_KEY];\n }\n if (ZARRAY_KEY in obj && obj[ZARRAY_KEY]) {\n result._zarray = obj[ZARRAY_KEY];\n }\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n // @ts-expect-error - Indexing unknown object for serialization.\n const val = obj[key];\n if (typeof val === 'function') {\n result[key] = '<function>';\n } else {\n result[key] = serializeZarrTree(val);\n }\n }\n }\n\n return result;\n}\n\nexport type {\n StoreReference,\n ZarrTree,\n ConsolidatedStore,\n LazyZarrArray,\n ZAttrsAny,\n} from './types';\nexport { ATTRS_KEY, ZARRAY_KEY } from './types';\n\nexport type { Result } from './result';\nexport { Ok, Err, isOk, isErr, unwrap, unwrapOr } from './result';\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { ConsolidatedStore, StoreReference, ZarrTree } from './types';
2
+ import { Result } from './result';
3
+ /**
4
+ * Open a zarr store or store-backed source and return a parsed tree representation.
5
+ */
6
+ export declare function openExtraConsolidated(source: StoreReference): Promise<Result<ConsolidatedStore>>;
7
+ /**
8
+ * Deep clone a ZarrTree, converting Symbol-keyed attrs to string keys for serialization/debugging
9
+ */
10
+ export declare function serializeZarrTree(obj: ZarrTree | unknown): unknown;
11
+ export type { StoreReference, ZarrTree, ConsolidatedStore, LazyZarrArray, ZAttrsAny, } from './types';
12
+ export { ATTRS_KEY, ZARRAY_KEY } from './types';
13
+ export type { Result } from './result';
14
+ export { Ok, Err, isOk, isErr, unwrap, unwrapOr } from './result';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EAEjB,cAAc,EACd,QAAQ,EAET,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AA+IhD;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CASpC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,CAyBlE;AAED,YAAY,EACV,cAAc,EACd,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,SAAS,GACV,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEhD,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,106 @@
1
+ import * as e from "zarrita";
2
+ //#region src/types.ts
3
+ var t = Symbol("attrs"), n = Symbol(".zarray"), r = (e) => ({
4
+ ok: !0,
5
+ value: e
6
+ }), i = (e) => ({
7
+ ok: !1,
8
+ error: e
9
+ }), a = (e) => e.ok, o = (e) => !e.ok, s = (e) => {
10
+ if (e.ok) return e.value;
11
+ throw e.error instanceof Error ? e.error : Error(String(e.error));
12
+ }, c = (e, t) => e.ok ? e.value : t, l = new TextDecoder();
13
+ function u(e) {
14
+ return typeof e != "string";
15
+ }
16
+ function d(e) {
17
+ return "contents" in e && typeof e.contents == "function";
18
+ }
19
+ function f(e) {
20
+ return e.replace(/\/+$/, "");
21
+ }
22
+ function p(e) {
23
+ return typeof e == "string" ? e : "[store instance]";
24
+ }
25
+ function m(e, t) {
26
+ let n = e === "/" ? "" : e;
27
+ return t === "array" ? [`${n}/zarr.json`, `${n}/.zarray`] : [`${n}/zarr.json`, `${n}/.zgroup`];
28
+ }
29
+ async function h(e, t, n) {
30
+ for (let r of m(t, n)) {
31
+ let t = await e.get(r);
32
+ if (t) return JSON.parse(l.decode(t));
33
+ }
34
+ }
35
+ async function g(t, n, r) {
36
+ return n === "/" ? t.attrs : (r === "array" ? await e.open(t.resolve(n), { kind: "array" }) : await e.open(t.resolve(n), { kind: "group" })).attrs;
37
+ }
38
+ function _(e, t) {
39
+ return e.path.split("/").filter(Boolean).length - t.path.split("/").filter(Boolean).length;
40
+ }
41
+ async function v(r) {
42
+ let i = await e.open(r, { kind: "group" }), a = { [t]: i.attrs }, o = r.contents().sort(_);
43
+ for (let { path: s, kind: c } of o) {
44
+ if (s === "/") continue;
45
+ let o = s.split("/").filter(Boolean), l = a;
46
+ for (let [a, s] of o.entries()) {
47
+ if (!(s in l)) if (a !== o.length - 1) l[s] = {};
48
+ else {
49
+ let u = `/${o.slice(0, a + 1).join("/")}`, d = await g(i, u, c);
50
+ if (c === "array") {
51
+ let a = await h(r, u, "array");
52
+ if (!a) throw Error(`Missing array metadata for '${u}'`);
53
+ l[s] = {
54
+ [t]: d,
55
+ [n]: a,
56
+ get: () => e.open(i.resolve(u), { kind: "array" })
57
+ };
58
+ } else l[s] = { [t]: d };
59
+ }
60
+ l = l[s];
61
+ }
62
+ }
63
+ return a;
64
+ }
65
+ async function y(t) {
66
+ let n = u(t) ? t : new e.FetchStore(f(t));
67
+ if (d(n)) return n;
68
+ try {
69
+ return await e.withConsolidatedMetadata(n);
70
+ } catch (t) {
71
+ try {
72
+ return await e.withConsolidatedMetadata(n, {
73
+ format: "v2",
74
+ metadataKey: "zmetadata"
75
+ });
76
+ } catch {
77
+ throw t;
78
+ }
79
+ }
80
+ }
81
+ async function b(e) {
82
+ try {
83
+ let t = await y(e);
84
+ return r({
85
+ zarritaStore: t,
86
+ tree: await v(t)
87
+ });
88
+ } catch (t) {
89
+ let n = t instanceof Error ? t.message : String(t);
90
+ return i(/* @__PURE__ */ Error(`Failed to open zarr store '${p(e)}': ${n}`));
91
+ }
92
+ }
93
+ function x(e) {
94
+ if (typeof e != "object" || !e) return e;
95
+ let r = {};
96
+ t in e && e[t] && (r._attrs = e[t]), n in e && e[n] && (r._zarray = e[n]);
97
+ for (let t in e) if (Object.prototype.hasOwnProperty.call(e, t)) {
98
+ let n = e[t];
99
+ typeof n == "function" ? r[t] = "<function>" : r[t] = x(n);
100
+ }
101
+ return r;
102
+ }
103
+ //#endregion
104
+ export { t as ATTRS_KEY, i as Err, r as Ok, n as ZARRAY_KEY, o as isErr, a as isOk, b as openExtraConsolidated, x as serializeZarrTree, s as unwrap, c as unwrapOr };
105
+
106
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/types.ts","../src/result.ts","../src/index.ts"],"sourcesContent":["import type * as zarr from 'zarrita';\n\n/**\n * Supported inputs for opening a store.\n */\nexport type StoreReference = string | zarr.Readable;\n\ntype Store = zarr.Readable;\n\n/**\n * Zarr attributes type - a record of string keys to unknown values\n */\nexport type ZAttrsAny = Record<string, unknown>;\n\n/**\n * Symbol key for storing zarr attributes in the tree structure\n */\nexport const ATTRS_KEY = Symbol('attrs');\n\n/**\n * Symbol key for storing zarr array metadata\n */\nexport const ZARRAY_KEY = Symbol('.zarray');\n\n/**\n * Lazy zarr array type - represents a zarr array with a `get()` method for loading it,\n * and `.zarray` from consolidated metadata.\n */\nexport type LazyZarrArray<T extends zarr.DataType> = {\n [ATTRS_KEY]?: ZAttrsAny;\n [ZARRAY_KEY]: ZAttrsAny;\n get: () => Promise<zarr.Array<T>>;\n};\n\n/**\n * Zarr tree type\n *\n * This is a tree of zarr arrays and groups, with the leaves being lazy arrays.\n * It is used to represent the structure of the zarr store.\n * Leaf type subject to change.\n */\nexport interface ZarrTree {\n [ATTRS_KEY]?: ZAttrsAny;\n [key: string]: ZarrTree | LazyZarrArray<zarr.DataType>;\n}\n\n/**\n * Zarr v3 array node metadata\n */\nexport type ZarrV3ArrayNode = {\n shape: number[];\n data_type: string;\n chunk_grid: {\n name: string;\n configuration: {\n chunk_shape: number[];\n };\n };\n chunk_key_encoding: {\n name: string;\n configuration: {\n separator: string;\n };\n };\n fill_value: number | string | boolean;\n codecs: Array<{\n name: string;\n configuration?: Record<string, unknown>;\n }>;\n attributes: Record<string, unknown>;\n dimension_names: string[];\n zarr_format: number;\n node_type: 'array';\n storage_transformers: unknown[];\n};\n\n/**\n * Zarr v3 group node metadata\n */\nexport type ZarrV3GroupNode = {\n attributes: Record<string, unknown>;\n zarr_format: number;\n consolidated_metadata: {\n kind: string;\n must_understand: boolean;\n metadata: Record<string, unknown>;\n };\n node_type: 'group';\n};\n\n/**\n * Zarr v3 consolidated metadata structure (zarr.json)\n * The actual structure has metadata nested under consolidated_metadata.metadata\n * with path keys like \"images/blobs_image\", \"labels/blobs_labels\", etc.\n * Each entry can be either a group node or an array node.\n */\nexport type ZarrV3Metadata = {\n attributes: Record<string, unknown>;\n zarr_format: number;\n consolidated_metadata: {\n kind: string;\n must_understand: boolean;\n metadata: Record<string, ZarrV3GroupNode | ZarrV3ArrayNode>;\n };\n node_type: 'group';\n};\n\n/**\n * This type is liable to change in future - for now, it has `zarritaStore` which is the `ListableStore` from `zarrita`,\n * and `tree: ZarrTree` which has the object hierarchy as described in the consolidated metadata as a mostly \"Plain Old Javascript Object\",\n * but with (weakly typed) `Symbol`-keyed `attrs` & `.zarray` properties where available, and a `get()` on leaf nodes\n * for requesting array data.\n *\n * The use of `Symbol('attrs')` is intended to make these properties easy to access, but not appear when using `Object.keys()` etc.\n */\nexport type ConsolidatedStore = {\n zarritaStore: zarr.Listable<Store>;\n tree: ZarrTree;\n};\n","/**\n * Result type for explicit error handling without exceptions.\n * Inspired by Rust's Result<T, E>.\n *\n * This type is useful for operations that can fail, especially in zarr operations\n * where errors should be handled explicitly rather than thrown as exceptions.\n *\n * Note: This is a custom implementation for simplicity. We may review using\n * an existing Result library (such as neverthrow) in the future,\n * but for now this provides a lightweight, dependency-free solution.\n */\n\n/**\n * A Result type for explicit error handling without exceptions.\n * Inspired by Rust's Result<T, E>.\n */\nexport type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };\n\n/** Create a successful Result */\nexport const Ok = <T>(value: T): Result<T, never> => ({ ok: true, value });\n\n/** Create a failed Result */\nexport const Err = <E>(error: E): Result<never, E> => ({ ok: false, error });\n\n/** Type guard for Ok results */\nexport const isOk = <T, E>(result: Result<T, E>): result is { ok: true; value: T } => result.ok;\n\n/** Type guard for Err results */\nexport const isErr = <T, E>(result: Result<T, E>): result is { ok: false; error: E } => !result.ok;\n\n/**\n * Unwrap a Result, throwing if it's an error.\n * Use when you want to convert back to exception-based error handling.\n */\nexport const unwrap = <T, E>(result: Result<T, E>): T => {\n if (result.ok) return result.value;\n throw result.error instanceof Error ? result.error : new Error(String(result.error));\n};\n\n/**\n * Unwrap a Result with a default value for errors.\n */\nexport const unwrapOr = <T, E>(result: Result<T, E>, defaultValue: T): T => {\n return result.ok ? result.value : defaultValue;\n};\n","import * as zarr from 'zarrita';\nimport type {\n ConsolidatedStore,\n LazyZarrArray,\n StoreReference,\n ZarrTree,\n ZAttrsAny,\n} from './types';\nimport { ATTRS_KEY, ZARRAY_KEY } from './types';\nimport { Err, Ok, type Result } from './result';\n\nconst decoder = new TextDecoder();\n\nfunction isReadableStore(source: StoreReference): source is zarr.Readable {\n return typeof source !== 'string';\n}\n\nfunction isListableStore(store: zarr.Readable): store is zarr.Listable<zarr.Readable> {\n return 'contents' in store && typeof store.contents === 'function';\n}\n\nfunction normalizeStringSource(source: string): string {\n return source.replace(/\\/+$/, '');\n}\n\nfunction describeSource(source: StoreReference): string {\n return typeof source === 'string' ? source : '[store instance]';\n}\n\nfunction metadataKeysForPath(\n path: zarr.AbsolutePath,\n kind: 'array' | 'group'\n): zarr.AbsolutePath[] {\n const basePath = path === '/' ? '' : path;\n if (kind === 'array') {\n return [`${basePath}/zarr.json`, `${basePath}/.zarray`] as zarr.AbsolutePath[];\n }\n return [`${basePath}/zarr.json`, `${basePath}/.zgroup`] as zarr.AbsolutePath[];\n}\n\nasync function readMetadataJson(\n store: zarr.Readable,\n path: zarr.AbsolutePath,\n kind: 'array' | 'group'\n): Promise<ZAttrsAny | undefined> {\n for (const metadataKey of metadataKeysForPath(path, kind)) {\n const bytes = await store.get(metadataKey);\n if (bytes) {\n return JSON.parse(decoder.decode(bytes)) as ZAttrsAny;\n }\n }\n return undefined;\n}\n\nasync function readNodeAttrs(\n root: zarr.Group<zarr.Readable>,\n path: zarr.AbsolutePath,\n kind: 'array' | 'group'\n): Promise<ZAttrsAny> {\n if (path === '/') {\n return root.attrs;\n }\n\n const node =\n kind === 'array'\n ? await zarr.open(root.resolve(path), { kind: 'array' })\n : await zarr.open(root.resolve(path), { kind: 'group' });\n return node.attrs;\n}\n\nfunction sortContentsByDepth(\n a: { path: zarr.AbsolutePath },\n b: { path: zarr.AbsolutePath }\n): number {\n const depthA = a.path.split('/').filter(Boolean).length;\n const depthB = b.path.split('/').filter(Boolean).length;\n return depthA - depthB;\n}\n\nasync function parseStoreContents(store: zarr.Listable<zarr.Readable>): Promise<ZarrTree> {\n const root = await zarr.open(store, { kind: 'group' });\n const tree: ZarrTree = {\n [ATTRS_KEY]: root.attrs,\n };\n const contents = store.contents().sort(sortContentsByDepth);\n\n for (const { path, kind } of contents) {\n if (path === '/') continue;\n\n const pathParts = path.split('/').filter(Boolean);\n let currentNode = tree;\n\n for (const [index, part] of pathParts.entries()) {\n if (!(part in currentNode)) {\n const isLeaf = index === pathParts.length - 1;\n\n if (!isLeaf) {\n currentNode[part] = {};\n } else {\n const absolutePath = `/${pathParts.slice(0, index + 1).join('/')}` as zarr.AbsolutePath;\n const attrs = await readNodeAttrs(root, absolutePath, kind);\n\n if (kind === 'array') {\n const arrayMetadata = await readMetadataJson(store, absolutePath, 'array');\n if (!arrayMetadata) {\n throw new Error(`Missing array metadata for '${absolutePath}'`);\n }\n\n const leafNode: LazyZarrArray<zarr.DataType> = {\n [ATTRS_KEY]: attrs,\n [ZARRAY_KEY]: arrayMetadata,\n get: () => zarr.open(root.resolve(absolutePath), { kind: 'array' }),\n };\n currentNode[part] = leafNode;\n } else {\n currentNode[part] = {\n [ATTRS_KEY]: attrs,\n };\n }\n }\n }\n\n currentNode = currentNode[part] as ZarrTree;\n }\n }\n\n return tree;\n}\n\nasync function resolveListableStore(source: StoreReference): Promise<zarr.Listable<zarr.Readable>> {\n const store = isReadableStore(source)\n ? source\n : new zarr.FetchStore(normalizeStringSource(source));\n\n if (isListableStore(store)) {\n return store;\n }\n\n try {\n return await zarr.withConsolidatedMetadata(store as zarr.AsyncReadable);\n } catch (defaultError) {\n try {\n return await zarr.withConsolidatedMetadata(store as zarr.AsyncReadable, {\n format: 'v2',\n metadataKey: 'zmetadata',\n });\n } catch {\n throw defaultError;\n }\n }\n}\n\n/**\n * Open a zarr store or store-backed source and return a parsed tree representation.\n */\nexport async function openExtraConsolidated(\n source: StoreReference\n): Promise<Result<ConsolidatedStore>> {\n try {\n const zarritaStore = await resolveListableStore(source);\n const tree = await parseStoreContents(zarritaStore);\n return Ok({ zarritaStore, tree });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return Err(new Error(`Failed to open zarr store '${describeSource(source)}': ${errorMessage}`));\n }\n}\n\n/**\n * Deep clone a ZarrTree, converting Symbol-keyed attrs to string keys for serialization/debugging\n */\nexport function serializeZarrTree(obj: ZarrTree | unknown): unknown {\n if (obj === null || typeof obj !== 'object') return obj;\n\n const result: Record<string, unknown> = {};\n\n if (ATTRS_KEY in obj && obj[ATTRS_KEY]) {\n result._attrs = obj[ATTRS_KEY];\n }\n if (ZARRAY_KEY in obj && obj[ZARRAY_KEY]) {\n result._zarray = obj[ZARRAY_KEY];\n }\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n // @ts-expect-error - Indexing unknown object for serialization.\n const val = obj[key];\n if (typeof val === 'function') {\n result[key] = '<function>';\n } else {\n result[key] = serializeZarrTree(val);\n }\n }\n }\n\n return result;\n}\n\nexport type {\n StoreReference,\n ZarrTree,\n ConsolidatedStore,\n LazyZarrArray,\n ZAttrsAny,\n} from './types';\nexport { ATTRS_KEY, ZARRAY_KEY } from './types';\n\nexport type { Result } from './result';\nexport { Ok, Err, isOk, isErr, unwrap, unwrapOr } from './result';\n"],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Result type for explicit error handling without exceptions.
3
+ * Inspired by Rust's Result<T, E>.
4
+ *
5
+ * This type is useful for operations that can fail, especially in zarr operations
6
+ * where errors should be handled explicitly rather than thrown as exceptions.
7
+ *
8
+ * Note: This is a custom implementation for simplicity. We may review using
9
+ * an existing Result library (such as neverthrow) in the future,
10
+ * but for now this provides a lightweight, dependency-free solution.
11
+ */
12
+ /**
13
+ * A Result type for explicit error handling without exceptions.
14
+ * Inspired by Rust's Result<T, E>.
15
+ */
16
+ export type Result<T, E = Error> = {
17
+ ok: true;
18
+ value: T;
19
+ } | {
20
+ ok: false;
21
+ error: E;
22
+ };
23
+ /** Create a successful Result */
24
+ export declare const Ok: <T>(value: T) => Result<T, never>;
25
+ /** Create a failed Result */
26
+ export declare const Err: <E>(error: E) => Result<never, E>;
27
+ /** Type guard for Ok results */
28
+ export declare const isOk: <T, E>(result: Result<T, E>) => result is {
29
+ ok: true;
30
+ value: T;
31
+ };
32
+ /** Type guard for Err results */
33
+ export declare const isErr: <T, E>(result: Result<T, E>) => result is {
34
+ ok: false;
35
+ error: E;
36
+ };
37
+ /**
38
+ * Unwrap a Result, throwing if it's an error.
39
+ * Use when you want to convert back to exception-based error handling.
40
+ */
41
+ export declare const unwrap: <T, E>(result: Result<T, E>) => T;
42
+ /**
43
+ * Unwrap a Result with a default value for errors.
44
+ */
45
+ export declare const unwrapOr: <T, E>(result: Result<T, E>, defaultValue: T) => T;
46
+ //# sourceMappingURL=result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;GAGG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC;AAEpF,iCAAiC;AACjC,eAAO,MAAM,EAAE,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,CAAC,EAAE,KAAK,CAA0B,CAAC;AAE3E,6BAA6B;AAC7B,eAAO,MAAM,GAAG,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,KAAK,EAAE,CAAC,CAA2B,CAAC;AAE7E,gCAAgC;AAChC,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,KAAG,MAAM,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAe,CAAC;AAEhG,iCAAiC;AACjC,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,KAAG,MAAM,IAAI;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAgB,CAAC;AAEnG;;;GAGG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,KAAG,CAGnD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,CAAC,KAAG,CAEtE,CAAC"}
@@ -0,0 +1,110 @@
1
+ import type * as zarr from 'zarrita';
2
+ /**
3
+ * Supported inputs for opening a store.
4
+ */
5
+ export type StoreReference = string | zarr.Readable;
6
+ type Store = zarr.Readable;
7
+ /**
8
+ * Zarr attributes type - a record of string keys to unknown values
9
+ */
10
+ export type ZAttrsAny = Record<string, unknown>;
11
+ /**
12
+ * Symbol key for storing zarr attributes in the tree structure
13
+ */
14
+ export declare const ATTRS_KEY: unique symbol;
15
+ /**
16
+ * Symbol key for storing zarr array metadata
17
+ */
18
+ export declare const ZARRAY_KEY: unique symbol;
19
+ /**
20
+ * Lazy zarr array type - represents a zarr array with a `get()` method for loading it,
21
+ * and `.zarray` from consolidated metadata.
22
+ */
23
+ export type LazyZarrArray<T extends zarr.DataType> = {
24
+ [ATTRS_KEY]?: ZAttrsAny;
25
+ [ZARRAY_KEY]: ZAttrsAny;
26
+ get: () => Promise<zarr.Array<T>>;
27
+ };
28
+ /**
29
+ * Zarr tree type
30
+ *
31
+ * This is a tree of zarr arrays and groups, with the leaves being lazy arrays.
32
+ * It is used to represent the structure of the zarr store.
33
+ * Leaf type subject to change.
34
+ */
35
+ export interface ZarrTree {
36
+ [ATTRS_KEY]?: ZAttrsAny;
37
+ [key: string]: ZarrTree | LazyZarrArray<zarr.DataType>;
38
+ }
39
+ /**
40
+ * Zarr v3 array node metadata
41
+ */
42
+ export type ZarrV3ArrayNode = {
43
+ shape: number[];
44
+ data_type: string;
45
+ chunk_grid: {
46
+ name: string;
47
+ configuration: {
48
+ chunk_shape: number[];
49
+ };
50
+ };
51
+ chunk_key_encoding: {
52
+ name: string;
53
+ configuration: {
54
+ separator: string;
55
+ };
56
+ };
57
+ fill_value: number | string | boolean;
58
+ codecs: Array<{
59
+ name: string;
60
+ configuration?: Record<string, unknown>;
61
+ }>;
62
+ attributes: Record<string, unknown>;
63
+ dimension_names: string[];
64
+ zarr_format: number;
65
+ node_type: 'array';
66
+ storage_transformers: unknown[];
67
+ };
68
+ /**
69
+ * Zarr v3 group node metadata
70
+ */
71
+ export type ZarrV3GroupNode = {
72
+ attributes: Record<string, unknown>;
73
+ zarr_format: number;
74
+ consolidated_metadata: {
75
+ kind: string;
76
+ must_understand: boolean;
77
+ metadata: Record<string, unknown>;
78
+ };
79
+ node_type: 'group';
80
+ };
81
+ /**
82
+ * Zarr v3 consolidated metadata structure (zarr.json)
83
+ * The actual structure has metadata nested under consolidated_metadata.metadata
84
+ * with path keys like "images/blobs_image", "labels/blobs_labels", etc.
85
+ * Each entry can be either a group node or an array node.
86
+ */
87
+ export type ZarrV3Metadata = {
88
+ attributes: Record<string, unknown>;
89
+ zarr_format: number;
90
+ consolidated_metadata: {
91
+ kind: string;
92
+ must_understand: boolean;
93
+ metadata: Record<string, ZarrV3GroupNode | ZarrV3ArrayNode>;
94
+ };
95
+ node_type: 'group';
96
+ };
97
+ /**
98
+ * This type is liable to change in future - for now, it has `zarritaStore` which is the `ListableStore` from `zarrita`,
99
+ * and `tree: ZarrTree` which has the object hierarchy as described in the consolidated metadata as a mostly "Plain Old Javascript Object",
100
+ * but with (weakly typed) `Symbol`-keyed `attrs` & `.zarray` properties where available, and a `get()` on leaf nodes
101
+ * for requesting array data.
102
+ *
103
+ * The use of `Symbol('attrs')` is intended to make these properties easy to access, but not appear when using `Object.keys()` etc.
104
+ */
105
+ export type ConsolidatedStore = {
106
+ zarritaStore: zarr.Listable<Store>;
107
+ tree: ZarrTree;
108
+ };
109
+ export {};
110
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { z } from 'zod';
2
+ import { ZarrV3ArrayNode } from './types';
3
+ /**
4
+ * Zod schema for v2 zarray metadata
5
+ * Handles both regular arrays and scalar arrays (empty shape/chunks)
6
+ */
7
+ export declare const v2ZarraySchema: z.ZodObject<{
8
+ shape: z.ZodArray<z.ZodNumber>;
9
+ chunks: z.ZodArray<z.ZodNumber>;
10
+ dtype: z.ZodString;
11
+ filters: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodObject<{
12
+ id: z.ZodString;
13
+ configuration: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
14
+ }, z.core.$catchall<z.ZodUnknown>>>>>;
15
+ compressor: z.ZodNullable<z.ZodOptional<z.ZodObject<{
16
+ id: z.ZodString;
17
+ configuration: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
18
+ }, z.core.$catchall<z.ZodUnknown>>>>;
19
+ fill_value: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString, z.ZodBoolean, z.ZodNull]>>;
20
+ dimension_names: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodString>>>;
21
+ zarr_format: z.ZodOptional<z.ZodNumber>;
22
+ }, z.core.$strip>;
23
+ /**
24
+ * Zod schema for v3 zarray metadata (for hybrid formats)
25
+ * Handles both regular arrays and scalar arrays (empty shape/chunk_shape)
26
+ */
27
+ export declare const v3ZarraySchema: z.ZodObject<{
28
+ shape: z.ZodArray<z.ZodNumber>;
29
+ data_type: z.ZodUnion<readonly [z.ZodLiteral<"bool">, z.ZodLiteral<"int8">, z.ZodLiteral<"int16">, z.ZodLiteral<"int32">, z.ZodLiteral<"int64">, z.ZodLiteral<"uint8">, z.ZodLiteral<"uint16">, z.ZodLiteral<"uint32">, z.ZodLiteral<"uint64">, z.ZodLiteral<"float16">, z.ZodLiteral<"float32">, z.ZodLiteral<"float64">, z.ZodLiteral<"complex64">, z.ZodLiteral<"complex128">, z.ZodLiteral<"string">, z.ZodString]>;
30
+ chunk_grid: z.ZodObject<{
31
+ name: z.ZodString;
32
+ configuration: z.ZodObject<{
33
+ chunk_shape: z.ZodArray<z.ZodNumber>;
34
+ }, z.core.$strip>;
35
+ }, z.core.$strip>;
36
+ chunk_key_encoding: z.ZodOptional<z.ZodObject<{
37
+ name: z.ZodString;
38
+ configuration: z.ZodObject<{
39
+ separator: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<"/">, z.ZodLiteral<".">]>>;
40
+ }, z.core.$strip>;
41
+ }, z.core.$strip>>;
42
+ fill_value: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString, z.ZodBoolean]>>;
43
+ codecs: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodObject<{
44
+ name: z.ZodString;
45
+ configuration: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
46
+ }, z.core.$strip>>>>;
47
+ dimension_names: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodString>>>;
48
+ storage_transformers: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodObject<{
49
+ name: z.ZodString;
50
+ configuration: z.ZodOptional<z.ZodUnknown>;
51
+ }, z.core.$strip>>>>;
52
+ zarr_format: z.ZodOptional<z.ZodNumber>;
53
+ node_type: z.ZodOptional<z.ZodLiteral<"array">>;
54
+ attributes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodJSONSchema>>;
55
+ }, z.core.$strip>;
56
+ /**
57
+ * Validate and convert v2 zarray metadata to v3 format
58
+ * Handles both pure v2 format (with chunks, dtype) and hybrid formats
59
+ * @throws Error if required fields are missing or invalid
60
+ */
61
+ export declare function validateAndConvertV2Zarray(zarray: unknown, path: string): ZarrV3ArrayNode;
62
+ /**
63
+ * Validate v3 zarray metadata (for hybrid formats that already have v3 fields)
64
+ * @throws Error if required fields are missing or invalid
65
+ */
66
+ export declare function validateV3Zarray(zarray: unknown, path: string): ZarrV3ArrayNode;
67
+ //# sourceMappingURL=zarrSchema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zarrSchema.d.ts","sourceRoot":"","sources":["../src/zarrSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA+H/C;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;iBAYxB,CAAC;AA0BJ;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAexB,CAAC;AAEJ;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,CAsEzF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,CA8B/E"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "zarrextra",
3
+ "version": "0.1.0",
4
+ "description": "Extra utilities for working with zarr stores using zarrita",
5
+ "type": "module",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "dependencies": {
21
+ "zarrita": "^0.7.1",
22
+ "zod": "^4.1.13"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^22.10.5",
26
+ "typescript": "^5.7.3",
27
+ "vite": "^8.0.8",
28
+ "vite-plugin-dts": "^4.5.0",
29
+ "vitest": "^4.1.4"
30
+ },
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/Taylor-CCB-Group/SpatialData.js.git",
35
+ "directory": "packages/zarrextra"
36
+ },
37
+ "scripts": {
38
+ "build": "vite build",
39
+ "dev": "vite build --watch",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest",
42
+ "test:coverage": "vitest run --coverage"
43
+ }
44
+ }