@vex-chat/libvex 6.6.1 → 6.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/Client.d.ts +3 -14
- package/dist/Client.d.ts.map +1 -1
- package/dist/Client.js +75 -104
- package/dist/Client.js.map +1 -1
- package/dist/codec.js +1 -1
- package/dist/codecs.d.ts +3 -6
- package/dist/codecs.d.ts.map +1 -1
- package/dist/codecs.js +6 -9
- package/dist/codecs.js.map +1 -1
- package/dist/http.d.ts +83 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +368 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
- package/src/Client.ts +113 -138
- package/src/__tests__/harness/shared-suite.ts +2 -3
- package/src/__tests__/http.test.ts +98 -0
- package/src/codec.ts +1 -1
- package/src/codecs.ts +6 -9
- package/src/http.ts +563 -0
- package/src/index.ts +6 -0
- package/dist/storage/node/http-agents.d.ts +0 -20
- package/dist/storage/node/http-agents.d.ts.map +0 -1
- package/dist/storage/node/http-agents.js +0 -26
- package/dist/storage/node/http-agents.js.map +0 -1
- package/src/storage/node/http-agents.ts +0 -39
package/dist/Client.js
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { enterCryptoProfileScope, getCryptoProfile, leaveCryptoProfileScope, setCryptoProfile, xBoxKeyPairAsync, xBoxKeyPairFromSecretAsync, xConcat, xConstants, xDHAsync, xEcdhKeyPairFromEcdsaKeyPairAsync, xEncode, xHMAC, xKDF, XKeyConvert, xMakeNonce, xMnemonic, xRandomBytes, xSecretboxAsync, xSecretboxOpenAsync, xSignAsync, xSignKeyPair, xSignKeyPairAsync, xSignKeyPairFromSecret, xSignKeyPairFromSecretAsync, XUtils, } from "@vex-chat/crypto";
|
|
7
7
|
import { MailType, MailWSSchema, PermissionSchema, WSMessageSchema, } from "@vex-chat/types";
|
|
8
|
-
import axios, { isAxiosError } from "axios";
|
|
9
8
|
import { EventEmitter } from "eventemitter3";
|
|
10
9
|
import * as uuid from "uuid";
|
|
11
10
|
import { z } from "zod/v4";
|
|
11
|
+
import { createFetchHttpClient, isHttpError, } from "./http.js";
|
|
12
12
|
import { clampLocalMessageRetentionDays, formatVexRetentionEnvelope, stripVexRetentionEnvelope, } from "./retention.js";
|
|
13
13
|
import { WebSocketAdapter, WebSocketNotOpenError, } from "./transport/websocket.js";
|
|
14
14
|
import { decodeFipsInitialExtraV1, encodeFipsInitialExtraV1, fipsP256AdFromIdentityPubs, fipsP256PreKeySignPayload, isFipsInitialExtraV1, } from "./utils/fipsMailExtra.js";
|
|
@@ -195,10 +195,22 @@ function spireErrorBodyMessage(data, max = 8_000) {
|
|
|
195
195
|
return t.length > max ? t.slice(0, max) + "…" : t;
|
|
196
196
|
}
|
|
197
197
|
import { msgpack } from "./codec.js";
|
|
198
|
-
import { ActionTokenCodec, AuthResponseCodec, ChannelArrayCodec, ChannelCodec, ConnectResponseCodec,
|
|
198
|
+
import { ActionTokenCodec, AuthResponseCodec, ChannelArrayCodec, ChannelCodec, ConnectResponseCodec, decodeHttpResponse, DeviceArrayCodec, DeviceChallengeCodec, DeviceCodec, DeviceRegistrationResultCodec, EmojiArrayCodec, EmojiCodec, FileSQLCodec, InviteArrayCodec, InviteCodec, KeyBundleCodec, OtkCountCodec, PasskeyArrayCodec, PasskeyAuthFinishResponseCodec, PasskeyCodec, PasskeyOptionsCodec, PendingDeviceRequestArrayCodec, PendingDeviceRequestCodec, PermissionArrayCodec, PermissionCodec, RegisterPendingApprovalCodec, RegisterResponseCodec, ServerArrayCodec, ServerChannelBootstrapCodec, ServerCodec, UserArrayCodec, UserCodec, WhoamiCodec, } from "./codecs.js";
|
|
199
199
|
import { sqlSessionToCrypto } from "./utils/sqlSessionToCrypto.js";
|
|
200
200
|
import { uuidToUint8 } from "./utils/uint8uuid.js";
|
|
201
201
|
const _protocolMsgRegex = /��\w+:\w+��/g;
|
|
202
|
+
const NotificationSubscriptionSchema = z.object({
|
|
203
|
+
channel: z.literal("expo"),
|
|
204
|
+
createdAt: z.string(),
|
|
205
|
+
deviceID: z.string(),
|
|
206
|
+
enabled: z.boolean(),
|
|
207
|
+
events: z.array(z.string()),
|
|
208
|
+
platform: z.string().nullable(),
|
|
209
|
+
subscriptionID: z.string(),
|
|
210
|
+
token: z.string(),
|
|
211
|
+
updatedAt: z.string(),
|
|
212
|
+
userID: z.string(),
|
|
213
|
+
});
|
|
202
214
|
function compareInboxEntries(a, b) {
|
|
203
215
|
const timeCmp = a[2].localeCompare(b[2]);
|
|
204
216
|
if (timeCmp !== 0) {
|
|
@@ -585,7 +597,7 @@ export class Client {
|
|
|
585
597
|
forwarded = new Set();
|
|
586
598
|
host;
|
|
587
599
|
http;
|
|
588
|
-
/** Cancels in-flight
|
|
600
|
+
/** Cancels in-flight HTTP work on `close()` so `postAuth`/`getMail` cannot hang forever. */
|
|
589
601
|
httpAbortController = new AbortController();
|
|
590
602
|
idKeys;
|
|
591
603
|
isAlive = true;
|
|
@@ -593,11 +605,6 @@ export class Client {
|
|
|
593
605
|
localRetentionPurgeTimer = null;
|
|
594
606
|
mailInterval;
|
|
595
607
|
manuallyClosing = false;
|
|
596
|
-
/**
|
|
597
|
-
* Node-only: per-client HTTP(S) agents (see `init()` + `storage/node/http-agents`).
|
|
598
|
-
* Dropped on `close()` so idle keep-alive sockets do not keep the process alive.
|
|
599
|
-
*/
|
|
600
|
-
nodeHttpAgents;
|
|
601
608
|
/* Retrieves the userID with the user identifier.
|
|
602
609
|
user identifier is checked for userID, then signkey,
|
|
603
610
|
and finally falls back to username. */
|
|
@@ -658,7 +665,7 @@ export class Client {
|
|
|
658
665
|
this.database.on("error", (_error) => {
|
|
659
666
|
void this.close(true);
|
|
660
667
|
});
|
|
661
|
-
this.http =
|
|
668
|
+
this.http = createFetchHttpClient({
|
|
662
669
|
responseType: "arraybuffer",
|
|
663
670
|
signal: this.httpAbortController.signal,
|
|
664
671
|
});
|
|
@@ -830,27 +837,6 @@ export class Client {
|
|
|
830
837
|
return undefined;
|
|
831
838
|
}
|
|
832
839
|
}
|
|
833
|
-
/**
|
|
834
|
-
* True when running under Node (has `process.versions`).
|
|
835
|
-
* Uses indirect lookup so the bare `process` global never appears in
|
|
836
|
-
* source that the platform-guard plugin scans.
|
|
837
|
-
*/
|
|
838
|
-
static isNodeRuntime() {
|
|
839
|
-
try {
|
|
840
|
-
const g = Object.getOwnPropertyDescriptor(globalThis, "\u0070rocess");
|
|
841
|
-
if (!g)
|
|
842
|
-
return false;
|
|
843
|
-
const proc = typeof g.get === "function" ? g.get() : g.value;
|
|
844
|
-
if (typeof proc !== "object" || proc === null) {
|
|
845
|
-
return false;
|
|
846
|
-
}
|
|
847
|
-
return ("versions" in proc &&
|
|
848
|
-
typeof proc.versions === "object");
|
|
849
|
-
}
|
|
850
|
-
catch {
|
|
851
|
-
return false;
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
840
|
/**
|
|
855
841
|
* Closes the client — disconnects the WebSocket, shuts down storage,
|
|
856
842
|
* and emits `closed` unless `muteEvent` is `true`.
|
|
@@ -867,11 +853,6 @@ export class Client {
|
|
|
867
853
|
this.httpAbortController.abort();
|
|
868
854
|
this.socket.close();
|
|
869
855
|
await this.database.close();
|
|
870
|
-
if (this.nodeHttpAgents) {
|
|
871
|
-
this.nodeHttpAgents.http.destroy();
|
|
872
|
-
this.nodeHttpAgents.https.destroy();
|
|
873
|
-
delete this.nodeHttpAgents;
|
|
874
|
-
}
|
|
875
856
|
if (this.pingInterval) {
|
|
876
857
|
clearInterval(this.pingInterval);
|
|
877
858
|
}
|
|
@@ -909,7 +890,7 @@ export class Client {
|
|
|
909
890
|
}
|
|
910
891
|
const signedAsync = await xSignAsync(Uint8Array.from(uuid.parse(connectToken.key)), this.signKeys.secretKey);
|
|
911
892
|
const res = await this.http.post(this.getHost() + "/device/" + this.device.deviceID + "/connect", msgpack.encode({ signed: signedAsync }), { headers: { "Content-Type": "application/msgpack" } });
|
|
912
|
-
const { deviceToken } =
|
|
893
|
+
const { deviceToken } = decodeHttpResponse(ConnectResponseCodec, res.data);
|
|
913
894
|
this.http.defaults.headers.common["X-Device-Token"] = deviceToken;
|
|
914
895
|
this.autoReconnectEnabled = true;
|
|
915
896
|
this.initSocket();
|
|
@@ -978,14 +959,14 @@ export class Client {
|
|
|
978
959
|
}), {
|
|
979
960
|
headers: { "Content-Type": "application/msgpack" },
|
|
980
961
|
});
|
|
981
|
-
const { token, user } =
|
|
962
|
+
const { token, user } = decodeHttpResponse(AuthResponseCodec, res.data);
|
|
982
963
|
this.setUser(user);
|
|
983
964
|
this.token = token;
|
|
984
965
|
this.http.defaults.headers.common.Authorization = `Bearer ${token}`;
|
|
985
966
|
return { ok: true };
|
|
986
967
|
}
|
|
987
968
|
catch (err) {
|
|
988
|
-
if (
|
|
969
|
+
if (isHttpError(err) && err.response) {
|
|
989
970
|
return {
|
|
990
971
|
error: spireErrorBodyMessage(err.response.data),
|
|
991
972
|
ok: false,
|
|
@@ -1014,10 +995,10 @@ export class Client {
|
|
|
1014
995
|
deviceID: id,
|
|
1015
996
|
signKey: signKeyHex,
|
|
1016
997
|
}), { headers: { "Content-Type": "application/msgpack" } });
|
|
1017
|
-
const { challenge, challengeID } =
|
|
998
|
+
const { challenge, challengeID } = decodeHttpResponse(DeviceChallengeCodec, challengeRes.data);
|
|
1018
999
|
const signed = XUtils.encodeHex(await xSignAsync(XUtils.decodeHex(challenge), this.signKeys.secretKey));
|
|
1019
1000
|
const verifyRes = await this.http.post(this.getHost() + "/auth/device/verify", msgpack.encode({ challengeID, signed }), { headers: { "Content-Type": "application/msgpack" } });
|
|
1020
|
-
const { token, user } =
|
|
1001
|
+
const { token, user } = decodeHttpResponse(AuthResponseCodec, verifyRes.data);
|
|
1021
1002
|
this.setUser(user);
|
|
1022
1003
|
this.token = token;
|
|
1023
1004
|
this.http.defaults.headers.common.Authorization = `Bearer ${token}`;
|
|
@@ -1128,7 +1109,7 @@ export class Client {
|
|
|
1128
1109
|
let didDecodeRegisterResponse = false;
|
|
1129
1110
|
let pendingApproval = null;
|
|
1130
1111
|
try {
|
|
1131
|
-
const { device, token, user } =
|
|
1112
|
+
const { device, token, user } = decodeHttpResponse(RegisterResponseCodec, res.data);
|
|
1132
1113
|
this.device = device;
|
|
1133
1114
|
this.setUser(user);
|
|
1134
1115
|
this.token = token;
|
|
@@ -1140,7 +1121,7 @@ export class Client {
|
|
|
1140
1121
|
}
|
|
1141
1122
|
if (!didDecodeRegisterResponse) {
|
|
1142
1123
|
try {
|
|
1143
|
-
pendingApproval =
|
|
1124
|
+
pendingApproval = decodeHttpResponse(RegisterPendingApprovalCodec, res.data);
|
|
1144
1125
|
}
|
|
1145
1126
|
catch {
|
|
1146
1127
|
// fall through to legacy decode path
|
|
@@ -1158,7 +1139,7 @@ export class Client {
|
|
|
1158
1139
|
}),
|
|
1159
1140
|
];
|
|
1160
1141
|
}
|
|
1161
|
-
const legacyUser =
|
|
1142
|
+
const legacyUser = decodeHttpResponse(UserCodec, res.data);
|
|
1162
1143
|
this.setUser(legacyUser);
|
|
1163
1144
|
// Legacy servers require /auth after /register to get a JWT.
|
|
1164
1145
|
const loginResult = await this.login(resolvedUsername, resolvedPassword);
|
|
@@ -1173,7 +1154,7 @@ export class Client {
|
|
|
1173
1154
|
return [this.getUser(), null];
|
|
1174
1155
|
}
|
|
1175
1156
|
catch (err) {
|
|
1176
|
-
if (
|
|
1157
|
+
if (isHttpError(err) && err.response) {
|
|
1177
1158
|
return [
|
|
1178
1159
|
null,
|
|
1179
1160
|
new Error(spireErrorBodyMessage(err.response.data)),
|
|
@@ -1213,7 +1194,7 @@ export class Client {
|
|
|
1213
1194
|
"/device/" +
|
|
1214
1195
|
this.getDevice().deviceID +
|
|
1215
1196
|
"/notifications/subscriptions", input, { responseType: "json" });
|
|
1216
|
-
return response.data;
|
|
1197
|
+
return NotificationSubscriptionSchema.parse(response.data);
|
|
1217
1198
|
}
|
|
1218
1199
|
/**
|
|
1219
1200
|
* Triggers an immediate inbox sync by fetching `/mail` once.
|
|
@@ -1254,7 +1235,7 @@ export class Client {
|
|
|
1254
1235
|
*/
|
|
1255
1236
|
async whoami() {
|
|
1256
1237
|
const res = await this.http.post(this.getHost() + "/whoami");
|
|
1257
|
-
const whoami =
|
|
1238
|
+
const whoami = decodeHttpResponse(WhoamiCodec, res.data);
|
|
1258
1239
|
return whoami;
|
|
1259
1240
|
}
|
|
1260
1241
|
async abortPendingDeviceRegistration(args) {
|
|
@@ -1299,16 +1280,16 @@ export class Client {
|
|
|
1299
1280
|
"/devices/requests/" +
|
|
1300
1281
|
requestID +
|
|
1301
1282
|
"/approve", msgpack.encode({ signed }), { headers: { "Content-Type": "application/msgpack" } });
|
|
1302
|
-
return
|
|
1283
|
+
return decodeHttpResponse(DeviceCodec, response.data);
|
|
1303
1284
|
}
|
|
1304
1285
|
async beginPasskeyAuthentication(username) {
|
|
1305
1286
|
const response = await this.http.post(this.getHost() + "/auth/passkey/begin", msgpack.encode({ username }), { headers: { "Content-Type": "application/msgpack" } });
|
|
1306
|
-
return
|
|
1287
|
+
return decodeHttpResponse(PasskeyOptionsCodec, response.data);
|
|
1307
1288
|
}
|
|
1308
1289
|
async beginPasskeyRegistration(name) {
|
|
1309
1290
|
const userID = this.getUser().userID;
|
|
1310
1291
|
const response = await this.http.post(this.getHost() + "/user/" + userID + "/passkeys/register/begin", msgpack.encode({ name }), { headers: { "Content-Type": "application/msgpack" } });
|
|
1311
|
-
return
|
|
1292
|
+
return decodeHttpResponse(PasskeyOptionsCodec, response.data);
|
|
1312
1293
|
}
|
|
1313
1294
|
censorPreKey(preKey) {
|
|
1314
1295
|
if (!preKey.index) {
|
|
@@ -1324,7 +1305,7 @@ export class Client {
|
|
|
1324
1305
|
async createChannel(name, serverID) {
|
|
1325
1306
|
const body = { name };
|
|
1326
1307
|
const res = await this.http.post(this.getHost() + "/server/" + serverID + "/channels", msgpack.encode(body), { headers: { "Content-Type": "application/msgpack" } });
|
|
1327
|
-
return
|
|
1308
|
+
return decodeHttpResponse(ChannelCodec, res.data);
|
|
1328
1309
|
}
|
|
1329
1310
|
// returns the file details and the encryption key
|
|
1330
1311
|
async createFile(file) {
|
|
@@ -1355,7 +1336,7 @@ export class Client {
|
|
|
1355
1336
|
this.emitter.emit("fileProgress", progress);
|
|
1356
1337
|
},
|
|
1357
1338
|
});
|
|
1358
|
-
const fcreatedFile =
|
|
1339
|
+
const fcreatedFile = decodeHttpResponse(FileSQLCodec, fres.data);
|
|
1359
1340
|
return [fcreatedFile, XUtils.encodeHex(fileKey)];
|
|
1360
1341
|
}
|
|
1361
1342
|
const payload = {
|
|
@@ -1364,7 +1345,7 @@ export class Client {
|
|
|
1364
1345
|
owner: this.getDevice().deviceID,
|
|
1365
1346
|
};
|
|
1366
1347
|
const res = await this.http.post(this.getHost() + "/file/json", msgpack.encode(payload), { headers: { "Content-Type": "application/msgpack" } });
|
|
1367
|
-
const createdFile =
|
|
1348
|
+
const createdFile = decodeHttpResponse(FileSQLCodec, res.data);
|
|
1368
1349
|
return [createdFile, XUtils.encodeHex(fileKey)];
|
|
1369
1350
|
});
|
|
1370
1351
|
}
|
|
@@ -1374,7 +1355,7 @@ export class Client {
|
|
|
1374
1355
|
serverID,
|
|
1375
1356
|
};
|
|
1376
1357
|
const res = await this.http.post(this.getHost() + "/server/" + serverID + "/invites", msgpack.encode(payload), { headers: { "Content-Type": "application/msgpack" } });
|
|
1377
|
-
return
|
|
1358
|
+
return decodeHttpResponse(InviteCodec, res.data);
|
|
1378
1359
|
}
|
|
1379
1360
|
async createPreKey() {
|
|
1380
1361
|
const preKeyPair = await xBoxKeyPairAsync();
|
|
@@ -1388,7 +1369,7 @@ export class Client {
|
|
|
1388
1369
|
}
|
|
1389
1370
|
async createServer(name) {
|
|
1390
1371
|
const res = await this.http.post(this.getHost() + "/server/" + globalThis.btoa(name));
|
|
1391
|
-
return
|
|
1372
|
+
return decodeHttpResponse(ServerCodec, res.data);
|
|
1392
1373
|
}
|
|
1393
1374
|
async createSession(device, user, message, group,
|
|
1394
1375
|
/* this is passed through if the first message is
|
|
@@ -1584,7 +1565,7 @@ export class Client {
|
|
|
1584
1565
|
await this.http.delete(this.getHost() + "/server/" + serverID);
|
|
1585
1566
|
}
|
|
1586
1567
|
deviceListFailureDetail(err) {
|
|
1587
|
-
if (!
|
|
1568
|
+
if (!isHttpError(err)) {
|
|
1588
1569
|
return "";
|
|
1589
1570
|
}
|
|
1590
1571
|
const st = err.response?.status;
|
|
@@ -1607,7 +1588,7 @@ export class Client {
|
|
|
1607
1588
|
"/server/" +
|
|
1608
1589
|
serverID +
|
|
1609
1590
|
"/permissions");
|
|
1610
|
-
return
|
|
1591
|
+
return decodeHttpResponse(PermissionArrayCodec, res.data);
|
|
1611
1592
|
}
|
|
1612
1593
|
async fetchUser(userIdentifier) {
|
|
1613
1594
|
// Usernames are case-insensitive at the protocol level, so
|
|
@@ -1631,19 +1612,19 @@ export class Client {
|
|
|
1631
1612
|
}
|
|
1632
1613
|
try {
|
|
1633
1614
|
const res = await this.http.get(this.getHost() + "/user/" + cacheKey);
|
|
1634
|
-
const userRecord =
|
|
1615
|
+
const userRecord = decodeHttpResponse(UserCodec, res.data);
|
|
1635
1616
|
this.userRecords[cacheKey] = userRecord;
|
|
1636
1617
|
this.notFoundUsers.delete(cacheKey);
|
|
1637
1618
|
return [userRecord, null];
|
|
1638
1619
|
}
|
|
1639
1620
|
catch (err) {
|
|
1640
|
-
if (
|
|
1621
|
+
if (isHttpError(err) && err.response?.status === 404) {
|
|
1641
1622
|
// Definitive: user doesn't exist — cache and don't retry
|
|
1642
1623
|
this.notFoundUsers.set(cacheKey, Date.now());
|
|
1643
1624
|
return [null, err];
|
|
1644
1625
|
}
|
|
1645
1626
|
// Transient (5xx, network error) — don't cache, caller can retry
|
|
1646
|
-
return [null,
|
|
1627
|
+
return [null, isHttpError(err) ? err : null];
|
|
1647
1628
|
}
|
|
1648
1629
|
}
|
|
1649
1630
|
async fetchUserDeviceListOnce(userID) {
|
|
@@ -1651,7 +1632,7 @@ export class Client {
|
|
|
1651
1632
|
return [];
|
|
1652
1633
|
}
|
|
1653
1634
|
const res = await this.http.get(this.getHost() + "/user/" + userID + "/devices");
|
|
1654
|
-
const devices =
|
|
1635
|
+
const devices = decodeHttpResponse(DeviceArrayCodec, res.data);
|
|
1655
1636
|
for (const device of devices) {
|
|
1656
1637
|
this.deviceRecords[device.deviceID] = device;
|
|
1657
1638
|
}
|
|
@@ -1698,7 +1679,7 @@ export class Client {
|
|
|
1698
1679
|
*/
|
|
1699
1680
|
async finishPasskeyAuthentication(args) {
|
|
1700
1681
|
const response = await this.http.post(this.getHost() + "/auth/passkey/finish", msgpack.encode(args), { headers: { "Content-Type": "application/msgpack" } });
|
|
1701
|
-
const decoded =
|
|
1682
|
+
const decoded = decodeHttpResponse(PasskeyAuthFinishResponseCodec, response.data);
|
|
1702
1683
|
this.setUser(decoded.user);
|
|
1703
1684
|
this.token = decoded.token;
|
|
1704
1685
|
this.http.defaults.headers.common.Authorization = `Bearer ${decoded.token}`;
|
|
@@ -1707,7 +1688,7 @@ export class Client {
|
|
|
1707
1688
|
async finishPasskeyRegistration(args) {
|
|
1708
1689
|
const userID = this.getUser().userID;
|
|
1709
1690
|
const response = await this.http.post(this.getHost() + "/user/" + userID + "/passkeys/register/finish", msgpack.encode(args), { headers: { "Content-Type": "application/msgpack" } });
|
|
1710
|
-
return
|
|
1691
|
+
return decodeHttpResponse(PasskeyCodec, response.data);
|
|
1711
1692
|
}
|
|
1712
1693
|
async forward(message) {
|
|
1713
1694
|
if (this.isManualCloseInFlight()) {
|
|
@@ -1741,7 +1722,7 @@ export class Client {
|
|
|
1741
1722
|
async getChannelByID(channelID) {
|
|
1742
1723
|
try {
|
|
1743
1724
|
const res = await this.http.get(this.getHost() + "/channel/" + channelID);
|
|
1744
|
-
return
|
|
1725
|
+
return decodeHttpResponse(ChannelCodec, res.data);
|
|
1745
1726
|
}
|
|
1746
1727
|
catch (_err) {
|
|
1747
1728
|
return null;
|
|
@@ -1749,7 +1730,7 @@ export class Client {
|
|
|
1749
1730
|
}
|
|
1750
1731
|
async getChannelList(serverID) {
|
|
1751
1732
|
const res = await this.http.get(this.getHost() + "/server/" + serverID + "/channels");
|
|
1752
|
-
return
|
|
1733
|
+
return decodeHttpResponse(ChannelArrayCodec, res.data);
|
|
1753
1734
|
}
|
|
1754
1735
|
getDevice() {
|
|
1755
1736
|
if (!this.device) {
|
|
@@ -1768,7 +1749,7 @@ export class Client {
|
|
|
1768
1749
|
}
|
|
1769
1750
|
try {
|
|
1770
1751
|
const res = await this.http.get(this.getHost() + "/device/" + deviceID);
|
|
1771
|
-
const fetchedDevice =
|
|
1752
|
+
const fetchedDevice = decodeHttpResponse(DeviceCodec, res.data);
|
|
1772
1753
|
this.deviceRecords[deviceID] = fetchedDevice;
|
|
1773
1754
|
await this.database.saveDevice(fetchedDevice);
|
|
1774
1755
|
return fetchedDevice;
|
|
@@ -1785,10 +1766,10 @@ export class Client {
|
|
|
1785
1766
|
this.getUser().userID +
|
|
1786
1767
|
"/devices/requests/" +
|
|
1787
1768
|
requestID);
|
|
1788
|
-
return
|
|
1769
|
+
return decodeHttpResponse(PendingDeviceRequestCodec, response.data);
|
|
1789
1770
|
}
|
|
1790
1771
|
catch (err) {
|
|
1791
|
-
if (
|
|
1772
|
+
if (isHttpError(err) && err.response?.status === 404) {
|
|
1792
1773
|
return null;
|
|
1793
1774
|
}
|
|
1794
1775
|
throw err;
|
|
@@ -1885,7 +1866,7 @@ export class Client {
|
|
|
1885
1866
|
async getMultiUserDeviceList(userIDs) {
|
|
1886
1867
|
try {
|
|
1887
1868
|
const res = await this.http.post(this.getHost() + "/deviceList", msgpack.encode(userIDs), { headers: { "Content-Type": "application/msgpack" } });
|
|
1888
|
-
const devices =
|
|
1869
|
+
const devices = decodeHttpResponse(DeviceArrayCodec, res.data);
|
|
1889
1870
|
for (const device of devices) {
|
|
1890
1871
|
this.deviceRecords[device.deviceID] = device;
|
|
1891
1872
|
}
|
|
@@ -1900,7 +1881,7 @@ export class Client {
|
|
|
1900
1881
|
"/device/" +
|
|
1901
1882
|
this.getDevice().deviceID +
|
|
1902
1883
|
"/otk/count");
|
|
1903
|
-
return
|
|
1884
|
+
return decodeHttpResponse(OtkCountCodec, res.data).count;
|
|
1904
1885
|
}
|
|
1905
1886
|
/**
|
|
1906
1887
|
* Gets all permissions for the logged in user.
|
|
@@ -1909,12 +1890,12 @@ export class Client {
|
|
|
1909
1890
|
*/
|
|
1910
1891
|
async getPermissions() {
|
|
1911
1892
|
const res = await this.http.get(this.getHost() + "/user/" + this.getUser().userID + "/permissions");
|
|
1912
|
-
return
|
|
1893
|
+
return decodeHttpResponse(PermissionArrayCodec, res.data);
|
|
1913
1894
|
}
|
|
1914
1895
|
async getServerByID(serverID) {
|
|
1915
1896
|
try {
|
|
1916
1897
|
const res = await this.http.get(this.getHost() + "/server/" + serverID);
|
|
1917
|
-
return
|
|
1898
|
+
return decodeHttpResponse(ServerCodec, res.data);
|
|
1918
1899
|
}
|
|
1919
1900
|
catch (_err) {
|
|
1920
1901
|
return null;
|
|
@@ -1925,11 +1906,11 @@ export class Client {
|
|
|
1925
1906
|
"/user/" +
|
|
1926
1907
|
this.getUser().userID +
|
|
1927
1908
|
"/servers/bootstrap");
|
|
1928
|
-
return
|
|
1909
|
+
return decodeHttpResponse(ServerChannelBootstrapCodec, res.data);
|
|
1929
1910
|
}
|
|
1930
1911
|
async getServerList() {
|
|
1931
1912
|
const res = await this.http.get(this.getHost() + "/user/" + this.getUser().userID + "/servers");
|
|
1932
|
-
return
|
|
1913
|
+
return decodeHttpResponse(ServerArrayCodec, res.data);
|
|
1933
1914
|
}
|
|
1934
1915
|
async getSessionByPubkey(publicKey) {
|
|
1935
1916
|
const strPubKey = XUtils.encodeHex(publicKey);
|
|
@@ -1950,7 +1931,7 @@ export class Client {
|
|
|
1950
1931
|
const res = await this.http.get(this.getHost() + "/token/" + type, {
|
|
1951
1932
|
responseType: "arraybuffer",
|
|
1952
1933
|
});
|
|
1953
|
-
return
|
|
1934
|
+
return decodeHttpResponse(ActionTokenCodec, res.data);
|
|
1954
1935
|
}
|
|
1955
1936
|
catch {
|
|
1956
1937
|
return null;
|
|
@@ -1980,7 +1961,7 @@ export class Client {
|
|
|
1980
1961
|
}
|
|
1981
1962
|
async getUserList(channelID) {
|
|
1982
1963
|
const res = await this.http.post(this.getHost() + "/userList/" + channelID);
|
|
1983
|
-
return
|
|
1964
|
+
return decodeHttpResponse(UserArrayCodec, res.data);
|
|
1984
1965
|
}
|
|
1985
1966
|
async handleNotify(msg) {
|
|
1986
1967
|
switch (msg.event) {
|
|
@@ -2044,12 +2025,6 @@ export class Client {
|
|
|
2044
2025
|
throw new Error("You should only call init() once.");
|
|
2045
2026
|
}
|
|
2046
2027
|
this.hasInit = true;
|
|
2047
|
-
if (Client.isNodeRuntime()) {
|
|
2048
|
-
const { attachNodeAgentsToAxios, createNodeHttpAgents } = await import("./storage/node/http-agents.js");
|
|
2049
|
-
const agents = createNodeHttpAgents();
|
|
2050
|
-
this.nodeHttpAgents = agents;
|
|
2051
|
-
attachNodeAgentsToAxios(this.http, agents);
|
|
2052
|
-
}
|
|
2053
2028
|
await this.populateKeyRing();
|
|
2054
2029
|
this.emitter.on("message", this.onInternalMessage);
|
|
2055
2030
|
void this.runLocalRetentionPurge();
|
|
@@ -2182,7 +2157,7 @@ export class Client {
|
|
|
2182
2157
|
"/user/" +
|
|
2183
2158
|
this.getUser().userID +
|
|
2184
2159
|
"/devices/requests");
|
|
2185
|
-
return
|
|
2160
|
+
return decodeHttpResponse(PendingDeviceRequestArrayCodec, response.data);
|
|
2186
2161
|
}
|
|
2187
2162
|
/**
|
|
2188
2163
|
* Lists every device the current account owns.
|
|
@@ -2193,12 +2168,12 @@ export class Client {
|
|
|
2193
2168
|
async listDevices() {
|
|
2194
2169
|
const userID = this.getUser().userID;
|
|
2195
2170
|
const res = await this.http.get(this.getHost() + "/user/" + userID + "/devices");
|
|
2196
|
-
return
|
|
2171
|
+
return decodeHttpResponse(DeviceArrayCodec, res.data);
|
|
2197
2172
|
}
|
|
2198
2173
|
async listPasskeys() {
|
|
2199
2174
|
const userID = this.getUser().userID;
|
|
2200
2175
|
const response = await this.http.get(this.getHost() + "/user/" + userID + "/passkeys");
|
|
2201
|
-
return
|
|
2176
|
+
return decodeHttpResponse(PasskeyArrayCodec, response.data);
|
|
2202
2177
|
}
|
|
2203
2178
|
async markSessionVerified(sessionID) {
|
|
2204
2179
|
return this.database.markSessionVerified(sessionID);
|
|
@@ -2249,7 +2224,7 @@ export class Client {
|
|
|
2249
2224
|
"/passkey/devices/requests/" +
|
|
2250
2225
|
requestID +
|
|
2251
2226
|
"/approve");
|
|
2252
|
-
return
|
|
2227
|
+
return decodeHttpResponse(DeviceCodec, response.data);
|
|
2253
2228
|
}
|
|
2254
2229
|
async passkeyDeleteDevice(deviceID) {
|
|
2255
2230
|
const userID = this.getUser().userID;
|
|
@@ -2258,7 +2233,7 @@ export class Client {
|
|
|
2258
2233
|
async passkeyListDevices() {
|
|
2259
2234
|
const userID = this.getUser().userID;
|
|
2260
2235
|
const response = await this.http.get(this.getHost() + "/user/" + userID + "/passkey/devices");
|
|
2261
|
-
return
|
|
2236
|
+
return decodeHttpResponse(DeviceArrayCodec, response.data);
|
|
2262
2237
|
}
|
|
2263
2238
|
async passkeyRejectDeviceRequest(requestID) {
|
|
2264
2239
|
const userID = this.getUser().userID;
|
|
@@ -2310,10 +2285,10 @@ export class Client {
|
|
|
2310
2285
|
"/user/devices/requests/" +
|
|
2311
2286
|
args.requestID +
|
|
2312
2287
|
"/poll", msgpack.encode({ signed }), { headers: { "Content-Type": "application/msgpack" } });
|
|
2313
|
-
return
|
|
2288
|
+
return decodeHttpResponse(PendingDeviceRequestCodec, response.data);
|
|
2314
2289
|
}
|
|
2315
2290
|
catch (err) {
|
|
2316
|
-
if (
|
|
2291
|
+
if (isHttpError(err) && err.response?.status === 404) {
|
|
2317
2292
|
return null;
|
|
2318
2293
|
}
|
|
2319
2294
|
throw err;
|
|
@@ -2930,7 +2905,7 @@ export class Client {
|
|
|
2930
2905
|
}
|
|
2931
2906
|
async redeemInvite(inviteID) {
|
|
2932
2907
|
const res = await this.http.patch(this.getHost() + "/invite/" + inviteID);
|
|
2933
|
-
return
|
|
2908
|
+
return decodeHttpResponse(PermissionCodec, res.data);
|
|
2934
2909
|
}
|
|
2935
2910
|
registerDecryptFailure(mail) {
|
|
2936
2911
|
const count = (this.decryptFailureCounts.get(mail.mailID) ?? 0) + 1;
|
|
@@ -2969,7 +2944,7 @@ export class Client {
|
|
|
2969
2944
|
"/user/" +
|
|
2970
2945
|
userDetails.userID +
|
|
2971
2946
|
"/devices", msgpack.encode(devMsg), { headers: { "Content-Type": "application/msgpack" } });
|
|
2972
|
-
return
|
|
2947
|
+
return decodeHttpResponse(DeviceRegistrationResultCodec, res.data);
|
|
2973
2948
|
}
|
|
2974
2949
|
async rejectDeviceRequest(requestID) {
|
|
2975
2950
|
await this.http.post(this.prefixes.HTTP +
|
|
@@ -2996,22 +2971,18 @@ export class Client {
|
|
|
2996
2971
|
}
|
|
2997
2972
|
async retrieveEmojiByID(emojiID) {
|
|
2998
2973
|
const res = await this.http.get(this.getHost() + "/emoji/" + emojiID + "/details");
|
|
2999
|
-
|
|
3000
|
-
return null;
|
|
3001
|
-
}
|
|
3002
|
-
return decodeAxios(EmojiCodec, res.data);
|
|
2974
|
+
return decodeHttpResponse(EmojiCodec, res.data);
|
|
3003
2975
|
}
|
|
3004
2976
|
async retrieveEmojiList(serverID) {
|
|
3005
2977
|
const res = await this.http.get(this.getHost() + "/server/" + serverID + "/emoji");
|
|
3006
|
-
return
|
|
2978
|
+
return decodeHttpResponse(EmojiArrayCodec, res.data);
|
|
3007
2979
|
}
|
|
3008
2980
|
async retrieveFile(fileID, key) {
|
|
3009
2981
|
const detailsRes = await this.http.get(this.getHost() + "/file/" + fileID + "/details");
|
|
3010
|
-
const details =
|
|
2982
|
+
const details = decodeHttpResponse(FileSQLCodec, detailsRes.data);
|
|
3011
2983
|
const res = await this.http.get(this.getHost() + "/file/" + fileID, {
|
|
3012
2984
|
onDownloadProgress: (progressEvent) => {
|
|
3013
|
-
const percentCompleted = Math.round((progressEvent.loaded * 100) /
|
|
3014
|
-
(progressEvent.total ?? 1));
|
|
2985
|
+
const percentCompleted = Math.round((progressEvent.loaded * 100) / (progressEvent.total ?? 1));
|
|
3015
2986
|
const { loaded, total = 0 } = progressEvent;
|
|
3016
2987
|
const progress = {
|
|
3017
2988
|
direction: "download",
|
|
@@ -3035,11 +3006,11 @@ export class Client {
|
|
|
3035
3006
|
}
|
|
3036
3007
|
async retrieveInvites(serverID) {
|
|
3037
3008
|
const res = await this.http.get(this.getHost() + "/server/" + serverID + "/invites");
|
|
3038
|
-
return
|
|
3009
|
+
return decodeHttpResponse(InviteArrayCodec, res.data);
|
|
3039
3010
|
}
|
|
3040
3011
|
async retrieveKeyBundle(deviceID) {
|
|
3041
3012
|
const res = await this.http.post(this.getHost() + "/device/" + deviceID + "/keyBundle");
|
|
3042
|
-
return
|
|
3013
|
+
return decodeHttpResponse(KeyBundleCodec, res.data);
|
|
3043
3014
|
}
|
|
3044
3015
|
async retrieveOrCreateDevice() {
|
|
3045
3016
|
let device;
|
|
@@ -3048,10 +3019,10 @@ export class Client {
|
|
|
3048
3019
|
this.host +
|
|
3049
3020
|
"/device/" +
|
|
3050
3021
|
XUtils.encodeHex(this.signKeys.publicKey));
|
|
3051
|
-
device =
|
|
3022
|
+
device = decodeHttpResponse(DeviceCodec, res.data);
|
|
3052
3023
|
}
|
|
3053
3024
|
catch (err) {
|
|
3054
|
-
if (
|
|
3025
|
+
if (isHttpError(err) && err.response?.status === 404) {
|
|
3055
3026
|
await this.database.purgeKeyData();
|
|
3056
3027
|
await this.populateKeyRing();
|
|
3057
3028
|
const newDevice = await this.registerDevice();
|
|
@@ -3634,7 +3605,7 @@ export class Client {
|
|
|
3634
3605
|
this.emitter.emit("fileProgress", progress);
|
|
3635
3606
|
},
|
|
3636
3607
|
});
|
|
3637
|
-
return
|
|
3608
|
+
return decodeHttpResponse(EmojiCodec, res.data);
|
|
3638
3609
|
}
|
|
3639
3610
|
catch (_err) {
|
|
3640
3611
|
return null;
|
|
@@ -3646,7 +3617,7 @@ export class Client {
|
|
|
3646
3617
|
};
|
|
3647
3618
|
try {
|
|
3648
3619
|
const res = await this.http.post(this.getHost() + "/emoji/" + serverID + "/json", msgpack.encode(payload), { headers: { "Content-Type": "application/msgpack" } });
|
|
3649
|
-
return
|
|
3620
|
+
return decodeHttpResponse(EmojiCodec, res.data);
|
|
3650
3621
|
}
|
|
3651
3622
|
catch (_err) {
|
|
3652
3623
|
return null;
|