前端代码

This commit is contained in:
ChloeChen0423
2025-05-12 16:42:36 +09:00
commit 7c63f2f07b
4531 changed files with 656010 additions and 0 deletions

20
node_modules/socket.io-adapter/LICENSE generated vendored Normal file
View File

@ -0,0 +1,20 @@
(The MIT License)
Copyright (c) 2014 Guillermo Rauch <guillermo@learnboost.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the 'Software'), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

23
node_modules/socket.io-adapter/Readme.md generated vendored Normal file
View File

@ -0,0 +1,23 @@
# socket.io-adapter
Default socket.io in-memory adapter class.
Compatibility table:
| Adapter version | Socket.IO server version |
|-----------------| ------------------------ |
| 1.x.x | 1.x.x / 2.x.x |
| 2.x.x | 3.x.x |
## How to use
This module is not intended for end-user usage, but can be used as an
interface to inherit from other adapters you might want to build.
As an example of an adapter that builds on top of this, please take a look
at [socket.io-redis](https://github.com/learnboost/socket.io-redis).
## License
MIT

View File

@ -0,0 +1,201 @@
import { Adapter } from "./in-memory-adapter";
import type { BroadcastFlags, BroadcastOptions, Room } from "./in-memory-adapter";
type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never;
/**
* The unique ID of a server
*/
export type ServerId = string;
/**
* The unique ID of a message (for the connection state recovery feature)
*/
export type Offset = string;
export interface ClusterAdapterOptions {
/**
* The number of ms between two heartbeats.
* @default 5_000
*/
heartbeatInterval?: number;
/**
* The number of ms without heartbeat before we consider a node down.
* @default 10_000
*/
heartbeatTimeout?: number;
}
export declare enum MessageType {
INITIAL_HEARTBEAT = 1,
HEARTBEAT = 2,
BROADCAST = 3,
SOCKETS_JOIN = 4,
SOCKETS_LEAVE = 5,
DISCONNECT_SOCKETS = 6,
FETCH_SOCKETS = 7,
FETCH_SOCKETS_RESPONSE = 8,
SERVER_SIDE_EMIT = 9,
SERVER_SIDE_EMIT_RESPONSE = 10,
BROADCAST_CLIENT_COUNT = 11,
BROADCAST_ACK = 12,
ADAPTER_CLOSE = 13
}
export type ClusterMessage = {
uid: ServerId;
nsp: string;
} & ({
type: MessageType.INITIAL_HEARTBEAT | MessageType.HEARTBEAT | MessageType.ADAPTER_CLOSE;
} | {
type: MessageType.BROADCAST;
data: {
opts: {
rooms: string[];
except: string[];
flags: BroadcastFlags;
};
packet: unknown;
requestId?: string;
};
} | {
type: MessageType.SOCKETS_JOIN | MessageType.SOCKETS_LEAVE;
data: {
opts: {
rooms: string[];
except: string[];
flags: BroadcastFlags;
};
rooms: string[];
};
} | {
type: MessageType.DISCONNECT_SOCKETS;
data: {
opts: {
rooms: string[];
except: string[];
flags: BroadcastFlags;
};
close?: boolean;
};
} | {
type: MessageType.FETCH_SOCKETS;
data: {
opts: {
rooms: string[];
except: string[];
flags: BroadcastFlags;
};
requestId: string;
};
} | {
type: MessageType.SERVER_SIDE_EMIT;
data: {
requestId?: string;
packet: any[];
};
});
export type ClusterResponse = {
uid: ServerId;
nsp: string;
} & ({
type: MessageType.FETCH_SOCKETS_RESPONSE;
data: {
requestId: string;
sockets: unknown[];
};
} | {
type: MessageType.SERVER_SIDE_EMIT_RESPONSE;
data: {
requestId: string;
packet: unknown;
};
} | {
type: MessageType.BROADCAST_CLIENT_COUNT;
data: {
requestId: string;
clientCount: number;
};
} | {
type: MessageType.BROADCAST_ACK;
data: {
requestId: string;
packet: unknown;
};
});
/**
* A cluster-ready adapter. Any extending class must:
*
* - implement {@link ClusterAdapter#doPublish} and {@link ClusterAdapter#doPublishResponse}
* - call {@link ClusterAdapter#onMessage} and {@link ClusterAdapter#onResponse}
*/
export declare abstract class ClusterAdapter extends Adapter {
protected readonly uid: ServerId;
private requests;
private ackRequests;
protected constructor(nsp: any);
/**
* Called when receiving a message from another member of the cluster.
*
* @param message
* @param offset
* @protected
*/
protected onMessage(message: ClusterMessage, offset?: string): void;
/**
* Called when receiving a response from another member of the cluster.
*
* @param response
* @protected
*/
protected onResponse(response: ClusterResponse): void;
broadcast(packet: any, opts: BroadcastOptions): Promise<void>;
/**
* Adds an offset at the end of the data array in order to allow the client to receive any missed packets when it
* reconnects after a temporary disconnection.
*
* @param packet
* @param opts
* @param offset
* @private
*/
private addOffsetIfNecessary;
broadcastWithAck(packet: any, opts: BroadcastOptions, clientCountCallback: (clientCount: number) => void, ack: (...args: any[]) => void): void;
addSockets(opts: BroadcastOptions, rooms: Room[]): Promise<void>;
delSockets(opts: BroadcastOptions, rooms: Room[]): Promise<void>;
disconnectSockets(opts: BroadcastOptions, close: boolean): Promise<void>;
fetchSockets(opts: BroadcastOptions): Promise<any[]>;
serverSideEmit(packet: any[]): Promise<any>;
protected publish(message: DistributiveOmit<ClusterMessage, "nsp" | "uid">): void;
protected publishAndReturnOffset(message: DistributiveOmit<ClusterMessage, "nsp" | "uid">): Promise<string>;
/**
* Send a message to the other members of the cluster.
*
* @param message
* @protected
* @return an offset, if applicable
*/
protected abstract doPublish(message: ClusterMessage): Promise<Offset>;
protected publishResponse(requesterUid: ServerId, response: Omit<ClusterResponse, "nsp" | "uid">): void;
/**
* Send a response to the given member of the cluster.
*
* @param requesterUid
* @param response
* @protected
*/
protected abstract doPublishResponse(requesterUid: ServerId, response: ClusterResponse): Promise<void>;
}
export declare abstract class ClusterAdapterWithHeartbeat extends ClusterAdapter {
private readonly _opts;
private heartbeatTimer;
private nodesMap;
private readonly cleanupTimer;
private customRequests;
protected constructor(nsp: any, opts: ClusterAdapterOptions);
init(): void;
private scheduleHeartbeat;
close(): void;
onMessage(message: ClusterMessage, offset?: string): void;
serverCount(): Promise<number>;
publish(message: DistributiveOmit<ClusterMessage, "nsp" | "uid">): void;
serverSideEmit(packet: any[]): Promise<any>;
fetchSockets(opts: BroadcastOptions): Promise<any[]>;
onResponse(response: ClusterResponse): void;
private removeNode;
}
export {};

674
node_modules/socket.io-adapter/dist/cluster-adapter.js generated vendored Normal file
View File

@ -0,0 +1,674 @@
"use strict";
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClusterAdapterWithHeartbeat = exports.ClusterAdapter = exports.MessageType = void 0;
const in_memory_adapter_1 = require("./in-memory-adapter");
const debug_1 = require("debug");
const crypto_1 = require("crypto");
const debug = (0, debug_1.debug)("socket.io-adapter");
const EMITTER_UID = "emitter";
const DEFAULT_TIMEOUT = 5000;
function randomId() {
return (0, crypto_1.randomBytes)(8).toString("hex");
}
var MessageType;
(function (MessageType) {
MessageType[MessageType["INITIAL_HEARTBEAT"] = 1] = "INITIAL_HEARTBEAT";
MessageType[MessageType["HEARTBEAT"] = 2] = "HEARTBEAT";
MessageType[MessageType["BROADCAST"] = 3] = "BROADCAST";
MessageType[MessageType["SOCKETS_JOIN"] = 4] = "SOCKETS_JOIN";
MessageType[MessageType["SOCKETS_LEAVE"] = 5] = "SOCKETS_LEAVE";
MessageType[MessageType["DISCONNECT_SOCKETS"] = 6] = "DISCONNECT_SOCKETS";
MessageType[MessageType["FETCH_SOCKETS"] = 7] = "FETCH_SOCKETS";
MessageType[MessageType["FETCH_SOCKETS_RESPONSE"] = 8] = "FETCH_SOCKETS_RESPONSE";
MessageType[MessageType["SERVER_SIDE_EMIT"] = 9] = "SERVER_SIDE_EMIT";
MessageType[MessageType["SERVER_SIDE_EMIT_RESPONSE"] = 10] = "SERVER_SIDE_EMIT_RESPONSE";
MessageType[MessageType["BROADCAST_CLIENT_COUNT"] = 11] = "BROADCAST_CLIENT_COUNT";
MessageType[MessageType["BROADCAST_ACK"] = 12] = "BROADCAST_ACK";
MessageType[MessageType["ADAPTER_CLOSE"] = 13] = "ADAPTER_CLOSE";
})(MessageType = exports.MessageType || (exports.MessageType = {}));
function encodeOptions(opts) {
return {
rooms: [...opts.rooms],
except: [...opts.except],
flags: opts.flags,
};
}
function decodeOptions(opts) {
return {
rooms: new Set(opts.rooms),
except: new Set(opts.except),
flags: opts.flags,
};
}
/**
* A cluster-ready adapter. Any extending class must:
*
* - implement {@link ClusterAdapter#doPublish} and {@link ClusterAdapter#doPublishResponse}
* - call {@link ClusterAdapter#onMessage} and {@link ClusterAdapter#onResponse}
*/
class ClusterAdapter extends in_memory_adapter_1.Adapter {
constructor(nsp) {
super(nsp);
this.requests = new Map();
this.ackRequests = new Map();
this.uid = randomId();
}
/**
* Called when receiving a message from another member of the cluster.
*
* @param message
* @param offset
* @protected
*/
onMessage(message, offset) {
if (message.uid === this.uid) {
return debug("[%s] ignore message from self", this.uid);
}
debug("[%s] new event of type %d from %s", this.uid, message.type, message.uid);
switch (message.type) {
case MessageType.BROADCAST: {
const withAck = message.data.requestId !== undefined;
if (withAck) {
super.broadcastWithAck(message.data.packet, decodeOptions(message.data.opts), (clientCount) => {
debug("[%s] waiting for %d client acknowledgements", this.uid, clientCount);
this.publishResponse(message.uid, {
type: MessageType.BROADCAST_CLIENT_COUNT,
data: {
requestId: message.data.requestId,
clientCount,
},
});
}, (arg) => {
debug("[%s] received acknowledgement with value %j", this.uid, arg);
this.publishResponse(message.uid, {
type: MessageType.BROADCAST_ACK,
data: {
requestId: message.data.requestId,
packet: arg,
},
});
});
}
else {
const packet = message.data.packet;
const opts = decodeOptions(message.data.opts);
this.addOffsetIfNecessary(packet, opts, offset);
super.broadcast(packet, opts);
}
break;
}
case MessageType.SOCKETS_JOIN:
super.addSockets(decodeOptions(message.data.opts), message.data.rooms);
break;
case MessageType.SOCKETS_LEAVE:
super.delSockets(decodeOptions(message.data.opts), message.data.rooms);
break;
case MessageType.DISCONNECT_SOCKETS:
super.disconnectSockets(decodeOptions(message.data.opts), message.data.close);
break;
case MessageType.FETCH_SOCKETS: {
debug("[%s] calling fetchSockets with opts %j", this.uid, message.data.opts);
super
.fetchSockets(decodeOptions(message.data.opts))
.then((localSockets) => {
this.publishResponse(message.uid, {
type: MessageType.FETCH_SOCKETS_RESPONSE,
data: {
requestId: message.data.requestId,
sockets: localSockets.map((socket) => {
// remove sessionStore from handshake, as it may contain circular references
const _a = socket.handshake, { sessionStore } = _a, handshake = __rest(_a, ["sessionStore"]);
return {
id: socket.id,
handshake,
rooms: [...socket.rooms],
data: socket.data,
};
}),
},
});
});
break;
}
case MessageType.SERVER_SIDE_EMIT: {
const packet = message.data.packet;
const withAck = message.data.requestId !== undefined;
if (!withAck) {
this.nsp._onServerSideEmit(packet);
return;
}
let called = false;
const callback = (arg) => {
// only one argument is expected
if (called) {
return;
}
called = true;
debug("[%s] calling acknowledgement with %j", this.uid, arg);
this.publishResponse(message.uid, {
type: MessageType.SERVER_SIDE_EMIT_RESPONSE,
data: {
requestId: message.data.requestId,
packet: arg,
},
});
};
this.nsp._onServerSideEmit([...packet, callback]);
break;
}
// @ts-ignore
case MessageType.BROADCAST_CLIENT_COUNT:
// @ts-ignore
case MessageType.BROADCAST_ACK:
// @ts-ignore
case MessageType.FETCH_SOCKETS_RESPONSE:
// @ts-ignore
case MessageType.SERVER_SIDE_EMIT_RESPONSE:
// extending classes may not make a distinction between a ClusterMessage and a ClusterResponse payload and may
// always call the onMessage() method
this.onResponse(message);
break;
default:
debug("[%s] unknown message type: %s", this.uid, message.type);
}
}
/**
* Called when receiving a response from another member of the cluster.
*
* @param response
* @protected
*/
onResponse(response) {
var _a, _b;
const requestId = response.data.requestId;
debug("[%s] received response %s to request %s", this.uid, response.type, requestId);
switch (response.type) {
case MessageType.BROADCAST_CLIENT_COUNT: {
(_a = this.ackRequests
.get(requestId)) === null || _a === void 0 ? void 0 : _a.clientCountCallback(response.data.clientCount);
break;
}
case MessageType.BROADCAST_ACK: {
(_b = this.ackRequests.get(requestId)) === null || _b === void 0 ? void 0 : _b.ack(response.data.packet);
break;
}
case MessageType.FETCH_SOCKETS_RESPONSE: {
const request = this.requests.get(requestId);
if (!request) {
return;
}
request.current++;
response.data.sockets.forEach((socket) => request.responses.push(socket));
if (request.current === request.expected) {
clearTimeout(request.timeout);
request.resolve(request.responses);
this.requests.delete(requestId);
}
break;
}
case MessageType.SERVER_SIDE_EMIT_RESPONSE: {
const request = this.requests.get(requestId);
if (!request) {
return;
}
request.current++;
request.responses.push(response.data.packet);
if (request.current === request.expected) {
clearTimeout(request.timeout);
request.resolve(null, request.responses);
this.requests.delete(requestId);
}
break;
}
default:
// @ts-ignore
debug("[%s] unknown response type: %s", this.uid, response.type);
}
}
async broadcast(packet, opts) {
var _a;
const onlyLocal = (_a = opts.flags) === null || _a === void 0 ? void 0 : _a.local;
if (!onlyLocal) {
try {
const offset = await this.publishAndReturnOffset({
type: MessageType.BROADCAST,
data: {
packet,
opts: encodeOptions(opts),
},
});
this.addOffsetIfNecessary(packet, opts, offset);
}
catch (e) {
return debug("[%s] error while broadcasting message: %s", this.uid, e.message);
}
}
super.broadcast(packet, opts);
}
/**
* Adds an offset at the end of the data array in order to allow the client to receive any missed packets when it
* reconnects after a temporary disconnection.
*
* @param packet
* @param opts
* @param offset
* @private
*/
addOffsetIfNecessary(packet, opts, offset) {
var _a;
if (!this.nsp.server.opts.connectionStateRecovery) {
return;
}
const isEventPacket = packet.type === 2;
// packets with acknowledgement are not stored because the acknowledgement function cannot be serialized and
// restored on another server upon reconnection
const withoutAcknowledgement = packet.id === undefined;
const notVolatile = ((_a = opts.flags) === null || _a === void 0 ? void 0 : _a.volatile) === undefined;
if (isEventPacket && withoutAcknowledgement && notVolatile) {
packet.data.push(offset);
}
}
broadcastWithAck(packet, opts, clientCountCallback, ack) {
var _a;
const onlyLocal = (_a = opts === null || opts === void 0 ? void 0 : opts.flags) === null || _a === void 0 ? void 0 : _a.local;
if (!onlyLocal) {
const requestId = randomId();
this.ackRequests.set(requestId, {
clientCountCallback,
ack,
});
this.publish({
type: MessageType.BROADCAST,
data: {
packet,
requestId,
opts: encodeOptions(opts),
},
});
// we have no way to know at this level whether the server has received an acknowledgement from each client, so we
// will simply clean up the ackRequests map after the given delay
setTimeout(() => {
this.ackRequests.delete(requestId);
}, opts.flags.timeout);
}
super.broadcastWithAck(packet, opts, clientCountCallback, ack);
}
async addSockets(opts, rooms) {
var _a;
const onlyLocal = (_a = opts.flags) === null || _a === void 0 ? void 0 : _a.local;
if (!onlyLocal) {
try {
await this.publishAndReturnOffset({
type: MessageType.SOCKETS_JOIN,
data: {
opts: encodeOptions(opts),
rooms,
},
});
}
catch (e) {
debug("[%s] error while publishing message: %s", this.uid, e.message);
}
}
super.addSockets(opts, rooms);
}
async delSockets(opts, rooms) {
var _a;
const onlyLocal = (_a = opts.flags) === null || _a === void 0 ? void 0 : _a.local;
if (!onlyLocal) {
try {
await this.publishAndReturnOffset({
type: MessageType.SOCKETS_LEAVE,
data: {
opts: encodeOptions(opts),
rooms,
},
});
}
catch (e) {
debug("[%s] error while publishing message: %s", this.uid, e.message);
}
}
super.delSockets(opts, rooms);
}
async disconnectSockets(opts, close) {
var _a;
const onlyLocal = (_a = opts.flags) === null || _a === void 0 ? void 0 : _a.local;
if (!onlyLocal) {
try {
await this.publishAndReturnOffset({
type: MessageType.DISCONNECT_SOCKETS,
data: {
opts: encodeOptions(opts),
close,
},
});
}
catch (e) {
debug("[%s] error while publishing message: %s", this.uid, e.message);
}
}
super.disconnectSockets(opts, close);
}
async fetchSockets(opts) {
var _a;
const [localSockets, serverCount] = await Promise.all([
super.fetchSockets(opts),
this.serverCount(),
]);
const expectedResponseCount = serverCount - 1;
if (((_a = opts.flags) === null || _a === void 0 ? void 0 : _a.local) || expectedResponseCount <= 0) {
return localSockets;
}
const requestId = randomId();
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
const storedRequest = this.requests.get(requestId);
if (storedRequest) {
reject(new Error(`timeout reached: only ${storedRequest.current} responses received out of ${storedRequest.expected}`));
this.requests.delete(requestId);
}
}, opts.flags.timeout || DEFAULT_TIMEOUT);
const storedRequest = {
type: MessageType.FETCH_SOCKETS,
resolve,
timeout,
current: 0,
expected: expectedResponseCount,
responses: localSockets,
};
this.requests.set(requestId, storedRequest);
this.publish({
type: MessageType.FETCH_SOCKETS,
data: {
opts: encodeOptions(opts),
requestId,
},
});
});
}
async serverSideEmit(packet) {
const withAck = typeof packet[packet.length - 1] === "function";
if (!withAck) {
return this.publish({
type: MessageType.SERVER_SIDE_EMIT,
data: {
packet,
},
});
}
const ack = packet.pop();
const expectedResponseCount = (await this.serverCount()) - 1;
debug('[%s] waiting for %d responses to "serverSideEmit" request', this.uid, expectedResponseCount);
if (expectedResponseCount <= 0) {
return ack(null, []);
}
const requestId = randomId();
const timeout = setTimeout(() => {
const storedRequest = this.requests.get(requestId);
if (storedRequest) {
ack(new Error(`timeout reached: only ${storedRequest.current} responses received out of ${storedRequest.expected}`), storedRequest.responses);
this.requests.delete(requestId);
}
}, DEFAULT_TIMEOUT);
const storedRequest = {
type: MessageType.SERVER_SIDE_EMIT,
resolve: ack,
timeout,
current: 0,
expected: expectedResponseCount,
responses: [],
};
this.requests.set(requestId, storedRequest);
this.publish({
type: MessageType.SERVER_SIDE_EMIT,
data: {
requestId,
packet,
},
});
}
publish(message) {
this.publishAndReturnOffset(message).catch((err) => {
debug("[%s] error while publishing message: %s", this.uid, err);
});
}
publishAndReturnOffset(message) {
message.uid = this.uid;
message.nsp = this.nsp.name;
return this.doPublish(message);
}
publishResponse(requesterUid, response) {
response.uid = this.uid;
response.nsp = this.nsp.name;
this.doPublishResponse(requesterUid, response).catch((err) => {
debug("[%s] error while publishing response: %s", this.uid, err);
});
}
}
exports.ClusterAdapter = ClusterAdapter;
class ClusterAdapterWithHeartbeat extends ClusterAdapter {
constructor(nsp, opts) {
super(nsp);
this.nodesMap = new Map(); // uid => timestamp of last message
this.customRequests = new Map();
this._opts = Object.assign({
heartbeatInterval: 5000,
heartbeatTimeout: 10000,
}, opts);
this.cleanupTimer = setInterval(() => {
const now = Date.now();
this.nodesMap.forEach((lastSeen, uid) => {
const nodeSeemsDown = now - lastSeen > this._opts.heartbeatTimeout;
if (nodeSeemsDown) {
debug("[%s] node %s seems down", this.uid, uid);
this.removeNode(uid);
}
});
}, 1000);
}
init() {
this.publish({
type: MessageType.INITIAL_HEARTBEAT,
});
}
scheduleHeartbeat() {
if (this.heartbeatTimer) {
this.heartbeatTimer.refresh();
}
else {
this.heartbeatTimer = setTimeout(() => {
this.publish({
type: MessageType.HEARTBEAT,
});
}, this._opts.heartbeatInterval);
}
}
close() {
this.publish({
type: MessageType.ADAPTER_CLOSE,
});
clearTimeout(this.heartbeatTimer);
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
}
}
onMessage(message, offset) {
if (message.uid === this.uid) {
return debug("[%s] ignore message from self", this.uid);
}
if (message.uid && message.uid !== EMITTER_UID) {
// we track the UID of each sender, in order to know how many servers there are in the cluster
this.nodesMap.set(message.uid, Date.now());
}
debug("[%s] new event of type %d from %s", this.uid, message.type, message.uid);
switch (message.type) {
case MessageType.INITIAL_HEARTBEAT:
this.publish({
type: MessageType.HEARTBEAT,
});
break;
case MessageType.HEARTBEAT:
// nothing to do
break;
case MessageType.ADAPTER_CLOSE:
this.removeNode(message.uid);
break;
default:
super.onMessage(message, offset);
}
}
serverCount() {
return Promise.resolve(1 + this.nodesMap.size);
}
publish(message) {
this.scheduleHeartbeat();
return super.publish(message);
}
async serverSideEmit(packet) {
const withAck = typeof packet[packet.length - 1] === "function";
if (!withAck) {
return this.publish({
type: MessageType.SERVER_SIDE_EMIT,
data: {
packet,
},
});
}
const ack = packet.pop();
const expectedResponseCount = this.nodesMap.size;
debug('[%s] waiting for %d responses to "serverSideEmit" request', this.uid, expectedResponseCount);
if (expectedResponseCount <= 0) {
return ack(null, []);
}
const requestId = randomId();
const timeout = setTimeout(() => {
const storedRequest = this.customRequests.get(requestId);
if (storedRequest) {
ack(new Error(`timeout reached: missing ${storedRequest.missingUids.size} responses`), storedRequest.responses);
this.customRequests.delete(requestId);
}
}, DEFAULT_TIMEOUT);
const storedRequest = {
type: MessageType.SERVER_SIDE_EMIT,
resolve: ack,
timeout,
missingUids: new Set([...this.nodesMap.keys()]),
responses: [],
};
this.customRequests.set(requestId, storedRequest);
this.publish({
type: MessageType.SERVER_SIDE_EMIT,
data: {
requestId,
packet,
},
});
}
async fetchSockets(opts) {
var _a;
const [localSockets, serverCount] = await Promise.all([
super.fetchSockets({
rooms: opts.rooms,
except: opts.except,
flags: {
local: true,
},
}),
this.serverCount(),
]);
const expectedResponseCount = serverCount - 1;
if (((_a = opts.flags) === null || _a === void 0 ? void 0 : _a.local) || expectedResponseCount <= 0) {
return localSockets;
}
const requestId = randomId();
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
const storedRequest = this.customRequests.get(requestId);
if (storedRequest) {
reject(new Error(`timeout reached: missing ${storedRequest.missingUids.size} responses`));
this.customRequests.delete(requestId);
}
}, opts.flags.timeout || DEFAULT_TIMEOUT);
const storedRequest = {
type: MessageType.FETCH_SOCKETS,
resolve,
timeout,
missingUids: new Set([...this.nodesMap.keys()]),
responses: localSockets,
};
this.customRequests.set(requestId, storedRequest);
this.publish({
type: MessageType.FETCH_SOCKETS,
data: {
opts: encodeOptions(opts),
requestId,
},
});
});
}
onResponse(response) {
const requestId = response.data.requestId;
debug("[%s] received response %s to request %s", this.uid, response.type, requestId);
switch (response.type) {
case MessageType.FETCH_SOCKETS_RESPONSE: {
const request = this.customRequests.get(requestId);
if (!request) {
return;
}
response.data.sockets.forEach((socket) => request.responses.push(socket));
request.missingUids.delete(response.uid);
if (request.missingUids.size === 0) {
clearTimeout(request.timeout);
request.resolve(request.responses);
this.customRequests.delete(requestId);
}
break;
}
case MessageType.SERVER_SIDE_EMIT_RESPONSE: {
const request = this.customRequests.get(requestId);
if (!request) {
return;
}
request.responses.push(response.data.packet);
request.missingUids.delete(response.uid);
if (request.missingUids.size === 0) {
clearTimeout(request.timeout);
request.resolve(null, request.responses);
this.customRequests.delete(requestId);
}
break;
}
default:
super.onResponse(response);
}
}
removeNode(uid) {
this.customRequests.forEach((request, requestId) => {
request.missingUids.delete(uid);
if (request.missingUids.size === 0) {
clearTimeout(request.timeout);
if (request.type === MessageType.FETCH_SOCKETS) {
request.resolve(request.responses);
}
else if (request.type === MessageType.SERVER_SIDE_EMIT) {
request.resolve(null, request.responses);
}
this.customRequests.delete(requestId);
}
});
this.nodesMap.delete(uid);
}
}
exports.ClusterAdapterWithHeartbeat = ClusterAdapterWithHeartbeat;

