@pluv/client 0.17.3 → 0.19.0
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/.turbo/turbo-build.log +15 -15
- package/CHANGELOG.md +176 -0
- package/README.md +2 -5
- package/dist/index.d.mts +15 -14
- package/dist/index.d.ts +15 -14
- package/dist/index.js +144 -293
- package/dist/index.mjs +144 -293
- package/package.json +6 -6
- package/src/AbstractRoom.ts +10 -35
- package/src/CrdtManager.ts +9 -33
- package/src/CrdtNotifier.ts +5 -11
- package/src/EventNotifier.ts +5 -11
- package/src/MockedRoom.ts +20 -55
- package/src/OtherNotifier.ts +2 -6
- package/src/PluvClient.ts +4 -17
- package/src/PluvRoom.ts +95 -204
- package/src/StateNotifier.ts +4 -17
- package/src/UsersManager.ts +4 -20
- package/src/createClient.ts +1 -3
- package/src/index.ts +2 -12
- package/src/types.ts +11 -16
package/src/PluvRoom.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AbstractCrdtDoc,
|
|
3
|
-
AbstractCrdtType,
|
|
4
|
-
InferCrdtStorageJson,
|
|
5
|
-
} from "@pluv/crdt";
|
|
1
|
+
import type { AbstractCrdtDoc, AbstractCrdtType, InferCrdtStorageJson } from "@pluv/crdt";
|
|
6
2
|
import type {
|
|
7
3
|
BaseIOEventRecord,
|
|
8
4
|
EventMessage,
|
|
@@ -17,7 +13,7 @@ import type {
|
|
|
17
13
|
JsonObject,
|
|
18
14
|
} from "@pluv/types";
|
|
19
15
|
import { AbstractRoom } from "./AbstractRoom";
|
|
20
|
-
import { AbstractStorageStore } from "./AbstractStorageStore";
|
|
16
|
+
import type { AbstractStorageStore } from "./AbstractStorageStore";
|
|
21
17
|
import type { CrdtManagerOptions } from "./CrdtManager";
|
|
22
18
|
import { CrdtManager } from "./CrdtManager";
|
|
23
19
|
import { CrdtNotifier } from "./CrdtNotifier";
|
|
@@ -25,10 +21,7 @@ import type { EventNotifierSubscriptionCallback } from "./EventNotifier";
|
|
|
25
21
|
import { EventNotifier } from "./EventNotifier";
|
|
26
22
|
import type { OtherNotifierSubscriptionCallback } from "./OtherNotifier";
|
|
27
23
|
import { OtherNotifier } from "./OtherNotifier";
|
|
28
|
-
import type {
|
|
29
|
-
StateNotifierSubjects,
|
|
30
|
-
SubscriptionCallback,
|
|
31
|
-
} from "./StateNotifier";
|
|
24
|
+
import type { StateNotifierSubjects, SubscriptionCallback } from "./StateNotifier";
|
|
32
25
|
import { StateNotifier } from "./StateNotifier";
|
|
33
26
|
import { StorageStore } from "./StorageStore";
|
|
34
27
|
import type { UsersManagerConfig } from "./UsersManager";
|
|
@@ -36,6 +29,7 @@ import { UsersManager } from "./UsersManager";
|
|
|
36
29
|
import type {
|
|
37
30
|
AuthorizationState,
|
|
38
31
|
InternalSubscriptions,
|
|
32
|
+
UpdateMyPresenceAction,
|
|
39
33
|
UserInfo,
|
|
40
34
|
WebSocketConnection,
|
|
41
35
|
WebSocketState,
|
|
@@ -89,10 +83,7 @@ interface TimeoutIds {
|
|
|
89
83
|
reconnect: number | null;
|
|
90
84
|
}
|
|
91
85
|
|
|
92
|
-
export type AuthEndpoint =
|
|
93
|
-
| string
|
|
94
|
-
| ((room: string) => string | FetchOptions)
|
|
95
|
-
| true;
|
|
86
|
+
export type AuthEndpoint = string | ((room: string) => string | FetchOptions) | true;
|
|
96
87
|
export type WsEndpoint = string | ((room: string) => string);
|
|
97
88
|
|
|
98
89
|
type FetchOptions = { url: string; options?: RequestInit };
|
|
@@ -111,9 +102,7 @@ export type PluvRoomAddon<
|
|
|
111
102
|
TIO extends IOLike = IOLike,
|
|
112
103
|
TPresence extends JsonObject = {},
|
|
113
104
|
TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
|
|
114
|
-
> = (
|
|
115
|
-
input: PluvRoomAddonInput<TIO, TPresence, TStorage>,
|
|
116
|
-
) => Partial<PluvRoomAddonResult>;
|
|
105
|
+
> = (input: PluvRoomAddonInput<TIO, TPresence, TStorage>) => Partial<PluvRoomAddonResult>;
|
|
117
106
|
|
|
118
107
|
export interface PluvRoomAddonInput<
|
|
119
108
|
TIO extends IOLike = IOLike,
|
|
@@ -220,10 +209,7 @@ export class PluvRoom<
|
|
|
220
209
|
this._listeners = {
|
|
221
210
|
onAuthorizationFail: (error) => {
|
|
222
211
|
this._clearTimeout(this._timeouts.reconnect);
|
|
223
|
-
this._timeouts.reconnect = setTimeout(
|
|
224
|
-
this._reconnect.bind(this),
|
|
225
|
-
RECONNECT_TIMEOUT_MS,
|
|
226
|
-
);
|
|
212
|
+
this._timeouts.reconnect = setTimeout(this._reconnect.bind(this), RECONNECT_TIMEOUT_MS);
|
|
227
213
|
|
|
228
214
|
onAuthorizationFail?.(error);
|
|
229
215
|
},
|
|
@@ -247,17 +233,25 @@ export class PluvRoom<
|
|
|
247
233
|
return this._state.webSocket;
|
|
248
234
|
}
|
|
249
235
|
|
|
250
|
-
public broadcast
|
|
251
|
-
event: TEvent,
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (!this._state.webSocket) return;
|
|
255
|
-
if (this._state.connection.state !== ConnectionState.Open) return;
|
|
236
|
+
public broadcast = new Proxy(
|
|
237
|
+
<TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>): void => {
|
|
238
|
+
if (!this._state.webSocket) return;
|
|
239
|
+
if (this._state.connection.state !== ConnectionState.Open) return;
|
|
256
240
|
|
|
257
|
-
|
|
241
|
+
const type = event.toString();
|
|
258
242
|
|
|
259
|
-
|
|
260
|
-
|
|
243
|
+
this._sendMessage({ data, type });
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
get(fn, prop) {
|
|
247
|
+
return (data: Id<InferIOInput<TIO>[any]>): void => {
|
|
248
|
+
return fn(prop, data);
|
|
249
|
+
};
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
) as (<TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>) => void) & {
|
|
253
|
+
[event in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[event]>) => void;
|
|
254
|
+
};
|
|
261
255
|
|
|
262
256
|
public canRedo = (): boolean => {
|
|
263
257
|
return !!this._crdtManager.doc.canRedo();
|
|
@@ -272,10 +266,9 @@ export class PluvRoom<
|
|
|
272
266
|
throw new Error("Must provide an wsEndpoint.");
|
|
273
267
|
}
|
|
274
268
|
|
|
275
|
-
const canConnect = [
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
].some((state) => this._state.connection.state === state);
|
|
269
|
+
const canConnect = [ConnectionState.Closed, ConnectionState.Untouched].some(
|
|
270
|
+
(state) => this._state.connection.state === state,
|
|
271
|
+
);
|
|
279
272
|
|
|
280
273
|
if (!canConnect) return;
|
|
281
274
|
|
|
@@ -298,8 +291,7 @@ export class PluvRoom<
|
|
|
298
291
|
try {
|
|
299
292
|
authToken = await this._getAuthorization(this.id);
|
|
300
293
|
|
|
301
|
-
authToken &&
|
|
302
|
-
url.searchParams.set("token", encodeURIComponent(authToken));
|
|
294
|
+
authToken && url.searchParams.set("token", encodeURIComponent(authToken));
|
|
303
295
|
|
|
304
296
|
webSocket = new WebSocket(url.toString());
|
|
305
297
|
} catch (err) {
|
|
@@ -328,10 +320,9 @@ export class PluvRoom<
|
|
|
328
320
|
|
|
329
321
|
if (!this._state.webSocket) return;
|
|
330
322
|
|
|
331
|
-
const canDisconnect = [
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
].some((state) => this._state.connection.state === state);
|
|
323
|
+
const canDisconnect = [ConnectionState.Connecting, ConnectionState.Open].some(
|
|
324
|
+
(state) => this._state.connection.state === state,
|
|
325
|
+
);
|
|
335
326
|
|
|
336
327
|
if (!canDisconnect) return;
|
|
337
328
|
|
|
@@ -346,23 +337,31 @@ export class PluvRoom<
|
|
|
346
337
|
this._stateNotifier.subjects["my-presence"].next(null);
|
|
347
338
|
}
|
|
348
339
|
|
|
349
|
-
public event =
|
|
340
|
+
public event = new Proxy(
|
|
341
|
+
<TEvent extends keyof InferIOOutput<TIO>>(
|
|
342
|
+
event: TEvent,
|
|
343
|
+
callback: EventNotifierSubscriptionCallback<TIO, TEvent>,
|
|
344
|
+
): (() => void) => this._eventNotifier.subscribe(event, callback),
|
|
345
|
+
{
|
|
346
|
+
get(fn, prop) {
|
|
347
|
+
return (callback: EventNotifierSubscriptionCallback<TIO, any>): (() => void) => fn(prop, callback);
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
) as (<TEvent extends keyof InferIOOutput<TIO>>(
|
|
350
351
|
event: TEvent,
|
|
351
352
|
callback: EventNotifierSubscriptionCallback<TIO, TEvent>,
|
|
352
|
-
)
|
|
353
|
-
|
|
353
|
+
) => () => void) & {
|
|
354
|
+
[event in keyof InferIOOutput<TIO>]: (callback: EventNotifierSubscriptionCallback<TIO, event>) => () => void;
|
|
354
355
|
};
|
|
355
356
|
|
|
356
|
-
public getConnection(): WebSocketConnection {
|
|
357
|
+
public getConnection = (): WebSocketConnection => {
|
|
357
358
|
// Create a read-only clone of the connection state
|
|
358
|
-
return Object.freeze(
|
|
359
|
-
|
|
360
|
-
);
|
|
361
|
-
}
|
|
359
|
+
return Object.freeze(JSON.parse(JSON.stringify(this._state.connection)));
|
|
360
|
+
};
|
|
362
361
|
|
|
363
|
-
public getDoc(): AbstractCrdtDoc<TStorage> {
|
|
362
|
+
public getDoc = (): AbstractCrdtDoc<TStorage> => {
|
|
364
363
|
return this._crdtManager.doc;
|
|
365
|
-
}
|
|
364
|
+
};
|
|
366
365
|
|
|
367
366
|
public getMyPresence = (): TPresence => {
|
|
368
367
|
return this._usersManager.myPresence;
|
|
@@ -372,9 +371,7 @@ export class PluvRoom<
|
|
|
372
371
|
return this._usersManager.myself;
|
|
373
372
|
};
|
|
374
373
|
|
|
375
|
-
public getOther = (
|
|
376
|
-
connectionId: string,
|
|
377
|
-
): Id<UserInfo<TIO, TPresence>> | null => {
|
|
374
|
+
public getOther = (connectionId: string): Id<UserInfo<TIO, TPresence>> | null => {
|
|
378
375
|
return this._usersManager.getOther(connectionId);
|
|
379
376
|
};
|
|
380
377
|
|
|
@@ -382,9 +379,7 @@ export class PluvRoom<
|
|
|
382
379
|
return this._usersManager.getOthers();
|
|
383
380
|
};
|
|
384
381
|
|
|
385
|
-
public getStorage = <TKey extends keyof TStorage>(
|
|
386
|
-
key: TKey,
|
|
387
|
-
): TStorage[TKey] | null => {
|
|
382
|
+
public getStorage = <TKey extends keyof TStorage>(key: TKey): TStorage[TKey] | null => {
|
|
388
383
|
const sharedType = this._crdtManager.get(key);
|
|
389
384
|
|
|
390
385
|
if (typeof sharedType === "undefined") return null;
|
|
@@ -392,10 +387,7 @@ export class PluvRoom<
|
|
|
392
387
|
return sharedType;
|
|
393
388
|
};
|
|
394
389
|
|
|
395
|
-
public other = (
|
|
396
|
-
connectionId: string,
|
|
397
|
-
callback: OtherNotifierSubscriptionCallback<TIO>,
|
|
398
|
-
): (() => void) => {
|
|
390
|
+
public other = (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): (() => void) => {
|
|
399
391
|
return this._otherNotifier.subscribe(connectionId, callback);
|
|
400
392
|
};
|
|
401
393
|
|
|
@@ -418,19 +410,14 @@ export class PluvRoom<
|
|
|
418
410
|
return this._crdtNotifier.subcribeRoot(fn);
|
|
419
411
|
};
|
|
420
412
|
|
|
421
|
-
public subscribe = <
|
|
422
|
-
TSubject extends keyof StateNotifierSubjects<TIO, TPresence>,
|
|
423
|
-
>(
|
|
413
|
+
public subscribe = <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(
|
|
424
414
|
name: TSubject,
|
|
425
415
|
callback: SubscriptionCallback<TIO, TPresence, TSubject>,
|
|
426
416
|
): (() => void) => {
|
|
427
417
|
return this._stateNotifier.subscribe(name, callback);
|
|
428
418
|
};
|
|
429
419
|
|
|
430
|
-
public transact = (
|
|
431
|
-
fn: (storage: TStorage) => void,
|
|
432
|
-
origin?: string,
|
|
433
|
-
): void => {
|
|
420
|
+
public transact = (fn: (storage: TStorage) => void, origin?: string): void => {
|
|
434
421
|
const _origin = origin ?? this._state.connection.id;
|
|
435
422
|
|
|
436
423
|
/**
|
|
@@ -453,8 +440,10 @@ export class PluvRoom<
|
|
|
453
440
|
this._crdtManager.doc.undo();
|
|
454
441
|
};
|
|
455
442
|
|
|
456
|
-
public updateMyPresence = (presence:
|
|
457
|
-
this.
|
|
443
|
+
public updateMyPresence = (presence: UpdateMyPresenceAction<TPresence>): void => {
|
|
444
|
+
const newPresence = typeof presence === "function" ? presence(this.getMyPresence()) : presence;
|
|
445
|
+
|
|
446
|
+
this._usersManager.updateMyPresence(newPresence);
|
|
458
447
|
|
|
459
448
|
const myPresence = this._usersManager.myPresence;
|
|
460
449
|
const myself = this._usersManager.myself ?? null;
|
|
@@ -462,10 +451,7 @@ export class PluvRoom<
|
|
|
462
451
|
this._stateNotifier.subjects["my-presence"].next(myPresence);
|
|
463
452
|
!!myself && this._stateNotifier.subjects["myself"].next(myself);
|
|
464
453
|
|
|
465
|
-
this.broadcast(
|
|
466
|
-
"$UPDATE_PRESENCE" as keyof InferIOInput<TIO>,
|
|
467
|
-
{ presence } as any,
|
|
468
|
-
);
|
|
454
|
+
this.broadcast("$UPDATE_PRESENCE" as keyof InferIOInput<TIO>, { presence: newPresence } as any);
|
|
469
455
|
};
|
|
470
456
|
|
|
471
457
|
private async _addToStorageStore(update: string): Promise<void> {
|
|
@@ -499,14 +485,8 @@ export class PluvRoom<
|
|
|
499
485
|
onVisibilityChange: this._onVisibilityChange.bind(this),
|
|
500
486
|
};
|
|
501
487
|
|
|
502
|
-
window.addEventListener(
|
|
503
|
-
|
|
504
|
-
this._windowListeners.onNavigatorOnline,
|
|
505
|
-
);
|
|
506
|
-
document.addEventListener(
|
|
507
|
-
"visibilitychange",
|
|
508
|
-
this._windowListeners.onVisibilityChange,
|
|
509
|
-
);
|
|
488
|
+
window.addEventListener("online", this._windowListeners.onNavigatorOnline);
|
|
489
|
+
document.addEventListener("visibilitychange", this._windowListeners.onVisibilityChange);
|
|
510
490
|
}
|
|
511
491
|
|
|
512
492
|
private _attachWsListeners(): void {
|
|
@@ -519,22 +499,10 @@ export class PluvRoom<
|
|
|
519
499
|
onOpen: this._onOpen.bind(this),
|
|
520
500
|
};
|
|
521
501
|
|
|
522
|
-
this._state.webSocket.addEventListener(
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
);
|
|
526
|
-
this._state.webSocket.addEventListener(
|
|
527
|
-
"error",
|
|
528
|
-
this._wsListeners.onError,
|
|
529
|
-
);
|
|
530
|
-
this._state.webSocket.addEventListener(
|
|
531
|
-
"message",
|
|
532
|
-
this._wsListeners.onMessage,
|
|
533
|
-
);
|
|
534
|
-
this._state.webSocket.addEventListener(
|
|
535
|
-
"open",
|
|
536
|
-
this._wsListeners.onOpen,
|
|
537
|
-
);
|
|
502
|
+
this._state.webSocket.addEventListener("close", this._wsListeners.onClose);
|
|
503
|
+
this._state.webSocket.addEventListener("error", this._wsListeners.onError);
|
|
504
|
+
this._state.webSocket.addEventListener("message", this._wsListeners.onMessage);
|
|
505
|
+
this._state.webSocket.addEventListener("open", this._wsListeners.onOpen);
|
|
538
506
|
}
|
|
539
507
|
|
|
540
508
|
private _clearInterval(interval: number | null) {
|
|
@@ -574,14 +542,8 @@ export class PluvRoom<
|
|
|
574
542
|
if (typeof window === "undefined") return;
|
|
575
543
|
if (typeof document === "undefined") return;
|
|
576
544
|
|
|
577
|
-
window.removeEventListener(
|
|
578
|
-
|
|
579
|
-
this._windowListeners.onNavigatorOnline,
|
|
580
|
-
);
|
|
581
|
-
document.removeEventListener(
|
|
582
|
-
"visibilitychange",
|
|
583
|
-
this._windowListeners.onVisibilityChange,
|
|
584
|
-
);
|
|
545
|
+
window.removeEventListener("online", this._windowListeners.onNavigatorOnline);
|
|
546
|
+
document.removeEventListener("visibilitychange", this._windowListeners.onVisibilityChange);
|
|
585
547
|
|
|
586
548
|
this._windowListeners = null;
|
|
587
549
|
}
|
|
@@ -589,22 +551,10 @@ export class PluvRoom<
|
|
|
589
551
|
private _detachWsListeners(): void {
|
|
590
552
|
if (!this._state.webSocket || !this._wsListeners) return;
|
|
591
553
|
|
|
592
|
-
this._state.webSocket.removeEventListener(
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
);
|
|
596
|
-
this._state.webSocket.removeEventListener(
|
|
597
|
-
"error",
|
|
598
|
-
this._wsListeners.onError,
|
|
599
|
-
);
|
|
600
|
-
this._state.webSocket.removeEventListener(
|
|
601
|
-
"message",
|
|
602
|
-
this._wsListeners.onMessage,
|
|
603
|
-
);
|
|
604
|
-
this._state.webSocket.removeEventListener(
|
|
605
|
-
"open",
|
|
606
|
-
this._wsListeners.onOpen,
|
|
607
|
-
);
|
|
554
|
+
this._state.webSocket.removeEventListener("close", this._wsListeners.onClose);
|
|
555
|
+
this._state.webSocket.removeEventListener("error", this._wsListeners.onError);
|
|
556
|
+
this._state.webSocket.removeEventListener("message", this._wsListeners.onMessage);
|
|
557
|
+
this._state.webSocket.removeEventListener("open", this._wsListeners.onOpen);
|
|
608
558
|
|
|
609
559
|
this._wsListeners = null;
|
|
610
560
|
}
|
|
@@ -676,9 +626,7 @@ export class PluvRoom<
|
|
|
676
626
|
|
|
677
627
|
const result = this._endpoints.authEndpoint(room);
|
|
678
628
|
|
|
679
|
-
return typeof result === "string"
|
|
680
|
-
? { url: result, options: {} }
|
|
681
|
-
: result;
|
|
629
|
+
return typeof result === "string" ? { url: result, options: {} } : result;
|
|
682
630
|
}
|
|
683
631
|
|
|
684
632
|
private async _getAuthorization(room: string): Promise<string | null> {
|
|
@@ -697,9 +645,7 @@ export class PluvRoom<
|
|
|
697
645
|
try {
|
|
698
646
|
return await res.text().then((text) => text.trim());
|
|
699
647
|
} catch (err) {
|
|
700
|
-
throw new Error(
|
|
701
|
-
err instanceof Error ? err.message : "Room is unauthorized",
|
|
702
|
-
);
|
|
648
|
+
throw new Error(err instanceof Error ? err.message : "Room is unauthorized");
|
|
703
649
|
}
|
|
704
650
|
}
|
|
705
651
|
|
|
@@ -737,14 +683,9 @@ export class PluvRoom<
|
|
|
737
683
|
// Should not reach here
|
|
738
684
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
739
685
|
|
|
740
|
-
const data = message.data as BaseIOEventRecord<
|
|
741
|
-
InferIOAuthorize<TIO>
|
|
742
|
-
>["$PRESENCE_UPDATED"];
|
|
686
|
+
const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>["$PRESENCE_UPDATED"];
|
|
743
687
|
|
|
744
|
-
this._usersManager.patchPresence(
|
|
745
|
-
connectionId,
|
|
746
|
-
data.presence as TPresence,
|
|
747
|
-
);
|
|
688
|
+
this._usersManager.patchPresence(connectionId, data.presence as TPresence);
|
|
748
689
|
|
|
749
690
|
const myPresence = this._usersManager.myPresence;
|
|
750
691
|
const myself = this._usersManager.myself ?? null;
|
|
@@ -768,24 +709,16 @@ export class PluvRoom<
|
|
|
768
709
|
// Should not reach here
|
|
769
710
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
770
711
|
|
|
771
|
-
const data = message.data as BaseIOEventRecord<
|
|
772
|
-
InferIOAuthorize<TIO>
|
|
773
|
-
>["$OTHERS_RECEIVED"];
|
|
712
|
+
const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>["$OTHERS_RECEIVED"];
|
|
774
713
|
|
|
775
714
|
Object.keys(data.others).forEach((connectionId) => {
|
|
776
715
|
const { presence, user } = data.others[connectionId];
|
|
777
716
|
|
|
778
|
-
if (!user) return;
|
|
779
|
-
|
|
780
717
|
this._usersManager.setUser(connectionId, user);
|
|
781
718
|
|
|
782
719
|
const other = this._usersManager.getOther(connectionId);
|
|
783
720
|
|
|
784
|
-
!!presence &&
|
|
785
|
-
this._usersManager.patchPresence(
|
|
786
|
-
connectionId,
|
|
787
|
-
presence as TPresence,
|
|
788
|
-
);
|
|
721
|
+
!!presence && this._usersManager.patchPresence(connectionId, presence as TPresence);
|
|
789
722
|
|
|
790
723
|
this._otherNotifier.subject(connectionId).next(other);
|
|
791
724
|
});
|
|
@@ -810,10 +743,7 @@ export class PluvRoom<
|
|
|
810
743
|
return oldState;
|
|
811
744
|
});
|
|
812
745
|
|
|
813
|
-
this._usersManager.setMyself(
|
|
814
|
-
connectionId,
|
|
815
|
-
user as Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>,
|
|
816
|
-
);
|
|
746
|
+
this._usersManager.setMyself(connectionId, user as Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>);
|
|
817
747
|
|
|
818
748
|
const presence = this._usersManager.myPresence;
|
|
819
749
|
const myself = this._usersManager.myself ?? null;
|
|
@@ -821,10 +751,7 @@ export class PluvRoom<
|
|
|
821
751
|
this._stateNotifier.subjects["my-presence"].next(presence);
|
|
822
752
|
this._stateNotifier.subjects["myself"].next(myself);
|
|
823
753
|
|
|
824
|
-
const update =
|
|
825
|
-
this._state.connection.count > 1
|
|
826
|
-
? this._crdtManager.doc.getEncodedState() ?? null
|
|
827
|
-
: null;
|
|
754
|
+
const update = this._state.connection.count > 1 ? this._crdtManager.doc.getEncodedState() ?? null : null;
|
|
828
755
|
|
|
829
756
|
this._sendMessage({
|
|
830
757
|
type: "$INITIALIZE_SESSION",
|
|
@@ -832,18 +759,14 @@ export class PluvRoom<
|
|
|
832
759
|
});
|
|
833
760
|
}
|
|
834
761
|
|
|
835
|
-
private async _handleStorageReceivedMessage(
|
|
836
|
-
message: IOEventMessage<TIO>,
|
|
837
|
-
): Promise<void> {
|
|
762
|
+
private async _handleStorageReceivedMessage(message: IOEventMessage<TIO>): Promise<void> {
|
|
838
763
|
const { connectionId } = message;
|
|
839
764
|
|
|
840
765
|
if (!connectionId) return;
|
|
841
766
|
// Should not reach here
|
|
842
767
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
843
768
|
|
|
844
|
-
const data = message.data as BaseIOEventRecord<
|
|
845
|
-
InferIOAuthorize<TIO>
|
|
846
|
-
>["$STORAGE_RECEIVED"];
|
|
769
|
+
const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>["$STORAGE_RECEIVED"];
|
|
847
770
|
|
|
848
771
|
this._crdtManager.initialize({
|
|
849
772
|
onInitialized: async (update) => {
|
|
@@ -878,9 +801,7 @@ export class PluvRoom<
|
|
|
878
801
|
// Should not reach here
|
|
879
802
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
880
803
|
|
|
881
|
-
const data = message.data as BaseIOEventRecord<
|
|
882
|
-
InferIOAuthorize<TIO>
|
|
883
|
-
>[typeof ORIGIN_STORAGE_UPDATED];
|
|
804
|
+
const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>[typeof ORIGIN_STORAGE_UPDATED];
|
|
884
805
|
|
|
885
806
|
if (!this._crdtManager) return;
|
|
886
807
|
|
|
@@ -917,9 +838,7 @@ export class PluvRoom<
|
|
|
917
838
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
918
839
|
if (!this._usersManager.myself) return;
|
|
919
840
|
|
|
920
|
-
const data = message.data as BaseIOEventRecord<
|
|
921
|
-
InferIOAuthorize<TIO>
|
|
922
|
-
>["$USER_JOINED"];
|
|
841
|
+
const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>["$USER_JOINED"];
|
|
923
842
|
|
|
924
843
|
const myself = this._usersManager.myself;
|
|
925
844
|
|
|
@@ -927,11 +846,7 @@ export class PluvRoom<
|
|
|
927
846
|
this._sendMessage({ type: "$GET_OTHERS", data: {} });
|
|
928
847
|
}
|
|
929
848
|
|
|
930
|
-
this._usersManager.setUser(
|
|
931
|
-
connectionId,
|
|
932
|
-
data.user,
|
|
933
|
-
data.presence as TPresence,
|
|
934
|
-
);
|
|
849
|
+
this._usersManager.setUser(connectionId, data.user, data.presence as TPresence);
|
|
935
850
|
|
|
936
851
|
const other = this._usersManager.getOther(connectionId);
|
|
937
852
|
const others = this._usersManager.getOthers();
|
|
@@ -942,10 +857,7 @@ export class PluvRoom<
|
|
|
942
857
|
|
|
943
858
|
private _heartbeat(): void {
|
|
944
859
|
this._clearTimeout(this._timeouts.pong);
|
|
945
|
-
this._timeouts.pong = setTimeout(
|
|
946
|
-
this._reconnect.bind(this),
|
|
947
|
-
PONG_TIMEOUT_MS,
|
|
948
|
-
);
|
|
860
|
+
this._timeouts.pong = setTimeout(this._reconnect.bind(this), PONG_TIMEOUT_MS);
|
|
949
861
|
|
|
950
862
|
this._sendMessage({ type: "$PING", data: {} });
|
|
951
863
|
}
|
|
@@ -968,10 +880,7 @@ export class PluvRoom<
|
|
|
968
880
|
this._addToStorageStore(event.update);
|
|
969
881
|
this._emitSharedTypes();
|
|
970
882
|
|
|
971
|
-
if (
|
|
972
|
-
origin === ORIGIN_INITIALIZED ||
|
|
973
|
-
origin === ORIGIN_STORAGE_UPDATED
|
|
974
|
-
) {
|
|
883
|
+
if (origin === ORIGIN_INITIALIZED || origin === ORIGIN_STORAGE_UPDATED) {
|
|
975
884
|
return;
|
|
976
885
|
}
|
|
977
886
|
|
|
@@ -989,8 +898,7 @@ export class PluvRoom<
|
|
|
989
898
|
}
|
|
990
899
|
|
|
991
900
|
private _onAuthorizationFail(error: unknown): void {
|
|
992
|
-
const authError =
|
|
993
|
-
error instanceof Error ? error : new Error("Authorization failed");
|
|
901
|
+
const authError = error instanceof Error ? error : new Error("Authorization failed");
|
|
994
902
|
|
|
995
903
|
this._listeners.onAuthorizationFail(authError);
|
|
996
904
|
|
|
@@ -1040,17 +948,13 @@ export class PluvRoom<
|
|
|
1040
948
|
if (!message) return;
|
|
1041
949
|
|
|
1042
950
|
const shouldLog =
|
|
1043
|
-
typeof this._debug === "boolean"
|
|
1044
|
-
? this._debug
|
|
1045
|
-
: this._debug.output.find((value) => value === message.type);
|
|
951
|
+
typeof this._debug === "boolean" ? this._debug : this._debug.output.find((value) => value === message.type);
|
|
1046
952
|
|
|
1047
953
|
if (shouldLog) {
|
|
1048
954
|
this._logDebug("WebSocket event received: ", message.type, message);
|
|
1049
955
|
}
|
|
1050
956
|
|
|
1051
|
-
this._eventNotifier
|
|
1052
|
-
.subject(message.type as keyof InferIOOutput<TIO>)
|
|
1053
|
-
.next(message as any);
|
|
957
|
+
this._eventNotifier.subject(message.type as keyof InferIOOutput<TIO>).next(message as any);
|
|
1054
958
|
|
|
1055
959
|
const { connectionId } = message;
|
|
1056
960
|
|
|
@@ -1105,10 +1009,7 @@ export class PluvRoom<
|
|
|
1105
1009
|
});
|
|
1106
1010
|
|
|
1107
1011
|
this._clearInterval(this._intervals.heartbeat);
|
|
1108
|
-
this._intervals.heartbeat = setInterval(
|
|
1109
|
-
this._heartbeat.bind(this),
|
|
1110
|
-
HEARTBEAT_INTERVAL_MS,
|
|
1111
|
-
);
|
|
1012
|
+
this._intervals.heartbeat = setInterval(this._heartbeat.bind(this), HEARTBEAT_INTERVAL_MS);
|
|
1112
1013
|
}
|
|
1113
1014
|
|
|
1114
1015
|
private async _onNavigatorOnline(): Promise<void> {
|
|
@@ -1137,9 +1038,7 @@ export class PluvRoom<
|
|
|
1137
1038
|
}
|
|
1138
1039
|
}
|
|
1139
1040
|
|
|
1140
|
-
private _parseMessage(message: {
|
|
1141
|
-
data: string | ArrayBuffer;
|
|
1142
|
-
}): IOEventMessage<TIO> | null {
|
|
1041
|
+
private _parseMessage(message: { data: string | ArrayBuffer }): IOEventMessage<TIO> | null {
|
|
1143
1042
|
/**
|
|
1144
1043
|
* !HACK
|
|
1145
1044
|
* @description We'll only handle stringified JSONs for now
|
|
@@ -1181,17 +1080,13 @@ export class PluvRoom<
|
|
|
1181
1080
|
await this.connect();
|
|
1182
1081
|
}
|
|
1183
1082
|
|
|
1184
|
-
private _sendMessage<
|
|
1185
|
-
TMessage extends EventMessage<string, any> = EventMessage<string, any>,
|
|
1186
|
-
>(data: TMessage): void {
|
|
1083
|
+
private _sendMessage<TMessage extends EventMessage<string, any> = EventMessage<string, any>>(data: TMessage): void {
|
|
1187
1084
|
const webSocket = this._state.webSocket;
|
|
1188
1085
|
|
|
1189
1086
|
if (webSocket?.readyState !== WebSocket.OPEN) return;
|
|
1190
1087
|
|
|
1191
1088
|
const shouldLog =
|
|
1192
|
-
typeof this._debug === "boolean"
|
|
1193
|
-
? this._debug
|
|
1194
|
-
: this._debug.input.find((value) => value === data.type);
|
|
1089
|
+
typeof this._debug === "boolean" ? this._debug : this._debug.input.find((value) => value === data.type);
|
|
1195
1090
|
|
|
1196
1091
|
if (shouldLog) {
|
|
1197
1092
|
this._logDebug("WebSocket event sent: ", data.type, data);
|
|
@@ -1200,15 +1095,11 @@ export class PluvRoom<
|
|
|
1200
1095
|
webSocket.send(JSON.stringify(data));
|
|
1201
1096
|
}
|
|
1202
1097
|
|
|
1203
|
-
private _updateState(
|
|
1204
|
-
updater: (oldState: WebSocketState<TIO>) => WebSocketState<TIO>,
|
|
1205
|
-
): WebSocketState<TIO> {
|
|
1098
|
+
private _updateState(updater: (oldState: WebSocketState<TIO>) => WebSocketState<TIO>): WebSocketState<TIO> {
|
|
1206
1099
|
let authorization: AuthorizationState<TIO>;
|
|
1207
1100
|
|
|
1208
1101
|
try {
|
|
1209
|
-
authorization = JSON.parse(
|
|
1210
|
-
JSON.stringify(this._state.authorization),
|
|
1211
|
-
);
|
|
1102
|
+
authorization = JSON.parse(JSON.stringify(this._state.authorization));
|
|
1212
1103
|
} catch {
|
|
1213
1104
|
throw new Error("User is not JSON serializable");
|
|
1214
1105
|
}
|
package/src/StateNotifier.ts
CHANGED
|
@@ -3,10 +3,7 @@ import type { Subject } from "wonka";
|
|
|
3
3
|
import { makeSubject, subscribe } from "wonka";
|
|
4
4
|
import type { UserInfo, WebSocketState } from "./types";
|
|
5
5
|
|
|
6
|
-
export interface StateNotifierSubjects<
|
|
7
|
-
TIO extends IOLike,
|
|
8
|
-
TPresence extends JsonObject,
|
|
9
|
-
> {
|
|
6
|
+
export interface StateNotifierSubjects<TIO extends IOLike, TPresence extends JsonObject> {
|
|
10
7
|
connection: Subject<Id<WebSocketState<TIO>>>;
|
|
11
8
|
"my-presence": Subject<TPresence | null>;
|
|
12
9
|
myself: Subject<Readonly<Id<UserInfo<TIO>>> | null>;
|
|
@@ -18,12 +15,7 @@ type InferSubjectValue<
|
|
|
18
15
|
TIO extends IOLike,
|
|
19
16
|
TPresence extends JsonObject,
|
|
20
17
|
TSubject extends keyof StateNotifierSubjects<TIO, TPresence>,
|
|
21
|
-
> =
|
|
22
|
-
StateNotifierSubjects<TIO, TPresence>[TSubject] extends Subject<
|
|
23
|
-
infer IValue
|
|
24
|
-
>
|
|
25
|
-
? Id<IValue>
|
|
26
|
-
: never;
|
|
18
|
+
> = StateNotifierSubjects<TIO, TPresence>[TSubject] extends Subject<infer IValue> ? Id<IValue> : never;
|
|
27
19
|
|
|
28
20
|
export type SubscriptionCallback<
|
|
29
21
|
TIO extends IOLike,
|
|
@@ -31,10 +23,7 @@ export type SubscriptionCallback<
|
|
|
31
23
|
TSubject extends keyof StateNotifierSubjects<TIO, TPresence>,
|
|
32
24
|
> = (value: InferSubjectValue<TIO, TPresence, TSubject>) => void;
|
|
33
25
|
|
|
34
|
-
export class StateNotifier<
|
|
35
|
-
TIO extends IOLike,
|
|
36
|
-
TPresence extends JsonObject = {},
|
|
37
|
-
> {
|
|
26
|
+
export class StateNotifier<TIO extends IOLike, TPresence extends JsonObject = {}> {
|
|
38
27
|
public subjects: StateNotifierSubjects<TIO, TPresence> = {
|
|
39
28
|
connection: makeSubject<Id<WebSocketState<TIO>>>(),
|
|
40
29
|
"my-presence": makeSubject<TPresence>(),
|
|
@@ -43,9 +32,7 @@ export class StateNotifier<
|
|
|
43
32
|
"storage-loaded": makeSubject<true>(),
|
|
44
33
|
};
|
|
45
34
|
|
|
46
|
-
public subscribe<
|
|
47
|
-
TSubject extends keyof StateNotifierSubjects<TIO, TPresence>,
|
|
48
|
-
>(
|
|
35
|
+
public subscribe<TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(
|
|
49
36
|
name: TSubject,
|
|
50
37
|
callback: SubscriptionCallback<TIO, TPresence, TSubject>,
|
|
51
38
|
): () => void {
|