15 Commits

Author SHA1 Message Date
Waldemar Schlegel
0c0eb00163 fix: add timeout method to remote socket (#4558)
The RemoteSocket interface, which is returned when the client is
connected on another Socket.IO server of the cluster, was lacking the
`timeout()` method.

Syntax:

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

Related: https://github.com/socketio/socket.io/issues/4595
2023-01-24 09:24:19 +01:00
Damien Arrachequesne
184f3cf7af feat: add promise-based acknowledgements
This commit adds some syntactic sugar around acknowledgements:

- `emitWithAck()`

```js
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
}

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

- `serverSideEmitWithAck()`

```js
try {
  const responses = await io.timeout(1000).serverSideEmitWithAck("some-event");
  console.log(responses); // one response per server (except itself)
} catch (e) {
  // some servers did not acknowledge the event in the given delay
}
```

Related:

- https://github.com/socketio/socket.io/issues/4175
- https://github.com/socketio/socket.io/issues/4577
- https://github.com/socketio/socket.io/issues/4583
2023-01-23 09:06:25 +01:00
Damien Arrachequesne
f3ada7d8cc fix(typings): properly type emits with timeout
When emitting with a timeout (added in version 4.4.0), the "err"
argument was not properly typed and would require to split the client
and server typings. It will now be automatically inferred as an Error
object.

Workaround for previous versions:

```ts
type WithTimeoutAck<isEmitter extends boolean, args extends any[]> = isEmitter extends true ? [Error, ...args] : args;

interface ClientToServerEvents<isEmitter extends boolean = false> {
    withAck: (data: { argName: boolean }, callback: (...args: WithTimeoutAck<isEmitter, [string]>) => void) => void;
}

interface ServerToClientEvents<isEmitter extends boolean = false> {

}

const io = new Server<ClientToServerEvents, ServerToClientEvents<true>>(3000);

io.on("connection", (socket) => {
    socket.on("withAck", (val, cb) => {
        cb("123");
    });
});

const socket: Socket<ServerToClientEvents, ClientToServerEvents<true>> = ioc("http://localhost:3000");

socket.timeout(100).emit("withAck", { argName: true }, (err, val) => {
  // ...
});
```

Related: https://github.com/socketio/socket.io-client/issues/1555
2023-01-19 11:48:18 +01:00
Damien Arrachequesne
8fae95dd18 docs: add jsdoc for each public method 2022-10-14 10:30:08 +02:00
Damien Arrachequesne
e357daf585 fix(typings): apply types to "io.timeout(...).emit()" calls
Typed events were not applied when calling "io.timeout(...).emit()".

Related: https://github.com/socketio/socket.io-client/issues/1555#issuecomment-1277289733

Reference: https://socket.io/docs/v4/typescript/
2022-10-13 14:50:09 +02:00
Damien Arrachequesne
9890b036cf chore: bump dependencies
Production:

- socket.io-parser: ~4.0.4 => ~4.2.0

Development:

- superagent: ^6.1.0 => ^8.0.0
- tsd: ^0.17.0 => ^0.21.0

Related: https://github.com/socketio/socket.io/issues/3709
2022-06-27 09:16:08 +02:00
Damien Arrachequesne
30430f0985 fix: forward the local flag to the adapter when using fetchSockets()
Related:

- https://github.com/socketio/socket.io/issues/4359
- https://github.com/socketio/socket.io-redis-adapter/issues/454
2022-05-03 15:41:24 +02:00
Damien Arrachequesne
8b204570a9 feat: broadcast and expect multiple acks
Syntax:

```js
io.timeout(1000).emit("some-event", (err, responses) => {
  // ...
});
```

The adapter exposes two additional methods:

- `broadcastWithAck(packets, opts, clientCountCallback, ack)`

Similar to `broadcast(packets, opts)`, but:

* `clientCountCallback()` is called with the number of clients that
  received the packet (can be called several times in a cluster)
* `ack()` is called for each client response

- `serverCount()`

It returns the number of Socket.IO servers in the cluster (1 for the
in-memory adapter).

Those two methods will be implemented in the other adapters (Redis,
Postgres, MongoDB, ...).

Related:

- https://github.com/socketio/socket.io/issues/1811
- https://github.com/socketio/socket.io/issues/4163
- https://github.com/socketio/socket.io-redis-adapter/issues/445
2022-03-31 07:49:09 +02:00
Orkhan Alikhanov
770ee5949f fix(types): make RemoteSocket.data type safe (#4234)
Related:

- https://github.com/socketio/socket.io/issues/4229
- fe8730ca0f
2022-01-06 07:14:55 +01:00
Damien Arrachequesne
7c44893d78 chore: bump dependencies 2021-07-04 00:37:35 +02:00
Solomon English
1a72ae4fe2 fix(typings): update return type from emit (#3843)
```
(channel ? io.to(channel) : io).emit("stuff", message);
```

would no longer compile.

Related: https://github.com/socketio/socket.io/issues/3844
2021-03-18 11:36:34 +01:00
Maxime Kjaer
0107510ba8 feat: add support for typed events (#3822)
Syntax:

```ts
interface ClientToServerEvents {
  "my-event": (a: number, b: string, c: number[]) => void;
}

interface ServerToClientEvents {
  hello: (message: string) => void;
}

const io = new Server<ClientToServerEvents, ServerToClientEvents>(httpServer);

io.emit("hello", "world");

io.on("connection", (socket) => {
  socket.on("my-event", (a, b, c) => {
    // ...
  });

  socket.emit("hello", "again");
});
```

The events are not typed by default (inferred as any), so this change
is backward compatible.

Note: we could also have reused the method here ([1]) to add types to
the EventEmitter, instead of creating a StrictEventEmitter class.

Related: https://github.com/socketio/socket.io/issues/3742

[1]: https://github.com/binier/tiny-typed-emitter
2021-03-10 00:18:13 +01:00
Damien Arrachequesne
b25495c069 feat: add some utility methods
This commit adds the following methods:

- fetchSockets: returns the matching socket instances

Syntax:

```js
// return all Socket instances
const sockets = await io.fetchSockets();

// return all Socket instances of the "admin" namespace in the "room1" room
const sockets = await io.of("/admin").in("room1").fetchSockets();
```

- socketsJoin: makes the matching socket instances join the specified rooms

Syntax:

```js
// make all Socket instances join the "room1" room
io.socketsJoin("room1");

// make all Socket instances of the "admin" namespace in the "room1" room join the "room2" room
io.of("/admin").in("room1").socketsJoin("room2");
```

- socketsLeave: makes the matching socket instances leave the specified rooms

Syntax:

```js
// make all Socket instances leave the "room1" room
io.socketsLeave("room1");

// make all Socket instances of the "admin" namespace in the "room1" room leave the "room2" room
io.of("/admin").in("room1").socketsLeave("room2");
```

- disconnectSockets: makes the matching socket instances disconnect

Syntax:

```js
// make all Socket instances disconnect
io.disconnectSockets();

// make all Socket instances of the "admin" namespace in the "room1" room disconnect
io.of("/admin").in("room1").disconnectSockets();
```

Those methods share the same semantics as broadcasting. They will also
work with multiple Socket.IO servers when using the Redis adapter. In
that case, the fetchSockets() method will return a list of RemoteSocket
instances, which expose a subset of the methods and attributes of the
Socket class (the "request" attribute cannot be mocked, for example).

Related:

- https://github.com/socketio/socket.io/issues/3042
- https://github.com/socketio/socket.io/issues/3418
- https://github.com/socketio/socket.io/issues/3570
- https://github.com/socketio/socket.io-redis/issues/283
2021-03-02 11:17:21 +01:00
Damien Arrachequesne
085d1de9df feat: allow to pass an array to io.to(...)
In some cases it is necessary to pass an array of rooms instead of a single room.

New syntax:

```
io.to(["room1", "room2"]).except(["room3"]).emit(...);

socket.to(["room1", "room2"]).except(["room3"]).emit(...);
```

Related: https://github.com/socketio/socket.io/issues/3048
2021-03-01 23:20:46 +01:00
Damien Arrachequesne
ac9e8ca6c7 fix: make io.to(...) immutable
Previously, broadcasting to a given room (by calling `io.to()`) would
mutate the io instance, which could lead to surprising behaviors, like:

```js
io.to("room1");
io.to("room2").emit(...); // also sent to room1

// or with async/await
io.to("room3").emit("details", await fetchDetails()); // random behavior: maybe in room3, maybe to all clients
```

Calling `io.to()` (or any other broadcast modifier) will now return an
immutable instance.

Related:

- https://github.com/socketio/socket.io/issues/3431
- https://github.com/socketio/socket.io/issues/3444
2021-03-01 23:17:08 +01:00