@push.rocks/smartarchive 5.0.0 → 5.0.1

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/readme.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # @push.rocks/smartarchive 📦
2
2
 
3
- Powerful archive manipulation for modern Node.js applications.
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
- - 🔄 **Unified API** – Consistent interface across different archive formats
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 streams seamlessly
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
- const archive = await SmartArchive.fromArchiveUrl(
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
- ### Process archive as a stream
47
+ ### Create an archive from entries
49
48
 
50
49
  ```typescript
51
50
  import { SmartArchive } from '@push.rocks/smartarchive';
52
51
 
53
- // Stream-based processing for memory efficiency
54
- const archive = await SmartArchive.fromArchiveFile('./large-archive.zip');
55
- const streamOfFiles = await archive.exportToStreamOfStreamFiles();
56
-
57
- // Process each file in the archive
58
- streamOfFiles.on('data', async (streamFile) => {
59
- console.log(`Processing ${streamFile.relativeFilePath}`);
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
- ## Core Concepts 💡
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 a ZIP file
96
- const zipArchive = await SmartArchive.fromArchiveFile('./archive.zip');
97
- await zipArchive.exportToFs('./output');
98
-
99
- // Stream ZIP contents for processing
100
- const fileStream = await zipArchive.exportToStreamOfStreamFiles();
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
- ### Working with TAR archives
74
+ ## Core Concepts 💡
111
75
 
112
- ```typescript
113
- import { SmartArchive, TarTools } from '@push.rocks/smartarchive';
76
+ ### Fluent Builder Pattern
114
77
 
115
- // Extract a .tar.gz file
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
- // Create a TAR archive using TarTools directly
120
- const tarTools = new TarTools();
121
- const pack = await tarTools.getPackStream();
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
- // Add files to the pack
124
- await tarTools.addFileToPack(pack, {
125
- fileName: 'hello.txt',
126
- content: 'Hello, World!'
127
- });
87
+ ### Two Operating Modes
128
88
 
129
- await tarTools.addFileToPack(pack, {
130
- fileName: 'data.json',
131
- content: Buffer.from(JSON.stringify({ foo: 'bar' }))
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
- // Finalize and pipe to destination
135
- pack.finalize();
136
- pack.pipe(createWriteStream('./output.tar'));
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
- ### Pack a directory into TAR
104
+ > ⚠️ **Note:** You cannot mix extraction and creation methods in the same chain.
140
105
 
141
- ```typescript
142
- import { TarTools } from '@push.rocks/smartarchive';
143
- import { createWriteStream } from 'fs';
106
+ ## API Reference 📚
144
107
 
145
- const tarTools = new TarTools();
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
- // Pack an entire directory
148
- const pack = await tarTools.packDirectory('./src');
149
- pack.finalize();
150
- pack.pipe(createWriteStream('./source.tar'));
151
- ```
166
+ ## Usage Examples 🔨
152
167
 
153
- ### Extracting from URLs
168
+ ### Download and extract npm packages
154
169
 
155
170
  ```typescript
156
171
  import { SmartArchive } from '@push.rocks/smartarchive';
157
172
 
158
- // Download and extract npm packages
159
- const npmPackage = await SmartArchive.fromArchiveUrl(
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
- // Or process as stream for memory efficiency
165
- const stream = await npmPackage.exportToStreamOfStreamFiles();
166
- stream.on('data', async (file) => {
167
- console.log(`Extracted: ${file.relativeFilePath}`);
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
- ### Working with GZIP files
190
+ ### Create ZIP archive
172
191
 
173
192
  ```typescript
174
- import { SmartArchive, GzipTools } from '@push.rocks/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
- createReadStream('./compressed.gz')
186
- .pipe(decompressStream)
187
- .pipe(createWriteStream('./decompressed.txt'));
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
- ### Working with BZIP2 files
204
+ ### Create TAR.GZ from directory
191
205
 
192
206
  ```typescript
193
207
  import { SmartArchive } from '@push.rocks/smartarchive';
194
208
 
195
- // Handle .bz2 files
196
- const bzipArchive = await SmartArchive.fromArchiveUrl(
197
- 'https://example.com/data.bz2'
198
- );
199
- await bzipArchive.exportToFs('./extracted', 'data.txt');
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
- ### In-memory processing (no filesystem)
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 archive = await SmartArchive.fromArchiveStream(memoryStream);
213
- const streamFiles = await archive.exportToStreamOfStreamFiles();
221
+ const fileStream = await SmartArchive.create()
222
+ .file('./large-archive.tar.gz')
223
+ .toStreamFiles();
214
224
 
215
- const extractedFiles: Array<{ name: string; content: Buffer }> = [];
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
- for await (const chunk of readStream) {
222
- chunks.push(chunk);
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
- await new Promise((resolve) => streamFiles.on('end', resolve));
232
- console.log(`Extracted ${extractedFiles.length} files in memory`);
235
+ fileStream.on('end', () => {
236
+ console.log('Extraction complete');
237
+ });
233
238
  ```
234
239
 
235
- ### Nested archive handling (e.g., .tar.gz)
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
- // Automatically handles gzip → tar extraction chain
247
- const tgzArchive = await SmartArchive.fromArchiveFile('./package.tar.gz');
248
- await tgzArchive.exportToFs('./extracted');
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
- ## API Reference 📚
252
+ for (const file of tsFiles) {
253
+ console.log(`${file.relative}: ${file.contents.length} bytes`);
254
+ }
255
+ ```
252
256
 
253
- ### SmartArchive Class
257
+ ### Analyze archive without extraction
254
258
 
255
- The main entry point for archive operations.
259
+ ```typescript
260
+ import { SmartArchive } from '@push.rocks/smartarchive';
256
261
 
257
- #### Static Factory Methods
262
+ const archive = SmartArchive.create()
263
+ .file('./unknown-archive.tar.gz');
258
264
 
259
- ```typescript
260
- // Create from URL - downloads and processes archive
261
- SmartArchive.fromArchiveUrl(url: string): Promise<SmartArchive>
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
- // Create from local file path
264
- SmartArchive.fromArchiveFile(path: string): Promise<SmartArchive>
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
- // Create from any Node.js readable stream
267
- SmartArchive.fromArchiveStream(stream: Readable | Duplex | Transform): Promise<SmartArchive>
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
- #### Instance Methods
283
+ ### Working with GZIP files
271
284
 
272
285
  ```typescript
273
- // Extract all files to a directory
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
- // Get a stream that emits StreamFile objects for each file in the archive
278
- exportToStreamOfStreamFiles(): Promise<StreamIntake<StreamFile>>
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
- // Get the raw archive stream (useful for piping)
281
- getArchiveStream(): Promise<Readable>
282
- ```
294
+ // Use GzipTools directly for compression/decompression
295
+ const gzipTools = new GzipTools();
283
296
 
284
- #### Instance Properties
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
- ```typescript
287
- archive.tarTools // TarTools instance for TAR-specific operations
288
- archive.zipTools // ZipTools instance for ZIP-specific operations
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
- ### TarTools Class
305
+ // Streaming
306
+ const compressStream = gzipTools.getCompressionStream(6);
307
+ const decompressStream = gzipTools.getDecompressionStream();
295
308
 
296
- TAR-specific operations for creating and extracting TAR archives.
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
- // Get a tar pack stream for creating archives
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: 'file.txt', // Name in archive
309
- content: 'Hello World', // String, Buffer, Readable, SmartFile, or StreamFile
310
- byteLength?: number, // Optional: specify size for streams
311
- filePath?: string // Optional: path to file on disk
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
- // Pack an entire directory
315
- const pack = await tarTools.packDirectory('./src');
334
+ pack.finalize();
335
+ pack.pipe(createWriteStream('./output.tar'));
316
336
 
317
- // Get extraction stream
318
- const extract = tarTools.getDecompressionStream();
319
- ```
337
+ // Pack a directory to TAR.GZ buffer
338
+ const tgzBuffer = await tarTools.packDirectoryToTarGz('./src', 6);
320
339
 
321
- ### ZipTools Class
340
+ // Pack a directory to TAR.GZ stream
341
+ const tgzStream = await tarTools.packDirectoryToTarGzStream('./src');
342
+ ```
322
343
 
323
- ZIP-specific operations.
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
- // Get compression stream (for creating ZIP)
331
- const compressor = zipTools.getCompressionStream();
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
- // Get decompression stream (for extracting ZIP)
334
- const decompressor = zipTools.getDecompressionStream();
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
- ### GzipTools Class
338
-
339
- GZIP compression/decompression streams.
364
+ ### In-memory round-trip
340
365
 
341
366
  ```typescript
342
- import { GzipTools } from '@push.rocks/smartarchive';
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
- 1. **Use streaming for large files** – Avoid loading entire archives into memory with `exportToStreamOfStreamFiles()`
368
- 2. **Provide byte lengths when known** – When adding streams to TAR, provide `byteLength` for better performance
369
- 3. **Process files as they stream** – Don't collect all files into an array unless necessary
370
- 4. **Choose the right format** – TAR.GZ for Unix/compression, ZIP for cross-platform compatibility
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
- ## Error Handling 🛡️
375
+ const buffer = await archive.toBuffer();
373
376
 
374
- ```typescript
375
- import { SmartArchive } from '@push.rocks/smartarchive';
377
+ // Extract from buffer
378
+ const files = await SmartArchive.create()
379
+ .buffer(buffer)
380
+ .toSmartFiles();
376
381
 
377
- try {
378
- const archive = await SmartArchive.fromArchiveUrl('https://example.com/file.zip');
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.fromArchiveUrl(
399
- `${CI_SERVER}/artifacts/build-${BUILD_ID}.zip`
400
- );
401
- await artifacts.exportToFs('./dist');
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: Restore from Archive
398
+ ### Backup System
405
399
 
406
400
  ```typescript
407
- const backup = await SmartArchive.fromArchiveFile('./backup-2024.tar.gz');
408
- await backup.exportToFs('/restore/location');
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
- ### NPM Package Inspection
414
+ ### Bundle files for HTTP download
412
415
 
413
416
  ```typescript
414
- const pkg = await SmartArchive.fromArchiveUrl(
415
- 'https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz'
416
- );
417
- const files = await pkg.exportToStreamOfStreamFiles();
418
-
419
- files.on('data', async (file) => {
420
- if (file.relativeFilePath.includes('package.json')) {
421
- const stream = await file.createReadStream();
422
- // Read and analyze package.json
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 dataset = await SmartArchive.fromArchiveUrl(
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
- const files = await dataset.exportToStreamOfStreamFiles();
435
- files.on('data', async (file) => {
441
+ fileStream.on('data', async (file) => {
436
442
  if (file.relativeFilePath.endsWith('.csv')) {
437
- const stream = await file.createReadStream();
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