Compare commits

...

17 Commits
3.1.2 ... 4.0.2

Author SHA1 Message Date
Damien Arrachequesne
9fff03487c chore(release): 4.0.2
Diff: https://github.com/socketio/socket.io/compare/4.0.1...4.0.2
2021-05-06 14:38:26 +02:00
Damien Arrachequesne
b81ce4c9d0 fix(typings): make "engine" attribute public 2021-05-06 14:36:25 +02:00
Damien Arrachequesne
d65b6ee84c fix: properly export the Socket class
Before this change, `require("socket.io").Socket` would return
"undefined".

Note: having access to the Socket class allows users to modify its
prototype.

Related: https://github.com/socketio/socket.io/issues/3726
2021-05-06 14:36:12 +02:00
Damien Arrachequesne
3665aada47 docs(examples): basic CRUD application
See also: https://socket.io/get-started/basic-crud-application/
2021-04-23 00:08:18 +02:00
Damien Arrachequesne
1faa7e3aea chore(release): 4.0.1
Diff: https://github.com/socketio/socket.io/compare/4.0.0...4.0.1
2021-04-01 01:25:56 +02:00
Maxime Kjaer
a11152f42b fix(typings): add fallback to untyped event listener (#3834)
Related: https://github.com/socketio/socket.io/issues/3833
2021-03-31 11:37:37 +02:00
Maxime Kjaer
259f29720b docs(examples): remove unnecessary type annotations (#3855)
Typed events in Socket.IO 4.0 remove the need for writing type
annotations in callbacks of reserved events.
2021-03-24 23:12:22 +01:00
Damien Arrachequesne
b4ae8d2e19 docs(examples): update all examples to Socket.IO v4 2021-03-18 15:07:04 +01:00
Adán Toscano
64be1c9985 docs(examples): fix chat example (#3787) 2021-03-18 14:06:13 +01: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
Damien Arrachequesne
5eaeffc8e2 chore(release): 4.0.0
Diff: https://github.com/socketio/socket.io/compare/3.1.2...4.0.0
2021-03-10 12:43:53 +01:00
Damien Arrachequesne
1b6d6de4ed chore: include Engine.IO v5
Release notes: https://github.com/socketio/engine.io/releases/tag/5.0.0
2021-03-10 11:14:33 +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
100 changed files with 7267 additions and 29615 deletions

View File

@@ -1,3 +1,37 @@
## [4.0.2](https://github.com/socketio/socket.io/compare/4.0.1...4.0.2) (2021-05-06)
### Bug Fixes
* **typings:** make "engine" attribute public ([b81ce4c](https://github.com/socketio/socket.io/commit/b81ce4c9d0b00666361498e2ba5e0d007d5860b8))
* properly export the Socket class ([d65b6ee](https://github.com/socketio/socket.io/commit/d65b6ee84c8e91deb61c3c1385eb19afa196a909))
## [4.0.1](https://github.com/socketio/socket.io/compare/4.0.0...4.0.1) (2021-03-31)
### Bug Fixes
* **typings:** add fallback to untyped event listener ([#3834](https://github.com/socketio/socket.io/issues/3834)) ([a11152f](https://github.com/socketio/socket.io/commit/a11152f42b281df83409313962f60f230239c79e))
* **typings:** update return type from emit ([#3843](https://github.com/socketio/socket.io/issues/3843)) ([1a72ae4](https://github.com/socketio/socket.io/commit/1a72ae4fe27a14cf60916f991a2c94da91d9e54a))
# [4.0.0](https://github.com/socketio/socket.io/compare/3.1.2...4.0.0) (2021-03-10)
### Bug Fixes
* make io.to(...) immutable ([ac9e8ca](https://github.com/socketio/socket.io/commit/ac9e8ca6c71e00d4af45ee03f590fe56f3951186))
### Features
* add some utility methods ([b25495c](https://github.com/socketio/socket.io/commit/b25495c069031674da08e19aed68922c7c7a0e28))
* add support for typed events ([#3822](https://github.com/socketio/socket.io/issues/3822)) ([0107510](https://github.com/socketio/socket.io/commit/0107510ba8a0f148c78029d8be8919b350feb633))
* allow to exclude specific rooms when broadcasting ([#3789](https://github.com/socketio/socket.io/issues/3789)) ([7de2e87](https://github.com/socketio/socket.io/commit/7de2e87e888d849eb2dfc5e362af4c9e86044701))
* allow to pass an array to io.to(...) ([085d1de](https://github.com/socketio/socket.io/commit/085d1de9df909651de8b313cc6f9f253374b702e))
## [3.1.2](https://github.com/socketio/socket.io/compare/3.1.1...3.1.2) (2021-02-26)

View File

@@ -1,5 +1,5 @@
/*!
* Socket.IO v3.1.2
* Socket.IO v4.0.2
* (c) 2014-2021 Guillermo Rauch
* Released under the MIT License.
*/
@@ -12,7 +12,7 @@
exports["io"] = factory();
else
root["io"] = factory();
})(this, function() {
})(self, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
@@ -116,21 +116,12 @@ function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "functi
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Socket = exports.io = exports.Manager = exports.protocol = void 0;
exports.io = exports.Socket = exports.Manager = exports.protocol = void 0;
var url_1 = __webpack_require__(/*! ./url */ "./build/url.js");
var manager_1 = __webpack_require__(/*! ./manager */ "./build/manager.js");
var socket_1 = __webpack_require__(/*! ./socket */ "./build/socket.js");
Object.defineProperty(exports, "Socket", {
enumerable: true,
get: function get() {
return socket_1.Socket;
}
});
var debug = __webpack_require__(/*! debug */ "./node_modules/debug/src/browser.js")("socket.io-client");
/**
* Module exports.
@@ -151,7 +142,7 @@ function lookup(uri, opts) {
}
opts = opts || {};
var parsed = url_1.url(uri, opts.path);
var parsed = url_1.url(uri, opts.path || "/socket.io");
var source = parsed.source;
var id = parsed.id;
var path = parsed.path;
@@ -216,6 +207,16 @@ Object.defineProperty(exports, "Manager", {
}
});
var socket_1 = __webpack_require__(/*! ./socket */ "./build/socket.js");
Object.defineProperty(exports, "Socket", {
enumerable: true,
get: function get() {
return socket_1.Socket;
}
});
exports["default"] = lookup;
/***/ }),
/***/ "./build/manager.js":
@@ -236,10 +237,6 @@ function _defineProperties(target, props) { for (var i = 0; i < props.length; i+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); }
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
@@ -263,18 +260,18 @@ var eio = __webpack_require__(/*! engine.io-client */ "./node_modules/engine.io-
var socket_1 = __webpack_require__(/*! ./socket */ "./build/socket.js");
var Emitter = __webpack_require__(/*! component-emitter */ "./node_modules/component-emitter/index.js");
var parser = __webpack_require__(/*! socket.io-parser */ "./node_modules/socket.io-parser/dist/index.js");
var on_1 = __webpack_require__(/*! ./on */ "./build/on.js");
var Backoff = __webpack_require__(/*! backo2 */ "./node_modules/backo2/index.js");
var typed_events_1 = __webpack_require__(/*! ./typed-events */ "./build/typed-events.js");
var debug = __webpack_require__(/*! debug */ "./node_modules/debug/src/browser.js")("socket.io-client:manager");
var Manager = /*#__PURE__*/function (_Emitter) {
_inherits(Manager, _Emitter);
var Manager = /*#__PURE__*/function (_typed_events_1$Stric) {
_inherits(Manager, _typed_events_1$Stric);
var _super = _createSuper(Manager);
@@ -425,7 +422,7 @@ var Manager = /*#__PURE__*/function (_Emitter) {
self.cleanup();
self._readyState = "closed";
_get(_getPrototypeOf(Manager.prototype), "emit", _this2).call(_this2, "error", err);
_this2.emitReserved("error", err);
if (fn) {
fn(err);
@@ -450,6 +447,11 @@ var Manager = /*#__PURE__*/function (_Emitter) {
socket.close();
socket.emit("error", new Error("timeout"));
}, timeout);
if (this.opts.autoUnref) {
timer.unref();
}
this.subs.push(function subDestroy() {
clearTimeout(timer);
});
@@ -485,9 +487,7 @@ var Manager = /*#__PURE__*/function (_Emitter) {
this.cleanup(); // mark as open
this._readyState = "open";
_get(_getPrototypeOf(Manager.prototype), "emit", this).call(this, "open"); // add new subs
this.emitReserved("open"); // add new subs
var socket = this.engine;
this.subs.push(on_1.on(socket, "ping", this.onping.bind(this)), on_1.on(socket, "data", this.ondata.bind(this)), on_1.on(socket, "error", this.onerror.bind(this)), on_1.on(socket, "close", this.onclose.bind(this)), on_1.on(this.decoder, "decoded", this.ondecoded.bind(this)));
@@ -501,7 +501,7 @@ var Manager = /*#__PURE__*/function (_Emitter) {
}, {
key: "onping",
value: function onping() {
_get(_getPrototypeOf(Manager.prototype), "emit", this).call(this, "ping");
this.emitReserved("ping");
}
/**
* Called with data.
@@ -523,7 +523,7 @@ var Manager = /*#__PURE__*/function (_Emitter) {
}, {
key: "ondecoded",
value: function ondecoded(packet) {
_get(_getPrototypeOf(Manager.prototype), "emit", this).call(this, "packet", packet);
this.emitReserved("packet", packet);
}
/**
* Called upon socket error.
@@ -535,8 +535,7 @@ var Manager = /*#__PURE__*/function (_Emitter) {
key: "onerror",
value: function onerror(err) {
debug("error", err);
_get(_getPrototypeOf(Manager.prototype), "emit", this).call(this, "error", err);
this.emitReserved("error", err);
}
/**
* Creates a new socket for the given `nsp`.
@@ -661,8 +660,7 @@ var Manager = /*#__PURE__*/function (_Emitter) {
this.cleanup();
this.backoff.reset();
this._readyState = "closed";
_get(_getPrototypeOf(Manager.prototype), "emit", this).call(this, "close", reason);
this.emitReserved("close", reason);
if (this._reconnection && !this.skipReconnect) {
this.reconnect();
@@ -685,9 +683,7 @@ var Manager = /*#__PURE__*/function (_Emitter) {
if (this.backoff.attempts >= this._reconnectionAttempts) {
debug("reconnect failed");
this.backoff.reset();
_get(_getPrototypeOf(Manager.prototype), "emit", this).call(this, "reconnect_failed");
this.emitReserved("reconnect_failed");
this._reconnecting = false;
} else {
var delay = this.backoff.duration();
@@ -697,7 +693,7 @@ var Manager = /*#__PURE__*/function (_Emitter) {
if (self.skipReconnect) return;
debug("attempting reconnect");
_get(_getPrototypeOf(Manager.prototype), "emit", _this3).call(_this3, "reconnect_attempt", self.backoff.attempts); // check again for the case socket closed in above events
_this3.emitReserved("reconnect_attempt", self.backoff.attempts); // check again for the case socket closed in above events
if (self.skipReconnect) return;
@@ -707,13 +703,18 @@ var Manager = /*#__PURE__*/function (_Emitter) {
self._reconnecting = false;
self.reconnect();
_get(_getPrototypeOf(Manager.prototype), "emit", _this3).call(_this3, "reconnect_error", err);
_this3.emitReserved("reconnect_error", err);
} else {
debug("reconnect success");
self.onreconnect();
}
});
}, delay);
if (this.opts.autoUnref) {
timer.unref();
}
this.subs.push(function subDestroy() {
clearTimeout(timer);
});
@@ -731,13 +732,12 @@ var Manager = /*#__PURE__*/function (_Emitter) {
var attempt = this.backoff.attempts;
this._reconnecting = false;
this.backoff.reset();
_get(_getPrototypeOf(Manager.prototype), "emit", this).call(this, "reconnect", attempt);
this.emitReserved("reconnect", attempt);
}
}]);
return Manager;
}(Emitter);
}(typed_events_1.StrictEventEmitter);
exports.Manager = Manager;
@@ -818,10 +818,10 @@ exports.Socket = void 0;
var socket_io_parser_1 = __webpack_require__(/*! socket.io-parser */ "./node_modules/socket.io-parser/dist/index.js");
var Emitter = __webpack_require__(/*! component-emitter */ "./node_modules/component-emitter/index.js");
var on_1 = __webpack_require__(/*! ./on */ "./build/on.js");
var typed_events_1 = __webpack_require__(/*! ./typed-events */ "./build/typed-events.js");
var debug = __webpack_require__(/*! debug */ "./node_modules/debug/src/browser.js")("socket.io-client:socket");
/**
* Internal events.
@@ -839,8 +839,8 @@ var RESERVED_EVENTS = Object.freeze({
removeListener: 1
});
var Socket = /*#__PURE__*/function (_Emitter) {
_inherits(Socket, _Emitter);
var Socket = /*#__PURE__*/function (_typed_events_1$Stric) {
_inherits(Socket, _typed_events_1$Stric);
var _super = _createSuper(Socket);
@@ -942,7 +942,6 @@ var Socket = /*#__PURE__*/function (_Emitter) {
* Override `emit`.
* If the event is in `events`, it's emitted normally.
*
* @param ev - event name
* @return self
* @public
*/
@@ -1038,7 +1037,7 @@ var Socket = /*#__PURE__*/function (_Emitter) {
key: "onerror",
value: function onerror(err) {
if (!this.connected) {
_get(_getPrototypeOf(Socket.prototype), "emit", this).call(this, "connect_error", err);
this.emitReserved("connect_error", err);
}
}
/**
@@ -1055,8 +1054,7 @@ var Socket = /*#__PURE__*/function (_Emitter) {
this.connected = false;
this.disconnected = true;
delete this.id;
_get(_getPrototypeOf(Socket.prototype), "emit", this).call(this, "disconnect", reason);
this.emitReserved("disconnect", reason);
}
/**
* Called with socket packet.
@@ -1077,7 +1075,7 @@ var Socket = /*#__PURE__*/function (_Emitter) {
var id = packet.data.sid;
this.onconnect(id);
} else {
_get(_getPrototypeOf(Socket.prototype), "emit", this).call(this, "connect_error", new Error("It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)"));
this.emitReserved("connect_error", new Error("It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)"));
}
break;
@@ -1106,9 +1104,7 @@ var Socket = /*#__PURE__*/function (_Emitter) {
var err = new Error(packet.data.message); // @ts-ignore
err.data = packet.data.data;
_get(_getPrototypeOf(Socket.prototype), "emit", this).call(this, "connect_error", err);
this.emitReserved("connect_error", err);
break;
}
}
@@ -1220,10 +1216,8 @@ var Socket = /*#__PURE__*/function (_Emitter) {
this.id = id;
this.connected = true;
this.disconnected = false;
_get(_getPrototypeOf(Socket.prototype), "emit", this).call(this, "connect");
this.emitBuffered();
this.emitReserved("connect");
}
/**
* Emit buffered events (received and emitted).
@@ -1429,12 +1423,172 @@ var Socket = /*#__PURE__*/function (_Emitter) {
}]);
return Socket;
}(Emitter);
}(typed_events_1.StrictEventEmitter);
exports.Socket = Socket;
/***/ }),
/***/ "./build/typed-events.js":
/*!*******************************!*\
!*** ./build/typed-events.js ***!
\*******************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); }
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.StrictEventEmitter = void 0;
var Emitter = __webpack_require__(/*! component-emitter */ "./node_modules/component-emitter/index.js");
/**
* Strictly typed version of an `EventEmitter`. A `TypedEventEmitter` takes type
* parameters for mappings of event names to event data types, and strictly
* types method calls to the `EventEmitter` according to these event maps.
*
* @typeParam ListenEvents - `EventsMap` of user-defined events that can be
* listened to with `on` or `once`
* @typeParam EmitEvents - `EventsMap` of user-defined events that can be
* emitted with `emit`
* @typeParam ReservedEvents - `EventsMap` of reserved events, that can be
* emitted by socket.io with `emitReserved`, and can be listened to with
* `listen`.
*/
var StrictEventEmitter = /*#__PURE__*/function (_Emitter) {
_inherits(StrictEventEmitter, _Emitter);
var _super = _createSuper(StrictEventEmitter);
function StrictEventEmitter() {
_classCallCheck(this, StrictEventEmitter);
return _super.apply(this, arguments);
}
_createClass(StrictEventEmitter, [{
key: "on",
/**
* Adds the `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
value: function on(ev, listener) {
_get(_getPrototypeOf(StrictEventEmitter.prototype), "on", this).call(this, ev, listener);
return this;
}
/**
* Adds a one-time `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
}, {
key: "once",
value: function once(ev, listener) {
_get(_getPrototypeOf(StrictEventEmitter.prototype), "once", this).call(this, ev, listener);
return this;
}
/**
* Emits an event.
*
* @param ev Name of the event
* @param args Values to send to listeners of this event
*/
}, {
key: "emit",
value: function emit(ev) {
var _get2;
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
(_get2 = _get(_getPrototypeOf(StrictEventEmitter.prototype), "emit", this)).call.apply(_get2, [this, ev].concat(args));
return this;
}
/**
* Emits a reserved event.
*
* This method is `protected`, so that only a class extending
* `StrictEventEmitter` can emit its own reserved events.
*
* @param ev Reserved event name
* @param args Arguments to emit along with the event
*/
}, {
key: "emitReserved",
value: function emitReserved(ev) {
var _get3;
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
(_get3 = _get(_getPrototypeOf(StrictEventEmitter.prototype), "emit", this)).call.apply(_get3, [this, ev].concat(args));
return this;
}
/**
* Returns the listeners listening to an event.
*
* @param event Event name
* @returns Array of listeners subscribed to `event`
*/
}, {
key: "listeners",
value: function listeners(event) {
return _get(_getPrototypeOf(StrictEventEmitter.prototype), "listeners", this).call(this, event);
}
}]);
return StrictEventEmitter;
}(Emitter);
exports.StrictEventEmitter = StrictEventEmitter;
/***/ }),
/***/ "./build/url.js":
/*!**********************!*\
!*** ./build/url.js ***!
@@ -2442,6 +2596,14 @@ var Socket = /*#__PURE__*/function (_Emitter) {
_this.transport.close();
}
}, false);
if (_this.hostname !== "localhost") {
_this.offlineEventListener = function () {
_this.onClose("transport close");
};
addEventListener("offline", _this.offlineEventListener, false);
}
}
_this.open();
@@ -2765,6 +2927,10 @@ var Socket = /*#__PURE__*/function (_Emitter) {
this.pingTimeoutTimer = setTimeout(function () {
_this2.onClose("ping timeout");
}, this.pingInterval + this.pingTimeout);
if (this.opts.autoUnref) {
this.pingTimeoutTimer.unref();
}
}
/**
* Called on `drain` event
@@ -2949,7 +3115,12 @@ var Socket = /*#__PURE__*/function (_Emitter) {
this.transport.close(); // ignore further transport communication
this.transport.removeAllListeners(); // set ready state
this.transport.removeAllListeners();
if (typeof removeEventListener === "function") {
removeEventListener("offline", this.offlineEventListener, false);
} // set ready state
this.readyState = "closed"; // clear session id
@@ -3046,6 +3217,8 @@ var parser = __webpack_require__(/*! engine.io-parser */ "./node_modules/engine.
var Emitter = __webpack_require__(/*! component-emitter */ "./node_modules/component-emitter/index.js");
var debug = __webpack_require__(/*! debug */ "./node_modules/debug/src/browser.js")("engine.io-client:transport");
var Transport = /*#__PURE__*/function (_Emitter) {
_inherits(Transport, _Emitter);
@@ -3132,7 +3305,8 @@ var Transport = /*#__PURE__*/function (_Emitter) {
if ("open" === this.readyState) {
this.write(packets);
} else {
throw new Error("Transport not open");
// this might happen if the transport was silently closed in the beforeunload event handler
debug("transport is not open, discarding packets");
}
}
/**
@@ -3198,7 +3372,7 @@ module.exports = Transport;
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
var XMLHttpRequest = __webpack_require__(/*! xmlhttprequest-ssl */ "./node_modules/engine.io-client/lib/xmlhttprequest.js");
var XMLHttpRequest = __webpack_require__(/*! ../../contrib/xmlhttprequest-ssl/XMLHttpRequest */ "./node_modules/engine.io-client/lib/xmlhttprequest.js");
var XHR = __webpack_require__(/*! ./polling-xhr */ "./node_modules/engine.io-client/lib/transports/polling-xhr.js");
@@ -3531,7 +3705,7 @@ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Re
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
/* global attachEvent */
var XMLHttpRequest = __webpack_require__(/*! xmlhttprequest-ssl */ "./node_modules/engine.io-client/lib/xmlhttprequest.js");
var XMLHttpRequest = __webpack_require__(/*! ../../contrib/xmlhttprequest-ssl/XMLHttpRequest */ "./node_modules/engine.io-client/lib/xmlhttprequest.js");
var Polling = __webpack_require__(/*! ./polling */ "./node_modules/engine.io-client/lib/transports/polling.js");
@@ -3698,7 +3872,7 @@ var Request = /*#__PURE__*/function (_Emitter) {
_createClass(Request, [{
key: "create",
value: function create() {
var opts = pick(this.opts, "agent", "enablesXDR", "pfx", "key", "passphrase", "cert", "ca", "ciphers", "rejectUnauthorized");
var opts = pick(this.opts, "agent", "enablesXDR", "pfx", "key", "passphrase", "cert", "ca", "ciphers", "rejectUnauthorized", "autoUnref");
opts.xdomain = !!this.opts.xd;
opts.xscheme = !!this.opts.xs;
var xhr = this.xhr = new XMLHttpRequest(opts);
@@ -4321,22 +4495,24 @@ var WS = /*#__PURE__*/function (_Transport) {
}, {
key: "addEventListeners",
value: function addEventListeners() {
var self = this;
var _this2 = this;
this.ws.onopen = function () {
self.onOpen();
if (_this2.opts.autoUnref) {
_this2.ws._socket.unref();
}
_this2.onOpen();
};
this.ws.onclose = function () {
self.onClose();
};
this.ws.onclose = this.onClose.bind(this);
this.ws.onmessage = function (ev) {
self.onData(ev.data);
return _this2.onData(ev.data);
};
this.ws.onerror = function (e) {
self.onError("websocket error", e);
return _this2.onError("websocket error", e);
};
}
/**

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
examples/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
package-lock.json

File diff suppressed because it is too large Load Diff

View File

@@ -21,8 +21,8 @@
"@angular/platform-browser-dynamic": "~11.0.4",
"@angular/router": "~11.0.4",
"rxjs": "~6.6.0",
"socket.io": "^3.0.4",
"socket.io-client": "^3.0.4",
"socket.io": "^4.0.0",
"socket.io-client": "^4.0.0",
"tslib": "^2.0.0",
"zone.js": "~0.10.2"
},

View File

@@ -1,4 +1,4 @@
import { Server, Socket } from "socket.io";
import { Server } from "socket.io";
const io = new Server(8080, {
cors: {
@@ -15,7 +15,7 @@ interface Todo {
let todos: Array<Todo> = [];
io.on("connect", (socket: Socket) => {
io.on("connect", (socket) => {
socket.emit("todos", todos);
// note: we could also create a CRUD (create/read/update/delete) service for the todo list

View File

@@ -0,0 +1,19 @@
# Basic CRUD application with Socket.IO
Please read the related [guide](https://socket.io/get-started/basic-crud-application/).
## Running the frontend
```
cd angular-client
npm install
npm start
```
### Running the server
```
cd server
npm install
npm start
```

View File

@@ -0,0 +1,17 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.

View File

@@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View File

@@ -0,0 +1,46 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,31 @@
# Angular TodoMVC + Socket.IO
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 11.0.4.
Inspired from the [TodoMVC](http://todomvc.com/) [angular example](https://github.com/tastejs/todomvc/tree/master/examples/angular2).
![demo](assets/demo.gif)
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

View File

@@ -0,0 +1,128 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"angular-todomvc": {
"projectType": "application",
"schematics": {
"@schematics/angular:application": {
"strict": true
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/angular-todomvc",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "angular-todomvc:build"
},
"configurations": {
"production": {
"browserTarget": "angular-todomvc:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "angular-todomvc:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "angular-todomvc:serve"
},
"configurations": {
"production": {
"devServerTarget": "angular-todomvc:serve:production"
}
}
}
}
}
},
"defaultProject": "angular-todomvc"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

View File

@@ -0,0 +1,37 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome'
},
directConnect: true,
SELENIUM_PROMISE_MANAGER: false,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
}
};

View File

@@ -0,0 +1,23 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', async () => {
await page.navigateTo();
expect(await page.getTitleText()).toEqual('angular-todomvc app is running!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

View File

@@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
async navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl);
}
async getTitleText(): Promise<string> {
return element(by.css('app-root .content span')).getText();
}
}

View File

@@ -0,0 +1,13 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": [
"jasmine",
"node"
]
}
}

View File

@@ -0,0 +1,44 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, './coverage/angular-todomvc'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' }
]
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

View File

@@ -0,0 +1,46 @@
{
"name": "angular-todomvc",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~11.0.4",
"@angular/common": "~11.0.4",
"@angular/compiler": "~11.0.4",
"@angular/core": "~11.0.4",
"@angular/forms": "~11.0.4",
"@angular/platform-browser": "~11.0.4",
"@angular/platform-browser-dynamic": "~11.0.4",
"@angular/router": "~11.0.4",
"rxjs": "~6.6.0",
"socket.io-client": "^4.0.0",
"tslib": "^2.0.0",
"zone.js": "~0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1100.4",
"@angular/cli": "~11.0.4",
"@angular/compiler-cli": "~11.0.4",
"@types/jasmine": "~3.6.0",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.1.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.0.2"
}
}

View File

@@ -0,0 +1,23 @@
<section class="todoapp">
<header class="header">
<h1>todos</h1>
<input class="new-todo" placeholder="What needs to be done?" autofocus="" [(ngModel)]="newTodoText" (keyup.enter)="addTodo()">
</header>
<section class="main" *ngIf="todoStore.todos.length > 0">
<input id="toggle-all" class="toggle-all" type="checkbox" *ngIf="todoStore.todos.length" #toggleall [checked]="todoStore.allCompleted()" (click)="todoStore.setAllTo(toggleall.checked)">
<ul class="todo-list">
<li *ngFor="let todo of todoStore.todos" [class.completed]="todo.completed" [class.editing]="todo.editing">
<div class="view">
<input class="toggle" type="checkbox" (click)="toggleCompletion(todo)" [checked]="todo.completed">
<label (dblclick)="editTodo(todo)">{{todo.title}}</label>
<button class="destroy" (click)="remove(todo)"></button>
</div>
<input class="edit" *ngIf="todo.editing" [value]="todo.title" #editedtodo (blur)="stopEditing(todo, editedtodo.value)" (keyup.enter)="updateEditingTodo(todo, editedtodo.value)" (keyup.escape)="cancelEditingTodo(todo)">
</li>
</ul>
</section>
<footer class="footer" *ngIf="todoStore.todos.length > 0">
<span class="todo-count"><strong>{{todoStore.getRemaining().length}}</strong> {{todoStore.getRemaining().length == 1 ? 'item' : 'items'}} left</span>
<button class="clear-completed" *ngIf="todoStore.getCompleted().length > 0" (click)="removeCompleted()">Clear completed</button>
</footer>
</section>

View File

@@ -0,0 +1,31 @@
import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'angular-todomvc'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('angular-todomvc');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('.content span').textContent).toContain('angular-todomvc app is running!');
});
});

View File

@@ -0,0 +1,59 @@
import { Component } from '@angular/core';
import { TodoStore, Todo } from './store';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
todoStore: TodoStore;
newTodoText = '';
constructor(todoStore: TodoStore) {
this.todoStore = todoStore;
}
stopEditing(todo: Todo, editedTitle: string) {
todo.title = editedTitle;
todo.editing = false;
}
cancelEditingTodo(todo: Todo) {
todo.editing = false;
}
updateEditingTodo(todo: Todo, editedTitle: string) {
editedTitle = editedTitle.trim();
todo.editing = false;
if (editedTitle.length === 0) {
return this.todoStore.remove(todo);
}
todo.title = editedTitle;
}
editTodo(todo: Todo) {
todo.editing = true;
}
removeCompleted() {
this.todoStore.removeCompleted();
}
toggleCompletion(todo: Todo) {
this.todoStore.toggleCompletion(todo);
}
remove(todo: Todo){
this.todoStore.remove(todo);
}
addTodo() {
if (this.newTodoText.trim().length) {
this.todoStore.add(this.newTodoText);
this.newTodoText = '';
}
}
}

View File

@@ -0,0 +1,19 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { TodoStore } from './store';
import { FormsModule } from "@angular/forms";
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [TodoStore],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@@ -0,0 +1,140 @@
import { io, Socket } from "socket.io-client";
import { ClientEvents, ServerEvents } from "../../../server/lib/events";
import { environment } from '../environments/environment';
export interface Todo {
id: string,
title: string,
completed: boolean,
editing: boolean,
synced: boolean
}
const mapTodo = (todo: any) => {
return {
...todo,
editing: false,
synced: true
}
}
export class TodoStore {
public todos: Array<Todo> = [];
private socket: Socket<ServerEvents, ClientEvents>;
constructor() {
this.socket = io(environment.serverUrl);
this.socket.on("connect", () => {
this.socket.emit("todo:list", (res) => {
if ("error" in res) {
// handle the error
return;
}
this.todos = res.data.map(mapTodo);
});
});
this.socket.on("todo:created", (todo) => {
this.todos.push(mapTodo(todo));
});
this.socket.on("todo:updated", (todo) => {
const existingTodo = this.todos.find(t => {
return t.id === todo.id
});
if (existingTodo) {
existingTodo.title = todo.title;
existingTodo.completed = todo.completed;
}
});
this.socket.on("todo:deleted", (id) => {
const index = this.todos.findIndex(t => {
return t.id === id
});
if (index !== -1) {
this.todos.splice(index, 1);
}
})
}
private getWithCompleted(completed: boolean) {
return this.todos.filter((todo: Todo) => todo.completed === completed);
}
allCompleted() {
return this.todos.length === this.getCompleted().length;
}
setAllTo(completed: boolean) {
this.todos.forEach(todo => {
todo.completed = completed;
todo.synced = false;
this.socket.emit("todo:update", todo, (res) => {
if (res && "error" in res) {
// handle the error
return;
}
todo.synced = true;
});
});
}
removeCompleted() {
this.getCompleted().forEach((todo) => {
this.socket.emit("todo:delete", todo.id, (res) => {
if (res && "error" in res) {
// handle the error
}
});
})
this.todos = this.getRemaining();
}
getRemaining() {
return this.getWithCompleted(false);
}
getCompleted() {
return this.getWithCompleted(true);
}
toggleCompletion(todo: Todo) {
todo.completed = !todo.completed;
todo.synced = false;
this.socket.emit("todo:update", todo, (res) => {
if (res && "error" in res) {
// handle the error
return;
}
todo.synced = true;
})
}
remove(todo: Todo) {
this.todos.splice(this.todos.indexOf(todo), 1);
this.socket.emit("todo:delete", todo.id, (res) => {
if (res && "error" in res) {
// handle the error
}
});
}
add(title: string) {
this.socket.emit("todo:create", { title, completed: false }, (res) => {
if ("error" in res) {
// handle the error
return;
}
this.todos.push({
id: res.data,
title,
completed: false,
editing: false,
synced: true
});
});
}
}

View File

@@ -0,0 +1,4 @@
export const environment = {
production: true,
serverUrl: "https://my-custom-domain.com"
};

View File

@@ -0,0 +1,17 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
serverUrl: "http://localhost:3000"
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Angular Todo MVC</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@@ -0,0 +1,12 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

View File

@@ -0,0 +1,63 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

View File

@@ -0,0 +1,381 @@
/* imported from node_modules/todomvc-app-css/index.css */
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
font-weight: inherit;
color: inherit;
-webkit-appearance: none;
appearance: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #f5f5f5;
color: #111111;
min-width: 230px;
max-width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-weight: 300;
}
:focus {
outline: 0;
}
.hidden {
display: none;
}
.todoapp {
background: #fff;
margin: 130px 0 40px 0;
position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.todoapp input::-webkit-input-placeholder {
font-style: italic;
font-weight: 300;
color: rgba(0, 0, 0, 0.4);
}
.todoapp input::-moz-placeholder {
font-style: italic;
font-weight: 300;
color: rgba(0, 0, 0, 0.4);
}
.todoapp input::input-placeholder {
font-style: italic;
font-weight: 300;
color: rgba(0, 0, 0, 0.4);
}
.todoapp h1 {
position: absolute;
top: -140px;
width: 100%;
font-size: 80px;
font-weight: 200;
text-align: center;
color: #b83f45;
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
.new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
font-weight: inherit;
line-height: 1.4em;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
}
.main {
position: relative;
z-index: 2;
border-top: 1px solid #e6e6e6;
}
.toggle-all {
width: 1px;
height: 1px;
border: none; /* Mobile Safari */
opacity: 0;
position: absolute;
right: 100%;
bottom: 100%;
}
.toggle-all + label {
width: 60px;
height: 34px;
font-size: 0;
position: absolute;
top: -52px;
left: -13px;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
.toggle-all + label:before {
content: '';
font-size: 22px;
color: #e6e6e6;
padding: 10px 27px 10px 27px;
}
.toggle-all:checked + label:before {
color: #737373;
}
.todo-list {
margin: 0;
padding: 0;
list-style: none;
}
.todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px solid #ededed;
}
.todo-list li:last-child {
border-bottom: none;
}
.todo-list li.editing {
border-bottom: none;
padding: 0;
}
.todo-list li.editing .edit {
display: block;
width: calc(100% - 43px);
padding: 12px 16px;
margin: 0 0 0 43px;
}
.todo-list li.editing .view {
display: none;
}
.todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
border: none; /* Mobile Safari */
-webkit-appearance: none;
appearance: none;
}
.todo-list li .toggle {
opacity: 0;
}
.todo-list li .toggle + label {
/*
Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
*/
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
background-repeat: no-repeat;
background-position: center left;
}
.todo-list li .toggle:checked + label {
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
}
.todo-list li label {
word-break: break-all;
padding: 15px 15px 15px 60px;
display: block;
line-height: 1.2;
transition: color 0.4s;
font-weight: 400;
color: #4d4d4d;
}
.todo-list li.completed label {
color: #cdcdcd;
text-decoration: line-through;
}
.todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 30px;
color: #cc9a9a;
margin-bottom: 11px;
transition: color 0.2s ease-out;
}
.todo-list li .destroy:hover {
color: #af5b5e;
}
.todo-list li .destroy:after {
content: '×';
}
.todo-list li:hover .destroy {
display: block;
}
.todo-list li .edit {
display: none;
}
.todo-list li.editing:last-child {
margin-bottom: -1px;
}
.footer {
padding: 10px 15px;
height: 20px;
text-align: center;
font-size: 15px;
border-top: 1px solid #e6e6e6;
}
.footer:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
0 8px 0 -3px #f6f6f6,
0 9px 1px -3px rgba(0, 0, 0, 0.2),
0 16px 0 -6px #f6f6f6,
0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
.todo-count {
float: left;
text-align: left;
}
.todo-count strong {
font-weight: 300;
}
.filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
.filters li {
display: inline;
}
.filters li a {
color: inherit;
margin: 3px;
padding: 3px 7px;
text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
}
.filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
.clear-completed,
html .clear-completed:active {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
cursor: pointer;
}
.clear-completed:hover {
text-decoration: underline;
}
.info {
margin: 65px auto 0;
color: #4d4d4d;
font-size: 11px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-align: center;
}
.info p {
line-height: 1;
}
.info a {
color: inherit;
text-decoration: none;
font-weight: 400;
}
.info a:hover {
text-decoration: underline;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
.toggle-all,
.todo-list li .toggle {
background: none;
}
.todo-list li .toggle {
height: 40px;
}
}
@media (max-width: 430px) {
.footer {
height: 50px;
}
.filters {
bottom: 10px;
}
}

View File

@@ -0,0 +1,25 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
keys(): string[];
<T>(id: string): T;
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -0,0 +1,15 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.d.ts"
]
}

View File

@@ -0,0 +1,29 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"module": "es2020",
"lib": [
"es2018",
"dom"
]
},
"angularCompilerOptions": {
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

View File

@@ -0,0 +1,18 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

View File

@@ -0,0 +1,152 @@
{
"extends": "tslint:recommended",
"rulesDirectory": [
"codelyzer"
],
"rules": {
"align": {
"options": [
"parameters",
"statements"
]
},
"array-type": false,
"arrow-return-shorthand": true,
"curly": true,
"deprecation": {
"severity": "warning"
},
"eofline": true,
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": {
"options": [
"spaces"
]
},
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"quotemark": [
true,
"single"
],
"semicolon": {
"options": [
"always"
]
},
"space-before-function-paren": {
"options": {
"anonymous": "never",
"asyncArrow": "always",
"constructor": "never",
"method": "never",
"named": "never"
}
},
"typedef": [
true,
"call-signature"
],
"typedef-whitespace": {
"options": [
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
},
{
"call-signature": "onespace",
"index-signature": "onespace",
"parameter": "onespace",
"property-declaration": "onespace",
"variable-declaration": "onespace"
}
]
},
"variable-name": {
"options": [
"ban-keywords",
"check-format",
"allow-pascal-case"
]
},
"whitespace": {
"options": [
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type",
"check-typecast"
]
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}

View File

@@ -0,0 +1,35 @@
import { Server as HttpServer } from "http";
import { Server, ServerOptions } from "socket.io";
import { ClientEvents, ServerEvents } from "./events";
import { TodoRepository } from "./todo-management/todo.repository";
import createTodoHandlers from "./todo-management/todo.handlers";
export interface Components {
todoRepository: TodoRepository;
}
export function createApplication(
httpServer: HttpServer,
components: Components,
serverOptions: Partial<ServerOptions> = {}
): Server<ClientEvents, ServerEvents> {
const io = new Server<ClientEvents, ServerEvents>(httpServer, serverOptions);
const {
createTodo,
readTodo,
updateTodo,
deleteTodo,
listTodo,
} = createTodoHandlers(components);
io.on("connection", (socket) => {
socket.on("todo:create", createTodo);
socket.on("todo:read", readTodo);
socket.on("todo:update", updateTodo);
socket.on("todo:delete", deleteTodo);
socket.on("todo:list", listTodo);
});
return io;
}

View File

@@ -0,0 +1,37 @@
import { Todo, TodoID } from "./todo-management/todo.repository";
import { ValidationErrorItem } from "joi";
interface Error {
error: string;
errorDetails?: ValidationErrorItem[];
}
interface Success<T> {
data: T;
}
export type Response<T> = Error | Success<T>;
export interface ServerEvents {
"todo:created": (todo: Todo) => void;
"todo:updated": (todo: Todo) => void;
"todo:deleted": (id: TodoID) => void;
}
export interface ClientEvents {
"todo:list": (callback: (res: Response<Todo[]>) => void) => void;
"todo:create": (
payload: Omit<Todo, "id">,
callback: (res: Response<TodoID>) => void
) => void;
"todo:read": (id: TodoID, callback: (res: Response<Todo>) => void) => void;
"todo:update": (
payload: Todo,
callback: (res?: Response<void>) => void
) => void;
"todo:delete": (id: TodoID, callback: (res?: Response<void>) => void) => void;
}

View File

@@ -0,0 +1,19 @@
import { createServer } from "http";
import { createApplication } from "./app";
import { InMemoryTodoRepository } from "./todo-management/todo.repository";
const httpServer = createServer();
createApplication(
httpServer,
{
todoRepository: new InMemoryTodoRepository(),
},
{
cors: {
origin: ["http://localhost:4200"],
},
}
);
httpServer.listen(3000);

View File

@@ -0,0 +1,159 @@
import { Errors, mapErrorDetails, sanitizeErrorMessage } from "../util";
import { v4 as uuid } from "uuid";
import { Components } from "../app";
import Joi = require("joi");
import { Todo, TodoID } from "./todo.repository";
import { ClientEvents, Response, ServerEvents } from "../events";
import { Socket } from "socket.io";
const idSchema = Joi.string().guid({
version: "uuidv4",
});
const todoSchema = Joi.object({
id: idSchema.alter({
create: (schema) => schema.forbidden(),
update: (schema) => schema.required(),
}),
title: Joi.string().max(256).required(),
completed: Joi.boolean().required(),
});
export default function (components: Components) {
const { todoRepository } = components;
return {
createTodo: async function (
payload: Omit<Todo, "id">,
callback: (res: Response<TodoID>) => void
) {
// @ts-ignore
const socket: Socket<ClientEvents, ServerEvents> = this;
// validate the payload
const { error, value } = todoSchema.tailor("create").validate(payload, {
abortEarly: false,
stripUnknown: true,
});
if (error) {
return callback({
error: Errors.INVALID_PAYLOAD,
errorDetails: mapErrorDetails(error.details),
});
}
value.id = uuid();
// persist the entity
try {
await todoRepository.save(value);
} catch (e) {
return callback({
error: sanitizeErrorMessage(e),
});
}
// acknowledge the creation
callback({
data: value.id,
});
// notify the other users
socket.broadcast.emit("todo:created", value);
},
readTodo: async function (
id: TodoID,
callback: (res: Response<Todo>) => void
) {
const { error } = idSchema.validate(id);
if (error) {
return callback({
error: Errors.ENTITY_NOT_FOUND,
});
}
try {
const todo = await todoRepository.findById(id);
callback({
data: todo,
});
} catch (e) {
callback({
error: sanitizeErrorMessage(e),
});
}
},
updateTodo: async function (
payload: Todo,
callback: (res?: Response<void>) => void
) {
// @ts-ignore
const socket: Socket<ClientEvents, ServerEvents> = this;
const { error, value } = todoSchema.tailor("update").validate(payload, {
abortEarly: false,
stripUnknown: true,
});
if (error) {
return callback({
error: Errors.INVALID_PAYLOAD,
errorDetails: mapErrorDetails(error.details),
});
}
try {
await todoRepository.save(value);
} catch (e) {
return callback({
error: sanitizeErrorMessage(e),
});
}
callback();
socket.broadcast.emit("todo:updated", value);
},
deleteTodo: async function (
id: TodoID,
callback: (res?: Response<void>) => void
) {
// @ts-ignore
const socket: Socket<ClientEvents, ServerEvents> = this;
const { error } = idSchema.validate(id);
if (error) {
return callback({
error: Errors.ENTITY_NOT_FOUND,
});
}
try {
await todoRepository.deleteById(id);
} catch (e) {
return callback({
error: sanitizeErrorMessage(e),
});
}
callback();
socket.broadcast.emit("todo:deleted", id);
},
listTodo: async function (callback: (res: Response<Todo[]>) => void) {
try {
callback({
data: await todoRepository.findAll(),
});
} catch (e) {
callback({
error: sanitizeErrorMessage(e),
});
}
},
};
}

View File

@@ -0,0 +1,49 @@
import { Errors } from "../util";
abstract class CrudRepository<T, ID> {
abstract findAll(): Promise<T[]>;
abstract findById(id: ID): Promise<T>;
abstract save(entity: T): Promise<void>;
abstract deleteById(id: ID): Promise<void>;
}
export type TodoID = string;
export interface Todo {
id: TodoID;
completed: boolean;
title: string;
}
export abstract class TodoRepository extends CrudRepository<Todo, TodoID> {}
export class InMemoryTodoRepository extends TodoRepository {
private readonly todos: Map<TodoID, Todo> = new Map();
findAll(): Promise<Todo[]> {
const entities = Array.from(this.todos.values());
return Promise.resolve(entities);
}
findById(id: TodoID): Promise<Todo> {
if (this.todos.has(id)) {
return Promise.resolve(this.todos.get(id)!);
} else {
return Promise.reject(Errors.ENTITY_NOT_FOUND);
}
}
save(entity: Todo): Promise<void> {
this.todos.set(entity.id, entity);
return Promise.resolve();
}
deleteById(id: TodoID): Promise<void> {
const deleted = this.todos.delete(id);
if (deleted) {
return Promise.resolve();
} else {
return Promise.reject(Errors.ENTITY_NOT_FOUND);
}
}
}

View File

@@ -0,0 +1,24 @@
import { ValidationErrorItem } from "joi";
export enum Errors {
ENTITY_NOT_FOUND = "entity not found",
INVALID_PAYLOAD = "invalid payload",
}
const errorValues: string[] = Object.values(Errors);
export function sanitizeErrorMessage(message: string) {
if (errorValues.includes(message)) {
return message;
} else {
return "an unknown error has occurred";
}
}
export function mapErrorDetails(details: ValidationErrorItem[]) {
return details.map((item) => ({
message: item.message,
path: item.path,
type: item.type,
}));
}

View File

@@ -0,0 +1,36 @@
{
"name": "basic-crud-server",
"version": "0.0.1",
"description": "Server for the Basic CRUD Socket.IO example",
"main": "dist/lib/index.js",
"scripts": {
"start": "ts-node lib/index.ts",
"build": "tsc",
"test": "nyc mocha --require ts-node/register test/**/*.ts"
},
"repository": {
"type": "git",
"url": "git+https://github.com/socketio/socket.io.git"
},
"author": "Damien Arrachequesne <damien.arrachequesne@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/socketio/socket.io/issues"
},
"homepage": "https://github.com/socketio/socket.io#readme",
"dependencies": {
"joi": "^17.4.0",
"socket.io": "^4.0.1",
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/chai": "^4.2.16",
"@types/uuid": "^8.3.0",
"chai": "^4.3.4",
"mocha": "^8.3.2",
"nyc": "^15.1.0",
"socket.io-client": "^4.0.1",
"ts-node": "^9.1.1",
"typescript": "^4.2.4"
}
}

View File

@@ -0,0 +1,309 @@
import { createApplication } from "../../lib/app";
import { createServer, Server } from "http";
import {
InMemoryTodoRepository,
TodoRepository,
} from "../../lib/todo-management/todo.repository";
import { AddressInfo } from "net";
import { io, Socket } from "socket.io-client";
import { ClientEvents, ServerEvents } from "../../lib/events";
import { expect } from "chai";
const createPartialDone = (count: number, done: () => void) => {
let i = 0;
return () => {
if (++i === count) {
done();
}
};
};
describe("todo management", () => {
let httpServer: Server,
socket: Socket<ServerEvents, ClientEvents>,
otherSocket: Socket<ServerEvents, ClientEvents>,
todoRepository: TodoRepository;
beforeEach((done) => {
const partialDone = createPartialDone(2, done);
httpServer = createServer();
todoRepository = new InMemoryTodoRepository();
createApplication(httpServer, {
todoRepository,
});
httpServer.listen(() => {
const port = (httpServer.address() as AddressInfo).port;
socket = io(`http://localhost:${port}`);
socket.on("connect", partialDone);
otherSocket = io(`http://localhost:${port}`);
otherSocket.on("connect", partialDone);
});
});
afterEach(() => {
httpServer.close();
socket.disconnect();
otherSocket.disconnect();
});
describe("create todo", () => {
it("should create a todo entity", (done) => {
const partialDone = createPartialDone(2, done);
socket.emit(
"todo:create",
{
title: "lorem ipsum",
completed: false,
},
async (res) => {
if ("error" in res) {
return done(new Error("should not happen"));
}
expect(res.data).to.be.a("string");
const storedEntity = await todoRepository.findById(res.data);
expect(storedEntity).to.eql({
id: res.data,
title: "lorem ipsum",
completed: false,
});
partialDone();
}
);
otherSocket.on("todo:created", (todo) => {
expect(todo.id).to.be.a("string");
expect(todo.title).to.eql("lorem ipsum");
expect(todo.completed).to.eql(false);
partialDone();
});
});
it("should fail with an invalid entity", (done) => {
const incompleteTodo = {
completed: "false",
description: true,
};
// @ts-ignore
socket.emit("todo:create", incompleteTodo, (res) => {
if (!("error" in res)) {
return done(new Error("should not happen"));
}
expect(res.error).to.eql("invalid payload");
expect(res.errorDetails).to.eql([
{
message: '"title" is required',
path: ["title"],
type: "any.required",
},
]);
done();
});
otherSocket.on("todo:created", () => {
done(new Error("should not happen"));
});
});
});
describe("read todo", () => {
it("should return a todo entity", (done) => {
todoRepository.save({
id: "254dbf85-f5b9-4675-b913-acab5d600884",
title: "lorem ipsum",
completed: true,
});
socket.emit(
"todo:read",
"254dbf85-f5b9-4675-b913-acab5d600884",
(res) => {
if ("error" in res) {
return done(new Error("should not happen"));
}
expect(res.data.id).to.eql("254dbf85-f5b9-4675-b913-acab5d600884");
expect(res.data.title).to.eql("lorem ipsum");
expect(res.data.completed).to.eql(true);
done();
}
);
});
it("should fail with an invalid ID", (done) => {
socket.emit("todo:read", "123", (res) => {
if ("error" in res) {
expect(res.error).to.eql("entity not found");
done();
} else {
done(new Error("should not happen"));
}
});
});
it("should fail with an unknown entity", (done) => {
socket.emit(
"todo:read",
"6edcf81e-7049-40e0-8497-9cdd52414f75",
(res) => {
if ("error" in res) {
expect(res.error).to.eql("entity not found");
done();
} else {
done(new Error("should not happen"));
}
}
);
});
});
describe("update todo", () => {
it("should update a todo entity", (done) => {
const partialDone = createPartialDone(2, done);
todoRepository.save({
id: "c7790b35-6bbb-45dd-8d67-a281ca407b43",
title: "lorem ipsum",
completed: true,
});
socket.emit(
"todo:update",
{
id: "c7790b35-6bbb-45dd-8d67-a281ca407b43",
title: "dolor sit amet",
completed: true,
},
async () => {
const storedEntity = await todoRepository.findById(
"c7790b35-6bbb-45dd-8d67-a281ca407b43"
);
expect(storedEntity).to.eql({
id: "c7790b35-6bbb-45dd-8d67-a281ca407b43",
title: "dolor sit amet",
completed: true,
});
partialDone();
}
);
otherSocket.on("todo:updated", (todo) => {
expect(todo.title).to.eql("dolor sit amet");
expect(todo.completed).to.eql(true);
partialDone();
});
});
it("should fail with an invalid entity", (done) => {
const incompleteTodo = {
id: "123",
completed: "false",
description: true,
};
// @ts-ignore
socket.emit("todo:update", incompleteTodo, (res) => {
if (!(res && "error" in res)) {
return done(new Error("should not happen"));
}
expect(res.error).to.eql("invalid payload");
expect(res.errorDetails).to.eql([
{
message: '"id" must be a valid GUID',
path: ["id"],
type: "string.guid",
},
{
message: '"title" is required',
path: ["title"],
type: "any.required",
},
]);
done();
});
otherSocket.on("todo:updated", () => {
done(new Error("should not happen"));
});
});
});
describe("delete todo", () => {
it("should delete a todo entity", (done) => {
const partialDone = createPartialDone(2, done);
const id = "58960ab2-4e78-4ced-8079-134f12179d46";
todoRepository.save({
id,
title: "lorem ipsum",
completed: true,
});
socket.emit("todo:delete", id, async () => {
try {
await todoRepository.findById(id);
} catch (e) {
partialDone();
}
});
otherSocket.on("todo:deleted", (id) => {
expect(id).to.eql("58960ab2-4e78-4ced-8079-134f12179d46");
partialDone();
});
});
it("should fail with an invalid ID", (done) => {
socket.emit("todo:delete", "123", (res) => {
if (!(res && "error" in res)) {
return done(new Error("should not happen"));
}
expect(res.error).to.eql("entity not found");
done();
});
otherSocket.on("todo:deleted", () => {
done(new Error("should not happen"));
});
});
});
describe("list todo", () => {
it("should return a list of entities", (done) => {
todoRepository.save({
id: "d445db6d-9d55-4ff2-88ae-bd1f81c299d2",
title: "lorem ipsum",
completed: false,
});
todoRepository.save({
id: "5f56fb59-a887-4984-93bf-eb39b4170a35",
title: "dolor sit amet",
completed: true,
});
socket.emit("todo:list", (res) => {
if ("error" in res) {
return done(new Error("should not happen"));
}
expect(res.data).to.eql([
{
id: "d445db6d-9d55-4ff2-88ae-bd1f81c299d2",
title: "lorem ipsum",
completed: false,
},
{
id: "5f56fb59-a887-4984-93bf-eb39b4170a35",
title: "dolor sit amet",
completed: true,
},
]);
done();
});
});
});
});

View File

@@ -0,0 +1,11 @@
{
"compilerOptions": {
"outDir": "./dist",
"module": "commonjs",
"target": "es2017",
"strict": true
},
"include": [
"./lib/**/*"
]
}

View File

@@ -1,499 +0,0 @@
{
"name": "socket.io-chat",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
}
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"engine.io": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.0.1.tgz",
"integrity": "sha512-6EaSBxasBUwxRdf6B68SEYpD3tcrG80J4YTzHl/D+9Q+vM0AMHZabfYcc2WdnvEaQxZjX/UZsa+UdGoM0qQQkQ==",
"requires": {
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.4.1",
"cors": "~2.8.5",
"debug": "~4.1.0",
"engine.io-parser": "~4.0.0",
"ws": "^7.1.2"
},
"dependencies": {
"cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"engine.io-parser": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.1.tgz",
"integrity": "sha512-v5aZK1hlckcJDGmHz3W8xvI3NUHYc9t8QtTbqdR5OaH3S9iJZilPubauOm+vLWOMMWzpE3hiq92l9lTAHamRCg=="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.43.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
},
"mime-types": {
"version": "2.1.26",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
"requires": {
"mime-db": "1.43.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"socket.io": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.0.0.tgz",
"integrity": "sha512-arLQtd+UoJ08NXBRBGUJDyQ9B+cc9WwD67hc5s1WQcs2DyAkYzI5HWg4U0CrFtK00kjyAWxBGhLwVbfOeMqz1A==",
"requires": {
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.1.0",
"engine.io": "~4.0.0",
"socket.io-adapter": "~2.0.3",
"socket.io-parser": "~4.0.1"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"socket.io-adapter": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.0.3.tgz",
"integrity": "sha512-2wo4EXgxOGSFueqvHAdnmi5JLZzWqMArjuP4nqC26AtLh5PoCPsaRbRdah2xhcwTAMooZfjYiNVNkkmmSMaxOQ=="
},
"socket.io-parser": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.1.tgz",
"integrity": "sha512-5JfNykYptCwU2lkOI0ieoePWm+6stEhkZ2UnLDjqnE1YEjUlXXLd1lpxPZ+g+h3rtaytwWkWrLQCaJULlGqjOg==",
"requires": {
"component-emitter": "~1.3.0",
"debug": "~4.1.0"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"ws": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz",
"integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ=="
}
}
}

View File

@@ -8,7 +8,7 @@
"license": "BSD",
"dependencies": {
"express": "~4.17.1",
"socket.io": "^3.0.0"
"socket.io": "^4.0.0"
},
"scripts": {
"start": "node index.js"

View File

@@ -72,7 +72,7 @@ $(function() {
}
// Adds the visual chat message to the message list
const addChatMessage = (data, options) => {
const addChatMessage = (data, options = {}) => {
// Don't fade the message in if there is an 'X was typing'
const $typingMessages = getTypingMessages(data);
if ($typingMessages.length !== 0) {

View File

@@ -8,7 +8,7 @@
"license": "BSD",
"dependencies": {
"express": "4.13.4",
"socket.io": "^3.1.0",
"socket.io": "^4.0.0",
"socket.io-redis": "^6.0.1"
},
"scripts": {

View File

@@ -8,7 +8,7 @@
"license": "BSD",
"dependencies": {
"express": "4.13.4",
"socket.io": "^3.1.0",
"socket.io": "^4.0.0",
"socket.io-redis": "^6.0.1"
},
"scripts": {

View File

@@ -7,7 +7,7 @@
"private": true,
"license": "MIT",
"dependencies": {
"socket.io-client": "^3.1.0"
"socket.io-client": "^4.0.0"
},
"scripts": {
"start": "node index.js"

View File

@@ -8,7 +8,7 @@
"license": "MIT",
"dependencies": {
"express": "4.13.4",
"socket.io": "^3.1.0",
"socket.io": "^4.0.0",
"socket.io-redis": "^6.0.1"
},
"scripts": {

View File

@@ -8,7 +8,7 @@
"license": "MIT",
"dependencies": {
"express": "4.13.4",
"socket.io": "^3.1.0",
"socket.io": "^4.0.0",
"socket.io-redis": "^6.0.1"
},
"scripts": {

View File

@@ -9,8 +9,8 @@
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1",
"socket.io": "^2.3.0",
"socket.io-client": "^2.3.0"
"socket.io": "4",
"socket.io-client": "4"
},
"scripts": {
"start": "react-scripts start",

View File

@@ -1,4 +1,8 @@
const io = require('socket.io')();
const io = require('socket.io')({
cors: {
origin: ['http://localhost:3000']
}
});
io.on('connection', socket => {
console.log(`connect: ${socket.id}`);

File diff suppressed because it is too large Load Diff

View File

@@ -12,10 +12,10 @@
"component-emitter": "^1.2.1",
"express": "^4.15.2",
"schemapack": "^1.4.2",
"socket.io": "socketio/socket.io",
"socket.io-client": "socketio/socket.io-client",
"socket.io-json-parser": "^1.0.0",
"socket.io-msgpack-parser": "^1.0.0",
"socket.io": "^4.0.0",
"socket.io-client": "^4.0.0",
"socket.io-json-parser": "^3.0.0",
"socket.io-msgpack-parser": "^3.0.1",
"webpack": "^2.4.1"
}
}

View File

@@ -0,0 +1 @@
*.bundle.js

View File

@@ -40,12 +40,12 @@ const errorPacket = {
};
class Encoder {
encode (packet, callback) {
encode (packet) {
switch (packet.type) {
case TYPES.EVENT:
return callback([ this.pack(packet) ]);
return [ this.pack(packet) ];
default:
return callback([ JSON.stringify(packet) ]);
return [ JSON.stringify(packet) ];
}
}
pack (packet) {

View File

@@ -14,15 +14,22 @@ const msgpackParser = require('socket.io-msgpack-parser');
const jsonParser = require('socket.io-json-parser');
const customParser = require('./custom-parser');
let server1 = io(3001, {});
const cors = {
origin: ["http://localhost:3000"]
}
let server1 = io(3001, { cors });
let server2 = io(3002, {
parser: msgpackParser
parser: msgpackParser,
cors
});
let server3 = io(3003, {
parser: jsonParser
parser: jsonParser,
cors
});
let server4 = io(3004, {
parser: customParser
parser: customParser,
cors
});
let string = [];

View File

@@ -1,217 +0,0 @@
{
"name": "es-modules",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/component-emitter": {
"version": "1.2.10",
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz",
"integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg=="
},
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"base64-arraybuffer": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
"integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
},
"base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
},
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
},
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
},
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"engine.io": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.0.1.tgz",
"integrity": "sha512-6EaSBxasBUwxRdf6B68SEYpD3tcrG80J4YTzHl/D+9Q+vM0AMHZabfYcc2WdnvEaQxZjX/UZsa+UdGoM0qQQkQ==",
"requires": {
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.4.1",
"cors": "~2.8.5",
"debug": "~4.1.0",
"engine.io-parser": "~4.0.0",
"ws": "^7.1.2"
}
},
"engine.io-client": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.1.tgz",
"integrity": "sha512-3XXfWrEutlf1vg5PlS805bD+AgZXhRIKYAG04f1iCGOs70dWEYlZGfCZUNwPwNx05lBCKs1lIeL3SkLB0P++xw==",
"requires": {
"base64-arraybuffer": "0.1.4",
"component-emitter": "~1.3.0",
"debug": "~4.1.0",
"engine.io-parser": "~4.0.1",
"has-cors": "1.1.0",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~7.2.1",
"xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2"
},
"dependencies": {
"ws": {
"version": "7.2.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz",
"integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA=="
}
}
},
"engine.io-parser": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.1.tgz",
"integrity": "sha512-v5aZK1hlckcJDGmHz3W8xvI3NUHYc9t8QtTbqdR5OaH3S9iJZilPubauOm+vLWOMMWzpE3hiq92l9lTAHamRCg=="
},
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"requires": {
"mime-db": "1.44.0"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"socket.io": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.0.0.tgz",
"integrity": "sha512-arLQtd+UoJ08NXBRBGUJDyQ9B+cc9WwD67hc5s1WQcs2DyAkYzI5HWg4U0CrFtK00kjyAWxBGhLwVbfOeMqz1A==",
"requires": {
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.1.0",
"engine.io": "~4.0.0",
"socket.io-adapter": "~2.0.3",
"socket.io-parser": "~4.0.1"
}
},
"socket.io-adapter": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.0.3.tgz",
"integrity": "sha512-2wo4EXgxOGSFueqvHAdnmi5JLZzWqMArjuP4nqC26AtLh5PoCPsaRbRdah2xhcwTAMooZfjYiNVNkkmmSMaxOQ=="
},
"socket.io-client": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-3.0.0.tgz",
"integrity": "sha512-NreA86JJSMMSyPomkpyWL+FeKB3wmsWbGbNvtoxPbodV7dEIWxwyOklYfhuTpJJDCpEFd55vET2/ZyYwJ66Bfg==",
"requires": {
"@types/component-emitter": "^1.2.10",
"backo2": "1.0.2",
"component-bind": "1.0.0",
"component-emitter": "~1.3.0",
"debug": "~4.1.0",
"engine.io-client": "~4.0.0",
"parseuri": "0.0.6",
"socket.io-parser": "~4.0.1"
}
},
"socket.io-parser": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.1.tgz",
"integrity": "sha512-5JfNykYptCwU2lkOI0ieoePWm+6stEhkZ2UnLDjqnE1YEjUlXXLd1lpxPZ+g+h3rtaytwWkWrLQCaJULlGqjOg==",
"requires": {
"component-emitter": "~1.3.0",
"debug": "~4.1.0"
}
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"ws": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz",
"integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ=="
},
"xmlhttprequest-ssl": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
},
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
}
}
}

View File

@@ -9,7 +9,7 @@
"node": ">=12.17.0"
},
"dependencies": {
"socket.io": "^3.0.0",
"socket.io-client": "^3.0.0"
"socket.io": "^4.0.0",
"socket.io-client": "^4.0.0"
}
}

View File

@@ -51,9 +51,9 @@ app.post(
app.post("/logout", (req, res) => {
console.log(`logout ${req.session.id}`);
const socketId = req.session.socketId;
if (socketId && io.of("/").connected[socketId]) {
if (socketId && io.of("/").sockets.get(socketId)) {
console.log(`forcefully closing socket ${socketId}`);
io.sockets.connected[socketId].disconnect(true);
io.of("/").sockets.get(socketId).disconnect(true);
}
req.logout();
res.cookie("connect.sid", "", { expires: new Date() });

View File

@@ -1,783 +0,0 @@
{
"name": "passport-example",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"after": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"arraybuffer.slice": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
},
"async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
},
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"base64-arraybuffer": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
},
"base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
},
"better-assert": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
"integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
"requires": {
"callsite": "1.0.0"
}
},
"blob": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
}
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"callsite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
},
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
},
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
},
"component-inherit": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"engine.io": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.1.tgz",
"integrity": "sha512-8MfIfF1/IIfxuc2gv5K+XlFZczw/BpTvqBdl0E2fBLkYQp4miv4LuDTVtYt4yMyaIFLEr4vtaSgV4mjvll8Crw==",
"requires": {
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "0.3.1",
"debug": "~4.1.0",
"engine.io-parser": "~2.2.0",
"ws": "^7.1.2"
},
"dependencies": {
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"engine.io-client": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.2.tgz",
"integrity": "sha512-AWjc1Xg06a6UPFOBAzJf48W1UR/qKYmv/ubgSCumo9GXgvL/xGIvo05dXoBL+2NTLMipDI7in8xK61C17L25xg==",
"requires": {
"component-emitter": "~1.3.0",
"component-inherit": "0.0.3",
"debug": "~4.1.0",
"engine.io-parser": "~2.2.0",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"ws": "~6.1.0",
"xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2"
},
"dependencies": {
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"ws": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
"integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
"requires": {
"async-limiter": "~1.0.0"
}
}
}
},
"engine.io-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
"integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
"requires": {
"after": "0.8.2",
"arraybuffer.slice": "~0.0.7",
"base64-arraybuffer": "0.1.5",
"blob": "0.0.5",
"has-binary2": "~1.0.2"
}
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"express-session": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz",
"integrity": "sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q==",
"requires": {
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-headers": "~1.0.2",
"parseurl": "~1.3.3",
"safe-buffer": "5.2.0",
"uid-safe": "~2.1.5"
},
"dependencies": {
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
},
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
}
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"has-binary2": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
"requires": {
"isarray": "2.0.1"
}
},
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"indexof": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"requires": {
"mime-db": "1.44.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"object-component": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
"integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
},
"parseqs": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
"integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
"requires": {
"better-assert": "~1.0.0"
}
},
"parseuri": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
"integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
"requires": {
"better-assert": "~1.0.0"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"passport": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz",
"integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==",
"requires": {
"passport-strategy": "1.x.x",
"pause": "0.0.1"
}
},
"passport-local": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
"integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=",
"requires": {
"passport-strategy": "1.x.x"
}
},
"passport-strategy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
"integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"pause": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"socket.io": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
"integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
"requires": {
"debug": "~4.1.0",
"engine.io": "~3.4.0",
"has-binary2": "~1.0.2",
"socket.io-adapter": "~1.1.0",
"socket.io-client": "2.3.0",
"socket.io-parser": "~3.4.0"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"socket.io-adapter": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
"integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
},
"socket.io-client": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
"integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
"requires": {
"backo2": "1.0.2",
"base64-arraybuffer": "0.1.5",
"component-bind": "1.0.0",
"component-emitter": "1.2.1",
"debug": "~4.1.0",
"engine.io-client": "~3.4.0",
"has-binary2": "~1.0.2",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"object-component": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"socket.io-parser": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
"integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
"requires": {
"component-emitter": "1.2.1",
"debug": "~3.1.0",
"isarray": "2.0.1"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
}
}
},
"socket.io-parser": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz",
"integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==",
"requires": {
"component-emitter": "1.2.1",
"debug": "~4.1.0",
"isarray": "2.0.1"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"to-array": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
"integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"requires": {
"random-bytes": "~1.0.0"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"ws": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz",
"integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w=="
},
"xmlhttprequest-ssl": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
},
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
}
}
}

