update @actions/tool-cache, install semver, nock

This commit is contained in:
Alex Kalyvitis 2020-06-18 16:31:13 +02:00
parent 74d434c5ca
commit 4c6749115a
678 changed files with 39259 additions and 14619 deletions

3
node_modules/nock/CHANGELOG.md generated vendored Normal file
View file

@ -0,0 +1,3 @@
# Changelog
Nocks changelog can be found directly in the [GitHub release notes](https://github.com/nock/nock/releases). These are automatically created by [semantic-release](https://github.com/semantic-release/semantic-release) based on their [commit message conventions](https://semantic-release.gitbook.io/semantic-release#commit-message-format).

21
node_modules/nock/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2011-2019 Pedro Teixeira and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1617
node_modules/nock/README.md generated vendored Normal file

File diff suppressed because it is too large Load diff

53
node_modules/nock/index.js generated vendored Normal file
View file

@ -0,0 +1,53 @@
'use strict'
const back = require('./lib/back')
const emitter = require('./lib/global_emitter')
const {
activate,
isActive,
isDone,
isOn,
pendingMocks,
activeMocks,
removeInterceptor,
disableNetConnect,
enableNetConnect,
removeAll,
abortPendingRequests,
} = require('./lib/intercept')
const recorder = require('./lib/recorder')
const { Scope, load, loadDefs, define } = require('./lib/scope')
module.exports = (basePath, options) => new Scope(basePath, options)
Object.assign(module.exports, {
activate,
isActive,
isDone,
pendingMocks,
activeMocks,
removeInterceptor,
disableNetConnect,
enableNetConnect,
cleanAll: removeAll,
abortPendingRequests,
load,
loadDefs,
define,
emitter,
recorder: {
rec: recorder.record,
clear: recorder.clear,
play: recorder.outputs,
},
restore: recorder.restore,
back,
})
// We always activate Nock on import, overriding the globals.
// Setting the Back mode "activates" Nock by overriding the global entries in the `http/s` modules.
// If Nock Back is configured, we need to honor that setting for backward compatibility,
// otherwise we rely on Nock Back's default initializing side effect.
if (isOn()) {
back.setMode(process.env.NOCK_BACK_MODE || 'dryrun')
}

279
node_modules/nock/lib/back.js generated vendored Normal file
View file

@ -0,0 +1,279 @@
'use strict'
const assert = require('assert')
const recorder = require('./recorder')
const {
activate,
disableNetConnect,
enableNetConnect,
removeAll: cleanAll,
} = require('./intercept')
const { loadDefs, define } = require('./scope')
const { format } = require('util')
const path = require('path')
const debug = require('debug')('nock.back')
let _mode = null
let fs
try {
fs = require('fs')
} catch (err) {
// do nothing, probably in browser
}
/**
* nock the current function with the fixture given
*
* @param {string} fixtureName - the name of the fixture, e.x. 'foo.json'
* @param {object} options - [optional] extra options for nock with, e.x. `{ assert: true }`
* @param {function} nockedFn - [optional] callback function to be executed with the given fixture being loaded;
* if defined the function will be called with context `{ scopes: loaded_nocks || [] }`
* set as `this` and `nockDone` callback function as first and only parameter;
* if not defined a promise resolving to `{nockDone, context}` where `context` is
* aforementioned `{ scopes: loaded_nocks || [] }`
*
* List of options:
*
* @param {function} before - a preprocessing function, gets called before nock.define
* @param {function} after - a postprocessing function, gets called after nock.define
* @param {function} afterRecord - a postprocessing function, gets called after recording. Is passed the array
* of scopes recorded and should return the array scopes to save to the fixture
* @param {function} recorder - custom options to pass to the recorder
*
*/
function Back(fixtureName, options, nockedFn) {
if (!Back.fixtures) {
throw new Error(
'Back requires nock.back.fixtures to be set\n' +
'Ex:\n' +
"\trequire(nock).back.fixtures = '/path/to/fixtures/'"
)
}
if (typeof fixtureName !== 'string') {
throw new Error('Parameter fixtureName must be a string')
}
if (arguments.length === 1) {
options = {}
} else if (arguments.length === 2) {
// If 2nd parameter is a function then `options` has been omitted
// otherwise `options` haven't been omitted but `nockedFn` was.
if (typeof options === 'function') {
nockedFn = options
options = {}
}
}
_mode.setup()
const fixture = path.join(Back.fixtures, fixtureName)
const context = _mode.start(fixture, options)
const nockDone = function() {
_mode.finish(fixture, options, context)
}
debug('context:', context)
// If nockedFn is a function then invoke it, otherwise return a promise resolving to nockDone.
if (typeof nockedFn === 'function') {
nockedFn.call(context, nockDone)
} else {
return Promise.resolve({ nockDone, context })
}
}
/*******************************************************************************
* Modes *
*******************************************************************************/
const wild = {
setup: function() {
cleanAll()
recorder.restore()
activate()
enableNetConnect()
},
start: function() {
return load() // don't load anything but get correct context
},
finish: function() {
// nothing to do
},
}
const dryrun = {
setup: function() {
recorder.restore()
cleanAll()
activate()
// We have to explicitly enable net connectivity as by default it's off.
enableNetConnect()
},
start: function(fixture, options) {
const contexts = load(fixture, options)
enableNetConnect()
return contexts
},
finish: function() {
// nothing to do
},
}
const record = {
setup: function() {
recorder.restore()
recorder.clear()
cleanAll()
activate()
disableNetConnect()
},
start: function(fixture, options) {
if (!fs) {
throw new Error('no fs')
}
const context = load(fixture, options)
if (!context.isLoaded) {
recorder.record({
dont_print: true,
output_objects: true,
...options.recorder,
})
context.isRecording = true
}
return context
},
finish: function(fixture, options, context) {
if (context.isRecording) {
let outputs = recorder.outputs()
if (typeof options.afterRecord === 'function') {
outputs = options.afterRecord(outputs)
}
outputs =
typeof outputs === 'string' ? outputs : JSON.stringify(outputs, null, 4)
debug('recorder outputs:', outputs)
fs.mkdirSync(path.dirname(fixture), { recursive: true })
fs.writeFileSync(fixture, outputs)
}
},
}
const lockdown = {
setup: function() {
recorder.restore()
recorder.clear()
cleanAll()
activate()
disableNetConnect()
},
start: function(fixture, options) {
return load(fixture, options)
},
finish: function() {
// nothing to do
},
}
function load(fixture, options) {
const context = {
scopes: [],
assertScopesFinished: function() {
assertScopes(this.scopes, fixture)
},
}
if (fixture && fixtureExists(fixture)) {
let scopes = loadDefs(fixture)
applyHook(scopes, options.before)
scopes = define(scopes)
applyHook(scopes, options.after)
context.scopes = scopes
context.isLoaded = true
}
return context
}
function applyHook(scopes, fn) {
if (!fn) {
return
}
if (typeof fn !== 'function') {
throw new Error('processing hooks must be a function')
}
scopes.forEach(fn)
}
function fixtureExists(fixture) {
if (!fs) {
throw new Error('no fs')
}
return fs.existsSync(fixture)
}
function assertScopes(scopes, fixture) {
const pending = scopes
.filter(scope => !scope.isDone())
.map(scope => scope.pendingMocks())
if (pending.length) {
assert.fail(
format(
'%j was not used, consider removing %s to rerecord fixture',
[].concat(...pending),
fixture
)
)
}
}
const Modes = {
wild, // all requests go out to the internet, dont replay anything, doesnt record anything
dryrun, // use recorded nocks, allow http calls, doesnt record anything, useful for writing new tests (default)
record, // use recorded nocks, record new nocks
lockdown, // use recorded nocks, disables all http calls even when not nocked, doesnt record
}
Back.setMode = function(mode) {
if (!(mode in Modes)) {
throw new Error(`Unknown mode: ${mode}`)
}
Back.currentMode = mode
debug('New nock back mode:', Back.currentMode)
_mode = Modes[mode]
_mode.setup()
}
Back.fixtures = null
Back.currentMode = null
module.exports = Back

645
node_modules/nock/lib/common.js generated vendored Normal file
View file

@ -0,0 +1,645 @@
'use strict'
const _ = require('lodash')
const debug = require('debug')('nock.common')
const url = require('url')
const timers = require('timers')
/**
* Normalizes the request options so that it always has `host` property.
*
* @param {Object} options - a parsed options object of the request
*/
function normalizeRequestOptions(options) {
options.proto = options.proto || 'http'
options.port = options.port || (options.proto === 'http' ? 80 : 443)
if (options.host) {
debug('options.host:', options.host)
if (!options.hostname) {
if (options.host.split(':').length === 2) {
options.hostname = options.host.split(':')[0]
} else {
options.hostname = options.host
}
}
}
debug('options.hostname in the end: %j', options.hostname)
options.host = `${options.hostname || 'localhost'}:${options.port}`
debug('options.host in the end: %j', options.host)
/// lowercase host names
;['hostname', 'host'].forEach(function(attr) {
if (options[attr]) {
options[attr] = options[attr].toLowerCase()
}
})
return options
}
/**
* Returns true if the data contained in buffer can be reconstructed
* from its utf8 representation.
*
* @param {Object} buffer - a Buffer object
* @returns {boolean}
*/
function isUtf8Representable(buffer) {
const utfEncodedBuffer = buffer.toString('utf8')
const reconstructedBuffer = Buffer.from(utfEncodedBuffer, 'utf8')
return reconstructedBuffer.equals(buffer)
}
// Array where all information about all the overridden requests are held.
let requestOverrides = {}
/**
* Overrides the current `request` function of `http` and `https` modules with
* our own version which intercepts issues HTTP/HTTPS requests and forwards them
* to the given `newRequest` function.
*
* @param {Function} newRequest - a function handling requests; it accepts four arguments:
* - proto - a string with the overridden module's protocol name (either `http` or `https`)
* - overriddenRequest - the overridden module's request function already bound to module's object
* - options - the options of the issued request
* - callback - the callback of the issued request
*/
function overrideRequests(newRequest) {
debug('overriding requests')
;['http', 'https'].forEach(function(proto) {
debug('- overriding request for', proto)
const moduleName = proto // 1 to 1 match of protocol and module is fortunate :)
const module = {
http: require('http'),
https: require('https'),
}[moduleName]
const overriddenRequest = module.request
const overriddenGet = module.get
if (requestOverrides[moduleName]) {
throw new Error(
`Module's request already overridden for ${moduleName} protocol.`
)
}
// Store the properties of the overridden request so that it can be restored later on.
requestOverrides[moduleName] = {
module,
request: overriddenRequest,
get: overriddenGet,
}
// https://nodejs.org/api/http.html#http_http_request_url_options_callback
module.request = function(input, options, callback) {
return newRequest(proto, overriddenRequest.bind(module), [
input,
options,
callback,
])
}
// https://nodejs.org/api/http.html#http_http_get_options_callback
module.get = function(input, options, callback) {
const req = newRequest(proto, overriddenGet.bind(module), [
input,
options,
callback,
])
req.end()
return req
}
debug('- overridden request for', proto)
})
}
/**
* Restores `request` function of `http` and `https` modules to values they
* held before they were overridden by us.
*/
function restoreOverriddenRequests() {
debug('restoring requests')
Object.entries(requestOverrides).forEach(
([proto, { module, request, get }]) => {
debug('- restoring request for', proto)
module.request = request
module.get = get
debug('- restored request for', proto)
}
)
requestOverrides = {}
}
/**
* In WHATWG URL vernacular, this returns the origin portion of a URL.
* However, the port is not included if it's standard and not already present on the host.
*/
function normalizeOrigin(proto, host, port) {
const hostHasPort = host.includes(':')
const portIsStandard =
(proto === 'http' && (port === 80 || port === '80')) ||
(proto === 'https' && (port === 443 || port === '443'))
const portStr = hostHasPort || portIsStandard ? '' : `:${port}`
return `${proto}://${host}${portStr}`
}
/**
* Get high level information about request as string
* @param {Object} options
* @param {string} options.method
* @param {number|string} options.port
* @param {string} options.proto Set internally. always http or https
* @param {string} options.hostname
* @param {string} options.path
* @param {Object} options.headers
* @param {string} body
* @return {string}
*/
function stringifyRequest(options, body) {
const { method = 'GET', path = '', port } = options
const origin = normalizeOrigin(options.proto, options.hostname, port)
const log = {
method,
url: `${origin}${path}`,
headers: options.headers,
}
if (body) {
log.body = body
}
return JSON.stringify(log, null, 2)
}
function isContentEncoded(headers) {
const contentEncoding = headers['content-encoding']
return typeof contentEncoding === 'string' && contentEncoding !== ''
}
function contentEncoding(headers, encoder) {
const contentEncoding = headers['content-encoding']
return contentEncoding === encoder
}
function isJSONContent(headers) {
// https://tools.ietf.org/html/rfc8259
const contentType = String(headers['content-type'] || '').toLowerCase()
return contentType.startsWith('application/json')
}
/**
* Return a new object with all field names of the headers lower-cased.
*
* Duplicates throw an error.
*/
function headersFieldNamesToLowerCase(headers) {
if (!_.isPlainObject(headers)) {
throw Error('Headers must be provided as an object')
}
const lowerCaseHeaders = {}
Object.entries(headers).forEach(([fieldName, fieldValue]) => {
const key = fieldName.toLowerCase()
if (lowerCaseHeaders[key] !== undefined) {
throw Error(
`Failed to convert header keys to lower case due to field name conflict: ${key}`
)
}
lowerCaseHeaders[key] = fieldValue
})
return lowerCaseHeaders
}
const headersFieldsArrayToLowerCase = headers => [
...new Set(headers.map(fieldName => fieldName.toLowerCase())),
]
/**
* Converts the various accepted formats of headers into a flat array representing "raw headers".
*
* Nock allows headers to be provided as a raw array, a plain object, or a Map.
*
* While all the header names are expected to be strings, the values are left intact as they can
* be functions, strings, or arrays of strings.
*
* https://nodejs.org/api/http.html#http_message_rawheaders
*/
function headersInputToRawArray(headers) {
if (headers === undefined) {
return []
}
if (Array.isArray(headers)) {
// If the input is an array, assume it's already in the raw format and simply return a copy
// but throw an error if there aren't an even number of items in the array
if (headers.length % 2) {
throw new Error(
`Raw headers must be provided as an array with an even number of items. [fieldName, value, ...]`
)
}
return [...headers]
}
// [].concat(...) is used instead of Array.flat until v11 is the minimum Node version
if (_.isMap(headers)) {
return [].concat(...Array.from(headers, ([k, v]) => [k.toString(), v]))
}
if (_.isPlainObject(headers)) {
return [].concat(...Object.entries(headers))
}
throw new Error(
`Headers must be provided as an array of raw values, a Map, or a plain Object. ${headers}`
)
}
/**
* Converts an array of raw headers to an object, using the same rules as Nodes `http.IncomingMessage.headers`.
*
* Header names/keys are lower-cased.
*/
function headersArrayToObject(rawHeaders) {
if (!Array.isArray(rawHeaders)) {
throw Error('Expected a header array')
}
const accumulator = {}
forEachHeader(rawHeaders, (value, fieldName) => {
addHeaderLine(accumulator, fieldName, value)
})
return accumulator
}
const noDuplicatesHeaders = new Set([
'age',
'authorization',
'content-length',
'content-type',
'etag',
'expires',
'from',
'host',
'if-modified-since',
'if-unmodified-since',
'last-modified',
'location',
'max-forwards',
'proxy-authorization',
'referer',
'retry-after',
'user-agent',
])
/**
* Set key/value data in accordance with Node's logic for folding duplicate headers.
*
* The `value` param should be a function, string, or array of strings.
*
* Node's docs and source:
* https://nodejs.org/api/http.html#http_message_headers
* https://github.com/nodejs/node/blob/908292cf1f551c614a733d858528ffb13fb3a524/lib/_http_incoming.js#L245
*
* Header names are lower-cased.
* Duplicates in raw headers are handled in the following ways, depending on the header name:
* - Duplicates of field names listed in `noDuplicatesHeaders` (above) are discarded.
* - `set-cookie` is always an array. Duplicates are added to the array.
* - For duplicate `cookie` headers, the values are joined together with '; '.
* - For all other headers, the values are joined together with ', '.
*
* Node's implementation is larger because it highly optimizes for not having to call `toLowerCase()`.
* We've opted to always call `toLowerCase` in exchange for a more concise function.
*
* While Node has the luxury of knowing `value` is always a string, we do an extra step of coercion at the top.
*/
function addHeaderLine(headers, name, value) {
let values // code below expects `values` to be an array of strings
if (typeof value === 'function') {
// Function values are evaluated towards the end of the response, before that we use a placeholder
// string just to designate that the header exists. Useful when `Content-Type` is set with a function.
values = [value.name]
} else if (Array.isArray(value)) {
values = value.map(String)
} else {
values = [String(value)]
}
const key = name.toLowerCase()
if (key === 'set-cookie') {
// Array header -- only Set-Cookie at the moment
if (headers['set-cookie'] === undefined) {
headers['set-cookie'] = values
} else {
headers['set-cookie'].push(...values)
}
} else if (noDuplicatesHeaders.has(key)) {
if (headers[key] === undefined) {
// Drop duplicates
headers[key] = values[0]
}
} else {
if (headers[key] !== undefined) {
values = [headers[key], ...values]
}
const separator = key === 'cookie' ? '; ' : ', '
headers[key] = values.join(separator)
}
}
/**
* Deletes the given `fieldName` property from `headers` object by performing
* case-insensitive search through keys.
*
* @headers {Object} headers - object of header field names and values
* @fieldName {String} field name - string with the case-insensitive field name
*/
function deleteHeadersField(headers, fieldNameToDelete) {
if (!_.isPlainObject(headers)) {
throw Error('headers must be an object')
}
if (typeof fieldNameToDelete !== 'string') {
throw Error('field name must be a string')
}
const lowerCaseFieldNameToDelete = fieldNameToDelete.toLowerCase()
// Search through the headers and delete all values whose field name matches the given field name.
Object.keys(headers)
.filter(fieldName => fieldName.toLowerCase() === lowerCaseFieldNameToDelete)
.forEach(fieldName => delete headers[fieldName])
}
/**
* Utility for iterating over a raw headers array.
*
* The callback is called with:
* - The header value. string, array of strings, or a function
* - The header field name. string
* - Index of the header field in the raw header array.
*/
function forEachHeader(rawHeaders, callback) {
for (let i = 0; i < rawHeaders.length; i += 2) {
callback(rawHeaders[i + 1], rawHeaders[i], i)
}
}
function percentDecode(str) {
try {
return decodeURIComponent(str.replace(/\+/g, ' '))
} catch (e) {
return str
}
}
/**
* URI encode the provided string, stringently adhering to RFC 3986.
*
* RFC 3986 reserves !, ', (, ), and * but encodeURIComponent does not encode them so we do it manually.
*
* https://tools.ietf.org/html/rfc3986
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
*/
function percentEncode(str) {
return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
return `%${c
.charCodeAt(0)
.toString(16)
.toUpperCase()}`
})
}
function matchStringOrRegexp(target, pattern) {
const targetStr =
target === undefined || target === null ? '' : String(target)
return pattern instanceof RegExp
? pattern.test(targetStr)
: targetStr === String(pattern)
}
/**
* Formats a query parameter.
*
* @param key The key of the query parameter to format.
* @param value The value of the query parameter to format.
* @param stringFormattingFn The function used to format string values. Can
* be used to encode or decode the query value.
*
* @returns *[] the formatted [key, value] pair.
*/
function formatQueryValue(key, value, stringFormattingFn) {
// TODO: Probably refactor code to replace `switch(true)` with `if`/`else`.
switch (true) {
case typeof value === 'number': // fall-through
case typeof value === 'boolean':
value = value.toString()
break
case value === null:
case value === undefined:
value = ''
break
case typeof value === 'string':
if (stringFormattingFn) {
value = stringFormattingFn(value)
}
break
case value instanceof RegExp:
break
case Array.isArray(value): {
value = value.map(function(val, idx) {
return formatQueryValue(idx, val, stringFormattingFn)[1]
})
break
}
case typeof value === 'object': {
value = Object.entries(value).reduce(function(acc, [subKey, subVal]) {
const subPair = formatQueryValue(subKey, subVal, stringFormattingFn)
acc[subPair[0]] = subPair[1]
return acc
}, {})
break
}
}
if (stringFormattingFn) key = stringFormattingFn(key)
return [key, value]
}
function isStream(obj) {
return (
obj &&
typeof obj !== 'string' &&
!Buffer.isBuffer(obj) &&
typeof obj.setEncoding === 'function'
)
}
/**
* Converts the arguments from the various signatures of http[s].request into a standard
* options object and an optional callback function.
*
* https://nodejs.org/api/http.html#http_http_request_url_options_callback
*
* Taken from the beginning of the native `ClientRequest`.
* https://github.com/nodejs/node/blob/908292cf1f551c614a733d858528ffb13fb3a524/lib/_http_client.js#L68
*/
function normalizeClientRequestArgs(input, options, cb) {
if (typeof input === 'string') {
input = urlToOptions(new url.URL(input))
} else if (input instanceof url.URL) {
input = urlToOptions(input)
} else {
cb = options
options = input
input = null
}
if (typeof options === 'function') {
cb = options
options = input || {}
} else {
options = Object.assign(input || {}, options)
}
return { options, callback: cb }
}
/**
* Utility function that converts a URL object into an ordinary
* options object as expected by the http.request and https.request APIs.
*
* This was copied from Node's source
* https://github.com/nodejs/node/blob/908292cf1f551c614a733d858528ffb13fb3a524/lib/internal/url.js#L1257
*/
function urlToOptions(url) {
const options = {
protocol: url.protocol,
hostname:
typeof url.hostname === 'string' && url.hostname.startsWith('[')
? url.hostname.slice(1, -1)
: url.hostname,
hash: url.hash,
search: url.search,
pathname: url.pathname,
path: `${url.pathname}${url.search || ''}`,
href: url.href,
}
if (url.port !== '') {
options.port = Number(url.port)
}
if (url.username || url.password) {
options.auth = `${url.username}:${url.password}`
}
return options
}
/**
* Determines if request data matches the expected schema.
*
* Used for comparing decoded search parameters, request body JSON objects,
* and URL decoded request form bodies.
*
* Performs a general recursive strict comparision with two caveats:
* - The expected data can use regexp to compare values
* - JSON path notation and nested objects are considered equal
*/
const dataEqual = (expected, actual) =>
deepEqual(expand(expected), expand(actual))
/**
* Converts flat objects whose keys use JSON path notation to nested objects.
*
* The input object is not mutated.
*
* @example
* { 'foo[bar][0]': 'baz' } -> { foo: { bar: [ 'baz' ] } }
*/
const expand = input =>
Object.entries(input).reduce((acc, [k, v]) => _.set(acc, k, v), {})
/**
* Performs a recursive strict comparison between two values.
*
* Expected values or leaf nodes of expected object values that are RegExp use test() for comparison.
*/
function deepEqual(expected, actual) {
debug('deepEqual comparing', typeof expected, expected, typeof actual, actual)
if (expected instanceof RegExp) {
return expected.test(actual)
}
if (Array.isArray(expected) || _.isPlainObject(expected)) {
if (actual === undefined) {
return false
}
const expKeys = Object.keys(expected)
if (expKeys.length !== Object.keys(actual).length) {
return false
}
return expKeys.every(key => deepEqual(expected[key], actual[key]))
}
return expected === actual
}
const timeouts = []
const intervals = []
const immediates = []
const wrapTimer = (timer, ids) => (...args) => {
const id = timer(...args)
ids.push(id)
return id
}
const setTimeout = wrapTimer(timers.setTimeout, timeouts)
const setInterval = wrapTimer(timers.setInterval, intervals)
const setImmediate = wrapTimer(timers.setImmediate, immediates)
function clearTimer(clear, ids) {
while (ids.length) {
clear(ids.shift())
}
}
function removeAllTimers() {
clearTimer(clearTimeout, timeouts)
clearTimer(clearInterval, intervals)
clearTimer(clearImmediate, immediates)
}
exports.normalizeClientRequestArgs = normalizeClientRequestArgs
exports.normalizeRequestOptions = normalizeRequestOptions
exports.normalizeOrigin = normalizeOrigin
exports.isUtf8Representable = isUtf8Representable
exports.overrideRequests = overrideRequests
exports.restoreOverriddenRequests = restoreOverriddenRequests
exports.stringifyRequest = stringifyRequest
exports.isContentEncoded = isContentEncoded
exports.contentEncoding = contentEncoding
exports.isJSONContent = isJSONContent
exports.headersFieldNamesToLowerCase = headersFieldNamesToLowerCase
exports.headersFieldsArrayToLowerCase = headersFieldsArrayToLowerCase
exports.headersArrayToObject = headersArrayToObject
exports.headersInputToRawArray = headersInputToRawArray
exports.deleteHeadersField = deleteHeadersField
exports.forEachHeader = forEachHeader
exports.percentEncode = percentEncode
exports.percentDecode = percentDecode
exports.matchStringOrRegexp = matchStringOrRegexp
exports.formatQueryValue = formatQueryValue
exports.isStream = isStream
exports.dataEqual = dataEqual
exports.setTimeout = setTimeout
exports.setInterval = setInterval
exports.setImmediate = setImmediate
exports.removeAllTimers = removeAllTimers

