@push.rocks/smartarchive 5.1.0 → 5.2.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.
Files changed (45) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/classes.smartarchive.d.ts +4 -4
  3. package/dist_ts/classes.smartarchive.js +29 -24
  4. package/dist_ts/classes.tartools.d.ts +81 -6
  5. package/dist_ts/classes.tartools.js +202 -16
  6. package/dist_ts/index.d.ts +2 -2
  7. package/dist_ts/index.js +3 -3
  8. package/dist_ts/plugins.d.ts +3 -1
  9. package/dist_ts/plugins.js +5 -2
  10. package/dist_ts_shared/bzip2/bititerator.d.ts +6 -0
  11. package/dist_ts_shared/bzip2/bititerator.js +50 -0
  12. package/dist_ts_shared/bzip2/bzip2.d.ts +29 -0
  13. package/dist_ts_shared/bzip2/bzip2.js +398 -0
  14. package/dist_ts_shared/bzip2/index.d.ts +5 -0
  15. package/dist_ts_shared/bzip2/index.js +92 -0
  16. package/dist_ts_shared/classes.bzip2tools.d.ts +10 -0
  17. package/dist_ts_shared/classes.bzip2tools.js +14 -0
  18. package/dist_ts_shared/classes.gziptools.d.ts +26 -0
  19. package/dist_ts_shared/classes.gziptools.js +38 -0
  20. package/dist_ts_shared/classes.tartools.d.ts +30 -0
  21. package/dist_ts_shared/classes.tartools.js +78 -0
  22. package/dist_ts_shared/classes.ziptools.d.ts +31 -0
  23. package/dist_ts_shared/classes.ziptools.js +103 -0
  24. package/dist_ts_shared/errors.d.ts +44 -0
  25. package/dist_ts_shared/errors.js +62 -0
  26. package/dist_ts_shared/index.d.ts +8 -0
  27. package/dist_ts_shared/index.js +14 -0
  28. package/dist_ts_shared/interfaces.d.ts +129 -0
  29. package/dist_ts_shared/interfaces.js +2 -0
  30. package/dist_ts_shared/plugins.d.ts +9 -0
  31. package/dist_ts_shared/plugins.js +14 -0
  32. package/dist_ts_web/00_commitinfo_data.d.ts +8 -0
  33. package/dist_ts_web/00_commitinfo_data.js +9 -0
  34. package/dist_ts_web/index.d.ts +1 -0
  35. package/dist_ts_web/index.js +4 -0
  36. package/dist_ts_web/plugins.d.ts +1 -0
  37. package/dist_ts_web/plugins.js +4 -0
  38. package/package.json +7 -5
  39. package/readme.md +154 -46
  40. package/ts/00_commitinfo_data.ts +1 -1
  41. package/ts/classes.smartarchive.ts +28 -23
  42. package/ts/classes.tartools.ts +235 -14
  43. package/ts/index.ts +2 -2
  44. package/ts/plugins.ts +4 -0
  45. package/ts_web/00_commitinfo_data.ts +1 -1
@@ -1,14 +1,230 @@
1
1
  import * as plugins from './plugins.js';
2
2
  import type { IArchiveEntry, TCompressionLevel } from '../ts_shared/interfaces.js';
3
3
  import { TarTools as SharedTarTools } from '../ts_shared/classes.tartools.js';
4
- import { GzipTools } from '../ts_shared/classes.gziptools.js';
5
4
 
6
5
  /**
7
- * Extended TAR archive utilities with Node.js filesystem support
6
+ * Options for adding a file to a TAR pack stream
7
+ */
8
+ export interface ITarPackFileOptions {
9
+ fileName: string;
10
+ content: string | Buffer | Uint8Array | plugins.stream.Readable;
11
+ size?: number;
12
+ mode?: number;
13
+ mtime?: Date;
14
+ }
15
+
16
+ /**
17
+ * Extended TAR archive utilities with Node.js streaming support
18
+ *
19
+ * For small archives: Use inherited buffer-based methods (packFiles, extractTar, etc.)
20
+ * For large archives: Use streaming methods (getPackStream, getExtractStream, etc.)
8
21
  */
