From 1e2f9cf68bc94200de28fc2ed41ef2766fd57d03 Mon Sep 17 00:00:00 2001 From: Maxime Kjaer Date: Mon, 1 Mar 2021 21:46:41 +0100 Subject: [PATCH 1/9] Add typed events --- .github/workflows/ci.yml | 1 + lib/broadcast-operator.ts | 54 +- lib/client.ts | 13 +- lib/index.ts | 103 ++- lib/namespace.ts | 59 +- lib/parent-namespace.ts | 24 +- lib/socket.ts | 104 ++- package-lock.json | 1329 ++++++++++++++++++++++++++++++++++++- package.json | 5 + test/socket.io.test-d.ts | 211 ++++++ test/socket.io.ts | 12 +- 11 files changed, 1832 insertions(+), 83 deletions(-) create mode 100644 test/socket.io.test-d.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21f0b4bd96..9eec6027e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,3 +24,4 @@ jobs: - run: npm test env: CI: true + - run: npm run test:types diff --git a/lib/broadcast-operator.ts b/lib/broadcast-operator.ts index 015382d8ab..dc7321edaf 100644 --- a/lib/broadcast-operator.ts +++ b/lib/broadcast-operator.ts @@ -2,8 +2,17 @@ import type { BroadcastFlags, Room, SocketId } from "socket.io-adapter"; import { Handshake, RESERVED_EVENTS, Socket } from "./socket"; import { PacketType } from "socket.io-parser"; import type { Adapter } from "socket.io-adapter"; +import type { + EventParams, + EventNames, + EventsMap, + DefaultEventsMap, +} from "./index"; -export class BroadcastOperator { +export class BroadcastOperator< + UserEvents extends EventsMap, + UserEmitEvents extends EventsMap = UserEvents +> { constructor( private readonly adapter: Adapter, private readonly rooms: Set = new Set(), @@ -18,7 +27,9 @@ export class BroadcastOperator { * @return a new BroadcastOperator instance * @public */ - public to(room: Room | Room[]): BroadcastOperator { + public to( + room: Room | Room[] + ): BroadcastOperator { const rooms = new Set(this.rooms); if (Array.isArray(room)) { room.forEach((r) => rooms.add(r)); @@ -40,7 +51,9 @@ export class BroadcastOperator { * @return a new BroadcastOperator instance * @public */ - public in(room: Room | Room[]): BroadcastOperator { + public in( + room: Room | Room[] + ): BroadcastOperator { return this.to(room); } @@ -51,7 +64,9 @@ export class BroadcastOperator { * @return a new BroadcastOperator instance * @public */ - public except(room: Room | Room[]): BroadcastOperator { + public except( + room: Room | Room[] + ): BroadcastOperator { const exceptRooms = new Set(this.exceptRooms); if (Array.isArray(room)) { room.forEach((r) => exceptRooms.add(r)); @@ -73,7 +88,9 @@ export class BroadcastOperator { * @return a new BroadcastOperator instance * @public */ - public compress(compress: boolean): BroadcastOperator { + public compress( + compress: boolean + ): BroadcastOperator { const flags = Object.assign({}, this.flags, { compress }); return new BroadcastOperator( this.adapter, @@ -91,7 +108,7 @@ export class BroadcastOperator { * @return a new BroadcastOperator instance * @public */ - public get volatile(): BroadcastOperator { + public get volatile(): BroadcastOperator { const flags = Object.assign({}, this.flags, { volatile: true }); return new BroadcastOperator( this.adapter, @@ -107,7 +124,7 @@ export class BroadcastOperator { * @return a new BroadcastOperator instance * @public */ - public get local(): BroadcastOperator { + public get local(): BroadcastOperator { const flags = Object.assign({}, this.flags, { local: true }); return new BroadcastOperator( this.adapter, @@ -123,18 +140,21 @@ export class BroadcastOperator { * @return Always true * @public */ - public emit(ev: string | Symbol, ...args: any[]): true { + public emit>( + ev: Ev, + ...args: EventParams + ): true { if (RESERVED_EVENTS.has(ev)) { throw new Error(`"${ev}" is a reserved event name`); } // set up packet object - args.unshift(ev); + const data = [ev, ...args]; const packet = { type: PacketType.EVENT, - data: args, + data: data, }; - if ("function" == typeof args[args.length - 1]) { + if ("function" == typeof data[data.length - 1]) { throw new Error("Callbacks are not supported when broadcasting"); } @@ -246,13 +266,16 @@ interface SocketDetails { /** * Expose of subset of the attributes and methods of the Socket class */ -export class RemoteSocket { +export class RemoteSocket< + UserEvents extends EventsMap = DefaultEventsMap, + UserEmitEvents extends EventsMap = UserEvents +> { public readonly id: SocketId; public readonly handshake: Handshake; public readonly rooms: Set; public readonly data: any; - private readonly operator: BroadcastOperator; + private readonly operator: BroadcastOperator; constructor(adapter: Adapter, details: SocketDetails) { this.id = details.id; @@ -262,7 +285,10 @@ export class RemoteSocket { this.operator = new BroadcastOperator(adapter, new Set([this.id])); } - public emit(ev: string, ...args: any[]): boolean { + public emit>( + ev: Ev, + ...args: EventParams + ): true { return this.operator.emit(ev, ...args); } diff --git a/lib/client.ts b/lib/client.ts index 13c9ec5362..8216f5f61c 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -2,17 +2,20 @@ import { Decoder, Encoder, Packet, PacketType } from "socket.io-parser"; import debugModule = require("debug"); import url = require("url"); import type { IncomingMessage } from "http"; -import type { Namespace, Server } from "./index"; +import type { DefaultEventsMap, EventsMap, Namespace, Server } from "./index"; import type { Socket } from "./socket"; import type { SocketId } from "socket.io-adapter"; const debug = debugModule("socket.io:client"); -export class Client { +export class Client< + UserEvents extends EventsMap = DefaultEventsMap, + UserEmitEvents extends EventsMap = UserEvents +> { public readonly conn; private readonly id: string; - private readonly server: Server; + private readonly server: Server; private readonly encoder: Encoder; private readonly decoder: Decoder; private sockets: Map = new Map(); @@ -26,7 +29,7 @@ export class Client { * @param conn * @package */ - constructor(server: Server, conn: Socket) { + constructor(server: Server, conn: Socket) { this.server = server; this.conn = conn; this.encoder = server.encoder; @@ -87,7 +90,7 @@ export class Client { this.server._checkNamespace( name, auth, - (dynamicNspName: Namespace | false) => { + (dynamicNspName: Namespace | false) => { if (dynamicNspName) { debug("dynamic namespace %s was created", dynamicNspName); this.doConnect(name, auth); diff --git a/lib/index.ts b/lib/index.ts index 8884cf9ffd..084c16e6b9 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -13,7 +13,7 @@ import { Adapter, Room, SocketId } from "socket.io-adapter"; import * as parser from "socket.io-parser"; import type { Encoder } from "socket.io-parser"; import debugModule from "debug"; -import { Socket } from "./socket"; +import { ServerReservedEventsMap, Socket } from "./socket"; import type { CookieSerializeOptions } from "cookie"; import type { CorsOptions } from "cors"; import type { BroadcastOperator, RemoteSocket } from "./broadcast-operator"; @@ -156,8 +156,58 @@ interface ServerOptions extends EngineAttachOptions { connectTimeout: number; } -export class Server extends EventEmitter { - public readonly sockets: Namespace; +/** + * An events map is an interface that maps event names to their values. Values + * can be either: + * + * - `void`, indicating that the `on` listener takes no parameters + * - a function type, representing the the type of the `on` listener + * - any other type, representing the type of the first parameter of the `on` listener + */ +export interface EventsMap { + [event: string]: any; +} + +/** + * The default events map, used if no EventsMap is given. Using this EventsMap + * is equivalent to accepting all event names, and any data. + */ +export interface DefaultEventsMap { + [event: string]: (...args: any[]) => void; +} + +/** + * Returns a union type containing all the keys of an event map. + */ +export type EventNames = keyof Map & (string | symbol); + +/** The tuple type representing the parameters of an event listener */ +export type EventParams< + Map extends EventsMap, + Ev extends keyof Map +> = Map[Ev] extends (...args: any[]) => void + ? Parameters + : Map[Ev] extends void + ? [] + : [Map[Ev]]; + +/** + * The type of listener callback that listens to the `Ev` event, as defined in `Map` + */ +export type Listener< + Map extends EventsMap, + Ev extends keyof Map +> = Map[Ev] extends (...args: any[]) => void + ? Map[Ev] + : Map[Ev] extends void + ? (...args: []) => void + : (...args: [Map[Ev]]) => void; + +export class Server< + UserEvents extends EventsMap = DefaultEventsMap, + UserEmitEvents extends EventsMap = UserEvents +> extends EventEmitter { + public readonly sockets: Namespace; /** @private */ readonly _parser: typeof parser; @@ -167,8 +217,11 @@ export class Server extends EventEmitter { /** * @private */ - _nsps: Map = new Map(); - private parentNsps: Map = new Map(); + _nsps: Map> = new Map(); + private parentNsps: Map< + ParentNspNameMatchFn, + ParentNamespace + > = new Map(); private _adapter?: typeof Adapter; private _serveClient: boolean; private opts: Partial; @@ -248,7 +301,7 @@ export class Server extends EventEmitter { _checkNamespace( name: string, auth: { [key: string]: any }, - fn: (nsp: Namespace | false) => void + fn: (nsp: Namespace | false) => void ): void { if (this.parentNsps.size === 0) return fn(false); @@ -558,7 +611,7 @@ export class Server extends EventEmitter { public of( name: string | RegExp | ParentNspNameMatchFn, fn?: (socket: Socket) => void - ): Namespace { + ): Namespace { if (typeof name === "function" || name instanceof RegExp) { const parentNsp = new ParentNamespace(this); debug("initializing parent namespace %s", parentNsp.name); @@ -629,7 +682,9 @@ export class Server extends EventEmitter { * @return self * @public */ - public to(room: Room | Room[]): BroadcastOperator { + public to( + room: Room | Room[] + ): BroadcastOperator { return this.sockets.to(room); } @@ -640,10 +695,26 @@ export class Server extends EventEmitter { * @return self * @public */ - public in(room: Room | Room[]): BroadcastOperator { + public in( + room: Room | Room[] + ): BroadcastOperator { return this.sockets.in(room); } + public on< + Ev extends EventNames< + UserEvents & ServerReservedEventsMap + > + >( + ev: Ev, + listener: Listener< + UserEvents & ServerReservedEventsMap, + Ev + > + ): this { + return super.on(ev, listener); + } + /** * Excludes a room when emitting. * @@ -651,7 +722,7 @@ export class Server extends EventEmitter { * @return self * @public */ - public except(name: Room | Room[]): Server { + public except(name: Room | Room[]): Server { this.sockets.except(name); return this; } @@ -662,7 +733,7 @@ export class Server extends EventEmitter { * @return self * @public */ - public send(...args: readonly any[]): this { + public send(...args: EventParams): this { this.sockets.emit("message", ...args); return this; } @@ -673,7 +744,7 @@ export class Server extends EventEmitter { * @return self * @public */ - public write(...args: readonly any[]): this { + public write(...args: EventParams): this { this.sockets.emit("message", ...args); return this; } @@ -694,7 +765,9 @@ export class Server extends EventEmitter { * @return self * @public */ - public compress(compress: boolean): BroadcastOperator { + public compress( + compress: boolean + ): BroadcastOperator { return this.sockets.compress(compress); } @@ -706,7 +779,7 @@ export class Server extends EventEmitter { * @return self * @public */ - public get volatile(): BroadcastOperator { + public get volatile(): BroadcastOperator { return this.sockets.volatile; } @@ -716,7 +789,7 @@ export class Server extends EventEmitter { * @return self * @public */ - public get local(): BroadcastOperator { + public get local(): BroadcastOperator { return this.sockets.local; } diff --git a/lib/namespace.ts b/lib/namespace.ts index 65417742a5..1ab57c8e7e 100644 --- a/lib/namespace.ts +++ b/lib/namespace.ts @@ -1,5 +1,11 @@ import { Socket } from "./socket"; -import type { Server } from "./index"; +import type { + DefaultEventsMap, + EventParams, + EventNames, + EventsMap, + Server, +} from "./index"; import type { Client } from "./client"; import { EventEmitter } from "events"; import debugModule from "debug"; @@ -12,14 +18,17 @@ export interface ExtendedError extends Error { data?: any; } -export class Namespace extends EventEmitter { +export class Namespace< + UserEvents extends EventsMap = DefaultEventsMap, + UserEmitEvents extends EventsMap = UserEvents +> extends EventEmitter { public readonly name: string; public readonly sockets: Map = new Map(); public adapter: Adapter; /** @private */ - readonly server: Server; + readonly server: Server; /** @private */ _fns: Array< @@ -35,7 +44,7 @@ export class Namespace extends EventEmitter { * @param server instance * @param name */ - constructor(server: Server, name: string) { + constructor(server: Server, name: string) { super(); this.server = server; this.name = name; @@ -100,7 +109,9 @@ export class Namespace extends EventEmitter { * @return self * @public */ - public to(room: Room | Room[]): BroadcastOperator { + public to( + room: Room | Room[] + ): BroadcastOperator { return new BroadcastOperator(this.adapter).to(room); } @@ -111,7 +122,9 @@ export class Namespace extends EventEmitter { * @return self * @public */ - public in(room: Room | Room[]): BroadcastOperator { + public in( + room: Room | Room[] + ): BroadcastOperator { return new BroadcastOperator(this.adapter).in(room); } @@ -122,7 +135,9 @@ export class Namespace extends EventEmitter { * @return self * @public */ - public except(room: Room | Room[]): BroadcastOperator { + public except( + room: Room | Room[] + ): BroadcastOperator { return new BroadcastOperator(this.adapter).except(room); } @@ -132,7 +147,11 @@ export class Namespace extends EventEmitter { * @return {Socket} * @private */ - _add(client: Client, query, fn?: () => void): Socket { + _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) => { @@ -189,8 +208,14 @@ export class Namespace extends EventEmitter { * @return Always true * @public */ - public emit(ev: string | Symbol, ...args: any[]): true { - return new BroadcastOperator(this.adapter).emit(ev, ...args); + public emit>( + ev: Ev, + ...args: EventParams + ): true { + return new BroadcastOperator(this.adapter).emit( + ev, + ...args + ); } /** @@ -199,7 +224,7 @@ export class Namespace extends EventEmitter { * @return self * @public */ - public send(...args: readonly any[]): this { + public send(...args: EventParams): this { this.emit("message", ...args); return this; } @@ -210,7 +235,7 @@ export class Namespace extends EventEmitter { * @return self * @public */ - public write(...args: readonly any[]): this { + public write(...args: EventParams): this { this.emit("message", ...args); return this; } @@ -232,7 +257,9 @@ export class Namespace extends EventEmitter { * @return self * @public */ - public compress(compress: boolean): BroadcastOperator { + public compress( + compress: boolean + ): BroadcastOperator { return new BroadcastOperator(this.adapter).compress(compress); } @@ -244,7 +271,7 @@ export class Namespace extends EventEmitter { * @return self * @public */ - public get volatile(): BroadcastOperator { + public get volatile(): BroadcastOperator { return new BroadcastOperator(this.adapter).volatile; } @@ -254,7 +281,7 @@ export class Namespace extends EventEmitter { * @return self * @public */ - public get local(): BroadcastOperator { + public get local(): BroadcastOperator { return new BroadcastOperator(this.adapter).local; } @@ -263,7 +290,7 @@ export class Namespace extends EventEmitter { * * @public */ - public fetchSockets(): Promise { + public fetchSockets(): Promise[]> { return new BroadcastOperator(this.adapter).fetchSockets(); } diff --git a/lib/parent-namespace.ts b/lib/parent-namespace.ts index 6af019e63c..f214424a4c 100644 --- a/lib/parent-namespace.ts +++ b/lib/parent-namespace.ts @@ -1,12 +1,21 @@ import { Namespace } from "./namespace"; -import type { Server } from "./index"; +import type { + DefaultEventsMap, + EventParams, + EventNames, + EventsMap, + Server, +} from "./index"; import type { BroadcastOptions } from "socket.io-adapter"; -export class ParentNamespace extends Namespace { +export class ParentNamespace< + UserEvents extends EventsMap = DefaultEventsMap, + UserEmitEvents extends EventsMap = UserEvents +> extends Namespace { private static count: number = 0; - private children: Set = new Set(); + private children: Set> = new Set(); - constructor(server: Server) { + constructor(server: Server) { super(server, "/_" + ParentNamespace.count++); } @@ -23,7 +32,10 @@ export class ParentNamespace extends Namespace { this.adapter = { broadcast }; } - public emit(ev: string | Symbol, ...args: [...any]): true { + public emit>( + ev: Ev, + ...args: EventParams + ): true { this.children.forEach((nsp) => { nsp.emit(ev, ...args); }); @@ -31,7 +43,7 @@ export class ParentNamespace extends Namespace { return true; } - createChild(name: string): Namespace { + createChild(name: string): Namespace { const namespace = new Namespace(this.server, name); namespace._fns = this._fns.slice(0); this.listeners("connect").forEach((listener) => diff --git a/lib/socket.ts b/lib/socket.ts index 4bae605e47..23da20ab78 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -2,7 +2,14 @@ import { EventEmitter } from "events"; import { Packet, PacketType } from "socket.io-parser"; import url = require("url"); import debugModule from "debug"; -import type { Server } from "./index"; +import type { + DefaultEventsMap, + EventParams, + EventNames, + EventsMap, + Listener, + Server, +} from "./index"; import type { Client } from "./client"; import type { Namespace } from "./namespace"; import type { IncomingMessage, IncomingHttpHeaders } from "http"; @@ -18,15 +25,46 @@ import { BroadcastOperator } from "./broadcast-operator"; const debug = debugModule("socket.io:socket"); -export const RESERVED_EVENTS = new Set([ +type ClientReservedEvents = "connect_error"; + +export interface ServerReservedEventsMap< + UserEvents extends EventsMap, + UserEmitEvents extends EventsMap +> { + connect: (socket: Socket) => void; + connection: (socket: Socket) => void; +} + +export interface SocketReservedEventsMap { + disconnect: (reason: string) => void; + disconnecting: (reason: string) => void; +} + +// EventEmitter reserved events: https://nodejs.org/api/events.html#events_event_newlistener +export interface EventEmitterReservedEventsMap { + newListener: ( + eventName: string | Symbol, + listener: (...args: any[]) => void + ) => void; + removeListener: ( + eventName: string | Symbol, + listener: (...args: any[]) => void + ) => void; +} + +export const RESERVED_EVENTS: ReadonlySet = new Set< + | ClientReservedEvents + | keyof ServerReservedEventsMap + | keyof SocketReservedEventsMap + | keyof EventEmitterReservedEventsMap +>([ "connect", "connect_error", "disconnect", "disconnecting", - // EventEmitter reserved events: https://nodejs.org/api/events.html#events_event_newlistener "newListener", "removeListener", -]) as ReadonlySet; +]); /** * The handshake details @@ -78,7 +116,10 @@ export interface Handshake { auth: { [key: string]: any }; } -export class Socket extends EventEmitter { +export class Socket< + UserEvents extends EventsMap = EventsMap, + UserEmitEvents extends EventsMap = UserEvents +> extends EventEmitter { public readonly id: SocketId; public readonly handshake: Handshake; /** @@ -89,7 +130,7 @@ export class Socket extends EventEmitter { public connected: boolean; public disconnected: boolean; - private readonly server: Server; + private readonly server: Server; private readonly adapter: Adapter; private acks: Map void> = new Map(); private fns: Array< @@ -106,7 +147,11 @@ export class Socket extends EventEmitter { * @param {Object} auth * @package */ - constructor(readonly nsp: Namespace, readonly client: Client, auth: object) { + constructor( + readonly nsp: Namespace, + readonly client: Client, + auth: object + ) { super(); this.server = nsp.server; this.adapter = this.nsp.adapter; @@ -147,20 +192,23 @@ export class Socket extends EventEmitter { * @return Always returns `true`. * @public */ - public emit(ev: string, ...args: any[]): boolean { + public emit>( + ev: Ev, + ...args: EventParams + ): boolean { if (RESERVED_EVENTS.has(ev)) { throw new Error(`"${ev}" is a reserved event name`); } - args.unshift(ev); + const data: any[] = [ev, ...args]; const packet: any = { type: PacketType.EVENT, - data: args, + data: data, }; // access last argument to see if it's an ACK callback - if (typeof args[args.length - 1] === "function") { + if (typeof data[data.length - 1] === "function") { debug("emitting packet with ack id %d", this.nsp._ids); - this.acks.set(this.nsp._ids, args.pop()); + this.acks.set(this.nsp._ids, data.pop()); packet.id = this.nsp._ids++; } @@ -172,6 +220,13 @@ export class Socket extends EventEmitter { return true; } + public on>( + ev: Ev, + listener: Listener + ): this { + return super.on(ev, listener); + } + /** * Targets a room when broadcasting. * @@ -179,7 +234,9 @@ export class Socket extends EventEmitter { * @return self * @public */ - public to(room: Room | Room[]): BroadcastOperator { + public to( + room: Room | Room[] + ): BroadcastOperator { return this.newBroadcastOperator().to(room); } @@ -190,7 +247,9 @@ export class Socket extends EventEmitter { * @return self * @public */ - public in(room: Room | Room[]): BroadcastOperator { + public in( + room: Room | Room[] + ): BroadcastOperator { return this.newBroadcastOperator().in(room); } @@ -201,7 +260,9 @@ export class Socket extends EventEmitter { * @return self * @public */ - public except(room: Room | Room[]): BroadcastOperator { + public except( + room: Room | Room[] + ): BroadcastOperator { return this.newBroadcastOperator().except(room); } @@ -211,7 +272,7 @@ export class Socket extends EventEmitter { * @return self * @public */ - public send(...args: readonly any[]): this { + public send(...args: EventParams): this { this.emit("message", ...args); return this; } @@ -222,7 +283,7 @@ export class Socket extends EventEmitter { * @return self * @public */ - public write(...args: readonly any[]): this { + public write(...args: EventParams): this { this.emit("message", ...args); return this; } @@ -505,7 +566,7 @@ export class Socket extends EventEmitter { * @return {Socket} self * @public */ - public get broadcast(): BroadcastOperator { + public get broadcast(): BroadcastOperator { return this.newBroadcastOperator(); } @@ -515,7 +576,7 @@ export class Socket extends EventEmitter { * @return {Socket} self * @public */ - public get local(): BroadcastOperator { + public get local(): BroadcastOperator { return this.newBroadcastOperator().local; } @@ -670,7 +731,10 @@ export class Socket extends EventEmitter { return this._anyListeners || []; } - private newBroadcastOperator(): BroadcastOperator { + private newBroadcastOperator(): BroadcastOperator< + UserEvents, + UserEmitEvents + > { const flags = Object.assign({}, this.flags); this.flags = {}; return new BroadcastOperator( diff --git a/package-lock.json b/package-lock.json index bcda3028d5..4140bcf9db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -350,6 +350,47 @@ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", "dev": true }, + "@nodelib/fs.scandir": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", + "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.4", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", + "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.4", + "fastq": "^1.6.0" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, "@types/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", @@ -411,6 +452,12 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==" }, + "@types/minimist": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", + "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", + "dev": true + }, "@types/mocha": { "version": "8.0.4", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.4.tgz", @@ -422,6 +469,12 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.10.tgz", "integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==" }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, "@types/qs": { "version": "6.9.5", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", @@ -490,12 +543,38 @@ "uri-js": "^4.2.2" } }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "dev": true, + "requires": { + "string-width": "^3.0.0" + } + }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", @@ -541,12 +620,24 @@ "sprintf-js": "~1.0.2" } }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, "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 }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -602,6 +693,96 @@ "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", "dev": true }, + "boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -612,6 +793,15 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "browser-stdout": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", @@ -624,6 +814,38 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, "caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -648,6 +870,17 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -659,12 +892,24 @@ "supports-color": "^5.3.0" } }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, + "cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true + }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -701,6 +946,15 @@ } } }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -763,6 +1017,20 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, "convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", @@ -803,6 +1071,12 @@ "which": "^2.0.1" } }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true + }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -817,6 +1091,39 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -832,6 +1139,12 @@ "strip-bom": "^4.0.0" } }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -844,6 +1157,15 @@ "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", "dev": true }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -853,12 +1175,36 @@ "esutils": "^2.0.2" } }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, "engine.io": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.0.tgz", @@ -905,12 +1251,27 @@ "ansi-colors": "^4.1.1" } }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, "es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -1037,6 +1398,101 @@ "get-stdin": "^6.0.0" } }, + "eslint-formatter-pretty": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-formatter-pretty/-/eslint-formatter-pretty-4.0.0.tgz", + "integrity": "sha512-QgdeZxQwWcN0TcXXNZJiS6BizhAANFhCzkE7Yl9HKB7WjElzwED6+FbbZB2gji8ofgJTGPqKm6VRCNT3OGCeEw==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "eslint-rule-docs": "^1.1.5", + "log-symbols": "^4.0.0", + "plur": "^4.0.0", + "string-width": "^4.2.0", + "supports-hyperlinks": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "eslint-rule-docs": { + "version": "1.1.222", + "resolved": "https://registry.npmjs.org/eslint-rule-docs/-/eslint-rule-docs-1.1.222.tgz", + "integrity": "sha512-lHzjFMdlpqaa38Zf6oeW/A+EX91ogcaUqolNwS2EOT3BhPqxPeYCdeH3H2+bQO1U7mzu01vsVNVr3L1cGyDrKA==", + "dev": true + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -1137,6 +1593,20 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1155,6 +1625,15 @@ "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", "dev": true }, + "fastq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "file-entry-cache": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", @@ -1164,6 +1643,15 @@ "flat-cache": "^2.0.1" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "find-cache-dir": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", @@ -1288,6 +1776,15 @@ "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", "dev": true }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -1311,12 +1808,62 @@ "is-glob": "^4.0.1" } }, + "global-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", + "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", + "dev": true, + "requires": { + "ini": "1.3.7" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, + "globby": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", + "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + } + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", @@ -1335,6 +1882,12 @@ "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", "dev": true }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -1365,6 +1918,12 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, "hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -1381,12 +1940,24 @@ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -1403,6 +1974,12 @@ "resolve-from": "^4.0.0" } }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -1437,6 +2014,33 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "ini": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", + "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", + "dev": true + }, + "irregular-plurals": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.2.0.tgz", + "integrity": "sha512-YqTdPLfwP7YFN0SsD3QUVCkm9ZG2VzOXv3DOrw5G5mkMbVwptTwVcFv7/C0vOpBmgTxAeTG19XpUs1E522LW9Q==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, "is-core-module": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", @@ -1467,6 +2071,46 @@ "is-extglob": "^2.1.1" } }, + "is-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", + "dev": true, + "requires": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + } + }, + "is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", @@ -1485,6 +2129,12 @@ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, "isarray": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", @@ -1626,6 +2276,18 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1653,6 +2315,30 @@ "minimist": "^1.2.5" } }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "requires": { + "package-json": "^6.3.0" + } + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -1663,6 +2349,12 @@ "type-check": "~0.4.0" } }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -1752,6 +2444,72 @@ "lodash.isarray": "^3.0.0" } }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -1775,12 +2533,61 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "map-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", + "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", + "dev": true + }, + "meow": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.1.tgz", + "integrity": "sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^2.5.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.13.1", + "yargs-parser": "^18.1.3" + }, + "dependencies": { + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true + } + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", "dev": true }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, "mime": { "version": "2.4.6", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", @@ -1800,6 +2607,18 @@ "mime-db": "1.44.0" } }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1815,6 +2634,17 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -1930,6 +2760,32 @@ "process-on-spawn": "^1.0.0" } }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + }, "nyc": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", @@ -2001,6 +2857,12 @@ "word-wrap": "^1.2.3" } }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -2046,13 +2908,45 @@ "release-zalgo": "^1.0.0" } }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "requires": { - "callsites": "^3.0.0" + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" } }, "parseqs": { @@ -2091,6 +2985,18 @@ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -2100,12 +3006,27 @@ "find-up": "^4.0.0" } }, + "plur": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz", + "integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==", + "dev": true, + "requires": { + "irregular-plurals": "^3.2.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, "prettier": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", @@ -2127,18 +3048,100 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, "qs": { "version": "6.9.4", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", "dev": true }, + "queue-microtask": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.2.tgz", + "integrity": "sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg==", + "dev": true + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -2150,12 +3153,40 @@ "util-deprecate": "^1.0.1" } }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, "regexpp": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", "dev": true }, + "registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -2193,6 +3224,21 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -2202,6 +3248,15 @@ "glob": "^7.1.3" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -2214,6 +3269,23 @@ "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -2241,6 +3313,12 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "slice-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", @@ -2412,6 +3490,38 @@ "which": "^2.0.1" } }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -2478,6 +3588,15 @@ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2522,6 +3641,33 @@ "has-flag": "^3.0.0" } }, + "supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", @@ -2534,6 +3680,12 @@ "string-width": "^3.0.0" } }, + "term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true + }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -2563,6 +3715,27 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", + "dev": true + }, "ts-node": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", @@ -2584,6 +3757,20 @@ } } }, + "tsd": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/tsd/-/tsd-0.14.0.tgz", + "integrity": "sha512-fl1gS5orAwqIb0P2xMdppgCrwv1BfCJn67wBzRBCV9OUaWHVXHqiIqL6yX/519xFgT1ZOaLMhr5W9XDo8kuuRA==", + "dev": true, + "requires": { + "eslint-formatter-pretty": "^4.0.0", + "globby": "^11.0.1", + "meow": "^7.0.1", + "path-exists": "^4.0.0", + "read-pkg-up": "^7.0.0", + "update-notifier": "^4.1.0" + } + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -2614,6 +3801,87 @@ "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==", "dev": true }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "update-notifier": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", + "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", + "dev": true, + "requires": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "uri-js": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", @@ -2623,6 +3891,15 @@ "punycode": "^2.1.0" } }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2641,6 +3918,16 @@ "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", "dev": true }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -2661,6 +3948,40 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "requires": { + "string-width": "^4.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -2759,6 +4080,12 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true + }, "xmlhttprequest-ssl": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", diff --git a/package.json b/package.json index 74950613c5..fdad8615fa 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "scripts": { "compile": "rimraf ./dist && tsc", "test": "npm run format:check && npm run compile && nyc mocha --require ts-node/register --reporter spec --slow 200 --bail --timeout 10000 test/socket.io.ts", + "test:types": "tsd", "format:check": "prettier --check \"lib/**/*.ts\" \"test/**/*.ts\"", "format:fix": "prettier --write \"lib/**/*.ts\" \"test/**/*.ts\"", "prepack": "npm run compile" @@ -68,6 +69,7 @@ "superagent": "^6.1.0", "supertest": "^6.0.1", "ts-node": "^9.0.0", + "tsd": "^0.14.0", "typescript": "^4.1.2" }, "contributors": [ @@ -90,5 +92,8 @@ ], "engines": { "node": ">=10.0.0" + }, + "tsd": { + "directory": "test" } } diff --git a/test/socket.io.test-d.ts b/test/socket.io.test-d.ts new file mode 100644 index 0000000000..3f814576f1 --- /dev/null +++ b/test/socket.io.test-d.ts @@ -0,0 +1,211 @@ +"use strict"; +import { DefaultEventsMap, Server, Socket } from ".."; +import { createServer } from "http"; +import { expectError, expectType } from "tsd"; +import "./support/util"; + +describe("server", () => { + describe("no event map", () => { + describe("on", () => { + it("infers correct types for listener parameters of reserved events", (done) => { + const srv = createServer(); + const sio = new Server(srv); + srv.listen(() => { + sio.on("connection", (s) => { + expectType>(s); + s.on("disconnect", (reason) => { + expectType(reason); + }); + s.on("disconnecting", (reason) => { + expectType(reason); + }); + }); + sio.on("connect", (s) => { + expectType>(s); + }); + done(); + }); + }); + + it("infers 'any' for listener parameters of other events", (done) => { + const srv = createServer(); + const sio = new Server(srv); + srv.listen(() => { + sio.on("connection", (s) => { + s.on("random", (a, b, c) => { + expectType(a); + expectType(b); + expectType(c); + done(); + }); + s.emit("random", 1, "2", [3]); + }); + }); + }); + }); + + describe("emit", () => { + it("accepts any parameters", () => { + const srv = createServer(); + const sio = new Server(srv); + srv.listen(() => { + sio.on("connection", (s) => { + s.emit("random", 1, "2", [3]); + s.emit("no parameters"); + }); + }); + }); + }); + }); + + describe("single event map", () => { + interface BidirectionalEvents { + random: (a: number, b: string, c: number[]) => void; + noParameter: void; + oneParameter: string; + "complicated name with spaces": void; + } + + describe("on", () => { + it("infers correct types for listener parameters", (done) => { + const srv = createServer(); + const sio = new Server(srv); + expectType>(sio); + srv.listen(() => { + sio.on("connection", (s) => { + expectType>(s); + s.on("random", (a, b, c) => { + expectType(a); + expectType(b); + expectType(c); + done(); + }); + s.on("complicated name with spaces", () => {}); + s.on("noParameter", () => {}); + s.on("oneParameter", (str) => { + expectType(str); + }); + }); + }); + }); + + it("does not accept arguments of wrong types", (done) => { + const srv = createServer(); + const sio = new Server(srv); + srv.listen(() => { + expectError(sio.on("wrong name", (s) => {})); + sio.on("connection", (s) => { + s.on("random", (a, b, c) => {}); + expectError(s.on("random")); + expectError(s.on("random", (a, b, c, d) => {})); + expectError(s.on("wrong name", () => {})); + expectError(s.on("wrong name")); + expectError(s.on("complicated name with spaces", (a) => {})); + expectError(s.on(2, 3)); + }); + }); + }); + }); + + describe("emit", () => { + it("accepts arguments of the correct types", () => { + const srv = createServer(); + const sio = new Server(srv); + srv.listen(() => { + sio.on("connection", (s) => { + s.emit("noParameter"); + s.emit("oneParameter", "hi"); + s.emit("random", 1, "2", [3]); + s.emit("complicated name with spaces"); + }); + }); + }); + + it("does not accept arguments of the wrong types", () => { + const srv = createServer(); + const sio = new Server(srv); + srv.listen(() => { + sio.on("connection", (s) => { + expectError(s.emit("noParameter", 2)); + expectError(s.emit("oneParameter")); + expectError(s.emit("random")); + expectError(s.emit("oneParameter", 2, 3)); + expectError(s.emit("random", (a, b, c) => {})); + expectError(s.emit("wrong name", () => {})); + expectError(s.emit("complicated name with spaces", 2)); + }); + }); + }); + }); + }); + + describe("listen and emit event maps", () => { + interface ClientToServerEvents { + helloFromClient: (message: string) => void; + } + + interface ServerToClientEvents { + helloFromServer: (message: string, x: number) => void; + } + + describe("on", () => { + it("infers correct types for listener parameters", (done) => { + const srv = createServer(); + const sio = new Server(srv); + expectType>(sio); + srv.listen(() => { + sio.on("connection", (s) => { + expectType>(s); + s.on("helloFromClient", (message) => { + expectType(message); + done(); + }); + }); + }); + }); + + it("does not accept emit events", (done) => { + const srv = createServer(); + const sio = new Server(srv); + srv.listen(() => { + sio.on("connection", (s) => { + expectError( + s.on("helloFromServer", (message, number) => { + done(); + }) + ); + }); + }); + }); + }); + + describe("emit", () => { + it("accepts arguments of the correct types", (done) => { + const srv = createServer(); + const sio = new Server(srv); + srv.listen(() => { + sio.on("connection", (s) => { + s.emit("helloFromServer", "hi", 10); + done(); + }); + }); + }); + + it("does not accept arguments of wrong types", (done) => { + const srv = createServer(); + const sio = new Server(srv); + srv.listen(() => { + sio.on("connection", (s) => { + expectError(s.emit("helloFromClient", "hi")); + expectError(s.emit("helloFromServer", "hi", 10, "10")); + expectError(s.emit("helloFromServer", "hi", "10")); + expectError(s.emit("helloFromServer", 0, 0)); + expectError(s.emit("wrong name", 10)); + expectError(s.emit("wrong name")); + done(); + }); + }); + }); + }); + }); +}); diff --git a/test/socket.io.ts b/test/socket.io.ts index cf8b647874..58ceac00ca 100644 --- a/test/socket.io.ts +++ b/test/socket.io.ts @@ -1042,7 +1042,7 @@ describe("socket.io", () => { done(); }); }); - s.client.ondata(null); + (s as any).client.ondata(null); }); }); }); @@ -2230,7 +2230,7 @@ describe("socket.io", () => { expect(s.rooms).to.contain(s.id, "a", "b", "c"); s.leave("b"); expect(s.rooms).to.contain(s.id, "a", "c"); - s.leaveAll(); + (s as any).leaveAll(); expect(s.rooms.size).to.eql(0); done(); }); @@ -2266,7 +2266,7 @@ describe("socket.io", () => { expect(s.rooms).to.contain(s.id, "a", "b"); s.leave("unknown"); expect(s.rooms).to.contain(s.id, "a", "b"); - s.leaveAll(); + (s as any).leaveAll(); expect(s.rooms.size).to.eql(0); done(); }); @@ -2469,7 +2469,7 @@ describe("socket.io", () => { srv.listen(() => { const socket = client(srv); sio.on("connection", (socket) => { - expect(socket.name).to.be("guillermo"); + expect((socket as any).name).to.be("guillermo"); done(); }); }); @@ -2591,13 +2591,13 @@ describe("socket.io", () => { socket.emit("join", "woot"); sio.on("connection", (socket) => { - socket.use((event, next) => { + (socket as any).use((event, next) => { expect(event).to.eql(["join", "woot"]); event.unshift("wrap"); run++; next(); }); - socket.use((event, next) => { + (socket as any).use((event, next) => { expect(event).to.eql(["wrap", "join", "woot"]); run++; next(); From 86df1b6aba3a47e21544e6e40182841d1a5b765f Mon Sep 17 00:00:00 2001 From: Maxime Kjaer Date: Tue, 2 Mar 2021 15:34:06 +0100 Subject: [PATCH 2/9] Add missing event type arguments --- lib/client.ts | 5 ++++- lib/index.ts | 7 +++++-- lib/namespace.ts | 19 ++++++++++++++----- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/client.ts b/lib/client.ts index 8216f5f61c..7262355175 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -29,7 +29,10 @@ export class Client< * @param conn * @package */ - constructor(server: Server, conn: Socket) { + constructor( + server: Server, + conn: Socket + ) { this.server = server; this.conn = conn; this.encoder = server.encoder; diff --git a/lib/index.ts b/lib/index.ts index 084c16e6b9..568c1ea060 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -610,7 +610,7 @@ export class Server< */ public of( name: string | RegExp | ParentNspNameMatchFn, - fn?: (socket: Socket) => void + fn?: (socket: Socket) => void ): Namespace { if (typeof name === "function" || name instanceof RegExp) { const parentNsp = new ParentNamespace(this); @@ -669,7 +669,10 @@ export class Server< * @public */ public use( - fn: (socket: Socket, next: (err?: ExtendedError) => void) => void + fn: ( + socket: Socket, + next: (err?: ExtendedError) => void + ) => void ): this { this.sockets.use(fn); return this; diff --git a/lib/namespace.ts b/lib/namespace.ts index 1ab57c8e7e..6006529fb2 100644 --- a/lib/namespace.ts +++ b/lib/namespace.ts @@ -32,7 +32,10 @@ export class Namespace< /** @private */ _fns: Array< - (socket: Socket, next: (err?: ExtendedError) => void) => void + ( + socket: Socket, + next: (err?: ExtendedError) => void + ) => void > = []; /** @private */ @@ -69,7 +72,10 @@ export class Namespace< * @public */ public use( - fn: (socket: Socket, next: (err?: ExtendedError) => void) => void + fn: ( + socket: Socket, + next: (err?: ExtendedError) => void + ) => void ): this { this._fns.push(fn); return this; @@ -82,7 +88,10 @@ export class Namespace< * @param fn - last fn call in the middleware * @private */ - private run(socket: Socket, fn: (err: ExtendedError | null) => void) { + private run( + socket: Socket, + fn: (err: ExtendedError | null) => void + ) { const fns = this._fns.slice(0); if (!fns.length) return fn(null); @@ -151,7 +160,7 @@ export class Namespace< client: Client, query, fn?: () => void - ): Socket { + ): Socket { debug("adding socket to nsp %s", this.name); const socket = new Socket(this, client, query); this.run(socket, (err) => { @@ -194,7 +203,7 @@ export class Namespace< * * @private */ - _remove(socket: Socket): void { + _remove(socket: Socket): void { if (this.sockets.has(socket.id)) { this.sockets.delete(socket.id); } else { From 7afde1c97be9e238586cbd39da21a74776269c94 Mon Sep 17 00:00:00 2001 From: Maxime Kjaer Date: Fri, 5 Mar 2021 13:51:16 +0100 Subject: [PATCH 3/9] Move npm run test:types command to npm test --- .github/workflows/ci.yml | 1 - package.json | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9eec6027e2..21f0b4bd96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,4 +24,3 @@ jobs: - run: npm test env: CI: true - - run: npm run test:types diff --git a/package.json b/package.json index fdad8615fa..a350cd78b0 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,9 @@ }, "scripts": { "compile": "rimraf ./dist && tsc", - "test": "npm run format:check && npm run compile && nyc mocha --require ts-node/register --reporter spec --slow 200 --bail --timeout 10000 test/socket.io.ts", + "test": "npm run format:check && npm run compile && npm run test:types && npm run test:unit", "test:types": "tsd", + "test:unit": "nyc mocha --require ts-node/register --reporter spec --slow 200 --bail --timeout 10000 test/socket.io.ts", "format:check": "prettier --check \"lib/**/*.ts\" \"test/**/*.ts\"", "format:fix": "prettier --write \"lib/**/*.ts\" \"test/**/*.ts\"", "prepack": "npm run compile" From 3230cf70ecd7df45e9e6901ecbb820145b008d5c Mon Sep 17 00:00:00 2001 From: Maxime Kjaer Date: Fri, 5 Mar 2021 17:04:12 +0100 Subject: [PATCH 4/9] Move typed EventEmitter code to new StrictEventEmitter class --- lib/broadcast-operator.ts | 7 +- lib/client.ts | 3 +- lib/index.ts | 73 +++------------- lib/namespace.ts | 20 +++-- lib/parent-namespace.ts | 8 +- lib/socket.ts | 34 ++++---- lib/typed-events.ts | 176 ++++++++++++++++++++++++++++++++++++++ test/socket.io.test-d.ts | 3 +- test/socket.io.ts | 15 ++-- 9 files changed, 234 insertions(+), 105 deletions(-) create mode 100644 lib/typed-events.ts diff --git a/lib/broadcast-operator.ts b/lib/broadcast-operator.ts index dc7321edaf..aff3c86665 100644 --- a/lib/broadcast-operator.ts +++ b/lib/broadcast-operator.ts @@ -7,12 +7,13 @@ import type { EventNames, EventsMap, DefaultEventsMap, -} from "./index"; + TypedEventBroadcaster, +} from "./typed-events"; export class BroadcastOperator< UserEvents extends EventsMap, UserEmitEvents extends EventsMap = UserEvents -> { +> implements TypedEventBroadcaster { constructor( private readonly adapter: Adapter, private readonly rooms: Set = new Set(), @@ -269,7 +270,7 @@ interface SocketDetails { export class RemoteSocket< UserEvents extends EventsMap = DefaultEventsMap, UserEmitEvents extends EventsMap = UserEvents -> { +> implements TypedEventBroadcaster { public readonly id: SocketId; public readonly handshake: Handshake; public readonly rooms: Set; diff --git a/lib/client.ts b/lib/client.ts index 7262355175..cd623ff348 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -2,7 +2,8 @@ import { Decoder, Encoder, Packet, PacketType } from "socket.io-parser"; import debugModule = require("debug"); import url = require("url"); import type { IncomingMessage } from "http"; -import type { DefaultEventsMap, EventsMap, Namespace, Server } from "./index"; +import type { Namespace, Server } from "./index"; +import type { DefaultEventsMap, EventsMap } from "./typed-events"; import type { Socket } from "./socket"; import type { SocketId } from "socket.io-adapter"; diff --git a/lib/index.ts b/lib/index.ts index 568c1ea060..b2b019b2f9 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -17,6 +17,12 @@ import { ServerReservedEventsMap, Socket } from "./socket"; import type { CookieSerializeOptions } from "cookie"; import type { CorsOptions } from "cors"; import type { BroadcastOperator, RemoteSocket } from "./broadcast-operator"; +import { + EventsMap, + DefaultEventsMap, + EventParams, + StrictEventEmitter, +} from "./typed-events"; const debug = debugModule("socket.io:server"); @@ -156,57 +162,14 @@ interface ServerOptions extends EngineAttachOptions { connectTimeout: number; } -/** - * An events map is an interface that maps event names to their values. Values - * can be either: - * - * - `void`, indicating that the `on` listener takes no parameters - * - a function type, representing the the type of the `on` listener - * - any other type, representing the type of the first parameter of the `on` listener - */ -export interface EventsMap { - [event: string]: any; -} - -/** - * The default events map, used if no EventsMap is given. Using this EventsMap - * is equivalent to accepting all event names, and any data. - */ -export interface DefaultEventsMap { - [event: string]: (...args: any[]) => void; -} - -/** - * Returns a union type containing all the keys of an event map. - */ -export type EventNames = keyof Map & (string | symbol); - -/** The tuple type representing the parameters of an event listener */ -export type EventParams< - Map extends EventsMap, - Ev extends keyof Map -> = Map[Ev] extends (...args: any[]) => void - ? Parameters - : Map[Ev] extends void - ? [] - : [Map[Ev]]; - -/** - * The type of listener callback that listens to the `Ev` event, as defined in `Map` - */ -export type Listener< - Map extends EventsMap, - Ev extends keyof Map -> = Map[Ev] extends (...args: any[]) => void - ? Map[Ev] - : Map[Ev] extends void - ? (...args: []) => void - : (...args: [Map[Ev]]) => void; - export class Server< UserEvents extends EventsMap = DefaultEventsMap, UserEmitEvents extends EventsMap = UserEvents -> extends EventEmitter { +> extends StrictEventEmitter< + UserEvents, + UserEmitEvents, + ServerReservedEventsMap +> { public readonly sockets: Namespace; /** @private */ @@ -704,20 +667,6 @@ export class Server< return this.sockets.in(room); } - public on< - Ev extends EventNames< - UserEvents & ServerReservedEventsMap - > - >( - ev: Ev, - listener: Listener< - UserEvents & ServerReservedEventsMap, - Ev - > - ): this { - return super.on(ev, listener); - } - /** * Excludes a room when emitting. * diff --git a/lib/namespace.ts b/lib/namespace.ts index 6006529fb2..da56d6d5f5 100644 --- a/lib/namespace.ts +++ b/lib/namespace.ts @@ -1,13 +1,13 @@ -import { Socket } from "./socket"; -import type { +import { ServerReservedEventsMap, Socket } from "./socket"; +import type { Server } from "./index"; +import { DefaultEventsMap, EventParams, EventNames, EventsMap, - Server, -} from "./index"; + StrictEventEmitter, +} from "./typed-events"; import type { Client } from "./client"; -import { EventEmitter } from "events"; import debugModule from "debug"; import type { Adapter, Room, SocketId } from "socket.io-adapter"; import { BroadcastOperator, RemoteSocket } from "./broadcast-operator"; @@ -21,7 +21,11 @@ export interface ExtendedError extends Error { export class Namespace< UserEvents extends EventsMap = DefaultEventsMap, UserEmitEvents extends EventsMap = UserEvents -> extends EventEmitter { +> extends StrictEventEmitter< + UserEvents, + UserEmitEvents, + ServerReservedEventsMap +> { public readonly name: string; public readonly sockets: Map = new Map(); @@ -188,8 +192,8 @@ export class Namespace< if (fn) fn(); // fire user-set events - super.emit("connect", socket); - super.emit("connection", socket); + super.emitReserved("connect", socket); + super.emitReserved("connection", socket); } else { debug("next called after client was closed - ignoring socket"); } diff --git a/lib/parent-namespace.ts b/lib/parent-namespace.ts index f214424a4c..6eee39b52e 100644 --- a/lib/parent-namespace.ts +++ b/lib/parent-namespace.ts @@ -1,11 +1,11 @@ import { Namespace } from "./namespace"; +import type { Server } from "./index"; import type { DefaultEventsMap, EventParams, EventNames, EventsMap, - Server, -} from "./index"; +} from "./typed-events"; import type { BroadcastOptions } from "socket.io-adapter"; export class ParentNamespace< @@ -47,10 +47,10 @@ export class ParentNamespace< const namespace = new Namespace(this.server, name); namespace._fns = this._fns.slice(0); this.listeners("connect").forEach((listener) => - namespace.on("connect", listener as (...args: any[]) => void) + namespace.on("connect", listener) ); this.listeners("connection").forEach((listener) => - namespace.on("connection", listener as (...args: any[]) => void) + namespace.on("connection", listener) ); this.children.add(namespace); this.server._nsps.set(name, namespace); diff --git a/lib/socket.ts b/lib/socket.ts index 23da20ab78..b1e41991c4 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -1,15 +1,13 @@ -import { EventEmitter } from "events"; import { Packet, PacketType } from "socket.io-parser"; import url = require("url"); import debugModule from "debug"; -import type { - DefaultEventsMap, +import type { Server } from "./index"; +import { EventParams, EventNames, EventsMap, - Listener, - Server, -} from "./index"; + StrictEventEmitter, +} from "./typed-events"; import type { Client } from "./client"; import type { Namespace } from "./namespace"; import type { IncomingMessage, IncomingHttpHeaders } from "http"; @@ -38,6 +36,7 @@ export interface ServerReservedEventsMap< export interface SocketReservedEventsMap { disconnect: (reason: string) => void; disconnecting: (reason: string) => void; + error: Error; } // EventEmitter reserved events: https://nodejs.org/api/events.html#events_event_newlistener @@ -119,7 +118,11 @@ export interface Handshake { export class Socket< UserEvents extends EventsMap = EventsMap, UserEmitEvents extends EventsMap = UserEvents -> extends EventEmitter { +> extends StrictEventEmitter< + UserEvents, + UserEmitEvents, + SocketReservedEventsMap +> { public readonly id: SocketId; public readonly handshake: Handshake; /** @@ -220,13 +223,6 @@ export class Socket< return true; } - public on>( - ev: Ev, - listener: Listener - ): this { - return super.on(ev, listener); - } - /** * Targets a room when broadcasting. * @@ -474,9 +470,9 @@ export class Socket< * * @private */ - _onerror(err): void { + _onerror(err: Error): void { if (this.listeners("error").length) { - super.emit("error", err); + this.emitReserved("error", err); } else { console.error("Missing error handler on `socket`."); console.error(err.stack); @@ -494,13 +490,13 @@ export class Socket< _onclose(reason: string): this | undefined { if (!this.connected) return this; debug("closing socket - reason %s", reason); - super.emit("disconnecting", reason); + this.emitReserved("disconnecting", reason); this.leaveAll(); this.nsp._remove(this); this.client._remove(this); this.connected = false; this.disconnected = true; - super.emit("disconnect", reason); + this.emitReserved("disconnect", reason); return; } @@ -594,7 +590,7 @@ export class Socket< return this._onerror(err); } if (this.connected) { - super.emit.apply(this, event); + this.untypedEventEmitter.emit.apply(this, event); } else { debug("ignore packet received after disconnection"); } diff --git a/lib/typed-events.ts b/lib/typed-events.ts new file mode 100644 index 0000000000..3fedde9f4e --- /dev/null +++ b/lib/typed-events.ts @@ -0,0 +1,176 @@ +import { EventEmitter } from "events"; + +/** + * An events map is an interface that maps event names to their values. Values + * can be either: + * + * - `void`, indicating that the `on` listener takes no parameters + * - a function type, representing the the type of the `on` listener + * - any other type, representing the type of the first parameter of the `on` listener + */ +export interface EventsMap { + [event: string]: any; +} + +/** + * The default events map, used if no EventsMap is given. Using this EventsMap + * is equivalent to accepting all event names, and any data. + */ +export interface DefaultEventsMap { + [event: string]: (...args: any[]) => void; +} + +/** + * Returns a union type containing all the keys of an event map. + */ +export type EventNames = keyof Map & (string | symbol); + +/** The tuple type representing the parameters of an event listener */ +export type EventParams< + Map extends EventsMap, + Ev extends EventNames +> = Map[Ev] extends (...args: any[]) => void + ? Parameters + : Map[Ev] extends void + ? [] + : [Map[Ev]]; + +/** + * The type of listener callback that listens to the `Ev` event, as defined in `Map` + */ +export type Listener< + Map extends EventsMap, + Ev extends EventNames +> = Map[Ev] extends (...args: any[]) => void + ? Map[Ev] + : Map[Ev] extends void + ? (...args: []) => void + : (...args: [Map[Ev]]) => void; + +/** + * The event names that are either in ReservedEvents or in UserEvents + */ +export type ReservedOrUserEventNames< + ReservedEventsMap extends EventsMap, + UserEvents extends EventsMap +> = EventNames | EventNames; + +/** + * Type of a listener of a user event or a reserved event. If `Ev` is in + * `ReservedEvents`, the reserved event listener is returned. + */ +export type ReservedOrUserListener< + ReservedEvents extends EventsMap, + UserEvents extends EventsMap, + Ev extends ReservedOrUserEventNames +> = Ev extends EventNames + ? Listener + : Ev extends EventNames + ? Listener + : never; + +/** + * Interface for classes that aren't `EventEmitter`s, but still expose a + * strictly typed `emit` method. + */ +export interface TypedEventBroadcaster { + emit>( + ev: Ev, + ...args: EventParams + ): boolean; +} + +/** + * Strictly typed version of an `EventEmitter`. A `TypedEventEmitter` takes type + * parameters for mappings of event names to event data types, and strictly + * types method calls to the `EventEmitter` according to these event maps. + * + * @typeParam ListenEvents - `EventsMap` of user-defined events that can be + * listened to with `on` or `once` + * @typeParam EmitEvents - `EventsMap` of user-defined events that can be + * emitted with `emit` + * @typeParam ReservedEvents - `EventsMap` of reserved events, that can be + * emitted by socket.io with `emitReserved`, and can be listened to with + * `listen`. + */ +export abstract class StrictEventEmitter< + ListenEvents extends EventsMap, + EmitEvents extends EventsMap, + ReservedEvents extends EventsMap = {} + > + extends EventEmitter + implements TypedEventBroadcaster { + /** An non-strictly typed version of this event emitter. */ + protected untypedEventEmitter: EventEmitter = this; + + /** + * Adds the `listener` function as an event listener for `ev`. + * + * @param ev Name of the event + * @param listener Callback function + */ + on>( + ev: Ev, + listener: ReservedOrUserListener + ): this { + return super.on(ev, listener); + } + + /** + * Adds a one-time `listener` function as an event listener for `ev`. + * + * @param ev Name of the event + * @param listener Callback function + */ + once>( + ev: Ev, + listener: ReservedOrUserListener + ): this { + return super.once(ev, listener); + } + + /** + * Emits an event. + * + * @param ev Name of the event + * @param args Values to send to listeners of this event + */ + emit>( + ev: Ev, + ...args: EventParams + ): boolean { + return super.emit(ev, ...args); + } + + /** + * Emits a reserved event. + * + * This method is `protected`, so that only a class extending `TypedEmitter` + * can emit its own reserved events. + * + * @param ev Reserved event name + * @param args Arguments to emit along with the event + */ + protected emitReserved>( + ev: Ev, + ...args: EventParams + ): boolean { + return super.emit(ev, ...args); + } + + /** + * Returns the listeners listening to an event. + * + * @param event Event name + * @returns Array of listeners subscribed to `event` + */ + listeners>( + event: Ev + ): ReservedOrUserListener[] { + return super.listeners(event) as ReservedOrUserListener< + ReservedEvents, + ListenEvents, + Ev + >[]; + } +} diff --git a/test/socket.io.test-d.ts b/test/socket.io.test-d.ts index 3f814576f1..e10f9e0266 100644 --- a/test/socket.io.test-d.ts +++ b/test/socket.io.test-d.ts @@ -1,5 +1,6 @@ "use strict"; -import { DefaultEventsMap, Server, Socket } from ".."; +import { Server, Socket } from ".."; +import type { DefaultEventsMap } from "../dist/typed-events"; import { createServer } from "http"; import { expectError, expectType } from "tsd"; import "./support/util"; diff --git a/test/socket.io.ts b/test/socket.io.ts index 58ceac00ca..0719733d8b 100644 --- a/test/socket.io.ts +++ b/test/socket.io.ts @@ -14,6 +14,7 @@ const ioc = require("socket.io-client"); import "./support/util"; import "./utility-methods"; +import type { SocketId } from "socket.io-adapter"; // Creates a socket.io client for the given server function client(srv, nsp?: string | object, opts?: object) { @@ -605,7 +606,7 @@ describe("socket.io", () => { const srv = createServer(); const sio = new Server(srv); const chatSids: string[] = []; - let otherSid = null; + let otherSid: SocketId | null = null; srv.listen(() => { const c1 = client(srv, "/chat"); const c2 = client(srv, "/chat", { forceNew: true }); @@ -632,9 +633,9 @@ describe("socket.io", () => { it("should find all clients in a namespace room", (done) => { const srv = createServer(); const sio = new Server(srv); - let chatFooSid = null; - let chatBarSid = null; - let otherSid = null; + let chatFooSid: SocketId | null = null; + let chatBarSid: SocketId | null = null; + let otherSid: SocketId | null = null; srv.listen(() => { const c1 = client(srv, "/chat"); const c2 = client(srv, "/chat", { forceNew: true }); @@ -671,9 +672,9 @@ describe("socket.io", () => { it("should find all clients across namespace rooms", (done) => { const srv = createServer(); const sio = new Server(srv); - let chatFooSid = null; - let chatBarSid = null; - let otherSid = null; + let chatFooSid: SocketId | null = null; + let chatBarSid: SocketId | null = null; + let otherSid: SocketId | null = null; srv.listen(() => { const c1 = client(srv, "/chat"); const c2 = client(srv, "/chat", { forceNew: true }); From 055c75890ff94e655bcd386a7eee1ac6be26e07d Mon Sep 17 00:00:00 2001 From: Maxime Kjaer Date: Fri, 5 Mar 2021 16:44:07 +0100 Subject: [PATCH 5/9] Add comment to say test-d file is covered by tsd --- test/socket.io.test-d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/socket.io.test-d.ts b/test/socket.io.test-d.ts index e10f9e0266..2f80049135 100644 --- a/test/socket.io.test-d.ts +++ b/test/socket.io.test-d.ts @@ -5,6 +5,8 @@ import { createServer } from "http"; import { expectError, expectType } from "tsd"; import "./support/util"; +// This file is run by tsd, not mocha. + describe("server", () => { describe("no event map", () => { describe("on", () => { From 36d961324ee605d47b3713df814b3ebeef816f9d Mon Sep 17 00:00:00 2001 From: Maxime Kjaer Date: Tue, 9 Mar 2021 13:58:32 +0100 Subject: [PATCH 6/9] Improve typing of client sockets in tests --- test/socket.io.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/socket.io.ts b/test/socket.io.ts index 0719733d8b..65b80a0c35 100644 --- a/test/socket.io.ts +++ b/test/socket.io.ts @@ -10,14 +10,14 @@ import expect from "expect.js"; import type { AddressInfo } from "net"; import * as io_v2 from "socket.io-client-v2"; -const ioc = require("socket.io-client"); +import * as ioc from "socket.io-client"; import "./support/util"; import "./utility-methods"; import type { SocketId } from "socket.io-adapter"; // Creates a socket.io client for the given server -function client(srv, nsp?: string | object, opts?: object) { +function client(srv, nsp?: string | object, opts?: object): ioc.Socket { if ("object" == typeof nsp) { opts = nsp; nsp = undefined; @@ -25,7 +25,7 @@ function client(srv, nsp?: string | object, opts?: object) { let addr = srv.address(); if (!addr) addr = srv.listen().address(); const url = "ws://localhost:" + addr.port + (nsp || ""); - return ioc(url, opts); + return ioc.io(url, opts); } const success = (sio, clientSocket, done) => { @@ -334,7 +334,9 @@ describe("socket.io", () => { const net = require("net"); const server = net.createServer(); - const clientSocket = ioc("ws://0.0.0.0:" + PORT, { reconnection: false }); + const clientSocket = ioc.io("ws://0.0.0.0:" + PORT, { + reconnection: false, + }); clientSocket.on("disconnect", () => { expect(Object.keys(sio._nsps["/"].sockets).length).to.equal(0); From a7e3035a1b851ca71ec1dd9ef0186116169de654 Mon Sep 17 00:00:00 2001 From: Maxime Kjaer Date: Tue, 9 Mar 2021 13:59:01 +0100 Subject: [PATCH 7/9] Fix wrong method call removeEventListener does not exist in component-emitter --- test/socket.io.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/socket.io.ts b/test/socket.io.ts index 65b80a0c35..8d0d8409cd 100644 --- a/test/socket.io.ts +++ b/test/socket.io.ts @@ -955,8 +955,7 @@ describe("socket.io", () => { const sio = new Server(srv); srv.listen(() => { const clientSocket = client(srv, { reconnection: false }); - clientSocket.on("connect", function init() { - clientSocket.removeListener("connect", init); + clientSocket.once("connect", () => { clientSocket.io.engine.close(); clientSocket.connect(); From 78502d9b6715946b003ce86e96136e646ac24bde Mon Sep 17 00:00:00 2001 From: Maxime Kjaer Date: Tue, 9 Mar 2021 14:14:00 +0100 Subject: [PATCH 8/9] Fix untyped message emit --- lib/socket.ts | 2 +- lib/typed-events.ts | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/socket.ts b/lib/socket.ts index b1e41991c4..25fee74b7c 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -590,7 +590,7 @@ export class Socket< return this._onerror(err); } if (this.connected) { - this.untypedEventEmitter.emit.apply(this, event); + super.emitUntyped.apply(this, event); } else { debug("ignore packet received after disconnection"); } diff --git a/lib/typed-events.ts b/lib/typed-events.ts index 3fedde9f4e..bd4a6a6997 100644 --- a/lib/typed-events.ts +++ b/lib/typed-events.ts @@ -100,9 +100,6 @@ export abstract class StrictEventEmitter< > extends EventEmitter implements TypedEventBroadcaster { - /** An non-strictly typed version of this event emitter. */ - protected untypedEventEmitter: EventEmitter = this; - /** * Adds the `listener` function as an event listener for `ev`. * @@ -158,6 +155,20 @@ export abstract class StrictEventEmitter< return super.emit(ev, ...args); } + /** + * Emits an event. + * + * This method is `protected`, so that only a class extending + * `StrictEventEmitter` can get around the strict typing. This is useful for + * calling `emit.apply`, which can be called as `emitUntyped.apply`. + * + * @param ev Event name + * @param args Arguments to emit along with the event + */ + protected emitUntyped(ev: string, ...args: any[]): boolean { + return super.emit(ev, ...args); + } + /** * Returns the listeners listening to an event. * From 7e9a378c82e9a6851f3f5b055800e6588f795b09 Mon Sep 17 00:00:00 2001 From: Maxime Kjaer Date: Tue, 9 Mar 2021 14:20:59 +0100 Subject: [PATCH 9/9] Fix class name in documentation --- lib/typed-events.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/typed-events.ts b/lib/typed-events.ts index bd4a6a6997..88071f5011 100644 --- a/lib/typed-events.ts +++ b/lib/typed-events.ts @@ -142,8 +142,8 @@ export abstract class StrictEventEmitter< /** * Emits a reserved event. * - * This method is `protected`, so that only a class extending `TypedEmitter` - * can emit its own reserved events. + * This method is `protected`, so that only a class extending + * `StrictEventEmitter` can emit its own reserved events. * * @param ev Reserved event name * @param args Arguments to emit along with the event