23
node_modules/socket.io-adapter/dist/contrib/yeast.d.ts generated vendored Normal file
View File

@ -0,0 +1,23 @@
/**
* Return a string representing the specified number.
*
* @param {Number} num The number to convert.
* @returns {String} The string representation of the number.
* @api public
*/
export declare function encode(num: any): string;
/**
* Return the integer value specified by the given string.
*
* @param {String} str The string to convert.
* @returns {Number} The integer value represented by the string.
* @api public
*/
export declare function decode(str: any): number;
/**
* Yeast: A tiny growing id generator.
*
* @returns {String} A unique id.
* @api public
*/
export declare function yeast(): string;

55
node_modules/socket.io-adapter/dist/contrib/yeast.js generated vendored Normal file
View File

@ -0,0 +1,55 @@
// imported from https://github.com/unshiftio/yeast
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.yeast = exports.decode = exports.encode = void 0;
const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_".split(""), length = 64, map = {};
let seed = 0, i = 0, prev;
/**
* Return a string representing the specified number.
*
* @param {Number} num The number to convert.
* @returns {String} The string representation of the number.
* @api public
*/
function encode(num) {
let encoded = "";
do {
encoded = alphabet[num % length] + encoded;
num = Math.floor(num / length);
} while (num > 0);
return encoded;
}
exports.encode = encode;
/**
* Return the integer value specified by the given string.
*
* @param {String} str The string to convert.
* @returns {Number} The integer value represented by the string.
* @api public
*/
function decode(str) {
let decoded = 0;
for (i = 0; i < str.length; i++) {
decoded = decoded * length + map[str.charAt(i)];
}
return decoded;
}
exports.decode = decode;
/**
* Yeast: A tiny growing id generator.
*
* @returns {String} A unique id.
* @api public
*/
function yeast() {
const now = encode(+new Date());
if (now !== prev)
return (seed = 0), (prev = now);
return now + "." + encode(seed++);
}
exports.yeast = yeast;
//
// Map each character to its index.
//
for (; i < length; i++)
map[alphabet[i]] = i;

