@pluv/client 0.11.1 → 0.12.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/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +254 -0
- package/dist/index.d.mts +31 -8
- package/dist/index.d.ts +31 -8
- package/dist/index.js +150 -36
- package/dist/index.mjs +150 -36
- package/package.json +7 -7
- package/src/AbstractRoom.ts +29 -10
- package/src/AbstractStorageStore.ts +0 -2
- package/src/CrdtManager.ts +33 -10
- package/src/CrdtNotifier.ts +14 -2
- package/src/EventNotifier.ts +3 -3
- package/src/MockedRoom.ts +85 -22
- package/src/OtherNotifier.ts +3 -3
- package/src/PluvClient.ts +6 -6
- package/src/PluvRoom.ts +121 -26
- package/src/StateNotifier.ts +6 -6
- package/src/UsersManager.ts +4 -4
- package/src/createClient.ts +1 -1
- package/src/index.ts +4 -1
- package/src/types.ts +1 -1
package/dist/index.mjs
CHANGED
|
@@ -61,14 +61,21 @@ import { doc } from "@pluv/crdt-yjs";
|
|
|
61
61
|
var CrdtManager = class {
|
|
62
62
|
constructor(options = {}) {
|
|
63
63
|
this.initialized = false;
|
|
64
|
-
const {
|
|
64
|
+
const {
|
|
65
|
+
captureTimeout,
|
|
66
|
+
encodedState,
|
|
67
|
+
initialStorage,
|
|
68
|
+
trackedOrigins = []
|
|
69
|
+
} = options;
|
|
65
70
|
const _doc = encodedState ? doc().applyUpdate(encodedState) : null;
|
|
66
71
|
if (_doc && !!Object.keys(_doc.toJSON()).length) {
|
|
67
72
|
this.doc = _doc;
|
|
73
|
+
this.trackOrigins({ captureTimeout, trackedOrigins });
|
|
68
74
|
return;
|
|
69
75
|
}
|
|
70
76
|
_doc == null ? void 0 : _doc.destroy();
|
|
71
77
|
this.doc = doc(initialStorage == null ? void 0 : initialStorage());
|
|
78
|
+
this.trackOrigins({ captureTimeout, trackedOrigins });
|
|
72
79
|
}
|
|
73
80
|
/**
|
|
74
81
|
* !HACK
|
|
@@ -90,8 +97,15 @@ var CrdtManager = class {
|
|
|
90
97
|
return this.doc.get(key);
|
|
91
98
|
}
|
|
92
99
|
initialize(params) {
|
|
93
|
-
const {
|
|
100
|
+
const {
|
|
101
|
+
captureTimeout,
|
|
102
|
+
onInitialized,
|
|
103
|
+
origin,
|
|
104
|
+
trackedOrigins,
|
|
105
|
+
update
|
|
106
|
+
} = params;
|
|
94
107
|
const updates = typeof update === "string" ? [update] : update;
|
|
108
|
+
this.trackOrigins({ captureTimeout, trackedOrigins });
|
|
95
109
|
if (!updates.length)
|
|
96
110
|
return this;
|
|
97
111
|
if (this.initialized) {
|
|
@@ -106,8 +120,13 @@ var CrdtManager = class {
|
|
|
106
120
|
_doc.destroy();
|
|
107
121
|
}
|
|
108
122
|
onInitialized == null ? void 0 : onInitialized();
|
|
123
|
+
this.trackOrigins({ captureTimeout, trackedOrigins });
|
|
109
124
|
return this;
|
|
110
125
|
}
|
|
126
|
+
trackOrigins(options) {
|
|
127
|
+
const { captureTimeout, trackedOrigins } = options;
|
|
128
|
+
this.doc.trackOrigins({ captureTimeout, trackedOrigins });
|
|
129
|
+
}
|
|
111
130
|
_applyDocUpdates(yjsDoc, updates, origin) {
|
|
112
131
|
yjsDoc.transact(() => {
|
|
113
132
|
updates.reduce((acc, _update) => acc.applyUpdate(_update), yjsDoc);
|
|
@@ -120,6 +139,7 @@ var CrdtManager = class {
|
|
|
120
139
|
import { makeSubject, subscribe } from "wonka";
|
|
121
140
|
var CrdtNotifier = class {
|
|
122
141
|
constructor() {
|
|
142
|
+
this.rootSubject = makeSubject();
|
|
123
143
|
this.subjects = /* @__PURE__ */ new Map();
|
|
124
144
|
}
|
|
125
145
|
subject(key) {
|
|
@@ -131,6 +151,9 @@ var CrdtNotifier = class {
|
|
|
131
151
|
this.subjects.set(key, newSubject);
|
|
132
152
|
return newSubject;
|
|
133
153
|
}
|
|
154
|
+
subcribeRoot(callback) {
|
|
155
|
+
return subscribe(callback)(this.rootSubject.source).unsubscribe;
|
|
156
|
+
}
|
|
134
157
|
subscribe(key, callback) {
|
|
135
158
|
const subject = this.subject(key);
|
|
136
159
|
return subscribe(callback)(subject.source).unsubscribe;
|
|
@@ -353,7 +376,8 @@ var ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1e3;
|
|
|
353
376
|
var HEARTBEAT_INTERVAL_MS = 1e4;
|
|
354
377
|
var PONG_TIMEOUT_MS = 2e3;
|
|
355
378
|
var RECONNECT_TIMEOUT_MS = 3e4;
|
|
356
|
-
var
|
|
379
|
+
var ORIGIN_INITIALIZED = "$INITIALIZED";
|
|
380
|
+
var ORIGIN_STORAGE_UPDATED = "$STORAGE_UPDATED";
|
|
357
381
|
var DEFAULT_PLUV_CLIENT_ADDON = (input) => ({
|
|
358
382
|
storage: new StorageStore(input.room.id)
|
|
359
383
|
});
|
|
@@ -362,14 +386,17 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
362
386
|
const {
|
|
363
387
|
addons = [],
|
|
364
388
|
authEndpoint,
|
|
389
|
+
captureTimeout,
|
|
365
390
|
debug = false,
|
|
366
391
|
initialPresence,
|
|
367
392
|
initialStorage,
|
|
368
393
|
onAuthorizationFail,
|
|
369
394
|
presence,
|
|
395
|
+
trackedOrigins,
|
|
370
396
|
wsEndpoint
|
|
371
397
|
} = options;
|
|
372
398
|
super(room);
|
|
399
|
+
this._captureTimeout = null;
|
|
373
400
|
this._crdtNotifier = new CrdtNotifier();
|
|
374
401
|
this._eventNotifier = new EventNotifier();
|
|
375
402
|
this._intervals = {
|
|
@@ -396,8 +423,15 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
396
423
|
pong: null,
|
|
397
424
|
reconnect: null
|
|
398
425
|
};
|
|
426
|
+
this._trackedOrigins = null;
|
|
399
427
|
this._windowListeners = null;
|
|
400
428
|
this._wsListeners = null;
|
|
429
|
+
this.canRedo = () => {
|
|
430
|
+
return !!this._crdtManager.doc.canRedo();
|
|
431
|
+
};
|
|
432
|
+
this.canUndo = () => {
|
|
433
|
+
return !!this._crdtManager.doc.canUndo();
|
|
434
|
+
};
|
|
401
435
|
this.event = (event, callback) => {
|
|
402
436
|
return this._eventNotifier.subscribe(event, callback);
|
|
403
437
|
};
|
|
@@ -414,18 +448,38 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
414
448
|
return this._usersManager.getOthers();
|
|
415
449
|
};
|
|
416
450
|
this.getStorage = (key) => {
|
|
417
|
-
|
|
418
|
-
|
|
451
|
+
const sharedType = this._crdtManager.get(key);
|
|
452
|
+
if (typeof sharedType === "undefined") {
|
|
453
|
+
throw new Error(`Could not find storege: ${key.toString()}`);
|
|
454
|
+
}
|
|
455
|
+
return sharedType;
|
|
419
456
|
};
|
|
420
457
|
this.other = (connectionId, callback) => {
|
|
421
458
|
return this._otherNotifier.subscribe(connectionId, callback);
|
|
422
459
|
};
|
|
460
|
+
this.redo = () => {
|
|
461
|
+
this._crdtManager.doc.redo();
|
|
462
|
+
};
|
|
423
463
|
this.storage = (key, fn) => {
|
|
424
464
|
return this._crdtNotifier.subscribe(key, fn);
|
|
425
465
|
};
|
|
466
|
+
this.storageRoot = (fn) => {
|
|
467
|
+
return this._crdtNotifier.subcribeRoot(fn);
|
|
468
|
+
};
|
|
426
469
|
this.subscribe = (name, callback) => {
|
|
427
470
|
return this._stateNotifier.subscribe(name, callback);
|
|
428
471
|
};
|
|
472
|
+
this.transact = (fn, origin) => {
|
|
473
|
+
const _origin = origin != null ? origin : this._state.connection.id;
|
|
474
|
+
if (typeof _origin !== "string")
|
|
475
|
+
return;
|
|
476
|
+
this._crdtManager.doc.transact(() => {
|
|
477
|
+
fn(this._crdtManager.doc.storage);
|
|
478
|
+
}, _origin);
|
|
479
|
+
};
|
|
480
|
+
this.undo = () => {
|
|
481
|
+
this._crdtManager.doc.undo();
|
|
482
|
+
};
|
|
429
483
|
this.updateMyPresence = (presence) => {
|
|
430
484
|
var _a;
|
|
431
485
|
this._usersManager.updateMyPresence(presence);
|
|
@@ -475,7 +529,13 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
475
529
|
initialPresence,
|
|
476
530
|
presence
|
|
477
531
|
});
|
|
478
|
-
this._crdtManager = new CrdtManager({
|
|
532
|
+
this._crdtManager = new CrdtManager({
|
|
533
|
+
captureTimeout,
|
|
534
|
+
initialStorage,
|
|
535
|
+
trackedOrigins
|
|
536
|
+
});
|
|
537
|
+
this._captureTimeout = captureTimeout != null ? captureTimeout : null;
|
|
538
|
+
this._trackedOrigins = trackedOrigins != null ? trackedOrigins : null;
|
|
479
539
|
}
|
|
480
540
|
get webSocket() {
|
|
481
541
|
return this._state.webSocket;
|
|
@@ -506,7 +566,6 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
506
566
|
});
|
|
507
567
|
yield this._storageStore.initialize();
|
|
508
568
|
yield this._applyStorageStore();
|
|
509
|
-
console.log(this._getWsEndpoint(this.id));
|
|
510
569
|
const url = new URL(this._getWsEndpoint(this.id));
|
|
511
570
|
let authToken = null;
|
|
512
571
|
let webSocket;
|
|
@@ -530,7 +589,6 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
530
589
|
}
|
|
531
590
|
disconnect() {
|
|
532
591
|
return __async(this, null, function* () {
|
|
533
|
-
var _a;
|
|
534
592
|
if (!this._endpoints.wsEndpoint) {
|
|
535
593
|
throw new Error("Must provide an wsEndpoint.");
|
|
536
594
|
}
|
|
@@ -545,7 +603,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
545
603
|
this._clearInterval(this._intervals.heartbeat);
|
|
546
604
|
this._closeWs();
|
|
547
605
|
yield this._storageStore.destroy();
|
|
548
|
-
|
|
606
|
+
this._crdtManager.destroy();
|
|
549
607
|
this._stateNotifier.subjects["my-presence"].next(null);
|
|
550
608
|
});
|
|
551
609
|
}
|
|
@@ -562,12 +620,16 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
562
620
|
}
|
|
563
621
|
_applyStorageStore() {
|
|
564
622
|
return __async(this, null, function* () {
|
|
623
|
+
var _a;
|
|
565
624
|
const updates = yield this._storageStore.getUpdates();
|
|
625
|
+
const trackedOrigins = this._getTrackedOrigins();
|
|
566
626
|
this._crdtManager.initialize({
|
|
627
|
+
captureTimeout: (_a = this._captureTimeout) != null ? _a : void 0,
|
|
567
628
|
onInitialized: () => {
|
|
568
629
|
this._observeCrdt();
|
|
569
630
|
},
|
|
570
|
-
origin:
|
|
631
|
+
origin: ORIGIN_INITIALIZED,
|
|
632
|
+
trackedOrigins: trackedOrigins != null ? trackedOrigins : void 0,
|
|
571
633
|
update: updates
|
|
572
634
|
});
|
|
573
635
|
this._emitSharedTypes();
|
|
@@ -686,10 +748,15 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
686
748
|
}
|
|
687
749
|
_emitSharedTypes() {
|
|
688
750
|
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
689
|
-
Object.entries(sharedTypes).
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
751
|
+
const storageRoot = Object.entries(sharedTypes).reduce(
|
|
752
|
+
(acc, [prop, sharedType]) => {
|
|
753
|
+
const serialized = sharedType.toJSON();
|
|
754
|
+
this._crdtNotifier.subject(prop).next(serialized);
|
|
755
|
+
return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
|
|
756
|
+
},
|
|
757
|
+
{}
|
|
758
|
+
);
|
|
759
|
+
this._crdtNotifier.rootSubject.next(storageRoot);
|
|
693
760
|
}
|
|
694
761
|
_getAuthFetchOptions(room) {
|
|
695
762
|
if (typeof this._endpoints.authEndpoint === "undefined")
|
|
@@ -728,6 +795,11 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
728
795
|
}
|
|
729
796
|
});
|
|
730
797
|
}
|
|
798
|
+
_getTrackedOrigins() {
|
|
799
|
+
var _a;
|
|
800
|
+
const connectionId = this._state.connection.id;
|
|
801
|
+
return !connectionId ? this._trackedOrigins : [connectionId, ...(_a = this._trackedOrigins) != null ? _a : []];
|
|
802
|
+
}
|
|
731
803
|
_getWsEndpoint(room) {
|
|
732
804
|
switch (typeof this._endpoints.wsEndpoint) {
|
|
733
805
|
case "undefined":
|
|
@@ -796,7 +868,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
796
868
|
this._stateNotifier.subjects["others"].next(others);
|
|
797
869
|
}
|
|
798
870
|
_handleRegisteredMessage(message) {
|
|
799
|
-
var _a, _b
|
|
871
|
+
var _a, _b;
|
|
800
872
|
const { connectionId, user } = message;
|
|
801
873
|
if (!connectionId)
|
|
802
874
|
return;
|
|
@@ -816,7 +888,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
816
888
|
const myself = (_a = this._usersManager.myself) != null ? _a : null;
|
|
817
889
|
this._stateNotifier.subjects["my-presence"].next(presence);
|
|
818
890
|
this._stateNotifier.subjects["myself"].next(myself);
|
|
819
|
-
const update = this._state.connection.count > 1 ? (
|
|
891
|
+
const update = this._state.connection.count > 1 ? (_b = this._crdtManager.doc.encodeStateAsUpdate()) != null ? _b : null : null;
|
|
820
892
|
this._sendMessage({
|
|
821
893
|
type: "$INITIALIZE_SESSION",
|
|
822
894
|
data: { presence, update }
|
|
@@ -824,17 +896,21 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
824
896
|
}
|
|
825
897
|
_handleStorageReceivedMessage(message) {
|
|
826
898
|
return __async(this, null, function* () {
|
|
899
|
+
var _a;
|
|
827
900
|
const { connectionId } = message;
|
|
828
901
|
if (!connectionId)
|
|
829
902
|
return;
|
|
830
903
|
if (!this._state.webSocket)
|
|
831
904
|
throw new Error("Could not find WebSocket");
|
|
832
905
|
const data = message.data;
|
|
906
|
+
const trackedOrigins = this._getTrackedOrigins();
|
|
833
907
|
this._crdtManager.initialize({
|
|
908
|
+
captureTimeout: (_a = this._captureTimeout) != null ? _a : void 0,
|
|
834
909
|
onInitialized: () => __async(this, null, function* () {
|
|
835
910
|
this._observeCrdt();
|
|
836
911
|
}),
|
|
837
|
-
origin:
|
|
912
|
+
origin: ORIGIN_INITIALIZED,
|
|
913
|
+
trackedOrigins: trackedOrigins != null ? trackedOrigins : void 0,
|
|
838
914
|
update: data.state
|
|
839
915
|
});
|
|
840
916
|
const encodedState = this._crdtManager.doc.encodeStateAsUpdate();
|
|
@@ -843,7 +919,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
843
919
|
const update = this._crdtManager.doc.encodeStateAsUpdate();
|
|
844
920
|
this._sendMessage({
|
|
845
921
|
type: "$UPDATE_STORAGE",
|
|
846
|
-
data: { origin:
|
|
922
|
+
data: { origin: ORIGIN_INITIALIZED, update }
|
|
847
923
|
});
|
|
848
924
|
});
|
|
849
925
|
}
|
|
@@ -856,14 +932,19 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
856
932
|
const data = message.data;
|
|
857
933
|
if (!this._crdtManager)
|
|
858
934
|
return;
|
|
859
|
-
this._crdtManager.doc.applyUpdate(data.state,
|
|
935
|
+
this._crdtManager.doc.applyUpdate(data.state, ORIGIN_STORAGE_UPDATED);
|
|
860
936
|
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
861
|
-
Object.entries(sharedTypes).
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
937
|
+
const storageRoot = Object.entries(sharedTypes).reduce(
|
|
938
|
+
(acc, [prop, sharedType]) => {
|
|
939
|
+
if (!this._crdtManager)
|
|
940
|
+
return acc;
|
|
941
|
+
const serialized = sharedType.toJSON();
|
|
942
|
+
this._crdtNotifier.subject(prop).next(serialized);
|
|
943
|
+
return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
|
|
944
|
+
},
|
|
945
|
+
{}
|
|
946
|
+
);
|
|
947
|
+
this._crdtNotifier.rootSubject.next(storageRoot);
|
|
867
948
|
}
|
|
868
949
|
_handleUserJoinedMessage(message) {
|
|
869
950
|
const { connectionId } = message;
|
|
@@ -912,7 +993,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
912
993
|
const origin = _origin != null ? _origin : null;
|
|
913
994
|
this._addToStorageStore(update);
|
|
914
995
|
this._emitSharedTypes();
|
|
915
|
-
if (origin ===
|
|
996
|
+
if (origin === ORIGIN_INITIALIZED || origin === this)
|
|
916
997
|
return;
|
|
917
998
|
if (!this._state.webSocket)
|
|
918
999
|
return;
|
|
@@ -920,7 +1001,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
920
1001
|
return;
|
|
921
1002
|
if (this._state.webSocket.readyState !== WebSocket.OPEN)
|
|
922
1003
|
return;
|
|
923
|
-
if (origin ===
|
|
1004
|
+
if (origin === ORIGIN_STORAGE_UPDATED)
|
|
924
1005
|
return;
|
|
925
1006
|
this._sendMessage({
|
|
926
1007
|
type: "$UPDATE_STORAGE",
|
|
@@ -1190,7 +1271,6 @@ var MockedRoom = class extends AbstractRoom {
|
|
|
1190
1271
|
constructor(room, options) {
|
|
1191
1272
|
const { events, initialPresence, initialStorage, presence } = options;
|
|
1192
1273
|
super(room);
|
|
1193
|
-
this._crdtManager = null;
|
|
1194
1274
|
this._crdtNotifier = new CrdtNotifier();
|
|
1195
1275
|
this._eventNotifier = new EventNotifier();
|
|
1196
1276
|
this._otherNotifier = new OtherNotifier();
|
|
@@ -1210,6 +1290,12 @@ var MockedRoom = class extends AbstractRoom {
|
|
|
1210
1290
|
this._subscriptions = {
|
|
1211
1291
|
observeCrdt: null
|
|
1212
1292
|
};
|
|
1293
|
+
this.canRedo = () => {
|
|
1294
|
+
return !!this._crdtManager.doc.canRedo();
|
|
1295
|
+
};
|
|
1296
|
+
this.canUndo = () => {
|
|
1297
|
+
return !!this._crdtManager.doc.canUndo();
|
|
1298
|
+
};
|
|
1213
1299
|
this.event = (event, callback) => {
|
|
1214
1300
|
return this._eventNotifier.subscribe(event, callback);
|
|
1215
1301
|
};
|
|
@@ -1226,18 +1312,41 @@ var MockedRoom = class extends AbstractRoom {
|
|
|
1226
1312
|
return this._usersManager.getOthers();
|
|
1227
1313
|
};
|
|
1228
1314
|
this.getStorage = (key) => {
|
|
1229
|
-
|
|
1230
|
-
|
|
1315
|
+
const sharedType = this._crdtManager.get(key);
|
|
1316
|
+
if (typeof sharedType === "undefined") {
|
|
1317
|
+
throw new Error(`Could not find storege: ${key.toString()}`);
|
|
1318
|
+
}
|
|
1319
|
+
return sharedType;
|
|
1231
1320
|
};
|
|
1232
1321
|
this.other = (connectionId, callback) => {
|
|
1233
1322
|
return this._otherNotifier.subscribe(connectionId, callback);
|
|
1234
1323
|
};
|
|
1324
|
+
this.redo = () => {
|
|
1325
|
+
this._crdtManager.doc.redo();
|
|
1326
|
+
};
|
|
1235
1327
|
this.storage = (key, fn) => {
|
|
1236
1328
|
return this._crdtNotifier.subscribe(key, fn);
|
|
1237
1329
|
};
|
|
1330
|
+
this.storageRoot = (fn) => {
|
|
1331
|
+
return this._crdtNotifier.subcribeRoot(fn);
|
|
1332
|
+
};
|
|
1238
1333
|
this.subscribe = (name, callback) => {
|
|
1239
1334
|
return this._stateNotifier.subscribe(name, callback);
|
|
1240
1335
|
};
|
|
1336
|
+
this.transact = (fn, origin) => {
|
|
1337
|
+
const _origin = origin != null ? origin : this._state.connection.id;
|
|
1338
|
+
const crdtManager = this._crdtManager;
|
|
1339
|
+
if (typeof _origin !== "string")
|
|
1340
|
+
return;
|
|
1341
|
+
if (!crdtManager)
|
|
1342
|
+
return;
|
|
1343
|
+
crdtManager.doc.transact(() => {
|
|
1344
|
+
fn(crdtManager.doc.storage);
|
|
1345
|
+
}, _origin);
|
|
1346
|
+
};
|
|
1347
|
+
this.undo = () => {
|
|
1348
|
+
this._crdtManager.doc.undo();
|
|
1349
|
+
};
|
|
1241
1350
|
this.updateMyPresence = (presence) => {
|
|
1242
1351
|
var _a;
|
|
1243
1352
|
this._usersManager.updateMyPresence(presence);
|
|
@@ -1289,12 +1398,17 @@ var MockedRoom = class extends AbstractRoom {
|
|
|
1289
1398
|
if (origin === "$STORAGE_UPDATED")
|
|
1290
1399
|
return;
|
|
1291
1400
|
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
1292
|
-
Object.entries(sharedTypes).
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1401
|
+
const storageRoot = Object.entries(sharedTypes).reduce(
|
|
1402
|
+
(acc, [prop, sharedType]) => {
|
|
1403
|
+
if (!this._crdtManager)
|
|
1404
|
+
return acc;
|
|
1405
|
+
const serialized = sharedType.toJSON();
|
|
1406
|
+
this._crdtNotifier.subject(prop).next(serialized);
|
|
1407
|
+
return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
|
|
1408
|
+
},
|
|
1409
|
+
{}
|
|
1410
|
+
);
|
|
1411
|
+
this._crdtNotifier.rootSubject.next(storageRoot);
|
|
1298
1412
|
}
|
|
1299
1413
|
);
|
|
1300
1414
|
this._subscriptions.observeCrdt = unsubscribe;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "@pluv/io frontend client",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,19 +18,19 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"wonka": "^6.3.4",
|
|
21
|
-
"@pluv/crdt-yjs": "^0.
|
|
22
|
-
"@pluv/types": "^0.
|
|
21
|
+
"@pluv/crdt-yjs": "^0.12.1",
|
|
22
|
+
"@pluv/types": "^0.12.1"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
25
|
"yjs": "^13.0.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"eslint": "^8.
|
|
28
|
+
"eslint": "^8.50.0",
|
|
29
29
|
"tsup": "^7.2.0",
|
|
30
30
|
"typescript": "^5.2.2",
|
|
31
|
-
"yjs": "^13.6.
|
|
32
|
-
"@pluv/tsconfig": "^0.
|
|
33
|
-
"eslint-config-pluv": "^0.
|
|
31
|
+
"yjs": "^13.6.8",
|
|
32
|
+
"@pluv/tsconfig": "^0.12.1",
|
|
33
|
+
"eslint-config-pluv": "^0.12.1"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
package/src/AbstractRoom.ts
CHANGED
|
@@ -18,7 +18,7 @@ import type { UserInfo, WebSocketConnection } from "./types";
|
|
|
18
18
|
export abstract class AbstractRoom<
|
|
19
19
|
TIO extends IOLike,
|
|
20
20
|
TPresence extends JsonObject = {},
|
|
21
|
-
TStorage extends Record<string, AbstractType<any>> = {}
|
|
21
|
+
TStorage extends Record<string, AbstractType<any>> = {},
|
|
22
22
|
> {
|
|
23
23
|
public readonly id: string;
|
|
24
24
|
|
|
@@ -28,12 +28,16 @@ export abstract class AbstractRoom<
|
|
|
28
28
|
|
|
29
29
|
public abstract broadcast<TEvent extends keyof InferIOInput<TIO>>(
|
|
30
30
|
event: TEvent,
|
|
31
|
-
data: Id<InferIOInput<TIO>[TEvent]
|
|
31
|
+
data: Id<InferIOInput<TIO>[TEvent]>,
|
|
32
32
|
): void;
|
|
33
33
|
|
|
34
|
+
public abstract canRedo(): boolean;
|
|
35
|
+
|
|
36
|
+
public abstract canUndo(): boolean;
|
|
37
|
+
|
|
34
38
|
public abstract event<TEvent extends keyof InferIOOutput<TIO>>(
|
|
35
39
|
event: TEvent,
|
|
36
|
-
callback: EventNotifierSubscriptionCallback<TIO, TEvent
|
|
40
|
+
callback: EventNotifierSubscriptionCallback<TIO, TEvent>,
|
|
37
41
|
): () => void;
|
|
38
42
|
|
|
39
43
|
public abstract getConnection(): WebSocketConnection;
|
|
@@ -43,31 +47,46 @@ export abstract class AbstractRoom<
|
|
|
43
47
|
public abstract getMyself(): Id<UserInfo<TIO, TPresence>> | null;
|
|
44
48
|
|
|
45
49
|
public abstract getOther(
|
|
46
|
-
connectionId: string
|
|
50
|
+
connectionId: string,
|
|
47
51
|
): Id<UserInfo<TIO, TPresence>> | null;
|
|
48
52
|
|
|
49
53
|
public abstract getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
|
|
50
54
|
|
|
51
55
|
public abstract getStorage<TKey extends keyof TStorage>(
|
|
52
|
-
key: TKey
|
|
53
|
-
): TStorage[TKey]
|
|
56
|
+
key: TKey,
|
|
57
|
+
): TStorage[TKey];
|
|
54
58
|
|
|
55
59
|
public abstract other(
|
|
56
60
|
connectionId: string,
|
|
57
|
-
callback: OtherNotifierSubscriptionCallback<TIO
|
|
61
|
+
callback: OtherNotifierSubscriptionCallback<TIO>,
|
|
58
62
|
): () => void;
|
|
59
63
|
|
|
64
|
+
public abstract redo(): void;
|
|
65
|
+
|
|
60
66
|
public abstract storage<TKey extends keyof TStorage>(
|
|
61
67
|
key: TKey,
|
|
62
|
-
fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void
|
|
68
|
+
fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void,
|
|
69
|
+
): () => void;
|
|
70
|
+
|
|
71
|
+
public abstract storageRoot(
|
|
72
|
+
fn: (value: {
|
|
73
|
+
[P in keyof TStorage]: InferYjsSharedTypeJson<TStorage[P]>;
|
|
74
|
+
}) => void,
|
|
63
75
|
): () => void;
|
|
64
76
|
|
|
65
77
|
public abstract subscribe<
|
|
66
|
-
TSubject extends keyof StateNotifierSubjects<TIO, TPresence
|
|
78
|
+
TSubject extends keyof StateNotifierSubjects<TIO, TPresence>,
|
|
67
79
|
>(
|
|
68
80
|
name: TSubject,
|
|
69
|
-
callback: SubscriptionCallback<TIO, TPresence, TSubject
|
|
81
|
+
callback: SubscriptionCallback<TIO, TPresence, TSubject>,
|
|
70
82
|
): () => void;
|
|
71
83
|
|
|
84
|
+
public abstract transact(
|
|
85
|
+
fn: (storage: TStorage) => void,
|
|
86
|
+
origin?: string,
|
|
87
|
+
): void;
|
|
88
|
+
|
|
89
|
+
public abstract undo(): void;
|
|
90
|
+
|
|
72
91
|
public abstract updateMyPresence(presence: Partial<TPresence>): void;
|
|
73
92
|
}
|
package/src/CrdtManager.ts
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
|
-
import type { YjsDoc } from "@pluv/crdt-yjs";
|
|
1
|
+
import type { TrackOriginOptions, YjsDoc } from "@pluv/crdt-yjs";
|
|
2
2
|
import { doc } from "@pluv/crdt-yjs";
|
|
3
3
|
import type { AbstractType } from "yjs";
|
|
4
4
|
|
|
5
|
-
export
|
|
6
|
-
TStorage extends Record<string, AbstractType<any>> = {}
|
|
7
|
-
> {
|
|
5
|
+
export type CrdtManagerOptions<
|
|
6
|
+
TStorage extends Record<string, AbstractType<any>> = {},
|
|
7
|
+
> = {
|
|
8
8
|
encodedState?: string | Uint8Array | null;
|
|
9
9
|
initialStorage?: () => TStorage;
|
|
10
|
-
}
|
|
10
|
+
} & TrackOriginOptions;
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
type CrdtManagerInitializeParams = {
|
|
13
13
|
onInitialized?: () => void;
|
|
14
14
|
origin?: any;
|
|
15
15
|
update: string | readonly string[];
|
|
16
|
-
}
|
|
16
|
+
} & TrackOriginOptions;
|
|
17
17
|
|
|
18
18
|
export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
|
|
19
19
|
public doc: YjsDoc<TStorage>;
|
|
20
20
|
public initialized: boolean = false;
|
|
21
21
|
|
|
22
22
|
constructor(options: CrdtManagerOptions<TStorage> = {}) {
|
|
23
|
-
const {
|
|
23
|
+
const {
|
|
24
|
+
captureTimeout,
|
|
25
|
+
encodedState,
|
|
26
|
+
initialStorage,
|
|
27
|
+
trackedOrigins = [],
|
|
28
|
+
} = options;
|
|
24
29
|
|
|
25
30
|
const _doc = encodedState
|
|
26
31
|
? doc<TStorage>().applyUpdate(encodedState)
|
|
@@ -28,6 +33,7 @@ export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
|
|
|
28
33
|
|
|
29
34
|
if (_doc && !!Object.keys(_doc.toJSON()).length) {
|
|
30
35
|
this.doc = _doc;
|
|
36
|
+
this.trackOrigins({ captureTimeout, trackedOrigins });
|
|
31
37
|
|
|
32
38
|
return;
|
|
33
39
|
}
|
|
@@ -35,6 +41,7 @@ export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
|
|
|
35
41
|
_doc?.destroy();
|
|
36
42
|
|
|
37
43
|
this.doc = doc<TStorage>(initialStorage?.());
|
|
44
|
+
this.trackOrigins({ captureTimeout, trackedOrigins });
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
/**
|
|
@@ -63,10 +70,18 @@ export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
|
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
public initialize(params: CrdtManagerInitializeParams): this {
|
|
66
|
-
const {
|
|
73
|
+
const {
|
|
74
|
+
captureTimeout,
|
|
75
|
+
onInitialized,
|
|
76
|
+
origin,
|
|
77
|
+
trackedOrigins,
|
|
78
|
+
update,
|
|
79
|
+
} = params;
|
|
67
80
|
|
|
68
81
|
const updates = typeof update === "string" ? [update] : update;
|
|
69
82
|
|
|
83
|
+
this.trackOrigins({ captureTimeout, trackedOrigins });
|
|
84
|
+
|
|
70
85
|
if (!updates.length) return this;
|
|
71
86
|
|
|
72
87
|
if (this.initialized) {
|
|
@@ -87,13 +102,21 @@ export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
|
|
|
87
102
|
|
|
88
103
|
onInitialized?.();
|
|
89
104
|
|
|
105
|
+
this.trackOrigins({ captureTimeout, trackedOrigins });
|
|
106
|
+
|
|
90
107
|
return this;
|
|
91
108
|
}
|
|
92
109
|
|
|
110
|
+
public trackOrigins(options: TrackOriginOptions): void {
|
|
111
|
+
const { captureTimeout, trackedOrigins } = options;
|
|
112
|
+
|
|
113
|
+
this.doc.trackOrigins({ captureTimeout, trackedOrigins });
|
|
114
|
+
}
|
|
115
|
+
|
|
93
116
|
private _applyDocUpdates(
|
|
94
117
|
yjsDoc: YjsDoc<TStorage>,
|
|
95
118
|
updates: readonly string[],
|
|
96
|
-
origin?: string
|
|
119
|
+
origin?: string,
|
|
97
120
|
): YjsDoc<TStorage> {
|
|
98
121
|
yjsDoc.transact(() => {
|
|
99
122
|
updates.reduce((acc, _update) => acc.applyUpdate(_update), yjsDoc);
|
package/src/CrdtNotifier.ts
CHANGED
|
@@ -3,13 +3,17 @@ import { makeSubject, Subject, subscribe } from "wonka";
|
|
|
3
3
|
import type { AbstractType } from "yjs";
|
|
4
4
|
|
|
5
5
|
export class CrdtNotifier<TStorage extends Record<string, AbstractType<any>>> {
|
|
6
|
+
public rootSubject = makeSubject<{
|
|
7
|
+
[P in keyof TStorage]: InferYjsSharedTypeJson<TStorage[P]>;
|
|
8
|
+
}>();
|
|
9
|
+
|
|
6
10
|
public subjects = new Map<
|
|
7
11
|
keyof TStorage,
|
|
8
12
|
Subject<InferYjsSharedTypeJson<TStorage[keyof TStorage]>> | null
|
|
9
13
|
>();
|
|
10
14
|
|
|
11
15
|
public subject<TKey extends keyof TStorage>(
|
|
12
|
-
key: TKey
|
|
16
|
+
key: TKey,
|
|
13
17
|
): Subject<InferYjsSharedTypeJson<TStorage[TKey]>> {
|
|
14
18
|
const subject = this.subjects.get(key);
|
|
15
19
|
|
|
@@ -25,9 +29,17 @@ export class CrdtNotifier<TStorage extends Record<string, AbstractType<any>>> {
|
|
|
25
29
|
return newSubject as Subject<InferYjsSharedTypeJson<TStorage[TKey]>>;
|
|
26
30
|
}
|
|
27
31
|
|
|
32
|
+
public subcribeRoot(
|
|
33
|
+
callback: (value: {
|
|
34
|
+
[P in keyof TStorage]: InferYjsSharedTypeJson<TStorage[P]>;
|
|
35
|
+
}) => void,
|
|
36
|
+
): () => void {
|
|
37
|
+
return subscribe(callback)(this.rootSubject.source).unsubscribe;
|
|
38
|
+
}
|
|
39
|
+
|
|
28
40
|
public subscribe<TKey extends keyof TStorage>(
|
|
29
41
|
key: TKey,
|
|
30
|
-
callback: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void
|
|
42
|
+
callback: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void,
|
|
31
43
|
): () => void {
|
|
32
44
|
const subject = this.subject(key);
|
|
33
45
|
|