Compare commits

...

11 Commits
4.4.0 ... 4.4.1

Author SHA1 Message Date
Damien Arrachequesne
c82a4bdf1f chore(release): 4.4.1
Diff: https://github.com/socketio/socket.io/compare/4.4.0...4.4.1
2022-01-06 07:32:03 +01: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
3bf5d92735 refactor: add note about fetchSockets() for parent namespaces
Related: https://github.com/socketio/socket.io/issues/4235
2022-01-05 08:50:40 +01:00
Shayan Yousefi
fc82e44f73 refactor(typings): export Event type (#4215)
So that it can be used by the end users:

```ts
const myMiddleware = ([eventName, ...args]: Event, next: (err?: Error) => void) => {
  console.log(eventName); // inferred as string
  next();
}

io.on("connection", (socket) => {
  socket.use(myMiddleware);
});
```
2022-01-05 08:08:18 +01:00
Damien Arrachequesne
c840bad43a test: fix flaky tests 2022-01-05 08:00:55 +01:00
Orkhan Alikhanov
f2b8de7191 fix(typings): pass SocketData type to custom namespaces (#4233)
The `SocketData` type was only available on the main namespace.

Related: https://github.com/socketio/socket.io/issues/4229
See also: fe8730ca0f
2022-01-04 09:09:42 +01:00
Gray Zhang
51784d0305 chore: add types to exports field to be compatible with nodenext module resolution (#4228)
See [1] for detail, in `nodenext` module resolution it requires a
`types` field in `exports` with full filename including extension.

[1]: https://github.com/microsoft/TypeScript/issues/46770#issuecomment-966612103
2021-12-28 10:27:08 +01:00
Damien Arrachequesne
c196689545 docs: fix basic crud example
Related: https://github.com/socketio/socket.io/issues/4213
2021-12-16 23:00:20 +01:00
Mikhail Dudin
7a70f63499 docs: fix reconnection handling in the chat demo app (#4189) 2021-12-01 00:03:43 +01:00
anderslatif
e5897dd7dc docs: add usage with ES modules (#4195) 2021-12-01 00:02:13 +01:00
Damien Arrachequesne
2071a66c5a docs: simplify nginx cluster example
- remove useless Dockerfile
- clean format
- migrate to @socket.io/redis-adapter
2021-11-24 18:15:26 +01:00
23 changed files with 4056 additions and 221 deletions

View File

@@ -1,3 +1,13 @@
## [4.4.1](https://github.com/socketio/socket.io/compare/4.4.0...4.4.1) (2022-01-06)
### Bug Fixes
* **types:** make `RemoteSocket.data` type safe ([#4234](https://github.com/socketio/socket.io/issues/4234)) ([770ee59](https://github.com/socketio/socket.io/commit/770ee5949fb47c2556876c622f06c862573657d6))
* **types:** pass `SocketData` type to custom namespaces ([#4233](https://github.com/socketio/socket.io/issues/4233)) ([f2b8de7](https://github.com/socketio/socket.io/commit/f2b8de71919e1b4d3e57f15a459972c1d1064787))
# [4.4.0](https://github.com/socketio/socket.io/compare/4.3.2...4.4.0) (2021-11-18)

View File

@@ -115,6 +115,14 @@ io.on('connection', client => { ... });
io.listen(3000);
```
### Module syntax
```js
import { Server } from "socket.io";
const io = new Server(server);
io.listen(3000);
```
### In conjunction with Express
Starting with **3.0**, express applications have become request handler

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
/*!
* Socket.IO v4.4.0
* (c) 2014-2021 Guillermo Rauch
* Socket.IO v4.4.1
* (c) 2014-2022 Guillermo Rauch
* Released under the MIT License.
*/
(function (global, factory) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -7,8 +7,8 @@ export enum Errors {
const errorValues: string[] = Object.values(Errors);
export function sanitizeErrorMessage(message: string) {
if (errorValues.includes(message)) {
export function sanitizeErrorMessage(message: any) {
if (typeof message === "string" && errorValues.includes(message)) {
return message;
} else {
return "an unknown error has occurred";

View File

@@ -6,7 +6,7 @@ A simple chat demo for Socket.IO
## How to use
```
$ npm ci
$ npm i
$ npm start
```

View File

@@ -264,14 +264,14 @@ $(function() {
log('you have been disconnected');
});
socket.on('reconnect', () => {
socket.io.on('reconnect', () => {
log('you have been reconnected');
if (username) {
socket.emit('add user', username);
}
});
socket.on('reconnect_error', () => {
socket.io.on('reconnect_error', () => {
log('attempt to reconnect has failed');
});

View File

@@ -1,6 +1,8 @@
nginx:
build: ./nginx
image: nginx:alpine
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
links:
- server-john
- server-paul

View File

@@ -1,3 +0,0 @@
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf

View File

@@ -1,15 +1,18 @@
// Setup basic express server
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var redis = require('socket.io-redis');
var port = process.env.PORT || 3000;
var serverName = process.env.NAME || 'Unknown';
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const { createAdapter } = require('@socket.io/redis-adapter');
const { createClient } = require('redis');
const port = process.env.PORT || 3000;
const serverName = process.env.NAME || 'Unknown';
io.adapter(redis({ host: 'redis', port: 6379 }));
const pubClient = createClient({ host: 'redis', port: 6379 });
const subClient = pubClient.duplicate();
server.listen(port, function () {
io.adapter(createAdapter(pubClient, subClient));
server.listen(port, () => {
console.log('Server listening at port %d', port);
console.log('Hello, I\'m %s, how can I help?', serverName);
});
@@ -19,15 +22,15 @@ app.use(express.static(__dirname + '/public'));
// Chatroom
var numUsers = 0;
let numUsers = 0;
io.on('connection', function (socket) {
io.on('connection', socket => {
socket.emit('my-name-is', serverName);
var addedUser = false;
let addedUser = false;
// when the client emits 'new message', this listens and executes
socket.on('new message', function (data) {
socket.on('new message', data => {
// we tell the client to execute 'new message'
socket.broadcast.emit('new message', {
username: socket.username,
@@ -36,7 +39,7 @@ io.on('connection', function (socket) {
});
// when the client emits 'add user', this listens and executes
socket.on('add user', function (username) {
socket.on('add user', username => {
if (addedUser) return;
// we store the username in the socket session for this client
@@ -54,21 +57,21 @@ io.on('connection', function (socket) {
});
// when the client emits 'typing', we broadcast it to others
socket.on('typing', function () {
socket.on('typing', () => {
socket.broadcast.emit('typing', {
username: socket.username
});
});
// when the client emits 'stop typing', we broadcast it to others
socket.on('stop typing', function () {
socket.on('stop typing', () => {
socket.broadcast.emit('stop typing', {
username: socket.username
});
});
// when the user disconnects.. perform this
socket.on('disconnect', function () {
socket.on('disconnect', () => {
if (addedUser) {
--numUsers;

View File

@@ -7,9 +7,10 @@
"private": true,
"license": "MIT",
"dependencies": {
"@socket.io/redis-adapter": "^7.0.1",
"express": "4.13.4",
"socket.io": "^4.0.0",
"socket.io-redis": "^6.0.1"
"redis": "^3.1.2",
"socket.io": "^4.0.0"
},
"scripts": {
"start": "node index.js"

View File

@@ -9,7 +9,7 @@ import type {
TypedEventBroadcaster,
} from "./typed-events";
export class BroadcastOperator<EmitEvents extends EventsMap>
export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
implements TypedEventBroadcaster<EmitEvents>
{
constructor(
@@ -26,7 +26,7 @@ export class BroadcastOperator<EmitEvents extends EventsMap>
* @return a new BroadcastOperator instance
* @public
*/
public to(room: Room | Room[]): BroadcastOperator<EmitEvents> {
public to(room: Room | Room[]): BroadcastOperator<EmitEvents, SocketData> {
const rooms = new Set(this.rooms);
if (Array.isArray(room)) {
room.forEach((r) => rooms.add(r));
@@ -48,7 +48,7 @@ export class BroadcastOperator<EmitEvents extends EventsMap>
* @return a new BroadcastOperator instance
* @public
*/
public in(room: Room | Room[]): BroadcastOperator<EmitEvents> {
public in(room: Room | Room[]): BroadcastOperator<EmitEvents, SocketData> {
return this.to(room);
}
@@ -59,7 +59,9 @@ export class BroadcastOperator<EmitEvents extends EventsMap>
* @return a new BroadcastOperator instance
* @public
*/
public except(room: Room | Room[]): BroadcastOperator<EmitEvents> {
public except(
room: Room | Room[]
): BroadcastOperator<EmitEvents, SocketData> {
const exceptRooms = new Set(this.exceptRooms);
if (Array.isArray(room)) {
room.forEach((r) => exceptRooms.add(r));
@@ -81,7 +83,9 @@ export class BroadcastOperator<EmitEvents extends EventsMap>
* @return a new BroadcastOperator instance
* @public
*/
public compress(compress: boolean): BroadcastOperator<EmitEvents> {
public compress(
compress: boolean
): BroadcastOperator<EmitEvents, SocketData> {
const flags = Object.assign({}, this.flags, { compress });
return new BroadcastOperator(
this.adapter,
@@ -99,7 +103,7 @@ export class BroadcastOperator<EmitEvents extends EventsMap>
* @return a new BroadcastOperator instance
* @public
*/
public get volatile(): BroadcastOperator<EmitEvents> {
public get volatile(): BroadcastOperator<EmitEvents, SocketData> {
const flags = Object.assign({}, this.flags, { volatile: true });
return new BroadcastOperator(
this.adapter,
@@ -115,7 +119,7 @@ export class BroadcastOperator<EmitEvents extends EventsMap>
* @return a new BroadcastOperator instance
* @public
*/
public get local(): BroadcastOperator<EmitEvents> {
public get local(): BroadcastOperator<EmitEvents, SocketData> {
const flags = Object.assign({}, this.flags, { local: true });
return new BroadcastOperator(
this.adapter,
@@ -177,7 +181,9 @@ export class BroadcastOperator<EmitEvents extends EventsMap>
*
* @public
*/
public fetchSockets(): Promise<RemoteSocket<EmitEvents>[]> {
public fetchSockets<SocketData = any>(): Promise<
RemoteSocket<EmitEvents, SocketData>[]
> {
return this.adapter
.fetchSockets({
rooms: this.rooms,
@@ -187,9 +193,12 @@ export class BroadcastOperator<EmitEvents extends EventsMap>
return sockets.map((socket) => {
if (socket instanceof Socket) {
// FIXME the TypeScript compiler complains about missing private properties
return socket as unknown as RemoteSocket<EmitEvents>;
return socket as unknown as RemoteSocket<EmitEvents, SocketData>;
} else {
return new RemoteSocket(this.adapter, socket as SocketDetails);
return new RemoteSocket(
this.adapter,
socket as SocketDetails<SocketData>
);
}
});
});
@@ -247,27 +256,27 @@ export class BroadcastOperator<EmitEvents extends EventsMap>
/**
* Format of the data when the Socket instance exists on another Socket.IO server
*/
interface SocketDetails {
interface SocketDetails<SocketData> {
id: SocketId;
handshake: Handshake;
rooms: Room[];
data: any;
data: SocketData;
}
/**
* Expose of subset of the attributes and methods of the Socket class
*/
export class RemoteSocket<EmitEvents extends EventsMap>
export class RemoteSocket<EmitEvents extends EventsMap, SocketData>
implements TypedEventBroadcaster<EmitEvents>
{
public readonly id: SocketId;
public readonly handshake: Handshake;
public readonly rooms: Set<Room>;
public readonly data: any;
public readonly data: SocketData;
private readonly operator: BroadcastOperator<EmitEvents>;
private readonly operator: BroadcastOperator<EmitEvents, SocketData>;
constructor(adapter: Adapter, details: SocketDetails) {
constructor(adapter: Adapter, details: SocketDetails<SocketData>) {
this.id = details.id;
this.handshake = details.handshake;
this.rooms = new Set(details.rooms);

View File

@@ -112,11 +112,13 @@ export class Server<
/**
* @private
*/
_nsps: Map<string, Namespace<ListenEvents, EmitEvents, ServerSideEvents>> =
new Map();
_nsps: Map<
string,
Namespace<ListenEvents, EmitEvents, ServerSideEvents, SocketData>
> = new Map();
private parentNsps: Map<
ParentNspNameMatchFn,
ParentNamespace<ListenEvents, EmitEvents, ServerSideEvents>
ParentNamespace<ListenEvents, EmitEvents, ServerSideEvents, SocketData>
> = new Map();
private _adapter?: AdapterConstructor;
private _serveClient: boolean;
@@ -197,7 +199,9 @@ export class Server<
name: string,
auth: { [key: string]: any },
fn: (
nsp: Namespace<ListenEvents, EmitEvents, ServerSideEvents> | false
nsp:
| Namespace<ListenEvents, EmitEvents, ServerSideEvents, SocketData>
| false
) => void
): void {
if (this.parentNsps.size === 0) return fn(false);
@@ -579,7 +583,7 @@ export class Server<
fn?: (
socket: Socket<ListenEvents, EmitEvents, ServerSideEvents, SocketData>
) => void
): Namespace<ListenEvents, EmitEvents, ServerSideEvents> {
): Namespace<ListenEvents, EmitEvents, ServerSideEvents, SocketData> {
if (typeof name === "function" || name instanceof RegExp) {
const parentNsp = new ParentNamespace(this);
debug("initializing parent namespace %s", parentNsp.name);
@@ -660,7 +664,7 @@ export class Server<
* @return self
* @public
*/
public to(room: Room | Room[]): BroadcastOperator<EmitEvents> {
public to(room: Room | Room[]): BroadcastOperator<EmitEvents, SocketData> {
return this.sockets.to(room);
}
@@ -671,7 +675,7 @@ export class Server<
* @return self
* @public
*/
public in(room: Room | Room[]): BroadcastOperator<EmitEvents> {
public in(room: Room | Room[]): BroadcastOperator<EmitEvents, SocketData> {
return this.sockets.in(room);
}
@@ -682,7 +686,9 @@ export class Server<
* @return self
* @public
*/
public except(name: Room | Room[]): BroadcastOperator<EmitEvents> {
public except(
name: Room | Room[]
): BroadcastOperator<EmitEvents, SocketData> {
return this.sockets.except(name);
}
@@ -738,7 +744,9 @@ export class Server<
* @return self
* @public
*/
public compress(compress: boolean): BroadcastOperator<EmitEvents> {
public compress(
compress: boolean
): BroadcastOperator<EmitEvents, SocketData> {
return this.sockets.compress(compress);
}
@@ -750,7 +758,7 @@ export class Server<
* @return self
* @public
*/
public get volatile(): BroadcastOperator<EmitEvents> {
public get volatile(): BroadcastOperator<EmitEvents, SocketData> {
return this.sockets.volatile;
}
@@ -760,7 +768,7 @@ export class Server<
* @return self
* @public
*/
public get local(): BroadcastOperator<EmitEvents> {
public get local(): BroadcastOperator<EmitEvents, SocketData> {
return this.sockets.local;
}
@@ -769,7 +777,7 @@ export class Server<
*
* @public
*/
public fetchSockets(): Promise<RemoteSocket<EmitEvents>[]> {
public fetchSockets(): Promise<RemoteSocket<EmitEvents, SocketData>[]> {
return this.sockets.fetchSockets();
}
@@ -826,3 +834,4 @@ module.exports.Namespace = Namespace;
module.exports.Socket = Socket;
export { Socket, ServerOptions, Namespace, BroadcastOperator, RemoteSocket };
export { Event } from "./socket";

View File

@@ -175,7 +175,7 @@ export class Namespace<
* @return self
* @public
*/
public to(room: Room | Room[]): BroadcastOperator<EmitEvents> {
public to(room: Room | Room[]): BroadcastOperator<EmitEvents, SocketData> {
return new BroadcastOperator(this.adapter).to(room);
}
@@ -186,7 +186,7 @@ export class Namespace<
* @return self
* @public
*/
public in(room: Room | Room[]): BroadcastOperator<EmitEvents> {
public in(room: Room | Room[]): BroadcastOperator<EmitEvents, SocketData> {
return new BroadcastOperator(this.adapter).in(room);
}
@@ -197,7 +197,9 @@ export class Namespace<
* @return self
* @public
*/
public except(room: Room | Room[]): BroadcastOperator<EmitEvents> {
public except(
room: Room | Room[]
): BroadcastOperator<EmitEvents, SocketData> {
return new BroadcastOperator(this.adapter).except(room);
}
@@ -274,7 +276,10 @@ export class Namespace<
ev: Ev,
...args: EventParams<EmitEvents, Ev>
): boolean {
return new BroadcastOperator<EmitEvents>(this.adapter).emit(ev, ...args);
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).emit(
ev,
...args
);
}
/**
@@ -346,7 +351,9 @@ export class Namespace<
* @return self
* @public
*/
public compress(compress: boolean): BroadcastOperator<EmitEvents> {
public compress(
compress: boolean
): BroadcastOperator<EmitEvents, SocketData> {
return new BroadcastOperator(this.adapter).compress(compress);
}
@@ -358,7 +365,7 @@ export class Namespace<
* @return self
* @public
*/
public get volatile(): BroadcastOperator<EmitEvents> {
public get volatile(): BroadcastOperator<EmitEvents, SocketData> {
return new BroadcastOperator(this.adapter).volatile;
}
@@ -368,7 +375,7 @@ export class Namespace<
* @return self
* @public
*/
public get local(): BroadcastOperator<EmitEvents> {
public get local(): BroadcastOperator<EmitEvents, SocketData> {
return new BroadcastOperator(this.adapter).local;
}
@@ -377,7 +384,7 @@ export class Namespace<
*
* @public
*/
public fetchSockets(): Promise<RemoteSocket<EmitEvents>[]> {
public fetchSockets(): Promise<RemoteSocket<EmitEvents, SocketData>[]> {
return new BroadcastOperator(this.adapter).fetchSockets();
}

View File

@@ -1,5 +1,5 @@
import { Namespace } from "./namespace";
import type { Server } from "./index";
import type { Server, RemoteSocket } from "./index";
import type {
EventParams,
EventNames,
@@ -64,4 +64,13 @@ export class ParentNamespace<
this.server._nsps.set(name, namespace);
return namespace;
}
fetchSockets(): Promise<RemoteSocket<EmitEvents, SocketData>[]> {
// note: we could make the fetchSockets() method work for dynamic namespaces created with a regex (by sending the
// regex to the other Socket.IO servers, and returning the sockets of each matching namespace for example), but
// the behavior for namespaces created with a function is less clear
// note²: we cannot loop over each children namespace, because with multiple Socket.IO servers, a given namespace
// may exist on one node but not exist on another (since it is created upon client connection)
throw new Error("fetchSockets() is not supported on parent namespaces");
}
}

View File

@@ -107,7 +107,7 @@ export interface Handshake {
auth: { [key: string]: any };
}
type Event = [eventName: string, ...args: any[]];
export type Event = [eventName: string, ...args: any[]];
export class Socket<
ListenEvents extends EventsMap = DefaultEventsMap,
@@ -251,7 +251,7 @@ export class Socket<
* @return self
* @public
*/
public to(room: Room | Room[]): BroadcastOperator<EmitEvents> {
public to(room: Room | Room[]): BroadcastOperator<EmitEvents, SocketData> {
return this.newBroadcastOperator().to(room);
}
@@ -262,7 +262,7 @@ export class Socket<
* @return self
* @public
*/
public in(room: Room | Room[]): BroadcastOperator<EmitEvents> {
public in(room: Room | Room[]): BroadcastOperator<EmitEvents, SocketData> {
return this.newBroadcastOperator().in(room);
}
@@ -273,7 +273,9 @@ export class Socket<
* @return self
* @public
*/
public except(room: Room | Room[]): BroadcastOperator<EmitEvents> {
public except(
room: Room | Room[]
): BroadcastOperator<EmitEvents, SocketData> {
return this.newBroadcastOperator().except(room);
}
@@ -577,7 +579,7 @@ export class Socket<
* @return {Socket} self
* @public
*/
public get broadcast(): BroadcastOperator<EmitEvents> {
public get broadcast(): BroadcastOperator<EmitEvents, SocketData> {
return this.newBroadcastOperator();
}
@@ -587,7 +589,7 @@ export class Socket<
* @return {Socket} self
* @public
*/
public get local(): BroadcastOperator<EmitEvents> {
public get local(): BroadcastOperator<EmitEvents, SocketData> {
return this.newBroadcastOperator().local;
}
@@ -764,7 +766,7 @@ export class Socket<
return this._anyListeners || [];
}
private newBroadcastOperator(): BroadcastOperator<EmitEvents> {
private newBroadcastOperator(): BroadcastOperator<EmitEvents, SocketData> {
const flags = Object.assign({}, this.flags);
this.flags = {};
return new BroadcastOperator(

4043
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "socket.io",
"version": "4.4.0",
"version": "4.4.1",
"description": "node.js realtime framework server",
"keywords": [
"realtime",
@@ -27,7 +27,8 @@
"main": "./dist/index.js",
"exports": {
"import": "./wrapper.mjs",
"require": "./dist/index.js"
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"types": "./dist/index.d.ts",
"license": "MIT",
@@ -59,7 +60,7 @@
"nyc": "^15.1.0",
"prettier": "^2.3.2",
"rimraf": "^3.0.2",
"socket.io-client": "4.4.0",
"socket.io-client": "4.4.1",
"socket.io-client-v2": "npm:socket.io-client@^2.4.0",
"superagent": "^6.1.0",
"supertest": "^6.1.6",

View File

@@ -1830,7 +1830,7 @@ describe("socket.io", () => {
reconnectionDelay: 100,
});
clientSocket.once("connect", () => {
srv.close(() => {
sio.close(() => {
clientSocket.io.on("reconnect", () => {
clientSocket.emit("ev", "payload");
});

View File

@@ -46,8 +46,10 @@ describe("socket.io with uWebSocket.js-based engine", () => {
});
const partialDone = createPartialDone(done, 4);
io.on("connection", partialDone);
io.of("/custom").on("connection", partialDone);
client.on("connect", partialDone);
clientWSOnly.on("connect", partialDone);
clientPollingOnly.on("connect", partialDone);
clientCustomNamespace.on("connect", partialDone);
});
afterEach(() => {