@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.
Files changed (38) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/classes.smartarchive.d.ts +1 -1
  3. package/dist_ts/classes.smartarchive.js +2 -2
  4. package/dist_ts/classes.ziptools.d.ts +18 -0
  5. package/dist_ts/classes.ziptools.js +70 -0
  6. package/dist_ts/index.d.ts +1 -0
  7. package/dist_ts/index.js +3 -1
  8. package/dist_ts_shared/bzip2/index.js +2 -2
  9. package/dist_ts_shared/classes.ziptools.d.ts +6 -13
  10. package/dist_ts_shared/classes.ziptools.js +511 -54
  11. package/dist_ts_shared/errors.d.ts +25 -0
  12. package/dist_ts_shared/errors.js +28 -1
  13. package/dist_ts_shared/index.d.ts +1 -1
  14. package/dist_ts_shared/index.js +2 -2
  15. package/dist_ts_shared/interfaces.d.ts +35 -0
  16. package/dist_ts_shared/plugins.ziptools.d.ts +3 -0
  17. package/dist_ts_shared/plugins.ziptools.js +4 -0
  18. package/dist_ts_web/00_commitinfo_data.js +1 -1
  19. package/dist_ts_web/index.d.ts +1 -1
  20. package/dist_ts_web/index.js +1 -1
  21. package/dist_ts_web/plugins.d.ts +1 -1
  22. package/dist_ts_web/plugins.js +1 -1
  23. package/dist_ts_web/zip.d.ts +3 -0
  24. package/dist_ts_web/zip.js +3 -0
  25. package/package.json +5 -3
  26. package/readme.hints.md +15 -1
  27. package/readme.md +43 -54
  28. package/ts/00_commitinfo_data.ts +1 -1
  29. package/ts/classes.smartarchive.ts +1 -1
  30. package/ts/classes.ziptools.ts +75 -0
  31. package/ts/index.ts +3 -0
  32. package/ts_shared/classes.ziptools.ts +690 -51
  33. package/ts_shared/errors.ts +31 -0
  34. package/ts_shared/index.ts +1 -1
  35. package/ts_shared/interfaces.ts +38 -0
  36. package/ts_shared/plugins.ziptools.ts +4 -0
  37. package/ts_web/00_commitinfo_data.ts +1 -1
  38. 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
  */
