@push.rocks/smartarchive 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/classes.archiveanalyzer.d.ts +1 -1
- package/dist_ts/classes.archiveanalyzer.js +34 -4
- package/dist_ts/classes.smartarchive.d.ts +151 -54
- package/dist_ts/classes.smartarchive.js +484 -234
- package/dist_ts/classes.tartools.d.ts +9 -33
- package/dist_ts/classes.tartools.js +18 -153
- package/dist_ts/index.d.ts +2 -6
- package/dist_ts/index.js +7 -11
- package/dist_ts/paths.js +1 -1
- package/dist_ts/plugins.d.ts +2 -9
- package/dist_ts/plugins.js +6 -13
- package/npmextra.json +13 -7
- package/package.json +23 -14
- package/readme.hints.md +69 -23
- package/readme.md +360 -274
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.archiveanalyzer.ts +33 -5
- package/ts/classes.smartarchive.ts +546 -256
- package/ts/classes.tartools.ts +20 -177
- package/ts/index.ts +7 -11
- package/ts/plugins.ts +5 -17
- package/{ts → ts_shared}/bzip2/bititerator.ts +1 -1
- package/{ts → ts_shared}/bzip2/bzip2.ts +2 -2
- package/{ts → ts_shared}/bzip2/index.ts +6 -6
- package/ts_shared/classes.bzip2tools.ts +14 -0
- package/ts_shared/classes.gziptools.ts +42 -0
- package/ts_shared/classes.tartools.ts +89 -0
- package/ts_shared/classes.ziptools.ts +107 -0
- package/ts_shared/index.ts +17 -0
- package/{ts → ts_shared}/interfaces.ts +23 -7
- package/ts_shared/plugins.ts +22 -0
- package/ts_web/00_commitinfo_data.ts +8 -0
- package/ts_web/index.ts +4 -0
- package/ts_web/plugins.ts +3 -0
- package/dist_ts/bzip2/bititerator.d.ts +0 -6
- package/dist_ts/bzip2/bititerator.js +0 -50
- package/dist_ts/bzip2/bzip2.d.ts +0 -29
- package/dist_ts/bzip2/bzip2.js +0 -398
- package/dist_ts/bzip2/index.d.ts +0 -5
- package/dist_ts/bzip2/index.js +0 -92
- package/dist_ts/classes.bzip2tools.d.ts +0 -7
- package/dist_ts/classes.bzip2tools.js +0 -11
- package/dist_ts/classes.gziptools.d.ts +0 -49
- package/dist_ts/classes.gziptools.js +0 -125
- package/dist_ts/classes.ziptools.d.ts +0 -56
- package/dist_ts/classes.ziptools.js +0 -185
- package/dist_ts/errors.d.ts +0 -44
- package/dist_ts/errors.js +0 -62
- package/dist_ts/interfaces.d.ts +0 -115
- package/dist_ts/interfaces.js +0 -2
- package/dist_ts/smartarchive.classes.smartarchive.d.ts +0 -34
- package/dist_ts/smartarchive.classes.smartarchive.js +0 -116
- package/dist_ts/smartarchive.paths.d.ts +0 -2
- package/dist_ts/smartarchive.paths.js +0 -4
- package/dist_ts/smartarchive.plugins.d.ts +0 -14
- package/dist_ts/smartarchive.plugins.js +0 -19
- package/ts/classes.bzip2tools.ts +0 -16
- package/ts/classes.gziptools.ts +0 -143
- package/ts/classes.ziptools.ts +0 -209
- /package/{ts → ts_shared}/errors.ts +0 -0
package/readme.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# @push.rocks/smartarchive 📦
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
`@push.rocks/smartarchive` is a versatile library for handling archive files with a focus on developer experience. Work with **zip**, **tar**, **gzip**, and **bzip2** formats through a unified, streaming-optimized API.
|
|
3
|
+
A powerful, streaming-first archive manipulation library with a fluent builder API. Works seamlessly in Node.js and Deno.
|
|
6
4
|
|
|
7
5
|
## Issue Reporting and Security
|
|
8
6
|
|
|
@@ -10,13 +8,15 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
|
|
|
10
8
|
|
|
11
9
|
## Features 🚀
|
|
12
10
|
|
|
13
|
-
- 📁 **Multi-format support** – Handle `.zip`, `.tar`, `.tar.gz`, `.tgz`, and `.bz2` archives
|
|
11
|
+
- 📁 **Multi-format support** – Handle `.zip`, `.tar`, `.tar.gz`, `.tgz`, `.gz`, and `.bz2` archives
|
|
14
12
|
- 🌊 **Streaming-first architecture** – Process large archives without memory constraints
|
|
15
|
-
-
|
|
13
|
+
- ✨ **Fluent builder API** – Chain methods for readable, expressive code
|
|
16
14
|
- 🎯 **Smart detection** – Automatically identifies archive types via magic bytes
|
|
17
15
|
- ⚡ **High performance** – Built on `tar-stream` and `fflate` for speed
|
|
18
|
-
- 🔧 **Flexible I/O** – Work with files, URLs, and
|
|
16
|
+
- 🔧 **Flexible I/O** – Work with files, URLs, streams, and buffers seamlessly
|
|
19
17
|
- 🛠️ **Modern TypeScript** – Full type safety and excellent IDE support
|
|
18
|
+
- 🔄 **Dual-mode operation** – Extract existing archives OR create new ones
|
|
19
|
+
- 🦕 **Cross-runtime** – Works in both Node.js and Deno environments
|
|
20
20
|
|
|
21
21
|
## Installation 📥
|
|
22
22
|
|
|
@@ -39,354 +39,348 @@ yarn add @push.rocks/smartarchive
|
|
|
39
39
|
import { SmartArchive } from '@push.rocks/smartarchive';
|
|
40
40
|
|
|
41
41
|
// Extract a .tar.gz archive from a URL directly to the filesystem
|
|
42
|
-
|
|
43
|
-
'https://registry.npmjs.org/some-package/-/some-package-1.0.0.tgz'
|
|
44
|
-
);
|
|
45
|
-
await archive.exportToFs('./extracted');
|
|
42
|
+
await SmartArchive.create()
|
|
43
|
+
.url('https://registry.npmjs.org/some-package/-/some-package-1.0.0.tgz')
|
|
44
|
+
.extract('./extracted');
|
|
46
45
|
```
|
|
47
46
|
|
|
48
|
-
###
|
|
47
|
+
### Create an archive from entries
|
|
49
48
|
|
|
50
49
|
```typescript
|
|
51
50
|
import { SmartArchive } from '@push.rocks/smartarchive';
|
|
52
51
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const readStream = await streamFile.createReadStream();
|
|
61
|
-
// Handle individual file stream
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
streamOfFiles.on('end', () => {
|
|
65
|
-
console.log('Extraction complete');
|
|
66
|
-
});
|
|
52
|
+
// Create a tar.gz archive with files
|
|
53
|
+
await SmartArchive.create()
|
|
54
|
+
.format('tar.gz')
|
|
55
|
+
.compression(6)
|
|
56
|
+
.entry('config.json', JSON.stringify({ name: 'myapp' }))
|
|
57
|
+
.entry('readme.txt', 'Hello World!')
|
|
58
|
+
.toFile('./backup.tar.gz');
|
|
67
59
|
```
|
|
68
60
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
### Archive Sources
|
|
72
|
-
|
|
73
|
-
`SmartArchive` accepts archives from three sources:
|
|
74
|
-
|
|
75
|
-
| Source | Method | Use Case |
|
|
76
|
-
|--------|--------|----------|
|
|
77
|
-
| **URL** | `SmartArchive.fromArchiveUrl(url)` | Download and process archives from the web |
|
|
78
|
-
| **File** | `SmartArchive.fromArchiveFile(path)` | Load archives from the local filesystem |
|
|
79
|
-
| **Stream** | `SmartArchive.fromArchiveStream(stream)` | Process archives from any Node.js stream |
|
|
80
|
-
|
|
81
|
-
### Export Destinations
|
|
82
|
-
|
|
83
|
-
| Destination | Method | Use Case |
|
|
84
|
-
|-------------|--------|----------|
|
|
85
|
-
| **Filesystem** | `exportToFs(targetDir, fileName?)` | Extract directly to a directory |
|
|
86
|
-
| **Stream of files** | `exportToStreamOfStreamFiles()` | Process files individually as `StreamFile` objects |
|
|
87
|
-
|
|
88
|
-
## Usage Examples 🔨
|
|
89
|
-
|
|
90
|
-
### Working with ZIP files
|
|
61
|
+
### Extract with filtering and path manipulation
|
|
91
62
|
|
|
92
63
|
```typescript
|
|
93
64
|
import { SmartArchive } from '@push.rocks/smartarchive';
|
|
94
65
|
|
|
95
|
-
// Extract
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
fileStream.on('data', async (streamFile) => {
|
|
103
|
-
if (streamFile.relativeFilePath.endsWith('.json')) {
|
|
104
|
-
const readStream = await streamFile.createReadStream();
|
|
105
|
-
// Process JSON files from the archive
|
|
106
|
-
}
|
|
107
|
-
});
|
|
66
|
+
// Extract only JSON files, stripping the first path component
|
|
67
|
+
await SmartArchive.create()
|
|
68
|
+
.url('https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz')
|
|
69
|
+
.stripComponents(1) // Remove 'package/' prefix
|
|
70
|
+
.include(/\.json$/) // Only extract JSON files
|
|
71
|
+
.extract('./node_modules/lodash');
|
|
108
72
|
```
|
|
109
73
|
|
|
110
|
-
|
|
74
|
+
## Core Concepts 💡
|
|
111
75
|
|
|
112
|
-
|
|
113
|
-
import { SmartArchive, TarTools } from '@push.rocks/smartarchive';
|
|
76
|
+
### Fluent Builder Pattern
|
|
114
77
|
|
|
115
|
-
|
|
116
|
-
const tarGzArchive = await SmartArchive.fromArchiveFile('./archive.tar.gz');
|
|
117
|
-
await tarGzArchive.exportToFs('./extracted');
|
|
78
|
+
`SmartArchive` uses a fluent builder pattern where you chain methods to configure the operation:
|
|
118
79
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
80
|
+
```typescript
|
|
81
|
+
SmartArchive.create() // Start a new builder
|
|
82
|
+
.source(...) // Configure source (extraction mode)
|
|
83
|
+
.options(...) // Set options
|
|
84
|
+
.terminal() // Execute the operation
|
|
85
|
+
```
|
|
122
86
|
|
|
123
|
-
|
|
124
|
-
await tarTools.addFileToPack(pack, {
|
|
125
|
-
fileName: 'hello.txt',
|
|
126
|
-
content: 'Hello, World!'
|
|
127
|
-
});
|
|
87
|
+
### Two Operating Modes
|
|
128
88
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
89
|
+
**Extraction Mode** - Load an existing archive and extract/analyze it:
|
|
90
|
+
```typescript
|
|
91
|
+
SmartArchive.create()
|
|
92
|
+
.url('...') // or .file(), .stream(), .buffer()
|
|
93
|
+
.extract('./out') // or .toSmartFiles(), .list(), etc.
|
|
94
|
+
```
|
|
133
95
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
96
|
+
**Creation Mode** - Build a new archive from entries:
|
|
97
|
+
```typescript
|
|
98
|
+
SmartArchive.create()
|
|
99
|
+
.format('tar.gz') // Set output format
|
|
100
|
+
.entry(...) // Add files
|
|
101
|
+
.toFile('./out.tar.gz') // or .toBuffer(), .toStream()
|
|
137
102
|
```
|
|
138
103
|
|
|
139
|
-
|
|
104
|
+
> ⚠️ **Note:** You cannot mix extraction and creation methods in the same chain.
|
|
140
105
|
|
|
141
|
-
|
|
142
|
-
import { TarTools } from '@push.rocks/smartarchive';
|
|
143
|
-
import { createWriteStream } from 'fs';
|
|
106
|
+
## API Reference 📚
|
|
144
107
|
|
|
145
|
-
|
|
108
|
+
### Source Methods (Extraction Mode)
|
|
109
|
+
|
|
110
|
+
| Method | Description |
|
|
111
|
+
|--------|-------------|
|
|
112
|
+
| `.url(url)` | Load archive from a URL |
|
|
113
|
+
| `.file(path)` | Load archive from local filesystem |
|
|
114
|
+
| `.stream(readable)` | Load archive from any Node.js readable stream |
|
|
115
|
+
| `.buffer(buffer)` | Load archive from an in-memory Buffer |
|
|
116
|
+
|
|
117
|
+
### Creation Methods (Creation Mode)
|
|
118
|
+
|
|
119
|
+
| Method | Description |
|
|
120
|
+
|--------|-------------|
|
|
121
|
+
| `.format(fmt)` | Set output format: `'tar'`, `'tar.gz'`, `'tgz'`, `'zip'`, `'gz'` |
|
|
122
|
+
| `.compression(level)` | Set compression level (0-9, default: 6) |
|
|
123
|
+
| `.entry(path, content)` | Add a file entry (string or Buffer content) |
|
|
124
|
+
| `.entries(array)` | Add multiple entries at once |
|
|
125
|
+
| `.directory(path, archiveBase?)` | Add entire directory contents |
|
|
126
|
+
| `.addSmartFile(file, path?)` | Add a SmartFile instance |
|
|
127
|
+
| `.addStreamFile(file, path?)` | Add a StreamFile instance |
|
|
128
|
+
|
|
129
|
+
### Filter Methods (Both Modes)
|
|
130
|
+
|
|
131
|
+
| Method | Description |
|
|
132
|
+
|--------|-------------|
|
|
133
|
+
| `.filter(predicate)` | Filter entries with custom function |
|
|
134
|
+
| `.include(pattern)` | Only include entries matching regex/string pattern |
|
|
135
|
+
| `.exclude(pattern)` | Exclude entries matching regex/string pattern |
|
|
136
|
+
|
|
137
|
+
### Extraction Options
|
|
138
|
+
|
|
139
|
+
| Method | Description |
|
|
140
|
+
|--------|-------------|
|
|
141
|
+
| `.stripComponents(n)` | Strip N leading path components |
|
|
142
|
+
| `.overwrite(bool)` | Overwrite existing files (default: false) |
|
|
143
|
+
| `.fileName(name)` | Set output filename for single-file archives (gz, bz2) |
|
|
144
|
+
|
|
145
|
+
### Terminal Methods (Extraction)
|
|
146
|
+
|
|
147
|
+
| Method | Returns | Description |
|
|
148
|
+
|--------|---------|-------------|
|
|
149
|
+
| `.extract(targetDir)` | `Promise<void>` | Extract to filesystem directory |
|
|
150
|
+
| `.toStreamFiles()` | `Promise<StreamIntake<StreamFile>>` | Get stream of StreamFile objects |
|
|
151
|
+
| `.toSmartFiles()` | `Promise<SmartFile[]>` | Get in-memory SmartFile array |
|
|
152
|
+
| `.extractFile(path)` | `Promise<SmartFile \| null>` | Extract single file by path |
|
|
153
|
+
| `.list()` | `Promise<IArchiveEntryInfo[]>` | List all entries |
|
|
154
|
+
| `.analyze()` | `Promise<IArchiveInfo>` | Get archive metadata |
|
|
155
|
+
| `.hasFile(path)` | `Promise<boolean>` | Check if file exists |
|
|
156
|
+
|
|
157
|
+
### Terminal Methods (Creation)
|
|
158
|
+
|
|
159
|
+
| Method | Returns | Description |
|
|
160
|
+
|--------|---------|-------------|
|
|
161
|
+
| `.build()` | `Promise<SmartArchive>` | Build the archive (implicit in other terminals) |
|
|
162
|
+
| `.toBuffer()` | `Promise<Buffer>` | Get archive as Buffer |
|
|
163
|
+
| `.toFile(path)` | `Promise<void>` | Write archive to disk |
|
|
164
|
+
| `.toStream()` | `Promise<Readable>` | Get raw archive stream |
|
|
146
165
|
|
|
147
|
-
|
|
148
|
-
const pack = await tarTools.packDirectory('./src');
|
|
149
|
-
pack.finalize();
|
|
150
|
-
pack.pipe(createWriteStream('./source.tar'));
|
|
151
|
-
```
|
|
166
|
+
## Usage Examples 🔨
|
|
152
167
|
|
|
153
|
-
###
|
|
168
|
+
### Download and extract npm packages
|
|
154
169
|
|
|
155
170
|
```typescript
|
|
156
171
|
import { SmartArchive } from '@push.rocks/smartarchive';
|
|
157
172
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
'https://registry.npmjs.org/@push.rocks/smartfile/-/smartfile-11.2.7.tgz'
|
|
161
|
-
);
|
|
162
|
-
await npmPackage.exportToFs('./node_modules/@push.rocks/smartfile');
|
|
173
|
+
const pkg = await SmartArchive.create()
|
|
174
|
+
.url('https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz');
|
|
163
175
|
|
|
164
|
-
//
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
});
|
|
176
|
+
// Quick inspection of package.json
|
|
177
|
+
const pkgJson = await pkg.extractFile('package/package.json');
|
|
178
|
+
if (pkgJson) {
|
|
179
|
+
const metadata = JSON.parse(pkgJson.contents.toString());
|
|
180
|
+
console.log(`Package: ${metadata.name}@${metadata.version}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Full extraction with path normalization
|
|
184
|
+
await SmartArchive.create()
|
|
185
|
+
.url('https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz')
|
|
186
|
+
.stripComponents(1)
|
|
187
|
+
.extract('./node_modules/lodash');
|
|
169
188
|
```
|
|
170
189
|
|
|
171
|
-
###
|
|
190
|
+
### Create ZIP archive
|
|
172
191
|
|
|
173
192
|
```typescript
|
|
174
|
-
import { SmartArchive
|
|
175
|
-
import { createReadStream, createWriteStream } from 'fs';
|
|
176
|
-
|
|
177
|
-
// Decompress a .gz file - provide filename since gzip doesn't store it
|
|
178
|
-
const gzipArchive = await SmartArchive.fromArchiveFile('./data.json.gz');
|
|
179
|
-
await gzipArchive.exportToFs('./decompressed', 'data.json');
|
|
180
|
-
|
|
181
|
-
// Use GzipTools directly for streaming decompression
|
|
182
|
-
const gzipTools = new GzipTools();
|
|
183
|
-
const decompressStream = gzipTools.getDecompressionStream();
|
|
193
|
+
import { SmartArchive } from '@push.rocks/smartarchive';
|
|
184
194
|
|
|
185
|
-
|
|
186
|
-
.
|
|
187
|
-
.
|
|
195
|
+
await SmartArchive.create()
|
|
196
|
+
.format('zip')
|
|
197
|
+
.compression(9)
|
|
198
|
+
.entry('report.txt', 'Monthly sales report...')
|
|
199
|
+
.entry('data/figures.json', JSON.stringify({ revenue: 10000 }))
|
|
200
|
+
.entry('images/logo.png', pngBuffer)
|
|
201
|
+
.toFile('./report-bundle.zip');
|
|
188
202
|
```
|
|
189
203
|
|
|
190
|
-
###
|
|
204
|
+
### Create TAR.GZ from directory
|
|
191
205
|
|
|
192
206
|
```typescript
|
|
193
207
|
import { SmartArchive } from '@push.rocks/smartarchive';
|
|
194
208
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
)
|
|
199
|
-
|
|
209
|
+
await SmartArchive.create()
|
|
210
|
+
.format('tar.gz')
|
|
211
|
+
.compression(9)
|
|
212
|
+
.directory('./src', 'source') // Archive ./src as 'source/' in archive
|
|
213
|
+
.toFile('./project-backup.tar.gz');
|
|
200
214
|
```
|
|
201
215
|
|
|
202
|
-
###
|
|
216
|
+
### Stream-based extraction
|
|
203
217
|
|
|
204
218
|
```typescript
|
|
205
219
|
import { SmartArchive } from '@push.rocks/smartarchive';
|
|
206
|
-
import { Readable } from 'stream';
|
|
207
|
-
|
|
208
|
-
// Process archives entirely in memory
|
|
209
|
-
const compressedBuffer = await fetchCompressedData();
|
|
210
|
-
const memoryStream = Readable.from(compressedBuffer);
|
|
211
220
|
|
|
212
|
-
const
|
|
213
|
-
|
|
221
|
+
const fileStream = await SmartArchive.create()
|
|
222
|
+
.file('./large-archive.tar.gz')
|
|
223
|
+
.toStreamFiles();
|
|
214
224
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
streamFiles.on('data', async (streamFile) => {
|
|
218
|
-
const chunks: Buffer[] = [];
|
|
219
|
-
const readStream = await streamFile.createReadStream();
|
|
225
|
+
fileStream.on('data', async (streamFile) => {
|
|
226
|
+
console.log(`Processing: ${streamFile.relativeFilePath}`);
|
|
220
227
|
|
|
221
|
-
|
|
222
|
-
|
|
228
|
+
if (streamFile.relativeFilePath.endsWith('.json')) {
|
|
229
|
+
const content = await streamFile.getContentAsBuffer();
|
|
230
|
+
const data = JSON.parse(content.toString());
|
|
231
|
+
// Process JSON data...
|
|
223
232
|
}
|
|
224
|
-
|
|
225
|
-
extractedFiles.push({
|
|
226
|
-
name: streamFile.relativeFilePath,
|
|
227
|
-
content: Buffer.concat(chunks)
|
|
228
|
-
});
|
|
229
233
|
});
|
|
230
234
|
|
|
231
|
-
|
|
232
|
-
console.log(
|
|
235
|
+
fileStream.on('end', () => {
|
|
236
|
+
console.log('Extraction complete');
|
|
237
|
+
});
|
|
233
238
|
```
|
|
234
239
|
|
|
235
|
-
###
|
|
236
|
-
|
|
237
|
-
The library automatically handles nested compression. A `.tar.gz` file is:
|
|
238
|
-
1. First decompressed from gzip
|
|
239
|
-
2. Then unpacked from tar
|
|
240
|
-
|
|
241
|
-
This happens transparently:
|
|
240
|
+
### Filter specific file types
|
|
242
241
|
|
|
243
242
|
```typescript
|
|
244
243
|
import { SmartArchive } from '@push.rocks/smartarchive';
|
|
245
244
|
|
|
246
|
-
//
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
245
|
+
// Extract only TypeScript files
|
|
246
|
+
const tsFiles = await SmartArchive.create()
|
|
247
|
+
.url('https://example.com/project.tar.gz')
|
|
248
|
+
.include(/\.ts$/)
|
|
249
|
+
.exclude(/node_modules/)
|
|
250
|
+
.toSmartFiles();
|
|
250
251
|
|
|
251
|
-
|
|
252
|
+
for (const file of tsFiles) {
|
|
253
|
+
console.log(`${file.relative}: ${file.contents.length} bytes`);
|
|
254
|
+
}
|
|
255
|
+
```
|
|
252
256
|
|
|
253
|
-
###
|
|
257
|
+
### Analyze archive without extraction
|
|
254
258
|
|
|
255
|
-
|
|
259
|
+
```typescript
|
|
260
|
+
import { SmartArchive } from '@push.rocks/smartarchive';
|
|
256
261
|
|
|
257
|
-
|
|
262
|
+
const archive = SmartArchive.create()
|
|
263
|
+
.file('./unknown-archive.tar.gz');
|
|
258
264
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
265
|
+
// Get format info
|
|
266
|
+
const info = await archive.analyze();
|
|
267
|
+
console.log(`Format: ${info.format}`);
|
|
268
|
+
console.log(`Compressed: ${info.isCompressed}`);
|
|
262
269
|
|
|
263
|
-
//
|
|
264
|
-
|
|
270
|
+
// List contents
|
|
271
|
+
const entries = await archive.list();
|
|
272
|
+
for (const entry of entries) {
|
|
273
|
+
console.log(`${entry.path} (${entry.isDirectory ? 'dir' : 'file'})`);
|
|
274
|
+
}
|
|
265
275
|
|
|
266
|
-
//
|
|
267
|
-
|
|
276
|
+
// Check for specific file
|
|
277
|
+
if (await archive.hasFile('package.json')) {
|
|
278
|
+
const pkgFile = await archive.extractFile('package.json');
|
|
279
|
+
console.log(pkgFile?.contents.toString());
|
|
280
|
+
}
|
|
268
281
|
```
|
|
269
282
|
|
|
270
|
-
|
|
283
|
+
### Working with GZIP files
|
|
271
284
|
|
|
272
285
|
```typescript
|
|
273
|
-
|
|
274
|
-
// fileName is optional - used for single-file archives (like .gz) that don't store filename
|
|
275
|
-
exportToFs(targetDir: string, fileName?: string): Promise<void>
|
|
286
|
+
import { SmartArchive, GzipTools } from '@push.rocks/smartarchive';
|
|
276
287
|
|
|
277
|
-
//
|
|
278
|
-
|
|
288
|
+
// Decompress a .gz file
|
|
289
|
+
await SmartArchive.create()
|
|
290
|
+
.file('./data.json.gz')
|
|
291
|
+
.fileName('data.json') // Specify output name (gzip doesn't store filename)
|
|
292
|
+
.extract('./decompressed');
|
|
279
293
|
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
```
|
|
294
|
+
// Use GzipTools directly for compression/decompression
|
|
295
|
+
const gzipTools = new GzipTools();
|
|
283
296
|
|
|
284
|
-
|
|
297
|
+
// Compress a buffer
|
|
298
|
+
const compressed = await gzipTools.compress(Buffer.from('Hello World'), 9);
|
|
299
|
+
const decompressed = await gzipTools.decompress(compressed);
|
|
285
300
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
archive.gzipTools // GzipTools instance for GZIP-specific operations
|
|
290
|
-
archive.bzip2Tools // Bzip2Tools instance for BZIP2-specific operations
|
|
291
|
-
archive.archiveAnalyzer // ArchiveAnalyzer for inspecting archive type
|
|
292
|
-
```
|
|
301
|
+
// Synchronous operations
|
|
302
|
+
const compressedSync = gzipTools.compressSync(inputBuffer, 6);
|
|
303
|
+
const decompressedSync = gzipTools.decompressSync(compressedSync);
|
|
293
304
|
|
|
294
|
-
|
|
305
|
+
// Streaming
|
|
306
|
+
const compressStream = gzipTools.getCompressionStream(6);
|
|
307
|
+
const decompressStream = gzipTools.getDecompressionStream();
|
|
295
308
|
|
|
296
|
-
|
|
309
|
+
createReadStream('./input.txt')
|
|
310
|
+
.pipe(compressStream)
|
|
311
|
+
.pipe(createWriteStream('./output.gz'));
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Working with TAR archives directly
|
|
297
315
|
|
|
298
316
|
```typescript
|
|
299
317
|
import { TarTools } from '@push.rocks/smartarchive';
|
|
300
318
|
|
|
301
319
|
const tarTools = new TarTools();
|
|
302
320
|
|
|
303
|
-
//
|
|
321
|
+
// Create a TAR archive manually
|
|
304
322
|
const pack = await tarTools.getPackStream();
|
|
305
323
|
|
|
306
|
-
// Add files to a pack stream
|
|
307
324
|
await tarTools.addFileToPack(pack, {
|
|
308
|
-
fileName: '
|
|
309
|
-
content: 'Hello World'
|
|
310
|
-
|
|
311
|
-
|
|
325
|
+
fileName: 'hello.txt',
|
|
326
|
+
content: 'Hello, World!'
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
await tarTools.addFileToPack(pack, {
|
|
330
|
+
fileName: 'data.json',
|
|
331
|
+
content: Buffer.from(JSON.stringify({ foo: 'bar' }))
|
|
312
332
|
});
|
|
313
333
|
|
|
314
|
-
|
|
315
|
-
|
|
334
|
+
pack.finalize();
|
|
335
|
+
pack.pipe(createWriteStream('./output.tar'));
|
|
316
336
|
|
|
317
|
-
//
|
|
318
|
-
const
|
|
319
|
-
```
|
|
337
|
+
// Pack a directory to TAR.GZ buffer
|
|
338
|
+
const tgzBuffer = await tarTools.packDirectoryToTarGz('./src', 6);
|
|
320
339
|
|
|
321
|
-
|
|
340
|
+
// Pack a directory to TAR.GZ stream
|
|
341
|
+
const tgzStream = await tarTools.packDirectoryToTarGzStream('./src');
|
|
342
|
+
```
|
|
322
343
|
|
|
323
|
-
ZIP
|
|
344
|
+
### Working with ZIP archives directly
|
|
324
345
|
|
|
325
346
|
```typescript
|
|
326
347
|
import { ZipTools } from '@push.rocks/smartarchive';
|
|
327
348
|
|
|
328
349
|
const zipTools = new ZipTools();
|
|
329
350
|
|
|
330
|
-
//
|
|
331
|
-
const
|
|
351
|
+
// Create a ZIP archive from entries
|
|
352
|
+
const zipBuffer = await zipTools.createZip([
|
|
353
|
+
{ archivePath: 'readme.txt', content: 'Hello!' },
|
|
354
|
+
{ archivePath: 'data.bin', content: Buffer.from([0x00, 0x01, 0x02]) }
|
|
355
|
+
], 6);
|
|
332
356
|
|
|
333
|
-
//
|
|
334
|
-
const
|
|
357
|
+
// Extract a ZIP buffer
|
|
358
|
+
const entries = await zipTools.extractZip(zipBuffer);
|
|
359
|
+
for (const entry of entries) {
|
|
360
|
+
console.log(`${entry.path}: ${entry.content.length} bytes`);
|
|
361
|
+
}
|
|
335
362
|
```
|
|
336
363
|
|
|
337
|
-
###
|
|
338
|
-
|
|
339
|
-
GZIP compression/decompression streams.
|
|
364
|
+
### In-memory round-trip
|
|
340
365
|
|
|
341
366
|
```typescript
|
|
342
|
-
import {
|
|
343
|
-
|
|
344
|
-
const gzipTools = new GzipTools();
|
|
345
|
-
|
|
346
|
-
// Get compression stream
|
|
347
|
-
const compressor = gzipTools.getCompressionStream();
|
|
348
|
-
|
|
349
|
-
// Get decompression stream
|
|
350
|
-
const decompressor = gzipTools.getDecompressionStream();
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
## Supported Formats 📋
|
|
354
|
-
|
|
355
|
-
| Format | Extension(s) | Extract | Create |
|
|
356
|
-
|--------|--------------|---------|--------|
|
|
357
|
-
| TAR | `.tar` | ✅ | ✅ |
|
|
358
|
-
| TAR.GZ / TGZ | `.tar.gz`, `.tgz` | ✅ | ⚠️ |
|
|
359
|
-
| ZIP | `.zip` | ✅ | ⚠️ |
|
|
360
|
-
| GZIP | `.gz` | ✅ | ✅ |
|
|
361
|
-
| BZIP2 | `.bz2` | ✅ | ❌ |
|
|
362
|
-
|
|
363
|
-
✅ Full support | ⚠️ Partial/basic support | ❌ Not supported
|
|
364
|
-
|
|
365
|
-
## Performance Tips 🏎️
|
|
367
|
+
import { SmartArchive } from '@push.rocks/smartarchive';
|
|
366
368
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
369
|
+
// Create archive in memory
|
|
370
|
+
const archive = await SmartArchive.create()
|
|
371
|
+
.format('tar.gz')
|
|
372
|
+
.entry('config.json', JSON.stringify({ version: '1.0.0' }))
|
|
373
|
+
.build();
|
|
371
374
|
|
|
372
|
-
|
|
375
|
+
const buffer = await archive.toBuffer();
|
|
373
376
|
|
|
374
|
-
|
|
375
|
-
|
|
377
|
+
// Extract from buffer
|
|
378
|
+
const files = await SmartArchive.create()
|
|
379
|
+
.buffer(buffer)
|
|
380
|
+
.toSmartFiles();
|
|
376
381
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
await archive.exportToFs('./output');
|
|
380
|
-
} catch (error) {
|
|
381
|
-
if (error.code === 'ENOENT') {
|
|
382
|
-
console.error('Archive file not found');
|
|
383
|
-
} else if (error.code === 'EACCES') {
|
|
384
|
-
console.error('Permission denied');
|
|
385
|
-
} else if (error.message.includes('fetch')) {
|
|
386
|
-
console.error('Network error downloading archive');
|
|
387
|
-
} else {
|
|
388
|
-
console.error('Archive extraction failed:', error.message);
|
|
389
|
-
}
|
|
382
|
+
for (const file of files) {
|
|
383
|
+
console.log(`${file.relative}: ${file.contents.toString()}`);
|
|
390
384
|
}
|
|
391
385
|
```
|
|
392
386
|
|
|
@@ -395,51 +389,139 @@ try {
|
|
|
395
389
|
### CI/CD: Download & Extract Build Artifacts
|
|
396
390
|
|
|
397
391
|
```typescript
|
|
398
|
-
const artifacts = await SmartArchive.
|
|
399
|
-
`${CI_SERVER}/artifacts/build-${BUILD_ID}.zip`
|
|
400
|
-
)
|
|
401
|
-
|
|
392
|
+
const artifacts = await SmartArchive.create()
|
|
393
|
+
.url(`${CI_SERVER}/artifacts/build-${BUILD_ID}.zip`)
|
|
394
|
+
.stripComponents(1)
|
|
395
|
+
.extract('./dist');
|
|
402
396
|
```
|
|
403
397
|
|
|
404
|
-
### Backup System
|
|
398
|
+
### Backup System
|
|
405
399
|
|
|
406
400
|
```typescript
|
|
407
|
-
|
|
408
|
-
await
|
|
401
|
+
// Create backup
|
|
402
|
+
await SmartArchive.create()
|
|
403
|
+
.format('tar.gz')
|
|
404
|
+
.compression(9)
|
|
405
|
+
.directory('./data')
|
|
406
|
+
.toFile(`./backups/backup-${Date.now()}.tar.gz`);
|
|
407
|
+
|
|
408
|
+
// Restore backup
|
|
409
|
+
await SmartArchive.create()
|
|
410
|
+
.file('./backups/backup-latest.tar.gz')
|
|
411
|
+
.extract('/restore/location');
|
|
409
412
|
```
|
|
410
413
|
|
|
411
|
-
###
|
|
414
|
+
### Bundle files for HTTP download
|
|
412
415
|
|
|
413
416
|
```typescript
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
417
|
+
import { SmartArchive } from '@push.rocks/smartarchive';
|
|
418
|
+
|
|
419
|
+
// Express/Fastify handler
|
|
420
|
+
app.get('/download-bundle', async (req, res) => {
|
|
421
|
+
const buffer = await SmartArchive.create()
|
|
422
|
+
.format('zip')
|
|
423
|
+
.entry('report.pdf', pdfBuffer)
|
|
424
|
+
.entry('data.xlsx', excelBuffer)
|
|
425
|
+
.entry('images/chart.png', chartBuffer)
|
|
426
|
+
.toBuffer();
|
|
427
|
+
|
|
428
|
+
res.setHeader('Content-Type', 'application/zip');
|
|
429
|
+
res.setHeader('Content-Disposition', 'attachment; filename=report-bundle.zip');
|
|
430
|
+
res.send(buffer);
|
|
424
431
|
});
|
|
425
432
|
```
|
|
426
433
|
|
|
427
434
|
### Data Pipeline: Process Compressed Datasets
|
|
428
435
|
|
|
429
436
|
```typescript
|
|
430
|
-
const
|
|
431
|
-
'https://data.source/dataset.tar.gz'
|
|
432
|
-
);
|
|
437
|
+
const fileStream = await SmartArchive.create()
|
|
438
|
+
.url('https://data.source/dataset.tar.gz')
|
|
439
|
+
.toStreamFiles();
|
|
433
440
|
|
|
434
|
-
|
|
435
|
-
files.on('data', async (file) => {
|
|
441
|
+
fileStream.on('data', async (file) => {
|
|
436
442
|
if (file.relativeFilePath.endsWith('.csv')) {
|
|
437
|
-
const
|
|
438
|
-
// Stream CSV processing
|
|
443
|
+
const content = await file.getContentAsBuffer();
|
|
444
|
+
// Stream CSV processing...
|
|
439
445
|
}
|
|
440
446
|
});
|
|
441
447
|
```
|
|
442
448
|
|
|
449
|
+
## Supported Formats 📋
|
|
450
|
+
|
|
451
|
+
| Format | Extension(s) | Extract | Create |
|
|
452
|
+
|--------|--------------|---------|--------|
|
|
453
|
+
| TAR | `.tar` | ✅ | ✅ |
|
|
454
|
+
| TAR.GZ / TGZ | `.tar.gz`, `.tgz` | ✅ | ✅ |
|
|
455
|
+
| ZIP | `.zip` | ✅ | ✅ |
|
|
456
|
+
| GZIP | `.gz` | ✅ | ✅ |
|
|
457
|
+
| BZIP2 | `.bz2` | ✅ | ❌ |
|
|
458
|
+
|
|
459
|
+
## Type Definitions
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
// Supported archive formats
|
|
463
|
+
type TArchiveFormat = 'tar' | 'tar.gz' | 'tgz' | 'zip' | 'gz' | 'bz2';
|
|
464
|
+
|
|
465
|
+
// Compression level (0 = none, 9 = maximum)
|
|
466
|
+
type TCompressionLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
|
|
467
|
+
|
|
468
|
+
// Entry for creating archives
|
|
469
|
+
interface IArchiveEntry {
|
|
470
|
+
archivePath: string;
|
|
471
|
+
content: string | Buffer | Readable | SmartFile | StreamFile;
|
|
472
|
+
size?: number;
|
|
473
|
+
mode?: number;
|
|
474
|
+
mtime?: Date;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Information about an archive entry
|
|
478
|
+
interface IArchiveEntryInfo {
|
|
479
|
+
path: string;
|
|
480
|
+
size: number;
|
|
481
|
+
isDirectory: boolean;
|
|
482
|
+
isFile: boolean;
|
|
483
|
+
mtime?: Date;
|
|
484
|
+
mode?: number;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Archive analysis result
|
|
488
|
+
interface IArchiveInfo {
|
|
489
|
+
format: TArchiveFormat | null;
|
|
490
|
+
isCompressed: boolean;
|
|
491
|
+
isArchive: boolean;
|
|
492
|
+
entries?: IArchiveEntryInfo[];
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
## Performance Tips 🏎️
|
|
497
|
+
|
|
498
|
+
1. **Use streaming for large files** – `.toStreamFiles()` processes entries one at a time without loading the entire archive
|
|
499
|
+
2. **Provide byte lengths when known** – When using TarTools directly, provide `byteLength` for better performance
|
|
500
|
+
3. **Choose appropriate compression** – Use 1-3 for speed, 6 (default) for balance, 9 for maximum compression
|
|
501
|
+
4. **Filter early** – Use `.include()`/`.exclude()` to skip unwanted entries before processing
|
|
502
|
+
|
|
503
|
+
## Error Handling 🛡️
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
import { SmartArchive } from '@push.rocks/smartarchive';
|
|
507
|
+
|
|
508
|
+
try {
|
|
509
|
+
await SmartArchive.create()
|
|
510
|
+
.url('https://example.com/file.zip')
|
|
511
|
+
.extract('./output');
|
|
512
|
+
} catch (error) {
|
|
513
|
+
if (error.message.includes('No source configured')) {
|
|
514
|
+
console.error('Forgot to specify source');
|
|
515
|
+
} else if (error.message.includes('No format specified')) {
|
|
516
|
+
console.error('Forgot to set format for creation');
|
|
517
|
+
} else if (error.message.includes('extraction mode')) {
|
|
518
|
+
console.error('Cannot mix extraction and creation methods');
|
|
519
|
+
} else {
|
|
520
|
+
console.error('Archive operation failed:', error.message);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
443
525
|
## License and Legal Information
|
|
444
526
|
|
|
445
527
|
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.
|
|
@@ -450,6 +532,10 @@ This repository contains open-source code that is licensed under the MIT License
|
|
|
450
532
|
|
|
451
533
|
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.
|
|
452
534
|
|
|
535
|
+
### Issue Reporting and Security
|
|
536
|
+
|
|
537
|
+
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
|
|
538
|
+
|
|
453
539
|
### Company Information
|
|
454
540
|
|
|
455
541
|
Task Venture Capital GmbH
|