@futpib/parser 1.0.0 → 1.0.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 (171) hide show
  1. package/build/apk.d.ts +29 -3
  2. package/build/apkParser.d.ts +15 -2
  3. package/build/apkParser.js +70 -41
  4. package/build/apkParser.test.js +2 -2
  5. package/build/apkUnparser.d.ts +4 -0
  6. package/build/apkUnparser.js +90 -0
  7. package/build/apkUnparser.test.d.ts +1 -0
  8. package/build/apkUnparser.test.js +26 -0
  9. package/build/arbitraryFileSystemEntry.js +1 -1
  10. package/build/arbitraryZip.d.ts +1 -1
  11. package/build/arbitraryZip.js +13 -19
  12. package/build/arbitraryZipPermissions.d.ts +1 -8
  13. package/build/arbitraryZipPermissions.js +1 -16
  14. package/build/arbitraryZipStream.d.ts +1 -1
  15. package/build/arbitraryZipStream.js +3 -3
  16. package/build/arrayParser.d.ts +1 -1
  17. package/build/arrayParser.js +2 -2
  18. package/build/arrayParser.test.js +2 -2
  19. package/build/arrayUnparser.d.ts +2 -0
  20. package/build/arrayUnparser.js +8 -0
  21. package/build/bsonParser.test.js +2 -2
  22. package/build/customInvariant.d.ts +4 -0
  23. package/build/customInvariant.js +11 -0
  24. package/build/debugLogParser.d.ts +1 -1
  25. package/build/debugLogParser.js +1 -1
  26. package/build/elementParser.d.ts +2 -2
  27. package/build/elementParser.js +1 -1
  28. package/build/endOfInputParser.d.ts +2 -2
  29. package/build/exactElementParser.d.ts +1 -1
  30. package/build/exactSequenceParser.js +1 -1
  31. package/build/index.d.ts +5 -2
  32. package/build/index.js +3 -0
  33. package/build/inputReader.d.ts +3 -3
  34. package/build/inputReader.js +6 -6
  35. package/build/inputReader.test.js +7 -7
  36. package/build/javaKeyStore.d.ts +1 -0
  37. package/build/javaKeyStore.js +1 -0
  38. package/build/javaKeyStoreParser.d.ts +2 -0
  39. package/build/javaKeyStoreParser.js +67 -0
  40. package/build/javaKeyStoreParser.test.d.ts +1 -0
  41. package/build/javaKeyStoreParser.test.js +16 -0
  42. package/build/javaKeystoreParser.d.ts +2 -0
  43. package/build/javaKeystoreParser.js +7 -0
  44. package/build/jsonParser.js +3 -2
  45. package/build/jsonParser.test.js +2 -2
  46. package/build/listParser.d.ts +1 -1
  47. package/build/listParser.js +5 -5
  48. package/build/negativeLookahead.d.ts +1 -1
  49. package/build/negativeLookahead.js +16 -18
  50. package/build/negativeLookaheadParser.d.ts +2 -0
  51. package/build/negativeLookaheadParser.js +18 -0
  52. package/build/optionalParser.d.ts +1 -1
  53. package/build/optionalParser.js +2 -2
  54. package/build/parser.d.ts +3 -3
  55. package/build/parser.js +3 -3
  56. package/build/parser.test.js +16 -16
  57. package/build/parserAccessorParser.d.ts +1 -1
  58. package/build/parserConsumedSequenceParser.d.ts +2 -0
  59. package/build/parserConsumedSequenceParser.js +17 -0
  60. package/build/parserContext.d.ts +6 -6
  61. package/build/parserContext.js +15 -14
  62. package/build/parserContext.test.js +7 -7
  63. package/build/parserCreatorCompose.d.ts +3 -3
  64. package/build/parserCreatorCompose.js +2 -2
  65. package/build/parserImplementationInvariant.d.ts +1 -1
  66. package/build/parserImplementationInvariant.js +2 -2
  67. package/build/parserInputCompanion.d.ts +20 -0
  68. package/build/parserInputCompanion.js +30 -0
  69. package/build/parserInvariant.d.ts +1 -1
  70. package/build/parserInvariant.js +1 -1
  71. package/build/quantifierParser.d.ts +2 -0
  72. package/build/quantifierParser.js +17 -0
  73. package/build/sequenceBuffer.d.ts +3 -3
  74. package/build/sequenceBuffer.js +6 -6
  75. package/build/sequenceBuffer.test.js +2 -2
  76. package/build/sequenceUnparser.d.ts +2 -0
  77. package/build/sequenceUnparser.js +6 -0
  78. package/build/sliceBoundedParser.d.ts +1 -1
  79. package/build/sliceBoundedParser.js +1 -1
  80. package/build/sliceBoundedParser.test.js +2 -2
  81. package/build/terminatedArrayParser.d.ts +1 -1
  82. package/build/terminatedArrayParser.js +3 -3
  83. package/build/uint8Array.d.ts +1 -0
  84. package/build/uint8Array.js +7 -0
  85. package/build/unparser.d.ts +8 -0
  86. package/build/unparser.js +104 -0
  87. package/build/unparser.test.d.ts +1 -0
  88. package/build/unparser.test.js +150 -0
  89. package/build/unparserContext.d.ts +31 -0
  90. package/build/unparserContext.js +74 -0
  91. package/build/unparserError.d.ts +9 -0
  92. package/build/unparserError.js +9 -0
  93. package/build/unparserImplementationInvariant.d.ts +2 -0
  94. package/build/unparserImplementationInvariant.js +5 -0
  95. package/build/unparserInputCompanion.d.ts +15 -0
  96. package/build/unparserInputCompanion.js +13 -0
  97. package/build/unparserOutputCompanion.d.ts +15 -0
  98. package/build/unparserOutputCompanion.js +13 -0
  99. package/build/zip.d.ts +9 -17
  100. package/build/zipParser.d.ts +13 -10
  101. package/build/zipParser.js +48 -60
  102. package/build/zipParser.test.js +2 -7
  103. package/build/zipUnparser.d.ts +5 -0
  104. package/build/zipUnparser.js +171 -0
  105. package/build/zipUnparser.test.d.ts +1 -0
  106. package/build/zipUnparser.test.js +80 -0
  107. package/package.json +4 -2
  108. package/src/apk.ts +35 -3
  109. package/src/apkParser.test.ts +2 -2
  110. package/src/apkParser.test.ts.md +114 -111
  111. package/src/apkParser.test.ts.snap +0 -0
  112. package/src/apkParser.ts +150 -85
  113. package/src/apkUnparser.test.ts +37 -0
  114. package/src/apkUnparser.ts +120 -0
  115. package/src/arbitraryFileSystemEntry.ts +2 -4
  116. package/src/arbitraryZip.ts +20 -27
  117. package/src/arbitraryZipPermissions.ts +0 -25
  118. package/src/arbitraryZipStream.ts +4 -4
  119. package/src/arrayParser.test.ts +3 -3
  120. package/src/arrayParser.ts +3 -2
  121. package/src/arrayUnparser.ts +13 -0
  122. package/src/bsonParser.test.ts +2 -2
  123. package/src/bsonParser.ts +3 -3
  124. package/src/{parserInvariant.ts → customInvariant.ts} +1 -1
  125. package/src/debugLogParser.ts +1 -1
  126. package/src/elementParser.ts +3 -3
  127. package/src/endOfInputParser.ts +4 -4
  128. package/src/exactElementParser.ts +1 -1
  129. package/src/exactSequenceParser.ts +2 -2
  130. package/src/index.ts +15 -2
  131. package/src/inputReader.test.ts +7 -7
  132. package/src/inputReader.ts +5 -5
  133. package/src/javaKeyStore.ts +0 -0
  134. package/src/javaKeyStoreParser.test.ts +23 -0
  135. package/src/javaKeyStoreParser.test.ts.md +103 -0
  136. package/src/javaKeyStoreParser.test.ts.snap +0 -0
  137. package/src/javaKeyStoreParser.ts +136 -0
  138. package/src/jsonParser.test.ts +2 -2
  139. package/src/jsonParser.ts +13 -12
  140. package/src/listParser.ts +6 -6
  141. package/src/negativeLookaheadParser.ts +24 -0
  142. package/src/optionalParser.ts +3 -3
  143. package/src/parser.test.ts +19 -17
  144. package/src/parser.ts +7 -7
  145. package/src/parserAccessorParser.ts +1 -1
  146. package/src/parserConsumedSequenceParser.ts +20 -0
  147. package/src/parserContext.test.ts +7 -7
  148. package/src/parserContext.ts +18 -14
  149. package/src/parserCreatorCompose.ts +6 -6
  150. package/src/parserImplementationInvariant.ts +2 -2
  151. package/src/{inputCompanion.ts → parserInputCompanion.ts} +10 -6
  152. package/src/quantifierParser.ts +25 -0
  153. package/src/sequenceBuffer.test.ts +2 -2
  154. package/src/sequenceBuffer.ts +5 -5
  155. package/src/sequenceUnparser.ts +9 -0
  156. package/src/sliceBoundedParser.test.ts +2 -2
  157. package/src/sliceBoundedParser.ts +2 -2
  158. package/src/terminatedArrayParser.ts +3 -3
  159. package/src/uint8Array.ts +10 -0
  160. package/src/unparser.test.ts +221 -0
  161. package/src/unparser.ts +209 -0
  162. package/src/unparserContext.ts +127 -0
  163. package/src/unparserError.ts +12 -0
  164. package/src/unparserImplementationInvariant.ts +6 -0
  165. package/src/unparserOutputCompanion.ts +24 -0
  166. package/src/zip.ts +10 -22
  167. package/src/zipParser.test.ts +2 -8
  168. package/src/zipParser.ts +147 -129
  169. package/src/zipUnparser.test.ts +119 -0
  170. package/src/zipUnparser.ts +239 -0
  171. package/src/negativeLookahead.ts +0 -26
