@rlanz/socket 0.0.1-6 → 0.0.1-8
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/README.md +186 -147
- package/build/{types-DiYaFvgi.d.ts → base_channel-DPOnqned.d.ts} +185 -171
- package/build/chunk-6PJ4ALJD.js +115 -0
- package/build/chunk-6PJ4ALJD.js.map +1 -0
- package/build/chunk-E6ILCRL3.js +182 -0
- package/build/chunk-E6ILCRL3.js.map +1 -0
- package/build/{chunk-SHH6U4CI.js → chunk-G75J233Z.js} +1 -1
- package/build/chunk-G75J233Z.js.map +1 -0
- package/build/{chunk-B4Y3TNDI.js → chunk-IUQ3GYZZ.js} +5 -24
- package/build/chunk-IUQ3GYZZ.js.map +1 -0
- package/build/chunk-K243S3QP.js +131 -0
- package/build/chunk-K243S3QP.js.map +1 -0
- package/build/chunk-QNOOQXYY.js +899 -0
- package/build/chunk-QNOOQXYY.js.map +1 -0
- package/build/{chunk-FVPY6HZW.js → chunk-YVC7IMDN.js} +21 -5
- package/build/chunk-YVC7IMDN.js.map +1 -0
- package/build/framework-B8pJ4cCA.d.ts +13 -0
- package/build/index-DjBYUhFR.d.ts +150 -0
- package/build/index.d.ts +23 -12
- package/build/index.js +12 -14
- package/build/index.js.map +1 -1
- package/build/providers/socket_provider.d.ts +2 -2
- package/build/providers/socket_provider.js +478 -215
- package/build/providers/socket_provider.js.map +1 -1
- package/build/services/socket.d.ts +2 -2
- package/build/{socket_service-D3jKrleE.d.ts → socket_service-BcC9ASCE.d.ts} +8 -7
- package/build/src/assembler_hook.js +33 -59
- package/build/src/assembler_hook.js.map +1 -1
- package/build/src/client/index.d.ts +3 -147
- package/build/src/client/index.js +4 -837
- package/build/src/client/index.js.map +1 -1
- package/build/src/client/react.d.ts +13 -5
- package/build/src/client/react.js +27 -11
- package/build/src/client/react.js.map +1 -1
- package/build/src/client/types.d.ts +30 -36
- package/build/src/client/vue.d.ts +17 -6
- package/build/src/client/vue.js +31 -13
- package/build/src/client/vue.js.map +1 -1
- package/build/src/decorators.d.ts +3 -2
- package/build/src/decorators.js +1 -1
- package/build/src/health_check.d.ts +2 -2
- package/build/src/health_check.js +14 -3
- package/build/src/health_check.js.map +1 -1
- package/build/src/otel.js +0 -1
- package/build/src/otel.js.map +1 -1
- package/build/src/testing.d.ts +3 -12
- package/build/src/testing.js +1 -1
- package/build/src/types/tracing_channels.d.ts +1 -2
- package/build/src/types.d.ts +1 -1
- package/package.json +3 -9
- package/build/chunk-B4Y3TNDI.js.map +0 -1
- package/build/chunk-FVPY6HZW.js.map +0 -1
- package/build/chunk-HK7Z65DA.js +0 -19
- package/build/chunk-HK7Z65DA.js.map +0 -1
- package/build/chunk-SFAY2ZA4.js +0 -28
- package/build/chunk-SFAY2ZA4.js.map +0 -1
- package/build/chunk-SHH6U4CI.js.map +0 -1
- package/build/chunk-YAX5EHHB.js +0 -453
- package/build/chunk-YAX5EHHB.js.map +0 -1
- package/build/framework-DuW6zpPk.d.ts +0 -14
- package/build/src/client/svelte.d.ts +0 -23
- package/build/src/client/svelte.js +0 -82
- package/build/src/client/svelte.js.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BaseChannel,
|
|
3
|
-
ChannelRouter,
|
|
4
|
-
PRESENCE_DATA_KEY,
|
|
5
|
-
PresenceManager,
|
|
6
3
|
SocketResponseError
|
|
7
|
-
} from "../chunk-
|
|
8
|
-
import
|
|
4
|
+
} from "../chunk-K243S3QP.js";
|
|
5
|
+
import {
|
|
6
|
+
ChannelPatternSyntax
|
|
7
|
+
} from "../chunk-E6ILCRL3.js";
|
|
8
|
+
import "../chunk-G75J233Z.js";
|
|
9
9
|
import {
|
|
10
10
|
broadcastChannel,
|
|
11
11
|
channelMessageChannel,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "../chunk-D3HUBCBW.js";
|
|
17
17
|
import {
|
|
18
18
|
SocketFake
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-IUQ3GYZZ.js";
|
|
20
20
|
|
|
21
21
|
// providers/socket_provider.ts
|
|
22
22
|
import { ServerResponse } from "http";
|
|
@@ -579,15 +579,255 @@ var MessageQueue = class {
|
|
|
579
579
|
}
|
|
580
580
|
};
|
|
581
581
|
|
|
582
|
-
// src/broadcasting.ts
|
|
583
|
-
function userRoom(userId) {
|
|
584
|
-
return `socket:user:${String(userId)}`;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
582
|
// src/socket_bus.ts
|
|
588
583
|
import { randomUUID } from "crypto";
|
|
589
584
|
import { Bus } from "@boringnode/bus";
|
|
590
585
|
|
|
586
|
+
// src/presence_manager.ts
|
|
587
|
+
var PRESENCE_DATA_KEY = "__rlanzSocketPresence";
|
|
588
|
+
var PresenceManager = class {
|
|
589
|
+
#presence = /* @__PURE__ */ new Map();
|
|
590
|
+
#fetchSockets = null;
|
|
591
|
+
/**
|
|
592
|
+
* Sets the socket fetcher used to build distributed snapshots.
|
|
593
|
+
*/
|
|
594
|
+
setSocketFetcher(fetchSockets) {
|
|
595
|
+
this.#fetchSockets = fetchSockets;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Adds a user to a channel.
|
|
599
|
+
*/
|
|
600
|
+
join(channel, socket, member) {
|
|
601
|
+
const user = {
|
|
602
|
+
...member.data,
|
|
603
|
+
id: member.id,
|
|
604
|
+
joinedAt: /* @__PURE__ */ new Date()
|
|
605
|
+
};
|
|
606
|
+
const users = this.#presence.get(channel) ?? /* @__PURE__ */ new Map();
|
|
607
|
+
users.set(socket.id, user);
|
|
608
|
+
this.#presence.set(channel, users);
|
|
609
|
+
this.#setSocketPresence(socket, channel, user);
|
|
610
|
+
return user;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Removes a user from a channel.
|
|
614
|
+
*/
|
|
615
|
+
leave(channel, socket) {
|
|
616
|
+
const channelPresence = this.#presence.get(channel);
|
|
617
|
+
if (channelPresence) {
|
|
618
|
+
channelPresence.delete(socket.id);
|
|
619
|
+
if (channelPresence.size === 0) {
|
|
620
|
+
this.#presence.delete(channel);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
this.#deleteSocketPresence(socket, channel);
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Removes a user from every channel.
|
|
627
|
+
* Returns the list of channels the user left.
|
|
628
|
+
*/
|
|
629
|
+
leaveAll(socket) {
|
|
630
|
+
const leftChannels = [];
|
|
631
|
+
for (const [channel, users] of this.#presence) {
|
|
632
|
+
if (users.has(socket.id)) {
|
|
633
|
+
users.delete(socket.id);
|
|
634
|
+
leftChannels.push(channel);
|
|
635
|
+
if (users.size === 0) {
|
|
636
|
+
this.#presence.delete(channel);
|
|
637
|
+
}
|
|
638
|
+
this.#deleteSocketPresence(socket, channel);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return leftChannels;
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Gets presence data for a channel.
|
|
645
|
+
*/
|
|
646
|
+
async snapshot(channel) {
|
|
647
|
+
if (this.#fetchSockets) {
|
|
648
|
+
return this.#getFromSockets(channel);
|
|
649
|
+
}
|
|
650
|
+
return this.#localSnapshot(channel);
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Gets the number of users in a channel.
|
|
654
|
+
*/
|
|
655
|
+
async count(channel) {
|
|
656
|
+
return (await this.snapshot(channel)).count;
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Checks whether a socket is locally present in a channel.
|
|
660
|
+
*/
|
|
661
|
+
hasLocal(channel, socketId) {
|
|
662
|
+
return this.#presence.get(channel)?.has(socketId) || false;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Gets the presence user retained for one local socket.
|
|
666
|
+
*/
|
|
667
|
+
getLocalUser(channel, socketId) {
|
|
668
|
+
return this.#presence.get(channel)?.get(socketId);
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Lists every local channel where a socket is present.
|
|
672
|
+
*/
|
|
673
|
+
getLocalChannelsForSocket(socketId) {
|
|
674
|
+
const channels = [];
|
|
675
|
+
for (const [channel, users] of this.#presence) {
|
|
676
|
+
if (users.has(socketId)) {
|
|
677
|
+
channels.push(channel);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return channels;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Lists sockets that are locally present in a channel.
|
|
684
|
+
*/
|
|
685
|
+
getLocalSockets(channel) {
|
|
686
|
+
const users = this.#presence.get(channel);
|
|
687
|
+
if (!users) {
|
|
688
|
+
return [];
|
|
689
|
+
}
|
|
690
|
+
return Array.from(users.entries()).map(([socketId, user]) => ({
|
|
691
|
+
id: socketId,
|
|
692
|
+
data: {
|
|
693
|
+
[PRESENCE_DATA_KEY]: {
|
|
694
|
+
[channel]: user
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}));
|
|
698
|
+
}
|
|
699
|
+
#localSnapshot(channel) {
|
|
700
|
+
return this.#createSnapshot(channel, Array.from(this.#presence.get(channel)?.entries() ?? []));
|
|
701
|
+
}
|
|
702
|
+
async #getFromSockets(channel) {
|
|
703
|
+
const sockets = await this.#fetchSockets(channel);
|
|
704
|
+
const users = sockets.flatMap((socket) => {
|
|
705
|
+
const user = socket.data?.[PRESENCE_DATA_KEY]?.[channel];
|
|
706
|
+
return user ? [[socket.id, user]] : [];
|
|
707
|
+
});
|
|
708
|
+
return this.#createSnapshot(channel, users);
|
|
709
|
+
}
|
|
710
|
+
#createSnapshot(channel, connections) {
|
|
711
|
+
const usersById = /* @__PURE__ */ new Map();
|
|
712
|
+
for (const connection of connections) {
|
|
713
|
+
const current = usersById.get(connection[1].id);
|
|
714
|
+
if (!current || this.#compareConnections(connection, current) < 0) {
|
|
715
|
+
usersById.set(connection[1].id, connection);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
const users = [...usersById.values()].sort((left, right) => this.#compareConnections(left, right)).map(([, user]) => user);
|
|
719
|
+
return {
|
|
720
|
+
channel,
|
|
721
|
+
users,
|
|
722
|
+
count: users.length
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
#compareConnections([leftSocketId, leftUser], [rightSocketId, rightUser]) {
|
|
726
|
+
const joinedAtDifference = leftUser.joinedAt.getTime() - rightUser.joinedAt.getTime();
|
|
727
|
+
return joinedAtDifference || leftSocketId.localeCompare(rightSocketId);
|
|
728
|
+
}
|
|
729
|
+
#setSocketPresence(socket, channel, user) {
|
|
730
|
+
socket.data[PRESENCE_DATA_KEY] ??= {};
|
|
731
|
+
socket.data[PRESENCE_DATA_KEY][channel] = user;
|
|
732
|
+
}
|
|
733
|
+
#deleteSocketPresence(socket, channel) {
|
|
734
|
+
if (!socket.data[PRESENCE_DATA_KEY]) {
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
delete socket.data[PRESENCE_DATA_KEY][channel];
|
|
738
|
+
if (Object.keys(socket.data[PRESENCE_DATA_KEY]).length === 0) {
|
|
739
|
+
delete socket.data[PRESENCE_DATA_KEY];
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
// src/presence_socket_frame.ts
|
|
745
|
+
var PresenceSocketFrame = class _PresenceSocketFrame {
|
|
746
|
+
constructor(transportValue, socketValue) {
|
|
747
|
+
this.transportValue = transportValue;
|
|
748
|
+
this.socketValue = socketValue;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Create one transport frame from a local socket presence entry.
|
|
752
|
+
*/
|
|
753
|
+
static fromSocket(channel, socket) {
|
|
754
|
+
const user = socket.data[PRESENCE_DATA_KEY]?.[channel];
|
|
755
|
+
if (!user || Number.isNaN(user.joinedAt.getTime())) {
|
|
756
|
+
throw new Error(`Socket "${socket.id}" has no valid presence for channel "${channel}"`);
|
|
757
|
+
}
|
|
758
|
+
const transport = {
|
|
759
|
+
id: socket.id,
|
|
760
|
+
data: {
|
|
761
|
+
[PRESENCE_DATA_KEY]: {
|
|
762
|
+
[channel]: {
|
|
763
|
+
// Keep custom fields. Convert only the presence timestamp.
|
|
764
|
+
...user,
|
|
765
|
+
joinedAt: user.joinedAt.toISOString()
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
return new _PresenceSocketFrame(transport, socket);
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Validate one transport value and restore its presence timestamps.
|
|
774
|
+
*/
|
|
775
|
+
static fromTransport(value) {
|
|
776
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
const transport = value;
|
|
780
|
+
if (typeof transport.id !== "string" || !transport.data || typeof transport.data !== "object" || Array.isArray(transport.data)) {
|
|
781
|
+
return null;
|
|
782
|
+
}
|
|
783
|
+
const data = transport.data;
|
|
784
|
+
const presence = data[PRESENCE_DATA_KEY];
|
|
785
|
+
if (presence === void 0) {
|
|
786
|
+
return new _PresenceSocketFrame(transport, {
|
|
787
|
+
id: transport.id,
|
|
788
|
+
data: { ...data }
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
if (!presence || typeof presence !== "object" || Array.isArray(presence)) {
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
const restoredPresence = {};
|
|
795
|
+
for (const [channel, candidate] of Object.entries(presence)) {
|
|
796
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
797
|
+
return null;
|
|
798
|
+
}
|
|
799
|
+
const user = candidate;
|
|
800
|
+
if (typeof user.id !== "string" || typeof user.joinedAt !== "string" || Number.isNaN(Date.parse(user.joinedAt))) {
|
|
801
|
+
return null;
|
|
802
|
+
}
|
|
803
|
+
restoredPresence[channel] = {
|
|
804
|
+
...user,
|
|
805
|
+
id: user.id,
|
|
806
|
+
joinedAt: new Date(user.joinedAt)
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
return new _PresenceSocketFrame(transport, {
|
|
810
|
+
id: transport.id,
|
|
811
|
+
data: {
|
|
812
|
+
...data,
|
|
813
|
+
[PRESENCE_DATA_KEY]: restoredPresence
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Return the validated transport value.
|
|
819
|
+
*/
|
|
820
|
+
toTransport() {
|
|
821
|
+
return this.transportValue;
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Return the socket value with restored presence timestamps.
|
|
825
|
+
*/
|
|
826
|
+
toSocket() {
|
|
827
|
+
return this.socketValue;
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
|
|
591
831
|
// src/duration.ts
|
|
592
832
|
import { parse as parseDurationExpression } from "@lukeed/ms";
|
|
593
833
|
function parseDuration(name, duration) {
|
|
@@ -604,27 +844,17 @@ function parseDuration(name, duration) {
|
|
|
604
844
|
// src/socket_bus.ts
|
|
605
845
|
var DEFAULT_CHANNEL = "socket::broadcast";
|
|
606
846
|
var DEFAULT_PRESENCE_TIMEOUT = 100;
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
if (typeof socket.id !== "string" || !socket.data || typeof socket.data !== "object" || Array.isArray(socket.data)) {
|
|
613
|
-
return null;
|
|
614
|
-
}
|
|
615
|
-
const presence = socket.data[PRESENCE_DATA_KEY];
|
|
616
|
-
if (presence === void 0) {
|
|
617
|
-
return socket;
|
|
618
|
-
}
|
|
619
|
-
if (!presence || typeof presence !== "object" || Array.isArray(presence)) {
|
|
620
|
-
return null;
|
|
621
|
-
}
|
|
622
|
-
for (const user of Object.values(presence)) {
|
|
623
|
-
if (!user || typeof user !== "object" || typeof user.id !== "string" || typeof user.name !== "string" || typeof user.joinedAt !== "string" || Number.isNaN(Date.parse(user.joinedAt))) {
|
|
624
|
-
return null;
|
|
625
|
-
}
|
|
847
|
+
var DEFAULT_RETRY_QUEUE_MAX_SIZE = 1e3;
|
|
848
|
+
function resolveRetryQueueConfig(config) {
|
|
849
|
+
const maxSize = config?.maxSize === void 0 ? DEFAULT_RETRY_QUEUE_MAX_SIZE : config.maxSize;
|
|
850
|
+
if (maxSize !== null && (!Number.isSafeInteger(maxSize) || maxSize <= 0)) {
|
|
851
|
+
throw new Error("transport.retryQueue.maxSize must be a positive integer or null");
|
|
626
852
|
}
|
|
627
|
-
return
|
|
853
|
+
return {
|
|
854
|
+
enabled: true,
|
|
855
|
+
maxSize,
|
|
856
|
+
...config
|
|
857
|
+
};
|
|
628
858
|
}
|
|
629
859
|
function parseBusMessage(payload) {
|
|
630
860
|
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
@@ -637,8 +867,6 @@ function parseBusMessage(payload) {
|
|
|
637
867
|
switch (message.type) {
|
|
638
868
|
case "channel:event":
|
|
639
869
|
return typeof message.channel === "string" && typeof message.event === "string" && Array.isArray(message.except) && message.except.every((socketId) => typeof socketId === "string") ? message : null;
|
|
640
|
-
case "user:event":
|
|
641
|
-
return typeof message.room === "string" && typeof message.event === "string" ? message : null;
|
|
642
870
|
case "broadcast:event":
|
|
643
871
|
return typeof message.event === "string" ? message : null;
|
|
644
872
|
case "presence:sockets:request":
|
|
@@ -647,7 +875,7 @@ function parseBusMessage(payload) {
|
|
|
647
875
|
if (typeof message.target !== "string" || typeof message.requestId !== "string" || !Array.isArray(message.sockets)) {
|
|
648
876
|
return null;
|
|
649
877
|
}
|
|
650
|
-
const sockets = Array.from(message.sockets,
|
|
878
|
+
const sockets = Array.from(message.sockets, PresenceSocketFrame.fromTransport);
|
|
651
879
|
return sockets.every((socket) => socket !== null) ? { ...message, sockets } : null;
|
|
652
880
|
default:
|
|
653
881
|
return null;
|
|
@@ -658,7 +886,9 @@ var SocketBus = class {
|
|
|
658
886
|
this.handlers = handlers;
|
|
659
887
|
this.#channel = transport.channel ?? DEFAULT_CHANNEL;
|
|
660
888
|
this.#presenceTimeout = parseDuration("transport.presenceTimeout", transport.presenceTimeout) ?? DEFAULT_PRESENCE_TIMEOUT;
|
|
661
|
-
this.#bus = new Bus(transport.driver(), {
|
|
889
|
+
this.#bus = new Bus(transport.driver(), {
|
|
890
|
+
retryQueue: resolveRetryQueueConfig(transport.retryQueue)
|
|
891
|
+
});
|
|
662
892
|
}
|
|
663
893
|
#origin = randomUUID();
|
|
664
894
|
#channel;
|
|
@@ -676,9 +906,6 @@ var SocketBus = class {
|
|
|
676
906
|
case "channel:event":
|
|
677
907
|
this.handlers.channel(message);
|
|
678
908
|
return;
|
|
679
|
-
case "user:event":
|
|
680
|
-
this.handlers.user(message);
|
|
681
|
-
return;
|
|
682
909
|
case "broadcast:event":
|
|
683
910
|
this.handlers.broadcast(message);
|
|
684
911
|
return;
|
|
@@ -706,16 +933,6 @@ var SocketBus = class {
|
|
|
706
933
|
};
|
|
707
934
|
void this.#bus.publish(this.#channel, message);
|
|
708
935
|
}
|
|
709
|
-
publishUser(room, event, data) {
|
|
710
|
-
const message = {
|
|
711
|
-
type: "user:event",
|
|
712
|
-
origin: this.#origin,
|
|
713
|
-
room,
|
|
714
|
-
event,
|
|
715
|
-
data
|
|
716
|
-
};
|
|
717
|
-
void this.#bus.publish(this.#channel, message);
|
|
718
|
-
}
|
|
719
936
|
publishBroadcast(event, data) {
|
|
720
937
|
const message = {
|
|
721
938
|
type: "broadcast:event",
|
|
@@ -734,7 +951,7 @@ var SocketBus = class {
|
|
|
734
951
|
return;
|
|
735
952
|
}
|
|
736
953
|
this.#pendingPresenceRequests.delete(requestId);
|
|
737
|
-
resolve(request.sockets.map((socket) =>
|
|
954
|
+
resolve(request.sockets.map((socket) => socket.toSocket()));
|
|
738
955
|
}, this.#presenceTimeout);
|
|
739
956
|
this.#pendingPresenceRequests.set(requestId, {
|
|
740
957
|
sockets: [],
|
|
@@ -753,7 +970,7 @@ var SocketBus = class {
|
|
|
753
970
|
async close() {
|
|
754
971
|
for (const [requestId, request] of this.#pendingPresenceRequests) {
|
|
755
972
|
clearTimeout(request.timeout);
|
|
756
|
-
request.resolve(request.sockets.map((socket) =>
|
|
973
|
+
request.resolve(request.sockets.map((socket) => socket.toSocket()));
|
|
757
974
|
this.#pendingPresenceRequests.delete(requestId);
|
|
758
975
|
}
|
|
759
976
|
await this.#bus.disconnect();
|
|
@@ -778,23 +995,6 @@ var SocketBus = class {
|
|
|
778
995
|
}
|
|
779
996
|
request.sockets.push(...message.sockets);
|
|
780
997
|
}
|
|
781
|
-
#deserializePresenceSocket(socket) {
|
|
782
|
-
const presence = socket.data[PRESENCE_DATA_KEY];
|
|
783
|
-
return {
|
|
784
|
-
id: socket.id,
|
|
785
|
-
data: {
|
|
786
|
-
[PRESENCE_DATA_KEY]: presence ? Object.fromEntries(
|
|
787
|
-
Object.entries(presence).map(([channel, user]) => [
|
|
788
|
-
channel,
|
|
789
|
-
{
|
|
790
|
-
...user,
|
|
791
|
-
joinedAt: new Date(user.joinedAt)
|
|
792
|
-
}
|
|
793
|
-
])
|
|
794
|
-
) : void 0
|
|
795
|
-
}
|
|
796
|
-
};
|
|
797
|
-
}
|
|
798
998
|
};
|
|
799
999
|
|
|
800
1000
|
// src/socket_upgrader.ts
|
|
@@ -807,25 +1007,23 @@ var SocketUpgrader = class _SocketUpgrader {
|
|
|
807
1007
|
this.runWithHttpContext = runWithHttpContext;
|
|
808
1008
|
this.reportError = reportError;
|
|
809
1009
|
this.#path = config?.path ?? DEFAULT_WEBSOCKET_PATH;
|
|
810
|
-
this.#allowedOrigins = new Set((config?.allowedOrigins ?? []).map(_SocketUpgrader.#parseOrigin));
|
|
811
1010
|
}
|
|
812
1011
|
#path;
|
|
813
|
-
#allowedOrigins;
|
|
814
1012
|
async handle(request, socket, head, accept) {
|
|
815
1013
|
if (!this.#matchesPath(request)) {
|
|
816
1014
|
return false;
|
|
817
1015
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
1016
|
+
const result = await this.#prepareUpgrade(request);
|
|
1017
|
+
if ("rejected" in result) {
|
|
1018
|
+
if (result.rejected === "origin") {
|
|
1019
|
+
_SocketUpgrader.reject(socket, 403, "Forbidden");
|
|
1020
|
+
return true;
|
|
1021
|
+
}
|
|
824
1022
|
_SocketUpgrader.reject(socket, 401, "Unauthorized");
|
|
825
1023
|
return true;
|
|
826
1024
|
}
|
|
827
1025
|
this.server.handleUpgrade(request, socket, head, (connection) => {
|
|
828
|
-
accept(connection, request, accepted);
|
|
1026
|
+
accept(connection, request, result.accepted);
|
|
829
1027
|
});
|
|
830
1028
|
return true;
|
|
831
1029
|
}
|
|
@@ -837,35 +1035,17 @@ var SocketUpgrader = class _SocketUpgrader {
|
|
|
837
1035
|
}
|
|
838
1036
|
socket.destroy();
|
|
839
1037
|
}
|
|
840
|
-
static #parseOrigin(value) {
|
|
841
|
-
let url;
|
|
842
|
-
try {
|
|
843
|
-
url = new URL(value);
|
|
844
|
-
} catch {
|
|
845
|
-
throw new Error(`websocket.allowedOrigins contains an invalid origin: ${value}`);
|
|
846
|
-
}
|
|
847
|
-
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
|
|
848
|
-
throw new Error(`websocket.allowedOrigins contains an invalid origin: ${value}`);
|
|
849
|
-
}
|
|
850
|
-
return url.origin;
|
|
851
|
-
}
|
|
852
1038
|
#matchesPath(request) {
|
|
853
1039
|
const url = new URL(request.url ?? "/", "ws://localhost");
|
|
854
1040
|
return url.pathname === this.#path;
|
|
855
1041
|
}
|
|
856
|
-
#isOriginAllowed(request) {
|
|
1042
|
+
#isOriginAllowed(request, httpContext) {
|
|
857
1043
|
const origin = request.headers.origin;
|
|
858
1044
|
if (!origin) {
|
|
859
1045
|
return true;
|
|
860
1046
|
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
parsedOrigin = _SocketUpgrader.#parseOrigin(origin);
|
|
864
|
-
} catch {
|
|
865
|
-
return false;
|
|
866
|
-
}
|
|
867
|
-
if (this.#allowedOrigins.has(parsedOrigin)) {
|
|
868
|
-
return true;
|
|
1047
|
+
if (this.config?.origin !== void 0) {
|
|
1048
|
+
return this.#matchesOriginPolicy(origin, httpContext);
|
|
869
1049
|
}
|
|
870
1050
|
const host = request.headers.host;
|
|
871
1051
|
if (!host) {
|
|
@@ -875,37 +1055,63 @@ var SocketUpgrader = class _SocketUpgrader {
|
|
|
875
1055
|
const forwardedProtocol = (Array.isArray(forwardedHeader) ? forwardedHeader[0] : forwardedHeader)?.split(",")[0]?.trim();
|
|
876
1056
|
const protocol = forwardedProtocol === "http" || forwardedProtocol === "https" ? forwardedProtocol : request.socket.encrypted ? "https" : "http";
|
|
877
1057
|
try {
|
|
878
|
-
return
|
|
1058
|
+
return origin === new URL(`${protocol}://${host}`).origin;
|
|
879
1059
|
} catch {
|
|
880
1060
|
return false;
|
|
881
1061
|
}
|
|
882
1062
|
}
|
|
883
|
-
|
|
1063
|
+
#matchesOriginPolicy(origin, httpContext) {
|
|
1064
|
+
let originPolicy = this.config?.origin ?? false;
|
|
1065
|
+
if (typeof originPolicy === "function") {
|
|
1066
|
+
originPolicy = originPolicy(origin, httpContext);
|
|
1067
|
+
}
|
|
1068
|
+
if (originPolicy === true || originPolicy === "*") {
|
|
1069
|
+
return true;
|
|
1070
|
+
}
|
|
1071
|
+
if (originPolicy === false) {
|
|
1072
|
+
return false;
|
|
1073
|
+
}
|
|
1074
|
+
if (Array.isArray(originPolicy)) {
|
|
1075
|
+
return originPolicy.includes(origin);
|
|
1076
|
+
}
|
|
1077
|
+
return originPolicy.split(",").includes(origin);
|
|
1078
|
+
}
|
|
1079
|
+
async #prepareUpgrade(request) {
|
|
1080
|
+
return this.runWithHttpContext(
|
|
1081
|
+
request,
|
|
1082
|
+
async (httpContext) => {
|
|
1083
|
+
const accepted = await this.#authenticate(httpContext);
|
|
1084
|
+
return accepted ? { accepted } : { rejected: "authentication" };
|
|
1085
|
+
},
|
|
1086
|
+
(httpContext) => {
|
|
1087
|
+
return this.#isOriginAllowed(request, httpContext) ? void 0 : { rejected: "origin" };
|
|
1088
|
+
}
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
async #authenticate(httpContext) {
|
|
884
1092
|
const config = this.config;
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
1093
|
+
if (!config?.authenticate) {
|
|
1094
|
+
return { httpContext };
|
|
1095
|
+
}
|
|
1096
|
+
try {
|
|
1097
|
+
const ctx = {
|
|
1098
|
+
httpContext
|
|
1099
|
+
};
|
|
1100
|
+
const result = await config.authenticate(ctx);
|
|
1101
|
+
if (result === false || result === null || result === void 0) {
|
|
1102
|
+
return null;
|
|
888
1103
|
}
|
|
1104
|
+
return {
|
|
1105
|
+
httpContext,
|
|
1106
|
+
user: result
|
|
1107
|
+
};
|
|
1108
|
+
} catch (error) {
|
|
889
1109
|
try {
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
};
|
|
893
|
-
const result = await config.authenticate(ctx);
|
|
894
|
-
if (result === false || result === null || result === void 0) {
|
|
895
|
-
return null;
|
|
896
|
-
}
|
|
897
|
-
return {
|
|
898
|
-
httpContext,
|
|
899
|
-
user: result
|
|
900
|
-
};
|
|
901
|
-
} catch (error) {
|
|
902
|
-
try {
|
|
903
|
-
this.reportError("socket authentication failed: %s", error);
|
|
904
|
-
} catch {
|
|
905
|
-
}
|
|
906
|
-
return null;
|
|
1110
|
+
this.reportError("socket authentication failed: %s", error);
|
|
1111
|
+
} catch {
|
|
907
1112
|
}
|
|
908
|
-
|
|
1113
|
+
return null;
|
|
1114
|
+
}
|
|
909
1115
|
}
|
|
910
1116
|
};
|
|
911
1117
|
|
|
@@ -1030,8 +1236,6 @@ var SocketService = class extends Emittery {
|
|
|
1030
1236
|
#httpServer = null;
|
|
1031
1237
|
#upgradeHandler = null;
|
|
1032
1238
|
#sockets = /* @__PURE__ */ new Map();
|
|
1033
|
-
#userRooms = /* @__PURE__ */ new Map();
|
|
1034
|
-
#socketUserRoom = /* @__PURE__ */ new Map();
|
|
1035
1239
|
#channelSubscriptions = null;
|
|
1036
1240
|
#presenceManager = null;
|
|
1037
1241
|
#bus = null;
|
|
@@ -1102,12 +1306,6 @@ var SocketService = class extends Emittery {
|
|
|
1102
1306
|
)
|
|
1103
1307
|
);
|
|
1104
1308
|
},
|
|
1105
|
-
user: (message) => {
|
|
1106
|
-
this.#traceBroadcast(
|
|
1107
|
-
{ target: "user", room: message.room, event: message.event, via: "bus" },
|
|
1108
|
-
() => this.#emitToUserRoomLocally(message.room, message.event, message.data)
|
|
1109
|
-
);
|
|
1110
|
-
},
|
|
1111
1309
|
broadcast: (message) => {
|
|
1112
1310
|
this.#traceBroadcast(
|
|
1113
1311
|
{ target: "global", event: message.event, via: "bus" },
|
|
@@ -1229,7 +1427,6 @@ var SocketService = class extends Emittery {
|
|
|
1229
1427
|
await this.#channelSubscriptions?.leaveAll(socket);
|
|
1230
1428
|
this.#channelSubscriptions?.deleteSocket(socket.id);
|
|
1231
1429
|
this.#sockets.delete(socket.id);
|
|
1232
|
-
this.#leaveUserRoom(socket.id);
|
|
1233
1430
|
this.#emitLifecycleEvent("disconnect", { socket, reason: "close" });
|
|
1234
1431
|
},
|
|
1235
1432
|
{
|
|
@@ -1347,40 +1544,12 @@ var SocketService = class extends Emittery {
|
|
|
1347
1544
|
emit: (event, data) => {
|
|
1348
1545
|
this.#sendSerializedEventFrame(connection, serializeFrame({ type: "event", event, data }));
|
|
1349
1546
|
},
|
|
1350
|
-
joinUserRoom: (userId) => {
|
|
1351
|
-
this.#joinUserRoom(raw.id, userRoom(userId));
|
|
1352
|
-
return Promise.resolve();
|
|
1353
|
-
},
|
|
1354
|
-
leaveUserRoom: () => {
|
|
1355
|
-
this.#leaveUserRoom(raw.id);
|
|
1356
|
-
return Promise.resolve();
|
|
1357
|
-
},
|
|
1358
1547
|
disconnect() {
|
|
1359
1548
|
connection.close();
|
|
1360
1549
|
},
|
|
1361
1550
|
raw
|
|
1362
1551
|
};
|
|
1363
1552
|
}
|
|
1364
|
-
#joinUserRoom(socketId, room) {
|
|
1365
|
-
this.#leaveUserRoom(socketId);
|
|
1366
|
-
if (!this.#userRooms.has(room)) {
|
|
1367
|
-
this.#userRooms.set(room, /* @__PURE__ */ new Set());
|
|
1368
|
-
}
|
|
1369
|
-
this.#userRooms.get(room).add(socketId);
|
|
1370
|
-
this.#socketUserRoom.set(socketId, room);
|
|
1371
|
-
}
|
|
1372
|
-
#leaveUserRoom(socketId) {
|
|
1373
|
-
const room = this.#socketUserRoom.get(socketId);
|
|
1374
|
-
if (!room) {
|
|
1375
|
-
return;
|
|
1376
|
-
}
|
|
1377
|
-
const sockets = this.#userRooms.get(room);
|
|
1378
|
-
sockets?.delete(socketId);
|
|
1379
|
-
if (sockets?.size === 0) {
|
|
1380
|
-
this.#userRooms.delete(room);
|
|
1381
|
-
}
|
|
1382
|
-
this.#socketUserRoom.delete(socketId);
|
|
1383
|
-
}
|
|
1384
1553
|
to(channel) {
|
|
1385
1554
|
return {
|
|
1386
1555
|
emit: (event, data) => {
|
|
@@ -1399,14 +1568,6 @@ var SocketService = class extends Emittery {
|
|
|
1399
1568
|
})
|
|
1400
1569
|
};
|
|
1401
1570
|
}
|
|
1402
|
-
toUser(userId) {
|
|
1403
|
-
const room = userRoom(userId);
|
|
1404
|
-
return {
|
|
1405
|
-
emit: (event, data) => {
|
|
1406
|
-
this.#broadcastSink.dispatch({ target: "user", userId, room, event, data });
|
|
1407
|
-
}
|
|
1408
|
-
};
|
|
1409
|
-
}
|
|
1410
1571
|
broadcast(event, data) {
|
|
1411
1572
|
this.#broadcastSink.dispatch({ target: "global", event, data });
|
|
1412
1573
|
}
|
|
@@ -1442,20 +1603,6 @@ var SocketService = class extends Emittery {
|
|
|
1442
1603
|
);
|
|
1443
1604
|
return;
|
|
1444
1605
|
}
|
|
1445
|
-
case "user": {
|
|
1446
|
-
const room = emission.room;
|
|
1447
|
-
const serializedFrame = serializeFrame({
|
|
1448
|
-
type: "event",
|
|
1449
|
-
event: emission.event,
|
|
1450
|
-
data: emission.data
|
|
1451
|
-
});
|
|
1452
|
-
this.#bus?.publishUser(room, emission.event, emission.data);
|
|
1453
|
-
this.#traceBroadcast(
|
|
1454
|
-
{ target: "user", room, event: emission.event, via: "local" },
|
|
1455
|
-
() => this.#emitToUserRoomLocally(room, emission.event, emission.data, serializedFrame)
|
|
1456
|
-
);
|
|
1457
|
-
return;
|
|
1458
|
-
}
|
|
1459
1606
|
case "global": {
|
|
1460
1607
|
const serializedFrame = serializeFrame({
|
|
1461
1608
|
type: "event",
|
|
@@ -1495,16 +1642,6 @@ var SocketService = class extends Emittery {
|
|
|
1495
1642
|
}
|
|
1496
1643
|
return delivered;
|
|
1497
1644
|
}
|
|
1498
|
-
#emitToUserRoomLocally(room, event, data, serializedFrame = serializeFrame({ type: "event", event, data })) {
|
|
1499
|
-
let delivered = 0;
|
|
1500
|
-
for (const socketId of this.#userRooms.get(room) ?? []) {
|
|
1501
|
-
const socket = this.#sockets.get(socketId);
|
|
1502
|
-
if (socket && this.#sendSerializedEventFrame(socket.raw.connection, serializedFrame)) {
|
|
1503
|
-
delivered += 1;
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
return delivered;
|
|
1507
|
-
}
|
|
1508
1645
|
#broadcastLocally(event, data, serializedFrame = serializeFrame({ type: "event", event, data })) {
|
|
1509
1646
|
let delivered = 0;
|
|
1510
1647
|
for (const socket of this.#sockets.values()) {
|
|
@@ -1527,23 +1664,9 @@ var SocketService = class extends Emittery {
|
|
|
1527
1664
|
}
|
|
1528
1665
|
#getLocalPresenceSockets(channel) {
|
|
1529
1666
|
return (this.#presenceManager?.getLocalSockets(channel) ?? []).map((socket) => {
|
|
1530
|
-
return
|
|
1667
|
+
return PresenceSocketFrame.fromSocket(channel, socket).toTransport();
|
|
1531
1668
|
});
|
|
1532
1669
|
}
|
|
1533
|
-
#serializePresenceSocket(channel, socket) {
|
|
1534
|
-
const user = socket.data[PRESENCE_DATA_KEY][channel];
|
|
1535
|
-
return {
|
|
1536
|
-
id: socket.id,
|
|
1537
|
-
data: {
|
|
1538
|
-
[PRESENCE_DATA_KEY]: {
|
|
1539
|
-
[channel]: {
|
|
1540
|
-
...user,
|
|
1541
|
-
joinedAt: user.joinedAt.toISOString()
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
};
|
|
1546
|
-
}
|
|
1547
1670
|
getSocket(socketId) {
|
|
1548
1671
|
return this.#sockets.get(socketId);
|
|
1549
1672
|
}
|
|
@@ -1663,8 +1786,6 @@ var SocketService = class extends Emittery {
|
|
|
1663
1786
|
#cleanup() {
|
|
1664
1787
|
this.#stopHeartbeat();
|
|
1665
1788
|
this.#sockets.clear();
|
|
1666
|
-
this.#userRooms.clear();
|
|
1667
|
-
this.#socketUserRoom.clear();
|
|
1668
1789
|
this.#messageQueue.clear();
|
|
1669
1790
|
this.#messageRates.clear();
|
|
1670
1791
|
this.#socketFinalizations.clear();
|
|
@@ -1731,6 +1852,142 @@ var SocketService = class extends Emittery {
|
|
|
1731
1852
|
}
|
|
1732
1853
|
};
|
|
1733
1854
|
|
|
1855
|
+
// src/channel_pattern.ts
|
|
1856
|
+
var ChannelPattern = class _ChannelPattern {
|
|
1857
|
+
constructor(value) {
|
|
1858
|
+
this.value = value;
|
|
1859
|
+
this.syntax = ChannelPatternSyntax.from(value);
|
|
1860
|
+
}
|
|
1861
|
+
syntax;
|
|
1862
|
+
/**
|
|
1863
|
+
* Parse one runtime channel pattern.
|
|
1864
|
+
*/
|
|
1865
|
+
static from(value) {
|
|
1866
|
+
return new _ChannelPattern(value);
|
|
1867
|
+
}
|
|
1868
|
+
/**
|
|
1869
|
+
* Match one concrete channel name.
|
|
1870
|
+
*/
|
|
1871
|
+
match(channelName) {
|
|
1872
|
+
return this.syntax.match(channelName);
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Return the most specific matching candidate. Static segments have the highest score.
|
|
1876
|
+
* Parameters score above optional parameters. Wildcards have the lowest score.
|
|
1877
|
+
* Insertion order is preserved for ties.
|
|
1878
|
+
*/
|
|
1879
|
+
static firstMatch(channelName, candidates) {
|
|
1880
|
+
const sorted = [...candidates].sort((left, right) => {
|
|
1881
|
+
return _ChannelPattern.#compareSpecificity(left.pattern, right.pattern);
|
|
1882
|
+
});
|
|
1883
|
+
const matched = ChannelPatternSyntax.firstMatch(
|
|
1884
|
+
sorted.map(({ pattern: pattern2 }) => pattern2.syntax),
|
|
1885
|
+
channelName
|
|
1886
|
+
);
|
|
1887
|
+
if (!matched) return null;
|
|
1888
|
+
const { pattern, channel } = sorted[matched.index];
|
|
1889
|
+
return { pattern, channel, params: matched.params };
|
|
1890
|
+
}
|
|
1891
|
+
static #compareSpecificity(left, right) {
|
|
1892
|
+
return left.syntax.compareSpecificity(right.syntax);
|
|
1893
|
+
}
|
|
1894
|
+
};
|
|
1895
|
+
|
|
1896
|
+
// src/channel_router.ts
|
|
1897
|
+
var ChannelRouter = class {
|
|
1898
|
+
constructor(makeChannel = (channel) => new channel(), createMiddlewareRunner) {
|
|
1899
|
+
this.makeChannel = makeChannel;
|
|
1900
|
+
this.createMiddlewareRunner = createMiddlewareRunner;
|
|
1901
|
+
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Registered channels keyed by their pattern.
|
|
1904
|
+
*/
|
|
1905
|
+
#channels = /* @__PURE__ */ new Map();
|
|
1906
|
+
/**
|
|
1907
|
+
* Registers a channel.
|
|
1908
|
+
*/
|
|
1909
|
+
register(channel) {
|
|
1910
|
+
const patternValue = channel.pattern;
|
|
1911
|
+
if (!patternValue) {
|
|
1912
|
+
throw new Error(`Channel ${channel.name} must define a static pattern`);
|
|
1913
|
+
}
|
|
1914
|
+
if (this.#channels.has(patternValue)) {
|
|
1915
|
+
throw new Error(`Channel pattern "${patternValue}" is already registered`);
|
|
1916
|
+
}
|
|
1917
|
+
const pattern = ChannelPattern.from(patternValue);
|
|
1918
|
+
this.#channels.set(pattern.value, { pattern, channel });
|
|
1919
|
+
}
|
|
1920
|
+
/**
|
|
1921
|
+
* Matches a channel name against registered patterns.
|
|
1922
|
+
*/
|
|
1923
|
+
match(channelName) {
|
|
1924
|
+
const matched = ChannelPattern.firstMatch(channelName, this.#channels.values());
|
|
1925
|
+
if (!matched) {
|
|
1926
|
+
return null;
|
|
1927
|
+
}
|
|
1928
|
+
return {
|
|
1929
|
+
channel: matched.channel,
|
|
1930
|
+
pattern: matched.pattern.value,
|
|
1931
|
+
params: matched.params
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
/**
|
|
1935
|
+
* Authorizes a socket before it joins a channel.
|
|
1936
|
+
*/
|
|
1937
|
+
async authorize(socket, channelName) {
|
|
1938
|
+
const matched = this.match(channelName);
|
|
1939
|
+
if (!matched) {
|
|
1940
|
+
return {
|
|
1941
|
+
success: false,
|
|
1942
|
+
error: "Channel not found"
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
const instance = await this.makeChannel(matched.channel);
|
|
1946
|
+
const paramValues = Object.values(matched.params);
|
|
1947
|
+
const ctx = {
|
|
1948
|
+
socket,
|
|
1949
|
+
channel: channelName,
|
|
1950
|
+
params: matched.params,
|
|
1951
|
+
presenceData: void 0,
|
|
1952
|
+
setPresenceData(data) {
|
|
1953
|
+
ctx.presenceData = data;
|
|
1954
|
+
}
|
|
1955
|
+
};
|
|
1956
|
+
try {
|
|
1957
|
+
await instance.$runMiddlewares(ctx, this.createMiddlewareRunner?.());
|
|
1958
|
+
} catch (error) {
|
|
1959
|
+
return {
|
|
1960
|
+
success: false,
|
|
1961
|
+
error: error instanceof SocketResponseError ? error.message : "Authorization failed",
|
|
1962
|
+
cause: error
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1965
|
+
return {
|
|
1966
|
+
success: true,
|
|
1967
|
+
instance,
|
|
1968
|
+
params: matched.params,
|
|
1969
|
+
paramValues,
|
|
1970
|
+
presenceData: ctx.presenceData ?? null
|
|
1971
|
+
};
|
|
1972
|
+
}
|
|
1973
|
+
/**
|
|
1974
|
+
* Lists all registered channels.
|
|
1975
|
+
*/
|
|
1976
|
+
list() {
|
|
1977
|
+
return [...this.#channels.values()].map(({ pattern, channel }) => ({
|
|
1978
|
+
pattern: pattern.value,
|
|
1979
|
+
name: channel.name,
|
|
1980
|
+
options: channel.options
|
|
1981
|
+
}));
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
1984
|
+
* Number of registered channels.
|
|
1985
|
+
*/
|
|
1986
|
+
get size() {
|
|
1987
|
+
return this.#channels.size;
|
|
1988
|
+
}
|
|
1989
|
+
};
|
|
1990
|
+
|
|
1734
1991
|
// providers/socket_provider.ts
|
|
1735
1992
|
var SocketProvider = class {
|
|
1736
1993
|
constructor(app) {
|
|
@@ -1741,7 +1998,16 @@ var SocketProvider = class {
|
|
|
1741
1998
|
*/
|
|
1742
1999
|
register() {
|
|
1743
2000
|
this.app.container.singleton("socket.router", () => {
|
|
1744
|
-
return new ChannelRouter(
|
|
2001
|
+
return new ChannelRouter(
|
|
2002
|
+
(channel) => this.app.container.make(channel),
|
|
2003
|
+
() => {
|
|
2004
|
+
const resolver = this.app.container.createResolver();
|
|
2005
|
+
return async (Middleware, ctx, next) => {
|
|
2006
|
+
const instance = await resolver.make(Middleware);
|
|
2007
|
+
await resolver.call(instance, "handle", [ctx, next]);
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
);
|
|
1745
2011
|
});
|
|
1746
2012
|
this.app.container.singleton("socket.presence", () => {
|
|
1747
2013
|
return new PresenceManager();
|
|
@@ -1809,7 +2075,7 @@ var SocketProvider = class {
|
|
|
1809
2075
|
*/
|
|
1810
2076
|
#createUpgradeContextRunner(config, server) {
|
|
1811
2077
|
const middleware = config.websocket?.middleware ?? [];
|
|
1812
|
-
return async (request, handler) => {
|
|
2078
|
+
return async (request, handler, shortCircuitBeforeMiddleware) => {
|
|
1813
2079
|
const response = new ServerResponse(request);
|
|
1814
2080
|
const adonisRequest = server.createRequest(request, response);
|
|
1815
2081
|
const adonisResponse = server.createResponse(request, response);
|
|
@@ -1818,6 +2084,10 @@ var SocketProvider = class {
|
|
|
1818
2084
|
adonisResponse,
|
|
1819
2085
|
this.app.container.createResolver()
|
|
1820
2086
|
);
|
|
2087
|
+
const earlyResult = await shortCircuitBeforeMiddleware?.(httpContext);
|
|
2088
|
+
if (earlyResult !== void 0) {
|
|
2089
|
+
return earlyResult;
|
|
2090
|
+
}
|
|
1821
2091
|
return this.#runUpgradeMiddleware(server, middleware, httpContext, handler);
|
|
1822
2092
|
};
|
|
1823
2093
|
}
|
|
@@ -1853,13 +2123,6 @@ var SocketProvider = class {
|
|
|
1853
2123
|
*/
|
|
1854
2124
|
async #registerChannels(router) {
|
|
1855
2125
|
const logger = await this.#resolveLogger();
|
|
1856
|
-
const config = this.app.config.get("socket", {});
|
|
1857
|
-
const legacyChannels = config.channels;
|
|
1858
|
-
if (legacyChannels?.patterns) {
|
|
1859
|
-
throw new Error(
|
|
1860
|
-
"Socket channel patterns are managed by the Assembler hook and cannot be configured at runtime"
|
|
1861
|
-
);
|
|
1862
|
-
}
|
|
1863
2126
|
let generated;
|
|
1864
2127
|
try {
|
|
1865
2128
|
generated = await this.app.import("#generated/socket_channels");
|