52
node_modules/nock/lib/delayed_body.js generated vendored Normal file
View file

@ -0,0 +1,52 @@
'use strict'
/**
* Creates a stream which becomes the response body of the interceptor when a
* delay is set. The stream outputs the intended body and EOF after the delay.
*
* @param {String|Buffer|Stream} body - the body to write/pipe out
* @param {Integer} ms - The delay in milliseconds
* @constructor
*/
const { Transform } = require('stream')
const common = require('./common')
module.exports = class DelayedBody extends Transform {
constructor(ms, body) {
super()
const self = this
let data = ''
let ended = false
if (common.isStream(body)) {
body.on('data', function(chunk) {
data += Buffer.isBuffer(chunk) ? chunk.toString() : chunk
})
body.once('end', function() {
ended = true
})
body.resume()
}
// TODO: This would be more readable if the stream case were moved into
// the `if` statement above.
common.setTimeout(function() {
if (common.isStream(body) && !ended) {
body.once('end', function() {
self.end(data)
})
} else {
self.end(data || body)
}
}, ms)
}
_transform(chunk, encoding, cb) {
this.push(chunk)
process.nextTick(cb)
}
}

5
node_modules/nock/lib/global_emitter.js generated vendored Normal file
View file

@ -0,0 +1,5 @@
'use strict'
const { EventEmitter } = require('events')
module.exports = new EventEmitter()

