@neoware_inc/neozipkit 0.5.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 (171) hide show
  1. package/README.md +134 -0
  2. package/dist/browser/ZipkitBrowser.d.ts +27 -0
  3. package/dist/browser/ZipkitBrowser.d.ts.map +1 -0
  4. package/dist/browser/ZipkitBrowser.js +303 -0
  5. package/dist/browser/ZipkitBrowser.js.map +1 -0
  6. package/dist/browser/index.d.ts +9 -0
  7. package/dist/browser/index.d.ts.map +1 -0
  8. package/dist/browser/index.esm.d.ts +12 -0
  9. package/dist/browser/index.esm.d.ts.map +1 -0
  10. package/dist/browser/index.esm.js +46 -0
  11. package/dist/browser/index.esm.js.map +1 -0
  12. package/dist/browser/index.js +38 -0
  13. package/dist/browser/index.js.map +1 -0
  14. package/dist/browser-esm/index.d.ts +9 -0
  15. package/dist/browser-esm/index.js +50211 -0
  16. package/dist/browser-esm/index.js.map +7 -0
  17. package/dist/browser-umd/index.d.ts +9 -0
  18. package/dist/browser-umd/index.js +50221 -0
  19. package/dist/browser-umd/index.js.map +7 -0
  20. package/dist/browser-umd/index.min.js +39 -0
  21. package/dist/browser.d.ts +9 -0
  22. package/dist/browser.js +38 -0
  23. package/dist/core/ZipCompress.d.ts +99 -0
  24. package/dist/core/ZipCompress.d.ts.map +1 -0
  25. package/dist/core/ZipCompress.js +287 -0
  26. package/dist/core/ZipCompress.js.map +1 -0
  27. package/dist/core/ZipCopy.d.ts +175 -0
  28. package/dist/core/ZipCopy.d.ts.map +1 -0
  29. package/dist/core/ZipCopy.js +310 -0
  30. package/dist/core/ZipCopy.js.map +1 -0
  31. package/dist/core/ZipDecompress.d.ts +57 -0
  32. package/dist/core/ZipDecompress.d.ts.map +1 -0
  33. package/dist/core/ZipDecompress.js +155 -0
  34. package/dist/core/ZipDecompress.js.map +1 -0
  35. package/dist/core/ZipEntry.d.ts +138 -0
  36. package/dist/core/ZipEntry.d.ts.map +1 -0
  37. package/dist/core/ZipEntry.js +829 -0
  38. package/dist/core/ZipEntry.js.map +1 -0
  39. package/dist/core/Zipkit.d.ts +315 -0
  40. package/dist/core/Zipkit.d.ts.map +1 -0
  41. package/dist/core/Zipkit.js +647 -0
  42. package/dist/core/Zipkit.js.map +1 -0
  43. package/dist/core/ZstdManager.d.ts +56 -0
  44. package/dist/core/ZstdManager.d.ts.map +1 -0
  45. package/dist/core/ZstdManager.js +144 -0
  46. package/dist/core/ZstdManager.js.map +1 -0
  47. package/dist/core/components/HashCalculator.d.ts +138 -0
  48. package/dist/core/components/HashCalculator.d.ts.map +1 -0
  49. package/dist/core/components/HashCalculator.js +360 -0
  50. package/dist/core/components/HashCalculator.js.map +1 -0
  51. package/dist/core/components/Logger.d.ts +73 -0
  52. package/dist/core/components/Logger.d.ts.map +1 -0
  53. package/dist/core/components/Logger.js +156 -0
  54. package/dist/core/components/Logger.js.map +1 -0
  55. package/dist/core/components/ProgressTracker.d.ts +43 -0
  56. package/dist/core/components/ProgressTracker.d.ts.map +1 -0
  57. package/dist/core/components/ProgressTracker.js +112 -0
  58. package/dist/core/components/ProgressTracker.js.map +1 -0
  59. package/dist/core/components/Support.d.ts +64 -0
  60. package/dist/core/components/Support.d.ts.map +1 -0
  61. package/dist/core/components/Support.js +71 -0
  62. package/dist/core/components/Support.js.map +1 -0
  63. package/dist/core/components/Util.d.ts +26 -0
  64. package/dist/core/components/Util.d.ts.map +1 -0
  65. package/dist/core/components/Util.js +95 -0
  66. package/dist/core/components/Util.js.map +1 -0
  67. package/dist/core/constants/Errors.d.ts +52 -0
  68. package/dist/core/constants/Errors.d.ts.map +1 -0
  69. package/dist/core/constants/Errors.js +67 -0
  70. package/dist/core/constants/Errors.js.map +1 -0
  71. package/dist/core/constants/Headers.d.ts +170 -0
  72. package/dist/core/constants/Headers.d.ts.map +1 -0
  73. package/dist/core/constants/Headers.js +194 -0
  74. package/dist/core/constants/Headers.js.map +1 -0
  75. package/dist/core/encryption/Manager.d.ts +58 -0
  76. package/dist/core/encryption/Manager.d.ts.map +1 -0
  77. package/dist/core/encryption/Manager.js +121 -0
  78. package/dist/core/encryption/Manager.js.map +1 -0
  79. package/dist/core/encryption/ZipCrypto.d.ts +172 -0
  80. package/dist/core/encryption/ZipCrypto.d.ts.map +1 -0
  81. package/dist/core/encryption/ZipCrypto.js +554 -0
  82. package/dist/core/encryption/ZipCrypto.js.map +1 -0
  83. package/dist/core/encryption/index.d.ts +9 -0
  84. package/dist/core/encryption/index.d.ts.map +1 -0
  85. package/dist/core/encryption/index.js +17 -0
  86. package/dist/core/encryption/index.js.map +1 -0
  87. package/dist/core/encryption/types.d.ts +29 -0
  88. package/dist/core/encryption/types.d.ts.map +1 -0
  89. package/dist/core/encryption/types.js +12 -0
  90. package/dist/core/encryption/types.js.map +1 -0
  91. package/dist/core/index.d.ts +27 -0
  92. package/dist/core/index.d.ts.map +1 -0
  93. package/dist/core/index.js +59 -0
  94. package/dist/core/index.js.map +1 -0
  95. package/dist/core/version.d.ts +5 -0
  96. package/dist/core/version.d.ts.map +1 -0
  97. package/dist/core/version.js +31 -0
  98. package/dist/core/version.js.map +1 -0
  99. package/dist/index.d.ts +9 -0
  100. package/dist/index.d.ts.map +1 -0
  101. package/dist/index.js +38 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/node/ZipCompressNode.d.ts +123 -0
  104. package/dist/node/ZipCompressNode.d.ts.map +1 -0
  105. package/dist/node/ZipCompressNode.js +565 -0
  106. package/dist/node/ZipCompressNode.js.map +1 -0
  107. package/dist/node/ZipCopyNode.d.ts +165 -0
  108. package/dist/node/ZipCopyNode.d.ts.map +1 -0
  109. package/dist/node/ZipCopyNode.js +347 -0
  110. package/dist/node/ZipCopyNode.js.map +1 -0
  111. package/dist/node/ZipDecompressNode.d.ts +197 -0
  112. package/dist/node/ZipDecompressNode.d.ts.map +1 -0
  113. package/dist/node/ZipDecompressNode.js +678 -0
  114. package/dist/node/ZipDecompressNode.js.map +1 -0
  115. package/dist/node/ZipkitNode.d.ts +466 -0
  116. package/dist/node/ZipkitNode.d.ts.map +1 -0
  117. package/dist/node/ZipkitNode.js +1426 -0
  118. package/dist/node/ZipkitNode.js.map +1 -0
  119. package/dist/node/index.d.ts +25 -0
  120. package/dist/node/index.d.ts.map +1 -0
  121. package/dist/node/index.js +54 -0
  122. package/dist/node/index.js.map +1 -0
  123. package/dist/types/index.d.ts +45 -0
  124. package/dist/types/index.d.ts.map +1 -0
  125. package/dist/types/index.js +11 -0
  126. package/dist/types/index.js.map +1 -0
  127. package/examples/README.md +261 -0
  128. package/examples/append-data.json +44 -0
  129. package/examples/copy-zip-append.ts +139 -0
  130. package/examples/copy-zip.ts +152 -0
  131. package/examples/create-zip.ts +172 -0
  132. package/examples/extract-zip.ts +118 -0
  133. package/examples/list-zip.ts +161 -0
  134. package/examples/test-files/data.json +116 -0
  135. package/examples/test-files/document.md +80 -0
  136. package/examples/test-files/document.txt +6 -0
  137. package/examples/test-files/file1.txt +48 -0
  138. package/examples/test-files/file2.txt +80 -0
  139. package/examples/tsconfig.json +44 -0
  140. package/package.json +167 -0
  141. package/src/browser/ZipkitBrowser.ts +305 -0
  142. package/src/browser/index.esm.ts +32 -0
  143. package/src/browser/index.ts +19 -0
  144. package/src/core/ZipCompress.ts +370 -0
  145. package/src/core/ZipCopy.ts +434 -0
  146. package/src/core/ZipDecompress.ts +191 -0
  147. package/src/core/ZipEntry.ts +917 -0
  148. package/src/core/Zipkit.ts +794 -0
  149. package/src/core/ZstdManager.ts +165 -0
  150. package/src/core/components/HashCalculator.ts +384 -0
  151. package/src/core/components/Logger.ts +180 -0
  152. package/src/core/components/ProgressTracker.ts +134 -0
  153. package/src/core/components/Support.ts +77 -0
  154. package/src/core/components/Util.ts +91 -0
  155. package/src/core/constants/Errors.ts +78 -0
  156. package/src/core/constants/Headers.ts +205 -0
  157. package/src/core/encryption/Manager.ts +137 -0
  158. package/src/core/encryption/ZipCrypto.ts +650 -0
  159. package/src/core/encryption/index.ts +15 -0
  160. package/src/core/encryption/types.ts +33 -0
  161. package/src/core/index.ts +42 -0
  162. package/src/core/version.ts +33 -0
  163. package/src/index.ts +19 -0
  164. package/src/node/ZipCompressNode.ts +618 -0
  165. package/src/node/ZipCopyNode.ts +437 -0
  166. package/src/node/ZipDecompressNode.ts +793 -0
  167. package/src/node/ZipkitNode.ts +1706 -0
  168. package/src/node/index.ts +40 -0
  169. package/src/types/index.ts +68 -0
  170. package/src/types/modules.d.ts +22 -0
  171. package/src/types/opentimestamps.d.ts +1 -0
