zip-writer 1.0.0 → 1.1.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 (46) hide show
  1. package/README.md +427 -0
  2. package/dist/constants.d.ts +21 -0
  3. package/dist/constants.d.ts.map +1 -0
  4. package/{src/constants.ts → dist/constants.js} +1 -5
  5. package/dist/constants.js.map +1 -0
  6. package/{src/crc.ts → dist/crc-browser.d.ts} +2 -39
  7. package/dist/crc-browser.d.ts.map +1 -0
  8. package/dist/crc-browser.js +82 -0
  9. package/dist/crc-browser.js.map +1 -0
  10. package/dist/crc-node.d.ts +2 -0
  11. package/dist/crc-node.d.ts.map +1 -0
  12. package/dist/crc-node.js +3 -0
  13. package/dist/crc-node.js.map +1 -0
  14. package/dist/index.d.ts +138 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +298 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/utils.d.ts +4 -0
  19. package/dist/utils.d.ts.map +1 -0
  20. package/dist/utils.js +12 -0
  21. package/dist/utils.js.map +1 -0
  22. package/dist/write-central-directory.d.ts +14 -0
  23. package/dist/write-central-directory.d.ts.map +1 -0
  24. package/dist/write-central-directory.js +279 -0
  25. package/dist/write-central-directory.js.map +1 -0
  26. package/dist/write-entry.d.ts +26 -0
  27. package/dist/write-entry.d.ts.map +1 -0
  28. package/dist/write-entry.js +172 -0
  29. package/dist/write-entry.js.map +1 -0
  30. package/package.json +32 -5
  31. package/.claude/settings.local.json +0 -11
  32. package/.github/workflows/test.yml +0 -36
  33. package/.husky/pre-commit +0 -1
  34. package/eslint.config.mjs +0 -9
  35. package/src/crc-lookup.ts +0 -27
  36. package/src/index.ts +0 -342
  37. package/src/utils.ts +0 -17
  38. package/src/write-central-directory.ts +0 -405
  39. package/src/write-entry.ts +0 -246
  40. package/test/commands.ts +0 -47
  41. package/test/crc.test.ts +0 -45
  42. package/test/integration.test.ts +0 -483
  43. package/test/utils.ts +0 -40
  44. package/tsconfig.json +0 -36
  45. package/tsconfig.node.json +0 -10
  46. package/vitest.config.ts +0 -46
