@pluv/io 0.3.9 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +28 -0
- package/dist/index.d.ts +33 -30
- package/dist/index.js +196 -189
- package/dist/index.mjs +196 -189
- package/package.json +9 -9
- package/src/IORoom.ts +242 -212
- package/src/PluvIO.ts +191 -61
- package/src/types.ts +38 -7
package/dist/index.js
CHANGED
|
@@ -261,6 +261,10 @@ var AbstractWebSocket = class {
|
|
|
261
261
|
// src/PluvIO.ts
|
|
262
262
|
var import_kleur2 = __toESM(require("kleur"));
|
|
263
263
|
|
|
264
|
+
// src/IORoom.ts
|
|
265
|
+
var import_crdt_yjs = require("@pluv/crdt-yjs");
|
|
266
|
+
var import_kleur = __toESM(require("kleur"));
|
|
267
|
+
|
|
264
268
|
// src/authorize.ts
|
|
265
269
|
var import_hkdf = __toESM(require("@panva/hkdf"));
|
|
266
270
|
var import_jose = require("jose");
|
|
@@ -298,25 +302,14 @@ var authorize = (params) => {
|
|
|
298
302
|
};
|
|
299
303
|
|
|
300
304
|
// src/IORoom.ts
|
|
301
|
-
var import_crdt_yjs = require("@pluv/crdt-yjs");
|
|
302
|
-
var import_kleur = __toESM(require("kleur"));
|
|
303
305
|
var PING_TIMEOUT_MS = 3e4;
|
|
304
306
|
var IORoom = class {
|
|
305
307
|
constructor(id, config) {
|
|
306
308
|
this._doc = (0, import_crdt_yjs.doc)();
|
|
307
|
-
this._initialStorage = null;
|
|
308
309
|
this._sessions = /* @__PURE__ */ new Map();
|
|
309
310
|
this._uninitialize = null;
|
|
310
311
|
this._authorize = null;
|
|
311
|
-
const {
|
|
312
|
-
authorize: authorize2,
|
|
313
|
-
context,
|
|
314
|
-
debug,
|
|
315
|
-
events,
|
|
316
|
-
initialStorage,
|
|
317
|
-
onDestroy,
|
|
318
|
-
platform
|
|
319
|
-
} = config;
|
|
312
|
+
const { authorize: authorize2, context, debug, events, onDestroy, platform } = config;
|
|
320
313
|
this._context = context;
|
|
321
314
|
this._debug = debug;
|
|
322
315
|
this._events = events;
|
|
@@ -329,8 +322,6 @@ var IORoom = class {
|
|
|
329
322
|
};
|
|
330
323
|
if (authorize2)
|
|
331
324
|
this._authorize = authorize2;
|
|
332
|
-
if (initialStorage)
|
|
333
|
-
this._initialStorage = initialStorage;
|
|
334
325
|
}
|
|
335
326
|
broadcast(event, data) {
|
|
336
327
|
const message = { type: event, data };
|
|
@@ -396,101 +387,12 @@ var IORoom = class {
|
|
|
396
387
|
sessionId,
|
|
397
388
|
user != null ? user : {}
|
|
398
389
|
);
|
|
390
|
+
const onClose = this._onClose(session, uninitializeWs).bind(this);
|
|
391
|
+
const onMessage = this._onMessage(session).bind(this);
|
|
392
|
+
pluvWs.addEventListener("close", onClose);
|
|
393
|
+
pluvWs.addEventListener("error", onClose);
|
|
394
|
+
pluvWs.addEventListener("message", onMessage);
|
|
399
395
|
this._emitRegistered(session);
|
|
400
|
-
this._emitStorageReceived(session);
|
|
401
|
-
this._emitUserJoined(session);
|
|
402
|
-
const context = {
|
|
403
|
-
context: this._context,
|
|
404
|
-
doc: this._doc,
|
|
405
|
-
room: this.id,
|
|
406
|
-
session,
|
|
407
|
-
sessions: this._sessions
|
|
408
|
-
};
|
|
409
|
-
pluvWs.addEventListener("message", (event) => {
|
|
410
|
-
if (!this._uninitialize)
|
|
411
|
-
return;
|
|
412
|
-
if (session.quit) {
|
|
413
|
-
session.webSocket.close(1011, "WebSocket broken.");
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
const message = this._parseMessage(event);
|
|
417
|
-
if (!message)
|
|
418
|
-
return;
|
|
419
|
-
const eventConfig = this._getEventConfig(message);
|
|
420
|
-
if (!eventConfig)
|
|
421
|
-
return;
|
|
422
|
-
this._resolveMessage(message, context).then((output) => {
|
|
423
|
-
var _a2, _b;
|
|
424
|
-
if (!output)
|
|
425
|
-
return;
|
|
426
|
-
const eventType = (_b = (_a2 = eventConfig.options) == null ? void 0 : _a2.type) != null ? _b : "broadcast";
|
|
427
|
-
switch (eventType) {
|
|
428
|
-
case "self": {
|
|
429
|
-
Object.entries(output).forEach(([type, data]) => {
|
|
430
|
-
this._sendSelfMessage({ data, type }, session);
|
|
431
|
-
});
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
case "sync": {
|
|
435
|
-
this._platform.pubSub.publish(this.id, __spreadValues({
|
|
436
|
-
connectionId: session.id,
|
|
437
|
-
options: eventConfig.options,
|
|
438
|
-
room: this.id,
|
|
439
|
-
user: session.user
|
|
440
|
-
}, message));
|
|
441
|
-
Object.entries(output).forEach(([type, data]) => {
|
|
442
|
-
this._sendSelfMessage({ data, type }, session);
|
|
443
|
-
});
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
case "broadcast":
|
|
447
|
-
default: {
|
|
448
|
-
Object.entries(output).forEach(([type, data]) => {
|
|
449
|
-
this._broadcast({
|
|
450
|
-
message: { data, type },
|
|
451
|
-
senderId: session.id
|
|
452
|
-
});
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
});
|
|
457
|
-
});
|
|
458
|
-
const closeHandler = () => {
|
|
459
|
-
if (!this._uninitialize)
|
|
460
|
-
return;
|
|
461
|
-
session.quit = true;
|
|
462
|
-
this._logDebug(
|
|
463
|
-
`${import_kleur.default.blue(
|
|
464
|
-
`(Unregistering connection for room ${this.id}:`
|
|
465
|
-
)} ${sessionId}`
|
|
466
|
-
);
|
|
467
|
-
this._sessions.delete(session.id);
|
|
468
|
-
this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
|
|
469
|
-
var _a2;
|
|
470
|
-
this._broadcast({
|
|
471
|
-
message: {
|
|
472
|
-
type: "$EXIT",
|
|
473
|
-
data: { sessionId: session.id }
|
|
474
|
-
},
|
|
475
|
-
senderId: session.id
|
|
476
|
-
});
|
|
477
|
-
uninitializeWs();
|
|
478
|
-
const size2 = this.getSize();
|
|
479
|
-
this._logDebug(
|
|
480
|
-
`${import_kleur.default.blue(
|
|
481
|
-
`Unregistered connection for room ${this.id}:`
|
|
482
|
-
)} ${sessionId}`
|
|
483
|
-
);
|
|
484
|
-
this._logDebug(
|
|
485
|
-
`${import_kleur.default.blue(`Room ${this.id} size:`)} ${size2}`
|
|
486
|
-
);
|
|
487
|
-
if (size2)
|
|
488
|
-
return;
|
|
489
|
-
(_a2 = this._uninitialize) == null ? void 0 : _a2.call(this);
|
|
490
|
-
});
|
|
491
|
-
};
|
|
492
|
-
pluvWs.addEventListener("close", closeHandler);
|
|
493
|
-
pluvWs.addEventListener("error", closeHandler);
|
|
494
396
|
this._logDebug(
|
|
495
397
|
`${import_kleur.default.blue(
|
|
496
398
|
`Registered connection for room ${this.id}:`
|
|
@@ -538,34 +440,6 @@ var IORoom = class {
|
|
|
538
440
|
session
|
|
539
441
|
);
|
|
540
442
|
}
|
|
541
|
-
_emitStorageReceived(session) {
|
|
542
|
-
this._platform.persistance.getStorageState(this.id).then((_state) => __async(this, null, function* () {
|
|
543
|
-
var _a, _b;
|
|
544
|
-
const state = (_b = _state != null ? _state : yield (_a = this._initialStorage) == null ? void 0 : _a.call(this)) != null ? _b : this._doc.encodeStateAsUpdate();
|
|
545
|
-
this._sendSelfMessage(
|
|
546
|
-
{
|
|
547
|
-
type: "$STORAGE_RECEIVED",
|
|
548
|
-
data: { state }
|
|
549
|
-
},
|
|
550
|
-
session
|
|
551
|
-
);
|
|
552
|
-
}));
|
|
553
|
-
}
|
|
554
|
-
_emitUserJoined(session) {
|
|
555
|
-
const user = session.user;
|
|
556
|
-
if (!user)
|
|
557
|
-
return;
|
|
558
|
-
this._broadcast({
|
|
559
|
-
message: {
|
|
560
|
-
type: "$USER_JOINED",
|
|
561
|
-
data: {
|
|
562
|
-
connectionId: session.id,
|
|
563
|
-
user
|
|
564
|
-
}
|
|
565
|
-
},
|
|
566
|
-
senderId: session.id
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
443
|
_getAuthorizedUser(token) {
|
|
570
444
|
return __async(this, null, function* () {
|
|
571
445
|
var _a;
|
|
@@ -604,6 +478,19 @@ var IORoom = class {
|
|
|
604
478
|
var _a;
|
|
605
479
|
return (_a = this._events[message.type]) != null ? _a : null;
|
|
606
480
|
}
|
|
481
|
+
_getEventInputs(message) {
|
|
482
|
+
const eventConfig = this._getEventConfig(message);
|
|
483
|
+
if (!eventConfig)
|
|
484
|
+
return message.data;
|
|
485
|
+
return eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
|
|
486
|
+
}
|
|
487
|
+
_getEventResolverObject(message) {
|
|
488
|
+
const eventConfig = this._getEventConfig(message);
|
|
489
|
+
if (!eventConfig)
|
|
490
|
+
return {};
|
|
491
|
+
const resolver = eventConfig.resolver;
|
|
492
|
+
return typeof resolver === "function" ? { broadcast: resolver } : __spreadValues({}, resolver);
|
|
493
|
+
}
|
|
607
494
|
_initialize() {
|
|
608
495
|
return __async(this, null, function* () {
|
|
609
496
|
this._logDebug(`${import_kleur.default.blue(`Initializing room ${this.id}:`)}`);
|
|
@@ -635,6 +522,105 @@ var IORoom = class {
|
|
|
635
522
|
_logDebug(...data) {
|
|
636
523
|
this._debug && console.log(...data);
|
|
637
524
|
}
|
|
525
|
+
_onClose(session, callback) {
|
|
526
|
+
return () => {
|
|
527
|
+
if (!this._uninitialize)
|
|
528
|
+
return;
|
|
529
|
+
session.quit = true;
|
|
530
|
+
this._logDebug(
|
|
531
|
+
`${import_kleur.default.blue(
|
|
532
|
+
`(Unregistering connection for room ${this.id}:`
|
|
533
|
+
)} ${session.id}`
|
|
534
|
+
);
|
|
535
|
+
this._sessions.delete(session.id);
|
|
536
|
+
this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
|
|
537
|
+
var _a;
|
|
538
|
+
this._broadcast({
|
|
539
|
+
message: {
|
|
540
|
+
type: "$EXIT",
|
|
541
|
+
data: { sessionId: session.id }
|
|
542
|
+
},
|
|
543
|
+
senderId: session.id
|
|
544
|
+
});
|
|
545
|
+
callback == null ? void 0 : callback();
|
|
546
|
+
const size = this.getSize();
|
|
547
|
+
this._logDebug(
|
|
548
|
+
`${import_kleur.default.blue(
|
|
549
|
+
`Unregistered connection for room ${this.id}:`
|
|
550
|
+
)} ${session.id}`
|
|
551
|
+
);
|
|
552
|
+
this._logDebug(
|
|
553
|
+
`${import_kleur.default.blue(`Room ${this.id} size:`)} ${size}`
|
|
554
|
+
);
|
|
555
|
+
if (size)
|
|
556
|
+
return;
|
|
557
|
+
(_a = this._uninitialize) == null ? void 0 : _a.call(this);
|
|
558
|
+
});
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
_onMessage(session) {
|
|
562
|
+
return (event) => {
|
|
563
|
+
var _a, _b, _c;
|
|
564
|
+
const context = {
|
|
565
|
+
context: this._context,
|
|
566
|
+
doc: this._doc,
|
|
567
|
+
room: this.id,
|
|
568
|
+
session,
|
|
569
|
+
sessions: this._sessions
|
|
570
|
+
};
|
|
571
|
+
if (!this._uninitialize)
|
|
572
|
+
return;
|
|
573
|
+
if (session.quit) {
|
|
574
|
+
session.webSocket.close(1011, "WebSocket broken.");
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const message = this._parseMessage(event);
|
|
578
|
+
if (!message)
|
|
579
|
+
return;
|
|
580
|
+
const resolver = this._getEventResolverObject(message);
|
|
581
|
+
let inputs;
|
|
582
|
+
try {
|
|
583
|
+
inputs = this._getEventInputs(message);
|
|
584
|
+
} catch (error) {
|
|
585
|
+
session == null ? void 0 : session.webSocket.handleError({
|
|
586
|
+
error,
|
|
587
|
+
message: "Invalid input",
|
|
588
|
+
session
|
|
589
|
+
});
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
Promise.all([
|
|
593
|
+
(_a = resolver.broadcast) == null ? void 0 : _a.call(resolver, inputs, context),
|
|
594
|
+
(_b = resolver.self) == null ? void 0 : _b.call(resolver, inputs, context),
|
|
595
|
+
(_c = resolver.sync) == null ? void 0 : _c.call(resolver, inputs, context)
|
|
596
|
+
]).then(([broadcast, self, sync]) => {
|
|
597
|
+
if (broadcast) {
|
|
598
|
+
Object.entries(broadcast).forEach(([type, data]) => {
|
|
599
|
+
this._broadcast({
|
|
600
|
+
message: { data, type },
|
|
601
|
+
senderId: session.id
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
if (self) {
|
|
606
|
+
Object.entries(self).forEach(([type, data]) => {
|
|
607
|
+
this._sendSelfMessage({ data, type }, session);
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
if (sync) {
|
|
611
|
+
this._platform.pubSub.publish(this.id, __spreadValues({
|
|
612
|
+
connectionId: session.id,
|
|
613
|
+
options: { type: "sync" },
|
|
614
|
+
room: this.id,
|
|
615
|
+
user: session.user
|
|
616
|
+
}, message));
|
|
617
|
+
Object.entries(sync).forEach(([type, data]) => {
|
|
618
|
+
this._sendSelfMessage({ data, type }, session);
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
};
|
|
623
|
+
}
|
|
638
624
|
_parseMessage(message) {
|
|
639
625
|
const parsed = this._platform.parseData(message.data);
|
|
640
626
|
try {
|
|
@@ -649,29 +635,6 @@ var IORoom = class {
|
|
|
649
635
|
return null;
|
|
650
636
|
}
|
|
651
637
|
}
|
|
652
|
-
_resolveMessage(message, context) {
|
|
653
|
-
return __async(this, null, function* () {
|
|
654
|
-
const { session } = context;
|
|
655
|
-
const eventConfig = this._getEventConfig(message);
|
|
656
|
-
if (!eventConfig)
|
|
657
|
-
return null;
|
|
658
|
-
let input;
|
|
659
|
-
try {
|
|
660
|
-
input = eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
|
|
661
|
-
} catch (error) {
|
|
662
|
-
session == null ? void 0 : session.webSocket.handleError({
|
|
663
|
-
error,
|
|
664
|
-
message: "Invalid input",
|
|
665
|
-
session
|
|
666
|
-
});
|
|
667
|
-
return null;
|
|
668
|
-
}
|
|
669
|
-
const output = yield eventConfig.resolver(input, context);
|
|
670
|
-
if (!output)
|
|
671
|
-
return null;
|
|
672
|
-
return output;
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
638
|
_sendMessage(message, sender, options = {}) {
|
|
676
639
|
switch (options.type) {
|
|
677
640
|
case "self": {
|
|
@@ -721,13 +684,23 @@ var IORoom = class {
|
|
|
721
684
|
const senderId = sender == null ? void 0 : sender.id;
|
|
722
685
|
if (!senderId)
|
|
723
686
|
return;
|
|
724
|
-
|
|
687
|
+
const context = {
|
|
725
688
|
context: this._context,
|
|
726
689
|
doc: this._doc,
|
|
727
690
|
room: this.id,
|
|
728
691
|
session: null,
|
|
729
692
|
sessions: this._sessions
|
|
730
|
-
}
|
|
693
|
+
};
|
|
694
|
+
const resolver = this._getEventResolverObject(message).sync;
|
|
695
|
+
if (!resolver)
|
|
696
|
+
return;
|
|
697
|
+
let inputs;
|
|
698
|
+
try {
|
|
699
|
+
inputs = this._getEventInputs(message);
|
|
700
|
+
} catch (e) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
Promise.resolve(resolver(inputs, context)).then((output) => {
|
|
731
704
|
if (!output)
|
|
732
705
|
return;
|
|
733
706
|
Object.keys(output).forEach((type) => {
|
|
@@ -754,32 +727,70 @@ var PluvIO = class {
|
|
|
754
727
|
this._rooms = /* @__PURE__ */ new Map();
|
|
755
728
|
this._authorize = null;
|
|
756
729
|
this._events = {
|
|
757
|
-
$PING: {
|
|
758
|
-
resolver: (data, { session }) => {
|
|
759
|
-
if (!session)
|
|
760
|
-
return {};
|
|
761
|
-
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
762
|
-
session.timers.ping = currentTime;
|
|
763
|
-
return { $PONG: {} };
|
|
764
|
-
},
|
|
765
|
-
options: { type: "self" }
|
|
766
|
-
},
|
|
767
730
|
$GET_OTHERS: {
|
|
768
|
-
resolver:
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
[connectionId]
|
|
772
|
-
connectionId
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
731
|
+
resolver: {
|
|
732
|
+
sync: (data, { room, session, sessions }) => {
|
|
733
|
+
const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
|
|
734
|
+
(acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
|
|
735
|
+
[connectionId]: {
|
|
736
|
+
connectionId,
|
|
737
|
+
presence,
|
|
738
|
+
room,
|
|
739
|
+
user
|
|
740
|
+
}
|
|
741
|
+
}),
|
|
742
|
+
{}
|
|
743
|
+
);
|
|
744
|
+
return { $OTHERS_RECEIVED: { others } };
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
$INITIALIZE_SESSION: {
|
|
749
|
+
resolver: {
|
|
750
|
+
broadcast: ({ presence }, { session }) => {
|
|
751
|
+
if (!session)
|
|
752
|
+
return {};
|
|
753
|
+
session.presence = presence;
|
|
754
|
+
return {
|
|
755
|
+
$USER_JOINED: {
|
|
756
|
+
connectionId: session.id,
|
|
757
|
+
user: session.user,
|
|
758
|
+
presence
|
|
776
759
|
}
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
)
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
760
|
+
};
|
|
761
|
+
},
|
|
762
|
+
self: (_0, _1) => __async(this, [_0, _1], function* ({ update }, { doc: doc2, room }) {
|
|
763
|
+
var _a, _b;
|
|
764
|
+
const oldState = yield this._platform.persistance.getStorageState(room);
|
|
765
|
+
const updates = [
|
|
766
|
+
oldState ? null : yield (_a = this._initialStorage) == null ? void 0 : _a.call(this, room),
|
|
767
|
+
update
|
|
768
|
+
];
|
|
769
|
+
if (updates.some((_update) => !!_update)) {
|
|
770
|
+
const state2 = updates.reduce((_doc, _up) => _doc.applyUpdate(_up), doc2).encodeStateAsUpdate();
|
|
771
|
+
yield this._platform.persistance.setStorageState(
|
|
772
|
+
room,
|
|
773
|
+
state2
|
|
774
|
+
);
|
|
775
|
+
this._listeners.onStorageUpdated(room, state2);
|
|
776
|
+
}
|
|
777
|
+
const state = (_b = yield this._platform.persistance.getStorageState(
|
|
778
|
+
room
|
|
779
|
+
)) != null ? _b : doc2.encodeStateAsUpdate();
|
|
780
|
+
return { $STORAGE_RECEIVED: { state } };
|
|
781
|
+
})
|
|
782
|
+
}
|
|
783
|
+
},
|
|
784
|
+
$PING: {
|
|
785
|
+
resolver: {
|
|
786
|
+
self: (data, { session }) => {
|
|
787
|
+
if (!session)
|
|
788
|
+
return {};
|
|
789
|
+
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
790
|
+
session.timers.ping = currentTime;
|
|
791
|
+
return { $PONG: {} };
|
|
792
|
+
}
|
|
793
|
+
}
|
|
783
794
|
},
|
|
784
795
|
$UPDATE_PRESENCE: {
|
|
785
796
|
resolver: ({ presence }, { session }) => {
|
|
@@ -880,10 +891,6 @@ var PluvIO = class {
|
|
|
880
891
|
context: this._context,
|
|
881
892
|
debug: debug != null ? debug : this._debug,
|
|
882
893
|
events: this._events,
|
|
883
|
-
initialStorage: () => {
|
|
884
|
-
var _a2;
|
|
885
|
-
return (_a2 = this._initialStorage) == null ? void 0 : _a2.call(this, room);
|
|
886
|
-
},
|
|
887
894
|
onDestroy: (encodedState) => {
|
|
888
895
|
this._logDebug(
|
|
889
896
|
`${import_kleur2.default.blue("Deleting empty room:")} ${room}`
|