Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve emit types #4817

Merged
merged 13 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
10 changes: 5 additions & 5 deletions lib/broadcast-operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import type {
EventsMap,
TypedEventBroadcaster,
DecorateAcknowledgements,
DecorateAcknowledgementsWithTimeoutAndMultipleResponses,
AllButLast,
Last,
SecondArg,
FirstNonErrorArg,
EventNamesWithError,
} from "./typed-events";

export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
Expand Down Expand Up @@ -177,7 +177,7 @@ export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
public timeout(timeout: number) {
const flags = Object.assign({}, this.flags, { timeout });
return new BroadcastOperator<
DecorateAcknowledgementsWithTimeoutAndMultipleResponses<EmitEvents>,
DecorateAcknowledgements<EmitEvents>,
SocketData
>(this.adapter, this.rooms, this.exceptRooms, flags);
}
Expand Down Expand Up @@ -300,10 +300,10 @@ export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
*
* @return a Promise that will be fulfilled when all clients have acknowledged the event
*/
public emitWithAck<Ev extends EventNames<EmitEvents>>(
public emitWithAck<Ev extends EventNamesWithError<EmitEvents>>(
ev: Ev,
...args: AllButLast<EventParams<EmitEvents, Ev>>
): Promise<SecondArg<Last<EventParams<EmitEvents, Ev>>>> {
): Promise<FirstNonErrorArg<Last<EventParams<EmitEvents, Ev>>>> {
return new Promise((resolve, reject) => {
args.push((err, responses) => {
if (err) {
Expand Down
32 changes: 7 additions & 25 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ import {
DecorateAcknowledgementsWithTimeoutAndMultipleResponses,
AllButLast,
Last,
FirstArg,
SecondArg,
RemoveAcknowledgements,
EventNamesWithAck,
FirstNonErrorArg,
DecorateAcknowledgementsWithMultipleResponses,
} from "./typed-events";
import { patchAdapter, restoreAdapter, serveFile } from "./uws";
import corsMiddleware from "cors";
Expand Down Expand Up @@ -140,7 +142,7 @@ export class Server<
SocketData = any
> extends StrictEventEmitter<
ServerSideEvents,
EmitEvents,
RemoveAcknowledgements<EmitEvents>,
ServerReservedEventsMap<
ListenEvents,
EmitEvents,
Expand Down Expand Up @@ -846,26 +848,6 @@ export class Server<
return this.sockets.except(room);
}

/**
* 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
*/
public emitWithAck<Ev extends EventNames<EmitEvents>>(
ev: Ev,
...args: AllButLast<EventParams<EmitEvents, Ev>>
): Promise<SecondArg<Last<EventParams<EmitEvents, Ev>>>> {
return this.sockets.emitWithAck(ev, ...args);
}

/**
* Sends a `message` event to all clients.
*
Expand Down Expand Up @@ -948,10 +930,10 @@ export class Server<
*
* @return a Promise that will be fulfilled when all servers have acknowledged the event
*/
public serverSideEmitWithAck<Ev extends EventNames<ServerSideEvents>>(
public serverSideEmitWithAck<Ev extends EventNamesWithAck<ServerSideEvents>>(
ev: Ev,
...args: AllButLast<EventParams<ServerSideEvents, Ev>>
): Promise<FirstArg<Last<EventParams<ServerSideEvents, Ev>>>[]> {
): Promise<FirstNonErrorArg<Last<EventParams<ServerSideEvents, Ev>>>[]> {
return this.sockets.serverSideEmitWithAck(ev, ...args);
}

Expand Down
89 changes: 45 additions & 44 deletions lib/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import {
DecorateAcknowledgementsWithTimeoutAndMultipleResponses,
AllButLast,
Last,
FirstArg,
SecondArg,
DecorateAcknowledgementsWithMultipleResponses,
DecorateAcknowledgements,
RemoveAcknowledgements,
EventNamesWithAck,
FirstNonErrorArg,
} from "./typed-events";
import type { Client } from "./client";
import debugModule from "debug";
Expand Down Expand Up @@ -117,7 +120,7 @@ export class Namespace<
SocketData = any
> extends StrictEventEmitter<
ServerSideEvents,
EmitEvents,
RemoveAcknowledgements<EmitEvents>,
NamespaceReservedEventsMap<
ListenEvents,
EmitEvents,
Expand Down Expand Up @@ -252,7 +255,10 @@ export class Namespace<
* @return a new {@link BroadcastOperator} instance for chaining
*/
public to(room: Room | Room[]) {
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).to(room);
return new BroadcastOperator<
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we move the DecorateAcknowledgementsWithMultipleResponses type to the BroadcastOperator class, so we don't have to apply it everytime?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely think that would be nicer; however after looking at the code, I don't think we can.
The BroadcastOperator has an expectSingleResponse flag, which changes the behavior between single responses and multiple. With that, the options I see are to either: apply the decorations when switching to a BroadcastOperator, or to setup the expectSingleResponse as a third generic type parameter on the BroadcastOperator.

SocketData
>(this.adapter).to(room);
}

/**
Expand All @@ -268,7 +274,10 @@ export class Namespace<
* @return a new {@link BroadcastOperator} instance for chaining
*/
public in(room: Room | Room[]) {
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).in(room);
return new BroadcastOperator<
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
SocketData
>(this.adapter).in(room);
}

/**
Expand All @@ -290,9 +299,10 @@ export class Namespace<
* @return a new {@link BroadcastOperator} instance for chaining
*/
public except(room: Room | Room[]) {
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).except(
room
);
return new BroadcastOperator<
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
SocketData
>(this.adapter).except(room);
}

/**
Expand Down Expand Up @@ -432,38 +442,14 @@ export class Namespace<
*/
public emit<Ev extends EventNames<EmitEvents>>(
ev: Ev,
...args: EventParams<EmitEvents, Ev>
...args: EventParams<RemoveAcknowledgements<EmitEvents>, Ev>
): boolean {
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).emit(
ev,
...args
);
}

/**
* 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
*/
public emitWithAck<Ev extends EventNames<EmitEvents>>(
ev: Ev,
...args: AllButLast<EventParams<EmitEvents, Ev>>
): Promise<SecondArg<Last<EventParams<EmitEvents, Ev>>>> {
return new BroadcastOperator<EmitEvents, SocketData>(
this.adapter
).emitWithAck(ev, ...args);
}

/**
* Sends a `message` event to all clients.
*
Expand Down Expand Up @@ -557,10 +543,10 @@ export class Namespace<
*
* @return a Promise that will be fulfilled when all servers have acknowledged the event
*/
public serverSideEmitWithAck<Ev extends EventNames<ServerSideEvents>>(
public serverSideEmitWithAck<Ev extends EventNamesWithAck<ServerSideEvents>>(
ev: Ev,
...args: AllButLast<EventParams<ServerSideEvents, Ev>>
): Promise<FirstArg<Last<EventParams<ServerSideEvents, Ev>>>[]> {
): Promise<FirstNonErrorArg<Last<EventParams<ServerSideEvents, Ev>>>[]> {
return new Promise((resolve, reject) => {
args.push((err, responses) => {
if (err) {
Expand Down Expand Up @@ -612,9 +598,10 @@ export class Namespace<
* @return self
*/
public compress(compress: boolean) {
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).compress(
compress
);
return new BroadcastOperator<
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
SocketData
>(this.adapter).compress(compress);
}

/**
Expand All @@ -630,7 +617,10 @@ export class Namespace<
* @return self
*/
public get volatile() {
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).volatile;
return new BroadcastOperator<
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
SocketData
>(this.adapter).volatile;
}

/**
Expand All @@ -645,7 +635,10 @@ export class Namespace<
* @return a new {@link BroadcastOperator} instance for chaining
*/
public get local() {
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).local;
return new BroadcastOperator<
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
SocketData
>(this.adapter).local;
}

/**
Expand All @@ -664,10 +657,18 @@ export class Namespace<
*
* @param timeout
*/
public timeout(timeout: number) {
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).timeout(
timeout
);
public timeout(
timeout: number
): BroadcastOperator<
DecorateAcknowledgements<
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>
>,
SocketData
> {
return new BroadcastOperator<
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
SocketData
>(this.adapter).timeout(timeout);
}

/**
Expand Down
7 changes: 4 additions & 3 deletions lib/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import {
DecorateAcknowledgementsWithMultipleResponses,
DefaultEventsMap,
EventNames,
EventNamesWithAck,
EventParams,
EventsMap,
FirstArg,
FirstNonErrorArg,
Last,
StrictEventEmitter,
} from "./typed-events";
Expand Down Expand Up @@ -383,10 +384,10 @@ export class Socket<
*
* @return a Promise that will be fulfilled when the client acknowledges the event
*/
public emitWithAck<Ev extends EventNames<EmitEvents>>(
public emitWithAck<Ev extends EventNamesWithAck<EmitEvents>>(
ev: Ev,
...args: AllButLast<EventParams<EmitEvents, Ev>>
): Promise<FirstArg<Last<EventParams<EmitEvents, Ev>>>> {
): Promise<FirstNonErrorArg<Last<EventParams<EmitEvents, Ev>>>> {
// the timeout flag is optional
const withErr = this.flags.timeout !== undefined;
return new Promise((resolve, reject) => {
Expand Down
Loading