449
node_modules/nock/lib/intercept.js generated vendored Normal file
View file

@ -0,0 +1,449 @@
'use strict'
/**
* @module nock/intercept
*/
const { InterceptedRequestRouter } = require('./intercepted_request_router')
const common = require('./common')
const { inherits } = require('util')
const http = require('http')
const debug = require('debug')('nock.intercept')
const globalEmitter = require('./global_emitter')
/**
* @name NetConnectNotAllowedError
* @private
* @desc Error trying to make a connection when disabled external access.
* @class
* @example
* nock.disableNetConnect();
* http.get('http://zombo.com');
* // throw NetConnectNotAllowedError
*/
function NetConnectNotAllowedError(host, path) {
Error.call(this)
this.name = 'NetConnectNotAllowedError'
this.code = 'ENETUNREACH'
this.message = `Nock: Disallowed net connect for "${host}${path}"`
Error.captureStackTrace(this, this.constructor)
}
inherits(NetConnectNotAllowedError, Error)
let allInterceptors = {}
let allowNetConnect
/**
* Enabled real request.
* @public
* @param {String|RegExp} matcher=RegExp.new('.*') Expression to match
* @example
* // Enables all real requests
* nock.enableNetConnect();
* @example
* // Enables real requests for url that matches google
* nock.enableNetConnect('google');
* @example
* // Enables real requests for url that matches google and amazon
* nock.enableNetConnect(/(google|amazon)/);
* @example
* // Enables real requests for url that includes google
* nock.enableNetConnect(host => host.includes('google'));
*/
function enableNetConnect(matcher) {
if (typeof matcher === 'string') {
allowNetConnect = new RegExp(matcher)
} else if (matcher instanceof RegExp) {
allowNetConnect = matcher
} else if (typeof matcher === 'function') {
allowNetConnect = { test: matcher }
} else {
allowNetConnect = /.*/
}
}
function isEnabledForNetConnect(options) {
common.normalizeRequestOptions(options)
const enabled = allowNetConnect && allowNetConnect.test(options.host)
debug('Net connect', enabled ? '' : 'not', 'enabled for', options.host)
return enabled
}
/**
* Disable all real requests.
* @public
* @example
* nock.disableNetConnect();
*/
function disableNetConnect() {
allowNetConnect = undefined
}
function isOn() {
return !isOff()
}
function isOff() {
return process.env.NOCK_OFF === 'true'
}
function addInterceptor(key, interceptor, scope, scopeOptions, host) {
if (!(key in allInterceptors)) {
allInterceptors[key] = { key, interceptors: [] }
}
interceptor.__nock_scope = scope
// We need scope's key and scope options for scope filtering function (if defined)
interceptor.__nock_scopeKey = key
interceptor.__nock_scopeOptions = scopeOptions
// We need scope's host for setting correct request headers for filtered scopes.
interceptor.__nock_scopeHost = host
interceptor.interceptionCounter = 0
if (scopeOptions.allowUnmocked) allInterceptors[key].allowUnmocked = true
allInterceptors[key].interceptors.push(interceptor)
}
function remove(interceptor) {
if (interceptor.__nock_scope.shouldPersist() || --interceptor.counter > 0) {
return
}
const { basePath } = interceptor
const interceptors =
(allInterceptors[basePath] && allInterceptors[basePath].interceptors) || []
// TODO: There is a clearer way to write that we want to delete the first
// matching instance. I'm also not sure why we couldn't delete _all_
// matching instances.
interceptors.some(function(thisInterceptor, i) {
return thisInterceptor === interceptor ? interceptors.splice(i, 1) : false
})
}
function removeAll() {
Object.keys(allInterceptors).forEach(function(key) {
allInterceptors[key].interceptors.forEach(function(interceptor) {
interceptor.scope.keyedInterceptors = {}
})
})
allInterceptors = {}
}
/**
* Return all the Interceptors whose Scopes match against the base path of the provided options.
*
* @returns {Interceptor[]}
*/
function interceptorsFor(options) {
common.normalizeRequestOptions(options)
debug('interceptors for %j', options.host)
const basePath = `${options.proto}://${options.host}`
debug('filtering interceptors for basepath', basePath)
// First try to use filteringScope if any of the interceptors has it defined.
for (const { key, interceptors, allowUnmocked } of Object.values(
allInterceptors
)) {
for (const interceptor of interceptors) {
const { filteringScope } = interceptor.__nock_scopeOptions
// If scope filtering function is defined and returns a truthy value then
// we have to treat this as a match.
if (filteringScope && filteringScope(basePath)) {
debug('found matching scope interceptor')
// Keep the filtered scope (its key) to signal the rest of the module
// that this wasn't an exact but filtered match.
interceptors.forEach(ic => {
ic.__nock_filteredScope = ic.__nock_scopeKey
})
return interceptors
}
}
if (common.matchStringOrRegexp(basePath, key)) {
if (allowUnmocked && interceptors.length === 0) {
debug('matched base path with allowUnmocked (no matching interceptors)')
return [
{
options: { allowUnmocked: true },
matchOrigin() {
return false
},
},
]
} else {
debug(
`matched base path (${interceptors.length} interceptor${
interceptors.length > 1 ? 's' : ''
})`
)
return interceptors
}
}
}
return undefined
}
function removeInterceptor(options) {
// Lazily import to avoid circular imports.
const Interceptor = require('./interceptor')
let baseUrl, key, method, proto
if (options instanceof Interceptor) {
baseUrl = options.basePath
key = options._key
} else {
proto = options.proto ? options.proto : 'http'
common.normalizeRequestOptions(options)
baseUrl = `${proto}://${options.host}`
method = (options.method && options.method.toUpperCase()) || 'GET'
key = `${method} ${baseUrl}${options.path || '/'}`
}
if (
allInterceptors[baseUrl] &&
allInterceptors[baseUrl].interceptors.length > 0
) {
for (let i = 0; i < allInterceptors[baseUrl].interceptors.length; i++) {
const interceptor = allInterceptors[baseUrl].interceptors[i]
if (interceptor._key === key) {
allInterceptors[baseUrl].interceptors.splice(i, 1)
interceptor.scope.remove(key, interceptor)
break
}
}
return true
}
return false
}
// Variable where we keep the ClientRequest we have overridden
// (which might or might not be node's original http.ClientRequest)
let originalClientRequest
function ErroringClientRequest(error) {
http.OutgoingMessage.call(this)
process.nextTick(
function() {
this.emit('error', error)
}.bind(this)
)
}
inherits(ErroringClientRequest, http.ClientRequest)
function overrideClientRequest() {
// Here's some background discussion about overriding ClientRequest:
// - https://github.com/nodejitsu/mock-request/issues/4
// - https://github.com/nock/nock/issues/26
// It would be good to add a comment that explains this more clearly.
debug('Overriding ClientRequest')
// ----- Extending http.ClientRequest
// Define the overriding client request that nock uses internally.
function OverriddenClientRequest(...args) {
const { options, callback } = common.normalizeClientRequestArgs(...args)
if (Object.keys(options).length === 0) {
// As weird as it is, it's possible to call `http.request` without
// options, and it makes a request to localhost or somesuch. We should
// support it too, for parity. However it doesn't work today, and fixing
// it seems low priority. Giving an explicit error is nicer than
// crashing with a weird stack trace. `http[s].request()`, nock's other
// client-facing entry point, makes a similar check.
// https://github.com/nock/nock/pull/1386
// https://github.com/nock/nock/pull/1440
throw Error(
'Creating a ClientRequest with empty `options` is not supported in Nock'
)
}
http.OutgoingMessage.call(this)
// Filter the interceptors per request options.
const interceptors = interceptorsFor(options)
if (isOn() && interceptors) {
debug('using', interceptors.length, 'interceptors')
// Use filtered interceptors to intercept requests.
const overrider = new InterceptedRequestRouter({
req: this,
options,
interceptors,
})
Object.assign(this, overrider)
if (callback) {
this.once('response', callback)
}
} else {
debug('falling back to original ClientRequest')
// Fallback to original ClientRequest if nock is off or the net connection is enabled.
if (isOff() || isEnabledForNetConnect(options)) {
originalClientRequest.apply(this, arguments)
} else {
common.setImmediate(
function() {
const error = new NetConnectNotAllowedError(
options.host,
options.path
)
this.emit('error', error)
}.bind(this)
)
}
}
}
inherits(OverriddenClientRequest, http.ClientRequest)
// Override the http module's request but keep the original so that we can use it and later restore it.
// NOTE: We only override http.ClientRequest as https module also uses it.
originalClientRequest = http.ClientRequest
http.ClientRequest = OverriddenClientRequest
debug('ClientRequest overridden')
}
function restoreOverriddenClientRequest() {
debug('restoring overridden ClientRequest')
// Restore the ClientRequest we have overridden.
if (!originalClientRequest) {
debug('- ClientRequest was not overridden')
} else {
http.ClientRequest = originalClientRequest
originalClientRequest = undefined
debug('- ClientRequest restored')
}
}
function isActive() {
// If ClientRequest has been overwritten by Nock then originalClientRequest is not undefined.
// This means that Nock has been activated.
return originalClientRequest !== undefined
}
function interceptorScopes() {
const nestedInterceptors = Object.values(allInterceptors).map(
i => i.interceptors
)
return [].concat(...nestedInterceptors).map(i => i.scope)
}
function isDone() {
return interceptorScopes().every(scope => scope.isDone())
}
function pendingMocks() {
return [].concat(...interceptorScopes().map(scope => scope.pendingMocks()))
}
function activeMocks() {
return [].concat(...interceptorScopes().map(scope => scope.activeMocks()))
}
function activate() {
if (originalClientRequest) {
throw new Error('Nock already active')
}
overrideClientRequest()
// ----- Overriding http.request and https.request:
common.overrideRequests(function(proto, overriddenRequest, args) {
// NOTE: overriddenRequest is already bound to its module.
const { options, callback } = common.normalizeClientRequestArgs(...args)
if (Object.keys(options).length === 0) {
// As weird as it is, it's possible to call `http.request` without
// options, and it makes a request to localhost or somesuch. We should
// support it too, for parity. However it doesn't work today, and fixing
// it seems low priority. Giving an explicit error is nicer than
// crashing with a weird stack trace. `new ClientRequest()`, nock's
// other client-facing entry point, makes a similar check.
// https://github.com/nock/nock/pull/1386
// https://github.com/nock/nock/pull/1440
throw Error(
'Making a request with empty `options` is not supported in Nock'
)
}
// The option per the docs is `protocol`. Its unclear if this line is meant to override that and is misspelled or if
// the intend is to explicitly keep track of which module was called using a separate name.
// Either way, `proto` is used as the source of truth from here on out.
options.proto = proto
const interceptors = interceptorsFor(options)
if (isOn() && interceptors) {
const matches = interceptors.some(interceptor =>
interceptor.matchOrigin(options)
)
const allowUnmocked = interceptors.some(
interceptor => interceptor.options.allowUnmocked
)
if (!matches && allowUnmocked) {
let req
if (proto === 'https') {
const { ClientRequest } = http
http.ClientRequest = originalClientRequest
req = overriddenRequest(options, callback)
http.ClientRequest = ClientRequest
} else {
req = overriddenRequest(options, callback)
}
globalEmitter.emit('no match', req)
return req
}
// NOTE: Since we already overrode the http.ClientRequest we are in fact constructing
// our own OverriddenClientRequest.
return new http.ClientRequest(options, callback)
} else {
globalEmitter.emit('no match', options)
if (isOff() || isEnabledForNetConnect(options)) {
return overriddenRequest(options, callback)
} else {
const error = new NetConnectNotAllowedError(options.host, options.path)
return new ErroringClientRequest(error)
}
}
})
}
module.exports = {
addInterceptor,
remove,
removeAll,
removeInterceptor,
isOn,
activate,
isActive,
isDone,
pendingMocks,
activeMocks,
enableNetConnect,
disableNetConnect,
restoreOverriddenClientRequest,
abortPendingRequests: common.removeAllTimers,
}

320
node_modules/nock/lib/intercepted_request_router.js generated vendored Normal file
View file

