@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.
@@ -0,0 +1,55 @@
1
+ import type { Id, IOLike, JsonObject } from "@pluv/types";
2
+ import type { Subject } from "wonka";
3
+ import { makeSubject, subscribe } from "wonka";
4
+ import type { UserInfo, WebSocketState } from "./types";
5
+
6
+ export interface StateNotifierSubjects<
7
+ TIO extends IOLike,
8
+ TPresence extends JsonObject
9
+ > {
10
+ connection: Subject<Id<WebSocketState<TIO>>>;
11
+ "my-presence": Subject<TPresence | null>;
12
+ myself: Subject<Readonly<Id<UserInfo<TIO>>> | null>;
13
+ others: Subject<readonly Id<UserInfo<TIO>>[]>;
14
+ }
15
+
16
+ type InferSubjectValue<
17
+ TIO extends IOLike,
18
+ TPresence extends JsonObject,
19
+ TSubject extends keyof StateNotifierSubjects<TIO, TPresence>
20
+ > = StateNotifierSubjects<TIO, TPresence>[TSubject] extends Subject<
21
+ infer IValue
22
+ >
23
+ ? Id<IValue>
24
+ : never;
25
+
26
+ export type SubscriptionCallback<
27
+ TIO extends IOLike,
28
+ TPresence extends JsonObject,
29
+ TSubject extends keyof StateNotifierSubjects<TIO, TPresence>
30
+ > = (value: InferSubjectValue<TIO, TPresence, TSubject>) => void;
31
+
32
+ export class StateNotifier<
33
+ TIO extends IOLike,
34
+ TPresence extends JsonObject = {}
35
+ > {
36
+ public subjects: StateNotifierSubjects<TIO, TPresence> = {
37
+ connection: makeSubject<Id<WebSocketState<TIO>>>(),
38
+ "my-presence": makeSubject<TPresence>(),
39
+ myself: makeSubject<Readonly<Id<UserInfo<TIO>>> | null>(),
40
+ others: makeSubject<readonly Id<UserInfo<TIO>>[]>(),
41
+ };
42
+
43
+ public subscribe<
44
+ TSubject extends keyof StateNotifierSubjects<TIO, TPresence>
45
+ >(
46
+ name: TSubject,
47
+ callback: SubscriptionCallback<TIO, TPresence, TSubject>
48
+ ): () => void {
49
+ const source = this.subjects[name]?.source as any;
50
+
51
+ if (!source) throw new Error(`Could not subscribe to source: ${name}`);
52
+
53
+ return subscribe(callback)(source).unsubscribe;
54
+ }
55
+ }
@@ -0,0 +1,128 @@
1
+ import type {
2
+ Id,
3
+ InferIOAuthorize,
4
+ InferIOAuthorizeUser,
5
+ InputZodLike,
6
+ IOLike,
7
+ JsonObject,
8
+ } from "@pluv/types";
9
+ import type { UserInfo } from "./types";
10
+
11
+ export type Presence = Record<string, unknown>;
12
+
13
+ export type UsersManagerConfig<TPresence extends JsonObject = {}> = {
14
+ initialPresence?: TPresence;
15
+ presence?: InputZodLike<TPresence>;
16
+ };
17
+
18
+ export class UsersManager<
19
+ TIO extends IOLike,
20
+ TPresence extends JsonObject = {}
21
+ > {
22
+ private _initialPresence: TPresence;
23
+ private _myself: UserInfo<TIO, TPresence> | null = null;
24
+ private _others = new Map<string, UserInfo<TIO, TPresence>>();
25
+ private _presence: InputZodLike<TPresence> | null = null;
26
+
27
+ constructor(config: UsersManagerConfig<TPresence>) {
28
+ const { initialPresence, presence = null } = config;
29
+
30
+ this._initialPresence = initialPresence as TPresence;
31
+ this._presence = presence;
32
+ }
33
+
34
+ public get myself(): UserInfo<TIO, TPresence> | null {
35
+ return this._myself;
36
+ }
37
+
38
+ public clear(): void {
39
+ this._others.clear();
40
+ }
41
+
42
+ public getOther(connectionId: string): UserInfo<TIO, TPresence> | null {
43
+ return this._others.get(connectionId) ?? null;
44
+ }
45
+
46
+ public getOthers(): readonly UserInfo<TIO, TPresence>[] {
47
+ return Array.from(this._others.values());
48
+ }
49
+
50
+ public patchPresence(
51
+ connectionId: string,
52
+ patch: Partial<TPresence>
53
+ ): void {
54
+ if (this._myself?.connectionId === connectionId) {
55
+ this._myself.presence = Object.assign(
56
+ Object.create(null),
57
+ this._myself.presence,
58
+ patch
59
+ ) as TPresence;
60
+
61
+ return;
62
+ }
63
+
64
+ const other = this._others.get(connectionId);
65
+
66
+ if (!other) return;
67
+
68
+ const presence = Object.assign(
69
+ Object.create(null),
70
+ other.presence,
71
+ patch
72
+ ) as TPresence;
73
+
74
+ this._presence?.parse(presence);
75
+
76
+ this._others.set(connectionId, { ...other, presence });
77
+ }
78
+
79
+ public removeMyself(): void {
80
+ this._myself = null;
81
+ }
82
+
83
+ public removeUser(connectionId: string): void {
84
+ this._others.delete(connectionId);
85
+ }
86
+
87
+ public setMyself(
88
+ connectionId: string,
89
+ user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>
90
+ ): void {
91
+ this._myself = {
92
+ connectionId,
93
+ presence: this._initialPresence,
94
+ user,
95
+ };
96
+ }
97
+
98
+ public setPresence(connectionId: string, presence: TPresence): void {
99
+ if (this._myself?.connectionId === connectionId) {
100
+ this._myself.presence = presence;
101
+
102
+ return;
103
+ }
104
+
105
+ const other = this._others.get(connectionId);
106
+
107
+ if (!other) return;
108
+
109
+ this._others.set(connectionId, { ...other, presence });
110
+ }
111
+
112
+ public setUser(
113
+ connectionId: string,
114
+ user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>
115
+ ): void {
116
+ if (this._myself?.connectionId === connectionId) return;
117
+
118
+ const other = this._others.get(connectionId);
119
+
120
+ if (other) return;
121
+
122
+ this._others.set(connectionId, {
123
+ connectionId,
124
+ presence: this._initialPresence,
125
+ user,
126
+ });
127
+ }
128
+ }
@@ -0,0 +1,9 @@
1
+ import type { IOLike } from "@pluv/types";
2
+ import type { PluvClientOptions } from "./PluvClient";
3
+ import { PluvClient } from "./PluvClient";
4
+
5
+ export const createClient = <TIO extends IOLike = IOLike>(
6
+ options: PluvClientOptions<TIO>
7
+ ): PluvClient<TIO> => {
8
+ return new PluvClient<TIO>(options);
9
+ };
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ export type { InferYjsSharedTypeJson } from "@pluv/crdt-yjs";
2
+ export type {
3
+ BaseUser,
4
+ EventMessage,
5
+ EventRecord,
6
+ IOEventMessage,
7
+ } from "@pluv/types";
8
+ export type { AbstractType } from "yjs";
9
+ export { createClient } from "./createClient";
10
+ export type { PluvClientOptions } from "./PluvClient";
11
+ export { PluvClient } from "./PluvClient";
12
+ export type { PluvRoomDebug } from "./PluvRoom";
13
+ export { PluvRoom } from "./PluvRoom";
14
+ export type { WebSocketConnection, UserInfo, WebSocketState } from "./types";
15
+ export { ConnectionState } from "./types";
16
+ export * as y from "./y";
package/src/types.ts ADDED
@@ -0,0 +1,40 @@
1
+ import type {
2
+ Id,
3
+ InferIOAuthorize,
4
+ InferIOAuthorizeUser,
5
+ IOLike,
6
+ JsonObject,
7
+ } from "@pluv/types";
8
+
9
+ export interface AuthorizationState<TIO extends IOLike> {
10
+ token: string | null;
11
+ user: InferIOAuthorizeUser<InferIOAuthorize<TIO>> | null;
12
+ }
13
+
14
+ export enum ConnectionState {
15
+ Closed = "Closed",
16
+ Connecting = "Connecting",
17
+ Open = "Open",
18
+ Unavailable = "Unavailable",
19
+ Untouched = "Untouched",
20
+ }
21
+
22
+ export interface WebSocketConnection {
23
+ id: string | null;
24
+ state: ConnectionState;
25
+ }
26
+
27
+ export interface WebSocketState<TIO extends IOLike> {
28
+ authorization: AuthorizationState<TIO>;
29
+ connection: WebSocketConnection;
30
+ webSocket: WebSocket | null;
31
+ }
32
+
33
+ export interface UserInfo<
34
+ TIO extends IOLike,
35
+ TPresence extends JsonObject = {}
36
+ > {
37
+ connectionId: string;
38
+ presence: TPresence;
39
+ user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;
40
+ }
package/src/y.ts ADDED
@@ -0,0 +1 @@
1
+ export { array, map, text, unstable__object } from "@pluv/crdt-yjs";
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "@pluv/tsconfig/frontend-generic.json",
3
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
4
+ "exclude": ["node_modules"]
5
+ }