View File

@@ -8,7 +8,7 @@
"express-session": "~1.17.1",
"passport": "~0.4.1",
"passport-local": "~1.0.0",
"socket.io": "~2.3.0"
"socket.io": "^4.0.0"
},
"scripts": {
"start": "node index.js"

View File

@@ -9,7 +9,7 @@
},
"dependencies": {
"core-js": "^3.6.5",
"socket.io-client": "^3.1.1",
"socket.io-client": "^4.0.0",
"vue": "^2.6.11"
},
"devDependencies": {

View File

@@ -11,7 +11,7 @@
"dependencies": {
"@socket.io/sticky": "^1.0.0",
"ioredis": "^4.22.0",
"socket.io": "^3.1.1",
"socket.io": "^4.0.0",
"socket.io-redis": "^6.0.1"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -13,8 +13,8 @@
"react-dom": "~16.9.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz",
"react-native-web": "~0.11.7",
"socket.io": "^3.0.1",
"socket.io-client": "^3.0.1"
"socket.io": "^4.0.0",
"socket.io-client": "^4.0.0"
},
"devDependencies": {
"babel-preset-expo": "~8.1.0",

View File

@@ -7,7 +7,11 @@ const twitter = new Twitter({
token_secret: process.env.TWITTER_TOKEN_SECRET
});
const io = require('socket.io')(process.env.PORT || 3000);
const io = require('socket.io')(process.env.PORT || 3000, {
cors: {
origin: true
}
});
twitter.track('socket.io');
twitter.track('javascript');

View File

@@ -1,705 +0,0 @@
{
"name": "socket.io-tweet-stream",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"after": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
},
"ajv": {
"version": "6.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
"integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"arraybuffer.slice": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
},
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
"requires": {
"safer-buffer": "~2.1.0"
}
},
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
"async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
},
"aws4": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
},
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"base64-arraybuffer": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
},
"base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
},
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
"requires": {
"tweetnacl": "^0.14.3"
}
},
"better-assert": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
"integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
"requires": {
"callsite": "1.0.0"
}
},
"blob": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
},
"callsite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
},
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
},
"component-inherit": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
},
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"duplexer": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
"integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E="
},
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"engine.io": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.1.tgz",
"integrity": "sha512-8MfIfF1/IIfxuc2gv5K+XlFZczw/BpTvqBdl0E2fBLkYQp4miv4LuDTVtYt4yMyaIFLEr4vtaSgV4mjvll8Crw==",
"requires": {
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "0.3.1",
"debug": "~4.1.0",
"engine.io-parser": "~2.2.0",
"ws": "^7.1.2"
}
},
"engine.io-client": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.1.tgz",
"integrity": "sha512-RJNmA+A9Js+8Aoq815xpGAsgWH1VoSYM//2VgIiu9lNOaHFfLpTjH4tOzktBpjIs5lvOfiNY1dwf+NuU6D38Mw==",
"requires": {
"component-emitter": "1.2.1",
"component-inherit": "0.0.3",
"debug": "~4.1.0",
"engine.io-parser": "~2.2.0",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"ws": "~6.1.0",
"xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2"
},
"dependencies": {
"ws": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
"integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
"requires": {
"async-limiter": "~1.0.0"
}
}
}
},
"engine.io-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
"integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
"requires": {
"after": "0.8.2",
"arraybuffer.slice": "~0.0.7",
"base64-arraybuffer": "0.1.5",
"blob": "0.0.5",
"has-binary2": "~1.0.2"
}
},
"event-stream": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.1.7.tgz",
"integrity": "sha1-tMVAAS0P4UmEIPPYlGAI22OTw3o=",
"requires": {
"duplexer": "~0.1.1",
"from": "~0",
"map-stream": "~0.1.0",
"pause-stream": "0.0.11",
"split": "0.2",
"stream-combiner": "~0.0.4",
"through": "~2.3.1"
},
"dependencies": {
"split": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz",
"integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=",
"requires": {
"through": "2"
}
}
}
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
},
"fast-deep-equal": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
"integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
},
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"from": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
"integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4="
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
},
"har-validator": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
"requires": {
"ajv": "^6.5.5",
"har-schema": "^2.0.0"
}
},
"has-binary2": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
"requires": {
"isarray": "2.0.1"
}
},
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"requires": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
},
"indexof": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"verror": "1.10.0"
}
},
"map-stream": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
"integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ="
},
"mime-db": {
"version": "1.43.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
},
"mime-types": {
"version": "2.1.26",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
"requires": {
"mime-db": "1.43.0"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"node-tweet-stream": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/node-tweet-stream/-/node-tweet-stream-2.0.3.tgz",
"integrity": "sha1-YOQkqqEDSGvaAtpZiL2NU0Z9sgE=",
"requires": {
"event-stream": "~3.1.0",
"request": "^2.55.0",
"split": "^0.3.0"
}
},
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
},
"object-component": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
"integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
},
"parseqs": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
"integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
"requires": {
"better-assert": "~1.0.0"
}
},
"parseuri": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
"integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
"requires": {
"better-assert": "~1.0.0"
}
},
"pause-stream": {
"version": "0.0.11",
"resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
"integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
"requires": {
"through": "~2.3"
}
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
"psl": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.3",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
}
},
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"socket.io": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
"integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
"requires": {
"debug": "~4.1.0",
"engine.io": "~3.4.0",
"has-binary2": "~1.0.2",
"socket.io-adapter": "~1.1.0",
"socket.io-client": "2.3.0",
"socket.io-parser": "~3.4.0"
}
},
"socket.io-adapter": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
"integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
},
"socket.io-client": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
"integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
"requires": {
"backo2": "1.0.2",
"base64-arraybuffer": "0.1.5",
"component-bind": "1.0.0",
"component-emitter": "1.2.1",
"debug": "~4.1.0",
"engine.io-client": "~3.4.0",
"has-binary2": "~1.0.2",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"object-component": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
},
"dependencies": {
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"socket.io-parser": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
"integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
"requires": {
"component-emitter": "1.2.1",
"debug": "~3.1.0",
"isarray": "2.0.1"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
}
}
}
}
},
"socket.io-parser": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.0.tgz",
"integrity": "sha512-/G/VOI+3DBp0+DJKW4KesGnQkQPFmUCbA/oO2QGT6CWxU7hLGWqU3tyuzeSK/dqcyeHsQg1vTe9jiZI8GU9SCQ==",
"requires": {
"component-emitter": "1.2.1",
"debug": "~4.1.0",
"isarray": "2.0.1"
}
},
"split": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
"integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=",
"requires": {
"through": "2"
}
},
"sshpk": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
"integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
}
},
"stream-combiner": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
"integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=",
"requires": {
"duplexer": "~0.1.1"
}
},
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"to-array": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
"integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
},
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"requires": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
},
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
"requires": {
"punycode": "^2.1.0"
}
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
},
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
"requires": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
},
"ws": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz",
"integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ=="
},
"xmlhttprequest-ssl": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
},
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
}
}
}

