@pluv/client 2.0.2 → 2.1.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 +97 -0
- package/dist/index.d.mts +27 -25
- package/dist/index.d.ts +27 -25
- package/dist/index.js +214 -68
- package/dist/index.mjs +214 -68
- package/package.json +5 -5
- package/src/CrdtNotifier.ts +2 -1
- package/src/MockedRoom.ts +83 -33
- package/src/PluvRoom.ts +160 -58
- package/src/StateNotifier.ts +11 -17
- package/src/UsersNotifier.ts +66 -0
- package/src/OtherNotifier.ts +0 -50
package/dist/index.mjs
CHANGED
|
@@ -200,35 +200,6 @@ var EventNotifier = class {
|
|
|
200
200
|
}
|
|
201
201
|
};
|
|
202
202
|
|
|
203
|
-
// src/OtherNotifier.ts
|
|
204
|
-
import { makeSubject as makeSubject3, subscribe as subscribe3 } from "wonka";
|
|
205
|
-
var OtherNotifier = class {
|
|
206
|
-
constructor() {
|
|
207
|
-
this._subjects = /* @__PURE__ */ new Map();
|
|
208
|
-
}
|
|
209
|
-
clear() {
|
|
210
|
-
this._subjects.forEach((subject) => {
|
|
211
|
-
subject.next(null);
|
|
212
|
-
});
|
|
213
|
-
this._subjects.clear();
|
|
214
|
-
}
|
|
215
|
-
delete(clientId) {
|
|
216
|
-
this.subject(clientId).next(null);
|
|
217
|
-
this._subjects.delete(clientId);
|
|
218
|
-
}
|
|
219
|
-
subject(clientId) {
|
|
220
|
-
const subject = this._subjects.get(clientId);
|
|
221
|
-
if (subject) return subject;
|
|
222
|
-
const newSubject = makeSubject3();
|
|
223
|
-
this._subjects.set(clientId, newSubject);
|
|
224
|
-
return newSubject;
|
|
225
|
-
}
|
|
226
|
-
subscribe(clientId, callback) {
|
|
227
|
-
const source = this.subject(clientId).source;
|
|
228
|
-
return subscribe3(callback)(source).unsubscribe;
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
|
|
232
203
|
// src/PluvRouter.ts
|
|
233
204
|
var PluvRouter = class _PluvRouter {
|
|
234
205
|
constructor(events) {
|
|
@@ -257,22 +228,22 @@ var PluvRouter = class _PluvRouter {
|
|
|
257
228
|
};
|
|
258
229
|
|
|
259
230
|
// src/StateNotifier.ts
|
|
260
|
-
import { makeSubject as
|
|
231
|
+
import { makeSubject as makeSubject3, subscribe as subscribe3 } from "wonka";
|
|
261
232
|
var StateNotifier = class {
|
|
262
233
|
constructor() {
|
|
263
234
|
this.subjects = {
|
|
264
|
-
connection:
|
|
265
|
-
"my-presence":
|
|
266
|
-
myself:
|
|
267
|
-
others:
|
|
268
|
-
"storage-loaded":
|
|
235
|
+
connection: makeSubject3(),
|
|
236
|
+
"my-presence": makeSubject3(),
|
|
237
|
+
myself: makeSubject3(),
|
|
238
|
+
others: makeSubject3(),
|
|
239
|
+
"storage-loaded": makeSubject3()
|
|
269
240
|
};
|
|
270
241
|
}
|
|
271
242
|
subscribe(name, callback) {
|
|
272
243
|
var _a;
|
|
273
244
|
const source = (_a = this.subjects[name]) == null ? void 0 : _a.source;
|
|
274
245
|
if (!source) throw new Error(`Could not subscribe to source: ${name}`);
|
|
275
|
-
return
|
|
246
|
+
return subscribe3(callback)(source).unsubscribe;
|
|
276
247
|
}
|
|
277
248
|
};
|
|
278
249
|
|
|
@@ -522,6 +493,43 @@ var UsersManager = class {
|
|
|
522
493
|
}
|
|
523
494
|
};
|
|
524
495
|
|
|
496
|
+
// src/UsersNotifier.ts
|
|
497
|
+
import { makeSubject as makeSubject4, subscribe as subscribe4 } from "wonka";
|
|
498
|
+
var UsersNotifier = class {
|
|
499
|
+
constructor() {
|
|
500
|
+
this.others = makeSubject4();
|
|
501
|
+
this._otherSubjects = /* @__PURE__ */ new Map();
|
|
502
|
+
}
|
|
503
|
+
clear() {
|
|
504
|
+
this._otherSubjects.forEach((subject) => {
|
|
505
|
+
subject.next(null);
|
|
506
|
+
});
|
|
507
|
+
this._otherSubjects.clear();
|
|
508
|
+
}
|
|
509
|
+
delete(clientId) {
|
|
510
|
+
this.other(clientId).next(null);
|
|
511
|
+
this._otherSubjects.delete(clientId);
|
|
512
|
+
}
|
|
513
|
+
other(clientId) {
|
|
514
|
+
const subject = this._otherSubjects.get(clientId);
|
|
515
|
+
if (subject) return subject;
|
|
516
|
+
const newSubject = makeSubject4();
|
|
517
|
+
this._otherSubjects.set(clientId, newSubject);
|
|
518
|
+
return newSubject;
|
|
519
|
+
}
|
|
520
|
+
subscribeOther(clientId, callback) {
|
|
521
|
+
const source = this.other(clientId).source;
|
|
522
|
+
const subscription = subscribe4(callback)(source);
|
|
523
|
+
return subscription.unsubscribe;
|
|
524
|
+
}
|
|
525
|
+
subscribeOthers(callback) {
|
|
526
|
+
const subscription = subscribe4(
|
|
527
|
+
({ others, event }) => callback(others, event)
|
|
528
|
+
)(this.others.source);
|
|
529
|
+
return subscription.unsubscribe;
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
|
|
525
533
|
// src/PluvRoom.ts
|
|
526
534
|
var ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1e3;
|
|
527
535
|
var HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -541,7 +549,6 @@ var PluvRoom = class {
|
|
|
541
549
|
this._intervals = {
|
|
542
550
|
heartbeat: null
|
|
543
551
|
};
|
|
544
|
-
this._otherNotifier = new OtherNotifier();
|
|
545
552
|
this._publicKey = null;
|
|
546
553
|
this._stateNotifier = new StateNotifier();
|
|
547
554
|
this._subscriptions = {
|
|
@@ -551,6 +558,7 @@ var PluvRoom = class {
|
|
|
551
558
|
pong: null,
|
|
552
559
|
reconnect: null
|
|
553
560
|
};
|
|
561
|
+
this._usersNotifier = new UsersNotifier();
|
|
554
562
|
this._lastMetadata = null;
|
|
555
563
|
this._state = {
|
|
556
564
|
authorization: {
|
|
@@ -605,6 +613,9 @@ var PluvRoom = class {
|
|
|
605
613
|
this.canUndo = () => {
|
|
606
614
|
return !!this._crdtManager.doc.canUndo();
|
|
607
615
|
};
|
|
616
|
+
/**
|
|
617
|
+
* @deprecated To be removed in v3. Use PluvRoom.subscribe.event instead
|
|
618
|
+
*/
|
|
608
619
|
this.event = new Proxy(
|
|
609
620
|
(event, callback) => this._eventNotifier.subscribe(event, callback),
|
|
610
621
|
{
|
|
@@ -636,23 +647,77 @@ var PluvRoom = class {
|
|
|
636
647
|
if (typeof sharedType === "undefined") return null;
|
|
637
648
|
return sharedType;
|
|
638
649
|
};
|
|
650
|
+
/**
|
|
651
|
+
* @deprecated To be removed in v3. Use PluvRoom.subscribe.other instead
|
|
652
|
+
*/
|
|
639
653
|
this.other = (connectionId, callback) => {
|
|
640
654
|
const clientId = this._usersManager.getClientId(connectionId);
|
|
641
655
|
if (!clientId) return () => void 0;
|
|
642
|
-
return this.
|
|
656
|
+
return this._usersNotifier.subscribeOther(clientId, callback);
|
|
643
657
|
};
|
|
644
658
|
this.redo = () => {
|
|
645
659
|
this._crdtManager.doc.redo();
|
|
646
660
|
};
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
this.
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
661
|
+
/**
|
|
662
|
+
* @deprecated To be removed in v3. To be removed in v3. Use PluvRoom.subscribe.storage instead
|
|
663
|
+
*/
|
|
664
|
+
this.storage = new Proxy(
|
|
665
|
+
(key, callback) => this._crdtNotifier.subscribe(key, callback),
|
|
666
|
+
{
|
|
667
|
+
get: (fn, prop) => {
|
|
668
|
+
if (!!prop) {
|
|
669
|
+
return (callback) => {
|
|
670
|
+
return this._crdtNotifier.subcribeRoot(callback);
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
return (callback) => {
|
|
674
|
+
return fn(prop, callback);
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
);
|
|
679
|
+
/**
|
|
680
|
+
* @deprecated To be removed in v3. Use PluvRoom.subscribe.storage instead
|
|
681
|
+
* @description Subscribes to the root storage object (serialized)
|
|
682
|
+
*/
|
|
683
|
+
this.storageRoot = (callback) => {
|
|
684
|
+
return this._crdtNotifier.subcribeRoot(callback);
|
|
655
685
|
};
|
|
686
|
+
this.subscribe = new Proxy(
|
|
687
|
+
(name, callback) => this._stateNotifier.subscribe(name, callback),
|
|
688
|
+
{
|
|
689
|
+
get: (fn, prop) => {
|
|
690
|
+
if (prop === "connection") {
|
|
691
|
+
return (callback) => {
|
|
692
|
+
return fn("connection", callback);
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
if (prop === "myPresence") {
|
|
696
|
+
return (callback) => {
|
|
697
|
+
return fn("my-presence", callback);
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
if (prop === "myself") {
|
|
701
|
+
return (callback) => {
|
|
702
|
+
return fn("myself", callback);
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
if (prop === "others") {
|
|
706
|
+
return (callback) => {
|
|
707
|
+
return this._usersNotifier.subscribeOthers(callback);
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
if (prop === "storageLoaded") {
|
|
711
|
+
return (callback) => {
|
|
712
|
+
return fn("storage-loaded", callback);
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
if (prop === "event") return this.event;
|
|
716
|
+
if (prop === "other") return this.other;
|
|
717
|
+
if (prop === "storage") return this.storage;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
);
|
|
656
721
|
this.transact = (fn, origin) => {
|
|
657
722
|
const _origin = origin != null ? origin : this._state.connection.id;
|
|
658
723
|
if (typeof _origin !== "string") return;
|
|
@@ -863,9 +928,10 @@ var PluvRoom = class {
|
|
|
863
928
|
this._detachWindowListeners();
|
|
864
929
|
this._usersManager.removeMyself();
|
|
865
930
|
this._usersManager.clearConnections();
|
|
866
|
-
this.
|
|
931
|
+
this._usersNotifier.clear();
|
|
867
932
|
this._stateNotifier.subjects.myself.next(null);
|
|
868
933
|
this._stateNotifier.subjects.others.next([]);
|
|
934
|
+
this._usersNotifier.others.next({ others: [], event: { kind: "clear" } });
|
|
869
935
|
(_c = this._state.webSocket) == null ? void 0 : _c.close();
|
|
870
936
|
this._detachWsListeners();
|
|
871
937
|
}
|
|
@@ -956,7 +1022,16 @@ var PluvRoom = class {
|
|
|
956
1022
|
const deleted = this._usersManager.deleteConnection(connectionId);
|
|
957
1023
|
const others = this._usersManager.getOthers();
|
|
958
1024
|
this._stateNotifier.subjects.others.next(others);
|
|
959
|
-
if (!
|
|
1025
|
+
if (!deleted) {
|
|
1026
|
+
console.warn("Could not identify exited connection");
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
const { data: user, remaining } = deleted;
|
|
1030
|
+
this._usersNotifier.others.next({
|
|
1031
|
+
others,
|
|
1032
|
+
event: { kind: "leave", user }
|
|
1033
|
+
});
|
|
1034
|
+
if (!remaining && !!clientId) this._usersNotifier.delete(clientId);
|
|
960
1035
|
}
|
|
961
1036
|
_handlePresenceUpdatedMessage(message) {
|
|
962
1037
|
var _a;
|
|
@@ -975,19 +1050,31 @@ var PluvRoom = class {
|
|
|
975
1050
|
return;
|
|
976
1051
|
}
|
|
977
1052
|
if (!!clientId) {
|
|
978
|
-
const
|
|
979
|
-
this.
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1053
|
+
const other2 = this._usersManager.getOther(connectionId);
|
|
1054
|
+
const others2 = this._usersManager.getOthers();
|
|
1055
|
+
this._usersNotifier.other(clientId).next(other2);
|
|
1056
|
+
this._stateNotifier.subjects.others.next(others2);
|
|
1057
|
+
if (!!other2) {
|
|
1058
|
+
this._usersNotifier.others.next({
|
|
1059
|
+
others: others2,
|
|
1060
|
+
event: { kind: "update", user: other2 }
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
return;
|
|
988
1064
|
}
|
|
1065
|
+
const added = this._usersManager.addConnection({
|
|
1066
|
+
connectionId,
|
|
1067
|
+
presence: data.presence,
|
|
1068
|
+
user: message.user
|
|
1069
|
+
});
|
|
1070
|
+
const other = this._usersManager.getOther(connectionId);
|
|
989
1071
|
const others = this._usersManager.getOthers();
|
|
1072
|
+
this._usersNotifier.other(added.clientId).next(other);
|
|
990
1073
|
this._stateNotifier.subjects.others.next(others);
|
|
1074
|
+
this._usersNotifier.others.next({
|
|
1075
|
+
others,
|
|
1076
|
+
event: { kind: "update", user: added.data }
|
|
1077
|
+
});
|
|
991
1078
|
}
|
|
992
1079
|
_handleReceiveOthers(message) {
|
|
993
1080
|
if (!message.connectionId) return;
|
|
@@ -1004,10 +1091,14 @@ var PluvRoom = class {
|
|
|
1004
1091
|
if (result.isMyself) return;
|
|
1005
1092
|
const clientId = result.clientId;
|
|
1006
1093
|
const other = this._usersManager.getOther(connectionId);
|
|
1007
|
-
this.
|
|
1094
|
+
this._usersNotifier.other(clientId).next(other);
|
|
1008
1095
|
});
|
|
1009
1096
|
const others = this._usersManager.getOthers();
|
|
1010
1097
|
this._stateNotifier.subjects.others.next(others);
|
|
1098
|
+
this._usersNotifier.others.next({
|
|
1099
|
+
others,
|
|
1100
|
+
event: { kind: "sync", users: others }
|
|
1101
|
+
});
|
|
1011
1102
|
}
|
|
1012
1103
|
_handleRegisteredMessage(message) {
|
|
1013
1104
|
return __async(this, null, function* () {
|
|
@@ -1109,6 +1200,11 @@ var PluvRoom = class {
|
|
|
1109
1200
|
const quitters = this._usersManager.getOthers().filter((other) => !active.has(other.connectionId));
|
|
1110
1201
|
quitters.forEach((quitter) => {
|
|
1111
1202
|
this._usersManager.deleteConnection(quitter.connectionId);
|
|
1203
|
+
const remaining2 = this._usersManager.getOthers();
|
|
1204
|
+
this._usersNotifier.others.next({
|
|
1205
|
+
others: remaining2,
|
|
1206
|
+
event: { kind: "leave", user: quitter }
|
|
1207
|
+
});
|
|
1112
1208
|
});
|
|
1113
1209
|
const remaining = this._usersManager.getOthers();
|
|
1114
1210
|
this._stateNotifier.subjects.others.next(remaining);
|
|
@@ -1131,8 +1227,12 @@ var PluvRoom = class {
|
|
|
1131
1227
|
});
|
|
1132
1228
|
const other = this._usersManager.getOther(connectionId);
|
|
1133
1229
|
const others = this._usersManager.getOthers();
|
|
1134
|
-
this.
|
|
1230
|
+
this._usersNotifier.other(added.clientId).next(other);
|
|
1135
1231
|
this._stateNotifier.subjects.others.next(others);
|
|
1232
|
+
this._usersNotifier.others.next({
|
|
1233
|
+
others,
|
|
1234
|
+
event: { kind: "enter", user: added.data }
|
|
1235
|
+
});
|
|
1136
1236
|
}
|
|
1137
1237
|
_heartbeat() {
|
|
1138
1238
|
this._clearTimeout(this._timeouts.pong);
|
|
@@ -1507,7 +1607,7 @@ var MockedRoom = class {
|
|
|
1507
1607
|
constructor(room, options) {
|
|
1508
1608
|
this._crdtNotifier = new CrdtNotifier();
|
|
1509
1609
|
this._eventNotifier = new EventNotifier();
|
|
1510
|
-
this.
|
|
1610
|
+
this._usersNotifier = new UsersNotifier();
|
|
1511
1611
|
this._state = {
|
|
1512
1612
|
authorization: {
|
|
1513
1613
|
token: null,
|
|
@@ -1594,20 +1694,66 @@ var MockedRoom = class {
|
|
|
1594
1694
|
this.other = (connectionId, callback) => {
|
|
1595
1695
|
const clientId = this._usersManager.getClientId(connectionId);
|
|
1596
1696
|
if (!clientId) return () => void 0;
|
|
1597
|
-
return this.
|
|
1697
|
+
return this._usersNotifier.subscribeOther(clientId, callback);
|
|
1598
1698
|
};
|
|
1599
1699
|
this.redo = () => {
|
|
1600
1700
|
this._crdtManager.doc.redo();
|
|
1601
1701
|
};
|
|
1602
|
-
this.storage = (
|
|
1603
|
-
|
|
1604
|
-
|
|
1702
|
+
this.storage = new Proxy(
|
|
1703
|
+
(key, callback) => this._crdtNotifier.subscribe(key, callback),
|
|
1704
|
+
{
|
|
1705
|
+
get: (fn, prop) => {
|
|
1706
|
+
if (!!prop) {
|
|
1707
|
+
return (callback) => {
|
|
1708
|
+
return this._crdtNotifier.subcribeRoot(callback);
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
return (callback) => {
|
|
1712
|
+
return fn(prop, callback);
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
);
|
|
1605
1717
|
this.storageRoot = (fn) => {
|
|
1606
1718
|
return this._crdtNotifier.subcribeRoot(fn);
|
|
1607
1719
|
};
|
|
1608
|
-
this.subscribe = (
|
|
1609
|
-
|
|
1610
|
-
|
|
1720
|
+
this.subscribe = new Proxy(
|
|
1721
|
+
(name, callback) => {
|
|
1722
|
+
return this._stateNotifier.subscribe(name, callback);
|
|
1723
|
+
},
|
|
1724
|
+
{
|
|
1725
|
+
get: (fn, prop) => {
|
|
1726
|
+
if (prop === "connection") {
|
|
1727
|
+
return (callback) => {
|
|
1728
|
+
return fn("connection", callback);
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
if (prop === "myPresence") {
|
|
1732
|
+
return (callback) => {
|
|
1733
|
+
return fn("my-presence", callback);
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1736
|
+
if (prop === "myself") {
|
|
1737
|
+
return (callback) => {
|
|
1738
|
+
return fn("myself", callback);
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
if (prop === "others") {
|
|
1742
|
+
return (callback) => {
|
|
1743
|
+
return this._usersNotifier.subscribeOthers(callback);
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1746
|
+
if (prop === "storageLoaded") {
|
|
1747
|
+
return (callback) => {
|
|
1748
|
+
return fn("storage-loaded", callback);
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
if (prop === "event") return this.event;
|
|
1752
|
+
if (prop === "other") return this.other;
|
|
1753
|
+
if (prop === "storage") return this.storage;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
);
|
|
1611
1757
|
this.transact = (fn, origin) => {
|
|
1612
1758
|
const _origin = origin != null ? origin : this._state.connection.id;
|
|
1613
1759
|
const crdtManager = this._crdtManager;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/client",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "@pluv/io frontend client",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"wonka": "^6.3.5",
|
|
21
|
-
"@pluv/crdt": "^2.0
|
|
22
|
-
"@pluv/types": "^2.0
|
|
21
|
+
"@pluv/crdt": "^2.1.0",
|
|
22
|
+
"@pluv/types": "^2.1.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"eslint": "^9.26.0",
|
|
26
26
|
"tsup": "^8.4.0",
|
|
27
27
|
"typescript": "^5.8.3",
|
|
28
|
-
"@pluv/tsconfig": "^2.0
|
|
29
|
-
"eslint-config-pluv": "^2.0
|
|
28
|
+
"@pluv/tsconfig": "^2.1.0",
|
|
29
|
+
"eslint-config-pluv": "^2.1.0"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
package/src/CrdtNotifier.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CrdtType, InferCrdtJson } from "@pluv/crdt";
|
|
2
|
+
import type { StorageSubscriptionCallback } from "@pluv/types";
|
|
2
3
|
import type { Subject } from "wonka";
|
|
3
4
|
import { makeSubject, subscribe } from "wonka";
|
|
4
5
|
|
|
@@ -36,7 +37,7 @@ export class CrdtNotifier<TStorage extends Record<string, CrdtType<any, any>>> {
|
|
|
36
37
|
|
|
37
38
|
public subscribe<TKey extends keyof TStorage>(
|
|
38
39
|
key: TKey,
|
|
39
|
-
callback:
|
|
40
|
+
callback: StorageSubscriptionCallback<TStorage, TKey>,
|
|
40
41
|
): () => void {
|
|
41
42
|
const subject = this.subject(key);
|
|
42
43
|
|
package/src/MockedRoom.ts
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import type { AbstractCrdtDocFactory,
|
|
1
|
+
import type { AbstractCrdtDocFactory, InferCrdtJson, InferStorage } from "@pluv/crdt";
|
|
2
2
|
import type {
|
|
3
|
+
BroadcastProxy,
|
|
3
4
|
CrdtDocLike,
|
|
4
5
|
EventNotifierSubscriptionCallback,
|
|
6
|
+
EventProxy,
|
|
5
7
|
IOLike,
|
|
6
8
|
Id,
|
|
7
9
|
InferIOInput,
|
|
8
10
|
InferIOOutput,
|
|
9
11
|
JsonObject,
|
|
10
12
|
MergeEvents,
|
|
11
|
-
|
|
13
|
+
OtherSubscriptionCallback,
|
|
14
|
+
OthersSubscriptionCallback,
|
|
12
15
|
RoomLike,
|
|
16
|
+
StateNotifierSubjects,
|
|
17
|
+
StorageProxy,
|
|
18
|
+
StorageRootSubscriptionCallback,
|
|
19
|
+
StorageSubscriptionCallback,
|
|
20
|
+
SubscribeProxy,
|
|
21
|
+
SubscriptionCallback,
|
|
13
22
|
UpdateMyPresenceAction,
|
|
14
23
|
UserInfo,
|
|
15
24
|
WebSocketState,
|
|
@@ -19,14 +28,13 @@ import type { CrdtManagerOptions } from "./CrdtManager";
|
|
|
19
28
|
import { CrdtManager } from "./CrdtManager";
|
|
20
29
|
import { CrdtNotifier } from "./CrdtNotifier";
|
|
21
30
|
import { EventNotifier } from "./EventNotifier";
|
|
22
|
-
import { OtherNotifier } from "./OtherNotifier";
|
|
23
31
|
import { PluvProcedure } from "./PluvProcedure";
|
|
24
32
|
import type { PluvRouterEventConfig } from "./PluvRouter";
|
|
25
33
|
import { PluvRouter } from "./PluvRouter";
|
|
26
|
-
import type { StateNotifierSubjects, SubscriptionCallback } from "./StateNotifier";
|
|
27
34
|
import { StateNotifier } from "./StateNotifier";
|
|
28
35
|
import type { UsersManagerConfig } from "./UsersManager";
|
|
29
36
|
import { UsersManager } from "./UsersManager";
|
|
37
|
+
import { UsersNotifier } from "./UsersNotifier";
|
|
30
38
|
import { MAX_PRESENCE_SIZE_BYTES } from "./constants";
|
|
31
39
|
import type {
|
|
32
40
|
EventResolver,
|
|
@@ -66,7 +74,7 @@ export class MockedRoom<
|
|
|
66
74
|
private readonly _eventNotifier = new EventNotifier<MergeEvents<TEvents, TIO>>();
|
|
67
75
|
private readonly _events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
|
|
68
76
|
private readonly _limits: PluvClientLimits;
|
|
69
|
-
private readonly
|
|
77
|
+
private readonly _usersNotifier = new UsersNotifier<TIO, TPresence>();
|
|
70
78
|
private readonly _router: PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>;
|
|
71
79
|
private _state: WebSocketState<TIO> = {
|
|
72
80
|
authorization: {
|
|
@@ -176,12 +184,7 @@ export class MockedRoom<
|
|
|
176
184
|
};
|
|
177
185
|
},
|
|
178
186
|
},
|
|
179
|
-
) as
|
|
180
|
-
event: TEvent,
|
|
181
|
-
data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>,
|
|
182
|
-
) => Promise<void>) & {
|
|
183
|
-
[event in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[event]>) => Promise<void>;
|
|
184
|
-
};
|
|
187
|
+
) as BroadcastProxy<TIO, TEvents>;
|
|
185
188
|
|
|
186
189
|
public canRedo = (): boolean => {
|
|
187
190
|
return !!this._crdtManager.doc.canRedo();
|
|
@@ -203,14 +206,7 @@ export class MockedRoom<
|
|
|
203
206
|
): (() => void) => fn(prop as any, callback);
|
|
204
207
|
},
|
|
205
208
|
},
|
|
206
|
-
) as
|
|
207
|
-
event: TEvent,
|
|
208
|
-
callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, TEvent>,
|
|
209
|
-
) => () => void) & {
|
|
210
|
-
[event in keyof InferIOOutput<MergeEvents<TEvents, TIO>>]: (
|
|
211
|
-
callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>,
|
|
212
|
-
) => () => void;
|
|
213
|
-
};
|
|
209
|
+
) as EventProxy<TIO, TEvents>;
|
|
214
210
|
|
|
215
211
|
public getConnection = (): WebSocketConnection => {
|
|
216
212
|
// Create a read-only clone of the connection state
|
|
@@ -261,25 +257,40 @@ export class MockedRoom<
|
|
|
261
257
|
|
|
262
258
|
public other = (
|
|
263
259
|
connectionId: string,
|
|
264
|
-
callback:
|
|
260
|
+
callback: OtherSubscriptionCallback<TIO, TPresence>,
|
|
265
261
|
): (() => void) => {
|
|
266
262
|
const clientId = this._usersManager.getClientId(connectionId);
|
|
267
263
|
|
|
268
264
|
if (!clientId) return () => undefined;
|
|
269
265
|
|
|
270
|
-
return this.
|
|
266
|
+
return this._usersNotifier.subscribeOther(clientId, callback);
|
|
271
267
|
};
|
|
272
268
|
|
|
273
269
|
public redo = (): void => {
|
|
274
270
|
this._crdtManager.doc.redo();
|
|
275
271
|
};
|
|
276
272
|
|
|
277
|
-
public storage =
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
273
|
+
public storage = new Proxy(
|
|
274
|
+
<TKey extends keyof InferStorage<TCrdt>>(
|
|
275
|
+
key: TKey,
|
|
276
|
+
callback: StorageSubscriptionCallback<InferStorage<TCrdt>, TKey>,
|
|
277
|
+
): (() => void) => this._crdtNotifier.subscribe(key, callback),
|
|
278
|
+
{
|
|
279
|
+
get: (fn, prop) => {
|
|
280
|
+
type _Storage = InferStorage<TCrdt>;
|
|
281
|
+
|
|
282
|
+
if (!!prop) {
|
|
283
|
+
return (callback: StorageRootSubscriptionCallback<_Storage>) => {
|
|
284
|
+
return this._crdtNotifier.subcribeRoot(callback);
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return (callback: StorageSubscriptionCallback<_Storage, keyof _Storage>) => {
|
|
289
|
+
return fn(prop as keyof InferStorage<TCrdt>, callback);
|
|
290
|
+
};
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
) as StorageProxy<InferStorage<TCrdt>>;
|
|
283
294
|
|
|
284
295
|
public storageRoot = (
|
|
285
296
|
fn: (value: {
|
|
@@ -289,12 +300,51 @@ export class MockedRoom<
|
|
|
289
300
|
return this._crdtNotifier.subcribeRoot(fn);
|
|
290
301
|
};
|
|
291
302
|
|
|
292
|
-
public subscribe =
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
303
|
+
public subscribe = new Proxy(
|
|
304
|
+
<TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(
|
|
305
|
+
name: TSubject,
|
|
306
|
+
callback: SubscriptionCallback<TIO, TPresence, TSubject>,
|
|
307
|
+
): (() => void) => {
|
|
308
|
+
return this._stateNotifier.subscribe(name, callback);
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
get: (fn, prop) => {
|
|
312
|
+
if (prop === "connection") {
|
|
313
|
+
return (callback: SubscriptionCallback<TIO, TPresence, "connection">) => {
|
|
314
|
+
return fn("connection", callback);
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (prop === "myPresence") {
|
|
319
|
+
return (callback: SubscriptionCallback<TIO, TPresence, "my-presence">) => {
|
|
320
|
+
return fn("my-presence", callback);
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (prop === "myself") {
|
|
325
|
+
return (callback: SubscriptionCallback<TIO, TPresence, "myself">) => {
|
|
326
|
+
return fn("myself", callback);
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (prop === "others") {
|
|
331
|
+
return (callback: OthersSubscriptionCallback<TIO, TPresence>) => {
|
|
332
|
+
return this._usersNotifier.subscribeOthers(callback);
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (prop === "storageLoaded") {
|
|
337
|
+
return (callback: SubscriptionCallback<TIO, TPresence, "storage-loaded">) => {
|
|
338
|
+
return fn("storage-loaded", callback);
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (prop === "event") return this.event;
|
|
343
|
+
if (prop === "other") return this.other;
|
|
344
|
+
if (prop === "storage") return this.storage;
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
) as SubscribeProxy<TIO, TPresence, InferStorage<TCrdt>, TEvents>;
|
|
298
348
|
|
|
299
349
|
public transact = (fn: (storage: InferStorage<TCrdt>) => void, origin?: string): void => {
|
|
300
350
|
const _origin = origin ?? this._state.connection.id;
|