@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.
- package/build/apk.d.ts +29 -3
- package/build/apkParser.d.ts +15 -2
- package/build/apkParser.js +70 -41
- package/build/apkParser.test.js +2 -2
- package/build/apkUnparser.d.ts +4 -0
- package/build/apkUnparser.js +90 -0
- package/build/apkUnparser.test.d.ts +1 -0
- package/build/apkUnparser.test.js +26 -0
- package/build/arbitraryFileSystemEntry.js +1 -1
- package/build/arbitraryZip.d.ts +1 -1
- package/build/arbitraryZip.js +13 -19
- package/build/arbitraryZipPermissions.d.ts +1 -8
- package/build/arbitraryZipPermissions.js +1 -16
- package/build/arbitraryZipStream.d.ts +1 -1
- package/build/arbitraryZipStream.js +3 -3
- package/build/arrayParser.d.ts +1 -1
- package/build/arrayParser.js +2 -2
- package/build/arrayParser.test.js +2 -2
- package/build/arrayUnparser.d.ts +2 -0
- package/build/arrayUnparser.js +8 -0
- package/build/bsonParser.test.js +2 -2
- package/build/customInvariant.d.ts +4 -0
- package/build/customInvariant.js +11 -0
- package/build/debugLogParser.d.ts +1 -1
- package/build/debugLogParser.js +1 -1
- package/build/elementParser.d.ts +2 -2
- package/build/elementParser.js +1 -1
- package/build/endOfInputParser.d.ts +2 -2
- package/build/exactElementParser.d.ts +1 -1
- package/build/exactSequenceParser.js +1 -1
- package/build/index.d.ts +5 -2
- package/build/index.js +3 -0
- package/build/inputReader.d.ts +3 -3
- package/build/inputReader.js +6 -6
- package/build/inputReader.test.js +7 -7
- package/build/javaKeyStore.d.ts +1 -0
- package/build/javaKeyStore.js +1 -0
- package/build/javaKeyStoreParser.d.ts +2 -0
- package/build/javaKeyStoreParser.js +67 -0
- package/build/javaKeyStoreParser.test.d.ts +1 -0
- package/build/javaKeyStoreParser.test.js +16 -0
- package/build/javaKeystoreParser.d.ts +2 -0
- package/build/javaKeystoreParser.js +7 -0
- package/build/jsonParser.js +3 -2
- package/build/jsonParser.test.js +2 -2
- package/build/listParser.d.ts +1 -1
- package/build/listParser.js +5 -5
- package/build/negativeLookahead.d.ts +1 -1
- package/build/negativeLookahead.js +16 -18
- package/build/negativeLookaheadParser.d.ts +2 -0
- package/build/negativeLookaheadParser.js +18 -0
- package/build/optionalParser.d.ts +1 -1
- package/build/optionalParser.js +2 -2
- package/build/parser.d.ts +3 -3
- package/build/parser.js +3 -3
- package/build/parser.test.js +16 -16
- package/build/parserAccessorParser.d.ts +1 -1
- package/build/parserConsumedSequenceParser.d.ts +2 -0
- package/build/parserConsumedSequenceParser.js +17 -0
- package/build/parserContext.d.ts +6 -6
- package/build/parserContext.js +15 -14
- package/build/parserContext.test.js +7 -7
- package/build/parserCreatorCompose.d.ts +3 -3
- package/build/parserCreatorCompose.js +2 -2
- package/build/parserImplementationInvariant.d.ts +1 -1
- package/build/parserImplementationInvariant.js +2 -2
- package/build/parserInputCompanion.d.ts +20 -0
- package/build/parserInputCompanion.js +30 -0
- package/build/parserInvariant.d.ts +1 -1
- package/build/parserInvariant.js +1 -1
- package/build/quantifierParser.d.ts +2 -0
- package/build/quantifierParser.js +17 -0
- package/build/sequenceBuffer.d.ts +3 -3
- package/build/sequenceBuffer.js +6 -6
- package/build/sequenceBuffer.test.js +2 -2
- package/build/sequenceUnparser.d.ts +2 -0
- package/build/sequenceUnparser.js +6 -0
- package/build/sliceBoundedParser.d.ts +1 -1
- package/build/sliceBoundedParser.js +1 -1
- package/build/sliceBoundedParser.test.js +2 -2
- package/build/terminatedArrayParser.d.ts +1 -1
- package/build/terminatedArrayParser.js +3 -3
- package/build/uint8Array.d.ts +1 -0
- package/build/uint8Array.js +7 -0
- package/build/unparser.d.ts +8 -0
- package/build/unparser.js +104 -0
- package/build/unparser.test.d.ts +1 -0
- package/build/unparser.test.js +150 -0
- package/build/unparserContext.d.ts +31 -0
- package/build/unparserContext.js +74 -0
- package/build/unparserError.d.ts +9 -0
- package/build/unparserError.js +9 -0
- package/build/unparserImplementationInvariant.d.ts +2 -0
- package/build/unparserImplementationInvariant.js +5 -0
- package/build/unparserInputCompanion.d.ts +15 -0
- package/build/unparserInputCompanion.js +13 -0
- package/build/unparserOutputCompanion.d.ts +15 -0
- package/build/unparserOutputCompanion.js +13 -0
- package/build/zip.d.ts +9 -17
- package/build/zipParser.d.ts +13 -10
- package/build/zipParser.js +48 -60
- package/build/zipParser.test.js +2 -7
- package/build/zipUnparser.d.ts +5 -0
- package/build/zipUnparser.js +171 -0
- package/build/zipUnparser.test.d.ts +1 -0
- package/build/zipUnparser.test.js +80 -0
- package/package.json +4 -2
- package/src/apk.ts +35 -3
- package/src/apkParser.test.ts +2 -2
- package/src/apkParser.test.ts.md +114 -111
- package/src/apkParser.test.ts.snap +0 -0
- package/src/apkParser.ts +150 -85
- package/src/apkUnparser.test.ts +37 -0
- package/src/apkUnparser.ts +120 -0
- package/src/arbitraryFileSystemEntry.ts +2 -4
- package/src/arbitraryZip.ts +20 -27
- package/src/arbitraryZipPermissions.ts +0 -25
- package/src/arbitraryZipStream.ts +4 -4
- package/src/arrayParser.test.ts +3 -3
- package/src/arrayParser.ts +3 -2
- package/src/arrayUnparser.ts +13 -0
- package/src/bsonParser.test.ts +2 -2
- package/src/bsonParser.ts +3 -3
- package/src/{parserInvariant.ts → customInvariant.ts} +1 -1
- package/src/debugLogParser.ts +1 -1
- package/src/elementParser.ts +3 -3
- package/src/endOfInputParser.ts +4 -4
- package/src/exactElementParser.ts +1 -1
- package/src/exactSequenceParser.ts +2 -2
- package/src/index.ts +15 -2
- package/src/inputReader.test.ts +7 -7
- package/src/inputReader.ts +5 -5
- package/src/javaKeyStore.ts +0 -0
- package/src/javaKeyStoreParser.test.ts +23 -0
- package/src/javaKeyStoreParser.test.ts.md +103 -0
- package/src/javaKeyStoreParser.test.ts.snap +0 -0
- package/src/javaKeyStoreParser.ts +136 -0
- package/src/jsonParser.test.ts +2 -2
- package/src/jsonParser.ts +13 -12
- package/src/listParser.ts +6 -6
- package/src/negativeLookaheadParser.ts +24 -0
- package/src/optionalParser.ts +3 -3
- package/src/parser.test.ts +19 -17
- package/src/parser.ts +7 -7
- package/src/parserAccessorParser.ts +1 -1
- package/src/parserConsumedSequenceParser.ts +20 -0
- package/src/parserContext.test.ts +7 -7
- package/src/parserContext.ts +18 -14
- package/src/parserCreatorCompose.ts +6 -6
- package/src/parserImplementationInvariant.ts +2 -2
- package/src/{inputCompanion.ts → parserInputCompanion.ts} +10 -6
- package/src/quantifierParser.ts +25 -0
- package/src/sequenceBuffer.test.ts +2 -2
- package/src/sequenceBuffer.ts +5 -5
- package/src/sequenceUnparser.ts +9 -0
- package/src/sliceBoundedParser.test.ts +2 -2
- package/src/sliceBoundedParser.ts +2 -2
- package/src/terminatedArrayParser.ts +3 -3
- package/src/uint8Array.ts +10 -0
- package/src/unparser.test.ts +221 -0
- package/src/unparser.ts +209 -0
- package/src/unparserContext.ts +127 -0
- package/src/unparserError.ts +12 -0
- package/src/unparserImplementationInvariant.ts +6 -0
- package/src/unparserOutputCompanion.ts +24 -0
- package/src/zip.ts +10 -22
- package/src/zipParser.test.ts +2 -8
- package/src/zipParser.ts +147 -129
- package/src/zipUnparser.test.ts +119 -0
- package/src/zipUnparser.ts +239 -0
- package/src/negativeLookahead.ts +0 -26
package/src/zipParser.ts
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
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) &
|
|
38
|
-
((date >> 5) &
|
|
39
|
-
date &
|
|
40
|
-
(time >> 11) &
|
|
41
|
-
(time >> 5) &
|
|
42
|
-
(time &
|
|
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
|
-
|
|
72
|
+
lastModifiedFile: Date;
|
|
66
73
|
crc32: number;
|
|
67
74
|
compressedSize: number;
|
|
68
75
|
uncompressedSize: number;
|
|
69
76
|
|
|
70
|
-
|
|
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
|
-
|
|
96
|
+
filePathLength,
|
|
90
97
|
extraFieldLength,
|
|
91
98
|
]) => createTupleParser([
|
|
92
|
-
createFixedLengthSequenceParser(
|
|
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
|
-
|
|
114
|
+
lastModifiedFile,
|
|
108
115
|
crc32,
|
|
109
116
|
compressedSize,
|
|
110
117
|
uncompressedSize,
|
|
111
|
-
[
|
|
118
|
+
[ filePath, extraField ],
|
|
112
119
|
]) => ({
|
|
113
120
|
versionNeededToExtract,
|
|
114
121
|
generalPurposeBitFlag,
|
|
115
122
|
compressionMethod,
|
|
116
|
-
|
|
123
|
+
lastModifiedFile,
|
|
117
124
|
crc32,
|
|
118
125
|
compressedSize,
|
|
119
126
|
uncompressedSize,
|
|
120
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
235
|
+
externalFileAttributes: ZipExternalFileAttributes;
|
|
206
236
|
relativeOffsetOfLocalHeader: number;
|
|
207
237
|
|
|
208
|
-
|
|
238
|
+
filePath: string;
|
|
209
239
|
extraField: Uint8Array;
|
|
210
240
|
fileComment: string;
|
|
211
241
|
};
|
|
212
242
|
|
|
213
|
-
const zipCentralDirectoryHeaderParser_ =
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
-
|
|
306
|
+
lastModifiedFile,
|
|
266
307
|
crc32,
|
|
267
308
|
compressedSize,
|
|
268
309
|
uncompressedSize,
|
|
269
310
|
[
|
|
270
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
434
|
+
path: zipLocalFileHeader.filePath,
|
|
415
435
|
comment: centralDirectoryHeader.fileComment,
|
|
416
|
-
date: zipLocalFileHeader.
|
|
417
|
-
|
|
436
|
+
date: zipLocalFileHeader.lastModifiedFile,
|
|
437
|
+
hostSystem: centralDirectoryHeader.versionMadeBy.hostSystem === 0 ? 'dos' : 'unix',
|
|
438
|
+
attributes: centralDirectoryHeader.externalFileAttributes,
|
|
418
439
|
};
|
|
419
440
|
|
|
420
|
-
const isDirectory =
|
|
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.
|
|
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
|
|
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,
|
|
442
|
-
(
|
|
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
|
+
);
|