@gryt/core 0.21.0 → 0.22.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.
- package/dist/import/unzip.d.ts +5 -0
- package/dist/import/unzip.js +187 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { PackageFiles } from "./discordPackage.js";
|
|
2
|
+
/** Raw DEFLATE, as a zip stores it. `size` is the inflated length the zip's header gives. */
|
|
3
|
+
export declare function inflateRaw(data: Uint8Array, size: number): Uint8Array;
|
|
4
|
+
/** The files in a zip held in memory, read lazily. */
|
|
5
|
+
export declare function openZipBytes(zip: Uint8Array): PackageFiles;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/* A zip reader with its own inflate (RFC 1951), for Hermes, which has no DecompressionStream.
|
|
2
|
+
Read bit by bit: slow next to zlib, and fine for a package of JSON. No Zip64, no encryption. */
|
|
3
|
+
class Bits {
|
|
4
|
+
pos = 0;
|
|
5
|
+
bit = 0;
|
|
6
|
+
data;
|
|
7
|
+
constructor(data) {
|
|
8
|
+
this.data = data;
|
|
9
|
+
}
|
|
10
|
+
read(n) {
|
|
11
|
+
let v = 0;
|
|
12
|
+
for (let i = 0; i < n; i++) {
|
|
13
|
+
if (this.pos >= this.data.length)
|
|
14
|
+
throw new Error("The zip ends in the middle of a file.");
|
|
15
|
+
v |= ((this.data[this.pos] >> this.bit) & 1) << i;
|
|
16
|
+
if (++this.bit === 8) {
|
|
17
|
+
this.bit = 0;
|
|
18
|
+
this.pos++;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return v;
|
|
22
|
+
}
|
|
23
|
+
align() {
|
|
24
|
+
if (this.bit) {
|
|
25
|
+
this.bit = 0;
|
|
26
|
+
this.pos++;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function tree(lengths) {
|
|
31
|
+
const counts = new Uint16Array(16);
|
|
32
|
+
for (let i = 0; i < lengths.length; i++)
|
|
33
|
+
counts[lengths[i]]++;
|
|
34
|
+
counts[0] = 0;
|
|
35
|
+
const offsets = new Uint16Array(16);
|
|
36
|
+
for (let i = 1; i < 16; i++)
|
|
37
|
+
offsets[i] = offsets[i - 1] + counts[i - 1];
|
|
38
|
+
const symbols = new Uint16Array(lengths.length);
|
|
39
|
+
for (let i = 0; i < lengths.length; i++)
|
|
40
|
+
if (lengths[i])
|
|
41
|
+
symbols[offsets[lengths[i]]++] = i;
|
|
42
|
+
return { counts, symbols };
|
|
43
|
+
}
|
|
44
|
+
function decode(bits, t) {
|
|
45
|
+
let sum = 0;
|
|
46
|
+
let cur = 0;
|
|
47
|
+
let len = 0;
|
|
48
|
+
do {
|
|
49
|
+
cur = 2 * cur + bits.read(1);
|
|
50
|
+
if (++len > 15)
|
|
51
|
+
throw new Error("The zip has a damaged file in it.");
|
|
52
|
+
sum += t.counts[len];
|
|
53
|
+
cur -= t.counts[len];
|
|
54
|
+
} while (cur >= 0);
|
|
55
|
+
return t.symbols[sum + cur];
|
|
56
|
+
}
|
|
57
|
+
const LENGTH_BASE = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258];
|
|
58
|
+
const LENGTH_EXTRA = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0];
|
|
59
|
+
const DIST_BASE = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577];
|
|
60
|
+
const DIST_EXTRA = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13];
|
|
61
|
+
const ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
|
|
62
|
+
function fixedLength(i) {
|
|
63
|
+
if (i < 144)
|
|
64
|
+
return 8;
|
|
65
|
+
if (i < 256)
|
|
66
|
+
return 9;
|
|
67
|
+
return i < 280 ? 7 : 8;
|
|
68
|
+
}
|
|
69
|
+
const FIXED_LIT = tree(Array.from({ length: 288 }, (_, i) => fixedLength(i)));
|
|
70
|
+
const FIXED_DIST = tree(new Array(30).fill(5));
|
|
71
|
+
function dynamicTrees(bits) {
|
|
72
|
+
const hlit = bits.read(5) + 257;
|
|
73
|
+
const hdist = bits.read(5) + 1;
|
|
74
|
+
const hclen = bits.read(4) + 4;
|
|
75
|
+
const codeLengths = new Uint8Array(19);
|
|
76
|
+
for (let i = 0; i < hclen; i++)
|
|
77
|
+
codeLengths[ORDER[i]] = bits.read(3);
|
|
78
|
+
const codes = tree(codeLengths);
|
|
79
|
+
const lengths = new Uint8Array(hlit + hdist);
|
|
80
|
+
for (let n = 0; n < hlit + hdist;) {
|
|
81
|
+
const sym = decode(bits, codes);
|
|
82
|
+
if (sym < 16) {
|
|
83
|
+
lengths[n++] = sym;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
let repeat = 0;
|
|
87
|
+
let value = 0;
|
|
88
|
+
if (sym === 16) {
|
|
89
|
+
if (n === 0)
|
|
90
|
+
throw new Error("The zip has a damaged file in it.");
|
|
91
|
+
value = lengths[n - 1];
|
|
92
|
+
repeat = 3 + bits.read(2);
|
|
93
|
+
}
|
|
94
|
+
else if (sym === 17)
|
|
95
|
+
repeat = 3 + bits.read(3);
|
|
96
|
+
else
|
|
97
|
+
repeat = 11 + bits.read(7);
|
|
98
|
+
while (repeat-- > 0)
|
|
99
|
+
lengths[n++] = value;
|
|
100
|
+
}
|
|
101
|
+
return [tree(lengths.subarray(0, hlit)), tree(lengths.subarray(hlit))];
|
|
102
|
+
}
|
|
103
|
+
/** Raw DEFLATE, as a zip stores it. `size` is the inflated length the zip's header gives. */
|
|
104
|
+
export function inflateRaw(data, size) {
|
|
105
|
+
const out = new Uint8Array(size);
|
|
106
|
+
let o = 0;
|
|
107
|
+
const bits = new Bits(data);
|
|
108
|
+
let last = 0;
|
|
109
|
+
while (!last) {
|
|
110
|
+
last = bits.read(1);
|
|
111
|
+
const type = bits.read(2);
|
|
112
|
+
if (type === 0) {
|
|
113
|
+
bits.align();
|
|
114
|
+
const len = data[bits.pos] | (data[bits.pos + 1] << 8);
|
|
115
|
+
bits.pos += 4;
|
|
116
|
+
out.set(data.subarray(bits.pos, bits.pos + len), o);
|
|
117
|
+
bits.pos += len;
|
|
118
|
+
o += len;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (type === 3)
|
|
122
|
+
throw new Error("The zip has a damaged file in it.");
|
|
123
|
+
const [lit, dist] = type === 1 ? [FIXED_LIT, FIXED_DIST] : dynamicTrees(bits);
|
|
124
|
+
for (;;) {
|
|
125
|
+
const sym = decode(bits, lit);
|
|
126
|
+
if (sym === 256)
|
|
127
|
+
break;
|
|
128
|
+
if (sym < 256) {
|
|
129
|
+
out[o++] = sym;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const li = sym - 257;
|
|
133
|
+
const length = LENGTH_BASE[li] + bits.read(LENGTH_EXTRA[li]);
|
|
134
|
+
const di = decode(bits, dist);
|
|
135
|
+
const back = DIST_BASE[di] + bits.read(DIST_EXTRA[di]);
|
|
136
|
+
if (back > o || o + length > size)
|
|
137
|
+
throw new Error("The zip has a damaged file in it.");
|
|
138
|
+
for (let k = 0; k < length; k++, o++)
|
|
139
|
+
out[o] = out[o - back];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return o === size ? out : out.subarray(0, o);
|
|
143
|
+
}
|
|
144
|
+
/** The files in a zip held in memory, read lazily. */
|
|
145
|
+
export function openZipBytes(zip) {
|
|
146
|
+
const view = new DataView(zip.buffer, zip.byteOffset, zip.byteLength);
|
|
147
|
+
let eocd = -1;
|
|
148
|
+
for (let i = zip.length - 22; i >= Math.max(0, zip.length - 65_557); i--) {
|
|
149
|
+
if (view.getUint32(i, true) === 0x06054b50) {
|
|
150
|
+
eocd = i;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (eocd < 0)
|
|
155
|
+
throw new Error("That file isn't a zip.");
|
|
156
|
+
const count = view.getUint16(eocd + 10, true);
|
|
157
|
+
let p = view.getUint32(eocd + 16, true);
|
|
158
|
+
if (p === 0xffffffff)
|
|
159
|
+
throw new Error("That zip is too big to read here.");
|
|
160
|
+
const decoder = new TextDecoder();
|
|
161
|
+
const entries = new Map();
|
|
162
|
+
for (let n = 0; n < count && view.getUint32(p, true) === 0x02014b50; n++) {
|
|
163
|
+
const nameLength = view.getUint16(p + 28, true);
|
|
164
|
+
const name = decoder.decode(zip.subarray(p + 46, p + 46 + nameLength));
|
|
165
|
+
if (!name.endsWith("/")) {
|
|
166
|
+
entries.set(name, { method: view.getUint16(p + 10, true), compressed: view.getUint32(p + 20, true), size: view.getUint32(p + 24, true), local: view.getUint32(p + 42, true) });
|
|
167
|
+
}
|
|
168
|
+
p += 46 + nameLength + view.getUint16(p + 30, true) + view.getUint16(p + 32, true);
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
names: [...entries.keys()],
|
|
172
|
+
async read(name) {
|
|
173
|
+
const e = entries.get(name);
|
|
174
|
+
if (!e)
|
|
175
|
+
throw new Error(`${name} isn't in the zip.`);
|
|
176
|
+
if (view.getUint32(e.local, true) !== 0x04034b50)
|
|
177
|
+
throw new Error(`${name} is damaged in the zip.`);
|
|
178
|
+
const start = e.local + 30 + view.getUint16(e.local + 26, true) + view.getUint16(e.local + 28, true);
|
|
179
|
+
const data = zip.subarray(start, start + e.compressed);
|
|
180
|
+
if (e.method === 0)
|
|
181
|
+
return decoder.decode(data);
|
|
182
|
+
if (e.method !== 8)
|
|
183
|
+
throw new Error(`${name} is packed in a way this can't read.`);
|
|
184
|
+
return decoder.decode(inflateRaw(data, e.size));
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export { mlsPinsFromPeerPins } from "./mls/pins.js";
|
|
|
15
15
|
export { decodeMlsDmContent, encodeMlsDmContent, acceptImport, historyBatches, importBatches, MAX_HISTORY_BATCH_BYTES, MLS_DM_CONTENT_VERSION, type MlsDmContent, type MlsHistoryEntry, type MlsImportEntry, readMlsDmContent, } from "./mls/content.js";
|
|
16
16
|
export { applyMlsReaction, mlsReactionAction, type MlsReaction } from "./mls/reactions.js";
|
|
17
17
|
export { type DiscordPackageDm, type DiscordPackageMessage, isDiscordPackage, linksExpireAt, listDiscordDms, type PackageFiles, readDiscordDm, } from "./import/discordPackage.js";
|
|
18
|
+
export { inflateRaw, openZipBytes } from "./import/unzip.js";
|
|
18
19
|
export type { DmSealingMode, MlsAddOwnDeviceOptions, MlsClaimedKeyPackage, MlsDecryptedMessage, MlsDeviceRecord, MlsDeviceRef, MlsDmDriver, MlsDmDriverOptions, MlsDmEvents, MlsGroupPosition, MlsGroupRecord, MlsGroupView, MlsKeyPackageRecord, MlsListedDevice, MlsLogEntry, MlsDmDiagnostics, MlsOwnDevice, MlsOwnDeviceAdd, MlsPins, MlsRefusal, MlsReply, MlsServerCapability, MlsStateStore, MlsTransport, MlsWelcomeDelivery, } from "./mls/interfaces.js";
|
|
19
20
|
export { createApproverPairing, type ApproverOptions, type ApproverPairing, type ApproverState, } from "./pairing/approver.js";
|
|
20
21
|
export { createNewDevicePairing, PAIRING_APPROVAL_MS, type NewDeviceOptions, type NewDevicePairing, type NewDeviceState, } from "./pairing/newDevice.js";
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export { mlsPinsFromPeerPins } from "./mls/pins.js";
|
|
|
17
17
|
export { decodeMlsDmContent, encodeMlsDmContent, acceptImport, historyBatches, importBatches, MAX_HISTORY_BATCH_BYTES, MLS_DM_CONTENT_VERSION, readMlsDmContent, } from "./mls/content.js";
|
|
18
18
|
export { applyMlsReaction, mlsReactionAction } from "./mls/reactions.js";
|
|
19
19
|
export { isDiscordPackage, linksExpireAt, listDiscordDms, readDiscordDm, } from "./import/discordPackage.js";
|
|
20
|
+
export { inflateRaw, openZipBytes } from "./import/unzip.js";
|
|
20
21
|
export { createApproverPairing, } from "./pairing/approver.js";
|
|
21
22
|
export { createNewDevicePairing, PAIRING_APPROVAL_MS, } from "./pairing/newDevice.js";
|
|
22
23
|
export { createPairingRelay, PairingRelayError } from "./pairing/relayClient.js";
|
package/package.json
CHANGED