@pluv/io 0.17.3 → 0.19.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);
@@ -113,8 +116,7 @@ var Persistance = class extends AbstractPersistance {
113
116
  deleteUser(room, connectionId) {
114
117
  return __async(this, null, function* () {
115
118
  const users = this._users.get(room);
116
- if (!users)
117
- return;
119
+ if (!users) return;
118
120
  users.delete(connectionId);
119
121
  if (!users.size) {
120
122
  yield this.deleteUsers(room);
@@ -143,8 +145,7 @@ var Persistance = class extends AbstractPersistance {
143
145
  }
144
146
  getUsers(room) {
145
147
  const users = this._users.get(room);
146
- if (!users)
147
- return Promise.resolve([]);
148
+ if (!users) return Promise.resolve([]);
148
149
  return Promise.resolve(Array.from(users.values()));
149
150
  }
150
151
  setStorageState(room, state) {
@@ -175,8 +176,7 @@ var PubSub = class extends AbstractPubSub {
175
176
  publish(roomName, payload) {
176
177
  return __async(this, null, function* () {
177
178
  const subject = this._subjects.get(roomName);
178
- if (!subject)
179
- return;
179
+ if (!subject) return;
180
180
  Promise.resolve(subject.next(payload));
181
181
  });
182
182
  }
@@ -194,11 +194,9 @@ var PubSub = class extends AbstractPubSub {
194
194
  unsubscribe(subscriptionId) {
195
195
  var _a;
196
196
  const [roomName, unsubscribe] = (_a = this._unsubscriptions.get(subscriptionId)) != null ? _a : [];
197
- if (!roomName || !unsubscribe)
198
- return;
197
+ if (!roomName || !unsubscribe) return;
199
198
  const refs = this._subscriptionsRefs.get(roomName);
200
- if (!refs)
201
- return;
199
+ if (!refs) return;
202
200
  if (refs.length === 1) {
203
201
  this._subjects.delete(roomName);
204
202
  this._subscriptionsRefs.delete(roomName);
@@ -241,11 +239,7 @@ var AbstractWebSocket = class {
241
239
  }
242
240
  handleError(params) {
243
241
  var _a, _b, _c;
244
- const {
245
- error,
246
- message = error instanceof Error ? error.message : "Unexpected error",
247
- session
248
- } = params;
242
+ const { error, message = error instanceof Error ? error.message : "Unexpected error", session } = params;
249
243
  const stack = error instanceof Error ? (_a = error.stack) != null ? _a : null : null;
250
244
  return this.sendMessage({
251
245
  connectionId: (_b = session == null ? void 0 : session.id) != null ? _b : null,
@@ -261,6 +255,76 @@ var AbstractWebSocket = class {
261
255
  };
262
256
 
263
257
  // src/PluvIO.ts
258
+ var import_crdt3 = require("@pluv/crdt");
259
+
260
+ // src/PluvProcedure.ts
261
+ var PluvProcedure = class _PluvProcedure {
262
+ constructor(config = {}) {
263
+ this._broadcast = null;
264
+ this._input = null;
265
+ this._self = null;
266
+ this._sync = null;
267
+ const { broadcast, input, self, sync } = config;
268
+ this._broadcast = broadcast != null ? broadcast : null;
269
+ this._input = input != null ? input : null;
270
+ this._self = self != null ? self : null;
271
+ this._sync = sync != null ? sync : null;
272
+ }
273
+ get config() {
274
+ var _a, _b, _c, _d, _e, _f, _g;
275
+ return {
276
+ broadcast: (_b = (_a = this._broadcast) == null ? void 0 : _a.bind(this)) != null ? _b : null,
277
+ input: (_c = this._input) != null ? _c : null,
278
+ resolver: this._resolver,
279
+ self: (_e = (_d = this._self) == null ? void 0 : _d.bind(this)) != null ? _e : null,
280
+ sync: (_g = (_f = this._sync) == null ? void 0 : _f.bind(this)) != null ? _g : null
281
+ };
282
+ }
283
+ broadcast(resolver) {
284
+ const { broadcast } = this.config;
285
+ if (!!broadcast) throw new Error("Broadcast was already defined for this procedure");
286
+ return new _PluvProcedure(__spreadProps(__spreadValues({}, this.config), {
287
+ broadcast: resolver
288
+ }));
289
+ }
290
+ input(input) {
291
+ return new _PluvProcedure({ input });
292
+ }
293
+ self(resolver) {
294
+ const { self } = this.config;
295
+ if (!!self) throw new Error("Self was already defined for this procedure");
296
+ return new _PluvProcedure(__spreadProps(__spreadValues({}, this.config), {
297
+ self: resolver
298
+ }));
299
+ }
300
+ sync(resolver) {
301
+ const { sync } = this.config;
302
+ if (!!sync) throw new Error("Sync was already defined for this procedure");
303
+ return new _PluvProcedure(__spreadProps(__spreadValues({}, this.config), {
304
+ sync: resolver
305
+ }));
306
+ }
307
+ _resolver() {
308
+ return (data, context) => {
309
+ var _a, _b, _c;
310
+ 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));
311
+ };
312
+ }
313
+ };
314
+
315
+ // src/PluvRouter.ts
316
+ var PluvRouter = class _PluvRouter {
317
+ constructor(events) {
318
+ this._events = {};
319
+ this._events = events;
320
+ }
321
+ static merge(...routers) {
322
+ const events = Object.assign(/* @__PURE__ */ Object.create(null), ...routers.map((router) => router._events));
323
+ return new _PluvRouter(events);
324
+ }
325
+ };
326
+
327
+ // src/PluvServer.ts
264
328
  var import_crdt2 = require("@pluv/crdt");
265
329
  var import_kleur2 = __toESM(require("kleur"));
266
330
 
@@ -273,13 +337,7 @@ var import_hkdf = __toESM(require("@panva/hkdf"));
273
337
  var import_jose = require("jose");
274
338
  var DEFAULT_MAX_AGE_MS = 6e4;
275
339
  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
- );
340
+ return yield (0, import_hkdf.default)("sha256", secret, "", "Pluv.io Generated Encryption Key", 32);
283
341
  });
284
342
  var now = () => Date.now() / 1e3 | 0;
285
343
  var authorize = (params) => {
@@ -311,37 +369,37 @@ var IORoom = class {
311
369
  this._sessions = /* @__PURE__ */ new Map();
312
370
  this._uninitialize = null;
313
371
  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;
372
+ const { authorize: authorize2, context, crdt = import_crdt.noop, debug, onDestroy, platform, router } = config;
323
373
  this._context = context;
324
374
  this._debug = debug;
325
375
  this._docFactory = crdt.doc(() => ({}));
326
376
  this._doc = this._docFactory.getEmpty();
327
- this._events = events;
377
+ this._router = router;
328
378
  this._platform = platform;
329
379
  this.id = id;
330
380
  this._listeners = {
331
381
  onDestroy: (event) => onDestroy == null ? void 0 : onDestroy(event)
332
382
  };
333
- if (authorize2)
334
- this._authorize = authorize2;
383
+ if (authorize2) this._authorize = authorize2;
335
384
  }
336
- broadcast(event, data) {
337
- const message = { type: event, data };
338
- return Promise.resolve(this._broadcast({ message }));
385
+ get _events() {
386
+ return this._router._events;
387
+ }
388
+ get broadcast() {
389
+ const _broadcast = (event, data) => {
390
+ const message = { type: event, data };
391
+ return Promise.resolve(this._broadcast({ message }));
392
+ };
393
+ return new Proxy(_broadcast, {
394
+ get(fn, prop) {
395
+ return (data) => fn(prop, data);
396
+ }
397
+ });
339
398
  }
340
399
  getSize() {
341
400
  const currentTime = (/* @__PURE__ */ new Date()).getTime();
342
401
  return Array.from(this._sessions.values()).reduce((count, session) => {
343
- if (session.quit)
344
- return count;
402
+ if (session.quit) return count;
345
403
  return currentTime - session.timers.ping > PING_TIMEOUT_MS ? count : count + 1;
346
404
  }, 0);
347
405
  }
@@ -350,8 +408,7 @@ var IORoom = class {
350
408
  var _b;
351
409
  const _a = options, { token } = _a, _platformEventContext = __objRest(_a, ["token"]);
352
410
  const platformEventContext = _platformEventContext;
353
- if (!this._uninitialize)
354
- yield this._initialize();
411
+ if (!this._uninitialize) yield this._initialize();
355
412
  const user = yield this._getAuthorizedUser(token, options);
356
413
  const sessionId = this._platform.randomUUID();
357
414
  const pluvWs = this._platform.convertWebSocket(webSocket, {
@@ -359,26 +416,16 @@ var IORoom = class {
359
416
  sessionId,
360
417
  userId: (_b = user == null ? void 0 : user.id) != null ? _b : null
361
418
  });
362
- this._logDebug(
363
- `${import_kleur.default.blue(
364
- `Registering connection for room ${this.id}:`
365
- )} ${sessionId}`
366
- );
419
+ this._logDebug(`${import_kleur.default.blue(`Registering connection for room ${this.id}:`)} ${sessionId}`);
367
420
  const uninitializeWs = yield pluvWs.initialize();
368
421
  const ioAuthorize = this._getIOAuthorize(options);
369
422
  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
- );
423
+ const isTokenInUse = !!user && Array.from(this._sessions.values()).some((session2) => {
424
+ var _a2;
425
+ return ((_a2 = session2.user) == null ? void 0 : _a2.id) === user.id;
426
+ });
376
427
  if (isUnauthorized || isTokenInUse) {
377
- this._logDebug(
378
- `${import_kleur.default.blue(
379
- "Authorization failed for connection:"
380
- )} ${sessionId}`
381
- );
428
+ this._logDebug(`${import_kleur.default.blue("Authorization failed for connection:")} ${sessionId}`);
382
429
  pluvWs.handleError({ error: new Error("Not authorized") });
383
430
  pluvWs.close(3e3, "WebSocket unauthorized.");
384
431
  return;
@@ -396,24 +443,14 @@ var IORoom = class {
396
443
  user
397
444
  };
398
445
  this._sessions.set(session.id, session);
399
- yield this._platform.persistance.addUser(
400
- this.id,
401
- sessionId,
402
- user != null ? user : {}
403
- );
446
+ yield this._platform.persistance.addUser(this.id, sessionId, user != null ? user : {});
404
447
  const onClose = this._onClose(session, uninitializeWs).bind(this);
405
- const onMessage = this._onMessage(session, platformEventContext).bind(
406
- this
407
- );
448
+ const onMessage = this._onMessage(session, platformEventContext).bind(this);
408
449
  pluvWs.addEventListener("close", onClose);
409
450
  pluvWs.addEventListener("error", onClose);
410
451
  pluvWs.addEventListener("message", onMessage);
411
452
  this._emitRegistered(session);
412
- this._logDebug(
413
- `${import_kleur.default.blue(
414
- `Registered connection for room ${this.id}:`
415
- )} ${sessionId}`
416
- );
453
+ this._logDebug(`${import_kleur.default.blue(`Registered connection for room ${this.id}:`)} ${sessionId}`);
417
454
  const size = this.getSize();
418
455
  this._logDebug(`${import_kleur.default.blue(`Room ${this.id} size:`)} ${size}`);
419
456
  });
@@ -466,10 +503,8 @@ var IORoom = class {
466
503
  return __async(this, null, function* () {
467
504
  var _a;
468
505
  const ioAuthorize = this._getIOAuthorize(context);
469
- if (!ioAuthorize)
470
- return null;
471
- if (!token)
472
- return null;
506
+ if (!ioAuthorize) return null;
507
+ if (!token) return null;
473
508
  const payload = yield authorize({
474
509
  platform: this._platform,
475
510
  secret: ioAuthorize.secret
@@ -480,9 +515,7 @@ var IORoom = class {
480
515
  return null;
481
516
  }
482
517
  if (payload.room !== this.id) {
483
- this._logDebug(
484
- import_kleur.default.blue(`Token is not authorized for room ${this.id}:`)
485
- );
518
+ this._logDebug(import_kleur.default.blue(`Token is not authorized for room ${this.id}:`));
486
519
  this._logDebug(import_kleur.default.blue("Received:"), payload.room);
487
520
  this._logDebug(token);
488
521
  return null;
@@ -490,29 +523,19 @@ var IORoom = class {
490
523
  try {
491
524
  return (_a = ioAuthorize.user.parse(payload.user)) != null ? _a : null;
492
525
  } catch (e) {
493
- this._logDebug(
494
- `${import_kleur.default.blue("Token fails validation:")} ${token}`
495
- );
526
+ this._logDebug(`${import_kleur.default.blue("Token fails validation:")} ${token}`);
496
527
  return null;
497
528
  }
498
529
  });
499
530
  }
500
- _getEventConfig(message) {
531
+ _getProcedure(message) {
501
532
  var _a;
502
- return (_a = this._events[message.type]) != null ? _a : null;
503
- }
504
- _getEventInputs(message) {
505
- const eventConfig = this._getEventConfig(message);
506
- if (!eventConfig)
507
- return message.data;
508
- return eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
533
+ return (_a = this._router._events[message.type]) != null ? _a : null;
509
534
  }
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);
535
+ _getProcedureInputs(message) {
536
+ const procedure = this._getProcedure(message);
537
+ if (!procedure) return message.data;
538
+ return procedure.config.input ? procedure.config.input.parse(message.data) : message.data;
516
539
  }
517
540
  _initialize() {
518
541
  return __async(this, null, function* () {
@@ -521,17 +544,14 @@ var IORoom = class {
521
544
  yield this._uninitialize();
522
545
  this._uninitialize = null;
523
546
  }
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
- );
547
+ const pubSubId = yield this._platform.pubSub.subscribe(this.id, (_a) => {
548
+ var _b = _a, { options = {} } = _b, message = __objRest(_b, ["options"]);
549
+ const session = {
550
+ id: message.connectionId,
551
+ user: message.user
552
+ };
553
+ this._sendMessage(message, session, options);
554
+ });
535
555
  this._uninitialize = () => __async(this, null, function* () {
536
556
  this._platform.pubSub.unsubscribe(pubSubId);
537
557
  const encodedState = this._doc.getEncodedState();
@@ -550,14 +570,9 @@ var IORoom = class {
550
570
  }
551
571
  _onClose(session, callback) {
552
572
  return () => {
553
- if (!this._uninitialize)
554
- return;
573
+ if (!this._uninitialize) return;
555
574
  session.quit = true;
556
- this._logDebug(
557
- `${import_kleur.default.blue(
558
- `(Unregistering connection for room ${this.id}:`
559
- )} ${session.id}`
560
- );
575
+ this._logDebug(`${import_kleur.default.blue(`(Unregistering connection for room ${this.id}:`)} ${session.id}`);
561
576
  this._sessions.delete(session.id);
562
577
  this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
563
578
  var _a;
@@ -570,23 +585,16 @@ var IORoom = class {
570
585
  });
571
586
  callback == null ? void 0 : callback();
572
587
  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
- );
581
- if (size)
582
- return;
588
+ this._logDebug(`${import_kleur.default.blue(`Unregistered connection for room ${this.id}:`)} ${session.id}`);
589
+ this._logDebug(`${import_kleur.default.blue(`Room ${this.id} size:`)} ${size}`);
590
+ if (size) return;
583
591
  (_a = this._uninitialize) == null ? void 0 : _a.call(this);
584
592
  });
585
593
  };
586
594
  }
587
595
  _onMessage(session, platformEventContext) {
588
596
  return (event) => {
589
- var _a, _b, _c;
597
+ var _a, _b, _c, _d, _e, _f;
590
598
  const baseContext = {
591
599
  context: this._context,
592
600
  doc: this._doc,
@@ -594,19 +602,18 @@ var IORoom = class {
594
602
  session,
595
603
  sessions: this._sessions
596
604
  };
597
- if (!this._uninitialize)
598
- return;
605
+ if (!this._uninitialize) return;
599
606
  if (session.quit) {
600
607
  session.webSocket.close(1011, "WebSocket broken.");
601
608
  return;
602
609
  }
603
610
  const message = this._parseMessage(event);
604
- if (!message)
605
- return;
606
- const resolver = this._getEventResolverObject(message);
611
+ if (!message) return;
612
+ const procedure = this._getProcedure(message);
613
+ if (!procedure) return;
607
614
  let inputs;
608
615
  try {
609
- inputs = this._getEventInputs(message);
616
+ inputs = this._getProcedureInputs(message);
610
617
  } catch (error) {
611
618
  session == null ? void 0 : session.webSocket.handleError({
612
619
  error,
@@ -619,9 +626,9 @@ var IORoom = class {
619
626
  context: __spreadValues(__spreadValues({}, this._context), platformEventContext)
620
627
  });
621
628
  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)
629
+ (_b = (_a = procedure.config).broadcast) == null ? void 0 : _b.call(_a, inputs, extendedContext),
630
+ (_d = (_c = procedure.config).self) == null ? void 0 : _d.call(_c, inputs, extendedContext),
631
+ (_f = (_e = procedure.config).sync) == null ? void 0 : _f.call(_e, inputs, baseContext)
625
632
  ]).then(([broadcast, self, sync]) => {
626
633
  if (broadcast) {
627
634
  Object.entries(broadcast).forEach(([type, data]) => {
@@ -653,12 +660,9 @@ var IORoom = class {
653
660
  _parseMessage(message) {
654
661
  try {
655
662
  const parsed = this._platform.parseData(message.data);
656
- if (typeof parsed !== "object")
657
- return null;
658
- if (typeof parsed.type !== "string")
659
- return null;
660
- if (typeof parsed.data !== "object")
661
- return null;
663
+ if (typeof parsed !== "object") return null;
664
+ if (typeof parsed.type !== "string") return null;
665
+ if (typeof parsed.data !== "object") return null;
662
666
  return parsed;
663
667
  } catch (e) {
664
668
  return null;
@@ -698,11 +702,9 @@ var IORoom = class {
698
702
  }
699
703
  _sendSelfMessage(message, sender) {
700
704
  const senderId = sender == null ? void 0 : sender.id;
701
- if (!senderId)
702
- return;
705
+ if (!senderId) return;
703
706
  const session = this._sessions.get(senderId);
704
- if (!session)
705
- return;
707
+ if (!session) return;
706
708
  session.webSocket.sendMessage(__spreadValues({
707
709
  connectionId: sender.id,
708
710
  room: this.id,
@@ -710,9 +712,9 @@ var IORoom = class {
710
712
  }, message));
711
713
  }
712
714
  _sendSyncMessage(message, sender) {
715
+ var _a;
713
716
  const senderId = sender == null ? void 0 : sender.id;
714
- if (!senderId)
715
- return;
717
+ if (!senderId) return;
716
718
  const context = {
717
719
  context: this._context,
718
720
  doc: this._doc,
@@ -720,21 +722,19 @@ var IORoom = class {
720
722
  session: null,
721
723
  sessions: this._sessions
722
724
  };
723
- const resolver = this._getEventResolverObject(message).sync;
724
- if (!resolver)
725
- return;
725
+ const resolver = (_a = this._getProcedure(message)) == null ? void 0 : _a.config.sync;
726
+ if (!resolver) return;
726
727
  let inputs;
727
728
  try {
728
- inputs = this._getEventInputs(message);
729
+ inputs = this._getProcedureInputs(message);
729
730
  } catch (e) {
730
731
  return;
731
732
  }
732
733
  Promise.resolve(resolver(inputs, context)).then((output) => {
733
- if (!output)
734
- return;
734
+ if (!output) return;
735
735
  Object.keys(output).forEach((type) => {
736
- var _a, _b;
737
- const data = (_a = output[type]) != null ? _a : {};
736
+ var _a2, _b;
737
+ const data = (_a2 = output[type]) != null ? _a2 : {};
738
738
  this._platform.pubSub.publish(this.id, {
739
739
  connectionId: senderId,
740
740
  data,
@@ -749,149 +749,43 @@ var IORoom = class {
749
749
  };
750
750
 
751
751
  // src/version.generated.ts
752
- var version = "0.17.3";
752
+ var version = "0.19.0";
753
753
 
754
- // src/PluvIO.ts
755
- var PluvIO = class _PluvIO {
754
+ // src/PluvServer.ts
755
+ var PluvServer = class {
756
756
  constructor(options) {
757
757
  this._rooms = /* @__PURE__ */ new Map();
758
758
  this._authorize = null;
759
759
  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
760
  this._version = version;
862
761
  const {
863
762
  authorize: authorize2,
864
763
  context,
865
764
  crdt = import_crdt2.noop,
866
765
  debug = false,
867
- events,
868
- getInitialStorage,
869
766
  onRoomDeleted,
870
767
  onStorageUpdated,
871
- platform
768
+ platform,
769
+ router = new PluvRouter({})
872
770
  } = options;
873
771
  this._crdt = crdt;
874
772
  this._debug = debug;
875
773
  this._platform = platform;
876
- if (authorize2)
877
- this._authorize = authorize2;
878
- if (context)
879
- this._context = context;
880
- if (events)
881
- this._events = events;
882
- if (getInitialStorage)
883
- this._getInitialStorage = getInitialStorage;
774
+ this._router = router != null ? router : null;
775
+ if (authorize2) this._authorize = authorize2;
776
+ if (context) this._context = context;
884
777
  this._listeners = {
885
778
  onRoomDeleted: (event) => onRoomDeleted == null ? void 0 : onRoomDeleted(event),
886
779
  onStorageUpdated: (event) => onStorageUpdated == null ? void 0 : onStorageUpdated(event)
887
780
  };
888
781
  }
782
+ get _events() {
783
+ return this._router._events;
784
+ }
889
785
  createToken(params) {
890
786
  return __async(this, null, function* () {
891
787
  if (!this._authorize) {
892
- throw new Error(
893
- "IO does not specify authorize during initialization."
894
- );
788
+ throw new Error("IO does not specify authorize during initialization.");
895
789
  }
896
790
  const ioAuthorize = typeof this._authorize === "function" ? this._authorize(params) : this._authorize;
897
791
  const secret = ioAuthorize.secret;
@@ -901,22 +795,6 @@ var PluvIO = class _PluvIO {
901
795
  }).encode(params);
902
796
  });
903
797
  }
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
798
  getRoom(room, options) {
921
799
  var _b;
922
800
  const _a = options, { debug } = _a, platformRoomContext = __objRest(_a, ["debug"]);
@@ -926,19 +804,15 @@ var PluvIO = class _PluvIO {
926
804
  }
927
805
  const platform = this._platform;
928
806
  const oldRoom = this._rooms.get(room);
929
- if (oldRoom)
930
- return oldRoom;
807
+ if (oldRoom) return oldRoom;
931
808
  const roomContext = __spreadValues(__spreadValues({}, this._context), platformRoomContext);
932
809
  const newRoom = new IORoom(room, {
933
810
  authorize: (_b = this._authorize) != null ? _b : void 0,
934
811
  context: roomContext,
935
812
  crdt: this._crdt,
936
813
  debug: debug != null ? debug : this._debug,
937
- events: this._events,
938
814
  onDestroy: ({ encodedState, room: room2 }) => {
939
- this._logDebug(
940
- `${import_kleur2.default.blue("Deleting empty room:")} ${room2}`
941
- );
815
+ this._logDebug(`${import_kleur2.default.blue("Deleting empty room:")} ${room2}`);
942
816
  const roomContext2 = __spreadValues({
943
817
  room: room2,
944
818
  encodedState
@@ -948,74 +822,176 @@ var PluvIO = class _PluvIO {
948
822
  if (this._debug) {
949
823
  const rooms = Array.from(this._rooms.keys());
950
824
  this._logDebug(`${import_kleur2.default.blue("Deleted room:")} ${room2}`);
951
- this._logDebug(
952
- `${import_kleur2.default.blue("Rooms available:")} ${rooms.join(
953
- ", "
954
- )}`
955
- );
825
+ this._logDebug(`${import_kleur2.default.blue("Rooms available:")} ${rooms.join(", ")}`);
956
826
  }
957
827
  },
958
- platform
828
+ platform,
829
+ router: this._router
959
830
  });
960
831
  this._rooms.set(room, newRoom);
961
832
  if (this._debug) {
962
833
  const rooms = Array.from(this._rooms.keys());
963
834
  this._logDebug(`${import_kleur2.default.blue("Created room:")} ${room}`);
964
- this._logDebug(
965
- `${import_kleur2.default.blue("Rooms available:")} ${rooms.join(", ")}`
966
- );
835
+ this._logDebug(`${import_kleur2.default.blue("Rooms available:")} ${rooms.join(", ")}`);
967
836
  }
968
837
  return newRoom;
969
838
  }
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({
839
+ _logDebug(...data) {
840
+ this._debug && console.log(...data);
841
+ }
842
+ _purgeEmptyRooms() {
843
+ const rooms = Array.from(this._rooms.entries());
844
+ rooms.forEach(([id, room]) => {
845
+ if (room.getSize()) return;
846
+ this._rooms.delete(id);
847
+ });
848
+ }
849
+ };
850
+
851
+ // src/PluvIO.ts
852
+ var PluvIO = class {
853
+ constructor(options) {
854
+ this._router = new PluvRouter({
855
+ $GET_OTHERS: this.procedure.sync((data, { room, session, sessions }) => {
856
+ const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
857
+ (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
858
+ [connectionId]: {
859
+ connectionId,
860
+ presence,
861
+ room,
862
+ user
863
+ }
864
+ }),
865
+ {}
866
+ );
867
+ return { $OTHERS_RECEIVED: { others } };
868
+ }),
869
+ $INITIALIZE_SESSION: this.procedure.broadcast((data, { session }) => {
870
+ const presence = data == null ? void 0 : data.presence;
871
+ if (!session) return {};
872
+ session.presence = presence;
873
+ return {
874
+ $USER_JOINED: {
875
+ connectionId: session.id,
876
+ user: session.user,
877
+ presence
878
+ }
879
+ };
880
+ }).self((_0, _1) => __async(this, [_0, _1], function* (data, { context, doc, room }) {
881
+ var _a, _b;
882
+ const oldState = yield this._platform.persistance.getStorageState(room);
883
+ const update = data == null ? void 0 : data.update;
884
+ const updates = [
885
+ oldState ? null : yield (_a = this._getInitialStorage) == null ? void 0 : _a.call(this, __spreadProps(__spreadValues({}, context), {
886
+ room
887
+ })),
888
+ update
889
+ ];
890
+ if (updates.some((_update) => !!_update)) {
891
+ const encodedState = doc.batchApplyEncodedState(updates).getEncodedState();
892
+ yield this._platform.persistance.setStorageState(room, encodedState);
893
+ this._listeners.onStorageUpdated(__spreadProps(__spreadValues({}, context), { encodedState, room }));
894
+ }
895
+ const state = (_b = yield this._platform.persistance.getStorageState(room)) != null ? _b : doc.getEncodedState();
896
+ return { $STORAGE_RECEIVED: { state } };
897
+ })),
898
+ $PING: this.procedure.self((data, { session }) => {
899
+ if (!session) return {};
900
+ const currentTime = (/* @__PURE__ */ new Date()).getTime();
901
+ session.timers.ping = currentTime;
902
+ return { $PONG: {} };
903
+ }),
904
+ $UPDATE_PRESENCE: this.procedure.broadcast((data, { session }) => {
905
+ const presence = data == null ? void 0 : data.presence;
906
+ if (!session) return {};
907
+ session.presence = Object.assign(/* @__PURE__ */ Object.create(null), session.presence, presence);
908
+ return { $PRESENCE_UPDATED: { presence } };
909
+ }),
910
+ $UPDATE_STORAGE: this.procedure.broadcast((data, { context, doc, room }) => {
911
+ var _a;
912
+ const origin = data == null ? void 0 : data.origin;
913
+ const update = (_a = data == null ? void 0 : data.update) != null ? _a : null;
914
+ if (origin === "$INITIALIZED" && Object.keys(doc.toJson()).length) {
915
+ return {};
916
+ }
917
+ const updated = update === null ? doc : doc.applyEncodedState({ update });
918
+ const encodedState = updated.getEncodedState();
919
+ this._platform.persistance.setStorageState(room, encodedState).then(() => {
920
+ this._listeners.onStorageUpdated(__spreadProps(__spreadValues({}, context), {
921
+ encodedState,
922
+ room
923
+ }));
924
+ });
925
+ return { $STORAGE_UPDATED: { state: encodedState } };
926
+ })
927
+ });
928
+ this._authorize = null;
929
+ this._context = {};
930
+ this._getInitialStorage = null;
931
+ this._version = version;
932
+ const {
983
933
  authorize: authorize2,
984
934
  context,
985
- crdt,
986
- debug,
987
- events,
935
+ crdt = import_crdt3.noop,
936
+ debug = false,
988
937
  getInitialStorage,
989
938
  onRoomDeleted,
990
939
  onStorageUpdated,
991
940
  platform
992
- });
941
+ } = options;
942
+ this._crdt = crdt;
943
+ this._debug = debug;
944
+ this._platform = platform;
945
+ if (authorize2) this._authorize = authorize2;
946
+ if (context) this._context = context;
947
+ if (getInitialStorage) this._getInitialStorage = getInitialStorage;
948
+ this._listeners = {
949
+ onRoomDeleted: (event) => onRoomDeleted == null ? void 0 : onRoomDeleted(event),
950
+ onStorageUpdated: (event) => onStorageUpdated == null ? void 0 : onStorageUpdated(event)
951
+ };
993
952
  }
994
- _logDebug(...data) {
995
- this._debug && console.log(...data);
953
+ get _events() {
954
+ return this._router._events;
996
955
  }
997
- _purgeEmptyRooms() {
998
- const rooms = Array.from(this._rooms.entries());
999
- rooms.forEach(([id, room]) => {
1000
- if (room.getSize())
1001
- return;
1002
- this._rooms.delete(id);
956
+ get procedure() {
957
+ return new PluvProcedure();
958
+ }
959
+ createToken(params) {
960
+ return __async(this, null, function* () {
961
+ if (!this._authorize) {
962
+ throw new Error("IO does not specify authorize during initialization.");
963
+ }
964
+ const ioAuthorize = typeof this._authorize === "function" ? this._authorize(params) : this._authorize;
965
+ const secret = ioAuthorize.secret;
966
+ return yield authorize({
967
+ platform: this._platform,
968
+ secret
969
+ }).encode(params);
1003
970
  });
1004
971
  }
972
+ mergeRouters(...routers) {
973
+ return PluvRouter.merge(...routers);
974
+ }
975
+ router(events) {
976
+ return new PluvRouter(events);
977
+ }
978
+ server(config = {}) {
979
+ var _a;
980
+ const router = config.router ? this.mergeRouters(this._router, config.router) : this._router;
981
+ return new PluvServer(__spreadProps(__spreadValues({}, this._listeners), {
982
+ authorize: (_a = this._authorize) != null ? _a : void 0,
983
+ context: this._context,
984
+ crdt: this._crdt,
985
+ debug: this._debug,
986
+ platform: this._platform,
987
+ router
988
+ }));
989
+ }
1005
990
  };
1006
991
 
1007
992
  // src/createIO.ts
1008
993
  var createIO = (params) => {
1009
- const {
1010
- authorize: authorize2,
1011
- context,
1012
- crdt,
1013
- debug,
1014
- getInitialStorage,
1015
- onRoomDeleted,
1016
- onStorageUpdated,
1017
- platform
1018
- } = params;
994
+ const { authorize: authorize2, context, crdt, debug, getInitialStorage, onRoomDeleted, onStorageUpdated, platform } = params;
1019
995
  return new PluvIO({
1020
996
  authorize: authorize2,
1021
997
  context,
@@ -1034,5 +1010,8 @@ var createIO = (params) => {
1034
1010
  AbstractPubSub,
1035
1011
  AbstractWebSocket,
1036
1012
  PluvIO,
1013
+ PluvProcedure,
1014
+ PluvRouter,
1015
+ PluvServer,
1037
1016
  createIO
1038
1017
  });