@pluv/client 0.1.3 → 0.3.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 +7 -7
- package/CHANGELOG.md +18 -0
- package/README.md +3 -1
- package/dist/index.d.ts +57 -11
- package/dist/index.js +144 -15
- package/dist/index.mjs +142 -15
- package/package.json +3 -3
- package/src/AbstractRoom.ts +71 -0
- package/src/MockedRoom.ts +214 -0
- package/src/PluvRoom.ts +14 -13
- package/src/index.ts +3 -0
- package/src/types.ts +4 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @pluv/client@0.
|
|
2
|
+
> @pluv/client@0.3.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
|
-
[
|
|
12
|
-
[
|
|
13
|
-
[
|
|
14
|
-
[
|
|
11
|
+
[32mCJS[39m [1mdist/index.js [22m[32m34.07 KB[39m
|
|
12
|
+
[32mCJS[39m ⚡️ Build success in 83ms
|
|
13
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m32.92 KB[39m
|
|
14
|
+
[32mESM[39m ⚡️ Build success in 88ms
|
|
15
15
|
[34mDTS[39m Build start
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 3519ms
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m11.52 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @pluv/client
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 595e66f: added mocked-room for mocking a client-side room for testing
|
|
8
|
+
|
|
9
|
+
## 0.2.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 95b5ef8: breaking: update PluvRoom.broadcast api
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [6858682]
|
|
18
|
+
- @pluv/crdt-yjs@0.2.0
|
|
19
|
+
- @pluv/types@0.1.2
|
|
20
|
+
|
|
3
21
|
## 0.1.3
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
<img src="https://img.shields.io/badge/docs-coming%20soon!-blue" alt="Docs coming soon!" />
|
|
27
27
|
</p>
|
|
28
28
|
|
|
29
|
-
<
|
|
29
|
+
<p align="center">
|
|
30
|
+
<img src="https://github.com/pluv-io/pluv/blob/master/assets/demo-events.gif?raw=true" alt="Demo" />
|
|
31
|
+
</p>
|
|
30
32
|
|
|
31
33
|
## `@pluv/client`
|
|
32
34
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { InferYjsSharedTypeJson, array, map, text, unstable__object } from '@pluv/crdt-yjs';
|
|
2
2
|
export { InferYjsSharedTypeJson } from '@pluv/crdt-yjs';
|
|
3
3
|
import * as _pluv_types from '@pluv/types';
|
|
4
|
-
import { IOLike, InferIOOutput, Id, IOEventMessage, JsonObject, InferIOAuthorizeUser, InferIOAuthorize,
|
|
4
|
+
import { IOLike, InferIOOutput, Id, IOEventMessage, JsonObject, InferIOAuthorizeUser, InferIOAuthorize, InferIOInput, InputZodLike, InferIOAuthorizeRequired } from '@pluv/types';
|
|
5
5
|
export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
|
|
6
6
|
import { AbstractType } from 'yjs';
|
|
7
7
|
export { AbstractType } from 'yjs';
|
|
8
8
|
import { Subject } from 'wonka';
|
|
9
9
|
|
|
10
|
-
interface CrdtManagerOptions<TStorage extends Record<string, AbstractType<any>> = {}> {
|
|
11
|
-
encodedState?: string | Uint8Array | null;
|
|
12
|
-
initialStorage?: () => TStorage;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
10
|
type EventNotifierSubscriptionCallback<TIO extends IOLike, TEvent extends keyof InferIOOutput<TIO>> = (value: Id<IOEventMessage<TIO, TEvent>>) => void;
|
|
16
11
|
|
|
17
12
|
interface AuthorizationState<TIO extends IOLike> {
|
|
@@ -51,6 +46,27 @@ interface StateNotifierSubjects<TIO extends IOLike, TPresence extends JsonObject
|
|
|
51
46
|
type InferSubjectValue<TIO extends IOLike, TPresence extends JsonObject, TSubject extends keyof StateNotifierSubjects<TIO, TPresence>> = StateNotifierSubjects<TIO, TPresence>[TSubject] extends Subject<infer IValue> ? Id<IValue> : never;
|
|
52
47
|
type SubscriptionCallback<TIO extends IOLike, TPresence extends JsonObject, TSubject extends keyof StateNotifierSubjects<TIO, TPresence>> = (value: InferSubjectValue<TIO, TPresence, TSubject>) => void;
|
|
53
48
|
|
|
49
|
+
declare abstract class AbstractRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}> {
|
|
50
|
+
readonly id: string;
|
|
51
|
+
constructor(room: string);
|
|
52
|
+
abstract broadcast<TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>): void;
|
|
53
|
+
abstract event<TEvent extends keyof InferIOOutput<TIO>>(event: TEvent, callback: EventNotifierSubscriptionCallback<TIO, TEvent>): () => void;
|
|
54
|
+
abstract getConnection(): WebSocketConnection;
|
|
55
|
+
abstract getMyPresence(): TPresence | null;
|
|
56
|
+
abstract getMyself(): Id<UserInfo<TIO, TPresence>> | null;
|
|
57
|
+
abstract getOther(connectionId: string): Id<UserInfo<TIO, TPresence>> | null;
|
|
58
|
+
abstract getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
|
|
59
|
+
abstract getStorage<TKey extends keyof TStorage>(key: TKey): TStorage[TKey] | null;
|
|
60
|
+
abstract other(connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): () => void;
|
|
61
|
+
abstract storage<TKey extends keyof TStorage>(key: TKey, fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void): () => void;
|
|
62
|
+
abstract subscribe<TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>): () => void;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface CrdtManagerOptions<TStorage extends Record<string, AbstractType<any>> = {}> {
|
|
66
|
+
encodedState?: string | Uint8Array | null;
|
|
67
|
+
initialStorage?: () => TStorage;
|
|
68
|
+
}
|
|
69
|
+
|
|
54
70
|
type UsersManagerConfig<TPresence extends JsonObject = {}> = {
|
|
55
71
|
initialPresence?: TPresence;
|
|
56
72
|
presence?: InputZodLike<TPresence>;
|
|
@@ -85,9 +101,8 @@ type PluvRoomOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TSto
|
|
|
85
101
|
onAuthorizationFail?: (error: Error) => void;
|
|
86
102
|
} & Omit<CrdtManagerOptions<TStorage>, "encodedState"> & UsersManagerConfig<TPresence>;
|
|
87
103
|
type RoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}> = RoomEndpoints<TIO> & PluvRoomOptions<TIO, TPresence, TStorage>;
|
|
88
|
-
declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}> {
|
|
104
|
+
declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}> extends AbstractRoom<TIO, TPresence, TStorage> {
|
|
89
105
|
readonly _endpoints: RoomEndpoints<TIO>;
|
|
90
|
-
readonly id: string;
|
|
91
106
|
private _crdtManager;
|
|
92
107
|
private _crdtNotifier;
|
|
93
108
|
private _debug;
|
|
@@ -105,10 +120,10 @@ declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TS
|
|
|
105
120
|
private _usersManager;
|
|
106
121
|
constructor(room: string, options: RoomConfig<TIO, TPresence, TStorage>);
|
|
107
122
|
get webSocket(): WebSocket | null;
|
|
108
|
-
broadcast(
|
|
123
|
+
broadcast<TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>): void;
|
|
109
124
|
connect(): Promise<void>;
|
|
110
125
|
disconnect(): void;
|
|
111
|
-
event: <TEvent extends keyof InferIOOutput<TIO>>(
|
|
126
|
+
event: <TEvent extends keyof InferIOOutput<TIO>>(event: TEvent, callback: EventNotifierSubscriptionCallback<TIO, TEvent>) => (() => void);
|
|
112
127
|
getConnection(): WebSocketConnection;
|
|
113
128
|
getMyPresence: () => TPresence | null;
|
|
114
129
|
getMyself: () => Id<UserInfo<TIO, TPresence>> | null;
|
|
@@ -182,6 +197,37 @@ declare class PluvClient<TIO extends IOLike = IOLike> {
|
|
|
182
197
|
|
|
183
198
|
declare const createClient: <TIO extends IOLike<_pluv_types.IOAuthorize<any, any>, {}, {}> = IOLike<_pluv_types.IOAuthorize<any, any>, {}, {}>>(options: PluvClientOptions<TIO>) => PluvClient<TIO>;
|
|
184
199
|
|
|
200
|
+
type MockedRoomEvents<TIO extends IOLike> = {
|
|
201
|
+
[P in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[P]>) => Partial<InferIOOutput<TIO>>;
|
|
202
|
+
};
|
|
203
|
+
type MockedRoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}> = {
|
|
204
|
+
events?: MockedRoomEvents<TIO>;
|
|
205
|
+
} & Omit<CrdtManagerOptions<TStorage>, "encodedState"> & UsersManagerConfig<TPresence>;
|
|
206
|
+
declare class MockedRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}> extends AbstractRoom<TIO, TPresence, TStorage> {
|
|
207
|
+
private _crdtManager;
|
|
208
|
+
private _crdtNotifier;
|
|
209
|
+
private _eventNotifier;
|
|
210
|
+
private _events?;
|
|
211
|
+
private _otherNotifier;
|
|
212
|
+
private _state;
|
|
213
|
+
private _stateNotifier;
|
|
214
|
+
private _subscriptions;
|
|
215
|
+
private _usersManager;
|
|
216
|
+
constructor(room: string, options: MockedRoomConfig<TIO, TPresence, TStorage>);
|
|
217
|
+
broadcast<TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>): void;
|
|
218
|
+
event: <TEvent extends keyof InferIOOutput<TIO>>(event: TEvent, callback: EventNotifierSubscriptionCallback<TIO, TEvent>) => (() => void);
|
|
219
|
+
getConnection(): WebSocketConnection;
|
|
220
|
+
getMyPresence: () => TPresence | null;
|
|
221
|
+
getMyself: () => Id<UserInfo<TIO, TPresence>> | null;
|
|
222
|
+
getOther: (connectionId: string) => Id<UserInfo<TIO, TPresence>> | null;
|
|
223
|
+
getOthers: () => readonly Id<UserInfo<TIO, TPresence>>[];
|
|
224
|
+
getStorage: <TKey extends keyof TStorage>(key: TKey) => TStorage[TKey] | null;
|
|
225
|
+
other: (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>) => (() => void);
|
|
226
|
+
storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void) => (() => void);
|
|
227
|
+
subscribe: <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>) => (() => void);
|
|
228
|
+
private _observeCrdt;
|
|
229
|
+
}
|
|
230
|
+
|
|
185
231
|
declare const y_array: typeof array;
|
|
186
232
|
declare const y_map: typeof map;
|
|
187
233
|
declare const y_text: typeof text;
|
|
@@ -195,4 +241,4 @@ declare namespace y {
|
|
|
195
241
|
};
|
|
196
242
|
}
|
|
197
243
|
|
|
198
|
-
export { ConnectionState, PluvClient, PluvClientOptions, PluvRoom, PluvRoomDebug, UserInfo, WebSocketConnection, WebSocketState, createClient, y };
|
|
244
|
+
export { AbstractRoom, ConnectionState, MockedRoom, MockedRoomConfig, MockedRoomEvents, PluvClient, PluvClientOptions, PluvRoom, PluvRoomDebug, UserInfo, WebSocketConnection, WebSocketState, createClient, y };
|
package/dist/index.js
CHANGED
|
@@ -57,7 +57,9 @@ var __async = (__this, __arguments, generator) => {
|
|
|
57
57
|
// src/index.ts
|
|
58
58
|
var src_exports = {};
|
|
59
59
|
__export(src_exports, {
|
|
60
|
+
AbstractRoom: () => AbstractRoom,
|
|
60
61
|
ConnectionState: () => ConnectionState,
|
|
62
|
+
MockedRoom: () => MockedRoom,
|
|
61
63
|
PluvClient: () => PluvClient,
|
|
62
64
|
PluvRoom: () => PluvRoom,
|
|
63
65
|
createClient: () => createClient,
|
|
@@ -65,6 +67,13 @@ __export(src_exports, {
|
|
|
65
67
|
});
|
|
66
68
|
module.exports = __toCommonJS(src_exports);
|
|
67
69
|
|
|
70
|
+
// src/AbstractRoom.ts
|
|
71
|
+
var AbstractRoom = class {
|
|
72
|
+
constructor(room) {
|
|
73
|
+
this.id = room;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
68
77
|
// src/PluvRoom.ts
|
|
69
78
|
var import_crdt_yjs2 = require("@pluv/crdt-yjs");
|
|
70
79
|
|
|
@@ -267,8 +276,18 @@ var UsersManager = class {
|
|
|
267
276
|
var HEARTBEAT_INTERVAL_MS = 1e4;
|
|
268
277
|
var PONG_TIMEOUT_MS = 2e3;
|
|
269
278
|
var RECONNECT_TIMEOUT_MS = 3e4;
|
|
270
|
-
var PluvRoom = class {
|
|
279
|
+
var PluvRoom = class extends AbstractRoom {
|
|
271
280
|
constructor(room, options) {
|
|
281
|
+
const {
|
|
282
|
+
authEndpoint,
|
|
283
|
+
debug = false,
|
|
284
|
+
initialPresence,
|
|
285
|
+
initialStorage,
|
|
286
|
+
onAuthorizationFail,
|
|
287
|
+
presence,
|
|
288
|
+
wsEndpoint
|
|
289
|
+
} = options;
|
|
290
|
+
super(room);
|
|
272
291
|
this._crdtManager = null;
|
|
273
292
|
this._crdtNotifier = new CrdtNotifier();
|
|
274
293
|
this._eventNotifier = new EventNotifier();
|
|
@@ -298,8 +317,8 @@ var PluvRoom = class {
|
|
|
298
317
|
};
|
|
299
318
|
this._windowListeners = null;
|
|
300
319
|
this._wsListeners = null;
|
|
301
|
-
this.event = (
|
|
302
|
-
return this._eventNotifier.subscribe(
|
|
320
|
+
this.event = (event, callback) => {
|
|
321
|
+
return this._eventNotifier.subscribe(event, callback);
|
|
303
322
|
};
|
|
304
323
|
this.getMyPresence = () => {
|
|
305
324
|
var _a, _b;
|
|
@@ -327,16 +346,6 @@ var PluvRoom = class {
|
|
|
327
346
|
this.subscribe = (name, callback) => {
|
|
328
347
|
return this._stateNotifier.subscribe(name, callback);
|
|
329
348
|
};
|
|
330
|
-
const {
|
|
331
|
-
authEndpoint,
|
|
332
|
-
debug = false,
|
|
333
|
-
initialPresence,
|
|
334
|
-
initialStorage,
|
|
335
|
-
onAuthorizationFail,
|
|
336
|
-
presence,
|
|
337
|
-
wsEndpoint
|
|
338
|
-
} = options;
|
|
339
|
-
this.id = room;
|
|
340
349
|
this._debug = debug;
|
|
341
350
|
this._endpoints = {
|
|
342
351
|
authEndpoint,
|
|
@@ -362,12 +371,13 @@ var PluvRoom = class {
|
|
|
362
371
|
get webSocket() {
|
|
363
372
|
return this._state.webSocket;
|
|
364
373
|
}
|
|
365
|
-
broadcast(
|
|
374
|
+
broadcast(event, data) {
|
|
366
375
|
if (!this._state.webSocket)
|
|
367
376
|
return;
|
|
368
377
|
if (this._state.connection.state !== "Open" /* Open */)
|
|
369
378
|
return;
|
|
370
|
-
|
|
379
|
+
const type = event.toString();
|
|
380
|
+
this._sendMessage({ data, type });
|
|
371
381
|
}
|
|
372
382
|
connect() {
|
|
373
383
|
return __async(this, null, function* () {
|
|
@@ -997,6 +1007,123 @@ var createClient = (options) => {
|
|
|
997
1007
|
return new PluvClient(options);
|
|
998
1008
|
};
|
|
999
1009
|
|
|
1010
|
+
// src/MockedRoom.ts
|
|
1011
|
+
var MockedRoom = class extends AbstractRoom {
|
|
1012
|
+
constructor(room, options) {
|
|
1013
|
+
const { events, initialPresence, initialStorage, presence } = options;
|
|
1014
|
+
super(room);
|
|
1015
|
+
this._crdtManager = null;
|
|
1016
|
+
this._crdtNotifier = new CrdtNotifier();
|
|
1017
|
+
this._eventNotifier = new EventNotifier();
|
|
1018
|
+
this._otherNotifier = new OtherNotifier();
|
|
1019
|
+
this._state = {
|
|
1020
|
+
authorization: {
|
|
1021
|
+
token: null,
|
|
1022
|
+
user: null
|
|
1023
|
+
},
|
|
1024
|
+
connection: {
|
|
1025
|
+
id: null,
|
|
1026
|
+
state: "Untouched" /* Untouched */
|
|
1027
|
+
},
|
|
1028
|
+
webSocket: null
|
|
1029
|
+
};
|
|
1030
|
+
this._stateNotifier = new StateNotifier();
|
|
1031
|
+
this._subscriptions = {
|
|
1032
|
+
observeCrdt: null
|
|
1033
|
+
};
|
|
1034
|
+
this.event = (event, callback) => {
|
|
1035
|
+
return this._eventNotifier.subscribe(event, callback);
|
|
1036
|
+
};
|
|
1037
|
+
this.getMyPresence = () => {
|
|
1038
|
+
var _a, _b;
|
|
1039
|
+
return (_b = (_a = this._usersManager.myself) == null ? void 0 : _a.presence) != null ? _b : null;
|
|
1040
|
+
};
|
|
1041
|
+
this.getMyself = () => {
|
|
1042
|
+
return this._usersManager.myself;
|
|
1043
|
+
};
|
|
1044
|
+
this.getOther = (connectionId) => {
|
|
1045
|
+
return this._usersManager.getOther(connectionId);
|
|
1046
|
+
};
|
|
1047
|
+
this.getOthers = () => {
|
|
1048
|
+
return this._usersManager.getOthers();
|
|
1049
|
+
};
|
|
1050
|
+
this.getStorage = (key) => {
|
|
1051
|
+
var _a, _b;
|
|
1052
|
+
return (_b = (_a = this._crdtManager) == null ? void 0 : _a.get(key)) != null ? _b : null;
|
|
1053
|
+
};
|
|
1054
|
+
this.other = (connectionId, callback) => {
|
|
1055
|
+
return this._otherNotifier.subscribe(connectionId, callback);
|
|
1056
|
+
};
|
|
1057
|
+
this.storage = (key, fn) => {
|
|
1058
|
+
return this._crdtNotifier.subscribe(key, fn);
|
|
1059
|
+
};
|
|
1060
|
+
this.subscribe = (name, callback) => {
|
|
1061
|
+
return this._stateNotifier.subscribe(name, callback);
|
|
1062
|
+
};
|
|
1063
|
+
this._events = events;
|
|
1064
|
+
this._usersManager = new UsersManager({
|
|
1065
|
+
initialPresence,
|
|
1066
|
+
presence
|
|
1067
|
+
});
|
|
1068
|
+
this._crdtManager = new CrdtManager({
|
|
1069
|
+
initialStorage
|
|
1070
|
+
});
|
|
1071
|
+
this._observeCrdt();
|
|
1072
|
+
}
|
|
1073
|
+
broadcast(event, data) {
|
|
1074
|
+
if (!this._events)
|
|
1075
|
+
return;
|
|
1076
|
+
const resolver = this._events[event];
|
|
1077
|
+
if (!resolver)
|
|
1078
|
+
return;
|
|
1079
|
+
const result = resolver(data);
|
|
1080
|
+
Object.keys(result).forEach((type) => {
|
|
1081
|
+
const _type = type.toString();
|
|
1082
|
+
const data2 = result[_type];
|
|
1083
|
+
if (!data2)
|
|
1084
|
+
return;
|
|
1085
|
+
this._eventNotifier.subject(_type).next(data2);
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
getConnection() {
|
|
1089
|
+
return Object.freeze(
|
|
1090
|
+
JSON.parse(JSON.stringify(this._state.connection))
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
_observeCrdt() {
|
|
1094
|
+
var _a, _b;
|
|
1095
|
+
(_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
|
|
1096
|
+
if (!this._crdtManager)
|
|
1097
|
+
return;
|
|
1098
|
+
const unsubscribe = this._crdtManager.doc.subscribe(
|
|
1099
|
+
(update, _origin) => {
|
|
1100
|
+
const origin = _origin != null ? _origin : null;
|
|
1101
|
+
if (!this._crdtManager)
|
|
1102
|
+
return;
|
|
1103
|
+
if (!this._state.webSocket)
|
|
1104
|
+
return;
|
|
1105
|
+
if (!this._state.connection.id)
|
|
1106
|
+
return;
|
|
1107
|
+
if (this._state.webSocket.readyState !== WebSocket.OPEN)
|
|
1108
|
+
return;
|
|
1109
|
+
if (origin === "$STORAGE_UPDATED")
|
|
1110
|
+
return;
|
|
1111
|
+
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
1112
|
+
Object.keys(sharedTypes).forEach((key) => {
|
|
1113
|
+
if (!this._crdtManager)
|
|
1114
|
+
return;
|
|
1115
|
+
const sharedType = sharedTypes[key];
|
|
1116
|
+
if (!sharedType)
|
|
1117
|
+
return;
|
|
1118
|
+
const serialized = sharedType.toJSON();
|
|
1119
|
+
this._crdtNotifier.subject(key).next(serialized);
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
);
|
|
1123
|
+
this._subscriptions.observeCrdt = unsubscribe;
|
|
1124
|
+
}
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1000
1127
|
// src/y.ts
|
|
1001
1128
|
var y_exports = {};
|
|
1002
1129
|
__export(y_exports, {
|
|
@@ -1008,7 +1135,9 @@ __export(y_exports, {
|
|
|
1008
1135
|
var import_crdt_yjs3 = require("@pluv/crdt-yjs");
|
|
1009
1136
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1010
1137
|
0 && (module.exports = {
|
|
1138
|
+
AbstractRoom,
|
|
1011
1139
|
ConnectionState,
|
|
1140
|
+
MockedRoom,
|
|
1012
1141
|
PluvClient,
|
|
1013
1142
|
PluvRoom,
|
|
1014
1143
|
createClient,
|
package/dist/index.mjs
CHANGED
|
@@ -42,6 +42,13 @@ var __async = (__this, __arguments, generator) => {
|
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
// src/AbstractRoom.ts
|
|
46
|
+
var AbstractRoom = class {
|
|
47
|
+
constructor(room) {
|
|
48
|
+
this.id = room;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
45
52
|
// src/PluvRoom.ts
|
|
46
53
|
import { doc as doc2 } from "@pluv/crdt-yjs";
|
|
47
54
|
|
|
@@ -244,8 +251,18 @@ var UsersManager = class {
|
|
|
244
251
|
var HEARTBEAT_INTERVAL_MS = 1e4;
|
|
245
252
|
var PONG_TIMEOUT_MS = 2e3;
|
|
246
253
|
var RECONNECT_TIMEOUT_MS = 3e4;
|
|
247
|
-
var PluvRoom = class {
|
|
254
|
+
var PluvRoom = class extends AbstractRoom {
|
|
248
255
|
constructor(room, options) {
|
|
256
|
+
const {
|
|
257
|
+
authEndpoint,
|
|
258
|
+
debug = false,
|
|
259
|
+
initialPresence,
|
|
260
|
+
initialStorage,
|
|
261
|
+
onAuthorizationFail,
|
|
262
|
+
presence,
|
|
263
|
+
wsEndpoint
|
|
264
|
+
} = options;
|
|
265
|
+
super(room);
|
|
249
266
|
this._crdtManager = null;
|
|
250
267
|
this._crdtNotifier = new CrdtNotifier();
|
|
251
268
|
this._eventNotifier = new EventNotifier();
|
|
@@ -275,8 +292,8 @@ var PluvRoom = class {
|
|
|
275
292
|
};
|
|
276
293
|
this._windowListeners = null;
|
|
277
294
|
this._wsListeners = null;
|
|
278
|
-
this.event = (
|
|
279
|
-
return this._eventNotifier.subscribe(
|
|
295
|
+
this.event = (event, callback) => {
|
|
296
|
+
return this._eventNotifier.subscribe(event, callback);
|
|
280
297
|
};
|
|
281
298
|
this.getMyPresence = () => {
|
|
282
299
|
var _a, _b;
|
|
@@ -304,16 +321,6 @@ var PluvRoom = class {
|
|
|
304
321
|
this.subscribe = (name, callback) => {
|
|
305
322
|
return this._stateNotifier.subscribe(name, callback);
|
|
306
323
|
};
|
|
307
|
-
const {
|
|
308
|
-
authEndpoint,
|
|
309
|
-
debug = false,
|
|
310
|
-
initialPresence,
|
|
311
|
-
initialStorage,
|
|
312
|
-
onAuthorizationFail,
|
|
313
|
-
presence,
|
|
314
|
-
wsEndpoint
|
|
315
|
-
} = options;
|
|
316
|
-
this.id = room;
|
|
317
324
|
this._debug = debug;
|
|
318
325
|
this._endpoints = {
|
|
319
326
|
authEndpoint,
|
|
@@ -339,12 +346,13 @@ var PluvRoom = class {
|
|
|
339
346
|
get webSocket() {
|
|
340
347
|
return this._state.webSocket;
|
|
341
348
|
}
|
|
342
|
-
broadcast(
|
|
349
|
+
broadcast(event, data) {
|
|
343
350
|
if (!this._state.webSocket)
|
|
344
351
|
return;
|
|
345
352
|
if (this._state.connection.state !== "Open" /* Open */)
|
|
346
353
|
return;
|
|
347
|
-
|
|
354
|
+
const type = event.toString();
|
|
355
|
+
this._sendMessage({ data, type });
|
|
348
356
|
}
|
|
349
357
|
connect() {
|
|
350
358
|
return __async(this, null, function* () {
|
|
@@ -974,6 +982,123 @@ var createClient = (options) => {
|
|
|
974
982
|
return new PluvClient(options);
|
|
975
983
|
};
|
|
976
984
|
|
|
985
|
+
// src/MockedRoom.ts
|
|
986
|
+
var MockedRoom = class extends AbstractRoom {
|
|
987
|
+
constructor(room, options) {
|
|
988
|
+
const { events, initialPresence, initialStorage, presence } = options;
|
|
989
|
+
super(room);
|
|
990
|
+
this._crdtManager = null;
|
|
991
|
+
this._crdtNotifier = new CrdtNotifier();
|
|
992
|
+
this._eventNotifier = new EventNotifier();
|
|
993
|
+
this._otherNotifier = new OtherNotifier();
|
|
994
|
+
this._state = {
|
|
995
|
+
authorization: {
|
|
996
|
+
token: null,
|
|
997
|
+
user: null
|
|
998
|
+
},
|
|
999
|
+
connection: {
|
|
1000
|
+
id: null,
|
|
1001
|
+
state: "Untouched" /* Untouched */
|
|
1002
|
+
},
|
|
1003
|
+
webSocket: null
|
|
1004
|
+
};
|
|
1005
|
+
this._stateNotifier = new StateNotifier();
|
|
1006
|
+
this._subscriptions = {
|
|
1007
|
+
observeCrdt: null
|
|
1008
|
+
};
|
|
1009
|
+
this.event = (event, callback) => {
|
|
1010
|
+
return this._eventNotifier.subscribe(event, callback);
|
|
1011
|
+
};
|
|
1012
|
+
this.getMyPresence = () => {
|
|
1013
|
+
var _a, _b;
|
|
1014
|
+
return (_b = (_a = this._usersManager.myself) == null ? void 0 : _a.presence) != null ? _b : null;
|
|
1015
|
+
};
|
|
1016
|
+
this.getMyself = () => {
|
|
1017
|
+
return this._usersManager.myself;
|
|
1018
|
+
};
|
|
1019
|
+
this.getOther = (connectionId) => {
|
|
1020
|
+
return this._usersManager.getOther(connectionId);
|
|
1021
|
+
};
|
|
1022
|
+
this.getOthers = () => {
|
|
1023
|
+
return this._usersManager.getOthers();
|
|
1024
|
+
};
|
|
1025
|
+
this.getStorage = (key) => {
|
|
1026
|
+
var _a, _b;
|
|
1027
|
+
return (_b = (_a = this._crdtManager) == null ? void 0 : _a.get(key)) != null ? _b : null;
|
|
1028
|
+
};
|
|
1029
|
+
this.other = (connectionId, callback) => {
|
|
1030
|
+
return this._otherNotifier.subscribe(connectionId, callback);
|
|
1031
|
+
};
|
|
1032
|
+
this.storage = (key, fn) => {
|
|
1033
|
+
return this._crdtNotifier.subscribe(key, fn);
|
|
1034
|
+
};
|
|
1035
|
+
this.subscribe = (name, callback) => {
|
|
1036
|
+
return this._stateNotifier.subscribe(name, callback);
|
|
1037
|
+
};
|
|
1038
|
+
this._events = events;
|
|
1039
|
+
this._usersManager = new UsersManager({
|
|
1040
|
+
initialPresence,
|
|
1041
|
+
presence
|
|
1042
|
+
});
|
|
1043
|
+
this._crdtManager = new CrdtManager({
|
|
1044
|
+
initialStorage
|
|
1045
|
+
});
|
|
1046
|
+
this._observeCrdt();
|
|
1047
|
+
}
|
|
1048
|
+
broadcast(event, data) {
|
|
1049
|
+
if (!this._events)
|
|
1050
|
+
return;
|
|
1051
|
+
const resolver = this._events[event];
|
|
1052
|
+
if (!resolver)
|
|
1053
|
+
return;
|
|
1054
|
+
const result = resolver(data);
|
|
1055
|
+
Object.keys(result).forEach((type) => {
|
|
1056
|
+
const _type = type.toString();
|
|
1057
|
+
const data2 = result[_type];
|
|
1058
|
+
if (!data2)
|
|
1059
|
+
return;
|
|
1060
|
+
this._eventNotifier.subject(_type).next(data2);
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
getConnection() {
|
|
1064
|
+
return Object.freeze(
|
|
1065
|
+
JSON.parse(JSON.stringify(this._state.connection))
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
_observeCrdt() {
|
|
1069
|
+
var _a, _b;
|
|
1070
|
+
(_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
|
|
1071
|
+
if (!this._crdtManager)
|
|
1072
|
+
return;
|
|
1073
|
+
const unsubscribe = this._crdtManager.doc.subscribe(
|
|
1074
|
+
(update, _origin) => {
|
|
1075
|
+
const origin = _origin != null ? _origin : null;
|
|
1076
|
+
if (!this._crdtManager)
|
|
1077
|
+
return;
|
|
1078
|
+
if (!this._state.webSocket)
|
|
1079
|
+
return;
|
|
1080
|
+
if (!this._state.connection.id)
|
|
1081
|
+
return;
|
|
1082
|
+
if (this._state.webSocket.readyState !== WebSocket.OPEN)
|
|
1083
|
+
return;
|
|
1084
|
+
if (origin === "$STORAGE_UPDATED")
|
|
1085
|
+
return;
|
|
1086
|
+
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
1087
|
+
Object.keys(sharedTypes).forEach((key) => {
|
|
1088
|
+
if (!this._crdtManager)
|
|
1089
|
+
return;
|
|
1090
|
+
const sharedType = sharedTypes[key];
|
|
1091
|
+
if (!sharedType)
|
|
1092
|
+
return;
|
|
1093
|
+
const serialized = sharedType.toJSON();
|
|
1094
|
+
this._crdtNotifier.subject(key).next(serialized);
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
);
|
|
1098
|
+
this._subscriptions.observeCrdt = unsubscribe;
|
|
1099
|
+
}
|
|
1100
|
+
};
|
|
1101
|
+
|
|
977
1102
|
// src/y.ts
|
|
978
1103
|
var y_exports = {};
|
|
979
1104
|
__export(y_exports, {
|
|
@@ -984,7 +1109,9 @@ __export(y_exports, {
|
|
|
984
1109
|
});
|
|
985
1110
|
import { array, map, text, unstable__object } from "@pluv/crdt-yjs";
|
|
986
1111
|
export {
|
|
1112
|
+
AbstractRoom,
|
|
987
1113
|
ConnectionState,
|
|
1114
|
+
MockedRoom,
|
|
988
1115
|
PluvClient,
|
|
989
1116
|
PluvRoom,
|
|
990
1117
|
createClient,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "@pluv/io frontend client",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"wonka": "^6.1.2",
|
|
21
|
-
"@pluv/crdt-yjs": "^0.
|
|
21
|
+
"@pluv/crdt-yjs": "^0.2.0",
|
|
22
22
|
"@pluv/types": "^0.1.2"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"typescript": "^4.9.4",
|
|
30
30
|
"yjs": "^13.5.44",
|
|
31
31
|
"@pluv/tsconfig": "^0.1.2",
|
|
32
|
-
"eslint-config-pluv": "^0.1.
|
|
32
|
+
"eslint-config-pluv": "^0.1.3"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { InferYjsSharedTypeJson } from "@pluv/crdt-yjs";
|
|
2
|
+
import type {
|
|
3
|
+
Id,
|
|
4
|
+
InferIOInput,
|
|
5
|
+
InferIOOutput,
|
|
6
|
+
IOLike,
|
|
7
|
+
JsonObject,
|
|
8
|
+
} from "@pluv/types";
|
|
9
|
+
import type { AbstractType } from "yjs";
|
|
10
|
+
import type { EventNotifierSubscriptionCallback } from "./EventNotifier";
|
|
11
|
+
import type { OtherNotifierSubscriptionCallback } from "./OtherNotifier";
|
|
12
|
+
import type {
|
|
13
|
+
StateNotifierSubjects,
|
|
14
|
+
SubscriptionCallback,
|
|
15
|
+
} from "./StateNotifier";
|
|
16
|
+
import type { UserInfo, WebSocketConnection } from "./types";
|
|
17
|
+
|
|
18
|
+
export abstract class AbstractRoom<
|
|
19
|
+
TIO extends IOLike,
|
|
20
|
+
TPresence extends JsonObject = {},
|
|
21
|
+
TStorage extends Record<string, AbstractType<any>> = {}
|
|
22
|
+
> {
|
|
23
|
+
public readonly id: string;
|
|
24
|
+
|
|
25
|
+
constructor(room: string) {
|
|
26
|
+
this.id = room;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public abstract broadcast<TEvent extends keyof InferIOInput<TIO>>(
|
|
30
|
+
event: TEvent,
|
|
31
|
+
data: Id<InferIOInput<TIO>[TEvent]>
|
|
32
|
+
): void;
|
|
33
|
+
|
|
34
|
+
public abstract event<TEvent extends keyof InferIOOutput<TIO>>(
|
|
35
|
+
event: TEvent,
|
|
36
|
+
callback: EventNotifierSubscriptionCallback<TIO, TEvent>
|
|
37
|
+
): () => void;
|
|
38
|
+
|
|
39
|
+
public abstract getConnection(): WebSocketConnection;
|
|
40
|
+
|
|
41
|
+
public abstract getMyPresence(): TPresence | null;
|
|
42
|
+
|
|
43
|
+
public abstract getMyself(): Id<UserInfo<TIO, TPresence>> | null;
|
|
44
|
+
|
|
45
|
+
public abstract getOther(
|
|
46
|
+
connectionId: string
|
|
47
|
+
): Id<UserInfo<TIO, TPresence>> | null;
|
|
48
|
+
|
|
49
|
+
public abstract getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
|
|
50
|
+
|
|
51
|
+
public abstract getStorage<TKey extends keyof TStorage>(
|
|
52
|
+
key: TKey
|
|
53
|
+
): TStorage[TKey] | null;
|
|
54
|
+
|
|
55
|
+
public abstract other(
|
|
56
|
+
connectionId: string,
|
|
57
|
+
callback: OtherNotifierSubscriptionCallback<TIO>
|
|
58
|
+
): () => void;
|
|
59
|
+
|
|
60
|
+
public abstract storage<TKey extends keyof TStorage>(
|
|
61
|
+
key: TKey,
|
|
62
|
+
fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void
|
|
63
|
+
): () => void;
|
|
64
|
+
|
|
65
|
+
public abstract subscribe<
|
|
66
|
+
TSubject extends keyof StateNotifierSubjects<TIO, TPresence>
|
|
67
|
+
>(
|
|
68
|
+
name: TSubject,
|
|
69
|
+
callback: SubscriptionCallback<TIO, TPresence, TSubject>
|
|
70
|
+
): () => void;
|
|
71
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import type { InferYjsSharedTypeJson } from "@pluv/crdt-yjs";
|
|
2
|
+
import type {
|
|
3
|
+
Id,
|
|
4
|
+
InferIOInput,
|
|
5
|
+
InferIOOutput,
|
|
6
|
+
IOLike,
|
|
7
|
+
JsonObject,
|
|
8
|
+
} from "@pluv/types";
|
|
9
|
+
import type { AbstractType } from "yjs";
|
|
10
|
+
import { AbstractRoom } from "./AbstractRoom";
|
|
11
|
+
import { CrdtManager, CrdtManagerOptions } from "./CrdtManager";
|
|
12
|
+
import { CrdtNotifier } from "./CrdtNotifier";
|
|
13
|
+
import type { EventNotifierSubscriptionCallback } from "./EventNotifier";
|
|
14
|
+
import { EventNotifier } from "./EventNotifier";
|
|
15
|
+
import type { OtherNotifierSubscriptionCallback } from "./OtherNotifier";
|
|
16
|
+
import { OtherNotifier } from "./OtherNotifier";
|
|
17
|
+
import type {
|
|
18
|
+
StateNotifierSubjects,
|
|
19
|
+
SubscriptionCallback,
|
|
20
|
+
} from "./StateNotifier";
|
|
21
|
+
import { StateNotifier } from "./StateNotifier";
|
|
22
|
+
import type {
|
|
23
|
+
InternalSubscriptions,
|
|
24
|
+
UserInfo,
|
|
25
|
+
WebSocketConnection,
|
|
26
|
+
WebSocketState,
|
|
27
|
+
} from "./types";
|
|
28
|
+
import { ConnectionState } from "./types";
|
|
29
|
+
import type { UsersManagerConfig } from "./UsersManager";
|
|
30
|
+
import { UsersManager } from "./UsersManager";
|
|
31
|
+
|
|
32
|
+
export type MockedRoomEvents<TIO extends IOLike> = {
|
|
33
|
+
[P in keyof InferIOInput<TIO>]: (
|
|
34
|
+
data: Id<InferIOInput<TIO>[P]>
|
|
35
|
+
) => Partial<InferIOOutput<TIO>>;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type MockedRoomConfig<
|
|
39
|
+
TIO extends IOLike,
|
|
40
|
+
TPresence extends JsonObject = {},
|
|
41
|
+
TStorage extends Record<string, AbstractType<any>> = {}
|
|
42
|
+
> = { events?: MockedRoomEvents<TIO> } & Omit<
|
|
43
|
+
CrdtManagerOptions<TStorage>,
|
|
44
|
+
"encodedState"
|
|
45
|
+
> &
|
|
46
|
+
UsersManagerConfig<TPresence>;
|
|
47
|
+
|
|
48
|
+
export class MockedRoom<
|
|
49
|
+
TIO extends IOLike,
|
|
50
|
+
TPresence extends JsonObject = {},
|
|
51
|
+
TStorage extends Record<string, AbstractType<any>> = {}
|
|
52
|
+
> extends AbstractRoom<TIO, TPresence, TStorage> {
|
|
53
|
+
private _crdtManager: CrdtManager<TStorage> | null = null;
|
|
54
|
+
private _crdtNotifier = new CrdtNotifier<TStorage>();
|
|
55
|
+
private _eventNotifier = new EventNotifier<TIO>();
|
|
56
|
+
private _events?: MockedRoomEvents<TIO>;
|
|
57
|
+
private _otherNotifier = new OtherNotifier<TIO>();
|
|
58
|
+
private _state: WebSocketState<TIO> = {
|
|
59
|
+
authorization: {
|
|
60
|
+
token: null,
|
|
61
|
+
user: null,
|
|
62
|
+
},
|
|
63
|
+
connection: {
|
|
64
|
+
id: null,
|
|
65
|
+
state: ConnectionState.Untouched,
|
|
66
|
+
},
|
|
67
|
+
webSocket: null,
|
|
68
|
+
};
|
|
69
|
+
private _stateNotifier = new StateNotifier<TIO, TPresence>();
|
|
70
|
+
private _subscriptions: InternalSubscriptions = {
|
|
71
|
+
observeCrdt: null,
|
|
72
|
+
};
|
|
73
|
+
private _usersManager: UsersManager<TIO, TPresence>;
|
|
74
|
+
|
|
75
|
+
constructor(
|
|
76
|
+
room: string,
|
|
77
|
+
options: MockedRoomConfig<TIO, TPresence, TStorage>
|
|
78
|
+
) {
|
|
79
|
+
const { events, initialPresence, initialStorage, presence } = options;
|
|
80
|
+
|
|
81
|
+
super(room);
|
|
82
|
+
|
|
83
|
+
this._events = events;
|
|
84
|
+
|
|
85
|
+
this._usersManager = new UsersManager<TIO, TPresence>({
|
|
86
|
+
initialPresence,
|
|
87
|
+
presence,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
this._crdtManager = new CrdtManager<TStorage>({
|
|
91
|
+
initialStorage,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
this._observeCrdt();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public broadcast<TEvent extends keyof InferIOInput<TIO>>(
|
|
98
|
+
event: TEvent,
|
|
99
|
+
data: Id<InferIOInput<TIO>[TEvent]>
|
|
100
|
+
): void {
|
|
101
|
+
if (!this._events) return;
|
|
102
|
+
|
|
103
|
+
const resolver = this._events[event];
|
|
104
|
+
|
|
105
|
+
if (!resolver) return;
|
|
106
|
+
|
|
107
|
+
const result = resolver(data);
|
|
108
|
+
|
|
109
|
+
Object.keys(result).forEach((type) => {
|
|
110
|
+
const _type = type.toString() as keyof Partial<InferIOOutput<TIO>>;
|
|
111
|
+
const data = result[_type] as any;
|
|
112
|
+
|
|
113
|
+
if (!data) return;
|
|
114
|
+
|
|
115
|
+
this._eventNotifier.subject(_type).next(data);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public event = <TEvent extends keyof InferIOOutput<TIO>>(
|
|
120
|
+
event: TEvent,
|
|
121
|
+
callback: EventNotifierSubscriptionCallback<TIO, TEvent>
|
|
122
|
+
): (() => void) => {
|
|
123
|
+
return this._eventNotifier.subscribe(event, callback);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
public getConnection(): WebSocketConnection {
|
|
127
|
+
// Create a read-only clone of the connection state
|
|
128
|
+
return Object.freeze(
|
|
129
|
+
JSON.parse(JSON.stringify(this._state.connection))
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public getMyPresence = (): TPresence | null => {
|
|
134
|
+
return this._usersManager.myself?.presence ?? null;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
public getMyself = (): Id<UserInfo<TIO, TPresence>> | null => {
|
|
138
|
+
return this._usersManager.myself;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
public getOther = (
|
|
142
|
+
connectionId: string
|
|
143
|
+
): Id<UserInfo<TIO, TPresence>> | null => {
|
|
144
|
+
return this._usersManager.getOther(connectionId);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
public getOthers = (): readonly Id<UserInfo<TIO, TPresence>>[] => {
|
|
148
|
+
return this._usersManager.getOthers();
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
public getStorage = <TKey extends keyof TStorage>(
|
|
152
|
+
key: TKey
|
|
153
|
+
): TStorage[TKey] | null => {
|
|
154
|
+
return this._crdtManager?.get(key) ?? null;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
public other = (
|
|
158
|
+
connectionId: string,
|
|
159
|
+
callback: OtherNotifierSubscriptionCallback<TIO>
|
|
160
|
+
): (() => void) => {
|
|
161
|
+
return this._otherNotifier.subscribe(connectionId, callback);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
public storage = <TKey extends keyof TStorage>(
|
|
165
|
+
key: TKey,
|
|
166
|
+
fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void
|
|
167
|
+
): (() => void) => {
|
|
168
|
+
return this._crdtNotifier.subscribe(key, fn);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
public subscribe = <
|
|
172
|
+
TSubject extends keyof StateNotifierSubjects<TIO, TPresence>
|
|
173
|
+
>(
|
|
174
|
+
name: TSubject,
|
|
175
|
+
callback: SubscriptionCallback<TIO, TPresence, TSubject>
|
|
176
|
+
): (() => void) => {
|
|
177
|
+
return this._stateNotifier.subscribe(name, callback);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
private _observeCrdt(): void {
|
|
181
|
+
this._subscriptions.observeCrdt?.();
|
|
182
|
+
|
|
183
|
+
if (!this._crdtManager) return;
|
|
184
|
+
|
|
185
|
+
const unsubscribe = this._crdtManager.doc.subscribe(
|
|
186
|
+
(update, _origin) => {
|
|
187
|
+
const origin = _origin ?? null;
|
|
188
|
+
|
|
189
|
+
if (!this._crdtManager) return;
|
|
190
|
+
if (!this._state.webSocket) return;
|
|
191
|
+
if (!this._state.connection.id) return;
|
|
192
|
+
if (this._state.webSocket.readyState !== WebSocket.OPEN) return;
|
|
193
|
+
|
|
194
|
+
if (origin === "$STORAGE_UPDATED") return;
|
|
195
|
+
|
|
196
|
+
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
197
|
+
|
|
198
|
+
Object.keys(sharedTypes).forEach((key) => {
|
|
199
|
+
if (!this._crdtManager) return;
|
|
200
|
+
|
|
201
|
+
const sharedType = sharedTypes[key];
|
|
202
|
+
|
|
203
|
+
if (!sharedType) return;
|
|
204
|
+
|
|
205
|
+
const serialized = sharedType.toJSON();
|
|
206
|
+
|
|
207
|
+
this._crdtNotifier.subject(key).next(serialized);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
this._subscriptions.observeCrdt = unsubscribe;
|
|
213
|
+
}
|
|
214
|
+
}
|
package/src/PluvRoom.ts
CHANGED
|
@@ -3,7 +3,6 @@ import type {
|
|
|
3
3
|
BaseIOEventRecord,
|
|
4
4
|
EventMessage,
|
|
5
5
|
Id,
|
|
6
|
-
InferEventMessage,
|
|
7
6
|
InferIOAuthorize,
|
|
8
7
|
InferIOAuthorizeRequired,
|
|
9
8
|
InferIOAuthorizeUser,
|
|
@@ -14,6 +13,7 @@ import type {
|
|
|
14
13
|
JsonObject,
|
|
15
14
|
} from "@pluv/types";
|
|
16
15
|
import type { AbstractType } from "yjs";
|
|
16
|
+
import { AbstractRoom } from "./AbstractRoom";
|
|
17
17
|
import type { CrdtManagerOptions } from "./CrdtManager";
|
|
18
18
|
import { CrdtManager } from "./CrdtManager";
|
|
19
19
|
import { CrdtNotifier } from "./CrdtNotifier";
|
|
@@ -28,6 +28,7 @@ import type {
|
|
|
28
28
|
import { StateNotifier } from "./StateNotifier";
|
|
29
29
|
import type {
|
|
30
30
|
AuthorizationState,
|
|
31
|
+
InternalSubscriptions,
|
|
31
32
|
UserInfo,
|
|
32
33
|
WebSocketConnection,
|
|
33
34
|
WebSocketState,
|
|
@@ -89,10 +90,6 @@ interface InternalListeners {
|
|
|
89
90
|
onAuthorizationFail: (error: Error) => void;
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
interface InternalSubscriptions {
|
|
93
|
-
observeCrdt: (() => void) | null;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
93
|
export type PluvRoomDebug<TIO extends IOLike> = Id<{
|
|
97
94
|
output: readonly (keyof InferIOOutput<TIO>)[];
|
|
98
95
|
input: readonly (keyof InferIOInput<TIO>)[];
|
|
@@ -118,11 +115,9 @@ export class PluvRoom<
|
|
|
118
115
|
TIO extends IOLike,
|
|
119
116
|
TPresence extends JsonObject = {},
|
|
120
117
|
TStorage extends Record<string, AbstractType<any>> = {}
|
|
121
|
-
> {
|
|
118
|
+
> extends AbstractRoom<TIO, TPresence, TStorage> {
|
|
122
119
|
readonly _endpoints: RoomEndpoints<TIO>;
|
|
123
120
|
|
|
124
|
-
public readonly id: string;
|
|
125
|
-
|
|
126
121
|
private _crdtManager: CrdtManager<TStorage> | null = null;
|
|
127
122
|
private _crdtNotifier = new CrdtNotifier<TStorage>();
|
|
128
123
|
private _debug: boolean | PluvRoomDebug<TIO>;
|
|
@@ -167,7 +162,8 @@ export class PluvRoom<
|
|
|
167
162
|
wsEndpoint,
|
|
168
163
|
} = options;
|
|
169
164
|
|
|
170
|
-
|
|
165
|
+
super(room);
|
|
166
|
+
|
|
171
167
|
this._debug = debug;
|
|
172
168
|
|
|
173
169
|
this._endpoints = {
|
|
@@ -201,11 +197,16 @@ export class PluvRoom<
|
|
|
201
197
|
return this._state.webSocket;
|
|
202
198
|
}
|
|
203
199
|
|
|
204
|
-
public broadcast
|
|
200
|
+
public broadcast<TEvent extends keyof InferIOInput<TIO>>(
|
|
201
|
+
event: TEvent,
|
|
202
|
+
data: Id<InferIOInput<TIO>[TEvent]>
|
|
203
|
+
): void {
|
|
205
204
|
if (!this._state.webSocket) return;
|
|
206
205
|
if (this._state.connection.state !== ConnectionState.Open) return;
|
|
207
206
|
|
|
208
|
-
|
|
207
|
+
const type = event.toString();
|
|
208
|
+
|
|
209
|
+
this._sendMessage({ data, type });
|
|
209
210
|
}
|
|
210
211
|
|
|
211
212
|
public async connect(): Promise<void> {
|
|
@@ -291,10 +292,10 @@ export class PluvRoom<
|
|
|
291
292
|
}
|
|
292
293
|
|
|
293
294
|
public event = <TEvent extends keyof InferIOOutput<TIO>>(
|
|
294
|
-
|
|
295
|
+
event: TEvent,
|
|
295
296
|
callback: EventNotifierSubscriptionCallback<TIO, TEvent>
|
|
296
297
|
): (() => void) => {
|
|
297
|
-
return this._eventNotifier.subscribe(
|
|
298
|
+
return this._eventNotifier.subscribe(event, callback);
|
|
298
299
|
};
|
|
299
300
|
|
|
300
301
|
public getConnection(): WebSocketConnection {
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,10 @@ export type {
|
|
|
6
6
|
IOEventMessage,
|
|
7
7
|
} from "@pluv/types";
|
|
8
8
|
export type { AbstractType } from "yjs";
|
|
9
|
+
export { AbstractRoom } from "./AbstractRoom";
|
|
9
10
|
export { createClient } from "./createClient";
|
|
11
|
+
export type { MockedRoomConfig, MockedRoomEvents } from "./MockedRoom";
|
|
12
|
+
export { MockedRoom } from "./MockedRoom";
|
|
10
13
|
export type { PluvClientOptions } from "./PluvClient";
|
|
11
14
|
export { PluvClient } from "./PluvClient";
|
|
12
15
|
export type { PluvRoomDebug } from "./PluvRoom";
|
package/src/types.ts
CHANGED