@pluv/client 1.0.0 → 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.
package/src/MockedRoom.ts CHANGED
@@ -86,8 +86,13 @@ export class MockedRoom<
86
86
  presenceMaxSize: MAX_PRESENCE_SIZE_BYTES,
87
87
  ...limits,
88
88
  };
89
- this._router = router ?? (new PluvRouter({}) as PluvRouter<TIO, TPresence, TStorage, TEvents>);
90
- this._usersManager = new UsersManager<TIO, TPresence>({ initialPresence, limits: this._limits, presence });
89
+ this._router =
90
+ router ?? (new PluvRouter({}) as PluvRouter<TIO, TPresence, TStorage, TEvents>);
91
+ this._usersManager = new UsersManager<TIO, TPresence>({
92
+ initialPresence,
93
+ limits: this._limits,
94
+ presence,
95
+ });
91
96
 
92
97
  this._crdtManager = new CrdtManager<TStorage>({
93
98
  initialStorage,
@@ -136,10 +141,9 @@ export class MockedRoom<
136
141
  user: myself,
137
142
  };
138
143
 
139
- const output = await (procedure.config.broadcast as EventResolver<TIO, any, any, TPresence, TStorage>)(
140
- parsed,
141
- context,
142
- );
144
+ const output = await (
145
+ procedure.config.broadcast as EventResolver<TIO, any, any, TPresence, TStorage>
146
+ )(parsed, context);
143
147
 
144
148
  Object.entries(output).forEach(([_type, _data]) => {
145
149
  this._simulateEvent(_type as TEvent, _data as any);
@@ -147,7 +151,9 @@ export class MockedRoom<
147
151
  },
148
152
  {
149
153
  get(fn, prop) {
150
- return async (data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[any]>): Promise<void> => {
154
+ return async (
155
+ data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[any]>,
156
+ ): Promise<void> => {
151
157
  return await fn(prop, data);
152
158
  };
153
159
  },
@@ -174,8 +180,9 @@ export class MockedRoom<
174
180
  ): (() => void) => this._eventNotifier.subscribe(event, callback),
175
181
  {
176
182
  get(fn, prop) {
177
- return (callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>): (() => void) =>
178
- fn(prop as any, callback);
183
+ return (
184
+ callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>,
185
+ ): (() => void) => fn(prop as any, callback);
179
186
  },
180
187
  },
181
188
  ) as (<TEvent extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
@@ -221,7 +228,9 @@ export class MockedRoom<
221
228
  };
222
229
 
223
230
  public getStorageJson(): InferCrdtJson<TStorage> | null;
224
- public getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
231
+ public getStorageJson<TKey extends keyof TStorage>(
232
+ type: TKey,
233
+ ): InferCrdtJson<TStorage[TKey]> | null;
225
234
  public getStorageJson<TKey extends keyof TStorage>(type?: TKey) {
226
235
  if (this._state.connection.id === null) return null;
227
236
 
@@ -293,7 +302,8 @@ export class MockedRoom<
293
302
  };
294
303
 
295
304
  public updateMyPresence = (presence: UpdateMyPresenceAction<TPresence>): void => {
296
- const newPresence = typeof presence === "function" ? presence(this.getMyPresence()) : presence;
305
+ const newPresence =
306
+ typeof presence === "function" ? presence(this.getMyPresence()) : presence;
297
307
 
298
308
  this._usersManager.updateMyPresence(newPresence);
299
309
 
@@ -8,7 +8,10 @@ export type OtherNotifierSubscriptionCallback<TIO extends IOLike, TPresence exte
8
8
  ) => void;
9
9
 
10
10
  export class OtherNotifier<TIO extends IOLike, TPresence extends JsonObject = {}> {
11
- private _subjects = new Map<[clientId: string][0], Subject<Id<UserInfo<TIO, TPresence>> | null>>();
11
+ private _subjects = new Map<
12
+ [clientId: string][0],
13
+ Subject<Id<UserInfo<TIO, TPresence>> | null>
14
+ >();
12
15
 
13
16
  public clear(): void {
14
17
  this._subjects.forEach((subject) => {
@@ -35,7 +38,10 @@ export class OtherNotifier<TIO extends IOLike, TPresence extends JsonObject = {}
35
38
  return newSubject;
36
39
  }
37
40
 
38
- public subscribe(clientId: string, callback: OtherNotifierSubscriptionCallback<TIO, TPresence>): () => void {
41
+ public subscribe(
42
+ clientId: string,
43
+ callback: OtherNotifierSubscriptionCallback<TIO, TPresence>,
44
+ ): () => void {
39
45
  const source = this.subject(clientId).source;
40
46
 
41
47
  return subscribe(callback)(source).unsubscribe;
package/src/PluvClient.ts CHANGED
@@ -158,7 +158,9 @@ export class PluvClient<
158
158
  };
159
159
 
160
160
  public getRoom = (room: string): PluvRoom<TIO, TMetadata, TPresence, TStorage, any> | null => {
161
- const found = this._rooms.get(room) as PluvRoom<TIO, TMetadata, TPresence, TStorage, any> | undefined;
161
+ const found = this._rooms.get(room) as
162
+ | PluvRoom<TIO, TMetadata, TPresence, TStorage, any>
163
+ | undefined;
162
164
 
163
165
  return found ?? null;
164
166
  };
@@ -21,7 +21,8 @@ export class PluvProcedure<
21
21
  TStorage extends Record<string, CrdtType<any, any>> = {},
22
22
  TFilled extends "input" | "broadcast" | "" = "",
23
23
  > {
24
- private _broadcast: EventResolver<TIO, TInput, Partial<TOutput>, TPresence, TStorage> | null = null;
24
+ private _broadcast: EventResolver<TIO, TInput, Partial<TOutput>, TPresence, TStorage> | null =
25
+ null;
25
26
  private _input: InputZodLike<TInput> | null = null;
26
27
 
27
28
  public get config(): ProcedureLike<TInput, TOutput>["config"] {
@@ -71,7 +72,10 @@ export class PluvProcedure<
71
72
 
72
73
  public input<TData extends JsonObject>(
73
74
  input: InputZodLike<TData>,
74
- ): Omit<PluvProcedure<TIO, TData, {}, TPresence, TStorage, TFilled | "input">, TFilled | "input"> {
75
+ ): Omit<
76
+ PluvProcedure<TIO, TData, {}, TPresence, TStorage, TFilled | "input">,
77
+ TFilled | "input"
78
+ > {
75
79
  return new PluvProcedure<TIO, TData, {}, TPresence, TStorage, TFilled | "input">({ input });
76
80
  }
77
81
 
package/src/PluvRoom.ts CHANGED
@@ -109,7 +109,9 @@ export type AuthEndpoint<TMetadata extends JsonObject> =
109
109
  | string
110
110
  | ((params: EndpointParams<TMetadata>) => string | FetchOptions)
111
111
  | true;
112
- export type WsEndpoint<TMetadata extends JsonObject> = string | ((params: EndpointParams<TMetadata>) => string);
112
+ export type WsEndpoint<TMetadata extends JsonObject> =
113
+ | string
114
+ | ((params: EndpointParams<TMetadata>) => string);
113
115
 
114
116
  type FetchOptions = { url: string; options?: RequestInit };
115
117
 
@@ -128,7 +130,9 @@ export type PluvRoomAddon<
128
130
  TMetadata extends JsonObject = {},
129
131
  TPresence extends JsonObject = {},
130
132
  TStorage extends Record<string, CrdtType<any, any>> = {},
131
- > = (input: PluvRoomAddonInput<TIO, TMetadata, TPresence, TStorage>) => Partial<PluvRoomAddonResult>;
133
+ > = (
134
+ input: PluvRoomAddonInput<TIO, TMetadata, TPresence, TStorage>,
135
+ ) => Partial<PluvRoomAddonResult>;
132
136
 
133
137
  export interface PluvRoomAddonInput<
134
138
  TIO extends IOLike = IOLike,
@@ -276,8 +280,13 @@ export class PluvRoom<
276
280
  },
277
281
  };
278
282
 
279
- this._router = router ?? (new PluvRouter({}) as PluvRouter<TIO, TPresence, TStorage, TEvents>);
280
- this._usersManager = new UsersManager<TIO, TPresence>({ initialPresence, limits: this._limits, presence });
283
+ this._router =
284
+ router ?? (new PluvRouter({}) as PluvRouter<TIO, TPresence, TStorage, TEvents>);
285
+ this._usersManager = new UsersManager<TIO, TPresence>({
286
+ initialPresence,
287
+ limits: this._limits,
288
+ presence,
289
+ });
281
290
  this._crdtManager = new CrdtManager<TStorage>({ initialStorage });
282
291
  }
283
292
 
@@ -325,10 +334,9 @@ export class PluvRoom<
325
334
  user: myself,
326
335
  };
327
336
 
328
- const output = await (procedure.config.broadcast as EventResolver<TIO, any, any, TPresence, TStorage>)(
329
- parsed,
330
- context,
331
- );
337
+ const output = await (
338
+ procedure.config.broadcast as EventResolver<TIO, any, any, TPresence, TStorage>
339
+ )(parsed, context);
332
340
 
333
341
  Object.entries(output).forEach(([_type, _data]) => {
334
342
  this._sendMessage({ data: _data, type: _type });
@@ -336,7 +344,9 @@ export class PluvRoom<
336
344
  },
337
345
  {
338
346
  get(fn, prop) {
339
- return async (data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[any]>): Promise<void> => {
347
+ return async (
348
+ data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[any]>,
349
+ ): Promise<void> => {
340
350
  return await fn(prop, data);
341
351
  };
342
352
  },
@@ -364,9 +374,11 @@ export class PluvRoom<
364
374
 
365
375
  this._setMetadata(metadata);
366
376
 
367
- const canConnect = [ConnectionState.Closed, ConnectionState.Unavailable, ConnectionState.Untouched].some(
368
- (state) => this._state.connection.state === state,
369
- );
377
+ const canConnect = [
378
+ ConnectionState.Closed,
379
+ ConnectionState.Unavailable,
380
+ ConnectionState.Untouched,
381
+ ].some((state) => this._state.connection.state === state);
370
382
 
371
383
  if (!canConnect) return;
372
384
 
@@ -378,7 +390,6 @@ export class PluvRoom<
378
390
  });
379
391
 
380
392
  await this._storageStore.initialize();
381
- await this._applyStorageStore();
382
393
 
383
394
  const wsEndpoint = this._getWsEndpoint(this.id, params);
384
395
  const url = new URL(wsEndpoint);
@@ -453,8 +464,9 @@ export class PluvRoom<
453
464
  ): (() => void) => this._eventNotifier.subscribe(event, callback),
454
465
  {
455
466
  get(fn, prop) {
456
- return (callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>): (() => void) =>
457
- fn(prop as any, callback);
467
+ return (
468
+ callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>,
469
+ ): (() => void) => fn(prop as any, callback);
458
470
  },
459
471
  },
460
472
  ) as (<TEvent extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
@@ -500,7 +512,9 @@ export class PluvRoom<
500
512
  };
501
513
 
502
514
  public getStorageJson(): InferCrdtJson<TStorage> | null;
503
- public getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
515
+ public getStorageJson<TKey extends keyof TStorage>(
516
+ type: TKey,
517
+ ): InferCrdtJson<TStorage[TKey]> | null;
504
518
  public getStorageJson<TKey extends keyof TStorage>(type?: TKey) {
505
519
  if (this._state.connection.id === null) return null;
506
520
 
@@ -574,7 +588,8 @@ export class PluvRoom<
574
588
  };
575
589
 
576
590
  public updateMyPresence = (presence: UpdateMyPresenceAction<TPresence>): void => {
577
- const newPresence = typeof presence === "function" ? presence(this.getMyPresence()) : presence;
591
+ const newPresence =
592
+ typeof presence === "function" ? presence(this.getMyPresence()) : presence;
578
593
 
579
594
  this._usersManager.updateMyPresence(newPresence);
580
595
 
@@ -596,22 +611,6 @@ export class PluvRoom<
596
611
  this._flattenStorageStore();
597
612
  }
598
613
 
599
- private async _applyStorageStore(): Promise<void> {
600
- const updates = await this._storageStore.getUpdates();
601
-
602
- this._crdtManager.initialize({
603
- onInitialized: () => {
604
- this._stateNotifier.subjects["storage-loaded"].next(true);
605
-
606
- this._observeCrdt();
607
- },
608
- origin: ORIGIN_INITIALIZED,
609
- update: updates,
610
- });
611
-
612
- this._emitSharedTypes();
613
- }
614
-
615
614
  private _attachWindowListeners(): void {
616
615
  if (typeof window === "undefined") return;
617
616
  if (typeof document === "undefined") return;
@@ -722,7 +721,7 @@ export class PluvRoom<
722
721
 
723
722
  const updates = await this._storageStore.getUpdates();
724
723
 
725
- this._crdtManager.applyUpdate(updates, this);
724
+ this._crdtManager.applyUpdate({ update: updates, origin: this });
726
725
 
727
726
  const encodedState = this._crdtManager.doc.getEncodedState();
728
727
 
@@ -743,7 +742,10 @@ export class PluvRoom<
743
742
  );
744
743
  };
745
744
 
746
- private _getAuthFetchOptions(room: string, params: GetAuthEndpointParams<TMetadata>): FetchOptions | null {
745
+ private _getAuthFetchOptions(
746
+ room: string,
747
+ params: GetAuthEndpointParams<TMetadata>,
748
+ ): FetchOptions | null {
747
749
  const metadata = params.metadata as TMetadata;
748
750
 
749
751
  if (typeof this._endpoints.authEndpoint === "undefined") return null;
@@ -767,7 +769,10 @@ export class PluvRoom<
767
769
  return typeof result === "string" ? { url: result, options: {} } : result;
768
770
  }
769
771
 
770
- private async _getAuthorization(room: string, params: GetAuthEndpointParams<TMetadata>): Promise<string | null> {
772
+ private async _getAuthorization(
773
+ room: string,
774
+ params: GetAuthEndpointParams<TMetadata>,
775
+ ): Promise<string | null> {
771
776
  const fetchOptions = this._getAuthFetchOptions(room, params);
772
777
 
773
778
  if (!fetchOptions) return null;
@@ -798,7 +803,9 @@ export class PluvRoom<
798
803
 
799
804
  switch (typeof this._endpoints.wsEndpoint) {
800
805
  case "undefined":
801
- return !!this._getPublicKey(params) ? `wss://rooms.pluv.io/api/room/${room}` : `/api/pluv/room/${room}`;
806
+ return !!this._getPublicKey(params)
807
+ ? `wss://rooms.pluv.io/api/room/${room}`
808
+ : `/api/pluv/room/${room}`;
802
809
  case "string":
803
810
  return this._endpoints.wsEndpoint;
804
811
  default:
@@ -917,7 +924,7 @@ export class PluvRoom<
917
924
  this._stateNotifier.subjects.others.next(others);
918
925
  }
919
926
 
920
- private _handleRegisteredMessage(message: IOEventMessage<TIO>): void {
927
+ private async _handleRegisteredMessage(message: IOEventMessage<TIO>): Promise<void> {
921
928
  const { connectionId } = message;
922
929
  const user = message.user as Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;
923
930
 
@@ -926,6 +933,7 @@ export class PluvRoom<
926
933
  if (!this._state.webSocket) throw new Error("Could not find WebSocket");
927
934
 
928
935
  const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>["$registered"];
936
+ const state = data.state;
929
937
 
930
938
  this._updateState((oldState) => {
931
939
  oldState.connection.count += 1;
@@ -948,7 +956,10 @@ export class PluvRoom<
948
956
  this._stateNotifier.subjects["my-presence"].next(presence);
949
957
  this._stateNotifier.subjects.myself.next(myself);
950
958
 
951
- const update = this._state.connection.count > 1 ? (this._crdtManager.doc.getEncodedState() ?? null) : null;
959
+ const update = !state
960
+ ? (this._crdtManager.resolveEncodedState(await this._storageStore.getUpdates()) ??
961
+ this._crdtManager.getInitialState())
962
+ : this._crdtManager.getInitialState();
952
963
 
953
964
  this._sendMessage({
954
965
  type: "$initializeSession",
@@ -964,30 +975,45 @@ export class PluvRoom<
964
975
  if (!this._state.webSocket) throw new Error("Could not find WebSocket");
965
976
 
966
977
  const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>["$storageReceived"];
978
+ const changeKind = data.changeKind;
979
+ const state = data.state;
967
980
 
968
- this._crdtManager.initialize({
969
- onInitialized: async (update) => {
970
- this._addToStorageStore(update);
971
- this._emitSharedTypes();
981
+ const updates = await this._storageStore.getUpdates();
982
+ const origin = ORIGIN_INITIALIZED;
972
983
 
973
- this._observeCrdt();
974
- this._stateNotifier.subjects["storage-loaded"].next(true);
975
- },
976
- origin: ORIGIN_INITIALIZED,
977
- update: data.state,
978
- });
984
+ const updateStorage = (update: string | readonly string[]) => {
985
+ return this._crdtManager.initialized
986
+ ? this._crdtManager.applyUpdate({ update, origin })
987
+ : this._crdtManager.initialize({ update, origin });
988
+ };
979
989
 
980
- const encodedState = this._crdtManager.doc.getEncodedState();
990
+ if (changeKind === "empty") {
991
+ this._crdtManager.destroy();
992
+ this._crdtManager.initialize({ origin, update: updates });
993
+ } else if (changeKind === "initialized") {
994
+ if (!!updates.length) updateStorage(updates);
995
+ else {
996
+ const encodedState = updateStorage(state).doc.getEncodedState();
981
997
 
982
- await this._storageStore.addUpdate(encodedState);
998
+ this._addToStorageStore(encodedState);
999
+ }
1000
+ } else {
1001
+ const encodedState = updateStorage(state).doc.getEncodedState();
983
1002
 
984
- this._emitSharedTypes();
1003
+ if (!!updates.length) this._crdtManager.applyUpdate({ update: updates, origin });
985
1004
 
986
- const update = this._crdtManager.doc.getEncodedState();
1005
+ await this._addToStorageStore(encodedState);
1006
+ }
1007
+
1008
+ const encodedState = this._crdtManager.doc.getEncodedState();
1009
+
1010
+ this._emitSharedTypes();
1011
+ this._observeCrdt();
1012
+ this._stateNotifier.subjects["storage-loaded"].next(true);
987
1013
 
988
1014
  this._sendMessage({
989
1015
  type: "$updateStorage",
990
- data: { origin: ORIGIN_INITIALIZED, update },
1016
+ data: { origin, update: encodedState },
991
1017
  });
992
1018
  }
993
1019
 
@@ -998,9 +1024,9 @@ export class PluvRoom<
998
1024
  // Should not reach here
999
1025
  if (!this._state.webSocket) throw new Error("Could not find WebSocket");
1000
1026
 
1001
- const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>[typeof ORIGIN_STORAGE_UPDATED];
1002
-
1003
- if (!this._crdtManager) return;
1027
+ const data = message.data as BaseIOEventRecord<
1028
+ InferIOAuthorize<TIO>
1029
+ >[typeof ORIGIN_STORAGE_UPDATED];
1004
1030
 
1005
1031
  this._crdtManager.doc.applyEncodedState({
1006
1032
  origin: ORIGIN_STORAGE_UPDATED,
@@ -1011,8 +1037,6 @@ export class PluvRoom<
1011
1037
 
1012
1038
  const storageRoot = Object.keys(sharedTypes).reduce(
1013
1039
  (acc, prop) => {
1014
- if (!this._crdtManager) return acc;
1015
-
1016
1040
  const serialized = this._crdtManager.doc.toJson(prop);
1017
1041
 
1018
1042
  this._crdtNotifier.subject(prop).next(serialized);
@@ -1038,7 +1062,9 @@ export class PluvRoom<
1038
1062
  const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>["$syncStateReceived"];
1039
1063
  const active = new Set(data?.connectionIds ?? []);
1040
1064
 
1041
- const quitters = this._usersManager.getOthers().filter((other) => !active.has(other.connectionId));
1065
+ const quitters = this._usersManager
1066
+ .getOthers()
1067
+ .filter((other) => !active.has(other.connectionId));
1042
1068
 
1043
1069
  quitters.forEach((quitter) => {
1044
1070
  this._usersManager.deleteConnection(quitter.connectionId);
@@ -1080,7 +1106,10 @@ export class PluvRoom<
1080
1106
 
1081
1107
  private _heartbeat(): void {
1082
1108
  this._clearTimeout(this._timeouts.pong);
1083
- this._timeouts.pong = setTimeout(this._reconnect.bind(this), PONG_TIMEOUT_MS) as unknown as number;
1109
+ this._timeouts.pong = setTimeout(
1110
+ this._reconnect.bind(this),
1111
+ PONG_TIMEOUT_MS,
1112
+ ) as unknown as number;
1084
1113
 
1085
1114
  /**
1086
1115
  * !HACK
@@ -1099,18 +1128,17 @@ export class PluvRoom<
1099
1128
  private _observeCrdt(): void {
1100
1129
  this._subscriptions.observeCrdt?.();
1101
1130
 
1102
- if (!this._crdtManager) return;
1103
-
1104
1131
  const unsubscribe = this._crdtManager.doc.subscribe((event) => {
1105
1132
  const origin = event.origin ?? null;
1106
1133
 
1107
- this._addToStorageStore(event.update);
1108
1134
  this._emitSharedTypes();
1109
1135
 
1110
1136
  if (origin === ORIGIN_INITIALIZED || origin === ORIGIN_STORAGE_UPDATED) {
1111
1137
  return;
1112
1138
  }
1113
1139
 
1140
+ this._addToStorageStore(event.update);
1141
+
1114
1142
  if (!this._state.webSocket) return;
1115
1143
  if (!this._state.connection.id) return;
1116
1144
  if (this._state.webSocket.readyState !== WebSocket.OPEN) return;
@@ -1193,7 +1221,9 @@ export class PluvRoom<
1193
1221
  if (!message) return;
1194
1222
 
1195
1223
  const shouldLog =
1196
- typeof this._debug === "boolean" ? this._debug : this._debug.output.find((value) => value === message.type);
1224
+ typeof this._debug === "boolean"
1225
+ ? this._debug
1226
+ : this._debug.output.find((value) => value === message.type);
1197
1227
 
1198
1228
  if (shouldLog) {
1199
1229
  this._logDebug("WebSocket event received: ", message.type, message);
@@ -1246,7 +1276,6 @@ export class PluvRoom<
1246
1276
  if (this._state.connection.state !== ConnectionState.Connecting) return;
1247
1277
 
1248
1278
  this._logDebug("WebSocket connected");
1249
-
1250
1279
  this._updateState((oldState) => {
1251
1280
  oldState.connection.state = ConnectionState.Open;
1252
1281
 
@@ -1254,7 +1283,10 @@ export class PluvRoom<
1254
1283
  });
1255
1284
 
1256
1285
  this._clearInterval(this._intervals.heartbeat);
1257
- this._intervals.heartbeat = setInterval(this._heartbeat.bind(this), HEARTBEAT_INTERVAL_MS) as unknown as number;
1286
+ this._intervals.heartbeat = setInterval(
1287
+ this._heartbeat.bind(this),
1288
+ HEARTBEAT_INTERVAL_MS,
1289
+ ) as unknown as number;
1258
1290
  }
1259
1291
 
1260
1292
  private async _onNavigatorOffline(): Promise<void> {
@@ -1325,9 +1357,15 @@ export class PluvRoom<
1325
1357
  typeof this._reconnectTimeoutMs === "number"
1326
1358
  ? this._reconnectTimeoutMs
1327
1359
  : this._reconnectTimeoutMs({ attempts: this._state.connection.attempts });
1328
- const clampedMs = Math.max(Math.min(MIN_RECONNECT_TIMEOUT_MS, timeoutMs), MAX_RECONNECT_TIMEOUT_MS);
1360
+ const clampedMs = Math.max(
1361
+ Math.min(MIN_RECONNECT_TIMEOUT_MS, timeoutMs),
1362
+ MAX_RECONNECT_TIMEOUT_MS,
1363
+ );
1329
1364
 
1330
- this._timeouts.reconnect = setTimeout(this._reconnect.bind(this), clampedMs) as unknown as number;
1365
+ this._timeouts.reconnect = setTimeout(
1366
+ this._reconnect.bind(this),
1367
+ clampedMs,
1368
+ ) as unknown as number;
1331
1369
  }
1332
1370
 
1333
1371
  private async _reconnect(): Promise<void> {
@@ -1342,13 +1380,17 @@ export class PluvRoom<
1342
1380
  if (this._state.connection.state === ConnectionState.Closed) return;
1343
1381
 
1344
1382
  const metadata = this._lastMetadata;
1345
- const params = (typeof metadata === "undefined" ? [] : [{ metadata }]) as RoomConnectParams<TMetadata>;
1383
+ const params = (
1384
+ typeof metadata === "undefined" ? [] : [{ metadata }]
1385
+ ) as RoomConnectParams<TMetadata>;
1346
1386
 
1347
1387
  await this.connect(...params);
1348
1388
  }
1349
1389
 
1350
1390
  private _sendMessage(data: string): void;
1351
- private _sendMessage<TMessage extends EventMessage<string, any> = EventMessage<string, any>>(data: TMessage): void;
1391
+ private _sendMessage<TMessage extends EventMessage<string, any> = EventMessage<string, any>>(
1392
+ data: TMessage,
1393
+ ): void;
1352
1394
  private _sendMessage(data: any): void {
1353
1395
  const webSocket = this._state.webSocket;
1354
1396
 
@@ -1387,7 +1429,9 @@ export class PluvRoom<
1387
1429
  return parsed;
1388
1430
  }
1389
1431
 
1390
- private _updateState(updater: (oldState: WebSocketState<TIO>) => WebSocketState<TIO>): WebSocketState<TIO> {
1432
+ private _updateState(
1433
+ updater: (oldState: WebSocketState<TIO>) => WebSocketState<TIO>,
1434
+ ): WebSocketState<TIO> {
1391
1435
  let authorization: AuthorizationState<TIO>;
1392
1436
 
1393
1437
  try {
package/src/PluvRouter.ts CHANGED
@@ -67,7 +67,10 @@ export class PluvRouter<
67
67
  public static merge<TRouters extends PluvRouter<any, any, any, any>[]>(
68
68
  ...routers: TRouters
69
69
  ): MergedRouter<TRouters> {
70
- const events = Object.assign(Object.create(null), ...routers.map((router) => router._defs.events));
70
+ const events = Object.assign(
71
+ Object.create(null),
72
+ ...routers.map((router) => router._defs.events),
73
+ );
71
74
 
72
75
  if (Object.keys(events).some((key) => key.startsWith("$"))) {
73
76
  throw new Error('Procedures may not start with "$"');
@@ -15,7 +15,10 @@ type InferSubjectValue<
15
15
  TIO extends IOLike,
16
16
  TPresence extends JsonObject,
17
17
  TSubject extends keyof StateNotifierSubjects<TIO, TPresence>,
18
- > = StateNotifierSubjects<TIO, TPresence>[TSubject] extends Subject<infer IValue> ? Id<IValue> : never;
18
+ > =
19
+ StateNotifierSubjects<TIO, TPresence>[TSubject] extends Subject<infer IValue>
20
+ ? Id<IValue>
21
+ : never;
19
22
 
20
23
  export type SubscriptionCallback<
21
24
  TIO extends IOLike,
@@ -129,7 +129,11 @@ export class UsersManager<TIO extends IOLike, TPresence extends JsonObject = {}>
129
129
  if (!userInfo) return null;
130
130
 
131
131
  const remaining = this._getConnectionIds(clientId)?.size ?? 0;
132
- const result: DeleteConnectionResult<TIO, TPresence> = { clientId, remaining, data: userInfo };
132
+ const result: DeleteConnectionResult<TIO, TPresence> = {
133
+ clientId,
134
+ remaining,
135
+ data: userInfo,
136
+ };
133
137
 
134
138
  /**
135
139
  * @description A user may have multiple websocket connections open. If one connection is
@@ -195,8 +199,15 @@ export class UsersManager<TIO extends IOLike, TPresence extends JsonObject = {}>
195
199
  if (!other) return null;
196
200
 
197
201
  const cleanedPatch = pickBy(patch ?? {}, (value) => typeof value !== "undefined");
198
- const cleanedPresence = pickBy(other.presence ?? {}, (value) => typeof value !== "undefined");
199
- const presence = { ...this.initialPresence, ...cleanedPresence, ...cleanedPatch } as TPresence;
202
+ const cleanedPresence = pickBy(
203
+ other.presence ?? {},
204
+ (value) => typeof value !== "undefined",
205
+ );
206
+ const presence = {
207
+ ...this.initialPresence,
208
+ ...cleanedPresence,
209
+ ...cleanedPatch,
210
+ } as TPresence;
200
211
  const validated = this._presence ? this._presence.parse(presence) : presence;
201
212
 
202
213
  this._others.set(clientId, { ...other, presence: validated });
@@ -255,7 +266,11 @@ export class UsersManager<TIO extends IOLike, TPresence extends JsonObject = {}>
255
266
  public updateMyPresence(patch: Partial<TPresence>): TPresence {
256
267
  const cleanedPatch = pickBy(patch, (value) => typeof value !== "undefined");
257
268
  const cleanedPresence = pickBy(this._myPresence, (value) => typeof value !== "undefined");
258
- const updated = { ...this.initialPresence, ...cleanedPresence, ...cleanedPatch } as TPresence;
269
+ const updated = {
270
+ ...this.initialPresence,
271
+ ...cleanedPresence,
272
+ ...cleanedPatch,
273
+ } as TPresence;
259
274
  const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
260
275
 
261
276
  if (!!this._limits.presenceMaxSize && bytes > this._limits.presenceMaxSize) {
@@ -297,7 +312,10 @@ export class UsersManager<TIO extends IOLike, TPresence extends JsonObject = {}>
297
312
  return set ?? null;
298
313
  }
299
314
 
300
- private _setConnectionId(connectionId: string, clientId: string): Set<[connectionId: string][0]> {
315
+ private _setConnectionId(
316
+ connectionId: string,
317
+ clientId: string,
318
+ ): Set<[connectionId: string][0]> {
301
319
  const set = this._idMap.fromClientId.get(clientId) ?? new Set<string>();
302
320
  const updated = set.add(connectionId);
303
321
 
package/src/index.ts CHANGED
@@ -23,4 +23,11 @@ export type {
23
23
  export type { MergeEvents, PluvRouter, PluvRouterEventConfig } from "./PluvRouter";
24
24
  export { register } from "./register";
25
25
  export type { RegisterParams } from "./register";
26
- export type { InferMetadata, PublicKey, PublicKeyParams, UserInfo, WebSocketConnection, WebSocketState } from "./types";
26
+ export type {
27
+ InferMetadata,
28
+ PublicKey,
29
+ PublicKeyParams,
30
+ UserInfo,
31
+ WebSocketConnection,
32
+ WebSocketState,
33
+ } from "./types";
package/src/infer.ts CHANGED
@@ -5,4 +5,6 @@ export type InferCallback<TIO extends IOLike<any, any>> = (i: typeof identity) =
5
5
  io: (io: TIO) => TIO;
6
6
  };
7
7
 
8
- export const infer = <TIO extends IOLike<any, any>>(callback: InferCallback<TIO>): InferCallback<TIO> => callback;
8
+ export const infer = <TIO extends IOLike<any, any>>(
9
+ callback: InferCallback<TIO>,
10
+ ): InferCallback<TIO> => callback;
package/src/register.ts CHANGED
@@ -9,7 +9,8 @@ export interface RegisterParams<TMetadata extends JsonObject> {
9
9
  export const register = <TMetadata extends JsonObject>(params: RegisterParams<TMetadata>) => {
10
10
  const { authEndpoint, publicKey } = params;
11
11
 
12
- const wsEndpoint: WsEndpoint<TMetadata> = ({ room }) => `wss://connect.pluv.io/api/room?room=${room}`;
12
+ const wsEndpoint: WsEndpoint<TMetadata> = ({ room }) =>
13
+ `wss://connect.pluv.io/api/room?room=${room}`;
13
14
 
14
15
  return {
15
16
  authEndpoint,