neozip-cli 0.75.1-beta → 0.80.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 (31) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/DOCUMENTATION.md +9 -1
  3. package/README.md +22 -10
  4. package/dist/src/commands/mintTimestampProof.js +335 -0
  5. package/dist/src/commands/verifyEmail.js +146 -0
  6. package/dist/src/config/ConfigSetup.js +50 -20
  7. package/dist/src/config/ConfigStore.js +36 -3
  8. package/dist/src/index.js +1 -1
  9. package/dist/src/neolist.js +18 -10
  10. package/dist/src/neounzip.js +305 -60
  11. package/dist/src/neozip/blockchain.js +5 -5
  12. package/dist/src/neozip/createZip.js +207 -41
  13. package/dist/src/neozip/upgradeZip.js +182 -0
  14. package/dist/src/neozip.js +143 -8
  15. package/env.example +10 -0
  16. package/package.json +91 -85
  17. package/dist/neozipkit-bundles/blockchain.js +0 -13725
  18. package/dist/neozipkit-bundles/browser.js +0 -6186
  19. package/dist/neozipkit-bundles/core.js +0 -3839
  20. package/dist/neozipkit-bundles/node.js +0 -17730
  21. package/dist/neozipkit-wrappers/blockchain/core/contracts.js +0 -16
  22. package/dist/neozipkit-wrappers/blockchain/index.js +0 -2
  23. package/dist/neozipkit-wrappers/core/ZipDecompress.js +0 -2
  24. package/dist/neozipkit-wrappers/core/components/HashCalculator.js +0 -2
  25. package/dist/neozipkit-wrappers/core/components/Logger.js +0 -2
  26. package/dist/neozipkit-wrappers/core/constants/Errors.js +0 -2
  27. package/dist/neozipkit-wrappers/core/constants/Headers.js +0 -2
  28. package/dist/neozipkit-wrappers/core/encryption/ZipCrypto.js +0 -7
  29. package/dist/neozipkit-wrappers/core/index.js +0 -3
  30. package/dist/neozipkit-wrappers/index.js +0 -13
  31. package/dist/neozipkit-wrappers/node/index.js +0 -2
@@ -46,11 +46,14 @@ const fs = __importStar(require("fs"));
46
46
  const path = __importStar(require("path"));
47
47
  const neozipkit_1 = require("neozipkit");
48
48
  const node_1 = __importDefault(require("neozipkit/node"));
49
- const blockchain_1 = require("neozipkit/blockchain");
49
+ const neozip_blockchain_1 = require("neozip-blockchain");
50
+ const ots_1 = require("neozip-blockchain/ots");
51
+ const zipstamp_server_1 = require("neozip-blockchain/zipstamp-server");
50
52
  const CommentManager_1 = require("./CommentManager");
51
53
  const utils_1 = require("./utils");
52
54
  const file_operations_1 = require("./file-operations");
53
- const blockchain_2 = require("./blockchain");
55
+ const blockchain_1 = require("./blockchain");
56
+ const ConfigStore_1 = require("../config/ConfigStore");
54
57
  const user_interaction_1 = require("./user-interaction");
55
58
  const exit_codes_1 = require("../exit-codes");
56
59
  const version_1 = require("../version");
