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.
Files changed (301) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +410 -186
  3. package/contracts/ABI.txt +889 -0
  4. package/contracts/Bytecode.txt +1 -0
  5. package/contracts/COMPILATION_NOTES.md +59 -0
  6. package/contracts/QUICK_DEPLOY.md +286 -0
  7. package/contracts/README.md +235 -131
  8. package/contracts/VERIFICATION.md +308 -0
  9. package/contracts/deploy-interactive.js +448 -0
  10. package/contracts/deployment-arbitrum-sepolia.json +12 -0
  11. package/contracts/deployment-arbitrum.json +12 -0
  12. package/contracts/deployment-eth-sepolia.json +12 -0
  13. package/contracts/hardhat.config.js +114 -0
  14. package/contracts/package.json +20 -0
  15. package/contracts/scripts/compile-and-extract.js +119 -0
  16. package/contracts/scripts/verify-contract.js +223 -0
  17. package/contracts/src/{NZIP-NFT-Private.sol → NZIP-NFT-v2.0.sol} +66 -151
  18. package/contracts/src/NZIP-NFT.sol +9 -15
  19. package/dist/blockchain/browser/TokenVerifierBrowser.d.ts +67 -0
  20. package/dist/blockchain/browser/TokenVerifierBrowser.d.ts.map +1 -0
  21. package/dist/blockchain/browser/TokenVerifierBrowser.js +313 -0
  22. package/dist/blockchain/browser/TokenVerifierBrowser.js.map +1 -0
  23. package/dist/blockchain/browser/WalletManagerBrowser.d.ts +98 -0
  24. package/dist/blockchain/browser/WalletManagerBrowser.d.ts.map +1 -0
  25. package/dist/blockchain/browser/WalletManagerBrowser.js +434 -0
  26. package/dist/blockchain/browser/WalletManagerBrowser.js.map +1 -0
  27. package/dist/blockchain/browser/ZipkitMinterBrowser.d.ts +58 -0
  28. package/dist/blockchain/browser/ZipkitMinterBrowser.d.ts.map +1 -0
  29. package/dist/blockchain/browser/ZipkitMinterBrowser.js +317 -0
  30. package/dist/blockchain/browser/ZipkitMinterBrowser.js.map +1 -0
  31. package/dist/blockchain/browser/index.d.ts +11 -0
  32. package/dist/blockchain/browser/index.d.ts.map +1 -0
  33. package/dist/blockchain/browser/index.js +16 -0
  34. package/dist/blockchain/browser/index.js.map +1 -0
  35. package/dist/blockchain/core/WalletManager.d.ts +124 -0
  36. package/dist/blockchain/core/WalletManager.d.ts.map +1 -0
  37. package/dist/blockchain/{ZipKitWallet.js → core/WalletManager.js} +157 -177
  38. package/dist/blockchain/core/WalletManager.js.map +1 -0
  39. package/dist/blockchain/{ZipKitMinter.d.ts → core/ZipkitMinter.d.ts} +46 -11
  40. package/dist/blockchain/core/ZipkitMinter.d.ts.map +1 -0
  41. package/dist/blockchain/core/ZipkitMinter.js +548 -0
  42. package/dist/blockchain/core/ZipkitMinter.js.map +1 -0
  43. package/dist/blockchain/{ZipKitOTS.d.ts → core/ZipkitOTS.d.ts} +6 -15
  44. package/dist/blockchain/core/ZipkitOTS.d.ts.map +1 -0
  45. package/dist/blockchain/{ZipKitOTS.js → core/ZipkitOTS.js} +26 -84
  46. package/dist/blockchain/core/ZipkitOTS.js.map +1 -0
  47. package/dist/blockchain/core/ZipkitVerifier.d.ts +177 -0
  48. package/dist/blockchain/core/ZipkitVerifier.d.ts.map +1 -0
  49. package/dist/blockchain/core/ZipkitVerifier.js +632 -0
  50. package/dist/blockchain/core/ZipkitVerifier.js.map +1 -0
  51. package/dist/blockchain/core/contracts.d.ts +83 -0
  52. package/dist/blockchain/core/contracts.d.ts.map +1 -0
  53. package/dist/blockchain/core/contracts.js +341 -0
  54. package/dist/blockchain/core/contracts.js.map +1 -0
  55. package/dist/blockchain/core/index.d.ts +12 -0
  56. package/dist/blockchain/core/index.d.ts.map +1 -0
  57. package/dist/blockchain/core/index.js +59 -0
  58. package/dist/blockchain/core/index.js.map +1 -0
  59. package/dist/blockchain/index.d.ts +11 -7
  60. package/dist/blockchain/index.d.ts.map +1 -0
  61. package/dist/blockchain/index.js +18 -34
  62. package/dist/blockchain/index.js.map +1 -0
  63. package/dist/blockchain/node/WalletManagerNode.d.ts +47 -0
  64. package/dist/blockchain/node/WalletManagerNode.d.ts.map +1 -0
  65. package/dist/blockchain/node/WalletManagerNode.js +203 -0
  66. package/dist/blockchain/node/WalletManagerNode.js.map +1 -0
  67. package/dist/blockchain/node/index.d.ts +12 -0
  68. package/dist/blockchain/node/index.d.ts.map +1 -0
  69. package/dist/blockchain/node/index.js +24 -0
  70. package/dist/blockchain/node/index.js.map +1 -0
  71. package/dist/browser/ZipkitBrowser.d.ts +27 -0
  72. package/dist/browser/ZipkitBrowser.d.ts.map +1 -0
  73. package/dist/browser/{ZipKitBrowser.js → ZipkitBrowser.js} +96 -43
  74. package/dist/browser/ZipkitBrowser.js.map +1 -0
  75. package/dist/browser/index.d.ts +12 -0
  76. package/dist/browser/index.d.ts.map +1 -0
  77. package/dist/browser/index.js +42 -0
  78. package/dist/browser/index.js.map +1 -0
  79. package/dist/browser-esm/index.d.ts +12 -0
  80. package/dist/browser-esm/index.js +59559 -0
  81. package/dist/browser-esm/index.js.map +7 -0
  82. package/dist/browser-umd/index.d.ts +12 -0
  83. package/dist/browser-umd/index.js +59576 -0
  84. package/dist/browser-umd/index.js.map +7 -0
  85. package/dist/browser-umd/index.min.js +63 -0
  86. package/dist/browser.d.ts +8 -14
  87. package/dist/browser.js +13 -28
  88. package/dist/core/ZipCompress.d.ts +99 -0
  89. package/dist/core/ZipCompress.d.ts.map +1 -0
  90. package/dist/core/ZipCompress.js +309 -0
  91. package/dist/core/ZipCompress.js.map +1 -0
  92. package/dist/core/ZipDecompress.d.ts +58 -0
  93. package/dist/core/ZipDecompress.d.ts.map +1 -0
  94. package/dist/core/ZipDecompress.js +172 -0
  95. package/dist/core/ZipDecompress.js.map +1 -0
  96. package/dist/{shared → core}/ZipEntry.d.ts +8 -1
  97. package/dist/core/ZipEntry.d.ts.map +1 -0
  98. package/dist/{shared → core}/ZipEntry.js +225 -82
  99. package/dist/core/ZipEntry.js.map +1 -0
  100. package/dist/core/Zipkit.d.ts +315 -0
  101. package/dist/core/Zipkit.d.ts.map +1 -0
  102. package/dist/core/Zipkit.js +647 -0
  103. package/dist/core/Zipkit.js.map +1 -0
  104. package/dist/core/components/HashCalculator.d.ts +138 -0
  105. package/dist/core/components/HashCalculator.d.ts.map +1 -0
  106. package/dist/core/components/HashCalculator.js +360 -0
  107. package/dist/core/components/HashCalculator.js.map +1 -0
  108. package/dist/core/components/Logger.d.ts +73 -0
  109. package/dist/core/components/Logger.d.ts.map +1 -0
  110. package/dist/core/components/Logger.js +156 -0
  111. package/dist/core/components/Logger.js.map +1 -0
  112. package/dist/core/components/ProgressTracker.d.ts +43 -0
  113. package/dist/core/components/ProgressTracker.d.ts.map +1 -0
  114. package/dist/core/components/ProgressTracker.js +112 -0
  115. package/dist/core/components/ProgressTracker.js.map +1 -0
  116. package/dist/{shared → core}/components/Support.d.ts +1 -0
  117. package/dist/core/components/Support.d.ts.map +1 -0
  118. package/dist/{shared → core}/components/Support.js +1 -0
  119. package/dist/core/components/Support.js.map +1 -0
  120. package/dist/{shared → core}/components/Util.d.ts +7 -2
  121. package/dist/core/components/Util.d.ts.map +1 -0
  122. package/dist/{shared → core}/components/Util.js +14 -34
  123. package/dist/core/components/Util.js.map +1 -0
  124. package/dist/{shared → core}/constants/Errors.d.ts +1 -2
  125. package/dist/core/constants/Errors.d.ts.map +1 -0
  126. package/dist/{shared → core}/constants/Errors.js +1 -3
  127. package/dist/core/constants/Errors.js.map +1 -0
  128. package/dist/{shared → core}/constants/Headers.d.ts +3 -0
  129. package/dist/core/constants/Headers.d.ts.map +1 -0
  130. package/dist/{shared → core}/constants/Headers.js +20 -17
  131. package/dist/core/constants/Headers.js.map +1 -0
  132. package/dist/core/encryption/Manager.d.ts +58 -0
  133. package/dist/core/encryption/Manager.d.ts.map +1 -0
  134. package/dist/core/encryption/Manager.js +121 -0
  135. package/dist/core/encryption/Manager.js.map +1 -0
  136. package/dist/core/encryption/ZipCrypto.d.ts +172 -0
  137. package/dist/core/encryption/ZipCrypto.d.ts.map +1 -0
  138. package/dist/core/encryption/ZipCrypto.js +554 -0
  139. package/dist/core/encryption/ZipCrypto.js.map +1 -0
  140. package/dist/core/encryption/index.d.ts +9 -0
  141. package/dist/core/encryption/index.d.ts.map +1 -0
  142. package/dist/core/encryption/index.js +17 -0
  143. package/dist/core/encryption/index.js.map +1 -0
  144. package/dist/core/encryption/types.d.ts +29 -0
  145. package/dist/core/encryption/types.d.ts.map +1 -0
  146. package/dist/core/encryption/types.js +12 -0
  147. package/dist/core/encryption/types.js.map +1 -0
  148. package/dist/core/index.d.ts +24 -0
  149. package/dist/core/index.d.ts.map +1 -0
  150. package/dist/core/index.js +57 -0
  151. package/dist/core/index.js.map +1 -0
  152. package/dist/core/version.d.ts +5 -0
  153. package/dist/core/version.d.ts.map +1 -0
  154. package/dist/core/version.js +31 -0
  155. package/dist/core/version.js.map +1 -0
  156. package/dist/index.d.ts +8 -18
  157. package/dist/index.d.ts.map +1 -0
  158. package/dist/index.js +29 -35
  159. package/dist/index.js.map +1 -0
  160. package/dist/node/ZipCompressNode.d.ts +123 -0
  161. package/dist/node/ZipCompressNode.d.ts.map +1 -0
  162. package/dist/node/ZipCompressNode.js +576 -0
  163. package/dist/node/ZipCompressNode.js.map +1 -0
  164. package/dist/node/ZipDecompressNode.d.ts +120 -0
  165. package/dist/node/ZipDecompressNode.d.ts.map +1 -0
  166. package/dist/node/ZipDecompressNode.js +461 -0
  167. package/dist/node/ZipDecompressNode.js.map +1 -0
  168. package/dist/node/ZipkitNode.d.ts +406 -0
  169. package/dist/node/ZipkitNode.d.ts.map +1 -0
  170. package/dist/node/ZipkitNode.js +1305 -0
  171. package/dist/node/ZipkitNode.js.map +1 -0
  172. package/dist/node/index.d.ts +26 -0
  173. package/dist/node/index.d.ts.map +1 -0
  174. package/dist/node/index.js +65 -0
  175. package/dist/node/index.js.map +1 -0
  176. package/dist/types/index.d.ts +59 -0
  177. package/dist/types/index.d.ts.map +1 -0
  178. package/dist/types/index.js +36 -1
  179. package/dist/types/index.js.map +1 -0
  180. package/examples/README.md +269 -0
  181. package/examples/blockchain-tokenize.ts +354 -0
  182. package/examples/create-zip.ts +172 -0
  183. package/examples/extract-zip.ts +158 -0
  184. package/examples/list-zip.ts +161 -0
  185. package/examples/test-files/data.json +116 -0
  186. package/examples/test-files/document.md +80 -0
  187. package/examples/test-files/document.txt +2 -0
  188. package/examples/test-files/file1.txt +48 -0
  189. package/examples/test-files/file2.txt +80 -0
  190. package/examples/tsconfig.json +44 -0
  191. package/examples/unzip-token.ts +491 -0
  192. package/examples/verify-tokenized-zip.ts +305 -0
  193. package/examples/zip-token.ts +604 -0
  194. package/package.json +108 -24
  195. package/src/blockchain/README.md +59 -66
  196. package/src/{browser/TokenVerifierBrowser.ts-remove → blockchain/browser/TokenVerifierBrowser.ts} +125 -79
  197. package/src/blockchain/browser/WalletManagerBrowser.ts +486 -0
  198. package/src/blockchain/browser/ZipkitMinterBrowser.ts +400 -0
  199. package/src/blockchain/browser/index.ts +13 -0
  200. package/src/blockchain/{ZipKitWallet.ts → core/WalletManager.ts} +241 -143
  201. package/src/blockchain/core/ZipkitMinter.ts +716 -0
  202. package/src/blockchain/{ZipKitOTS.ts → core/ZipkitOTS.ts} +28 -62
  203. package/src/blockchain/core/ZipkitVerifier.ts +853 -0
  204. package/src/blockchain/core/contracts.ts +370 -0
  205. package/src/blockchain/core/index.ts +71 -0
  206. package/src/blockchain/index.ts +21 -55
  207. package/src/blockchain/node/WalletManagerNode.ts +171 -0
  208. package/src/blockchain/node/index.ts +40 -0
  209. package/src/browser/{ZipKitBrowser.ts → ZipkitBrowser.ts} +104 -41
  210. package/src/browser/index.esm.ts +37 -0
  211. package/src/browser/index.ts +25 -0
  212. package/src/core/ZipCompress.ts +398 -0
  213. package/src/core/ZipDecompress.ts +212 -0
  214. package/src/{shared → core}/ZipEntry.ts +240 -90
  215. package/src/core/Zipkit.ts +794 -0
  216. package/src/core/components/HashCalculator.ts +384 -0
  217. package/src/core/components/Logger.ts +180 -0
  218. package/src/core/components/ProgressTracker.ts +134 -0
  219. package/src/{shared → core}/components/Util.ts +12 -34
  220. package/src/{shared → core}/constants/Errors.ts +0 -4
  221. package/src/{shared → core}/constants/Headers.ts +19 -17
  222. package/src/core/encryption/Manager.ts +137 -0
  223. package/src/core/encryption/ZipCrypto.ts +650 -0
  224. package/src/core/encryption/index.ts +15 -0
  225. package/src/core/encryption/types.ts +33 -0
  226. package/src/core/index.ts +34 -0
  227. package/src/core/version.ts +33 -0
  228. package/src/index.ts +54 -25
  229. package/src/node/ZipCompressNode.ts +632 -0
  230. package/src/node/ZipDecompressNode.ts +508 -0
  231. package/src/node/ZipkitNode.ts +1559 -0
  232. package/src/node/index.ts +69 -0
  233. package/src/types/index.ts +131 -1
  234. package/src/types/modules.d.ts +1 -11
  235. package/src/types/opentimestamps.d.ts +1 -23
  236. package/contracts/INTEGRATION.md +0 -338
  237. package/contracts/NETWORKS.md +0 -156
  238. package/contracts/NZIP-NFT-bytecode.txt +0 -1
  239. package/contracts/constants.ts +0 -952
  240. package/contracts/deploy-bytecode.mjs +0 -94
  241. package/dist/blockchain/CertificateToken.d.ts +0 -48
  242. package/dist/blockchain/CertificateToken.js +0 -386
  243. package/dist/blockchain/ZipKitMinter.js +0 -347
  244. package/dist/blockchain/ZipKitNFT.d.ts +0 -3
  245. package/dist/blockchain/ZipKitNFT.js +0 -7
  246. package/dist/blockchain/ZipKitVerifier.d.ts +0 -69
  247. package/dist/blockchain/ZipKitVerifier.js +0 -289
  248. package/dist/blockchain/ZipKitWallet.d.ts +0 -69
  249. package/dist/blockchain/contracts.d.ts +0 -14
  250. package/dist/blockchain/contracts.js +0 -77
  251. package/dist/blockchain/types.d.ts +0 -47
  252. package/dist/blockchain/types.js +0 -30
  253. package/dist/browser/ZipKitBrowser.d.ts +0 -18
  254. package/dist/browser/ZipKitStampBrowser.d.ts +0 -2
  255. package/dist/browser/ZipKitStampBrowser.js +0 -12
  256. package/dist/browser-index.d.ts +0 -18
  257. package/dist/browser-index.js +0 -57
  258. package/dist/server/ZipKitServer.d.ts +0 -74
  259. package/dist/server/ZipKitServer.js +0 -310
  260. package/dist/shared/ZipKit.d.ts +0 -243
  261. package/dist/shared/ZipKit.js +0 -772
  262. package/dist/shared/components/CertificateOTS.d.ts +0 -15
  263. package/dist/shared/components/CertificateOTS.js +0 -116
  264. package/dist/shared/components/FileSys.d.ts +0 -2
  265. package/dist/shared/components/FileSys.js +0 -113
  266. package/dist/shared/components/HashAccumulator.d.ts +0 -55
  267. package/dist/shared/components/HashAccumulator.js +0 -100
  268. package/dist/shared/components/ZipCrypto.d.ts +0 -3
  269. package/dist/shared/components/ZipCrypto.js +0 -78
  270. package/dist/shared/components/merkleTree.d.ts +0 -66
  271. package/dist/shared/components/merkleTree.js +0 -136
  272. package/dist/shared/types/index.d.ts +0 -21
  273. package/dist/shared/types/index.js +0 -3
  274. package/dist/shared/types.d.ts +0 -33
  275. package/dist/shared/types.js +0 -8
  276. package/dist/shared/version.d.ts +0 -2
  277. package/dist/shared/version.js +0 -6
  278. package/src/blockchain/CertificateToken.ts +0 -444
  279. package/src/blockchain/ZipKitMinter.ts +0 -470
  280. package/src/blockchain/ZipKitNFT.ts +0 -12
  281. package/src/blockchain/ZipKitVerifier.ts +0 -382
  282. package/src/blockchain/contracts.ts +0 -88
  283. package/src/blockchain/types.ts +0 -91
  284. package/src/browser/CertificateGeneratorBrowser.ts-remove +0 -498
  285. package/src/browser/ZipKitStampBrowser.ts +0 -9
  286. package/src/browser-index.ts +0 -36
  287. package/src/server/README.md +0 -25
  288. package/src/server/ZipKitServer.ts +0 -305
  289. package/src/shared/ZipKit.ts +0 -881
  290. package/src/shared/components/CertificateOTS.ts +0 -132
  291. package/src/shared/components/FileSys.ts +0 -87
  292. package/src/shared/components/HashAccumulator.ts +0 -113
  293. package/src/shared/components/ZipCrypto.ts +0 -91
  294. package/src/shared/components/merkleTree.ts +0 -162
  295. package/src/shared/types/index.ts +0 -25
  296. package/src/shared/types/modules.d.ts +0 -22
  297. package/src/shared/types/opentimestamps.d.ts +0 -23
  298. package/src/shared/types.d.ts +0 -8
  299. package/src/shared/types.ts +0 -43
  300. package/src/shared/version.ts +0 -3
  301. /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
+