@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.mjs CHANGED
@@ -75,8 +75,7 @@ var Persistance = class extends AbstractPersistance {
75
75
  deleteUser(room, connectionId) {
76
76
  return __async(this, null, function* () {
77
77
  const users = this._users.get(room);
78
- if (!users)
79
- return;
78
+ if (!users) return;
80
79
  users.delete(connectionId);
81
80
  if (!users.size) {
82
81
  yield this.deleteUsers(room);
@@ -105,8 +104,7 @@ var Persistance = class extends AbstractPersistance {
105
104
  }
106
105
  getUsers(room) {
107
106
  const users = this._users.get(room);
108
- if (!users)
109
- return Promise.resolve([]);
107
+ if (!users) return Promise.resolve([]);
110
108
  return Promise.resolve(Array.from(users.values()));
111
109
  }
112
110
  setStorageState(room, state) {
@@ -137,8 +135,7 @@ var PubSub = class extends AbstractPubSub {
137
135
  publish(roomName, payload) {
138
136
  return __async(this, null, function* () {
139
137
  const subject = this._subjects.get(roomName);
140
- if (!subject)
141
- return;
138
+ if (!subject) return;
142
139
  Promise.resolve(subject.next(payload));
143
140
  });
144
141
  }
@@ -156,11 +153,9 @@ var PubSub = class extends AbstractPubSub {
156
153
  unsubscribe(subscriptionId) {
157
154
  var _a;
158
155
  const [roomName, unsubscribe] = (_a = this._unsubscriptions.get(subscriptionId)) != null ? _a : [];
159
- if (!roomName || !unsubscribe)
160
- return;
156
+ if (!roomName || !unsubscribe) return;
161
157
  const refs = this._subscriptionsRefs.get(roomName);
162
- if (!refs)
163
- return;
158
+ if (!refs) return;
164
159
  if (refs.length === 1) {
165
160
  this._subjects.delete(roomName);
166
161
  this._subscriptionsRefs.delete(roomName);
@@ -203,11 +198,7 @@ var AbstractWebSocket = class {
203
198
  }
204
199
  handleError(params) {
205
200
  var _a, _b, _c;
206
- const {
207
- error,
208
- message = error instanceof Error ? error.message : "Unexpected error",
209
- session
210
- } = params;
201
+ const { error, message = error instanceof Error ? error.message : "Unexpected error", session } = params;
211
202
  const stack = error instanceof Error ? (_a = error.stack) != null ? _a : null : null;
212
203
  return this.sendMessage({
213
204
  connectionId: (_b = session == null ? void 0 : session.id) != null ? _b : null,
@@ -223,6 +214,76 @@ var AbstractWebSocket = class {
223
214
  };
224
215
 
225
216
  // src/PluvIO.ts
217
+ import { noop as noop3 } from "@pluv/crdt";
218
+
219
+ // src/PluvProcedure.ts
220
+ var PluvProcedure = class _PluvProcedure {
221
+ constructor(config = {}) {
222
+ this._broadcast = null;
223
+ this._input = null;
224
+ this._self = null;
225
+ this._sync = null;
226
+ const { broadcast, input, self, sync } = config;
227
+ this._broadcast = broadcast != null ? broadcast : null;
228
+ this._input = input != null ? input : null;
229
+ this._self = self != null ? self : null;
230
+ this._sync = sync != null ? sync : null;
231
+ }
232
+ get config() {
233
+ var _a, _b, _c, _d, _e, _f, _g;
234
+ return {
235
+ broadcast: (_b = (_a = this._broadcast) == null ? void 0 : _a.bind(this)) != null ? _b : null,
236
+ input: (_c = this._input) != null ? _c : null,
237
+ resolver: this._resolver,
238
+ self: (_e = (_d = this._self) == null ? void 0 : _d.bind(this)) != null ? _e : null,
239
+ sync: (_g = (_f = this._sync) == null ? void 0 : _f.bind(this)) != null ? _g : null
240
+ };
241
+ }
242
+ broadcast(resolver) {
243
+ const { broadcast } = this.config;
244
+ if (!!broadcast) throw new Error("Broadcast was already defined for this procedure");
245
+ return new _PluvProcedure(__spreadProps(__spreadValues({}, this.config), {
246
+ broadcast: resolver
247
+ }));
248
+ }
249
+ input(input) {
250
+ return new _PluvProcedure({ input });
251
+ }
252
+ self(resolver) {
253
+ const { self } = this.config;
254
+ if (!!self) throw new Error("Self was already defined for this procedure");
255
+ return new _PluvProcedure(__spreadProps(__spreadValues({}, this.config), {
256
+ self: resolver
257
+ }));
258
+ }
259
+ sync(resolver) {
260
+ const { sync } = this.config;
261
+ if (!!sync) throw new Error("Sync was already defined for this procedure");
262
+ return new _PluvProcedure(__spreadProps(__spreadValues({}, this.config), {
263
+ sync: resolver
264
+ }));
265
+ }
266
+ _resolver() {
267
+ return (data, context) => {
268
+ var _a, _b, _c;
269
+ 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));
270
+ };
271
+ }
272
+ };
273
+
274
+ // src/PluvRouter.ts
275
+ var PluvRouter = class _PluvRouter {
276
+ constructor(events) {
277
+ this._events = {};
278
+ this._events = events;
279
+ }
280
+ static merge(...routers) {
281
+ const events = Object.assign(/* @__PURE__ */ Object.create(null), ...routers.map((router) => router._events));
282
+ return new _PluvRouter(events);
283
+ }
284
+ };
285
+
286
+ // src/PluvServer.ts
226
287
  import { noop as noop2 } from "@pluv/crdt";
227
288
  import colors2 from "kleur";
228
289
 
@@ -235,13 +296,7 @@ import hkdf from "@panva/hkdf";
235
296
  import { EncryptJWT, jwtDecrypt } from "jose";
236
297
  var DEFAULT_MAX_AGE_MS = 6e4;
237
298
  var getEncryptionKey = (secret) => __async(void 0, null, function* () {
238
- return yield hkdf(
239
- "sha256",
240
- secret,
241
- "",
242
- "Pluv.io Generated Encryption Key",
243
- 32
244
- );
299
+ return yield hkdf("sha256", secret, "", "Pluv.io Generated Encryption Key", 32);
245
300
  });
246
301
  var now = () => Date.now() / 1e3 | 0;
247
302
  var authorize = (params) => {
@@ -273,37 +328,37 @@ var IORoom = class {
273
328
  this._sessions = /* @__PURE__ */ new Map();
274
329
  this._uninitialize = null;
275
330
  this._authorize = null;
276
- const {
277
- authorize: authorize2,
278
- context,
279
- crdt = noop,
280
- debug,
281
- events,
282
- onDestroy,
283
- platform
284
- } = config;
331
+ const { authorize: authorize2, context, crdt = noop, debug, onDestroy, platform, router } = config;
285
332
  this._context = context;
286
333
  this._debug = debug;
287
334
  this._docFactory = crdt.doc(() => ({}));
288
335
  this._doc = this._docFactory.getEmpty();
289
- this._events = events;
336
+ this._router = router;
290
337
  this._platform = platform;
291
338
  this.id = id;
292
339
  this._listeners = {
293
340
  onDestroy: (event) => onDestroy == null ? void 0 : onDestroy(event)
294
341
  };
295
- if (authorize2)
296
- this._authorize = authorize2;
342
+ if (authorize2) this._authorize = authorize2;
297
343
  }
298
- broadcast(event, data) {
299
- const message = { type: event, data };
300
- return Promise.resolve(this._broadcast({ message }));
344
+ get _events() {
345
+ return this._router._events;
346
+ }
347
+ get broadcast() {
348
+ const _broadcast = (event, data) => {
349
+ const message = { type: event, data };
350
+ return Promise.resolve(this._broadcast({ message }));
351
+ };
352
+ return new Proxy(_broadcast, {
353
+ get(fn, prop) {
354
+ return (data) => fn(prop, data);
355
+ }
356
+ });
301
357
  }
302
358
  getSize() {
303
359
  const currentTime = (/* @__PURE__ */ new Date()).getTime();
304
360
  return Array.from(this._sessions.values()).reduce((count, session) => {
305
- if (session.quit)
306
- return count;
361
+ if (session.quit) return count;
307
362
  return currentTime - session.timers.ping > PING_TIMEOUT_MS ? count : count + 1;
308
363
  }, 0);
309
364
  }
@@ -312,8 +367,7 @@ var IORoom = class {
312
367
  var _b;
313
368
  const _a = options, { token } = _a, _platformEventContext = __objRest(_a, ["token"]);
314
369
  const platformEventContext = _platformEventContext;
315
- if (!this._uninitialize)
316
- yield this._initialize();
370
+ if (!this._uninitialize) yield this._initialize();
317
371
  const user = yield this._getAuthorizedUser(token, options);
318
372
  const sessionId = this._platform.randomUUID();
319
373
  const pluvWs = this._platform.convertWebSocket(webSocket, {
@@ -321,26 +375,16 @@ var IORoom = class {
321
375
  sessionId,
322
376
  userId: (_b = user == null ? void 0 : user.id) != null ? _b : null
323
377
  });
324
- this._logDebug(
325
- `${colors.blue(
326
- `Registering connection for room ${this.id}:`
327
- )} ${sessionId}`
328
- );
378
+ this._logDebug(`${colors.blue(`Registering connection for room ${this.id}:`)} ${sessionId}`);
329
379
  const uninitializeWs = yield pluvWs.initialize();
330
380
  const ioAuthorize = this._getIOAuthorize(options);
331
381
  const isUnauthorized = !!(ioAuthorize == null ? void 0 : ioAuthorize.required) && !user;
332
- const isTokenInUse = !!user && Array.from(this._sessions.values()).some(
333
- (session2) => {
334
- var _a2;
335
- return ((_a2 = session2.user) == null ? void 0 : _a2.id) === user.id;
336
- }
337
- );
382
+ const isTokenInUse = !!user && Array.from(this._sessions.values()).some((session2) => {
383
+ var _a2;
384
+ return ((_a2 = session2.user) == null ? void 0 : _a2.id) === user.id;
385
+ });
338
386
  if (isUnauthorized || isTokenInUse) {
339
- this._logDebug(
340
- `${colors.blue(
341
- "Authorization failed for connection:"
342
- )} ${sessionId}`
343
- );
387
+ this._logDebug(`${colors.blue("Authorization failed for connection:")} ${sessionId}`);
344
388
  pluvWs.handleError({ error: new Error("Not authorized") });
345
389
  pluvWs.close(3e3, "WebSocket unauthorized.");
346
390
  return;
@@ -358,24 +402,14 @@ var IORoom = class {
358
402
  user
359
403
  };
360
404
  this._sessions.set(session.id, session);
361
- yield this._platform.persistance.addUser(
362
- this.id,
363
- sessionId,
364
- user != null ? user : {}
365
- );
405
+ yield this._platform.persistance.addUser(this.id, sessionId, user != null ? user : {});
366
406
  const onClose = this._onClose(session, uninitializeWs).bind(this);
367
- const onMessage = this._onMessage(session, platformEventContext).bind(
368
- this
369
- );
407
+ const onMessage = this._onMessage(session, platformEventContext).bind(this);
370
408
  pluvWs.addEventListener("close", onClose);
371
409
  pluvWs.addEventListener("error", onClose);
372
410
  pluvWs.addEventListener("message", onMessage);
373
411
  this._emitRegistered(session);
374
- this._logDebug(
375
- `${colors.blue(
376
- `Registered connection for room ${this.id}:`
377
- )} ${sessionId}`
378
- );
412
+ this._logDebug(`${colors.blue(`Registered connection for room ${this.id}:`)} ${sessionId}`);
379
413
  const size = this.getSize();
380
414
  this._logDebug(`${colors.blue(`Room ${this.id} size:`)} ${size}`);
381
415
  });
@@ -428,10 +462,8 @@ var IORoom = class {
428
462
  return __async(this, null, function* () {
429
463
  var _a;
430
464
  const ioAuthorize = this._getIOAuthorize(context);
431
- if (!ioAuthorize)
432
- return null;
433
- if (!token)
434
- return null;
465
+ if (!ioAuthorize) return null;
466
+ if (!token) return null;
435
467
  const payload = yield authorize({
436
468
  platform: this._platform,
437
469
  secret: ioAuthorize.secret
@@ -442,9 +474,7 @@ var IORoom = class {
442
474
  return null;
443
475
  }
444
476
  if (payload.room !== this.id) {
445
- this._logDebug(
446
- colors.blue(`Token is not authorized for room ${this.id}:`)
447
- );
477
+ this._logDebug(colors.blue(`Token is not authorized for room ${this.id}:`));
448
478
  this._logDebug(colors.blue("Received:"), payload.room);
449
479
  this._logDebug(token);
450
480
  return null;
@@ -452,29 +482,19 @@ var IORoom = class {
452
482
  try {
453
483
  return (_a = ioAuthorize.user.parse(payload.user)) != null ? _a : null;
454
484
  } catch (e) {
455
- this._logDebug(
456
- `${colors.blue("Token fails validation:")} ${token}`
457
- );
485
+ this._logDebug(`${colors.blue("Token fails validation:")} ${token}`);
458
486
  return null;
459
487
  }
460
488
  });
461
489
  }
462
- _getEventConfig(message) {
490
+ _getProcedure(message) {
463
491
  var _a;
464
- return (_a = this._events[message.type]) != null ? _a : null;
465
- }
466
- _getEventInputs(message) {
467
- const eventConfig = this._getEventConfig(message);
468
- if (!eventConfig)
469
- return message.data;
470
- return eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
492
+ return (_a = this._router._events[message.type]) != null ? _a : null;
471
493
  }
472
- _getEventResolverObject(message) {
473
- const eventConfig = this._getEventConfig(message);
474
- if (!eventConfig)
475
- return {};
476
- const resolver = eventConfig.resolver;
477
- return typeof resolver === "function" ? { broadcast: resolver } : __spreadValues({}, resolver);
494
+ _getProcedureInputs(message) {
495
+ const procedure = this._getProcedure(message);
496
+ if (!procedure) return message.data;
497
+ return procedure.config.input ? procedure.config.input.parse(message.data) : message.data;
478
498
  }
479
499
  _initialize() {
480
500
  return __async(this, null, function* () {
@@ -483,17 +503,14 @@ var IORoom = class {
483
503
  yield this._uninitialize();
484
504
  this._uninitialize = null;
485
505
  }
486
- const pubSubId = yield this._platform.pubSub.subscribe(
487
- this.id,
488
- (_a) => {
489
- var _b = _a, { options = {} } = _b, message = __objRest(_b, ["options"]);
490
- const session = {
491
- id: message.connectionId,
492
- user: message.user
493
- };
494
- this._sendMessage(message, session, options);
495
- }
496
- );
506
+ const pubSubId = yield this._platform.pubSub.subscribe(this.id, (_a) => {
507
+ var _b = _a, { options = {} } = _b, message = __objRest(_b, ["options"]);
508
+ const session = {
509
+ id: message.connectionId,
510
+ user: message.user
511
+ };
512
+ this._sendMessage(message, session, options);
513
+ });
497
514
  this._uninitialize = () => __async(this, null, function* () {
498
515
  this._platform.pubSub.unsubscribe(pubSubId);
499
516
  const encodedState = this._doc.getEncodedState();
@@ -512,14 +529,9 @@ var IORoom = class {
512
529
  }
513
530
  _onClose(session, callback) {
514
531
  return () => {
515
- if (!this._uninitialize)
516
- return;
532
+ if (!this._uninitialize) return;
517
533
  session.quit = true;
518
- this._logDebug(
519
- `${colors.blue(
520
- `(Unregistering connection for room ${this.id}:`
521
- )} ${session.id}`
522
- );
534
+ this._logDebug(`${colors.blue(`(Unregistering connection for room ${this.id}:`)} ${session.id}`);
523
535
  this._sessions.delete(session.id);
524
536
  this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
525
537
  var _a;
@@ -532,23 +544,16 @@ var IORoom = class {
532
544
  });
533
545
  callback == null ? void 0 : callback();
534
546
  const size = this.getSize();
535
- this._logDebug(
536
- `${colors.blue(
537
- `Unregistered connection for room ${this.id}:`
538
- )} ${session.id}`
539
- );
540
- this._logDebug(
541
- `${colors.blue(`Room ${this.id} size:`)} ${size}`
542
- );
543
- if (size)
544
- return;
547
+ this._logDebug(`${colors.blue(`Unregistered connection for room ${this.id}:`)} ${session.id}`);
548
+ this._logDebug(`${colors.blue(`Room ${this.id} size:`)} ${size}`);
549
+ if (size) return;
545
550
  (_a = this._uninitialize) == null ? void 0 : _a.call(this);
546
551
  });
547
552
  };
548
553
  }
549
554
  _onMessage(session, platformEventContext) {
550
555
  return (event) => {
551
- var _a, _b, _c;
556
+ var _a, _b, _c, _d, _e, _f;
552
557
  const baseContext = {
553
558
  context: this._context,
554
559
  doc: this._doc,
@@ -556,19 +561,18 @@ var IORoom = class {
556
561
  session,
557
562
  sessions: this._sessions
558
563
  };
559
- if (!this._uninitialize)
560
- return;
564
+ if (!this._uninitialize) return;
561
565
  if (session.quit) {
562
566
  session.webSocket.close(1011, "WebSocket broken.");
563
567
  return;
564
568
  }
565
569
  const message = this._parseMessage(event);
566
- if (!message)
567
- return;
568
- const resolver = this._getEventResolverObject(message);
570
+ if (!message) return;
571
+ const procedure = this._getProcedure(message);
572
+ if (!procedure) return;
569
573
  let inputs;
570
574
  try {
571
- inputs = this._getEventInputs(message);
575
+ inputs = this._getProcedureInputs(message);
572
576
  } catch (error) {
573
577
  session == null ? void 0 : session.webSocket.handleError({
574
578
  error,
@@ -581,9 +585,9 @@ var IORoom = class {
581
585
  context: __spreadValues(__spreadValues({}, this._context), platformEventContext)
582
586
  });
583
587
  Promise.all([
584
- (_a = resolver.broadcast) == null ? void 0 : _a.call(resolver, inputs, extendedContext),
585
- (_b = resolver.self) == null ? void 0 : _b.call(resolver, inputs, extendedContext),
586
- (_c = resolver.sync) == null ? void 0 : _c.call(resolver, inputs, baseContext)
588
+ (_b = (_a = procedure.config).broadcast) == null ? void 0 : _b.call(_a, inputs, extendedContext),
589
+ (_d = (_c = procedure.config).self) == null ? void 0 : _d.call(_c, inputs, extendedContext),
590
+ (_f = (_e = procedure.config).sync) == null ? void 0 : _f.call(_e, inputs, baseContext)
587
591
  ]).then(([broadcast, self, sync]) => {
588
592
  if (broadcast) {
589
593
  Object.entries(broadcast).forEach(([type, data]) => {
@@ -615,12 +619,9 @@ var IORoom = class {
615
619
  _parseMessage(message) {
616
620
  try {
617
621
  const parsed = this._platform.parseData(message.data);
618
- if (typeof parsed !== "object")
619
- return null;
620
- if (typeof parsed.type !== "string")
621
- return null;
622
- if (typeof parsed.data !== "object")
623
- return null;
622
+ if (typeof parsed !== "object") return null;
623
+ if (typeof parsed.type !== "string") return null;
624
+ if (typeof parsed.data !== "object") return null;
624
625
  return parsed;
625
626
  } catch (e) {
626
627
  return null;
@@ -660,11 +661,9 @@ var IORoom = class {
660
661
  }
661
662
  _sendSelfMessage(message, sender) {
662
663
  const senderId = sender == null ? void 0 : sender.id;
663
- if (!senderId)
664
- return;
664
+ if (!senderId) return;
665
665
  const session = this._sessions.get(senderId);
666
- if (!session)
667
- return;
666
+ if (!session) return;
668
667
  session.webSocket.sendMessage(__spreadValues({
669
668
  connectionId: sender.id,
670
669
  room: this.id,
@@ -672,9 +671,9 @@ var IORoom = class {
672
671
  }, message));
673
672
  }
674
673
  _sendSyncMessage(message, sender) {
674
+ var _a;
675
675
  const senderId = sender == null ? void 0 : sender.id;
676
- if (!senderId)
677
- return;
676
+ if (!senderId) return;
678
677
  const context = {
679
678
  context: this._context,
680
679
  doc: this._doc,
@@ -682,21 +681,19 @@ var IORoom = class {
682
681
  session: null,
683
682
  sessions: this._sessions
684
683
  };
685
- const resolver = this._getEventResolverObject(message).sync;
686
- if (!resolver)
687
- return;
684
+ const resolver = (_a = this._getProcedure(message)) == null ? void 0 : _a.config.sync;
685
+ if (!resolver) return;
688
686
  let inputs;
689
687
  try {
690
- inputs = this._getEventInputs(message);
688
+ inputs = this._getProcedureInputs(message);
691
689
  } catch (e) {
692
690
  return;
693
691
  }
694
692
  Promise.resolve(resolver(inputs, context)).then((output) => {
695
- if (!output)
696
- return;
693
+ if (!output) return;
697
694
  Object.keys(output).forEach((type) => {
698
- var _a, _b;
699
- const data = (_a = output[type]) != null ? _a : {};
695
+ var _a2, _b;
696
+ const data = (_a2 = output[type]) != null ? _a2 : {};
700
697
  this._platform.pubSub.publish(this.id, {
701
698
  connectionId: senderId,
702
699
  data,
@@ -711,149 +708,43 @@ var IORoom = class {
711
708
  };
712
709
 
713
710
  // src/version.generated.ts
714
- var version = "0.17.3";
711
+ var version = "0.19.0";
715
712
 
716
- // src/PluvIO.ts
717
- var PluvIO = class _PluvIO {
713
+ // src/PluvServer.ts
714
+ var PluvServer = class {
718
715
  constructor(options) {
719
716
  this._rooms = /* @__PURE__ */ new Map();
720
717
  this._authorize = null;
721
718
  this._context = {};
722
- this._events = {
723
- $GET_OTHERS: {
724
- resolver: {
725
- sync: (data, { room, session, sessions }) => {
726
- const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
727
- (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
728
- [connectionId]: {
729
- connectionId,
730
- presence,
731
- room,
732
- user
733
- }
734
- }),
735
- {}
736
- );
737
- return { $OTHERS_RECEIVED: { others } };
738
- }
739
- }
740
- },
741
- $INITIALIZE_SESSION: {
742
- resolver: {
743
- broadcast: ({ presence }, { session }) => {
744
- if (!session)
745
- return {};
746
- session.presence = presence;
747
- return {
748
- $USER_JOINED: {
749
- connectionId: session.id,
750
- user: session.user,
751
- presence
752
- }
753
- };
754
- },
755
- self: (_0, _1) => __async(this, [_0, _1], function* ({ update }, { context, doc, room }) {
756
- var _a, _b;
757
- const oldState = yield this._platform.persistance.getStorageState(room);
758
- const updates = [
759
- oldState ? null : yield (_a = this._getInitialStorage) == null ? void 0 : _a.call(this, __spreadProps(__spreadValues({}, context), {
760
- room
761
- })),
762
- update
763
- ];
764
- if (updates.some((_update) => !!_update)) {
765
- const encodedState = doc.batchApplyEncodedState(updates).getEncodedState();
766
- yield this._platform.persistance.setStorageState(
767
- room,
768
- encodedState
769
- );
770
- this._listeners.onStorageUpdated(__spreadProps(__spreadValues({}, context), {
771
- encodedState,
772
- room
773
- }));
774
- }
775
- const state = (_b = yield this._platform.persistance.getStorageState(
776
- room
777
- )) != null ? _b : doc.getEncodedState();
778
- return { $STORAGE_RECEIVED: { state } };
779
- })
780
- }
781
- },
782
- $PING: {
783
- resolver: {
784
- self: (data, { session }) => {
785
- if (!session)
786
- return {};
787
- const currentTime = (/* @__PURE__ */ new Date()).getTime();
788
- session.timers.ping = currentTime;
789
- return { $PONG: {} };
790
- }
791
- }
792
- },
793
- $UPDATE_PRESENCE: {
794
- resolver: ({ presence }, { session }) => {
795
- if (!session)
796
- return {};
797
- session.presence = Object.assign(
798
- /* @__PURE__ */ Object.create(null),
799
- session.presence,
800
- presence
801
- );
802
- return { $PRESENCE_UPDATED: { presence } };
803
- }
804
- },
805
- $UPDATE_STORAGE: {
806
- resolver: ({ origin, update }, { context, doc, room }) => {
807
- if (origin === "$INITIALIZED" && Object.keys(doc.toJson()).length) {
808
- return;
809
- }
810
- const updated = doc.applyEncodedState({ update });
811
- const encodedState = updated.getEncodedState();
812
- this._platform.persistance.setStorageState(room, encodedState).then(() => {
813
- this._listeners.onStorageUpdated(__spreadProps(__spreadValues({}, context), {
814
- encodedState,
815
- room
816
- }));
817
- });
818
- return { $STORAGE_UPDATED: { state: encodedState } };
819
- }
820
- }
821
- };
822
- this._getInitialStorage = null;
823
719
  this._version = version;
824
720
  const {
825
721
  authorize: authorize2,
826
722
  context,
827
723
  crdt = noop2,
828
724
  debug = false,
829
- events,
830
- getInitialStorage,
831
725
  onRoomDeleted,
832
726
  onStorageUpdated,
833
- platform
727
+ platform,
728
+ router = new PluvRouter({})
834
729
  } = options;
835
730
  this._crdt = crdt;
836
731
  this._debug = debug;
837
732
  this._platform = platform;
838
- if (authorize2)
839
- this._authorize = authorize2;
840
- if (context)
841
- this._context = context;
842
- if (events)
843
- this._events = events;
844
- if (getInitialStorage)
845
- this._getInitialStorage = getInitialStorage;
733
+ this._router = router != null ? router : null;
734
+ if (authorize2) this._authorize = authorize2;
735
+ if (context) this._context = context;
846
736
  this._listeners = {
847
737
  onRoomDeleted: (event) => onRoomDeleted == null ? void 0 : onRoomDeleted(event),
848
738
  onStorageUpdated: (event) => onStorageUpdated == null ? void 0 : onStorageUpdated(event)
849
739
  };
850
740
  }
741
+ get _events() {
742
+ return this._router._events;
743
+ }
851
744
  createToken(params) {
852
745
  return __async(this, null, function* () {
853
746
  if (!this._authorize) {
854
- throw new Error(
855
- "IO does not specify authorize during initialization."
856
- );
747
+ throw new Error("IO does not specify authorize during initialization.");
857
748
  }
858
749
  const ioAuthorize = typeof this._authorize === "function" ? this._authorize(params) : this._authorize;
859
750
  const secret = ioAuthorize.secret;
@@ -863,22 +754,6 @@ var PluvIO = class _PluvIO {
863
754
  }).encode(params);
864
755
  });
865
756
  }
866
- event(event, config) {
867
- if (event.startsWith("$")) {
868
- throw new Error('Event name must not start with "$".');
869
- }
870
- const newEvent = {
871
- [event]: config
872
- };
873
- return _PluvIO.merge(
874
- this,
875
- new _PluvIO({
876
- crdt: this._crdt,
877
- events: newEvent,
878
- platform: this._platform
879
- })
880
- );
881
- }
882
757
  getRoom(room, options) {
883
758
  var _b;
884
759
  const _a = options, { debug } = _a, platformRoomContext = __objRest(_a, ["debug"]);
@@ -888,19 +763,15 @@ var PluvIO = class _PluvIO {
888
763
  }
889
764
  const platform = this._platform;
890
765
  const oldRoom = this._rooms.get(room);
891
- if (oldRoom)
892
- return oldRoom;
766
+ if (oldRoom) return oldRoom;
893
767
  const roomContext = __spreadValues(__spreadValues({}, this._context), platformRoomContext);
894
768
  const newRoom = new IORoom(room, {
895
769
  authorize: (_b = this._authorize) != null ? _b : void 0,
896
770
  context: roomContext,
897
771
  crdt: this._crdt,
898
772
  debug: debug != null ? debug : this._debug,
899
- events: this._events,
900
773
  onDestroy: ({ encodedState, room: room2 }) => {
901
- this._logDebug(
902
- `${colors2.blue("Deleting empty room:")} ${room2}`
903
- );
774
+ this._logDebug(`${colors2.blue("Deleting empty room:")} ${room2}`);
904
775
  const roomContext2 = __spreadValues({
905
776
  room: room2,
906
777
  encodedState
@@ -910,74 +781,176 @@ var PluvIO = class _PluvIO {
910
781
  if (this._debug) {
911
782
  const rooms = Array.from(this._rooms.keys());
912
783
  this._logDebug(`${colors2.blue("Deleted room:")} ${room2}`);
913
- this._logDebug(
914
- `${colors2.blue("Rooms available:")} ${rooms.join(
915
- ", "
916
- )}`
917
- );
784
+ this._logDebug(`${colors2.blue("Rooms available:")} ${rooms.join(", ")}`);
918
785
  }
919
786
  },
920
- platform
787
+ platform,
788
+ router: this._router
921
789
  });
922
790
  this._rooms.set(room, newRoom);
923
791
  if (this._debug) {
924
792
  const rooms = Array.from(this._rooms.keys());
925
793
  this._logDebug(`${colors2.blue("Created room:")} ${room}`);
926
- this._logDebug(
927
- `${colors2.blue("Rooms available:")} ${rooms.join(", ")}`
928
- );
794
+ this._logDebug(`${colors2.blue("Rooms available:")} ${rooms.join(", ")}`);
929
795
  }
930
796
  return newRoom;
931
797
  }
932
- static merge(first, second) {
933
- var _a;
934
- const authorize2 = (_a = first._authorize) != null ? _a : void 0;
935
- const context = first._context;
936
- const crdt = first._crdt;
937
- const debug = first._debug;
938
- const platform = first._platform;
939
- const getInitialStorage = first._getInitialStorage;
940
- const events1 = first._events;
941
- const events2 = second._events;
942
- const { onRoomDeleted, onStorageUpdated } = first._listeners;
943
- const events = Object.assign(/* @__PURE__ */ Object.create(null), events1, events2);
944
- return new _PluvIO({
798
+ _logDebug(...data) {
799
+ this._debug && console.log(...data);
800
+ }
801
+ _purgeEmptyRooms() {
802
+ const rooms = Array.from(this._rooms.entries());
803
+ rooms.forEach(([id, room]) => {
804
+ if (room.getSize()) return;
805
+ this._rooms.delete(id);
806
+ });
807
+ }
808
+ };
809
+
810
+ // src/PluvIO.ts
811
+ var PluvIO = class {
812
+ constructor(options) {
813
+ this._router = new PluvRouter({
814
+ $GET_OTHERS: this.procedure.sync((data, { room, session, sessions }) => {
815
+ const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
816
+ (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
817
+ [connectionId]: {
818
+ connectionId,
819
+ presence,
820
+ room,
821
+ user
822
+ }
823
+ }),
824
+ {}
825
+ );
826
+ return { $OTHERS_RECEIVED: { others } };
827
+ }),
828
+ $INITIALIZE_SESSION: this.procedure.broadcast((data, { session }) => {
829
+ const presence = data == null ? void 0 : data.presence;
830
+ if (!session) return {};
831
+ session.presence = presence;
832
+ return {
833
+ $USER_JOINED: {
834
+ connectionId: session.id,
835
+ user: session.user,
836
+ presence
837
+ }
838
+ };
839
+ }).self((_0, _1) => __async(this, [_0, _1], function* (data, { context, doc, room }) {
840
+ var _a, _b;
841
+ const oldState = yield this._platform.persistance.getStorageState(room);
842
+ const update = data == null ? void 0 : data.update;
843
+ const updates = [
844
+ oldState ? null : yield (_a = this._getInitialStorage) == null ? void 0 : _a.call(this, __spreadProps(__spreadValues({}, context), {
845
+ room
846
+ })),
847
+ update
848
+ ];
849
+ if (updates.some((_update) => !!_update)) {
850
+ const encodedState = doc.batchApplyEncodedState(updates).getEncodedState();
851
+ yield this._platform.persistance.setStorageState(room, encodedState);
852
+ this._listeners.onStorageUpdated(__spreadProps(__spreadValues({}, context), { encodedState, room }));
853
+ }
854
+ const state = (_b = yield this._platform.persistance.getStorageState(room)) != null ? _b : doc.getEncodedState();
855
+ return { $STORAGE_RECEIVED: { state } };
856
+ })),
857
+ $PING: this.procedure.self((data, { session }) => {
858
+ if (!session) return {};
859
+ const currentTime = (/* @__PURE__ */ new Date()).getTime();
860
+ session.timers.ping = currentTime;
861
+ return { $PONG: {} };
862
+ }),
863
+ $UPDATE_PRESENCE: this.procedure.broadcast((data, { session }) => {
864
+ const presence = data == null ? void 0 : data.presence;
865
+ if (!session) return {};
866
+ session.presence = Object.assign(/* @__PURE__ */ Object.create(null), session.presence, presence);
867
+ return { $PRESENCE_UPDATED: { presence } };
868
+ }),
869
+ $UPDATE_STORAGE: this.procedure.broadcast((data, { context, doc, room }) => {
870
+ var _a;
871
+ const origin = data == null ? void 0 : data.origin;
872
+ const update = (_a = data == null ? void 0 : data.update) != null ? _a : null;
873
+ if (origin === "$INITIALIZED" && Object.keys(doc.toJson()).length) {
874
+ return {};
875
+ }
876
+ const updated = update === null ? doc : doc.applyEncodedState({ update });
877
+ const encodedState = updated.getEncodedState();
878
+ this._platform.persistance.setStorageState(room, encodedState).then(() => {
879
+ this._listeners.onStorageUpdated(__spreadProps(__spreadValues({}, context), {
880
+ encodedState,
881
+ room
882
+ }));
883
+ });
884
+ return { $STORAGE_UPDATED: { state: encodedState } };
885
+ })
886
+ });
887
+ this._authorize = null;
888
+ this._context = {};
889
+ this._getInitialStorage = null;
890
+ this._version = version;
891
+ const {
945
892
  authorize: authorize2,
946
893
  context,
947
- crdt,
948
- debug,
949
- events,
894
+ crdt = noop3,
895
+ debug = false,
950
896
  getInitialStorage,
951
897
  onRoomDeleted,
952
898
  onStorageUpdated,
953
899
  platform
954
- });
900
+ } = options;
901
+ this._crdt = crdt;
902
+ this._debug = debug;
903
+ this._platform = platform;
904
+ if (authorize2) this._authorize = authorize2;
905
+ if (context) this._context = context;
906
+ if (getInitialStorage) this._getInitialStorage = getInitialStorage;
907
+ this._listeners = {
908
+ onRoomDeleted: (event) => onRoomDeleted == null ? void 0 : onRoomDeleted(event),
909
+ onStorageUpdated: (event) => onStorageUpdated == null ? void 0 : onStorageUpdated(event)
910
+ };
955
911
  }
956
- _logDebug(...data) {
957
- this._debug && console.log(...data);
912
+ get _events() {
913
+ return this._router._events;
958
914
  }
959
- _purgeEmptyRooms() {
960
- const rooms = Array.from(this._rooms.entries());
961
- rooms.forEach(([id, room]) => {
962
- if (room.getSize())
963
- return;
964
- this._rooms.delete(id);
915
+ get procedure() {
916
+ return new PluvProcedure();
917
+ }
918
+ createToken(params) {
919
+ return __async(this, null, function* () {
920
+ if (!this._authorize) {
921
+ throw new Error("IO does not specify authorize during initialization.");
922
+ }
923
+ const ioAuthorize = typeof this._authorize === "function" ? this._authorize(params) : this._authorize;
924
+ const secret = ioAuthorize.secret;
925
+ return yield authorize({
926
+ platform: this._platform,
927
+ secret
928
+ }).encode(params);
965
929
  });
966
930
  }
931
+ mergeRouters(...routers) {
932
+ return PluvRouter.merge(...routers);
933
+ }
934
+ router(events) {
935
+ return new PluvRouter(events);
936
+ }
937
+ server(config = {}) {
938
+ var _a;
939
+ const router = config.router ? this.mergeRouters(this._router, config.router) : this._router;
940
+ return new PluvServer(__spreadProps(__spreadValues({}, this._listeners), {
941
+ authorize: (_a = this._authorize) != null ? _a : void 0,
942
+ context: this._context,
943
+ crdt: this._crdt,
944
+ debug: this._debug,
945
+ platform: this._platform,
946
+ router
947
+ }));
948
+ }
967
949
  };
968
950
 
969
951
  // src/createIO.ts
970
952
  var createIO = (params) => {
971
- const {
972
- authorize: authorize2,
973
- context,
974
- crdt,
975
- debug,
976
- getInitialStorage,
977
- onRoomDeleted,
978
- onStorageUpdated,
979
- platform
980
- } = params;
953
+ const { authorize: authorize2, context, crdt, debug, getInitialStorage, onRoomDeleted, onStorageUpdated, platform } = params;
981
954
  return new PluvIO({
982
955
  authorize: authorize2,
983
956
  context,
@@ -995,5 +968,8 @@ export {
995
968
  AbstractPubSub,
996
969
  AbstractWebSocket,
997
970
  PluvIO,
971
+ PluvProcedure,
972
+ PluvRouter,
973
+ PluvServer,
998
974
  createIO
999
975
  };