@gryt/core 0.31.0 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/import/discordImportJob.d.ts +14 -0
- package/dist/import/discordImportJob.js +14 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mls/dmDriver.js +23 -14
- package/dist/pairing/approver.js +19 -5
- package/dist/pairing/newDevice.js +8 -1
- package/package.json +1 -1
|
@@ -41,6 +41,14 @@ export interface ImportDeps {
|
|
|
41
41
|
/** The server's file size limit, so a file over it is never sealed and sent to be refused. */
|
|
42
42
|
maxFileBytes?: number | null;
|
|
43
43
|
}
|
|
44
|
+
/** A file that didn't come over, for the receipt. */
|
|
45
|
+
export interface ImportLeftOut {
|
|
46
|
+
url: string;
|
|
47
|
+
name: string;
|
|
48
|
+
/** Bytes, when it was downloaded. */
|
|
49
|
+
size: number | null;
|
|
50
|
+
reason: "too_large" | "not_downloaded";
|
|
51
|
+
}
|
|
44
52
|
export interface ImportProgress {
|
|
45
53
|
messages: {
|
|
46
54
|
done: number;
|
|
@@ -58,6 +66,7 @@ export interface ImportProgress {
|
|
|
58
66
|
/** Files this server won't take, each left out with a note on its message. */
|
|
59
67
|
tooLarge: number;
|
|
60
68
|
bytes: number;
|
|
69
|
+
leftOut: ImportLeftOut[];
|
|
61
70
|
startedAt: number;
|
|
62
71
|
}
|
|
63
72
|
export interface ImportControl {
|
|
@@ -67,5 +76,10 @@ export interface ImportControl {
|
|
|
67
76
|
/** For a 429's Retry-After header. Only the seconds form: anything else gets the default wait. */
|
|
68
77
|
export declare function retryAfterMs(header: string | null): number;
|
|
69
78
|
export declare const importId: (discordId: string) => string;
|
|
79
|
+
/**
|
|
80
|
+
* Another go at files left out: new entries beside the originals, holding only those files.
|
|
81
|
+
* A first run's message is already in history, and a repeat of its id is skipped there.
|
|
82
|
+
*/
|
|
83
|
+
export declare function retryLeftOut(messages: DiscordPackageMessage[], urls: ReadonlySet<string>): DiscordPackageMessage[];
|
|
70
84
|
/** Text first, then 20 messages at a time: their files go straight through, then those messages go. */
|
|
71
85
|
export declare function runDiscordImport(messages: DiscordPackageMessage[], deps: ImportDeps, control: ImportControl, report: (p: ImportProgress) => void): Promise<ImportProgress>;
|
|
@@ -24,6 +24,16 @@ function nameOf(url) {
|
|
|
24
24
|
return "file";
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Another go at files left out: new entries beside the originals, holding only those files.
|
|
29
|
+
* A first run's message is already in history, and a repeat of its id is skipped there.
|
|
30
|
+
*/
|
|
31
|
+
export function retryLeftOut(messages, urls) {
|
|
32
|
+
return messages.flatMap((m) => {
|
|
33
|
+
const attachments = m.attachments.filter((u) => urls.has(u));
|
|
34
|
+
return attachments.length ? [{ ...m, discordId: `${m.discordId}-files`, text: "", attachments }] : [];
|
|
35
|
+
});
|
|
36
|
+
}
|
|
27
37
|
/** Text first, then 20 messages at a time: their files go straight through, then those messages go. */
|
|
28
38
|
export async function runDiscordImport(messages, deps, control, report) {
|
|
29
39
|
const fresh = [];
|
|
@@ -39,9 +49,10 @@ export async function runDiscordImport(messages, deps, control, report) {
|
|
|
39
49
|
missing: 0,
|
|
40
50
|
tooLarge: 0,
|
|
41
51
|
bytes: 0,
|
|
52
|
+
leftOut: [],
|
|
42
53
|
startedAt: Date.now(),
|
|
43
54
|
};
|
|
44
|
-
const tick = () => report({ ...p, messages: { ...p.messages }, downloaded: { ...p.downloaded }, sent: { ...p.sent } });
|
|
55
|
+
const tick = () => report({ ...p, leftOut: [...p.leftOut], messages: { ...p.messages }, downloaded: { ...p.downloaded }, sent: { ...p.sent } });
|
|
45
56
|
tick();
|
|
46
57
|
const hold = async () => {
|
|
47
58
|
while (control.paused() && !control.stopped())
|
|
@@ -118,6 +129,7 @@ export async function runDiscordImport(messages, deps, control, report) {
|
|
|
118
129
|
else if (!control.stopped()) {
|
|
119
130
|
missing.add(url);
|
|
120
131
|
p.missing = missing.size;
|
|
132
|
+
p.leftOut.push({ url, name: nameOf(url), size: null, reason: "not_downloaded" });
|
|
121
133
|
}
|
|
122
134
|
}
|
|
123
135
|
if (control.stopped()) {
|
|
@@ -143,6 +155,7 @@ export async function runDiscordImport(messages, deps, control, report) {
|
|
|
143
155
|
if ("tooLarge" in up) {
|
|
144
156
|
tooLarge.add(url);
|
|
145
157
|
p.tooLarge = tooLarge.size;
|
|
158
|
+
p.leftOut.push({ url, name: nameOf(url), size: file.bytes.length, reason: "too_large" });
|
|
146
159
|
}
|
|
147
160
|
else
|
|
148
161
|
uploaded.set(url, up);
|
package/dist/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export { decodeMlsDmContent, encodeImportBundle, readImportBundle, encodeMlsDmCo
|
|
|
17
17
|
export { applyMlsReaction, mlsReactionAction, type MlsReaction } from "./mls/reactions.js";
|
|
18
18
|
export { type DiscordPackageDm, type DiscordPackageMessage, isDiscordPackage, linksExpireAt, listDiscordDms, type PackageFiles, readDiscordDm, } from "./import/discordPackage.js";
|
|
19
19
|
export { inflateRaw, openZipBytes } from "./import/unzip.js";
|
|
20
|
-
export { type ImportControl, type ImportDeps, type ImportFile, type ImportFileStore, importId, type ImportProgress, retryAfterMs, runDiscordImport, } from "./import/discordImportJob.js";
|
|
20
|
+
export { type ImportControl, type ImportDeps, type ImportFile, type ImportFileStore, importId, type ImportProgress, retryAfterMs, retryLeftOut, runDiscordImport, type ImportLeftOut, } from "./import/discordImportJob.js";
|
|
21
21
|
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";
|
|
22
22
|
export { createApproverPairing, type ApproverOptions, type ApproverPairing, type ApproverState, } from "./pairing/approver.js";
|
|
23
23
|
export { createNewDevicePairing, PAIRING_APPROVAL_MS, type NewDeviceOptions, type NewDevicePairing, type NewDeviceState, } from "./pairing/newDevice.js";
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ export { decodeMlsDmContent, encodeImportBundle, readImportBundle, encodeMlsDmCo
|
|
|
19
19
|
export { applyMlsReaction, mlsReactionAction } from "./mls/reactions.js";
|
|
20
20
|
export { isDiscordPackage, linksExpireAt, listDiscordDms, readDiscordDm, } from "./import/discordPackage.js";
|
|
21
21
|
export { inflateRaw, openZipBytes } from "./import/unzip.js";
|
|
22
|
-
export { importId, retryAfterMs, runDiscordImport, } from "./import/discordImportJob.js";
|
|
22
|
+
export { importId, retryAfterMs, retryLeftOut, runDiscordImport, } from "./import/discordImportJob.js";
|
|
23
23
|
export { createApproverPairing, } from "./pairing/approver.js";
|
|
24
24
|
export { createNewDevicePairing, PAIRING_APPROVAL_MS, } from "./pairing/newDevice.js";
|
|
25
25
|
export { createPairingRelay, PairingRelayError } from "./pairing/relayClient.js";
|
package/dist/mls/dmDriver.js
CHANGED
|
@@ -2,6 +2,7 @@ import { addMlsMembers, asIdentityScope, base64UrlDecode, createMlsGroup, decode
|
|
|
2
2
|
/** Devices per person per server. Must match the server's MLS_MAX_DEVICES, or extra devices are left out quietly. */
|
|
3
3
|
const MLS_MAX_DEVICES_PER_PERSON = 100;
|
|
4
4
|
const COMMIT_ATTEMPTS = 5;
|
|
5
|
+
const OWN_DEVICE_ADDS_AT_ONCE = 4;
|
|
5
6
|
/** socket.io closes the connection on a packet with more than 10 binary parts (GRYT-1522). */
|
|
6
7
|
const MAX_BINARY_PARTS = 10;
|
|
7
8
|
const LOG_PAGE = MAX_BINARY_PARTS;
|
|
@@ -1072,21 +1073,29 @@ export function createMlsDmDriver(options) {
|
|
|
1072
1073
|
const ids = (await store.listGroups()).map((r) => r.conversationId);
|
|
1073
1074
|
const first = (options.order ?? []).filter((id) => ids.includes(id));
|
|
1074
1075
|
const ordered = [...new Set([...first, ...ids])];
|
|
1075
|
-
const results =
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1076
|
+
const results = new Array(ordered.length);
|
|
1077
|
+
let done = 0;
|
|
1078
|
+
let next = 0;
|
|
1079
|
+
// Each group is its own commit, so a few go at once; `order` still decides which start first.
|
|
1080
|
+
const worker = async () => {
|
|
1081
|
+
while (next < ordered.length) {
|
|
1082
|
+
const i = next++;
|
|
1083
|
+
const conversationId = ordered[i];
|
|
1084
|
+
let result;
|
|
1085
|
+
try {
|
|
1086
|
+
result = await run(conversationId, () => addOwnDeviceTo(conversationId, deviceId, ownPersonKey));
|
|
1087
|
+
}
|
|
1088
|
+
catch (e) {
|
|
1089
|
+
if (e instanceof MlsDriverError && e.code === "not_own_device")
|
|
1090
|
+
throw e;
|
|
1091
|
+
const groupId = (await store.loadGroup(conversationId))?.groupId ?? "";
|
|
1092
|
+
result = { conversationId, groupId, outcome: "failed", add: null, error: e instanceof MlsDriverError ? e.code : "error" };
|
|
1093
|
+
}
|
|
1094
|
+
results[i] = result;
|
|
1095
|
+
options.onProgress?.({ done: ++done, total: ordered.length, result });
|
|
1086
1096
|
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
}
|
|
1097
|
+
};
|
|
1098
|
+
await Promise.all(Array.from({ length: Math.min(OWN_DEVICE_ADDS_AT_ONCE, ordered.length) }, worker));
|
|
1090
1099
|
return results;
|
|
1091
1100
|
}
|
|
1092
1101
|
return {
|
package/dist/pairing/approver.js
CHANGED
|
@@ -142,16 +142,30 @@ export function createApproverPairing(options) {
|
|
|
142
142
|
}
|
|
143
143
|
async function addEverywhere(ready) {
|
|
144
144
|
const added = {};
|
|
145
|
-
|
|
145
|
+
// Servers go at once, so progress is the sum over them.
|
|
146
|
+
const counts = new Map();
|
|
147
|
+
const report = (host) => {
|
|
148
|
+
let done = 0;
|
|
149
|
+
let total = 0;
|
|
150
|
+
for (const c of counts.values()) {
|
|
151
|
+
done += c.done;
|
|
152
|
+
total += c.total;
|
|
153
|
+
}
|
|
154
|
+
set({ phase: "adding", device: confirmed().device, host, done, total });
|
|
155
|
+
};
|
|
156
|
+
await Promise.all(ready.map(async ({ host, deviceId }) => {
|
|
146
157
|
const driver = devices(host);
|
|
147
158
|
if (!driver)
|
|
148
|
-
|
|
159
|
+
return;
|
|
149
160
|
for (let tries = 0;; tries++) {
|
|
150
161
|
try {
|
|
151
162
|
added[host] = await driver.addOwnDevice(deviceId, {
|
|
152
|
-
onProgress: ({ done, total }) =>
|
|
163
|
+
onProgress: ({ done, total }) => {
|
|
164
|
+
counts.set(host, { done, total });
|
|
165
|
+
report(host);
|
|
166
|
+
},
|
|
153
167
|
});
|
|
154
|
-
|
|
168
|
+
return;
|
|
155
169
|
}
|
|
156
170
|
catch (e) {
|
|
157
171
|
// The server lists N's device once its KeyPackages are in, which can trail N's "ready".
|
|
@@ -160,7 +174,7 @@ export function createApproverPairing(options) {
|
|
|
160
174
|
await clock.sleep(2000, abort.signal);
|
|
161
175
|
}
|
|
162
176
|
}
|
|
163
|
-
}
|
|
177
|
+
}));
|
|
164
178
|
return added;
|
|
165
179
|
}
|
|
166
180
|
/** Each group's cursor on every server in the envelope: the snapshot covers up to there. */
|
|
@@ -234,7 +234,14 @@ export function createNewDevicePairing(options) {
|
|
|
234
234
|
throw new Error("Not waiting to join.");
|
|
235
235
|
const { from } = state;
|
|
236
236
|
const body = sealJson(current.session, { t: "ready", devices: devices.map(({ host, deviceId }) => ({ host, deviceId })) });
|
|
237
|
-
|
|
237
|
+
try {
|
|
238
|
+
await relay.post(current.id, current.token, { type: "sealed", body });
|
|
239
|
+
}
|
|
240
|
+
catch (e) {
|
|
241
|
+
// A closed before N got here: say so, rather than sit on "joining" with nothing listening.
|
|
242
|
+
await end(reasonFor(e));
|
|
243
|
+
throw e;
|
|
244
|
+
}
|
|
238
245
|
set({ phase: "linked", from });
|
|
239
246
|
void linked(from);
|
|
240
247
|
},
|
package/package.json
CHANGED