View File

@@ -15,6 +15,6 @@
"license": "MIT",
"dependencies": {
"node-tweet-stream": "^2.0.1",
"socket.io": "~2.3.0"
"socket.io": "^4.0.0"
}
}

View File

@@ -1,295 +0,0 @@
{
"name": "typescript-example",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/component-emitter": {
"version": "1.2.10",
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz",
"integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg=="
},
"@types/cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg=="
},
"@types/cors": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.9.tgz",
"integrity": "sha512-zurD1ibz21BRlAOIKP8yhrxlqKx6L9VCwkB5kMiP6nZAhoF5MvC7qS1qPA7nRcr1GJolfkQC7/EAL4hdYejLtg=="
},
"@types/node": {
"version": "14.14.16",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.16.tgz",
"integrity": "sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw=="
},
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
},
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"base64-arraybuffer": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
"integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
},
"base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
},
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
},
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
},
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
},
"engine.io": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.0.5.tgz",
"integrity": "sha512-Ri+whTNr2PKklxQkfbGjwEo+kCBUM4Qxk4wtLqLrhH+b1up2NFL9g9pjYWiCV/oazwB0rArnvF/ZmZN2ab5Hpg==",
"requires": {
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.4.1",
"cors": "~2.8.5",
"debug": "~4.1.0",
"engine.io-parser": "~4.0.0",
"ws": "^7.1.2"
}
},
"engine.io-client": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.5.tgz",
"integrity": "sha512-1lkn0QdekHQPMTcxUh8LqIuxQHNtKV5GvqkQzmZ1rYKAvB6puMm13U7K1ps3OQZ4joE46asQiAKrcdL9weNEVw==",
"requires": {
"base64-arraybuffer": "0.1.4",
"component-emitter": "~1.3.0",
"debug": "~4.1.0",
"engine.io-parser": "~4.0.1",
"has-cors": "1.1.0",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~7.2.1",
"xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2"
},
"dependencies": {
"ws": {
"version": "7.2.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz",
"integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA=="
}
}
},
"engine.io-parser": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz",
"integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==",
"requires": {
"base64-arraybuffer": "0.1.4"
}
},
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
},
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"requires": {
"mime-db": "1.44.0"
}
},
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"socket.io": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.0.4.tgz",
"integrity": "sha512-Vj1jUoO75WGc9txWd311ZJJqS9Dr8QtNJJ7gk2r7dcM/yGe9sit7qOijQl3GAwhpBOz/W8CwkD7R6yob07nLbA==",
"requires": {
"@types/cookie": "^0.4.0",
"@types/cors": "^2.8.8",
"@types/node": "^14.14.7",
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.1.0",
"engine.io": "~4.0.0",
"socket.io-adapter": "~2.0.3",
"socket.io-parser": "~4.0.1"
}
},
"socket.io-adapter": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.0.3.tgz",
"integrity": "sha512-2wo4EXgxOGSFueqvHAdnmi5JLZzWqMArjuP4nqC26AtLh5PoCPsaRbRdah2xhcwTAMooZfjYiNVNkkmmSMaxOQ=="
},
"socket.io-client": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-3.0.4.tgz",
"integrity": "sha512-qMvBuS+W9JIN2mkfAWDCxuIt+jpIKDf8C0604zEqx1JrPaPSS6cN0F3B2GYWC83TqBeVJXW66GFxWV3KD88n0Q==",
"requires": {
"@types/component-emitter": "^1.2.10",
"backo2": "1.0.2",
"component-bind": "1.0.0",
"component-emitter": "~1.3.0",
"debug": "~4.1.0",
"engine.io-client": "~4.0.0",
"parseuri": "0.0.6",
"socket.io-parser": "~4.0.1"
}
},
"socket.io-parser": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.2.tgz",
"integrity": "sha512-Bs3IYHDivwf+bAAuW/8xwJgIiBNtlvnjYRc4PbXgniLmcP1BrakBoq/QhO24rgtgW7VZ7uAaswRGxutUnlAK7g==",
"requires": {
"@types/component-emitter": "^1.2.10",
"component-emitter": "~1.3.0",
"debug": "~4.1.0"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"ts-node": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz",
"integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==",
"requires": {
"arg": "^4.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"source-map-support": "^0.5.17",
"yn": "3.1.1"
}
},
"typescript": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
"integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ=="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"ws": {
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz",
"integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA=="
},
"xmlhttprequest-ssl": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
},
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
}
}
}

