@vex-chat/libvex 1.0.0-rc.2 → 1.0.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.
Files changed (58) hide show
  1. package/dist/Client.d.ts +13 -9
  2. package/dist/Client.js +131 -74
  3. package/dist/Client.js.map +1 -1
  4. package/dist/IStorage.d.ts +1 -1
  5. package/dist/IStorage.js +1 -1
  6. package/dist/IStorage.js.map +1 -1
  7. package/dist/__tests__/harness/memory-storage.d.ts +45 -0
  8. package/dist/__tests__/harness/memory-storage.js +181 -0
  9. package/dist/__tests__/harness/memory-storage.js.map +1 -0
  10. package/dist/index.d.ts +3 -0
  11. package/dist/keystore/memory.d.ts +11 -0
  12. package/dist/keystore/memory.js +18 -0
  13. package/dist/keystore/memory.js.map +1 -0
  14. package/dist/keystore/node.d.ts +10 -0
  15. package/dist/keystore/node.js +64 -0
  16. package/dist/keystore/node.js.map +1 -0
  17. package/dist/keystore/types.d.ts +4 -0
  18. package/dist/keystore/types.js +2 -0
  19. package/dist/keystore/types.js.map +1 -0
  20. package/dist/preset/expo.d.ts +2 -0
  21. package/dist/preset/expo.js +37 -0
  22. package/dist/preset/expo.js.map +1 -0
  23. package/dist/preset/node.d.ts +12 -0
  24. package/dist/preset/node.js +16 -0
  25. package/dist/preset/node.js.map +1 -0
  26. package/dist/preset/tauri.d.ts +2 -0
  27. package/dist/preset/tauri.js +35 -0
  28. package/dist/preset/tauri.js.map +1 -0
  29. package/dist/preset/test.d.ts +10 -0
  30. package/dist/preset/test.js +28 -0
  31. package/dist/preset/test.js.map +1 -0
  32. package/dist/preset/types.d.ts +13 -0
  33. package/dist/preset/types.js +2 -0
  34. package/dist/preset/types.js.map +1 -0
  35. package/dist/storage/expo.d.ts +3 -0
  36. package/dist/storage/expo.js +18 -0
  37. package/dist/storage/expo.js.map +1 -0
  38. package/dist/storage/node.d.ts +3 -0
  39. package/dist/storage/node.js +24 -0
  40. package/dist/storage/node.js.map +1 -0
  41. package/dist/storage/schema.d.ts +79 -0
  42. package/dist/storage/schema.js +2 -0
  43. package/dist/storage/schema.js.map +1 -0
  44. package/dist/{Storage.d.ts → storage/sqlite.d.ts} +19 -21
  45. package/dist/storage/sqlite.js +487 -0
  46. package/dist/storage/sqlite.js.map +1 -0
  47. package/dist/storage/tauri.d.ts +3 -0
  48. package/dist/storage/tauri.js +21 -0
  49. package/dist/storage/tauri.js.map +1 -0
  50. package/dist/transport/browser.d.ts +17 -0
  51. package/dist/transport/browser.js +56 -0
  52. package/dist/transport/browser.js.map +1 -0
  53. package/dist/transport/types.d.ts +20 -0
  54. package/dist/transport/types.js +2 -0
  55. package/dist/transport/types.js.map +1 -0
  56. package/package.json +102 -14
  57. package/dist/Storage.js +0 -444
  58. package/dist/Storage.js.map +0 -1
package/dist/Client.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { IChannel, IDevice, IEmoji, IFileResponse, IFileSQL, IInvite, IPermission, IServer, ISessionSQL } from "@vex-chat/types";
2
2
  import { AxiosError } from "axios";
3
- import { EventEmitter } from "events";
3
+ import { EventEmitter } from "eventemitter3";
4
+ import type { IClientAdapters } from "./transport/types.js";
4
5
  import type { IStorage } from "./IStorage.js";
5
6
  /**
6
7
  * IMessage is a chat message.
@@ -128,7 +129,7 @@ export type { IChannel } from "@vex-chat/types";
128
129
  */
129
130
  export type { IServer } from "@vex-chat/types";
130
131
  /**
131
- * Ifile is an uploaded encrypted file.
132
+ * IFile is an uploaded encrypted file.
132
133
  *
133
134
  * Common fields:
134
135
  * - `fileID`: file identifier
@@ -176,7 +177,7 @@ export interface IMe {
176
177
  /** Returns metadata for the currently authenticated device. */
177
178
  device: () => IDevice;
178
179
  /** Uploads and sets a new avatar image for the current user. */
179
- setAvatar: (avatar: Buffer) => Promise<void>;
180
+ setAvatar: (avatar: Uint8Array) => Promise<void>;
180
181
  }
