@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/ts/classes.tartools.ts
CHANGED
|
@@ -1,208 +1,51 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
|
-
import type { IArchiveEntry, TCompressionLevel } from '
|
|
3
|
-
import {
|
|
2
|
+
import type { IArchiveEntry, TCompressionLevel } from '../ts_shared/interfaces.js';
|
|
3
|
+
import { TarTools as SharedTarTools } from '../ts_shared/classes.tartools.js';
|
|
4
|
+
import { GzipTools } from '../ts_shared/classes.gziptools.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* TAR archive
|
|
7
|
+
* Extended TAR archive utilities with Node.js filesystem support
|
|
7
8
|
*/
|
|
8
|
-
export class TarTools {
|
|
9
|
+
export class TarTools extends SharedTarTools {
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* Pack a directory into a TAR buffer (Node.js only)
|
|
11
12
|
*/
|
|
12
|
-
public async
|
|
13
|
-
pack: plugins.tarStream.Pack,
|
|
14
|
-
optionsArg: {
|
|
15
|
-
fileName?: string;
|
|
16
|
-
content?:
|
|
17
|
-
| string
|
|
18
|
-
| Buffer
|
|
19
|
-
| plugins.stream.Readable
|
|
20
|
-
| plugins.smartfile.SmartFile
|
|
21
|
-
| plugins.smartfile.StreamFile;
|
|
22
|
-
byteLength?: number;
|
|
23
|
-
filePath?: string;
|
|
24
|
-
}
|
|
25
|
-
): Promise<void> {
|
|
26
|
-
return new Promise<void>(async (resolve, reject) => {
|
|
27
|
-
let fileName: string | null = null;
|
|
28
|
-
|
|
29
|
-
if (optionsArg.fileName) {
|
|
30
|
-
fileName = optionsArg.fileName;
|
|
31
|
-
} else if (optionsArg.content instanceof plugins.smartfile.SmartFile) {
|
|
32
|
-
fileName = optionsArg.content.relative;
|
|
33
|
-
} else if (optionsArg.content instanceof plugins.smartfile.StreamFile) {
|
|
34
|
-
fileName = optionsArg.content.relativeFilePath;
|
|
35
|
-
} else if (optionsArg.filePath) {
|
|
36
|
-
fileName = optionsArg.filePath;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (!fileName) {
|
|
40
|
-
reject(new Error('No filename specified for TAR entry'));
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Determine content byte length
|
|
45
|
-
let contentByteLength: number | undefined;
|
|
46
|
-
if (optionsArg.byteLength) {
|
|
47
|
-
contentByteLength = optionsArg.byteLength;
|
|
48
|
-
} else if (typeof optionsArg.content === 'string') {
|
|
49
|
-
contentByteLength = Buffer.byteLength(optionsArg.content, 'utf8');
|
|
50
|
-
} else if (Buffer.isBuffer(optionsArg.content)) {
|
|
51
|
-
contentByteLength = optionsArg.content.length;
|
|
52
|
-
} else if (optionsArg.content instanceof plugins.smartfile.SmartFile) {
|
|
53
|
-
contentByteLength = await optionsArg.content.getSize();
|
|
54
|
-
} else if (optionsArg.content instanceof plugins.smartfile.StreamFile) {
|
|
55
|
-
contentByteLength = await optionsArg.content.getSize();
|
|
56
|
-
} else if (optionsArg.filePath) {
|
|
57
|
-
const fileStat = await plugins.fsPromises.stat(optionsArg.filePath);
|
|
58
|
-
contentByteLength = fileStat.size;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Convert all content types to Readable stream
|
|
62
|
-
let content: plugins.stream.Readable;
|
|
63
|
-
if (Buffer.isBuffer(optionsArg.content)) {
|
|
64
|
-
content = plugins.stream.Readable.from(optionsArg.content);
|
|
65
|
-
} else if (typeof optionsArg.content === 'string') {
|
|
66
|
-
content = plugins.stream.Readable.from(Buffer.from(optionsArg.content));
|
|
67
|
-
} else if (optionsArg.content instanceof plugins.smartfile.SmartFile) {
|
|
68
|
-
content = plugins.stream.Readable.from(optionsArg.content.contents);
|
|
69
|
-
} else if (optionsArg.content instanceof plugins.smartfile.StreamFile) {
|
|
70
|
-
content = await optionsArg.content.createReadStream();
|
|
71
|
-
} else if (optionsArg.content instanceof plugins.stream.Readable) {
|
|
72
|
-
content = optionsArg.content;
|
|
73
|
-
} else if (optionsArg.filePath) {
|
|
74
|
-
content = plugins.fs.createReadStream(optionsArg.filePath);
|
|
75
|
-
} else {
|
|
76
|
-
reject(new Error('No content or filePath specified for TAR entry'));
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const entry = pack.entry(
|
|
81
|
-
{
|
|
82
|
-
name: fileName,
|
|
83
|
-
...(contentByteLength !== undefined ? { size: contentByteLength } : {}),
|
|
84
|
-
},
|
|
85
|
-
(err: Error | null) => {
|
|
86
|
-
if (err) {
|
|
87
|
-
reject(err);
|
|
88
|
-
} else {
|
|
89
|
-
resolve();
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
content.pipe(entry);
|
|
95
|
-
// Note: resolve() is called in the callback above when pipe completes
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Pack a directory into a TAR stream
|
|
101
|
-
*/
|
|
102
|
-
public async packDirectory(directoryPath: string): Promise<plugins.tarStream.Pack> {
|
|
13
|
+
public async packDirectory(directoryPath: string): Promise<Uint8Array> {
|
|
103
14
|
const fileTree = await plugins.listFileTree(directoryPath, '**/*');
|
|
104
|
-
const
|
|
15
|
+
const entries: IArchiveEntry[] = [];
|
|
105
16
|
|
|
106
17
|
for (const filePath of fileTree) {
|
|
107
18
|
const absolutePath = plugins.path.join(directoryPath, filePath);
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
fileName: filePath,
|
|
113
|
-
content: plugins.fs.createReadStream(absolutePath),
|
|
19
|
+
const content = await plugins.fsPromises.readFile(absolutePath);
|
|
20
|
+
entries.push({
|
|
21
|
+
archivePath: filePath,
|
|
22
|
+
content: new Uint8Array(content),
|
|
114
23
|
});
|
|
115
24
|
}
|
|
116
25
|
|
|
117
|
-
return
|
|
26
|
+
return this.packFiles(entries);
|
|
118
27
|
}
|
|
119
28
|
|
|
120
29
|
/**
|
|
121
|
-
*
|
|
122
|
-
*/
|
|
123
|
-
public async getPackStream(): Promise<plugins.tarStream.Pack> {
|
|
124
|
-
return plugins.tarStream.pack();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Get a TAR extraction stream
|
|
129
|
-
*/
|
|
130
|
-
public getDecompressionStream(): plugins.tarStream.Extract {
|
|
131
|
-
return plugins.tarStream.extract();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Pack files into a TAR buffer
|
|
136
|
-
*/
|
|
137
|
-
public async packFiles(files: IArchiveEntry[]): Promise<Buffer> {
|
|
138
|
-
const pack = await this.getPackStream();
|
|
139
|
-
|
|
140
|
-
for (const file of files) {
|
|
141
|
-
await this.addFileToPack(pack, {
|
|
142
|
-
fileName: file.archivePath,
|
|
143
|
-
content: file.content as string | Buffer | plugins.stream.Readable | plugins.smartfile.SmartFile | plugins.smartfile.StreamFile,
|
|
144
|
-
byteLength: file.size,
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
pack.finalize();
|
|
149
|
-
|
|
150
|
-
const chunks: Buffer[] = [];
|
|
151
|
-
return new Promise((resolve, reject) => {
|
|
152
|
-
pack.on('data', (chunk: Buffer) => chunks.push(chunk));
|
|
153
|
-
pack.on('end', () => resolve(Buffer.concat(chunks)));
|
|
154
|
-
pack.on('error', reject);
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Pack a directory into a TAR.GZ buffer
|
|
30
|
+
* Pack a directory into a TAR.GZ buffer (Node.js only)
|
|
160
31
|
*/
|
|
161
32
|
public async packDirectoryToTarGz(
|
|
162
33
|
directoryPath: string,
|
|
163
34
|
compressionLevel?: TCompressionLevel
|
|
164
|
-
): Promise<
|
|
165
|
-
const
|
|
166
|
-
pack.finalize();
|
|
167
|
-
|
|
35
|
+
): Promise<Uint8Array> {
|
|
36
|
+
const tarBuffer = await this.packDirectory(directoryPath);
|
|
168
37
|
const gzipTools = new GzipTools();
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const chunks: Buffer[] = [];
|
|
172
|
-
return new Promise((resolve, reject) => {
|
|
173
|
-
pack
|
|
174
|
-
.pipe(gzipStream)
|
|
175
|
-
.on('data', (chunk: Buffer) => chunks.push(chunk))
|
|
176
|
-
.on('end', () => resolve(Buffer.concat(chunks)))
|
|
177
|
-
.on('error', reject);
|
|
178
|
-
});
|
|
38
|
+
return gzipTools.compress(tarBuffer, compressionLevel);
|
|
179
39
|
}
|
|
180
40
|
|
|
181
41
|
/**
|
|
182
|
-
* Pack a directory into a TAR.GZ stream
|
|
42
|
+
* Pack a directory into a TAR.GZ stream (Node.js only)
|
|
183
43
|
*/
|
|
184
44
|
public async packDirectoryToTarGzStream(
|
|
185
45
|
directoryPath: string,
|
|
186
46
|
compressionLevel?: TCompressionLevel
|
|
187
47
|
): Promise<plugins.stream.Readable> {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const gzipTools = new GzipTools();
|
|
192
|
-
const gzipStream = gzipTools.getCompressionStream(compressionLevel);
|
|
193
|
-
|
|
194
|
-
return pack.pipe(gzipStream);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Pack files into a TAR.GZ buffer
|
|
199
|
-
*/
|
|
200
|
-
public async packFilesToTarGz(
|
|
201
|
-
files: IArchiveEntry[],
|
|
202
|
-
compressionLevel?: TCompressionLevel
|
|
203
|
-
): Promise<Buffer> {
|
|
204
|
-
const tarBuffer = await this.packFiles(files);
|
|
205
|
-
const gzipTools = new GzipTools();
|
|
206
|
-
return gzipTools.compress(tarBuffer, compressionLevel);
|
|
48
|
+
const buffer = await this.packDirectoryToTarGz(directoryPath, compressionLevel);
|
|
49
|
+
return plugins.stream.Readable.from(buffer);
|
|
207
50
|
}
|
|
208
51
|
}
|
package/ts/index.ts
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
//
|
|
2
|
-
export * from '
|
|
3
|
-
export * from './errors.js';
|
|
1
|
+
// Re-export everything from ts_shared (browser-compatible)
|
|
2
|
+
export * from '../ts_shared/index.js';
|
|
4
3
|
|
|
5
|
-
// Main archive class
|
|
4
|
+
// Node.js-specific: Main archive class with filesystem support
|
|
6
5
|
export * from './classes.smartarchive.js';
|
|
7
6
|
|
|
8
|
-
//
|
|
9
|
-
export * from './classes.tartools.js';
|
|
10
|
-
export * from './classes.ziptools.js';
|
|
11
|
-
export * from './classes.gziptools.js';
|
|
12
|
-
export * from './classes.bzip2tools.js';
|
|
13
|
-
|
|
14
|
-
// Archive analysis
|
|
7
|
+
// Node.js-specific: Archive analysis with SmartArchive integration
|
|
15
8
|
export * from './classes.archiveanalyzer.js';
|
|
9
|
+
|
|
10
|
+
// Node.js-specific: Extended TarTools with filesystem support (overrides shared TarTools)
|
|
11
|
+
export { TarTools } from './classes.tartools.js';
|
package/ts/plugins.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Node.js native scope
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import * as stream from 'node:stream';
|
|
4
4
|
import * as fs from 'node:fs';
|
|
@@ -30,32 +30,20 @@ export async function listFileTree(dirPath: string, _pattern: string = '**/*'):
|
|
|
30
30
|
return results;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
// Re-export browser-compatible plugins from ts_shared
|
|
34
|
+
export * from '../ts_shared/plugins.js';
|
|
35
|
+
|
|
36
|
+
// Additional Node.js-specific @pushrocks packages
|
|
36
37
|
import * as smartpath from '@push.rocks/smartpath';
|
|
37
|
-
import * as smartpromise from '@push.rocks/smartpromise';
|
|
38
38
|
import * as smartrequest from '@push.rocks/smartrequest';
|
|
39
39
|
import * as smartunique from '@push.rocks/smartunique';
|
|
40
|
-
import * as smartstream from '@push.rocks/smartstream';
|
|
41
40
|
import * as smartrx from '@push.rocks/smartrx';
|
|
42
41
|
import * as smarturl from '@push.rocks/smarturl';
|
|
43
42
|
|
|
44
43
|
export {
|
|
45
|
-
smartfile,
|
|
46
|
-
smartdelay,
|
|
47
44
|
smartpath,
|
|
48
|
-
smartpromise,
|
|
49
45
|
smartrequest,
|
|
50
46
|
smartunique,
|
|
51
|
-
smartstream,
|
|
52
47
|
smartrx,
|
|
53
48
|
smarturl,
|
|
54
49
|
};
|
|
55
|
-
|
|
56
|
-
// third party scope
|
|
57
|
-
import * as fileType from 'file-type';
|
|
58
|
-
import * as fflate from 'fflate';
|
|
59
|
-
import tarStream from 'tar-stream';
|
|
60
|
-
|
|
61
|
-
export { fileType, fflate, tarStream };
|
|
@@ -6,7 +6,7 @@ const BITMASK = [0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff] as const;
|
|
|
6
6
|
* Creates a bit reader function for BZIP2 decompression.
|
|
7
7
|
* Takes a buffer iterator as input and returns a function that reads bits.
|
|
8
8
|
*/
|
|
9
|
-
export function bitIterator(nextBuffer: () =>
|
|
9
|
+
export function bitIterator(nextBuffer: () => Uint8Array): IBitReader {
|
|
10
10
|
let bit = 0;
|
|
11
11
|
let byte = 0;
|
|
12
12
|
let bytes = nextBuffer();
|
|
@@ -71,7 +71,7 @@ export class Bzip2 {
|
|
|
71
71
|
/**
|
|
72
72
|
* Create a bit reader from a byte array
|
|
73
73
|
*/
|
|
74
|
-
array(bytes: Uint8Array
|
|
74
|
+
array(bytes: Uint8Array): (n: number) => number {
|
|
75
75
|
let bit = 0;
|
|
76
76
|
let byte = 0;
|
|
77
77
|
const BITMASK = [0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff];
|
|
@@ -99,7 +99,7 @@ export class Bzip2 {
|
|
|
99
99
|
/**
|
|
100
100
|
* Simple decompression from a buffer
|
|
101
101
|
*/
|
|
102
|
-
simple(srcbuffer: Uint8Array
|
|
102
|
+
simple(srcbuffer: Uint8Array, stream: (byte: number) => void): void {
|
|
103
103
|
const bits = this.array(srcbuffer);
|
|
104
104
|
const size = this.header(bits as IBitReader);
|
|
105
105
|
let ret: number | null = 0;
|
|
@@ -8,16 +8,16 @@ import { bitIterator } from './bititerator.js';
|
|
|
8
8
|
/**
|
|
9
9
|
* Creates a streaming BZIP2 decompression transform
|
|
10
10
|
*/
|
|
11
|
-
export function unbzip2Stream(): plugins.smartstream.SmartDuplex<
|
|
11
|
+
export function unbzip2Stream(): plugins.smartstream.SmartDuplex<Uint8Array, Uint8Array> {
|
|
12
12
|
const bzip2Instance = new Bzip2();
|
|
13
|
-
const bufferQueue:
|
|
13
|
+
const bufferQueue: Uint8Array[] = [];
|
|
14
14
|
let hasBytes = 0;
|
|
15
15
|
let blockSize = 0;
|
|
16
16
|
let broken = false;
|
|
17
17
|
let bitReader: IBitReader | null = null;
|
|
18
18
|
let streamCRC: number | null = null;
|
|
19
19
|
|
|
20
|
-
function decompressBlock():
|
|
20
|
+
function decompressBlock(): Uint8Array | undefined {
|
|
21
21
|
if (!blockSize) {
|
|
22
22
|
blockSize = bzip2Instance.header(bitReader!);
|
|
23
23
|
streamCRC = 0;
|
|
@@ -40,12 +40,12 @@ export function unbzip2Stream(): plugins.smartstream.SmartDuplex<Buffer, Buffer>
|
|
|
40
40
|
return undefined;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
return
|
|
43
|
+
return new Uint8Array(chunk);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
let outlength = 0;
|
|
47
47
|
|
|
48
|
-
const decompressAndPush = async (): Promise<
|
|
48
|
+
const decompressAndPush = async (): Promise<Uint8Array | undefined> => {
|
|
49
49
|
if (broken) return undefined;
|
|
50
50
|
|
|
51
51
|
try {
|
|
@@ -63,7 +63,7 @@ export function unbzip2Stream(): plugins.smartstream.SmartDuplex<Buffer, Buffer>
|
|
|
63
63
|
}
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
-
return new plugins.smartstream.SmartDuplex<
|
|
66
|
+
return new plugins.smartstream.SmartDuplex<Uint8Array, Uint8Array>({
|
|
67
67
|
objectMode: true,
|
|
68
68
|
name: 'bzip2',
|
|
69
69
|
highWaterMark: 1,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { unbzip2Stream } from './bzip2/index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* BZIP2 decompression utilities (browser-compatible)
|
|
6
|
+
*/
|
|
7
|
+
export class Bzip2Tools {
|
|
8
|
+
/**
|
|
9
|
+
* Get a streaming decompression transform
|
|
10
|
+
*/
|
|
11
|
+
getDecompressionStream(): plugins.smartstream.SmartDuplex<Uint8Array, Uint8Array> {
|
|
12
|
+
return unbzip2Stream();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { TCompressionLevel } from './interfaces.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* GZIP compression and decompression utilities (browser-compatible)
|
|
6
|
+
*/
|
|
7
|
+
export class GzipTools {
|
|
8
|
+
/**
|
|
9
|
+
* Compress data synchronously
|
|
10
|
+
*/
|
|
11
|
+
public compressSync(data: Uint8Array, level?: TCompressionLevel): Uint8Array {
|
|
12
|
+
const options = level !== undefined ? { level } : undefined;
|
|
13
|
+
return plugins.fflate.gzipSync(data, options);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Decompress data synchronously
|
|
18
|
+
*/
|
|
19
|
+
public decompressSync(data: Uint8Array): Uint8Array {
|
|
20
|
+
return plugins.fflate.gunzipSync(data);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Compress data asynchronously
|
|
25
|
+
* Note: Uses sync version for Deno compatibility (fflate async uses Web Workers
|
|
26
|
+
* which have issues in Deno)
|
|
27
|
+
*/
|
|
28
|
+
public async compress(data: Uint8Array, level?: TCompressionLevel): Promise<Uint8Array> {
|
|
29
|
+
// Use sync version wrapped in Promise for cross-runtime compatibility
|
|
30
|
+
return this.compressSync(data, level);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Decompress data asynchronously
|
|
35
|
+
* Note: Uses sync version for Deno compatibility (fflate async uses Web Workers
|
|
36
|
+
* which have issues in Deno)
|
|
37
|
+
*/
|
|
38
|
+
public async decompress(data: Uint8Array): Promise<Uint8Array> {
|
|
39
|
+
// Use sync version wrapped in Promise for cross-runtime compatibility
|
|
40
|
+
return this.decompressSync(data);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { IArchiveEntry, ITarEntry, TCompressionLevel } from './interfaces.js';
|
|
3
|
+
import { GzipTools } from './classes.gziptools.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* TAR archive creation and extraction utilities using modern-tar (browser-compatible)
|
|
7
|
+
*/
|
|
8
|
+
export class TarTools {
|
|
9
|
+
/**
|
|
10
|
+
* Pack files into a TAR buffer
|
|
11
|
+
*/
|
|
12
|
+
public async packFiles(files: IArchiveEntry[]): Promise<Uint8Array> {
|
|
13
|
+
const entries: ITarEntry[] = [];
|
|
14
|
+
|
|
15
|
+
for (const file of files) {
|
|
16
|
+
let data: Uint8Array;
|
|
17
|
+
|
|
18
|
+
if (typeof file.content === 'string') {
|
|
19
|
+
data = new TextEncoder().encode(file.content);
|
|
20
|
+
} else if (file.content instanceof Uint8Array) {
|
|
21
|
+
data = file.content;
|
|
22
|
+
} else if (file.content instanceof plugins.smartfile.SmartFile) {
|
|
23
|
+
data = new Uint8Array(file.content.contents);
|
|
24
|
+
} else if (file.content instanceof plugins.smartfile.StreamFile) {
|
|
25
|
+
const buffer = await file.content.getContentAsBuffer();
|
|
26
|
+
data = new Uint8Array(buffer);
|
|
27
|
+
} else {
|
|
28
|
+
throw new Error('Unsupported content type for TAR entry');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
entries.push({
|
|
32
|
+
header: {
|
|
33
|
+
name: file.archivePath,
|
|
34
|
+
size: data.length,
|
|
35
|
+
type: 'file',
|
|
36
|
+
mode: file.mode,
|
|
37
|
+
mtime: file.mtime,
|
|
38
|
+
},
|
|
39
|
+
body: data,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return plugins.modernTar.packTar(entries);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Extract a TAR buffer to an array of entries
|
|
48
|
+
*/
|
|
49
|
+
public async extractTar(data: Uint8Array): Promise<Array<{ path: string; content: Uint8Array; isDirectory: boolean }>> {
|
|
50
|
+
const entries = await plugins.modernTar.unpackTar(data);
|
|
51
|
+
const result: Array<{ path: string; content: Uint8Array; isDirectory: boolean }> = [];
|
|
52
|
+
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
const isDirectory = entry.header.type === 'directory' || entry.header.name.endsWith('/');
|
|
55
|
+
|
|
56
|
+
// modern-tar uses 'data' property, not 'body'
|
|
57
|
+
const content = entry.data ?? new Uint8Array(0);
|
|
58
|
+
|
|
59
|
+
result.push({
|
|
60
|
+
path: entry.header.name,
|
|
61
|
+
content,
|
|
62
|
+
isDirectory,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Pack files into a TAR.GZ buffer
|
|
71
|
+
*/
|
|
72
|
+
public async packFilesToTarGz(
|
|
73
|
+
files: IArchiveEntry[],
|
|
74
|
+
compressionLevel?: TCompressionLevel
|
|
75
|
+
): Promise<Uint8Array> {
|
|
76
|
+
const tarBuffer = await this.packFiles(files);
|
|
77
|
+
const gzipTools = new GzipTools();
|
|
78
|
+
return gzipTools.compress(tarBuffer, compressionLevel);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Extract a TAR.GZ buffer to an array of entries
|
|
83
|
+
*/
|
|
84
|
+
public async extractTarGz(data: Uint8Array): Promise<Array<{ path: string; content: Uint8Array; isDirectory: boolean }>> {
|
|
85
|
+
const gzipTools = new GzipTools();
|
|
86
|
+
const tarBuffer = await gzipTools.decompress(data);
|
|
87
|
+
return this.extractTar(tarBuffer);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { IArchiveEntry, TCompressionLevel } from './interfaces.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Transform stream for ZIP decompression using fflate
|
|
6
|
+
* Emits StreamFile objects for each file in the archive
|
|
7
|
+
*/
|
|
8
|
+
export class ZipDecompressionTransform extends plugins.smartstream.SmartDuplex<Uint8Array, plugins.smartfile.StreamFile> {
|
|
9
|
+
private streamtools!: plugins.smartstream.IStreamTools;
|
|
10
|
+
private unzipper = new plugins.fflate.Unzip(async (fileArg) => {
|
|
11
|
+
let resultBuffer: Uint8Array;
|
|
12
|
+
fileArg.ondata = async (_flateError, dat, final) => {
|
|
13
|
+
if (resultBuffer) {
|
|
14
|
+
const combined = new Uint8Array(resultBuffer.length + dat.length);
|
|
15
|
+
combined.set(resultBuffer);
|
|
16
|
+
combined.set(dat, resultBuffer.length);
|
|
17
|
+
resultBuffer = combined;
|
|
18
|
+
} else {
|
|
19
|
+
resultBuffer = new Uint8Array(dat);
|
|
20
|
+
}
|
|
21
|
+
if (final) {
|
|
22
|
+
const streamFile = plugins.smartfile.StreamFile.fromBuffer(Buffer.from(resultBuffer));
|
|
23
|
+
streamFile.relativeFilePath = fileArg.name;
|
|
24
|
+
this.streamtools.push(streamFile);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
fileArg.start();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
constructor() {
|
|
31
|
+
super({
|
|
32
|
+
objectMode: true,
|
|
33
|
+
writeFunction: async (chunkArg, streamtoolsArg) => {
|
|
34
|
+
this.streamtools ? null : (this.streamtools = streamtoolsArg);
|
|
35
|
+
const chunk = chunkArg instanceof Uint8Array ? chunkArg : new Uint8Array(chunkArg);
|
|
36
|
+
this.unzipper.push(chunk, false);
|
|
37
|
+
return null;
|
|
38
|
+
},
|
|
39
|
+
finalFunction: async () => {
|
|
40
|
+
this.unzipper.push(new Uint8Array(0), true);
|
|
41
|
+
await plugins.smartdelay.delayFor(0);
|
|
42
|
+
await this.streamtools.push(null);
|
|
43
|
+
return null;
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
this.unzipper.register(plugins.fflate.UnzipInflate);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* ZIP compression and decompression utilities
|
|
52
|
+
*/
|
|
53
|
+
export class ZipTools {
|
|
54
|
+
/**
|
|
55
|
+
* Get a streaming decompression transform for extracting ZIP archives
|
|
56
|
+
*/
|
|
57
|
+
public getDecompressionStream(): ZipDecompressionTransform {
|
|
58
|
+
return new ZipDecompressionTransform();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Create a ZIP archive from an array of entries
|
|
63
|
+
*/
|
|
64
|
+
public async createZip(entries: IArchiveEntry[], compressionLevel?: TCompressionLevel): Promise<Uint8Array> {
|
|
65
|
+
const filesObj: plugins.fflate.Zippable = {};
|
|
66
|
+
|
|
67
|
+
for (const entry of entries) {
|
|
68
|
+
let data: Uint8Array;
|
|
69
|
+
|
|
70
|
+
if (typeof entry.content === 'string') {
|
|
71
|
+
data = new TextEncoder().encode(entry.content);
|
|
72
|
+
} else if (entry.content instanceof Uint8Array) {
|
|
73
|
+
data = entry.content;
|
|
74
|
+
} else if (entry.content instanceof plugins.smartfile.SmartFile) {
|
|
75
|
+
data = new Uint8Array(entry.content.contents);
|
|
76
|
+
} else if (entry.content instanceof plugins.smartfile.StreamFile) {
|
|
77
|
+
const buffer = await entry.content.getContentAsBuffer();
|
|
78
|
+
data = new Uint8Array(buffer);
|
|
79
|
+
} else {
|
|
80
|
+
throw new Error('Unsupported content type for ZIP entry');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (compressionLevel !== undefined) {
|
|
84
|
+
filesObj[entry.archivePath] = [data, { level: compressionLevel }];
|
|
85
|
+
} else {
|
|
86
|
+
filesObj[entry.archivePath] = data;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Use sync version for Deno compatibility (fflate async uses Web Workers)
|
|
91
|
+
const result = plugins.fflate.zipSync(filesObj);
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Extract a ZIP buffer to an array of entries
|
|
97
|
+
*/
|
|
98
|
+
public async extractZip(data: Uint8Array): Promise<Array<{ path: string; content: Uint8Array }>> {
|
|
99
|
+
// Use sync version for Deno compatibility (fflate async uses Web Workers)
|
|
100
|
+
const result = plugins.fflate.unzipSync(data);
|
|
101
|
+
const entries: Array<{ path: string; content: Uint8Array }> = [];
|
|
102
|
+
for (const [path, content] of Object.entries(result)) {
|
|
103
|
+
entries.push({ path, content });
|
|
104
|
+
}
|
|
105
|
+
return entries;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// ts_shared - Browser-compatible shared code
|
|
2
|
+
|
|
3
|
+
// Interfaces and types
|
|
4
|
+
export * from './interfaces.js';
|
|
5
|
+
|
|
6
|
+
// Error classes
|
|
7
|
+
export * from './errors.js';
|
|
8
|
+
|
|
9
|
+
// Tool classes
|
|
10
|
+
export { ZipTools, ZipDecompressionTransform } from './classes.ziptools.js';
|
|
11
|
+
export { GzipTools } from './classes.gziptools.js';
|
|
12
|
+
export { TarTools } from './classes.tartools.js';
|
|
13
|
+
export { Bzip2Tools } from './classes.bzip2tools.js';
|
|
14
|
+
|
|
15
|
+
// BZIP2 internals (for advanced usage)
|
|
16
|
+
export { unbzip2Stream } from './bzip2/index.js';
|
|
17
|
+
export { Bzip2 } from './bzip2/bzip2.js';
|