View File

@@ -11,8 +11,8 @@
"author": "Damien Arrachequesne",
"license": "MIT",
"dependencies": {
"socket.io": "^3.0.4",
"socket.io-client": "^3.0.4",
"socket.io": "^4.0.0",
"socket.io-client": "^4.0.0",
"ts-node": "^9.0.0",
"typescript": "^4.0.5"
}

View File

@@ -1,8 +1,8 @@
import { Server, Socket } from "socket.io";
import { Server } from "socket.io";
const io = new Server(8080);
io.on("connect", (socket: Socket) => {
io.on("connect", (socket) => {
console.log(`connect ${socket.id}`);
socket.on("ping", (cb) => {

View File

@@ -1,8 +1,7 @@
const server = require('http').createServer();
const io = require('socket.io')(server, {
serveClient: false,
wsEngine: 'ws' // uws is not supported since it is a native module
serveClient: false
});
const port = process.env.PORT || 3000;

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@
"author": "Damien Arrachequesne",
"license": "MIT",
"devDependencies": {
"socket.io": "~2.3.0",
"socket.io": "^4.0.0",
"webpack": "~4.43.0",
"webpack-cli": "~3.3.11"
}

View File

@@ -5,5 +5,6 @@ module.exports = {
output: {
path: require('path').join(__dirname, '../dist'),
filename: 'server.js'
}
},
mode: 'production'
};

View File

@@ -1,499 +0,0 @@
{
"name": "whiteboard",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
}
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"engine.io": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.0.1.tgz",
"integrity": "sha512-6EaSBxasBUwxRdf6B68SEYpD3tcrG80J4YTzHl/D+9Q+vM0AMHZabfYcc2WdnvEaQxZjX/UZsa+UdGoM0qQQkQ==",
"requires": {
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.4.1",
"cors": "~2.8.5",
"debug": "~4.1.0",
"engine.io-parser": "~4.0.0",
"ws": "^7.1.2"
},
"dependencies": {
"cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"engine.io-parser": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.1.tgz",
"integrity": "sha512-v5aZK1hlckcJDGmHz3W8xvI3NUHYc9t8QtTbqdR5OaH3S9iJZilPubauOm+vLWOMMWzpE3hiq92l9lTAHamRCg=="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.43.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
},
"mime-types": {
"version": "2.1.26",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
"requires": {
"mime-db": "1.43.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"socket.io": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.0.0.tgz",
"integrity": "sha512-arLQtd+UoJ08NXBRBGUJDyQ9B+cc9WwD67hc5s1WQcs2DyAkYzI5HWg4U0CrFtK00kjyAWxBGhLwVbfOeMqz1A==",
"requires": {
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.1.0",
"engine.io": "~4.0.0",
"socket.io-adapter": "~2.0.3",
"socket.io-parser": "~4.0.1"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"socket.io-adapter": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.0.3.tgz",
"integrity": "sha512-2wo4EXgxOGSFueqvHAdnmi5JLZzWqMArjuP4nqC26AtLh5PoCPsaRbRdah2xhcwTAMooZfjYiNVNkkmmSMaxOQ=="
},
"socket.io-parser": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.1.tgz",
"integrity": "sha512-5JfNykYptCwU2lkOI0ieoePWm+6stEhkZ2UnLDjqnE1YEjUlXXLd1lpxPZ+g+h3rtaytwWkWrLQCaJULlGqjOg==",
"requires": {
"component-emitter": "~1.3.0",
"debug": "~4.1.0"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"ws": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz",
"integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ=="
}
}
}

View File

@@ -9,7 +9,7 @@
],
"dependencies": {
"express": "~4.17.1",
"socket.io": "^3.0.0"
"socket.io": "^4.0.0"
},
"scripts": {
"start": "node index"

315
lib/broadcast-operator.ts Normal file
View File

@@ -0,0 +1,315 @@
import type { BroadcastFlags, Room, SocketId } from "socket.io-adapter";
import { Handshake, RESERVED_EVENTS, Socket } from "./socket";
import { PacketType } from "socket.io-parser";
import type { Adapter } from "socket.io-adapter";
import type {
EventParams,
EventNames,
EventsMap,
TypedEventBroadcaster,
} from "./typed-events";
export class BroadcastOperator<EmitEvents extends EventsMap>
implements TypedEventBroadcaster<EmitEvents> {
constructor(
private readonly adapter: Adapter,
private readonly rooms: Set<Room> = new Set<Room>(),
private readonly exceptRooms: Set<Room> = new Set<Room>(),
private readonly flags: BroadcastFlags = {}
) {}
/**
* Targets a room when emitting.
*
* @param room
* @return a new BroadcastOperator instance
* @public
*/
public to(room: Room | Room[]): BroadcastOperator<EmitEvents> {
const rooms = new Set(this.rooms);
if (Array.isArray(room)) {
room.forEach((r) => rooms.add(r));
} else {
rooms.add(room);
}
return new BroadcastOperator(
this.adapter,
rooms,
this.exceptRooms,
this.flags
);
}
/**
* Targets a room when emitting.
*
* @param room
* @return a new BroadcastOperator instance
* @public
*/
public in(room: Room | Room[]): BroadcastOperator<EmitEvents> {
return this.to(room);
}
/**
* Excludes a room when emitting.
*
* @param room
* @return a new BroadcastOperator instance
* @public
*/
public except(room: Room | Room[]): BroadcastOperator<EmitEvents> {
const exceptRooms = new Set(this.exceptRooms);
if (Array.isArray(room)) {
room.forEach((r) => exceptRooms.add(r));
} else {
exceptRooms.add(room);
}
return new BroadcastOperator(
this.adapter,
this.rooms,
exceptRooms,
this.flags
);
}
/**
* Sets the compress flag.
*
* @param compress - if `true`, compresses the sending data
* @return a new BroadcastOperator instance
* @public
*/
public compress(compress: boolean): BroadcastOperator<EmitEvents> {
const flags = Object.assign({}, this.flags, { compress });
return new BroadcastOperator(
this.adapter,
this.rooms,
this.exceptRooms,
flags
);
}
/**
* Sets a modifier for a subsequent event emission that the event data may be lost if the client is not ready to
* receive messages (because of network slowness or other issues, or because theyre connected through long polling
* and is in the middle of a request-response cycle).
*
* @return a new BroadcastOperator instance
* @public
*/
public get volatile(): BroadcastOperator<EmitEvents> {
const flags = Object.assign({}, this.flags, { volatile: true });
return new BroadcastOperator(
this.adapter,
this.rooms,
this.exceptRooms,
flags
);
}
/**
* Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node.
*
* @return a new BroadcastOperator instance
* @public
*/
public get local(): BroadcastOperator<EmitEvents> {
const flags = Object.assign({}, this.flags, { local: true });
return new BroadcastOperator(
this.adapter,
this.rooms,
this.exceptRooms,
flags
);
}
/**
* Emits to all clients.
*
* @return Always true
* @public
*/
public emit<Ev extends EventNames<EmitEvents>>(
ev: Ev,
...args: EventParams<EmitEvents, Ev>
): boolean {
if (RESERVED_EVENTS.has(ev)) {
throw new Error(`"${ev}" is a reserved event name`);
}
// set up packet object
const data = [ev, ...args];
const packet = {
type: PacketType.EVENT,
data: data,
};
if ("function" == typeof data[data.length - 1]) {
throw new Error("Callbacks are not supported when broadcasting");
}
this.adapter.broadcast(packet, {
rooms: this.rooms,
except: this.exceptRooms,
flags: this.flags,
});
return true;
}
/**
* Gets a list of clients.
*
* @public
*/
public allSockets(): Promise<Set<SocketId>> {
if (!this.adapter) {
throw new Error(
"No adapter for this namespace, are you trying to get the list of clients of a dynamic namespace?"
);
}
return this.adapter.sockets(this.rooms);
}
/**
* Returns the matching socket instances
*
* @public
*/
public fetchSockets(): Promise<RemoteSocket<EmitEvents>[]> {
return this.adapter
.fetchSockets({
rooms: this.rooms,
except: this.exceptRooms,
})
.then((sockets) => {
return sockets.map((socket) => {
if (socket instanceof Socket) {
// FIXME the TypeScript compiler complains about missing private properties
return (socket as unknown) as RemoteSocket<EmitEvents>;
} else {
return new RemoteSocket(this.adapter, socket as SocketDetails);
}
});
});
}
/**
* Makes the matching socket instances join the specified rooms
*
* @param room
* @public
*/
public socketsJoin(room: Room | Room[]): void {
this.adapter.addSockets(
{
rooms: this.rooms,
except: this.exceptRooms,
},
Array.isArray(room) ? room : [room]
);
}
/**
* Makes the matching socket instances leave the specified rooms
*
* @param room
* @public
*/
public socketsLeave(room: Room | Room[]): void {
this.adapter.delSockets(
{
rooms: this.rooms,
except: this.exceptRooms,
},
Array.isArray(room) ? room : [room]
);
}
/**
* Makes the matching socket instances disconnect
*
* @param close - whether to close the underlying connection
* @public
*/
public disconnectSockets(close: boolean = false): void {
this.adapter.disconnectSockets(
{
rooms: this.rooms,
except: this.exceptRooms,
},
close
);
}
}
/**
* Format of the data when the Socket instance exists on another Socket.IO server
*/
interface SocketDetails {
id: SocketId;
handshake: Handshake;
rooms: Room[];
data: any;
}
/**
* Expose of subset of the attributes and methods of the Socket class
*/
export class RemoteSocket<EmitEvents extends EventsMap>
implements TypedEventBroadcaster<EmitEvents> {
public readonly id: SocketId;
public readonly handshake: Handshake;
public readonly rooms: Set<Room>;
public readonly data: any;
private readonly operator: BroadcastOperator<EmitEvents>;
constructor(adapter: Adapter, details: SocketDetails) {
this.id = details.id;
this.handshake = details.handshake;
this.rooms = new Set(details.rooms);
this.data = details.data;
this.operator = new BroadcastOperator(adapter, new Set([this.id]));
}
public emit<Ev extends EventNames<EmitEvents>>(
ev: Ev,
...args: EventParams<EmitEvents, Ev>
): boolean {
return this.operator.emit(ev, ...args);
}
/**
* Joins a room.
*
* @param {String|Array} room - room or array of rooms
* @public
*/
public join(room: Room | Room[]): void {
return this.operator.socketsJoin(room);
}
/**
* Leaves a room.
*
* @param {String} room
* @public
*/
public leave(room: Room): void {
return this.operator.socketsLeave(room);
}
/**
* Disconnects this client.
*
* @param {Boolean} close - if `true`, closes the underlying connection
* @return {Socket} self
*
* @public
*/
public disconnect(close = false): this {
this.operator.disconnectSockets(close);
return this;
}
}

View File

@@ -2,21 +2,26 @@ import { Decoder, Encoder, Packet, PacketType } from "socket.io-parser";
import debugModule = require("debug");
import url = require("url");
import type { IncomingMessage } from "http";
import type { Namespace, Server } from "./index";
import type { Server } from "./index";
import type { Namespace } from "./namespace";
import type { EventsMap } from "./typed-events";
import type { Socket } from "./socket";
import type { SocketId } from "socket.io-adapter";
const debug = debugModule("socket.io:client");
export class Client {
export class Client<
ListenEvents extends EventsMap,
EmitEvents extends EventsMap
> {
public readonly conn;
private readonly id: string;
private readonly server: Server;
private readonly server: Server<ListenEvents, EmitEvents>;
private readonly encoder: Encoder;
private readonly decoder: Decoder;
private sockets: Map<SocketId, Socket> = new Map();
private nsps: Map<string, Socket> = new Map();
private sockets: Map<SocketId, Socket<ListenEvents, EmitEvents>> = new Map();
private nsps: Map<string, Socket<ListenEvents, EmitEvents>> = new Map();
private connectTimeout?: NodeJS.Timeout;
/**
@@ -26,7 +31,7 @@ export class Client {
* @param conn
* @package
*/
constructor(server: Server, conn: Socket) {
constructor(server: Server<ListenEvents, EmitEvents>, conn: any) {
this.server = server;
this.conn = conn;
this.encoder = server.encoder;
@@ -87,7 +92,7 @@ export class Client {
this.server._checkNamespace(
name,
auth,
(dynamicNspName: Namespace | false) => {
(dynamicNspName: Namespace<ListenEvents, EmitEvents> | false) => {
if (dynamicNspName) {
debug("dynamic namespace %s was created", dynamicNspName);
this.doConnect(name, auth);
@@ -145,7 +150,7 @@ export class Client {
*
* @private
*/
_remove(socket: Socket): void {
_remove(socket: Socket<ListenEvents, EmitEvents>): void {
if (this.sockets.has(socket.id)) {
const nsp = this.sockets.get(socket.id)!.nsp.name;
this.sockets.delete(socket.id);

View File

@@ -7,7 +7,11 @@ import path = require("path");
import engine = require("engine.io");
import { Client } from "./client";
import { EventEmitter } from "events";
import { ExtendedError, Namespace } from "./namespace";
import {
ExtendedError,
Namespace,
NamespaceReservedEventsMap,
} from "./namespace";
import { ParentNamespace } from "./parent-namespace";
import { Adapter, Room, SocketId } from "socket.io-adapter";
import * as parser from "socket.io-parser";
@@ -16,6 +20,13 @@ import debugModule from "debug";
import { Socket } from "./socket";
import type { CookieSerializeOptions } from "cookie";
import type { CorsOptions } from "cors";
import type { BroadcastOperator, RemoteSocket } from "./broadcast-operator";
import {
EventsMap,
DefaultEventsMap,
EventParams,
StrictEventEmitter,
} from "./typed-events";
const debug = debugModule("socket.io:server");
@@ -32,7 +43,7 @@ type ParentNspNameMatchFn = (
interface EngineOptions {
/**
* how many ms without a pong packet to consider the connection closed
* @default 5000
* @default 20000
*/
pingTimeout: number;
/**
@@ -82,10 +93,12 @@ interface EngineOptions {
httpCompression: boolean | object;
/**
* what WebSocket server implementation to use. Specified module must
* conform to the ws interface (see ws module api docs). Default value is ws.
* An alternative c++ addon is also available by installing uws module.
* conform to the ws interface (see ws module api docs).
* An alternative c++ addon is also available by installing eiows module.
*
* @default `require("ws").Server`
*/
wsEngine: string;
wsEngine: Function;
/**
* an optional packet which will be concatenated to the handshake packet emitted by Engine.IO.
*/
@@ -155,8 +168,26 @@ interface ServerOptions extends EngineAttachOptions {
connectTimeout: number;
}
export class Server extends EventEmitter {
public readonly sockets: Namespace;
export class Server<
ListenEvents extends EventsMap = DefaultEventsMap,
EmitEvents extends EventsMap = ListenEvents
> extends StrictEventEmitter<
{},
EmitEvents,
NamespaceReservedEventsMap<ListenEvents, EmitEvents>
> {
public readonly sockets: Namespace<ListenEvents, EmitEvents>;
/**
* A reference to the underlying Engine.IO server.
*
* Example:
*
* <code>
* const clientsCount = io.engine.clientsCount;
* </code>
*
*/
public engine: any;
/** @private */
readonly _parser: typeof parser;
@@ -166,13 +197,15 @@ export class Server extends EventEmitter {
/**
* @private
*/
_nsps: Map<string, Namespace> = new Map();
private parentNsps: Map<ParentNspNameMatchFn, ParentNamespace> = new Map();
_nsps: Map<string, Namespace<ListenEvents, EmitEvents>> = new Map();
private parentNsps: Map<
ParentNspNameMatchFn,
ParentNamespace<ListenEvents, EmitEvents>
> = new Map();
private _adapter?: typeof Adapter;
private _serveClient: boolean;
private opts: Partial<EngineOptions>;
private eio;
private engine;
private _path: string;
private clientPathRegex: RegExp;
@@ -247,7 +280,7 @@ export class Server extends EventEmitter {
_checkNamespace(
name: string,
auth: { [key: string]: any },
fn: (nsp: Namespace | false) => void
fn: (nsp: Namespace<ListenEvents, EmitEvents> | false) => void
): void {
if (this.parentNsps.size === 0) return fn(false);
@@ -556,8 +589,8 @@ export class Server extends EventEmitter {
*/
public of(
name: string | RegExp | ParentNspNameMatchFn,
fn?: (socket: Socket) => void
): Namespace {
fn?: (socket: Socket<ListenEvents, EmitEvents>) => void
): Namespace<ListenEvents, EmitEvents> {
if (typeof name === "function" || name instanceof RegExp) {
const parentNsp = new ParentNamespace(this);
debug("initializing parent namespace %s", parentNsp.name);
@@ -615,7 +648,10 @@ export class Server extends EventEmitter {
* @public
*/
public use(
fn: (socket: Socket, next: (err?: ExtendedError) => void) => void
fn: (
socket: Socket<ListenEvents, EmitEvents>,
next: (err?: ExtendedError) => void
) => void
): this {
this.sockets.use(fn);
return this;
@@ -624,24 +660,34 @@ export class Server extends EventEmitter {
/**
* Targets a room when emitting.
*
* @param name
* @param room
* @return self
* @public
*/
public to(name: Room): this {
this.sockets.to(name);
return this;
public to(room: Room | Room[]): BroadcastOperator<EmitEvents> {
return this.sockets.to(room);
}
/**
* Targets a room when emitting.
*
* @param room
* @return self
* @public
*/
public in(room: Room | Room[]): BroadcastOperator<EmitEvents> {
return this.sockets.in(room);
}
/**
* Excludes a room when emitting.
*
* @param name
* @return self
* @public
*/
public in(name: Room): this {
this.sockets.in(name);
public except(name: Room | Room[]): Server<ListenEvents, EmitEvents> {
this.sockets.except(name);
return this;
}
@@ -651,7 +697,7 @@ export class Server extends EventEmitter {
* @return self
* @public
*/
public send(...args: readonly any[]): this {
public send(...args: EventParams<EmitEvents, "message">): this {
this.sockets.emit("message", ...args);
return this;
}
@@ -662,7 +708,7 @@ export class Server extends EventEmitter {
* @return self
* @public
*/
public write(...args: readonly any[]): this {
public write(...args: EventParams<EmitEvents, "message">): this {
this.sockets.emit("message", ...args);
return this;
}
@@ -683,9 +729,8 @@ export class Server extends EventEmitter {
* @return self
* @public
*/
public compress(compress: boolean): this {
this.sockets.compress(compress);
return this;
public compress(compress: boolean): BroadcastOperator<EmitEvents> {
return this.sockets.compress(compress);
}
/**
@@ -696,9 +741,8 @@ export class Server extends EventEmitter {
* @return self
* @public
*/
public get volatile(): this {
this.sockets.volatile;
return this;
public get volatile(): BroadcastOperator<EmitEvents> {
return this.sockets.volatile;
}
/**
@@ -707,9 +751,47 @@ export class Server extends EventEmitter {
* @return self
* @public
*/
public get local(): this {
this.sockets.local;
return this;
public get local(): BroadcastOperator<EmitEvents> {
return this.sockets.local;
}
/**
* Returns the matching socket instances
*
* @public
*/
public fetchSockets(): Promise<RemoteSocket<EmitEvents>[]> {
return this.sockets.fetchSockets();
}
/**
* Makes the matching socket instances join the specified rooms
*
* @param room
* @public
*/
public socketsJoin(room: Room | Room[]): void {
return this.sockets.socketsJoin(room);
}
/**
* Makes the matching socket instances leave the specified rooms
*
* @param room
* @public
*/
public socketsLeave(room: Room | Room[]): void {
return this.sockets.socketsLeave(room);
}
/**
* Makes the matching socket instances disconnect
*
* @param close - whether to close the underlying connection
* @public
*/
public disconnectSockets(close: boolean = false): void {
return this.sockets.disconnectSockets(close);
}
}
@@ -731,5 +813,7 @@ emitterMethods.forEach(function (fn) {
module.exports = (srv?, opts?) => new Server(srv, opts);
module.exports.Server = Server;
module.exports.Namespace = Namespace;
module.exports.Socket = Socket;
export { Socket, ServerOptions, Namespace };
export { Socket, ServerOptions, Namespace, BroadcastOperator, RemoteSocket };

View File

@@ -1,10 +1,16 @@
import { Socket, RESERVED_EVENTS } from "./socket";
import { Socket } from "./socket";
import type { Server } from "./index";
import {
EventParams,
EventNames,
EventsMap,
StrictEventEmitter,
DefaultEventsMap,
} from "./typed-events";
import type { Client } from "./client";
import { EventEmitter } from "events";
import { PacketType } from "socket.io-parser";
import debugModule from "debug";
import type { Adapter, Room, SocketId } from "socket.io-adapter";
import { BroadcastOperator, RemoteSocket } from "./broadcast-operator";
const debug = debugModule("socket.io:namespace");
@@ -12,26 +18,41 @@ export interface ExtendedError extends Error {
data?: any;
}
export class Namespace extends EventEmitter {
export interface NamespaceReservedEventsMap<
ListenEvents extends EventsMap,
EmitEvents extends EventsMap
> {
connect: (socket: Socket<ListenEvents, EmitEvents>) => void;
connection: (socket: Socket<ListenEvents, EmitEvents>) => void;
}
export class Namespace<
ListenEvents extends EventsMap = DefaultEventsMap,
EmitEvents extends EventsMap = ListenEvents
> extends StrictEventEmitter<
{},
EmitEvents,
NamespaceReservedEventsMap<ListenEvents, EmitEvents>
> {
public readonly name: string;
public readonly sockets: Map<SocketId, Socket> = new Map();
public readonly sockets: Map<
SocketId,
Socket<ListenEvents, EmitEvents>
> = new Map();
public adapter: Adapter;
/** @private */
readonly server: Server;
readonly server: Server<ListenEvents, EmitEvents>;
/** @private */
_fns: Array<
(socket: Socket, next: (err?: ExtendedError) => void) => void
(
socket: Socket<ListenEvents, EmitEvents>,
next: (err?: ExtendedError) => void
) => void
> = [];
/** @private */
_rooms: Set<Room> = new Set();
/** @private */
_flags: any = {};
/** @private */
_ids: number = 0;
@@ -41,7 +62,7 @@ export class Namespace extends EventEmitter {
* @param server instance
* @param name
*/
constructor(server: Server, name: string) {
constructor(server: Server<ListenEvents, EmitEvents>, name: string) {
super();
this.server = server;
this.name = name;
@@ -66,7 +87,10 @@ export class Namespace extends EventEmitter {
* @public
*/
public use(
fn: (socket: Socket, next: (err?: ExtendedError) => void) => void
fn: (
socket: Socket<ListenEvents, EmitEvents>,
next: (err?: ExtendedError) => void
) => void
): this {
this._fns.push(fn);
return this;
@@ -79,7 +103,10 @@ export class Namespace extends EventEmitter {
* @param fn - last fn call in the middleware
* @private
*/
private run(socket: Socket, fn: (err: ExtendedError | null) => void) {
private run(
socket: Socket<ListenEvents, EmitEvents>,
fn: (err: ExtendedError | null) => void
) {
const fns = this._fns.slice(0);
if (!fns.length) return fn(null);
@@ -102,25 +129,34 @@ export class Namespace extends EventEmitter {
/**
* Targets a room when emitting.
*
* @param name
* @param room
* @return self
* @public
*/
public to(name: Room): this {
this._rooms.add(name);
return this;
public to(room: Room | Room[]): BroadcastOperator<EmitEvents> {
return new BroadcastOperator(this.adapter).to(room);
}
/**
* Targets a room when emitting.
*
* @param name
* @param room
* @return self
* @public
*/
public in(name: Room): this {
this._rooms.add(name);
return this;
public in(room: Room | Room[]): BroadcastOperator<EmitEvents> {
return new BroadcastOperator(this.adapter).in(room);
}
/**
* Excludes a room when emitting.
*
* @param room
* @return self
* @public
*/
public except(room: Room | Room[]): BroadcastOperator<EmitEvents> {
return new BroadcastOperator(this.adapter).except(room);
}
/**
@@ -129,7 +165,11 @@ export class Namespace extends EventEmitter {
* @return {Socket}
* @private
*/
_add(client: Client, query, fn?: () => void): Socket {
_add(
client: Client<ListenEvents, EmitEvents>,
query,
fn?: () => void
): Socket<ListenEvents, EmitEvents> {
debug("adding socket to nsp %s", this.name);
const socket = new Socket(this, client, query);
this.run(socket, (err) => {
@@ -157,8 +197,8 @@ export class Namespace extends EventEmitter {
if (fn) fn();
// fire user-set events
super.emit("connect", socket);
super.emit("connection", socket);
this.emitReserved("connect", socket);
this.emitReserved("connection", socket);
} else {
debug("next called after client was closed - ignoring socket");
}
@@ -172,7 +212,7 @@ export class Namespace extends EventEmitter {
*
* @private
*/
_remove(socket: Socket): void {
_remove(socket: Socket<ListenEvents, EmitEvents>): void {
if (this.sockets.has(socket.id)) {
this.sockets.delete(socket.id);
} else {
@@ -186,34 +226,11 @@ export class Namespace extends EventEmitter {
* @return Always true
* @public
*/
public emit(ev: string | Symbol, ...args: any[]): true {
if (RESERVED_EVENTS.has(ev)) {
throw new Error(`"${ev}" is a reserved event name`);
}
// set up packet object
args.unshift(ev);
const packet = {
type: PacketType.EVENT,
data: args,
};
if ("function" == typeof args[args.length - 1]) {
throw new Error("Callbacks are not supported when broadcasting");
}
const rooms = new Set(this._rooms);
const flags = Object.assign({}, this._flags);
// reset flags
this._rooms.clear();
this._flags = {};
this.adapter.broadcast(packet, {
rooms: rooms,
flags: flags,
});
return true;
public emit<Ev extends EventNames<EmitEvents>>(
ev: Ev,
...args: EventParams<EmitEvents, Ev>
): boolean {
return new BroadcastOperator<EmitEvents>(this.adapter).emit(ev, ...args);
}
/**
@@ -222,7 +239,7 @@ export class Namespace extends EventEmitter {
* @return self
* @public
*/
public send(...args: readonly any[]): this {
public send(...args: EventParams<EmitEvents, "message">): this {
this.emit("message", ...args);
return this;
}
@@ -233,7 +250,7 @@ export class Namespace extends EventEmitter {
* @return self
* @public
*/
public write(...args: readonly any[]): this {
public write(...args: EventParams<EmitEvents, "message">): this {
this.emit("message", ...args);
return this;
}
@@ -245,14 +262,7 @@ export class Namespace extends EventEmitter {
* @public
*/
public allSockets(): Promise<Set<SocketId>> {
if (!this.adapter) {
throw new Error(
"No adapter for this namespace, are you trying to get the list of clients of a dynamic namespace?"
);
}
const rooms = new Set(this._rooms);
this._rooms.clear();
return this.adapter.sockets(rooms);
return new BroadcastOperator(this.adapter).allSockets();
}
/**
@@ -262,9 +272,8 @@ export class Namespace extends EventEmitter {
* @return self
* @public
*/
public compress(compress: boolean): this {
this._flags.compress = compress;
return this;
public compress(compress: boolean): BroadcastOperator<EmitEvents> {
return new BroadcastOperator(this.adapter).compress(compress);
}
/**
@@ -275,9 +284,8 @@ export class Namespace extends EventEmitter {
* @return self
* @public
*/
public get volatile(): this {
this._flags.volatile = true;
return this;
public get volatile(): BroadcastOperator<EmitEvents> {
return new BroadcastOperator(this.adapter).volatile;
}
/**
@@ -286,8 +294,46 @@ export class Namespace extends EventEmitter {
* @return self
* @public
*/
public get local(): this {
this._flags.local = true;
return this;
public get local(): BroadcastOperator<EmitEvents> {
return new BroadcastOperator(this.adapter).local;
}
/**
* Returns the matching socket instances
*
* @public
*/
public fetchSockets(): Promise<RemoteSocket<EmitEvents>[]> {
return new BroadcastOperator(this.adapter).fetchSockets();
}
/**
* Makes the matching socket instances join the specified rooms
*
* @param room
* @public
*/
public socketsJoin(room: Room | Room[]): void {
return new BroadcastOperator(this.adapter).socketsJoin(room);
}
/**
* Makes the matching socket instances leave the specified rooms
*
* @param room
* @public
*/
public socketsLeave(room: Room | Room[]): void {
return new BroadcastOperator(this.adapter).socketsLeave(room);
}
/**
* Makes the matching socket instances disconnect
*
* @param close - whether to close the underlying connection
* @public
*/
public disconnectSockets(close: boolean = false): void {
return new BroadcastOperator(this.adapter).disconnectSockets(close);
}
}

View File

@@ -1,11 +1,21 @@
import { Namespace } from "./namespace";
import type { Server } from "./index";
import type {
EventParams,
EventNames,
EventsMap,
DefaultEventsMap,
} from "./typed-events";
import type { BroadcastOptions } from "socket.io-adapter";
export class ParentNamespace extends Namespace {
export class ParentNamespace<
ListenEvents extends EventsMap = DefaultEventsMap,
EmitEvents extends EventsMap = ListenEvents
> extends Namespace<ListenEvents, EmitEvents> {
private static count: number = 0;
private children: Set<Namespace> = new Set();
private children: Set<Namespace<ListenEvents, EmitEvents>> = new Set();
constructor(server: Server) {
constructor(server: Server<ListenEvents, EmitEvents>) {
super(server, "/_" + ParentNamespace.count++);
}
@@ -13,29 +23,34 @@ export class ParentNamespace extends Namespace {
* @private
*/
_initAdapter(): void {
/* no-op */
const broadcast = (packet: any, opts: BroadcastOptions) => {
this.children.forEach((nsp) => {
nsp.adapter.broadcast(packet, opts);
});
};
// @ts-ignore FIXME is there a way to declare an inner class in TypeScript?
this.adapter = { broadcast };
}
public emit(ev: string | Symbol, ...args: [...any]): true {
public emit<Ev extends EventNames<EmitEvents>>(
ev: Ev,
...args: EventParams<EmitEvents, Ev>
): boolean {
this.children.forEach((nsp) => {
nsp._rooms = this._rooms;
nsp._flags = this._flags;
nsp.emit(ev, ...args);
});
this._rooms.clear();
this._flags = {};
return true;
}
createChild(name: string): Namespace {
createChild(name: string): Namespace<ListenEvents, EmitEvents> {
const namespace = new Namespace(this.server, name);
namespace._fns = this._fns.slice(0);
this.listeners("connect").forEach((listener) =>
namespace.on("connect", listener as (...args: any[]) => void)
namespace.on("connect", listener)
);
this.listeners("connection").forEach((listener) =>
namespace.on("connection", listener as (...args: any[]) => void)
namespace.on("connection", listener)
);
this.children.add(namespace);
this.server._nsps.set(name, namespace);

View File

@@ -1,10 +1,16 @@
import { EventEmitter } from "events";
import { Packet, PacketType } from "socket.io-parser";
import url = require("url");
import debugModule from "debug";
import type { Server } from "./index";
import {
EventParams,
EventNames,
EventsMap,
StrictEventEmitter,
DefaultEventsMap,
} from "./typed-events";
import type { Client } from "./client";
import type { Namespace } from "./namespace";
import type { Namespace, NamespaceReservedEventsMap } from "./namespace";
import type { IncomingMessage, IncomingHttpHeaders } from "http";
import type {
Adapter,
@@ -14,18 +20,43 @@ import type {
} from "socket.io-adapter";
import base64id from "base64id";
import type { ParsedUrlQuery } from "querystring";
import { BroadcastOperator } from "./broadcast-operator";
const debug = debugModule("socket.io:socket");
export const RESERVED_EVENTS = new Set(<const>[
type ClientReservedEvents = "connect_error";
export interface SocketReservedEventsMap {
disconnect: (reason: string) => void;
disconnecting: (reason: string) => void;
error: (err: Error) => void;
}
// EventEmitter reserved events: https://nodejs.org/api/events.html#events_event_newlistener
export interface EventEmitterReservedEventsMap {
newListener: (
eventName: string | Symbol,
listener: (...args: any[]) => void
) => void;
removeListener: (
eventName: string | Symbol,
listener: (...args: any[]) => void
) => void;
}
export const RESERVED_EVENTS: ReadonlySet<string | Symbol> = new Set<
| ClientReservedEvents
| keyof NamespaceReservedEventsMap<never, never>
| keyof SocketReservedEventsMap
| keyof EventEmitterReservedEventsMap
>(<const>[
"connect",
"connect_error",
"disconnect",
"disconnecting",
// EventEmitter reserved events: https://nodejs.org/api/events.html#events_event_newlistener
"newListener",
"removeListener",
]) as ReadonlySet<string | Symbol>;
]);
/**
* The handshake details
@@ -77,21 +108,31 @@ export interface Handshake {
auth: { [key: string]: any };
}
export class Socket extends EventEmitter {
export class Socket<
ListenEvents extends EventsMap = DefaultEventsMap,
EmitEvents extends EventsMap = ListenEvents
> extends StrictEventEmitter<
ListenEvents,
EmitEvents,
SocketReservedEventsMap
> {
public readonly id: SocketId;
public readonly handshake: Handshake;
/**
* Additional information that can be attached to the Socket instance and which will be used in the fetchSockets method
*/
public data: any = {};
public connected: boolean;
public disconnected: boolean;
private readonly server: Server;
private readonly server: Server<ListenEvents, EmitEvents>;
private readonly adapter: Adapter;
private acks: Map<number, () => void> = new Map();
private fns: Array<
(event: Array<any>, next: (err: Error) => void) => void
(event: Array<any>, next: (err?: Error) => void) => void
> = [];
private flags: BroadcastFlags = {};
private _rooms: Set<Room> = new Set();
private _anyListeners?: Array<(...args: any[]) => void>;
/**
@@ -102,7 +143,11 @@ export class Socket extends EventEmitter {
* @param {Object} auth
* @package
*/
constructor(readonly nsp: Namespace, readonly client: Client, auth: object) {
constructor(
readonly nsp: Namespace<ListenEvents, EmitEvents>,
readonly client: Client<ListenEvents, EmitEvents>,
auth: object
) {
super();
this.server = nsp.server;
this.adapter = this.nsp.adapter;
@@ -143,69 +188,65 @@ export class Socket extends EventEmitter {
* @return Always returns `true`.
* @public
*/
public emit(ev: string, ...args: any[]): boolean {
public emit<Ev extends EventNames<EmitEvents>>(
ev: Ev,
...args: EventParams<EmitEvents, Ev>
): boolean {
if (RESERVED_EVENTS.has(ev)) {
throw new Error(`"${ev}" is a reserved event name`);
}
args.unshift(ev);
const data: any[] = [ev, ...args];
const packet: any = {
type: PacketType.EVENT,
data: args,
data: data,
};
// access last argument to see if it's an ACK callback
if (typeof args[args.length - 1] === "function") {
if (this._rooms.size || this.flags.broadcast) {
throw new Error("Callbacks are not supported when broadcasting");
}
if (typeof data[data.length - 1] === "function") {
debug("emitting packet with ack id %d", this.nsp._ids);
this.acks.set(this.nsp._ids, args.pop());
this.acks.set(this.nsp._ids, data.pop());
packet.id = this.nsp._ids++;
}
const rooms = new Set(this._rooms);
const flags = Object.assign({}, this.flags);
// reset flags
this._rooms.clear();
this.flags = {};
if (rooms.size || flags.broadcast) {
this.adapter.broadcast(packet, {
except: new Set([this.id]),
rooms: rooms,
flags: flags,
});
} else {
// dispatch packet
this.packet(packet, flags);
}
this.packet(packet, flags);
return true;
}
/**
* Targets a room when broadcasting.
*
* @param name
* @param room
* @return self
* @public
*/
public to(name: Room): this {
this._rooms.add(name);
return this;
public to(room: Room | Room[]): BroadcastOperator<EmitEvents> {
return this.newBroadcastOperator().to(room);
}
/**
* Targets a room when broadcasting.
*
* @param name
* @param room
* @return self
* @public
*/
public in(name: Room): this {
this._rooms.add(name);
return this;
public in(room: Room | Room[]): BroadcastOperator<EmitEvents> {
return this.newBroadcastOperator().in(room);
}
/**
* Excludes a room when broadcasting.
*
* @param room
* @return self
* @public
*/
public except(room: Room | Room[]): BroadcastOperator<EmitEvents> {
return this.newBroadcastOperator().except(room);
}
/**
@@ -214,7 +255,7 @@ export class Socket extends EventEmitter {
* @return self
* @public
*/
public send(...args: readonly any[]): this {
public send(...args: EventParams<EmitEvents, "message">): this {
this.emit("message", ...args);
return this;
}
@@ -225,7 +266,7 @@ export class Socket extends EventEmitter {
* @return self
* @public
*/
public write(...args: readonly any[]): this {
public write(...args: EventParams<EmitEvents, "message">): this {
this.emit("message", ...args);
return this;
}
@@ -416,9 +457,9 @@ export class Socket extends EventEmitter {
*
* @private
*/
_onerror(err): void {
_onerror(err: Error): void {
if (this.listeners("error").length) {
super.emit("error", err);
this.emitReserved("error", err);
} else {
console.error("Missing error handler on `socket`.");
console.error(err.stack);
@@ -436,13 +477,13 @@ export class Socket extends EventEmitter {
_onclose(reason: string): this | undefined {
if (!this.connected) return this;
debug("closing socket - reason %s", reason);
super.emit("disconnecting", reason);
this.emitReserved("disconnecting", reason);
this.leaveAll();
this.nsp._remove(this);
this.client._remove(this);
this.connected = false;
this.disconnected = true;
super.emit("disconnect", reason);
this.emitReserved("disconnect", reason);
return;
}
@@ -508,9 +549,8 @@ export class Socket extends EventEmitter {
* @return {Socket} self
* @public
*/
public get broadcast(): this {
this.flags.broadcast = true;
return this;
public get broadcast(): BroadcastOperator<EmitEvents> {
return this.newBroadcastOperator();
}
/**
@@ -519,9 +559,8 @@ export class Socket extends EventEmitter {
* @return {Socket} self
* @public
*/
public get local(): this {
this.flags.local = true;
return this;
public get local(): BroadcastOperator<EmitEvents> {
return this.newBroadcastOperator().local;
}
/**
@@ -538,7 +577,7 @@ export class Socket extends EventEmitter {
return this._onerror(err);
}
if (this.connected) {
super.emit.apply(this, event);
super.emitUntyped.apply(this, event);
} else {
debug("ignore packet received after disconnection");
}
@@ -554,7 +593,7 @@ export class Socket extends EventEmitter {
* @public
*/
public use(
fn: (event: Array<any>, next: (err: Error) => void) => void
fn: (event: Array<any>, next: (err?: Error) => void) => void
): this {
this.fns.push(fn);
return this;
@@ -674,4 +713,15 @@ export class Socket extends EventEmitter {
public listenersAny() {
return this._anyListeners || [];
}
private newBroadcastOperator(): BroadcastOperator<EmitEvents> {
const flags = Object.assign({}, this.flags);
this.flags = {};
return new BroadcastOperator(
this.adapter,
new Set<Room>(),
new Set<Room>([this.id]),
flags
);
}
}

179
lib/typed-events.ts Normal file
View File

@@ -0,0 +1,179 @@
import { EventEmitter } from "events";
/**
* An events map is an interface that maps event names to their value, which
* represents the type of the `on` listener.
*/
export interface EventsMap {
[event: string]: any;
}
/**
* The default events map, used if no EventsMap is given. Using this EventsMap
* is equivalent to accepting all event names, and any data.
*/
export interface DefaultEventsMap {
[event: string]: (...args: any[]) => void;
}
/**
* Returns a union type containing all the keys of an event map.
*/
export type EventNames<Map extends EventsMap> = keyof Map & (string | symbol);
/** The tuple type representing the parameters of an event listener */
export type EventParams<
Map extends EventsMap,
Ev extends EventNames<Map>
> = Parameters<Map[Ev]>;
/**
* The event names that are either in ReservedEvents or in UserEvents
*/
export type ReservedOrUserEventNames<
ReservedEventsMap extends EventsMap,
UserEvents extends EventsMap
> = EventNames<ReservedEventsMap> | EventNames<UserEvents>;
/**
* Type of a listener of a user event or a reserved event. If `Ev` is in
* `ReservedEvents`, the reserved event listener is returned.
*/
export type ReservedOrUserListener<
ReservedEvents extends EventsMap,
UserEvents extends EventsMap,
Ev extends ReservedOrUserEventNames<ReservedEvents, UserEvents>
> = FallbackToUntypedListener<
Ev extends EventNames<ReservedEvents>
? ReservedEvents[Ev]
: Ev extends EventNames<UserEvents>
? UserEvents[Ev]
: never
>;
/**
* Returns an untyped listener type if `T` is `never`; otherwise, returns `T`.
*
* This is a hack to mitigate https://github.com/socketio/socket.io/issues/3833.
* Needed because of https://github.com/microsoft/TypeScript/issues/41778
*/
type FallbackToUntypedListener<T> = [T] extends [never]
? (...args: any[]) => void
: T;
/**
* Interface for classes that aren't `EventEmitter`s, but still expose a
* strictly typed `emit` method.
*/
export interface TypedEventBroadcaster<EmitEvents extends EventsMap> {
emit<Ev extends EventNames<EmitEvents>>(
ev: Ev,
...args: EventParams<EmitEvents, Ev>
): boolean;
}
/**
* Strictly typed version of an `EventEmitter`. A `TypedEventEmitter` takes type
* parameters for mappings of event names to event data types, and strictly
* types method calls to the `EventEmitter` according to these event maps.
*
* @typeParam ListenEvents - `EventsMap` of user-defined events that can be
* listened to with `on` or `once`
* @typeParam EmitEvents - `EventsMap` of user-defined events that can be
* emitted with `emit`
* @typeParam ReservedEvents - `EventsMap` of reserved events, that can be
* emitted by socket.io with `emitReserved`, and can be listened to with
* `listen`.
*/
export abstract class StrictEventEmitter<
ListenEvents extends EventsMap,
EmitEvents extends EventsMap,
ReservedEvents extends EventsMap = {}
>
extends EventEmitter
implements TypedEventBroadcaster<EmitEvents> {
/**
* Adds the `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
on<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
ev: Ev,
listener: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this {
return super.on(ev, listener);
}
/**
* Adds a one-time `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
once<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
ev: Ev,
listener: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this {
return super.once(ev, listener);
}
/**
* Emits an event.
*
* @param ev Name of the event
* @param args Values to send to listeners of this event
*/
emit<Ev extends EventNames<EmitEvents>>(
ev: Ev,
...args: EventParams<EmitEvents, Ev>
): boolean {
return super.emit(ev, ...args);
}
/**
* Emits a reserved event.
*
* This method is `protected`, so that only a class extending
* `StrictEventEmitter` can emit its own reserved events.
*
* @param ev Reserved event name
* @param args Arguments to emit along with the event
*/
protected emitReserved<Ev extends EventNames<ReservedEvents>>(
ev: Ev,
...args: EventParams<ReservedEvents, Ev>
): boolean {
return super.emit(ev, ...args);
}
/**
* Emits an event.
*
* This method is `protected`, so that only a class extending
* `StrictEventEmitter` can get around the strict typing. This is useful for
* calling `emit.apply`, which can be called as `emitUntyped.apply`.
*
* @param ev Event name
* @param args Arguments to emit along with the event
*/
protected emitUntyped(ev: string, ...args: any[]): boolean {
return super.emit(ev, ...args);
}
/**
* Returns the listeners listening to an event.
*
* @param event Event name
* @returns Array of listeners subscribed to `event`
*/
listeners<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
event: Ev
): ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>[] {
return super.listeners(event) as ReservedOrUserListener<
ReservedEvents,
ListenEvents,
Ev
>[];
}
}

1362
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "socket.io",
"version": "3.1.2",
"version": "4.0.2",
"description": "node.js realtime framework server",
"keywords": [
"realtime",
@@ -37,7 +37,9 @@
},
"scripts": {
"compile": "rimraf ./dist && tsc",
"test": "npm run format:check && npm run compile && nyc mocha --require ts-node/register --reporter spec --slow 200 --bail --timeout 10000 test/socket.io.ts",
"test": "npm run format:check && npm run compile && npm run test:types && npm run test:unit",
"test:types": "tsd",
"test:unit": "nyc mocha --require ts-node/register --reporter spec --slow 200 --bail --timeout 10000 test/socket.io.ts",
"format:check": "prettier --check \"lib/**/*.ts\" \"test/**/*.ts\"",
"format:fix": "prettier --write \"lib/**/*.ts\" \"test/**/*.ts\"",
"prepack": "npm run compile"
@@ -49,8 +51,8 @@
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.3.1",
"engine.io": "~4.1.0",
"socket.io-adapter": "~2.1.0",
"engine.io": "~5.0.0",
"socket.io-adapter": "~2.2.0",
"socket.io-parser": "~4.0.3"
},
"devDependencies": {
@@ -63,11 +65,12 @@
"nyc": "^15.1.0",
"prettier": "^2.2.0",
"rimraf": "^3.0.2",
"socket.io-client": "3.1.2",
"socket.io-client": "4.0.2",
"socket.io-client-v2": "npm:socket.io-client@^2.4.0",
"superagent": "^6.1.0",
"supertest": "^6.0.1",
"ts-node": "^9.0.0",
"tsd": "^0.14.0",
"typescript": "^4.1.2"
},
"contributors": [
@@ -90,5 +93,8 @@
],
"engines": {
"node": ">=10.0.0"
},
"tsd": {
"directory": "test"
}
}

232
test/socket.io.test-d.ts Normal file
View File

@@ -0,0 +1,232 @@
"use strict";
import { Server, Socket } from "..";
import type { DefaultEventsMap } from "../lib/typed-events";
import { createServer } from "http";
import { expectError, expectType } from "tsd";
// This file is run by tsd, not mocha.
describe("server", () => {
describe("no event map", () => {
describe("on", () => {
it("infers correct types for listener parameters of reserved events", (done) => {
const srv = createServer();
const sio = new Server(srv);
srv.listen(() => {
sio.on("connection", (s) => {
expectType<Socket<DefaultEventsMap, DefaultEventsMap>>(s);
s.on("disconnect", (reason) => {
expectType<string>(reason);
});
s.on("disconnecting", (reason) => {
expectType<string>(reason);
});
});
sio.on("connect", (s) => {
expectType<Socket<DefaultEventsMap, DefaultEventsMap>>(s);
});
done();
});
});
it("infers 'any' for listener parameters of other events", (done) => {
const srv = createServer();
const sio = new Server(srv);
srv.listen(() => {
sio.on("connection", (s) => {
s.on("random", (a, b, c) => {
expectType<any>(a);
expectType<any>(b);
expectType<any>(c);
done();
});
s.emit("random", 1, "2", [3]);
});
});
});
it("infers 'any' for listener parameters of other events using enums", () => {
const srv = createServer();
const sio = new Server(srv);
srv.listen(() => {
sio.on("connection", (socket) => {
expectType<Socket<DefaultEventsMap, DefaultEventsMap>>(socket);
});
enum Events {
CONNECTION = "connection",
TEST = "test",
}
sio.on(Events.CONNECTION, (socket) => {
// TODO(#3833): Make this expect `Socket<DefaultEventsMap, DefaultEventsMap>`
expectType<any>(socket);
socket.on("test", (a, b, c) => {
expectType<any>(a);
expectType<any>(b);
expectType<any>(c);
});
socket.on(Events.TEST, (a, b, c) => {
expectType<any>(a);
expectType<any>(b);
expectType<any>(c);
});
});
});
});
});
describe("emit", () => {
it("accepts any parameters", () => {
const srv = createServer();
const sio = new Server(srv);
srv.listen(() => {
sio.on("connection", (s) => {
s.emit("random", 1, "2", [3]);
s.emit("no parameters");
});
});
});
});
});
describe("single event map", () => {
interface BidirectionalEvents {
random: (a: number, b: string, c: number[]) => void;
}
describe("on", () => {
it("infers correct types for listener parameters", (done) => {
const srv = createServer();
const sio = new Server<BidirectionalEvents>(srv);
expectType<Server<BidirectionalEvents, BidirectionalEvents>>(sio);
srv.listen(() => {
sio.on("connection", (s) => {
expectType<Socket<BidirectionalEvents, BidirectionalEvents>>(s);
s.on("random", (a, b, c) => {
expectType<number>(a);
expectType<string>(b);
expectType<number[]>(c);
done();
});
});
});
});
it("does not accept arguments of wrong types", (done) => {
const srv = createServer();
const sio = new Server<BidirectionalEvents>(srv);
expectError(sio.on("random", (a, b, c) => {}));
srv.listen(() => {
expectError(sio.on("wrong name", (s) => {}));
sio.on("connection", (s) => {
s.on("random", (a, b, c) => {});
expectError(s.on("random"));
expectError(s.on("random", (a, b, c, d) => {}));
expectError(s.on(2, 3));
});
});
});
});
describe("emit", () => {
it("accepts arguments of the correct types", () => {
const srv = createServer();
const sio = new Server<BidirectionalEvents>(srv);
srv.listen(() => {
sio.on("connection", (s) => {
s.emit("random", 1, "2", [3]);
});
});
});
it("does not accept arguments of the wrong types", () => {
const srv = createServer();
const sio = new Server<BidirectionalEvents>(srv);
srv.listen(() => {
sio.on("connection", (s) => {
expectError(s.emit("noParameter", 2));
expectError(s.emit("oneParameter"));
expectError(s.emit("random"));
expectError(s.emit("oneParameter", 2, 3));
expectError(s.emit("random", (a, b, c) => {}));
expectError(s.emit("wrong name", () => {}));
expectError(s.emit("complicated name with spaces", 2));
});
});
});
});
});
describe("listen and emit event maps", () => {
interface ClientToServerEvents {
helloFromClient: (message: string) => void;
}
interface ServerToClientEvents {
helloFromServer: (message: string, x: number) => void;
}
describe("on", () => {
it("infers correct types for listener parameters", (done) => {
const srv = createServer();
const sio = new Server<ClientToServerEvents, ServerToClientEvents>(srv);
expectType<Server<ClientToServerEvents, ServerToClientEvents>>(sio);
srv.listen(() => {
sio.on("connection", (s) => {
expectType<Socket<ClientToServerEvents, ServerToClientEvents>>(s);
s.on("helloFromClient", (message) => {
expectType<string>(message);
done();
});
});
});
});
it("does not accept emit events", (done) => {
const srv = createServer();
const sio = new Server<ClientToServerEvents, ServerToClientEvents>(srv);
srv.listen(() => {
sio.on("connection", (s) => {
expectError(
s.on("helloFromServer", (message, number) => {
done();
})
);
});
});
});
});
describe("emit", () => {
it("accepts arguments of the correct types", (done) => {
const srv = createServer();
const sio = new Server<ClientToServerEvents, ServerToClientEvents>(srv);
srv.listen(() => {
sio.on("connection", (s) => {
s.emit("helloFromServer", "hi", 10);
done();
});
});
});
it("does not accept arguments of wrong types", (done) => {
const srv = createServer();
const sio = new Server<ClientToServerEvents, ServerToClientEvents>(srv);
srv.listen(() => {
sio.on("connection", (s) => {
expectError(s.emit("helloFromClient", "hi"));
expectError(s.emit("helloFromServer", "hi", 10, "10"));
expectError(s.emit("helloFromServer", "hi", "10"));
expectError(s.emit("helloFromServer", 0, 0));
expectError(s.emit("wrong name", 10));
expectError(s.emit("wrong name"));
done();
});
});
});
});
});
});

View File

@@ -1,6 +1,6 @@
"use strict";
import { Server, Socket } from "..";
import { Server, Socket, Namespace } from "..";
import { createServer } from "http";
import fs = require("fs");
import { join } from "path";
@@ -9,13 +9,14 @@ import request from "supertest";
import expect from "expect.js";
import type { AddressInfo } from "net";
import * as io_v2 from "socket.io-client-v2";
const ioc = require("socket.io-client");
import type { SocketId } from "socket.io-adapter";
import { io as ioc, Socket as ClientSocket } from "socket.io-client";
import "./support/util";
import "./utility-methods";
// Creates a socket.io client for the given server
function client(srv, nsp?: string | object, opts?: object) {
function client(srv, nsp?: string | object, opts?: object): ClientSocket {
if ("object" == typeof nsp) {
opts = nsp;
nsp = undefined;
@@ -227,7 +228,7 @@ describe("socket.io", () => {
});
request
.options("http://localhost:54013/socket.io/default/")
.query({ transport: "polling" })
.query({ transport: "polling", EIO: 4 })
.set("Origin", "http://localhost:54023")
.end((err, res) => {
expect(res.status).to.be(204);
@@ -255,7 +256,7 @@ describe("socket.io", () => {
});
request
.get("http://localhost:54014/socket.io/default/")
.query({ transport: "polling" })
.query({ transport: "polling", EIO: 4 })
.set("Origin", "http://localhost:54024")
.end((err, res) => {
expect(res.status).to.be(200);
@@ -275,7 +276,7 @@ describe("socket.io", () => {
request
.get("http://localhost:54022/socket.io/default/")
.query({ transport: "polling" })
.query({ transport: "polling", EIO: 4 })
.end((err, res) => {
expect(res.status).to.be(200);
done();
@@ -289,7 +290,7 @@ describe("socket.io", () => {
request
.get("http://localhost:54023/socket.io/default/")
.set("origin", "http://foo.example")
.query({ transport: "polling" })
.query({ transport: "polling", EIO: 4 })
.end((err, res) => {
expect(res.status).to.be(403);
done();
@@ -332,7 +333,9 @@ describe("socket.io", () => {
const net = require("net");
const server = net.createServer();
const clientSocket = ioc("ws://0.0.0.0:" + PORT, { reconnection: false });
const clientSocket = ioc("ws://0.0.0.0:" + PORT, {
reconnection: false,
});
clientSocket.on("disconnect", () => {
expect(Object.keys(sio._nsps["/"].sockets).length).to.equal(0);
@@ -370,9 +373,6 @@ describe("socket.io", () => {
});
describe("namespaces", () => {
const { Socket } = require("../dist/socket");
const { Namespace } = require("../dist/namespace");
it("should be accessible through .sockets", () => {
const sio = new Server();
expect(sio.sockets).to.be.a(Namespace);
@@ -388,10 +388,22 @@ describe("socket.io", () => {
expect(sio.write).to.be.a("function");
expect(sio.allSockets).to.be.a("function");
expect(sio.compress).to.be.a("function");
expect(sio.volatile).to.be(sio);
expect(sio.local).to.be(sio);
expect(sio.sockets._flags).to.eql({ volatile: true, local: true });
delete sio.sockets._flags;
});
it("should return an immutable broadcast operator", () => {
const sio = new Server();
const operator = sio.local.to(["room1", "room2"]).except("room3");
operator.compress(true).emit("hello");
operator.volatile.emit("hello");
operator.to("room4").emit("hello");
operator.except("room5").emit("hello");
sio.to("room6").emit("hello");
// @ts-ignore
expect(operator.rooms).to.contain("room1", "room2");
// @ts-ignore
expect(operator.exceptRooms).to.contain("room3");
// @ts-ignore
expect(operator.flags).to.eql({ local: true });
});
it("should automatically connect", (done) => {
@@ -592,7 +604,7 @@ describe("socket.io", () => {
const srv = createServer();
const sio = new Server(srv);
const chatSids: string[] = [];
let otherSid = null;
let otherSid: SocketId | null = null;
srv.listen(() => {
const c1 = client(srv, "/chat");
const c2 = client(srv, "/chat", { forceNew: true });
@@ -619,9 +631,9 @@ describe("socket.io", () => {
it("should find all clients in a namespace room", (done) => {
const srv = createServer();
const sio = new Server(srv);
let chatFooSid = null;
let chatBarSid = null;
let otherSid = null;
let chatFooSid: SocketId | null = null;
let chatBarSid: SocketId | null = null;
let otherSid: SocketId | null = null;
srv.listen(() => {
const c1 = client(srv, "/chat");
const c2 = client(srv, "/chat", { forceNew: true });
@@ -658,9 +670,9 @@ describe("socket.io", () => {
it("should find all clients across namespace rooms", (done) => {
const srv = createServer();
const sio = new Server(srv);
let chatFooSid = null;
let chatBarSid = null;
let otherSid = null;
let chatFooSid: SocketId | null = null;
let chatBarSid: SocketId | null = null;
let otherSid: SocketId | null = null;
srv.listen(() => {
const c1 = client(srv, "/chat");
const c2 = client(srv, "/chat", { forceNew: true });
@@ -823,6 +835,57 @@ describe("socket.io", () => {
});
});
it("should exclude a specific socket when emitting", (done) => {
const srv = createServer();
const sio = new Server(srv);
const nsp = sio.of("/nsp");
srv.listen(() => {
const socket1 = client(srv, "/nsp");
const socket2 = client(srv, "/nsp");
socket2.on("a", () => {
done(new Error("not"));
});
socket1.on("a", () => {
done();
});
socket2.on("connect", () => {
nsp.except(socket2.id).emit("a");
});
});
});
it("should exclude a specific room when emitting", (done) => {
const srv = createServer();
const sio = new Server(srv);
const nsp = sio.of("/nsp");
srv.listen(() => {
const socket1 = client(srv, "/nsp");
const socket2 = client(srv, "/nsp");
socket1.on("a", () => {
done();
});
socket2.on("a", () => {
done(new Error("not"));
});
nsp.on("connection", (socket) => {
socket.on("broadcast", () => {
socket.join("room1");
nsp.except("room1").emit("a");
});
});
socket2.emit("broadcast");
});
});
describe("dynamic namespaces", () => {
it("should allow connections to dynamic namespaces with a regex", (done) => {
const srv = createServer();
@@ -889,7 +952,7 @@ describe("socket.io", () => {
srv.listen(() => {
const clientSocket = client(srv, { reconnection: false });
clientSocket.on("connect", function init() {
clientSocket.removeListener("connect", init);
clientSocket.off("connect", init);
clientSocket.io.engine.close();
clientSocket.connect();
@@ -978,7 +1041,7 @@ describe("socket.io", () => {
done();
});
});
s.client.ondata(null);
(s as any).client.ondata(null);
});
});
});
@@ -2166,7 +2229,7 @@ describe("socket.io", () => {
expect(s.rooms).to.contain(s.id, "a", "b", "c");
s.leave("b");
expect(s.rooms).to.contain(s.id, "a", "c");
s.leaveAll();
(s as any).leaveAll();
expect(s.rooms.size).to.eql(0);
done();
});
@@ -2202,7 +2265,7 @@ describe("socket.io", () => {
expect(s.rooms).to.contain(s.id, "a", "b");
s.leave("unknown");
expect(s.rooms).to.contain(s.id, "a", "b");
s.leaveAll();
(s as any).leaveAll();
expect(s.rooms.size).to.eql(0);
done();
});
@@ -2222,11 +2285,109 @@ describe("socket.io", () => {
});
});
});
it("should exclude specific sockets when broadcasting", (done) => {
const srv = createServer();
const sio = new Server(srv);
srv.listen(() => {
const socket1 = client(srv, { multiplex: false });
const socket2 = client(srv, { multiplex: false });
const socket3 = client(srv, { multiplex: false });
socket2.on("a", () => {
done(new Error("not"));
});
socket3.on("a", () => {
done(new Error("not"));
});
socket1.on("a", () => {
done();
});
sio.on("connection", (socket) => {
socket.on("exclude", (id) => {
socket.broadcast.except(id).emit("a");
});
});
socket2.on("connect", () => {
socket3.emit("exclude", socket2.id);
});
});
});
it("should exclude a specific room when broadcasting", (done) => {
const srv = createServer();
const sio = new Server(srv);
srv.listen(() => {
const socket1 = client(srv, { multiplex: false });
const socket2 = client(srv, { multiplex: false });
const socket3 = client(srv, { multiplex: false });
socket2.on("a", () => {
done(new Error("not"));
});
socket3.on("a", () => {
done(new Error("not"));
});
socket1.on("a", () => {
done();
});
sio.on("connection", (socket) => {
socket.on("join", (room, cb) => {
socket.join(room);
cb();
});
socket.on("broadcast", () => {
socket.broadcast.except("room1").emit("a");
});
});
socket2.emit("join", "room1", () => {
socket3.emit("broadcast");
});
});
});
it("should return an immutable broadcast operator", (done) => {
const srv = createServer();
const sio = new Server(srv);
srv.listen(() => {
const clientSocket = client(srv, { multiplex: false });
sio.on("connection", (socket: Socket) => {
const operator = socket.local
.compress(false)
.to(["room1", "room2"])
.except("room3");
operator.compress(true).emit("hello");
operator.volatile.emit("hello");
operator.to("room4").emit("hello");
operator.except("room5").emit("hello");
socket.emit("hello");
socket.to("room6").emit("hello");
// @ts-ignore
expect(operator.rooms).to.contain("room1", "room2");
// @ts-ignore
expect(operator.rooms).to.not.contain("room4", "room5", "room6");
// @ts-ignore
expect(operator.exceptRooms).to.contain("room3");
// @ts-ignore
expect(operator.flags).to.eql({ local: true, compress: false });
clientSocket.close();
sio.close();
done();
});
});
});
});
describe("middleware", () => {
const { Socket } = require("../dist/socket");
it("should call functions", (done) => {
const srv = createServer();
const sio = new Server(srv);
@@ -2289,6 +2450,7 @@ describe("socket.io", () => {
socket.on("connect_error", (err) => {
expect(err).to.be.an(Error);
expect(err.message).to.eql("Authentication error");
// @ts-ignore
expect(err.data).to.eql({ a: "b", c: 3 });
done();
});
@@ -2305,7 +2467,7 @@ describe("socket.io", () => {
srv.listen(() => {
const socket = client(srv);
sio.on("connection", (socket) => {
expect(socket.name).to.be("guillermo");
expect((socket as any).name).to.be("guillermo");
done();
});
});
@@ -2414,8 +2576,6 @@ describe("socket.io", () => {
});
describe("socket middleware", () => {
const { Socket } = require("../dist/socket");
it("should call functions", (done) => {
const srv = createServer();
const sio = new Server(srv);

176
test/utility-methods.ts Normal file
View File

@@ -0,0 +1,176 @@
import { createServer } from "http";
import { Server, Socket } from "..";
import { io as ioc, Socket as ClientSocket } from "socket.io-client";
import { Adapter, BroadcastOptions } from "socket.io-adapter";
import expect from "expect.js";
import type { AddressInfo } from "net";
import "./support/util";
const SOCKETS_COUNT = 3;
const createPartialDone = (
count: number,
done: () => void,
callback?: () => void
) => {
let i = 0;
return () => {
i++;
if (i === count) {
done();
if (callback) {
callback();
}
}
};
};
class DummyAdapter extends Adapter {
fetchSockets(opts: BroadcastOptions): Promise<any[]> {
return Promise.resolve([
{
id: "42",
handshake: {
headers: {
accept: "*/*",
},
query: {
transport: "polling",
EIO: "4",
},
},
rooms: ["42", "room1"],
data: {
username: "john",
},
},
]);
}
}
describe("socket.io", () => {
let io: Server, clientSockets: ClientSocket[], serverSockets: Socket[];
beforeEach((done) => {
const srv = createServer();
io = new Server(srv);
srv.listen(() => {
const port = (srv.address() as AddressInfo).port;
clientSockets = [];
for (let i = 0; i < SOCKETS_COUNT; i++) {
clientSockets.push(ioc(`http://localhost:${port}`));
}
serverSockets = [];
io.on("connection", (socket: Socket) => {
serverSockets.push(socket);
if (serverSockets.length === SOCKETS_COUNT) {
done();
}
});
});
});
afterEach(() => {
io.close();
clientSockets.forEach((socket) => socket.disconnect());
});
describe("utility methods", () => {
describe("fetchSockets", () => {
it("returns all socket instances", async () => {
const sockets = await io.fetchSockets();
expect(sockets.length).to.eql(3);
});
it("returns all socket instances in the given room", async () => {
serverSockets[0].join(["room1", "room2"]);
serverSockets[1].join("room1");
serverSockets[2].join("room2");
const sockets = await io.in("room1").fetchSockets();
expect(sockets.length).to.eql(2);
});
it("works with a custom adapter", async () => {
io.adapter(DummyAdapter);
const sockets = await io.fetchSockets();
expect(sockets.length).to.eql(1);
const remoteSocket = sockets[0];
expect(remoteSocket.id).to.eql("42");
expect(remoteSocket.rooms).to.contain("42", "room1");
expect(remoteSocket.data).to.eql({ username: "john" });
});
});
describe("socketsJoin", () => {
it("makes all socket instances join the given room", () => {
io.socketsJoin("room1");
serverSockets.forEach((socket) => {
expect(socket.rooms).to.contain("room1");
});
});
it("makes all socket instances in a room join the given room", () => {
serverSockets[0].join(["room1", "room2"]);
serverSockets[1].join("room1");
serverSockets[2].join("room2");
io.in("room1").socketsJoin("room3");
expect(serverSockets[0].rooms).to.contain("room3");
expect(serverSockets[1].rooms).to.contain("room3");
expect(serverSockets[2].rooms).to.not.contain("room3");
});
});
describe("socketsLeave", () => {
it("makes all socket instances leave the given room", () => {
serverSockets[0].join(["room1", "room2"]);
serverSockets[1].join("room1");
serverSockets[2].join("room2");
io.socketsLeave("room1");
expect(serverSockets[0].rooms).to.contain("room2");
expect(serverSockets[0].rooms).to.not.contain("room1");
expect(serverSockets[1].rooms).to.not.contain("room1");
});
it("makes all socket instances in a room leave the given room", () => {
serverSockets[0].join(["room1", "room2"]);
serverSockets[1].join("room1");
serverSockets[2].join("room2");
io.in("room2").socketsLeave("room1");
expect(serverSockets[0].rooms).to.contain("room2");
expect(serverSockets[0].rooms).to.not.contain("room1");
expect(serverSockets[1].rooms).to.contain("room1");
});
});
describe("disconnectSockets", () => {
it("makes all socket instances disconnect", (done) => {
io.disconnectSockets(true);
const partialDone = createPartialDone(3, done);
clientSockets[0].on("disconnect", partialDone);
clientSockets[1].on("disconnect", partialDone);
clientSockets[2].on("disconnect", partialDone);
});
it("makes all socket instances in a room disconnect", (done) => {
serverSockets[0].join(["room1", "room2"]);
serverSockets[1].join("room1");
serverSockets[2].join("room2");
io.in("room2").disconnectSockets(true);
const partialDone = createPartialDone(2, done, () => {
clientSockets[1].off("disconnect");
});
clientSockets[0].on("disconnect", partialDone);
clientSockets[1].on("disconnect", () => {
done(new Error("should not happen"));
});
clientSockets[2].on("disconnect", partialDone);
});
});
});
});