@futpib/parser 1.0.1 → 1.0.2
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/allSettledStream.js +32 -14
- package/build/allSettledStream.test.js +32 -0
- package/build/androidPackage.d.ts +39 -0
- package/build/androidPackageParser.d.ts +17 -0
- package/build/androidPackageParser.js +183 -0
- package/build/{apkParser.test.js → androidPackageParser.test.js} +6 -6
- package/build/androidPackageUnparser.d.ts +4 -0
- package/build/{apkUnparser.js → androidPackageUnparser.js} +23 -23
- package/build/androidPackageUnparser.test.js +26 -0
- package/build/arbitrarilySlicedAsyncInterable.d.ts +3 -1
- package/build/arbitrarilySlicedAsyncInterable.js +3 -3
- package/build/arrayParser.test.js +3 -3
- package/build/backsmali.d.ts +1 -0
- package/build/backsmali.js +22 -0
- package/build/bsonParser.js +6 -2
- package/build/customInvariant.d.ts +1 -1
- package/build/dalvikBytecodeParser/formatParsers.d.ts +97 -0
- package/build/dalvikBytecodeParser/formatParsers.js +169 -0
- package/build/dalvikBytecodeParser.d.ts +107 -0
- package/build/dalvikBytecodeParser.js +836 -0
- package/build/dalvikExecutable.d.ts +158 -0
- package/build/dalvikExecutable.js +20 -0
- package/build/dalvikExecutableParser/stringSyntaxParser.d.ts +4 -0
- package/build/dalvikExecutableParser/stringSyntaxParser.js +76 -0
- package/build/dalvikExecutableParser/typeParsers.d.ts +10 -0
- package/build/dalvikExecutableParser/typeParsers.js +34 -0
- package/build/dalvikExecutableParser/typedNumbers.d.ts +90 -0
- package/build/dalvikExecutableParser/typedNumbers.js +19 -0
- package/build/dalvikExecutableParser.d.ts +5 -0
- package/build/dalvikExecutableParser.js +1439 -0
- package/build/dalvikExecutableParser.test.js +70 -0
- package/build/dalvikExecutableParserAgainstSmaliParser.test.js +298 -0
- package/build/debugLogInputParser.d.ts +4 -0
- package/build/debugLogInputParser.js +16 -0
- package/build/debugLogParser.js +14 -3
- package/build/disjunctionParser.d.ts +2 -1
- package/build/disjunctionParser.js +4 -2
- package/build/elementTerminatedArrayParser.d.ts +3 -0
- package/build/elementTerminatedArrayParser.js +18 -0
- package/build/elementTerminatedArrayParser.test.js +52 -0
- package/build/elementTerminatedSequenceArrayParser.d.ts +3 -0
- package/build/elementTerminatedSequenceArrayParser.js +32 -0
- package/build/elementTerminatedSequenceArrayParser.test.js +34 -0
- package/build/elementTerminatedSequenceParser.d.ts +3 -0
- package/build/elementTerminatedSequenceParser.js +27 -0
- package/build/elementTerminatedSequenceParser.test.js +34 -0
- package/build/exactElementParser.js +10 -5
- package/build/exactSequenceParser.d.ts +2 -1
- package/build/exactSequenceParser.js +12 -2
- package/build/fetchCid.d.ts +1 -0
- package/build/fetchCid.js +107 -0
- package/build/fixedLengthSequenceParser.d.ts +1 -0
- package/build/fixedLengthSequenceParser.js +18 -1
- package/build/fixedLengthSequenceParser.test.js +41 -0
- package/build/hasExecutable.d.ts +1 -0
- package/build/hasExecutable.js +8 -0
- package/build/highResolutionTimer.d.ts +16 -0
- package/build/highResolutionTimer.js +42 -0
- package/build/inputReader.d.ts +11 -0
- package/build/inputReader.js +37 -0
- package/build/inputReader.test.js +165 -0
- package/build/inputReaderState.d.ts +10 -0
- package/build/inputReaderState.js +16 -0
- package/build/inspect.d.ts +1 -0
- package/build/inspect.js +7 -0
- package/build/javaKeyStoreParser.test.js +2 -2
- package/build/jsonParser.d.ts +2 -0
- package/build/jsonParser.js +11 -14
- package/build/leb128Parser.d.ts +7 -0
- package/build/leb128Parser.js +82 -0
- package/build/leb128Parser.test.js +107 -0
- package/build/lookaheadParser.d.ts +2 -0
- package/build/lookaheadParser.js +14 -0
- package/build/negativeLookaheadParser.js +21 -15
- package/build/negativeLookaheadParser.test.js +30 -0
- package/build/nonEmptyArrayParser.d.ts +2 -0
- package/build/nonEmptyArrayParser.js +32 -0
- package/build/nonEmptyArrayParser.test.js +16 -0
- package/build/parser.d.ts +10 -1
- package/build/parser.js +66 -31
- package/build/parser.test.js +79 -12
- package/build/parserAccessorParser.js +9 -1
- package/build/parserConsumedSequenceParser.js +20 -15
- package/build/parserContext.d.ts +14 -0
- package/build/parserContext.js +56 -27
- package/build/parserContext.test.js +27 -0
- package/build/parserCreatorCompose.d.ts +1 -0
- package/build/parserCreatorCompose.js +8 -2
- package/build/parserError.d.ts +6 -0
- package/build/parserError.js +6 -6
- package/build/parserInputCompanion.d.ts +15 -0
- package/build/parserInputCompanion.js +38 -0
- package/build/promiseCompose.d.ts +1 -1
- package/build/promiseCompose.js +12 -1
- package/build/promiseSettled.d.ts +1 -0
- package/build/promiseSettled.js +4 -0
- package/build/separatedArrayParser.d.ts +2 -0
- package/build/separatedArrayParser.js +39 -0
- package/build/separatedArrayParser.test.d.ts +1 -0
- package/build/separatedArrayParser.test.js +21 -0
- package/build/sequenceBuffer.d.ts +10 -0
- package/build/sequenceBuffer.js +54 -2
- package/build/sequenceBuffer.test.js +57 -0
- package/build/sequenceTerminatedSequenceParser.d.ts +5 -0
- package/build/sequenceTerminatedSequenceParser.js +32 -0
- package/build/sequenceTerminatedSequenceParser.test.d.ts +1 -0
- package/build/sequenceTerminatedSequenceParser.test.js +37 -0
- package/build/skipParser.d.ts +1 -1
- package/build/skipParser.js +4 -2
- package/build/skipToParser.d.ts +2 -0
- package/build/skipToParser.js +11 -0
- package/build/sliceBoundedParser.d.ts +1 -1
- package/build/sliceBoundedParser.js +7 -2
- package/build/sliceBoundedParser.test.js +35 -1
- package/build/smali.d.ts +1 -0
- package/build/smali.js +17 -0
- package/build/smaliParser.d.ts +12 -0
- package/build/smaliParser.js +656 -0
- package/build/smaliParser.test.d.ts +1 -0
- package/build/smaliParser.test.js +115 -0
- package/build/terminatedArrayParser.d.ts +3 -1
- package/build/terminatedArrayParser.js +79 -2
- package/build/terminatedArrayParser.test.d.ts +1 -0
- package/build/terminatedArrayParser.test.js +131 -0
- package/build/toAsyncIterable.d.ts +1 -0
- package/build/toAsyncIterable.js +7 -0
- package/build/toAsyncIterator.d.ts +1 -0
- package/build/toAsyncIterator.js +33 -0
- package/build/tupleParser.d.ts +4 -0
- package/build/tupleParser.js +1 -5
- package/build/unionParser.d.ts +2 -1
- package/build/unionParser.js +27 -12
- package/build/unionParser.test.d.ts +1 -0
- package/build/unionParser.test.js +60 -0
- package/build/zipParser.d.ts +7 -2
- package/build/zipParser.js +36 -12
- package/build/zipUnparser.d.ts +4 -1
- package/build/zipUnparser.js +55 -26
- package/build/zipUnparser.test.js +14 -14
- package/package.json +23 -7
- package/src/allSettledStream.test.ts +40 -0
- package/src/allSettledStream.ts +47 -15
- package/src/androidPackage.ts +48 -0
- package/src/{apkParser.test.ts → androidPackageParser.test.ts} +6 -7
- package/src/{apkParser.test.ts.md → androidPackageParser.test.ts.md} +4 -4
- package/src/androidPackageParser.test.ts.snap +0 -0
- package/src/androidPackageParser.ts +440 -0
- package/src/androidPackageUnparser.test.ts +36 -0
- package/src/androidPackageUnparser.ts +120 -0
- package/src/arbitrarilySlicedAsyncInterable.ts +7 -2
- package/src/arrayParser.test.ts +3 -3
- package/src/backsmali.ts +30 -0
- package/src/bsonParser.ts +13 -2
- package/src/customInvariant.ts +1 -1
- package/src/dalvikBytecodeParser/formatParsers.ts +421 -0
- package/src/dalvikBytecodeParser.ts +2074 -0
- package/src/dalvikExecutable.ts +220 -0
- package/src/dalvikExecutableParser/stringSyntaxParser.ts +145 -0
- package/src/dalvikExecutableParser/typeParsers.ts +65 -0
- package/src/dalvikExecutableParser/typedNumbers.ts +57 -0
- package/src/dalvikExecutableParser.test.ts +89 -0
- package/src/dalvikExecutableParser.test.ts.md +634 -0
- package/src/dalvikExecutableParser.test.ts.snap +0 -0
- package/src/dalvikExecutableParser.ts +2768 -0
- package/src/dalvikExecutableParserAgainstSmaliParser.test.ts +346 -0
- package/src/debugLogInputParser.ts +28 -0
- package/src/debugLogParser.ts +19 -3
- package/src/disjunctionParser.ts +10 -5
- package/src/elementTerminatedArrayParser.test.ts +99 -0
- package/src/elementTerminatedArrayParser.ts +31 -0
- package/src/elementTerminatedSequenceArrayParser.test.ts +54 -0
- package/src/elementTerminatedSequenceArrayParser.ts +52 -0
- package/src/elementTerminatedSequenceParser.test.ts +54 -0
- package/src/elementTerminatedSequenceParser.ts +43 -0
- package/src/exactElementParser.ts +17 -11
- package/src/exactSequenceParser.ts +23 -2
- package/src/fetchCid.ts +125 -0
- package/src/fixedLengthSequenceParser.test.ts +77 -0
- package/src/fixedLengthSequenceParser.ts +28 -1
- package/src/hasExecutable.ts +11 -0
- package/src/highResolutionTimer.ts +49 -0
- package/src/inputReader.test.ts +209 -0
- package/src/inputReader.ts +75 -0
- package/src/inputReaderState.ts +33 -0
- package/src/inspect.ts +9 -0
- package/src/javaKeyStoreParser.test.ts +2 -3
- package/src/jsonParser.ts +13 -25
- package/src/leb128Parser.test.ts +171 -0
- package/src/leb128Parser.ts +125 -0
- package/src/lookaheadParser.ts +19 -0
- package/src/negativeLookaheadParser.test.ts +53 -0
- package/src/negativeLookaheadParser.ts +26 -14
- package/src/nonEmptyArrayParser.test.ts +20 -0
- package/src/nonEmptyArrayParser.ts +44 -0
- package/src/optionalParser.ts +1 -0
- package/src/parser.test.ts +131 -12
- package/src/parser.test.ts.md +21 -21
- package/src/parser.test.ts.snap +0 -0
- package/src/parser.ts +149 -45
- package/src/parserAccessorParser.ts +12 -2
- package/src/parserConsumedSequenceParser.ts +25 -16
- package/src/parserContext.test.ts +31 -0
- package/src/parserContext.ts +109 -37
- package/src/parserCreatorCompose.ts +20 -2
- package/src/parserError.ts +9 -6
- package/src/parserInputCompanion.ts +55 -0
- package/src/promiseCompose.ts +17 -3
- package/src/promiseSettled.ts +6 -0
- package/src/separatedArrayParser.test.ts +34 -0
- package/src/separatedArrayParser.ts +55 -0
- package/src/sequenceBuffer.test.ts +70 -0
- package/src/sequenceBuffer.ts +88 -2
- package/src/sequenceTerminatedSequenceParser.test.ts +60 -0
- package/src/sequenceTerminatedSequenceParser.ts +62 -0
- package/src/skipParser.ts +7 -5
- package/src/skipToParser.ts +16 -0
- package/src/sliceBoundedParser.test.ts +43 -1
- package/src/sliceBoundedParser.ts +19 -1
- package/src/smali.ts +24 -0
- package/src/smaliParser.test.ts +132 -0
- package/src/smaliParser.test.ts.md +2320 -0
- package/src/smaliParser.test.ts.snap +0 -0
- package/src/smaliParser.ts +1166 -0
- package/src/terminatedArrayParser.test.ts +258 -0
- package/src/terminatedArrayParser.ts +108 -3
- package/src/toAsyncIterable.ts +7 -0
- package/src/toAsyncIterator.ts +48 -0
- package/src/tupleParser.ts +8 -5
- package/src/unionParser.test.ts +79 -0
- package/src/unionParser.ts +44 -16
- package/src/zipParser.ts +77 -18
- package/src/zipUnparser.test.ts +18 -18
- package/src/zipUnparser.ts +88 -27
- package/build/apk.d.ts +0 -39
- package/build/apkParser.d.ts +0 -16
- package/build/apkParser.js +0 -164
- package/build/apkUnparser.d.ts +0 -4
- package/build/apkUnparser.test.js +0 -26
- package/build/arbitraryDosDate.d.ts +0 -2
- package/build/arbitraryDosDate.js +0 -8
- package/build/arbitraryZipEntry.d.ts +0 -3
- package/build/arbitraryZipEntry.js +0 -26
- package/build/createDisjunctionParser.d.ts +0 -2
- package/build/createDisjunctionParser.js +0 -47
- package/build/createExactParser.d.ts +0 -2
- package/build/createExactParser.js +0 -12
- package/build/createSequentialUnionParser.d.ts +0 -2
- package/build/createSequentialUnionParser.js +0 -69
- package/build/fixedLengthChunkParser.d.ts +0 -2
- package/build/fixedLengthChunkParser.js +0 -12
- package/build/fixedLengthParser.d.ts +0 -2
- package/build/fixedLengthParser.js +0 -12
- package/build/inputChunkBuffer.d.ts +0 -15
- package/build/inputChunkBuffer.js +0 -40
- package/build/inputChunkBuffer.test.js +0 -34
- package/build/inputCompanion.d.ts +0 -18
- package/build/inputCompanion.js +0 -28
- package/build/invariantDefined.d.ts +0 -1
- package/build/invariantDefined.js +0 -5
- package/build/invariantIdentity.d.ts +0 -3
- package/build/invariantIdentity.js +0 -5
- package/build/javaKeystoreParser.d.ts +0 -2
- package/build/javaKeystoreParser.js +0 -7
- package/build/jsonParser2.d.ts +0 -3
- package/build/jsonParser2.js +0 -52
- package/build/jsonParser2.test.js +0 -22
- package/build/negativeLookahead.d.ts +0 -2
- package/build/negativeLookahead.js +0 -18
- package/build/parserCompose.d.ts +0 -3
- package/build/parserCompose.js +0 -7
- package/build/parserImplementationInvariantInvariant.d.ts +0 -3
- package/build/parserImplementationInvariantInvariant.js +0 -15
- package/build/parserInvariant.d.ts +0 -4
- package/build/parserInvariant.js +0 -11
- package/build/promiseFish.d.ts +0 -1
- package/build/promiseFish.js +0 -3
- package/build/sequenceParser.d.ts +0 -3
- package/build/sequenceParser.js +0 -10
- package/build/terminatedSequenceParser.d.ts +0 -2
- package/build/terminatedSequenceParser.js +0 -24
- package/build/unparserInputCompanion.d.ts +0 -15
- package/build/unparserInputCompanion.js +0 -13
- package/build/zipEntry.d.ts +0 -28
- package/build/zipFile.d.ts +0 -32
- package/build/zipFileEntry.d.ts +0 -6
- package/src/apk.ts +0 -48
- package/src/apkParser.test.ts.snap +0 -0
- package/src/apkParser.ts +0 -392
- package/src/apkUnparser.test.ts +0 -37
- package/src/apkUnparser.ts +0 -120
- package/src/invariantDefined.ts +0 -6
- package/src/invariantIdentity.ts +0 -8
- /package/build/{apk.js → androidPackage.js} +0 -0
- /package/build/{apkParser.test.d.ts → androidPackageParser.test.d.ts} +0 -0
- /package/build/{apkUnparser.test.d.ts → androidPackageUnparser.test.d.ts} +0 -0
- /package/build/{arbitraryDosPermissions.d.ts → dalvikExecutableParser.test.d.ts} +0 -0
- /package/build/{arbitraryDosPermissions.js → dalvikExecutableParserAgainstSmaliParser.test.d.ts} +0 -0
- /package/build/{inputChunkBuffer.test.d.ts → elementTerminatedArrayParser.test.d.ts} +0 -0
- /package/build/{jsonParser2.test.d.ts → elementTerminatedSequenceArrayParser.test.d.ts} +0 -0
- /package/build/{parserParsingInvariant.d.ts → elementTerminatedSequenceParser.test.d.ts} +0 -0
- /package/build/{parserParsingInvariant.js → fixedLengthSequenceParser.test.d.ts} +0 -0
- /package/build/{zipEntry.js → leb128Parser.test.d.ts} +0 -0
- /package/build/{zipFile.js → negativeLookaheadParser.test.d.ts} +0 -0
- /package/build/{zipFileEntry.js → nonEmptyArrayParser.test.d.ts} +0 -0
package/build/zipParser.js
CHANGED
|
@@ -11,6 +11,8 @@ import { createFixedLengthSequenceParser } from './fixedLengthSequenceParser.js'
|
|
|
11
11
|
import { createOptionalParser } from './optionalParser.js';
|
|
12
12
|
import { parserCreatorCompose } from './parserCreatorCompose.js';
|
|
13
13
|
import { uint8ArrayAsyncIterableToUint8Array } from './uint8Array.js';
|
|
14
|
+
import { createNegativeLookaheadParser } from './negativeLookaheadParser.js';
|
|
15
|
+
import { createSequenceTerminatedSequenceParser } from './sequenceTerminatedSequenceParser.js';
|
|
14
16
|
// https://pkwaredownloads.blob.core.windows.net/pem/APPNOTE.txt
|
|
15
17
|
const uint16LEParser = promiseCompose(createFixedLengthSequenceParser(2), array => Buffer.from(array).readUInt16LE());
|
|
16
18
|
setParserName(uint16LEParser, 'uint16LEParser');
|
|
@@ -29,8 +31,9 @@ const zipCompressionMethodParser = promiseCompose(uint16LEParser, compressionMet
|
|
|
29
31
|
}
|
|
30
32
|
invariant(false, 'Unsupported compression method %s', compressionMethod);
|
|
31
33
|
});
|
|
34
|
+
const zipLocalFileHeaderSignatureParser = createExactSequenceParser(Buffer.from('504b0304', 'hex'));
|
|
32
35
|
const zipLocalFileHeaderParser_ = createTupleParser([
|
|
33
|
-
|
|
36
|
+
zipLocalFileHeaderSignatureParser,
|
|
34
37
|
uint16LEParser,
|
|
35
38
|
uint16LEParser,
|
|
36
39
|
zipCompressionMethodParser,
|
|
@@ -61,20 +64,40 @@ const zipLocalFileHeaderParser = promiseCompose(zipLocalFileHeaderParser_, ([_zi
|
|
|
61
64
|
const zipEncryptionHeaderParser = async (parserContext) => {
|
|
62
65
|
parserContext.invariant(false, 'Not implemented');
|
|
63
66
|
};
|
|
64
|
-
const
|
|
67
|
+
const zipDataDescriptorSignature = Buffer.from('504b0708', 'hex');
|
|
68
|
+
const zipDataDescriptorSignatureParser = createExactSequenceParser(zipDataDescriptorSignature);
|
|
69
|
+
const zipDataDescriptorParser = promiseCompose(createTupleParser([
|
|
70
|
+
createNegativeLookaheadParser(zipLocalFileHeaderSignatureParser),
|
|
65
71
|
// FIXME: optional in spec
|
|
66
|
-
//
|
|
67
|
-
|
|
72
|
+
//createOptionalParser(zipDataDescriptorSignatureParser),
|
|
73
|
+
zipDataDescriptorSignatureParser,
|
|
68
74
|
uint32LEParser,
|
|
69
75
|
uint32LEParser,
|
|
70
76
|
uint32LEParser,
|
|
71
|
-
])
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
]), ([_notZipLocalFileHeaderSignature, _zipDataDescriptorSignature, crc32, compressedSize, uncompressedSize,]) => ({
|
|
78
|
+
crc32,
|
|
79
|
+
compressedSize,
|
|
80
|
+
uncompressedSize,
|
|
81
|
+
}));
|
|
82
|
+
setParserName(zipDataDescriptorParser, 'zipDataDescriptorParser');
|
|
83
|
+
export const zipLocalFileParser = promiseCompose(parserCreatorCompose(() => zipLocalFileHeaderParser, zipLocalFileHeader => {
|
|
84
|
+
const sizeInDataDescriptor = Boolean(zipLocalFileHeader.generalPurposeBitFlag & 0b0000_0000_0000_1000
|
|
85
|
+
&& zipLocalFileHeader.crc32 === 0
|
|
86
|
+
&& zipLocalFileHeader.compressedSize === 0
|
|
87
|
+
&& zipLocalFileHeader.uncompressedSize === 0);
|
|
88
|
+
return createTupleParser([
|
|
89
|
+
async () => zipLocalFileHeader,
|
|
90
|
+
createOptionalParser(zipEncryptionHeaderParser),
|
|
91
|
+
(sizeInDataDescriptor
|
|
92
|
+
? createSequenceTerminatedSequenceParser(zipDataDescriptorSignature, {
|
|
93
|
+
consumeTerminator: false,
|
|
94
|
+
})
|
|
95
|
+
: createFixedLengthSequenceParser(zipLocalFileHeader.compressedSize)),
|
|
96
|
+
(sizeInDataDescriptor
|
|
97
|
+
? ((parser) => parser)
|
|
98
|
+
: createOptionalParser)(zipDataDescriptorParser),
|
|
99
|
+
]);
|
|
100
|
+
})(), ([zipLocalFileHeader, zipEncryptionHeader, compressedData, zipDataDescriptor,]) => ({
|
|
78
101
|
zipLocalFileHeader,
|
|
79
102
|
zipEncryptionHeader,
|
|
80
103
|
compressedData,
|
|
@@ -163,7 +186,7 @@ const zipEndOfCentralDirectoryRecordParser_ = createTupleParser([
|
|
|
163
186
|
uint32LEParser,
|
|
164
187
|
zipFileCommentParser,
|
|
165
188
|
]);
|
|
166
|
-
setParserName(zipEndOfCentralDirectoryRecordParser_, '
|
|
189
|
+
setParserName(zipEndOfCentralDirectoryRecordParser_, 'zipEndOfCentralDirectoryRecordParser_');
|
|
167
190
|
export const zipEndOfCentralDirectoryRecordParser = promiseCompose(zipEndOfCentralDirectoryRecordParser_, ([_endOfCentralDirectoryRecordSignature, numberOfThisDisk, numberOfTheDiskWithTheStartOfTheCentralDirectory, totalNumberOfEntriesInTheCentralDirectoryOnThisDisk, totalNumberOfEntriesInTheCentralDirectory, sizeOfTheCentralDirectory, offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber, zipFileComment,]) => ({
|
|
168
191
|
numberOfThisDisk,
|
|
169
192
|
numberOfTheDiskWithTheStartOfTheCentralDirectory,
|
|
@@ -173,6 +196,7 @@ export const zipEndOfCentralDirectoryRecordParser = promiseCompose(zipEndOfCentr
|
|
|
173
196
|
offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber,
|
|
174
197
|
zipFileComment,
|
|
175
198
|
}));
|
|
199
|
+
setParserName(zipEndOfCentralDirectoryRecordParser, 'zipEndOfCentralDirectoryRecordParser');
|
|
176
200
|
const zipParser_ = createTupleParser([
|
|
177
201
|
createArrayParser(zipLocalFileParser),
|
|
178
202
|
createOptionalParser(zipArchiveDecryptionHeaderParser),
|
package/build/zipUnparser.d.ts
CHANGED
|
@@ -2,4 +2,7 @@ import { Unparser } from "./unparser.js";
|
|
|
2
2
|
import { Zip } from "./zip.js";
|
|
3
3
|
import { ZipEndOfCentralDirectoryRecord } from './zipParser.js';
|
|
4
4
|
export declare const zipEndOfCentralDirectoryRecordUnparser: Unparser<ZipEndOfCentralDirectoryRecord, Uint8Array>;
|
|
5
|
-
export declare const
|
|
5
|
+
export declare const createZipUnparser: ({ dataDescriptor, }?: {
|
|
6
|
+
dataDescriptor?: boolean;
|
|
7
|
+
}) => Unparser<Zip, Uint8Array>;
|
|
8
|
+
export declare const zipUnparser: Unparser<Zip, Uint8Array<ArrayBufferLike>>;
|
package/build/zipUnparser.js
CHANGED
|
@@ -45,7 +45,28 @@ export const zipEndOfCentralDirectoryRecordUnparser = async function* (zipEndOfC
|
|
|
45
45
|
yield* uint32LEUnparser(zipEndOfCentralDirectoryRecord.offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber, unparserContext);
|
|
46
46
|
yield* uint16LEPrefixedStringUnparser(zipEndOfCentralDirectoryRecord.zipFileComment, unparserContext);
|
|
47
47
|
};
|
|
48
|
-
|
|
48
|
+
const zipDataDescriptorUnparser = async function* ({ crc32, compressedSize, uncompressedSize, }, unparserContext) {
|
|
49
|
+
yield Buffer.from('504b0708', 'hex');
|
|
50
|
+
yield* uint32LEUnparser(crc32, unparserContext);
|
|
51
|
+
yield* uint32LEUnparser(compressedSize, unparserContext);
|
|
52
|
+
yield* uint32LEUnparser(uncompressedSize, unparserContext);
|
|
53
|
+
};
|
|
54
|
+
const zipLocalFileHeaderUnparser = async function* ({ versionNeededToExtract, generalPurposeBitFlag, compressionMethod, lastModifiedFile, crc32, compressedSize, uncompressedSize, filePath, extraField, }, unparserContext) {
|
|
55
|
+
yield Buffer.from('504b0304', 'hex');
|
|
56
|
+
yield* uint16LEUnparser(versionNeededToExtract, unparserContext);
|
|
57
|
+
yield* uint16LEUnparser(generalPurposeBitFlag, unparserContext);
|
|
58
|
+
yield* zipCompressionMethodUnparser(compressionMethod, unparserContext);
|
|
59
|
+
yield* dosDateTimeUnparser(lastModifiedFile, unparserContext);
|
|
60
|
+
yield* uint32LEUnparser(crc32, unparserContext);
|
|
61
|
+
yield* uint32LEUnparser(compressedSize, unparserContext);
|
|
62
|
+
yield* uint32LEUnparser(uncompressedSize, unparserContext);
|
|
63
|
+
const filePathBuffer = Buffer.from(filePath, 'utf8');
|
|
64
|
+
yield* uint16LEUnparser(filePathBuffer.length, unparserContext);
|
|
65
|
+
yield* uint16LEUnparser(extraField.length, unparserContext);
|
|
66
|
+
yield filePathBuffer;
|
|
67
|
+
yield extraField;
|
|
68
|
+
};
|
|
69
|
+
export const createZipUnparser = ({ dataDescriptor = false, } = {}) => async function* (zip, unparserContext) {
|
|
49
70
|
const compressedContentByZipFileEntry = new Map(zip.entries.flatMap(zipEntry => {
|
|
50
71
|
if (zipEntry.type !== 'file') {
|
|
51
72
|
return [];
|
|
@@ -66,37 +87,44 @@ export const zipUnparser = async function* (zip, unparserContext) {
|
|
|
66
87
|
const localHeaderPositionByZipEntry = new Map();
|
|
67
88
|
for (const zipEntry of zip.entries) {
|
|
68
89
|
localHeaderPositionByZipEntry.set(zipEntry, unparserContext.position);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
90
|
+
const filePath = filePathByZipEntry.get(zipEntry);
|
|
91
|
+
const zipLocalFileHeader = {
|
|
92
|
+
versionNeededToExtract: 20,
|
|
93
|
+
generalPurposeBitFlag: 0,
|
|
94
|
+
compressionMethod: 'store',
|
|
95
|
+
lastModifiedFile: zipEntry.date,
|
|
96
|
+
crc32: 0,
|
|
97
|
+
compressedSize: 0,
|
|
98
|
+
uncompressedSize: 0,
|
|
99
|
+
filePath,
|
|
100
|
+
extraField: Buffer.alloc(0),
|
|
101
|
+
};
|
|
102
|
+
let shouldWriteDataDescriptor = false;
|
|
79
103
|
if (zipEntry.type === 'file') {
|
|
80
104
|
const compressedContent = await compressedContentByZipFileEntry.get(zipEntry);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
105
|
+
zipLocalFileHeader.compressionMethod = zipEntry.compression;
|
|
106
|
+
zipLocalFileHeader.crc32 = 0; // TODO
|
|
107
|
+
zipLocalFileHeader.compressedSize = compressedContent.length;
|
|
108
|
+
zipLocalFileHeader.uncompressedSize = zipEntry.content.length;
|
|
109
|
+
if (dataDescriptor) {
|
|
110
|
+
shouldWriteDataDescriptor = true;
|
|
111
|
+
zipLocalFileHeader.generalPurposeBitFlag |= 0b0000_0000_0000_1000;
|
|
112
|
+
zipLocalFileHeader.crc32 = 0;
|
|
113
|
+
zipLocalFileHeader.compressedSize = 0;
|
|
114
|
+
zipLocalFileHeader.uncompressedSize = 0;
|
|
115
|
+
}
|
|
84
116
|
}
|
|
85
|
-
|
|
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;
|
|
117
|
+
yield* zipLocalFileHeaderUnparser(zipLocalFileHeader, unparserContext);
|
|
97
118
|
if (zipEntry.type === 'file') {
|
|
98
119
|
const compressedContent = await compressedContentByZipFileEntry.get(zipEntry);
|
|
99
120
|
yield compressedContent;
|
|
121
|
+
if (shouldWriteDataDescriptor) {
|
|
122
|
+
yield* zipDataDescriptorUnparser({
|
|
123
|
+
crc32: zipLocalFileHeader.crc32,
|
|
124
|
+
compressedSize: zipLocalFileHeader.compressedSize,
|
|
125
|
+
uncompressedSize: zipLocalFileHeader.uncompressedSize,
|
|
126
|
+
}, unparserContext);
|
|
127
|
+
}
|
|
100
128
|
}
|
|
101
129
|
}
|
|
102
130
|
const startOfCentralDirectoryPosition = unparserContext.position;
|
|
@@ -169,3 +197,4 @@ export const zipUnparser = async function* (zip, unparserContext) {
|
|
|
169
197
|
zipFileComment: zip.comment,
|
|
170
198
|
}, unparserContext);
|
|
171
199
|
};
|
|
200
|
+
export const zipUnparser = createZipUnparser();
|
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
import { testProp } from '@fast-check/ava';
|
|
1
|
+
import { fc, testProp } from '@fast-check/ava';
|
|
2
2
|
import { temporaryFile } from 'tempy';
|
|
3
3
|
import { execa } from 'execa';
|
|
4
4
|
import fsPromises from 'node:fs/promises';
|
|
5
5
|
import { runUnparser } from './unparser.js';
|
|
6
|
-
import {
|
|
6
|
+
import { createZipUnparser } from './zipUnparser.js';
|
|
7
7
|
import { uint8ArrayUnparserOutputCompanion } from './unparserOutputCompanion.js';
|
|
8
8
|
import { runParser } from './parser.js';
|
|
9
9
|
import { zipParser } from './zipParser.js';
|
|
10
10
|
import { uint8ArrayParserInputCompanion } from './parserInputCompanion.js';
|
|
11
11
|
import { arbitraryZip } from './arbitraryZip.js';
|
|
12
|
-
|
|
13
|
-
const hasExecutable = execa(executable).then(() => true, () => false);
|
|
14
|
-
if (!hasExecutable) {
|
|
15
|
-
console.warn(`Executable %o not found`, executable);
|
|
16
|
-
}
|
|
17
|
-
return hasExecutable;
|
|
18
|
-
}
|
|
12
|
+
import { hasExecutable } from './hasExecutable.js';
|
|
19
13
|
const hasZipinfoPromise = hasExecutable('zipinfo');
|
|
20
14
|
const has7zPromise = hasExecutable('7z');
|
|
21
15
|
async function zipinfo(zipFilePath) {
|
|
@@ -45,16 +39,22 @@ async function _7zList(zipFilePath) {
|
|
|
45
39
|
}
|
|
46
40
|
testProp('zip', [
|
|
47
41
|
arbitraryZip,
|
|
48
|
-
|
|
42
|
+
fc.record({
|
|
43
|
+
dataDescriptor: fc.boolean(),
|
|
44
|
+
}),
|
|
45
|
+
], async (t, zip, options) => {
|
|
46
|
+
const zipUnparser = createZipUnparser(options);
|
|
47
|
+
{
|
|
48
|
+
const zipStream = runUnparser(zipUnparser, zip, uint8ArrayUnparserOutputCompanion);
|
|
49
|
+
await fsPromises.writeFile('/tmp/zip.zip', zipStream);
|
|
50
|
+
}
|
|
49
51
|
const actualStream = runUnparser(zipUnparser, zip, uint8ArrayUnparserOutputCompanion);
|
|
50
52
|
const actual = await runParser(zipParser, actualStream, uint8ArrayParserInputCompanion);
|
|
51
|
-
|
|
52
|
-
if (!isDeepEqual) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
53
|
+
t.deepEqual(actual, zip);
|
|
55
54
|
const hasZipinfo = await hasZipinfoPromise;
|
|
56
55
|
const has7z = await has7zPromise;
|
|
57
56
|
if (!hasZipinfo && !has7z) {
|
|
57
|
+
t.pass('no zipinfo or 7z, skipping zipfile check');
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
60
|
const temporaryFilePath = temporaryFile({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futpib/parser",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"main": "build/index.js",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
@@ -24,20 +24,24 @@
|
|
|
24
24
|
"@ava/typescript": "^5.0.0",
|
|
25
25
|
"@fast-check/ava": "^2.0.1",
|
|
26
26
|
"@types/invariant": "^2.2.37",
|
|
27
|
-
"@types/node": "^22.10.
|
|
27
|
+
"@types/node": "^22.10.2",
|
|
28
28
|
"ava": "^6.2.0",
|
|
29
|
-
"bson": "^6.10.
|
|
30
|
-
"c8": "^10.1.
|
|
29
|
+
"bson": "^6.10.1",
|
|
30
|
+
"c8": "^10.1.3",
|
|
31
31
|
"coveralls": "^3.1.1",
|
|
32
|
+
"env-paths": "^3.0.0",
|
|
32
33
|
"eslint-config-xo-typescript-overrides": "^1.6.1",
|
|
33
34
|
"execa": "^9.5.2",
|
|
34
|
-
"fast-check": "^3.23.
|
|
35
|
+
"fast-check": "^3.23.2",
|
|
35
36
|
"invariant": "^2.2.4",
|
|
36
37
|
"jszip": "^3.10.1",
|
|
38
|
+
"leb128": "^0.0.5",
|
|
39
|
+
"mutf-8": "^1.1.4",
|
|
40
|
+
"p-memoize": "^7.1.1",
|
|
37
41
|
"tempy": "^3.1.0",
|
|
38
|
-
"type-fest": "^4.
|
|
42
|
+
"type-fest": "^4.30.1",
|
|
39
43
|
"typescript": "^5.7.2",
|
|
40
|
-
"xo": "^0.
|
|
44
|
+
"xo": "^0.60.0"
|
|
41
45
|
},
|
|
42
46
|
"xo": {
|
|
43
47
|
"extends": [
|
|
@@ -49,6 +53,18 @@
|
|
|
49
53
|
},
|
|
50
54
|
"packageManager": "yarn@4.5.3",
|
|
51
55
|
"dependencies": {
|
|
56
|
+
"fp-ts": "^2.16.9",
|
|
57
|
+
"mem": "^10.0.0",
|
|
58
|
+
"monocle-ts": "^2.3.13",
|
|
59
|
+
"newtype-ts": "^0.3.5",
|
|
52
60
|
"p-mutex": "^0.1.0"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"mutf-8": "^1.1.4"
|
|
64
|
+
},
|
|
65
|
+
"peerDependenciesMeta": {
|
|
66
|
+
"mutf-8": {
|
|
67
|
+
"optional": true
|
|
68
|
+
}
|
|
53
69
|
}
|
|
54
70
|
}
|
|
@@ -57,3 +57,43 @@ test('allSettledStream', async t => {
|
|
|
57
57
|
t.snapshot(results);
|
|
58
58
|
t.is(results.length, 8);
|
|
59
59
|
});
|
|
60
|
+
|
|
61
|
+
test('allSettledStream reader cancel', async t => {
|
|
62
|
+
const stream = allSettledStream<number, number>([
|
|
63
|
+
10,
|
|
64
|
+
20,
|
|
65
|
+
0,
|
|
66
|
+
40,
|
|
67
|
+
30,
|
|
68
|
+
].map((delay) => ({
|
|
69
|
+
promise: new Promise<number>((resolve) => {
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
resolve(delay);
|
|
72
|
+
}, delay);
|
|
73
|
+
}),
|
|
74
|
+
context: delay,
|
|
75
|
+
})));
|
|
76
|
+
|
|
77
|
+
const results: any[] = [];
|
|
78
|
+
|
|
79
|
+
for await (const value of stream) {
|
|
80
|
+
results.push(value);
|
|
81
|
+
|
|
82
|
+
if (value.context === 30) {
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await new Promise<void>(resolve => {
|
|
88
|
+
setTimeout(resolve, 50);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
stream.cancel();
|
|
92
|
+
|
|
93
|
+
for await (const value of stream) {
|
|
94
|
+
t.fail();
|
|
95
|
+
results.push(value);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
t.deepEqual(results.map(({ context }) => context), [ 0, 10, 20, 30 ]);
|
|
99
|
+
});
|
package/src/allSettledStream.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { promiseSettled } from "./promiseSettled.js";
|
|
2
|
+
|
|
1
3
|
type AllSettledStreamFulfilledResult<T, Context> = {
|
|
2
4
|
status: 'fulfilled';
|
|
3
5
|
value: T;
|
|
@@ -18,27 +20,57 @@ type AllSettledStreamTask<T, Context> = {
|
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
export function allSettledStream<T, Context>(tasks: Array<AllSettledStreamTask<T, Context>>): ReadableStream<AllSettedStreamResult<T, Context>> {
|
|
23
|
+
let cancelled = false;
|
|
24
|
+
|
|
21
25
|
return new ReadableStream({
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
start(controller) {
|
|
27
|
+
const startStack = (
|
|
28
|
+
(new Error('allSettledStream ReadableStream start stack holder').stack ?? '')
|
|
29
|
+
.split('\n')
|
|
30
|
+
.slice(1)
|
|
31
|
+
.join('\n')
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const allSettledStreamTaskAwaiter = async ({
|
|
35
|
+
promise,
|
|
36
|
+
context,
|
|
37
|
+
}: AllSettledStreamTask<T, Context>) => {
|
|
38
|
+
const promiseSettledResult = await promiseSettled(promise);
|
|
39
|
+
|
|
40
|
+
if (
|
|
41
|
+
promiseSettledResult.status === 'rejected'
|
|
42
|
+
&& promiseSettledResult.reason instanceof Error
|
|
43
|
+
&& promiseSettledResult.reason.stack
|
|
44
|
+
) {
|
|
45
|
+
promiseSettledResult.reason.stack += `\n${startStack}`;
|
|
46
|
+
}
|
|
27
47
|
|
|
28
|
-
|
|
48
|
+
settledCount++;
|
|
29
49
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
};
|
|
50
|
+
if (cancelled) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
34
53
|
|
|
35
|
-
|
|
54
|
+
const allSettedStreamResult: AllSettedStreamResult<T, Context> = {
|
|
55
|
+
...promiseSettledResult,
|
|
56
|
+
context,
|
|
57
|
+
};
|
|
36
58
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
59
|
+
controller.enqueue(allSettedStreamResult);
|
|
60
|
+
|
|
61
|
+
if (settledCount === tasks.length) {
|
|
62
|
+
controller.close();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
let settledCount = 0;
|
|
67
|
+
for (const task of tasks) {
|
|
68
|
+
allSettledStreamTaskAwaiter(task);
|
|
41
69
|
}
|
|
42
70
|
},
|
|
71
|
+
|
|
72
|
+
cancel() {
|
|
73
|
+
cancelled = true;
|
|
74
|
+
},
|
|
43
75
|
});
|
|
44
76
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type Zip } from './zip.js';
|
|
2
|
+
|
|
3
|
+
export type AndroidPackageSigningBlockPair = {
|
|
4
|
+
id: number;
|
|
5
|
+
value: Uint8Array;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type AndroidPackageSignatureV2Digest = {
|
|
9
|
+
signatureAlgorithmId: number;
|
|
10
|
+
digest: Uint8Array;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type AndroidPackageSignatureV2AdditionalAttribute = {
|
|
14
|
+
id: number;
|
|
15
|
+
value: Uint8Array;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type AndroidPackageSignatureV2SignedData = {
|
|
19
|
+
digests: AndroidPackageSignatureV2Digest[];
|
|
20
|
+
certificates: Uint8Array[];
|
|
21
|
+
additionalAttributes: AndroidPackageSignatureV2AdditionalAttribute[];
|
|
22
|
+
zeroPaddingLength?: number;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type AndroidPackageSignatureV2Signature = {
|
|
26
|
+
signatureAlgorithmId: number;
|
|
27
|
+
signature: Uint8Array;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type AndroidPackageSignatureV2Signer = {
|
|
31
|
+
signedData: AndroidPackageSignatureV2SignedData;
|
|
32
|
+
signatures: AndroidPackageSignatureV2Signature[];
|
|
33
|
+
publicKey: Uint8Array;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type AndroidPackageSignatureV2 = {
|
|
37
|
+
signers: AndroidPackageSignatureV2Signer[];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type AndroidPackageSigningBlock = {
|
|
41
|
+
pairs: AndroidPackageSigningBlockPair[];
|
|
42
|
+
signatureV2?: AndroidPackageSignatureV2;
|
|
43
|
+
zeroPaddingLength?: number;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type AndroidPackage = Zip & {
|
|
47
|
+
signingBlock?: AndroidPackageSigningBlock;
|
|
48
|
+
};
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import test from 'ava';
|
|
2
2
|
import { uint8ArrayParserInputCompanion } from './parserInputCompanion.js';
|
|
3
3
|
import { runParser } from './parser.js';
|
|
4
|
-
import {
|
|
4
|
+
import { androidPackageParser } from './androidPackageParser.js';
|
|
5
|
+
import { fetchCid } from './fetchCid.js';
|
|
5
6
|
|
|
6
|
-
for (const
|
|
7
|
+
for (const androidPackageCid of [
|
|
7
8
|
'bafkreicckcmzrdxwoc3w2in3tivpyxrdtcfpct4zwauq3igew3nkpvfapu',
|
|
8
9
|
'bafkreidqycbgrtp7ywhevsgtm464dqpamxsijpaqfcfz2k3ymc3wrzsb5m',
|
|
9
10
|
]) {
|
|
10
11
|
test(
|
|
11
|
-
'
|
|
12
|
+
'androidPackage ' + androidPackageCid,
|
|
12
13
|
async t => {
|
|
13
|
-
const
|
|
14
|
+
const androidPackageStream = await fetchCid(androidPackageCid);
|
|
14
15
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const actual = await runParser(apkParser, apkStream, uint8ArrayParserInputCompanion, {
|
|
16
|
+
const actual = await runParser(androidPackageParser, androidPackageStream, uint8ArrayParserInputCompanion, {
|
|
18
17
|
errorJoinMode: 'all',
|
|
19
18
|
});
|
|
20
19
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# Snapshot report for `src/
|
|
1
|
+
# Snapshot report for `src/androidPackageParser.test.ts`
|
|
2
2
|
|
|
3
|
-
The actual snapshot is saved in `
|
|
3
|
+
The actual snapshot is saved in `androidPackageParser.test.ts.snap`.
|
|
4
4
|
|
|
5
5
|
Generated by [AVA](https://avajs.dev).
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## androidPackage bafkreicckcmzrdxwoc3w2in3tivpyxrdtcfpct4zwauq3igew3nkpvfapu
|
|
8
8
|
|
|
9
9
|
> Snapshot 1
|
|
10
10
|
|
|
@@ -189,7 +189,7 @@ Generated by [AVA](https://avajs.dev).
|
|
|
189
189
|
},
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
##
|
|
192
|
+
## androidPackage bafkreidqycbgrtp7ywhevsgtm464dqpamxsijpaqfcfz2k3ymc3wrzsb5m
|
|
193
193
|
|
|
194
194
|
> Snapshot 1
|
|
195
195
|
|
|
Binary file
|