import type { SmartArchive } from './classes.smartarchive.js'; import type { TSupportedMime } from './interfaces.js'; import * as plugins from './plugins.js'; /** * Type for decompression streams */ export type TDecompressionStream = | plugins.stream.Transform | plugins.stream.Duplex | plugins.tarStream.Extract; /** * Result of archive analysis */ export interface IAnalyzedResult { fileType: plugins.fileType.FileTypeResult | undefined; isArchive: boolean; resultStream: plugins.smartstream.SmartDuplex; decompressionStream: TDecompressionStream; } /** * Analyzes archive streams to detect format and provide decompression */ export class ArchiveAnalyzer { private smartArchiveRef: SmartArchive; constructor(smartArchiveRefArg: SmartArchive) { this.smartArchiveRef = smartArchiveRefArg; } /** * Check if a MIME type represents an archive format */ private async mimeTypeIsArchive(mimeType: string | undefined): Promise { if (!mimeType) return false; const archiveMimeTypes: Set = new Set([ 'application/zip', 'application/x-rar-compressed', 'application/x-tar', 'application/gzip', 'application/x-7z-compressed', 'application/x-bzip2', ]); return archiveMimeTypes.has(mimeType); } /** * Get the appropriate decompression stream for a MIME type */ private async getDecompressionStream(mimeTypeArg: TSupportedMime): Promise { switch (mimeTypeArg) { case 'application/gzip': return this.smartArchiveRef.gzipTools.getDecompressionStream(); case 'application/zip': return this.smartArchiveRef.zipTools.getDecompressionStream(); case 'application/x-bzip2': return this.smartArchiveRef.bzip2Tools.getDecompressionStream(); case 'application/x-tar': return this.smartArchiveRef.tarTools.getDecompressionStream(); default: // Handle unsupported formats or no decompression needed return plugins.smartstream.createPassThrough(); } } /** * Create an analyzed stream that detects archive type and provides decompression * Emits a single IAnalyzedResult object */ public getAnalyzedStream(): plugins.smartstream.SmartDuplex { let firstRun = true; const resultStream = plugins.smartstream.createPassThrough(); const analyzerstream = new plugins.smartstream.SmartDuplex({ readableObjectMode: true, writeFunction: async (chunkArg: Buffer, streamtools) => { if (firstRun) { firstRun = false; const fileType = await plugins.fileType.fileTypeFromBuffer(chunkArg); const decompressionStream = await this.getDecompressionStream(fileType?.mime as TSupportedMime); const result: IAnalyzedResult = { fileType, isArchive: await this.mimeTypeIsArchive(fileType?.mime), resultStream, decompressionStream, }; await streamtools.push(result); } await resultStream.backpressuredPush(chunkArg); return null; }, finalFunction: async () => { resultStream.push(null); return null; }, }); return analyzerstream; } }