feat(tartools): add streaming TAR support (tar-stream), Node.js streaming APIs for TarTools, and browser / web bundle docs

This commit is contained in:
2026-01-01 23:40:13 +00:00
parent d97e9c1dce
commit db48fcd455
13 changed files with 456 additions and 96 deletions

View File

@@ -380,36 +380,41 @@ export class SmartArchive {
plugins.smartstream.createTransformFunction<IAnalyzedResult, void>(
async (analyzedResultChunk) => {
if (analyzedResultChunk.fileType?.mime === 'application/x-tar') {
// Use modern-tar for TAR extraction
const chunks: Buffer[] = [];
// Use tar-stream for streaming TAR extraction
// Buffer each entry to ensure tar-stream can proceed to next entry
const extract = this.tarTools.getExtractStream();
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);
extract.on('entry', (header, stream, next) => {
if (header.type === 'directory') {
stream.resume(); // Drain the stream
next();
return;
}
// Buffer the entry content to avoid blocking tar-stream
const chunks: Buffer[] = [];
stream.on('data', (chunk: Buffer) => chunks.push(chunk));
stream.on('end', () => {
const content = Buffer.concat(chunks);
const streamFile = plugins.smartfile.StreamFile.fromBuffer(content);
streamFile.relativeFilePath = header.name;
streamFileIntake.push(streamFile);
next();
});
stream.on('error', (err: Error) => {
streamFileIntake.emit('error', err);
});
});
analyzedResultChunk.resultStream.on('error', (err: Error) => {
extract.on('finish', () => {
safeSignalEnd();
});
extract.on('error', (err: Error) => {
streamFileIntake.emit('error', err);
});
analyzedResultChunk.resultStream.pipe(extract);
} else if (analyzedResultChunk.fileType?.mime === 'application/zip') {
analyzedResultChunk.resultStream
.pipe(analyzedResultChunk.decompressionStream)