Compare commits

...

39 Commits

Author SHA1 Message Date
f257c0c5a4 4.0.24 2024-06-06 20:59:05 +02:00
725546e409 fix(core): update 2024-06-06 20:59:04 +02:00
b9645dfb99 4.0.23 2024-06-06 20:48:03 +02:00
b860aca103 fix(core): update 2024-06-06 20:48:02 +02:00
39fb6e8ad1 update description 2024-05-29 14:11:45 +02:00
04968a80b0 update tsconfig 2024-04-14 17:20:20 +02:00
e4a2c143bc update npmextra.json: githost 2024-04-01 21:33:47 +02:00
ed6d186a85 update npmextra.json: githost 2024-04-01 19:57:39 +02:00
553c5dfe99 update npmextra.json: githost 2024-03-30 21:46:35 +01:00
5d94efb9ee 4.0.22 2024-03-17 00:42:19 +01:00
c978ca107b fix(core): update 2024-03-17 00:42:19 +01:00
876c8ce9d8 4.0.21 2024-03-17 00:35:18 +01:00
7327bf1bd0 fix(core): update 2024-03-17 00:35:17 +01:00
2dcb10d233 4.0.20 2024-03-17 00:29:42 +01:00
d53c46fa82 fix(core): update 2024-03-17 00:29:42 +01:00
25e847a9ea 4.0.19 2023-11-14 13:17:06 +01:00
cc0ecb3f16 fix(core): update 2023-11-14 13:17:05 +01:00
2cd0846c74 4.0.18 2023-11-14 10:58:02 +01:00
49ab40af09 fix(core): update 2023-11-14 10:58:01 +01:00
5ff51ff88d 4.0.17 2023-11-14 10:55:20 +01:00
c578a3fdc1 fix(core): update 2023-11-14 10:55:19 +01:00
ad0352a712 4.0.16 2023-11-13 23:14:39 +01:00
f921338fd6 fix(core): update 2023-11-13 23:14:39 +01:00
614dae5ade 4.0.15 2023-11-13 22:11:25 +01:00
f87359fb97 fix(core): update 2023-11-13 22:11:24 +01:00
21da75c09a 4.0.14 2023-11-13 20:41:52 +01:00
fe49d25765 fix(core): update 2023-11-13 20:41:52 +01:00
5b693c6143 4.0.13 2023-11-13 20:11:10 +01:00
3206738da5 fix(core): update 2023-11-13 20:11:10 +01:00
f709421621 4.0.12 2023-11-13 20:00:29 +01:00
75be95fe45 fix(core): update 2023-11-13 20:00:28 +01:00
1113020e17 4.0.11 2023-11-11 21:06:12 +01:00
adf4bb64ad fix(core): update 2023-11-11 21:06:11 +01:00
0f630382e1 4.0.10 2023-11-11 19:58:20 +01:00
fc09d5aeac fix(core): update 2023-11-11 19:58:19 +01:00
e697730559 4.0.9 2023-11-11 19:55:20 +01:00
0e61bd7c9a fix(core): update 2023-11-11 19:55:19 +01:00
be5f3912cf 4.0.8 2023-11-11 19:31:41 +01:00
bb6fa71b99 fix(core): update 2023-11-11 19:31:40 +01:00
12 changed files with 1079 additions and 1608 deletions

View File

@ -6,12 +6,24 @@
"gitzone": { "gitzone": {
"projectType": "npm", "projectType": "npm",
"module": { "module": {
"githost": "gitlab.com", "githost": "code.foss.global",
"gitscope": "push.rocks", "gitscope": "push.rocks",
"gitrepo": "smartarchive", "gitrepo": "smartarchive",
"description": "work with archives", "description": "A library for working with archive files, providing utilities for compressing and decompressing data.",
"npmPackagename": "@push.rocks/smartarchive", "npmPackagename": "@push.rocks/smartarchive",
"license": "MIT" "license": "MIT",
"keywords": [
"archive",
"compression",
"decompression",
"zip",
"tar",
"bzip2",
"gzip"
]
} }
},
"tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
} }
} }

View File