package/src/zipParser.ts CHANGED
@@ -1,16 +1,23 @@
1
- import invariant from "invariant";
2
- import zlib from "node:zlib";
3
- import { createArrayParser } from "./arrayParser.js";
4
- import { createExactSequenceParser } from "./exactSequenceParser.js";
5
- import { Parser, setParserName } from "./parser.js";
6
- import { createTupleParser } from "./tupleParser.js";
7
- import { promiseCompose } from "./promiseCompose.js";
8
- import { createFixedLengthSequenceParser } from "./fixedLengthSequenceParser.js";
9
- import { createOptionalParser } from "./optionalParser.js";
10
- import { parserCreatorCompose } from "./parserCreatorCompose.js";
11
- import { Zip, ZipCompression, ZipDirectoryEntry, ZipEntry, ZipFileEntry, ZipPermissions } from "./zip.js";
12
- import { Readable } from "node:stream";
13
- import { pipeline } from "node:stream/promises";
1
+ import zlib from 'node:zlib';
2
+ import { Readable } from 'node:stream';
3
+ import { pipeline } from 'node:stream/promises';
4
+ import invariant from 'invariant';
5
+ import { createArrayParser } from './arrayParser.js';
6
+ import { createExactSequenceParser } from './exactSequenceParser.js';
7
+ import { type Parser, setParserName } from './parser.js';
8
+ import { createTupleParser } from './tupleParser.js';
9
+ import { promiseCompose } from './promiseCompose.js';
10
+ import { createFixedLengthSequenceParser } from './fixedLengthSequenceParser.js';
11
+ import { createOptionalParser } from './optionalParser.js';
12
+ import { parserCreatorCompose } from './parserCreatorCompose.js';
13
+ import {
14
+ type Zip,
15
+ type ZipCompression,
16
+ type ZipDirectoryEntry,
17
+ type ZipEntry,
18
+ type ZipFileEntry,
19
+ } from './zip.js';
20
+ import { uint8ArrayAsyncIterableToUint8Array } from './uint8Array.js';
14
21
 