@@ -0,0 +1,829 @@
1
+ "use strict";
2
+ // ======================================
3
+ // ZipEntry.ts
4
+ // Copyright (c) 2025 NeoWare, Inc. All rights reserved.
5
+ // ======================================
6
+ // Zip Directory Item class
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.ZipEntry = void 0;
12
+ const Errors_1 = __importDefault(require("./constants/Errors"));
13
+ const Headers_1 = require("./constants/Headers");
14
+ const Logger_1 = require("./components/Logger");
15
+ const ZipCrypto_1 = require("./encryption/ZipCrypto");
16
+ const VER_ENCODING = 30;
17
+ const VER_EXTRACT = 10; // Version needed to extract (1.0)
18
+ /**
19
+ * Class representing a single entry (file or directory) within a ZIP archive
20
+ */
21
+ class ZipEntry {
22
+ /**
23
+ * Creates a new ZIP entry
24
+ * @param fname - Name of the file within the ZIP
25
+ * @param comment - Optional comment for this entry
26
+ * @param debug - Enable debug logging
27
+ */
28
+ constructor(fname, comment, debug) {
29
+ this.debug = true;
30
+ this.verMadeBy = 0; // Read Version Made By
31
+ this.verExtract = 0; // Read Version Needed to Extract
32
+ this.bitFlags = 0; // General purpose bit flag
33
+ this.cmpMethod = 0; // Compression method
34
+ this.timeDateDOS = 0; // DOS File Time(2 bytes) & Date(2 bytes)
35
+ this.crc = 0; // CRC-32
36
+ this.compressedSize = 0; // Compressed size
37
+ this.uncompressedSize = 0; // Uncompressed size
38
+ this.volNumber = 0; // Disk number start
39
+ this.intFileAttr = 0; // Internal file attributes
40
+ this.extFileAttr = 0; // External file attributes
41
+ this.localHdrOffset = 0; // Relative offset to local header from File/Disk start
42
+ this.filename = ''; // File name
43
+ this.extraField = null; // Extra field
44
+ this.comment = null; // Entry comment
45
+ // File Data
46
+ this.fileBuffer = null; // File Data Buffer
47
+ // Zip Compressed Data
48
+ this.cmpData = null; // Compressed Data Buffer
49
+ this.isEncrypted = false; // Zip Entry is encrypted
50
+ this.isStrongEncrypt = false; // Zip Entry is strong encrypted
51
+ this.encryptHdr = null; // Encrypted Header (12 bytes)
52
+ this.lastModTimeDate = 0; // Data Descriptor File Time & Date
53
+ this.decrypt = null; // Decrypt Class Function
54
+ this.isUpdated = true; // Entry has been updated
55
+ this.isDirectory = false; // Entry is a directory
56
+ this.isMetaData = false; // Entry is Zip MetaData
57
+ // Platform specific data
58
+ this.platform = null; // Platform
59
+ this.universalTime = null; // Universal Time
60
+ this.uid = null; // User ID
61
+ this.gid = null; // Group ID
62
+ this.sha256 = null; // SHA-256 hash of the file
63
+ // Symbolic link data
64
+ this.isSymlink = false; // Entry is a symbolic link
65
+ this.linkTarget = null; // Target path for symbolic links
66
+ // Hard link data
67
+ this.isHardLink = false; // Entry is a hard link
68
+ this.originalEntry = null; // Original entry name for hard links
69
+ this.inode = null; // Inode number for hard links
70
+ this.isMSDOS = this.platform === null;
71
+ this.isUnixLike = this.platform != null && this.platform !== 'win32';
72
+ this.isMacOS = this.platform != null && this.platform === 'darwin';
73
+ this.isLinux = this.platform != null && this.platform === 'linux';
74
+ this.VER_MADE_BY = (() => {
75
+ switch (this.platform) {
76
+ case 'darwin':
77
+ return (Headers_1.FILE_SYSTEM.DARWIN << 8) | VER_ENCODING; // macOS/Darwin
78
+ case 'win32':
79
+ return (Headers_1.FILE_SYSTEM.NTFS << 8) | VER_ENCODING; // Windows
80
+ default:
81
+ return (Headers_1.FILE_SYSTEM.UNIX << 8) | VER_ENCODING; // Unix/Linux
82
+ }
83
+ })();
84
+ this.filename = fname || '';
85
+ this.comment = comment || null;
86
+ this.debug = debug || false;
87
+ // Set the UTF-8 Language Encoding Flag (EFS) to indicate UTF-8 encoding for filenames
88
+ this.bitFlags |= Headers_1.GP_FLAG.EFS;
89
+ }
90
+ /**
91
+ * Reads ZIP entry data from a central directory buffer
92
+ * @param data - Buffer containing central directory entry data
93
+ * @returns Buffer positioned at start of next entry
94
+ * @throws Error if central directory entry is invalid
95
+ */
96
+ readZipEntry(data) {
97
+ // Check if buffer is too small before trying to read from it
98
+ if (data.length < Headers_1.CENTRAL_DIR.SIZE) {
99
+ throw new Error('Zip entry data is too small or corrupt');
100
+ }
101
+ // Verify this is a Central Directory Header
102
+ // data should be 46 bytes and start with "PK 01 02"
103
+ if (data.readUInt32LE(0) !== Headers_1.CENTRAL_DIR.SIGNATURE) {
104
+ throw new Error(Errors_1.default.INVALID_CEN);
105
+ }
106
+ // Read Zip version made by
107
+ this.verMadeBy = data.readUInt16LE(Headers_1.CENTRAL_DIR.VER_MADE);
108
+ // Read Zip version needed to extract
109
+ this.verExtract = data.readUInt16LE(Headers_1.CENTRAL_DIR.VER_EXT);
110
+ // encrypt, decrypt flags
111
+ this.bitFlags = data.readUInt16LE(Headers_1.CENTRAL_DIR.FLAGS);
112
+ // Test if Zip Entry is encrypted
113
+ if ((this.bitFlags & Headers_1.GP_FLAG.ENCRYPTED) != 0) {
114
+ this.isEncrypted = true;
115
+ if ((this.bitFlags & Headers_1.GP_FLAG.STRONG_ENCRYPT) != 0)
116
+ this.isStrongEncrypt = true;
117
+ }
118
+ // compression method
119
+ this.cmpMethod = data.readUInt16LE(Headers_1.CENTRAL_DIR.CMP_METHOD);
120
+ // modification time (2 bytes time, 2 bytes date)
121
+ this.timeDateDOS = data.readUInt32LE(Headers_1.CENTRAL_DIR.TIMEDATE_DOS);
122
+ // uncompressed file crc-32 value
123
+ this.crc = data.readUInt32LE(Headers_1.CENTRAL_DIR.CRC);
124
+ // compressed size
125
+ this.compressedSize = data.readUInt32LE(Headers_1.CENTRAL_DIR.CMP_SIZE);
126
+ // uncompressed size
127
+ this.uncompressedSize = data.readUInt32LE(Headers_1.CENTRAL_DIR.UNCMP_SIZE);
128
+ // volume number start
129
+ this.volNumber = data.readUInt16LE(Headers_1.CENTRAL_DIR.DISK_NUM);
130
+ // internal file attributes
131
+ this.intFileAttr = data.readUInt16LE(Headers_1.CENTRAL_DIR.INT_FILE_ATTR);
132
+ // external file attributes
133
+ this.extFileAttr = data.readUInt32LE(Headers_1.CENTRAL_DIR.EXT_FILE_ATTR);
134
+ if (this.extFileAttr & Headers_1.DOS_FILE_ATTR.DIRECTORY)
135
+ this.isDirectory = true;
136
+ // LOC header offset
137
+ this.localHdrOffset = data.readUInt32LE(Headers_1.CENTRAL_DIR.LOCAL_HDR_OFFSET);
138
+ // Filename Length - 2 bytes
139
+ let fnameLen = data.readUInt16LE(Headers_1.CENTRAL_DIR.FNAME_LEN);
140
+ const filename = data.toString('utf8', Headers_1.CENTRAL_DIR.SIZE, Headers_1.CENTRAL_DIR.SIZE + fnameLen);
141
+ this.filename = filename;
142
+ if (this.filename.endsWith('/'))
143
+ this.isDirectory = true;
144
+ // Extra Field Length - 2 bytes
145
+ let extraLen = data.readUInt16LE(Headers_1.CENTRAL_DIR.EXTRA_LEN);
146
+ if (extraLen > 0) {
147
+ this.extraField = data.subarray(Headers_1.CENTRAL_DIR.SIZE + fnameLen, Headers_1.CENTRAL_DIR.SIZE + fnameLen + extraLen);
148
+ // First pass: Check for Unicode Path to ensure correct filename before processing other fields
149
+ for (let i = 0; i < extraLen;) {
150
+ let _id = this.extraField.readUInt16LE(i);
151
+ let _len = this.extraField.readUInt16LE(i + 2);
152
+ let _data = this.extraField.subarray(i + 4, i + 4 + _len);
153
+ if (_id === Headers_1.HDR_ID.UNICODE_PATH && _len >= 5) {
154
+ // Unicode Path Extra Field
155
+ const version = _data.readUInt8(0);
156
+ const nameCrc32 = _data.readUInt32LE(1);
157
+ // Calculate CRC32 of the current filename
158
+ const fnameBuf = Buffer.from(this.filename);
159
+ const calculatedCrc = (0, ZipCrypto_1.crc32)(fnameBuf);
160
+ // If CRCs match, use the UTF-8 filename from the extra field
161
+ if (calculatedCrc === nameCrc32) {
162
+ const unicodeName = _data.subarray(5).toString('utf8');
163
+ this.filename = unicodeName;
164
+ if (this.debug) {
165
+ Logger_1.Logger.log(`Using Unicode Path: ${this.filename}`);
166
+ }
167
+ }
168
+ }
169
+ i += 4 + _len;
170
+ }
171
+ // Second pass: Process all other extra fields
172
+ for (let i = 0; i < extraLen;) {
173
+ let _id = this.extraField.readUInt16LE(i);
174
+ let _len = this.extraField.readUInt16LE(i + 2);
175
+ let _data = this.extraField.subarray(i + 4, i + 4 + _len);
176
+ if (_id === Headers_1.HDR_ID.SHA256) {
177
+ if (_len === 64)
178
+ // Early versions of NeoZip used a UTF-8 encoded string
179
+ this.sha256 = _data.toString('utf8');
180
+ else
181
+ this.sha256 = _data.toString('hex');
182
+ }
183
+ else if (_id === Headers_1.HDR_ID.UNV_TIME) {
184
+ // Universal Time field has a flag byte followed by a 4-byte timestamp
185
+ if (_len >= 5) {
186
+ const flags = _data.readUInt8(0);
187
+ // Check if modification time is present (bit 0)
188
+ if (flags & 0x01) {
189
+ this.universalTime = _data.readUInt32LE(1);
190
+ }
191
+ }
192
+ }
193
+ else if (_id === Headers_1.HDR_ID.UID_GID) {
194
+ // Extract UID/GID if present
195
+ if (_len >= 5) { // Version + UID size + UID + GID size + GID
196
+ const version = _data.readUInt8(0);
197
+ const uidSize = _data.readUInt8(1);
198
+ if (uidSize <= 4 && _len >= 2 + uidSize) {
199
+ // Read UID based on its size (1-4 bytes)
200
+ this.uid = _data.readUIntLE(2, uidSize);
201
+ // Check if GID is also present
202
+ if (_len >= 2 + uidSize + 1) {
203
+ const gidSize = _data.readUInt8(2 + uidSize);
204
+ if (gidSize <= 4 && _len >= 2 + uidSize + 1 + gidSize) {
205
+ this.gid = _data.readUIntLE(2 + uidSize + 1, gidSize);
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+ else if (_id === Headers_1.HDR_ID.SYMLINK) {
212
+ // Extract symbolic link information
213
+ if (_len >= 3) { // Version + Target Length + Target
214
+ const version = _data.readUInt8(0);
215
+ const targetLength = _data.readUInt16LE(1);
216
+ if (targetLength > 0 && _len >= 3 + targetLength) {
217
+ this.isSymlink = true;
218
+ this.linkTarget = _data.subarray(3, 3 + targetLength).toString('utf8');
219
+ }
220
+ }
221
+ }
222
+ else if (_id === Headers_1.HDR_ID.HARDLINK) {
223
+ // Extract hard link information
224
+ if (_len >= 11) { // Version + Inode + Original Length + Original
225
+ const version = _data.readUInt8(0);
226
+ this.inode = _data.readUInt32LE(1);
227
+ const originalLength = _data.readUInt16LE(5);
228
+ if (originalLength > 0 && _len >= 7 + originalLength) {
229
+ this.isHardLink = true;
230
+ this.originalEntry = _data.subarray(7, 7 + originalLength).toString('utf8');
231
+ }
232
+ }
233
+ }
234
+ // Skip Unicode Path here as we already processed it
235
+ i += 4 + _len;
236
+ }
237
+ }
238
+ // File Comment Length - 2 bytes
239
+ let comLen = data.readUInt16LE(Headers_1.CENTRAL_DIR.COMMENT_LEN);
240
+ if (comLen > 0)
241
+ this.comment = data.toString('utf8', Headers_1.CENTRAL_DIR.SIZE + fnameLen, Headers_1.CENTRAL_DIR.SIZE + fnameLen + comLen);
242
+ if (this.debug)
243
+ this.showVerboseInfo();
244
+ // Calculate the Buffer for the next entry
245
+ let rawSize = Headers_1.CENTRAL_DIR.SIZE + fnameLen + extraLen + comLen;
246
+ return data.subarray(rawSize);
247
+ }
248
+ /**
249
+ * Checks if the filename contains characters that require Unicode handling
250
+ * @returns true if the filename contains non-ASCII characters or special characters
251
+ */
252
+ needsUnicodeHandling() {
253
+ // Check if filename contains non-ASCII characters or special characters like apostrophes
254
+ return /[^\x00-\x7E]|['"]/.test(this.filename);
255
+ }
256
+ /**
257
+ * Adds UTF-8 Unicode Path field to a buffer
258
+ * @param buffer - The buffer to write to
259
+ * @param offset - The offset in the buffer to start writing
260
+ * @returns The new offset after writing
261
+ */
262
+ addUnicodePathField(buffer, offset) {
263
+ // Create a UTF-8 buffer of the filename
264
+ const unicodePathBuf = Buffer.from(this.filename, 'utf8');
265
+ // Calculate CRC32 of the ASCII version of the filename
266
+ // Create an ASCII version by replacing non-ASCII chars with '?'
267
+ const asciiName = this.filename.replace(/[^\x00-\x7E]/g, '?');
268
+ const asciiNameBuf = Buffer.from(asciiName, 'ascii');
269
+ const nameCrc32 = (0, ZipCrypto_1.crc32)(asciiNameBuf);
270
+ // 1 byte version + 4 bytes CRC + filename
271
+ const unicodePathLen = 5 + unicodePathBuf.length;
272
+ // Write Unicode Path Extra Field (0x7075)
273
+ buffer.writeUInt16LE(Headers_1.HDR_ID.UNICODE_PATH, offset); // "up" header ID
274
+ buffer.writeUInt16LE(unicodePathLen, offset + 2); // data length
275
+ buffer.writeUInt8(1, offset + 4); // version (1)
276
+ buffer.writeUInt32LE(nameCrc32, offset + 5); // CRC-32 of standard filename
277
+ unicodePathBuf.copy(buffer, offset + 9); // UTF-8 version of filename
278
+ return offset + 4 + unicodePathLen; // 4 bytes for header + data length
279
+ }
280
+ /**
281
+ * Creates a local header for this ZIP entry
282
+ * @returns Buffer containing the local header data
283
+ */
284
+ createLocalHdr() {
285
+ let extraFieldLen = 0;
286
+ // Only create Unicode Path field if needed
287
+ const needsUnicode = this.needsUnicodeHandling();
288
+ if (needsUnicode) {
289
+ // 1 byte version + 4 bytes CRC + filename + 4 bytes header
290
+ const unicodeNameLen = Buffer.from(this.filename, 'utf8').length;
291
+ extraFieldLen = 5 + unicodeNameLen + 4;
292
+ }
293
+ const data = Buffer.alloc(Headers_1.LOCAL_HDR.SIZE + this.filename.length + extraFieldLen);
294
+ // "PK\003\004"
295
+ data.writeUInt32LE(Headers_1.LOCAL_HDR.SIGNATURE, 0);
296
+ // version needed to extract
297
+ data.writeUInt16LE(VER_EXTRACT, Headers_1.LOCAL_HDR.VER_EXTRACT);
298
+ // general purpose bit flag
299
+ data.writeUInt16LE(this.bitFlags >>> 0, Headers_1.LOCAL_HDR.FLAGS);
300
+ // compression method
301
+ data.writeUInt16LE(this.cmpMethod, Headers_1.LOCAL_HDR.COMPRESSION);
302
+ // modification time (2 bytes time, 2 bytes date)
303
+ data.writeUInt32LE(this.timeDateDOS >>> 0, Headers_1.LOCAL_HDR.TIMEDATE_DOS);
304
+ // uncompressed file crc-32 value
305
+ data.writeUInt32LE(this.crc, Headers_1.LOCAL_HDR.CRC);
306
+ // compressed size
307
+ data.writeUInt32LE(this.compressedSize, Headers_1.LOCAL_HDR.CMP_SIZE);
308
+ // uncompressed size
309
+ data.writeUInt32LE(this.uncompressedSize, Headers_1.LOCAL_HDR.UNCMP_SIZE);
310
+ // filename length
311
+ data.writeUInt16LE(this.filename.length, Headers_1.LOCAL_HDR.FNAME_LEN);
312
+ // extra field length
313
+ data.writeUInt16LE(extraFieldLen, Headers_1.LOCAL_HDR.EXTRA_LEN);
314
+ // Write filename - use ASCII filename (replacing non-ASCII with ?)
315
+ // This ensures compatibility with older ZIP readers
316
+ const asciiName = this.filename.replace(/[^\x00-\x7E]/g, '?');
317
+ const fnameBuf = Buffer.from(asciiName);
318
+ fnameBuf.copy(data, Headers_1.LOCAL_HDR.SIZE);
319
+ let extraOffset = Headers_1.LOCAL_HDR.SIZE + fnameBuf.length;
320
+ // Add Unicode Path Extra Field only if needed
321
+ if (needsUnicode) {
322
+ extraOffset = this.addUnicodePathField(data, extraOffset);
323
+ }
324
+ // File comments are NOT stored in local headers (ZIP specification)
325
+ // They are only stored in the central directory
326
+ return data;
327
+ }
328
+ /**
329
+ * Creates a central directory entry for this ZIP entry
330
+ * @returns Buffer containing the central directory entry data
331
+ */
332
+ centralDirEntry() {
333
+ // Calculate the length of the extra fields
334
+ const commentLen = this.comment ? Buffer.from(this.comment, 'utf8').length : 0;
335
+ const utfLen = this.universalTime ? 9 : 0; // 4 bytes header + 5 bytes data
336
+ const uidgidLen = this.uid && this.gid ? 11 + 4 : 0; // 1 byte version + 1 byte size + 4 bytes data + 1 byte size + 4 bytes data
337
+ const sha256Buf = this.sha256 ? Buffer.from(this.sha256, 'hex') : null;
338
+ const sha256Len = sha256Buf ? (sha256Buf.length + 4) : 0;
339
+ // Calculate symbolic link extra field length
340
+ const symlinkLen = this.isSymlink && this.linkTarget ?
341
+ (4 + 1 + 2 + Buffer.byteLength(this.linkTarget, 'utf8')) : 0; // 4 bytes header + 1 byte version + 2 bytes length + target
342
+ // Calculate hard link extra field length
343
+ const hardlinkLen = this.isHardLink && this.originalEntry && this.inode !== null ?
344
+ (4 + 1 + 4 + 2 + Buffer.byteLength(this.originalEntry, 'utf8')) : 0; // 4 bytes header + 1 byte version + 4 bytes inode + 2 bytes length + original
345
+ // Only add Unicode Path field if needed
346
+ const needsUnicode = this.needsUnicodeHandling();
347
+ let unicodePathLen = 0;
348
+ if (needsUnicode) {
349
+ // 1 byte version + 4 bytes CRC + filename + 4 bytes header
350
+ const unicodeNameLen = Buffer.from(this.filename, 'utf8').length;
351
+ unicodePathLen = 5 + unicodeNameLen + 4;
352
+ }
353
+ const extraLen = utfLen + sha256Len + uidgidLen + symlinkLen + hardlinkLen + (needsUnicode ? unicodePathLen : 0);
354
+ // Calculate actual filename length (ASCII conversion may change length)
355
+ const asciiName = this.filename.replace(/[^\x00-\x7E]/g, '?');
356
+ const fnameLen = asciiName.length;
357
+ // Central directory header size (46 Bytes + filename + comment + extra fields)
358
+ const data = Buffer.alloc(Headers_1.CENTRAL_DIR.SIZE + fnameLen + commentLen + extraLen);
359
+ // "PK\001\002"
360
+ data.writeUInt32LE(Headers_1.CENTRAL_DIR.SIGNATURE, 0);
361
+ // Version made by - Needs to be set for NeoZip
362
+ data.writeUInt16LE(this.isUpdated ? this.VER_MADE_BY : this.verMadeBy, Headers_1.CENTRAL_DIR.VER_MADE);
363
+ // Version needed to extract
364
+ data.writeInt16LE(this.isUpdated ? VER_EXTRACT : this.verMadeBy, Headers_1.CENTRAL_DIR.VER_EXT);
365
+ // Encrypt, Decrypt Flags
366
+ data.writeInt16LE(this.bitFlags >>> 0, Headers_1.CENTRAL_DIR.FLAGS);
367
+ // Compression method
368
+ data.writeInt16LE(this.cmpMethod, Headers_1.CENTRAL_DIR.CMP_METHOD);
369
+ // Modification time (2 bytes time, 2 bytes date)
370
+ data.writeUInt32LE(this.timeDateDOS >>> 0, Headers_1.CENTRAL_DIR.TIMEDATE_DOS);
371
+ // Uncompressed file CRC-32 value
372
+ data.writeUInt32LE(this.crc, Headers_1.CENTRAL_DIR.CRC);
373
+ // Compressed Size
374
+ data.writeUInt32LE(this.compressedSize, Headers_1.CENTRAL_DIR.CMP_SIZE);
375
+ // Uncompressed Size
376
+ data.writeUInt32LE(this.uncompressedSize, Headers_1.CENTRAL_DIR.UNCMP_SIZE);
377
+ // Filename Length
378
+ data.writeUInt16LE(this.filename.length, Headers_1.CENTRAL_DIR.FNAME_LEN);
379
+ // Extra Field Length
380
+ data.writeUInt16LE(extraLen, Headers_1.CENTRAL_DIR.EXTRA_LEN);
381
+ // File Comment Length
382
+ data.writeUInt16LE(commentLen, Headers_1.CENTRAL_DIR.COMMENT_LEN);
383
+ // Volume Number Start
384
+ data.writeUInt16LE(0, Headers_1.CENTRAL_DIR.DISK_NUM);
385
+ // Internal File Attributes
386
+ data.writeUInt16LE(this.intFileAttr >>> 0, Headers_1.CENTRAL_DIR.INT_FILE_ATTR);
387
+ // External File Attributes
388
+ data.writeUInt32LE(this.extFileAttr >>> 0, Headers_1.CENTRAL_DIR.EXT_FILE_ATTR);
389
+ // Local Header Offset
390
+ data.writeUInt32LE(this.localHdrOffset, Headers_1.CENTRAL_DIR.LOCAL_HDR_OFFSET);
391
+ // Write filename - use ASCII filename (replacing non-ASCII with ?)
392
+ // This ensures compatibility with older ZIP readers
393
+ const fnameBuf = Buffer.from(asciiName);
394
+ fnameBuf.copy(data, Headers_1.CENTRAL_DIR.SIZE);
395
+ // Add file comment immediately after filename (InfoZip format)
396
+ let currentOffset = Headers_1.CENTRAL_DIR.SIZE + fnameLen;
397
+ if (commentLen > 0 && this.comment) {
398
+ const commentBuf = Buffer.from(this.comment, 'utf8');
399
+ commentBuf.copy(data, currentOffset);
400
+ currentOffset += commentLen;
401
+ }
402
+ // Add Extra Field data after file comment
403
+ let extraOffset = currentOffset;
404
+ // Add Universal Time field
405
+ if (this.universalTime) {
406
+ data.writeUInt16LE(Headers_1.HDR_ID.UNV_TIME, extraOffset); // 0x5455
407
+ data.writeUInt16LE(5, extraOffset + 2); // Length of data (flags + time)
408
+ data.writeUInt8(1, extraOffset + 4); // Flags: modification time present
409
+ data.writeUInt32LE(Math.floor(Date.now() / 1000), extraOffset + 5); // Unix timestamp
410
+ extraOffset += 9;
411
+ }
412
+ // Add SHA-256 field
413
+ if (sha256Buf) {
414
+ data.writeUInt16LE(Headers_1.HDR_ID.SHA256, extraOffset); // 0x1f
415
+ data.writeUInt16LE(sha256Buf.length, extraOffset + 2); // Length of data
416
+ sha256Buf.copy(data, extraOffset + 4);
417
+ extraOffset += 4 + sha256Buf.length;
418
+ }
419
+ // Add UID/GID field
420
+ if (this.uid && this.gid) {
421
+ data.writeUInt16LE(Headers_1.HDR_ID.UID_GID, extraOffset); // 0x7875
422
+ data.writeUInt16LE(11, extraOffset + 2); // Length of data
423
+ data.writeUInt8(1, extraOffset + 4); // Version
424
+ data.writeUInt8(4, extraOffset + 5); // UID size
425
+ data.writeUInt32LE(this.uid, extraOffset + 6); // UID
426
+ data.writeUInt8(4, extraOffset + 10); // GID size
427
+ data.writeUInt32LE(this.gid, extraOffset + 11); // GID
428
+ extraOffset += 15;
429
+ }
430
+ // Add symbolic link field
431
+ if (this.isSymlink && this.linkTarget) {
432
+ const targetBuf = Buffer.from(this.linkTarget, 'utf8');
433
+ const dataLen = 1 + 2 + targetBuf.length; // version + length + target
434
+ data.writeUInt16LE(Headers_1.HDR_ID.SYMLINK, extraOffset); // 0x7855
435
+ data.writeUInt16LE(dataLen, extraOffset + 2); // Length of data
436
+ data.writeUInt8(1, extraOffset + 4); // Version
437
+ data.writeUInt16LE(targetBuf.length, extraOffset + 5); // Target length
438
+ targetBuf.copy(data, extraOffset + 7); // Target path
439
+ extraOffset += 4 + dataLen;
440
+ }
441
+ // Add hard link field
442
+ if (this.isHardLink && this.originalEntry && this.inode !== null) {
443
+ const originalBuf = Buffer.from(this.originalEntry, 'utf8');
444
+ const dataLen = 1 + 4 + 2 + originalBuf.length; // version + inode + length + original
445
+ data.writeUInt16LE(Headers_1.HDR_ID.HARDLINK, extraOffset); // 0x7865
446
+ data.writeUInt16LE(dataLen, extraOffset + 2); // Length of data
447
+ data.writeUInt8(1, extraOffset + 4); // Version
448
+ data.writeUInt32LE(this.inode, extraOffset + 5); // Inode number
449
+ data.writeUInt16LE(originalBuf.length, extraOffset + 9); // Original entry length
450
+ originalBuf.copy(data, extraOffset + 11); // Original entry path
451
+ extraOffset += 4 + dataLen;
452
+ }
453
+ // Add Unicode Path field if needed
454
+ if (needsUnicode) {
455
+ extraOffset = this.addUnicodePathField(data, extraOffset);
456
+ }
457
+ // File comment is already written immediately after filename
458
+ return data;
459
+ }
460
+ // ======================================
461
+ // Routines to handle the details of the Zip Entry
462
+ // ======================================
463
+ /**
464
+ * Sets the DOS date/time for this entry
465
+ * @param date - Date to convert to DOS format
466
+ * @returns number - DOS format date/time
467
+ */
468
+ setDateTime(date) {
469
+ if (!date)
470
+ return 0;
471
+ // DOS date/time format:
472
+ // Date part (16 bits): Year (7 bits) + Month (4 bits) + Day (5 bits)
473
+ // Time part (16 bits): Hour (5 bits) + Minute (6 bits) + Second (5 bits, stored as seconds/2)
474
+ const year = date.getFullYear() - 1980; // Years since 1980
475
+ const month = date.getMonth() + 1; // Month (1-12)
476
+ const day = date.getDate(); // Day (1-31)
477
+ const hour = date.getHours(); // Hour (0-23)
478
+ const minute = date.getMinutes(); // Minute (0-59)
479
+ const second = date.getSeconds(); // Second (0-59)
480
+ // Pack date: year (7 bits) + month (4 bits) + day (5 bits)
481
+ const datePart = ((year & 0x7f) << 9) | ((month & 0x0f) << 5) | (day & 0x1f);
482
+ // Pack time: hour (5 bits) + minute (6 bits) + second/2 (5 bits)
483
+ const timePart = ((hour & 0x1f) << 11) | ((minute & 0x3f) << 5) | ((second >> 1) & 0x1f);
484
+ // Combine date and time parts
485
+ const time = (datePart << 16) | timePart;
486
+ return time;
487
+ }
488
+ /**
489
+ * Converts DOS date/time to JavaScript Date
490
+ * @param timeStamp - DOS format date/time
491
+ * @returns Date object or null if timestamp is 0
492
+ */
493
+ parseDateTime(timeStamp) {
494
+ if (timeStamp == 0)
495
+ return null;
496
+ // Extract date part (upper 16 bits)
497
+ const datePart = (timeStamp >> 16) & 0xffff;
498
+ const year = ((datePart >> 9) & 0x7f) + 1980; // Year (7 bits) + 1980
499
+ const month = ((datePart >> 5) & 0x0f) - 1; // Month (4 bits) - 1 for 0-based
500
+ const day = datePart & 0x1f; // Day (5 bits)
501
+ // Extract time part (lower 16 bits)
502
+ const timePart = timeStamp & 0xffff;
503
+ const hour = (timePart >> 11) & 0x1f; // Hour (5 bits)
504
+ const minute = (timePart >> 5) & 0x3f; // Minute (6 bits)
505
+ const second = (timePart & 0x1f) << 1; // Second (5 bits) * 2
506
+ return new Date(year, month, day, hour, minute, second);
507
+ }
508
+ /**
509
+ * Formats the entry's date in local format
510
+ * @returns String in MM/DD/YYYY format or "--/--/----" if no date
511
+ */
512
+ toLocalDateString() {
513
+ let _timeDate = this.parseDateTime(this.timeDateDOS);
514
+ if (_timeDate === null)
515
+ return "--/--/----";
516
+ return _timeDate.toLocaleDateString('en-US', {
517
+ year: 'numeric',
518
+ month: '2-digit',
519
+ day: '2-digit'
520
+ }).slice(0, 10);
521
+ }
522
+ /**
523
+ * Formats the entry's time
524
+ * @returns String in HH:MM format or "--:--" if no time
525
+ */
526
+ toTimeString() {
527
+ let _timeDate = this.parseDateTime(this.timeDateDOS);
528
+ if (_timeDate === null)
529
+ return "--:--";
530
+ return _timeDate.toTimeString().slice(0, 5);
531
+ }
532
+ /**
533
+ * Formats the entry's date and time in local format
534
+ * @returns String like "Jan 01, 2024 13:45:30" or "--/--/-- --:--" if no date/time
535
+ */
536
+ toFormattedDateString() {
537
+ let _timeDate = this.parseDateTime(this.timeDateDOS);
538
+ if (_timeDate == null)
539
+ return "--/--/-- --:--";
540
+ const datePart = _timeDate.toLocaleDateString('en-US', {
541
+ year: 'numeric',
542
+ month: 'short',
543
+ day: '2-digit'
544
+ });
545
+ const timePart = _timeDate.toLocaleTimeString('en-US', {
546
+ hour: '2-digit',
547
+ minute: '2-digit',
548
+ second: '2-digit',
549
+ hour12: false
550
+ });
551
+ return `${datePart} ${timePart}`;
552
+ }
553
+ /**
554
+ * Formats the entry's date and time in UTC
555
+ * @returns String like "Jan 01, 2024 13:45:30 UTC" or "--/--/-- --:--" if no date/time
556
+ */
557
+ toFormattedUTCDateString() {
558
+ let _timeDate = this.parseDateTime(this.timeDateDOS);
559
+ if (_timeDate == null)
560
+ return "--/--/-- --:--";
561
+ const datePart = _timeDate.toLocaleDateString('en-US', {
562
+ year: 'numeric',
563
+ month: 'short',
564
+ day: '2-digit',
565
+ timeZone: 'UTC'
566
+ });
567
+ const timePart = _timeDate.toLocaleTimeString('en-US', {
568
+ hour: '2-digit',
569
+ minute: '2-digit',
570
+ second: '2-digit',
571
+ hour12: false,
572
+ timeZone: 'UTC'
573
+ });
574
+ return `${datePart} ${timePart} UTC`;
575
+ }
576
+ /**
577
+ * Converts compression method code to human-readable string
578
+ * @returns String describing the compression method
579
+ */
580
+ cmpMethodToString() {
581
+ switch (this.cmpMethod) {
582
+ case Headers_1.CMP_METHOD.STORED: return 'Stored';
583
+ case Headers_1.CMP_METHOD.SHRUNK: return 'Shrunk';
584
+ case Headers_1.CMP_METHOD.REDUCED1: return 'Reduced-1';
585
+ case Headers_1.CMP_METHOD.REDUCED2: return 'Reduced-2';
586
+ case Headers_1.CMP_METHOD.REDUCED3: return 'Reduced-3';
587
+ case Headers_1.CMP_METHOD.REDUCED4: return 'Reduced-4';
588
+ case Headers_1.CMP_METHOD.IMPLODED: return 'Imploded';
589
+ case Headers_1.CMP_METHOD.DEFLATED:
590
+ switch (this.bitFlags & 0x6) {
591
+ case 0: return 'Deflate-N'; // Deflate Normal
592
+ case 2: return 'Deflate-M'; // Deflate Maximum
593
+ case 4: return 'Deflate-F'; // Deflate Fast
594
+ case 6: return 'Deflate-S'; // Deflate Super Fast
595
+ }
596
+ case Headers_1.CMP_METHOD.ENHANCED_DEFLATE: return 'Deflate-Enh';
597
+ case Headers_1.CMP_METHOD.IBM_TERSE: return 'PKDCL-LZ77';
598
+ case Headers_1.CMP_METHOD.ZSTD: return 'Zstandard';
599
+ default: return 'Unknown';
600
+ }
601
+ }
602
+ /**
603
+ * Converts file system code to human-readable string
604
+ * @returns String describing the file system
605
+ */
606
+ fileSystemToString() {
607
+ switch (this.verMadeBy >> 8) {
608
+ case Headers_1.FILE_SYSTEM.MSDOS: return 'MS-DOS';
609
+ case Headers_1.FILE_SYSTEM.AMIGA: return 'Amiga';
610
+ case Headers_1.FILE_SYSTEM.OPENVMS: return 'OpenVMS';
611
+ case Headers_1.FILE_SYSTEM.UNIX: return 'Unix';
612
+ case Headers_1.FILE_SYSTEM.VM_CMS: return 'VM/CMS';
613
+ case Headers_1.FILE_SYSTEM.ATARI: return 'Atari ST';
614
+ case Headers_1.FILE_SYSTEM.OS2: return 'OS/2 HPFS';
615
+ case Headers_1.FILE_SYSTEM.MAC: return 'Macintosh';
616
+ case Headers_1.FILE_SYSTEM.CP_M: return 'CP/M';
617
+ case Headers_1.FILE_SYSTEM.NTFS: return 'Windows NTFS';
618
+ case Headers_1.FILE_SYSTEM.MVS: return 'MVS (OS/390 - Z/OS)';
619
+ case Headers_1.FILE_SYSTEM.VSE: return 'VSE';
620
+ case Headers_1.FILE_SYSTEM.ACORN: return 'Acorn Risc';
621
+ case Headers_1.FILE_SYSTEM.ALTMVS: return 'Alternate MVS';
622
+ case Headers_1.FILE_SYSTEM.BEOS: return 'BeOS';
623
+ case Headers_1.FILE_SYSTEM.TANDEM: return 'Tandem';
624
+ case Headers_1.FILE_SYSTEM.OS400: return 'OS/400';
625
+ case Headers_1.FILE_SYSTEM.DARWIN: return 'Apple OS/X (Darwin)';
626
+ default: return 'Unknown';
627
+ }
628
+ }
629
+ /**
630
+ * Converts MS-DOS file attributes to string representation
631
+ * @returns String like "----" where R=readonly, H=hidden, S=system, A=archive
632
+ */
633
+ dosAttributesToString() {
634
+ let dosAttr = this.extFileAttr & 0xFFFF;
635
+ if (dosAttr === 0)
636
+ return 'none';
637
+ let attrs = '';
638
+ attrs += (dosAttr & Headers_1.DOS_FILE_ATTR.READONLY) ? 'r' : '-';
639
+ attrs += (dosAttr & Headers_1.DOS_FILE_ATTR.HIDDEN) ? 'h' : '-';
640
+ attrs += (dosAttr & Headers_1.DOS_FILE_ATTR.SYSTEM) ? 's' : '-';
641
+ // attrs += (dosAttr & DOS_FILE_ATTR.VOLUME) ? 'v' : '-';
642
+ attrs += (dosAttr & Headers_1.DOS_FILE_ATTR.DIRECTORY) ? 'd' : '-';
643
+ attrs += (dosAttr & Headers_1.DOS_FILE_ATTR.ARCHIVE) ? 'a' : '-';
644
+ return attrs;
645
+ }
646
+ /**
647
+ * Outputs detailed information about this entry for debugging
648
+ * Includes compression, encryption, timestamps, and extra fields
649
+ */
650
+ showVerboseInfo() {
651
+ Logger_1.Logger.log('=== Central Directory Entry (%s) ===', this.filename);
652
+ Logger_1.Logger.log('unzip from start of archive: ', this.localHdrOffset);
653
+ Logger_1.Logger.log('File system or operating system of origin: ', this.fileSystemToString());
654
+ Logger_1.Logger.log('Version of encoding software: ', this.verMadeBy);
655
+ Logger_1.Logger.log('Compression Method: ', this.cmpMethodToString());
656
+ Logger_1.Logger.log('File Security Status: ', this.bitFlags & Headers_1.GP_FLAG.ENCRYPTED ?
657
+ this.bitFlags & Headers_1.GP_FLAG.STRONG_ENCRYPT ? 'Strong Encrypt' : 'Encrypted' :
658
+ 'Not Encrypted');
659
+ Logger_1.Logger.log('File Modified (DOS date/time) ', this.toFormattedDateString());
660
+ Logger_1.Logger.log('File Modified (UTC) ', this.toFormattedUTCDateString());
661
+ Logger_1.Logger.log('Compressed Size: ', this.compressedSize);
662
+ Logger_1.Logger.log('UnCompressed Size: ', this.uncompressedSize);
663
+ Logger_1.Logger.log(`32-bit CRC value (hex): ${this.crc.toString(16).padStart(8, '0')}`);
664
+ Logger_1.Logger.log('Length of extra field: ', this.extraField?.length ?? 0);
665
+ Logger_1.Logger.log('Length of file comment: ', this.comment?.length ?? 0);
666
+ Logger_1.Logger.log('Unix File Attributes: ');
667
+ Logger_1.Logger.log('MS-DOS File Attributes: ', this.dosAttributesToString());
668
+ if (this.extraField) {
669
+ Logger_1.Logger.log('\nThe Central-Directory Extra Field contains:');
670
+ try {
671
+ for (let i = 0; i < this.extraField.length;) {
672
+ // Ensure we have at least 4 bytes (header ID + length)
673
+ if (i + 4 > this.extraField.length) {
674
+ Logger_1.Logger.log(` Warning: Truncated extra field at offset ${i}`);
675
+ break;
676
+ }
677
+ let _id = this.extraField.readUInt16LE(i);
678
+ let _idStr = _id.toString(16).padStart(4, '0');
679
+ let _len = this.extraField.readUInt16LE(i + 2);
680
+ // Validate the length to ensure it doesn't exceed buffer bounds
681
+ if (_len < 0 || i + 4 + _len > this.extraField.length) {
682
+ Logger_1.Logger.log(` Warning: Invalid extra field length (${_len}) at offset ${i} for ID ${_idStr}`);
683
+ break;
684
+ }
685
+ let _data = this.extraField.subarray(i + 4, i + 4 + _len);
686
+ try {
687
+ if (_id === Headers_1.HDR_ID.SHA256) {
688
+ Logger_1.Logger.log(` ID[0x${_idStr}] NeoZip-SHA256: ${_data.toString('hex')}`);
689
+ }
690
+ else if (_id === Headers_1.HDR_ID.UNV_TIME) {
691
+ if (_len >= 5) {
692
+ const flags = _data.readUInt8(0);
693
+ const timestamp = _data.readUInt32LE(1);
694
+ const date = new Date(timestamp * 1000);
695
+ Logger_1.Logger.log(` ID[0x${_idStr}] Universal Time: flags=${flags}, time=${date.toISOString()}`);
696
+ }
697
+ else {
698
+ Logger_1.Logger.log(` ID[0x${_idStr}] Universal Time: (invalid length ${_len})`);
699
+ }
700
+ }
701
+ else if (_id === Headers_1.HDR_ID.UID_GID) {
702
+ if (_len >= 5) {
703
+ const version = _data.readUInt8(0);
704
+ const uidSize = _data.readUInt8(1);
705
+ if (2 + uidSize > _len) {
706
+ Logger_1.Logger.log(` ID[0x${_idStr}] Unix UID/GID: (invalid UID size ${uidSize})`);
707
+ }
708
+ else {
709
+ const uid = _data.readUIntLE(2, Math.min(uidSize, 4));
710
+ let gid = 0;
711
+ if (2 + uidSize + 1 < _len) {
712
+ const gidSize = _data.readUInt8(2 + uidSize);
713
+ if (2 + uidSize + 1 + gidSize <= _len) {
714
+ gid = _data.readUIntLE(2 + uidSize + 1, Math.min(gidSize, 4));
715
+ }
716
+ }
717
+ Logger_1.Logger.log(` ID[0x${_idStr}] Unix UID/GID: version=${version}, uid=${uid}, gid=${gid}`);
718
+ }
719
+ }
720
+ else {
721
+ Logger_1.Logger.log(` ID[0x${_idStr}] Unix UID/GID: (invalid length ${_len})`);
722
+ }
723
+ }
724
+ else if (_id === Headers_1.HDR_ID.SYMLINK) {
725
+ if (_len >= 3) {
726
+ const version = _data.readUInt8(0);
727
+ const targetLength = _data.readUInt16LE(1);
728
+ if (targetLength > 0 && _len >= 3 + targetLength) {
729
+ const target = _data.subarray(3, 3 + targetLength).toString('utf8');
730
+ Logger_1.Logger.log(` ID[0x${_idStr}] Symbolic Link: version=${version}, target="${target}"`);
731
+ }
732
+ else {
733
+ Logger_1.Logger.log(` ID[0x${_idStr}] Symbolic Link: (invalid target length ${targetLength})`);
734
+ }
735
+ }
736
+ else {
737
+ Logger_1.Logger.log(` ID[0x${_idStr}] Symbolic Link: (invalid length ${_len})`);
738
+ }
739
+ }
740
+ else if (_id === Headers_1.HDR_ID.HARDLINK) {
741
+ if (_len >= 11) {
742
+ const version = _data.readUInt8(0);
743
+ const inode = _data.readUInt32LE(1);
744
+ const originalLength = _data.readUInt16LE(5);
745
+ if (originalLength > 0 && _len >= 7 + originalLength) {
746
+ const original = _data.subarray(7, 7 + originalLength).toString('utf8');
747
+ Logger_1.Logger.log(` ID[0x${_idStr}] Hard Link: version=${version}, inode=${inode}, original="${original}"`);
748
+ }
749
+ else {
750
+ Logger_1.Logger.log(` ID[0x${_idStr}] Hard Link: (invalid original length ${originalLength})`);
751
+ }
752
+ }
753
+ else {
754
+ Logger_1.Logger.log(` ID[0x${_idStr}] Hard Link: (invalid length ${_len})`);
755
+ }
756
+ }
757
+ else if (_id === Headers_1.HDR_ID.UNICODE_PATH) {
758
+ if (_len >= 5) {
759
+ const version = _data.readUInt8(0);
760
+ const nameCrc32 = _data.readUInt32LE(1);
761
+ const unicodeName = _data.subarray(5).toString('utf8');
762
+ // Calculate CRC32 of the original filename for verification
763
+ const fnameBuf = Buffer.from(this.filename);
764
+ const calculatedCrc = (0, ZipCrypto_1.crc32)(fnameBuf);
765
+ const crcMatch = nameCrc32 === calculatedCrc ? 'MATCH' : 'MISMATCH';
766
+ Logger_1.Logger.log(` ID[0x${_idStr}] Unicode Path: version=${version}, CRC32=${nameCrc32.toString(16)} (${crcMatch})`);
767
+ Logger_1.Logger.log(` Path: "${unicodeName}"`);
768
+ }
769
+ else {
770
+ Logger_1.Logger.log(` ID[0x${_idStr}] Unicode Path: (invalid length ${_len})`);
771
+ }
772
+ }
773
+ else if (_id === Headers_1.HDR_ID.ZIP64) {
774
+ // ZIP64 Extended Information (0x0001)
775
+ Logger_1.Logger.log(` ID[0x${_idStr}] ZIP64 Extended Information:`);
776
+ if (_len >= 8) {
777
+ let offset = 0;
778
+ // Read uncompressed size (8 bytes) if present
779
+ if (offset + 8 <= _len) {
780
+ const uncompressedSize = _data.readBigUInt64LE(offset);
781
+ Logger_1.Logger.log(` Uncompressed Size (ZIP64): ${uncompressedSize.toString()} bytes`);
782
+ offset += 8;
783
+ }
784
+ // Read compressed size (8 bytes) if present
785
+ if (offset + 8 <= _len) {
786
+ const compressedSize = _data.readBigUInt64LE(offset);
787
+ Logger_1.Logger.log(` Compressed Size (ZIP64): ${compressedSize.toString()} bytes`);
788
+ offset += 8;
789
+ }
790
+ // Read local header offset (8 bytes) if present
791
+ if (offset + 8 <= _len) {
792
+ const localHeaderOffset = _data.readBigUInt64LE(offset);
793
+ Logger_1.Logger.log(` Local Header Offset (ZIP64): ${localHeaderOffset.toString()}`);
794
+ offset += 8;
795
+ }
796
+ // Read disk number (4 bytes) if present
797
+ if (offset + 4 <= _len) {
798
+ const diskNumber = _data.readUInt32LE(offset);
799
+ Logger_1.Logger.log(` Disk Number (ZIP64): ${diskNumber}`);
800
+ }
801
+ }
802
+ else {
803
+ Logger_1.Logger.log(` ZIP64 Extended Information: (invalid length ${_len})`);
804
+ }
805
+ }
806
+ else {
807
+ // For unknown fields, show a hex preview of the first few bytes
808
+ const preview = _len > 0
809
+ ? _data.slice(0, Math.min(16, _len)).toString('hex')
810
+ : '';
811
+ Logger_1.Logger.log(` ID[0x${_idStr}] Unknown field: length=${_len} bytes${preview ? ', data=' + preview + '...' : ''}`);
812
+ }
813
+ }
814
+ catch (error) {
815
+ Logger_1.Logger.log(` Error parsing extra field ID[0x${_idStr}]: ${error.message}`);
816
+ }
817
+ i += 4 + _len;
818
+ }
819
+ }
820
+ catch (error) {
821
+ Logger_1.Logger.log(` Error parsing extra fields: ${error.message}`);
822
+ }
823
+ }
824
+ Logger_1.Logger.log('\n');
825
+ }
826
+ }
827
+ exports.default = ZipEntry;
828
+ exports.ZipEntry = ZipEntry;
829
+ //# sourceMappingURL=ZipEntry.js.map