@@ -1,405 +0,0 @@
1
- import type { EntryInfo, EntryInfoStandard, EntryInfoZip64 } from "./index.js";
2
- import {
3
- CENTRAL_DIRECTORY_SIGNATURE,
4
- END_OF_CENTRAL_DIR_SIGNATURE,
5
- ZIP64_END_OF_CENTRAL_DIR_SIGNATURE,
6
- ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE,
7
- VERSION_MADE_BY,
8
- VERSION_NEEDED_STANDARD,
9
- VERSION_NEEDED_ZIP64,
10
- GENERAL_PURPOSE_FLAGS,
11
- COMPRESSION_METHOD_STORE,
12
- COMPRESSION_METHOD_DEFLATE,
13
- ZIP64_LIMIT,
14
- CENTRAL_DIRECTORY_HEADER_SIZE,
15
- EOCD_SIZE,
16
- EOCD64_SIZE,
17
- EOCD64_LOCATOR_SIZE,
18
- } from "./constants.js";
19
- import { getDosTime, getDosDate } from "./utils.js";
20
-
21
- const textEncoder = new TextEncoder();
22
-
23
- /**
24
- * Generate the Central Directory File Header for the given entry info.
25
- */
26
- export function getCDFH(entryInfo: EntryInfo): Uint8Array<ArrayBuffer> {
27
- if (entryInfo.zip64) {
28
- return getCDFHZip64(entryInfo);
29
- } else {
30
- return getCDFHStandard(entryInfo);
31
- }
32
- }
33
-
34
- function getCDFHStandard(
35
- entryInfo: EntryInfoStandard
36
- ): Uint8Array<ArrayBuffer> {
37
- const nameBytes = textEncoder.encode(entryInfo.name);
38
- const commentBytes = entryInfo.comment
39
- ? textEncoder.encode(entryInfo.comment)
40
- : new Uint8Array(0);
41
-
42
- const headerSize =
43
- CENTRAL_DIRECTORY_HEADER_SIZE + nameBytes.length + commentBytes.length;
44
- const header = new Uint8Array(headerSize);
45
- const view = new DataView(header.buffer);
46
-
47
- let offset = 0;
48
-
49
- // Central directory file header signature
50
- view.setUint32(offset, CENTRAL_DIRECTORY_SIGNATURE);
51
- offset += 4;
52
-
53
- // Version made by
54
- view.setUint16(offset, VERSION_MADE_BY, true);
55
- offset += 2;
56
-
57
- // Version needed to extract
58
- view.setUint16(offset, VERSION_NEEDED_STANDARD, true);
59
- offset += 2;
60
-
61
- // General purpose bit flag
62
- view.setUint16(offset, GENERAL_PURPOSE_FLAGS);
63
- offset += 2;
64
-
65
- // Compression method
66
- view.setUint16(
67
- offset,
68
- entryInfo.store ? COMPRESSION_METHOD_STORE : COMPRESSION_METHOD_DEFLATE,
69
- true
70
- );
71
- offset += 2;
72
-
73
- // Last mod file time & date
74
- const date = entryInfo.date || new Date();
75
- view.setUint16(offset, getDosTime(date), true);
76
- offset += 2;
77
- view.setUint16(offset, getDosDate(date), true);
78
- offset += 2;
79
-
80
- // CRC-32
81
- view.setUint32(offset, entryInfo.crc32, true);
82
- offset += 4;
83
-
84
- // Compressed size
85
- view.setUint32(offset, entryInfo.compressedSize, true);
86
- offset += 4;
87
-
88
- // Uncompressed size
89
- view.setUint32(offset, entryInfo.uncompressedSize, true);
90
- offset += 4;
91
-
92
- // File name length
93
- view.setUint16(offset, nameBytes.length, true);
94
- offset += 2;
95
-
96
- // Extra field length
97
- view.setUint16(offset, 0, true);
98
- offset += 2;
99
-
100
- // File comment length
101
- view.setUint16(offset, commentBytes.length, true);
102
- offset += 2;
103
-
104
- // Disk number start
105
- view.setUint16(offset, 0, true);
106
- offset += 2;
107
-
108
- // Internal file attributes
109
- view.setUint16(offset, 0, true);
110
- offset += 2;
111
-
112
- // External file attributes (Unix permissions if provided)
113
- const externalAttrs = entryInfo.mode ? entryInfo.mode << 16 : 0;
114
- view.setUint32(offset, externalAttrs, true);
115
- offset += 4;
116
-
117
- // Relative offset of local header
118
- view.setUint32(offset, entryInfo.startOffset, true);
119
- offset += 4;
120
-
121
- // File name
122
- header.set(nameBytes, offset);
123
- offset += nameBytes.length;
124
-
125
- // File comment
126
- if (commentBytes.length > 0) {
127
- header.set(commentBytes, offset);
128
- }
129
-
130
- return header;
131
- }
132
-
133
- function getCDFHZip64(entryInfo: EntryInfoZip64): Uint8Array<ArrayBuffer> {
134
- const nameBytes = textEncoder.encode(entryInfo.name);
135
- const commentBytes = entryInfo.comment
136
- ? textEncoder.encode(entryInfo.comment)
137
- : new Uint8Array(0);
138
-
139
- // Extra field for ZIP64
140
- const extraField = new Uint8Array(28); // ZIP64 extra field
141
- const extraView = new DataView(extraField.buffer);
142
- extraView.setUint16(0, 0x0001, true); // ZIP64 extra field tag
143
- extraView.setUint16(2, 24, true); // Size of extra field data (3x 8-byte fields)
144
- extraView.setBigUint64(4, entryInfo.uncompressedSize, true); // Original size
145
- extraView.setBigUint64(12, entryInfo.compressedSize, true); // Compressed size
146
- extraView.setBigUint64(20, entryInfo.startOffset, true); // Relative header offset
147
-
148
- const headerSize =
149
- CENTRAL_DIRECTORY_HEADER_SIZE +
150
- nameBytes.length +
151
- extraField.length +
152
- commentBytes.length;
153
- const header = new Uint8Array(headerSize);
154
- const view = new DataView(header.buffer);
155
-
156
- let offset = 0;
157
-
158
- // Central directory file header signature
159
- view.setUint32(offset, CENTRAL_DIRECTORY_SIGNATURE);
160
- offset += 4;
161
-
162
- // Version made by
163
- view.setUint16(offset, VERSION_MADE_BY, true);
164
- offset += 2;
165
-
166
- // Version needed to extract
167
- view.setUint16(offset, VERSION_NEEDED_ZIP64, true);
168
- offset += 2;
169
-
170
- // General purpose bit flag
171
- view.setUint16(offset, GENERAL_PURPOSE_FLAGS);
172
- offset += 2;
173
-
174
- // Compression method
175
- view.setUint16(
176
- offset,
177
- entryInfo.store ? COMPRESSION_METHOD_STORE : COMPRESSION_METHOD_DEFLATE,
178
- true
179
- );
180
- offset += 2;
181
-
182
- // Last mod file time & date
183
- const date = entryInfo.date || new Date();
184
- view.setUint16(offset, getDosTime(date), true);
185
- offset += 2;
186
- view.setUint16(offset, getDosDate(date), true);
187
- offset += 2;
188
-
189
- // CRC-32
190
- view.setUint32(offset, entryInfo.crc32, true);
191
- offset += 4;
192
-
193
- // Compressed size (0xFFFFFFFF for ZIP64)
194
- view.setUint32(offset, 0xffffffff, true);
195
- offset += 4;
196
-
197
- // Uncompressed size (0xFFFFFFFF for ZIP64)
198
- view.setUint32(offset, 0xffffffff, true);
199
- offset += 4;
200
-
201
- // File name length
202
- view.setUint16(offset, nameBytes.length, true);
203
- offset += 2;
204
-
205
- // Extra field length
206
- view.setUint16(offset, extraField.length, true);
207
- offset += 2;
208
-
209
- // File comment length
210
- view.setUint16(offset, commentBytes.length, true);
211
- offset += 2;
212
-
213
- // Disk number start
214
- view.setUint16(offset, 0, true);
215
- offset += 2;
216
-
217
- // Internal file attributes
218
- view.setUint16(offset, 0, true);
219
- offset += 2;
220
-
221
- // External file attributes (Unix permissions if provided)
222
- const externalAttrs = entryInfo.mode ? entryInfo.mode << 16 : 0;
223
- view.setUint32(offset, externalAttrs, true);
224
- offset += 4;
225
-
226
- // Relative offset of local header (0xFFFFFFFF for ZIP64)
227
- view.setUint32(offset, 0xffffffff, true);
228
- offset += 4;
229
-
230
- // File name
231
- header.set(nameBytes, offset);
232
- offset += nameBytes.length;
233
-
234
- // Extra field
235
- header.set(extraField, offset);
236
- offset += extraField.length;
237
-
238
- // File comment
239
- if (commentBytes.length > 0) {
240
- header.set(commentBytes, offset);
241
- }
242
-
243
- return header;
244
- }
245
-
246
- /**
247
- * Generate the End of Central Directory record with zip64 support.
248
- */
249
- export function getEOCDStandard(
250
- entriesCount: number,
251
- centralDirectoryOffset: number,
252
- centralDirectorySize: number
253
- ): Uint8Array<ArrayBuffer> {
254
- const eocd = new Uint8Array(EOCD_SIZE);
255
- const view = new DataView(eocd.buffer);
256
-
257
- let offset = 0;
258
-
259
- // End of central dir signature
260
- view.setUint32(offset, END_OF_CENTRAL_DIR_SIGNATURE);
261
- offset += 4;
262
-
263
- // Number of this disk
264
- view.setUint16(offset, 0, true);
265
- offset += 2;
266
-
267
- // Disk where central directory starts
268
- view.setUint16(offset, 0, true);
269
- offset += 2;
270
-
271
- // Number of central directory records on this disk
272
- view.setUint16(offset, entriesCount, true);
273
- offset += 2;
274
-
275
- // Total number of central directory records
276
- view.setUint16(offset, entriesCount, true);
277
- offset += 2;
278
-
279
- // Size of central directory
280
- view.setUint32(offset, Number(centralDirectorySize), true);
281
- offset += 4;
282
-
283
- // Offset of start of central directory
284
- view.setUint32(offset, Number(centralDirectoryOffset), true);
285
- offset += 4;
286
-
287
- // ZIP file comment length
288
- view.setUint16(offset, 0, true);
289
-
290
- return eocd;
291
- }
292
-
293
- /**
294
- * Generate the ZIP64 End of Central Directory record.
295
- */
296
- export function getEOCDZip64(
297
- entriesCount: bigint,
298
- centralDirectoryOffset: bigint,
299
- centralDirectorySize: bigint
300
- ): Uint8Array<ArrayBuffer> {
301
- const totalSize = EOCD64_SIZE + EOCD64_LOCATOR_SIZE + EOCD_SIZE;
302
- const buffer = new Uint8Array(totalSize);
303
- const view = new DataView(buffer.buffer);
304
-
305
- let offset = 0;
306
-
307
- // === ZIP64 End of Central Directory Record ===
308
-
309
- // Signature
310
- view.setUint32(offset, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE);
311
- offset += 4;
312
-
313
- // Size of zip64 end of central directory record excluding the leading 12 bytes
314
- view.setBigUint64(offset, BigInt(EOCD64_SIZE - 12), true);
315
- offset += 8;
316
-
317
- // Version made by
318
- view.setUint16(offset, VERSION_MADE_BY, true);
319
- offset += 2;
320
-
321
- // Version needed to extract
322
- view.setUint16(offset, VERSION_NEEDED_ZIP64, true);
323
- offset += 2;
324
-
325
- // Number of this disk
326
- view.setUint32(offset, 0, true);
327
- offset += 4;
328
-
329
- // Disk where central directory starts
330
- view.setUint32(offset, 0, true);
331
- offset += 4;
332
-
333
- // Number of central directory records on this disk
334
- view.setBigUint64(offset, entriesCount, true);
335
- offset += 8;
336
-
337
- // Total number of central directory records
338
- view.setBigUint64(offset, entriesCount, true);
339
- offset += 8;
340
-
341
- // Size of central directory
342
- view.setBigUint64(offset, centralDirectorySize, true);
343
- offset += 8;
344
-
345
- // Offset of start of central directory
346
- view.setBigUint64(offset, centralDirectoryOffset, true);
347
- offset += 8;
348
-
349
- // === ZIP64 End of Central Directory Locator ===
350
-
351
- // Signature
352
- view.setUint32(offset, ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE);
353
- offset += 4;
354
-
355
- // Number of the disk with the start of the zip64 end of central directory
356
- view.setUint32(offset, 0, true);
357
- offset += 4;
358
-
359
- // Relative offset of the zip64 end of central directory record
360
- view.setBigUint64(
361
- offset,
362
- centralDirectoryOffset + centralDirectorySize,
363
- true
364
- );
365
- offset += 8;
366
-
367
- // Total number of disks
368
- view.setUint32(offset, 1, true);
369
- offset += 4;
370
-
371
- // === Standard End of Central Directory Record ===
372
-
373
- // Signature
374
- view.setUint32(offset, END_OF_CENTRAL_DIR_SIGNATURE);
375
- offset += 4;
376
-
377
- // Number of this disk
378
- view.setUint16(offset, 0xffff, true);
379
- offset += 2;
380
-
381
- // Disk where central directory starts
382
- view.setUint16(offset, 0xffff, true);
383
- offset += 2;
384
-
385
- // Number of central directory records on this disk
386
- view.setUint16(offset, 0xffff, true);
387
- offset += 2;
388
-
389
- // Total number of central directory records
390
- view.setUint16(offset, 0xffff, true);
391
- offset += 2;
392
-
393
- // Size of central directory
394
- view.setUint32(offset, 0xffffffff, true);
395
- offset += 4;
396
-
397
- // Offset of start of central directory
398
- view.setUint32(offset, 0xffffffff, true);
399
- offset += 4;
400
-
401
- // ZIP file comment length
402
- view.setUint16(offset, 0, true);
403
-
404
- return buffer;
405
- }
@@ -1,246 +0,0 @@
1
- import type {
2
- EntryInfoStandard,
3
- EntryInfoZip64,
4
- EntryOptions,
5
- } from "./index.js";
6
- import {
7
- LOCAL_FILE_HEADER_SIGNATURE,
8
- DATA_DESCRIPTOR_SIGNATURE,
9
- VERSION_NEEDED_STANDARD,
10
- GENERAL_PURPOSE_FLAGS,
11
- COMPRESSION_METHOD_STORE,
12
- COMPRESSION_METHOD_DEFLATE,
13
- LOCAL_FILE_HEADER_SIZE,
14
- ZIP64_LIMIT,
15
- DATA_DESCRIPTOR_SIZE,
16
- DATA_DESCRIPTOR_SIZE_ZIP64,
17
- } from "./constants.js";
18
- import { getDosTime, getDosDate } from "./utils.js";
19
- import { crc32 } from "./crc.js";
20
-
21
- const textEncoder = new TextEncoder();
22
-
23
- /**
24
- * Write a zip entry to the given writer from the given reader.
25
- */
26
- export async function writeZipEntry({
27
- writer,
28
- reader,
29
- entryOptions,
30
- startOffset,
31
- }: {
32
- /** Writer for the zip archive */
33
- writer: WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>;
34
- /** Reader for the entry data */
35
- reader: ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>;
36
- /** Options for the zip entry */
37
- entryOptions: EntryOptions;
38
- /** Offset in the zip archive where this entry starts */
39
- startOffset: bigint;
40
- }) {
41
- let uncompressedSize = BigInt(0);
42
- let compressedSize = BigInt(0);
43
- let crc = 0;
44
-
45
- await writer.write(getLocalFileHeader(entryOptions));
46
-
47
- if (entryOptions.store) {
48
- // Store mode: no compression, direct copy
49
- while (true) {
50
- const { value, done } = await reader.read();
51
- if (done) break;
52
- const byteLength = BigInt(value.byteLength);
53
- uncompressedSize += byteLength;
54
- compressedSize += byteLength;
55
- crc = crc32(value, crc);
56
- await writer.write(value);
57
- }
58
- } else {
59
- // Deflate mode: manual reader/writer loops to minimize overhead
60
- const compressionStream = new CompressionStream("deflate-raw");
61
- const compressionWriter = compressionStream.writable.getWriter();
62
- const compressionReader = compressionStream.readable.getReader();
63
-
64
- const writePromise = (async () => {
65
- try {
66
- while (true) {
67
- const { value, done } = await reader.read();
68
- if (done) break;
69
- uncompressedSize += BigInt(value.byteLength);
70
- crc = crc32(value, crc);
71
- await compressionWriter.write(value);
72
- }
73
- await compressionWriter.close();
74
- } catch (err) {
75
- await compressionWriter.abort(err);
76
- throw err;
77
- }
78
- })();
79
-
80
- const readPromise = (async () => {
81
- try {
82
- while (true) {
83
- const { value, done } = await compressionReader.read();
84
- if (done) break;
85
- compressedSize += BigInt(value.byteLength);
86
- await writer.write(value);
87
- }
88
- compressionReader.releaseLock();
89
- } catch (err) {
90
- await compressionReader.cancel(err);
91
- throw err;
92
- }
93
- })();
94
-
95
- await Promise.all([writePromise, readPromise]);
96
- }
97
-
98
- const needsZip64 =
99
- uncompressedSize > ZIP64_LIMIT ||
100
- compressedSize > ZIP64_LIMIT ||
101
- startOffset > ZIP64_LIMIT;
102
-
103
- if (needsZip64) {
104
- const entryInfo: EntryInfoZip64 = {
105
- ...entryOptions,
106
- startOffset,
107
- crc32: crc,
108
- uncompressedSize,
109
- compressedSize,
110
- zip64: true,
111
- };
112
- await writer.write(getDataDescriptorZip64(entryInfo));
113
- return entryInfo;
114
- } else {
115
- const entryInfo: EntryInfoStandard = {
116
- ...entryOptions,
117
- startOffset: Number(startOffset),
118
- crc32: crc,
119
- uncompressedSize: Number(uncompressedSize),
120
- compressedSize: Number(compressedSize),
121
- zip64: false,
122
- };
123
- await writer.write(getDataDescriptorStandard(entryInfo));
124
- return entryInfo;
125
- }
126
- }
127
-
128
- export function getLocalFileHeader(
129
- options: EntryOptions
130
- ): Uint8Array<ArrayBuffer> {
131
- const nameBytes = textEncoder.encode(options.name);
132
-
133
- const headerSize = LOCAL_FILE_HEADER_SIZE + nameBytes.length;
134
- const header = new Uint8Array(headerSize);
135
- const view = new DataView(header.buffer);
136
-
137
- let offset = 0;
138
-
139
- // Local file header signature
140
- view.setUint32(offset, LOCAL_FILE_HEADER_SIGNATURE);
141
- offset += 4;
142
-
143
- // Version needed to extract
144
- view.setUint16(offset, VERSION_NEEDED_STANDARD);
145
- offset += 2;
146
-
147
- // General purpose bit flag
148
- view.setUint16(offset, GENERAL_PURPOSE_FLAGS);
149
- offset += 2;
150
-
151
- // Compression method
152
- view.setUint16(
153
- offset,
154
- options.store ? COMPRESSION_METHOD_STORE : COMPRESSION_METHOD_DEFLATE,
155
- true
156
- );
157
- offset += 2;
158
-
159
- // Last mod file time & date (MS-DOS format)
160
- const date = options.date || new Date();
161
- view.setUint16(offset, getDosTime(date), true);
162
- offset += 2;
163
- view.setUint16(offset, getDosDate(date), true);
164
- offset += 2;
165
-
166
- // CRC-32 (0 when using data descriptor)
167
- view.setUint32(offset, 0, true);
168
- offset += 4;
169
-
170
- // Compressed size (0 when using data descriptor)
171
- view.setUint32(offset, 0, true);
172
- offset += 4;
173
-
174
- // Uncompressed size (0 when using data descriptor)
175
- view.setUint32(offset, 0, true);
176
- offset += 4;
177
-
178
- // File name length
179
- view.setUint16(offset, nameBytes.length, true);
180
- offset += 2;
181
-
182
- // Extra field length
183
- view.setUint16(offset, 0, true);
184
- offset += 2;
185
-
186
- // File name
187
- header.set(nameBytes, offset);
188
-
189
- return header;
190
- }
191
-
192
- export function getDataDescriptorStandard(entryInfo: {
193
- uncompressedSize: number;
194
- compressedSize: number;
195
- }): Uint8Array<ArrayBuffer> {
196
- const descriptor = new Uint8Array(DATA_DESCRIPTOR_SIZE);
197
- const view = new DataView(descriptor.buffer);
198
-
199
- let offset = 0;
200
-
201
- // Data descriptor signature
202
- view.setUint32(offset, DATA_DESCRIPTOR_SIGNATURE);
203
- offset += 4;
204
-
205
- // CRC-32 (TODO: implement CRC calculation)
206
- view.setUint32(offset, 0, true);
207
- offset += 4;
208
-
209
- // Compressed size (4 bytes)
210
- view.setUint32(offset, entryInfo.compressedSize, true);
211
- offset += 4;
212
-
213
- // Uncompressed size (4 bytes)
214
- view.setUint32(offset, entryInfo.uncompressedSize, true);
215
- offset += 4;
216
-
217
- return descriptor;
218
- }
219
-
220
- export function getDataDescriptorZip64(entryInfo: {
221
- uncompressedSize: bigint;
222
- compressedSize: bigint;
223
- }): Uint8Array<ArrayBuffer> {
224
- const descriptor = new Uint8Array(DATA_DESCRIPTOR_SIZE_ZIP64);
225
- const view = new DataView(descriptor.buffer);
226
-
227
- let offset = 0;
228
-
229
- // Data descriptor signature
230
- view.setUint32(offset, DATA_DESCRIPTOR_SIGNATURE);
231
- offset += 4;
232
-
233
- // CRC-32 (TODO: implement CRC calculation)
234
- view.setUint32(offset, 0, true);
235
- offset += 4;
236
-
237
- // Compressed size (8 bytes for ZIP64)
238
- view.setBigUint64(offset, entryInfo.compressedSize, true);
239
- offset += 8;
240
-
241
- // Uncompressed size (8 bytes for ZIP64)
242
- view.setBigUint64(offset, entryInfo.uncompressedSize, true);
243
- offset += 8;
244
-
245
- return descriptor;
246
- }
package/test/commands.ts DELETED
@@ -1,47 +0,0 @@
1
- import type { BrowserCommand } from "vitest/node";
2
- import yauzl from "yauzl-promise";
3
- import { createHash } from "crypto";
4
- import type { ZipEntryInfo } from "./utils.js";
5
-
6
- /**
7
- * Validate a ZIP file buffer using yauzl-promise in Node context.
8
- * Returns entry information including SHA256 hashes of content.
9
- */
10
- export const validateZip: BrowserCommand<[zipBuffer: any]> = async (
11
- _ctx,
12
- dataAsHex: string
13
- ): Promise<ZipEntryInfo[]> => {
14
- const buffer = Buffer.from(dataAsHex, "hex");
15
- const zipFile = await yauzl.fromBuffer(buffer);
16
- const entries: ZipEntryInfo[] = [];
17
-
18
- for await (const entry of zipFile) {
19
- // Read entry content and calculate SHA256
20
- const readStream = await entry.openReadStream();
21
- const chunks: Buffer[] = [];
22
- const hash = createHash("sha256");
23
-
24
- await new Promise<void>((resolve, reject) => {
25
- readStream.on("data", (chunk: Buffer) => {
26
- chunks.push(chunk);
27
- hash.update(chunk);
28
- });
29
- readStream.on("end", () => resolve());
30
- readStream.on("error", reject);
31
- });
32
-
33
- const isDirectory = entry.filename.endsWith("/");
34
-
35
- entries.push({
36
- filename: entry.filename,
37
- uncompressedSize: entry.uncompressedSize,
38
- compressedSize: entry.compressedSize,
39
- compressionMethod: entry.compressionMethod,
40
- sha256: hash.digest("hex"),
41
- isDirectory,
42
- externalFileAttributes: entry.externalFileAttributes,
43
- });
44
- }
45
-
46
- return entries;
47
- };