@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/build/zipParser.test.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { testProp } from '@fast-check/ava';
|
|
2
2
|
import { arbitraryZipStream } from './arbitraryZipStream.js';
|
|
3
3
|
import { zipParser } from './zipParser.js';
|
|
4
|
-
import {
|
|
4
|
+
import { uint8ArrayParserInputCompanion } from './parserInputCompanion.js';
|
|
5
5
|
import { runParser } from './parser.js';
|
|
6
|
-
async function* readableStreamToUint8ArrayAsyncIterator(stream) {
|
|
7
|
-
for await (const chunk of stream) {
|
|
8
|
-
yield chunk;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
6
|
testProp('zip', [
|
|
12
7
|
arbitraryZipStream,
|
|
13
8
|
], async (t, [zip, zipStream]) => {
|
|
14
|
-
const actual = await runParser(zipParser,
|
|
9
|
+
const actual = await runParser(zipParser, zipStream, uint8ArrayParserInputCompanion, {
|
|
15
10
|
errorJoinMode: 'all',
|
|
16
11
|
});
|
|
17
12
|
actual.entries.sort((a, b) => a.path.localeCompare(b.path));
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Unparser } from "./unparser.js";
|
|
2
|
+
import { Zip } from "./zip.js";
|
|
3
|
+
import { ZipEndOfCentralDirectoryRecord } from './zipParser.js';
|
|
4
|
+
export declare const zipEndOfCentralDirectoryRecordUnparser: Unparser<ZipEndOfCentralDirectoryRecord, Uint8Array>;
|
|
5
|
+
export declare const zipUnparser: Unparser<Zip, Uint8Array>;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import zlib from 'node:zlib';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
3
|
+
import { pipeline } from 'node:stream/promises';
|
|
4
|
+
import { uint8ArrayAsyncIterableToUint8Array } from './uint8Array.js';
|
|
5
|
+
const uint16LEUnparser = async function* (uint16LE) {
|
|
6
|
+
const buffer = Buffer.alloc(2);
|
|
7
|
+
buffer.writeUInt16LE(uint16LE);
|
|
8
|
+
yield buffer;
|
|
9
|
+
};
|
|
10
|
+
const uint32LEUnparser = async function* (uint32LE) {
|
|
11
|
+
const buffer = Buffer.alloc(4);
|
|
12
|
+
buffer.writeUInt32LE(uint32LE);
|
|
13
|
+
yield buffer;
|
|
14
|
+
};
|
|
15
|
+
const uint16LEPrefixedUint8ArrayUnparser = async function* (uint8Array, unparserContext) {
|
|
16
|
+
yield* uint16LEUnparser(uint8Array.length, unparserContext);
|
|
17
|
+
yield uint8Array;
|
|
18
|
+
};
|
|
19
|
+
const uint16LEPrefixedStringUnparser = async function* (string, unparserContext) {
|
|
20
|
+
yield* uint16LEPrefixedUint8ArrayUnparser(Buffer.from(string, 'utf8'), unparserContext);
|
|
21
|
+
};
|
|
22
|
+
const dosDateTimeUnparser = async function* (date, unparserContext) {
|
|
23
|
+
yield* uint16LEUnparser((date.getUTCSeconds() / 2
|
|
24
|
+
| date.getUTCMinutes() << 5
|
|
25
|
+
| date.getUTCHours() << 11), unparserContext);
|
|
26
|
+
yield* uint16LEUnparser((date.getUTCDate()
|
|
27
|
+
| (date.getUTCMonth() + 1) << 5
|
|
28
|
+
| (date.getUTCFullYear() - 1980) << 9), unparserContext);
|
|
29
|
+
};
|
|
30
|
+
const zipCompressionMethodUnparser = async function* (compressionMethod, unparserContext) {
|
|
31
|
+
if (compressionMethod === 'store') {
|
|
32
|
+
yield* uint16LEUnparser(0, unparserContext);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
yield* uint16LEUnparser(8, unparserContext);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
export const zipEndOfCentralDirectoryRecordUnparser = async function* (zipEndOfCentralDirectoryRecord, unparserContext) {
|
|
39
|
+
yield Buffer.from('504b0506', 'hex');
|
|
40
|
+
yield* uint16LEUnparser(zipEndOfCentralDirectoryRecord.numberOfThisDisk, unparserContext);
|
|
41
|
+
yield* uint16LEUnparser(zipEndOfCentralDirectoryRecord.numberOfTheDiskWithTheStartOfTheCentralDirectory, unparserContext);
|
|
42
|
+
yield* uint16LEUnparser(zipEndOfCentralDirectoryRecord.totalNumberOfEntriesInTheCentralDirectoryOnThisDisk, unparserContext);
|
|
43
|
+
yield* uint16LEUnparser(zipEndOfCentralDirectoryRecord.totalNumberOfEntriesInTheCentralDirectory, unparserContext);
|
|
44
|
+
yield* uint32LEUnparser(zipEndOfCentralDirectoryRecord.sizeOfTheCentralDirectory, unparserContext);
|
|
45
|
+
yield* uint32LEUnparser(zipEndOfCentralDirectoryRecord.offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber, unparserContext);
|
|
46
|
+
yield* uint16LEPrefixedStringUnparser(zipEndOfCentralDirectoryRecord.zipFileComment, unparserContext);
|
|
47
|
+
};
|
|
48
|
+
export const zipUnparser = async function* (zip, unparserContext) {
|
|
49
|
+
const compressedContentByZipFileEntry = new Map(zip.entries.flatMap(zipEntry => {
|
|
50
|
+
if (zipEntry.type !== 'file') {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
if (zipEntry.compression === 'store') {
|
|
54
|
+
return [[zipEntry, Promise.resolve(zipEntry.content)]];
|
|
55
|
+
}
|
|
56
|
+
const uncompressedContent = zipEntry.content;
|
|
57
|
+
const deflate = zlib.createDeflateRaw();
|
|
58
|
+
const input = Readable.from(Buffer.from(uncompressedContent));
|
|
59
|
+
const promise = Promise.all([
|
|
60
|
+
pipeline(input, deflate),
|
|
61
|
+
uint8ArrayAsyncIterableToUint8Array(deflate),
|
|
62
|
+
]);
|
|
63
|
+
return [[zipEntry, promise.then(([, compressedContent]) => compressedContent)]];
|
|
64
|
+
}));
|
|
65
|
+
const filePathByZipEntry = new Map(zip.entries.map(zipEntry => [zipEntry, zipEntry.type === 'file' ? zipEntry.path : zipEntry.path + '/']));
|
|
66
|
+
const localHeaderPositionByZipEntry = new Map();
|
|
67
|
+
for (const zipEntry of zip.entries) {
|
|
68
|
+
localHeaderPositionByZipEntry.set(zipEntry, unparserContext.position);
|
|
69
|
+
yield Buffer.from('504b0304', 'hex');
|
|
70
|
+
yield* uint16LEUnparser(10, unparserContext); // version needed to extract
|
|
71
|
+
yield* uint16LEUnparser(0, unparserContext); // general purpose bit flag
|
|
72
|
+
if (zipEntry.type === 'file') {
|
|
73
|
+
yield* zipCompressionMethodUnparser(zipEntry.compression, unparserContext);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
yield* uint16LEUnparser(0, unparserContext);
|
|
77
|
+
}
|
|
78
|
+
yield* dosDateTimeUnparser(zipEntry.date, unparserContext);
|
|
79
|
+
if (zipEntry.type === 'file') {
|
|
80
|
+
const compressedContent = await compressedContentByZipFileEntry.get(zipEntry);
|
|
81
|
+
yield* uint32LEUnparser(0, unparserContext); // crc32 // TODO
|
|
82
|
+
yield* uint32LEUnparser(compressedContent.length, unparserContext);
|
|
83
|
+
yield* uint32LEUnparser(zipEntry.content.length, unparserContext);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
yield* uint32LEUnparser(0, unparserContext);
|
|
87
|
+
yield* uint32LEUnparser(0, unparserContext);
|
|
88
|
+
yield* uint32LEUnparser(0, unparserContext);
|
|
89
|
+
}
|
|
90
|
+
const filePath = filePathByZipEntry.get(zipEntry);
|
|
91
|
+
const filePathBuffer = Buffer.from(filePath, 'utf8');
|
|
92
|
+
const extraFieldBuffer = Buffer.alloc(0);
|
|
93
|
+
yield* uint16LEUnparser(filePathBuffer.length, unparserContext);
|
|
94
|
+
yield* uint16LEUnparser(extraFieldBuffer.length, unparserContext);
|
|
95
|
+
yield filePathBuffer;
|
|
96
|
+
yield extraFieldBuffer;
|
|
97
|
+
if (zipEntry.type === 'file') {
|
|
98
|
+
const compressedContent = await compressedContentByZipFileEntry.get(zipEntry);
|
|
99
|
+
yield compressedContent;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const startOfCentralDirectoryPosition = unparserContext.position;
|
|
103
|
+
for (const zipEntry of zip.entries) {
|
|
104
|
+
yield Buffer.from('504b0102', 'hex');
|
|
105
|
+
if (zipEntry.hostSystem === 'unix') {
|
|
106
|
+
yield 0; // zip specification version
|
|
107
|
+
yield 3; // host system
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
yield 0;
|
|
111
|
+
yield 0;
|
|
112
|
+
}
|
|
113
|
+
yield* uint16LEUnparser(0, unparserContext); // version needed to extract
|
|
114
|
+
yield* uint16LEUnparser(0, unparserContext); // general purpose bit flag
|
|
115
|
+
if (zipEntry.type === 'file') {
|
|
116
|
+
yield* zipCompressionMethodUnparser(zipEntry.compression, unparserContext);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
yield* uint16LEUnparser(0, unparserContext);
|
|
120
|
+
}
|
|
121
|
+
yield* dosDateTimeUnparser(zipEntry.date, unparserContext);
|
|
122
|
+
if (zipEntry.type === 'file') {
|
|
123
|
+
const compressedContent = await compressedContentByZipFileEntry.get(zipEntry);
|
|
124
|
+
yield* uint32LEUnparser(0, unparserContext); // crc32 // TODO
|
|
125
|
+
yield* uint32LEUnparser(compressedContent.length, unparserContext);
|
|
126
|
+
yield* uint32LEUnparser(zipEntry.content.length, unparserContext);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
yield* uint32LEUnparser(0, unparserContext);
|
|
130
|
+
yield* uint32LEUnparser(0, unparserContext);
|
|
131
|
+
yield* uint32LEUnparser(0, unparserContext);
|
|
132
|
+
}
|
|
133
|
+
const filePath = filePathByZipEntry.get(zipEntry);
|
|
134
|
+
const filePathBuffer = Buffer.from(filePath, 'utf8');
|
|
135
|
+
yield* uint16LEUnparser(filePathBuffer.length, unparserContext);
|
|
136
|
+
const extraFieldBuffer = Buffer.alloc(0);
|
|
137
|
+
yield* uint16LEUnparser(extraFieldBuffer.length, unparserContext);
|
|
138
|
+
const fileCommentBuffer = Buffer.from(zipEntry.comment, 'utf8');
|
|
139
|
+
yield* uint16LEUnparser(fileCommentBuffer.length, unparserContext);
|
|
140
|
+
yield* uint16LEUnparser(0, unparserContext); // disk number start
|
|
141
|
+
yield* uint16LEUnparser(0, unparserContext); // internal file attributes
|
|
142
|
+
if (zipEntry.hostSystem === 'unix') {
|
|
143
|
+
yield* uint32LEUnparser((0
|
|
144
|
+
| (zipEntry.type === 'directory'
|
|
145
|
+
? (0b0100_0000_0000_0000 << 16)
|
|
146
|
+
: (0b1000_0000_0000_0000 << 16))) >>> 0, unparserContext);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
yield* uint32LEUnparser((0
|
|
150
|
+
| (zipEntry.type === 'directory'
|
|
151
|
+
? 0b0001_0000
|
|
152
|
+
: 0)), unparserContext);
|
|
153
|
+
}
|
|
154
|
+
const localHeaderPosition = localHeaderPositionByZipEntry.get(zipEntry);
|
|
155
|
+
yield* uint32LEUnparser(localHeaderPosition, unparserContext);
|
|
156
|
+
yield filePathBuffer;
|
|
157
|
+
yield extraFieldBuffer;
|
|
158
|
+
yield fileCommentBuffer;
|
|
159
|
+
}
|
|
160
|
+
const endOfCentralDirectoryPosition = unparserContext.position;
|
|
161
|
+
const sizeOfTheCentralDirectory = endOfCentralDirectoryPosition - startOfCentralDirectoryPosition;
|
|
162
|
+
yield* zipEndOfCentralDirectoryRecordUnparser({
|
|
163
|
+
numberOfThisDisk: 0,
|
|
164
|
+
numberOfTheDiskWithTheStartOfTheCentralDirectory: 0,
|
|
165
|
+
totalNumberOfEntriesInTheCentralDirectoryOnThisDisk: zip.entries.length,
|
|
166
|
+
totalNumberOfEntriesInTheCentralDirectory: zip.entries.length,
|
|
167
|
+
sizeOfTheCentralDirectory,
|
|
168
|
+
offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber: startOfCentralDirectoryPosition,
|
|
169
|
+
zipFileComment: zip.comment,
|
|
170
|
+
}, unparserContext);
|
|
171
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
async function hasExecutable(executable) {
|
|
13
|
+
const hasExecutable = execa(executable).then(() => true, () => false);
|
|
14
|
+
if (!hasExecutable) {
|
|
15
|
+
console.warn(`Executable %o not found`, executable);
|
|
16
|
+
}
|
|
17
|
+
return hasExecutable;
|
|
18
|
+
}
|
|
19
|
+
const hasZipinfoPromise = hasExecutable('zipinfo');
|
|
20
|
+
const has7zPromise = hasExecutable('7z');
|
|
21
|
+
async function zipinfo(zipFilePath) {
|
|
22
|
+
const { stdout } = await execa('zipinfo', [
|
|
23
|
+
'-v',
|
|
24
|
+
zipFilePath,
|
|
25
|
+
], {
|
|
26
|
+
reject: false,
|
|
27
|
+
});
|
|
28
|
+
return {
|
|
29
|
+
isEmptyZipfile: stdout.endsWith('Empty zipfile.'),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async function _7zList(zipFilePath) {
|
|
33
|
+
const { stdout } = await execa('7z', [
|
|
34
|
+
'l',
|
|
35
|
+
zipFilePath,
|
|
36
|
+
], {
|
|
37
|
+
reject: false,
|
|
38
|
+
});
|
|
39
|
+
if (/Errors: \d+$/.test(stdout)) {
|
|
40
|
+
throw new Error(`7z reports errors: ${stdout}`);
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
isEmptyZipfile: false, // TODO
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
testProp('zip', [
|
|
47
|
+
arbitraryZip,
|
|
48
|
+
], async (t, zip) => {
|
|
49
|
+
const actualStream = runUnparser(zipUnparser, zip, uint8ArrayUnparserOutputCompanion);
|
|
50
|
+
const actual = await runParser(zipParser, actualStream, uint8ArrayParserInputCompanion);
|
|
51
|
+
const isDeepEqual = t.deepEqual(actual, zip);
|
|
52
|
+
if (!isDeepEqual) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const hasZipinfo = await hasZipinfoPromise;
|
|
56
|
+
const has7z = await has7zPromise;
|
|
57
|
+
if (!hasZipinfo && !has7z) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const temporaryFilePath = temporaryFile({
|
|
61
|
+
extension: 'zip',
|
|
62
|
+
});
|
|
63
|
+
const zipStream = runUnparser(zipUnparser, zip, uint8ArrayUnparserOutputCompanion);
|
|
64
|
+
await fsPromises.writeFile(temporaryFilePath, zipStream);
|
|
65
|
+
if (has7z) {
|
|
66
|
+
const { isEmptyZipfile, } = await _7zList(temporaryFilePath);
|
|
67
|
+
if (isEmptyZipfile) {
|
|
68
|
+
t.deepEqual(actual.entries, [], '7z reports the zipfile as empty, but it has entries');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (hasZipinfo) {
|
|
72
|
+
const { isEmptyZipfile, } = await zipinfo(temporaryFilePath);
|
|
73
|
+
if (isEmptyZipfile) {
|
|
74
|
+
t.deepEqual(actual.entries.filter(entry => entry.type === 'file'), [], 'zipinfo reports the zipfile as empty, but it has file entries');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
await fsPromises.unlink(temporaryFilePath);
|
|
78
|
+
}, {
|
|
79
|
+
verbose: true,
|
|
80
|
+
});
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futpib/parser",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"main": "build/index.js",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"dev": "tsc --watch",
|
|
9
9
|
"build": "tsc",
|
|
10
|
-
"test": "
|
|
10
|
+
"test": "c8 ava",
|
|
11
11
|
"prepack": "yarn build"
|
|
12
12
|
},
|
|
13
13
|
"homepage": "https://github.com/futpib/parser",
|
|
@@ -30,9 +30,11 @@
|
|
|
30
30
|
"c8": "^10.1.2",
|
|
31
31
|
"coveralls": "^3.1.1",
|
|
32
32
|
"eslint-config-xo-typescript-overrides": "^1.6.1",
|
|
33
|
+
"execa": "^9.5.2",
|
|
33
34
|
"fast-check": "^3.23.1",
|
|
34
35
|
"invariant": "^2.2.4",
|
|
35
36
|
"jszip": "^3.10.1",
|
|
37
|
+
"tempy": "^3.1.0",
|
|
36
38
|
"type-fest": "^4.29.0",
|
|
37
39
|
"typescript": "^5.7.2",
|
|
38
40
|
"xo": "^0.59.3"
|
package/src/apk.ts
CHANGED
|
@@ -1,14 +1,46 @@
|
|
|
1
|
-
import { Zip } from './zip.js';
|
|
1
|
+
import { type Zip } from './zip.js';
|
|
2
2
|
|
|
3
3
|
export type ApkSigningBlockPair = {
|
|
4
4
|
id: number;
|
|
5
5
|
value: Uint8Array;
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
export type
|
|
8
|
+
export type ApkSignatureV2Digest = {
|
|
9
|
+
signatureAlgorithmId: number;
|
|
10
|
+
digest: Uint8Array;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type ApkSignatureV2AdditionalAttribute = {
|
|
14
|
+
id: number;
|
|
15
|
+
value: Uint8Array;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type ApkSignatureV2SignedData = {
|
|
19
|
+
digests: ApkSignatureV2Digest[];
|
|
20
|
+
certificates: Uint8Array[];
|
|
21
|
+
additionalAttributes: ApkSignatureV2AdditionalAttribute[];
|
|
9
22
|
zeroPaddingLength?: number;
|
|
10
|
-
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type ApkSignatureV2Signature = {
|
|
26
|
+
signatureAlgorithmId: number;
|
|
27
|
+
signature: Uint8Array;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type ApkSignatureV2Signer = {
|
|
31
|
+
signedData: ApkSignatureV2SignedData;
|
|
32
|
+
signatures: ApkSignatureV2Signature[];
|
|
33
|
+
publicKey: Uint8Array;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type ApkSignatureV2 = {
|
|
37
|
+
signers: ApkSignatureV2Signer[];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type ApkSigningBlock = {
|
|
11
41
|
pairs: ApkSigningBlockPair[];
|
|
42
|
+
signatureV2?: ApkSignatureV2;
|
|
43
|
+
zeroPaddingLength?: number;
|
|
12
44
|
};
|
|
13
45
|
|
|
14
46
|
export type Apk = Zip & {
|
package/src/apkParser.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import test from 'ava';
|
|
2
|
-
import {
|
|
2
|
+
import { uint8ArrayParserInputCompanion } from './parserInputCompanion.js';
|
|
3
3
|
import { runParser } from './parser.js';
|
|
4
4
|
import { apkParser } from './apkParser.js';
|
|
5
5
|
|
|
@@ -14,7 +14,7 @@ for (const apkCid of [
|
|
|
14
14
|
|
|
15
15
|
const apkStream = apkResponse.body!;
|
|
16
16
|
|
|
17
|
-
const actual = await runParser(apkParser, apkStream,
|
|
17
|
+
const actual = await runParser(apkParser, apkStream, uint8ArrayParserInputCompanion, {
|
|
18
18
|
errorJoinMode: 'all',
|
|
19
19
|
});
|
|
20
20
|
|