@pluv/client 0.8.0 → 0.9.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 +56 -0
- package/dist/index.d.ts +32 -8
- package/dist/index.js +233 -100
- package/dist/index.mjs +232 -100
- package/package.json +10 -8
- package/src/AbstractRoom.ts +1 -1
- package/src/AbstractStorageStore.ts +21 -0
- package/src/CrdtManager.ts +64 -0
- package/src/MockedRoom.ts +2 -2
- package/src/PluvClient.ts +9 -6
- package/src/PluvRoom.ts +162 -73
- package/src/StorageStore.ts +31 -0
- package/src/index.ts +7 -1
- package/src/utils/debounce.ts +27 -0
- package/src/utils/index.ts +1 -0
package/dist/index.mjs
CHANGED
|
@@ -49,10 +49,18 @@ var AbstractRoom = class {
|
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
+
// src/AbstractStorageStore.ts
|
|
53
|
+
var AbstractStorageStore = class {
|
|
54
|
+
constructor(room) {
|
|
55
|
+
this.room = room;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
52
59
|
// src/CrdtManager.ts
|
|
53
60
|
import { doc } from "@pluv/crdt-yjs";
|
|
54
61
|
var CrdtManager = class {
|
|
55
62
|
constructor(options = {}) {
|
|
63
|
+
this.initialized = false;
|
|
56
64
|
const { encodedState, initialStorage } = options;
|
|
57
65
|
const _doc = encodedState ? doc().applyUpdate(encodedState) : null;
|
|
58
66
|
if (_doc && !!Object.keys(_doc.toJSON()).length) {
|
|
@@ -62,12 +70,50 @@ var CrdtManager = class {
|
|
|
62
70
|
_doc == null ? void 0 : _doc.destroy();
|
|
63
71
|
this.doc = doc(initialStorage == null ? void 0 : initialStorage());
|
|
64
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* !HACK
|
|
75
|
+
* @description If this is called before the doc is initialized. Omit the
|
|
76
|
+
* update. This unfortunately means updates can be dropped entirely
|
|
77
|
+
* @date June 13, 2023
|
|
78
|
+
*/
|
|
79
|
+
applyUpdate(update, origin) {
|
|
80
|
+
if (!this.initialized)
|
|
81
|
+
return this;
|
|
82
|
+
const updates = typeof update === "string" ? [update] : update;
|
|
83
|
+
this._applyDocUpdates(this.doc, updates, origin);
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
65
86
|
destroy() {
|
|
66
87
|
this.doc.destroy();
|
|
67
88
|
}
|
|
68
89
|
get(key) {
|
|
69
90
|
return this.doc.get(key);
|
|
70
91
|
}
|
|
92
|
+
initialize(params) {
|
|
93
|
+
const { onInitialized, origin, update } = params;
|
|
94
|
+
const updates = typeof update === "string" ? [update] : update;
|
|
95
|
+
if (!updates.length)
|
|
96
|
+
return this;
|
|
97
|
+
if (this.initialized) {
|
|
98
|
+
this._applyDocUpdates(this.doc, updates, origin);
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
const _doc = this._applyDocUpdates(doc(), updates, origin);
|
|
102
|
+
this.initialized = true;
|
|
103
|
+
if (!!Object.keys(_doc.toJSON()).length) {
|
|
104
|
+
this.doc = _doc;
|
|
105
|
+
} else {
|
|
106
|
+
_doc.destroy();
|
|
107
|
+
}
|
|
108
|
+
onInitialized == null ? void 0 : onInitialized();
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
_applyDocUpdates(yjsDoc, updates, origin) {
|
|
112
|
+
yjsDoc.transact(() => {
|
|
113
|
+
updates.reduce((acc, _update) => acc.applyUpdate(_update), yjsDoc);
|
|
114
|
+
}, origin);
|
|
115
|
+
return yjsDoc;
|
|
116
|
+
}
|
|
71
117
|
};
|
|
72
118
|
|
|
73
119
|
// src/CrdtNotifier.ts
|
|
@@ -151,6 +197,31 @@ var StateNotifier = class {
|
|
|
151
197
|
}
|
|
152
198
|
};
|
|
153
199
|
|
|
200
|
+
// src/StorageStore.ts
|
|
201
|
+
var StorageStore = class extends AbstractStorageStore {
|
|
202
|
+
addUpdate() {
|
|
203
|
+
return Promise.resolve();
|
|
204
|
+
}
|
|
205
|
+
applyUpdates() {
|
|
206
|
+
return Promise.resolve();
|
|
207
|
+
}
|
|
208
|
+
destroy() {
|
|
209
|
+
return Promise.resolve();
|
|
210
|
+
}
|
|
211
|
+
flatten() {
|
|
212
|
+
return Promise.resolve();
|
|
213
|
+
}
|
|
214
|
+
getShouldFlatten() {
|
|
215
|
+
return Promise.resolve(false);
|
|
216
|
+
}
|
|
217
|
+
getUpdates() {
|
|
218
|
+
return Promise.resolve([]);
|
|
219
|
+
}
|
|
220
|
+
initialize() {
|
|
221
|
+
return Promise.resolve();
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
154
225
|
// src/types.ts
|
|
155
226
|
var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
|
|
156
227
|
ConnectionState2["Closed"] = "Closed";
|
|
@@ -256,13 +327,40 @@ var UsersManager = class {
|
|
|
256
327
|
}
|
|
257
328
|
};
|
|
258
329
|
|
|
330
|
+
// src/utils/debounce.ts
|
|
331
|
+
var debounce = (fn, options) => {
|
|
332
|
+
const { wait, immediate = false } = options != null ? options : {};
|
|
333
|
+
let prevResult;
|
|
334
|
+
let timeout = null;
|
|
335
|
+
return (...args) => {
|
|
336
|
+
if (typeof timeout === "number")
|
|
337
|
+
clearTimeout(timeout);
|
|
338
|
+
timeout = setTimeout(() => {
|
|
339
|
+
timeout = null;
|
|
340
|
+
if (!immediate)
|
|
341
|
+
prevResult = fn(args);
|
|
342
|
+
}, wait);
|
|
343
|
+
if (immediate && !timeout) {
|
|
344
|
+
fn(args);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
return prevResult;
|
|
348
|
+
};
|
|
349
|
+
};
|
|
350
|
+
|
|
259
351
|
// src/PluvRoom.ts
|
|
352
|
+
var ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1e3;
|
|
260
353
|
var HEARTBEAT_INTERVAL_MS = 1e4;
|
|
261
354
|
var PONG_TIMEOUT_MS = 2e3;
|
|
262
355
|
var RECONNECT_TIMEOUT_MS = 3e4;
|
|
356
|
+
var Y_ORIGIN_INITIALIZED = "$INITIALIZED";
|
|
357
|
+
var DEFAULT_PLUV_CLIENT_ADDON = (input) => ({
|
|
358
|
+
storage: new StorageStore(input.room.id)
|
|
359
|
+
});
|
|
263
360
|
var PluvRoom = class extends AbstractRoom {
|
|
264
361
|
constructor(room, options) {
|
|
265
362
|
const {
|
|
363
|
+
addons = [],
|
|
266
364
|
authEndpoint,
|
|
267
365
|
debug = false,
|
|
268
366
|
initialPresence,
|
|
@@ -272,10 +370,8 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
272
370
|
wsEndpoint
|
|
273
371
|
} = options;
|
|
274
372
|
super(room);
|
|
275
|
-
this._crdtManager = null;
|
|
276
373
|
this._crdtNotifier = new CrdtNotifier();
|
|
277
374
|
this._eventNotifier = new EventNotifier();
|
|
278
|
-
this._initialStorage = null;
|
|
279
375
|
this._intervals = {
|
|
280
376
|
heartbeat: null
|
|
281
377
|
};
|
|
@@ -306,8 +402,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
306
402
|
return this._eventNotifier.subscribe(event, callback);
|
|
307
403
|
};
|
|
308
404
|
this.getMyPresence = () => {
|
|
309
|
-
|
|
310
|
-
return (_b = (_a = this._usersManager.myself) == null ? void 0 : _a.presence) != null ? _b : null;
|
|
405
|
+
return this._usersManager.myPresence;
|
|
311
406
|
};
|
|
312
407
|
this.getMyself = () => {
|
|
313
408
|
return this._usersManager.myself;
|
|
@@ -343,6 +438,27 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
343
438
|
{ presence }
|
|
344
439
|
);
|
|
345
440
|
};
|
|
441
|
+
this._flattenStorageStore = debounce(
|
|
442
|
+
() => __async(this, null, function* () {
|
|
443
|
+
const shouldFlatten = yield this._storageStore.getShouldFlatten();
|
|
444
|
+
if (!shouldFlatten)
|
|
445
|
+
return;
|
|
446
|
+
const updates = yield this._storageStore.getUpdates();
|
|
447
|
+
this._crdtManager.applyUpdate(updates, this);
|
|
448
|
+
const encodedState = this._crdtManager.doc.encodeStateAsUpdate();
|
|
449
|
+
yield this._storageStore.flatten(encodedState);
|
|
450
|
+
}),
|
|
451
|
+
{ wait: ADD_TO_STORAGE_STATE_DEBOUNCE_MS }
|
|
452
|
+
);
|
|
453
|
+
this._getAddon = (addons) => {
|
|
454
|
+
return addons.reduce(
|
|
455
|
+
(acc, addon) => () => __spreadValues(__spreadValues({}, acc({ room: this })), addon({ room: this })),
|
|
456
|
+
DEFAULT_PLUV_CLIENT_ADDON
|
|
457
|
+
);
|
|
458
|
+
};
|
|
459
|
+
const addon = this._getAddon(addons);
|
|
460
|
+
const { storage } = __spreadValues(__spreadValues({}, DEFAULT_PLUV_CLIENT_ADDON({ room: this })), addon({ room: this }));
|
|
461
|
+
this._storageStore = storage;
|
|
346
462
|
this._debug = debug;
|
|
347
463
|
this._endpoints = { authEndpoint, wsEndpoint };
|
|
348
464
|
this._listeners = {
|
|
@@ -359,8 +475,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
359
475
|
initialPresence,
|
|
360
476
|
presence
|
|
361
477
|
});
|
|
362
|
-
|
|
363
|
-
this._initialStorage = initialStorage;
|
|
478
|
+
this._crdtManager = new CrdtManager({ initialStorage });
|
|
364
479
|
}
|
|
365
480
|
get webSocket() {
|
|
366
481
|
return this._state.webSocket;
|
|
@@ -389,19 +504,17 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
389
504
|
oldState.connection.state = "Connecting" /* Connecting */;
|
|
390
505
|
return oldState;
|
|
391
506
|
});
|
|
507
|
+
yield this._storageStore.initialize();
|
|
508
|
+
yield this._applyStorageStore();
|
|
392
509
|
const url = new URL(this._getWsEndpoint(this.id));
|
|
393
510
|
let authToken = null;
|
|
394
|
-
try {
|
|
395
|
-
authToken = yield this._getAuthorization(this.id);
|
|
396
|
-
} catch (err) {
|
|
397
|
-
this._onAuthorizationFail(err);
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
authToken && url.searchParams.set("token", encodeURIComponent(authToken));
|
|
401
511
|
let webSocket;
|
|
402
512
|
try {
|
|
513
|
+
authToken = yield this._getAuthorization(this.id);
|
|
514
|
+
authToken && url.searchParams.set("token", encodeURIComponent(authToken));
|
|
403
515
|
webSocket = new WebSocket(url.toString());
|
|
404
516
|
} catch (err) {
|
|
517
|
+
yield this._storageStore.destroy();
|
|
405
518
|
this._onAuthorizationFail(err);
|
|
406
519
|
return;
|
|
407
520
|
}
|
|
@@ -415,27 +528,24 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
415
528
|
});
|
|
416
529
|
}
|
|
417
530
|
disconnect() {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
oldState.connection.state = "Closed" /* Closed */;
|
|
437
|
-
oldState.webSocket = null;
|
|
438
|
-
return oldState;
|
|
531
|
+
return __async(this, null, function* () {
|
|
532
|
+
var _a;
|
|
533
|
+
if (!this._endpoints.wsEndpoint) {
|
|
534
|
+
throw new Error("Must provide an wsEndpoint.");
|
|
535
|
+
}
|
|
536
|
+
if (!this._state.webSocket)
|
|
537
|
+
return;
|
|
538
|
+
const canDisconnect = [
|
|
539
|
+
"Connecting" /* Connecting */,
|
|
540
|
+
"Open" /* Open */
|
|
541
|
+
].some((state) => this._state.connection.state === state);
|
|
542
|
+
if (!canDisconnect)
|
|
543
|
+
return;
|
|
544
|
+
this._clearInterval(this._intervals.heartbeat);
|
|
545
|
+
this._closeWs();
|
|
546
|
+
yield this._storageStore.destroy();
|
|
547
|
+
(_a = this._crdtManager) == null ? void 0 : _a.destroy();
|
|
548
|
+
this._stateNotifier.subjects["my-presence"].next(null);
|
|
439
549
|
});
|
|
440
550
|
}
|
|
441
551
|
getConnection() {
|
|
@@ -443,6 +553,25 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
443
553
|
JSON.parse(JSON.stringify(this._state.connection))
|
|
444
554
|
);
|
|
445
555
|
}
|
|
556
|
+
_addToStorageStore(update) {
|
|
557
|
+
return __async(this, null, function* () {
|
|
558
|
+
yield this._storageStore.addUpdate(update);
|
|
559
|
+
this._flattenStorageStore();
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
_applyStorageStore() {
|
|
563
|
+
return __async(this, null, function* () {
|
|
564
|
+
const updates = yield this._storageStore.getUpdates();
|
|
565
|
+
this._crdtManager.initialize({
|
|
566
|
+
onInitialized: () => {
|
|
567
|
+
this._observeCrdt();
|
|
568
|
+
},
|
|
569
|
+
origin: Y_ORIGIN_INITIALIZED,
|
|
570
|
+
update: updates
|
|
571
|
+
});
|
|
572
|
+
this._emitSharedTypes();
|
|
573
|
+
});
|
|
574
|
+
}
|
|
446
575
|
_attachWindowListeners() {
|
|
447
576
|
if (typeof window === "undefined")
|
|
448
577
|
return;
|
|
@@ -515,7 +644,6 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
515
644
|
if (canClose)
|
|
516
645
|
this._state.webSocket.close();
|
|
517
646
|
this._detachWsListeners();
|
|
518
|
-
this._stateNotifier.subjects["myself"].next(null);
|
|
519
647
|
}
|
|
520
648
|
_detachWindowListeners() {
|
|
521
649
|
if (!this._windowListeners)
|
|
@@ -555,23 +683,11 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
555
683
|
);
|
|
556
684
|
this._wsListeners = null;
|
|
557
685
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
const { url, options } = fetchOptions;
|
|
564
|
-
const res = yield fetch(url, options);
|
|
565
|
-
if (!res.ok || res.status !== 200) {
|
|
566
|
-
throw new Error("Room is unauthorized");
|
|
567
|
-
}
|
|
568
|
-
try {
|
|
569
|
-
return yield res.text().then((text2) => text2.trim());
|
|
570
|
-
} catch (err) {
|
|
571
|
-
throw new Error(
|
|
572
|
-
err instanceof Error ? err.message : "Room is unauthorized"
|
|
573
|
-
);
|
|
574
|
-
}
|
|
686
|
+
_emitSharedTypes() {
|
|
687
|
+
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
688
|
+
Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
|
|
689
|
+
const serialized = sharedType.toJSON();
|
|
690
|
+
this._crdtNotifier.subject(prop).next(serialized);
|
|
575
691
|
});
|
|
576
692
|
}
|
|
577
693
|
_getAuthFetchOptions(room) {
|
|
@@ -592,6 +708,25 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
592
708
|
const result = this._endpoints.authEndpoint(room);
|
|
593
709
|
return typeof result === "string" ? { url: result, options: {} } : result;
|
|
594
710
|
}
|
|
711
|
+
_getAuthorization(room) {
|
|
712
|
+
return __async(this, null, function* () {
|
|
713
|
+
const fetchOptions = this._getAuthFetchOptions(room);
|
|
714
|
+
if (!fetchOptions)
|
|
715
|
+
return null;
|
|
716
|
+
const { url, options } = fetchOptions;
|
|
717
|
+
const res = yield fetch(url, options);
|
|
718
|
+
if (!res.ok || res.status !== 200) {
|
|
719
|
+
throw new Error("Room is unauthorized");
|
|
720
|
+
}
|
|
721
|
+
try {
|
|
722
|
+
return yield res.text().then((text2) => text2.trim());
|
|
723
|
+
} catch (err) {
|
|
724
|
+
throw new Error(
|
|
725
|
+
err instanceof Error ? err.message : "Room is unauthorized"
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
}
|
|
595
730
|
_getWsEndpoint(room) {
|
|
596
731
|
switch (typeof this._endpoints.wsEndpoint) {
|
|
597
732
|
case "undefined":
|
|
@@ -687,28 +822,28 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
687
822
|
});
|
|
688
823
|
}
|
|
689
824
|
_handleStorageReceivedMessage(message) {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
const
|
|
705
|
-
|
|
706
|
-
this.
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
825
|
+
return __async(this, null, function* () {
|
|
826
|
+
const { connectionId } = message;
|
|
827
|
+
if (!connectionId)
|
|
828
|
+
return;
|
|
829
|
+
if (!this._state.webSocket)
|
|
830
|
+
throw new Error("Could not find WebSocket");
|
|
831
|
+
const data = message.data;
|
|
832
|
+
this._crdtManager.initialize({
|
|
833
|
+
onInitialized: () => __async(this, null, function* () {
|
|
834
|
+
this._observeCrdt();
|
|
835
|
+
}),
|
|
836
|
+
origin: Y_ORIGIN_INITIALIZED,
|
|
837
|
+
update: data.state
|
|
838
|
+
});
|
|
839
|
+
const encodedState = this._crdtManager.doc.encodeStateAsUpdate();
|
|
840
|
+
yield this._storageStore.addUpdate(encodedState);
|
|
841
|
+
this._emitSharedTypes();
|
|
842
|
+
const update = this._crdtManager.doc.encodeStateAsUpdate();
|
|
843
|
+
this._sendMessage({
|
|
844
|
+
type: "$UPDATE_STORAGE",
|
|
845
|
+
data: { origin: Y_ORIGIN_INITIALIZED, update }
|
|
846
|
+
});
|
|
712
847
|
});
|
|
713
848
|
}
|
|
714
849
|
_handleStorageUpdatedMessage(message) {
|
|
@@ -722,14 +857,11 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
722
857
|
return;
|
|
723
858
|
this._crdtManager.doc.applyUpdate(data.state, "$STORAGE_UPDATED");
|
|
724
859
|
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
725
|
-
Object.
|
|
860
|
+
Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
|
|
726
861
|
if (!this._crdtManager)
|
|
727
862
|
return;
|
|
728
|
-
const sharedType = sharedTypes[key];
|
|
729
|
-
if (!sharedType)
|
|
730
|
-
return;
|
|
731
863
|
const serialized = sharedType.toJSON();
|
|
732
|
-
this._crdtNotifier.subject(
|
|
864
|
+
this._crdtNotifier.subject(prop).next(serialized);
|
|
733
865
|
});
|
|
734
866
|
}
|
|
735
867
|
_handleUserJoinedMessage(message) {
|
|
@@ -777,7 +909,9 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
777
909
|
const unsubscribe = this._crdtManager.doc.subscribe(
|
|
778
910
|
(update, _origin) => {
|
|
779
911
|
const origin = _origin != null ? _origin : null;
|
|
780
|
-
|
|
912
|
+
this._addToStorageStore(update);
|
|
913
|
+
this._emitSharedTypes();
|
|
914
|
+
if (origin === Y_ORIGIN_INITIALIZED || origin === this)
|
|
781
915
|
return;
|
|
782
916
|
if (!this._state.webSocket)
|
|
783
917
|
return;
|
|
@@ -787,13 +921,6 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
787
921
|
return;
|
|
788
922
|
if (origin === "$STORAGE_UPDATED")
|
|
789
923
|
return;
|
|
790
|
-
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
791
|
-
Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
|
|
792
|
-
if (!this._crdtManager)
|
|
793
|
-
return;
|
|
794
|
-
const serialized = sharedType.toJSON();
|
|
795
|
-
this._crdtNotifier.subject(prop).next(serialized);
|
|
796
|
-
});
|
|
797
924
|
this._sendMessage({
|
|
798
925
|
type: "$UPDATE_STORAGE",
|
|
799
926
|
data: { origin, update }
|
|
@@ -818,14 +945,18 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
818
945
|
* @date August 19, 2022
|
|
819
946
|
*/
|
|
820
947
|
_onClose(event) {
|
|
821
|
-
this
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
oldState
|
|
828
|
-
|
|
948
|
+
return __async(this, null, function* () {
|
|
949
|
+
this._logDebug("WebSocket closed");
|
|
950
|
+
!!event.reason && this._logDebug(event.reason);
|
|
951
|
+
this._clearInterval(this._intervals.heartbeat);
|
|
952
|
+
this._clearTimeout(this._timeouts.pong);
|
|
953
|
+
this._stateNotifier.subjects["myself"].next(null);
|
|
954
|
+
this._updateState((oldState) => {
|
|
955
|
+
oldState.authorization.token = null;
|
|
956
|
+
oldState.connection.state = "Closed" /* Closed */;
|
|
957
|
+
oldState.webSocket = null;
|
|
958
|
+
return oldState;
|
|
959
|
+
});
|
|
829
960
|
});
|
|
830
961
|
}
|
|
831
962
|
/**
|
|
@@ -998,6 +1129,7 @@ var PluvClient = class {
|
|
|
998
1129
|
if (oldRoom)
|
|
999
1130
|
return oldRoom;
|
|
1000
1131
|
const newRoom = new PluvRoom(room, {
|
|
1132
|
+
addons: options.addons,
|
|
1001
1133
|
authEndpoint: this._authEndpoint,
|
|
1002
1134
|
debug: options.debug,
|
|
1003
1135
|
initialPresence: options.initialPresence,
|
|
@@ -1026,14 +1158,14 @@ var PluvClient = class {
|
|
|
1026
1158
|
this.getRooms = () => {
|
|
1027
1159
|
return Array.from(this._rooms.values());
|
|
1028
1160
|
};
|
|
1029
|
-
this.leave = (room) => {
|
|
1161
|
+
this.leave = (room) => __async(this, null, function* () {
|
|
1030
1162
|
const toLeave = typeof room === "string" ? this.getRoom(room) : room;
|
|
1031
1163
|
if (!toLeave)
|
|
1032
1164
|
return;
|
|
1033
|
-
toLeave.disconnect();
|
|
1165
|
+
yield toLeave.disconnect();
|
|
1034
1166
|
this._rooms.delete(toLeave.id);
|
|
1035
1167
|
this._logDebug(`Left and deleted room: ${room}`);
|
|
1036
|
-
};
|
|
1168
|
+
});
|
|
1037
1169
|
const { authEndpoint, debug = false, wsEndpoint } = options;
|
|
1038
1170
|
this._authEndpoint = authEndpoint;
|
|
1039
1171
|
this._debug = debug;
|
|
@@ -1081,8 +1213,7 @@ var MockedRoom = class extends AbstractRoom {
|
|
|
1081
1213
|
return this._eventNotifier.subscribe(event, callback);
|
|
1082
1214
|
};
|
|
1083
1215
|
this.getMyPresence = () => {
|
|
1084
|
-
|
|
1085
|
-
return (_b = (_a = this._usersManager.myself) == null ? void 0 : _a.presence) != null ? _b : null;
|
|
1216
|
+
return this._usersManager.myPresence;
|
|
1086
1217
|
};
|
|
1087
1218
|
this.getMyself = () => {
|
|
1088
1219
|
return this._usersManager.myself;
|
|
@@ -1180,6 +1311,7 @@ __export(y_exports, {
|
|
|
1180
1311
|
import { array, map, text, object } from "@pluv/crdt-yjs";
|
|
1181
1312
|
export {
|
|
1182
1313
|
AbstractRoom,
|
|
1314
|
+
AbstractStorageStore,
|
|
1183
1315
|
ConnectionState,
|
|
1184
1316
|
MockedRoom,
|
|
1185
1317
|
PluvClient,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "@pluv/io frontend client",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,19 +17,21 @@
|
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
+
"@playwright/test": "^1.35.1",
|
|
21
|
+
"@types/react": "^18.2.13",
|
|
20
22
|
"wonka": "^6.3.2",
|
|
21
|
-
"@pluv/crdt-yjs": "^0.
|
|
22
|
-
"@pluv/types": "^0.2.
|
|
23
|
+
"@pluv/crdt-yjs": "^0.4.0",
|
|
24
|
+
"@pluv/types": "^0.2.1"
|
|
23
25
|
},
|
|
24
26
|
"peerDependencies": {
|
|
25
27
|
"yjs": "^13.0.0"
|
|
26
28
|
},
|
|
27
29
|
"devDependencies": {
|
|
28
|
-
"tsup": "^
|
|
29
|
-
"typescript": "^5.
|
|
30
|
-
"yjs": "^13.6.
|
|
31
|
-
"@pluv/tsconfig": "^0.1.
|
|
32
|
-
"eslint-config-pluv": "^0.1.
|
|
30
|
+
"tsup": "^7.0.0",
|
|
31
|
+
"typescript": "^5.1.3",
|
|
32
|
+
"yjs": "^13.6.4",
|
|
33
|
+
"@pluv/tsconfig": "^0.1.7",
|
|
34
|
+
"eslint-config-pluv": "^0.1.13"
|
|
33
35
|
},
|
|
34
36
|
"scripts": {
|
|
35
37
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
package/src/AbstractRoom.ts
CHANGED
|
@@ -38,7 +38,7 @@ export abstract class AbstractRoom<
|
|
|
38
38
|
|
|
39
39
|
public abstract getConnection(): WebSocketConnection;
|
|
40
40
|
|
|
41
|
-
public abstract getMyPresence(): TPresence
|
|
41
|
+
public abstract getMyPresence(): TPresence;
|
|
42
42
|
|
|
43
43
|
public abstract getMyself(): Id<UserInfo<TIO, TPresence>> | null;
|
|
44
44
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { MaybePromise } from "@pluv/types";
|
|
2
|
+
|
|
3
|
+
export abstract class AbstractStorageStore {
|
|
4
|
+
public room: string;
|
|
5
|
+
|
|
6
|
+
constructor(room: string) {
|
|
7
|
+
this.room = room;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public abstract addUpdate(update: string): Promise<void>;
|
|
11
|
+
|
|
12
|
+
public abstract destroy(): Promise<void>;
|
|
13
|
+
|
|
14
|
+
public abstract flatten(encodedState: string): Promise<void>;
|
|
15
|
+
|
|
16
|
+
public abstract getShouldFlatten(): Promise<boolean>;
|
|
17
|
+
|
|
18
|
+
public abstract getUpdates(start?: number): Promise<readonly string[]>;
|
|
19
|
+
|
|
20
|
+
public abstract initialize(): Promise<void>;
|
|
21
|
+
}
|
package/src/CrdtManager.ts
CHANGED
|
@@ -9,8 +9,15 @@ export interface CrdtManagerOptions<
|
|
|
9
9
|
initialStorage?: () => TStorage;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
interface CrdtManagerInitializeParams {
|
|
13
|
+
onInitialized?: () => void;
|
|
14
|
+
origin?: any;
|
|
15
|
+
update: string | readonly string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
12
18
|
export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
|
|
13
19
|
public doc: YjsDoc<TStorage>;
|
|
20
|
+
public initialized: boolean = false;
|
|
14
21
|
|
|
15
22
|
constructor(options: CrdtManagerOptions<TStorage> = {}) {
|
|
16
23
|
const { encodedState, initialStorage } = options;
|
|
@@ -30,6 +37,23 @@ export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
|
|
|
30
37
|
this.doc = doc<TStorage>(initialStorage?.());
|
|
31
38
|
}
|
|
32
39
|
|
|
40
|
+
/**
|
|
41
|
+
* !HACK
|
|
42
|
+
* @description If this is called before the doc is initialized. Omit the
|
|
43
|
+
* update. This unfortunately means updates can be dropped entirely
|
|
44
|
+
* @date June 13, 2023
|
|
45
|
+
*/
|
|
46
|
+
public applyUpdate(update: string | readonly string[], origin?: any): this {
|
|
47
|
+
if (!this.initialized) return this;
|
|
48
|
+
|
|
49
|
+
const updates: readonly string[] =
|
|
50
|
+
typeof update === "string" ? [update] : update;
|
|
51
|
+
|
|
52
|
+
this._applyDocUpdates(this.doc, updates, origin);
|
|
53
|
+
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
33
57
|
public destroy(): void {
|
|
34
58
|
this.doc.destroy();
|
|
35
59
|
}
|
|
@@ -37,4 +61,44 @@ export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
|
|
|
37
61
|
public get<TKey extends keyof TStorage>(key: TKey): TStorage[TKey] {
|
|
38
62
|
return this.doc.get(key);
|
|
39
63
|
}
|
|
64
|
+
|
|
65
|
+
public initialize(params: CrdtManagerInitializeParams): this {
|
|
66
|
+
const { onInitialized, origin, update } = params;
|
|
67
|
+
|
|
68
|
+
const updates = typeof update === "string" ? [update] : update;
|
|
69
|
+
|
|
70
|
+
if (!updates.length) return this;
|
|
71
|
+
|
|
72
|
+
if (this.initialized) {
|
|
73
|
+
this._applyDocUpdates(this.doc, updates, origin);
|
|
74
|
+
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const _doc = this._applyDocUpdates(doc<TStorage>(), updates, origin);
|
|
79
|
+
|
|
80
|
+
this.initialized = true;
|
|
81
|
+
|
|
82
|
+
if (!!Object.keys(_doc.toJSON()).length) {
|
|
83
|
+
this.doc = _doc;
|
|
84
|
+
} else {
|
|
85
|
+
_doc.destroy();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
onInitialized?.();
|
|
89
|
+
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private _applyDocUpdates(
|
|
94
|
+
yjsDoc: YjsDoc<TStorage>,
|
|
95
|
+
updates: readonly string[],
|
|
96
|
+
origin?: string
|
|
97
|
+
): YjsDoc<TStorage> {
|
|
98
|
+
yjsDoc.transact(() => {
|
|
99
|
+
updates.reduce((acc, _update) => acc.applyUpdate(_update), yjsDoc);
|
|
100
|
+
}, origin);
|
|
101
|
+
|
|
102
|
+
return yjsDoc;
|
|
103
|
+
}
|
|
40
104
|
}
|
package/src/MockedRoom.ts
CHANGED
|
@@ -131,8 +131,8 @@ export class MockedRoom<
|
|
|
131
131
|
);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
public getMyPresence = (): TPresence
|
|
135
|
-
return this._usersManager.
|
|
134
|
+
public getMyPresence = (): TPresence => {
|
|
135
|
+
return this._usersManager.myPresence;
|
|
136
136
|
};
|
|
137
137
|
|
|
138
138
|
public getMyself = (): Id<UserInfo<TIO, TPresence>> | null => {
|