15
22
  // https://pkwaredownloads.blob.core.windows.net/pem/APPNOTE.txt
16
23
 
@@ -34,12 +41,12 @@ const dosDateTimeParser: Parser<Date, Uint8Array> = promiseCompose(
34
41
  uint16LEParser,
35
42
  ]),
36
43
  ([ time, date ]) => new Date(Date.UTC(
37
- 1980 + ((date >> 9) & 0x7f),
38
- ((date >> 5) & 0xf) - 1,
39
- date & 0x1f,
40
- (time >> 11) & 0x1f,
41
- (time >> 5) & 0x3f,
42
- (time & 0x1f) * 2,
44
+ 1980 + ((date >> 9) & 0x7F),
45
+ ((date >> 5) & 0xF) - 1,
46
+ date & 0x1F,
47
+ (time >> 11) & 0x1F,
48
+ (time >> 5) & 0x3F,
49
+ (time & 0x1F) * 2,
43
50
  )),
44
51
  );
45
52
 
@@ -62,12 +69,12 @@ type ZipLocalFileHeader = {
62
69
  versionNeededToExtract: number;
63
70
  generalPurposeBitFlag: number;
64
71
  compressionMethod: ZipCompression;
65
- lastModFile: Date;
72
+ lastModifiedFile: Date;
66
73
  crc32: number;
67
74
  compressedSize: number;
68
75
  uncompressedSize: number;
69
76
 
70
- fileName: string;
77
+ filePath: string;
71
78
  extraField: Uint8Array;
72
79
  };
