@pluv/client 0.18.0 → 0.20.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 +101 -0
- package/README.md +0 -92
- package/dist/index.d.mts +40 -33
- package/dist/index.d.ts +40 -33
- package/dist/index.js +118 -185
- package/dist/index.mjs +118 -185
- package/package.json +6 -6
- package/src/AbstractRoom.ts +10 -7
- package/src/CrdtManager.ts +3 -3
- package/src/CrdtNotifier.ts +10 -10
- package/src/MockedRoom.ts +35 -17
- package/src/OtherNotifier.ts +2 -6
- package/src/PluvClient.ts +5 -10
- package/src/PluvRoom.ts +50 -31
- package/src/index.ts +1 -1
- package/src/types.ts +10 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "@pluv/io frontend client",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"wonka": "^6.3.4",
|
|
21
|
-
"@pluv/crdt": "^0.
|
|
22
|
-
"@pluv/types": "^0.
|
|
21
|
+
"@pluv/crdt": "^0.20.0",
|
|
22
|
+
"@pluv/types": "^0.20.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"eslint": "^8.57.0",
|
|
26
|
-
"tsup": "^8.0
|
|
26
|
+
"tsup": "^8.1.0",
|
|
27
27
|
"typescript": "^5.4.5",
|
|
28
|
-
"@pluv/tsconfig": "^0.
|
|
29
|
-
"eslint-config-pluv": "^0.
|
|
28
|
+
"@pluv/tsconfig": "^0.20.0",
|
|
29
|
+
"eslint-config-pluv": "^0.20.0"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
package/src/AbstractRoom.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import type { AbstractCrdtDoc,
|
|
1
|
+
import type { AbstractCrdtDoc, CrdtType, InferCrdtJson } from "@pluv/crdt";
|
|
2
2
|
import type { IOLike, Id, InferIOInput, InferIOOutput, JsonObject } from "@pluv/types";
|
|
3
3
|
import type { EventNotifierSubscriptionCallback } from "./EventNotifier";
|
|
4
4
|
import type { OtherNotifierSubscriptionCallback } from "./OtherNotifier";
|
|
5
5
|
import type { StateNotifierSubjects, SubscriptionCallback } from "./StateNotifier";
|
|
6
|
-
import type { UserInfo, WebSocketConnection } from "./types";
|
|
6
|
+
import type { UpdateMyPresenceAction, UserInfo, WebSocketConnection } from "./types";
|
|
7
7
|
|
|
8
8
|
export abstract class AbstractRoom<
|
|
9
9
|
TIO extends IOLike,
|
|
10
10
|
TPresence extends JsonObject = {},
|
|
11
|
-
TStorage extends Record<string,
|
|
11
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
12
12
|
> {
|
|
13
13
|
public readonly id: string;
|
|
14
14
|
|
|
@@ -44,7 +44,10 @@ export abstract class AbstractRoom<
|
|
|
44
44
|
|
|
45
45
|
public abstract getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
|
|
46
46
|
|
|
47
|
-
public abstract getStorage<TKey extends keyof TStorage>(
|
|
47
|
+
public abstract getStorage<TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null;
|
|
48
|
+
|
|
49
|
+
public abstract getStorageJson(): InferCrdtJson<TStorage> | null;
|
|
50
|
+
public abstract getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
|
|
48
51
|
|
|
49
52
|
public abstract other(connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): () => void;
|
|
50
53
|
|
|
@@ -52,12 +55,12 @@ export abstract class AbstractRoom<
|
|
|
52
55
|
|
|
53
56
|
public abstract storage<TKey extends keyof TStorage>(
|
|
54
57
|
key: TKey,
|
|
55
|
-
fn: (value:
|
|
58
|
+
fn: (value: InferCrdtJson<TStorage[TKey]>) => void,
|
|
56
59
|
): () => void;
|
|
57
60
|
|
|
58
61
|
public abstract storageRoot(
|
|
59
62
|
fn: (value: {
|
|
60
|
-
[P in keyof TStorage]:
|
|
63
|
+
[P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
|
|
61
64
|
}) => void,
|
|
62
65
|
): () => void;
|
|
63
66
|
|
|
@@ -70,5 +73,5 @@ export abstract class AbstractRoom<
|
|
|
70
73
|
|
|
71
74
|
public abstract undo(): void;
|
|
72
75
|
|
|
73
|
-
public abstract updateMyPresence(presence:
|
|
76
|
+
public abstract updateMyPresence(presence: UpdateMyPresenceAction<TPresence>): void;
|
|
74
77
|
}
|
package/src/CrdtManager.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { AbstractCrdtDoc, AbstractCrdtDocFactory,
|
|
1
|
+
import type { AbstractCrdtDoc, AbstractCrdtDocFactory, CrdtType } from "@pluv/crdt";
|
|
2
2
|
import { noop } from "@pluv/crdt";
|
|
3
3
|
|
|
4
|
-
export type CrdtManagerOptions<TStorage extends Record<string,
|
|
4
|
+
export type CrdtManagerOptions<TStorage extends Record<string, CrdtType<any, any>> = {}> = {
|
|
5
5
|
encodedState?: string | Uint8Array | null;
|
|
6
6
|
initialStorage?: AbstractCrdtDocFactory<TStorage>;
|
|
7
7
|
};
|
|
@@ -12,7 +12,7 @@ type CrdtManagerInitializeParams = {
|
|
|
12
12
|
update: string | readonly string[];
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
export class CrdtManager<TStorage extends Record<string,
|
|
15
|
+
export class CrdtManager<TStorage extends Record<string, CrdtType<any, any>>> {
|
|
16
16
|
public doc: AbstractCrdtDoc<TStorage>;
|
|
17
17
|
public initialized: boolean = false;
|
|
18
18
|
|
package/src/CrdtNotifier.ts
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CrdtType, InferCrdtJson } from "@pluv/crdt";
|
|
2
2
|
import type { Subject } from "wonka";
|
|
3
3
|
import { makeSubject, subscribe } from "wonka";
|
|
4
4
|
|
|
5
|
-
export class CrdtNotifier<TStorage extends Record<string,
|
|
5
|
+
export class CrdtNotifier<TStorage extends Record<string, CrdtType<any, any>>> {
|
|
6
6
|
public rootSubject = makeSubject<{
|
|
7
|
-
[P in keyof TStorage]:
|
|
7
|
+
[P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
|
|
8
8
|
}>();
|
|
9
9
|
|
|
10
|
-
public subjects = new Map<keyof TStorage, Subject<
|
|
10
|
+
public subjects = new Map<keyof TStorage, Subject<InferCrdtJson<TStorage[keyof TStorage]>> | null>();
|
|
11
11
|
|
|
12
|
-
public subject<TKey extends keyof TStorage>(key: TKey): Subject<
|
|
12
|
+
public subject<TKey extends keyof TStorage>(key: TKey): Subject<InferCrdtJson<TStorage[TKey]>> {
|
|
13
13
|
const subject = this.subjects.get(key);
|
|
14
14
|
|
|
15
15
|
if (subject) {
|
|
16
|
-
return subject as Subject<
|
|
16
|
+
return subject as Subject<InferCrdtJson<TStorage[TKey]>>;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const newSubject = makeSubject<
|
|
19
|
+
const newSubject = makeSubject<InferCrdtJson<TStorage[TKey]>>();
|
|
20
20
|
|
|
21
21
|
this.subjects.set(key, newSubject);
|
|
22
22
|
|
|
23
|
-
return newSubject as Subject<
|
|
23
|
+
return newSubject as Subject<InferCrdtJson<TStorage[TKey]>>;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
public subcribeRoot(
|
|
27
27
|
callback: (value: {
|
|
28
|
-
[P in keyof TStorage]:
|
|
28
|
+
[P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
|
|
29
29
|
}) => void,
|
|
30
30
|
): () => void {
|
|
31
31
|
return subscribe(callback)(this.rootSubject.source).unsubscribe;
|
|
@@ -33,7 +33,7 @@ export class CrdtNotifier<TStorage extends Record<string, AbstractCrdtType<any,
|
|
|
33
33
|
|
|
34
34
|
public subscribe<TKey extends keyof TStorage>(
|
|
35
35
|
key: TKey,
|
|
36
|
-
callback: (value:
|
|
36
|
+
callback: (value: InferCrdtJson<TStorage[TKey]>) => void,
|
|
37
37
|
): () => void {
|
|
38
38
|
const subject = this.subject(key);
|
|
39
39
|
|
package/src/MockedRoom.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AbstractCrdtDoc,
|
|
1
|
+
import type { AbstractCrdtDoc, CrdtType, InferCrdtJson } from "@pluv/crdt";
|
|
2
2
|
import type { IOLike, Id, InferIOInput, InferIOOutput, JsonObject } from "@pluv/types";
|
|
3
3
|
import { AbstractRoom } from "./AbstractRoom";
|
|
4
4
|
import type { CrdtManagerOptions } from "./CrdtManager";
|
|
@@ -12,7 +12,13 @@ import type { StateNotifierSubjects, SubscriptionCallback } from "./StateNotifie
|
|
|
12
12
|
import { StateNotifier } from "./StateNotifier";
|
|
13
13
|
import type { UsersManagerConfig } from "./UsersManager";
|
|
14
14
|
import { UsersManager } from "./UsersManager";
|
|
15
|
-
import type {
|
|
15
|
+
import type {
|
|
16
|
+
InternalSubscriptions,
|
|
17
|
+
UpdateMyPresenceAction,
|
|
18
|
+
UserInfo,
|
|
19
|
+
WebSocketConnection,
|
|
20
|
+
WebSocketState,
|
|
21
|
+
} from "./types";
|
|
16
22
|
import { ConnectionState } from "./types";
|
|
17
23
|
|
|
18
24
|
export type MockedRoomEvents<TIO extends IOLike> = Partial<{
|
|
@@ -22,14 +28,14 @@ export type MockedRoomEvents<TIO extends IOLike> = Partial<{
|
|
|
22
28
|
export type MockedRoomConfig<
|
|
23
29
|
TIO extends IOLike,
|
|
24
30
|
TPresence extends JsonObject = {},
|
|
25
|
-
TStorage extends Record<string,
|
|
31
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
26
32
|
> = { events?: MockedRoomEvents<TIO> } & Omit<CrdtManagerOptions<TStorage>, "encodedState"> &
|
|
27
33
|
UsersManagerConfig<TPresence>;
|
|
28
34
|
|
|
29
35
|
export class MockedRoom<
|
|
30
36
|
TIO extends IOLike,
|
|
31
37
|
TPresence extends JsonObject = {},
|
|
32
|
-
TStorage extends Record<string,
|
|
38
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
33
39
|
> extends AbstractRoom<TIO, TPresence, TStorage> {
|
|
34
40
|
private _crdtManager: CrdtManager<TStorage>;
|
|
35
41
|
private _crdtNotifier = new CrdtNotifier<TStorage>();
|
|
@@ -111,10 +117,10 @@ export class MockedRoom<
|
|
|
111
117
|
return this._eventNotifier.subscribe(event, callback);
|
|
112
118
|
};
|
|
113
119
|
|
|
114
|
-
public getConnection(): WebSocketConnection {
|
|
120
|
+
public getConnection = (): WebSocketConnection => {
|
|
115
121
|
// Create a read-only clone of the connection state
|
|
116
122
|
return Object.freeze(JSON.parse(JSON.stringify(this._state.connection)));
|
|
117
|
-
}
|
|
123
|
+
};
|
|
118
124
|
|
|
119
125
|
public getDoc(): AbstractCrdtDoc<TStorage> {
|
|
120
126
|
return this._crdtManager.doc;
|
|
@@ -136,14 +142,24 @@ export class MockedRoom<
|
|
|
136
142
|
return this._usersManager.getOthers();
|
|
137
143
|
};
|
|
138
144
|
|
|
139
|
-
public getStorage = <TKey extends keyof TStorage>(
|
|
140
|
-
const sharedType = this._crdtManager.get(
|
|
145
|
+
public getStorage = <TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null => {
|
|
146
|
+
const sharedType = this._crdtManager.get(type);
|
|
141
147
|
|
|
142
148
|
if (typeof sharedType === "undefined") return null;
|
|
143
149
|
|
|
144
|
-
return sharedType
|
|
150
|
+
return sharedType;
|
|
145
151
|
};
|
|
146
152
|
|
|
153
|
+
public getStorageJson(): InferCrdtJson<TStorage> | null;
|
|
154
|
+
public getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
|
|
155
|
+
public getStorageJson<TKey extends keyof TStorage>(type?: TKey) {
|
|
156
|
+
if (this._state.connection.id === null) return null;
|
|
157
|
+
|
|
158
|
+
if (typeof type === "undefined") return this._crdtManager.doc.toJson();
|
|
159
|
+
|
|
160
|
+
return this._crdtManager.doc.toJson(type);
|
|
161
|
+
}
|
|
162
|
+
|
|
147
163
|
public other = (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): (() => void) => {
|
|
148
164
|
return this._otherNotifier.subscribe(connectionId, callback);
|
|
149
165
|
};
|
|
@@ -154,14 +170,14 @@ export class MockedRoom<
|
|
|
154
170
|
|
|
155
171
|
public storage = <TKey extends keyof TStorage>(
|
|
156
172
|
key: TKey,
|
|
157
|
-
fn: (value:
|
|
173
|
+
fn: (value: InferCrdtJson<TStorage[TKey]>) => void,
|
|
158
174
|
): (() => void) => {
|
|
159
175
|
return this._crdtNotifier.subscribe(key, fn);
|
|
160
176
|
};
|
|
161
177
|
|
|
162
178
|
public storageRoot = (
|
|
163
179
|
fn: (value: {
|
|
164
|
-
[P in keyof TStorage]:
|
|
180
|
+
[P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
|
|
165
181
|
}) => void,
|
|
166
182
|
): (() => void) => {
|
|
167
183
|
return this._crdtNotifier.subcribeRoot(fn);
|
|
@@ -199,8 +215,10 @@ export class MockedRoom<
|
|
|
199
215
|
this._crdtManager.doc.undo();
|
|
200
216
|
};
|
|
201
217
|
|
|
202
|
-
public updateMyPresence = (presence:
|
|
203
|
-
this.
|
|
218
|
+
public updateMyPresence = (presence: UpdateMyPresenceAction<TPresence>): void => {
|
|
219
|
+
const newPresence = typeof presence === "function" ? presence(this.getMyPresence()) : presence;
|
|
220
|
+
|
|
221
|
+
this._usersManager.updateMyPresence(newPresence);
|
|
204
222
|
|
|
205
223
|
const myPresence = this._usersManager.myPresence;
|
|
206
224
|
const myself = this._usersManager.myself ?? null;
|
|
@@ -222,18 +240,18 @@ export class MockedRoom<
|
|
|
222
240
|
|
|
223
241
|
const sharedTypes = this._crdtManager.doc.get();
|
|
224
242
|
|
|
225
|
-
const storageRoot = Object.
|
|
226
|
-
(acc,
|
|
243
|
+
const storageRoot = Object.keys(sharedTypes).reduce(
|
|
244
|
+
(acc, prop) => {
|
|
227
245
|
if (!this._crdtManager) return acc;
|
|
228
246
|
|
|
229
|
-
const serialized =
|
|
247
|
+
const serialized = this._crdtManager.doc.toJson(prop);
|
|
230
248
|
|
|
231
249
|
this._crdtNotifier.subject(prop).next(serialized);
|
|
232
250
|
|
|
233
251
|
return { ...acc, [prop]: serialized };
|
|
234
252
|
},
|
|
235
253
|
{} as {
|
|
236
|
-
[P in keyof TStorage]:
|
|
254
|
+
[P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
|
|
237
255
|
},
|
|
238
256
|
);
|
|
239
257
|
|
package/src/OtherNotifier.ts
CHANGED
|
@@ -3,16 +3,12 @@ import type { Subject } from "wonka";
|
|
|
3
3
|
import { makeSubject, subscribe } from "wonka";
|
|
4
4
|
import type { UserInfo } from "./types";
|
|
5
5
|
|
|
6
|
-
export type OtherNotifierSubscriptionCallback<TIO extends IOLike> = (
|
|
7
|
-
value: Id<UserInfo<TIO>> | null,
|
|
8
|
-
) => void;
|
|
6
|
+
export type OtherNotifierSubscriptionCallback<TIO extends IOLike> = (value: Id<UserInfo<TIO>> | null) => void;
|
|
9
7
|
|
|
10
8
|
export class OtherNotifier<TIO extends IOLike> {
|
|
11
9
|
public subjects = new Map<string, Subject<Id<UserInfo<TIO>> | null>>();
|
|
12
10
|
|
|
13
|
-
public subject<TEvent extends string>(
|
|
14
|
-
name: TEvent,
|
|
15
|
-
): Subject<Id<UserInfo<TIO>> | null> {
|
|
11
|
+
public subject<TEvent extends string>(name: TEvent): Subject<Id<UserInfo<TIO>> | null> {
|
|
16
12
|
const subject = this.subjects.get(name);
|
|
17
13
|
|
|
18
14
|
if (subject) return subject;
|
package/src/PluvClient.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CrdtType } from "@pluv/crdt";
|
|
2
2
|
import type { IOLike, JsonObject } from "@pluv/types";
|
|
3
|
-
import { AuthEndpoint,
|
|
3
|
+
import type { AuthEndpoint, PluvRoomOptions, RoomConfig, RoomEndpoints, WsEndpoint } from "./PluvRoom";
|
|
4
|
+
import { PluvRoom } from "./PluvRoom";
|
|
4
5
|
|
|
5
6
|
export type PluvClientOptions<TIO extends IOLike> = RoomEndpoints<TIO> & {
|
|
6
7
|
debug?: boolean;
|
|
@@ -21,10 +22,7 @@ export class PluvClient<TIO extends IOLike = IOLike> {
|
|
|
21
22
|
this._wsEndpoint = wsEndpoint;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
public createRoom = <
|
|
25
|
-
TPresence extends JsonObject = {},
|
|
26
|
-
TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
|
|
27
|
-
>(
|
|
25
|
+
public createRoom = <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(
|
|
28
26
|
room: string,
|
|
29
27
|
options: PluvRoomOptions<TIO, TPresence, TStorage>,
|
|
30
28
|
): PluvRoom<TIO, TPresence, TStorage> => {
|
|
@@ -65,10 +63,7 @@ export class PluvClient<TIO extends IOLike = IOLike> {
|
|
|
65
63
|
return toEnter;
|
|
66
64
|
};
|
|
67
65
|
|
|
68
|
-
public getRoom = <
|
|
69
|
-
TPresence extends JsonObject = {},
|
|
70
|
-
TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
|
|
71
|
-
>(
|
|
66
|
+
public getRoom = <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(
|
|
72
67
|
room: string,
|
|
73
68
|
): PluvRoom<TIO, TPresence, TStorage> | null => {
|
|
74
69
|
const found = this._rooms.get(room) as PluvRoom<TIO, TPresence, TStorage> | undefined;
|
package/src/PluvRoom.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AbstractCrdtDoc,
|
|
1
|
+
import type { AbstractCrdtDoc, CrdtType, InferCrdtJson } from "@pluv/crdt";
|
|
2
2
|
import type {
|
|
3
3
|
BaseIOEventRecord,
|
|
4
4
|
EventMessage,
|
|
@@ -26,7 +26,14 @@ import { StateNotifier } from "./StateNotifier";
|
|
|
26
26
|
import { StorageStore } from "./StorageStore";
|
|
27
27
|
import type { UsersManagerConfig } from "./UsersManager";
|
|
28
28
|
import { UsersManager } from "./UsersManager";
|
|
29
|
-
import type {
|
|
29
|
+
import type {
|
|
30
|
+
AuthorizationState,
|
|
31
|
+
InternalSubscriptions,
|
|
32
|
+
UpdateMyPresenceAction,
|
|
33
|
+
UserInfo,
|
|
34
|
+
WebSocketConnection,
|
|
35
|
+
WebSocketState,
|
|
36
|
+
} from "./types";
|
|
30
37
|
import { ConnectionState } from "./types";
|
|
31
38
|
import { debounce } from "./utils";
|
|
32
39
|
|
|
@@ -40,7 +47,7 @@ const ORIGIN_STORAGE_UPDATED = "$STORAGE_UPDATED";
|
|
|
40
47
|
export const DEFAULT_PLUV_CLIENT_ADDON = <
|
|
41
48
|
TIO extends IOLike = IOLike,
|
|
42
49
|
TPresence extends JsonObject = {},
|
|
43
|
-
TStorage extends Record<string,
|
|
50
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
44
51
|
>(
|
|
45
52
|
input: PluvRoomAddonInput<TIO, TPresence, TStorage>,
|
|
46
53
|
): PluvRoomAddonResult => ({
|
|
@@ -94,13 +101,13 @@ interface InternalListeners {
|
|
|
94
101
|
export type PluvRoomAddon<
|
|
95
102
|
TIO extends IOLike = IOLike,
|
|
96
103
|
TPresence extends JsonObject = {},
|
|
97
|
-
TStorage extends Record<string,
|
|
104
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
98
105
|
> = (input: PluvRoomAddonInput<TIO, TPresence, TStorage>) => Partial<PluvRoomAddonResult>;
|
|
99
106
|
|
|
100
107
|
export interface PluvRoomAddonInput<
|
|
101
108
|
TIO extends IOLike = IOLike,
|
|
102
109
|
TPresence extends JsonObject = {},
|
|
103
|
-
TStorage extends Record<string,
|
|
110
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
104
111
|
> {
|
|
105
112
|
room: PluvRoom<TIO, TPresence, TStorage>;
|
|
106
113
|
}
|
|
@@ -117,7 +124,7 @@ export type PluvRoomDebug<TIO extends IOLike> = Id<{
|
|
|
117
124
|
export type PluvRoomOptions<
|
|
118
125
|
TIO extends IOLike,
|
|
119
126
|
TPresence extends JsonObject = {},
|
|
120
|
-
TStorage extends Record<string,
|
|
127
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
121
128
|
> = {
|
|
122
129
|
addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
|
|
123
130
|
debug?: boolean | PluvRoomDebug<TIO>;
|
|
@@ -128,13 +135,13 @@ export type PluvRoomOptions<
|
|
|
128
135
|
export type RoomConfig<
|
|
129
136
|
TIO extends IOLike,
|
|
130
137
|
TPresence extends JsonObject = {},
|
|
131
|
-
TStorage extends Record<string,
|
|
138
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
132
139
|
> = RoomEndpoints<TIO> & PluvRoomOptions<TIO, TPresence, TStorage>;
|
|
133
140
|
|
|
134
141
|
export class PluvRoom<
|
|
135
142
|
TIO extends IOLike,
|
|
136
143
|
TPresence extends JsonObject = {},
|
|
137
|
-
TStorage extends Record<string,
|
|
144
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
138
145
|
> extends AbstractRoom<TIO, TPresence, TStorage> {
|
|
139
146
|
readonly _endpoints: RoomEndpoints<TIO>;
|
|
140
147
|
|
|
@@ -347,14 +354,14 @@ export class PluvRoom<
|
|
|
347
354
|
[event in keyof InferIOOutput<TIO>]: (callback: EventNotifierSubscriptionCallback<TIO, event>) => () => void;
|
|
348
355
|
};
|
|
349
356
|
|
|
350
|
-
public getConnection(): WebSocketConnection {
|
|
357
|
+
public getConnection = (): WebSocketConnection => {
|
|
351
358
|
// Create a read-only clone of the connection state
|
|
352
359
|
return Object.freeze(JSON.parse(JSON.stringify(this._state.connection)));
|
|
353
|
-
}
|
|
360
|
+
};
|
|
354
361
|
|
|
355
|
-
public getDoc(): AbstractCrdtDoc<TStorage> {
|
|
362
|
+
public getDoc = (): AbstractCrdtDoc<TStorage> => {
|
|
356
363
|
return this._crdtManager.doc;
|
|
357
|
-
}
|
|
364
|
+
};
|
|
358
365
|
|
|
359
366
|
public getMyPresence = (): TPresence => {
|
|
360
367
|
return this._usersManager.myPresence;
|
|
@@ -372,14 +379,28 @@ export class PluvRoom<
|
|
|
372
379
|
return this._usersManager.getOthers();
|
|
373
380
|
};
|
|
374
381
|
|
|
375
|
-
public getStorage = <TKey extends keyof TStorage>(
|
|
376
|
-
const sharedType = this._crdtManager.get(
|
|
382
|
+
public getStorage = <TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null => {
|
|
383
|
+
const sharedType = this._crdtManager.get(type);
|
|
377
384
|
|
|
378
385
|
if (typeof sharedType === "undefined") return null;
|
|
379
386
|
|
|
380
387
|
return sharedType;
|
|
381
388
|
};
|
|
382
389
|
|
|
390
|
+
public getStorageJson(): InferCrdtJson<TStorage> | null;
|
|
391
|
+
public getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
|
|
392
|
+
public getStorageJson<TKey extends keyof TStorage>(type?: TKey) {
|
|
393
|
+
if (this._state.connection.id === null) return null;
|
|
394
|
+
|
|
395
|
+
if (typeof type === "undefined") return this._crdtManager.doc.toJson();
|
|
396
|
+
|
|
397
|
+
const sharedType = this._crdtManager.get(type);
|
|
398
|
+
|
|
399
|
+
if (typeof sharedType === "undefined") return null;
|
|
400
|
+
|
|
401
|
+
return this._crdtManager.doc.toJson(type);
|
|
402
|
+
}
|
|
403
|
+
|
|
383
404
|
public other = (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): (() => void) => {
|
|
384
405
|
return this._otherNotifier.subscribe(connectionId, callback);
|
|
385
406
|
};
|
|
@@ -390,14 +411,14 @@ export class PluvRoom<
|
|
|
390
411
|
|
|
391
412
|
public storage = <TKey extends keyof TStorage>(
|
|
392
413
|
key: TKey,
|
|
393
|
-
fn: (value:
|
|
414
|
+
fn: (value: InferCrdtJson<TStorage[TKey]>) => void,
|
|
394
415
|
): (() => void) => {
|
|
395
416
|
return this._crdtNotifier.subscribe(key, fn);
|
|
396
417
|
};
|
|
397
418
|
|
|
398
419
|
public storageRoot = (
|
|
399
420
|
fn: (value: {
|
|
400
|
-
[P in keyof TStorage]:
|
|
421
|
+
[P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
|
|
401
422
|
}) => void,
|
|
402
423
|
): (() => void) => {
|
|
403
424
|
return this._crdtNotifier.subcribeRoot(fn);
|
|
@@ -433,8 +454,10 @@ export class PluvRoom<
|
|
|
433
454
|
this._crdtManager.doc.undo();
|
|
434
455
|
};
|
|
435
456
|
|
|
436
|
-
public updateMyPresence = (presence:
|
|
437
|
-
this.
|
|
457
|
+
public updateMyPresence = (presence: UpdateMyPresenceAction<TPresence>): void => {
|
|
458
|
+
const newPresence = typeof presence === "function" ? presence(this.getMyPresence()) : presence;
|
|
459
|
+
|
|
460
|
+
this._usersManager.updateMyPresence(newPresence);
|
|
438
461
|
|
|
439
462
|
const myPresence = this._usersManager.myPresence;
|
|
440
463
|
const myself = this._usersManager.myself ?? null;
|
|
@@ -442,7 +465,7 @@ export class PluvRoom<
|
|
|
442
465
|
this._stateNotifier.subjects["my-presence"].next(myPresence);
|
|
443
466
|
!!myself && this._stateNotifier.subjects["myself"].next(myself);
|
|
444
467
|
|
|
445
|
-
this.broadcast("$UPDATE_PRESENCE" as keyof InferIOInput<TIO>, { presence } as any);
|
|
468
|
+
this.broadcast("$UPDATE_PRESENCE" as keyof InferIOInput<TIO>, { presence: newPresence } as any);
|
|
446
469
|
};
|
|
447
470
|
|
|
448
471
|
private async _addToStorageStore(update: string): Promise<void> {
|
|
@@ -553,17 +576,15 @@ export class PluvRoom<
|
|
|
553
576
|
private _emitSharedTypes(): void {
|
|
554
577
|
const sharedTypes = this._crdtManager.doc.get();
|
|
555
578
|
|
|
556
|
-
const storageRoot = Object.
|
|
557
|
-
(acc,
|
|
558
|
-
const serialized =
|
|
579
|
+
const storageRoot = Object.keys(sharedTypes).reduce(
|
|
580
|
+
(acc, prop) => {
|
|
581
|
+
const serialized = this._crdtManager.doc.toJson(prop);
|
|
559
582
|
|
|
560
583
|
this._crdtNotifier.subject(prop).next(serialized);
|
|
561
584
|
|
|
562
585
|
return { ...acc, [prop]: serialized };
|
|
563
586
|
},
|
|
564
|
-
{} as {
|
|
565
|
-
[P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
|
|
566
|
-
},
|
|
587
|
+
{} as { [P in keyof TStorage]: InferCrdtJson<TStorage[P]> },
|
|
567
588
|
);
|
|
568
589
|
|
|
569
590
|
this._crdtNotifier.rootSubject.next(storageRoot);
|
|
@@ -705,8 +726,6 @@ export class PluvRoom<
|
|
|
705
726
|
Object.keys(data.others).forEach((connectionId) => {
|
|
706
727
|
const { presence, user } = data.others[connectionId];
|
|
707
728
|
|
|
708
|
-
if (!user) return;
|
|
709
|
-
|
|
710
729
|
this._usersManager.setUser(connectionId, user);
|
|
711
730
|
|
|
712
731
|
const other = this._usersManager.getOther(connectionId);
|
|
@@ -805,18 +824,18 @@ export class PluvRoom<
|
|
|
805
824
|
|
|
806
825
|
const sharedTypes = this._crdtManager.doc.get();
|
|
807
826
|
|
|
808
|
-
const storageRoot = Object.
|
|
809
|
-
(acc,
|
|
827
|
+
const storageRoot = Object.keys(sharedTypes).reduce(
|
|
828
|
+
(acc, prop) => {
|
|
810
829
|
if (!this._crdtManager) return acc;
|
|
811
830
|
|
|
812
|
-
const serialized =
|
|
831
|
+
const serialized = this._crdtManager.doc.toJson(prop);
|
|
813
832
|
|
|
814
833
|
this._crdtNotifier.subject(prop).next(serialized);
|
|
815
834
|
|
|
816
835
|
return { ...acc, [prop]: serialized };
|
|
817
836
|
},
|
|
818
837
|
{} as {
|
|
819
|
-
[P in keyof TStorage]:
|
|
838
|
+
[P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
|
|
820
839
|
},
|
|
821
840
|
);
|
|
822
841
|
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type { CrdtType, InferCrdtJson } from "@pluv/crdt";
|
|
2
2
|
export type { BaseUser, EventMessage, EventRecord, IOEventMessage } from "@pluv/types";
|
|
3
3
|
export { AbstractRoom } from "./AbstractRoom";
|
|
4
4
|
export { AbstractStorageStore } from "./AbstractStorageStore";
|
package/src/types.ts
CHANGED
|
@@ -17,6 +17,16 @@ export interface InternalSubscriptions {
|
|
|
17
17
|
observeCrdt: (() => void) | null;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export type UpdateMyPresenceAction<TPresence extends JsonObject> =
|
|
21
|
+
| Partial<TPresence>
|
|
22
|
+
| ((oldPresence: TPresence | null) => Partial<TPresence>);
|
|
23
|
+
|
|
24
|
+
export interface UserInfo<TIO extends IOLike, TPresence extends JsonObject = {}> {
|
|
25
|
+
connectionId: string;
|
|
26
|
+
presence: TPresence;
|
|
27
|
+
user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;
|
|
28
|
+
}
|
|
29
|
+
|
|
20
30
|
export interface WebSocketConnection {
|
|
21
31
|
count: number;
|
|
22
32
|
id: string | null;
|
|
@@ -28,9 +38,3 @@ export interface WebSocketState<TIO extends IOLike> {
|
|
|
28
38
|
connection: WebSocketConnection;
|
|
29
39
|
webSocket: WebSocket | null;
|
|
30
40
|
}
|
|
31
|
-
|
|
32
|
-
export interface UserInfo<TIO extends IOLike, TPresence extends JsonObject = {}> {
|
|
33
|
-
connectionId: string;
|
|
34
|
-
presence: TPresence;
|
|
35
|
-
user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;
|
|
36
|
-
}
|