diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index a8a0b06..0000000 --- a/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -# top-most EditorConfig file -root = true - -# Unix-style newlines with a newline ending every file -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -insert_final_newline = true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 94caacc..d964f63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,11 +2,11 @@ name: CI on: [push, pull_request] jobs: - build: + test: runs-on: ubuntu-latest strategy: matrix: - node: [14.x, 16.x, 18.x] + node: [16.x, 18.x, 19.x] name: Node ${{ matrix.node }} steps: - uses: actions/checkout@v1 diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 2b7b234..8ea2d2a 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -6,7 +6,7 @@ on: - 'v*.*.*-rc**' jobs: - build: + release: runs-on: ubuntu-latest permissions: contents: write diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9a9451c..b142cd3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,7 +5,7 @@ on: - 'v[0-9]+.[0-9]+.[0-9]+' jobs: - build: + release: runs-on: ubuntu-latest permissions: contents: write diff --git a/bin/wait-on b/bin/wait-on deleted file mode 100755 index 391cd1d..0000000 --- a/bin/wait-on +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs') -const minimist = require('minimist') -const path = require('path') -const waitOn = require('../') - -const minimistOpts = { - string: ['c', 'd', 'i', 's', 't', 'w', 'httpTimeout', 'tcpTimeout'], - boolean: ['h', 'l', 'r', 'v'], - alias: { - c: 'config', - d: 'delay', - i: 'interval', - l: 'log', - r: 'reverse', - s: 'simultaneous', - t: 'timeout', - v: 'verbose', - w: 'window', - h: 'help' - } -} - -const argv = minimist(process.argv.slice(2), minimistOpts) -// if a js/json configuration file is provided require it -const configOpts = argv.config ? require(path.resolve(argv.config)) : {} -const hasResources = argv._.length || (configOpts.resources && configOpts.resources.length) - -if (argv.help || !hasResources) { - // help - fs.createReadStream(path.join(__dirname, '/usage.txt')) - .pipe(process.stdout) - .on('close', function () { - process.exit(1) - }) -} else { - // if resources are present in the command line then they take - // precedence over those in the config file. - if (argv._.length) { - configOpts.resources = argv._ - } - - // now check for specific options and set those - const opts = [ - 'delay', - 'httpTimeout', - 'interval', - 'log', - 'reverse', - 'simultaneous', - 'timeout', - 'tcpTimeout', - 'verbose', - 'window' - ].reduce(function (accum, x) { - if (argv[x]) { - accum[x] = argv[x] - } - return accum - }, configOpts) - - waitOn(opts, function (err) { - if (err) { - return errorExit(err) - } - // success, could just let it exit on its own, however since - // rxjs window waits an extra loop before heeding the unsubscribe - // we can exit to speed things up - process.exit(0) - }) -} - -function errorExit (err) { - if (err.stack) { - console.error(err.stack) - } else { - console.error(String(err)) - } - process.exit(1) -} diff --git a/exampleConfig.js b/exampleConfig.js index c0f7020..714f791 100644 --- a/exampleConfig.js +++ b/exampleConfig.js @@ -22,4 +22,4 @@ module.exports = { }, // optional default resources if not specified in command args resources: ['http://foo/bar', 'http://cat/dog'] -}; +} diff --git a/examples/http.js b/examples/http.js new file mode 100644 index 0000000..fe5c13f --- /dev/null +++ b/examples/http.js @@ -0,0 +1,9 @@ +const { WaitOn } = require('..') + +WaitOn({ + resources: ['http://localhost:3000'], + timeout: 10000, + events: { + onResourceResponse: console.log + } +}).then(res => console.log('done:', res), console.error) diff --git a/examples/socket.js b/examples/socket.js new file mode 100644 index 0000000..a33c20e --- /dev/null +++ b/examples/socket.js @@ -0,0 +1,9 @@ +const { WaitOn } = require('..') + +WaitOn({ + resources: ['socket:/path/to/socket.sock'], + timeout: 10000, + events: { + onResourceResponse: console.log + } +}).then(res => console.log('done:', res), console.error) diff --git a/examples/tcp.js b/examples/tcp.js new file mode 100644 index 0000000..855e1d3 --- /dev/null +++ b/examples/tcp.js @@ -0,0 +1,9 @@ +const { WaitOn } = require('..') + +WaitOn({ + resources: ['tcp://localhost:3000'], + timeout: 10000, + events: { + onResourceResponse: console.log + } +}).then(res => console.log('done:', res), console.error) diff --git a/bin/usage.txt b/help.txt similarity index 100% rename from bin/usage.txt rename to help.txt diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..565554e --- /dev/null +++ b/index.d.ts @@ -0,0 +1,60 @@ +/// +import { AgentOptions as HTTPAgentOptions } from "node:http"; +import { AgentOptions as HTTPSAgentOptions } from "node:https"; + +declare function WaitOn( + options: WaitOnOptions, + cb?: WaitOnCallback +): Promise | void; + +type WaitOnCallback = (err?: Error) => unknown; + +type WaitOnProxyConfig = { + host?: string; + protocol?: string; + auth?: WaitOnOptions["auth"]; +}; + +type WaitOnResourcesType = + | `file:${string}` + | `http-get:${string}` + | `https-get:${string}` + | `http:${string}` + | `https:${string}` + | `tcp:${string}` + | `socket:${string}`; + +type WaitOnValidateStatusCallback = (status: number) => boolean; + +type WaitOnOptions = { + resources: WaitOnResourcesType[]; + delay?: number; + interval?: number; + log?: boolean; + reverse?: boolean; + simultaneous?: number; + timeout?: number; + tcpTimeout?: number; + verbose?: boolean; + window?: number; + passphrase?: string; + proxy?: boolean | WaitOnProxyConfig; + auth?: { + user: string; + pass: string; + }; + headers?: Record; + validateStatus?: WaitOnValidateStatusCallback; + strictSSL?: boolean; +} & HTTPAgentOptions & + HTTPSAgentOptions; + +export default WaitOn; +export { + WaitOnOptions, + WaitOnProxyConfig, + WaitOnResourcesType, + WaitOnValidateStatusCallback, + WaitOnCallback, + WaitOn, +}; diff --git a/index.js b/index.js new file mode 100644 index 0000000..60a8d6b --- /dev/null +++ b/index.js @@ -0,0 +1,261 @@ +'use strict' + +const { once } = require('node:events') +const { setTimeout } = require('node:timers/promises') +const { join, isAbsolute } = require('node:path') + +const { AsyncPool } = require('@metcoder95/tiny-pool') + +const { + validateHooks, + validateOptions, + parseAjvError, + parseAjvErrors +} = require('./lib/validate') +const { createHTTPResource } = require('./lib/http') +const { createTCPResource } = require('./lib/tcp') +const { createSocketResource } = require('./lib/socket') +const { createFileResource } = require('./lib/file') + +/** + Waits for resources to become available before calling callback + + Polls file, http(s), tcp ports, sockets for availability. + + Resource types are distinquished by their prefix with default being `file:` + - file:/path/to/file - waits for file to be available and size to stabilize + - http://foo.com:8000/bar verifies HTTP HEAD request returns 2XX + - https://my.bar.com/cat verifies HTTPS HEAD request returns 2XX + - http-get: - HTTP GET returns 2XX response. ex: http://m.com:90/foo + - https-get: - HTTPS GET returns 2XX response. ex: https://my/bar + - tcp:my.server.com:3000 verifies a service is listening on port + - socket:/path/sock verifies a service is listening on (UDS) socket + For http over socket, use http://unix:SOCK_PATH:URL_PATH + like http://unix:/path/to/sock:/foo/bar or + http-get://unix:/path/to/sock:/foo/bar + + @param opts object configuring waitOn + @param opts.resources array of string resources to wait for. prefix determines the type of resource with the default type of `file:` + @param opts.delay integer - optional initial delay in ms, default 0 + @param opts.httpTimeout integer - optional http HEAD/GET timeout to wait for request, default 0 + @param opts.interval integer - optional poll resource interval in ms, default 250ms + @param opts.log boolean - optional flag to turn on logging to stdout + @param opts.reverse boolean - optional flag which reverses the mode, succeeds when resources are not available + @param opts.simultaneous integer - optional limit of concurrent connections to a resource, default Infinity + @param opts.tcpTimeout - Maximum time in ms for tcp connect, default 300ms + @param opts.timeout integer - optional timeout in ms, default Infinity. Aborts with error. + @param opts.verbose boolean - optional flag to turn on debug log + @param opts.window integer - optional stabilization time in ms, default 750ms. Waits this amount of time for file sizes to stabilize or other resource availability to remain unchanged. If less than interval then will be reset to interval + @param cb optional callback function with signature cb(err) - if err is provided then, resource checks did not succeed + if not specified, wait-on will return a promise that will be rejected if resource checks did not succeed or resolved otherwise + */ +function WaitOn (opts, cb) { + if (cb != null && cb.constructor.name === 'Function') { + waitOnImpl(opts).then(cb, cb) + } else { + return waitOnImpl(opts) + } +} + +async function waitOnImpl (opts) { + // TODO: deepclone instead of shallow + const waitOnOptions = Object.assign(Object.create(null), opts) + const validResult = validateOptions(waitOnOptions) + if (!validResult) { + const parsedError = parseAjvErrors(validateOptions.errors) + throw new Error(`Invalid options: ${parsedError}`) + } + + // window needs to be at least interval + if (waitOnOptions.window < waitOnOptions.interval) { + waitOnOptions.window = waitOnOptions.interval + } + + // Validate hooks + if (waitOnOptions.hooks != null) { + const areHooksInvalid = validateHooks(waitOnOptions.hooks) + if (areHooksInvalid != null) { + const parsedError = parseAjvError(areHooksInvalid) + throw new Error(`Invalid hook: ${parsedError}`) + } + } + + const { + resources: incomingResources, + timeout, + simultaneous, + events + } = waitOnOptions + const resources = [] + const invalidResources = [] + + for (const resource of incomingResources) { + const parsedResource = createResource(waitOnOptions, resource) + if (parsedResource != null) { + resources.push(parsedResource) + } else { + invalidResources.push(resource) + } + } + + if (invalidResources.length > 0 && events?.onInvalidResource != null) { + for (const resource of invalidResources) { + events.onInvalidResource(resource) + } + } + + if (resources.length === 0) { + throw new Error(`No valid resources provided - ${resources.join(', ')}`) + } + + const pool = new AsyncPool({ + maxConcurrent: simultaneous, + maxEnqueued: resources.length * 2 + }) + const controller = new AbortController() + const timerController = new AbortController() + const globalState = new Map() + + if (waitOnOptions.delay != null && waitOnOptions.delay > 0) { + await setTimeout(waitOnOptions.delay) + } + + for (const resource of resources) { + const { onResponse, onError } = handleResponse({ + resource, + pool, + waitOnOptions, + signal: controller.signal, + state: globalState + }) + + const promise = pool.run(resource.exec.bind(null, controller.signal)) + promise.then(onResponse, onError) + } + + const timer = timedout(timeout, controller, timerController.signal) + + let success + while (success == null) { + let unfinished = false + // Serves as checkpoint to validate the status of all resources + const result = await Promise.race([once(pool, 'idle'), timer]) + // If the `once` function returns an array when the event is emitted, then + // the pool is idle, otherwise the timer has expired + const timedout = Array.isArray(result) ? false : result + + for (const [, state] of globalState) { + if (!state) { + unfinished = true + break + } + } + + if (unfinished && !timedout) { + continue + } else { + timerController.abort() + success = !timedout + break + } + } + + return success +} + +async function timedout (timeout, controller, signal) { + const timed = await setTimeout(timeout, true, { + signal + }) + + process.nextTick(() => controller.abort()) + + return timed +} + +function handleResponse ({ resource, pool, signal, waitOnOptions, state }) { + const { interval, events, reverse } = waitOnOptions + + state.set(resource.name, false) + + return { + onError, + onResponse + } + + function onResponse ({ successfull, reason }) { + // All done + if ((reverse && !successfull) || (!reverse && successfull)) { + events?.onResourceDone?.(resource.name) + state.set(resource.name, true) + + return + } + + events?.onResourceResponse?.(resource.name, reason) + + if (signal.aborted) { + events?.onResourceError?.(resource.name, new Error('Request timed out')) + state.set(resource.name, true) + + return + } + + if (signal.aborted) { + events?.onResourceTimeout?.(resource.name, new Error('Request timed out')) + state.set(resource.name, true) + + return + } + + return setTimeout(interval) + .then(() => pool.run(resource.exec.bind(null, signal))) + .then(onResponse, onError) + } + + function onError (err) { + if (signal.aborted) { + events?.onResourceTimeout?.(resource.name, new Error('Request timed out')) + state.set(resource.name, true) + + return + } + + events?.onResourceError?.(resource.name, err) + } +} + +function createResource (deps, resource) { + let protocol + // TODO: refactor resource to use URL instance instead + try { + protocol = new URL(resource).protocol + } catch { + // Not a valid URL meaning that we fallback into file protocol + const parsed = new URL( + `file:/${isAbsolute(resource) ? resource : join(process.cwd(), resource)}` + ) + protocol = parsed.protocol + resource = parsed.href + } + + switch (protocol) { + case 'https-get:': + case 'http-get:': + case 'https:': + case 'http:': + return createHTTPResource(deps, resource) + case 'tcp:': + return createTCPResource(deps, resource) + case 'socket:': + return createSocketResource(deps, resource) + case 'file:': + default: + return createFileResource(deps, resource) + } +} + +// Add support for multiple export combos +module.exports = WaitOn +module.exports.WaitOn = WaitOn +module.exports.default = WaitOn diff --git a/lib/file.js b/lib/file.js new file mode 100644 index 0000000..1182fe6 --- /dev/null +++ b/lib/file.js @@ -0,0 +1,72 @@ +'use strict' +const { stat } = require('node:fs/promises') +const { setTimeout } = require('node:timers/promises') + +// TODO: add config for socke timeout +function createFileResource (config, resource) { + const { window } = config + const { href } = new URL(resource) + const path = href.split('file:/')[1] + let prevSize = -1 + let prevMtime = -1 + + return { + exec, + name: href + } + + async function exec (signal) { + const operation = { + successfull: false, + reason: 'unknown' + } + + // TODO: rediscuss the implementation for the file resource + /** + * On Node > v19 we can use fs.watch instead of polling + * The stability might be better measured by comparing + * the mtime of the file with the last mtime over the interval + * window until the timeout finishes + */ + + try { + if (prevSize > 0 && prevMtime > 0 && !signal.aborted) { + await setTimeout(window, null, { signal }) + } + + const { + size: currentSize, + mtimeMs: lastMofiedAt, + birthtime, + mtime + } = await stat(path) + + if (prevSize === -1 && prevMtime === -1) { + prevSize = currentSize + prevMtime = lastMofiedAt + operation.reason = `File ${resource} found, specs: created: ${birthtime}, size: ${currentSize}, modified: ${mtime}` + } else if ( + prevMtime - lastMofiedAt !== 0 || + prevSize - currentSize !== 0 + ) { + // Control variables + prevSize = currentSize + prevMtime = lastMofiedAt + + // Reporting variables + operation.reason = `File ${resource} modified, specs: created: ${birthtime}, old-size: ${prevSize}, new-size: ${currentSize}, modified: ${mtime}` + } else { + operation.successfull = true + operation.reason = `File ${resource} stable, specs: created: ${birthtime}, size: ${currentSize}, modified: ${mtime}` + } + } catch (err) { + operation.reason = err.message + } + + return operation + } +} + +module.exports = { + createFileResource +} diff --git a/lib/http.js b/lib/http.js new file mode 100644 index 0000000..78981a6 --- /dev/null +++ b/lib/http.js @@ -0,0 +1,85 @@ +'use strict' +const { Agent, ProxyAgent, request } = require('undici') + +const HTTP_GET_RE = /^https?-get:/ +const HTTP_UNIX_RE = /^http:\/\/unix:([^:]+):([^:]+)$/ + +function getHTTPAgent (config, resource) { + const { + followRedirect, + maxRedirections, + timeout, + http: { bodyTimeout, headersTimeout } = {}, + proxy, + strictSSL: rejectUnauthorized + } = config + const isProxied = proxy != null + const url = resource.replace('-get:', ':') + // http://unix:/sock:/url + const matchHttpUnixSocket = HTTP_UNIX_RE.exec(url) + const socketPath = matchHttpUnixSocket != null ? matchHttpUnixSocket[1] : null + + const httpOptions = { + maxRedirections: followRedirect != null ? maxRedirections : 0, + bodyTimeout, + headersTimeout, + connections: 1, // Single connection per resource + pipelining: 0, // to disable keep-alive + connect: { + timeout, + socketPath, + rejectUnauthorized + } + } + + return isProxied + ? new ProxyAgent(Object.assign({}, httpOptions, proxy)) + : new Agent(httpOptions) +} + +function createHTTPResource (config, resource) { + const dispatcher = getHTTPAgent(config, resource) + const method = HTTP_GET_RE.test(resource) ? 'get' : 'head' + const url = resource.replace('-get:', ':') + + return { + exec, + name: resource + } + + async function exec (signal) { + const start = Date.now() + const operation = { + successfull: false, + reason: 'unknown' + } + + try { + // TODO: implement small happy eyeballs algorithm for IPv4/IPv6 on localhost + // TODO: implement window tolerance feature + const { statusCode, body } = await request(url, { + method, + dispatcher, + signal + }) + const duration = Date.now() - start + + // We allow data to flow without worrying about it + body.resume() + + // TODO: add support allow range of status codes + operation.successfull = statusCode >= 200 && statusCode < 500 + operation.reason = `HTTP(s) request for ${method}-${resource} replied with code ${statusCode} - duration ${duration}ms` + } catch (e) { + operation.reason = `HTTP(s) request for ${method}-${resource} errored: ${ + e.message + } - duration ${Date.now() - start}` + } + + return operation + } +} + +module.exports = { + createHTTPResource +} diff --git a/lib/socket.js b/lib/socket.js new file mode 100644 index 0000000..5c19299 --- /dev/null +++ b/lib/socket.js @@ -0,0 +1,61 @@ +'use strict' +const net = require('node:net') +const { join } = require('node:path') + +// TODO: add config for socke timeout +function createSocketResource (_, resource) { + const { href, host, pathname } = new URL(resource) + const path = join(host, pathname) + + return { + exec, + name: href + } + + function exec (signal) { + let res + const operation = { + successfull: false, + reason: 'unknown' + } + const promise = new Promise(resolve => { + res = resolve + }) + const start = Date.now() + const socket = net.connect({ + path, + signal + }) + + socket.once('connect', () => { + const duration = Date.now() - start + operation.successfull = true + operation.reason = `Socket connection established for ${href} - duration ${duration}ms` + + socket.destroy() + res(operation) + }) + + socket.on('error', err => { + const duration = Date.now() - start + operation.reason = `Socket connection failed for ${href} - duration ${duration}ms - ${err.message}` + + socket.destroy() + res(operation) + }) + + socket.once('timeout', () => { + const duration = Date.now() - start + operation.reason = `Socket connection timed out for ${href} - duration ${duration}ms` + + socket.destroy() + res(operation) + }) + + return promise + } +} + +module.exports = { + createSocketResource +} diff --git a/lib/tcp.js b/lib/tcp.js new file mode 100644 index 0000000..2e4e90f --- /dev/null +++ b/lib/tcp.js @@ -0,0 +1,72 @@ +'use strict' +const net = require('node:net') + +function createTCPResource (config, resource) { + const { tcpTimeout } = config + const { port, href, hostname: host } = new URL(resource) + /** @type {import('node:net').Socket} */ + let socket + + return { + exec, + name: href + } + + function exec (signal) { + let res + const operation = { + successfull: false, + reason: 'unknown' + } + const promise = new Promise(resolve => { + res = resolve + }) + const start = Date.now() + + if (socket == null) { + socket = new net.Socket({ + signal + }) + + socket.setKeepAlive(false) + socket.setTimeout(tcpTimeout) + } + + socket.on('error', err => { + const duration = Date.now() - start + operation.reason = `TCP connection failed for ${href} - duration ${duration}ms - ${err.message}` + + if (!socket.destroyed) { + socket.destroy() + } + + res(operation) + }) + + socket.once('timeout', () => { + const duration = Date.now() - start + operation.reason = `TCP connection timed out for ${href} - duration ${duration}ms` + + if (!socket.destroyed) { + socket.destroy() + } + + res(operation) + }) + + socket.connect({ host, port }, () => { + const duration = Date.now() - start + operation.successfull = true + operation.reason = `TCP connection established for ${href} - duration ${duration}ms` + + socket.destroy() + res(operation) + }) + + return promise + } +} + +module.exports = { + createTCPResource +} diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 0000000..a35fc45 --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,13 @@ +const PREFIX_RESOURCE = /^((https?-get|https?|tcp|socket|file):)(.+)$/ + +function extractPath (resource) { + const m = PREFIX_RESOURCE.exec(resource) + if (m) { + return m[3] + } + return resource +} + +module.exports = { + extractPath +} diff --git a/lib/validate.js b/lib/validate.js new file mode 100644 index 0000000..fcd97a1 --- /dev/null +++ b/lib/validate.js @@ -0,0 +1,192 @@ +const Ajv = require('ajv') + +const SUPPORTED_HOOKS = [ + 'onInvalidResource', + 'onResourceTimeout', + 'onResourceResponse', + 'onResourceError', + 'onResourceDone' +] + +const ajv = new Ajv({ + strict: false, + useDefaults: true +}) + +// TODO: add function keyword +// ajv.addKeyword({ +// keyword: 'function', +// compile: (_, parent) => { +// return data => { +// const shouldValidateParams = parent.params ?? false +// const shouldAllowAsync = +// parent.allowAsync != null ? parent.allowAsync : false +// let result = false + +// if (shouldAllowAsync) { +// result = +// data.constructor.name === 'AsyncFunction' || +// data.constructor.name === 'Function' +// } else { +// result = data.constructor.name === 'Function' +// } + +// if (result && shouldValidateParams) { +// result = data.length === parent.params +// } +// } +// }, +// metaSchema: { +// type: 'boolean', +// additionalItems: false +// } +// }) + +function parseAjvError (error) { + return error.message +} + +function parseAjvErrors (errors) { + let result = '' + for (const error of errors) { + result += parseAjvError(error) + } + + return result +} + +function validateHooks (hooks) { + for (const hook of SUPPORTED_HOOKS) { + if (hooks[hook] != null && hooks[hook].constructor.name !== 'Function') { + return { + message: `Invalid hook: ${hook} is not a function. Received: ${hooks[hook]?.constructor?.name}` + } + } + } +} + +module.exports = { + parseAjvError, + parseAjvErrors, + validateHooks, + validateOptions: ajv.compile({ + type: 'object', + required: ['resources'], + properties: { + // Resource Handling + resources: { + type: 'array', + minItems: 1, + items: { + type: 'string' + } + }, + throwOnInvalidResource: { + type: 'boolean', + default: false + }, + // Timing settings + delay: { + type: 'integer', + minimum: 0, + default: 0 + }, + timeout: { + type: 'integer', + minimum: 1, + default: 60000 + }, + http: { + type: 'object', + properties: { + bodyTimeout: { + type: 'integer', + minimum: 0 + }, + headersTimeout: { + type: 'integer', + minimum: 0 + } + } + }, + interval: { + type: 'integer', + minimum: 0, + default: 250 + }, + reverse: { + type: 'boolean', + default: false + }, + simultaneous: { + type: 'integer', + minimum: 1, + defaults: 10 + }, + tcpTimeout: { + type: 'integer', + minimum: 0, + default: 300 // 300ms + }, + window: { + type: 'integer', + minimum: 0, + default: 0 + }, + passphrase: { + type: 'string' + }, + strictSSL: { + type: 'boolean', + default: false + }, + maxRedirects: { + type: 'integer', + minimum: 0, + default: 5 + }, + followRedirect: { + type: 'boolean', + default: true + }, + headers: { + type: 'object' + }, + auth: { + type: 'object', + required: ['username', 'password'], + properties: { + username: { + type: 'string' + }, + password: { + type: 'string' + } + } + }, + proxy: { + type: 'object', + required: ['uri'], + properties: { + uri: { + type: 'string' + }, + token: { + type: 'string' + } + } + }, + /** + * Supports: + * - onInvalidResource + * - onResourceTimeout + * - onResourceError + * - onResourceResponse + * - onResourceDone + */ + events: { + type: 'object' + } + } + }) +} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 236e163..0000000 --- a/package-lock.json +++ /dev/null @@ -1,2698 +0,0 @@ -{ - "name": "wait-on", - "version": "6.0.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", - "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", - "dev": true - }, - "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, - "@eslint/eslintrc": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", - "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - } - }, - "@hapi/hoek": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", - "integrity": "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw==" - }, - "@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@sideway/address": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.3.tgz", - "integrity": "sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" - }, - "@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "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-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.7" - }, - "dependencies": { - "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - } - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - } - } - }, - "array.prototype.flat": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", - "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" - }, - "dependencies": { - "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - } - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - } - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "axios": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", - "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", - "requires": { - "follow-redirects": "^1.14.7" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "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.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.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" - } - }, - "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" - } - } - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "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 - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "es-get-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", - "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", - "dev": true, - "requires": { - "es-abstract": "^1.17.4", - "has-symbols": "^1.0.1", - "is-arguments": "^1.0.4", - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-string": "^1.0.5", - "isarray": "^2.0.5" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - } - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.29.0.tgz", - "integrity": "sha512-82G/JToB9qIy/ArBzIWG9xvvwL3R86AlCjtGw+A29OMZDqhTybz/MByORSukGxeI+YPCR4coYyITKk8BFH9nDA==", - "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "dev": true - }, - "eslint-config-standard": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", - "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, - "requires": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - } - } - }, - "eslint-module-utils": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", - "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "find-up": "^2.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-plugin-chai-friendly": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.7.2.tgz", - "integrity": "sha512-LOIfGx5sZZ5FwM1shr2GlYAWV9Omdi+1/3byuVagvQNoGUuU0iHhp7AfjA1uR+4dJ4Isfb4+FwBJgQajIw9iAg==", - "dev": true - }, - "eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "dev": true, - "requires": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - }, - "dependencies": { - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - } - } - }, - "eslint-plugin-import": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", - "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", - "dev": true, - "requires": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", - "has": "^1.0.3", - "is-core-module": "^2.8.0", - "is-glob": "^4.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.5", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, - "requires": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - } - } - }, - "eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", - "dev": true, - "requires": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "dependencies": { - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - } - } - }, - "eslint-plugin-promise": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz", - "integrity": "sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==", - "dev": true - }, - "eslint-plugin-standard": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-5.0.0.tgz", - "integrity": "sha512-eSIXPc9wBM4BrniMzJRBm2uoVuXz2EPa+NXPk2+itrVt+r5SbKFERx/IgrK/HmfjddyKVz2f+j+7gBRvu19xLg==", - "dev": true - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "expect-legacy": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/expect-legacy/-/expect-legacy-1.20.2.tgz", - "integrity": "sha512-LGitOy9dl5jKR9JmVLhykO27TzK2f/zIe0RvohcBtF6DO80FbFq0S9+Z1y11qWEOaXsZCiH+9mARrbWGJtomiw==", - "dev": true, - "requires": { - "define-properties": "~1.1.2", - "has": "^1.0.1", - "is-equal": "^1.5.5", - "is-regex": "^1.0.3", - "object-inspect": "^1.1.0", - "object-keys": "^1.0.9", - "tmatch": "^2.0.1" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "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-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "follow-redirects": { - "version": "1.14.8", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", - "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "functions-have-names": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.1.tgz", - "integrity": "sha512-j48B/ZI7VKs3sgeI2cZp7WXWmZXu7Iq5pl5/vptV5N2mq+DGFuS/ulaDjtaoLpYzuD6u8UgrUKHfgo7fDTSiBA==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - }, - "dependencies": { - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - } - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true - }, - "is-arrow-function": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-arrow-function/-/is-arrow-function-2.0.3.tgz", - "integrity": "sha1-Kb4sLY2UUIUri7r7Y1unuNjofsI=", - "dev": true, - "requires": { - "is-callable": "^1.0.4" - } - }, - "is-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", - "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", - "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==", - "dev": true - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", - "dev": true - }, - "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-equal": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/is-equal/-/is-equal-1.6.1.tgz", - "integrity": "sha512-3/79QTolnfNFrxQAvqH8M+O01uGWsVq54BUPG2mXQH7zi4BE/0TY+fmA444t8xSBvIwyNMvsTmCZ5ViVDlqPJg==", - "dev": true, - "requires": { - "es-get-iterator": "^1.0.1", - "functions-have-names": "^1.2.0", - "has": "^1.0.3", - "is-arrow-function": "^2.0.3", - "is-bigint": "^1.0.0", - "is-boolean-object": "^1.0.0", - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-generator-function": "^1.0.7", - "is-number-object": "^1.0.3", - "is-regex": "^1.0.4", - "is-string": "^1.0.4", - "is-symbol": "^1.0.3", - "isarray": "^2.0.5", - "object-inspect": "^1.7.0", - "object.entries": "^1.1.0", - "which-boxed-primitive": "^1.0.1", - "which-collection": "^1.0.0" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - } - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "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 - }, - "is-generator-function": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz", - "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", - "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "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-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-set": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", - "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", - "dev": true - }, - "is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", - "dev": true - }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-weakset": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", - "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "joi": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz", - "integrity": "sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==", - "requires": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.3", - "@sideway/formula": "^3.0.0", - "@sideway/pinpoint": "^2.0.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "mocha": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.0.tgz", - "integrity": "sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.2.0", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "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 - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.entries": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz", - "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "has": "^1.0.3" - } - }, - "object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - } - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - } - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "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" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "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 - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "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 - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rxjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz", - "integrity": "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==", - "requires": { - "tslib": "^2.1.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "dependencies": { - "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", - "dev": true - } - } - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.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" - } - }, - "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 - } - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "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" - } - }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", - "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, - "temp": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", - "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1", - "rimraf": "~2.6.2" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "tmatch": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tmatch/-/tmatch-2.0.1.tgz", - "integrity": "sha1-DFYkbzPzDaG409colauvFmYPOM8=", - "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" - } - }, - "tsconfig-paths": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", - "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - } - }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - }, - "dependencies": { - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - } - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz", - "integrity": "sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==", - "dev": true, - "requires": { - "is-bigint": "^1.0.0", - "is-boolean-object": "^1.0.0", - "is-number-object": "^1.0.3", - "is-string": "^1.0.4", - "is-symbol": "^1.0.2" - } - }, - "which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "requires": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.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" - } - }, - "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 - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } - } -} diff --git a/package.json b/package.json index 11ce277..b49c0ea 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "@metcoder95/wait-on", "description": "wait-on is a cross platform command line utility and Node.js API which will wait for files, ports, sockets, and http(s) resources to become available", - "version": "6.0.1", + "version": "0.0.0", "main": "index.js", "types": "index.d.ts", "bin": { - "wait-on": "bin/wait-on" + "wait-on": "./wait-on" }, "author": "Jeff Barczewski ", "contributors": [ @@ -34,7 +34,7 @@ "release": "npx standard-version" }, "engines": { - "node": ">=14" + "node": ">=16.x" }, "tsd": { "directory": "test" @@ -43,7 +43,10 @@ "check-coverage": false }, "standard": { - "ignore": ["*.d.ts", "*.test-d.ts"] + "ignore": [ + "*.d.ts", + "*.test-d.ts" + ] }, "devDependencies": { "@types/node": "^18.11.5", @@ -59,11 +62,12 @@ "typescript": "^4.8.4" }, "dependencies": { - "axios": "^0.25.0", - "joi": "^17.6.0", - "lodash": "^4.17.21", - "minimist": "^1.2.5", - "rxjs": "^7.5.4" + "@metcoder95/tiny-pool": "^1.0.0", + "ajv": "^8.11.0", + "minimist": "^1.2.7", + "ora": "^6.1.2", + "signale": "^1.4.0", + "undici": "^5.12.0" }, "keywords": [ "wait", diff --git a/test/api.mocha.js b/test/api.mocha.js deleted file mode 100644 index 86cb5e3..0000000 --- a/test/api.mocha.js +++ /dev/null @@ -1,768 +0,0 @@ -'use strict'; - -const waitOn = require('../'); -const fs = require('fs'); -const http = require('http'); -const path = require('path'); -const temp = require('temp'); -const mkdirp = require('mkdirp'); - -const mocha = require('mocha'); -const describe = mocha.describe; -const it = mocha.it; -const afterEach = mocha.afterEach; -const expect = require('expect-legacy'); - -temp.track(); // cleanup files on exit - -describe('api', function () { - this.timeout(3000); - let httpServer = null; - - afterEach(function (done) { - if (httpServer) { - httpServer.close(); - httpServer = null; - } - done(); - }); - - it('should succeed when file resources are available', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar/deeper/deep/yet')] - }; - fs.writeFileSync(opts.resources[0], 'data1'); - mkdirp.sync(path.dirname(opts.resources[1])); - fs.writeFileSync(opts.resources[1], 'data2'); - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - }); - - it('should succeed when file resources are become available later', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar/deeper/deep/yet')] - }; - - setTimeout(function () { - fs.writeFile(opts.resources[0], 'data1', function () {}); - mkdirp.sync(path.dirname(opts.resources[1])); - fs.writeFile(opts.resources[1], 'data2', function () {}); - }, 300); - - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - }); - - it('should succeed when http resources are become available later', function (done) { - const opts = { - resources: ['http://localhost:3000', 'http://localhost:3000/foo'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.end('data'); - }); - httpServer.listen(3000, 'localhost'); - }, 300); - - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - - it('should succeed when custom validateStatus fn is provided http resource returns 401', function (done) { - const opts = { - resources: ['http://localhost:3000'], - validateStatus: function (status) { - return status === 401 || (status >= 200 && status < 300); - } - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.statusCode = 401; - res.end('Not authorized'); - }); - httpServer.listen(3000, 'localhost'); - }, 300); - - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - - it('should succeed when http resource become available later via redirect', function (done) { - const opts = { - // followRedirect: true // default is true - resources: ['http://localhost:3000'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - const pathname = req.url; - if (pathname === '/') { - res.writeHead(302, { Location: 'http://localhost:3000/foo' }); - } - res.end('data'); - }); - httpServer.listen(3000, 'localhost'); - }, 300); - - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - - it('should succeed when http GET resources become available later', function (done) { - const opts = { - resources: ['http-get://localhost:3011', 'http-get://localhost:3011/foo'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.end('data'); - }); - httpServer.listen(3011, 'localhost'); - }, 300); - - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - - it('should succeed when http GET resource become available later via redirect', function (done) { - const opts = { - // followRedirect: true, // default is true - resources: ['http-get://localhost:3000'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - const pathname = req.url; - if (pathname === '/') { - res.writeHead(302, { Location: 'http://localhost:3000/foo' }); - } - res.end('data'); - }); - httpServer.listen(3000, 'localhost'); - }, 300); - - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - - /* - it('should succeed when an https resource is available', function (done) { - const opts = { - resources: [ - 'https://www.google.com' - ] - }; - - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - - it('should succeed when an https GET resource is available', function (done) { - const opts = { - resources: [ - 'https-get://www.google.com' - ] - }; - - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - */ - - it('should succeed when a service is listening to tcp port', function (done) { - const opts = { - resources: ['tcp:localhost:3001', 'tcp:3001'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.end('data'); - }); - httpServer.listen(3001, 'localhost'); - }, 300); - - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - - it('should succeed when a service is listening to a socket', function (done) { - let socketPath; - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - socketPath = path.resolve(dirPath, 'sock'); - const opts = { - resources: ['socket:' + socketPath] - }; - - setTimeout(function () { - httpServer = http.createServer(); - httpServer.listen(socketPath); - }, 300); - - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - }); - - it('should succeed when a http service is listening to a socket', function (done) { - let socketPath; - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - socketPath = path.resolve(dirPath, 'sock'); - const opts = { - resources: ['http://unix:' + socketPath + ':/', 'http://unix:' + socketPath + ':/foo'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.end('data'); - }); - httpServer.listen(socketPath); - }, 300); - - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - }); - - it('should succeed when a http GET service is listening to a socket', function (done) { - let socketPath; - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - socketPath = path.resolve(dirPath, 'sock'); - const opts = { - resources: ['http-get://unix:' + socketPath + ':/', 'http-get://unix:' + socketPath + ':/foo'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.end('data'); - }); - httpServer.listen(socketPath); - }, 300); - - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - }); - - // Error situations - - it('should timeout when all resources are not available and timout option is specified', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo')], - timeout: 1000 - }; - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - }); - - it('should timeout when some resources are not available and timout option is specified', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')], - timeout: 1000 - }; - fs.writeFile(opts.resources[0], 'data', function () {}); - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - }); - - it('should timeout when an http resource returns 404', function (done) { - const opts = { - resources: ['http://localhost:3002'], - timeout: 1000, - interval: 100, - window: 100 - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.statusCode = 404; - res.end('data'); - }); - httpServer.listen(3002, 'localhost'); - }, 300); - - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - - it('should timeout when an http resource is not available', function (done) { - const opts = { - resources: ['http://localhost:3010'], - timeout: 1000, - interval: 100, - window: 100 - }; - - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - - it('should timeout when an http resource does not respond before httpTimeout', function (done) { - const opts = { - resources: ['http://localhost:8125'], - timeout: 1000, - interval: 100, - window: 100, - httpTimeout: 70 - }; - - httpServer = http.createServer().on('request', function (req, res) { - // make it a slow response, longer than the httpTimeout - setTimeout(function () { - res.end('data'); - }, 90); - }); - httpServer.listen(8125, 'localhost'); - - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - - it('should timeout when followRedirect is false and http resource redirects', function (done) { - const opts = { - timeout: 1000, - interval: 100, - window: 100, - followRedirect: false, - resources: ['http://localhost:3000'] - }; - - httpServer = http.createServer().on('request', function (req, res) { - const pathname = req.url; - if (pathname === '/') { - res.writeHead(302, { Location: 'http://localhost:3000/foo' }); - } - res.end('data'); - }); - httpServer.listen(3000, 'localhost'); - - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - - it('should timeout when an http GET resource is not available', function (done) { - const opts = { - resources: ['http-get://localhost:3010'], - timeout: 1000, - interval: 100, - window: 100 - }; - - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - - it('should timeout when an https resource is not available', function (done) { - const opts = { - resources: ['https://localhost:3010/foo/bar'], - timeout: 1000, - interval: 100, - window: 100 - }; - - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - - it('should timeout when an https GET resource is not available', function (done) { - const opts = { - resources: ['https-get://localhost:3010/foo/bar'], - timeout: 1000, - interval: 100, - window: 100 - }; - - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - - it('should timeout when followRedirect is false and http GET resource redirects', function (done) { - const opts = { - timeout: 1000, - interval: 100, - window: 100, - followRedirect: false, - resources: ['http-get://localhost:3000'] - }; - - httpServer = http.createServer().on('request', function (req, res) { - const pathname = req.url; - if (pathname === '/') { - res.writeHead(302, { Location: 'http://localhost:3000/foo' }); - } - res.end('data'); - }); - httpServer.listen(3000, 'localhost'); - - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - - it('should timeout when a service is not listening to tcp port', function (done) { - const opts = { - resources: ['tcp:localhost:3010'], - timeout: 1000 - }; - - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - - it('should timeout when a service is not listening to a socket', function (done) { - let socketPath; - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - socketPath = path.resolve(dirPath, 'sock'); - const opts = { - resources: ['socket:' + socketPath], - timeout: 1000, - interval: 100, - window: 100 - }; - - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - }); - - it('should timeout when a service host is unreachable', function (done) { - const opts = { - resources: ['tcp:256.0.0.1:1234'], - timeout: 1000, - tcpTimeout: 1000 - }; - - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - - it('should timeout when an http service listening to a socket returns 404', function (done) { - let socketPath; - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - socketPath = path.resolve(dirPath, 'sock'); - const opts = { - resources: ['http://unix:' + socketPath + ':/', 'http://unix:' + socketPath + ':/foo'], - timeout: 1000, - interval: 100, - window: 100 - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.statusCode = 404; - res.end('data'); - }); - httpServer.listen(socketPath); - }, 300); - - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - }); - - it('should timeout when an http service listening to a socket is too slow', function (done) { - let socketPath; - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - socketPath = path.resolve(dirPath, 'sock'); - const opts = { - resources: ['package.json', 'http://unix:' + socketPath + ':/', 'http://unix:' + socketPath + ':/foo'], - timeout: 1000, - interval: 100, - window: 100 - }; - - httpServer = http.createServer().on('request', function (req, res) { - setTimeout(function () { - // res.statusCode = 404; - res.end('data'); - }, 1100); - }); - httpServer.listen(socketPath); - - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - }); - - it('should succeed when a service host is unreachable in reverse mode', function (done) { - const opts = { - resources: ['tcp:256.0.0.1:1234'], - interval: 100, - timeout: 1000, - tcpTimeout: 1000, - reverse: true, - window: 100 - }; - - waitOn(opts, function (err) { - if (err) return done(err); - expect(err).toNotExist(); - done(); - }); - }); - - it('should succeed when file resources are not available in reverse mode', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')], - reverse: true - }; - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - }); - - it('should succeed when file resources are not available later in reverse mode', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')], - reverse: true - }; - fs.writeFileSync(opts.resources[0], 'data1'); - fs.writeFileSync(opts.resources[1], 'data2'); - setTimeout(function () { - fs.unlinkSync(opts.resources[0]); - fs.unlinkSync(opts.resources[1]); - }, 300); - waitOn(opts, function (err) { - expect(err).toNotExist(); - done(); - }); - }); - }); - - it('should timeout when file resources are available in reverse mode', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')], - reverse: true, - timeout: 1000 - }; - fs.writeFileSync(opts.resources[0], 'data1'); - fs.writeFileSync(opts.resources[1], 'data2'); - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - }); - - describe('promise support', function () { - it('should succeed when file resources are available', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')] - }; - fs.writeFileSync(opts.resources[0], 'data1'); - fs.writeFileSync(opts.resources[1], 'data2'); - waitOn(opts) - .then(function () { - done(); - }) - .catch(function (err) { - done(err); - }); - }); - }); - - it('should succeed when file resources are become available later', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')] - }; - - setTimeout(function () { - fs.writeFile(opts.resources[0], 'data1', function () {}); - fs.writeFile(opts.resources[1], 'data2', function () {}); - }, 300); - - waitOn(opts) - .then(function () { - done(); - }) - .catch(function (err) { - done(err); - }); - }); - }); - - it('should timeout when all resources are not available and timout option is specified', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo')], - timeout: 1000 - }; - waitOn(opts) - .then(function () { - done(new Error('Should not be resolved')); - }) - .catch(function (err) { - expect(err).toExist(); - done(); - }); - }); - }); - - it('should timeout when some resources are not available and timout option is specified', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')], - timeout: 1000 - }; - fs.writeFile(opts.resources[0], 'data', function () {}); - waitOn(opts) - .then(function () { - done(new Error('Should not be resolved')); - }) - .catch(function (err) { - expect(err).toExist(); - done(); - }); - }); - }); - - it('should succeed when file resources are not available in reverse mode', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')], - reverse: true - }; - waitOn(opts) - .then(function () { - done(); - }) - .catch(function (err) { - done(err); - }); - }); - }); - - it('should succeed when file resources are not available later in reverse mode', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')], - reverse: true - }; - fs.writeFileSync(opts.resources[0], 'data1'); - fs.writeFileSync(opts.resources[1], 'data2'); - setTimeout(function () { - fs.unlinkSync(opts.resources[0]); - fs.unlinkSync(opts.resources[1]); - }, 300); - waitOn(opts) - .then(function () { - done(); - }) - .catch(function (err) { - done(err); - }); - }); - }); - - it('should timeout when file resources are available in reverse mode', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')], - reverse: true, - timeout: 1000 - }; - fs.writeFileSync(opts.resources[0], 'data1'); - fs.writeFileSync(opts.resources[1], 'data2'); - waitOn(opts) - .then(function () { - done(new Error('Should not be resolved')); - }) - .catch(function (err) { - expect(err).toExist(); - done(); - }); - }); - }); - }); -}); diff --git a/test/cli.mocha.js b/test/cli.mocha.js deleted file mode 100644 index e4e9369..0000000 --- a/test/cli.mocha.js +++ /dev/null @@ -1,572 +0,0 @@ -'use strict'; - -const childProcess = require('child_process'); -const fs = require('fs'); -const http = require('http'); -const path = require('path'); -const temp = require('temp'); -const mkdirp = require('mkdirp'); - -const mocha = require('mocha'); -const describe = mocha.describe; -const it = mocha.it; -const afterEach = mocha.afterEach; -const expect = require('expect-legacy'); - -const CLI_PATH = path.resolve(__dirname, '../bin/wait-on'); - -temp.track(); // cleanup files on exit - -function execCLI(args, options) { - const fullArgs = [CLI_PATH].concat(args); - return childProcess.spawn(process.execPath, fullArgs, options); -} - -const FAST_OPTS = '-t 1000 -i 100 -w 100'.split(' '); - -describe('cli', function () { - this.timeout(3000); - let httpServer = null; - - afterEach(function (done) { - if (httpServer) { - httpServer.close(); - httpServer = null; - } - done(); - }); - - it('should succeed when file resources are available', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar/deeper/deep/yet')] - }; - fs.writeFileSync(opts.resources[0], 'data1'); - mkdirp.sync(path.dirname(opts.resources[1])); - fs.writeFileSync(opts.resources[1], 'data2'); - - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - }); - - it('should succeed when file resources are become available later', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar/deeper/deep/yet')] - }; - - setTimeout(function () { - fs.writeFile(opts.resources[0], 'data1', function () {}); - mkdirp.sync(path.dirname(opts.resources[1])); - fs.writeFile(opts.resources[1], 'data2', function () {}); - }, 300); - - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - }); - - it('should succeed when http resources become available later', function (done) { - const opts = { - resources: ['http://localhost:8123', 'http://localhost:8123/foo'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.end('data'); - }); - httpServer.listen(8123, 'localhost'); - }, 300); - - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - - it('should succeed when http resources become available later via redirect', function (done) { - const opts = { - resources: ['http://localhost:8123'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - const pathname = req.url; - if (pathname === '/') { - res.writeHead(302, { Location: 'http://localhost:8123/foo' }); - } - res.end('data'); - }); - httpServer.listen(8123, 'localhost'); - }, 300); - - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - - it('should succeed when http GET resources become available later', function (done) { - const opts = { - resources: ['http-get://localhost:8124', 'http-get://localhost:8124/foo'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.end('data'); - }); - httpServer.listen(8124, 'localhost'); - }, 300); - - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - - it('should succeed when http GET resources become available later via redirect', function (done) { - const opts = { - resources: ['http-get://localhost:8124'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - const pathname = req.url; - if (pathname === '/') { - res.writeHead(302, { Location: 'http://localhost:8124/foo' }); - } - res.end('data'); - }); - httpServer.listen(8124, 'localhost'); - }, 300); - - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - - /* - it('should succeed when an https resource is available', function (done) { - const opts = { - resources: [ - 'https://www.google.com' - ] - }; - - execCLI(opts.resources.concat(FAST_OPTS), {}) - .on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - */ - - it('should succeed when a service is listening to tcp port', function (done) { - const opts = { - resources: ['tcp:localhost:3030', 'tcp:3030'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.end('data'); - }); - httpServer.listen(3030, 'localhost'); - }, 300); - - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - - it('should succeed when a service is listening to a socket', function (done) { - let socketPath; - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - socketPath = path.resolve(dirPath, 'sock'); - const opts = { - resources: ['socket:' + socketPath] - }; - - setTimeout(function () { - httpServer = http.createServer(); - httpServer.listen(socketPath); - }, 300); - - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - }); - - it('should succeed when a http service is listening to a socket', function (done) { - let socketPath; - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - socketPath = path.resolve(dirPath, 'sock'); - const opts = { - resources: ['http://unix:' + socketPath + ':/', 'http://unix:' + socketPath + ':/foo'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.end('data'); - }); - httpServer.listen(socketPath); - }, 300); - - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - }); - - it('should succeed when a http GET service is listening to a socket', function (done) { - let socketPath; - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - socketPath = path.resolve(dirPath, 'sock'); - const opts = { - resources: ['http-get://unix:' + socketPath + ':/', 'http-get://unix:' + socketPath + ':/foo'] - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.end('data'); - }); - httpServer.listen(socketPath); - }, 300); - - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - }); - - // Error situations - - it('should timeout when all resources are not available and timout option is specified', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo')], - timeout: 1000 - }; - // timeout is in FAST_OPTS - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - }); - - it('should timeout when some resources are not available and timout option is specified', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')], - timeout: 1000 - }; - fs.writeFile(opts.resources[0], 'data', function () {}); - // timeout is in FAST_OPTS - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - }); - - it('should timeout when an http resource returns 404', function (done) { - const opts = { - resources: ['http://localhost:3998'], - timeout: 1000, - interval: 100, - window: 100 - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.statusCode = 404; - res.end('data'); - }); - httpServer.listen(3998, 'localhost'); - }, 300); - // timeout, interval, window are in FAST_OPTS - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - - it('should timeout when an http resource is not available', function (done) { - const opts = { - resources: ['http://localhost:3999'], - timeout: 1000, - interval: 100, - window: 100 - }; - - // timeout is in FAST_OPTS - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - - it('should timeout when an http resource does not respond before httpTimeout', function (done) { - const opts = { - resources: ['http://localhost:8125'], - timeout: 1000, - interval: 100, - window: 100, - httpTimeout: 70 - }; - - httpServer = http.createServer().on('request', function (req, res) { - // make it a slow response, longer than the httpTimeout - setTimeout(function () { - res.end('data'); - }, 90); - }); - httpServer.listen(8125, 'localhost'); - - const addOpts = '--httpTimeout 70'.split(' '); - // timeout, interval, and window are in FAST_OPTS - execCLI(opts.resources.concat(FAST_OPTS).concat(addOpts), {}).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - - it('should timeout when an http GET resource is not available', function (done) { - const opts = { - resources: ['http-get://localhost:3999'], - timeout: 1000, - interval: 100, - window: 100 - }; - - // timeout, interval, window are in FAST_OPTS - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - - it('should timeout when an https resource is not available', function (done) { - const opts = { - resources: ['https://localhost:3010/foo/bar'], - timeout: 1000, - interval: 100, - window: 100 - }; - - // timeout, interval, window are in FAST_OPTS - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - - it('should timeout when an https GET resource is not available', function (done) { - const opts = { - resources: ['https-get://localhost:3010/foo/bar'], - timeout: 1000, - interval: 100, - window: 100 - }; - - // timeout, interval, window are in FAST_OPTS - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - - it('should timeout when a service is not listening to tcp port', function (done) { - const opts = { - resources: ['tcp:localhost:3010'], - timeout: 1000 - }; - - // timeout is in FAST_OPTS - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - - it('should timeout when a service host is unreachable', function (done) { - const opts = { - resources: ['tcp:256.0.0.1:1234'], - timeout: 1000, - tcpTimeout: 1000 - }; - - const addOpts = '--tcpTimeout 1000'.split(' '); - // timeout is in FAST_OPTS - execCLI(opts.resources.concat(FAST_OPTS).concat(addOpts), {}).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - - it('should timeout when a service is not listening to a socket', function (done) { - let socketPath; - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - socketPath = path.resolve(dirPath, 'sock'); - const opts = { - resources: ['socket:' + socketPath], - timeout: 1000, - interval: 100, - window: 100 - }; - - // timeout, interval, window are in FAST_OPTS - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - }); - - it('should timeout when an http service listening to a socket returns 404', function (done) { - let socketPath; - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - socketPath = path.resolve(dirPath, 'sock'); - const opts = { - resources: ['http://unix:' + socketPath + ':/', 'http://unix:' + socketPath + ':/foo'], - timeout: 1000, - interval: 100, - window: 100 - }; - - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.statusCode = 404; - res.end('data'); - }); - httpServer.listen(socketPath); - }, 300); - - // timeout, interval, window are in FAST_OPTS - execCLI(opts.resources.concat(FAST_OPTS), {}).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - }); - - it('should succeed when file resources are not available in reverse mode', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')] - }; - const OPTS = FAST_OPTS.concat(['-r']); - execCLI(opts.resources.concat(OPTS), {}).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - }); - - it('should succeed when file resources are not available later in reverse mode', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')] - }; - fs.writeFileSync(opts.resources[0], 'data1'); - fs.writeFileSync(opts.resources[1], 'data2'); - setTimeout(function () { - fs.unlinkSync(opts.resources[0]); - fs.unlinkSync(opts.resources[1]); - }, 300); - const OPTS = FAST_OPTS.concat(['-r']); - execCLI(opts.resources.concat(OPTS), {}).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - }); - - it('should timeout when file resources are available in reverse mode', function (done) { - temp.mkdir({}, function (err, dirPath) { - if (err) return done(err); - const opts = { - resources: [path.resolve(dirPath, 'foo'), path.resolve(dirPath, 'bar')] - }; - fs.writeFileSync(opts.resources[0], 'data1'); - fs.writeFileSync(opts.resources[1], 'data2'); - const OPTS = FAST_OPTS.concat(['-r']); - execCLI(opts.resources.concat(OPTS), {}).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - }); - - it('should succeed when a service host is unreachable in reverse mode', function (done) { - const opts = { - resources: ['tcp:256.0.0.1:1234'], - timeout: 1000, - tcpTimeout: 1000 - }; - // timeout is in FAST_OPTS - const OPTS = FAST_OPTS.concat(['-r', '--tcpTimeout', '1000']); - execCLI(opts.resources.concat(OPTS), {}).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - - context('resources are specified in config', () => { - it('should succeed when http resources become available later', function (done) { - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.end('data'); - }); - httpServer.listen(8123, 'localhost'); - }, 300); - - execCLI(['--config', path.join(__dirname, 'config-http-resources.js')].concat(FAST_OPTS), {}).on( - 'exit', - function (code) { - expect(code).toBe(0); - done(); - } - ); - }); - - it('should succeed when http resources from command line become available later (ignores config resources)', function (done) { - setTimeout(function () { - httpServer = http.createServer().on('request', function (req, res) { - res.end('data'); - }); - httpServer.listen(3030, 'localhost'); - }, 300); - - execCLI( - ['--config', path.join(__dirname, 'config-http-resources.js'), 'http://localhost:3030/'].concat(FAST_OPTS), - {} - ).on('exit', function (code) { - expect(code).toBe(0); - done(); - }); - }); - }); -}); diff --git a/test/config-http-resources.js b/test/config-http-resources.js deleted file mode 100644 index 9266535..0000000 --- a/test/config-http-resources.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - resources: ['http://localhost:8123', 'http://localhost:8123/foo'], -}; diff --git a/test/file.test.js b/test/file.test.js new file mode 100644 index 0000000..e494b3b --- /dev/null +++ b/test/file.test.js @@ -0,0 +1,112 @@ +'use strict' +const os = require('node:os') +const path = require('node:path') +const { writeFile, unlink, appendFile } = require('node:fs/promises') +const { setTimeout } = require('node:timers/promises') + +const { test } = require('tap') + +const waitOn = require('..') + +test('Wait-On#File', context => { + context.plan(5) + + context.test('Basic File - write', async t => { + const tmpdir = os.tmpdir() + const filePath = path.join(tmpdir, 'test-file1.txt') + + t.plan(1) + t.teardown(unlink.bind(null, filePath)) + + const promise = waitOn({ + resources: [`file:/${filePath}`], + window: 1500 + }) + + await setTimeout(500) + + await writeFile(filePath, 'Hello World!') + + const result = await promise + + t.equal(result, true) + }) + + context.test('Basic File - append', async t => { + const tmpdir = os.tmpdir() + const filePath = path.join(tmpdir, 'test-file2.txt') + + t.plan(1) + t.teardown(unlink.bind(null, filePath)) + + const promise = waitOn({ + resources: [`file:/${filePath}`], + window: 1500 + }) + + await setTimeout(500) + + await writeFile(filePath, 'Hello World!') + + await setTimeout(500) + + await appendFile(filePath, 'Hello World!(x2)') + + const result = await promise + + t.equal(result, true) + }) + + context.test('Basic File - with initial delay', async t => { + const tmpdir = os.tmpdir() + const filePath = path.join(tmpdir, 'test-file1.txt') + + t.plan(1) + t.teardown(unlink.bind(null, filePath)) + + const promise = waitOn({ + resources: [`file:/${filePath}`], + window: 500, + delay: 1000 + }) + + await setTimeout(1000) + + await writeFile(filePath, 'Hello World!') + + const result = await promise + + t.equal(result, true) + }) + + context.test('Basic File - file already exists', async t => { + const tmpdir = os.tmpdir() + const filePath = path.join(tmpdir, 'test-file3.txt') + + t.plan(1) + t.teardown(unlink.bind(null, filePath)) + + await writeFile(filePath, 'Hello World!') + + const result = await waitOn({ + resources: [`file:/${filePath}`], + window: 1500 + }) + + t.equal(result, true) + }) + + context.test('Basic File with timeout', async t => { + const tmpdir = os.tmpdir() + const filePath = path.join(tmpdir, 'test-file4.txt') + + t.plan(1) + + const result = await waitOn({ + resources: [`file:/${filePath}`], + timeout: 1000 + }) + + t.equal(result, false) + }) +}) diff --git a/test/fixtures/file.js b/test/fixtures/file.js new file mode 100644 index 0000000..62cd12f --- /dev/null +++ b/test/fixtures/file.js @@ -0,0 +1,52 @@ +const os = require('node:os') +const path = require('node:path') +const ReadLine = require('node:readline') +const { writeFileSync, appendFileSync } = require('node:fs') + +const options = { + mode: '', + filanme: '' +} +const tmpdir = os.tmpdir() +const reader = ReadLine.createInterface({ + input: process.stdin, + output: process.stdout +}) + +process.stdout.write(`Writting to temp dir: ${tmpdir}\n`) + +reader.question( + 'Should create(c) a file or append(a) to an already existent one? (c/a) ', + answer => { + if (answer !== 'c' && answer !== 'a') { + reader.write(`Invalid option, exiting now ${os.EOL}`) + reader.close() + process.exit(1) + } else if (answer === 'e') { + reader.write('Press enter to exit') + reader.once('line', process.exit.bind(process, 0)) + } else { + reader.question('Enter the filename: ', answer => { + options.filename = answer + + if (options.mode === 'c') { + const filepath = path.join(tmpdir, options.filename) + writeFileSync(filepath, 'Hello World!', { encoding: 'utf8' }) + + reader.write( + `File created at ${filepath}. Click enter to exit ${os.EOL}` + ) + } else { + const filepath = path.join(tmpdir, options.filename) + + appendFileSync(filepath, 'Hello World!', { encoding: 'utf8' }) + reader.write('Press enter to exit') + } + + reader.once('line', process.exit.bind(process, 0)) + }) + } + + options.mode = answer + } +) diff --git a/test/fixtures/socket.js b/test/fixtures/socket.js new file mode 100644 index 0000000..61d9d65 --- /dev/null +++ b/test/fixtures/socket.js @@ -0,0 +1,19 @@ +const os = require('node:os') +const path = require('node:path') +const { createServer } = require('node:http') + +const tmpdir = os.tmpdir() +const socketPath = path.join(tmpdir, 'sock') +const server = createServer((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }) + res.end('OK') +}) + +process.on('SIGINT', () => { + server.close(() => console.log('server closed at', socketPath)) +}) + +server.listen(socketPath, err => { + if (err != null) console.error(err) + else console.log('server listening at', socketPath) +}) diff --git a/test/fixtures/tcp.js b/test/fixtures/tcp.js new file mode 100644 index 0000000..406678f --- /dev/null +++ b/test/fixtures/tcp.js @@ -0,0 +1,13 @@ +const net = require('node:net') + +const server = net.createServer({ + keepAlive: false +}) + +server.on('connection', socket => { + socket.end('Hello World!') +}) + +server.listen(3000, () => { + console.log('server bound') +}) diff --git a/test/http.test.js b/test/http.test.js new file mode 100644 index 0000000..cbda1a5 --- /dev/null +++ b/test/http.test.js @@ -0,0 +1,94 @@ +'use strict' +const { createServer } = require('node:http') +const { setTimeout } = require('node:timers/promises') + +const { test } = require('tap') + +const waitOn = require('..') + +test('Wait-On#HTTP', context => { + context.plan(4) + + context.test('Basic HTTP', t => { + const server = createServer((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }) + res.end('Hello World') + }) + + t.plan(1) + + t.teardown(server.close.bind(server)) + + const waiting = waitOn({ + resources: ['http://localhost:3001'] + }) + + setTimeout(1500).then(() => { + server.listen(3001, async e => { + if (e != null) t.fail(e.message) + + const result = await waiting + + t.equal(result, true) + }) + }) + }) + + context.test('Basic HTTP - with initial delay', t => { + const server = createServer((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }) + res.end('Hello World') + }) + + t.plan(1) + + t.teardown(server.close.bind(server)) + + const waiting = waitOn({ + resources: ['http://localhost:3002'], + delay: 1000 + }) + + setTimeout(0).then(() => { + server.listen(3002, async e => { + if (e != null) t.fail(e.message) + + const result = await waiting + + t.equal(result, true) + }) + }) + }) + + context.test('Basic HTTP - immediate connect', t => { + const server = createServer((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }) + res.end('Hello World') + }) + + t.plan(1) + + t.teardown(server.close.bind(server)) + + server.listen(3003, async e => { + if (e != null) t.fail(e.message) + + const result = await waitOn({ + resources: ['http://localhost:3003'] + }) + + t.equal(result, true) + }) + }) + + context.test('Basic HTTP with timeout', async t => { + t.plan(1) + + const result = await waitOn({ + resources: ['http://localhost:3004'], + timeout: 1500 + }) + + t.equal(result, false) + }) +}) diff --git a/test/socket.test.js b/test/socket.test.js new file mode 100644 index 0000000..a85e511 --- /dev/null +++ b/test/socket.test.js @@ -0,0 +1,111 @@ +'use strict' +const os = require('node:os') +const path = require('node:path') +const { createServer } = require('node:http') +const { setTimeout } = require('node:timers/promises') + +const { test } = require('tap') + +const waitOn = require('..') + +test('Wait-On#Socket', context => { + context.plan(4) + + context.test('Basic Socket', async t => { + const tmpdir = os.tmpdir() + const socketPath = path.join(tmpdir, 'sock') + const server = createServer((_, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }) + res.end('OK') + }) + + t.plan(1) + t.teardown(server.close.bind(server)) + + const promise = waitOn({ + resources: [`socket:${socketPath}`] + }) + + await setTimeout(1500).then( + () => + new Promise((resolve, reject) => { + server.listen(socketPath, err => { + if (err != null) reject(err) + + resolve() + }) + }) + ) + + const result = await promise + + t.equal(result, true) + }) + + context.test('Basic Socket - with initial delay', async t => { + const tmpdir = os.tmpdir() + const socketPath = path.join(tmpdir, 'sock') + const server = createServer((_, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }) + res.end('OK') + }) + + t.plan(1) + t.teardown(server.close.bind(server)) + + await new Promise((resolve, reject) => { + server.listen(socketPath, err => { + if (err != null) reject(err) + + resolve() + }) + }) + + const result = await waitOn({ + resources: [`socket:${socketPath}`], + delay: 1500 + }) + + t.equal(result, true) + }) + + context.test('Basic Socket - immediate connect', async t => { + const tmpdir = os.tmpdir() + const socketPath = path.join(tmpdir, 'sock') + const server = createServer((_, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }) + res.end('OK') + }) + + t.plan(1) + t.teardown(server.close.bind(server)) + + await new Promise((resolve, reject) => { + server.listen(socketPath, err => { + if (err != null) reject(err) + + resolve() + }) + }) + + const result = await waitOn({ + resources: [`socket:${socketPath}`] + }) + + t.equal(result, true) + }) + + context.test('Basic Socket with timeout', async t => { + const tmpdir = os.tmpdir() + const socketPath = path.join(tmpdir, 'sock') + + t.plan(1) + + const result = await waitOn({ + resources: [`socket:${socketPath}`], + timeout: 1000 + }) + + t.equal(result, false) + }) +}) diff --git a/test/tcp.test.js b/test/tcp.test.js new file mode 100644 index 0000000..2cd78ba --- /dev/null +++ b/test/tcp.test.js @@ -0,0 +1,110 @@ +'use strict' +const { createServer } = require('node:net') +const { setTimeout } = require('node:timers/promises') + +const { test } = require('tap') + +const waitOn = require('..') + +function noop () {} + +test('Wait-On#TCP', context => { + context.plan(4) + + context.test('Basic TCP', async t => { + const server = createServer({ + keepAlive: false + }) + + server.on('connection', socket => { + t.ok(socket) + }) + + server.on('error', noop) + + t.plan(2) + t.teardown(server.close.bind(server)) + + const promise = waitOn({ + resources: ['tcp://localhost:4001'] + }) + + await setTimeout(1500).then( + () => + new Promise(resolve => { + server.listen(4001, resolve) + }) + ) + + const result = await promise + + t.equal(result, true) + }) + + context.test('Basic TCP - with initial delay', async t => { + const server = createServer({ + keepAlive: false + }) + + server.on('connection', socket => { + t.ok(socket) + }) + + server.on('error', noop) + + t.plan(2) + t.teardown(server.close.bind(server)) + + const promise = waitOn({ + resources: ['tcp://localhost:4002'], + delay: 1000 + }) + + await new Promise(resolve => { + server.listen(4002, resolve) + }) + + const result = await promise + + t.equal(result, true) + }) + + context.test('Basic TCP - immediate connect', async t => { + const server = createServer({ + keepAlive: false + }) + + server.on('connection', socket => { + t.ok(socket) + }) + + server.on('error', noop) + + t.plan(2) + t.teardown(server.close.bind(server)) + + await new Promise(resolve => { + server.listen(4003, resolve) + }) + + const result = await waitOn({ + resources: ['tcp://localhost:4003'] + }) + + t.equal(result, true) + }) + + context.test('Basic TCP with timeout', async t => { + t.plan(1) + + const promise = waitOn({ + resources: ['tcp://127.0.0.1:5030'], + timeout: 500, + interval: 1000 + }) + + const result = await promise + + t.equal(result, false) + }) +}) diff --git a/test/validation.mocha.js b/test/validation.mocha.js deleted file mode 100644 index ba796e7..0000000 --- a/test/validation.mocha.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; - -const waitOn = require('../'); -const childProcess = require('child_process'); - -const mocha = require('mocha'); -const describe = mocha.describe; -const it = mocha.it; -const expect = require('expect-legacy'); - -function execCLI(args, options) { - return childProcess.exec('../bin/wait-on', args, options); -} - -describe('validation', function () { - describe('API', function () { - it('should callback with error when resources property is omitted', function (done) { - const opts = {}; - waitOn(opts, function (err) { - expect(err).toExist(); - done(); - }); - }); - - it('should callback with error when no resources are provided', function (done) { - const opts = { resources: [] }; - waitOn(opts, function (err) { - expect(err.toString()).toInclude('"resources" does not contain 1 required value(s)'); - done(); - }); - }); - - it('should return error when opts is null', function (done) { - waitOn(null, function (err) { - expect(err).toExist(); - done(); - }); - }); - }); - - describe('CLI', function () { - it('should exit with non-zero error code when no resources provided', function (done) { - execCLI([]).on('exit', function (code) { - expect(code).toNotBe(0); - done(); - }); - }); - }); -}); diff --git a/test/validation.test.js b/test/validation.test.js new file mode 100644 index 0000000..20c1832 --- /dev/null +++ b/test/validation.test.js @@ -0,0 +1,48 @@ +'use strict' + +const childProcess = require('child_process') +const { promisify } = require('util') + +const { test } = require('tap') + +const waitOn = require('..') + +test('Wait-On#Programatically', context => { + context.plan(2) + + context.test('Should throw if empty Options', async t => { + t.plan(2) + + await t.rejects(waitOn(), 'Should throw if empty Options') + await t.rejects(waitOn(null), 'Should throw if empty Options') + }) + + context.test('Should throw if empty Options#resources', async t => { + t.plan(2) + + await t.rejects( + waitOn({ resources: null }), + 'Should throw if empty Options#resources' + ) + + await t.rejects( + waitOn({}), + 'Should throw if empty Options#resources (null)' + ) + }) +}) + +test('Wait-On#CLI', context => { + const exec = promisify(childProcess.exec) + context.plan(1) + + context.test('Should exit with code 1', async t => { + t.plan(1) + + try { + await exec('./wait-on') + } catch (err) { + t.equal(err.code, 1, 'Should exit with code 1') + } + }) +}) diff --git a/wait-on b/wait-on new file mode 100755 index 0000000..8fd9d67 --- /dev/null +++ b/wait-on @@ -0,0 +1,114 @@ +#!/usr/bin/env node + +'use strict' + +const fs = require('node:fs') +const path = require('node:path') + +const minimist = require('minimist') +const { Signale } = require('signale') + +const waitOn = require('.') + +const minimistOpts = { + string: ['c', 'httpTimeout', 'tcpTimeout'], + boolean: ['h', 'l', 'r', 'v'], + alias: { + c: 'config', + d: 'delay', + i: 'interval', + l: 'log', + r: 'reverse', + s: 'simultaneous', + t: 'timeout', + v: 'verbose', + w: 'window', + h: 'help' + } +} +const waitOnOpts = [ + 'delay', + 'httpTimeout', + 'interval', + 'log', + 'reverse', + 'simultaneous', + 'timeout', + 'tcpTimeout', + 'verbose', + 'window' +] + +const logger = new Signale({ scope: 'wait-on' }) + +;(async () => { + const argv = minimist(process.argv.slice(2), minimistOpts) + // if a js/json configuration file is provided require it + const configOpts = + argv.config != null ? require(path.resolve(argv.config)) : {} + + if (argv.help) { + // help + const help = fs.readFileSync(path.join(__dirname, 'help.txt'), 'utf8') + console.log(help) + process.exit(0) + } else { + // if resources are present in the command line then they take + // precedence over those in the config file. + if (argv._.length > 0) { + configOpts.resources = argv._ + } + + for (const optName of waitOnOpts) { + if (argv[optName] != null) { + configOpts[optName] = argv[optName] + } + } + + let exitCode = 0 + const eventLogger = logger.scope('wait-on:events') + + if (configOpts.verbose === false) { + logger.disable() + eventLogger.disable() + } + + logger.start('Waiting for resources') + + try { + const result = await waitOn({ + ...configOpts, + events: { + onInvalidResource: resource => { + eventLogger.error(`Invalid resource: ${resource}`) + }, + onResourceTimeout: resource => { + eventLogger.error(`Resource timeout: ${resource}`) + }, + onResourceResponse: (resource, reason) => { + eventLogger.warn(`Resource response: ${resource} (${reason})`) + }, + onResourceError: (resource, err) => { + eventLogger.error(`Resource error: ${resource} (${err.reason})`) + }, + onResourceDone: resource => { + eventLogger.complete(`Resource done: ${resource}`) + } + } + }) + + if (result) { + logger.success('Done!') + exitCode = 0 + } else { + logger.error('Resources timed out') + exitCode = 1 + } + } catch (error) { + logger.error('Initialization failed: ' + error.message) + exitCode = 1 + } + + process.exit(exitCode) + } +})() diff --git a/wait-on.js b/wait-on.js deleted file mode 100644 index 6ba905c..0000000 --- a/wait-on.js +++ /dev/null @@ -1,494 +0,0 @@ -'use strict' - -const fs = require('fs') -const { promisify } = require('util') -const Joi = require('joi') -const https = require('https') -const net = require('net') -const util = require('util') -const axiosPkg = require('axios').default -const axiosHttpAdapter = require('axios/lib/adapters/http') -const { - isBoolean, - isEmpty, - negate, - noop, - once, - partial, - pick, - zip -} = require('lodash/fp') -const { NEVER, combineLatest, from, merge, throwError, timer } = require('rxjs') -const { - distinctUntilChanged, - map, - mergeMap, - scan, - startWith, - take, - takeWhile -} = require('rxjs/operators') - -// force http adapter for axios, otherwise if using jest/jsdom xhr might -// be used and it logs all errors polluting the logs -const axios = axiosPkg.create({ adapter: axiosHttpAdapter }) -const isNotABoolean = negate(isBoolean) -const isNotEmpty = negate(isEmpty) -const fstat = promisify(fs.stat) -const PREFIX_RE = /^((https?-get|https?|tcp|socket|file):)(.+)$/ -const HOST_PORT_RE = /^(([^:]*):)?(\d+)$/ -const HTTP_GET_RE = /^https?-get:/ -const HTTP_UNIX_RE = /^http:\/\/unix:([^:]+):([^:]+)$/ -const TIMEOUT_ERR_MSG = 'Timed out waiting for' - -const WAIT_ON_SCHEMA = Joi.object({ - resources: Joi.array() - .items(Joi.string().required()) - .required(), - delay: Joi.number() - .integer() - .min(0) - .default(0), - httpTimeout: Joi.number() - .integer() - .min(0), - interval: Joi.number() - .integer() - .min(0) - .default(250), - log: Joi.boolean().default(false), - reverse: Joi.boolean().default(false), - simultaneous: Joi.number() - .integer() - .min(1) - .default(Infinity), - timeout: Joi.number() - .integer() - .min(0) - .default(Infinity), - validateStatus: Joi.function(), - verbose: Joi.boolean().default(false), - window: Joi.number() - .integer() - .min(0) - .default(750), - tcpTimeout: Joi.number() - .integer() - .min(0) - .default(300), - - // http/https options - ca: [Joi.string(), Joi.binary()], - cert: [Joi.string(), Joi.binary()], - key: [Joi.string(), Joi.binary(), Joi.object()], - passphrase: Joi.string(), - proxy: [Joi.boolean(), Joi.object()], - auth: Joi.object({ - username: Joi.string(), - password: Joi.string() - }), - strictSSL: Joi.boolean().default(false), - followRedirect: Joi.boolean().default(true), // HTTP 3XX responses - headers: Joi.object() -}) - -/** - Waits for resources to become available before calling callback - - Polls file, http(s), tcp ports, sockets for availability. - - Resource types are distinquished by their prefix with default being `file:` - - file:/path/to/file - waits for file to be available and size to stabilize - - http://foo.com:8000/bar verifies HTTP HEAD request returns 2XX - - https://my.bar.com/cat verifies HTTPS HEAD request returns 2XX - - http-get: - HTTP GET returns 2XX response. ex: http://m.com:90/foo - - https-get: - HTTPS GET returns 2XX response. ex: https://my/bar - - tcp:my.server.com:3000 verifies a service is listening on port - - socket:/path/sock verifies a service is listening on (UDS) socket - For http over socket, use http://unix:SOCK_PATH:URL_PATH - like http://unix:/path/to/sock:/foo/bar or - http-get://unix:/path/to/sock:/foo/bar - - @param opts object configuring waitOn - @param opts.resources array of string resources to wait for. prefix determines the type of resource with the default type of `file:` - @param opts.delay integer - optional initial delay in ms, default 0 - @param opts.httpTimeout integer - optional http HEAD/GET timeout to wait for request, default 0 - @param opts.interval integer - optional poll resource interval in ms, default 250ms - @param opts.log boolean - optional flag to turn on logging to stdout - @param opts.reverse boolean - optional flag which reverses the mode, succeeds when resources are not available - @param opts.simultaneous integer - optional limit of concurrent connections to a resource, default Infinity - @param opts.tcpTimeout - Maximum time in ms for tcp connect, default 300ms - @param opts.timeout integer - optional timeout in ms, default Infinity. Aborts with error. - @param opts.verbose boolean - optional flag to turn on debug log - @param opts.window integer - optional stabilization time in ms, default 750ms. Waits this amount of time for file sizes to stabilize or other resource availability to remain unchanged. If less than interval then will be reset to interval - @param cb optional callback function with signature cb(err) - if err is provided then, resource checks did not succeed - if not specified, wait-on will return a promise that will be rejected if resource checks did not succeed or resolved otherwise - */ -function waitOn (opts, cb) { - if (cb !== undefined) { - return waitOnImpl(opts, cb) - } else { - // promise API - return new Promise(function (resolve, reject) { - waitOnImpl(opts, function (err) { - if (err) { - reject(err) - } else { - resolve() - } - }) - }) - } -} - -function waitOnImpl (opts, cbFunc) { - const cbOnce = once(cbFunc) - const validResult = WAIT_ON_SCHEMA.validate(opts) - if (validResult.error) { - return cbOnce(validResult.error) - } - const validatedOpts = { - ...validResult.value, // use defaults - // window needs to be at least interval - ...(validResult.value.window < validResult.value.interval - ? { window: validResult.value.interval } - : {}), - ...(validResult.value.verbose ? { log: true } : {}) // if debug logging then normal log is also enabled - } - - const { resources, log: shouldLog, timeout, verbose, reverse } = validatedOpts - - const output = verbose ? console.log.bind() : noop - const log = shouldLog ? console.log.bind() : noop - const logWaitingForWDeps = partial(logWaitingFor, [{ log, resources }]) - const createResourceWithDeps$ = partial(createResource$, [ - { validatedOpts, output, log } - ]) - - let lastResourcesState = resources // the last state we had recorded - - const timeoutError$ = - timeout !== Infinity - ? timer(timeout).pipe( - mergeMap(() => { - const resourcesWaitingFor = determineRemainingResources( - resources, - lastResourcesState - ).join(', ') - return throwError( - Error(`${TIMEOUT_ERR_MSG}: ${resourcesWaitingFor}`) - ) - }) - ) - : NEVER - - function cleanup (err) { - if (err) { - if (err.message.startsWith(TIMEOUT_ERR_MSG)) { - log('wait-on(%s) %s; exiting with error', process.pid, err.message) - } else { - log('wait-on(%s) exiting with error', process.pid, err) - } - } else { - // no error, we are complete - log('wait-on(%s) complete', process.pid) - } - cbOnce(err) - } - - if (reverse) { - log('wait-on reverse mode - waiting for resources to be unavailable') - } - logWaitingForWDeps(resources) - - const resourcesCompleted$ = combineLatest( - resources.map(createResourceWithDeps$) - ) - - merge(timeoutError$, resourcesCompleted$) - .pipe(takeWhile(resourceStates => resourceStates.some(x => !x))) - .subscribe({ - next: resourceStates => { - lastResourcesState = resourceStates - logWaitingForWDeps(resourceStates) - }, - error: cleanup, - complete: cleanup - }) -} - -function logWaitingFor ({ log, resources }, resourceStates) { - const remainingResources = determineRemainingResources( - resources, - resourceStates - ) - if (isNotEmpty(remainingResources)) { - log( - `waiting for ${ - remainingResources.length - } resources: ${remainingResources.join(', ')}` - ) - } -} - -function determineRemainingResources (resources, resourceStates) { - // resourcesState is array of completed booleans - const resourceAndStateTuples = zip(resources, resourceStates) - return resourceAndStateTuples - .filter(([, /* r */ s]) => !s) - .map(([r /*, s */]) => r) -} - -function createResource$ (deps, resource) { - const prefix = extractPrefix(resource) - switch (prefix) { - case 'https-get:': - case 'http-get:': - case 'https:': - case 'http:': - return createHTTP$(deps, resource) - case 'tcp:': - return createTCP$(deps, resource) - case 'socket:': - return createSocket$(deps, resource) - default: - return createFileResource$(deps, resource) - } -} - -function createFileResource$ ( - { - validatedOpts: { - delay, - interval, - reverse, - simultaneous, - window: stabilityWindow - }, - output - }, - resource -) { - const filePath = extractPath(resource) - const checkOperator = reverse - ? map(size => size === -1) // check that file does not exist - : scan( - // check that file exists and the size is stable - (acc, x) => { - if (x > -1) { - const { size, t } = acc - const now = Date.now() - if (size !== -1 && x === size) { - if (now >= t + stabilityWindow) { - // file size has stabilized - output(` file stabilized at size:${size} file:${filePath}`) - return true - } - output( - ` file exists, checking for size change during stability window, size:${size} file:${filePath}` - ) - return acc // return acc unchanged, just waiting to pass stability window - } - output( - ` file exists, checking for size changes, size:${x} file:${filePath}` - ) - return { size: x, t: now } // update acc with new value and timestamp - } - return acc - }, - { size: -1, t: Date.now() } - ) - - return timer(delay, interval).pipe( - mergeMap(() => { - output(`checking file stat for file:${filePath} ...`) - return from(getFileSize(filePath)) - }, simultaneous), - checkOperator, - map(x => (isNotABoolean(x) ? false : x)), - startWith(false), - distinctUntilChanged(), - take(2) - ) -} - -function extractPath (resource) { - const m = PREFIX_RE.exec(resource) - if (m) { - return m[3] - } - return resource -} - -function extractPrefix (resource) { - const m = PREFIX_RE.exec(resource) - if (m) { - return m[1] - } - return '' -} - -async function getFileSize (filePath) { - try { - const { size } = await fstat(filePath) - return size - } catch (err) { - return -1 - } -} - -function createHTTP$ ({ validatedOpts, output }, resource) { - const { - delay, - followRedirect, - httpTimeout: timeout, - interval, - proxy, - reverse, - simultaneous, - strictSSL: rejectUnauthorized - } = validatedOpts - const method = HTTP_GET_RE.test(resource) ? 'get' : 'head' - const url = resource.replace('-get:', ':') - const matchHttpUnixSocket = HTTP_UNIX_RE.exec(url) // http://unix:/sock:/url - const urlSocketOptions = matchHttpUnixSocket - ? { socketPath: matchHttpUnixSocket[1], url: matchHttpUnixSocket[2] } - : { url } - const socketPathDesc = urlSocketOptions.socketPath - ? `socketPath:${urlSocketOptions.socketPath}` - : '' - const httpOptions = { - ...pick(['auth', 'headers', 'validateStatus'], validatedOpts), - httpsAgent: new https.Agent({ - rejectUnauthorized, - ...pick(['ca', 'cert', 'key', 'passphrase'], validatedOpts) - }), - ...(followRedirect ? {} : { maxRedirects: 0 }), // defaults to 5 (enabled) - proxy, // can be undefined, false, or object - ...(timeout && { timeout }), - ...urlSocketOptions, - method - // by default it provides full response object - // validStatus is 2xx unless followRedirect is true (default) - } - const checkFn = reverse ? negateAsync(httpCallSucceeds) : httpCallSucceeds - return timer(delay, interval).pipe( - mergeMap(() => { - output( - `making HTTP(S) ${method} request to ${socketPathDesc} url:${urlSocketOptions.url} ...` - ) - return from(checkFn(output, httpOptions)) - }, simultaneous), - startWith(false), - distinctUntilChanged(), - take(2) - ) -} - -async function httpCallSucceeds (output, httpOptions) { - try { - const result = await axios(httpOptions) - output( - ` HTTP(S) result for ${httpOptions.url}: ${util.inspect( - pick(['status', 'statusText', 'headers', 'data'], result) - )}` - ) - return true - } catch (err) { - output(` HTTP(S) error for ${httpOptions.url} ${err.toString()}`) - return false - } -} - -function createTCP$ ( - { - validatedOpts: { delay, interval, tcpTimeout, reverse, simultaneous }, - output - }, - resource -) { - const tcpPath = extractPath(resource) - const checkFn = reverse ? negateAsync(tcpExists) : tcpExists - return timer(delay, interval).pipe( - mergeMap(() => { - output(`making TCP connection to ${tcpPath} ...`) - return from(checkFn(output, tcpPath, tcpTimeout)) - }, simultaneous), - startWith(false), - distinctUntilChanged(), - take(2) - ) -} - -async function tcpExists (output, tcpPath, tcpTimeout) { - const [, , /* full, hostWithColon */ hostMatched, port] = HOST_PORT_RE.exec( - tcpPath - ) - const host = hostMatched || 'localhost' - return new Promise(resolve => { - const conn = net - .connect(port, host) - .on('error', err => { - output( - ` error connecting to TCP host:${host} port:${port} ${err.toString()}` - ) - resolve(false) - }) - .on('timeout', () => { - output( - ` timed out connecting to TCP host:${host} port:${port} tcpTimeout:${tcpTimeout}ms` - ) - conn.end() - resolve(false) - }) - .on('connect', () => { - output(` TCP connection successful to host:${host} port:${port}`) - conn.end() - resolve(true) - }) - conn.setTimeout(tcpTimeout) - }) -} - -function createSocket$ ( - { validatedOpts: { delay, interval, reverse, simultaneous }, output }, - resource -) { - const socketPath = extractPath(resource) - const checkFn = reverse ? negateAsync(socketExists) : socketExists - return timer(delay, interval).pipe( - mergeMap(() => { - output(`making socket connection to ${socketPath} ...`) - return from(checkFn(output, socketPath)) - }, simultaneous), - startWith(false), - distinctUntilChanged(), - take(2) - ) -} - -async function socketExists (output, socketPath) { - return new Promise(resolve => { - const conn = net - .connect(socketPath) - .on('error', err => { - output( - ` error connecting to socket socket:${socketPath} ${err.toString()}` - ) - resolve(false) - }) - .on('connect', () => { - output(` connected to socket:${socketPath}`) - conn.end() - resolve(true) - }) - }) -} - -function negateAsync (asyncFn) { - return async function (...args) { - return !(await asyncFn(...args)) - } -} - -module.exports = waitOn