@rlanz/socket 0.0.1-5 → 0.0.1-7
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 +174 -160
- package/build/{types-C7Q36ryo.d.ts → base_channel-Dr40l7CW.d.ts} +178 -169
- package/build/chunk-2X6WGCH5.js +120 -0
- package/build/chunk-2X6WGCH5.js.map +1 -0
- 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-QNOOQXYY.js +899 -0
- package/build/chunk-QNOOQXYY.js.map +1 -0
- package/build/{chunk-ANT2E3LT.js → chunk-YVC7IMDN.js} +21 -5
- package/build/chunk-YVC7IMDN.js.map +1 -0
- package/build/framework-B-4kGmAw.d.ts +13 -0
- package/build/index-Fg-tkHhP.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 +474 -215
- package/build/providers/socket_provider.js.map +1 -1
- package/build/services/socket.d.ts +2 -2
- package/build/{socket_service-mybqv1hj.d.ts → socket_service-BYEB8LIN.d.ts} +7 -7
- package/build/src/assembler_hook.d.ts +6 -4
- package/build/src/assembler_hook.js +49 -63
- 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 -35
- package/build/src/client/vue.d.ts +17 -5
- 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 +2 -9
- package/build/chunk-ANT2E3LT.js.map +0 -1
- package/build/chunk-B4Y3TNDI.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-CvxgHwbv.d.ts +0 -12
- package/build/src/client/svelte.d.ts +0 -22
- 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-2X6WGCH5.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
|
{
|
|
@@ -1338,43 +1535,21 @@ var SocketService = class extends Emittery {
|
|
|
1338
1535
|
return {
|
|
1339
1536
|
id: raw.id,
|
|
1340
1537
|
user: upgrade.user,
|
|
1538
|
+
getUserOrFail() {
|
|
1539
|
+
if (this.user === void 0) {
|
|
1540
|
+
throw new SocketResponseError("Unauthorized");
|
|
1541
|
+
}
|
|
1542
|
+
return this.user;
|
|
1543
|
+
},
|
|
1341
1544
|
emit: (event, data) => {
|
|
1342
1545
|
this.#sendSerializedEventFrame(connection, serializeFrame({ type: "event", event, data }));
|
|
1343
1546
|
},
|
|
1344
|
-
joinUserRoom: (userId) => {
|
|
1345
|
-
this.#joinUserRoom(raw.id, userRoom(userId));
|
|
1346
|
-
return Promise.resolve();
|
|
1347
|
-
},
|
|
1348
|
-
leaveUserRoom: () => {
|
|
1349
|
-
this.#leaveUserRoom(raw.id);
|
|
1350
|
-
return Promise.resolve();
|
|
1351
|
-
},
|
|
1352
1547
|
disconnect() {
|
|
1353
1548
|
connection.close();
|
|
1354
1549
|
},
|
|
1355
1550
|
raw
|
|
1356
1551
|
};
|
|
1357
1552
|
}
|
|
1358
|
-
#joinUserRoom(socketId, room) {
|
|
1359
|
-
this.#leaveUserRoom(socketId);
|
|
1360
|
-
if (!this.#userRooms.has(room)) {
|
|
1361
|
-
this.#userRooms.set(room, /* @__PURE__ */ new Set());
|
|
1362
|
-
}
|
|
1363
|
-
this.#userRooms.get(room).add(socketId);
|
|
1364
|
-
this.#socketUserRoom.set(socketId, room);
|
|
1365
|
-
}
|
|
1366
|
-
#leaveUserRoom(socketId) {
|
|
1367
|
-
const room = this.#socketUserRoom.get(socketId);
|
|
1368
|
-
if (!room) {
|
|
1369
|
-
return;
|
|
1370
|
-
}
|
|
1371
|
-
const sockets = this.#userRooms.get(room);
|
|
1372
|
-
sockets?.delete(socketId);
|
|
1373
|
-
if (sockets?.size === 0) {
|
|
1374
|
-
this.#userRooms.delete(room);
|
|
1375
|
-
}
|
|
1376
|
-
this.#socketUserRoom.delete(socketId);
|
|
1377
|
-
}
|
|
1378
1553
|
to(channel) {
|
|
1379
1554
|
return {
|
|
1380
1555
|
emit: (event, data) => {
|
|
@@ -1393,14 +1568,6 @@ var SocketService = class extends Emittery {
|
|
|
1393
1568
|
})
|
|
1394
1569
|
};
|
|
1395
1570
|
}
|
|
1396
|
-
toUser(userId) {
|
|
1397
|
-
const room = userRoom(userId);
|
|
1398
|
-
return {
|
|
1399
|
-
emit: (event, data) => {
|
|
1400
|
-
this.#broadcastSink.dispatch({ target: "user", userId, room, event, data });
|
|
1401
|
-
}
|
|
1402
|
-
};
|
|
1403
|
-
}
|
|
1404
1571
|
broadcast(event, data) {
|
|
1405
1572
|
this.#broadcastSink.dispatch({ target: "global", event, data });
|
|
1406
1573
|
}
|
|
@@ -1436,20 +1603,6 @@ var SocketService = class extends Emittery {
|
|
|
1436
1603
|
);
|
|
1437
1604
|
return;
|
|
1438
1605
|
}
|
|
1439
|
-
case "user": {
|
|
1440
|
-
const room = emission.room;
|
|
1441
|
-
const serializedFrame = serializeFrame({
|
|
1442
|
-
type: "event",
|
|
1443
|
-
event: emission.event,
|
|
1444
|
-
data: emission.data
|
|
1445
|
-
});
|
|
1446
|
-
this.#bus?.publishUser(room, emission.event, emission.data);
|
|
1447
|
-
this.#traceBroadcast(
|
|
1448
|
-
{ target: "user", room, event: emission.event, via: "local" },
|
|
1449
|
-
() => this.#emitToUserRoomLocally(room, emission.event, emission.data, serializedFrame)
|
|
1450
|
-
);
|
|
1451
|
-
return;
|
|
1452
|
-
}
|
|
1453
1606
|
case "global": {
|
|
1454
1607
|
const serializedFrame = serializeFrame({
|
|
1455
1608
|
type: "event",
|
|
@@ -1489,16 +1642,6 @@ var SocketService = class extends Emittery {
|
|
|
1489
1642
|
}
|
|
1490
1643
|
return delivered;
|
|
1491
1644
|
}
|
|
1492
|
-
#emitToUserRoomLocally(room, event, data, serializedFrame = serializeFrame({ type: "event", event, data })) {
|
|
1493
|
-
let delivered = 0;
|
|
1494
|
-
for (const socketId of this.#userRooms.get(room) ?? []) {
|
|
1495
|
-
const socket = this.#sockets.get(socketId);
|
|
1496
|
-
if (socket && this.#sendSerializedEventFrame(socket.raw.connection, serializedFrame)) {
|
|
1497
|
-
delivered += 1;
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
return delivered;
|
|
1501
|
-
}
|
|
1502
1645
|
#broadcastLocally(event, data, serializedFrame = serializeFrame({ type: "event", event, data })) {
|
|
1503
1646
|
let delivered = 0;
|
|
1504
1647
|
for (const socket of this.#sockets.values()) {
|
|
@@ -1521,23 +1664,9 @@ var SocketService = class extends Emittery {
|
|
|
1521
1664
|
}
|
|
1522
1665
|
#getLocalPresenceSockets(channel) {
|
|
1523
1666
|
return (this.#presenceManager?.getLocalSockets(channel) ?? []).map((socket) => {
|
|
1524
|
-
return
|
|
1667
|
+
return PresenceSocketFrame.fromSocket(channel, socket).toTransport();
|
|
1525
1668
|
});
|
|
1526
1669
|
}
|
|
1527
|
-
#serializePresenceSocket(channel, socket) {
|
|
1528
|
-
const user = socket.data[PRESENCE_DATA_KEY][channel];
|
|
1529
|
-
return {
|
|
1530
|
-
id: socket.id,
|
|
1531
|
-
data: {
|
|
1532
|
-
[PRESENCE_DATA_KEY]: {
|
|
1533
|
-
[channel]: {
|
|
1534
|
-
...user,
|
|
1535
|
-
joinedAt: user.joinedAt.toISOString()
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
};
|
|
1540
|
-
}
|
|
1541
1670
|
getSocket(socketId) {
|
|
1542
1671
|
return this.#sockets.get(socketId);
|
|
1543
1672
|
}
|
|
@@ -1657,8 +1786,6 @@ var SocketService = class extends Emittery {
|
|
|
1657
1786
|
#cleanup() {
|
|
1658
1787
|
this.#stopHeartbeat();
|
|
1659
1788
|
this.#sockets.clear();
|
|
1660
|
-
this.#userRooms.clear();
|
|
1661
|
-
this.#socketUserRoom.clear();
|
|
1662
1789
|
this.#messageQueue.clear();
|
|
1663
1790
|
this.#messageRates.clear();
|
|
1664
1791
|
this.#socketFinalizations.clear();
|
|
@@ -1725,6 +1852,141 @@ var SocketService = class extends Emittery {
|
|
|
1725
1852
|
}
|
|
1726
1853
|
};
|
|
1727
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()) {
|
|
1899
|
+
this.makeChannel = makeChannel;
|
|
1900
|
+
}
|
|
1901
|
+
/**
|
|
1902
|
+
* Registered channels keyed by their pattern.
|
|
1903
|
+
*/
|
|
1904
|
+
#channels = /* @__PURE__ */ new Map();
|
|
1905
|
+
/**
|
|
1906
|
+
* Registers a channel.
|
|
1907
|
+
*/
|
|
1908
|
+
register(channel) {
|
|
1909
|
+
const patternValue = channel.pattern;
|
|
1910
|
+
if (!patternValue) {
|
|
1911
|
+
throw new Error(`Channel ${channel.name} must define a static pattern`);
|
|
1912
|
+
}
|
|
1913
|
+
if (this.#channels.has(patternValue)) {
|
|
1914
|
+
throw new Error(`Channel pattern "${patternValue}" is already registered`);
|
|
1915
|
+
}
|
|
1916
|
+
const pattern = ChannelPattern.from(patternValue);
|
|
1917
|
+
this.#channels.set(pattern.value, { pattern, channel });
|
|
1918
|
+
}
|
|
1919
|
+
/**
|
|
1920
|
+
* Matches a channel name against registered patterns.
|
|
1921
|
+
*/
|
|
1922
|
+
match(channelName) {
|
|
1923
|
+
const matched = ChannelPattern.firstMatch(channelName, this.#channels.values());
|
|
1924
|
+
if (!matched) {
|
|
1925
|
+
return null;
|
|
1926
|
+
}
|
|
1927
|
+
return {
|
|
1928
|
+
channel: matched.channel,
|
|
1929
|
+
pattern: matched.pattern.value,
|
|
1930
|
+
params: matched.params
|
|
1931
|
+
};
|
|
1932
|
+
}
|
|
1933
|
+
/**
|
|
1934
|
+
* Authorizes a socket before it joins a channel.
|
|
1935
|
+
*/
|
|
1936
|
+
async authorize(socket, channelName) {
|
|
1937
|
+
const matched = this.match(channelName);
|
|
1938
|
+
if (!matched) {
|
|
1939
|
+
return {
|
|
1940
|
+
success: false,
|
|
1941
|
+
error: "Channel not found"
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
const instance = await this.makeChannel(matched.channel);
|
|
1945
|
+
const paramValues = Object.values(matched.params);
|
|
1946
|
+
const ctx = {
|
|
1947
|
+
socket,
|
|
1948
|
+
channel: channelName,
|
|
1949
|
+
params: matched.params,
|
|
1950
|
+
presenceData: void 0,
|
|
1951
|
+
setPresenceData(data) {
|
|
1952
|
+
ctx.presenceData = data;
|
|
1953
|
+
}
|
|
1954
|
+
};
|
|
1955
|
+
try {
|
|
1956
|
+
await instance.$runMiddlewares(ctx);
|
|
1957
|
+
} catch (error) {
|
|
1958
|
+
return {
|
|
1959
|
+
success: false,
|
|
1960
|
+
error: error instanceof SocketResponseError ? error.message : "Authorization failed",
|
|
1961
|
+
cause: error
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1964
|
+
return {
|
|
1965
|
+
success: true,
|
|
1966
|
+
instance,
|
|
1967
|
+
params: matched.params,
|
|
1968
|
+
paramValues,
|
|
1969
|
+
presenceData: ctx.presenceData ?? null
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* Lists all registered channels.
|
|
1974
|
+
*/
|
|
1975
|
+
list() {
|
|
1976
|
+
return [...this.#channels.values()].map(({ pattern, channel }) => ({
|
|
1977
|
+
pattern: pattern.value,
|
|
1978
|
+
name: channel.name,
|
|
1979
|
+
options: channel.options
|
|
1980
|
+
}));
|
|
1981
|
+
}
|
|
1982
|
+
/**
|
|
1983
|
+
* Number of registered channels.
|
|
1984
|
+
*/
|
|
1985
|
+
get size() {
|
|
1986
|
+
return this.#channels.size;
|
|
1987
|
+
}
|
|
1988
|
+
};
|
|
1989
|
+
|
|
1728
1990
|
// providers/socket_provider.ts
|
|
1729
1991
|
var SocketProvider = class {
|
|
1730
1992
|
constructor(app) {
|
|
@@ -1735,7 +1997,7 @@ var SocketProvider = class {
|
|
|
1735
1997
|
*/
|
|
1736
1998
|
register() {
|
|
1737
1999
|
this.app.container.singleton("socket.router", () => {
|
|
1738
|
-
return new ChannelRouter();
|
|
2000
|
+
return new ChannelRouter((channel) => this.app.container.make(channel));
|
|
1739
2001
|
});
|
|
1740
2002
|
this.app.container.singleton("socket.presence", () => {
|
|
1741
2003
|
return new PresenceManager();
|
|
@@ -1803,7 +2065,7 @@ var SocketProvider = class {
|
|
|
1803
2065
|
*/
|
|
1804
2066
|
#createUpgradeContextRunner(config, server) {
|
|
1805
2067
|
const middleware = config.websocket?.middleware ?? [];
|
|
1806
|
-
return async (request, handler) => {
|
|
2068
|
+
return async (request, handler, shortCircuitBeforeMiddleware) => {
|
|
1807
2069
|
const response = new ServerResponse(request);
|
|
1808
2070
|
const adonisRequest = server.createRequest(request, response);
|
|
1809
2071
|
const adonisResponse = server.createResponse(request, response);
|
|
@@ -1812,6 +2074,10 @@ var SocketProvider = class {
|
|
|
1812
2074
|
adonisResponse,
|
|
1813
2075
|
this.app.container.createResolver()
|
|
1814
2076
|
);
|
|
2077
|
+
const earlyResult = await shortCircuitBeforeMiddleware?.(httpContext);
|
|
2078
|
+
if (earlyResult !== void 0) {
|
|
2079
|
+
return earlyResult;
|
|
2080
|
+
}
|
|
1815
2081
|
return this.#runUpgradeMiddleware(server, middleware, httpContext, handler);
|
|
1816
2082
|
};
|
|
1817
2083
|
}
|
|
@@ -1847,13 +2113,6 @@ var SocketProvider = class {
|
|
|
1847
2113
|
*/
|
|
1848
2114
|
async #registerChannels(router) {
|
|
1849
2115
|
const logger = await this.#resolveLogger();
|
|
1850
|
-
const config = this.app.config.get("socket", {});
|
|
1851
|
-
const legacyChannels = config.channels;
|
|
1852
|
-
if (legacyChannels?.patterns) {
|
|
1853
|
-
throw new Error(
|
|
1854
|
-
"Socket channel patterns are managed by the Assembler hook and cannot be configured at runtime"
|
|
1855
|
-
);
|
|
1856
|
-
}
|
|
1857
2116
|
let generated;
|
|
1858
2117
|
try {
|
|
1859
2118
|
generated = await this.app.import("#generated/socket_channels");
|