@cj-tech-master/excelts 4.2.1-canary.20260111102127.f808a37 → 4.2.1

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 (232) hide show
  1. package/THIRD_PARTY_NOTICES.md +31 -0
  2. package/dist/browser/index.browser.d.ts +0 -1
  3. package/dist/browser/index.browser.js +0 -12
  4. package/dist/browser/modules/archive/byte-queue.d.ts +18 -0
  5. package/dist/browser/modules/archive/byte-queue.js +125 -0
  6. package/dist/browser/modules/archive/{compression/compress.base.js → compress.base.js} +1 -1
  7. package/dist/browser/modules/archive/{compression/compress.browser.d.ts → compress.browser.d.ts} +8 -2
  8. package/dist/{esm/modules/archive/compression → browser/modules/archive}/compress.browser.js +11 -3
  9. package/dist/browser/modules/archive/{compression/compress.d.ts → compress.d.ts} +2 -2
  10. package/dist/{esm/modules/archive/compression → browser/modules/archive}/compress.js +1 -1
  11. package/dist/browser/modules/archive/{compression/crc32.browser.d.ts → crc32.browser.d.ts} +1 -1
  12. package/dist/browser/modules/archive/{compression/crc32.d.ts → crc32.d.ts} +1 -1
  13. package/dist/browser/modules/archive/{compression/crc32.js → crc32.js} +1 -1
  14. package/dist/browser/modules/archive/defaults.d.ts +0 -1
  15. package/dist/browser/modules/archive/defaults.js +3 -6
  16. package/dist/browser/modules/archive/{compression/deflate-fallback.js → deflate-fallback.js} +1 -1
  17. package/dist/browser/modules/archive/{unzip/extract.d.ts → extract.d.ts} +2 -2
  18. package/dist/browser/modules/archive/index.base.d.ts +4 -4
  19. package/dist/browser/modules/archive/index.base.js +6 -3
  20. package/dist/browser/modules/archive/index.browser.d.ts +4 -3
  21. package/dist/browser/modules/archive/index.browser.js +7 -3
  22. package/dist/browser/modules/archive/index.d.ts +4 -3
  23. package/dist/browser/modules/archive/index.js +5 -3
  24. package/dist/browser/modules/archive/{unzip/stream.base.d.ts → parse.base.d.ts} +2 -36
  25. package/dist/browser/modules/archive/parse.base.js +644 -0
  26. package/dist/browser/modules/archive/{unzip/stream.browser.d.ts → parse.browser.d.ts} +1 -1
  27. package/dist/{esm/modules/archive/unzip/stream.browser.js → browser/modules/archive/parse.browser.js} +110 -371
  28. package/dist/browser/modules/archive/{unzip/stream.d.ts → parse.d.ts} +2 -2
  29. package/dist/{esm/modules/archive/unzip/stream.js → browser/modules/archive/parse.js} +5 -6
  30. package/dist/browser/modules/archive/{compression/streaming-compress.browser.d.ts → streaming-compress.browser.d.ts} +2 -2
  31. package/dist/browser/modules/archive/{compression/streaming-compress.browser.js → streaming-compress.browser.js} +3 -3
  32. package/dist/browser/modules/archive/{compression/streaming-compress.d.ts → streaming-compress.d.ts} +2 -2
  33. package/dist/browser/modules/archive/{compression/streaming-compress.js → streaming-compress.js} +2 -2
  34. package/dist/browser/modules/archive/{zip/stream.d.ts → streaming-zip.d.ts} +5 -28
  35. package/dist/{esm/modules/archive/zip/stream.js → browser/modules/archive/streaming-zip.js} +48 -192
  36. package/dist/browser/modules/archive/utils/bytes.js +16 -16
  37. package/dist/browser/modules/archive/utils/parse-buffer.js +23 -21
  38. package/dist/browser/modules/archive/utils/timestamps.js +1 -62
  39. package/dist/browser/modules/archive/utils/zip-extra-fields.d.ts +1 -1
  40. package/dist/browser/modules/archive/utils/zip-extra-fields.js +14 -26
  41. package/dist/browser/modules/archive/utils/zip-extra.d.ts +18 -0
  42. package/dist/browser/modules/archive/utils/zip-extra.js +68 -0
  43. package/dist/browser/modules/archive/zip-builder.d.ts +117 -0
  44. package/dist/browser/modules/archive/zip-builder.js +292 -0
  45. package/dist/browser/modules/archive/zip-constants.d.ts +18 -0
  46. package/dist/browser/modules/archive/zip-constants.js +23 -0
  47. package/dist/{esm/modules/archive/zip → browser/modules/archive}/zip-entry-metadata.js +3 -3
  48. package/dist/{types/modules/archive/unzip → browser/modules/archive}/zip-parser.d.ts +1 -1
  49. package/dist/{esm/modules/archive/unzip → browser/modules/archive}/zip-parser.js +24 -38
  50. package/dist/browser/modules/archive/{zip-spec/zip-records.d.ts → zip-records.d.ts} +0 -20
  51. package/dist/browser/modules/archive/zip-records.js +84 -0
  52. package/dist/browser/modules/excel/stream/workbook-reader.browser.js +1 -1
  53. package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +1 -1
  54. package/dist/browser/modules/excel/stream/workbook-writer.browser.js +1 -1
  55. package/dist/browser/modules/excel/xlsx/xlsx.browser.js +6 -3
  56. package/dist/browser/modules/excel/xlsx/xlsx.js +1 -1
  57. package/dist/browser/modules/stream/streams.browser.d.ts +30 -28
  58. package/dist/browser/modules/stream/streams.browser.js +710 -830
  59. package/dist/browser/modules/stream/streams.js +58 -140
  60. package/dist/cjs/modules/archive/byte-queue.js +129 -0
  61. package/dist/cjs/modules/archive/{compression/compress.base.js → compress.base.js} +1 -1
  62. package/dist/cjs/modules/archive/{compression/compress.browser.js → compress.browser.js} +11 -3
  63. package/dist/cjs/modules/archive/{compression/compress.js → compress.js} +1 -1
  64. package/dist/cjs/modules/archive/{compression/crc32.js → crc32.js} +1 -1
  65. package/dist/cjs/modules/archive/defaults.js +4 -7
  66. package/dist/cjs/modules/archive/{compression/deflate-fallback.js → deflate-fallback.js} +1 -1
  67. package/dist/cjs/modules/archive/index.base.js +19 -9
  68. package/dist/cjs/modules/archive/index.browser.js +10 -4
  69. package/dist/cjs/modules/archive/index.js +8 -4
  70. package/dist/cjs/modules/archive/parse.base.js +666 -0
  71. package/dist/cjs/modules/archive/{unzip/stream.browser.js → parse.browser.js} +111 -372
  72. package/dist/cjs/modules/archive/{unzip/stream.js → parse.js} +8 -9
  73. package/dist/cjs/modules/archive/{compression/streaming-compress.browser.js → streaming-compress.browser.js} +3 -3
  74. package/dist/cjs/modules/archive/{compression/streaming-compress.js → streaming-compress.js} +2 -2
  75. package/dist/cjs/modules/archive/{zip/stream.js → streaming-zip.js} +50 -194
  76. package/dist/cjs/modules/archive/utils/bytes.js +16 -16
  77. package/dist/cjs/modules/archive/utils/parse-buffer.js +23 -21
  78. package/dist/cjs/modules/archive/utils/timestamps.js +3 -64
  79. package/dist/cjs/modules/archive/utils/zip-extra-fields.js +14 -26
  80. package/dist/cjs/modules/archive/utils/zip-extra.js +74 -0
  81. package/dist/cjs/modules/archive/zip-builder.js +297 -0
  82. package/dist/cjs/modules/archive/zip-constants.js +26 -0
  83. package/dist/cjs/modules/archive/{zip/zip-entry-metadata.js → zip-entry-metadata.js} +5 -5
  84. package/dist/cjs/modules/archive/{unzip/zip-parser.js → zip-parser.js} +33 -47
  85. package/dist/cjs/modules/archive/zip-records.js +90 -0
  86. package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +2 -2
  87. package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +4 -4
  88. package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +9 -6
  89. package/dist/cjs/modules/excel/xlsx/xlsx.js +2 -2
  90. package/dist/cjs/modules/stream/streams.browser.js +710 -830
  91. package/dist/cjs/modules/stream/streams.js +58 -140
  92. package/dist/esm/index.browser.js +0 -12
  93. package/dist/esm/modules/archive/byte-queue.js +125 -0
  94. package/dist/esm/modules/archive/{compression/compress.base.js → compress.base.js} +1 -1
  95. package/dist/{browser/modules/archive/compression → esm/modules/archive}/compress.browser.js +11 -3
  96. package/dist/{browser/modules/archive/compression → esm/modules/archive}/compress.js +1 -1
  97. package/dist/esm/modules/archive/{compression/crc32.js → crc32.js} +1 -1
  98. package/dist/esm/modules/archive/defaults.js +3 -6
  99. package/dist/esm/modules/archive/{compression/deflate-fallback.js → deflate-fallback.js} +1 -1
  100. package/dist/esm/modules/archive/index.base.js +6 -3
  101. package/dist/esm/modules/archive/index.browser.js +7 -3
  102. package/dist/esm/modules/archive/index.js +5 -3
  103. package/dist/esm/modules/archive/parse.base.js +644 -0
  104. package/dist/{browser/modules/archive/unzip/stream.browser.js → esm/modules/archive/parse.browser.js} +110 -371
  105. package/dist/{browser/modules/archive/unzip/stream.js → esm/modules/archive/parse.js} +5 -6
  106. package/dist/esm/modules/archive/{compression/streaming-compress.browser.js → streaming-compress.browser.js} +3 -3
  107. package/dist/esm/modules/archive/{compression/streaming-compress.js → streaming-compress.js} +2 -2
  108. package/dist/{browser/modules/archive/zip/stream.js → esm/modules/archive/streaming-zip.js} +48 -192
  109. package/dist/esm/modules/archive/utils/bytes.js +16 -16
  110. package/dist/esm/modules/archive/utils/parse-buffer.js +23 -21
  111. package/dist/esm/modules/archive/utils/timestamps.js +1 -62
  112. package/dist/esm/modules/archive/utils/zip-extra-fields.js +14 -26
  113. package/dist/esm/modules/archive/utils/zip-extra.js +68 -0
  114. package/dist/esm/modules/archive/zip-builder.js +292 -0
  115. package/dist/esm/modules/archive/zip-constants.js +23 -0
  116. package/dist/{browser/modules/archive/zip → esm/modules/archive}/zip-entry-metadata.js +3 -3
  117. package/dist/{browser/modules/archive/unzip → esm/modules/archive}/zip-parser.js +24 -38
  118. package/dist/esm/modules/archive/zip-records.js +84 -0
  119. package/dist/esm/modules/excel/stream/workbook-reader.browser.js +1 -1
  120. package/dist/esm/modules/excel/stream/workbook-writer.browser.js +1 -1
  121. package/dist/esm/modules/excel/xlsx/xlsx.browser.js +6 -3
  122. package/dist/esm/modules/excel/xlsx/xlsx.js +1 -1
  123. package/dist/esm/modules/stream/streams.browser.js +710 -830
  124. package/dist/esm/modules/stream/streams.js +58 -140
  125. package/dist/iife/THIRD_PARTY_NOTICES.md +31 -0
  126. package/dist/iife/excelts.iife.js +4425 -6215
  127. package/dist/iife/excelts.iife.js.map +1 -1
  128. package/dist/iife/excelts.iife.min.js +31 -103
  129. package/dist/types/index.browser.d.ts +0 -1
  130. package/dist/types/modules/archive/byte-queue.d.ts +18 -0
  131. package/dist/types/modules/archive/{compression/compress.browser.d.ts → compress.browser.d.ts} +8 -2
  132. package/dist/types/modules/archive/defaults.d.ts +0 -1
  133. package/dist/types/modules/archive/index.base.d.ts +4 -4
  134. package/dist/types/modules/archive/index.browser.d.ts +4 -3
  135. package/dist/types/modules/archive/index.d.ts +4 -3
  136. package/dist/types/modules/archive/{unzip/stream.base.d.ts → parse.base.d.ts} +4 -38
  137. package/dist/types/modules/archive/{unzip/stream.browser.d.ts → parse.browser.d.ts} +2 -2
  138. package/dist/types/modules/archive/{unzip/stream.d.ts → parse.d.ts} +3 -3
  139. package/dist/types/modules/archive/{compression/streaming-compress.browser.d.ts → streaming-compress.browser.d.ts} +1 -1
  140. package/dist/types/modules/archive/{zip/stream.d.ts → streaming-zip.d.ts} +6 -29
  141. package/dist/types/modules/archive/utils/zip-extra-fields.d.ts +1 -1
  142. package/dist/types/modules/archive/utils/zip-extra.d.ts +18 -0
  143. package/dist/types/modules/archive/zip-builder.d.ts +117 -0
  144. package/dist/types/modules/archive/zip-constants.d.ts +18 -0
  145. package/dist/types/modules/archive/{zip/zip-entry-metadata.d.ts → zip-entry-metadata.d.ts} +1 -1
  146. package/dist/{browser/modules/archive/unzip → types/modules/archive}/zip-parser.d.ts +1 -1
  147. package/dist/types/modules/archive/{zip-spec/zip-records.d.ts → zip-records.d.ts} +0 -20
  148. package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +1 -1
  149. package/dist/types/modules/stream/streams.browser.d.ts +30 -28
  150. package/package.json +1 -5
  151. package/dist/browser/modules/archive/internal/byte-queue.d.ts +0 -33
  152. package/dist/browser/modules/archive/internal/byte-queue.js +0 -407
  153. package/dist/browser/modules/archive/io/archive-sink.d.ts +0 -9
  154. package/dist/browser/modules/archive/io/archive-sink.js +0 -77
  155. package/dist/browser/modules/archive/io/archive-source.d.ts +0 -8
  156. package/dist/browser/modules/archive/io/archive-source.js +0 -107
  157. package/dist/browser/modules/archive/unzip/index.d.ts +0 -40
  158. package/dist/browser/modules/archive/unzip/index.js +0 -164
  159. package/dist/browser/modules/archive/unzip/stream.base.js +0 -1022
  160. package/dist/browser/modules/archive/utils/async-queue.d.ts +0 -7
  161. package/dist/browser/modules/archive/utils/async-queue.js +0 -103
  162. package/dist/browser/modules/archive/utils/compressibility.d.ts +0 -10
  163. package/dist/browser/modules/archive/utils/compressibility.js +0 -57
  164. package/dist/browser/modules/archive/utils/pattern-scanner.d.ts +0 -21
  165. package/dist/browser/modules/archive/utils/pattern-scanner.js +0 -27
  166. package/dist/browser/modules/archive/zip/index.d.ts +0 -42
  167. package/dist/browser/modules/archive/zip/index.js +0 -157
  168. package/dist/browser/modules/archive/zip/zip-bytes.d.ts +0 -73
  169. package/dist/browser/modules/archive/zip/zip-bytes.js +0 -239
  170. package/dist/browser/modules/archive/zip-spec/zip-records.js +0 -126
  171. package/dist/cjs/modules/archive/internal/byte-queue.js +0 -411
  172. package/dist/cjs/modules/archive/io/archive-sink.js +0 -82
  173. package/dist/cjs/modules/archive/io/archive-source.js +0 -114
  174. package/dist/cjs/modules/archive/unzip/index.js +0 -170
  175. package/dist/cjs/modules/archive/unzip/stream.base.js +0 -1044
  176. package/dist/cjs/modules/archive/utils/async-queue.js +0 -106
  177. package/dist/cjs/modules/archive/utils/compressibility.js +0 -60
  178. package/dist/cjs/modules/archive/utils/pattern-scanner.js +0 -31
  179. package/dist/cjs/modules/archive/zip/index.js +0 -162
  180. package/dist/cjs/modules/archive/zip/zip-bytes.js +0 -242
  181. package/dist/cjs/modules/archive/zip-spec/zip-records.js +0 -136
  182. package/dist/esm/modules/archive/internal/byte-queue.js +0 -407
  183. package/dist/esm/modules/archive/io/archive-sink.js +0 -77
  184. package/dist/esm/modules/archive/io/archive-source.js +0 -107
  185. package/dist/esm/modules/archive/unzip/index.js +0 -164
  186. package/dist/esm/modules/archive/unzip/stream.base.js +0 -1022
  187. package/dist/esm/modules/archive/utils/async-queue.js +0 -103
  188. package/dist/esm/modules/archive/utils/compressibility.js +0 -57
  189. package/dist/esm/modules/archive/utils/pattern-scanner.js +0 -27
  190. package/dist/esm/modules/archive/zip/index.js +0 -157
  191. package/dist/esm/modules/archive/zip/zip-bytes.js +0 -239
  192. package/dist/esm/modules/archive/zip-spec/zip-records.js +0 -126
  193. package/dist/types/modules/archive/internal/byte-queue.d.ts +0 -33
  194. package/dist/types/modules/archive/io/archive-sink.d.ts +0 -9
  195. package/dist/types/modules/archive/io/archive-source.d.ts +0 -8
  196. package/dist/types/modules/archive/unzip/index.d.ts +0 -40
  197. package/dist/types/modules/archive/utils/async-queue.d.ts +0 -7
  198. package/dist/types/modules/archive/utils/compressibility.d.ts +0 -10
  199. package/dist/types/modules/archive/utils/pattern-scanner.d.ts +0 -21
  200. package/dist/types/modules/archive/zip/index.d.ts +0 -42
  201. package/dist/types/modules/archive/zip/zip-bytes.d.ts +0 -73
  202. /package/dist/browser/modules/archive/{compression/compress.base.d.ts → compress.base.d.ts} +0 -0
  203. /package/dist/browser/modules/archive/{compression/crc32.base.d.ts → crc32.base.d.ts} +0 -0
  204. /package/dist/browser/modules/archive/{compression/crc32.base.js → crc32.base.js} +0 -0
  205. /package/dist/browser/modules/archive/{compression/crc32.browser.js → crc32.browser.js} +0 -0
  206. /package/dist/browser/modules/archive/{compression/deflate-fallback.d.ts → deflate-fallback.d.ts} +0 -0
  207. /package/dist/browser/modules/archive/{unzip/extract.js → extract.js} +0 -0
  208. /package/dist/browser/modules/archive/{compression/streaming-compress.base.d.ts → streaming-compress.base.d.ts} +0 -0
  209. /package/dist/browser/modules/archive/{compression/streaming-compress.base.js → streaming-compress.base.js} +0 -0
  210. /package/dist/browser/modules/archive/{zip-spec/zip-entry-info.d.ts → zip-entry-info.d.ts} +0 -0
  211. /package/dist/browser/modules/archive/{zip-spec/zip-entry-info.js → zip-entry-info.js} +0 -0
  212. /package/dist/browser/modules/archive/{zip/zip-entry-metadata.d.ts → zip-entry-metadata.d.ts} +0 -0
  213. /package/dist/cjs/modules/archive/{compression/crc32.base.js → crc32.base.js} +0 -0
  214. /package/dist/cjs/modules/archive/{compression/crc32.browser.js → crc32.browser.js} +0 -0
  215. /package/dist/cjs/modules/archive/{unzip/extract.js → extract.js} +0 -0
  216. /package/dist/cjs/modules/archive/{compression/streaming-compress.base.js → streaming-compress.base.js} +0 -0
  217. /package/dist/cjs/modules/archive/{zip-spec/zip-entry-info.js → zip-entry-info.js} +0 -0
  218. /package/dist/esm/modules/archive/{compression/crc32.base.js → crc32.base.js} +0 -0
  219. /package/dist/esm/modules/archive/{compression/crc32.browser.js → crc32.browser.js} +0 -0
  220. /package/dist/esm/modules/archive/{unzip/extract.js → extract.js} +0 -0
  221. /package/dist/esm/modules/archive/{compression/streaming-compress.base.js → streaming-compress.base.js} +0 -0
  222. /package/dist/esm/modules/archive/{zip-spec/zip-entry-info.js → zip-entry-info.js} +0 -0
  223. /package/dist/types/modules/archive/{compression/compress.base.d.ts → compress.base.d.ts} +0 -0
  224. /package/dist/types/modules/archive/{compression/compress.d.ts → compress.d.ts} +0 -0
  225. /package/dist/types/modules/archive/{compression/crc32.base.d.ts → crc32.base.d.ts} +0 -0
  226. /package/dist/types/modules/archive/{compression/crc32.browser.d.ts → crc32.browser.d.ts} +0 -0
  227. /package/dist/types/modules/archive/{compression/crc32.d.ts → crc32.d.ts} +0 -0
  228. /package/dist/types/modules/archive/{compression/deflate-fallback.d.ts → deflate-fallback.d.ts} +0 -0
  229. /package/dist/types/modules/archive/{unzip/extract.d.ts → extract.d.ts} +0 -0
  230. /package/dist/types/modules/archive/{compression/streaming-compress.base.d.ts → streaming-compress.base.d.ts} +0 -0
  231. /package/dist/types/modules/archive/{compression/streaming-compress.d.ts → streaming-compress.d.ts} +0 -0
  232. /package/dist/types/modules/archive/{zip-spec/zip-entry-info.d.ts → zip-entry-info.d.ts} +0 -0
