From c7998d544688ce2d3e793e7b5af4dc37b67dfe1f Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 14 Oct 2020 22:59:58 +0200 Subject: [PATCH] refactor: add Manager and Socket typings --- build/index.d.ts | 13 - build/index.js | 81 ----- build/manager.d.ts | 167 --------- build/manager.js | 459 ------------------------- build/on.d.ts | 3 - build/on.js | 12 - build/socket.d.ts | 148 -------- build/socket.js | 388 --------------------- build/url.d.ts | 9 - build/url.js | 68 ---- lib/index.ts | 22 +- lib/manager.ts | 471 ++++++++++++++++++++------ lib/socket.ts | 150 ++++---- lib/url.ts | 10 +- package.json | 15 +- test/{connection.js => connection.ts} | 62 ++-- test/index.js | 6 +- test/{socket.js => socket.ts} | 4 +- test/{url.js => url.ts} | 7 +- tsconfig.json | 2 +- zuul.config.js | 26 +- 21 files changed, 532 insertions(+), 1591 deletions(-) delete mode 100644 build/index.d.ts delete mode 100644 build/index.js delete mode 100644 build/manager.d.ts delete mode 100644 build/manager.js delete mode 100644 build/on.d.ts delete mode 100644 build/on.js delete mode 100644 build/socket.d.ts delete mode 100644 build/socket.js delete mode 100644 build/url.d.ts delete mode 100644 build/url.js rename test/{connection.js => connection.ts} (93%) rename test/{socket.js => socket.ts} (98%) rename test/{url.js => url.ts} (96%) diff --git a/build/index.d.ts b/build/index.d.ts deleted file mode 100644 index 052d9b51f..000000000 --- a/build/index.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Protocol version. - * - * @api public - */ -export { protocol } from "socket.io-parser"; -/** - * Expose constructors for standalone build. - * - * @api public - */ -export { Manager } from "./manager"; -export { Socket } from "./socket"; diff --git a/build/index.js b/build/index.js deleted file mode 100644 index 594239761..000000000 --- a/build/index.js +++ /dev/null @@ -1,81 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Socket = exports.Manager = exports.protocol = void 0; -const url_1 = require("./url"); -const manager_1 = require("./manager"); -const debug = require("debug")("socket.io-client"); -/** - * Module exports. - */ -module.exports = exports = lookup; -/** - * Managers cache. - */ -const cache = (exports.managers = {}); -/** - * Looks up an existing `Manager` for multiplexing. - * If the user summons: - * - * `io('http://localhost/a');` - * `io('http://localhost/b');` - * - * We reuse the existing instance based on same scheme/port/host, - * and we initialize sockets for each namespace. - * - * @api public - */ -function lookup(uri, opts) { - if (typeof uri === "object") { - opts = uri; - uri = undefined; - } - opts = opts || {}; - const parsed = url_1.url(uri); - const source = parsed.source; - const id = parsed.id; - const path = parsed.path; - const sameNamespace = cache[id] && path in cache[id].nsps; - const newConnection = opts.forceNew || - opts["force new connection"] || - false === opts.multiplex || - sameNamespace; - let io; - if (newConnection) { - debug("ignoring socket cache for %s", source); - io = new manager_1.Manager(source, opts); - } - else { - if (!cache[id]) { - debug("new io instance for %s", source); - cache[id] = new manager_1.Manager(source, opts); - } - io = cache[id]; - } - if (parsed.query && !opts.query) { - opts.query = parsed.query; - } - return io.socket(parsed.path, opts); -} -/** - * Protocol version. - * - * @api public - */ -var socket_io_parser_1 = require("socket.io-parser"); -Object.defineProperty(exports, "protocol", { enumerable: true, get: function () { return socket_io_parser_1.protocol; } }); -/** - * `connect`. - * - * @param {String} uri - * @api public - */ -exports.connect = lookup; -/** - * Expose constructors for standalone build. - * - * @api public - */ -var manager_2 = require("./manager"); -Object.defineProperty(exports, "Manager", { enumerable: true, get: function () { return manager_2.Manager; } }); -var socket_1 = require("./socket"); -Object.defineProperty(exports, "Socket", { enumerable: true, get: function () { return socket_1.Socket; } }); diff --git a/build/manager.d.ts b/build/manager.d.ts deleted file mode 100644 index 465697691..000000000 --- a/build/manager.d.ts +++ /dev/null @@ -1,167 +0,0 @@ -import Emitter from "component-emitter"; -export declare class Manager extends Emitter { - autoConnect: boolean; - readyState: "opening" | "open" | "closed"; - reconnecting: boolean; - private readonly uri; - private readonly opts; - private nsps; - private subs; - private backoff; - private _reconnection; - private _reconnectionAttempts; - private _reconnectionDelay; - private _randomizationFactor; - private _reconnectionDelayMax; - private _timeout; - private connecting; - private encoder; - private decoder; - private engine; - private skipReconnect; - /** - * `Manager` constructor. - * - * @param {String} engine instance or engine uri/opts - * @param {Object} options - * @api public - */ - constructor(uri: any, opts: any); - /** - * Sets the `reconnection` config. - * - * @param {Boolean} true/false if it should automatically reconnect - * @return {Manager} self or value - * @api public - */ - reconnection(v?: any): boolean | this; - /** - * Sets the reconnection attempts config. - * - * @param {Number} max reconnection attempts before giving up - * @return {Manager} self or value - * @api public - */ - reconnectionAttempts(v?: any): number | this; - /** - * Sets the delay between reconnections. - * - * @param {Number} delay - * @return {Manager} self or value - * @api public - */ - reconnectionDelay(v?: any): number | this; - randomizationFactor(v?: any): number | this; - /** - * Sets the maximum delay between reconnections. - * - * @param {Number} delay - * @return {Manager} self or value - * @api public - */ - reconnectionDelayMax(v?: any): number | this; - /** - * Sets the connection timeout. `false` to disable - * - * @return {Manager} self or value - * @api public - */ - timeout(v?: any): any; - /** - * Starts trying to reconnect if reconnection is enabled and we have not - * started reconnecting yet - * - * @api private - */ - maybeReconnectOnOpen(): void; - /** - * Sets the current transport `socket`. - * - * @param {Function} optional, callback - * @return {Manager} self - * @api public - */ - open(fn?: any, opts?: any): Manager; - connect(fn: any, opts: any): Manager; - /** - * Called upon transport open. - * - * @api private - */ - onopen(): void; - /** - * Called upon a ping. - * - * @api private - */ - onping(): void; - /** - * Called with data. - * - * @api private - */ - ondata(data: any): void; - /** - * Called when parser fully decodes a packet. - * - * @api private - */ - ondecoded(packet: any): void; - /** - * Called upon socket error. - * - * @api private - */ - onerror(err: any): void; - /** - * Creates a new socket for the given `nsp`. - * - * @return {Socket} - * @api public - */ - socket(nsp: any, opts: any): any; - /** - * Called upon a socket close. - * - * @param {Socket} socket - */ - destroy(socket: any): void; - /** - * Writes a packet. - * - * @param {Object} packet - * @api private - */ - packet(packet: any): void; - /** - * Clean up transport subscriptions and packet buffer. - * - * @api private - */ - cleanup(): void; - /** - * Close the current socket. - * - * @api private - */ - close(): void; - disconnect(): void; - /** - * Called upon engine close. - * - * @api private - */ - onclose(reason: any): void; - /** - * Attempt a reconnection. - * - * @api private - */ - reconnect(): this; - /** - * Called upon successful reconnect. - * - * @api private - */ - onreconnect(): void; -} diff --git a/build/manager.js b/build/manager.js deleted file mode 100644 index 8eb0aa33d..000000000 --- a/build/manager.js +++ /dev/null @@ -1,459 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Manager = void 0; -const engine_io_client_1 = __importDefault(require("engine.io-client")); -const socket_1 = require("./socket"); -const component_emitter_1 = __importDefault(require("component-emitter")); -const parser = __importStar(require("socket.io-parser")); -const on_1 = require("./on"); -const component_bind_1 = __importDefault(require("component-bind")); -const indexof_1 = __importDefault(require("indexof")); -const backo2_1 = __importDefault(require("backo2")); -const debug = require("debug")("socket.io-client:manager"); -/** - * IE6+ hasOwnProperty - */ -const has = Object.prototype.hasOwnProperty; -class Manager extends component_emitter_1.default { - /** - * `Manager` constructor. - * - * @param {String} engine instance or engine uri/opts - * @param {Object} options - * @api public - */ - constructor(uri, opts) { - super(); - this.nsps = {}; - this.subs = []; - this.connecting = []; - if (uri && "object" === typeof uri) { - opts = uri; - uri = undefined; - } - opts = opts || {}; - opts.path = opts.path || "/socket.io"; - this.opts = opts; - this.reconnection(opts.reconnection !== false); - this.reconnectionAttempts(opts.reconnectionAttempts || Infinity); - this.reconnectionDelay(opts.reconnectionDelay || 1000); - this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000); - this.randomizationFactor(opts.randomizationFactor || 0.5); - this.backoff = new backo2_1.default({ - min: this.reconnectionDelay(), - max: this.reconnectionDelayMax(), - jitter: this.randomizationFactor(), - }); - this.timeout(null == opts.timeout ? 20000 : opts.timeout); - this.readyState = "closed"; - this.uri = uri; - const _parser = opts.parser || parser; - this.encoder = new _parser.Encoder(); - this.decoder = new _parser.Decoder(); - this.autoConnect = opts.autoConnect !== false; - if (this.autoConnect) - this.open(); - } - /** - * Sets the `reconnection` config. - * - * @param {Boolean} true/false if it should automatically reconnect - * @return {Manager} self or value - * @api public - */ - reconnection(v) { - if (!arguments.length) - return this._reconnection; - this._reconnection = !!v; - return this; - } - /** - * Sets the reconnection attempts config. - * - * @param {Number} max reconnection attempts before giving up - * @return {Manager} self or value - * @api public - */ - reconnectionAttempts(v) { - if (!arguments.length) - return this._reconnectionAttempts; - this._reconnectionAttempts = v; - return this; - } - /** - * Sets the delay between reconnections. - * - * @param {Number} delay - * @return {Manager} self or value - * @api public - */ - reconnectionDelay(v) { - if (!arguments.length) - return this._reconnectionDelay; - this._reconnectionDelay = v; - this.backoff && this.backoff.setMin(v); - return this; - } - randomizationFactor(v) { - if (!arguments.length) - return this._randomizationFactor; - this._randomizationFactor = v; - this.backoff && this.backoff.setJitter(v); - return this; - } - /** - * Sets the maximum delay between reconnections. - * - * @param {Number} delay - * @return {Manager} self or value - * @api public - */ - reconnectionDelayMax(v) { - if (!arguments.length) - return this._reconnectionDelayMax; - this._reconnectionDelayMax = v; - this.backoff && this.backoff.setMax(v); - return this; - } - /** - * Sets the connection timeout. `false` to disable - * - * @return {Manager} self or value - * @api public - */ - timeout(v) { - if (!arguments.length) - return this._timeout; - this._timeout = v; - return this; - } - /** - * Starts trying to reconnect if reconnection is enabled and we have not - * started reconnecting yet - * - * @api private - */ - maybeReconnectOnOpen() { - // Only try to reconnect if it's the first time we're connecting - if (!this.reconnecting && - this._reconnection && - this.backoff.attempts === 0) { - // keeps reconnection from firing twice for the same reconnection loop - this.reconnect(); - } - } - /** - * Sets the current transport `socket`. - * - * @param {Function} optional, callback - * @return {Manager} self - * @api public - */ - open(fn, opts) { - debug("readyState %s", this.readyState); - if (~this.readyState.indexOf("open")) - return this; - debug("opening %s", this.uri); - this.engine = engine_io_client_1.default(this.uri, this.opts); - const socket = this.engine; - const self = this; - this.readyState = "opening"; - this.skipReconnect = false; - // emit `open` - const openSub = on_1.on(socket, "open", function () { - self.onopen(); - fn && fn(); - }); - // emit `connect_error` - const errorSub = on_1.on(socket, "error", (err) => { - debug("connect_error"); - self.cleanup(); - self.readyState = "closed"; - super.emit("connect_error", err); - if (fn) { - fn(err); - } - else { - // Only do this if there is no fn to handle the error - self.maybeReconnectOnOpen(); - } - }); - // emit `connect_timeout` - if (false !== this._timeout) { - const timeout = this._timeout; - debug("connect attempt will timeout after %d", timeout); - if (timeout === 0) { - openSub.destroy(); // prevents a race condition with the 'open' event - } - // set timer - const timer = setTimeout(() => { - debug("connect attempt timed out after %d", timeout); - openSub.destroy(); - socket.close(); - socket.emit("error", "timeout"); - super.emit("connect_error", new Error("timeout")); - }, timeout); - this.subs.push({ - destroy: function () { - clearTimeout(timer); - }, - }); - } - this.subs.push(openSub); - this.subs.push(errorSub); - return this; - } - connect(fn, opts) { - return this.open(fn, opts); - } - /** - * Called upon transport open. - * - * @api private - */ - onopen() { - debug("open"); - // clear old subs - this.cleanup(); - // mark as open - this.readyState = "open"; - super.emit("open"); - // add new subs - const socket = this.engine; - this.subs.push(on_1.on(socket, "data", component_bind_1.default(this, "ondata"))); - this.subs.push(on_1.on(socket, "ping", component_bind_1.default(this, "onping"))); - this.subs.push(on_1.on(socket, "error", component_bind_1.default(this, "onerror"))); - this.subs.push(on_1.on(socket, "close", component_bind_1.default(this, "onclose"))); - this.subs.push(on_1.on(this.decoder, "decoded", component_bind_1.default(this, "ondecoded"))); - } - /** - * Called upon a ping. - * - * @api private - */ - onping() { - super.emit("ping"); - } - /** - * Called with data. - * - * @api private - */ - ondata(data) { - this.decoder.add(data); - } - /** - * Called when parser fully decodes a packet. - * - * @api private - */ - ondecoded(packet) { - super.emit("packet", packet); - } - /** - * Called upon socket error. - * - * @api private - */ - onerror(err) { - debug("error", err); - super.emit("error", err); - } - /** - * Creates a new socket for the given `nsp`. - * - * @return {Socket} - * @api public - */ - socket(nsp, opts) { - let socket = this.nsps[nsp]; - if (!socket) { - socket = new socket_1.Socket(this, nsp, opts); - this.nsps[nsp] = socket; - var self = this; - socket.on("connecting", onConnecting); - if (this.autoConnect) { - // manually call here since connecting event is fired before listening - onConnecting(); - } - } - function onConnecting() { - if (!~indexof_1.default(self.connecting, socket)) { - self.connecting.push(socket); - } - } - return socket; - } - /** - * Called upon a socket close. - * - * @param {Socket} socket - */ - destroy(socket) { - const index = indexof_1.default(this.connecting, socket); - if (~index) - this.connecting.splice(index, 1); - if (this.connecting.length) - return; - this.close(); - } - /** - * Writes a packet. - * - * @param {Object} packet - * @api private - */ - packet(packet) { - debug("writing packet %j", packet); - if (packet.query && packet.type === 0) - packet.nsp += "?" + packet.query; - const encodedPackets = this.encoder.encode(packet); - for (let i = 0; i < encodedPackets.length; i++) { - this.engine.write(encodedPackets[i], packet.options); - } - } - /** - * Clean up transport subscriptions and packet buffer. - * - * @api private - */ - cleanup() { - debug("cleanup"); - const subsLength = this.subs.length; - for (let i = 0; i < subsLength; i++) { - const sub = this.subs.shift(); - sub.destroy(); - } - this.decoder.destroy(); - } - /** - * Close the current socket. - * - * @api private - */ - close() { - debug("disconnect"); - this.skipReconnect = true; - this.reconnecting = false; - if ("opening" === this.readyState) { - // `onclose` will not fire because - // an open event never happened - this.cleanup(); - } - this.backoff.reset(); - this.readyState = "closed"; - if (this.engine) - this.engine.close(); - } - disconnect() { - debug("disconnect"); - this.skipReconnect = true; - this.reconnecting = false; - if ("opening" === this.readyState) { - // `onclose` will not fire because - // an open event never happened - this.cleanup(); - } - this.backoff.reset(); - this.readyState = "closed"; - if (this.engine) - this.engine.close(); - } - /** - * Called upon engine close. - * - * @api private - */ - onclose(reason) { - debug("onclose"); - this.cleanup(); - this.backoff.reset(); - this.readyState = "closed"; - super.emit("close", reason); - if (this._reconnection && !this.skipReconnect) { - this.reconnect(); - } - } - /** - * Attempt a reconnection. - * - * @api private - */ - reconnect() { - if (this.reconnecting || this.skipReconnect) - return this; - const self = this; - if (this.backoff.attempts >= this._reconnectionAttempts) { - debug("reconnect failed"); - this.backoff.reset(); - super.emit("reconnect_failed"); - this.reconnecting = false; - } - else { - const delay = this.backoff.duration(); - debug("will wait %dms before reconnect attempt", delay); - this.reconnecting = true; - const timer = setTimeout(() => { - if (self.skipReconnect) - return; - debug("attempting reconnect"); - super.emit("reconnect_attempt", self.backoff.attempts); - super.emit("reconnecting", self.backoff.attempts); - // check again for the case socket closed in above events - if (self.skipReconnect) - return; - self.open((err) => { - if (err) { - debug("reconnect attempt error"); - self.reconnecting = false; - self.reconnect(); - super.emit("reconnect_error", err); - } - else { - debug("reconnect success"); - self.onreconnect(); - } - }); - }, delay); - this.subs.push({ - destroy: function () { - clearTimeout(timer); - }, - }); - } - } - /** - * Called upon successful reconnect. - * - * @api private - */ - onreconnect() { - const attempt = this.backoff.attempts; - this.reconnecting = false; - this.backoff.reset(); - super.emit("reconnect", attempt); - } -} -exports.Manager = Manager; diff --git a/build/on.d.ts b/build/on.d.ts deleted file mode 100644 index add15fead..000000000 --- a/build/on.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -export declare function on(obj: any, ev: any, fn: any): { - destroy: () => void; -}; diff --git a/build/on.js b/build/on.js deleted file mode 100644 index c18ba5af5..000000000 --- a/build/on.js +++ /dev/null @@ -1,12 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.on = void 0; -function on(obj, ev, fn) { - obj.on(ev, fn); - return { - destroy: function () { - obj.removeListener(ev, fn); - }, - }; -} -exports.on = on; diff --git a/build/socket.d.ts b/build/socket.d.ts deleted file mode 100644 index 743e100f9..000000000 --- a/build/socket.d.ts +++ /dev/null @@ -1,148 +0,0 @@ -import Emitter from "component-emitter"; -import { Manager } from "./manager"; -export declare class Socket extends Emitter { - readonly io: Manager; - id: string; - connected: boolean; - disconnected: boolean; - private readonly nsp; - private readonly auth; - private ids; - private acks; - private receiveBuffer; - private sendBuffer; - private flags; - private subs; - /** - * `Socket` constructor. - * - * @api public - */ - constructor(io: any, nsp: any, opts: any); - /** - * Subscribe to open, close and packet events - * - * @api private - */ - subEvents(): void; - /** - * "Opens" the socket. - * - * @api public - */ - open(): this; - connect(): this; - /** - * Sends a `message` event. - * - * @return {Socket} self - * @api public - */ - send(): this; - /** - * Override `emit`. - * If the event is in `events`, it's emitted normally. - * - * @param {String} event name - * @return {Socket} self - * @api public - */ - emit(ev: any): this; - /** - * Sends a packet. - * - * @param {Object} packet - * @api private - */ - packet(packet: any): void; - /** - * Called upon engine `open`. - * - * @api private - */ - onopen(): void; - /** - * Called upon engine `close`. - * - * @param {String} reason - * @api private - */ - onclose(reason: any): void; - /** - * Called with socket packet. - * - * @param {Object} packet - * @api private - */ - onpacket(packet: any): void; - /** - * Called upon a server event. - * - * @param {Object} packet - * @api private - */ - onevent(packet: any): void; - /** - * Produces an ack callback to emit with an event. - * - * @api private - */ - ack(id: any): () => void; - /** - * Called upon a server acknowlegement. - * - * @param {Object} packet - * @api private - */ - onack(packet: any): void; - /** - * Called upon server connect. - * - * @api private - */ - onconnect(id: string): void; - /** - * Emit buffered events (received and emitted). - * - * @api private - */ - emitBuffered(): void; - /** - * Called upon server disconnect. - * - * @api private - */ - ondisconnect(): void; - /** - * Called upon forced client/server side disconnections, - * this method ensures the manager stops tracking us and - * that reconnections don't get triggered for this. - * - * @api private. - */ - destroy(): void; - /** - * Disconnects the socket manually. - * - * @return {Socket} self - * @api public - */ - close(): this; - disconnect(): this; - /** - * Sets the compress flag. - * - * @param {Boolean} if `true`, compresses the sending data - * @return {Socket} self - * @api public - */ - compress(compress: any): this; - /** - * Sets the binary flag - * - * @param {Boolean} whether the emitted data contains binary - * @return {Socket} self - * @api public - */ - binary(binary: any): this; -} diff --git a/build/socket.js b/build/socket.js deleted file mode 100644 index b59460bde..000000000 --- a/build/socket.js +++ /dev/null @@ -1,388 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Socket = void 0; -const socket_io_parser_1 = require("socket.io-parser"); -const component_emitter_1 = __importDefault(require("component-emitter")); -const to_array_1 = __importDefault(require("to-array")); -const on_1 = require("./on"); -const component_bind_1 = __importDefault(require("component-bind")); -const has_binary2_1 = __importDefault(require("has-binary2")); -const debug = require("debug")("socket.io-client:socket"); -/** - * Internal events. - * These events can't be emitted by the user. - * - * @api private - */ -const RESERVED_EVENTS = { - connect: 1, - disconnect: 1, - disconnecting: 1, - error: 1, - // EventEmitter reserved events: https://nodejs.org/api/events.html#events_event_newlistener - newListener: 1, - removeListener: 1, -}; -class Socket extends component_emitter_1.default { - /** - * `Socket` constructor. - * - * @api public - */ - constructor(io, nsp, opts) { - super(); - this.ids = 0; - this.acks = {}; - this.receiveBuffer = []; - this.sendBuffer = []; - this.flags = {}; - this.io = io; - this.nsp = nsp; - this.ids = 0; - this.acks = {}; - this.receiveBuffer = []; - this.sendBuffer = []; - this.connected = false; - this.disconnected = true; - this.flags = {}; - if (opts && opts.auth) { - this.auth = opts.auth; - } - if (this.io.autoConnect) - this.open(); - } - /** - * Subscribe to open, close and packet events - * - * @api private - */ - subEvents() { - if (this.subs) - return; - const io = this.io; - this.subs = [ - on_1.on(io, "open", component_bind_1.default(this, "onopen")), - on_1.on(io, "packet", component_bind_1.default(this, "onpacket")), - on_1.on(io, "close", component_bind_1.default(this, "onclose")), - ]; - } - /** - * "Opens" the socket. - * - * @api public - */ - open() { - if (this.connected) - return this; - this.subEvents(); - if (!this.io.reconnecting) - this.io.open(); // ensure open - if ("open" === this.io.readyState) - this.onopen(); - return this; - } - connect() { - if (this.connected) - return this; - this.subEvents(); - if (!this.io.reconnecting) - this.io.open(); // ensure open - if ("open" === this.io.readyState) - this.onopen(); - return this; - } - /** - * Sends a `message` event. - * - * @return {Socket} self - * @api public - */ - send() { - const args = to_array_1.default(arguments); - args.unshift("message"); - this.emit.apply(this, args); - return this; - } - /** - * Override `emit`. - * If the event is in `events`, it's emitted normally. - * - * @param {String} event name - * @return {Socket} self - * @api public - */ - emit(ev) { - if (RESERVED_EVENTS.hasOwnProperty(ev)) { - throw new Error('"' + ev + '" is a reserved event name'); - } - const args = to_array_1.default(arguments); - const packet = { - type: (this.flags.binary !== undefined ? this.flags.binary : has_binary2_1.default(args)) - ? socket_io_parser_1.PacketType.BINARY_EVENT - : socket_io_parser_1.PacketType.EVENT, - data: args, - }; - packet.options = {}; - packet.options.compress = !this.flags || false !== this.flags.compress; - // event ack callback - if ("function" === typeof args[args.length - 1]) { - debug("emitting packet with ack id %d", this.ids); - this.acks[this.ids] = args.pop(); - packet.id = this.ids++; - } - if (this.connected) { - this.packet(packet); - } - else { - this.sendBuffer.push(packet); - } - this.flags = {}; - return this; - } - /** - * Sends a packet. - * - * @param {Object} packet - * @api private - */ - packet(packet) { - packet.nsp = this.nsp; - this.io.packet(packet); - } - /** - * Called upon engine `open`. - * - * @api private - */ - onopen() { - debug("transport is open - connecting"); - if (typeof this.auth == "function") { - this.auth((data) => { - this.packet({ type: socket_io_parser_1.PacketType.CONNECT, data }); - }); - } - else { - this.packet({ type: socket_io_parser_1.PacketType.CONNECT, data: this.auth }); - } - } - /** - * Called upon engine `close`. - * - * @param {String} reason - * @api private - */ - onclose(reason) { - debug("close (%s)", reason); - this.connected = false; - this.disconnected = true; - delete this.id; - super.emit("disconnect", reason); - } - /** - * Called with socket packet. - * - * @param {Object} packet - * @api private - */ - onpacket(packet) { - const sameNamespace = packet.nsp === this.nsp; - const rootNamespaceError = packet.type === socket_io_parser_1.PacketType.ERROR && packet.nsp === "/"; - if (!sameNamespace && !rootNamespaceError) - return; - switch (packet.type) { - case socket_io_parser_1.PacketType.CONNECT: - const id = packet.data.sid; - this.onconnect(id); - break; - case socket_io_parser_1.PacketType.EVENT: - this.onevent(packet); - break; - case socket_io_parser_1.PacketType.BINARY_EVENT: - this.onevent(packet); - break; - case socket_io_parser_1.PacketType.ACK: - this.onack(packet); - break; - case socket_io_parser_1.PacketType.BINARY_ACK: - this.onack(packet); - break; - case socket_io_parser_1.PacketType.DISCONNECT: - this.ondisconnect(); - break; - case socket_io_parser_1.PacketType.ERROR: - super.emit("error", packet.data); - break; - } - } - /** - * Called upon a server event. - * - * @param {Object} packet - * @api private - */ - onevent(packet) { - const args = packet.data || []; - debug("emitting event %j", args); - if (null != packet.id) { - debug("attaching ack callback to event"); - args.push(this.ack(packet.id)); - } - if (this.connected) { - super.emit.apply(this, args); - } - else { - this.receiveBuffer.push(args); - } - } - /** - * Produces an ack callback to emit with an event. - * - * @api private - */ - ack(id) { - const self = this; - let sent = false; - return function () { - // prevent double callbacks - if (sent) - return; - sent = true; - const args = to_array_1.default(arguments); - debug("sending ack %j", args); - self.packet({ - type: has_binary2_1.default(args) ? socket_io_parser_1.PacketType.BINARY_ACK : socket_io_parser_1.PacketType.ACK, - id: id, - data: args, - }); - }; - } - /** - * Called upon a server acknowlegement. - * - * @param {Object} packet - * @api private - */ - onack(packet) { - const ack = this.acks[packet.id]; - if ("function" === typeof ack) { - debug("calling ack %s with %j", packet.id, packet.data); - ack.apply(this, packet.data); - delete this.acks[packet.id]; - } - else { - debug("bad ack %s", packet.id); - } - } - /** - * Called upon server connect. - * - * @api private - */ - onconnect(id) { - this.id = id; - this.connected = true; - this.disconnected = false; - super.emit("connect"); - this.emitBuffered(); - } - /** - * Emit buffered events (received and emitted). - * - * @api private - */ - emitBuffered() { - for (let i = 0; i < this.receiveBuffer.length; i++) { - super.emit.apply(this, this.receiveBuffer[i]); - } - this.receiveBuffer = []; - for (let i = 0; i < this.sendBuffer.length; i++) { - this.packet(this.sendBuffer[i]); - } - this.sendBuffer = []; - } - /** - * Called upon server disconnect. - * - * @api private - */ - ondisconnect() { - debug("server disconnect (%s)", this.nsp); - this.destroy(); - this.onclose("io server disconnect"); - } - /** - * Called upon forced client/server side disconnections, - * this method ensures the manager stops tracking us and - * that reconnections don't get triggered for this. - * - * @api private. - */ - destroy() { - if (this.subs) { - // clean subscriptions to avoid reconnections - for (let i = 0; i < this.subs.length; i++) { - this.subs[i].destroy(); - } - this.subs = null; - } - this.io.destroy(this); - } - /** - * Disconnects the socket manually. - * - * @return {Socket} self - * @api public - */ - close() { - if (this.connected) { - debug("performing disconnect (%s)", this.nsp); - this.packet({ type: socket_io_parser_1.PacketType.DISCONNECT }); - } - // remove socket from pool - this.destroy(); - if (this.connected) { - // fire events - this.onclose("io client disconnect"); - } - return this; - } - disconnect() { - if (this.connected) { - debug("performing disconnect (%s)", this.nsp); - this.packet({ type: socket_io_parser_1.PacketType.DISCONNECT }); - } - // remove socket from pool - this.destroy(); - if (this.connected) { - // fire events - this.onclose("io client disconnect"); - } - return this; - } - /** - * Sets the compress flag. - * - * @param {Boolean} if `true`, compresses the sending data - * @return {Socket} self - * @api public - */ - compress(compress) { - this.flags.compress = compress; - return this; - } - /** - * Sets the binary flag - * - * @param {Boolean} whether the emitted data contains binary - * @return {Socket} self - * @api public - */ - binary(binary) { - this.flags.binary = binary; - return this; - } -} -exports.Socket = Socket; diff --git a/build/url.d.ts b/build/url.d.ts deleted file mode 100644 index 80b1cf729..000000000 --- a/build/url.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * URL parser. - * - * @param {String} url - * @param {Object} An object meant to mimic window.location. - * Defaults to window.location. - * @api public - */ -export declare function url(uri: any, loc?: any): any; diff --git a/build/url.js b/build/url.js deleted file mode 100644 index 79065cfb5..000000000 --- a/build/url.js +++ /dev/null @@ -1,68 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.url = void 0; -const parseuri_1 = __importDefault(require("parseuri")); -const debug = require("debug")("socket.io-client:url"); -/** - * URL parser. - * - * @param {String} url - * @param {Object} An object meant to mimic window.location. - * Defaults to window.location. - * @api public - */ -function url(uri, loc) { - let obj = uri; - // default to window.location - loc = loc || (typeof location !== "undefined" && location); - if (null == uri) - uri = loc.protocol + "//" + loc.host; - // relative path support - if ("string" === typeof uri) { - if ("/" === uri.charAt(0)) { - if ("/" === uri.charAt(1)) { - uri = loc.protocol + uri; - } - else { - uri = loc.host + uri; - } - } - if (!/^(https?|wss?):\/\//.test(uri)) { - debug("protocol-less url %s", uri); - if ("undefined" !== typeof loc) { - uri = loc.protocol + "//" + uri; - } - else { - uri = "https://" + uri; - } - } - // parse - debug("parse %s", uri); - obj = parseuri_1.default(uri); - } - // make sure we treat `localhost:80` and `localhost` equally - if (!obj.port) { - if (/^(http|ws)$/.test(obj.protocol)) { - obj.port = "80"; - } - else if (/^(http|ws)s$/.test(obj.protocol)) { - obj.port = "443"; - } - } - obj.path = obj.path || "/"; - const ipv6 = obj.host.indexOf(":") !== -1; - const host = ipv6 ? "[" + obj.host + "]" : obj.host; - // define unique id - obj.id = obj.protocol + "://" + host + ":" + obj.port; - // define href - obj.href = - obj.protocol + - "://" + - host + - (loc && loc.port === obj.port ? "" : ":" + obj.port); - return obj; -} -exports.url = url; diff --git a/lib/index.ts b/lib/index.ts index 3a07830b4..69cde969a 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,6 +1,6 @@ import { url } from "./url"; -import { Manager } from "./manager"; -import { Socket } from "./socket"; +import { Manager, ManagerOptions } from "./manager"; +import { Socket, SocketOptions } from "./socket"; const debug = require("debug")("socket.io-client"); @@ -25,10 +25,14 @@ const cache = (exports.managers = {}); * We reuse the existing instance based on same scheme/port/host, * and we initialize sockets for each namespace. * - * @api public + * @public */ - -function lookup(uri, opts): Socket { +function lookup(opts?: Partial): Socket; +function lookup( + uri: string, + opts?: Partial +): Socket; +function lookup(uri: any, opts?: any): Socket { if (typeof uri === "object") { opts = uri; uri = undefined; @@ -68,7 +72,7 @@ function lookup(uri, opts): Socket { /** * Protocol version. * - * @api public + * @public */ export { protocol } from "socket.io-parser"; @@ -77,7 +81,7 @@ export { protocol } from "socket.io-parser"; * `connect`. * * @param {String} uri - * @api public + * @public */ exports.connect = lookup; @@ -85,8 +89,8 @@ exports.connect = lookup; /** * Expose constructors for standalone build. * - * @api public + * @public */ export { Manager } from "./manager"; -export { Socket } from "./socket"; +export { lookup as io }; diff --git a/lib/manager.ts b/lib/manager.ts index 289a46d08..bace6061d 100644 --- a/lib/manager.ts +++ b/lib/manager.ts @@ -1,24 +1,271 @@ -import eio from "engine.io-client"; -import { Socket } from "./socket"; -import Emitter from "component-emitter"; +import * as eio from "engine.io-client"; +import { Socket, SocketOptions } from "./socket"; +import * as Emitter from "component-emitter"; import * as parser from "socket.io-parser"; import { Decoder, Encoder } from "socket.io-parser"; import { on } from "./on"; -import bind from "component-bind"; -import indexOf from "indexof"; -import Backoff from "backo2"; +import * as bind from "component-bind"; +import * as indexOf from "indexof"; +import * as Backoff from "backo2"; const debug = require("debug")("socket.io-client:manager"); -/** - * IE6+ hasOwnProperty - */ -const has = Object.prototype.hasOwnProperty; +interface EngineOptions { + /** + * The host that we're connecting to. Set from the URI passed when connecting + */ + host: string; + + /** + * The hostname for our connection. Set from the URI passed when connecting + */ + hostname: string; + + /** + * If this is a secure connection. Set from the URI passed when connecting + */ + secure: boolean; + + /** + * The port for our connection. Set from the URI passed when connecting + */ + port: string; + + /** + * Any query parameters in our uri. Set from the URI passed when connecting + */ + query: Object; + + /** + * `http.Agent` to use, defaults to `false` (NodeJS only) + */ + agent: string | boolean; + + /** + * Whether the client should try to upgrade the transport from + * long-polling to something better. + * @default true + */ + upgrade: boolean; + + /** + * Forces JSONP for polling transport. + */ + forceJSONP: boolean; + + /** + * Determines whether to use JSONP when necessary for polling. If + * disabled (by settings to false) an error will be emitted (saying + * "No transports available") if no other transports are available. + * If another transport is available for opening a connection (e.g. + * WebSocket) that transport will be used instead. + * @default true + */ + jsonp: boolean; + + /** + * Forces base 64 encoding for polling transport even when XHR2 + * responseType is available and WebSocket even if the used standard + * supports binary. + */ + forceBase64: boolean; + + /** + * Enables XDomainRequest for IE8 to avoid loading bar flashing with + * click sound. default to `false` because XDomainRequest has a flaw + * of not sending cookie. + * @default false + */ + enablesXDR: boolean; + + /** + * The param name to use as our timestamp key + * @default 't' + */ + timestampParam: string; + + /** + * Whether to add the timestamp with each transport request. Note: this + * is ignored if the browser is IE or Android, in which case requests + * are always stamped + * @default false + */ + timestampRequests: boolean; + + /** + * A list of transports to try (in order). Engine.io always attempts to + * connect directly with the first one, provided the feature detection test + * for it passes. + * @default ['polling','websocket'] + */ + transports: string[]; + + /** + * The port the policy server listens on + * @default 843 + */ + policyPost: number; + + /** + * If true and if the previous websocket connection to the server succeeded, + * the connection attempt will bypass the normal upgrade process and will + * initially try websocket. A connection attempt following a transport error + * will use the normal upgrade process. It is recommended you turn this on + * only when using SSL/TLS connections, or if you know that your network does + * not block websockets. + * @default false + */ + rememberUpgrade: boolean; + + /** + * Are we only interested in transports that support binary? + */ + onlyBinaryUpgrades: boolean; + + /** + * Transport options for Node.js client (headers etc) + */ + transportOptions: Object; + + /** + * (SSL) Certificate, Private key and CA certificates to use for SSL. + * Can be used in Node.js client environment to manually specify + * certificate information. + */ + pfx: string; + + /** + * (SSL) Private key to use for SSL. Can be used in Node.js client + * environment to manually specify certificate information. + */ + key: string; + + /** + * (SSL) A string or passphrase for the private key or pfx. Can be + * used in Node.js client environment to manually specify certificate + * information. + */ + passphrase: string; + + /** + * (SSL) Public x509 certificate to use. Can be used in Node.js client + * environment to manually specify certificate information. + */ + cert: string; + + /** + * (SSL) An authority certificate or array of authority certificates to + * check the remote host against.. Can be used in Node.js client + * environment to manually specify certificate information. + */ + ca: string | string[]; + + /** + * (SSL) A string describing the ciphers to use or exclude. Consult the + * [cipher format list] + * (http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT) for + * details on the format.. Can be used in Node.js client environment to + * manually specify certificate information. + */ + ciphers: string; + + /** + * (SSL) If true, the server certificate is verified against the list of + * supplied CAs. An 'error' event is emitted if verification fails. + * Verification happens at the connection level, before the HTTP request + * is sent. Can be used in Node.js client environment to manually specify + * certificate information. + */ + rejectUnauthorized: boolean; +} + +export interface ManagerOptions extends EngineOptions { + /** + * Should we force a new Manager for this connection? + * @default false + */ + forceNew: boolean; + + /** + * Should we multiplex our connection (reuse existing Manager) ? + * @default true + */ + multiplex: boolean; + + /** + * The path to get our client file from, in the case of the server + * serving it + * @default '/socket.io' + */ + path: string; + + /** + * Should we allow reconnections? + * @default true + */ + reconnection: boolean; + + /** + * How many reconnection attempts should we try? + * @default Infinity + */ + reconnectionAttempts: number; + + /** + * The time delay in milliseconds between reconnection attempts + * @default 1000 + */ + reconnectionDelay: number; + + /** + * The max time delay in milliseconds between reconnection attempts + * @default 5000 + */ + reconnectionDelayMax: number; + + /** + * Used in the exponential backoff jitter when reconnecting + * @default 0.5 + */ + randomizationFactor: number; + + /** + * The timeout in milliseconds for our connection attempt + * @default 20000 + */ + timeout: number; + + /** + * Should we automatically connect? + * @default true + */ + autoConnect: boolean; + + /** + * the parser to use. Defaults to an instance of the Parser that ships with socket.io. + */ + parser: any; +} export class Manager extends Emitter { - public autoConnect: boolean; - public readyState: "opening" | "open" | "closed"; - public reconnecting: boolean; + /** + * The Engine.IO client instance + * + * @public + */ + public engine: any; + /** + * @private + */ + _autoConnect: boolean; + /** + * @private + */ + _readyState: "opening" | "open" | "closed"; + /** + * @private + */ + _reconnecting: boolean; private readonly uri: string; private readonly opts: object; @@ -36,17 +283,18 @@ export class Manager extends Emitter { private connecting: Array = []; private encoder: Encoder; private decoder: Decoder; - private engine: any; private skipReconnect: boolean; /** * `Manager` constructor. * - * @param {String} engine instance or engine uri/opts - * @param {Object} options - * @api public + * @param {String} uri - engine instance or engine uri/opts + * @param {Object} opts - options + * @public */ - constructor(uri, opts) { + constructor(opts: Partial); + constructor(uri?: string, opts?: Partial); + constructor(uri?: any, opts?: any) { super(); if (uri && "object" === typeof uri) { opts = uri; @@ -67,23 +315,25 @@ export class Manager extends Emitter { jitter: this.randomizationFactor(), }); this.timeout(null == opts.timeout ? 20000 : opts.timeout); - this.readyState = "closed"; + this._readyState = "closed"; this.uri = uri; const _parser = opts.parser || parser; this.encoder = new _parser.Encoder(); this.decoder = new _parser.Decoder(); - this.autoConnect = opts.autoConnect !== false; - if (this.autoConnect) this.open(); + this._autoConnect = opts.autoConnect !== false; + if (this._autoConnect) this.open(); } /** * Sets the `reconnection` config. * - * @param {Boolean} true/false if it should automatically reconnect + * @param {Boolean} v - true/false if it should automatically reconnect * @return {Manager} self or value - * @api public + * @public */ - reconnection(v?) { + public reconnection(v: boolean): Manager; + public reconnection(): boolean; + public reconnection(v?: boolean): Manager | boolean { if (!arguments.length) return this._reconnection; this._reconnection = !!v; return this; @@ -92,12 +342,14 @@ export class Manager extends Emitter { /** * Sets the reconnection attempts config. * - * @param {Number} max reconnection attempts before giving up + * @param {Number} v - max reconnection attempts before giving up * @return {Manager} self or value - * @api public + * @public */ - reconnectionAttempts(v?) { - if (!arguments.length) return this._reconnectionAttempts; + public reconnectionAttempts(v: number): Manager; + public reconnectionAttempts(): number; + public reconnectionAttempts(v?: number): Manager | number { + if (v === undefined) return this._reconnectionAttempts; this._reconnectionAttempts = v; return this; } @@ -105,19 +357,30 @@ export class Manager extends Emitter { /** * Sets the delay between reconnections. * - * @param {Number} delay + * @param {Number} v - delay * @return {Manager} self or value - * @api public + * @public */ - reconnectionDelay(v?) { - if (!arguments.length) return this._reconnectionDelay; + public reconnectionDelay(v: number): Manager; + public reconnectionDelay(): number; + public reconnectionDelay(v?: number): Manager | number { + if (v === undefined) return this._reconnectionDelay; this._reconnectionDelay = v; this.backoff && this.backoff.setMin(v); return this; } - randomizationFactor(v?) { - if (!arguments.length) return this._randomizationFactor; + /** + * Sets the randomization factor + * + * @param {Number} v - the randomization factor + * @return {Manager} self or value + * @public + */ + public randomizationFactor(v: number): Manager; + public randomizationFactor(): number; + public randomizationFactor(v?: number): Manager | number { + if (v === undefined) return this._randomizationFactor; this._randomizationFactor = v; this.backoff && this.backoff.setJitter(v); return this; @@ -126,12 +389,14 @@ export class Manager extends Emitter { /** * Sets the maximum delay between reconnections. * - * @param {Number} delay + * @param {Number} v - delay * @return {Manager} self or value - * @api public + * @public */ - reconnectionDelayMax(v?) { - if (!arguments.length) return this._reconnectionDelayMax; + public reconnectionDelayMax(v: number): Manager; + public reconnectionDelayMax(): number; + public reconnectionDelayMax(v?: number): Manager | number { + if (v === undefined) return this._reconnectionDelayMax; this._reconnectionDelayMax = v; this.backoff && this.backoff.setMax(v); return this; @@ -141,9 +406,11 @@ export class Manager extends Emitter { * Sets the connection timeout. `false` to disable * * @return {Manager} self or value - * @api public + * @public */ - timeout(v?) { + public timeout(v: number | boolean): Manager; + public timeout(): number | boolean; + public timeout(v?: number | boolean): Manager | number | boolean { if (!arguments.length) return this._timeout; this._timeout = v; return this; @@ -153,12 +420,12 @@ export class Manager extends Emitter { * Starts trying to reconnect if reconnection is enabled and we have not * started reconnecting yet * - * @api private + * @private */ - maybeReconnectOnOpen() { + private maybeReconnectOnOpen() { // Only try to reconnect if it's the first time we're connecting if ( - !this.reconnecting && + !this._reconnecting && this._reconnection && this.backoff.attempts === 0 ) { @@ -170,19 +437,19 @@ export class Manager extends Emitter { /** * Sets the current transport `socket`. * - * @param {Function} optional, callback + * @param {Function} fn - optional, callback * @return {Manager} self - * @api public + * @public */ - open(fn?, opts?): Manager { - debug("readyState %s", this.readyState); - if (~this.readyState.indexOf("open")) return this; + public open(fn?: (err?: Error) => void): Manager { + debug("readyState %s", this._readyState); + if (~this._readyState.indexOf("open")) return this; debug("opening %s", this.uri); this.engine = eio(this.uri, this.opts); const socket = this.engine; const self = this; - this.readyState = "opening"; + this._readyState = "opening"; this.skipReconnect = false; // emit `open` @@ -195,7 +462,7 @@ export class Manager extends Emitter { const errorSub = on(socket, "error", (err) => { debug("connect_error"); self.cleanup(); - self.readyState = "closed"; + self._readyState = "closed"; super.emit("connect_error", err); if (fn) { fn(err); @@ -236,23 +503,29 @@ export class Manager extends Emitter { return this; } - connect(fn, opts): Manager { - return this.open(fn, opts); + /** + * Alias for open() + * + * @return {Manager} self + * @public + */ + public connect(fn?: (err?: Error) => void): Manager { + return this.open(fn); } /** * Called upon transport open. * - * @api private + * @private */ - onopen() { + private onopen() { debug("open"); // clear old subs this.cleanup(); // mark as open - this.readyState = "open"; + this._readyState = "open"; super.emit("open"); // add new subs @@ -267,36 +540,36 @@ export class Manager extends Emitter { /** * Called upon a ping. * - * @api private + * @private */ - onping() { + private onping() { super.emit("ping"); } /** * Called with data. * - * @api private + * @private */ - ondata(data) { + private ondata(data) { this.decoder.add(data); } /** * Called when parser fully decodes a packet. * - * @api private + * @private */ - ondecoded(packet) { + private ondecoded(packet) { super.emit("packet", packet); } /** * Called upon socket error. * - * @api private + * @private */ - onerror(err) { + private onerror(err) { debug("error", err); super.emit("error", err); } @@ -305,9 +578,9 @@ export class Manager extends Emitter { * Creates a new socket for the given `nsp`. * * @return {Socket} - * @api public + * @public */ - socket(nsp, opts) { + public socket(nsp: string, opts?: SocketOptions): Socket { let socket = this.nsps[nsp]; if (!socket) { socket = new Socket(this, nsp, opts); @@ -315,7 +588,7 @@ export class Manager extends Emitter { var self = this; socket.on("connecting", onConnecting); - if (this.autoConnect) { + if (this._autoConnect) { // manually call here since connecting event is fired before listening onConnecting(); } @@ -334,22 +607,23 @@ export class Manager extends Emitter { * Called upon a socket close. * * @param {Socket} socket + * @private */ - destroy(socket) { + _destroy(socket) { const index = indexOf(this.connecting, socket); if (~index) this.connecting.splice(index, 1); if (this.connecting.length) return; - this.close(); + this._close(); } /** * Writes a packet. * * @param {Object} packet - * @api private + * @private */ - packet(packet) { + _packet(packet) { debug("writing packet %j", packet); if (packet.query && packet.type === 0) packet.nsp += "?" + packet.query; @@ -362,9 +636,9 @@ export class Manager extends Emitter { /** * Clean up transport subscriptions and packet buffer. * - * @api private + * @private */ - cleanup() { + private cleanup() { debug("cleanup"); const subsLength = this.subs.length; @@ -379,47 +653,42 @@ export class Manager extends Emitter { /** * Close the current socket. * - * @api private + * @private */ - close() { + _close() { debug("disconnect"); this.skipReconnect = true; - this.reconnecting = false; - if ("opening" === this.readyState) { + this._reconnecting = false; + if ("opening" === this._readyState) { // `onclose` will not fire because // an open event never happened this.cleanup(); } this.backoff.reset(); - this.readyState = "closed"; + this._readyState = "closed"; if (this.engine) this.engine.close(); } - disconnect() { - debug("disconnect"); - this.skipReconnect = true; - this.reconnecting = false; - if ("opening" === this.readyState) { - // `onclose` will not fire because - // an open event never happened - this.cleanup(); - } - this.backoff.reset(); - this.readyState = "closed"; - if (this.engine) this.engine.close(); + /** + * Alias for close() + * + * @private + */ + private disconnect() { + return this._close(); } /** * Called upon engine close. * - * @api private + * @private */ - onclose(reason) { + private onclose(reason) { debug("onclose"); this.cleanup(); this.backoff.reset(); - this.readyState = "closed"; + this._readyState = "closed"; super.emit("close", reason); if (this._reconnection && !this.skipReconnect) { @@ -430,10 +699,10 @@ export class Manager extends Emitter { /** * Attempt a reconnection. * - * @api private + * @private */ - reconnect() { - if (this.reconnecting || this.skipReconnect) return this; + private reconnect() { + if (this._reconnecting || this.skipReconnect) return this; const self = this; @@ -441,12 +710,12 @@ export class Manager extends Emitter { debug("reconnect failed"); this.backoff.reset(); super.emit("reconnect_failed"); - this.reconnecting = false; + this._reconnecting = false; } else { const delay = this.backoff.duration(); debug("will wait %dms before reconnect attempt", delay); - this.reconnecting = true; + this._reconnecting = true; const timer = setTimeout(() => { if (self.skipReconnect) return; @@ -460,7 +729,7 @@ export class Manager extends Emitter { self.open((err) => { if (err) { debug("reconnect attempt error"); - self.reconnecting = false; + self._reconnecting = false; self.reconnect(); super.emit("reconnect_error", err); } else { @@ -481,11 +750,11 @@ export class Manager extends Emitter { /** * Called upon successful reconnect. * - * @api private + * @private */ - onreconnect() { + private onreconnect() { const attempt = this.backoff.attempts; - this.reconnecting = false; + this._reconnecting = false; this.backoff.reset(); super.emit("reconnect", attempt); } diff --git a/lib/socket.ts b/lib/socket.ts index 832c440e1..75c748c86 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -1,18 +1,22 @@ -import { PacketType } from "socket.io-parser"; -import Emitter from "component-emitter"; -import toArray from "to-array"; +import { Packet, PacketType } from "socket.io-parser"; +import * as Emitter from "component-emitter"; import { on } from "./on"; -import bind from "component-bind"; -import hasBin from "has-binary2"; +import * as bind from "component-bind"; +import * as hasBin from "has-binary2"; import { Manager } from "./manager"; const debug = require("debug")("socket.io-client:socket"); +export interface SocketOptions { + /** + * the authentication payload sent when connecting to the Namespace + */ + auth: object | ((cb: (data: object) => void) => void); +} + /** * Internal events. * These events can't be emitted by the user. - * - * @api private */ const RESERVED_EVENTS = { @@ -45,9 +49,9 @@ export class Socket extends Emitter { /** * `Socket` constructor. * - * @api public + * @public */ - constructor(io, nsp, opts) { + constructor(io: Manager, nsp: string, opts?: Partial) { super(); this.io = io; this.nsp = nsp; @@ -61,15 +65,15 @@ export class Socket extends Emitter { if (opts && opts.auth) { this.auth = opts.auth; } - if (this.io.autoConnect) this.open(); + if (this.io._autoConnect) this.open(); } /** * Subscribe to open, close and packet events * - * @api private + * @private */ - subEvents() { + private subEvents() { if (this.subs) return; const io = this.io; @@ -83,34 +87,31 @@ export class Socket extends Emitter { /** * "Opens" the socket. * - * @api public + * @public */ - open() { + public connect(): Socket { if (this.connected) return this; this.subEvents(); - if (!this.io.reconnecting) this.io.open(); // ensure open - if ("open" === this.io.readyState) this.onopen(); + if (!this.io._reconnecting) this.io.open(); // ensure open + if ("open" === this.io._readyState) this.onopen(); return this; } - connect() { - if (this.connected) return this; - - this.subEvents(); - if (!this.io.reconnecting) this.io.open(); // ensure open - if ("open" === this.io.readyState) this.onopen(); - return this; + /** + * Alias for connect() + */ + public open(): Socket { + return this.connect(); } /** * Sends a `message` event. * * @return {Socket} self - * @api public + * @public */ - send() { - const args = toArray(arguments); + public send(...args: any[]) { args.unshift("message"); this.emit.apply(this, args); return this; @@ -120,16 +121,16 @@ export class Socket extends Emitter { * Override `emit`. * If the event is in `events`, it's emitted normally. * - * @param {String} event name + * @param {String} ev - event name * @return {Socket} self - * @api public + * @public */ - emit(ev) { + public emit(ev: string, ...args: any[]) { if (RESERVED_EVENTS.hasOwnProperty(ev)) { throw new Error('"' + ev + '" is a reserved event name'); } - const args = toArray(arguments); + args.unshift(ev); const packet: any = { type: (this.flags.binary !== undefined ? this.flags.binary : hasBin(args)) ? PacketType.BINARY_EVENT @@ -162,19 +163,19 @@ export class Socket extends Emitter { * Sends a packet. * * @param {Object} packet - * @api private + * @private */ - packet(packet) { + private packet(packet: Partial) { packet.nsp = this.nsp; - this.io.packet(packet); + this.io._packet(packet); } /** * Called upon engine `open`. * - * @api private + * @private */ - onopen() { + private onopen() { debug("transport is open - connecting"); if (typeof this.auth == "function") { this.auth((data) => { @@ -189,9 +190,9 @@ export class Socket extends Emitter { * Called upon engine `close`. * * @param {String} reason - * @api private + * @private */ - onclose(reason) { + private onclose(reason) { debug("close (%s)", reason); this.connected = false; this.disconnected = true; @@ -203,9 +204,9 @@ export class Socket extends Emitter { * Called with socket packet. * * @param {Object} packet - * @api private + * @private */ - onpacket(packet) { + private onpacket(packet) { const sameNamespace = packet.nsp === this.nsp; const rootNamespaceError = packet.type === PacketType.ERROR && packet.nsp === "/"; @@ -248,9 +249,9 @@ export class Socket extends Emitter { * Called upon a server event. * * @param {Object} packet - * @api private + * @private */ - onevent(packet) { + private onevent(packet) { const args = packet.data || []; debug("emitting event %j", args); @@ -269,16 +270,15 @@ export class Socket extends Emitter { /** * Produces an ack callback to emit with an event. * - * @api private + * @private */ - ack(id) { + private ack(id) { const self = this; let sent = false; - return function () { + return function (...args: any[]) { // prevent double callbacks if (sent) return; sent = true; - const args = toArray(arguments); debug("sending ack %j", args); self.packet({ @@ -293,9 +293,9 @@ export class Socket extends Emitter { * Called upon a server acknowlegement. * * @param {Object} packet - * @api private + * @private */ - onack(packet) { + private onack(packet) { const ack = this.acks[packet.id]; if ("function" === typeof ack) { debug("calling ack %s with %j", packet.id, packet.data); @@ -309,9 +309,9 @@ export class Socket extends Emitter { /** * Called upon server connect. * - * @api private + * @private */ - onconnect(id: string) { + private onconnect(id: string) { this.id = id; this.connected = true; this.disconnected = false; @@ -322,9 +322,9 @@ export class Socket extends Emitter { /** * Emit buffered events (received and emitted). * - * @api private + * @private */ - emitBuffered() { + private emitBuffered() { for (let i = 0; i < this.receiveBuffer.length; i++) { super.emit.apply(this, this.receiveBuffer[i]); } @@ -339,9 +339,9 @@ export class Socket extends Emitter { /** * Called upon server disconnect. * - * @api private + * @private */ - ondisconnect() { + private ondisconnect() { debug("server disconnect (%s)", this.nsp); this.destroy(); this.onclose("io server disconnect"); @@ -352,9 +352,9 @@ export class Socket extends Emitter { * this method ensures the manager stops tracking us and * that reconnections don't get triggered for this. * - * @api private. + * @private */ - destroy() { + private destroy() { if (this.subs) { // clean subscriptions to avoid reconnections for (let i = 0; i < this.subs.length; i++) { @@ -363,16 +363,16 @@ export class Socket extends Emitter { this.subs = null; } - this.io.destroy(this); + this.io._destroy(this); } /** * Disconnects the socket manually. * * @return {Socket} self - * @api public + * @public */ - close() { + public disconnect(): Socket { if (this.connected) { debug("performing disconnect (%s)", this.nsp); this.packet({ type: PacketType.DISCONNECT }); @@ -388,30 +388,24 @@ export class Socket extends Emitter { return this; } - disconnect() { - if (this.connected) { - debug("performing disconnect (%s)", this.nsp); - this.packet({ type: PacketType.DISCONNECT }); - } - - // remove socket from pool - this.destroy(); - - if (this.connected) { - // fire events - this.onclose("io client disconnect"); - } - return this; + /** + * Alias for disconnect() + * + * @return {Socket} self + * @public + */ + public close(): Socket { + return this.disconnect(); } /** * Sets the compress flag. * - * @param {Boolean} if `true`, compresses the sending data + * @param {Boolean} compress - if `true`, compresses the sending data * @return {Socket} self - * @api public + * @public */ - compress(compress) { + public compress(compress: boolean) { this.flags.compress = compress; return this; } @@ -419,11 +413,11 @@ export class Socket extends Emitter { /** * Sets the binary flag * - * @param {Boolean} whether the emitted data contains binary + * @param {Boolean} binary - whether the emitted data contains binary * @return {Socket} self - * @api public + * @public */ - binary(binary) { + public binary(binary: boolean): Socket { this.flags.binary = binary; return this; } diff --git a/lib/url.ts b/lib/url.ts index ead8de9c1..4f22fc605 100644 --- a/lib/url.ts +++ b/lib/url.ts @@ -1,17 +1,17 @@ -import parseuri from "parseuri"; +import * as parseuri from "parseuri"; const debug = require("debug")("socket.io-client:url"); /** * URL parser. * - * @param {String} url - * @param {Object} An object meant to mimic window.location. + * @param {String} uri - url + * @param {Object} loc - An object meant to mimic window.location. * Defaults to window.location. - * @api public + * @public */ -export function url(uri, loc?) { +export function url(uri: any, loc?: Location) { let obj = uri; // default to window.location diff --git a/package.json b/package.json index 1f8af62d4..ea861dc97 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ }, "types": "./build/index.d.ts", "dependencies": { + "@types/component-emitter": "^1.2.10", "backo2": "1.0.2", "component-bind": "1.0.0", "component-emitter": "~1.3.0", @@ -35,14 +36,14 @@ "has-binary2": "~1.0.2", "indexof": "0.0.1", "parseuri": "0.0.6", - "socket.io-parser": "4.0.1-rc1", - "to-array": "0.1.4" + "socket.io-parser": "4.0.1-rc1" }, "devDependencies": { "@babel/core": "^7.11.6", "@babel/plugin-transform-object-assign": "^7.10.4", "@babel/preset-env": "^7.11.5", - "@types/node": "^14.11.2", + "@types/mocha": "^8.0.3", + "@types/node": "^14.11.8", "babel-eslint": "^10.1.0", "babel-loader": "^8.1.0", "babel-preset-es2015": "6.24.1", @@ -58,6 +59,8 @@ "socket.io": "3.0.0-rc1", "socket.io-browsers": "^1.0.0", "text-blob-builder": "0.0.1", + "ts-loader": "^8.0.5", + "ts-node": "^9.0.0", "typescript": "^4.0.3", "webpack": "^4.44.2", "webpack-cli": "^3.3.12", @@ -69,11 +72,11 @@ "scripts": { "compile": "tsc", "test": "npm run format:check && npm run compile && if test \"$BROWSERS\" = \"1\" ; then npm run test:browser; else npm run test:node; fi", - "test:node": "mocha --reporter dot --require test/support/server.js test/index.js", + "test:node": "mocha --require ts-node/register --reporter dot --require test/support/server.js test/index.js", "test:browser": "zuul test/index.js", "build": "npm run compile && webpack --config ./support/webpack.config.js --config ./support/prod.config.js", - "format:check": "prettier --check 'lib/**/*.ts' 'test/**/*.js' 'support/**/*.js'", - "format:fix": "prettier --write 'lib/**/*.ts' 'test/**/*.js' 'support/**/*.js'", + "format:check": "prettier --check 'lib/**/*.ts' 'test/**/*.js' 'test/**/*.ts' 'support/**/*.js'", + "format:fix": "prettier --write 'lib/**/*.ts' 'test/**/*.js' 'test/**/*.ts' 'support/**/*.js'", "prepack": "npm run compile" }, "contributors": [ diff --git a/test/connection.js b/test/connection.ts similarity index 93% rename from test/connection.js rename to test/connection.ts index 5234a886f..5d6c154f6 100644 --- a/test/connection.js +++ b/test/connection.ts @@ -1,8 +1,8 @@ -const expect = require("expect.js"); -const io = require("../"); -const hasCORS = require("has-cors"); -const textBlobBuilder = require("text-blob-builder"); -const env = require("./support/env"); +import * as expect from "expect.js"; +import { io, Manager } from ".."; +import * as hasCORS from "has-cors"; +import * as textBlobBuilder from "text-blob-builder"; +import * as env from "./support/env"; describe("connection", function () { this.timeout(70000); @@ -94,21 +94,21 @@ describe("connection", function () { }); it("should connect to a namespace after connection established", (done) => { - const manager = new io.Manager(); + const manager = new Manager(); const socket = manager.socket("/"); socket.on("connect", () => { const foo = manager.socket("/foo"); foo.on("connect", () => { foo.close(); socket.close(); - manager.close(); + manager._close(); done(); }); }); }); it("should open a new namespace after connection gets closed", (done) => { - const manager = new io.Manager(); + const manager = new Manager(); const socket = manager.socket("/"); socket .on("connect", () => { @@ -118,7 +118,7 @@ describe("connection", function () { const foo = manager.socket("/foo"); foo.on("connect", () => { foo.disconnect(); - manager.close(); + manager._close(); done(); }); }); @@ -170,7 +170,7 @@ describe("connection", function () { }); it("should attempt reconnects after a failed reconnect", (done) => { - const manager = new io.Manager({ + const manager = new Manager({ reconnection: true, timeout: 0, reconnectionAttempts: 2, @@ -187,7 +187,7 @@ describe("connection", function () { manager.on("reconnect_failed", () => { expect(reconnects).to.be(2); socket.close(); - manager.close(); + manager._close(); done(); }); socket.connect(); @@ -195,7 +195,7 @@ describe("connection", function () { }); it("reconnect delay should increase every time", (done) => { - const manager = new io.Manager({ + const manager = new Manager({ reconnection: true, timeout: 0, reconnectionAttempts: 3, @@ -225,7 +225,7 @@ describe("connection", function () { expect(reconnects).to.be(3); expect(increasingDelay).to.be.ok(); socket.close(); - manager.close(); + manager._close(); done(); }); }); @@ -283,7 +283,7 @@ describe("connection", function () { }); it("should stop reconnecting on a socket and keep to reconnect on another", (done) => { - const manager = new io.Manager(); + const manager = new Manager(); const socket1 = manager.socket("/"); const socket2 = manager.socket("/asd"); @@ -294,7 +294,7 @@ describe("connection", function () { socket2.on("connect", () => { setTimeout(() => { socket2.disconnect(); - manager.disconnect(); + manager._close(); done(); }, 500); }); @@ -307,7 +307,7 @@ describe("connection", function () { }); it("should try to reconnect twice and fail when requested two attempts with immediate timeout and reconnect enabled", (done) => { - const manager = new io.Manager({ + const manager = new Manager({ reconnection: true, timeout: 0, reconnectionAttempts: 2, @@ -324,7 +324,7 @@ describe("connection", function () { manager.on("reconnect_failed", () => { expect(reconnects).to.be(2); socket.close(); - manager.close(); + manager._close(); done(); }); @@ -332,7 +332,7 @@ describe("connection", function () { }); it("should fire reconnect_* events on manager", (done) => { - const manager = new io.Manager({ + const manager = new Manager({ reconnection: true, timeout: 0, reconnectionAttempts: 2, @@ -350,13 +350,13 @@ describe("connection", function () { manager.on("reconnect_failed", () => { expect(reconnects).to.be(2); socket.close(); - manager.close(); + manager._close(); done(); }); }); it("should fire reconnecting (on manager) with attempts number when reconnecting twice", (done) => { - const manager = new io.Manager({ + const manager = new Manager({ reconnection: true, timeout: 0, reconnectionAttempts: 2, @@ -374,13 +374,13 @@ describe("connection", function () { manager.on("reconnect_failed", () => { expect(reconnects).to.be(2); socket.close(); - manager.close(); + manager._close(); done(); }); }); it("should not try to reconnect and should form a connection when connecting to correct port with default timeout", (done) => { - const manager = new io.Manager({ + const manager = new Manager({ reconnection: true, reconnectionDelay: 10, }); @@ -395,14 +395,14 @@ describe("connection", function () { // set a timeout to let reconnection possibly fire setTimeout(() => { socket.close(); - manager.close(); + manager._close(); done(); }, 1000); }); }); it("should connect while disconnecting another socket", (done) => { - const manager = new io.Manager(); + const manager = new Manager(); const socket1 = manager.socket("/foo"); socket1.on("connect", () => { const socket2 = manager.socket("/asd"); @@ -415,7 +415,7 @@ describe("connection", function () { // `script.onerror` (see: http://requirejs.org/docs/api.html#ieloadfail) if (!global.document || hasCORS) { it("should try to reconnect twice and fail when requested two attempts with incorrect address and reconnect enabled", (done) => { - const manager = new io.Manager("http://localhost:3940", { + const manager = new Manager("http://localhost:3940", { reconnection: true, reconnectionAttempts: 2, reconnectionDelay: 10, @@ -431,13 +431,13 @@ describe("connection", function () { manager.on("reconnect_failed", () => { expect(reconnects).to.be(2); socket.disconnect(); - manager.close(); + manager._close(); done(); }); }); it("should not try to reconnect with incorrect port when reconnection disabled", (done) => { - const manager = new io.Manager("http://localhost:9823", { + const manager = new Manager("http://localhost:9823", { reconnection: false, }); const cb = () => { @@ -450,7 +450,7 @@ describe("connection", function () { // set a timeout to let reconnection possibly fire setTimeout(() => { socket.disconnect(); - manager.close(); + manager._close(); done(); }, 1000); }); @@ -459,8 +459,8 @@ describe("connection", function () { }); it("should still try to reconnect twice after opening another socket asynchronously", (done) => { - const manager = new io.Manager("http://localhost:9823", { - reconnect: true, + const manager = new Manager("http://localhost:9823", { + reconnection: true, reconnectionAttempts: 2, }); let delay = Math.floor( @@ -478,7 +478,7 @@ describe("connection", function () { manager.on("reconnect_failed", () => { expect(reconnects).to.be(2); socket.disconnect(); - manager.close(); + manager._close(); done(); }); diff --git a/test/index.js b/test/index.js index bf69ccdcd..24113d388 100644 --- a/test/index.js +++ b/test/index.js @@ -5,8 +5,8 @@ if (global.mocha) { global.mocha.globals(["___eio", "eio_iframe_*"]); } -require("./url"); +require("./url.ts"); // browser only tests -require("./connection"); -require("./socket"); +require("./connection.ts"); +require("./socket.ts"); diff --git a/test/socket.js b/test/socket.ts similarity index 98% rename from test/socket.js rename to test/socket.ts index c4b8a548a..09ce1b006 100644 --- a/test/socket.js +++ b/test/socket.ts @@ -1,5 +1,5 @@ -const expect = require("expect.js"); -const io = require("../"); +import * as expect from "expect.js"; +import { io } from ".."; describe("socket", function () { this.timeout(70000); diff --git a/test/url.js b/test/url.ts similarity index 96% rename from test/url.js rename to test/url.ts index 9462eb6e9..d2ac7cc3a 100644 --- a/test/url.js +++ b/test/url.ts @@ -1,6 +1,7 @@ -const loc = {}; -const { url } = require("../build/url"); -const expect = require("expect.js"); +import { url } from "../build/url"; +import * as expect from "expect.js"; + +const loc: any = {}; describe("url", () => { it("works with undefined", () => { diff --git a/tsconfig.json b/tsconfig.json index a29c767aa..bd40c1107 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "target": "es2017", "module": "commonjs", "declaration": true, - "esModuleInterop": true + "allowSyntheticDefaultImports": true }, "include": [ "./lib/**/*" diff --git a/zuul.config.js b/zuul.config.js index 1690a67a0..15e5a9565 100644 --- a/zuul.config.js +++ b/zuul.config.js @@ -1,12 +1,30 @@ 'use strict'; -var browsers = require('socket.io-browsers'); +const browsers = require('socket.io-browsers'); -var zuulConfig = module.exports = { +const webpackConfig = require('./support/prod.config.js'); + +webpackConfig.module.rules.push({ + test: /\.tsx?$/, + use: [ + { + loader: "ts-loader", + options: { + compilerOptions: { + target: "es5", + } + } + }, + ], + exclude: /node_modules/, +}); + +const zuulConfig = module.exports = { ui: 'mocha-bdd', // test on localhost by default local: true, + open: true, concurrency: 2, // ngrok only accepts two tunnels by default // if browser does not sends output in 120s since last output: @@ -18,7 +36,7 @@ var zuulConfig = module.exports = { server: './test/support/server.js', builder: 'zuul-builder-webpack', - webpack: require('./support/prod.config.js') + webpack: webpackConfig }; if (process.env.CI === 'true') { @@ -29,5 +47,5 @@ if (process.env.CI === 'true') { }; } -var isPullRequest = process.env.TRAVIS_PULL_REQUEST && process.env.TRAVIS_PULL_REQUEST !== 'false'; +const isPullRequest = process.env.TRAVIS_PULL_REQUEST && process.env.TRAVIS_PULL_REQUEST !== 'false'; zuulConfig.browsers = isPullRequest ? browsers.pullRequest : browsers.all;