@pluv/client 0.17.3 → 0.19.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/client",
3
- "version": "0.17.3",
3
+ "version": "0.19.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.17.3",
22
- "@pluv/types": "^0.17.3"
21
+ "@pluv/crdt": "^0.19.0",
22
+ "@pluv/types": "^0.19.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "eslint": "^8.57.0",
26
- "tsup": "^8.0.2",
26
+ "tsup": "^8.1.0",
27
27
  "typescript": "^5.4.5",
28
- "@pluv/tsconfig": "^0.17.3",
29
- "eslint-config-pluv": "^0.17.3"
28
+ "@pluv/tsconfig": "^0.19.0",
29
+ "eslint-config-pluv": "^0.19.0"
30
30
  },
31
31
  "scripts": {
32
32
  "build": "tsup src/index.ts --format esm,cjs --dts",
@@ -1,22 +1,9 @@
1
- import type {
2
- AbstractCrdtDoc,
3
- AbstractCrdtType,
4
- InferCrdtStorageJson,
5
- } from "@pluv/crdt";
6
- import type {
7
- IOLike,
8
- Id,
9
- InferIOInput,
10
- InferIOOutput,
11
- JsonObject,
12
- } from "@pluv/types";
1
+ import type { AbstractCrdtDoc, AbstractCrdtType, InferCrdtStorageJson } from "@pluv/crdt";
2
+ import type { IOLike, Id, InferIOInput, InferIOOutput, JsonObject } from "@pluv/types";
13
3
  import type { EventNotifierSubscriptionCallback } from "./EventNotifier";
14
4
  import type { OtherNotifierSubscriptionCallback } from "./OtherNotifier";
15
- import type {
16
- StateNotifierSubjects,
17
- SubscriptionCallback,
18
- } from "./StateNotifier";
19
- import type { UserInfo, WebSocketConnection } from "./types";
5
+ import type { StateNotifierSubjects, SubscriptionCallback } from "./StateNotifier";
6
+ import type { UpdateMyPresenceAction, UserInfo, WebSocketConnection } from "./types";
20
7
 
21
8
  export abstract class AbstractRoom<
22
9
  TIO extends IOLike,
@@ -53,20 +40,13 @@ export abstract class AbstractRoom<
53
40
 
54
41
  public abstract getMyself(): Id<UserInfo<TIO, TPresence>> | null;
55
42
 
56
- public abstract getOther(
57
- connectionId: string,
58
- ): Id<UserInfo<TIO, TPresence>> | null;
43
+ public abstract getOther(connectionId: string): Id<UserInfo<TIO, TPresence>> | null;
59
44
 
60
45
  public abstract getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
61
46
 
62
- public abstract getStorage<TKey extends keyof TStorage>(
63
- key: TKey,
64
- ): TStorage[TKey] | null;
47
+ public abstract getStorage<TKey extends keyof TStorage>(key: TKey): TStorage[TKey] | null;
65
48
 
66
- public abstract other(
67
- connectionId: string,
68
- callback: OtherNotifierSubscriptionCallback<TIO>,
69
- ): () => void;
49
+ public abstract other(connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): () => void;
70
50
 
71
51
  public abstract redo(): void;
72
52
 
@@ -81,19 +61,14 @@ export abstract class AbstractRoom<
81
61
  }) => void,
82
62
  ): () => void;
83
63
 
84
- public abstract subscribe<
85
- TSubject extends keyof StateNotifierSubjects<TIO, TPresence>,
86
- >(
64
+ public abstract subscribe<TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(
87
65
  name: TSubject,
88
66
  callback: SubscriptionCallback<TIO, TPresence, TSubject>,
89
67
  ): () => void;
90
68
 
91
- public abstract transact(
92
- fn: (storage: TStorage) => void,
93
- origin?: string,
94
- ): void;
69
+ public abstract transact(fn: (storage: TStorage) => void, origin?: string): void;
95
70
 
96
71
  public abstract undo(): void;
97
72
 
98
- public abstract updateMyPresence(presence: Partial<TPresence>): void;
73
+ public abstract updateMyPresence(presence: UpdateMyPresenceAction<TPresence>): void;
99
74
  }
