@vex-chat/libvex 6.6.0 → 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/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
- decodeAxios,
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, AxiosError | null]>;
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: AxiosInstance;
1443
- /** Cancels in-flight axios work on `close()` so `postAuth`/`getMail` cannot hang forever. */
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 = axios.create({
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 } = decodeAxios(ConnectResponseCodec, res.data);
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 } = decodeAxios(AuthResponseCodec, res.data);
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 (isAxiosError(err) && err.response) {
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 } = decodeAxios(
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 } = decodeAxios(
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 } = decodeAxios(
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 = decodeAxios(
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 = decodeAxios(UserCodec, res.data);
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 (isAxiosError(err) && err.response) {
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<NotificationSubscription>(
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 = decodeAxios(WhoamiCodec, res.data);
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 decodeAxios(DeviceCodec, response.data);
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 decodeAxios(PasskeyOptionsCodec, response.data);
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 decodeAxios(PasskeyOptionsCodec, response.data);
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 decodeAxios(ChannelCodec, res.data);
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 = decodeAxios(FileSQLCodec, fres.data);
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 = decodeAxios(FileSQLCodec, res.data);
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 decodeAxios(InviteCodec, res.data);
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 decodeAxios(ServerCodec, res.data);
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 (!isAxiosError(err)) {
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 decodeAxios(PermissionArrayCodec, res.data);
2809
+ return decodeHttpResponse(PermissionArrayCodec, res.data);
2823
2810
  }
2824
2811
 
2825
2812
  private async fetchUser(
2826
2813
  userIdentifier: string,
2827
- ): Promise<[null | User, AxiosError | null]> {
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 = decodeAxios(UserCodec, res.data);
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 (isAxiosError(err) && err.response?.status === 404) {
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, isAxiosError(err) ? err : 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 = decodeAxios(DeviceArrayCodec, res.data);
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 = decodeAxios(
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 decodeAxios(PasskeyCodec, response.data);
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 decodeAxios(ChannelCodec, res.data);
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 decodeAxios(ChannelArrayCodec, res.data);
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 = decodeAxios(DeviceCodec, res.data);
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 decodeAxios(PendingDeviceRequestCodec, response.data);
3054
+ return decodeHttpResponse(PendingDeviceRequestCodec, response.data);
3068
3055
  } catch (err: unknown) {
3069
- if (isAxiosError(err) && err.response?.status === 404) {
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<ArrayBuffer>(
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 = decodeAxios(DeviceArrayCodec, res.data);
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 decodeAxios(OtkCountCodec, res.data).count;
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 decodeAxios(PermissionArrayCodec, res.data);
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 decodeAxios(ServerCodec, res.data);
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 decodeAxios(ServerChannelBootstrapCodec, res.data);
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 decodeAxios(ServerArrayCodec, res.data);
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 decodeAxios(ActionTokenCodec, res.data);
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 decodeAxios(UserArrayCodec, res.data);
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 decodeAxios(PendingDeviceRequestArrayCodec, response.data);
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 decodeAxios(DeviceArrayCodec, res.data);
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 decodeAxios(PasskeyArrayCodec, response.data);
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 decodeAxios(DeviceCodec, response.data);
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 decodeAxios(DeviceArrayCodec, response.data);
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 decodeAxios(PendingDeviceRequestCodec, response.data);
3701
+ return decodeHttpResponse(PendingDeviceRequestCodec, response.data);
3720
3702
  } catch (err: unknown) {
3721
- if (isAxiosError(err) && err.response?.status === 404) {
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 decodeAxios(PermissionCodec, res.data);
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 decodeAxios(DeviceRegistrationResultCodec, res.data);
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
- if (!res.data) {
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 decodeAxios(EmojiArrayCodec, res.data);
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 = decodeAxios(FileSQLCodec, detailsRes.data);
4642
+ const details = decodeHttpResponse(FileSQLCodec, detailsRes.data);
4664
4643
 
4665
- const res = await this.http.get<ArrayBuffer>(
4666
- this.getHost() + "/file/" + fileID,
4667
- {
4668
- onDownloadProgress: (progressEvent) => {
4669
- const percentCompleted = Math.round(
4670
- (progressEvent.loaded * 100) /
4671
- (progressEvent.total ?? 1),
4672
- );
4673
- const { loaded, total = 0 } = progressEvent;
4674
- const progress: FileProgress = {
4675
- direction: "download",
4676
- loaded,
4677
- progress: percentCompleted,
4678
- token: fileID,
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 decodeAxios(InviteArrayCodec, res.data);
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 decodeAxios(KeyBundleCodec, res.data);
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 = decodeAxios(DeviceCodec, res.data);
4700
+ device = decodeHttpResponse(DeviceCodec, res.data);
4726
4701
  } catch (err: unknown) {
4727
- if (isAxiosError(err) && err.response?.status === 404) {
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 decodeAxios(EmojiCodec, res.data);
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 decodeAxios(EmojiCodec, res.data);
5471
+ return decodeHttpResponse(EmojiCodec, res.data);
5497
5472
  } catch (_err: unknown) {
5498
5473
  return null;
5499
5474
  }