module xmcl import { Readable } from "stream" ; import { Entry , fromBuffer , fromFd , open as yopen , ZipFile , ZipFileOptions , Options } from "yauzl" ; export type OpenTarget = string | Buffer | number ; param target param options export async function open ( target , options = { lazyEntries : true , autoClose : false } ) { return new Promise < ZipFile > ( ( resolve , reject ) => { function handleZip ( err , zipfile ) { if ( err || ! zipfile ) { reject ( err ?? new Error ( "Cannot open zip!" ) ) ; } else { resolve ( zipfile ) ; } } if ( typeof target === "string" ) { yopen ( target , options , handleZip ) ; } else if ( target instanceof Buffer ) { fromBuffer ( target , options , handleZip ) ; } else { fromFd ( target , options , handleZip ) ; } } ) ; } param zip param entry param options export function openEntryReadStream ( zip , entry , options ? ) { return new Promise < Readable > ( ( resolve , reject ) => { function handleStream ( err , stream ) { if ( err || ! stream ) { reject ( err ) ; } else { resolve ( stream ) ; } } if ( options ) { zip . openReadStream ( entry , options , handleStream ) ; } else { zip . openReadStream ( entry , handleStream ) ; } } ) ; } param zip param entry param options export async function readEntry ( zip , entry , options ? ) { const stream = await openEntryReadStream ( zip , entry , options ) ; const buffers = [ ] ; await new Promise ( ( resolve , reject ) => { stream . on ( "data" , ( chunk ) => { buffers . push ( chunk ) ; } ) ; stream . on ( "end" , resolve ) ; stream . on ( "error" , reject ) ; } ) ; return Buffer . concat ( buffers ) ; } param zip export async function * walkEntriesGenerator ( zip ) { let ended = false ; let error ; let resume = ( ) => { } ; let wait = new Promise < void > ( ( resolve ) => { resume = resolve ; } ) ; const entries = [ ] ; const onEntry = ( e ) => { entries . push ( e ) ; resume ( ) ; } ; const onEnd = ( ) => { ended = true ; resume ( ) ; } ; const onError = ( e ) => { error = e ; resume ( ) ; } ; zip . addListener ( "entry" , onEntry ) . addListener ( "end" , onEnd ) . addListener ( "error" , onError ) ; try { while ( ! ended ) { if ( zip . lazyEntries ) { zip . readEntry ( ) ; } await wait ; if ( error ) { throw error ; } while ( entries . length > 0 && ! ended ) { ended = ! ! ( yield entries . pop ( ) ! ) ; } wait = new Promise < void > ( ( resolve ) => { resume = resolve ; } ) ; } } finally { zip . removeListener ( "entry" , onEntry ) . removeListener ( "end" , onEnd ) . removeListener ( "error" , onError ) ; } } param zip param entries export async function filterEntries ( zip , entries ) { const remaining = new Set ( entries ) ; const record = { } ; for await ( const entry of walkEntriesGenerator ( zip ) ) { if ( remaining . size === 0 ) { break ; } if ( remaining . has ( entry . fileName ) ) { remaining . delete ( entry . fileName ) ; record [ entry . fileName ] = entry ; } } return entries . map ( ( name ) => record [ name ] ) ; } param zip param entryHandler export async function walkEntries ( zip , entryHandler ) { const itr = walkEntriesGenerator ( zip ) ; for await ( const entry of itr ) { const result = await entryHandler ( entry ) ; if ( result ) { break ; } } } export function getEntriesRecord ( entries ) { const record = { } ; for ( const entry of entries ) { record [ entry . fileName ] = entry ; } return record ; } param zipFile export async function readAllEntries ( zipFile ) { const entries = [ ] ; for await ( const entry of walkEntriesGenerator ( zipFile ) ) { entries . push ( entry ) ; } return entries ; }