@ -0,0 +1,320 @@
'use strict'
const debug = require('debug')('nock.request_overrider')
const {
IncomingMessage,
ClientRequest,
request: originalHttpRequest,
} = require('http')
const { request: originalHttpsRequest } = require('https')
const propagate = require('propagate')
const common = require('./common')
const globalEmitter = require('./global_emitter')
const Socket = require('./socket')
const { playbackInterceptor } = require('./playback_interceptor')
/**
* Given a group of interceptors, appropriately route an outgoing request.
* Identify which interceptor ought to respond, if any, then delegate to
* `playbackInterceptor()` to consume the request itself.
*/
class InterceptedRequestRouter {
constructor({ req, options, interceptors }) {
this.req = req
this.options = {
// We may be changing the options object and we don't want those changes
// affecting the user so we use a clone of the object.
...options,
// We use lower-case header field names throughout Nock.
headers: common.headersFieldNamesToLowerCase(options.headers || {}),
}
this.interceptors = interceptors
this.socket = new Socket(options)
// support setting `timeout` using request `options`
// https://nodejs.org/docs/latest-v12.x/api/http.html#http_http_request_url_options_callback
if (options.timeout) {
this.socket.setTimeout(options.timeout)
}
this.response = new IncomingMessage(this.socket)
this.playbackStarted = false
this.requestBodyBuffers = []
this.attachToReq()
}
attachToReq() {
const { req, response, socket, options } = this
response.req = req
for (const [name, val] of Object.entries(options.headers)) {
req.setHeader(name.toLowerCase(), val)
}
if (options.auth && !options.headers.authorization) {
req.setHeader(
// We use lower-case header field names throughout Nock.
'authorization',
`Basic ${Buffer.from(options.auth).toString('base64')}`
)
}
req.path = options.path
req.method = options.method
// ClientRequest.connection is an alias for ClientRequest.socket
// https://nodejs.org/api/http.html#http_request_socket
// https://github.com/nodejs/node/blob/b0f75818f39ed4e6bd80eb7c4010c1daf5823ef7/lib/_http_client.js#L640-L641
// The same Socket is shared between the request and response to mimic native behavior.
req.socket = req.connection = socket
propagate(['error', 'timeout'], req.socket, req)
req.write = (...args) => this.handleWrite(...args)
req.end = (...args) => this.handleEnd(...args)
req.flushHeaders = (...args) => this.handleFlushHeaders(...args)
req.abort = (...args) => this.handleAbort(...args)
// https://github.com/nock/nock/issues/256
if (options.headers.expect === '100-continue') {
common.setImmediate(() => {
debug('continue')
req.emit('continue')
})
}
// Emit a fake socket event on the next tick to mimic what would happen on a real request.
// Some clients listen for a 'socket' event to be emitted before calling end(),
// which causes nock to hang.
process.nextTick(() => {
req.emit('socket', socket)
// https://nodejs.org/api/net.html#net_event_connect
socket.emit('connect')
// https://nodejs.org/api/tls.html#tls_event_secureconnect
if (socket.authorized) {
socket.emit('secureConnect')
}
})
}
emitError(error) {
const { req } = this
process.nextTick(() => {
req.emit('error', error)
})
}
handleWrite(buffer, encoding, callback) {
debug('write', arguments)
const { req } = this
if (!req.aborted) {
if (buffer) {
if (!Buffer.isBuffer(buffer)) {
buffer = Buffer.from(buffer, encoding)
}
this.requestBodyBuffers.push(buffer)
}
// can't use instanceof Function because some test runners
// run tests in vm.runInNewContext where Function is not same
// as that in the current context
// https://github.com/nock/nock/pull/1754#issuecomment-571531407
if (typeof callback === 'function') {
callback()
}
} else {
this.emitError(new Error('Request aborted'))
}
common.setImmediate(function() {
req.emit('drain')
})
return false
}
handleEnd(chunk, encoding, callback) {
debug('req.end')
const { req } = this
// handle the different overloaded param signatures
if (typeof chunk === 'function') {
callback = chunk
chunk = null
} else if (typeof encoding === 'function') {
callback = encoding
encoding = null
}
if (typeof callback === 'function') {
req.once('finish', callback)
}
if (!req.aborted && !this.playbackStarted) {
req.write(chunk, encoding)
this.startPlayback()
}
if (req.aborted) {
this.emitError(new Error('Request aborted'))
}
return req
}
handleFlushHeaders() {
debug('req.flushHeaders')
const { req } = this
if (!req.aborted && !this.playbackStarted) {
this.startPlayback()
}
if (req.aborted) {
this.emitError(new Error('Request aborted'))
}
}
handleAbort() {
debug('req.abort')
const { req, response, socket } = this
if (req.aborted) {
return
}
req.aborted = Date.now()
if (!this.playbackStarted) {
this.startPlayback()
}
const err = new Error()
err.code = 'aborted'
response.emit('close', err)
socket.destroy()
req.emit('abort')
const connResetError = new Error('socket hang up')
connResetError.code = 'ECONNRESET'
this.emitError(connResetError)
}
/**
* Set request headers of the given request. This is needed both during the
* routing phase, in case header filters were specified, and during the
* interceptor-playback phase, to correctly pass mocked request headers.
* TODO There are some problems with this; see https://github.com/nock/nock/issues/1718
*/
setHostHeaderUsingInterceptor(interceptor) {
const { req, options } = this
// If a filtered scope is being used we have to use scope's host in the
// header, otherwise 'host' header won't match.
// NOTE: We use lower-case header field names throughout Nock.
const HOST_HEADER = 'host'
if (interceptor.__nock_filteredScope && interceptor.__nock_scopeHost) {
options.headers[HOST_HEADER] = interceptor.__nock_scopeHost
req.setHeader(HOST_HEADER, interceptor.__nock_scopeHost)
} else {
// For all other cases, we always add host header equal to the requested
// host unless it was already defined.
if (options.host && !req.getHeader(HOST_HEADER)) {
let hostHeader = options.host
if (options.port === 80 || options.port === 443) {
hostHeader = hostHeader.split(':')[0]
}
req.setHeader(HOST_HEADER, hostHeader)
}
}
}
startPlayback() {
debug('ending')
this.playbackStarted = true
const { req, response, socket, options, interceptors } = this
Object.assign(options, {
// Re-update `options` with the current value of `req.path` because badly
// behaving agents like superagent like to change `req.path` mid-flight.
path: req.path,
// Similarly, node-http-proxy will modify headers in flight, so we have
// to put the headers back into options.
// https://github.com/nock/nock/pull/1484
headers: req.getHeaders(),
// Fixes https://github.com/nock/nock/issues/976
protocol: `${options.proto}:`,
})
interceptors.forEach(interceptor => {
this.setHostHeaderUsingInterceptor(interceptor)
})
const requestBodyBuffer = Buffer.concat(this.requestBodyBuffers)
// When request body is a binary buffer we internally use in its hexadecimal
// representation.
const requestBodyIsUtf8Representable = common.isUtf8Representable(
requestBodyBuffer
)
const requestBodyString = requestBodyBuffer.toString(
requestBodyIsUtf8Representable ? 'utf8' : 'hex'
)
const matchedInterceptor = interceptors.find(i =>
i.match(req, options, requestBodyString)
)
if (matchedInterceptor) {
debug('interceptor identified, starting mocking')
// wait to emit the finish event until we know for sure an Interceptor is going to playback.
// otherwise an unmocked request might emit finish twice.
req.finished = true
req.emit('finish')
playbackInterceptor({
req,
socket,
options,
requestBodyString,
requestBodyIsUtf8Representable,
response,
interceptor: matchedInterceptor,
})
} else {
globalEmitter.emit('no match', req, options, requestBodyString)
// Try to find a hostname match that allows unmocked.
const allowUnmocked = interceptors.some(
i => i.matchHostName(options) && i.options.allowUnmocked
)
if (allowUnmocked && req instanceof ClientRequest) {
const newReq =
options.proto === 'https'
? originalHttpsRequest(options)
: originalHttpRequest(options)
propagate(newReq, req)
// We send the raw buffer as we received it, not as we interpreted it.
newReq.end(requestBodyBuffer)
} else {
const err = new Error(
`Nock: No match for request ${common.stringifyRequest(
options,
requestBodyString
)}`
)
err.statusCode = err.status = 404
this.emitError(err)
}
}
}
}
module.exports = { InterceptedRequestRouter }

619
node_modules/nock/lib/interceptor.js generated vendored Normal file
View file

