@pluv/client 1.0.1 → 2.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/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +19 -0
- package/dist/index.d.mts +111 -172
- package/dist/index.d.ts +111 -172
- package/dist/index.js +117 -127
- package/dist/index.mjs +98 -106
- package/package.json +5 -5
- package/src/CrdtManager.ts +28 -18
- package/src/EventNotifier.ts +7 -6
- package/src/MockedRoom.ts +61 -41
- package/src/OtherNotifier.ts +7 -6
- package/src/PluvClient.ts +36 -28
- package/src/PluvProcedure.ts +2 -1
- package/src/PluvRoom.ts +123 -110
- package/src/PluvRouter.ts +1 -23
- package/src/StateNotifier.ts +1 -2
- package/src/UsersManager.ts +2 -2
- package/src/createClient.ts +5 -5
- package/src/index.ts +14 -12
- package/src/types.ts +5 -19
- package/src/AbstractRoom.ts +0 -84
- package/src/enums.ts +0 -9
package/src/PluvRoom.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
AbstractCrdtDocFactory,
|
|
3
|
+
CrdtType,
|
|
4
|
+
InferCrdtJson,
|
|
5
|
+
InferStorage,
|
|
6
|
+
NoopCrdtDocFactory,
|
|
7
|
+
} from "@pluv/crdt";
|
|
2
8
|
import type {
|
|
3
9
|
BaseIOEventRecord,
|
|
4
10
|
BaseUser,
|
|
11
|
+
CrdtDocLike,
|
|
5
12
|
EventMessage,
|
|
13
|
+
EventNotifierSubscriptionCallback,
|
|
6
14
|
IOEventMessage,
|
|
7
15
|
IOLike,
|
|
8
16
|
Id,
|
|
@@ -12,26 +20,27 @@ import type {
|
|
|
12
20
|
InferIOOutput,
|
|
13
21
|
InputZodLike,
|
|
14
22
|
JsonObject,
|
|
23
|
+
MergeEvents,
|
|
15
24
|
OptionalProps,
|
|
25
|
+
OtherNotifierSubscriptionCallback,
|
|
26
|
+
RoomLike,
|
|
27
|
+
UpdateMyPresenceAction,
|
|
28
|
+
UserInfo,
|
|
29
|
+
WebSocketState,
|
|
16
30
|
} from "@pluv/types";
|
|
17
|
-
import {
|
|
31
|
+
import { ConnectionState } from "@pluv/types";
|
|
18
32
|
import type { AbstractStorageStore } from "./AbstractStorageStore";
|
|
19
33
|
import type { CrdtManagerOptions } from "./CrdtManager";
|
|
20
34
|
import { CrdtManager } from "./CrdtManager";
|
|
21
35
|
import { CrdtNotifier } from "./CrdtNotifier";
|
|
22
|
-
import type { EventNotifierSubscriptionCallback } from "./EventNotifier";
|
|
23
36
|
import { EventNotifier } from "./EventNotifier";
|
|
24
|
-
import type { OtherNotifierSubscriptionCallback } from "./OtherNotifier";
|
|
25
37
|
import { OtherNotifier } from "./OtherNotifier";
|
|
26
38
|
import { PluvProcedure } from "./PluvProcedure";
|
|
27
|
-
import type {
|
|
39
|
+
import type { PluvRouterEventConfig } from "./PluvRouter";
|
|
28
40
|
import { PluvRouter } from "./PluvRouter";
|
|
29
41
|
import type { StateNotifierSubjects, SubscriptionCallback } from "./StateNotifier";
|
|
30
42
|
import { StateNotifier } from "./StateNotifier";
|
|
31
43
|
import { StorageStore } from "./StorageStore";
|
|
32
|
-
import type { UsersManagerConfig } from "./UsersManager";
|
|
33
|
-
import { UsersManager } from "./UsersManager";
|
|
34
|
-
import { ConnectionState } from "./enums";
|
|
35
44
|
import type {
|
|
36
45
|
AuthorizationState,
|
|
37
46
|
EventResolver,
|
|
@@ -39,12 +48,11 @@ import type {
|
|
|
39
48
|
InternalSubscriptions,
|
|
40
49
|
PluvClientLimits,
|
|
41
50
|
PublicKey,
|
|
42
|
-
UpdateMyPresenceAction,
|
|
43
|
-
UserInfo,
|
|
44
51
|
WebSocketConnection,
|
|
45
|
-
WebSocketState,
|
|
46
52
|
WithMetadata,
|
|
47
53
|
} from "./types";
|
|
54
|
+
import type { UsersManagerConfig } from "./UsersManager";
|
|
55
|
+
import { UsersManager } from "./UsersManager";
|
|
48
56
|
import { debounce } from "./utils";
|
|
49
57
|
|
|
50
58
|
const ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1_000;
|
|
@@ -68,12 +76,12 @@ declare global {
|
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
export const DEFAULT_PLUV_CLIENT_ADDON = <
|
|
71
|
-
TIO extends IOLike
|
|
72
|
-
TMetadata extends JsonObject
|
|
73
|
-
TPresence extends JsonObject
|
|
74
|
-
|
|
79
|
+
TIO extends IOLike,
|
|
80
|
+
TMetadata extends JsonObject,
|
|
81
|
+
TPresence extends JsonObject,
|
|
82
|
+
TCrdt extends AbstractCrdtDocFactory<any>,
|
|
75
83
|
>(
|
|
76
|
-
input: PluvRoomAddonInput<TIO, TMetadata, TPresence,
|
|
84
|
+
input: PluvRoomAddonInput<TIO, TMetadata, TPresence, TCrdt>,
|
|
77
85
|
): PluvRoomAddonResult => ({
|
|
78
86
|
storage: new StorageStore(input.room.id),
|
|
79
87
|
});
|
|
@@ -126,21 +134,19 @@ interface InternalListeners {
|
|
|
126
134
|
}
|
|
127
135
|
|
|
128
136
|
export type PluvRoomAddon<
|
|
129
|
-
TIO extends IOLike
|
|
130
|
-
TMetadata extends JsonObject
|
|
131
|
-
TPresence extends JsonObject
|
|
132
|
-
|
|
133
|
-
> = (
|
|
134
|
-
input: PluvRoomAddonInput<TIO, TMetadata, TPresence, TStorage>,
|
|
135
|
-
) => Partial<PluvRoomAddonResult>;
|
|
137
|
+
TIO extends IOLike,
|
|
138
|
+
TMetadata extends JsonObject,
|
|
139
|
+
TPresence extends JsonObject,
|
|
140
|
+
TCrdt extends AbstractCrdtDocFactory<any>,
|
|
141
|
+
> = (input: PluvRoomAddonInput<TIO, TMetadata, TPresence, TCrdt>) => Partial<PluvRoomAddonResult>;
|
|
136
142
|
|
|
137
143
|
export interface PluvRoomAddonInput<
|
|
138
|
-
TIO extends IOLike
|
|
139
|
-
TMetadata extends JsonObject
|
|
140
|
-
TPresence extends JsonObject
|
|
141
|
-
|
|
144
|
+
TIO extends IOLike,
|
|
145
|
+
TMetadata extends JsonObject,
|
|
146
|
+
TPresence extends JsonObject,
|
|
147
|
+
TCrdt extends AbstractCrdtDocFactory<any>,
|
|
142
148
|
> {
|
|
143
|
-
room: PluvRoom<TIO, TMetadata, TPresence,
|
|
149
|
+
room: PluvRoom<TIO, TMetadata, TPresence, TCrdt>;
|
|
144
150
|
}
|
|
145
151
|
|
|
146
152
|
export interface PluvRoomAddonResult {
|
|
@@ -167,22 +173,22 @@ export type ReconnectTimeoutMs = number | ((params: ReconnectTimeoutMsParams) =>
|
|
|
167
173
|
|
|
168
174
|
export type RoomConfig<
|
|
169
175
|
TIO extends IOLike,
|
|
170
|
-
TMetadata extends JsonObject
|
|
171
|
-
TPresence extends JsonObject
|
|
172
|
-
|
|
173
|
-
TEvents extends PluvRouterEventConfig<TIO, TPresence,
|
|
176
|
+
TMetadata extends JsonObject,
|
|
177
|
+
TPresence extends JsonObject,
|
|
178
|
+
TCrdt extends AbstractCrdtDocFactory<any>,
|
|
179
|
+
TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>>,
|
|
174
180
|
> = Id<
|
|
175
181
|
{
|
|
176
|
-
addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence,
|
|
182
|
+
addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt>[];
|
|
177
183
|
debug?: boolean | PluvRoomDebug<TIO>;
|
|
178
184
|
limits: PluvClientLimits;
|
|
179
185
|
onAuthorizationFail?: (error: Error) => void;
|
|
180
186
|
metadata?: InputZodLike<TMetadata>;
|
|
181
187
|
publicKey?: PublicKey<TMetadata>;
|
|
182
188
|
reconnectTimeoutMs?: ReconnectTimeoutMs;
|
|
183
|
-
router?: PluvRouter<TIO, TPresence,
|
|
189
|
+
router?: PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>;
|
|
184
190
|
} & RoomEndpoints<TIO, TMetadata> &
|
|
185
|
-
Pick<CrdtManagerOptions<
|
|
191
|
+
Pick<CrdtManagerOptions<TCrdt>, "initialStorage"> &
|
|
186
192
|
UsersManagerConfig<TPresence>
|
|
187
193
|
>;
|
|
188
194
|
|
|
@@ -190,15 +196,17 @@ export class PluvRoom<
|
|
|
190
196
|
TIO extends IOLike<any, any>,
|
|
191
197
|
TMetadata extends JsonObject = {},
|
|
192
198
|
TPresence extends JsonObject = {},
|
|
193
|
-
|
|
194
|
-
TEvents extends PluvRouterEventConfig<TIO, TPresence,
|
|
195
|
-
>
|
|
199
|
+
TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory,
|
|
200
|
+
TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},
|
|
201
|
+
> implements RoomLike<TIO, TPresence, InferStorage<TCrdt>>
|
|
202
|
+
{
|
|
196
203
|
readonly _endpoints: RoomEndpoints<TIO, TMetadata>;
|
|
197
204
|
|
|
205
|
+
public readonly id: string;
|
|
198
206
|
public readonly metadata?: InputZodLike<TMetadata>;
|
|
199
207
|
|
|
200
|
-
private readonly _crdtManager: CrdtManager<
|
|
201
|
-
private readonly _crdtNotifier = new CrdtNotifier<
|
|
208
|
+
private readonly _crdtManager: CrdtManager<TCrdt>;
|
|
209
|
+
private readonly _crdtNotifier = new CrdtNotifier<InferStorage<TCrdt>>();
|
|
202
210
|
private readonly _debug: boolean | PluvRoomDebug<TIO>;
|
|
203
211
|
private readonly _eventNotifier = new EventNotifier<MergeEvents<TEvents, TIO>>();
|
|
204
212
|
private readonly _intervals: IntervalIds = {
|
|
@@ -209,7 +217,7 @@ export class PluvRoom<
|
|
|
209
217
|
private readonly _otherNotifier = new OtherNotifier<TIO, TPresence>();
|
|
210
218
|
private readonly _publicKey: PublicKey<TMetadata> | null = null;
|
|
211
219
|
private readonly _reconnectTimeoutMs: ReconnectTimeoutMs;
|
|
212
|
-
private readonly _router: PluvRouter<TIO, TPresence,
|
|
220
|
+
private readonly _router: PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>;
|
|
213
221
|
private readonly _stateNotifier = new StateNotifier<TIO, TPresence>();
|
|
214
222
|
private readonly _storageStore: AbstractStorageStore;
|
|
215
223
|
private readonly _subscriptions: InternalSubscriptions = {
|
|
@@ -238,7 +246,7 @@ export class PluvRoom<
|
|
|
238
246
|
private _windowListeners: WindowListeners | null = null;
|
|
239
247
|
private _wsListeners: WebSocketListeners | null = null;
|
|
240
248
|
|
|
241
|
-
constructor(room: string, options: RoomConfig<TIO, TMetadata, TPresence,
|
|
249
|
+
constructor(room: string, options: RoomConfig<TIO, TMetadata, TPresence, TCrdt, TEvents>) {
|
|
242
250
|
const {
|
|
243
251
|
addons = [],
|
|
244
252
|
authEndpoint,
|
|
@@ -255,22 +263,20 @@ export class PluvRoom<
|
|
|
255
263
|
wsEndpoint,
|
|
256
264
|
} = options;
|
|
257
265
|
|
|
258
|
-
|
|
266
|
+
this.id = room;
|
|
267
|
+
this.metadata = metadata;
|
|
259
268
|
|
|
260
269
|
const addon = this._getAddon(addons);
|
|
261
|
-
|
|
262
270
|
const { storage } = {
|
|
263
271
|
...DEFAULT_PLUV_CLIENT_ADDON({ room: this }),
|
|
264
272
|
...addon({ room: this }),
|
|
265
273
|
};
|
|
266
|
-
|
|
267
|
-
this.metadata = metadata;
|
|
274
|
+
this._storageStore = storage;
|
|
268
275
|
|
|
269
276
|
this._debug = debug;
|
|
270
277
|
this._endpoints = { authEndpoint, wsEndpoint } as RoomEndpoints<TIO, TMetadata>;
|
|
271
278
|
this._limits = limits;
|
|
272
279
|
this._reconnectTimeoutMs = reconnectTimeoutMs;
|
|
273
|
-
this._storageStore = storage;
|
|
274
280
|
|
|
275
281
|
if (!!publicKey) this._publicKey = publicKey;
|
|
276
282
|
|
|
@@ -281,13 +287,14 @@ export class PluvRoom<
|
|
|
281
287
|
};
|
|
282
288
|
|
|
283
289
|
this._router =
|
|
284
|
-
router ??
|
|
290
|
+
router ??
|
|
291
|
+
(new PluvRouter({}) as PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>);
|
|
285
292
|
this._usersManager = new UsersManager<TIO, TPresence>({
|
|
286
293
|
initialPresence,
|
|
287
294
|
limits: this._limits,
|
|
288
295
|
presence,
|
|
289
296
|
});
|
|
290
|
-
this._crdtManager = new CrdtManager<
|
|
297
|
+
this._crdtManager = new CrdtManager<TCrdt>({ initialStorage });
|
|
291
298
|
}
|
|
292
299
|
|
|
293
300
|
public get storageLoaded(): boolean {
|
|
@@ -313,7 +320,7 @@ export class PluvRoom<
|
|
|
313
320
|
any,
|
|
314
321
|
any,
|
|
315
322
|
TPresence,
|
|
316
|
-
|
|
323
|
+
InferStorage<TCrdt>
|
|
317
324
|
> | null;
|
|
318
325
|
|
|
319
326
|
if (!procedure?.config.broadcast) {
|
|
@@ -327,7 +334,7 @@ export class PluvRoom<
|
|
|
327
334
|
if (!myself) return;
|
|
328
335
|
|
|
329
336
|
const parsed = procedure.config.input ? procedure.config.input.parse(data) : data;
|
|
330
|
-
const context: EventResolverContext<TIO, TPresence,
|
|
337
|
+
const context: EventResolverContext<TIO, TPresence, InferStorage<TCrdt>> = {
|
|
331
338
|
doc: this._crdtManager.doc,
|
|
332
339
|
others: this._usersManager.getOthers(),
|
|
333
340
|
room: this.id,
|
|
@@ -335,7 +342,13 @@ export class PluvRoom<
|
|
|
335
342
|
};
|
|
336
343
|
|
|
337
344
|
const output = await (
|
|
338
|
-
procedure.config.broadcast as EventResolver<
|
|
345
|
+
procedure.config.broadcast as EventResolver<
|
|
346
|
+
TIO,
|
|
347
|
+
any,
|
|
348
|
+
any,
|
|
349
|
+
TPresence,
|
|
350
|
+
InferStorage<TCrdt>
|
|
351
|
+
>
|
|
339
352
|
)(parsed, context);
|
|
340
353
|
|
|
341
354
|
Object.entries(output).forEach(([_type, _data]) => {
|
|
@@ -389,8 +402,7 @@ export class PluvRoom<
|
|
|
389
402
|
return oldState;
|
|
390
403
|
});
|
|
391
404
|
|
|
392
|
-
|
|
393
|
-
// await this._applyStorageStore();
|
|
405
|
+
await this._storageStore.initialize();
|
|
394
406
|
|
|
395
407
|
const wsEndpoint = this._getWsEndpoint(this.id, params);
|
|
396
408
|
const url = new URL(wsEndpoint);
|
|
@@ -484,7 +496,7 @@ export class PluvRoom<
|
|
|
484
496
|
return Object.freeze(JSON.parse(JSON.stringify(this._state.connection)));
|
|
485
497
|
};
|
|
486
498
|
|
|
487
|
-
public getDoc = ():
|
|
499
|
+
public getDoc = (): CrdtDocLike<InferStorage<TCrdt>> => {
|
|
488
500
|
return this._crdtManager.doc;
|
|
489
501
|
};
|
|
490
502
|
|
|
@@ -504,7 +516,9 @@ export class PluvRoom<
|
|
|
504
516
|
return this._usersManager.getOthers();
|
|
505
517
|
};
|
|
506
518
|
|
|
507
|
-
public getStorage = <TKey extends keyof
|
|
519
|
+
public getStorage = <TKey extends keyof InferStorage<TCrdt>>(
|
|
520
|
+
type: TKey,
|
|
521
|
+
): InferStorage<TCrdt>[TKey] | null => {
|
|
508
522
|
const sharedType = this._crdtManager.get(type);
|
|
509
523
|
|
|
510
524
|
if (typeof sharedType === "undefined") return null;
|
|
@@ -512,11 +526,11 @@ export class PluvRoom<
|
|
|
512
526
|
return sharedType;
|
|
513
527
|
};
|
|
514
528
|
|
|
515
|
-
public getStorageJson(): InferCrdtJson<
|
|
516
|
-
public getStorageJson<TKey extends keyof
|
|
529
|
+
public getStorageJson(): InferCrdtJson<InferStorage<TCrdt>> | null;
|
|
530
|
+
public getStorageJson<TKey extends keyof InferStorage<TCrdt>>(
|
|
517
531
|
type: TKey,
|
|
518
|
-
): InferCrdtJson<
|
|
519
|
-
public getStorageJson<TKey extends keyof
|
|
532
|
+
): InferCrdtJson<InferStorage<TCrdt>[TKey]> | null;
|
|
533
|
+
public getStorageJson<TKey extends keyof InferStorage<TCrdt>>(type?: TKey) {
|
|
520
534
|
if (this._state.connection.id === null) return null;
|
|
521
535
|
|
|
522
536
|
if (typeof type === "undefined") return this._crdtManager.doc.toJson();
|
|
@@ -543,16 +557,16 @@ export class PluvRoom<
|
|
|
543
557
|
this._crdtManager.doc.redo();
|
|
544
558
|
};
|
|
545
559
|
|
|
546
|
-
public storage = <TKey extends keyof
|
|
560
|
+
public storage = <TKey extends keyof InferStorage<TCrdt>>(
|
|
547
561
|
key: TKey,
|
|
548
|
-
fn: (value: InferCrdtJson<
|
|
562
|
+
fn: (value: InferCrdtJson<InferStorage<TCrdt>[TKey]>) => void,
|
|
549
563
|
): (() => void) => {
|
|
550
564
|
return this._crdtNotifier.subscribe(key, fn);
|
|
551
565
|
};
|
|
552
566
|
|
|
553
567
|
public storageRoot = (
|
|
554
568
|
fn: (value: {
|
|
555
|
-
[P in keyof
|
|
569
|
+
[P in keyof InferStorage<TCrdt>]: InferCrdtJson<InferStorage<TCrdt>[P]>;
|
|
556
570
|
}) => void,
|
|
557
571
|
): (() => void) => {
|
|
558
572
|
return this._crdtNotifier.subcribeRoot(fn);
|
|
@@ -565,7 +579,7 @@ export class PluvRoom<
|
|
|
565
579
|
return this._stateNotifier.subscribe(name, callback);
|
|
566
580
|
};
|
|
567
581
|
|
|
568
|
-
public transact = (fn: (storage:
|
|
582
|
+
public transact = (fn: (storage: InferStorage<TCrdt>) => void, origin?: string): void => {
|
|
569
583
|
const _origin = origin ?? this._state.connection.id;
|
|
570
584
|
|
|
571
585
|
/**
|
|
@@ -612,22 +626,6 @@ export class PluvRoom<
|
|
|
612
626
|
this._flattenStorageStore();
|
|
613
627
|
}
|
|
614
628
|
|
|
615
|
-
private async _applyStorageStore(): Promise<void> {
|
|
616
|
-
const updates = await this._storageStore.getUpdates();
|
|
617
|
-
|
|
618
|
-
this._crdtManager.initialize({
|
|
619
|
-
onInitialized: () => {
|
|
620
|
-
this._stateNotifier.subjects["storage-loaded"].next(true);
|
|
621
|
-
|
|
622
|
-
this._observeCrdt();
|
|
623
|
-
},
|
|
624
|
-
origin: ORIGIN_INITIALIZED,
|
|
625
|
-
update: updates,
|
|
626
|
-
});
|
|
627
|
-
|
|
628
|
-
this._emitSharedTypes();
|
|
629
|
-
}
|
|
630
|
-
|
|
631
629
|
private _attachWindowListeners(): void {
|
|
632
630
|
if (typeof window === "undefined") return;
|
|
633
631
|
if (typeof document === "undefined") return;
|
|
@@ -724,7 +722,7 @@ export class PluvRoom<
|
|
|
724
722
|
|
|
725
723
|
return { ...acc, [prop]: serialized };
|
|
726
724
|
},
|
|
727
|
-
{} as { [P in keyof
|
|
725
|
+
{} as { [P in keyof InferStorage<TCrdt>]: InferCrdtJson<InferStorage<TCrdt>[P]> },
|
|
728
726
|
);
|
|
729
727
|
|
|
730
728
|
this._crdtNotifier.rootSubject.next(storageRoot);
|
|
@@ -738,7 +736,7 @@ export class PluvRoom<
|
|
|
738
736
|
|
|
739
737
|
const updates = await this._storageStore.getUpdates();
|
|
740
738
|
|
|
741
|
-
this._crdtManager.applyUpdate(updates, this);
|
|
739
|
+
this._crdtManager.applyUpdate({ update: updates, origin: this });
|
|
742
740
|
|
|
743
741
|
const encodedState = this._crdtManager.doc.getEncodedState();
|
|
744
742
|
|
|
@@ -748,9 +746,9 @@ export class PluvRoom<
|
|
|
748
746
|
);
|
|
749
747
|
|
|
750
748
|
private _getAddon = (
|
|
751
|
-
addons: readonly PluvRoomAddon<TIO, TMetadata, TPresence,
|
|
752
|
-
): PluvRoomAddon<TIO, TMetadata, TPresence,
|
|
753
|
-
return addons.reduce<PluvRoomAddon<TIO, TMetadata, TPresence,
|
|
749
|
+
addons: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt>[],
|
|
750
|
+
): PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt> => {
|
|
751
|
+
return addons.reduce<PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt>>(
|
|
754
752
|
(acc, addon) => () => ({
|
|
755
753
|
...acc({ room: this }),
|
|
756
754
|
...addon({ room: this }),
|
|
@@ -941,7 +939,7 @@ export class PluvRoom<
|
|
|
941
939
|
this._stateNotifier.subjects.others.next(others);
|
|
942
940
|
}
|
|
943
941
|
|
|
944
|
-
private _handleRegisteredMessage(message: IOEventMessage<TIO>): void {
|
|
942
|
+
private async _handleRegisteredMessage(message: IOEventMessage<TIO>): Promise<void> {
|
|
945
943
|
const { connectionId } = message;
|
|
946
944
|
const user = message.user as Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;
|
|
947
945
|
|
|
@@ -950,11 +948,6 @@ export class PluvRoom<
|
|
|
950
948
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
951
949
|
|
|
952
950
|
const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>["$registered"];
|
|
953
|
-
/**
|
|
954
|
-
* TODO
|
|
955
|
-
* @description Maybe we can use this to resolve storage initialization more efficiently
|
|
956
|
-
* @date May 7, 2025
|
|
957
|
-
*/
|
|
958
951
|
const state = data.state;
|
|
959
952
|
|
|
960
953
|
this._updateState((oldState) => {
|
|
@@ -978,7 +971,10 @@ export class PluvRoom<
|
|
|
978
971
|
this._stateNotifier.subjects["my-presence"].next(presence);
|
|
979
972
|
this._stateNotifier.subjects.myself.next(myself);
|
|
980
973
|
|
|
981
|
-
const update =
|
|
974
|
+
const update = !state
|
|
975
|
+
? (this._crdtManager.resolveEncodedState(await this._storageStore.getUpdates()) ??
|
|
976
|
+
this._crdtManager.getInitialState())
|
|
977
|
+
: this._crdtManager.getInitialState();
|
|
982
978
|
|
|
983
979
|
this._sendMessage({
|
|
984
980
|
type: "$initializeSession",
|
|
@@ -994,30 +990,45 @@ export class PluvRoom<
|
|
|
994
990
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
995
991
|
|
|
996
992
|
const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>["$storageReceived"];
|
|
993
|
+
const changeKind = data.changeKind;
|
|
997
994
|
const state = data.state;
|
|
998
995
|
|
|
999
|
-
this.
|
|
1000
|
-
|
|
1001
|
-
await this._addToStorageStore(update);
|
|
1002
|
-
this._emitSharedTypes();
|
|
1003
|
-
this._observeCrdt();
|
|
1004
|
-
this._stateNotifier.subjects["storage-loaded"].next(true);
|
|
1005
|
-
},
|
|
1006
|
-
origin: ORIGIN_INITIALIZED,
|
|
1007
|
-
update: state,
|
|
1008
|
-
});
|
|
996
|
+
const updates = await this._storageStore.getUpdates();
|
|
997
|
+
const origin = ORIGIN_INITIALIZED;
|
|
1009
998
|
|
|
1010
|
-
const
|
|
999
|
+
const updateStorage = (update: string | readonly string[]) => {
|
|
1000
|
+
return this._crdtManager.initialized
|
|
1001
|
+
? this._crdtManager.applyUpdate({ update, origin })
|
|
1002
|
+
: this._crdtManager.initialize({ update, origin });
|
|
1003
|
+
};
|
|
1011
1004
|
|
|
1012
|
-
|
|
1005
|
+
if (changeKind === "empty") {
|
|
1006
|
+
this._crdtManager.destroy();
|
|
1007
|
+
this._crdtManager.initialize({ origin, update: updates });
|
|
1008
|
+
} else if (changeKind === "initialized") {
|
|
1009
|
+
if (!!updates.length) updateStorage(updates);
|
|
1010
|
+
else {
|
|
1011
|
+
const encodedState = updateStorage(state).doc.getEncodedState();
|
|
1013
1012
|
|
|
1014
|
-
|
|
1013
|
+
this._addToStorageStore(encodedState);
|
|
1014
|
+
}
|
|
1015
|
+
} else {
|
|
1016
|
+
const encodedState = updateStorage(state).doc.getEncodedState();
|
|
1017
|
+
|
|
1018
|
+
if (!!updates.length) this._crdtManager.applyUpdate({ update: updates, origin });
|
|
1019
|
+
|
|
1020
|
+
await this._addToStorageStore(encodedState);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
const encodedState = this._crdtManager.doc.getEncodedState();
|
|
1015
1024
|
|
|
1016
|
-
|
|
1025
|
+
this._emitSharedTypes();
|
|
1026
|
+
this._observeCrdt();
|
|
1027
|
+
this._stateNotifier.subjects["storage-loaded"].next(true);
|
|
1017
1028
|
|
|
1018
1029
|
this._sendMessage({
|
|
1019
1030
|
type: "$updateStorage",
|
|
1020
|
-
data: { origin
|
|
1031
|
+
data: { origin, update: encodedState },
|
|
1021
1032
|
});
|
|
1022
1033
|
}
|
|
1023
1034
|
|
|
@@ -1048,7 +1059,7 @@ export class PluvRoom<
|
|
|
1048
1059
|
return { ...acc, [prop]: serialized };
|
|
1049
1060
|
},
|
|
1050
1061
|
{} as {
|
|
1051
|
-
[P in keyof
|
|
1062
|
+
[P in keyof InferStorage<TCrdt>]: InferCrdtJson<InferStorage<TCrdt>[P]>;
|
|
1052
1063
|
},
|
|
1053
1064
|
);
|
|
1054
1065
|
|
|
@@ -1125,7 +1136,8 @@ export class PluvRoom<
|
|
|
1125
1136
|
}
|
|
1126
1137
|
|
|
1127
1138
|
private _logDebug(...data: any[]): void {
|
|
1128
|
-
if (
|
|
1139
|
+
if (typeof process === "undefined") return;
|
|
1140
|
+
if (process.env?.NODE_ENV === "production") return;
|
|
1129
1141
|
if (this._debug) console.log(...data);
|
|
1130
1142
|
}
|
|
1131
1143
|
|
|
@@ -1135,13 +1147,14 @@ export class PluvRoom<
|
|
|
1135
1147
|
const unsubscribe = this._crdtManager.doc.subscribe((event) => {
|
|
1136
1148
|
const origin = event.origin ?? null;
|
|
1137
1149
|
|
|
1138
|
-
this._addToStorageStore(event.update);
|
|
1139
1150
|
this._emitSharedTypes();
|
|
1140
1151
|
|
|
1141
1152
|
if (origin === ORIGIN_INITIALIZED || origin === ORIGIN_STORAGE_UPDATED) {
|
|
1142
1153
|
return;
|
|
1143
1154
|
}
|
|
1144
1155
|
|
|
1156
|
+
this._addToStorageStore(event.update);
|
|
1157
|
+
|
|
1145
1158
|
if (!this._state.webSocket) return;
|
|
1146
1159
|
if (!this._state.connection.id) return;
|
|
1147
1160
|
if (this._state.webSocket.readyState !== WebSocket.OPEN) return;
|
package/src/PluvRouter.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CrdtType } from "@pluv/crdt";
|
|
2
|
-
import type {
|
|
2
|
+
import type { IOLike, IORouterLike, JsonObject } from "@pluv/types";
|
|
3
3
|
import type { PluvProcedure } from "./PluvProcedure";
|
|
4
4
|
|
|
5
5
|
export type PluvRouterEventConfig<
|
|
@@ -21,28 +21,6 @@ export type MergedRouter<
|
|
|
21
21
|
? MergedRouter<ITail, TIO, TPresence, TStorage, TRoot & IHead["_defs"]["events"]>
|
|
22
22
|
: PluvRouter<TIO, TPresence, TStorage, TRoot>;
|
|
23
23
|
|
|
24
|
-
export type MergeEvents<
|
|
25
|
-
TClientEvents extends PluvRouterEventConfig<any, any, any>,
|
|
26
|
-
TServerIO extends IOLike<any, any>,
|
|
27
|
-
> =
|
|
28
|
-
TServerIO extends IOLike<infer IAuthorize, infer IServerEvents>
|
|
29
|
-
? IOLike<
|
|
30
|
-
IAuthorize,
|
|
31
|
-
{
|
|
32
|
-
[P in keyof TClientEvents]: TClientEvents[P] extends ProcedureLike<
|
|
33
|
-
infer IClientInput,
|
|
34
|
-
infer IClientOutput
|
|
35
|
-
>
|
|
36
|
-
? {
|
|
37
|
-
[K in keyof IClientOutput]: K extends keyof IServerEvents
|
|
38
|
-
? IServerEvents[K]
|
|
39
|
-
: ProcedureLike<IClientInput, Id<Pick<IClientOutput, K>>>;
|
|
40
|
-
}[keyof IClientOutput]
|
|
41
|
-
: never;
|
|
42
|
-
} & IServerEvents
|
|
43
|
-
>
|
|
44
|
-
: never;
|
|
45
|
-
|
|
46
24
|
export class PluvRouter<
|
|
47
25
|
TIO extends IOLike = IOLike,
|
|
48
26
|
TPresence extends JsonObject = {},
|
package/src/StateNotifier.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { Id, IOLike, JsonObject } from "@pluv/types";
|
|
1
|
+
import type { Id, IOLike, JsonObject, UserInfo, WebSocketState } from "@pluv/types";
|
|
2
2
|
import type { Subject } from "wonka";
|
|
3
3
|
import { makeSubject, subscribe } from "wonka";
|
|
4
|
-
import type { UserInfo, WebSocketState } from "./types";
|
|
5
4
|
|
|
6
5
|
export interface StateNotifierSubjects<TIO extends IOLike, TPresence extends JsonObject> {
|
|
7
6
|
connection: Subject<Id<WebSocketState<TIO>>>;
|
package/src/UsersManager.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { InputZodLike, IOLike, JsonObject, OptionalProps } from "@pluv/types";
|
|
2
|
-
import type { PluvClientLimits
|
|
1
|
+
import type { InputZodLike, IOLike, JsonObject, OptionalProps, UserInfo } from "@pluv/types";
|
|
2
|
+
import type { PluvClientLimits } from "./types";
|
|
3
3
|
import { pickBy } from "./utils";
|
|
4
4
|
|
|
5
5
|
export type Presence = Record<string, unknown>;
|
package/src/createClient.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AbstractCrdtDocFactory, NoopCrdtDocFactory } from "@pluv/crdt";
|
|
2
2
|
import type { IOLike, JsonObject } from "@pluv/types";
|
|
3
3
|
import type { PluvClientOptions } from "./PluvClient";
|
|
4
4
|
import { PluvClient } from "./PluvClient";
|
|
@@ -6,10 +6,10 @@ import { PluvClient } from "./PluvClient";
|
|
|
6
6
|
export const createClient = <
|
|
7
7
|
TIO extends IOLike<any, any>,
|
|
8
8
|
TPresence extends JsonObject = {},
|
|
9
|
-
|
|
9
|
+
TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory,
|
|
10
10
|
TMetadata extends JsonObject = {},
|
|
11
11
|
>(
|
|
12
|
-
options: PluvClientOptions<TIO, TPresence,
|
|
13
|
-
): PluvClient<TIO, TPresence,
|
|
14
|
-
return new PluvClient<TIO, TPresence,
|
|
12
|
+
options: PluvClientOptions<TIO, TPresence, TCrdt, TMetadata>,
|
|
13
|
+
): PluvClient<TIO, TPresence, TCrdt, TMetadata> => {
|
|
14
|
+
return new PluvClient<TIO, TPresence, TCrdt, TMetadata>(options);
|
|
15
15
|
};
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
export type { CrdtType, InferCrdtJson } from "@pluv/crdt";
|
|
2
|
-
export type {
|
|
3
|
-
|
|
2
|
+
export type {
|
|
3
|
+
BaseUser,
|
|
4
|
+
ConnectionState,
|
|
5
|
+
EventMessage,
|
|
6
|
+
EventNotifierSubscriptionCallback,
|
|
7
|
+
EventRecord,
|
|
8
|
+
IOEventMessage,
|
|
9
|
+
MergeEvents,
|
|
10
|
+
RoomLike,
|
|
11
|
+
UserInfo,
|
|
12
|
+
WebSocketState,
|
|
13
|
+
} from "@pluv/types";
|
|
4
14
|
export { AbstractStorageStore } from "./AbstractStorageStore";
|
|
5
15
|
export { createClient } from "./createClient";
|
|
6
|
-
export { ConnectionState } from "./enums";
|
|
7
16
|
export { infer } from "./infer";
|
|
8
17
|
export type { InferCallback } from "./infer";
|
|
9
18
|
export { MockedRoom } from "./MockedRoom";
|
|
@@ -20,14 +29,7 @@ export type {
|
|
|
20
29
|
RoomConnectParams,
|
|
21
30
|
RoomEndpoints,
|
|
22
31
|
} from "./PluvRoom";
|
|
23
|
-
export type {
|
|
32
|
+
export type { PluvRouter, PluvRouterEventConfig } from "./PluvRouter";
|
|
24
33
|
export { register } from "./register";
|
|
25
34
|
export type { RegisterParams } from "./register";
|
|
26
|
-
export type {
|
|
27
|
-
InferMetadata,
|
|
28
|
-
PublicKey,
|
|
29
|
-
PublicKeyParams,
|
|
30
|
-
UserInfo,
|
|
31
|
-
WebSocketConnection,
|
|
32
|
-
WebSocketState,
|
|
33
|
-
} from "./types";
|
|
35
|
+
export type { InferMetadata, PublicKey, PublicKeyParams, WebSocketConnection } from "./types";
|