neozipkit 0.2.1 → 0.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/LICENSE +22 -0
- package/README.md +410 -186
- package/contracts/ABI.txt +889 -0
- package/contracts/Bytecode.txt +1 -0
- package/contracts/COMPILATION_NOTES.md +59 -0
- package/contracts/QUICK_DEPLOY.md +286 -0
- package/contracts/README.md +235 -131
- package/contracts/VERIFICATION.md +308 -0
- package/contracts/deploy-interactive.js +448 -0
- package/contracts/deployment-arbitrum-sepolia.json +12 -0
- package/contracts/deployment-arbitrum.json +12 -0
- package/contracts/deployment-eth-sepolia.json +12 -0
- package/contracts/hardhat.config.js +114 -0
- package/contracts/package.json +20 -0
- package/contracts/scripts/compile-and-extract.js +119 -0
- package/contracts/scripts/verify-contract.js +223 -0
- package/contracts/src/{NZIP-NFT-Private.sol → NZIP-NFT-v2.0.sol} +66 -151
- package/contracts/src/NZIP-NFT.sol +9 -15
- package/dist/blockchain/browser/TokenVerifierBrowser.d.ts +67 -0
- package/dist/blockchain/browser/TokenVerifierBrowser.d.ts.map +1 -0
- package/dist/blockchain/browser/TokenVerifierBrowser.js +313 -0
- package/dist/blockchain/browser/TokenVerifierBrowser.js.map +1 -0
- package/dist/blockchain/browser/WalletManagerBrowser.d.ts +98 -0
- package/dist/blockchain/browser/WalletManagerBrowser.d.ts.map +1 -0
- package/dist/blockchain/browser/WalletManagerBrowser.js +434 -0
- package/dist/blockchain/browser/WalletManagerBrowser.js.map +1 -0
- package/dist/blockchain/browser/ZipkitMinterBrowser.d.ts +58 -0
- package/dist/blockchain/browser/ZipkitMinterBrowser.d.ts.map +1 -0
- package/dist/blockchain/browser/ZipkitMinterBrowser.js +317 -0
- package/dist/blockchain/browser/ZipkitMinterBrowser.js.map +1 -0
- package/dist/blockchain/browser/index.d.ts +11 -0
- package/dist/blockchain/browser/index.d.ts.map +1 -0
- package/dist/blockchain/browser/index.js +16 -0
- package/dist/blockchain/browser/index.js.map +1 -0
- package/dist/blockchain/core/WalletManager.d.ts +124 -0
- package/dist/blockchain/core/WalletManager.d.ts.map +1 -0
- package/dist/blockchain/{ZipKitWallet.js → core/WalletManager.js} +157 -177
- package/dist/blockchain/core/WalletManager.js.map +1 -0
- package/dist/blockchain/{ZipKitMinter.d.ts → core/ZipkitMinter.d.ts} +46 -11
- package/dist/blockchain/core/ZipkitMinter.d.ts.map +1 -0
- package/dist/blockchain/core/ZipkitMinter.js +548 -0
- package/dist/blockchain/core/ZipkitMinter.js.map +1 -0
- package/dist/blockchain/{ZipKitOTS.d.ts → core/ZipkitOTS.d.ts} +6 -15
- package/dist/blockchain/core/ZipkitOTS.d.ts.map +1 -0
- package/dist/blockchain/{ZipKitOTS.js → core/ZipkitOTS.js} +26 -84
- package/dist/blockchain/core/ZipkitOTS.js.map +1 -0
- package/dist/blockchain/core/ZipkitVerifier.d.ts +177 -0
- package/dist/blockchain/core/ZipkitVerifier.d.ts.map +1 -0
- package/dist/blockchain/core/ZipkitVerifier.js +632 -0
- package/dist/blockchain/core/ZipkitVerifier.js.map +1 -0
- package/dist/blockchain/core/contracts.d.ts +83 -0
- package/dist/blockchain/core/contracts.d.ts.map +1 -0
- package/dist/blockchain/core/contracts.js +341 -0
- package/dist/blockchain/core/contracts.js.map +1 -0
- package/dist/blockchain/core/index.d.ts +12 -0
- package/dist/blockchain/core/index.d.ts.map +1 -0
- package/dist/blockchain/core/index.js +59 -0
- package/dist/blockchain/core/index.js.map +1 -0
- package/dist/blockchain/index.d.ts +11 -7
- package/dist/blockchain/index.d.ts.map +1 -0
- package/dist/blockchain/index.js +18 -34
- package/dist/blockchain/index.js.map +1 -0
- package/dist/blockchain/node/WalletManagerNode.d.ts +47 -0
- package/dist/blockchain/node/WalletManagerNode.d.ts.map +1 -0
- package/dist/blockchain/node/WalletManagerNode.js +203 -0
- package/dist/blockchain/node/WalletManagerNode.js.map +1 -0
- package/dist/blockchain/node/index.d.ts +12 -0
- package/dist/blockchain/node/index.d.ts.map +1 -0
- package/dist/blockchain/node/index.js +24 -0
- package/dist/blockchain/node/index.js.map +1 -0
- package/dist/browser/ZipkitBrowser.d.ts +27 -0
- package/dist/browser/ZipkitBrowser.d.ts.map +1 -0
- package/dist/browser/{ZipKitBrowser.js → ZipkitBrowser.js} +96 -43
- package/dist/browser/ZipkitBrowser.js.map +1 -0
- package/dist/browser/index.d.ts +12 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +42 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser-esm/index.d.ts +12 -0
- package/dist/browser-esm/index.js +59559 -0
- package/dist/browser-esm/index.js.map +7 -0
- package/dist/browser-umd/index.d.ts +12 -0
- package/dist/browser-umd/index.js +59576 -0
- package/dist/browser-umd/index.js.map +7 -0
- package/dist/browser-umd/index.min.js +63 -0
- package/dist/browser.d.ts +8 -14
- package/dist/browser.js +13 -28
- package/dist/core/ZipCompress.d.ts +99 -0
- package/dist/core/ZipCompress.d.ts.map +1 -0
- package/dist/core/ZipCompress.js +309 -0
- package/dist/core/ZipCompress.js.map +1 -0
- package/dist/core/ZipDecompress.d.ts +58 -0
- package/dist/core/ZipDecompress.d.ts.map +1 -0
- package/dist/core/ZipDecompress.js +172 -0
- package/dist/core/ZipDecompress.js.map +1 -0
- package/dist/{shared → core}/ZipEntry.d.ts +8 -1
- package/dist/core/ZipEntry.d.ts.map +1 -0
- package/dist/{shared → core}/ZipEntry.js +225 -82
- package/dist/core/ZipEntry.js.map +1 -0
- package/dist/core/Zipkit.d.ts +315 -0
- package/dist/core/Zipkit.d.ts.map +1 -0
- package/dist/core/Zipkit.js +647 -0
- package/dist/core/Zipkit.js.map +1 -0
- package/dist/core/components/HashCalculator.d.ts +138 -0
- package/dist/core/components/HashCalculator.d.ts.map +1 -0
- package/dist/core/components/HashCalculator.js +360 -0
- package/dist/core/components/HashCalculator.js.map +1 -0
- package/dist/core/components/Logger.d.ts +73 -0
- package/dist/core/components/Logger.d.ts.map +1 -0
- package/dist/core/components/Logger.js +156 -0
- package/dist/core/components/Logger.js.map +1 -0
- package/dist/core/components/ProgressTracker.d.ts +43 -0
- package/dist/core/components/ProgressTracker.d.ts.map +1 -0
- package/dist/core/components/ProgressTracker.js +112 -0
- package/dist/core/components/ProgressTracker.js.map +1 -0
- package/dist/{shared → core}/components/Support.d.ts +1 -0
- package/dist/core/components/Support.d.ts.map +1 -0
- package/dist/{shared → core}/components/Support.js +1 -0
- package/dist/core/components/Support.js.map +1 -0
- package/dist/{shared → core}/components/Util.d.ts +7 -2
- package/dist/core/components/Util.d.ts.map +1 -0
- package/dist/{shared → core}/components/Util.js +14 -34
- package/dist/core/components/Util.js.map +1 -0
- package/dist/{shared → core}/constants/Errors.d.ts +1 -2
- package/dist/core/constants/Errors.d.ts.map +1 -0
- package/dist/{shared → core}/constants/Errors.js +1 -3
- package/dist/core/constants/Errors.js.map +1 -0
- package/dist/{shared → core}/constants/Headers.d.ts +3 -0
- package/dist/core/constants/Headers.d.ts.map +1 -0
- package/dist/{shared → core}/constants/Headers.js +20 -17
- package/dist/core/constants/Headers.js.map +1 -0
- package/dist/core/encryption/Manager.d.ts +58 -0
- package/dist/core/encryption/Manager.d.ts.map +1 -0
- package/dist/core/encryption/Manager.js +121 -0
- package/dist/core/encryption/Manager.js.map +1 -0
- package/dist/core/encryption/ZipCrypto.d.ts +172 -0
- package/dist/core/encryption/ZipCrypto.d.ts.map +1 -0
- package/dist/core/encryption/ZipCrypto.js +554 -0
- package/dist/core/encryption/ZipCrypto.js.map +1 -0
- package/dist/core/encryption/index.d.ts +9 -0
- package/dist/core/encryption/index.d.ts.map +1 -0
- package/dist/core/encryption/index.js +17 -0
- package/dist/core/encryption/index.js.map +1 -0
- package/dist/core/encryption/types.d.ts +29 -0
- package/dist/core/encryption/types.d.ts.map +1 -0
- package/dist/core/encryption/types.js +12 -0
- package/dist/core/encryption/types.js.map +1 -0
- package/dist/core/index.d.ts +24 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +57 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/version.d.ts +5 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/core/version.js +31 -0
- package/dist/core/version.js.map +1 -0
- package/dist/index.d.ts +8 -18
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -35
- package/dist/index.js.map +1 -0
- package/dist/node/ZipCompressNode.d.ts +123 -0
- package/dist/node/ZipCompressNode.d.ts.map +1 -0
- package/dist/node/ZipCompressNode.js +576 -0
- package/dist/node/ZipCompressNode.js.map +1 -0
- package/dist/node/ZipDecompressNode.d.ts +120 -0
- package/dist/node/ZipDecompressNode.d.ts.map +1 -0
- package/dist/node/ZipDecompressNode.js +461 -0
- package/dist/node/ZipDecompressNode.js.map +1 -0
- package/dist/node/ZipkitNode.d.ts +406 -0
- package/dist/node/ZipkitNode.d.ts.map +1 -0
- package/dist/node/ZipkitNode.js +1305 -0
- package/dist/node/ZipkitNode.js.map +1 -0
- package/dist/node/index.d.ts +26 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +65 -0
- package/dist/node/index.js.map +1 -0
- package/dist/types/index.d.ts +59 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +36 -1
- package/dist/types/index.js.map +1 -0
- package/examples/README.md +269 -0
- package/examples/blockchain-tokenize.ts +354 -0
- package/examples/create-zip.ts +172 -0
- package/examples/extract-zip.ts +158 -0
- package/examples/list-zip.ts +161 -0
- package/examples/test-files/data.json +116 -0
- package/examples/test-files/document.md +80 -0
- package/examples/test-files/document.txt +2 -0
- package/examples/test-files/file1.txt +48 -0
- package/examples/test-files/file2.txt +80 -0
- package/examples/tsconfig.json +44 -0
- package/examples/unzip-token.ts +491 -0
- package/examples/verify-tokenized-zip.ts +305 -0
- package/examples/zip-token.ts +604 -0
- package/package.json +108 -24
- package/src/blockchain/README.md +59 -66
- package/src/{browser/TokenVerifierBrowser.ts-remove → blockchain/browser/TokenVerifierBrowser.ts} +125 -79
- package/src/blockchain/browser/WalletManagerBrowser.ts +486 -0
- package/src/blockchain/browser/ZipkitMinterBrowser.ts +400 -0
- package/src/blockchain/browser/index.ts +13 -0
- package/src/blockchain/{ZipKitWallet.ts → core/WalletManager.ts} +241 -143
- package/src/blockchain/core/ZipkitMinter.ts +716 -0
- package/src/blockchain/{ZipKitOTS.ts → core/ZipkitOTS.ts} +28 -62
- package/src/blockchain/core/ZipkitVerifier.ts +853 -0
- package/src/blockchain/core/contracts.ts +370 -0
- package/src/blockchain/core/index.ts +71 -0
- package/src/blockchain/index.ts +21 -55
- package/src/blockchain/node/WalletManagerNode.ts +171 -0
- package/src/blockchain/node/index.ts +40 -0
- package/src/browser/{ZipKitBrowser.ts → ZipkitBrowser.ts} +104 -41
- package/src/browser/index.esm.ts +37 -0
- package/src/browser/index.ts +25 -0
- package/src/core/ZipCompress.ts +398 -0
- package/src/core/ZipDecompress.ts +212 -0
- package/src/{shared → core}/ZipEntry.ts +240 -90
- package/src/core/Zipkit.ts +794 -0
- package/src/core/components/HashCalculator.ts +384 -0
- package/src/core/components/Logger.ts +180 -0
- package/src/core/components/ProgressTracker.ts +134 -0
- package/src/{shared → core}/components/Util.ts +12 -34
- package/src/{shared → core}/constants/Errors.ts +0 -4
- package/src/{shared → core}/constants/Headers.ts +19 -17
- package/src/core/encryption/Manager.ts +137 -0
- package/src/core/encryption/ZipCrypto.ts +650 -0
- package/src/core/encryption/index.ts +15 -0
- package/src/core/encryption/types.ts +33 -0
- package/src/core/index.ts +34 -0
- package/src/core/version.ts +33 -0
- package/src/index.ts +54 -25
- package/src/node/ZipCompressNode.ts +632 -0
- package/src/node/ZipDecompressNode.ts +508 -0
- package/src/node/ZipkitNode.ts +1559 -0
- package/src/node/index.ts +69 -0
- package/src/types/index.ts +131 -1
- package/src/types/modules.d.ts +1 -11
- package/src/types/opentimestamps.d.ts +1 -23
- package/contracts/INTEGRATION.md +0 -338
- package/contracts/NETWORKS.md +0 -156
- package/contracts/NZIP-NFT-bytecode.txt +0 -1
- package/contracts/constants.ts +0 -952
- package/contracts/deploy-bytecode.mjs +0 -94
- package/dist/blockchain/CertificateToken.d.ts +0 -48
- package/dist/blockchain/CertificateToken.js +0 -386
- package/dist/blockchain/ZipKitMinter.js +0 -347
- package/dist/blockchain/ZipKitNFT.d.ts +0 -3
- package/dist/blockchain/ZipKitNFT.js +0 -7
- package/dist/blockchain/ZipKitVerifier.d.ts +0 -69
- package/dist/blockchain/ZipKitVerifier.js +0 -289
- package/dist/blockchain/ZipKitWallet.d.ts +0 -69
- package/dist/blockchain/contracts.d.ts +0 -14
- package/dist/blockchain/contracts.js +0 -77
- package/dist/blockchain/types.d.ts +0 -47
- package/dist/blockchain/types.js +0 -30
- package/dist/browser/ZipKitBrowser.d.ts +0 -18
- package/dist/browser/ZipKitStampBrowser.d.ts +0 -2
- package/dist/browser/ZipKitStampBrowser.js +0 -12
- package/dist/browser-index.d.ts +0 -18
- package/dist/browser-index.js +0 -57
- package/dist/server/ZipKitServer.d.ts +0 -74
- package/dist/server/ZipKitServer.js +0 -310
- package/dist/shared/ZipKit.d.ts +0 -243
- package/dist/shared/ZipKit.js +0 -772
- package/dist/shared/components/CertificateOTS.d.ts +0 -15
- package/dist/shared/components/CertificateOTS.js +0 -116
- package/dist/shared/components/FileSys.d.ts +0 -2
- package/dist/shared/components/FileSys.js +0 -113
- package/dist/shared/components/HashAccumulator.d.ts +0 -55
- package/dist/shared/components/HashAccumulator.js +0 -100
- package/dist/shared/components/ZipCrypto.d.ts +0 -3
- package/dist/shared/components/ZipCrypto.js +0 -78
- package/dist/shared/components/merkleTree.d.ts +0 -66
- package/dist/shared/components/merkleTree.js +0 -136
- package/dist/shared/types/index.d.ts +0 -21
- package/dist/shared/types/index.js +0 -3
- package/dist/shared/types.d.ts +0 -33
- package/dist/shared/types.js +0 -8
- package/dist/shared/version.d.ts +0 -2
- package/dist/shared/version.js +0 -6
- package/src/blockchain/CertificateToken.ts +0 -444
- package/src/blockchain/ZipKitMinter.ts +0 -470
- package/src/blockchain/ZipKitNFT.ts +0 -12
- package/src/blockchain/ZipKitVerifier.ts +0 -382
- package/src/blockchain/contracts.ts +0 -88
- package/src/blockchain/types.ts +0 -91
- package/src/browser/CertificateGeneratorBrowser.ts-remove +0 -498
- package/src/browser/ZipKitStampBrowser.ts +0 -9
- package/src/browser-index.ts +0 -36
- package/src/server/README.md +0 -25
- package/src/server/ZipKitServer.ts +0 -305
- package/src/shared/ZipKit.ts +0 -881
- package/src/shared/components/CertificateOTS.ts +0 -132
- package/src/shared/components/FileSys.ts +0 -87
- package/src/shared/components/HashAccumulator.ts +0 -113
- package/src/shared/components/ZipCrypto.ts +0 -91
- package/src/shared/components/merkleTree.ts +0 -162
- package/src/shared/types/index.ts +0 -25
- package/src/shared/types/modules.d.ts +0 -22
- package/src/shared/types/opentimestamps.d.ts +0 -23
- package/src/shared/types.d.ts +0 -8
- package/src/shared/types.ts +0 -43
- package/src/shared/version.ts +0 -3
- /package/src/{shared → core}/components/Support.ts +0 -0
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
// ======================================
|
|
2
|
+
// ZipDecompressNode.ts - Node.js File-Based Decompression
|
|
3
|
+
// Copyright (c) 2025 NeoWare, Inc. All rights reserved.
|
|
4
|
+
// ======================================
|
|
5
|
+
//
|
|
6
|
+
// LOGGING INSTRUCTIONS:
|
|
7
|
+
// ---------------------
|
|
8
|
+
// To enable/disable logging, set loggingEnabled to true/false in the class:
|
|
9
|
+
// private static loggingEnabled: boolean = true; // Enable logging
|
|
10
|
+
// private static loggingEnabled: boolean = false; // Disable logging
|
|
11
|
+
//
|
|
12
|
+
// Logging respects the global Logger level (debug, info, warn, error, silent).
|
|
13
|
+
// Logger level is automatically set to 'debug' when loggingEnabled is true.
|
|
14
|
+
//
|
|
15
|
+
|
|
16
|
+
const pako = require('pako');
|
|
17
|
+
import { ZstdInit, ZstdSimple } from '@oneidentity/zstd-js';
|
|
18
|
+
import ZipkitNode from './ZipkitNode';
|
|
19
|
+
import { Logger } from '../core/components/Logger';
|
|
20
|
+
import ZipEntry from '../core/ZipEntry';
|
|
21
|
+
import Errors from '../core/constants/Errors';
|
|
22
|
+
import { CMP_METHOD } from '../core/constants/Headers';
|
|
23
|
+
import { HashCalculator } from '../core/components/HashCalculator';
|
|
24
|
+
import { DecryptionStream, ZipCrypto } from '../core/encryption/ZipCrypto';
|
|
25
|
+
import { EncryptionMethod } from '../core/encryption/types';
|
|
26
|
+
import { StreamingFileHandle } from '../core';
|
|
27
|
+
import * as fs from 'fs';
|
|
28
|
+
|
|
29
|
+
// Module-level singleton for ZSTD (shared across all instances)
|
|
30
|
+
let zstdCodec: { ZstdSimple: typeof ZstdSimple } | null = null;
|
|
31
|
+
|
|
32
|
+
// Async initialization function for zstd
|
|
33
|
+
async function initZstd(): Promise<{ ZstdSimple: typeof ZstdSimple }> {
|
|
34
|
+
if (!zstdCodec) {
|
|
35
|
+
zstdCodec = await ZstdInit();
|
|
36
|
+
}
|
|
37
|
+
return zstdCodec;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* ZipDecompressNode - Node.js file-based decompression operations
|
|
42
|
+
*
|
|
43
|
+
* Independent decompression implementation for Node.js environments.
|
|
44
|
+
* All decompression logic is implemented directly without delegating to ZipDecompress.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const zipkitNode = new ZipkitNode();
|
|
49
|
+
* const decompressNode = new ZipDecompressNode(zipkitNode);
|
|
50
|
+
* await decompressNode.extractToFile(entry, './output/file.txt');
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export class ZipDecompressNode {
|
|
54
|
+
private zipkitNode: ZipkitNode;
|
|
55
|
+
private zstdCodec: { ZstdSimple: typeof ZstdSimple } | null = null;
|
|
56
|
+
|
|
57
|
+
// Class-level logging control - set to true to enable logging
|
|
58
|
+
private static loggingEnabled: boolean = false;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Creates a new ZipDecompressNode instance
|
|
62
|
+
* @param zipkitNode - ZipkitNode instance to use for ZIP operations
|
|
63
|
+
*/
|
|
64
|
+
constructor(zipkitNode: ZipkitNode) {
|
|
65
|
+
this.zipkitNode = zipkitNode;
|
|
66
|
+
// If logging is enabled, ensure Logger level is set to debug
|
|
67
|
+
if (ZipDecompressNode.loggingEnabled) {
|
|
68
|
+
Logger.setLevel('debug');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Internal logging method - only logs if class logging is enabled
|
|
74
|
+
*/
|
|
75
|
+
private log(...args: any[]): void {
|
|
76
|
+
if (ZipDecompressNode.loggingEnabled) {
|
|
77
|
+
Logger.debug(`[ZipDecompressNode]`, ...args);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ============================================================================
|
|
82
|
+
// File-Based Extraction Methods
|
|
83
|
+
// ============================================================================
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Extract file directly to disk with true streaming (no memory buffering)
|
|
87
|
+
* Public method that validates file mode and extracts entry to file
|
|
88
|
+
*
|
|
89
|
+
* This method processes chunks as they are decompressed and writes them
|
|
90
|
+
* directly to disk, maintaining minimal memory footprint regardless of file size.
|
|
91
|
+
* This is the recommended method for file extraction to avoid memory issues.
|
|
92
|
+
*
|
|
93
|
+
* @param entry ZIP entry to extract
|
|
94
|
+
* @param outputPath Path where the file should be written
|
|
95
|
+
* @param options Optional extraction options including progress callback
|
|
96
|
+
* @throws Error if not a File-based ZIP
|
|
97
|
+
*/
|
|
98
|
+
async extractToFile(
|
|
99
|
+
entry: ZipEntry,
|
|
100
|
+
outputPath: string,
|
|
101
|
+
options?: {
|
|
102
|
+
skipHashCheck?: boolean;
|
|
103
|
+
onProgress?: (bytes: number) => void;
|
|
104
|
+
}
|
|
105
|
+
): Promise<void> {
|
|
106
|
+
// Lazy ZSTD initialization
|
|
107
|
+
// Note: ZSTD codec is lazily initialized on first use (module-level singleton)
|
|
108
|
+
if (entry.cmpMethod === CMP_METHOD.ZSTD && !this.zstdCodec) {
|
|
109
|
+
this.zstdCodec = await initZstd();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Get fileHandle from zipkitNode (merged from ZipLoadEntriesServer)
|
|
113
|
+
const fileHandle = (this.zipkitNode as any).getFileHandle();
|
|
114
|
+
|
|
115
|
+
// Call internal method with fileHandle
|
|
116
|
+
await this.extractToFileInternal(fileHandle, entry, outputPath, options);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// Internal File-Based Methods
|
|
121
|
+
// ============================================================================
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Read compressed data from file and yield one block at a time
|
|
125
|
+
*
|
|
126
|
+
* MEMORY EFFICIENCY: Yields compressed data chunks one at a time without accumulation.
|
|
127
|
+
* Each chunk is read from disk and yielded immediately, allowing downstream processing
|
|
128
|
+
* (decryption, decompression) to handle one block at a time.
|
|
129
|
+
*
|
|
130
|
+
* @param fileHandle - File handle to read from
|
|
131
|
+
* @param entry - ZIP entry to read compressed data for
|
|
132
|
+
* @param chunkSize - Optional chunk size override (defaults to ZipkitServer's bufferSize)
|
|
133
|
+
* @returns Async generator yielding compressed data chunks one at a time
|
|
134
|
+
*/
|
|
135
|
+
private async *readCompressedDataStream(
|
|
136
|
+
fileHandle: StreamingFileHandle,
|
|
137
|
+
entry: ZipEntry,
|
|
138
|
+
chunkSize?: number
|
|
139
|
+
): AsyncGenerator<Buffer> {
|
|
140
|
+
// Use provided chunkSize or ZipkitServer's default bufferSize
|
|
141
|
+
const effectiveChunkSize = chunkSize || this.zipkitNode.getBufferSize();
|
|
142
|
+
// Read local file header
|
|
143
|
+
const localHeaderBuffer = Buffer.alloc(30);
|
|
144
|
+
await fileHandle.read(localHeaderBuffer, 0, 30, entry.localHdrOffset);
|
|
145
|
+
|
|
146
|
+
if (localHeaderBuffer.readUInt32LE(0) !== 0x04034b50) { // LOCAL_HDR.SIGNATURE
|
|
147
|
+
throw new Error(Errors.INVALID_CEN);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Calculate data start position
|
|
151
|
+
const filenameLength = localHeaderBuffer.readUInt16LE(26);
|
|
152
|
+
const extraFieldLength = localHeaderBuffer.readUInt16LE(28);
|
|
153
|
+
const dataStart = entry.localHdrOffset + 30 + filenameLength + extraFieldLength;
|
|
154
|
+
|
|
155
|
+
// Yield compressed data in chunks - one block at a time
|
|
156
|
+
let remaining = entry.compressedSize;
|
|
157
|
+
let position = dataStart;
|
|
158
|
+
|
|
159
|
+
while (remaining > 0) {
|
|
160
|
+
const currentChunkSize = Math.min(effectiveChunkSize, remaining);
|
|
161
|
+
const chunk = Buffer.alloc(currentChunkSize);
|
|
162
|
+
await fileHandle.read(chunk, 0, currentChunkSize, position);
|
|
163
|
+
|
|
164
|
+
this.log(`readCompressedDataStream: Yielding compressed chunk: ${chunk.length} bytes (${remaining} bytes remaining)`);
|
|
165
|
+
yield chunk;
|
|
166
|
+
|
|
167
|
+
position += currentChunkSize;
|
|
168
|
+
remaining -= currentChunkSize;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Handles: reading compressed data, optional decryption, decompression, hashing, and writing
|
|
174
|
+
* Internal method that takes fileHandle as parameter
|
|
175
|
+
*/
|
|
176
|
+
private async extractToFileInternal(
|
|
177
|
+
fileHandle: any,
|
|
178
|
+
entry: ZipEntry,
|
|
179
|
+
outputPath: string,
|
|
180
|
+
options?: {
|
|
181
|
+
skipHashCheck?: boolean;
|
|
182
|
+
onProgress?: (bytes: number) => void;
|
|
183
|
+
}
|
|
184
|
+
): Promise<void> {
|
|
185
|
+
this.log(`extractToFileInternal called for entry: ${entry.filename}`);
|
|
186
|
+
this.log(`Entry isEncrypted: ${(entry as any).isEncrypted}, has password: ${!!(this.zipkitNode as any)?.password}`);
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
// Create output stream with overwrite flag to truncate existing files
|
|
190
|
+
const writeStream = fs.createWriteStream(outputPath, { flags: 'w' });
|
|
191
|
+
|
|
192
|
+
// Build compressed data stream - yields one block at a time
|
|
193
|
+
let dataStream = this.readCompressedDataStream(fileHandle, entry);
|
|
194
|
+
|
|
195
|
+
// Decrypt if needed using password on zipkitNode instance
|
|
196
|
+
// Decryption maintains state across blocks via updateKeys()
|
|
197
|
+
const isEncrypted = (entry as any).isEncrypted && (this.zipkitNode as any)?.password;
|
|
198
|
+
|
|
199
|
+
if (isEncrypted) {
|
|
200
|
+
this.log(`Starting decryption for entry: ${entry.filename}`);
|
|
201
|
+
|
|
202
|
+
// Prepare entry for decryption by parsing local header
|
|
203
|
+
await DecryptionStream.prepareEntryForDecryption(fileHandle, entry);
|
|
204
|
+
|
|
205
|
+
const encryptionMethod = (entry as any).encryptionMethod || EncryptionMethod.ZIP_CRYPTO;
|
|
206
|
+
|
|
207
|
+
this.log(`Creating DecryptionStream with method: ${encryptionMethod}`);
|
|
208
|
+
|
|
209
|
+
const decryptor = new DecryptionStream({
|
|
210
|
+
password: (this.zipkitNode as any).password,
|
|
211
|
+
method: encryptionMethod,
|
|
212
|
+
entry: entry
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
this.log(`DecryptionStream created, calling decrypt()...`);
|
|
216
|
+
// Decryption processes one block at a time, maintaining state across blocks
|
|
217
|
+
dataStream = decryptor.decrypt(dataStream);
|
|
218
|
+
this.log(`decrypt() returned, dataStream is now a generator that yields one decrypted block at a time`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Pipeline: readCompressedDataStream() → DecryptionStream.decrypt() → decompressStream() → writeStream
|
|
222
|
+
// Each stage processes one block at a time without accumulation
|
|
223
|
+
await this.unCompressToFile(dataStream, entry, writeStream, {
|
|
224
|
+
skipHashCheck: options?.skipHashCheck,
|
|
225
|
+
onProgress: options?.onProgress,
|
|
226
|
+
outputPath
|
|
227
|
+
});
|
|
228
|
+
} catch (error) {
|
|
229
|
+
throw error;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Decompress data stream and write to file
|
|
235
|
+
*
|
|
236
|
+
* MEMORY EFFICIENCY: Processes decompressed chunks one at a time.
|
|
237
|
+
* Pipeline: compressedStream → decompressStream() → hashCalc → writeStream
|
|
238
|
+
* - Each decompressed chunk is written immediately without accumulation
|
|
239
|
+
* - Hash calculation is incremental (HashCalculator)
|
|
240
|
+
* - Progress callbacks are invoked per chunk
|
|
241
|
+
*
|
|
242
|
+
* Handles decompression, hash calculation, file writing, and verification.
|
|
243
|
+
* Internal method only
|
|
244
|
+
*/
|
|
245
|
+
private async unCompressToFile(
|
|
246
|
+
compressedStream: AsyncGenerator<Buffer>,
|
|
247
|
+
entry: ZipEntry,
|
|
248
|
+
writeStream: any, // Node.js WriteStream
|
|
249
|
+
options?: {
|
|
250
|
+
skipHashCheck?: boolean;
|
|
251
|
+
onProgress?: (bytes: number) => void;
|
|
252
|
+
outputPath?: string; // For cleanup on error
|
|
253
|
+
}
|
|
254
|
+
): Promise<void> {
|
|
255
|
+
this.log(`unCompressToFile() called for entry: ${entry.filename}, method: ${entry.cmpMethod}`);
|
|
256
|
+
|
|
257
|
+
// Decompress stream - processes one block at a time
|
|
258
|
+
const decompressedStream = this.decompressStream(compressedStream, entry.cmpMethod);
|
|
259
|
+
|
|
260
|
+
// Process and write chunks - one block at a time
|
|
261
|
+
const hashCalc = new HashCalculator({ useSHA256: !!entry.sha256 });
|
|
262
|
+
let totalBytes = 0;
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
for await (const chunk of decompressedStream) {
|
|
266
|
+
this.log(`unCompressToFile: Processing decompressed chunk: ${chunk.length} bytes`);
|
|
267
|
+
hashCalc.update(chunk);
|
|
268
|
+
writeStream.write(chunk);
|
|
269
|
+
totalBytes += chunk.length;
|
|
270
|
+
|
|
271
|
+
if (options?.onProgress) {
|
|
272
|
+
options.onProgress(totalBytes);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Close stream
|
|
277
|
+
await new Promise((resolve, reject) => {
|
|
278
|
+
writeStream.end(() => resolve(undefined));
|
|
279
|
+
writeStream.on('error', reject);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Verify hash
|
|
283
|
+
if (!options?.skipHashCheck) {
|
|
284
|
+
if (entry.sha256) {
|
|
285
|
+
const calculatedHash = hashCalc.finalizeSHA256();
|
|
286
|
+
this.log(`SHA-256 comparison: calculated=${calculatedHash}, stored=${entry.sha256}`);
|
|
287
|
+
if (calculatedHash !== entry.sha256) {
|
|
288
|
+
if (options?.outputPath && fs) {
|
|
289
|
+
fs.unlinkSync(options.outputPath);
|
|
290
|
+
}
|
|
291
|
+
throw new Error(Errors.INVALID_SHA256);
|
|
292
|
+
}
|
|
293
|
+
this.log(`SHA-256 comparison: calculated=${calculatedHash}, stored=${entry.sha256}`);
|
|
294
|
+
} else {
|
|
295
|
+
const calculatedCRC = hashCalc.finalizeCRC32();
|
|
296
|
+
this.log(`CRC-32 comparison: calculated=${calculatedCRC}, stored=${entry.crc}`);
|
|
297
|
+
if (calculatedCRC !== entry.crc) {
|
|
298
|
+
if (options?.outputPath && fs) {
|
|
299
|
+
fs.unlinkSync(options.outputPath);
|
|
300
|
+
}
|
|
301
|
+
throw new Error(Errors.INVALID_CRC);
|
|
302
|
+
}
|
|
303
|
+
this.log(`CRC-32 comparison: calculated=${calculatedCRC}, stored=${entry.crc}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
} catch (error) {
|
|
307
|
+
// Cleanup file on error
|
|
308
|
+
if (options?.outputPath && fs) {
|
|
309
|
+
try {
|
|
310
|
+
fs.unlinkSync(options.outputPath);
|
|
311
|
+
} catch {
|
|
312
|
+
// Ignore cleanup errors
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ============================================================================
|
|
320
|
+
// Decompression Methods
|
|
321
|
+
// ============================================================================
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Decompress data stream chunk by chunk
|
|
325
|
+
*
|
|
326
|
+
* MEMORY EFFICIENCY: Processes compressed data one block at a time.
|
|
327
|
+
* - For STORED: Passes through chunks unchanged (no accumulation)
|
|
328
|
+
* - For DEFLATED: Uses pako streaming inflate (maintains state across chunks)
|
|
329
|
+
* - For ZSTD: Collects all chunks (ZSTD limitation - requires full buffer)
|
|
330
|
+
*
|
|
331
|
+
* Pipeline: readCompressedDataStream() → DecryptionStream.decrypt() → decompressStream() → writeStream
|
|
332
|
+
*
|
|
333
|
+
* Internal method only
|
|
334
|
+
*/
|
|
335
|
+
private async *decompressStream(
|
|
336
|
+
compressedStream: AsyncGenerator<Buffer>,
|
|
337
|
+
method: number,
|
|
338
|
+
chunkSize?: number
|
|
339
|
+
): AsyncGenerator<Buffer> {
|
|
340
|
+
// chunkSize parameter is currently unused but kept for API consistency
|
|
341
|
+
if (method === CMP_METHOD.STORED) {
|
|
342
|
+
// Pass through unchanged - one block at a time
|
|
343
|
+
for await (const chunk of compressedStream) {
|
|
344
|
+
yield chunk;
|
|
345
|
+
}
|
|
346
|
+
} else if (method === CMP_METHOD.DEFLATED) {
|
|
347
|
+
// Use pako streaming inflate - maintains state across chunks
|
|
348
|
+
yield* this.inflateStream(compressedStream);
|
|
349
|
+
} else if (method === CMP_METHOD.ZSTD) {
|
|
350
|
+
// Use ZSTD streaming decompression - note: ZSTD requires full buffer
|
|
351
|
+
yield* this.zstdDecompressStream(compressedStream);
|
|
352
|
+
} else {
|
|
353
|
+
throw new Error(`Unsupported compression method: ${method}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Streaming deflate decompression using pako
|
|
359
|
+
*
|
|
360
|
+
* MEMORY EFFICIENCY: Processes compressed chunks one at a time.
|
|
361
|
+
* - Inflator maintains decompression state across chunks
|
|
362
|
+
* - Decompressed chunks are yielded immediately after processing each compressed chunk
|
|
363
|
+
* - No accumulation of compressed data (except in pako's internal buffers)
|
|
364
|
+
*/
|
|
365
|
+
private async *inflateStream(
|
|
366
|
+
compressedStream: AsyncGenerator<Buffer>
|
|
367
|
+
): AsyncGenerator<Buffer> {
|
|
368
|
+
const inflator = new pako.Inflate({ raw: true });
|
|
369
|
+
const decompressedChunks: Buffer[] = [];
|
|
370
|
+
|
|
371
|
+
inflator.onData = (chunk: Uint8Array) => {
|
|
372
|
+
decompressedChunks.push(Buffer.from(chunk));
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// Process each compressed chunk one at a time
|
|
376
|
+
for await (const compressedChunk of compressedStream) {
|
|
377
|
+
this.log(`inflateStream: Processing compressed chunk: ${compressedChunk.length} bytes`);
|
|
378
|
+
inflator.push(compressedChunk, false);
|
|
379
|
+
|
|
380
|
+
// Yield accumulated decompressed chunks immediately (no accumulation)
|
|
381
|
+
for (const chunk of decompressedChunks) {
|
|
382
|
+
yield chunk;
|
|
383
|
+
}
|
|
384
|
+
decompressedChunks.length = 0;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Finalize decompression
|
|
388
|
+
inflator.push(new Uint8Array(0), true);
|
|
389
|
+
for (const chunk of decompressedChunks) {
|
|
390
|
+
yield chunk;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Streaming ZSTD decompression
|
|
396
|
+
*/
|
|
397
|
+
private async *zstdDecompressStream(
|
|
398
|
+
compressedStream: AsyncGenerator<Buffer>
|
|
399
|
+
): AsyncGenerator<Buffer> {
|
|
400
|
+
// ZSTD is guaranteed to be initialized via factory method
|
|
401
|
+
|
|
402
|
+
// Collect all compressed chunks first (ZSTD needs complete data)
|
|
403
|
+
const compressedChunks: Buffer[] = [];
|
|
404
|
+
for await (const chunk of compressedStream) {
|
|
405
|
+
compressedChunks.push(chunk);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const compressedData = Buffer.concat(compressedChunks);
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
if (!this.zstdCodec) {
|
|
412
|
+
throw new Error('ZSTD codec not initialized.');
|
|
413
|
+
}
|
|
414
|
+
const decompressed = this.zstdCodec.ZstdSimple.decompress(compressedData);
|
|
415
|
+
const decompressedBuffer = Buffer.from(decompressed);
|
|
416
|
+
|
|
417
|
+
// Yield decompressed data in chunks using ZipkitServer's bufferSize
|
|
418
|
+
const chunkSize = this.zipkitNode.getBufferSize();
|
|
419
|
+
let offset = 0;
|
|
420
|
+
while (offset < decompressedBuffer.length) {
|
|
421
|
+
const end = Math.min(offset + chunkSize, decompressedBuffer.length);
|
|
422
|
+
yield decompressedBuffer.slice(offset, end);
|
|
423
|
+
offset = end;
|
|
424
|
+
}
|
|
425
|
+
} catch (error) {
|
|
426
|
+
throw new Error(`ZSTD streaming decompression failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Inflate data using pako (internal use only)
|
|
432
|
+
*/
|
|
433
|
+
private inflate(data: Buffer): Buffer {
|
|
434
|
+
this.log(`inflate() called with ${data.length} bytes`);
|
|
435
|
+
const result = pako.inflateRaw(data);
|
|
436
|
+
return Buffer.from(result.buffer, result.byteOffset, result.byteLength);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Synchronous zstd decompress method for in-memory mode
|
|
441
|
+
* ZSTD codec is guaranteed to be initialized via factory method
|
|
442
|
+
* Internal method only
|
|
443
|
+
*/
|
|
444
|
+
private zstdDecompressSync(data: Buffer): Buffer {
|
|
445
|
+
this.log(`zstdDecompressSync() called with ${data.length} bytes`);
|
|
446
|
+
|
|
447
|
+
try {
|
|
448
|
+
// Ensure ZSTD is initialized
|
|
449
|
+
if (!this.zstdCodec) {
|
|
450
|
+
throw new Error('ZSTD codec not initialized.');
|
|
451
|
+
}
|
|
452
|
+
const decompressed = this.zstdCodec.ZstdSimple.decompress(data);
|
|
453
|
+
this.log(`ZSTD synchronous decompression successful: ${data.length} bytes -> ${decompressed.length} bytes`);
|
|
454
|
+
return Buffer.from(decompressed);
|
|
455
|
+
} catch (error) {
|
|
456
|
+
this.log(`ZSTD synchronous decompression failed: ${error}`);
|
|
457
|
+
throw new Error(`ZSTD synchronous decompression failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Uncompress compressed data buffer (synchronous for in-memory mode)
|
|
463
|
+
* Handles decompression and hash verification
|
|
464
|
+
* Internal method only
|
|
465
|
+
*/
|
|
466
|
+
private unCompress(
|
|
467
|
+
compressedData: Buffer,
|
|
468
|
+
entry: ZipEntry,
|
|
469
|
+
skipHashCheck?: boolean
|
|
470
|
+
): Buffer {
|
|
471
|
+
this.log(`unCompress() called for entry: ${entry.filename}, method: ${entry.cmpMethod}, data length: ${compressedData.length}`);
|
|
472
|
+
|
|
473
|
+
if (compressedData.length === 0) {
|
|
474
|
+
return Buffer.alloc(0);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
let outBuf: Buffer;
|
|
478
|
+
if (entry.cmpMethod === CMP_METHOD.STORED) {
|
|
479
|
+
outBuf = compressedData;
|
|
480
|
+
} else if (entry.cmpMethod === CMP_METHOD.DEFLATED) {
|
|
481
|
+
// Use synchronous inflate for deflate
|
|
482
|
+
outBuf = this.inflate(compressedData);
|
|
483
|
+
} else if (entry.cmpMethod === CMP_METHOD.ZSTD) {
|
|
484
|
+
// Use synchronous ZSTD decompression for in-memory mode
|
|
485
|
+
outBuf = this.zstdDecompressSync(compressedData);
|
|
486
|
+
} else {
|
|
487
|
+
throw new Error(`Unsupported compression method: ${entry.cmpMethod}`);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Verify hash
|
|
491
|
+
if (!skipHashCheck) {
|
|
492
|
+
if (entry.sha256) {
|
|
493
|
+
const isValid = this.zipkitNode.testSHA256(entry, outBuf);
|
|
494
|
+
if (!isValid) {
|
|
495
|
+
throw new Error(Errors.INVALID_SHA256);
|
|
496
|
+
}
|
|
497
|
+
} else {
|
|
498
|
+
const isValid = this.zipkitNode.testCRC32(entry, outBuf);
|
|
499
|
+
if (!isValid) {
|
|
500
|
+
throw new Error(Errors.INVALID_CRC);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return outBuf;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|