@ -1,7 +1,7 @@
{ {
"name": "@push.rocks/smartarchive", "name": "@push.rocks/smartarchive",
"version": "4.0.7", "version": "4.0.24",
"description": "work with archives", "description": "A library for working with archive files, providing utilities for compressing and decompressing data.",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts", "typings": "dist_ts/index.d.ts",
"type": "module", "type": "module",
@ -12,34 +12,35 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/pushrocks/smartarchive.git" "url": "https://code.foss.global/push.rocks/smartarchive.git"
}, },
"author": "Lossless GmbH", "author": "Lossless GmbH",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/pushrocks/smartarchive/issues" "url": "https://github.com/pushrocks/smartarchive/issues"
}, },
"homepage": "https://github.com/pushrocks/smartarchive#readme", "homepage": "https://code.foss.global/push.rocks/smartarchive",
"dependencies": { "dependencies": {
"@push.rocks/smartfile": "^11.0.0", "@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartfile": "^11.0.4",
"@push.rocks/smartpath": "^5.0.11", "@push.rocks/smartpath": "^5.0.11",
"@push.rocks/smartpromise": "^4.0.3", "@push.rocks/smartpromise": "^4.0.3",
"@push.rocks/smartrequest": "^2.0.20", "@push.rocks/smartrequest": "^2.0.21",
"@push.rocks/smartrx": "^3.0.7", "@push.rocks/smartrx": "^3.0.7",
"@push.rocks/smartstream": "^3.0.12", "@push.rocks/smartstream": "^3.0.34",
"@push.rocks/smartunique": "^3.0.6", "@push.rocks/smartunique": "^3.0.8",
"@push.rocks/smarturl": "^3.0.7", "@push.rocks/smarturl": "^3.0.7",
"@types/tar-stream": "^3.1.3", "@types/tar-stream": "^3.1.3",
"fflate": "^0.8.1", "fflate": "^0.8.2",
"file-type": "^18.6.0", "file-type": "^19.0.0",
"tar-stream": "^3.1.6", "tar-stream": "^3.1.7",
"through": "^2.3.8" "through": "^2.3.8"
}, },
"devDependencies": { "devDependencies": {
"@git.zone/tsbuild": "^2.1.66", "@git.zone/tsbuild": "^2.1.72",
"@git.zone/tsrun": "^1.2.44", "@git.zone/tsrun": "^1.2.44",
"@git.zone/tstest": "^1.0.77", "@git.zone/tstest": "^1.0.88",
"@push.rocks/tapbundle": "^5.0.15" "@push.rocks/tapbundle": "^5.0.17"
}, },
"private": false, "private": false,
"files": [ "files": [
@ -56,5 +57,14 @@
], ],
"browserslist": [ "browserslist": [
"last 1 chrome versions" "last 1 chrome versions"
],
"keywords": [
"archive",
"compression",
"decompression",
"zip",
"tar",
"bzip2",
"gzip"
] ]
} }

2276
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

1
readme.hints.md Normal file
View File

@ -0,0 +1 @@

130
readme.md
View File

