@vex-chat/libvex 6.6.1 → 6.6.3
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 +88 -105
- 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 +2 -3
- package/src/Client.ts +127 -139
- package/src/__tests__/harness/shared-suite.ts +33 -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/src/Client.ts
CHANGED
|
@@ -39,7 +39,6 @@ import type {
|
|
|
39
39
|
SessionSQL,
|
|
40
40
|
} from "@vex-chat/types";
|
|
41
41
|
import type { ClientMessage } from "@vex-chat/types";
|
|
42
|
-
import type { AxiosInstance } from "axios";
|
|
43
42
|
|
|
44
43
|
import {
|
|
45
44
|
type CryptoProfile,
|
|
@@ -76,11 +75,16 @@ import {
|
|
|
76
75
|
WSMessageSchema,
|
|
77
76
|
} from "@vex-chat/types";
|
|
78
77
|
|
|
79
|
-
import axios, { type AxiosError, isAxiosError } from "axios";
|
|
80
78
|
import { EventEmitter } from "eventemitter3";
|
|
81
79
|
import * as uuid from "uuid";
|
|
82
80
|
import { z } from "zod/v4";
|
|
83
81
|
|
|
82
|
+
import {
|
|
83
|
+
createFetchHttpClient,
|
|
84
|
+
type FetchHttpClient,
|
|
85
|
+
type HttpError,
|
|
86
|
+
isHttpError,
|
|
87
|
+
} from "./http.js";
|
|
84
88
|
import {
|
|
85
89
|
clampLocalMessageRetentionDays,
|
|
86
90
|
formatVexRetentionEnvelope,
|
|
@@ -317,7 +321,7 @@ import {
|
|
|
317
321
|
ChannelArrayCodec,
|
|
318
322
|
ChannelCodec,
|
|
319
323
|
ConnectResponseCodec,
|
|
320
|
-
|
|
324
|
+
decodeHttpResponse,
|
|
321
325
|
DeviceArrayCodec,
|
|
322
326
|
DeviceChallengeCodec,
|
|
323
327
|
DeviceCodec,
|
|
@@ -666,6 +670,20 @@ export interface NotificationSubscription {
|
|
|
666
670
|
userID: string;
|
|
667
671
|
}
|
|
668
672
|
|
|
673
|
+
const NotificationSubscriptionSchema: z.ZodType<NotificationSubscription> =
|
|
674
|
+
z.object({
|
|
675
|
+
channel: z.literal("expo"),
|
|
676
|
+
createdAt: z.string(),
|
|
677
|
+
deviceID: z.string(),
|
|
678
|
+
enabled: z.boolean(),
|
|
679
|
+
events: z.array(z.string()),
|
|
680
|
+
platform: z.string().nullable(),
|
|
681
|
+
subscriptionID: z.string(),
|
|
682
|
+
token: z.string(),
|
|
683
|
+
updatedAt: z.string(),
|
|
684
|
+
userID: z.string(),
|
|
685
|
+
});
|
|
686
|
+
|
|
669
687
|
export interface NotificationSubscriptionInput {
|
|
670
688
|
channel: "expo";
|
|
671
689
|
events?: string[];
|
|
@@ -1032,7 +1050,7 @@ export interface Users {
|
|
|
1032
1050
|
/**
|
|
1033
1051
|
* Looks up a user by user ID, username, or signing key.
|
|
1034
1052
|
*/
|
|
1035
|
-
retrieve: (userID: string) => Promise<[null | User,
|
|
1053
|
+
retrieve: (userID: string) => Promise<[null | User, HttpError | null]>;
|
|
1036
1054
|
}
|
|
1037
1055
|
|
|
1038
1056
|
/**
|
|
@@ -1439,8 +1457,8 @@ export class Client {
|
|
|
1439
1457
|
private firstMailFetch = true;
|
|
1440
1458
|
private readonly forwarded = new Set<string>();
|
|
1441
1459
|
private readonly host: string;
|
|
1442
|
-
private readonly http:
|
|
1443
|
-
/** Cancels in-flight
|
|
1460
|
+
private readonly http: FetchHttpClient;
|
|
1461
|
+
/** Cancels in-flight HTTP work on `close()` so `postAuth`/`getMail` cannot hang forever. */
|
|
1444
1462
|
private readonly httpAbortController = new AbortController();
|
|
1445
1463
|
private readonly idKeys: KeyPair | null;
|
|
1446
1464
|
private isAlive: boolean = true;
|
|
@@ -1451,14 +1469,6 @@ export class Client {
|
|
|
1451
1469
|
private readonly mailInterval?: NodeJS.Timeout;
|
|
1452
1470
|
|
|
1453
1471
|
private manuallyClosing: boolean = false;
|
|
1454
|
-
/**
|
|
1455
|
-
* Node-only: per-client HTTP(S) agents (see `init()` + `storage/node/http-agents`).
|
|
1456
|
-
* Dropped on `close()` so idle keep-alive sockets do not keep the process alive.
|
|
1457
|
-
*/
|
|
1458
|
-
private nodeHttpAgents?: {
|
|
1459
|
-
http: { destroy(): void };
|
|
1460
|
-
https: { destroy(): void };
|
|
1461
|
-
};
|
|
1462
1472
|
/* Retrieves the userID with the user identifier.
|
|
1463
1473
|
user identifier is checked for userID, then signkey,
|
|
1464
1474
|
and finally falls back to username. */
|
|
@@ -1544,7 +1554,7 @@ export class Client {
|
|
|
1544
1554
|
void this.close(true);
|
|
1545
1555
|
});
|
|
1546
1556
|
|
|
1547
|
-
this.http =
|
|
1557
|
+
this.http = createFetchHttpClient({
|
|
1548
1558
|
responseType: "arraybuffer",
|
|
1549
1559
|
signal: this.httpAbortController.signal,
|
|
1550
1560
|
});
|
|
@@ -1754,32 +1764,6 @@ export class Client {
|
|
|
1754
1764
|
}
|
|
1755
1765
|
}
|
|
1756
1766
|
|
|
1757
|
-
/**
|
|
1758
|
-
* True when running under Node (has `process.versions`).
|
|
1759
|
-
* Uses indirect lookup so the bare `process` global never appears in
|
|
1760
|
-
* source that the platform-guard plugin scans.
|
|
1761
|
-
*/
|
|
1762
|
-
private static isNodeRuntime(): boolean {
|
|
1763
|
-
try {
|
|
1764
|
-
const g = Object.getOwnPropertyDescriptor(
|
|
1765
|
-
globalThis,
|
|
1766
|
-
"\u0070rocess",
|
|
1767
|
-
);
|
|
1768
|
-
if (!g) return false;
|
|
1769
|
-
const proc: unknown =
|
|
1770
|
-
typeof g.get === "function" ? g.get() : g.value;
|
|
1771
|
-
if (typeof proc !== "object" || proc === null) {
|
|
1772
|
-
return false;
|
|
1773
|
-
}
|
|
1774
|
-
return (
|
|
1775
|
-
"versions" in proc &&
|
|
1776
|
-
typeof (proc as { versions?: unknown }).versions === "object"
|
|
1777
|
-
);
|
|
1778
|
-
} catch {
|
|
1779
|
-
return false;
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1782
|
-
|
|
1783
1767
|
/**
|
|
1784
1768
|
* Closes the client — disconnects the WebSocket, shuts down storage,
|
|
1785
1769
|
* and emits `closed` unless `muteEvent` is `true`.
|
|
@@ -1797,12 +1781,6 @@ export class Client {
|
|
|
1797
1781
|
this.socket.close();
|
|
1798
1782
|
await this.database.close();
|
|
1799
1783
|
|
|
1800
|
-
if (this.nodeHttpAgents) {
|
|
1801
|
-
this.nodeHttpAgents.http.destroy();
|
|
1802
|
-
this.nodeHttpAgents.https.destroy();
|
|
1803
|
-
delete this.nodeHttpAgents;
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
1784
|
if (this.pingInterval) {
|
|
1807
1785
|
clearInterval(this.pingInterval);
|
|
1808
1786
|
}
|
|
@@ -1855,7 +1833,10 @@ export class Client {
|
|
|
1855
1833
|
msgpack.encode({ signed: signedAsync }),
|
|
1856
1834
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
1857
1835
|
);
|
|
1858
|
-
const { deviceToken } =
|
|
1836
|
+
const { deviceToken } = decodeHttpResponse(
|
|
1837
|
+
ConnectResponseCodec,
|
|
1838
|
+
res.data,
|
|
1839
|
+
);
|
|
1859
1840
|
this.http.defaults.headers.common["X-Device-Token"] = deviceToken;
|
|
1860
1841
|
|
|
1861
1842
|
this.autoReconnectEnabled = true;
|
|
@@ -1937,14 +1918,17 @@ export class Client {
|
|
|
1937
1918
|
headers: { "Content-Type": "application/msgpack" },
|
|
1938
1919
|
},
|
|
1939
1920
|
);
|
|
1940
|
-
const { token, user } =
|
|
1921
|
+
const { token, user } = decodeHttpResponse(
|
|
1922
|
+
AuthResponseCodec,
|
|
1923
|
+
res.data,
|
|
1924
|
+
);
|
|
1941
1925
|
|
|
1942
1926
|
this.setUser(user);
|
|
1943
1927
|
this.token = token;
|
|
1944
1928
|
this.http.defaults.headers.common.Authorization = `Bearer ${token}`;
|
|
1945
1929
|
return { ok: true };
|
|
1946
1930
|
} catch (err: unknown) {
|
|
1947
|
-
if (
|
|
1931
|
+
if (isHttpError(err) && err.response) {
|
|
1948
1932
|
return {
|
|
1949
1933
|
error: spireErrorBodyMessage(err.response.data),
|
|
1950
1934
|
ok: false,
|
|
@@ -1979,7 +1963,7 @@ export class Client {
|
|
|
1979
1963
|
}),
|
|
1980
1964
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
1981
1965
|
);
|
|
1982
|
-
const { challenge, challengeID } =
|
|
1966
|
+
const { challenge, challengeID } = decodeHttpResponse(
|
|
1983
1967
|
DeviceChallengeCodec,
|
|
1984
1968
|
challengeRes.data,
|
|
1985
1969
|
);
|
|
@@ -1996,7 +1980,7 @@ export class Client {
|
|
|
1996
1980
|
msgpack.encode({ challengeID, signed }),
|
|
1997
1981
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
1998
1982
|
);
|
|
1999
|
-
const { token, user } =
|
|
1983
|
+
const { token, user } = decodeHttpResponse(
|
|
2000
1984
|
AuthResponseCodec,
|
|
2001
1985
|
verifyRes.data,
|
|
2002
1986
|
);
|
|
@@ -2150,7 +2134,7 @@ export class Client {
|
|
|
2150
2134
|
let didDecodeRegisterResponse = false;
|
|
2151
2135
|
let pendingApproval: null | PendingDeviceRegistration = null;
|
|
2152
2136
|
try {
|
|
2153
|
-
const { device, token, user } =
|
|
2137
|
+
const { device, token, user } = decodeHttpResponse(
|
|
2154
2138
|
RegisterResponseCodec,
|
|
2155
2139
|
res.data,
|
|
2156
2140
|
);
|
|
@@ -2165,7 +2149,7 @@ export class Client {
|
|
|
2165
2149
|
|
|
2166
2150
|
if (!didDecodeRegisterResponse) {
|
|
2167
2151
|
try {
|
|
2168
|
-
pendingApproval =
|
|
2152
|
+
pendingApproval = decodeHttpResponse(
|
|
2169
2153
|
RegisterPendingApprovalCodec,
|
|
2170
2154
|
res.data,
|
|
2171
2155
|
);
|
|
@@ -2186,7 +2170,7 @@ export class Client {
|
|
|
2186
2170
|
}),
|
|
2187
2171
|
];
|
|
2188
2172
|
}
|
|
2189
|
-
const legacyUser =
|
|
2173
|
+
const legacyUser = decodeHttpResponse(UserCodec, res.data);
|
|
2190
2174
|
this.setUser(legacyUser);
|
|
2191
2175
|
|
|
2192
2176
|
// Legacy servers require /auth after /register to get a JWT.
|
|
@@ -2206,7 +2190,7 @@ export class Client {
|
|
|
2206
2190
|
}
|
|
2207
2191
|
return [this.getUser(), null];
|
|
2208
2192
|
} catch (err: unknown) {
|
|
2209
|
-
if (
|
|
2193
|
+
if (isHttpError(err) && err.response) {
|
|
2210
2194
|
return [
|
|
2211
2195
|
null,
|
|
2212
2196
|
new Error(spireErrorBodyMessage(err.response.data)),
|
|
@@ -2246,7 +2230,7 @@ export class Client {
|
|
|
2246
2230
|
public async subscribeNotifications(
|
|
2247
2231
|
input: NotificationSubscriptionInput,
|
|
2248
2232
|
): Promise<NotificationSubscription> {
|
|
2249
|
-
const response = await this.http.post
|
|
2233
|
+
const response = await this.http.post(
|
|
2250
2234
|
this.getHost() +
|
|
2251
2235
|
"/device/" +
|
|
2252
2236
|
this.getDevice().deviceID +
|
|
@@ -2254,7 +2238,7 @@ export class Client {
|
|
|
2254
2238
|
input,
|
|
2255
2239
|
{ responseType: "json" },
|
|
2256
2240
|
);
|
|
2257
|
-
return response.data;
|
|
2241
|
+
return NotificationSubscriptionSchema.parse(response.data);
|
|
2258
2242
|
}
|
|
2259
2243
|
|
|
2260
2244
|
/**
|
|
@@ -2309,7 +2293,7 @@ export class Client {
|
|
|
2309
2293
|
}> {
|
|
2310
2294
|
const res = await this.http.post(this.getHost() + "/whoami");
|
|
2311
2295
|
|
|
2312
|
-
const whoami =
|
|
2296
|
+
const whoami = decodeHttpResponse(WhoamiCodec, res.data);
|
|
2313
2297
|
return whoami;
|
|
2314
2298
|
}
|
|
2315
2299
|
|
|
@@ -2380,7 +2364,7 @@ export class Client {
|
|
|
2380
2364
|
msgpack.encode({ signed }),
|
|
2381
2365
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
2382
2366
|
);
|
|
2383
|
-
return
|
|
2367
|
+
return decodeHttpResponse(DeviceCodec, response.data);
|
|
2384
2368
|
}
|
|
2385
2369
|
|
|
2386
2370
|
private async beginPasskeyAuthentication(username: string): Promise<{
|
|
@@ -2393,7 +2377,7 @@ export class Client {
|
|
|
2393
2377
|
msgpack.encode({ username }),
|
|
2394
2378
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
2395
2379
|
);
|
|
2396
|
-
return
|
|
2380
|
+
return decodeHttpResponse(PasskeyOptionsCodec, response.data);
|
|
2397
2381
|
}
|
|
2398
2382
|
|
|
2399
2383
|
private async beginPasskeyRegistration(name: string): Promise<{
|
|
@@ -2407,7 +2391,7 @@ export class Client {
|
|
|
2407
2391
|
msgpack.encode({ name }),
|
|
2408
2392
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
2409
2393
|
);
|
|
2410
|
-
return
|
|
2394
|
+
return decodeHttpResponse(PasskeyOptionsCodec, response.data);
|
|
2411
2395
|
}
|
|
2412
2396
|
|
|
2413
2397
|
private censorPreKey(preKey: PreKeysSQL): PreKeysWS {
|
|
@@ -2432,7 +2416,7 @@ export class Client {
|
|
|
2432
2416
|
msgpack.encode(body),
|
|
2433
2417
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
2434
2418
|
);
|
|
2435
|
-
return
|
|
2419
|
+
return decodeHttpResponse(ChannelCodec, res.data);
|
|
2436
2420
|
}
|
|
2437
2421
|
|
|
2438
2422
|
// returns the file details and the encryption key
|
|
@@ -2449,7 +2433,20 @@ export class Client {
|
|
|
2449
2433
|
fileKey,
|
|
2450
2434
|
);
|
|
2451
2435
|
|
|
2452
|
-
|
|
2436
|
+
const canUseMultipart =
|
|
2437
|
+
typeof FormData !== "undefined" &&
|
|
2438
|
+
(() => {
|
|
2439
|
+
try {
|
|
2440
|
+
// React Native/Hermes can expose Blob/FormData but
|
|
2441
|
+
// reject ArrayBufferView-backed blobs at runtime.
|
|
2442
|
+
void new Blob([new Uint8Array([1, 2, 3])]);
|
|
2443
|
+
return true;
|
|
2444
|
+
} catch {
|
|
2445
|
+
return false;
|
|
2446
|
+
}
|
|
2447
|
+
})();
|
|
2448
|
+
|
|
2449
|
+
if (canUseMultipart) {
|
|
2453
2450
|
const fpayload = new FormData();
|
|
2454
2451
|
fpayload.set("owner", this.getDevice().deviceID);
|
|
2455
2452
|
fpayload.set("nonce", XUtils.encodeHex(nonce));
|
|
@@ -2477,7 +2474,10 @@ export class Client {
|
|
|
2477
2474
|
},
|
|
2478
2475
|
},
|
|
2479
2476
|
);
|
|
2480
|
-
const fcreatedFile =
|
|
2477
|
+
const fcreatedFile = decodeHttpResponse(
|
|
2478
|
+
FileSQLCodec,
|
|
2479
|
+
fres.data,
|
|
2480
|
+
);
|
|
2481
2481
|
|
|
2482
2482
|
return [fcreatedFile, XUtils.encodeHex(fileKey)];
|
|
2483
2483
|
}
|
|
@@ -2496,7 +2496,7 @@ export class Client {
|
|
|
2496
2496
|
msgpack.encode(payload),
|
|
2497
2497
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
2498
2498
|
);
|
|
2499
|
-
const createdFile =
|
|
2499
|
+
const createdFile = decodeHttpResponse(FileSQLCodec, res.data);
|
|
2500
2500
|
|
|
2501
2501
|
return [createdFile, XUtils.encodeHex(fileKey)];
|
|
2502
2502
|
});
|
|
@@ -2514,7 +2514,7 @@ export class Client {
|
|
|
2514
2514
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
2515
2515
|
);
|
|
2516
2516
|
|
|
2517
|
-
return
|
|
2517
|
+
return decodeHttpResponse(InviteCodec, res.data);
|
|
2518
2518
|
}
|
|
2519
2519
|
|
|
2520
2520
|
private async createPreKey(): Promise<UnsavedPreKey> {
|
|
@@ -2533,7 +2533,7 @@ export class Client {
|
|
|
2533
2533
|
const res = await this.http.post(
|
|
2534
2534
|
this.getHost() + "/server/" + globalThis.btoa(name),
|
|
2535
2535
|
);
|
|
2536
|
-
return
|
|
2536
|
+
return decodeHttpResponse(ServerCodec, res.data);
|
|
2537
2537
|
}
|
|
2538
2538
|
|
|
2539
2539
|
private async createSession(
|
|
@@ -2794,7 +2794,7 @@ export class Client {
|
|
|
2794
2794
|
await this.http.delete(this.getHost() + "/server/" + serverID);
|
|
2795
2795
|
}
|
|
2796
2796
|
private deviceListFailureDetail(err: unknown): string {
|
|
2797
|
-
if (!
|
|
2797
|
+
if (!isHttpError(err)) {
|
|
2798
2798
|
return "";
|
|
2799
2799
|
}
|
|
2800
2800
|
const st = err.response?.status;
|
|
@@ -2819,12 +2819,12 @@ export class Client {
|
|
|
2819
2819
|
serverID +
|
|
2820
2820
|
"/permissions",
|
|
2821
2821
|
);
|
|
2822
|
-
return
|
|
2822
|
+
return decodeHttpResponse(PermissionArrayCodec, res.data);
|
|
2823
2823
|
}
|
|
2824
2824
|
|
|
2825
2825
|
private async fetchUser(
|
|
2826
2826
|
userIdentifier: string,
|
|
2827
|
-
): Promise<[null | User,
|
|
2827
|
+
): Promise<[null | User, HttpError | null]> {
|
|
2828
2828
|
// Usernames are case-insensitive at the protocol level, so
|
|
2829
2829
|
// canonicalize the *cache key* to lowercase for username
|
|
2830
2830
|
// lookups. Without this we'd accumulate duplicate
|
|
@@ -2851,18 +2851,18 @@ export class Client {
|
|
|
2851
2851
|
const res = await this.http.get(
|
|
2852
2852
|
this.getHost() + "/user/" + cacheKey,
|
|
2853
2853
|
);
|
|
2854
|
-
const userRecord =
|
|
2854
|
+
const userRecord = decodeHttpResponse(UserCodec, res.data);
|
|
2855
2855
|
this.userRecords[cacheKey] = userRecord;
|
|
2856
2856
|
this.notFoundUsers.delete(cacheKey);
|
|
2857
2857
|
return [userRecord, null];
|
|
2858
2858
|
} catch (err: unknown) {
|
|
2859
|
-
if (
|
|
2859
|
+
if (isHttpError(err) && err.response?.status === 404) {
|
|
2860
2860
|
// Definitive: user doesn't exist — cache and don't retry
|
|
2861
2861
|
this.notFoundUsers.set(cacheKey, Date.now());
|
|
2862
2862
|
return [null, err];
|
|
2863
2863
|
}
|
|
2864
2864
|
// Transient (5xx, network error) — don't cache, caller can retry
|
|
2865
|
-
return [null,
|
|
2865
|
+
return [null, isHttpError(err) ? err : null];
|
|
2866
2866
|
}
|
|
2867
2867
|
}
|
|
2868
2868
|
|
|
@@ -2873,7 +2873,7 @@ export class Client {
|
|
|
2873
2873
|
const res = await this.http.get(
|
|
2874
2874
|
this.getHost() + "/user/" + userID + "/devices",
|
|
2875
2875
|
);
|
|
2876
|
-
const devices =
|
|
2876
|
+
const devices = decodeHttpResponse(DeviceArrayCodec, res.data);
|
|
2877
2877
|
for (const device of devices) {
|
|
2878
2878
|
this.deviceRecords[device.deviceID] = device;
|
|
2879
2879
|
}
|
|
@@ -2936,7 +2936,7 @@ export class Client {
|
|
|
2936
2936
|
msgpack.encode(args),
|
|
2937
2937
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
2938
2938
|
);
|
|
2939
|
-
const decoded =
|
|
2939
|
+
const decoded = decodeHttpResponse(
|
|
2940
2940
|
PasskeyAuthFinishResponseCodec,
|
|
2941
2941
|
response.data,
|
|
2942
2942
|
);
|
|
@@ -2957,7 +2957,7 @@ export class Client {
|
|
|
2957
2957
|
msgpack.encode(args),
|
|
2958
2958
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
2959
2959
|
);
|
|
2960
|
-
return
|
|
2960
|
+
return decodeHttpResponse(PasskeyCodec, response.data);
|
|
2961
2961
|
}
|
|
2962
2962
|
|
|
2963
2963
|
private async forward(message: Message) {
|
|
@@ -3007,7 +3007,7 @@ export class Client {
|
|
|
3007
3007
|
const res = await this.http.get(
|
|
3008
3008
|
this.getHost() + "/channel/" + channelID,
|
|
3009
3009
|
);
|
|
3010
|
-
return
|
|
3010
|
+
return decodeHttpResponse(ChannelCodec, res.data);
|
|
3011
3011
|
} catch (_err: unknown) {
|
|
3012
3012
|
return null;
|
|
3013
3013
|
}
|
|
@@ -3017,7 +3017,7 @@ export class Client {
|
|
|
3017
3017
|
const res = await this.http.get(
|
|
3018
3018
|
this.getHost() + "/server/" + serverID + "/channels",
|
|
3019
3019
|
);
|
|
3020
|
-
return
|
|
3020
|
+
return decodeHttpResponse(ChannelArrayCodec, res.data);
|
|
3021
3021
|
}
|
|
3022
3022
|
|
|
3023
3023
|
private getDevice(): Device {
|
|
@@ -3043,7 +3043,7 @@ export class Client {
|
|
|
3043
3043
|
const res = await this.http.get(
|
|
3044
3044
|
this.getHost() + "/device/" + deviceID,
|
|
3045
3045
|
);
|
|
3046
|
-
const fetchedDevice =
|
|
3046
|
+
const fetchedDevice = decodeHttpResponse(DeviceCodec, res.data);
|
|
3047
3047
|
this.deviceRecords[deviceID] = fetchedDevice;
|
|
3048
3048
|
await this.database.saveDevice(fetchedDevice);
|
|
3049
3049
|
return fetchedDevice;
|
|
@@ -3064,9 +3064,9 @@ export class Client {
|
|
|
3064
3064
|
"/devices/requests/" +
|
|
3065
3065
|
requestID,
|
|
3066
3066
|
);
|
|
3067
|
-
return
|
|
3067
|
+
return decodeHttpResponse(PendingDeviceRequestCodec, response.data);
|
|
3068
3068
|
} catch (err: unknown) {
|
|
3069
|
-
if (
|
|
3069
|
+
if (isHttpError(err) && err.response?.status === 404) {
|
|
3070
3070
|
return null;
|
|
3071
3071
|
}
|
|
3072
3072
|
throw err;
|
|
@@ -3112,7 +3112,7 @@ export class Client {
|
|
|
3112
3112
|
}
|
|
3113
3113
|
|
|
3114
3114
|
try {
|
|
3115
|
-
const res = await this.http.post
|
|
3115
|
+
const res = await this.http.post(
|
|
3116
3116
|
this.getHost() +
|
|
3117
3117
|
"/device/" +
|
|
3118
3118
|
this.getDevice().deviceID +
|
|
@@ -3186,7 +3186,7 @@ export class Client {
|
|
|
3186
3186
|
msgpack.encode(userIDs),
|
|
3187
3187
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
3188
3188
|
);
|
|
3189
|
-
const devices =
|
|
3189
|
+
const devices = decodeHttpResponse(DeviceArrayCodec, res.data);
|
|
3190
3190
|
for (const device of devices) {
|
|
3191
3191
|
this.deviceRecords[device.deviceID] = device;
|
|
3192
3192
|
}
|
|
@@ -3204,7 +3204,7 @@ export class Client {
|
|
|
3204
3204
|
this.getDevice().deviceID +
|
|
3205
3205
|
"/otk/count",
|
|
3206
3206
|
);
|
|
3207
|
-
return
|
|
3207
|
+
return decodeHttpResponse(OtkCountCodec, res.data).count;
|
|
3208
3208
|
}
|
|
3209
3209
|
|
|
3210
3210
|
/**
|
|
@@ -3216,7 +3216,7 @@ export class Client {
|
|
|
3216
3216
|
const res = await this.http.get(
|
|
3217
3217
|
this.getHost() + "/user/" + this.getUser().userID + "/permissions",
|
|
3218
3218
|
);
|
|
3219
|
-
return
|
|
3219
|
+
return decodeHttpResponse(PermissionArrayCodec, res.data);
|
|
3220
3220
|
}
|
|
3221
3221
|
|
|
3222
3222
|
private async getServerByID(serverID: string): Promise<null | Server> {
|
|
@@ -3224,7 +3224,7 @@ export class Client {
|
|
|
3224
3224
|
const res = await this.http.get(
|
|
3225
3225
|
this.getHost() + "/server/" + serverID,
|
|
3226
3226
|
);
|
|
3227
|
-
return
|
|
3227
|
+
return decodeHttpResponse(ServerCodec, res.data);
|
|
3228
3228
|
} catch (_err: unknown) {
|
|
3229
3229
|
return null;
|
|
3230
3230
|
}
|
|
@@ -3237,14 +3237,14 @@ export class Client {
|
|
|
3237
3237
|
this.getUser().userID +
|
|
3238
3238
|
"/servers/bootstrap",
|
|
3239
3239
|
);
|
|
3240
|
-
return
|
|
3240
|
+
return decodeHttpResponse(ServerChannelBootstrapCodec, res.data);
|
|
3241
3241
|
}
|
|
3242
3242
|
|
|
3243
3243
|
private async getServerList(): Promise<Server[]> {
|
|
3244
3244
|
const res = await this.http.get(
|
|
3245
3245
|
this.getHost() + "/user/" + this.getUser().userID + "/servers",
|
|
3246
3246
|
);
|
|
3247
|
-
return
|
|
3247
|
+
return decodeHttpResponse(ServerArrayCodec, res.data);
|
|
3248
3248
|
}
|
|
3249
3249
|
|
|
3250
3250
|
private async getSessionByPubkey(publicKey: Uint8Array) {
|
|
@@ -3277,7 +3277,7 @@ export class Client {
|
|
|
3277
3277
|
const res = await this.http.get(this.getHost() + "/token/" + type, {
|
|
3278
3278
|
responseType: "arraybuffer",
|
|
3279
3279
|
});
|
|
3280
|
-
return
|
|
3280
|
+
return decodeHttpResponse(ActionTokenCodec, res.data);
|
|
3281
3281
|
} catch {
|
|
3282
3282
|
return null;
|
|
3283
3283
|
}
|
|
@@ -3312,7 +3312,7 @@ export class Client {
|
|
|
3312
3312
|
const res = await this.http.post(
|
|
3313
3313
|
this.getHost() + "/userList/" + channelID,
|
|
3314
3314
|
);
|
|
3315
|
-
return
|
|
3315
|
+
return decodeHttpResponse(UserArrayCodec, res.data);
|
|
3316
3316
|
}
|
|
3317
3317
|
|
|
3318
3318
|
private async handleNotify(msg: NotifyMsg) {
|
|
@@ -3385,14 +3385,6 @@ export class Client {
|
|
|
3385
3385
|
}
|
|
3386
3386
|
this.hasInit = true;
|
|
3387
3387
|
|
|
3388
|
-
if (Client.isNodeRuntime()) {
|
|
3389
|
-
const { attachNodeAgentsToAxios, createNodeHttpAgents } =
|
|
3390
|
-
await import("./storage/node/http-agents.js");
|
|
3391
|
-
const agents = createNodeHttpAgents();
|
|
3392
|
-
this.nodeHttpAgents = agents;
|
|
3393
|
-
attachNodeAgentsToAxios(this.http, agents);
|
|
3394
|
-
}
|
|
3395
|
-
|
|
3396
3388
|
await this.populateKeyRing();
|
|
3397
3389
|
this.emitter.on("message", this.onInternalMessage);
|
|
3398
3390
|
void this.runLocalRetentionPurge();
|
|
@@ -3546,7 +3538,10 @@ export class Client {
|
|
|
3546
3538
|
this.getUser().userID +
|
|
3547
3539
|
"/devices/requests",
|
|
3548
3540
|
);
|
|
3549
|
-
return
|
|
3541
|
+
return decodeHttpResponse(
|
|
3542
|
+
PendingDeviceRequestArrayCodec,
|
|
3543
|
+
response.data,
|
|
3544
|
+
);
|
|
3550
3545
|
}
|
|
3551
3546
|
|
|
3552
3547
|
/**
|
|
@@ -3560,7 +3555,7 @@ export class Client {
|
|
|
3560
3555
|
const res = await this.http.get(
|
|
3561
3556
|
this.getHost() + "/user/" + userID + "/devices",
|
|
3562
3557
|
);
|
|
3563
|
-
return
|
|
3558
|
+
return decodeHttpResponse(DeviceArrayCodec, res.data);
|
|
3564
3559
|
}
|
|
3565
3560
|
|
|
3566
3561
|
private async listPasskeys(): Promise<Passkey[]> {
|
|
@@ -3568,7 +3563,7 @@ export class Client {
|
|
|
3568
3563
|
const response = await this.http.get(
|
|
3569
3564
|
this.getHost() + "/user/" + userID + "/passkeys",
|
|
3570
3565
|
);
|
|
3571
|
-
return
|
|
3566
|
+
return decodeHttpResponse(PasskeyArrayCodec, response.data);
|
|
3572
3567
|
}
|
|
3573
3568
|
|
|
3574
3569
|
private async markSessionVerified(sessionID: string) {
|
|
@@ -3634,7 +3629,7 @@ export class Client {
|
|
|
3634
3629
|
requestID +
|
|
3635
3630
|
"/approve",
|
|
3636
3631
|
);
|
|
3637
|
-
return
|
|
3632
|
+
return decodeHttpResponse(DeviceCodec, response.data);
|
|
3638
3633
|
}
|
|
3639
3634
|
|
|
3640
3635
|
private async passkeyDeleteDevice(deviceID: string): Promise<void> {
|
|
@@ -3649,7 +3644,7 @@ export class Client {
|
|
|
3649
3644
|
const response = await this.http.get(
|
|
3650
3645
|
this.getHost() + "/user/" + userID + "/passkey/devices",
|
|
3651
3646
|
);
|
|
3652
|
-
return
|
|
3647
|
+
return decodeHttpResponse(DeviceArrayCodec, response.data);
|
|
3653
3648
|
}
|
|
3654
3649
|
|
|
3655
3650
|
private async passkeyRejectDeviceRequest(requestID: string): Promise<void> {
|
|
@@ -3716,9 +3711,9 @@ export class Client {
|
|
|
3716
3711
|
msgpack.encode({ signed }),
|
|
3717
3712
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
3718
3713
|
);
|
|
3719
|
-
return
|
|
3714
|
+
return decodeHttpResponse(PendingDeviceRequestCodec, response.data);
|
|
3720
3715
|
} catch (err: unknown) {
|
|
3721
|
-
if (
|
|
3716
|
+
if (isHttpError(err) && err.response?.status === 404) {
|
|
3722
3717
|
return null;
|
|
3723
3718
|
}
|
|
3724
3719
|
throw err;
|
|
@@ -4548,7 +4543,7 @@ export class Client {
|
|
|
4548
4543
|
const res = await this.http.patch(
|
|
4549
4544
|
this.getHost() + "/invite/" + inviteID,
|
|
4550
4545
|
);
|
|
4551
|
-
return
|
|
4546
|
+
return decodeHttpResponse(PermissionCodec, res.data);
|
|
4552
4547
|
}
|
|
4553
4548
|
|
|
4554
4549
|
private registerDecryptFailure(mail: MailWS): number {
|
|
@@ -4603,7 +4598,7 @@ export class Client {
|
|
|
4603
4598
|
msgpack.encode(devMsg),
|
|
4604
4599
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
4605
4600
|
);
|
|
4606
|
-
return
|
|
4601
|
+
return decodeHttpResponse(DeviceRegistrationResultCodec, res.data);
|
|
4607
4602
|
}
|
|
4608
4603
|
|
|
4609
4604
|
private async rejectDeviceRequest(requestID: string): Promise<void> {
|
|
@@ -4640,17 +4635,14 @@ export class Client {
|
|
|
4640
4635
|
const res = await this.http.get(
|
|
4641
4636
|
this.getHost() + "/emoji/" + emojiID + "/details",
|
|
4642
4637
|
);
|
|
4643
|
-
|
|
4644
|
-
return null;
|
|
4645
|
-
}
|
|
4646
|
-
return decodeAxios(EmojiCodec, res.data);
|
|
4638
|
+
return decodeHttpResponse(EmojiCodec, res.data);
|
|
4647
4639
|
}
|
|
4648
4640
|
|
|
4649
4641
|
private async retrieveEmojiList(serverID: string): Promise<Emoji[]> {
|
|
4650
4642
|
const res = await this.http.get(
|
|
4651
4643
|
this.getHost() + "/server/" + serverID + "/emoji",
|
|
4652
4644
|
);
|
|
4653
|
-
return
|
|
4645
|
+
return decodeHttpResponse(EmojiArrayCodec, res.data);
|
|
4654
4646
|
}
|
|
4655
4647
|
|
|
4656
4648
|
private async retrieveFile(
|
|
@@ -4660,28 +4652,24 @@ export class Client {
|
|
|
4660
4652
|
const detailsRes = await this.http.get(
|
|
4661
4653
|
this.getHost() + "/file/" + fileID + "/details",
|
|
4662
4654
|
);
|
|
4663
|
-
const details =
|
|
4655
|
+
const details = decodeHttpResponse(FileSQLCodec, detailsRes.data);
|
|
4664
4656
|
|
|
4665
|
-
const res = await this.http.get
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
total,
|
|
4680
|
-
};
|
|
4681
|
-
this.emitter.emit("fileProgress", progress);
|
|
4682
|
-
},
|
|
4657
|
+
const res = await this.http.get(this.getHost() + "/file/" + fileID, {
|
|
4658
|
+
onDownloadProgress: (progressEvent) => {
|
|
4659
|
+
const percentCompleted = Math.round(
|
|
4660
|
+
(progressEvent.loaded * 100) / (progressEvent.total ?? 1),
|
|
4661
|
+
);
|
|
4662
|
+
const { loaded, total = 0 } = progressEvent;
|
|
4663
|
+
const progress: FileProgress = {
|
|
4664
|
+
direction: "download",
|
|
4665
|
+
loaded,
|
|
4666
|
+
progress: percentCompleted,
|
|
4667
|
+
token: fileID,
|
|
4668
|
+
total,
|
|
4669
|
+
};
|
|
4670
|
+
this.emitter.emit("fileProgress", progress);
|
|
4683
4671
|
},
|
|
4684
|
-
);
|
|
4672
|
+
});
|
|
4685
4673
|
const fileData = res.data;
|
|
4686
4674
|
|
|
4687
4675
|
const decrypted = await xSecretboxOpenAsync(
|
|
@@ -4703,14 +4691,14 @@ export class Client {
|
|
|
4703
4691
|
const res = await this.http.get(
|
|
4704
4692
|
this.getHost() + "/server/" + serverID + "/invites",
|
|
4705
4693
|
);
|
|
4706
|
-
return
|
|
4694
|
+
return decodeHttpResponse(InviteArrayCodec, res.data);
|
|
4707
4695
|
}
|
|
4708
4696
|
|
|
4709
4697
|
private async retrieveKeyBundle(deviceID: string): Promise<KeyBundle> {
|
|
4710
4698
|
const res = await this.http.post(
|
|
4711
4699
|
this.getHost() + "/device/" + deviceID + "/keyBundle",
|
|
4712
4700
|
);
|
|
4713
|
-
return
|
|
4701
|
+
return decodeHttpResponse(KeyBundleCodec, res.data);
|
|
4714
4702
|
}
|
|
4715
4703
|
|
|
4716
4704
|
private async retrieveOrCreateDevice(): Promise<Device> {
|
|
@@ -4722,9 +4710,9 @@ export class Client {
|
|
|
4722
4710
|
"/device/" +
|
|
4723
4711
|
XUtils.encodeHex(this.signKeys.publicKey),
|
|
4724
4712
|
);
|
|
4725
|
-
device =
|
|
4713
|
+
device = decodeHttpResponse(DeviceCodec, res.data);
|
|
4726
4714
|
} catch (err: unknown) {
|
|
4727
|
-
if (
|
|
4715
|
+
if (isHttpError(err) && err.response?.status === 404) {
|
|
4728
4716
|
await this.database.purgeKeyData();
|
|
4729
4717
|
await this.populateKeyRing();
|
|
4730
4718
|
|
|
@@ -5477,7 +5465,7 @@ export class Client {
|
|
|
5477
5465
|
},
|
|
5478
5466
|
},
|
|
5479
5467
|
);
|
|
5480
|
-
return
|
|
5468
|
+
return decodeHttpResponse(EmojiCodec, res.data);
|
|
5481
5469
|
} catch (_err: unknown) {
|
|
5482
5470
|
return null;
|
|
5483
5471
|
}
|
|
@@ -5493,7 +5481,7 @@ export class Client {
|
|
|
5493
5481
|
msgpack.encode(payload),
|
|
5494
5482
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
5495
5483
|
);
|
|
5496
|
-
return
|
|
5484
|
+
return decodeHttpResponse(EmojiCodec, res.data);
|
|
5497
5485
|
} catch (_err: unknown) {
|
|
5498
5486
|
return null;
|
|
5499
5487
|
}
|