@push.rocks/smartarchive 5.2.3 → 5.3.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.d.ts +1 -1
- package/dist_ts/classes.smartarchive.js +2 -2
- package/dist_ts/classes.ziptools.d.ts +18 -0
- package/dist_ts/classes.ziptools.js +70 -0
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +3 -1
- package/dist_ts_shared/bzip2/index.js +2 -2
- package/dist_ts_shared/classes.ziptools.d.ts +6 -13
- package/dist_ts_shared/classes.ziptools.js +511 -54
- package/dist_ts_shared/errors.d.ts +25 -0
- package/dist_ts_shared/errors.js +28 -1
- package/dist_ts_shared/index.d.ts +1 -1
- package/dist_ts_shared/index.js +2 -2
- package/dist_ts_shared/interfaces.d.ts +35 -0
- package/dist_ts_shared/plugins.ziptools.d.ts +3 -0
- package/dist_ts_shared/plugins.ziptools.js +4 -0
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/index.d.ts +1 -1
- package/dist_ts_web/index.js +1 -1
- package/dist_ts_web/plugins.d.ts +1 -1
- package/dist_ts_web/plugins.js +1 -1
- package/dist_ts_web/zip.d.ts +3 -0
- package/dist_ts_web/zip.js +3 -0
- package/package.json +5 -3
- package/readme.hints.md +15 -1
- package/readme.md +43 -54
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.smartarchive.ts +1 -1
- package/ts/classes.ziptools.ts +75 -0
- package/ts/index.ts +3 -0
- package/ts_shared/classes.ziptools.ts +690 -51
- package/ts_shared/errors.ts +31 -0
- package/ts_shared/index.ts +1 -1
- package/ts_shared/interfaces.ts +38 -0
- package/ts_shared/plugins.ziptools.ts +4 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/zip.ts +7 -0
|
@@ -23,6 +23,31 @@ export declare class ArchiveFormatError extends SmartArchiveError {
|
|
|
23
23
|
export declare class StreamError extends SmartArchiveError {
|
|
24
24
|
constructor(message: string);
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Stable ZIP error codes for bounded selective extraction
|
|
28
|
+
*/
|
|
29
|
+
export declare const ZIP_ERROR_CODES: {
|
|
30
|
+
readonly INVALID_LIMIT: "ZIP_INVALID_LIMIT";
|
|
31
|
+
readonly INVALID_ARCHIVE: "ZIP_INVALID_ARCHIVE";
|
|
32
|
+
readonly UNSUPPORTED_FEATURE: "ZIP_UNSUPPORTED_FEATURE";
|
|
33
|
+
readonly ARCHIVE_SIZE_LIMIT: "ZIP_ARCHIVE_SIZE_LIMIT";
|
|
34
|
+
readonly ENTRY_COUNT_LIMIT: "ZIP_ENTRY_COUNT_LIMIT";
|
|
35
|
+
readonly SELECTED_ENTRY_COUNT_LIMIT: "ZIP_SELECTED_ENTRY_COUNT_LIMIT";
|
|
36
|
+
readonly COMPRESSED_SIZE_LIMIT: "ZIP_COMPRESSED_SIZE_LIMIT";
|
|
37
|
+
readonly UNCOMPRESSED_SIZE_LIMIT: "ZIP_UNCOMPRESSED_SIZE_LIMIT";
|
|
38
|
+
readonly TOTAL_UNCOMPRESSED_SIZE_LIMIT: "ZIP_TOTAL_UNCOMPRESSED_SIZE_LIMIT";
|
|
39
|
+
readonly DUPLICATE_SELECTED_PATH: "ZIP_DUPLICATE_SELECTED_PATH";
|
|
40
|
+
readonly COMPRESSED_SIZE_MISMATCH: "ZIP_COMPRESSED_SIZE_MISMATCH";
|
|
41
|
+
readonly UNCOMPRESSED_SIZE_MISMATCH: "ZIP_UNCOMPRESSED_SIZE_MISMATCH";
|
|
42
|
+
readonly CRC_MISMATCH: "ZIP_CRC_MISMATCH";
|
|
43
|
+
};
|
|
44
|
+
export type TZipErrorCode = typeof ZIP_ERROR_CODES[keyof typeof ZIP_ERROR_CODES];
|
|
45
|
+
/**
|
|
46
|
+
* ZIP-specific extraction error
|
|
47
|
+
*/
|
|
48
|
+
export declare class ZipError extends SmartArchiveError {
|
|
49
|
+
constructor(message: string, code: TZipErrorCode);
|
|
50
|
+
}
|
|
26
51
|
/**
|
|
27
52
|
* BZIP2 error codes for programmatic error handling
|
|
28
53
|
*/
|
package/dist_ts_shared/errors.js
CHANGED
|
@@ -39,6 +39,33 @@ export class StreamError extends SmartArchiveError {
|
|
|
39
39
|
this.name = 'StreamError';
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Stable ZIP error codes for bounded selective extraction
|
|
44
|
+
*/
|
|
45
|
+
export const ZIP_ERROR_CODES = {
|
|
46
|
+
INVALID_LIMIT: 'ZIP_INVALID_LIMIT',
|
|
47
|
+
INVALID_ARCHIVE: 'ZIP_INVALID_ARCHIVE',
|
|
48
|
+
UNSUPPORTED_FEATURE: 'ZIP_UNSUPPORTED_FEATURE',
|
|
49
|
+
ARCHIVE_SIZE_LIMIT: 'ZIP_ARCHIVE_SIZE_LIMIT',
|
|
50
|
+
ENTRY_COUNT_LIMIT: 'ZIP_ENTRY_COUNT_LIMIT',
|
|
51
|
+
SELECTED_ENTRY_COUNT_LIMIT: 'ZIP_SELECTED_ENTRY_COUNT_LIMIT',
|
|
52
|
+
COMPRESSED_SIZE_LIMIT: 'ZIP_COMPRESSED_SIZE_LIMIT',
|
|
53
|
+
UNCOMPRESSED_SIZE_LIMIT: 'ZIP_UNCOMPRESSED_SIZE_LIMIT',
|
|
54
|
+
TOTAL_UNCOMPRESSED_SIZE_LIMIT: 'ZIP_TOTAL_UNCOMPRESSED_SIZE_LIMIT',
|
|
55
|
+
DUPLICATE_SELECTED_PATH: 'ZIP_DUPLICATE_SELECTED_PATH',
|
|
56
|
+
COMPRESSED_SIZE_MISMATCH: 'ZIP_COMPRESSED_SIZE_MISMATCH',
|
|
57
|
+
UNCOMPRESSED_SIZE_MISMATCH: 'ZIP_UNCOMPRESSED_SIZE_MISMATCH',
|
|
58
|
+
CRC_MISMATCH: 'ZIP_CRC_MISMATCH',
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* ZIP-specific extraction error
|
|
62
|
+
*/
|
|
63
|
+
export class ZipError extends SmartArchiveError {
|
|
64
|
+
constructor(message, code) {
|
|
65
|
+
super(message, code);
|
|
66
|
+
this.name = 'ZipError';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
42
69
|
/**
|
|
43
70
|
* BZIP2 error codes for programmatic error handling
|
|
44
71
|
*/
|
|
@@ -59,4 +86,4 @@ export const BZIP2_ERROR_CODES = {
|
|
|
59
86
|
export function throwBzip2Error(message, code) {
|
|
60
87
|
throw new Bzip2Error(message, code);
|
|
61
88
|
}
|
|
62
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
89
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfc2hhcmVkL2Vycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUNILE1BQU0sT0FBTyxpQkFBa0IsU0FBUSxLQUFLO0lBRzFDLFlBQVksT0FBZSxFQUFFLElBQVk7UUFDdkMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2YsSUFBSSxDQUFDLElBQUksR0FBRyxtQkFBbUIsQ0FBQztRQUNoQyxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNqQiwrREFBK0Q7UUFDL0QsSUFBSSxLQUFLLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZQUM1QixLQUFLLENBQUMsaUJBQWlCLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNsRCxDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sVUFBVyxTQUFRLGlCQUFpQjtJQUMvQyxZQUFZLE9BQWUsRUFBRSxPQUFlLGFBQWE7UUFDdkQsS0FBSyxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztRQUNyQixJQUFJLENBQUMsSUFBSSxHQUFHLFlBQVksQ0FBQztJQUMzQixDQUFDO0NBQ0Y7QUFFRDs7R0FFRztBQUNILE1BQU0sT0FBTyxrQkFBbUIsU0FBUSxpQkFBaUI7SUFDdkQsWUFBWSxPQUFlO1FBQ3pCLEtBQUssQ0FBQyxPQUFPLEVBQUUsc0JBQXNCLENBQUMsQ0FBQztRQUN2QyxJQUFJLENBQUMsSUFBSSxHQUFHLG9CQUFvQixDQUFDO0lBQ25DLENBQUM7Q0FDRjtBQUVEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLFdBQVksU0FBUSxpQkFBaUI7SUFDaEQsWUFBWSxPQUFlO1FBQ3pCLEtBQUssQ0FBQyxPQUFPLEVBQUUsY0FBYyxDQUFDLENBQUM7UUFDL0IsSUFBSSxDQUFDLElBQUksR0FBRyxhQUFhLENBQUM7SUFDNUIsQ0FBQztDQUNGO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUc7SUFDN0IsYUFBYSxFQUFFLG1CQUFtQjtJQUNsQyxlQUFlLEVBQUUscUJBQXFCO0lBQ3RDLG1CQUFtQixFQUFFLHlCQUF5QjtJQUM5QyxrQkFBa0IsRUFBRSx3QkFBd0I7SUFDNUMsaUJBQWlCLEVBQUUsdUJBQXVCO0lBQzFDLDBCQUEwQixFQUFFLGdDQUFnQztJQUM1RCxxQkFBcUIsRUFBRSwyQkFBMkI7SUFDbEQsdUJBQXVCLEVBQUUsNkJBQTZCO0lBQ3RELDZCQUE2QixFQUFFLG1DQUFtQztJQUNsRSx1QkFBdUIsRUFBRSw2QkFBNkI7SUFDdEQsd0JBQXdCLEVBQUUsOEJBQThCO0lBQ3hELDBCQUEwQixFQUFFLGdDQUFnQztJQUM1RCxZQUFZLEVBQUUsa0JBQWtCO0NBQ3hCLENBQUM7QUFJWDs7R0FFRztBQUNILE1BQU0sT0FBTyxRQUFTLFNBQVEsaUJBQWlCO0lBQzdDLFlBQVksT0FBZSxFQUFFLElBQW1CO1FBQzlDLEtBQUssQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDckIsSUFBSSxDQUFDLElBQUksR0FBRyxVQUFVLENBQUM7SUFDekIsQ0FBQztDQUNGO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRztJQUMvQixlQUFlLEVBQUUsZ0JBQWdCO0lBQ2pDLGVBQWUsRUFBRSx1QkFBdUI7SUFDeEMsWUFBWSxFQUFFLG9CQUFvQjtJQUNsQyxrQkFBa0IsRUFBRSxxQkFBcUI7SUFDekMsZUFBZSxFQUFFLHVCQUF1QjtJQUN4QyxlQUFlLEVBQUUsdUJBQXVCO0lBQ3hDLGdCQUFnQixFQUFFLHdCQUF3QjtJQUMxQyxnQkFBZ0IsRUFBRSx3QkFBd0I7SUFDMUMsYUFBYSxFQUFFLHFCQUFxQjtDQUM1QixDQUFDO0FBSVg7O0dBRUc7QUFDSCxNQUFNLFVBQVUsZUFBZSxDQUFDLE9BQWUsRUFBRSxJQUFxQjtJQUNwRSxNQUFNLElBQUksVUFBVSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztBQUN0QyxDQUFDIn0=
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from './interfaces.js';
|
|
2
2
|
export * from './errors.js';
|
|
3
|
-
export { ZipTools
|
|
3
|
+
export { ZipTools } from './classes.ziptools.js';
|
|
4
4
|
export { GzipTools } from './classes.gziptools.js';
|
|
5
5
|
export { TarTools } from './classes.tartools.js';
|
|
6
6
|
export { Bzip2Tools } from './classes.bzip2tools.js';
|
package/dist_ts_shared/index.js
CHANGED
|
@@ -4,11 +4,11 @@ export * from './interfaces.js';
|
|
|
4
4
|
// Error classes
|
|
5
5
|
export * from './errors.js';
|
|
6
6
|
// Tool classes
|
|
7
|
-
export { ZipTools
|
|
7
|
+
export { ZipTools } from './classes.ziptools.js';
|
|
8
8
|
export { GzipTools } from './classes.gziptools.js';
|
|
9
9
|
export { TarTools } from './classes.tartools.js';
|
|
10
10
|
export { Bzip2Tools } from './classes.bzip2tools.js';
|
|
11
11
|
// BZIP2 internals (for advanced usage)
|
|
12
12
|
export { unbzip2Stream } from './bzip2/index.js';
|
|
13
13
|
export { Bzip2 } from './bzip2/bzip2.js';
|
|
14
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
14
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19zaGFyZWQvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsNkNBQTZDO0FBRTdDLHVCQUF1QjtBQUN2QixjQUFjLGlCQUFpQixDQUFDO0FBRWhDLGdCQUFnQjtBQUNoQixjQUFjLGFBQWEsQ0FBQztBQUU1QixlQUFlO0FBQ2YsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ2pELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNuRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDakQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRXJELHVDQUF1QztBQUN2QyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDakQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGtCQUFrQixDQUFDIn0=
|
|
@@ -69,6 +69,41 @@ export interface IArchiveEntryInfo {
|
|
|
69
69
|
/** File mode/permissions */
|
|
70
70
|
mode?: number;
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* ZIP entry metadata available before selective decompression
|
|
74
|
+
*/
|
|
75
|
+
export interface IZipEntryInfo extends IArchiveEntryInfo {
|
|
76
|
+
/** Compressed size in bytes */
|
|
77
|
+
compressedSize: number;
|
|
78
|
+
/** ZIP compression method from the central directory */
|
|
79
|
+
compressionMethod: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Required limits for security-sensitive selective ZIP extraction
|
|
83
|
+
*/
|
|
84
|
+
export interface ISelectiveZipExtractionOptions {
|
|
85
|
+
/** Select entries using metadata parsed before decompression */
|
|
86
|
+
filter: (entry: IZipEntryInfo) => boolean;
|
|
87
|
+
/** Maximum bytes accepted for the complete ZIP archive */
|
|
88
|
+
maxArchiveBytes: number;
|
|
89
|
+
/** Maximum number of entries in the complete ZIP archive */
|
|
90
|
+
maxEntries: number;
|
|
91
|
+
/** Maximum number of selected entries */
|
|
92
|
+
maxSelectedEntries: number;
|
|
93
|
+
/** Maximum compressed bytes for each selected entry */
|
|
94
|
+
maxCompressedEntryBytes: number;
|
|
95
|
+
/** Maximum uncompressed bytes for each selected entry */
|
|
96
|
+
maxUncompressedEntryBytes: number;
|
|
97
|
+
/** Maximum total uncompressed bytes across selected entries */
|
|
98
|
+
maxTotalUncompressedBytes: number;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* One entry returned by selective ZIP extraction
|
|
102
|
+
*/
|
|
103
|
+
export interface IExtractedZipEntry {
|
|
104
|
+
path: string;
|
|
105
|
+
content: Uint8Array;
|
|
106
|
+
}
|
|
72
107
|
/**
|
|
73
108
|
* Result of archive analysis
|
|
74
109
|
*/
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import * as fflate from 'fflate';
|
|
2
|
+
import * as pako from 'pako';
|
|
3
|
+
export { fflate, pako };
|
|
4
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy56aXB0b29scy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX3NoYXJlZC9wbHVnaW5zLnppcHRvb2xzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxNQUFNLE1BQU0sUUFBUSxDQUFDO0FBQ2pDLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBRTdCLE9BQU8sRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLENBQUMifQ==
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartarchive',
|
|
6
|
-
version: '5.
|
|
6
|
+
version: '5.3.0',
|
|
7
7
|
description: 'A library for working with archive files, providing utilities for compressing and decompressing data.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHNfd2ViLzAwX2NvbW1pdGluZm9fZGF0YS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRztJQUN4QixJQUFJLEVBQUUsMEJBQTBCO0lBQ2hDLE9BQU8sRUFBRSxPQUFPO0lBQ2hCLFdBQVcsRUFBRSx1R0FBdUc7Q0FDckgsQ0FBQSJ9
|
package/dist_ts_web/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from '../
|
|
1
|
+
export * from '../dist_ts_shared/index.js';
|
package/dist_ts_web/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// ts_web - Browser-compatible entry point
|
|
2
2
|
// Re-exports everything from ts_shared
|
|
3
|
-
export * from '../
|
|
3
|
+
export * from '../dist_ts_shared/index.js';
|
|
4
4
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90c193ZWIvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsMENBQTBDO0FBQzFDLHVDQUF1QztBQUV2QyxjQUFjLHVCQUF1QixDQUFDIn0=
|
package/dist_ts_web/plugins.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from '../
|
|
1
|
+
export * from '../dist_ts_shared/plugins.js';
|
package/dist_ts_web/plugins.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// Browser-compatible plugins for ts_web
|
|
2
2
|
// Re-export from ts_shared
|
|
3
|
-
export * from '../
|
|
3
|
+
export * from '../dist_ts_shared/plugins.js';
|
|
4
4
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX3dlYi9wbHVnaW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHdDQUF3QztBQUN4QywyQkFBMkI7QUFDM0IsY0FBYyx5QkFBeUIsQ0FBQyJ9
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { ZipTools } from '../dist_ts_shared/classes.ziptools.js';
|
|
2
|
+
export { ZIP_ERROR_CODES, ZipError, type TZipErrorCode } from '../dist_ts_shared/errors.js';
|
|
3
|
+
export type { IExtractedZipEntry, ISelectiveZipExtractionOptions, IZipEntryInfo, } from '../dist_ts_shared/interfaces.js';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { ZipTools } from '../dist_ts_shared/classes.ziptools.js';
|
|
2
|
+
export { ZIP_ERROR_CODES, ZipError } from '../dist_ts_shared/errors.js';
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiemlwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHNfd2ViL3ppcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFDNUQsT0FBTyxFQUFFLGVBQWUsRUFBRSxRQUFRLEVBQXNCLE1BQU0sd0JBQXdCLENBQUMifQ==
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartarchive",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"description": "A library for working with archive files, providing utilities for compressing and decompressing data.",
|
|
5
5
|
"main": "dist_ts/index.js",
|
|
6
6
|
"typings": "dist_ts/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./dist_ts/index.js",
|
|
10
|
-
"./web": "./dist_ts_web/index.js"
|
|
10
|
+
"./web": "./dist_ts_web/index.js",
|
|
11
|
+
"./web/zip": "./dist_ts_web/zip.js"
|
|
11
12
|
},
|
|
12
13
|
"repository": {
|
|
13
14
|
"type": "git",
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
"fflate": "^0.8.2",
|
|
34
35
|
"file-type": "^22.0.1",
|
|
35
36
|
"modern-tar": "^0.7.6",
|
|
37
|
+
"pako": "^3.0.1",
|
|
36
38
|
"tar-stream": "^3.2.0"
|
|
37
39
|
},
|
|
38
40
|
"devDependencies": {
|
|
@@ -76,7 +78,7 @@
|
|
|
76
78
|
],
|
|
77
79
|
"scripts": {
|
|
78
80
|
"test": "(tstest test/ --verbose)",
|
|
79
|
-
"build": "tsbuild
|
|
81
|
+
"build": "tsbuild tsfolders",
|
|
80
82
|
"buildDocs": "tsdoc"
|
|
81
83
|
}
|
|
82
84
|
}
|
package/readme.hints.md
CHANGED
|
@@ -15,7 +15,7 @@ Modes are mutually exclusive - you cannot mix extraction and creation methods in
|
|
|
15
15
|
|
|
16
16
|
- **SmartArchive** - Main class with fluent API for all operations
|
|
17
17
|
- **TarTools** - TAR-specific operations (pack/extract)
|
|
18
|
-
- **ZipTools** - ZIP
|
|
18
|
+
- **ZipTools** - ZIP creation/general extraction using fflate plus bounded selective extraction using strict metadata validation and pako
|
|
19
19
|
- **GzipTools** - GZIP compression/decompression using fflate
|
|
20
20
|
- **Bzip2Tools** - BZIP2 decompression (extract only, no creation)
|
|
21
21
|
- **ArchiveAnalyzer** - Format detection via magic bytes
|
|
@@ -23,6 +23,7 @@ Modes are mutually exclusive - you cannot mix extraction and creation methods in
|
|
|
23
23
|
## Dependencies
|
|
24
24
|
|
|
25
25
|
- **fflate** - Pure JS compression for ZIP/GZIP (works in browser)
|
|
26
|
+
- **pako** - Browser-compatible raw DEFLATE stream used where exact compressed-input consumption must be verified
|
|
26
27
|
- **tar-stream** - TAR archive handling
|
|
27
28
|
- **file-type** - MIME type detection via magic bytes
|
|
28
29
|
- **@push.rocks/smartfile** - SmartFile and StreamFile classes
|
|
@@ -73,6 +74,19 @@ tstest test/test.node+deno.ts --verbose # Run specific test
|
|
|
73
74
|
|
|
74
75
|
Tests use a Verdaccio registry URL (`verdaccio.lossless.digital`) for test archives.
|
|
75
76
|
|
|
77
|
+
## Bounded ZIP Extraction
|
|
78
|
+
|
|
79
|
+
`ZipTools.extractSelectedZipEntries()` is separate from the compatibility-oriented `extractZip()` and streaming transform. It is intended for untrusted archives where selection must happen before decompression.
|
|
80
|
+
|
|
81
|
+
Browser consumers that need an isolated dependency graph should import it from `@push.rocks/smartarchive/web/zip`. Node consumers use the normal package root. The Node `ZipTools` subclass preserves streaming, `SmartFile`, and `StreamFile` support while delegating bounded extraction to the shared implementation.
|
|
82
|
+
|
|
83
|
+
- All limits are required and must be positive safe integers.
|
|
84
|
+
- `maxArchiveBytes` and `maxEntries` cover the whole archive; the remaining limits cover selected entries only.
|
|
85
|
+
- The parser supports single-disk, non-ZIP64 archives and validates exact central/local record relationships without assuming matching record order.
|
|
86
|
+
- Paths are canonical ASCII or UTF-8. Unicode path extras, absolute paths, backslashes, dot segments, encryption, and ambiguous descriptors fail closed.
|
|
87
|
+
- Only selected stored/DEFLATE entries are decoded. Compressed input is fed in fixed 16 KiB slices, and pako exposes exact consumed-input state so trailing compressed bytes are rejected.
|
|
88
|
+
- Peak selected-output memory is bounded by retained output, one 16 KiB decoder chunk, and one additional output copy while combining chunks. Callers own the configured trust ceilings.
|
|
89
|
+
|
|
76
90
|
## Key Files
|
|
77
91
|
|
|
78
92
|
- `ts/classes.smartarchive.ts` - Main SmartArchive class with fluent API
|
package/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @push.rocks/smartarchive 📦
|
|
2
2
|
|
|
3
|
-
A powerful, streaming-first archive manipulation library with a fluent builder API
|
|
3
|
+
A powerful, streaming-first archive manipulation library with a fluent builder API for Node.js and Deno, plus an isolated browser entrypoint for ZIP operations.
|
|
4
4
|
|
|
5
5
|
## Issue Reporting and Security
|
|
6
6
|
|
|
@@ -16,8 +16,9 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
|
|
|
16
16
|
- 🔧 **Flexible I/O** – Work with files, URLs, streams, and buffers seamlessly
|
|
17
17
|
- 🛠️ **Modern TypeScript** – Full type safety and excellent IDE support
|
|
18
18
|
- 🔄 **Dual-mode operation** – Extract existing archives OR create new ones
|
|
19
|
-
- 🦕 **Cross-runtime** –
|
|
20
|
-
- 🌐 **Browser-ready** –
|
|
19
|
+
- 🦕 **Cross-runtime ZIP tools** – ZIP operations work in Node.js, Deno, and browsers
|
|
20
|
+
- 🌐 **Browser-ready ZIP entrypoint** – `@push.rocks/smartarchive/web/zip` has no Node.js runtime dependencies
|
|
21
|
+
- **Bounded ZIP selection** - Validate archive structure and extract only selected entries under explicit limits
|
|
21
22
|
|
|
22
23
|
## Installation 📥
|
|
23
24
|
|
|
@@ -72,59 +73,32 @@ await SmartArchive.create()
|
|
|
72
73
|
.extract('./node_modules/lodash');
|
|
73
74
|
```
|
|
74
75
|
|
|
75
|
-
## Browser Usage 🌐
|
|
76
|
+
## Browser ZIP Usage 🌐
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
Use the isolated ZIP subpath in browsers so the module graph contains no Node.js runtime dependencies:
|
|
78
79
|
|
|
79
80
|
```typescript
|
|
80
|
-
|
|
81
|
-
import { TarTools, ZipTools, GzipTools, Bzip2Tools } from '@push.rocks/smartarchive/web';
|
|
81
|
+
import { ZipTools } from '@push.rocks/smartarchive/web/zip';
|
|
82
82
|
|
|
83
|
-
// Create a TAR archive in the browser
|
|
84
|
-
const tarTools = new TarTools();
|
|
85
|
-
const tarBuffer = await tarTools.packFiles([
|
|
86
|
-
{ archivePath: 'hello.txt', content: 'Hello from the browser!' },
|
|
87
|
-
{ archivePath: 'data.json', content: JSON.stringify({ browser: true }) }
|
|
88
|
-
]);
|
|
89
|
-
|
|
90
|
-
// Create a TAR.GZ archive
|
|
91
|
-
const tgzBuffer = await tarTools.packFilesToTarGz([
|
|
92
|
-
{ archivePath: 'file.txt', content: 'Compressed!' }
|
|
93
|
-
], 6);
|
|
94
|
-
|
|
95
|
-
// Extract a TAR archive
|
|
96
|
-
const entries = await tarTools.extractTar(tarBuffer);
|
|
97
|
-
for (const entry of entries) {
|
|
98
|
-
console.log(`${entry.path}: ${entry.content.length} bytes`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Work with ZIP files
|
|
102
83
|
const zipTools = new ZipTools();
|
|
103
84
|
const zipBuffer = await zipTools.createZip([
|
|
104
|
-
{ archivePath: '
|
|
85
|
+
{ archivePath: 'manifest.json', content: JSON.stringify({ name: 'example' }) }
|
|
105
86
|
], 6);
|
|
106
87
|
|
|
107
88
|
const zipEntries = await zipTools.extractZip(zipBuffer);
|
|
108
89
|
|
|
109
|
-
//
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
90
|
+
// Security-sensitive extraction validates first and only decompresses matches
|
|
91
|
+
const manifests = await zipTools.extractSelectedZipEntries(zipBuffer, {
|
|
92
|
+
filter: entry => entry.path === 'manifest.json',
|
|
93
|
+
maxArchiveBytes: 64 * 1024 * 1024,
|
|
94
|
+
maxEntries: 10_000,
|
|
95
|
+
maxSelectedEntries: 1,
|
|
96
|
+
maxCompressedEntryBytes: 1024 * 1024,
|
|
97
|
+
maxUncompressedEntryBytes: 1024 * 1024,
|
|
98
|
+
maxTotalUncompressedBytes: 1024 * 1024,
|
|
99
|
+
});
|
|
113
100
|
```
|
|
114
101
|
|
|
115
|
-
### Browser Bundle Exports
|
|
116
|
-
|
|
117
|
-
The `/web` subpath exports these browser-compatible tools:
|
|
118
|
-
|
|
119
|
-
| Export | Description |
|
|
120
|
-
|--------|-------------|
|
|
121
|
-
| `TarTools` | Create and extract TAR and TAR.GZ archives |
|
|
122
|
-
| `ZipTools` | Create and extract ZIP archives |
|
|
123
|
-
| `GzipTools` | GZIP compression and decompression |
|
|
124
|
-
| `Bzip2Tools` | BZIP2 decompression (extraction only) |
|
|
125
|
-
|
|
126
|
-
> 💡 **Note:** The browser bundle does **not** include `SmartArchive` (which requires filesystem access). Use the individual tool classes for browser applications.
|
|
127
|
-
|
|
128
102
|
## Core Concepts 💡
|
|
129
103
|
|
|
130
104
|
### Fluent Builder Pattern
|
|
@@ -469,8 +443,23 @@ const entries = await zipTools.extractZip(zipBuffer);
|
|
|
469
443
|
for (const entry of entries) {
|
|
470
444
|
console.log(`${entry.path}: ${entry.content.length} bytes`);
|
|
471
445
|
}
|
|
446
|
+
|
|
447
|
+
// Extract selected entries with explicit resource limits
|
|
448
|
+
const selectedEntries = await zipTools.extractSelectedZipEntries(zipBuffer, {
|
|
449
|
+
filter: entry => entry.path === 'data.bin',
|
|
450
|
+
maxArchiveBytes: 64 * 1024 * 1024,
|
|
451
|
+
maxEntries: 10_000,
|
|
452
|
+
maxSelectedEntries: 1,
|
|
453
|
+
maxCompressedEntryBytes: 2 * 1024 * 1024,
|
|
454
|
+
maxUncompressedEntryBytes: 8 * 1024 * 1024,
|
|
455
|
+
maxTotalUncompressedBytes: 8 * 1024 * 1024,
|
|
456
|
+
});
|
|
472
457
|
```
|
|
473
458
|
|
|
459
|
+
`extractSelectedZipEntries()` is the fail-closed API for untrusted ZIP input. It parses and validates the complete central directory and referenced local records before running the filter, then decompresses only selected entries. Archive and entry-count limits apply to the whole ZIP; compressed, uncompressed, selected-count, and total-output limits apply only to selected entries.
|
|
460
|
+
|
|
461
|
+
The bounded API intentionally accepts a strict interoperable subset: single-disk, non-ZIP64 archives with canonical ASCII or UTF-8 paths. Selected entries may be stored or DEFLATE-compressed. It rejects encrypted entries, ambiguous metadata, duplicate selected paths, overlapping records, trailing DEFLATE bytes, size mismatches, and CRC mismatches with `ZipError` and stable `ZIP_ERROR_CODES`. Existing `extractZip()` behavior is unchanged for trusted inputs requiring broader compatibility.
|
|
462
|
+
|
|
474
463
|
### In-memory round-trip
|
|
475
464
|
|
|
476
465
|
```typescript
|
|
@@ -558,13 +547,13 @@ fileStream.on('data', async (file) => {
|
|
|
558
547
|
|
|
559
548
|
## Supported Formats 📋
|
|
560
549
|
|
|
561
|
-
| Format | Extension(s) | Extract | Create |
|
|
562
|
-
|
|
563
|
-
| TAR | `.tar` | ✅ | ✅ |
|
|
564
|
-
| TAR.GZ / TGZ | `.tar.gz`, `.tgz` | ✅ | ✅ |
|
|
565
|
-
| ZIP | `.zip` | ✅ | ✅ |
|
|
566
|
-
| GZIP | `.gz` | ✅ | ✅ |
|
|
567
|
-
| BZIP2 | `.bz2` | ✅ | ❌ |
|
|
550
|
+
| Format | Extension(s) | Extract | Create |
|
|
551
|
+
|--------|--------------|---------|--------|
|
|
552
|
+
| TAR | `.tar` | ✅ | ✅ |
|
|
553
|
+
| TAR.GZ / TGZ | `.tar.gz`, `.tgz` | ✅ | ✅ |
|
|
554
|
+
| ZIP | `.zip` | ✅ | ✅ |
|
|
555
|
+
| GZIP | `.gz` | ✅ | ✅ |
|
|
556
|
+
| BZIP2 | `.bz2` | ✅ | ❌ |
|
|
568
557
|
|
|
569
558
|
## Type Definitions
|
|
570
559
|
|
|
@@ -608,7 +597,7 @@ interface IArchiveInfo {
|
|
|
608
597
|
1. **Use streaming for large files** – `.toStreamFiles()` processes entries one at a time without loading the entire archive
|
|
609
598
|
2. **Choose appropriate compression** – Use 1-3 for speed, 6 (default) for balance, 9 for maximum compression
|
|
610
599
|
3. **Filter early** – Use `.include()`/`.exclude()` to skip unwanted entries before processing
|
|
611
|
-
4. **Use Uint8Array in browsers** – The browser
|
|
600
|
+
4. **Use Uint8Array in browsers** – The browser ZIP entrypoint works with `Uint8Array`
|
|
612
601
|
|
|
613
602
|
## Error Handling 🛡️
|
|
614
603
|
|
|
@@ -634,7 +623,7 @@ try {
|
|
|
634
623
|
|
|
635
624
|
## License and Legal Information
|
|
636
625
|
|
|
637
|
-
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [
|
|
626
|
+
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](license) file.
|
|
638
627
|
|
|
639
628
|
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
640
629
|
|
|
@@ -646,7 +635,7 @@ Use of these trademarks must comply with Task Venture Capital GmbH's Trademark G
|
|
|
646
635
|
|
|
647
636
|
### Company Information
|
|
648
637
|
|
|
649
|
-
Task Venture Capital GmbH
|
|
638
|
+
Task Venture Capital GmbH<br>
|
|
650
639
|
Registered at District Court Bremen HRB 35230 HB, Germany
|
|
651
640
|
|
|
652
641
|
For any legal inquiries or further information, please contact us via email at hello@task.vc.
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type {
|
|
|
11
11
|
// Import browser-compatible tools from ts_shared
|
|
12
12
|
import { Bzip2Tools } from '../ts_shared/classes.bzip2tools.js';
|
|
13
13
|
import { GzipTools } from '../ts_shared/classes.gziptools.js';
|
|
14
|
-
import { ZipTools } from '
|
|
14
|
+
import { ZipTools } from './classes.ziptools.js';
|
|
15
15
|
|
|
16
16
|
// Import Node.js-extended TarTools
|
|
17
17
|
import { TarTools } from './classes.tartools.js';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { IArchiveEntry, TCompressionLevel } from '../ts_shared/interfaces.js';
|
|
3
|
+
import { ZipTools as SharedZipTools } from '../ts_shared/classes.ziptools.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Node.js streaming ZIP decompression transform
|
|
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
|
+
* Node.js ZIP tools with SmartFile and StreamFile creation support
|
|
52
|
+
*/
|
|
53
|
+
export class ZipTools extends SharedZipTools {
|
|
54
|
+
public getDecompressionStream(): ZipDecompressionTransform {
|
|
55
|
+
return new ZipDecompressionTransform();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public override async createZip(
|
|
59
|
+
entries: IArchiveEntry[],
|
|
60
|
+
compressionLevel?: TCompressionLevel,
|
|
61
|
+
): Promise<Uint8Array> {
|
|
62
|
+
const normalizedEntries: IArchiveEntry[] = [];
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
if (entry.content instanceof plugins.smartfile.SmartFile) {
|
|
65
|
+
normalizedEntries.push({ ...entry, content: new Uint8Array(entry.content.contents) });
|
|
66
|
+
} else if (entry.content instanceof plugins.smartfile.StreamFile) {
|
|
67
|
+
const buffer = await entry.content.getContentAsBuffer();
|
|
68
|
+
normalizedEntries.push({ ...entry, content: new Uint8Array(buffer) });
|
|
69
|
+
} else {
|
|
70
|
+
normalizedEntries.push(entry);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return super.createZip(normalizedEntries, compressionLevel);
|
|
74
|
+
}
|
|
75
|
+
}
|
package/ts/index.ts
CHANGED
|
@@ -9,3 +9,6 @@ export * from './classes.archiveanalyzer.js';
|
|
|
9
9
|
|
|
10
10
|
// Node.js-specific: Extended TarTools with streaming support (overrides shared TarTools)
|
|
11
11
|
export { TarTools, type ITarPackFileOptions } from './classes.tartools.js';
|
|
12
|
+
|
|
13
|
+
// Node.js-specific: Extended ZipTools with streaming and SmartFile support
|
|
14
|
+
export { ZipTools, ZipDecompressionTransform } from './classes.ziptools.js';
|