@@ -16,6 +16,7 @@ import { SourceDimensionMap } from "../chunk";
1616type OmeZarrImageSourceProps = {
1717 location : Location < Readable > ;
1818 version ?: OmeZarrVersion ;
19+ loader : OmeZarrImageLoader ;
1920} ;
2021
2122type HttpOmeZarrImageSourceProps = {
@@ -34,18 +35,28 @@ export class OmeZarrImageSource {
3435 readonly location : Location < Readable > ;
3536 readonly version ?: OmeZarrVersion ;
3637
37- private loader_ ? : OmeZarrImageLoader ;
38+ private readonly loader_ : OmeZarrImageLoader ;
3839
40+ /**
41+ * Construct only via a static factory (`fromHttp`/`fromFileSystem`). A factory must
42+ * fully open the data up front — e.g. via {@link openLoader}, which resolves to a
43+ * ready loader or throws — and pass that loader in. The constructor therefore always
44+ * receives an opened loader, so a source instance is never in a half-open state
45+ * (`getLoader`/`getDimensions` cannot fail). To add a new factory, open the loader
46+ * first, then `new OmeZarrImageSource({ location, version, loader })`.
47+ */
3948 private constructor ( props : OmeZarrImageSourceProps ) {
4049 this . location = props . location ;
4150 this . version = props . version ;
51+ this . loader_ = props . loader ;
4252 }
4353
44- public async open ( ) : Promise < OmeZarrImageLoader > {
45- if ( this . loader_ ) return this . loader_ ;
46-
47- let zarrVersion = omeZarrToZarrVersion ( this . version ) ;
48- const root = await openGroup ( this . location , zarrVersion ) ;
54+ private static async openLoader (
55+ location : Location < Readable > ,
56+ version ?: OmeZarrVersion
57+ ) : Promise < OmeZarrImageLoader > {
58+ let zarrVersion = omeZarrToZarrVersion ( version ) ;
59+ const root = await openGroup ( location , zarrVersion ) ;
4960 const adaptedOmeImage = parseOmeZarrImage ( root . attrs ) ;
5061 const images = adaptedOmeImage . multiscales ;
5162 if ( images . length !== 1 ) {
@@ -61,7 +72,7 @@ export class OmeZarrImageSource {
6172 zarrVersion = omeZarrToZarrVersion ( adaptedOmeImage . originalVersion ) ;
6273 }
6374 const arrayParams = metadata . datasets . map ( ( d ) =>
64- createZarrArrayParams ( this . location , d . path , zarrVersion )
75+ createZarrArrayParams ( location , d . path , zarrVersion )
6576 ) ;
6677 const arrays = await Promise . all (
6778 arrayParams . map ( ( params ) => openArrayFromParams ( params ) )
@@ -74,24 +85,22 @@ export class OmeZarrImageSource {
7485 `Mismatch between number of axes (${ axes . length } ) and array shape (${ shape . length } )`
7586 ) ;
7687 }
77- this . loader_ = new OmeZarrImageLoader ( { metadata, arrays, arrayParams } ) ;
78- return this . loader_ ;
88+ return new OmeZarrImageLoader ( { metadata, arrays, arrayParams } ) ;
7989 }
8090
8191 public getDimensions ( ) : SourceDimensionMap {
82- if ( ! this . loader_ ) {
83- throw new Error (
84- "OmeZarrImageSource.getDimensions() requires the source to be opened first; " +
85- "use `await OmeZarrImageSource.fromHttp({ url })` or `await source.open()`."
86- ) ;
87- }
8892 return this . loader_ . getSourceDimensionMap ( ) ;
8993 }
9094
9195 public getChannelCount ( ) : number {
9296 return this . getDimensions ( ) . c ?. lods [ 0 ] . size ?? 1 ;
9397 }
9498
99+ /** The source's loader, for synchronous view creation by the chunk manager. */
100+ public getLoader ( ) : OmeZarrImageLoader {
101+ return this . loader_ ;
102+ }
103+
95104 /**
96105 * Creates and opens an OmeZarrImageSource from an HTTP(S) URL.
97106 *
@@ -101,13 +110,9 @@ export class OmeZarrImageSource {
101110 public static async fromHttp (
102111 props : HttpOmeZarrImageSourceProps
103112 ) : Promise < OmeZarrImageSource > {
104- const store = new FetchStore ( props . url ) ;
105- const source = new OmeZarrImageSource ( {
106- location : new Location ( store ) ,
107- version : props . version ,
108- } ) ;
109- await source . open ( ) ;
110- return source ;
113+ const location = new Location ( new FetchStore ( props . url ) ) ;
114+ const loader = await OmeZarrImageSource . openLoader ( location , props . version ) ;
115+ return new OmeZarrImageSource ( { location, version : props . version , loader } ) ;
111116 }
112117
113118 /**
@@ -122,12 +127,11 @@ export class OmeZarrImageSource {
122127 public static async fromFileSystem (
123128 props : FileSystemOmeZarrImageSourceProps
124129 ) : Promise < OmeZarrImageSource > {
125- const store = new WebFileSystemStore ( props . directory ) ;
126- const source = new OmeZarrImageSource ( {
127- location : new Location ( store , props . path ) ,
128- version : props . version ,
129- } ) ;
130- await source . open ( ) ;
131- return source ;
130+ const location = new Location (
131+ new WebFileSystemStore ( props . directory ) ,
132+ props . path
133+ ) ;
134+ const loader = await OmeZarrImageSource . openLoader ( location , props . version ) ;
135+ return new OmeZarrImageSource ( { location, version : props . version , loader } ) ;
132136 }
133137}
0 commit comments