73
80
 
@@ -86,10 +93,10 @@ const zipLocalFileHeaderParser_ = createTupleParser([
86
93
  uint16LEParser,
87
94
  ]),
88
95
  ([
89
- fileNameLength,
96
+ filePathLength,
90
97
  extraFieldLength,
91
98
  ]) => createTupleParser([
92
- createFixedLengthSequenceParser(fileNameLength),
99
+ createFixedLengthSequenceParser(filePathLength),
93
100
  createFixedLengthSequenceParser(extraFieldLength),
94
101
  ]),
95
102
  )(),
@@ -104,20 +111,20 @@ const zipLocalFileHeaderParser: Parser<ZipLocalFileHeader, Uint8Array> = promise
104
111
  versionNeededToExtract,
105
112
  generalPurposeBitFlag,
106
113
  compressionMethod,
107
- lastModFile,
114
+ lastModifiedFile,
108
115
  crc32,
109
116
  compressedSize,
110
117
  uncompressedSize,
111
- [ fileName, extraField ],
118
+ [ filePath, extraField ],
112
119
  ]) => ({
113
120
  versionNeededToExtract,
114
121
  generalPurposeBitFlag,
115
122
  compressionMethod,
116
- lastModFile,
123
+ lastModifiedFile,
117
124
  crc32,
118
125
  compressedSize,
119
126
  uncompressedSize,
120
- fileName: Buffer.from(fileName).toString('utf8'),
127
+ filePath: Buffer.from(filePath).toString('utf8'),
121
128
  extraField,
122
129
  }),
123
130
  );
@@ -135,7 +142,7 @@ const zipDataDescriptorParser: Parser<unknown, Uint8Array> = createTupleParser([
135
142
  uint32LEParser,
136
143
  ]);
137
144
 
