@pluv/client 0.11.1 → 0.12.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 +246 -0
- package/dist/index.d.mts +28 -5
- package/dist/index.d.ts +28 -5
- package/dist/index.js +141 -27
- package/dist/index.mjs +141 -27
- package/package.json +7 -7
- package/src/AbstractRoom.ts +28 -9
- 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 +76 -19
- package/src/OtherNotifier.ts +3 -3
- package/src/PluvClient.ts +6 -6
- package/src/PluvRoom.ts +111 -22
- 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
|
};
|
|
@@ -420,12 +454,29 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
420
454
|
this.other = (connectionId, callback) => {
|
|
421
455
|
return this._otherNotifier.subscribe(connectionId, callback);
|
|
422
456
|
};
|
|
457
|
+
this.redo = () => {
|
|
458
|
+
this._crdtManager.doc.redo();
|
|
459
|
+
};
|
|
423
460
|
this.storage = (key, fn) => {
|
|
424
461
|
return this._crdtNotifier.subscribe(key, fn);
|
|
425
462
|
};
|
|
463
|
+
this.storageRoot = (fn) => {
|
|
464
|
+
return this._crdtNotifier.subcribeRoot(fn);
|
|
465
|
+
};
|
|
426
466
|
this.subscribe = (name, callback) => {
|
|
427
467
|
return this._stateNotifier.subscribe(name, callback);
|
|
428
468
|
};
|
|
469
|
+
this.transact = (fn, origin) => {
|
|
470
|
+
const _origin = origin != null ? origin : this._state.connection.id;
|
|
471
|
+
if (typeof _origin !== "string")
|
|
472
|
+
return;
|
|
473
|
+
this._crdtManager.doc.transact(() => {
|
|
474
|
+
fn(this._crdtManager.doc.storage);
|
|
475
|
+
}, _origin);
|
|
476
|
+
};
|
|
477
|
+
this.undo = () => {
|
|
478
|
+
this._crdtManager.doc.undo();
|
|
479
|
+
};
|
|
429
480
|
this.updateMyPresence = (presence) => {
|
|
430
481
|
var _a;
|
|
431
482
|
this._usersManager.updateMyPresence(presence);
|
|
@@ -475,7 +526,13 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
475
526
|
initialPresence,
|
|
476
527
|
presence
|
|
477
528
|
});
|
|
478
|
-
this._crdtManager = new CrdtManager({
|
|
529
|
+
this._crdtManager = new CrdtManager({
|
|
530
|
+
captureTimeout,
|
|
531
|
+
initialStorage,
|
|
532
|
+
trackedOrigins
|
|
533
|
+
});
|
|
534
|
+
this._captureTimeout = captureTimeout != null ? captureTimeout : null;
|
|
535
|
+
this._trackedOrigins = trackedOrigins != null ? trackedOrigins : null;
|
|
479
536
|
}
|
|
480
537
|
get webSocket() {
|
|
481
538
|
return this._state.webSocket;
|
|
@@ -506,7 +563,6 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
506
563
|
});
|
|
507
564
|
yield this._storageStore.initialize();
|
|
508
565
|
yield this._applyStorageStore();
|
|
509
|
-
console.log(this._getWsEndpoint(this.id));
|
|
510
566
|
const url = new URL(this._getWsEndpoint(this.id));
|
|
511
567
|
let authToken = null;
|
|
512
568
|
let webSocket;
|
|
@@ -562,12 +618,16 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
562
618
|
}
|
|
563
619
|
_applyStorageStore() {
|
|
564
620
|
return __async(this, null, function* () {
|
|
621
|
+
var _a;
|
|
565
622
|
const updates = yield this._storageStore.getUpdates();
|
|
623
|
+
const trackedOrigins = this._getTrackedOrigins();
|
|
566
624
|
this._crdtManager.initialize({
|
|
625
|
+
captureTimeout: (_a = this._captureTimeout) != null ? _a : void 0,
|
|
567
626
|
onInitialized: () => {
|
|
568
627
|
this._observeCrdt();
|
|
569
628
|
},
|
|
570
|
-
origin:
|
|
629
|
+
origin: ORIGIN_INITIALIZED,
|
|
630
|
+
trackedOrigins: trackedOrigins != null ? trackedOrigins : void 0,
|
|
571
631
|
update: updates
|
|
572
632
|
});
|
|
573
633
|
this._emitSharedTypes();
|
|
@@ -686,10 +746,15 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
686
746
|
}
|
|
687
747
|
_emitSharedTypes() {
|
|
688
748
|
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
689
|
-
Object.entries(sharedTypes).
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
749
|
+
const storageRoot = Object.entries(sharedTypes).reduce(
|
|
750
|
+
(acc, [prop, sharedType]) => {
|
|
751
|
+
const serialized = sharedType.toJSON();
|
|
752
|
+
this._crdtNotifier.subject(prop).next(serialized);
|
|
753
|
+
return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
|
|
754
|
+
},
|
|
755
|
+
{}
|
|
756
|
+
);
|
|
757
|
+
this._crdtNotifier.rootSubject.next(storageRoot);
|
|
693
758
|
}
|
|
694
759
|
_getAuthFetchOptions(room) {
|
|
695
760
|
if (typeof this._endpoints.authEndpoint === "undefined")
|
|
@@ -728,6 +793,11 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
728
793
|
}
|
|
729
794
|
});
|
|
730
795
|
}
|
|
796
|
+
_getTrackedOrigins() {
|
|
797
|
+
var _a;
|
|
798
|
+
const connectionId = this._state.connection.id;
|
|
799
|
+
return !connectionId ? this._trackedOrigins : [connectionId, ...(_a = this._trackedOrigins) != null ? _a : []];
|
|
800
|
+
}
|
|
731
801
|
_getWsEndpoint(room) {
|
|
732
802
|
switch (typeof this._endpoints.wsEndpoint) {
|
|
733
803
|
case "undefined":
|
|
@@ -824,17 +894,21 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
824
894
|
}
|
|
825
895
|
_handleStorageReceivedMessage(message) {
|
|
826
896
|
return __async(this, null, function* () {
|
|
897
|
+
var _a;
|
|
827
898
|
const { connectionId } = message;
|
|
828
899
|
if (!connectionId)
|
|
829
900
|
return;
|
|
830
901
|
if (!this._state.webSocket)
|
|
831
902
|
throw new Error("Could not find WebSocket");
|
|
832
903
|
const data = message.data;
|
|
904
|
+
const trackedOrigins = this._getTrackedOrigins();
|
|
833
905
|
this._crdtManager.initialize({
|
|
906
|
+
captureTimeout: (_a = this._captureTimeout) != null ? _a : void 0,
|
|
834
907
|
onInitialized: () => __async(this, null, function* () {
|
|
835
908
|
this._observeCrdt();
|
|
836
909
|
}),
|
|
837
|
-
origin:
|
|
910
|
+
origin: ORIGIN_INITIALIZED,
|
|
911
|
+
trackedOrigins: trackedOrigins != null ? trackedOrigins : void 0,
|
|
838
912
|
update: data.state
|
|
839
913
|
});
|
|
840
914
|
const encodedState = this._crdtManager.doc.encodeStateAsUpdate();
|
|
@@ -843,7 +917,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
843
917
|
const update = this._crdtManager.doc.encodeStateAsUpdate();
|
|
844
918
|
this._sendMessage({
|
|
845
919
|
type: "$UPDATE_STORAGE",
|
|
846
|
-
data: { origin:
|
|
920
|
+
data: { origin: ORIGIN_INITIALIZED, update }
|
|
847
921
|
});
|
|
848
922
|
});
|
|
849
923
|
}
|
|
@@ -856,14 +930,19 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
856
930
|
const data = message.data;
|
|
857
931
|
if (!this._crdtManager)
|
|
858
932
|
return;
|
|
859
|
-
this._crdtManager.doc.applyUpdate(data.state,
|
|
933
|
+
this._crdtManager.doc.applyUpdate(data.state, ORIGIN_STORAGE_UPDATED);
|
|
860
934
|
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
861
|
-
Object.entries(sharedTypes).
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
935
|
+
const storageRoot = Object.entries(sharedTypes).reduce(
|
|
936
|
+
(acc, [prop, sharedType]) => {
|
|
937
|
+
if (!this._crdtManager)
|
|
938
|
+
return acc;
|
|
939
|
+
const serialized = sharedType.toJSON();
|
|
940
|
+
this._crdtNotifier.subject(prop).next(serialized);
|
|
941
|
+
return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
|
|
942
|
+
},
|
|
943
|
+
{}
|
|
944
|
+
);
|
|
945
|
+
this._crdtNotifier.rootSubject.next(storageRoot);
|
|
867
946
|
}
|
|
868
947
|
_handleUserJoinedMessage(message) {
|
|
869
948
|
const { connectionId } = message;
|
|
@@ -912,7 +991,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
912
991
|
const origin = _origin != null ? _origin : null;
|
|
913
992
|
this._addToStorageStore(update);
|
|
914
993
|
this._emitSharedTypes();
|
|
915
|
-
if (origin ===
|
|
994
|
+
if (origin === ORIGIN_INITIALIZED || origin === this)
|
|
916
995
|
return;
|
|
917
996
|
if (!this._state.webSocket)
|
|
918
997
|
return;
|
|
@@ -920,7 +999,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
920
999
|
return;
|
|
921
1000
|
if (this._state.webSocket.readyState !== WebSocket.OPEN)
|
|
922
1001
|
return;
|
|
923
|
-
if (origin ===
|
|
1002
|
+
if (origin === ORIGIN_STORAGE_UPDATED)
|
|
924
1003
|
return;
|
|
925
1004
|
this._sendMessage({
|
|
926
1005
|
type: "$UPDATE_STORAGE",
|
|
@@ -1210,6 +1289,14 @@ var MockedRoom = class extends AbstractRoom {
|
|
|
1210
1289
|
this._subscriptions = {
|
|
1211
1290
|
observeCrdt: null
|
|
1212
1291
|
};
|
|
1292
|
+
this.canRedo = () => {
|
|
1293
|
+
var _a;
|
|
1294
|
+
return !!((_a = this._crdtManager) == null ? void 0 : _a.doc.canRedo());
|
|
1295
|
+
};
|
|
1296
|
+
this.canUndo = () => {
|
|
1297
|
+
var _a;
|
|
1298
|
+
return !!((_a = this._crdtManager) == null ? void 0 : _a.doc.canUndo());
|
|
1299
|
+
};
|
|
1213
1300
|
this.event = (event, callback) => {
|
|
1214
1301
|
return this._eventNotifier.subscribe(event, callback);
|
|
1215
1302
|
};
|
|
@@ -1232,12 +1319,34 @@ var MockedRoom = class extends AbstractRoom {
|
|
|
1232
1319
|
this.other = (connectionId, callback) => {
|
|
1233
1320
|
return this._otherNotifier.subscribe(connectionId, callback);
|
|
1234
1321
|
};
|
|
1322
|
+
this.redo = () => {
|
|
1323
|
+
var _a;
|
|
1324
|
+
(_a = this._crdtManager) == null ? void 0 : _a.doc.redo();
|
|
1325
|
+
};
|
|
1235
1326
|
this.storage = (key, fn) => {
|
|
1236
1327
|
return this._crdtNotifier.subscribe(key, fn);
|
|
1237
1328
|
};
|
|
1329
|
+
this.storageRoot = (fn) => {
|
|
1330
|
+
return this._crdtNotifier.subcribeRoot(fn);
|
|
1331
|
+
};
|
|
1238
1332
|
this.subscribe = (name, callback) => {
|
|
1239
1333
|
return this._stateNotifier.subscribe(name, callback);
|
|
1240
1334
|
};
|
|
1335
|
+
this.transact = (fn, origin) => {
|
|
1336
|
+
const _origin = origin != null ? origin : this._state.connection.id;
|
|
1337
|
+
const crdtManager = this._crdtManager;
|
|
1338
|
+
if (typeof _origin !== "string")
|
|
1339
|
+
return;
|
|
1340
|
+
if (!crdtManager)
|
|
1341
|
+
return;
|
|
1342
|
+
crdtManager.doc.transact(() => {
|
|
1343
|
+
fn(crdtManager.doc.storage);
|
|
1344
|
+
}, _origin);
|
|
1345
|
+
};
|
|
1346
|
+
this.undo = () => {
|
|
1347
|
+
var _a;
|
|
1348
|
+
(_a = this._crdtManager) == null ? void 0 : _a.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.0",
|
|
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.0",
|
|
22
|
+
"@pluv/types": "^0.12.0"
|
|
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.0",
|
|
33
|
+
"eslint-config-pluv": "^0.12.0"
|
|
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
|
|
56
|
+
key: TKey,
|
|
53
57
|
): TStorage[TKey] | null;
|
|
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
|
|
package/src/EventNotifier.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { makeSubject, subscribe } from "wonka";
|
|
|
4
4
|
|
|
5
5
|
export type EventNotifierSubscriptionCallback<
|
|
6
6
|
TIO extends IOLike,
|
|
7
|
-
TEvent extends keyof InferIOOutput<TIO
|
|
7
|
+
TEvent extends keyof InferIOOutput<TIO>,
|
|
8
8
|
> = (value: Id<IOEventMessage<TIO, TEvent>>) => void;
|
|
9
9
|
|
|
10
10
|
export class EventNotifier<TIO extends IOLike> {
|
|
@@ -14,7 +14,7 @@ export class EventNotifier<TIO extends IOLike> {
|
|
|
14
14
|
>();
|
|
15
15
|
|
|
16
16
|
public subject<TEvent extends keyof InferIOOutput<TIO>>(
|
|
17
|
-
name: TEvent
|
|
17
|
+
name: TEvent,
|
|
18
18
|
): Subject<Id<IOEventMessage<TIO, TEvent>>> {
|
|
19
19
|
const subject = this.subjects.get(name);
|
|
20
20
|
|
|
@@ -29,7 +29,7 @@ export class EventNotifier<TIO extends IOLike> {
|
|
|
29
29
|
|
|
30
30
|
public subscribe<TEvent extends keyof InferIOOutput<TIO>>(
|
|
31
31
|
name: TEvent,
|
|
32
|
-
callback: EventNotifierSubscriptionCallback<TIO, TEvent
|
|
32
|
+
callback: EventNotifierSubscriptionCallback<TIO, TEvent>,
|
|
33
33
|
): () => void {
|
|
34
34
|
const subject = this.subject(name);
|
|
35
35
|
|