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,632 @@
1
+ // ======================================
2
+ // ZipCompressNode.ts - Node.js File-Based Compression
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
+ import { CompressOptions } from '../core/ZipCompress';
17
+ import ZipEntry from '../core/ZipEntry';
18
+ import ZipkitNode from './ZipkitNode';
19
+ import { Logger } from '../core/components/Logger';
20
+ import { CMP_METHOD, GP_FLAG, ENCRYPT_HDR_SIZE } from '../core/constants/Headers';
21
+ import { HashCalculator } from '../core/components/HashCalculator';
22
+ import { ZipCrypto } from '../core/encryption/ZipCrypto';
23
+ import { ZstdInit, ZstdSimple } from '@oneidentity/zstd-js';
24
+ import Errors from '../core/constants/Errors';
25
+ import * as fs from 'fs';
26
+ import * as path from 'path';
27
+
28
+ const pako = require('pako');
29
+
30
+ // Re-export types from ZipCompress (from core module)
31
+ export { CompressOptions } from '../core/ZipCompress';
32
+
33
+ // We'll initialize zstd when needed
34
+ let zstdCodec: { ZstdSimple: typeof ZstdSimple } | null = null;
35
+
36
+ // Async initialization function for zstd
37
+ async function initZstd(): Promise<{ ZstdSimple: typeof ZstdSimple }> {
38
+ if (!zstdCodec) {
39
+ zstdCodec = await ZstdInit();
40
+ }
41
+ return zstdCodec;
42
+ }
43
+
44
+ /**
45
+ * ZipCompressNode - Node.js file-based compression operations
46
+ *
47
+ * Independent compression implementation for Node.js environments.
48
+ * All compression logic is implemented directly without delegating to ZipCompress.
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * const zipkitNode = new ZipkitNode();
53
+ * const compressNode = new ZipCompressNode(zipkitNode);
54
+ * const compressed = await compressNode.compressFile('/path/to/file.txt', entry);
55
+ * ```
56
+ */
57
+ export class ZipCompressNode {
58
+ private zipkitNode: ZipkitNode;
59
+
60
+ // Class-level logging control - set to true to enable logging
61
+ private static loggingEnabled: boolean = false;
62
+
63
+ /**
64
+ * Internal logging method - only logs if class logging is enabled
65
+ */
66
+ private log(...args: any[]): void {
67
+ if (ZipCompressNode.loggingEnabled) {
68
+ Logger.debug(`[ZipCompressNode]`, ...args);
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Creates a new ZipCompressNode instance
74
+ * @param zipkitNode - ZipkitNode instance to use for ZIP operations
75
+ */
76
+ constructor(zipkitNode: ZipkitNode) {
77
+ this.zipkitNode = zipkitNode;
78
+ // If logging is enabled, ensure Logger level is set to debug
79
+ if (ZipCompressNode.loggingEnabled) {
80
+ Logger.setLevel('debug');
81
+ }
82
+ }
83
+
84
+ // ============================================================================
85
+ // Compression Methods
86
+ // ============================================================================
87
+
88
+ /**
89
+ * Compress data for a ZIP entry (Buffer-based only)
90
+ * @param entry - ZIP entry to compress
91
+ * @param data - Buffer containing data to compress
92
+ * @param options - Compression options
93
+ * @param onOutputBuffer - Optional callback for streaming output
94
+ * @returns Buffer containing compressed data
95
+ */
96
+ async compressData(entry: ZipEntry, data: Buffer, options?: CompressOptions, onOutputBuffer?: (data: Buffer) => Promise<void>): Promise<Buffer> {
97
+ // Set uncompressed size if not already set
98
+ if (!entry.uncompressedSize || entry.uncompressedSize === 0) {
99
+ entry.uncompressedSize = data.length;
100
+ }
101
+ const totalSize = data.length;
102
+ const bufferSize = options?.bufferSize || this.zipkitNode.getBufferSize();
103
+
104
+ // Determine compression method
105
+ let compressionMethod: number;
106
+
107
+ if (options?.level === 0) {
108
+ compressionMethod = CMP_METHOD.STORED;
109
+ } else if (options?.useZstd) {
110
+ // ZSTD fallback to STORED if file too small
111
+ if (totalSize < 100) {
112
+ compressionMethod = CMP_METHOD.STORED;
113
+ } else {
114
+ compressionMethod = CMP_METHOD.ZSTD;
115
+ }
116
+ } else {
117
+ compressionMethod = CMP_METHOD.DEFLATED;
118
+ }
119
+
120
+ entry.cmpMethod = compressionMethod;
121
+
122
+ // Initialize hash calculator
123
+ const needsHashCalculation = (!entry.crc || entry.crc === 0) || (options?.useSHA256 && !entry.sha256);
124
+ const hashCalculator = needsHashCalculation ? new HashCalculator({ useSHA256: options?.useSHA256 && !entry.sha256 || false }) : null;
125
+
126
+ // Calculate hashes if needed
127
+ let buffer: Buffer = Buffer.alloc(0);
128
+
129
+ if (hashCalculator) {
130
+ hashCalculator.update(data);
131
+ if (!entry.crc || entry.crc === 0) {
132
+ entry.crc = hashCalculator.finalizeCRC32();
133
+ }
134
+ if (options?.useSHA256 && !entry.sha256) {
135
+ entry.sha256 = hashCalculator.finalizeSHA256();
136
+ }
137
+ }
138
+
139
+ // Compress based on method
140
+ if (compressionMethod === CMP_METHOD.STORED) {
141
+ buffer = data;
142
+ entry.compressedSize = data.length;
143
+ } else if (compressionMethod === CMP_METHOD.ZSTD) {
144
+ buffer = await this.zstdCompress(data, options, bufferSize, entry, onOutputBuffer);
145
+ } else {
146
+ // DEFLATED
147
+ buffer = await this.deflateCompress(data, options, bufferSize, entry, onOutputBuffer);
148
+ }
149
+
150
+ // Only set compressed size if it hasn't been set already
151
+ if (entry.compressedSize === undefined || entry.compressedSize === 0) {
152
+ entry.compressedSize = buffer.length;
153
+ }
154
+
155
+ // Apply encryption if password is provided
156
+ if (options?.password && buffer.length > 0) {
157
+ buffer = this.encryptCompressedData(buffer, entry, options.password);
158
+ }
159
+
160
+ return buffer;
161
+ }
162
+
163
+
164
+ /**
165
+ * Compresses data using deflate algorithm with chunked processing
166
+ * @param data - Data to compress (Buffer or chunked reader)
167
+ * @param options - Compression options
168
+ * @param bufferSize - Size of buffer to read (default: 512KB)
169
+ * @param entry - Optional ZIP entry for hash calculation
170
+ * @param onOutputBuffer - Optional callback for streaming output
171
+ * @returns Compressed data buffer
172
+ */
173
+ async deflateCompress(
174
+ data: Buffer | { totalSize: number, onReadChunk: (position: number, size: number) => Buffer, onOutChunk: (chunk: Buffer) => void },
175
+ options?: CompressOptions,
176
+ bufferSize?: number,
177
+ entry?: ZipEntry,
178
+ onOutputBuffer?: (data: Buffer) => Promise<void>
179
+ ): Promise<Buffer> {
180
+ const effectiveBufferSize = bufferSize || options?.bufferSize || this.zipkitNode.getBufferSize();
181
+
182
+ // Initialize hash calculator for incremental hash calculation during chunk reads
183
+ const needsHashCalculation = entry && ((!entry.crc || entry.crc === 0) || (options?.useSHA256 && !entry.sha256));
184
+ const hashCalculator = needsHashCalculation ? new HashCalculator({ useSHA256: options?.useSHA256 && !entry.sha256 || false }) : null;
185
+
186
+ if (options?.level === 0) {
187
+ // Store without compression
188
+ if (Buffer.isBuffer(data)) {
189
+ // For buffer, calculate hashes if needed
190
+ if (hashCalculator && entry) {
191
+ hashCalculator.update(data);
192
+ if (!entry.crc || entry.crc === 0) {
193
+ entry.crc = hashCalculator.finalizeCRC32();
194
+ }
195
+ if (options?.useSHA256 && !entry.sha256) {
196
+ entry.sha256 = hashCalculator.finalizeSHA256();
197
+ }
198
+ }
199
+ return data;
200
+ } else {
201
+ // For chunked reader, process in chunks and call onOutputBuffer
202
+ let position = 0;
203
+ let totalProcessed = 0;
204
+
205
+ while (position < data.totalSize) {
206
+ const readSize = Math.min(effectiveBufferSize, data.totalSize - position);
207
+ const chunk = data.onReadChunk(position, readSize);
208
+
209
+ // [READ] -> [HASH] -> [OUTPUT] sequence
210
+ if (hashCalculator) {
211
+ hashCalculator.update(chunk);
212
+ }
213
+
214
+ if (onOutputBuffer) {
215
+ await onOutputBuffer(chunk);
216
+ }
217
+
218
+ totalProcessed += chunk.length;
219
+ position += chunk.length;
220
+ }
221
+
222
+ // Finalize hashes
223
+ if (hashCalculator && entry) {
224
+ if (!entry.crc || entry.crc === 0) {
225
+ entry.crc = hashCalculator.finalizeCRC32();
226
+ }
227
+ if (options?.useSHA256 && !entry.sha256) {
228
+ entry.sha256 = hashCalculator.finalizeSHA256();
229
+ }
230
+ }
231
+
232
+ return Buffer.alloc(0);
233
+ }
234
+ }
235
+
236
+ try {
237
+ const level = options?.level as 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | undefined;
238
+ const isBuffer = Buffer.isBuffer(data);
239
+ const totalSize = isBuffer ? data.length : data.totalSize;
240
+ const compressedChunks: Buffer[] = [];
241
+ let totalProcessed = 0;
242
+ let totalCompressedSize = 0;
243
+ let position = 0;
244
+
245
+ if (isBuffer) {
246
+ // Buffer-based processing
247
+ if (hashCalculator && entry) {
248
+ hashCalculator.update(data);
249
+ // Always set CRC if hash calculator was used (it calculated the correct CRC)
250
+ entry.crc = hashCalculator.finalizeCRC32();
251
+ if (options?.useSHA256 && !entry.sha256) {
252
+ entry.sha256 = hashCalculator.finalizeSHA256();
253
+ }
254
+ }
255
+
256
+ const result = pako.deflateRaw(data, { level: level ?? 6 });
257
+ const compressed = Buffer.from(result.buffer, result.byteOffset, result.byteLength);
258
+
259
+ if (onOutputBuffer) {
260
+ await onOutputBuffer(compressed);
261
+ }
262
+
263
+ return compressed;
264
+ } else {
265
+ // Chunked reader processing - use streaming deflator to maintain state across chunks
266
+ const deflator = new pako.Deflate({ level: level ?? 6, raw: true });
267
+ const compressedChunks: Buffer[] = [];
268
+ let resultOffset = 0; // Track how much of deflator.result we've already processed
269
+
270
+ while (position < totalSize) {
271
+ const readSize = Math.min(effectiveBufferSize, totalSize - position);
272
+ const chunk = data.onReadChunk(position, readSize);
273
+ const isLast = position + readSize >= totalSize;
274
+
275
+ // [READ] -> [HASH] sequence
276
+ if (hashCalculator) {
277
+ hashCalculator.update(chunk);
278
+ }
279
+
280
+ // Push chunk to streaming deflator (maintains state across chunks)
281
+ deflator.push(chunk, isLast);
282
+
283
+ // Collect compressed chunks from deflator
284
+ // deflator.result accumulates compressed data, so we need to process only new data
285
+ if (deflator.result && deflator.result.length > resultOffset) {
286
+ const newCompressed = Buffer.from(deflator.result.subarray(resultOffset));
287
+ compressedChunks.push(newCompressed);
288
+ totalCompressedSize += newCompressed.length;
289
+ resultOffset = deflator.result.length;
290
+
291
+ if (onOutputBuffer) {
292
+ await onOutputBuffer(newCompressed);
293
+ }
294
+ }
295
+
296
+ totalProcessed += chunk.length;
297
+ position += chunk.length;
298
+ }
299
+
300
+ // Finalize hashes after all chunks processed and compressed
301
+ if (hashCalculator && entry) {
302
+ // Always set CRC if hash calculator was used (it calculated the correct CRC)
303
+ entry.crc = hashCalculator.finalizeCRC32();
304
+ if (options?.useSHA256 && !entry.sha256) {
305
+ entry.sha256 = hashCalculator.finalizeSHA256();
306
+ }
307
+ }
308
+
309
+ // For chunked processing, return empty buffer (data already written via onOutputBuffer)
310
+ if (entry) {
311
+ entry.compressedSize = totalCompressedSize;
312
+ }
313
+ return Buffer.alloc(0);
314
+ }
315
+ } catch (e) {
316
+ Logger.error('Error during chunked deflate compression:', e);
317
+ throw new Error(Errors.COMPRESSION_ERROR);
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Compresses data using deflate algorithm (legacy method for small buffers)
323
+ * @param inbuf - Data to compress
324
+ * @param options - Compression options
325
+ * @returns Compressed data buffer
326
+ */
327
+ deflate(inbuf: Buffer, options?: CompressOptions): Buffer {
328
+ if (options?.level == 0) {
329
+ return inbuf; // Store without compression
330
+ }
331
+ try {
332
+ const level = options?.level as 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | undefined;
333
+ const result = pako.deflateRaw(inbuf, {
334
+ level: level ?? 6
335
+ });
336
+ return Buffer.from(result.buffer, result.byteOffset, result.byteLength);
337
+ } catch (e) {
338
+ Logger.error('Error during compression:', e);
339
+ throw new Error(Errors.COMPRESSION_ERROR);
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Compresses data using Zstandard (zstd) algorithm
345
+ * @param input - Buffer to compress OR chunked reader object with totalSize and readChunk callback
346
+ * @param options - Compression options
347
+ * @param bufferSize - Size of buffer to read if using chunked reader (default: 512KB)
348
+ * @param entry - Optional ZIP entry for hash calculation
349
+ * @param onOutputBuffer - Optional callback for streaming output
350
+ * @returns Compressed data buffer
351
+ */
352
+ async zstdCompress(
353
+ input: Buffer | { totalSize: number, readChunk: (position: number, size: number) => Buffer },
354
+ options?: CompressOptions,
355
+ bufferSize?: number,
356
+ entry?: ZipEntry,
357
+ onOutputBuffer?: (data: Buffer) => Promise<void>
358
+ ): Promise<Buffer> {
359
+ const effectiveBufferSize = bufferSize || options?.bufferSize || this.zipkitNode.getBufferSize();
360
+ const isBuffer = Buffer.isBuffer(input);
361
+ const totalSize = isBuffer ? input.length : input.totalSize;
362
+
363
+ if (options?.level == 0) {
364
+ // For store mode, return as-is
365
+ if (isBuffer) {
366
+ return input;
367
+ } else {
368
+ // Read all chunks
369
+ const chunks: Buffer[] = [];
370
+ let position = 0;
371
+ while (position < totalSize) {
372
+ const size = Math.min(effectiveBufferSize, totalSize - position);
373
+ const chunk = input.readChunk(position, size);
374
+ chunks.push(chunk);
375
+ position += size;
376
+ }
377
+ return Buffer.concat(chunks);
378
+ }
379
+ }
380
+
381
+ try {
382
+ // Ensure zstd is initialized
383
+ const codec = await initZstd();
384
+
385
+ // Zstd compression levels range from 1 (fastest) to 22 (highest compression)
386
+ // Map our 1-9 level to a reasonable zstd range (1-19)
387
+ const level = options?.level ?? 6;
388
+ const zstdLevel = Math.min(Math.max(1, Math.floor(level * 2.1)), 19);
389
+
390
+ // Get the full buffer (zstd doesn't support true streaming compression)
391
+ const inbuf = isBuffer ? input : (() => {
392
+ const chunks: Buffer[] = [];
393
+ let position = 0;
394
+ while (position < totalSize) {
395
+ const size = Math.min(effectiveBufferSize, totalSize - position);
396
+ const chunk = input.readChunk(position, size);
397
+ chunks.push(chunk);
398
+ position += size;
399
+ }
400
+ return Buffer.concat(chunks);
401
+ })();
402
+
403
+ // Validate input
404
+ if (!inbuf || inbuf.length === 0) {
405
+ throw new Error('ZSTD compression: empty input buffer');
406
+ }
407
+
408
+ // Convert Buffer to Uint8Array for WASM module (same fix as decompression)
409
+ const inputArray = new Uint8Array(inbuf.buffer, inbuf.byteOffset, inbuf.byteLength);
410
+
411
+ // Compress the data with zstd
412
+ const compressedData = codec.ZstdSimple.compress(inputArray, zstdLevel);
413
+ const compressedBuffer = Buffer.from(compressedData);
414
+
415
+ // Set the compressed size in the entry for ZIP file structure
416
+ if (entry) {
417
+ entry.compressedSize = compressedBuffer.length;
418
+ }
419
+
420
+ if (onOutputBuffer) {
421
+ await onOutputBuffer(compressedBuffer);
422
+ }
423
+
424
+ return compressedBuffer;
425
+ } catch (e) {
426
+ Logger.error('Error during zstd compression:', e);
427
+ throw new Error(Errors.COMPRESSION_ERROR);
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Encrypt compressed data using PKZIP encryption
433
+ * Creates encryption header, encrypts compressed data, and updates entry flags
434
+ * @param compressedData - Compressed data to encrypt
435
+ * @param entry - ZIP entry to encrypt
436
+ * @param password - Password for encryption
437
+ * @returns Encrypted buffer (encrypted header + encrypted compressed data)
438
+ */
439
+ private encryptCompressedData(compressedData: Buffer, entry: ZipEntry, password: string): Buffer {
440
+ // Create ZipCrypto instance
441
+ const zipCrypto = new ZipCrypto();
442
+
443
+ // Encrypt the compressed data (includes header creation and encryption)
444
+ const encryptedData = zipCrypto.encryptBuffer(entry, compressedData, password);
445
+
446
+ // Set encryption flags on entry
447
+ entry.isEncrypted = true;
448
+ entry.bitFlags |= GP_FLAG.ENCRYPTED;
449
+
450
+ // Update compressed size to include encryption header (12 bytes)
451
+ entry.compressedSize = encryptedData.length;
452
+
453
+ return encryptedData;
454
+ }
455
+
456
+ // ============================================================================
457
+ // File-Based Compression Methods
458
+ // ============================================================================
459
+
460
+ /**
461
+ * Compress a file from disk
462
+ *
463
+ * Reads file from disk, sets entry metadata from file stats, and compresses the data.
464
+ *
465
+ * @param filePath - Path to the file to compress
466
+ * @param entry - ZIP entry to compress (filename should already be set)
467
+ * @param options - Optional compression options
468
+ * @returns Promise resolving to Buffer containing compressed data
469
+ * @throws Error if file not found or not a file
470
+ */
471
+ async compressFile(
472
+ filePath: string,
473
+ entry: ZipEntry,
474
+ options?: CompressOptions
475
+ ): Promise<Buffer> {
476
+ // Validate file exists
477
+ if (!fs.existsSync(filePath)) {
478
+ throw new Error(`File not found: ${filePath}`);
479
+ }
480
+
481
+ const stats = fs.statSync(filePath);
482
+ if (!stats.isFile()) {
483
+ throw new Error(`Path is not a file: ${filePath}`);
484
+ }
485
+
486
+ // Set entry metadata from file stats
487
+ entry.uncompressedSize = stats.size;
488
+ entry.timeDateDOS = entry.setDateTime(stats.mtime);
489
+
490
+ // Read file data
491
+ const fileData = fs.readFileSync(filePath);
492
+
493
+ // Compress the buffer using compressData (buffer-based compression)
494
+ return await this.compressData(entry, fileData, options);
495
+ }
496
+
497
+ /**
498
+ * Compress a file from disk using streaming for large files
499
+ *
500
+ * Streams file in chunks for memory-efficient compression of large files.
501
+ * All chunk reading logic is handled in this server class.
502
+ *
503
+ * @param filePath - Path to the file to compress
504
+ * @param entry - ZIP entry to compress (filename should already be set)
505
+ * @param options - Optional compression options
506
+ * @param onOutputBuffer - Optional callback for streaming output
507
+ * @returns Promise resolving to Buffer containing compressed data
508
+ * @throws Error if file not found or not a file
509
+ */
510
+ async compressFileStream(
511
+ filePath: string,
512
+ entry: ZipEntry,
513
+ options?: CompressOptions,
514
+ onOutputBuffer?: (data: Buffer) => Promise<void>
515
+ ): Promise<Buffer> {
516
+ // Validate file exists
517
+ if (!fs.existsSync(filePath)) {
518
+ throw new Error(`File not found: ${filePath}`);
519
+ }
520
+
521
+ const stats = fs.statSync(filePath);
522
+ if (!stats.isFile()) {
523
+ throw new Error(`Path is not a file: ${filePath}`);
524
+ }
525
+
526
+ // Set entry metadata from file stats
527
+ entry.uncompressedSize = stats.size;
528
+ entry.timeDateDOS = entry.setDateTime(stats.mtime);
529
+
530
+ // Determine buffer size for chunked reading
531
+ const bufferSize = options?.bufferSize || this.zipkitNode.getBufferSize();
532
+
533
+ // Create chunked reader for streaming compression
534
+ // All chunk reading logic is in this server class
535
+ const chunkedReader = {
536
+ totalSize: stats.size,
537
+ onReadChunk: (position: number, size: number): Buffer => {
538
+ const fd = fs.openSync(filePath, 'r');
539
+ try {
540
+ const buffer = Buffer.alloc(size);
541
+ const bytesRead = fs.readSync(fd, buffer, 0, size, position);
542
+ return buffer.subarray(0, bytesRead);
543
+ } finally {
544
+ fs.closeSync(fd);
545
+ }
546
+ },
547
+ onOutChunk: (chunk: Buffer): void => {
548
+ // Output chunks are accumulated in compression methods
549
+ }
550
+ };
551
+
552
+ // Determine compression method and call appropriate method
553
+ const compressionMethod = options?.level === 0 ? 'STORED' :
554
+ options?.useZstd ? 'ZSTD' : 'DEFLATED';
555
+
556
+ // Set entry compression method before compression
557
+ if (compressionMethod === 'STORED') {
558
+ entry.cmpMethod = CMP_METHOD.STORED;
559
+ } else if (compressionMethod === 'ZSTD') {
560
+ entry.cmpMethod = CMP_METHOD.ZSTD;
561
+ } else {
562
+ entry.cmpMethod = CMP_METHOD.DEFLATED;
563
+ }
564
+
565
+ if (compressionMethod === 'STORED') {
566
+ // For STORED, read file and pass as buffer
567
+ const fileData = fs.readFileSync(filePath);
568
+ return await this.compressData(entry, fileData, options, onOutputBuffer);
569
+ } else if (compressionMethod === 'ZSTD') {
570
+ // ZSTD requires full buffer, so read file first
571
+ const fileData = fs.readFileSync(filePath);
572
+ return await this.compressData(entry, fileData, options, onOutputBuffer);
573
+ } else {
574
+ // DEFLATED: Use deflateCompress with chunked reader
575
+ return await this.deflateCompress(chunkedReader, options, bufferSize, entry, onOutputBuffer);
576
+ }
577
+ }
578
+
579
+ /**
580
+ * Compress multiple files from disk to a ZIP file
581
+ *
582
+ * Batch compression from file paths. Creates entries for each file and writes
583
+ * to output ZIP file. This is a simplified implementation - full implementation
584
+ * would need to write ZIP structure incrementally.
585
+ *
586
+ * @param filePaths - Array of file paths to compress
587
+ * @param outputPath - Path where the ZIP file should be created
588
+ * @param options - Optional compression options
589
+ * @returns Promise that resolves when ZIP creation is complete
590
+ * @throws Error if any file not found
591
+ */
592
+ async compressFiles(
593
+ filePaths: string[],
594
+ outputPath: string,
595
+ options?: CompressOptions
596
+ ): Promise<void> {
597
+ // This is a placeholder for future implementation
598
+ // Full implementation would need to:
599
+ // 1. Create ZIP file structure
600
+ // 2. Write local headers and compressed data for each file
601
+ // 3. Write central directory
602
+ // 4. Write end of central directory record
603
+ // For now, this is a simplified version that compresses files but doesn't write ZIP structure
604
+
605
+ const entries: ZipEntry[] = [];
606
+ const compressedData: Buffer[] = [];
607
+
608
+ for (const filePath of filePaths) {
609
+ if (!fs.existsSync(filePath)) {
610
+ throw new Error(`File not found: ${filePath}`);
611
+ }
612
+
613
+ const stats = fs.statSync(filePath);
614
+ if (!stats.isFile()) {
615
+ continue; // Skip directories
616
+ }
617
+
618
+ // Create entry
619
+ const entryName = path.relative(process.cwd(), filePath) || path.basename(filePath);
620
+ const entry = this.zipkitNode.createZipEntry(entryName);
621
+
622
+ // Compress file
623
+ const compressed = await this.compressFile(filePath, entry, options);
624
+ entries.push(entry);
625
+ compressedData.push(compressed);
626
+ }
627
+
628
+ // For now, this is a placeholder
629
+ // Full implementation would write ZIP structure to outputPath
630
+ throw new Error('compressFiles() - Full implementation pending. Use neozip CLI for now.');
631
+ }
632
+ }