11// SPDX-License-Identifier: LGPL-3.0-or-later
2- import { FileSystem , Inode , Sync , type UsageInfo } from '@zenfs/core' ;
2+ import { FileSystem , Inode , type UsageInfo } from '@zenfs/core' ;
33import type { Backend } from '@zenfs/core/backends/backend.js' ;
44import { Readonly } from '@zenfs/core/mixins/readonly.js' ;
55import { parse } from '@zenfs/core/path.js' ;
6- import { S_IFDIR } from '@zenfs/core/vfs/constants.js' ;
6+ import { S_IFDIR , S_IFREG } from '@zenfs/core/vfs/constants.js' ;
77import { withErrno } from 'kerium' ;
88import { err } from 'kerium/log' ;
99import { _throw } from 'utilium' ;
@@ -63,7 +63,7 @@ export interface ZipOptions<TBuffer extends ArrayBufferLike = ArrayBuffer> {
6363 * - Stream it out to a location.
6464 * This isn't that bad, so we might do this at a later date.
6565 */
66- export class ZipFS < TBuffer extends ArrayBufferLike = ArrayBuffer > extends Readonly ( Sync ( FileSystem ) ) {
66+ export class ZipFS < TBuffer extends ArrayBufferLike = ArrayBuffer > extends Readonly ( FileSystem ) {
6767 protected files : Map < string , FileEntry < TBuffer > > = new Map ( ) ;
6868 protected directories : Map < string , Set < string > > = new Map ( ) ;
6969
@@ -91,8 +91,9 @@ export class ZipFS<TBuffer extends ArrayBufferLike = ArrayBuffer> extends Readon
9191 const cdEnd = ptr + this . eocd . size ;
9292
9393 while ( ptr < cdEnd ) {
94- const entryData = await this . data . get ( ptr , FileEntry . size ) ;
95- const cd = new FileEntry < TBuffer > ( entryData . buffer , entryData . byteOffset ) ;
94+ const cd = await FileEntry . from < TBuffer > ( this . data , ptr ) ;
95+
96+ if ( ! this . lazy ) await cd . loadContents ( ) ;
9697 /* Paths must be absolute,
9798 yet zip file paths are always relative to the zip root.
9899 So we prepend '/' and call it a day. */
@@ -132,7 +133,8 @@ export class ZipFS<TBuffer extends ArrayBufferLike = ArrayBuffer> extends Readon
132133
133134 public constructor (
134135 public label : string ,
135- protected data : ZipDataSource < TBuffer >
136+ protected data : ZipDataSource < TBuffer > ,
137+ public readonly lazy : boolean = false
136138 ) {
137139 super ( 0x207a6970 , 'zipfs' ) ;
138140 }
@@ -144,6 +146,10 @@ export class ZipFS<TBuffer extends ArrayBufferLike = ArrayBuffer> extends Readon
144146 } ;
145147 }
146148
149+ public async stat ( path : string ) : Promise < Inode > {
150+ return this . statSync ( path ) ;
151+ }
152+
147153 public statSync ( path : string ) : Inode {
148154 // The EOCD/Header does not track directories, so it does not exist in `entries`
149155 if ( this . directories . has ( path ) ) {
@@ -161,27 +167,54 @@ export class ZipFS<TBuffer extends ArrayBufferLike = ArrayBuffer> extends Readon
161167
162168 if ( ! entry ) throw withErrno ( 'ENOENT' ) ;
163169
164- return entry . inode ;
170+ return new Inode ( {
171+ mode : 0o555 | ( entry . isDirectory ? S_IFDIR : S_IFREG ) ,
172+ size : entry . uncompressedSize ,
173+ mtimeMs : entry . lastModified . getTime ( ) ,
174+ } ) ;
175+ }
176+
177+ public async readdir ( path : string ) : Promise < string [ ] > {
178+ const inode = await this . stat ( path ) ;
179+ if ( ! ( inode . mode & S_IFDIR ) ) throw withErrno ( 'ENOTDIR' ) ;
180+
181+ const entries = this . directories . get ( path ) ;
182+ if ( ! entries ) throw withErrno ( 'ENODATA' ) ;
183+
184+ return Array . from ( entries ) ;
165185 }
166186
167187 public readdirSync ( path : string ) : string [ ] {
168188 const inode = this . statSync ( path ) ;
169-
170189 if ( ! ( inode . mode & S_IFDIR ) ) throw withErrno ( 'ENOTDIR' ) ;
171190
172191 const entries = this . directories . get ( path ) ;
173-
174192 if ( ! entries ) throw withErrno ( 'ENODATA' ) ;
175193
176194 return Array . from ( entries ) ;
177195 }
178196
197+ public async read ( path : string , buffer : Uint8Array , offset : number , end : number ) : Promise < void > {
198+ if ( this . directories . has ( path ) ) throw withErrno ( 'EISDIR' ) ;
199+
200+ const file = this . files . get ( path ) ?? _throw ( withErrno ( 'ENOENT' ) ) ;
201+
202+ if ( ! file . contents ) await file . loadContents ( ) ;
203+
204+ buffer . set ( file . contents . subarray ( offset , end ) ) ;
205+ }
206+
179207 public readSync ( path : string , buffer : Uint8Array , offset : number , end : number ) : void {
180208 if ( this . directories . has ( path ) ) throw withErrno ( 'EISDIR' ) ;
181209
182- const { contents : data } = this . files . get ( path ) ?? _throw ( withErrno ( 'ENOENT' ) ) ;
210+ const file = this . files . get ( path ) ?? _throw ( withErrno ( 'ENOENT' ) ) ;
211+
212+ if ( ! file . contents ) {
213+ void file . loadContents ( ) ;
214+ throw withErrno ( 'EAGAIN' ) ;
215+ }
183216
184- buffer . set ( data . subarray ( offset , end ) ) ;
217+ buffer . set ( file . contents . subarray ( offset , end ) ) ;
185218 }
186219}
187220
@@ -278,8 +311,8 @@ const _Zip = {
278311 return true ;
279312 } ,
280313
281- create < TBuffer extends ArrayBufferLike = ArrayBuffer > ( { name , data } : ZipOptions < TBuffer > ) : ZipFS < TBuffer > {
282- return new ZipFS < TBuffer > ( name ?? '' , getSource ( data ) ) ;
314+ create < TBuffer extends ArrayBufferLike = ArrayBuffer > ( opt : ZipOptions < TBuffer > ) : ZipFS < TBuffer > {
315+ return new ZipFS < TBuffer > ( opt . name ?? '' , getSource ( opt . data ) , opt . lazy ) ;
283316 } ,
284317} satisfies Backend < ZipFS , ZipOptions > ;
285318type _Zip = typeof _Zip ;
0 commit comments