181
182
  /**
182
183
  * @ignore
@@ -292,7 +293,7 @@ export interface IDevices {
292
293
  */
293
294
  export interface IFiles {
294
295
  /** Uploads and encrypts a file. */
295
- create: (file: Buffer) => Promise<[IFileSQL, string]>;
296
+ create: (file: Uint8Array) => Promise<[IFileSQL, string]>;
296
297
  /** Downloads and decrypts a file using a file ID and key. */
297
298
  retrieve: (fileID: string, key: string) => Promise<IFileResponse | null>;
298
299
  }
@@ -301,7 +302,7 @@ export interface IFiles {
301
302
  */
302
303
  export interface IEmojis {
303
304
  /** Uploads a custom emoji to a server. */
304
- create: (emoji: Buffer, name: string, serverID: string) => Promise<IEmoji | null>;
305
+ create: (emoji: Uint8Array, name: string, serverID: string) => Promise<IEmoji | null>;
305
306
  /** Lists emojis available on a server. */
306
307
  retrieveList: (serverID: string) => Promise<IEmoji[]>;
307
308
  /** Fetches one emoji's metadata by ID. */
@@ -340,6 +341,8 @@ export interface IClientOptions {
340
341
  unsafeHttp?: boolean;
341
342
  /** Whether local message history should be persisted by default storage. */
342
343
  saveHistory?: boolean;
344
+ /** Platform-specific adapters (WebSocket + Logger). When omitted, defaults to Node.js ws. */
345
+ adapters?: IClientAdapters;
343
346
  }
344
347
  export declare interface Client {
345
348
  /**
@@ -518,17 +521,17 @@ export declare interface Client {
518
521
  */
519
522
  export declare class Client extends EventEmitter {
520
523
  /**
521
- * Loads a key file from disk.
524
+ * Encrypts a secret key with a password.
522
525
  *
523
526
  * Pass-through utility from `@vex-chat/crypto`.
524
527
  */
525
- static loadKeyFile: (path: string, password: string) => string;
528
+ static encryptKeyData: (password: string, keyToSave: string, iterationOverride?: number) => Uint8Array;
526
529
  /**
527
- * Saves a key file to disk.
530
+ * Decrypts a secret key from encrypted data produced by encryptKeyData().
528
531
  *
529
532
  * Pass-through utility from `@vex-chat/crypto`.
530
533
  */
531
- static saveKeyFile: (path: string, password: string, keyToSave: string, iterationOverride?: number) => void;
534
+ static decryptKeyData: (keyData: Uint8Array, password: string) => string;
532
535
  /**
533
536
  * Creates and initializes a client in one step.
534
537
  *
@@ -641,6 +644,7 @@ export declare class Client extends EventEmitter {
641
644
  private dbPath;
642
645
  private conn;
643
646
  private host;
647
+ private adapters;
644
648
  private firstMailFetch;
645
649
  private signKeys;
646
650
  private idKeys;
package/dist/Client.js CHANGED
@@ -4,25 +4,30 @@ import { xConcat, xConstants, xDH, xEncode, xHMAC, xKDF, XKeyConvert, xMakeNonce
4
4
  import { MailType } from "@vex-chat/types";
5
5
  import ax, { AxiosError } from "axios";
6
6
  import { isBrowser, isNode } from "browser-or-node";
7
- import btoa from "btoa";
8
- import chalk from "chalk";
9
- import { EventEmitter } from "events";
7
+ // btoa is native in browsers and Node 16+
8
+ import pc from "picocolors";
9
+ import { EventEmitter } from "eventemitter3";
10
10
  import { Packr } from "msgpackr";
11
11
  // useRecords:false emits standard msgpack (no nonstandard record extension).
12
12
  // moreTypes:false keeps the extension set to what every other decoder understands.
13
- // Packr.pack() returns Node Buffer, which axios sends correctly (plain Uint8Array
14
- // would have its pool buffer sent in full — see axios issue #4068).
15
- const msgpack = new Packr({ useRecords: false, moreTypes: false });
13
+ const _packr = new Packr({ useRecords: false, moreTypes: false });
14
+ // Packr.encode() returns a subarray of its internal pool buffer.
15
+ // In browsers, XMLHttpRequest.send() sends the full underlying ArrayBuffer,
16
+ // not just the slice — corrupting the payload (axios issue #4068).
17
+ // Wrap encode to always return a fresh copy.
18
+ const msgpack = {
19
+ encode: (value) => {
20
+ const packed = _packr.encode(value);
21
+ return new Uint8Array(packed.buffer.slice(packed.byteOffset, packed.byteOffset + packed.byteLength));
22
+ },
23
+ decode: _packr.decode.bind(_packr),
24
+ };
16
25
  import objectHash from "object-hash";
17
- import * as os from "node:os";
18
- import { performance } from "node:perf_hooks";
19
26
  import nacl from "tweetnacl";
20
27
  import * as uuid from "uuid";
21
- import winston from "winston";
22
- import WebSocket from "ws";
23
- import { Storage } from "./Storage.js";
28
+ // Storage (Kysely/better-sqlite3) loaded lazily — only when no external storage is provided.
24
29
  import { capitalize } from "./utils/capitalize.js";
25
- import { createLogger } from "./utils/createLogger.js";
30
+ // createLogger (winston) loaded lazily — only in Node when no adapter logger is provided.
26
31
  import { formatBytes } from "./utils/formatBytes.js";
27
32
  import { sqlSessionToCrypto } from "./utils/sqlSessionToCrypto.js";
28
33
  import { uuidToUint8 } from "./utils/uint8uuid.js";
@@ -78,17 +83,17 @@ const protocolMsgRegex = /��\w+:\w+��/g;
78
83
  */
79
84
  export class Client extends EventEmitter {
80
85
  /**
81
- * Loads a key file from disk.
86
+ * Encrypts a secret key with a password.
82
87
  *
83
88
  * Pass-through utility from `@vex-chat/crypto`.
84
89
  */
85
- static loadKeyFile = XUtils.loadKeyFile;
90
+ static encryptKeyData = XUtils.encryptKeyData;
86
91
  /**
87
- * Saves a key file to disk.
92
+ * Decrypts a secret key from encrypted data produced by encryptKeyData().
88
93
  *
89
94
  * Pass-through utility from `@vex-chat/crypto`.
90
95
  */
91
- static saveKeyFile = XUtils.saveKeyFile;
96
+ static decryptKeyData = XUtils.decryptKeyData;
92
97
  /**
93
98
  * Creates and initializes a client in one step.
94
99
  *
@@ -102,7 +107,36 @@ export class Client extends EventEmitter {
102
107
  * ```
103
108
  */
104
109
  static create = async (privateKey, options, storage) => {
105
- const client = new Client(privateKey, options, storage);
110
+ let opts = options;
111
+ if (!opts?.adapters) {
112
+ const { default: WebSocket } = await import("ws");
113
+ const { createLogger: makeLog } = await import("./utils/createLogger.js");
114
+ opts = {
115
+ ...opts,
116
+ adapters: {
117
+ logger: makeLog("libvex", opts?.logLevel),
118
+ WebSocket: WebSocket,
119
+ },
120
+ };
121
+ }
122
+ // Lazily create Node Storage only on the Node path (no adapters).
123
+ // When adapters are provided (browser/RN), the caller must supply storage
124
+ // via PlatformPreset.createStorage() — there is no Node fallback.
125
+ let resolvedStorage = storage;
126
+ if (!resolvedStorage) {
127
+ if (opts?.adapters) {
128
+ throw new Error("No storage provided. When using adapters (browser/RN), pass storage from your PlatformPreset.");
129
+ }
130
+ const { createNodeStorage } = await import("./storage/node.js");
131
+ const dbFileName = opts?.inMemoryDb
132
+ ? ":memory:"
133
+ : XUtils.encodeHex(nacl.sign.keyPair.fromSecretKey(XUtils.decodeHex(privateKey || "")).publicKey) + ".sqlite";
134
+ const dbPath = opts?.dbFolder
135
+ ? opts.dbFolder + "/" + dbFileName
136
+ : dbFileName;
137
+ resolvedStorage = createNodeStorage(dbPath, privateKey || XUtils.encodeHex(nacl.sign.keyPair().secretKey));
138
+ }
139
+ const client = new Client(privateKey, opts, resolvedStorage);
106
140
  await client.init();
107
141
  return client;
108
142
  };
@@ -397,6 +431,7 @@ export class Client extends EventEmitter {
397
431
  dbPath;
398
432
  conn;
399
433
  host;
434
+ adapters;
400
435
  firstMailFetch = true;
401
436
  // these are created from one set of sign keys
402
437
  signKeys;
@@ -420,7 +455,12 @@ export class Client extends EventEmitter {
420
455
  prefixes;
421
456
  constructor(privateKey, options, storage) {
422
457
  super();
423
- this.log = createLogger("client", options?.logLevel);
458
+ this.log = options?.adapters?.logger ?? {
459
+ info() { },
460
+ warn() { },
461
+ error() { },
462
+ debug() { },
463
+ };
424
464
  this.prefixes = options?.unsafeHttp
425
465
  ? { HTTP: "http://", WS: "ws://" }
426
466
  : { HTTP: "https://", WS: "wss://" };
@@ -438,17 +478,20 @@ export class Client extends EventEmitter {
438
478
  this.dbPath = options?.dbFolder
439
479
  ? options?.dbFolder + "/" + dbFileName
440
480
  : dbFileName;
441
- this.database = storage
442
- ? storage
443
- : new Storage(this.dbPath, XUtils.encodeHex(this.signKeys.secretKey), options);
481
+ if (!storage) {
482
+ throw new Error("No storage provided. Use Client.create() which resolves storage automatically.");
483
+ }
484
+ this.database = storage;
444
485
  this.database.on("error", (error) => {
445
486
  this.log.error(error.toString());
446
487
  this.close(true);
447
488
  });
448
- // we want to initialize this later with login()
449
- this.conn = new WebSocket("ws://localhost:1234");
450
- // silence the error for connecting to junk ws
451
- // tslint:disable-next-line: no-empty
489
+ if (!options?.adapters) {
490
+ throw new Error("No adapters provided. Use Client.create() which resolves adapters automatically.");
491
+ }
492
+ this.adapters = options.adapters;
493
+ // Placeholder connection — replaced by initSocket() during connect()
494
+ this.conn = new this.adapters.WebSocket("ws://localhost:1234");
452
495
  this.conn.onerror = () => { };
453
496
  this.log.info("Client debug information: " +
454
497
  JSON.stringify({
@@ -523,7 +566,7 @@ export class Client extends EventEmitter {
523
566
  }), {
524
567
  headers: { "Content-Type": "application/msgpack" },
525
568
  });
526
- const { user, token } = msgpack.decode(Buffer.from(res.data));
569
+ const { user, token } = msgpack.decode(new Uint8Array(res.data));
527
570
  const cookies = res.headers["set-cookie"];
528
571
  if (cookies) {
529
572
  for (const cookie of cookies) {
@@ -561,7 +604,7 @@ export class Client extends EventEmitter {
561
604
  withCredentials: true,
562
605
  responseType: "arraybuffer",
563
606
  });
564
- const whoami = msgpack.decode(Buffer.from(res.data));
607
+ const whoami = msgpack.decode(new Uint8Array(res.data));
565
608
  return whoami;
566
609
  }
567
610
  /**
@@ -624,11 +667,13 @@ export class Client extends EventEmitter {
624
667
  preKeySignature: XUtils.encodeHex(this.xKeyRing.preKeys.signature),
625
668
  preKeyIndex: this.xKeyRing.preKeys.index,
626
669
  password,
627
- deviceName: `${os.platform()}`,
670
+ deviceName: typeof navigator !== "undefined"
671
+ ? navigator.userAgent.slice(0, 64)
672
+ : "node",
628
673
  };
629
674
  try {
630
675
  const res = await ax.post(this.getHost() + "/register", msgpack.encode(regMsg), { headers: { "Content-Type": "application/msgpack" } });
631
- this.setUser(msgpack.decode(Buffer.from(res.data)));
676
+ this.setUser(msgpack.decode(new Uint8Array(res.data)));
632
677
  return [this.getUser(), null];
633
678
  }
634
679
  catch (err) {
@@ -652,11 +697,11 @@ export class Client extends EventEmitter {
652
697
  }
653
698
  async redeemInvite(inviteID) {
654
699
  const res = await ax.patch(this.getHost() + "/invite/" + inviteID);
655
- return msgpack.decode(Buffer.from(res.data));
700
+ return msgpack.decode(new Uint8Array(res.data));
656
701
  }
657
702
  async retrieveInvites(serverID) {
658
703
  const res = await ax.get(this.getHost() + "/server/" + serverID + "/invites");
659
- return msgpack.decode(Buffer.from(res.data));
704
+ return msgpack.decode(new Uint8Array(res.data));
660
705
  }
661
706
  async createInvite(serverID, duration) {
662
707
  const payload = {
@@ -664,11 +709,11 @@ export class Client extends EventEmitter {
664
709
  duration,
665
710
  };
666
711
  const res = await ax.post(this.getHost() + "/server/" + serverID + "/invites", payload);
667
- return msgpack.decode(Buffer.from(res.data));
712
+ return msgpack.decode(new Uint8Array(res.data));
668
713
  }
669
714
  async retrieveEmojiList(serverID) {
670
715
  const res = await ax.get(this.getHost() + "/server/" + serverID + "/emoji");
671
- return msgpack.decode(Buffer.from(res.data));
716
+ return msgpack.decode(new Uint8Array(res.data));
672
717
  }
673
718
  async retrieveEmojiByID(emojiID) {
674
719
  const res = await ax.get(this.getHost() + "/emoji/" + emojiID + "/details");
@@ -676,7 +721,7 @@ export class Client extends EventEmitter {
676
721
  if (!res.data) {
677
722
  return null;
678
723
  }
679
- return msgpack.decode(Buffer.from(res.data));
724
+ return msgpack.decode(new Uint8Array(res.data));
680
725
  }
681
726
  async leaveServer(serverID) {
682
727
  const permissionList = await this.permissions.retrieve();
@@ -730,7 +775,7 @@ export class Client extends EventEmitter {
730
775
  this.emit("fileProgress", progress);
731
776
  },
732
777
  });
733
- return msgpack.decode(Buffer.from(res.data));
778
+ return msgpack.decode(new Uint8Array(res.data));
734
779
  }
735
780
  catch (err) {
736
781
  return null;
@@ -742,7 +787,7 @@ export class Client extends EventEmitter {
742
787
  };
743
788
  try {
744
789
  const res = await ax.post(this.getHost() + "/emoji/" + serverID + "/json", msgpack.encode(payload), { headers: { "Content-Type": "application/msgpack" } });
745
- return msgpack.decode(Buffer.from(res.data));
790
+ return msgpack.decode(new Uint8Array(res.data));
746
791
  }
747
792
  catch (err) {
748
793
  return null;
@@ -755,7 +800,7 @@ export class Client extends EventEmitter {
755
800
  this.host +
756
801
  "/device/" +
757
802
  XUtils.encodeHex(this.signKeys.publicKey));
758
- device = msgpack.decode(Buffer.from(res.data));
803
+ device = msgpack.decode(new Uint8Array(res.data));
759
804
  }
760
805
  catch (err) {
761
806
  this.log.error(err.toString());
@@ -803,7 +848,9 @@ export class Client extends EventEmitter {
803
848
  preKey: XUtils.encodeHex(this.xKeyRing.preKeys.keyPair.publicKey),
804
849
  preKeySignature: XUtils.encodeHex(this.xKeyRing.preKeys.signature),
805
850
  preKeyIndex: this.xKeyRing.preKeys.index,
806
- deviceName: `${os.platform()}`,
851
+ deviceName: typeof navigator !== "undefined"
852
+ ? navigator.userAgent.slice(0, 64)
853
+ : "node",
807
854
  };
808
855
  try {
809
856
  const res = await ax.post(this.prefixes.HTTP +
@@ -811,7 +858,7 @@ export class Client extends EventEmitter {
811
858
  "/user/" +
812
859
  userDetails.userID +
813
860
  "/devices", msgpack.encode(devMsg), { headers: { "Content-Type": "application/msgpack" } });
814
- return msgpack.decode(Buffer.from(res.data));
861
+ return msgpack.decode(new Uint8Array(res.data));
815
862
  }
816
863
  catch (err) {
817
864
  throw err;
@@ -822,7 +869,7 @@ export class Client extends EventEmitter {
822
869
  const res = await ax.get(this.getHost() + "/token/" + type, {
823
870
  responseType: "arraybuffer",
824
871
  });
825
- return msgpack.decode(Buffer.from(res.data));
872
+ return msgpack.decode(new Uint8Array(res.data));
826
873
  }
827
874
  catch (err) {
828
875
  this.log.warn(err.toString());
@@ -874,7 +921,7 @@ export class Client extends EventEmitter {
874
921
  "/server/" +
875
922
  serverID +
876
923
  "/permissions");
877
- return msgpack.decode(Buffer.from(res.data));
924
+ return msgpack.decode(new Uint8Array(res.data));
878
925
  }
879
926
  /**
880
927
  * Gets all permissions for the logged in user.
@@ -883,7 +930,7 @@ export class Client extends EventEmitter {
883
930
  */
884
931
  async getPermissions() {
885
932
  const res = await ax.get(this.getHost() + "/user/" + this.getUser().userID + "/permissions");
886
- return msgpack.decode(Buffer.from(res.data));
933
+ return msgpack.decode(new Uint8Array(res.data));
887
934
  }
888
935
  async deletePermission(permissionID) {
889
936
  await ax.delete(this.getHost() + "/permission/" + permissionID);
@@ -891,7 +938,7 @@ export class Client extends EventEmitter {
891
938
  async retrieveFile(fileID, key) {
892
939
  try {
893
940
  const detailsRes = await ax.get(this.getHost() + "/file/" + fileID + "/details");
894
- const details = msgpack.decode(Buffer.from(detailsRes.data));
941
+ const details = msgpack.decode(new Uint8Array(detailsRes.data));
895
942
  const res = await ax.get(this.getHost() + "/file/" + fileID, {
896
943
  onDownloadProgress: (progressEvent) => {
897
944
  const percentCompleted = Math.round((progressEvent.loaded * 100) /
@@ -908,11 +955,11 @@ export class Client extends EventEmitter {
908
955
  },
909
956
  });
910
957
  const fileData = res.data;
911
- const decrypted = nacl.secretbox.open(Uint8Array.from(Buffer.from(fileData)), XUtils.decodeHex(details.nonce), XUtils.decodeHex(key));
958
+ const decrypted = nacl.secretbox.open(new Uint8Array(fileData), XUtils.decodeHex(details.nonce), XUtils.decodeHex(key));
912
959
  if (decrypted) {
913
960
  const resp = {
914
961
  details,
915
- data: Buffer.from(decrypted),
962
+ data: new Uint8Array(decrypted),
916
963
  };
917
964
  return resp;
918
965
  }
@@ -930,7 +977,7 @@ export class Client extends EventEmitter {
930
977
  */
931
978
  async init() {
932
979
  if (this.hasInit) {
933
- return new Error("You should only call init() once.");
980
+ throw new Error("You should only call init() once.");
934
981
  }
935
982
  this.hasInit = true;
936
983
  await this.populateKeyRing();
@@ -951,11 +998,11 @@ export class Client extends EventEmitter {
951
998
  }
952
999
  // returns the file details and the encryption key
953
1000
  async createFile(file) {
954
- this.log.info("Creating file, size: " + formatBytes(Buffer.byteLength(file)));
1001
+ this.log.info("Creating file, size: " + formatBytes(file.byteLength));
955
1002
  const nonce = xMakeNonce();
956
1003
  const key = nacl.box.keyPair();
957
1004
  const box = nacl.secretbox(Uint8Array.from(file), nonce, key.secretKey);
958
- this.log.info("Encrypted size: " + formatBytes(Buffer.byteLength(box)));
1005
+ this.log.info("Encrypted size: " + formatBytes(box.byteLength));
959
1006
  if (typeof FormData !== "undefined") {
960
1007
  const fpayload = new FormData();
961
1008
  fpayload.set("owner", this.getDevice().deviceID);
@@ -977,7 +1024,7 @@ export class Client extends EventEmitter {
977
1024
  this.emit("fileProgress", progress);
978
1025
  },
979
1026
  });
980
- const fcreatedFile = msgpack.decode(Buffer.from(fres.data));
1027
+ const fcreatedFile = msgpack.decode(new Uint8Array(fres.data));
981
1028
  return [fcreatedFile, XUtils.encodeHex(key.secretKey)];
982
1029
  }
983
1030
  const payload = {
@@ -986,12 +1033,12 @@ export class Client extends EventEmitter {
986
1033
  file: XUtils.encodeBase64(box),
987
1034
  };
988
1035
  const res = await ax.post(this.getHost() + "/file/json", msgpack.encode(payload), { headers: { "Content-Type": "application/msgpack" } });
989
- const createdFile = msgpack.decode(Buffer.from(res.data));
1036
+ const createdFile = msgpack.decode(new Uint8Array(res.data));
990
1037
  return [createdFile, XUtils.encodeHex(key.secretKey)];
991
1038
  }
992
1039
  async getUserList(channelID) {
993
1040
  const res = await ax.post(this.getHost() + "/userList/" + channelID);
994
- return msgpack.decode(Buffer.from(res.data));
1041
+ return msgpack.decode(new Uint8Array(res.data));
995
1042
  }
996
1043
  async markSessionVerified(sessionID) {
997
1044
  return this.database.markSessionVerified(sessionID);
@@ -1040,7 +1087,7 @@ export class Client extends EventEmitter {
1040
1087
  const { status } = result;
1041
1088
  if (status === "rejected") {
1042
1089
  this.log.warn("Message failed.");
1043
- this.log.warn(result);
1090
+ this.log.warn(JSON.stringify(result));
1044
1091
  }
1045
1092
  }
1046
1093
  });
@@ -1073,14 +1120,14 @@ export class Client extends EventEmitter {
1073
1120
  const { status } = result;
1074
1121
  if (status === "rejected") {
1075
1122
  this.log.warn("Message failed.");
1076
- this.log.warn(result);
1123
+ this.log.warn(JSON.stringify(result));
1077
1124
  }
1078
1125
  }
1079
1126
  });
1080
1127
  }
1081
1128
  async createServer(name) {
1082
- const res = await ax.post(this.getHost() + "/server/" + btoa(name));
1083
- return msgpack.decode(Buffer.from(res.data));
1129
+ const res = await ax.post(this.getHost() + "/server/" + globalThis.btoa(name));
1130
+ return msgpack.decode(new Uint8Array(res.data));
1084
1131
  }
1085
1132
  async forward(message) {
1086
1133
  const copy = { ...message };
@@ -1109,7 +1156,7 @@ export class Client extends EventEmitter {
1109
1156
  const { status } = result;
1110
1157
  if (status === "rejected") {
1111
1158
  this.log.warn("Message failed.");
1112
- this.log.warn(result);
1159
+ this.log.warn(JSON.stringify(result));
1113
1160
  }
1114
1161
  }
1115
1162
  });
@@ -1204,12 +1251,12 @@ export class Client extends EventEmitter {
1204
1251
  }
1205
1252
  async getServerList() {
1206
1253
  const res = await ax.get(this.getHost() + "/user/" + this.getUser().userID + "/servers");
1207
- return msgpack.decode(Buffer.from(res.data));
1254
+ return msgpack.decode(new Uint8Array(res.data));
1208
1255
  }
1209
1256
  async createChannel(name, serverID) {
1210
1257
  const body = { name };
1211
1258
  const res = await ax.post(this.getHost() + "/server/" + serverID + "/channels", msgpack.encode(body), { headers: { "Content-Type": "application/msgpack" } });
1212
- return msgpack.decode(Buffer.from(res.data));
1259
+ return msgpack.decode(new Uint8Array(res.data));
1213
1260
  }
1214
1261
  async getDeviceByID(deviceID) {
1215
1262
  if (this.deviceRecords[deviceID]) {
@@ -1225,7 +1272,7 @@ export class Client extends EventEmitter {
1225
1272
  try {
1226
1273
  const res = await ax.get(this.getHost() + "/device/" + deviceID);
1227
1274
  this.log.info("Retrieved device from server.");
1228
- const fetchedDevice = msgpack.decode(Buffer.from(res.data));
1275
+ const fetchedDevice = msgpack.decode(new Uint8Array(res.data));
1229
1276
  this.deviceRecords[deviceID] = fetchedDevice;
1230
1277
  await this.database.saveDevice(fetchedDevice);
1231
1278
  return fetchedDevice;
@@ -1248,7 +1295,7 @@ export class Client extends EventEmitter {
1248
1295
  async getMultiUserDeviceList(userIDs) {
1249
1296
  try {
1250
1297
  const res = await ax.post(this.getHost() + "/deviceList", msgpack.encode(userIDs), { headers: { "Content-Type": "application/msgpack" } });
1251
- const devices = msgpack.decode(Buffer.from(res.data));
1298
+ const devices = msgpack.decode(new Uint8Array(res.data));
1252
1299
  for (const device of devices) {
1253
1300
  this.deviceRecords[device.deviceID] = device;
1254
1301
  }
@@ -1261,7 +1308,7 @@ export class Client extends EventEmitter {
1261
1308
  async getUserDeviceList(userID) {
1262
1309
  try {
1263
1310
  const res = await ax.get(this.getHost() + "/user/" + userID + "/devices");
1264
- const devices = msgpack.decode(Buffer.from(res.data));
1311
+ const devices = msgpack.decode(new Uint8Array(res.data));
1265
1312
  for (const device of devices) {
1266
1313
  this.deviceRecords[device.deviceID] = device;
1267
1314
  }
@@ -1274,7 +1321,7 @@ export class Client extends EventEmitter {
1274
1321
  async getServerByID(serverID) {
1275
1322
  try {
1276
1323
  const res = await ax.get(this.getHost() + "/server/" + serverID);
1277
- return msgpack.decode(Buffer.from(res.data));
1324
+ return msgpack.decode(new Uint8Array(res.data));
1278
1325
  }
1279
1326
  catch (err) {
1280
1327
  return null;
@@ -1283,7 +1330,7 @@ export class Client extends EventEmitter {
1283
1330
  async getChannelByID(channelID) {
1284
1331
  try {
1285
1332
  const res = await ax.get(this.getHost() + "/channel/" + channelID);
1286
- return msgpack.decode(Buffer.from(res.data));
1333
+ return msgpack.decode(new Uint8Array(res.data));
1287
1334
  }
1288
1335
  catch (err) {
1289
1336
  return null;
@@ -1291,7 +1338,7 @@ export class Client extends EventEmitter {
1291
1338
  }
1292
1339
  async getChannelList(serverID) {
1293
1340
  const res = await ax.get(this.getHost() + "/server/" + serverID + "/channels");
1294
- return msgpack.decode(Buffer.from(res.data));
1341
+ return msgpack.decode(new Uint8Array(res.data));
1295
1342
  }
1296
1343
  /* Get the currently logged in user. You cannot call this until
1297
1344
  after the auth event is emitted. */
@@ -1319,7 +1366,7 @@ export class Client extends EventEmitter {
1319
1366
  }
1320
1367
  try {
1321
1368
  const res = await ax.get(this.getHost() + "/user/" + userIdentifier);
1322
- const userRecord = msgpack.decode(Buffer.from(res.data));
1369
+ const userRecord = msgpack.decode(new Uint8Array(res.data));
1323
1370
  this.userRecords[userIdentifier] = userRecord;
1324
1371
  return [userRecord, null];
1325
1372
  }
@@ -1804,13 +1851,23 @@ export class Client extends EventEmitter {
1804
1851
  if (!this.token) {
1805
1852
  throw new Error("No token found, did you call login()?");
1806
1853
  }
1807
- this.conn = new WebSocket(this.prefixes.WS + this.host + "/socket", { headers: { Cookie: "auth=" + this.token } });
1854
+ const wsUrl = this.prefixes.WS + this.host + "/socket";
1855
+ // Pass cookie as option — Node ws forwards it as an upgrade header.
1856
+ // Browser/RN WebSocket ignores the options object entirely,
1857
+ // so the cookie never reaches the server on those platforms.
1858
+ this.conn = new this.adapters.WebSocket(wsUrl, {
1859
+ headers: { Cookie: "auth=" + this.token },
1860
+ });
1808
1861
  this.conn.on("open", () => {
1809
1862
  this.log.info("Connection opened.");
1810
1863
  this.pingInterval = setInterval(this.ping.bind(this), 15000);
1811
1864
  });
1812
1865
  this.conn.on("close", () => {
1813
1866
  this.log.info("Connection closed.");
1867
+ if (this.pingInterval) {
1868
+ clearInterval(this.pingInterval);
1869
+ this.pingInterval = null;
1870
+ }
1814
1871
  if (!this.manuallyClosing) {
1815
1872
  this.emit("disconnect");
1816
1873
  }
@@ -1820,8 +1877,8 @@ export class Client extends EventEmitter {
1820
1877
  });
1821
1878
  this.conn.on("message", async (message) => {
1822
1879
  const [header, msg] = XUtils.unpackMessage(message);
1823
- this.log.debug(chalk.red.bold("INH ") + XUtils.encodeHex(header));
1824
- this.log.debug(chalk.red.bold("IN ") + JSON.stringify(msg, null, 4));
1880
+ this.log.debug(pc.red(pc.bold("INH ") + XUtils.encodeHex(header)));
1881
+ this.log.debug(pc.red(pc.bold("IN ") + JSON.stringify(msg, null, 4)));
1825
1882
  switch (msg.type) {
1826
1883
  case "ping":
1827
1884
  this.pong(msg.transmissionID);
@@ -1899,7 +1956,7 @@ export class Client extends EventEmitter {
1899
1956
  this.getDevice().deviceID +
1900
1957
  "/mail");
1901
1958
  const inbox = msgpack
1902
- .decode(Buffer.from(res.data))
1959
+ .decode(new Uint8Array(res.data))
1903
1960
  .sort((a, b) => b[2].getTime() - a[2].getTime());
1904
1961
  for (const mailDetails of inbox) {
1905
1962
  const [mailHeader, mailBody, timestamp] = mailDetails;
@@ -1925,21 +1982,21 @@ export class Client extends EventEmitter {
1925
1982
  await sleep(i);
1926
1983
  i *= 2;
1927
1984
  }
1928
- this.log.debug(chalk.red.bold("OUTH ") +
1929
- XUtils.encodeHex(header || XUtils.emptyHeader()));
1930
- this.log.debug(chalk.red.bold("OUT ") + JSON.stringify(msg, null, 4));
1985
+ this.log.debug(pc.red(pc.bold("OUTH ") +
1986
+ XUtils.encodeHex(header || XUtils.emptyHeader())));
1987
+ this.log.debug(pc.red(pc.bold("OUT ") + JSON.stringify(msg, null, 4)));
1931
1988
  this.conn.send(XUtils.packMessage(msg, header));
1932
1989
  }
1933
1990
  async retrieveKeyBundle(deviceID) {
1934
1991
  const res = await ax.post(this.getHost() + "/device/" + deviceID + "/keyBundle");
1935
- return msgpack.decode(Buffer.from(res.data));
1992
+ return msgpack.decode(new Uint8Array(res.data));
1936
1993
  }
1937
1994
  async getOTKCount() {
1938
1995
  const res = await ax.get(this.getHost() +
1939
1996
  "/device/" +
1940
1997
  this.getDevice().deviceID +
1941
1998
  "/otk/count");
1942
- return msgpack.decode(Buffer.from(res.data)).count;
1999
+ return msgpack.decode(new Uint8Array(res.data)).count;
1943
2000
  }
1944
2001
  async submitOTK(amount) {
1945
2002
  const otks = [];