@pluv/io 0.3.9 → 0.4.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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +19 -0
- package/dist/index.d.ts +29 -24
- package/dist/index.js +195 -173
- package/dist/index.mjs +195 -173
- package/package.json +9 -9
- package/src/IORoom.ts +238 -198
- package/src/PluvIO.ts +168 -53
- package/src/types.ts +38 -7
package/dist/index.mjs
CHANGED
|
@@ -224,6 +224,10 @@ var AbstractWebSocket = class {
|
|
|
224
224
|
// src/PluvIO.ts
|
|
225
225
|
import colors2 from "kleur";
|
|
226
226
|
|
|
227
|
+
// src/IORoom.ts
|
|
228
|
+
import { doc } from "@pluv/crdt-yjs";
|
|
229
|
+
import colors from "kleur";
|
|
230
|
+
|
|
227
231
|
// src/authorize.ts
|
|
228
232
|
import hkdf from "@panva/hkdf";
|
|
229
233
|
import { EncryptJWT, jwtDecrypt } from "jose";
|
|
@@ -261,8 +265,6 @@ var authorize = (params) => {
|
|
|
261
265
|
};
|
|
262
266
|
|
|
263
267
|
// src/IORoom.ts
|
|
264
|
-
import { doc } from "@pluv/crdt-yjs";
|
|
265
|
-
import colors from "kleur";
|
|
266
268
|
var PING_TIMEOUT_MS = 3e4;
|
|
267
269
|
var IORoom = class {
|
|
268
270
|
constructor(id, config) {
|
|
@@ -359,101 +361,12 @@ var IORoom = class {
|
|
|
359
361
|
sessionId,
|
|
360
362
|
user != null ? user : {}
|
|
361
363
|
);
|
|
364
|
+
const onClose = this._onClose(session, uninitializeWs).bind(this);
|
|
365
|
+
const onMessage = this._onMessage(session).bind(this);
|
|
366
|
+
pluvWs.addEventListener("close", onClose);
|
|
367
|
+
pluvWs.addEventListener("error", onClose);
|
|
368
|
+
pluvWs.addEventListener("message", onMessage);
|
|
362
369
|
this._emitRegistered(session);
|
|
363
|
-
this._emitStorageReceived(session);
|
|
364
|
-
this._emitUserJoined(session);
|
|
365
|
-
const context = {
|
|
366
|
-
context: this._context,
|
|
367
|
-
doc: this._doc,
|
|
368
|
-
room: this.id,
|
|
369
|
-
session,
|
|
370
|
-
sessions: this._sessions
|
|
371
|
-
};
|
|
372
|
-
pluvWs.addEventListener("message", (event) => {
|
|
373
|
-
if (!this._uninitialize)
|
|
374
|
-
return;
|
|
375
|
-
if (session.quit) {
|
|
376
|
-
session.webSocket.close(1011, "WebSocket broken.");
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
const message = this._parseMessage(event);
|
|
380
|
-
if (!message)
|
|
381
|
-
return;
|
|
382
|
-
const eventConfig = this._getEventConfig(message);
|
|
383
|
-
if (!eventConfig)
|
|
384
|
-
return;
|
|
385
|
-
this._resolveMessage(message, context).then((output) => {
|
|
386
|
-
var _a2, _b;
|
|
387
|
-
if (!output)
|
|
388
|
-
return;
|
|
389
|
-
const eventType = (_b = (_a2 = eventConfig.options) == null ? void 0 : _a2.type) != null ? _b : "broadcast";
|
|
390
|
-
switch (eventType) {
|
|
391
|
-
case "self": {
|
|
392
|
-
Object.entries(output).forEach(([type, data]) => {
|
|
393
|
-
this._sendSelfMessage({ data, type }, session);
|
|
394
|
-
});
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
case "sync": {
|
|
398
|
-
this._platform.pubSub.publish(this.id, __spreadValues({
|
|
399
|
-
connectionId: session.id,
|
|
400
|
-
options: eventConfig.options,
|
|
401
|
-
room: this.id,
|
|
402
|
-
user: session.user
|
|
403
|
-
}, message));
|
|
404
|
-
Object.entries(output).forEach(([type, data]) => {
|
|
405
|
-
this._sendSelfMessage({ data, type }, session);
|
|
406
|
-
});
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
case "broadcast":
|
|
410
|
-
default: {
|
|
411
|
-
Object.entries(output).forEach(([type, data]) => {
|
|
412
|
-
this._broadcast({
|
|
413
|
-
message: { data, type },
|
|
414
|
-
senderId: session.id
|
|
415
|
-
});
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
});
|
|
421
|
-
const closeHandler = () => {
|
|
422
|
-
if (!this._uninitialize)
|
|
423
|
-
return;
|
|
424
|
-
session.quit = true;
|
|
425
|
-
this._logDebug(
|
|
426
|
-
`${colors.blue(
|
|
427
|
-
`(Unregistering connection for room ${this.id}:`
|
|
428
|
-
)} ${sessionId}`
|
|
429
|
-
);
|
|
430
|
-
this._sessions.delete(session.id);
|
|
431
|
-
this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
|
|
432
|
-
var _a2;
|
|
433
|
-
this._broadcast({
|
|
434
|
-
message: {
|
|
435
|
-
type: "$EXIT",
|
|
436
|
-
data: { sessionId: session.id }
|
|
437
|
-
},
|
|
438
|
-
senderId: session.id
|
|
439
|
-
});
|
|
440
|
-
uninitializeWs();
|
|
441
|
-
const size2 = this.getSize();
|
|
442
|
-
this._logDebug(
|
|
443
|
-
`${colors.blue(
|
|
444
|
-
`Unregistered connection for room ${this.id}:`
|
|
445
|
-
)} ${sessionId}`
|
|
446
|
-
);
|
|
447
|
-
this._logDebug(
|
|
448
|
-
`${colors.blue(`Room ${this.id} size:`)} ${size2}`
|
|
449
|
-
);
|
|
450
|
-
if (size2)
|
|
451
|
-
return;
|
|
452
|
-
(_a2 = this._uninitialize) == null ? void 0 : _a2.call(this);
|
|
453
|
-
});
|
|
454
|
-
};
|
|
455
|
-
pluvWs.addEventListener("close", closeHandler);
|
|
456
|
-
pluvWs.addEventListener("error", closeHandler);
|
|
457
370
|
this._logDebug(
|
|
458
371
|
`${colors.blue(
|
|
459
372
|
`Registered connection for room ${this.id}:`
|
|
@@ -501,34 +414,6 @@ var IORoom = class {
|
|
|
501
414
|
session
|
|
502
415
|
);
|
|
503
416
|
}
|
|
504
|
-
_emitStorageReceived(session) {
|
|
505
|
-
this._platform.persistance.getStorageState(this.id).then((_state) => __async(this, null, function* () {
|
|
506
|
-
var _a, _b;
|
|
507
|
-
const state = (_b = _state != null ? _state : yield (_a = this._initialStorage) == null ? void 0 : _a.call(this)) != null ? _b : this._doc.encodeStateAsUpdate();
|
|
508
|
-
this._sendSelfMessage(
|
|
509
|
-
{
|
|
510
|
-
type: "$STORAGE_RECEIVED",
|
|
511
|
-
data: { state }
|
|
512
|
-
},
|
|
513
|
-
session
|
|
514
|
-
);
|
|
515
|
-
}));
|
|
516
|
-
}
|
|
517
|
-
_emitUserJoined(session) {
|
|
518
|
-
const user = session.user;
|
|
519
|
-
if (!user)
|
|
520
|
-
return;
|
|
521
|
-
this._broadcast({
|
|
522
|
-
message: {
|
|
523
|
-
type: "$USER_JOINED",
|
|
524
|
-
data: {
|
|
525
|
-
connectionId: session.id,
|
|
526
|
-
user
|
|
527
|
-
}
|
|
528
|
-
},
|
|
529
|
-
senderId: session.id
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
417
|
_getAuthorizedUser(token) {
|
|
533
418
|
return __async(this, null, function* () {
|
|
534
419
|
var _a;
|
|
@@ -567,6 +452,19 @@ var IORoom = class {
|
|
|
567
452
|
var _a;
|
|
568
453
|
return (_a = this._events[message.type]) != null ? _a : null;
|
|
569
454
|
}
|
|
455
|
+
_getEventInputs(message) {
|
|
456
|
+
const eventConfig = this._getEventConfig(message);
|
|
457
|
+
if (!eventConfig)
|
|
458
|
+
return message.data;
|
|
459
|
+
return eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
|
|
460
|
+
}
|
|
461
|
+
_getEventResolverObject(message) {
|
|
462
|
+
const eventConfig = this._getEventConfig(message);
|
|
463
|
+
if (!eventConfig)
|
|
464
|
+
return {};
|
|
465
|
+
const resolver = eventConfig.resolver;
|
|
466
|
+
return typeof resolver === "function" ? { broadcast: resolver } : __spreadValues({}, resolver);
|
|
467
|
+
}
|
|
570
468
|
_initialize() {
|
|
571
469
|
return __async(this, null, function* () {
|
|
572
470
|
this._logDebug(`${colors.blue(`Initializing room ${this.id}:`)}`);
|
|
@@ -598,6 +496,105 @@ var IORoom = class {
|
|
|
598
496
|
_logDebug(...data) {
|
|
599
497
|
this._debug && console.log(...data);
|
|
600
498
|
}
|
|
499
|
+
_onClose(session, callback) {
|
|
500
|
+
return () => {
|
|
501
|
+
if (!this._uninitialize)
|
|
502
|
+
return;
|
|
503
|
+
session.quit = true;
|
|
504
|
+
this._logDebug(
|
|
505
|
+
`${colors.blue(
|
|
506
|
+
`(Unregistering connection for room ${this.id}:`
|
|
507
|
+
)} ${session.id}`
|
|
508
|
+
);
|
|
509
|
+
this._sessions.delete(session.id);
|
|
510
|
+
this._platform.persistance.deleteUser(this.id, session.id).finally(() => {
|
|
511
|
+
var _a;
|
|
512
|
+
this._broadcast({
|
|
513
|
+
message: {
|
|
514
|
+
type: "$EXIT",
|
|
515
|
+
data: { sessionId: session.id }
|
|
516
|
+
},
|
|
517
|
+
senderId: session.id
|
|
518
|
+
});
|
|
519
|
+
callback == null ? void 0 : callback();
|
|
520
|
+
const size = this.getSize();
|
|
521
|
+
this._logDebug(
|
|
522
|
+
`${colors.blue(
|
|
523
|
+
`Unregistered connection for room ${this.id}:`
|
|
524
|
+
)} ${session.id}`
|
|
525
|
+
);
|
|
526
|
+
this._logDebug(
|
|
527
|
+
`${colors.blue(`Room ${this.id} size:`)} ${size}`
|
|
528
|
+
);
|
|
529
|
+
if (size)
|
|
530
|
+
return;
|
|
531
|
+
(_a = this._uninitialize) == null ? void 0 : _a.call(this);
|
|
532
|
+
});
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
_onMessage(session) {
|
|
536
|
+
return (event) => {
|
|
537
|
+
var _a, _b, _c;
|
|
538
|
+
const context = {
|
|
539
|
+
context: this._context,
|
|
540
|
+
doc: this._doc,
|
|
541
|
+
room: this.id,
|
|
542
|
+
session,
|
|
543
|
+
sessions: this._sessions
|
|
544
|
+
};
|
|
545
|
+
if (!this._uninitialize)
|
|
546
|
+
return;
|
|
547
|
+
if (session.quit) {
|
|
548
|
+
session.webSocket.close(1011, "WebSocket broken.");
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
const message = this._parseMessage(event);
|
|
552
|
+
if (!message)
|
|
553
|
+
return;
|
|
554
|
+
const resolver = this._getEventResolverObject(message);
|
|
555
|
+
let inputs;
|
|
556
|
+
try {
|
|
557
|
+
inputs = this._getEventInputs(message);
|
|
558
|
+
} catch (error) {
|
|
559
|
+
session == null ? void 0 : session.webSocket.handleError({
|
|
560
|
+
error,
|
|
561
|
+
message: "Invalid input",
|
|
562
|
+
session
|
|
563
|
+
});
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
Promise.all([
|
|
567
|
+
(_a = resolver.broadcast) == null ? void 0 : _a.call(resolver, inputs, context),
|
|
568
|
+
(_b = resolver.self) == null ? void 0 : _b.call(resolver, inputs, context),
|
|
569
|
+
(_c = resolver.sync) == null ? void 0 : _c.call(resolver, inputs, context)
|
|
570
|
+
]).then(([broadcast, self, sync]) => {
|
|
571
|
+
if (broadcast) {
|
|
572
|
+
Object.entries(broadcast).forEach(([type, data]) => {
|
|
573
|
+
this._broadcast({
|
|
574
|
+
message: { data, type },
|
|
575
|
+
senderId: session.id
|
|
576
|
+
});
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
if (self) {
|
|
580
|
+
Object.entries(self).forEach(([type, data]) => {
|
|
581
|
+
this._sendSelfMessage({ data, type }, session);
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
if (sync) {
|
|
585
|
+
this._platform.pubSub.publish(this.id, __spreadValues({
|
|
586
|
+
connectionId: session.id,
|
|
587
|
+
options: { type: "sync" },
|
|
588
|
+
room: this.id,
|
|
589
|
+
user: session.user
|
|
590
|
+
}, message));
|
|
591
|
+
Object.entries(sync).forEach(([type, data]) => {
|
|
592
|
+
this._sendSelfMessage({ data, type }, session);
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
};
|
|
597
|
+
}
|
|
601
598
|
_parseMessage(message) {
|
|
602
599
|
const parsed = this._platform.parseData(message.data);
|
|
603
600
|
try {
|
|
@@ -612,29 +609,6 @@ var IORoom = class {
|
|
|
612
609
|
return null;
|
|
613
610
|
}
|
|
614
611
|
}
|
|
615
|
-
_resolveMessage(message, context) {
|
|
616
|
-
return __async(this, null, function* () {
|
|
617
|
-
const { session } = context;
|
|
618
|
-
const eventConfig = this._getEventConfig(message);
|
|
619
|
-
if (!eventConfig)
|
|
620
|
-
return null;
|
|
621
|
-
let input;
|
|
622
|
-
try {
|
|
623
|
-
input = eventConfig.input ? eventConfig.input.parse(message.data) : message.data;
|
|
624
|
-
} catch (error) {
|
|
625
|
-
session == null ? void 0 : session.webSocket.handleError({
|
|
626
|
-
error,
|
|
627
|
-
message: "Invalid input",
|
|
628
|
-
session
|
|
629
|
-
});
|
|
630
|
-
return null;
|
|
631
|
-
}
|
|
632
|
-
const output = yield eventConfig.resolver(input, context);
|
|
633
|
-
if (!output)
|
|
634
|
-
return null;
|
|
635
|
-
return output;
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
612
|
_sendMessage(message, sender, options = {}) {
|
|
639
613
|
switch (options.type) {
|
|
640
614
|
case "self": {
|
|
@@ -684,13 +658,23 @@ var IORoom = class {
|
|
|
684
658
|
const senderId = sender == null ? void 0 : sender.id;
|
|
685
659
|
if (!senderId)
|
|
686
660
|
return;
|
|
687
|
-
|
|
661
|
+
const context = {
|
|
688
662
|
context: this._context,
|
|
689
663
|
doc: this._doc,
|
|
690
664
|
room: this.id,
|
|
691
665
|
session: null,
|
|
692
666
|
sessions: this._sessions
|
|
693
|
-
}
|
|
667
|
+
};
|
|
668
|
+
const resolver = this._getEventResolverObject(message).sync;
|
|
669
|
+
if (!resolver)
|
|
670
|
+
return;
|
|
671
|
+
let inputs;
|
|
672
|
+
try {
|
|
673
|
+
inputs = this._getEventInputs(message);
|
|
674
|
+
} catch (e) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
Promise.resolve(resolver(inputs, context)).then((output) => {
|
|
694
678
|
if (!output)
|
|
695
679
|
return;
|
|
696
680
|
Object.keys(output).forEach((type) => {
|
|
@@ -717,32 +701,70 @@ var PluvIO = class {
|
|
|
717
701
|
this._rooms = /* @__PURE__ */ new Map();
|
|
718
702
|
this._authorize = null;
|
|
719
703
|
this._events = {
|
|
720
|
-
$PING: {
|
|
721
|
-
resolver: (data, { session }) => {
|
|
722
|
-
if (!session)
|
|
723
|
-
return {};
|
|
724
|
-
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
725
|
-
session.timers.ping = currentTime;
|
|
726
|
-
return { $PONG: {} };
|
|
727
|
-
},
|
|
728
|
-
options: { type: "self" }
|
|
729
|
-
},
|
|
730
704
|
$GET_OTHERS: {
|
|
731
|
-
resolver:
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
[connectionId]
|
|
735
|
-
connectionId
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
705
|
+
resolver: {
|
|
706
|
+
sync: (data, { room, session, sessions }) => {
|
|
707
|
+
const others = Array.from(sessions.entries()).filter(([, wsSession]) => wsSession.id !== (session == null ? void 0 : session.id)).reduce(
|
|
708
|
+
(acc, [connectionId, { presence, user }]) => __spreadProps(__spreadValues({}, acc), {
|
|
709
|
+
[connectionId]: {
|
|
710
|
+
connectionId,
|
|
711
|
+
presence,
|
|
712
|
+
room,
|
|
713
|
+
user
|
|
714
|
+
}
|
|
715
|
+
}),
|
|
716
|
+
{}
|
|
717
|
+
);
|
|
718
|
+
return { $OTHERS_RECEIVED: { others } };
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
$INITIALIZE_SESSION: {
|
|
723
|
+
resolver: {
|
|
724
|
+
broadcast: ({ presence }, { session }) => {
|
|
725
|
+
if (!session)
|
|
726
|
+
return {};
|
|
727
|
+
session.presence = presence;
|
|
728
|
+
return {
|
|
729
|
+
$USER_JOINED: {
|
|
730
|
+
connectionId: session.id,
|
|
731
|
+
user: session.user,
|
|
732
|
+
presence
|
|
739
733
|
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
)
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
734
|
+
};
|
|
735
|
+
},
|
|
736
|
+
self: (_0, _1) => __async(this, [_0, _1], function* ({ update }, { doc: doc2, room }) {
|
|
737
|
+
var _a, _b;
|
|
738
|
+
const oldState = yield this._platform.persistance.getStorageState(room);
|
|
739
|
+
const updates = [
|
|
740
|
+
oldState ? null : yield (_a = this._initialStorage) == null ? void 0 : _a.call(this, room),
|
|
741
|
+
update
|
|
742
|
+
];
|
|
743
|
+
if (updates.some((_update) => !!_update)) {
|
|
744
|
+
const state2 = updates.reduce((_doc, _up) => _doc.applyUpdate(_up), doc2).encodeStateAsUpdate();
|
|
745
|
+
yield this._platform.persistance.setStorageState(
|
|
746
|
+
room,
|
|
747
|
+
state2
|
|
748
|
+
);
|
|
749
|
+
this._listeners.onStorageUpdated(room, state2);
|
|
750
|
+
}
|
|
751
|
+
const state = (_b = yield this._platform.persistance.getStorageState(
|
|
752
|
+
room
|
|
753
|
+
)) != null ? _b : doc2.encodeStateAsUpdate();
|
|
754
|
+
return { $STORAGE_RECEIVED: { state } };
|
|
755
|
+
})
|
|
756
|
+
}
|
|
757
|
+
},
|
|
758
|
+
$PING: {
|
|
759
|
+
resolver: {
|
|
760
|
+
self: (data, { session }) => {
|
|
761
|
+
if (!session)
|
|
762
|
+
return {};
|
|
763
|
+
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
764
|
+
session.timers.ping = currentTime;
|
|
765
|
+
return { $PONG: {} };
|
|
766
|
+
}
|
|
767
|
+
}
|
|
746
768
|
},
|
|
747
769
|
$UPDATE_PRESENCE: {
|
|
748
770
|
resolver: ({ presence }, { session }) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/io",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "@pluv/io server-side websocket manager",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@panva/hkdf": "^1.
|
|
21
|
-
"jose": "^4.
|
|
20
|
+
"@panva/hkdf": "^1.1.1",
|
|
21
|
+
"jose": "^4.14.4",
|
|
22
22
|
"kleur": "^4.1.5",
|
|
23
|
-
"wonka": "^6.3.
|
|
24
|
-
"@pluv/crdt-yjs": "^0.3.
|
|
25
|
-
"@pluv/types": "^0.
|
|
23
|
+
"wonka": "^6.3.2",
|
|
24
|
+
"@pluv/crdt-yjs": "^0.3.7",
|
|
25
|
+
"@pluv/types": "^0.2.0"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"yjs": "^13.0.0"
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"tsup": "^6.7.0",
|
|
32
32
|
"typescript": "^5.0.4",
|
|
33
|
-
"yjs": "^13.
|
|
34
|
-
"@pluv/tsconfig": "^0.1.
|
|
35
|
-
"eslint-config-pluv": "^0.1.
|
|
33
|
+
"yjs": "^13.6.0",
|
|
34
|
+
"@pluv/tsconfig": "^0.1.6",
|
|
35
|
+
"eslint-config-pluv": "^0.1.11"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|