@pluv/io 0.3.9 → 0.4.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/dist/index.js CHANGED
@@ -261,6 +261,10 @@ var AbstractWebSocket = class {
261
261
  // src/PluvIO.ts
262
262
  var import_kleur2 = __toESM(require("kleur"));
263
263
 
264
+ // src/IORoom.ts
265
+ var import_crdt_yjs = require("@pluv/crdt-yjs");
266
+ var import_kleur = __toESM(require("kleur"));
267
+
264
268
  // src/authorize.ts
265
269
  var import_hkdf = __toESM(require("@panva/hkdf"));
266
270
  var import_jose = require("jose");
@@ -298,25 +302,14 @@ var authorize = (params) => {
298
302
  };
299
303
 
300
304
  // src/IORoom.ts
301
- var import_crdt_yjs = require("@pluv/crdt-yjs");
302
- var import_kleur = __toESM(require("kleur"));
303
305
  var PING_TIMEOUT_MS = 3e4;
304
306
  var IORoom = class {
305
307
  constructor(id, config) {
306
308
  this._doc = (0, import_crdt_yjs.doc)();
307
- this._initialStorage = null;
308
309
  this._sessions = /* @__PURE__ */ new Map();
309
310
  this._uninitialize = null;
310
311
  this._authorize = null;
311
- const {
312
- authorize: authorize2,
313
- context,
314
- debug,
315
- events,
316
- initialStorage,
317
- onDestroy,
318
- platform
319
- } = config;
312
+ const { authorize: authorize2, context, debug, events, onDestroy, platform } = config;
320
313
  this._context = context;
321
314
  this._debug = debug;
322
315
  this._events = events;
@@ -329,8 +322,6 @@ var IORoom = class {
329
322
  };
330
323
  if (authorize2)
331
324
  this._authorize = authorize2;
332
- if (initialStorage)
333
- this._initialStorage = initialStorage;
334
325
  }
335
326
  broadcast(event, data) {
336
327
  const message = { type: event, data };
@@ -396,101 +387,12 @@ var IORoom = class {
396
387
  sessionId,
397
388
  user != null ? user : {}
398
389
  );
390
+ const onClose = this._onClose(session, uninitializeWs).bind(this);
391
+ const onMessage = this._onMessage(session).bind(this);
392
+ pluvWs.addEventListener("close", onClose);
393
+ pluvWs.addEventListener("error", onClose);
394
+ pluvWs.addEventListener("message", onMessage);
399
395
  this._emitRegistered(session);
400
- this._emitStorageReceived(session);
401
- this._emitUserJoined(session);
402
- const context = {
403
- context: this._context,
404
- doc: this._doc,
405
- room: this.id,
406
- session,
407
- sessions: this._sessions
408
- };
409
- pluvWs.addEventListener("message", (event) => {
410
- if (!this._uninitialize)
411
- return;
412
- if (session.quit) {
413
- session.webSocket.close(1011, "WebSocket broken.");
414
- return;
415
- }
416
- const message = this._parseMessage(event);
417
- if (!message)
418
- return;
419
- const eventConfig = this._getEventConfig(message);
420
- if (!eventConfig)
421
- return;
422
- this._resolveMessage(message, context).then((output) => {
423
- var _a2, _b;
424
- if (!output)
425
- return;
426
- const eventType = (_b = (_a2 = eventConfig.options) == null ? void 0 : _a2.type) != null ? _b : "broadcast";
427
- switch (eventType) {
428
- case "self": {
429
- Object.entries(output).forEach(([type, data]) => {
430
- this._sendSelfMessage({ data, type }, session);
431
- });
432
- return;
433
- }
434
- case "sync": {
435
- this._platform.pubSub.publish(this.id, __spreadValues({
436
- connectionId: session.id,
437
- options: eventConfig.options,
438
- room: this.id,
439
- user: session.user
440
- }, message));
441
- Object.entries(output).forEach(([type, data]) => {
442
- this._sendSelfMessage({ data, type }, session);
443
- });
444
- return;
445
- }
446
- case "broadcast":
447
- default: {
448
- Object.entries(output).forEach(([type, data]) => {
449
- this._broadcast({
450
- message: { data, type },
451
- senderId: session.id
452
- });
453
- });
454
- }
455
- }
456
- });
457
- });
458
- const closeHandler = () => {
459
- if (!this._uninitialize)
460
- return;
461
- session.quit = true;
462
- this._logDebug(
463
- `${import_kleur.default.blue(
464
- `(Unregistering connection for room ${this.id}:`
465
- )} ${sessionId}`
466
- );
467
- this._sessions.delete(session.id);
468
- this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
469
- var _a2;
470
- this._broadcast({
471
- message: {
472
- type: "$EXIT",
473
- data: { sessionId: session.id }
474
- },
475
- senderId: session.id
476
- });
477
- uninitializeWs();
478
- const size2 = this.getSize();
479
- this._logDebug(
480
- `${import_kleur.default.blue(
481
- `Unregistered connection for room ${this.id}:`
482
- )} ${sessionId}`
483
- );
484
- this._logDebug(
485
- `${import_kleur.default.blue(`Room ${this.id} size:`)} ${size2}`
486
- );
487
- if (size2)
488
- return;
489
- (_a2 = this._uninitialize) == null ? void 0 : _a2.call(this);
490
- });
491
- };
492
- pluvWs.addEventListener("close", closeHandler);
493
- pluvWs.addEventListener("error", closeHandler);
494
396
  this._logDebug(
495
397
  `${import_kleur.default.blue(
496
398
  `Registered connection for room ${this.id}:`
@@ -538,34 +440,6 @@ var IORoom = class {
538
440
  session
539
441
  );
540
442
  }
541
- _emitStorageReceived(session) {
542
- this._platform.persistance.getStorageState(this.id).then((_state) => __async(this, null, function* () {
543
- var _a, _b;
544
- const state = (_b = _state != null ? _state : yield (_a = this._initialStorage) == null ? void 0 : _a.call(this)) != null ? _b : this._doc.encodeStateAsUpdate();
545
- this._sendSelfMessage(
546
- {
547
- type: "$STORAGE_RECEIVED",
548
- data: { state }
549
- },
550
- session
551
- );
552
- }));
553
- }
554
- _emitUserJoined(session) {
555
- const user = session.user;
556
- if (!user)
557
- return;
558
- this._broadcast({
559
- message: {
560
- type: "$USER_JOINED",
561
- data: {
562
- connectionId: session.id,
563
- user
564
- }
565
- },
566
- senderId: session.id
567
- });
568
- }
569
443
  _getAuthorizedUser(token) {
570
444
  return __async(this, null, function* () {
571
445
  var _a;
@@ -604,6 +478,19 @@ var IORoom = class {
604
478
  var _a;
605
479
  return (_a = this._events[message.type]) != null ? _a : null;
606
480
  }
481
+ _getEventInputs(message) {
482
+ const eventConfig = this._getEventConfig(message);
483
+ if (!eventConfig)
484
+ return message.data;
485
+ return eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
486
+ }
487
+ _getEventResolverObject(message) {
488
+ const eventConfig = this._getEventConfig(message);
489
+ if (!eventConfig)
490
+ return {};
491
+ const resolver = eventConfig.resolver;
492
+ return typeof resolver === "function" ? { broadcast: resolver } : __spreadValues({}, resolver);
493
+ }
607
494
  _initialize() {
608
495
  return __async(this, null, function* () {
609
496
  this._logDebug(`${import_kleur.default.blue(`Initializing room ${this.id}:`)}`);
@@ -635,6 +522,105 @@ var IORoom = class {
635
522
  _logDebug(...data) {
636
523
  this._debug && console.log(...data);
637
524
  }
525
+ _onClose(session, callback) {
526
+ return () => {
527
+ if (!this._uninitialize)
528
+ return;
529
+ session.quit = true;
530
+ this._logDebug(
531
+ `${import_kleur.default.blue(
532
+ `(Unregistering connection for room ${this.id}:`
533
+ )} ${session.id}`
534
+ );
535
+ this._sessions.delete(session.id);
536
+ this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
537
+ var _a;
538
+ this._broadcast({
539
+ message: {
540
+ type: "$EXIT",
541
+ data: { sessionId: session.id }
542
+ },
543
+ senderId: session.id
544
+ });
545
+ callback == null ? void 0 : callback();
546
+ const size = this.getSize();
547
+ this._logDebug(
548
+ `${import_kleur.default.blue(
549
+ `Unregistered connection for room ${this.id}:`
550
+ )} ${session.id}`
551
+ );
552
+ this._logDebug(
553
+ `${import_kleur.default.blue(`Room ${this.id} size:`)} ${size}`
554
+ );
555
+ if (size)
556
+ return;
557
+ (_a = this._uninitialize) == null ? void 0 : _a.call(this);
558
+ });
559
+ };
560
+ }
561
+ _onMessage(session) {
562
+ return (event) => {
563
+ var _a, _b, _c;
564
+ const context = {
565
+ context: this._context,
566
+ doc: this._doc,
567
+ room: this.id,
568
+ session,
569
+ sessions: this._sessions
570
+ };
571
+ if (!this._uninitialize)
572
+ return;
573
+ if (session.quit) {
574
+ session.webSocket.close(1011, "WebSocket broken.");
575
+ return;
576
+ }
577
+ const message = this._parseMessage(event);
578
+ if (!message)
579
+ return;
580
+ const resolver = this._getEventResolverObject(message);
581
+ let inputs;
582
+ try {
583
+ inputs = this._getEventInputs(message);
584
+ } catch (error) {
585
+ session == null ? void 0 : session.webSocket.handleError({
586
+ error,
587
+ message: "Invalid input",
588
+ session
589
+ });
590
+ return;
591
+ }
592
+ Promise.all([
593
+ (_a = resolver.broadcast) == null ? void 0 : _a.call(resolver, inputs, context),
594
+ (_b = resolver.self) == null ? void 0 : _b.call(resolver, inputs, context),
595
+ (_c = resolver.sync) == null ? void 0 : _c.call(resolver, inputs, context)
596
+ ]).then(([broadcast, self, sync]) => {
597
+ if (broadcast) {
598
+ Object.entries(broadcast).forEach(([type, data]) => {
599
+ this._broadcast({
600
+ message: { data, type },
601
+ senderId: session.id
602
+ });
603
+ });
604
+ }
605
+ if (self) {
606
+ Object.entries(self).forEach(([type, data]) => {
607
+ this._sendSelfMessage({ data, type }, session);
608
+ });
609
+ }
610
+ if (sync) {
611
+ this._platform.pubSub.publish(this.id, __spreadValues({
612
+ connectionId: session.id,
613
+ options: { type: "sync" },
614
+ room: this.id,
615
+ user: session.user
616
+ }, message));
617
+ Object.entries(sync).forEach(([type, data]) => {
618
+ this._sendSelfMessage({ data, type }, session);
619
+ });
620
+ }
621
+ });
622
+ };
623
+ }
638
624
  _parseMessage(message) {
639
625
  const parsed = this._platform.parseData(message.data);
640
626
  try {
@@ -649,29 +635,6 @@ var IORoom = class {
649
635
  return null;
650
636
  }
651
637
  }
652
- _resolveMessage(message, context) {
653
- return __async(this, null, function* () {
654
- const { session } = context;
655
- const eventConfig = this._getEventConfig(message);
656
- if (!eventConfig)
657
- return null;
658
- let input;
659
- try {
660
- input = eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
661
- } catch (error) {
662
- session == null ? void 0 : session.webSocket.handleError({
663
- error,
664
- message: "Invalid input",
665
- session
666
- });
667
- return null;
668
- }
669
- const output = yield eventConfig.resolver(input, context);
670
- if (!output)
671
- return null;
672
- return output;
673
- });
674
- }
675
638
  _sendMessage(message, sender, options = {}) {
676
639
  switch (options.type) {
677
640
  case "self": {
@@ -721,13 +684,23 @@ var IORoom = class {
721
684
  const senderId = sender == null ? void 0 : sender.id;
722
685
  if (!senderId)
723
686
  return;
724
- this._resolveMessage(message, {
687
+ const context = {
725
688
  context: this._context,
726
689
  doc: this._doc,
727
690
  room: this.id,
728
691
  session: null,
729
692
  sessions: this._sessions
730
- }).then((output) => {
693
+ };
694
+ const resolver = this._getEventResolverObject(message).sync;
695
+ if (!resolver)
696
+ return;
697
+ let inputs;
698
+ try {
699
+ inputs = this._getEventInputs(message);
700
+ } catch (e) {
701
+ return;
702
+ }
703
+ Promise.resolve(resolver(inputs, context)).then((output) => {
731
704
  if (!output)
732
705
  return;
733
706
  Object.keys(output).forEach((type) => {
@@ -754,32 +727,70 @@ var PluvIO = class {
754
727
  this._rooms = /* @__PURE__ */ new Map();
755
728
  this._authorize = null;
756
729
  this._events = {
757
- $PING: {
758
- resolver: (data, { session }) => {
759
- if (!session)
760
- return {};
761
- const currentTime = (/* @__PURE__ */ new Date()).getTime();
762
- session.timers.ping = currentTime;
763
- return { $PONG: {} };
764
- },
765
- options: { type: "self" }
766
- },
767
730
  $GET_OTHERS: {
768
- resolver: (data, { room, session, sessions }) => {
769
- const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
770
- (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
771
- [connectionId]: {
772
- connectionId,
773
- presence,
774
- room,
775
- user
731
+ resolver: {
732
+ sync: (data, { room, session, sessions }) => {
733
+ const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
734
+ (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
735
+ [connectionId]: {
736
+ connectionId,
737
+ presence,
738
+ room,
739
+ user
740
+ }
741
+ }),
742
+ {}
743
+ );
744
+ return { $OTHERS_RECEIVED: { others } };
745
+ }
746
+ }
747
+ },
748
+ $INITIALIZE_SESSION: {
749
+ resolver: {
750
+ broadcast: ({ presence }, { session }) => {
751
+ if (!session)
752
+ return {};
753
+ session.presence = presence;
754
+ return {
755
+ $USER_JOINED: {
756
+ connectionId: session.id,
757
+ user: session.user,
758
+ presence
776
759
  }
777
- }),
778
- {}
779
- );
780
- return { $OTHERS_RECEIVED: { others } };
781
- },
782
- options: { type: "sync" }
760
+ };
761
+ },
762
+ self: (_0, _1) => __async(this, [_0, _1], function* ({ update }, { doc: doc2, room }) {
763
+ var _a, _b;
764
+ const oldState = yield this._platform.persistance.getStorageState(room);
765
+ const updates = [
766
+ oldState ? null : yield (_a = this._initialStorage) == null ? void 0 : _a.call(this, room),
767
+ update
768
+ ];
769
+ if (updates.some((_update) => !!_update)) {
770
+ const state2 = updates.reduce((_doc, _up) => _doc.applyUpdate(_up), doc2).encodeStateAsUpdate();
771
+ yield this._platform.persistance.setStorageState(
772
+ room,
773
+ state2
774
+ );
775
+ this._listeners.onStorageUpdated(room, state2);
776
+ }
777
+ const state = (_b = yield this._platform.persistance.getStorageState(
778
+ room
779
+ )) != null ? _b : doc2.encodeStateAsUpdate();
780
+ return { $STORAGE_RECEIVED: { state } };
781
+ })
782
+ }
783
+ },
784
+ $PING: {
785
+ resolver: {
786
+ self: (data, { session }) => {
787
+ if (!session)
788
+ return {};
789
+ const currentTime = (/* @__PURE__ */ new Date()).getTime();
790
+ session.timers.ping = currentTime;
791
+ return { $PONG: {} };
792
+ }
793
+ }
783
794
  },
784
795
  $UPDATE_PRESENCE: {
785
796
  resolver: ({ presence }, { session }) => {
@@ -880,10 +891,6 @@ var PluvIO = class {
880
891
  context: this._context,
881
892
  debug: debug != null ? debug : this._debug,
882
893
  events: this._events,
883
- initialStorage: () => {
884
- var _a2;
885
- return (_a2 = this._initialStorage) == null ? void 0 : _a2.call(this, room);
886
- },
887
894
  onDestroy: (encodedState) => {
888
895
  this._logDebug(
889
896
  `${import_kleur2.default.blue("Deleting empty room:")} ${room}`