@pluv/client 0.38.14 → 0.39.1

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/dist/index.mjs CHANGED
@@ -407,7 +407,9 @@ var debounce = (fn, options) => {
407
407
  var ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1e3;
408
408
  var HEARTBEAT_INTERVAL_MS = 1e4;
409
409
  var PONG_TIMEOUT_MS = 2e3;
410
- var RECONNECT_TIMEOUT_MS = 3e4;
410
+ var RECONNECT_TIMEOUT_MS = 5e3;
411
+ var MIN_RECONNECT_TIMEOUT_MS = 1e3;
412
+ var MAX_RECONNECT_TIMEOUT_MS = 6e4;
411
413
  var ORIGIN_INITIALIZED = "$initialized";
412
414
  var ORIGIN_STORAGE_UPDATED = "$storageUpdated";
413
415
  var DEFAULT_PLUV_CLIENT_ADDON = (input) => ({
@@ -425,6 +427,7 @@ var PluvRoom = class extends AbstractRoom {
425
427
  onAuthorizationFail,
426
428
  presence,
427
429
  publicKey,
430
+ reconnectTimeoutMs = RECONNECT_TIMEOUT_MS,
428
431
  router,
429
432
  wsEndpoint
430
433
  } = options;
@@ -434,7 +437,6 @@ var PluvRoom = class extends AbstractRoom {
434
437
  this._intervals = {
435
438
  heartbeat: null
436
439
  };
437
- this._metadata = {};
438
440
  this._otherNotifier = new OtherNotifier();
439
441
  this._publicKey = null;
440
442
  this._stateNotifier = new StateNotifier();
@@ -445,12 +447,14 @@ var PluvRoom = class extends AbstractRoom {
445
447
  pong: null,
446
448
  reconnect: null
447
449
  };
450
+ this._lastMetadata = null;
448
451
  this._state = {
449
452
  authorization: {
450
453
  token: null,
451
454
  user: null
452
455
  },
453
456
  connection: {
457
+ attempts: 0,
454
458
  count: 0,
455
459
  id: null,
456
460
  state: "Untouched" /* Untouched */
@@ -589,18 +593,14 @@ var PluvRoom = class extends AbstractRoom {
589
593
  };
590
594
  const addon = this._getAddon(addons);
591
595
  const { storage } = __spreadValues(__spreadValues({}, DEFAULT_PLUV_CLIENT_ADDON({ room: this })), addon({ room: this }));
596
+ this.metadata = metadata;
592
597
  this._debug = debug;
593
598
  this._endpoints = { authEndpoint, wsEndpoint };
599
+ this._reconnectTimeoutMs = reconnectTimeoutMs;
594
600
  this._storageStore = storage;
595
- if (typeof metadata !== "undefined") this._metadata = metadata;
596
601
  if (!!publicKey) this._publicKey = publicKey;
597
602
  this._listeners = {
598
603
  onAuthorizationFail: (error) => {
599
- this._clearTimeout(this._timeouts.reconnect);
600
- this._timeouts.reconnect = setTimeout(
601
- this._reconnect.bind(this),
602
- RECONNECT_TIMEOUT_MS
603
- );
604
604
  onAuthorizationFail == null ? void 0 : onAuthorizationFail(error);
605
605
  }
606
606
  };
@@ -614,11 +614,13 @@ var PluvRoom = class extends AbstractRoom {
614
614
  get webSocket() {
615
615
  return this._state.webSocket;
616
616
  }
617
- connect() {
617
+ connect(...args) {
618
618
  return __async(this, null, function* () {
619
- if (!this._endpoints.wsEndpoint) {
620
- throw new Error("Must provide an wsEndpoint.");
621
- }
619
+ var _a;
620
+ const params = (_a = args[0]) != null ? _a : {};
621
+ const metadata = params.metadata;
622
+ this._setMetadata(metadata);
623
+ if (!this._endpoints.wsEndpoint) throw new Error("Must provide a wsEndpoint.");
622
624
  const canConnect = ["Closed" /* Closed */, "Untouched" /* Untouched */].some(
623
625
  (state) => this._state.connection.state === state
624
626
  );
@@ -630,21 +632,27 @@ var PluvRoom = class extends AbstractRoom {
630
632
  });
631
633
  yield this._storageStore.initialize();
632
634
  yield this._applyStorageStore();
633
- const url = new URL(this._getWsEndpoint(this.id));
635
+ const url = new URL(this._getWsEndpoint(this.id, params));
634
636
  let authToken = null;
635
637
  let webSocket;
636
638
  try {
637
- const publicKey = this._getPublicKey();
638
- authToken = yield this._getAuthorization(this.id);
639
+ const publicKey = this._getPublicKey(params);
640
+ authToken = yield this._getAuthorization(this.id, params);
639
641
  authToken && url.searchParams.set("token", encodeURIComponent(authToken));
640
642
  publicKey && url.searchParams.set("public_key", encodeURIComponent(publicKey));
641
643
  webSocket = new WebSocket(url.toString());
642
644
  } catch (err) {
645
+ this._updateState((oldState) => {
646
+ oldState.connection.attempts += 1;
647
+ return oldState;
648
+ });
643
649
  yield this._storageStore.destroy();
644
- this._onAuthorizationFail(err);
650
+ this._pollReconnect();
651
+ yield Promise.resolve(this._onAuthorizationFail(err));
645
652
  return;
646
653
  }
647
654
  this._updateState((oldState) => {
655
+ oldState.connection.attempts = 0;
648
656
  oldState.authorization.token = authToken;
649
657
  oldState.webSocket = webSocket;
650
658
  return oldState;
@@ -655,16 +663,21 @@ var PluvRoom = class extends AbstractRoom {
655
663
  }
656
664
  disconnect() {
657
665
  return __async(this, null, function* () {
658
- if (!this._endpoints.wsEndpoint) {
659
- throw new Error("Must provide an wsEndpoint.");
660
- }
666
+ if (!this._endpoints.wsEndpoint) throw new Error("Must provide a wsEndpoint.");
661
667
  if (!this._state.webSocket) return;
662
668
  const canDisconnect = ["Connecting" /* Connecting */, "Open" /* Open */].some(
663
669
  (state) => this._state.connection.state === state
664
670
  );
665
671
  if (!canDisconnect) return;
666
- this._clearInterval(this._intervals.heartbeat);
667
672
  this._closeWs();
673
+ this._clearInterval(this._intervals.heartbeat);
674
+ this._clearTimeout(this._timeouts.reconnect);
675
+ this._updateState((oldState) => {
676
+ oldState.authorization.token = null;
677
+ oldState.connection.state = "Closed" /* Closed */;
678
+ oldState.webSocket = null;
679
+ return oldState;
680
+ });
668
681
  yield this._storageStore.destroy();
669
682
  this._crdtManager.destroy();
670
683
  this._stateNotifier.subjects["my-presence"].next(null);
@@ -701,9 +714,11 @@ var PluvRoom = class extends AbstractRoom {
701
714
  if (typeof window === "undefined") return;
702
715
  if (typeof document === "undefined") return;
703
716
  this._windowListeners = {
717
+ onNavigatorOffline: this._onNavigatorOffline.bind(this),
704
718
  onNavigatorOnline: this._onNavigatorOnline.bind(this),
705
719
  onVisibilityChange: this._onVisibilityChange.bind(this)
706
720
  };
721
+ window.addEventListener("offline", this._windowListeners.onNavigatorOffline);
707
722
  window.addEventListener("online", this._windowListeners.onNavigatorOnline);
708
723
  document.addEventListener("visibilitychange", this._windowListeners.onVisibilityChange);
709
724
  }
@@ -729,24 +744,17 @@ var PluvRoom = class extends AbstractRoom {
729
744
  clearTimeout(timeout);
730
745
  }
731
746
  _closeWs() {
732
- var _a, _b;
733
- if (!this._state.webSocket) return;
747
+ var _a, _b, _c;
734
748
  (_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
749
+ this._clearInterval(this._intervals.heartbeat);
735
750
  this._clearTimeout(this._timeouts.pong);
736
- this._clearTimeout(this._timeouts.reconnect);
737
751
  this._detachWindowListeners();
738
752
  this._usersManager.removeMyself();
739
753
  this._usersManager.removeUsers();
740
754
  this._otherNotifier.clear();
741
755
  this._stateNotifier.subjects.myself.next(null);
742
756
  this._stateNotifier.subjects.others.next([]);
743
- const canClose = [WebSocket.CONNECTING, WebSocket.OPEN].some(
744
- (readyState) => {
745
- var _a2;
746
- return readyState === ((_a2 = this._state.webSocket) == null ? void 0 : _a2.readyState);
747
- }
748
- );
749
- if (canClose) this._state.webSocket.close();
757
+ (_c = this._state.webSocket) == null ? void 0 : _c.close();
750
758
  this._detachWsListeners();
751
759
  }
752
760
  _detachWindowListeners() {
@@ -777,7 +785,8 @@ var PluvRoom = class extends AbstractRoom {
777
785
  );
778
786
  this._crdtNotifier.rootSubject.next(storageRoot);
779
787
  }
780
- _getAuthFetchOptions(room) {
788
+ _getAuthFetchOptions(room, params) {
789
+ const metadata = params.metadata;
781
790
  if (typeof this._endpoints.authEndpoint === "undefined") return null;
782
791
  if (this._endpoints.authEndpoint === true) {
783
792
  return {
@@ -791,18 +800,16 @@ var PluvRoom = class extends AbstractRoom {
791
800
  options: {}
792
801
  };
793
802
  }
794
- const result = this._endpoints.authEndpoint({ metadata: this._metadata, room });
803
+ const result = this._endpoints.authEndpoint({ metadata, room });
795
804
  return typeof result === "string" ? { url: result, options: {} } : result;
796
805
  }
797
- _getAuthorization(room) {
806
+ _getAuthorization(room, params) {
798
807
  return __async(this, null, function* () {
799
- const fetchOptions = this._getAuthFetchOptions(room);
808
+ const fetchOptions = this._getAuthFetchOptions(room, params);
800
809
  if (!fetchOptions) return null;
801
810
  const { url, options } = fetchOptions;
802
811
  const res = yield fetch(url, options);
803
- if (!res.ok || res.status !== 200) {
804
- throw new Error("Room is unauthorized");
805
- }
812
+ if (!res.ok || res.status !== 200) throw new Error("Room is unauthorized");
806
813
  try {
807
814
  return yield res.text().then((text) => text.trim());
808
815
  } catch (err) {
@@ -810,19 +817,21 @@ var PluvRoom = class extends AbstractRoom {
810
817
  }
811
818
  });
812
819
  }
813
- _getPublicKey() {
820
+ _getPublicKey(params) {
821
+ const metadata = params.metadata;
814
822
  if (!this._publicKey) return null;
815
823
  if (typeof this._publicKey === "string") return this._publicKey;
816
- return this._publicKey({ metadata: this._metadata });
824
+ return this._publicKey({ metadata });
817
825
  }
818
- _getWsEndpoint(room) {
826
+ _getWsEndpoint(room, params) {
827
+ const metadata = params.metadata;
819
828
  switch (typeof this._endpoints.wsEndpoint) {
820
829
  case "undefined":
821
- return !!this._getPublicKey() ? `wss://rooms.pluv.io/api/room/${room}` : `/api/pluv/room/${room}`;
830
+ return !!this._getPublicKey(params) ? `wss://rooms.pluv.io/api/room/${room}` : `/api/pluv/room/${room}`;
822
831
  case "string":
823
832
  return this._endpoints.wsEndpoint;
824
833
  default:
825
- return this._endpoints.wsEndpoint({ metadata: this._metadata, room });
834
+ return this._endpoints.wsEndpoint({ metadata, room });
826
835
  }
827
836
  }
828
837
  _handleExit(message) {
@@ -989,23 +998,30 @@ var PluvRoom = class extends AbstractRoom {
989
998
  return oldState;
990
999
  });
991
1000
  }
992
- /**
993
- * TODO
994
- * @description Determine if ws was closed with a code for closing without retries.
995
- * Otherwise, attempt to retry again with incremental backoff
996
- * @author leedavidcs
997
- * @date August 19, 2022
998
- */
999
1001
  _onClose(event) {
1000
1002
  return __async(this, null, function* () {
1001
1003
  this._logDebug("WebSocket closed");
1002
1004
  !!event.reason && this._logDebug(event.reason);
1003
- this._clearInterval(this._intervals.heartbeat);
1004
- this._clearTimeout(this._timeouts.pong);
1005
- this._stateNotifier.subjects["myself"].next(null);
1005
+ const shouldRetry = [
1006
+ // Going away: Client/server is shutting down or navigating
1007
+ 1001,
1008
+ // Abnormal closure: Usually network error
1009
+ 1006,
1010
+ // Internal error: may be a larger problem, but try reconnecting for now
1011
+ 1011,
1012
+ // Service restart: Server restarted
1013
+ 1012,
1014
+ // Try again later: server overloaded
1015
+ 1013,
1016
+ // Bad gateway
1017
+ 1014
1018
+ ].some((okToRetryCode) => event.code === okToRetryCode);
1019
+ this._closeWs();
1020
+ if (!shouldRetry) this._clearTimeout(this._timeouts.reconnect);
1021
+ else this._pollReconnect();
1006
1022
  this._updateState((oldState) => {
1007
1023
  oldState.authorization.token = null;
1008
- oldState.connection.state = "Closed" /* Closed */;
1024
+ oldState.connection.state = shouldRetry ? "Unavailable" /* Unavailable */ : "Closed" /* Closed */;
1009
1025
  oldState.webSocket = null;
1010
1026
  return oldState;
1011
1027
  });
@@ -1014,7 +1030,6 @@ var PluvRoom = class extends AbstractRoom {
1014
1030
  /**
1015
1031
  * TODO
1016
1032
  * @description Handle websocket errors in a meaningful way, should they occur
1017
- * @author leedavidcs
1018
1033
  * @date August 19, 2022
1019
1034
  */
1020
1035
  _onError() {
@@ -1073,11 +1088,18 @@ var PluvRoom = class extends AbstractRoom {
1073
1088
  this._clearInterval(this._intervals.heartbeat);
1074
1089
  this._intervals.heartbeat = setInterval(this._heartbeat.bind(this), HEARTBEAT_INTERVAL_MS);
1075
1090
  }
1091
+ _onNavigatorOffline() {
1092
+ return __async(this, null, function* () {
1093
+ if (this._state.connection.state === "Closed" /* Closed */) return;
1094
+ this._updateState((oldState) => {
1095
+ oldState.connection.state = "Unavailable" /* Unavailable */;
1096
+ return oldState;
1097
+ });
1098
+ });
1099
+ }
1076
1100
  _onNavigatorOnline() {
1077
1101
  return __async(this, null, function* () {
1078
- if (this._state.connection.state !== "Unavailable" /* Unavailable */) {
1079
- return;
1080
- }
1102
+ if (this._state.connection.state !== "Unavailable" /* Unavailable */) return;
1081
1103
  yield this._reconnect();
1082
1104
  });
1083
1105
  }
@@ -1109,6 +1131,12 @@ var PluvRoom = class extends AbstractRoom {
1109
1131
  return null;
1110
1132
  }
1111
1133
  }
1134
+ _pollReconnect() {
1135
+ this._clearTimeout(this._timeouts.reconnect);
1136
+ const timeoutMs = typeof this._reconnectTimeoutMs === "number" ? this._reconnectTimeoutMs : this._reconnectTimeoutMs({ attempts: this._state.connection.attempts });
1137
+ const clampedMs = Math.max(Math.min(MIN_RECONNECT_TIMEOUT_MS, timeoutMs), MAX_RECONNECT_TIMEOUT_MS);
1138
+ this._timeouts.reconnect = setTimeout(this._reconnect.bind(this), clampedMs);
1139
+ }
1112
1140
  _reconnect() {
1113
1141
  return __async(this, null, function* () {
1114
1142
  if (!this._endpoints.wsEndpoint) {
@@ -1116,13 +1144,9 @@ var PluvRoom = class extends AbstractRoom {
1116
1144
  }
1117
1145
  if (!this._state.webSocket) return;
1118
1146
  this._closeWs();
1119
- this._updateState((oldState) => {
1120
- oldState.authorization.token = null;
1121
- oldState.connection.state = "Closed" /* Closed */;
1122
- oldState.webSocket = null;
1123
- return oldState;
1124
- });
1125
- yield this.connect();
1147
+ const metadata = this._lastMetadata;
1148
+ const params = typeof metadata === "undefined" ? [] : [{ metadata }];
1149
+ yield this.connect(...params);
1126
1150
  });
1127
1151
  }
1128
1152
  _sendMessage(data) {
@@ -1143,6 +1167,9 @@ var PluvRoom = class extends AbstractRoom {
1143
1167
  const message = typeof data === "string" ? data : JSON.stringify(data);
1144
1168
  webSocket.send(message);
1145
1169
  }
1170
+ _setMetadata(metadata) {
1171
+ return this.metadata ? this.metadata.parse(metadata) : metadata;
1172
+ }
1146
1173
  _updateState(updater) {
1147
1174
  let authorization;
1148
1175
  try {
@@ -1172,17 +1199,17 @@ var PluvClient = class {
1172
1199
  var _a;
1173
1200
  const oldRoom = this.getRoom(room);
1174
1201
  if (oldRoom) return oldRoom;
1175
- const metadata = this._metadata ? this._metadata.parse(options.metadata) : options.metadata;
1176
1202
  const newRoom = new PluvRoom(room, {
1177
1203
  addons: options.addons,
1178
1204
  authEndpoint: this._authEndpoint,
1179
1205
  debug: options.debug,
1180
1206
  initialPresence: options.initialPresence,
1181
1207
  initialStorage: this._initialStorage,
1182
- metadata,
1208
+ metadata: this.metadata,
1183
1209
  onAuthorizationFail: options.onAuthorizationFail,
1184
1210
  presence: this._presence,
1185
1211
  publicKey: (_a = this._publicKey) != null ? _a : void 0,
1212
+ reconnectTimeoutMs: options.reconnectTimeoutMs,
1186
1213
  router: options.router,
1187
1214
  wsEndpoint: this._wsEndpoint
1188
1215
  });
@@ -1190,11 +1217,11 @@ var PluvClient = class {
1190
1217
  this._logDebug(`New room was created: ${room}`);
1191
1218
  return newRoom;
1192
1219
  };
1193
- this.enter = (room) => __async(this, null, function* () {
1220
+ this.enter = (room, ...args) => __async(this, null, function* () {
1194
1221
  const toEnter = typeof room === "string" ? this.getRoom(room) : room;
1195
1222
  if (!toEnter) throw new Error(`Could not find room: ${room}.`);
1196
1223
  this._rooms.set(toEnter.id, toEnter);
1197
- yield toEnter.connect();
1224
+ yield toEnter.connect(...args);
1198
1225
  this._logDebug(`Entered room: ${room}`);
1199
1226
  return toEnter;
1200
1227
  });
@@ -1213,10 +1240,10 @@ var PluvClient = class {
1213
1240
  this._logDebug(`Left and deleted room: ${room}`);
1214
1241
  });
1215
1242
  const { authEndpoint, debug = false, initialStorage, metadata, presence, publicKey, wsEndpoint } = options;
1243
+ this.metadata = metadata;
1216
1244
  this._authEndpoint = authEndpoint;
1217
1245
  this._debug = debug;
1218
1246
  this._initialStorage = initialStorage;
1219
- this._metadata = metadata;
1220
1247
  this._presence = presence;
1221
1248
  this._wsEndpoint = wsEndpoint;
1222
1249
  if (!!publicKey) this._publicKey = publicKey;
@@ -1265,6 +1292,7 @@ var MockedRoom = class extends AbstractRoom {
1265
1292
  user: null
1266
1293
  },
1267
1294
  connection: {
1295
+ attempts: 0,
1268
1296
  count: 1,
1269
1297
  id: null,
1270
1298
  state: "Untouched" /* Untouched */
@@ -0,0 +1,4 @@
1
+ import { config } from "eslint-config-pluv/base";
2
+
3
+ /** @type {import("eslint").Linter.Config} */
4
+ export default config;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/client",
3
- "version": "0.38.14",
3
+ "version": "0.39.1",
4
4
  "description": "@pluv/io frontend client",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -18,20 +18,20 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "wonka": "^6.3.5",
21
- "@pluv/crdt": "^0.38.14",
22
- "@pluv/types": "^0.38.14"
21
+ "@pluv/crdt": "^0.39.1",
22
+ "@pluv/types": "^0.39.1"
23
23
  },
24
24
  "devDependencies": {
25
- "eslint": "^8.57.1",
25
+ "eslint": "^9.24.0",
26
26
  "tsup": "^8.4.0",
27
27
  "typescript": "^5.8.3",
28
- "@pluv/tsconfig": "^0.38.14",
29
- "eslint-config-pluv": "^0.38.14"
28
+ "@pluv/tsconfig": "^0.39.1",
29
+ "eslint-config-pluv": "^0.39.1"
30
30
  },
31
31
  "scripts": {
32
32
  "build": "tsup src/index.ts --format esm,cjs --dts",
33
33
  "dev": "tsup src/index.ts --format esm,cjs --watch --dts",
34
- "lint": "eslint src/**/*.ts* --fix",
34
+ "lint": "eslint src/**/*.ts* --fix --max-warnings 0",
35
35
  "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
36
36
  }
37
37
  }
package/src/MockedRoom.ts CHANGED
@@ -59,6 +59,7 @@ export class MockedRoom<
59
59
  user: null,
60
60
  },
61
61
  connection: {
62
+ attempts: 0,
62
63
  count: 1,
63
64
  id: null,
64
65
  state: ConnectionState.Untouched,
package/src/PluvClient.ts CHANGED
@@ -2,11 +2,19 @@ import type { AbstractCrdtDocFactory, CrdtType } from "@pluv/crdt";
2
2
  import type { InputZodLike, IOLike, JsonObject } from "@pluv/types";
3
3
  import type { InferCallback } from "./infer";
4
4
  import { PluvProcedure } from "./PluvProcedure";
5
- import type { AuthEndpoint, PluvRoomAddon, PluvRoomDebug, RoomConfig, RoomEndpoints, WsEndpoint } from "./PluvRoom";
5
+ import type {
6
+ AuthEndpoint,
7
+ PluvRoomAddon,
8
+ PluvRoomDebug,
9
+ ReconnectTimeoutMs,
10
+ RoomConfig,
11
+ RoomEndpoints,
12
+ WsEndpoint,
13
+ } from "./PluvRoom";
6
14
  import { PluvRoom } from "./PluvRoom";
7
15
  import type { PluvRouterEventConfig } from "./PluvRouter";
8
16
  import { PluvRouter } from "./PluvRouter";
9
- import type { PublicKey } from "./types";
17
+ import type { PublicKey, WithMetadata } from "./types";
10
18
 
11
19
  export type PluvClientOptions<
12
20
  TIO extends IOLike<any, any>,
@@ -34,8 +42,13 @@ export type CreateRoomOptions<
34
42
  initialPresence?: TPresence;
35
43
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
36
44
  onAuthorizationFail?: (error: Error) => void;
45
+ reconnectTimeoutMs?: ReconnectTimeoutMs;
37
46
  router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
38
- } & (keyof TMetadata extends never ? { metadata?: undefined } : { metadata: TMetadata });
47
+ };
48
+
49
+ export type EnterRoomParams<TMetadata extends JsonObject = {}> = keyof TMetadata extends never
50
+ ? []
51
+ : [WithMetadata<TMetadata>];
39
52
 
40
53
  export class PluvClient<
41
54
  TIO extends IOLike<any, any>,
@@ -43,10 +56,11 @@ export class PluvClient<
43
56
  TStorage extends Record<string, CrdtType<any, any>> = {},
44
57
  TMetadata extends JsonObject = {},
45
58
  > {
59
+ public readonly metadata?: InputZodLike<TMetadata>;
60
+
46
61
  private readonly _authEndpoint: AuthEndpoint<TMetadata> | undefined;
47
62
  private readonly _debug: boolean;
48
63
  private readonly _initialStorage?: AbstractCrdtDocFactory<TStorage>;
49
- private readonly _metadata?: InputZodLike<TMetadata>;
50
64
  private readonly _presence?: InputZodLike<TPresence>;
51
65
  private readonly _publicKey: PublicKey<TMetadata> | null = null;
52
66
  private readonly _rooms = new Map<string, PluvRoom<TIO, TMetadata, TPresence, TStorage, any>>();
@@ -65,10 +79,11 @@ export class PluvClient<
65
79
  constructor(options: PluvClientOptions<TIO, TPresence, TStorage, TMetadata>) {
66
80
  const { authEndpoint, debug = false, initialStorage, metadata, presence, publicKey, wsEndpoint } = options;
67
81
 
82
+ this.metadata = metadata;
83
+
68
84
  this._authEndpoint = authEndpoint as AuthEndpoint<TMetadata>;
69
85
  this._debug = debug;
70
86
  this._initialStorage = initialStorage;
71
- this._metadata = metadata;
72
87
  this._presence = presence;
73
88
  this._wsEndpoint = wsEndpoint;
74
89
 
@@ -83,18 +98,17 @@ export class PluvClient<
83
98
 
84
99
  if (oldRoom) return oldRoom;
85
100
 
86
- const metadata = this._metadata ? this._metadata.parse(options.metadata) : options.metadata;
87
-
88
101
  const newRoom = new PluvRoom<TIO, TMetadata, TPresence, TStorage, TEvents>(room, {
89
102
  addons: options.addons,
90
103
  authEndpoint: this._authEndpoint,
91
104
  debug: options.debug,
92
105
  initialPresence: options.initialPresence,
93
106
  initialStorage: this._initialStorage,
94
- metadata,
107
+ metadata: this.metadata,
95
108
  onAuthorizationFail: options.onAuthorizationFail,
96
109
  presence: this._presence,
97
110
  publicKey: this._publicKey ?? undefined,
111
+ reconnectTimeoutMs: options.reconnectTimeoutMs,
98
112
  router: options.router,
99
113
  wsEndpoint: this._wsEndpoint,
100
114
  } as RoomConfig<TIO, TMetadata, TPresence, TStorage, TEvents>);
@@ -107,15 +121,15 @@ export class PluvClient<
107
121
  };
108
122
 
109
123
  public enter = async (
110
- room: string | PluvRoom<TIO, any, any, any>,
124
+ room: string | PluvRoom<TIO, TMetadata, any, any, any>,
125
+ ...args: EnterRoomParams<TMetadata>
111
126
  ): Promise<PluvRoom<TIO, TMetadata, TPresence, TStorage, any>> => {
112
127
  const toEnter = typeof room === "string" ? this.getRoom(room) : room;
113
128
 
114
129
  if (!toEnter) throw new Error(`Could not find room: ${room}.`);
115
130
 
116
131
  this._rooms.set(toEnter.id, toEnter);
117
-
118
- await toEnter.connect();
132
+ await toEnter.connect(...args);
119
133
 
120
134
  this._logDebug(`Entered room: ${room}`);
121
135