@php-wasm/stream-compression 0.0.1 → 0.9.16

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 (59) hide show
  1. package/LICENSE +339 -0
  2. package/index.cjs +1 -0
  3. package/index.d.ts +143 -0
  4. package/index.js +546 -0
  5. package/package.json +36 -29
  6. package/{src/test/vitest-setup-file.ts → test/vitest-setup-file.d.ts} +0 -1
  7. package/utils/append-bytes.d.ts +7 -0
  8. package/utils/collect-bytes.d.ts +8 -0
  9. package/utils/collect-file.d.ts +8 -0
  10. package/utils/collect-string.d.ts +8 -0
  11. package/utils/concat-bytes.d.ts +9 -0
  12. package/utils/concat-string.d.ts +6 -0
  13. package/utils/concat-uint8-array.d.ts +7 -0
  14. package/utils/filter-stream.d.ts +7 -0
  15. package/utils/iterable-stream-polyfill.d.ts +1 -0
  16. package/utils/iterator-to-stream.d.ts +8 -0
  17. package/utils/limit-bytes.d.ts +8 -0
  18. package/utils/prepend-bytes.d.ts +7 -0
  19. package/utils/skip-first-bytes.d.ts +7 -0
  20. package/utils/skip-last-bytes.d.ts +7 -0
  21. package/utils/streamed-file.d.ts +39 -0
  22. package/zip/decode-remote-zip.d.ts +14 -0
  23. package/zip/decode-zip.d.ts +82 -0
  24. package/zip/encode-zip.d.ts +7 -0
  25. package/{src/zip/index.ts → zip/index.d.ts} +0 -2
  26. package/zip/types.d.ts +66 -0
  27. package/.eslintrc.json +0 -18
  28. package/project.json +0 -34
  29. package/src/index.ts +0 -7
  30. package/src/test/append-bytes.spec.ts +0 -25
  31. package/src/test/decode-zip.spec.ts +0 -22
  32. package/src/test/encode-zip.spec.ts +0 -47
  33. package/src/test/fixtures/hello-dolly.zip +0 -0
  34. package/src/test/prepend-bytes.spec.ts +0 -25
  35. package/src/test/skip-first-bytes.spec.ts +0 -41
  36. package/src/test/skip-last-bytes.spec.ts +0 -27
  37. package/src/utils/append-bytes.ts +0 -16
  38. package/src/utils/collect-bytes.ts +0 -24
  39. package/src/utils/collect-file.ts +0 -16
  40. package/src/utils/collect-string.ts +0 -25
  41. package/src/utils/concat-bytes.ts +0 -38
  42. package/src/utils/concat-string.ts +0 -17
  43. package/src/utils/concat-uint8-array.ts +0 -17
  44. package/src/utils/filter-stream.ts +0 -15
  45. package/src/utils/iterable-stream-polyfill.ts +0 -35
  46. package/src/utils/iterator-to-stream.ts +0 -39
  47. package/src/utils/limit-bytes.ts +0 -40
  48. package/src/utils/prepend-bytes.ts +0 -18
  49. package/src/utils/skip-first-bytes.ts +0 -21
  50. package/src/utils/skip-last-bytes.ts +0 -24
  51. package/src/utils/streamed-file.ts +0 -58
  52. package/src/zip/decode-remote-zip.ts +0 -409
  53. package/src/zip/decode-zip.ts +0 -349
  54. package/src/zip/encode-zip.ts +0 -278
  55. package/src/zip/types.ts +0 -76
  56. package/tsconfig.json +0 -23
  57. package/tsconfig.lib.json +0 -14
  58. package/tsconfig.spec.json +0 -25
  59. package/vite.config.ts +0 -55
