feat(archive): introduce ts_shared browser-compatible layer, refactor Node-specific tools to wrap/shared implementations, and modernize archive handling
This commit is contained in:
@@ -6,12 +6,15 @@ import type {
|
||||
TArchiveFormat,
|
||||
TCompressionLevel,
|
||||
TEntryFilter,
|
||||
} from './interfaces.js';
|
||||
} from '../ts_shared/interfaces.js';
|
||||
|
||||
import { Bzip2Tools } from './classes.bzip2tools.js';
|
||||
import { GzipTools } from './classes.gziptools.js';
|
||||
// Import browser-compatible tools from ts_shared
|
||||
import { Bzip2Tools } from '../ts_shared/classes.bzip2tools.js';
|
||||
import { GzipTools } from '../ts_shared/classes.gziptools.js';
|
||||
import { ZipTools } from '../ts_shared/classes.ziptools.js';
|
||||
|
||||
// Import Node.js-extended TarTools
|
||||
import { TarTools } from './classes.tartools.js';
|
||||
import { ZipTools } from './classes.ziptools.js';
|
||||
import { ArchiveAnalyzer, type IAnalyzedResult } from './classes.archiveanalyzer.js';
|
||||
|
||||
/**
|
||||
@@ -62,7 +65,7 @@ export class SmartArchive {
|
||||
public tarTools = new TarTools();
|
||||
public zipTools = new ZipTools();
|
||||
public gzipTools = new GzipTools();
|
||||
public bzip2Tools = new Bzip2Tools(this);
|
||||
public bzip2Tools = new Bzip2Tools();
|
||||
public archiveAnalyzer = new ArchiveAnalyzer(this);
|
||||
|
||||
// ============================================
|
||||
@@ -173,7 +176,7 @@ export class SmartArchive {
|
||||
public entry(archivePath: string, content: string | Buffer): this {
|
||||
this.ensureNotInExtractMode('entry');
|
||||
if (!this._mode) this._mode = 'create';
|
||||
this.pendingEntries.push({ archivePath, content });
|
||||
this.pendingEntries.push({ archivePath, content: content instanceof Buffer ? new Uint8Array(content) : content });
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -184,7 +187,10 @@ export class SmartArchive {
|
||||
this.ensureNotInExtractMode('entries');
|
||||
if (!this._mode) this._mode = 'create';
|
||||
for (const e of entriesArg) {
|
||||
this.pendingEntries.push({ archivePath: e.archivePath, content: e.content });
|
||||
this.pendingEntries.push({
|
||||
archivePath: e.archivePath,
|
||||
content: e.content instanceof Buffer ? new Uint8Array(e.content) : e.content
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -374,30 +380,36 @@ export class SmartArchive {
|
||||
plugins.smartstream.createTransformFunction<IAnalyzedResult, void>(
|
||||
async (analyzedResultChunk) => {
|
||||
if (analyzedResultChunk.fileType?.mime === 'application/x-tar') {
|
||||
const tarStream = analyzedResultChunk.decompressionStream as plugins.tarStream.Extract;
|
||||
// Use modern-tar for TAR extraction
|
||||
const chunks: Buffer[] = [];
|
||||
|
||||
tarStream.on('entry', async (header, stream, next) => {
|
||||
if (header.type === 'directory') {
|
||||
stream.resume();
|
||||
stream.on('end', () => next());
|
||||
return;
|
||||
analyzedResultChunk.resultStream.on('data', (chunk: Buffer) => {
|
||||
chunks.push(chunk);
|
||||
});
|
||||
|
||||
analyzedResultChunk.resultStream.on('end', async () => {
|
||||
try {
|
||||
const tarBuffer = Buffer.concat(chunks);
|
||||
const entries = await this.tarTools.extractTar(new Uint8Array(tarBuffer));
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory) continue;
|
||||
|
||||
const streamFile = plugins.smartfile.StreamFile.fromBuffer(
|
||||
Buffer.from(entry.content)
|
||||
);
|
||||
streamFile.relativeFilePath = entry.path;
|
||||
streamFileIntake.push(streamFile);
|
||||
}
|
||||
safeSignalEnd();
|
||||
} catch (err) {
|
||||
streamFileIntake.emit('error', err);
|
||||
}
|
||||
|
||||
const passThrough = new plugins.stream.PassThrough();
|
||||
const streamfile = plugins.smartfile.StreamFile.fromStream(passThrough, header.name);
|
||||
streamFileIntake.push(streamfile);
|
||||
stream.pipe(passThrough);
|
||||
stream.on('end', () => {
|
||||
passThrough.end();
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
tarStream.on('finish', () => {
|
||||
safeSignalEnd();
|
||||
analyzedResultChunk.resultStream.on('error', (err: Error) => {
|
||||
streamFileIntake.emit('error', err);
|
||||
});
|
||||
|
||||
analyzedResultChunk.resultStream.pipe(analyzedResultChunk.decompressionStream);
|
||||
} else if (analyzedResultChunk.fileType?.mime === 'application/zip') {
|
||||
analyzedResultChunk.resultStream
|
||||
.pipe(analyzedResultChunk.decompressionStream)
|
||||
@@ -544,25 +556,29 @@ export class SmartArchive {
|
||||
|
||||
if (this.creationFormat === 'tar' || this.creationFormat === 'tar.gz' || this.creationFormat === 'tgz') {
|
||||
if (this.creationFormat === 'tar') {
|
||||
this.archiveBuffer = await this.tarTools.packFiles(entries);
|
||||
const result = await this.tarTools.packFiles(entries);
|
||||
this.archiveBuffer = Buffer.from(result);
|
||||
} else {
|
||||
this.archiveBuffer = await this.tarTools.packFilesToTarGz(entries, this._compressionLevel);
|
||||
const result = await this.tarTools.packFilesToTarGz(entries, this._compressionLevel);
|
||||
this.archiveBuffer = Buffer.from(result);
|
||||
}
|
||||
} else if (this.creationFormat === 'zip') {
|
||||
this.archiveBuffer = await this.zipTools.createZip(entries, this._compressionLevel);
|
||||
const result = await this.zipTools.createZip(entries, this._compressionLevel);
|
||||
this.archiveBuffer = Buffer.from(result);
|
||||
} else if (this.creationFormat === 'gz') {
|
||||
if (entries.length !== 1) {
|
||||
throw new Error('GZIP format only supports a single file');
|
||||
}
|
||||
let content: Buffer;
|
||||
let content: Uint8Array;
|
||||
if (typeof entries[0].content === 'string') {
|
||||
content = Buffer.from(entries[0].content);
|
||||
} else if (Buffer.isBuffer(entries[0].content)) {
|
||||
content = new TextEncoder().encode(entries[0].content);
|
||||
} else if (entries[0].content instanceof Uint8Array) {
|
||||
content = entries[0].content;
|
||||
} else {
|
||||
throw new Error('GZIP format requires string or Buffer content');
|
||||
throw new Error('GZIP format requires string or Uint8Array content');
|
||||
}
|
||||
this.archiveBuffer = await this.gzipTools.compress(content, this._compressionLevel);
|
||||
const result = await this.gzipTools.compress(content, this._compressionLevel);
|
||||
this.archiveBuffer = Buffer.from(result);
|
||||
} else {
|
||||
throw new Error(`Unsupported format: ${this.creationFormat}`);
|
||||
}
|
||||
@@ -808,7 +824,7 @@ export class SmartArchive {
|
||||
const content = await plugins.fsPromises.readFile(absolutePath);
|
||||
this.pendingEntries.push({
|
||||
archivePath,
|
||||
content,
|
||||
content: new Uint8Array(content),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user