@pluv/io 0.3.8 → 0.4.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
@@ -224,6 +224,10 @@ var AbstractWebSocket = class {
224
224
  // src/PluvIO.ts
225
225
  import colors2 from "kleur";
226
226
 
227
+ // src/IORoom.ts
228
+ import { doc } from "@pluv/crdt-yjs";
229
+ import colors from "kleur";
230
+
227
231
  // src/authorize.ts
228
232
  import hkdf from "@panva/hkdf";
229
233
  import { EncryptJWT, jwtDecrypt } from "jose";
@@ -261,8 +265,6 @@ var authorize = (params) => {
261
265
  };
262
266
 
263
267
  // src/IORoom.ts
264
- import { doc } from "@pluv/crdt-yjs";
265
- import colors from "kleur";
266
268
  var PING_TIMEOUT_MS = 3e4;
267
269
  var IORoom = class {
268
270
  constructor(id, config) {
@@ -286,7 +288,9 @@ var IORoom = class {
286
288
  this._platform = platform;
287
289
  this.id = id;
288
290
  this._listeners = {
289
- onDestroy: (encodedState) => onDestroy == null ? void 0 : onDestroy(encodedState)
291
+ onDestroy: (encodedState) => {
292
+ onDestroy == null ? void 0 : onDestroy(encodedState);
293
+ }
290
294
  };
291
295
  if (authorize2)
292
296
  this._authorize = authorize2;
@@ -357,101 +361,12 @@ var IORoom = class {
357
361
  sessionId,
358
362
  user != null ? user : {}
359
363
  );
364
+ const onClose = this._onClose(session, uninitializeWs).bind(this);
365
+ const onMessage = this._onMessage(session).bind(this);
366
+ pluvWs.addEventListener("close", onClose);
367
+ pluvWs.addEventListener("error", onClose);
368
+ pluvWs.addEventListener("message", onMessage);
360
369
  this._emitRegistered(session);
361
- this._emitStorageReceived(session);
362
- this._emitUserJoined(session);
363
- const context = {
364
- context: this._context,
365
- doc: this._doc,
366
- room: this.id,
367
- session,
368
- sessions: this._sessions
369
- };
370
- pluvWs.addEventListener("message", (event) => {
371
- if (!this._uninitialize)
372
- return;
373
- if (session.quit) {
374
- session.webSocket.close(1011, "WebSocket broken.");
375
- return;
376
- }
377
- const message = this._parseMessage(event);
378
- if (!message)
379
- return;
380
- const eventConfig = this._getEventConfig(message);
381
- if (!eventConfig)
382
- return;
383
- this._resolveMessage(message, context).then((output) => {
384
- var _a2, _b;
385
- if (!output)
386
- return;
387
- const eventType = (_b = (_a2 = eventConfig.options) == null ? void 0 : _a2.type) != null ? _b : "broadcast";
388
- switch (eventType) {
389
- case "self": {
390
- Object.entries(output).forEach(([type, data]) => {
391
- this._sendSelfMessage({ data, type }, session);
392
- });
393
- return;
394
- }
395
- case "sync": {
396
- this._platform.pubSub.publish(this.id, __spreadValues({
397
- connectionId: session.id,
398
- options: eventConfig.options,
399
- room: this.id,
400
- user: session.user
401
- }, message));
402
- Object.entries(output).forEach(([type, data]) => {
403
- this._sendSelfMessage({ data, type }, session);
404
- });
405
- return;
406
- }
407
- case "broadcast":
408
- default: {
409
- Object.entries(output).forEach(([type, data]) => {
410
- this._broadcast({
411
- message: { data, type },
412
- senderId: session.id
413
- });
414
- });
415
- }
416
- }
417
- });
418
- });
419
- const closeHandler = () => {
420
- if (!this._uninitialize)
421
- return;
422
- session.quit = true;
423
- this._logDebug(
424
- `${colors.blue(
425
- `(Unregistering connection for room ${this.id}:`
426
- )} ${sessionId}`
427
- );
428
- this._sessions.delete(session.id);
429
- this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
430
- var _a2;
431
- this._broadcast({
432
- message: {
433
- type: "$EXIT",
434
- data: { sessionId: session.id }
435
- },
436
- senderId: session.id
437
- });
438
- uninitializeWs();
439
- const size2 = this.getSize();
440
- this._logDebug(
441
- `${colors.blue(
442
- `Unregistered connection for room ${this.id}:`
443
- )} ${sessionId}`
444
- );
445
- this._logDebug(
446
- `${colors.blue(`Room ${this.id} size:`)} ${size2}`
447
- );
448
- if (size2)
449
- return;
450
- (_a2 = this._uninitialize) == null ? void 0 : _a2.call(this);
451
- });
452
- };
453
- pluvWs.addEventListener("close", closeHandler);
454
- pluvWs.addEventListener("error", closeHandler);
455
370
  this._logDebug(
456
371
  `${colors.blue(
457
372
  `Registered connection for room ${this.id}:`
@@ -499,34 +414,6 @@ var IORoom = class {
499
414
  session
500
415
  );
501
416
  }
502
- _emitStorageReceived(session) {
503
- this._platform.persistance.getStorageState(this.id).then((_state) => __async(this, null, function* () {
504
- var _a, _b;
505
- const state = (_b = _state != null ? _state : yield (_a = this._initialStorage) == null ? void 0 : _a.call(this)) != null ? _b : this._doc.encodeStateAsUpdate();
506
- this._sendSelfMessage(
507
- {
508
- type: "$STORAGE_RECEIVED",
509
- data: { state }
510
- },
511
- session
512
- );
513
- }));
514
- }
515
- _emitUserJoined(session) {
516
- const user = session.user;
517
- if (!user)
518
- return;
519
- this._broadcast({
520
- message: {
521
- type: "$USER_JOINED",
522
- data: {
523
- connectionId: session.id,
524
- user
525
- }
526
- },
527
- senderId: session.id
528
- });
529
- }
530
417
  _getAuthorizedUser(token) {
531
418
  return __async(this, null, function* () {
532
419
  var _a;
@@ -565,6 +452,19 @@ var IORoom = class {
565
452
  var _a;
566
453
  return (_a = this._events[message.type]) != null ? _a : null;
567
454
  }
455
+ _getEventInputs(message) {
456
+ const eventConfig = this._getEventConfig(message);
457
+ if (!eventConfig)
458
+ return message.data;
459
+ return eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
460
+ }
461
+ _getEventResolverObject(message) {
462
+ const eventConfig = this._getEventConfig(message);
463
+ if (!eventConfig)
464
+ return {};
465
+ const resolver = eventConfig.resolver;
466
+ return typeof resolver === "function" ? { broadcast: resolver } : __spreadValues({}, resolver);
467
+ }
568
468
  _initialize() {
569
469
  return __async(this, null, function* () {
570
470
  this._logDebug(`${colors.blue(`Initializing room ${this.id}:`)}`);
@@ -589,12 +489,112 @@ var IORoom = class {
589
489
  this._doc.destroy();
590
490
  this._doc = doc();
591
491
  this._listeners.onDestroy(encodedState);
492
+ this._uninitialize = null;
592
493
  });
593
494
  });
594
495
  }
595
496
  _logDebug(...data) {
596
497
  this._debug && console.log(...data);
597
498
  }
499
+ _onClose(session, callback) {
500
+ return () => {
501
+ if (!this._uninitialize)
502
+ return;
503
+ session.quit = true;
504
+ this._logDebug(
505
+ `${colors.blue(
506
+ `(Unregistering connection for room ${this.id}:`
507
+ )} ${session.id}`
508
+ );
509
+ this._sessions.delete(session.id);
510
+ this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
511
+ var _a;
512
+ this._broadcast({
513
+ message: {
514
+ type: "$EXIT",
515
+ data: { sessionId: session.id }
516
+ },
517
+ senderId: session.id
518
+ });
519
+ callback == null ? void 0 : callback();
520
+ const size = this.getSize();
521
+ this._logDebug(
522
+ `${colors.blue(
523
+ `Unregistered connection for room ${this.id}:`
524
+ )} ${session.id}`
525
+ );
526
+ this._logDebug(
527
+ `${colors.blue(`Room ${this.id} size:`)} ${size}`
528
+ );
529
+ if (size)
530
+ return;
531
+ (_a = this._uninitialize) == null ? void 0 : _a.call(this);
532
+ });
533
+ };
534
+ }
535
+ _onMessage(session) {
536
+ return (event) => {
537
+ var _a, _b, _c;
538
+ const context = {
539
+ context: this._context,
540
+ doc: this._doc,
541
+ room: this.id,
542
+ session,
543
+ sessions: this._sessions
544
+ };
545
+ if (!this._uninitialize)
546
+ return;
547
+ if (session.quit) {
548
+ session.webSocket.close(1011, "WebSocket broken.");
549
+ return;
550
+ }
551
+ const message = this._parseMessage(event);
552
+ if (!message)
553
+ return;
554
+ const resolver = this._getEventResolverObject(message);
555
+ let inputs;
556
+ try {
557
+ inputs = this._getEventInputs(message);
558
+ } catch (error) {
559
+ session == null ? void 0 : session.webSocket.handleError({
560
+ error,
561
+ message: "Invalid input",
562
+ session
563
+ });
564
+ return;
565
+ }
566
+ Promise.all([
567
+ (_a = resolver.broadcast) == null ? void 0 : _a.call(resolver, inputs, context),
568
+ (_b = resolver.self) == null ? void 0 : _b.call(resolver, inputs, context),
569
+ (_c = resolver.sync) == null ? void 0 : _c.call(resolver, inputs, context)
570
+ ]).then(([broadcast, self, sync]) => {
571
+ if (broadcast) {
572
+ Object.entries(broadcast).forEach(([type, data]) => {
573
+ this._broadcast({
574
+ message: { data, type },
575
+ senderId: session.id
576
+ });
577
+ });
578
+ }
579
+ if (self) {
580
+ Object.entries(self).forEach(([type, data]) => {
581
+ this._sendSelfMessage({ data, type }, session);
582
+ });
583
+ }
584
+ if (sync) {
585
+ this._platform.pubSub.publish(this.id, __spreadValues({
586
+ connectionId: session.id,
587
+ options: { type: "sync" },
588
+ room: this.id,
589
+ user: session.user
590
+ }, message));
591
+ Object.entries(sync).forEach(([type, data]) => {
592
+ this._sendSelfMessage({ data, type }, session);
593
+ });
594
+ }
595
+ });
596
+ };
597
+ }
598
598
  _parseMessage(message) {
599
599
  const parsed = this._platform.parseData(message.data);
600
600
  try {
@@ -609,29 +609,6 @@ var IORoom = class {
609
609
  return null;
610
610
  }
611
611
  }
612
- _resolveMessage(message, context) {
613
- return __async(this, null, function* () {
614
- const { session } = context;
615
- const eventConfig = this._getEventConfig(message);
616
- if (!eventConfig)
617
- return null;
618
- let input;
619
- try {
620
- input = eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
621
- } catch (error) {
622
- session == null ? void 0 : session.webSocket.handleError({
623
- error,
624
- message: "Invalid input",
625
- session
626
- });
627
- return null;
628
- }
629
- const output = yield eventConfig.resolver(input, context);
630
- if (!output)
631
- return null;
632
- return output;
633
- });
634
- }
635
612
  _sendMessage(message, sender, options = {}) {
636
613
  switch (options.type) {
637
614
  case "self": {
@@ -681,13 +658,23 @@ var IORoom = class {
681
658
  const senderId = sender == null ? void 0 : sender.id;
682
659
  if (!senderId)
683
660
  return;
684
- this._resolveMessage(message, {
661
+ const context = {
685
662
  context: this._context,
686
663
  doc: this._doc,
687
664
  room: this.id,
688
665
  session: null,
689
666
  sessions: this._sessions
690
- }).then((output) => {
667
+ };
668
+ const resolver = this._getEventResolverObject(message).sync;
669
+ if (!resolver)
670
+ return;
671
+ let inputs;
672
+ try {
673
+ inputs = this._getEventInputs(message);
674
+ } catch (e) {
675
+ return;
676
+ }
677
+ Promise.resolve(resolver(inputs, context)).then((output) => {
691
678
  if (!output)
692
679
  return;
693
680
  Object.keys(output).forEach((type) => {
@@ -714,32 +701,70 @@ var PluvIO = class {
714
701
  this._rooms = /* @__PURE__ */ new Map();
715
702
  this._authorize = null;
716
703
  this._events = {
717
- $PING: {
718
- resolver: (data, { session }) => {
719
- if (!session)
720
- return {};
721
- const currentTime = (/* @__PURE__ */ new Date()).getTime();
722
- session.timers.ping = currentTime;
723
- return { $PONG: {} };
724
- },
725
- options: { type: "self" }
726
- },
727
704
  $GET_OTHERS: {
728
- resolver: (data, { room, session, sessions }) => {
729
- const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
730
- (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
731
- [connectionId]: {
732
- connectionId,
733
- presence,
734
- room,
735
- user
705
+ resolver: {
706
+ sync: (data, { room, session, sessions }) => {
707
+ const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
708
+ (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
709
+ [connectionId]: {
710
+ connectionId,
711
+ presence,
712
+ room,
713
+ user
714
+ }
715
+ }),
716
+ {}
717
+ );
718
+ return { $OTHERS_RECEIVED: { others } };
719
+ }
720
+ }
721
+ },
722
+ $INITIALIZE_SESSION: {
723
+ resolver: {
724
+ broadcast: ({ presence }, { session }) => {
725
+ if (!session)
726
+ return {};
727
+ session.presence = presence;
728
+ return {
729
+ $USER_JOINED: {
730
+ connectionId: session.id,
731
+ user: session.user,
732
+ presence
736
733
  }
737
- }),
738
- {}
739
- );
740
- return { $OTHERS_RECEIVED: { others } };
741
- },
742
- options: { type: "sync" }
734
+ };
735
+ },
736
+ self: (_0, _1) => __async(this, [_0, _1], function* ({ update }, { doc: doc2, room }) {
737
+ var _a, _b;
738
+ const oldState = yield this._platform.persistance.getStorageState(room);
739
+ const updates = [
740
+ oldState ? null : yield (_a = this._initialStorage) == null ? void 0 : _a.call(this, room),
741
+ update
742
+ ];
743
+ if (updates.some((_update) => !!_update)) {
744
+ const state2 = updates.reduce((_doc, _up) => _doc.applyUpdate(_up), doc2).encodeStateAsUpdate();
745
+ yield this._platform.persistance.setStorageState(
746
+ room,
747
+ state2
748
+ );
749
+ this._listeners.onStorageUpdated(room, state2);
750
+ }
751
+ const state = (_b = yield this._platform.persistance.getStorageState(
752
+ room
753
+ )) != null ? _b : doc2.encodeStateAsUpdate();
754
+ return { $STORAGE_RECEIVED: { state } };
755
+ })
756
+ }
757
+ },
758
+ $PING: {
759
+ resolver: {
760
+ self: (data, { session }) => {
761
+ if (!session)
762
+ return {};
763
+ const currentTime = (/* @__PURE__ */ new Date()).getTime();
764
+ session.timers.ping = currentTime;
765
+ return { $PONG: {} };
766
+ }
767
+ }
743
768
  },
744
769
  $UPDATE_PRESENCE: {
745
770
  resolver: ({ presence }, { session }) => {
@@ -849,6 +874,7 @@ var PluvIO = class {
849
874
  `${colors2.blue("Deleting empty room:")} ${room}`
850
875
  );
851
876
  this._rooms.delete(room);
877
+ this._listeners.onRoomDeleted(room, encodedState);
852
878
  if (this._debug) {
853
879
  const rooms = Array.from(this._rooms.keys());
854
880
  this._logDebug(`${colors2.blue("Deleted room:")} ${room}`);
@@ -856,11 +882,6 @@ var PluvIO = class {
856
882
  `${colors2.blue("Rooms available:")} ${rooms.join(", ")}`
857
883
  );
858
884
  }
859
- this._platform.persistance.getSize(room).then((size) => {
860
- if (size)
861
- return;
862
- this._listeners.onRoomDeleted(room, encodedState);
863
- });
864
885
  },
865
886
  platform
866
887
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/io",
3
- "version": "0.3.8",
3
+ "version": "0.4.0",
4
4
  "description": "@pluv/io server-side websocket manager",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -17,12 +17,12 @@
17
17
  "access": "public"
18
18
  },
19
19
  "dependencies": {
20
- "@panva/hkdf": "^1.0.4",
21
- "jose": "^4.13.1",
20
+ "@panva/hkdf": "^1.1.1",
21
+ "jose": "^4.14.4",
22
22
  "kleur": "^4.1.5",
23
- "wonka": "^6.3.1",
24
- "@pluv/crdt-yjs": "^0.3.6",
25
- "@pluv/types": "^0.1.6"
23
+ "wonka": "^6.3.2",
24
+ "@pluv/crdt-yjs": "^0.3.7",
25
+ "@pluv/types": "^0.2.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "yjs": "^13.0.0"
@@ -30,9 +30,9 @@
30
30
  "devDependencies": {
31
31
  "tsup": "^6.7.0",
32
32
  "typescript": "^5.0.4",
33
- "yjs": "^13.5.52",
34
- "@pluv/tsconfig": "^0.1.5",
35
- "eslint-config-pluv": "^0.1.10"
33
+ "yjs": "^13.6.0",
34
+ "@pluv/tsconfig": "^0.1.6",
35
+ "eslint-config-pluv": "^0.1.11"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsup src/index.ts --format esm,cjs --dts",