@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/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
|
|
@@ -2477,7 +2461,10 @@ export class Client {
|
|
|
2477
2461
|
},
|
|
2478
2462
|
},
|
|
2479
2463
|
);
|
|
2480
|
-
const fcreatedFile =
|
|
2464
|
+
const fcreatedFile = decodeHttpResponse(
|
|
2465
|
+
FileSQLCodec,
|
|
2466
|
+
fres.data,
|
|
2467
|
+
);
|
|
2481
2468
|
|
|
2482
2469
|
return [fcreatedFile, XUtils.encodeHex(fileKey)];
|
|
2483
2470
|
}
|
|
@@ -2496,7 +2483,7 @@ export class Client {
|
|
|
2496
2483
|
msgpack.encode(payload),
|
|
2497
2484
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
2498
2485
|
);
|
|
2499
|
-
const createdFile =
|
|
2486
|
+
const createdFile = decodeHttpResponse(FileSQLCodec, res.data);
|
|
2500
2487
|
|
|
2501
2488
|
return [createdFile, XUtils.encodeHex(fileKey)];
|
|
2502
2489
|
});
|
|
@@ -2514,7 +2501,7 @@ export class Client {
|
|
|
2514
2501
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
2515
2502
|
);
|
|
2516
2503
|
|
|
2517
|
-
return
|
|
2504
|
+
return decodeHttpResponse(InviteCodec, res.data);
|
|
2518
2505
|
}
|
|
2519
2506
|
|
|
2520
2507
|
private async createPreKey(): Promise<UnsavedPreKey> {
|
|
@@ -2533,7 +2520,7 @@ export class Client {
|
|
|
2533
2520
|
const res = await this.http.post(
|
|
2534
2521
|
this.getHost() + "/server/" + globalThis.btoa(name),
|
|
2535
2522
|
);
|
|
2536
|
-
return
|
|
2523
|
+
return decodeHttpResponse(ServerCodec, res.data);
|
|
2537
2524
|
}
|
|
2538
2525
|
|
|
2539
2526
|
private async createSession(
|
|
@@ -2794,7 +2781,7 @@ export class Client {
|
|
|
2794
2781
|
await this.http.delete(this.getHost() + "/server/" + serverID);
|
|
2795
2782
|
}
|
|
2796
2783
|
private deviceListFailureDetail(err: unknown): string {
|
|
2797
|
-
if (!
|
|
2784
|
+
if (!isHttpError(err)) {
|
|
2798
2785
|
return "";
|
|
2799
2786
|
}
|
|
2800
2787
|
const st = err.response?.status;
|
|
@@ -2819,12 +2806,12 @@ export class Client {
|
|
|
2819
2806
|
serverID +
|
|
2820
2807
|
"/permissions",
|
|
2821
2808
|
);
|
|
2822
|
-
return
|
|
2809
|
+
return decodeHttpResponse(PermissionArrayCodec, res.data);
|
|
2823
2810
|
}
|
|
2824
2811
|
|
|
2825
2812
|
private async fetchUser(
|
|
2826
2813
|
userIdentifier: string,
|
|
2827
|
-
): Promise<[null | User,
|
|
2814
|
+
): Promise<[null | User, HttpError | null]> {
|
|
2828
2815
|
// Usernames are case-insensitive at the protocol level, so
|
|
2829
2816
|
// canonicalize the *cache key* to lowercase for username
|
|
2830
2817
|
// lookups. Without this we'd accumulate duplicate
|
|
@@ -2851,18 +2838,18 @@ export class Client {
|
|
|
2851
2838
|
const res = await this.http.get(
|
|
2852
2839
|
this.getHost() + "/user/" + cacheKey,
|
|
2853
2840
|
);
|
|
2854
|
-
const userRecord =
|
|
2841
|
+
const userRecord = decodeHttpResponse(UserCodec, res.data);
|
|
2855
2842
|
this.userRecords[cacheKey] = userRecord;
|
|
2856
2843
|
this.notFoundUsers.delete(cacheKey);
|
|
2857
2844
|
return [userRecord, null];
|
|
2858
2845
|
} catch (err: unknown) {
|
|
2859
|
-
if (
|
|
2846
|
+
if (isHttpError(err) && err.response?.status === 404) {
|
|
2860
2847
|
// Definitive: user doesn't exist — cache and don't retry
|
|
2861
2848
|
this.notFoundUsers.set(cacheKey, Date.now());
|
|
2862
2849
|
return [null, err];
|
|
2863
2850
|
}
|
|
2864
2851
|
// Transient (5xx, network error) — don't cache, caller can retry
|
|
2865
|
-
return [null,
|
|
2852
|
+
return [null, isHttpError(err) ? err : null];
|
|
2866
2853
|
}
|
|
2867
2854
|
}
|
|
2868
2855
|
|
|
@@ -2873,7 +2860,7 @@ export class Client {
|
|
|
2873
2860
|
const res = await this.http.get(
|
|
2874
2861
|
this.getHost() + "/user/" + userID + "/devices",
|
|
2875
2862
|
);
|
|
2876
|
-
const devices =
|
|
2863
|
+
const devices = decodeHttpResponse(DeviceArrayCodec, res.data);
|
|
2877
2864
|
for (const device of devices) {
|
|
2878
2865
|
this.deviceRecords[device.deviceID] = device;
|
|
2879
2866
|
}
|
|
@@ -2936,7 +2923,7 @@ export class Client {
|
|
|
2936
2923
|
msgpack.encode(args),
|
|
2937
2924
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
2938
2925
|
);
|
|
2939
|
-
const decoded =
|
|
2926
|
+
const decoded = decodeHttpResponse(
|
|
2940
2927
|
PasskeyAuthFinishResponseCodec,
|
|
2941
2928
|
response.data,
|
|
2942
2929
|
);
|
|
@@ -2957,7 +2944,7 @@ export class Client {
|
|
|
2957
2944
|
msgpack.encode(args),
|
|
2958
2945
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
2959
2946
|
);
|
|
2960
|
-
return
|
|
2947
|
+
return decodeHttpResponse(PasskeyCodec, response.data);
|
|
2961
2948
|
}
|
|
2962
2949
|
|
|
2963
2950
|
private async forward(message: Message) {
|
|
@@ -3007,7 +2994,7 @@ export class Client {
|
|
|
3007
2994
|
const res = await this.http.get(
|
|
3008
2995
|
this.getHost() + "/channel/" + channelID,
|
|
3009
2996
|
);
|
|
3010
|
-
return
|
|
2997
|
+
return decodeHttpResponse(ChannelCodec, res.data);
|
|
3011
2998
|
} catch (_err: unknown) {
|
|
3012
2999
|
return null;
|
|
3013
3000
|
}
|
|
@@ -3017,7 +3004,7 @@ export class Client {
|
|
|
3017
3004
|
const res = await this.http.get(
|
|
3018
3005
|
this.getHost() + "/server/" + serverID + "/channels",
|
|
3019
3006
|
);
|
|
3020
|
-
return
|
|
3007
|
+
return decodeHttpResponse(ChannelArrayCodec, res.data);
|
|
3021
3008
|
}
|
|
3022
3009
|
|
|
3023
3010
|
private getDevice(): Device {
|
|
@@ -3043,7 +3030,7 @@ export class Client {
|
|
|
3043
3030
|
const res = await this.http.get(
|
|
3044
3031
|
this.getHost() + "/device/" + deviceID,
|
|
3045
3032
|
);
|
|
3046
|
-
const fetchedDevice =
|
|
3033
|
+
const fetchedDevice = decodeHttpResponse(DeviceCodec, res.data);
|
|
3047
3034
|
this.deviceRecords[deviceID] = fetchedDevice;
|
|
3048
3035
|
await this.database.saveDevice(fetchedDevice);
|
|
3049
3036
|
return fetchedDevice;
|
|
@@ -3064,9 +3051,9 @@ export class Client {
|
|
|
3064
3051
|
"/devices/requests/" +
|
|
3065
3052
|
requestID,
|
|
3066
3053
|
);
|
|
3067
|
-
return
|
|
3054
|
+
return decodeHttpResponse(PendingDeviceRequestCodec, response.data);
|
|
3068
3055
|
} catch (err: unknown) {
|
|
3069
|
-
if (
|
|
3056
|
+
if (isHttpError(err) && err.response?.status === 404) {
|
|
3070
3057
|
return null;
|
|
3071
3058
|
}
|
|
3072
3059
|
throw err;
|
|
@@ -3112,7 +3099,7 @@ export class Client {
|
|
|
3112
3099
|
}
|
|
3113
3100
|
|
|
3114
3101
|
try {
|
|
3115
|
-
const res = await this.http.post
|
|
3102
|
+
const res = await this.http.post(
|
|
3116
3103
|
this.getHost() +
|
|
3117
3104
|
"/device/" +
|
|
3118
3105
|
this.getDevice().deviceID +
|
|
@@ -3186,7 +3173,7 @@ export class Client {
|
|
|
3186
3173
|
msgpack.encode(userIDs),
|
|
3187
3174
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
3188
3175
|
);
|
|
3189
|
-
const devices =
|
|
3176
|
+
const devices = decodeHttpResponse(DeviceArrayCodec, res.data);
|
|
3190
3177
|
for (const device of devices) {
|
|
3191
3178
|
this.deviceRecords[device.deviceID] = device;
|
|
3192
3179
|
}
|
|
@@ -3204,7 +3191,7 @@ export class Client {
|
|
|
3204
3191
|
this.getDevice().deviceID +
|
|
3205
3192
|
"/otk/count",
|
|
3206
3193
|
);
|
|
3207
|
-
return
|
|
3194
|
+
return decodeHttpResponse(OtkCountCodec, res.data).count;
|
|
3208
3195
|
}
|
|
3209
3196
|
|
|
3210
3197
|
/**
|
|
@@ -3216,7 +3203,7 @@ export class Client {
|
|
|
3216
3203
|
const res = await this.http.get(
|
|
3217
3204
|
this.getHost() + "/user/" + this.getUser().userID + "/permissions",
|
|
3218
3205
|
);
|
|
3219
|
-
return
|
|
3206
|
+
return decodeHttpResponse(PermissionArrayCodec, res.data);
|
|
3220
3207
|
}
|
|
3221
3208
|
|
|
3222
3209
|
private async getServerByID(serverID: string): Promise<null | Server> {
|
|
@@ -3224,7 +3211,7 @@ export class Client {
|
|
|
3224
3211
|
const res = await this.http.get(
|
|
3225
3212
|
this.getHost() + "/server/" + serverID,
|
|
3226
3213
|
);
|
|
3227
|
-
return
|
|
3214
|
+
return decodeHttpResponse(ServerCodec, res.data);
|
|
3228
3215
|
} catch (_err: unknown) {
|
|
3229
3216
|
return null;
|
|
3230
3217
|
}
|
|
@@ -3237,14 +3224,14 @@ export class Client {
|
|
|
3237
3224
|
this.getUser().userID +
|
|
3238
3225
|
"/servers/bootstrap",
|
|
3239
3226
|
);
|
|
3240
|
-
return
|
|
3227
|
+
return decodeHttpResponse(ServerChannelBootstrapCodec, res.data);
|
|
3241
3228
|
}
|
|
3242
3229
|
|
|
3243
3230
|
private async getServerList(): Promise<Server[]> {
|
|
3244
3231
|
const res = await this.http.get(
|
|
3245
3232
|
this.getHost() + "/user/" + this.getUser().userID + "/servers",
|
|
3246
3233
|
);
|
|
3247
|
-
return
|
|
3234
|
+
return decodeHttpResponse(ServerArrayCodec, res.data);
|
|
3248
3235
|
}
|
|
3249
3236
|
|
|
3250
3237
|
private async getSessionByPubkey(publicKey: Uint8Array) {
|
|
@@ -3277,7 +3264,7 @@ export class Client {
|
|
|
3277
3264
|
const res = await this.http.get(this.getHost() + "/token/" + type, {
|
|
3278
3265
|
responseType: "arraybuffer",
|
|
3279
3266
|
});
|
|
3280
|
-
return
|
|
3267
|
+
return decodeHttpResponse(ActionTokenCodec, res.data);
|
|
3281
3268
|
} catch {
|
|
3282
3269
|
return null;
|
|
3283
3270
|
}
|
|
@@ -3312,7 +3299,7 @@ export class Client {
|
|
|
3312
3299
|
const res = await this.http.post(
|
|
3313
3300
|
this.getHost() + "/userList/" + channelID,
|
|
3314
3301
|
);
|
|
3315
|
-
return
|
|
3302
|
+
return decodeHttpResponse(UserArrayCodec, res.data);
|
|
3316
3303
|
}
|
|
3317
3304
|
|
|
3318
3305
|
private async handleNotify(msg: NotifyMsg) {
|
|
@@ -3385,14 +3372,6 @@ export class Client {
|
|
|
3385
3372
|
}
|
|
3386
3373
|
this.hasInit = true;
|
|
3387
3374
|
|
|
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
3375
|
await this.populateKeyRing();
|
|
3397
3376
|
this.emitter.on("message", this.onInternalMessage);
|
|
3398
3377
|
void this.runLocalRetentionPurge();
|
|
@@ -3546,7 +3525,10 @@ export class Client {
|
|
|
3546
3525
|
this.getUser().userID +
|
|
3547
3526
|
"/devices/requests",
|
|
3548
3527
|
);
|
|
3549
|
-
return
|
|
3528
|
+
return decodeHttpResponse(
|
|
3529
|
+
PendingDeviceRequestArrayCodec,
|
|
3530
|
+
response.data,
|
|
3531
|
+
);
|
|
3550
3532
|
}
|
|
3551
3533
|
|
|
3552
3534
|
/**
|
|
@@ -3560,7 +3542,7 @@ export class Client {
|
|
|
3560
3542
|
const res = await this.http.get(
|
|
3561
3543
|
this.getHost() + "/user/" + userID + "/devices",
|
|
3562
3544
|
);
|
|
3563
|
-
return
|
|
3545
|
+
return decodeHttpResponse(DeviceArrayCodec, res.data);
|
|
3564
3546
|
}
|
|
3565
3547
|
|
|
3566
3548
|
private async listPasskeys(): Promise<Passkey[]> {
|
|
@@ -3568,7 +3550,7 @@ export class Client {
|
|
|
3568
3550
|
const response = await this.http.get(
|
|
3569
3551
|
this.getHost() + "/user/" + userID + "/passkeys",
|
|
3570
3552
|
);
|
|
3571
|
-
return
|
|
3553
|
+
return decodeHttpResponse(PasskeyArrayCodec, response.data);
|
|
3572
3554
|
}
|
|
3573
3555
|
|
|
3574
3556
|
private async markSessionVerified(sessionID: string) {
|
|
@@ -3634,7 +3616,7 @@ export class Client {
|
|
|
3634
3616
|
requestID +
|
|
3635
3617
|
"/approve",
|
|
3636
3618
|
);
|
|
3637
|
-
return
|
|
3619
|
+
return decodeHttpResponse(DeviceCodec, response.data);
|
|
3638
3620
|
}
|
|
3639
3621
|
|
|
3640
3622
|
private async passkeyDeleteDevice(deviceID: string): Promise<void> {
|
|
@@ -3649,7 +3631,7 @@ export class Client {
|
|
|
3649
3631
|
const response = await this.http.get(
|
|
3650
3632
|
this.getHost() + "/user/" + userID + "/passkey/devices",
|
|
3651
3633
|
);
|
|
3652
|
-
return
|
|
3634
|
+
return decodeHttpResponse(DeviceArrayCodec, response.data);
|
|
3653
3635
|
}
|
|
3654
3636
|
|
|
3655
3637
|
private async passkeyRejectDeviceRequest(requestID: string): Promise<void> {
|
|
@@ -3716,9 +3698,9 @@ export class Client {
|
|
|
3716
3698
|
msgpack.encode({ signed }),
|
|
3717
3699
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
3718
3700
|
);
|
|
3719
|
-
return
|
|
3701
|
+
return decodeHttpResponse(PendingDeviceRequestCodec, response.data);
|
|
3720
3702
|
} catch (err: unknown) {
|
|
3721
|
-
if (
|
|
3703
|
+
if (isHttpError(err) && err.response?.status === 404) {
|
|
3722
3704
|
return null;
|
|
3723
3705
|
}
|
|
3724
3706
|
throw err;
|
|
@@ -4548,7 +4530,7 @@ export class Client {
|
|
|
4548
4530
|
const res = await this.http.patch(
|
|
4549
4531
|
this.getHost() + "/invite/" + inviteID,
|
|
4550
4532
|
);
|
|
4551
|
-
return
|
|
4533
|
+
return decodeHttpResponse(PermissionCodec, res.data);
|
|
4552
4534
|
}
|
|
4553
4535
|
|
|
4554
4536
|
private registerDecryptFailure(mail: MailWS): number {
|
|
@@ -4603,7 +4585,7 @@ export class Client {
|
|
|
4603
4585
|
msgpack.encode(devMsg),
|
|
4604
4586
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
4605
4587
|
);
|
|
4606
|
-
return
|
|
4588
|
+
return decodeHttpResponse(DeviceRegistrationResultCodec, res.data);
|
|
4607
4589
|
}
|
|
4608
4590
|
|
|
4609
4591
|
private async rejectDeviceRequest(requestID: string): Promise<void> {
|
|
@@ -4640,17 +4622,14 @@ export class Client {
|
|
|
4640
4622
|
const res = await this.http.get(
|
|
4641
4623
|
this.getHost() + "/emoji/" + emojiID + "/details",
|
|
4642
4624
|
);
|
|
4643
|
-
|
|
4644
|
-
return null;
|
|
4645
|
-
}
|
|
4646
|
-
return decodeAxios(EmojiCodec, res.data);
|
|
4625
|
+
return decodeHttpResponse(EmojiCodec, res.data);
|
|
4647
4626
|
}
|
|
4648
4627
|
|
|
4649
4628
|
private async retrieveEmojiList(serverID: string): Promise<Emoji[]> {
|
|
4650
4629
|
const res = await this.http.get(
|
|
4651
4630
|
this.getHost() + "/server/" + serverID + "/emoji",
|
|
4652
4631
|
);
|
|
4653
|
-
return
|
|
4632
|
+
return decodeHttpResponse(EmojiArrayCodec, res.data);
|
|
4654
4633
|
}
|
|
4655
4634
|
|
|
4656
4635
|
private async retrieveFile(
|
|
@@ -4660,28 +4639,24 @@ export class Client {
|
|
|
4660
4639
|
const detailsRes = await this.http.get(
|
|
4661
4640
|
this.getHost() + "/file/" + fileID + "/details",
|
|
4662
4641
|
);
|
|
4663
|
-
const details =
|
|
4642
|
+
const details = decodeHttpResponse(FileSQLCodec, detailsRes.data);
|
|
4664
4643
|
|
|
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
|
-
},
|
|
4644
|
+
const res = await this.http.get(this.getHost() + "/file/" + fileID, {
|
|
4645
|
+
onDownloadProgress: (progressEvent) => {
|
|
4646
|
+
const percentCompleted = Math.round(
|
|
4647
|
+
(progressEvent.loaded * 100) / (progressEvent.total ?? 1),
|
|
4648
|
+
);
|
|
4649
|
+
const { loaded, total = 0 } = progressEvent;
|
|
4650
|
+
const progress: FileProgress = {
|
|
4651
|
+
direction: "download",
|
|
4652
|
+
loaded,
|
|
4653
|
+
progress: percentCompleted,
|
|
4654
|
+
token: fileID,
|
|
4655
|
+
total,
|
|
4656
|
+
};
|
|
4657
|
+
this.emitter.emit("fileProgress", progress);
|
|
4683
4658
|
},
|
|
4684
|
-
);
|
|
4659
|
+
});
|
|
4685
4660
|
const fileData = res.data;
|
|
4686
4661
|
|
|
4687
4662
|
const decrypted = await xSecretboxOpenAsync(
|
|
@@ -4703,14 +4678,14 @@ export class Client {
|
|
|
4703
4678
|
const res = await this.http.get(
|
|
4704
4679
|
this.getHost() + "/server/" + serverID + "/invites",
|
|
4705
4680
|
);
|
|
4706
|
-
return
|
|
4681
|
+
return decodeHttpResponse(InviteArrayCodec, res.data);
|
|
4707
4682
|
}
|
|
4708
4683
|
|
|
4709
4684
|
private async retrieveKeyBundle(deviceID: string): Promise<KeyBundle> {
|
|
4710
4685
|
const res = await this.http.post(
|
|
4711
4686
|
this.getHost() + "/device/" + deviceID + "/keyBundle",
|
|
4712
4687
|
);
|
|
4713
|
-
return
|
|
4688
|
+
return decodeHttpResponse(KeyBundleCodec, res.data);
|
|
4714
4689
|
}
|
|
4715
4690
|
|
|
4716
4691
|
private async retrieveOrCreateDevice(): Promise<Device> {
|
|
@@ -4722,9 +4697,9 @@ export class Client {
|
|
|
4722
4697
|
"/device/" +
|
|
4723
4698
|
XUtils.encodeHex(this.signKeys.publicKey),
|
|
4724
4699
|
);
|
|
4725
|
-
device =
|
|
4700
|
+
device = decodeHttpResponse(DeviceCodec, res.data);
|
|
4726
4701
|
} catch (err: unknown) {
|
|
4727
|
-
if (
|
|
4702
|
+
if (isHttpError(err) && err.response?.status === 404) {
|
|
4728
4703
|
await this.database.purgeKeyData();
|
|
4729
4704
|
await this.populateKeyRing();
|
|
4730
4705
|
|
|
@@ -5477,7 +5452,7 @@ export class Client {
|
|
|
5477
5452
|
},
|
|
5478
5453
|
},
|
|
5479
5454
|
);
|
|
5480
|
-
return
|
|
5455
|
+
return decodeHttpResponse(EmojiCodec, res.data);
|
|
5481
5456
|
} catch (_err: unknown) {
|
|
5482
5457
|
return null;
|
|
5483
5458
|
}
|
|
@@ -5493,7 +5468,7 @@ export class Client {
|
|
|
5493
5468
|
msgpack.encode(payload),
|
|
5494
5469
|
{ headers: { "Content-Type": "application/msgpack" } },
|
|
5495
5470
|
);
|
|
5496
|
-
return
|
|
5471
|
+
return decodeHttpResponse(EmojiCodec, res.data);
|
|
5497
5472
|
} catch (_err: unknown) {
|
|
5498
5473
|
return null;
|
|
5499
5474
|
}
|