View File

@ -0,0 +1,179 @@
/// <reference types="node" />
import { EventEmitter } from "events";
/**
* A public ID, sent by the server at the beginning of the Socket.IO session and which can be used for private messaging
*/
export type SocketId = string;
/**
* A private ID, sent by the server at the beginning of the Socket.IO session and used for connection state recovery
* upon reconnection
*/
export type PrivateSessionId = string;
export type Room = string;
export interface BroadcastFlags {
volatile?: boolean;
compress?: boolean;
local?: boolean;
broadcast?: boolean;
binary?: boolean;
timeout?: number;
}
export interface BroadcastOptions {
rooms: Set<Room>;
except?: Set<Room>;
flags?: BroadcastFlags;
}
interface SessionToPersist {
sid: SocketId;
pid: PrivateSessionId;
rooms: Room[];
data: unknown;
}
export type Session = SessionToPersist & {
missedPackets: unknown[][];
};
export declare class Adapter extends EventEmitter {
readonly nsp: any;
rooms: Map<Room, Set<SocketId>>;
sids: Map<SocketId, Set<Room>>;
private readonly encoder;
/**
* In-memory adapter constructor.
*
* @param {Namespace} nsp
*/
constructor(nsp: any);
/**
* To be overridden
*/
init(): Promise<void> | void;
/**
* To be overridden
*/
close(): Promise<void> | void;
/**
* Returns the number of Socket.IO servers in the cluster
*
* @public
*/
serverCount(): Promise<number>;
/**
* Adds a socket to a list of room.
*
* @param {SocketId} id the socket id
* @param {Set<Room>} rooms a set of rooms
* @public
*/
addAll(id: SocketId, rooms: Set<Room>): Promise<void> | void;
/**
* Removes a socket from a room.
*
* @param {SocketId} id the socket id
* @param {Room} room the room name
*/
del(id: SocketId, room: Room): Promise<void> | void;
private _del;
/**
* Removes a socket from all rooms it's joined.
*
* @param {SocketId} id the socket id
*/
delAll(id: SocketId): void;
/**
* Broadcasts a packet.
*
* Options:
* - `flags` {Object} flags for this packet
* - `except` {Array} sids that should be excluded
* - `rooms` {Array} list of rooms to broadcast to
*
* @param {Object} packet the packet object
* @param {Object} opts the options
* @public
*/
broadcast(packet: any, opts: BroadcastOptions): void;
/**
* Broadcasts a packet and expects multiple acknowledgements.
*
* Options:
* - `flags` {Object} flags for this packet
* - `except` {Array} sids that should be excluded
* - `rooms` {Array} list of rooms to broadcast to
*
* @param {Object} packet the packet object
* @param {Object} opts the options
* @param clientCountCallback - the number of clients that received the packet
* @param ack - the callback that will be called for each client response
*
* @public
*/
broadcastWithAck(packet: any, opts: BroadcastOptions, clientCountCallback: (clientCount: number) => void, ack: (...args: any[]) => void): void;
private _encode;
/**
* Gets a list of sockets by sid.
*
* @param {Set<Room>} rooms the explicit set of rooms to check.
*/
sockets(rooms: Set<Room>): Promise<Set<SocketId>>;
/**
* Gets the list of rooms a given socket has joined.
*
* @param {SocketId} id the socket id
*/
socketRooms(id: SocketId): Set<Room> | undefined;
/**
* Returns the matching socket instances
*
* @param opts - the filters to apply
*/
fetchSockets(opts: BroadcastOptions): Promise<any[]>;
/**
* Makes the matching socket instances join the specified rooms
*
* @param opts - the filters to apply
* @param rooms - the rooms to join
*/
addSockets(opts: BroadcastOptions, rooms: Room[]): void;
/**
* Makes the matching socket instances leave the specified rooms
*
* @param opts - the filters to apply
* @param rooms - the rooms to leave
*/
delSockets(opts: BroadcastOptions, rooms: Room[]): void;
/**
* Makes the matching socket instances disconnect
*
* @param opts - the filters to apply
* @param close - whether to close the underlying connection
*/
disconnectSockets(opts: BroadcastOptions, close: boolean): void;
private apply;
private computeExceptSids;
/**
* Send a packet to the other Socket.IO servers in the cluster
* @param packet - an array of arguments, which may include an acknowledgement callback at the end
*/
serverSideEmit(packet: any[]): void;
/**
* Save the client session in order to restore it upon reconnection.
*/
persistSession(session: SessionToPersist): void;
/**
* Restore the session and find the packets that were missed by the client.
* @param pid
* @param offset
*/
restoreSession(pid: PrivateSessionId, offset: string): Promise<Session>;
}
export declare class SessionAwareAdapter extends Adapter {
readonly nsp: any;
private readonly maxDisconnectionDuration;
private sessions;
private packets;
constructor(nsp: any);
persistSession(session: SessionToPersist): void;
restoreSession(pid: PrivateSessionId, offset: string): Promise<Session>;
broadcast(packet: any, opts: BroadcastOptions): void;
}
export {};