@ -1,54 +1,110 @@
# @push.rocks/smartarchive # @push.rocks/smartarchive
work with archives work with archives
## Availabililty and Links ## Install
* [npmjs.org (npm package)](https://www.npmjs.com/package/@push.rocks/smartarchive)
* [gitlab.com (source)](https://gitlab.com/push.rocks/smartarchive)
* [github.com (source mirror)](https://github.com/push.rocks/smartarchive)
* [docs (typedoc)](https://push.rocks.gitlab.io/smartarchive/)
## Status for master To install `@push.rocks/smartarchive`, you need to use npm or yarn. Run either of the following commands in your project directory:
Status Category | Status Badge ```shell
-- | -- npm install @push.rocks/smartarchive --save
GitLab Pipelines | [![pipeline status](https://gitlab.com/push.rocks/smartarchive/badges/master/pipeline.svg)](https://lossless.cloud) ```
GitLab Pipline Test Coverage | [![coverage report](https://gitlab.com/push.rocks/smartarchive/badges/master/coverage.svg)](https://lossless.cloud)
npm | [![npm downloads per month](https://badgen.net/npm/dy/@push.rocks/smartarchive)](https://lossless.cloud) or if you prefer yarn:
Snyk | [![Known Vulnerabilities](https://badgen.net/snyk/push.rocks/smartarchive)](https://lossless.cloud)
TypeScript Support | [![TypeScript](https://badgen.net/badge/TypeScript/>=%203.x/blue?icon=typescript)](https://lossless.cloud) ```shell
node Support | [![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/) yarn add @push.rocks/smartarchive
Code Style | [![Code Style](https://badgen.net/badge/style/prettier/purple)](https://lossless.cloud) ```
PackagePhobia (total standalone install weight) | [![PackagePhobia](https://badgen.net/packagephobia/install/@push.rocks/smartarchive)](https://lossless.cloud)
PackagePhobia (package size on registry) | [![PackagePhobia](https://badgen.net/packagephobia/publish/@push.rocks/smartarchive)](https://lossless.cloud) This will add `@push.rocks/smartarchive` to your project's dependencies.
BundlePhobia (total size when bundled) | [![BundlePhobia](https://badgen.net/bundlephobia/minzip/@push.rocks/smartarchive)](https://lossless.cloud)
## Usage ## Usage
Use TypeScript for best in class instellisense. `@push.rocks/smartarchive` is a powerful module designed to simplify the process of working with archive files such as zip, tar, gzip, and more. It provides an easy-to-use API for extracting, creating, and analyzing archives, making it an ideal choice for projects that require manipulation of archive files.
```javascript ### Getting Started
import * as smartarchive from 'smartarchive';
smartarchive First, import `SmartArchive` from `@push.rocks/smartarchive` using ESM syntax:
.get({
from: 'https://example.com/example.zip', ```typescript
toPath: '/some/local/absolute/path', import { SmartArchive } from '@push.rocks/smartarchive';
})
.then(/*...*/);
``` ```
For further information read the linked docs at the top of this README. ### Extracting Archive Files
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh) To extract an archive file, you can use `SmartArchive.fromArchiveUrl`, `SmartArchive.fromArchiveFile`, or `SmartArchive.fromArchiveStream` methods depending on the source of your archive. Here's an example of extracting an archive from a URL:
> | By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy.html)
[![repo-footer](https://pushrocks.gitlab.io/assets/repo-footer.svg)](https://push.rocks) ```typescript
async function extractArchiveFromURL() {
const url = 'https://example.com/archive.zip';
const targetDir = '/path/to/extract';
## Contribution const archive = await SmartArchive.fromArchiveUrl(url);
await archive.exportToFs(targetDir);
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :) console.log('Archive extracted successfully.');
}
```
For further information read the linked docs at the top of this readme. ### Creating Archive Files
## Legal Creating archive files such as zip or tar.gz is straightforward with `smartarchive`. At the moment, you'll prepare the contents programmatically and then compress them. Detailed support for creating archives will be covered in future updates.
> MIT licensed | **©** [Task Venture Capital GmbH](https://task.vc)
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy) ### Analyzing Archive Files
Analyzing the content of archives without extracting them can be useful in various scenarios, such as when you need to inspect the archive's content before deciding to extract it. Here's how you might analyze an archive:
```typescript
async function analyzeArchive() {
const url = 'https://example.com/archive.zip';
const archive = await SmartArchive.fromArchiveUrl(url);
const analysisResult = await archive.analyzeContent();
console.log(analysisResult); // Outputs details about the archive content
}
```
Note: Replace `analyzeContent` with the appropriate method calls as per your implementation or update, as `smartarchive` provides foundational classes and methods for interaction with archive files but does not directly implement an `analyzeContent` method by default.
### Stream Operations
`smartarchive` offers streaming operations, allowing you to work with large archives efficiently. Here's an example of using streams to extract an archive:
```typescript
import { createReadStream, createWriteStream } from 'fs';
async function extractArchiveUsingStream() {
const archiveStream = createReadStream('/path/to/archive.zip');
const archive = await SmartArchive.fromArchiveStream(archiveStream);
const extractionStream = await archive.exportToStreamOfStreamFiles();
extractionStream.pipe(createWriteStream('/path/to/destination'));
}
```
### Conclusion
`@push.rocks/smartarchive` simplifies the process of working with various archive formats in JavaScript and TypeScript projects. By providing an easy-to-use API for common archive operations, it enables developers to integrate archive manipulation features into their applications efficiently.
Remember, the examples provided here are to give you a starting point. Depending on your specific use case, you may need to adjust these examples to fit your project's requirements. Always refer to the latest documentation for the most current information and methods available in `@push.rocks/smartarchive`.
For more information and API references, check the official [`@push.rocks/smartarchive` GitHub repository](https://github.com/pushrocks/smartarchive).
## License and Legal Information
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
### Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
### Company Information
Task Venture Capital GmbH
Registered at District court Bremen HRB 35230 HB, Germany
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

View File

@ -47,4 +47,4 @@ tap.skip.test('should extract a b2zip', async () => {
); );
}) })
tap.start(); await tap.start();

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartarchive', name: '@push.rocks/smartarchive',
version: '4.0.7', version: '4.0.24',
description: 'work with archives' description: 'A library for working with archive files, providing utilities for compressing and decompressing data.'
} }

View File

@ -38,26 +38,27 @@ export function unbzip2Stream() {
} }
var outlength = 0; var outlength = 0;
const decompressAndPush = async (smartDuplexStream: plugins.smartstream.SmartDuplex) => { const decompressAndPush = async () => {
if (broken) return; if (broken) return;
try { try {
const resultChunk = decompressBlock(); const resultChunk = decompressBlock();
smartDuplexStream.push(resultChunk);
if (resultChunk) { if (resultChunk) {
//console.error('write at', outlength.toString(16));
outlength += resultChunk.length; outlength += resultChunk.length;
} }
return resultChunk;
} catch (e) { } catch (e) {
console.error(e); console.error(e);
smartDuplexStream.emit('error', e);
broken = true; broken = true;
return false;
} }
}; };
let counter = 0;
return new plugins.smartstream.SmartDuplex({ return new plugins.smartstream.SmartDuplex({
objectMode: true,
name: 'bzip2',
debug: false,
highWaterMark: 1,
writeFunction: async function (data, streamTools) { writeFunction: async function (data, streamTools) {
//console.error('received', data.length,'bytes in', typeof data); // console.log(`got chunk ${counter++}`)
bufferQueue.push(data); bufferQueue.push(data);
hasBytes += data.length; hasBytes += data.length;
if (bitReader === null) { if (bitReader === null) {
@ -67,17 +68,25 @@ export function unbzip2Stream() {
} }
while (!broken && hasBytes - bitReader.bytesRead + 1 >= (25000 + 100000 * blockSize || 4)) { while (!broken && hasBytes - bitReader.bytesRead + 1 >= (25000 + 100000 * blockSize || 4)) {
//console.error('decompressing with', hasBytes - bitReader.bytesRead + 1, 'bytes in buffer'); //console.error('decompressing with', hasBytes - bitReader.bytesRead + 1, 'bytes in buffer');
await decompressAndPush(this); const result = await decompressAndPush();
if (!result) {
continue;
}
// console.log(result.toString());
await streamTools.push(result);
} }
}, },
finalFunction: async function (streamTools) { finalFunction: async function (streamTools) {
//console.error(x,'last compressing with', hasBytes, 'bytes in buffer'); //console.error(x,'last compressing with', hasBytes, 'bytes in buffer');
while (!broken && bitReader && hasBytes > bitReader.bytesRead) { while (!broken && bitReader && hasBytes > bitReader.bytesRead) {
await decompressAndPush(this); const result = await decompressAndPush();
if (!result) {
continue;
}
await streamTools.push(result);
} }
if (!broken) { if (!broken) {
if (streamCRC !== null) this.emit('error', new Error('input stream ended prematurely')); if (streamCRC !== null) this.emit('error', new Error('input stream ended prematurely'));
this.queue(null);
} }
}, },
}); });

View File

@ -4,7 +4,7 @@ import * as plugins from './plugins.js';
export interface IAnalyzedResult { export interface IAnalyzedResult {
fileType: plugins.fileType.FileTypeResult; fileType: plugins.fileType.FileTypeResult;
isArchive: boolean; isArchive: boolean;
resultStream: plugins.smartstream.PassThrough; resultStream: plugins.smartstream.SmartDuplex;
decompressionStream: plugins.stream.Transform | plugins.stream.Duplex | plugins.tarStream.Extract; decompressionStream: plugins.stream.Transform | plugins.stream.Duplex | plugins.tarStream.Extract;
} }
@ -36,37 +36,41 @@ export class ArchiveAnalyzer {
switch (mimeTypeArg) { switch (mimeTypeArg) {
case 'application/gzip': case 'application/gzip':
return this.smartArchiveRef.gzipTools.getDecompressionStream(); return this.smartArchiveRef.gzipTools.getDecompressionStream();
case 'application/zip':
return this.smartArchiveRef.zipTools.getDecompressionStream();
case 'application/x-bzip2': case 'application/x-bzip2':
return await this.smartArchiveRef.bzip2Tools.getDecompressionStream(); // replace with your own bzip2 decompression stream return await this.smartArchiveRef.bzip2Tools.getDecompressionStream(); // replace with your own bzip2 decompression stream
case 'application/x-tar': case 'application/x-tar':
return this.smartArchiveRef.tarTools.getDecompressionStream(); // replace with your own tar decompression stream return this.smartArchiveRef.tarTools.getDecompressionStream(); // replace with your own tar decompression stream
default: default:
// Handle unsupported formats or no decompression needed // Handle unsupported formats or no decompression needed
return new plugins.smartstream.PassThrough(); return plugins.smartstream.createPassThrough();
} }
} }
public getAnalyzedStream() { public getAnalyzedStream() {
let firstRun = true; let firstRun = true;
const resultStream = new plugins.smartstream.PassThrough(); const resultStream = plugins.smartstream.createPassThrough();
const analyzerstream = new plugins.smartstream.SmartDuplex<Buffer, IAnalyzedResult>({ const analyzerstream = new plugins.smartstream.SmartDuplex<Buffer, IAnalyzedResult>({
readableObjectMode: true, readableObjectMode: true,
writeFunction: async (chunkArg: Buffer, streamtools) => { writeFunction: async (chunkArg: Buffer, streamtools) => {
const fileType = await plugins.fileType.fileTypeFromBuffer(chunkArg);
const decompressionStream = await this.getDecompressionStream(fileType?.mime as any);
resultStream.push(chunkArg);
if (firstRun) { if (firstRun) {
firstRun = false; firstRun = false;
const fileType = await plugins.fileType.fileTypeFromBuffer(chunkArg);
const decompressionStream = await this.getDecompressionStream(fileType?.mime as any);
/**
* analyzed stream emits once with this object
*/
const result: IAnalyzedResult = { const result: IAnalyzedResult = {
fileType, fileType,
isArchive: await this.mimeTypeIsArchive(fileType?.mime), isArchive: await this.mimeTypeIsArchive(fileType?.mime),
resultStream, resultStream,
decompressionStream, decompressionStream,
}; };
streamtools.push(result); await streamtools.push(result);
streamtools.push(null);
return null;
} }
await resultStream.backpressuredPush(chunkArg);
return null;
}, },
finalFunction: async (tools) => { finalFunction: async (tools) => {
resultStream.push(null); resultStream.push(null);

View File

@ -1,9 +1,10 @@
import * as plugins from './plugins.js'; import * as plugins from './plugins.js';
import * as paths from './paths.js'; import * as paths from './paths.js';
import { Bzip2Tools } from './classes.bzip2tools.js';
import { GzipTools } from './classes.gziptools.js'; import { GzipTools } from './classes.gziptools.js';
import { TarTools } from './classes.tartools.js'; import { TarTools } from './classes.tartools.js';
import { Bzip2Tools } from './classes.bzip2tools.js'; import { ZipTools } from './classes.ziptools.js';
import { ArchiveAnalyzer, type IAnalyzedResult } from './classes.archiveanalyzer.js'; import { ArchiveAnalyzer, type IAnalyzedResult } from './classes.archiveanalyzer.js';
@ -32,9 +33,10 @@ export class SmartArchive {
} }
// INSTANCE // INSTANCE
public tarTools = new TarTools(this);
public gzipTools = new GzipTools(this); public gzipTools = new GzipTools(this);
public bzip2Tools = new Bzip2Tools(this); public bzip2Tools = new Bzip2Tools(this);
public tarTools = new TarTools(this);
public zipTools = new ZipTools(this);
public archiveAnalyzer = new ArchiveAnalyzer(this); public archiveAnalyzer = new ArchiveAnalyzer(this);
public sourceUrl: string; public sourceUrl: string;
@ -77,27 +79,32 @@ export class SmartArchive {
public async exportToFs(targetDir: string, fileNameArg?: string): Promise<void> { public async exportToFs(targetDir: string, fileNameArg?: string): Promise<void> {
const done = plugins.smartpromise.defer<void>(); const done = plugins.smartpromise.defer<void>();
const streamFileStream = await this.exportToStreamOfStreamFiles(); const streamFileStream = await this.exportToStreamOfStreamFiles();
streamFileStream.pipe(new plugins.smartstream.SmartDuplex({ streamFileStream.pipe(
objectMode: true, new plugins.smartstream.SmartDuplex({
writeFunction: async (chunkArg: plugins.smartfile.StreamFile, streamtools) => { objectMode: true,
const done = plugins.smartpromise.defer<void>(); writeFunction: async (streamFileArg: plugins.smartfile.StreamFile, streamtools) => {
console.log(chunkArg.relativeFilePath); const done = plugins.smartpromise.defer<void>();
const streamFile = chunkArg; console.log(streamFileArg.relativeFilePath ? streamFileArg.relativeFilePath : 'no relative path');
const readStream = await streamFile.createReadStream(); const streamFile = streamFileArg;
await plugins.smartfile.fs.ensureDir(targetDir); const readStream = await streamFile.createReadStream();
const writePath = plugins.path.join(targetDir, (streamFile.relativeFilePath || fileNameArg)); await plugins.smartfile.fs.ensureDir(targetDir);
await plugins.smartfile.fs.ensureDir(plugins.path.dirname(writePath)); const writePath = plugins.path.join(
const writeStream = plugins.smartfile.fsStream.createWriteStream(writePath); targetDir,
readStream.pipe(writeStream); streamFile.relativeFilePath || fileNameArg
writeStream.on('finish', () => { );
await plugins.smartfile.fs.ensureDir(plugins.path.dirname(writePath));
const writeStream = plugins.smartfile.fsStream.createWriteStream(writePath);
readStream.pipe(writeStream);
writeStream.on('finish', () => {
done.resolve();
});
await done.promise;
},
finalFunction: async () => {
done.resolve(); done.resolve();
}) },
await done.promise; })
}, );
finalFunction: async () => {
done.resolve();
}
}));
return done.promise; return done.promise;
} }
@ -114,21 +121,36 @@ export class SmartArchive {
async (analyzedResultChunk) => { async (analyzedResultChunk) => {
if (analyzedResultChunk.fileType?.mime === 'application/x-tar') { if (analyzedResultChunk.fileType?.mime === 'application/x-tar') {
const tarStream = analyzedResultChunk.decompressionStream as plugins.tarStream.Extract; const tarStream = analyzedResultChunk.decompressionStream as plugins.tarStream.Extract;
tarStream.on( tarStream.on('entry', async (header, stream, next) => {
'entry', if (header.type === 'directory') {
async (header, stream, next) => { console.log(`tar stream directory: ${header.name} ... skipping!`);
const streamfile = plugins.smartfile.StreamFile.fromStream(stream, header.name); next();
streamFileIntake.push(streamfile); return;
stream.on('end', function () {
next(); // ready for next entry
});
} }
); console.log(`tar stream file: ${header.name}`);
const streamfile = plugins.smartfile.StreamFile.fromStream(stream, header.name);
streamFileIntake.push(streamfile);
stream.on('end', function () {
next(); // ready for next entry
});
});
tarStream.on('finish', function () { tarStream.on('finish', function () {
console.log('finished'); console.log('finished');
streamFileIntake.signalEnd(); streamFileIntake.signalEnd();
}); });
analyzedResultChunk.resultStream.pipe(analyzedResultChunk.decompressionStream); analyzedResultChunk.resultStream.pipe(analyzedResultChunk.decompressionStream);
} else if (analyzedResultChunk.fileType?.mime === 'application/zip') {
analyzedResultChunk.resultStream
.pipe(analyzedResultChunk.decompressionStream)
.pipe(new plugins.smartstream.SmartDuplex({
objectMode: true,
writeFunction: async (streamFileArg: plugins.smartfile.StreamFile, streamtools) => {
streamFileIntake.push(streamFileArg);
},
finalFunction: async () => {
streamFileIntake.signalEnd();
}
}));
} else if (analyzedResultChunk.isArchive && analyzedResultChunk.decompressionStream) { } else if (analyzedResultChunk.isArchive && analyzedResultChunk.decompressionStream) {
analyzedResultChunk.resultStream analyzedResultChunk.resultStream
.pipe(analyzedResultChunk.decompressionStream) .pipe(analyzedResultChunk.decompressionStream)
@ -143,6 +165,9 @@ export class SmartArchive {
streamFileIntake.signalEnd(); streamFileIntake.signalEnd();
} }
}, },
{
objectMode: true,
}
); );
archiveStream.pipe(createAnalyzedStream()).pipe(createUnpackStream()); archiveStream.pipe(createAnalyzedStream()).pipe(createUnpackStream());

77
ts/classes.ziptools.ts Normal file
View File

@ -0,0 +1,77 @@
import type { SmartArchive } from './classes.smartarchive.js';
import * as plugins from './plugins.js';
class DecompressZipTransform extends plugins.smartstream.SmartDuplex<ArrayBufferLike> {
private streamtools: plugins.smartstream.IStreamTools;
private unzipper = new plugins.fflate.Unzip(async (fileArg) => {
let resultBuffer: Buffer;
fileArg.ondata = async (dataArg, dat, final) => {
resultBuffer? resultBuffer = Buffer.concat([resultBuffer, Buffer.from(dat)])
: resultBuffer = Buffer.from(dat);
if (final) {
const streamFile = plugins.smartfile.StreamFile.fromBuffer(resultBuffer);
streamFile.relativeFilePath = fileArg.name;
this.streamtools.push(streamFile);
}
}
fileArg.start();
});
constructor() {
super({
objectMode: true,
writeFunction: async (chunkArg: Buffer, streamtoolsArg) => {
this.streamtools? null : this.streamtools = streamtoolsArg;
this.unzipper.push(chunkArg, false);
},
finalFunction: async () => {
this.unzipper.push(Buffer.from(''), true);
await plugins.smartdelay.delayFor(0);
await this.streamtools.push(null);
}
});
this.unzipper.register(plugins.fflate.UnzipInflate);
}
}
// This class wraps fflate's zip in a Node.js Transform stream for compression
export class CompressZipTransform extends plugins.stream.Transform {
files: { [fileName: string]: Uint8Array };
constructor() {
super();
this.files = {};
}
_transform(chunk: Buffer, encoding: BufferEncoding, callback: plugins.stream.TransformCallback) {
// Simple example: storing chunks in memory before finalizing ZIP in _flush
this.files['file.txt'] = new Uint8Array(chunk);
callback();
}
_flush(callback: plugins.stream.TransformCallback) {
plugins.fflate.zip(this.files, (err, zipped) => {
if (err) {
callback(err);
} else {
this.push(Buffer.from(zipped));
callback();
}
});
}
}
export class ZipTools {
smartArchiveRef: SmartArchive;
constructor(smartArchiveRefArg: SmartArchive) {
this.smartArchiveRef = smartArchiveRefArg;
}
public getCompressionStream() {
return new CompressZipTransform();
}
public getDecompressionStream() {
return new DecompressZipTransform();
}
}

View File

@ -6,6 +6,7 @@ export { path, stream };
// @pushrocks scope // @pushrocks scope
import * as smartfile from '@push.rocks/smartfile'; import * as smartfile from '@push.rocks/smartfile';
import * as smartdelay from '@push.rocks/smartdelay';
import * as smartpath from '@push.rocks/smartpath'; import * as smartpath from '@push.rocks/smartpath';
import * as smartpromise from '@push.rocks/smartpromise'; import * as smartpromise from '@push.rocks/smartpromise';
import * as smartrequest from '@push.rocks/smartrequest'; import * as smartrequest from '@push.rocks/smartrequest';
@ -14,7 +15,7 @@ import * as smartstream from '@push.rocks/smartstream';
import * as smartrx from '@push.rocks/smartrx'; import * as smartrx from '@push.rocks/smartrx';
import * as smarturl from '@push.rocks/smarturl'; import * as smarturl from '@push.rocks/smarturl';
export { smartfile, smartpath, smartpromise, smartrequest, smartunique, smartstream, smartrx, smarturl }; export { smartfile, smartdelay, smartpath, smartpromise, smartrequest, smartunique, smartstream, smartrx, smarturl };
// third party scope // third party scope
import * as fileType from 'file-type'; import * as fileType from 'file-type';