138
- type ZipLocalFile = {
145
+ export type ZipLocalFile = {
139
146
  zipLocalFileHeader: ZipLocalFileHeader;
140
147
  zipEncryptionHeader: unknown;
141
148
  compressedData: Uint8Array;
@@ -145,7 +152,7 @@ type ZipLocalFile = {
145
152
  export const zipLocalFileParser: Parser<ZipLocalFile, Uint8Array> = promiseCompose(
146
153
  parserCreatorCompose(
147
154
  () => zipLocalFileHeaderParser,
148
- (zipLocalFileHeader) => createTupleParser([
155
+ zipLocalFileHeader => createTupleParser([
149
156
  async () => zipLocalFileHeader,
150
157
  createOptionalParser(zipEncryptionHeaderParser),
151
158
  createFixedLengthSequenceParser(zipLocalFileHeader.compressedSize),
@@ -191,83 +198,117 @@ const zipVersionMadeByParser: Parser<ZipVersionMadeBy, Uint8Array> = promiseComp
191
198
  }),
192
199
  );
193
200
 
194
- type ZipCentralDirectoryHeader = {
201
+ type ZipExternalFileAttributes = {
202
+ directory: boolean;
203
+ }
204
+
205
+ const dosExternalFileAttributesParser: Parser<ZipExternalFileAttributes, Uint8Array> = promiseCompose(
206
+ uint32LEParser,
207
+ externalFileAttributes => ({
208
+ directory: (externalFileAttributes & 0b0001_0000) !== 0,
209
+ }),
210
+ );
211
+
212
+ const unixExternalFileAttributesParser: Parser<ZipExternalFileAttributes, Uint8Array> = promiseCompose(
213
+ uint32LEParser,
214
+ externalFileAttributes => ({
215
+ directory: (externalFileAttributes & (0b0100_0000_0000_0000 << 16)) !== 0,
216
+ }),
217
+ );
218
+
219
+ const createExternalFileAttributesParser = (hostSystem: number) => promiseCompose(
220
+ hostSystem === 0 ? dosExternalFileAttributesParser : unixExternalFileAttributesParser,
221
+ externalFileAttributes => externalFileAttributes,
222
+ );
223
+
224
+ export type ZipCentralDirectoryHeader = {
195
225
  versionMadeBy: ZipVersionMadeBy;
196
226
  versionNeededToExtract: number;
197
227
  generalPurposeBitFlag: number;
198
228
  compressionMethod: ZipCompression;
199
- lastModFile: Date;
229
+ lastModifiedFile: Date;
200
230
  crc32: number;
201
231
  compressedSize: number;
202
232
  uncompressedSize: number;
203
233
  diskNumberStart: number;
204
234
  internalFileAttributes: number;
205
- externalFileAttributes: number;
235
+ externalFileAttributes: ZipExternalFileAttributes;
206
236
  relativeOffsetOfLocalHeader: number;
207
237
 
208
- fileName: string;
238
+ filePath: string;
209
239
  extraField: Uint8Array;
210
240
  fileComment: string;
211
241
  };
212
242
 
213
- const zipCentralDirectoryHeaderParser_ = createTupleParser([
214
- createExactSequenceParser<Uint8Array>(Buffer.from('504b0102', 'hex')),
215
- zipVersionMadeByParser,
216
- uint16LEParser,
217
- uint16LEParser,
218
- zipCompressionMethodParser,
219
- dosDateTimeParser,
220
- uint32LEParser,
221
- uint32LEParser,
222
- uint32LEParser,
223
- parserCreatorCompose(
224
- () => createTupleParser([
225
- uint16LEParser,
226
- uint16LEParser,
227
- uint16LEParser,
228
- uint16LEParser,
229
- uint16LEParser,
230
- uint32LEParser,
231
- uint32LEParser,
232
- ]),
233
- ([
234
- fileNameLength,
235
- extraFieldLength,
236
- fileCommentLength,
237
-
238
- diskNumberStart,
239
- internalFileAttributes,
240
- externalFileAttributes,
241
- relativeOffsetOfLocalHeader,
242
- ]) => createTupleParser([
243
- createFixedLengthSequenceParser(fileNameLength),
244
- createFixedLengthSequenceParser(extraFieldLength),
245
- createFixedLengthSequenceParser(fileCommentLength),
246
-
247
- async () => diskNumberStart,
248
- async () => internalFileAttributes,
249
- async () => externalFileAttributes,
250
- async () => relativeOffsetOfLocalHeader,
251
- ]),
252
- )(),
253
- ]);
243
+ const zipCentralDirectoryHeaderParser_ = parserCreatorCompose(
244
+ () => createTupleParser([
245
+ createExactSequenceParser<Uint8Array>(Buffer.from('504b0102', 'hex')),
246
+ zipVersionMadeByParser,
247
+ ]),
248
+ ([
249
+ _centralDirectoryHeaderSignature,
250
+ versionMadeBy,
251
+ ]) => createTupleParser([
252
+ async () => versionMadeBy,
253
+ uint16LEParser,
254
+ uint16LEParser,
255
+ zipCompressionMethodParser,
256
+ dosDateTimeParser,
257
+ uint32LEParser,
258
+ uint32LEParser,
259
+ uint32LEParser,
260
+ parserCreatorCompose(
261
+ () => createTupleParser([
262
+ uint16LEParser,
263
+ uint16LEParser,
264
+ uint16LEParser,
265
+
266
+ uint16LEParser,
267
+ uint16LEParser,
268
+
269
+ createExternalFileAttributesParser(versionMadeBy.hostSystem),
270
+ uint32LEParser,
271
+ ]),
272
+ ([
273
+ filePathLength,
274
+ extraFieldLength,
275
+ fileCommentLength,
276
+
277
+ diskNumberStart,
278
+ internalFileAttributes,
279
+
280
+ externalFileAttributes,
281
+ relativeOffsetOfLocalHeader,
282
+ ]) => createTupleParser([
283
+ createFixedLengthSequenceParser(filePathLength),
284
+ createFixedLengthSequenceParser(extraFieldLength),
285
+ createFixedLengthSequenceParser(fileCommentLength),
286
+
287
+ async () => diskNumberStart,
288
+ async () => internalFileAttributes,
289
+
290
+ async () => externalFileAttributes,
291
+ async () => relativeOffsetOfLocalHeader,
292
+ ]),
293
+ )(),
294
+ ]),
295
+ )();
254
296
 
255
297
  setParserName(zipCentralDirectoryHeaderParser_, 'centralDirectoryHeaderParser_');
256
298
 
257
299
  export const zipCentralDirectoryHeaderParser: Parser<ZipCentralDirectoryHeader, Uint8Array> = promiseCompose(
258
300
  zipCentralDirectoryHeaderParser_,
259
301
  ([
260
- _centralDirectoryHeaderSignature,
261
302
  versionMadeBy,
262
303
  versionNeededToExtract,
263
304
  generalPurposeBitFlag,
264
305
  compressionMethod,
265
- lastModFile,
306
+ lastModifiedFile,
266
307
  crc32,
267
308
  compressedSize,
268
309
  uncompressedSize,
269
310
  [
270
- fileName,
311
+ filePath,
271
312
  extraField,
272
313
  fileComment,
273
314
 
@@ -281,7 +322,7 @@ export const zipCentralDirectoryHeaderParser: Parser<ZipCentralDirectoryHeader,
281
322
  versionNeededToExtract,
282
323
  generalPurposeBitFlag,
283
324
  compressionMethod,
284
- lastModFile,
325
+ lastModifiedFile,
285
326
  crc32,
286
327
  compressedSize,
287
328
  uncompressedSize,
@@ -289,7 +330,7 @@ export const zipCentralDirectoryHeaderParser: Parser<ZipCentralDirectoryHeader,
289
330
  internalFileAttributes,
290
331
  externalFileAttributes,
291
332
  relativeOffsetOfLocalHeader,
292
- fileName: Buffer.from(fileName).toString('utf8'),
333
+ filePath: Buffer.from(filePath).toString('utf8'),
293
334
  extraField,
294
335
  fileComment: Buffer.from(fileComment).toString('utf8'),
295
336
  }),
@@ -311,14 +352,14 @@ const zipFileCommentParser: Parser<string, Uint8Array> = promiseCompose(
311
352
  uint8Array => Buffer.from(uint8Array).toString('utf8'),
312
353
  );
313
354
 
314
- type ZipEndOfCentralDirectoryRecord = {
315
- numberOfThisDisk: number,
316
- numberOfTheDiskWithTheStartOfTheCentralDirectory: number,
317
- totalNumberOfEntriesInTheCentralDirectoryOnThisDisk: number,
318
- totalNumberOfEntriesInTheCentralDirectory: number,
319
- sizeOfTheCentralDirectory: number,
320
- offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber: number,
321
- zipFileComment: string,
355
+ export type ZipEndOfCentralDirectoryRecord = {
356
+ numberOfThisDisk: number;
357
+ numberOfTheDiskWithTheStartOfTheCentralDirectory: number;
358
+ totalNumberOfEntriesInTheCentralDirectoryOnThisDisk: number;
359
+ totalNumberOfEntriesInTheCentralDirectory: number;
360
+ sizeOfTheCentralDirectory: number;
361
+ offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber: number;
362
+ zipFileComment: string;
322
363
  };
323
364
 
324
365
  const zipEndOfCentralDirectoryRecordParser_ = createTupleParser([
@@ -372,10 +413,10 @@ export async function zipEntriesFromZipSegments({
372
413
  zipLocalFiles,
373
414
  zipCentralDirectoryHeaders,
374
415
  }: {
375
- zipLocalFiles: ZipLocalFile[],
376
- zipCentralDirectoryHeaders: ZipCentralDirectoryHeader[],
416
+ zipLocalFiles: ZipLocalFile[];
417
+ zipCentralDirectoryHeaders: ZipCentralDirectoryHeader[];
377
418
  }) {
378
- const decompressPromises: Promise<void>[] = [];
419
+ const decompressPromises: Array<Promise<void>> = [];
379
420
 
380
421
  const entries: ZipEntry[] = await Promise.all(zipLocalFiles.map(async (zipLocalFile, index) => {
381
422
  const {
@@ -389,41 +430,24 @@ export async function zipEntriesFromZipSegments({
389
430
 
390
431
  invariant(centralDirectoryHeader, 'Central directory header not found for local file %s', index);
391
432
 
392
- let isDosDirectory = false;
393
- let permissions: ZipPermissions = {
394
- type: 'unix',
395
- unixPermissions: 0,
396
- };
397
-
398
- if (centralDirectoryHeader.versionMadeBy.hostSystem === 0) {
399
- isDosDirectory = (centralDirectoryHeader.externalFileAttributes & 0b00010000) !== 0;
400
- permissions = {
401
- type: 'dos',
402
- dosPermissions: centralDirectoryHeader.externalFileAttributes & 0b00111111,
403
- };
404
- }
405
-
406
- if (centralDirectoryHeader.versionMadeBy.hostSystem === 3) {
407
- permissions = {
408
- type: 'unix',
409
- unixPermissions: (centralDirectoryHeader.externalFileAttributes >> 16) & 0b111111111,
410
- };
411
- }
412
-
413
433
  const commonFields: Omit<ZipFileEntry | ZipDirectoryEntry, 'type'> = {
414
- path: zipLocalFileHeader.fileName,
434
+ path: zipLocalFileHeader.filePath,
415
435
  comment: centralDirectoryHeader.fileComment,
416
- date: zipLocalFileHeader.lastModFile,
417
- permissions,
436
+ date: zipLocalFileHeader.lastModifiedFile,
437
+ hostSystem: centralDirectoryHeader.versionMadeBy.hostSystem === 0 ? 'dos' : 'unix',
438
+ attributes: centralDirectoryHeader.externalFileAttributes,
418
439
  };
419
440
 
420
- const isDirectory = isDosDirectory || commonFields.path.endsWith('/');
441
+ const isDirectory = (
442
+ centralDirectoryHeader.externalFileAttributes.directory
443
+ || commonFields.path.endsWith('/')
444
+ );
421
445
 
422
446
  if (isDirectory) {
423
447
  return {
424
448
  ...commonFields,
425
449
  type: 'directory',
426
- path: commonFields.path.slice(0, -1),
450
+ path: commonFields.path.replace(/\/$/, ''),
427
451
  };
428
452
  }
429
453
 
@@ -434,18 +458,12 @@ export async function zipEntriesFromZipSegments({
434
458
  compression: zipLocalFileHeader.compressionMethod,
435
459
  };
436
460
 
437
- if (fileEntry.content.length && fileEntry.compression === 'deflate') {
438
- const deflate = zlib.createInflateRaw();
461
+ if (fileEntry.content.length > 0 && fileEntry.compression === 'deflate') {
462
+ const inflate = zlib.createInflateRaw();
439
463
  const input = Readable.from(Buffer.from(compressedData));
440
464
  const [ _, buffer ] = await Promise.all([
441
- pipeline(input, deflate),
442
- (async () => {
443
- const chunks: Buffer[] = [];
444
- for await (const chunk of deflate) {
445
- chunks.push(chunk);
446
- }
447
- return Buffer.concat(chunks);
448
- })(),
465
+ pipeline(input, inflate),
466
+ uint8ArrayAsyncIterableToUint8Array(inflate),
449
467
  ]);
450
468
 
451
469
  fileEntry.content = Uint8Array.from(buffer);
@@ -465,10 +483,10 @@ export async function zipFromZipSegments({
465
483
 
466
484
  zipEndOfCentralDirectoryRecord,
467
485
  }: {
468
- zipLocalFiles: ZipLocalFile[],
469
- zipCentralDirectoryHeaders: ZipCentralDirectoryHeader[],
486
+ zipLocalFiles: ZipLocalFile[];
487
+ zipCentralDirectoryHeaders: ZipCentralDirectoryHeader[];
470
488
 
471
- zipEndOfCentralDirectoryRecord: ZipEndOfCentralDirectoryRecord,
489
+ zipEndOfCentralDirectoryRecord: ZipEndOfCentralDirectoryRecord;
472
490
  }) {
473
491
  const entries = await zipEntriesFromZipSegments({
474
492
  zipLocalFiles,
@@ -0,0 +1,119 @@
1
+ import { testProp } from '@fast-check/ava';
2
+ import { temporaryFile } from 'tempy';
3
+ import { execa } from 'execa';
4
+ import fsPromises from 'node:fs/promises';
5
+ import { runUnparser } from './unparser.js';
6
+ import { zipUnparser } from './zipUnparser.js';
7
+ import { uint8ArrayUnparserOutputCompanion } from './unparserOutputCompanion.js';
8
+ import { runParser } from './parser.js';
9
+ import { zipParser } from './zipParser.js';
10
+ import { uint8ArrayParserInputCompanion } from './parserInputCompanion.js';
11
+ import { arbitraryZip } from './arbitraryZip.js';
12
+
13
+ async function hasExecutable(executable: string) {
14
+ const hasExecutable = execa(executable).then(() => true, () => false);
15
+
16
+ if (!hasExecutable) {
17
+ console.warn(`Executable %o not found`, executable);
18
+ }
19
+
20
+ return hasExecutable;
21
+ }
22
+
23
+ const hasZipinfoPromise = hasExecutable('zipinfo');
24
+ const has7zPromise = hasExecutable('7z');
25
+
26
+ async function zipinfo(zipFilePath: string) {
27
+ const { stdout } = await execa('zipinfo', [
28
+ '-v',
29
+ zipFilePath,
30
+ ], {
31
+ reject: false,
32
+ });
33
+
34
+ return {
35
+ isEmptyZipfile: stdout.endsWith('Empty zipfile.'),
36
+ };
37
+ }
38
+
39
+ async function _7zList(zipFilePath: string) {
40
+ const { stdout } = await execa('7z', [
41
+ 'l',
42
+ zipFilePath,
43
+ ], {
44
+ reject: false,
45
+ });
46
+
47
+ if (/Errors: \d+$/.test(stdout)) {
48
+ throw new Error(`7z reports errors: ${stdout}`);
49
+ }
50
+
51
+ return {
52
+ isEmptyZipfile: false, // TODO
53
+ };
54
+ }
55
+
56
+ testProp(
57
+ 'zip',
58
+ [
59
+ arbitraryZip,
60
+ ],
61
+ async (t, zip) => {
62
+ const actualStream = runUnparser(zipUnparser, zip, uint8ArrayUnparserOutputCompanion);
63
+ const actual = await runParser(zipParser, actualStream, uint8ArrayParserInputCompanion);
64
+
65
+ const isDeepEqual = t.deepEqual(actual, zip);
66
+
67
+ if (!isDeepEqual) {
68
+ return;
69
+ }
70
+
71
+ const hasZipinfo = await hasZipinfoPromise;
72
+ const has7z = await has7zPromise;
73
+
74
+ if (!hasZipinfo && !has7z) {
75
+ return;
76
+ }
77
+
78
+ const temporaryFilePath = temporaryFile({
79
+ extension: 'zip',
80
+ });
81
+
82
+ const zipStream = runUnparser(zipUnparser, zip, uint8ArrayUnparserOutputCompanion);
83
+
84
+ await fsPromises.writeFile(temporaryFilePath, zipStream);
85
+
86
+ if (has7z) {
87
+ const {
88
+ isEmptyZipfile,
89
+ } = await _7zList(temporaryFilePath);
90
+
91
+ if (isEmptyZipfile) {
92
+ t.deepEqual(
93
+ actual.entries,
94
+ [],
95
+ '7z reports the zipfile as empty, but it has entries',
96
+ );
97
+ }
98
+ }
99
+
100
+ if (hasZipinfo) {
101
+ const {
102
+ isEmptyZipfile,
103
+ } = await zipinfo(temporaryFilePath);
104
+
105
+ if (isEmptyZipfile) {
106
+ t.deepEqual(
107
+ actual.entries.filter(entry => entry.type === 'file'),
108
+ [],
109
+ 'zipinfo reports the zipfile as empty, but it has file entries',
110
+ );
111
+ }
112
+ }
113
+
114
+ await fsPromises.unlink(temporaryFilePath);
115
+ },
116
+ {
117
+ verbose: true,
118
+ },
119
+ );