mirror of
https://github.com/socketio/socket.io.git
synced 2026-01-11 16:08:24 -05:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12221f296d | ||
|
|
6f4bd7f8e7 | ||
|
|
4f2e9a716d | ||
|
|
9e8f288ca9 | ||
|
|
86eb4227b2 | ||
|
|
cf873fd831 | ||
|
|
0d10e6131b | ||
|
|
10aafbbc16 | ||
|
|
f34cfca26d | ||
|
|
d412e876b8 | ||
|
|
f05a4a6f82 | ||
|
|
2c883f5d4e | ||
|
|
161091dd4c | ||
|
|
d52532b7be | ||
|
|
6b1d7901db | ||
|
|
b55892ae80 | ||
|
|
233650c222 | ||
|
|
9925746c8e | ||
|
|
de8dffd252 | ||
|
|
f8a66fd11a | ||
|
|
752dfe3b1e | ||
|
|
bf54327421 | ||
|
|
170b739f14 | ||
|
|
230cd19164 | ||
|
|
a0a3481c64 | ||
|
|
f773b4889c | ||
|
|
292d62ea69 | ||
|
|
178e899f48 | ||
|
|
d1bfe40dbb |
32
.github/ISSUE_TEMPLATE.md
vendored
32
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,32 +0,0 @@
|
||||
|
||||
**Note**: for support questions, please use one of these channels: [stackoverflow](http://stackoverflow.com/questions/tagged/socket.io) or [slack](https://socketio.slack.com)
|
||||
|
||||
For bug reports and feature requests for the **Swift client**, please open an issue [there](https://github.com/socketio/socket.io-client-swift).
|
||||
|
||||
For bug reports and feature requests for the **Java client**, please open an issue [there](https://github.com/socketio/socket.io-client-java).
|
||||
|
||||
### You want to:
|
||||
|
||||
* [x] report a *bug*
|
||||
* [ ] request a *feature*
|
||||
|
||||
### Current behaviour
|
||||
|
||||
*What is actually happening?*
|
||||
|
||||
### Steps to reproduce (if the current behaviour is a bug)
|
||||
|
||||
**Note**: the best way (and by that we mean **the only way**) to get a quick answer is to provide a failing test case by forking the following [fiddle](https://github.com/socketio/socket.io-fiddle).
|
||||
|
||||
### Expected behaviour
|
||||
|
||||
*What is expected?*
|
||||
|
||||
### Setup
|
||||
- OS:
|
||||
- browser:
|
||||
- socket.io version:
|
||||
|
||||
### Other information (e.g. stacktraces, related issues, suggestions how to fix)
|
||||
|
||||
|
||||
61
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
61
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
|
||||
Please fill the following code example:
|
||||
|
||||
Socket.IO server version: `x.y.z`
|
||||
|
||||
*Server*
|
||||
|
||||
```js
|
||||
import { Server } from "socket.io";
|
||||
|
||||
const io = new Server(3000, {});
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
console.log(`connect ${socket.id}`);
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
console.log(`disconnect ${socket.id}`);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Socket.IO client version: `x.y.z`
|
||||
|
||||
*Client*
|
||||
|
||||
```js
|
||||
import { io } from "socket.io-client";
|
||||
|
||||
const socket = io("ws://localhost:3000/", {});
|
||||
|
||||
socket.on("connect", () => {
|
||||
console.log(`connect ${socket.id}`);
|
||||
});
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
console.log("disconnect");
|
||||
});
|
||||
```
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Platform:**
|
||||
- Device: [e.g. Samsung S8]
|
||||
- OS: [e.g. Android 9.2]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Ask a Question
|
||||
url: https://github.com/socketio/socket.io/discussions/new?category=q-a
|
||||
about: Ask the community for help
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: 'enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x, 14.x]
|
||||
node-version: [10.x, 12.x, 14.x, 15.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
39
CHANGELOG.md
39
CHANGELOG.md
@@ -1,3 +1,42 @@
|
||||
## [3.1.1](https://github.com/socketio/socket.io/compare/3.1.0...3.1.1) (2021-02-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* properly parse the CONNECT packet in v2 compatibility mode ([6f4bd7f](https://github.com/socketio/socket.io/commit/6f4bd7f8e7c41a075a8014565330a77c38b03a8d))
|
||||
* **typings:** add return types and general-case overload signatures ([#3776](https://github.com/socketio/socket.io/issues/3776)) ([9e8f288](https://github.com/socketio/socket.io/commit/9e8f288ca9f14f91064b8d3cce5946f7d23d407c))
|
||||
* **typings:** update the types of "query", "auth" and "headers" ([4f2e9a7](https://github.com/socketio/socket.io/commit/4f2e9a716d9835b550c8fd9a9b429ebf069c2895))
|
||||
|
||||
|
||||
# [3.1.0](https://github.com/socketio/socket.io/compare/3.0.5...3.1.0) (2021-01-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* confirm a weak but matching ETag ([#3485](https://github.com/socketio/socket.io/issues/3485)) ([161091d](https://github.com/socketio/socket.io/commit/161091dd4c9e1b1610ac3d45d964195e63d92b94))
|
||||
* **esm:** export the Namespace and Socket class ([#3699](https://github.com/socketio/socket.io/issues/3699)) ([233650c](https://github.com/socketio/socket.io/commit/233650c22209708b5fccc4349c38d2fa1b465d8f))
|
||||
* add support for Socket.IO v2 clients ([9925746](https://github.com/socketio/socket.io/commit/9925746c8ee3a6522bd640b5d586c83f04f2f1ba))
|
||||
* add room events ([155fa63](https://github.com/socketio/socket.io-adapter/commit/155fa6333a504036e99a33667dc0397f6aede25e))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow integers as event names ([1c220dd](https://github.com/socketio/socket.io-parser/commit/1c220ddbf45ea4b44bc8dbf6f9ae245f672ba1b9))
|
||||
|
||||
|
||||
## [3.0.5](https://github.com/socketio/socket.io/compare/3.0.4...3.0.5) (2021-01-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* properly clear timeout on connection failure ([170b739](https://github.com/socketio/socket.io/commit/170b739f147cb6c92b423729b877e242e376927d))
|
||||
|
||||
|
||||
### Reverts
|
||||
|
||||
* restore the socket middleware functionality ([bf54327](https://github.com/socketio/socket.io/commit/bf5432742158e4d5ba2722cff4a614967dffa5b9))
|
||||
|
||||
|
||||
## [3.0.4](https://github.com/socketio/socket.io/compare/3.0.3...3.0.4) (2020-12-07)
|
||||
|
||||
|
||||
|
||||
11
Readme.md
11
Readme.md
@@ -1,6 +1,5 @@
|
||||
|
||||
# socket.io
|
||||
|
||||
[](https://repl.it/github/socketio/socket.io)
|
||||
[](#backers) [](#sponsors)
|
||||
[](https://github.com/socketio/socket.io/actions)
|
||||
[](https://david-dm.org/socketio/socket.io)
|
||||
@@ -22,6 +21,8 @@ Some implementations in other languages are also available:
|
||||
- [C++](https://github.com/socketio/socket.io-client-cpp)
|
||||
- [Swift](https://github.com/socketio/socket.io-client-swift)
|
||||
- [Dart](https://github.com/rikulo/socket.io-client-dart)
|
||||
- [Python](https://github.com/miguelgrinberg/python-socketio)
|
||||
- [.Net](https://github.com/Quobject/SocketIoClientDotNet)
|
||||
|
||||
Its main features are:
|
||||
|
||||
@@ -35,7 +36,7 @@ For this purpose, it relies on [Engine.IO](https://github.com/socketio/engine.io
|
||||
|
||||
#### Auto-reconnection support
|
||||
|
||||
Unless instructed otherwise a disconnected client will try to reconnect forever, until the server is available again. Please see the available reconnection options [here](https://github.com/socketio/socket.io-client/blob/master/docs/API.md#new-managerurl-options).
|
||||
Unless instructed otherwise a disconnected client will try to reconnect forever, until the server is available again. Please see the available reconnection options [here](https://socket.io/docs/v3/client-api/#new-Manager-url-options).
|
||||
|
||||
#### Disconnection detection
|
||||
|
||||
@@ -84,7 +85,11 @@ This is a useful feature to send notifications to a group of users, or to a give
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
// with npm
|
||||
npm install socket.io
|
||||
|
||||
// with yarn
|
||||
yarn add socket.io
|
||||
```
|
||||
|
||||
## How to use
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Socket.IO v3.0.4
|
||||
* (c) 2014-2020 Guillermo Rauch
|
||||
* Socket.IO v3.1.1
|
||||
* (c) 2014-2021 Guillermo Rauch
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
@@ -12,17 +12,7 @@
|
||||
exports["io"] = factory();
|
||||
else
|
||||
root["io"] = factory();
|
||||
})((() => {
|
||||
if (typeof self !== 'undefined') {
|
||||
return self;
|
||||
} else if (typeof window !== 'undefined') {
|
||||
return window;
|
||||
} else if (typeof global !== 'undefined') {
|
||||
return global;
|
||||
} else {
|
||||
return Function('return this')();
|
||||
}
|
||||
})(), function() {
|
||||
})(window, function() {
|
||||
return /******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
@@ -161,7 +151,7 @@ function lookup(uri, opts) {
|
||||
}
|
||||
|
||||
opts = opts || {};
|
||||
var parsed = url_1.url(uri);
|
||||
var parsed = url_1.url(uri, opts.path);
|
||||
var source = parsed.source;
|
||||
var id = parsed.id;
|
||||
var path = parsed.path;
|
||||
@@ -182,7 +172,7 @@ function lookup(uri, opts) {
|
||||
}
|
||||
|
||||
if (parsed.query && !opts.query) {
|
||||
opts.query = parsed.query;
|
||||
opts.query = parsed.queryKey;
|
||||
}
|
||||
|
||||
return io.socket(parsed.path, opts);
|
||||
@@ -279,8 +269,6 @@ var parser = __webpack_require__(/*! socket.io-parser */ "./node_modules/socket.
|
||||
|
||||
var on_1 = __webpack_require__(/*! ./on */ "./build/on.js");
|
||||
|
||||
var bind = __webpack_require__(/*! component-bind */ "./node_modules/component-bind/index.js");
|
||||
|
||||
var Backoff = __webpack_require__(/*! backo2 */ "./node_modules/backo2/index.js");
|
||||
|
||||
var debug = __webpack_require__(/*! debug */ "./node_modules/debug/src/browser.js")("socket.io-client:manager");
|
||||
@@ -427,7 +415,7 @@ var Manager = /*#__PURE__*/function (_Emitter) {
|
||||
this._readyState = "opening";
|
||||
this.skipReconnect = false; // emit `open`
|
||||
|
||||
var openSub = on_1.on(socket, "open", function () {
|
||||
var openSubDestroy = on_1.on(socket, "open", function () {
|
||||
self.onopen();
|
||||
fn && fn();
|
||||
}); // emit `error`
|
||||
@@ -452,31 +440,29 @@ var Manager = /*#__PURE__*/function (_Emitter) {
|
||||
debug("connect attempt will timeout after %d", timeout);
|
||||
|
||||
if (timeout === 0) {
|
||||
openSub.destroy(); // prevents a race condition with the 'open' event
|
||||
openSubDestroy(); // prevents a race condition with the 'open' event
|
||||
} // set timer
|
||||
|
||||
|
||||
var timer = setTimeout(function () {
|
||||
debug("connect attempt timed out after %d", timeout);
|
||||
openSub.destroy();
|
||||
openSubDestroy();
|
||||
socket.close();
|
||||
socket.emit("error", new Error("timeout"));
|
||||
}, timeout);
|
||||
this.subs.push({
|
||||
destroy: function destroy() {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
this.subs.push(function subDestroy() {
|
||||
clearTimeout(timer);
|
||||
});
|
||||
}
|
||||
|
||||
this.subs.push(openSub);
|
||||
this.subs.push(openSubDestroy);
|
||||
this.subs.push(errorSub);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Alias for open()
|
||||
*
|
||||
* @return {Manager} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
|
||||
@@ -504,7 +490,7 @@ var Manager = /*#__PURE__*/function (_Emitter) {
|
||||
|
||||
|
||||
var socket = this.engine;
|
||||
this.subs.push(on_1.on(socket, "data", bind(this, "ondata")), on_1.on(socket, "ping", bind(this, "onping")), on_1.on(socket, "error", bind(this, "onerror")), on_1.on(socket, "close", bind(this, "onclose")), on_1.on(this.decoder, "decoded", bind(this, "ondecoded")));
|
||||
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)));
|
||||
}
|
||||
/**
|
||||
* Called upon a ping.
|
||||
@@ -606,7 +592,6 @@ var Manager = /*#__PURE__*/function (_Emitter) {
|
||||
key: "_packet",
|
||||
value: function _packet(packet) {
|
||||
debug("writing packet %j", packet);
|
||||
if (packet.query && packet.type === 0) packet.nsp += "?" + packet.query;
|
||||
var encodedPackets = this.encoder.encode(packet);
|
||||
|
||||
for (var i = 0; i < encodedPackets.length; i++) {
|
||||
@@ -623,13 +608,10 @@ var Manager = /*#__PURE__*/function (_Emitter) {
|
||||
key: "cleanup",
|
||||
value: function cleanup() {
|
||||
debug("cleanup");
|
||||
var subsLength = this.subs.length;
|
||||
|
||||
for (var i = 0; i < subsLength; i++) {
|
||||
var sub = this.subs.shift();
|
||||
sub.destroy();
|
||||
}
|
||||
|
||||
this.subs.forEach(function (subDestroy) {
|
||||
return subDestroy();
|
||||
});
|
||||
this.subs.length = 0;
|
||||
this.decoder.destroy();
|
||||
}
|
||||
/**
|
||||
@@ -732,10 +714,8 @@ var Manager = /*#__PURE__*/function (_Emitter) {
|
||||
}
|
||||
});
|
||||
}, delay);
|
||||
this.subs.push({
|
||||
destroy: function destroy() {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
this.subs.push(function subDestroy() {
|
||||
clearTimeout(timer);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -780,10 +760,8 @@ exports.on = void 0;
|
||||
|
||||
function on(obj, ev, fn) {
|
||||
obj.on(ev, fn);
|
||||
return {
|
||||
destroy: function destroy() {
|
||||
obj.off(ev, fn);
|
||||
}
|
||||
return function subDestroy() {
|
||||
obj.off(ev, fn);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -844,8 +822,6 @@ var Emitter = __webpack_require__(/*! component-emitter */ "./node_modules/compo
|
||||
|
||||
var on_1 = __webpack_require__(/*! ./on */ "./build/on.js");
|
||||
|
||||
var bind = __webpack_require__(/*! component-bind */ "./node_modules/component-bind/index.js");
|
||||
|
||||
var debug = __webpack_require__(/*! debug */ "./node_modules/debug/src/browser.js")("socket.io-client:socket");
|
||||
/**
|
||||
* Internal events.
|
||||
@@ -879,10 +855,10 @@ var Socket = /*#__PURE__*/function (_Emitter) {
|
||||
_classCallCheck(this, Socket);
|
||||
|
||||
_this = _super.call(this);
|
||||
_this.ids = 0;
|
||||
_this.acks = {};
|
||||
_this.receiveBuffer = [];
|
||||
_this.sendBuffer = [];
|
||||
_this.ids = 0;
|
||||
_this.acks = {};
|
||||
_this.flags = {};
|
||||
_this.io = io;
|
||||
_this.nsp = nsp;
|
||||
@@ -913,7 +889,7 @@ var Socket = /*#__PURE__*/function (_Emitter) {
|
||||
value: function subEvents() {
|
||||
if (this.subs) return;
|
||||
var io = this.io;
|
||||
this.subs = [on_1.on(io, "open", bind(this, "onopen")), on_1.on(io, "packet", bind(this, "onpacket")), on_1.on(io, "close", bind(this, "onclose"))];
|
||||
this.subs = [on_1.on(io, "open", this.onopen.bind(this)), on_1.on(io, "packet", this.onpacket.bind(this)), on_1.on(io, "error", this.onerror.bind(this)), on_1.on(io, "close", this.onclose.bind(this))];
|
||||
}
|
||||
/**
|
||||
* Whether the Socket will try to reconnect when its Manager connects or reconnects
|
||||
@@ -1051,6 +1027,20 @@ var Socket = /*#__PURE__*/function (_Emitter) {
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Called upon engine or manager `error`.
|
||||
*
|
||||
* @param err
|
||||
* @private
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: "onerror",
|
||||
value: function onerror(err) {
|
||||
if (!this.connected) {
|
||||
_get(_getPrototypeOf(Socket.prototype), "emit", this).call(this, "connect_error", err);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Called upon engine `close`.
|
||||
*
|
||||
@@ -1281,11 +1271,10 @@ var Socket = /*#__PURE__*/function (_Emitter) {
|
||||
value: function destroy() {
|
||||
if (this.subs) {
|
||||
// clean subscriptions to avoid reconnections
|
||||
for (var i = 0; i < this.subs.length; i++) {
|
||||
this.subs[i].destroy();
|
||||
}
|
||||
|
||||
this.subs = null;
|
||||
this.subs.forEach(function (subDestroy) {
|
||||
return subDestroy();
|
||||
});
|
||||
this.subs = undefined;
|
||||
}
|
||||
|
||||
this.io["_destroy"](this);
|
||||
@@ -1468,13 +1457,16 @@ var debug = __webpack_require__(/*! debug */ "./node_modules/debug/src/browser.j
|
||||
* URL parser.
|
||||
*
|
||||
* @param uri - url
|
||||
* @param path - the request path of the connection
|
||||
* @param loc - An object meant to mimic window.location.
|
||||
* Defaults to window.location.
|
||||
* @public
|
||||
*/
|
||||
|
||||
|
||||
function url(uri, loc) {
|
||||
function url(uri) {
|
||||
var path = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
|
||||
var loc = arguments.length > 2 ? arguments[2] : undefined;
|
||||
var obj = uri; // default to window.location
|
||||
|
||||
loc = loc || typeof location !== "undefined" && location;
|
||||
@@ -1517,7 +1509,7 @@ function url(uri, loc) {
|
||||
var ipv6 = obj.host.indexOf(":") !== -1;
|
||||
var host = ipv6 ? "[" + obj.host + "]" : obj.host; // define unique id
|
||||
|
||||
obj.id = obj.protocol + "://" + host + ":" + obj.port; // define href
|
||||
obj.id = obj.protocol + "://" + host + ":" + obj.port + path; // define href
|
||||
|
||||
obj.href = obj.protocol + "://" + host + (loc && loc.port === obj.port ? "" : ":" + obj.port);
|
||||
return obj;
|
||||
@@ -1620,121 +1612,6 @@ Backoff.prototype.setJitter = function (jitter) {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/base64-arraybuffer/lib/base64-arraybuffer.js":
|
||||
/*!*******************************************************************!*\
|
||||
!*** ./node_modules/base64-arraybuffer/lib/base64-arraybuffer.js ***!
|
||||
\*******************************************************************/
|
||||
/*! no static exports found */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
/*
|
||||
* base64-arraybuffer
|
||||
* https://github.com/niklasvh/base64-arraybuffer
|
||||
*
|
||||
* Copyright (c) 2012 Niklas von Hertzen
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // Use a lookup table to find the index.
|
||||
|
||||
var lookup = new Uint8Array(256);
|
||||
|
||||
for (var i = 0; i < chars.length; i++) {
|
||||
lookup[chars.charCodeAt(i)] = i;
|
||||
}
|
||||
|
||||
exports.encode = function (arraybuffer) {
|
||||
var bytes = new Uint8Array(arraybuffer),
|
||||
i,
|
||||
len = bytes.length,
|
||||
base64 = "";
|
||||
|
||||
for (i = 0; i < len; i += 3) {
|
||||
base64 += chars[bytes[i] >> 2];
|
||||
base64 += chars[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4];
|
||||
base64 += chars[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6];
|
||||
base64 += chars[bytes[i + 2] & 63];
|
||||
}
|
||||
|
||||
if (len % 3 === 2) {
|
||||
base64 = base64.substring(0, base64.length - 1) + "=";
|
||||
} else if (len % 3 === 1) {
|
||||
base64 = base64.substring(0, base64.length - 2) + "==";
|
||||
}
|
||||
|
||||
return base64;
|
||||
};
|
||||
|
||||
exports.decode = function (base64) {
|
||||
var bufferLength = base64.length * 0.75,
|
||||
len = base64.length,
|
||||
i,
|
||||
p = 0,
|
||||
encoded1,
|
||||
encoded2,
|
||||
encoded3,
|
||||
encoded4;
|
||||
|
||||
if (base64[base64.length - 1] === "=") {
|
||||
bufferLength--;
|
||||
|
||||
if (base64[base64.length - 2] === "=") {
|
||||
bufferLength--;
|
||||
}
|
||||
}
|
||||
|
||||
var arraybuffer = new ArrayBuffer(bufferLength),
|
||||
bytes = new Uint8Array(arraybuffer);
|
||||
|
||||
for (i = 0; i < len; i += 4) {
|
||||
encoded1 = lookup[base64.charCodeAt(i)];
|
||||
encoded2 = lookup[base64.charCodeAt(i + 1)];
|
||||
encoded3 = lookup[base64.charCodeAt(i + 2)];
|
||||
encoded4 = lookup[base64.charCodeAt(i + 3)];
|
||||
bytes[p++] = encoded1 << 2 | encoded2 >> 4;
|
||||
bytes[p++] = (encoded2 & 15) << 4 | encoded3 >> 2;
|
||||
bytes[p++] = (encoded3 & 3) << 6 | encoded4 & 63;
|
||||
}
|
||||
|
||||
return arraybuffer;
|
||||
};
|
||||
})();
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/component-bind/index.js":
|
||||
/*!**********************************************!*\
|
||||
!*** ./node_modules/component-bind/index.js ***!
|
||||
\**********************************************/
|
||||
/*! no static exports found */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
/**
|
||||
* Slice reference.
|
||||
*/
|
||||
var slice = [].slice;
|
||||
/**
|
||||
* Bind `obj` to `fn`.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @param {Function|String} fn or string
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function (obj, fn) {
|
||||
if ('string' == typeof fn) fn = obj[fn];
|
||||
if ('function' != typeof fn) throw new Error('bind() requires a function');
|
||||
var args = slice.call(arguments, 2);
|
||||
return function () {
|
||||
return fn.apply(obj, args.concat(slice.call(arguments)));
|
||||
};
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/component-emitter/index.js":
|
||||
/*!*************************************************!*\
|
||||
!*** ./node_modules/component-emitter/index.js ***!
|
||||
@@ -1922,23 +1799,31 @@ Emitter.prototype.hasListeners = function (event) {
|
||||
/*! no static exports found */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
/* WEBPACK VAR INJECTION */(function(process) {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); }
|
||||
|
||||
/* eslint-env browser */
|
||||
|
||||
/**
|
||||
* This is the web browser implementation of `debug()`.
|
||||
*/
|
||||
exports.log = log;
|
||||
exports.formatArgs = formatArgs;
|
||||
exports.save = save;
|
||||
exports.load = load;
|
||||
exports.useColors = useColors;
|
||||
exports.storage = localstorage();
|
||||
|
||||
exports.destroy = function () {
|
||||
var warned = false;
|
||||
return function () {
|
||||
if (!warned) {
|
||||
warned = true;
|
||||
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
|
||||
}
|
||||
};
|
||||
}();
|
||||
/**
|
||||
* Colors.
|
||||
*/
|
||||
|
||||
|
||||
exports.colors = ['#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33'];
|
||||
/**
|
||||
* Currently only WebKit-based Web Inspectors, Firefox >= v31,
|
||||
@@ -2007,20 +1892,16 @@ function formatArgs(args) {
|
||||
args.splice(lastC, 0, c);
|
||||
}
|
||||
/**
|
||||
* Invokes `console.log()` when available.
|
||||
* No-op when `console.log` is not a "function".
|
||||
* Invokes `console.debug()` when available.
|
||||
* No-op when `console.debug` is not a "function".
|
||||
* If `console.debug` is not available, falls back
|
||||
* to `console.log`.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
|
||||
function log() {
|
||||
var _console;
|
||||
|
||||
// This hackery is required for IE8/9, where
|
||||
// the `console.log` function doesn't have 'apply'
|
||||
return (typeof console === "undefined" ? "undefined" : _typeof(console)) === 'object' && console.log && (_console = console).log.apply(_console, arguments);
|
||||
}
|
||||
exports.log = console.debug || console.log || function () {};
|
||||
/**
|
||||
* Save `namespaces`.
|
||||
*
|
||||
@@ -2099,7 +1980,6 @@ formatters.j = function (v) {
|
||||
return '[UnexpectedJSONParseError]: ' + error.message;
|
||||
}
|
||||
};
|
||||
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../process/browser.js */ "./node_modules/process/browser.js")))
|
||||
|
||||
/***/ }),
|
||||
|
||||
@@ -2134,15 +2014,11 @@ function setup(env) {
|
||||
createDebug.enable = enable;
|
||||
createDebug.enabled = enabled;
|
||||
createDebug.humanize = __webpack_require__(/*! ms */ "./node_modules/ms/index.js");
|
||||
createDebug.destroy = destroy;
|
||||
Object.keys(env).forEach(function (key) {
|
||||
createDebug[key] = env[key];
|
||||
});
|
||||
/**
|
||||
* Active `debug` instances.
|
||||
*/
|
||||
|
||||
createDebug.instances = [];
|
||||
/**
|
||||
* The currently active debug mode names, and names to skip.
|
||||
*/
|
||||
|
||||
@@ -2184,6 +2060,7 @@ function setup(env) {
|
||||
|
||||
function createDebug(namespace) {
|
||||
var prevTime;
|
||||
var enableOverride = null;
|
||||
|
||||
function debug() {
|
||||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||
@@ -2215,7 +2092,7 @@ function setup(env) {
|
||||
args[0] = args[0].replace(/%([a-zA-Z%])/g, function (match, format) {
|
||||
// If we encounter an escaped % then don't increase the array index
|
||||
if (match === '%%') {
|
||||
return match;
|
||||
return '%';
|
||||
}
|
||||
|
||||
index++;
|
||||
@@ -2238,33 +2115,29 @@ function setup(env) {
|
||||
}
|
||||
|
||||
debug.namespace = namespace;
|
||||
debug.enabled = createDebug.enabled(namespace);
|
||||
debug.useColors = createDebug.useColors();
|
||||
debug.color = selectColor(namespace);
|
||||
debug.destroy = destroy;
|
||||
debug.extend = extend; // Debug.formatArgs = formatArgs;
|
||||
// debug.rawLog = rawLog;
|
||||
// env-specific initialization logic for debug instances
|
||||
debug.color = createDebug.selectColor(namespace);
|
||||
debug.extend = extend;
|
||||
debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.
|
||||
|
||||
Object.defineProperty(debug, 'enabled', {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get: function get() {
|
||||
return enableOverride === null ? createDebug.enabled(namespace) : enableOverride;
|
||||
},
|
||||
set: function set(v) {
|
||||
enableOverride = v;
|
||||
}
|
||||
}); // Env-specific initialization logic for debug instances
|
||||
|
||||
if (typeof createDebug.init === 'function') {
|
||||
createDebug.init(debug);
|
||||
}
|
||||
|
||||
createDebug.instances.push(debug);
|
||||
return debug;
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
var index = createDebug.instances.indexOf(this);
|
||||
|
||||
if (index !== -1) {
|
||||
createDebug.instances.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function extend(namespace, delimiter) {
|
||||
var newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
|
||||
newDebug.log = this.log;
|
||||
@@ -2301,11 +2174,6 @@ function setup(env) {
|
||||
createDebug.names.push(new RegExp('^' + namespaces + '$'));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < createDebug.instances.length; i++) {
|
||||
var instance = createDebug.instances[i];
|
||||
instance.enabled = createDebug.enabled(instance.namespace);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Disable debug output.
|
||||
@@ -2381,6 +2249,15 @@ function setup(env) {
|
||||
|
||||
return val;
|
||||
}
|
||||
/**
|
||||
* XXX DO NOT USE. This is a temporary stub function.
|
||||
* XXX It WILL be removed in the next major release.
|
||||
*/
|
||||
|
||||
|
||||
function destroy() {
|
||||
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
|
||||
}
|
||||
|
||||
createDebug.enable(createDebug.load());
|
||||
return createDebug;
|
||||
@@ -4418,7 +4295,7 @@ var WS = /*#__PURE__*/function (_Transport) {
|
||||
var uri = this.uri();
|
||||
var protocols = this.opts.protocols; // React Native only supports the 'headers' option, and will print a warning if anything else is passed
|
||||
|
||||
var opts = isReactNative ? {} : pick(this.opts, "agent", "perMessageDeflate", "pfx", "key", "passphrase", "cert", "ca", "ciphers", "rejectUnauthorized", "localAddress");
|
||||
var opts = isReactNative ? {} : pick(this.opts, "agent", "perMessageDeflate", "pfx", "key", "passphrase", "cert", "ca", "ciphers", "rejectUnauthorized", "localAddress", "protocolVersion", "origin", "maxPayload", "family", "checkServerIdentity");
|
||||
|
||||
if (this.opts.extraHeaders) {
|
||||
opts.headers = this.opts.extraHeaders;
|
||||
@@ -4626,7 +4503,10 @@ module.exports.pick = function (obj) {
|
||||
}
|
||||
|
||||
return attr.reduce(function (acc, k) {
|
||||
acc[k] = obj[k];
|
||||
if (obj.hasOwnProperty(k)) {
|
||||
acc[k] = obj[k];
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
@@ -4725,7 +4605,7 @@ var withNativeArrayBuffer = typeof ArrayBuffer === "function";
|
||||
var base64decoder;
|
||||
|
||||
if (withNativeArrayBuffer) {
|
||||
base64decoder = __webpack_require__(/*! base64-arraybuffer */ "./node_modules/base64-arraybuffer/lib/base64-arraybuffer.js");
|
||||
base64decoder = __webpack_require__(/*! base64-arraybuffer */ "./node_modules/engine.io-parser/node_modules/base64-arraybuffer/lib/base64-arraybuffer.js");
|
||||
}
|
||||
|
||||
var decodePacket = function decodePacket(encodedPacket, binaryType) {
|
||||
@@ -4897,6 +4777,82 @@ module.exports = {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/engine.io-parser/node_modules/base64-arraybuffer/lib/base64-arraybuffer.js":
|
||||
/*!*************************************************************************************************!*\
|
||||
!*** ./node_modules/engine.io-parser/node_modules/base64-arraybuffer/lib/base64-arraybuffer.js ***!
|
||||
\*************************************************************************************************/
|
||||
/*! no static exports found */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
/*
|
||||
* base64-arraybuffer
|
||||
* https://github.com/niklasvh/base64-arraybuffer
|
||||
*
|
||||
* Copyright (c) 2012 Niklas von Hertzen
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
(function (chars) {
|
||||
"use strict";
|
||||
|
||||
exports.encode = function (arraybuffer) {
|
||||
var bytes = new Uint8Array(arraybuffer),
|
||||
i,
|
||||
len = bytes.length,
|
||||
base64 = "";
|
||||
|
||||
for (i = 0; i < len; i += 3) {
|
||||
base64 += chars[bytes[i] >> 2];
|
||||
base64 += chars[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4];
|
||||
base64 += chars[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6];
|
||||
base64 += chars[bytes[i + 2] & 63];
|
||||
}
|
||||
|
||||
if (len % 3 === 2) {
|
||||
base64 = base64.substring(0, base64.length - 1) + "=";
|
||||
} else if (len % 3 === 1) {
|
||||
base64 = base64.substring(0, base64.length - 2) + "==";
|
||||
}
|
||||
|
||||
return base64;
|
||||
};
|
||||
|
||||
exports.decode = function (base64) {
|
||||
var bufferLength = base64.length * 0.75,
|
||||
len = base64.length,
|
||||
i,
|
||||
p = 0,
|
||||
encoded1,
|
||||
encoded2,
|
||||
encoded3,
|
||||
encoded4;
|
||||
|
||||
if (base64[base64.length - 1] === "=") {
|
||||
bufferLength--;
|
||||
|
||||
if (base64[base64.length - 2] === "=") {
|
||||
bufferLength--;
|
||||
}
|
||||
}
|
||||
|
||||
var arraybuffer = new ArrayBuffer(bufferLength),
|
||||
bytes = new Uint8Array(arraybuffer);
|
||||
|
||||
for (i = 0; i < len; i += 4) {
|
||||
encoded1 = chars.indexOf(base64[i]);
|
||||
encoded2 = chars.indexOf(base64[i + 1]);
|
||||
encoded3 = chars.indexOf(base64[i + 2]);
|
||||
encoded4 = chars.indexOf(base64[i + 3]);
|
||||
bytes[p++] = encoded1 << 2 | encoded2 >> 4;
|
||||
bytes[p++] = (encoded2 & 15) << 4 | encoded3 >> 2;
|
||||
bytes[p++] = (encoded3 & 3) << 6 | encoded4 & 63;
|
||||
}
|
||||
|
||||
return arraybuffer;
|
||||
};
|
||||
})("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/has-cors/index.js":
|
||||
/*!****************************************!*\
|
||||
!*** ./node_modules/has-cors/index.js ***!
|
||||
@@ -5233,224 +5189,6 @@ function queryKey(uri, query) {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/process/browser.js":
|
||||
/*!*****************************************!*\
|
||||
!*** ./node_modules/process/browser.js ***!
|
||||
\*****************************************/
|
||||
/*! no static exports found */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
// shim for using process in browser
|
||||
var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it
|
||||
// don't break things. But we need to wrap it in a try catch in case it is
|
||||
// wrapped in strict mode code which doesn't define any globals. It's inside a
|
||||
// function because try/catches deoptimize in certain engines.
|
||||
|
||||
var cachedSetTimeout;
|
||||
var cachedClearTimeout;
|
||||
|
||||
function defaultSetTimout() {
|
||||
throw new Error('setTimeout has not been defined');
|
||||
}
|
||||
|
||||
function defaultClearTimeout() {
|
||||
throw new Error('clearTimeout has not been defined');
|
||||
}
|
||||
|
||||
(function () {
|
||||
try {
|
||||
if (typeof setTimeout === 'function') {
|
||||
cachedSetTimeout = setTimeout;
|
||||
} else {
|
||||
cachedSetTimeout = defaultSetTimout;
|
||||
}
|
||||
} catch (e) {
|
||||
cachedSetTimeout = defaultSetTimout;
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof clearTimeout === 'function') {
|
||||
cachedClearTimeout = clearTimeout;
|
||||
} else {
|
||||
cachedClearTimeout = defaultClearTimeout;
|
||||
}
|
||||
} catch (e) {
|
||||
cachedClearTimeout = defaultClearTimeout;
|
||||
}
|
||||
})();
|
||||
|
||||
function runTimeout(fun) {
|
||||
if (cachedSetTimeout === setTimeout) {
|
||||
//normal enviroments in sane situations
|
||||
return setTimeout(fun, 0);
|
||||
} // if setTimeout wasn't available but was latter defined
|
||||
|
||||
|
||||
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
|
||||
cachedSetTimeout = setTimeout;
|
||||
return setTimeout(fun, 0);
|
||||
}
|
||||
|
||||
try {
|
||||
// when when somebody has screwed with setTimeout but no I.E. maddness
|
||||
return cachedSetTimeout(fun, 0);
|
||||
} catch (e) {
|
||||
try {
|
||||
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
||||
return cachedSetTimeout.call(null, fun, 0);
|
||||
} catch (e) {
|
||||
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
|
||||
return cachedSetTimeout.call(this, fun, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runClearTimeout(marker) {
|
||||
if (cachedClearTimeout === clearTimeout) {
|
||||
//normal enviroments in sane situations
|
||||
return clearTimeout(marker);
|
||||
} // if clearTimeout wasn't available but was latter defined
|
||||
|
||||
|
||||
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
|
||||
cachedClearTimeout = clearTimeout;
|
||||
return clearTimeout(marker);
|
||||
}
|
||||
|
||||
try {
|
||||
// when when somebody has screwed with setTimeout but no I.E. maddness
|
||||
return cachedClearTimeout(marker);
|
||||
} catch (e) {
|
||||
try {
|
||||
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
||||
return cachedClearTimeout.call(null, marker);
|
||||
} catch (e) {
|
||||
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
|
||||
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
|
||||
return cachedClearTimeout.call(this, marker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var queue = [];
|
||||
var draining = false;
|
||||
var currentQueue;
|
||||
var queueIndex = -1;
|
||||
|
||||
function cleanUpNextTick() {
|
||||
if (!draining || !currentQueue) {
|
||||
return;
|
||||
}
|
||||
|
||||
draining = false;
|
||||
|
||||
if (currentQueue.length) {
|
||||
queue = currentQueue.concat(queue);
|
||||
} else {
|
||||
queueIndex = -1;
|
||||
}
|
||||
|
||||
if (queue.length) {
|
||||
drainQueue();
|
||||
}
|
||||
}
|
||||
|
||||
function drainQueue() {
|
||||
if (draining) {
|
||||
return;
|
||||
}
|
||||
|
||||
var timeout = runTimeout(cleanUpNextTick);
|
||||
draining = true;
|
||||
var len = queue.length;
|
||||
|
||||
while (len) {
|
||||
currentQueue = queue;
|
||||
queue = [];
|
||||
|
||||
while (++queueIndex < len) {
|
||||
if (currentQueue) {
|
||||
currentQueue[queueIndex].run();
|
||||
}
|
||||
}
|
||||
|
||||
queueIndex = -1;
|
||||
len = queue.length;
|
||||
}
|
||||
|
||||
currentQueue = null;
|
||||
draining = false;
|
||||
runClearTimeout(timeout);
|
||||
}
|
||||
|
||||
process.nextTick = function (fun) {
|
||||
var args = new Array(arguments.length - 1);
|
||||
|
||||
if (arguments.length > 1) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
args[i - 1] = arguments[i];
|
||||
}
|
||||
}
|
||||
|
||||
queue.push(new Item(fun, args));
|
||||
|
||||
if (queue.length === 1 && !draining) {
|
||||
runTimeout(drainQueue);
|
||||
}
|
||||
}; // v8 likes predictible objects
|
||||
|
||||
|
||||
function Item(fun, array) {
|
||||
this.fun = fun;
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
Item.prototype.run = function () {
|
||||
this.fun.apply(null, this.array);
|
||||
};
|
||||
|
||||
process.title = 'browser';
|
||||
process.browser = true;
|
||||
process.env = {};
|
||||
process.argv = [];
|
||||
process.version = ''; // empty string to avoid regexp issues
|
||||
|
||||
process.versions = {};
|
||||
|
||||
function noop() {}
|
||||
|
||||
process.on = noop;
|
||||
process.addListener = noop;
|
||||
process.once = noop;
|
||||
process.off = noop;
|
||||
process.removeListener = noop;
|
||||
process.removeAllListeners = noop;
|
||||
process.emit = noop;
|
||||
process.prependListener = noop;
|
||||
process.prependOnceListener = noop;
|
||||
|
||||
process.listeners = function (name) {
|
||||
return [];
|
||||
};
|
||||
|
||||
process.binding = function (name) {
|
||||
throw new Error('process.binding is not supported');
|
||||
};
|
||||
|
||||
process.cwd = function () {
|
||||
return '/';
|
||||
};
|
||||
|
||||
process.chdir = function (dir) {
|
||||
throw new Error('process.chdir is not supported');
|
||||
};
|
||||
|
||||
process.umask = function () {
|
||||
return 0;
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/socket.io-parser/dist/binary.js":
|
||||
/*!******************************************************!*\
|
||||
!*** ./node_modules/socket.io-parser/dist/binary.js ***!
|
||||
@@ -5889,7 +5627,7 @@ var Decoder = /*#__PURE__*/function (_Emitter) {
|
||||
|
||||
case PacketType.EVENT:
|
||||
case PacketType.BINARY_EVENT:
|
||||
return Array.isArray(payload) && typeof payload[0] === "string";
|
||||
return Array.isArray(payload) && payload.length > 0;
|
||||
|
||||
case PacketType.ACK:
|
||||
case PacketType.BINARY_ACK:
|
||||
|
||||
File diff suppressed because one or more lines are too long
6
client-dist/socket.io.min.js
vendored
6
client-dist/socket.io.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6
client-dist/socket.io.msgpack.min.js
vendored
6
client-dist/socket.io.msgpack.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
17
examples/angular-todomvc/.browserslistrc
Normal file
17
examples/angular-todomvc/.browserslistrc
Normal 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.
|
||||
16
examples/angular-todomvc/.editorconfig
Normal file
16
examples/angular-todomvc/.editorconfig
Normal 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
|
||||
46
examples/angular-todomvc/.gitignore
vendored
Normal file
46
examples/angular-todomvc/.gitignore
vendored
Normal 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
|
||||
35
examples/angular-todomvc/README.md
Normal file
35
examples/angular-todomvc/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# 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).
|
||||
|
||||

|
||||
|
||||
## 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.
|
||||
|
||||
## Socket.IO server
|
||||
|
||||
Run `npm run start:server` to start the Socket.IO server.
|
||||
|
||||
## 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.
|
||||
128
examples/angular-todomvc/angular.json
Normal file
128
examples/angular-todomvc/angular.json
Normal 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"
|
||||
}
|
||||
BIN
examples/angular-todomvc/assets/demo.gif
Normal file
BIN
examples/angular-todomvc/assets/demo.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 205 KiB |
37
examples/angular-todomvc/e2e/protractor.conf.js
Normal file
37
examples/angular-todomvc/e2e/protractor.conf.js
Normal 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
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
||||
23
examples/angular-todomvc/e2e/src/app.e2e-spec.ts
Normal file
23
examples/angular-todomvc/e2e/src/app.e2e-spec.ts
Normal 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));
|
||||
});
|
||||
});
|
||||
11
examples/angular-todomvc/e2e/src/app.po.ts
Normal file
11
examples/angular-todomvc/e2e/src/app.po.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
13
examples/angular-todomvc/e2e/tsconfig.json
Normal file
13
examples/angular-todomvc/e2e/tsconfig.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
44
examples/angular-todomvc/karma.conf.js
Normal file
44
examples/angular-todomvc/karma.conf.js
Normal 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
|
||||
});
|
||||
};
|
||||
13694
examples/angular-todomvc/package-lock.json
generated
Normal file
13694
examples/angular-todomvc/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
48
examples/angular-todomvc/package.json
Normal file
48
examples/angular-todomvc/package.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"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",
|
||||
"start:server": "ts-node -O '{\"module\":\"commonjs\"}' server.ts"
|
||||
},
|
||||
"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": "^3.0.4",
|
||||
"socket.io-client": "^3.0.4",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
28
examples/angular-todomvc/server.ts
Normal file
28
examples/angular-todomvc/server.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Server, Socket } from "socket.io";
|
||||
|
||||
const io = new Server(8080, {
|
||||
cors: {
|
||||
origin: "http://localhost:4200",
|
||||
methods: ["GET", "POST"]
|
||||
}
|
||||
});
|
||||
|
||||
interface Todo {
|
||||
completed: boolean;
|
||||
editing: boolean;
|
||||
title: string;
|
||||
}
|
||||
|
||||
let todos: Array<Todo> = [];
|
||||
|
||||
io.on("connect", (socket: Socket) => {
|
||||
socket.emit("todos", todos);
|
||||
|
||||
// note: we could also create a CRUD (create/read/update/delete) service for the todo list
|
||||
socket.on("update-store", (updatedTodos) => {
|
||||
// store it locally
|
||||
todos = updatedTodos;
|
||||
// broadcast to everyone but the sender
|
||||
socket.broadcast.emit("todos", todos);
|
||||
});
|
||||
});
|
||||
0
examples/angular-todomvc/src/app/app.component.css
Normal file
0
examples/angular-todomvc/src/app/app.component.css
Normal file
23
examples/angular-todomvc/src/app/app.component.html
Normal file
23
examples/angular-todomvc/src/app/app.component.html
Normal 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>
|
||||
31
examples/angular-todomvc/src/app/app.component.spec.ts
Normal file
31
examples/angular-todomvc/src/app/app.component.spec.ts
Normal 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!');
|
||||
});
|
||||
});
|
||||
59
examples/angular-todomvc/src/app/app.component.ts
Normal file
59
examples/angular-todomvc/src/app/app.component.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { RemoteTodoStore, Todo } from './store';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
todoStore: RemoteTodoStore;
|
||||
newTodoText = '';
|
||||
|
||||
constructor(todoStore: RemoteTodoStore) {
|
||||
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 = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
19
examples/angular-todomvc/src/app/app.module.ts
Normal file
19
examples/angular-todomvc/src/app/app.module.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { RemoteTodoStore } from './store';
|
||||
import { FormsModule } from "@angular/forms";
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule
|
||||
],
|
||||
providers: [RemoteTodoStore],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
95
examples/angular-todomvc/src/app/store.ts
Normal file
95
examples/angular-todomvc/src/app/store.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { io, Socket } from "socket.io-client";
|
||||
|
||||
export class Todo {
|
||||
completed: boolean;
|
||||
editing: boolean;
|
||||
|
||||
private _title: String = "";
|
||||
get title() {
|
||||
return this._title;
|
||||
}
|
||||
set title(value: String) {
|
||||
this._title = value.trim();
|
||||
}
|
||||
|
||||
constructor(title: String) {
|
||||
this.completed = false;
|
||||
this.editing = false;
|
||||
this.title = title.trim();
|
||||
}
|
||||
}
|
||||
|
||||
export class TodoStore {
|
||||
todos: Array<Todo>;
|
||||
|
||||
constructor() {
|
||||
let persistedTodos = JSON.parse(localStorage.getItem('angular2-todos') || '[]');
|
||||
// Normalize back into classes
|
||||
this.todos = persistedTodos.map( (todo: {_title: String, completed: boolean}) => {
|
||||
let ret = new Todo(todo._title);
|
||||
ret.completed = todo.completed;
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
||||
protected updateStore() {
|
||||
localStorage.setItem('angular2-todos', JSON.stringify(this.todos));
|
||||
}
|
||||
|
||||
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((t: Todo) => t.completed = completed);
|
||||
this.updateStore();
|
||||
}
|
||||
|
||||
removeCompleted() {
|
||||
this.todos = this.getWithCompleted(false);
|
||||
this.updateStore();
|
||||
}
|
||||
|
||||
getRemaining() {
|
||||
return this.getWithCompleted(false);
|
||||
}
|
||||
|
||||
getCompleted() {
|
||||
return this.getWithCompleted(true);
|
||||
}
|
||||
|
||||
toggleCompletion(todo: Todo) {
|
||||
todo.completed = !todo.completed;
|
||||
this.updateStore();
|
||||
}
|
||||
|
||||
remove(todo: Todo) {
|
||||
this.todos.splice(this.todos.indexOf(todo), 1);
|
||||
this.updateStore();
|
||||
}
|
||||
|
||||
add(title: String) {
|
||||
this.todos.push(new Todo(title));
|
||||
this.updateStore();
|
||||
}
|
||||
}
|
||||
|
||||
export class RemoteTodoStore extends TodoStore {
|
||||
private socket: Socket;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.socket = io("http://localhost:8080");
|
||||
this.socket.on("todos", (updatedTodos: Array<Todo>) => {
|
||||
this.todos = updatedTodos;
|
||||
});
|
||||
}
|
||||
|
||||
protected updateStore() {
|
||||
this.socket.emit("update-store", this.todos.map(({ title, editing, completed }) => ({ title, editing, completed })));
|
||||
}
|
||||
}
|
||||
0
examples/angular-todomvc/src/assets/.gitkeep
Normal file
0
examples/angular-todomvc/src/assets/.gitkeep
Normal file
@@ -0,0 +1,3 @@
|
||||
export const environment = {
|
||||
production: true
|
||||
};
|
||||
16
examples/angular-todomvc/src/environments/environment.ts
Normal file
16
examples/angular-todomvc/src/environments/environment.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// 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
|
||||
};
|
||||
|
||||
/*
|
||||
* 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.
|
||||
BIN
examples/angular-todomvc/src/favicon.ico
Normal file
BIN
examples/angular-todomvc/src/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 948 B |
13
examples/angular-todomvc/src/index.html
Normal file
13
examples/angular-todomvc/src/index.html
Normal 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>
|
||||
12
examples/angular-todomvc/src/main.ts
Normal file
12
examples/angular-todomvc/src/main.ts
Normal 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));
|
||||
63
examples/angular-todomvc/src/polyfills.ts
Normal file
63
examples/angular-todomvc/src/polyfills.ts
Normal 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
|
||||
*/
|
||||
381
examples/angular-todomvc/src/styles.css
Normal file
381
examples/angular-todomvc/src/styles.css
Normal 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;
|
||||
}
|
||||
}
|
||||
25
examples/angular-todomvc/src/test.ts
Normal file
25
examples/angular-todomvc/src/test.ts
Normal 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);
|
||||
15
examples/angular-todomvc/tsconfig.app.json
Normal file
15
examples/angular-todomvc/tsconfig.app.json
Normal 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"
|
||||
]
|
||||
}
|
||||
29
examples/angular-todomvc/tsconfig.json
Normal file
29
examples/angular-todomvc/tsconfig.json
Normal 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
|
||||
}
|
||||
}
|
||||
18
examples/angular-todomvc/tsconfig.spec.json
Normal file
18
examples/angular-todomvc/tsconfig.spec.json
Normal 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"
|
||||
]
|
||||
}
|
||||
152
examples/angular-todomvc/tslint.json
Normal file
152
examples/angular-todomvc/tslint.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,36 @@
|
||||
$(function() {
|
||||
var FADE_TIME = 150; // ms
|
||||
var TYPING_TIMER_LENGTH = 400; // ms
|
||||
var COLORS = [
|
||||
const FADE_TIME = 150; // ms
|
||||
const TYPING_TIMER_LENGTH = 400; // ms
|
||||
const COLORS = [
|
||||
'#e21400', '#91580f', '#f8a700', '#f78b00',
|
||||
'#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
|
||||
'#3b88eb', '#3824aa', '#a700ff', '#d300e7'
|
||||
];
|
||||
|
||||
// Initialize variables
|
||||
var $window = $(window);
|
||||
var $usernameInput = $('.usernameInput'); // Input for username
|
||||
var $messages = $('.messages'); // Messages area
|
||||
var $inputMessage = $('.inputMessage'); // Input message input box
|
||||
const $window = $(window);
|
||||
const $usernameInput = $('.usernameInput'); // Input for username
|
||||
const $messages = $('.messages'); // Messages area
|
||||
const $inputMessage = $('.inputMessage'); // Input message input box
|
||||
|
||||
var $loginPage = $('.login.page'); // The login page
|
||||
var $chatPage = $('.chat.page'); // The chatroom page
|
||||
const $loginPage = $('.login.page'); // The login page
|
||||
const $chatPage = $('.chat.page'); // The chatroom page
|
||||
|
||||
const socket = io();
|
||||
|
||||
// Prompt for setting a username
|
||||
var username;
|
||||
var connected = false;
|
||||
var typing = false;
|
||||
var lastTypingTime;
|
||||
var $currentInput = $usernameInput.focus();
|
||||
|
||||
var socket = io();
|
||||
let username;
|
||||
let connected = false;
|
||||
let typing = false;
|
||||
let lastTypingTime;
|
||||
let $currentInput = $usernameInput.focus();
|
||||
|
||||
const addParticipantsMessage = (data) => {
|
||||
var message = '';
|
||||
let message = '';
|
||||
if (data.numUsers === 1) {
|
||||
message += "there's 1 participant";
|
||||
message += `there's 1 participant`;
|
||||
} else {
|
||||
message += "there are " + data.numUsers + " participants";
|
||||
message += `there are ${data.numUsers} participants`;
|
||||
}
|
||||
log(message);
|
||||
}
|
||||
@@ -53,45 +53,41 @@ $(function() {
|
||||
|
||||
// Sends a chat message
|
||||
const sendMessage = () => {
|
||||
var message = $inputMessage.val();
|
||||
let message = $inputMessage.val();
|
||||
// Prevent markup from being injected into the message
|
||||
message = cleanInput(message);
|
||||
// if there is a non-empty message and a socket connection
|
||||
if (message && connected) {
|
||||
$inputMessage.val('');
|
||||
addChatMessage({
|
||||
username: username,
|
||||
message: message
|
||||
});
|
||||
addChatMessage({ username, message });
|
||||
// tell server to execute 'new message' and send along one parameter
|
||||
socket.emit('new message', message);
|
||||
}
|
||||
}
|
||||
|
||||
// Log a message
|
||||
const log = (message, options) => {
|
||||
var $el = $('<li>').addClass('log').text(message);
|
||||
const log = (message, options) => {
|
||||
const $el = $('<li>').addClass('log').text(message);
|
||||
addMessageElement($el, options);
|
||||
}
|
||||
|
||||
// Adds the visual chat message to the message list
|
||||
const addChatMessage = (data, options) => {
|
||||
// Don't fade the message in if there is an 'X was typing'
|
||||
var $typingMessages = getTypingMessages(data);
|
||||
options = options || {};
|
||||
const $typingMessages = getTypingMessages(data);
|
||||
if ($typingMessages.length !== 0) {
|
||||
options.fade = false;
|
||||
$typingMessages.remove();
|
||||
}
|
||||
|
||||
var $usernameDiv = $('<span class="username"/>')
|
||||
const $usernameDiv = $('<span class="username"/>')
|
||||
.text(data.username)
|
||||
.css('color', getUsernameColor(data.username));
|
||||
var $messageBodyDiv = $('<span class="messageBody">')
|
||||
const $messageBodyDiv = $('<span class="messageBody">')
|
||||
.text(data.message);
|
||||
|
||||
var typingClass = data.typing ? 'typing' : '';
|
||||
var $messageDiv = $('<li class="message"/>')
|
||||
const typingClass = data.typing ? 'typing' : '';
|
||||
const $messageDiv = $('<li class="message"/>')
|
||||
.data('username', data.username)
|
||||
.addClass(typingClass)
|
||||
.append($usernameDiv, $messageBodyDiv);
|
||||
@@ -119,8 +115,7 @@ $(function() {
|
||||
// options.prepend - If the element should prepend
|
||||
// all other messages (default = false)
|
||||
const addMessageElement = (el, options) => {
|
||||
var $el = $(el);
|
||||
|
||||
const $el = $(el);
|
||||
// Setup default options
|
||||
if (!options) {
|
||||
options = {};
|
||||
@@ -141,6 +136,7 @@ $(function() {
|
||||
} else {
|
||||
$messages.append($el);
|
||||
}
|
||||
|
||||
$messages[0].scrollTop = $messages[0].scrollHeight;
|
||||
}
|
||||
|
||||
@@ -159,8 +155,8 @@ $(function() {
|
||||
lastTypingTime = (new Date()).getTime();
|
||||
|
||||
setTimeout(() => {
|
||||
var typingTimer = (new Date()).getTime();
|
||||
var timeDiff = typingTimer - lastTypingTime;
|
||||
const typingTimer = (new Date()).getTime();
|
||||
const timeDiff = typingTimer - lastTypingTime;
|
||||
if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
|
||||
socket.emit('stop typing');
|
||||
typing = false;
|
||||
@@ -179,12 +175,12 @@ $(function() {
|
||||
// Gets the color of a username through our hash function
|
||||
const getUsernameColor = (username) => {
|
||||
// Compute hash code
|
||||
var hash = 7;
|
||||
for (var i = 0; i < username.length; i++) {
|
||||
hash = username.charCodeAt(i) + (hash << 5) - hash;
|
||||
let hash = 7;
|
||||
for (let i = 0; i < username.length; i++) {
|
||||
hash = username.charCodeAt(i) + (hash << 5) - hash;
|
||||
}
|
||||
// Calculate color
|
||||
var index = Math.abs(hash % COLORS.length);
|
||||
const index = Math.abs(hash % COLORS.length);
|
||||
return COLORS[index];
|
||||
}
|
||||
|
||||
@@ -229,7 +225,7 @@ $(function() {
|
||||
socket.on('login', (data) => {
|
||||
connected = true;
|
||||
// Display the welcome message
|
||||
var message = "Welcome to Socket.IO Chat – ";
|
||||
const message = 'Welcome to Socket.IO Chat – ';
|
||||
log(message, {
|
||||
prepend: true
|
||||
});
|
||||
@@ -243,13 +239,13 @@ $(function() {
|
||||
|
||||
// Whenever the server emits 'user joined', log it in the chat body
|
||||
socket.on('user joined', (data) => {
|
||||
log(data.username + ' joined');
|
||||
log(`${data.username} joined`);
|
||||
addParticipantsMessage(data);
|
||||
});
|
||||
|
||||
// Whenever the server emits 'user left', log it in the chat body
|
||||
socket.on('user left', (data) => {
|
||||
log(data.username + ' left');
|
||||
log(`${data.username} left`);
|
||||
addParticipantsMessage(data);
|
||||
removeChatTyping(data);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM mhart/alpine-node:6
|
||||
FROM node:14-alpine
|
||||
|
||||
# Create app directory
|
||||
RUN mkdir -p /usr/src/app
|
||||
@@ -6,7 +6,7 @@ WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
COPY package.json /usr/src/app/
|
||||
RUN npm install
|
||||
RUN npm install --prod
|
||||
|
||||
# Bundle app source
|
||||
COPY . /usr/src/app
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
"license": "BSD",
|
||||
"dependencies": {
|
||||
"express": "4.13.4",
|
||||
"socket.io": "^1.7.2",
|
||||
"socket.io-redis": "^3.0.0"
|
||||
"socket.io": "^3.1.0",
|
||||
"socket.io-redis": "^6.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node index.js"
|
||||
|
||||
@@ -3,6 +3,8 @@ Listen 80
|
||||
|
||||
ServerName localhost
|
||||
|
||||
LoadModule mpm_event_module modules/mod_mpm_event.so
|
||||
|
||||
LoadModule authn_file_module modules/mod_authn_file.so
|
||||
LoadModule authn_core_module modules/mod_authn_core.so
|
||||
LoadModule authz_host_module modules/mod_authz_host.so
|
||||
|
||||
@@ -6,7 +6,7 @@ WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
COPY package.json /usr/src/app/
|
||||
RUN npm install
|
||||
RUN npm install --prod
|
||||
|
||||
# Bundle app source
|
||||
COPY . /usr/src/app
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
"license": "BSD",
|
||||
"dependencies": {
|
||||
"express": "4.13.4",
|
||||
"socket.io": "^1.7.2",
|
||||
"socket.io-redis": "^3.0.0"
|
||||
"socket.io": "^3.1.0",
|
||||
"socket.io-redis": "^6.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node index.js"
|
||||
|
||||
@@ -22,6 +22,16 @@ Each node connects to the redis backend, which will enable to broadcast to every
|
||||
$ docker-compose stop server-george
|
||||
```
|
||||
|
||||
A `client` container is included in the `docker-compose.yml` file, in order to test the routing.
|
||||
|
||||
You can create additional `client` containers with:
|
||||
|
||||
```
|
||||
$ docker-compose up -d --scale=client=10 client
|
||||
# and then
|
||||
$ docker-compose logs client
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Multiple users can join a chat room by each entering a unique username
|
||||
|
||||
15
examples/cluster-nginx/client/Dockerfile
Normal file
15
examples/cluster-nginx/client/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM node:14-alpine
|
||||
|
||||
# Create app directory
|
||||
RUN mkdir -p /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
COPY package.json /usr/src/app/
|
||||
RUN npm install --prod
|
||||
|
||||
# Bundle app source
|
||||
COPY . /usr/src/app
|
||||
|
||||
EXPOSE 3000
|
||||
CMD [ "npm", "start" ]
|
||||
13
examples/cluster-nginx/client/index.js
Normal file
13
examples/cluster-nginx/client/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const socket = require('socket.io-client')('ws://nginx');
|
||||
|
||||
socket.on('connect', () => {
|
||||
console.log('connected');
|
||||
});
|
||||
|
||||
socket.on('my-name-is', (serverName) => {
|
||||
console.log(`connected to ${serverName}`);
|
||||
});
|
||||
|
||||
socket.on('disconnect', (reason) => {
|
||||
console.log(`disconnected due to ${reason}`);
|
||||
});
|
||||
15
examples/cluster-nginx/client/package.json
Normal file
15
examples/cluster-nginx/client/package.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "socket.io-chat",
|
||||
"version": "0.0.0",
|
||||
"description": "A simple chat client using socket.io",
|
||||
"main": "index.js",
|
||||
"author": "Grant Timmerman",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"socket.io-client": "^3.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node index.js"
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,11 @@ server-ringo:
|
||||
environment:
|
||||
- NAME=Ringo
|
||||
|
||||
client:
|
||||
build: ./client
|
||||
links:
|
||||
- nginx
|
||||
|
||||
redis:
|
||||
image: redis:alpine
|
||||
expose:
|
||||
|
||||
@@ -24,8 +24,12 @@ http {
|
||||
}
|
||||
|
||||
upstream nodes {
|
||||
# enable sticky session
|
||||
ip_hash;
|
||||
# enable sticky session with either "hash" (uses the complete IP address)
|
||||
hash $remote_addr consistent;
|
||||
# or "ip_hash" (uses the first three octets of the client IPv4 address, or the entire IPv6 address)
|
||||
# ip_hash;
|
||||
# or "sticky" (needs commercial subscription)
|
||||
# sticky cookie srv_id expires=1h domain=.example.com path=/;
|
||||
|
||||
server server-john:3000;
|
||||
server server-paul:3000;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM mhart/alpine-node:6
|
||||
FROM node:14-alpine
|
||||
|
||||
# Create app directory
|
||||
RUN mkdir -p /usr/src/app
|
||||
@@ -6,7 +6,7 @@ WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
COPY package.json /usr/src/app/
|
||||
RUN npm install
|
||||
RUN npm install --prod
|
||||
|
||||
# Bundle app source
|
||||
COPY . /usr/src/app
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"express": "4.13.4",
|
||||
"socket.io": "^1.7.2",
|
||||
"socket.io-redis": "^3.0.0"
|
||||
"socket.io": "^3.1.0",
|
||||
"socket.io-redis": "^6.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node index.js"
|
||||
|
||||
22
examples/cluster-traefik/README.md
Normal file
22
examples/cluster-traefik/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
# Socket.IO Chat with traefik & [redis](https://redis.io/)
|
||||
|
||||
A simple chat demo for Socket.IO
|
||||
|
||||
## How to use
|
||||
|
||||
Install [Docker Compose](https://docs.docker.com/compose/install/), then:
|
||||
|
||||
```
|
||||
$ docker-compose up -d
|
||||
```
|
||||
|
||||
And then point your browser to `http://localhost:3000`.
|
||||
|
||||
You can then scale the server to multiple instances:
|
||||
|
||||
```
|
||||
$ docker-compose up -d --scale=server=7
|
||||
```
|
||||
|
||||
The session stickiness, which is [required](https://socket.io/docs/v3/using-multiple-nodes/) when using multiple Socket.IO server instances, is achieved with a cookie. More information [here](https://doc.traefik.io/traefik/v2.0/routing/services/#sticky-sessions).
|
||||
27
examples/cluster-traefik/docker-compose.yml
Normal file
27
examples/cluster-traefik/docker-compose.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:2.4
|
||||
volumes:
|
||||
- ./traefik.yml:/etc/traefik/traefik.yml
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
links:
|
||||
- server
|
||||
ports:
|
||||
- "3000:80"
|
||||
- "8080:8080"
|
||||
|
||||
server:
|
||||
build: ./server
|
||||
links:
|
||||
- redis
|
||||
labels:
|
||||
- "traefik.http.routers.chat.rule=PathPrefix(`/`)"
|
||||
- traefik.http.services.chat.loadBalancer.sticky.cookie.name=server_id
|
||||
- traefik.http.services.chat.loadBalancer.sticky.cookie.httpOnly=true
|
||||
|
||||
redis:
|
||||
image: redis:6-alpine
|
||||
labels:
|
||||
- traefik.enable=false
|
||||
15
examples/cluster-traefik/server/Dockerfile
Normal file
15
examples/cluster-traefik/server/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM node:14-alpine
|
||||
|
||||
# Create app directory
|
||||
RUN mkdir -p /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
COPY package.json /usr/src/app/
|
||||
RUN npm install --prod
|
||||
|
||||
# Bundle app source
|
||||
COPY . /usr/src/app
|
||||
|
||||
EXPOSE 3000
|
||||
CMD [ "npm", "start" ]
|
||||
83
examples/cluster-traefik/server/index.js
Normal file
83
examples/cluster-traefik/server/index.js
Normal file
@@ -0,0 +1,83 @@
|
||||
// Setup basic express server
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
var server = require('http').createServer(app);
|
||||
var io = require('socket.io')(server);
|
||||
var redis = require('socket.io-redis');
|
||||
var port = process.env.PORT || 3000;
|
||||
var crypto = require('crypto');
|
||||
var serverName = crypto.randomBytes(3).toString('hex');
|
||||
|
||||
io.adapter(redis({ host: 'redis', port: 6379 }));
|
||||
|
||||
server.listen(port, function () {
|
||||
console.log('Server listening at port %d', port);
|
||||
console.log('Hello, I\'m %s, how can I help?', serverName);
|
||||
});
|
||||
|
||||
// Routing
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// Chatroom
|
||||
|
||||
var numUsers = 0;
|
||||
|
||||
io.on('connection', function (socket) {
|
||||
socket.emit('my-name-is', serverName);
|
||||
|
||||
var addedUser = false;
|
||||
|
||||
// when the client emits 'new message', this listens and executes
|
||||
socket.on('new message', function (data) {
|
||||
// we tell the client to execute 'new message'
|
||||
socket.broadcast.emit('new message', {
|
||||
username: socket.username,
|
||||
message: data
|
||||
});
|
||||
});
|
||||
|
||||
// when the client emits 'add user', this listens and executes
|
||||
socket.on('add user', function (username) {
|
||||
if (addedUser) return;
|
||||
|
||||
// we store the username in the socket session for this client
|
||||
socket.username = username;
|
||||
++numUsers;
|
||||
addedUser = true;
|
||||
socket.emit('login', {
|
||||
numUsers: numUsers
|
||||
});
|
||||
// echo globally (all clients) that a person has connected
|
||||
socket.broadcast.emit('user joined', {
|
||||
username: socket.username,
|
||||
numUsers: numUsers
|
||||
});
|
||||
});
|
||||
|
||||
// when the client emits 'typing', we broadcast it to others
|
||||
socket.on('typing', function () {
|
||||
socket.broadcast.emit('typing', {
|
||||
username: socket.username
|
||||
});
|
||||
});
|
||||
|
||||
// when the client emits 'stop typing', we broadcast it to others
|
||||
socket.on('stop typing', function () {
|
||||
socket.broadcast.emit('stop typing', {
|
||||
username: socket.username
|
||||
});
|
||||
});
|
||||
|
||||
// when the user disconnects.. perform this
|
||||
socket.on('disconnect', function () {
|
||||
if (addedUser) {
|
||||
--numUsers;
|
||||
|
||||
// echo globally that this client has left
|
||||
socket.broadcast.emit('user left', {
|
||||
username: socket.username,
|
||||
numUsers: numUsers
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
17
examples/cluster-traefik/server/package.json
Normal file
17
examples/cluster-traefik/server/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "socket.io-chat",
|
||||
"version": "0.0.0",
|
||||
"description": "A simple chat client using socket.io",
|
||||
"main": "index.js",
|
||||
"author": "Grant Timmerman",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"express": "4.13.4",
|
||||
"socket.io": "^3.1.0",
|
||||
"socket.io-redis": "^6.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node index.js"
|
||||
}
|
||||
}
|
||||
28
examples/cluster-traefik/server/public/index.html
Normal file
28
examples/cluster-traefik/server/public/index.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Socket.IO Chat Example</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<ul class="pages">
|
||||
<li class="chat page">
|
||||
<div class="chatArea">
|
||||
<ul class="messages"></ul>
|
||||
</div>
|
||||
<input class="inputMessage" placeholder="Type here..."/>
|
||||
</li>
|
||||
<li class="login page">
|
||||
<div class="form">
|
||||
<h3 class="title">What's your nickname?</h3>
|
||||
<input class="usernameInput" type="text" maxlength="14" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script src="/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
286
examples/cluster-traefik/server/public/main.js
Normal file
286
examples/cluster-traefik/server/public/main.js
Normal file
@@ -0,0 +1,286 @@
|
||||
$(function() {
|
||||
var FADE_TIME = 150; // ms
|
||||
var TYPING_TIMER_LENGTH = 400; // ms
|
||||
var COLORS = [
|
||||
'#e21400', '#91580f', '#f8a700', '#f78b00',
|
||||
'#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
|
||||
'#3b88eb', '#3824aa', '#a700ff', '#d300e7'
|
||||
];
|
||||
|
||||
// Initialize variables
|
||||
var $window = $(window);
|
||||
var $usernameInput = $('.usernameInput'); // Input for username
|
||||
var $messages = $('.messages'); // Messages area
|
||||
var $inputMessage = $('.inputMessage'); // Input message input box
|
||||
|
||||
var $loginPage = $('.login.page'); // The login page
|
||||
var $chatPage = $('.chat.page'); // The chatroom page
|
||||
|
||||
// Prompt for setting a username
|
||||
var username;
|
||||
var connected = false;
|
||||
var typing = false;
|
||||
var lastTypingTime;
|
||||
var $currentInput = $usernameInput.focus();
|
||||
|
||||
var socket = io();
|
||||
|
||||
function addParticipantsMessage (data) {
|
||||
var message = '';
|
||||
if (data.numUsers === 1) {
|
||||
message += "there's 1 participant";
|
||||
} else {
|
||||
message += "there are " + data.numUsers + " participants";
|
||||
}
|
||||
log(message);
|
||||
}
|
||||
|
||||
// Sets the client's username
|
||||
function setUsername () {
|
||||
username = cleanInput($usernameInput.val().trim());
|
||||
|
||||
// If the username is valid
|
||||
if (username) {
|
||||
$loginPage.fadeOut();
|
||||
$chatPage.show();
|
||||
$loginPage.off('click');
|
||||
$currentInput = $inputMessage.focus();
|
||||
|
||||
// Tell the server your username
|
||||
socket.emit('add user', username);
|
||||
}
|
||||
}
|
||||
|
||||
// Sends a chat message
|
||||
function sendMessage () {
|
||||
var message = $inputMessage.val();
|
||||
// Prevent markup from being injected into the message
|
||||
message = cleanInput(message);
|
||||
// if there is a non-empty message and a socket connection
|
||||
if (message && connected) {
|
||||
$inputMessage.val('');
|
||||
addChatMessage({
|
||||
username: username,
|
||||
message: message
|
||||
});
|
||||
// tell server to execute 'new message' and send along one parameter
|
||||
socket.emit('new message', message);
|
||||
}
|
||||
}
|
||||
|
||||
// Log a message
|
||||
function log (message, options) {
|
||||
var $el = $('<li>').addClass('log').text(message);
|
||||
addMessageElement($el, options);
|
||||
}
|
||||
|
||||
// Adds the visual chat message to the message list
|
||||
function addChatMessage (data, options) {
|
||||
// Don't fade the message in if there is an 'X was typing'
|
||||
var $typingMessages = getTypingMessages(data);
|
||||
options = options || {};
|
||||
if ($typingMessages.length !== 0) {
|
||||
options.fade = false;
|
||||
$typingMessages.remove();
|
||||
}
|
||||
|
||||
var $usernameDiv = $('<span class="username"/>')
|
||||
.text(data.username)
|
||||
.css('color', getUsernameColor(data.username));
|
||||
var $messageBodyDiv = $('<span class="messageBody">')
|
||||
.text(data.message);
|
||||
|
||||
var typingClass = data.typing ? 'typing' : '';
|
||||
var $messageDiv = $('<li class="message"/>')
|
||||
.data('username', data.username)
|
||||
.addClass(typingClass)
|
||||
.append($usernameDiv, $messageBodyDiv);
|
||||
|
||||
addMessageElement($messageDiv, options);
|
||||
}
|
||||
|
||||
// Adds the visual chat typing message
|
||||
function addChatTyping (data) {
|
||||
data.typing = true;
|
||||
data.message = 'is typing';
|
||||
addChatMessage(data);
|
||||
}
|
||||
|
||||
// Removes the visual chat typing message
|
||||
function removeChatTyping (data) {
|
||||
getTypingMessages(data).fadeOut(function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
|
||||
// Adds a message element to the messages and scrolls to the bottom
|
||||
// el - The element to add as a message
|
||||
// options.fade - If the element should fade-in (default = true)
|
||||
// options.prepend - If the element should prepend
|
||||
// all other messages (default = false)
|
||||
function addMessageElement (el, options) {
|
||||
var $el = $(el);
|
||||
|
||||
// Setup default options
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
if (typeof options.fade === 'undefined') {
|
||||
options.fade = true;
|
||||
}
|
||||
if (typeof options.prepend === 'undefined') {
|
||||
options.prepend = false;
|
||||
}
|
||||
|
||||
// Apply options
|
||||
if (options.fade) {
|
||||
$el.hide().fadeIn(FADE_TIME);
|
||||
}
|
||||
if (options.prepend) {
|
||||
$messages.prepend($el);
|
||||
} else {
|
||||
$messages.append($el);
|
||||
}
|
||||
$messages[0].scrollTop = $messages[0].scrollHeight;
|
||||
}
|
||||
|
||||
// Prevents input from having injected markup
|
||||
function cleanInput (input) {
|
||||
return $('<div/>').text(input).text();
|
||||
}
|
||||
|
||||
// Updates the typing event
|
||||
function updateTyping () {
|
||||
if (connected) {
|
||||
if (!typing) {
|
||||
typing = true;
|
||||
socket.emit('typing');
|
||||
}
|
||||
lastTypingTime = (new Date()).getTime();
|
||||
|
||||
setTimeout(function () {
|
||||
var typingTimer = (new Date()).getTime();
|
||||
var timeDiff = typingTimer - lastTypingTime;
|
||||
if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
|
||||
socket.emit('stop typing');
|
||||
typing = false;
|
||||
}
|
||||
}, TYPING_TIMER_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the 'X is typing' messages of a user
|
||||
function getTypingMessages (data) {
|
||||
return $('.typing.message').filter(function (i) {
|
||||
return $(this).data('username') === data.username;
|
||||
});
|
||||
}
|
||||
|
||||
// Gets the color of a username through our hash function
|
||||
function getUsernameColor (username) {
|
||||
// Compute hash code
|
||||
var hash = 7;
|
||||
for (var i = 0; i < username.length; i++) {
|
||||
hash = username.charCodeAt(i) + (hash << 5) - hash;
|
||||
}
|
||||
// Calculate color
|
||||
var index = Math.abs(hash % COLORS.length);
|
||||
return COLORS[index];
|
||||
}
|
||||
|
||||
// Keyboard events
|
||||
|
||||
$window.keydown(function (event) {
|
||||
// Auto-focus the current input when a key is typed
|
||||
if (!(event.ctrlKey || event.metaKey || event.altKey)) {
|
||||
$currentInput.focus();
|
||||
}
|
||||
// When the client hits ENTER on their keyboard
|
||||
if (event.which === 13) {
|
||||
if (username) {
|
||||
sendMessage();
|
||||
socket.emit('stop typing');
|
||||
typing = false;
|
||||
} else {
|
||||
setUsername();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$inputMessage.on('input', function() {
|
||||
updateTyping();
|
||||
});
|
||||
|
||||
// Click events
|
||||
|
||||
// Focus input when clicking anywhere on login page
|
||||
$loginPage.click(function () {
|
||||
$currentInput.focus();
|
||||
});
|
||||
|
||||
// Focus input when clicking on the message input's border
|
||||
$inputMessage.click(function () {
|
||||
$inputMessage.focus();
|
||||
});
|
||||
|
||||
// Socket events
|
||||
|
||||
// Whenever the server emits 'login', log the login message
|
||||
socket.on('login', function (data) {
|
||||
connected = true;
|
||||
// Display the welcome message
|
||||
var message = "Welcome to Socket.IO Chat – ";
|
||||
log(message, {
|
||||
prepend: true
|
||||
});
|
||||
addParticipantsMessage(data);
|
||||
});
|
||||
|
||||
// Whenever the server emits 'new message', update the chat body
|
||||
socket.on('new message', function (data) {
|
||||
addChatMessage(data);
|
||||
});
|
||||
|
||||
// Whenever the server emits 'user joined', log it in the chat body
|
||||
socket.on('user joined', function (data) {
|
||||
log(data.username + ' joined');
|
||||
addParticipantsMessage(data);
|
||||
});
|
||||
|
||||
// Whenever the server emits 'user left', log it in the chat body
|
||||
socket.on('user left', function (data) {
|
||||
log(data.username + ' left');
|
||||
addParticipantsMessage(data);
|
||||
removeChatTyping(data);
|
||||
});
|
||||
|
||||
// Whenever the server emits 'typing', show the typing message
|
||||
socket.on('typing', function (data) {
|
||||
addChatTyping(data);
|
||||
});
|
||||
|
||||
// Whenever the server emits 'stop typing', kill the typing message
|
||||
socket.on('stop typing', function (data) {
|
||||
removeChatTyping(data);
|
||||
});
|
||||
|
||||
socket.on('disconnect', function () {
|
||||
log('you have been disconnected');
|
||||
});
|
||||
|
||||
socket.on('connect', function () {
|
||||
if (username) {
|
||||
log('you have been reconnected');
|
||||
socket.emit('add user', username);
|
||||
}
|
||||
});
|
||||
|
||||
socket.io.on('reconnect_error', function () {
|
||||
log('attempt to reconnect has failed');
|
||||
});
|
||||
|
||||
socket.on('my-name-is', function (serverName) {
|
||||
log('host is now ' + serverName);
|
||||
})
|
||||
|
||||
});
|
||||
149
examples/cluster-traefik/server/public/style.css
Normal file
149
examples/cluster-traefik/server/public/style.css
Normal file
@@ -0,0 +1,149 @@
|
||||
/* Fix user-agent */
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-weight: 300;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
html, input {
|
||||
font-family:
|
||||
"HelveticaNeue-Light",
|
||||
"Helvetica Neue Light",
|
||||
"Helvetica Neue",
|
||||
Helvetica,
|
||||
Arial,
|
||||
"Lucida Grande",
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Pages */
|
||||
|
||||
.pages {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.page {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Login Page */
|
||||
|
||||
.login.page {
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.login.page .form {
|
||||
height: 100px;
|
||||
margin-top: -100px;
|
||||
position: absolute;
|
||||
|
||||
text-align: center;
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login.page .form .usernameInput {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-bottom: 2px solid #fff;
|
||||
outline: none;
|
||||
padding-bottom: 15px;
|
||||
text-align: center;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.login.page .title {
|
||||
font-size: 200%;
|
||||
}
|
||||
|
||||
.login.page .usernameInput {
|
||||
font-size: 200%;
|
||||
letter-spacing: 3px;
|
||||
}
|
||||
|
||||
.login.page .title, .login.page .usernameInput {
|
||||
color: #fff;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
/* Chat page */
|
||||
|
||||
.chat.page {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Font */
|
||||
|
||||
.messages {
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
.inputMessage {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.log {
|
||||
color: gray;
|
||||
font-size: 70%;
|
||||
margin: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Messages */
|
||||
|
||||
.chatArea {
|
||||
height: 100%;
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
|
||||
.messages {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow-y: scroll;
|
||||
padding: 10px 20px 10px 20px;
|
||||
}
|
||||
|
||||
.message.typing .messageBody {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
padding-right: 15px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Input */
|
||||
|
||||
.inputMessage {
|
||||
border: 10px solid #000;
|
||||
bottom: 0;
|
||||
height: 60px;
|
||||
left: 0;
|
||||
outline: none;
|
||||
padding-left: 10px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
10
examples/cluster-traefik/traefik.yml
Normal file
10
examples/cluster-traefik/traefik.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
api:
|
||||
insecure: true
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
|
||||
providers:
|
||||
docker: {}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Manager } from "socket.io-client";
|
||||
import { io } from "socket.io-client";
|
||||
|
||||
const manager = new Manager("ws://localhost:8080");
|
||||
const socket = manager.socket("/");
|
||||
const socket = io("ws://localhost:8080/", {});
|
||||
|
||||
socket.on("connect", () => {
|
||||
console.log(`connect ${socket.id}`);
|
||||
|
||||
70
examples/typescript/package-lock.json
generated
70
examples/typescript/package-lock.json
generated
@@ -9,6 +9,21 @@
|
||||
"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",
|
||||
@@ -81,9 +96,9 @@
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
|
||||
},
|
||||
"engine.io": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.0.2.tgz",
|
||||
"integrity": "sha512-sumdttqWLNjbuSMOSgDdL2xiEld9s5QZDk9VLyr4e28o+lzNNADhU3qpQDAY7cm2VZH0Otw/U0fL8mEjZ6kBMg==",
|
||||
"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",
|
||||
@@ -95,9 +110,9 @@
|
||||
}
|
||||
},
|
||||
"engine.io-client": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.2.tgz",
|
||||
"integrity": "sha512-cfzFu0u7rr/Gmz/CefwZ6mBj9kxtsOtOavV/YLbn+2sPGE1ZTSWh3tj8427a0od+BK27zsWDpnDx98fnpnmksA==",
|
||||
"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",
|
||||
@@ -119,9 +134,12 @@
|
||||
}
|
||||
},
|
||||
"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=="
|
||||
"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",
|
||||
@@ -147,9 +165,9 @@
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
"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",
|
||||
@@ -172,10 +190,13 @@
|
||||
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
|
||||
},
|
||||
"socket.io": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.0.1.tgz",
|
||||
"integrity": "sha512-oVYbCQ4sCwm4wVi+f1bsE3YFXcvd6b4JjVP8D7IZnQqBeJOKX9XrdgJWSbXqBEqUXPY3jdTqb1M3s4KFTa/IHg==",
|
||||
"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",
|
||||
@@ -190,9 +211,9 @@
|
||||
"integrity": "sha512-2wo4EXgxOGSFueqvHAdnmi5JLZzWqMArjuP4nqC26AtLh5PoCPsaRbRdah2xhcwTAMooZfjYiNVNkkmmSMaxOQ=="
|
||||
},
|
||||
"socket.io-client": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-3.0.1.tgz",
|
||||
"integrity": "sha512-iIzWRDrF/h3KPtHjvLt5LL/1n7Euvv35zVa1r10ScRjVw40yc8DxFj7GnKrj1RNYkbtveWOwEsy2lWp3oFJO7w==",
|
||||
"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",
|
||||
@@ -205,10 +226,11 @@
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"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"
|
||||
}
|
||||
@@ -250,9 +272,9 @@
|
||||
"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=="
|
||||
"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",
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
"author": "Damien Arrachequesne",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"socket.io": "^3.0.1",
|
||||
"socket.io-client": "^3.0.1",
|
||||
"socket.io": "^3.0.4",
|
||||
"socket.io-client": "^3.0.4",
|
||||
"ts-node": "^9.0.0",
|
||||
"typescript": "^4.0.5"
|
||||
}
|
||||
|
||||
108
lib/client.ts
108
lib/client.ts
@@ -1,10 +1,10 @@
|
||||
import { Decoder, Encoder, Packet, PacketType } from "socket.io-parser";
|
||||
import debugModule = require("debug");
|
||||
import { IncomingMessage } from "http";
|
||||
import { Server } from "./index";
|
||||
import { Socket } from "./socket";
|
||||
import { SocketId } from "socket.io-adapter";
|
||||
import { ParentNamespace } from "./parent-namespace";
|
||||
import url = require("url");
|
||||
import type { IncomingMessage } from "http";
|
||||
import type { Namespace, Server } from "./index";
|
||||
import type { Socket } from "./socket";
|
||||
import type { SocketId } from "socket.io-adapter";
|
||||
|
||||
const debug = debugModule("socket.io:client");
|
||||
|
||||
@@ -17,16 +17,16 @@ export class Client {
|
||||
private readonly decoder: Decoder;
|
||||
private sockets: Map<SocketId, Socket> = new Map();
|
||||
private nsps: Map<string, Socket> = new Map();
|
||||
private connectTimeout: NodeJS.Timeout;
|
||||
private connectTimeout?: NodeJS.Timeout;
|
||||
|
||||
/**
|
||||
* Client constructor.
|
||||
*
|
||||
* @param {Server} server instance
|
||||
* @param {Socket} conn
|
||||
* @param server instance
|
||||
* @param conn
|
||||
* @package
|
||||
*/
|
||||
constructor(server: Server, conn) {
|
||||
constructor(server: Server, conn: Socket) {
|
||||
this.server = server;
|
||||
this.conn = conn;
|
||||
this.encoder = server.encoder;
|
||||
@@ -78,47 +78,52 @@ export class Client {
|
||||
* @param {Object} auth - the auth parameters
|
||||
* @private
|
||||
*/
|
||||
private connect(name: string, auth: object = {}) {
|
||||
private connect(name: string, auth: object = {}): void {
|
||||
if (this.server._nsps.has(name)) {
|
||||
debug("connecting to namespace %s", name);
|
||||
return this.doConnect(name, auth);
|
||||
}
|
||||
|
||||
this.server._checkNamespace(name, auth, (dynamicNsp: ParentNamespace) => {
|
||||
if (dynamicNsp) {
|
||||
debug("dynamic namespace %s was created", dynamicNsp.name);
|
||||
this.doConnect(name, auth);
|
||||
} else {
|
||||
debug("creation of namespace %s was denied", name);
|
||||
this._packet({
|
||||
type: PacketType.CONNECT_ERROR,
|
||||
nsp: name,
|
||||
data: {
|
||||
message: "Invalid namespace"
|
||||
}
|
||||
});
|
||||
this.server._checkNamespace(
|
||||
name,
|
||||
auth,
|
||||
(dynamicNspName: Namespace | false) => {
|
||||
if (dynamicNspName) {
|
||||
debug("dynamic namespace %s was created", dynamicNspName);
|
||||
this.doConnect(name, auth);
|
||||
} else {
|
||||
debug("creation of namespace %s was denied", name);
|
||||
this._packet({
|
||||
type: PacketType.CONNECT_ERROR,
|
||||
nsp: name,
|
||||
data: {
|
||||
message: "Invalid namespace",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects a client to a namespace.
|
||||
*
|
||||
* @param {String} name - the namespace
|
||||
* @param name - the namespace
|
||||
* @param {Object} auth - the auth parameters
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private doConnect(name: string, auth: object) {
|
||||
if (this.connectTimeout) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.connectTimeout = null;
|
||||
}
|
||||
private doConnect(name: string, auth: object): void {
|
||||
const nsp = this.server.of(name);
|
||||
|
||||
const socket = nsp._add(this, auth, () => {
|
||||
this.sockets.set(socket.id, socket);
|
||||
this.nsps.set(nsp.name, socket);
|
||||
|
||||
if (this.connectTimeout) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.connectTimeout = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -127,7 +132,7 @@ export class Client {
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_disconnect() {
|
||||
_disconnect(): void {
|
||||
for (const socket of this.sockets.values()) {
|
||||
socket.disconnect();
|
||||
}
|
||||
@@ -140,9 +145,9 @@ export class Client {
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_remove(socket: Socket) {
|
||||
_remove(socket: Socket): void {
|
||||
if (this.sockets.has(socket.id)) {
|
||||
const nsp = this.sockets.get(socket.id).nsp.name;
|
||||
const nsp = this.sockets.get(socket.id)!.nsp.name;
|
||||
this.sockets.delete(socket.id);
|
||||
this.nsps.delete(nsp);
|
||||
} else {
|
||||
@@ -155,8 +160,8 @@ export class Client {
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private close() {
|
||||
if ("open" == this.conn.readyState) {
|
||||
private close(): void {
|
||||
if ("open" === this.conn.readyState) {
|
||||
debug("forcing transport close");
|
||||
this.conn.close();
|
||||
this.onclose("forced server close");
|
||||
@@ -170,19 +175,20 @@ export class Client {
|
||||
* @param {Object} opts
|
||||
* @private
|
||||
*/
|
||||
_packet(packet, opts?) {
|
||||
_packet(packet: Packet, opts?: any): void {
|
||||
opts = opts || {};
|
||||
const self = this;
|
||||
|
||||
// this writes to the actual connection
|
||||
function writeToEngine(encodedPackets) {
|
||||
function writeToEngine(encodedPackets: any) {
|
||||
// TODO clarify this.
|
||||
if (opts.volatile && !self.conn.transport.writable) return;
|
||||
for (let i = 0; i < encodedPackets.length; i++) {
|
||||
self.conn.write(encodedPackets[i], { compress: opts.compress });
|
||||
}
|
||||
}
|
||||
|
||||
if ("open" == this.conn.readyState) {
|
||||
if ("open" === this.conn.readyState) {
|
||||
debug("writing packet %j", packet);
|
||||
if (!opts.preEncoded) {
|
||||
// not broadcasting, need to encode
|
||||
@@ -201,7 +207,7 @@ export class Client {
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private ondata(data) {
|
||||
private ondata(data): void {
|
||||
// try/catch is needed for protocol violations (GH-1880)
|
||||
try {
|
||||
this.decoder.add(data);
|
||||
@@ -215,13 +221,18 @@ export class Client {
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private ondecoded(packet: Packet) {
|
||||
if (PacketType.CONNECT == packet.type) {
|
||||
this.connect(packet.nsp, packet.data);
|
||||
private ondecoded(packet: Packet): void {
|
||||
if (PacketType.CONNECT === packet.type) {
|
||||
if (this.conn.protocol === 3) {
|
||||
const parsed = url.parse(packet.nsp, true);
|
||||
this.connect(parsed.pathname!, parsed.query);
|
||||
} else {
|
||||
this.connect(packet.nsp, packet.data);
|
||||
}
|
||||
} else {
|
||||
const socket = this.nsps.get(packet.nsp);
|
||||
if (socket) {
|
||||
process.nextTick(function() {
|
||||
process.nextTick(function () {
|
||||
socket._onpacket(packet);
|
||||
});
|
||||
} else {
|
||||
@@ -236,7 +247,7 @@ export class Client {
|
||||
* @param {Object} err object
|
||||
* @private
|
||||
*/
|
||||
private onerror(err) {
|
||||
private onerror(err): void {
|
||||
for (const socket of this.sockets.values()) {
|
||||
socket._onerror(err);
|
||||
}
|
||||
@@ -249,7 +260,7 @@ export class Client {
|
||||
* @param reason
|
||||
* @private
|
||||
*/
|
||||
private onclose(reason: string) {
|
||||
private onclose(reason: string): void {
|
||||
debug("client close with reason %s", reason);
|
||||
|
||||
// ignore a potential subsequent `close` event
|
||||
@@ -268,11 +279,16 @@ export class Client {
|
||||
* Cleans up event listeners.
|
||||
* @private
|
||||
*/
|
||||
private destroy() {
|
||||
private destroy(): void {
|
||||
this.conn.removeListener("data", this.ondata);
|
||||
this.conn.removeListener("error", this.onerror);
|
||||
this.conn.removeListener("close", this.onclose);
|
||||
// @ts-ignore
|
||||
this.decoder.removeListener("decoded", this.ondecoded);
|
||||
|
||||
if (this.connectTimeout) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.connectTimeout = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
238
lib/index.ts
238
lib/index.ts
@@ -11,11 +11,11 @@ import { ExtendedError, Namespace } from "./namespace";
|
||||
import { ParentNamespace } from "./parent-namespace";
|
||||
import { Adapter, Room, SocketId } from "socket.io-adapter";
|
||||
import * as parser from "socket.io-parser";
|
||||
import { Encoder } from "socket.io-parser";
|
||||
import type { Encoder } from "socket.io-parser";
|
||||
import debugModule from "debug";
|
||||
import { Socket } from "./socket";
|
||||
import { CookieSerializeOptions } from "cookie";
|
||||
import { CorsOptions } from "cors";
|
||||
import type { CookieSerializeOptions } from "cookie";
|
||||
import type { CorsOptions } from "cors";
|
||||
|
||||
const debug = debugModule("socket.io:server");
|
||||
|
||||
@@ -23,6 +23,11 @@ const clientVersion = require("../package.json").version;
|
||||
const dotMapRegex = /\.map/;
|
||||
|
||||
type Transport = "polling" | "websocket";
|
||||
type ParentNspNameMatchFn = (
|
||||
name: string,
|
||||
auth: { [key: string]: any },
|
||||
fn: (err: Error | null, success: boolean) => void
|
||||
) => void;
|
||||
|
||||
interface EngineOptions {
|
||||
/**
|
||||
@@ -95,6 +100,11 @@ interface EngineOptions {
|
||||
* the options that will be forwarded to the cors module
|
||||
*/
|
||||
cors: CorsOptions;
|
||||
/**
|
||||
* whether to enable compatibility with Socket.IO v2 clients
|
||||
* @default false
|
||||
*/
|
||||
allowEIO3: boolean;
|
||||
}
|
||||
|
||||
interface AttachOptions {
|
||||
@@ -149,7 +159,7 @@ export class Server extends EventEmitter {
|
||||
public readonly sockets: Namespace;
|
||||
|
||||
/** @private */
|
||||
readonly _parser;
|
||||
readonly _parser: typeof parser;
|
||||
/** @private */
|
||||
readonly encoder: Encoder;
|
||||
|
||||
@@ -157,17 +167,8 @@ export class Server extends EventEmitter {
|
||||
* @private
|
||||
*/
|
||||
_nsps: Map<string, Namespace> = new Map();
|
||||
private parentNsps: Map<
|
||||
| string
|
||||
| RegExp
|
||||
| ((
|
||||
name: string,
|
||||
query: object,
|
||||
fn: (err: Error, success: boolean) => void
|
||||
) => void),
|
||||
ParentNamespace
|
||||
> = new Map();
|
||||
private _adapter: any;
|
||||
private parentNsps: Map<ParentNspNameMatchFn, ParentNamespace> = new Map();
|
||||
private _adapter?: typeof Adapter;
|
||||
private _serveClient: boolean;
|
||||
private opts: Partial<EngineOptions>;
|
||||
private eio;
|
||||
@@ -184,18 +185,28 @@ export class Server extends EventEmitter {
|
||||
/**
|
||||
* Server constructor.
|
||||
*
|
||||
* @param {http.Server|Number|Object} srv http server, port or options
|
||||
* @param {Object} [opts]
|
||||
* @param srv http server, port, or options
|
||||
* @param [opts]
|
||||
* @public
|
||||
*/
|
||||
constructor(opts?: Partial<ServerOptions>);
|
||||
constructor(srv: http.Server, opts?: Partial<ServerOptions>);
|
||||
constructor(srv: number, opts?: Partial<ServerOptions>);
|
||||
constructor(srv?: any, opts: Partial<ServerOptions> = {}) {
|
||||
constructor(srv?: http.Server | number, opts?: Partial<ServerOptions>);
|
||||
constructor(
|
||||
srv: undefined | Partial<ServerOptions> | http.Server | number,
|
||||
opts?: Partial<ServerOptions>
|
||||
);
|
||||
constructor(
|
||||
srv: undefined | Partial<ServerOptions> | http.Server | number,
|
||||
opts: Partial<ServerOptions> = {}
|
||||
) {
|
||||
super();
|
||||
if ("object" == typeof srv && srv instanceof Object && !srv.listen) {
|
||||
opts = srv;
|
||||
srv = null;
|
||||
if (
|
||||
"object" === typeof srv &&
|
||||
srv instanceof Object &&
|
||||
!(srv as Partial<http.Server>).listen
|
||||
) {
|
||||
opts = srv as Partial<ServerOptions>;
|
||||
srv = undefined;
|
||||
}
|
||||
this.path(opts.path || "/socket.io");
|
||||
this.connectTimeout(opts.connectTimeout || 45000);
|
||||
@@ -205,44 +216,45 @@ export class Server extends EventEmitter {
|
||||
this.adapter(opts.adapter || Adapter);
|
||||
this.sockets = this.of("/");
|
||||
this.opts = opts;
|
||||
if (srv) this.attach(srv);
|
||||
if (srv) this.attach(srv as http.Server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets/gets whether client code is being served.
|
||||
*
|
||||
* @param {Boolean} v - whether to serve client code
|
||||
* @return {Server|Boolean} self when setting or value when getting
|
||||
* @param v - whether to serve client code
|
||||
* @return self when setting or value when getting
|
||||
* @public
|
||||
*/
|
||||
public serveClient(v: boolean): Server;
|
||||
public serveClient(v: boolean): this;
|
||||
public serveClient(): boolean;
|
||||
public serveClient(v?: boolean): Server | boolean {
|
||||
public serveClient(v?: boolean): this | boolean;
|
||||
public serveClient(v?: boolean): this | boolean {
|
||||
if (!arguments.length) return this._serveClient;
|
||||
this._serveClient = v;
|
||||
this._serveClient = v!;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the middleware for an incoming namespace not already created on the server.
|
||||
*
|
||||
* @param {String} name - name of incoming namespace
|
||||
* @param {Object} auth - the auth parameters
|
||||
* @param {Function} fn - callback
|
||||
* @param name - name of incoming namespace
|
||||
* @param auth - the auth parameters
|
||||
* @param fn - callback
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_checkNamespace(
|
||||
name: string,
|
||||
auth: object,
|
||||
fn: (nsp: Namespace | boolean) => void
|
||||
) {
|
||||
auth: { [key: string]: any },
|
||||
fn: (nsp: Namespace | false) => void
|
||||
): void {
|
||||
if (this.parentNsps.size === 0) return fn(false);
|
||||
|
||||
const keysIterator = this.parentNsps.keys();
|
||||
|
||||
const run = () => {
|
||||
let nextFn = keysIterator.next();
|
||||
const nextFn = keysIterator.next();
|
||||
if (nextFn.done) {
|
||||
return fn(false);
|
||||
}
|
||||
@@ -250,7 +262,7 @@ export class Server extends EventEmitter {
|
||||
if (err || !allow) {
|
||||
run();
|
||||
} else {
|
||||
fn(this.parentNsps.get(nextFn.value).createChild(name));
|
||||
fn(this.parentNsps.get(nextFn.value)!.createChild(name));
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -265,12 +277,13 @@ export class Server extends EventEmitter {
|
||||
* @return {Server|String} self when setting or value when getting
|
||||
* @public
|
||||
*/
|
||||
public path(v: string): Server;
|
||||
public path(v: string): this;
|
||||
public path(): string;
|
||||
public path(v?: string): Server | string {
|
||||
public path(v?: string): this | string;
|
||||
public path(v?: string): this | string {
|
||||
if (!arguments.length) return this._path;
|
||||
|
||||
this._path = v.replace(/\/$/, "");
|
||||
this._path = v!.replace(/\/$/, "");
|
||||
|
||||
const escapedPath = this._path.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||||
this.clientPathRegex = new RegExp(
|
||||
@@ -286,9 +299,10 @@ export class Server extends EventEmitter {
|
||||
* @param v
|
||||
* @public
|
||||
*/
|
||||
public connectTimeout(v: number): Server;
|
||||
public connectTimeout(v: number): this;
|
||||
public connectTimeout(): number;
|
||||
public connectTimeout(v?: number): Server | number {
|
||||
public connectTimeout(v?: number): this | number;
|
||||
public connectTimeout(v?: number): this | number {
|
||||
if (v === undefined) return this._connectTimeout;
|
||||
this._connectTimeout = v;
|
||||
return this;
|
||||
@@ -297,13 +311,14 @@ export class Server extends EventEmitter {
|
||||
/**
|
||||
* Sets the adapter for rooms.
|
||||
*
|
||||
* @param {Adapter} v pathname
|
||||
* @return {Server|Adapter} self when setting or value when getting
|
||||
* @param v pathname
|
||||
* @return self when setting or value when getting
|
||||
* @public
|
||||
*/
|
||||
public adapter(): any;
|
||||
public adapter(v: any);
|
||||
public adapter(v?): Server | any {
|
||||
public adapter(): typeof Adapter | undefined;
|
||||
public adapter(v: typeof Adapter): this;
|
||||
public adapter(v?: typeof Adapter): typeof Adapter | undefined | this;
|
||||
public adapter(v?: typeof Adapter): typeof Adapter | undefined | this {
|
||||
if (!arguments.length) return this._adapter;
|
||||
this._adapter = v;
|
||||
for (const nsp of this._nsps.values()) {
|
||||
@@ -315,28 +330,30 @@ export class Server extends EventEmitter {
|
||||
/**
|
||||
* Attaches socket.io to a server or port.
|
||||
*
|
||||
* @param {http.Server|Number} srv - server or port
|
||||
* @param {Object} opts - options passed to engine.io
|
||||
* @return {Server} self
|
||||
* @param srv - server or port
|
||||
* @param opts - options passed to engine.io
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public listen(srv: http.Server, opts?: Partial<ServerOptions>): Server;
|
||||
public listen(srv: number, opts?: Partial<ServerOptions>): Server;
|
||||
public listen(srv: any, opts: Partial<ServerOptions> = {}): Server {
|
||||
public listen(
|
||||
srv: http.Server | number,
|
||||
opts: Partial<ServerOptions> = {}
|
||||
): this {
|
||||
return this.attach(srv, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches socket.io to a server or port.
|
||||
*
|
||||
* @param {http.Server|Number} srv - server or port
|
||||
* @param {Object} opts - options passed to engine.io
|
||||
* @return {Server} self
|
||||
* @param srv - server or port
|
||||
* @param opts - options passed to engine.io
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public attach(srv: http.Server, opts?: Partial<ServerOptions>): Server;
|
||||
public attach(port: number, opts?: Partial<ServerOptions>): Server;
|
||||
public attach(srv: any, opts: Partial<ServerOptions> = {}): Server {
|
||||
public attach(
|
||||
srv: http.Server | number,
|
||||
opts: Partial<ServerOptions> = {}
|
||||
): this {
|
||||
if ("function" == typeof srv) {
|
||||
const msg =
|
||||
"You are trying to attach socket.io to an express " +
|
||||
@@ -376,7 +393,10 @@ export class Server extends EventEmitter {
|
||||
* @param opts - options passed to engine.io
|
||||
* @private
|
||||
*/
|
||||
private initEngine(srv: http.Server, opts: Partial<EngineAttachOptions>) {
|
||||
private initEngine(
|
||||
srv: http.Server,
|
||||
opts: Partial<EngineAttachOptions>
|
||||
): void {
|
||||
// initialize engine
|
||||
debug("creating engine.io instance with opts %j", opts);
|
||||
this.eio = engine.attach(srv, opts);
|
||||
@@ -394,10 +414,10 @@ export class Server extends EventEmitter {
|
||||
/**
|
||||
* Attaches the static file serving.
|
||||
*
|
||||
* @param {Function|http.Server} srv http server
|
||||
* @param srv http server
|
||||
* @private
|
||||
*/
|
||||
private attachServe(srv) {
|
||||
private attachServe(srv: http.Server): void {
|
||||
debug("attaching client serving req handler");
|
||||
|
||||
const evs = srv.listeners("request").slice(0);
|
||||
@@ -416,22 +436,23 @@ export class Server extends EventEmitter {
|
||||
/**
|
||||
* Handles a request serving of client source and map
|
||||
*
|
||||
* @param {http.IncomingMessage} req
|
||||
* @param {http.ServerResponse} res
|
||||
* @param req
|
||||
* @param res
|
||||
* @private
|
||||
*/
|
||||
private serve(req: http.IncomingMessage, res: http.ServerResponse) {
|
||||
const filename = req.url.replace(this._path, "");
|
||||
private serve(req: http.IncomingMessage, res: http.ServerResponse): void {
|
||||
const filename = req.url!.replace(this._path, "");
|
||||
const isMap = dotMapRegex.test(filename);
|
||||
const type = isMap ? "map" : "source";
|
||||
|
||||
// Per the standard, ETags must be quoted:
|
||||
// https://tools.ietf.org/html/rfc7232#section-2.3
|
||||
const expectedEtag = '"' + clientVersion + '"';
|
||||
const weakEtag = "W/" + expectedEtag;
|
||||
|
||||
const etag = req.headers["if-none-match"];
|
||||
if (etag) {
|
||||
if (expectedEtag == etag) {
|
||||
if (expectedEtag === etag || weakEtag === etag) {
|
||||
debug("serve client %s 304", type);
|
||||
res.writeHead(304);
|
||||
res.end();
|
||||
@@ -464,13 +485,13 @@ export class Server extends EventEmitter {
|
||||
filename: string,
|
||||
req: http.IncomingMessage,
|
||||
res: http.ServerResponse
|
||||
) {
|
||||
): void {
|
||||
const readStream = createReadStream(
|
||||
path.join(__dirname, "../client-dist/", filename)
|
||||
);
|
||||
const encoding = accepts(req).encodings(["br", "gzip", "deflate"]);
|
||||
|
||||
const onError = err => {
|
||||
const onError = (err: NodeJS.ErrnoException | null) => {
|
||||
if (err) {
|
||||
res.end();
|
||||
}
|
||||
@@ -500,10 +521,10 @@ export class Server extends EventEmitter {
|
||||
* Binds socket.io to an engine.io instance.
|
||||
*
|
||||
* @param {engine.Server} engine engine.io (or compatible) server
|
||||
* @return {Server} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public bind(engine): Server {
|
||||
public bind(engine): this {
|
||||
this.engine = engine;
|
||||
this.engine.on("connection", this.onconnection.bind(this));
|
||||
return this;
|
||||
@@ -513,12 +534,16 @@ export class Server extends EventEmitter {
|
||||
* Called with each incoming transport connection.
|
||||
*
|
||||
* @param {engine.Socket} conn
|
||||
* @return {Server} self
|
||||
* @return self
|
||||
* @private
|
||||
*/
|
||||
private onconnection(conn): Server {
|
||||
private onconnection(conn): this {
|
||||
debug("incoming connection with id %s", conn.id);
|
||||
new Client(this, conn);
|
||||
const client = new Client(this, conn);
|
||||
if (conn.protocol === 3) {
|
||||
// @ts-ignore
|
||||
client.connect("/");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -526,20 +551,13 @@ export class Server extends EventEmitter {
|
||||
* Looks up a namespace.
|
||||
*
|
||||
* @param {String|RegExp|Function} name nsp name
|
||||
* @param {Function} [fn] optional, nsp `connection` ev handler
|
||||
* @param fn optional, nsp `connection` ev handler
|
||||
* @public
|
||||
*/
|
||||
public of(
|
||||
name:
|
||||
| string
|
||||
| RegExp
|
||||
| ((
|
||||
name: string,
|
||||
query: object,
|
||||
fn: (err: Error, success: boolean) => void
|
||||
) => void),
|
||||
name: string | RegExp | ParentNspNameMatchFn,
|
||||
fn?: (socket: Socket) => void
|
||||
) {
|
||||
): Namespace {
|
||||
if (typeof name === "function" || name instanceof RegExp) {
|
||||
const parentNsp = new ParentNamespace(this);
|
||||
debug("initializing parent namespace %s", parentNsp.name);
|
||||
@@ -573,7 +591,7 @@ export class Server extends EventEmitter {
|
||||
/**
|
||||
* Closes server connection
|
||||
*
|
||||
* @param {Function} [fn] optional, called as `fn([err])` on error OR all conns closed
|
||||
* @param [fn] optional, called as `fn([err])` on error OR all conns closed
|
||||
* @public
|
||||
*/
|
||||
public close(fn?: (err?: Error) => void): void {
|
||||
@@ -593,12 +611,12 @@ export class Server extends EventEmitter {
|
||||
/**
|
||||
* Sets up namespace middleware.
|
||||
*
|
||||
* @return {Server} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public use(
|
||||
fn: (socket: Socket, next: (err?: ExtendedError) => void) => void
|
||||
): Server {
|
||||
): this {
|
||||
this.sockets.use(fn);
|
||||
return this;
|
||||
}
|
||||
@@ -606,11 +624,11 @@ export class Server extends EventEmitter {
|
||||
/**
|
||||
* Targets a room when emitting.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Server} self
|
||||
* @param name
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public to(name: Room): Server {
|
||||
public to(name: Room): this {
|
||||
this.sockets.to(name);
|
||||
return this;
|
||||
}
|
||||
@@ -618,11 +636,11 @@ export class Server extends EventEmitter {
|
||||
/**
|
||||
* Targets a room when emitting.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Server} self
|
||||
* @param name
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public in(name: Room): Server {
|
||||
public in(name: Room): this {
|
||||
this.sockets.in(name);
|
||||
return this;
|
||||
}
|
||||
@@ -630,24 +648,22 @@ export class Server extends EventEmitter {
|
||||
/**
|
||||
* Sends a `message` event to all clients.
|
||||
*
|
||||
* @return {Server} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public send(...args): Server {
|
||||
args.unshift("message");
|
||||
this.sockets.emit.apply(this.sockets, args);
|
||||
public send(...args: readonly any[]): this {
|
||||
this.sockets.emit("message", ...args);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a `message` event to all clients.
|
||||
*
|
||||
* @return {Server} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public write(...args): Server {
|
||||
args.unshift("message");
|
||||
this.sockets.emit.apply(this.sockets, args);
|
||||
public write(...args: readonly any[]): this {
|
||||
this.sockets.emit("message", ...args);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -663,11 +679,11 @@ export class Server extends EventEmitter {
|
||||
/**
|
||||
* Sets the compress flag.
|
||||
*
|
||||
* @param {Boolean} compress - if `true`, compresses the sending data
|
||||
* @return {Server} self
|
||||
* @param compress - if `true`, compresses the sending data
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public compress(compress: boolean): Server {
|
||||
public compress(compress: boolean): this {
|
||||
this.sockets.compress(compress);
|
||||
return this;
|
||||
}
|
||||
@@ -677,10 +693,10 @@ export class Server extends EventEmitter {
|
||||
* receive messages (because of network slowness or other issues, or because they’re connected through long polling
|
||||
* and is in the middle of a request-response cycle).
|
||||
*
|
||||
* @return {Server} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public get volatile(): Server {
|
||||
public get volatile(): this {
|
||||
this.sockets.volatile;
|
||||
return this;
|
||||
}
|
||||
@@ -688,10 +704,10 @@ export class Server extends EventEmitter {
|
||||
/**
|
||||
* Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node.
|
||||
*
|
||||
* @return {Server} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public get local(): Server {
|
||||
public get local(): this {
|
||||
this.sockets.local;
|
||||
return this;
|
||||
}
|
||||
@@ -701,14 +717,14 @@ export class Server extends EventEmitter {
|
||||
* Expose main namespace (/).
|
||||
*/
|
||||
|
||||
const emitterMethods = Object.keys(EventEmitter.prototype).filter(function(
|
||||
const emitterMethods = Object.keys(EventEmitter.prototype).filter(function (
|
||||
key
|
||||
) {
|
||||
return typeof EventEmitter.prototype[key] === "function";
|
||||
});
|
||||
|
||||
emitterMethods.forEach(function(fn) {
|
||||
Server.prototype[fn] = function() {
|
||||
emitterMethods.forEach(function (fn) {
|
||||
Server.prototype[fn] = function () {
|
||||
return this.sockets[fn].apply(this.sockets, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Socket, RESERVED_EVENTS } from "./socket";
|
||||
import { Server } from "./index";
|
||||
import { Client } from "./client";
|
||||
import type { Server } from "./index";
|
||||
import type { Client } from "./client";
|
||||
import { EventEmitter } from "events";
|
||||
import { PacketType } from "socket.io-parser";
|
||||
import debugModule from "debug";
|
||||
import { Adapter, Room, SocketId } from "socket.io-adapter";
|
||||
import type { Adapter, Room, SocketId } from "socket.io-adapter";
|
||||
|
||||
const debug = debugModule("socket.io:namespace");
|
||||
|
||||
@@ -23,7 +23,7 @@ export class Namespace extends EventEmitter {
|
||||
|
||||
/** @private */
|
||||
_fns: Array<
|
||||
(socket: Socket, next: (err: ExtendedError) => void) => void
|
||||
(socket: Socket, next: (err?: ExtendedError) => void) => void
|
||||
> = [];
|
||||
|
||||
/** @private */
|
||||
@@ -38,8 +38,8 @@ export class Namespace extends EventEmitter {
|
||||
/**
|
||||
* Namespace constructor.
|
||||
*
|
||||
* @param {Server} server instance
|
||||
* @param {string} name
|
||||
* @param server instance
|
||||
* @param name
|
||||
*/
|
||||
constructor(server: Server, name: string) {
|
||||
super();
|
||||
@@ -56,18 +56,18 @@ export class Namespace extends EventEmitter {
|
||||
* @private
|
||||
*/
|
||||
_initAdapter(): void {
|
||||
this.adapter = new (this.server.adapter())(this);
|
||||
this.adapter = new (this.server.adapter()!)(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up namespace middleware.
|
||||
*
|
||||
* @return {Namespace} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public use(
|
||||
fn: (socket: Socket, next: (err?: ExtendedError) => void) => void
|
||||
): Namespace {
|
||||
): this {
|
||||
this._fns.push(fn);
|
||||
return this;
|
||||
}
|
||||
@@ -75,16 +75,16 @@ export class Namespace extends EventEmitter {
|
||||
/**
|
||||
* Executes the middleware for an incoming client.
|
||||
*
|
||||
* @param {Socket} socket - the socket that will get added
|
||||
* @param {Function} fn - last fn call in the middleware
|
||||
* @param socket - the socket that will get added
|
||||
* @param fn - last fn call in the middleware
|
||||
* @private
|
||||
*/
|
||||
private run(socket: Socket, fn: (err: ExtendedError) => void) {
|
||||
private run(socket: Socket, fn: (err: ExtendedError | null) => void) {
|
||||
const fns = this._fns.slice(0);
|
||||
if (!fns.length) return fn(null);
|
||||
|
||||
function run(i) {
|
||||
fns[i](socket, function(err) {
|
||||
function run(i: number) {
|
||||
fns[i](socket, function (err) {
|
||||
// upon error, short-circuit
|
||||
if (err) return fn(err);
|
||||
|
||||
@@ -102,11 +102,11 @@ export class Namespace extends EventEmitter {
|
||||
/**
|
||||
* Targets a room when emitting.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Namespace} self
|
||||
* @param name
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public to(name: Room): Namespace {
|
||||
public to(name: Room): this {
|
||||
this._rooms.add(name);
|
||||
return this;
|
||||
}
|
||||
@@ -114,11 +114,11 @@ export class Namespace extends EventEmitter {
|
||||
/**
|
||||
* Targets a room when emitting.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Namespace} self
|
||||
* @param name
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public in(name: Room): Namespace {
|
||||
public in(name: Room): this {
|
||||
this._rooms.add(name);
|
||||
return this;
|
||||
}
|
||||
@@ -132,14 +132,19 @@ export class Namespace extends EventEmitter {
|
||||
_add(client: Client, query, fn?: () => void): Socket {
|
||||
debug("adding socket to nsp %s", this.name);
|
||||
const socket = new Socket(this, client, query);
|
||||
this.run(socket, err => {
|
||||
this.run(socket, (err) => {
|
||||
process.nextTick(() => {
|
||||
if ("open" == client.conn.readyState) {
|
||||
if (err)
|
||||
return socket._error({
|
||||
message: err.message,
|
||||
data: err.data
|
||||
});
|
||||
if (err) {
|
||||
if (client.conn.protocol === 3) {
|
||||
return socket._error(err.data || err.message);
|
||||
} else {
|
||||
return socket._error({
|
||||
message: err.message,
|
||||
data: err.data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// track socket
|
||||
this.sockets.set(socket.id, socket);
|
||||
@@ -178,10 +183,10 @@ export class Namespace extends EventEmitter {
|
||||
/**
|
||||
* Emits to all clients.
|
||||
*
|
||||
* @return {Boolean} Always true
|
||||
* @return Always true
|
||||
* @public
|
||||
*/
|
||||
public emit(ev: string, ...args: any[]): boolean {
|
||||
public emit(ev: string | Symbol, ...args: any[]): true {
|
||||
if (RESERVED_EVENTS.has(ev)) {
|
||||
throw new Error(`"${ev}" is a reserved event name`);
|
||||
}
|
||||
@@ -189,7 +194,7 @@ export class Namespace extends EventEmitter {
|
||||
args.unshift(ev);
|
||||
const packet = {
|
||||
type: PacketType.EVENT,
|
||||
data: args
|
||||
data: args,
|
||||
};
|
||||
|
||||
if ("function" == typeof args[args.length - 1]) {
|
||||
@@ -205,7 +210,7 @@ export class Namespace extends EventEmitter {
|
||||
|
||||
this.adapter.broadcast(packet, {
|
||||
rooms: rooms,
|
||||
flags: flags
|
||||
flags: flags,
|
||||
});
|
||||
|
||||
return true;
|
||||
@@ -214,31 +219,29 @@ export class Namespace extends EventEmitter {
|
||||
/**
|
||||
* Sends a `message` event to all clients.
|
||||
*
|
||||
* @return {Namespace} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public send(...args): Namespace {
|
||||
args.unshift("message");
|
||||
this.emit.apply(this, args);
|
||||
public send(...args: readonly any[]): this {
|
||||
this.emit("message", ...args);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a `message` event to all clients.
|
||||
*
|
||||
* @return {Namespace} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public write(...args): Namespace {
|
||||
args.unshift("message");
|
||||
this.emit.apply(this, args);
|
||||
public write(...args: readonly any[]): this {
|
||||
this.emit("message", ...args);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of clients.
|
||||
*
|
||||
* @return {Namespace} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public allSockets(): Promise<Set<SocketId>> {
|
||||
@@ -255,11 +258,11 @@ export class Namespace extends EventEmitter {
|
||||
/**
|
||||
* Sets the compress flag.
|
||||
*
|
||||
* @param {Boolean} compress - if `true`, compresses the sending data
|
||||
* @return {Namespace} self
|
||||
* @param compress - if `true`, compresses the sending data
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public compress(compress: boolean): Namespace {
|
||||
public compress(compress: boolean): this {
|
||||
this._flags.compress = compress;
|
||||
return this;
|
||||
}
|
||||
@@ -269,10 +272,10 @@ export class Namespace extends EventEmitter {
|
||||
* receive messages (because of network slowness or other issues, or because they’re connected through long polling
|
||||
* and is in the middle of a request-response cycle).
|
||||
*
|
||||
* @return {Namespace} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public get volatile(): Namespace {
|
||||
public get volatile(): this {
|
||||
this._flags.volatile = true;
|
||||
return this;
|
||||
}
|
||||
@@ -280,10 +283,10 @@ export class Namespace extends EventEmitter {
|
||||
/**
|
||||
* Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node.
|
||||
*
|
||||
* @return {Namespace} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public get local(): Namespace {
|
||||
public get local(): this {
|
||||
this._flags.local = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
import { Namespace } from "./namespace";
|
||||
import type { Server } from "./index";
|
||||
|
||||
export class ParentNamespace extends Namespace {
|
||||
private static count: number = 0;
|
||||
private children: Set<Namespace> = new Set();
|
||||
|
||||
constructor(server) {
|
||||
constructor(server: Server) {
|
||||
super(server, "/_" + ParentNamespace.count++);
|
||||
}
|
||||
|
||||
_initAdapter() {}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_initAdapter(): void {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
public emit(...args: any[]): boolean {
|
||||
this.children.forEach(nsp => {
|
||||
public emit(ev: string | Symbol, ...args: [...any]): true {
|
||||
this.children.forEach((nsp) => {
|
||||
nsp._rooms = this._rooms;
|
||||
nsp._flags = this._flags;
|
||||
nsp.emit.apply(nsp, args);
|
||||
nsp.emit(ev, ...args);
|
||||
});
|
||||
this._rooms.clear();
|
||||
this._flags = {};
|
||||
@@ -22,16 +28,14 @@ export class ParentNamespace extends Namespace {
|
||||
return true;
|
||||
}
|
||||
|
||||
createChild(name) {
|
||||
createChild(name: string): Namespace {
|
||||
const namespace = new Namespace(this.server, name);
|
||||
namespace._fns = this._fns.slice(0);
|
||||
this.listeners("connect").forEach(listener =>
|
||||
// @ts-ignore
|
||||
namespace.on("connect", listener)
|
||||
this.listeners("connect").forEach((listener) =>
|
||||
namespace.on("connect", listener as (...args: any[]) => void)
|
||||
);
|
||||
this.listeners("connection").forEach(listener =>
|
||||
// @ts-ignore
|
||||
namespace.on("connection", listener)
|
||||
this.listeners("connection").forEach((listener) =>
|
||||
namespace.on("connection", listener as (...args: any[]) => void)
|
||||
);
|
||||
this.children.add(namespace);
|
||||
this.server._nsps.set(name, namespace);
|
||||
|
||||
193
lib/socket.ts
193
lib/socket.ts
@@ -1,25 +1,31 @@
|
||||
import { EventEmitter } from "events";
|
||||
import { PacketType } from "socket.io-parser";
|
||||
import { Packet, PacketType } from "socket.io-parser";
|
||||
import url = require("url");
|
||||
import debugModule from "debug";
|
||||
import { Server } from "./index";
|
||||
import { Client } from "./client";
|
||||
import { Namespace } from "./namespace";
|
||||
import { IncomingMessage } from "http";
|
||||
import { Adapter, BroadcastFlags, Room, SocketId } from "socket.io-adapter";
|
||||
import type { Server } from "./index";
|
||||
import type { Client } from "./client";
|
||||
import type { Namespace } from "./namespace";
|
||||
import type { IncomingMessage, IncomingHttpHeaders } from "http";
|
||||
import type {
|
||||
Adapter,
|
||||
BroadcastFlags,
|
||||
Room,
|
||||
SocketId,
|
||||
} from "socket.io-adapter";
|
||||
import base64id from "base64id";
|
||||
import type { ParsedUrlQuery } from "querystring";
|
||||
|
||||
const debug = debugModule("socket.io:socket");
|
||||
|
||||
export const RESERVED_EVENTS = new Set([
|
||||
export const RESERVED_EVENTS = new Set(<const>[
|
||||
"connect",
|
||||
"connect_error",
|
||||
"disconnect",
|
||||
"disconnecting",
|
||||
// EventEmitter reserved events: https://nodejs.org/api/events.html#events_event_newlistener
|
||||
"newListener",
|
||||
"removeListener"
|
||||
]);
|
||||
"removeListener",
|
||||
]) as ReadonlySet<string | Symbol>;
|
||||
|
||||
/**
|
||||
* The handshake details
|
||||
@@ -28,7 +34,7 @@ export interface Handshake {
|
||||
/**
|
||||
* The headers sent as part of the handshake
|
||||
*/
|
||||
headers: object;
|
||||
headers: IncomingHttpHeaders;
|
||||
|
||||
/**
|
||||
* The date of creation (as string)
|
||||
@@ -63,12 +69,12 @@ export interface Handshake {
|
||||
/**
|
||||
* The query object
|
||||
*/
|
||||
query: object;
|
||||
query: ParsedUrlQuery;
|
||||
|
||||
/**
|
||||
* The auth object
|
||||
*/
|
||||
auth: object;
|
||||
auth: { [key: string]: any };
|
||||
}
|
||||
|
||||
export class Socket extends EventEmitter {
|
||||
@@ -86,7 +92,7 @@ export class Socket extends EventEmitter {
|
||||
> = [];
|
||||
private flags: BroadcastFlags = {};
|
||||
private _rooms: Set<Room> = new Set();
|
||||
private _anyListeners: Array<(...args: any[]) => void>;
|
||||
private _anyListeners?: Array<(...args: any[]) => void>;
|
||||
|
||||
/**
|
||||
* Interface to a `Client` for a given `Namespace`.
|
||||
@@ -100,7 +106,12 @@ export class Socket extends EventEmitter {
|
||||
super();
|
||||
this.server = nsp.server;
|
||||
this.adapter = this.nsp.adapter;
|
||||
this.id = base64id.generateId(); // don't reuse the Engine.IO id because it's sensitive information
|
||||
if (client.conn.protocol === 3) {
|
||||
// @ts-ignore
|
||||
this.id = nsp.name !== "/" ? nsp.name + "#" + client.id : client.id;
|
||||
} else {
|
||||
this.id = base64id.generateId(); // don't reuse the Engine.IO id because it's sensitive information
|
||||
}
|
||||
this.connected = true;
|
||||
this.disconnected = false;
|
||||
this.handshake = this.buildHandshake(auth);
|
||||
@@ -120,16 +131,16 @@ export class Socket extends EventEmitter {
|
||||
// @ts-ignore
|
||||
secure: !!this.request.connection.encrypted,
|
||||
issued: +new Date(),
|
||||
url: this.request.url,
|
||||
query: url.parse(this.request.url, true).query,
|
||||
auth
|
||||
url: this.request.url!,
|
||||
query: url.parse(this.request.url!, true).query,
|
||||
auth,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits to this client.
|
||||
*
|
||||
* @return {Boolean} Always true
|
||||
* @return Always returns `true`.
|
||||
* @public
|
||||
*/
|
||||
public emit(ev: string, ...args: any[]): boolean {
|
||||
@@ -139,7 +150,7 @@ export class Socket extends EventEmitter {
|
||||
args.unshift(ev);
|
||||
const packet: any = {
|
||||
type: PacketType.EVENT,
|
||||
data: args
|
||||
data: args,
|
||||
};
|
||||
|
||||
// access last argument to see if it's an ACK callback
|
||||
@@ -164,7 +175,7 @@ export class Socket extends EventEmitter {
|
||||
this.adapter.broadcast(packet, {
|
||||
except: new Set([this.id]),
|
||||
rooms: rooms,
|
||||
flags: flags
|
||||
flags: flags,
|
||||
});
|
||||
} else {
|
||||
// dispatch packet
|
||||
@@ -176,11 +187,11 @@ export class Socket extends EventEmitter {
|
||||
/**
|
||||
* Targets a room when broadcasting.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Socket} self
|
||||
* @param name
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public to(name: Room) {
|
||||
public to(name: Room): this {
|
||||
this._rooms.add(name);
|
||||
return this;
|
||||
}
|
||||
@@ -188,11 +199,11 @@ export class Socket extends EventEmitter {
|
||||
/**
|
||||
* Targets a room when broadcasting.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Socket} self
|
||||
* @param name
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public in(name: Room): Socket {
|
||||
public in(name: Room): this {
|
||||
this._rooms.add(name);
|
||||
return this;
|
||||
}
|
||||
@@ -200,24 +211,22 @@ export class Socket extends EventEmitter {
|
||||
/**
|
||||
* Sends a `message` event.
|
||||
*
|
||||
* @return {Socket} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public send(...args): Socket {
|
||||
args.unshift("message");
|
||||
this.emit.apply(this, args);
|
||||
public send(...args: readonly any[]): this {
|
||||
this.emit("message", ...args);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a `message` event.
|
||||
*
|
||||
* @return {Socket} self
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public write(...args): Socket {
|
||||
args.unshift("message");
|
||||
this.emit.apply(this, args);
|
||||
public write(...args: readonly any[]): this {
|
||||
this.emit("message", ...args);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -228,10 +237,13 @@ export class Socket extends EventEmitter {
|
||||
* @param {Object} opts - options
|
||||
* @private
|
||||
*/
|
||||
private packet(packet, opts: any = {}) {
|
||||
private packet(
|
||||
packet: Omit<Packet, "nsp"> & Partial<Pick<Packet, "nsp">>,
|
||||
opts: any = {}
|
||||
): void {
|
||||
packet.nsp = this.nsp.name;
|
||||
opts.compress = false !== opts.compress;
|
||||
this.client._packet(packet, opts);
|
||||
this.client._packet(packet as Packet, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,7 +295,11 @@ export class Socket extends EventEmitter {
|
||||
_onconnect(): void {
|
||||
debug("socket connected - writing packet");
|
||||
this.join(this.id);
|
||||
this.packet({ type: PacketType.CONNECT, data: { sid: this.id } });
|
||||
if (this.conn.protocol === 3) {
|
||||
this.packet({ type: PacketType.CONNECT });
|
||||
} else {
|
||||
this.packet({ type: PacketType.CONNECT, data: { sid: this.id } });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -292,7 +308,7 @@ export class Socket extends EventEmitter {
|
||||
* @param {Object} packet
|
||||
* @private
|
||||
*/
|
||||
_onpacket(packet) {
|
||||
_onpacket(packet: Packet): void {
|
||||
debug("got packet %j", packet);
|
||||
switch (packet.type) {
|
||||
case PacketType.EVENT:
|
||||
@@ -323,10 +339,10 @@ export class Socket extends EventEmitter {
|
||||
/**
|
||||
* Called upon event packet.
|
||||
*
|
||||
* @param {Object} packet - packet object
|
||||
* @param {Packet} packet - packet object
|
||||
* @private
|
||||
*/
|
||||
private onevent(packet): void {
|
||||
private onevent(packet: Packet): void {
|
||||
const args = packet.data || [];
|
||||
debug("emitting event %j", args);
|
||||
|
||||
@@ -341,7 +357,7 @@ export class Socket extends EventEmitter {
|
||||
listener.apply(this, args);
|
||||
}
|
||||
}
|
||||
super.emit.apply(this, args);
|
||||
this.dispatch(args);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -350,10 +366,10 @@ export class Socket extends EventEmitter {
|
||||
* @param {Number} id - packet id
|
||||
* @private
|
||||
*/
|
||||
private ack(id: number) {
|
||||
private ack(id: number): () => void {
|
||||
const self = this;
|
||||
let sent = false;
|
||||
return function() {
|
||||
return function () {
|
||||
// prevent double callbacks
|
||||
if (sent) return;
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
@@ -362,7 +378,7 @@ export class Socket extends EventEmitter {
|
||||
self.packet({
|
||||
id: id,
|
||||
type: PacketType.ACK,
|
||||
data: args
|
||||
data: args,
|
||||
});
|
||||
|
||||
sent = true;
|
||||
@@ -374,12 +390,12 @@ export class Socket extends EventEmitter {
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private onack(packet): void {
|
||||
const ack = this.acks.get(packet.id);
|
||||
private onack(packet: Packet): void {
|
||||
const ack = this.acks.get(packet.id!);
|
||||
if ("function" == typeof ack) {
|
||||
debug("calling ack %s with %j", packet.id, packet.data);
|
||||
ack.apply(this, packet.data);
|
||||
this.acks.delete(packet.id);
|
||||
this.acks.delete(packet.id!);
|
||||
} else {
|
||||
debug("bad ack %s", packet.id);
|
||||
}
|
||||
@@ -417,7 +433,7 @@ export class Socket extends EventEmitter {
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onclose(reason: string) {
|
||||
_onclose(reason: string): this | undefined {
|
||||
if (!this.connected) return this;
|
||||
debug("closing socket - reason %s", reason);
|
||||
super.emit("disconnecting", reason);
|
||||
@@ -427,6 +443,7 @@ export class Socket extends EventEmitter {
|
||||
this.connected = false;
|
||||
this.disconnected = true;
|
||||
super.emit("disconnect", reason);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -436,7 +453,7 @@ export class Socket extends EventEmitter {
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_error(err) {
|
||||
_error(err): void {
|
||||
this.packet({ type: PacketType.CONNECT_ERROR, data: err });
|
||||
}
|
||||
|
||||
@@ -448,7 +465,7 @@ export class Socket extends EventEmitter {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
public disconnect(close = false): Socket {
|
||||
public disconnect(close = false): this {
|
||||
if (!this.connected) return this;
|
||||
if (close) {
|
||||
this.client._disconnect();
|
||||
@@ -466,7 +483,7 @@ export class Socket extends EventEmitter {
|
||||
* @return {Socket} self
|
||||
* @public
|
||||
*/
|
||||
public compress(compress: boolean): Socket {
|
||||
public compress(compress: boolean): this {
|
||||
this.flags.compress = compress;
|
||||
return this;
|
||||
}
|
||||
@@ -479,7 +496,7 @@ export class Socket extends EventEmitter {
|
||||
* @return {Socket} self
|
||||
* @public
|
||||
*/
|
||||
public get volatile(): Socket {
|
||||
public get volatile(): this {
|
||||
this.flags.volatile = true;
|
||||
return this;
|
||||
}
|
||||
@@ -491,7 +508,7 @@ export class Socket extends EventEmitter {
|
||||
* @return {Socket} self
|
||||
* @public
|
||||
*/
|
||||
public get broadcast(): Socket {
|
||||
public get broadcast(): this {
|
||||
this.flags.broadcast = true;
|
||||
return this;
|
||||
}
|
||||
@@ -502,11 +519,73 @@ export class Socket extends EventEmitter {
|
||||
* @return {Socket} self
|
||||
* @public
|
||||
*/
|
||||
public get local(): Socket {
|
||||
public get local(): this {
|
||||
this.flags.local = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch incoming event to socket listeners.
|
||||
*
|
||||
* @param {Array} event - event that will get emitted
|
||||
* @private
|
||||
*/
|
||||
private dispatch(event: [eventName: string, ...args: any[]]): void {
|
||||
debug("dispatching an event %j", event);
|
||||
this.run(event, (err) => {
|
||||
process.nextTick(() => {
|
||||
if (err) {
|
||||
return this._onerror(err);
|
||||
}
|
||||
super.emit.apply(this, event);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up socket middleware.
|
||||
*
|
||||
* @param {Function} fn - middleware function (event, next)
|
||||
* @return {Socket} self
|
||||
* @public
|
||||
*/
|
||||
public use(
|
||||
fn: (event: Array<any>, next: (err: Error) => void) => void
|
||||
): this {
|
||||
this.fns.push(fn);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the middleware for an incoming event.
|
||||
*
|
||||
* @param {Array} event - event that will get emitted
|
||||
* @param {Function} fn - last fn call in the middleware
|
||||
* @private
|
||||
*/
|
||||
private run(
|
||||
event: [eventName: string, ...args: any[]],
|
||||
fn: (err: Error | null) => void
|
||||
): void {
|
||||
const fns = this.fns.slice(0);
|
||||
if (!fns.length) return fn(null);
|
||||
|
||||
function run(i: number) {
|
||||
fns[i](event, function (err) {
|
||||
// upon error, short-circuit
|
||||
if (err) return fn(err);
|
||||
|
||||
// if no middleware left, summon callback
|
||||
if (!fns[i + 1]) return fn(null);
|
||||
|
||||
// go on to next
|
||||
run(i + 1);
|
||||
});
|
||||
}
|
||||
|
||||
run(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to the request that originated the underlying Engine.IO Socket.
|
||||
*
|
||||
@@ -539,7 +618,7 @@ export class Socket extends EventEmitter {
|
||||
* @param listener
|
||||
* @public
|
||||
*/
|
||||
public onAny(listener: (...args: any[]) => void): Socket {
|
||||
public onAny(listener: (...args: any[]) => void): this {
|
||||
this._anyListeners = this._anyListeners || [];
|
||||
this._anyListeners.push(listener);
|
||||
return this;
|
||||
@@ -552,7 +631,7 @@ export class Socket extends EventEmitter {
|
||||
* @param listener
|
||||
* @public
|
||||
*/
|
||||
public prependAny(listener: (...args: any[]) => void): Socket {
|
||||
public prependAny(listener: (...args: any[]) => void): this {
|
||||
this._anyListeners = this._anyListeners || [];
|
||||
this._anyListeners.unshift(listener);
|
||||
return this;
|
||||
@@ -564,7 +643,7 @@ export class Socket extends EventEmitter {
|
||||
* @param listener
|
||||
* @public
|
||||
*/
|
||||
public offAny(listener?: (...args: any[]) => void): Socket {
|
||||
public offAny(listener?: (...args: any[]) => void): this {
|
||||
if (!this._anyListeners) {
|
||||
return this;
|
||||
}
|
||||
|
||||
364
package-lock.json
generated
364
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "socket.io",
|
||||
"version": "3.0.4",
|
||||
"version": "3.1.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -362,8 +362,7 @@
|
||||
"@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==",
|
||||
"dev": true
|
||||
"integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg=="
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.33",
|
||||
@@ -413,15 +412,15 @@
|
||||
"integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q=="
|
||||
},
|
||||
"@types/mocha": {
|
||||
"version": "8.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.3.tgz",
|
||||
"integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==",
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.4.tgz",
|
||||
"integrity": "sha512-M4BwiTJjHmLq6kjON7ZoI2JMlBvpY3BYSdiP6s/qCT3jb1s9/DeJF0JELpAxiVSIxXDzfNKe+r7yedMIoLbknQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "14.14.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz",
|
||||
"integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg=="
|
||||
"version": "14.14.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.10.tgz",
|
||||
"integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ=="
|
||||
},
|
||||
"@types/qs": {
|
||||
"version": "6.9.5",
|
||||
@@ -463,6 +462,12 @@
|
||||
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
|
||||
"dev": true
|
||||
},
|
||||
"after": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
|
||||
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=",
|
||||
"dev": true
|
||||
},
|
||||
"aggregate-error": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
||||
@@ -536,6 +541,12 @@
|
||||
"sprintf-js": "~1.0.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==",
|
||||
"dev": true
|
||||
},
|
||||
"astral-regex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
|
||||
@@ -585,6 +596,12 @@
|
||||
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
||||
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
|
||||
},
|
||||
"blob": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
||||
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@@ -734,6 +751,12 @@
|
||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
|
||||
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
|
||||
},
|
||||
"component-inherit": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
|
||||
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=",
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
@@ -760,12 +783,6 @@
|
||||
"integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==",
|
||||
"dev": true
|
||||
},
|
||||
"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=",
|
||||
"dev": true
|
||||
},
|
||||
"cors": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||
@@ -787,11 +804,11 @@
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"decamelize": {
|
||||
@@ -843,43 +860,35 @@
|
||||
"dev": true
|
||||
},
|
||||
"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==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.0.tgz",
|
||||
"integrity": "sha512-vW7EAtn0HDQ4MtT5QbmCHF17TaYLONv2/JwdYsq9USPRZVM4zG7WB3k0Nc321z8EuSOlhGokrYlYx4176QhD0A==",
|
||||
"requires": {
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "~0.4.1",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~4.0.0",
|
||||
"ws": "^7.1.2"
|
||||
"ws": "~7.4.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==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.1.1.tgz",
|
||||
"integrity": "sha512-iYasV/EttP/2pLrdowe9G3zwlNIFhwny8VSIh+vPlMnYZqSzLsTzSLa9hFy015OrH1s4fzoYxeHjVkO8hSFKwg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base64-arraybuffer": "0.1.4",
|
||||
"component-emitter": "~1.3.0",
|
||||
"debug": "~4.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~4.0.1",
|
||||
"has-cors": "1.1.0",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"ws": "~7.2.1",
|
||||
"ws": "~7.4.2",
|
||||
"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==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"engine.io-parser": {
|
||||
@@ -909,9 +918,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"eslint": {
|
||||
"version": "7.12.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.12.1.tgz",
|
||||
"integrity": "sha512-HlMTEdr/LicJfN08LB3nM1rRYliDXOmfoO4vj39xN6BLpFzF00hbwBoqHk8UcJ2M/3nlARZWy/mslvGEuZFvsg==",
|
||||
"version": "7.14.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.14.0.tgz",
|
||||
"integrity": "sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
@@ -1122,12 +1131,6 @@
|
||||
"integrity": "sha1-sKWaDS7/VDdUTr8M6qYBWEHQm1s=",
|
||||
"dev": true
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -1146,6 +1149,12 @@
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||
"dev": true
|
||||
},
|
||||
"fast-safe-stringify": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
|
||||
"integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==",
|
||||
"dev": true
|
||||
},
|
||||
"file-entry-cache": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
|
||||
@@ -1215,13 +1224,13 @@
|
||||
}
|
||||
},
|
||||
"form-data": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
|
||||
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz",
|
||||
"integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.6",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
@@ -1335,6 +1344,15 @@
|
||||
"function-bind": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"has-binary2": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
|
||||
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isarray": "2.0.1"
|
||||
}
|
||||
},
|
||||
"has-cors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
|
||||
@@ -1397,6 +1415,12 @@
|
||||
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
|
||||
"dev": true
|
||||
},
|
||||
"indexof": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
|
||||
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
@@ -1462,9 +1486,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
|
||||
"dev": true
|
||||
},
|
||||
"isexe": {
|
||||
@@ -1758,9 +1782,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"version": "2.4.6",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
|
||||
"integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==",
|
||||
"dev": true
|
||||
},
|
||||
"mime-db": {
|
||||
@@ -2083,15 +2107,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"prettier": {
|
||||
"version": "1.19.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
|
||||
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
|
||||
"dev": true
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz",
|
||||
"integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==",
|
||||
"dev": true
|
||||
},
|
||||
"process-on-spawn": {
|
||||
@@ -2122,18 +2140,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"regexpp": {
|
||||
@@ -2239,33 +2253,125 @@
|
||||
}
|
||||
},
|
||||
"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=="
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz",
|
||||
"integrity": "sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg=="
|
||||
},
|
||||
"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==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-3.1.1.tgz",
|
||||
"integrity": "sha512-BLgIuCjI7Sf3mDHunKddX9zKR/pbkP7IACM3sJS3jha+zJ6/pGKRV6Fz5XSBHCfUs9YzT8kYIqNwOOuFNLtnYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/component-emitter": "^1.2.10",
|
||||
"backo2": "~1.0.2",
|
||||
"component-emitter": "~1.3.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-client": "~4.1.0",
|
||||
"parseuri": "0.0.6",
|
||||
"socket.io-parser": "~4.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"socket.io-parser": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
|
||||
"integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/component-emitter": "^1.2.10",
|
||||
"component-emitter": "~1.3.0",
|
||||
"debug": "~4.3.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"socket.io-client-v2": {
|
||||
"version": "npm:socket.io-client@2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz",
|
||||
"integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"backo2": "1.0.2",
|
||||
"component-bind": "1.0.0",
|
||||
"component-emitter": "~1.3.0",
|
||||
"debug": "~4.1.0",
|
||||
"engine.io-client": "~4.0.0",
|
||||
"debug": "~3.1.0",
|
||||
"engine.io-client": "~3.5.0",
|
||||
"has-binary2": "~1.0.2",
|
||||
"indexof": "0.0.1",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"socket.io-parser": "~4.0.1"
|
||||
"socket.io-parser": "~3.3.0",
|
||||
"to-array": "0.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"engine.io-client": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.0.tgz",
|
||||
"integrity": "sha512-12wPRfMrugVw/DNyJk34GQ5vIVArEcVMXWugQGGuw2XxUSztFNmJggZmv8IZlLyEdnpO1QB9LkcjeWewO2vxtA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"component-emitter": "~1.3.0",
|
||||
"component-inherit": "0.0.3",
|
||||
"debug": "~3.1.0",
|
||||
"engine.io-parser": "~2.2.0",
|
||||
"has-cors": "1.1.0",
|
||||
"indexof": "0.0.1",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"ws": "~7.4.2",
|
||||
"xmlhttprequest-ssl": "~1.5.4",
|
||||
"yeast": "0.1.2"
|
||||
}
|
||||
},
|
||||
"engine.io-parser": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
|
||||
"integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"after": "0.8.2",
|
||||
"arraybuffer.slice": "~0.0.7",
|
||||
"base64-arraybuffer": "0.1.4",
|
||||
"blob": "0.0.5",
|
||||
"has-binary2": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||
"dev": true
|
||||
},
|
||||
"socket.io-parser": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz",
|
||||
"integrity": "sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"component-emitter": "~1.3.0",
|
||||
"debug": "~3.1.0",
|
||||
"isarray": "2.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==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.3.tgz",
|
||||
"integrity": "sha512-m4ybFiP4UYVORRt7jcdqf8UWx+ywVdAqqsJyruXxAdD3Sv6MDemijWij34mOWdMJ55bEdIb9jACBhxUgNK6sxw==",
|
||||
"requires": {
|
||||
"@types/component-emitter": "^1.2.10",
|
||||
"component-emitter": "~1.3.0",
|
||||
"debug": "~4.1.0"
|
||||
"debug": "~4.3.1"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
@@ -2341,12 +2447,20 @@
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
"safe-buffer": "~5.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
@@ -2371,42 +2485,32 @@
|
||||
"dev": true
|
||||
},
|
||||
"superagent": {
|
||||
"version": "3.8.3",
|
||||
"resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz",
|
||||
"integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz",
|
||||
"integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"component-emitter": "^1.2.0",
|
||||
"cookiejar": "^2.1.0",
|
||||
"debug": "^3.1.0",
|
||||
"extend": "^3.0.0",
|
||||
"form-data": "^2.3.1",
|
||||
"formidable": "^1.2.0",
|
||||
"methods": "^1.1.1",
|
||||
"mime": "^1.4.1",
|
||||
"qs": "^6.5.1",
|
||||
"readable-stream": "^2.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
||||
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
}
|
||||
"component-emitter": "^1.3.0",
|
||||
"cookiejar": "^2.1.2",
|
||||
"debug": "^4.1.1",
|
||||
"fast-safe-stringify": "^2.0.7",
|
||||
"form-data": "^3.0.0",
|
||||
"formidable": "^1.2.2",
|
||||
"methods": "^1.1.2",
|
||||
"mime": "^2.4.6",
|
||||
"qs": "^6.9.4",
|
||||
"readable-stream": "^3.6.0",
|
||||
"semver": "^7.3.2"
|
||||
}
|
||||
},
|
||||
"supertest": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/supertest/-/supertest-3.4.2.tgz",
|
||||
"integrity": "sha512-WZWbwceHUo2P36RoEIdXvmqfs47idNNZjCuJOqDz6rvtkk8ym56aU5oglORCpPeXGxT7l9rkJ41+O1lffQXYSA==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/supertest/-/supertest-6.0.1.tgz",
|
||||
"integrity": "sha512-8yDNdm+bbAN/jeDdXsRipbq9qMpVF7wRsbwLgsANHqdjPsCoecmlTuqEcLQMGpmojFBhxayZ0ckXmLXYq7e+0g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"methods": "^1.1.2",
|
||||
"superagent": "^3.8.3"
|
||||
"methods": "1.1.2",
|
||||
"superagent": "6.1.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
@@ -2447,6 +2551,12 @@
|
||||
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
|
||||
"dev": true
|
||||
},
|
||||
"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=",
|
||||
"dev": true
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
@@ -2499,9 +2609,9 @@
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
|
||||
"integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz",
|
||||
"integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==",
|
||||
"dev": true
|
||||
},
|
||||
"uri-js": {
|
||||
@@ -2645,9 +2755,9 @@
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
|
||||
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
|
||||
"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",
|
||||
|
||||
36
package.json
36
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "socket.io",
|
||||
"version": "3.0.4",
|
||||
"version": "3.1.1",
|
||||
"description": "node.js realtime framework server",
|
||||
"keywords": [
|
||||
"realtime",
|
||||
@@ -14,8 +14,15 @@
|
||||
"files": [
|
||||
"dist/",
|
||||
"client-dist/",
|
||||
"wrapper.mjs"
|
||||
"wrapper.mjs",
|
||||
"!**/*.tsbuildinfo"
|
||||
],
|
||||
"directories": {
|
||||
"doc": "docs/",
|
||||
"example": "example/",
|
||||
"lib": "lib/",
|
||||
"test": "test/"
|
||||
},
|
||||
"type": "commonjs",
|
||||
"main": "./dist/index.js",
|
||||
"exports": {
|
||||
@@ -38,29 +45,30 @@
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.4.0",
|
||||
"@types/cors": "^2.8.8",
|
||||
"@types/node": "^14.14.7",
|
||||
"@types/node": "^14.14.10",
|
||||
"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"
|
||||
"debug": "~4.3.1",
|
||||
"engine.io": "~4.1.0",
|
||||
"socket.io-adapter": "~2.1.0",
|
||||
"socket.io-parser": "~4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^8.0.3",
|
||||
"@types/mocha": "^8.0.4",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^7.9.0",
|
||||
"eslint": "^7.14.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"expect.js": "0.3.1",
|
||||
"mocha": "^3.5.3",
|
||||
"nyc": "^15.1.0",
|
||||
"prettier": "^1.19.1",
|
||||
"prettier": "^2.2.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"socket.io-client": "3.0.4",
|
||||
"superagent": "^3.8.2",
|
||||
"supertest": "^3.0.0",
|
||||
"socket.io-client": "3.1.1",
|
||||
"socket.io-client-v2": "npm:socket.io-client@^2.4.0",
|
||||
"superagent": "^6.1.0",
|
||||
"supertest": "^6.0.1",
|
||||
"ts-node": "^9.0.0",
|
||||
"typescript": "^4.0.3"
|
||||
"typescript": "^4.1.2"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,15 +3,15 @@ const i = expect.stringify;
|
||||
|
||||
// add support for Set/Map
|
||||
const contain = expect.Assertion.prototype.contain;
|
||||
expect.Assertion.prototype.contain = function(...args) {
|
||||
expect.Assertion.prototype.contain = function (...args) {
|
||||
if (typeof this.obj === "object") {
|
||||
args.forEach(obj => {
|
||||
args.forEach((obj) => {
|
||||
this.assert(
|
||||
this.obj.has(obj),
|
||||
function() {
|
||||
function () {
|
||||
return "expected " + i(this.obj) + " to contain " + i(obj);
|
||||
},
|
||||
function() {
|
||||
function () {
|
||||
return "expected " + i(this.obj) + " to not contain " + i(obj);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -4,7 +4,17 @@
|
||||
"target": "es2017",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"esModuleInterop": true
|
||||
"esModuleInterop": true,
|
||||
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"noImplicitThis": false,
|
||||
"strictPropertyInitialization": false,
|
||||
"noImplicitReturns": true,
|
||||
"importsNotUsedAsValues": "error",
|
||||
},
|
||||
"include": [
|
||||
"./lib/**/*"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import io from "./dist/index.js";
|
||||
|
||||
export const Server = io.Server;
|
||||
export const {Server, Namespace, Socket} = io;
|
||||
|
||||
Reference in New Issue
Block a user