@ -0,0 +1,619 @@
'use strict'
const debug = require('debug')('nock.interceptor')
const stringify = require('json-stringify-safe')
const _ = require('lodash')
const querystring = require('querystring')
const { URL, URLSearchParams } = require('url')
const common = require('./common')
const { remove } = require('./intercept')
const matchBody = require('./match_body')
let fs
try {
fs = require('fs')
} catch (err) {
// do nothing, we're in the browser
}
module.exports = class Interceptor {
/**
*
* Valid argument types for `uri`:
* - A string used for strict comparisons with pathname.
* The search portion of the URI may also be postfixed, in which case the search params
* are striped and added via the `query` method.
* - A RegExp instance that tests against only the pathname of requests.
* - A synchronous function bound to this Interceptor instance. It's provided the pathname
* of requests and must return a boolean denoting if the request is considered a match.
*/
constructor(scope, uri, method, requestBody, interceptorOptions) {
const uriIsStr = typeof uri === 'string'
// Check for leading slash. Uri can be either a string or a regexp, but
// When enabled filteringScope ignores the passed URL entirely so we skip validation.
if (
!scope.scopeOptions.filteringScope &&
uriIsStr &&
!uri.startsWith('/') &&
!uri.startsWith('*')
) {
throw Error(
`Non-wildcard URL path strings must begin with a slash (otherwise they won't match anything) (got: ${uri})`
)
}
if (!method) {
throw new Error(
'The "method" parameter is required for an intercept call.'
)
}
this.scope = scope
this.interceptorMatchHeaders = []
this.method = method.toUpperCase()
this.uri = uri
this._key = `${this.method} ${scope.basePath}${scope.basePathname}${
uriIsStr ? '' : '/'
}${uri}`
this.basePath = this.scope.basePath
this.path = uriIsStr ? scope.basePathname + uri : uri
this.queries = null
this.options = interceptorOptions || {}
this.counter = 1
this._requestBody = requestBody
// We use lower-case header field names throughout Nock.
this.reqheaders = common.headersFieldNamesToLowerCase(
scope.scopeOptions.reqheaders || {}
)
this.badheaders = common.headersFieldsArrayToLowerCase(
scope.scopeOptions.badheaders || []
)
this.delayInMs = 0
this.delayConnectionInMs = 0
this.optional = false
// strip off literal query parameters if they were provided as part of the URI
if (uriIsStr && uri.includes('?')) {
// localhost is a dummy value because the URL constructor errors for only relative inputs
const parsedURL = new URL(this.path, 'http://localhost')
this.path = parsedURL.pathname
this.query(parsedURL.searchParams)
this._key = `${this.method} ${scope.basePath}${this.path}`
}
}
optionally(flag = true) {
// The default behaviour of optionally() with no arguments is to make the mock optional.
if (typeof flag !== 'boolean') {
throw new Error('Invalid arguments: argument should be a boolean')
}
this.optional = flag
return this
}
replyWithError(errorMessage) {
this.errorMessage = errorMessage
this.options = {
...this.scope.scopeOptions,
...this.options,
}
this.scope.add(this._key, this)
return this.scope
}
reply(statusCode, body, rawHeaders) {
// support the format of only passing in a callback
if (typeof statusCode === 'function') {
if (arguments.length > 1) {
// It's not very Javascript-y to throw an error for extra args to a function, but because
// of legacy behavior, this error was added to reduce confusion for those migrating.
throw Error(
'Invalid arguments. When providing a function for the first argument, .reply does not accept other arguments.'
)
}
this.statusCode = null
this.fullReplyFunction = statusCode
} else {
if (statusCode !== undefined && !Number.isInteger(statusCode)) {
throw new Error(`Invalid ${typeof statusCode} value for status code`)
}
this.statusCode = statusCode || 200
if (typeof body === 'function') {
this.replyFunction = body
body = null
}
}
this.options = {
...this.scope.scopeOptions,
...this.options,
}
this.rawHeaders = common.headersInputToRawArray(rawHeaders)
if (this.scope.date) {
// https://tools.ietf.org/html/rfc7231#section-7.1.1.2
this.rawHeaders.push('Date', this.scope.date.toUTCString())
}
// Prepare the headers temporarily so we can make best guesses about content-encoding and content-type
// below as well as while the response is being processed in RequestOverrider.end().
// Including all the default headers is safe for our purposes because of the specific headers we introspect.
// A more thoughtful process is used to merge the default headers when the response headers are finally computed.
this.headers = common.headersArrayToObject(
this.rawHeaders.concat(this.scope._defaultReplyHeaders)
)
// If the content is not encoded we may need to transform the response body.
// Otherwise we leave it as it is.
if (
body &&
typeof body !== 'string' &&
!Buffer.isBuffer(body) &&
!common.isStream(body) &&
!common.isContentEncoded(this.headers)
) {
try {
body = stringify(body)
} catch (err) {
throw new Error('Error encoding response body into JSON')
}
if (!this.headers['content-type']) {
// https://tools.ietf.org/html/rfc7231#section-3.1.1.5
this.rawHeaders.push('Content-Type', 'application/json')
}
if (this.scope.contentLen) {
// https://tools.ietf.org/html/rfc7230#section-3.3.2
this.rawHeaders.push('Content-Length', body.length)
}
}
debug('reply.headers:', this.headers)
debug('reply.rawHeaders:', this.rawHeaders)
this.body = body
this.scope.add(this._key, this)
return this.scope
}
replyWithFile(statusCode, filePath, headers) {
if (!fs) {
throw new Error('No fs')
}
const readStream = fs.createReadStream(filePath)
readStream.pause()
this.filePath = filePath
return this.reply(statusCode, readStream, headers)
}
// Also match request headers
// https://github.com/nock/nock/issues/163
reqheaderMatches(options, key) {
const reqHeader = this.reqheaders[key]
let header = options.headers[key]
// https://github.com/nock/nock/issues/399
// https://github.com/nock/nock/issues/822
if (header && typeof header !== 'string' && header.toString) {
header = header.toString()
}
// We skip 'host' header comparison unless it's available in both mock and
// actual request. This because 'host' may get inserted by Nock itself and
// then get recorded. NOTE: We use lower-case header field names throughout
// Nock. See https://github.com/nock/nock/pull/196.
if (key === 'host' && (header === undefined || reqHeader === undefined)) {
return true
}
if (reqHeader !== undefined && header !== undefined) {
if (typeof reqHeader === 'function') {
return reqHeader(header)
} else if (common.matchStringOrRegexp(header, reqHeader)) {
return true
}
}
debug("request header field doesn't match:", key, header, reqHeader)
return false
}
match(req, options, body) {
if (debug.enabled) {
debug('match %s, body = %s', stringify(options), stringify(body))
}
const method = (options.method || 'GET').toUpperCase()
let { path = '/' } = options
let matches
let matchKey
const { proto } = options
if (this.method !== method) {
debug(
`Method did not match. Request ${method} Interceptor ${this.method}`
)
return false
}
if (this.scope.transformPathFunction) {
path = this.scope.transformPathFunction(path)
}
const requestMatchesFilter = ({ name, value: predicate }) => {
const headerValue = req.getHeader(name)
if (typeof predicate === 'function') {
return predicate(headerValue)
} else {
return common.matchStringOrRegexp(headerValue, predicate)
}
}
if (
!this.scope.matchHeaders.every(requestMatchesFilter) ||
!this.interceptorMatchHeaders.every(requestMatchesFilter)
) {
this.scope.logger("headers don't match")
return false
}
const reqHeadersMatch = Object.keys(this.reqheaders).every(key =>
this.reqheaderMatches(options, key)
)
if (!reqHeadersMatch) {
return false
}
if (
this.scope.scopeOptions.conditionally &&
!this.scope.scopeOptions.conditionally()
) {
return false
}
const reqContainsBadHeaders = this.badheaders.some(
header => header in options.headers
)
if (reqContainsBadHeaders) {
return false
}
// Match query strings when using query()
if (this.queries === null) {
debug('query matching skipped')
} else {
// can't rely on pathname or search being in the options, but path has a default
const [pathname, search] = path.split('?')
const matchQueries = this.matchQuery({ search })
debug(matchQueries ? 'query matching succeeded' : 'query matching failed')
if (!matchQueries) {
return false
}
// If the query string was explicitly checked then subsequent checks against
// the path using a callback or regexp only validate the pathname.
path = pathname
}
// If we have a filtered scope then we use it instead reconstructing the
// scope from the request options (proto, host and port) as these two won't
// necessarily match and we have to remove the scope that was matched (vs.
// that was defined).
if (this.__nock_filteredScope) {
matchKey = this.__nock_filteredScope
} else {
matchKey = common.normalizeOrigin(proto, options.host, options.port)
}
if (typeof this.uri === 'function') {
matches =
common.matchStringOrRegexp(matchKey, this.basePath) &&
// This is a false positive, as `uri` is not bound to `this`.
// eslint-disable-next-line no-useless-call
this.uri.call(this, path)
} else {
matches =
common.matchStringOrRegexp(matchKey, this.basePath) &&
common.matchStringOrRegexp(path, this.path)
}
this.scope.logger(`matching ${matchKey}${path} to ${this._key}: ${matches}`)
if (matches && this._requestBody !== undefined) {
if (this.scope.transformRequestBodyFunction) {
body = this.scope.transformRequestBodyFunction(body, this._requestBody)
}
matches = matchBody(options, this._requestBody, body)
if (!matches) {
this.scope.logger(
"bodies don't match: \n",
this._requestBody,
'\n',
body
)
}
}
return matches
}
/**
* Return true when the interceptor's method, protocol, host, port, and path
* match the provided options.
*/
matchOrigin(options) {
const isPathFn = typeof this.path === 'function'
const isRegex = this.path instanceof RegExp
const isRegexBasePath = this.scope.basePath instanceof RegExp
const method = (options.method || 'GET').toUpperCase()
let { path } = options
const { proto } = options
// NOTE: Do not split off the query params as the regex could use them
if (!isRegex) {
path = path ? path.split('?')[0] : ''
}
if (this.scope.transformPathFunction) {
path = this.scope.transformPathFunction(path)
}
const comparisonKey = isPathFn || isRegex ? this.__nock_scopeKey : this._key
const matchKey = `${method} ${proto}://${options.host}${path}`
if (isPathFn) {
return !!(matchKey.match(comparisonKey) && this.path(path))
}
if (isRegex && !isRegexBasePath) {
return !!matchKey.match(comparisonKey) && this.path.test(path)
}
if (isRegexBasePath) {
return this.scope.basePath.test(matchKey) && !!path.match(this.path)
}
return comparisonKey === matchKey
}
matchHostName(options) {
return options.hostname === this.scope.urlParts.hostname
}
matchQuery(options) {
if (this.queries === true) {
return true
}
const reqQueries = querystring.parse(options.search)
debug('Interceptor queries: %j', this.queries)
debug(' Request queries: %j', reqQueries)
if (typeof this.queries === 'function') {
return this.queries(reqQueries)
}
return common.dataEqual(this.queries, reqQueries)
}
filteringPath(...args) {
this.scope.filteringPath(...args)
return this
}
// TODO filtering by path is valid on the intercept level, but not filtering
// by request body?
markConsumed() {
this.interceptionCounter++
remove(this)
if ((this.scope.shouldPersist() || this.counter > 0) && this.filePath) {
this.body = fs.createReadStream(this.filePath)
this.body.pause()
}
if (!this.scope.shouldPersist() && this.counter < 1) {
this.scope.remove(this._key, this)
}
}
matchHeader(name, value) {
this.interceptorMatchHeaders.push({ name, value })
return this
}
basicAuth({ user, pass = '' }) {
const encoded = Buffer.from(`${user}:${pass}`).toString('base64')
this.matchHeader('authorization', `Basic ${encoded}`)
return this
}
/**
* Set query strings for the interceptor
* @name query
* @param queries Object of query string name,values (accepts regexp values)
* @public
* @example
* // Will match 'http://zombo.com/?q=t'
* nock('http://zombo.com').get('/').query({q: 't'});
*/
query(queries) {
if (this.queries !== null) {
throw Error(`Query parameters have already been defined`)
}
// Allow all query strings to match this route
if (queries === true) {
this.queries = queries
return this
}
if (typeof queries === 'function') {
this.queries = queries
return this
}
let strFormattingFn
if (this.scope.scopeOptions.encodedQueryParams) {
strFormattingFn = common.percentDecode
}
if (queries instanceof URLSearchParams) {
// Normalize the data into the shape that is matched against.
// Duplicate keys are handled by combining the values into an array.
queries = querystring.parse(queries.toString())
} else if (!_.isPlainObject(queries)) {
throw Error(`Argument Error: ${queries}`)
}
this.queries = {}
for (const [key, value] of Object.entries(queries)) {
const formatted = common.formatQueryValue(key, value, strFormattingFn)
const [formattedKey, formattedValue] = formatted
this.queries[formattedKey] = formattedValue
}
return this
}
/**
* Set number of times will repeat the interceptor
* @name times
* @param newCounter Number of times to repeat (should be > 0)
* @public
* @example
* // Will repeat mock 5 times for same king of request
* nock('http://zombo.com).get('/').times(5).reply(200, 'Ok');
*/
times(newCounter) {
if (newCounter < 1) {
return this
}
this.counter = newCounter
return this
}
/**
* An sugar syntax for times(1)
* @name once
* @see {@link times}
* @public
* @example
* nock('http://zombo.com).get('/').once().reply(200, 'Ok');
*/
once() {
return this.times(1)
}
/**
* An sugar syntax for times(2)
* @name twice
* @see {@link times}
* @public
* @example
* nock('http://zombo.com).get('/').twice().reply(200, 'Ok');
*/
twice() {
return this.times(2)
}
/**
* An sugar syntax for times(3).
* @name thrice
* @see {@link times}
* @public
* @example
* nock('http://zombo.com).get('/').thrice().reply(200, 'Ok');
*/
thrice() {
return this.times(3)
}
/**
* Delay the response by a certain number of ms.
*
* @param {(integer|object)} opts - Number of milliseconds to wait, or an object
* @param {integer} [opts.head] - Number of milliseconds to wait before response is sent
* @param {integer} [opts.body] - Number of milliseconds to wait before response body is sent
* @return {Interceptor} - the current interceptor for chaining
*/
delay(opts) {
let headDelay
let bodyDelay
if (typeof opts === 'number') {
headDelay = opts
bodyDelay = 0
} else if (typeof opts === 'object') {
headDelay = opts.head || 0
bodyDelay = opts.body || 0
} else {
throw new Error(`Unexpected input opts ${opts}`)
}
return this.delayConnection(headDelay).delayBody(bodyDelay)
}
/**
* Delay the response body by a certain number of ms.
*
* @param {integer} ms - Number of milliseconds to wait before response is sent
* @return {Interceptor} - the current interceptor for chaining
*/
delayBody(ms) {
this.delayInMs += ms
return this
}
/**
* Delay the connection by a certain number of ms.
*
* @param {integer} ms - Number of milliseconds to wait
* @return {Interceptor} - the current interceptor for chaining
*/
delayConnection(ms) {
this.delayConnectionInMs += ms
return this
}
/**
* @private
* @returns {number}
*/
getTotalDelay() {
return this.delayInMs + this.delayConnectionInMs
}
/**
* Make the socket idle for a certain number of ms (simulated).
*
* @param {integer} ms - Number of milliseconds to wait
* @return {Interceptor} - the current interceptor for chaining
*/
socketDelay(ms) {
this.socketDelayInMs = ms
return this
}
}

77
node_modules/nock/lib/match_body.js generated vendored Normal file
View file

@ -0,0 +1,77 @@
'use strict'
const _ = require('lodash')
const querystring = require('querystring')
const common = require('./common')
module.exports = function matchBody(options, spec, body) {
if (spec instanceof RegExp) {
return spec.test(body)
}
if (Buffer.isBuffer(spec)) {
const encoding = common.isUtf8Representable(spec) ? 'utf8' : 'hex'
spec = spec.toString(encoding)
}
const contentType = (
(options.headers &&
(options.headers['Content-Type'] || options.headers['content-type'])) ||
''
).toString()
const isMultipart = contentType.includes('multipart')
const isUrlencoded = contentType.includes('application/x-www-form-urlencoded')
// try to transform body to json
let json
if (typeof spec === 'object' || typeof spec === 'function') {
try {
json = JSON.parse(body)
} catch (err) {
// not a valid JSON string
}
if (json !== undefined) {
body = json
} else if (isUrlencoded) {
body = querystring.parse(body)
}
}
if (typeof spec === 'function') {
return spec.call(options, body)
}
// strip line endings from both so that we get a match no matter what OS we are running on
// if Content-Type does not contains 'multipart'
if (!isMultipart && typeof body === 'string') {
body = body.replace(/\r?\n|\r/g, '')
}
if (!isMultipart && typeof spec === 'string') {
spec = spec.replace(/\r?\n|\r/g, '')
}
// Because the nature of URL encoding, all the values in the body have been cast to strings.
// dataEqual does strict checking so we we have to cast the non-regexp values in the spec too.
if (isUrlencoded) {
spec = mapValuesDeep(spec, val => (val instanceof RegExp ? val : `${val}`))
}
return common.dataEqual(spec, body)
}
/**
* Based on lodash issue discussion
* https://github.com/lodash/lodash/issues/1244
*/
function mapValuesDeep(obj, cb) {
if (Array.isArray(obj)) {
return obj.map(v => mapValuesDeep(v, cb))
}
if (_.isPlainObject(obj)) {
return _.mapValues(obj, v => mapValuesDeep(v, cb))
}
return cb(obj)
}

348
node_modules/nock/lib/playback_interceptor.js generated vendored Normal file
View file