@@ -254,7 +257,7 @@ class ZipCreator {
254
257
  this.centralDirOffset = 0;
255
258
  this.zip = new node_1.default(); // Use ZipkitNode for ZIP operations
256
259
  // Initialize HashAccumulator if blockchain features are enabled
257
- if (this.options.blockchain || this.options.blockchainOts) {
260
+ if (this.options.blockchain || this.options.blockchainOts || this.options.blockchainZipstamp) {
258
261
  this.hashAccumulator = new neozipkit_1.HashCalculator({ enableAccumulation: true });
259
262
  }
260
263
  }
@@ -284,7 +287,7 @@ class ZipCreator {
284
287
  */
285
288
  async writeChunk(data) {
286
289
  // For in-memory non-blockchain mode, write to buffer
287
- if (this.options.inMemory && !this.options.blockchain && !this.options.blockchainOts) {
290
+ if (this.options.inMemory && !this.options.blockchain && !this.options.blockchainOts && !this.options.blockchainZipstamp) {
288
291
  const bufferWriter = this.zip.bufferWriter;
289
292
  if (bufferWriter) {
290
293
  // Determine if this is a header or data by checking signature
@@ -333,7 +336,7 @@ class ZipCreator {
333
336
  // Initialize output stream for file-based mode OR in-memory blockchain mode
334
337
  // For in-memory blockchain mode, we need to write directly to file during compression
335
338
  // For in-memory non-blockchain mode, output is written directly during compression
336
- if (!this.options.inMemory || (this.options.blockchain || this.options.blockchainOts)) {
339
+ if (!this.options.inMemory || (this.options.blockchain || this.options.blockchainOts || this.options.blockchainZipstamp)) {
337
340
  await this.initializeOutput();
338
341
  }
339
342
  await this.processFiles();
@@ -401,7 +404,9 @@ class ZipCreator {
401
404
  const compressionDesc = this.options.level === 0 ? 'store (no compression)' : `${this.options.compression} (level ${this.options.level})`;
402
405
  (0, utils_1.log)(`🔧 Compression: ${compressionDesc}`, this.options);
403
406
  (0, utils_1.log)(`📊 Block size: ${(0, utils_1.formatBytes)(this.options.blockSize || 64 * 1024)}`, this.options);
404
- (0, utils_1.log)(`🔗 Tokenization: ${this.options.blockchain ? 'enabled' : 'disabled'}`, this.options);
407
+ if (!this.options.blockchainZipstamp && !this.options.blockchainOts) {
408
+ (0, utils_1.log)(`🔗 Tokenization: ${this.options.blockchain ? 'enabled' : 'disabled'}`, this.options);
409
+ }
405
410
  const encryptionMethod = this.options.encryptionMethod || 'pkzip';
406
411
  (0, utils_1.log)(`🔐 Encryption: ${this.options.encrypt ? `${encryptionMethod.toUpperCase()} enabled` : 'disabled'}`, this.options);
407
412
  (0, utils_1.log)('', this.options);
@@ -433,6 +438,10 @@ class ZipCreator {
433
438
  (0, utils_1.log)(`Blockchain: enabled (OpenTimestamp)`, this.options);
434
439
  (0, utils_1.log)(`Bitcoin blockchain: enabled`, this.options);
435
440
  }
441
+ else if (this.options.blockchainZipstamp) {
442
+ (0, utils_1.log)(`Blockchain: enabled (Zipstamp)`, this.options);
443
+ (0, utils_1.log)(`Zipstamp: enabled`, this.options);
444
+ }
436
445
  else {
437
446
  (0, utils_1.log)(`Blockchain: disabled`, this.options);
438
447
  (0, utils_1.log)(`Tokenization: disabled`, this.options);
@@ -811,7 +820,7 @@ class ZipCreator {
811
820
  const compressionOptions = {
812
821
  level: level,
813
822
  useZstd: level === 0 ? false : (this.options.compression || 'zstd') === 'zstd',
814
- useSHA256: this.options.blockchain || this.options.blockchainOts || false,
823
+ useSHA256: this.options.blockchain || this.options.blockchainOts || this.options.blockchainZipstamp || false,
815
824
  password: this.options.encrypt ? this.options.password : null,
816
825
  encryptionMethod: this.options.encryptionMethod || 'pkzip'
817
826
  };
@@ -829,7 +838,7 @@ class ZipCreator {
829
838
  // Create ZipkitNode instance for buffer-based compression
830
839
  const zipkit = new node_1.default();
831
840
  // Determine if we need to write to file (for blockchain mode)
832
- const writeToFile = this.options.blockchain || this.options.blockchainOts;
841
+ const writeToFile = this.options.blockchain || this.options.blockchainOts || this.options.blockchainZipstamp;
833
842
  // Create appropriate output writer
834
843
  let writer;
835
844
  const positionRef = { current: this.currentPosition };
@@ -911,9 +920,11 @@ class ZipCreator {
911
920
  if (this.hashAccumulator && entry.sha256) {
912
921
  const filename = entry.filename || '';
913
922
  // Skip blockchain metadata files
914
- if (filename !== neozipkit_1.TOKENIZED_METADATA &&
915
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
916
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED) {
923
+ if (filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
924
+ filename !== ots_1.TIMESTAMP_METADATA &&
925
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
926
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
927
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA) {
917
928
  const hashBuffer = Buffer.from(entry.sha256, 'hex');
918
929
  this.hashAccumulator.addHash(hashBuffer);
919
930
  if (this.options.verbose) {
@@ -978,9 +989,11 @@ class ZipCreator {
978
989
  for (let index = 0; index < entryCnt; index++) {
979
990
  const entry = zipEntries[index];
980
991
  // Filter out metadata entries (handled separately in handleTokenization)
981
- if (entry.filename === neozipkit_1.TOKENIZED_METADATA ||
982
- entry.filename === neozipkit_1.TIMESTAMP_METADATA ||
983
- entry.filename === neozipkit_1.TIMESTAMP_SUBMITTED) {
992
+ if (entry.filename === neozip_blockchain_1.TOKENIZED_METADATA ||
993
+ entry.filename === ots_1.TIMESTAMP_METADATA ||
994
+ entry.filename === ots_1.TIMESTAMP_SUBMITTED ||
995
+ entry.filename === zipstamp_server_1.SUBMIT_METADATA ||
996
+ entry.filename === zipstamp_server_1.TIMESTAMP_METADATA) {
984
997
  zipEntries.splice(index, 1);
985
998
  entryCnt--;
986
999
  index--;
@@ -1059,9 +1072,11 @@ class ZipCreator {
1059
1072
  if (this.hashAccumulator) {
1060
1073
  const filename = entry.filename || '';
1061
1074
  // Skip blockchain metadata files
1062
- if (filename !== neozipkit_1.TOKENIZED_METADATA &&
1063
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
1064
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED) {
1075
+ if (filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
1076
+ filename !== ots_1.TIMESTAMP_METADATA &&
1077
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
1078
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
1079
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA) {
1065
1080
  this.hashAccumulator.addHash(hash);
1066
1081
  if (this.options.verbose) {
1067
1082
  (0, utils_1.logDebug)(`Added SHA-256 hash to accumulator for ${filename}`, this.options);
@@ -1139,9 +1154,11 @@ class ZipCreator {
1139
1154
  // Filter out blockchain metadata files to ensure consistent Merkle Root calculation
1140
1155
  const contentEntries = entries.filter(entry => {
1141
1156
  const filename = entry.filename || '';
1142
- return filename !== neozipkit_1.TOKENIZED_METADATA &&
1143
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
1144
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED;
1157
+ return filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
1158
+ filename !== ots_1.TIMESTAMP_METADATA &&
1159
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
1160
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
1161
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA;
1145
1162
  });
1146
1163
  // Add SHA-256 hashes to accumulator
1147
1164
  for (const entry of contentEntries) {
@@ -1158,12 +1175,13 @@ class ZipCreator {
1158
1175
  let tokenMeta = null;
1159
1176
  let tokenInfoEntry = null;
1160
1177
  let otsMetaEntry = null;
1178
+ let zipstampMetaEntry = null;
1161
1179
  let merkleRoot = null;
1162
1180
  if (this.options.blockchain) {
1163
1181
  // Check for wallet passkey AFTER compression is complete
1164
1182
  // Priority: -w option > wallet.json > environment variable
1165
1183
  // Don't show error yet - we'll prompt user if key is missing
1166
- const walletPasskey = (0, blockchain_2.getWalletPasskey)(this.options.walletKey, false);
1184
+ const walletPasskey = (0, blockchain_1.getWalletPasskey)(this.options.walletKey, false);
1167
1185
  if (!walletPasskey) {
1168
1186
  // Show error message and prompt user
1169
1187
  console.error('\n❌ Error: Wallet private key is required for blockchain operations');
@@ -1186,6 +1204,7 @@ class ZipCreator {
1186
1204
  (0, utils_1.log)('⚠️ Continuing without blockchain tokenization (ZIP file is already created)...', this.options);
1187
1205
  this.options.blockchain = false;
1188
1206
  this.options.blockchainOts = false;
1207
+ this.options.blockchainZipstamp = false;
1189
1208
  return; // Skip tokenization, ZIP is already complete
1190
1209
  }
1191
1210
  else {
@@ -1276,7 +1295,7 @@ class ZipCreator {
1276
1295
  // Always display network when tokenization is enabled
1277
1296
  console.log(`🌐 Network: ${network}`);
1278
1297
  // Get network config for contract address and version info
1279
- const networkConfig = (0, blockchain_1.getNetworkByName)(network);
1298
+ const networkConfig = (0, neozip_blockchain_1.getNetworkByName)(network);
1280
1299
  let contractAddress;
1281
1300
  let networkChainId;
1282
1301
  let contractVersion;
@@ -1290,7 +1309,7 @@ class ZipCreator {
1290
1309
  }
1291
1310
  else {
1292
1311
  // Fallback to default (Base Sepolia)
1293
- const defaultConfig = (0, blockchain_1.getContractConfig)(84532);
1312
+ const defaultConfig = (0, neozip_blockchain_1.getContractConfig)(84532);
1294
1313
  contractAddress = defaultConfig.address;
1295
1314
  networkChainId = defaultConfig.chainId;
1296
1315
  const versionStr = defaultConfig.version || '0';
@@ -1300,8 +1319,8 @@ class ZipCreator {
1300
1319
  // Display contract address and version right after network
1301
1320
  console.log(`📄 Contract: ${contractAddress}`);
1302
1321
  console.log(`🔢 Version: ${contractVersion}`);
1303
- if (!(0, blockchain_2.isSupportedNetwork)(network)) {
1304
- const supportedNetworks = (0, blockchain_1.getSupportedNetworkNames)();
1322
+ if (!(0, blockchain_1.isSupportedNetwork)(network)) {
1323
+ const supportedNetworks = (0, neozip_blockchain_1.getSupportedNetworkNames)();
1305
1324
  console.error(`❌ Error: Unsupported network: ${network}`);
1306
1325
  console.error(` Currently supported networks: ${supportedNetworks.join(', ')}`);
1307
1326
  (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.BLOCKCHAIN_CONFIG_ERROR);
@@ -1309,7 +1328,7 @@ class ZipCreator {
1309
1328
  let minter = null;
1310
1329
  try {
1311
1330
  // Initialize blockchain minter
1312
- minter = new neozipkit_1.ZipkitMinter(merkleRoot, {
1331
+ minter = new neozip_blockchain_1.ZipkitMinter(merkleRoot, {
1313
1332
  network: network,
1314
1333
  walletPrivateKey: this.options.walletPasskey,
1315
1334
  verbose: this.options.verbose,
@@ -1380,7 +1399,7 @@ class ZipCreator {
1380
1399
  }
1381
1400
  else if (userChoice.action === 'mint-new') {
1382
1401
  // Mint new token
1383
- const mintResult = await (0, blockchain_2.handleTokenMinting)(minter, this.zip, this.options.nonInteractive || false, 0);
1402
+ const mintResult = await (0, blockchain_1.handleTokenMinting)(minter, this.zip, this.options.nonInteractive || false, 0);
1384
1403
  if (mintResult.success && mintResult.tokenInfo) {
1385
1404
  tokenMeta = mintResult.tokenInfo;
1386
1405
  // Add token metadata directly to ZIP (after compression, at the end)
@@ -1417,7 +1436,7 @@ class ZipCreator {
1417
1436
  }
1418
1437
  else {
1419
1438
  // No existing tokens, mint new one
1420
- const mintResult = await (0, blockchain_2.handleTokenMinting)(minter, this.zip, this.options.nonInteractive || false, 0);
1439
+ const mintResult = await (0, blockchain_1.handleTokenMinting)(minter, this.zip, this.options.nonInteractive || false, 0);
1421
1440
  if (mintResult.success && mintResult.tokenInfo) {
1422
1441
  tokenMeta = mintResult.tokenInfo;
1423
1442
  // Add token metadata directly to ZIP (after compression, at the end)
@@ -1537,16 +1556,16 @@ class ZipCreator {
1537
1556
  console.log(`📋 Merkle Root: ${merkleRoot}`);
1538
1557
  // Create actual OpenTimestamp proof
1539
1558
  try {
1540
- const otsProof = await (0, neozipkit_1.createTimestamp)(merkleRoot, { debug: this.options.verbose });
1559
+ const otsProof = await (0, ots_1.createTimestamp)(merkleRoot, { debug: this.options.verbose });
1541
1560
  if (!otsProof) {
1542
1561
  throw new Error('Failed to create OpenTimestamp proof');
1543
1562
  }
1544
1563
  console.log('📄 OpenTimestamp metadata prepared');
1545
1564
  // Use the createOtsMetadataEntry function like in the example
1546
- const metaEntry = (0, blockchain_1.createOtsMetadataEntry)(this.zip, otsProof);
1565
+ const metaEntry = (0, ots_1.createOtsMetadataEntry)(this.zip, otsProof);
1547
1566
  if (metaEntry) {
1548
1567
  // Ensure the filename is set correctly
1549
- metaEntry.filename = neozipkit_1.TIMESTAMP_SUBMITTED;
1568
+ metaEntry.filename = ots_1.TIMESTAMP_SUBMITTED;
1550
1569
  // Ensure OTS entry uses STORED compression (no compression for small metadata file)
1551
1570
  metaEntry.cmpMethod = 0; // STORED
1552
1571
  metaEntry.compressedSize = metaEntry.fileBuffer?.length || metaEntry.uncompressedSize || 0;
@@ -1586,10 +1605,143 @@ class ZipCreator {
1586
1605
  }
1587
1606
  }
1588
1607
  }
1608
+ else if (this.options.blockchainZipstamp) {
1609
+ // Handle Zipstamp timestamp for blockchain
1610
+ if (this.options.debug) {
1611
+ (0, utils_1.logDebug)('Zipstamp process starting...', this.options);
1612
+ }
1613
+ console.log('🔗 Zipstamp: Creating timestamp proof on blockchain...');
1614
+ // Calculate merkle root (reuse same logic as OTS)
1615
+ let merkleRoot = null;
1616
+ if (this.hashAccumulator && this.hashAccumulator.leafCount() > 0) {
1617
+ merkleRoot = this.hashAccumulator.merkleRoot();
1618
+ if (this.options.verbose) {
1619
+ console.log(`✅ Calculated merkle root from ${this.hashAccumulator.leafCount()} accumulated SHA-256 hashes`);
1620
+ }
1621
+ }
1622
+ else {
1623
+ let entriesForMerkle = [];
1624
+ const zipEntries = this.zip.getDirectory();
1625
+ if (zipEntries.length > 0) {
1626
+ entriesForMerkle = zipEntries;
1627
+ }
1628
+ else {
1629
+ try {
1630
+ if (fs.existsSync(this.archiveName)) {
1631
+ const zipkit = new node_1.default();
1632
+ if (this.options.inMemory) {
1633
+ const zipBuffer = fs.readFileSync(this.archiveName);
1634
+ zipkit.loadZip(zipBuffer);
1635
+ }
1636
+ else {
1637
+ await zipkit.loadZipFile(this.archiveName);
1638
+ }
1639
+ entriesForMerkle = zipkit.getDirectory() || [];
1640
+ }
1641
+ else {
1642
+ entriesForMerkle = await this.zip.getDirectory() || [];
1643
+ }
1644
+ }
1645
+ catch (error) {
1646
+ console.error('❌ Error loading ZIP file for merkle root calculation:', error instanceof Error ? error.message : String(error));
1647
+ entriesForMerkle = await this.zip.getDirectory() || [];
1648
+ }
1649
+ }
1650
+ merkleRoot = this.calculateMerkleRootFromEntries(entriesForMerkle);
1651
+ }
1652
+ if (!merkleRoot) {
1653
+ console.error('❌ Error calculating merkle root for Zipstamp');
1654
+ console.error(' Make sure files have been compressed with SHA-256 hashes.');
1655
+ if (this.options.verbose) {
1656
+ const hashCount = this.hashAccumulator ? this.hashAccumulator.leafCount() : 0;
1657
+ console.error(` Found ${hashCount} accumulated SHA-256 hashes`);
1658
+ }
1659
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT);
1660
+ }
1661
+ if (this.options.verbose && this.hashAccumulator) {
1662
+ console.log(`✅ Calculated merkle root from ${this.hashAccumulator.leafCount()} accumulated SHA-256 hashes`);
1663
+ }
1664
+ if (merkleRoot) {
1665
+ console.log(`📋 Merkle Root: ${merkleRoot}`);
1666
+ const recipientEmail = this.options.timestampEmail ||
1667
+ process.env.NEOZIP_TIMESTAMP_EMAIL ||
1668
+ ConfigStore_1.ConfigStore.getConfig().timestampEmail;
1669
+ console.log('🔗 Submitting hash digest to ZipStamp server...');
1670
+ console.log(` Zipstamp Server: ${(0, zipstamp_server_1.getZipStampServerUrl)()}`);
1671
+ console.log(`📧 Email: ${recipientEmail || '(none set)'}`);
1672
+ try {
1673
+ const zipstampMetadata = await (0, zipstamp_server_1.createTimestamp)(merkleRoot, {
1674
+ recipientEmail: recipientEmail || undefined,
1675
+ debug: this.options.verbose
1676
+ });
1677
+ if (!zipstampMetadata) {
1678
+ throw new Error('Failed to create Zipstamp timestamp');
1679
+ }
1680
+ console.log('📄 Zipstamp metadata prepared');
1681
+ const metaEntry = (0, zipstamp_server_1.createTimestampMetadataEntry)(this.zip, zipstampMetadata);
1682
+ if (metaEntry) {
1683
+ // createTimestampMetadataEntry sets filename based on status (TS-SUBMIT.NZIP or TIMESTAMP.NZIP)
1684
+ metaEntry.cmpMethod = 0; // STORED
1685
+ metaEntry.compressedSize = metaEntry.fileBuffer?.length || metaEntry.uncompressedSize || 0;
1686
+ if (this.options.verbose) {
1687
+ console.log(`📂 Final timestamp entry: ${metaEntry.filename}`);
1688
+ console.log(`📏 Final timestamp entry size: ${metaEntry.fileBuffer?.length || metaEntry.uncompressedSize || 0} bytes`);
1689
+ }
1690
+ console.log('✅ Zipstamp proof created and queued for inclusion');
1691
+ const isConfirmed = (metaEntry.filename || '').includes('TIMESTAMP') && !(metaEntry.filename || '').includes('TS-SUBMIT');
1692
+ console.log(isConfirmed
1693
+ ? ' Timestamp proof is recorded on the blockchain.'
1694
+ : ' Timestamp proof will be recorded on the blockchain once confirmed.');
1695
+ metaEntry.localHdrOffset = this.currentPosition;
1696
+ if (this.zipWriter) {
1697
+ this.zipWriter.entryPositions.set(metaEntry.filename, this.currentPosition);
1698
+ }
1699
+ const localHdr = metaEntry.createLocalHdr();
1700
+ if (this.options.verbose) {
1701
+ (0, utils_1.log)(`📝 Writing Zipstamp entry: ${metaEntry.filename} at offset ${this.currentPosition}`, this.options);
1702
+ (0, utils_1.log)(`📊 Zipstamp entry size: ${metaEntry.fileBuffer?.length || 0} bytes`, this.options);
1703
+ }
1704
+ await this.writeChunk(localHdr);
1705
+ if (metaEntry.fileBuffer) {
1706
+ await this.writeChunk(metaEntry.fileBuffer);
1707
+ }
1708
+ zipstampMetaEntry = metaEntry;
1709
+ }
1710
+ else {
1711
+ console.warn('⚠️ Warning: Failed to create Zipstamp metadata entry');
1712
+ }
1713
+ }
1714
+ catch (error) {
1715
+ const errorMessage = error instanceof Error ? error.message : String(error);
1716
+ console.error('❌ Failed to create Zipstamp proof:', errorMessage);
1717
+ const isValidationOrEmailError = errorMessage.toLowerCase().includes('validation') ||
1718
+ errorMessage.toLowerCase().includes('email') ||
1719
+ errorMessage.toLowerCase().includes('verify');
1720
+ if (isValidationOrEmailError) {
1721
+ console.error('');
1722
+ if (recipientEmail) {
1723
+ console.error(` Email used: ${recipientEmail}`);
1724
+ console.error('');
1725
+ }
1726
+ else {
1727
+ console.error(' No email was set (use --timestamp-email, NEOZIP_TIMESTAMP_EMAIL, or neozip verify-email).');
1728
+ console.error('');
1729
+ }
1730
+ console.error('💡 Zipstamp requires a verified email. Run:');
1731
+ console.error(' neozip verify-email');
1732
+ console.error('');
1733
+ console.error(' Or use: --timestamp-email <your-verified-email>');
1734
+ console.error(' Or set: NEOZIP_TIMESTAMP_EMAIL environment variable');
1735
+ console.error('');
1736
+ }
1737
+ throw error;
1738
+ }
1739
+ }
1740
+ }
1589
1741
  }
1590
1742
  async finalize() {
1591
1743
  // Check if this is in-memory non-blockchain mode (needs special handling)
1592
- if (this.options.inMemory && !this.options.blockchain && !this.options.blockchainOts) {
1744
+ if (this.options.inMemory && !this.options.blockchain && !this.options.blockchainOts && !this.options.blockchainZipstamp) {
1593
1745
  // For in-memory non-blockchain mode, write complete ZIP file from buffer
1594
1746
  // Metadata should already be in the buffer (added via writeChunk() in handleTokenization())
1595
1747
  const bufferWriter = this.zip.bufferWriter;
@@ -1752,6 +1904,9 @@ async function testArchiveIntegrity(archiveName, options) {
1752
1904
  try {
1753
1905
  // Always show when integrity testing starts
1754
1906
  (0, utils_1.log)(`\n🧪 Testing archive integrity: ${archiveName}`, options);
1907
+ if (options.blockchainZipstamp) {
1908
+ (0, utils_1.log)(` (Validating file integrity only; timestamp not yet confirmed on blockchain)`, options);
1909
+ }
1755
1910
  // Import child_process for shelling out
1756
1911
  const { spawn } = require('child_process');
1757
1912
  // Use the installed neounzip command directly (works in both dev and installed)
@@ -1760,6 +1915,11 @@ async function testArchiveIntegrity(archiveName, options) {
1760
1915
  return new Promise((resolve) => {
1761
1916
  // Build neounzip command arguments
1762
1917
  const args = ['-t'];
1918
+ // For Zipstamp timestamped files: skip blockchain verification during integrity test.
1919
+ // We only validate file integrity (SHA-256); timestamp is pending and not yet minted.
1920
+ if (options.blockchainZipstamp) {
1921
+ args.push('--skip-blockchain');
1922
+ }
1763
1923
  // Add password if encryption is enabled
1764
1924
  if (options.encrypt && options.password) {
1765
1925
  args.push('-P', options.password);
@@ -1806,6 +1966,10 @@ async function testArchiveIntegrity(archiveName, options) {
1806
1966
  else {
1807
1967
  (0, utils_1.log)(`✅ Archive integrity test passed`, options);
1808
1968
  }
1969
+ // For Zipstamp: clarify that timestamp is not validated and not minted
1970
+ if (options.blockchainZipstamp) {
1971
+ (0, utils_1.log)(` Note: File integrity validated. Timestamp is pending (not yet confirmed on blockchain).`, options);
1972
+ }
1809
1973
  resolve(true);
1810
1974
  }
1811
1975
  else {
@@ -1910,7 +2074,7 @@ async function upgradeZipForTokenization(inputZipPath, options) {
1910
2074
  }
1911
2075
  (0, utils_1.log)(` Found ${entries.length} entries in ZIP`, options);
1912
2076
  // Check for existing token metadata (ignore per requirement 3a)
1913
- const hasTokenMetadata = entries.some((e) => e.filename === neozipkit_1.TOKENIZED_METADATA);
2077
+ const hasTokenMetadata = entries.some((e) => e.filename === neozip_blockchain_1.TOKENIZED_METADATA);
1914
2078
  if (hasTokenMetadata && options.verbose) {
1915
2079
  (0, utils_1.log)(` Note: ZIP already contains token metadata (will create new token)`, options);
1916
2080
  }
@@ -1922,9 +2086,11 @@ async function upgradeZipForTokenization(inputZipPath, options) {
1922
2086
  for (const entry of entries) {
1923
2087
  const filename = entry.filename || '';
1924
2088
  // Skip metadata entries and directories
1925
- if (filename === neozipkit_1.TOKENIZED_METADATA ||
1926
- filename === neozipkit_1.TIMESTAMP_METADATA ||
1927
- filename === neozipkit_1.TIMESTAMP_SUBMITTED ||
2089
+ if (filename === neozip_blockchain_1.TOKENIZED_METADATA ||
2090
+ filename === ots_1.TIMESTAMP_METADATA ||
2091
+ filename === ots_1.TIMESTAMP_SUBMITTED ||
2092
+ filename === zipstamp_server_1.SUBMIT_METADATA ||
2093
+ filename === zipstamp_server_1.TIMESTAMP_METADATA ||
1928
2094
  entry.isDirectory) {
1929
2095
  continue;
1930
2096
  }
@@ -2080,7 +2246,7 @@ async function upgradeZipForTokenization(inputZipPath, options) {
2080
2246
  }
2081
2247
  // Mint new token before finalizing ZIP (so we can add metadata)
2082
2248
  // Check for wallet passkey - don't show error yet
2083
- const walletPasskey = (0, blockchain_2.getWalletPasskey)(options.walletKey, false);
2249
+ const walletPasskey = (0, blockchain_1.getWalletPasskey)(options.walletKey, false);
2084
2250
  if (!walletPasskey) {
2085
2251
  // Show error message and prompt user
2086
2252
  console.error('❌ Error: Wallet private key is required for blockchain operations');
@@ -2118,10 +2284,10 @@ async function upgradeZipForTokenization(inputZipPath, options) {
2118
2284
  if (walletPasskey) {
2119
2285
  (0, utils_1.log)(` Minting new token on blockchain...`, options);
2120
2286
  const network = options.network || 'base-sepolia';
2121
- if (!(0, blockchain_2.isSupportedNetwork)(network)) {
2287
+ if (!(0, blockchain_1.isSupportedNetwork)(network)) {
2122
2288
  (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.BLOCKCHAIN_CONFIG_ERROR, `Error: Unsupported network: ${network}`);
2123
2289
  }
2124
- const minter = new neozipkit_1.ZipkitMinter(merkleRoot, {
2290
+ const minter = new neozip_blockchain_1.ZipkitMinter(merkleRoot, {
2125
2291
  network,
2126
2292
  walletPrivateKey: walletPasskey,
2127
2293
  verbose: options.verbose,
@@ -2131,12 +2297,12 @@ async function upgradeZipForTokenization(inputZipPath, options) {
2131
2297
  // Create a temporary ZipkitNode instance for handleTokenMinting
2132
2298
  // (it needs a zip instance, but we'll add metadata manually)
2133
2299
  const tempZip = new node_1.default();
2134
- const mintResult = await (0, blockchain_2.handleTokenMinting)(minter, tempZip, options.nonInteractive || false, 0);
2300
+ const mintResult = await (0, blockchain_1.handleTokenMinting)(minter, tempZip, options.nonInteractive || false, 0);
2135
2301
  if (mintResult.success && mintResult.tokenInfo) {
2136
2302
  // Add token metadata entry before central directory
2137
2303
  const tokenContent = JSON.stringify(mintResult.tokenInfo, null, 2);
2138
2304
  const tokenBuffer = Buffer.from(tokenContent, 'utf8');
2139
- const tokenEntry = new neozipkit_1.ZipEntry(neozipkit_1.TOKENIZED_METADATA, null, false);
2305
+ const tokenEntry = new neozipkit_1.ZipEntry(neozip_blockchain_1.TOKENIZED_METADATA, null, false);
2140
2306
  tokenEntry.timeDateDOS = tokenEntry.setDateTime(new Date());
2141
2307
  tokenEntry.compressedSize = tokenBuffer.length;
2142
2308
  tokenEntry.uncompressedSize = tokenBuffer.length;
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ /**
3
+ * Upgrade Zipstamp timestamp from pending (TS-SUBMIT.NZIP) to confirmed (TIMESTAMP.NZIP)
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.upgradeZipForTimestamp = upgradeZipForTimestamp;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const os = __importStar(require("os"));
46
+ const node_1 = __importDefault(require("neozipkit/node"));
47
+ const node_2 = require("neozipkit/node");
48
+ const zipstamp_server_1 = require("neozip-blockchain/zipstamp-server");
49
+ const exit_codes_1 = require("../exit-codes");
50
+ /**
51
+ * Extract timestamp metadata from a ZIP entry.
52
+ * Tries the library's extractTimestampData first; if it fails (e.g. file-based ZIP),
53
+ * fall back to extracting to a temp file and parsing JSON.
54
+ */
55
+ async function extractTimestampMetadata(zip, entry) {
56
+ let metadata = await (0, zipstamp_server_1.extractTimestampData)(zip, entry);
57
+ if (metadata)
58
+ return metadata;
59
+ // Fallback for file-based ZIPs where zip.extract() may not work
60
+ try {
61
+ const tmpPath = path.join(os.tmpdir(), `neozip-upgrade-meta-${Date.now()}.json`);
62
+ await zip.extractToFile(entry, tmpPath, { skipHashCheck: true });
63
+ try {
64
+ const buf = fs.readFileSync(tmpPath);
65
+ return JSON.parse(buf.toString('utf-8'));
66
+ }
67
+ finally {
68
+ if (fs.existsSync(tmpPath))
69
+ fs.unlinkSync(tmpPath);
70
+ }
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ /**
77
+ * Upgrade a ZIP file with pending Zipstamp timestamp to confirmed status.
78
+ * Replaces TS-SUBMIT.NZIP with TIMESTAMP.NZIP when the batch is confirmed on the blockchain.
79
+ */
80
+ async function upgradeZipForTimestamp(inputPath, outputPath, options = {}) {
81
+ const { wait = false, verbose = false, debug = false } = options;
82
+ if (!fs.existsSync(inputPath)) {
83
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.CANT_FIND_ARCHIVE, `Error: ZIP file not found: ${inputPath}`);
84
+ }
85
+ const zip = new node_1.default();
86
+ await zip.loadZipFile(inputPath);
87
+ const entries = zip.getDirectory() || [];
88
+ const metadataResult = (0, zipstamp_server_1.findMetadataEntry)(entries);
89
+ if (!metadataResult) {
90
+ console.log('No Zipstamp timestamp found in ZIP file');
91
+ return;
92
+ }
93
+ if (metadataResult.type === 'confirmed') {
94
+ console.log('✓ Timestamp already confirmed - no upgrade needed');
95
+ return;
96
+ }
97
+ const metadata = await extractTimestampMetadata(zip, metadataResult.entry);
98
+ if (!metadata || !metadata.digest) {
99
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT, 'Error: Failed to extract timestamp metadata');
100
+ }
101
+ if (!(0, zipstamp_server_1.shouldUpgrade)(metadata, metadataResult.type)) {
102
+ console.log('✓ Timestamp does not need upgrade');
103
+ return;
104
+ }
105
+ const digest = metadata.digest;
106
+ const chainId = metadata.chainId && metadata.chainId > 0 ? metadata.chainId : undefined;
107
+ const batchId = metadata.batchId ?? undefined;
108
+ let verified = null;
109
+ if (wait) {
110
+ if (verbose || debug)
111
+ console.log('Polling for confirmation (--wait)...');
112
+ verified = await (0, zipstamp_server_1.pollForConfirmation)(digest, chainId, batchId, 300000, 5000, { debug });
113
+ }
114
+ else {
115
+ const verifyRes = await (0, zipstamp_server_1.verifyDigest)(digest, chainId, batchId, undefined, { debug });
116
+ if (verifyRes.verified) {
117
+ verified = verifyRes;
118
+ }
119
+ }
120
+ if (!verified || !verified.verified) {
121
+ console.log('⏳ Timestamp not yet confirmed on blockchain');
122
+ console.log(' Run with --wait to poll until confirmed, or try again later');
123
+ return;
124
+ }
125
+ console.log('✓ Timestamp confirmed on blockchain');
126
+ if (verified.transactionHash && (verbose || debug)) {
127
+ console.log(` Transaction: ${verified.transactionHash}`);
128
+ }
129
+ const confirmedMetadata = {
130
+ ...metadata,
131
+ status: 'confirmed',
132
+ merkleProof: verified.merkleProof ?? metadata.merkleProof,
133
+ merkleRoot: verified.merkleRoot ?? metadata.merkleRoot,
134
+ transactionHash: verified.transactionHash ?? metadata.transactionHash,
135
+ blockNumber: verified.blockNumber ?? metadata.blockNumber,
136
+ timestamp: verified.timestamp ?? metadata.timestamp
137
+ };
138
+ const metadataFiles = (0, zipstamp_server_1.getMetadataFileNames)();
139
+ const outPath = outputPath ?? inputPath.replace(/(\.nzip|\.zip)$/i, '-upgrade$1');
140
+ const tempDir = path.dirname(outPath) || '.';
141
+ const tempPath = path.join(tempDir, `.neozip-upgrade-${Date.now()}.tmp`);
142
+ try {
143
+ const zipCopy = new node_2.ZipCopyNode(new node_1.default());
144
+ const { destPath, dataEndOffset, copiedEntries } = await zipCopy.copyZipEntriesOnly(inputPath, tempPath, {
145
+ entryFilter: (entry) => !metadataFiles.includes(entry.filename || '')
146
+ });
147
+ const newEntry = (0, zipstamp_server_1.createTimestampMetadataEntry)(zip, confirmedMetadata);
148
+ if (!newEntry) {
149
+ throw new Error('Failed to create timestamp metadata entry');
150
+ }
151
+ newEntry.localHdrOffset = dataEndOffset;
152
+ newEntry.cmpMethod = 0;
153
+ newEntry.compressedSize = newEntry.fileBuffer?.length ?? newEntry.uncompressedSize ?? 0;
154
+ const localHdr = newEntry.createLocalHdr();
155
+ const data = newEntry.fileBuffer ?? Buffer.alloc(0);
156
+ const fd = fs.openSync(destPath, 'r+');
157
+ try {
158
+ fs.writeSync(fd, localHdr, 0, localHdr.length, dataEndOffset);
159
+ fs.writeSync(fd, data, 0, data.length, dataEndOffset + localHdr.length);
160
+ }
161
+ finally {
162
+ fs.closeSync(fd);
163
+ }
164
+ const allEntries = [...copiedEntries, newEntry];
165
+ zipCopy.writeCentralDirectoryAndEOCD(destPath, allEntries, { zipComment: '' });
166
+ fs.renameSync(destPath, outPath);
167
+ console.log(`✓ Upgraded ZIP written to: ${outPath}`);
168
+ }
169
+ catch (error) {
170
+ if (fs.existsSync(tempPath)) {
171
+ try {
172
+ fs.unlinkSync(tempPath);
173
+ }
174
+ catch {
175
+ // Ignore cleanup errors
176
+ }
177
+ }
178
+ const msg = error instanceof Error ? error.message : String(error);
179
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.CANT_WRITE_ARCHIVE, `Error upgrading ZIP: ${msg}`);
180
+ }
181
+ }
182
+ //# sourceMappingURL=upgradeZip.js.map