Updated script that can be controled by Nodejs web app

This commit is contained in:
mac OS
2024-11-25 12:24:18 +07:00
parent c440eda1f4
commit 8b0ab2bd3a
8662 changed files with 1803808 additions and 34 deletions

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; } });