@pluv/io 0.0.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 ADDED
@@ -0,0 +1,857 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __objRest = (source, exclude) => {
21
+ var target = {};
22
+ for (var prop in source)
23
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
24
+ target[prop] = source[prop];
25
+ if (source != null && __getOwnPropSymbols)
26
+ for (var prop of __getOwnPropSymbols(source)) {
27
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
28
+ target[prop] = source[prop];
29
+ }
30
+ return target;
31
+ };
32
+ var __async = (__this, __arguments, generator) => {
33
+ return new Promise((resolve, reject) => {
34
+ var fulfilled = (value) => {
35
+ try {
36
+ step(generator.next(value));
37
+ } catch (e) {
38
+ reject(e);
39
+ }
40
+ };
41
+ var rejected = (value) => {
42
+ try {
43
+ step(generator.throw(value));
44
+ } catch (e) {
45
+ reject(e);
46
+ }
47
+ };
48
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
49
+ step((generator = generator.apply(__this, __arguments)).next());
50
+ });
51
+ };
52
+
53
+ // src/AbstractPersistance.ts
54
+ var AbstractPersistance = class {
55
+ };
56
+
57
+ // src/Persistance.ts
58
+ var Persistance = class extends AbstractPersistance {
59
+ constructor() {
60
+ super(...arguments);
61
+ this._storages = /* @__PURE__ */ new Map();
62
+ this._users = /* @__PURE__ */ new Map();
63
+ }
64
+ addUser(room, connectionId, user) {
65
+ var _a;
66
+ const users = (_a = this._users.get(room)) != null ? _a : /* @__PURE__ */ new Map();
67
+ this._users.set(room, users);
68
+ users.set(connectionId, user);
69
+ return Promise.resolve();
70
+ }
71
+ deleteStorageState(room) {
72
+ this._storages.delete(room);
73
+ return Promise.resolve();
74
+ }
75
+ deleteUser(room, connectionId) {
76
+ return __async(this, null, function* () {
77
+ const users = this._users.get(room);
78
+ if (!users)
79
+ return;
80
+ users.delete(connectionId);
81
+ if (!users.size) {
82
+ yield this.deleteUsers(room);
83
+ yield this.deleteStorageState(room);
84
+ }
85
+ return Promise.resolve();
86
+ });
87
+ }
88
+ deleteUsers(room) {
89
+ this._users.delete(room);
90
+ return Promise.resolve();
91
+ }
92
+ getStorageState(room) {
93
+ var _a;
94
+ const storage = (_a = this._storages.get(room)) != null ? _a : null;
95
+ return Promise.resolve(storage);
96
+ }
97
+ getUser(room, connectionId) {
98
+ var _a;
99
+ const user = (_a = this._users.get(room)) == null ? void 0 : _a.get(connectionId);
100
+ return Promise.resolve(user != null ? user : null);
101
+ }
102
+ getUsers(room) {
103
+ const users = this._users.get(room);
104
+ if (!users)
105
+ return Promise.resolve([]);
106
+ return Promise.resolve(Array.from(users.values()));
107
+ }
108
+ setStorageState(room, state) {
109
+ this._storages.set(room, state);
110
+ return Promise.resolve();
111
+ }
112
+ };
113
+
114
+ // src/PubSub.ts
115
+ import { makeSubject, subscribe } from "wonka";
116
+
117
+ // src/AbstractPubSub.ts
118
+ var AbstractPubSub = class {
119
+ };
120
+
121
+ // src/PubSub.ts
122
+ var PubSub = class extends AbstractPubSub {
123
+ constructor() {
124
+ super(...arguments);
125
+ this._currentSubscriptionId = 0;
126
+ this._subjects = /* @__PURE__ */ new Map();
127
+ this._subscriptionsRefs = /* @__PURE__ */ new Map();
128
+ this._unsubscriptions = /* @__PURE__ */ new Map();
129
+ }
130
+ publish(roomName, payload) {
131
+ return __async(this, null, function* () {
132
+ const subject = this._subjects.get(roomName);
133
+ if (!subject)
134
+ return;
135
+ Promise.resolve(subject.next(payload));
136
+ });
137
+ }
138
+ subscribe(roomName, onMessage) {
139
+ var _a;
140
+ const id = this._currentSubscriptionId++;
141
+ const refs = (_a = this._subscriptionsRefs.get(roomName)) != null ? _a : [];
142
+ const subject = makeSubject();
143
+ this._subjects.set(roomName, subject);
144
+ this._subscriptionsRefs.set(roomName, [...refs, id]);
145
+ const unsubscribe = subscribe(onMessage)(subject.source).unsubscribe;
146
+ this._unsubscriptions.set(id, [roomName, unsubscribe]);
147
+ return Promise.resolve(id);
148
+ }
149
+ unsubscribe(subscriptionId) {
150
+ var _a;
151
+ const [roomName, unsubscribe] = (_a = this._unsubscriptions.get(subscriptionId)) != null ? _a : [];
152
+ if (!roomName || !unsubscribe)
153
+ return;
154
+ const refs = this._subscriptionsRefs.get(roomName);
155
+ if (!refs)
156
+ return;
157
+ if (refs.length === 1) {
158
+ this._subjects.delete(roomName);
159
+ this._subscriptionsRefs.delete(roomName);
160
+ this._unsubscriptions.delete(subscriptionId);
161
+ return;
162
+ }
163
+ const index = refs.indexOf(subscriptionId);
164
+ this._subscriptionsRefs.set(
165
+ roomName,
166
+ index === -1 ? refs : [...refs.slice(0, index), ...refs.slice(index + 1)]
167
+ );
168
+ unsubscribe();
169
+ this._unsubscriptions.delete(subscriptionId);
170
+ }
171
+ };
172
+
173
+ // src/AbstractPlatform.ts
174
+ var AbstractPlatform = class {
175
+ constructor(config = {}) {
176
+ const { persistance, pubSub } = config;
177
+ this.persistance = persistance != null ? persistance : new Persistance();
178
+ this.pubSub = pubSub != null ? pubSub : new PubSub();
179
+ }
180
+ };
181
+
182
+ // src/AbstractWebSocket.ts
183
+ var AbstractWebSocket = class {
184
+ constructor(config) {
185
+ this.CONNECTING = 0;
186
+ this.OPEN = 1;
187
+ this.CLOSING = 2;
188
+ this.CLOSED = 3;
189
+ const { room } = config;
190
+ this.room = room;
191
+ }
192
+ handleError(params) {
193
+ var _a, _b, _c;
194
+ const {
195
+ error,
196
+ message = error instanceof Error ? error.message : "Unexpected error",
197
+ session
198
+ } = params;
199
+ const stack = error instanceof Error ? (_a = error.stack) != null ? _a : null : null;
200
+ return this.sendMessage({
201
+ connectionId: (_b = session == null ? void 0 : session.id) != null ? _b : null,
202
+ data: { message, stack },
203
+ room: this.room,
204
+ type: "$ERROR",
205
+ user: (_c = session == null ? void 0 : session.user) != null ? _c : null
206
+ });
207
+ }
208
+ sendMessage(data) {
209
+ return this.send(JSON.stringify(data));
210
+ }
211
+ };
212
+
213
+ // src/PluvIO.ts
214
+ import chalk2 from "chalk";
215
+
216
+ // src/authorize.ts
217
+ import hkdf from "@panva/hkdf";
218
+ import { EncryptJWT, jwtDecrypt } from "jose";
219
+ var DEFAULT_MAX_AGE_MS = 6e4;
220
+ var getEncryptionKey = (secret) => __async(void 0, null, function* () {
221
+ return yield hkdf(
222
+ "sha256",
223
+ secret,
224
+ "",
225
+ "Pluv.io Generated Encryption Key",
226
+ 32
227
+ );
228
+ });
229
+ var now = () => Date.now() / 1e3 | 0;
230
+ var authorize = (params) => {
231
+ const { platform, secret } = params;
232
+ const encode = (encodeParams) => __async(void 0, null, function* () {
233
+ const { maxAge = DEFAULT_MAX_AGE_MS, room, user } = encodeParams;
234
+ const encryptionSecret = yield getEncryptionKey(secret);
235
+ const token = yield new EncryptJWT({
236
+ room,
237
+ sub: room ? `${room}|${user.id}` : user.id,
238
+ user
239
+ }).setProtectedHeader({ alg: "dir", enc: "A256GCM" }).setIssuedAt().setExpirationTime(now() + maxAge).setJti(platform.randomUUID()).encrypt(encryptionSecret);
240
+ return token.toString();
241
+ });
242
+ const decode = (jwt) => __async(void 0, null, function* () {
243
+ const encryptionSecret = yield getEncryptionKey(secret);
244
+ const { payload } = yield jwtDecrypt(jwt, encryptionSecret, {
245
+ clockTolerance: 15
246
+ });
247
+ return payload != null ? payload : null;
248
+ });
249
+ return { decode, encode };
250
+ };
251
+
252
+ // src/IORoom.ts
253
+ import { doc } from "@pluv/crdt-yjs";
254
+ import chalk from "chalk";
255
+ var PING_TIMEOUT_MS = 3e4;
256
+ var IORoom = class {
257
+ constructor(room, config) {
258
+ this._doc = doc();
259
+ this._sessions = /* @__PURE__ */ new Map();
260
+ this._uninitialize = null;
261
+ this._authorize = null;
262
+ const { authorize: authorize2, context, debug, events, onDestroy, platform } = config;
263
+ this._context = context;
264
+ this._debug = debug;
265
+ this._events = events;
266
+ this._platform = platform;
267
+ this.room = room;
268
+ this._listeners = {
269
+ onDestroy: () => onDestroy == null ? void 0 : onDestroy()
270
+ };
271
+ if (authorize2)
272
+ this._authorize = authorize2;
273
+ }
274
+ broadcast(message) {
275
+ return Promise.resolve(this._broadcast({ message }));
276
+ }
277
+ register(webSocket, options) {
278
+ return __async(this, null, function* () {
279
+ var _a;
280
+ if (!this._uninitialize)
281
+ yield this._initialize();
282
+ const token = options == null ? void 0 : options.token;
283
+ const sessionId = this._platform.randomUUID();
284
+ const pluvWs = this._platform.convertWebSocket(webSocket, {
285
+ room: this.room
286
+ });
287
+ this._logDebug(
288
+ `${chalk.blue(
289
+ `Registering connection for room ${this.room}:`
290
+ )} ${sessionId}`
291
+ );
292
+ const uninitializeWs = yield pluvWs.initialize();
293
+ const user = yield this._getAuthorizedUser(token);
294
+ const isUnauthorized = !!((_a = this._authorize) == null ? void 0 : _a.required) && !user;
295
+ const isTokenInUse = !!user && Array.from(this._sessions.values()).some(
296
+ (session2) => {
297
+ var _a2;
298
+ return ((_a2 = session2.user) == null ? void 0 : _a2.id) === user.id;
299
+ }
300
+ );
301
+ if (isUnauthorized || isTokenInUse) {
302
+ this._logDebug(
303
+ `${chalk.blue(
304
+ "Authorization failed for connection:"
305
+ )} ${sessionId}`
306
+ );
307
+ pluvWs.handleError({ error: new Error("Not authorized") });
308
+ pluvWs.close(3e3, "WebSocket unauthorized.");
309
+ return;
310
+ }
311
+ const currentTime = new Date().getTime();
312
+ const session = {
313
+ id: sessionId,
314
+ presence: null,
315
+ quit: false,
316
+ room: this.room,
317
+ timers: {
318
+ ping: currentTime
319
+ },
320
+ webSocket: pluvWs,
321
+ user
322
+ };
323
+ this._sessions.set(session.id, session);
324
+ yield this._platform.persistance.addUser(
325
+ this.room,
326
+ sessionId,
327
+ user != null ? user : {}
328
+ );
329
+ this._emitRegistered(session);
330
+ this._emitStorageReceived(session);
331
+ this._emitUserJoined(session);
332
+ const context = {
333
+ context: this._context,
334
+ doc: this._doc,
335
+ room: this.room,
336
+ session,
337
+ sessions: this._sessions
338
+ };
339
+ pluvWs.addEventListener("message", (event) => {
340
+ if (!this._uninitialize)
341
+ return;
342
+ if (session.quit) {
343
+ session.webSocket.close(1011, "WebSocket broken.");
344
+ return;
345
+ }
346
+ const message = this._parseMessage(event);
347
+ if (!message)
348
+ return;
349
+ const eventConfig = this._getEventConfig(message);
350
+ if (!eventConfig)
351
+ return;
352
+ this._resolveMessage(message, context).then((output) => {
353
+ var _a2, _b;
354
+ if (!output)
355
+ return;
356
+ const eventType = (_b = (_a2 = eventConfig.options) == null ? void 0 : _a2.type) != null ? _b : "broadcast";
357
+ switch (eventType) {
358
+ case "self": {
359
+ Object.entries(output).forEach(([type, data]) => {
360
+ this._sendSelfMessage({ data, type }, session);
361
+ });
362
+ return;
363
+ }
364
+ case "sync": {
365
+ this._platform.pubSub.publish(this.room, __spreadValues({
366
+ connectionId: session.id,
367
+ options: eventConfig.options,
368
+ room: this.room,
369
+ user: session.user
370
+ }, message));
371
+ Object.entries(output).forEach(([type, data]) => {
372
+ this._sendSelfMessage({ data, type }, session);
373
+ });
374
+ return;
375
+ }
376
+ case "broadcast":
377
+ default: {
378
+ Object.entries(output).forEach(([type, data]) => {
379
+ this._broadcast({
380
+ message: { data, type },
381
+ senderId: session.id
382
+ });
383
+ });
384
+ }
385
+ }
386
+ });
387
+ });
388
+ const closeHandler = () => {
389
+ if (!this._uninitialize)
390
+ return;
391
+ session.quit = true;
392
+ this._logDebug(
393
+ `${chalk.blue(
394
+ `(Unregistering connection for room ${this.room}:`
395
+ )} ${sessionId}`
396
+ );
397
+ this._sessions.delete(session.id);
398
+ this._platform.persistance.deleteUser(this.room, session.id);
399
+ this._broadcast({
400
+ message: {
401
+ type: "$EXIT",
402
+ data: { sessionId: session.id }
403
+ },
404
+ senderId: session.id
405
+ });
406
+ uninitializeWs();
407
+ const size2 = this._getSessionsSize();
408
+ this._logDebug(
409
+ `${chalk.blue(
410
+ `Unregistered connection for room ${this.room}:`
411
+ )} ${sessionId}`
412
+ );
413
+ this._logDebug(`${chalk.blue(`Room ${this.room} size:`)} ${size2}`);
414
+ if (size2)
415
+ return;
416
+ this._uninitialize();
417
+ };
418
+ pluvWs.addEventListener("close", closeHandler);
419
+ pluvWs.addEventListener("error", closeHandler);
420
+ this._logDebug(
421
+ `${chalk.blue(
422
+ `Registered connection for room ${this.room}:`
423
+ )} ${sessionId}`
424
+ );
425
+ const size = this._getSessionsSize();
426
+ this._logDebug(`${chalk.blue(`Room ${this.room} size:`)} ${size}`);
427
+ });
428
+ }
429
+ _broadcast(params) {
430
+ var _a;
431
+ const { message, senderId } = params;
432
+ const currentTime = new Date().getTime();
433
+ const sender = senderId ? this._sessions.get(senderId) : null;
434
+ const quitters = Array.from(this._sessions.values()).filter(
435
+ (session) => session.quit || currentTime - session.timers.ping > PING_TIMEOUT_MS
436
+ );
437
+ quitters.forEach((session) => {
438
+ this._sessions.delete(session.id);
439
+ });
440
+ this._emitQuitters(quitters);
441
+ this._platform.pubSub.publish(this.room, __spreadValues({
442
+ connectionId: senderId != null ? senderId : null,
443
+ room: this.room,
444
+ user: (_a = sender == null ? void 0 : sender.user) != null ? _a : null
445
+ }, message));
446
+ }
447
+ _emitQuitters(quitters) {
448
+ quitters.forEach((quitter) => {
449
+ this._broadcast({
450
+ message: {
451
+ type: "$EXIT",
452
+ data: { sessionId: quitter.id }
453
+ },
454
+ senderId: quitter.id
455
+ });
456
+ });
457
+ }
458
+ _emitRegistered(session) {
459
+ this._sendSelfMessage(
460
+ {
461
+ type: "$REGISTERED",
462
+ data: { sessionId: session.id }
463
+ },
464
+ session
465
+ );
466
+ }
467
+ _emitStorageReceived(session) {
468
+ this._platform.persistance.getStorageState(this.room).then((_state) => {
469
+ const state = _state != null ? _state : this._doc.encodeStateAsUpdate();
470
+ this._sendSelfMessage(
471
+ {
472
+ type: "$STORAGE_RECEIVED",
473
+ data: { state }
474
+ },
475
+ session
476
+ );
477
+ });
478
+ }
479
+ _emitUserJoined(session) {
480
+ const user = session.user;
481
+ if (!user)
482
+ return;
483
+ this._broadcast({
484
+ message: {
485
+ type: "$USER_JOINED",
486
+ data: {
487
+ connectionId: session.id,
488
+ user
489
+ }
490
+ },
491
+ senderId: session.id
492
+ });
493
+ }
494
+ _getAuthorizedUser(token) {
495
+ return __async(this, null, function* () {
496
+ var _a;
497
+ if (!this._authorize)
498
+ return null;
499
+ if (!token)
500
+ return null;
501
+ const payload = yield authorize({
502
+ platform: this._platform,
503
+ secret: this._authorize.secret
504
+ }).decode(token);
505
+ if (!payload) {
506
+ this._logDebug(chalk.blue("Could not decode token:"));
507
+ this._logDebug(token);
508
+ return null;
509
+ }
510
+ if (payload.room !== this.room) {
511
+ this._logDebug(
512
+ chalk.blue(`Token is not authorized for room ${this.room}:`)
513
+ );
514
+ this._logDebug(chalk.blue("Received:"), payload.room);
515
+ this._logDebug(token);
516
+ return null;
517
+ }
518
+ try {
519
+ return (_a = this._authorize.user.parse(payload.user)) != null ? _a : null;
520
+ } catch (e) {
521
+ this._logDebug(`${chalk.blue("Token fails validation:")} ${token}`);
522
+ return null;
523
+ }
524
+ });
525
+ }
526
+ _getSessionsSize() {
527
+ const currentTime = new Date().getTime();
528
+ return Array.from(this._sessions.values()).reduce((count, session) => {
529
+ if (session.quit)
530
+ return count;
531
+ return currentTime - session.timers.ping > PING_TIMEOUT_MS ? count : count + 1;
532
+ }, 0);
533
+ }
534
+ _getEventConfig(message) {
535
+ var _a;
536
+ return (_a = this._events[message.type]) != null ? _a : null;
537
+ }
538
+ _initialize() {
539
+ return __async(this, null, function* () {
540
+ this._logDebug(`${chalk.blue(`Initializing room ${this.room}:`)}`);
541
+ if (!!this._uninitialize) {
542
+ yield this._uninitialize();
543
+ this._uninitialize = null;
544
+ }
545
+ const pubSubId = yield this._platform.pubSub.subscribe(
546
+ this.room,
547
+ (_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
+ }
555
+ );
556
+ this._uninitialize = () => __async(this, null, function* () {
557
+ this._platform.pubSub.unsubscribe(pubSubId);
558
+ this._doc.destroy();
559
+ this._doc = doc();
560
+ this._listeners.onDestroy();
561
+ });
562
+ });
563
+ }
564
+ _logDebug(...data) {
565
+ this._debug && console.log(...data);
566
+ }
567
+ _parseMessage(message) {
568
+ const parsed = this._platform.parseData(message.data);
569
+ try {
570
+ if (typeof parsed !== "object")
571
+ return null;
572
+ if (typeof parsed.type !== "string")
573
+ return null;
574
+ if (typeof parsed.data !== "object")
575
+ return null;
576
+ return parsed;
577
+ } catch (e) {
578
+ return null;
579
+ }
580
+ }
581
+ _resolveMessage(message, context) {
582
+ return __async(this, null, function* () {
583
+ const { session } = context;
584
+ const eventConfig = this._getEventConfig(message);
585
+ if (!eventConfig)
586
+ return null;
587
+ let input;
588
+ try {
589
+ input = eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
590
+ } catch (error) {
591
+ session == null ? void 0 : session.webSocket.handleError({
592
+ error,
593
+ message: "Invalid input",
594
+ session
595
+ });
596
+ return null;
597
+ }
598
+ const output = yield eventConfig.resolver(input, context);
599
+ if (!output)
600
+ return null;
601
+ return output;
602
+ });
603
+ }
604
+ _sendMessage(message, sender, options = {}) {
605
+ switch (options.type) {
606
+ case "self": {
607
+ this._sendSelfMessage(message, sender);
608
+ return;
609
+ }
610
+ case "sync": {
611
+ this._sendSyncMessage(message, sender);
612
+ return;
613
+ }
614
+ case "broadcast":
615
+ default: {
616
+ const sessionIds = options.sessionIds;
617
+ this._sendBroadcastMessage(message, sender, sessionIds);
618
+ }
619
+ }
620
+ }
621
+ _sendBroadcastMessage(message, sender, sessionIds) {
622
+ var _a, _b;
623
+ const senderId = (_a = sender == null ? void 0 : sender.id) != null ? _a : null;
624
+ const sessions = (_b = sessionIds == null ? void 0 : sessionIds.reduce((dict, id) => {
625
+ const _session = this._sessions.get(id);
626
+ return _session ? dict.set(id, _session) : dict;
627
+ }, /* @__PURE__ */ new Map())) != null ? _b : this._sessions;
628
+ Array.from(sessions.values()).forEach((_session) => {
629
+ _session.webSocket.sendMessage(__spreadValues({
630
+ connectionId: senderId,
631
+ room: this.room,
632
+ user: _session.user
633
+ }, message));
634
+ });
635
+ }
636
+ _sendSelfMessage(message, sender) {
637
+ const senderId = sender == null ? void 0 : sender.id;
638
+ if (!senderId)
639
+ return;
640
+ const session = this._sessions.get(senderId);
641
+ if (!session)
642
+ return;
643
+ session.webSocket.sendMessage(__spreadValues({
644
+ connectionId: sender.id,
645
+ room: this.room,
646
+ user: sender.user
647
+ }, message));
648
+ }
649
+ _sendSyncMessage(message, sender) {
650
+ const senderId = sender == null ? void 0 : sender.id;
651
+ if (!senderId)
652
+ return;
653
+ this._resolveMessage(message, {
654
+ context: this._context,
655
+ doc: this._doc,
656
+ room: this.room,
657
+ session: null,
658
+ sessions: this._sessions
659
+ }).then((output) => {
660
+ if (!output)
661
+ return;
662
+ Object.keys(output).forEach((type) => {
663
+ var _a, _b;
664
+ const data = (_a = output[type]) != null ? _a : {};
665
+ this._platform.pubSub.publish(this.room, {
666
+ connectionId: senderId,
667
+ data,
668
+ options: { type: "self" },
669
+ room: this.room,
670
+ type,
671
+ user: (_b = sender.user) != null ? _b : null
672
+ });
673
+ });
674
+ });
675
+ }
676
+ };
677
+
678
+ // src/PluvIO.ts
679
+ var PluvIO = class {
680
+ constructor(options) {
681
+ this._authorize = null;
682
+ this._events = {
683
+ $PING: {
684
+ resolver: (data, { session }) => {
685
+ if (!session)
686
+ return {};
687
+ const currentTime = new Date().getTime();
688
+ session.timers.ping = currentTime;
689
+ return { $PONG: {} };
690
+ },
691
+ options: { type: "self" }
692
+ },
693
+ $GET_OTHERS: {
694
+ resolver: (data, { session, sessions }) => {
695
+ const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
696
+ (acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
697
+ [connectionId]: {
698
+ connectionId,
699
+ presence,
700
+ room: this.room,
701
+ user
702
+ }
703
+ }),
704
+ {}
705
+ );
706
+ return { $OTHERS_RECEIVED: { others } };
707
+ },
708
+ options: { type: "sync" }
709
+ },
710
+ $UPDATE_PRESENCE: {
711
+ resolver: ({ presence }, { session }) => {
712
+ if (!session)
713
+ return {};
714
+ session.presence = Object.assign(
715
+ /* @__PURE__ */ Object.create(null),
716
+ session.presence,
717
+ presence
718
+ );
719
+ return { $PRESENCE_UPDATED: { presence } };
720
+ }
721
+ },
722
+ $UPDATE_STORAGE: {
723
+ resolver: ({ origin, update }, { doc: doc2, room }) => {
724
+ if (origin === "$STORAGE_RECEIVED" && Object.keys(doc2.toJSON()).length) {
725
+ return;
726
+ }
727
+ const updated = doc2.applyUpdate(update);
728
+ const state = updated.encodeStateAsUpdate();
729
+ this._platform.persistance.setStorageState(room, state);
730
+ return { $STORAGE_UPDATED: { state } };
731
+ }
732
+ }
733
+ };
734
+ this.room = null;
735
+ this._context = {};
736
+ this._rooms = /* @__PURE__ */ new Map();
737
+ const {
738
+ authorize: authorize2,
739
+ context,
740
+ debug = false,
741
+ events,
742
+ platform,
743
+ room
744
+ } = options;
745
+ this._debug = debug;
746
+ this._platform = platform;
747
+ if (authorize2)
748
+ this._authorize = authorize2;
749
+ if (context)
750
+ this._context = context;
751
+ if (events)
752
+ this._events = events;
753
+ if (room)
754
+ this.room = room;
755
+ }
756
+ createToken(params) {
757
+ return __async(this, null, function* () {
758
+ if (!this._authorize) {
759
+ throw new Error(
760
+ "IO does not specify authorize during initialization."
761
+ );
762
+ }
763
+ return yield authorize({
764
+ platform: this._platform,
765
+ secret: this._authorize.secret
766
+ }).encode(params);
767
+ });
768
+ }
769
+ event(event, config) {
770
+ if (event.startsWith("$")) {
771
+ throw new Error('Event name must not start with "$".');
772
+ }
773
+ const newEvent = {
774
+ [event]: config
775
+ };
776
+ return PluvIO.merge(
777
+ this,
778
+ new PluvIO({
779
+ events: newEvent,
780
+ platform: this._platform
781
+ })
782
+ );
783
+ }
784
+ getRoom(room) {
785
+ var _a;
786
+ if (this.room) {
787
+ throw new Error("IO cannot getRoom from within a room.");
788
+ }
789
+ if (!/^[a-z0-9]+[a-z0-9\-_]+[a-z0-9]+$/i.test(room)) {
790
+ throw new Error("Unsupported room name");
791
+ }
792
+ const platform = this._platform;
793
+ const oldRoom = this._rooms.get(room);
794
+ if (oldRoom)
795
+ return oldRoom;
796
+ const newRoom = new IORoom(room, {
797
+ authorize: (_a = this._authorize) != null ? _a : void 0,
798
+ context: this._context,
799
+ debug: this._debug,
800
+ events: this._events,
801
+ onDestroy: () => {
802
+ this._logDebug(`${chalk2.blue("Deleting empty room:")} ${room}`);
803
+ this._rooms.delete(room);
804
+ if (this._debug) {
805
+ const rooms = Array.from(this._rooms.keys());
806
+ this._logDebug(`${chalk2.blue("Deleted room:")} ${room}`);
807
+ this._logDebug(
808
+ `${chalk2.blue("Rooms available:")} ${rooms.join(", ")}`
809
+ );
810
+ }
811
+ },
812
+ platform
813
+ });
814
+ this._rooms.set(room, newRoom);
815
+ if (this._debug) {
816
+ const rooms = Array.from(this._rooms.keys());
817
+ this._logDebug(`${chalk2.blue("Created room:")} ${room}`);
818
+ this._logDebug(
819
+ `${chalk2.blue("Rooms available:")} ${rooms.join(", ")}`
820
+ );
821
+ }
822
+ return newRoom;
823
+ }
824
+ static merge(first, second) {
825
+ var _a, _b;
826
+ const authorize2 = (_a = first._authorize) != null ? _a : void 0;
827
+ const debug = first._debug;
828
+ const platform = first._platform;
829
+ const room = (_b = first.room) != null ? _b : void 0;
830
+ const events1 = first._events;
831
+ const events2 = second._events;
832
+ const events = Object.assign(/* @__PURE__ */ Object.create(null), events1, events2);
833
+ return new PluvIO({
834
+ authorize: authorize2,
835
+ debug,
836
+ events,
837
+ platform,
838
+ room
839
+ });
840
+ }
841
+ _logDebug(...data) {
842
+ this._debug && console.log(...data);
843
+ }
844
+ };
845
+
846
+ // src/createIO.ts
847
+ var createIO = (params) => {
848
+ const { authorize: authorize2, context, debug, platform } = params;
849
+ return new PluvIO({ authorize: authorize2, context, debug, platform });
850
+ };
851
+ export {
852
+ AbstractPersistance,
853
+ AbstractPlatform,
854
+ AbstractPubSub,
855
+ AbstractWebSocket,
856
+ createIO
857
+ };