@@ -1,13 +1,7 @@
1
- import type {
2
- AbstractCrdtDoc,
3
- AbstractCrdtDocFactory,
4
- AbstractCrdtType,
5
- } from "@pluv/crdt";
1
+ import type { AbstractCrdtDoc, AbstractCrdtDocFactory, AbstractCrdtType } from "@pluv/crdt";
6
2
  import { noop } from "@pluv/crdt";
7
3
 
8
- export type CrdtManagerOptions<
9
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
10
- > = {
4
+ export type CrdtManagerOptions<TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = {
11
5
  encodedState?: string | Uint8Array | null;
12
6
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
13
7
  };
@@ -18,26 +12,19 @@ type CrdtManagerInitializeParams = {
18
12
  update: string | readonly string[];
19
13
  };
20
14
 
21
- export class CrdtManager<
22
- TStorage extends Record<string, AbstractCrdtType<any, any>>,
23
- > {
15
+ export class CrdtManager<TStorage extends Record<string, AbstractCrdtType<any, any>>> {
24
16
  public doc: AbstractCrdtDoc<TStorage>;
25
17
  public initialized: boolean = false;
26
18
 
27
19
  private readonly _docFactory: AbstractCrdtDocFactory<TStorage>;
28
20
 
29
21
  constructor(options: CrdtManagerOptions<TStorage>) {
30
- const {
31
- encodedState,
32
- initialStorage = noop.doc({}) as AbstractCrdtDocFactory<TStorage>,
33
- } = options;
22
+ const { encodedState, initialStorage = noop.doc({}) as AbstractCrdtDocFactory<TStorage> } = options;
34
23
 
35
24
  this._docFactory = initialStorage;
36
25
 
37
26
  this.doc = encodedState
38
- ? initialStorage
39
- .getFresh()
40
- .applyEncodedState({ update: encodedState })
27
+ ? initialStorage.getFresh().applyEncodedState({ update: encodedState })
41
28
  : initialStorage.getFresh();
42
29
  }
43
30
 
@@ -50,8 +37,7 @@ export class CrdtManager<
50
37
  public applyUpdate(update: string | readonly string[], origin?: any): this {
51
38
  if (!this.initialized) return this;
52
39
 
53
- const updates: readonly string[] =
54
- typeof update === "string" ? [update] : update;
40
+ const updates: readonly string[] = typeof update === "string" ? [update] : update;
55
41
 
56
42
  this._applyDocUpdates(this.doc, updates, origin);
57
43
 
@@ -79,20 +65,12 @@ export class CrdtManager<
79
65
  return this;
80
66
  }
81
67
 
82
- const onEmpty = this._applyDocUpdates(
83
- this._docFactory.getEmpty(),
84
- updates,
85
- origin,
86
- );
68
+ const onEmpty = this._applyDocUpdates(this._docFactory.getEmpty(), updates, origin);
87
69
 
88
70
  if (!onEmpty.isEmpty()) {
89
71
  onEmpty.destroy();
90
72
 
91
- this.doc = this._applyDocUpdates(
92
- this._docFactory.getFresh(),
93
- updates,
94
- origin,
95
- ).track();
73
+ this.doc = this._applyDocUpdates(this._docFactory.getFresh(), updates, origin).track();
96
74
  this.initialized = true;
97
75
 
98
76
  onInitialized?.(this.doc.getEncodedState());
@@ -115,9 +93,7 @@ export class CrdtManager<
115
93
  origin?: string,
116
94
  ): AbstractCrdtDoc<TStorage> {
117
95
  doc.transact(() => {
118
- doc.batchApplyEncodedState(
119
- updates.map((update) => ({ origin, update })),
120
- );
96
+ doc.batchApplyEncodedState(updates.map((update) => ({ origin, update })));
121
97
  }, origin);
122
98
 
123
99
  return doc;
@@ -1,21 +1,15 @@
1
1
  import type { AbstractCrdtType, InferCrdtStorageJson } from "@pluv/crdt";
2
- import { makeSubject, Subject, subscribe } from "wonka";
2
+ import type { Subject } from "wonka";
3
+ import { makeSubject, subscribe } from "wonka";
3
4
 
4
- export class CrdtNotifier<
5
- TStorage extends Record<string, AbstractCrdtType<any, any>>,
6
- > {
5
+ export class CrdtNotifier<TStorage extends Record<string, AbstractCrdtType<any, any>>> {
7
6
  public rootSubject = makeSubject<{
8
7
  [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
9
8
  }>();
10
9
 
11
- public subjects = new Map<
12
- keyof TStorage,
13
- Subject<InferCrdtStorageJson<TStorage[keyof TStorage]>> | null
14
- >();
10
+ public subjects = new Map<keyof TStorage, Subject<InferCrdtStorageJson<TStorage[keyof TStorage]>> | null>();
15
11
 
16
- public subject<TKey extends keyof TStorage>(
17
- key: TKey,
18
- ): Subject<InferCrdtStorageJson<TStorage[TKey]>> {
12
+ public subject<TKey extends keyof TStorage>(key: TKey): Subject<InferCrdtStorageJson<TStorage[TKey]>> {
19
13
  const subject = this.subjects.get(key);
20
14
 
21
15
  if (subject) {
@@ -2,20 +2,14 @@ import type { Id, InferIOOutput, IOEventMessage, IOLike } from "@pluv/types";
2
2
  import type { Subject } from "wonka";
3
3
  import { makeSubject, subscribe } from "wonka";
4
4
 
5
- export type EventNotifierSubscriptionCallback<
6
- TIO extends IOLike,
7
- TEvent extends keyof InferIOOutput<TIO>,
8
- > = (value: Id<IOEventMessage<TIO, TEvent>>) => void;
5
+ export type EventNotifierSubscriptionCallback<TIO extends IOLike, TEvent extends keyof InferIOOutput<TIO>> = (
6
+ value: Id<IOEventMessage<TIO, TEvent>>,
7
+ ) => void;
9
8
 
10
9
  export class EventNotifier<TIO extends IOLike> {
11
- public subjects = new Map<
12
- keyof InferIOOutput<TIO>,
13
- Subject<Id<IOEventMessage<TIO, keyof InferIOOutput<TIO>>>>
14
- >();
10
+ public subjects = new Map<keyof InferIOOutput<TIO>, Subject<Id<IOEventMessage<TIO, keyof InferIOOutput<TIO>>>>>();
15
11
 
16
- public subject<TEvent extends keyof InferIOOutput<TIO>>(
17
- name: TEvent,
18
- ): Subject<Id<IOEventMessage<TIO, TEvent>>> {
12
+ public subject<TEvent extends keyof InferIOOutput<TIO>>(name: TEvent): Subject<Id<IOEventMessage<TIO, TEvent>>> {
19
13
  const subject = this.subjects.get(name);
20
14
 
21
15
  if (subject) return subject;
package/src/MockedRoom.ts CHANGED
@@ -1,15 +1,5 @@
1
- import type {
2
- AbstractCrdtDoc,
3
- AbstractCrdtType,
4
- InferCrdtStorageJson,
5
- } from "@pluv/crdt";
6
- import type {
7
- IOLike,
8
- Id,
9
- InferIOInput,
10
- InferIOOutput,
11
- JsonObject,
12
- } from "@pluv/types";
1
+ import type { AbstractCrdtDoc, AbstractCrdtType, InferCrdtStorageJson } from "@pluv/crdt";
2
+ import type { IOLike, Id, InferIOInput, InferIOOutput, JsonObject } from "@pluv/types";
13
3
  import { AbstractRoom } from "./AbstractRoom";
14
4
  import type { CrdtManagerOptions } from "./CrdtManager";
15
5
  import { CrdtManager } from "./CrdtManager";
@@ -18,15 +8,13 @@ import type { EventNotifierSubscriptionCallback } from "./EventNotifier";
18
8
  import { EventNotifier } from "./EventNotifier";
19
9
  import type { OtherNotifierSubscriptionCallback } from "./OtherNotifier";
20
10
  import { OtherNotifier } from "./OtherNotifier";
21
- import type {
22
- StateNotifierSubjects,
23
- SubscriptionCallback,
24
- } from "./StateNotifier";
11
+ import type { StateNotifierSubjects, SubscriptionCallback } from "./StateNotifier";
25
12
  import { StateNotifier } from "./StateNotifier";
26
13
  import type { UsersManagerConfig } from "./UsersManager";
27
14
  import { UsersManager } from "./UsersManager";
28
15
  import type {
29
16
  InternalSubscriptions,
17
+ UpdateMyPresenceAction,
30
18
  UserInfo,
31
19
  WebSocketConnection,
32
20
  WebSocketState,
@@ -34,19 +22,14 @@ import type {
34
22
  import { ConnectionState } from "./types";
35
23
 
36
24
  export type MockedRoomEvents<TIO extends IOLike> = Partial<{
37
- [P in keyof InferIOInput<TIO>]: (
38
- data: Id<InferIOInput<TIO>[P]>,
39
- ) => Partial<InferIOOutput<TIO>>;
25
+ [P in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[P]>) => Partial<InferIOOutput<TIO>>;
40
26
  }>;
41
27
 
42
28
  export type MockedRoomConfig<
43
29
  TIO extends IOLike,
44
30
  TPresence extends JsonObject = {},
45
31
  TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
46
- > = { events?: MockedRoomEvents<TIO> } & Omit<
47
- CrdtManagerOptions<TStorage>,
48
- "encodedState"
49
- > &
32
+ > = { events?: MockedRoomEvents<TIO> } & Omit<CrdtManagerOptions<TStorage>, "encodedState"> &
50
33
  UsersManagerConfig<TPresence>;
51
34
 
52
35
  export class MockedRoom<
@@ -77,10 +60,7 @@ export class MockedRoom<
77
60
  };
78
61
  private _usersManager: UsersManager<TIO, TPresence>;
79
62
 
80
- constructor(
81
- room: string,
82
- options: MockedRoomConfig<TIO, TPresence, TStorage>,
83
- ) {
63
+ constructor(room: string, options: MockedRoomConfig<TIO, TPresence, TStorage>) {
84
64
  const { events, initialPresence, initialStorage, presence } = options;
85
65
 
86
66
  super(room);
@@ -103,10 +83,7 @@ export class MockedRoom<
103
83
  return true;
104
84
  }
105
85
 
106
- public broadcast<TEvent extends keyof InferIOInput<TIO>>(
107
- event: TEvent,
108
- data: Id<InferIOInput<TIO>[TEvent]>,
109
- ): void {
86
+ public broadcast<TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>): void {
110
87
  if (!this._events) return;
111
88
 
112
89
  const resolver = this._events[event];
@@ -140,12 +117,10 @@ export class MockedRoom<
140
117
  return this._eventNotifier.subscribe(event, callback);
141
118
  };
142
119
 
143
- public getConnection(): WebSocketConnection {
120
+ public getConnection = (): WebSocketConnection => {
144
121
  // Create a read-only clone of the connection state
145
- return Object.freeze(
146
- JSON.parse(JSON.stringify(this._state.connection)),
147
- );
148
- }
122
+ return Object.freeze(JSON.parse(JSON.stringify(this._state.connection)));
123
+ };
149
124
 
150
125
  public getDoc(): AbstractCrdtDoc<TStorage> {
151
126
  return this._crdtManager.doc;
@@ -159,9 +134,7 @@ export class MockedRoom<
159
134
  return this._usersManager.myself;
160
135
  };
161
136
 
162
- public getOther = (
163
- connectionId: string,
164
- ): Id<UserInfo<TIO, TPresence>> | null => {
137
+ public getOther = (connectionId: string): Id<UserInfo<TIO, TPresence>> | null => {
165
138
  return this._usersManager.getOther(connectionId);
166
139
  };
167
140
 
@@ -169,9 +142,7 @@ export class MockedRoom<
169
142
  return this._usersManager.getOthers();
170
143
  };
171
144
 
172
- public getStorage = <TKey extends keyof TStorage>(
173
- key: TKey,
174
- ): TStorage[TKey] | null => {
145
+ public getStorage = <TKey extends keyof TStorage>(key: TKey): TStorage[TKey] | null => {
175
146
  const sharedType = this._crdtManager.get(key);
176
147
 
177
148
  if (typeof sharedType === "undefined") return null;
@@ -179,10 +150,7 @@ export class MockedRoom<
179
150
  return sharedType.value;
180
151
  };
181
152
 
182
- public other = (
183
- connectionId: string,
184
- callback: OtherNotifierSubscriptionCallback<TIO>,
185
- ): (() => void) => {
153
+ public other = (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): (() => void) => {
186
154
  return this._otherNotifier.subscribe(connectionId, callback);
187
155
  };
188
156
 
@@ -205,19 +173,14 @@ export class MockedRoom<
205
173
  return this._crdtNotifier.subcribeRoot(fn);
206
174
  };
207
175
 
208
- public subscribe = <
209
- TSubject extends keyof StateNotifierSubjects<TIO, TPresence>,
210
- >(
176
+ public subscribe = <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(
211
177
  name: TSubject,
212
178
  callback: SubscriptionCallback<TIO, TPresence, TSubject>,
213
179
  ): (() => void) => {
214
180
  return this._stateNotifier.subscribe(name, callback);
215
181
  };
216
182
 
217
- public transact = (
218
- fn: (storage: TStorage) => void,
219
- origin?: string,
220
- ): void => {
183
+ public transact = (fn: (storage: TStorage) => void, origin?: string): void => {
221
184
  const _origin = origin ?? this._state.connection.id;
222
185
  const crdtManager = this._crdtManager;
223
186
 
@@ -242,8 +205,10 @@ export class MockedRoom<
242
205
  this._crdtManager.doc.undo();
243
206
  };
244
207
 
245
- public updateMyPresence = (presence: Partial<TPresence>): void => {
246
- this._usersManager.updateMyPresence(presence);
208
+ public updateMyPresence = (presence: UpdateMyPresenceAction<TPresence>): void => {
209
+ const newPresence = typeof presence === "function" ? presence(this.getMyPresence()) : presence;
210
+
211
+ this._usersManager.updateMyPresence(newPresence);
247
212
 
248
213
  const myPresence = this._usersManager.myPresence;
249
214
  const myself = this._usersManager.myself ?? null;
@@ -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,13 +1,6 @@
1
1
  import type { AbstractCrdtType } from "@pluv/crdt";
2
2
  import type { IOLike, JsonObject } from "@pluv/types";
3
- import {
4
- AuthEndpoint,
5
- PluvRoom,
6
- PluvRoomOptions,
7
- RoomConfig,
8
- RoomEndpoints,
9
- WsEndpoint,
10
- } from "./PluvRoom";
3
+ import { AuthEndpoint, PluvRoom, PluvRoomOptions, RoomConfig, RoomEndpoints, WsEndpoint } from "./PluvRoom";
11
4
 
12
5
  export type PluvClientOptions<TIO extends IOLike> = RoomEndpoints<TIO> & {
13
6
  debug?: boolean;
@@ -56,9 +49,7 @@ export class PluvClient<TIO extends IOLike = IOLike> {
56
49
  return newRoom;
57
50
  };
58
51
 
59
- public enter = async (
60
- room: string | PluvRoom<TIO, any, any>,
61
- ): Promise<PluvRoom<TIO, JsonObject, any>> => {
52
+ public enter = async (room: string | PluvRoom<TIO, any, any>): Promise<PluvRoom<TIO, JsonObject, any>> => {
62
53
  const toEnter = typeof room === "string" ? this.getRoom(room) : room;
63
54
 
64
55
  if (!toEnter) {
@@ -80,9 +71,7 @@ export class PluvClient<TIO extends IOLike = IOLike> {
80
71
  >(
81
72
  room: string,
82
73
  ): PluvRoom<TIO, TPresence, TStorage> | null => {
83
- const found = this._rooms.get(room) as
84
- | PluvRoom<TIO, TPresence, TStorage>
85
- | undefined;
74
+ const found = this._rooms.get(room) as PluvRoom<TIO, TPresence, TStorage> | undefined;
86
75
 
87
76
  return found ?? null;
88
77
  };
@@ -91,9 +80,7 @@ export class PluvClient<TIO extends IOLike = IOLike> {
91
80
  return Array.from(this._rooms.values());
92
81
  };
93
82
 
94
- public leave = async (
95
- room: string | PluvRoom<TIO, any, any>,
96
- ): Promise<void> => {
83
+ public leave = async (room: string | PluvRoom<TIO, any, any>): Promise<void> => {
97
84
  const toLeave = typeof room === "string" ? this.getRoom(room) : room;
98
85
 
99
86
  if (!toLeave) return;