@pluv/client 0.0.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/.eslintrc.js +4 -0
- package/.turbo/turbo-build.log +17 -0
- package/dist/index.d.ts +193 -0
- package/dist/index.js +1001 -0
- package/dist/index.mjs +977 -0
- package/package.json +31 -0
- package/src/CrdtManager.ts +40 -0
- package/src/CrdtNotifier.ts +36 -0
- package/src/EventNotifier.ts +38 -0
- package/src/OtherNotifier.ts +35 -0
- package/src/PluvClient.ts +81 -0
- package/src/PluvRoom.ts +993 -0
- package/src/StateNotifier.ts +55 -0
- package/src/UsersManager.ts +128 -0
- package/src/createClient.ts +9 -0
- package/src/index.ts +16 -0
- package/src/types.ts +40 -0
- package/src/y.ts +1 -0
- package/tsconfig.json +5 -0
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pluv/client",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"module": "./dist/index.mjs",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@pluv/crdt-yjs": "^0.0.0",
|
|
12
|
+
"@pluv/types": "^0.0.0",
|
|
13
|
+
"wonka": "^6.1.2"
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"yjs": "^13.0.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@pluv/tsconfig": "^0.0.0",
|
|
20
|
+
"eslint-config-pluv": "^0.0.0",
|
|
21
|
+
"tsup": "^6.5.0",
|
|
22
|
+
"typescript": "^4.9.4",
|
|
23
|
+
"yjs": "^13.5.44"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
27
|
+
"dev": "tsup src/index.ts --format esm,cjs --watch --dts",
|
|
28
|
+
"lint": "eslint src/**/*.ts* --fix",
|
|
29
|
+
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { YjsDoc } from "@pluv/crdt-yjs";
|
|
2
|
+
import { doc } from "@pluv/crdt-yjs";
|
|
3
|
+
import type { AbstractType } from "yjs";
|
|
4
|
+
|
|
5
|
+
export interface CrdtManagerOptions<
|
|
6
|
+
TStorage extends Record<string, AbstractType<any>> = {}
|
|
7
|
+
> {
|
|
8
|
+
encodedState?: string | Uint8Array;
|
|
9
|
+
initialStorage?: () => TStorage;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
|
|
13
|
+
public doc: YjsDoc<TStorage>;
|
|
14
|
+
|
|
15
|
+
constructor(options: CrdtManagerOptions<TStorage> = {}) {
|
|
16
|
+
const { encodedState, initialStorage } = options;
|
|
17
|
+
|
|
18
|
+
const doc1 = encodedState
|
|
19
|
+
? doc<TStorage>().applyUpdate(encodedState)
|
|
20
|
+
: null;
|
|
21
|
+
|
|
22
|
+
if (doc1 && !!Object.keys(doc1.toJSON()).length) {
|
|
23
|
+
this.doc = doc1;
|
|
24
|
+
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
doc1?.destroy();
|
|
29
|
+
|
|
30
|
+
this.doc = doc<TStorage>(initialStorage?.());
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public destroy(): void {
|
|
34
|
+
this.doc.destroy();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public get<TKey extends keyof TStorage>(key: TKey): TStorage[TKey] {
|
|
38
|
+
return this.doc.get(key);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { InferYjsSharedTypeJson } from "@pluv/crdt-yjs";
|
|
2
|
+
import { makeSubject, Subject, subscribe } from "wonka";
|
|
3
|
+
import type { AbstractType } from "yjs";
|
|
4
|
+
|
|
5
|
+
export class CrdtNotifier<TStorage extends Record<string, AbstractType<any>>> {
|
|
6
|
+
public subjects = new Map<
|
|
7
|
+
keyof TStorage,
|
|
8
|
+
Subject<InferYjsSharedTypeJson<TStorage[keyof TStorage]>> | null
|
|
9
|
+
>();
|
|
10
|
+
|
|
11
|
+
public subject<TKey extends keyof TStorage>(
|
|
12
|
+
key: TKey
|
|
13
|
+
): Subject<InferYjsSharedTypeJson<TStorage[TKey]>> {
|
|
14
|
+
const subject = this.subjects.get(key);
|
|
15
|
+
|
|
16
|
+
if (subject) {
|
|
17
|
+
return subject as Subject<InferYjsSharedTypeJson<TStorage[TKey]>>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const newSubject =
|
|
21
|
+
makeSubject<InferYjsSharedTypeJson<TStorage[TKey]>>();
|
|
22
|
+
|
|
23
|
+
this.subjects.set(key, newSubject);
|
|
24
|
+
|
|
25
|
+
return newSubject as Subject<InferYjsSharedTypeJson<TStorage[TKey]>>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public subscribe<TKey extends keyof TStorage>(
|
|
29
|
+
key: TKey,
|
|
30
|
+
callback: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void
|
|
31
|
+
): () => void {
|
|
32
|
+
const subject = this.subject(key);
|
|
33
|
+
|
|
34
|
+
return subscribe(callback)(subject.source).unsubscribe;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Id, InferIOOutput, IOEventMessage, IOLike } from "@pluv/types";
|
|
2
|
+
import type { Subject } from "wonka";
|
|
3
|
+
import { makeSubject, subscribe } from "wonka";
|
|
4
|
+
|
|
5
|
+
export type EventNotifierSubscriptionCallback<
|
|
6
|
+
TIO extends IOLike,
|
|
7
|
+
TEvent extends keyof InferIOOutput<TIO>
|
|
8
|
+
> = (value: Id<IOEventMessage<TIO, TEvent>>) => void;
|
|
9
|
+
|
|
10
|
+
export class EventNotifier<TIO extends IOLike> {
|
|
11
|
+
public subjects = new Map<
|
|
12
|
+
keyof InferIOOutput<TIO>,
|
|
13
|
+
Subject<Id<IOEventMessage<TIO, keyof InferIOOutput<TIO>>>>
|
|
14
|
+
>();
|
|
15
|
+
|
|
16
|
+
public subject<TEvent extends keyof InferIOOutput<TIO>>(
|
|
17
|
+
name: TEvent
|
|
18
|
+
): Subject<Id<IOEventMessage<TIO, TEvent>>> {
|
|
19
|
+
const subject = this.subjects.get(name);
|
|
20
|
+
|
|
21
|
+
if (subject) return subject;
|
|
22
|
+
|
|
23
|
+
const newSubject = makeSubject<Id<IOEventMessage<TIO, TEvent>>>();
|
|
24
|
+
|
|
25
|
+
this.subjects.set(name, newSubject);
|
|
26
|
+
|
|
27
|
+
return newSubject;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public subscribe<TEvent extends keyof InferIOOutput<TIO>>(
|
|
31
|
+
name: TEvent,
|
|
32
|
+
callback: EventNotifierSubscriptionCallback<TIO, TEvent>
|
|
33
|
+
): () => void {
|
|
34
|
+
const subject = this.subject(name);
|
|
35
|
+
|
|
36
|
+
return subscribe(callback)(subject.source).unsubscribe;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Id, IOLike } from "@pluv/types";
|
|
2
|
+
import type { Subject } from "wonka";
|
|
3
|
+
import { makeSubject, subscribe } from "wonka";
|
|
4
|
+
import { UserInfo } from "./types";
|
|
5
|
+
|
|
6
|
+
export type OtherNotifierSubscriptionCallback<TIO extends IOLike> = (
|
|
7
|
+
value: Id<UserInfo<TIO>> | null
|
|
8
|
+
) => void;
|
|
9
|
+
|
|
10
|
+
export class OtherNotifier<TIO extends IOLike> {
|
|
11
|
+
public subjects = new Map<string, Subject<Id<UserInfo<TIO>> | null>>();
|
|
12
|
+
|
|
13
|
+
public subject<TEvent extends string>(
|
|
14
|
+
name: TEvent
|
|
15
|
+
): Subject<Id<UserInfo<TIO>> | null> {
|
|
16
|
+
const subject = this.subjects.get(name);
|
|
17
|
+
|
|
18
|
+
if (subject) return subject;
|
|
19
|
+
|
|
20
|
+
const newSubject = makeSubject<Id<UserInfo<TIO>>>();
|
|
21
|
+
|
|
22
|
+
this.subjects.set(name, newSubject);
|
|
23
|
+
|
|
24
|
+
return newSubject;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public subscribe<TEvent extends string>(
|
|
28
|
+
name: TEvent,
|
|
29
|
+
callback: OtherNotifierSubscriptionCallback<TIO>
|
|
30
|
+
): () => void {
|
|
31
|
+
const source = this.subject(name).source;
|
|
32
|
+
|
|
33
|
+
return subscribe(callback)(source).unsubscribe;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { IOLike, JsonObject } from "@pluv/types";
|
|
2
|
+
import { AbstractType } from "yjs";
|
|
3
|
+
import type {
|
|
4
|
+
AuthEndpoint,
|
|
5
|
+
RoomEndpoints,
|
|
6
|
+
PluvRoomOptions,
|
|
7
|
+
WsEndpoint,
|
|
8
|
+
} from "./PluvRoom";
|
|
9
|
+
import { PluvRoom } from "./PluvRoom";
|
|
10
|
+
|
|
11
|
+
export type PluvClientOptions<TIO extends IOLike> = RoomEndpoints<TIO>;
|
|
12
|
+
|
|
13
|
+
export class PluvClient<TIO extends IOLike = IOLike> {
|
|
14
|
+
private _authEndpoint: AuthEndpoint;
|
|
15
|
+
private _wsEndpoint: WsEndpoint;
|
|
16
|
+
|
|
17
|
+
private _rooms = new Map<string, PluvRoom<TIO, JsonObject, any>>();
|
|
18
|
+
|
|
19
|
+
constructor(options: PluvClientOptions<TIO>) {
|
|
20
|
+
this._authEndpoint = options.authEndpoint as AuthEndpoint;
|
|
21
|
+
this._wsEndpoint = options.wsEndpoint;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public createRoom = <
|
|
25
|
+
TPresence extends JsonObject = {},
|
|
26
|
+
TStorage extends Record<string, AbstractType<any>> = {}
|
|
27
|
+
>(
|
|
28
|
+
room: string,
|
|
29
|
+
options: PluvRoomOptions<TIO, TPresence, TStorage>
|
|
30
|
+
): PluvRoom<TIO, TPresence, TStorage> => {
|
|
31
|
+
const oldRoom = this.getRoom<TPresence, TStorage>(room);
|
|
32
|
+
|
|
33
|
+
if (oldRoom) return oldRoom;
|
|
34
|
+
|
|
35
|
+
const newRoom = new PluvRoom<TIO, TPresence, TStorage>(room, {
|
|
36
|
+
authEndpoint: this._authEndpoint,
|
|
37
|
+
debug: options.debug,
|
|
38
|
+
initialPresence: options.initialPresence,
|
|
39
|
+
initialStorage: options.initialStorage,
|
|
40
|
+
onAuthorizationFail: options.onAuthorizationFail,
|
|
41
|
+
wsEndpoint: this._wsEndpoint,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
this._rooms.set(room, newRoom);
|
|
45
|
+
|
|
46
|
+
return newRoom;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
public enter = async (room: string): Promise<void> => {
|
|
50
|
+
const toEnter = this.getRoom(room);
|
|
51
|
+
|
|
52
|
+
if (!toEnter) {
|
|
53
|
+
throw new Error(`Could not find room: ${room}.`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await toEnter.connect();
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
public getRoom = <
|
|
60
|
+
TPresence extends JsonObject = {},
|
|
61
|
+
TStorage extends Record<string, AbstractType<any>> = {}
|
|
62
|
+
>(
|
|
63
|
+
room: string
|
|
64
|
+
): PluvRoom<TIO, TPresence, TStorage> | null => {
|
|
65
|
+
const found = this._rooms.get(room) as
|
|
66
|
+
| PluvRoom<TIO, TPresence, TStorage>
|
|
67
|
+
| undefined;
|
|
68
|
+
|
|
69
|
+
return found ?? null;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
public leave = (room: string): void => {
|
|
73
|
+
const toLeave = this.getRoom(room);
|
|
74
|
+
|
|
75
|
+
if (!toLeave) return;
|
|
76
|
+
|
|
77
|
+
toLeave.disconnect();
|
|
78
|
+
|
|
79
|
+
this._rooms.delete(room);
|
|
80
|
+
};
|
|
81
|
+
}
|