View File

@ -0,0 +1,394 @@
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SessionAwareAdapter = exports.Adapter = void 0;
const events_1 = require("events");
const yeast_1 = require("./contrib/yeast");
const WebSocket = require("ws");
const canPreComputeFrame = typeof ((_a = WebSocket === null || WebSocket === void 0 ? void 0 : WebSocket.Sender) === null || _a === void 0 ? void 0 : _a.frame) === "function";
class Adapter extends events_1.EventEmitter {
/**
* In-memory adapter constructor.
*
* @param {Namespace} nsp
*/
constructor(nsp) {
super();
this.nsp = nsp;
this.rooms = new Map();
this.sids = new Map();
this.encoder = nsp.server.encoder;
}
/**
* To be overridden
*/
init() { }
/**
* To be overridden
*/
close() { }
/**
* Returns the number of Socket.IO servers in the cluster
*
* @public
*/
serverCount() {
return Promise.resolve(1);
}
/**
* Adds a socket to a list of room.
*
* @param {SocketId} id the socket id
* @param {Set<Room>} rooms a set of rooms
* @public
*/
addAll(id, rooms) {
if (!this.sids.has(id)) {
this.sids.set(id, new Set());
}
for (const room of rooms) {
this.sids.get(id).add(room);
if (!this.rooms.has(room)) {
this.rooms.set(room, new Set());
this.emit("create-room", room);
}
if (!this.rooms.get(room).has(id)) {
this.rooms.get(room).add(id);
this.emit("join-room", room, id);
}
}
}
/**
* Removes a socket from a room.
*
* @param {SocketId} id the socket id
* @param {Room} room the room name
*/
del(id, room) {
if (this.sids.has(id)) {
this.sids.get(id).delete(room);
}
this._del(room, id);
}
_del(room, id) {
const _room = this.rooms.get(room);
if (_room != null) {
const deleted = _room.delete(id);
if (deleted) {
this.emit("leave-room", room, id);
}
if (_room.size === 0 && this.rooms.delete(room)) {
this.emit("delete-room", room);
}
}
}
/**
* Removes a socket from all rooms it's joined.
*
* @param {SocketId} id the socket id
*/
delAll(id) {
if (!this.sids.has(id)) {
return;
}
for (const room of this.sids.get(id)) {
this._del(room, id);
}
this.sids.delete(id);
}
/**
* Broadcasts a packet.
*
* Options:
* - `flags` {Object} flags for this packet
* - `except` {Array} sids that should be excluded
* - `rooms` {Array} list of rooms to broadcast to
*
* @param {Object} packet the packet object
* @param {Object} opts the options
* @public
*/
broadcast(packet, opts) {
const flags = opts.flags || {};
const packetOpts = {
preEncoded: true,
volatile: flags.volatile,
compress: flags.compress,
};
packet.nsp = this.nsp.name;
const encodedPackets = this._encode(packet, packetOpts);
this.apply(opts, (socket) => {
if (typeof socket.notifyOutgoingListeners === "function") {
socket.notifyOutgoingListeners(packet);
}
socket.client.writeToEngine(encodedPackets, packetOpts);
});
}
/**
* Broadcasts a packet and expects multiple acknowledgements.
*
* Options:
* - `flags` {Object} flags for this packet
* - `except` {Array} sids that should be excluded
* - `rooms` {Array} list of rooms to broadcast to
*
* @param {Object} packet the packet object
* @param {Object} opts the options
* @param clientCountCallback - the number of clients that received the packet
* @param ack - the callback that will be called for each client response
*
* @public
*/
broadcastWithAck(packet, opts, clientCountCallback, ack) {
const flags = opts.flags || {};
const packetOpts = {
preEncoded: true,
volatile: flags.volatile,
compress: flags.compress,
};
packet.nsp = this.nsp.name;
// we can use the same id for each packet, since the _ids counter is common (no duplicate)
packet.id = this.nsp._ids++;
const encodedPackets = this._encode(packet, packetOpts);
let clientCount = 0;
this.apply(opts, (socket) => {
// track the total number of acknowledgements that are expected
clientCount++;
// call the ack callback for each client response
socket.acks.set(packet.id, ack);
if (typeof socket.notifyOutgoingListeners === "function") {
socket.notifyOutgoingListeners(packet);
}
socket.client.writeToEngine(encodedPackets, packetOpts);
});
clientCountCallback(clientCount);
}
_encode(packet, packetOpts) {
const encodedPackets = this.encoder.encode(packet);
if (canPreComputeFrame &&
encodedPackets.length === 1 &&
typeof encodedPackets[0] === "string") {
// "4" being the "message" packet type in the Engine.IO protocol
const data = Buffer.from("4" + encodedPackets[0]);
// see https://github.com/websockets/ws/issues/617#issuecomment-283002469
packetOpts.wsPreEncodedFrame = WebSocket.Sender.frame(data, {
readOnly: false,
mask: false,
rsv1: false,
opcode: 1,
fin: true,
});
}
return encodedPackets;
}
/**
* Gets a list of sockets by sid.
*
* @param {Set<Room>} rooms the explicit set of rooms to check.
*/
sockets(rooms) {
const sids = new Set();
this.apply({ rooms }, (socket) => {
sids.add(socket.id);
});
return Promise.resolve(sids);
}
/**
* Gets the list of rooms a given socket has joined.
*
* @param {SocketId} id the socket id
*/
socketRooms(id) {
return this.sids.get(id);
}
/**
* Returns the matching socket instances
*
* @param opts - the filters to apply
*/
fetchSockets(opts) {
const sockets = [];
this.apply(opts, (socket) => {
sockets.push(socket);
});
return Promise.resolve(sockets);
}
/**
* Makes the matching socket instances join the specified rooms
*
* @param opts - the filters to apply
* @param rooms - the rooms to join
*/
addSockets(opts, rooms) {
this.apply(opts, (socket) => {
socket.join(rooms);
});
}
/**
* Makes the matching socket instances leave the specified rooms
*
* @param opts - the filters to apply
* @param rooms - the rooms to leave
*/
delSockets(opts, rooms) {
this.apply(opts, (socket) => {
rooms.forEach((room) => socket.leave(room));
});
}
/**
* Makes the matching socket instances disconnect
*
* @param opts - the filters to apply
* @param close - whether to close the underlying connection
*/
disconnectSockets(opts, close) {
this.apply(opts, (socket) => {
socket.disconnect(close);
});
}
apply(opts, callback) {
const rooms = opts.rooms;
const except = this.computeExceptSids(opts.except);
if (rooms.size) {
const ids = new Set();
for (const room of rooms) {
if (!this.rooms.has(room))
continue;
for (const id of this.rooms.get(room)) {
if (ids.has(id) || except.has(id))
continue;
const socket = this.nsp.sockets.get(id);
if (socket) {
callback(socket);
ids.add(id);
}
}
}
}
else {
for (const [id] of this.sids) {
if (except.has(id))
continue;
const socket = this.nsp.sockets.get(id);
if (socket)
callback(socket);
}
}
}
computeExceptSids(exceptRooms) {
const exceptSids = new Set();
if (exceptRooms && exceptRooms.size > 0) {
for (const room of exceptRooms) {
if (this.rooms.has(room)) {
this.rooms.get(room).forEach((sid) => exceptSids.add(sid));
}
}
}
return exceptSids;
}
/**
* Send a packet to the other Socket.IO servers in the cluster
* @param packet - an array of arguments, which may include an acknowledgement callback at the end
*/
serverSideEmit(packet) {
console.warn("this adapter does not support the serverSideEmit() functionality");
}
/**
* Save the client session in order to restore it upon reconnection.
*/
persistSession(session) { }
/**
* Restore the session and find the packets that were missed by the client.
* @param pid
* @param offset
*/
restoreSession(pid, offset) {
return null;
}
}
exports.Adapter = Adapter;
class SessionAwareAdapter extends Adapter {
constructor(nsp) {
super(nsp);
this.nsp = nsp;
this.sessions = new Map();
this.packets = [];
this.maxDisconnectionDuration =
nsp.server.opts.connectionStateRecovery.maxDisconnectionDuration;
const timer = setInterval(() => {
const threshold = Date.now() - this.maxDisconnectionDuration;
this.sessions.forEach((session, sessionId) => {
const hasExpired = session.disconnectedAt < threshold;
if (hasExpired) {
this.sessions.delete(sessionId);
}
});
for (let i = this.packets.length - 1; i >= 0; i--) {
const hasExpired = this.packets[i].emittedAt < threshold;
if (hasExpired) {
this.packets.splice(0, i + 1);
break;
}
}
}, 60 * 1000);
// prevents the timer from keeping the process alive
timer.unref();
}
persistSession(session) {
session.disconnectedAt = Date.now();
this.sessions.set(session.pid, session);
}
restoreSession(pid, offset) {
const session = this.sessions.get(pid);
if (!session) {
// the session may have expired
return null;
}
const hasExpired = session.disconnectedAt + this.maxDisconnectionDuration < Date.now();
if (hasExpired) {
// the session has expired
this.sessions.delete(pid);
return null;
}
const index = this.packets.findIndex((packet) => packet.id === offset);
if (index === -1) {
// the offset may be too old
return null;
}
const missedPackets = [];
for (let i = index + 1; i < this.packets.length; i++) {
const packet = this.packets[i];
if (shouldIncludePacket(session.rooms, packet.opts)) {
missedPackets.push(packet.data);
}
}
return Promise.resolve(Object.assign(Object.assign({}, session), { missedPackets }));
}
broadcast(packet, opts) {
var _a;
const isEventPacket = packet.type === 2;
// packets with acknowledgement are not stored because the acknowledgement function cannot be serialized and
// restored on another server upon reconnection
const withoutAcknowledgement = packet.id === undefined;
const notVolatile = ((_a = opts.flags) === null || _a === void 0 ? void 0 : _a.volatile) === undefined;
if (isEventPacket && withoutAcknowledgement && notVolatile) {
const id = (0, yeast_1.yeast)();
// the offset is stored at the end of the data array, so the client knows the ID of the last packet it has
// processed (and the format is backward-compatible)
packet.data.push(id);
this.packets.push({
id,
opts,
data: packet.data,
emittedAt: Date.now(),
});
}
super.broadcast(packet, opts);
}
}
exports.SessionAwareAdapter = SessionAwareAdapter;
function shouldIncludePacket(sessionRooms, opts) {
const included = opts.rooms.size === 0 || sessionRooms.some((room) => opts.rooms.has(room));
const notExcluded = sessionRooms.every((room) => !opts.except.has(room));
return included && notExcluded;
}

