@pluv/io 0.40.2 → 0.41.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/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
- this._logDebug(`${colors.blue(`Registering connection for room ${this.id}:`)} ${pluvWs.sessionId}`);
518
- yield this._platform.acceptWebSocket(pluvWs);
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 = yield sender == null ? void 0 : sender.getSession()) != null ? _b : null;
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 [session, doc] = yield Promise.all([webSocket.getSession(), this._doc]);
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: session.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 = yield pluvWs.getSession();
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({ type: "$registered", data: { sessionId } }, { sessionId, user });
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
- return __async(this, null, function* () {
695
- const pluvWs = this._getAbstractWs(webSocket);
696
- if (!pluvWs) throw new Error("Session could not be found");
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
- return __async(this, null, function* () {
702
- const promises = Array.from(this._sessions.values()).map((pluvWs) => __async(this, null, function* () {
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 [session, sessions] = yield Promise.all([
780
- this._getSession(pluvWs),
781
- this._getSessions()
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
- yield Promise.all(
836
- Object.entries(broadcast).map((_02) => __async(this, [_02], function* ([type, data]) {
837
- yield this._broadcast({
838
- message: { data, type },
839
- senderId: sessionId
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
- yield Promise.all(
847
- Object.entries(self).map((_02) => __async(this, [_02], function* ([type, data]) {
848
- yield this._sendSelfMessage({ data, type }, { sessionId, user });
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
- yield Promise.all(
861
- Object.entries(sync).map((_02) => __async(this, [_02], function* ([type, data]) {
862
- yield this._sendSelfMessage({ data, type }, { sessionId, user });
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 = yield pluvWs.getSession();
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") yield this._emitQuitters();
915
- const session = yield pluvWs.getSession();
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: yield this._getSessions()
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.40.2";
1066
+ var version = "0.41.0";
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, { session }) => {
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
- session.webSocket.presence = updated;
1043
- return { $presenceUpdated: { presence: updated } };
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.40.2",
3
+ "version": "0.41.0",
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.40.2",
25
- "@pluv/types": "^0.40.2"
24
+ "@pluv/crdt": "^0.41.0",
25
+ "@pluv/types": "^0.41.0"
26
26
  },
27
27
  "devDependencies": {
28
- "eslint": "^9.24.0",
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.40.2",
33
- "eslint-config-pluv": "^0.40.2"
32
+ "@pluv/tsconfig": "^0.41.0",
33
+ "eslint-config-pluv": "^0.41.0"
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* --fix --max-warnings 0",
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
  }
@@ -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
 
@@ -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