9
22
  export class TarTools extends SharedTarTools {
23
+ // ============================================
24
+ // STREAMING PACK METHODS (for large files)
25
+ // ============================================
26
+
27
+ /**
28
+ * Get a streaming TAR pack instance
29
+ * Use this for packing large files without buffering everything in memory
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const pack = tarTools.getPackStream();
34
+ *
35
+ * await tarTools.addFileToPack(pack, { fileName: 'large.bin', content: readStream, size: fileSize });
36
+ * await tarTools.addFileToPack(pack, { fileName: 'small.txt', content: 'Hello World' });
37
+ *
38
+ * pack.finalize();
39
+ * pack.pipe(fs.createWriteStream('output.tar'));
40
+ * ```
41
+ */
42
+ public getPackStream(): plugins.tarStream.Pack {
43
+ return plugins.tarStream.pack();
44
+ }
45
+
46
+ /**
47
+ * Add a file to a TAR pack stream
48
+ * Supports strings, buffers, and readable streams
49
+ *
50
+ * @param pack - The pack stream from getPackStream()
51
+ * @param options - File options including name, content, and optional metadata
52
+ */
53
+ public async addFileToPack(
54
+ pack: plugins.tarStream.Pack,
55
+ options: ITarPackFileOptions
56
+ ): Promise<void> {
57
+ const { fileName, content, mode = 0o644, mtime = new Date() } = options;
58
+
59
+ if (typeof content === 'string') {
60
+ // String content - convert to buffer
61
+ const buffer = Buffer.from(content, 'utf8');
62
+ const entry = pack.entry({
63
+ name: fileName,
64
+ size: buffer.length,
65
+ mode,
66
+ mtime,
67
+ });
68
+ entry.write(buffer);
69
+ entry.end();
70
+ } else if (Buffer.isBuffer(content) || content instanceof Uint8Array) {
71
+ // Buffer content
72
+ const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
73
+ const entry = pack.entry({
74
+ name: fileName,
75
+ size: buffer.length,
76
+ mode,
77
+ mtime,
78
+ });
79
+ entry.write(buffer);
80
+ entry.end();
81
+ } else if (content && typeof (content as any).pipe === 'function') {
82
+ // Readable stream - requires size to be provided
83
+ const size = options.size;
84
+ if (size === undefined) {
85
+ throw new Error('Size must be provided when adding a stream to TAR pack');
86
+ }
87
+
88
+ return new Promise<void>((resolve, reject) => {
89
+ const entry = pack.entry({
90
+ name: fileName,
91
+ size,
92
+ mode,
93
+ mtime,
94
+ }, (err) => {
95
+ if (err) reject(err);
96
+ else resolve();
97
+ });
98
+
99
+ (content as plugins.stream.Readable).pipe(entry);
100
+ });
101
+ } else {
102
+ throw new Error('Unsupported content type for TAR entry');
103
+ }
104
+ }
105
+
106
+ // ============================================
107
+ // STREAMING EXTRACT METHODS (for large files)
108
+ // ============================================
109
+
110
+ /**
111
+ * Get a streaming TAR extract instance
112
+ * Use this for extracting large archives without buffering everything in memory
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * const extract = tarTools.getExtractStream();
117
+ *
118
+ * extract.on('entry', (header, stream, next) => {
119
+ * console.log(`Extracting: ${header.name}`);
120
+ * stream.pipe(fs.createWriteStream(`./out/${header.name}`));
121
+ * stream.on('end', next);
122
+ * });
123
+ *
124
+ * fs.createReadStream('archive.tar').pipe(extract);
125
+ * ```
126
+ */
127
+ public getExtractStream(): plugins.tarStream.Extract {
128
+ return plugins.tarStream.extract();
129
+ }
130
+
131
+ /**
132
+ * Extract a TAR stream to a directory with true streaming (no buffering)
133
+ *
134
+ * @param sourceStream - The TAR archive stream
135
+ * @param targetDir - Directory to extract files to
136
+ */
137
+ public async extractToDirectory(
138
+ sourceStream: plugins.stream.Readable,
139
+ targetDir: string
140
+ ): Promise<void> {
141
+ await plugins.fsPromises.mkdir(targetDir, { recursive: true });
142
+
143
+ return new Promise<void>((resolve, reject) => {
144
+ const extract = this.getExtractStream();
145
+
146
+ extract.on('entry', async (header, stream, next) => {
147
+ const filePath = plugins.path.join(targetDir, header.name);
148
+
149
+ if (header.type === 'directory') {
150
+ await plugins.fsPromises.mkdir(filePath, { recursive: true });
151
+ stream.resume(); // Drain the stream
152
+ next();
153
+ } else if (header.type === 'file') {
154
+ await plugins.fsPromises.mkdir(plugins.path.dirname(filePath), { recursive: true });
155
+ const writeStream = plugins.fs.createWriteStream(filePath);
156
+ stream.pipe(writeStream);
157
+ writeStream.on('finish', next);
158
+ writeStream.on('error', reject);
159
+ } else {
160
+ stream.resume(); // Skip other types
161
+ next();
162
+ }
163
+ });
164
+
165
+ extract.on('finish', resolve);
166
+ extract.on('error', reject);
167
+
168
+ sourceStream.pipe(extract);
169
+ });
170
+ }
171
+
172
+ // ============================================
173
+ // STREAMING DIRECTORY PACK (for large directories)
174
+ // ============================================
175
+
10
176
  /**
11
- * Pack a directory into a TAR buffer (Node.js only)
177
+ * Pack a directory into a TAR stream with true streaming (no buffering)
178
+ * Files are read and written one at a time, never loading everything into memory
179
+ */
180
+ public async getDirectoryPackStream(directoryPath: string): Promise<plugins.tarStream.Pack> {
181
+ const pack = this.getPackStream();
182
+ const fileTree = await plugins.listFileTree(directoryPath, '**/*');
183
+
184
+ // Process files sequentially to avoid memory issues
185
+ (async () => {
186
+ for (const filePath of fileTree) {
187
+ const absolutePath = plugins.path.join(directoryPath, filePath);
188
+ const stat = await plugins.fsPromises.stat(absolutePath);
189
+
190
+ if (stat.isFile()) {
191
+ const readStream = plugins.fs.createReadStream(absolutePath);
192
+ await this.addFileToPack(pack, {
193
+ fileName: filePath,
194
+ content: readStream,
195
+ size: stat.size,
196
+ mode: stat.mode,
197
+ mtime: stat.mtime,
198
+ });
199
+ }
200
+ }
201
+ pack.finalize();
202
+ })().catch((err) => pack.destroy(err));
203
+
204
+ return pack;
205
+ }
206
+
207
+ /**
208
+ * Pack a directory into a TAR.GZ stream with true streaming
209
+ * Uses Node.js zlib for streaming compression
210
+ */
211
+ public async getDirectoryPackStreamGz(
212
+ directoryPath: string,
213
+ compressionLevel?: TCompressionLevel
214
+ ): Promise<plugins.stream.Readable> {
215
+ const tarStream = await this.getDirectoryPackStream(directoryPath);
216
+ const { createGzip } = await import('node:zlib');
217
+ const gzip = createGzip({ level: compressionLevel ?? 6 });
218
+ return tarStream.pipe(gzip);
219
+ }
220
+
221
+ // ============================================
222
+ // BUFFER-BASED METHODS (inherited + filesystem)
223
+ // ============================================
224
+
225
+ /**
226
+ * Pack a directory into a TAR buffer (loads all files into memory)
227
+ * For large directories, use getDirectoryPackStream() instead
12
228
  */
13
229
  public async packDirectory(directoryPath: string): Promise<Uint8Array> {
14
230
  const fileTree = await plugins.listFileTree(directoryPath, '**/*');
@@ -16,36 +232,41 @@ export class TarTools extends SharedTarTools {
16
232
 
17
233
  for (const filePath of fileTree) {
18
234
  const absolutePath = plugins.path.join(directoryPath, filePath);
19
- const content = await plugins.fsPromises.readFile(absolutePath);
20
- entries.push({
21
- archivePath: filePath,
22
- content: new Uint8Array(content),
23
- });
235
+ const stat = await plugins.fsPromises.stat(absolutePath);
236
+
237
+ if (stat.isFile()) {
238
+ const content = await plugins.fsPromises.readFile(absolutePath);
239
+ entries.push({
240
+ archivePath: filePath,
241
+ content: new Uint8Array(content),
242
+ });
243
+ }
24
244
  }
25
245
 
26
246
  return this.packFiles(entries);
27
247
  }
28
248
 
29
249
  /**
30
- * Pack a directory into a TAR.GZ buffer (Node.js only)
250
+ * Pack a directory into a TAR.GZ buffer (loads all files into memory)
251
+ * For large directories, use getDirectoryPackStreamGz() instead
31
252
  */
32
253
  public async packDirectoryToTarGz(
33
254
  directoryPath: string,
34
255
  compressionLevel?: TCompressionLevel
35
256
  ): Promise<Uint8Array> {
36
257
  const tarBuffer = await this.packDirectory(directoryPath);
37
- const gzipTools = new GzipTools();
38
- return gzipTools.compress(tarBuffer, compressionLevel);
258
+ const { gzipSync } = await import('fflate');
259
+ return gzipSync(new Uint8Array(tarBuffer), { level: compressionLevel ?? 6 });
39
260
  }
40
261
 
41
262
  /**
42
- * Pack a directory into a TAR.GZ stream (Node.js only)
263
+ * Pack a directory into a TAR.GZ stream
264
+ * This is now a true streaming implementation
43
265
  */
44
266
  public async packDirectoryToTarGzStream(
45
267
  directoryPath: string,
46
268
  compressionLevel?: TCompressionLevel
47
269
  ): Promise<plugins.stream.Readable> {
48
- const buffer = await this.packDirectoryToTarGz(directoryPath, compressionLevel);
49
- return plugins.stream.Readable.from(buffer);
270
+ return this.getDirectoryPackStreamGz(directoryPath, compressionLevel);
50
271
  }
51
272
  }
package/ts/index.ts CHANGED
@@ -7,5 +7,5 @@ export * from './classes.smartarchive.js';
7
7
  // Node.js-specific: Archive analysis with SmartArchive integration
8
8
  export * from './classes.archiveanalyzer.js';
9
9
 
10
- // Node.js-specific: Extended TarTools with filesystem support (overrides shared TarTools)
11
- export { TarTools } from './classes.tartools.js';
10
+ // Node.js-specific: Extended TarTools with streaming support (overrides shared TarTools)
11
+ export { TarTools, type ITarPackFileOptions } from './classes.tartools.js';
package/ts/plugins.ts CHANGED
@@ -47,3 +47,7 @@ export {
47
47
  smartrx,
48
48
  smarturl,
49
49
  };
50
+
51
+ // Node.js-specific: tar-stream for true streaming TAR support
52
+ import * as tarStream from 'tar-stream';
53
+ export { tarStream };
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartarchive',
6
- version: '5.1.0',
6
+ version: '5.2.0',
7
7
  description: 'A library for working with archive files, providing utilities for compressing and decompressing data.'
8
8
  }