@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.mjs CHANGED
@@ -175,15 +175,23 @@ var PubSub = class extends AbstractPubSub {
175
175
  // src/AbstractPlatform.ts
176
176
  var AbstractPlatform = class {
177
177
  constructor(config = {}) {
178
- const { persistance, pubSub } = config;
178
+ this._initialized = false;
179
+ const { context, persistance, pubSub } = config;
180
+ context && (this._ioContext = context);
181
+ context && (this._roomContext = context);
179
182
  this.persistance = persistance != null ? persistance : new Persistance();
180
183
  this.pubSub = pubSub != null ? pubSub : new PubSub();
181
184
  }
185
+ _initialize() {
186
+ if (this._initialized) throw new Error("Platform is already initialized");
187
+ this._initialized = true;
188
+ return this;
189
+ }
182
190
  };
183
191
 
184
192
  // src/AbstractWebSocket.ts
185
193
  var AbstractWebSocket = class {
186
- constructor(config) {
194
+ constructor(webSocket, config) {
187
195
  /** The connection is not yet open. */
188
196
  this.CONNECTING = 0;
189
197
  /** The connection is open and ready to communicate. */
@@ -192,18 +200,36 @@ var AbstractWebSocket = class {
192
200
  this.CLOSING = 2;
193
201
  /** The connection is closed. */
194
202
  this.CLOSED = 3;
195
- const { room, sessionId } = config;
203
+ this._user = null;
204
+ const { persistance, room } = config;
205
+ this.persistance = persistance;
196
206
  this.room = room;
197
- this.sessionId = sessionId;
207
+ this.webSocket = webSocket;
208
+ }
209
+ getSession() {
210
+ return __async(this, null, function* () {
211
+ var _a;
212
+ const sessionId = this.sessionId;
213
+ const state = this.state;
214
+ const room = state.room;
215
+ const user = (_a = this._user) != null ? _a : yield this.persistance.getUser(room, sessionId);
216
+ this._user = user;
217
+ return __spreadProps(__spreadValues({}, state), {
218
+ id: sessionId,
219
+ user,
220
+ webSocket: this
221
+ });
222
+ });
198
223
  }
199
224
  handleError(params) {
200
225
  var _a, _b, _c;
201
- const { error, message = error instanceof Error ? error.message : "Unexpected error", session } = params;
226
+ const { error, message: _message, room, session } = params;
227
+ const message = error instanceof Error ? error.message : "Unexpected error";
202
228
  const stack = error instanceof Error ? (_a = error.stack) != null ? _a : null : null;
203
229
  return this.sendMessage({
204
230
  connectionId: (_b = session == null ? void 0 : session.id) != null ? _b : null,
205
231
  data: { message, stack },
206
- room: this.room,
232
+ room,
207
233
  type: "$ERROR",
208
234
  user: (_c = session == null ? void 0 : session.user) != null ? _c : null
209
235
  });
@@ -321,8 +347,10 @@ var authorize = (params) => {
321
347
  return { decode, encode };
322
348
  };
323
349
 
324
- // src/IORoom.ts
350
+ // src/constants.ts
325
351
  var PING_TIMEOUT_MS = 3e4;
352
+
353
+ // src/IORoom.ts
326
354
  var IORoom = class {
327
355
  constructor(id, config) {
328
356
  this._sessions = /* @__PURE__ */ new Map();
@@ -334,12 +362,21 @@ var IORoom = class {
334
362
  this._docFactory = crdt.doc(() => ({}));
335
363
  this._doc = this._docFactory.getEmpty();
336
364
  this._router = router;
337
- this._platform = platform;
365
+ this._platform = platform.initialize({ context });
338
366
  this.id = id;
339
367
  this._listeners = {
340
368
  onDestroy: (event) => onDestroy == null ? void 0 : onDestroy(event)
341
369
  };
342
370
  if (authorize2) this._authorize = authorize2;
371
+ const webSockets = this._platform.getWebSockets();
372
+ if (!webSockets.length) return;
373
+ webSockets.forEach((webSocket) => {
374
+ const deserialized = this._platform.getSerializedState(webSocket);
375
+ const sessionId = this._platform.getSessionId(webSocket);
376
+ if (!deserialized) return;
377
+ if (typeof sessionId !== "string") return;
378
+ this._sessions.set(sessionId, this._platform.convertWebSocket(webSocket, { room: this.id }));
379
+ });
343
380
  }
344
381
  get _events() {
345
382
  return this._router._events;
@@ -357,111 +394,135 @@ var IORoom = class {
357
394
  }
358
395
  getSize() {
359
396
  const currentTime = (/* @__PURE__ */ new Date()).getTime();
360
- return Array.from(this._sessions.values()).reduce((count, session) => {
361
- if (session.quit) return count;
362
- return currentTime - session.timers.ping > PING_TIMEOUT_MS ? count : count + 1;
397
+ return Array.from(this._sessions.values()).reduce((count, pluvWs) => {
398
+ var _a;
399
+ if (pluvWs.state.quit) return count;
400
+ const pingTime = (_a = this._platform.getLastPing(pluvWs)) != null ? _a : pluvWs.state.timers.ping;
401
+ return currentTime - pingTime > PING_TIMEOUT_MS ? count : count + 1;
363
402
  }, 0);
364
403
  }
365
- register(webSocket, options) {
366
- return __async(this, null, function* () {
367
- var _b;
368
- const _a = options, { token } = _a, _platformEventContext = __objRest(_a, ["token"]);
369
- const platformEventContext = _platformEventContext;
404
+ onClose(webSocket) {
405
+ this._ensureDetached();
406
+ const wsSession = this._getAbstractWs(webSocket);
407
+ if (!wsSession) return () => void 0;
408
+ return this._onClose(wsSession);
409
+ }
410
+ onError(webSocket) {
411
+ this._ensureDetached();
412
+ const wsSession = this._getAbstractWs(webSocket);
413
+ if (!wsSession) return () => void 0;
414
+ return this._onClose(wsSession);
415
+ }
416
+ onMessage(webSocket) {
417
+ this._ensureDetached();
418
+ const wsSession = this._getAbstractWs(webSocket);
419
+ if (!wsSession) return () => void 0;
420
+ return this._onMessage(wsSession);
421
+ }
422
+ register(_0) {
423
+ return __async(this, arguments, function* (webSocket, options = {}) {
424
+ const { token } = options;
370
425
  if (!this._uninitialize) yield this._initialize();
371
- const user = yield this._getAuthorizedUser(token, options);
372
- const sessionId = this._platform.randomUUID();
373
- const pluvWs = this._platform.convertWebSocket(webSocket, {
374
- room: this.id,
375
- sessionId,
376
- userId: (_b = user == null ? void 0 : user.id) != null ? _b : null
377
- });
378
- this._logDebug(`${colors.blue(`Registering connection for room ${this.id}:`)} ${sessionId}`);
379
- const uninitializeWs = yield pluvWs.initialize();
380
- const ioAuthorize = this._getIOAuthorize(options);
426
+ const user = yield this._getAuthorizedUser(token);
427
+ const ioAuthorize = this._getIOAuthorize();
381
428
  const isUnauthorized = !!(ioAuthorize == null ? void 0 : ioAuthorize.required) && !user;
382
- const isTokenInUse = !!user && Array.from(this._sessions.values()).some((session2) => {
383
- var _a2;
384
- return ((_a2 = session2.user) == null ? void 0 : _a2.id) === user.id;
385
- });
429
+ const isTokenInUse = !!user && (yield this._getSessions().then((sessions) => sessions.some((session) => {
430
+ var _a;
431
+ return ((_a = session.user) == null ? void 0 : _a.id) === user.id;
432
+ })));
433
+ const pluvWs = yield this._platform.convertWebSocket(webSocket, { room: this.id });
434
+ this._logDebug(`${colors.blue(`Registering connection for room ${this.id}:`)} ${pluvWs.sessionId}`);
435
+ yield this._platform.acceptWebSocket(pluvWs);
386
436
  if (isUnauthorized || isTokenInUse) {
387
- this._logDebug(`${colors.blue("Authorization failed for connection:")} ${sessionId}`);
388
- pluvWs.handleError({ error: new Error("Not authorized") });
437
+ this._logDebug(`${colors.blue("Authorization failed for connection:")} ${pluvWs.sessionId}`);
438
+ pluvWs.handleError({ error: new Error("Not authorized"), room: this.id });
389
439
  pluvWs.close(3e3, "WebSocket unauthorized.");
390
440
  return;
391
441
  }
392
- const currentTime = (/* @__PURE__ */ new Date()).getTime();
393
- const session = {
394
- id: sessionId,
395
- presence: null,
396
- quit: false,
397
- room: this.id,
398
- timers: {
399
- ping: currentTime
400
- },
401
- webSocket: pluvWs,
402
- user
403
- };
404
- this._sessions.set(session.id, session);
405
- yield this._platform.persistance.addUser(this.id, sessionId, user != null ? user : {});
406
- const onClose = this._onClose(session, uninitializeWs).bind(this);
407
- const onMessage = this._onMessage(session, platformEventContext).bind(this);
408
- pluvWs.addEventListener("close", onClose);
409
- pluvWs.addEventListener("error", onClose);
410
- pluvWs.addEventListener("message", onMessage);
411
- this._emitRegistered(session);
412
- this._logDebug(`${colors.blue(`Registered connection for room ${this.id}:`)} ${sessionId}`);
442
+ this._sessions.set(pluvWs.sessionId, pluvWs);
443
+ yield this._platform.persistance.addUser(this.id, pluvWs.sessionId, user != null ? user : {});
444
+ if (this._platform._registrationMode === "attached") {
445
+ const onClose = this._onClose(pluvWs).bind(this);
446
+ const onMessage = this._onMessage(pluvWs).bind(this);
447
+ pluvWs.addEventListener("close", onClose);
448
+ pluvWs.addEventListener("error", onClose);
449
+ pluvWs.addEventListener("message", onMessage);
450
+ }
451
+ yield this._emitRegistered(pluvWs);
452
+ this._logDebug(`${colors.blue(`Registered connection for room ${this.id}:`)} ${pluvWs.sessionId}`);
413
453
  const size = this.getSize();
414
454
  this._logDebug(`${colors.blue(`Room ${this.id} size:`)} ${size}`);
415
455
  });
416
456
  }
417
457
  _broadcast(params) {
418
- var _a;
419
- const { message, senderId } = params;
420
- const currentTime = (/* @__PURE__ */ new Date()).getTime();
421
- const sender = senderId ? this._sessions.get(senderId) : null;
422
- const quitters = Array.from(this._sessions.values()).filter(
423
- (session) => session.quit || currentTime - session.timers.ping > PING_TIMEOUT_MS
424
- );
425
- quitters.forEach((session) => {
426
- this._sessions.delete(session.id);
458
+ return __async(this, null, function* () {
459
+ var _a, _b, _c;
460
+ const { message, senderId } = params;
461
+ const sender = senderId ? (_a = this._sessions.get(senderId)) != null ? _a : null : null;
462
+ const session = (_b = yield sender == null ? void 0 : sender.getSession()) != null ? _b : null;
463
+ const user = (_c = session == null ? void 0 : session.user) != null ? _c : null;
464
+ this._platform.pubSub.publish(this.id, __spreadValues({
465
+ connectionId: senderId != null ? senderId : null,
466
+ room: this.id,
467
+ user
468
+ }, message));
427
469
  });
428
- this._emitQuitters(quitters);
429
- this._platform.pubSub.publish(this.id, __spreadValues({
430
- connectionId: senderId != null ? senderId : null,
431
- room: this.id,
432
- user: (_a = sender == null ? void 0 : sender.user) != null ? _a : null
433
- }, message));
434
- }
435
- _emitQuitters(quitters) {
436
- quitters.forEach((quitter) => {
437
- this._broadcast({
438
- message: {
439
- type: "$EXIT",
440
- data: { sessionId: quitter.id }
441
- },
442
- senderId: quitter.id
470
+ }
471
+ _emitQuitters() {
472
+ return __async(this, null, function* () {
473
+ const currentTime = (/* @__PURE__ */ new Date()).getTime();
474
+ const quitters = Array.from(this._sessions.values()).filter(
475
+ (pluvWs) => pluvWs.state.quit || currentTime - pluvWs.state.timers.ping > PING_TIMEOUT_MS
476
+ );
477
+ quitters.forEach((pluvWs) => {
478
+ this._sessions.delete(pluvWs.sessionId);
443
479
  });
480
+ const promises = quitters.map((quitter) => __async(this, null, function* () {
481
+ yield this._broadcast({
482
+ message: {
483
+ type: "$EXIT",
484
+ data: { sessionId: quitter.sessionId }
485
+ },
486
+ senderId: quitter.sessionId
487
+ });
488
+ }));
489
+ yield Promise.all(promises);
444
490
  });
445
491
  }
446
- _emitRegistered(session) {
447
- this._sendSelfMessage(
448
- {
449
- type: "$REGISTERED",
450
- data: { sessionId: session.id }
451
- },
452
- session
453
- );
492
+ _emitRegistered(pluvWs) {
493
+ return __async(this, null, function* () {
494
+ const session = yield pluvWs.getSession();
495
+ const sessionId = session.id;
496
+ const user = session.user;
497
+ yield this._sendSelfMessage({ type: "$REGISTERED", data: { sessionId } }, { sessionId, user });
498
+ });
454
499
  }
455
- _getIOAuthorize(context) {
456
- if (typeof this._authorize === "function") {
457
- return this._authorize(context);
500
+ _ensureDetached() {
501
+ if (this._platform._registrationMode === "detached") return;
502
+ throw new Error("Platform must use detached mode");
503
+ }
504
+ _getAbstractWs(webSocket) {
505
+ var _a, _b;
506
+ if (webSocket instanceof AbstractWebSocket) return webSocket;
507
+ const sessionId = this._platform.getSessionId(webSocket);
508
+ if (typeof sessionId === "string") {
509
+ const session = (_a = this._sessions.get(sessionId)) != null ? _a : null;
510
+ if (session) return session;
458
511
  }
459
- return this._authorize;
512
+ const sessions = Array.from(this._sessions.values());
513
+ return (_b = sessions.find((pluvWs) => pluvWs.webSocket === webSocket)) != null ? _b : null;
460
514
  }
461
- _getAuthorizedUser(token, context) {
515
+ _getSession(webSocket) {
516
+ return __async(this, null, function* () {
517
+ const pluvWs = this._getAbstractWs(webSocket);
518
+ if (!pluvWs) throw new Error("Session could not be found");
519
+ return yield pluvWs.getSession();
520
+ });
521
+ }
522
+ _getAuthorizedUser(token) {
462
523
  return __async(this, null, function* () {
463
524
  var _a;
464
- const ioAuthorize = this._getIOAuthorize(context);
525
+ const ioAuthorize = this._getIOAuthorize();
465
526
  if (!ioAuthorize) return null;
466
527
  if (!token) return null;
467
528
  const payload = yield authorize({
@@ -487,6 +548,12 @@ var IORoom = class {
487
548
  }
488
549
  });
489
550
  }
551
+ _getIOAuthorize() {
552
+ if (typeof this._authorize === "function") {
553
+ return this._authorize(this._context);
554
+ }
555
+ return this._authorize;
556
+ }
490
557
  _getProcedure(message) {
491
558
  var _a;
492
559
  return (_a = this._router._events[message.type]) != null ? _a : null;
@@ -496,6 +563,14 @@ var IORoom = class {
496
563
  if (!procedure) return message.data;
497
564
  return procedure.config.input ? procedure.config.input.parse(message.data) : message.data;
498
565
  }
566
+ _getSessions() {
567
+ return __async(this, null, function* () {
568
+ const promises = Array.from(this._sessions.values()).map((pluvWs) => __async(this, null, function* () {
569
+ return yield pluvWs.getSession();
570
+ }));
571
+ return yield Promise.all(promises);
572
+ });
573
+ }
499
574
  _initialize() {
500
575
  return __async(this, null, function* () {
501
576
  this._logDebug(`${colors.blue(`Initializing room ${this.id}:`)}`);
@@ -503,14 +578,15 @@ var IORoom = class {
503
578
  yield this._uninitialize();
504
579
  this._uninitialize = null;
505
580
  }
506
- const pubSubId = yield this._platform.pubSub.subscribe(this.id, (_a) => {
507
- var _b = _a, { options = {} } = _b, message = __objRest(_b, ["options"]);
508
- const session = {
509
- id: message.connectionId,
510
- user: message.user
511
- };
512
- this._sendMessage(message, session, options);
513
- });
581
+ const pubSubId = yield this._platform.pubSub.subscribe(
582
+ this.id,
583
+ (_a) => __async(this, null, function* () {
584
+ var _b = _a, { options = {} } = _b, message = __objRest(_b, ["options"]);
585
+ const sessionId = message.connectionId;
586
+ const user = message.user;
587
+ yield this._sendMessage(message, { sessionId, user }, options);
588
+ })
589
+ );
514
590
  this._uninitialize = () => __async(this, null, function* () {
515
591
  this._platform.pubSub.unsubscribe(pubSubId);
516
592
  const encodedState = this._doc.getEncodedState();
@@ -527,43 +603,48 @@ var IORoom = class {
527
603
  _logDebug(...data) {
528
604
  this._debug && console.log(...data);
529
605
  }
530
- _onClose(session, callback) {
606
+ _onClose(webSocket) {
531
607
  return () => {
532
608
  if (!this._uninitialize) return;
533
- session.quit = true;
534
- this._logDebug(`${colors.blue(`(Unregistering connection for room ${this.id}:`)} ${session.id}`);
535
- this._sessions.delete(session.id);
536
- this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
609
+ webSocket.state.quit = true;
610
+ this._logDebug(`${colors.blue(`Unregistering connection for room ${this.id}:`)} ${webSocket.sessionId}`);
611
+ this._sessions.delete(webSocket.sessionId);
612
+ this._platform.persistance.deleteUser(this.id, webSocket.sessionId).finally(() => __async(this, null, function* () {
537
613
  var _a;
538
- this._broadcast({
614
+ yield this._broadcast({
539
615
  message: {
540
616
  type: "$EXIT",
541
- data: { sessionId: session.id }
617
+ data: { sessionId: webSocket.sessionId }
542
618
  },
543
- senderId: session.id
619
+ senderId: webSocket.sessionId
544
620
  });
545
- callback == null ? void 0 : callback();
546
621
  const size = this.getSize();
547
- this._logDebug(`${colors.blue(`Unregistered connection for room ${this.id}:`)} ${session.id}`);
622
+ this._logDebug(`${colors.blue(`Unregistered connection for room ${this.id}:`)} ${webSocket.sessionId}`);
548
623
  this._logDebug(`${colors.blue(`Room ${this.id} size:`)} ${size}`);
549
624
  if (size) return;
550
625
  (_a = this._uninitialize) == null ? void 0 : _a.call(this);
551
- });
626
+ }));
552
627
  };
553
628
  }
554
- _onMessage(session, platformEventContext) {
555
- return (event) => {
629
+ _onMessage(webSocket) {
630
+ return (event) => __async(this, null, function* () {
556
631
  var _a, _b, _c, _d, _e, _f;
632
+ const pluvWs = this._getAbstractWs(webSocket);
633
+ if (!pluvWs) throw new Error("Could not get session");
634
+ const [session, sessions] = yield Promise.all([
635
+ this._getSession(pluvWs),
636
+ this._getSessions()
637
+ ]);
557
638
  const baseContext = {
558
639
  context: this._context,
559
640
  doc: this._doc,
560
641
  room: this.id,
561
642
  session,
562
- sessions: this._sessions
643
+ sessions
563
644
  };
564
645
  if (!this._uninitialize) return;
565
- if (session.quit) {
566
- session.webSocket.close(1011, "WebSocket broken.");
646
+ if (pluvWs.state.quit) {
647
+ pluvWs.close(1011, "WebSocket broken.");
567
648
  return;
568
649
  }
569
650
  const message = this._parseMessage(event);
@@ -574,47 +655,52 @@ var IORoom = class {
574
655
  try {
575
656
  inputs = this._getProcedureInputs(message);
576
657
  } catch (error) {
577
- session == null ? void 0 : session.webSocket.handleError({
658
+ pluvWs.handleError({
578
659
  error,
579
660
  message: "Invalid input",
661
+ room: this.id,
580
662
  session
581
663
  });
582
664
  return;
583
665
  }
584
- const extendedContext = __spreadProps(__spreadValues({}, baseContext), {
585
- context: __spreadValues(__spreadValues({}, this._context), platformEventContext)
586
- });
666
+ const extendedContext = __spreadProps(__spreadValues({}, baseContext), { context: __spreadValues({}, this._context) });
587
667
  Promise.all([
588
668
  (_b = (_a = procedure.config).broadcast) == null ? void 0 : _b.call(_a, inputs, extendedContext),
589
669
  (_d = (_c = procedure.config).self) == null ? void 0 : _d.call(_c, inputs, extendedContext),
590
670
  (_f = (_e = procedure.config).sync) == null ? void 0 : _f.call(_e, inputs, baseContext)
591
- ]).then(([broadcast, self, sync]) => {
671
+ ]).then((_0) => __async(this, [_0], function* ([broadcast, self, sync]) {
672
+ const sessionId = session.id;
673
+ const user = session.user;
592
674
  if (broadcast) {
593
675
  Object.entries(broadcast).forEach(([type, data]) => {
594
676
  this._broadcast({
595
677
  message: { data, type },
596
- senderId: session.id
678
+ senderId: sessionId
597
679
  });
598
680
  });
599
681
  }
600
682
  if (self) {
601
- Object.entries(self).forEach(([type, data]) => {
602
- this._sendSelfMessage({ data, type }, session);
603
- });
683
+ yield Promise.all(
684
+ Object.entries(self).map((_02) => __async(this, [_02], function* ([type, data]) {
685
+ yield this._sendSelfMessage({ data, type }, { sessionId, user });
686
+ }))
687
+ );
604
688
  }
605
689
  if (sync) {
606
690
  this._platform.pubSub.publish(this.id, __spreadValues({
607
691
  connectionId: session.id,
608
692
  options: { type: "sync" },
609
693
  room: this.id,
610
- user: session.user
694
+ user
611
695
  }, message));
612
- Object.entries(sync).forEach(([type, data]) => {
613
- this._sendSelfMessage({ data, type }, session);
614
- });
696
+ yield Promise.all(
697
+ Object.entries(sync).map((_02) => __async(this, [_02], function* ([type, data]) {
698
+ yield this._sendSelfMessage({ data, type }, { sessionId, user });
699
+ }))
700
+ );
615
701
  }
616
- });
617
- };
702
+ }));
703
+ });
618
704
  }
619
705
  _parseMessage(message) {
620
706
  try {
@@ -627,80 +713,93 @@ var IORoom = class {
627
713
  return null;
628
714
  }
629
715
  }
630
- _sendMessage(message, sender, options = {}) {
631
- switch (options.type) {
632
- case "self": {
633
- this._sendSelfMessage(message, sender);
634
- return;
635
- }
636
- case "sync": {
637
- this._sendSyncMessage(message, sender);
638
- return;
639
- }
640
- case "broadcast":
641
- default: {
642
- const sessionIds = options.sessionIds;
643
- this._sendBroadcastMessage(message, sender, sessionIds);
716
+ _sendMessage(_0, _1) {
717
+ return __async(this, arguments, function* (message, sender, options = {}) {
718
+ switch (options.type) {
719
+ case "self": {
720
+ yield this._sendSelfMessage(message, sender);
721
+ return;
722
+ }
723
+ case "sync": {
724
+ yield this._sendSyncMessage(message, sender);
725
+ return;
726
+ }
727
+ case "broadcast":
728
+ default: {
729
+ const sessionIds = options.sessionIds;
730
+ yield this._sendBroadcastMessage(message, sender, sessionIds);
731
+ }
644
732
  }
645
- }
733
+ });
646
734
  }
647
735
  _sendBroadcastMessage(message, sender, sessionIds) {
648
- var _a, _b;
649
- const senderId = (_a = sender == null ? void 0 : sender.id) != null ? _a : null;
650
- const sessions = (_b = sessionIds == null ? void 0 : sessionIds.reduce((dict, id) => {
651
- const _session = this._sessions.get(id);
652
- return _session ? dict.set(id, _session) : dict;
653
- }, /* @__PURE__ */ new Map())) != null ? _b : this._sessions;
654
- Array.from(sessions.values()).forEach((_session) => {
655
- _session.webSocket.sendMessage(__spreadValues({
736
+ return __async(this, null, function* () {
737
+ var _a, _b;
738
+ const { data, type } = message;
739
+ const connectionId = (_a = sender == null ? void 0 : sender.sessionId) != null ? _a : null;
740
+ const room = this.id;
741
+ const webSockets = (_b = sessionIds == null ? void 0 : sessionIds.reduce((dict, id) => {
742
+ const pluvWs = this._sessions.get(id);
743
+ return pluvWs ? dict.set(id, pluvWs) : dict;
744
+ }, /* @__PURE__ */ new Map())) != null ? _b : this._sessions;
745
+ const promises = Array.from(webSockets.values()).map((pluvWs) => __async(this, null, function* () {
746
+ var _a2;
747
+ const session = yield pluvWs.getSession();
748
+ const user = (_a2 = session.user) != null ? _a2 : null;
749
+ pluvWs.sendMessage({ connectionId, data, room, type, user });
750
+ }));
751
+ yield Promise.all(promises);
752
+ });
753
+ }
754
+ _sendSelfMessage(message, sender) {
755
+ return __async(this, null, function* () {
756
+ var _a, _b;
757
+ const senderId = (_a = sender == null ? void 0 : sender.sessionId) != null ? _a : null;
758
+ if (typeof senderId !== "string") return;
759
+ const pluvWs = this._sessions.get(senderId);
760
+ if (!pluvWs) return;
761
+ if (message.type === "$PONG") yield this._emitQuitters();
762
+ const session = yield pluvWs.getSession();
763
+ const user = (_b = session.user) != null ? _b : null;
764
+ pluvWs.sendMessage(__spreadValues({
656
765
  connectionId: senderId,
657
766
  room: this.id,
658
- user: _session.user
767
+ user
659
768
  }, message));
660
769
  });
661
770
  }
662
- _sendSelfMessage(message, sender) {
663
- const senderId = sender == null ? void 0 : sender.id;
664
- if (!senderId) return;
665
- const session = this._sessions.get(senderId);
666
- if (!session) return;
667
- session.webSocket.sendMessage(__spreadValues({
668
- connectionId: sender.id,
669
- room: this.id,
670
- user: sender.user
671
- }, message));
672
- }
673
771
  _sendSyncMessage(message, sender) {
674
- var _a;
675
- const senderId = sender == null ? void 0 : sender.id;
676
- if (!senderId) return;
677
- const context = {
678
- context: this._context,
679
- doc: this._doc,
680
- room: this.id,
681
- session: null,
682
- sessions: this._sessions
683
- };
684
- const resolver = (_a = this._getProcedure(message)) == null ? void 0 : _a.config.sync;
685
- if (!resolver) return;
686
- let inputs;
687
- try {
688
- inputs = this._getProcedureInputs(message);
689
- } catch (e) {
690
- return;
691
- }
692
- Promise.resolve(resolver(inputs, context)).then((output) => {
693
- if (!output) return;
694
- Object.keys(output).forEach((type) => {
695
- var _a2, _b;
696
- const data = (_a2 = output[type]) != null ? _a2 : {};
697
- this._platform.pubSub.publish(this.id, {
698
- connectionId: senderId,
699
- data,
700
- options: { type: "self" },
701
- room: this.id,
702
- type,
703
- user: (_b = sender.user) != null ? _b : null
772
+ return __async(this, null, function* () {
773
+ var _a;
774
+ if (!sender) return;
775
+ const context = {
776
+ context: this._context,
777
+ doc: this._doc,
778
+ room: this.id,
779
+ session: null,
780
+ sessions: yield this._getSessions()
781
+ };
782
+ const resolver = (_a = this._getProcedure(message)) == null ? void 0 : _a.config.sync;
783
+ if (!resolver) return;
784
+ let inputs;
785
+ try {
786
+ inputs = this._getProcedureInputs(message);
787
+ } catch (e) {
788
+ return;
789
+ }
790
+ Promise.resolve(resolver(inputs, context)).then((output) => {
791
+ if (!output) return;
792
+ Object.keys(output).forEach((type) => {
793
+ var _a2, _b;
794
+ const data = (_a2 = output[type]) != null ? _a2 : {};
795
+ this._platform.pubSub.publish(this.id, {
796
+ connectionId: sender.sessionId,
797
+ data,
798
+ options: { type: "self" },
799
+ room: this.id,
800
+ type,
801
+ user: (_b = sender.user) != null ? _b : null
802
+ });
704
803
  });
705
804
  });
706
805
  });
@@ -708,7 +807,7 @@ var IORoom = class {
708
807
  };
709
808
 
710
809
  // src/version.generated.ts
711
- var version = "0.19.0";
810
+ var version = "0.21.0";
712
811
 
713
812
  // src/PluvServer.ts
714
813
  var PluvServer = class {
@@ -741,6 +840,9 @@ var PluvServer = class {
741
840
  get _events() {
742
841
  return this._router._events;
743
842
  }
843
+ get _registrationMode() {
844
+ return this._platform._registrationMode;
845
+ }
744
846
  createToken(params) {
745
847
  return __async(this, null, function* () {
746
848
  if (!this._authorize) {
@@ -754,9 +856,9 @@ var PluvServer = class {
754
856
  }).encode(params);
755
857
  });
756
858
  }
757
- getRoom(room, options) {
758
- var _b;
759
- const _a = options, { debug } = _a, platformRoomContext = __objRest(_a, ["debug"]);
859
+ getRoom(room, ...options) {
860
+ var _a, _c;
861
+ const _b = (_a = options[0]) != null ? _a : {}, { debug } = _b, platformRoomContext = __objRest(_b, ["debug"]);
760
862
  this._purgeEmptyRooms();
761
863
  if (!/^[a-z0-9]+[a-z0-9\-_]+[a-z0-9]+$/i.test(room)) {
762
864
  throw new Error("Unsupported room name");
@@ -766,24 +868,25 @@ var PluvServer = class {
766
868
  if (oldRoom) return oldRoom;
767
869
  const roomContext = __spreadValues(__spreadValues({}, this._context), platformRoomContext);
768
870
  const newRoom = new IORoom(room, {
769
- authorize: (_b = this._authorize) != null ? _b : void 0,
871
+ authorize: (_c = this._authorize) != null ? _c : void 0,
770
872
  context: roomContext,
771
873
  crdt: this._crdt,
772
874
  debug: debug != null ? debug : this._debug,
773
- onDestroy: ({ encodedState, room: room2 }) => {
875
+ onDestroy: (_0) => __async(this, [_0], function* ({ encodedState, room: room2 }) {
774
876
  this._logDebug(`${colors2.blue("Deleting empty room:")} ${room2}`);
775
877
  const roomContext2 = __spreadValues({
776
878
  room: room2,
777
879
  encodedState
778
880
  }, platformRoomContext);
779
881
  this._rooms.delete(room2);
780
- this._listeners.onRoomDeleted(roomContext2);
882
+ yield Promise.resolve(this._listeners.onRoomDeleted(roomContext2));
883
+ yield this._platform.persistance.deleteStorageState(room2);
781
884
  if (this._debug) {
782
885
  const rooms = Array.from(this._rooms.keys());
783
886
  this._logDebug(`${colors2.blue("Deleted room:")} ${room2}`);
784
887
  this._logDebug(`${colors2.blue("Rooms available:")} ${rooms.join(", ")}`);
785
888
  }
786
- },
889
+ }),
787
890
  platform,
788
891
  router: this._router
789
892
  });
@@ -812,10 +915,16 @@ var PluvIO = class {
812
915
  constructor(options) {
813
916
  this._router = new PluvRouter({
814
917
  $GET_OTHERS: this.procedure.sync((data, { room, session, sessions }) => {
815
- const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
816
- (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
817
- [connectionId]: {
818
- connectionId,
918
+ const currentTime = (/* @__PURE__ */ new Date()).getTime();
919
+ const others = sessions.filter((wsSession) => {
920
+ if (wsSession.id === (session == null ? void 0 : session.id)) return false;
921
+ if (wsSession.quit) return false;
922
+ if (currentTime - wsSession.timers.ping > PING_TIMEOUT_MS) return false;
923
+ return true;
924
+ }).reduce(
925
+ (acc, { id, presence, user }) => __spreadProps(__spreadValues({}, acc), {
926
+ [id]: {
927
+ connectionId: id,
819
928
  presence,
820
929
  room,
821
930
  user
@@ -863,8 +972,9 @@ var PluvIO = class {
863
972
  $UPDATE_PRESENCE: this.procedure.broadcast((data, { session }) => {
864
973
  const presence = data == null ? void 0 : data.presence;
865
974
  if (!session) return {};
866
- session.presence = Object.assign(/* @__PURE__ */ Object.create(null), session.presence, presence);
867
- return { $PRESENCE_UPDATED: { presence } };
975
+ const updated = Object.assign(/* @__PURE__ */ Object.create(null), session.presence, presence);
976
+ session.webSocket.presence = updated;
977
+ return { $PRESENCE_UPDATED: { presence: updated } };
868
978
  }),
869
979
  $UPDATE_STORAGE: this.procedure.broadcast((data, { context, doc, room }) => {
870
980
  var _a;
@@ -912,14 +1022,15 @@ var PluvIO = class {
912
1022
  get _events() {
913
1023
  return this._router._events;
914
1024
  }
1025
+ get _registrationMode() {
1026
+ return this._platform._registrationMode;
1027
+ }
915
1028
  get procedure() {
916
1029
  return new PluvProcedure();
917
1030
  }
918
1031
  createToken(params) {
919
1032
  return __async(this, null, function* () {
920
- if (!this._authorize) {
921
- throw new Error("IO does not specify authorize during initialization.");
922
- }
1033
+ if (!this._authorize) throw new Error("IO does not specify authorize during initialization.");
923
1034
  const ioAuthorize = typeof this._authorize === "function" ? this._authorize(params) : this._authorize;
924
1035
  const secret = ioAuthorize.secret;
925
1036
  return yield authorize({