@ -0,0 +1,348 @@
'use strict'
const util = require('util')
const zlib = require('zlib')
const debug = require('debug')('nock.playback_interceptor')
const common = require('./common')
const DelayedBody = require('./delayed_body')
function parseJSONRequestBody(req, requestBody) {
if (!requestBody || !common.isJSONContent(req.headers)) {
return requestBody
}
if (common.contentEncoding(req.headers, 'gzip')) {
requestBody = String(zlib.gunzipSync(Buffer.from(requestBody, 'hex')))
} else if (common.contentEncoding(req.headers, 'deflate')) {
requestBody = String(zlib.inflateSync(Buffer.from(requestBody, 'hex')))
}
return JSON.parse(requestBody)
}
function parseFullReplyResult(response, fullReplyResult) {
debug('full response from callback result: %j', fullReplyResult)
if (!Array.isArray(fullReplyResult)) {
throw Error('A single function provided to .reply MUST return an array')
}
if (fullReplyResult.length > 3) {
throw Error(
'The array returned from the .reply callback contains too many values'
)
}
const [status, body = '', headers] = fullReplyResult
if (!Number.isInteger(status)) {
throw new Error(`Invalid ${typeof status} value for status code`)
}
response.statusCode = status
response.rawHeaders.push(...common.headersInputToRawArray(headers))
debug('response.rawHeaders after reply: %j', response.rawHeaders)
return body
}
/**
* Determine which of the default headers should be added to the response.
*
* Don't include any defaults whose case-insensitive keys are already on the response.
*/
function selectDefaultHeaders(existingHeaders, defaultHeaders) {
if (!defaultHeaders.length) {
return [] // return early if we don't need to bother
}
const definedHeaders = new Set()
const result = []
common.forEachHeader(existingHeaders, (_, fieldName) => {
definedHeaders.add(fieldName.toLowerCase())
})
common.forEachHeader(defaultHeaders, (value, fieldName) => {
if (!definedHeaders.has(fieldName.toLowerCase())) {
result.push(fieldName, value)
}
})
return result
}
/**
* Play back an interceptor using the given request and mock response.
*/
function playbackInterceptor({
req,
socket,
options,
requestBodyString,
requestBodyIsUtf8Representable,
response,
interceptor,
}) {
function emitError(error) {
process.nextTick(() => {
req.emit('error', error)
})
}
function start() {
interceptor.req = req
req.headers = req.getHeaders()
interceptor.scope.emit('request', req, interceptor, requestBodyString)
if (typeof interceptor.errorMessage !== 'undefined') {
interceptor.markConsumed()
let error
if (typeof interceptor.errorMessage === 'object') {
error = interceptor.errorMessage
} else {
error = new Error(interceptor.errorMessage)
}
common.setTimeout(() => emitError(error), interceptor.getTotalDelay())
return
}
// This will be null if we have a fullReplyFunction,
// in that case status code will be set in `parseFullReplyResult`
response.statusCode = interceptor.statusCode
// Clone headers/rawHeaders to not override them when evaluating later
response.rawHeaders = [...interceptor.rawHeaders]
debug('response.rawHeaders:', response.rawHeaders)
if (interceptor.replyFunction) {
const parsedRequestBody = parseJSONRequestBody(req, requestBodyString)
let fn = interceptor.replyFunction
if (fn.length === 3) {
// Handle the case of an async reply function, the third parameter being the callback.
fn = util.promisify(fn)
}
// At this point `fn` is either a synchronous function or a promise-returning function;
// wrapping in `Promise.resolve` makes it into a promise either way.
Promise.resolve(fn.call(interceptor, options.path, parsedRequestBody))
.then(responseBody => continueWithResponseBody({ responseBody }))
.catch(err => emitError(err))
return
}
if (interceptor.fullReplyFunction) {
const parsedRequestBody = parseJSONRequestBody(req, requestBodyString)
let fn = interceptor.fullReplyFunction
if (fn.length === 3) {
fn = util.promisify(fn)
}
Promise.resolve(fn.call(interceptor, options.path, parsedRequestBody))
.then(fullReplyResult => continueWithFullResponse({ fullReplyResult }))
.catch(err => emitError(err))
return
}
if (
common.isContentEncoded(interceptor.headers) &&
!common.isStream(interceptor.body)
) {
// If the content is encoded we know that the response body *must* be an array
// of response buffers which should be mocked one by one.
// (otherwise decompressions after the first one fails as unzip expects to receive
// buffer by buffer and not one single merged buffer)
if (interceptor.delayInMs) {
emitError(
new Error(
'Response delay of the body is currently not supported with content-encoded responses.'
)
)
return
}
const bufferData = Array.isArray(interceptor.body)
? interceptor.body
: [interceptor.body]
const responseBuffers = bufferData.map(data => Buffer.from(data, 'hex'))
continueWithResponseBody({ responseBuffers })
return
}
// If we get to this point, the body is either a string or an object that
// will eventually be JSON stringified.
let responseBody = interceptor.body
// If the request was not UTF8-representable then we assume that the
// response won't be either. In that case we send the response as a Buffer
// object as that's what the client will expect.
if (!requestBodyIsUtf8Representable && typeof responseBody === 'string') {
// Try to create the buffer from the interceptor's body response as hex.
responseBody = Buffer.from(responseBody, 'hex')
// Creating buffers does not necessarily throw errors; check for difference in size.
if (
!responseBody ||
(interceptor.body.length > 0 && responseBody.length === 0)
) {
// We fallback on constructing buffer from utf8 representation of the body.
responseBody = Buffer.from(interceptor.body, 'utf8')
}
}
return continueWithResponseBody({ responseBody })
}
function continueWithFullResponse({ fullReplyResult }) {
let responseBody
try {
responseBody = parseFullReplyResult(response, fullReplyResult)
} catch (innerErr) {
emitError(innerErr)
return
}
continueWithResponseBody({ responseBody })
}
function continueWithResponseBody({ responseBuffers, responseBody }) {
// Transform the response body if it exists (it may not exist
// if we have `responseBuffers` instead)
if (responseBody !== undefined) {
debug('transform the response body')
if (interceptor.delayInMs) {
debug(
'delaying the response for',
interceptor.delayInMs,
'milliseconds'
)
// Because setTimeout is called immediately in DelayedBody(), so we
// need count in the delayConnectionInMs.
responseBody = new DelayedBody(
interceptor.getTotalDelay(),
responseBody
)
}
if (common.isStream(responseBody)) {
debug('response body is a stream')
responseBody.pause()
responseBody.on('data', function(d) {
response.push(d)
})
responseBody.on('end', function() {
response.push(null)
// https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_message_complete
response.complete = true
})
responseBody.on('error', function(err) {
response.emit('error', err)
})
} else if (!Buffer.isBuffer(responseBody)) {
if (typeof responseBody === 'string') {
responseBody = Buffer.from(responseBody)
} else {
responseBody = JSON.stringify(responseBody)
response.rawHeaders.push('Content-Type', 'application/json')
}
}
// Why are strings converted to a Buffer, but JSON data is left as a string?
// Related to https://github.com/nock/nock/issues/1542 ?
}
interceptor.markConsumed()
if (req.aborted) {
return
}
response.rawHeaders.push(
...selectDefaultHeaders(
response.rawHeaders,
interceptor.scope._defaultReplyHeaders
)
)
// Evaluate functional headers.
common.forEachHeader(response.rawHeaders, (value, fieldName, i) => {
if (typeof value === 'function') {
response.rawHeaders[i + 1] = value(req, response, responseBody)
}
})
response.headers = common.headersArrayToObject(response.rawHeaders)
process.nextTick(() =>
respondUsingInterceptor({
responseBody,
responseBuffers,
})
)
}
function respondUsingInterceptor({ responseBody, responseBuffers }) {
if (req.aborted) {
return
}
function respond() {
if (req.aborted) {
return
}
debug('emitting response')
req.emit('response', response)
if (common.isStream(responseBody)) {
debug('resuming response stream')
responseBody.resume()
} else {
responseBuffers = responseBuffers || []
if (typeof responseBody !== 'undefined') {
debug('adding body to buffer list')
responseBuffers.push(responseBody)
}
// Stream the response chunks one at a time.
common.setImmediate(function emitChunk() {
const chunk = responseBuffers.shift()
if (chunk) {
debug('emitting response chunk')
response.push(chunk)
common.setImmediate(emitChunk)
} else {
debug('ending response stream')
response.push(null)
// https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_message_complete
response.complete = true
interceptor.scope.emit('replied', req, interceptor)
}
})
}
}
if (interceptor.socketDelayInMs && interceptor.socketDelayInMs > 0) {
socket.applyDelay(interceptor.socketDelayInMs)
}
if (
interceptor.delayConnectionInMs &&
interceptor.delayConnectionInMs > 0
) {
socket.applyDelay(interceptor.delayConnectionInMs)
common.setTimeout(respond, interceptor.delayConnectionInMs)
} else {
respond()
}
}
start()
}
module.exports = { playbackInterceptor }

386
node_modules/nock/lib/recorder.js generated vendored Normal file
View file

@ -0,0 +1,386 @@
'use strict'
const debug = require('debug')('nock.recorder')
const querystring = require('querystring')
const { inspect } = require('util')
const common = require('./common')
const { restoreOverriddenClientRequest } = require('./intercept')
const SEPARATOR = '\n<<<<<<-- cut here -->>>>>>\n'
let recordingInProgress = false
let outputs = []
function getScope(options) {
const { proto, host, port } = common.normalizeRequestOptions(options)
return common.normalizeOrigin(proto, host, port)
}
function getMethod(options) {
return options.method || 'GET'
}
function getBodyFromChunks(chunks, headers) {
// If we have headers and there is content-encoding it means that the body
// shouldn't be merged but instead persisted as an array of hex strings so
// that the response chunks can be mocked one by one.
if (headers && common.isContentEncoded(headers)) {
return {
body: chunks.map(chunk => chunk.toString('hex')),
}
}
const mergedBuffer = Buffer.concat(chunks)
// The merged buffer can be one of three things:
// 1. A UTF-8-representable string buffer which represents a JSON object.
// 2. A UTF-8-representable buffer which doesn't represent a JSON object.
// 3. A non-UTF-8-representable buffer which then has to be recorded as a hex string.
const isUtf8Representable = common.isUtf8Representable(mergedBuffer)
if (isUtf8Representable) {
const maybeStringifiedJson = mergedBuffer.toString('utf8')
try {
return {
isUtf8Representable,
body: JSON.parse(maybeStringifiedJson),
}
} catch (err) {
return {
isUtf8Representable,
body: maybeStringifiedJson,
}
}
} else {
return {
isUtf8Representable,
body: mergedBuffer.toString('hex'),
}
}
}
function generateRequestAndResponseObject({
req,
bodyChunks,
options,
res,
dataChunks,
reqheaders,
}) {
const { body, isUtf8Representable } = getBodyFromChunks(
dataChunks,
res.headers
)
options.path = req.path
return {
scope: getScope(options),
method: getMethod(options),
path: options.path,
// Is it deliberate that `getBodyFromChunks()` is called a second time?
body: getBodyFromChunks(bodyChunks).body,
status: res.statusCode,
response: body,
rawHeaders: res.rawHeaders,
reqheaders: reqheaders || undefined,
// When content-encoding is enabled, isUtf8Representable is `undefined`,
// so we explicitly check for `false`.
responseIsBinary: isUtf8Representable === false,
}
}
function generateRequestAndResponse({
req,
bodyChunks,
options,
res,
dataChunks,
reqheaders,
}) {
const requestBody = getBodyFromChunks(bodyChunks).body
const responseBody = getBodyFromChunks(dataChunks, res.headers).body
// Remove any query params from options.path so they can be added in the query() function
let { path } = options
const queryIndex = req.path.indexOf('?')
let queryObj = {}
if (queryIndex !== -1) {
// Remove the query from the path
path = path.substring(0, queryIndex)
const queryStr = req.path.slice(queryIndex + 1)
queryObj = querystring.parse(queryStr)
}
// Always encode the query parameters when recording.
const encodedQueryObj = {}
for (const key in queryObj) {
const formattedPair = common.formatQueryValue(
key,
queryObj[key],
common.percentEncode
)
encodedQueryObj[formattedPair[0]] = formattedPair[1]
}
const lines = []
// We want a leading newline.
lines.push('')
const scope = getScope(options)
lines.push(`nock('${scope}', {"encodedQueryParams":true})`)
const methodName = getMethod(options).toLowerCase()
if (requestBody) {
lines.push(` .${methodName}('${path}', ${JSON.stringify(requestBody)})`)
} else {
lines.push(` .${methodName}('${path}')`)
}
Object.entries(reqheaders || {}).forEach(([fieldName, fieldValue]) => {
const safeName = JSON.stringify(fieldName)
const safeValue = JSON.stringify(fieldValue)
lines.push(` .matchHeader(${safeName}, ${safeValue})`)
})
if (queryIndex !== -1) {
lines.push(` .query(${JSON.stringify(encodedQueryObj)})`)
}
const statusCode = res.statusCode.toString()
const stringifiedResponseBody = JSON.stringify(responseBody)
const headers = inspect(res.rawHeaders)
lines.push(` .reply(${statusCode}, ${stringifiedResponseBody}, ${headers});`)
return lines.join('\n')
}
// This module variable is used to identify a unique recording ID in order to skip
// spurious requests that sometimes happen. This problem has been, so far,
// exclusively detected in nock's unit testing where 'checks if callback is specified'
// interferes with other tests as its t.end() is invoked without waiting for request
// to finish (which is the point of the test).
let currentRecordingId = 0
const defaultRecordOptions = {
dont_print: false,
enable_reqheaders_recording: false,
logging: console.log,
output_objects: false,
use_separator: true,
}
function record(recOptions) {
// Trying to start recording with recording already in progress implies an error
// in the recording configuration (double recording makes no sense and used to lead
// to duplicates in output)
if (recordingInProgress) {
throw new Error('Nock recording already in progress')
}
recordingInProgress = true
// Set the new current recording ID and capture its value in this instance of record().
currentRecordingId = currentRecordingId + 1
const thisRecordingId = currentRecordingId
// Originally the parameter was a dont_print boolean flag.
// To keep the existing code compatible we take that case into account.
if (typeof recOptions === 'boolean') {
recOptions = { dont_print: recOptions }
}
recOptions = { ...defaultRecordOptions, ...recOptions }
debug('start recording', thisRecordingId, recOptions)
const {
dont_print: dontPrint,
enable_reqheaders_recording: enableReqHeadersRecording,
logging,
output_objects: outputObjects,
use_separator: useSeparator,
} = recOptions
debug(thisRecordingId, 'restoring overridden requests before new overrides')
// To preserve backward compatibility (starting recording wasn't throwing if nock was already active)
// we restore any requests that may have been overridden by other parts of nock (e.g. intercept)
// NOTE: This is hacky as hell but it keeps the backward compatibility *and* allows correct
// behavior in the face of other modules also overriding ClientRequest.
common.restoreOverriddenRequests()
// We restore ClientRequest as it messes with recording of modules that also override ClientRequest (e.g. xhr2)
restoreOverriddenClientRequest()
// We override the requests so that we can save information on them before executing.
common.overrideRequests(function(proto, overriddenRequest, rawArgs) {
const { options, callback } = common.normalizeClientRequestArgs(...rawArgs)
const bodyChunks = []
// Node 0.11 https.request calls http.request -- don't want to record things
// twice.
/* istanbul ignore if */
if (options._recording) {
return overriddenRequest(options, callback)
}
options._recording = true
const req = overriddenRequest(options, function(res) {
debug(thisRecordingId, 'intercepting', proto, 'request to record')
// We put our 'end' listener to the front of the listener array.
res.once('end', function() {
debug(thisRecordingId, proto, 'intercepted request ended')
let reqheaders
// Ignore request headers completely unless it was explicitly enabled by the user (see README)
if (enableReqHeadersRecording) {
// We never record user-agent headers as they are worse than useless -
// they actually make testing more difficult without providing any benefit (see README)
reqheaders = req.getHeaders()
common.deleteHeadersField(reqheaders, 'user-agent')
}
const generateFn = outputObjects
? generateRequestAndResponseObject
: generateRequestAndResponse
let out = generateFn({
req,
bodyChunks,
options,
res,
dataChunks,
reqheaders,
})
debug('out:', out)
// Check that the request was made during the current recording.
// If it hasn't then skip it. There is no other simple way to handle
// this as it depends on the timing of requests and responses. Throwing
// will make some recordings/unit tests fail randomly depending on how
// fast/slow the response arrived.
// If you are seeing this error then you need to make sure that all
// the requests made during a single recording session finish before
// ending the same recording session.
if (thisRecordingId !== currentRecordingId) {
debug('skipping recording of an out-of-order request', out)
return
}
outputs.push(out)
if (!dontPrint) {
if (useSeparator) {
if (typeof out !== 'string') {
out = JSON.stringify(out, null, 2)
}
logging(SEPARATOR + out + SEPARATOR)
} else {
logging(out)
}
}
})
let encoding
// We need to be aware of changes to the stream's encoding so that we
// don't accidentally mangle the data.
const { setEncoding } = res
res.setEncoding = function(newEncoding) {
encoding = newEncoding
return setEncoding.apply(this, arguments)
}
const dataChunks = []
// Replace res.push with our own implementation that stores chunks
const origResPush = res.push
res.push = function(data) {
if (data) {
if (encoding) {
data = Buffer.from(data, encoding)
}
dataChunks.push(data)
}
return origResPush.call(res, data)
}
if (callback) {
callback(res, options, callback)
} else {
res.resume()
}
debug('finished setting up intercepting')
// We override both the http and the https modules; when we are
// serializing the request, we need to know which was called.
// By stuffing the state, we can make sure that nock records
// the intended protocol.
if (proto === 'https') {
options.proto = 'https'
}
})
const recordChunk = (chunk, encoding) => {
debug(thisRecordingId, 'new', proto, 'body chunk')
if (!Buffer.isBuffer(chunk)) {
chunk = Buffer.from(chunk, encoding)
}
bodyChunks.push(chunk)
}
const oldWrite = req.write
req.write = function(chunk, encoding) {
if (typeof chunk !== 'undefined') {
recordChunk(chunk, encoding)
oldWrite.apply(req, arguments)
} else {
throw new Error('Data was undefined.')
}
}
// Starting in Node 8, `OutgoingMessage.end()` directly calls an internal
// `write_` function instead of proxying to the public
// `OutgoingMessage.write()` method, so we have to wrap `end` too.
const oldEnd = req.end
req.end = function(chunk, encoding, callback) {
debug('req.end')
if (typeof chunk === 'function') {
callback = chunk
chunk = null
} else if (typeof encoding === 'function') {
callback = encoding
encoding = null
}
if (chunk) {
recordChunk(chunk, encoding)
}
oldEnd.call(req, chunk, encoding, callback)
}
return req
})
}
// Restore *all* the overridden http/https modules' properties.
function restore() {
debug(
currentRecordingId,
'restoring all the overridden http/https properties'
)
common.restoreOverriddenRequests()
restoreOverriddenClientRequest()
recordingInProgress = false
}
function clear() {
outputs = []
}
module.exports = {
record,
outputs: () => outputs,
restore,
clear,
}

