diff --git a/packages/socket.io/lib/broadcast-operator.ts b/packages/socket.io/lib/broadcast-operator.ts index ca7eccf2f..b2d9c127c 100644 --- a/packages/socket.io/lib/broadcast-operator.ts +++ b/packages/socket.io/lib/broadcast-operator.ts @@ -325,6 +325,14 @@ export class BroadcastOperator ev: Ev, ...args: AllButLast> ): Promise>>> { + const defaultTimeout = this.adapter.nsp.server?.opts?.ackTimeout; + const operator = + this.flags.timeout === undefined && defaultTimeout !== undefined + ? new BroadcastOperator(this.adapter, this.rooms, this.exceptRooms, { + ...this.flags, + timeout: defaultTimeout, + }) + : this; return new Promise((resolve, reject) => { args.push((err, responses) => { if (err) { @@ -334,7 +342,10 @@ export class BroadcastOperator return resolve(responses); } }); - this.emit(ev, ...(args as any[] as EventParams)); + (operator as this).emit( + ev, + ...(args as any[] as EventParams), + ); }); } diff --git a/packages/socket.io/lib/index.ts b/packages/socket.io/lib/index.ts index 0cebc9254..bfbf7a464 100644 --- a/packages/socket.io/lib/index.ts +++ b/packages/socket.io/lib/index.ts @@ -45,6 +45,7 @@ import { RemoveAcknowledgements, EventNamesWithAck, FirstNonErrorArg, + EventNamesWithError, } from "./typed-events"; import { patchAdapter, restoreAdapter, serveFile } from "./uws"; import corsMiddleware from "cors"; @@ -94,6 +95,10 @@ interface ServerOptions extends EngineOptions, AttachOptions { * @default 45000 */ connectTimeout: number; + /** + * the default timeout in milliseconds used when waiting for an acknowledgement + */ + ackTimeout?: number; /** * Whether to enable the recovery of connection state when a client temporarily disconnects. * @@ -1086,6 +1091,21 @@ export class Server< return this.sockets.timeout(timeout); } + /** + * Emits an event and waits for an acknowledgement from all clients. + * + * @example + * const responses = await io.emitWithAck("some-event"); + * + * @return a Promise that will be fulfilled when all clients have acknowledged the event + */ + public emitWithAck>( + ev: Ev, + ...args: AllButLast> + ): Promise>>> { + return this.sockets.emitWithAck(ev, ...args); + } + /** * Returns the matching socket instances. * diff --git a/packages/socket.io/lib/namespace.ts b/packages/socket.io/lib/namespace.ts index 72df7301e..ae2ca0335 100644 --- a/packages/socket.io/lib/namespace.ts +++ b/packages/socket.io/lib/namespace.ts @@ -14,6 +14,7 @@ import { EventNamesWithAck, FirstNonErrorArg, EventNamesWithoutAck, + EventNamesWithError, } from "./typed-events"; import type { Client } from "./client"; import debugModule from "debug"; @@ -469,6 +470,23 @@ export class Namespace< ); } + /** + * Emits an event and waits for an acknowledgement from all clients. + * + * @example + * const responses = await myNamespace.emitWithAck("some-event"); + * + * @return a Promise that will be fulfilled when all clients have acknowledged the event + */ + public emitWithAck>( + ev: Ev, + ...args: AllButLast> + ): Promise>>> { + return new BroadcastOperator( + this.adapter, + ).emitWithAck(ev, ...args); + } + /** * Sends a `message` event to all clients. *