@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.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,25 +265,14 @@ 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) {
269
271
  this._doc = doc();
270
- this._initialStorage = null;
271
272
  this._sessions = /* @__PURE__ */ new Map();
272
273
  this._uninitialize = null;
273
274
  this._authorize = null;
274
- const {
275
- authorize: authorize2,
276
- context,
277
- debug,
278
- events,
279
- initialStorage,
280
- onDestroy,
281
- platform
282
- } = config;
275
+ const { authorize: authorize2, context, debug, events, onDestroy, platform } = config;
283
276
  this._context = context;
284
277
  this._debug = debug;
285
278
  this._events = events;
@@ -292,8 +285,6 @@ var IORoom = class {
292
285
  };
293
286
  if (authorize2)
294
287
  this._authorize = authorize2;
295
- if (initialStorage)
296
- this._initialStorage = initialStorage;
297
288
  }
298
289
  broadcast(event, data) {
299
290
  const message = { type: event, data };
@@ -359,101 +350,12 @@ var IORoom = class {
359
350
  sessionId,
360
351
  user != null ? user : {}
361
352
  );
353
+ const onClose = this._onClose(session, uninitializeWs).bind(this);
354
+ const onMessage = this._onMessage(session).bind(this);
355
+ pluvWs.addEventListener("close", onClose);
356
+ pluvWs.addEventListener("error", onClose);
357
+ pluvWs.addEventListener("message", onMessage);
362
358
  this._emitRegistered(session);
363
- this._emitStorageReceived(session);
364
- this._emitUserJoined(session);
365
- const context = {
366
- context: this._context,
367
- doc: this._doc,
368
- room: this.id,
369
- session,
370
- sessions: this._sessions
371
- };
372
- pluvWs.addEventListener("message", (event) => {
373
- if (!this._uninitialize)
374
- return;
375
- if (session.quit) {
376
- session.webSocket.close(1011, "WebSocket broken.");
377
- return;
378
- }
379
- const message = this._parseMessage(event);
380
- if (!message)
381
- return;
382
- const eventConfig = this._getEventConfig(message);
383
- if (!eventConfig)
384
- return;
385
- this._resolveMessage(message, context).then((output) => {
386
- var _a2, _b;
387
- if (!output)
388
- return;
389
- const eventType = (_b = (_a2 = eventConfig.options) == null ? void 0 : _a2.type) != null ? _b : "broadcast";
390
- switch (eventType) {
391
- case "self": {
392
- Object.entries(output).forEach(([type, data]) => {
393
- this._sendSelfMessage({ data, type }, session);
394
- });
395
- return;
396
- }
397
- case "sync": {
398
- this._platform.pubSub.publish(this.id, __spreadValues({
399
- connectionId: session.id,
400
- options: eventConfig.options,
401
- room: this.id,
402
- user: session.user
403
- }, message));
404
- Object.entries(output).forEach(([type, data]) => {
405
- this._sendSelfMessage({ data, type }, session);
406
- });
407
- return;
408
- }
409
- case "broadcast":
410
- default: {
411
- Object.entries(output).forEach(([type, data]) => {
412
- this._broadcast({
413
- message: { data, type },
414
- senderId: session.id
415
- });
416
- });
417
- }
418
- }
419
- });
420
- });
421
- const closeHandler = () => {
422
- if (!this._uninitialize)
423
- return;
424
- session.quit = true;
425
- this._logDebug(
426
- `${colors.blue(
427
- `(Unregistering connection for room ${this.id}:`
428
- )} ${sessionId}`
429
- );
430
- this._sessions.delete(session.id);
431
- this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
432
- var _a2;
433
- this._broadcast({
434
- message: {
435
- type: "$EXIT",
436
- data: { sessionId: session.id }
437
- },
438
- senderId: session.id
439
- });
440
- uninitializeWs();
441
- const size2 = this.getSize();
442
- this._logDebug(
443
- `${colors.blue(
444
- `Unregistered connection for room ${this.id}:`
445
- )} ${sessionId}`
446
- );
447
- this._logDebug(
448
- `${colors.blue(`Room ${this.id} size:`)} ${size2}`
449
- );
450
- if (size2)
451
- return;
452
- (_a2 = this._uninitialize) == null ? void 0 : _a2.call(this);
453
- });
454
- };
455
- pluvWs.addEventListener("close", closeHandler);
456
- pluvWs.addEventListener("error", closeHandler);
457
359
  this._logDebug(
458
360
  `${colors.blue(
459
361
  `Registered connection for room ${this.id}:`
@@ -501,34 +403,6 @@ var IORoom = class {
501
403
  session
502
404
  );
503
405
  }
504
- _emitStorageReceived(session) {
505
- this._platform.persistance.getStorageState(this.id).then((_state) => __async(this, null, function* () {
506
- var _a, _b;
507
- const state = (_b = _state != null ? _state : yield (_a = this._initialStorage) == null ? void 0 : _a.call(this)) != null ? _b : this._doc.encodeStateAsUpdate();
508
- this._sendSelfMessage(
509
- {
510
- type: "$STORAGE_RECEIVED",
511
- data: { state }
512
- },
513
- session
514
- );
515
- }));
516
- }
517
- _emitUserJoined(session) {
518
- const user = session.user;
519
- if (!user)
520
- return;
521
- this._broadcast({
522
- message: {
523
- type: "$USER_JOINED",
524
- data: {
525
- connectionId: session.id,
526
- user
527
- }
528
- },
529
- senderId: session.id
530
- });
531
- }
532
406
  _getAuthorizedUser(token) {
533
407
  return __async(this, null, function* () {
534
408
  var _a;
@@ -567,6 +441,19 @@ var IORoom = class {
567
441
  var _a;
568
442
  return (_a = this._events[message.type]) != null ? _a : null;
569
443
  }
444
+ _getEventInputs(message) {
445
+ const eventConfig = this._getEventConfig(message);
446
+ if (!eventConfig)
447
+ return message.data;
448
+ return eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
449
+ }
450
+ _getEventResolverObject(message) {
451
+ const eventConfig = this._getEventConfig(message);
452
+ if (!eventConfig)
453
+ return {};
454
+ const resolver = eventConfig.resolver;
455
+ return typeof resolver === "function" ? { broadcast: resolver } : __spreadValues({}, resolver);
456
+ }
570
457
  _initialize() {
571
458
  return __async(this, null, function* () {
572
459
  this._logDebug(`${colors.blue(`Initializing room ${this.id}:`)}`);
@@ -598,6 +485,105 @@ var IORoom = class {
598
485
  _logDebug(...data) {
599
486
  this._debug && console.log(...data);
600
487
  }
488
+ _onClose(session, callback) {
489
+ return () => {
490
+ if (!this._uninitialize)
491
+ return;
492
+ session.quit = true;
493
+ this._logDebug(
494
+ `${colors.blue(
495
+ `(Unregistering connection for room ${this.id}:`
496
+ )} ${session.id}`
497
+ );
498
+ this._sessions.delete(session.id);
499
+ this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
500
+ var _a;
501
+ this._broadcast({
502
+ message: {
503
+ type: "$EXIT",
504
+ data: { sessionId: session.id }
505
+ },
506
+ senderId: session.id
507
+ });
508
+ callback == null ? void 0 : callback();
509
+ const size = this.getSize();
510
+ this._logDebug(
511
+ `${colors.blue(
512
+ `Unregistered connection for room ${this.id}:`
513
+ )} ${session.id}`
514
+ );
515
+ this._logDebug(
516
+ `${colors.blue(`Room ${this.id} size:`)} ${size}`
517
+ );
518
+ if (size)
519
+ return;
520
+ (_a = this._uninitialize) == null ? void 0 : _a.call(this);
521
+ });
522
+ };
523
+ }
524
+ _onMessage(session) {
525
+ return (event) => {
526
+ var _a, _b, _c;
527
+ const context = {
528
+ context: this._context,
529
+ doc: this._doc,
530
+ room: this.id,
531
+ session,
532
+ sessions: this._sessions
533
+ };
534
+ if (!this._uninitialize)
535
+ return;
536
+ if (session.quit) {
537
+ session.webSocket.close(1011, "WebSocket broken.");
538
+ return;
539
+ }
540
+ const message = this._parseMessage(event);
541
+ if (!message)
542
+ return;
543
+ const resolver = this._getEventResolverObject(message);
544
+ let inputs;
545
+ try {
546
+ inputs = this._getEventInputs(message);
547
+ } catch (error) {
548
+ session == null ? void 0 : session.webSocket.handleError({
549
+ error,
550
+ message: "Invalid input",
551
+ session
552
+ });
553
+ return;
554
+ }
555
+ Promise.all([
556
+ (_a = resolver.broadcast) == null ? void 0 : _a.call(resolver, inputs, context),
557
+ (_b = resolver.self) == null ? void 0 : _b.call(resolver, inputs, context),
558
+ (_c = resolver.sync) == null ? void 0 : _c.call(resolver, inputs, context)
559
+ ]).then(([broadcast, self, sync]) => {
560
+ if (broadcast) {
561
+ Object.entries(broadcast).forEach(([type, data]) => {
562
+ this._broadcast({
563
+ message: { data, type },
564
+ senderId: session.id
565
+ });
566
+ });
567
+ }
568
+ if (self) {
569
+ Object.entries(self).forEach(([type, data]) => {
570
+ this._sendSelfMessage({ data, type }, session);
571
+ });
572
+ }
573
+ if (sync) {
574
+ this._platform.pubSub.publish(this.id, __spreadValues({
575
+ connectionId: session.id,
576
+ options: { type: "sync" },
577
+ room: this.id,
578
+ user: session.user
579
+ }, message));
580
+ Object.entries(sync).forEach(([type, data]) => {
581
+ this._sendSelfMessage({ data, type }, session);
582
+ });
583
+ }
584
+ });
585
+ };
586
+ }
601
587
  _parseMessage(message) {
602
588
  const parsed = this._platform.parseData(message.data);
603
589
  try {
@@ -612,29 +598,6 @@ var IORoom = class {
612
598
  return null;
613
599
  }
614
600
  }
615
- _resolveMessage(message, context) {
616
- return __async(this, null, function* () {
617
- const { session } = context;
618
- const eventConfig = this._getEventConfig(message);
619
- if (!eventConfig)
620
- return null;
621
- let input;
622
- try {
623
- input = eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
624
- } catch (error) {
625
- session == null ? void 0 : session.webSocket.handleError({
626
- error,
627
- message: "Invalid input",
628
- session
629
- });
630
- return null;
631
- }
632
- const output = yield eventConfig.resolver(input, context);
633
- if (!output)
634
- return null;
635
- return output;
636
- });
637
- }
638
601
  _sendMessage(message, sender, options = {}) {
639
602
  switch (options.type) {
640
603
  case "self": {
@@ -684,13 +647,23 @@ var IORoom = class {
684
647
  const senderId = sender == null ? void 0 : sender.id;
685
648
  if (!senderId)
686
649
  return;
687
- this._resolveMessage(message, {
650
+ const context = {
688
651
  context: this._context,
689
652
  doc: this._doc,
690
653
  room: this.id,
691
654
  session: null,
692
655
  sessions: this._sessions
693
- }).then((output) => {
656
+ };
657
+ const resolver = this._getEventResolverObject(message).sync;
658
+ if (!resolver)
659
+ return;
660
+ let inputs;
661
+ try {
662
+ inputs = this._getEventInputs(message);
663
+ } catch (e) {
664
+ return;
665
+ }
666
+ Promise.resolve(resolver(inputs, context)).then((output) => {
694
667
  if (!output)
695
668
  return;
696
669
  Object.keys(output).forEach((type) => {
@@ -717,32 +690,70 @@ var PluvIO = class {
717
690
  this._rooms = /* @__PURE__ */ new Map();
718
691
  this._authorize = null;
719
692
  this._events = {
720
- $PING: {
721
- resolver: (data, { session }) => {
722
- if (!session)
723
- return {};
724
- const currentTime = (/* @__PURE__ */ new Date()).getTime();
725
- session.timers.ping = currentTime;
726
- return { $PONG: {} };
727
- },
728
- options: { type: "self" }
729
- },
730
693
  $GET_OTHERS: {
731
- resolver: (data, { room, session, sessions }) => {
732
- const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
733
- (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
734
- [connectionId]: {
735
- connectionId,
736
- presence,
737
- room,
738
- user
694
+ resolver: {
695
+ sync: (data, { room, session, sessions }) => {
696
+ const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
697
+ (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
698
+ [connectionId]: {
699
+ connectionId,
700
+ presence,
701
+ room,
702
+ user
703
+ }
704
+ }),
705
+ {}
706
+ );
707
+ return { $OTHERS_RECEIVED: { others } };
708
+ }
709
+ }
710
+ },
711
+ $INITIALIZE_SESSION: {
712
+ resolver: {
713
+ broadcast: ({ presence }, { session }) => {
714
+ if (!session)
715
+ return {};
716
+ session.presence = presence;
717
+ return {
718
+ $USER_JOINED: {
719
+ connectionId: session.id,
720
+ user: session.user,
721
+ presence
739
722
  }
740
- }),
741
- {}
742
- );
743
- return { $OTHERS_RECEIVED: { others } };
744
- },
745
- options: { type: "sync" }
723
+ };
724
+ },
725
+ self: (_0, _1) => __async(this, [_0, _1], function* ({ update }, { doc: doc2, room }) {
726
+ var _a, _b;
727
+ const oldState = yield this._platform.persistance.getStorageState(room);
728
+ const updates = [
729
+ oldState ? null : yield (_a = this._initialStorage) == null ? void 0 : _a.call(this, room),
730
+ update
731
+ ];
732
+ if (updates.some((_update) => !!_update)) {
733
+ const state2 = updates.reduce((_doc, _up) => _doc.applyUpdate(_up), doc2).encodeStateAsUpdate();
734
+ yield this._platform.persistance.setStorageState(
735
+ room,
736
+ state2
737
+ );
738
+ this._listeners.onStorageUpdated(room, state2);
739
+ }
740
+ const state = (_b = yield this._platform.persistance.getStorageState(
741
+ room
742
+ )) != null ? _b : doc2.encodeStateAsUpdate();
743
+ return { $STORAGE_RECEIVED: { state } };
744
+ })
745
+ }
746
+ },
747
+ $PING: {
748
+ resolver: {
749
+ self: (data, { session }) => {
750
+ if (!session)
751
+ return {};
752
+ const currentTime = (/* @__PURE__ */ new Date()).getTime();
753
+ session.timers.ping = currentTime;
754
+ return { $PONG: {} };
755
+ }
756
+ }
746
757
  },
747
758
  $UPDATE_PRESENCE: {
748
759
  resolver: ({ presence }, { session }) => {
@@ -843,10 +854,6 @@ var PluvIO = class {
843
854
  context: this._context,
844
855
  debug: debug != null ? debug : this._debug,
845
856
  events: this._events,
846
- initialStorage: () => {
847
- var _a2;
848
- return (_a2 = this._initialStorage) == null ? void 0 : _a2.call(this, room);
849
- },
850
857
  onDestroy: (encodedState) => {
851
858
  this._logDebug(
852
859
  `${colors2.blue("Deleting empty room:")} ${room}`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/io",
3
- "version": "0.3.9",
3
+ "version": "0.4.1",
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.8",
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.1",
34
+ "@pluv/tsconfig": "^0.1.6",
35
+ "eslint-config-pluv": "^0.1.12"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsup src/index.ts --format esm,cjs --dts",