@pluv/io 0.40.2 → 0.41.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 +9 -9
- package/CHANGELOG.md +69 -0
- package/dist/index.d.mts +30 -21
- package/dist/index.d.ts +30 -21
- package/dist/index.js +198 -75
- package/dist/index.mjs +198 -75
- package/package.json +7 -7
- package/src/AbstractPlatform.ts +3 -3
- package/src/AbstractWebSocket.ts +3 -20
- package/src/IORoom.ts +248 -65
- package/src/PluvServer.ts +39 -12
- package/src/authorize.ts +4 -4
- package/src/constants.ts +3 -0
- package/src/types.ts +4 -8
- package/src/version.generated.ts +1 -1
- package/tsconfig.json +5 -1
package/dist/index.mjs
CHANGED
|
@@ -202,28 +202,12 @@ var AbstractWebSocket = class {
|
|
|
202
202
|
this.CLOSING = 2;
|
|
203
203
|
/** The connection is closed. */
|
|
204
204
|
this.CLOSED = 3;
|
|
205
|
-
this._user = null;
|
|
206
205
|
const { persistence, platform, room } = config;
|
|
207
206
|
this.persistence = persistence;
|
|
208
207
|
this._platform = platform;
|
|
209
208
|
this.room = room;
|
|
210
209
|
this.webSocket = webSocket;
|
|
211
210
|
}
|
|
212
|
-
getSession() {
|
|
213
|
-
return __async(this, null, function* () {
|
|
214
|
-
var _a;
|
|
215
|
-
const sessionId = this.sessionId;
|
|
216
|
-
const state = this.state;
|
|
217
|
-
const room = state.room;
|
|
218
|
-
const user = (_a = this._user) != null ? _a : yield this.persistence.getUser(room, sessionId);
|
|
219
|
-
this._user = user;
|
|
220
|
-
return __spreadProps(__spreadValues({}, state), {
|
|
221
|
-
id: sessionId,
|
|
222
|
-
user,
|
|
223
|
-
webSocket: this
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
211
|
handleError(params) {
|
|
228
212
|
var _a, _b, _c;
|
|
229
213
|
const { error, message: _message, room, session } = params;
|
|
@@ -361,14 +345,19 @@ import { noop } from "@pluv/crdt";
|
|
|
361
345
|
import colors from "kleur";
|
|
362
346
|
|
|
363
347
|
// src/constants.ts
|
|
348
|
+
var GARBAGE_COLLECT_INTERVAL_MS = 6e4;
|
|
349
|
+
var MAX_PRESENCE_SIZE_BYTES = 512;
|
|
350
|
+
var MAX_USER_SIZE_BYTES = 512;
|
|
364
351
|
var PING_TIMEOUT_MS = 3e4;
|
|
365
352
|
|
|
366
353
|
// src/IORoom.ts
|
|
367
354
|
var IORoom = class {
|
|
368
355
|
constructor(id, config) {
|
|
356
|
+
this._lastGarbageCollectMs = -1 * (GARBAGE_COLLECT_INTERVAL_MS + 1);
|
|
369
357
|
this._uninitialize = null;
|
|
370
358
|
this._authorize = null;
|
|
371
359
|
this._sessions = /* @__PURE__ */ new Map();
|
|
360
|
+
this._userSessionss = /* @__PURE__ */ new Map();
|
|
372
361
|
const {
|
|
373
362
|
_meta,
|
|
374
363
|
authorize: authorize2,
|
|
@@ -398,12 +387,15 @@ var IORoom = class {
|
|
|
398
387
|
if (authorize2) this._authorize = authorize2;
|
|
399
388
|
const webSockets = this._platform.getWebSockets();
|
|
400
389
|
webSockets.forEach((webSocket) => {
|
|
390
|
+
var _a;
|
|
401
391
|
const deserialized = this._platform.getSerializedState(webSocket);
|
|
402
392
|
const sessionId = this._platform.getSessionId(webSocket);
|
|
403
393
|
if (!deserialized) return;
|
|
404
394
|
if (typeof sessionId !== "string") return;
|
|
405
395
|
const pluvWs = this._platform.convertWebSocket(webSocket, { room: this.id });
|
|
406
396
|
this._sessions.set(sessionId, pluvWs);
|
|
397
|
+
const userId = (_a = pluvWs.session.user) == null ? void 0 : _a.id;
|
|
398
|
+
if (!!userId) this._addUserSession(userId, sessionId);
|
|
407
399
|
});
|
|
408
400
|
const { doc, uninitialize } = this._initialize();
|
|
409
401
|
this._doc = doc;
|
|
@@ -461,6 +453,16 @@ var IORoom = class {
|
|
|
461
453
|
yield this._emitQuitters();
|
|
462
454
|
});
|
|
463
455
|
}
|
|
456
|
+
/**
|
|
457
|
+
* @description The IORoom will garbage collect occasionally as connections ping/pong the
|
|
458
|
+
* server
|
|
459
|
+
*/
|
|
460
|
+
garbageCollect() {
|
|
461
|
+
return __async(this, null, function* () {
|
|
462
|
+
this._lastGarbageCollectMs = Date.now();
|
|
463
|
+
this._emitQuitters();
|
|
464
|
+
});
|
|
465
|
+
}
|
|
464
466
|
getSize() {
|
|
465
467
|
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
466
468
|
return Array.from(this._sessions.values()).reduce((count, pluvWs) => {
|
|
@@ -509,19 +511,25 @@ var IORoom = class {
|
|
|
509
511
|
const user = yield this._getAuthorizedUser(token, _options);
|
|
510
512
|
const ioAuthorize = this._getIOAuthorize(_options);
|
|
511
513
|
const isUnauthorized = !!ioAuthorize && !user;
|
|
512
|
-
const isTokenInUse = !!user && (yield this._getSessions().then((sessions) => sessions.some((session) => {
|
|
513
|
-
var _a2;
|
|
514
|
-
return ((_a2 = session.user) == null ? void 0 : _a2.id) === user.id;
|
|
515
|
-
})));
|
|
516
514
|
const pluvWs = this._platform.convertWebSocket(webSocket, { room: this.id });
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
if (isUnauthorized || isTokenInUse) {
|
|
520
|
-
this._logDebug(`${colors.blue("Authorization failed for connection:")} ${pluvWs.sessionId}`);
|
|
515
|
+
if (isUnauthorized) {
|
|
516
|
+
this._logDebug(`${colors.blue("Authorization failed for connection")}`);
|
|
521
517
|
pluvWs.handleError({ error: new Error("Not authorized"), room: this.id });
|
|
522
518
|
pluvWs.close(3e3, "WebSocket unauthorized.");
|
|
523
519
|
return;
|
|
524
520
|
}
|
|
521
|
+
if (!!user) {
|
|
522
|
+
const latest = this._getLatestPresence(user.id);
|
|
523
|
+
const prevState = pluvWs.state;
|
|
524
|
+
pluvWs.user = user;
|
|
525
|
+
this._platform.setSerializedState(pluvWs, __spreadProps(__spreadValues({}, prevState), {
|
|
526
|
+
presence: latest.presence,
|
|
527
|
+
timers: __spreadProps(__spreadValues({}, prevState.timers), { presence: latest.timer })
|
|
528
|
+
}));
|
|
529
|
+
this._addUserSession(user.id, pluvWs.sessionId);
|
|
530
|
+
}
|
|
531
|
+
this._logDebug(`${colors.blue(`Registering connection for room ${this.id}:`)} ${pluvWs.sessionId}`);
|
|
532
|
+
yield this._platform.acceptWebSocket(pluvWs);
|
|
525
533
|
this._sessions.set(pluvWs.sessionId, pluvWs);
|
|
526
534
|
yield this._platform.persistence.addUser(this.id, pluvWs.sessionId, user != null ? user : {});
|
|
527
535
|
if (this._platform._config.registrationMode === "attached") {
|
|
@@ -532,17 +540,24 @@ var IORoom = class {
|
|
|
532
540
|
pluvWs.addEventListener("message", onMessage);
|
|
533
541
|
}
|
|
534
542
|
yield this._emitRegistered(pluvWs);
|
|
535
|
-
this._logDebug(`${colors.blue(`Registered connection for room ${this.id}:`)} ${pluvWs.sessionId}`);
|
|
536
543
|
const size = this.getSize();
|
|
544
|
+
this._logDebug(`${colors.blue(`Registered connection for room ${this.id}:`)} ${pluvWs.sessionId}`);
|
|
537
545
|
this._logDebug(`${colors.blue(`Room ${this.id} size:`)} ${size}`);
|
|
538
546
|
});
|
|
539
547
|
}
|
|
548
|
+
_addUserSession(userId, sessionId) {
|
|
549
|
+
var _a;
|
|
550
|
+
const set = (_a = this._userSessionss.get(userId)) != null ? _a : /* @__PURE__ */ new Set();
|
|
551
|
+
const updated = set.add(sessionId);
|
|
552
|
+
this._userSessionss.set(userId, updated);
|
|
553
|
+
return updated;
|
|
554
|
+
}
|
|
540
555
|
_broadcast(params) {
|
|
541
556
|
return __async(this, null, function* () {
|
|
542
557
|
var _a, _b, _c;
|
|
543
558
|
const { message, senderId } = params;
|
|
544
559
|
const sender = senderId ? (_a = this._sessions.get(senderId)) != null ? _a : null : null;
|
|
545
|
-
const session = (_b =
|
|
560
|
+
const session = (_b = sender == null ? void 0 : sender.session) != null ? _b : null;
|
|
546
561
|
const user = (_c = session == null ? void 0 : session.user) != null ? _c : null;
|
|
547
562
|
if (typeof senderId !== "string") return;
|
|
548
563
|
this._platform.pubSub.publish(this.id, __spreadValues({
|
|
@@ -564,15 +579,18 @@ var IORoom = class {
|
|
|
564
579
|
message: { type: "$exit", data: { sessionId } },
|
|
565
580
|
senderId: sessionId
|
|
566
581
|
});
|
|
582
|
+
const session = webSocket.session;
|
|
583
|
+
const user = session.user;
|
|
584
|
+
if (!!user) this._removeUserSession(user.id, sessionId);
|
|
567
585
|
try {
|
|
568
|
-
const
|
|
586
|
+
const doc = yield this._doc;
|
|
569
587
|
const encodedState = doc.getEncodedState();
|
|
570
588
|
yield Promise.resolve(
|
|
571
589
|
this._listeners.onUserDisconnected({
|
|
572
590
|
context: this._context,
|
|
573
591
|
encodedState,
|
|
574
592
|
room: this.id,
|
|
575
|
-
user
|
|
593
|
+
user
|
|
576
594
|
})
|
|
577
595
|
);
|
|
578
596
|
} catch (error) {
|
|
@@ -603,10 +621,21 @@ var IORoom = class {
|
|
|
603
621
|
}
|
|
604
622
|
_emitRegistered(pluvWs) {
|
|
605
623
|
return __async(this, null, function* () {
|
|
606
|
-
const session =
|
|
624
|
+
const session = pluvWs.session;
|
|
607
625
|
const sessionId = session.id;
|
|
626
|
+
const presence = session.presence;
|
|
608
627
|
const user = session.user;
|
|
609
|
-
yield this._sendSelfMessage(
|
|
628
|
+
yield this._sendSelfMessage(
|
|
629
|
+
{
|
|
630
|
+
type: "$registered",
|
|
631
|
+
data: {
|
|
632
|
+
presence,
|
|
633
|
+
sessionId,
|
|
634
|
+
timers: { presence: session.timers.presence }
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
{ sessionId, user }
|
|
638
|
+
);
|
|
610
639
|
try {
|
|
611
640
|
const doc = yield this._doc;
|
|
612
641
|
const encodedState = doc.getEncodedState();
|
|
@@ -681,6 +710,27 @@ var IORoom = class {
|
|
|
681
710
|
if (typeof this._authorize === "function") return this._authorize(options);
|
|
682
711
|
return this._authorize;
|
|
683
712
|
}
|
|
713
|
+
_getLatestPresence(userId) {
|
|
714
|
+
var _a, _b;
|
|
715
|
+
if (!this._authorize) return { timer: null, presence: null };
|
|
716
|
+
const sessionIds = Array.from((_b = (_a = this._userSessionss.get(userId)) == null ? void 0 : _a.values()) != null ? _b : []);
|
|
717
|
+
if (!sessionIds.length) return { timer: null, presence: null };
|
|
718
|
+
return sessionIds.reduce(
|
|
719
|
+
(state, sessionId) => {
|
|
720
|
+
var _a2, _b2;
|
|
721
|
+
const pluvWs = (_a2 = this._sessions.get(sessionId)) != null ? _a2 : null;
|
|
722
|
+
if (!pluvWs) return state;
|
|
723
|
+
const session = pluvWs.session;
|
|
724
|
+
const presence = session.presence;
|
|
725
|
+
const timer = session.timers.presence;
|
|
726
|
+
if (((_b2 = session.user) == null ? void 0 : _b2.id) !== userId) return state;
|
|
727
|
+
if (typeof state.timer !== "number") return { presence, timer };
|
|
728
|
+
if (typeof timer !== "number") return state;
|
|
729
|
+
return timer > state.timer ? { presence, timer } : state;
|
|
730
|
+
},
|
|
731
|
+
{ presence: null, timer: null }
|
|
732
|
+
);
|
|
733
|
+
}
|
|
684
734
|
_getProcedure(message) {
|
|
685
735
|
var _a;
|
|
686
736
|
return (_a = this._router._defs.events[message.type]) != null ? _a : null;
|
|
@@ -691,19 +741,13 @@ var IORoom = class {
|
|
|
691
741
|
return procedure.config.input ? procedure.config.input.parse(message.data) : message.data;
|
|
692
742
|
}
|
|
693
743
|
_getSession(webSocket) {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
return yield pluvWs.getSession();
|
|
698
|
-
});
|
|
744
|
+
const pluvWs = this._getAbstractWs(webSocket);
|
|
745
|
+
if (!pluvWs) throw new Error("Session could not be found");
|
|
746
|
+
return pluvWs.session;
|
|
699
747
|
}
|
|
700
748
|
_getSessions() {
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
return yield pluvWs.getSession();
|
|
704
|
-
}));
|
|
705
|
-
return yield Promise.all(promises);
|
|
706
|
-
});
|
|
749
|
+
const sessions = Array.from(this._sessions.values()).map((pluvWs) => pluvWs.session);
|
|
750
|
+
return sessions;
|
|
707
751
|
}
|
|
708
752
|
_initialize() {
|
|
709
753
|
const promise = (() => __async(this, null, function* () {
|
|
@@ -776,17 +820,31 @@ var IORoom = class {
|
|
|
776
820
|
if (!(yield this._initialized)) return;
|
|
777
821
|
const pluvWs = this._getAbstractWs(webSocket);
|
|
778
822
|
if (!pluvWs) throw new Error("Could not get session");
|
|
779
|
-
const
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
823
|
+
const session = this._getSession(pluvWs);
|
|
824
|
+
const sessions = this._getSessions();
|
|
825
|
+
const setPresence = (params) => {
|
|
826
|
+
this._setPresence.bind(this)(params);
|
|
827
|
+
};
|
|
783
828
|
const doc = yield this._doc;
|
|
784
829
|
const eventContext = {
|
|
785
830
|
context: this._context,
|
|
786
831
|
doc,
|
|
832
|
+
garbageCollect: () => __async(this, null, function* () {
|
|
833
|
+
yield this.garbageCollect();
|
|
834
|
+
}),
|
|
835
|
+
get presence() {
|
|
836
|
+
return session.presence;
|
|
837
|
+
},
|
|
838
|
+
set presence(presence) {
|
|
839
|
+
var _a2;
|
|
840
|
+
const sessionId2 = (_a2 = this.session) == null ? void 0 : _a2.id;
|
|
841
|
+
if (!sessionId2) return;
|
|
842
|
+
setPresence({ presence, sessionId: sessionId2, timer: this.time });
|
|
843
|
+
},
|
|
787
844
|
room: this.id,
|
|
788
845
|
session,
|
|
789
|
-
sessions
|
|
846
|
+
sessions,
|
|
847
|
+
time: (/* @__PURE__ */ new Date()).getTime()
|
|
790
848
|
};
|
|
791
849
|
if (pluvWs.state.quit) {
|
|
792
850
|
yield this._closeWebSockets([pluvWs]).catch(() => null);
|
|
@@ -832,22 +890,20 @@ var IORoom = class {
|
|
|
832
890
|
]).then((_0) => __async(this, [_0], function* ([broadcast, self, sync]) {
|
|
833
891
|
const handleBroadcast = () => __async(this, null, function* () {
|
|
834
892
|
if (!broadcast) return;
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
);
|
|
893
|
+
const messages = Object.entries(broadcast).map((_02) => __async(this, [_02], function* ([type, data]) {
|
|
894
|
+
yield this._broadcast({
|
|
895
|
+
message: { data, type },
|
|
896
|
+
senderId: sessionId
|
|
897
|
+
});
|
|
898
|
+
}));
|
|
899
|
+
yield Promise.all(messages);
|
|
843
900
|
});
|
|
844
901
|
const handleSelf = () => __async(this, null, function* () {
|
|
845
902
|
if (!self) return;
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
);
|
|
903
|
+
const messages = Object.entries(self).map((_02) => __async(this, [_02], function* ([type, data]) {
|
|
904
|
+
yield this._sendSelfMessage({ data, type }, { sessionId, user });
|
|
905
|
+
}));
|
|
906
|
+
yield Promise.all(messages);
|
|
851
907
|
});
|
|
852
908
|
const handleSync = () => __async(this, null, function* () {
|
|
853
909
|
if (!sync) return;
|
|
@@ -857,11 +913,10 @@ var IORoom = class {
|
|
|
857
913
|
room: this.id,
|
|
858
914
|
user
|
|
859
915
|
}, message));
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
);
|
|
916
|
+
const messages = Object.entries(sync).map((_02) => __async(this, [_02], function* ([type, data]) {
|
|
917
|
+
yield this._sendSelfMessage({ data, type }, { sessionId, user });
|
|
918
|
+
}));
|
|
919
|
+
yield Promise.all(messages);
|
|
865
920
|
});
|
|
866
921
|
yield Promise.all([handleBroadcast(), handleSelf(), handleSync()]);
|
|
867
922
|
}));
|
|
@@ -878,6 +933,36 @@ var IORoom = class {
|
|
|
878
933
|
return null;
|
|
879
934
|
}
|
|
880
935
|
}
|
|
936
|
+
_setPresence(params) {
|
|
937
|
+
var _a, _b;
|
|
938
|
+
const { presence, sessionId, timer: _timer } = params;
|
|
939
|
+
const timer = _timer != null ? _timer : (/* @__PURE__ */ new Date()).getTime();
|
|
940
|
+
const pluvWs = (_a = this._sessions.get(sessionId)) != null ? _a : null;
|
|
941
|
+
if (!pluvWs) return;
|
|
942
|
+
const wsSession = pluvWs.session;
|
|
943
|
+
const user = wsSession.user;
|
|
944
|
+
const sessionIds = user ? /* @__PURE__ */ new Set([...(_b = this._userSessionss.get(user.id)) != null ? _b : [], sessionId]) : /* @__PURE__ */ new Set([sessionId]);
|
|
945
|
+
sessionIds.forEach((sId) => {
|
|
946
|
+
const pWs = this._sessions.get(sId);
|
|
947
|
+
const session = pWs == null ? void 0 : pWs.session;
|
|
948
|
+
if (!session) return;
|
|
949
|
+
const prevState = session.webSocket.state;
|
|
950
|
+
this._platform.setSerializedState(session.webSocket, __spreadProps(__spreadValues({}, prevState), {
|
|
951
|
+
presence,
|
|
952
|
+
timers: __spreadProps(__spreadValues({}, prevState.timers), {
|
|
953
|
+
presence: timer
|
|
954
|
+
})
|
|
955
|
+
}));
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
_removeUserSession(userId, sessionId) {
|
|
959
|
+
const set = this._userSessionss.get(userId);
|
|
960
|
+
if (!set) return null;
|
|
961
|
+
set.delete(sessionId);
|
|
962
|
+
if (!!set.size) return set;
|
|
963
|
+
this._userSessionss.delete(userId);
|
|
964
|
+
return set;
|
|
965
|
+
}
|
|
881
966
|
_sendMessage(pluvWs, message) {
|
|
882
967
|
return __async(this, null, function* () {
|
|
883
968
|
if (!(yield this._initialized)) return;
|
|
@@ -897,7 +982,7 @@ var IORoom = class {
|
|
|
897
982
|
}, /* @__PURE__ */ new Map())) != null ? _b : this._sessions;
|
|
898
983
|
const promises = Array.from(webSockets.values()).map((pluvWs) => __async(this, null, function* () {
|
|
899
984
|
var _a2;
|
|
900
|
-
const session =
|
|
985
|
+
const session = pluvWs.session;
|
|
901
986
|
const user = (_a2 = session.user) != null ? _a2 : null;
|
|
902
987
|
this._sendMessage(pluvWs, { connectionId, data, room, type, user });
|
|
903
988
|
}));
|
|
@@ -911,8 +996,13 @@ var IORoom = class {
|
|
|
911
996
|
if (typeof senderId !== "string") return;
|
|
912
997
|
const pluvWs = this._sessions.get(senderId);
|
|
913
998
|
if (!pluvWs) return;
|
|
914
|
-
if (message.type === "$pong")
|
|
915
|
-
|
|
999
|
+
if (message.type === "$pong") {
|
|
1000
|
+
const elapsedMs = Date.now() - this._lastGarbageCollectMs;
|
|
1001
|
+
if (elapsedMs > GARBAGE_COLLECT_INTERVAL_MS) {
|
|
1002
|
+
yield this.garbageCollect();
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
const session = pluvWs.session;
|
|
916
1006
|
const user = (_b = session.user) != null ? _b : null;
|
|
917
1007
|
this._sendMessage(pluvWs, __spreadValues({
|
|
918
1008
|
connectionId: senderId,
|
|
@@ -931,9 +1021,19 @@ var IORoom = class {
|
|
|
931
1021
|
const context = {
|
|
932
1022
|
context: this._context,
|
|
933
1023
|
doc,
|
|
1024
|
+
garbageCollect: () => __async(this, null, function* () {
|
|
1025
|
+
yield this.garbageCollect();
|
|
1026
|
+
}),
|
|
1027
|
+
get presence() {
|
|
1028
|
+
return null;
|
|
1029
|
+
},
|
|
1030
|
+
set presence(presence) {
|
|
1031
|
+
return;
|
|
1032
|
+
},
|
|
934
1033
|
room: this.id,
|
|
935
1034
|
session: null,
|
|
936
|
-
sessions:
|
|
1035
|
+
sessions: this._getSessions(),
|
|
1036
|
+
time: (/* @__PURE__ */ new Date()).getTime()
|
|
937
1037
|
};
|
|
938
1038
|
const resolver = (_a = this._getProcedure(message)) == null ? void 0 : _a.config.sync;
|
|
939
1039
|
if (!resolver) return;
|
|
@@ -963,7 +1063,7 @@ var IORoom = class {
|
|
|
963
1063
|
};
|
|
964
1064
|
|
|
965
1065
|
// src/version.generated.ts
|
|
966
|
-
var version = "0.
|
|
1066
|
+
var version = "0.41.1";
|
|
967
1067
|
|
|
968
1068
|
// src/PluvServer.ts
|
|
969
1069
|
var PluvServer = class {
|
|
@@ -979,11 +1079,12 @@ var PluvServer = class {
|
|
|
979
1079
|
if (currentTime - wsSession.timers.ping > PING_TIMEOUT_MS) return false;
|
|
980
1080
|
return true;
|
|
981
1081
|
}).reduce(
|
|
982
|
-
(acc, { id, presence, user }) => __spreadProps(__spreadValues({}, acc), {
|
|
1082
|
+
(acc, { id, presence, timers, user }) => __spreadProps(__spreadValues({}, acc), {
|
|
983
1083
|
[id]: {
|
|
984
1084
|
connectionId: id,
|
|
985
1085
|
presence,
|
|
986
1086
|
room,
|
|
1087
|
+
timers: { presence: timers.presence },
|
|
987
1088
|
user
|
|
988
1089
|
}
|
|
989
1090
|
}),
|
|
@@ -993,13 +1094,15 @@ var PluvServer = class {
|
|
|
993
1094
|
}),
|
|
994
1095
|
$initializeSession: this._procedure.broadcast((data, { session }) => {
|
|
995
1096
|
const presence = data == null ? void 0 : data.presence;
|
|
1097
|
+
const timers = session.timers;
|
|
996
1098
|
if (!session) return {};
|
|
997
1099
|
session.presence = presence;
|
|
998
1100
|
return {
|
|
999
1101
|
$userJoined: {
|
|
1000
1102
|
connectionId: session.id,
|
|
1001
1103
|
user: session.user,
|
|
1002
|
-
presence
|
|
1104
|
+
presence,
|
|
1105
|
+
timers: { presence: timers.presence }
|
|
1003
1106
|
}
|
|
1004
1107
|
};
|
|
1005
1108
|
}).self((_0, _1) => __async(this, [_0, _1], function* (data, { context, doc, room, session }) {
|
|
@@ -1035,12 +1138,24 @@ var PluvServer = class {
|
|
|
1035
1138
|
}));
|
|
1036
1139
|
return { $pong: {} };
|
|
1037
1140
|
}),
|
|
1038
|
-
$updatePresence: this._procedure.broadcast((data,
|
|
1141
|
+
$updatePresence: this._procedure.broadcast((data, context) => {
|
|
1039
1142
|
const presence = data == null ? void 0 : data.presence;
|
|
1143
|
+
const { session } = context;
|
|
1040
1144
|
if (!session) return {};
|
|
1041
1145
|
const updated = Object.assign(/* @__PURE__ */ Object.create(null), session.presence, presence);
|
|
1042
|
-
|
|
1043
|
-
|
|
1146
|
+
const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
|
|
1147
|
+
if (bytes > MAX_PRESENCE_SIZE_BYTES) {
|
|
1148
|
+
throw new Error(
|
|
1149
|
+
`Large presence. Presence must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
context.presence = updated;
|
|
1153
|
+
return {
|
|
1154
|
+
$presenceUpdated: {
|
|
1155
|
+
presence: updated,
|
|
1156
|
+
timers: { presence: session.timers.presence }
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1044
1159
|
}),
|
|
1045
1160
|
$updateStorage: this._procedure.broadcast((data, { context, doc, room }) => {
|
|
1046
1161
|
var _a;
|
|
@@ -1153,8 +1268,16 @@ var PluvServer = class {
|
|
|
1153
1268
|
if (!this._authorize) {
|
|
1154
1269
|
throw new Error("IO does not specify authorize during initialization.");
|
|
1155
1270
|
}
|
|
1156
|
-
if (this._platform._createToken) return yield this._platform._createToken(params);
|
|
1157
1271
|
const ioAuthorize = this._getIOAuthorize(params);
|
|
1272
|
+
const user = params.user;
|
|
1273
|
+
const parsed = !!ioAuthorize ? ioAuthorize.user.parse(user) : user;
|
|
1274
|
+
const bytes = new TextEncoder().encode(JSON.stringify(parsed)).length;
|
|
1275
|
+
if (bytes > MAX_USER_SIZE_BYTES) {
|
|
1276
|
+
throw new Error(
|
|
1277
|
+
`createToken called with large payload. User must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1280
|
+
if (this._platform._createToken) return yield this._platform._createToken(params);
|
|
1158
1281
|
const secret = (_a = ioAuthorize == null ? void 0 : ioAuthorize.secret) != null ? _a : null;
|
|
1159
1282
|
if (!secret) throw new Error("`authorize` was specified without a valid secret");
|
|
1160
1283
|
return yield authorize({ platform: this._platform, secret }).encode(params);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/io",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.1",
|
|
4
4
|
"description": "@pluv/io server-side websocket manager",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,21 +21,21 @@
|
|
|
21
21
|
"jose": "^5.10.0",
|
|
22
22
|
"kleur": "^4.1.5",
|
|
23
23
|
"wonka": "^6.3.5",
|
|
24
|
-
"@pluv/crdt": "^0.
|
|
25
|
-
"@pluv/types": "^0.
|
|
24
|
+
"@pluv/crdt": "^0.41.1",
|
|
25
|
+
"@pluv/types": "^0.41.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"eslint": "^9.
|
|
28
|
+
"eslint": "^9.25.0",
|
|
29
29
|
"genversion": "^3.2.0",
|
|
30
30
|
"tsup": "^8.4.0",
|
|
31
31
|
"typescript": "^5.8.3",
|
|
32
|
-
"@pluv/tsconfig": "^0.
|
|
33
|
-
"eslint-config-pluv": "^0.
|
|
32
|
+
"@pluv/tsconfig": "^0.41.1",
|
|
33
|
+
"eslint-config-pluv": "^0.41.1"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "pnpm run version && tsup src/index.ts --format esm,cjs --dts",
|
|
37
37
|
"dev": "tsup src/index.ts --format esm,cjs --watch --dts",
|
|
38
|
-
"lint": "eslint src/**/*.ts
|
|
38
|
+
"lint": "eslint \"src/**/*.ts*\" --fix --max-warnings 0",
|
|
39
39
|
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
|
|
40
40
|
"version": "genversion ./src/version.generated.ts --esm"
|
|
41
41
|
}
|
package/src/AbstractPlatform.ts
CHANGED
|
@@ -29,7 +29,7 @@ export interface ConvertWebSocketConfig {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export abstract class AbstractPlatform<
|
|
32
|
-
TWebSocket extends AbstractWebSocket = AbstractWebSocket,
|
|
32
|
+
TWebSocket extends AbstractWebSocket<any, any> = AbstractWebSocket<any, any>,
|
|
33
33
|
TInitContext extends Record<string, any> = {},
|
|
34
34
|
TRoomContext extends Record<string, any> = {},
|
|
35
35
|
TConfig extends PlatformConfig = PlatformConfig,
|
|
@@ -62,9 +62,9 @@ export abstract class AbstractPlatform<
|
|
|
62
62
|
public abstract convertWebSocket(
|
|
63
63
|
webSocket: InferWebSocketSource<TWebSocket>,
|
|
64
64
|
config: ConvertWebSocketConfig,
|
|
65
|
-
): AbstractWebSocket
|
|
65
|
+
): AbstractWebSocket<any, any>;
|
|
66
66
|
|
|
67
|
-
public abstract getLastPing(webSocket: AbstractWebSocket): number | null;
|
|
67
|
+
public abstract getLastPing(webSocket: AbstractWebSocket<any, any>): number | null;
|
|
68
68
|
|
|
69
69
|
public abstract getSerializedState(webSocket: InferWebSocketSource<TWebSocket>): WebSocketSerializedState | null;
|
|
70
70
|
|
package/src/AbstractWebSocket.ts
CHANGED
|
@@ -54,7 +54,7 @@ export interface AbstractWebSocketConfig {
|
|
|
54
54
|
room: string;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
export abstract class AbstractWebSocket<TWebSocket = any> {
|
|
57
|
+
export abstract class AbstractWebSocket<TWebSocket = any, TAuthorize extends IOAuthorize<any, any> | null = null> {
|
|
58
58
|
/** The connection is not yet open. */
|
|
59
59
|
public readonly CONNECTING = 0;
|
|
60
60
|
/** The connection is open and ready to communicate. */
|
|
@@ -68,15 +68,15 @@ export abstract class AbstractWebSocket<TWebSocket = any> {
|
|
|
68
68
|
public readonly room: string;
|
|
69
69
|
public readonly webSocket: TWebSocket;
|
|
70
70
|
|
|
71
|
-
private _user: BaseUser | null = null;
|
|
72
|
-
|
|
73
71
|
protected readonly _platform: AbstractPlatform<this>;
|
|
74
72
|
|
|
75
73
|
public abstract set presence(presence: JsonObject | null);
|
|
76
74
|
public abstract get readyState(): 0 | 1 | 2 | 3;
|
|
75
|
+
public abstract get session(): WebSocketSession<TAuthorize>;
|
|
77
76
|
public abstract get sessionId(): string;
|
|
78
77
|
public abstract get state(): WebSocketSerializedState;
|
|
79
78
|
public abstract set state(state: WebSocketSerializedState);
|
|
79
|
+
public abstract set user(user: InferIOAuthorizeUser<TAuthorize>);
|
|
80
80
|
|
|
81
81
|
constructor(webSocket: TWebSocket, config: AbstractWebSocketConfig) {
|
|
82
82
|
const { persistence, platform, room } = config;
|
|
@@ -104,23 +104,6 @@ export abstract class AbstractWebSocket<TWebSocket = any> {
|
|
|
104
104
|
|
|
105
105
|
public abstract terminate(): void;
|
|
106
106
|
|
|
107
|
-
public async getSession<TAuthorize extends IOAuthorize<any, any> | null>(): Promise<WebSocketSession<TAuthorize>> {
|
|
108
|
-
const sessionId = this.sessionId;
|
|
109
|
-
const state = this.state;
|
|
110
|
-
const room = state.room;
|
|
111
|
-
|
|
112
|
-
const user = this._user ?? (await this.persistence.getUser(room, sessionId));
|
|
113
|
-
|
|
114
|
-
this._user = user as BaseUser;
|
|
115
|
-
|
|
116
|
-
return {
|
|
117
|
-
...state,
|
|
118
|
-
id: sessionId,
|
|
119
|
-
user: user as InferIOAuthorizeUser<TAuthorize>,
|
|
120
|
-
webSocket: this,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
107
|
public handleError(params: AbstractWebSocketHandleErrorParams): void {
|
|
125
108
|
const { error, message: _message, room, session } = params;
|
|
126
109
|
|