@push.rocks/smartarchive 5.1.0 → 5.2.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 +1 -1
- package/dist_ts/classes.smartarchive.js +26 -21
- package/dist_ts/classes.tartools.d.ts +79 -4
- package/dist_ts/classes.tartools.js +201 -15
- package/dist_ts/index.d.ts +1 -1
- package/dist_ts/index.js +2 -2
- package/dist_ts/plugins.d.ts +2 -0
- package/dist_ts/plugins.js +4 -1
- package/dist_ts_shared/bzip2/bititerator.d.ts +6 -0
- package/dist_ts_shared/bzip2/bititerator.js +50 -0
- package/dist_ts_shared/bzip2/bzip2.d.ts +29 -0
- package/dist_ts_shared/bzip2/bzip2.js +398 -0
- package/dist_ts_shared/bzip2/index.d.ts +5 -0
- package/dist_ts_shared/bzip2/index.js +92 -0
- package/dist_ts_shared/classes.bzip2tools.d.ts +10 -0
- package/dist_ts_shared/classes.bzip2tools.js +14 -0
- package/dist_ts_shared/classes.gziptools.d.ts +26 -0
- package/dist_ts_shared/classes.gziptools.js +38 -0
- package/dist_ts_shared/classes.tartools.d.ts +30 -0
- package/dist_ts_shared/classes.tartools.js +78 -0
- package/dist_ts_shared/classes.ziptools.d.ts +31 -0
- package/dist_ts_shared/classes.ziptools.js +103 -0
- package/dist_ts_shared/errors.d.ts +44 -0
- package/dist_ts_shared/errors.js +62 -0
- package/dist_ts_shared/index.d.ts +8 -0
- package/dist_ts_shared/index.js +14 -0
- package/dist_ts_shared/interfaces.d.ts +129 -0
- package/dist_ts_shared/interfaces.js +2 -0
- package/dist_ts_shared/plugins.d.ts +9 -0
- package/dist_ts_shared/plugins.js +14 -0
- package/dist_ts_web/00_commitinfo_data.d.ts +8 -0
- package/dist_ts_web/00_commitinfo_data.js +9 -0
- package/dist_ts_web/index.d.ts +1 -0
- package/dist_ts_web/index.js +4 -0
- package/dist_ts_web/plugins.d.ts +1 -0
- package/dist_ts_web/plugins.js +4 -0
- package/package.json +5 -3
- package/readme.md +154 -46
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.smartarchive.ts +28 -23
- package/ts/classes.tartools.ts +235 -14
- package/ts/index.ts +2 -2
- package/ts/plugins.ts +4 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
package/ts/classes.tartools.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
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
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
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 (
|
|
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
|
|
38
|
-
return
|
|
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
|
|
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
|
-
|
|
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
|
|
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