2
node_modules/socket.io-adapter/dist/index.d.ts generated vendored Normal file
View File

@ -0,0 +1,2 @@
export { SocketId, PrivateSessionId, Room, BroadcastFlags, BroadcastOptions, Session, Adapter, SessionAwareAdapter, } from "./in-memory-adapter";
export { ClusterAdapter, ClusterAdapterWithHeartbeat, ClusterAdapterOptions, ClusterMessage, ClusterResponse, MessageType, ServerId, Offset, } from "./cluster-adapter";

10
node_modules/socket.io-adapter/dist/index.js generated vendored Normal file
View File

@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MessageType = exports.ClusterAdapterWithHeartbeat = exports.ClusterAdapter = exports.SessionAwareAdapter = exports.Adapter = void 0;
var in_memory_adapter_1 = require("./in-memory-adapter");
Object.defineProperty(exports, "Adapter", { enumerable: true, get: function () { return in_memory_adapter_1.Adapter; } });
Object.defineProperty(exports, "SessionAwareAdapter", { enumerable: true, get: function () { return in_memory_adapter_1.SessionAwareAdapter; } });
var cluster_adapter_1 = require("./cluster-adapter");
Object.defineProperty(exports, "ClusterAdapter", { enumerable: true, get: function () { return cluster_adapter_1.ClusterAdapter; } });
Object.defineProperty(exports, "ClusterAdapterWithHeartbeat", { enumerable: true, get: function () { return cluster_adapter_1.ClusterAdapterWithHeartbeat; } });
Object.defineProperty(exports, "MessageType", { enumerable: true, get: function () { return cluster_adapter_1.MessageType; } });

39
node_modules/socket.io-adapter/package.json generated vendored Normal file
View File

@ -0,0 +1,39 @@
{
"name": "socket.io-adapter",
"version": "2.5.5",
"license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/socketio/socket.io-adapter.git"
},
"files": [
"dist/"
],
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"description": "default socket.io in-memory adapter",
"dependencies": {
"debug": "~4.3.4",
"ws": "~8.17.1"
},
"devDependencies": {
"@types/debug": "^4.1.12",
"@types/expect.js": "^0.3.32",
"@types/mocha": "^10.0.1",
"@types/node": "^14.11.2",
"expect.js": "^0.3.1",
"mocha": "^10.2.0",
"nyc": "^15.1.0",
"prettier": "^2.8.1",
"socket.io": "^4.7.4",
"socket.io-client": "^4.7.4",
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
},
"scripts": {
"test": "npm run format:check && tsc && nyc mocha --require ts-node/register test/*.ts",
"format:check": "prettier --parser typescript --check 'lib/**/*.ts' 'test/**/*.ts'",
"format:fix": "prettier --parser typescript --write 'lib/**/*.ts' 'test/**/*.ts'",
"prepack": "tsc"
}
}