@futpib/parser 1.0.0

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 (228) hide show
  1. package/.editorconfig +12 -0
  2. package/.gitattributes +2 -0
  3. package/.github/workflows/main.yml +29 -0
  4. package/.yarn/releases/yarn-4.5.3.cjs +934 -0
  5. package/.yarnrc.yml +7 -0
  6. package/build/allSettledStream.d.ts +17 -0
  7. package/build/allSettledStream.js +21 -0
  8. package/build/allSettledStream.test.d.ts +1 -0
  9. package/build/allSettledStream.test.js +56 -0
  10. package/build/apk.d.ts +13 -0
  11. package/build/apk.js +1 -0
  12. package/build/apkParser.d.ts +3 -0
  13. package/build/apkParser.js +135 -0
  14. package/build/apkParser.test.d.ts +1 -0
  15. package/build/apkParser.test.js +22 -0
  16. package/build/arbitrarilySlicedAsyncInterable.d.ts +2 -0
  17. package/build/arbitrarilySlicedAsyncInterable.js +46 -0
  18. package/build/arbitrarilySlicedAsyncInterator.d.ts +2 -0
  19. package/build/arbitrarilySlicedAsyncInterator.js +17 -0
  20. package/build/arbitraryDosDate.d.ts +2 -0
  21. package/build/arbitraryDosDate.js +8 -0
  22. package/build/arbitraryDosDateTime.d.ts +2 -0
  23. package/build/arbitraryDosDateTime.js +9 -0
  24. package/build/arbitraryDosPermissions.d.ts +1 -0
  25. package/build/arbitraryDosPermissions.js +1 -0
  26. package/build/arbitraryFileSystemEntry.d.ts +14 -0
  27. package/build/arbitraryFileSystemEntry.js +12 -0
  28. package/build/arbitraryZip.d.ts +6 -0
  29. package/build/arbitraryZip.js +88 -0
  30. package/build/arbitraryZipEntry.d.ts +3 -0
  31. package/build/arbitraryZipEntry.js +26 -0
  32. package/build/arbitraryZipPermissions.d.ts +8 -0
  33. package/build/arbitraryZipPermissions.js +16 -0
  34. package/build/arbitraryZipStream.d.ts +5 -0
  35. package/build/arbitraryZipStream.js +50 -0
  36. package/build/arrayParser.d.ts +2 -0
  37. package/build/arrayParser.js +30 -0
  38. package/build/arrayParser.test.d.ts +1 -0
  39. package/build/arrayParser.test.js +9 -0
  40. package/build/bsonParser.d.ts +3 -0
  41. package/build/bsonParser.js +70 -0
  42. package/build/bsonParser.test.d.ts +1 -0
  43. package/build/bsonParser.test.js +24 -0
  44. package/build/createDisjunctionParser.d.ts +2 -0
  45. package/build/createDisjunctionParser.js +47 -0
  46. package/build/createExactParser.d.ts +2 -0
  47. package/build/createExactParser.js +12 -0
  48. package/build/createSequentialUnionParser.d.ts +2 -0
  49. package/build/createSequentialUnionParser.js +69 -0
  50. package/build/debugLogParser.d.ts +2 -0
  51. package/build/debugLogParser.js +23 -0
  52. package/build/disjunctionParser.d.ts +2 -0
  53. package/build/disjunctionParser.js +35 -0
  54. package/build/elementParser.d.ts +3 -0
  55. package/build/elementParser.js +1 -0
  56. package/build/endOfInputParser.d.ts +4 -0
  57. package/build/endOfInputParser.js +4 -0
  58. package/build/exactElementParser.d.ts +3 -0
  59. package/build/exactElementParser.js +6 -0
  60. package/build/exactSequenceParser.d.ts +2 -0
  61. package/build/exactSequenceParser.js +15 -0
  62. package/build/fixedLengthChunkParser.d.ts +2 -0
  63. package/build/fixedLengthChunkParser.js +12 -0
  64. package/build/fixedLengthParser.d.ts +2 -0
  65. package/build/fixedLengthParser.js +12 -0
  66. package/build/fixedLengthSequenceParser.d.ts +2 -0
  67. package/build/fixedLengthSequenceParser.js +16 -0
  68. package/build/index.d.ts +18 -0
  69. package/build/index.js +17 -0
  70. package/build/index.test.d.ts +1 -0
  71. package/build/index.test.js +2 -0
  72. package/build/inputChunkBuffer.d.ts +15 -0
  73. package/build/inputChunkBuffer.js +40 -0
  74. package/build/inputChunkBuffer.test.d.ts +1 -0
  75. package/build/inputChunkBuffer.test.js +34 -0
  76. package/build/inputCompanion.d.ts +18 -0
  77. package/build/inputCompanion.js +28 -0
  78. package/build/inputReader.d.ts +22 -0
  79. package/build/inputReader.js +105 -0
  80. package/build/inputReader.test.d.ts +1 -0
  81. package/build/inputReader.test.js +174 -0
  82. package/build/invariantDefined.d.ts +1 -0
  83. package/build/invariantDefined.js +5 -0
  84. package/build/invariantIdentity.d.ts +3 -0
  85. package/build/invariantIdentity.js +5 -0
  86. package/build/jsonParser.d.ts +3 -0
  87. package/build/jsonParser.js +133 -0
  88. package/build/jsonParser.test.d.ts +1 -0
  89. package/build/jsonParser.test.js +17 -0
  90. package/build/jsonParser2.d.ts +3 -0
  91. package/build/jsonParser2.js +52 -0
  92. package/build/jsonParser2.test.d.ts +1 -0
  93. package/build/jsonParser2.test.js +22 -0
  94. package/build/listParser.d.ts +2 -0
  95. package/build/listParser.js +34 -0
  96. package/build/negativeLookahead.d.ts +2 -0
  97. package/build/negativeLookahead.js +20 -0
  98. package/build/optionalParser.d.ts +2 -0
  99. package/build/optionalParser.js +23 -0
  100. package/build/parser.d.ts +10 -0
  101. package/build/parser.js +53 -0
  102. package/build/parser.test.d.ts +1 -0
  103. package/build/parser.test.js +126 -0
  104. package/build/parserAccessorParser.d.ts +2 -0
  105. package/build/parserAccessorParser.js +1 -0
  106. package/build/parserCompose.d.ts +3 -0
  107. package/build/parserCompose.js +7 -0
  108. package/build/parserContext.d.ts +48 -0
  109. package/build/parserContext.js +193 -0
  110. package/build/parserContext.test.d.ts +1 -0
  111. package/build/parserContext.test.js +115 -0
  112. package/build/parserCreatorCompose.d.ts +3 -0
  113. package/build/parserCreatorCompose.js +10 -0
  114. package/build/parserError.d.ts +43 -0
  115. package/build/parserError.js +61 -0
  116. package/build/parserImplementationInvariant.d.ts +2 -0
  117. package/build/parserImplementationInvariant.js +5 -0
  118. package/build/parserImplementationInvariantInvariant.d.ts +3 -0
  119. package/build/parserImplementationInvariantInvariant.js +15 -0
  120. package/build/parserInvariant.d.ts +4 -0
  121. package/build/parserInvariant.js +11 -0
  122. package/build/parserParsingInvariant.d.ts +1 -0
  123. package/build/parserParsingInvariant.js +1 -0
  124. package/build/promiseCompose.d.ts +1 -0
  125. package/build/promiseCompose.js +3 -0
  126. package/build/promiseFish.d.ts +1 -0
  127. package/build/promiseFish.js +3 -0
  128. package/build/sequence.d.ts +1 -0
  129. package/build/sequence.js +1 -0
  130. package/build/sequenceBuffer.d.ts +15 -0
  131. package/build/sequenceBuffer.js +40 -0
  132. package/build/sequenceBuffer.test.d.ts +1 -0
  133. package/build/sequenceBuffer.test.js +34 -0
  134. package/build/sequenceParser.d.ts +3 -0
  135. package/build/sequenceParser.js +10 -0
  136. package/build/skipParser.d.ts +2 -0
  137. package/build/skipParser.js +8 -0
  138. package/build/sliceBoundedParser.d.ts +2 -0
  139. package/build/sliceBoundedParser.js +24 -0
  140. package/build/sliceBoundedParser.test.d.ts +1 -0
  141. package/build/sliceBoundedParser.test.js +24 -0
  142. package/build/terminatedArrayParser.d.ts +2 -0
  143. package/build/terminatedArrayParser.js +27 -0
  144. package/build/terminatedSequenceParser.d.ts +2 -0
  145. package/build/terminatedSequenceParser.js +24 -0
  146. package/build/tupleParser.d.ts +11 -0
  147. package/build/tupleParser.js +17 -0
  148. package/build/unionParser.d.ts +2 -0
  149. package/build/unionParser.js +68 -0
  150. package/build/zip.d.ts +31 -0
  151. package/build/zip.js +1 -0
  152. package/build/zipEntry.d.ts +28 -0
  153. package/build/zipEntry.js +1 -0
  154. package/build/zipFile.d.ts +32 -0
  155. package/build/zipFile.js +1 -0
  156. package/build/zipFileEntry.d.ts +6 -0
  157. package/build/zipFileEntry.js +1 -0
  158. package/build/zipParser.d.ts +70 -0
  159. package/build/zipParser.js +255 -0
  160. package/build/zipParser.test.d.ts +1 -0
  161. package/build/zipParser.test.js +22 -0
  162. package/package.json +52 -0
  163. package/readme.md +17 -0
  164. package/renovate.json +6 -0
  165. package/src/allSettledStream.test.ts +59 -0
  166. package/src/allSettledStream.test.ts.md +52 -0
  167. package/src/allSettledStream.test.ts.snap +0 -0
  168. package/src/allSettledStream.ts +44 -0
  169. package/src/apk.ts +16 -0
  170. package/src/apkParser.test.ts +30 -0
  171. package/src/apkParser.test.ts.md +268 -0
  172. package/src/apkParser.test.ts.snap +0 -0
  173. package/src/apkParser.ts +327 -0
  174. package/src/arbitrarilySlicedAsyncInterable.ts +73 -0
  175. package/src/arbitrarilySlicedAsyncInterator.ts +25 -0
  176. package/src/arbitraryDosDateTime.ts +10 -0
  177. package/src/arbitraryFileSystemEntry.ts +36 -0
  178. package/src/arbitraryZip.ts +132 -0
  179. package/src/arbitraryZipPermissions.ts +25 -0
  180. package/src/arbitraryZipStream.ts +60 -0
  181. package/src/arrayParser.test.ts +11 -0
  182. package/src/arrayParser.ts +35 -0
  183. package/src/bsonParser.test.ts +37 -0
  184. package/src/bsonParser.ts +131 -0
  185. package/src/debugLogParser.ts +51 -0
  186. package/src/disjunctionParser.ts +55 -0
  187. package/src/elementParser.ts +4 -0
  188. package/src/endOfInputParser.ts +11 -0
  189. package/src/exactElementParser.ts +20 -0
  190. package/src/exactSequenceParser.ts +27 -0
  191. package/src/fixedLengthSequenceParser.ts +24 -0
  192. package/src/index.test.ts +3 -0
  193. package/src/index.ts +76 -0
  194. package/src/inputCompanion.ts +43 -0
  195. package/src/inputReader.test.ts +238 -0
  196. package/src/inputReader.ts +159 -0
  197. package/src/invariantDefined.ts +6 -0
  198. package/src/invariantIdentity.ts +8 -0
  199. package/src/jsonParser.test.ts +26 -0
  200. package/src/jsonParser.ts +225 -0
  201. package/src/listParser.ts +52 -0
  202. package/src/negativeLookahead.ts +26 -0
  203. package/src/optionalParser.ts +27 -0
  204. package/src/parser.test.ts +176 -0
  205. package/src/parser.test.ts.md +68 -0
  206. package/src/parser.test.ts.snap +0 -0
  207. package/src/parser.ts +103 -0
  208. package/src/parserAccessorParser.ts +3 -0
  209. package/src/parserContext.test.ts +154 -0
  210. package/src/parserContext.ts +316 -0
  211. package/src/parserCreatorCompose.ts +23 -0
  212. package/src/parserError.ts +85 -0
  213. package/src/parserImplementationInvariant.ts +6 -0
  214. package/src/parserInvariant.ts +26 -0
  215. package/src/promiseCompose.ts +7 -0
  216. package/src/sequence.ts +10 -0
  217. package/src/sequenceBuffer.test.ts +50 -0
  218. package/src/sequenceBuffer.ts +58 -0
  219. package/src/skipParser.ts +12 -0
  220. package/src/sliceBoundedParser.test.ts +28 -0
  221. package/src/sliceBoundedParser.ts +30 -0
  222. package/src/terminatedArrayParser.ts +40 -0
  223. package/src/tupleParser.ts +33 -0
  224. package/src/unionParser.ts +104 -0
  225. package/src/zip.ts +48 -0
  226. package/src/zipParser.test.ts +31 -0
  227. package/src/zipParser.ts +501 -0
  228. package/tsconfig.json +109 -0