391
node_modules/nock/lib/scope.js generated vendored Normal file
View file

@ -0,0 +1,391 @@
'use strict'
/**
* @module nock/scope
*/
const { addInterceptor, isOn } = require('./intercept')
const common = require('./common')
const assert = require('assert')
const url = require('url')
const debug = require('debug')('nock.scope')
const { EventEmitter } = require('events')
const util = require('util')
const Interceptor = require('./interceptor')
let fs
try {
fs = require('fs')
} catch (err) {
// do nothing, we're in the browser
}
/**
* @param {string|RegExp|url.url} basePath
* @param {Object} options
* @param {boolean} options.allowUnmocked
* @param {string[]} options.badheaders
* @param {function} options.conditionally
* @param {boolean} options.encodedQueryParams
* @param {function} options.filteringScope
* @param {Object} options.reqheaders
* @constructor
*/
class Scope extends EventEmitter {
constructor(basePath, options) {
super()
this.keyedInterceptors = {}
this.interceptors = []
this.transformPathFunction = null
this.transformRequestBodyFunction = null
this.matchHeaders = []
this.logger = debug
this.scopeOptions = options || {}
this.urlParts = {}
this._persist = false
this.contentLen = false
this.date = null
this.basePath = basePath
this.basePathname = ''
this.port = null
this._defaultReplyHeaders = []
if (!(basePath instanceof RegExp)) {
this.urlParts = url.parse(basePath)
this.port =
this.urlParts.port || (this.urlParts.protocol === 'http:' ? 80 : 443)
this.basePathname = this.urlParts.pathname.replace(/\/$/, '')
this.basePath = `${this.urlParts.protocol}//${this.urlParts.hostname}:${this.port}`
}
}
add(key, interceptor) {
if (!(key in this.keyedInterceptors)) {
this.keyedInterceptors[key] = []
}
this.keyedInterceptors[key].push(interceptor)
addInterceptor(
this.basePath,
interceptor,
this,
this.scopeOptions,
this.urlParts.hostname
)
}
remove(key, interceptor) {
if (this._persist) {
return
}
const arr = this.keyedInterceptors[key]
if (arr) {
arr.splice(arr.indexOf(interceptor), 1)
if (arr.length === 0) {
delete this.keyedInterceptors[key]
}
}
}
intercept(uri, method, requestBody, interceptorOptions) {
const ic = new Interceptor(
this,
uri,
method,
requestBody,
interceptorOptions
)
this.interceptors.push(ic)
return ic
}
get(uri, requestBody, options) {
return this.intercept(uri, 'GET', requestBody, options)
}
post(uri, requestBody, options) {
return this.intercept(uri, 'POST', requestBody, options)
}
put(uri, requestBody, options) {
return this.intercept(uri, 'PUT', requestBody, options)
}
head(uri, requestBody, options) {
return this.intercept(uri, 'HEAD', requestBody, options)
}
patch(uri, requestBody, options) {
return this.intercept(uri, 'PATCH', requestBody, options)
}
merge(uri, requestBody, options) {
return this.intercept(uri, 'MERGE', requestBody, options)
}
delete(uri, requestBody, options) {
return this.intercept(uri, 'DELETE', requestBody, options)
}
options(uri, requestBody, options) {
return this.intercept(uri, 'OPTIONS', requestBody, options)
}
// Returns the list of keys for non-optional Interceptors that haven't been completed yet.
// TODO: This assumes that completed mocks are removed from the keyedInterceptors list
// (when persistence is off). We should change that (and this) in future.
pendingMocks() {
return this.activeMocks().filter(key =>
this.keyedInterceptors[key].some(({ interceptionCounter, optional }) => {
const persistedAndUsed = this._persist && interceptionCounter > 0
return !persistedAndUsed && !optional
})
)
}
// Returns all keyedInterceptors that are active.
// This includes incomplete interceptors, persisted but complete interceptors, and
// optional interceptors, but not non-persisted and completed interceptors.
activeMocks() {
return Object.keys(this.keyedInterceptors)
}
isDone() {
if (!isOn()) {
return true
}
return this.pendingMocks().length === 0
}
done() {
assert.ok(
this.isDone(),
`Mocks not yet satisfied:\n${this.pendingMocks().join('\n')}`
)
}
buildFilter() {
const filteringArguments = arguments
if (arguments[0] instanceof RegExp) {
return function(candidate) {
/* istanbul ignore if */
if (typeof candidate !== 'string') {
// Given the way nock is written, it seems like `candidate` will always
// be a string, regardless of what options might be passed to it.
// However the code used to contain a truthiness test of `candidate`.
// The check is being preserved for now.
throw Error(
`Nock internal assertion failed: typeof candidate is ${typeof candidate}. If you encounter this error, please report it as a bug.`
)
}
return candidate.replace(filteringArguments[0], filteringArguments[1])
}
} else if (typeof arguments[0] === 'function') {
return arguments[0]
}
}
filteringPath() {
this.transformPathFunction = this.buildFilter.apply(this, arguments)
if (!this.transformPathFunction) {
throw new Error(
'Invalid arguments: filtering path should be a function or a regular expression'
)
}
return this
}
filteringRequestBody() {
this.transformRequestBodyFunction = this.buildFilter.apply(this, arguments)
if (!this.transformRequestBodyFunction) {
throw new Error(
'Invalid arguments: filtering request body should be a function or a regular expression'
)
}
return this
}
matchHeader(name, value) {
// We use lower-case header field names throughout Nock.
this.matchHeaders.push({ name: name.toLowerCase(), value })
return this
}
defaultReplyHeaders(headers) {
this._defaultReplyHeaders = common.headersInputToRawArray(headers)
return this
}
log(newLogger) {
this.logger = newLogger
return this
}
persist(flag = true) {
if (typeof flag !== 'boolean') {
throw new Error('Invalid arguments: argument should be a boolean')
}
this._persist = flag
return this
}
/**
* @private
* @returns {boolean}
*/
shouldPersist() {
return this._persist
}
replyContentLength() {
this.contentLen = true
return this
}
replyDate(d) {
this.date = d || new Date()
return this
}
}
function loadDefs(path) {
if (!fs) {
throw new Error('No fs')
}
const contents = fs.readFileSync(path)
return JSON.parse(contents)
}
function load(path) {
return define(loadDefs(path))
}
function getStatusFromDefinition(nockDef) {
// Backward compatibility for when `status` was encoded as string in `reply`.
if (nockDef.reply !== undefined) {
const parsedReply = parseInt(nockDef.reply, 10)
if (isNaN(parsedReply)) {
throw Error('`reply`, when present, must be a numeric string')
}
return parsedReply
}
const DEFAULT_STATUS_OK = 200
return nockDef.status || DEFAULT_STATUS_OK
}
function getScopeFromDefinition(nockDef) {
// Backward compatibility for when `port` was part of definition.
if (nockDef.port !== undefined) {
// Include `port` into scope if it doesn't exist.
const options = url.parse(nockDef.scope)
if (options.port === null) {
return `${nockDef.scope}:${nockDef.port}`
} else {
if (parseInt(options.port) !== parseInt(nockDef.port)) {
throw new Error(
'Mismatched port numbers in scope and port properties of nock definition.'
)
}
}
}
return nockDef.scope
}
function tryJsonParse(string) {
try {
return JSON.parse(string)
} catch (err) {
return string
}
}
// Use a noop deprecate util instead calling emitWarning directly so we get --no-deprecation and single warning behavior for free.
const emitAsteriskDeprecation = util.deprecate(
() => {},
'Skipping body matching using "*" is deprecated. Set the definition body to undefined instead.',
'NOCK1579'
)
function define(nockDefs) {
const scopes = []
nockDefs.forEach(function(nockDef) {
const nscope = getScopeFromDefinition(nockDef)
const npath = nockDef.path
if (!nockDef.method) {
throw Error('Method is required')
}
const method = nockDef.method.toLowerCase()
const status = getStatusFromDefinition(nockDef)
const rawHeaders = nockDef.rawHeaders || []
const reqheaders = nockDef.reqheaders || {}
const badheaders = nockDef.badheaders || []
const options = { ...nockDef.options }
// We use request headers for both filtering (see below) and mocking.
// Here we are setting up mocked request headers but we don't want to
// be changing the user's options object so we clone it first.
options.reqheaders = reqheaders
options.badheaders = badheaders
let { body } = nockDef
if (body === '*') {
// In previous versions, it was impossible to NOT filter on request bodies. This special value
// is sniffed out for users manipulating the definitions and not wanting to match on the
// request body. For newer versions, users should remove the `body` key or set to `undefined`
// to achieve the same affect. Maintaining legacy behavior for now.
emitAsteriskDeprecation()
body = undefined
}
// Response is not always JSON as it could be a string or binary data or
// even an array of binary buffers (e.g. when content is encoded).
let response
if (!nockDef.response) {
response = ''
// TODO: Rename `responseIsBinary` to `reponseIsUtf8Representable`.
} else if (nockDef.responseIsBinary) {
response = Buffer.from(nockDef.response, 'hex')
} else {
response =
typeof nockDef.response === 'string'
? tryJsonParse(nockDef.response)
: nockDef.response
}
const scope = new Scope(nscope, options)
// If request headers were specified filter by them.
Object.entries(reqheaders).forEach(([fieldName, value]) => {
scope.matchHeader(fieldName, value)
})
const acceptableFilters = ['filteringRequestBody', 'filteringPath']
acceptableFilters.forEach(filter => {
if (nockDef[filter]) {
scope[filter](nockDef[filter])
}
})
scope.intercept(npath, method, body).reply(status, response, rawHeaders)
scopes.push(scope)
})
return scopes
}
module.exports = {
Scope,
load,
loadDefs,
define,
}

