@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
@@ -0,0 +1,70 @@
1
+ import { Parser } from "./parser.js";
2
+ import { Zip, ZipCompression, ZipEntry } from "./zip.js";
3
+ type ZipLocalFileHeader = {
4
+ versionNeededToExtract: number;
5
+ generalPurposeBitFlag: number;
6
+ compressionMethod: ZipCompression;
7
+ lastModFile: Date;
8
+ crc32: number;
9
+ compressedSize: number;
10
+ uncompressedSize: number;
11
+ fileName: string;
12
+ extraField: Uint8Array;
13
+ };
14
+ type ZipLocalFile = {
15
+ zipLocalFileHeader: ZipLocalFileHeader;
16
+ zipEncryptionHeader: unknown;
17
+ compressedData: Uint8Array;
18
+ zipDataDescriptor: unknown;
19
+ };
20
+ export declare const zipLocalFileParser: Parser<ZipLocalFile, Uint8Array>;
21
+ export declare const zipArchiveDecryptionHeaderParser: Parser<unknown, Uint8Array>;
22
+ export declare const zipArchiveExtraDataRecordParser: Parser<unknown, Uint8Array>;
23
+ type ZipVersionMadeBy = {
24
+ hostSystem: number;
25
+ zipSpecificationVersion: number;
26
+ };
27
+ type ZipCentralDirectoryHeader = {
28
+ versionMadeBy: ZipVersionMadeBy;
29
+ versionNeededToExtract: number;
30
+ generalPurposeBitFlag: number;
31
+ compressionMethod: ZipCompression;
32
+ lastModFile: Date;
33
+ crc32: number;
34
+ compressedSize: number;
35
+ uncompressedSize: number;
36
+ diskNumberStart: number;
37
+ internalFileAttributes: number;
38
+ externalFileAttributes: number;
39
+ relativeOffsetOfLocalHeader: number;
40
+ fileName: string;
41
+ extraField: Uint8Array;
42
+ fileComment: string;
43
+ };
44
+ export declare const zipCentralDirectoryHeaderParser: Parser<ZipCentralDirectoryHeader, Uint8Array>;
45
+ export declare const zip64EndOfCentralDirectoryRecordParser: Parser<unknown, Uint8Array>;
46
+ export declare const zip64EndOfCentralDirectoryLocatorParser: Parser<unknown, Uint8Array>;
47
+ type ZipEndOfCentralDirectoryRecord = {
48
+ numberOfThisDisk: number;
49
+ numberOfTheDiskWithTheStartOfTheCentralDirectory: number;
50
+ totalNumberOfEntriesInTheCentralDirectoryOnThisDisk: number;
51
+ totalNumberOfEntriesInTheCentralDirectory: number;
52
+ sizeOfTheCentralDirectory: number;
53
+ offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber: number;
54
+ zipFileComment: string;
55
+ };
56
+ export declare const zipEndOfCentralDirectoryRecordParser: Parser<ZipEndOfCentralDirectoryRecord, Uint8Array>;
57
+ export declare function zipEntriesFromZipSegments({ zipLocalFiles, zipCentralDirectoryHeaders, }: {
58
+ zipLocalFiles: ZipLocalFile[];
59
+ zipCentralDirectoryHeaders: ZipCentralDirectoryHeader[];
60
+ }): Promise<ZipEntry[]>;
61
+ export declare function zipFromZipSegments({ zipLocalFiles, zipCentralDirectoryHeaders, zipEndOfCentralDirectoryRecord, }: {
62
+ zipLocalFiles: ZipLocalFile[];
63
+ zipCentralDirectoryHeaders: ZipCentralDirectoryHeader[];
64
+ zipEndOfCentralDirectoryRecord: ZipEndOfCentralDirectoryRecord;
65
+ }): Promise<{
66
+ comment: string;
67
+ entries: ZipEntry[];
68
+ }>;
69
+ export declare const zipParser: Parser<Zip, Uint8Array>;
70
+ export {};
@@ -0,0 +1,255 @@
1
+ import invariant from "invariant";
2
+ import zlib from "node:zlib";
3
+ import { createArrayParser } from "./arrayParser.js";
4
+ import { createExactSequenceParser } from "./exactSequenceParser.js";
5
+ import { setParserName } from "./parser.js";
6
+ import { createTupleParser } from "./tupleParser.js";
7
+ import { promiseCompose } from "./promiseCompose.js";
8
+ import { createFixedLengthSequenceParser } from "./fixedLengthSequenceParser.js";
9
+ import { createOptionalParser } from "./optionalParser.js";
10
+ import { parserCreatorCompose } from "./parserCreatorCompose.js";
11
+ import { Readable } from "node:stream";
12
+ import { pipeline } from "node:stream/promises";
13
+ // https://pkwaredownloads.blob.core.windows.net/pem/APPNOTE.txt
14
+ const uint16LEParser = promiseCompose(createFixedLengthSequenceParser(2), array => Buffer.from(array).readUInt16LE());
15
+ setParserName(uint16LEParser, 'uint16LEParser');
16
+ const uint32LEParser = promiseCompose(createFixedLengthSequenceParser(4), array => Buffer.from(array).readUInt32LE());
17
+ setParserName(uint32LEParser, 'uint32LEParser');
18
+ const dosDateTimeParser = promiseCompose(createTupleParser([
19
+ uint16LEParser,
20
+ uint16LEParser,
21
+ ]), ([time, date]) => new Date(Date.UTC(1980 + ((date >> 9) & 0x7f), ((date >> 5) & 0xf) - 1, date & 0x1f, (time >> 11) & 0x1f, (time >> 5) & 0x3f, (time & 0x1f) * 2)));
22
+ const zipCompressionMethodParser = promiseCompose(uint16LEParser, compressionMethod => {
23
+ if (compressionMethod === 0) {
24
+ return 'store';
25
+ }
26
+ if (compressionMethod === 8) {
27
+ return 'deflate';
28
+ }
29
+ invariant(false, 'Unsupported compression method %s', compressionMethod);
30
+ });
31
+ const zipLocalFileHeaderParser_ = createTupleParser([
32
+ createExactSequenceParser(Buffer.from('504b0304', 'hex')),
33
+ uint16LEParser,
34
+ uint16LEParser,
35
+ zipCompressionMethodParser,
36
+ dosDateTimeParser,
37
+ uint32LEParser,
38
+ uint32LEParser,
39
+ uint32LEParser,
40
+ parserCreatorCompose(() => createTupleParser([
41
+ uint16LEParser,
42
+ uint16LEParser,
43
+ ]), ([fileNameLength, extraFieldLength,]) => createTupleParser([
44
+ createFixedLengthSequenceParser(fileNameLength),
45
+ createFixedLengthSequenceParser(extraFieldLength),
46
+ ]))(),
47
+ ]);
48
+ setParserName(zipLocalFileHeaderParser_, 'zipLocalFileHeaderParser_');
49
+ const zipLocalFileHeaderParser = promiseCompose(zipLocalFileHeaderParser_, ([_zipLocalFileHeaderSignature, versionNeededToExtract, generalPurposeBitFlag, compressionMethod, lastModFile, crc32, compressedSize, uncompressedSize, [fileName, extraField],]) => ({
50
+ versionNeededToExtract,
51
+ generalPurposeBitFlag,
52
+ compressionMethod,
53
+ lastModFile,
54
+ crc32,
55
+ compressedSize,
56
+ uncompressedSize,
57
+ fileName: Buffer.from(fileName).toString('utf8'),
58
+ extraField,
59
+ }));
60
+ const zipEncryptionHeaderParser = async (parserContext) => {
61
+ parserContext.invariant(false, 'Not implemented');
62
+ };
63
+ const zipDataDescriptorParser = createTupleParser([
64
+ // FIXME: optional in spec
65
+ // createOptionalParser(createExactSequenceParser<Uint8Array>(Buffer.from('504b0708', 'hex'))),
66
+ createExactSequenceParser(Buffer.from('504b0708', 'hex')),
67
+ uint32LEParser,
68
+ uint32LEParser,
69
+ uint32LEParser,
70
+ ]);
71
+ export const zipLocalFileParser = promiseCompose(parserCreatorCompose(() => zipLocalFileHeaderParser, (zipLocalFileHeader) => createTupleParser([
72
+ async () => zipLocalFileHeader,
73
+ createOptionalParser(zipEncryptionHeaderParser),
74
+ createFixedLengthSequenceParser(zipLocalFileHeader.compressedSize),
75
+ createOptionalParser(zipDataDescriptorParser),
76
+ ]))(), ([zipLocalFileHeader, zipEncryptionHeader, compressedData, zipDataDescriptor,]) => ({
77
+ zipLocalFileHeader,
78
+ zipEncryptionHeader,
79
+ compressedData,
80
+ zipDataDescriptor,
81
+ }));
82
+ setParserName(zipLocalFileParser, 'zipEntryParser');
83
+ export const zipArchiveDecryptionHeaderParser = async (parserContext) => {
84
+ parserContext.invariant(false, 'Not implemented %s', await parserContext.peek(0));
85
+ };
86
+ export const zipArchiveExtraDataRecordParser = async (parserContext) => {
87
+ parserContext.invariant(false, 'Not implemented');
88
+ };
89
+ const zipVersionMadeByParser = promiseCompose(createFixedLengthSequenceParser(2), async ([zipSpecificationVersion, hostSystem,]) => ({
90
+ hostSystem,
91
+ zipSpecificationVersion,
92
+ }));
93
+ const zipCentralDirectoryHeaderParser_ = createTupleParser([
94
+ createExactSequenceParser(Buffer.from('504b0102', 'hex')),
95
+ zipVersionMadeByParser,
96
+ uint16LEParser,
97
+ uint16LEParser,
98
+ zipCompressionMethodParser,
99
+ dosDateTimeParser,
100
+ uint32LEParser,
101
+ uint32LEParser,
102
+ uint32LEParser,
103
+ parserCreatorCompose(() => createTupleParser([
104
+ uint16LEParser,
105
+ uint16LEParser,
106
+ uint16LEParser,
107
+ uint16LEParser,
108
+ uint16LEParser,
109
+ uint32LEParser,
110
+ uint32LEParser,
111
+ ]), ([fileNameLength, extraFieldLength, fileCommentLength, diskNumberStart, internalFileAttributes, externalFileAttributes, relativeOffsetOfLocalHeader,]) => createTupleParser([
112
+ createFixedLengthSequenceParser(fileNameLength),
113
+ createFixedLengthSequenceParser(extraFieldLength),
114
+ createFixedLengthSequenceParser(fileCommentLength),
115
+ async () => diskNumberStart,
116
+ async () => internalFileAttributes,
117
+ async () => externalFileAttributes,
118
+ async () => relativeOffsetOfLocalHeader,
119
+ ]))(),
120
+ ]);
121
+ setParserName(zipCentralDirectoryHeaderParser_, 'centralDirectoryHeaderParser_');
122
+ export const zipCentralDirectoryHeaderParser = promiseCompose(zipCentralDirectoryHeaderParser_, ([_centralDirectoryHeaderSignature, versionMadeBy, versionNeededToExtract, generalPurposeBitFlag, compressionMethod, lastModFile, crc32, compressedSize, uncompressedSize, [fileName, extraField, fileComment, diskNumberStart, internalFileAttributes, externalFileAttributes, relativeOffsetOfLocalHeader,],]) => ({
123
+ versionMadeBy,
124
+ versionNeededToExtract,
125
+ generalPurposeBitFlag,
126
+ compressionMethod,
127
+ lastModFile,
128
+ crc32,
129
+ compressedSize,
130
+ uncompressedSize,
131
+ diskNumberStart,
132
+ internalFileAttributes,
133
+ externalFileAttributes,
134
+ relativeOffsetOfLocalHeader,
135
+ fileName: Buffer.from(fileName).toString('utf8'),
136
+ extraField,
137
+ fileComment: Buffer.from(fileComment).toString('utf8'),
138
+ }));
139
+ export const zip64EndOfCentralDirectoryRecordParser = async (parserContext) => {
140
+ parserContext.invariant(false, 'Not implemented');
141
+ };
142
+ export const zip64EndOfCentralDirectoryLocatorParser = async (parserContext) => {
143
+ parserContext.invariant(false, 'Not implemented');
144
+ };
145
+ const zipFileCommentParser = promiseCompose(parserCreatorCompose(() => uint16LEParser, length => createFixedLengthSequenceParser(length))(), uint8Array => Buffer.from(uint8Array).toString('utf8'));
146
+ const zipEndOfCentralDirectoryRecordParser_ = createTupleParser([
147
+ createExactSequenceParser(Buffer.from('504b0506', 'hex')),
148
+ uint16LEParser,
149
+ uint16LEParser,
150
+ uint16LEParser,
151
+ uint16LEParser,
152
+ uint32LEParser,
153
+ uint32LEParser,
154
+ zipFileCommentParser,
155
+ ]);
156
+ setParserName(zipEndOfCentralDirectoryRecordParser_, 'endOfCentralDirectoryRecordParser_');
157
+ export const zipEndOfCentralDirectoryRecordParser = promiseCompose(zipEndOfCentralDirectoryRecordParser_, ([_endOfCentralDirectoryRecordSignature, numberOfThisDisk, numberOfTheDiskWithTheStartOfTheCentralDirectory, totalNumberOfEntriesInTheCentralDirectoryOnThisDisk, totalNumberOfEntriesInTheCentralDirectory, sizeOfTheCentralDirectory, offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber, zipFileComment,]) => ({
158
+ numberOfThisDisk,
159
+ numberOfTheDiskWithTheStartOfTheCentralDirectory,
160
+ totalNumberOfEntriesInTheCentralDirectoryOnThisDisk,
161
+ totalNumberOfEntriesInTheCentralDirectory,
162
+ sizeOfTheCentralDirectory,
163
+ offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber,
164
+ zipFileComment,
165
+ }));
166
+ const zipParser_ = createTupleParser([
167
+ createArrayParser(zipLocalFileParser),
168
+ createOptionalParser(zipArchiveDecryptionHeaderParser),
169
+ createOptionalParser(zipArchiveExtraDataRecordParser),
170
+ createArrayParser(zipCentralDirectoryHeaderParser),
171
+ createOptionalParser(zip64EndOfCentralDirectoryRecordParser),
172
+ createOptionalParser(zip64EndOfCentralDirectoryLocatorParser),
173
+ zipEndOfCentralDirectoryRecordParser,
174
+ ]);
175
+ setParserName(zipParser_, 'zipParser_');
176
+ export async function zipEntriesFromZipSegments({ zipLocalFiles, zipCentralDirectoryHeaders, }) {
177
+ const decompressPromises = [];
178
+ const entries = await Promise.all(zipLocalFiles.map(async (zipLocalFile, index) => {
179
+ const { zipLocalFileHeader, compressedData, zipDataDescriptor, zipEncryptionHeader, } = zipLocalFile;
180
+ const centralDirectoryHeader = zipCentralDirectoryHeaders.at(index);
181
+ invariant(centralDirectoryHeader, 'Central directory header not found for local file %s', index);
182
+ let isDosDirectory = false;
183
+ let permissions = {
184
+ type: 'unix',
185
+ unixPermissions: 0,
186
+ };
187
+ if (centralDirectoryHeader.versionMadeBy.hostSystem === 0) {
188
+ isDosDirectory = (centralDirectoryHeader.externalFileAttributes & 0b00010000) !== 0;
189
+ permissions = {
190
+ type: 'dos',
191
+ dosPermissions: centralDirectoryHeader.externalFileAttributes & 0b00111111,
192
+ };
193
+ }
194
+ if (centralDirectoryHeader.versionMadeBy.hostSystem === 3) {
195
+ permissions = {
196
+ type: 'unix',
197
+ unixPermissions: (centralDirectoryHeader.externalFileAttributes >> 16) & 0b111111111,
198
+ };
199
+ }
200
+ const commonFields = {
201
+ path: zipLocalFileHeader.fileName,
202
+ comment: centralDirectoryHeader.fileComment,
203
+ date: zipLocalFileHeader.lastModFile,
204
+ permissions,
205
+ };
206
+ const isDirectory = isDosDirectory || commonFields.path.endsWith('/');
207
+ if (isDirectory) {
208
+ return {
209
+ ...commonFields,
210
+ type: 'directory',
211
+ path: commonFields.path.slice(0, -1),
212
+ };
213
+ }
214
+ const fileEntry = {
215
+ ...commonFields,
216
+ type: 'file',
217
+ content: compressedData,
218
+ compression: zipLocalFileHeader.compressionMethod,
219
+ };
220
+ if (fileEntry.content.length && fileEntry.compression === 'deflate') {
221
+ const deflate = zlib.createInflateRaw();
222
+ const input = Readable.from(Buffer.from(compressedData));
223
+ const [_, buffer] = await Promise.all([
224
+ pipeline(input, deflate),
225
+ (async () => {
226
+ const chunks = [];
227
+ for await (const chunk of deflate) {
228
+ chunks.push(chunk);
229
+ }
230
+ return Buffer.concat(chunks);
231
+ })(),
232
+ ]);
233
+ fileEntry.content = Uint8Array.from(buffer);
234
+ }
235
+ return fileEntry;
236
+ }));
237
+ await Promise.all(decompressPromises);
238
+ return entries;
239
+ }
240
+ export async function zipFromZipSegments({ zipLocalFiles, zipCentralDirectoryHeaders, zipEndOfCentralDirectoryRecord, }) {
241
+ const entries = await zipEntriesFromZipSegments({
242
+ zipLocalFiles,
243
+ zipCentralDirectoryHeaders,
244
+ });
245
+ return {
246
+ comment: zipEndOfCentralDirectoryRecord.zipFileComment,
247
+ entries,
248
+ };
249
+ }
250
+ export const zipParser = promiseCompose(zipParser_, async ([zipLocalFiles, zipArchiveDecryptionHeader, zipArchiveExtraDataRecords, zipCentralDirectoryHeaders, zip64EndOfCentralDirectoryRecord, zip64EndOfCentralDirectoryLocator, zipEndOfCentralDirectoryRecord,]) => zipFromZipSegments({
251
+ zipLocalFiles,
252
+ zipCentralDirectoryHeaders,
253
+ zipEndOfCentralDirectoryRecord,
254
+ }));
255
+ setParserName(zipParser, 'zipParser');
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ import { testProp } from '@fast-check/ava';
2
+ import { arbitraryZipStream } from './arbitraryZipStream.js';
3
+ import { zipParser } from './zipParser.js';
4
+ import { uint8ArrayInputCompanion } from './inputCompanion.js';
5
+ import { runParser } from './parser.js';
6
+ async function* readableStreamToUint8ArrayAsyncIterator(stream) {
7
+ for await (const chunk of stream) {
8
+ yield chunk;
9
+ }
10
+ }
11
+ testProp('zip', [
12
+ arbitraryZipStream,
13
+ ], async (t, [zip, zipStream]) => {
14
+ const actual = await runParser(zipParser, readableStreamToUint8ArrayAsyncIterator(zipStream), uint8ArrayInputCompanion, {
15
+ errorJoinMode: 'all',
16
+ });
17
+ actual.entries.sort((a, b) => a.path.localeCompare(b.path));
18
+ zip.entries.sort((a, b) => a.path.localeCompare(b.path));
19
+ t.deepEqual(actual, zip);
20
+ }, {
21
+ verbose: true,
22
+ });
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@futpib/parser",
3
+ "version": "1.0.0",
4
+ "main": "build/index.js",
5
+ "license": "GPL-3.0-only",
6
+ "type": "module",
7
+ "scripts": {
8
+ "dev": "tsc --watch",
9
+ "build": "tsc",
10
+ "test": "xo && c8 ava",
11
+ "prepack": "yarn build"
12
+ },
13
+ "homepage": "https://github.com/futpib/parser",
14
+ "ava": {
15
+ "typescript": {
16
+ "rewritePaths": {
17
+ "src/": "build/"
18
+ },
19
+ "compile": false
20
+ },
21
+ "verbose": true
22
+ },
23
+ "devDependencies": {
24
+ "@ava/typescript": "^5.0.0",
25
+ "@fast-check/ava": "^2.0.1",
26
+ "@types/invariant": "^2.2.37",
27
+ "@types/node": "^22.10.1",
28
+ "ava": "^6.2.0",
29
+ "bson": "^6.10.0",
30
+ "c8": "^10.1.2",
31
+ "coveralls": "^3.1.1",
32
+ "eslint-config-xo-typescript-overrides": "^1.6.1",
33
+ "fast-check": "^3.23.1",
34
+ "invariant": "^2.2.4",
35
+ "jszip": "^3.10.1",
36
+ "type-fest": "^4.29.0",
37
+ "typescript": "^5.7.2",
38
+ "xo": "^0.59.3"
39
+ },
40
+ "xo": {
41
+ "extends": [
42
+ "eslint-config-xo-typescript-overrides"
43
+ ],
44
+ "rules": {
45
+ "ava/no-ignored-test-files": "off"
46
+ }
47
+ },
48
+ "packageManager": "yarn@4.5.3",
49
+ "dependencies": {
50
+ "p-mutex": "^0.1.0"
51
+ }
52
+ }
package/readme.md ADDED
@@ -0,0 +1,17 @@
1
+ # parser
2
+
3
+ > parser
4
+
5
+ [![Coverage Status](https://coveralls.io/repos/github/futpib/parser/badge.svg?branch=master)](https://coveralls.io/github/futpib/parser?branch=master)
6
+
7
+ ## Example
8
+
9
+ ```js
10
+ // parser
11
+ ```
12
+
13
+ ## Install
14
+
15
+ ```
16
+ yarn add --dev @futpib/parser
17
+ ```
package/renovate.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": [
3
+ "config:recommended"
4
+ ],
5
+ "rangeStrategy": "replace"
6
+ }
@@ -0,0 +1,59 @@
1
+ import test from 'ava';
2
+ import { allSettledStream } from './allSettledStream.js';
3
+
4
+ test('allSettledStream', async t => {
5
+ const stream = allSettledStream<number, number | string>([
6
+ {
7
+ delay: 10,
8
+ context: 1,
9
+ },
10
+ {
11
+ delay: 10,
12
+ context: 'a',
13
+ },
14
+ {
15
+ delay: 20,
16
+ context: 2,
17
+ },
18
+ {
19
+ delay: 20,
20
+ context: 'b',
21
+ },
22
+ {
23
+ delay: 0,
24
+ context: 3,
25
+ },
26
+ {
27
+ delay: 0,
28
+ context: 'c',
29
+ },
30
+ {
31
+ delay: 40,
32
+ context: 4,
33
+ },
34
+ {
35
+ delay: 40,
36
+ context: 'd',
37
+ },
38
+ ].map(({ delay, context }) => ({
39
+ promise: new Promise<number>((resolve, reject) => {
40
+ setTimeout(() => {
41
+ if (typeof context === 'number') {
42
+ resolve(context);
43
+ } else {
44
+ reject(context);
45
+ }
46
+ }, delay);
47
+ }),
48
+ context,
49
+ })));
50
+
51
+ const results: any[] = [];
52
+
53
+ for await (const value of stream) {
54
+ results.push(value);
55
+ }
56
+
57
+ t.snapshot(results);
58
+ t.is(results.length, 8);
59
+ });
@@ -0,0 +1,52 @@
1
+ # Snapshot report for `src/allSettledStream.test.ts`
2
+
3
+ The actual snapshot is saved in `allSettledStream.test.ts.snap`.
4
+
5
+ Generated by [AVA](https://avajs.dev).
6
+
7
+ ## allSettledStream
8
+
9
+ > Snapshot 1
10
+
11
+ [
12
+ {
13
+ context: 3,
14
+ status: 'fulfilled',
15
+ value: 3,
16
+ },
17
+ {
18
+ context: 'c',
19
+ reason: 'c',
20
+ status: 'rejected',
21
+ },
22
+ {
23
+ context: 1,
24
+ status: 'fulfilled',
25
+ value: 1,
26
+ },
27
+ {
28
+ context: 'a',
29
+ reason: 'a',
30
+ status: 'rejected',
31
+ },
32
+ {
33
+ context: 2,
34
+ status: 'fulfilled',
35
+ value: 2,
36
+ },
37
+ {
38
+ context: 'b',
39
+ reason: 'b',
40
+ status: 'rejected',
41
+ },
42
+ {
43
+ context: 4,
44
+ status: 'fulfilled',
45
+ value: 4,
46
+ },
47
+ {
48
+ context: 'd',
49
+ reason: 'd',
50
+ status: 'rejected',
51
+ },
52
+ ]
@@ -0,0 +1,44 @@
1
+ type AllSettledStreamFulfilledResult<T, Context> = {
2
+ status: 'fulfilled';
3
+ value: T;
4
+ context: Context;
5
+ };
6
+
7
+ type AllSettledStreamRejectedResult<T, Context> = {
8
+ status: 'rejected';
9
+ reason: unknown;
10
+ context: Context;
11
+ };
12
+
13
+ type AllSettedStreamResult<T, Context> = AllSettledStreamFulfilledResult<T, Context> | AllSettledStreamRejectedResult<T, Context>;
14
+
15
+ type AllSettledStreamTask<T, Context> = {
16
+ promise: Promise<T>;
17
+ context: Context;
18
+ };
19
+
20
+ export function allSettledStream<T, Context>(tasks: Array<AllSettledStreamTask<T, Context>>): ReadableStream<AllSettedStreamResult<T, Context>> {
21
+ return new ReadableStream({
22
+ async start(controller) {
23
+ let settledCount = 0;
24
+ for (const { promise, context } of tasks) {
25
+ (async () => {
26
+ const [ promiseSettledResult ] = await Promise.allSettled([ promise ]);
27
+
28
+ settledCount++;
29
+
30
+ const allSettedStreamResult: AllSettedStreamResult<T, Context> = {
31
+ ...promiseSettledResult,
32
+ context,
33
+ };
34
+
35
+ controller.enqueue(allSettedStreamResult);
36
+
37
+ if (settledCount === tasks.length) {
38
+ controller.close();
39
+ }
40
+ })();
41
+ }
42
+ },
43
+ });
44
+ }
package/src/apk.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { Zip } from './zip.js';
2
+
3
+ export type ApkSigningBlockPair = {
4
+ id: number;
5
+ value: Uint8Array;
6
+ };
7
+
8
+ export type ApkSigningBlock = {
9
+ zeroPaddingLength?: number;
10
+ signatureV2?: unknown;
11
+ pairs: ApkSigningBlockPair[];
12
+ };
13
+
14
+ export type Apk = Zip & {
15
+ signingBlock?: ApkSigningBlock;
16
+ };
@@ -0,0 +1,30 @@
1
+ import test from 'ava';
2
+ import { uint8ArrayInputCompanion } from './inputCompanion.js';
3
+ import { runParser } from './parser.js';
4
+ import { apkParser } from './apkParser.js';
5
+
6
+ for (const apkCid of [
7
+ 'bafkreicckcmzrdxwoc3w2in3tivpyxrdtcfpct4zwauq3igew3nkpvfapu',
8
+ 'bafkreidqycbgrtp7ywhevsgtm464dqpamxsijpaqfcfz2k3ymc3wrzsb5m',
9
+ ]) {
10
+ test(
11
+ 'apk ' + apkCid,
12
+ async t => {
13
+ const apkResponse = await fetch('https://ipfs.io/ipfs/' + apkCid);
14
+
15
+ const apkStream = apkResponse.body!;
16
+
17
+ const actual = await runParser(apkParser, apkStream, uint8ArrayInputCompanion, {
18
+ errorJoinMode: 'all',
19
+ });
20
+
21
+ for (const entry of actual.entries) {
22
+ if (entry.type === 'file') {
23
+ entry.content = new Uint8Array([ entry.content.length ]);
24
+ }
25
+ }
26
+
27
+ t.snapshot(actual);
28
+ },
29
+ );
30
+ }