28 Commits

Author SHA1 Message Date
Damien Arrachequesne
0cb6ac95b4 fix(typings): ensure compatibility with TypeScript 3.x
Labeled tuple elements were added in TypeScript 4.0.

Reference: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#labeled-tuple-elements

Related: https://github.com/socketio/socket.io/issues/3916
2021-05-17 23:15:22 +02:00
Damien Arrachequesne
891b1870e9 fix(typings): properly type the adapter attribute
Related: https://github.com/socketio/socket.io/issues/3796
2021-05-11 23:59:44 +02:00
Damien Arrachequesne
b84ed1e41c fix(typings): properly type server-side events
See also: 93cce05fb3
2021-05-11 23:59:18 +02:00
Damien Arrachequesne
499c89250d feat: notify upon namespace creation
A "new_namespace" event will be emitted when a new namespace is created:

```js
io.on("new_namespace", (namespace) => {
  // ...
});
```

This could be used for example for registering the same middleware for
each namespace.

See https://github.com/socketio/socket.io/issues/3851
2021-05-11 00:09:18 +02:00
Damien Arrachequesne
93cce05fb3 feat: add support for inter-server communication
Syntax:

```js
// server A
io.serverSideEmit("hello", "world");

// server B
io.on("hello", (arg) => {
  console.log(arg); // prints "world"
});
```

With acknowledgements:

```js
// server A
io.serverSideEmit("hello", "world", (err, responses) => {
  console.log(responses); // prints ["hi"]
});

// server B
io.on("hello", (arg, callback) => {
  callback("hi");
});
```

