@pluv/client 0.15.0 → 0.16.1

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.15.0",
3
+ "version": "0.16.1",
4
4
  "description": "@pluv/io frontend client",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -18,19 +18,15 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "wonka": "^6.3.4",
21
- "@pluv/crdt-yjs": "^0.15.0",
22
- "@pluv/types": "^0.15.0"
23
- },
24
- "peerDependencies": {
25
- "yjs": "^13.0.0"
21
+ "@pluv/crdt": "^0.16.1",
22
+ "@pluv/types": "^0.16.1"
26
23
  },
27
24
  "devDependencies": {
28
25
  "eslint": "^8.56.0",
29
- "tsup": "^8.0.1",
30
- "typescript": "^5.2.2",
31
- "yjs": "^13.6.10",
32
- "@pluv/tsconfig": "^0.15.0",
33
- "eslint-config-pluv": "^0.15.0"
26
+ "tsup": "^8.0.2",
27
+ "typescript": "^5.3.3",
28
+ "@pluv/tsconfig": "^0.16.1",
29
+ "eslint-config-pluv": "^0.16.1"
34
30
  },
35
31
  "scripts": {
36
32
  "build": "tsup src/index.ts --format esm,cjs --dts",
@@ -1,12 +1,15 @@
1
- import type { InferYjsSharedTypeJson } from "@pluv/crdt-yjs";
2
1
  import type {
2
+ AbstractCrdtDoc,
3
+ AbstractCrdtType,
4
+ InferCrdtStorageJson,
5
+ } from "@pluv/crdt";
6
+ import type {
7
+ IOLike,
3
8
  Id,
4
9
  InferIOInput,
5
10
  InferIOOutput,
6
- IOLike,
7
11
  JsonObject,
8
12
  } from "@pluv/types";
9
- import type { AbstractType, Doc } from "yjs";
10
13
  import type { EventNotifierSubscriptionCallback } from "./EventNotifier";
11
14
  import type { OtherNotifierSubscriptionCallback } from "./OtherNotifier";
12
15
  import type {
@@ -18,7 +21,7 @@ import type { UserInfo, WebSocketConnection } from "./types";
18
21
  export abstract class AbstractRoom<
19
22
  TIO extends IOLike,
20
23
  TPresence extends JsonObject = {},
21
- TStorage extends Record<string, AbstractType<any>> = {},
24
+ TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
22
25
  > {
23
26
  public readonly id: string;
24
27
 
@@ -26,6 +29,8 @@ export abstract class AbstractRoom<
26
29
  this.id = room;
27
30
  }
28
31
 
32
+ public abstract storageLoaded: boolean;
33
+
29
34
  public abstract broadcast<TEvent extends keyof InferIOInput<TIO>>(
30
35
  event: TEvent,
31
36
  data: Id<InferIOInput<TIO>[TEvent]>,
@@ -35,7 +40,7 @@ export abstract class AbstractRoom<
35
40
 
36
41
  public abstract canUndo(): boolean;
37
42
 
38
- public abstract getDoc(): Doc;
43
+ public abstract getDoc(): AbstractCrdtDoc<TStorage>;
39
44
 
40
45
  public abstract event<TEvent extends keyof InferIOOutput<TIO>>(
41
46
  event: TEvent,
@@ -56,7 +61,7 @@ export abstract class AbstractRoom<
56
61
 
57
62
  public abstract getStorage<TKey extends keyof TStorage>(
58
63
  key: TKey,
59
- ): TStorage[TKey];
64
+ ): TStorage[TKey] | null;
60
65
 
61
66
  public abstract other(
62
67
  connectionId: string,
@@ -67,12 +72,12 @@ export abstract class AbstractRoom<
67
72
 
68
73
  public abstract storage<TKey extends keyof TStorage>(
69
74
  key: TKey,
70
- fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void,
75
+ fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void,
71
76
  ): () => void;
72
77
 
73
78
  public abstract storageRoot(
74
79
  fn: (value: {
75
- [P in keyof TStorage]: InferYjsSharedTypeJson<TStorage[P]>;
80
+ [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
76
81
  }) => void,
77
82
  ): () => void;
78
83
 
@@ -1,47 +1,44 @@
1
- import type { TrackOriginOptions, YjsDoc } from "@pluv/crdt-yjs";
2
- import { doc } from "@pluv/crdt-yjs";
3
- import type { AbstractType } from "yjs";
1
+ import type {
2
+ AbstractCrdtDoc,
3
+ AbstractCrdtDocFactory,
4
+ AbstractCrdtType,
5
+ } from "@pluv/crdt";
6
+ import { noop } from "@pluv/crdt";
4
7
 
5
8
  export type CrdtManagerOptions<
6
- TStorage extends Record<string, AbstractType<any>> = {},
9
+ TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
7
10
  > = {
8
11
  encodedState?: string | Uint8Array | null;
9
- initialStorage?: () => TStorage;
10
- } & TrackOriginOptions;
12
+ initialStorage?: AbstractCrdtDocFactory<TStorage>;
13
+ };
11
14
 
12
15
  type CrdtManagerInitializeParams = {
13
- onInitialized?: () => void;
16
+ onInitialized?: (state: string) => void;
14
17
  origin?: any;
15
18
  update: string | readonly string[];
16
- } & TrackOriginOptions;
19
+ };
17
20
 
18
- export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
19
- public doc: YjsDoc<TStorage>;
21
+ export class CrdtManager<
22
+ TStorage extends Record<string, AbstractCrdtType<any, any>>,
23
+ > {
24
+ public doc: AbstractCrdtDoc<TStorage>;
20
25
  public initialized: boolean = false;
21
26
 
22
- constructor(options: CrdtManagerOptions<TStorage> = {}) {
27
+ private readonly _docFactory: AbstractCrdtDocFactory<TStorage>;
28
+
29
+ constructor(options: CrdtManagerOptions<TStorage>) {
23
30
  const {
24
- captureTimeout,
25
31
  encodedState,
26
- initialStorage,
27
- trackedOrigins = [],
32
+ initialStorage = noop.doc({}) as AbstractCrdtDocFactory<TStorage>,
28
33
  } = options;
29
34
 
30
- const _doc = encodedState
31
- ? doc<TStorage>().applyUpdate(encodedState)
32
- : null;
33
-
34
- if (_doc && !!Object.keys(_doc.toJSON()).length) {
35
- this.doc = _doc;
36
- this.trackOrigins({ captureTimeout, trackedOrigins });
37
-
38
- return;
39
- }
40
-
41
- _doc?.destroy();
35
+ this._docFactory = initialStorage;
42
36
 
43
- this.doc = doc<TStorage>(initialStorage?.());
44
- this.trackOrigins({ captureTimeout, trackedOrigins });
37
+ this.doc = encodedState
38
+ ? initialStorage
39
+ .getFresh()
40
+ .applyEncodedState({ update: encodedState })
41
+ : initialStorage.getFresh();
45
42
  }
46
43
 
47
44
  /**
@@ -70,58 +67,59 @@ export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
70
67
  }
71
68
 
72
69
  public initialize(params: CrdtManagerInitializeParams): this {
73
- const {
74
- captureTimeout,
75
- onInitialized,
76
- origin,
77
- trackedOrigins,
78
- update,
79
- } = params;
70
+ const { onInitialized, origin, update } = params;
80
71
 
81
72
  const updates = typeof update === "string" ? [update] : update;
82
73
 
83
- this.trackOrigins({ captureTimeout, trackedOrigins });
84
-
85
74
  if (!updates.length) return this;
86
75
 
87
76
  if (this.initialized) {
88
- this._applyDocUpdates(this.doc, updates, origin);
77
+ this.doc = this._applyDocUpdates(this.doc, updates, origin).track();
89
78
 
90
79
  return this;
91
80
  }
92
81
 
93
- const _doc = this._applyDocUpdates(doc<TStorage>(), updates, origin);
82
+ const onEmpty = this._applyDocUpdates(
83
+ this._docFactory.getEmpty(),
84
+ updates,
85
+ origin,
86
+ );
94
87
 
95
- this.initialized = true;
88
+ if (!onEmpty.isEmpty()) {
89
+ onEmpty.destroy();
96
90
 
97
- if (!!Object.keys(_doc.toJSON()).length) {
98
- this.doc = _doc;
99
- } else {
100
- _doc.destroy();
101
- }
91
+ this.doc = this._applyDocUpdates(
92
+ this._docFactory.getFresh(),
93
+ updates,
94
+ origin,
95
+ ).track();
96
+ this.initialized = true;
102
97
 
103
- onInitialized?.();
98
+ onInitialized?.(this.doc.getEncodedState());
104
99
 
105
- this.trackOrigins({ captureTimeout, trackedOrigins });
100
+ return this;
101
+ }
106
102
 
107
- return this;
108
- }
103
+ onEmpty.destroy();
104
+ this.doc = this._docFactory.getInitialized().track();
105
+ this.initialized = true;
109
106
 
110
- public trackOrigins(options: TrackOriginOptions): void {
111
- const { captureTimeout, trackedOrigins } = options;
107
+ onInitialized?.(this.doc.getEncodedState());
112
108
 
113
- this.doc.trackOrigins({ captureTimeout, trackedOrigins });
109
+ return this;
114
110
  }
115
111
 
116
112
  private _applyDocUpdates(
117
- yjsDoc: YjsDoc<TStorage>,
113
+ doc: AbstractCrdtDoc<TStorage>,
118
114
  updates: readonly string[],
119
115
  origin?: string,
120
- ): YjsDoc<TStorage> {
121
- yjsDoc.transact(() => {
122
- updates.reduce((acc, _update) => acc.applyUpdate(_update), yjsDoc);
116
+ ): AbstractCrdtDoc<TStorage> {
117
+ doc.transact(() => {
118
+ doc.batchApplyEncodedState(
119
+ updates.map((update) => ({ origin, update })),
120
+ );
123
121
  }, origin);
124
122
 
125
- return yjsDoc;
123
+ return doc;
126
124
  }
127
125
  }
@@ -1,37 +1,37 @@
1
- import type { InferYjsSharedTypeJson } from "@pluv/crdt-yjs";
1
+ import type { AbstractCrdtType, InferCrdtStorageJson } from "@pluv/crdt";
2
2
  import { makeSubject, Subject, subscribe } from "wonka";
3
- import type { AbstractType } from "yjs";
4
3
 
5
- export class CrdtNotifier<TStorage extends Record<string, AbstractType<any>>> {
4
+ export class CrdtNotifier<
5
+ TStorage extends Record<string, AbstractCrdtType<any, any>>,
6
+ > {
6
7
  public rootSubject = makeSubject<{
7
- [P in keyof TStorage]: InferYjsSharedTypeJson<TStorage[P]>;
8
+ [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
8
9
  }>();
9
10
 
10
11
  public subjects = new Map<
11
12
  keyof TStorage,
12
- Subject<InferYjsSharedTypeJson<TStorage[keyof TStorage]>> | null
13
+ Subject<InferCrdtStorageJson<TStorage[keyof TStorage]>> | null
13
14
  >();
14
15
 
15
16
  public subject<TKey extends keyof TStorage>(
16
17
  key: TKey,
17
- ): Subject<InferYjsSharedTypeJson<TStorage[TKey]>> {
18
+ ): Subject<InferCrdtStorageJson<TStorage[TKey]>> {
18
19
  const subject = this.subjects.get(key);
19
20
 
20
21
  if (subject) {
21
- return subject as Subject<InferYjsSharedTypeJson<TStorage[TKey]>>;
22
+ return subject as Subject<InferCrdtStorageJson<TStorage[TKey]>>;
22
23
  }
23
24
 
24
- const newSubject =
25
- makeSubject<InferYjsSharedTypeJson<TStorage[TKey]>>();
25
+ const newSubject = makeSubject<InferCrdtStorageJson<TStorage[TKey]>>();
26
26
 
27
27
  this.subjects.set(key, newSubject);
28
28
 
29
- return newSubject as Subject<InferYjsSharedTypeJson<TStorage[TKey]>>;
29
+ return newSubject as Subject<InferCrdtStorageJson<TStorage[TKey]>>;
30
30
  }
31
31
 
32
32
  public subcribeRoot(
33
33
  callback: (value: {
34
- [P in keyof TStorage]: InferYjsSharedTypeJson<TStorage[P]>;
34
+ [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
35
35
  }) => void,
36
36
  ): () => void {
37
37
  return subscribe(callback)(this.rootSubject.source).unsubscribe;
@@ -39,7 +39,7 @@ export class CrdtNotifier<TStorage extends Record<string, AbstractType<any>>> {
39
39
 
40
40
  public subscribe<TKey extends keyof TStorage>(
41
41
  key: TKey,
42
- callback: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void,
42
+ callback: (value: InferCrdtStorageJson<TStorage[TKey]>) => void,
43
43
  ): () => void {
44
44
  const subject = this.subject(key);
45
45
 
package/src/MockedRoom.ts CHANGED
@@ -1,14 +1,18 @@
1
- import type { InferYjsSharedTypeJson } from "@pluv/crdt-yjs";
2
1
  import type {
2
+ AbstractCrdtDoc,
3
+ AbstractCrdtType,
4
+ InferCrdtStorageJson,
5
+ } from "@pluv/crdt";
6
+ import type {
7
+ IOLike,
3
8
  Id,
4
9
  InferIOInput,
5
10
  InferIOOutput,
6
- IOLike,
7
11
  JsonObject,
8
12
  } from "@pluv/types";
9
- import type { AbstractType, Doc } from "yjs";
10
13
  import { AbstractRoom } from "./AbstractRoom";
11
- import { CrdtManager, CrdtManagerOptions } from "./CrdtManager";
14
+ import type { CrdtManagerOptions } from "./CrdtManager";
15
+ import { CrdtManager } from "./CrdtManager";
12
16
  import { CrdtNotifier } from "./CrdtNotifier";
13
17
  import type { EventNotifierSubscriptionCallback } from "./EventNotifier";
14
18
  import { EventNotifier } from "./EventNotifier";
@@ -19,6 +23,8 @@ import type {
19
23
  SubscriptionCallback,
20
24
  } from "./StateNotifier";
21
25
  import { StateNotifier } from "./StateNotifier";
26
+ import type { UsersManagerConfig } from "./UsersManager";
27
+ import { UsersManager } from "./UsersManager";
22
28
  import type {
23
29
  InternalSubscriptions,
24
30
  UserInfo,
@@ -26,8 +32,6 @@ import type {
26
32
  WebSocketState,
27
33
  } from "./types";
28
34
  import { ConnectionState } from "./types";
29
- import type { UsersManagerConfig } from "./UsersManager";
30
- import { UsersManager } from "./UsersManager";
31
35
 
32
36
  export type MockedRoomEvents<TIO extends IOLike> = Partial<{
33
37
  [P in keyof InferIOInput<TIO>]: (
@@ -38,7 +42,7 @@ export type MockedRoomEvents<TIO extends IOLike> = Partial<{
38
42
  export type MockedRoomConfig<
39
43
  TIO extends IOLike,
40
44
  TPresence extends JsonObject = {},
41
- TStorage extends Record<string, AbstractType<any>> = {},
45
+ TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
42
46
  > = { events?: MockedRoomEvents<TIO> } & Omit<
43
47
  CrdtManagerOptions<TStorage>,
44
48
  "encodedState"
@@ -48,7 +52,7 @@ export type MockedRoomConfig<
48
52
  export class MockedRoom<
49
53
  TIO extends IOLike,
50
54
  TPresence extends JsonObject = {},
51
- TStorage extends Record<string, AbstractType<any>> = {},
55
+ TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
52
56
  > extends AbstractRoom<TIO, TPresence, TStorage> {
53
57
  private _crdtManager: CrdtManager<TStorage>;
54
58
  private _crdtNotifier = new CrdtNotifier<TStorage>();
@@ -95,6 +99,10 @@ export class MockedRoom<
95
99
  this._observeCrdt();
96
100
  }
97
101
 
102
+ public get storageLoaded(): boolean {
103
+ return true;
104
+ }
105
+
98
106
  public broadcast<TEvent extends keyof InferIOInput<TIO>>(
99
107
  event: TEvent,
100
108
  data: Id<InferIOInput<TIO>[TEvent]>,
@@ -139,8 +147,8 @@ export class MockedRoom<
139
147
  );
140
148
  }
141
149
 
142
- public getDoc(): Doc {
143
- return this._crdtManager.doc.value;
150
+ public getDoc(): AbstractCrdtDoc<TStorage> {
151
+ return this._crdtManager.doc;
144
152
  }
145
153
 
146
154
  public getMyPresence = (): TPresence => {
@@ -163,14 +171,12 @@ export class MockedRoom<
163
171
 
164
172
  public getStorage = <TKey extends keyof TStorage>(
165
173
  key: TKey,
166
- ): TStorage[TKey] => {
174
+ ): TStorage[TKey] | null => {
167
175
  const sharedType = this._crdtManager.get(key);
168
176
 
169
- if (typeof sharedType === "undefined") {
170
- throw new Error(`Could not find storege: ${key.toString()}`);
171
- }
177
+ if (typeof sharedType === "undefined") return null;
172
178
 
173
- return sharedType;
179
+ return sharedType.value;
174
180
  };
175
181
 
176
182
  public other = (
@@ -186,14 +192,14 @@ export class MockedRoom<
186
192
 
187
193
  public storage = <TKey extends keyof TStorage>(
188
194
  key: TKey,
189
- fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void,
195
+ fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void,
190
196
  ): (() => void) => {
191
197
  return this._crdtNotifier.subscribe(key, fn);
192
198
  };
193
199
 
194
200
  public storageRoot = (
195
201
  fn: (value: {
196
- [P in keyof TStorage]: InferYjsSharedTypeJson<TStorage[P]>;
202
+ [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
197
203
  }) => void,
198
204
  ): (() => void) => {
199
205
  return this._crdtNotifier.subcribeRoot(fn);
@@ -226,7 +232,9 @@ export class MockedRoom<
226
232
  if (!crdtManager) return;
227
233
 
228
234
  crdtManager.doc.transact(() => {
229
- fn(crdtManager.doc.storage);
235
+ const storage = crdtManager.doc.get();
236
+
237
+ fn(storage);
230
238
  }, _origin);
231
239
  };
232
240
 
@@ -249,35 +257,31 @@ export class MockedRoom<
249
257
 
250
258
  if (!this._crdtManager) return;
251
259
 
252
- const unsubscribe = this._crdtManager.doc.subscribe(
253
- (update, _origin) => {
254
- const origin = _origin ?? null;
260
+ const unsubscribe = this._crdtManager.doc.subscribe((event) => {
261
+ const origin = event.origin ?? null;
255
262
 
256
- if (!this._crdtManager) return;
257
- if (origin === "$STORAGE_UPDATED") return;
263
+ if (!this._crdtManager) return;
264
+ if (origin === "$STORAGE_UPDATED") return;
258
265
 
259
- const sharedTypes = this._crdtManager.doc.getSharedTypes();
266
+ const sharedTypes = this._crdtManager.doc.get();
260
267
 
261
- const storageRoot = Object.entries(sharedTypes).reduce(
262
- (acc, [prop, sharedType]) => {
263
- if (!this._crdtManager) return acc;
268
+ const storageRoot = Object.entries(sharedTypes).reduce(
269
+ (acc, [prop, sharedType]) => {
270
+ if (!this._crdtManager) return acc;
264
271
 
265
- const serialized = sharedType.toJSON();
272
+ const serialized = sharedType.toJson();
266
273
 
267
- this._crdtNotifier.subject(prop).next(serialized);
274
+ this._crdtNotifier.subject(prop).next(serialized);
268
275
 
269
- return { ...acc, [prop]: serialized };
270
- },
271
- {} as {
272
- [P in keyof TStorage]: InferYjsSharedTypeJson<
273
- TStorage[P]
274
- >;
275
- },
276
- );
276
+ return { ...acc, [prop]: serialized };
277
+ },
278
+ {} as {
279
+ [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
280
+ },
281
+ );
277
282
 
278
- this._crdtNotifier.rootSubject.next(storageRoot);
279
- },
280
- );
283
+ this._crdtNotifier.rootSubject.next(storageRoot);
284
+ });
281
285
 
282
286
  this._subscriptions.observeCrdt = unsubscribe;
283
287
  }
@@ -1,7 +1,7 @@
1
1
  import type { Id, IOLike } from "@pluv/types";
2
2
  import type { Subject } from "wonka";
3
3
  import { makeSubject, subscribe } from "wonka";
4
- import { UserInfo } from "./types";
4
+ import type { UserInfo } from "./types";
5
5
 
6
6
  export type OtherNotifierSubscriptionCallback<TIO extends IOLike> = (
7
7
  value: Id<UserInfo<TIO>> | null,
package/src/PluvClient.ts CHANGED
@@ -1,5 +1,5 @@
1
+ import type { AbstractCrdtType } from "@pluv/crdt";
1
2
  import type { IOLike, JsonObject } from "@pluv/types";
2
- import { AbstractType } from "yjs";
3
3
  import {
4
4
  AuthEndpoint,
5
5
  PluvRoom,
@@ -30,10 +30,10 @@ export class PluvClient<TIO extends IOLike = IOLike> {
30
30
 
31
31
  public createRoom = <
32
32
  TPresence extends JsonObject = {},
33
- TStorage extends Record<string, AbstractType<any>> = {},
33
+ TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
34
34
  >(
35
35
  room: string,
36
- options: PluvRoomOptions<TIO, TPresence, TStorage> = {},
36
+ options: PluvRoomOptions<TIO, TPresence, TStorage>,
37
37
  ): PluvRoom<TIO, TPresence, TStorage> => {
38
38
  const oldRoom = this.getRoom<TPresence, TStorage>(room);
39
39
 
@@ -76,7 +76,7 @@ export class PluvClient<TIO extends IOLike = IOLike> {
76
76
 
77
77
  public getRoom = <
78
78
  TPresence extends JsonObject = {},
79
- TStorage extends Record<string, AbstractType<any>> = {},
79
+ TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
80
80
  >(
81
81
  room: string,
82
82
  ): PluvRoom<TIO, TPresence, TStorage> | null => {