@@ -1,349 +0,0 @@
1
- /**
2
- * Reads files from a stream of zip file bytes.
3
- */
4
- import { IterableReadableStream } from '../utils/iterable-stream-polyfill';
5
-
6
- import {
7
- SIGNATURE_FILE,
8
- SIGNATURE_CENTRAL_DIRECTORY,
9
- SIGNATURE_CENTRAL_DIRECTORY_END,
10
- FILE_HEADER_SIZE,
11
- COMPRESSION_DEFLATE,
12
- CompressionMethod,
13
- } from './types';
14
- import {
15
- CentralDirectoryEntry,
16
- FileEntry,
17
- ZipEntry,
18
- CentralDirectoryEndEntry,
19
- } from './types';
20
- import { filterStream } from '../utils/filter-stream';
21
- import { collectBytes } from '../utils/collect-bytes';
22
- import { limitBytes } from '../utils/limit-bytes';
23
- import { concatBytes } from '../utils/concat-bytes';
24
- import { prependBytes } from '../utils/prepend-bytes';
25
- import { appendBytes } from '../utils/append-bytes';
26
-
27
- /**
28
- * Unzips a stream of zip file bytes.
29
- *
30
- * @param stream A stream of zip file bytes.
31
- * @param predicate Optional. A function that returns true if the file should be downloaded.
32
- * @returns An iterable stream of File objects.
33
- */
34
- export function decodeZip(
35
- stream: ReadableStream<Uint8Array>,
36
- predicate?: () => boolean
37
- ) {
38
- return streamZippedFileEntries(stream, predicate).pipeThrough(
39
- new TransformStream<FileEntry, File>({
40
- async transform(zipEntry, controller) {
41
- const file = new File(
42
- [zipEntry.bytes],
43
- new TextDecoder().decode(zipEntry.path),
44
- {
45
- type: zipEntry.isDirectory ? 'directory' : undefined,
46
- }
47
- );
48
- controller.enqueue(file);
49
- },
50
- })
51
- ) as IterableReadableStream<File>;
52
- }
53
-
54
- const DEFAULT_PREDICATE = () => true;
55
-
56
- /**
57
- * Parses a stream of zipped bytes into FileEntry informations.
58
- *
59
- * @param stream A stream of zip file bytes.
60
- * @param predicate Optional. A function that returns true if the file should be downloaded.
61
- * @returns An iterable stream of FileEntry objects.
62
- */
63
- export function streamZippedFileEntries(
64
- stream: ReadableStream<Uint8Array>,
65
- predicate: (
66
- dirEntry: CentralDirectoryEntry | FileEntry
67
- ) => boolean = DEFAULT_PREDICATE
68
- ) {
69
- const entriesStream = new ReadableStream<ZipEntry>({
70
- async pull(controller) {
71
- const entry = await nextZipEntry(stream);
72
- if (!entry) {
73
- controller.close();
74
- return;
75
- }
76
- controller.enqueue(entry);
77
- },
78
- }) as IterableReadableStream<ZipEntry>;
79
-
80
- return entriesStream
81
- .pipeThrough(
82
- filterStream(({ signature }) => signature === SIGNATURE_FILE)
83
- )
84
- .pipeThrough(
85
- filterStream(predicate as any)
86
- ) as IterableReadableStream<FileEntry>;
87
- }
88
-
89
- /**
90
- * Reads the next zip entry from a stream of zip file bytes.
91
- *
92
- * @param stream A stream of zip file bytes.
93
- * @returns A FileEntry object.
94
- */
95
- async function nextZipEntry(stream: ReadableStream<Uint8Array>) {
96
- const sigData = new DataView((await collectBytes(stream, 4))!.buffer);
97
- const signature = sigData.getUint32(0, true);
98
- if (signature === SIGNATURE_FILE) {
99
- return await readFileEntry(stream, true);
100
- } else if (signature === SIGNATURE_CENTRAL_DIRECTORY) {
101
- return await readCentralDirectoryEntry(stream, true);
102
- } else if (signature === SIGNATURE_CENTRAL_DIRECTORY_END) {
103
- return await readEndCentralDirectoryEntry(stream, true);
104
- }
105
- return null;
106
- }
107
-
108
- /**
109
- * Reads a file entry from a zip file.
110
- *
111
- * The file entry is structured as follows:
112
- *
113
- * ```
114
- * Offset Bytes Description
115
- * 0 4 Local file header signature = 0x04034b50 (PK♥♦ or "PK\3\4")
116
- * 4 2 Version needed to extract (minimum)
117
- * 6 2 General purpose bit flag
118
- * 8 2 Compression method; e.g. none = 0, DEFLATE = 8 (or "\0x08\0x00")
119
- * 10 2 File last modification time
120
- * 12 2 File last modification date
121
- * 14 4 CRC-32 of uncompressed data
122
- * 18 4 Compressed size (or 0xffffffff for ZIP64)
123
- * 22 4 Uncompressed size (or 0xffffffff for ZIP64)
124
- * 26 2 File name length (n)
125
- * 28 2 Extra field length (m)
126
- * 30 n File name
127
- * 30+n m Extra field
128
- * ```
129
- *
130
- * @param stream
131
- * @param skipSignature Do not consume the signature from the stream.
132
- * @returns
133
- */
134
- export async function readFileEntry(
135
- stream: ReadableStream<Uint8Array>,
136
- skipSignature = false
137
- ): Promise<FileEntry | null> {
138
- if (!skipSignature) {
139
- const sigData = new DataView((await collectBytes(stream, 4))!.buffer);
140
- const signature = sigData.getUint32(0, true);
141
- if (signature !== SIGNATURE_FILE) {
142
- return null;
143
- }
144
- }
145
- const data = new DataView((await collectBytes(stream, 26))!.buffer);
146
- const pathLength = data.getUint16(22, true);
147
- const extraLength = data.getUint16(24, true);
148
- const entry: Partial<FileEntry> = {
149
- signature: SIGNATURE_FILE,
150
- version: data.getUint32(0, true),
151
- generalPurpose: data.getUint16(2, true),
152
- compressionMethod: data.getUint16(4, true) as CompressionMethod,
153
- lastModifiedTime: data.getUint16(6, true),
154
- lastModifiedDate: data.getUint16(8, true),
155
- crc: data.getUint32(10, true),
156
- compressedSize: data.getUint32(14, true),
157
- uncompressedSize: data.getUint32(18, true),
158
- };
159
-
160
- entry['path'] = await collectBytes(stream, pathLength);
161
- entry['isDirectory'] = endsWithSlash(entry.path!);
162
- entry['extra'] = await collectBytes(stream, extraLength);
163
-
164
- // Make sure we consume the body stream or else
165
- // we'll start reading the next file at the wrong
166
- // offset.
167
- // @TODO: Expose the body stream instead of reading it all
168
- // eagerly. Ensure the next iteration exhausts
169
- // the last body stream before moving on.
170
-
171
- let bodyStream = limitBytes(stream, entry['compressedSize']!);
172
-
173
- if (entry['compressionMethod'] === COMPRESSION_DEFLATE) {
174
- /**
175
- * We want to write raw deflate-compressed bytes into our
176
- * final ZIP file. CompressionStream supports "deflate-raw"
177
- * compression, but not on Node.js v18.
178
- *
179
- * As a workaround, we use the "gzip" compression and add
180
- * the header and footer bytes. It works, because "gzip"
181
- * compression is the same as "deflate" compression plus
182
- * the header and the footer.
183
- *
184
- * The header is 10 bytes long:
185
- * - 2 magic bytes: 0x1f, 0x8b
186
- * - 1 compression method: 0x08 (deflate)
187
- * - 1 header flags
188
- * - 4 mtime: 0x00000000 (no timestamp)
189
- * - 1 compression flags
190
- * - 1 OS: 0x03 (Unix)
191
- *
192
- * The footer is 8 bytes long:
193
- * - 4 bytes for CRC32 of the uncompressed data
194
- * - 4 bytes for ISIZE (uncompressed size modulo 2^32)
195
- */
196
- const header = new Uint8Array(10);
197
- header.set([0x1f, 0x8b, 0x08]);
198
-
199
- const footer = new Uint8Array(8);
200
- const footerView = new DataView(footer.buffer);
201
- footerView.setUint32(0, entry.crc!, true);
202
- footerView.setUint32(4, entry.uncompressedSize! % 2 ** 32, true);
203
- bodyStream = bodyStream
204
- .pipeThrough(prependBytes(header))
205
- .pipeThrough(appendBytes(footer))
206
- .pipeThrough(new DecompressionStream('gzip'));
207
- }
208
- entry['bytes'] = await bodyStream
209
- .pipeThrough(concatBytes(entry['uncompressedSize']))
210
- .getReader()
211
- .read()
212
- .then(({ value }) => value!);
213
- return entry as FileEntry;
214
- }
215
-
216
- /**
217
- * Reads a central directory entry from a zip file.
218
- *
219
- * The central directory entry is structured as follows:
220
- *
221
- * ```
222
- * Offset Bytes Description
223
- * 0 4 Central directory file header signature = 0x02014b50
224
- * 4 2 Version made by
225
- * 6 2 Version needed to extract (minimum)
226
- * 8 2 General purpose bit flag
227
- * 10 2 Compression method
228
- * 12 2 File last modification time
229
- * 14 2 File last modification date
230
- * 16 4 CRC-32 of uncompressed data
231
- * 20 4 Compressed size (or 0xffffffff for ZIP64)
232
- * 24 4 Uncompressed size (or 0xffffffff for ZIP64)
233
- * 28 2 File name length (n)
234
- * 30 2 Extra field length (m)
235
- * 32 2 File comment length (k)
236
- * 34 2 Disk number where file starts (or 0xffff for ZIP64)
237
- * 36 2 Internal file attributes
238
- * 38 4 External file attributes
239
- * 42 4 Relative offset of local file header (or 0xffffffff for ZIP64). This is the number of bytes between the start of the first disk on which the file occurs, and the start of the local file header. This allows software reading the central directory to locate the position of the file inside the ZIP file.
240
- * 46 n File name
241
- * 46+n m Extra field
242
- * 46+n+m k File comment
243
- * ```
244
- *
245
- * @param stream
246
- * @param skipSignature
247
- * @returns
248
- */
249
- export async function readCentralDirectoryEntry(
250
- stream: ReadableStream<Uint8Array>,
251
- skipSignature = false
252
- ): Promise<CentralDirectoryEntry | null> {
253
- if (!skipSignature) {
254
- const sigData = new DataView((await collectBytes(stream, 4))!.buffer);
255
- const signature = sigData.getUint32(0, true);
256
- if (signature !== SIGNATURE_CENTRAL_DIRECTORY) {
257
- return null;
258
- }
259
- }
260
- const data = new DataView((await collectBytes(stream, 42))!.buffer);
261
- const pathLength = data.getUint16(24, true);
262
- const extraLength = data.getUint16(26, true);
263
- const fileCommentLength = data.getUint16(28, true);
264
- const centralDirectory: Partial<CentralDirectoryEntry> = {
265
- signature: SIGNATURE_CENTRAL_DIRECTORY,
266
- versionCreated: data.getUint16(0, true),
267
- versionNeeded: data.getUint16(2, true),
268
- generalPurpose: data.getUint16(4, true),
269
- compressionMethod: data.getUint16(6, true) as CompressionMethod,
270
- lastModifiedTime: data.getUint16(8, true),
271
- lastModifiedDate: data.getUint16(10, true),
272
- crc: data.getUint32(12, true),
273
- compressedSize: data.getUint32(16, true),
274
- uncompressedSize: data.getUint32(20, true),
275
- diskNumber: data.getUint16(30, true),
276
- internalAttributes: data.getUint16(32, true),
277
- externalAttributes: data.getUint32(34, true),
278
- firstByteAt: data.getUint32(38, true),
279
- };
280
- centralDirectory['lastByteAt'] =
281
- centralDirectory.firstByteAt! +
282
- FILE_HEADER_SIZE +
283
- pathLength +
284
- fileCommentLength +
285
- extraLength! +
286
- centralDirectory.compressedSize! -
287
- 1;
288
-
289
- centralDirectory['path'] = await collectBytes(stream, pathLength);
290
- centralDirectory['isDirectory'] = endsWithSlash(centralDirectory.path!);
291
- centralDirectory['extra'] = await collectBytes(stream, extraLength);
292
- centralDirectory['fileComment'] = await collectBytes(
293
- stream,
294
- fileCommentLength
295
- );
296
- return centralDirectory as CentralDirectoryEntry;
297
- }
298
-
299
- function endsWithSlash(path: Uint8Array) {
300
- return path[path.byteLength - 1] == '/'.charCodeAt(0);
301
- }
302
-
303
- /**
304
- * Reads the end of central directory entry from a zip file.
305
- *
306
- * The end of central directory entry is structured as follows:
307
- *
308
- * ```
309
- * Offset Bytes Description[33]
310
- * 0 4 End of central directory signature = 0x06054b50
311
- * 4 2 Number of this disk (or 0xffff for ZIP64)
312
- * 6 2 Disk where central directory starts (or 0xffff for ZIP64)
313
- * 8 2 Number of central directory records on this disk (or 0xffff for ZIP64)
314
- * 10 2 Total number of central directory records (or 0xffff for ZIP64)
315
- * 12 4 Size of central directory (bytes) (or 0xffffffff for ZIP64)
316
- * 16 4 Offset of start of central directory, relative to start of archive (or 0xffffffff for ZIP64)
317
- * 20 2 Comment length (n)
318
- * 22 n Comment
319
- * ```
320
- *
321
- * @param stream
322
- * @param skipSignature
323
- * @returns
324
- */
325
- async function readEndCentralDirectoryEntry(
326
- stream: ReadableStream<Uint8Array>,
327
- skipSignature = false
328
- ) {
329
- if (!skipSignature) {
330
- const sigData = new DataView((await collectBytes(stream, 4))!.buffer);
331
- const signature = sigData.getUint32(0, true);
332
- if (signature !== SIGNATURE_CENTRAL_DIRECTORY_END) {
333
- return null;
334
- }
335
- }
336
- const data = new DataView((await collectBytes(stream, 18))!.buffer);
337
- const endOfDirectory: Partial<CentralDirectoryEndEntry> = {
338
- signature: SIGNATURE_CENTRAL_DIRECTORY_END,
339
- numberOfDisks: data.getUint16(0, true),
340
- centralDirectoryStartDisk: data.getUint16(2, true),
341
- numberCentralDirectoryRecordsOnThisDisk: data.getUint16(4, true),
342
- numberCentralDirectoryRecords: data.getUint16(6, true),
343
- centralDirectorySize: data.getUint32(8, true),
344
- centralDirectoryOffset: data.getUint32(12, true),
345
- };
346
- const commentLength = data.getUint16(16, true);
347
- endOfDirectory['comment'] = await collectBytes(stream, commentLength);
348
- return endOfDirectory as CentralDirectoryEndEntry;
349
- }
@@ -1,278 +0,0 @@
1
- import {
2
- COMPRESSION_DEFLATE,
3
- COMPRESSION_NONE,
4
- CentralDirectoryEndEntry,
5
- CentralDirectoryEntry,
6
- FileHeader,
7
- } from './types';
8
- import {
9
- SIGNATURE_CENTRAL_DIRECTORY_END,
10
- SIGNATURE_CENTRAL_DIRECTORY,
11
- SIGNATURE_FILE,
12
- } from './types';
13
- import { iteratorToStream } from '../utils/iterator-to-stream';
14
- import { collectBytes } from '../utils/collect-bytes';
15
-
16
- /**
17
- * Compresses the given files into a ZIP archive.
18
- *
19
- * @param files - An async or sync iterable of files to be compressed.
20
- * @returns A readable stream of the compressed ZIP archive as Uint8Array chunks.
21
- */
22
- export function encodeZip(
23
- files: AsyncIterable<File> | Iterable<File>
24
- ): ReadableStream<Uint8Array> {
25
- return iteratorToStream(files).pipeThrough(encodeZipTransform());
26
- }
27
-
28
- /**
29
- * Encodes the files into a ZIP format.
30
- *
31
- * @returns A stream transforming File objects into zipped bytes.
32
- */
33
- function encodeZipTransform() {
34
- const offsetToFileHeaderMap: Map<number, FileHeader> = new Map();
35
- let writtenBytes = 0;
36
- return new TransformStream<File, Uint8Array>({
37
- async transform(file, controller) {
38
- const entryBytes = new Uint8Array(await file.arrayBuffer());
39
- /**
40
- * We want to write raw deflate-compressed bytes into our
41
- * final ZIP file. CompressionStream supports "deflate-raw"
42
- * compression, but not on Node.js v18.
43
- *
44
- * As a workaround, we use the "gzip" compression and add
45
- * the header and footer bytes. It works, because "gzip"
46
- * compression is the same as "deflate" compression plus
47
- * the header and the footer.
48
- *
49
- * The header is 10 bytes long:
50
- * - 2 magic bytes: 0x1f, 0x8b
51
- * - 1 compression method: 0x08 (deflate)
52
- * - 1 header flags
53
- * - 4 mtime: 0x00000000 (no timestamp)
54
- * - 1 compression flags
55
- * - 1 OS: 0x03 (Unix)
56
- *
57
- * The footer is 8 bytes long:
58
- * - 4 bytes for CRC32 of the uncompressed data
59
- * - 4 bytes for ISIZE (uncompressed size modulo 2^32)
60
- */
61
- let compressed = (await collectBytes(
62
- new Blob([entryBytes])
63
- .stream()
64
- .pipeThrough(new CompressionStream('gzip'))
65
- ))!;
66
- // Grab the CRC32 hash from the footer.
67
- const crcHash = new DataView(compressed.buffer).getUint32(
68
- compressed.byteLength - 8,
69
- true
70
- );
71
- // Strip the header and the footer.
72
- compressed = compressed.slice(10, compressed.byteLength - 8);
73
-
74
- const encodedPath = new TextEncoder().encode(file.name);
75
- const zipFileEntry: FileHeader = {
76
- signature: SIGNATURE_FILE,
77
- version: 2,
78
- generalPurpose: 0,
79
- compressionMethod:
80
- file.type === 'directory' || compressed.byteLength === 0
81
- ? COMPRESSION_NONE
82
- : COMPRESSION_DEFLATE,
83
- lastModifiedTime: 0,
84
- lastModifiedDate: 0,
85
- crc: crcHash,
86
- compressedSize: compressed.byteLength,
87
- uncompressedSize: entryBytes.byteLength,
88
- path: encodedPath,
89
- extra: new Uint8Array(0),
90
- };
91
- offsetToFileHeaderMap.set(writtenBytes, zipFileEntry);
92
-
93
- const headerBytes = encodeFileEntryHeader(zipFileEntry);
94
- controller.enqueue(headerBytes);
95
- writtenBytes += headerBytes.byteLength;
96
-
97
- controller.enqueue(compressed);
98
- writtenBytes += compressed.byteLength;
99
- },
100
- flush(controller) {
101
- const centralDirectoryOffset = writtenBytes;
102
- let centralDirectorySize = 0;
103
- for (const [
104
- fileOffset,
105
- header,
106
- ] of offsetToFileHeaderMap.entries()) {
107
- const centralDirectoryEntry: Partial<CentralDirectoryEntry> = {
108
- ...header,
109
- signature: SIGNATURE_CENTRAL_DIRECTORY,
110
- fileComment: new Uint8Array(0),
111
- diskNumber: 1,
112
- internalAttributes: 0,
113
- externalAttributes: 0,
114
- firstByteAt: fileOffset,
115
- };
116
- const centralDirectoryEntryBytes = encodeCentralDirectoryEntry(
117
- centralDirectoryEntry as CentralDirectoryEntry,
118
- fileOffset
119
- );
120
- controller.enqueue(centralDirectoryEntryBytes);
121
- centralDirectorySize += centralDirectoryEntryBytes.byteLength;
122
- }
123
- const centralDirectoryEnd: CentralDirectoryEndEntry = {
124
- signature: SIGNATURE_CENTRAL_DIRECTORY_END,
125
- numberOfDisks: 1,
126
- centralDirectoryOffset,
127
- centralDirectorySize,
128
- centralDirectoryStartDisk: 1,
129
- numberCentralDirectoryRecordsOnThisDisk:
130
- offsetToFileHeaderMap.size,
131
- numberCentralDirectoryRecords: offsetToFileHeaderMap.size,
132
- comment: new Uint8Array(0),
133
- };
134
- const centralDirectoryEndBytes =
135
- encodeCentralDirectoryEnd(centralDirectoryEnd);
136
- controller.enqueue(centralDirectoryEndBytes);
137
- offsetToFileHeaderMap.clear();
138
- },
139
- });
140
- }
141
-
142
- /**
143
- * Encodes a file entry header as a Uint8Array.
144
- *
145
- * The array is structured as follows:
146
- *
147
- * ```
148
- * Offset Bytes Description
149
- * 0 4 Local file header signature = 0x04034b50 (PK♥♦ or "PK\3\4")
150
- * 4 2 Version needed to extract (minimum)
151
- * 6 2 General purpose bit flag
152
- * 8 2 Compression method; e.g. none = 0, DEFLATE = 8 (or "\0x08\0x00")
153
- * 10 2 File last modification time
154
- * 12 2 File last modification date
155
- * 14 4 CRC-32 of uncompressed data
156
- * 18 4 Compressed size (or 0xffffffff for ZIP64)
157
- * 22 4 Uncompressed size (or 0xffffffff for ZIP64)
158
- * 26 2 File name length (n)
159
- * 28 2 Extra field length (m)
160
- * 30 n File name
161
- * 30+n m Extra field
162
- * ```
163
- */
164
- function encodeFileEntryHeader(entry: FileHeader) {
165
- const buffer = new ArrayBuffer(
166
- 30 + entry.path.byteLength + entry.extra.byteLength
167
- );
168
- const view = new DataView(buffer);
169
- view.setUint32(0, entry.signature, true);
170
- view.setUint16(4, entry.version, true);
171
- view.setUint16(6, entry.generalPurpose, true);
172
- view.setUint16(8, entry.compressionMethod, true);
173
- view.setUint16(10, entry.lastModifiedDate, true);
174
- view.setUint16(12, entry.lastModifiedTime, true);
175
- view.setUint32(14, entry.crc, true);
176
- view.setUint32(18, entry.compressedSize, true);
177
- view.setUint32(22, entry.uncompressedSize, true);
178
- view.setUint16(26, entry.path.byteLength, true);
179
- view.setUint16(28, entry.extra.byteLength, true);
180
- const uint8Header = new Uint8Array(buffer);
181
- uint8Header.set(entry.path, 30);
182
- uint8Header.set(entry.extra, 30 + entry.path.byteLength);
183
- return uint8Header;
184
- }
185
-
186
- /**
187
- * Encodes a central directory entry as a Uint8Array.
188
- *
189
- * The central directory entry is structured as follows:
190
- *
191
- * ```
192
- * Offset Bytes Description
193
- * 0 4 Central directory file header signature = 0x02014b50
194
- * 4 2 Version made by
195
- * 6 2 Version needed to extract (minimum)
196
- * 8 2 General purpose bit flag
197
- * 10 2 Compression method
198
- * 12 2 File last modification time
199
- * 14 2 File last modification date
200
- * 16 4 CRC-32 of uncompressed data
201
- * 20 4 Compressed size (or 0xffffffff for ZIP64)
202
- * 24 4 Uncompressed size (or 0xffffffff for ZIP64)
203
- * 28 2 File name length (n)
204
- * 30 2 Extra field length (m)
205
- * 32 2 File comment length (k)
206
- * 34 2 Disk number where file starts (or 0xffff for ZIP64)
207
- * 36 2 Internal file attributes
208
- * 38 4 External file attributes
209
- * 42 4 Relative offset of local file header (or 0xffffffff for ZIP64). This is the number of bytes between the start of the first disk on which the file occurs, and the start of the local file header. This allows software reading the central directory to locate the position of the file inside the ZIP file.
210
- * 46 n File name
211
- * 46+n m Extra field
212
- * 46+n+m k File comment
213
- * ```
214
- */
215
- function encodeCentralDirectoryEntry(
216
- entry: CentralDirectoryEntry,
217
- fileEntryOffset: number
218
- ) {
219
- const buffer = new ArrayBuffer(
220
- 46 + entry.path.byteLength + entry.extra.byteLength
221
- );
222
- const view = new DataView(buffer);
223
- view.setUint32(0, entry.signature, true);
224
- view.setUint16(4, entry.versionCreated, true);
225
- view.setUint16(6, entry.versionNeeded, true);
226
- view.setUint16(8, entry.generalPurpose, true);
227
- view.setUint16(10, entry.compressionMethod, true);
228
- view.setUint16(12, entry.lastModifiedDate, true);
229
- view.setUint16(14, entry.lastModifiedTime, true);
230
- view.setUint32(16, entry.crc, true);
231
- view.setUint32(20, entry.compressedSize, true);
232
- view.setUint32(24, entry.uncompressedSize, true);
233
- view.setUint16(28, entry.path.byteLength, true);
234
- view.setUint16(30, entry.extra.byteLength, true);
235
- view.setUint16(32, entry.fileComment.byteLength, true);
236
- view.setUint16(34, entry.diskNumber, true);
237
- view.setUint16(36, entry.internalAttributes, true);
238
- view.setUint32(38, entry.externalAttributes, true);
239
- view.setUint32(42, fileEntryOffset, true);
240
- const uint8Header = new Uint8Array(buffer);
241
- uint8Header.set(entry.path, 46);
242
- uint8Header.set(entry.extra, 46 + entry.path.byteLength);
243
- return uint8Header;
244
- }
245
-
246
- /**
247
- * Encodes the end of central directory entry as a Uint8Array.
248
- *
249
- * The end of central directory entry is structured as follows:
250
- *
251
- * ```
252
- * Offset Bytes Description[33]
253
- * 0 4 End of central directory signature = 0x06054b50
254
- * 4 2 Number of this disk (or 0xffff for ZIP64)
255
- * 6 2 Disk where central directory starts (or 0xffff for ZIP64)
256
- * 8 2 Number of central directory records on this disk (or 0xffff for ZIP64)
257
- * 10 2 Total number of central directory records (or 0xffff for ZIP64)
258
- * 12 4 Size of central directory (bytes) (or 0xffffffff for ZIP64)
259
- * 16 4 Offset of start of central directory, relative to start of archive (or 0xffffffff for ZIP64)
260
- * 20 2 Comment length (n)
261
- * 22 n Comment
262
- * ```
263
- */
264
- function encodeCentralDirectoryEnd(entry: CentralDirectoryEndEntry) {
265
- const buffer = new ArrayBuffer(22 + entry.comment.byteLength);
266
- const view = new DataView(buffer);
267
- view.setUint32(0, entry.signature, true);
268
- view.setUint16(4, entry.numberOfDisks, true);
269
- view.setUint16(6, entry.centralDirectoryStartDisk, true);
270
- view.setUint16(8, entry.numberCentralDirectoryRecordsOnThisDisk, true);
271
- view.setUint16(10, entry.numberCentralDirectoryRecords, true);
272
- view.setUint32(12, entry.centralDirectorySize, true);
273
- view.setUint32(16, entry.centralDirectoryOffset, true);
274
- view.setUint16(20, entry.comment.byteLength, true);
275
- const uint8Header = new Uint8Array(buffer);
276
- uint8Header.set(entry.comment, 22);
277
- return uint8Header;
278
- }
package/src/zip/types.ts DELETED
@@ -1,76 +0,0 @@
1
- export const FILE_HEADER_SIZE = 32;
2
- export const SIGNATURE_FILE = 67324752 as const;
3
- export const SIGNATURE_CENTRAL_DIRECTORY = 33639248 as const;
4
- export const SIGNATURE_CENTRAL_DIRECTORY_END = 101010256 as const;
5
- export const SIGNATURE_DATA_DESCRIPTOR = 134695760 as const;
6
-
7
- export const COMPRESSION_NONE = 0 as const;
8
- export const COMPRESSION_DEFLATE = 8 as const;
9
- export type CompressionMethod =
10
- | typeof COMPRESSION_NONE
11
- | typeof COMPRESSION_DEFLATE;
12
-
13
- export type ZipEntry =
14
- | FileEntry
15
- | CentralDirectoryEntry
16
- | CentralDirectoryEndEntry;
17
-
18
- /**
19
- * Data of the file entry header encoded in a ".zip" file.
20
- */
21
- export interface FileHeader {
22
- signature: typeof SIGNATURE_FILE;
23
- version: number;
24
- generalPurpose: number;
25
- compressionMethod: CompressionMethod;
26
- lastModifiedTime: number;
27
- lastModifiedDate: number;
28
- crc: number;
29
- compressedSize: number;
30
- uncompressedSize: number;
31
- path: Uint8Array;
32
- extra: Uint8Array;
33
- }
34
- export interface FileEntry extends FileHeader {
35
- isDirectory: boolean;
36
- bytes: Uint8Array;
37
- }
38
-
39
- /**
40
- * Data of the central directory entry encoded in a ".zip" file.
41
- */
42
- export interface CentralDirectoryEntry {
43
- signature: typeof SIGNATURE_CENTRAL_DIRECTORY;
44
- versionCreated: number;
45
- versionNeeded: number;
46
- generalPurpose: number;
47
- compressionMethod: CompressionMethod;
48
- lastModifiedTime: number;
49
- lastModifiedDate: number;
50
- crc: number;
51
- compressedSize: number;
52
- uncompressedSize: number;
53
- diskNumber: number;
54
- internalAttributes: number;
55
- externalAttributes: number;
56
- firstByteAt: number;
57
- lastByteAt: number;
58
- path: Uint8Array;
59
- extra: Uint8Array;
60
- fileComment: Uint8Array;
61
- isDirectory: boolean;
62
- }
63
-
64
- /**
65
- * Data of the central directory end entry encoded in a ".zip" file.
66
- */
67
- export interface CentralDirectoryEndEntry {
68
- signature: typeof SIGNATURE_CENTRAL_DIRECTORY_END;
69
- numberOfDisks: number;
70
- centralDirectoryStartDisk: number;
71
- numberCentralDirectoryRecordsOnThisDisk: number;
72
- numberCentralDirectoryRecords: number;
73
- centralDirectorySize: number;
74
- centralDirectoryOffset: number;
75
- comment: Uint8Array;
76
- }