This feature replaces the customHook/customRequest API from the Redis
adapter: https://github.com/socketio/socket.io-redis/issues/370
2021-05-11 00:07:20 +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
Sebastiaan Marynissen
7de2e87e88 feat: allow to exclude specific rooms when broadcasting (#3789)
New syntax:

```
io.except("room1").emit(...);
io.to("room1").except("room2").emit(...);

socket.broadcast.except("room1").emit(...);
socket.to("room1").except("room2").emit(...);
```

Related:

- https://github.com/socketio/socket.io/issues/3629
- https://github.com/socketio/socket.io/issues/3657
2021-03-01 09:30:58 +01:00
david-fong
9e8f288ca9 fix(typings): add return types and general-case overload signatures (#3776)
See also: https://stackoverflow.com/questions/52760509/typescript-returntype-of-overloaded-function/52760599#52760599
2021-02-02 11:50:08 +01:00
Damien Arrachequesne
9925746c8e feat: add support for Socket.IO v2 clients
In order to ease the migration to Socket.IO v3, the Socket.IO server
can now communicate with v2 clients.

```js
const io = require("socket.io")({
  allowEIO3: true
});
```

This feature is disabled by default.
2021-01-14 23:38:24 +01:00
David Fong
d1bfe40dbb refactor: add more typing info and upgrade prettier (#3725)
This upgrades prettier to 2.2.0 to gain support for TypeScript's new
type-only-imports feature.
2020-12-11 12:19:20 +01:00
Damien Arrachequesne
50671d984a fix(typings): update the signature of the emit method
The previous signature was not compatible with EventEmitter.emit(). The typescript compilation threw:

```
node_modules/socket.io/dist/namespace.d.ts(89,5): error TS2416: Property 'emit' in type 'Namespace' is not assignable to the same property in base type 'EventEmitter'.
  Type '(ev: string, ...args: any[]) => Namespace' is not assignable to type '(event: string | symbol, ...args: any[]) => boolean'.
    Type 'Namespace' is not assignable to type 'boolean'.
node_modules/socket.io/dist/socket.d.ts(84,5): error TS2416: Property 'emit' in type 'Socket' is not assignable to the same property in base type 'EventEmitter'.
  Type '(ev: string, ...args: any[]) => this' is not assignable to type '(event: string | symbol, ...args: any[]) => boolean'.
    Type 'this' is not assignable to type 'boolean'.
      Type 'Socket' is not assignable to type 'boolean'.
```

Note: the emit calls cannot be chained anymore:

```js
socket.emit("hello").emit("world"); // will not work anymore
```
2020-11-08 00:07:56 +01:00
Damien Arrachequesne
54bf4a44e9 feat: emit an Error object upon middleware error
This commit restores the ability to send additional data in the
middleware functions, which was removed during the rewrite to
Typescript ([1]).

The only difference with the previous implementation is that the client
will now emit a "connect_error" (previously, "error") event with an
actual Error object, with both the message and an optional "data"
attribute.

```js
// server-side
io.use((socket, next) => {
  const err = new Error("not authorized");
  err.data = { content: "Please retry later" };
  next(err);
});

// client-side
socket.on("connect_error", err => {
  console.log(err.message); // not authorized
  console.log(err.data.content); // Please retry later
});
```

[1]: a5581a9789
2020-10-30 22:52:08 +01:00
Damien Arrachequesne
8a5db7fa36 refactor: remove duplicate _sockets map
Both the "connected" and the "_sockets" maps were used to track the
Socket instances in the namespace.

Let's merge them into "sockets". It's a breaking change, but:

- the "sockets" object did already exist in Socket.IO v2 (and appears in some examples/tutorials)
- "sockets" makes more sense than "connected" in my opinion
- there was already a breaking change regarding the "connected" property (from object to Map)

Breaking change: the "connected" map is renamed to "sockets"
2020-10-15 12:45:42 +02:00
Damien Arrachequesne
2a05042e2c refactor: add additional typings 2020-10-15 12:04:42 +02:00
Damien Arrachequesne
58b66f8089 refactor: hide internal methods and properties
There is no concept of package-private methods in TypeScript, so we'll
just prefix them with "_" and mark them as private in the JSDoc.
2020-10-15 11:54:06 +02:00
Damien Arrachequesne
669592d120 feat: move binary detection back to the parser
See 285e7cd0d8

Breaking change: the Socket#binary() method is removed, as this use
case is now covered by the ability to provide your own parser.
2020-10-15 10:45:56 +02:00
Damien Arrachequesne
4bd5b2339a feat: throw upon reserved event names
These events cannot be used by the end users, because they are part of
the Socket.IO public API, so using them will now throw an error
explicitly.
2020-10-13 23:02:09 +02:00
Damien Arrachequesne
83a2356648 refactor: properly delegate to the main namespace 2020-10-13 23:02:08 +02:00
Damien Arrachequesne
3289f7ec37 feat: remove the implicit connection to the default namespace
In previous versions, a client was always connected to the default
namespace, even if it requested access to another namespace.

This meant that the middlewares registered for the default namespace
were triggered in any case, which is a surprising behavior for end
users.

This also meant that the query option of the Socket on the client-side
was not sent in the Socket.IO CONNECT packet for the default namespace:

```js
// default namespace: query sent in the query params
const socket = io({
  query: {
    abc: "def"
  }
});

// another namespace: query sent in the query params + the CONNECT packet
const socket = io("/admin", {
  query: {
    abc: "def"
  }
});
```

The client will now send a CONNECT packet in any case, and the query
option of the Socket is renamed to "auth", in order to make a clear
distinction with the query option of the Manager (included in the query
parameters of the HTTP requests).

```js
// server-side
io.use((socket, next) => {
  // not triggered anymore
});

io.of("/admin").use((socket, next => {
  // triggered
  console.log(socket.handshake.query.abc); // "def"
  console.log(socket.handshake.auth.abc); // "123"
});

// client-side
const socket = io("/admin", {
  query: {
    abc: "def"
  },
  auth: {
    abc: "123"
  }
});
```
2020-10-13 23:02:07 +02:00
Damien Arrachequesne
64bd9fb01a chore: include Engine.IO v4
Release notes: https://github.com/socketio/engine.io/releases/tag/4.0.0
2020-10-13 23:02:06 +02:00
Damien Arrachequesne
1108ede120 chore: bump socket.io-parser
Breaking change:

- the encode() method is now synchronous

Please note that the exchange [protocol][1] is left untouched and thus
stays in version 4.

Diff: https://github.com/socketio/socket.io-parser/compare/3.4.1...4.0.0

[1] https://github.com/socketio/socket.io-protocol
2020-09-28 16:07:09 +02:00
Damien Arrachequesne
424a473c22 refactor: use ES6 Maps instead of plain objects
These attributes were not part of the public API, so there's no
breaking change.
2020-09-26 01:21:51 +02:00
Damien Arrachequesne
84437dc2a6 chore: bump socket.io-adapter
Breaking changes:

- Namespace#connected is now a Map instead of an object.

- Namespace#clients() is renamed to Namespace#allSockets() and now
returns a Promise

Diff: https://github.com/socketio/socket.io-adapter/compare/1.1.2...2.0.0
2020-09-26 00:24:54 +02:00
Damien Arrachequesne
a5581a9789 refactor: migrate to TypeScript 2020-09-25 23:41:53 +02:00