/** * Remote ZIP Reader - On-demand ZIP archive reading via HTTP Range requests * * This module provides efficient access to ZIP archives stored on remote servers. * Instead of downloading the entire archive, it uses HTTP Range requests to: * * 1. Read the End of Central Directory (EOCD) from the end of the file * 2. Read the Central Directory to get file metadata * 3. Read individual entries on demand * * This can dramatically reduce bandwidth usage when you only need a few files * from a large archive. * * @module */ import type { RandomAccessReader, HttpRangeReaderOptions } from "../io/random-access.js"; import { type ZipStringEncoding } from "../shared/text.js"; import type { ZipEntryInfo } from "../zip-spec/zip-entry-info.js"; /** * Options for RemoteZipReader */ export interface RemoteZipReaderOptions { /** * Password for encrypted entries. */ password?: string | Uint8Array; /** * Whether to decode file names as UTF-8. * @default true */ decodeStrings?: boolean; /** Optional string encoding for legacy (non-UTF8) names/comments. */ encoding?: ZipStringEncoding; /** * Abort signal for cancellation. */ signal?: AbortSignal; /** * Whether to validate CRC32 checksum after extraction. * @default false */ checkCrc32?: boolean; } /** * Options for extracting entries */ export interface ExtractOptions { /** * Password for encrypted entries (overrides constructor password). */ password?: string | Uint8Array; /** * Whether to validate CRC32 checksum after extraction. * Overrides the constructor option. */ checkCrc32?: boolean; /** * Progress callback for large file extraction. * Called with current bytes processed and total bytes. */ onprogress?: (current: number, total: number) => void; } /** * Options for reading raw (compressed) entry data. */ export interface RawEntryReadOptions { /** Abort signal for cancellation. */ signal?: AbortSignal; } /** * Options for streaming raw (compressed) entry data. */ export interface RawEntryStreamOptions extends RawEntryReadOptions { /** Chunk size for range reads. Default: 64 KiB */ chunkSize?: number; } /** * Options for opening a remote ZIP file via URL */ export interface RemoteZipOpenOptions extends RemoteZipReaderOptions, HttpRangeReaderOptions { } /** * Statistics about remote ZIP reading operations */ export interface RemoteZipStats { /** Total size of the ZIP file */ totalSize: number; /** Number of entries in the archive */ entryCount: number; /** HTTP request statistics (if using HttpRangeReader) */ http?: { requestCount: number; bytesDownloaded: number; downloadedPercent: number; }; } /** * Error thrown when CRC32 validation fails */ export { Crc32MismatchError } from "../shared/errors.js"; /** * Remote ZIP Reader * * Provides on-demand access to ZIP archives via random access reading. * Only downloads the parts of the archive that are actually needed. * * @example * ```ts * // Open a remote ZIP file * const reader = await RemoteZipReader.open("https://example.com/large-archive.zip"); * * // List entries without downloading file content * for (const entry of reader.getEntries()) { * console.log(entry.path, entry.uncompressedSize); * } * * // Extract just one file * const data = await reader.extract("important-file.txt"); * * // Check how much was downloaded * console.log(reader.getStats()); * * await reader.close(); * ``` */ export declare class RemoteZipReader { private readonly reader; private readonly options; private entries; private entryMap; private archiveComment; private initialized; private httpReader?; private readonly dataOffsetCache; private _hasEncryptedEntries; private _decoder?; private constructor(); private get _encodingDecoder(); /** * Open a remote ZIP file via URL. * * @param url - URL of the ZIP file * @param options - Reader options * @returns Initialized RemoteZipReader */ static open(url: string, options?: RemoteZipOpenOptions): Promise; /** * Create a RemoteZipReader from any RandomAccessReader. * * @param reader - A random access reader * @param options - Reader options * @returns Initialized RemoteZipReader */ static fromReader(reader: RandomAccessReader, options?: RemoteZipReaderOptions): Promise; /** * Initialize the reader by parsing EOCD and Central Directory. */ private init; /** * Read and parse the End of Central Directory record. */ private readEOCD; /** * Read and parse the Central Directory. */ private readCentralDirectory; /** * Get all entries in the ZIP file. */ getEntries(): readonly ZipEntryInfo[]; /** * Get entry by path. */ getEntry(path: string): ZipEntryInfo | undefined; /** * Check if entry exists. */ hasEntry(path: string): boolean; /** * Get the archive comment. */ getZipComment(): string; /** * Get raw (compressed) entry payload as a single Uint8Array. * * Notes: * - This returns the bytes as stored in the ZIP (compressed and possibly encrypted). * - The returned bytes do NOT include the local file header, extra field, or data descriptor. * - For large entries, prefer {@link getRawCompressedStream}. */ getRawCompressedData(pathOrEntry: string | ZipEntryInfo, options?: RawEntryReadOptions): Promise; /** * Get raw (compressed) entry payload as an async iterable. * * This is the most memory-efficient way to read raw entry bytes. */ getRawCompressedStream(pathOrEntry: string | ZipEntryInfo, options?: RawEntryStreamOptions): AsyncIterable | null; /** * Get a raw entry (metadata + compressed payload). */ getRawEntry(path: string, options?: RawEntryReadOptions): Promise<{ entry: ZipEntryInfo; compressedData: Uint8Array; } | null>; /** * Get a raw entry stream (metadata + async iterable compressed payload). */ getRawEntryStream(path: string, options?: RawEntryStreamOptions): { entry: ZipEntryInfo; compressedData: AsyncIterable; } | null; /** * List all file paths. */ listFiles(): string[]; /** * Get the number of file entries (excluding directories). */ getFileCount(): number; /** * Get the number of directory entries. */ getDirectoryCount(): number; /** * Filter entries by a predicate function. * * @param predicate - Function to test each entry * @returns Array of entries that pass the test */ filterEntries(predicate: (entry: ZipEntryInfo) => boolean): ZipEntryInfo[]; /** * Find entries matching a glob-like pattern. * Supports * (any characters) and ? (single character). * * @param pattern - Glob pattern (e.g., "*.txt", "folder/*", "**\/data.json") * @returns Array of matching entries */ findEntries(pattern: string): ZipEntryInfo[]; /** * Check if the archive contains encrypted entries. * Result is cached after first call for performance. */ hasEncryptedEntries(): boolean; /** * Extract a single file. * * @param path - File path within the archive * @param options - Extract options or password * @returns File data, or null if entry not found */ extract(path: string, options?: ExtractOptions | string | Uint8Array): Promise; /** * Extract a specific entry. * * @param entry - Entry to extract * @param options - Extract options or password * @returns File data */ extractEntry(entry: ZipEntryInfo, options?: ExtractOptions | string | Uint8Array): Promise; private processEntryCompressedData; /** * Normalize extract options from various input formats. */ private normalizeExtractOptions; /** * Extract all files from the archive. * This is a convenience method that calls extractMultiple with all file paths. * * @param options - Extract options or password * @returns Map of path to file data (directories are excluded) */ extractAll(options?: ExtractOptions | string | Uint8Array): Promise>; /** * Extract multiple entries efficiently. * Entries are sorted by offset to minimize seeks/requests. * * @param paths - File paths to extract * @param options - Extract options or password * @returns Map of path to file data */ extractMultiple(paths: string[], options?: ExtractOptions | string | Uint8Array): Promise>; /** * Iterate over entries with async callback. * * @param callback - Callback for each entry. Return false to stop iteration. * @param options - Extract options or password */ forEach(callback: (entry: ZipEntryInfo, getData: () => Promise) => Promise, options?: ExtractOptions | string | Uint8Array): Promise; /** * Async generator to iterate over entries one by one. * Useful for processing large archives without loading all entries into memory. * * @example * ```ts * for await (const { entry, getData } of reader.entriesGenerator()) { * if (entry.path.endsWith('.json')) { * const data = await getData(); * console.log(JSON.parse(new TextDecoder().decode(data))); * } * } * ``` */ entriesGenerator(options?: ExtractOptions | string | Uint8Array): AsyncGenerator<{ entry: ZipEntryInfo; getData: () => Promise; }>; /** * Check if a password is correct for an encrypted entry without extracting the full file. * This is much faster than extracting the file as it only reads the encryption header. * * @param path - File path within the archive * @param password - Password to check * @returns true if password is correct, false if incorrect, null if entry not found or not encrypted */ checkPassword(path: string, password: string | Uint8Array): Promise; /** * Check if a password is correct for an encrypted entry. * * @param entry - Entry to check * @param password - Password to check * @returns true if password is correct, false if incorrect, null if not encrypted */ checkEntryPassword(entry: ZipEntryInfo, password: string | Uint8Array): Promise; private getEntryDataOffset; /** * Extract to a WritableStream (streaming output). * Useful for large files to avoid loading the entire content into memory. * * @param path - File path within the archive * @param writable - WritableStream to write the extracted data to * @param options - Extract options or password * @returns true if extraction succeeded, false if entry not found */ extractToStream(path: string, writable: WritableStream, options?: ExtractOptions | string | Uint8Array): Promise; /** * Verify CRC32 for an entry without returning the data. * Useful for integrity checking. * * @param path - File path within the archive * @param options - Extract options (password if encrypted) * @returns true if CRC32 matches, throws Crc32MismatchError if not, null if entry not found */ verifyCrc32(path: string, options?: ExtractOptions | string | Uint8Array): Promise; /** * Get statistics about the reader's operations. */ getStats(): RemoteZipStats; /** * Close the reader and release resources. */ close(): Promise; }