@pluv/io 0.19.0 → 0.21.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.js CHANGED
@@ -216,15 +216,23 @@ var PubSub = class extends AbstractPubSub {
216
216
  // src/AbstractPlatform.ts
217
217
  var AbstractPlatform = class {
218
218
  constructor(config = {}) {
219
- const { persistance, pubSub } = config;
219
+ this._initialized = false;
220
+ const { context, persistance, pubSub } = config;
221
+ context && (this._ioContext = context);
222
+ context && (this._roomContext = context);
220
223
  this.persistance = persistance != null ? persistance : new Persistance();
221
224
  this.pubSub = pubSub != null ? pubSub : new PubSub();
222
225
  }
226
+ _initialize() {
227
+ if (this._initialized) throw new Error("Platform is already initialized");
228
+ this._initialized = true;
229
+ return this;
230
+ }
223
231
  };
224
232
 
225
233
  // src/AbstractWebSocket.ts
226
234
  var AbstractWebSocket = class {
227
- constructor(config) {
235
+ constructor(webSocket, config) {
228
236
  /** The connection is not yet open. */
229
237
  this.CONNECTING = 0;
230
238
  /** The connection is open and ready to communicate. */
@@ -233,18 +241,36 @@ var AbstractWebSocket = class {
233
241
  this.CLOSING = 2;
234
242
  /** The connection is closed. */
235
243
  this.CLOSED = 3;
236
- const { room, sessionId } = config;
244
+ this._user = null;
245
+ const { persistance, room } = config;
246
+ this.persistance = persistance;
237
247
  this.room = room;
238
- this.sessionId = sessionId;
248
+ this.webSocket = webSocket;
249
+ }
250
+ getSession() {
251
+ return __async(this, null, function* () {
252
+ var _a;
253
+ const sessionId = this.sessionId;
254
+ const state = this.state;
255
+ const room = state.room;
256
+ const user = (_a = this._user) != null ? _a : yield this.persistance.getUser(room, sessionId);
257
+ this._user = user;
258
+ return __spreadProps(__spreadValues({}, state), {
259
+ id: sessionId,
260
+ user,
261
+ webSocket: this
262
+ });
263
+ });
239
264
  }
240
265
  handleError(params) {
241
266
  var _a, _b, _c;
242
- const { error, message = error instanceof Error ? error.message : "Unexpected error", session } = params;
267
+ const { error, message: _message, room, session } = params;
268
+ const message = error instanceof Error ? error.message : "Unexpected error";
243
269
  const stack = error instanceof Error ? (_a = error.stack) != null ? _a : null : null;
244
270
  return this.sendMessage({
245
271
  connectionId: (_b = session == null ? void 0 : session.id) != null ? _b : null,
246
272
  data: { message, stack },
247
- room: this.room,
273
+ room,
248
274
  type: "$ERROR",
249
275
  user: (_c = session == null ? void 0 : session.user) != null ? _c : null
250
276
  });
@@ -362,8 +388,10 @@ var authorize = (params) => {
362
388
  return { decode, encode };
363
389
  };
364
390
 
365
- // src/IORoom.ts
391
+ // src/constants.ts
366
392
  var PING_TIMEOUT_MS = 3e4;
393
+
394
+ // src/IORoom.ts
367
395
  var IORoom = class {
368
396
  constructor(id, config) {
369
397
  this._sessions = /* @__PURE__ */ new Map();
@@ -375,12 +403,21 @@ var IORoom = class {
375
403
  this._docFactory = crdt.doc(() => ({}));
376
404
  this._doc = this._docFactory.getEmpty();
377
405
  this._router = router;
378
- this._platform = platform;
406
+ this._platform = platform.initialize({ context });
379
407
  this.id = id;
380
408
  this._listeners = {
381
409
  onDestroy: (event) => onDestroy == null ? void 0 : onDestroy(event)
382
410
  };
383
411
  if (authorize2) this._authorize = authorize2;
412
+ const webSockets = this._platform.getWebSockets();
413
+ if (!webSockets.length) return;
414
+ webSockets.forEach((webSocket) => {
415
+ const deserialized = this._platform.getSerializedState(webSocket);
416
+ const sessionId = this._platform.getSessionId(webSocket);
417
+ if (!deserialized) return;
418
+ if (typeof sessionId !== "string") return;
419
+ this._sessions.set(sessionId, this._platform.convertWebSocket(webSocket, { room: this.id }));
420
+ });
384
421
  }
385
422
  get _events() {
386
423
  return this._router._events;
@@ -398,111 +435,135 @@ var IORoom = class {
398
435
  }
399
436
  getSize() {
400
437
  const currentTime = (/* @__PURE__ */ new Date()).getTime();
401
- return Array.from(this._sessions.values()).reduce((count, session) => {
402
- if (session.quit) return count;
403
- return currentTime - session.timers.ping > PING_TIMEOUT_MS ? count : count + 1;
438
+ return Array.from(this._sessions.values()).reduce((count, pluvWs) => {
439
+ var _a;
440
+ if (pluvWs.state.quit) return count;
441
+ const pingTime = (_a = this._platform.getLastPing(pluvWs)) != null ? _a : pluvWs.state.timers.ping;
442
+ return currentTime - pingTime > PING_TIMEOUT_MS ? count : count + 1;
404
443
  }, 0);
405
444
  }
406
- register(webSocket, options) {
407
- return __async(this, null, function* () {
408
- var _b;
409
- const _a = options, { token } = _a, _platformEventContext = __objRest(_a, ["token"]);
410
- const platformEventContext = _platformEventContext;
445
+ onClose(webSocket) {
446
+ this._ensureDetached();
447
+ const wsSession = this._getAbstractWs(webSocket);
448
+ if (!wsSession) return () => void 0;
449
+ return this._onClose(wsSession);
450
+ }
451
+ onError(webSocket) {
452
+ this._ensureDetached();
453
+ const wsSession = this._getAbstractWs(webSocket);
454
+ if (!wsSession) return () => void 0;
455
+ return this._onClose(wsSession);
456
+ }
457
+ onMessage(webSocket) {
458
+ this._ensureDetached();
459
+ const wsSession = this._getAbstractWs(webSocket);
460
+ if (!wsSession) return () => void 0;
461
+ return this._onMessage(wsSession);
462
+ }
463
+ register(_0) {
464
+ return __async(this, arguments, function* (webSocket, options = {}) {
465
+ const { token } = options;
411
466
  if (!this._uninitialize) yield this._initialize();
412
- const user = yield this._getAuthorizedUser(token, options);
413
- const sessionId = this._platform.randomUUID();
414
- const pluvWs = this._platform.convertWebSocket(webSocket, {
415
- room: this.id,
416
- sessionId,
417
- userId: (_b = user == null ? void 0 : user.id) != null ? _b : null
418
- });
419
- this._logDebug(`${import_kleur.default.blue(`Registering connection for room ${this.id}:`)} ${sessionId}`);
420
- const uninitializeWs = yield pluvWs.initialize();
421
- const ioAuthorize = this._getIOAuthorize(options);
467
+ const user = yield this._getAuthorizedUser(token);
468
+ const ioAuthorize = this._getIOAuthorize();
422
469
  const isUnauthorized = !!(ioAuthorize == null ? void 0 : ioAuthorize.required) && !user;
423
- const isTokenInUse = !!user && Array.from(this._sessions.values()).some((session2) => {
424
- var _a2;
425
- return ((_a2 = session2.user) == null ? void 0 : _a2.id) === user.id;
426
- });
470
+ const isTokenInUse = !!user && (yield this._getSessions().then((sessions) => sessions.some((session) => {
471
+ var _a;
472
+ return ((_a = session.user) == null ? void 0 : _a.id) === user.id;
473
+ })));
474
+ const pluvWs = yield this._platform.convertWebSocket(webSocket, { room: this.id });
475
+ this._logDebug(`${import_kleur.default.blue(`Registering connection for room ${this.id}:`)} ${pluvWs.sessionId}`);
476
+ yield this._platform.acceptWebSocket(pluvWs);
427
477
  if (isUnauthorized || isTokenInUse) {
428
- this._logDebug(`${import_kleur.default.blue("Authorization failed for connection:")} ${sessionId}`);
429
- pluvWs.handleError({ error: new Error("Not authorized") });
478
+ this._logDebug(`${import_kleur.default.blue("Authorization failed for connection:")} ${pluvWs.sessionId}`);
479
+ pluvWs.handleError({ error: new Error("Not authorized"), room: this.id });
430
480
  pluvWs.close(3e3, "WebSocket unauthorized.");
431
481
  return;
432
482
  }
433
- const currentTime = (/* @__PURE__ */ new Date()).getTime();
434
- const session = {
435
- id: sessionId,
436
- presence: null,
437
- quit: false,
438
- room: this.id,
439
- timers: {
440
- ping: currentTime
441
- },
442
- webSocket: pluvWs,
443
- user
444
- };
445
- this._sessions.set(session.id, session);
446
- yield this._platform.persistance.addUser(this.id, sessionId, user != null ? user : {});
447
- const onClose = this._onClose(session, uninitializeWs).bind(this);
448
- const onMessage = this._onMessage(session, platformEventContext).bind(this);
449
- pluvWs.addEventListener("close", onClose);
450
- pluvWs.addEventListener("error", onClose);
451
- pluvWs.addEventListener("message", onMessage);
452
- this._emitRegistered(session);
453
- this._logDebug(`${import_kleur.default.blue(`Registered connection for room ${this.id}:`)} ${sessionId}`);
483
+ this._sessions.set(pluvWs.sessionId, pluvWs);
484
+ yield this._platform.persistance.addUser(this.id, pluvWs.sessionId, user != null ? user : {});
485
+ if (this._platform._registrationMode === "attached") {
486
+ const onClose = this._onClose(pluvWs).bind(this);
487
+ const onMessage = this._onMessage(pluvWs).bind(this);
488
+ pluvWs.addEventListener("close", onClose);
489
+ pluvWs.addEventListener("error", onClose);
490
+ pluvWs.addEventListener("message", onMessage);
491
+ }
492
+ yield this._emitRegistered(pluvWs);
493
+ this._logDebug(`${import_kleur.default.blue(`Registered connection for room ${this.id}:`)} ${pluvWs.sessionId}`);
454
494
  const size = this.getSize();
455
495
  this._logDebug(`${import_kleur.default.blue(`Room ${this.id} size:`)} ${size}`);
456
496
  });
457
497
  }
458
498
  _broadcast(params) {
459
- var _a;
460
- const { message, senderId } = params;
461
- const currentTime = (/* @__PURE__ */ new Date()).getTime();
462
- const sender = senderId ? this._sessions.get(senderId) : null;
463
- const quitters = Array.from(this._sessions.values()).filter(
464
- (session) => session.quit || currentTime - session.timers.ping > PING_TIMEOUT_MS
465
- );
466
- quitters.forEach((session) => {
467
- this._sessions.delete(session.id);
499
+ return __async(this, null, function* () {
500
+ var _a, _b, _c;
501
+ const { message, senderId } = params;
502
+ const sender = senderId ? (_a = this._sessions.get(senderId)) != null ? _a : null : null;
503
+ const session = (_b = yield sender == null ? void 0 : sender.getSession()) != null ? _b : null;
504
+ const user = (_c = session == null ? void 0 : session.user) != null ? _c : null;
505
+ this._platform.pubSub.publish(this.id, __spreadValues({
506
+ connectionId: senderId != null ? senderId : null,
507
+ room: this.id,
508
+ user
509
+ }, message));
468
510
  });
469
- this._emitQuitters(quitters);
470
- this._platform.pubSub.publish(this.id, __spreadValues({
471
- connectionId: senderId != null ? senderId : null,
472
- room: this.id,
473
- user: (_a = sender == null ? void 0 : sender.user) != null ? _a : null
474
- }, message));
475
- }
476
- _emitQuitters(quitters) {
477
- quitters.forEach((quitter) => {
478
- this._broadcast({
479
- message: {
480
- type: "$EXIT",
481
- data: { sessionId: quitter.id }
482
- },
483
- senderId: quitter.id
511
+ }
512
+ _emitQuitters() {
513
+ return __async(this, null, function* () {
514
+ const currentTime = (/* @__PURE__ */ new Date()).getTime();
515
+ const quitters = Array.from(this._sessions.values()).filter(
516
+ (pluvWs) => pluvWs.state.quit || currentTime - pluvWs.state.timers.ping > PING_TIMEOUT_MS
517
+ );
518
+ quitters.forEach((pluvWs) => {
519
+ this._sessions.delete(pluvWs.sessionId);
484
520
  });
521
+ const promises = quitters.map((quitter) => __async(this, null, function* () {
522
+ yield this._broadcast({
523
+ message: {
524
+ type: "$EXIT",
525
+ data: { sessionId: quitter.sessionId }
526
+ },
527
+ senderId: quitter.sessionId
528
+ });
529
+ }));
530
+ yield Promise.all(promises);
485
531
  });
486
532
  }
487
- _emitRegistered(session) {
488
- this._sendSelfMessage(
489
- {
490
- type: "$REGISTERED",
491
- data: { sessionId: session.id }
492
- },
493
- session
494
- );
533
+ _emitRegistered(pluvWs) {
534
+ return __async(this, null, function* () {
535
+ const session = yield pluvWs.getSession();
536
+ const sessionId = session.id;
537
+ const user = session.user;
538
+ yield this._sendSelfMessage({ type: "$REGISTERED", data: { sessionId } }, { sessionId, user });
539
+ });
495
540
  }
496
- _getIOAuthorize(context) {
497
- if (typeof this._authorize === "function") {
498
- return this._authorize(context);
541
+ _ensureDetached() {
542
+ if (this._platform._registrationMode === "detached") return;
543
+ throw new Error("Platform must use detached mode");
544
+ }
545
+ _getAbstractWs(webSocket) {
546
+ var _a, _b;
547
+ if (webSocket instanceof AbstractWebSocket) return webSocket;
548
+ const sessionId = this._platform.getSessionId(webSocket);
549
+ if (typeof sessionId === "string") {
550
+ const session = (_a = this._sessions.get(sessionId)) != null ? _a : null;
551
+ if (session) return session;
499
552
  }
500
- return this._authorize;
553
+ const sessions = Array.from(this._sessions.values());
554
+ return (_b = sessions.find((pluvWs) => pluvWs.webSocket === webSocket)) != null ? _b : null;
501
555
  }
502
- _getAuthorizedUser(token, context) {
556
+ _getSession(webSocket) {
557
+ return __async(this, null, function* () {
558
+ const pluvWs = this._getAbstractWs(webSocket);
559
+ if (!pluvWs) throw new Error("Session could not be found");
560
+ return yield pluvWs.getSession();
561
+ });
562
+ }
563
+ _getAuthorizedUser(token) {
503
564
  return __async(this, null, function* () {
504
565
  var _a;
505
- const ioAuthorize = this._getIOAuthorize(context);
566
+ const ioAuthorize = this._getIOAuthorize();
506
567
  if (!ioAuthorize) return null;
507
568
  if (!token) return null;
508
569
  const payload = yield authorize({
@@ -528,6 +589,12 @@ var IORoom = class {
528
589
  }
529
590
  });
530
591
  }
592
+ _getIOAuthorize() {
593
+ if (typeof this._authorize === "function") {
594
+ return this._authorize(this._context);
595
+ }
596
+ return this._authorize;
597
+ }
531
598
  _getProcedure(message) {
532
599
  var _a;
533
600
  return (_a = this._router._events[message.type]) != null ? _a : null;
@@ -537,6 +604,14 @@ var IORoom = class {
537
604
  if (!procedure) return message.data;
538
605
  return procedure.config.input ? procedure.config.input.parse(message.data) : message.data;
539
606
  }
607
+ _getSessions() {
608
+ return __async(this, null, function* () {
609
+ const promises = Array.from(this._sessions.values()).map((pluvWs) => __async(this, null, function* () {
610
+ return yield pluvWs.getSession();
611
+ }));
612
+ return yield Promise.all(promises);
613
+ });
614
+ }
540
615
  _initialize() {
541
616
  return __async(this, null, function* () {
542
617
  this._logDebug(`${import_kleur.default.blue(`Initializing room ${this.id}:`)}`);
@@ -544,14 +619,15 @@ var IORoom = class {
544
619
  yield this._uninitialize();
545
620
  this._uninitialize = null;
546
621
  }
547
- const pubSubId = yield this._platform.pubSub.subscribe(this.id, (_a) => {
548
- var _b = _a, { options = {} } = _b, message = __objRest(_b, ["options"]);
549
- const session = {
550
- id: message.connectionId,
551
- user: message.user
552
- };
553
- this._sendMessage(message, session, options);
554
- });
622
+ const pubSubId = yield this._platform.pubSub.subscribe(
623
+ this.id,
624
+ (_a) => __async(this, null, function* () {
625
+ var _b = _a, { options = {} } = _b, message = __objRest(_b, ["options"]);
626
+ const sessionId = message.connectionId;
627
+ const user = message.user;
628
+ yield this._sendMessage(message, { sessionId, user }, options);
629
+ })
630
+ );
555
631
  this._uninitialize = () => __async(this, null, function* () {
556
632
  this._platform.pubSub.unsubscribe(pubSubId);
557
633
  const encodedState = this._doc.getEncodedState();
@@ -568,43 +644,48 @@ var IORoom = class {
568
644
  _logDebug(...data) {
569
645
  this._debug && console.log(...data);
570
646
  }
571
- _onClose(session, callback) {
647
+ _onClose(webSocket) {
572
648
  return () => {
573
649
  if (!this._uninitialize) return;
574
- session.quit = true;
575
- this._logDebug(`${import_kleur.default.blue(`(Unregistering connection for room ${this.id}:`)} ${session.id}`);
576
- this._sessions.delete(session.id);
577
- this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
650
+ webSocket.state.quit = true;
651
+ this._logDebug(`${import_kleur.default.blue(`Unregistering connection for room ${this.id}:`)} ${webSocket.sessionId}`);
652
+ this._sessions.delete(webSocket.sessionId);
653
+ this._platform.persistance.deleteUser(this.id, webSocket.sessionId).finally(() => __async(this, null, function* () {
578
654
  var _a;
579
- this._broadcast({
655
+ yield this._broadcast({
580
656
  message: {
581
657
  type: "$EXIT",
582
- data: { sessionId: session.id }
658
+ data: { sessionId: webSocket.sessionId }
583
659
  },
584
- senderId: session.id
660
+ senderId: webSocket.sessionId
585
661
  });
586
- callback == null ? void 0 : callback();
587
662
  const size = this.getSize();
588
- this._logDebug(`${import_kleur.default.blue(`Unregistered connection for room ${this.id}:`)} ${session.id}`);
663
+ this._logDebug(`${import_kleur.default.blue(`Unregistered connection for room ${this.id}:`)} ${webSocket.sessionId}`);
589
664
  this._logDebug(`${import_kleur.default.blue(`Room ${this.id} size:`)} ${size}`);
590
665
  if (size) return;
591
666
  (_a = this._uninitialize) == null ? void 0 : _a.call(this);
592
- });
667
+ }));
593
668
  };
594
669
  }
595
- _onMessage(session, platformEventContext) {
596
- return (event) => {
670
+ _onMessage(webSocket) {
671
+ return (event) => __async(this, null, function* () {
597
672
  var _a, _b, _c, _d, _e, _f;
673
+ const pluvWs = this._getAbstractWs(webSocket);
674
+ if (!pluvWs) throw new Error("Could not get session");
675
+ const [session, sessions] = yield Promise.all([
676
+ this._getSession(pluvWs),
677
+ this._getSessions()
678
+ ]);
598
679
  const baseContext = {
599
680
  context: this._context,
600
681
  doc: this._doc,
601
682
  room: this.id,
602
683
  session,
603
- sessions: this._sessions
684
+ sessions
604
685
  };
605
686
  if (!this._uninitialize) return;
606
- if (session.quit) {
607
- session.webSocket.close(1011, "WebSocket broken.");
687
+ if (pluvWs.state.quit) {
688
+ pluvWs.close(1011, "WebSocket broken.");
608
689
  return;
609
690
  }
610
691
  const message = this._parseMessage(event);
@@ -615,47 +696,52 @@ var IORoom = class {
615
696
  try {
616
697
  inputs = this._getProcedureInputs(message);
617
698
  } catch (error) {
618
- session == null ? void 0 : session.webSocket.handleError({
699
+ pluvWs.handleError({
619
700
  error,
620
701
  message: "Invalid input",
702
+ room: this.id,
621
703
  session
622
704
  });
623
705
  return;
624
706
  }
625
- const extendedContext = __spreadProps(__spreadValues({}, baseContext), {
626
- context: __spreadValues(__spreadValues({}, this._context), platformEventContext)
627
- });
707
+ const extendedContext = __spreadProps(__spreadValues({}, baseContext), { context: __spreadValues({}, this._context) });
628
708
  Promise.all([
629
709
  (_b = (_a = procedure.config).broadcast) == null ? void 0 : _b.call(_a, inputs, extendedContext),
630
710
  (_d = (_c = procedure.config).self) == null ? void 0 : _d.call(_c, inputs, extendedContext),
631
711
  (_f = (_e = procedure.config).sync) == null ? void 0 : _f.call(_e, inputs, baseContext)
632
- ]).then(([broadcast, self, sync]) => {
712
+ ]).then((_0) => __async(this, [_0], function* ([broadcast, self, sync]) {
713
+ const sessionId = session.id;
714
+ const user = session.user;
633
715
  if (broadcast) {
634
716
  Object.entries(broadcast).forEach(([type, data]) => {
635
717
  this._broadcast({
636
718
  message: { data, type },
637
- senderId: session.id
719
+ senderId: sessionId
638
720
  });
639
721
  });
640
722
  }
641
723
  if (self) {
642
- Object.entries(self).forEach(([type, data]) => {
643
- this._sendSelfMessage({ data, type }, session);
644
- });
724
+ yield Promise.all(
725
+ Object.entries(self).map((_02) => __async(this, [_02], function* ([type, data]) {
726
+ yield this._sendSelfMessage({ data, type }, { sessionId, user });
727
+ }))
728
+ );
645
729
  }
646
730
  if (sync) {
647
731
  this._platform.pubSub.publish(this.id, __spreadValues({
648
732
  connectionId: session.id,
649
733
  options: { type: "sync" },
650
734
  room: this.id,
651
- user: session.user
735
+ user
652
736
  }, message));
653
- Object.entries(sync).forEach(([type, data]) => {
654
- this._sendSelfMessage({ data, type }, session);
655
- });
737
+ yield Promise.all(
738
+ Object.entries(sync).map((_02) => __async(this, [_02], function* ([type, data]) {
739
+ yield this._sendSelfMessage({ data, type }, { sessionId, user });
740
+ }))
741
+ );
656
742
  }
657
- });
658
- };
743
+ }));
744
+ });
659
745
  }
660
746
  _parseMessage(message) {
661
747
  try {
@@ -668,80 +754,93 @@ var IORoom = class {
668
754
  return null;
669
755
  }
670
756
  }
671
- _sendMessage(message, sender, options = {}) {
672
- switch (options.type) {
673
- case "self": {
674
- this._sendSelfMessage(message, sender);
675
- return;
676
- }
677
- case "sync": {
678
- this._sendSyncMessage(message, sender);
679
- return;
680
- }
681
- case "broadcast":
682
- default: {
683
- const sessionIds = options.sessionIds;
684
- this._sendBroadcastMessage(message, sender, sessionIds);
757
+ _sendMessage(_0, _1) {
758
+ return __async(this, arguments, function* (message, sender, options = {}) {
759
+ switch (options.type) {
760
+ case "self": {
761
+ yield this._sendSelfMessage(message, sender);
762
+ return;
763
+ }
764
+ case "sync": {
765
+ yield this._sendSyncMessage(message, sender);
766
+ return;
767
+ }
768
+ case "broadcast":
769
+ default: {
770
+ const sessionIds = options.sessionIds;
771
+ yield this._sendBroadcastMessage(message, sender, sessionIds);
772
+ }
685
773
  }
686
- }
774
+ });
687
775
  }
688
776
  _sendBroadcastMessage(message, sender, sessionIds) {
689
- var _a, _b;
690
- const senderId = (_a = sender == null ? void 0 : sender.id) != null ? _a : null;
691
- const sessions = (_b = sessionIds == null ? void 0 : sessionIds.reduce((dict, id) => {
692
- const _session = this._sessions.get(id);
693
- return _session ? dict.set(id, _session) : dict;
694
- }, /* @__PURE__ */ new Map())) != null ? _b : this._sessions;
695
- Array.from(sessions.values()).forEach((_session) => {
696
- _session.webSocket.sendMessage(__spreadValues({
777
+ return __async(this, null, function* () {
778
+ var _a, _b;
779
+ const { data, type } = message;
780
+ const connectionId = (_a = sender == null ? void 0 : sender.sessionId) != null ? _a : null;
781
+ const room = this.id;
782
+ const webSockets = (_b = sessionIds == null ? void 0 : sessionIds.reduce((dict, id) => {
783
+ const pluvWs = this._sessions.get(id);
784
+ return pluvWs ? dict.set(id, pluvWs) : dict;
785
+ }, /* @__PURE__ */ new Map())) != null ? _b : this._sessions;
786
+ const promises = Array.from(webSockets.values()).map((pluvWs) => __async(this, null, function* () {
787
+ var _a2;
788
+ const session = yield pluvWs.getSession();
789
+ const user = (_a2 = session.user) != null ? _a2 : null;
790
+ pluvWs.sendMessage({ connectionId, data, room, type, user });
791
+ }));
792
+ yield Promise.all(promises);
793
+ });
794
+ }
795
+ _sendSelfMessage(message, sender) {
796
+ return __async(this, null, function* () {
797
+ var _a, _b;
798
+ const senderId = (_a = sender == null ? void 0 : sender.sessionId) != null ? _a : null;
799
+ if (typeof senderId !== "string") return;
800
+ const pluvWs = this._sessions.get(senderId);
801
+ if (!pluvWs) return;
802
+ if (message.type === "$PONG") yield this._emitQuitters();
803
+ const session = yield pluvWs.getSession();
804
+ const user = (_b = session.user) != null ? _b : null;
805
+ pluvWs.sendMessage(__spreadValues({
697
806
  connectionId: senderId,
698
807
  room: this.id,
699
- user: _session.user
808
+ user
700
809
  }, message));
701
810
  });
702
811
  }
703
- _sendSelfMessage(message, sender) {
704
- const senderId = sender == null ? void 0 : sender.id;
705
- if (!senderId) return;
706
- const session = this._sessions.get(senderId);
707
- if (!session) return;
708
- session.webSocket.sendMessage(__spreadValues({
709
- connectionId: sender.id,
710
- room: this.id,
711
- user: sender.user
712
- }, message));
713
- }
714
812
  _sendSyncMessage(message, sender) {
715
- var _a;
716
- const senderId = sender == null ? void 0 : sender.id;
717
- if (!senderId) return;
718
- const context = {
719
- context: this._context,
720
- doc: this._doc,
721
- room: this.id,
722
- session: null,
723
- sessions: this._sessions
724
- };
725
- const resolver = (_a = this._getProcedure(message)) == null ? void 0 : _a.config.sync;
726
- if (!resolver) return;
727
- let inputs;
728
- try {
729
- inputs = this._getProcedureInputs(message);
730
- } catch (e) {
731
- return;
732
- }
733
- Promise.resolve(resolver(inputs, context)).then((output) => {
734
- if (!output) return;
735
- Object.keys(output).forEach((type) => {
736
- var _a2, _b;
737
- const data = (_a2 = output[type]) != null ? _a2 : {};
738
- this._platform.pubSub.publish(this.id, {
739
- connectionId: senderId,
740
- data,
741
- options: { type: "self" },
742
- room: this.id,
743
- type,
744
- user: (_b = sender.user) != null ? _b : null
813
+ return __async(this, null, function* () {
814
+ var _a;
815
+ if (!sender) return;
816
+ const context = {
817
+ context: this._context,
818
+ doc: this._doc,
819
+ room: this.id,
820
+ session: null,
821
+ sessions: yield this._getSessions()
822
+ };
823
+ const resolver = (_a = this._getProcedure(message)) == null ? void 0 : _a.config.sync;
824
+ if (!resolver) return;
825
+ let inputs;
826
+ try {
827
+ inputs = this._getProcedureInputs(message);
828
+ } catch (e) {
829
+ return;
830
+ }
831
+ Promise.resolve(resolver(inputs, context)).then((output) => {
832
+ if (!output) return;
833
+ Object.keys(output).forEach((type) => {
834
+ var _a2, _b;
835
+ const data = (_a2 = output[type]) != null ? _a2 : {};
836
+ this._platform.pubSub.publish(this.id, {
837
+ connectionId: sender.sessionId,
838
+ data,
839
+ options: { type: "self" },
840
+ room: this.id,
841
+ type,
842
+ user: (_b = sender.user) != null ? _b : null
843
+ });
745
844
  });
746
845
  });
747
846
  });
@@ -749,7 +848,7 @@ var IORoom = class {
749
848
  };
750
849
 
751
850
  // src/version.generated.ts
752
- var version = "0.19.0";
851
+ var version = "0.21.0";
753
852
 
754
853
  // src/PluvServer.ts
755
854
  var PluvServer = class {
@@ -782,6 +881,9 @@ var PluvServer = class {
782
881
  get _events() {
783
882
  return this._router._events;
784
883
  }
884
+ get _registrationMode() {
885
+ return this._platform._registrationMode;
886
+ }
785
887
  createToken(params) {
786
888
  return __async(this, null, function* () {
787
889
  if (!this._authorize) {
@@ -795,9 +897,9 @@ var PluvServer = class {
795
897
  }).encode(params);
796
898
  });
797
899
  }
798
- getRoom(room, options) {
799
- var _b;
800
- const _a = options, { debug } = _a, platformRoomContext = __objRest(_a, ["debug"]);
900
+ getRoom(room, ...options) {
901
+ var _a, _c;
902
+ const _b = (_a = options[0]) != null ? _a : {}, { debug } = _b, platformRoomContext = __objRest(_b, ["debug"]);
801
903
  this._purgeEmptyRooms();
802
904
  if (!/^[a-z0-9]+[a-z0-9\-_]+[a-z0-9]+$/i.test(room)) {
803
905
  throw new Error("Unsupported room name");
@@ -807,24 +909,25 @@ var PluvServer = class {
807
909
  if (oldRoom) return oldRoom;
808
910
  const roomContext = __spreadValues(__spreadValues({}, this._context), platformRoomContext);
809
911
  const newRoom = new IORoom(room, {
810
- authorize: (_b = this._authorize) != null ? _b : void 0,
912
+ authorize: (_c = this._authorize) != null ? _c : void 0,
811
913
  context: roomContext,
812
914
  crdt: this._crdt,
813
915
  debug: debug != null ? debug : this._debug,
814
- onDestroy: ({ encodedState, room: room2 }) => {
916
+ onDestroy: (_0) => __async(this, [_0], function* ({ encodedState, room: room2 }) {
815
917
  this._logDebug(`${import_kleur2.default.blue("Deleting empty room:")} ${room2}`);
816
918
  const roomContext2 = __spreadValues({
817
919
  room: room2,
818
920
  encodedState
819
921
  }, platformRoomContext);
820
922
  this._rooms.delete(room2);
821
- this._listeners.onRoomDeleted(roomContext2);
923
+ yield Promise.resolve(this._listeners.onRoomDeleted(roomContext2));
924
+ yield this._platform.persistance.deleteStorageState(room2);
822
925
  if (this._debug) {
823
926
  const rooms = Array.from(this._rooms.keys());
824
927
  this._logDebug(`${import_kleur2.default.blue("Deleted room:")} ${room2}`);
825
928
  this._logDebug(`${import_kleur2.default.blue("Rooms available:")} ${rooms.join(", ")}`);
826
929
  }
827
- },
930
+ }),
828
931
  platform,
829
932
  router: this._router
830
933
  });
@@ -853,10 +956,16 @@ var PluvIO = class {
853
956
  constructor(options) {
854
957
  this._router = new PluvRouter({
855
958
  $GET_OTHERS: this.procedure.sync((data, { room, session, sessions }) => {
856
- const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
857
- (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
858
- [connectionId]: {
859
- connectionId,
959
+ const currentTime = (/* @__PURE__ */ new Date()).getTime();
960
+ const others = sessions.filter((wsSession) => {
961
+ if (wsSession.id === (session == null ? void 0 : session.id)) return false;
962
+ if (wsSession.quit) return false;
963
+ if (currentTime - wsSession.timers.ping > PING_TIMEOUT_MS) return false;
964
+ return true;
965
+ }).reduce(
966
+ (acc, { id, presence, user }) => __spreadProps(__spreadValues({}, acc), {
967
+ [id]: {
968
+ connectionId: id,
860
969
  presence,
861
970
  room,
862
971
  user
@@ -904,8 +1013,9 @@ var PluvIO = class {
904
1013
  $UPDATE_PRESENCE: this.procedure.broadcast((data, { session }) => {
905
1014
  const presence = data == null ? void 0 : data.presence;
906
1015
  if (!session) return {};
907
- session.presence = Object.assign(/* @__PURE__ */ Object.create(null), session.presence, presence);
908
- return { $PRESENCE_UPDATED: { presence } };
1016
+ const updated = Object.assign(/* @__PURE__ */ Object.create(null), session.presence, presence);
1017
+ session.webSocket.presence = updated;
1018
+ return { $PRESENCE_UPDATED: { presence: updated } };
909
1019
  }),
910
1020
  $UPDATE_STORAGE: this.procedure.broadcast((data, { context, doc, room }) => {
911
1021
  var _a;
@@ -953,14 +1063,15 @@ var PluvIO = class {
953
1063
  get _events() {
954
1064
  return this._router._events;
955
1065
  }
1066
+ get _registrationMode() {
1067
+ return this._platform._registrationMode;
1068
+ }
956
1069
  get procedure() {
957
1070
  return new PluvProcedure();
958
1071
  }
959
1072
  createToken(params) {
960
1073
  return __async(this, null, function* () {
961
- if (!this._authorize) {
962
- throw new Error("IO does not specify authorize during initialization.");
963
- }
1074
+ if (!this._authorize) throw new Error("IO does not specify authorize during initialization.");
964
1075
  const ioAuthorize = typeof this._authorize === "function" ? this._authorize(params) : this._authorize;
965
1076
  const secret = ioAuthorize.secret;
966
1077
  return yield authorize({