@pluv/client 2.1.0 → 2.2.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 +51 -0
- package/dist/index.d.mts +49 -56
- package/dist/index.d.ts +49 -56
- package/dist/index.js +80 -27
- package/dist/index.mjs +82 -29
- package/package.json +5 -5
- package/src/CrdtManager.ts +9 -13
- package/src/ListenerManager.ts +21 -0
- package/src/MockedRoom.ts +36 -16
- package/src/PluvClient.ts +8 -8
- package/src/PluvProcedure.ts +27 -25
- package/src/PluvRoom.ts +76 -25
- package/src/PluvRouter.ts +31 -22
- package/src/StateNotifier.ts +2 -2
- package/src/UsersManager.ts +18 -4
- package/src/UsersNotifier.ts +1 -1
- package/src/constants.ts +1 -0
- package/src/createClient.ts +2 -2
- package/src/index.ts +2 -1
- package/src/types.ts +10 -31
package/dist/index.mjs
CHANGED
|
@@ -50,6 +50,7 @@ var AbstractStorageStore = class {
|
|
|
50
50
|
|
|
51
51
|
// src/constants.ts
|
|
52
52
|
var MAX_PRESENCE_SIZE_BYTES = 512;
|
|
53
|
+
var PLUV_PRESENCE_META_KEY = "__pluv";
|
|
53
54
|
|
|
54
55
|
// src/PluvProcedure.ts
|
|
55
56
|
var PluvProcedure = class _PluvProcedure {
|
|
@@ -87,7 +88,7 @@ var PluvProcedure = class _PluvProcedure {
|
|
|
87
88
|
};
|
|
88
89
|
|
|
89
90
|
// src/PluvRoom.ts
|
|
90
|
-
import { ConnectionState } from "@pluv/types";
|
|
91
|
+
import { ConnectionState, StorageState } from "@pluv/types";
|
|
91
92
|
|
|
92
93
|
// src/CrdtManager.ts
|
|
93
94
|
import { noop } from "@pluv/crdt";
|
|
@@ -96,7 +97,7 @@ var CrdtManager = class {
|
|
|
96
97
|
this.initialized = false;
|
|
97
98
|
const { initialStorage = noop.doc({}) } = options;
|
|
98
99
|
this._docFactory = initialStorage;
|
|
99
|
-
this.doc =
|
|
100
|
+
this.doc = this._docFactory.getEmpty();
|
|
100
101
|
}
|
|
101
102
|
/**
|
|
102
103
|
* !HACK
|
|
@@ -136,11 +137,7 @@ var CrdtManager = class {
|
|
|
136
137
|
if (!updates.length) return this;
|
|
137
138
|
const initialized = this._docFactory.getInitialized();
|
|
138
139
|
const reference = initialized.get();
|
|
139
|
-
this.doc = this._applyDocUpdates(
|
|
140
|
-
this._docFactory.getEmpty(),
|
|
141
|
-
updates,
|
|
142
|
-
origin
|
|
143
|
-
).rebuildStorage(reference);
|
|
140
|
+
this.doc = this._applyDocUpdates(this.doc, updates, origin).rebuildStorage(reference);
|
|
144
141
|
this.initialized = true;
|
|
145
142
|
initialized.destroy();
|
|
146
143
|
return this;
|
|
@@ -200,6 +197,25 @@ var EventNotifier = class {
|
|
|
200
197
|
}
|
|
201
198
|
};
|
|
202
199
|
|
|
200
|
+
// src/ListenerManager.ts
|
|
201
|
+
import { makeSubject as makeSubject3, subscribe as subscribe3 } from "wonka";
|
|
202
|
+
var ListenerManager = class {
|
|
203
|
+
constructor() {
|
|
204
|
+
this.subjects = {
|
|
205
|
+
close: makeSubject3(),
|
|
206
|
+
error: makeSubject3(),
|
|
207
|
+
message: makeSubject3(),
|
|
208
|
+
open: makeSubject3()
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
subscribe(kind, callback) {
|
|
212
|
+
var _a;
|
|
213
|
+
const source = (_a = this.subjects[kind]) == null ? void 0 : _a.source;
|
|
214
|
+
if (!source) return () => void 0;
|
|
215
|
+
return subscribe3(callback)(source).unsubscribe;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
203
219
|
// src/PluvRouter.ts
|
|
204
220
|
var PluvRouter = class _PluvRouter {
|
|
205
221
|
constructor(events) {
|
|
@@ -228,22 +244,22 @@ var PluvRouter = class _PluvRouter {
|
|
|
228
244
|
};
|
|
229
245
|
|
|
230
246
|
// src/StateNotifier.ts
|
|
231
|
-
import { makeSubject as
|
|
247
|
+
import { makeSubject as makeSubject4, subscribe as subscribe4 } from "wonka";
|
|
232
248
|
var StateNotifier = class {
|
|
233
249
|
constructor() {
|
|
234
250
|
this.subjects = {
|
|
235
|
-
connection:
|
|
236
|
-
"my-presence":
|
|
237
|
-
myself:
|
|
238
|
-
others:
|
|
239
|
-
"storage-loaded":
|
|
251
|
+
connection: makeSubject4(),
|
|
252
|
+
"my-presence": makeSubject4(),
|
|
253
|
+
myself: makeSubject4(),
|
|
254
|
+
others: makeSubject4(),
|
|
255
|
+
"storage-loaded": makeSubject4()
|
|
240
256
|
};
|
|
241
257
|
}
|
|
242
258
|
subscribe(name, callback) {
|
|
243
259
|
var _a;
|
|
244
260
|
const source = (_a = this.subjects[name]) == null ? void 0 : _a.source;
|
|
245
261
|
if (!source) throw new Error(`Could not subscribe to source: ${name}`);
|
|
246
|
-
return
|
|
262
|
+
return subscribe4(callback)(source).unsubscribe;
|
|
247
263
|
}
|
|
248
264
|
};
|
|
249
265
|
|
|
@@ -416,6 +432,9 @@ var UsersManager = class {
|
|
|
416
432
|
);
|
|
417
433
|
const presence = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
|
|
418
434
|
const validated = this._presence ? this._presence.parse(presence) : presence;
|
|
435
|
+
if (!!presence[PLUV_PRESENCE_META_KEY]) {
|
|
436
|
+
validated[PLUV_PRESENCE_META_KEY] = presence[PLUV_PRESENCE_META_KEY];
|
|
437
|
+
}
|
|
419
438
|
this._others.set(clientId, __spreadProps(__spreadValues({}, other), { presence: validated }));
|
|
420
439
|
return validated;
|
|
421
440
|
}
|
|
@@ -494,10 +513,10 @@ var UsersManager = class {
|
|
|
494
513
|
};
|
|
495
514
|
|
|
496
515
|
// src/UsersNotifier.ts
|
|
497
|
-
import { makeSubject as
|
|
516
|
+
import { makeSubject as makeSubject5, subscribe as subscribe5 } from "wonka";
|
|
498
517
|
var UsersNotifier = class {
|
|
499
518
|
constructor() {
|
|
500
|
-
this.others =
|
|
519
|
+
this.others = makeSubject5();
|
|
501
520
|
this._otherSubjects = /* @__PURE__ */ new Map();
|
|
502
521
|
}
|
|
503
522
|
clear() {
|
|
@@ -513,17 +532,17 @@ var UsersNotifier = class {
|
|
|
513
532
|
other(clientId) {
|
|
514
533
|
const subject = this._otherSubjects.get(clientId);
|
|
515
534
|
if (subject) return subject;
|
|
516
|
-
const newSubject =
|
|
535
|
+
const newSubject = makeSubject5();
|
|
517
536
|
this._otherSubjects.set(clientId, newSubject);
|
|
518
537
|
return newSubject;
|
|
519
538
|
}
|
|
520
539
|
subscribeOther(clientId, callback) {
|
|
521
540
|
const source = this.other(clientId).source;
|
|
522
|
-
const subscription =
|
|
541
|
+
const subscription = subscribe5(callback)(source);
|
|
523
542
|
return subscription.unsubscribe;
|
|
524
543
|
}
|
|
525
544
|
subscribeOthers(callback) {
|
|
526
|
-
const subscription =
|
|
545
|
+
const subscription = subscribe5(
|
|
527
546
|
({ others, event }) => callback(others, event)
|
|
528
547
|
)(this.others.source);
|
|
529
548
|
return subscription.unsubscribe;
|
|
@@ -549,6 +568,7 @@ var PluvRoom = class {
|
|
|
549
568
|
this._intervals = {
|
|
550
569
|
heartbeat: null
|
|
551
570
|
};
|
|
571
|
+
this._listenerManager = new ListenerManager();
|
|
552
572
|
this._publicKey = null;
|
|
553
573
|
this._stateNotifier = new StateNotifier();
|
|
554
574
|
this._subscriptions = {
|
|
@@ -567,10 +587,12 @@ var PluvRoom = class {
|
|
|
567
587
|
},
|
|
568
588
|
connection: {
|
|
569
589
|
attempts: 0,
|
|
570
|
-
count: 0,
|
|
571
590
|
id: null,
|
|
572
591
|
state: ConnectionState.Untouched
|
|
573
592
|
},
|
|
593
|
+
storage: {
|
|
594
|
+
state: StorageState.Unavailable
|
|
595
|
+
},
|
|
574
596
|
webSocket: null
|
|
575
597
|
};
|
|
576
598
|
this._windowListeners = null;
|
|
@@ -647,6 +669,9 @@ var PluvRoom = class {
|
|
|
647
669
|
if (typeof sharedType === "undefined") return null;
|
|
648
670
|
return sharedType;
|
|
649
671
|
};
|
|
672
|
+
this.getStorageLoaded = () => {
|
|
673
|
+
return this._state.storage.state === StorageState.Offline || this._state.storage.state === StorageState.Synced;
|
|
674
|
+
};
|
|
650
675
|
/**
|
|
651
676
|
* @deprecated To be removed in v3. Use PluvRoom.subscribe.other instead
|
|
652
677
|
*/
|
|
@@ -659,7 +684,7 @@ var PluvRoom = class {
|
|
|
659
684
|
this._crdtManager.doc.redo();
|
|
660
685
|
};
|
|
661
686
|
/**
|
|
662
|
-
* @deprecated To be removed in v3.
|
|
687
|
+
* @deprecated To be removed in v3. Use PluvRoom.subscribe.storage instead
|
|
663
688
|
*/
|
|
664
689
|
this.storage = new Proxy(
|
|
665
690
|
(key, callback) => this._crdtNotifier.subscribe(key, callback),
|
|
@@ -797,12 +822,18 @@ var PluvRoom = class {
|
|
|
797
822
|
});
|
|
798
823
|
this._crdtManager = new CrdtManager({ initialStorage });
|
|
799
824
|
}
|
|
825
|
+
/**
|
|
826
|
+
* @deprecated To be removed in v3. Use `getStorageLoaded` instead.
|
|
827
|
+
*/
|
|
800
828
|
get storageLoaded() {
|
|
801
|
-
return this.
|
|
829
|
+
return this.getStorageLoaded();
|
|
802
830
|
}
|
|
803
831
|
get webSocket() {
|
|
804
832
|
return this._state.webSocket;
|
|
805
833
|
}
|
|
834
|
+
addEventListener(kind, handler) {
|
|
835
|
+
return this._listenerManager.subscribe(kind, handler);
|
|
836
|
+
}
|
|
806
837
|
connect(...args) {
|
|
807
838
|
return __async(this, null, function* () {
|
|
808
839
|
var _a;
|
|
@@ -844,6 +875,7 @@ var PluvRoom = class {
|
|
|
844
875
|
this._updateState((oldState) => {
|
|
845
876
|
oldState.connection.attempts = 0;
|
|
846
877
|
oldState.authorization.token = authToken;
|
|
878
|
+
oldState.storage.state = StorageState.Loading;
|
|
847
879
|
oldState.webSocket = webSocket;
|
|
848
880
|
return oldState;
|
|
849
881
|
});
|
|
@@ -864,6 +896,7 @@ var PluvRoom = class {
|
|
|
864
896
|
this._updateState((oldState) => {
|
|
865
897
|
oldState.authorization.token = null;
|
|
866
898
|
oldState.connection.state = ConnectionState.Closed;
|
|
899
|
+
oldState.storage.state = oldState.storage.state === StorageState.Synced ? StorageState.Offline : StorageState.Unavailable;
|
|
867
900
|
oldState.webSocket = null;
|
|
868
901
|
return oldState;
|
|
869
902
|
});
|
|
@@ -1110,7 +1143,6 @@ var PluvRoom = class {
|
|
|
1110
1143
|
const data = message.data;
|
|
1111
1144
|
const state = data.state;
|
|
1112
1145
|
this._updateState((oldState) => {
|
|
1113
|
-
oldState.connection.count += 1;
|
|
1114
1146
|
oldState.connection.id = connectionId;
|
|
1115
1147
|
oldState.authorization.user = user != null ? user : null;
|
|
1116
1148
|
return oldState;
|
|
@@ -1162,6 +1194,10 @@ var PluvRoom = class {
|
|
|
1162
1194
|
const encodedState = this._crdtManager.doc.getEncodedState();
|
|
1163
1195
|
this._emitSharedTypes();
|
|
1164
1196
|
this._observeCrdt();
|
|
1197
|
+
this._updateState((oldState) => {
|
|
1198
|
+
oldState.storage.state = StorageState.Synced;
|
|
1199
|
+
return oldState;
|
|
1200
|
+
});
|
|
1165
1201
|
this._stateNotifier.subjects["storage-loaded"].next(true);
|
|
1166
1202
|
this._sendMessage({
|
|
1167
1203
|
type: "$updateStorage",
|
|
@@ -1274,11 +1310,13 @@ var PluvRoom = class {
|
|
|
1274
1310
|
this._listeners.onAuthorizationFail(authError);
|
|
1275
1311
|
this._updateState((oldState) => {
|
|
1276
1312
|
oldState.connection.state = ConnectionState.Unavailable;
|
|
1313
|
+
oldState.storage.state = StorageState.Unavailable;
|
|
1277
1314
|
return oldState;
|
|
1278
1315
|
});
|
|
1279
1316
|
}
|
|
1280
1317
|
_onClose(event) {
|
|
1281
1318
|
return __async(this, null, function* () {
|
|
1319
|
+
this._listenerManager.subjects.close.next(event);
|
|
1282
1320
|
this._logDebug("WebSocket closed");
|
|
1283
1321
|
if (!!event.reason) this._logDebug(event.reason);
|
|
1284
1322
|
const shouldRetry = [
|
|
@@ -1306,6 +1344,7 @@ var PluvRoom = class {
|
|
|
1306
1344
|
*/
|
|
1307
1345
|
ConnectionState.Closed
|
|
1308
1346
|
);
|
|
1347
|
+
oldState.storage.state = oldState.storage.state === StorageState.Synced ? StorageState.Offline : StorageState.Unavailable;
|
|
1309
1348
|
oldState.webSocket = null;
|
|
1310
1349
|
return oldState;
|
|
1311
1350
|
});
|
|
@@ -1322,9 +1361,11 @@ var PluvRoom = class {
|
|
|
1322
1361
|
* @description Handle websocket errors in a meaningful way, should they occur
|
|
1323
1362
|
* @date August 19, 2022
|
|
1324
1363
|
*/
|
|
1325
|
-
_onError() {
|
|
1364
|
+
_onError(event) {
|
|
1365
|
+
this._listenerManager.subjects.error.next(event);
|
|
1326
1366
|
}
|
|
1327
1367
|
_onMessage(event) {
|
|
1368
|
+
this._listenerManager.subjects.message.next(event);
|
|
1328
1369
|
const message = this._parseMessage(event);
|
|
1329
1370
|
if (!message) return;
|
|
1330
1371
|
const shouldLog = typeof this._debug === "boolean" ? this._debug : this._debug.output.find((value) => value === message.type);
|
|
@@ -1372,7 +1413,8 @@ var PluvRoom = class {
|
|
|
1372
1413
|
default:
|
|
1373
1414
|
}
|
|
1374
1415
|
}
|
|
1375
|
-
_onOpen() {
|
|
1416
|
+
_onOpen(event) {
|
|
1417
|
+
this._listenerManager.subjects.open.next(event);
|
|
1376
1418
|
if (this._state.connection.state !== ConnectionState.Connecting) return;
|
|
1377
1419
|
this._logDebug("WebSocket connected");
|
|
1378
1420
|
this._updateState((oldState) => {
|
|
@@ -1390,6 +1432,7 @@ var PluvRoom = class {
|
|
|
1390
1432
|
if (this._state.connection.state === ConnectionState.Closed) return;
|
|
1391
1433
|
this._updateState((oldState) => {
|
|
1392
1434
|
oldState.connection.state = ConnectionState.Unavailable;
|
|
1435
|
+
oldState.storage.state = oldState.storage.state === StorageState.Synced ? StorageState.Offline : StorageState.Unavailable;
|
|
1393
1436
|
return oldState;
|
|
1394
1437
|
});
|
|
1395
1438
|
});
|
|
@@ -1489,6 +1532,7 @@ var PluvRoom = class {
|
|
|
1489
1532
|
const oldState = __spreadProps(__spreadValues({}, this._state), {
|
|
1490
1533
|
authorization,
|
|
1491
1534
|
connection: JSON.parse(JSON.stringify(this._state.connection)),
|
|
1535
|
+
storage: JSON.parse(JSON.stringify(this._state.storage)),
|
|
1492
1536
|
webSocket: this._state.webSocket
|
|
1493
1537
|
});
|
|
1494
1538
|
const newState = updater(oldState);
|
|
@@ -1602,9 +1646,13 @@ var createClient = (options) => {
|
|
|
1602
1646
|
var infer = (callback) => callback;
|
|
1603
1647
|
|
|
1604
1648
|
// src/MockedRoom.ts
|
|
1605
|
-
import { ConnectionState as ConnectionState2 } from "@pluv/types";
|
|
1649
|
+
import { ConnectionState as ConnectionState2, StorageState as StorageState2 } from "@pluv/types";
|
|
1606
1650
|
var MockedRoom = class {
|
|
1607
1651
|
constructor(room, options) {
|
|
1652
|
+
/**
|
|
1653
|
+
* @deprecated To be removed in v3. Use `getStorageLoaded` instead.
|
|
1654
|
+
*/
|
|
1655
|
+
this.storageLoaded = true;
|
|
1608
1656
|
this._crdtNotifier = new CrdtNotifier();
|
|
1609
1657
|
this._eventNotifier = new EventNotifier();
|
|
1610
1658
|
this._usersNotifier = new UsersNotifier();
|
|
@@ -1615,10 +1663,12 @@ var MockedRoom = class {
|
|
|
1615
1663
|
},
|
|
1616
1664
|
connection: {
|
|
1617
1665
|
attempts: 0,
|
|
1618
|
-
count: 1,
|
|
1619
1666
|
id: null,
|
|
1620
1667
|
state: ConnectionState2.Untouched
|
|
1621
1668
|
},
|
|
1669
|
+
storage: {
|
|
1670
|
+
state: StorageState2.Unavailable
|
|
1671
|
+
},
|
|
1622
1672
|
webSocket: null
|
|
1623
1673
|
};
|
|
1624
1674
|
this._stateNotifier = new StateNotifier();
|
|
@@ -1793,8 +1843,8 @@ var MockedRoom = class {
|
|
|
1793
1843
|
});
|
|
1794
1844
|
this._observeCrdt();
|
|
1795
1845
|
}
|
|
1796
|
-
|
|
1797
|
-
return
|
|
1846
|
+
addEventListener(kind, handler) {
|
|
1847
|
+
return () => void 0;
|
|
1798
1848
|
}
|
|
1799
1849
|
getDoc() {
|
|
1800
1850
|
return this._crdtManager.doc;
|
|
@@ -1804,6 +1854,9 @@ var MockedRoom = class {
|
|
|
1804
1854
|
if (typeof type === "undefined") return this._crdtManager.doc.toJson();
|
|
1805
1855
|
return this._crdtManager.doc.toJson(type);
|
|
1806
1856
|
}
|
|
1857
|
+
getStorageLoaded() {
|
|
1858
|
+
return true;
|
|
1859
|
+
}
|
|
1807
1860
|
_observeCrdt() {
|
|
1808
1861
|
var _a, _b;
|
|
1809
1862
|
(_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/client",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
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.1
|
|
22
|
-
"@pluv/types": "^2.1
|
|
21
|
+
"@pluv/crdt": "^2.2.1",
|
|
22
|
+
"@pluv/types": "^2.2.1"
|
|
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.1
|
|
29
|
-
"eslint-config-pluv": "^2.1
|
|
28
|
+
"@pluv/tsconfig": "^2.2.1",
|
|
29
|
+
"eslint-config-pluv": "^2.2.1"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
package/src/CrdtManager.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { AbstractCrdtDocFactory,
|
|
1
|
+
import type { AbstractCrdtDocFactory, InferDocLike, InferStorage } from "@pluv/crdt";
|
|
2
2
|
import { noop } from "@pluv/crdt";
|
|
3
3
|
|
|
4
|
-
export type CrdtManagerOptions<TCrdt extends AbstractCrdtDocFactory<any>> = {
|
|
4
|
+
export type CrdtManagerOptions<TCrdt extends AbstractCrdtDocFactory<any, any>> = {
|
|
5
5
|
initialStorage?: TCrdt;
|
|
6
6
|
};
|
|
7
7
|
|
|
@@ -15,8 +15,8 @@ interface ApplyUpdateParams {
|
|
|
15
15
|
update: string | readonly string[];
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any>> {
|
|
19
|
-
public doc:
|
|
18
|
+
export class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any, any>> {
|
|
19
|
+
public doc: InferDocLike<TCrdt>;
|
|
20
20
|
public initialized: boolean = false;
|
|
21
21
|
|
|
22
22
|
private readonly _docFactory: TCrdt;
|
|
@@ -25,7 +25,7 @@ export class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any>> {
|
|
|
25
25
|
const { initialStorage = noop.doc({}) as any } = options;
|
|
26
26
|
|
|
27
27
|
this._docFactory = initialStorage;
|
|
28
|
-
this.doc =
|
|
28
|
+
this.doc = this._docFactory.getEmpty() as InferDocLike<TCrdt>;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
@@ -49,7 +49,7 @@ export class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any>> {
|
|
|
49
49
|
public destroy(): void {
|
|
50
50
|
this.initialized = false;
|
|
51
51
|
this.doc.destroy();
|
|
52
|
-
this.doc = this._docFactory.getEmpty()
|
|
52
|
+
this.doc = this._docFactory.getEmpty() as InferDocLike<TCrdt>;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
public get<TKey extends keyof InferStorage<TCrdt>>(key: TKey): InferStorage<TCrdt>[TKey] {
|
|
@@ -94,11 +94,7 @@ export class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any>> {
|
|
|
94
94
|
const initialized = this._docFactory.getInitialized();
|
|
95
95
|
const reference = initialized.get();
|
|
96
96
|
|
|
97
|
-
this.doc = this._applyDocUpdates(
|
|
98
|
-
this._docFactory.getEmpty(),
|
|
99
|
-
updates,
|
|
100
|
-
origin,
|
|
101
|
-
).rebuildStorage(reference);
|
|
97
|
+
this.doc = this._applyDocUpdates(this.doc, updates, origin).rebuildStorage(reference);
|
|
102
98
|
this.initialized = true;
|
|
103
99
|
initialized.destroy();
|
|
104
100
|
|
|
@@ -110,10 +106,10 @@ export class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any>> {
|
|
|
110
106
|
}
|
|
111
107
|
|
|
112
108
|
private _applyDocUpdates(
|
|
113
|
-
doc:
|
|
109
|
+
doc: InferDocLike<TCrdt>,
|
|
114
110
|
updates: readonly string[],
|
|
115
111
|
origin?: string,
|
|
116
|
-
):
|
|
112
|
+
): InferDocLike<TCrdt> {
|
|
117
113
|
doc.transact(() => {
|
|
118
114
|
doc.batchApplyEncodedState({ origin, updates });
|
|
119
115
|
}, origin);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { makeSubject, subscribe, TypeOfSource } from "wonka";
|
|
2
|
+
|
|
3
|
+
export class ListenerManager {
|
|
4
|
+
public subjects = {
|
|
5
|
+
close: makeSubject<CloseEvent>(),
|
|
6
|
+
error: makeSubject<Event>(),
|
|
7
|
+
message: makeSubject<MessageEvent<any>>(),
|
|
8
|
+
open: makeSubject<Event>(),
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
public subscribe<TKind extends keyof typeof this.subjects>(
|
|
12
|
+
kind: TKind,
|
|
13
|
+
callback: (event: TypeOfSource<(typeof this.subjects)[TKind]["source"]>) => void,
|
|
14
|
+
): () => void {
|
|
15
|
+
const source = this.subjects[kind]?.source;
|
|
16
|
+
|
|
17
|
+
if (!source) return () => undefined;
|
|
18
|
+
|
|
19
|
+
return subscribe(callback)(source as any).unsubscribe;
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/MockedRoom.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
AbstractCrdtDocFactory,
|
|
3
|
+
InferCrdtJson,
|
|
4
|
+
InferDoc,
|
|
5
|
+
InferDocLike,
|
|
6
|
+
InferStorage,
|
|
7
|
+
} from "@pluv/crdt";
|
|
2
8
|
import type {
|
|
3
9
|
BroadcastProxy,
|
|
4
10
|
CrdtDocLike,
|
|
@@ -8,10 +14,10 @@ import type {
|
|
|
8
14
|
Id,
|
|
9
15
|
InferIOInput,
|
|
10
16
|
InferIOOutput,
|
|
11
|
-
JsonObject,
|
|
12
17
|
MergeEvents,
|
|
13
18
|
OtherSubscriptionCallback,
|
|
14
19
|
OthersSubscriptionCallback,
|
|
20
|
+
RoomEventListenerMap,
|
|
15
21
|
RoomLike,
|
|
16
22
|
StateNotifierSubjects,
|
|
17
23
|
StorageProxy,
|
|
@@ -21,9 +27,10 @@ import type {
|
|
|
21
27
|
SubscriptionCallback,
|
|
22
28
|
UpdateMyPresenceAction,
|
|
23
29
|
UserInfo,
|
|
30
|
+
WebSocketConnection,
|
|
24
31
|
WebSocketState,
|
|
25
32
|
} from "@pluv/types";
|
|
26
|
-
import { ConnectionState } from "@pluv/types";
|
|
33
|
+
import { ConnectionState, StorageState } from "@pluv/types";
|
|
27
34
|
import type { CrdtManagerOptions } from "./CrdtManager";
|
|
28
35
|
import { CrdtManager } from "./CrdtManager";
|
|
29
36
|
import { CrdtNotifier } from "./CrdtNotifier";
|
|
@@ -41,7 +48,6 @@ import type {
|
|
|
41
48
|
EventResolverContext,
|
|
42
49
|
InternalSubscriptions,
|
|
43
50
|
PluvClientLimits,
|
|
44
|
-
WebSocketConnection,
|
|
45
51
|
} from "./types";
|
|
46
52
|
|
|
47
53
|
export type MockedRoomEvents<TIO extends IOLike> = Partial<{
|
|
@@ -50,8 +56,8 @@ export type MockedRoomEvents<TIO extends IOLike> = Partial<{
|
|
|
50
56
|
|
|
51
57
|
export type MockedRoomConfig<
|
|
52
58
|
TIO extends IOLike,
|
|
53
|
-
TPresence extends
|
|
54
|
-
TCrdt extends AbstractCrdtDocFactory<any>,
|
|
59
|
+
TPresence extends Record<string, any>,
|
|
60
|
+
TCrdt extends AbstractCrdtDocFactory<any, any>,
|
|
55
61
|
TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>>,
|
|
56
62
|
> = {
|
|
57
63
|
events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
|
|
@@ -62,13 +68,18 @@ export type MockedRoomConfig<
|
|
|
62
68
|
|
|
63
69
|
export class MockedRoom<
|
|
64
70
|
TIO extends IOLike,
|
|
65
|
-
TPresence extends
|
|
66
|
-
TCrdt extends AbstractCrdtDocFactory<any>,
|
|
71
|
+
TPresence extends Record<string, any>,
|
|
72
|
+
TCrdt extends AbstractCrdtDocFactory<any, any>,
|
|
67
73
|
TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>>,
|
|
68
|
-
> implements RoomLike<TIO, TPresence, InferStorage<TCrdt>>
|
|
74
|
+
> implements RoomLike<TIO, InferDoc<TCrdt>, TPresence, InferStorage<TCrdt>>
|
|
69
75
|
{
|
|
70
76
|
public readonly id: string;
|
|
71
77
|
|
|
78
|
+
/**
|
|
79
|
+
* @deprecated To be removed in v3. Use `getStorageLoaded` instead.
|
|
80
|
+
*/
|
|
81
|
+
public readonly storageLoaded: boolean = true;
|
|
82
|
+
|
|
72
83
|
private readonly _crdtManager: CrdtManager<TCrdt>;
|
|
73
84
|
private readonly _crdtNotifier = new CrdtNotifier<InferStorage<TCrdt>>();
|
|
74
85
|
private readonly _eventNotifier = new EventNotifier<MergeEvents<TEvents, TIO>>();
|
|
@@ -83,10 +94,12 @@ export class MockedRoom<
|
|
|
83
94
|
},
|
|
84
95
|
connection: {
|
|
85
96
|
attempts: 0,
|
|
86
|
-
count: 1,
|
|
87
97
|
id: null,
|
|
88
98
|
state: ConnectionState.Untouched,
|
|
89
99
|
},
|
|
100
|
+
storage: {
|
|
101
|
+
state: StorageState.Unavailable,
|
|
102
|
+
},
|
|
90
103
|
webSocket: null,
|
|
91
104
|
};
|
|
92
105
|
private readonly _stateNotifier = new StateNotifier<TIO, TPresence>();
|
|
@@ -121,8 +134,11 @@ export class MockedRoom<
|
|
|
121
134
|
this._observeCrdt();
|
|
122
135
|
}
|
|
123
136
|
|
|
124
|
-
public
|
|
125
|
-
|
|
137
|
+
public addEventListener<TKind extends keyof RoomEventListenerMap>(
|
|
138
|
+
kind: TKind,
|
|
139
|
+
handler: RoomEventListenerMap[TKind],
|
|
140
|
+
): () => void {
|
|
141
|
+
return () => undefined;
|
|
126
142
|
}
|
|
127
143
|
|
|
128
144
|
public broadcast = new Proxy(
|
|
@@ -140,7 +156,8 @@ export class MockedRoom<
|
|
|
140
156
|
any,
|
|
141
157
|
any,
|
|
142
158
|
TPresence,
|
|
143
|
-
|
|
159
|
+
TCrdt,
|
|
160
|
+
""
|
|
144
161
|
> | null;
|
|
145
162
|
|
|
146
163
|
if (!procedure?.config.broadcast) {
|
|
@@ -154,7 +171,7 @@ export class MockedRoom<
|
|
|
154
171
|
if (!myself) return;
|
|
155
172
|
|
|
156
173
|
const parsed = procedure.config.input ? procedure.config.input.parse(data) : data;
|
|
157
|
-
const context: EventResolverContext<TIO, TPresence,
|
|
174
|
+
const context: EventResolverContext<TIO, TPresence, InferDocLike<TCrdt>> = {
|
|
158
175
|
doc: this._crdtManager.doc,
|
|
159
176
|
others: this._usersManager.getOthers(),
|
|
160
177
|
room: this.id,
|
|
@@ -213,7 +230,7 @@ export class MockedRoom<
|
|
|
213
230
|
return Object.freeze(JSON.parse(JSON.stringify(this._state.connection)));
|
|
214
231
|
};
|
|
215
232
|
|
|
216
|
-
public getDoc(): CrdtDocLike<InferStorage<TCrdt>> {
|
|
233
|
+
public getDoc(): CrdtDocLike<InferDoc<TCrdt>, InferStorage<TCrdt>> {
|
|
217
234
|
return this._crdtManager.doc;
|
|
218
235
|
}
|
|
219
236
|
|
|
@@ -249,12 +266,15 @@ export class MockedRoom<
|
|
|
249
266
|
): InferCrdtJson<InferStorage<TCrdt>[TKey]> | null;
|
|
250
267
|
public getStorageJson<TKey extends keyof InferStorage<TCrdt>>(type?: TKey) {
|
|
251
268
|
if (this._state.connection.id === null) return null;
|
|
252
|
-
|
|
253
269
|
if (typeof type === "undefined") return this._crdtManager.doc.toJson();
|
|
254
270
|
|
|
255
271
|
return this._crdtManager.doc.toJson(type);
|
|
256
272
|
}
|
|
257
273
|
|
|
274
|
+
public getStorageLoaded(): boolean {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
|
|
258
278
|
public other = (
|
|
259
279
|
connectionId: string,
|
|
260
280
|
callback: OtherSubscriptionCallback<TIO, TPresence>,
|
package/src/PluvClient.ts
CHANGED
|
@@ -24,8 +24,8 @@ import type { PluvClientLimits, PublicKey, WithMetadata } from "./types";
|
|
|
24
24
|
|
|
25
25
|
export type PluvClientOptions<
|
|
26
26
|
TIO extends IOLike<any, any>,
|
|
27
|
-
TPresence extends
|
|
28
|
-
TCrdt extends AbstractCrdtDocFactory<any>,
|
|
27
|
+
TPresence extends Record<string, any>,
|
|
28
|
+
TCrdt extends AbstractCrdtDocFactory<any, any>,
|
|
29
29
|
TMetadata extends JsonObject,
|
|
30
30
|
> = RoomEndpoints<TIO, TMetadata> & {
|
|
31
31
|
debug?: boolean;
|
|
@@ -43,8 +43,8 @@ export type PluvClientOptions<
|
|
|
43
43
|
|
|
44
44
|
export type CreateRoomOptions<
|
|
45
45
|
TIO extends IOLike<any, any>,
|
|
46
|
-
TPresence extends
|
|
47
|
-
TCrdt extends AbstractCrdtDocFactory<any>,
|
|
46
|
+
TPresence extends Record<string, any>,
|
|
47
|
+
TCrdt extends AbstractCrdtDocFactory<any, any>,
|
|
48
48
|
TMetadata extends JsonObject,
|
|
49
49
|
TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},
|
|
50
50
|
> = {
|
|
@@ -63,8 +63,8 @@ export type EnterRoomParams<TMetadata extends JsonObject = {}> = keyof TMetadata
|
|
|
63
63
|
|
|
64
64
|
export class PluvClient<
|
|
65
65
|
TIO extends IOLike<any, any>,
|
|
66
|
-
TPresence extends
|
|
67
|
-
TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory,
|
|
66
|
+
TPresence extends Record<string, any> = {},
|
|
67
|
+
TCrdt extends AbstractCrdtDocFactory<any, any> = NoopCrdtDocFactory,
|
|
68
68
|
TMetadata extends JsonObject = {},
|
|
69
69
|
> {
|
|
70
70
|
public readonly metadata?: InputZodLike<TMetadata>;
|
|
@@ -84,8 +84,8 @@ export class PluvClient<
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
public get procedure(): PluvProcedure<TIO, {}, {}, TPresence,
|
|
88
|
-
return new PluvProcedure<TIO, {}, {}, TPresence,
|
|
87
|
+
public get procedure(): PluvProcedure<TIO, {}, {}, TPresence, TCrdt, ""> {
|
|
88
|
+
return new PluvProcedure<TIO, {}, {}, TPresence, TCrdt, "">();
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
constructor(options: PluvClientOptions<TIO, TPresence, TCrdt, TMetadata>) {
|