@@ -1,3 +1,25 @@
1
+ /**
2
+ * Read unsigned little-endian integer from Uint8Array
3
+ */
4
+ function readUIntLE(buffer, offset, size) {
5
+ const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
6
+ switch (size) {
7
+ case 1:
8
+ return view.getUint8(offset);
9
+ case 2:
10
+ return view.getUint16(offset, true);
11
+ case 4:
12
+ return view.getUint32(offset, true);
13
+ case 8: {
14
+ // Read as BigUint64 and convert to Number
15
+ const low = view.getUint32(offset, true);
16
+ const high = view.getUint32(offset + 4, true);
17
+ return high * 0x100000000 + low;
18
+ }
19
+ default:
20
+ throw new Error("Unsupported UInt LE size!");
21
+ }
22
+ }
1
23
  /**
2
24
  * Parses sequential unsigned little endian numbers from the head of the passed buffer according to
3
25
  * the specified format passed. If the buffer is not large enough to satisfy the full format,
@@ -16,30 +38,10 @@
16
38
  */
17
39
  export function parse(buffer, format) {
18
40
  const result = {};
19
- const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
20
41
  let offset = 0;
21
42
  for (const [key, size] of format) {
22
43
  if (buffer.length >= offset + size) {
23
- switch (size) {
24
- case 1:
25
- result[key] = view.getUint8(offset);
26
- break;
27
- case 2:
28
- result[key] = view.getUint16(offset, true);
29
- break;
30
- case 4:
31
- result[key] = view.getUint32(offset, true);
32
- break;
33
- case 8: {
34
- // Keep behavior (Number) while avoiding BigInt costs.
35
- const low = view.getUint32(offset, true);
36
- const high = view.getUint32(offset + 4, true);
37
- result[key] = high * 0x100000000 + low;
38
- break;
39
- }
40
- default:
41
- throw new Error("Unsupported UInt LE size!");
42
- }
44
+ result[key] = readUIntLE(buffer, offset, size);
43
45
  }
44
46
  else {
45
47
  result[key] = null;
@@ -1,65 +1,4 @@
1
- const EXTENDED_TIMESTAMP_ID = 0x5455;
2
- function clampUint32(value) {
3
- if (!Number.isFinite(value)) {
4
- return 0;
5
- }
6
- if (value <= 0) {
7
- return 0;
8
- }
9
- // 0xFFFFFFFF fits JS safe integer.
10
- if (value >= 0xffffffff) {
11
- return 0xffffffff;
12
- }
13
- return value >>> 0;
14
- }
15
- function unixSecondsFromDate(date) {
16
- return clampUint32(Math.floor(date.getTime() / 1000));
17
- }
18
- /**
19
- * Parse Info-ZIP "Extended Timestamp" extra field (0x5455) and return mtime.
20
- * Returns Unix seconds (UTC) if present.
21
- */
22
- function parseExtendedTimestampMtimeUnixSeconds(extraField) {
23
- const view = new DataView(extraField.buffer, extraField.byteOffset, extraField.byteLength);
24
- let offset = 0;
25
- while (offset + 4 <= extraField.length) {
26
- const headerId = view.getUint16(offset, true);
27
- const dataSize = view.getUint16(offset + 2, true);
28
- const dataStart = offset + 4;
29
- const dataEnd = dataStart + dataSize;
30
- if (dataEnd > extraField.length) {
31
- break;
32
- }
33
- if (headerId === EXTENDED_TIMESTAMP_ID && dataSize >= 1) {
34
- const flags = extraField[dataStart];
35
- if ((flags & 0x01) !== 0 && dataSize >= 5) {
36
- // mtime is 4 bytes right after flags.
37
- return view.getUint32(dataStart + 1, true) >>> 0;
38
- }
39
- }
40
- offset = dataEnd;
41
- }
42
- return undefined;
43
- }
44
- /**
45
- * Build Info-ZIP "Extended Timestamp" extra field (0x5455).
46
- * We write only mtime (UTC, Unix seconds) to minimize size.
47
- */
48
- function buildExtendedTimestampExtraFieldFromUnixSeconds(unixSeconds) {
49
- const ts = clampUint32(unixSeconds);
50
- // flags(1) + mtime(4)
51
- const payloadSize = 5;
52
- const out = new Uint8Array(4 + payloadSize);
53
- const view = new DataView(out.buffer);
54
- view.setUint16(0, EXTENDED_TIMESTAMP_ID, true);
55
- view.setUint16(2, payloadSize, true);
56
- out[4] = 0x01; // mtime present
57
- view.setUint32(5, ts, true);
58
- return out;
59
- }
60
- function buildExtendedTimestampExtraFieldFromDate(date) {
61
- return buildExtendedTimestampExtraFieldFromUnixSeconds(unixSecondsFromDate(date));
62
- }
1
+ import { buildExtendedTimestampExtraFieldFromDate, parseExtendedTimestampMtimeUnixSeconds } from "./zip-extra.js";
63
2
  /**
64
3
  * DOS date/time helpers for ZIP files.
65
4
  */
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * ZIP extra field parsing helpers.
3
3
  *
4
- * Kept standalone so both streaming parser (`stream.base.ts`) and buffer parser
4
+ * Kept standalone so both streaming parser (`parse.base.ts`) and buffer parser
5
5
  * (`zip-parser.ts`) can share ZIP64 + Info-ZIP timestamp handling.
6
6
  */
7
7
  export interface ZipVars {
@@ -1,31 +1,10 @@
1
1
  /**
2
2
  * ZIP extra field parsing helpers.
3
3
  *
4
- * Kept standalone so both streaming parser (`stream.base.ts`) and buffer parser
4
+ * Kept standalone so both streaming parser (`parse.base.ts`) and buffer parser
5
5
  * (`zip-parser.ts`) can share ZIP64 + Info-ZIP timestamp handling.
6
6
  */
7
- const EXTENDED_TIMESTAMP_ID = 0x5455;
8
- function parseExtendedTimestampMtimeUnixSeconds(extraField) {
9
- const view = new DataView(extraField.buffer, extraField.byteOffset, extraField.byteLength);
10
- let offset = 0;
11
- while (offset + 4 <= extraField.length) {
12
- const headerId = view.getUint16(offset, true);
13
- const dataSize = view.getUint16(offset + 2, true);
14
- const dataStart = offset + 4;
15
- const dataEnd = dataStart + dataSize;
16
- if (dataEnd > extraField.length) {
17
- break;
18
- }
19
- if (headerId === EXTENDED_TIMESTAMP_ID && dataSize >= 1) {
20
- const flags = extraField[dataStart];
21
- if ((flags & 0x01) !== 0 && dataSize >= 5) {
22
- return view.getUint32(dataStart + 1, true) >>> 0;
23
- }
24
- }
25
- offset = dataEnd;
26
- }
27
- return undefined;
28
- }
7
+ import { parseExtendedTimestampMtimeUnixSeconds } from "./zip-extra.js";
29
8
  function readUint64LE(view, offset) {
30
9
  // Convert to Number via 2x Uint32 to avoid BigInt requirements.
31
10
  const low = view.getUint32(offset, true);
@@ -64,9 +43,18 @@ export function parseZipExtraFields(extraField, vars) {
64
43
  }
65
44
  }
66
45
  else if (signature === 0x5455) {
67
- const unixSeconds = parseExtendedTimestampMtimeUnixSeconds(extraField.subarray(offset, dataEnd));
68
- if (unixSeconds !== undefined) {
69
- extra.mtimeUnixSeconds = unixSeconds;
46
+ // Fast-path parse for Info-ZIP extended timestamp (mtime only).
47
+ if (partSize >= 1) {
48
+ const flags = extraField[dataStart];
49
+ if ((flags & 0x01) !== 0 && partSize >= 5) {
50
+ extra.mtimeUnixSeconds = view.getUint32(dataStart + 1, true) >>> 0;
51
+ }
52
+ else {
53
+ const unixSeconds = parseExtendedTimestampMtimeUnixSeconds(extraField.subarray(offset, dataEnd));
54
+ if (unixSeconds !== undefined) {
55
+ extra.mtimeUnixSeconds = unixSeconds;
56
+ }
57
+ }
70
58
  }
71
59
  }
72
60
  offset = dataEnd;
@@ -0,0 +1,18 @@
1
+ export declare function unixSecondsFromDate(date: Date): number;
2
+ /**
3
+ * Build Info-ZIP "Extended Timestamp" extra field (0x5455).
4
+ * We write only mtime (UTC, Unix seconds) to minimize size.
5
+ *
6
+ * Layout:
7
+ * - Header ID: 2 bytes (0x5455)
8
+ * - Data size: 2 bytes
9
+ * - Flags: 1 byte (bit0 = mtime)
10
+ * - mtime: 4 bytes (Unix seconds)
11
+ */
12
+ export declare function buildExtendedTimestampExtraFieldFromUnixSeconds(unixSeconds: number): Uint8Array;
13
+ export declare function buildExtendedTimestampExtraFieldFromDate(date: Date): Uint8Array;
14
+ /**
15
+ * Parse Info-ZIP "Extended Timestamp" extra field (0x5455) and return mtime.
16
+ * Returns Unix seconds (UTC) if present.
17
+ */
18
+ export declare function parseExtendedTimestampMtimeUnixSeconds(extraField: Uint8Array): number | undefined;
@@ -0,0 +1,68 @@
1
+ const EXTENDED_TIMESTAMP_ID = 0x5455;
2
+ function clampUint32(value) {
3
+ if (!Number.isFinite(value)) {
4
+ return 0;
5
+ }
6
+ if (value <= 0) {
7
+ return 0;
8
+ }
9
+ // 0xFFFFFFFF fits JS safe integer.
10
+ if (value >= 0xffffffff) {
11
+ return 0xffffffff;
12
+ }
13
+ return value >>> 0;
14
+ }
15
+ export function unixSecondsFromDate(date) {
16
+ return clampUint32(Math.floor(date.getTime() / 1000));
17
+ }
18
+ /**
19
+ * Build Info-ZIP "Extended Timestamp" extra field (0x5455).
20
+ * We write only mtime (UTC, Unix seconds) to minimize size.
21
+ *
22
+ * Layout:
23
+ * - Header ID: 2 bytes (0x5455)
24
+ * - Data size: 2 bytes
25
+ * - Flags: 1 byte (bit0 = mtime)
26
+ * - mtime: 4 bytes (Unix seconds)
27
+ */
28
+ export function buildExtendedTimestampExtraFieldFromUnixSeconds(unixSeconds) {
29
+ const ts = clampUint32(unixSeconds);
30
+ // flags(1) + mtime(4)
31
+ const payloadSize = 5;
32
+ const out = new Uint8Array(4 + payloadSize);
33
+ const view = new DataView(out.buffer);
34
+ view.setUint16(0, EXTENDED_TIMESTAMP_ID, true);
35
+ view.setUint16(2, payloadSize, true);
36
+ out[4] = 0x01; // mtime present
37
+ view.setUint32(5, ts, true);
38
+ return out;
39
+ }
40
+ export function buildExtendedTimestampExtraFieldFromDate(date) {
41
+ return buildExtendedTimestampExtraFieldFromUnixSeconds(unixSecondsFromDate(date));
42
+ }
43
+ /**
44
+ * Parse Info-ZIP "Extended Timestamp" extra field (0x5455) and return mtime.
45
+ * Returns Unix seconds (UTC) if present.
46
+ */
47
+ export function parseExtendedTimestampMtimeUnixSeconds(extraField) {
48
+ const view = new DataView(extraField.buffer, extraField.byteOffset, extraField.byteLength);
49
+ let offset = 0;
50
+ while (offset + 4 <= extraField.length) {
51
+ const headerId = view.getUint16(offset, true);
52
+ const dataSize = view.getUint16(offset + 2, true);
53
+ const dataStart = offset + 4;
54
+ const dataEnd = dataStart + dataSize;
55
+ if (dataEnd > extraField.length) {
56
+ break;
57
+ }
58
+ if (headerId === EXTENDED_TIMESTAMP_ID && dataSize >= 1) {
59
+ const flags = extraField[dataStart];
60
+ if ((flags & 0x01) !== 0 && dataSize >= 5) {
61
+ // mtime is 4 bytes right after flags.
62
+ return view.getUint32(dataStart + 1, true) >>> 0;
63
+ }
64
+ }
65
+ offset = dataEnd;
66
+ }
67
+ return undefined;
68
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * ZIP file format builder
3
+ *
4
+ * Implements ZIP file structure according to PKWARE's APPNOTE.TXT specification
5
+ * https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
6
+ *
7
+ * ZIP file structure:
8
+ * ┌──────────────────────────┐
9
+ * │ Local File Header 1 │
10
+ * │ File Data 1 │
11
+ * ├──────────────────────────┤
12
+ * │ Local File Header 2 │
13
+ * │ File Data 2 │
14
+ * ├──────────────────────────┤
15
+ * │ ... │
16
+ * ├──────────────────────────┤
17
+ * │ Central Directory 1 │
18
+ * │ Central Directory 2 │
19
+ * │ ... │
20
+ * ├──────────────────────────┤
21
+ * │ End of Central Directory │
22
+ * └──────────────────────────┘
23
+ */
24
+ import { type CompressOptions } from "@archive/compress";
25
+ import { type ZipTimestampMode } from "@archive/utils/timestamps";
26
+ /**
27
+ * ZIP file entry
28
+ */
29
+ export interface ZipEntry {
30
+ /** File name (can include directory path, use forward slashes) */
31
+ name: string;
32
+ /** File data (will be compressed unless level=0) */
33
+ data: Uint8Array;
34
+ /** File modification time (optional, defaults to current time) */
35
+ modTime?: Date;
36
+ /** File comment (optional) */
37
+ comment?: string;
38
+ }
39
+ /**
40
+ * ZIP builder options
41
+ */
42
+ export interface ZipOptions extends CompressOptions {
43
+ /** ZIP file comment (optional) */
44
+ comment?: string;
45
+ /**
46
+ * Timestamp writing strategy.
47
+ * - "dos": only write DOS date/time fields (smallest output)
48
+ * - "dos+utc": also write UTC mtime in 0x5455 extra field (default, best practice)
49
+ */
50
+ timestamps?: ZipTimestampMode;
51
+ }
52
+ /**
53
+ * Create a ZIP file from entries (async)
54
+ *
55
+ * @param entries - Files to include in ZIP
56
+ * @param options - ZIP options
57
+ * @returns ZIP file as Uint8Array
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * const zip = await createZip([
62
+ * { name: "hello.txt", data: new TextEncoder().encode("Hello!") },
63
+ * { name: "folder/file.txt", data: new TextEncoder().encode("Nested!") }
64
+ * ], { level: 6 });
65
+ * ```
66
+ */
67
+ export declare function createZip(entries: ZipEntry[], options?: ZipOptions): Promise<Uint8Array>;
68
+ /**
69
+ * Create a ZIP file from entries (sync)
70
+ *
71
+ * This is supported in both Node.js and browser builds.
72
+ */
73
+ export declare function createZipSync(entries: ZipEntry[], options?: ZipOptions): Uint8Array;
74
+ /**
75
+ * Streaming ZIP builder for large files
76
+ * Writes chunks to a callback as they are generated
77
+ */
78
+ export declare class ZipBuilder {
79
+ private entries;
80
+ private currentOffset;
81
+ private level;
82
+ private zipComment;
83
+ private timestamps;
84
+ private compressOptions;
85
+ private settings;
86
+ private finalized;
87
+ /**
88
+ * Create a new ZIP builder
89
+ * @param options - ZIP options
90
+ */
91
+ constructor(options?: ZipOptions);
92
+ /**
93
+ * Add a file to the ZIP (async)
94
+ * @param entry - File entry
95
+ * @returns Local file header and compressed data chunks
96
+ */
97
+ addFile(entry: ZipEntry): Promise<Uint8Array[]>;
98
+ /**
99
+ * Add a file to the ZIP (sync)
100
+ * @param entry - File entry
101
+ * @returns Local file header and compressed data chunks
102
+ */
103
+ addFileSync(entry: ZipEntry): Uint8Array[];
104
+ /**
105
+ * Finalize the ZIP and return central directory + end record
106
+ * @returns Central directory and end of central directory chunks
107
+ */
108
+ finalize(): Uint8Array[];
109
+ /**
110
+ * Get current number of entries
111
+ */
112
+ get entryCount(): number;
113
+ /**
114
+ * Get current ZIP data size (without central directory)
115
+ */
116
+ get dataSize(): number;
117
+ }
@@ -0,0 +1,292 @@
1
+ /**
2
+ * ZIP file format builder
3
+ *
4
+ * Implements ZIP file structure according to PKWARE's APPNOTE.TXT specification
5
+ * https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
6
+ *
7
+ * ZIP file structure:
8
+ * ┌──────────────────────────┐
9
+ * │ Local File Header 1 │
10
+ * │ File Data 1 │
11
+ * ├──────────────────────────┤
12
+ * │ Local File Header 2 │
13
+ * │ File Data 2 │
14
+ * ├──────────────────────────┤
15
+ * │ ... │
16
+ * ├──────────────────────────┤
17
+ * │ Central Directory 1 │
18
+ * │ Central Directory 2 │
19
+ * │ ... │
20
+ * ├──────────────────────────┤
21
+ * │ End of Central Directory │
22
+ * └──────────────────────────┘
23
+ */
24
+ import { compress, compressSync } from "./compress.browser.js";
25
+ import { crc32 } from "./crc32.browser.js";
26
+ import { concatUint8Arrays, sumUint8ArrayLengths } from "./utils/bytes.js";
27
+ import {} from "./utils/timestamps.js";
28
+ import { buildZipEntryMetadata, resolveZipCompressionMethod } from "./zip-entry-metadata.js";
29
+ import { DEFAULT_ZIP_LEVEL, DEFAULT_ZIP_TIMESTAMPS } from "./defaults.js";
30
+ import { buildCentralDirectoryHeader, buildEndOfCentralDirectory, buildLocalFileHeader } from "./zip-records.js";
31
+ import { FLAG_UTF8, VERSION_MADE_BY, VERSION_NEEDED } from "./zip-constants.js";
32
+ const LOCAL_FILE_HEADER_FIXED_SIZE = 30;
33
+ function encodeZipComment(comment) {
34
+ // Keep empty comment as empty bytes (no encoding surprises).
35
+ return comment ? new TextEncoder().encode(comment) : new Uint8Array(0);
36
+ }
37
+ function shouldDeflate(level, data) {
38
+ return level > 0 && data.length > 0;
39
+ }
40
+ function computeLocalRecordSize(entry) {
41
+ return (LOCAL_FILE_HEADER_FIXED_SIZE +
42
+ entry.name.length +
43
+ entry.extraField.length +
44
+ entry.compressedData.length);
45
+ }
46
+ function buildProcessedEntry(entry, offset, settings, compressedData) {
47
+ const modDate = entry.modTime ?? settings.defaultModTime;
48
+ const isCompressed = shouldDeflate(settings.level, entry.data);
49
+ const metadata = buildZipEntryMetadata({
50
+ name: entry.name,
51
+ comment: entry.comment,
52
+ modTime: modDate,
53
+ timestamps: settings.timestamps,
54
+ useDataDescriptor: false,
55
+ deflate: isCompressed
56
+ });
57
+ return {
58
+ name: metadata.nameBytes,
59
+ data: entry.data,
60
+ compressedData,
61
+ crc: crc32(entry.data),
62
+ compressionMethod: resolveZipCompressionMethod(isCompressed),
63
+ modTime: metadata.dosTime,
64
+ modDate: metadata.dosDate,
65
+ extraField: metadata.extraField,
66
+ comment: metadata.commentBytes,
67
+ offset
68
+ };
69
+ }
70
+ function appendProcessedEntry(processedEntries, entry, compressedData, currentOffset, settings) {
71
+ const processedEntry = buildProcessedEntry(entry, currentOffset, settings, compressedData);
72
+ processedEntries.push(processedEntry);
73
+ return {
74
+ processedEntry,
75
+ nextOffset: currentOffset + computeLocalRecordSize(processedEntry)
76
+ };
77
+ }
78
+ function finalizeZip(processedEntries, zipComment, centralDirOffset) {
79
+ // Build ZIP structure
80
+ const chunks = [];
81
+ // Local file headers and data
82
+ for (const entry of processedEntries) {
83
+ chunks.push(buildLocalFileHeaderChunk(entry));
84
+ chunks.push(entry.compressedData);
85
+ }
86
+ chunks.push(...buildCentralDirectorySection(processedEntries, centralDirOffset, zipComment));
87
+ return concatUint8Arrays(chunks);
88
+ }
89
+ function buildLocalFileHeaderChunk(entry) {
90
+ return buildLocalFileHeader({
91
+ fileName: entry.name,
92
+ extraField: entry.extraField,
93
+ flags: FLAG_UTF8,
94
+ compressionMethod: entry.compressionMethod,
95
+ dosTime: entry.modTime,
96
+ dosDate: entry.modDate,
97
+ crc32: entry.crc,
98
+ compressedSize: entry.compressedData.length,
99
+ uncompressedSize: entry.data.length,
100
+ versionNeeded: VERSION_NEEDED
101
+ });
102
+ }
103
+ function buildCentralDirHeaderChunk(entry) {
104
+ return buildCentralDirectoryHeader({
105
+ fileName: entry.name,
106
+ extraField: entry.extraField,
107
+ comment: entry.comment,
108
+ flags: FLAG_UTF8,
109
+ compressionMethod: entry.compressionMethod,
110
+ dosTime: entry.modTime,
111
+ dosDate: entry.modDate,
112
+ crc32: entry.crc,
113
+ compressedSize: entry.compressedData.length,
114
+ uncompressedSize: entry.data.length,
115
+ localHeaderOffset: entry.offset,
116
+ versionMadeBy: VERSION_MADE_BY,
117
+ versionNeeded: VERSION_NEEDED
118
+ });
119
+ }
120
+ function buildCentralDirectorySection(processedEntries, centralDirOffset, zipComment) {
121
+ const chunks = [];
122
+ for (const entry of processedEntries) {
123
+ chunks.push(buildCentralDirHeaderChunk(entry));
124
+ }
125
+ const centralDirSize = sumUint8ArrayLengths(chunks);
126
+ chunks.push(buildEndOfCentralDirectory({
127
+ entryCount: processedEntries.length,
128
+ centralDirSize,
129
+ centralDirOffset,
130
+ comment: zipComment
131
+ }));
132
+ return chunks;
133
+ }
134
+ /**
135
+ * Create a ZIP file from entries (async)
136
+ *
137
+ * @param entries - Files to include in ZIP
138
+ * @param options - ZIP options
139
+ * @returns ZIP file as Uint8Array
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * const zip = await createZip([
144
+ * { name: "hello.txt", data: new TextEncoder().encode("Hello!") },
145
+ * { name: "folder/file.txt", data: new TextEncoder().encode("Nested!") }
146
+ * ], { level: 6 });
147
+ * ```
148
+ */
149
+ export async function createZip(entries, options = {}) {
150
+ const level = options.level ?? DEFAULT_ZIP_LEVEL;
151
+ const timestamps = options.timestamps ?? DEFAULT_ZIP_TIMESTAMPS;
152
+ const zipComment = encodeZipComment(options.comment);
153
+ const defaultModTime = new Date();
154
+ const settings = {
155
+ level,
156
+ timestamps,
157
+ defaultModTime
158
+ };
159
+ const compressOptions = {
160
+ level,
161
+ thresholdBytes: options.thresholdBytes
162
+ };
163
+ const compressedDatas = await Promise.all(entries.map(async (entry) => {
164
+ return shouldDeflate(level, entry.data) ? compress(entry.data, compressOptions) : entry.data;
165
+ }));
166
+ // Process entries
167
+ const processedEntries = [];
168
+ let currentOffset = 0;
169
+ for (let i = 0; i < entries.length; i++) {
170
+ const entry = entries[i];
171
+ const compressedData = compressedDatas[i];
172
+ const result = appendProcessedEntry(processedEntries, entry, compressedData, currentOffset, settings);
173
+ currentOffset = result.nextOffset;
174
+ }
175
+ return finalizeZip(processedEntries, zipComment, currentOffset);
176
+ }
177
+ /**
178
+ * Create a ZIP file from entries (sync)
179
+ *
180
+ * This is supported in both Node.js and browser builds.
181
+ */
182
+ export function createZipSync(entries, options = {}) {
183
+ const level = options.level ?? DEFAULT_ZIP_LEVEL;
184
+ const timestamps = options.timestamps ?? DEFAULT_ZIP_TIMESTAMPS;
185
+ const zipComment = encodeZipComment(options.comment);
186
+ const defaultModTime = new Date();
187
+ const settings = {
188
+ level,
189
+ timestamps,
190
+ defaultModTime
191
+ };
192
+ const compressOptions = {
193
+ level,
194
+ thresholdBytes: options.thresholdBytes
195
+ };
196
+ // Process entries
197
+ const processedEntries = [];
198
+ let currentOffset = 0;
199
+ for (const entry of entries) {
200
+ // Compress data
201
+ const compressedData = shouldDeflate(level, entry.data)
202
+ ? compressSync(entry.data, compressOptions)
203
+ : entry.data;
204
+ const result = appendProcessedEntry(processedEntries, entry, compressedData, currentOffset, settings);
205
+ currentOffset = result.nextOffset;
206
+ }
207
+ return finalizeZip(processedEntries, zipComment, currentOffset);
208
+ }
209
+ /**
210
+ * Streaming ZIP builder for large files
211
+ * Writes chunks to a callback as they are generated
212
+ */
213
+ export class ZipBuilder {
214
+ /**
215
+ * Create a new ZIP builder
216
+ * @param options - ZIP options
217
+ */
218
+ constructor(options = {}) {
219
+ this.entries = [];
220
+ this.currentOffset = 0;
221
+ this.finalized = false;
222
+ this.level = options.level ?? DEFAULT_ZIP_LEVEL;
223
+ this.zipComment = encodeZipComment(options.comment);
224
+ this.timestamps = options.timestamps ?? DEFAULT_ZIP_TIMESTAMPS;
225
+ this.compressOptions = {
226
+ level: this.level,
227
+ thresholdBytes: options.thresholdBytes
228
+ };
229
+ this.settings = {
230
+ level: this.level,
231
+ timestamps: this.timestamps,
232
+ defaultModTime: new Date()
233
+ };
234
+ }
235
+ /**
236
+ * Add a file to the ZIP (async)
237
+ * @param entry - File entry
238
+ * @returns Local file header and compressed data chunks
239
+ */
240
+ async addFile(entry) {
241
+ if (this.finalized) {
242
+ throw new Error("Cannot add files after finalizing");
243
+ }
244
+ // Compress data
245
+ const compressedData = shouldDeflate(this.level, entry.data)
246
+ ? await compress(entry.data, this.compressOptions)
247
+ : entry.data;
248
+ const result = appendProcessedEntry(this.entries, entry, compressedData, this.currentOffset, this.settings);
249
+ this.currentOffset = result.nextOffset;
250
+ return [buildLocalFileHeaderChunk(result.processedEntry), compressedData];
251
+ }
252
+ /**
253
+ * Add a file to the ZIP (sync)
254
+ * @param entry - File entry
255
+ * @returns Local file header and compressed data chunks
256
+ */
257
+ addFileSync(entry) {
258
+ if (this.finalized) {
259
+ throw new Error("Cannot add files after finalizing");
260
+ }
261
+ // Compress data
262
+ const compressedData = shouldDeflate(this.level, entry.data)
263
+ ? compressSync(entry.data, this.compressOptions)
264
+ : entry.data;
265
+ const result = appendProcessedEntry(this.entries, entry, compressedData, this.currentOffset, this.settings);
266
+ this.currentOffset = result.nextOffset;
267
+ return [buildLocalFileHeaderChunk(result.processedEntry), compressedData];
268
+ }
269
+ /**
270
+ * Finalize the ZIP and return central directory + end record
271
+ * @returns Central directory and end of central directory chunks
272
+ */
273
+ finalize() {
274
+ if (this.finalized) {
275
+ throw new Error("ZIP already finalized");
276
+ }
277
+ this.finalized = true;
278
+ return buildCentralDirectorySection(this.entries, this.currentOffset, this.zipComment);
279
+ }
280
+ /**
281
+ * Get current number of entries
282
+ */
283
+ get entryCount() {
284
+ return this.entries.length;
285
+ }
286
+ /**
287
+ * Get current ZIP data size (without central directory)
288
+ */
289
+ get dataSize() {
290
+ return this.currentOffset;
291
+ }
292
+ }