@@ -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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfc2hhcmVkL2Vycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUNILE1BQU0sT0FBTyxpQkFBa0IsU0FBUSxLQUFLO0lBRzFDLFlBQVksT0FBZSxFQUFFLElBQVk7UUFDdkMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2YsSUFBSSxDQUFDLElBQUksR0FBRyxtQkFBbUIsQ0FBQztRQUNoQyxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNqQiwrREFBK0Q7UUFDL0QsSUFBSSxLQUFLLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZQUM1QixLQUFLLENBQUMsaUJBQWlCLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNsRCxDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sVUFBVyxTQUFRLGlCQUFpQjtJQUMvQyxZQUFZLE9BQWUsRUFBRSxPQUFlLGFBQWE7UUFDdkQsS0FBSyxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztRQUNyQixJQUFJLENBQUMsSUFBSSxHQUFHLFlBQVksQ0FBQztJQUMzQixDQUFDO0NBQ0Y7QUFFRDs7R0FFRztBQUNILE1BQU0sT0FBTyxrQkFBbUIsU0FBUSxpQkFBaUI7SUFDdkQsWUFBWSxPQUFlO1FBQ3pCLEtBQUssQ0FBQyxPQUFPLEVBQUUsc0JBQXNCLENBQUMsQ0FBQztRQUN2QyxJQUFJLENBQUMsSUFBSSxHQUFHLG9CQUFvQixDQUFDO0lBQ25DLENBQUM7Q0FDRjtBQUVEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLFdBQVksU0FBUSxpQkFBaUI7SUFDaEQsWUFBWSxPQUFlO1FBQ3pCLEtBQUssQ0FBQyxPQUFPLEVBQUUsY0FBYyxDQUFDLENBQUM7UUFDL0IsSUFBSSxDQUFDLElBQUksR0FBRyxhQUFhLENBQUM7SUFDNUIsQ0FBQztDQUNGO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRztJQUMvQixlQUFlLEVBQUUsZ0JBQWdCO0lBQ2pDLGVBQWUsRUFBRSx1QkFBdUI7SUFDeEMsWUFBWSxFQUFFLG9CQUFvQjtJQUNsQyxrQkFBa0IsRUFBRSxxQkFBcUI7SUFDekMsZUFBZSxFQUFFLHVCQUF1QjtJQUN4QyxlQUFlLEVBQUUsdUJBQXVCO0lBQ3hDLGdCQUFnQixFQUFFLHdCQUF3QjtJQUMxQyxnQkFBZ0IsRUFBRSx3QkFBd0I7SUFDMUMsYUFBYSxFQUFFLHFCQUFxQjtDQUM1QixDQUFDO0FBSVg7O0dBRUc7QUFDSCxNQUFNLFVBQVUsZUFBZSxDQUFDLE9BQWUsRUFBRSxJQUFxQjtJQUNwRSxNQUFNLElBQUksVUFBVSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztBQUN0QyxDQUFDIn0=
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, ZipDecompressionTransform } from './classes.ziptools.js';
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';
@@ -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, ZipDecompressionTransform } from './classes.ziptools.js';
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19zaGFyZWQvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsNkNBQTZDO0FBRTdDLHVCQUF1QjtBQUN2QixjQUFjLGlCQUFpQixDQUFDO0FBRWhDLGdCQUFnQjtBQUNoQixjQUFjLGFBQWEsQ0FBQztBQUU1QixlQUFlO0FBQ2YsT0FBTyxFQUFFLFFBQVEsRUFBRSx5QkFBeUIsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQzVFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNuRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDakQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRXJELHVDQUF1QztBQUN2QyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDakQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGtCQUFrQixDQUFDIn0=
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,3 @@
1
+ import * as fflate from 'fflate';
2
+ import * as pako from 'pako';
3
+ export { fflate, pako };
@@ -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.1.0',
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
@@ -1 +1 @@
1
- export * from '../ts_shared/index.js';
1
+ export * from '../dist_ts_shared/index.js';
@@ -1,4 +1,4 @@
1
1
  // ts_web - Browser-compatible entry point
2
2
  // Re-exports everything from ts_shared
3
- export * from '../ts_shared/index.js';
3
+ export * from '../dist_ts_shared/index.js';
4
4
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90c193ZWIvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsMENBQTBDO0FBQzFDLHVDQUF1QztBQUV2QyxjQUFjLHVCQUF1QixDQUFDIn0=
@@ -1 +1 @@
1
- export * from '../ts_shared/plugins.js';
1
+ export * from '../dist_ts_shared/plugins.js';
@@ -1,4 +1,4 @@
1
1
  // Browser-compatible plugins for ts_web
2
2
  // Re-export from ts_shared
3
- export * from '../ts_shared/plugins.js';
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.2.3",
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 --web",
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-specific operations using fflate
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. Works seamlessly in **Node.js**, **Deno**, and **browsers**.
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** – Works in Node.js, Deno, and browsers
20
- - 🌐 **Browser-ready** – Dedicated browser bundle with zero Node.js dependencies
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
- smartarchive provides a dedicated browser-compatible bundle with no Node.js dependencies:
78
+ Use the isolated ZIP subpath in browsers so the module graph contains no Node.js runtime dependencies:
78
79
 
79
80
  ```typescript
80
- // Import from the /web subpath for browser environments
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: 'doc.txt', content: 'Document content' }
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
- // GZIP compression
110
- const gzipTools = new GzipTools();
111
- const compressed = gzipTools.compressSync(new TextEncoder().encode('Hello World'), 6);
112
- const decompressed = gzipTools.decompressSync(compressed);
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 | Browser |
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 bundle works with `Uint8Array` for optimal performance
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 [LICENSE](./LICENSE) file.
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.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartarchive',
6
- version: '5.2.3',
6
+ version: '5.3.0',
7
7
  description: 'A library for working with archive files, providing utilities for compressing and decompressing data.'
8
8
  }
@@ -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 '../ts_shared/classes.ziptools.js';
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';