Socket
Socket
Sign inDemoInstall

socket.io

Package Overview
Dependencies
Maintainers
2
Versions
157
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

socket.io - npm Package Compare versions

Comparing version 4.5.4 to 4.6.0-alpha1

46

dist/broadcast-operator.d.ts
import type { BroadcastFlags, Room, SocketId } from "socket.io-adapter";
import { Handshake } from "./socket";
import type { Adapter } from "socket.io-adapter";
import type { EventParams, EventNames, EventsMap, TypedEventBroadcaster } from "./typed-events";
import type { EventParams, EventNames, EventsMap, TypedEventBroadcaster, DecorateAcknowledgements, DecorateAcknowledgementsWithTimeoutAndMultipleResponses, AllButLast, Last, SecondArg } from "./typed-events";
export declare class BroadcastOperator<EmitEvents extends EventsMap, SocketData> implements TypedEventBroadcaster<EmitEvents> {

@@ -10,3 +10,5 @@ private readonly adapter;

private readonly flags;
constructor(adapter: Adapter, rooms?: Set<Room>, exceptRooms?: Set<Room>, flags?: BroadcastFlags);
constructor(adapter: Adapter, rooms?: Set<Room>, exceptRooms?: Set<Room>, flags?: BroadcastFlags & {
expectSingleResponse?: boolean;
});
/**

@@ -102,3 +104,3 @@ * Targets a room when emitting.

*/
timeout(timeout: number): BroadcastOperator<EmitEvents, SocketData>;
timeout(timeout: number): BroadcastOperator<DecorateAcknowledgementsWithTimeoutAndMultipleResponses<EmitEvents>, SocketData>;
/**

@@ -127,2 +129,16 @@ * Emits to all clients.

/**
* Emits an event and waits for an acknowledgement from all clients.
*
* @example
* try {
* const responses = await io.timeout(1000).emitWithAck("some-event");
* console.log(responses); // one response per client
* } catch (e) {
* // some clients did not acknowledge the event in the given delay
* }
*
* @return a Promise that will be fulfilled when all clients have acknowledged the event
*/
emitWithAck<Ev extends EventNames<EmitEvents>>(ev: Ev, ...args: AllButLast<EventParams<EmitEvents, Ev>>): Promise<SecondArg<Last<EventParams<EmitEvents, Ev>>>>;
/**
* Gets a list of clients.

@@ -225,2 +241,26 @@ *

constructor(adapter: Adapter, details: SocketDetails<SocketData>);
/**
* Adds a timeout in milliseconds for the next operation.
*
* @example
* const sockets = await io.fetchSockets();
*
* for (const socket of sockets) {
* if (someCondition) {
* socket.timeout(1000).emit("some-event", (err) => {
* if (err) {
* // the client did not acknowledge the event in the given delay
* }
* });
* }
* }
*
* // note: if possible, using a room instead of looping over all sockets is preferable
* io.timeout(1000).to(someConditionRoom).emit("some-event", (err, responses) => {
* // ...
* });
*
* @param timeout
*/
timeout(timeout: number): BroadcastOperator<DecorateAcknowledgements<EmitEvents>, SocketData>;
emit<Ev extends EventNames<EmitEvents>>(ev: Ev, ...args: EventParams<EmitEvents, Ev>): boolean;

@@ -227,0 +267,0 @@ /**

@@ -181,3 +181,6 @@ "use strict";

timedOut = true;
ack.apply(this, [new Error("operation has timed out"), responses]);
ack.apply(this, [
new Error("operation has timed out"),
this.flags.expectSingleResponse ? null : responses,
]);
}, this.flags.timeout);

@@ -192,3 +195,6 @@ let expectedServerCount = -1;

clearTimeout(timer);
ack.apply(this, [null, responses]);
ack.apply(this, [
null,
this.flags.expectSingleResponse ? null : responses,
]);
}

@@ -217,2 +223,29 @@ };

/**
* Emits an event and waits for an acknowledgement from all clients.
*
* @example
* try {
* const responses = await io.timeout(1000).emitWithAck("some-event");
* console.log(responses); // one response per client
* } catch (e) {
* // some clients did not acknowledge the event in the given delay
* }
*
* @return a Promise that will be fulfilled when all clients have acknowledged the event
*/
emitWithAck(ev, ...args) {
return new Promise((resolve, reject) => {
args.push((err, responses) => {
if (err) {
err.responses = responses;
return reject(err);
}
else {
return resolve(responses);
}
});
this.emit(ev, ...args);
});
}
/**
* Gets a list of clients.

@@ -347,4 +380,32 @@ *

this.data = details.data;
this.operator = new BroadcastOperator(adapter, new Set([this.id]));
this.operator = new BroadcastOperator(adapter, new Set([this.id]), new Set(), {
expectSingleResponse: true, // so that remoteSocket.emit() with acknowledgement behaves like socket.emit()
});
}
/**
* Adds a timeout in milliseconds for the next operation.
*
* @example
* const sockets = await io.fetchSockets();
*
* for (const socket of sockets) {
* if (someCondition) {
* socket.timeout(1000).emit("some-event", (err) => {
* if (err) {
* // the client did not acknowledge the event in the given delay
* }
* });
* }
* }
*
* // note: if possible, using a room instead of looping over all sockets is preferable
* io.timeout(1000).to(someConditionRoom).emit("some-event", (err, responses) => {
* // ...
* });
*
* @param timeout
*/
timeout(timeout) {
return this.operator.timeout(timeout);
}
emit(ev, ...args) {

@@ -351,0 +412,0 @@ return this.operator.emit(ev, ...args);

2

dist/client.js

@@ -97,3 +97,3 @@ "use strict";

const nsp = this.server.of(name);
const socket = nsp._add(this, auth, () => {
nsp._add(this, auth, (socket) => {
this.sockets.set(socket.id, socket);

@@ -100,0 +100,0 @@ this.nsps.set(nsp.name, socket);

@@ -12,5 +12,6 @@ /// <reference types="node" />

import type { Encoder } from "socket.io-parser";
import { Socket } from "./socket";
import { Socket, DisconnectReason } from "./socket";
import type { BroadcastOperator, RemoteSocket } from "./broadcast-operator";
import { EventsMap, DefaultEventsMap, EventParams, StrictEventEmitter, EventNames } from "./typed-events";
import { EventsMap, DefaultEventsMap, EventParams, StrictEventEmitter, EventNames, DecorateAcknowledgementsWithTimeoutAndMultipleResponses, AllButLast, Last, FirstArg, SecondArg } from "./typed-events";
import type { BaseServer } from "engine.io/build/server";
declare type ParentNspNameMatchFn = (name: string, auth: {

@@ -46,2 +47,26 @@ [key: string]: any;

connectTimeout: number;
/**
* Whether to enable the recovery of connection state when a client temporarily disconnects.
*
* The connection state includes the missed packets, the rooms the socket was in and the `data` attribute.
*/
connectionStateRecovery: {
/**
* The backup duration of the sessions and the packets.
*
* @default 120000 (2 minutes)
*/
maxDisconnectionDuration?: number;
/**
* Whether to skip middlewares upon successful connection state recovery.
*
* @default true
*/
skipMiddlewares?: boolean;
};
/**
* Whether to remove child namespaces that have no sockets connected to them
* @default false
*/
cleanupEmptyChildNamespaces: boolean;
}

@@ -83,3 +108,3 @@ /**

*/
engine: any;
engine: BaseServer;
/** @private */

@@ -96,3 +121,3 @@ readonly _parser: typeof parser;

private _serveClient;
private opts;
private readonly opts;
private eio;

@@ -115,2 +140,3 @@ private _path;

constructor(srv: undefined | Partial<ServerOptions> | http.Server | HTTPSServer | Http2SecureServer | number, opts?: Partial<ServerOptions>);
get _opts(): Partial<ServerOptions>;
/**

@@ -211,6 +237,6 @@ * Sets/gets whether client code is being served.

*
* @param {engine.Server} engine engine.io (or compatible) server
* @param engine engine.io (or compatible) server
* @return self
*/
bind(engine: any): this;
bind(engine: BaseServer): this;
/**

@@ -307,2 +333,16 @@ * Called with each incoming transport connection.

/**
* Emits an event and waits for an acknowledgement from all clients.
*
* @example
* try {
* const responses = await io.timeout(1000).emitWithAck("some-event");
* console.log(responses); // one response per client
* } catch (e) {
* // some clients did not acknowledge the event in the given delay
* }
*
* @return a Promise that will be fulfilled when all clients have acknowledged the event
*/
emitWithAck<Ev extends EventNames<EmitEvents>>(ev: Ev, ...args: AllButLast<EventParams<EmitEvents, Ev>>): Promise<SecondArg<Last<EventParams<EmitEvents, Ev>>>>;
/**
* Sends a `message` event to all clients.

@@ -342,5 +382,5 @@ *

* if (err) {
* // some clients did not acknowledge the event in the given delay
* // some servers did not acknowledge the event in the given delay
* } else {
* console.log(responses); // one response per client
* console.log(responses); // one response per server (except the current one)
* }

@@ -356,4 +396,21 @@ * });

*/
serverSideEmit<Ev extends EventNames<ServerSideEvents>>(ev: Ev, ...args: EventParams<ServerSideEvents, Ev>): boolean;
serverSideEmit<Ev extends EventNames<ServerSideEvents>>(ev: Ev, ...args: EventParams<DecorateAcknowledgementsWithTimeoutAndMultipleResponses<ServerSideEvents>, Ev>): boolean;
/**
* Sends a message and expect an acknowledgement from the other Socket.IO servers of the cluster.
*
* @example
* try {
* const responses = await io.serverSideEmitWithAck("ping");
* console.log(responses); // one response per server (except the current one)
* } catch (e) {
* // some servers did not acknowledge the event in the given delay
* }
*
* @param ev - the event name
* @param args - an array of arguments
*
* @return a Promise that will be fulfilled when all servers have acknowledged the event
*/
serverSideEmitWithAck<Ev extends EventNames<ServerSideEvents>>(ev: Ev, ...args: AllButLast<EventParams<ServerSideEvents, Ev>>): Promise<FirstArg<Last<EventParams<ServerSideEvents, Ev>>>[]>;
/**
* Gets a list of socket ids.

@@ -410,3 +467,3 @@ *

*/
timeout(timeout: number): BroadcastOperator<EmitEvents, SocketData>;
timeout(timeout: number): BroadcastOperator<DecorateAcknowledgementsWithTimeoutAndMultipleResponses<EmitEvents>, SocketData>;
/**

@@ -484,3 +541,3 @@ * Returns the matching socket instances.

}
export { Socket, ServerOptions, Namespace, BroadcastOperator, RemoteSocket };
export { Socket, DisconnectReason, ServerOptions, Namespace, BroadcastOperator, RemoteSocket, };
export { Event } from "./socket";

@@ -97,8 +97,21 @@ "use strict";

this.encoder = new this._parser.Encoder();
this.adapter(opts.adapter || socket_io_adapter_1.Adapter);
this.opts = opts;
if (opts.connectionStateRecovery) {
opts.connectionStateRecovery = Object.assign({
maxDisconnectionDuration: 2 * 60 * 1000,
skipMiddlewares: true,
}, opts.connectionStateRecovery);
this.adapter(opts.adapter || socket_io_adapter_1.SessionAwareAdapter);
}
else {
this.adapter(opts.adapter || socket_io_adapter_1.Adapter);
}
opts.cleanupEmptyChildNamespaces = !!opts.cleanupEmptyChildNamespaces;
this.sockets = this.of("/");
this.opts = opts;
if (srv || typeof srv == "number")
this.attach(srv);
}
get _opts() {
return this.opts;
}
serveClient(v) {

@@ -254,3 +267,3 @@ if (!arguments.length)

res.writeHeader("cache-control", "public, max-age=0");
res.writeHeader("content-type", "application/" + (isMap ? "json" : "javascript"));
res.writeHeader("content-type", "application/" + (isMap ? "json" : "javascript") + "; charset=utf-8");
res.writeHeader("etag", expectedEtag);

@@ -329,3 +342,3 @@ const filepath = path.join(__dirname, "../client-dist/", filename);

res.setHeader("Cache-Control", "public, max-age=0");
res.setHeader("Content-Type", "application/" + (isMap ? "json" : "javascript"));
res.setHeader("Content-Type", "application/" + (isMap ? "json" : "javascript") + "; charset=utf-8");
res.setHeader("ETag", expectedEtag);

@@ -370,3 +383,3 @@ Server.sendFile(filename, req, res);

*
* @param {engine.Server} engine engine.io (or compatible) server
* @param engine engine.io (or compatible) server
* @return self

@@ -531,2 +544,18 @@ */

/**
* Emits an event and waits for an acknowledgement from all clients.
*
* @example
* try {
* const responses = await io.timeout(1000).emitWithAck("some-event");
* console.log(responses); // one response per client
* } catch (e) {
* // some clients did not acknowledge the event in the given delay
* }
*
* @return a Promise that will be fulfilled when all clients have acknowledged the event
*/
emitWithAck(ev, ...args) {
return this.sockets.emitWithAck(ev, ...args);
}
/**
* Sends a `message` event to all clients.

@@ -572,5 +601,5 @@ *

* if (err) {
* // some clients did not acknowledge the event in the given delay
* // some servers did not acknowledge the event in the given delay
* } else {
* console.log(responses); // one response per client
* console.log(responses); // one response per server (except the current one)
* }

@@ -590,2 +619,21 @@ * });

/**
* Sends a message and expect an acknowledgement from the other Socket.IO servers of the cluster.
*
* @example
* try {
* const responses = await io.serverSideEmitWithAck("ping");
* console.log(responses); // one response per server (except the current one)
* } catch (e) {
* // some servers did not acknowledge the event in the given delay
* }
*
* @param ev - the event name
* @param args - an array of arguments
*
* @return a Promise that will be fulfilled when all servers have acknowledged the event
*/
serverSideEmitWithAck(ev, ...args) {
return this.sockets.serverSideEmitWithAck(ev, ...args);
}
/**
* Gets a list of socket ids.

@@ -592,0 +640,0 @@ *

import { Socket } from "./socket";
import type { Server } from "./index";
import { EventParams, EventNames, EventsMap, StrictEventEmitter, DefaultEventsMap } from "./typed-events";
import { EventParams, EventNames, EventsMap, StrictEventEmitter, DefaultEventsMap, DecorateAcknowledgementsWithTimeoutAndMultipleResponses, AllButLast, Last, FirstArg, SecondArg } from "./typed-events";
import type { Client } from "./client";
import type { Adapter, Room, SocketId } from "socket.io-adapter";
import { BroadcastOperator, RemoteSocket } from "./broadcast-operator";
import { BroadcastOperator } from "./broadcast-operator";
export interface ExtendedError extends Error {

@@ -175,3 +175,5 @@ data?: any;

*/
_add(client: Client<ListenEvents, EmitEvents, ServerSideEvents>, query: any, fn?: () => void): Socket<ListenEvents, EmitEvents, ServerSideEvents, SocketData>;
_add(client: Client<ListenEvents, EmitEvents, ServerSideEvents>, auth: Record<string, unknown>, fn: (socket: Socket<ListenEvents, EmitEvents, ServerSideEvents, SocketData>) => void): any;
private _createSocket;
private _doConnect;
/**

@@ -207,2 +209,18 @@ * Removes a client. Called by each `Socket`.

/**
* Emits an event and waits for an acknowledgement from all clients.
*
* @example
* const myNamespace = io.of("/my-namespace");
*
* try {
* const responses = await myNamespace.timeout(1000).emitWithAck("some-event");
* console.log(responses); // one response per client
* } catch (e) {
* // some clients did not acknowledge the event in the given delay
* }
*
* @return a Promise that will be fulfilled when all clients have acknowledged the event
*/
emitWithAck<Ev extends EventNames<EmitEvents>>(ev: Ev, ...args: AllButLast<EventParams<EmitEvents, Ev>>): Promise<SecondArg<Last<EventParams<EmitEvents, Ev>>>>;
/**
* Sends a `message` event to all clients.

@@ -246,5 +264,5 @@ *

* if (err) {
* // some clients did not acknowledge the event in the given delay
* // some servers did not acknowledge the event in the given delay
* } else {
* console.log(responses); // one response per client
* console.log(responses); // one response per server (except the current one)
* }

@@ -260,4 +278,23 @@ * });

*/
serverSideEmit<Ev extends EventNames<ServerSideEvents>>(ev: Ev, ...args: EventParams<ServerSideEvents, Ev>): boolean;
serverSideEmit<Ev extends EventNames<ServerSideEvents>>(ev: Ev, ...args: EventParams<DecorateAcknowledgementsWithTimeoutAndMultipleResponses<ServerSideEvents>, Ev>): boolean;
/**
* Sends a message and expect an acknowledgement from the other Socket.IO servers of the cluster.
*
* @example
* const myNamespace = io.of("/my-namespace");
*
* try {
* const responses = await myNamespace.serverSideEmitWithAck("ping");
* console.log(responses); // one response per server (except the current one)
* } catch (e) {
* // some servers did not acknowledge the event in the given delay
* }
*
* @param ev - the event name
* @param args - an array of arguments
*
* @return a Promise that will be fulfilled when all servers have acknowledged the event
*/
serverSideEmitWithAck<Ev extends EventNames<ServerSideEvents>>(ev: Ev, ...args: AllButLast<EventParams<ServerSideEvents, Ev>>): Promise<FirstArg<Last<EventParams<ServerSideEvents, Ev>>>[]>;
/**
* Called when a packet is received from another Socket.IO server

@@ -330,3 +367,3 @@ *

*/
timeout(timeout: number): BroadcastOperator<EmitEvents, SocketData>;
timeout(timeout: number): BroadcastOperator<DecorateAcknowledgementsWithTimeoutAndMultipleResponses<EmitEvents>, SocketData>;
/**

@@ -358,3 +395,3 @@ * Returns the matching socket instances.

*/
fetchSockets(): Promise<RemoteSocket<EmitEvents, SocketData>[]>;
fetchSockets(): Promise<import("./broadcast-operator").RemoteSocket<EmitEvents, SocketData>[]>;
/**

@@ -361,0 +398,0 @@ * Makes the matching socket instances join the specified rooms.

@@ -200,5 +200,13 @@ "use strict";

*/
_add(client, query, fn) {
async _add(client, auth, fn) {
var _a;
debug("adding socket to nsp %s", this.name);
const socket = new socket_1.Socket(this, client, query);
const socket = await this._createSocket(client, auth);
if (
// @ts-ignore
((_a = this.server.opts.connectionStateRecovery) === null || _a === void 0 ? void 0 : _a.skipMiddlewares) &&
socket.recovered &&
client.conn.readyState === "open") {
return this._doConnect(socket, fn);
}
this.run(socket, (err) => {

@@ -224,18 +232,39 @@ process.nextTick(() => {

}
// track socket
this.sockets.set(socket.id, socket);
// it's paramount that the internal `onconnect` logic
// fires before user-set events to prevent state order
// violations (such as a disconnection before the connection
// logic is complete)
socket._onconnect();
if (fn)
fn();
// fire user-set events
this.emitReserved("connect", socket);
this.emitReserved("connection", socket);
this._doConnect(socket, fn);
});
});
return socket;
}
async _createSocket(client, auth) {
const sessionId = auth.pid;
const offset = auth.offset;
if (
// @ts-ignore
this.server.opts.connectionStateRecovery &&
typeof sessionId === "string" &&
typeof offset === "string") {
const session = await this.adapter.restoreSession(sessionId, offset);
if (session) {
debug("connection state recovered for sid %s", session.sid);
return new socket_1.Socket(this, client, auth, session);
}
else {
debug("unable to restore session state");
}
}
return new socket_1.Socket(this, client, auth);
}
_doConnect(socket, fn) {
// track socket
this.sockets.set(socket.id, socket);
// it's paramount that the internal `onconnect` logic
// fires before user-set events to prevent state order
// violations (such as a disconnection before the connection
// logic is complete)
socket._onconnect();
if (fn)
fn(socket);
// fire user-set events
this.emitReserved("connect", socket);
this.emitReserved("connection", socket);
}
/**

@@ -280,2 +309,20 @@ * Removes a client. Called by each `Socket`.

/**
* Emits an event and waits for an acknowledgement from all clients.
*
* @example
* const myNamespace = io.of("/my-namespace");
*
* try {
* const responses = await myNamespace.timeout(1000).emitWithAck("some-event");
* console.log(responses); // one response per client
* } catch (e) {
* // some clients did not acknowledge the event in the given delay
* }
*
* @return a Promise that will be fulfilled when all clients have acknowledged the event
*/
emitWithAck(ev, ...args) {
return new broadcast_operator_1.BroadcastOperator(this.adapter).emitWithAck(ev, ...args);
}
/**
* Sends a `message` event to all clients.

@@ -325,5 +372,5 @@ *

* if (err) {
* // some clients did not acknowledge the event in the given delay
* // some servers did not acknowledge the event in the given delay
* } else {
* console.log(responses); // one response per client
* console.log(responses); // one response per server (except the current one)
* }

@@ -348,2 +395,34 @@ * });

/**
* Sends a message and expect an acknowledgement from the other Socket.IO servers of the cluster.
*
* @example
* const myNamespace = io.of("/my-namespace");
*
* try {
* const responses = await myNamespace.serverSideEmitWithAck("ping");
* console.log(responses); // one response per server (except the current one)
* } catch (e) {
* // some servers did not acknowledge the event in the given delay
* }
*
* @param ev - the event name
* @param args - an array of arguments
*
* @return a Promise that will be fulfilled when all servers have acknowledged the event
*/
serverSideEmitWithAck(ev, ...args) {
return new Promise((resolve, reject) => {
args.push((err, responses) => {
if (err) {
err.responses = responses;
return reject(err);
}
else {
return resolve(responses);
}
});
this.serverSideEmit(ev, ...args);
});
}
/**
* Called when a packet is received from another Socket.IO server

@@ -350,0 +429,0 @@ *

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParentNamespace = void 0;
const namespace_1 = require("./namespace");
const debug_1 = __importDefault(require("debug"));
const debug = (0, debug_1.default)("socket.io:parent-namespace");
class ParentNamespace extends namespace_1.Namespace {

@@ -29,2 +34,3 @@ constructor(server) {

createChild(name) {
debug("creating child namespace %s", name);
const namespace = new namespace_1.Namespace(this.server, name);

@@ -35,2 +41,14 @@ namespace._fns = this._fns.slice(0);

this.children.add(namespace);
if (this.server._opts.cleanupEmptyChildNamespaces) {
const remove = namespace._remove;
namespace._remove = (socket) => {
remove.call(namespace, socket);
if (namespace.sockets.size === 0) {
debug("closing child namespace %s", name);
namespace.adapter.close();
this.server._nsps.delete(namespace.name);
this.children.delete(namespace);
}
};
}
this.server._nsps.set(name, namespace);

@@ -37,0 +55,0 @@ return namespace;

/// <reference types="node" />
/// <reference types="node" />
import { Packet } from "socket.io-parser";
import { EventParams, EventNames, EventsMap, StrictEventEmitter, DefaultEventsMap } from "./typed-events";
import { AllButLast, DecorateAcknowledgements, DecorateAcknowledgementsWithMultipleResponses, DefaultEventsMap, EventNames, EventParams, EventsMap, FirstArg, Last, StrictEventEmitter } from "./typed-events";
import type { Client } from "./client";
import type { Namespace } from "./namespace";
import type { IncomingMessage, IncomingHttpHeaders } from "http";
import type { Room, SocketId } from "socket.io-adapter";
import type { IncomingHttpHeaders, IncomingMessage } from "http";
import type { Room, Session, SocketId } from "socket.io-adapter";
import type { ParsedUrlQuery } from "querystring";

@@ -108,2 +108,7 @@ import { BroadcastOperator } from "./broadcast-operator";

/**
* Whether the connection state was recovered after a temporary disconnection. In that case, any missed packets will
* be transmitted to the client, the data attribute and the rooms will be restored.
*/
readonly recovered: boolean;
/**
* The handshake details.

@@ -131,2 +136,8 @@ */

connected: boolean;
/**
* The session ID, which must not be shared (unlike {@link id}).
*
* @private
*/
private readonly pid;
private readonly server;

@@ -147,3 +158,3 @@ private readonly adapter;

*/
constructor(nsp: Namespace<ListenEvents, EmitEvents, ServerSideEvents>, client: Client<ListenEvents, EmitEvents, ServerSideEvents>, auth: object);
constructor(nsp: Namespace<ListenEvents, EmitEvents, ServerSideEvents>, client: Client<ListenEvents, EmitEvents, ServerSideEvents>, auth: Record<string, unknown>, previousSession?: Session);
/**

@@ -175,2 +186,21 @@ * Builds the `handshake` BC object

/**
* Emits an event and waits for an acknowledgement
*
* @example
* io.on("connection", async (socket) => {
* // without timeout
* const response = await socket.emitWithAck("hello", "world");
*
* // with a specific timeout
* try {
* const response = await socket.timeout(1000).emitWithAck("hello", "world");
* } catch (err) {
* // the client did not acknowledge the event in the given delay
* }
* });
*
* @return a Promise that will be fulfilled when the client acknowledges the event
*/
emitWithAck<Ev extends EventNames<EmitEvents>>(ev: Ev, ...args: AllButLast<EventParams<EmitEvents, Ev>>): Promise<FirstArg<Last<EventParams<EmitEvents, Ev>>>>;
/**
* @private

@@ -200,3 +230,3 @@ */

*/
to(room: Room | Room[]): BroadcastOperator<EmitEvents, SocketData>;
to(room: Room | Room[]): BroadcastOperator<DecorateAcknowledgementsWithMultipleResponses<EmitEvents>, SocketData>;
/**

@@ -214,3 +244,3 @@ * Targets a room when broadcasting. Similar to `to()`, but might feel clearer in some cases:

*/
in(room: Room | Room[]): BroadcastOperator<EmitEvents, SocketData>;
in(room: Room | Room[]): BroadcastOperator<DecorateAcknowledgementsWithMultipleResponses<EmitEvents>, SocketData>;
/**

@@ -235,3 +265,3 @@ * Excludes a room when broadcasting.

*/
except(room: Room | Room[]): BroadcastOperator<EmitEvents, SocketData>;
except(room: Room | Room[]): BroadcastOperator<DecorateAcknowledgementsWithMultipleResponses<EmitEvents>, SocketData>;
/**

@@ -431,3 +461,3 @@ * Sends a `message` event.

*/
get broadcast(): BroadcastOperator<EmitEvents, SocketData>;
get broadcast(): BroadcastOperator<DecorateAcknowledgementsWithMultipleResponses<EmitEvents>, SocketData>;
/**

@@ -444,3 +474,3 @@ * Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node.

*/
get local(): BroadcastOperator<EmitEvents, SocketData>;
get local(): BroadcastOperator<DecorateAcknowledgementsWithMultipleResponses<EmitEvents>, SocketData>;
/**

@@ -461,3 +491,3 @@ * Sets a modifier for a subsequent event emission that the callback will be called with an error when the

*/
timeout(timeout: number): this;
timeout(timeout: number): Socket<ListenEvents, DecorateAcknowledgements<EmitEvents>, ServerSideEvents, SocketData>;
/**

@@ -464,0 +494,0 @@ * Dispatch incoming event to socket listeners.

@@ -13,2 +13,10 @@ "use strict";

const debug = (0, debug_1.default)("socket.io:socket");
const RECOVERABLE_DISCONNECT_REASONS = new Set([
"transport error",
"transport close",
"forced close",
"ping timeout",
"server shutting down",
"forced server close",
]);
exports.RESERVED_EVENTS = new Set([

@@ -63,3 +71,3 @@ "connect",

*/
constructor(nsp, client, auth) {
constructor(nsp, client, auth, previousSession) {
super();

@@ -69,2 +77,7 @@ this.nsp = nsp;

/**
* Whether the connection state was recovered after a temporary disconnection. In that case, any missed packets will
* be transmitted to the client, the data attribute and the rooms will be restored.
*/
this.recovered = false;
/**
* Additional information that can be attached to the Socket instance and which will be used in the

@@ -93,8 +106,24 @@ * {@link Server.fetchSockets()} method.

this.adapter = this.nsp.adapter;
if (client.conn.protocol === 3) {
// @ts-ignore
this.id = nsp.name !== "/" ? nsp.name + "#" + client.id : client.id;
if (previousSession) {
this.id = previousSession.sid;
this.pid = previousSession.pid;
previousSession.rooms.forEach((room) => this.join(room));
this.data = previousSession.data;
previousSession.missedPackets.forEach((packet) => {
this.packet({
type: socket_io_parser_1.PacketType.EVENT,
data: packet,
});
});
this.recovered = true;
}
else {
this.id = base64id_1.default.generateId(); // don't reuse the Engine.IO id because it's sensitive information
if (client.conn.protocol === 3) {
// @ts-ignore
this.id = nsp.name !== "/" ? nsp.name + "#" + client.id : client.id;
}
else {
this.id = base64id_1.default.generateId(); // don't reuse the Engine.IO id because it's sensitive information
}
this.pid = base64id_1.default.generateId();
}

@@ -159,7 +188,51 @@ this.handshake = this.buildHandshake(auth);

this.flags = {};
this.notifyOutgoingListeners(packet);
this.packet(packet, flags);
// @ts-ignore
if (this.nsp.server.opts.connectionStateRecovery) {
// this ensures the packet is stored and can be transmitted upon reconnection
this.adapter.broadcast(packet, {
rooms: new Set([this.id]),
except: new Set(),
flags,
});
}
else {
this.notifyOutgoingListeners(packet);
this.packet(packet, flags);
}
return true;
}
/**
* Emits an event and waits for an acknowledgement
*
* @example
* io.on("connection", async (socket) => {
* // without timeout
* const response = await socket.emitWithAck("hello", "world");
*
* // with a specific timeout
* try {
* const response = await socket.timeout(1000).emitWithAck("hello", "world");
* } catch (err) {
* // the client did not acknowledge the event in the given delay
* }
* });
*
* @return a Promise that will be fulfilled when the client acknowledges the event
*/
emitWithAck(ev, ...args) {
// the timeout flag is optional
const withErr = this.flags.timeout !== undefined;
return new Promise((resolve, reject) => {
args.push((arg1, arg2) => {
if (withErr) {
return arg1 ? reject(arg1) : resolve(arg2);
}
else {
return resolve(arg1);
}
});
this.emit(ev, ...args);
});
}
/**
* @private

@@ -348,3 +421,6 @@ */

else {
this.packet({ type: socket_io_parser_1.PacketType.CONNECT, data: { sid: this.id } });
this.packet({
type: socket_io_parser_1.PacketType.CONNECT,
data: { sid: this.id, pid: this.pid },
});
}

@@ -474,2 +550,11 @@ }

this.emitReserved("disconnecting", reason);
if (RECOVERABLE_DISCONNECT_REASONS.has(reason)) {
debug("connection state recovery is enabled for sid %s", this.id);
this.adapter.persistSession({
sid: this.id,
pid: this.pid,
rooms: [...this.rooms],
data: this.data,
});
}
this._cleanup();

@@ -476,0 +561,0 @@ this.nsp._remove(this);

@@ -110,2 +110,40 @@ /// <reference types="node" />

}
export declare type Last<T extends any[]> = T extends [...infer H, infer L] ? L : any;
export declare type AllButLast<T extends any[]> = T extends [...infer H, infer L] ? H : any[];
export declare type FirstArg<T> = T extends (arg: infer Param) => infer Result ? Param : any;
export declare type SecondArg<T> = T extends (err: Error, arg: infer Param) => infer Result ? Param : any;
declare type PrependTimeoutError<T extends any[]> = {
[K in keyof T]: T[K] extends (...args: infer Params) => infer Result ? (err: Error, ...args: Params) => Result : T[K];
};
declare type ExpectMultipleResponses<T extends any[]> = {
[K in keyof T]: T[K] extends (err: Error, arg: infer Param) => infer Result ? (err: Error, arg: Param[]) => Result : T[K];
};
/**
* Utility type to decorate the acknowledgement callbacks with a timeout error.
*
* This is needed because the timeout() flag breaks the symmetry between the sender and the receiver:
*
* @example
* interface Events {
* "my-event": (val: string) => void;
* }
*
* socket.on("my-event", (cb) => {
* cb("123"); // one single argument here
* });
*
* socket.timeout(1000).emit("my-event", (err, val) => {
* // two arguments there (the "err" argument is not properly typed)
* });
*
*/
export declare type DecorateAcknowledgements<E> = {
[K in keyof E]: E[K] extends (...args: infer Params) => infer Result ? (...args: PrependTimeoutError<Params>) => Result : E[K];
};
export declare type DecorateAcknowledgementsWithTimeoutAndMultipleResponses<E> = {
[K in keyof E]: E[K] extends (...args: infer Params) => infer Result ? (...args: ExpectMultipleResponses<PrependTimeoutError<Params>>) => Result : E[K];
};
export declare type DecorateAcknowledgementsWithMultipleResponses<E> = {
[K in keyof E]: E[K] extends (...args: infer Params) => infer Result ? (...args: ExpectMultipleResponses<Params>) => Result : E[K];
};
export {};
{
"name": "socket.io",
"version": "4.5.4",
"version": "4.6.0-alpha1",
"description": "node.js realtime framework server",

@@ -52,4 +52,4 @@ "keywords": [

"debug": "~4.3.2",
"engine.io": "~6.2.1",
"socket.io-adapter": "~2.4.0",
"engine.io": "~6.3.1",
"socket.io-adapter": "~2.5.2",
"socket.io-parser": "~4.2.1"

@@ -56,0 +56,0 @@ },

@@ -24,2 +24,3 @@ # socket.io

- [.NET](https://github.com/doghappy/socket.io-client-csharp)
- [Rust](https://github.com/1c3t3a/rust-socketio)

@@ -26,0 +27,0 @@ Its main features are:

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc