@pluv/client 0.6.5 → 0.8.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 +5 -5
- package/CHANGELOG.md +32 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +66 -35
- package/dist/index.mjs +66 -35
- package/package.json +6 -6
- package/src/CrdtManager.ts +4 -4
- package/src/MockedRoom.ts +1 -0
- package/src/PluvClient.ts +4 -3
- package/src/PluvRoom.ts +78 -44
- package/src/UsersManager.ts +3 -2
- package/src/types.ts +1 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @pluv/client@0.
|
|
2
|
+
> @pluv/client@0.8.0 build /home/runner/work/pluv/pluv/packages/client
|
|
3
3
|
> tsup src/index.ts --format esm,cjs --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
[34mCLI[39m Target: es6
|
|
9
9
|
[34mESM[39m Build start
|
|
10
10
|
[34mCJS[39m Build start
|
|
11
|
-
[32mESM[39m [1mdist/index.mjs [22m[
|
|
11
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m35.24 KB[39m
|
|
12
12
|
[32mESM[39m ⚡️ Build success in 95ms
|
|
13
|
-
[32mCJS[39m [1mdist/index.js [22m[
|
|
13
|
+
[32mCJS[39m [1mdist/index.js [22m[32m36.38 KB[39m
|
|
14
14
|
[32mCJS[39m ⚡️ Build success in 96ms
|
|
15
15
|
[34mDTS[39m Build start
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m11.
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 4360ms
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m11.75 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @pluv/client
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- fde89cf: added defaults to the client to align it with createPluvClient by default
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 5bbfb98: fix not properly handling unauthorized checks
|
|
12
|
+
- b85a232: bumped dependencies
|
|
13
|
+
- Updated dependencies [b85a232]
|
|
14
|
+
- @pluv/crdt-yjs@0.3.8
|
|
15
|
+
- @pluv/types@0.2.0
|
|
16
|
+
|
|
17
|
+
## 0.7.0
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- ae679a8: updated offline presence to be set when reconnecting to a room
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- 0dd847e: updated storage to be synced when reconnected to the room
|
|
26
|
+
- bb2886b: fixed not reconnecting during heartbeat
|
|
27
|
+
- 3518a83: bumped dependencies
|
|
28
|
+
- Updated dependencies [0dd847e]
|
|
29
|
+
- Updated dependencies [bb2886b]
|
|
30
|
+
- Updated dependencies [ae679a8]
|
|
31
|
+
- Updated dependencies [3518a83]
|
|
32
|
+
- @pluv/crdt-yjs@0.3.7
|
|
33
|
+
- @pluv/types@0.2.0
|
|
34
|
+
|
|
3
35
|
## 0.6.5
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ declare enum ConnectionState {
|
|
|
21
21
|
Untouched = "Untouched"
|
|
22
22
|
}
|
|
23
23
|
interface WebSocketConnection {
|
|
24
|
+
count: number;
|
|
24
25
|
id: string | null;
|
|
25
26
|
state: ConnectionState;
|
|
26
27
|
}
|
|
@@ -80,18 +81,18 @@ declare global {
|
|
|
80
81
|
};
|
|
81
82
|
};
|
|
82
83
|
}
|
|
83
|
-
type AuthEndpoint = string | ((room: string) => FetchOptions);
|
|
84
|
+
type AuthEndpoint = string | ((room: string) => string | FetchOptions) | true;
|
|
84
85
|
type WsEndpoint = string | ((room: string) => string);
|
|
85
|
-
type FetchOptions =
|
|
86
|
+
type FetchOptions = {
|
|
86
87
|
url: string;
|
|
87
88
|
options?: RequestInit;
|
|
88
89
|
};
|
|
89
90
|
type RoomEndpoints<TIO extends IOLike> = {
|
|
90
|
-
wsEndpoint
|
|
91
|
+
wsEndpoint?: WsEndpoint;
|
|
91
92
|
} & (InferIOAuthorizeRequired<InferIOAuthorize<TIO>> extends true ? {
|
|
92
93
|
authEndpoint: AuthEndpoint;
|
|
93
94
|
} : {
|
|
94
|
-
authEndpoint?:
|
|
95
|
+
authEndpoint?: undefined;
|
|
95
96
|
});
|
|
96
97
|
type PluvRoomDebug<TIO extends IOLike> = Id<{
|
|
97
98
|
output: readonly (keyof InferIOOutput<TIO>)[];
|
|
@@ -143,6 +144,7 @@ declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TS
|
|
|
143
144
|
private _detachWindowListeners;
|
|
144
145
|
private _detachWsListeners;
|
|
145
146
|
private _getAuthorization;
|
|
147
|
+
private _getAuthFetchOptions;
|
|
146
148
|
private _getWsEndpoint;
|
|
147
149
|
private _handleExit;
|
|
148
150
|
private _handlePresenceUpdatedMessage;
|
|
@@ -197,7 +199,7 @@ declare class PluvClient<TIO extends IOLike = IOLike> {
|
|
|
197
199
|
private _logDebug;
|
|
198
200
|
}
|
|
199
201
|
|
|
200
|
-
declare const createClient: <TIO extends IOLike<_pluv_types.IOAuthorize<any, any>, {}, {}> = IOLike<_pluv_types.IOAuthorize<any, any>, {}, {}>>(options: PluvClientOptions<TIO>) => PluvClient<TIO>;
|
|
202
|
+
declare const createClient: <TIO extends IOLike<_pluv_types.IOAuthorize<any, any>, {}, {}, {}, {}> = IOLike<_pluv_types.IOAuthorize<any, any>, {}, {}, {}, {}>>(options: PluvClientOptions<TIO>) => PluvClient<TIO>;
|
|
201
203
|
|
|
202
204
|
type MockedRoomEvents<TIO extends IOLike> = Partial<{
|
|
203
205
|
[P in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[P]>) => Partial<InferIOOutput<TIO>>;
|
package/dist/index.js
CHANGED
|
@@ -79,12 +79,12 @@ var import_crdt_yjs = require("@pluv/crdt-yjs");
|
|
|
79
79
|
var CrdtManager = class {
|
|
80
80
|
constructor(options = {}) {
|
|
81
81
|
const { encodedState, initialStorage } = options;
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
84
|
-
this.doc =
|
|
82
|
+
const _doc = encodedState ? (0, import_crdt_yjs.doc)().applyUpdate(encodedState) : null;
|
|
83
|
+
if (_doc && !!Object.keys(_doc.toJSON()).length) {
|
|
84
|
+
this.doc = _doc;
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
|
-
|
|
87
|
+
_doc == null ? void 0 : _doc.destroy();
|
|
88
88
|
this.doc = (0, import_crdt_yjs.doc)(initialStorage == null ? void 0 : initialStorage());
|
|
89
89
|
}
|
|
90
90
|
destroy() {
|
|
@@ -257,7 +257,7 @@ var UsersManager = class {
|
|
|
257
257
|
return;
|
|
258
258
|
this._others.set(connectionId, __spreadProps(__spreadValues({}, other), { presence }));
|
|
259
259
|
}
|
|
260
|
-
setUser(connectionId, user) {
|
|
260
|
+
setUser(connectionId, user, presence) {
|
|
261
261
|
var _a;
|
|
262
262
|
if (((_a = this._myself) == null ? void 0 : _a.connectionId) === connectionId)
|
|
263
263
|
return;
|
|
@@ -266,7 +266,7 @@ var UsersManager = class {
|
|
|
266
266
|
return;
|
|
267
267
|
this._others.set(connectionId, {
|
|
268
268
|
connectionId,
|
|
269
|
-
presence: this._initialPresence,
|
|
269
|
+
presence: presence != null ? presence : this._initialPresence,
|
|
270
270
|
user
|
|
271
271
|
});
|
|
272
272
|
}
|
|
@@ -311,6 +311,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
311
311
|
user: null
|
|
312
312
|
},
|
|
313
313
|
connection: {
|
|
314
|
+
count: 0,
|
|
314
315
|
id: null,
|
|
315
316
|
state: "Untouched" /* Untouched */
|
|
316
317
|
},
|
|
@@ -368,10 +369,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
368
369
|
);
|
|
369
370
|
};
|
|
370
371
|
this._debug = debug;
|
|
371
|
-
this._endpoints = {
|
|
372
|
-
authEndpoint,
|
|
373
|
-
wsEndpoint
|
|
374
|
-
};
|
|
372
|
+
this._endpoints = { authEndpoint, wsEndpoint };
|
|
375
373
|
this._listeners = {
|
|
376
374
|
onAuthorizationFail: (error) => {
|
|
377
375
|
this._clearTimeout(this._timeouts.reconnect);
|
|
@@ -584,24 +582,50 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
584
582
|
}
|
|
585
583
|
_getAuthorization(room) {
|
|
586
584
|
return __async(this, null, function* () {
|
|
587
|
-
|
|
585
|
+
const fetchOptions = this._getAuthFetchOptions(room);
|
|
586
|
+
if (!fetchOptions)
|
|
588
587
|
return null;
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
588
|
+
const { url, options } = fetchOptions;
|
|
589
|
+
const res = yield fetch(url, options);
|
|
590
|
+
if (!res.ok || res.status !== 200) {
|
|
591
|
+
throw new Error("Room is unauthorized");
|
|
592
|
+
}
|
|
593
|
+
try {
|
|
594
|
+
return yield res.text().then((text2) => text2.trim());
|
|
595
|
+
} catch (err) {
|
|
596
|
+
throw new Error(
|
|
597
|
+
err instanceof Error ? err.message : "Room is unauthorized"
|
|
598
|
+
);
|
|
592
599
|
}
|
|
593
|
-
const config = this._endpoints.authEndpoint(room);
|
|
594
|
-
const endpoint = typeof config === "string" ? config : config.url;
|
|
595
|
-
const options = typeof config === "string" ? void 0 : config.options;
|
|
596
|
-
const token = yield fetch(endpoint, options).then((res) => res.text()).then((text2) => text2.trim());
|
|
597
|
-
return token;
|
|
598
600
|
});
|
|
599
601
|
}
|
|
602
|
+
_getAuthFetchOptions(room) {
|
|
603
|
+
if (typeof this._endpoints.authEndpoint === "undefined")
|
|
604
|
+
return null;
|
|
605
|
+
if (this._endpoints.authEndpoint === true) {
|
|
606
|
+
return {
|
|
607
|
+
url: `/api/pluv/authorize?room=${room}`,
|
|
608
|
+
options: {}
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
if (typeof this._endpoints.authEndpoint === "string") {
|
|
612
|
+
return {
|
|
613
|
+
url: this._endpoints.authEndpoint,
|
|
614
|
+
options: {}
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
const result = this._endpoints.authEndpoint(room);
|
|
618
|
+
return typeof result === "string" ? { url: result, options: {} } : result;
|
|
619
|
+
}
|
|
600
620
|
_getWsEndpoint(room) {
|
|
601
|
-
|
|
602
|
-
|
|
621
|
+
switch (typeof this._endpoints.wsEndpoint) {
|
|
622
|
+
case "undefined":
|
|
623
|
+
return `/api/pluv/room/${room}`;
|
|
624
|
+
case "string":
|
|
625
|
+
return this._endpoints.wsEndpoint;
|
|
626
|
+
default:
|
|
627
|
+
return this._endpoints.wsEndpoint(room);
|
|
603
628
|
}
|
|
604
|
-
return this._endpoints.wsEndpoint(room);
|
|
605
629
|
}
|
|
606
630
|
_handleExit(message) {
|
|
607
631
|
const { connectionId } = message;
|
|
@@ -661,13 +685,14 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
661
685
|
this._stateNotifier.subjects["others"].next(others);
|
|
662
686
|
}
|
|
663
687
|
_handleRegisteredMessage(message) {
|
|
664
|
-
var _a;
|
|
688
|
+
var _a, _b, _c;
|
|
665
689
|
const { connectionId, user } = message;
|
|
666
690
|
if (!connectionId)
|
|
667
691
|
return;
|
|
668
692
|
if (!this._state.webSocket)
|
|
669
693
|
throw new Error("Could not find WebSocket");
|
|
670
694
|
this._updateState((oldState) => {
|
|
695
|
+
oldState.connection.count += 1;
|
|
671
696
|
oldState.connection.id = connectionId;
|
|
672
697
|
oldState.authorization.user = user;
|
|
673
698
|
return oldState;
|
|
@@ -676,11 +701,15 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
676
701
|
connectionId,
|
|
677
702
|
user
|
|
678
703
|
);
|
|
679
|
-
const
|
|
704
|
+
const presence = this._usersManager.myPresence;
|
|
680
705
|
const myself = (_a = this._usersManager.myself) != null ? _a : null;
|
|
681
|
-
this._stateNotifier.subjects["my-presence"].next(
|
|
706
|
+
this._stateNotifier.subjects["my-presence"].next(presence);
|
|
682
707
|
this._stateNotifier.subjects["myself"].next(myself);
|
|
683
|
-
this.
|
|
708
|
+
const update = this._state.connection.count > 1 ? (_c = (_b = this._crdtManager) == null ? void 0 : _b.doc.encodeStateAsUpdate()) != null ? _c : null : null;
|
|
709
|
+
this._sendMessage({
|
|
710
|
+
type: "$INITIALIZE_SESSION",
|
|
711
|
+
data: { presence, update }
|
|
712
|
+
});
|
|
684
713
|
}
|
|
685
714
|
_handleStorageReceivedMessage(message) {
|
|
686
715
|
var _a;
|
|
@@ -737,20 +766,21 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
737
766
|
if (!this._usersManager.myself)
|
|
738
767
|
return;
|
|
739
768
|
const data = message.data;
|
|
740
|
-
this._usersManager.
|
|
769
|
+
const myself = this._usersManager.myself;
|
|
770
|
+
if (myself.connectionId === connectionId) {
|
|
771
|
+
this._sendMessage({ type: "$GET_OTHERS", data: {} });
|
|
772
|
+
}
|
|
773
|
+
this._usersManager.setUser(
|
|
774
|
+
connectionId,
|
|
775
|
+
data.user,
|
|
776
|
+
data.presence
|
|
777
|
+
);
|
|
741
778
|
const other = this._usersManager.getOther(connectionId);
|
|
742
779
|
const others = this._usersManager.getOthers();
|
|
743
780
|
this._otherNotifier.subject(connectionId).next(other);
|
|
744
781
|
this._stateNotifier.subjects["others"].next(others);
|
|
745
782
|
}
|
|
746
783
|
_heartbeat() {
|
|
747
|
-
if (!this._state.webSocket)
|
|
748
|
-
return;
|
|
749
|
-
if (!this._state.connection.id)
|
|
750
|
-
return;
|
|
751
|
-
if (this._state.webSocket.readyState !== this._state.webSocket.OPEN) {
|
|
752
|
-
return;
|
|
753
|
-
}
|
|
754
784
|
this._clearTimeout(this._timeouts.pong);
|
|
755
785
|
this._timeouts.pong = setTimeout(
|
|
756
786
|
this._reconnect.bind(this),
|
|
@@ -945,7 +975,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
945
975
|
this._closeWs();
|
|
946
976
|
this._updateState((oldState) => {
|
|
947
977
|
oldState.authorization.token = null;
|
|
948
|
-
oldState.connection.state = "
|
|
978
|
+
oldState.connection.state = "Closed" /* Closed */;
|
|
949
979
|
oldState.webSocket = null;
|
|
950
980
|
return oldState;
|
|
951
981
|
});
|
|
@@ -1062,6 +1092,7 @@ var MockedRoom = class extends AbstractRoom {
|
|
|
1062
1092
|
user: null
|
|
1063
1093
|
},
|
|
1064
1094
|
connection: {
|
|
1095
|
+
count: 1,
|
|
1065
1096
|
id: null,
|
|
1066
1097
|
state: "Untouched" /* Untouched */
|
|
1067
1098
|
},
|
package/dist/index.mjs
CHANGED
|
@@ -54,12 +54,12 @@ import { doc } from "@pluv/crdt-yjs";
|
|
|
54
54
|
var CrdtManager = class {
|
|
55
55
|
constructor(options = {}) {
|
|
56
56
|
const { encodedState, initialStorage } = options;
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
59
|
-
this.doc =
|
|
57
|
+
const _doc = encodedState ? doc().applyUpdate(encodedState) : null;
|
|
58
|
+
if (_doc && !!Object.keys(_doc.toJSON()).length) {
|
|
59
|
+
this.doc = _doc;
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
_doc == null ? void 0 : _doc.destroy();
|
|
63
63
|
this.doc = doc(initialStorage == null ? void 0 : initialStorage());
|
|
64
64
|
}
|
|
65
65
|
destroy() {
|
|
@@ -232,7 +232,7 @@ var UsersManager = class {
|
|
|
232
232
|
return;
|
|
233
233
|
this._others.set(connectionId, __spreadProps(__spreadValues({}, other), { presence }));
|
|
234
234
|
}
|
|
235
|
-
setUser(connectionId, user) {
|
|
235
|
+
setUser(connectionId, user, presence) {
|
|
236
236
|
var _a;
|
|
237
237
|
if (((_a = this._myself) == null ? void 0 : _a.connectionId) === connectionId)
|
|
238
238
|
return;
|
|
@@ -241,7 +241,7 @@ var UsersManager = class {
|
|
|
241
241
|
return;
|
|
242
242
|
this._others.set(connectionId, {
|
|
243
243
|
connectionId,
|
|
244
|
-
presence: this._initialPresence,
|
|
244
|
+
presence: presence != null ? presence : this._initialPresence,
|
|
245
245
|
user
|
|
246
246
|
});
|
|
247
247
|
}
|
|
@@ -286,6 +286,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
286
286
|
user: null
|
|
287
287
|
},
|
|
288
288
|
connection: {
|
|
289
|
+
count: 0,
|
|
289
290
|
id: null,
|
|
290
291
|
state: "Untouched" /* Untouched */
|
|
291
292
|
},
|
|
@@ -343,10 +344,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
343
344
|
);
|
|
344
345
|
};
|
|
345
346
|
this._debug = debug;
|
|
346
|
-
this._endpoints = {
|
|
347
|
-
authEndpoint,
|
|
348
|
-
wsEndpoint
|
|
349
|
-
};
|
|
347
|
+
this._endpoints = { authEndpoint, wsEndpoint };
|
|
350
348
|
this._listeners = {
|
|
351
349
|
onAuthorizationFail: (error) => {
|
|
352
350
|
this._clearTimeout(this._timeouts.reconnect);
|
|
@@ -559,24 +557,50 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
559
557
|
}
|
|
560
558
|
_getAuthorization(room) {
|
|
561
559
|
return __async(this, null, function* () {
|
|
562
|
-
|
|
560
|
+
const fetchOptions = this._getAuthFetchOptions(room);
|
|
561
|
+
if (!fetchOptions)
|
|
563
562
|
return null;
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
563
|
+
const { url, options } = fetchOptions;
|
|
564
|
+
const res = yield fetch(url, options);
|
|
565
|
+
if (!res.ok || res.status !== 200) {
|
|
566
|
+
throw new Error("Room is unauthorized");
|
|
567
|
+
}
|
|
568
|
+
try {
|
|
569
|
+
return yield res.text().then((text2) => text2.trim());
|
|
570
|
+
} catch (err) {
|
|
571
|
+
throw new Error(
|
|
572
|
+
err instanceof Error ? err.message : "Room is unauthorized"
|
|
573
|
+
);
|
|
567
574
|
}
|
|
568
|
-
const config = this._endpoints.authEndpoint(room);
|
|
569
|
-
const endpoint = typeof config === "string" ? config : config.url;
|
|
570
|
-
const options = typeof config === "string" ? void 0 : config.options;
|
|
571
|
-
const token = yield fetch(endpoint, options).then((res) => res.text()).then((text2) => text2.trim());
|
|
572
|
-
return token;
|
|
573
575
|
});
|
|
574
576
|
}
|
|
577
|
+
_getAuthFetchOptions(room) {
|
|
578
|
+
if (typeof this._endpoints.authEndpoint === "undefined")
|
|
579
|
+
return null;
|
|
580
|
+
if (this._endpoints.authEndpoint === true) {
|
|
581
|
+
return {
|
|
582
|
+
url: `/api/pluv/authorize?room=${room}`,
|
|
583
|
+
options: {}
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
if (typeof this._endpoints.authEndpoint === "string") {
|
|
587
|
+
return {
|
|
588
|
+
url: this._endpoints.authEndpoint,
|
|
589
|
+
options: {}
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
const result = this._endpoints.authEndpoint(room);
|
|
593
|
+
return typeof result === "string" ? { url: result, options: {} } : result;
|
|
594
|
+
}
|
|
575
595
|
_getWsEndpoint(room) {
|
|
576
|
-
|
|
577
|
-
|
|
596
|
+
switch (typeof this._endpoints.wsEndpoint) {
|
|
597
|
+
case "undefined":
|
|
598
|
+
return `/api/pluv/room/${room}`;
|
|
599
|
+
case "string":
|
|
600
|
+
return this._endpoints.wsEndpoint;
|
|
601
|
+
default:
|
|
602
|
+
return this._endpoints.wsEndpoint(room);
|
|
578
603
|
}
|
|
579
|
-
return this._endpoints.wsEndpoint(room);
|
|
580
604
|
}
|
|
581
605
|
_handleExit(message) {
|
|
582
606
|
const { connectionId } = message;
|
|
@@ -636,13 +660,14 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
636
660
|
this._stateNotifier.subjects["others"].next(others);
|
|
637
661
|
}
|
|
638
662
|
_handleRegisteredMessage(message) {
|
|
639
|
-
var _a;
|
|
663
|
+
var _a, _b, _c;
|
|
640
664
|
const { connectionId, user } = message;
|
|
641
665
|
if (!connectionId)
|
|
642
666
|
return;
|
|
643
667
|
if (!this._state.webSocket)
|
|
644
668
|
throw new Error("Could not find WebSocket");
|
|
645
669
|
this._updateState((oldState) => {
|
|
670
|
+
oldState.connection.count += 1;
|
|
646
671
|
oldState.connection.id = connectionId;
|
|
647
672
|
oldState.authorization.user = user;
|
|
648
673
|
return oldState;
|
|
@@ -651,11 +676,15 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
651
676
|
connectionId,
|
|
652
677
|
user
|
|
653
678
|
);
|
|
654
|
-
const
|
|
679
|
+
const presence = this._usersManager.myPresence;
|
|
655
680
|
const myself = (_a = this._usersManager.myself) != null ? _a : null;
|
|
656
|
-
this._stateNotifier.subjects["my-presence"].next(
|
|
681
|
+
this._stateNotifier.subjects["my-presence"].next(presence);
|
|
657
682
|
this._stateNotifier.subjects["myself"].next(myself);
|
|
658
|
-
this.
|
|
683
|
+
const update = this._state.connection.count > 1 ? (_c = (_b = this._crdtManager) == null ? void 0 : _b.doc.encodeStateAsUpdate()) != null ? _c : null : null;
|
|
684
|
+
this._sendMessage({
|
|
685
|
+
type: "$INITIALIZE_SESSION",
|
|
686
|
+
data: { presence, update }
|
|
687
|
+
});
|
|
659
688
|
}
|
|
660
689
|
_handleStorageReceivedMessage(message) {
|
|
661
690
|
var _a;
|
|
@@ -712,20 +741,21 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
712
741
|
if (!this._usersManager.myself)
|
|
713
742
|
return;
|
|
714
743
|
const data = message.data;
|
|
715
|
-
this._usersManager.
|
|
744
|
+
const myself = this._usersManager.myself;
|
|
745
|
+
if (myself.connectionId === connectionId) {
|
|
746
|
+
this._sendMessage({ type: "$GET_OTHERS", data: {} });
|
|
747
|
+
}
|
|
748
|
+
this._usersManager.setUser(
|
|
749
|
+
connectionId,
|
|
750
|
+
data.user,
|
|
751
|
+
data.presence
|
|
752
|
+
);
|
|
716
753
|
const other = this._usersManager.getOther(connectionId);
|
|
717
754
|
const others = this._usersManager.getOthers();
|
|
718
755
|
this._otherNotifier.subject(connectionId).next(other);
|
|
719
756
|
this._stateNotifier.subjects["others"].next(others);
|
|
720
757
|
}
|
|
721
758
|
_heartbeat() {
|
|
722
|
-
if (!this._state.webSocket)
|
|
723
|
-
return;
|
|
724
|
-
if (!this._state.connection.id)
|
|
725
|
-
return;
|
|
726
|
-
if (this._state.webSocket.readyState !== this._state.webSocket.OPEN) {
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
759
|
this._clearTimeout(this._timeouts.pong);
|
|
730
760
|
this._timeouts.pong = setTimeout(
|
|
731
761
|
this._reconnect.bind(this),
|
|
@@ -920,7 +950,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
920
950
|
this._closeWs();
|
|
921
951
|
this._updateState((oldState) => {
|
|
922
952
|
oldState.authorization.token = null;
|
|
923
|
-
oldState.connection.state = "
|
|
953
|
+
oldState.connection.state = "Closed" /* Closed */;
|
|
924
954
|
oldState.webSocket = null;
|
|
925
955
|
return oldState;
|
|
926
956
|
});
|
|
@@ -1037,6 +1067,7 @@ var MockedRoom = class extends AbstractRoom {
|
|
|
1037
1067
|
user: null
|
|
1038
1068
|
},
|
|
1039
1069
|
connection: {
|
|
1070
|
+
count: 1,
|
|
1040
1071
|
id: null,
|
|
1041
1072
|
state: "Untouched" /* Untouched */
|
|
1042
1073
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "@pluv/io frontend client",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"wonka": "^6.3.
|
|
21
|
-
"@pluv/crdt-yjs": "^0.3.
|
|
22
|
-
"@pluv/types": "^0.
|
|
20
|
+
"wonka": "^6.3.2",
|
|
21
|
+
"@pluv/crdt-yjs": "^0.3.8",
|
|
22
|
+
"@pluv/types": "^0.2.0"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
25
|
"yjs": "^13.0.0"
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"tsup": "^6.7.0",
|
|
29
29
|
"typescript": "^5.0.4",
|
|
30
|
-
"yjs": "^13.
|
|
30
|
+
"yjs": "^13.6.1",
|
|
31
31
|
"@pluv/tsconfig": "^0.1.6",
|
|
32
|
-
"eslint-config-pluv": "^0.1.
|
|
32
|
+
"eslint-config-pluv": "^0.1.12"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
package/src/CrdtManager.ts
CHANGED
|
@@ -15,17 +15,17 @@ export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
|
|
|
15
15
|
constructor(options: CrdtManagerOptions<TStorage> = {}) {
|
|
16
16
|
const { encodedState, initialStorage } = options;
|
|
17
17
|
|
|
18
|
-
const
|
|
18
|
+
const _doc = encodedState
|
|
19
19
|
? doc<TStorage>().applyUpdate(encodedState)
|
|
20
20
|
: null;
|
|
21
21
|
|
|
22
|
-
if (
|
|
23
|
-
this.doc =
|
|
22
|
+
if (_doc && !!Object.keys(_doc.toJSON()).length) {
|
|
23
|
+
this.doc = _doc;
|
|
24
24
|
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
_doc?.destroy();
|
|
29
29
|
|
|
30
30
|
this.doc = doc<TStorage>(initialStorage?.());
|
|
31
31
|
}
|
package/src/MockedRoom.ts
CHANGED
package/src/PluvClient.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
RoomEndpoints,
|
|
6
6
|
PluvRoomOptions,
|
|
7
7
|
WsEndpoint,
|
|
8
|
+
RoomConfig,
|
|
8
9
|
} from "./PluvRoom";
|
|
9
10
|
import { PluvRoom } from "./PluvRoom";
|
|
10
11
|
|
|
@@ -13,9 +14,9 @@ export type PluvClientOptions<TIO extends IOLike> = RoomEndpoints<TIO> & {
|
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
export class PluvClient<TIO extends IOLike = IOLike> {
|
|
16
|
-
private _authEndpoint: AuthEndpoint;
|
|
17
|
+
private _authEndpoint: AuthEndpoint | undefined;
|
|
17
18
|
private _debug: boolean;
|
|
18
|
-
private _wsEndpoint: WsEndpoint;
|
|
19
|
+
private _wsEndpoint: WsEndpoint | undefined;
|
|
19
20
|
|
|
20
21
|
private _rooms = new Map<string, PluvRoom<TIO, any, any>>();
|
|
21
22
|
|
|
@@ -45,7 +46,7 @@ export class PluvClient<TIO extends IOLike = IOLike> {
|
|
|
45
46
|
initialStorage: options.initialStorage,
|
|
46
47
|
onAuthorizationFail: options.onAuthorizationFail,
|
|
47
48
|
wsEndpoint: this._wsEndpoint,
|
|
48
|
-
});
|
|
49
|
+
} as RoomConfig<TIO, TPresence, TStorage>);
|
|
49
50
|
|
|
50
51
|
this._rooms.set(room, newRoom);
|
|
51
52
|
|
package/src/PluvRoom.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { InferYjsSharedTypeJson } from "@pluv/crdt-yjs";
|
|
2
2
|
import type {
|
|
3
3
|
BaseIOEventRecord,
|
|
4
4
|
EventMessage,
|
|
@@ -70,21 +70,19 @@ interface TimeoutIds {
|
|
|
70
70
|
reconnect: number | null;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
export type AuthEndpoint =
|
|
73
|
+
export type AuthEndpoint =
|
|
74
|
+
| string
|
|
75
|
+
| ((room: string) => string | FetchOptions)
|
|
76
|
+
| true;
|
|
74
77
|
export type WsEndpoint = string | ((room: string) => string);
|
|
75
78
|
|
|
76
|
-
|
|
77
|
-
authEndpoint?: AuthEndpoint;
|
|
78
|
-
wsEndpoint: WsEndpoint;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
type FetchOptions = string | { url: string; options?: RequestInit };
|
|
79
|
+
type FetchOptions = { url: string; options?: RequestInit };
|
|
82
80
|
|
|
83
81
|
export type RoomEndpoints<TIO extends IOLike> = {
|
|
84
|
-
wsEndpoint
|
|
82
|
+
wsEndpoint?: WsEndpoint;
|
|
85
83
|
} & (InferIOAuthorizeRequired<InferIOAuthorize<TIO>> extends true
|
|
86
84
|
? { authEndpoint: AuthEndpoint }
|
|
87
|
-
: { authEndpoint?:
|
|
85
|
+
: { authEndpoint?: undefined });
|
|
88
86
|
|
|
89
87
|
interface InternalListeners {
|
|
90
88
|
onAuthorizationFail: (error: Error) => void;
|
|
@@ -134,6 +132,7 @@ export class PluvRoom<
|
|
|
134
132
|
user: null,
|
|
135
133
|
},
|
|
136
134
|
connection: {
|
|
135
|
+
count: 0,
|
|
137
136
|
id: null,
|
|
138
137
|
state: ConnectionState.Untouched,
|
|
139
138
|
},
|
|
@@ -166,12 +165,7 @@ export class PluvRoom<
|
|
|
166
165
|
|
|
167
166
|
this._debug = debug;
|
|
168
167
|
|
|
169
|
-
this._endpoints = {
|
|
170
|
-
authEndpoint: authEndpoint as
|
|
171
|
-
| string
|
|
172
|
-
| ((room: string) => FetchOptions),
|
|
173
|
-
wsEndpoint,
|
|
174
|
-
};
|
|
168
|
+
this._endpoints = { authEndpoint, wsEndpoint } as RoomEndpoints<TIO>;
|
|
175
169
|
|
|
176
170
|
this._listeners = {
|
|
177
171
|
onAuthorizationFail: (error) => {
|
|
@@ -493,33 +487,60 @@ export class PluvRoom<
|
|
|
493
487
|
}
|
|
494
488
|
|
|
495
489
|
private async _getAuthorization(room: string): Promise<string | null> {
|
|
496
|
-
|
|
490
|
+
const fetchOptions = this._getAuthFetchOptions(room);
|
|
497
491
|
|
|
498
|
-
if (
|
|
499
|
-
const token = await fetch(this._endpoints.authEndpoint)
|
|
500
|
-
.then((res) => res.text())
|
|
501
|
-
.then((text) => text.trim());
|
|
492
|
+
if (!fetchOptions) return null;
|
|
502
493
|
|
|
503
|
-
|
|
494
|
+
const { url, options } = fetchOptions;
|
|
495
|
+
|
|
496
|
+
const res = await fetch(url, options);
|
|
497
|
+
|
|
498
|
+
if (!res.ok || res.status !== 200) {
|
|
499
|
+
throw new Error("Room is unauthorized");
|
|
504
500
|
}
|
|
505
501
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
502
|
+
try {
|
|
503
|
+
return await res.text().then((text) => text.trim());
|
|
504
|
+
} catch (err) {
|
|
505
|
+
throw new Error(
|
|
506
|
+
err instanceof Error ? err.message : "Room is unauthorized"
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
509
510
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
.then((text) => text.trim());
|
|
511
|
+
private _getAuthFetchOptions(room: string): FetchOptions | null {
|
|
512
|
+
if (typeof this._endpoints.authEndpoint === "undefined") return null;
|
|
513
513
|
|
|
514
|
-
|
|
514
|
+
if (this._endpoints.authEndpoint === true) {
|
|
515
|
+
return {
|
|
516
|
+
url: `/api/pluv/authorize?room=${room}`,
|
|
517
|
+
options: {},
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (typeof this._endpoints.authEndpoint === "string") {
|
|
522
|
+
return {
|
|
523
|
+
url: this._endpoints.authEndpoint,
|
|
524
|
+
options: {},
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const result = this._endpoints.authEndpoint(room);
|
|
529
|
+
|
|
530
|
+
return typeof result === "string"
|
|
531
|
+
? { url: result, options: {} }
|
|
532
|
+
: result;
|
|
515
533
|
}
|
|
516
534
|
|
|
517
535
|
private _getWsEndpoint(room: string): string {
|
|
518
|
-
|
|
519
|
-
|
|
536
|
+
switch (typeof this._endpoints.wsEndpoint) {
|
|
537
|
+
case "undefined":
|
|
538
|
+
return `/api/pluv/room/${room}`;
|
|
539
|
+
case "string":
|
|
540
|
+
return this._endpoints.wsEndpoint;
|
|
541
|
+
default:
|
|
542
|
+
return this._endpoints.wsEndpoint(room);
|
|
520
543
|
}
|
|
521
|
-
|
|
522
|
-
return this._endpoints.wsEndpoint(room);
|
|
523
544
|
}
|
|
524
545
|
|
|
525
546
|
private _handleExit(message: IOEventMessage<TIO>): void {
|
|
@@ -611,6 +632,7 @@ export class PluvRoom<
|
|
|
611
632
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
612
633
|
|
|
613
634
|
this._updateState((oldState) => {
|
|
635
|
+
oldState.connection.count += 1;
|
|
614
636
|
oldState.connection.id = connectionId;
|
|
615
637
|
oldState.authorization.user = user;
|
|
616
638
|
|
|
@@ -622,13 +644,21 @@ export class PluvRoom<
|
|
|
622
644
|
user as Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>
|
|
623
645
|
);
|
|
624
646
|
|
|
625
|
-
const
|
|
647
|
+
const presence = this._usersManager.myPresence;
|
|
626
648
|
const myself = this._usersManager.myself ?? null;
|
|
627
649
|
|
|
628
|
-
this._stateNotifier.subjects["my-presence"].next(
|
|
650
|
+
this._stateNotifier.subjects["my-presence"].next(presence);
|
|
629
651
|
this._stateNotifier.subjects["myself"].next(myself);
|
|
630
652
|
|
|
631
|
-
|
|
653
|
+
const update =
|
|
654
|
+
this._state.connection.count > 1
|
|
655
|
+
? this._crdtManager?.doc.encodeStateAsUpdate() ?? null
|
|
656
|
+
: null;
|
|
657
|
+
|
|
658
|
+
this._sendMessage({
|
|
659
|
+
type: "$INITIALIZE_SESSION",
|
|
660
|
+
data: { presence, update },
|
|
661
|
+
});
|
|
632
662
|
}
|
|
633
663
|
|
|
634
664
|
private _handleStorageReceivedMessage(message: IOEventMessage<TIO>): void {
|
|
@@ -708,7 +738,17 @@ export class PluvRoom<
|
|
|
708
738
|
InferIOAuthorize<TIO>
|
|
709
739
|
>["$USER_JOINED"];
|
|
710
740
|
|
|
711
|
-
this._usersManager.
|
|
741
|
+
const myself = this._usersManager.myself;
|
|
742
|
+
|
|
743
|
+
if (myself.connectionId === connectionId) {
|
|
744
|
+
this._sendMessage({ type: "$GET_OTHERS", data: {} });
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
this._usersManager.setUser(
|
|
748
|
+
connectionId,
|
|
749
|
+
data.user,
|
|
750
|
+
data.presence as TPresence
|
|
751
|
+
);
|
|
712
752
|
|
|
713
753
|
const other = this._usersManager.getOther(connectionId);
|
|
714
754
|
const others = this._usersManager.getOthers();
|
|
@@ -718,12 +758,6 @@ export class PluvRoom<
|
|
|
718
758
|
}
|
|
719
759
|
|
|
720
760
|
private _heartbeat(): void {
|
|
721
|
-
if (!this._state.webSocket) return;
|
|
722
|
-
if (!this._state.connection.id) return;
|
|
723
|
-
if (this._state.webSocket.readyState !== this._state.webSocket.OPEN) {
|
|
724
|
-
return;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
761
|
this._clearTimeout(this._timeouts.pong);
|
|
728
762
|
this._timeouts.pong = setTimeout(
|
|
729
763
|
this._reconnect.bind(this),
|
|
@@ -957,7 +991,7 @@ export class PluvRoom<
|
|
|
957
991
|
|
|
958
992
|
this._updateState((oldState) => {
|
|
959
993
|
oldState.authorization.token = null;
|
|
960
|
-
oldState.connection.state = ConnectionState.
|
|
994
|
+
oldState.connection.state = ConnectionState.Closed;
|
|
961
995
|
oldState.webSocket = null;
|
|
962
996
|
|
|
963
997
|
return oldState;
|
package/src/UsersManager.ts
CHANGED
|
@@ -128,7 +128,8 @@ export class UsersManager<
|
|
|
128
128
|
|
|
129
129
|
public setUser(
|
|
130
130
|
connectionId: string,
|
|
131
|
-
user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO
|
|
131
|
+
user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>,
|
|
132
|
+
presence?: TPresence
|
|
132
133
|
): void {
|
|
133
134
|
if (this._myself?.connectionId === connectionId) return;
|
|
134
135
|
|
|
@@ -138,7 +139,7 @@ export class UsersManager<
|
|
|
138
139
|
|
|
139
140
|
this._others.set(connectionId, {
|
|
140
141
|
connectionId,
|
|
141
|
-
presence: this._initialPresence,
|
|
142
|
+
presence: presence ?? this._initialPresence,
|
|
142
143
|
user,
|
|
143
144
|
});
|
|
144
145
|
}
|