package/.yarnrc.yml ADDED
@@ -0,0 +1,7 @@
1
+ compressionLevel: mixed
2
+
3
+ enableGlobalCache: false
4
+
5
+ nodeLinker: node-modules
6
+
7
+ yarnPath: .yarn/releases/yarn-4.5.3.cjs
@@ -0,0 +1,17 @@
1
+ type AllSettledStreamFulfilledResult<T, Context> = {
2
+ status: 'fulfilled';
3
+ value: T;
4
+ context: Context;
5
+ };
6
+ type AllSettledStreamRejectedResult<T, Context> = {
7
+ status: 'rejected';
8
+ reason: unknown;
9
+ context: Context;
10
+ };
11
+ type AllSettedStreamResult<T, Context> = AllSettledStreamFulfilledResult<T, Context> | AllSettledStreamRejectedResult<T, Context>;
12
+ type AllSettledStreamTask<T, Context> = {
13
+ promise: Promise<T>;
14
+ context: Context;
15
+ };
16
+ export declare function allSettledStream<T, Context>(tasks: Array<AllSettledStreamTask<T, Context>>): ReadableStream<AllSettedStreamResult<T, Context>>;
17
+ export {};
@@ -0,0 +1,21 @@
1
+ export function allSettledStream(tasks) {
2
+ return new ReadableStream({
3
+ async start(controller) {
4
+ let settledCount = 0;
5
+ for (const { promise, context } of tasks) {
6
+ (async () => {
7
+ const [promiseSettledResult] = await Promise.allSettled([promise]);
8
+ settledCount++;
9
+ const allSettedStreamResult = {
10
+ ...promiseSettledResult,
11
+ context,
12
+ };
13
+ controller.enqueue(allSettedStreamResult);
14
+ if (settledCount === tasks.length) {
15
+ controller.close();
16
+ }
17
+ })();
18
+ }
19
+ },
20
+ });
21
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,56 @@
1
+ import test from 'ava';
2
+ import { allSettledStream } from './allSettledStream.js';
3
+ test('allSettledStream', async (t) => {
4
+ const stream = allSettledStream([
5
+ {
6
+ delay: 10,
7
+ context: 1,
8
+ },
9
+ {
10
+ delay: 10,
11
+ context: 'a',
12
+ },
13
+ {
14
+ delay: 20,
15
+ context: 2,
16
+ },
17
+ {
18
+ delay: 20,
19
+ context: 'b',
20
+ },
21
+ {
22
+ delay: 0,
23
+ context: 3,
24
+ },
25
+ {
26
+ delay: 0,
27
+ context: 'c',
28
+ },
29
+ {
30
+ delay: 40,
31
+ context: 4,
32
+ },
33
+ {
34
+ delay: 40,
35
+ context: 'd',
36
+ },
37
+ ].map(({ delay, context }) => ({
38
+ promise: new Promise((resolve, reject) => {
39
+ setTimeout(() => {
40
+ if (typeof context === 'number') {
41
+ resolve(context);
42
+ }
43
+ else {
44
+ reject(context);
45
+ }
46
+ }, delay);
47
+ }),
48
+ context,
49
+ })));
50
+ const results = [];
51
+ for await (const value of stream) {
52
+ results.push(value);
53
+ }
54
+ t.snapshot(results);
55
+ t.is(results.length, 8);
56
+ });
package/build/apk.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { Zip } from './zip.js';
2
+ export type ApkSigningBlockPair = {
3
+ id: number;
4
+ value: Uint8Array;
5
+ };
6
+ export type ApkSigningBlock = {
7
+ zeroPaddingLength?: number;
8
+ signatureV2?: unknown;
9
+ pairs: ApkSigningBlockPair[];
10
+ };
11
+ export type Apk = Zip & {
12
+ signingBlock?: ApkSigningBlock;
13
+ };
package/build/apk.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import { Parser } from "./parser.js";
2
+ import { Apk } from "./apk.js";
3
+ export declare const apkParser: Parser<Apk, Uint8Array>;
@@ -0,0 +1,135 @@
1
+ import { createArrayParser } from "./arrayParser.js";
2
+ import { createOptionalParser } from "./optionalParser.js";
3
+ import { setParserName } from "./parser.js";
4
+ import { promiseCompose } from "./promiseCompose.js";
5
+ import { createTupleParser } from "./tupleParser.js";
6
+ import { zip64EndOfCentralDirectoryLocatorParser, zip64EndOfCentralDirectoryRecordParser, zipArchiveDecryptionHeaderParser, zipArchiveExtraDataRecordParser, zipCentralDirectoryHeaderParser, zipEndOfCentralDirectoryRecordParser, zipFromZipSegments, zipLocalFileParser } from "./zipParser.js";
7
+ import { createFixedLengthSequenceParser } from "./fixedLengthSequenceParser.js";
8
+ import { parserCreatorCompose } from "./parserCreatorCompose.js";
9
+ import { createExactSequenceParser } from "./exactSequenceParser.js";
10
+ import { createSliceBoundedParser } from "./sliceBoundedParser.js";
11
+ import { createDisjunctionParser } from "./disjunctionParser.js";
12
+ import invariant from "invariant";
13
+ import { createExactElementParser } from "./exactElementParser.js";
14
+ // https://source.android.com/docs/security/features/apksigning/v2#apk-signing-block
15
+ const uint32LEParser = promiseCompose(createFixedLengthSequenceParser(4), array => Buffer.from(array).readUInt32LE());
16
+ setParserName(uint32LEParser, 'uint32LEParser');
17
+ const uint64LEParser = promiseCompose(createFixedLengthSequenceParser(8), array => Buffer.from(array).readBigUInt64LE());
18
+ setParserName(uint64LEParser, 'uint64LEParser');
19
+ const createUint32LengthPrefixedParser = (createInnerParser) => parserCreatorCompose(() => uint32LEParser, length => createInnerParser(length))();
20
+ const createUint64LengthPrefixedParser = (createInnerParser) => parserCreatorCompose(() => uint64LEParser, length => createInnerParser(length))();
21
+ const createUint32LengthPrefixedSliceBoundedParser = (innerParser) => createUint32LengthPrefixedParser(length => createSliceBoundedParser(innerParser, length));
22
+ const createUint32LengthPrefixedSliceBoundedArrayParser = (innerParser) => createUint32LengthPrefixedSliceBoundedParser(createArrayParser(innerParser));
23
+ const createApkSigningBlockZeroPaddingPairInnerParser = (length) => promiseCompose(createTupleParser([
24
+ createExactSequenceParser(Buffer.from('77657242', 'hex')),
25
+ createFixedLengthSequenceParser(length - 4),
26
+ ]), ([_magic, zeroPadding]) => ({ type: 'zeroPadding', length: zeroPadding.length }));
27
+ const createApkSigningBlockSignatureV2PairInnerParser = (length) => {
28
+ const apkSigningBlockSignatureV2PairInnerParser = promiseCompose(createTupleParser([
29
+ createExactSequenceParser(Buffer.from('1a870971', 'hex')),
30
+ apkSignatureSchemeV2SignersParser,
31
+ ]), ([_magic, signers = []]) => ({ type: 'signatureV2', signers }));
32
+ return setParserName(apkSigningBlockSignatureV2PairInnerParser, 'apkSigningBlockSignatureV2PairInnerParser');
33
+ };
34
+ const createApkSigningBlockGenericPairInnerParser = (length) => promiseCompose(createTupleParser([
35
+ uint32LEParser,
36
+ createFixedLengthSequenceParser(length - 4),
37
+ ]), ([id, value]) => ({ type: 'generic', pair: { id, value } }));
38
+ const createApkSigningBlockPairInnerParser = (length) => createDisjunctionParser([
39
+ createApkSigningBlockZeroPaddingPairInnerParser(length),
40
+ createApkSigningBlockSignatureV2PairInnerParser(length),
41
+ createApkSigningBlockGenericPairInnerParser(length),
42
+ ]);
43
+ const apkSigningBlockPairParser = createUint64LengthPrefixedParser(length => createApkSigningBlockPairInnerParser(Number(length)));
44
+ setParserName(apkSigningBlockPairParser, 'apkSigningBlockPairParser');
45
+ const apkSigningBlockPairsParser = createArrayParser(apkSigningBlockPairParser);
46
+ const apkSigningBlockParser = createUint64LengthPrefixedParser(sizeOfBlock => promiseCompose(createTupleParser([
47
+ apkSigningBlockPairsParser,
48
+ uint64LEParser,
49
+ createExactSequenceParser(Buffer.from('APK Sig Block 42', 'utf8')),
50
+ ]), async ([pairs, sizeOfBlockRepeated, _magic]) => {
51
+ invariant(sizeOfBlock === sizeOfBlockRepeated, 'Size of block mismatch: %s !== %s.', sizeOfBlock, sizeOfBlockRepeated);
52
+ const zeroPaddingPair = pairs.find(pair => pair.type === 'zeroPadding');
53
+ const signatureV2Pair = pairs.find(pair => pair.type === 'signatureV2');
54
+ const genericPairs = (pairs
55
+ .filter(pair => (pair !== zeroPaddingPair
56
+ && pair !== signatureV2Pair))
57
+ .map(pair => {
58
+ invariant(pair.type === 'generic', 'Expected generic pair, got %s.', pair.type);
59
+ return pair.pair;
60
+ }));
61
+ return {
62
+ zeroPaddingLength: zeroPaddingPair?.length,
63
+ signatureV2: signatureV2Pair?.signers,
64
+ pairs: genericPairs,
65
+ };
66
+ }));
67
+ const apkSignatureSchemeV2DigestParser = createUint32LengthPrefixedParser(pairLength => promiseCompose(createTupleParser([
68
+ uint32LEParser,
69
+ createUint32LengthPrefixedParser(digestLength => createFixedLengthSequenceParser(digestLength)),
70
+ ]), ([signatureAlgorithmId, digest]) => ({ signatureAlgorithmId, digest })));
71
+ const apkSignatureSchemeV2DigestsParser = createUint32LengthPrefixedSliceBoundedArrayParser(apkSignatureSchemeV2DigestParser);
72
+ const apkSignatureSchemeV2CertificateParser = createUint32LengthPrefixedParser(certificateLength => createFixedLengthSequenceParser(certificateLength));
73
+ const apkSignatureSchemeV2CertificatesParser = createUint32LengthPrefixedSliceBoundedArrayParser(apkSignatureSchemeV2CertificateParser);
74
+ const apkSignatureSchemeV2AdditionalAttributeParser = createUint32LengthPrefixedParser(pairLength => promiseCompose(createTupleParser([
75
+ uint32LEParser,
76
+ createFixedLengthSequenceParser(pairLength - 4),
77
+ ]), ([id, value]) => ({ id, value })));
78
+ const apkSignatureSchemeV2AdditionalAttributesParser = createUint32LengthPrefixedSliceBoundedArrayParser(apkSignatureSchemeV2AdditionalAttributeParser);
79
+ const apkSignatureSchemeV2SignedDataParser = createUint32LengthPrefixedSliceBoundedParser(promiseCompose(createTupleParser([
80
+ apkSignatureSchemeV2DigestsParser,
81
+ apkSignatureSchemeV2CertificatesParser,
82
+ createOptionalParser(apkSignatureSchemeV2AdditionalAttributesParser),
83
+ createArrayParser(createExactElementParser(0)),
84
+ ]), ([digests = [], certificates = [], additionalAttributes = [],]) => ({
85
+ digests,
86
+ certificates,
87
+ additionalAttributes,
88
+ })));
89
+ setParserName(apkSignatureSchemeV2SignedDataParser, 'apkSignatureSchemeV2SignedDataParser');
90
+ const apkSignatureSchemeV2SignatureParser = createUint32LengthPrefixedParser(signatureLength => promiseCompose(createTupleParser([
91
+ uint32LEParser,
92
+ createUint32LengthPrefixedParser(signatureLength => createFixedLengthSequenceParser(signatureLength)),
93
+ ]), ([signatureAlgorithmId, signature,]) => ({
94
+ signatureAlgorithmId,
95
+ signature,
96
+ })));
97
+ const apkSignatureSchemeV2SignaturesParser = createUint32LengthPrefixedSliceBoundedArrayParser(apkSignatureSchemeV2SignatureParser);
98
+ setParserName(apkSignatureSchemeV2SignaturesParser, 'apkSignatureSchemeV2SignaturesParser');
99
+ const apkSignatureSchemeV2PublicKeyParser = createUint32LengthPrefixedParser(publicKeyLength => createFixedLengthSequenceParser(publicKeyLength));
100
+ setParserName(apkSignatureSchemeV2PublicKeyParser, 'apkSignatureSchemeV2PublicKeyParser');
101
+ const apkSignatureSchemeV2SignerParser = createUint32LengthPrefixedSliceBoundedParser(promiseCompose(createTupleParser([
102
+ apkSignatureSchemeV2SignedDataParser,
103
+ apkSignatureSchemeV2SignaturesParser,
104
+ apkSignatureSchemeV2PublicKeyParser,
105
+ ]), ([signedData, signatures = [], publicKey]) => ({
106
+ signedData,
107
+ signatures,
108
+ publicKey,
109
+ })));
110
+ setParserName(apkSignatureSchemeV2SignerParser, 'apkSignatureSchemeV2SignerParser');
111
+ const apkSignatureSchemeV2SignersParser = createUint32LengthPrefixedSliceBoundedArrayParser(apkSignatureSchemeV2SignerParser);
112
+ setParserName(apkSignatureSchemeV2SignersParser, 'apkSignatureSchemeV2SignersParser');
113
+ const apkParser_ = createTupleParser([
114
+ createArrayParser(zipLocalFileParser),
115
+ createOptionalParser(zipArchiveDecryptionHeaderParser),
116
+ createOptionalParser(zipArchiveExtraDataRecordParser),
117
+ createOptionalParser(apkSigningBlockParser),
118
+ createArrayParser(zipCentralDirectoryHeaderParser),
119
+ createOptionalParser(zip64EndOfCentralDirectoryRecordParser),
120
+ createOptionalParser(zip64EndOfCentralDirectoryLocatorParser),
121
+ zipEndOfCentralDirectoryRecordParser,
122
+ ]);
123
+ setParserName(apkParser_, 'apkParser_');
124
+ export const apkParser = promiseCompose(apkParser_, async ([zipLocalFiles, zipArchiveDecryptionHeader, zipArchiveExtraDataRecord, apkSigningBlock, zipCentralDirectoryHeaders, zip64EndOfCentralDirectoryRecord, zip64EndOfCentralDirectoryLocator, zipEndOfCentralDirectoryRecord,]) => {
125
+ const zip = await zipFromZipSegments({
126
+ zipLocalFiles,
127
+ zipCentralDirectoryHeaders,
128
+ zipEndOfCentralDirectoryRecord,
129
+ });
130
+ return {
131
+ ...zip,
132
+ signingBlock: apkSigningBlock,
133
+ };
134
+ });
135
+ setParserName(apkParser, 'apkParser');
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ import test from 'ava';
2
+ import { uint8ArrayInputCompanion } from './inputCompanion.js';
3
+ import { runParser } from './parser.js';
4
+ import { apkParser } from './apkParser.js';
5
+ for (const apkCid of [
6
+ 'bafkreicckcmzrdxwoc3w2in3tivpyxrdtcfpct4zwauq3igew3nkpvfapu',
7
+ 'bafkreidqycbgrtp7ywhevsgtm464dqpamxsijpaqfcfz2k3ymc3wrzsb5m',
8
+ ]) {
9
+ test('apk ' + apkCid, async (t) => {
10
+ const apkResponse = await fetch('https://ipfs.io/ipfs/' + apkCid);
11
+ const apkStream = apkResponse.body;
12
+ const actual = await runParser(apkParser, apkStream, uint8ArrayInputCompanion, {
13
+ errorJoinMode: 'all',
14
+ });
15
+ for (const entry of actual.entries) {
16
+ if (entry.type === 'file') {
17
+ entry.content = new Uint8Array([entry.content.length]);
18
+ }
19
+ }
20
+ t.snapshot(actual);
21
+ });
22
+ }
@@ -0,0 +1,2 @@
1
+ import { type Arbitrary } from 'fast-check';
2
+ export declare function arbitrarilySlicedAsyncIterable<Sliceable extends string | Uint8Array>(arbitrarySliceable: Arbitrary<Sliceable>): Arbitrary<[Sliceable, AsyncIterable<Sliceable>]>;
@@ -0,0 +1,46 @@
1
+ import { inspect } from 'node:util';
2
+ import * as fc from 'fast-check';
3
+ import invariant from 'invariant';
4
+ function concat(slices) {
5
+ const firstSlice = slices.at(0);
6
+ if (firstSlice instanceof Uint8Array) {
7
+ return Buffer.concat(slices);
8
+ }
9
+ return slices.join('');
10
+ }
11
+ function equals(a, b) {
12
+ if (a instanceof Uint8Array) {
13
+ return Buffer.compare(a, b) === 0;
14
+ }
15
+ return a === b;
16
+ }
17
+ export function arbitrarilySlicedAsyncIterable(arbitrarySliceable) {
18
+ return (fc.tuple(arbitrarySliceable, fc.array(fc.nat(), { minLength: 1 }))
19
+ .map(([sliceable, sliceSizes]) => {
20
+ let start = 0;
21
+ const slices = sliceSizes.map(sliceSize => {
22
+ const slice = sliceable.slice(start, start + sliceSize);
23
+ start += sliceSize;
24
+ return slice;
25
+ });
26
+ const lastSlice = sliceable.slice(start);
27
+ if (lastSlice.length > 0) {
28
+ slices.push(lastSlice);
29
+ }
30
+ const concatenated = concat(slices);
31
+ invariant(equals(concatenated, sliceable), [
32
+ 'Slices do not concatenate to the original sliceable.',
33
+ 'Original sliceable: %s.',
34
+ 'Concatenated slices: %s.',
35
+ 'Slice sizes: %s.',
36
+ 'Slices: %s.',
37
+ ].join('\n'), inspect(sliceable), inspect(concatenated), inspect(sliceSizes), inspect(slices));
38
+ const asyncIterable = {
39
+ async *[Symbol.asyncIterator]() {
40
+ yield* slices;
41
+ },
42
+ [Symbol.toStringTag]: 'ArbitrarilySlicedAsyncIterable ' + slices.length + ' ' + JSON.stringify(slices),
43
+ };
44
+ return [sliceable, asyncIterable];
45
+ }));
46
+ }
@@ -0,0 +1,2 @@
1
+ import { type Arbitrary } from 'fast-check';
2
+ export declare function arbitrarilySlicedAsyncIterator<Sliceable extends string | Uint8Array>(arbitrarySliceable: Arbitrary<Sliceable>): Arbitrary<[Sliceable, AsyncIterator<Sliceable>]>;
@@ -0,0 +1,17 @@
1
+ import { arbitrarilySlicedAsyncIterable } from './arbitrarilySlicedAsyncInterable.js';
2
+ export function arbitrarilySlicedAsyncIterator(arbitrarySliceable) {
3
+ return (arbitrarilySlicedAsyncIterable(arbitrarySliceable)
4
+ .map(([sliceable, asyncIterable]) => {
5
+ const asyncIterator_ = asyncIterable[Symbol.asyncIterator]();
6
+ const asyncIterator = {
7
+ next: asyncIterator_.next.bind(asyncIterator_),
8
+ return: asyncIterator_.return?.bind(asyncIterator_),
9
+ throw: asyncIterator_.throw?.bind(asyncIterator_),
10
+ [Symbol.toStringTag]: 'ArbitrarilySlicedAsyncIterator ' + asyncIterable[Symbol.toStringTag],
11
+ };
12
+ return [
13
+ sliceable,
14
+ asyncIterator,
15
+ ];
16
+ }));
17
+ }
@@ -0,0 +1,2 @@
1
+ import * as fc from 'fast-check';
2
+ export declare const arbitraryDosDateTime: fc.Arbitrary<Date>;
@@ -0,0 +1,8 @@
1
+ import * as fc from 'fast-check';
2
+ export const arbitraryDosDateTime = fc.date({
3
+ min: new Date(Date.UTC(1980, 0, 1)),
4
+ max: new Date(Date.UTC(2099, 11, 31)),
5
+ }).map(date => {
6
+ date.setMilliseconds(0);
7
+ return date;
8
+ });
@@ -0,0 +1,2 @@
1
+ import * as fc from 'fast-check';
2
+ export declare const arbitraryDosDateTime: fc.Arbitrary<Date>;
@@ -0,0 +1,9 @@
1
+ import * as fc from 'fast-check';
2
+ export const arbitraryDosDateTime = fc.date({
3
+ min: new Date(Date.UTC(1980, 0, 1)),
4
+ max: new Date(Date.UTC(2099, 11, 31)),
5
+ }).map(date => {
6
+ date.setSeconds(0);
7
+ date.setMilliseconds(0);
8
+ return date;
9
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import * as fc from 'fast-check';
2
+ type FileSystemDirectory = {
3
+ type: 'directory';
4
+ name: string;
5
+ entries: FileSystemEntry[];
6
+ };
7
+ type FileSystemFile = {
8
+ type: 'file';
9
+ name: string;
10
+ content: Uint8Array;
11
+ };
12
+ export type FileSystemEntry = FileSystemFile | FileSystemDirectory;
13
+ export declare const arbitraryFileSystemEntry: fc.Arbitrary<FileSystemFile | FileSystemDirectory>;
14
+ export {};
@@ -0,0 +1,12 @@
1
+ import * as fc from 'fast-check';
2
+ export const arbitraryFileSystemEntry = fc.letrec((tie) => ({
3
+ entry: fc.oneof(fc.record({
4
+ type: fc.constant('file'),
5
+ name: fc.string(),
6
+ content: fc.uint8Array(),
7
+ }), fc.record({
8
+ type: fc.constant('directory'),
9
+ name: fc.string(),
10
+ entries: fc.array(tie('entry')),
11
+ })),
12
+ })).entry;
@@ -0,0 +1,6 @@
1
+ import * as fc from 'fast-check';
2
+ import { ZipEntry } from './zip.js';
3
+ export declare const arbitraryZip: fc.Arbitrary<{
4
+ comment: string;
5
+ entries: ZipEntry[];
6
+ }>;
@@ -0,0 +1,88 @@
1
+ import * as fc from 'fast-check';
2
+ import { arbitraryDosDateTime } from './arbitraryDosDateTime.js';
3
+ import { createArbitraryZipPermissions } from './arbitraryZipPermissions.js';
4
+ import invariant from 'invariant';
5
+ const DOS_DIRECTORY_FLAG = 0b00010000;
6
+ const arbitraryPath = fc.string({ minLength: 1 }).filter(path => {
7
+ const pathSegments = path.split('/');
8
+ const pathHasEmptySegments = pathSegments.some(segment => segment.length === 0);
9
+ if (pathHasEmptySegments) {
10
+ return false;
11
+ }
12
+ return true;
13
+ });
14
+ const createArbitraryZipEntry = (platform) => fc.oneof(fc.record({
15
+ type: fc.constant('file'),
16
+ path: arbitraryPath,
17
+ date: arbitraryDosDateTime,
18
+ comment: fc.string(),
19
+ permissions: createArbitraryZipPermissions(platform),
20
+ compression: fc.oneof(fc.constant('store'), fc.constant('deflate')),
21
+ content: fc.uint8Array(),
22
+ }).filter(zipFileEntry => {
23
+ if (zipFileEntry.content.length === 0
24
+ && zipFileEntry.compression === 'deflate') {
25
+ return false;
26
+ }
27
+ if (zipFileEntry.permissions.type === 'dos'
28
+ && zipFileEntry.permissions.dosPermissions & DOS_DIRECTORY_FLAG) {
29
+ return false;
30
+ }
31
+ return true;
32
+ }), fc.record({
33
+ type: fc.constant('directory'),
34
+ path: arbitraryPath,
35
+ date: arbitraryDosDateTime,
36
+ comment: fc.string(),
37
+ permissions: createArbitraryZipPermissions(platform),
38
+ }).filter(zipDirectoryEntry => {
39
+ if (zipDirectoryEntry.permissions.type === 'dos'
40
+ && !(zipDirectoryEntry.permissions.dosPermissions & DOS_DIRECTORY_FLAG)) {
41
+ return false;
42
+ }
43
+ return true;
44
+ }));
45
+ const createArbitraryZipEntries = (platform) => (fc
46
+ .array(createArbitraryZipEntry(platform))
47
+ .map(entries => {
48
+ const seenPaths = new Set();
49
+ const seenDirectoryPaths = new Set();
50
+ const normalizedEntries = [];
51
+ for (const entry of entries) {
52
+ if (seenPaths.has(entry.path)) {
53
+ continue;
54
+ }
55
+ seenPaths.add(entry.path);
56
+ if (entry.type === 'directory') {
57
+ seenDirectoryPaths.add(entry.path);
58
+ continue;
59
+ }
60
+ invariant(entry.type === 'file', 'Unexpected entry type %s.', entry.type);
61
+ const directories = entry.path.split('/').slice(0, -1);
62
+ for (const depth of Array.from({ length: directories.length }).map((_, index) => index)) {
63
+ const directoryPath = directories.slice(0, depth + 1).join('/');
64
+ if (directoryPath.length === 0) {
65
+ continue;
66
+ }
67
+ if (!seenDirectoryPaths.has(directoryPath)) {
68
+ normalizedEntries.push({
69
+ type: 'directory',
70
+ path: directoryPath,
71
+ date: entry.date,
72
+ comment: '',
73
+ permissions: entry.permissions.type === 'dos' ? {
74
+ type: 'dos',
75
+ dosPermissions: entry.permissions.dosPermissions | DOS_DIRECTORY_FLAG,
76
+ } : entry.permissions,
77
+ });
78
+ seenDirectoryPaths.add(directoryPath);
79
+ }
80
+ }
81
+ normalizedEntries.push(entry);
82
+ }
83
+ return normalizedEntries;
84
+ }));
85
+ export const arbitraryZip = fc.oneof(fc.constant('unix'), fc.constant('dos')).chain(platform => fc.record({
86
+ comment: fc.string(),
87
+ entries: createArbitraryZipEntries(platform),
88
+ }));
@@ -0,0 +1,3 @@
1
+ import * as fc from 'fast-check';
2
+ import { ZipDirectoryEntry, ZipFileEntry } from './zip.js';
3
+ export declare const arbitraryZipEntry: fc.Arbitrary<ZipFileEntry | ZipDirectoryEntry>;
@@ -0,0 +1,26 @@
1
+ import * as fc from 'fast-check';
2
+ export const arbitraryZipEntry = fc.letrec((tie) => ({
3
+ entry: fc.oneof(fc.record({
4
+ type: fc.constant('file'),
5
+ name: fc.string({ minLength: 1 }),
6
+ date: fc.date(),
7
+ comment: fc.uint8Array(),
8
+ unixPermissions: fc.integer(),
9
+ dosPermissions: fc.integer(),
10
+ compression: fc.oneof(fc.record({
11
+ type: fc.constant('store'),
12
+ }), fc.record({
13
+ type: fc.constant('deflate'),
14
+ level: fc.nat({ max: 9 }),
15
+ })),
16
+ content: fc.uint8Array(),
17
+ }), fc.record({
18
+ type: fc.constant('directory'),
19
+ name: fc.string({ minLength: 1 }),
20
+ date: fc.date(),
21
+ comment: fc.uint8Array(),
22
+ unixPermissions: fc.integer(),
23
+ dosPermissions: fc.integer(),
24
+ entries: fc.array(tie('entry')),
25
+ })),
26
+ })).entry;
@@ -0,0 +1,8 @@
1
+ import * as fc from 'fast-check';
2
+ export declare const createArbitraryZipPermissions: (type: "unix" | "dos") => fc.Arbitrary<{
3
+ type: "unix";
4
+ unixPermissions: number;
5
+ }> | fc.Arbitrary<{
6
+ type: "dos";
7
+ dosPermissions: number;
8
+ }>;
@@ -0,0 +1,16 @@
1
+ import * as fc from 'fast-check';
2
+ const arbitraryZipUnixPermissions = fc.record({
3
+ type: fc.constant('unix'),
4
+ unixPermissions: (fc.nat({ max: 0b0000000111111111 })
5
+ .filter((unixPermissions) => unixPermissions > 0)),
6
+ });
7
+ const arbitraryZipDosPermissions = fc.record({
8
+ type: fc.constant('dos'),
9
+ dosPermissions: (fc.nat({ max: 0b00111111 })),
10
+ });
11
+ export const createArbitraryZipPermissions = (type) => {
12
+ if (type === 'unix') {
13
+ return arbitraryZipUnixPermissions;
14
+ }
15
+ return arbitraryZipDosPermissions;
16
+ };
@@ -0,0 +1,5 @@
1
+ import { ZipEntry } from './zip.js';
2
+ export declare const arbitraryZipStream: import("fast-check").Arbitrary<readonly [{
3
+ comment: string;
4
+ entries: ZipEntry[];
5
+ }, ReadableStream<Uint8Array<ArrayBufferLike>>]>;
@@ -0,0 +1,50 @@
1
+ import JSZip from 'jszip';
2
+ import { arbitraryZip } from './arbitraryZip.js';
3
+ function addZipEntryToZip(zip, zipEntry) {
4
+ const options = {
5
+ comment: zipEntry.comment,
6
+ date: zipEntry.date,
7
+ unixPermissions: zipEntry.permissions.type === 'unix' ? zipEntry.permissions.unixPermissions : undefined,
8
+ dosPermissions: zipEntry.permissions.type === 'dos' ? zipEntry.permissions.dosPermissions : undefined,
9
+ };
10
+ if (zipEntry.type === 'file') {
11
+ zip.file(zipEntry.path, zipEntry.content, {
12
+ compression: zipEntry.compression.toUpperCase(),
13
+ compressionOptions: undefined,
14
+ binary: true,
15
+ ...options,
16
+ });
17
+ }
18
+ else {
19
+ zip.file(zipEntry.path, null, {
20
+ dir: true,
21
+ ...options,
22
+ });
23
+ }
24
+ }
25
+ export const arbitraryZipStream = arbitraryZip.map(zip => {
26
+ const jsZip = new JSZip();
27
+ for (const zipEntry of zip.entries) {
28
+ addZipEntryToZip(jsZip, zipEntry);
29
+ }
30
+ const zipInternalStream = jsZip.generateInternalStream({
31
+ type: 'uint8array',
32
+ comment: zip.comment,
33
+ platform: zip.entries.at(0)?.permissions.type.toUpperCase(),
34
+ });
35
+ const zipStream = new ReadableStream({
36
+ start(controller) {
37
+ zipInternalStream.on('data', (chunk) => {
38
+ controller.enqueue(chunk);
39
+ });
40
+ zipInternalStream.on('end', () => {
41
+ controller.close();
42
+ });
43
+ zipInternalStream.on('error', (error) => {
44
+ controller.error(error);
45
+ });
46
+ zipInternalStream.resume();
47
+ },
48
+ });
49
+ return [zip, zipStream];
50
+ });
@@ -0,0 +1,2 @@
1
+ import { Parser } from "./parser.js";
2
+ export declare const createArrayParser: <ElementOutput, Sequence>(elementParser: Parser<ElementOutput, Sequence>) => Parser<ElementOutput[], Sequence>;