@pluv/io 0.17.2 → 0.18.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
@@ -84,6 +84,9 @@ __export(src_exports, {
84
84
  AbstractPubSub: () => AbstractPubSub,
85
85
  AbstractWebSocket: () => AbstractWebSocket,
86
86
  PluvIO: () => PluvIO,
87
+ PluvProcedure: () => PluvProcedure,
88
+ PluvRouter: () => PluvRouter,
89
+ PluvServer: () => PluvServer,
87
90
  createIO: () => createIO
88
91
  });
89
92
  module.exports = __toCommonJS(src_exports);
@@ -241,11 +244,7 @@ var AbstractWebSocket = class {
241
244
  }
242
245
  handleError(params) {
243
246
  var _a, _b, _c;
244
- const {
245
- error,
246
- message = error instanceof Error ? error.message : "Unexpected error",
247
- session
248
- } = params;
247
+ const { error, message = error instanceof Error ? error.message : "Unexpected error", session } = params;
249
248
  const stack = error instanceof Error ? (_a = error.stack) != null ? _a : null : null;
250
249
  return this.sendMessage({
251
250
  connectionId: (_b = session == null ? void 0 : session.id) != null ? _b : null,
@@ -261,6 +260,79 @@ var AbstractWebSocket = class {
261
260
  };
262
261
 
263
262
  // src/PluvIO.ts
263
+ var import_crdt3 = require("@pluv/crdt");
264
+
265
+ // src/PluvProcedure.ts
266
+ var PluvProcedure = class _PluvProcedure {
267
+ constructor(config = {}) {
268
+ this._broadcast = null;
269
+ this._input = null;
270
+ this._self = null;
271
+ this._sync = null;
272
+ const { broadcast, input, self, sync } = config;
273
+ this._broadcast = broadcast != null ? broadcast : null;
274
+ this._input = input != null ? input : null;
275
+ this._self = self != null ? self : null;
276
+ this._sync = sync != null ? sync : null;
277
+ }
278
+ get config() {
279
+ var _a, _b, _c, _d, _e, _f, _g;
280
+ return {
281
+ broadcast: (_b = (_a = this._broadcast) == null ? void 0 : _a.bind(this)) != null ? _b : null,
282
+ input: (_c = this._input) != null ? _c : null,
283
+ resolver: this._resolver,
284
+ self: (_e = (_d = this._self) == null ? void 0 : _d.bind(this)) != null ? _e : null,
285
+ sync: (_g = (_f = this._sync) == null ? void 0 : _f.bind(this)) != null ? _g : null
286
+ };
287
+ }
288
+ broadcast(resolver) {
289
+ const { broadcast } = this.config;
290
+ if (!!broadcast)
291
+ throw new Error("Broadcast was already defined for this procedure");
292
+ return new _PluvProcedure(__spreadProps(__spreadValues({}, this.config), {
293
+ broadcast: resolver
294
+ }));
295
+ }
296
+ input(input) {
297
+ return new _PluvProcedure({ input });
298
+ }
299
+ self(resolver) {
300
+ const { self } = this.config;
301
+ if (!!self)
302
+ throw new Error("Self was already defined for this procedure");
303
+ return new _PluvProcedure(__spreadProps(__spreadValues({}, this.config), {
304
+ self: resolver
305
+ }));
306
+ }
307
+ sync(resolver) {
308
+ const { sync } = this.config;
309
+ if (!!sync)
310
+ throw new Error("Sync was already defined for this procedure");
311
+ return new _PluvProcedure(__spreadProps(__spreadValues({}, this.config), {
312
+ sync: resolver
313
+ }));
314
+ }
315
+ _resolver() {
316
+ return (data, context) => {
317
+ var _a, _b, _c;
318
+ return __spreadValues(__spreadValues(__spreadValues({}, (_a = this._broadcast) == null ? void 0 : _a.call(this, data, context)), (_b = this._self) == null ? void 0 : _b.call(this, data, context)), (_c = this._sync) == null ? void 0 : _c.call(this, data, context));
319
+ };
320
+ }
321
+ };
322
+
323
+ // src/PluvRouter.ts
324
+ var PluvRouter = class _PluvRouter {
325
+ constructor(events) {
326
+ this._events = {};
327
+ this._events = events;
328
+ }
329
+ static merge(...routers) {
330
+ const events = Object.assign(/* @__PURE__ */ Object.create(null), ...routers.map((router) => router._events));
331
+ return new _PluvRouter(events);
332
+ }
333
+ };
334
+
335
+ // src/PluvServer.ts
264
336
  var import_crdt2 = require("@pluv/crdt");
265
337
  var import_kleur2 = __toESM(require("kleur"));
266
338
 
@@ -273,13 +345,7 @@ var import_hkdf = __toESM(require("@panva/hkdf"));
273
345
  var import_jose = require("jose");
274
346
  var DEFAULT_MAX_AGE_MS = 6e4;
275
347
  var getEncryptionKey = (secret) => __async(void 0, null, function* () {
276
- return yield (0, import_hkdf.default)(
277
- "sha256",
278
- secret,
279
- "",
280
- "Pluv.io Generated Encryption Key",
281
- 32
282
- );
348
+ return yield (0, import_hkdf.default)("sha256", secret, "", "Pluv.io Generated Encryption Key", 32);
283
349
  });
284
350
  var now = () => Date.now() / 1e3 | 0;
285
351
  var authorize = (params) => {
@@ -311,20 +377,12 @@ var IORoom = class {
311
377
  this._sessions = /* @__PURE__ */ new Map();
312
378
  this._uninitialize = null;
313
379
  this._authorize = null;
314
- const {
315
- authorize: authorize2,
316
- context,
317
- crdt = import_crdt.noop,
318
- debug,
319
- events,
320
- onDestroy,
321
- platform
322
- } = config;
380
+ const { authorize: authorize2, context, crdt = import_crdt.noop, debug, onDestroy, platform, router } = config;
323
381
  this._context = context;
324
382
  this._debug = debug;
325
383
  this._docFactory = crdt.doc(() => ({}));
326
384
  this._doc = this._docFactory.getEmpty();
327
- this._events = events;
385
+ this._router = router;
328
386
  this._platform = platform;
329
387
  this.id = id;
330
388
  this._listeners = {
@@ -333,9 +391,19 @@ var IORoom = class {
333
391
  if (authorize2)
334
392
  this._authorize = authorize2;
335
393
  }
336
- broadcast(event, data) {
337
- const message = { type: event, data };
338
- return Promise.resolve(this._broadcast({ message }));
394
+ get _events() {
395
+ return this._router._events;
396
+ }
397
+ get broadcast() {
398
+ const _broadcast = (event, data) => {
399
+ const message = { type: event, data };
400
+ return Promise.resolve(this._broadcast({ message }));
401
+ };
402
+ return new Proxy(_broadcast, {
403
+ get(fn, prop) {
404
+ return (data) => fn(prop, data);
405
+ }
406
+ });
339
407
  }
340
408
  getSize() {
341
409
  const currentTime = (/* @__PURE__ */ new Date()).getTime();
@@ -359,26 +427,16 @@ var IORoom = class {
359
427
  sessionId,
360
428
  userId: (_b = user == null ? void 0 : user.id) != null ? _b : null
361
429
  });
362
- this._logDebug(
363
- `${import_kleur.default.blue(
364
- `Registering connection for room ${this.id}:`
365
- )} ${sessionId}`
366
- );
430
+ this._logDebug(`${import_kleur.default.blue(`Registering connection for room ${this.id}:`)} ${sessionId}`);
367
431
  const uninitializeWs = yield pluvWs.initialize();
368
432
  const ioAuthorize = this._getIOAuthorize(options);
369
433
  const isUnauthorized = !!(ioAuthorize == null ? void 0 : ioAuthorize.required) && !user;
370
- const isTokenInUse = !!user && Array.from(this._sessions.values()).some(
371
- (session2) => {
372
- var _a2;
373
- return ((_a2 = session2.user) == null ? void 0 : _a2.id) === user.id;
374
- }
375
- );
434
+ const isTokenInUse = !!user && Array.from(this._sessions.values()).some((session2) => {
435
+ var _a2;
436
+ return ((_a2 = session2.user) == null ? void 0 : _a2.id) === user.id;
437
+ });
376
438
  if (isUnauthorized || isTokenInUse) {
377
- this._logDebug(
378
- `${import_kleur.default.blue(
379
- "Authorization failed for connection:"
380
- )} ${sessionId}`
381
- );
439
+ this._logDebug(`${import_kleur.default.blue("Authorization failed for connection:")} ${sessionId}`);
382
440
  pluvWs.handleError({ error: new Error("Not authorized") });
383
441
  pluvWs.close(3e3, "WebSocket unauthorized.");
384
442
  return;
@@ -396,24 +454,14 @@ var IORoom = class {
396
454
  user
397
455
  };
398
456
  this._sessions.set(session.id, session);
399
- yield this._platform.persistance.addUser(
400
- this.id,
401
- sessionId,
402
- user != null ? user : {}
403
- );
457
+ yield this._platform.persistance.addUser(this.id, sessionId, user != null ? user : {});
404
458
  const onClose = this._onClose(session, uninitializeWs).bind(this);
405
- const onMessage = this._onMessage(session, platformEventContext).bind(
406
- this
407
- );
459
+ const onMessage = this._onMessage(session, platformEventContext).bind(this);
408
460
  pluvWs.addEventListener("close", onClose);
409
461
  pluvWs.addEventListener("error", onClose);
410
462
  pluvWs.addEventListener("message", onMessage);
411
463
  this._emitRegistered(session);
412
- this._logDebug(
413
- `${import_kleur.default.blue(
414
- `Registered connection for room ${this.id}:`
415
- )} ${sessionId}`
416
- );
464
+ this._logDebug(`${import_kleur.default.blue(`Registered connection for room ${this.id}:`)} ${sessionId}`);
417
465
  const size = this.getSize();
418
466
  this._logDebug(`${import_kleur.default.blue(`Room ${this.id} size:`)} ${size}`);
419
467
  });
@@ -480,9 +528,7 @@ var IORoom = class {
480
528
  return null;
481
529
  }
482
530
  if (payload.room !== this.id) {
483
- this._logDebug(
484
- import_kleur.default.blue(`Token is not authorized for room ${this.id}:`)
485
- );
531
+ this._logDebug(import_kleur.default.blue(`Token is not authorized for room ${this.id}:`));
486
532
  this._logDebug(import_kleur.default.blue("Received:"), payload.room);
487
533
  this._logDebug(token);
488
534
  return null;
@@ -490,29 +536,20 @@ var IORoom = class {
490
536
  try {
491
537
  return (_a = ioAuthorize.user.parse(payload.user)) != null ? _a : null;
492
538
  } catch (e) {
493
- this._logDebug(
494
- `${import_kleur.default.blue("Token fails validation:")} ${token}`
495
- );
539
+ this._logDebug(`${import_kleur.default.blue("Token fails validation:")} ${token}`);
496
540
  return null;
497
541
  }
498
542
  });
499
543
  }
500
- _getEventConfig(message) {
544
+ _getProcedure(message) {
501
545
  var _a;
502
- return (_a = this._events[message.type]) != null ? _a : null;
546
+ return (_a = this._router._events[message.type]) != null ? _a : null;
503
547
  }
504
- _getEventInputs(message) {
505
- const eventConfig = this._getEventConfig(message);
506
- if (!eventConfig)
548
+ _getProcedureInputs(message) {
549
+ const procedure = this._getProcedure(message);
550
+ if (!procedure)
507
551
  return message.data;
508
- return eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
509
- }
510
- _getEventResolverObject(message) {
511
- const eventConfig = this._getEventConfig(message);
512
- if (!eventConfig)
513
- return {};
514
- const resolver = eventConfig.resolver;
515
- return typeof resolver === "function" ? { broadcast: resolver } : __spreadValues({}, resolver);
552
+ return procedure.config.input ? procedure.config.input.parse(message.data) : message.data;
516
553
  }
517
554
  _initialize() {
518
555
  return __async(this, null, function* () {
@@ -521,17 +558,14 @@ var IORoom = class {
521
558
  yield this._uninitialize();
522
559
  this._uninitialize = null;
523
560
  }
524
- const pubSubId = yield this._platform.pubSub.subscribe(
525
- this.id,
526
- (_a) => {
527
- var _b = _a, { options = {} } = _b, message = __objRest(_b, ["options"]);
528
- const session = {
529
- id: message.connectionId,
530
- user: message.user
531
- };
532
- this._sendMessage(message, session, options);
533
- }
534
- );
561
+ const pubSubId = yield this._platform.pubSub.subscribe(this.id, (_a) => {
562
+ var _b = _a, { options = {} } = _b, message = __objRest(_b, ["options"]);
563
+ const session = {
564
+ id: message.connectionId,
565
+ user: message.user
566
+ };
567
+ this._sendMessage(message, session, options);
568
+ });
535
569
  this._uninitialize = () => __async(this, null, function* () {
536
570
  this._platform.pubSub.unsubscribe(pubSubId);
537
571
  const encodedState = this._doc.getEncodedState();
@@ -553,11 +587,7 @@ var IORoom = class {
553
587
  if (!this._uninitialize)
554
588
  return;
555
589
  session.quit = true;
556
- this._logDebug(
557
- `${import_kleur.default.blue(
558
- `(Unregistering connection for room ${this.id}:`
559
- )} ${session.id}`
560
- );
590
+ this._logDebug(`${import_kleur.default.blue(`(Unregistering connection for room ${this.id}:`)} ${session.id}`);
561
591
  this._sessions.delete(session.id);
562
592
  this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
563
593
  var _a;
@@ -570,14 +600,8 @@ var IORoom = class {
570
600
  });
571
601
  callback == null ? void 0 : callback();
572
602
  const size = this.getSize();
573
- this._logDebug(
574
- `${import_kleur.default.blue(
575
- `Unregistered connection for room ${this.id}:`
576
- )} ${session.id}`
577
- );
578
- this._logDebug(
579
- `${import_kleur.default.blue(`Room ${this.id} size:`)} ${size}`
580
- );
603
+ this._logDebug(`${import_kleur.default.blue(`Unregistered connection for room ${this.id}:`)} ${session.id}`);
604
+ this._logDebug(`${import_kleur.default.blue(`Room ${this.id} size:`)} ${size}`);
581
605
  if (size)
582
606
  return;
583
607
  (_a = this._uninitialize) == null ? void 0 : _a.call(this);
@@ -586,7 +610,7 @@ var IORoom = class {
586
610
  }
587
611
  _onMessage(session, platformEventContext) {
588
612
  return (event) => {
589
- var _a, _b, _c;
613
+ var _a, _b, _c, _d, _e, _f;
590
614
  const baseContext = {
591
615
  context: this._context,
592
616
  doc: this._doc,
@@ -603,10 +627,12 @@ var IORoom = class {
603
627
  const message = this._parseMessage(event);
604
628
  if (!message)
605
629
  return;
606
- const resolver = this._getEventResolverObject(message);
630
+ const procedure = this._getProcedure(message);
631
+ if (!procedure)
632
+ return;
607
633
  let inputs;
608
634
  try {
609
- inputs = this._getEventInputs(message);
635
+ inputs = this._getProcedureInputs(message);
610
636
  } catch (error) {
611
637
  session == null ? void 0 : session.webSocket.handleError({
612
638
  error,
@@ -619,9 +645,9 @@ var IORoom = class {
619
645
  context: __spreadValues(__spreadValues({}, this._context), platformEventContext)
620
646
  });
621
647
  Promise.all([
622
- (_a = resolver.broadcast) == null ? void 0 : _a.call(resolver, inputs, extendedContext),
623
- (_b = resolver.self) == null ? void 0 : _b.call(resolver, inputs, extendedContext),
624
- (_c = resolver.sync) == null ? void 0 : _c.call(resolver, inputs, baseContext)
648
+ (_b = (_a = procedure.config).broadcast) == null ? void 0 : _b.call(_a, inputs, extendedContext),
649
+ (_d = (_c = procedure.config).self) == null ? void 0 : _d.call(_c, inputs, extendedContext),
650
+ (_f = (_e = procedure.config).sync) == null ? void 0 : _f.call(_e, inputs, baseContext)
625
651
  ]).then(([broadcast, self, sync]) => {
626
652
  if (broadcast) {
627
653
  Object.entries(broadcast).forEach(([type, data]) => {
@@ -710,6 +736,7 @@ var IORoom = class {
710
736
  }, message));
711
737
  }
712
738
  _sendSyncMessage(message, sender) {
739
+ var _a;
713
740
  const senderId = sender == null ? void 0 : sender.id;
714
741
  if (!senderId)
715
742
  return;
@@ -720,12 +747,12 @@ var IORoom = class {
720
747
  session: null,
721
748
  sessions: this._sessions
722
749
  };
723
- const resolver = this._getEventResolverObject(message).sync;
750
+ const resolver = (_a = this._getProcedure(message)) == null ? void 0 : _a.config.sync;
724
751
  if (!resolver)
725
752
  return;
726
753
  let inputs;
727
754
  try {
728
- inputs = this._getEventInputs(message);
755
+ inputs = this._getProcedureInputs(message);
729
756
  } catch (e) {
730
757
  return;
731
758
  }
@@ -733,8 +760,8 @@ var IORoom = class {
733
760
  if (!output)
734
761
  return;
735
762
  Object.keys(output).forEach((type) => {
736
- var _a, _b;
737
- const data = (_a = output[type]) != null ? _a : {};
763
+ var _a2, _b;
764
+ const data = (_a2 = output[type]) != null ? _a2 : {};
738
765
  this._platform.pubSub.publish(this.id, {
739
766
  connectionId: senderId,
740
767
  data,
@@ -749,149 +776,45 @@ var IORoom = class {
749
776
  };
750
777
 
751
778
  // src/version.generated.ts
752
- var version = "0.17.2";
779
+ var version = "0.18.0";
753
780
 
754
- // src/PluvIO.ts
755
- var PluvIO = class _PluvIO {
781
+ // src/PluvServer.ts
782
+ var PluvServer = class {
756
783
  constructor(options) {
757
784
  this._rooms = /* @__PURE__ */ new Map();
758
785
  this._authorize = null;
759
786
  this._context = {};
760
- this._events = {
761
- $GET_OTHERS: {
762
- resolver: {
763
- sync: (data, { room, session, sessions }) => {
764
- const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
765
- (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
766
- [connectionId]: {
767
- connectionId,
768
- presence,
769
- room,
770
- user
771
- }
772
- }),
773
- {}
774
- );
775
- return { $OTHERS_RECEIVED: { others } };
776
- }
777
- }
778
- },
779
- $INITIALIZE_SESSION: {
780
- resolver: {
781
- broadcast: ({ presence }, { session }) => {
782
- if (!session)
783
- return {};
784
- session.presence = presence;
785
- return {
786
- $USER_JOINED: {
787
- connectionId: session.id,
788
- user: session.user,
789
- presence
790
- }
791
- };
792
- },
793
- self: (_0, _1) => __async(this, [_0, _1], function* ({ update }, { context, doc, room }) {
794
- var _a, _b;
795
- const oldState = yield this._platform.persistance.getStorageState(room);
796
- const updates = [
797
- oldState ? null : yield (_a = this._getInitialStorage) == null ? void 0 : _a.call(this, __spreadProps(__spreadValues({}, context), {
798
- room
799
- })),
800
- update
801
- ];
802
- if (updates.some((_update) => !!_update)) {
803
- const encodedState = doc.batchApplyEncodedState(updates).getEncodedState();
804
- yield this._platform.persistance.setStorageState(
805
- room,
806
- encodedState
807
- );
808
- this._listeners.onStorageUpdated(__spreadProps(__spreadValues({}, context), {
809
- encodedState,
810
- room
811
- }));
812
- }
813
- const state = (_b = yield this._platform.persistance.getStorageState(
814
- room
815
- )) != null ? _b : doc.getEncodedState();
816
- return { $STORAGE_RECEIVED: { state } };
817
- })
818
- }
819
- },
820
- $PING: {
821
- resolver: {
822
- self: (data, { session }) => {
823
- if (!session)
824
- return {};
825
- const currentTime = (/* @__PURE__ */ new Date()).getTime();
826
- session.timers.ping = currentTime;
827
- return { $PONG: {} };
828
- }
829
- }
830
- },
831
- $UPDATE_PRESENCE: {
832
- resolver: ({ presence }, { session }) => {
833
- if (!session)
834
- return {};
835
- session.presence = Object.assign(
836
- /* @__PURE__ */ Object.create(null),
837
- session.presence,
838
- presence
839
- );
840
- return { $PRESENCE_UPDATED: { presence } };
841
- }
842
- },
843
- $UPDATE_STORAGE: {
844
- resolver: ({ origin, update }, { context, doc, room }) => {
845
- if (origin === "$INITIALIZED" && Object.keys(doc.toJson()).length) {
846
- return;
847
- }
848
- const updated = doc.applyEncodedState({ update });
849
- const encodedState = updated.getEncodedState();
850
- this._platform.persistance.setStorageState(room, encodedState).then(() => {
851
- this._listeners.onStorageUpdated(__spreadProps(__spreadValues({}, context), {
852
- encodedState,
853
- room
854
- }));
855
- });
856
- return { $STORAGE_UPDATED: { state: encodedState } };
857
- }
858
- }
859
- };
860
- this._getInitialStorage = null;
861
787
  this._version = version;
862
788
  const {
863
789
  authorize: authorize2,
864
790
  context,
865
791
  crdt = import_crdt2.noop,
866
792
  debug = false,
867
- events,
868
- getInitialStorage,
869
793
  onRoomDeleted,
870
794
  onStorageUpdated,
871
- platform
795
+ platform,
796
+ router = new PluvRouter({})
872
797
  } = options;
873
798
  this._crdt = crdt;
874
799
  this._debug = debug;
875
800
  this._platform = platform;
801
+ this._router = router != null ? router : null;
876
802
  if (authorize2)
877
803
  this._authorize = authorize2;
878
804
  if (context)
879
805
  this._context = context;
880
- if (events)
881
- this._events = events;
882
- if (getInitialStorage)
883
- this._getInitialStorage = getInitialStorage;
884
806
  this._listeners = {
885
807
  onRoomDeleted: (event) => onRoomDeleted == null ? void 0 : onRoomDeleted(event),
886
808
  onStorageUpdated: (event) => onStorageUpdated == null ? void 0 : onStorageUpdated(event)
887
809
  };
888
810
  }
811
+ get _events() {
812
+ return this._router._events;
813
+ }
889
814
  createToken(params) {
890
815
  return __async(this, null, function* () {
891
816
  if (!this._authorize) {
892
- throw new Error(
893
- "IO does not specify authorize during initialization."
894
- );
817
+ throw new Error("IO does not specify authorize during initialization.");
895
818
  }
896
819
  const ioAuthorize = typeof this._authorize === "function" ? this._authorize(params) : this._authorize;
897
820
  const secret = ioAuthorize.secret;
@@ -901,22 +824,6 @@ var PluvIO = class _PluvIO {
901
824
  }).encode(params);
902
825
  });
903
826
  }
904
- event(event, config) {
905
- if (event.startsWith("$")) {
906
- throw new Error('Event name must not start with "$".');
907
- }
908
- const newEvent = {
909
- [event]: config
910
- };
911
- return _PluvIO.merge(
912
- this,
913
- new _PluvIO({
914
- crdt: this._crdt,
915
- events: newEvent,
916
- platform: this._platform
917
- })
918
- );
919
- }
920
827
  getRoom(room, options) {
921
828
  var _b;
922
829
  const _a = options, { debug } = _a, platformRoomContext = __objRest(_a, ["debug"]);
@@ -934,11 +841,8 @@ var PluvIO = class _PluvIO {
934
841
  context: roomContext,
935
842
  crdt: this._crdt,
936
843
  debug: debug != null ? debug : this._debug,
937
- events: this._events,
938
844
  onDestroy: ({ encodedState, room: room2 }) => {
939
- this._logDebug(
940
- `${import_kleur2.default.blue("Deleting empty room:")} ${room2}`
941
- );
845
+ this._logDebug(`${import_kleur2.default.blue("Deleting empty room:")} ${room2}`);
942
846
  const roomContext2 = __spreadValues({
943
847
  room: room2,
944
848
  encodedState
@@ -948,49 +852,20 @@ var PluvIO = class _PluvIO {
948
852
  if (this._debug) {
949
853
  const rooms = Array.from(this._rooms.keys());
950
854
  this._logDebug(`${import_kleur2.default.blue("Deleted room:")} ${room2}`);
951
- this._logDebug(
952
- `${import_kleur2.default.blue("Rooms available:")} ${rooms.join(
953
- ", "
954
- )}`
955
- );
855
+ this._logDebug(`${import_kleur2.default.blue("Rooms available:")} ${rooms.join(", ")}`);
956
856
  }
957
857
  },
958
- platform
858
+ platform,
859
+ router: this._router
959
860
  });
960
861
  this._rooms.set(room, newRoom);
961
862
  if (this._debug) {
962
863
  const rooms = Array.from(this._rooms.keys());
963
864
  this._logDebug(`${import_kleur2.default.blue("Created room:")} ${room}`);
964
- this._logDebug(
965
- `${import_kleur2.default.blue("Rooms available:")} ${rooms.join(", ")}`
966
- );
865
+ this._logDebug(`${import_kleur2.default.blue("Rooms available:")} ${rooms.join(", ")}`);
967
866
  }
968
867
  return newRoom;
969
868
  }
970
- static merge(first, second) {
971
- var _a;
972
- const authorize2 = (_a = first._authorize) != null ? _a : void 0;
973
- const context = first._context;
974
- const crdt = first._crdt;
975
- const debug = first._debug;
976
- const platform = first._platform;
977
- const getInitialStorage = first._getInitialStorage;
978
- const events1 = first._events;
979
- const events2 = second._events;
980
- const { onRoomDeleted, onStorageUpdated } = first._listeners;
981
- const events = Object.assign(/* @__PURE__ */ Object.create(null), events1, events2);
982
- return new _PluvIO({
983
- authorize: authorize2,
984
- context,
985
- crdt,
986
- debug,
987
- events,
988
- getInitialStorage,
989
- onRoomDeleted,
990
- onStorageUpdated,
991
- platform
992
- });
993
- }
994
869
  _logDebug(...data) {
995
870
  this._debug && console.log(...data);
996
871
  }
@@ -1004,18 +879,156 @@ var PluvIO = class _PluvIO {
1004
879
  }
1005
880
  };
1006
881
 
882
+ // src/PluvIO.ts
883
+ var PluvIO = class {
884
+ constructor(options) {
885
+ this._router = new PluvRouter({
886
+ $GET_OTHERS: this.procedure.sync((data, { room, session, sessions }) => {
887
+ const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
888
+ (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
889
+ [connectionId]: {
890
+ connectionId,
891
+ presence,
892
+ room,
893
+ user
894
+ }
895
+ }),
896
+ {}
897
+ );
898
+ return { $OTHERS_RECEIVED: { others } };
899
+ }),
900
+ $INITIALIZE_SESSION: this.procedure.broadcast((data, { session }) => {
901
+ const presence = data == null ? void 0 : data.presence;
902
+ if (!session)
903
+ return {};
904
+ session.presence = presence;
905
+ return {
906
+ $USER_JOINED: {
907
+ connectionId: session.id,
908
+ user: session.user,
909
+ presence
910
+ }
911
+ };
912
+ }).self((_0, _1) => __async(this, [_0, _1], function* (data, { context, doc, room }) {
913
+ var _a, _b;
914
+ const oldState = yield this._platform.persistance.getStorageState(room);
915
+ const update = data == null ? void 0 : data.update;
916
+ const updates = [
917
+ oldState ? null : yield (_a = this._getInitialStorage) == null ? void 0 : _a.call(this, __spreadProps(__spreadValues({}, context), {
918
+ room
919
+ })),
920
+ update
921
+ ];
922
+ if (updates.some((_update) => !!_update)) {
923
+ const encodedState = doc.batchApplyEncodedState(updates).getEncodedState();
924
+ yield this._platform.persistance.setStorageState(room, encodedState);
925
+ this._listeners.onStorageUpdated(__spreadProps(__spreadValues({}, context), { encodedState, room }));
926
+ }
927
+ const state = (_b = yield this._platform.persistance.getStorageState(room)) != null ? _b : doc.getEncodedState();
928
+ return { $STORAGE_RECEIVED: { state } };
929
+ })),
930
+ $PING: this.procedure.self((data, { session }) => {
931
+ if (!session)
932
+ return {};
933
+ const currentTime = (/* @__PURE__ */ new Date()).getTime();
934
+ session.timers.ping = currentTime;
935
+ return { $PONG: {} };
936
+ }),
937
+ $UPDATE_PRESENCE: this.procedure.broadcast((data, { session }) => {
938
+ const presence = data == null ? void 0 : data.presence;
939
+ if (!session)
940
+ return {};
941
+ session.presence = Object.assign(/* @__PURE__ */ Object.create(null), session.presence, presence);
942
+ return { $PRESENCE_UPDATED: { presence } };
943
+ }),
944
+ $UPDATE_STORAGE: this.procedure.broadcast((data, { context, doc, room }) => {
945
+ var _a;
946
+ const origin = data == null ? void 0 : data.origin;
947
+ const update = (_a = data == null ? void 0 : data.update) != null ? _a : null;
948
+ if (origin === "$INITIALIZED" && Object.keys(doc.toJson()).length) {
949
+ return {};
950
+ }
951
+ const updated = update === null ? doc : doc.applyEncodedState({ update });
952
+ const encodedState = updated.getEncodedState();
953
+ this._platform.persistance.setStorageState(room, encodedState).then(() => {
954
+ this._listeners.onStorageUpdated(__spreadProps(__spreadValues({}, context), {
955
+ encodedState,
956
+ room
957
+ }));
958
+ });
959
+ return { $STORAGE_UPDATED: { state: encodedState } };
960
+ })
961
+ });
962
+ this._authorize = null;
963
+ this._context = {};
964
+ this._getInitialStorage = null;
965
+ this._version = version;
966
+ const {
967
+ authorize: authorize2,
968
+ context,
969
+ crdt = import_crdt3.noop,
970
+ debug = false,
971
+ getInitialStorage,
972
+ onRoomDeleted,
973
+ onStorageUpdated,
974
+ platform
975
+ } = options;
976
+ this._crdt = crdt;
977
+ this._debug = debug;
978
+ this._platform = platform;
979
+ if (authorize2)
980
+ this._authorize = authorize2;
981
+ if (context)
982
+ this._context = context;
983
+ if (getInitialStorage)
984
+ this._getInitialStorage = getInitialStorage;
985
+ this._listeners = {
986
+ onRoomDeleted: (event) => onRoomDeleted == null ? void 0 : onRoomDeleted(event),
987
+ onStorageUpdated: (event) => onStorageUpdated == null ? void 0 : onStorageUpdated(event)
988
+ };
989
+ }
990
+ get _events() {
991
+ return this._router._events;
992
+ }
993
+ get procedure() {
994
+ return new PluvProcedure();
995
+ }
996
+ createToken(params) {
997
+ return __async(this, null, function* () {
998
+ if (!this._authorize) {
999
+ throw new Error("IO does not specify authorize during initialization.");
1000
+ }
1001
+ const ioAuthorize = typeof this._authorize === "function" ? this._authorize(params) : this._authorize;
1002
+ const secret = ioAuthorize.secret;
1003
+ return yield authorize({
1004
+ platform: this._platform,
1005
+ secret
1006
+ }).encode(params);
1007
+ });
1008
+ }
1009
+ mergeRouters(...routers) {
1010
+ return PluvRouter.merge(...routers);
1011
+ }
1012
+ router(events) {
1013
+ return new PluvRouter(events);
1014
+ }
1015
+ server(config = {}) {
1016
+ var _a;
1017
+ const router = config.router ? this.mergeRouters(this._router, config.router) : this._router;
1018
+ return new PluvServer(__spreadProps(__spreadValues({}, this._listeners), {
1019
+ authorize: (_a = this._authorize) != null ? _a : void 0,
1020
+ context: this._context,
1021
+ crdt: this._crdt,
1022
+ debug: this._debug,
1023
+ platform: this._platform,
1024
+ router
1025
+ }));
1026
+ }
1027
+ };
1028
+
1007
1029
  // src/createIO.ts
1008
1030
  var createIO = (params) => {
1009
- const {
1010
- authorize: authorize2,
1011
- context,
1012
- crdt,
1013
- debug,
1014
- getInitialStorage,
1015
- onRoomDeleted,
1016
- onStorageUpdated,
1017
- platform
1018
- } = params;
1031
+ const { authorize: authorize2, context, crdt, debug, getInitialStorage, onRoomDeleted, onStorageUpdated, platform } = params;
1019
1032
  return new PluvIO({
1020
1033
  authorize: authorize2,
1021
1034
  context,
@@ -1034,5 +1047,8 @@ var createIO = (params) => {
1034
1047
  AbstractPubSub,
1035
1048
  AbstractWebSocket,
1036
1049
  PluvIO,
1050
+ PluvProcedure,
1051
+ PluvRouter,
1052
+ PluvServer,
1037
1053
  createIO
1038
1054
  });