@pluv/client 1.0.0 → 1.0.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 +8 -0
- package/dist/index.d.mts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +70 -42
- package/dist/index.mjs +70 -42
- package/package.json +5 -5
- package/src/AbstractRoom.ts +3 -1
- package/src/CrdtManager.ts +35 -23
- package/src/CrdtNotifier.ts +4 -1
- package/src/EventNotifier.ts +11 -5
- package/src/MockedRoom.ts +21 -11
- package/src/OtherNotifier.ts +8 -2
- package/src/PluvClient.ts +3 -1
- package/src/PluvProcedure.ts +6 -2
- package/src/PluvRoom.ts +89 -42
- package/src/PluvRouter.ts +4 -1
- package/src/StateNotifier.ts +4 -1
- package/src/UsersManager.ts +23 -5
- package/src/index.ts +8 -1
- package/src/infer.ts +3 -1
- package/src/register.ts +2 -1
- package/src/types.ts +7 -2
- package/src/utils/pickBy.ts +4 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @pluv/client@1.0.
|
|
2
|
+
> @pluv/client@1.0.1 build /home/runner/work/pluv/pluv/packages/client
|
|
3
3
|
> tsup src/index.ts --format esm,cjs --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
[34mCLI[39m Target: es6
|
|
9
9
|
[34mESM[39m Build start
|
|
10
10
|
[34mCJS[39m Build start
|
|
11
|
-
[32mCJS[39m [1mdist/index.js [22m[32m58.
|
|
12
|
-
[32mCJS[39m ⚡️ Build success in
|
|
13
|
-
[32mESM[39m [1mdist/index.mjs [22m[32m56.
|
|
14
|
-
[32mESM[39m ⚡️ Build success in
|
|
11
|
+
[32mCJS[39m [1mdist/index.js [22m[32m58.07 KB[39m
|
|
12
|
+
[32mCJS[39m ⚡️ Build success in 125ms
|
|
13
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m56.75 KB[39m
|
|
14
|
+
[32mESM[39m ⚡️ Build success in 127ms
|
|
15
15
|
[34mDTS[39m Build start
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m25.
|
|
18
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m25.
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 3647ms
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m25.34 KB[39m
|
|
18
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m25.34 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @pluv/client
|
|
2
2
|
|
|
3
|
+
## 1.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c583be9: Fixed storage race condition that occurred when 2 users joined a room near simultaneously that would cause users to have mismatched storage states from the server (and thereby preventing updates from properly registering) until the user reconnected.
|
|
8
|
+
- @pluv/crdt@1.0.1
|
|
9
|
+
- @pluv/types@1.0.1
|
|
10
|
+
|
|
3
11
|
## 1.0.0
|
|
4
12
|
|
|
5
13
|
### Major Changes
|
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -126,9 +126,9 @@ var import_crdt = require("@pluv/crdt");
|
|
|
126
126
|
var CrdtManager = class {
|
|
127
127
|
constructor(options) {
|
|
128
128
|
this.initialized = false;
|
|
129
|
-
const {
|
|
129
|
+
const { initialStorage = import_crdt.noop.doc({}) } = options;
|
|
130
130
|
this._docFactory = initialStorage;
|
|
131
|
-
this.doc =
|
|
131
|
+
this.doc = initialStorage.getEmpty();
|
|
132
132
|
}
|
|
133
133
|
/**
|
|
134
134
|
* !HACK
|
|
@@ -148,25 +148,30 @@ var CrdtManager = class {
|
|
|
148
148
|
get(key) {
|
|
149
149
|
return this.doc.get(key);
|
|
150
150
|
}
|
|
151
|
+
getInitialState() {
|
|
152
|
+
return this._docFactory.getInitialized().getEncodedState();
|
|
153
|
+
}
|
|
151
154
|
initialize(params) {
|
|
152
155
|
const { onInitialized, origin, update } = params;
|
|
153
|
-
const updates = typeof update === "string" ? [update] : update;
|
|
154
|
-
if (!updates.length) return this;
|
|
155
156
|
if (this.initialized) {
|
|
156
|
-
|
|
157
|
+
console.warn("Attempted to initialize storage multiple times");
|
|
157
158
|
return this;
|
|
158
159
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
onEmpty.destroy();
|
|
162
|
-
this.doc = this._applyDocUpdates(this._docFactory.getFresh(), updates, origin).track();
|
|
163
|
-
this.initialized = true;
|
|
164
|
-
onInitialized == null ? void 0 : onInitialized(this.doc.getEncodedState());
|
|
160
|
+
if (!this.doc.isEmpty()) {
|
|
161
|
+
console.warn("Attempted to initialize over a non-empty storage");
|
|
165
162
|
return this;
|
|
166
163
|
}
|
|
167
|
-
|
|
168
|
-
|
|
164
|
+
const updates = typeof update === "string" ? [update] : update;
|
|
165
|
+
if (!updates.length) return this;
|
|
166
|
+
const initialized = this._docFactory.getInitialized();
|
|
167
|
+
const reference = initialized.get();
|
|
168
|
+
this.doc = this._applyDocUpdates(
|
|
169
|
+
this._docFactory.getEmpty(),
|
|
170
|
+
updates,
|
|
171
|
+
origin
|
|
172
|
+
).rebuildStorage(reference);
|
|
169
173
|
this.initialized = true;
|
|
174
|
+
initialized.destroy();
|
|
170
175
|
onInitialized == null ? void 0 : onInitialized(this.doc.getEncodedState());
|
|
171
176
|
return this;
|
|
172
177
|
}
|
|
@@ -264,7 +269,10 @@ var PluvRouter = class _PluvRouter {
|
|
|
264
269
|
this._defs = { events };
|
|
265
270
|
}
|
|
266
271
|
static merge(...routers) {
|
|
267
|
-
const events = Object.assign(
|
|
272
|
+
const events = Object.assign(
|
|
273
|
+
/* @__PURE__ */ Object.create(null),
|
|
274
|
+
...routers.map((router) => router._defs.events)
|
|
275
|
+
);
|
|
268
276
|
if (Object.keys(events).some((key) => key.startsWith("$"))) {
|
|
269
277
|
throw new Error('Procedures may not start with "$"');
|
|
270
278
|
}
|
|
@@ -417,7 +425,11 @@ var UsersManager = class {
|
|
|
417
425
|
const userInfo = (_a = this._others.get(clientId)) != null ? _a : null;
|
|
418
426
|
if (!userInfo) return null;
|
|
419
427
|
const remaining = (_c = (_b = this._getConnectionIds(clientId)) == null ? void 0 : _b.size) != null ? _c : 0;
|
|
420
|
-
const result = {
|
|
428
|
+
const result = {
|
|
429
|
+
clientId,
|
|
430
|
+
remaining,
|
|
431
|
+
data: userInfo
|
|
432
|
+
};
|
|
421
433
|
if (!remaining) this._others.delete(clientId);
|
|
422
434
|
return result;
|
|
423
435
|
}
|
|
@@ -454,7 +466,10 @@ var UsersManager = class {
|
|
|
454
466
|
const other = this._others.get(clientId);
|
|
455
467
|
if (!other) return null;
|
|
456
468
|
const cleanedPatch = pickBy(patch != null ? patch : {}, (value) => typeof value !== "undefined");
|
|
457
|
-
const cleanedPresence = pickBy(
|
|
469
|
+
const cleanedPresence = pickBy(
|
|
470
|
+
(_a = other.presence) != null ? _a : {},
|
|
471
|
+
(value) => typeof value !== "undefined"
|
|
472
|
+
);
|
|
458
473
|
const presence = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
|
|
459
474
|
const validated = this._presence ? this._presence.parse(presence) : presence;
|
|
460
475
|
this._others.set(clientId, __spreadProps(__spreadValues({}, other), { presence: validated }));
|
|
@@ -623,10 +638,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
623
638
|
room: this.id,
|
|
624
639
|
user: myself
|
|
625
640
|
};
|
|
626
|
-
const output = yield procedure.config.broadcast(
|
|
627
|
-
parsed,
|
|
628
|
-
context
|
|
629
|
-
);
|
|
641
|
+
const output = yield procedure.config.broadcast(parsed, context);
|
|
630
642
|
Object.entries(output).forEach(([_type, _data]) => {
|
|
631
643
|
this._sendMessage({ data: _data, type: _type });
|
|
632
644
|
});
|
|
@@ -749,7 +761,11 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
749
761
|
}
|
|
750
762
|
};
|
|
751
763
|
this._router = router != null ? router : new PluvRouter({});
|
|
752
|
-
this._usersManager = new UsersManager({
|
|
764
|
+
this._usersManager = new UsersManager({
|
|
765
|
+
initialPresence,
|
|
766
|
+
limits: this._limits,
|
|
767
|
+
presence
|
|
768
|
+
});
|
|
753
769
|
this._crdtManager = new CrdtManager({ initialStorage });
|
|
754
770
|
}
|
|
755
771
|
get storageLoaded() {
|
|
@@ -764,17 +780,17 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
764
780
|
const params = (_a = args[0]) != null ? _a : {};
|
|
765
781
|
const metadata = params.metadata;
|
|
766
782
|
this._setMetadata(metadata);
|
|
767
|
-
const canConnect = [
|
|
768
|
-
|
|
769
|
-
|
|
783
|
+
const canConnect = [
|
|
784
|
+
ConnectionState.Closed,
|
|
785
|
+
ConnectionState.Unavailable,
|
|
786
|
+
ConnectionState.Untouched
|
|
787
|
+
].some((state) => this._state.connection.state === state);
|
|
770
788
|
if (!canConnect) return;
|
|
771
789
|
this._clearTimeout(this._timeouts.reconnect);
|
|
772
790
|
this._updateState((oldState) => {
|
|
773
791
|
oldState.connection.state = ConnectionState.Connecting;
|
|
774
792
|
return oldState;
|
|
775
793
|
});
|
|
776
|
-
yield this._storageStore.initialize();
|
|
777
|
-
yield this._applyStorageStore();
|
|
778
794
|
const wsEndpoint = this._getWsEndpoint(this.id, params);
|
|
779
795
|
const url = new URL(wsEndpoint);
|
|
780
796
|
let authToken = null;
|
|
@@ -1043,12 +1059,13 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1043
1059
|
this._stateNotifier.subjects.others.next(others);
|
|
1044
1060
|
}
|
|
1045
1061
|
_handleRegisteredMessage(message) {
|
|
1046
|
-
var _a, _b
|
|
1062
|
+
var _a, _b;
|
|
1047
1063
|
const { connectionId } = message;
|
|
1048
1064
|
const user = message.user;
|
|
1049
1065
|
if (!connectionId) return;
|
|
1050
1066
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1051
1067
|
const data = message.data;
|
|
1068
|
+
const state = data.state;
|
|
1052
1069
|
this._updateState((oldState) => {
|
|
1053
1070
|
oldState.connection.count += 1;
|
|
1054
1071
|
oldState.connection.id = connectionId;
|
|
@@ -1065,7 +1082,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1065
1082
|
const myself = (_b = this._usersManager.myself) != null ? _b : null;
|
|
1066
1083
|
this._stateNotifier.subjects["my-presence"].next(presence);
|
|
1067
1084
|
this._stateNotifier.subjects.myself.next(myself);
|
|
1068
|
-
const update = this.
|
|
1085
|
+
const update = this._crdtManager.getInitialState();
|
|
1069
1086
|
this._sendMessage({
|
|
1070
1087
|
type: "$initializeSession",
|
|
1071
1088
|
data: { presence, update }
|
|
@@ -1077,15 +1094,16 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1077
1094
|
if (!connectionId) return;
|
|
1078
1095
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1079
1096
|
const data = message.data;
|
|
1097
|
+
const state = data.state;
|
|
1080
1098
|
this._crdtManager.initialize({
|
|
1081
1099
|
onInitialized: (update2) => __async(this, null, function* () {
|
|
1082
|
-
this._addToStorageStore(update2);
|
|
1100
|
+
yield this._addToStorageStore(update2);
|
|
1083
1101
|
this._emitSharedTypes();
|
|
1084
1102
|
this._observeCrdt();
|
|
1085
1103
|
this._stateNotifier.subjects["storage-loaded"].next(true);
|
|
1086
1104
|
}),
|
|
1087
1105
|
origin: ORIGIN_INITIALIZED,
|
|
1088
|
-
update:
|
|
1106
|
+
update: state
|
|
1089
1107
|
});
|
|
1090
1108
|
const encodedState = this._crdtManager.doc.getEncodedState();
|
|
1091
1109
|
yield this._storageStore.addUpdate(encodedState);
|
|
@@ -1102,7 +1120,6 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1102
1120
|
if (!connectionId) return;
|
|
1103
1121
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1104
1122
|
const data = message.data;
|
|
1105
|
-
if (!this._crdtManager) return;
|
|
1106
1123
|
this._crdtManager.doc.applyEncodedState({
|
|
1107
1124
|
origin: ORIGIN_STORAGE_UPDATED,
|
|
1108
1125
|
update: data.state
|
|
@@ -1110,7 +1127,6 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1110
1127
|
const sharedTypes = this._crdtManager.doc.get();
|
|
1111
1128
|
const storageRoot = Object.keys(sharedTypes).reduce(
|
|
1112
1129
|
(acc, prop) => {
|
|
1113
|
-
if (!this._crdtManager) return acc;
|
|
1114
1130
|
const serialized = this._crdtManager.doc.toJson(prop);
|
|
1115
1131
|
this._crdtNotifier.subject(prop).next(serialized);
|
|
1116
1132
|
return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
|
|
@@ -1157,7 +1173,10 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1157
1173
|
}
|
|
1158
1174
|
_heartbeat() {
|
|
1159
1175
|
this._clearTimeout(this._timeouts.pong);
|
|
1160
|
-
this._timeouts.pong = setTimeout(
|
|
1176
|
+
this._timeouts.pong = setTimeout(
|
|
1177
|
+
this._reconnect.bind(this),
|
|
1178
|
+
PONG_TIMEOUT_MS
|
|
1179
|
+
);
|
|
1161
1180
|
this._sendMessage('{"type":"$ping","data":{}}');
|
|
1162
1181
|
}
|
|
1163
1182
|
_logDebug(...data) {
|
|
@@ -1168,7 +1187,6 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1168
1187
|
_observeCrdt() {
|
|
1169
1188
|
var _a, _b;
|
|
1170
1189
|
(_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
|
|
1171
|
-
if (!this._crdtManager) return;
|
|
1172
1190
|
const unsubscribe = this._crdtManager.doc.subscribe((event) => {
|
|
1173
1191
|
var _a2;
|
|
1174
1192
|
const origin = (_a2 = event.origin) != null ? _a2 : null;
|
|
@@ -1298,7 +1316,10 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1298
1316
|
return oldState;
|
|
1299
1317
|
});
|
|
1300
1318
|
this._clearInterval(this._intervals.heartbeat);
|
|
1301
|
-
this._intervals.heartbeat = setInterval(
|
|
1319
|
+
this._intervals.heartbeat = setInterval(
|
|
1320
|
+
this._heartbeat.bind(this),
|
|
1321
|
+
HEARTBEAT_INTERVAL_MS
|
|
1322
|
+
);
|
|
1302
1323
|
}
|
|
1303
1324
|
_onNavigatorOffline() {
|
|
1304
1325
|
return __async(this, null, function* () {
|
|
@@ -1353,8 +1374,14 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1353
1374
|
_pollReconnect() {
|
|
1354
1375
|
this._clearTimeout(this._timeouts.reconnect);
|
|
1355
1376
|
const timeoutMs = typeof this._reconnectTimeoutMs === "number" ? this._reconnectTimeoutMs : this._reconnectTimeoutMs({ attempts: this._state.connection.attempts });
|
|
1356
|
-
const clampedMs = Math.max(
|
|
1357
|
-
|
|
1377
|
+
const clampedMs = Math.max(
|
|
1378
|
+
Math.min(MIN_RECONNECT_TIMEOUT_MS, timeoutMs),
|
|
1379
|
+
MAX_RECONNECT_TIMEOUT_MS
|
|
1380
|
+
);
|
|
1381
|
+
this._timeouts.reconnect = setTimeout(
|
|
1382
|
+
this._reconnect.bind(this),
|
|
1383
|
+
clampedMs
|
|
1384
|
+
);
|
|
1358
1385
|
}
|
|
1359
1386
|
_reconnect() {
|
|
1360
1387
|
return __async(this, null, function* () {
|
|
@@ -1553,10 +1580,7 @@ var MockedRoom = class extends AbstractRoom {
|
|
|
1553
1580
|
room: this.id,
|
|
1554
1581
|
user: myself
|
|
1555
1582
|
};
|
|
1556
|
-
const output = yield procedure.config.broadcast(
|
|
1557
|
-
parsed,
|
|
1558
|
-
context
|
|
1559
|
-
);
|
|
1583
|
+
const output = yield procedure.config.broadcast(parsed, context);
|
|
1560
1584
|
Object.entries(output).forEach(([_type, _data]) => {
|
|
1561
1585
|
this._simulateEvent(_type, _data);
|
|
1562
1586
|
});
|
|
@@ -1647,7 +1671,11 @@ var MockedRoom = class extends AbstractRoom {
|
|
|
1647
1671
|
presenceMaxSize: MAX_PRESENCE_SIZE_BYTES
|
|
1648
1672
|
}, limits);
|
|
1649
1673
|
this._router = router != null ? router : new PluvRouter({});
|
|
1650
|
-
this._usersManager = new UsersManager({
|
|
1674
|
+
this._usersManager = new UsersManager({
|
|
1675
|
+
initialPresence,
|
|
1676
|
+
limits: this._limits,
|
|
1677
|
+
presence
|
|
1678
|
+
});
|
|
1651
1679
|
this._crdtManager = new CrdtManager({
|
|
1652
1680
|
initialStorage
|
|
1653
1681
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -95,9 +95,9 @@ import { noop } from "@pluv/crdt";
|
|
|
95
95
|
var CrdtManager = class {
|
|
96
96
|
constructor(options) {
|
|
97
97
|
this.initialized = false;
|
|
98
|
-
const {
|
|
98
|
+
const { initialStorage = noop.doc({}) } = options;
|
|
99
99
|
this._docFactory = initialStorage;
|
|
100
|
-
this.doc =
|
|
100
|
+
this.doc = initialStorage.getEmpty();
|
|
101
101
|
}
|
|
102
102
|
/**
|
|
103
103
|
* !HACK
|
|
@@ -117,25 +117,30 @@ var CrdtManager = class {
|
|
|
117
117
|
get(key) {
|
|
118
118
|
return this.doc.get(key);
|
|
119
119
|
}
|
|
120
|
+
getInitialState() {
|
|
121
|
+
return this._docFactory.getInitialized().getEncodedState();
|
|
122
|
+
}
|
|
120
123
|
initialize(params) {
|
|
121
124
|
const { onInitialized, origin, update } = params;
|
|
122
|
-
const updates = typeof update === "string" ? [update] : update;
|
|
123
|
-
if (!updates.length) return this;
|
|
124
125
|
if (this.initialized) {
|
|
125
|
-
|
|
126
|
+
console.warn("Attempted to initialize storage multiple times");
|
|
126
127
|
return this;
|
|
127
128
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
onEmpty.destroy();
|
|
131
|
-
this.doc = this._applyDocUpdates(this._docFactory.getFresh(), updates, origin).track();
|
|
132
|
-
this.initialized = true;
|
|
133
|
-
onInitialized == null ? void 0 : onInitialized(this.doc.getEncodedState());
|
|
129
|
+
if (!this.doc.isEmpty()) {
|
|
130
|
+
console.warn("Attempted to initialize over a non-empty storage");
|
|
134
131
|
return this;
|
|
135
132
|
}
|
|
136
|
-
|
|
137
|
-
|
|
133
|
+
const updates = typeof update === "string" ? [update] : update;
|
|
134
|
+
if (!updates.length) return this;
|
|
135
|
+
const initialized = this._docFactory.getInitialized();
|
|
136
|
+
const reference = initialized.get();
|
|
137
|
+
this.doc = this._applyDocUpdates(
|
|
138
|
+
this._docFactory.getEmpty(),
|
|
139
|
+
updates,
|
|
140
|
+
origin
|
|
141
|
+
).rebuildStorage(reference);
|
|
138
142
|
this.initialized = true;
|
|
143
|
+
initialized.destroy();
|
|
139
144
|
onInitialized == null ? void 0 : onInitialized(this.doc.getEncodedState());
|
|
140
145
|
return this;
|
|
141
146
|
}
|
|
@@ -233,7 +238,10 @@ var PluvRouter = class _PluvRouter {
|
|
|
233
238
|
this._defs = { events };
|
|
234
239
|
}
|
|
235
240
|
static merge(...routers) {
|
|
236
|
-
const events = Object.assign(
|
|
241
|
+
const events = Object.assign(
|
|
242
|
+
/* @__PURE__ */ Object.create(null),
|
|
243
|
+
...routers.map((router) => router._defs.events)
|
|
244
|
+
);
|
|
237
245
|
if (Object.keys(events).some((key) => key.startsWith("$"))) {
|
|
238
246
|
throw new Error('Procedures may not start with "$"');
|
|
239
247
|
}
|
|
@@ -386,7 +394,11 @@ var UsersManager = class {
|
|
|
386
394
|
const userInfo = (_a = this._others.get(clientId)) != null ? _a : null;
|
|
387
395
|
if (!userInfo) return null;
|
|
388
396
|
const remaining = (_c = (_b = this._getConnectionIds(clientId)) == null ? void 0 : _b.size) != null ? _c : 0;
|
|
389
|
-
const result = {
|
|
397
|
+
const result = {
|
|
398
|
+
clientId,
|
|
399
|
+
remaining,
|
|
400
|
+
data: userInfo
|
|
401
|
+
};
|
|
390
402
|
if (!remaining) this._others.delete(clientId);
|
|
391
403
|
return result;
|
|
392
404
|
}
|
|
@@ -423,7 +435,10 @@ var UsersManager = class {
|
|
|
423
435
|
const other = this._others.get(clientId);
|
|
424
436
|
if (!other) return null;
|
|
425
437
|
const cleanedPatch = pickBy(patch != null ? patch : {}, (value) => typeof value !== "undefined");
|
|
426
|
-
const cleanedPresence = pickBy(
|
|
438
|
+
const cleanedPresence = pickBy(
|
|
439
|
+
(_a = other.presence) != null ? _a : {},
|
|
440
|
+
(value) => typeof value !== "undefined"
|
|
441
|
+
);
|
|
427
442
|
const presence = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
|
|
428
443
|
const validated = this._presence ? this._presence.parse(presence) : presence;
|
|
429
444
|
this._others.set(clientId, __spreadProps(__spreadValues({}, other), { presence: validated }));
|
|
@@ -592,10 +607,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
592
607
|
room: this.id,
|
|
593
608
|
user: myself
|
|
594
609
|
};
|
|
595
|
-
const output = yield procedure.config.broadcast(
|
|
596
|
-
parsed,
|
|
597
|
-
context
|
|
598
|
-
);
|
|
610
|
+
const output = yield procedure.config.broadcast(parsed, context);
|
|
599
611
|
Object.entries(output).forEach(([_type, _data]) => {
|
|
600
612
|
this._sendMessage({ data: _data, type: _type });
|
|
601
613
|
});
|
|
@@ -718,7 +730,11 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
718
730
|
}
|
|
719
731
|
};
|
|
720
732
|
this._router = router != null ? router : new PluvRouter({});
|
|
721
|
-
this._usersManager = new UsersManager({
|
|
733
|
+
this._usersManager = new UsersManager({
|
|
734
|
+
initialPresence,
|
|
735
|
+
limits: this._limits,
|
|
736
|
+
presence
|
|
737
|
+
});
|
|
722
738
|
this._crdtManager = new CrdtManager({ initialStorage });
|
|
723
739
|
}
|
|
724
740
|
get storageLoaded() {
|
|
@@ -733,17 +749,17 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
733
749
|
const params = (_a = args[0]) != null ? _a : {};
|
|
734
750
|
const metadata = params.metadata;
|
|
735
751
|
this._setMetadata(metadata);
|
|
736
|
-
const canConnect = [
|
|
737
|
-
|
|
738
|
-
|
|
752
|
+
const canConnect = [
|
|
753
|
+
ConnectionState.Closed,
|
|
754
|
+
ConnectionState.Unavailable,
|
|
755
|
+
ConnectionState.Untouched
|
|
756
|
+
].some((state) => this._state.connection.state === state);
|
|
739
757
|
if (!canConnect) return;
|
|
740
758
|
this._clearTimeout(this._timeouts.reconnect);
|
|
741
759
|
this._updateState((oldState) => {
|
|
742
760
|
oldState.connection.state = ConnectionState.Connecting;
|
|
743
761
|
return oldState;
|
|
744
762
|
});
|
|
745
|
-
yield this._storageStore.initialize();
|
|
746
|
-
yield this._applyStorageStore();
|
|
747
763
|
const wsEndpoint = this._getWsEndpoint(this.id, params);
|
|
748
764
|
const url = new URL(wsEndpoint);
|
|
749
765
|
let authToken = null;
|
|
@@ -1012,12 +1028,13 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1012
1028
|
this._stateNotifier.subjects.others.next(others);
|
|
1013
1029
|
}
|
|
1014
1030
|
_handleRegisteredMessage(message) {
|
|
1015
|
-
var _a, _b
|
|
1031
|
+
var _a, _b;
|
|
1016
1032
|
const { connectionId } = message;
|
|
1017
1033
|
const user = message.user;
|
|
1018
1034
|
if (!connectionId) return;
|
|
1019
1035
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1020
1036
|
const data = message.data;
|
|
1037
|
+
const state = data.state;
|
|
1021
1038
|
this._updateState((oldState) => {
|
|
1022
1039
|
oldState.connection.count += 1;
|
|
1023
1040
|
oldState.connection.id = connectionId;
|
|
@@ -1034,7 +1051,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1034
1051
|
const myself = (_b = this._usersManager.myself) != null ? _b : null;
|
|
1035
1052
|
this._stateNotifier.subjects["my-presence"].next(presence);
|
|
1036
1053
|
this._stateNotifier.subjects.myself.next(myself);
|
|
1037
|
-
const update = this.
|
|
1054
|
+
const update = this._crdtManager.getInitialState();
|
|
1038
1055
|
this._sendMessage({
|
|
1039
1056
|
type: "$initializeSession",
|
|
1040
1057
|
data: { presence, update }
|
|
@@ -1046,15 +1063,16 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1046
1063
|
if (!connectionId) return;
|
|
1047
1064
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1048
1065
|
const data = message.data;
|
|
1066
|
+
const state = data.state;
|
|
1049
1067
|
this._crdtManager.initialize({
|
|
1050
1068
|
onInitialized: (update2) => __async(this, null, function* () {
|
|
1051
|
-
this._addToStorageStore(update2);
|
|
1069
|
+
yield this._addToStorageStore(update2);
|
|
1052
1070
|
this._emitSharedTypes();
|
|
1053
1071
|
this._observeCrdt();
|
|
1054
1072
|
this._stateNotifier.subjects["storage-loaded"].next(true);
|
|
1055
1073
|
}),
|
|
1056
1074
|
origin: ORIGIN_INITIALIZED,
|
|
1057
|
-
update:
|
|
1075
|
+
update: state
|
|
1058
1076
|
});
|
|
1059
1077
|
const encodedState = this._crdtManager.doc.getEncodedState();
|
|
1060
1078
|
yield this._storageStore.addUpdate(encodedState);
|
|
@@ -1071,7 +1089,6 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1071
1089
|
if (!connectionId) return;
|
|
1072
1090
|
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1073
1091
|
const data = message.data;
|
|
1074
|
-
if (!this._crdtManager) return;
|
|
1075
1092
|
this._crdtManager.doc.applyEncodedState({
|
|
1076
1093
|
origin: ORIGIN_STORAGE_UPDATED,
|
|
1077
1094
|
update: data.state
|
|
@@ -1079,7 +1096,6 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1079
1096
|
const sharedTypes = this._crdtManager.doc.get();
|
|
1080
1097
|
const storageRoot = Object.keys(sharedTypes).reduce(
|
|
1081
1098
|
(acc, prop) => {
|
|
1082
|
-
if (!this._crdtManager) return acc;
|
|
1083
1099
|
const serialized = this._crdtManager.doc.toJson(prop);
|
|
1084
1100
|
this._crdtNotifier.subject(prop).next(serialized);
|
|
1085
1101
|
return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
|
|
@@ -1126,7 +1142,10 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1126
1142
|
}
|
|
1127
1143
|
_heartbeat() {
|
|
1128
1144
|
this._clearTimeout(this._timeouts.pong);
|
|
1129
|
-
this._timeouts.pong = setTimeout(
|
|
1145
|
+
this._timeouts.pong = setTimeout(
|
|
1146
|
+
this._reconnect.bind(this),
|
|
1147
|
+
PONG_TIMEOUT_MS
|
|
1148
|
+
);
|
|
1130
1149
|
this._sendMessage('{"type":"$ping","data":{}}');
|
|
1131
1150
|
}
|
|
1132
1151
|
_logDebug(...data) {
|
|
@@ -1137,7 +1156,6 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1137
1156
|
_observeCrdt() {
|
|
1138
1157
|
var _a, _b;
|
|
1139
1158
|
(_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
|
|
1140
|
-
if (!this._crdtManager) return;
|
|
1141
1159
|
const unsubscribe = this._crdtManager.doc.subscribe((event) => {
|
|
1142
1160
|
var _a2;
|
|
1143
1161
|
const origin = (_a2 = event.origin) != null ? _a2 : null;
|
|
@@ -1267,7 +1285,10 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1267
1285
|
return oldState;
|
|
1268
1286
|
});
|
|
1269
1287
|
this._clearInterval(this._intervals.heartbeat);
|
|
1270
|
-
this._intervals.heartbeat = setInterval(
|
|
1288
|
+
this._intervals.heartbeat = setInterval(
|
|
1289
|
+
this._heartbeat.bind(this),
|
|
1290
|
+
HEARTBEAT_INTERVAL_MS
|
|
1291
|
+
);
|
|
1271
1292
|
}
|
|
1272
1293
|
_onNavigatorOffline() {
|
|
1273
1294
|
return __async(this, null, function* () {
|
|
@@ -1322,8 +1343,14 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
1322
1343
|
_pollReconnect() {
|
|
1323
1344
|
this._clearTimeout(this._timeouts.reconnect);
|
|
1324
1345
|
const timeoutMs = typeof this._reconnectTimeoutMs === "number" ? this._reconnectTimeoutMs : this._reconnectTimeoutMs({ attempts: this._state.connection.attempts });
|
|
1325
|
-
const clampedMs = Math.max(
|
|
1326
|
-
|
|
1346
|
+
const clampedMs = Math.max(
|
|
1347
|
+
Math.min(MIN_RECONNECT_TIMEOUT_MS, timeoutMs),
|
|
1348
|
+
MAX_RECONNECT_TIMEOUT_MS
|
|
1349
|
+
);
|
|
1350
|
+
this._timeouts.reconnect = setTimeout(
|
|
1351
|
+
this._reconnect.bind(this),
|
|
1352
|
+
clampedMs
|
|
1353
|
+
);
|
|
1327
1354
|
}
|
|
1328
1355
|
_reconnect() {
|
|
1329
1356
|
return __async(this, null, function* () {
|
|
@@ -1522,10 +1549,7 @@ var MockedRoom = class extends AbstractRoom {
|
|
|
1522
1549
|
room: this.id,
|
|
1523
1550
|
user: myself
|
|
1524
1551
|
};
|
|
1525
|
-
const output = yield procedure.config.broadcast(
|
|
1526
|
-
parsed,
|
|
1527
|
-
context
|
|
1528
|
-
);
|
|
1552
|
+
const output = yield procedure.config.broadcast(parsed, context);
|
|
1529
1553
|
Object.entries(output).forEach(([_type, _data]) => {
|
|
1530
1554
|
this._simulateEvent(_type, _data);
|
|
1531
1555
|
});
|
|
@@ -1616,7 +1640,11 @@ var MockedRoom = class extends AbstractRoom {
|
|
|
1616
1640
|
presenceMaxSize: MAX_PRESENCE_SIZE_BYTES
|
|
1617
1641
|
}, limits);
|
|
1618
1642
|
this._router = router != null ? router : new PluvRouter({});
|
|
1619
|
-
this._usersManager = new UsersManager({
|
|
1643
|
+
this._usersManager = new UsersManager({
|
|
1644
|
+
initialPresence,
|
|
1645
|
+
limits: this._limits,
|
|
1646
|
+
presence
|
|
1647
|
+
});
|
|
1620
1648
|
this._crdtManager = new CrdtManager({
|
|
1621
1649
|
initialStorage
|
|
1622
1650
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.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": "^1.0.
|
|
22
|
-
"@pluv/types": "^1.0.
|
|
21
|
+
"@pluv/crdt": "^1.0.1",
|
|
22
|
+
"@pluv/types": "^1.0.1"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"eslint": "^9.25.1",
|
|
26
26
|
"tsup": "^8.4.0",
|
|
27
27
|
"typescript": "^5.8.3",
|
|
28
|
-
"@pluv/tsconfig": "^1.0.
|
|
29
|
-
"eslint-config-pluv": "^1.0.
|
|
28
|
+
"@pluv/tsconfig": "^1.0.1",
|
|
29
|
+
"eslint-config-pluv": "^1.0.1"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
package/src/AbstractRoom.ts
CHANGED
|
@@ -49,7 +49,9 @@ export abstract class AbstractRoom<
|
|
|
49
49
|
public abstract getStorage<TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null;
|
|
50
50
|
|
|
51
51
|
public abstract getStorageJson(): InferCrdtJson<TStorage> | null;
|
|
52
|
-
public abstract getStorageJson<TKey extends keyof TStorage>(
|
|
52
|
+
public abstract getStorageJson<TKey extends keyof TStorage>(
|
|
53
|
+
type: TKey,
|
|
54
|
+
): InferCrdtJson<TStorage[TKey]> | null;
|
|
53
55
|
|
|
54
56
|
public abstract other(
|
|
55
57
|
connectionId: string,
|