80
node_modules/nock/lib/socket.js generated vendored Normal file
View file

@ -0,0 +1,80 @@
'use strict'
const { EventEmitter } = require('events')
const debug = require('debug')('nock.socket')
module.exports = class Socket extends EventEmitter {
constructor(options) {
super()
if (options.proto === 'https') {
// https://github.com/nock/nock/issues/158
this.authorized = true
}
this.bufferSize = 0
this.writableLength = 0
this.writable = true
this.readable = true
this.pending = false
this.destroyed = false
this.connecting = false
// totalDelay that has already been applied to the current
// request/connection, timeout error will be generated if
// it is timed-out.
this.totalDelayMs = 0
// Maximum allowed delay. Null means unlimited.
this.timeoutMs = null
const ipv6 = options.family === 6
this.remoteFamily = ipv6 ? 'IPv6' : 'IPv4'
this.localAddress = this.remoteAddress = ipv6 ? '::1' : '127.0.0.1'
this.localPort = this.remotePort = parseInt(options.port)
}
setNoDelay() {}
setKeepAlive() {}
resume() {}
ref() {}
unref() {}
address() {
return {
port: this.remotePort,
family: this.remoteFamily,
address: this.remoteAddress,
}
}
setTimeout(timeoutMs, fn) {
this.timeoutMs = timeoutMs
if (fn) {
this.once('timeout', fn)
}
}
applyDelay(delayMs) {
this.totalDelayMs += delayMs
if (this.timeoutMs && this.totalDelayMs > this.timeoutMs) {
debug('socket timeout')
this.emit('timeout')
}
}
getPeerCertificate() {
return Buffer.from(
(Math.random() * 10000 + Date.now()).toString()
).toString('base64')
}
destroy(err) {
this.destroyed = true
this.readable = this.writable = false
if (err) {
this.emit('error', err)
}
return this
}
}

117
node_modules/nock/package.json generated vendored Normal file
View file

@ -0,0 +1,117 @@
{
"_from": "nock",
"_id": "nock@12.0.3",
"_inBundle": false,
"_integrity": "sha512-QNb/j8kbFnKCiyqi9C5DD0jH/FubFGj5rt9NQFONXwQm3IPB0CULECg/eS3AU1KgZb/6SwUa4/DTRKhVxkGABw==",
"_location": "/nock",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "nock",
"name": "nock",
"escapedName": "nock",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#DEV:/",
"#USER",
"/@types/nock"
],
"_resolved": "https://registry.npmjs.org/nock/-/nock-12.0.3.tgz",
"_shasum": "83f25076dbc4c9aa82b5cdf54c9604c7a778d1c9",
"_spec": "nock",
"_where": "/Users/alexkappa/Code/ts/github.com/github/codeql-action",
"author": {
"name": "Pedro Teixeira",
"email": "pedro.teixeira@gmail.com"
},
"bugs": {
"url": "http://github.com/nock/nock/issues"
},
"bundleDependencies": false,
"dependencies": {
"debug": "^4.1.0",
"json-stringify-safe": "^5.0.1",
"lodash": "^4.17.13",
"propagate": "^2.0.0"
},
"deprecated": false,
"description": "HTTP server mocking and expectations library for Node.js",
"devDependencies": {
"@sinonjs/fake-timers": "^6.0.0",
"assert-rejects": "^1.0.0",
"chai": "^4.1.2",
"dirty-chai": "^2.0.1",
"dtslint": "^3.0.0",
"eslint": "^6.0.0",
"eslint-config-prettier": "^6.0.0",
"eslint-config-standard": "^14.0.0",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-mocha": "^6.2.0",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-promise": "^4.1.1",
"eslint-plugin-standard": "^4.0.0",
"got": "^10.5.2",
"mocha": "^7.0.1",
"npm-run-all": "^4.1.5",
"nyc": "^15.0.0",
"prettier": "1.19.0",
"proxyquire": "^2.1.0",
"request": "^2.83.0",
"rimraf": "^3.0.0",
"semantic-release": "^17.0.2",
"sinon": "^9.0.0",
"sinon-chai": "^3.3.0",
"superagent": "^5.0.2",
"tap": "14.6.1"
},
"engines": {
"node": ">= 10.13"
},
"files": [
"index.js",
"lib",
"types/index.d.ts"
],
"homepage": "https://github.com/nock/nock#readme",
"license": "MIT",
"main": "./index.js",
"name": "nock",
"nyc": {
"reporter": [
"lcov",
"text-summary"
],
"exclude": [
"tests/"
]
},
"repository": {
"type": "git",
"url": "git+https://github.com/nock/nock.git"
},
"scripts": {
"format": "prettier --check '**/*.{js,json,md,ts,yml,yaml}'",
"format:fix": "prettier --write '**/*.{js,json,md,ts,yml,yaml}'",
"lint": "run-p lint:js lint:ts",
"lint:js": "eslint --cache --cache-location './.cache/eslint' '**/*.js'",
"lint:js:fix": "eslint --cache --cache-location './.cache/eslint' --fix '**/*.js'",
"lint:ts": "dtslint types",
"semantic-release": "semantic-release",
"test": "run-s test:mocha test:tap",
"test:coverage": "tap --coverage-report=html && open coverage/lcov-report/index.html",
"test:mocha": "nyc mocha $(grep -lr '^\\s*it(' tests)",
"test:tap": "tap --100 --coverage --coverage-report=text ./tests/test_*.js"
},
"tags": [
"Mock",
"HTTP",
"testing",
"isolation"
],
"types": "types",
"version": "12.0.3"
}

278
node_modules/nock/types/index.d.ts generated vendored Normal file
View file

@ -0,0 +1,278 @@
// TypeScript Version: 3.5
import { ReadStream } from 'fs'
import { ClientRequest, IncomingMessage, RequestOptions } from 'http'
import { ParsedUrlQuery } from 'querystring'
import { Url, URLSearchParams } from 'url'
export = nock
declare function nock(
basePath: string | RegExp | Url,
options?: nock.Options
): nock.Scope
declare namespace nock {
function cleanAll(): void
function activate(): void
function isActive(): boolean
function isDone(): boolean
function pendingMocks(): string[]
function activeMocks(): string[]
function removeInterceptor(interceptor: Interceptor | ReqOptions): boolean
function disableNetConnect(): void
function enableNetConnect(
matcher?: string | RegExp | ((host: string) => boolean)
): void
function load(path: string): Scope[]
function loadDefs(path: string): Definition[]
function define(defs: Definition[]): Scope[]
function restore(): void
function abortPendingRequests(): void
let back: Back
let emitter: NodeJS.EventEmitter
let recorder: Recorder
type InterceptFunction = (
uri: string | RegExp | { (uri: string): boolean },
requestBody?: RequestBodyMatcher,
interceptorOptions?: Options
) => Interceptor
// Essentially valid, decoded JSON with the addition of possible RegExp. TS doesn't currently have
// a great way to represent JSON type data, this data matcher design is based off this comment.
// https://github.com/microsoft/TypeScript/issues/1897#issuecomment-338650717
type DataMatcher =
| boolean
| number
| string
| null
| undefined
| RegExp
| DataMatcherArray
| DataMatcherMap
interface DataMatcherArray extends Array<DataMatcher> {}
interface DataMatcherMap {
[key: string]: DataMatcher
}
type RequestBodyMatcher =
| string
| Buffer
| RegExp
| DataMatcherArray
| DataMatcherMap
| { (body: any): boolean }
type RequestHeaderMatcher =
| string
| RegExp
| { (fieldValue: string): boolean }
type Body = string | Record<string, any> // a string or decoded JSON
type ReplyBody = Body | Buffer | ReadStream
type ReplyHeaderFunction = (
req: ClientRequest,
res: IncomingMessage,
body: string | Buffer
) => string | string[]
type ReplyHeaderValue = string | string[] | ReplyHeaderFunction
type ReplyHeaders =
| Record<string, ReplyHeaderValue>
| Map<string, ReplyHeaderValue>
| ReplyHeaderValue[]
type StatusCode = number
type ReplyFnResult =
| readonly [StatusCode]
| readonly [StatusCode, ReplyBody]
| readonly [StatusCode, ReplyBody, ReplyHeaders]
interface ReplyFnContext extends Interceptor {
req: ClientRequest & {
headers: Record<string, string>
}
}
interface Scope extends NodeJS.EventEmitter {
get: InterceptFunction
post: InterceptFunction
put: InterceptFunction
head: InterceptFunction
patch: InterceptFunction
merge: InterceptFunction
delete: InterceptFunction
options: InterceptFunction
intercept: (
uri: string | RegExp | { (uri: string): boolean },
method: string,
requestBody?: RequestBodyMatcher,
options?: Options
) => Interceptor
defaultReplyHeaders(headers: ReplyHeaders): this
matchHeader(name: string, value: RequestHeaderMatcher): this
filteringPath(regex: RegExp, replace: string): this
filteringPath(fn: (path: string) => string): this
filteringRequestBody(regex: RegExp, replace: string): this
filteringRequestBody(fn: (body: string) => string): this
log(out: (message: any, ...optionalParams: any[]) => void): this
persist(flag?: boolean): this
replyContentLength(): this
replyDate(d?: Date): this
done(): void
isDone(): boolean
pendingMocks(): string[]
activeMocks(): string[]
}
interface Interceptor {
query(
matcher:
| boolean
| string
| DataMatcherMap
| URLSearchParams
| { (parsedObj: ParsedUrlQuery): boolean }
): this
// tslint (as of 5.16) is under the impression that the callback types can be unified,
// however, doing so causes the params to lose their inherited types during use.
// the order of the overrides is important for determining the param types in the replay fns.
/* tslint:disable:unified-signatures */
reply(
replyFnWithCallback: (
this: ReplyFnContext,
uri: string,
body: Body,
callback: (
err: NodeJS.ErrnoException | null,
result: ReplyFnResult
) => void
) => void
): Scope
reply(
replyFn: (
this: ReplyFnContext,
uri: string,
body: Body
) => ReplyFnResult | Promise<ReplyFnResult>
): Scope
reply(
statusCode: StatusCode,
replyBodyFnWithCallback: (
this: ReplyFnContext,
uri: string,
body: Body,
callback: (err: NodeJS.ErrnoException | null, result: ReplyBody) => void
) => void,
headers?: ReplyHeaders
): Scope
reply(
statusCode: StatusCode,
replyBodyFn: (
this: ReplyFnContext,
uri: string,
body: Body
) => ReplyBody | Promise<ReplyBody>,
headers?: ReplyHeaders
): Scope
reply(responseCode?: StatusCode, body?: Body, headers?: ReplyHeaders): Scope
/* tslint:enable:unified-signatures */
replyWithError(errorMessage: string | object): Scope
replyWithFile(
statusCode: StatusCode,
fileName: string,
headers?: ReplyHeaders
): Scope
matchHeader(name: string, value: RequestHeaderMatcher): this
basicAuth(options: { user: string; pass?: string }): this
times(newCounter: number): this
once(): this
twice(): this
thrice(): this
optionally(): this
delay(opts: number | { head?: number; body?: number }): this
delayBody(timeMs: number): this
delayConnection(timeMs: number): this
socketDelay(timeMs: number): this
}
interface Options {
allowUnmocked?: boolean
reqheaders?: Record<string, RequestHeaderMatcher>
badheaders?: string[]
filteringScope?: { (scope: string): boolean }
encodedQueryParams?: boolean
}
interface Recorder {
rec(options?: boolean | RecorderOptions): void
clear(): void
play(): string[] | Definition[]
}
interface RecorderOptions {
dont_print?: boolean
output_objects?: boolean
enable_reqheaders_recording?: boolean
logging?: (content: string) => void
use_separator?: boolean
}
interface Definition {
scope: string
path: string
port?: number | string
method?: string
status?: number
body?: RequestBodyMatcher
reqheaders?: Record<string, RequestHeaderMatcher>
response?: ReplyBody
headers?: ReplyHeaders
options?: Options
}
type BackMode = 'wild' | 'dryrun' | 'record' | 'lockdown'
interface Back {
currentMode: BackMode
fixtures: string
setMode(mode: BackMode): void
(fixtureName: string, nockedFn: (nockDone: () => void) => void): void
(
fixtureName: string,
options: BackOptions,
nockedFn: (nockDone: () => void) => void
): void
(fixtureName: string, options?: BackOptions): Promise<{
nockDone: () => void
context: BackContext
}>
}
interface BackContext {
isLoaded: boolean
scopes: Scope[]
assertScopesFinished(): void
}
interface BackOptions {
before?: (def: Definition) => void
after?: (scope: Scope) => void
afterRecord?: (defs: Definition[]) => Definition[]
recorder?: RecorderOptions
}
}
type ReqOptions = RequestOptions & { proto?: string }