From 098ee568ede99ea4b3fdbddb2c943269d530512e Mon Sep 17 00:00:00 2001 From: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com> Date: Fri, 7 Aug 2020 14:11:20 -0400 Subject: [PATCH] Update build --- dist/index.js | 15747 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 15532 insertions(+), 215 deletions(-) diff --git a/dist/index.js b/dist/index.js index 48a23c5..1b5b182 100644 --- a/dist/index.js +++ b/dist/index.js @@ -19,13 +19,7 @@ module.exports = /******/ }; /******/ /******/ // Execute the module function -/******/ var threw = true; -/******/ try { -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ threw = false; -/******/ } finally { -/******/ if(threw) delete installedModules[moduleId]; -/******/ } +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; @@ -40,7 +34,7 @@ module.exports = /******/ // the startup function /******/ function startup() { /******/ // Load entry module and return exports -/******/ return __webpack_require__(130); +/******/ return __webpack_require__(492); /******/ }; /******/ /******/ // run startup @@ -49,15 +43,14060 @@ module.exports = /************************************************************************/ /******/ ({ -/***/ 94: +/***/ 9: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var once = __webpack_require__(49); + +var noop = function() {}; + +var isRequest = function(stream) { + return stream.setHeader && typeof stream.abort === 'function'; +}; + +var isChildProcess = function(stream) { + return stream.stdio && Array.isArray(stream.stdio) && stream.stdio.length === 3 +}; + +var eos = function(stream, opts, callback) { + if (typeof opts === 'function') return eos(stream, null, opts); + if (!opts) opts = {}; + + callback = once(callback || noop); + + var ws = stream._writableState; + var rs = stream._readableState; + var readable = opts.readable || (opts.readable !== false && stream.readable); + var writable = opts.writable || (opts.writable !== false && stream.writable); + + var onlegacyfinish = function() { + if (!stream.writable) onfinish(); + }; + + var onfinish = function() { + writable = false; + if (!readable) callback.call(stream); + }; + + var onend = function() { + readable = false; + if (!writable) callback.call(stream); + }; + + var onexit = function(exitCode) { + callback.call(stream, exitCode ? new Error('exited with error code: ' + exitCode) : null); + }; + + var onerror = function(err) { + callback.call(stream, err); + }; + + var onclose = function() { + if (readable && !(rs && rs.ended)) return callback.call(stream, new Error('premature close')); + if (writable && !(ws && ws.ended)) return callback.call(stream, new Error('premature close')); + }; + + var onrequest = function() { + stream.req.on('finish', onfinish); + }; + + if (isRequest(stream)) { + stream.on('complete', onfinish); + stream.on('abort', onclose); + if (stream.req) onrequest(); + else stream.on('request', onrequest); + } else if (writable && !ws) { // legacy streams + stream.on('end', onlegacyfinish); + stream.on('close', onlegacyfinish); + } + + if (isChildProcess(stream)) stream.on('exit', onexit); + + stream.on('end', onend); + stream.on('finish', onfinish); + if (opts.error !== false) stream.on('error', onerror); + stream.on('close', onclose); + + return function() { + stream.removeListener('complete', onfinish); + stream.removeListener('abort', onclose); + stream.removeListener('request', onrequest); + if (stream.req) stream.req.removeListener('finish', onfinish); + stream.removeListener('end', onlegacyfinish); + stream.removeListener('close', onlegacyfinish); + stream.removeListener('finish', onfinish); + stream.removeListener('exit', onexit); + stream.removeListener('end', onend); + stream.removeListener('error', onerror); + stream.removeListener('close', onclose); + }; +}; + +module.exports = eos; + + +/***/ }), + +/***/ 10: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const is_1 = __webpack_require__(534); +exports.default = (url) => { + // Cast to URL + url = url; + const options = { + protocol: url.protocol, + hostname: is_1.default.string(url.hostname) && url.hostname.startsWith('[') ? url.hostname.slice(1, -1) : url.hostname, + host: url.host, + hash: url.hash, + search: url.search, + pathname: url.pathname, + href: url.href, + path: `${url.pathname || ''}${url.search || ''}` + }; + if (is_1.default.string(url.port) && url.port.length !== 0) { + options.port = Number(url.port); + } + if (url.username || url.password) { + options.auth = `${url.username || ''}:${url.password || ''}`; + } + return options; +}; + + +/***/ }), + +/***/ 11: +/***/ (function(module) { + +// Returns a wrapper function that returns a wrapped callback +// The wrapper function should do some stuff, and return a +// presumably different callback function. +// This makes sure that own properties are retained, so that +// decorations and such are not lost along the way. +module.exports = wrappy +function wrappy (fn, cb) { + if (fn && cb) return wrappy(fn)(cb) + + if (typeof fn !== 'function') + throw new TypeError('need wrapper function') + + Object.keys(fn).forEach(function (k) { + wrapper[k] = fn[k] + }) + + return wrapper + + function wrapper() { + var args = new Array(arguments.length) + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i] + } + var ret = fn.apply(this, args) + var cb = args[args.length-1] + if (typeof ret === 'function' && ret !== cb) { + Object.keys(cb).forEach(function (k) { + ret[k] = cb[k] + }) + } + return ret + } +} + + +/***/ }), + +/***/ 16: +/***/ (function(module) { + +module.exports = require("tls"); + +/***/ }), + +/***/ 36: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CancelError = exports.ParseError = void 0; +const p_cancelable_1 = __webpack_require__(557); +Object.defineProperty(exports, "CancelError", { enumerable: true, get: function () { return p_cancelable_1.CancelError; } }); +const core_1 = __webpack_require__(946); +class ParseError extends core_1.RequestError { + constructor(error, response) { + const { options } = response.request; + super(`${error.message} in "${options.url.toString()}"`, error, response.request); + this.name = 'ParseError'; + Object.defineProperty(this, 'response', { + enumerable: false, + value: response + }); + } +} +exports.ParseError = ParseError; +__exportStar(__webpack_require__(946), exports); + + +/***/ }), + +/***/ 48: +/***/ (function(__unusedmodule, exports) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +class WeakableMap { + constructor() { + this.weakMap = new WeakMap(); + this.map = new Map(); + } + set(key, value) { + if (typeof key === 'object') { + this.weakMap.set(key, value); + } + else { + this.map.set(key, value); + } + } + get(key) { + if (typeof key === 'object') { + return this.weakMap.get(key); + } + return this.map.get(key); + } + has(key) { + if (typeof key === 'object') { + return this.weakMap.has(key); + } + return this.map.has(key); + } +} +exports.default = WeakableMap; + + +/***/ }), + +/***/ 49: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var wrappy = __webpack_require__(11) +module.exports = wrappy(once) +module.exports.strict = wrappy(onceStrict) + +once.proto = once(function () { + Object.defineProperty(Function.prototype, 'once', { + value: function () { + return once(this) + }, + configurable: true + }) + + Object.defineProperty(Function.prototype, 'onceStrict', { + value: function () { + return onceStrict(this) + }, + configurable: true + }) +}) + +function once (fn) { + var f = function () { + if (f.called) return f.value + f.called = true + return f.value = fn.apply(this, arguments) + } + f.called = false + return f +} + +function onceStrict (fn) { + var f = function () { + if (f.called) + throw new Error(f.onceError) + f.called = true + return f.value = fn.apply(this, arguments) + } + var name = fn.name || 'Function wrapped with `once`' + f.onceError = name + " shouldn't be called more than once" + f.called = false + return f +} + + +/***/ }), + +/***/ 53: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + +// TODO: Use the `URL` global when targeting Node.js 10 +const URLParser = typeof URL === 'undefined' ? __webpack_require__(835).URL : URL; + +// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs +const DATA_URL_DEFAULT_MIME_TYPE = 'text/plain'; +const DATA_URL_DEFAULT_CHARSET = 'us-ascii'; + +const testParameter = (name, filters) => { + return filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name); +}; + +const normalizeDataURL = (urlString, {stripHash}) => { + const parts = urlString.match(/^data:(.*?),(.*?)(?:#(.*))?$/); + + if (!parts) { + throw new Error(`Invalid URL: ${urlString}`); + } + + const mediaType = parts[1].split(';'); + const body = parts[2]; + const hash = stripHash ? '' : parts[3]; + + let base64 = false; + + if (mediaType[mediaType.length - 1] === 'base64') { + mediaType.pop(); + base64 = true; + } + + // Lowercase MIME type + const mimeType = (mediaType.shift() || '').toLowerCase(); + const attributes = mediaType + .map(attribute => { + let [key, value = ''] = attribute.split('=').map(string => string.trim()); + + // Lowercase `charset` + if (key === 'charset') { + value = value.toLowerCase(); + + if (value === DATA_URL_DEFAULT_CHARSET) { + return ''; + } + } + + return `${key}${value ? `=${value}` : ''}`; + }) + .filter(Boolean); + + const normalizedMediaType = [ + ...attributes + ]; + + if (base64) { + normalizedMediaType.push('base64'); + } + + if (normalizedMediaType.length !== 0 || (mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE)) { + normalizedMediaType.unshift(mimeType); + } + + return `data:${normalizedMediaType.join(';')},${base64 ? body.trim() : body}${hash ? `#${hash}` : ''}`; +}; + +const normalizeUrl = (urlString, options) => { + options = { + defaultProtocol: 'http:', + normalizeProtocol: true, + forceHttp: false, + forceHttps: false, + stripAuthentication: true, + stripHash: false, + stripWWW: true, + removeQueryParameters: [/^utm_\w+/i], + removeTrailingSlash: true, + removeDirectoryIndex: false, + sortQueryParameters: true, + ...options + }; + + // TODO: Remove this at some point in the future + if (Reflect.has(options, 'normalizeHttps')) { + throw new Error('options.normalizeHttps is renamed to options.forceHttp'); + } + + if (Reflect.has(options, 'normalizeHttp')) { + throw new Error('options.normalizeHttp is renamed to options.forceHttps'); + } + + if (Reflect.has(options, 'stripFragment')) { + throw new Error('options.stripFragment is renamed to options.stripHash'); + } + + urlString = urlString.trim(); + + // Data URL + if (/^data:/i.test(urlString)) { + return normalizeDataURL(urlString, options); + } + + const hasRelativeProtocol = urlString.startsWith('//'); + const isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString); + + // Prepend protocol + if (!isRelativeUrl) { + urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol); + } + + const urlObj = new URLParser(urlString); + + if (options.forceHttp && options.forceHttps) { + throw new Error('The `forceHttp` and `forceHttps` options cannot be used together'); + } + + if (options.forceHttp && urlObj.protocol === 'https:') { + urlObj.protocol = 'http:'; + } + + if (options.forceHttps && urlObj.protocol === 'http:') { + urlObj.protocol = 'https:'; + } + + // Remove auth + if (options.stripAuthentication) { + urlObj.username = ''; + urlObj.password = ''; + } + + // Remove hash + if (options.stripHash) { + urlObj.hash = ''; + } + + // Remove duplicate slashes if not preceded by a protocol + if (urlObj.pathname) { + // TODO: Use the following instead when targeting Node.js 10 + // `urlObj.pathname = urlObj.pathname.replace(/(? { + if (/^(?!\/)/g.test(p1)) { + return `${p1}/`; + } + + return '/'; + }); + } + + // Decode URI octets + if (urlObj.pathname) { + urlObj.pathname = decodeURI(urlObj.pathname); + } + + // Remove directory index + if (options.removeDirectoryIndex === true) { + options.removeDirectoryIndex = [/^index\.[a-z]+$/]; + } + + if (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) { + let pathComponents = urlObj.pathname.split('/'); + const lastComponent = pathComponents[pathComponents.length - 1]; + + if (testParameter(lastComponent, options.removeDirectoryIndex)) { + pathComponents = pathComponents.slice(0, pathComponents.length - 1); + urlObj.pathname = pathComponents.slice(1).join('/') + '/'; + } + } + + if (urlObj.hostname) { + // Remove trailing dot + urlObj.hostname = urlObj.hostname.replace(/\.$/, ''); + + // Remove `www.` + if (options.stripWWW && /^www\.([a-z\-\d]{2,63})\.([a-z.]{2,5})$/.test(urlObj.hostname)) { + // Each label should be max 63 at length (min: 2). + // The extension should be max 5 at length (min: 2). + // Source: https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names + urlObj.hostname = urlObj.hostname.replace(/^www\./, ''); + } + } + + // Remove query unwanted parameters + if (Array.isArray(options.removeQueryParameters)) { + for (const key of [...urlObj.searchParams.keys()]) { + if (testParameter(key, options.removeQueryParameters)) { + urlObj.searchParams.delete(key); + } + } + } + + // Sort query parameters + if (options.sortQueryParameters) { + urlObj.searchParams.sort(); + } + + if (options.removeTrailingSlash) { + urlObj.pathname = urlObj.pathname.replace(/\/$/, ''); + } + + // Take advantage of many of the Node `url` normalizations + urlString = urlObj.toString(); + + // Remove ending `/` + if ((options.removeTrailingSlash || urlObj.pathname === '/') && urlObj.hash === '') { + urlString = urlString.replace(/\/$/, ''); + } + + // Restore relative protocol, if applicable + if (hasRelativeProtocol && !options.normalizeProtocol) { + urlString = urlString.replace(/^http:\/\//, '//'); + } + + // Remove http/https + if (options.stripProtocol) { + urlString = urlString.replace(/^(?:https?:)?\/\//, ''); + } + + return urlString; +}; + +module.exports = normalizeUrl; +// TODO: Remove this for the next major release +module.exports.default = normalizeUrl; + + +/***/ }), + +/***/ 77: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const url_1 = __webpack_require__(835); +const create_1 = __webpack_require__(323); +const defaults = { + options: { + method: 'GET', + retry: { + limit: 2, + methods: [ + 'GET', + 'PUT', + 'HEAD', + 'DELETE', + 'OPTIONS', + 'TRACE' + ], + statusCodes: [ + 408, + 413, + 429, + 500, + 502, + 503, + 504, + 521, + 522, + 524 + ], + errorCodes: [ + 'ETIMEDOUT', + 'ECONNRESET', + 'EADDRINUSE', + 'ECONNREFUSED', + 'EPIPE', + 'ENOTFOUND', + 'ENETUNREACH', + 'EAI_AGAIN' + ], + maxRetryAfter: undefined, + calculateDelay: ({ computedValue }) => computedValue + }, + timeout: {}, + headers: { + 'user-agent': 'got (https://github.com/sindresorhus/got)' + }, + hooks: { + init: [], + beforeRequest: [], + beforeRedirect: [], + beforeRetry: [], + beforeError: [], + afterResponse: [] + }, + cache: undefined, + dnsCache: undefined, + decompress: true, + throwHttpErrors: true, + followRedirect: true, + isStream: false, + responseType: 'text', + resolveBodyOnly: false, + maxRedirects: 10, + prefixUrl: '', + methodRewriting: true, + ignoreInvalidCookies: false, + context: {}, + // TODO: Set this to `true` when Got 12 gets released + http2: false, + allowGetBody: false, + https: undefined, + pagination: { + transform: (response) => { + if (response.request.options.responseType === 'json') { + return response.body; + } + return JSON.parse(response.body); + }, + paginate: response => { + if (!Reflect.has(response.headers, 'link')) { + return false; + } + const items = response.headers.link.split(','); + let next; + for (const item of items) { + const parsed = item.split(';'); + if (parsed[1].includes('next')) { + next = parsed[0].trimStart().trim(); + next = next.slice(1, -1); + break; + } + } + if (next) { + const options = { + url: new url_1.URL(next) + }; + return options; + } + return false; + }, + filter: () => true, + shouldContinue: () => true, + countLimit: Infinity, + backoff: 0, + requestLimit: 10000, + stackAllItems: true + }, + parseJson: (text) => JSON.parse(text), + stringifyJson: (object) => JSON.stringify(object) + }, + handlers: [create_1.defaultHandler], + mutableDefaults: false +}; +const got = create_1.default(defaults); +exports.default = got; +// For CommonJS default export support +module.exports = got; +module.exports.default = got; +module.exports.__esModule = true; // Workaround for TS issue: https://github.com/sindresorhus/got/pull/1267 +__exportStar(__webpack_require__(323), exports); +__exportStar(__webpack_require__(577), exports); + + +/***/ }), + +/***/ 87: +/***/ (function(module) { + +module.exports = require("os"); + +/***/ }), + +/***/ 89: +/***/ (function(module) { + +"use strict"; + + +// We define these manually to ensure they're always copied +// even if they would move up the prototype chain +// https://nodejs.org/api/http.html#http_class_http_incomingmessage +const knownProps = [ + 'destroy', + 'setTimeout', + 'socket', + 'headers', + 'trailers', + 'rawHeaders', + 'statusCode', + 'httpVersion', + 'httpVersionMinor', + 'httpVersionMajor', + 'rawTrailers', + 'statusMessage' +]; + +module.exports = (fromStream, toStream) => { + const fromProps = new Set(Object.keys(fromStream).concat(knownProps)); + + for (const prop of fromProps) { + // Don't overwrite existing properties + if (prop in toStream) { + continue; + } + + toStream[prop] = typeof fromStream[prop] === 'function' ? fromStream[prop].bind(fromStream) : fromStream[prop]; + } +}; + + +/***/ }), + +/***/ 93: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +const Readable = __webpack_require__(413).Readable; +const lowercaseKeys = __webpack_require__(474); + +class Response extends Readable { + constructor(statusCode, headers, body, url) { + if (typeof statusCode !== 'number') { + throw new TypeError('Argument `statusCode` should be a number'); + } + if (typeof headers !== 'object') { + throw new TypeError('Argument `headers` should be an object'); + } + if (!(body instanceof Buffer)) { + throw new TypeError('Argument `body` should be a buffer'); + } + if (typeof url !== 'string') { + throw new TypeError('Argument `url` should be a string'); + } + + super(); + this.statusCode = statusCode; + this.headers = lowercaseKeys(headers); + this.body = body; + this.url = url; + } + + _read() { + this.push(this.body); + this.push(null); + } +} + +module.exports = Response; + + +/***/ }), + +/***/ 151: /***/ (function(module, __unusedexports, __webpack_require__) { // @ts-check -const core = __webpack_require__(968); -const command = __webpack_require__(422); -const got = __webpack_require__(349).default; -const jsonata = __webpack_require__(128); -const { auth: { retrieveToken }, secrets: { getSecrets } } = __webpack_require__(897); +const core = __webpack_require__(470); + +/*** + * Authenticate with Vault and retrieve a Vault token that can be used for requests. + * @param {string} method + * @param {import('got').Got} client + */ +async function retrieveToken(method, client) { + switch (method) { + case 'approle': { + const vaultRoleId = core.getInput('roleId', { required: true }); + const vaultSecretId = core.getInput('secretId', { required: true }); + return await getClientToken(client, method, { role_id: vaultRoleId, secret_id: vaultSecretId }); + } + case 'github': { + const githubToken = core.getInput('githubToken', { required: true }); + return await getClientToken(client, method, { token: githubToken }); + } + default: { + if (!method || method === 'token') { + return core.getInput('token', { required: true }); + } else { + /** @type {string} */ + const payload = core.getInput('authPayload', { required: true }); + if (!payload) { + throw Error('When using a custom authentication method, you must provide the payload'); + } + return await getClientToken(client, method, JSON.parse(payload.trim())); + } + } + } +} + +/*** + * Call the appropriate login endpoint and parse out the token in the response. + * @param {import('got').Got} client + * @param {string} method + * @param {any} payload + */ +async function getClientToken(client, method, payload) { + /** @type {'json'} */ + const responseType = 'json'; + var options = { + json: payload, + responseType, + }; + + core.debug(`Retrieving Vault Token from v1/auth/${method}/login endpoint`); + + /** @type {import('got').Response} */ + const response = await client.post(`v1/auth/${method}/login`, options); + if (response && response.body && response.body.auth && response.body.auth.client_token) { + core.debug('✔ Vault Token successfully retrieved'); + + core.startGroup('Token Info'); + core.debug(`Operating under policies: ${JSON.stringify(response.body.auth.policies)}`); + core.debug(`Token Metadata: ${JSON.stringify(response.body.auth.metadata)}`); + core.endGroup(); + + return response.body.auth.client_token; + } else { + throw Error(`Unable to retrieve token from ${method}'s login endpoint.`); + } +} + +/*** + * @typedef {Object} VaultLoginResponse + * @property {{ + * client_token: string; + * accessor: string; + * policies: string[]; + * metadata: unknown; + * lease_duration: number; + * renewable: boolean; + * }} auth + */ + +module.exports = { + retrieveToken, +}; + + +/***/ }), + +/***/ 154: +/***/ (function(module) { + +"use strict"; + +// rfc7231 6.1 +const statusCodeCacheableByDefault = new Set([ + 200, + 203, + 204, + 206, + 300, + 301, + 404, + 405, + 410, + 414, + 501, +]); + +// This implementation does not understand partial responses (206) +const understoodStatuses = new Set([ + 200, + 203, + 204, + 300, + 301, + 302, + 303, + 307, + 308, + 404, + 405, + 410, + 414, + 501, +]); + +const errorStatusCodes = new Set([ + 500, + 502, + 503, + 504, +]); + +const hopByHopHeaders = { + date: true, // included, because we add Age update Date + connection: true, + 'keep-alive': true, + 'proxy-authenticate': true, + 'proxy-authorization': true, + te: true, + trailer: true, + 'transfer-encoding': true, + upgrade: true, +}; + +const excludedFromRevalidationUpdate = { + // Since the old body is reused, it doesn't make sense to change properties of the body + 'content-length': true, + 'content-encoding': true, + 'transfer-encoding': true, + 'content-range': true, +}; + +function toNumberOrZero(s) { + const n = parseInt(s, 10); + return isFinite(n) ? n : 0; +} + +// RFC 5861 +function isErrorResponse(response) { + // consider undefined response as faulty + if(!response) { + return true + } + return errorStatusCodes.has(response.status); +} + +function parseCacheControl(header) { + const cc = {}; + if (!header) return cc; + + // TODO: When there is more than one value present for a given directive (e.g., two Expires header fields, multiple Cache-Control: max-age directives), + // the directive's value is considered invalid. Caches are encouraged to consider responses that have invalid freshness information to be stale + const parts = header.trim().split(/\s*,\s*/); // TODO: lame parsing + for (const part of parts) { + const [k, v] = part.split(/\s*=\s*/, 2); + cc[k] = v === undefined ? true : v.replace(/^"|"$/g, ''); // TODO: lame unquoting + } + + return cc; +} + +function formatCacheControl(cc) { + let parts = []; + for (const k in cc) { + const v = cc[k]; + parts.push(v === true ? k : k + '=' + v); + } + if (!parts.length) { + return undefined; + } + return parts.join(', '); +} + +module.exports = class CachePolicy { + constructor( + req, + res, + { + shared, + cacheHeuristic, + immutableMinTimeToLive, + ignoreCargoCult, + _fromObject, + } = {} + ) { + if (_fromObject) { + this._fromObject(_fromObject); + return; + } + + if (!res || !res.headers) { + throw Error('Response headers missing'); + } + this._assertRequestHasHeaders(req); + + this._responseTime = this.now(); + this._isShared = shared !== false; + this._cacheHeuristic = + undefined !== cacheHeuristic ? cacheHeuristic : 0.1; // 10% matches IE + this._immutableMinTtl = + undefined !== immutableMinTimeToLive + ? immutableMinTimeToLive + : 24 * 3600 * 1000; + + this._status = 'status' in res ? res.status : 200; + this._resHeaders = res.headers; + this._rescc = parseCacheControl(res.headers['cache-control']); + this._method = 'method' in req ? req.method : 'GET'; + this._url = req.url; + this._host = req.headers.host; + this._noAuthorization = !req.headers.authorization; + this._reqHeaders = res.headers.vary ? req.headers : null; // Don't keep all request headers if they won't be used + this._reqcc = parseCacheControl(req.headers['cache-control']); + + // Assume that if someone uses legacy, non-standard uncecessary options they don't understand caching, + // so there's no point stricly adhering to the blindly copy&pasted directives. + if ( + ignoreCargoCult && + 'pre-check' in this._rescc && + 'post-check' in this._rescc + ) { + delete this._rescc['pre-check']; + delete this._rescc['post-check']; + delete this._rescc['no-cache']; + delete this._rescc['no-store']; + delete this._rescc['must-revalidate']; + this._resHeaders = Object.assign({}, this._resHeaders, { + 'cache-control': formatCacheControl(this._rescc), + }); + delete this._resHeaders.expires; + delete this._resHeaders.pragma; + } + + // When the Cache-Control header field is not present in a request, caches MUST consider the no-cache request pragma-directive + // as having the same effect as if "Cache-Control: no-cache" were present (see Section 5.2.1). + if ( + res.headers['cache-control'] == null && + /no-cache/.test(res.headers.pragma) + ) { + this._rescc['no-cache'] = true; + } + } + + now() { + return Date.now(); + } + + storable() { + // The "no-store" request directive indicates that a cache MUST NOT store any part of either this request or any response to it. + return !!( + !this._reqcc['no-store'] && + // A cache MUST NOT store a response to any request, unless: + // The request method is understood by the cache and defined as being cacheable, and + ('GET' === this._method || + 'HEAD' === this._method || + ('POST' === this._method && this._hasExplicitExpiration())) && + // the response status code is understood by the cache, and + understoodStatuses.has(this._status) && + // the "no-store" cache directive does not appear in request or response header fields, and + !this._rescc['no-store'] && + // the "private" response directive does not appear in the response, if the cache is shared, and + (!this._isShared || !this._rescc.private) && + // the Authorization header field does not appear in the request, if the cache is shared, + (!this._isShared || + this._noAuthorization || + this._allowsStoringAuthenticated()) && + // the response either: + // contains an Expires header field, or + (this._resHeaders.expires || + // contains a max-age response directive, or + // contains a s-maxage response directive and the cache is shared, or + // contains a public response directive. + this._rescc['max-age'] || + (this._isShared && this._rescc['s-maxage']) || + this._rescc.public || + // has a status code that is defined as cacheable by default + statusCodeCacheableByDefault.has(this._status)) + ); + } + + _hasExplicitExpiration() { + // 4.2.1 Calculating Freshness Lifetime + return ( + (this._isShared && this._rescc['s-maxage']) || + this._rescc['max-age'] || + this._resHeaders.expires + ); + } + + _assertRequestHasHeaders(req) { + if (!req || !req.headers) { + throw Error('Request headers missing'); + } + } + + satisfiesWithoutRevalidation(req) { + this._assertRequestHasHeaders(req); + + // When presented with a request, a cache MUST NOT reuse a stored response, unless: + // the presented request does not contain the no-cache pragma (Section 5.4), nor the no-cache cache directive, + // unless the stored response is successfully validated (Section 4.3), and + const requestCC = parseCacheControl(req.headers['cache-control']); + if (requestCC['no-cache'] || /no-cache/.test(req.headers.pragma)) { + return false; + } + + if (requestCC['max-age'] && this.age() > requestCC['max-age']) { + return false; + } + + if ( + requestCC['min-fresh'] && + this.timeToLive() < 1000 * requestCC['min-fresh'] + ) { + return false; + } + + // the stored response is either: + // fresh, or allowed to be served stale + if (this.stale()) { + const allowsStale = + requestCC['max-stale'] && + !this._rescc['must-revalidate'] && + (true === requestCC['max-stale'] || + requestCC['max-stale'] > this.age() - this.maxAge()); + if (!allowsStale) { + return false; + } + } + + return this._requestMatches(req, false); + } + + _requestMatches(req, allowHeadMethod) { + // The presented effective request URI and that of the stored response match, and + return ( + (!this._url || this._url === req.url) && + this._host === req.headers.host && + // the request method associated with the stored response allows it to be used for the presented request, and + (!req.method || + this._method === req.method || + (allowHeadMethod && 'HEAD' === req.method)) && + // selecting header fields nominated by the stored response (if any) match those presented, and + this._varyMatches(req) + ); + } + + _allowsStoringAuthenticated() { + // following Cache-Control response directives (Section 5.2.2) have such an effect: must-revalidate, public, and s-maxage. + return ( + this._rescc['must-revalidate'] || + this._rescc.public || + this._rescc['s-maxage'] + ); + } + + _varyMatches(req) { + if (!this._resHeaders.vary) { + return true; + } + + // A Vary header field-value of "*" always fails to match + if (this._resHeaders.vary === '*') { + return false; + } + + const fields = this._resHeaders.vary + .trim() + .toLowerCase() + .split(/\s*,\s*/); + for (const name of fields) { + if (req.headers[name] !== this._reqHeaders[name]) return false; + } + return true; + } + + _copyWithoutHopByHopHeaders(inHeaders) { + const headers = {}; + for (const name in inHeaders) { + if (hopByHopHeaders[name]) continue; + headers[name] = inHeaders[name]; + } + // 9.1. Connection + if (inHeaders.connection) { + const tokens = inHeaders.connection.trim().split(/\s*,\s*/); + for (const name of tokens) { + delete headers[name]; + } + } + if (headers.warning) { + const warnings = headers.warning.split(/,/).filter(warning => { + return !/^\s*1[0-9][0-9]/.test(warning); + }); + if (!warnings.length) { + delete headers.warning; + } else { + headers.warning = warnings.join(',').trim(); + } + } + return headers; + } + + responseHeaders() { + const headers = this._copyWithoutHopByHopHeaders(this._resHeaders); + const age = this.age(); + + // A cache SHOULD generate 113 warning if it heuristically chose a freshness + // lifetime greater than 24 hours and the response's age is greater than 24 hours. + if ( + age > 3600 * 24 && + !this._hasExplicitExpiration() && + this.maxAge() > 3600 * 24 + ) { + headers.warning = + (headers.warning ? `${headers.warning}, ` : '') + + '113 - "rfc7234 5.5.4"'; + } + headers.age = `${Math.round(age)}`; + headers.date = new Date(this.now()).toUTCString(); + return headers; + } + + /** + * Value of the Date response header or current time if Date was invalid + * @return timestamp + */ + date() { + const serverDate = Date.parse(this._resHeaders.date); + if (isFinite(serverDate)) { + return serverDate; + } + return this._responseTime; + } + + /** + * Value of the Age header, in seconds, updated for the current time. + * May be fractional. + * + * @return Number + */ + age() { + let age = this._ageValue(); + + const residentTime = (this.now() - this._responseTime) / 1000; + return age + residentTime; + } + + _ageValue() { + return toNumberOrZero(this._resHeaders.age); + } + + /** + * Value of applicable max-age (or heuristic equivalent) in seconds. This counts since response's `Date`. + * + * For an up-to-date value, see `timeToLive()`. + * + * @return Number + */ + maxAge() { + if (!this.storable() || this._rescc['no-cache']) { + return 0; + } + + // Shared responses with cookies are cacheable according to the RFC, but IMHO it'd be unwise to do so by default + // so this implementation requires explicit opt-in via public header + if ( + this._isShared && + (this._resHeaders['set-cookie'] && + !this._rescc.public && + !this._rescc.immutable) + ) { + return 0; + } + + if (this._resHeaders.vary === '*') { + return 0; + } + + if (this._isShared) { + if (this._rescc['proxy-revalidate']) { + return 0; + } + // if a response includes the s-maxage directive, a shared cache recipient MUST ignore the Expires field. + if (this._rescc['s-maxage']) { + return toNumberOrZero(this._rescc['s-maxage']); + } + } + + // If a response includes a Cache-Control field with the max-age directive, a recipient MUST ignore the Expires field. + if (this._rescc['max-age']) { + return toNumberOrZero(this._rescc['max-age']); + } + + const defaultMinTtl = this._rescc.immutable ? this._immutableMinTtl : 0; + + const serverDate = this.date(); + if (this._resHeaders.expires) { + const expires = Date.parse(this._resHeaders.expires); + // A cache recipient MUST interpret invalid date formats, especially the value "0", as representing a time in the past (i.e., "already expired"). + if (Number.isNaN(expires) || expires < serverDate) { + return 0; + } + return Math.max(defaultMinTtl, (expires - serverDate) / 1000); + } + + if (this._resHeaders['last-modified']) { + const lastModified = Date.parse(this._resHeaders['last-modified']); + if (isFinite(lastModified) && serverDate > lastModified) { + return Math.max( + defaultMinTtl, + ((serverDate - lastModified) / 1000) * this._cacheHeuristic + ); + } + } + + return defaultMinTtl; + } + + timeToLive() { + const age = this.maxAge() - this.age(); + const staleIfErrorAge = age + toNumberOrZero(this._rescc['stale-if-error']); + const staleWhileRevalidateAge = age + toNumberOrZero(this._rescc['stale-while-revalidate']); + return Math.max(0, age, staleIfErrorAge, staleWhileRevalidateAge) * 1000; + } + + stale() { + return this.maxAge() <= this.age(); + } + + _useStaleIfError() { + return this.maxAge() + toNumberOrZero(this._rescc['stale-if-error']) > this.age(); + } + + useStaleWhileRevalidate() { + return this.maxAge() + toNumberOrZero(this._rescc['stale-while-revalidate']) > this.age(); + } + + static fromObject(obj) { + return new this(undefined, undefined, { _fromObject: obj }); + } + + _fromObject(obj) { + if (this._responseTime) throw Error('Reinitialized'); + if (!obj || obj.v !== 1) throw Error('Invalid serialization'); + + this._responseTime = obj.t; + this._isShared = obj.sh; + this._cacheHeuristic = obj.ch; + this._immutableMinTtl = + obj.imm !== undefined ? obj.imm : 24 * 3600 * 1000; + this._status = obj.st; + this._resHeaders = obj.resh; + this._rescc = obj.rescc; + this._method = obj.m; + this._url = obj.u; + this._host = obj.h; + this._noAuthorization = obj.a; + this._reqHeaders = obj.reqh; + this._reqcc = obj.reqcc; + } + + toObject() { + return { + v: 1, + t: this._responseTime, + sh: this._isShared, + ch: this._cacheHeuristic, + imm: this._immutableMinTtl, + st: this._status, + resh: this._resHeaders, + rescc: this._rescc, + m: this._method, + u: this._url, + h: this._host, + a: this._noAuthorization, + reqh: this._reqHeaders, + reqcc: this._reqcc, + }; + } + + /** + * Headers for sending to the origin server to revalidate stale response. + * Allows server to return 304 to allow reuse of the previous response. + * + * Hop by hop headers are always stripped. + * Revalidation headers may be added or removed, depending on request. + */ + revalidationHeaders(incomingReq) { + this._assertRequestHasHeaders(incomingReq); + const headers = this._copyWithoutHopByHopHeaders(incomingReq.headers); + + // This implementation does not understand range requests + delete headers['if-range']; + + if (!this._requestMatches(incomingReq, true) || !this.storable()) { + // revalidation allowed via HEAD + // not for the same resource, or wasn't allowed to be cached anyway + delete headers['if-none-match']; + delete headers['if-modified-since']; + return headers; + } + + /* MUST send that entity-tag in any cache validation request (using If-Match or If-None-Match) if an entity-tag has been provided by the origin server. */ + if (this._resHeaders.etag) { + headers['if-none-match'] = headers['if-none-match'] + ? `${headers['if-none-match']}, ${this._resHeaders.etag}` + : this._resHeaders.etag; + } + + // Clients MAY issue simple (non-subrange) GET requests with either weak validators or strong validators. Clients MUST NOT use weak validators in other forms of request. + const forbidsWeakValidators = + headers['accept-ranges'] || + headers['if-match'] || + headers['if-unmodified-since'] || + (this._method && this._method != 'GET'); + + /* SHOULD send the Last-Modified value in non-subrange cache validation requests (using If-Modified-Since) if only a Last-Modified value has been provided by the origin server. + Note: This implementation does not understand partial responses (206) */ + if (forbidsWeakValidators) { + delete headers['if-modified-since']; + + if (headers['if-none-match']) { + const etags = headers['if-none-match'] + .split(/,/) + .filter(etag => { + return !/^\s*W\//.test(etag); + }); + if (!etags.length) { + delete headers['if-none-match']; + } else { + headers['if-none-match'] = etags.join(',').trim(); + } + } + } else if ( + this._resHeaders['last-modified'] && + !headers['if-modified-since'] + ) { + headers['if-modified-since'] = this._resHeaders['last-modified']; + } + + return headers; + } + + /** + * Creates new CachePolicy with information combined from the previews response, + * and the new revalidation response. + * + * Returns {policy, modified} where modified is a boolean indicating + * whether the response body has been modified, and old cached body can't be used. + * + * @return {Object} {policy: CachePolicy, modified: Boolean} + */ + revalidatedPolicy(request, response) { + this._assertRequestHasHeaders(request); + if(this._useStaleIfError() && isErrorResponse(response)) { // I consider the revalidation request unsuccessful + return { + modified: false, + matches: false, + policy: this, + }; + } + if (!response || !response.headers) { + throw Error('Response headers missing'); + } + + // These aren't going to be supported exactly, since one CachePolicy object + // doesn't know about all the other cached objects. + let matches = false; + if (response.status !== undefined && response.status != 304) { + matches = false; + } else if ( + response.headers.etag && + !/^\s*W\//.test(response.headers.etag) + ) { + // "All of the stored responses with the same strong validator are selected. + // If none of the stored responses contain the same strong validator, + // then the cache MUST NOT use the new response to update any stored responses." + matches = + this._resHeaders.etag && + this._resHeaders.etag.replace(/^\s*W\//, '') === + response.headers.etag; + } else if (this._resHeaders.etag && response.headers.etag) { + // "If the new response contains a weak validator and that validator corresponds + // to one of the cache's stored responses, + // then the most recent of those matching stored responses is selected for update." + matches = + this._resHeaders.etag.replace(/^\s*W\//, '') === + response.headers.etag.replace(/^\s*W\//, ''); + } else if (this._resHeaders['last-modified']) { + matches = + this._resHeaders['last-modified'] === + response.headers['last-modified']; + } else { + // If the new response does not include any form of validator (such as in the case where + // a client generates an If-Modified-Since request from a source other than the Last-Modified + // response header field), and there is only one stored response, and that stored response also + // lacks a validator, then that stored response is selected for update. + if ( + !this._resHeaders.etag && + !this._resHeaders['last-modified'] && + !response.headers.etag && + !response.headers['last-modified'] + ) { + matches = true; + } + } + + if (!matches) { + return { + policy: new this.constructor(request, response), + // Client receiving 304 without body, even if it's invalid/mismatched has no option + // but to reuse a cached body. We don't have a good way to tell clients to do + // error recovery in such case. + modified: response.status != 304, + matches: false, + }; + } + + // use other header fields provided in the 304 (Not Modified) response to replace all instances + // of the corresponding header fields in the stored response. + const headers = {}; + for (const k in this._resHeaders) { + headers[k] = + k in response.headers && !excludedFromRevalidationUpdate[k] + ? response.headers[k] + : this._resHeaders[k]; + } + + const newResponse = Object.assign({}, response, { + status: this._status, + method: this._method, + headers, + }); + return { + policy: new this.constructor(request, newResponse, { + shared: this._isShared, + cacheHeuristic: this._cacheHeuristic, + immutableMinTimeToLive: this._immutableMinTtl, + }), + modified: false, + matches: true, + }; + } +}; + + +/***/ }), + +/***/ 157: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + +const http2 = __webpack_require__(565); +const agent = __webpack_require__(899); +const ClientRequest = __webpack_require__(181); +const IncomingMessage = __webpack_require__(750); +const auto = __webpack_require__(988); + +const request = (url, options, callback) => { + return new ClientRequest(url, options, callback); +}; + +const get = (url, options, callback) => { + // eslint-disable-next-line unicorn/prevent-abbreviations + const req = new ClientRequest(url, options, callback); + req.end(); + + return req; +}; + +module.exports = { + ...http2, + ClientRequest, + IncomingMessage, + ...agent, + request, + get, + auto +}; + + +/***/ }), + +/***/ 181: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + +const http2 = __webpack_require__(565); +const {Writable} = __webpack_require__(413); +const {Agent, globalAgent} = __webpack_require__(899); +const IncomingMessage = __webpack_require__(750); +const urlToOptions = __webpack_require__(507); +const proxyEvents = __webpack_require__(231); +const isRequestPseudoHeader = __webpack_require__(723); +const { + ERR_INVALID_ARG_TYPE, + ERR_INVALID_PROTOCOL, + ERR_HTTP_HEADERS_SENT, + ERR_INVALID_HTTP_TOKEN, + ERR_HTTP_INVALID_HEADER_VALUE, + ERR_INVALID_CHAR +} = __webpack_require__(699); + +const { + HTTP2_HEADER_STATUS, + HTTP2_HEADER_METHOD, + HTTP2_HEADER_PATH, + HTTP2_METHOD_CONNECT +} = http2.constants; + +const kHeaders = Symbol('headers'); +const kOrigin = Symbol('origin'); +const kSession = Symbol('session'); +const kOptions = Symbol('options'); +const kFlushedHeaders = Symbol('flushedHeaders'); +const kJobs = Symbol('jobs'); + +const isValidHttpToken = /^[\^`\-\w!#$%&*+.|~]+$/; +const isInvalidHeaderValue = /[^\t\u0020-\u007E\u0080-\u00FF]/; + +class ClientRequest extends Writable { + constructor(input, options, callback) { + super({ + autoDestroy: false + }); + + const hasInput = typeof input === 'string' || input instanceof URL; + if (hasInput) { + input = urlToOptions(input instanceof URL ? input : new URL(input)); + } + + if (typeof options === 'function' || options === undefined) { + // (options, callback) + callback = options; + options = hasInput ? input : {...input}; + } else { + // (input, options, callback) + options = {...input, ...options}; + } + + if (options.h2session) { + this[kSession] = options.h2session; + } else if (options.agent === false) { + this.agent = new Agent({maxFreeSessions: 0}); + } else if (typeof options.agent === 'undefined' || options.agent === null) { + if (typeof options.createConnection === 'function') { + // This is a workaround - we don't have to create the session on our own. + this.agent = new Agent({maxFreeSessions: 0}); + this.agent.createConnection = options.createConnection; + } else { + this.agent = globalAgent; + } + } else if (typeof options.agent.request === 'function') { + this.agent = options.agent; + } else { + throw new ERR_INVALID_ARG_TYPE('options.agent', ['Agent-like Object', 'undefined', 'false'], options.agent); + } + + if (options.protocol && options.protocol !== 'https:') { + throw new ERR_INVALID_PROTOCOL(options.protocol, 'https:'); + } + + const port = options.port || options.defaultPort || (this.agent && this.agent.defaultPort) || 443; + const host = options.hostname || options.host || 'localhost'; + + // Don't enforce the origin via options. It may be changed in an Agent. + delete options.hostname; + delete options.host; + delete options.port; + + const {timeout} = options; + options.timeout = undefined; + + this[kHeaders] = Object.create(null); + this[kJobs] = []; + + this.socket = null; + this.connection = null; + + this.method = options.method || 'GET'; + this.path = options.path; + + this.res = null; + this.aborted = false; + this.reusedSocket = false; + + if (options.headers) { + for (const [header, value] of Object.entries(options.headers)) { + this.setHeader(header, value); + } + } + + if (options.auth && !('authorization' in this[kHeaders])) { + this[kHeaders].authorization = 'Basic ' + Buffer.from(options.auth).toString('base64'); + } + + options.session = options.tlsSession; + options.path = options.socketPath; + + this[kOptions] = options; + + // Clients that generate HTTP/2 requests directly SHOULD use the :authority pseudo-header field instead of the Host header field. + if (port === 443) { + this[kOrigin] = `https://${host}`; + + if (!(':authority' in this[kHeaders])) { + this[kHeaders][':authority'] = host; + } + } else { + this[kOrigin] = `https://${host}:${port}`; + + if (!(':authority' in this[kHeaders])) { + this[kHeaders][':authority'] = `${host}:${port}`; + } + } + + if (timeout) { + this.setTimeout(timeout); + } + + if (callback) { + this.once('response', callback); + } + + this[kFlushedHeaders] = false; + } + + get method() { + return this[kHeaders][HTTP2_HEADER_METHOD]; + } + + set method(value) { + if (value) { + this[kHeaders][HTTP2_HEADER_METHOD] = value.toUpperCase(); + } + } + + get path() { + return this[kHeaders][HTTP2_HEADER_PATH]; + } + + set path(value) { + if (value) { + this[kHeaders][HTTP2_HEADER_PATH] = value; + } + } + + get _mustNotHaveABody() { + return this.method === 'GET' || this.method === 'HEAD' || this.method === 'DELETE'; + } + + _write(chunk, encoding, callback) { + // https://github.com/nodejs/node/blob/654df09ae0c5e17d1b52a900a545f0664d8c7627/lib/internal/http2/util.js#L148-L156 + if (this._mustNotHaveABody) { + callback(new Error('The GET, HEAD and DELETE methods must NOT have a body')); + /* istanbul ignore next: Node.js 12 throws directly */ + return; + } + + this.flushHeaders(); + + const callWrite = () => this._request.write(chunk, encoding, callback); + if (this._request) { + callWrite(); + } else { + this[kJobs].push(callWrite); + } + } + + _final(callback) { + if (this.destroyed) { + return; + } + + this.flushHeaders(); + + const callEnd = () => { + // For GET, HEAD and DELETE + if (this._mustNotHaveABody) { + callback(); + return; + } + + this._request.end(callback); + }; + + if (this._request) { + callEnd(); + } else { + this[kJobs].push(callEnd); + } + } + + abort() { + if (this.res && this.res.complete) { + return; + } + + if (!this.aborted) { + process.nextTick(() => this.emit('abort')); + } + + this.aborted = true; + + this.destroy(); + } + + _destroy(error, callback) { + if (this.res) { + this.res._dump(); + } + + if (this._request) { + this._request.destroy(); + } + + callback(error); + } + + async flushHeaders() { + if (this[kFlushedHeaders] || this.destroyed) { + return; + } + + this[kFlushedHeaders] = true; + + const isConnectMethod = this.method === HTTP2_METHOD_CONNECT; + + // The real magic is here + const onStream = stream => { + this._request = stream; + + if (this.destroyed) { + stream.destroy(); + return; + } + + // Forwards `timeout`, `continue`, `close` and `error` events to this instance. + if (!isConnectMethod) { + proxyEvents(stream, this, ['timeout', 'continue', 'close', 'error']); + } + + // Wait for the `finish` event. We don't want to emit the `response` event + // before `request.end()` is called. + const waitForEnd = fn => { + return (...args) => { + if (!this.writable && !this.destroyed) { + fn(...args); + } else { + this.once('finish', () => { + fn(...args); + }); + } + }; + }; + + // This event tells we are ready to listen for the data. + stream.once('response', waitForEnd((headers, flags, rawHeaders) => { + // If we were to emit raw request stream, it would be as fast as the native approach. + // Note that wrapping the raw stream in a Proxy instance won't improve the performance (already tested it). + const response = new IncomingMessage(this.socket, stream.readableHighWaterMark); + this.res = response; + + response.req = this; + response.statusCode = headers[HTTP2_HEADER_STATUS]; + response.headers = headers; + response.rawHeaders = rawHeaders; + + response.once('end', () => { + if (this.aborted) { + response.aborted = true; + response.emit('aborted'); + } else { + response.complete = true; + + // Has no effect, just be consistent with the Node.js behavior + response.socket = null; + response.connection = null; + } + }); + + if (isConnectMethod) { + response.upgrade = true; + + // The HTTP1 API says the socket is detached here, + // but we can't do that so we pass the original HTTP2 request. + if (this.emit('connect', response, stream, Buffer.alloc(0))) { + this.emit('close'); + } else { + // No listeners attached, destroy the original request. + stream.destroy(); + } + } else { + // Forwards data + stream.on('data', chunk => { + if (!response._dumped && !response.push(chunk)) { + stream.pause(); + } + }); + + stream.once('end', () => { + response.push(null); + }); + + if (!this.emit('response', response)) { + // No listeners attached, dump the response. + response._dump(); + } + } + })); + + // Emits `information` event + stream.once('headers', waitForEnd( + headers => this.emit('information', {statusCode: headers[HTTP2_HEADER_STATUS]}) + )); + + stream.once('trailers', waitForEnd((trailers, flags, rawTrailers) => { + const {res} = this; + + // Assigns trailers to the response object. + res.trailers = trailers; + res.rawTrailers = rawTrailers; + })); + + const {socket} = stream.session; + this.socket = socket; + this.connection = socket; + + for (const job of this[kJobs]) { + job(); + } + + this.emit('socket', this.socket); + }; + + // Makes a HTTP2 request + if (this[kSession]) { + try { + onStream(this[kSession].request(this[kHeaders])); + } catch (error) { + this.emit('error', error); + } + } else { + this.reusedSocket = true; + + try { + onStream(await this.agent.request(this[kOrigin], this[kOptions], this[kHeaders])); + } catch (error) { + this.emit('error', error); + } + } + } + + getHeader(name) { + if (typeof name !== 'string') { + throw new ERR_INVALID_ARG_TYPE('name', 'string', name); + } + + return this[kHeaders][name.toLowerCase()]; + } + + get headersSent() { + return this[kFlushedHeaders]; + } + + removeHeader(name) { + if (typeof name !== 'string') { + throw new ERR_INVALID_ARG_TYPE('name', 'string', name); + } + + if (this.headersSent) { + throw new ERR_HTTP_HEADERS_SENT('remove'); + } + + delete this[kHeaders][name.toLowerCase()]; + } + + setHeader(name, value) { + if (this.headersSent) { + throw new ERR_HTTP_HEADERS_SENT('set'); + } + + if (typeof name !== 'string' || (!isValidHttpToken.test(name) && !isRequestPseudoHeader(name))) { + throw new ERR_INVALID_HTTP_TOKEN('Header name', name); + } + + if (typeof value === 'undefined') { + throw new ERR_HTTP_INVALID_HEADER_VALUE(value, name); + } + + if (isInvalidHeaderValue.test(value)) { + throw new ERR_INVALID_CHAR('header content', name); + } + + this[kHeaders][name.toLowerCase()] = value; + } + + setNoDelay() { + // HTTP2 sockets cannot be malformed, do nothing. + } + + setSocketKeepAlive() { + // HTTP2 sockets cannot be malformed, do nothing. + } + + setTimeout(ms, callback) { + const applyTimeout = () => this._request.setTimeout(ms, callback); + + if (this._request) { + applyTimeout(); + } else { + this[kJobs].push(applyTimeout); + } + + return this; + } + + get maxHeadersCount() { + if (!this.destroyed && this._request) { + return this._request.session.localSettings.maxHeaderListSize; + } + + return undefined; + } + + set maxHeadersCount(_value) { + // Updating HTTP2 settings would affect all requests, do nothing. + } +} + +module.exports = ClientRequest; + + +/***/ }), + +/***/ 189: +/***/ (function(__unusedmodule, exports) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const alreadyWarned = new Set(); +exports.default = (message) => { + if (alreadyWarned.has(message)) { + return; + } + alreadyWarned.add(message); + // @ts-expect-error Missing types. + process.emitWarning(`Got: ${message}`, { + type: 'DeprecationWarning' + }); +}; + + +/***/ }), + +/***/ 205: +/***/ (function(__unusedmodule, exports) { + +//TODO: handle reviver/dehydrate function like normal +//and handle indentation, like normal. +//if anyone needs this... please send pull request. + +exports.stringify = function stringify (o) { + if('undefined' == typeof o) return o + + if(o && Buffer.isBuffer(o)) + return JSON.stringify(':base64:' + o.toString('base64')) + + if(o && o.toJSON) + o = o.toJSON() + + if(o && 'object' === typeof o) { + var s = '' + var array = Array.isArray(o) + s = array ? '[' : '{' + var first = true + + for(var k in o) { + var ignore = 'function' == typeof o[k] || (!array && 'undefined' === typeof o[k]) + if(Object.hasOwnProperty.call(o, k) && !ignore) { + if(!first) + s += ',' + first = false + if (array) { + if(o[k] == undefined) + s += 'null' + else + s += stringify(o[k]) + } else if (o[k] !== void(0)) { + s += stringify(k) + ':' + stringify(o[k]) + } + } + } + + s += array ? ']' : '}' + + return s + } else if ('string' === typeof o) { + return JSON.stringify(/^:/.test(o) ? ':' + o : o) + } else if ('undefined' === typeof o) { + return 'null'; + } else + return JSON.stringify(o) +} + +exports.parse = function (s) { + return JSON.parse(s, function (key, value) { + if('string' === typeof value) { + if(/^:base64:/.test(value)) + return Buffer.from(value.substring(8), 'base64') + else + return /^:/.test(value) ? value.substring(1) : value + } + return value + }) +} + + +/***/ }), + +/***/ 211: +/***/ (function(module) { + +module.exports = require("https"); + +/***/ }), + +/***/ 231: +/***/ (function(module) { + +"use strict"; + + +module.exports = (from, to, events) => { + for (const event of events) { + from.on(event, (...args) => to.emit(event, ...args)); + } +}; + + +/***/ }), + +/***/ 291: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const is_1 = __webpack_require__(534); +function deepFreeze(object) { + for (const value of Object.values(object)) { + if (is_1.default.plainObject(value) || is_1.default.array(value)) { + deepFreeze(value); + } + } + return Object.freeze(object); +} +exports.default = deepFreeze; + + +/***/ }), + +/***/ 303: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +const EventEmitter = __webpack_require__(614); +const JSONB = __webpack_require__(205); + +const loadStore = opts => { + const adapters = { + redis: '@keyv/redis', + mongodb: '@keyv/mongo', + mongo: '@keyv/mongo', + sqlite: '@keyv/sqlite', + postgresql: '@keyv/postgres', + postgres: '@keyv/postgres', + mysql: '@keyv/mysql' + }; + if (opts.adapter || opts.uri) { + const adapter = opts.adapter || /^[^:]*/.exec(opts.uri)[0]; + return new (require(adapters[adapter]))(opts); + } + + return new Map(); +}; + +class Keyv extends EventEmitter { + constructor(uri, opts) { + super(); + this.opts = Object.assign( + { + namespace: 'keyv', + serialize: JSONB.stringify, + deserialize: JSONB.parse + }, + (typeof uri === 'string') ? { uri } : uri, + opts + ); + + if (!this.opts.store) { + const adapterOpts = Object.assign({}, this.opts); + this.opts.store = loadStore(adapterOpts); + } + + if (typeof this.opts.store.on === 'function') { + this.opts.store.on('error', err => this.emit('error', err)); + } + + this.opts.store.namespace = this.opts.namespace; + } + + _getKeyPrefix(key) { + return `${this.opts.namespace}:${key}`; + } + + get(key, opts) { + const keyPrefixed = this._getKeyPrefix(key); + const { store } = this.opts; + return Promise.resolve() + .then(() => store.get(keyPrefixed)) + .then(data => { + return (typeof data === 'string') ? this.opts.deserialize(data) : data; + }) + .then(data => { + if (data === undefined) { + return undefined; + } + + if (typeof data.expires === 'number' && Date.now() > data.expires) { + this.delete(key); + return undefined; + } + + return (opts && opts.raw) ? data : data.value; + }); + } + + set(key, value, ttl) { + const keyPrefixed = this._getKeyPrefix(key); + if (typeof ttl === 'undefined') { + ttl = this.opts.ttl; + } + + if (ttl === 0) { + ttl = undefined; + } + + const { store } = this.opts; + + return Promise.resolve() + .then(() => { + const expires = (typeof ttl === 'number') ? (Date.now() + ttl) : null; + value = { value, expires }; + return this.opts.serialize(value); + }) + .then(value => store.set(keyPrefixed, value, ttl)) + .then(() => true); + } + + delete(key) { + const keyPrefixed = this._getKeyPrefix(key); + const { store } = this.opts; + return Promise.resolve() + .then(() => store.delete(keyPrefixed)); + } + + clear() { + const { store } = this.opts; + return Promise.resolve() + .then(() => store.clear()); + } +} + +module.exports = Keyv; + + +/***/ }), + +/***/ 323: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.defaultHandler = void 0; +const p_cancelable_1 = __webpack_require__(557); +const is_1 = __webpack_require__(534); +const as_promise_1 = __webpack_require__(577); +const create_rejection_1 = __webpack_require__(910); +const core_1 = __webpack_require__(946); +const deep_freeze_1 = __webpack_require__(291); +const errors = { + RequestError: as_promise_1.RequestError, + CacheError: as_promise_1.CacheError, + ReadError: as_promise_1.ReadError, + HTTPError: as_promise_1.HTTPError, + MaxRedirectsError: as_promise_1.MaxRedirectsError, + TimeoutError: as_promise_1.TimeoutError, + ParseError: as_promise_1.ParseError, + CancelError: p_cancelable_1.CancelError, + UnsupportedProtocolError: as_promise_1.UnsupportedProtocolError, + UploadError: as_promise_1.UploadError +}; +// The `delay` package weighs 10KB (!) +const delay = async (ms) => new Promise(resolve => setTimeout(resolve, ms)); +const { normalizeArguments, mergeOptions } = as_promise_1.PromisableRequest; +const getPromiseOrStream = (options) => options.isStream ? new core_1.default(options.url, options) : as_promise_1.default(options); +const isGotInstance = (value) => ('defaults' in value && 'options' in value.defaults); +const aliases = [ + 'get', + 'post', + 'put', + 'patch', + 'head', + 'delete' +]; +exports.defaultHandler = (options, next) => next(options); +const callInitHooks = (hooks, options) => { + if (hooks) { + for (const hook of hooks) { + hook(options); + } + } +}; +const create = (defaults) => { + // Proxy properties from next handlers + defaults._rawHandlers = defaults.handlers; + defaults.handlers = defaults.handlers.map(fn => ((options, next) => { + // This will be assigned by assigning result + let root; + const result = fn(options, newOptions => { + root = next(newOptions); + return root; + }); + if (result !== root && !options.isStream && root) { + const typedResult = result; + const { then: promiseThen, catch: promiseCatch, finally: promiseFianlly } = typedResult; + Object.setPrototypeOf(typedResult, Object.getPrototypeOf(root)); + Object.defineProperties(typedResult, Object.getOwnPropertyDescriptors(root)); + // These should point to the new promise + // eslint-disable-next-line promise/prefer-await-to-then + typedResult.then = promiseThen; + typedResult.catch = promiseCatch; + typedResult.finally = promiseFianlly; + } + return result; + })); + // Got interface + const got = ((url, options) => { + var _a, _b; + let iteration = 0; + const iterateHandlers = (newOptions) => { + return defaults.handlers[iteration++](newOptions, iteration === defaults.handlers.length ? getPromiseOrStream : iterateHandlers); + }; + // TODO: Remove this in Got 12. + if (is_1.default.plainObject(url)) { + const mergedOptions = { + ...url, + ...options + }; + core_1.setNonEnumerableProperties([url, options], mergedOptions); + options = mergedOptions; + url = undefined; + } + try { + // Call `init` hooks + let initHookError; + try { + callInitHooks(defaults.options.hooks.init, options); + callInitHooks((_a = options === null || options === void 0 ? void 0 : options.hooks) === null || _a === void 0 ? void 0 : _a.init, options); + } + catch (error) { + initHookError = error; + } + // Normalize options & call handlers + const normalizedOptions = normalizeArguments(url, options, defaults.options); + normalizedOptions[core_1.kIsNormalizedAlready] = true; + if (initHookError) { + throw new as_promise_1.RequestError(initHookError.message, initHookError, normalizedOptions); + } + return iterateHandlers(normalizedOptions); + } + catch (error) { + if (options === null || options === void 0 ? void 0 : options.isStream) { + throw error; + } + else { + return create_rejection_1.default(error, defaults.options.hooks.beforeError, (_b = options === null || options === void 0 ? void 0 : options.hooks) === null || _b === void 0 ? void 0 : _b.beforeError); + } + } + }); + got.extend = (...instancesOrOptions) => { + const optionsArray = [defaults.options]; + let handlers = [...defaults._rawHandlers]; + let isMutableDefaults; + for (const value of instancesOrOptions) { + if (isGotInstance(value)) { + optionsArray.push(value.defaults.options); + handlers.push(...value.defaults._rawHandlers); + isMutableDefaults = value.defaults.mutableDefaults; + } + else { + optionsArray.push(value); + if ('handlers' in value) { + handlers.push(...value.handlers); + } + isMutableDefaults = value.mutableDefaults; + } + } + handlers = handlers.filter(handler => handler !== exports.defaultHandler); + if (handlers.length === 0) { + handlers.push(exports.defaultHandler); + } + return create({ + options: mergeOptions(...optionsArray), + handlers, + mutableDefaults: Boolean(isMutableDefaults) + }); + }; + // Pagination + const paginateEach = (async function* (url, options) { + // TODO: Remove this `@ts-expect-error` when upgrading to TypeScript 4. + // Error: Argument of type 'Merge> | undefined' is not assignable to parameter of type 'Options | undefined'. + // @ts-expect-error + let normalizedOptions = normalizeArguments(url, options, defaults.options); + normalizedOptions.resolveBodyOnly = false; + const pagination = normalizedOptions.pagination; + if (!is_1.default.object(pagination)) { + throw new TypeError('`options.pagination` must be implemented'); + } + const all = []; + let { countLimit } = pagination; + let numberOfRequests = 0; + while (numberOfRequests < pagination.requestLimit) { + if (numberOfRequests !== 0) { + // eslint-disable-next-line no-await-in-loop + await delay(pagination.backoff); + } + // TODO: Throw when result is not an instance of Response + // eslint-disable-next-line no-await-in-loop + const result = (await got(normalizedOptions)); + // eslint-disable-next-line no-await-in-loop + const parsed = await pagination.transform(result); + const current = []; + for (const item of parsed) { + if (pagination.filter(item, all, current)) { + if (!pagination.shouldContinue(item, all, current)) { + return; + } + yield item; + if (pagination.stackAllItems) { + all.push(item); + } + current.push(item); + if (--countLimit <= 0) { + return; + } + } + } + const optionsToMerge = pagination.paginate(result, all, current); + if (optionsToMerge === false) { + return; + } + if (optionsToMerge === result.request.options) { + normalizedOptions = result.request.options; + } + else if (optionsToMerge !== undefined) { + normalizedOptions = normalizeArguments(undefined, optionsToMerge, normalizedOptions); + } + numberOfRequests++; + } + }); + got.paginate = ((url, options) => { + return paginateEach(url, options); + }); + got.paginate.all = (async (url, options) => { + const results = []; + for await (const item of got.paginate(url, options)) { + results.push(item); + } + return results; + }); + // For those who like very descriptive names + got.paginate.each = paginateEach; + // Stream API + got.stream = ((url, options) => got(url, { ...options, isStream: true })); + // Shortcuts + for (const method of aliases) { + got[method] = ((url, options) => got(url, { ...options, method })); + got.stream[method] = ((url, options) => { + return got(url, { ...options, method, isStream: true }); + }); + } + Object.assign(got, { ...errors, mergeOptions }); + Object.defineProperty(got, 'defaults', { + value: defaults.mutableDefaults ? defaults : deep_freeze_1.default(defaults), + writable: defaults.mutableDefaults, + configurable: defaults.mutableDefaults, + enumerable: true + }); + return got; +}; +exports.default = create; +__exportStar(__webpack_require__(839), exports); + + +/***/ }), + +/***/ 325: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +const PassThrough = __webpack_require__(413).PassThrough; +const mimicResponse = __webpack_require__(89); + +const cloneResponse = response => { + if (!(response && response.pipe)) { + throw new TypeError('Parameter `response` must be a response stream.'); + } + + const clone = new PassThrough(); + mimicResponse(response, clone); + + return response.pipe(clone); +}; + +module.exports = cloneResponse; + + +/***/ }), + +/***/ 350: +/***/ (function(module) { + +(function(f){if(true){module.exports=f()}else { var g; }})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=require,i=0;i 0) { + words += '-' + lookup(remainder, false, ord); + } else if (ord) { + words = words.substring(0, words.length - 1) + 'ieth'; + } + } else if (num < 1000) { + const hundreds = Math.floor(num / 100); + const remainder = num % 100; + words = (prev ? ', ' : '') + few[hundreds] + ' Hundred'; + if (remainder > 0) { + words += lookup(remainder, true, ord); + } else if (ord) { + words += 'th'; + } + } else { + var mag = Math.floor(Math.log10(num) / 3); + if (mag > magnitudes.length) { + mag = magnitudes.length; // the largest word + } + const factor = Math.pow(10, mag * 3); + const mant = Math.floor(num / factor); + const remainder = num - mant * factor; + words = (prev ? ', ' : '') + lookup(mant, false, false) + ' ' + magnitudes[mag - 1]; + if (remainder > 0) { + words += lookup(remainder, true, ord); + } else if (ord) { + words += 'th'; + } + } + return words; + }; + + var words = lookup(value, false, ordinal); + return words; + } + + const wordValues = {}; + few.forEach(function (word, index) { + wordValues[word.toLowerCase()] = index; + }); + ordinals.forEach(function (word, index) { + wordValues[word.toLowerCase()] = index; + }); + decades.forEach(function (word, index) { + const lword = word.toLowerCase(); + wordValues[lword] = (index + 2) * 10; + wordValues[lword.substring(0, word.length - 1) + 'ieth'] = wordValues[lword]; + }); + wordValues.hundredth = 100; + magnitudes.forEach(function (word, index) { + const lword = word.toLowerCase(); + const val = Math.pow(10, (index + 1) * 3); + wordValues[lword] = val; + wordValues[lword + 'th'] = val; + }); + + /** + * Converts a number in english words to numeric value + * @param {string} text - the number in words + * @returns {number} - the numeric value + */ + function wordsToNumber(text) { + const parts = text.split(/,\s|\sand\s|[\s\\-]/); + const values = parts.map(part => wordValues[part]); + let segs = [0]; + values.forEach(value => { + if (value < 100) { + let top = segs.pop(); + if (top >= 1000) { + segs.push(top); + top = 0; + } + segs.push(top + value); + } else { + segs.push(segs.pop() * value); + } + }); + const result = segs.reduce((a, b) => a + b, 0); + return result; + } + + const romanNumerals = [ + [1000, 'm'], + [900, 'cm'], + [500, 'd'], + [400, 'cd'], + [100, 'c'], + [90, 'xc'], + [50, 'l'], + [40, 'xl'], + [10, 'x'], + [9, 'ix'], + [5, 'v'], + [4, 'iv'], + [1, 'i'] + ]; + + const romanValues = {'M': 1000, 'D': 500, 'C': 100, 'L': 50, 'X': 10, 'V': 5, 'I': 1}; + + /** + * converts a number to roman numerals + * @param {number} value - the number + * @returns {string} - the number in roman numerals + */ + function decimalToRoman(value) { + for (var index = 0; index < romanNumerals.length; index++) { + const numeral = romanNumerals[index]; + if (value >= numeral[0]) { + return numeral[1] + decimalToRoman(value - numeral[0]); + } + } + return ''; + } + + /** + * converts roman numerals to a number + * @param {string} roman - roman number + * @returns {number} - the numeric value + */ + function romanToDecimal(roman) { + var decimal = 0; + var max = 1; + for (var i = roman.length - 1; i >= 0; i--) { + const digit = roman[i]; + const value = romanValues[digit]; + if (value < max) { + decimal -= value; + } else { + max = value; + decimal += value; + } + } + return decimal; + } + + /** + * converts a number to spreadsheet style letters + * @param {number} value - the number + * @param {string} aChar - the character representing the start of the sequence, e.g. 'A' + * @returns {string} - the letters + */ + function decimalToLetters(value, aChar) { + var letters = []; + var aCode = aChar.charCodeAt(0); + while (value > 0) { + letters.unshift(String.fromCharCode((value - 1) % 26 + aCode)); + value = Math.floor((value - 1) / 26); + } + return letters.join(''); + } + + /** + * converts spreadsheet style letters to a number + * @param {string} letters - the letters + * @param {string} aChar - the character representing the start of the sequence, e.g. 'A' + * @returns {number} - the numeric value + */ + function lettersToDecimal(letters, aChar) { + var aCode = aChar.charCodeAt(0); + var decimal = 0; + for (var i = 0; i < letters.length; i++) { + decimal += (letters.charCodeAt(letters.length - i - 1) - aCode + 1) * Math.pow(26, i); + } + return decimal; + } + + /** + * Formats an integer as specified by the XPath fn:format-integer function + * See https://www.w3.org/TR/xpath-functions-31/#func-format-integer + * @param {number} value - the number to be formatted + * @param {string} picture - the picture string that specifies the format + * @returns {string} - the formatted number + */ + function formatInteger(value, picture) { + if (typeof value === 'undefined') { + return undefined; + } + + value = Math.floor(value); + + const format = analyseIntegerPicture(picture); + return _formatInteger(value, format); + } + + const formats = { + DECIMAL: 'decimal', + LETTERS: 'letters', + ROMAN: 'roman', + WORDS: 'words', + SEQUENCE: 'sequence' + }; + + const tcase = { + UPPER: 'upper', + LOWER: 'lower', + TITLE: 'title' + }; + + /** + * formats an integer using a preprocessed representation of the picture string + * @param {number} value - the number to be formatted + * @param {object} format - the preprocessed representation of the pucture string + * @returns {string} - the formatted number + * @private + */ + function _formatInteger(value, format) { + let formattedInteger; + const negative = value < 0; + value = Math.abs(value); + switch (format.primary) { + case formats.LETTERS: + formattedInteger = decimalToLetters(value, format.case === tcase.UPPER ? 'A' : 'a'); + break; + case formats.ROMAN: + formattedInteger = decimalToRoman(value); + if (format.case === tcase.UPPER) { + formattedInteger = formattedInteger.toUpperCase(); + } + break; + case formats.WORDS: + formattedInteger = numberToWords(value, format.ordinal); + if (format.case === tcase.UPPER) { + formattedInteger = formattedInteger.toUpperCase(); + } else if (format.case === tcase.LOWER) { + formattedInteger = formattedInteger.toLowerCase(); + } + break; + case formats.DECIMAL: + formattedInteger = '' + value; + // TODO use functionPad + var padLength = format.mandatoryDigits - formattedInteger.length; + if (padLength > 0) { + var padding = (new Array(padLength + 1)).join('0'); + formattedInteger = padding + formattedInteger; + } + if (format.zeroCode !== 0x30) { + formattedInteger = Array.from(formattedInteger).map(code => { + return String.fromCodePoint(code.codePointAt(0) + format.zeroCode - 0x30); + }).join(''); + } + // insert the grouping-separator-signs, if any + if (format.regular) { + const n = Math.floor((formattedInteger.length - 1) / format.groupingSeparators.position); + for (let ii = n; ii > 0; ii--) { + const pos = formattedInteger.length - ii * format.groupingSeparators.position; + formattedInteger = formattedInteger.substr(0, pos) + format.groupingSeparators.character + formattedInteger.substr(pos); + } + } else { + format.groupingSeparators.reverse().forEach(separator => { + const pos = formattedInteger.length - separator.position; + formattedInteger = formattedInteger.substr(0, pos) + separator.character + formattedInteger.substr(pos); + }); + } + + if (format.ordinal) { + var suffix123 = {'1': 'st', '2': 'nd', '3': 'rd'}; + var lastDigit = formattedInteger[formattedInteger.length - 1]; + var suffix = suffix123[lastDigit]; + if (!suffix || (formattedInteger.length > 1 && formattedInteger[formattedInteger.length - 2] === '1')) { + suffix = 'th'; + } + formattedInteger = formattedInteger + suffix; + } + break; + case formats.SEQUENCE: + throw { + code: 'D3130', + value: format.token + }; + } + if (negative) { + formattedInteger = '-' + formattedInteger; + } + + return formattedInteger; + } + + //TODO what about decimal groups in the unicode supplementary planes (surrogate pairs) ??? + const decimalGroups = [0x30, 0x0660, 0x06F0, 0x07C0, 0x0966, 0x09E6, 0x0A66, 0x0AE6, 0x0B66, 0x0BE6, 0x0C66, 0x0CE6, 0x0D66, 0x0DE6, 0x0E50, 0x0ED0, 0x0F20, 0x1040, 0x1090, 0x17E0, 0x1810, 0x1946, 0x19D0, 0x1A80, 0x1A90, 0x1B50, 0x1BB0, 0x1C40, 0x1C50, 0xA620, 0xA8D0, 0xA900, 0xA9D0, 0xA9F0, 0xAA50, 0xABF0, 0xFF10]; + + /** + * preprocesses the picture string + * @param {string} picture - picture string + * @returns {{type: string, primary: string, case: string, ordinal: boolean}} - analysed picture + */ + function analyseIntegerPicture(picture) { + const format = { + type: 'integer', + primary: formats.DECIMAL, + case: tcase.LOWER, + ordinal: false + }; + + let primaryFormat, formatModifier; + const semicolon = picture.lastIndexOf(';'); + if (semicolon === -1) { + primaryFormat = picture; + } else { + primaryFormat = picture.substring(0, semicolon); + formatModifier = picture.substring(semicolon + 1); + if (formatModifier[0] === 'o') { + format.ordinal = true; + } + } + + /* eslnt-disable-next no-fallthrough */ + switch (primaryFormat) { + case 'A': + format.case = tcase.UPPER; + /* eslnt-disable-next-line no-fallthrough */ + case 'a': + format.primary = formats.LETTERS; + break; + case 'I': + format.case = tcase.UPPER; + /* eslnt-disable-next-line no-fallthrough */ + case 'i': + format.primary = formats.ROMAN; + break; + case 'W': + format.case = tcase.UPPER; + format.primary = formats.WORDS; + break; + case 'Ww': + format.case = tcase.TITLE; + format.primary = formats.WORDS; + break; + case 'w': + format.primary = formats.WORDS; + break; + default: { + // this is a decimal-digit-pattern if it contains a decimal digit (from any unicode decimal digit group) + let zeroCode = null; + let mandatoryDigits = 0; + let optionalDigits = 0; + let groupingSeparators = []; + let separatorPosition = 0; + const formatCodepoints = Array.from(primaryFormat, c => c.codePointAt(0)).reverse(); // reverse the array to determine positions of grouping-separator-signs + formatCodepoints.forEach((codePoint) => { + // step though each char in the picture to determine the digit group + let digit = false; + for (let ii = 0; ii < decimalGroups.length; ii++) { + const group = decimalGroups[ii]; + if (codePoint >= group && codePoint <= group + 9) { + // codepoint is part of this decimal group + digit = true; + mandatoryDigits++; + separatorPosition++; + if (zeroCode === null) { + zeroCode = group; + } else if (group !== zeroCode) { + // error! different decimal groups in the same pattern + throw { + code: 'D3131' + }; + } + break; + } + } + if (!digit) { + if (codePoint === 0x23) { // # - optional-digit-sign + separatorPosition++; + optionalDigits++; + } else { + // neither a decimal-digit-sign ot optional-digit-sign, assume it is a grouping-separator-sign + groupingSeparators.push({ + position: separatorPosition, + character: String.fromCodePoint(codePoint) + }); + } + } + }); + if (mandatoryDigits > 0) { + format.primary = formats.DECIMAL; + // TODO validate decimal-digit-pattern + + // the decimal digit family (codepoint offset) + format.zeroCode = zeroCode; + // the number of mandatory digits + format.mandatoryDigits = mandatoryDigits; + // the number of optional digits + format.optionalDigits = optionalDigits; + // grouping separator template + // are the grouping-separator-signs 'regular'? + const regularRepeat = function (separators) { + // are the grouping positions regular? i.e. same interval between each of them + // is there at least one separator? + if (separators.length === 0) { + return 0; + } + // are all the characters the same? + const sepChar = separators[0].character; + for (let ii = 1; ii < separators.length; ii++) { + if (separators[ii].character !== sepChar) { + return 0; + } + } + // are they equally spaced? + const indexes = separators.map(separator => separator.position); + const gcd = function (a, b) { + return b === 0 ? a : gcd(b, a % b); + }; + // find the greatest common divisor of all the positions + const factor = indexes.reduce(gcd); + // is every position separated by this divisor? If so, it's regular + for (let index = 1; index <= indexes.length; index++) { + if (indexes.indexOf(index * factor) === -1) { + return 0; + } + } + return factor; + }; + + const regular = regularRepeat(groupingSeparators); + if (regular > 0) { + format.regular = true; + format.groupingSeparators = { + position: regular, + character: groupingSeparators[0].character + }; + } else { + format.regular = false; + format.groupingSeparators = groupingSeparators; + } + + } else { + // this is a 'numbering sequence' which the spec says is implementation-defined + // this implementation doesn't support any numbering sequences at the moment. + format.primary = formats.SEQUENCE; + format.token = primaryFormat; + } + } + } + + return format; + } + + const defaultPresentationModifiers = { + Y: '1', M: '1', D: '1', d: '1', F: 'n', W: '1', w: '1', X: '1', x: '1', H: '1', h: '1', + P: 'n', m: '01', s: '01', f: '1', Z: '01:01', z: '01:01', C: 'n', E: 'n' + }; + + // §9.8.4.1 the format specifier is an array of string literals and variable markers + /** + * analyse the date-time picture string + * @param {string} picture - picture string + * @returns {{type: string, parts: Array}} - the analysed string + */ + function analyseDateTimePicture(picture) { + var spec = []; + const format = { + type: 'datetime', + parts: spec + }; + const addLiteral = function (start, end) { + if (end > start) { + let literal = picture.substring(start, end); + // replace any doubled ]] with single ] + // what if there are instances of single ']' ? - the spec doesn't say + literal = literal.split(']]').join(']'); + spec.push({type: 'literal', value: literal}); + } + }; + + var start = 0, pos = 0; + while (pos < picture.length) { + if (picture.charAt(pos) === '[') { + // check it's not a doubled [[ + if (picture.charAt(pos + 1) === '[') { + // literal [ + addLiteral(start, pos); + spec.push({type: 'literal', value: '['}); + pos += 2; + start = pos; + continue; + } + // start of variable marker + // push the string literal (if there is one) onto the array + addLiteral(start, pos); + start = pos; + // search forward to closing ] + pos = picture.indexOf(']', start); + // TODO handle error case if pos === -1 + if(pos === -1) { + // error - no closing bracket + throw { + code: 'D3135' + }; + } + let marker = picture.substring(start + 1, pos); + // whitespace within a variable marker is ignored (i.e. remove it) + marker = marker.split(/\s+/).join(''); + var def = { + type: 'marker', + component: marker.charAt(0) // 1. The component specifier is always present and is always a single letter. + }; + var comma = marker.lastIndexOf(','); // 2. The width modifier may be recognized by the presence of a comma + var presMod; // the presentation modifiers + if (comma !== -1) { + // §9.8.4.2 The Width Modifier + const widthMod = marker.substring(comma + 1); + const dash = widthMod.indexOf('-'); + let min, max; + const parseWidth = function (wm) { + if (typeof wm === 'undefined' || wm === '*') { + return undefined; + } else { + // TODO validate wm is an unsigned int + return parseInt(wm); + } + }; + if (dash === -1) { + min = widthMod; + } else { + min = widthMod.substring(0, dash); + max = widthMod.substring(dash + 1); + } + const widthDef = { + min: parseWidth(min), + max: parseWidth(max) + }; + def.width = widthDef; + presMod = marker.substring(1, comma); + } else { + presMod = marker.substring(1); + } + if (presMod.length === 1) { + def.presentation1 = presMod; // first presentation modifier + //TODO validate the first presentation modifier - it's either N, n, Nn or it passes analyseIntegerPicture + } else if (presMod.length > 1) { + var lastChar = presMod.charAt(presMod.length - 1); + if ('atco'.indexOf(lastChar) !== -1) { + def.presentation2 = lastChar; + if (lastChar === 'o') { + def.ordinal = true; + } + // 'c' means 'cardinal' and is the default (i.e. not 'ordinal') + // 'a' & 't' are ignored (not sure of their relevance to English numbering) + def.presentation1 = presMod.substring(0, presMod.length - 1); + } else { + def.presentation1 = presMod; + //TODO validate the first presentation modifier - it's either N, n, Nn or it passes analyseIntegerPicture, + // doesn't use ] as grouping separator, and if grouping separator is , then must have width modifier + } + } else { + // no presentation modifier specified - apply the default; + def.presentation1 = defaultPresentationModifiers[def.component]; + } + if (typeof def.presentation1 === 'undefined') { + // unknown component specifier + throw { + code: 'D3132', + value: def.component + }; + } + if (def.presentation1[0] === 'n') { + def.names = tcase.LOWER; + } else if (def.presentation1[0] === 'N') { + if (def.presentation1[1] === 'n') { + def.names = tcase.TITLE; + } else { + def.names = tcase.UPPER; + } + } else if ('YMDdFWwXxHhmsf'.indexOf(def.component) !== -1) { + var integerPattern = def.presentation1; + if (def.presentation2) { + integerPattern += ';' + def.presentation2; + } + def.integerFormat = analyseIntegerPicture(integerPattern); + if (def.width && def.width.min !== undefined) { + if (def.integerFormat.mandatoryDigits < def.width.min) { + def.integerFormat.mandatoryDigits = def.width.min; + } + } + if (def.component === 'Y') { + // §9.8.4.4 + def.n = -1; + if (def.width && def.width.max !== undefined) { + def.n = def.width.max; + def.integerFormat.mandatoryDigits = def.n; + } else { + var w = def.integerFormat.mandatoryDigits + def.integerFormat.optionalDigits; + if (w >= 2) { + def.n = w; + } + } + } + } + if (def.component === 'Z' || def.component === 'z') { + def.integerFormat = analyseIntegerPicture(def.presentation1); + } + spec.push(def); + start = pos + 1; + } + pos++; + } + addLiteral(start, pos); + return format; + } + + const days = ['', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; + const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + const millisInADay = 1000 * 60 * 60 * 24; + + const startOfFirstWeek = function (ym) { + // ISO 8601 defines the first week of the year to be the week that contains the first Thursday + // XPath F&O extends this same definition for the first week of a month + // the week starts on a Monday - calculate the millis for the start of the first week + // millis for given 1st Jan of that year (at 00:00 UTC) + const jan1 = Date.UTC(ym.year, ym.month); + var dayOfJan1 = (new Date(jan1)).getUTCDay(); + if (dayOfJan1 === 0) { + dayOfJan1 = 7; + } + // if Jan 1 is Fri, Sat or Sun, then add the number of days (in millis) to jan1 to get the start of week 1 + return dayOfJan1 > 4 ? jan1 + (8 - dayOfJan1) * millisInADay : jan1 - (dayOfJan1 - 1) * millisInADay; + }; + + const yearMonth = function (year, month) { + return { + year: year, + month: month, + nextMonth: function () { + return (month === 11) ? yearMonth(year + 1, 0) : yearMonth(year, month + 1); + }, + previousMonth: function () { + return (month === 0) ? yearMonth(year - 1, 11) : yearMonth(year, month - 1); + }, + nextYear: function () { + return yearMonth(year + 1, month); + }, + previousYear: function () { + return yearMonth(year - 1, month); + } + }; + }; + + const deltaWeeks = function (start, end) { + return (end - start) / (millisInADay * 7) + 1; + }; + + const getDateTimeFragment = (date, component) => { + let componentValue; + switch (component) { + case 'Y': // year + componentValue = date.getUTCFullYear(); + break; + case 'M': // month in year + componentValue = date.getUTCMonth() + 1; + break; + case 'D': // day in month + componentValue = date.getUTCDate(); + break; + case 'd': { // day in year + // millis for given date (at 00:00 UTC) + const today = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()); + // millis for given 1st Jan of that year (at 00:00 UTC) + const firstJan = Date.UTC(date.getUTCFullYear(), 0); + componentValue = (today - firstJan) / millisInADay + 1; + break; + } + case 'F': // day of week + componentValue = date.getUTCDay(); + if (componentValue === 0) { + // ISO 8601 defines days 1-7: Mon-Sun + componentValue = 7; + } + break; + case 'W': { // week in year + const thisYear = yearMonth(date.getUTCFullYear(), 0); + const startOfWeek1 = startOfFirstWeek(thisYear); + const today = Date.UTC(thisYear.year, date.getUTCMonth(), date.getUTCDate()); + let week = deltaWeeks(startOfWeek1, today); + if (week > 52) { + // might be first week of the following year + const startOfFollowingYear = startOfFirstWeek(thisYear.nextYear()); + if (today >= startOfFollowingYear) { + week = 1; + } + } else if (week < 1) { + // must be end of the previous year + const startOfPreviousYear = startOfFirstWeek(thisYear.previousYear()); + week = deltaWeeks(startOfPreviousYear, today); + } + componentValue = Math.floor(week); + break; + } + case 'w': { // week in month + const thisMonth = yearMonth(date.getUTCFullYear(), date.getUTCMonth()); + const startOfWeek1 = startOfFirstWeek(thisMonth); + const today = Date.UTC(thisMonth.year, thisMonth.month, date.getUTCDate()); + let week = deltaWeeks(startOfWeek1, today); + if (week > 4) { + // might be first week of the following month + const startOfFollowingMonth = startOfFirstWeek(thisMonth.nextMonth()); + if (today >= startOfFollowingMonth) { + week = 1; + } + } else if (week < 1) { + // must be end of the previous month + const startOfPreviousMonth = startOfFirstWeek(thisMonth.previousMonth()); + week = deltaWeeks(startOfPreviousMonth, today); + } + componentValue = Math.floor(week); + break; + } + case 'X': { // ISO week-numbering year + // Extension: The F&O spec says nothing about how to access the year associated with the week-of-the-year + // e.g. Sat 1 Jan 2005 is in the 53rd week of 2004. + // The 'W' component specifier gives 53, but 'Y' will give 2005. + // I propose to add 'X' as the component specifier to give the ISO week-numbering year (2004 in this example) + const thisYear = yearMonth(date.getUTCFullYear(), 0); + const startOfISOYear = startOfFirstWeek(thisYear); + const endOfISOYear = startOfFirstWeek(thisYear.nextYear()); + const now = date.getTime(); + if (now < startOfISOYear) { + componentValue = thisYear.year - 1; + } else if (now >= endOfISOYear) { + componentValue = thisYear.year + 1; + } else { + componentValue = thisYear.year; + } + break; + } + case 'x': { // ISO week-numbering month + // Extension: The F&O spec says nothing about how to access the month associated with the week-of-the-month + // e.g. Sat 1 Jan 2005 is in the 5th week of December 2004. + // The 'w' component specifier gives 5, but 'W' will give January and 'Y' will give 2005. + // I propose to add 'x' as the component specifier to give the 'week-numbering' month (December in this example) + const thisMonth = yearMonth(date.getUTCFullYear(), date.getUTCMonth()); + const startOfISOMonth = startOfFirstWeek(thisMonth); + const nextMonth = thisMonth.nextMonth(); + const endOfISOMonth = startOfFirstWeek(nextMonth); + const now = date.getTime(); + if (now < startOfISOMonth) { + componentValue = thisMonth.previousMonth().month + 1; + } else if (now >= endOfISOMonth) { + componentValue = nextMonth.month + 1; + } else { + componentValue = thisMonth.month + 1; + } + break; + } + case 'H': // hour in day (24 hours) + componentValue = date.getUTCHours(); + break; + case 'h': // hour in half-day (12 hours) + componentValue = date.getUTCHours(); + componentValue = componentValue % 12; + if (componentValue === 0) { + componentValue = 12; + } + break; + case 'P': // am/pm marker + componentValue = date.getUTCHours() >= 12 ? 'pm' : 'am'; + break; + case 'm': // minute in hour + componentValue = date.getUTCMinutes(); + break; + case 's': // second in minute + componentValue = date.getUTCSeconds(); + break; + case 'f': // fractional seconds + componentValue = date.getUTCMilliseconds(); + break; + case 'Z': // timezone + case 'z': + // since the date object is constructed from epoch millis, the TZ component is always be UTC. + break; + case 'C': // calendar name + componentValue = 'ISO'; + break; + case 'E': // era + componentValue = 'ISO'; + break; + } + return componentValue; + }; + + const iso8601Spec = analyseDateTimePicture('[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01].[f001][Z01:01t]'); + + /** + * formats the date/time as specified by the XPath fn:format-dateTime function + * @param {number} millis - the timestamp to be formatted, in millis since the epoch + * @param {string} picture - the picture string that specifies the format + * @param {string} timezone - the timezone to use + * @returns {string} - the formatted timestamp + */ + function formatDateTime(millis, picture, timezone) { + var offsetHours = 0; + var offsetMinutes = 0; + + if (typeof timezone !== 'undefined') { + // parse the hour and minute offsets + // assume for now the format supplied is +hhmm + const offset = parseInt(timezone); + offsetHours = Math.floor(offset / 100); + offsetMinutes = offset % 100; + } + + var formatComponent = function (date, markerSpec) { + var componentValue = getDateTimeFragment(date, markerSpec.component); + + // §9.8.4.3 Formatting Integer-Valued Date/Time Components + if ('YMDdFWwXxHhms'.indexOf(markerSpec.component) !== -1) { + if (markerSpec.component === 'Y') { + // §9.8.4.4 Formatting the Year Component + if (markerSpec.n !== -1) { + componentValue = componentValue % Math.pow(10, markerSpec.n); + } + } + if (markerSpec.names) { + if (markerSpec.component === 'M' || markerSpec.component === 'x') { + componentValue = months[componentValue - 1]; + } else if (markerSpec.component === 'F') { + componentValue = days[componentValue]; + } else { + throw { + code: 'D3133', + value: markerSpec.component + }; + } + if (markerSpec.names === tcase.UPPER) { + componentValue = componentValue.toUpperCase(); + } else if (markerSpec.names === tcase.LOWER) { + componentValue = componentValue.toLowerCase(); + } + if (markerSpec.width && componentValue.length > markerSpec.width.max) { + componentValue = componentValue.substring(0, markerSpec.width.max); + } + } else { + componentValue = _formatInteger(componentValue, markerSpec.integerFormat); + } + } else if (markerSpec.component === 'f') { + // TODO §9.8.4.5 Formatting Fractional Seconds + componentValue = _formatInteger(componentValue, markerSpec.integerFormat); + } else if (markerSpec.component === 'Z' || markerSpec.component === 'z') { + // §9.8.4.6 Formatting timezones + const offset = offsetHours * 100 + offsetMinutes; + if (markerSpec.integerFormat.regular) { + componentValue = _formatInteger(offset, markerSpec.integerFormat); + } else { + const numDigits = markerSpec.integerFormat.mandatoryDigits; + if (numDigits === 1 || numDigits === 2) { + componentValue = _formatInteger(offsetHours, markerSpec.integerFormat); + if (offsetMinutes !== 0) { + componentValue += ':' + formatInteger(offsetMinutes, '00'); + } + } else if (numDigits === 3 || numDigits === 4) { + componentValue = _formatInteger(offset, markerSpec.integerFormat); + } else { + throw { + code: 'D3134', + value: numDigits + }; + } + } + if (offset >= 0) { + componentValue = '+' + componentValue; + } + if (markerSpec.component === 'z') { + componentValue = 'GMT' + componentValue; + } + if (offset === 0 && markerSpec.presentation2 === 't') { + componentValue = 'Z'; + } + } + return componentValue; + }; + + let formatSpec; + if(typeof picture === 'undefined') { + // default to ISO 8601 format + formatSpec = iso8601Spec; + } else { + formatSpec = analyseDateTimePicture(picture); + } + + const offsetMillis = (60 * offsetHours + offsetMinutes) * 60 * 1000; + const dateTime = new Date(millis + offsetMillis); + + let result = ''; + formatSpec.parts.forEach(function (part) { + if (part.type === 'literal') { + result += part.value; + } else { + result += formatComponent(dateTime, part); + } + }); + + return result; + } + + /** + * Generate a regex to parse integers or timestamps + * @param {object} formatSpec - object representing the format + * @returns {object} - regex + */ + function generateRegex(formatSpec) { + var matcher = {}; + if (formatSpec.type === 'datetime') { + matcher.type = 'datetime'; + matcher.parts = formatSpec.parts.map(function (part) { + var res = {}; + if (part.type === 'literal') { + res.regex = part.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } else if (part.integerFormat) { + res = generateRegex(part.integerFormat); + } else { + // must be a month or day name + res.regex = '[a-zA-Z]+'; + var lookup = {}; + if (part.component === 'M' || part.component === 'x') { + // months + months.forEach(function (name, index) { + if (part.width && part.width.max) { + lookup[name.substring(0, part.width.max)] = index + 1; + } else { + lookup[name] = index + 1; + } + }); + } else if (part.component === 'F') { + // days + days.forEach(function (name, index) { + if (index > 0) { + if (part.width && part.width.max) { + lookup[name.substring(0, part.width.max)] = index; + } else { + lookup[name] = index; + } + } + }); + } else if (part.component === 'P') { + lookup = {'am': 0, 'AM': 0, 'pm': 1, 'PM': 1}; + } else { + // unsupported 'name' option for this component + throw { + code: 'D3133', + value: part.component + }; + } + res.parse = function (value) { + return lookup[value]; + }; + } + res.component = part.component; + return res; + }); + } else { // type === 'integer' + matcher.type = 'integer'; + const isUpper = formatSpec.case === tcase.UPPER; + switch (formatSpec.primary) { + case formats.LETTERS: + matcher.regex = isUpper ? '[A-Z]+' : '[a-z]+'; + matcher.parse = function (value) { + return lettersToDecimal(value, isUpper ? 'A' : 'a'); + }; + break; + case formats.ROMAN: + matcher.regex = isUpper ? '[MDCLXVI]+' : '[mdclxvi]+'; + matcher.parse = function (value) { + return romanToDecimal(isUpper ? value : value.toUpperCase()); + }; + break; + case formats.WORDS: + matcher.regex = '(?:' + Object.keys(wordValues).concat('and', '[\\-, ]').join('|') + ')+'; + matcher.parse = function (value) { + return wordsToNumber(value.toLowerCase()); + }; + break; + case formats.DECIMAL: + matcher.regex = '[0-9]+'; + if (formatSpec.ordinal) { + // ordinals + matcher.regex += '(?:th|st|nd|rd)'; + } + matcher.parse = function (value) { + let digits = value; + if (formatSpec.ordinal) { + // strip off the suffix + digits = value.substring(0, value.length - 2); + } + // strip out the separators + if (formatSpec.regular) { + digits = digits.split(',').join(''); + } else { + formatSpec.groupingSeparators.forEach(sep => { + digits = digits.split(sep.character).join(''); + }); + } + if (formatSpec.zeroCode !== 0x30) { + // apply offset + digits = digits.split('').map(char => String.fromCodePoint(char.codePointAt(0) - formatSpec.zeroCode + 0x30)).join(''); + } + return parseInt(digits); + }; + break; + case formats.SEQUENCE: + throw { + code: 'D3130', + value: formatSpec.token + }; + } + + } + return matcher; + } + + /** + * parse a string containing an integer as specified by the picture string + * @param {string} value - the string to parse + * @param {string} picture - the picture string + * @returns {number} - the parsed number + */ + function parseInteger(value, picture) { + if (typeof value === 'undefined') { + return undefined; + } + + const formatSpec = analyseIntegerPicture(picture); + const matchSpec = generateRegex(formatSpec); + //const fullRegex = '^' + matchSpec.regex + '$'; + //const matcher = new RegExp(fullRegex); + // TODO validate input based on the matcher regex + const result = matchSpec.parse(value); + return result; + } + + /** + * parse a string containing a timestamp as specified by the picture string + * @param {string} timestamp - the string to parse + * @param {string} picture - the picture string + * @returns {number} - the parsed timestamp in millis since the epoch + */ + function parseDateTime(timestamp, picture) { + const formatSpec = analyseDateTimePicture(picture); + const matchSpec = generateRegex(formatSpec); + const fullRegex = '^' + matchSpec.parts.map(part => '(' + part.regex + ')').join('') + '$'; + + const matcher = new RegExp(fullRegex, 'i'); // TODO can cache this against the picture + var info = matcher.exec(timestamp); + if (info !== null) { + // validate what we've just parsed - do we have enough information to create a timestamp? + // rules: + // The date is specified by one of: + // {Y, M, D} (dateA) + // or {Y, d} (dateB) + // or {Y, x, w, F} (dateC) + // or {X, W, F} (dateD) + // The time is specified by one of: + // {H, m, s, f} (timeA) + // or {P, h, m, s, f} (timeB) + // All sets can have an optional Z + // To create a timestamp (epoch millis) we need both date and time, but we can default missing + // information according to the following rules: + // - line up one combination of the above from date, and one from time, most significant value (MSV) to least significant (LSV + // - for the values that have been captured, if there are any gaps between MSV and LSV, then throw an error + // (e.g.) if hour and seconds, but not minutes is given - throw + // (e.g.) if month, hour and minutes, but not day-of-month is given - throw + // - anything right of the LSV should be defaulted to zero + // (e.g.) if hour and minutes given, default seconds and fractional seconds to zero + // (e.g.) if date only given, default the time to 0:00:00.000 (midnight) + // - anything left of the MSV should be defaulted to the value of that component returned by $now() + // (e.g.) if time only given, default the date to today + // (e.g.) if month and date given, default to this year (and midnight, by previous rule) + // -- default values for X, x, W, w, F will be derived from the values returned by $now() + + // implement the above rules + // determine which of the above date/time combinations we have by using bit masks + + // Y X M x W w d D F P H h m s f Z + // dateA 1 0 1 0 0 0 0 1 ? 0 - must not appear + // dateB 1 0 0 0 0 0 1 0 ? 1 - can appear - relevant + // dateC 0 1 0 1 0 1 0 0 1 ? - can appear - ignored + // dateD 0 1 0 0 1 0 0 0 1 + // timeA 0 1 0 1 1 1 + // timeB 1 0 1 1 1 1 + + // create bitmasks based on the above + // date mask YXMxWwdD + const dmA = 161; // binary 10100001 + const dmB = 130; // binary 10000010 + const dmC = 84; // binary 01010100 + const dmD = 72; // binary 01001000 + // time mask PHhmsf + const tmA = 23; // binary 010111 + const tmB = 47; // binary 101111 + + const components = {}; + for (let i = 1; i < info.length; i++) { + const mpart = matchSpec.parts[i - 1]; + if (mpart.parse) { + components[mpart.component] = mpart.parse(info[i]); + } + } + + if(Object.getOwnPropertyNames(components).length === 0) { + // nothing specified + return undefined; + } + + let mask = 0; + + const shift = bit => { + mask <<= 1; + mask += bit ? 1 : 0; + }; + + const isType = type => { + // shouldn't match any 0's, must match at least one 1 + return !(~type & mask) && !!(type & mask); + }; + + 'YXMxWwdD'.split('').forEach(part => shift(components[part])); + + const dateA = isType(dmA); + const dateB = !dateA && isType(dmB); + const dateC = isType(dmC); + const dateD = !dateC && isType(dmD); + + mask = 0; + 'PHhmsf'.split('').forEach(part => shift(components[part])); + + const timeA = isType(tmA); + const timeB = !timeA && isType(tmB); + + // should only be zero or one date type and zero or one time type + + const dateComps = dateB ? 'YD' : dateC ? 'XxwF' : dateD? 'XWF' : 'YMD'; + const timeComps = timeB ? 'Phmsf' : 'Hmsf'; + + const comps = dateComps + timeComps; + + // step through the candidate parts from most significant to least significant + // default the most significant unspecified parts to current timestamp component + // default the least significant unspecified parts to zero + // if any gaps in between the specified parts, throw an error + + const now = this.environment.timestamp; // must get the fixed timestamp from jsonata + + let startSpecified = false; + let endSpecified = false; + comps.split('').forEach(part => { + if(typeof components[part] === 'undefined') { + if(startSpecified) { + // past the specified block - default to zero + components[part] = ('MDd'.indexOf(part) !== -1) ? 1 : 0; + endSpecified = true; + } else { + // haven't hit the specified block yet, default to current timestamp + components[part] = getDateTimeFragment(now, part); + } + } else { + startSpecified = true; + if(endSpecified) { + throw { + code: 'D3136' + }; + } + } + }); + + // validate and fill in components + if (components.M > 0) { + components.M -= 1; // Date.UTC requires a zero-indexed month + } else { + components.M = 0; // default to January + } + if (dateB) { + // millis for given 1st Jan of that year (at 00:00 UTC) + const firstJan = Date.UTC(components.Y, 0); + const offsetMillis = (components.d - 1) * 1000 * 60 * 60 * 24; + const derivedDate = new Date(firstJan + offsetMillis); + components.M = derivedDate.getUTCMonth(); + components.D = derivedDate.getUTCDate(); + } + if (dateC) { + // TODO implement this + // parsing this format not currently supported + throw { + code: 'D3136' + }; + } + if (dateD) { + // TODO implement this + // parsing this format (ISO week date) not currently supported + throw { + code: 'D3136' + }; + } + if (timeB) { + // 12hr to 24hr + components.H = components.h === 12 ? 0 : components.h; + if (components.P === 1) { + components.H += 12; + } + } + + var millis = Date.UTC(components.Y, components.M, components.D, components.H, components.m, components.s, components.f); + return millis; + } + } + + // Regular expression to match an ISO 8601 formatted timestamp + var iso8601regex = new RegExp('^\\d{4}(-[01]\\d)*(-[0-3]\\d)*(T[0-2]\\d:[0-5]\\d:[0-5]\\d)*(\\.\\d+)?([+-][0-2]\\d:?[0-5]\\d|Z)?$'); + + /** + * Converts an ISO 8601 timestamp to milliseconds since the epoch + * + * @param {string} timestamp - the timestamp to be converted + * @param {string} [picture] - the picture string defining the format of the timestamp (defaults to ISO 8601) + * @returns {Number} - milliseconds since the epoch + */ + function toMillis(timestamp, picture) { + // undefined inputs always return undefined + if(typeof timestamp === 'undefined') { + return undefined; + } + + if(typeof picture === 'undefined') { + if (!iso8601regex.test(timestamp)) { + throw { + stack: (new Error()).stack, + code: "D3110", + value: timestamp + }; + } + + return Date.parse(timestamp); + } else { + return parseDateTime.call(this, timestamp, picture); + } + } + + /** + * Converts milliseconds since the epoch to an ISO 8601 timestamp + * @param {Number} millis - milliseconds since the epoch to be converted + * @param {string} [picture] - the picture string defining the format of the timestamp (defaults to ISO 8601) + * @param {string} [timezone] - the timezone to format the timestamp in (defaults to UTC) + * @returns {String} - the formatted timestamp + */ + function fromMillis(millis, picture, timezone) { + // undefined inputs always return undefined + if(typeof millis === 'undefined') { + return undefined; + } + + return formatDateTime.call(this, millis, picture, timezone); + } + + return { + formatInteger, parseInteger, fromMillis, toMillis + }; +})(); + +module.exports = dateTime; + +},{}],2:[function(require,module,exports){ +(function (global){ +/** + * © Copyright IBM Corp. 2016, 2018 All Rights Reserved + * Project name: JSONata + * This project is licensed under the MIT License, see LICENSE + */ + +var utils = require('./utils'); + +const functions = (() => { + 'use strict'; + + var isNumeric = utils.isNumeric; + var isArrayOfStrings = utils.isArrayOfStrings; + var isArrayOfNumbers = utils.isArrayOfNumbers; + var createSequence = utils.createSequence; + var isSequence = utils.isSequence; + var isFunction = utils.isFunction; + var isLambda = utils.isLambda; + var isIterable = utils.isIterable; + var getFunctionArity = utils.getFunctionArity; + var deepEquals = utils.isDeepEqual; + + /** + * Sum function + * @param {Object} args - Arguments + * @returns {number} Total value of arguments + */ + function sum(args) { + // undefined inputs always return undefined + if (typeof args === 'undefined') { + return undefined; + } + + var total = 0; + args.forEach(function (num) { + total += num; + }); + return total; + } + + /** + * Count function + * @param {Object} args - Arguments + * @returns {number} Number of elements in the array + */ + function count(args) { + // undefined inputs always return undefined + if (typeof args === 'undefined') { + return 0; + } + + return args.length; + } + + /** + * Max function + * @param {Object} args - Arguments + * @returns {number} Max element in the array + */ + function max(args) { + // undefined inputs always return undefined + if (typeof args === 'undefined' || args.length === 0) { + return undefined; + } + + return Math.max.apply(Math, args); + } + + /** + * Min function + * @param {Object} args - Arguments + * @returns {number} Min element in the array + */ + function min(args) { + // undefined inputs always return undefined + if (typeof args === 'undefined' || args.length === 0) { + return undefined; + } + + return Math.min.apply(Math, args); + } + + /** + * Average function + * @param {Object} args - Arguments + * @returns {number} Average element in the array + */ + function average(args) { + // undefined inputs always return undefined + if (typeof args === 'undefined' || args.length === 0) { + return undefined; + } + + var total = 0; + args.forEach(function (num) { + total += num; + }); + return total / args.length; + } + + /** + * Stringify arguments + * @param {Object} arg - Arguments + * @param {boolean} [prettify] - Pretty print the result + * @returns {String} String from arguments + */ + function string(arg, prettify = false) { + // undefined inputs always return undefined + if (typeof arg === 'undefined') { + return undefined; + } + + var str; + + if (typeof arg === 'string') { + // already a string + str = arg; + } else if (isFunction(arg)) { + // functions (built-in and lambda convert to empty string + str = ''; + } else if (typeof arg === 'number' && !isFinite(arg)) { + throw { + code: "D3001", + value: arg, + stack: (new Error()).stack + }; + } else { + var space = prettify ? 2 : 0; + if(Array.isArray(arg) && arg.outerWrapper) { + arg = arg[0]; + } + str = JSON.stringify(arg, function (key, val) { + return (typeof val !== 'undefined' && val !== null && val.toPrecision && isNumeric(val)) ? Number(val.toPrecision(15)) : + (val && isFunction(val)) ? '' : val; + }, space); + } + return str; + } + + /** + * Create substring based on character number and length + * @param {String} str - String to evaluate + * @param {Integer} start - Character number to start substring + * @param {Integer} [length] - Number of characters in substring + * @returns {string|*} Substring + */ + function substring(str, start, length) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + var strArray = Array.from(str); + var strLength = strArray.length; + + if (strLength + start < 0) { + start = 0; + } + + if (typeof length !== 'undefined') { + if (length <= 0) { + return ''; + } + var end = start >= 0 ? start + length : strLength + start + length; + return strArray.slice(start, end).join(''); + } + + return strArray.slice(start).join(''); + } + + /** + * Create substring up until a character + * @param {String} str - String to evaluate + * @param {String} chars - Character to define substring boundary + * @returns {*} Substring + */ + function substringBefore(str, chars) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + var pos = str.indexOf(chars); + if (pos > -1) { + return str.substr(0, pos); + } else { + return str; + } + } + + /** + * Create substring after a character + * @param {String} str - String to evaluate + * @param {String} chars - Character to define substring boundary + * @returns {*} Substring + */ + function substringAfter(str, chars) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + var pos = str.indexOf(chars); + if (pos > -1) { + return str.substr(pos + chars.length); + } else { + return str; + } + } + + /** + * Lowercase a string + * @param {String} str - String to evaluate + * @returns {string} Lowercase string + */ + function lowercase(str) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + return str.toLowerCase(); + } + + /** + * Uppercase a string + * @param {String} str - String to evaluate + * @returns {string} Uppercase string + */ + function uppercase(str) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + return str.toUpperCase(); + } + + /** + * length of a string + * @param {String} str - string + * @returns {Number} The number of characters in the string + */ + function length(str) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + return Array.from(str).length; + } + + /** + * Normalize and trim whitespace within a string + * @param {string} str - string to be trimmed + * @returns {string} - trimmed string + */ + function trim(str) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + // normalize whitespace + var result = str.replace(/[ \t\n\r]+/gm, ' '); + if (result.charAt(0) === ' ') { + // strip leading space + result = result.substring(1); + } + if (result.charAt(result.length - 1) === ' ') { + // strip trailing space + result = result.substring(0, result.length - 1); + } + return result; + } + + /** + * Pad a string to a minimum width by adding characters to the start or end + * @param {string} str - string to be padded + * @param {number} width - the minimum width; +ve pads to the right, -ve pads to the left + * @param {string} [char] - the pad character(s); defaults to ' ' + * @returns {string} - padded string + */ + function pad(str, width, char) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + if (typeof char === 'undefined' || char.length === 0) { + char = ' '; + } + + var result; + var padLength = Math.abs(width) - length(str); + if (padLength > 0) { + var padding = (new Array(padLength + 1)).join(char); + if (char.length > 1) { + padding = substring(padding, 0, padLength); + } + if (width > 0) { + result = str + padding; + } else { + result = padding + str; + } + } else { + result = str; + } + return result; + } + + /** + * Evaluate the matcher function against the str arg + * + * @param {*} matcher - matching function (native or lambda) + * @param {string} str - the string to match against + * @returns {object} - structure that represents the match(es) + */ + function* evaluateMatcher(matcher, str) { + var result = matcher.apply(this, [str]); // eslint-disable-line no-useless-call + if(isIterable(result)) { + result = yield * result; + } + if(result && !(typeof result.start === 'number' || result.end === 'number' || Array.isArray(result.groups) || isFunction(result.next))) { + // the matcher function didn't return the correct structure + throw { + code: "T1010", + stack: (new Error()).stack, + }; + } + return result; + } + + /** + * Tests if the str contains the token + * @param {String} str - string to test + * @param {String} token - substring or regex to find + * @returns {Boolean} - true if str contains token + */ + function* contains(str, token) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + var result; + + if (typeof token === 'string') { + result = (str.indexOf(token) !== -1); + } else { + var matches = yield* evaluateMatcher(token, str); + result = (typeof matches !== 'undefined'); + } + + return result; + } + + /** + * Match a string with a regex returning an array of object containing details of each match + * @param {String} str - string + * @param {String} regex - the regex applied to the string + * @param {Integer} [limit] - max number of matches to return + * @returns {Array} The array of match objects + */ + function* match(str, regex, limit) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + // limit, if specified, must be a non-negative number + if (limit < 0) { + throw { + stack: (new Error()).stack, + value: limit, + code: 'D3040', + index: 3 + }; + } + + var result = createSequence(); + + if (typeof limit === 'undefined' || limit > 0) { + var count = 0; + var matches = yield* evaluateMatcher(regex, str); + if (typeof matches !== 'undefined') { + while (typeof matches !== 'undefined' && (typeof limit === 'undefined' || count < limit)) { + result.push({ + match: matches.match, + index: matches.start, + groups: matches.groups + }); + matches = yield* evaluateMatcher(matches.next); + count++; + } + } + } + + return result; + } + + /** + * Match a string with a regex returning an array of object containing details of each match + * @param {String} str - string + * @param {String} pattern - the substring/regex applied to the string + * @param {String} replacement - text to replace the matched substrings + * @param {Integer} [limit] - max number of matches to return + * @returns {Array} The array of match objects + */ + function* replace(str, pattern, replacement, limit) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + var self = this; + + // pattern cannot be an empty string + if (pattern === '') { + throw { + code: "D3010", + stack: (new Error()).stack, + value: pattern, + index: 2 + }; + } + + // limit, if specified, must be a non-negative number + if (limit < 0) { + throw { + code: "D3011", + stack: (new Error()).stack, + value: limit, + index: 4 + }; + } + + var replacer; + if (typeof replacement === 'string') { + replacer = function (regexMatch) { + var substitute = ''; + // scan forward, copying the replacement text into the substitute string + // and replace any occurrence of $n with the values matched by the regex + var position = 0; + var index = replacement.indexOf('$', position); + while (index !== -1 && position < replacement.length) { + substitute += replacement.substring(position, index); + position = index + 1; + var dollarVal = replacement.charAt(position); + if (dollarVal === '$') { + // literal $ + substitute += '$'; + position++; + } else if (dollarVal === '0') { + substitute += regexMatch.match; + position++; + } else { + var maxDigits; + if (regexMatch.groups.length === 0) { + // no sub-matches; any $ followed by a digit will be replaced by an empty string + maxDigits = 1; + } else { + // max number of digits to parse following the $ + maxDigits = Math.floor(Math.log(regexMatch.groups.length) * Math.LOG10E) + 1; + } + index = parseInt(replacement.substring(position, position + maxDigits), 10); + if (maxDigits > 1 && index > regexMatch.groups.length) { + index = parseInt(replacement.substring(position, position + maxDigits - 1), 10); + } + if (!isNaN(index)) { + if (regexMatch.groups.length > 0) { + var submatch = regexMatch.groups[index - 1]; + if (typeof submatch !== 'undefined') { + substitute += submatch; + } + } + position += index.toString().length; + } else { + // not a capture group, treat the $ as literal + substitute += '$'; + } + } + index = replacement.indexOf('$', position); + } + substitute += replacement.substring(position); + return substitute; + }; + } else { + replacer = replacement; + } + + var result = ''; + var position = 0; + + if (typeof limit === 'undefined' || limit > 0) { + var count = 0; + if (typeof pattern === 'string') { + var index = str.indexOf(pattern, position); + while (index !== -1 && (typeof limit === 'undefined' || count < limit)) { + result += str.substring(position, index); + result += replacement; + position = index + pattern.length; + count++; + index = str.indexOf(pattern, position); + } + result += str.substring(position); + } else { + var matches = yield* evaluateMatcher(pattern, str); + if (typeof matches !== 'undefined') { + while (typeof matches !== 'undefined' && (typeof limit === 'undefined' || count < limit)) { + result += str.substring(position, matches.start); + var replacedWith = replacer.apply(self, [matches]); + if (isIterable(replacedWith)) { + replacedWith = yield* replacedWith; + } + // check replacedWith is a string + if (typeof replacedWith === 'string') { + result += replacedWith; + } else { + // not a string - throw error + throw { + code: "D3012", + stack: (new Error()).stack, + value: replacedWith + }; + } + position = matches.start + matches.match.length; + count++; + matches = yield* evaluateMatcher(matches.next); + } + result += str.substring(position); + } else { + result = str; + } + } + } else { + result = str; + } + + return result; + } + + /** + * Base64 encode a string + * @param {String} str - string + * @returns {String} Base 64 encoding of the binary data + */ + function base64encode(str) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + // Use btoa in a browser, or Buffer in Node.js + + var btoa = typeof window !== 'undefined' ? + /* istanbul ignore next */ window.btoa : + function (str) { + // Simply doing `new Buffer` at this point causes Browserify to pull + // in the entire Buffer browser library, which is large and unnecessary. + // Using `global.Buffer` defeats this. + return new global.Buffer.from(str, 'binary').toString('base64'); // eslint-disable-line new-cap + }; + return btoa(str); + } + + /** + * Base64 decode a string + * @param {String} str - string + * @returns {String} Base 64 encoding of the binary data + */ + function base64decode(str) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + // Use btoa in a browser, or Buffer in Node.js + var atob = typeof window !== 'undefined' ? + /* istanbul ignore next */ window.atob : + function (str) { + // Simply doing `new Buffer` at this point causes Browserify to pull + // in the entire Buffer browser library, which is large and unnecessary. + // Using `global.Buffer` defeats this. + return new global.Buffer(str, 'base64').toString('binary'); + }; + return atob(str); + } + + /** + * Encode a string into a component for a url + * @param {String} str - String to encode + * @returns {string} Encoded string + */ + function encodeUrlComponent(str) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + // Catch URIErrors when URI sequence is malformed + var returnVal; + try { + returnVal = encodeURIComponent(str); + } catch (e) { + throw { + code: "D3140", + stack: (new Error()).stack, + value: str, + functionName: "encodeUrlComponent" + }; + } + return returnVal; + } + + /** + * Encode a string into a url + * @param {String} str - String to encode + * @returns {string} Encoded string + */ + function encodeUrl(str) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + // Catch URIErrors when URI sequence is malformed + var returnVal; + try { + returnVal = encodeURI(str); + } catch (e) { + throw { + code: "D3140", + stack: (new Error()).stack, + value: str, + functionName: "encodeUrl" + }; + } + return returnVal; + } + + /** + * Decode a string from a component for a url + * @param {String} str - String to decode + * @returns {string} Decoded string + */ + function decodeUrlComponent(str) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + // Catch URIErrors when URI sequence is malformed + var returnVal; + try { + returnVal = decodeURIComponent(str); + } catch (e) { + throw { + code: "D3140", + stack: (new Error()).stack, + value: str, + functionName: "decodeUrlComponent" + }; + } + return returnVal; + } + + /** + * Decode a string from a url + * @param {String} str - String to decode + * @returns {string} Decoded string + */ + function decodeUrl(str) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + // Catch URIErrors when URI sequence is malformed + var returnVal; + try { + returnVal = decodeURI(str); + } catch (e) { + throw { + code: "D3140", + stack: (new Error()).stack, + value: str, + functionName: "decodeUrl" + }; + } + return returnVal; + } + + /** + * Split a string into an array of substrings + * @param {String} str - string + * @param {String} separator - the token or regex that splits the string + * @param {Integer} [limit] - max number of substrings + * @returns {Array} The array of string + */ + function* split(str, separator, limit) { + // undefined inputs always return undefined + if (typeof str === 'undefined') { + return undefined; + } + + // limit, if specified, must be a non-negative number + if (limit < 0) { + throw { + code: "D3020", + stack: (new Error()).stack, + value: limit, + index: 3 + }; + } + + var result = []; + + if (typeof limit === 'undefined' || limit > 0) { + if (typeof separator === 'string') { + result = str.split(separator, limit); + } else { + var count = 0; + var matches = yield* evaluateMatcher(separator, str); + if (typeof matches !== 'undefined') { + var start = 0; + while (typeof matches !== 'undefined' && (typeof limit === 'undefined' || count < limit)) { + result.push(str.substring(start, matches.start)); + start = matches.end; + matches = yield* evaluateMatcher(matches.next); + count++; + } + if (typeof limit === 'undefined' || count < limit) { + result.push(str.substring(start)); + } + } else { + result.push(str); + } + } + } + + return result; + } + + /** + * Join an array of strings + * @param {Array} strs - array of string + * @param {String} [separator] - the token that splits the string + * @returns {String} The concatenated string + */ + function join(strs, separator) { + // undefined inputs always return undefined + if (typeof strs === 'undefined') { + return undefined; + } + + // if separator is not specified, default to empty string + if (typeof separator === 'undefined') { + separator = ""; + } + + return strs.join(separator); + } + + /** + * Formats a number into a decimal string representation using XPath 3.1 F&O fn:format-number spec + * @param {number} value - number to format + * @param {String} picture - picture string definition + * @param {Object} [options] - override locale defaults + * @returns {String} The formatted string + */ + function formatNumber(value, picture, options) { + // undefined inputs always return undefined + if (typeof value === 'undefined') { + return undefined; + } + + var defaults = { + "decimal-separator": ".", + "grouping-separator": ",", + "exponent-separator": "e", + "infinity": "Infinity", + "minus-sign": "-", + "NaN": "NaN", + "percent": "%", + "per-mille": "\u2030", + "zero-digit": "0", + "digit": "#", + "pattern-separator": ";" + }; + + // if `options` is specified, then its entries override defaults + var properties = defaults; + if (typeof options !== 'undefined') { + Object.keys(options).forEach(function (key) { + properties[key] = options[key]; + }); + } + + var decimalDigitFamily = []; + var zeroCharCode = properties['zero-digit'].charCodeAt(0); + for (var ii = zeroCharCode; ii < zeroCharCode + 10; ii++) { + decimalDigitFamily.push(String.fromCharCode(ii)); + } + + var activeChars = decimalDigitFamily.concat([properties['decimal-separator'], properties['exponent-separator'], properties['grouping-separator'], properties.digit, properties['pattern-separator']]); + + var subPictures = picture.split(properties['pattern-separator']); + + if (subPictures.length > 2) { + throw { + code: 'D3080', + stack: (new Error()).stack + }; + } + + var splitParts = function (subpicture) { + var prefix = (function () { + var ch; + for (var ii = 0; ii < subpicture.length; ii++) { + ch = subpicture.charAt(ii); + if (activeChars.indexOf(ch) !== -1 && ch !== properties['exponent-separator']) { + return subpicture.substring(0, ii); + } + } + })(); + var suffix = (function () { + var ch; + for (var ii = subpicture.length - 1; ii >= 0; ii--) { + ch = subpicture.charAt(ii); + if (activeChars.indexOf(ch) !== -1 && ch !== properties['exponent-separator']) { + return subpicture.substring(ii + 1); + } + } + })(); + var activePart = subpicture.substring(prefix.length, subpicture.length - suffix.length); + var mantissaPart, exponentPart, integerPart, fractionalPart; + var exponentPosition = subpicture.indexOf(properties['exponent-separator'], prefix.length); + if (exponentPosition === -1 || exponentPosition > subpicture.length - suffix.length) { + mantissaPart = activePart; + exponentPart = undefined; + } else { + mantissaPart = activePart.substring(0, exponentPosition); + exponentPart = activePart.substring(exponentPosition + 1); + } + var decimalPosition = mantissaPart.indexOf(properties['decimal-separator']); + if (decimalPosition === -1) { + integerPart = mantissaPart; + fractionalPart = suffix; + } else { + integerPart = mantissaPart.substring(0, decimalPosition); + fractionalPart = mantissaPart.substring(decimalPosition + 1); + } + return { + prefix: prefix, + suffix: suffix, + activePart: activePart, + mantissaPart: mantissaPart, + exponentPart: exponentPart, + integerPart: integerPart, + fractionalPart: fractionalPart, + subpicture: subpicture + }; + }; + + // validate the picture string, F&O 4.7.3 + var validate = function (parts) { + var error; + var ii; + var subpicture = parts.subpicture; + var decimalPos = subpicture.indexOf(properties['decimal-separator']); + if (decimalPos !== subpicture.lastIndexOf(properties['decimal-separator'])) { + error = 'D3081'; + } + if (subpicture.indexOf(properties.percent) !== subpicture.lastIndexOf(properties.percent)) { + error = 'D3082'; + } + if (subpicture.indexOf(properties['per-mille']) !== subpicture.lastIndexOf(properties['per-mille'])) { + error = 'D3083'; + } + if (subpicture.indexOf(properties.percent) !== -1 && subpicture.indexOf(properties['per-mille']) !== -1) { + error = 'D3084'; + } + var valid = false; + for (ii = 0; ii < parts.mantissaPart.length; ii++) { + var ch = parts.mantissaPart.charAt(ii); + if (decimalDigitFamily.indexOf(ch) !== -1 || ch === properties.digit) { + valid = true; + break; + } + } + if (!valid) { + error = 'D3085'; + } + var charTypes = parts.activePart.split('').map(function (char) { + return activeChars.indexOf(char) === -1 ? 'p' : 'a'; + }).join(''); + if (charTypes.indexOf('p') !== -1) { + error = 'D3086'; + } + if (decimalPos !== -1) { + if (subpicture.charAt(decimalPos - 1) === properties['grouping-separator'] || subpicture.charAt(decimalPos + 1) === properties['grouping-separator']) { + error = 'D3087'; + } + } else if (parts.integerPart.charAt(parts.integerPart.length - 1) === properties['grouping-separator']) { + error = 'D3088'; + } + if (subpicture.indexOf(properties['grouping-separator'] + properties['grouping-separator']) !== -1) { + error = 'D3089'; + } + var optionalDigitPos = parts.integerPart.indexOf(properties.digit); + if (optionalDigitPos !== -1 && parts.integerPart.substring(0, optionalDigitPos).split('').filter(function (char) { + return decimalDigitFamily.indexOf(char) > -1; + }).length > 0) { + error = 'D3090'; + } + optionalDigitPos = parts.fractionalPart.lastIndexOf(properties.digit); + if (optionalDigitPos !== -1 && parts.fractionalPart.substring(optionalDigitPos).split('').filter(function (char) { + return decimalDigitFamily.indexOf(char) > -1; + }).length > 0) { + error = 'D3091'; + } + var exponentExists = (typeof parts.exponentPart === 'string'); + if (exponentExists && parts.exponentPart.length > 0 && (subpicture.indexOf(properties.percent) !== -1 || subpicture.indexOf(properties['per-mille']) !== -1)) { + error = 'D3092'; + } + if (exponentExists && (parts.exponentPart.length === 0 || parts.exponentPart.split('').filter(function (char) { + return decimalDigitFamily.indexOf(char) === -1; + }).length > 0)) { + error = 'D3093'; + } + if (error) { + throw { + code: error, + stack: (new Error()).stack + }; + } + }; + + // analyse the picture string, F&O 4.7.4 + var analyse = function (parts) { + var getGroupingPositions = function (part, toLeft) { + var positions = []; + var groupingPosition = part.indexOf(properties['grouping-separator']); + while (groupingPosition !== -1) { + var charsToTheRight = (toLeft ? part.substring(0, groupingPosition) : part.substring(groupingPosition)).split('').filter(function (char) { + return decimalDigitFamily.indexOf(char) !== -1 || char === properties.digit; + }).length; + positions.push(charsToTheRight); + groupingPosition = parts.integerPart.indexOf(properties['grouping-separator'], groupingPosition + 1); + } + return positions; + }; + var integerPartGroupingPositions = getGroupingPositions(parts.integerPart); + var regular = function (indexes) { + // are the grouping positions regular? i.e. same interval between each of them + if (indexes.length === 0) { + return 0; + } + var gcd = function (a, b) { + return b === 0 ? a : gcd(b, a % b); + }; + // find the greatest common divisor of all the positions + var factor = indexes.reduce(gcd); + // is every position separated by this divisor? If so, it's regular + for (var index = 1; index <= indexes.length; index++) { + if (indexes.indexOf(index * factor) === -1) { + return 0; + } + } + return factor; + }; + + var regularGrouping = regular(integerPartGroupingPositions); + var fractionalPartGroupingPositions = getGroupingPositions(parts.fractionalPart, true); + + var minimumIntegerPartSize = parts.integerPart.split('').filter(function (char) { + return decimalDigitFamily.indexOf(char) !== -1; + }).length; + var scalingFactor = minimumIntegerPartSize; + + var fractionalPartArray = parts.fractionalPart.split(''); + var minimumFactionalPartSize = fractionalPartArray.filter(function (char) { + return decimalDigitFamily.indexOf(char) !== -1; + }).length; + var maximumFactionalPartSize = fractionalPartArray.filter(function (char) { + return decimalDigitFamily.indexOf(char) !== -1 || char === properties.digit; + }).length; + var exponentPresent = typeof parts.exponentPart === 'string'; + if (minimumIntegerPartSize === 0 && maximumFactionalPartSize === 0) { + if (exponentPresent) { + minimumFactionalPartSize = 1; + maximumFactionalPartSize = 1; + } else { + minimumIntegerPartSize = 1; + } + } + if (exponentPresent && minimumIntegerPartSize === 0 && parts.integerPart.indexOf(properties.digit) !== -1) { + minimumIntegerPartSize = 1; + } + if (minimumIntegerPartSize === 0 && minimumFactionalPartSize === 0) { + minimumFactionalPartSize = 1; + } + var minimumExponentSize = 0; + if (exponentPresent) { + minimumExponentSize = parts.exponentPart.split('').filter(function (char) { + return decimalDigitFamily.indexOf(char) !== -1; + }).length; + } + + return { + integerPartGroupingPositions: integerPartGroupingPositions, + regularGrouping: regularGrouping, + minimumIntegerPartSize: minimumIntegerPartSize, + scalingFactor: scalingFactor, + prefix: parts.prefix, + fractionalPartGroupingPositions: fractionalPartGroupingPositions, + minimumFactionalPartSize: minimumFactionalPartSize, + maximumFactionalPartSize: maximumFactionalPartSize, + minimumExponentSize: minimumExponentSize, + suffix: parts.suffix, + picture: parts.subpicture + }; + }; + + var parts = subPictures.map(splitParts); + parts.forEach(validate); + + var variables = parts.map(analyse); + + var minus_sign = properties['minus-sign']; + var zero_digit = properties['zero-digit']; + var decimal_separator = properties['decimal-separator']; + var grouping_separator = properties['grouping-separator']; + + if (variables.length === 1) { + variables.push(JSON.parse(JSON.stringify(variables[0]))); + variables[1].prefix = minus_sign + variables[1].prefix; + } + + // TODO cache the result of the analysis + + // format the number + // bullet 1: TODO: NaN - not sure we'd ever get this in JSON + var pic; + // bullet 2: + if (value >= 0) { + pic = variables[0]; + } else { + pic = variables[1]; + } + var adjustedNumber; + // bullet 3: + if (pic.picture.indexOf(properties.percent) !== -1) { + adjustedNumber = value * 100; + } else if (pic.picture.indexOf(properties['per-mille']) !== -1) { + adjustedNumber = value * 1000; + } else { + adjustedNumber = value; + } + // bullet 4: + // TODO: infinity - not sure we'd ever get this in JSON + // bullet 5: + var mantissa, exponent; + if (pic.minimumExponentSize === 0) { + mantissa = adjustedNumber; + } else { + // mantissa * 10^exponent = adjustedNumber + var maxMantissa = Math.pow(10, pic.scalingFactor); + var minMantissa = Math.pow(10, pic.scalingFactor - 1); + mantissa = adjustedNumber; + exponent = 0; + while (mantissa < minMantissa) { + mantissa *= 10; + exponent -= 1; + } + while (mantissa > maxMantissa) { + mantissa /= 10; + exponent += 1; + } + } + // bullet 6: + var roundedNumber = round(mantissa, pic.maximumFactionalPartSize); + // bullet 7: + var makeString = function (value, dp) { + var str = Math.abs(value).toFixed(dp); + if (zero_digit !== '0') { + str = str.split('').map(function (digit) { + if (digit >= '0' && digit <= '9') { + return decimalDigitFamily[digit.charCodeAt(0) - 48]; + } else { + return digit; + } + }).join(''); + } + return str; + }; + var stringValue = makeString(roundedNumber, pic.maximumFactionalPartSize); + var decimalPos = stringValue.indexOf('.'); + if (decimalPos === -1) { + stringValue = stringValue + decimal_separator; + } else { + stringValue = stringValue.replace('.', decimal_separator); + } + while (stringValue.charAt(0) === zero_digit) { + stringValue = stringValue.substring(1); + } + while (stringValue.charAt(stringValue.length - 1) === zero_digit) { + stringValue = stringValue.substring(0, stringValue.length - 1); + } + // bullets 8 & 9: + decimalPos = stringValue.indexOf(decimal_separator); + var padLeft = pic.minimumIntegerPartSize - decimalPos; + var padRight = pic.minimumFactionalPartSize - (stringValue.length - decimalPos - 1); + stringValue = (padLeft > 0 ? new Array(padLeft + 1).join(zero_digit) : '') + stringValue; + stringValue = stringValue + (padRight > 0 ? new Array(padRight + 1).join(zero_digit) : ''); + decimalPos = stringValue.indexOf(decimal_separator); + // bullet 10: + if (pic.regularGrouping > 0) { + var groupCount = Math.floor((decimalPos - 1) / pic.regularGrouping); + for (var group = 1; group <= groupCount; group++) { + stringValue = [stringValue.slice(0, decimalPos - group * pic.regularGrouping), grouping_separator, stringValue.slice(decimalPos - group * pic.regularGrouping)].join(''); + } + } else { + pic.integerPartGroupingPositions.forEach(function (pos) { + stringValue = [stringValue.slice(0, decimalPos - pos), grouping_separator, stringValue.slice(decimalPos - pos)].join(''); + decimalPos++; + }); + } + // bullet 11: + decimalPos = stringValue.indexOf(decimal_separator); + pic.fractionalPartGroupingPositions.forEach(function (pos) { + stringValue = [stringValue.slice(0, pos + decimalPos + 1), grouping_separator, stringValue.slice(pos + decimalPos + 1)].join(''); + }); + // bullet 12: + decimalPos = stringValue.indexOf(decimal_separator); + if (pic.picture.indexOf(decimal_separator) === -1 || decimalPos === stringValue.length - 1) { + stringValue = stringValue.substring(0, stringValue.length - 1); + } + // bullet 13: + if (typeof exponent !== 'undefined') { + var stringExponent = makeString(exponent, 0); + padLeft = pic.minimumExponentSize - stringExponent.length; + if (padLeft > 0) { + stringExponent = new Array(padLeft + 1).join(zero_digit) + stringExponent; + } + stringValue = stringValue + properties['exponent-separator'] + (exponent < 0 ? minus_sign : '') + stringExponent; + } + // bullet 14: + stringValue = pic.prefix + stringValue + pic.suffix; + return stringValue; + } + + /** + * Converts a number to a string using a specified number base + * @param {number} value - the number to convert + * @param {number} [radix] - the number base; must be between 2 and 36. Defaults to 10 + * @returns {string} - the converted string + */ + function formatBase(value, radix) { + // undefined inputs always return undefined + if (typeof value === 'undefined') { + return undefined; + } + + value = round(value); + + if (typeof radix === 'undefined') { + radix = 10; + } else { + radix = round(radix); + } + + if (radix < 2 || radix > 36) { + throw { + code: 'D3100', + stack: (new Error()).stack, + value: radix + }; + + } + + var result = value.toString(radix); + + return result; + } + + /** + * Cast argument to number + * @param {Object} arg - Argument + * @returns {Number} numeric value of argument + */ + function number(arg) { + var result; + + // undefined inputs always return undefined + if (typeof arg === 'undefined') { + return undefined; + } + + if (typeof arg === 'number') { + // already a number + result = arg; + } else if (typeof arg === 'string' && /^-?[0-9]+(\.[0-9]+)?([Ee][-+]?[0-9]+)?$/.test(arg) && !isNaN(parseFloat(arg)) && isFinite(arg)) { + result = parseFloat(arg); + } else if (arg === true) { + // boolean true casts to 1 + result = 1; + } else if (arg === false) { + // boolean false casts to 0 + result = 0; + } else { + throw { + code: "D3030", + value: arg, + stack: (new Error()).stack, + index: 1 + }; + } + return result; + } + + /** + * Absolute value of a number + * @param {Number} arg - Argument + * @returns {Number} absolute value of argument + */ + function abs(arg) { + var result; + + // undefined inputs always return undefined + if (typeof arg === 'undefined') { + return undefined; + } + + result = Math.abs(arg); + return result; + } + + /** + * Rounds a number down to integer + * @param {Number} arg - Argument + * @returns {Number} rounded integer + */ + function floor(arg) { + var result; + + // undefined inputs always return undefined + if (typeof arg === 'undefined') { + return undefined; + } + + result = Math.floor(arg); + return result; + } + + /** + * Rounds a number up to integer + * @param {Number} arg - Argument + * @returns {Number} rounded integer + */ + function ceil(arg) { + var result; + + // undefined inputs always return undefined + if (typeof arg === 'undefined') { + return undefined; + } + + result = Math.ceil(arg); + return result; + } + + /** + * Round to half even + * @param {Number} arg - Argument + * @param {Number} [precision] - number of decimal places + * @returns {Number} rounded integer + */ + function round(arg, precision) { + var result; + + // undefined inputs always return undefined + if (typeof arg === 'undefined') { + return undefined; + } + + if (precision) { + // shift the decimal place - this needs to be done in a string since multiplying + // by a power of ten can introduce floating point precision errors which mess up + // this rounding algorithm - See 'Decimal rounding' in + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round + // Shift + var value = arg.toString().split('e'); + arg = +(value[0] + 'e' + (value[1] ? (+value[1] + precision) : precision)); + + } + + // round up to nearest int + result = Math.round(arg); + var diff = result - arg; + if (Math.abs(diff) === 0.5 && Math.abs(result % 2) === 1) { + // rounded the wrong way - adjust to nearest even number + result = result - 1; + } + if (precision) { + // Shift back + value = result.toString().split('e'); + /* istanbul ignore next */ + result = +(value[0] + 'e' + (value[1] ? (+value[1] - precision) : -precision)); + } + if (Object.is(result, -0)) { // ESLint rule 'no-compare-neg-zero' suggests this way + // JSON doesn't do -0 + result = 0; + } + return result; + } + + /** + * Square root of number + * @param {Number} arg - Argument + * @returns {Number} square root + */ + function sqrt(arg) { + var result; + + // undefined inputs always return undefined + if (typeof arg === 'undefined') { + return undefined; + } + + if (arg < 0) { + throw { + stack: (new Error()).stack, + code: "D3060", + index: 1, + value: arg + }; + } + + result = Math.sqrt(arg); + + return result; + } + + /** + * Raises number to the power of the second number + * @param {Number} arg - the base + * @param {Number} exp - the exponent + * @returns {Number} rounded integer + */ + function power(arg, exp) { + var result; + + // undefined inputs always return undefined + if (typeof arg === 'undefined') { + return undefined; + } + + result = Math.pow(arg, exp); + + if (!isFinite(result)) { + throw { + stack: (new Error()).stack, + code: "D3061", + index: 1, + value: arg, + exp: exp + }; + } + + return result; + } + + /** + * Returns a random number 0 <= n < 1 + * @returns {number} random number + */ + function random() { + return Math.random(); + } + + /** + * Evaluate an input and return a boolean + * @param {*} arg - Arguments + * @returns {boolean} Boolean + */ + function boolean(arg) { + // cast arg to its effective boolean value + // boolean: unchanged + // string: zero-length -> false; otherwise -> true + // number: 0 -> false; otherwise -> true + // null -> false + // array: empty -> false; length > 1 -> true + // object: empty -> false; non-empty -> true + // function -> false + + // undefined inputs always return undefined + if (typeof arg === 'undefined') { + return undefined; + } + + var result = false; + if (Array.isArray(arg)) { + if (arg.length === 1) { + result = boolean(arg[0]); + } else if (arg.length > 1) { + var trues = arg.filter(function (val) { + return boolean(val); + }); + result = trues.length > 0; + } + } else if (typeof arg === 'string') { + if (arg.length > 0) { + result = true; + } + } else if (isNumeric(arg)) { + if (arg !== 0) { + result = true; + } + } else if (arg !== null && typeof arg === 'object') { + if (Object.keys(arg).length > 0) { + result = true; + } + } else if (typeof arg === 'boolean' && arg === true) { + result = true; + } + return result; + } + + /** + * returns the Boolean NOT of the arg + * @param {*} arg - argument + * @returns {boolean} - NOT arg + */ + function not(arg) { + return !boolean(arg); + } + + /** + * Helper function to build the arguments to be supplied to the function arg of the + * HOFs map, filter, each, sift and single + * @param {function} func - the function to be invoked + * @param {*} arg1 - the first (required) arg - the value + * @param {*} arg2 - the second (optional) arg - the position (index or key) + * @param {*} arg3 - the third (optional) arg - the whole structure (array or object) + * @returns {*[]} the argument list + */ + function hofFuncArgs(func, arg1, arg2, arg3) { + var func_args = [arg1]; // the first arg (the value) is required + // the other two are optional - only supply it if the function can take it + var length = getFunctionArity(func); + if (length >= 2) { + func_args.push(arg2); + } + if (length >= 3) { + func_args.push(arg3); + } + return func_args; + } + + /** + * Create a map from an array of arguments + * @param {Array} [arr] - array to map over + * @param {Function} func - function to apply + * @returns {Array} Map array + */ + function* map(arr, func) { + // undefined inputs always return undefined + if (typeof arr === 'undefined') { + return undefined; + } + + var result = createSequence(); + // do the map - iterate over the arrays, and invoke func + for (var i = 0; i < arr.length; i++) { + var func_args = hofFuncArgs(func, arr[i], i, arr); + // invoke func + var res = yield* func.apply(this, func_args); + if (typeof res !== 'undefined') { + result.push(res); + } + } + + return result; + } + + /** + * Create a map from an array of arguments + * @param {Array} [arr] - array to filter + * @param {Function} func - predicate function + * @returns {Array} Map array + */ + function* filter(arr, func) { // eslint-disable-line require-yield + // undefined inputs always return undefined + if (typeof arr === 'undefined') { + return undefined; + } + + var result = createSequence(); + + for (var i = 0; i < arr.length; i++) { + var entry = arr[i]; + var func_args = hofFuncArgs(func, entry, i, arr); + // invoke func + var res = yield* func.apply(this, func_args); + if (boolean(res)) { + result.push(entry); + } + } + + return result; + } + + /** + * Given an array, find the single element matching a specified condition + * Throws an exception if the number of matching elements is not exactly one + * @param {Array} [arr] - array to filter + * @param {Function} [func] - predicate function + * @returns {*} Matching element + */ + function* single(arr, func) { // eslint-disable-line require-yield + // undefined inputs always return undefined + if (typeof arr === 'undefined') { + return undefined; + } + + var hasFoundMatch = false; + var result; + + for (var i = 0; i < arr.length; i++) { + var entry = arr[i]; + var positiveResult = true; + if (typeof func !== 'undefined') { + var func_args = hofFuncArgs(func, entry, i, arr); + // invoke func + var res = yield* func.apply(this, func_args); + positiveResult = boolean(res); + } + if (positiveResult) { + if(!hasFoundMatch) { + result = entry; + hasFoundMatch = true; + } else { + throw { + stack: (new Error()).stack, + code: "D3138", + index: i + }; + } + } + } + + if(!hasFoundMatch) { + throw { + stack: (new Error()).stack, + code: "D3139" + }; + } + + return result; + } + + /** + * Convolves (zips) each value from a set of arrays + * @param {Array} [args] - arrays to zip + * @returns {Array} Zipped array + */ + function zip() { + // this can take a variable number of arguments + var result = []; + var args = Array.prototype.slice.call(arguments); + // length of the shortest array + var length = Math.min.apply(Math, args.map(function (arg) { + if (Array.isArray(arg)) { + return arg.length; + } + return 0; + })); + for (var i = 0; i < length; i++) { + var tuple = args.map((arg) => { + return arg[i]; + }); + result.push(tuple); + } + return result; + } + + /** + * Fold left function + * @param {Array} sequence - Sequence + * @param {Function} func - Function + * @param {Object} init - Initial value + * @returns {*} Result + */ + function* foldLeft(sequence, func, init) { + // undefined inputs always return undefined + if (typeof sequence === 'undefined') { + return undefined; + } + + var result; + + var arity = getFunctionArity(func); + if (arity < 2) { + throw { + stack: (new Error()).stack, + code: "D3050", + index: 1 + }; + } + + var index; + if (typeof init === 'undefined' && sequence.length > 0) { + result = sequence[0]; + index = 1; + } else { + result = init; + index = 0; + } + + while (index < sequence.length) { + var args = [result, sequence[index]]; + if (arity >= 3) { + args.push(index); + } + if (arity >= 4) { + args.push(sequence); + } + result = yield* func.apply(this, args); + index++; + } + + return result; + } + + /** + * Return keys for an object + * @param {Object} arg - Object + * @returns {Array} Array of keys + */ + function keys(arg) { + var result = createSequence(); + + if (Array.isArray(arg)) { + // merge the keys of all of the items in the array + var merge = {}; + arg.forEach(function (item) { + var allkeys = keys(item); + allkeys.forEach(function (key) { + merge[key] = true; + }); + }); + result = keys(merge); + } else if (arg !== null && typeof arg === 'object' && !(isLambda(arg))) { + Object.keys(arg).forEach(key => result.push(key)); + } + return result; + } + + /** + * Return value from an object for a given key + * @param {Object} input - Object/Array + * @param {String} key - Key in object + * @returns {*} Value of key in object + */ + function lookup(input, key) { + // lookup the 'name' item in the input + var result; + if (Array.isArray(input)) { + result = createSequence(); + for(var ii = 0; ii < input.length; ii++) { + var res = lookup(input[ii], key); + if (typeof res !== 'undefined') { + if (Array.isArray(res)) { + result.push(...res); + } else { + result.push(res); + } + } + } + } else if (input !== null && typeof input === 'object') { + result = input[key]; + } + return result; + } + + /** + * Append second argument to first + * @param {Array|Object} arg1 - First argument + * @param {Array|Object} arg2 - Second argument + * @returns {*} Appended arguments + */ + function append(arg1, arg2) { + // disregard undefined args + if (typeof arg1 === 'undefined') { + return arg2; + } + if (typeof arg2 === 'undefined') { + return arg1; + } + // if either argument is not an array, make it so + if (!Array.isArray(arg1)) { + arg1 = createSequence(arg1); + } + if (!Array.isArray(arg2)) { + arg2 = [arg2]; + } + return arg1.concat(arg2); + } + + /** + * Determines if the argument is undefined + * @param {*} arg - argument + * @returns {boolean} False if argument undefined, otherwise true + */ + function exists(arg) { + if (typeof arg === 'undefined') { + return false; + } else { + return true; + } + } + + /** + * Splits an object into an array of object with one property each + * @param {*} arg - the object to split + * @returns {*} - the array + */ + function spread(arg) { + var result = createSequence(); + + if (Array.isArray(arg)) { + // spread all of the items in the array + arg.forEach(function (item) { + result = append(result, spread(item)); + }); + } else if (arg !== null && typeof arg === 'object' && !isLambda(arg)) { + for (var key in arg) { + var obj = {}; + obj[key] = arg[key]; + result.push(obj); + } + } else { + result = arg; + } + return result; + } + + /** + * Merges an array of objects into a single object. Duplicate properties are + * overridden by entries later in the array + * @param {*} arg - the objects to merge + * @returns {*} - the object + */ + function merge(arg) { + // undefined inputs always return undefined + if (typeof arg === 'undefined') { + return undefined; + } + + var result = {}; + + arg.forEach(function (obj) { + for (var prop in obj) { + result[prop] = obj[prop]; + } + }); + return result; + } + + /** + * Reverses the order of items in an array + * @param {Array} arr - the array to reverse + * @returns {Array} - the reversed array + */ + function reverse(arr) { + // undefined inputs always return undefined + if (typeof arr === 'undefined') { + return undefined; + } + + if (arr.length <= 1) { + return arr; + } + + var length = arr.length; + var result = new Array(length); + for (var i = 0; i < length; i++) { + result[length - i - 1] = arr[i]; + } + + return result; + } + + /** + * + * @param {*} obj - the input object to iterate over + * @param {*} func - the function to apply to each key/value pair + * @returns {Array} - the resultant array + */ + function* each(obj, func) { + var result = createSequence(); + + for (var key in obj) { + var func_args = hofFuncArgs(func, obj[key], key, obj); + // invoke func + var val = yield* func.apply(this, func_args); + if(typeof val !== 'undefined') { + result.push(val); + } + } + + return result; + } + + /** + * + * @param {string} [message] - the message to attach to the error + * @throws custom error with code 'D3137' + */ + function error(message) { + throw { + code: "D3137", + stack: (new Error()).stack, + message: message || "$error() function evaluated" + }; + } + + /** + * + * @param {boolean} condition - the condition to evaluate + * @param {string} [message] - the message to attach to the error + * @throws custom error with code 'D3137' + * @returns {undefined} + */ + function assert(condition, message) { + if(!condition) { + throw { + code: "D3141", + stack: (new Error()).stack, + message: message || "$assert() statement failed" + }; + } + + return undefined; + } + + /** + * + * @param {*} [value] - the input to which the type will be checked + * @returns {string} - the type of the input + */ + function type(value) { + if (value === undefined) { + return undefined; + } + + if (value === null) { + return 'null'; + } + + if (isNumeric(value)) { + return 'number'; + } + + if (typeof value === 'string') { + return 'string'; + } + + if (typeof value === 'boolean') { + return 'boolean'; + } + + if(Array.isArray(value)) { + return 'array'; + } + + if(isFunction(value)) { + return 'function'; + } + + return 'object'; + } + + /** + * Implements the merge sort (stable) with optional comparator function + * + * @param {Array} arr - the array to sort + * @param {*} comparator - comparator function + * @returns {Array} - sorted array + */ + function* sort(arr, comparator) { + // undefined inputs always return undefined + if (typeof arr === 'undefined') { + return undefined; + } + + if (arr.length <= 1) { + return arr; + } + + var comp; + if (typeof comparator === 'undefined') { + // inject a default comparator - only works for numeric or string arrays + if (!isArrayOfNumbers(arr) && !isArrayOfStrings(arr)) { + throw { + stack: (new Error()).stack, + code: "D3070", + index: 1 + }; + } + + comp = function* (a, b) { // eslint-disable-line require-yield + return a > b; + }; + } else { + // for internal usage of functionSort (i.e. order-by syntax) + comp = comparator; + } + + var merge = function* (l, r) { + var merge_iter = function* (result, left, right) { + if (left.length === 0) { + Array.prototype.push.apply(result, right); + } else if (right.length === 0) { + Array.prototype.push.apply(result, left); + } else if (yield* comp(left[0], right[0])) { // invoke the comparator function + // if it returns true - swap left and right + result.push(right[0]); + yield* merge_iter(result, left, right.slice(1)); + } else { + // otherwise keep the same order + result.push(left[0]); + yield* merge_iter(result, left.slice(1), right); + } + }; + var merged = []; + yield* merge_iter(merged, l, r); + return merged; + }; + + var msort = function* (array) { + if (!Array.isArray(array) || array.length <= 1) { + return array; + } else { + var middle = Math.floor(array.length / 2); + var left = array.slice(0, middle); + var right = array.slice(middle); + left = yield* msort(left); + right = yield* msort(right); + return yield* merge(left, right); + } + }; + + var result = yield* msort(arr); + + return result; + } + + /** + * Randomly shuffles the contents of an array + * @param {Array} arr - the input array + * @returns {Array} the shuffled array + */ + function shuffle(arr) { + // undefined inputs always return undefined + if (typeof arr === 'undefined') { + return undefined; + } + + if (arr.length <= 1) { + return arr; + } + + // shuffle using the 'inside-out' variant of the Fisher-Yates algorithm + var result = new Array(arr.length); + for (var i = 0; i < arr.length; i++) { + var j = Math.floor(Math.random() * (i + 1)); // random integer such that 0 ≤ j ≤ i + if (i !== j) { + result[i] = result[j]; + } + result[j] = arr[i]; + } + + return result; + } + + /** + * Returns the values that appear in a sequence, with duplicates eliminated. + * @param {Array} arr - An array or sequence of values + * @returns {Array} - sequence of distinct values + */ + function distinct(arr) { + // undefined inputs always return undefined + if (typeof arr === 'undefined') { + return undefined; + } + + if(!Array.isArray(arr) || arr.length <= 1) { + return arr; + } + + var results = isSequence(arr) ? createSequence() : []; + + for(var ii = 0; ii < arr.length; ii++) { + var value = arr[ii]; + // is this value already in the result sequence? + var includes = false; + for(var jj = 0; jj < results.length; jj++) { + if (deepEquals(value, results[jj])) { + includes = true; + break; + } + } + if(!includes) { + results.push(value); + } + } + return results; + } + + /** + * Applies a predicate function to each key/value pair in an object, and returns an object containing + * only the key/value pairs that passed the predicate + * + * @param {object} arg - the object to be sifted + * @param {object} func - the predicate function (lambda or native) + * @returns {object} - sifted object + */ + function* sift(arg, func) { + var result = {}; + + for (var item in arg) { + var entry = arg[item]; + var func_args = hofFuncArgs(func, entry, item, arg); + // invoke func + var res = yield* func.apply(this, func_args); + if (boolean(res)) { + result[item] = entry; + } + } + + // empty objects should be changed to undefined + if (Object.keys(result).length === 0) { + result = undefined; + } + + return result; + } + + return { + sum, count, max, min, average, + string, substring, substringBefore, substringAfter, lowercase, uppercase, length, trim, pad, + match, contains, replace, split, join, + formatNumber, formatBase, number, floor, ceil, round, abs, sqrt, power, random, + boolean, not, + map, zip, filter, single, foldLeft, sift, + keys, lookup, append, exists, spread, merge, reverse, each, error, assert, type, sort, shuffle, distinct, + base64encode, base64decode, encodeUrlComponent, encodeUrl, decodeUrlComponent, decodeUrl + }; +})(); + +module.exports = functions; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./utils":6}],3:[function(require,module,exports){ +/** + * © Copyright IBM Corp. 2016, 2017 All Rights Reserved + * Project name: JSONata + * This project is licensed under the MIT License, see LICENSE + */ + +/** + * @module JSONata + * @description JSON query and transformation language + */ + +var datetime = require('./datetime'); +var fn = require('./functions'); +var utils = require('./utils'); +var parser = require('./parser'); +var parseSignature = require('./signature'); + +/** + * jsonata + * @function + * @param {Object} expr - JSONata expression + * @returns {{evaluate: evaluate, assign: assign}} Evaluated expression + */ +var jsonata = (function() { + 'use strict'; + + var isNumeric = utils.isNumeric; + var isArrayOfStrings = utils.isArrayOfStrings; + var isArrayOfNumbers = utils.isArrayOfNumbers; + var createSequence = utils.createSequence; + var isSequence = utils.isSequence; + var isFunction = utils.isFunction; + var isLambda = utils.isLambda; + var isIterable = utils.isIterable; + var getFunctionArity = utils.getFunctionArity; + var isDeepEqual = utils.isDeepEqual; + + // Start of Evaluator code + + var staticFrame = createFrame(null); + + /** + * Evaluate expression against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} Evaluated input data + */ + function* evaluate(expr, input, environment) { + var result; + + var entryCallback = environment.lookup('__evaluate_entry'); + if(entryCallback) { + entryCallback(expr, input, environment); + } + + switch (expr.type) { + case 'path': + result = yield * evaluatePath(expr, input, environment); + break; + case 'binary': + result = yield * evaluateBinary(expr, input, environment); + break; + case 'unary': + result = yield * evaluateUnary(expr, input, environment); + break; + case 'name': + result = evaluateName(expr, input, environment); + break; + case 'string': + case 'number': + case 'value': + result = evaluateLiteral(expr, input, environment); + break; + case 'wildcard': + result = evaluateWildcard(expr, input, environment); + break; + case 'descendant': + result = evaluateDescendants(expr, input, environment); + break; + case 'parent': + result = environment.lookup(expr.slot.label); + break; + case 'condition': + result = yield * evaluateCondition(expr, input, environment); + break; + case 'block': + result = yield * evaluateBlock(expr, input, environment); + break; + case 'bind': + result = yield * evaluateBindExpression(expr, input, environment); + break; + case 'regex': + result = evaluateRegex(expr, input, environment); + break; + case 'function': + result = yield * evaluateFunction(expr, input, environment); + break; + case 'variable': + result = evaluateVariable(expr, input, environment); + break; + case 'lambda': + result = evaluateLambda(expr, input, environment); + break; + case 'partial': + result = yield * evaluatePartialApplication(expr, input, environment); + break; + case 'apply': + result = yield * evaluateApplyExpression(expr, input, environment); + break; + case 'transform': + result = evaluateTransformExpression(expr, input, environment); + break; + } + + if(environment.async && + (typeof result === 'undefined' || result === null || typeof result.then !== 'function')) { + result = Promise.resolve(result); + } + if(environment.async && typeof result.then === 'function' && expr.nextFunction && typeof result[expr.nextFunction] === 'function') { + // although this is a 'thenable', it is chaining a different function + // so don't yield since yielding will trigger the .then() + } else { + result = yield result; + } + + if (Object.prototype.hasOwnProperty.call(expr, 'predicate')) { + for(var ii = 0; ii < expr.predicate.length; ii++) { + result = yield * evaluateFilter(expr.predicate[ii].expr, result, environment); + } + } + + if (expr.type !== 'path' && Object.prototype.hasOwnProperty.call(expr, 'group')) { + result = yield * evaluateGroupExpression(expr.group, result, environment); + } + + var exitCallback = environment.lookup('__evaluate_exit'); + if(exitCallback) { + exitCallback(expr, input, environment, result); + } + + if(result && isSequence(result) && !result.tupleStream) { + if(expr.keepArray) { + result.keepSingleton = true; + } + if(result.length === 0) { + result = undefined; + } else if(result.length === 1) { + result = result.keepSingleton ? result : result[0]; + } + + } + + return result; + } + + /** + * Evaluate path expression against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} Evaluated input data + */ + function* evaluatePath(expr, input, environment) { + var inputSequence; + // expr is an array of steps + // if the first step is a variable reference ($...), including root reference ($$), + // then the path is absolute rather than relative + if (Array.isArray(input) && expr.steps[0].type !== 'variable') { + inputSequence = input; + } else { + // if input is not an array, make it so + inputSequence = createSequence(input); + } + + var resultSequence; + var isTupleStream = false; + var tupleBindings = undefined; + + // evaluate each step in turn + for(var ii = 0; ii < expr.steps.length; ii++) { + var step = expr.steps[ii]; + + if(step.tuple) { + isTupleStream = true; + } + + // if the first step is an explicit array constructor, then just evaluate that (i.e. don't iterate over a context array) + if(ii === 0 && step.consarray) { + resultSequence = yield * evaluate(step, inputSequence, environment); + } else { + if(isTupleStream) { + tupleBindings = yield * evaluateTupleStep(step, inputSequence, tupleBindings, environment); + } else { + resultSequence = yield * evaluateStep(step, inputSequence, environment, ii === expr.steps.length - 1); + } + } + + if (!isTupleStream && (typeof resultSequence === 'undefined' || resultSequence.length === 0)) { + break; + } + + if(typeof step.focus === 'undefined') { + inputSequence = resultSequence; + } + + } + + if(isTupleStream) { + if(expr.tuple) { + // tuple stream is carrying ancestry information - keep this + resultSequence = tupleBindings; + } else { + resultSequence = createSequence(); + for (ii = 0; ii < tupleBindings.length; ii++) { + resultSequence.push(tupleBindings[ii]['@']); + } + } + } + + if(expr.keepSingletonArray) { + if(!isSequence(resultSequence)) { + resultSequence = createSequence(resultSequence); + } + resultSequence.keepSingleton = true; + } + + if (expr.hasOwnProperty('group')) { + resultSequence = yield* evaluateGroupExpression(expr.group, isTupleStream ? tupleBindings : resultSequence, environment) + } + + return resultSequence; + } + + function createFrameFromTuple(environment, tuple) { + var frame = createFrame(environment); + for(const prop in tuple) { + frame.bind(prop, tuple[prop]); + } + return frame; + } + + /** + * Evaluate a step within a path + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @param {boolean} lastStep - flag the last step in a path + * @returns {*} Evaluated input data + */ + function* evaluateStep(expr, input, environment, lastStep) { + var result; + if(expr.type === 'sort') { + result = yield* evaluateSortExpression(expr, input, environment); + if(expr.stages) { + result = yield* evaluateStages(expr.stages, result, environment); + } + return result; + } + + result = createSequence(); + + for(var ii = 0; ii < input.length; ii++) { + var res = yield * evaluate(expr, input[ii], environment); + if(expr.stages) { + for(var ss = 0; ss < expr.stages.length; ss++) { + res = yield* evaluateFilter(expr.stages[ss].expr, res, environment); + } + } + if(typeof res !== 'undefined') { + result.push(res); + } + } + + var resultSequence = createSequence(); + if(lastStep && result.length === 1 && Array.isArray(result[0]) && !isSequence(result[0])) { + resultSequence = result[0]; + } else { + // flatten the sequence + result.forEach(function(res) { + if (!Array.isArray(res) || res.cons) { + // it's not an array - just push into the result sequence + resultSequence.push(res); + } else { + // res is a sequence - flatten it into the parent sequence + Array.prototype.push.apply(resultSequence, res); + } + }); + } + + return resultSequence; + } + + function* evaluateStages(stages, input, environment) { + var result = input; + for(var ss = 0; ss < stages.length; ss++) { + var stage = stages[ss]; + switch(stage.type) { + case 'filter': + result = yield * evaluateFilter(stage.expr, result, environment); + break; + case 'index': + for(var ee = 0; ee < result.length; ee++) { + var tuple = result[ee]; + tuple[stage.value] = ee; + } + break; + } + } + return result; + } + + /** + * Evaluate a step within a path + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} tupleBindings - The tuple stream + * @param {Object} environment - Environment + * @returns {*} Evaluated input data + */ + function* evaluateTupleStep(expr, input, tupleBindings, environment) { + var result; + if(expr.type === 'sort') { + if(tupleBindings) { + result = yield* evaluateSortExpression(expr, tupleBindings, environment); + } else { + var sorted = yield* evaluateSortExpression(expr, input, environment); + result = createSequence(); + result.tupleStream = true; + for(var ss = 0; ss < sorted.length; ss++) { + var tuple = {'@': sorted[ss]}; + tuple[expr.index] = ss; + result.push(tuple); + } + } + if(expr.stages) { + result = yield* evaluateStages(expr.stages, result, environment); + } + return result; + } + + result = createSequence(); + result.tupleStream = true; + var stepEnv = environment; + if(tupleBindings === undefined) { + tupleBindings = input.map(item => { return {'@': item} }); + } + + for(var ee = 0; ee < tupleBindings.length; ee++) { + stepEnv = createFrameFromTuple(environment, tupleBindings[ee]); + var res = yield* evaluate(expr, tupleBindings[ee]['@'], stepEnv); + // res is the binding sequence for the output tuple stream + if(typeof res !== 'undefined') { + if (!Array.isArray(res)) { + res = [res]; + } + for (var bb = 0; bb < res.length; bb++) { + tuple = {}; + Object.assign(tuple, tupleBindings[ee]); + if(res.tupleStream) { + Object.assign(tuple, res[bb]); + } else { + if (expr.focus) { + tuple[expr.focus] = res[bb]; + tuple['@'] = tupleBindings[ee]['@']; + } else { + tuple['@'] = res[bb]; + } + if (expr.index) { + tuple[expr.index] = bb; + } + if (expr.ancestor) { + tuple[expr.ancestor.label] = tupleBindings[ee]['@']; + } + } + result.push(tuple); + } + } + } + + if(expr.stages) { + result = yield * evaluateStages(expr.stages, result, environment); + } + + return result; + } + + /** + * Apply filter predicate to input data + * @param {Object} predicate - filter expression + * @param {Object} input - Input data to apply predicates against + * @param {Object} environment - Environment + * @returns {*} Result after applying predicates + */ + function* evaluateFilter(predicate, input, environment) { + var results = createSequence(); + if( input && input.tupleStream) { + results.tupleStream = true; + } + if (!Array.isArray(input)) { + input = createSequence(input); + } + if (predicate.type === 'number') { + var index = Math.floor(predicate.value); // round it down + if (index < 0) { + // count in from end of array + index = input.length + index; + } + var item = input[index]; + if(typeof item !== 'undefined') { + if(Array.isArray(item)) { + results = item; + } else { + results.push(item); + } + } + } else { + for (index = 0; index < input.length; index++) { + var item = input[index]; + var context = item; + var env = environment; + if(input.tupleStream) { + context = item['@']; + env = createFrameFromTuple(environment, item); + } + var res = yield* evaluate(predicate, context, env); + if (isNumeric(res)) { + res = [res]; + } + if (isArrayOfNumbers(res)) { + res.forEach(function (ires) { + // round it down + var ii = Math.floor(ires); + if (ii < 0) { + // count in from end of array + ii = input.length + ii; + } + if (ii === index) { + results.push(item); + } + }); + } else if (fn.boolean(res)) { // truthy + results.push(item); + } + } + } + return results; + } + + /** + * Evaluate binary expression against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} Evaluated input data + */ + function * evaluateBinary(expr, input, environment) { + var result; + var lhs = yield * evaluate(expr.lhs, input, environment); + var rhs = yield * evaluate(expr.rhs, input, environment); + var op = expr.value; + + try { + switch (op) { + case '+': + case '-': + case '*': + case '/': + case '%': + result = evaluateNumericExpression(lhs, rhs, op); + break; + case '=': + case '!=': + result = evaluateEqualityExpression(lhs, rhs, op); + break; + case '<': + case '<=': + case '>': + case '>=': + result = evaluateComparisonExpression(lhs, rhs, op); + break; + case '&': + result = evaluateStringConcat(lhs, rhs); + break; + case 'and': + case 'or': + result = evaluateBooleanExpression(lhs, rhs, op); + break; + case '..': + result = evaluateRangeExpression(lhs, rhs); + break; + case 'in': + result = evaluateIncludesExpression(lhs, rhs); + break; + } + } catch(err) { + err.position = expr.position; + err.token = op; + throw err; + } + return result; + } + + /** + * Evaluate unary expression against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} Evaluated input data + */ + function* evaluateUnary(expr, input, environment) { + var result; + + switch (expr.value) { + case '-': + result = yield * evaluate(expr.expression, input, environment); + if(typeof result === 'undefined') { + result = undefined; + } else if (isNumeric(result)) { + result = -result; + } else { + throw { + code: "D1002", + stack: (new Error()).stack, + position: expr.position, + token: expr.value, + value: result + }; + } + break; + case '[': + // array constructor - evaluate each item + result = []; + for(var ii = 0; ii < expr.expressions.length; ii++) { + var item = expr.expressions[ii]; + var value = yield * evaluate(item, input, environment); + if (typeof value !== 'undefined') { + if(item.value === '[') { + result.push(value); + } else { + result = fn.append(result, value); + } + } + } + if(expr.consarray) { + Object.defineProperty(result, 'cons', { + enumerable: false, + configurable: false, + value: true + }); + } + break; + case '{': + // object constructor - apply grouping + result = yield * evaluateGroupExpression(expr, input, environment); + break; + + } + return result; + } + + /** + * Evaluate name object against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} Evaluated input data + */ + function evaluateName(expr, input, environment) { + // lookup the 'name' item in the input + return fn.lookup(input, expr.value); + } + + /** + * Evaluate literal against input data + * @param {Object} expr - JSONata expression + * @returns {*} Evaluated input data + */ + function evaluateLiteral(expr) { + return expr.value; + } + + /** + * Evaluate wildcard against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @returns {*} Evaluated input data + */ + function evaluateWildcard(expr, input) { + var results = createSequence(); + if (input !== null && typeof input === 'object') { + Object.keys(input).forEach(function (key) { + var value = input[key]; + if(Array.isArray(value)) { + value = flatten(value); + results = fn.append(results, value); + } else { + results.push(value); + } + }); + } + + // result = normalizeSequence(results); + return results; + } + + /** + * Returns a flattened array + * @param {Array} arg - the array to be flatten + * @param {Array} flattened - carries the flattened array - if not defined, will initialize to [] + * @returns {Array} - the flattened array + */ + function flatten(arg, flattened) { + if(typeof flattened === 'undefined') { + flattened = []; + } + if(Array.isArray(arg)) { + arg.forEach(function (item) { + flatten(item, flattened); + }); + } else { + flattened.push(arg); + } + return flattened; + } + + /** + * Evaluate descendants against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @returns {*} Evaluated input data + */ + function evaluateDescendants(expr, input) { + var result; + var resultSequence = createSequence(); + if (typeof input !== 'undefined') { + // traverse all descendants of this object/array + recurseDescendants(input, resultSequence); + if (resultSequence.length === 1) { + result = resultSequence[0]; + } else { + result = resultSequence; + } + } + return result; + } + + /** + * Recurse through descendants + * @param {Object} input - Input data + * @param {Object} results - Results + */ + function recurseDescendants(input, results) { + // this is the equivalent of //* in XPath + if (!Array.isArray(input)) { + results.push(input); + } + if (Array.isArray(input)) { + input.forEach(function (member) { + recurseDescendants(member, results); + }); + } else if (input !== null && typeof input === 'object') { + Object.keys(input).forEach(function (key) { + recurseDescendants(input[key], results); + }); + } + } + + /** + * Evaluate numeric expression against input data + * @param {Object} lhs - LHS value + * @param {Object} rhs - RHS value + * @param {Object} op - opcode + * @returns {*} Result + */ + function evaluateNumericExpression(lhs, rhs, op) { + var result; + + if (typeof lhs !== 'undefined' && !isNumeric(lhs)) { + throw { + code: "T2001", + stack: (new Error()).stack, + value: lhs + }; + } + if (typeof rhs !== 'undefined' && !isNumeric(rhs)) { + throw { + code: "T2002", + stack: (new Error()).stack, + value: rhs + }; + } + + if (typeof lhs === 'undefined' || typeof rhs === 'undefined') { + // if either side is undefined, the result is undefined + return result; + } + + switch (op) { + case '+': + result = lhs + rhs; + break; + case '-': + result = lhs - rhs; + break; + case '*': + result = lhs * rhs; + break; + case '/': + result = lhs / rhs; + break; + case '%': + result = lhs % rhs; + break; + } + return result; + } + + /** + * Evaluate equality expression against input data + * @param {Object} lhs - LHS value + * @param {Object} rhs - RHS value + * @param {Object} op - opcode + * @returns {*} Result + */ + function evaluateEqualityExpression(lhs, rhs, op) { + var result; + + // type checks + var ltype = typeof lhs; + var rtype = typeof rhs; + + if (ltype === 'undefined' || rtype === 'undefined') { + // if either side is undefined, the result is false + return false; + } + + switch (op) { + case '=': + result = isDeepEqual(lhs, rhs); + break; + case '!=': + result = !isDeepEqual(lhs, rhs); + break; + } + return result; + } + + /** + * Evaluate comparison expression against input data + * @param {Object} lhs - LHS value + * @param {Object} rhs - RHS value + * @param {Object} op - opcode + * @returns {*} Result + */ + function evaluateComparisonExpression(lhs, rhs, op) { + var result; + + // type checks + var ltype = typeof lhs; + var rtype = typeof rhs; + + var lcomparable = (ltype === 'undefined' || ltype === 'string' || ltype === 'number'); + var rcomparable = (rtype === 'undefined' || rtype === 'string' || rtype === 'number'); + + // if either aa or bb are not comparable (string or numeric) values, then throw an error + if (!lcomparable || !rcomparable) { + throw { + code: "T2010", + stack: (new Error()).stack, + value: !(ltype === 'string' || ltype === 'number') ? lhs : rhs + }; + } + + // if either side is undefined, the result is undefined + if (ltype === 'undefined' || rtype === 'undefined') { + return undefined; + } + + //if aa and bb are not of the same type + if (ltype !== rtype) { + throw { + code: "T2009", + stack: (new Error()).stack, + value: lhs, + value2: rhs + }; + } + + switch (op) { + case '<': + result = lhs < rhs; + break; + case '<=': + result = lhs <= rhs; + break; + case '>': + result = lhs > rhs; + break; + case '>=': + result = lhs >= rhs; + break; + } + return result; + } + + /** + * Inclusion operator - in + * + * @param {Object} lhs - LHS value + * @param {Object} rhs - RHS value + * @returns {boolean} - true if lhs is a member of rhs + */ + function evaluateIncludesExpression(lhs, rhs) { + var result = false; + + if (typeof lhs === 'undefined' || typeof rhs === 'undefined') { + // if either side is undefined, the result is false + return false; + } + + if(!Array.isArray(rhs)) { + rhs = [rhs]; + } + + for(var i = 0; i < rhs.length; i++) { + if(rhs[i] === lhs) { + result = true; + break; + } + } + + return result; + } + + /** + * Evaluate boolean expression against input data + * @param {Object} lhs - LHS value + * @param {Object} rhs - RHS value + * @param {Object} op - opcode + * @returns {*} Result + */ + function evaluateBooleanExpression(lhs, rhs, op) { + var result; + + var lBool = fn.boolean(lhs); + var rBool = fn.boolean(rhs); + + if (typeof lBool === 'undefined') { + lBool = false; + } + + if (typeof rBool === 'undefined') { + rBool = false; + } + + switch (op) { + case 'and': + result = lBool && rBool; + break; + case 'or': + result = lBool || rBool; + break; + } + return result; + } + + /** + * Evaluate string concatenation against input data + * @param {Object} lhs - LHS value + * @param {Object} rhs - RHS value + * @returns {string|*} Concatenated string + */ + function evaluateStringConcat(lhs, rhs) { + var result; + + var lstr = ''; + var rstr = ''; + if (typeof lhs !== 'undefined') { + lstr = fn.string(lhs); + } + if (typeof rhs !== 'undefined') { + rstr = fn.string(rhs); + } + + result = lstr.concat(rstr); + return result; + } + + /** + * Evaluate group expression against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {{}} Evaluated input data + */ + function* evaluateGroupExpression(expr, input, environment) { + var result = {}; + var groups = {}; + var reduce = input && input.tupleStream ? true : false; + // group the input sequence by 'key' expression + if (!Array.isArray(input)) { + input = createSequence(input); + } + + for(var itemIndex = 0; itemIndex < input.length; itemIndex++) { + var item = input[itemIndex]; + var env = reduce ? createFrameFromTuple(environment, item) : environment; + for(var pairIndex = 0; pairIndex < expr.lhs.length; pairIndex++) { + var pair = expr.lhs[pairIndex]; + var key = yield * evaluate(pair[0], reduce ? item['@'] : item, env); + // key has to be a string + if (typeof key !== 'string') { + throw { + code: "T1003", + stack: (new Error()).stack, + position: expr.position, + value: key + }; + } + var entry = {data: item, exprIndex: pairIndex}; + if (groups.hasOwnProperty(key)) { + // a value already exists in this slot + if(groups[key].exprIndex !== pairIndex) { + // this key has been generated by another expression in this group + // when multiple key expressions evaluate to the same key, then error D1009 must be thrown + throw { + code: "D1009", + stack: (new Error()).stack, + position: expr.position, + value: key + }; + } + + // append it as an array + groups[key].data = fn.append(groups[key].data, item); + } else { + groups[key] = entry; + } + } + } + + // iterate over the groups to evaluate the 'value' expression + for (key in groups) { + entry = groups[key]; + var context = entry.data; + var env = environment; + if (reduce) { + var tuple = reduceTupleStream(entry.data); + context = tuple['@']; + delete tuple['@']; + env = createFrameFromTuple(environment, tuple); + } + var value = yield * evaluate(expr.lhs[entry.exprIndex][1], context, env); + if(typeof value !== 'undefined') { + result[key] = value; + } + } + + return result; + } + + function reduceTupleStream(tupleStream) { + if(!Array.isArray(tupleStream)) { + return tupleStream; + } + var result = {}; + Object.assign(result, tupleStream[0]); + for(var ii = 1; ii < tupleStream.length; ii++) { + for(const prop in tupleStream[ii]) { + result[prop] = fn.append(result[prop], tupleStream[ii][prop]); + } + } + return result; + } + + /** + * Evaluate range expression against input data + * @param {Object} lhs - LHS value + * @param {Object} rhs - RHS value + * @returns {Array} Resultant array + */ + function evaluateRangeExpression(lhs, rhs) { + var result; + + if (typeof lhs !== 'undefined' && !Number.isInteger(lhs)) { + throw { + code: "T2003", + stack: (new Error()).stack, + value: lhs + }; + } + if (typeof rhs !== 'undefined' && !Number.isInteger(rhs)) { + throw { + code: "T2004", + stack: (new Error()).stack, + value: rhs + }; + } + + if (typeof lhs === 'undefined' || typeof rhs === 'undefined') { + // if either side is undefined, the result is undefined + return result; + } + + if (lhs > rhs) { + // if the lhs is greater than the rhs, return undefined + return result; + } + + // limit the size of the array to ten million entries (1e7) + // this is an implementation defined limit to protect against + // memory and performance issues. This value may increase in the future. + var size = rhs - lhs + 1; + if(size > 1e7) { + throw { + code: "D2014", + stack: (new Error()).stack, + value: size + }; + } + + result = new Array(size); + for (var item = lhs, index = 0; item <= rhs; item++, index++) { + result[index] = item; + } + result.sequence = true; + return result; + } + + /** + * Evaluate bind expression against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} Evaluated input data + */ + function* evaluateBindExpression(expr, input, environment) { + // The RHS is the expression to evaluate + // The LHS is the name of the variable to bind to - should be a VARIABLE token (enforced by parser) + var value = yield * evaluate(expr.rhs, input, environment); + environment.bind(expr.lhs.value, value); + return value; + } + + /** + * Evaluate condition against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} Evaluated input data + */ + function* evaluateCondition(expr, input, environment) { + var result; + var condition = yield * evaluate(expr.condition, input, environment); + if (fn.boolean(condition)) { + result = yield * evaluate(expr.then, input, environment); + } else if (typeof expr.else !== 'undefined') { + result = yield * evaluate(expr.else, input, environment); + } + return result; + } + + /** + * Evaluate block against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} Evaluated input data + */ + function* evaluateBlock(expr, input, environment) { + var result; + // create a new frame to limit the scope of variable assignments + // TODO, only do this if the post-parse stage has flagged this as required + var frame = createFrame(environment); + // invoke each expression in turn + // only return the result of the last one + for(var ii = 0; ii < expr.expressions.length; ii++) { + result = yield * evaluate(expr.expressions[ii], input, frame); + } + + return result; + } + + /** + * Prepare a regex + * @param {Object} expr - expression containing regex + * @returns {Function} Higher order function representing prepared regex + */ + function evaluateRegex(expr) { + var re = new RegExp(expr.value); + var closure = function(str, fromIndex) { + var result; + re.lastIndex = fromIndex || 0; + var match = re.exec(str); + if(match !== null) { + result = { + match: match[0], + start: match.index, + end: match.index + match[0].length, + groups: [] + }; + if(match.length > 1) { + for(var i = 1; i < match.length; i++) { + result.groups.push(match[i]); + } + } + result.next = function() { + if(re.lastIndex >= str.length) { + return undefined; + } else { + var next = closure(str, re.lastIndex); + if(next && next.match === '') { + // matches zero length string; this will never progress + throw { + code: "D1004", + stack: (new Error()).stack, + position: expr.position, + value: expr.value.source + }; + } + return next; + } + }; + } + + return result; + }; + return closure; + } + + /** + * Evaluate variable against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} Evaluated input data + */ + function evaluateVariable(expr, input, environment) { + // lookup the variable value in the environment + var result; + // if the variable name is empty string, then it refers to context value + if (expr.value === '') { + result = input && input.outerWrapper ? input[0] : input; + } else { + result = environment.lookup(expr.value); + } + return result; + } + + /** + * sort / order-by operator + * @param {Object} expr - AST for operator + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} Ordered sequence + */ + function* evaluateSortExpression(expr, input, environment) { + var result; + + // evaluate the lhs, then sort the results in order according to rhs expression + //var lhs = yield * evaluate(expr.lhs, input, environment); + var lhs = input; + var isTupleSort = input.tupleStream ? true : false; + + // sort the lhs array + // use comparator function + var comparator = function*(a, b) { // eslint-disable-line require-yield + // expr.terms is an array of order-by in priority order + var comp = 0; + for(var index = 0; comp === 0 && index < expr.terms.length; index++) { + var term = expr.terms[index]; + //evaluate the sort term in the context of a + var context = a; + var env = environment; + if(isTupleSort) { + context = a['@']; + env = createFrameFromTuple(environment, a); + } + var aa = yield * evaluate(term.expression, context, env); + //evaluate the sort term in the context of b + context = b; + env = environment; + if(isTupleSort) { + context = b['@']; + env = createFrameFromTuple(environment, b); + } + var bb = yield * evaluate(term.expression, context, env); + + // type checks + var atype = typeof aa; + var btype = typeof bb; + // undefined should be last in sort order + if(atype === 'undefined') { + // swap them, unless btype is also undefined + comp = (btype === 'undefined') ? 0 : 1; + continue; + } + if(btype === 'undefined') { + comp = -1; + continue; + } + + // if aa or bb are not string or numeric values, then throw an error + if(!(atype === 'string' || atype === 'number') || !(btype === 'string' || btype === 'number')) { + throw { + code: "T2008", + stack: (new Error()).stack, + position: expr.position, + value: !(atype === 'string' || atype === 'number') ? aa : bb + }; + } + + //if aa and bb are not of the same type + if(atype !== btype) { + throw { + code: "T2007", + stack: (new Error()).stack, + position: expr.position, + value: aa, + value2: bb + }; + } + if(aa === bb) { + // both the same - move on to next term + continue; + } else if (aa < bb) { + comp = -1; + } else { + comp = 1; + } + if(term.descending === true) { + comp = -comp; + } + } + // only swap a & b if comp equals 1 + return comp === 1; + }; + + var focus = { + environment: environment, + input: input + }; + // the `focus` is passed in as the `this` for the invoked function + result = yield * fn.sort.apply(focus, [lhs, comparator]); + + return result; + } + + /** + * create a transformer function + * @param {Object} expr - AST for operator + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} tranformer function + */ + function evaluateTransformExpression(expr, input, environment) { + // create a function to implement the transform definition + var transformer = function*(obj) { // signature <(oa):o> + // undefined inputs always return undefined + if(typeof obj === 'undefined') { + return undefined; + } + + // this function returns a copy of obj with changes specified by the pattern/operation + var cloneFunction = environment.lookup('clone'); + if(!isFunction(cloneFunction)) { + // throw type error + throw { + code: "T2013", + stack: (new Error()).stack, + position: expr.position + }; + } + var result = yield * apply(cloneFunction, [obj], null, environment); + var matches = yield * evaluate(expr.pattern, result, environment); + if(typeof matches !== 'undefined') { + if(!Array.isArray(matches)) { + matches = [matches]; + } + for(var ii = 0; ii < matches.length; ii++) { + var match = matches[ii]; + // evaluate the update value for each match + var update = yield * evaluate(expr.update, match, environment); + // update must be an object + var updateType = typeof update; + if(updateType !== 'undefined') { + if(updateType !== 'object' || update === null || Array.isArray(update)) { + // throw type error + throw { + code: "T2011", + stack: (new Error()).stack, + position: expr.update.position, + value: update + }; + } + // merge the update + for(var prop in update) { + match[prop] = update[prop]; + } + } + + // delete, if specified, must be an array of strings (or single string) + if(typeof expr.delete !== 'undefined') { + var deletions = yield * evaluate(expr.delete, match, environment); + if(typeof deletions !== 'undefined') { + var val = deletions; + if (!Array.isArray(deletions)) { + deletions = [deletions]; + } + if (!isArrayOfStrings(deletions)) { + // throw type error + throw { + code: "T2012", + stack: (new Error()).stack, + position: expr.delete.position, + value: val + }; + } + for (var jj = 0; jj < deletions.length; jj++) { + if(typeof match === 'object' && match !== null) { + delete match[deletions[jj]]; + } + } + } + } + } + } + + return result; + }; + + return defineFunction(transformer, '<(oa):o>'); + } + + var chainAST = parser('function($f, $g) { function($x){ $g($f($x)) } }'); + + /** + * Apply the function on the RHS using the sequence on the LHS as the first argument + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} Evaluated input data + */ + function* evaluateApplyExpression(expr, input, environment) { + var result; + + + var lhs = yield * evaluate(expr.lhs, input, environment); + if(expr.rhs.type === 'function') { + // this is a function _invocation_; invoke it with lhs expression as the first argument + result = yield * evaluateFunction(expr.rhs, input, environment, { context: lhs }); + } else { + var func = yield * evaluate(expr.rhs, input, environment); + + if(!isFunction(func)) { + throw { + code: "T2006", + stack: (new Error()).stack, + position: expr.position, + value: func + }; + } + + if(isFunction(lhs)) { + // this is function chaining (func1 ~> func2) + // λ($f, $g) { λ($x){ $g($f($x)) } } + var chain = yield * evaluate(chainAST, null, environment); + result = yield * apply(chain, [lhs, func], null, environment); + } else { + result = yield * apply(func, [lhs], null, environment); + } + + } + + return result; + } + + /** + * Evaluate function against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} Evaluated input data + */ + function* evaluateFunction(expr, input, environment, applyto) { + var result; + + // create the procedure + // can't assume that expr.procedure is a lambda type directly + // could be an expression that evaluates to a function (e.g. variable reference, parens expr etc. + // evaluate it generically first, then check that it is a function. Throw error if not. + var proc = yield * evaluate(expr.procedure, input, environment); + + if (typeof proc === 'undefined' && expr.procedure.type === 'path' && environment.lookup(expr.procedure.steps[0].value)) { + // help the user out here if they simply forgot the leading $ + throw { + code: "T1005", + stack: (new Error()).stack, + position: expr.position, + token: expr.procedure.steps[0].value + }; + } + + var evaluatedArgs = []; + if(typeof applyto !== 'undefined') { + evaluatedArgs.push(applyto.context); + } + // eager evaluation - evaluate the arguments + for (var jj = 0; jj < expr.arguments.length; jj++) { + const arg = yield* evaluate(expr.arguments[jj], input, environment); + if(isFunction(arg)) { + // wrap this in a closure + const closure = function* (...params) { + // invoke func + return yield * apply(arg, params, null, environment); + }; + closure.arity = getFunctionArity(arg); + evaluatedArgs.push(closure); + } else { + evaluatedArgs.push(arg); + } + } + // apply the procedure + var procName = expr.procedure.type === 'path' ? expr.procedure.steps[0].value : expr.procedure.value; + try { + if(typeof proc === 'object') { + proc.token = procName; + proc.position = expr.position; + } + result = yield * apply(proc, evaluatedArgs, input, environment); + } catch (err) { + if(!err.position) { + // add the position field to the error + err.position = expr.position; + } + if (!err.token) { + // and the function identifier + err.token = procName; + } + throw err; + } + return result; + } + + /** + * Apply procedure or function + * @param {Object} proc - Procedure + * @param {Array} args - Arguments + * @param {Object} input - input + * @param {Object} environment - environment + * @returns {*} Result of procedure + */ + function* apply(proc, args, input, environment) { + var result; + result = yield * applyInner(proc, args, input, environment); + while(isLambda(result) && result.thunk === true) { + // trampoline loop - this gets invoked as a result of tail-call optimization + // the function returned a tail-call thunk + // unpack it, evaluate its arguments, and apply the tail call + var next = yield * evaluate(result.body.procedure, result.input, result.environment); + if(result.body.procedure.type === 'variable') { + next.token = result.body.procedure.value; + } + next.position = result.body.procedure.position; + var evaluatedArgs = []; + for(var ii = 0; ii < result.body.arguments.length; ii++) { + evaluatedArgs.push(yield * evaluate(result.body.arguments[ii], result.input, result.environment)); + } + + result = yield * applyInner(next, evaluatedArgs, input, environment); + } + return result; + } + + /** + * Apply procedure or function + * @param {Object} proc - Procedure + * @param {Array} args - Arguments + * @param {Object} input - input + * @param {Object} environment - environment + * @returns {*} Result of procedure + */ + function* applyInner(proc, args, input, environment) { + var result; + try { + var validatedArgs = args; + if (proc) { + validatedArgs = validateArguments(proc.signature, args, input); + } + + if (isLambda(proc)) { + result = yield* applyProcedure(proc, validatedArgs); + } else if (proc && proc._jsonata_function === true) { + var focus = { + environment: environment, + input: input + }; + // the `focus` is passed in as the `this` for the invoked function + result = proc.implementation.apply(focus, validatedArgs); + // `proc.implementation` might be a generator function + // and `result` might be a generator - if so, yield + if (isIterable(result)) { + result = yield* result; + } + } else if (typeof proc === 'function') { + // typically these are functions that are returned by the invocation of plugin functions + // the `input` is being passed in as the `this` for the invoked function + // this is so that functions that return objects containing functions can chain + // e.g. $func().next().next() + result = proc.apply(input, validatedArgs); + /* istanbul ignore next */ + if (isIterable(result)) { + result = yield* result; + } + } else { + throw { + code: "T1006", + stack: (new Error()).stack + }; + } + } catch(err) { + if(proc) { + if (typeof err.token == 'undefined' && typeof proc.token !== 'undefined') { + err.token = proc.token; + } + err.position = proc.position; + } + throw err; + } + return result; + } + + /** + * Evaluate lambda against input data + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {{lambda: boolean, input: *, environment: *, arguments: *, body: *}} Evaluated input data + */ + function evaluateLambda(expr, input, environment) { + // make a function (closure) + var procedure = { + _jsonata_lambda: true, + input: input, + environment: environment, + arguments: expr.arguments, + signature: expr.signature, + body: expr.body + }; + if(expr.thunk === true) { + procedure.thunk = true; + } + procedure.apply = function*(self, args) { + return yield * apply(procedure, args, input, self.environment); + }; + return procedure; + } + + /** + * Evaluate partial application + * @param {Object} expr - JSONata expression + * @param {Object} input - Input data to evaluate against + * @param {Object} environment - Environment + * @returns {*} Evaluated input data + */ + function* evaluatePartialApplication(expr, input, environment) { + // partially apply a function + var result; + // evaluate the arguments + var evaluatedArgs = []; + for(var ii = 0; ii < expr.arguments.length; ii++) { + var arg = expr.arguments[ii]; + if (arg.type === 'operator' && arg.value === '?') { + evaluatedArgs.push(arg); + } else { + evaluatedArgs.push(yield * evaluate(arg, input, environment)); + } + } + // lookup the procedure + var proc = yield * evaluate(expr.procedure, input, environment); + if (typeof proc === 'undefined' && expr.procedure.type === 'path' && environment.lookup(expr.procedure.steps[0].value)) { + // help the user out here if they simply forgot the leading $ + throw { + code: "T1007", + stack: (new Error()).stack, + position: expr.position, + token: expr.procedure.steps[0].value + }; + } + if (isLambda(proc)) { + result = partialApplyProcedure(proc, evaluatedArgs); + } else if (proc && proc._jsonata_function === true) { + result = partialApplyNativeFunction(proc.implementation, evaluatedArgs); + } else if (typeof proc === 'function') { + result = partialApplyNativeFunction(proc, evaluatedArgs); + } else { + throw { + code: "T1008", + stack: (new Error()).stack, + position: expr.position, + token: expr.procedure.type === 'path' ? expr.procedure.steps[0].value : expr.procedure.value + }; + } + return result; + } + + /** + * Validate the arguments against the signature validator (if it exists) + * @param {Function} signature - validator function + * @param {Array} args - function arguments + * @param {*} context - context value + * @returns {Array} - validated arguments + */ + function validateArguments(signature, args, context) { + if(typeof signature === 'undefined') { + // nothing to validate + return args; + } + var validatedArgs = signature.validate(args, context); + return validatedArgs; + } + + /** + * Apply procedure + * @param {Object} proc - Procedure + * @param {Array} args - Arguments + * @returns {*} Result of procedure + */ + function* applyProcedure(proc, args) { + var result; + var env = createFrame(proc.environment); + proc.arguments.forEach(function (param, index) { + env.bind(param.value, args[index]); + }); + if (typeof proc.body === 'function') { + // this is a lambda that wraps a native function - generated by partially evaluating a native + result = yield * applyNativeFunction(proc.body, env); + } else { + result = yield * evaluate(proc.body, proc.input, env); + } + return result; + } + + /** + * Partially apply procedure + * @param {Object} proc - Procedure + * @param {Array} args - Arguments + * @returns {{lambda: boolean, input: *, environment: {bind, lookup}, arguments: Array, body: *}} Result of partially applied procedure + */ + function partialApplyProcedure(proc, args) { + // create a closure, bind the supplied parameters and return a function that takes the remaining (?) parameters + var env = createFrame(proc.environment); + var unboundArgs = []; + proc.arguments.forEach(function (param, index) { + var arg = args[index]; + if (arg && arg.type === 'operator' && arg.value === '?') { + unboundArgs.push(param); + } else { + env.bind(param.value, arg); + } + }); + var procedure = { + _jsonata_lambda: true, + input: proc.input, + environment: env, + arguments: unboundArgs, + body: proc.body + }; + return procedure; + } + + /** + * Partially apply native function + * @param {Function} native - Native function + * @param {Array} args - Arguments + * @returns {{lambda: boolean, input: *, environment: {bind, lookup}, arguments: Array, body: *}} Result of partially applying native function + */ + function partialApplyNativeFunction(native, args) { + // create a lambda function that wraps and invokes the native function + // get the list of declared arguments from the native function + // this has to be picked out from the toString() value + var sigArgs = getNativeFunctionArguments(native); + sigArgs = sigArgs.map(function (sigArg) { + return '$' + sigArg.trim(); + }); + var body = 'function(' + sigArgs.join(', ') + '){ _ }'; + + var bodyAST = parser(body); + bodyAST.body = native; + + var partial = partialApplyProcedure(bodyAST, args); + return partial; + } + + /** + * Apply native function + * @param {Object} proc - Procedure + * @param {Object} env - Environment + * @returns {*} Result of applying native function + */ + function* applyNativeFunction(proc, env) { + var sigArgs = getNativeFunctionArguments(proc); + // generate the array of arguments for invoking the function - look them up in the environment + var args = sigArgs.map(function (sigArg) { + return env.lookup(sigArg.trim()); + }); + + var focus = { + environment: env + }; + var result = proc.apply(focus, args); + if(isIterable(result)) { + result = yield * result; + } + return result; + } + + /** + * Get native function arguments + * @param {Function} func - Function + * @returns {*|Array} Native function arguments + */ + function getNativeFunctionArguments(func) { + var signature = func.toString(); + var sigParens = /\(([^)]*)\)/.exec(signature)[1]; // the contents of the parens + var sigArgs = sigParens.split(','); + return sigArgs; + } + + /** + * Creates a function definition + * @param {Function} func - function implementation in Javascript + * @param {string} signature - JSONata function signature definition + * @returns {{implementation: *, signature: *}} function definition + */ + function defineFunction(func, signature) { + var definition = { + _jsonata_function: true, + implementation: func + }; + if(typeof signature !== 'undefined') { + definition.signature = parseSignature(signature); + } + return definition; + } + + + /** + * parses and evaluates the supplied expression + * @param {string} expr - expression to evaluate + * @returns {*} - result of evaluating the expression + */ + function* functionEval(expr, focus) { + // undefined inputs always return undefined + if(typeof expr === 'undefined') { + return undefined; + } + var input = this.input; + if(typeof focus !== 'undefined') { + input = focus; + } + + try { + var ast = parser(expr, false); + } catch(err) { + // error parsing the expression passed to $eval + populateMessage(err); + throw { + stack: (new Error()).stack, + code: "D3120", + value: err.message, + error: err + }; + } + try { + var result = yield* evaluate(ast, input, this.environment); + } catch(err) { + // error evaluating the expression passed to $eval + populateMessage(err); + throw { + stack: (new Error()).stack, + code: "D3121", + value:err.message, + error: err + }; + } + + return result; + } + + /** + * Clones an object + * @param {Object} arg - object to clone (deep copy) + * @returns {*} - the cloned object + */ + function functionClone(arg) { + // undefined inputs always return undefined + if(typeof arg === 'undefined') { + return undefined; + } + + return JSON.parse(fn.string(arg)); + } + + /** + * Create frame + * @param {Object} enclosingEnvironment - Enclosing environment + * @returns {{bind: bind, lookup: lookup}} Created frame + */ + function createFrame(enclosingEnvironment) { + var bindings = {}; + return { + bind: function (name, value) { + bindings[name] = value; + }, + lookup: function (name) { + var value; + if(bindings.hasOwnProperty(name)) { + value = bindings[name]; + } else if (enclosingEnvironment) { + value = enclosingEnvironment.lookup(name); + } + return value; + }, + timestamp: enclosingEnvironment ? enclosingEnvironment.timestamp : null, + async: enclosingEnvironment ? enclosingEnvironment.async : false, + global: enclosingEnvironment ? enclosingEnvironment.global : { + ancestry: [ null ] + } + }; + } + + // Function registration + staticFrame.bind('sum', defineFunction(fn.sum, ':n>')); + staticFrame.bind('count', defineFunction(fn.count, '')); + staticFrame.bind('max', defineFunction(fn.max, ':n>')); + staticFrame.bind('min', defineFunction(fn.min, ':n>')); + staticFrame.bind('average', defineFunction(fn.average, ':n>')); + staticFrame.bind('string', defineFunction(fn.string, '')); + staticFrame.bind('substring', defineFunction(fn.substring, '')); + staticFrame.bind('substringBefore', defineFunction(fn.substringBefore, '')); + staticFrame.bind('substringAfter', defineFunction(fn.substringAfter, '')); + staticFrame.bind('lowercase', defineFunction(fn.lowercase, '')); + staticFrame.bind('uppercase', defineFunction(fn.uppercase, '')); + staticFrame.bind('length', defineFunction(fn.length, '')); + staticFrame.bind('trim', defineFunction(fn.trim, '')); + staticFrame.bind('pad', defineFunction(fn.pad, '')); + staticFrame.bind('match', defineFunction(fn.match, 'n?:a>')); + staticFrame.bind('contains', defineFunction(fn.contains, '')); // TODO ):b> + staticFrame.bind('replace', defineFunction(fn.replace, '')); // TODO )(sf)n?:s> + staticFrame.bind('split', defineFunction(fn.split, '>')); // TODO )n?:a> + staticFrame.bind('join', defineFunction(fn.join, 's?:s>')); + staticFrame.bind('formatNumber', defineFunction(fn.formatNumber, '')); + staticFrame.bind('formatBase', defineFunction(fn.formatBase, '')); + staticFrame.bind('formatInteger', defineFunction(datetime.formatInteger, '')); + staticFrame.bind('parseInteger', defineFunction(datetime.parseInteger, '')); + staticFrame.bind('number', defineFunction(fn.number, '<(nsb)-:n>')); + staticFrame.bind('floor', defineFunction(fn.floor, '')); + staticFrame.bind('ceil', defineFunction(fn.ceil, '')); + staticFrame.bind('round', defineFunction(fn.round, '')); + staticFrame.bind('abs', defineFunction(fn.abs, '')); + staticFrame.bind('sqrt', defineFunction(fn.sqrt, '')); + staticFrame.bind('power', defineFunction(fn.power, '')); + staticFrame.bind('random', defineFunction(fn.random, '<:n>')); + staticFrame.bind('boolean', defineFunction(fn.boolean, '')); + staticFrame.bind('not', defineFunction(fn.not, '')); + staticFrame.bind('map', defineFunction(fn.map, '')); + staticFrame.bind('zip', defineFunction(fn.zip, '')); + staticFrame.bind('filter', defineFunction(fn.filter, '')); + staticFrame.bind('single', defineFunction(fn.single, '')); + staticFrame.bind('reduce', defineFunction(fn.foldLeft, '')); // TODO aj?:j> + staticFrame.bind('sift', defineFunction(fn.sift, '')); + staticFrame.bind('keys', defineFunction(fn.keys, '>')); + staticFrame.bind('lookup', defineFunction(fn.lookup, '')); + staticFrame.bind('append', defineFunction(fn.append, '')); + staticFrame.bind('exists', defineFunction(fn.exists, '')); + staticFrame.bind('spread', defineFunction(fn.spread, '>')); + staticFrame.bind('merge', defineFunction(fn.merge, ':o>')); + staticFrame.bind('reverse', defineFunction(fn.reverse, '')); + staticFrame.bind('each', defineFunction(fn.each, '')); + staticFrame.bind('error', defineFunction(fn.error, '')); + staticFrame.bind('assert', defineFunction(fn.assert, '')); + staticFrame.bind('type', defineFunction(fn.type, '')); + staticFrame.bind('sort', defineFunction(fn.sort, '')); + staticFrame.bind('shuffle', defineFunction(fn.shuffle, '')); + staticFrame.bind('distinct', defineFunction(fn.distinct, '')); + staticFrame.bind('base64encode', defineFunction(fn.base64encode, '')); + staticFrame.bind('base64decode', defineFunction(fn.base64decode, '')); + staticFrame.bind('encodeUrlComponent', defineFunction(fn.encodeUrlComponent, '')); + staticFrame.bind('encodeUrl', defineFunction(fn.encodeUrl, '')); + staticFrame.bind('decodeUrlComponent', defineFunction(fn.decodeUrlComponent, '')); + staticFrame.bind('decodeUrl', defineFunction(fn.decodeUrl, '')); + staticFrame.bind('eval', defineFunction(functionEval, '')); + staticFrame.bind('toMillis', defineFunction(datetime.toMillis, '')); + staticFrame.bind('fromMillis', defineFunction(datetime.fromMillis, '')); + staticFrame.bind('clone', defineFunction(functionClone, '<(oa)-:o>')); + + /** + * Error codes + * + * Sxxxx - Static errors (compile time) + * Txxxx - Type errors + * Dxxxx - Dynamic errors (evaluate time) + * 01xx - tokenizer + * 02xx - parser + * 03xx - regex parser + * 04xx - function signature parser/evaluator + * 10xx - evaluator + * 20xx - operators + * 3xxx - functions (blocks of 10 for each function) + */ + var errorCodes = { + "S0101": "String literal must be terminated by a matching quote", + "S0102": "Number out of range: {{token}}", + "S0103": "Unsupported escape sequence: \\{{token}}", + "S0104": "The escape sequence \\u must be followed by 4 hex digits", + "S0105": "Quoted property name must be terminated with a backquote (`)", + "S0106": "Comment has no closing tag", + "S0201": "Syntax error: {{token}}", + "S0202": "Expected {{value}}, got {{token}}", + "S0203": "Expected {{value}} before end of expression", + "S0204": "Unknown operator: {{token}}", + "S0205": "Unexpected token: {{token}}", + "S0206": "Unknown expression type: {{token}}", + "S0207": "Unexpected end of expression", + "S0208": "Parameter {{value}} of function definition must be a variable name (start with $)", + "S0209": "A predicate cannot follow a grouping expression in a step", + "S0210": "Each step can only have one grouping expression", + "S0211": "The symbol {{token}} cannot be used as a unary operator", + "S0212": "The left side of := must be a variable name (start with $)", + "S0213": "The literal value {{value}} cannot be used as a step within a path expression", + "S0214": "The right side of {{token}} must be a variable name (start with $)", + "S0215": "A context variable binding must precede any predicates on a step", + "S0216": "A context variable binding must precede the 'order-by' clause on a step", + "S0217": "The object representing the 'parent' cannot be derived from this expression", + "S0301": "Empty regular expressions are not allowed", + "S0302": "No terminating / in regular expression", + "S0402": "Choice groups containing parameterized types are not supported", + "S0401": "Type parameters can only be applied to functions and arrays", + "S0500": "Attempted to evaluate an expression containing syntax error(s)", + "T0410": "Argument {{index}} of function {{token}} does not match function signature", + "T0411": "Context value is not a compatible type with argument {{index}} of function {{token}}", + "T0412": "Argument {{index}} of function {{token}} must be an array of {{type}}", + "D1001": "Number out of range: {{value}}", + "D1002": "Cannot negate a non-numeric value: {{value}}", + "T1003": "Key in object structure must evaluate to a string; got: {{value}}", + "D1004": "Regular expression matches zero length string", + "T1005": "Attempted to invoke a non-function. Did you mean ${{{token}}}?", + "T1006": "Attempted to invoke a non-function", + "T1007": "Attempted to partially apply a non-function. Did you mean ${{{token}}}?", + "T1008": "Attempted to partially apply a non-function", + "D1009": "Multiple key definitions evaluate to same key: {{value}}", + "T1010": "The matcher function argument passed to function {{token}} does not return the correct object structure", + "T2001": "The left side of the {{token}} operator must evaluate to a number", + "T2002": "The right side of the {{token}} operator must evaluate to a number", + "T2003": "The left side of the range operator (..) must evaluate to an integer", + "T2004": "The right side of the range operator (..) must evaluate to an integer", + "D2005": "The left side of := must be a variable name (start with $)", // defunct - replaced by S0212 parser error + "T2006": "The right side of the function application operator ~> must be a function", + "T2007": "Type mismatch when comparing values {{value}} and {{value2}} in order-by clause", + "T2008": "The expressions within an order-by clause must evaluate to numeric or string values", + "T2009": "The values {{value}} and {{value2}} either side of operator {{token}} must be of the same data type", + "T2010": "The expressions either side of operator {{token}} must evaluate to numeric or string values", + "T2011": "The insert/update clause of the transform expression must evaluate to an object: {{value}}", + "T2012": "The delete clause of the transform expression must evaluate to a string or array of strings: {{value}}", + "T2013": "The transform expression clones the input object using the $clone() function. This has been overridden in the current scope by a non-function.", + "D2014": "The size of the sequence allocated by the range operator (..) must not exceed 1e6. Attempted to allocate {{value}}.", + "D3001": "Attempting to invoke string function on Infinity or NaN", + "D3010": "Second argument of replace function cannot be an empty string", + "D3011": "Fourth argument of replace function must evaluate to a positive number", + "D3012": "Attempted to replace a matched string with a non-string value", + "D3020": "Third argument of split function must evaluate to a positive number", + "D3030": "Unable to cast value to a number: {{value}}", + "D3040": "Third argument of match function must evaluate to a positive number", + "D3050": "The second argument of reduce function must be a function with at least two arguments", + "D3060": "The sqrt function cannot be applied to a negative number: {{value}}", + "D3061": "The power function has resulted in a value that cannot be represented as a JSON number: base={{value}}, exponent={{exp}}", + "D3070": "The single argument form of the sort function can only be applied to an array of strings or an array of numbers. Use the second argument to specify a comparison function", + "D3080": "The picture string must only contain a maximum of two sub-pictures", + "D3081": "The sub-picture must not contain more than one instance of the 'decimal-separator' character", + "D3082": "The sub-picture must not contain more than one instance of the 'percent' character", + "D3083": "The sub-picture must not contain more than one instance of the 'per-mille' character", + "D3084": "The sub-picture must not contain both a 'percent' and a 'per-mille' character", + "D3085": "The mantissa part of a sub-picture must contain at least one character that is either an 'optional digit character' or a member of the 'decimal digit family'", + "D3086": "The sub-picture must not contain a passive character that is preceded by an active character and that is followed by another active character", + "D3087": "The sub-picture must not contain a 'grouping-separator' character that appears adjacent to a 'decimal-separator' character", + "D3088": "The sub-picture must not contain a 'grouping-separator' at the end of the integer part", + "D3089": "The sub-picture must not contain two adjacent instances of the 'grouping-separator' character", + "D3090": "The integer part of the sub-picture must not contain a member of the 'decimal digit family' that is followed by an instance of the 'optional digit character'", + "D3091": "The fractional part of the sub-picture must not contain an instance of the 'optional digit character' that is followed by a member of the 'decimal digit family'", + "D3092": "A sub-picture that contains a 'percent' or 'per-mille' character must not contain a character treated as an 'exponent-separator'", + "D3093": "The exponent part of the sub-picture must comprise only of one or more characters that are members of the 'decimal digit family'", + "D3100": "The radix of the formatBase function must be between 2 and 36. It was given {{value}}", + "D3110": "The argument of the toMillis function must be an ISO 8601 formatted timestamp. Given {{value}}", + "D3120": "Syntax error in expression passed to function eval: {{value}}", + "D3121": "Dynamic error evaluating the expression passed to function eval: {{value}}", + "D3130": "Formatting or parsing an integer as a sequence starting with {{value}} is not supported by this implementation", + "D3131": "In a decimal digit pattern, all digits must be from the same decimal group", + "D3132": "Unknown component specifier {{value}} in date/time picture string", + "D3133": "The 'name' modifier can only be applied to months and days in the date/time picture string, not {{value}}", + "D3134": "The timezone integer format specifier cannot have more than four digits", + "D3135": "No matching closing bracket ']' in date/time picture string", + "D3136": "The date/time picture string is missing specifiers required to parse the timestamp", + "D3137": "{{{message}}}", + "D3138": "The $single() function expected exactly 1 matching result. Instead it matched more.", + "D3139": "The $single() function expected exactly 1 matching result. Instead it matched 0.", + "D3140": "Malformed URL passed to ${{{functionName}}}(): {{value}}", + "D3141": "{{{message}}}" + }; + + /** + * lookup a message template from the catalog and substitute the inserts. + * Populates `err.message` with the substituted message. Leaves `err.message` + * untouched if code lookup fails. + * @param {string} err - error code to lookup + * @returns {undefined} - `err` is modified in place + */ + function populateMessage(err) { + var template = errorCodes[err.code]; + if(typeof template !== 'undefined') { + // if there are any handlebars, replace them with the field references + // triple braces - replace with value + // double braces - replace with json stringified value + var message = template.replace(/\{\{\{([^}]+)}}}/g, function() { + return err[arguments[1]]; + }); + message = message.replace(/\{\{([^}]+)}}/g, function() { + return JSON.stringify(err[arguments[1]]); + }); + err.message = message; + } + // Otherwise retain the original `err.message` + } + + /** + * JSONata + * @param {Object} expr - JSONata expression + * @param {boolean} options - recover: attempt to recover on parse error + * @returns {{evaluate: evaluate, assign: assign}} Evaluated expression + */ + function jsonata(expr, options) { + var ast; + var errors; + try { + ast = parser(expr, options && options.recover); + errors = ast.errors; + delete ast.errors; + } catch(err) { + // insert error message into structure + populateMessage(err); // possible side-effects on `err` + throw err; + } + var environment = createFrame(staticFrame); + + var timestamp = new Date(); // will be overridden on each call to evalute() + environment.bind('now', defineFunction(function(picture, timezone) { + return datetime.fromMillis(timestamp.getTime(), picture, timezone); + }, '')); + environment.bind('millis', defineFunction(function() { + return timestamp.getTime(); + }, '<:n>')); + + return { + evaluate: function (input, bindings, callback) { + // throw if the expression compiled with syntax errors + if(typeof errors !== 'undefined') { + var err = { + code: 'S0500', + position: 0 + }; + populateMessage(err); // possible side-effects on `err` + throw err; + } + + if (typeof bindings !== 'undefined') { + var exec_env; + // the variable bindings have been passed in - create a frame to hold these + exec_env = createFrame(environment); + for (var v in bindings) { + exec_env.bind(v, bindings[v]); + } + } else { + exec_env = environment; + } + // put the input document into the environment as the root object + exec_env.bind('$', input); + + // capture the timestamp and put it in the execution environment + // the $now() and $millis() functions will return this value - whenever it is called + timestamp = new Date(); + exec_env.timestamp = timestamp; + + // if the input is a JSON array, then wrap it in a singleton sequence so it gets treated as a single input + if(Array.isArray(input) && !isSequence(input)) { + input = createSequence(input); + input.outerWrapper = true; + } + + var result, it; + // if a callback function is supplied, then drive the generator in a promise chain + if(typeof callback === 'function') { + exec_env.async = true; + var catchHandler = function (err) { + populateMessage(err); // possible side-effects on `err` + callback(err, null); + }; + var thenHandler = function (response) { + result = it.next(response); + if (result.done) { + callback(null, result.value); + } else { + result.value.then(thenHandler).catch(catchHandler); + } + }; + it = evaluate(ast, input, exec_env); + result = it.next(); + result.value.then(thenHandler).catch(catchHandler); + } else { + // no callback function - drive the generator to completion synchronously + try { + it = evaluate(ast, input, exec_env); + result = it.next(); + while (!result.done) { + result = it.next(result.value); + } + return result.value; + } catch (err) { + // insert error message into structure + populateMessage(err); // possible side-effects on `err` + throw err; + } + } + }, + assign: function (name, value) { + environment.bind(name, value); + }, + registerFunction: function(name, implementation, signature) { + var func = defineFunction(implementation, signature); + environment.bind(name, func); + }, + ast: function() { + return ast; + }, + errors: function() { + return errors; + } + }; + } + + jsonata.parser = parser; // TODO remove this in a future release - use ast() instead + + return jsonata; + +})(); + +module.exports = jsonata; + +},{"./datetime":1,"./functions":2,"./parser":4,"./signature":5,"./utils":6}],4:[function(require,module,exports){ +/** + * © Copyright IBM Corp. 2016, 2018 All Rights Reserved + * Project name: JSONata + * This project is licensed under the MIT License, see LICENSE + */ + +var parseSignature = require('./signature'); + +const parser = (() => { + 'use strict'; + + var operators = { + '.': 75, + '[': 80, + ']': 0, + '{': 70, + '}': 0, + '(': 80, + ')': 0, + ',': 0, + '@': 80, + '#': 80, + ';': 80, + ':': 80, + '?': 20, + '+': 50, + '-': 50, + '*': 60, + '/': 60, + '%': 60, + '|': 20, + '=': 40, + '<': 40, + '>': 40, + '^': 40, + '**': 60, + '..': 20, + ':=': 10, + '!=': 40, + '<=': 40, + '>=': 40, + '~>': 40, + 'and': 30, + 'or': 25, + 'in': 40, + '&': 50, + '!': 0, // not an operator, but needed as a stop character for name tokens + '~': 0 // not an operator, but needed as a stop character for name tokens + }; + + var escapes = { // JSON string escape sequences - see json.org + '"': '"', + '\\': '\\', + '/': '/', + 'b': '\b', + 'f': '\f', + 'n': '\n', + 'r': '\r', + 't': '\t' + }; + + // Tokenizer (lexer) - invoked by the parser to return one token at a time + var tokenizer = function (path) { + var position = 0; + var length = path.length; + + var create = function (type, value) { + var obj = {type: type, value: value, position: position}; + return obj; + }; + + var scanRegex = function () { + // the prefix '/' will have been previously scanned. Find the end of the regex. + // search for closing '/' ignoring any that are escaped, or within brackets + var start = position; + var depth = 0; + var pattern; + var flags; + while (position < length) { + var currentChar = path.charAt(position); + if (currentChar === '/' && path.charAt(position - 1) !== '\\' && depth === 0) { + // end of regex found + pattern = path.substring(start, position); + if (pattern === '') { + throw { + code: "S0301", + stack: (new Error()).stack, + position: position + }; + } + position++; + currentChar = path.charAt(position); + // flags + start = position; + while (currentChar === 'i' || currentChar === 'm') { + position++; + currentChar = path.charAt(position); + } + flags = path.substring(start, position) + 'g'; + return new RegExp(pattern, flags); + } + if ((currentChar === '(' || currentChar === '[' || currentChar === '{') && path.charAt(position - 1) !== '\\') { + depth++; + } + if ((currentChar === ')' || currentChar === ']' || currentChar === '}') && path.charAt(position - 1) !== '\\') { + depth--; + } + + position++; + } + throw { + code: "S0302", + stack: (new Error()).stack, + position: position + }; + }; + + var next = function (prefix) { + if (position >= length) return null; + var currentChar = path.charAt(position); + // skip whitespace + while (position < length && ' \t\n\r\v'.indexOf(currentChar) > -1) { + position++; + currentChar = path.charAt(position); + } + // skip comments + if (currentChar === '/' && path.charAt(position + 1) === '*') { + var commentStart = position; + position += 2; + currentChar = path.charAt(position); + while (!(currentChar === '*' && path.charAt(position + 1) === '/')) { + currentChar = path.charAt(++position); + if (position >= length) { + // no closing tag + throw { + code: "S0106", + stack: (new Error()).stack, + position: commentStart + }; + } + } + position += 2; + currentChar = path.charAt(position); + return next(prefix); // need this to swallow any following whitespace + } + // test for regex + if (prefix !== true && currentChar === '/') { + position++; + return create('regex', scanRegex()); + } + // handle double-char operators + if (currentChar === '.' && path.charAt(position + 1) === '.') { + // double-dot .. range operator + position += 2; + return create('operator', '..'); + } + if (currentChar === ':' && path.charAt(position + 1) === '=') { + // := assignment + position += 2; + return create('operator', ':='); + } + if (currentChar === '!' && path.charAt(position + 1) === '=') { + // != + position += 2; + return create('operator', '!='); + } + if (currentChar === '>' && path.charAt(position + 1) === '=') { + // >= + position += 2; + return create('operator', '>='); + } + if (currentChar === '<' && path.charAt(position + 1) === '=') { + // <= + position += 2; + return create('operator', '<='); + } + if (currentChar === '*' && path.charAt(position + 1) === '*') { + // ** descendant wildcard + position += 2; + return create('operator', '**'); + } + if (currentChar === '~' && path.charAt(position + 1) === '>') { + // ~> chain function + position += 2; + return create('operator', '~>'); + } + // test for single char operators + if (Object.prototype.hasOwnProperty.call(operators, currentChar)) { + position++; + return create('operator', currentChar); + } + // test for string literals + if (currentChar === '"' || currentChar === "'") { + var quoteType = currentChar; + // double quoted string literal - find end of string + position++; + var qstr = ""; + while (position < length) { + currentChar = path.charAt(position); + if (currentChar === '\\') { // escape sequence + position++; + currentChar = path.charAt(position); + if (Object.prototype.hasOwnProperty.call(escapes, currentChar)) { + qstr += escapes[currentChar]; + } else if (currentChar === 'u') { + // \u should be followed by 4 hex digits + var octets = path.substr(position + 1, 4); + if (/^[0-9a-fA-F]+$/.test(octets)) { + var codepoint = parseInt(octets, 16); + qstr += String.fromCharCode(codepoint); + position += 4; + } else { + throw { + code: "S0104", + stack: (new Error()).stack, + position: position + }; + } + } else { + // illegal escape sequence + throw { + code: "S0103", + stack: (new Error()).stack, + position: position, + token: currentChar + }; + + } + } else if (currentChar === quoteType) { + position++; + return create('string', qstr); + } else { + qstr += currentChar; + } + position++; + } + throw { + code: "S0101", + stack: (new Error()).stack, + position: position + }; + } + // test for numbers + var numregex = /^-?(0|([1-9][0-9]*))(\.[0-9]+)?([Ee][-+]?[0-9]+)?/; + var match = numregex.exec(path.substring(position)); + if (match !== null) { + var num = parseFloat(match[0]); + if (!isNaN(num) && isFinite(num)) { + position += match[0].length; + return create('number', num); + } else { + throw { + code: "S0102", + stack: (new Error()).stack, + position: position, + token: match[0] + }; + } + } + // test for quoted names (backticks) + var name; + if (currentChar === '`') { + // scan for closing quote + position++; + var end = path.indexOf('`', position); + if (end !== -1) { + name = path.substring(position, end); + position = end + 1; + return create('name', name); + } + position = length; + throw { + code: "S0105", + stack: (new Error()).stack, + position: position + }; + } + // test for names + var i = position; + var ch; + for (; ;) { + ch = path.charAt(i); + if (i === length || ' \t\n\r\v'.indexOf(ch) > -1 || Object.prototype.hasOwnProperty.call(operators, ch)) { + if (path.charAt(position) === '$') { + // variable reference + name = path.substring(position + 1, i); + position = i; + return create('variable', name); + } else { + name = path.substring(position, i); + position = i; + switch (name) { + case 'or': + case 'in': + case 'and': + return create('operator', name); + case 'true': + return create('value', true); + case 'false': + return create('value', false); + case 'null': + return create('value', null); + default: + if (position === length && name === '') { + // whitespace at end of input + return null; + } + return create('name', name); + } + } + } else { + i++; + } + } + }; + + return next; + }; + + // This parser implements the 'Top down operator precedence' algorithm developed by Vaughan R Pratt; http://dl.acm.org/citation.cfm?id=512931. + // and builds on the Javascript framework described by Douglas Crockford at http://javascript.crockford.com/tdop/tdop.html + // and in 'Beautiful Code', edited by Andy Oram and Greg Wilson, Copyright 2007 O'Reilly Media, Inc. 798-0-596-51004-6 + + var parser = function (source, recover) { + var node; + var lexer; + + var symbol_table = {}; + var errors = []; + + var remainingTokens = function () { + var remaining = []; + if (node.id !== '(end)') { + remaining.push({type: node.type, value: node.value, position: node.position}); + } + var nxt = lexer(); + while (nxt !== null) { + remaining.push(nxt); + nxt = lexer(); + } + return remaining; + }; + + var base_symbol = { + nud: function () { + // error - symbol has been invoked as a unary operator + var err = { + code: 'S0211', + token: this.value, + position: this.position + }; + + if (recover) { + err.remaining = remainingTokens(); + err.type = 'error'; + errors.push(err); + return err; + } else { + err.stack = (new Error()).stack; + throw err; + } + } + }; + + var symbol = function (id, bp) { + var s = symbol_table[id]; + bp = bp || 0; + if (s) { + if (bp >= s.lbp) { + s.lbp = bp; + } + } else { + s = Object.create(base_symbol); + s.id = s.value = id; + s.lbp = bp; + symbol_table[id] = s; + } + return s; + }; + + var handleError = function (err) { + if (recover) { + // tokenize the rest of the buffer and add it to an error token + err.remaining = remainingTokens(); + errors.push(err); + var symbol = symbol_table["(error)"]; + node = Object.create(symbol); + node.error = err; + node.type = "(error)"; + return node; + } else { + err.stack = (new Error()).stack; + throw err; + } + }; + + var advance = function (id, infix) { + if (id && node.id !== id) { + var code; + if (node.id === '(end)') { + // unexpected end of buffer + code = "S0203"; + } else { + code = "S0202"; + } + var err = { + code: code, + position: node.position, + token: node.value, + value: id + }; + return handleError(err); + } + var next_token = lexer(infix); + if (next_token === null) { + node = symbol_table["(end)"]; + node.position = source.length; + return node; + } + var value = next_token.value; + var type = next_token.type; + var symbol; + switch (type) { + case 'name': + case 'variable': + symbol = symbol_table["(name)"]; + break; + case 'operator': + symbol = symbol_table[value]; + if (!symbol) { + return handleError({ + code: "S0204", + stack: (new Error()).stack, + position: next_token.position, + token: value + }); + } + break; + case 'string': + case 'number': + case 'value': + symbol = symbol_table["(literal)"]; + break; + case 'regex': + type = "regex"; + symbol = symbol_table["(regex)"]; + break; + /* istanbul ignore next */ + default: + return handleError({ + code: "S0205", + stack: (new Error()).stack, + position: next_token.position, + token: value + }); + } + + node = Object.create(symbol); + node.value = value; + node.type = type; + node.position = next_token.position; + return node; + }; + + // Pratt's algorithm + var expression = function (rbp) { + var left; + var t = node; + advance(null, true); + left = t.nud(); + while (rbp < node.lbp) { + t = node; + advance(); + left = t.led(left); + } + return left; + }; + + var terminal = function (id) { + var s = symbol(id, 0); + s.nud = function () { + return this; + }; + }; + + // match infix operators + // + // left associative + var infix = function (id, bp, led) { + var bindingPower = bp || operators[id]; + var s = symbol(id, bindingPower); + s.led = led || function (left) { + this.lhs = left; + this.rhs = expression(bindingPower); + this.type = "binary"; + return this; + }; + return s; + }; + + // match infix operators + // + // right associative + var infixr = function (id, bp, led) { + var s = symbol(id, bp); + s.led = led; + return s; + }; + + // match prefix operators + // + var prefix = function (id, nud) { + var s = symbol(id); + s.nud = nud || function () { + this.expression = expression(70); + this.type = "unary"; + return this; + }; + return s; + }; + + terminal("(end)"); + terminal("(name)"); + terminal("(literal)"); + terminal("(regex)"); + symbol(":"); + symbol(";"); + symbol(","); + symbol(")"); + symbol("]"); + symbol("}"); + symbol(".."); // range operator + infix("."); // map operator + infix("+"); // numeric addition + infix("-"); // numeric subtraction + infix("*"); // numeric multiplication + infix("/"); // numeric division + infix("%"); // numeric modulus + infix("="); // equality + infix("<"); // less than + infix(">"); // greater than + infix("!="); // not equal to + infix("<="); // less than or equal + infix(">="); // greater than or equal + infix("&"); // string concatenation + infix("and"); // Boolean AND + infix("or"); // Boolean OR + infix("in"); // is member of array + terminal("and"); // the 'keywords' can also be used as terminals (field names) + terminal("or"); // + terminal("in"); // + prefix("-"); // unary numeric negation + infix("~>"); // function application + + infixr("(error)", 10, function (left) { + this.lhs = left; + + this.error = node.error; + this.remaining = remainingTokens(); + this.type = 'error'; + return this; + }); + + // field wildcard (single level) + prefix('*', function () { + this.type = "wildcard"; + return this; + }); + + // descendant wildcard (multi-level) + prefix('**', function () { + this.type = "descendant"; + return this; + }); + + // parent operator + prefix('%', function () { + this.type = "parent"; + return this; + }); + + // function invocation + infix("(", operators['('], function (left) { + // left is is what we are trying to invoke + this.procedure = left; + this.type = 'function'; + this.arguments = []; + if (node.id !== ')') { + for (; ;) { + if (node.type === 'operator' && node.id === '?') { + // partial function application + this.type = 'partial'; + this.arguments.push(node); + advance('?'); + } else { + this.arguments.push(expression(0)); + } + if (node.id !== ',') break; + advance(','); + } + } + advance(")", true); + // if the name of the function is 'function' or λ, then this is function definition (lambda function) + if (left.type === 'name' && (left.value === 'function' || left.value === '\u03BB')) { + // all of the args must be VARIABLE tokens + this.arguments.forEach(function (arg, index) { + if (arg.type !== 'variable') { + return handleError({ + code: "S0208", + stack: (new Error()).stack, + position: arg.position, + token: arg.value, + value: index + 1 + }); + } + }); + this.type = 'lambda'; + // is the next token a '<' - if so, parse the function signature + if (node.id === '<') { + var sigPos = node.position; + var depth = 1; + var sig = '<'; + while (depth > 0 && node.id !== '{' && node.id !== '(end)') { + var tok = advance(); + if (tok.id === '>') { + depth--; + } else if (tok.id === '<') { + depth++; + } + sig += tok.value; + } + advance('>'); + try { + this.signature = parseSignature(sig); + } catch (err) { + // insert the position into this error + err.position = sigPos + err.offset; + return handleError(err); + } + } + // parse the function body + advance('{'); + this.body = expression(0); + advance('}'); + } + return this; + }); + + // parenthesis - block expression + prefix("(", function () { + var expressions = []; + while (node.id !== ")") { + expressions.push(expression(0)); + if (node.id !== ";") { + break; + } + advance(";"); + } + advance(")", true); + this.type = 'block'; + this.expressions = expressions; + return this; + }); + + // array constructor + prefix("[", function () { + var a = []; + if (node.id !== "]") { + for (; ;) { + var item = expression(0); + if (node.id === "..") { + // range operator + var range = {type: "binary", value: "..", position: node.position, lhs: item}; + advance(".."); + range.rhs = expression(0); + item = range; + } + a.push(item); + if (node.id !== ",") { + break; + } + advance(","); + } + } + advance("]", true); + this.expressions = a; + this.type = "unary"; + return this; + }); + + // filter - predicate or array index + infix("[", operators['['], function (left) { + if (node.id === "]") { + // empty predicate means maintain singleton arrays in the output + var step = left; + while (step && step.type === 'binary' && step.value === '[') { + step = step.lhs; + } + step.keepArray = true; + advance("]"); + return left; + } else { + this.lhs = left; + this.rhs = expression(operators[']']); + this.type = 'binary'; + advance("]", true); + return this; + } + }); + + // order-by + infix("^", operators['^'], function (left) { + advance("("); + var terms = []; + for (; ;) { + var term = { + descending: false + }; + if (node.id === "<") { + // ascending sort + advance("<"); + } else if (node.id === ">") { + // descending sort + term.descending = true; + advance(">"); + } else { + //unspecified - default to ascending + } + term.expression = expression(0); + terms.push(term); + if (node.id !== ",") { + break; + } + advance(","); + } + advance(")"); + this.lhs = left; + this.rhs = terms; + this.type = 'binary'; + return this; + }); + + var objectParser = function (left) { + var a = []; + if (node.id !== "}") { + for (; ;) { + var n = expression(0); + advance(":"); + var v = expression(0); + a.push([n, v]); // holds an array of name/value expression pairs + if (node.id !== ",") { + break; + } + advance(","); + } + } + advance("}", true); + if (typeof left === 'undefined') { + // NUD - unary prefix form + this.lhs = a; + this.type = "unary"; + } else { + // LED - binary infix form + this.lhs = left; + this.rhs = a; + this.type = 'binary'; + } + return this; + }; + + // object constructor + prefix("{", objectParser); + + // object grouping + infix("{", operators['{'], objectParser); + + // bind variable + infixr(":=", operators[':='], function (left) { + if (left.type !== 'variable') { + return handleError({ + code: "S0212", + stack: (new Error()).stack, + position: left.position, + token: left.value + }); + } + this.lhs = left; + this.rhs = expression(operators[':='] - 1); // subtract 1 from bindingPower for right associative operators + this.type = "binary"; + return this; + }); + + // focus variable bind + infix("@", operators['@'], function (left) { + this.lhs = left; + this.rhs = expression(operators['@']); + if(this.rhs.type !== 'variable') { + return handleError({ + code: "S0214", + stack: (new Error()).stack, + position: this.rhs.position, + token: "@" + }); + } + this.type = "binary"; + return this; + }); + + // index (position) variable bind + infix("#", operators['#'], function (left) { + this.lhs = left; + this.rhs = expression(operators['#']); + if(this.rhs.type !== 'variable') { + return handleError({ + code: "S0214", + stack: (new Error()).stack, + position: this.rhs.position, + token: "#" + }); + } + this.type = "binary"; + return this; + }); + + // if/then/else ternary operator ?: + infix("?", operators['?'], function (left) { + this.type = 'condition'; + this.condition = left; + this.then = expression(0); + if (node.id === ':') { + // else condition + advance(":"); + this.else = expression(0); + } + return this; + }); + + // object transformer + prefix("|", function () { + this.type = 'transform'; + this.pattern = expression(0); + advance('|'); + this.update = expression(0); + if (node.id === ',') { + advance(','); + this.delete = expression(0); + } + advance('|'); + return this; + }); + + // tail call optimization + // this is invoked by the post parser to analyse lambda functions to see + // if they make a tail call. If so, it is replaced by a thunk which will + // be invoked by the trampoline loop during function application. + // This enables tail-recursive functions to be written without growing the stack + var tailCallOptimize = function (expr) { + var result; + if (expr.type === 'function' && !expr.predicate) { + var thunk = {type: 'lambda', thunk: true, arguments: [], position: expr.position}; + thunk.body = expr; + result = thunk; + } else if (expr.type === 'condition') { + // analyse both branches + expr.then = tailCallOptimize(expr.then); + if (typeof expr.else !== 'undefined') { + expr.else = tailCallOptimize(expr.else); + } + result = expr; + } else if (expr.type === 'block') { + // only the last expression in the block + var length = expr.expressions.length; + if (length > 0) { + expr.expressions[length - 1] = tailCallOptimize(expr.expressions[length - 1]); + } + result = expr; + } else { + result = expr; + } + return result; + }; + + var ancestorLabel = 0; + var ancestorIndex = 0; + var ancestry = []; + + var seekParent = function (node, slot) { + switch (node.type) { + case 'name': + case 'wildcard': + slot.level--; + if(slot.level === 0) { + if (typeof node.ancestor === 'undefined') { + node.ancestor = slot; + } else { + // reuse the existing label + ancestry[slot.index].slot.label = node.ancestor.label; + node.ancestor = slot; + } + node.tuple = true; + } + break; + case 'parent': + slot.level++; + break; + case 'block': + // look in last expression in the block + if(node.expressions.length > 0) { + node.tuple = true; + slot = seekParent(node.expressions[node.expressions.length - 1], slot); + } + break; + case 'path': + // last step in path + node.tuple = true; + var index = node.steps.length - 1; + slot = seekParent(node.steps[index--], slot); + while (slot.level > 0 && index >= 0) { + // check previous steps + slot = seekParent(node.steps[index--], slot); + } + break; + default: + // error - can't derive ancestor + throw { + code: "S0217", + token: node.type, + position: node.position + }; + } + return slot; + }; + + var pushAncestry = function(result, value) { + if(typeof value.seekingParent !== 'undefined' || value.type === 'parent') { + var slots = (typeof value.seekingParent !== 'undefined') ? value.seekingParent : []; + if (value.type === 'parent') { + slots.push(value.slot); + } + if(typeof result.seekingParent === 'undefined') { + result.seekingParent = slots; + } else { + Array.prototype.push.apply(result.seekingParent, slots); + } + } + }; + + var resolveAncestry = function(path) { + var index = path.steps.length - 1; + var laststep = path.steps[index]; + var slots = (typeof laststep.seekingParent !== 'undefined') ? laststep.seekingParent : []; + if (laststep.type === 'parent') { + slots.push(laststep.slot); + } + for(var is = 0; is < slots.length; is++) { + var slot = slots[is]; + index = path.steps.length - 2; + while (slot.level > 0) { + if (index < 0) { + if(typeof path.seekingParent === 'undefined') { + path.seekingParent = [slot]; + } else { + path.seekingParent.push(slot); + } + break; + } + // try previous step + var step = path.steps[index--]; + // multiple contiguous steps that bind the focus should be skipped + while(index >= 0 && step.focus && path.steps[index].focus) { + step = path.steps[index--]; + } + slot = seekParent(step, slot); + } + } + }; + + // post-parse stage + // the purpose of this is to add as much semantic value to the parse tree as possible + // in order to simplify the work of the evaluator. + // This includes flattening the parts of the AST representing location paths, + // converting them to arrays of steps which in turn may contain arrays of predicates. + // following this, nodes containing '.' and '[' should be eliminated from the AST. + var processAST = function (expr) { + var result; + switch (expr.type) { + case 'binary': + switch (expr.value) { + case '.': + var lstep = processAST(expr.lhs); + + if (lstep.type === 'path') { + result = lstep; + } else { + result = {type: 'path', steps: [lstep]}; + } + if(lstep.type === 'parent') { + result.seekingParent = [lstep.slot]; + } + var rest = processAST(expr.rhs); + if (rest.type === 'function' && + rest.procedure.type === 'path' && + rest.procedure.steps.length === 1 && + rest.procedure.steps[0].type === 'name' && + result.steps[result.steps.length - 1].type === 'function') { + // next function in chain of functions - will override a thenable + result.steps[result.steps.length - 1].nextFunction = rest.procedure.steps[0].value; + } + if (rest.type === 'path') { + Array.prototype.push.apply(result.steps, rest.steps); + } else { + if(typeof rest.predicate !== 'undefined') { + rest.stages = rest.predicate; + delete rest.predicate; + } + result.steps.push(rest); + } + // any steps within a path that are string literals, should be changed to 'name' + result.steps.filter(function (step) { + if (step.type === 'number' || step.type === 'value') { + // don't allow steps to be numbers or the values true/false/null + throw { + code: "S0213", + stack: (new Error()).stack, + position: step.position, + value: step.value + }; + } + return step.type === 'string'; + }).forEach(function (lit) { + lit.type = 'name'; + }); + // any step that signals keeping a singleton array, should be flagged on the path + if (result.steps.filter(function (step) { + return step.keepArray === true; + }).length > 0) { + result.keepSingletonArray = true; + } + // if first step is a path constructor, flag it for special handling + var firststep = result.steps[0]; + if (firststep.type === 'unary' && firststep.value === '[') { + firststep.consarray = true; + } + // if the last step is an array constructor, flag it so it doesn't flatten + var laststep = result.steps[result.steps.length - 1]; + if (laststep.type === 'unary' && laststep.value === '[') { + laststep.consarray = true; + } + resolveAncestry(result); + break; + case '[': + // predicated step + // LHS is a step or a predicated step + // RHS is the predicate expr + result = processAST(expr.lhs); + var step = result; + var type = 'predicate'; + if (result.type === 'path') { + step = result.steps[result.steps.length - 1]; + type = 'stages'; + } + if (typeof step.group !== 'undefined') { + throw { + code: "S0209", + stack: (new Error()).stack, + position: expr.position + }; + } + if (typeof step[type] === 'undefined') { + step[type] = []; + } + var predicate = processAST(expr.rhs); + if(typeof predicate.seekingParent !== 'undefined') { + predicate.seekingParent.forEach(slot => { + if(slot.level === 1) { + seekParent(step, slot); + } else { + slot.level--; + } + }); + pushAncestry(step, predicate); + } + step[type].push({type: 'filter', expr: predicate, position: expr.position}); + break; + case '{': + // group-by + // LHS is a step or a predicated step + // RHS is the object constructor expr + result = processAST(expr.lhs); + if (typeof result.group !== 'undefined') { + throw { + code: "S0210", + stack: (new Error()).stack, + position: expr.position + }; + } + // object constructor - process each pair + result.group = { + lhs: expr.rhs.map(function (pair) { + return [processAST(pair[0]), processAST(pair[1])]; + }), + position: expr.position + }; + break; + case '^': + // order-by + // LHS is the array to be ordered + // RHS defines the terms + result = processAST(expr.lhs); + if (result.type !== 'path') { + result = {type: 'path', steps: [result]}; + } + var sortStep = {type: 'sort', position: expr.position}; + sortStep.terms = expr.rhs.map(function (terms) { + var expression = processAST(terms.expression); + pushAncestry(sortStep, expression); + return { + descending: terms.descending, + expression: expression + }; + }); + result.steps.push(sortStep); + resolveAncestry(result); + break; + case ':=': + result = {type: 'bind', value: expr.value, position: expr.position}; + result.lhs = processAST(expr.lhs); + result.rhs = processAST(expr.rhs); + pushAncestry(result, result.rhs); + break; + case '@': + result = processAST(expr.lhs); + step = result; + if (result.type === 'path') { + step = result.steps[result.steps.length - 1]; + } + // throw error if there are any predicates defined at this point + // at this point the only type of stages can be predicates + if(typeof step.stages !== 'undefined' || typeof step.predicate !== 'undefined') { + throw { + code: "S0215", + stack: (new Error()).stack, + position: expr.position + }; + } + // also throw if this is applied after an 'order-by' clause + if(step.type === 'sort') { + throw { + code: "S0216", + stack: (new Error()).stack, + position: expr.position + }; + } + if(expr.keepArray) { + step.keepArray = true; + } + step.focus = expr.rhs.value; + step.tuple = true; + break; + case '#': + result = processAST(expr.lhs); + step = result; + if (result.type === 'path') { + step = result.steps[result.steps.length - 1]; + } else { + result = {type: 'path', steps: [result]}; + if (typeof step.predicate !== 'undefined') { + step.stages = step.predicate; + delete step.predicate; + } + } + if (typeof step.stages === 'undefined') { + step.index = expr.rhs.value; + } else { + step.stages.push({type: 'index', value: expr.rhs.value, position: expr.position}); + } + step.tuple = true; + break; + case '~>': + result = {type: 'apply', value: expr.value, position: expr.position}; + result.lhs = processAST(expr.lhs); + result.rhs = processAST(expr.rhs); + break; + default: + result = {type: expr.type, value: expr.value, position: expr.position}; + result.lhs = processAST(expr.lhs); + result.rhs = processAST(expr.rhs); + pushAncestry(result, result.lhs); + pushAncestry(result, result.rhs); + } + break; + case 'unary': + result = {type: expr.type, value: expr.value, position: expr.position}; + if (expr.value === '[') { + // array constructor - process each item + result.expressions = expr.expressions.map(function (item) { + var value = processAST(item); + pushAncestry(result, value); + return value; + }); + } else if (expr.value === '{') { + // object constructor - process each pair + result.lhs = expr.lhs.map(function (pair) { + var key = processAST(pair[0]); + pushAncestry(result, key); + var value = processAST(pair[1]); + pushAncestry(result, value); + return [key, value]; + }); + } else { + // all other unary expressions - just process the expression + result.expression = processAST(expr.expression); + // if unary minus on a number, then pre-process + if (expr.value === '-' && result.expression.type === 'number') { + result = result.expression; + result.value = -result.value; + } else { + pushAncestry(result, result.expression); + } + } + break; + case 'function': + case 'partial': + result = {type: expr.type, name: expr.name, value: expr.value, position: expr.position}; + result.arguments = expr.arguments.map(function (arg) { + var argAST = processAST(arg); + pushAncestry(result, argAST); + return argAST; + }); + result.procedure = processAST(expr.procedure); + break; + case 'lambda': + result = { + type: expr.type, + arguments: expr.arguments, + signature: expr.signature, + position: expr.position + }; + var body = processAST(expr.body); + result.body = tailCallOptimize(body); + break; + case 'condition': + result = {type: expr.type, position: expr.position}; + result.condition = processAST(expr.condition); + pushAncestry(result, result.condition); + result.then = processAST(expr.then); + pushAncestry(result, result.then); + if (typeof expr.else !== 'undefined') { + result.else = processAST(expr.else); + pushAncestry(result, result.else); + } + break; + case 'transform': + result = {type: expr.type, position: expr.position}; + result.pattern = processAST(expr.pattern); + result.update = processAST(expr.update); + if (typeof expr.delete !== 'undefined') { + result.delete = processAST(expr.delete); + } + break; + case 'block': + result = {type: expr.type, position: expr.position}; + // array of expressions - process each one + result.expressions = expr.expressions.map(function (item) { + var part = processAST(item); + pushAncestry(result, part); + if (part.consarray || (part.type === 'path' && part.steps[0].consarray)) { + result.consarray = true; + } + return part; + }); + // TODO scan the array of expressions to see if any of them assign variables + // if so, need to mark the block as one that needs to create a new frame + break; + case 'name': + result = {type: 'path', steps: [expr]}; + if (expr.keepArray) { + result.keepSingletonArray = true; + } + break; + case 'parent': + result = {type: 'parent', slot: { label: '!' + ancestorLabel++, level: 1, index: ancestorIndex++ } }; + ancestry.push(result); + break; + case 'string': + case 'number': + case 'value': + case 'wildcard': + case 'descendant': + case 'variable': + case 'regex': + result = expr; + break; + case 'operator': + // the tokens 'and' and 'or' might have been used as a name rather than an operator + if (expr.value === 'and' || expr.value === 'or' || expr.value === 'in') { + expr.type = 'name'; + result = processAST(expr); + } else /* istanbul ignore else */ if (expr.value === '?') { + // partial application + result = expr; + } else { + throw { + code: "S0201", + stack: (new Error()).stack, + position: expr.position, + token: expr.value + }; + } + break; + case 'error': + result = expr; + if (expr.lhs) { + result = processAST(expr.lhs); + } + break; + default: + var code = "S0206"; + /* istanbul ignore else */ + if (expr.id === '(end)') { + code = "S0207"; + } + var err = { + code: code, + position: expr.position, + token: expr.value + }; + if (recover) { + errors.push(err); + return {type: 'error', error: err}; + } else { + err.stack = (new Error()).stack; + throw err; + } + } + if (expr.keepArray) { + result.keepArray = true; + } + return result; + }; + + // now invoke the tokenizer and the parser and return the syntax tree + lexer = tokenizer(source); + advance(); + // parse the tokens + var expr = expression(0); + if (node.id !== '(end)') { + var err = { + code: "S0201", + position: node.position, + token: node.value + }; + handleError(err); + } + expr = processAST(expr); + + if(expr.type === 'parent' || typeof expr.seekingParent !== 'undefined') { + // error - trying to derive ancestor at top level + throw { + code: "S0217", + token: expr.type, + position: expr.position + }; + } + + if (errors.length > 0) { + expr.errors = errors; + } + + return expr; + }; + + return parser; +})(); + +module.exports = parser; + +},{"./signature":5}],5:[function(require,module,exports){ +/** + * © Copyright IBM Corp. 2016, 2018 All Rights Reserved + * Project name: JSONata + * This project is licensed under the MIT License, see LICENSE + */ + +var utils = require('./utils'); + +const signature = (() => { + 'use strict'; + + // A mapping between the function signature symbols and the full plural of the type + // Expected to be used in error messages + var arraySignatureMapping = { + "a": "arrays", + "b": "booleans", + "f": "functions", + "n": "numbers", + "o": "objects", + "s": "strings" + }; + + /** + * Parses a function signature definition and returns a validation function + * @param {string} signature - the signature between the + * @returns {Function} validation function + */ + function parseSignature(signature) { + // create a Regex that represents this signature and return a function that when invoked, + // returns the validated (possibly fixed-up) arguments, or throws a validation error + // step through the signature, one symbol at a time + var position = 1; + var params = []; + var param = {}; + var prevParam = param; + while (position < signature.length) { + var symbol = signature.charAt(position); + if (symbol === ':') { + // TODO figure out what to do with the return type + // ignore it for now + break; + } + + var next = function () { + params.push(param); + prevParam = param; + param = {}; + }; + + var findClosingBracket = function (str, start, openSymbol, closeSymbol) { + // returns the position of the closing symbol (e.g. bracket) in a string + // that balances the opening symbol at position start + var depth = 1; + var position = start; + while (position < str.length) { + position++; + symbol = str.charAt(position); + if (symbol === closeSymbol) { + depth--; + if (depth === 0) { + // we're done + break; // out of while loop + } + } else if (symbol === openSymbol) { + depth++; + } + } + return position; + }; + + switch (symbol) { + case 's': // string + case 'n': // number + case 'b': // boolean + case 'l': // not so sure about expecting null? + case 'o': // object + param.regex = '[' + symbol + 'm]'; + param.type = symbol; + next(); + break; + case 'a': // array + // normally treat any value as singleton array + param.regex = '[asnblfom]'; + param.type = symbol; + param.array = true; + next(); + break; + case 'f': // function + param.regex = 'f'; + param.type = symbol; + next(); + break; + case 'j': // any JSON type + param.regex = '[asnblom]'; + param.type = symbol; + next(); + break; + case 'x': // any type + param.regex = '[asnblfom]'; + param.type = symbol; + next(); + break; + case '-': // use context if param not supplied + prevParam.context = true; + prevParam.contextRegex = new RegExp(prevParam.regex); // pre-compiled to test the context type at runtime + prevParam.regex += '?'; + break; + case '?': // optional param + case '+': // one or more + prevParam.regex += symbol; + break; + case '(': // choice of types + // search forward for matching ')' + var endParen = findClosingBracket(signature, position, '(', ')'); + var choice = signature.substring(position + 1, endParen); + if (choice.indexOf('<') === -1) { + // no parameterized types, simple regex + param.regex = '[' + choice + 'm]'; + } else { + // TODO harder + throw { + code: "S0402", + stack: (new Error()).stack, + value: choice, + offset: position + }; + } + param.type = '(' + choice + ')'; + position = endParen; + next(); + break; + case '<': // type parameter - can only be applied to 'a' and 'f' + if (prevParam.type === 'a' || prevParam.type === 'f') { + // search forward for matching '>' + var endPos = findClosingBracket(signature, position, '<', '>'); + prevParam.subtype = signature.substring(position + 1, endPos); + position = endPos; + } else { + throw { + code: "S0401", + stack: (new Error()).stack, + value: prevParam.type, + offset: position + }; + } + break; + } + position++; + } + var regexStr = '^' + + params.map(function (param) { + return '(' + param.regex + ')'; + }).join('') + + '$'; + var regex = new RegExp(regexStr); + var getSymbol = function (value) { + var symbol; + if (utils.isFunction(value)) { + symbol = 'f'; + } else { + var type = typeof value; + switch (type) { + case 'string': + symbol = 's'; + break; + case 'number': + symbol = 'n'; + break; + case 'boolean': + symbol = 'b'; + break; + case 'object': + if (value === null) { + symbol = 'l'; + } else if (Array.isArray(value)) { + symbol = 'a'; + } else { + symbol = 'o'; + } + break; + case 'undefined': + default: + // any value can be undefined, but should be allowed to match + symbol = 'm'; // m for missing + } + } + return symbol; + }; + + var throwValidationError = function (badArgs, badSig) { + // to figure out where this went wrong we need apply each component of the + // regex to each argument until we get to the one that fails to match + var partialPattern = '^'; + var goodTo = 0; + for (var index = 0; index < params.length; index++) { + partialPattern += params[index].regex; + var match = badSig.match(partialPattern); + if (match === null) { + // failed here + throw { + code: "T0410", + stack: (new Error()).stack, + value: badArgs[goodTo], + index: goodTo + 1 + }; + } + goodTo = match[0].length; + } + // if it got this far, it's probably because of extraneous arguments (we + // haven't added the trailing '$' in the regex yet. + throw { + code: "T0410", + stack: (new Error()).stack, + value: badArgs[goodTo], + index: goodTo + 1 + }; + }; + + return { + definition: signature, + validate: function (args, context) { + var suppliedSig = ''; + args.forEach(function (arg) { + suppliedSig += getSymbol(arg); + }); + var isValid = regex.exec(suppliedSig); + if (isValid) { + var validatedArgs = []; + var argIndex = 0; + params.forEach(function (param, index) { + var arg = args[argIndex]; + var match = isValid[index + 1]; + if (match === '') { + if (param.context && param.contextRegex) { + // substitute context value for missing arg + // first check that the context value is the right type + var contextType = getSymbol(context); + // test contextType against the regex for this arg (without the trailing ?) + if (param.contextRegex.test(contextType)) { + validatedArgs.push(context); + } else { + // context value not compatible with this argument + throw { + code: "T0411", + stack: (new Error()).stack, + value: context, + index: argIndex + 1 + }; + } + } else { + validatedArgs.push(arg); + argIndex++; + } + } else { + // may have matched multiple args (if the regex ends with a '+' + // split into single tokens + match.split('').forEach(function (single) { + if (param.type === 'a') { + if (single === 'm') { + // missing (undefined) + arg = undefined; + } else { + arg = args[argIndex]; + var arrayOK = true; + // is there type information on the contents of the array? + if (typeof param.subtype !== 'undefined') { + if (single !== 'a' && match !== param.subtype) { + arrayOK = false; + } else if (single === 'a') { + if (arg.length > 0) { + var itemType = getSymbol(arg[0]); + if (itemType !== param.subtype.charAt(0)) { // TODO recurse further + arrayOK = false; + } else { + // make sure every item in the array is this type + var differentItems = arg.filter(function (val) { + return (getSymbol(val) !== itemType); + }); + arrayOK = (differentItems.length === 0); + } + } + } + } + if (!arrayOK) { + throw { + code: "T0412", + stack: (new Error()).stack, + value: arg, + index: argIndex + 1, + type: arraySignatureMapping[param.subtype] + }; + } + // the function expects an array. If it's not one, make it so + if (single !== 'a') { + arg = [arg]; + } + } + validatedArgs.push(arg); + argIndex++; + } else { + validatedArgs.push(arg); + argIndex++; + } + }); + } + }); + return validatedArgs; + } + throwValidationError(args, suppliedSig); + } + }; + } + + return parseSignature; +})(); + +module.exports = signature; + +},{"./utils":6}],6:[function(require,module,exports){ +/** + * © Copyright IBM Corp. 2016, 2018 All Rights Reserved + * Project name: JSONata + * This project is licensed under the MIT License, see LICENSE + */ + +const utils = (() => { + 'use strict'; + + /** + * Check if value is a finite number + * @param {float} n - number to evaluate + * @returns {boolean} True if n is a finite number + */ + function isNumeric(n) { + var isNum = false; + if(typeof n === 'number') { + isNum = !isNaN(n); + if (isNum && !isFinite(n)) { + throw { + code: "D1001", + value: n, + stack: (new Error()).stack + }; + } + } + return isNum; + } + + /** + * Returns true if the arg is an array of strings + * @param {*} arg - the item to test + * @returns {boolean} True if arg is an array of strings + */ + function isArrayOfStrings(arg) { + var result = false; + /* istanbul ignore else */ + if(Array.isArray(arg)) { + result = (arg.filter(function(item){return typeof item !== 'string';}).length === 0); + } + return result; + } + + /** + * Returns true if the arg is an array of numbers + * @param {*} arg - the item to test + * @returns {boolean} True if arg is an array of numbers + */ + function isArrayOfNumbers(arg) { + var result = false; + if(Array.isArray(arg)) { + result = (arg.filter(function(item){return !isNumeric(item);}).length === 0); + } + return result; + } + + /** + * Create an empty sequence to contain query results + * @returns {Array} - empty sequence + */ + function createSequence() { + var sequence = []; + sequence.sequence = true; + if (arguments.length === 1) { + sequence.push(arguments[0]); + } + return sequence; + } + + /** + * Tests if a value is a sequence + * @param {*} value the value to test + * @returns {boolean} true if it's a sequence + */ + function isSequence(value) { + return value.sequence === true && Array.isArray(value); + } + + /** + * + * @param {Object} arg - expression to test + * @returns {boolean} - true if it is a function (lambda or built-in) + */ + function isFunction(arg) { + return ((arg && (arg._jsonata_function === true || arg._jsonata_lambda === true)) || typeof arg === 'function'); + } + + /** + * Returns the arity (number of arguments) of the function + * @param {*} func - the function + * @returns {*} - the arity + */ + function getFunctionArity(func) { + var arity = typeof func.arity === 'number' ? func.arity : + typeof func.implementation === 'function' ? func.implementation.length : + typeof func.length === 'number' ? func.length : func.arguments.length; + return arity; + } + + /** + * Tests whether arg is a lambda function + * @param {*} arg - the value to test + * @returns {boolean} - true if it is a lambda function + */ + function isLambda(arg) { + return arg && arg._jsonata_lambda === true; + } + + // istanbul ignore next + var $Symbol = typeof Symbol === "function" ? Symbol : {}; + // istanbul ignore next + var iteratorSymbol = $Symbol.iterator || "@@iterator"; + + /** + * @param {Object} arg - expression to test + * @returns {boolean} - true if it is iterable + */ + function isIterable(arg) { + return ( + typeof arg === 'object' && + arg !== null && + iteratorSymbol in arg && + 'next' in arg && + typeof arg.next === 'function' + ); + } + + /** + * Compares two values for equality + * @param {*} lhs first value + * @param {*} rhs second value + * @returns {boolean} true if they are deep equal + */ + function isDeepEqual(lhs, rhs) { + if (lhs === rhs) { + return true; + } + if(typeof lhs === 'object' && typeof rhs === 'object' && lhs !== null && rhs !== null) { + if(Array.isArray(lhs) && Array.isArray(rhs)) { + // both arrays (or sequences) + // must be the same length + if(lhs.length !== rhs.length) { + return false; + } + // must contain same values in same order + for(var ii = 0; ii < lhs.length; ii++) { + if(!isDeepEqual(lhs[ii], rhs[ii])) { + return false; + } + } + return true; + } + // both objects + // must have the same set of keys (in any order) + var lkeys = Object.getOwnPropertyNames(lhs); + var rkeys = Object.getOwnPropertyNames(rhs); + if(lkeys.length !== rkeys.length) { + return false; + } + lkeys = lkeys.sort(); + rkeys = rkeys.sort(); + for(ii=0; ii < lkeys.length; ii++) { + if(lkeys[ii] !== rkeys[ii]) { + return false; + } + } + // must have the same values + for(ii=0; ii < lkeys.length; ii++) { + var key = lkeys[ii]; + if(!isDeepEqual(lhs[key], rhs[key])) { + return false; + } + } + return true; + } + return false; + } + + return { + isNumeric, + isArrayOfStrings, + isArrayOfNumbers, + createSequence, + isSequence, + isFunction, + isLambda, + isIterable, + getFunctionArity, + isDeepEqual + }; +})(); + +module.exports = utils; + +},{}]},{},[3])(3) +}); + + +/***/ }), + +/***/ 375: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + +const {PassThrough: PassThroughStream} = __webpack_require__(413); + +module.exports = options => { + options = {...options}; + + const {array} = options; + let {encoding} = options; + const isBuffer = encoding === 'buffer'; + let objectMode = false; + + if (array) { + objectMode = !(encoding || isBuffer); + } else { + encoding = encoding || 'utf8'; + } + + if (isBuffer) { + encoding = null; + } + + const stream = new PassThroughStream({objectMode}); + + if (encoding) { + stream.setEncoding(encoding); + } + + let length = 0; + const chunks = []; + + stream.on('data', chunk => { + chunks.push(chunk); + + if (objectMode) { + length = chunks.length; + } else { + length += chunk.length; + } + }); + + stream.getBufferedValue = () => { + if (array) { + return chunks; + } + + return isBuffer ? Buffer.concat(chunks, length) : chunks.join(''); + }; + + stream.getBufferedLength = () => length; + + return stream; +}; + + +/***/ }), + +/***/ 390: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +const EventEmitter = __webpack_require__(614); +const urlLib = __webpack_require__(835); +const normalizeUrl = __webpack_require__(53); +const getStream = __webpack_require__(997); +const CachePolicy = __webpack_require__(154); +const Response = __webpack_require__(93); +const lowercaseKeys = __webpack_require__(474); +const cloneResponse = __webpack_require__(325); +const Keyv = __webpack_require__(303); + +class CacheableRequest { + constructor(request, cacheAdapter) { + if (typeof request !== 'function') { + throw new TypeError('Parameter `request` must be a function'); + } + + this.cache = new Keyv({ + uri: typeof cacheAdapter === 'string' && cacheAdapter, + store: typeof cacheAdapter !== 'string' && cacheAdapter, + namespace: 'cacheable-request' + }); + + return this.createCacheableRequest(request); + } + + createCacheableRequest(request) { + return (opts, cb) => { + let url; + if (typeof opts === 'string') { + url = normalizeUrlObject(urlLib.parse(opts)); + opts = {}; + } else if (opts instanceof urlLib.URL) { + url = normalizeUrlObject(urlLib.parse(opts.toString())); + opts = {}; + } else { + const [pathname, ...searchParts] = (opts.path || '').split('?'); + const search = searchParts.length > 0 ? + `?${searchParts.join('?')}` : + ''; + url = normalizeUrlObject({ ...opts, pathname, search }); + } + + opts = { + headers: {}, + method: 'GET', + cache: true, + strictTtl: false, + automaticFailover: false, + ...opts, + ...urlObjectToRequestOptions(url) + }; + opts.headers = lowercaseKeys(opts.headers); + + const ee = new EventEmitter(); + const normalizedUrlString = normalizeUrl( + urlLib.format(url), + { + stripWWW: false, + removeTrailingSlash: false, + stripAuthentication: false + } + ); + const key = `${opts.method}:${normalizedUrlString}`; + let revalidate = false; + let madeRequest = false; + + const makeRequest = opts => { + madeRequest = true; + let requestErrored = false; + let requestErrorCallback; + + const requestErrorPromise = new Promise(resolve => { + requestErrorCallback = () => { + if (!requestErrored) { + requestErrored = true; + resolve(); + } + }; + }); + + const handler = response => { + if (revalidate && !opts.forceRefresh) { + response.status = response.statusCode; + const revalidatedPolicy = CachePolicy.fromObject(revalidate.cachePolicy).revalidatedPolicy(opts, response); + if (!revalidatedPolicy.modified) { + const headers = revalidatedPolicy.policy.responseHeaders(); + response = new Response(revalidate.statusCode, headers, revalidate.body, revalidate.url); + response.cachePolicy = revalidatedPolicy.policy; + response.fromCache = true; + } + } + + if (!response.fromCache) { + response.cachePolicy = new CachePolicy(opts, response, opts); + response.fromCache = false; + } + + let clonedResponse; + if (opts.cache && response.cachePolicy.storable()) { + clonedResponse = cloneResponse(response); + + (async () => { + try { + const bodyPromise = getStream.buffer(response); + + await Promise.race([ + requestErrorPromise, + new Promise(resolve => response.once('end', resolve)) + ]); + + if (requestErrored) { + return; + } + + const body = await bodyPromise; + + const value = { + cachePolicy: response.cachePolicy.toObject(), + url: response.url, + statusCode: response.fromCache ? revalidate.statusCode : response.statusCode, + body + }; + + let ttl = opts.strictTtl ? response.cachePolicy.timeToLive() : undefined; + if (opts.maxTtl) { + ttl = ttl ? Math.min(ttl, opts.maxTtl) : opts.maxTtl; + } + + await this.cache.set(key, value, ttl); + } catch (error) { + ee.emit('error', new CacheableRequest.CacheError(error)); + } + })(); + } else if (opts.cache && revalidate) { + (async () => { + try { + await this.cache.delete(key); + } catch (error) { + ee.emit('error', new CacheableRequest.CacheError(error)); + } + })(); + } + + ee.emit('response', clonedResponse || response); + if (typeof cb === 'function') { + cb(clonedResponse || response); + } + }; + + try { + const req = request(opts, handler); + req.once('error', requestErrorCallback); + req.once('abort', requestErrorCallback); + ee.emit('request', req); + } catch (error) { + ee.emit('error', new CacheableRequest.RequestError(error)); + } + }; + + (async () => { + const get = async opts => { + await Promise.resolve(); + + const cacheEntry = opts.cache ? await this.cache.get(key) : undefined; + if (typeof cacheEntry === 'undefined') { + return makeRequest(opts); + } + + const policy = CachePolicy.fromObject(cacheEntry.cachePolicy); + if (policy.satisfiesWithoutRevalidation(opts) && !opts.forceRefresh) { + const headers = policy.responseHeaders(); + const response = new Response(cacheEntry.statusCode, headers, cacheEntry.body, cacheEntry.url); + response.cachePolicy = policy; + response.fromCache = true; + + ee.emit('response', response); + if (typeof cb === 'function') { + cb(response); + } + } else { + revalidate = cacheEntry; + opts.headers = policy.revalidationHeaders(opts); + makeRequest(opts); + } + }; + + const errorHandler = error => ee.emit('error', new CacheableRequest.CacheError(error)); + this.cache.once('error', errorHandler); + ee.on('response', () => this.cache.removeListener('error', errorHandler)); + + try { + await get(opts); + } catch (error) { + if (opts.automaticFailover && !madeRequest) { + makeRequest(opts); + } + + ee.emit('error', new CacheableRequest.CacheError(error)); + } + })(); + + return ee; + }; + } +} + +function urlObjectToRequestOptions(url) { + const options = { ...url }; + options.path = `${url.pathname || '/'}${url.search || ''}`; + delete options.pathname; + delete options.search; + return options; +} + +function normalizeUrlObject(url) { + // If url was parsed by url.parse or new URL: + // - hostname will be set + // - host will be hostname[:port] + // - port will be set if it was explicit in the parsed string + // Otherwise, url was from request options: + // - hostname or host may be set + // - host shall not have port encoded + return { + protocol: url.protocol, + auth: url.auth, + hostname: url.hostname || url.host || 'localhost', + port: url.port, + pathname: url.pathname, + search: url.search + }; +} + +CacheableRequest.RequestError = class extends Error { + constructor(error) { + super(error.message); + this.name = 'RequestError'; + Object.assign(this, error); + } +}; + +CacheableRequest.CacheError = class extends Error { + constructor(error) { + super(error.message); + this.name = 'CacheError'; + Object.assign(this, error); + } +}; + +module.exports = CacheableRequest; + + +/***/ }), + +/***/ 413: +/***/ (function(module) { + +module.exports = require("stream"); + +/***/ }), + +/***/ 431: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const os = __importStar(__webpack_require__(87)); +/** + * Commands + * + * Command Format: + * ::name key=value,key=value::message + * + * Examples: + * ::warning::This is the message + * ::set-env name=MY_VAR::some value + */ +function issueCommand(command, properties, message) { + const cmd = new Command(command, properties, message); + process.stdout.write(cmd.toString() + os.EOL); +} +exports.issueCommand = issueCommand; +function issue(name, message = '') { + issueCommand(name, {}, message); +} +exports.issue = issue; +const CMD_STRING = '::'; +class Command { + constructor(command, properties, message) { + if (!command) { + command = 'missing.command'; + } + this.command = command; + this.properties = properties; + this.message = message; + } + toString() { + let cmdStr = CMD_STRING + this.command; + if (this.properties && Object.keys(this.properties).length > 0) { + cmdStr += ' '; + let first = true; + for (const key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + const val = this.properties[key]; + if (val) { + if (first) { + first = false; + } + else { + cmdStr += ','; + } + cmdStr += `${key}=${escapeProperty(val)}`; + } + } + } + } + cmdStr += `${CMD_STRING}${escapeData(this.message)}`; + return cmdStr; + } +} +function escapeData(s) { + return (s || '') + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A'); +} +function escapeProperty(s) { + return (s || '') + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A') + .replace(/:/g, '%3A') + .replace(/,/g, '%2C'); +} +//# sourceMappingURL=command.js.map + +/***/ }), + +/***/ 452: +/***/ (function(__unusedmodule, exports) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +// TODO: Update https://github.com/sindresorhus/get-stream +const getBuffer = async (stream) => { + const chunks = []; + let length = 0; + for await (const chunk of stream) { + chunks.push(chunk); + length += Buffer.byteLength(chunk); + } + if (Buffer.isBuffer(chunks[0])) { + return Buffer.concat(chunks, length); + } + return Buffer.from(chunks.join('')); +}; +exports.default = getBuffer; + + +/***/ }), + +/***/ 453: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var once = __webpack_require__(49) +var eos = __webpack_require__(9) +var fs = __webpack_require__(747) // we only need fs to get the ReadStream and WriteStream prototypes + +var noop = function () {} +var ancient = /^v?\.0/.test(process.version) + +var isFn = function (fn) { + return typeof fn === 'function' +} + +var isFS = function (stream) { + if (!ancient) return false // newer node version do not need to care about fs is a special way + if (!fs) return false // browser + return (stream instanceof (fs.ReadStream || noop) || stream instanceof (fs.WriteStream || noop)) && isFn(stream.close) +} + +var isRequest = function (stream) { + return stream.setHeader && isFn(stream.abort) +} + +var destroyer = function (stream, reading, writing, callback) { + callback = once(callback) + + var closed = false + stream.on('close', function () { + closed = true + }) + + eos(stream, {readable: reading, writable: writing}, function (err) { + if (err) return callback(err) + closed = true + callback() + }) + + var destroyed = false + return function (err) { + if (closed) return + if (destroyed) return + destroyed = true + + if (isFS(stream)) return stream.close(noop) // use close for fs streams to avoid fd leaks + if (isRequest(stream)) return stream.abort() // request.destroy just do .end - .abort is what we want + + if (isFn(stream.destroy)) return stream.destroy() + + callback(err || new Error('stream was destroyed')) + } +} + +var call = function (fn) { + fn() +} + +var pipe = function (from, to) { + return from.pipe(to) +} + +var pump = function () { + var streams = Array.prototype.slice.call(arguments) + var callback = isFn(streams[streams.length - 1] || noop) && streams.pop() || noop + + if (Array.isArray(streams[0])) streams = streams[0] + if (streams.length < 2) throw new Error('pump requires two streams per minimum') + + var error + var destroys = streams.map(function (stream, i) { + var reading = i < streams.length - 1 + var writing = i > 0 + return destroyer(stream, reading, writing, function (err) { + if (!error) error = err + if (err) destroys.forEach(call) + if (reading) return + destroys.forEach(call) + callback(error) + }) + }) + + return streams.reduce(pipe) +} + +module.exports = pump + + +/***/ }), + +/***/ 460: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const is_1 = __webpack_require__(534); +exports.default = (body) => is_1.default.nodeStream(body) && is_1.default.function_(body.getBoundary); + + +/***/ }), + +/***/ 461: +/***/ (function(module, __unusedexports, __webpack_require__) { + +const jsonata = __webpack_require__(350); + + +/** + * @typedef {Object} SecretRequest + * @property {string} path + * @property {string} selector + */ + +/** + * @template {SecretRequest} TRequest + * @typedef {Object} SecretResponse + * @property {TRequest} request + * @property {string} value + * @property {boolean} cachedResponse + */ + + /** + * @template TRequest + * @param {Array} secretRequests + * @param {import('got').Got} client + * @return {Promise[]>} + */ +async function getSecrets(secretRequests, client) { + const responseCache = new Map(); + const results = []; + for (const secretRequest of secretRequests) { + const { path, selector } = secretRequest; + + const requestPath = `v1${path}`; + let body; + let cachedResponse = false; + if (responseCache.has(requestPath)) { + body = responseCache.get(requestPath); + cachedResponse = true; + } else { + const result = await client.get(requestPath); + body = result.body; + responseCache.set(requestPath, body); + } + + const value = selectData(JSON.parse(body), selector); + results.push({ + request: secretRequest, + value, + cachedResponse + }); + } + return results; +} + +/** + * Uses a Jsonata selector retrieve a bit of data from the result + * @param {object} data + * @param {string} selector + */ +function selectData(data, selector) { + const ata = jsonata(selector); + let result = JSON.stringify(ata.evaluate(data)); + // Compat for custom engines + if (!result && ata.ast().type === "path" && ata.ast()['steps'].length === 1 && selector !== 'data' && 'data' in data) { + result = JSON.stringify(jsonata(`data.${selector}`).evaluate(data)); + } else if (!result) { + throw Error(`Unable to retrieve result for ${selector}. No match data was found. Double check your Key or Selector.`); + } + + if (result.startsWith(`"`)) { + result = result.substring(1, result.length - 1); + } + return result; +} + +module.exports = { + getSecrets, + selectData +} + +/***/ }), + +/***/ 468: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.parseBody = exports.knownBodyTypes = void 0; +const is_1 = __webpack_require__(534); +const types_1 = __webpack_require__(36); +const core_1 = __webpack_require__(946); +if (!core_1.knownHookEvents.includes('beforeRetry')) { + core_1.knownHookEvents.push('beforeRetry', 'afterResponse'); +} +exports.knownBodyTypes = ['json', 'buffer', 'text']; +exports.parseBody = (response, responseType, parseJson, encoding) => { + const { rawBody } = response; + try { + if (responseType === 'text') { + return rawBody.toString(encoding); + } + if (responseType === 'json') { + return rawBody.length === 0 ? '' : parseJson(rawBody.toString()); + } + if (responseType === 'buffer') { + return Buffer.from(rawBody); + } + throw new types_1.ParseError({ + message: `Unknown body type '${responseType}'`, + name: 'Error' + }, response); + } + catch (error) { + throw new types_1.ParseError(error, response); + } +}; +class PromisableRequest extends core_1.default { + static normalizeArguments(url, nonNormalizedOptions, defaults) { + const options = super.normalizeArguments(url, nonNormalizedOptions, defaults); + if (is_1.default.null_(options.encoding)) { + throw new TypeError('To get a Buffer, set `options.responseType` to `buffer` instead'); + } + is_1.assert.any([is_1.default.string, is_1.default.undefined], options.encoding); + is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.resolveBodyOnly); + is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.methodRewriting); + is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.isStream); + is_1.assert.any([is_1.default.string, is_1.default.undefined], options.responseType); + // `options.responseType` + if (options.responseType === undefined) { + options.responseType = 'text'; + } + // `options.retry` + const { retry } = options; + if (defaults) { + options.retry = { ...defaults.retry }; + } + else { + options.retry = { + calculateDelay: retryObject => retryObject.computedValue, + limit: 0, + methods: [], + statusCodes: [], + errorCodes: [], + maxRetryAfter: undefined + }; + } + if (is_1.default.object(retry)) { + options.retry = { + ...options.retry, + ...retry + }; + options.retry.methods = [...new Set(options.retry.methods.map(method => method.toUpperCase()))]; + options.retry.statusCodes = [...new Set(options.retry.statusCodes)]; + options.retry.errorCodes = [...new Set(options.retry.errorCodes)]; + } + else if (is_1.default.number(retry)) { + options.retry.limit = retry; + } + if (is_1.default.undefined(options.retry.maxRetryAfter)) { + options.retry.maxRetryAfter = Math.min( + // TypeScript is not smart enough to handle `.filter(x => is.number(x))`. + // eslint-disable-next-line unicorn/no-fn-reference-in-iterator + ...[options.timeout.request, options.timeout.connect].filter(is_1.default.number)); + } + // `options.pagination` + if (is_1.default.object(options.pagination)) { + if (defaults) { + options.pagination = { + ...defaults.pagination, + ...options.pagination + }; + } + const { pagination } = options; + if (!is_1.default.function_(pagination.transform)) { + throw new Error('`options.pagination.transform` must be implemented'); + } + if (!is_1.default.function_(pagination.shouldContinue)) { + throw new Error('`options.pagination.shouldContinue` must be implemented'); + } + if (!is_1.default.function_(pagination.filter)) { + throw new TypeError('`options.pagination.filter` must be implemented'); + } + if (!is_1.default.function_(pagination.paginate)) { + throw new Error('`options.pagination.paginate` must be implemented'); + } + } + // JSON mode + if (options.responseType === 'json' && options.headers.accept === undefined) { + options.headers.accept = 'application/json'; + } + return options; + } + static mergeOptions(...sources) { + let mergedOptions; + for (const source of sources) { + mergedOptions = PromisableRequest.normalizeArguments(undefined, source, mergedOptions); + } + return mergedOptions; + } + _beforeError(error) { + if (this.destroyed) { + return; + } + if (!(error instanceof core_1.RequestError)) { + error = new core_1.RequestError(error.message, error, this); + } + // Let the promise decide whether to abort or not + // It is also responsible for the `beforeError` hook + this.emit('error', error); + } +} +exports.default = PromisableRequest; + + +/***/ }), + +/***/ 470: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const command_1 = __webpack_require__(431); +const os = __importStar(__webpack_require__(87)); +const path = __importStar(__webpack_require__(622)); +/** + * The code to exit an action + */ +var ExitCode; +(function (ExitCode) { + /** + * A code indicating that the action was successful + */ + ExitCode[ExitCode["Success"] = 0] = "Success"; + /** + * A code indicating that the action was a failure + */ + ExitCode[ExitCode["Failure"] = 1] = "Failure"; +})(ExitCode = exports.ExitCode || (exports.ExitCode = {})); +//----------------------------------------------------------------------- +// Variables +//----------------------------------------------------------------------- +/** + * Sets env variable for this action and future actions in the job + * @param name the name of the variable to set + * @param val the value of the variable + */ +function exportVariable(name, val) { + process.env[name] = val; + command_1.issueCommand('set-env', { name }, val); +} +exports.exportVariable = exportVariable; +/** + * Registers a secret which will get masked from logs + * @param secret value of the secret + */ +function setSecret(secret) { + command_1.issueCommand('add-mask', {}, secret); +} +exports.setSecret = setSecret; +/** + * Prepends inputPath to the PATH (for this action and future actions) + * @param inputPath + */ +function addPath(inputPath) { + command_1.issueCommand('add-path', {}, inputPath); + process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`; +} +exports.addPath = addPath; +/** + * Gets the value of an input. The value is also trimmed. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string + */ +function getInput(name, options) { + const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; + if (options && options.required && !val) { + throw new Error(`Input required and not supplied: ${name}`); + } + return val.trim(); +} +exports.getInput = getInput; +/** + * Sets the value of an output. + * + * @param name name of the output to set + * @param value value to store + */ +function setOutput(name, value) { + command_1.issueCommand('set-output', { name }, value); +} +exports.setOutput = setOutput; +//----------------------------------------------------------------------- +// Results +//----------------------------------------------------------------------- +/** + * Sets the action status to failed. + * When the action exits it will be with an exit code of 1 + * @param message add error issue message + */ +function setFailed(message) { + process.exitCode = ExitCode.Failure; + error(message); +} +exports.setFailed = setFailed; +//----------------------------------------------------------------------- +// Logging Commands +//----------------------------------------------------------------------- +/** + * Gets whether Actions Step Debug is on or not + */ +function isDebug() { + return process.env['RUNNER_DEBUG'] === '1'; +} +exports.isDebug = isDebug; +/** + * Writes debug message to user log + * @param message debug message + */ +function debug(message) { + command_1.issueCommand('debug', {}, message); +} +exports.debug = debug; +/** + * Adds an error issue + * @param message error issue message + */ +function error(message) { + command_1.issue('error', message); +} +exports.error = error; +/** + * Adds an warning issue + * @param message warning issue message + */ +function warning(message) { + command_1.issue('warning', message); +} +exports.warning = warning; +/** + * Writes info to log with console.log. + * @param message info message + */ +function info(message) { + process.stdout.write(message + os.EOL); +} +exports.info = info; +/** + * Begin an output group. + * + * Output until the next `groupEnd` will be foldable in this group + * + * @param name The name of the output group + */ +function startGroup(name) { + command_1.issue('group', name); +} +exports.startGroup = startGroup; +/** + * End an output group. + */ +function endGroup() { + command_1.issue('endgroup'); +} +exports.endGroup = endGroup; +/** + * Wrap an asynchronous function call in a group. + * + * Returns the same type as the function itself. + * + * @param name The name of the group + * @param fn The function to wrap in the group + */ +function group(name, fn) { + return __awaiter(this, void 0, void 0, function* () { + startGroup(name); + let result; + try { + result = yield fn(); + } + finally { + endGroup(); + } + return result; + }); +} +exports.group = group; +//----------------------------------------------------------------------- +// Wrapper action state +//----------------------------------------------------------------------- +/** + * Saves state for current action, the state can only be retrieved by this action's post job execution. + * + * @param name name of the state to store + * @param value value to store + */ +function saveState(name, value) { + command_1.issueCommand('save-state', { name }, value); +} +exports.saveState = saveState; +/** + * Gets the value of an state set by this action's main execution. + * + * @param name name of the state to get + * @returns string + */ +function getState(name) { + return process.env[`STATE_${name}`] || ''; +} +exports.getState = getState; +//# sourceMappingURL=core.js.map + +/***/ }), + +/***/ 474: +/***/ (function(module) { + +"use strict"; + +module.exports = object => { + const result = {}; + + for (const [key, value] of Object.entries(object)) { + result[key.toLowerCase()] = value; + } + + return result; +}; + + +/***/ }), + +/***/ 490: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const defer_to_connect_1 = __webpack_require__(790); +const nodejsMajorVersion = Number(process.versions.node.split('.')[0]); +const timer = (request) => { + const timings = { + start: Date.now(), + socket: undefined, + lookup: undefined, + connect: undefined, + secureConnect: undefined, + upload: undefined, + response: undefined, + end: undefined, + error: undefined, + abort: undefined, + phases: { + wait: undefined, + dns: undefined, + tcp: undefined, + tls: undefined, + request: undefined, + firstByte: undefined, + download: undefined, + total: undefined + } + }; + request.timings = timings; + const handleError = (origin) => { + const emit = origin.emit.bind(origin); + origin.emit = (event, ...args) => { + // Catches the `error` event + if (event === 'error') { + timings.error = Date.now(); + timings.phases.total = timings.error - timings.start; + origin.emit = emit; + } + // Saves the original behavior + return emit(event, ...args); + }; + }; + handleError(request); + request.prependOnceListener('abort', () => { + timings.abort = Date.now(); + // Let the `end` response event be responsible for setting the total phase, + // unless the Node.js major version is >= 13. + if (!timings.response || nodejsMajorVersion >= 13) { + timings.phases.total = Date.now() - timings.start; + } + }); + const onSocket = (socket) => { + timings.socket = Date.now(); + timings.phases.wait = timings.socket - timings.start; + const lookupListener = () => { + timings.lookup = Date.now(); + timings.phases.dns = timings.lookup - timings.socket; + }; + socket.prependOnceListener('lookup', lookupListener); + defer_to_connect_1.default(socket, { + connect: () => { + timings.connect = Date.now(); + if (timings.lookup === undefined) { + socket.removeListener('lookup', lookupListener); + timings.lookup = timings.connect; + timings.phases.dns = timings.lookup - timings.socket; + } + timings.phases.tcp = timings.connect - timings.lookup; + // This callback is called before flushing any data, + // so we don't need to set `timings.phases.request` here. + }, + secureConnect: () => { + timings.secureConnect = Date.now(); + timings.phases.tls = timings.secureConnect - timings.connect; + } + }); + }; + if (request.socket) { + onSocket(request.socket); + } + else { + request.prependOnceListener('socket', onSocket); + } + const onUpload = () => { + var _a; + timings.upload = Date.now(); + timings.phases.request = timings.upload - (_a = timings.secureConnect, (_a !== null && _a !== void 0 ? _a : timings.connect)); + }; + const writableFinished = () => { + if (typeof request.writableFinished === 'boolean') { + return request.writableFinished; + } + // Node.js doesn't have `request.writableFinished` property + return request.finished && request.outputSize === 0 && (!request.socket || request.socket.writableLength === 0); + }; + if (writableFinished()) { + onUpload(); + } + else { + request.prependOnceListener('finish', onUpload); + } + request.prependOnceListener('response', (response) => { + timings.response = Date.now(); + timings.phases.firstByte = timings.response - timings.upload; + response.timings = timings; + handleError(response); + response.prependOnceListener('end', () => { + timings.end = Date.now(); + timings.phases.download = timings.end - timings.response; + timings.phases.total = timings.end - timings.start; + }); + }); + return timings; +}; +exports.default = timer; +// For CommonJS default export support +module.exports = timer; +module.exports.default = timer; + + +/***/ }), + +/***/ 492: +/***/ (function(__unusedmodule, __unusedexports, __webpack_require__) { + +const core = __webpack_require__(470); +const { exportSecrets } = __webpack_require__(928); + +(async () => { + try { + await core.group('Get Vault Secrets', exportSecrets); + } catch (error) { + core.setFailed(error.message); + } +})(); + + +/***/ }), + +/***/ 507: +/***/ (function(module) { + +"use strict"; + +/* istanbul ignore file: https://github.com/nodejs/node/blob/a91293d4d9ab403046ab5eb022332e4e3d249bd3/lib/internal/url.js#L1257 */ + +module.exports = url => { + const options = { + protocol: url.protocol, + hostname: typeof url.hostname === 'string' && url.hostname.startsWith('[') ? url.hostname.slice(1, -1) : url.hostname, + host: url.host, + hash: url.hash, + search: url.search, + pathname: url.pathname, + href: url.href, + path: `${url.pathname || ''}${url.search || ''}` + }; + + if (typeof url.port === 'string' && url.port.length !== 0) { + options.port = Number(url.port); + } + + if (url.username || url.password) { + options.auth = `${url.username || ''}:${url.password || ''}`; + } + + return options; +}; + + +/***/ }), + +/***/ 524: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + +const tls = __webpack_require__(16); + +module.exports = (options = {}) => new Promise((resolve, reject) => { + const socket = tls.connect(options, () => { + if (options.resolveSocket) { + socket.off('error', reject); + resolve({alpnProtocol: socket.alpnProtocol, socket}); + } else { + socket.destroy(); + resolve({alpnProtocol: socket.alpnProtocol}); + } + }); + + socket.on('error', reject); +}); + + +/***/ }), + +/***/ 534: +/***/ (function(module, exports) { + +"use strict"; + +/// +/// +/// +Object.defineProperty(exports, "__esModule", { value: true }); +const typedArrayTypeNames = [ + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array', + 'BigInt64Array', + 'BigUint64Array' +]; +function isTypedArrayName(name) { + return typedArrayTypeNames.includes(name); +} +const objectTypeNames = [ + 'Function', + 'Generator', + 'AsyncGenerator', + 'GeneratorFunction', + 'AsyncGeneratorFunction', + 'AsyncFunction', + 'Observable', + 'Array', + 'Buffer', + 'Object', + 'RegExp', + 'Date', + 'Error', + 'Map', + 'Set', + 'WeakMap', + 'WeakSet', + 'ArrayBuffer', + 'SharedArrayBuffer', + 'DataView', + 'Promise', + 'URL', + 'HTMLElement', + ...typedArrayTypeNames +]; +function isObjectTypeName(name) { + return objectTypeNames.includes(name); +} +const primitiveTypeNames = [ + 'null', + 'undefined', + 'string', + 'number', + 'bigint', + 'boolean', + 'symbol' +]; +function isPrimitiveTypeName(name) { + return primitiveTypeNames.includes(name); +} +// eslint-disable-next-line @typescript-eslint/ban-types +function isOfType(type) { + return (value) => typeof value === type; +} +const { toString } = Object.prototype; +const getObjectType = (value) => { + const objectTypeName = toString.call(value).slice(8, -1); + if (/HTML\w+Element/.test(objectTypeName) && is.domElement(value)) { + return 'HTMLElement'; + } + if (isObjectTypeName(objectTypeName)) { + return objectTypeName; + } + return undefined; +}; +const isObjectOfType = (type) => (value) => getObjectType(value) === type; +function is(value) { + if (value === null) { + return 'null'; + } + switch (typeof value) { + case 'undefined': + return 'undefined'; + case 'string': + return 'string'; + case 'number': + return 'number'; + case 'boolean': + return 'boolean'; + case 'function': + return 'Function'; + case 'bigint': + return 'bigint'; + case 'symbol': + return 'symbol'; + default: + } + if (is.observable(value)) { + return 'Observable'; + } + if (is.array(value)) { + return 'Array'; + } + if (is.buffer(value)) { + return 'Buffer'; + } + const tagType = getObjectType(value); + if (tagType) { + return tagType; + } + if (value instanceof String || value instanceof Boolean || value instanceof Number) { + throw new TypeError('Please don\'t use object wrappers for primitive types'); + } + return 'Object'; +} +is.undefined = isOfType('undefined'); +is.string = isOfType('string'); +const isNumberType = isOfType('number'); +is.number = (value) => isNumberType(value) && !is.nan(value); +is.bigint = isOfType('bigint'); +// eslint-disable-next-line @typescript-eslint/ban-types +is.function_ = isOfType('function'); +is.null_ = (value) => value === null; +is.class_ = (value) => is.function_(value) && value.toString().startsWith('class '); +is.boolean = (value) => value === true || value === false; +is.symbol = isOfType('symbol'); +is.numericString = (value) => is.string(value) && !is.emptyStringOrWhitespace(value) && !Number.isNaN(Number(value)); +is.array = (value, assertion) => { + if (!Array.isArray(value)) { + return false; + } + if (!assertion) { + return true; + } + return value.every(assertion); +}; +is.buffer = (value) => { var _a, _b, _c, _d; return (_d = (_c = (_b = (_a = value) === null || _a === void 0 ? void 0 : _a.constructor) === null || _b === void 0 ? void 0 : _b.isBuffer) === null || _c === void 0 ? void 0 : _c.call(_b, value)) !== null && _d !== void 0 ? _d : false; }; +is.nullOrUndefined = (value) => is.null_(value) || is.undefined(value); +is.object = (value) => !is.null_(value) && (typeof value === 'object' || is.function_(value)); +is.iterable = (value) => { var _a; return is.function_((_a = value) === null || _a === void 0 ? void 0 : _a[Symbol.iterator]); }; +is.asyncIterable = (value) => { var _a; return is.function_((_a = value) === null || _a === void 0 ? void 0 : _a[Symbol.asyncIterator]); }; +is.generator = (value) => is.iterable(value) && is.function_(value.next) && is.function_(value.throw); +is.asyncGenerator = (value) => is.asyncIterable(value) && is.function_(value.next) && is.function_(value.throw); +is.nativePromise = (value) => isObjectOfType('Promise')(value); +const hasPromiseAPI = (value) => { + var _a, _b; + return is.function_((_a = value) === null || _a === void 0 ? void 0 : _a.then) && + is.function_((_b = value) === null || _b === void 0 ? void 0 : _b.catch); +}; +is.promise = (value) => is.nativePromise(value) || hasPromiseAPI(value); +is.generatorFunction = isObjectOfType('GeneratorFunction'); +is.asyncGeneratorFunction = (value) => getObjectType(value) === 'AsyncGeneratorFunction'; +is.asyncFunction = (value) => getObjectType(value) === 'AsyncFunction'; +// eslint-disable-next-line no-prototype-builtins, @typescript-eslint/ban-types +is.boundFunction = (value) => is.function_(value) && !value.hasOwnProperty('prototype'); +is.regExp = isObjectOfType('RegExp'); +is.date = isObjectOfType('Date'); +is.error = isObjectOfType('Error'); +is.map = (value) => isObjectOfType('Map')(value); +is.set = (value) => isObjectOfType('Set')(value); +is.weakMap = (value) => isObjectOfType('WeakMap')(value); +is.weakSet = (value) => isObjectOfType('WeakSet')(value); +is.int8Array = isObjectOfType('Int8Array'); +is.uint8Array = isObjectOfType('Uint8Array'); +is.uint8ClampedArray = isObjectOfType('Uint8ClampedArray'); +is.int16Array = isObjectOfType('Int16Array'); +is.uint16Array = isObjectOfType('Uint16Array'); +is.int32Array = isObjectOfType('Int32Array'); +is.uint32Array = isObjectOfType('Uint32Array'); +is.float32Array = isObjectOfType('Float32Array'); +is.float64Array = isObjectOfType('Float64Array'); +is.bigInt64Array = isObjectOfType('BigInt64Array'); +is.bigUint64Array = isObjectOfType('BigUint64Array'); +is.arrayBuffer = isObjectOfType('ArrayBuffer'); +is.sharedArrayBuffer = isObjectOfType('SharedArrayBuffer'); +is.dataView = isObjectOfType('DataView'); +is.directInstanceOf = (instance, class_) => Object.getPrototypeOf(instance) === class_.prototype; +is.urlInstance = (value) => isObjectOfType('URL')(value); +is.urlString = (value) => { + if (!is.string(value)) { + return false; + } + try { + new URL(value); // eslint-disable-line no-new + return true; + } + catch (_a) { + return false; + } +}; +// TODO: Use the `not` operator with a type guard here when it's available. +// Example: `is.truthy = (value: unknown): value is (not false | not 0 | not '' | not undefined | not null) => Boolean(value);` +is.truthy = (value) => Boolean(value); +// Example: `is.falsy = (value: unknown): value is (not true | 0 | '' | undefined | null) => Boolean(value);` +is.falsy = (value) => !value; +is.nan = (value) => Number.isNaN(value); +is.primitive = (value) => is.null_(value) || isPrimitiveTypeName(typeof value); +is.integer = (value) => Number.isInteger(value); +is.safeInteger = (value) => Number.isSafeInteger(value); +is.plainObject = (value) => { + // From: https://github.com/sindresorhus/is-plain-obj/blob/master/index.js + if (toString.call(value) !== '[object Object]') { + return false; + } + const prototype = Object.getPrototypeOf(value); + return prototype === null || prototype === Object.getPrototypeOf({}); +}; +is.typedArray = (value) => isTypedArrayName(getObjectType(value)); +const isValidLength = (value) => is.safeInteger(value) && value >= 0; +is.arrayLike = (value) => !is.nullOrUndefined(value) && !is.function_(value) && isValidLength(value.length); +is.inRange = (value, range) => { + if (is.number(range)) { + return value >= Math.min(0, range) && value <= Math.max(range, 0); + } + if (is.array(range) && range.length === 2) { + return value >= Math.min(...range) && value <= Math.max(...range); + } + throw new TypeError(`Invalid range: ${JSON.stringify(range)}`); +}; +const NODE_TYPE_ELEMENT = 1; +const DOM_PROPERTIES_TO_CHECK = [ + 'innerHTML', + 'ownerDocument', + 'style', + 'attributes', + 'nodeValue' +]; +is.domElement = (value) => { + return is.object(value) && + value.nodeType === NODE_TYPE_ELEMENT && + is.string(value.nodeName) && + !is.plainObject(value) && + DOM_PROPERTIES_TO_CHECK.every(property => property in value); +}; +is.observable = (value) => { + var _a, _b, _c, _d; + if (!value) { + return false; + } + // eslint-disable-next-line no-use-extend-native/no-use-extend-native + if (value === ((_b = (_a = value)[Symbol.observable]) === null || _b === void 0 ? void 0 : _b.call(_a))) { + return true; + } + if (value === ((_d = (_c = value)['@@observable']) === null || _d === void 0 ? void 0 : _d.call(_c))) { + return true; + } + return false; +}; +is.nodeStream = (value) => is.object(value) && is.function_(value.pipe) && !is.observable(value); +is.infinite = (value) => value === Infinity || value === -Infinity; +const isAbsoluteMod2 = (remainder) => (value) => is.integer(value) && Math.abs(value % 2) === remainder; +is.evenInteger = isAbsoluteMod2(0); +is.oddInteger = isAbsoluteMod2(1); +is.emptyArray = (value) => is.array(value) && value.length === 0; +is.nonEmptyArray = (value) => is.array(value) && value.length > 0; +is.emptyString = (value) => is.string(value) && value.length === 0; +// TODO: Use `not ''` when the `not` operator is available. +is.nonEmptyString = (value) => is.string(value) && value.length > 0; +const isWhiteSpaceString = (value) => is.string(value) && !/\S/.test(value); +is.emptyStringOrWhitespace = (value) => is.emptyString(value) || isWhiteSpaceString(value); +is.emptyObject = (value) => is.object(value) && !is.map(value) && !is.set(value) && Object.keys(value).length === 0; +// TODO: Use `not` operator here to remove `Map` and `Set` from type guard: +// - https://github.com/Microsoft/TypeScript/pull/29317 +is.nonEmptyObject = (value) => is.object(value) && !is.map(value) && !is.set(value) && Object.keys(value).length > 0; +is.emptySet = (value) => is.set(value) && value.size === 0; +is.nonEmptySet = (value) => is.set(value) && value.size > 0; +is.emptyMap = (value) => is.map(value) && value.size === 0; +is.nonEmptyMap = (value) => is.map(value) && value.size > 0; +const predicateOnArray = (method, predicate, values) => { + if (!is.function_(predicate)) { + throw new TypeError(`Invalid predicate: ${JSON.stringify(predicate)}`); + } + if (values.length === 0) { + throw new TypeError('Invalid number of values'); + } + return method.call(values, predicate); +}; +is.any = (predicate, ...values) => { + const predicates = is.array(predicate) ? predicate : [predicate]; + return predicates.some(singlePredicate => predicateOnArray(Array.prototype.some, singlePredicate, values)); +}; +is.all = (predicate, ...values) => predicateOnArray(Array.prototype.every, predicate, values); +const assertType = (condition, description, value) => { + if (!condition) { + throw new TypeError(`Expected value which is \`${description}\`, received value of type \`${is(value)}\`.`); + } +}; +exports.assert = { + // Unknowns. + undefined: (value) => assertType(is.undefined(value), 'undefined', value), + string: (value) => assertType(is.string(value), 'string', value), + number: (value) => assertType(is.number(value), 'number', value), + bigint: (value) => assertType(is.bigint(value), 'bigint', value), + // eslint-disable-next-line @typescript-eslint/ban-types + function_: (value) => assertType(is.function_(value), 'Function', value), + null_: (value) => assertType(is.null_(value), 'null', value), + class_: (value) => assertType(is.class_(value), "Class" /* class_ */, value), + boolean: (value) => assertType(is.boolean(value), 'boolean', value), + symbol: (value) => assertType(is.symbol(value), 'symbol', value), + numericString: (value) => assertType(is.numericString(value), "string with a number" /* numericString */, value), + array: (value, assertion) => { + const assert = assertType; + assert(is.array(value), 'Array', value); + if (assertion) { + value.forEach(assertion); + } + }, + buffer: (value) => assertType(is.buffer(value), 'Buffer', value), + nullOrUndefined: (value) => assertType(is.nullOrUndefined(value), "null or undefined" /* nullOrUndefined */, value), + object: (value) => assertType(is.object(value), 'Object', value), + iterable: (value) => assertType(is.iterable(value), "Iterable" /* iterable */, value), + asyncIterable: (value) => assertType(is.asyncIterable(value), "AsyncIterable" /* asyncIterable */, value), + generator: (value) => assertType(is.generator(value), 'Generator', value), + asyncGenerator: (value) => assertType(is.asyncGenerator(value), 'AsyncGenerator', value), + nativePromise: (value) => assertType(is.nativePromise(value), "native Promise" /* nativePromise */, value), + promise: (value) => assertType(is.promise(value), 'Promise', value), + generatorFunction: (value) => assertType(is.generatorFunction(value), 'GeneratorFunction', value), + asyncGeneratorFunction: (value) => assertType(is.asyncGeneratorFunction(value), 'AsyncGeneratorFunction', value), + // eslint-disable-next-line @typescript-eslint/ban-types + asyncFunction: (value) => assertType(is.asyncFunction(value), 'AsyncFunction', value), + // eslint-disable-next-line @typescript-eslint/ban-types + boundFunction: (value) => assertType(is.boundFunction(value), 'Function', value), + regExp: (value) => assertType(is.regExp(value), 'RegExp', value), + date: (value) => assertType(is.date(value), 'Date', value), + error: (value) => assertType(is.error(value), 'Error', value), + map: (value) => assertType(is.map(value), 'Map', value), + set: (value) => assertType(is.set(value), 'Set', value), + weakMap: (value) => assertType(is.weakMap(value), 'WeakMap', value), + weakSet: (value) => assertType(is.weakSet(value), 'WeakSet', value), + int8Array: (value) => assertType(is.int8Array(value), 'Int8Array', value), + uint8Array: (value) => assertType(is.uint8Array(value), 'Uint8Array', value), + uint8ClampedArray: (value) => assertType(is.uint8ClampedArray(value), 'Uint8ClampedArray', value), + int16Array: (value) => assertType(is.int16Array(value), 'Int16Array', value), + uint16Array: (value) => assertType(is.uint16Array(value), 'Uint16Array', value), + int32Array: (value) => assertType(is.int32Array(value), 'Int32Array', value), + uint32Array: (value) => assertType(is.uint32Array(value), 'Uint32Array', value), + float32Array: (value) => assertType(is.float32Array(value), 'Float32Array', value), + float64Array: (value) => assertType(is.float64Array(value), 'Float64Array', value), + bigInt64Array: (value) => assertType(is.bigInt64Array(value), 'BigInt64Array', value), + bigUint64Array: (value) => assertType(is.bigUint64Array(value), 'BigUint64Array', value), + arrayBuffer: (value) => assertType(is.arrayBuffer(value), 'ArrayBuffer', value), + sharedArrayBuffer: (value) => assertType(is.sharedArrayBuffer(value), 'SharedArrayBuffer', value), + dataView: (value) => assertType(is.dataView(value), 'DataView', value), + urlInstance: (value) => assertType(is.urlInstance(value), 'URL', value), + urlString: (value) => assertType(is.urlString(value), "string with a URL" /* urlString */, value), + truthy: (value) => assertType(is.truthy(value), "truthy" /* truthy */, value), + falsy: (value) => assertType(is.falsy(value), "falsy" /* falsy */, value), + nan: (value) => assertType(is.nan(value), "NaN" /* nan */, value), + primitive: (value) => assertType(is.primitive(value), "primitive" /* primitive */, value), + integer: (value) => assertType(is.integer(value), "integer" /* integer */, value), + safeInteger: (value) => assertType(is.safeInteger(value), "integer" /* safeInteger */, value), + plainObject: (value) => assertType(is.plainObject(value), "plain object" /* plainObject */, value), + typedArray: (value) => assertType(is.typedArray(value), "TypedArray" /* typedArray */, value), + arrayLike: (value) => assertType(is.arrayLike(value), "array-like" /* arrayLike */, value), + domElement: (value) => assertType(is.domElement(value), "HTMLElement" /* domElement */, value), + observable: (value) => assertType(is.observable(value), 'Observable', value), + nodeStream: (value) => assertType(is.nodeStream(value), "Node.js Stream" /* nodeStream */, value), + infinite: (value) => assertType(is.infinite(value), "infinite number" /* infinite */, value), + emptyArray: (value) => assertType(is.emptyArray(value), "empty array" /* emptyArray */, value), + nonEmptyArray: (value) => assertType(is.nonEmptyArray(value), "non-empty array" /* nonEmptyArray */, value), + emptyString: (value) => assertType(is.emptyString(value), "empty string" /* emptyString */, value), + nonEmptyString: (value) => assertType(is.nonEmptyString(value), "non-empty string" /* nonEmptyString */, value), + emptyStringOrWhitespace: (value) => assertType(is.emptyStringOrWhitespace(value), "empty string or whitespace" /* emptyStringOrWhitespace */, value), + emptyObject: (value) => assertType(is.emptyObject(value), "empty object" /* emptyObject */, value), + nonEmptyObject: (value) => assertType(is.nonEmptyObject(value), "non-empty object" /* nonEmptyObject */, value), + emptySet: (value) => assertType(is.emptySet(value), "empty set" /* emptySet */, value), + nonEmptySet: (value) => assertType(is.nonEmptySet(value), "non-empty set" /* nonEmptySet */, value), + emptyMap: (value) => assertType(is.emptyMap(value), "empty map" /* emptyMap */, value), + nonEmptyMap: (value) => assertType(is.nonEmptyMap(value), "non-empty map" /* nonEmptyMap */, value), + // Numbers. + evenInteger: (value) => assertType(is.evenInteger(value), "even integer" /* evenInteger */, value), + oddInteger: (value) => assertType(is.oddInteger(value), "odd integer" /* oddInteger */, value), + // Two arguments. + directInstanceOf: (instance, class_) => assertType(is.directInstanceOf(instance, class_), "T" /* directInstanceOf */, instance), + inRange: (value, range) => assertType(is.inRange(value, range), "in range" /* inRange */, value), + // Variadic functions. + any: (predicate, ...values) => assertType(is.any(predicate, ...values), "predicate returns truthy for any value" /* any */, values), + all: (predicate, ...values) => assertType(is.all(predicate, ...values), "predicate returns truthy for all values" /* all */, values) +}; +// Some few keywords are reserved, but we'll populate them for Node.js users +// See https://github.com/Microsoft/TypeScript/issues/2536 +Object.defineProperties(is, { + class: { + value: is.class_ + }, + function: { + value: is.function_ + }, + null: { + value: is.null_ + } +}); +Object.defineProperties(exports.assert, { + class: { + value: exports.assert.class_ + }, + function: { + value: exports.assert.function_ + }, + null: { + value: exports.assert.null_ + } +}); +exports.default = is; +// For CommonJS default export support +module.exports = is; +module.exports.default = is; +module.exports.assert = exports.assert; + + +/***/ }), + +/***/ 537: +/***/ (function(module) { + +"use strict"; + + +// We define these manually to ensure they're always copied +// even if they would move up the prototype chain +// https://nodejs.org/api/http.html#http_class_http_incomingmessage +const knownProperties = [ + 'aborted', + 'complete', + 'headers', + 'httpVersion', + 'httpVersionMinor', + 'httpVersionMajor', + 'method', + 'rawHeaders', + 'rawTrailers', + 'setTimeout', + 'socket', + 'statusCode', + 'statusMessage', + 'trailers', + 'url' +]; + +module.exports = (fromStream, toStream) => { + if (toStream._readableState.autoDestroy) { + throw new Error('The second stream must have the `autoDestroy` option set to `false`'); + } + + const fromProperties = new Set(Object.keys(fromStream).concat(knownProperties)); + + const properties = {}; + + for (const property of fromProperties) { + // Don't overwrite existing properties. + if (property in toStream) { + continue; + } + + properties[property] = { + get() { + const value = fromStream[property]; + const isFunction = typeof value === 'function'; + + return isFunction ? value.bind(fromStream) : value; + }, + set(value) { + fromStream[property] = value; + }, + enumerable: true, + configurable: false + }; + } + + Object.defineProperties(toStream, properties); + + fromStream.once('aborted', () => { + toStream.destroy(); + + toStream.emit('aborted'); + }); + + fromStream.once('close', () => { + if (fromStream.complete) { + if (toStream.readable) { + toStream.once('end', () => { + toStream.emit('close'); + }); + } else { + toStream.emit('close'); + } + } else { + toStream.emit('close'); + } + }); + + return toStream; +}; + + +/***/ }), + +/***/ 557: +/***/ (function(module) { + +"use strict"; + + +class CancelError extends Error { + constructor(reason) { + super(reason || 'Promise was canceled'); + this.name = 'CancelError'; + } + + get isCanceled() { + return true; + } +} + +class PCancelable { + static fn(userFn) { + return (...arguments_) => { + return new PCancelable((resolve, reject, onCancel) => { + arguments_.push(onCancel); + // eslint-disable-next-line promise/prefer-await-to-then + userFn(...arguments_).then(resolve, reject); + }); + }; + } + + constructor(executor) { + this._cancelHandlers = []; + this._isPending = true; + this._isCanceled = false; + this._rejectOnCancel = true; + + this._promise = new Promise((resolve, reject) => { + this._reject = reject; + + const onResolve = value => { + this._isPending = false; + resolve(value); + }; + + const onReject = error => { + this._isPending = false; + reject(error); + }; + + const onCancel = handler => { + if (!this._isPending) { + throw new Error('The `onCancel` handler was attached after the promise settled.'); + } + + this._cancelHandlers.push(handler); + }; + + Object.defineProperties(onCancel, { + shouldReject: { + get: () => this._rejectOnCancel, + set: boolean => { + this._rejectOnCancel = boolean; + } + } + }); + + return executor(onResolve, onReject, onCancel); + }); + } + + then(onFulfilled, onRejected) { + // eslint-disable-next-line promise/prefer-await-to-then + return this._promise.then(onFulfilled, onRejected); + } + + catch(onRejected) { + return this._promise.catch(onRejected); + } + + finally(onFinally) { + return this._promise.finally(onFinally); + } + + cancel(reason) { + if (!this._isPending || this._isCanceled) { + return; + } + + if (this._cancelHandlers.length > 0) { + try { + for (const handler of this._cancelHandlers) { + handler(); + } + } catch (error) { + this._reject(error); + } + } + + this._isCanceled = true; + if (this._rejectOnCancel) { + this._reject(new CancelError(reason)); + } + } + + get isCanceled() { + return this._isCanceled; + } +} + +Object.setPrototypeOf(PCancelable.prototype, Promise.prototype); + +module.exports = PCancelable; +module.exports.CancelError = CancelError; + + +/***/ }), + +/***/ 565: +/***/ (function(module) { + +module.exports = require("http2"); + +/***/ }), + +/***/ 570: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + +const { + V4MAPPED, + ADDRCONFIG, + ALL, + promises: { + Resolver: AsyncResolver + }, + lookup: dnsLookup +} = __webpack_require__(881); +const {promisify} = __webpack_require__(669); +const os = __webpack_require__(87); + +const kCacheableLookupCreateConnection = Symbol('cacheableLookupCreateConnection'); +const kCacheableLookupInstance = Symbol('cacheableLookupInstance'); +const kExpires = Symbol('expires'); + +const supportsALL = typeof ALL === 'number'; + +const verifyAgent = agent => { + if (!(agent && typeof agent.createConnection === 'function')) { + throw new Error('Expected an Agent instance as the first argument'); + } +}; + +const map4to6 = entries => { + for (const entry of entries) { + if (entry.family === 6) { + continue; + } + + entry.address = `::ffff:${entry.address}`; + entry.family = 6; + } +}; + +const getIfaceInfo = () => { + let has4 = false; + let has6 = false; + + for (const device of Object.values(os.networkInterfaces())) { + for (const iface of device) { + if (iface.internal) { + continue; + } + + if (iface.family === 'IPv6') { + has6 = true; + } else { + has4 = true; + } + + if (has4 && has6) { + return {has4, has6}; + } + } + } + + return {has4, has6}; +}; + +const isIterable = map => { + return Symbol.iterator in map; +}; + +const ttl = {ttl: true}; +const all = {all: true}; + +class CacheableLookup { + constructor({ + cache = new Map(), + maxTtl = Infinity, + fallbackDuration = 3600, + errorTtl = 0.15, + resolver = new AsyncResolver(), + lookup = dnsLookup + } = {}) { + this.maxTtl = maxTtl; + this.errorTtl = errorTtl; + + this._cache = cache; + this._resolver = resolver; + this._dnsLookup = promisify(lookup); + + if (this._resolver instanceof AsyncResolver) { + this._resolve4 = this._resolver.resolve4.bind(this._resolver); + this._resolve6 = this._resolver.resolve6.bind(this._resolver); + } else { + this._resolve4 = promisify(this._resolver.resolve4.bind(this._resolver)); + this._resolve6 = promisify(this._resolver.resolve6.bind(this._resolver)); + } + + this._iface = getIfaceInfo(); + + this._pending = {}; + this._nextRemovalTime = false; + this._hostnamesToFallback = new Set(); + + if (fallbackDuration < 1) { + this._fallback = false; + } else { + this._fallback = true; + + const interval = setInterval(() => { + this._hostnamesToFallback.clear(); + }, fallbackDuration * 1000); + + /* istanbul ignore next: There is no `interval.unref()` when running inside an Electron renderer */ + if (interval.unref) { + interval.unref(); + } + } + + this.lookup = this.lookup.bind(this); + this.lookupAsync = this.lookupAsync.bind(this); + } + + set servers(servers) { + this.clear(); + + this._resolver.setServers(servers); + } + + get servers() { + return this._resolver.getServers(); + } + + lookup(hostname, options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } else if (typeof options === 'number') { + options = { + family: options + }; + } + + if (!callback) { + throw new Error('Callback must be a function.'); + } + + // eslint-disable-next-line promise/prefer-await-to-then + this.lookupAsync(hostname, options).then(result => { + if (options.all) { + callback(null, result); + } else { + callback(null, result.address, result.family, result.expires, result.ttl); + } + }, callback); + } + + async lookupAsync(hostname, options = {}) { + if (typeof options === 'number') { + options = { + family: options + }; + } + + let cached = await this.query(hostname); + + if (options.family === 6) { + const filtered = cached.filter(entry => entry.family === 6); + + if (options.hints & V4MAPPED) { + if ((supportsALL && options.hints & ALL) || filtered.length === 0) { + map4to6(cached); + } else { + cached = filtered; + } + } else { + cached = filtered; + } + } else if (options.family === 4) { + cached = cached.filter(entry => entry.family === 4); + } + + if (options.hints & ADDRCONFIG) { + const {_iface} = this; + cached = cached.filter(entry => entry.family === 6 ? _iface.has6 : _iface.has4); + } + + if (cached.length === 0) { + const error = new Error(`cacheableLookup ENOTFOUND ${hostname}`); + error.code = 'ENOTFOUND'; + error.hostname = hostname; + + throw error; + } + + if (options.all) { + return cached; + } + + return cached[0]; + } + + async query(hostname) { + let cached = await this._cache.get(hostname); + + if (!cached) { + const pending = this._pending[hostname]; + + if (pending) { + cached = await pending; + } else { + const newPromise = this.queryAndCache(hostname); + this._pending[hostname] = newPromise; + + cached = await newPromise; + } + } + + cached = cached.map(entry => { + return {...entry}; + }); + + return cached; + } + + async _resolve(hostname) { + const wrap = async promise => { + try { + return await promise; + } catch (error) { + if (error.code === 'ENODATA' || error.code === 'ENOTFOUND') { + return []; + } + + throw error; + } + }; + + // ANY is unsafe as it doesn't trigger new queries in the underlying server. + const [A, AAAA] = await Promise.all([ + this._resolve4(hostname, ttl), + this._resolve6(hostname, ttl) + ].map(promise => wrap(promise))); + + let aTtl = 0; + let aaaaTtl = 0; + let cacheTtl = 0; + + const now = Date.now(); + + for (const entry of A) { + entry.family = 4; + entry.expires = now + (entry.ttl * 1000); + + aTtl = Math.max(aTtl, entry.ttl); + } + + for (const entry of AAAA) { + entry.family = 6; + entry.expires = now + (entry.ttl * 1000); + + aaaaTtl = Math.max(aaaaTtl, entry.ttl); + } + + if (A.length > 0) { + if (AAAA.length > 0) { + cacheTtl = Math.min(aTtl, aaaaTtl); + } else { + cacheTtl = aTtl; + } + } else { + cacheTtl = aaaaTtl; + } + + return { + entries: [ + ...A, + ...AAAA + ], + cacheTtl + }; + } + + async _lookup(hostname) { + try { + const entries = await this._dnsLookup(hostname, { + all: true + }); + + return { + entries, + cacheTtl: 0 + }; + } catch (_) { + return { + entries: [], + cacheTtl: 0 + }; + } + } + + async _set(hostname, data, cacheTtl) { + if (this.maxTtl > 0 && cacheTtl > 0) { + cacheTtl = Math.min(cacheTtl, this.maxTtl) * 1000; + data[kExpires] = Date.now() + cacheTtl; + + try { + await this._cache.set(hostname, data, cacheTtl); + } catch (error) { + this.lookupAsync = async () => { + const cacheError = new Error('Cache Error. Please recreate the CacheableLookup instance.'); + cacheError.cause = error; + + throw cacheError; + }; + } + + if (isIterable(this._cache)) { + this._tick(cacheTtl); + } + } + } + + async queryAndCache(hostname) { + if (this._hostnamesToFallback.has(hostname)) { + return this._dnsLookup(hostname, all); + } + + try { + let query = await this._resolve(hostname); + + if (query.entries.length === 0 && this._fallback) { + query = await this._lookup(hostname); + + if (query.entries.length !== 0) { + // Use `dns.lookup(...)` for that particular hostname + this._hostnamesToFallback.add(hostname); + } + } + + const cacheTtl = query.entries.length === 0 ? this.errorTtl : query.cacheTtl; + await this._set(hostname, query.entries, cacheTtl); + + delete this._pending[hostname]; + + return query.entries; + } catch (error) { + delete this._pending[hostname]; + + throw error; + } + } + + _tick(ms) { + const nextRemovalTime = this._nextRemovalTime; + + if (!nextRemovalTime || ms < nextRemovalTime) { + clearTimeout(this._removalTimeout); + + this._nextRemovalTime = ms; + + this._removalTimeout = setTimeout(() => { + this._nextRemovalTime = false; + + let nextExpiry = Infinity; + + const now = Date.now(); + + for (const [hostname, entries] of this._cache) { + const expires = entries[kExpires]; + + if (now >= expires) { + this._cache.delete(hostname); + } else if (expires < nextExpiry) { + nextExpiry = expires; + } + } + + if (nextExpiry !== Infinity) { + this._tick(nextExpiry - now); + } + }, ms); + + /* istanbul ignore next: There is no `timeout.unref()` when running inside an Electron renderer */ + if (this._removalTimeout.unref) { + this._removalTimeout.unref(); + } + } + } + + install(agent) { + verifyAgent(agent); + + if (kCacheableLookupCreateConnection in agent) { + throw new Error('CacheableLookup has been already installed'); + } + + agent[kCacheableLookupCreateConnection] = agent.createConnection; + agent[kCacheableLookupInstance] = this; + + agent.createConnection = (options, callback) => { + if (!('lookup' in options)) { + options.lookup = this.lookup; + } + + return agent[kCacheableLookupCreateConnection](options, callback); + }; + } + + uninstall(agent) { + verifyAgent(agent); + + if (agent[kCacheableLookupCreateConnection]) { + if (agent[kCacheableLookupInstance] !== this) { + throw new Error('The agent is not owned by this CacheableLookup instance'); + } + + agent.createConnection = agent[kCacheableLookupCreateConnection]; + + delete agent[kCacheableLookupCreateConnection]; + delete agent[kCacheableLookupInstance]; + } + } + + updateInterfaceInfo() { + const {_iface} = this; + + this._iface = getIfaceInfo(); + + if ((_iface.has4 && !this._iface.has4) || (_iface.has6 && !this._iface.has6)) { + this._cache.clear(); + } + } + + clear(hostname) { + if (hostname) { + this._cache.delete(hostname); + return; + } + + this._cache.clear(); + } +} + +module.exports = CacheableLookup; +module.exports.default = CacheableLookup; + + +/***/ }), + +/***/ 577: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PromisableRequest = void 0; +const events_1 = __webpack_require__(614); +const PCancelable = __webpack_require__(557); +const calculate_retry_delay_1 = __webpack_require__(927); +const types_1 = __webpack_require__(36); +const core_1 = __webpack_require__(468); +exports.PromisableRequest = core_1.default; +const proxy_events_1 = __webpack_require__(628); +const get_buffer_1 = __webpack_require__(452); +const proxiedRequestEvents = [ + 'request', + 'response', + 'redirect', + 'uploadProgress', + 'downloadProgress' +]; +function asPromise(options) { + let retryCount = 0; + let globalRequest; + let globalResponse; + const emitter = new events_1.EventEmitter(); + const promise = new PCancelable((resolve, _reject, onCancel) => { + const makeRequest = () => { + // Support retries + // `options.throwHttpErrors` needs to be always true, + // so the HTTP errors are caught and the request is retried. + // The error is **eventually** thrown if the user value is true. + const { throwHttpErrors } = options; + if (!throwHttpErrors) { + options.throwHttpErrors = true; + } + // Note from @szmarczak: I think we should use `request.options` instead of the local options + const request = new core_1.default(options.url, options); + request._noPipe = true; + onCancel(() => request.destroy()); + const reject = (error) => { + void (async () => { + try { + for (const hook of options.hooks.beforeError) { + // eslint-disable-next-line no-await-in-loop + error = await hook(error); + } + } + catch (error_) { + _reject(new types_1.RequestError(error_.message, error_, request)); + return; + } + _reject(error); + })(); + }; + globalRequest = request; + const onResponse = async (response) => { + var _a; + response.retryCount = retryCount; + if (response.request.aborted) { + // Canceled while downloading - will throw a `CancelError` or `TimeoutError` error + return; + } + const isOk = () => { + const { statusCode } = response; + const limitStatusCode = options.followRedirect ? 299 : 399; + return (statusCode >= 200 && statusCode <= limitStatusCode) || statusCode === 304; + }; + // Download body + let rawBody; + try { + rawBody = await get_buffer_1.default(request); + response.rawBody = rawBody; + } + catch (_b) { + // The same error is caught below. + // See request.once('error') + return; + } + // Parse body + const contentEncoding = ((_a = response.headers['content-encoding']) !== null && _a !== void 0 ? _a : '').toLowerCase(); + const isCompressed = ['gzip', 'deflate', 'br'].includes(contentEncoding); + if (isCompressed && !options.decompress) { + response.body = rawBody; + } + else { + try { + response.body = core_1.parseBody(response, options.responseType, options.parseJson, options.encoding); + } + catch (error) { + // Fallback to `utf8` + response.body = rawBody.toString(); + if (isOk()) { + // TODO: Call `request._beforeError`, see https://github.com/nodejs/node/issues/32995 + reject(error); + return; + } + } + } + try { + for (const [index, hook] of options.hooks.afterResponse.entries()) { + // @ts-expect-error TS doesn't notice that CancelableRequest is a Promise + // eslint-disable-next-line no-await-in-loop + response = await hook(response, async (updatedOptions) => { + const typedOptions = core_1.default.normalizeArguments(undefined, { + ...updatedOptions, + retry: { + calculateDelay: () => 0 + }, + throwHttpErrors: false, + resolveBodyOnly: false + }, options); + // Remove any further hooks for that request, because we'll call them anyway. + // The loop continues. We don't want duplicates (asPromise recursion). + typedOptions.hooks.afterResponse = typedOptions.hooks.afterResponse.slice(0, index); + for (const hook of typedOptions.hooks.beforeRetry) { + // eslint-disable-next-line no-await-in-loop + await hook(typedOptions); + } + const promise = asPromise(typedOptions); + onCancel(() => { + promise.catch(() => { }); + promise.cancel(); + }); + return promise; + }); + } + } + catch (error) { + // TODO: Call `request._beforeError`, see https://github.com/nodejs/node/issues/32995 + reject(new types_1.RequestError(error.message, error, request)); + return; + } + if (throwHttpErrors && !isOk()) { + reject(new types_1.HTTPError(response)); + return; + } + globalResponse = response; + resolve(options.resolveBodyOnly ? response.body : response); + }; + const onError = async (error) => { + if (promise.isCanceled) { + return; + } + if (!request.options) { + reject(error); + return; + } + request.off('response', onResponse); + let gotUnexpectedError = false; + const onUnexpectedError = (error) => { + gotUnexpectedError = true; + reject(error); + }; + // If this is an HTTP error, then it can throw again with `ECONNRESET` or `Parse Error` + request.once('error', onUnexpectedError); + let backoff; + retryCount++; + try { + backoff = await options.retry.calculateDelay({ + attemptCount: retryCount, + retryOptions: options.retry, + error, + computedValue: calculate_retry_delay_1.default({ + attemptCount: retryCount, + retryOptions: options.retry, + error, + computedValue: 0 + }) + }); + } + catch (error_) { + // Don't emit the `response` event + request.destroy(); + reject(new types_1.RequestError(error_.message, error, request)); + return; + } + // Another error was thrown already + if (gotUnexpectedError) { + return; + } + request.off('error', onUnexpectedError); + if (backoff) { + // Don't emit the `response` event + request.destroy(); + const retry = async () => { + options.throwHttpErrors = throwHttpErrors; + try { + for (const hook of options.hooks.beforeRetry) { + // eslint-disable-next-line no-await-in-loop + await hook(options, error, retryCount); + } + } + catch (error_) { + // Don't emit the `response` event + request.destroy(); + reject(new types_1.RequestError(error_.message, error, request)); + return; + } + makeRequest(); + }; + setTimeout(retry, backoff); + return; + } + // The retry has not been made + retryCount--; + if (error instanceof types_1.HTTPError) { + // The error will be handled by the `response` event + void onResponse(request._response); + // Reattach the error handler, because there may be a timeout later. + request.once('error', onError); + return; + } + // Don't emit the `response` event + request.destroy(); + reject(error); + }; + request.once('response', onResponse); + request.once('error', onError); + proxy_events_1.default(request, emitter, proxiedRequestEvents); + }; + makeRequest(); + }); + promise.on = (event, fn) => { + emitter.on(event, fn); + return promise; + }; + const shortcut = (responseType) => { + const newPromise = (async () => { + // Wait until downloading has ended + await promise; + return core_1.parseBody(globalResponse, responseType, options.parseJson, options.encoding); + })(); + Object.defineProperties(newPromise, Object.getOwnPropertyDescriptors(promise)); + return newPromise; + }; + promise.json = () => { + if (!globalRequest.writableFinished && options.headers.accept === undefined) { + options.headers.accept = 'application/json'; + } + return shortcut('json'); + }; + promise.buffer = () => shortcut('buffer'); + promise.text = () => shortcut('text'); + return promise; +} +exports.default = asPromise; +__exportStar(__webpack_require__(36), exports); + + +/***/ }), + +/***/ 605: +/***/ (function(module) { + +module.exports = require("http"); + +/***/ }), + +/***/ 614: +/***/ (function(module) { + +module.exports = require("events"); + +/***/ }), + +/***/ 622: +/***/ (function(module) { + +module.exports = require("path"); + +/***/ }), + +/***/ 628: +/***/ (function(__unusedmodule, exports) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +function default_1(from, to, events) { + const fns = {}; + for (const event of events) { + fns[event] = (...args) => { + to.emit(event, ...args); + }; + from.on(event, fns[event]); + } + return () => { + for (const event of events) { + from.off(event, fns[event]); + } + }; +} +exports.default = default_1; + + +/***/ }), + +/***/ 631: +/***/ (function(module) { + +module.exports = require("net"); + +/***/ }), + +/***/ 669: +/***/ (function(module) { + +module.exports = require("util"); + +/***/ }), + +/***/ 676: +/***/ (function(module, __unusedexports, __webpack_require__) { + +const auth = __webpack_require__(151); +const secrets = __webpack_require__(461); + +module.exports = { + auth, + secrets +}; + +/***/ }), + +/***/ 699: +/***/ (function(module) { + +"use strict"; + +/* istanbul ignore file: https://github.com/nodejs/node/blob/master/lib/internal/errors.js */ + +const makeError = (Base, key, getMessage) => { + module.exports[key] = class NodeError extends Base { + constructor(...args) { + super(typeof getMessage === 'string' ? getMessage : getMessage(args)); + this.name = `${super.name} [${key}]`; + this.code = key; + } + }; +}; + +makeError(TypeError, 'ERR_INVALID_ARG_TYPE', args => { + const type = args[0].includes('.') ? 'property' : 'argument'; + + let valid = args[1]; + const isManyTypes = Array.isArray(valid); + + if (isManyTypes) { + valid = `${valid.slice(0, -1).join(', ')} or ${valid.slice(-1)}`; + } + + return `The "${args[0]}" ${type} must be ${isManyTypes ? 'one of' : 'of'} type ${valid}. Received ${typeof args[2]}`; +}); + +makeError(TypeError, 'ERR_INVALID_PROTOCOL', args => { + return `Protocol "${args[0]}" not supported. Expected "${args[1]}"`; +}); + +makeError(Error, 'ERR_HTTP_HEADERS_SENT', args => { + return `Cannot ${args[0]} headers after they are sent to the client`; +}); + +makeError(TypeError, 'ERR_INVALID_HTTP_TOKEN', args => { + return `${args[0]} must be a valid HTTP token [${args[1]}]`; +}); + +makeError(TypeError, 'ERR_HTTP_INVALID_HEADER_VALUE', args => { + return `Invalid value "${args[0]} for header "${args[1]}"`; +}); + +makeError(TypeError, 'ERR_INVALID_CHAR', args => { + return `Invalid character in ${args[0]} [${args[1]}]`; +}); + + +/***/ }), + +/***/ 723: +/***/ (function(module) { + +"use strict"; + + +module.exports = header => { + switch (header) { + case ':method': + case ':scheme': + case ':authority': + case ':path': + return true; + default: + return false; + } +}; + + +/***/ }), + +/***/ 738: +/***/ (function(__unusedmodule, exports) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.dnsLookupIpVersionToFamily = exports.isDnsLookupIpVersion = void 0; +const conversionTable = { + auto: 0, + ipv4: 4, + ipv6: 6 +}; +exports.isDnsLookupIpVersion = (value) => { + return value in conversionTable; +}; +exports.dnsLookupIpVersionToFamily = (dnsLookupIpVersion) => { + if (exports.isDnsLookupIpVersion(dnsLookupIpVersion)) { + return conversionTable[dnsLookupIpVersion]; + } + throw new Error('Invalid DNS lookup IP version'); +}; + + +/***/ }), + +/***/ 747: +/***/ (function(module) { + +module.exports = require("fs"); + +/***/ }), + +/***/ 750: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + +const {Readable} = __webpack_require__(413); + +class IncomingMessage extends Readable { + constructor(socket, highWaterMark) { + super({ + highWaterMark, + autoDestroy: false + }); + + this.statusCode = null; + this.statusMessage = ''; + this.httpVersion = '2.0'; + this.httpVersionMajor = 2; + this.httpVersionMinor = 0; + this.headers = {}; + this.trailers = {}; + this.req = null; + + this.aborted = false; + this.complete = false; + this.upgrade = null; + + this.rawHeaders = []; + this.rawTrailers = []; + + this.socket = socket; + this.connection = socket; + + this._dumped = false; + } + + _destroy(error) { + this.req._request.destroy(error); + } + + setTimeout(ms, callback) { + this.req.setTimeout(ms, callback); + return this; + } + + _dump() { + if (!this._dumped) { + this._dumped = true; + + this.removeAllListeners('data'); + this.resume(); + } + } + + _read() { + if (this.req) { + this.req._request.resume(); + } + } +} + +module.exports = IncomingMessage; + + +/***/ }), + +/***/ 751: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + +const net = __webpack_require__(631); +/* istanbul ignore file: https://github.com/nodejs/node/blob/v13.0.1/lib/_http_agent.js */ + +module.exports = options => { + let servername = options.host; + const hostHeader = options.headers && options.headers.host; + + if (hostHeader) { + if (hostHeader.startsWith('[')) { + const index = hostHeader.indexOf(']'); + if (index === -1) { + servername = hostHeader; + } else { + servername = hostHeader.slice(1, -1); + } + } else { + servername = hostHeader.split(':', 1)[0]; + } + } + + if (net.isIP(servername)) { + return ''; + } + + return servername; +}; + + +/***/ }), + +/***/ 761: +/***/ (function(module) { + +module.exports = require("zlib"); + +/***/ }), + +/***/ 784: +/***/ (function(__unusedmodule, exports) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +// When attaching listeners, it's very easy to forget about them. +// Especially if you do error handling and set timeouts. +// So instead of checking if it's proper to throw an error on every timeout ever, +// use this simple tool which will remove all listeners you have attached. +exports.default = () => { + const handlers = []; + return { + once(origin, event, fn) { + origin.once(event, fn); + handlers.push({ origin, event, fn }); + }, + unhandleAll() { + for (const handler of handlers) { + const { origin, event, fn } = handler; + origin.removeListener(event, fn); + } + handlers.length = 0; + } + }; +}; + + +/***/ }), + +/***/ 786: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fs_1 = __webpack_require__(747); +const util_1 = __webpack_require__(669); +const is_1 = __webpack_require__(534); +const is_form_data_1 = __webpack_require__(460); +const statAsync = util_1.promisify(fs_1.stat); +exports.default = async (body, headers) => { + if (headers && 'content-length' in headers) { + return Number(headers['content-length']); + } + if (!body) { + return 0; + } + if (is_1.default.string(body)) { + return Buffer.byteLength(body); + } + if (is_1.default.buffer(body)) { + return body.length; + } + if (is_form_data_1.default(body)) { + return util_1.promisify(body.getLength.bind(body))(); + } + if (body instanceof fs_1.ReadStream) { + const { size } = await statAsync(body.path); + return size; + } + return undefined; +}; + + +/***/ }), + +/***/ 790: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const tls_1 = __webpack_require__(16); +const deferToConnect = (socket, fn) => { + let listeners; + if (typeof fn === 'function') { + const connect = fn; + listeners = { connect }; + } + else { + listeners = fn; + } + const hasConnectListener = typeof listeners.connect === 'function'; + const hasSecureConnectListener = typeof listeners.secureConnect === 'function'; + const hasCloseListener = typeof listeners.close === 'function'; + const onConnect = () => { + if (hasConnectListener) { + listeners.connect(); + } + if (socket instanceof tls_1.TLSSocket && hasSecureConnectListener) { + if (socket.authorized) { + listeners.secureConnect(); + } + else if (!socket.authorizationError) { + socket.once('secureConnect', listeners.secureConnect); + } + } + if (hasCloseListener) { + socket.once('close', listeners.close); + } + }; + if (socket.writable && !socket.connecting) { + onConnect(); + } + else if (socket.connecting) { + socket.once('connect', onConnect); + } + else if (socket.destroyed && hasCloseListener) { + listeners.close(socket._hadError); + } +}; +exports.default = deferToConnect; +// For CommonJS default export support +module.exports = deferToConnect; +module.exports.default = deferToConnect; + + +/***/ }), + +/***/ 811: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TimeoutError = void 0; +const net = __webpack_require__(631); +const unhandle_1 = __webpack_require__(784); +const reentry = Symbol('reentry'); +const noop = () => { }; +class TimeoutError extends Error { + constructor(threshold, event) { + super(`Timeout awaiting '${event}' for ${threshold}ms`); + this.event = event; + this.name = 'TimeoutError'; + this.code = 'ETIMEDOUT'; + } +} +exports.TimeoutError = TimeoutError; +exports.default = (request, delays, options) => { + if (reentry in request) { + return noop; + } + request[reentry] = true; + const cancelers = []; + const { once, unhandleAll } = unhandle_1.default(); + const addTimeout = (delay, callback, event) => { + var _a; + const timeout = setTimeout(callback, delay, delay, event); + (_a = timeout.unref) === null || _a === void 0 ? void 0 : _a.call(timeout); + const cancel = () => { + clearTimeout(timeout); + }; + cancelers.push(cancel); + return cancel; + }; + const { host, hostname } = options; + const timeoutHandler = (delay, event) => { + request.destroy(new TimeoutError(delay, event)); + }; + const cancelTimeouts = () => { + for (const cancel of cancelers) { + cancel(); + } + unhandleAll(); + }; + request.once('error', error => { + cancelTimeouts(); + // Save original behavior + /* istanbul ignore next */ + if (request.listenerCount('error') === 0) { + throw error; + } + }); + request.once('close', cancelTimeouts); + once(request, 'response', (response) => { + once(response, 'end', cancelTimeouts); + }); + if (typeof delays.request !== 'undefined') { + addTimeout(delays.request, timeoutHandler, 'request'); + } + if (typeof delays.socket !== 'undefined') { + const socketTimeoutHandler = () => { + timeoutHandler(delays.socket, 'socket'); + }; + request.setTimeout(delays.socket, socketTimeoutHandler); + // `request.setTimeout(0)` causes a memory leak. + // We can just remove the listener and forget about the timer - it's unreffed. + // See https://github.com/sindresorhus/got/issues/690 + cancelers.push(() => { + request.removeListener('timeout', socketTimeoutHandler); + }); + } + once(request, 'socket', (socket) => { + var _a; + const { socketPath } = request; + /* istanbul ignore next: hard to test */ + if (socket.connecting) { + const hasPath = Boolean(socketPath !== null && socketPath !== void 0 ? socketPath : net.isIP((_a = hostname !== null && hostname !== void 0 ? hostname : host) !== null && _a !== void 0 ? _a : '') !== 0); + if (typeof delays.lookup !== 'undefined' && !hasPath && typeof socket.address().address === 'undefined') { + const cancelTimeout = addTimeout(delays.lookup, timeoutHandler, 'lookup'); + once(socket, 'lookup', cancelTimeout); + } + if (typeof delays.connect !== 'undefined') { + const timeConnect = () => addTimeout(delays.connect, timeoutHandler, 'connect'); + if (hasPath) { + once(socket, 'connect', timeConnect()); + } + else { + once(socket, 'lookup', (error) => { + if (error === null) { + once(socket, 'connect', timeConnect()); + } + }); + } + } + if (typeof delays.secureConnect !== 'undefined' && options.protocol === 'https:') { + once(socket, 'connect', () => { + const cancelTimeout = addTimeout(delays.secureConnect, timeoutHandler, 'secureConnect'); + once(socket, 'secureConnect', cancelTimeout); + }); + } + } + if (typeof delays.send !== 'undefined') { + const timeRequest = () => addTimeout(delays.send, timeoutHandler, 'send'); + /* istanbul ignore next: hard to test */ + if (socket.connecting) { + once(socket, 'connect', () => { + once(request, 'upload-complete', timeRequest()); + }); + } + else { + once(request, 'upload-complete', timeRequest()); + } + } + }); + if (typeof delays.response !== 'undefined') { + once(request, 'upload-complete', () => { + const cancelTimeout = addTimeout(delays.response, timeoutHandler, 'response'); + once(request, 'response', cancelTimeout); + }); + } + return cancelTimeouts; +}; + + +/***/ }), + +/***/ 835: +/***/ (function(module) { + +module.exports = require("url"); + +/***/ }), + +/***/ 839: +/***/ (function(__unusedmodule, exports) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); + + +/***/ }), + +/***/ 861: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + +const {Transform, PassThrough} = __webpack_require__(413); +const zlib = __webpack_require__(761); +const mimicResponse = __webpack_require__(537); + +module.exports = response => { + const contentEncoding = (response.headers['content-encoding'] || '').toLowerCase(); + + if (!['gzip', 'deflate', 'br'].includes(contentEncoding)) { + return response; + } + + // TODO: Remove this when targeting Node.js 12. + const isBrotli = contentEncoding === 'br'; + if (isBrotli && typeof zlib.createBrotliDecompress !== 'function') { + response.destroy(new Error('Brotli is not supported on Node.js < 12')); + return response; + } + + let isEmpty = true; + + const checker = new Transform({ + transform(data, _encoding, callback) { + isEmpty = false; + + callback(null, data); + }, + + flush(callback) { + callback(); + } + }); + + const finalStream = new PassThrough({ + autoDestroy: false, + destroy(error, callback) { + response.destroy(); + + callback(error); + } + }); + + const decompressStream = isBrotli ? zlib.createBrotliDecompress() : zlib.createUnzip(); + + decompressStream.once('error', error => { + if (isEmpty && !response.readable) { + finalStream.end(); + return; + } + + finalStream.destroy(error); + }); + + mimicResponse(response, finalStream); + response.pipe(checker).pipe(decompressStream).pipe(finalStream); + + return finalStream; +}; + + +/***/ }), + +/***/ 881: +/***/ (function(module) { + +module.exports = require("dns"); + +/***/ }), + +/***/ 899: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + +const EventEmitter = __webpack_require__(614); +const tls = __webpack_require__(16); +const http2 = __webpack_require__(565); +const QuickLRU = __webpack_require__(904); + +const kCurrentStreamsCount = Symbol('currentStreamsCount'); +const kRequest = Symbol('request'); +const kOriginSet = Symbol('cachedOriginSet'); +const kGracefullyClosing = Symbol('gracefullyClosing'); + +const nameKeys = [ + // `http2.connect()` options + 'maxDeflateDynamicTableSize', + 'maxSessionMemory', + 'maxHeaderListPairs', + 'maxOutstandingPings', + 'maxReservedRemoteStreams', + 'maxSendHeaderBlockLength', + 'paddingStrategy', + + // `tls.connect()` options + 'localAddress', + 'path', + 'rejectUnauthorized', + 'minDHSize', + + // `tls.createSecureContext()` options + 'ca', + 'cert', + 'clientCertEngine', + 'ciphers', + 'key', + 'pfx', + 'servername', + 'minVersion', + 'maxVersion', + 'secureProtocol', + 'crl', + 'honorCipherOrder', + 'ecdhCurve', + 'dhparam', + 'secureOptions', + 'sessionIdContext' +]; + +const getSortedIndex = (array, value, compare) => { + let low = 0; + let high = array.length; + + while (low < high) { + const mid = (low + high) >>> 1; + + /* istanbul ignore next */ + if (compare(array[mid], value)) { + // This never gets called because we use descending sort. Better to have this anyway. + low = mid + 1; + } else { + high = mid; + } + } + + return low; +}; + +const compareSessions = (a, b) => { + return a.remoteSettings.maxConcurrentStreams > b.remoteSettings.maxConcurrentStreams; +}; + +// See https://tools.ietf.org/html/rfc8336 +const closeCoveredSessions = (where, session) => { + // Clients SHOULD NOT emit new requests on any connection whose Origin + // Set is a proper subset of another connection's Origin Set, and they + // SHOULD close it once all outstanding requests are satisfied. + for (const coveredSession of where) { + if ( + // The set is a proper subset when its length is less than the other set. + coveredSession[kOriginSet].length < session[kOriginSet].length && + + // And the other set includes all elements of the subset. + coveredSession[kOriginSet].every(origin => session[kOriginSet].includes(origin)) && + + // Makes sure that the session can handle all requests from the covered session. + coveredSession[kCurrentStreamsCount] + session[kCurrentStreamsCount] <= session.remoteSettings.maxConcurrentStreams + ) { + // This allows pending requests to finish and prevents making new requests. + gracefullyClose(coveredSession); + } + } +}; + +// This is basically inverted `closeCoveredSessions(...)`. +const closeSessionIfCovered = (where, coveredSession) => { + for (const session of where) { + if ( + coveredSession[kOriginSet].length < session[kOriginSet].length && + coveredSession[kOriginSet].every(origin => session[kOriginSet].includes(origin)) && + coveredSession[kCurrentStreamsCount] + session[kCurrentStreamsCount] <= session.remoteSettings.maxConcurrentStreams + ) { + gracefullyClose(coveredSession); + } + } +}; + +const getSessions = ({agent, isFree}) => { + const result = {}; + + // eslint-disable-next-line guard-for-in + for (const normalizedOptions in agent.sessions) { + const sessions = agent.sessions[normalizedOptions]; + + const filtered = sessions.filter(session => { + const result = session[Agent.kCurrentStreamsCount] < session.remoteSettings.maxConcurrentStreams; + + return isFree ? result : !result; + }); + + if (filtered.length !== 0) { + result[normalizedOptions] = filtered; + } + } + + return result; +}; + +const gracefullyClose = session => { + session[kGracefullyClosing] = true; + + if (session[kCurrentStreamsCount] === 0) { + session.close(); + } +}; + +class Agent extends EventEmitter { + constructor({timeout = 60000, maxSessions = Infinity, maxFreeSessions = 10, maxCachedTlsSessions = 100} = {}) { + super(); + + // A session is considered busy when its current streams count + // is equal to or greater than the `maxConcurrentStreams` value. + + // A session is considered free when its current streams count + // is less than the `maxConcurrentStreams` value. + + // SESSIONS[NORMALIZED_OPTIONS] = []; + this.sessions = {}; + + // The queue for creating new sessions. It looks like this: + // QUEUE[NORMALIZED_OPTIONS][NORMALIZED_ORIGIN] = ENTRY_FUNCTION + // + // The entry function has `listeners`, `completed` and `destroyed` properties. + // `listeners` is an array of objects containing `resolve` and `reject` functions. + // `completed` is a boolean. It's set to true after ENTRY_FUNCTION is executed. + // `destroyed` is a boolean. If it's set to true, the session will be destroyed if hasn't connected yet. + this.queue = {}; + + // Each session will use this timeout value. + this.timeout = timeout; + + // Max sessions in total + this.maxSessions = maxSessions; + + // Max free sessions in total + // TODO: decreasing `maxFreeSessions` should close some sessions + this.maxFreeSessions = maxFreeSessions; + + this._freeSessionsCount = 0; + this._sessionsCount = 0; + + // We don't support push streams by default. + this.settings = { + enablePush: false + }; + + // Reusing TLS sessions increases performance. + this.tlsSessionCache = new QuickLRU({maxSize: maxCachedTlsSessions}); + } + + static normalizeOrigin(url, servername) { + if (typeof url === 'string') { + url = new URL(url); + } + + if (servername && url.hostname !== servername) { + url.hostname = servername; + } + + return url.origin; + } + + normalizeOptions(options) { + let normalized = ''; + + if (options) { + for (const key of nameKeys) { + if (options[key]) { + normalized += `:${options[key]}`; + } + } + } + + return normalized; + } + + _tryToCreateNewSession(normalizedOptions, normalizedOrigin) { + if (!(normalizedOptions in this.queue) || !(normalizedOrigin in this.queue[normalizedOptions])) { + return; + } + + const item = this.queue[normalizedOptions][normalizedOrigin]; + + // The entry function can be run only once. + // BUG: The session may be never created when: + // - the first condition is false AND + // - this function is never called with the same arguments in the future. + if (this._sessionsCount < this.maxSessions && !item.completed) { + item.completed = true; + + item(); + } + } + + getSession(origin, options, listeners) { + return new Promise((resolve, reject) => { + if (Array.isArray(listeners)) { + listeners = [...listeners]; + + // Resolve the current promise ASAP, we're just moving the listeners. + // They will be executed at a different time. + resolve(); + } else { + listeners = [{resolve, reject}]; + } + + const normalizedOptions = this.normalizeOptions(options); + const normalizedOrigin = Agent.normalizeOrigin(origin, options && options.servername); + + if (normalizedOrigin === undefined) { + for (const {reject} of listeners) { + reject(new TypeError('The `origin` argument needs to be a string or an URL object')); + } + + return; + } + + if (normalizedOptions in this.sessions) { + const sessions = this.sessions[normalizedOptions]; + + let maxConcurrentStreams = -1; + let currentStreamsCount = -1; + let optimalSession; + + // We could just do this.sessions[normalizedOptions].find(...) but that isn't optimal. + // Additionally, we are looking for session which has biggest current pending streams count. + for (const session of sessions) { + const sessionMaxConcurrentStreams = session.remoteSettings.maxConcurrentStreams; + + if (sessionMaxConcurrentStreams < maxConcurrentStreams) { + break; + } + + if (session[kOriginSet].includes(normalizedOrigin)) { + const sessionCurrentStreamsCount = session[kCurrentStreamsCount]; + + if ( + sessionCurrentStreamsCount >= sessionMaxConcurrentStreams || + session[kGracefullyClosing] || + // Unfortunately the `close` event isn't called immediately, + // so `session.destroyed` is `true`, but `session.closed` is `false`. + session.destroyed + ) { + continue; + } + + // We only need set this once. + if (!optimalSession) { + maxConcurrentStreams = sessionMaxConcurrentStreams; + } + + // We're looking for the session which has biggest current pending stream count, + // in order to minimalize the amount of active sessions. + if (sessionCurrentStreamsCount > currentStreamsCount) { + optimalSession = session; + currentStreamsCount = sessionCurrentStreamsCount; + } + } + } + + if (optimalSession) { + /* istanbul ignore next: safety check */ + if (listeners.length !== 1) { + for (const {reject} of listeners) { + const error = new Error( + `Expected the length of listeners to be 1, got ${listeners.length}.\n` + + 'Please report this to https://github.com/szmarczak/http2-wrapper/' + ); + + reject(error); + } + + return; + } + + listeners[0].resolve(optimalSession); + return; + } + } + + if (normalizedOptions in this.queue) { + if (normalizedOrigin in this.queue[normalizedOptions]) { + // There's already an item in the queue, just attach ourselves to it. + this.queue[normalizedOptions][normalizedOrigin].listeners.push(...listeners); + + // This shouldn't be executed here. + // See the comment inside _tryToCreateNewSession. + this._tryToCreateNewSession(normalizedOptions, normalizedOrigin); + return; + } + } else { + this.queue[normalizedOptions] = {}; + } + + // The entry must be removed from the queue IMMEDIATELY when: + // 1. the session connects successfully, + // 2. an error occurs. + const removeFromQueue = () => { + // Our entry can be replaced. We cannot remove the new one. + if (normalizedOptions in this.queue && this.queue[normalizedOptions][normalizedOrigin] === entry) { + delete this.queue[normalizedOptions][normalizedOrigin]; + + if (Object.keys(this.queue[normalizedOptions]).length === 0) { + delete this.queue[normalizedOptions]; + } + } + }; + + // The main logic is here + const entry = () => { + const name = `${normalizedOrigin}:${normalizedOptions}`; + let receivedSettings = false; + + try { + const session = http2.connect(origin, { + createConnection: this.createConnection, + settings: this.settings, + session: this.tlsSessionCache.get(name), + ...options + }); + session[kCurrentStreamsCount] = 0; + session[kGracefullyClosing] = false; + + const isFree = () => session[kCurrentStreamsCount] < session.remoteSettings.maxConcurrentStreams; + let wasFree = true; + + session.socket.once('session', tlsSession => { + this.tlsSessionCache.set(name, tlsSession); + }); + + session.once('error', error => { + // Listeners are empty when the session successfully connected. + for (const {reject} of listeners) { + reject(error); + } + + // The connection got broken, purge the cache. + this.tlsSessionCache.delete(name); + }); + + session.setTimeout(this.timeout, () => { + // Terminates all streams owned by this session. + // TODO: Maybe the streams should have a "Session timed out" error? + session.destroy(); + }); + + session.once('close', () => { + if (receivedSettings) { + // 1. If it wasn't free then no need to decrease because + // it has been decreased already in session.request(). + // 2. `stream.once('close')` won't increment the count + // because the session is already closed. + if (wasFree) { + this._freeSessionsCount--; + } + + this._sessionsCount--; + + // This cannot be moved to the stream logic, + // because there may be a session that hadn't made a single request. + const where = this.sessions[normalizedOptions]; + where.splice(where.indexOf(session), 1); + + if (where.length === 0) { + delete this.sessions[normalizedOptions]; + } + } else { + // Broken connection + const error = new Error('Session closed without receiving a SETTINGS frame'); + error.code = 'HTTP2WRAPPER_NOSETTINGS'; + + for (const {reject} of listeners) { + reject(error); + } + + removeFromQueue(); + } + + // There may be another session awaiting. + this._tryToCreateNewSession(normalizedOptions, normalizedOrigin); + }); + + // Iterates over the queue and processes listeners. + const processListeners = () => { + if (!(normalizedOptions in this.queue) || !isFree()) { + return; + } + + for (const origin of session[kOriginSet]) { + if (origin in this.queue[normalizedOptions]) { + const {listeners} = this.queue[normalizedOptions][origin]; + + // Prevents session overloading. + while (listeners.length !== 0 && isFree()) { + // We assume `resolve(...)` calls `request(...)` *directly*, + // otherwise the session will get overloaded. + listeners.shift().resolve(session); + } + + const where = this.queue[normalizedOptions]; + if (where[origin].listeners.length === 0) { + delete where[origin]; + + if (Object.keys(where).length === 0) { + delete this.queue[normalizedOptions]; + break; + } + } + + // We're no longer free, no point in continuing. + if (!isFree()) { + break; + } + } + } + }; + + // The Origin Set cannot shrink. No need to check if it suddenly became covered by another one. + session.on('origin', () => { + session[kOriginSet] = session.originSet; + + if (!isFree()) { + // The session is full. + return; + } + + processListeners(); + + // Close covered sessions (if possible). + closeCoveredSessions(this.sessions[normalizedOptions], session); + }); + + session.once('remoteSettings', () => { + // Fix Node.js bug preventing the process from exiting + session.ref(); + session.unref(); + + this._sessionsCount++; + + // The Agent could have been destroyed already. + if (entry.destroyed) { + const error = new Error('Agent has been destroyed'); + + for (const listener of listeners) { + listener.reject(error); + } + + session.destroy(); + return; + } + + session[kOriginSet] = session.originSet; + + { + const where = this.sessions; + + if (normalizedOptions in where) { + const sessions = where[normalizedOptions]; + sessions.splice(getSortedIndex(sessions, session, compareSessions), 0, session); + } else { + where[normalizedOptions] = [session]; + } + } + + this._freeSessionsCount += 1; + receivedSettings = true; + + this.emit('session', session); + + processListeners(); + removeFromQueue(); + + // TODO: Close last recently used (or least used?) session + if (session[kCurrentStreamsCount] === 0 && this._freeSessionsCount > this.maxFreeSessions) { + session.close(); + } + + // Check if we haven't managed to execute all listeners. + if (listeners.length !== 0) { + // Request for a new session with predefined listeners. + this.getSession(normalizedOrigin, options, listeners); + listeners.length = 0; + } + + // `session.remoteSettings.maxConcurrentStreams` might get increased + session.on('remoteSettings', () => { + processListeners(); + + // In case the Origin Set changes + closeCoveredSessions(this.sessions[normalizedOptions], session); + }); + }); + + // Shim `session.request()` in order to catch all streams + session[kRequest] = session.request; + session.request = (headers, streamOptions) => { + if (session[kGracefullyClosing]) { + throw new Error('The session is gracefully closing. No new streams are allowed.'); + } + + const stream = session[kRequest](headers, streamOptions); + + // The process won't exit until the session is closed or all requests are gone. + session.ref(); + + ++session[kCurrentStreamsCount]; + + if (session[kCurrentStreamsCount] === session.remoteSettings.maxConcurrentStreams) { + this._freeSessionsCount--; + } + + stream.once('close', () => { + wasFree = isFree(); + + --session[kCurrentStreamsCount]; + + if (!session.destroyed && !session.closed) { + closeSessionIfCovered(this.sessions[normalizedOptions], session); + + if (isFree() && !session.closed) { + if (!wasFree) { + this._freeSessionsCount++; + + wasFree = true; + } + + const isEmpty = session[kCurrentStreamsCount] === 0; + + if (isEmpty) { + session.unref(); + } + + if ( + isEmpty && + ( + this._freeSessionsCount > this.maxFreeSessions || + session[kGracefullyClosing] + ) + ) { + session.close(); + } else { + closeCoveredSessions(this.sessions[normalizedOptions], session); + processListeners(); + } + } + } + }); + + return stream; + }; + } catch (error) { + for (const listener of listeners) { + listener.reject(error); + } + + removeFromQueue(); + } + }; + + entry.listeners = listeners; + entry.completed = false; + entry.destroyed = false; + + this.queue[normalizedOptions][normalizedOrigin] = entry; + this._tryToCreateNewSession(normalizedOptions, normalizedOrigin); + }); + } + + request(origin, options, headers, streamOptions) { + return new Promise((resolve, reject) => { + this.getSession(origin, options, [{ + reject, + resolve: session => { + try { + resolve(session.request(headers, streamOptions)); + } catch (error) { + reject(error); + } + } + }]); + }); + } + + createConnection(origin, options) { + return Agent.connect(origin, options); + } + + static connect(origin, options) { + options.ALPNProtocols = ['h2']; + + const port = origin.port || 443; + const host = origin.hostname || origin.host; + + if (typeof options.servername === 'undefined') { + options.servername = host; + } + + return tls.connect(port, host, options); + } + + closeFreeSessions() { + for (const sessions of Object.values(this.sessions)) { + for (const session of sessions) { + if (session[kCurrentStreamsCount] === 0) { + session.close(); + } + } + } + } + + destroy(reason) { + for (const sessions of Object.values(this.sessions)) { + for (const session of sessions) { + session.destroy(reason); + } + } + + for (const entriesOfAuthority of Object.values(this.queue)) { + for (const entry of Object.values(entriesOfAuthority)) { + entry.destroyed = true; + } + } + + // New requests should NOT attach to destroyed sessions + this.queue = {}; + } + + get freeSessions() { + return getSessions({agent: this, isFree: true}); + } + + get busySessions() { + return getSessions({agent: this, isFree: false}); + } +} + +Agent.kCurrentStreamsCount = kCurrentStreamsCount; +Agent.kGracefullyClosing = kGracefullyClosing; + +module.exports = { + Agent, + globalAgent: new Agent() +}; + + +/***/ }), + +/***/ 904: +/***/ (function(module) { + +"use strict"; + + +class QuickLRU { + constructor(options = {}) { + if (!(options.maxSize && options.maxSize > 0)) { + throw new TypeError('`maxSize` must be a number greater than 0'); + } + + this.maxSize = options.maxSize; + this.onEviction = options.onEviction; + this.cache = new Map(); + this.oldCache = new Map(); + this._size = 0; + } + + _set(key, value) { + this.cache.set(key, value); + this._size++; + + if (this._size >= this.maxSize) { + this._size = 0; + + if (typeof this.onEviction === 'function') { + for (const [key, value] of this.oldCache.entries()) { + this.onEviction(key, value); + } + } + + this.oldCache = this.cache; + this.cache = new Map(); + } + } + + get(key) { + if (this.cache.has(key)) { + return this.cache.get(key); + } + + if (this.oldCache.has(key)) { + const value = this.oldCache.get(key); + this.oldCache.delete(key); + this._set(key, value); + return value; + } + } + + set(key, value) { + if (this.cache.has(key)) { + this.cache.set(key, value); + } else { + this._set(key, value); + } + + return this; + } + + has(key) { + return this.cache.has(key) || this.oldCache.has(key); + } + + peek(key) { + if (this.cache.has(key)) { + return this.cache.get(key); + } + + if (this.oldCache.has(key)) { + return this.oldCache.get(key); + } + } + + delete(key) { + const deleted = this.cache.delete(key); + if (deleted) { + this._size--; + } + + return this.oldCache.delete(key) || deleted; + } + + clear() { + this.cache.clear(); + this.oldCache.clear(); + this._size = 0; + } + + * keys() { + for (const [key] of this) { + yield key; + } + } + + * values() { + for (const [, value] of this) { + yield value; + } + } + + * [Symbol.iterator]() { + for (const item of this.cache) { + yield item; + } + + for (const item of this.oldCache) { + const [key] = item; + if (!this.cache.has(key)) { + yield item; + } + } + } + + get size() { + let oldCacheSize = 0; + for (const key of this.oldCache.keys()) { + if (!this.cache.has(key)) { + oldCacheSize++; + } + } + + return Math.min(this._size + oldCacheSize, this.maxSize); + } +} + +module.exports = QuickLRU; + + +/***/ }), + +/***/ 907: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +/* istanbul ignore file: deprecated */ +const url_1 = __webpack_require__(835); +const keys = [ + 'protocol', + 'host', + 'hostname', + 'port', + 'pathname', + 'search' +]; +exports.default = (origin, options) => { + var _a, _b; + if (options.path) { + if (options.pathname) { + throw new TypeError('Parameters `path` and `pathname` are mutually exclusive.'); + } + if (options.search) { + throw new TypeError('Parameters `path` and `search` are mutually exclusive.'); + } + if (options.searchParams) { + throw new TypeError('Parameters `path` and `searchParams` are mutually exclusive.'); + } + } + if (options.search && options.searchParams) { + throw new TypeError('Parameters `search` and `searchParams` are mutually exclusive.'); + } + if (!origin) { + if (!options.protocol) { + throw new TypeError('No URL protocol specified'); + } + origin = `${options.protocol}//${(_b = (_a = options.hostname) !== null && _a !== void 0 ? _a : options.host) !== null && _b !== void 0 ? _b : ''}`; + } + const url = new url_1.URL(origin); + if (options.path) { + const searchIndex = options.path.indexOf('?'); + if (searchIndex === -1) { + options.pathname = options.path; + } + else { + options.pathname = options.path.slice(0, searchIndex); + options.search = options.path.slice(searchIndex + 1); + } + delete options.path; + } + for (const key of keys) { + if (options[key]) { + url[key] = options[key].toString(); + } + } + return url; +}; + + +/***/ }), + +/***/ 910: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const types_1 = __webpack_require__(36); +function createRejection(error, ...beforeErrorGroups) { + const promise = (async () => { + if (error instanceof types_1.RequestError) { + try { + for (const hooks of beforeErrorGroups) { + if (hooks) { + for (const hook of hooks) { + // eslint-disable-next-line no-await-in-loop + error = await hook(error); + } + } + } + } + catch (error_) { + error = error_; + } + } + throw error; + })(); + const returnPromise = () => promise; + promise.json = returnPromise; + promise.text = returnPromise; + promise.buffer = returnPromise; + promise.on = returnPromise; + return promise; +} +exports.default = createRejection; + + +/***/ }), + +/***/ 927: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const types_1 = __webpack_require__(36); +const retryAfterStatusCodes = new Set([413, 429, 503]); +const isErrorWithResponse = (error) => (error instanceof types_1.HTTPError || error instanceof types_1.ParseError || error instanceof types_1.MaxRedirectsError); +const calculateRetryDelay = ({ attemptCount, retryOptions, error }) => { + if (attemptCount > retryOptions.limit) { + return 0; + } + const hasMethod = retryOptions.methods.includes(error.options.method); + const hasErrorCode = retryOptions.errorCodes.includes(error.code); + const hasStatusCode = isErrorWithResponse(error) && retryOptions.statusCodes.includes(error.response.statusCode); + if (!hasMethod || (!hasErrorCode && !hasStatusCode)) { + return 0; + } + if (isErrorWithResponse(error)) { + const { response } = error; + if (response && 'retry-after' in response.headers && retryAfterStatusCodes.has(response.statusCode)) { + let after = Number(response.headers['retry-after']); + if (Number.isNaN(after)) { + after = Date.parse(response.headers['retry-after']) - Date.now(); + } + else { + after *= 1000; + } + if (retryOptions.maxRetryAfter === undefined || after > retryOptions.maxRetryAfter) { + return 0; + } + return after; + } + if (response.statusCode === 413) { + return 0; + } + } + const noise = Math.random() * 100; + return ((2 ** (attemptCount - 1)) * 1000) + noise; +}; +exports.default = calculateRetryDelay; + + +/***/ }), + +/***/ 928: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// @ts-check +const core = __webpack_require__(470); +const command = __webpack_require__(431); +const got = __webpack_require__(77).default; +const jsonata = __webpack_require__(350); +const { auth: { retrieveToken }, secrets: { getSecrets } } = __webpack_require__(676); const AUTH_METHODS = ['approle', 'token', 'github']; const VALID_KV_VERSION = [-1, 1, 2]; @@ -280,234 +14319,1512 @@ module.exports = { /***/ }), -/***/ 128: -/***/ (function(module) { +/***/ 946: +/***/ (function(__unusedmodule, exports, __webpack_require__) { -module.exports = eval("require")("jsonata"); +"use strict"; - -/***/ }), - -/***/ 130: -/***/ (function(__unusedmodule, __unusedexports, __webpack_require__) { - -const core = __webpack_require__(968); -const { exportSecrets } = __webpack_require__(94); - -(async () => { - try { - await core.group('Get Vault Secrets', exportSecrets); - } catch (error) { - core.setFailed(error.message); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UnsupportedProtocolError = exports.ReadError = exports.TimeoutError = exports.UploadError = exports.CacheError = exports.HTTPError = exports.MaxRedirectsError = exports.RequestError = exports.setNonEnumerableProperties = exports.knownHookEvents = exports.withoutBody = exports.kIsNormalizedAlready = void 0; +const util_1 = __webpack_require__(669); +const stream_1 = __webpack_require__(413); +const fs_1 = __webpack_require__(747); +const url_1 = __webpack_require__(835); +const http = __webpack_require__(605); +const http_1 = __webpack_require__(605); +const https = __webpack_require__(211); +const http_timer_1 = __webpack_require__(490); +const cacheable_lookup_1 = __webpack_require__(570); +const CacheableRequest = __webpack_require__(390); +const decompressResponse = __webpack_require__(861); +// @ts-expect-error Missing types +const http2wrapper = __webpack_require__(157); +const lowercaseKeys = __webpack_require__(474); +const is_1 = __webpack_require__(534); +const get_body_size_1 = __webpack_require__(786); +const is_form_data_1 = __webpack_require__(460); +const proxy_events_1 = __webpack_require__(628); +const timed_out_1 = __webpack_require__(811); +const url_to_options_1 = __webpack_require__(10); +const options_to_url_1 = __webpack_require__(907); +const weakable_map_1 = __webpack_require__(48); +const get_buffer_1 = __webpack_require__(452); +const dns_ip_version_1 = __webpack_require__(738); +const deprecation_warning_1 = __webpack_require__(189); +const kRequest = Symbol('request'); +const kResponse = Symbol('response'); +const kResponseSize = Symbol('responseSize'); +const kDownloadedSize = Symbol('downloadedSize'); +const kBodySize = Symbol('bodySize'); +const kUploadedSize = Symbol('uploadedSize'); +const kServerResponsesPiped = Symbol('serverResponsesPiped'); +const kUnproxyEvents = Symbol('unproxyEvents'); +const kIsFromCache = Symbol('isFromCache'); +const kCancelTimeouts = Symbol('cancelTimeouts'); +const kStartedReading = Symbol('startedReading'); +const kStopReading = Symbol('stopReading'); +const kTriggerRead = Symbol('triggerRead'); +const kBody = Symbol('body'); +const kJobs = Symbol('jobs'); +const kOriginalResponse = Symbol('originalResponse'); +exports.kIsNormalizedAlready = Symbol('isNormalizedAlready'); +const supportsBrotli = is_1.default.string(process.versions.brotli); +exports.withoutBody = new Set(['GET', 'HEAD']); +exports.knownHookEvents = ['init', 'beforeRequest', 'beforeRedirect', 'beforeError']; +function validateSearchParameters(searchParameters) { + // eslint-disable-next-line guard-for-in + for (const key in searchParameters) { + const value = searchParameters[key]; + if (!is_1.default.string(value) && !is_1.default.number(value) && !is_1.default.boolean(value) && !is_1.default.null_(value) && !is_1.default.undefined(value)) { + throw new TypeError(`The \`searchParams\` value '${String(value)}' must be a string, number, boolean or null`); + } } -})(); - - -/***/ }), - -/***/ 238: -/***/ (function(module, __unusedexports, __webpack_require__) { - -// @ts-check -const core = __webpack_require__(968); - -/*** - * Authenticate with Vault and retrieve a Vault token that can be used for requests. - * @param {string} method - * @param {import('got').Got} client - */ -async function retrieveToken(method, client) { - switch (method) { - case 'approle': { - const vaultRoleId = core.getInput('roleId', { required: true }); - const vaultSecretId = core.getInput('secretId', { required: true }); - return await getClientToken(client, method, { role_id: vaultRoleId, secret_id: vaultSecretId }); +} +function isClientRequest(clientRequest) { + return is_1.default.object(clientRequest) && !('statusCode' in clientRequest); +} +const cacheableStore = new weakable_map_1.default(); +const waitForOpenFile = async (file) => new Promise((resolve, reject) => { + const onError = (error) => { + reject(error); + }; + // Node.js 12 has incomplete types + if (!file.pending) { + resolve(); + } + file.once('error', onError); + file.once('ready', () => { + file.off('error', onError); + resolve(); + }); +}); +const redirectCodes = new Set([300, 301, 302, 303, 304, 307, 308]); +const nonEnumerableProperties = [ + 'context', + 'body', + 'json', + 'form' +]; +exports.setNonEnumerableProperties = (sources, to) => { + // Non enumerable properties shall not be merged + const properties = {}; + for (const source of sources) { + if (!source) { + continue; } - case 'github': { - const githubToken = core.getInput('githubToken', { required: true }); - return await getClientToken(client, method, { token: githubToken }); + for (const name of nonEnumerableProperties) { + if (!(name in source)) { + continue; + } + properties[name] = { + writable: true, + configurable: true, + enumerable: false, + // @ts-expect-error TS doesn't see the check above + value: source[name] + }; } - default: { - if (!method || method === 'token') { - return core.getInput('token', { required: true }); - } else { - /** @type {string} */ - const payload = core.getInput('authPayload', { required: true }); - if (!payload) { - throw Error('When using a custom authentication method, you must provide the payload'); + } + Object.defineProperties(to, properties); +}; +class RequestError extends Error { + constructor(message, error, self) { + var _a; + super(message); + Error.captureStackTrace(this, this.constructor); + this.name = 'RequestError'; + this.code = error.code; + if (self instanceof Request) { + Object.defineProperty(this, 'request', { + enumerable: false, + value: self + }); + Object.defineProperty(this, 'response', { + enumerable: false, + value: self[kResponse] + }); + Object.defineProperty(this, 'options', { + // This fails because of TS 3.7.2 useDefineForClassFields + // Ref: https://github.com/microsoft/TypeScript/issues/34972 + enumerable: false, + value: self.options + }); + } + else { + Object.defineProperty(this, 'options', { + // This fails because of TS 3.7.2 useDefineForClassFields + // Ref: https://github.com/microsoft/TypeScript/issues/34972 + enumerable: false, + value: self + }); + } + this.timings = (_a = this.request) === null || _a === void 0 ? void 0 : _a.timings; + // Recover the original stacktrace + if (!is_1.default.undefined(error.stack)) { + const indexOfMessage = this.stack.indexOf(this.message) + this.message.length; + const thisStackTrace = this.stack.slice(indexOfMessage).split('\n').reverse(); + const errorStackTrace = error.stack.slice(error.stack.indexOf(error.message) + error.message.length).split('\n').reverse(); + // Remove duplicated traces + while (errorStackTrace.length !== 0 && errorStackTrace[0] === thisStackTrace[0]) { + thisStackTrace.shift(); + } + this.stack = `${this.stack.slice(0, indexOfMessage)}${thisStackTrace.reverse().join('\n')}${errorStackTrace.reverse().join('\n')}`; + } + } +} +exports.RequestError = RequestError; +class MaxRedirectsError extends RequestError { + constructor(request) { + super(`Redirected ${request.options.maxRedirects} times. Aborting.`, {}, request); + this.name = 'MaxRedirectsError'; + } +} +exports.MaxRedirectsError = MaxRedirectsError; +class HTTPError extends RequestError { + constructor(response) { + super(`Response code ${response.statusCode} (${response.statusMessage})`, {}, response.request); + this.name = 'HTTPError'; + } +} +exports.HTTPError = HTTPError; +class CacheError extends RequestError { + constructor(error, request) { + super(error.message, error, request); + this.name = 'CacheError'; + } +} +exports.CacheError = CacheError; +class UploadError extends RequestError { + constructor(error, request) { + super(error.message, error, request); + this.name = 'UploadError'; + } +} +exports.UploadError = UploadError; +class TimeoutError extends RequestError { + constructor(error, timings, request) { + super(error.message, error, request); + this.name = 'TimeoutError'; + this.event = error.event; + this.timings = timings; + } +} +exports.TimeoutError = TimeoutError; +class ReadError extends RequestError { + constructor(error, request) { + super(error.message, error, request); + this.name = 'ReadError'; + } +} +exports.ReadError = ReadError; +class UnsupportedProtocolError extends RequestError { + constructor(options) { + super(`Unsupported protocol "${options.url.protocol}"`, {}, options); + this.name = 'UnsupportedProtocolError'; + } +} +exports.UnsupportedProtocolError = UnsupportedProtocolError; +const proxiedRequestEvents = [ + 'socket', + 'connect', + 'continue', + 'information', + 'upgrade', + 'timeout' +]; +class Request extends stream_1.Duplex { + constructor(url, options = {}, defaults) { + super({ + // It needs to be zero because we're just proxying the data to another stream + highWaterMark: 0 + }); + this[kDownloadedSize] = 0; + this[kUploadedSize] = 0; + this.requestInitialized = false; + this[kServerResponsesPiped] = new Set(); + this.redirects = []; + this[kStopReading] = false; + this[kTriggerRead] = false; + this[kJobs] = []; + // TODO: Remove this when targeting Node.js >= 12 + this._progressCallbacks = []; + const unlockWrite = () => this._unlockWrite(); + const lockWrite = () => this._lockWrite(); + this.on('pipe', (source) => { + source.prependListener('data', unlockWrite); + source.on('data', lockWrite); + source.prependListener('end', unlockWrite); + source.on('end', lockWrite); + }); + this.on('unpipe', (source) => { + source.off('data', unlockWrite); + source.off('data', lockWrite); + source.off('end', unlockWrite); + source.off('end', lockWrite); + }); + this.on('pipe', source => { + if (source instanceof http_1.IncomingMessage) { + this.options.headers = { + ...source.headers, + ...this.options.headers + }; + } + }); + const { json, body, form } = options; + if (json || body || form) { + this._lockWrite(); + } + (async (nonNormalizedOptions) => { + var _a; + try { + if (nonNormalizedOptions.body instanceof fs_1.ReadStream) { + await waitForOpenFile(nonNormalizedOptions.body); } - return await getClientToken(client, method, JSON.parse(payload.trim())); + if (exports.kIsNormalizedAlready in nonNormalizedOptions) { + this.options = nonNormalizedOptions; + } + else { + // @ts-expect-error Common TypeScript bug saying that `this.constructor` is not accessible + this.options = this.constructor.normalizeArguments(url, nonNormalizedOptions, defaults); + } + const { url: normalizedURL } = this.options; + if (!normalizedURL) { + throw new TypeError('Missing `url` property'); + } + this.requestUrl = normalizedURL.toString(); + decodeURI(this.requestUrl); + await this._finalizeBody(); + await this._makeRequest(); + if (this.destroyed) { + (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.destroy(); + return; + } + // Queued writes etc. + for (const job of this[kJobs]) { + job(); + } + this.requestInitialized = true; + } + catch (error) { + if (error instanceof RequestError) { + this._beforeError(error); + return; + } + // This is a workaround for https://github.com/nodejs/node/issues/33335 + if (!this.destroyed) { + this.destroy(error); + } + } + })(options); + } + static normalizeArguments(url, options, defaults) { + var _a, _b, _c, _d; + const rawOptions = options; + if (is_1.default.object(url) && !is_1.default.urlInstance(url)) { + options = { ...defaults, ...url, ...options }; + } + else { + if (url && options && options.url !== undefined) { + throw new TypeError('The `url` option is mutually exclusive with the `input` argument'); + } + options = { ...defaults, ...options }; + if (url !== undefined) { + options.url = url; + } + if (is_1.default.urlInstance(options.url)) { + options.url = new url_1.URL(options.url.toString()); + } + } + // TODO: Deprecate URL options in Got 12. + // Support extend-specific options + if (options.cache === false) { + options.cache = undefined; + } + if (options.dnsCache === false) { + options.dnsCache = undefined; + } + // Nice type assertions + is_1.assert.any([is_1.default.string, is_1.default.undefined], options.method); + is_1.assert.any([is_1.default.object, is_1.default.undefined], options.headers); + is_1.assert.any([is_1.default.string, is_1.default.urlInstance, is_1.default.undefined], options.prefixUrl); + is_1.assert.any([is_1.default.object, is_1.default.undefined], options.cookieJar); + is_1.assert.any([is_1.default.object, is_1.default.string, is_1.default.undefined], options.searchParams); + is_1.assert.any([is_1.default.object, is_1.default.string, is_1.default.undefined], options.cache); + is_1.assert.any([is_1.default.object, is_1.default.number, is_1.default.undefined], options.timeout); + is_1.assert.any([is_1.default.object, is_1.default.undefined], options.context); + is_1.assert.any([is_1.default.object, is_1.default.undefined], options.hooks); + is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.decompress); + is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.ignoreInvalidCookies); + is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.followRedirect); + is_1.assert.any([is_1.default.number, is_1.default.undefined], options.maxRedirects); + is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.throwHttpErrors); + is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.http2); + is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.allowGetBody); + is_1.assert.any([is_1.default.string, is_1.default.undefined], options.localAddress); + is_1.assert.any([dns_ip_version_1.isDnsLookupIpVersion, is_1.default.undefined], options.dnsLookupIpVersion); + is_1.assert.any([is_1.default.object, is_1.default.undefined], options.https); + is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.rejectUnauthorized); + if (options.https) { + is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.https.rejectUnauthorized); + is_1.assert.any([is_1.default.function_, is_1.default.undefined], options.https.checkServerIdentity); + is_1.assert.any([is_1.default.string, is_1.default.object, is_1.default.array, is_1.default.undefined], options.https.certificateAuthority); + is_1.assert.any([is_1.default.string, is_1.default.object, is_1.default.array, is_1.default.undefined], options.https.key); + is_1.assert.any([is_1.default.string, is_1.default.object, is_1.default.array, is_1.default.undefined], options.https.certificate); + is_1.assert.any([is_1.default.string, is_1.default.undefined], options.https.passphrase); + } + // `options.method` + if (is_1.default.string(options.method)) { + options.method = options.method.toUpperCase(); + } + else { + options.method = 'GET'; + } + // `options.headers` + if (options.headers === (defaults === null || defaults === void 0 ? void 0 : defaults.headers)) { + options.headers = { ...options.headers }; + } + else { + options.headers = lowercaseKeys({ ...(defaults === null || defaults === void 0 ? void 0 : defaults.headers), ...options.headers }); + } + // Disallow legacy `url.Url` + if ('slashes' in options) { + throw new TypeError('The legacy `url.Url` has been deprecated. Use `URL` instead.'); + } + // `options.auth` + if ('auth' in options) { + throw new TypeError('Parameter `auth` is deprecated. Use `username` / `password` instead.'); + } + // `options.searchParams` + if ('searchParams' in options) { + if (options.searchParams && options.searchParams !== (defaults === null || defaults === void 0 ? void 0 : defaults.searchParams)) { + let searchParameters; + if (is_1.default.string(options.searchParams) || (options.searchParams instanceof url_1.URLSearchParams)) { + searchParameters = new url_1.URLSearchParams(options.searchParams); + } + else { + validateSearchParameters(options.searchParams); + searchParameters = new url_1.URLSearchParams(); + // eslint-disable-next-line guard-for-in + for (const key in options.searchParams) { + const value = options.searchParams[key]; + if (value === null) { + searchParameters.append(key, ''); + } + else if (value !== undefined) { + searchParameters.append(key, value); + } + } + } + // `normalizeArguments()` is also used to merge options + (_a = defaults === null || defaults === void 0 ? void 0 : defaults.searchParams) === null || _a === void 0 ? void 0 : _a.forEach((value, key) => { + // Only use default if one isn't already defined + if (!searchParameters.has(key)) { + searchParameters.append(key, value); + } + }); + options.searchParams = searchParameters; + } + } + // `options.username` & `options.password` + options.username = (_b = options.username) !== null && _b !== void 0 ? _b : ''; + options.password = (_c = options.password) !== null && _c !== void 0 ? _c : ''; + // `options.prefixUrl` & `options.url` + if (options.prefixUrl) { + options.prefixUrl = options.prefixUrl.toString(); + if (options.prefixUrl !== '' && !options.prefixUrl.endsWith('/')) { + options.prefixUrl += '/'; + } + } + else { + options.prefixUrl = ''; + } + if (is_1.default.string(options.url)) { + if (options.url.startsWith('/')) { + throw new Error('`input` must not start with a slash when using `prefixUrl`'); + } + options.url = options_to_url_1.default(options.prefixUrl + options.url, options); + } + else if ((is_1.default.undefined(options.url) && options.prefixUrl !== '') || options.protocol) { + options.url = options_to_url_1.default(options.prefixUrl, options); + } + if (options.url) { + // Make it possible to change `options.prefixUrl` + let { prefixUrl } = options; + Object.defineProperty(options, 'prefixUrl', { + set: (value) => { + const url = options.url; + if (!url.href.startsWith(value)) { + throw new Error(`Cannot change \`prefixUrl\` from ${prefixUrl} to ${value}: ${url.href}`); + } + options.url = new url_1.URL(value + url.href.slice(prefixUrl.length)); + prefixUrl = value; + }, + get: () => prefixUrl + }); + // Support UNIX sockets + let { protocol } = options.url; + if (protocol === 'unix:') { + protocol = 'http:'; + options.url = new url_1.URL(`http://unix${options.url.pathname}${options.url.search}`); + } + // Set search params + if (options.searchParams) { + // eslint-disable-next-line @typescript-eslint/no-base-to-string + options.url.search = options.searchParams.toString(); + } + // Protocol check + if (protocol !== 'http:' && protocol !== 'https:') { + throw new UnsupportedProtocolError(options); + } + // Update `username` + if (options.username === '') { + options.username = options.url.username; + } + else { + options.url.username = options.username; + } + // Update `password` + if (options.password === '') { + options.password = options.url.password; + } + else { + options.url.password = options.password; + } + } + // `options.cookieJar` + const { cookieJar } = options; + if (cookieJar) { + let { setCookie, getCookieString } = cookieJar; + is_1.assert.function_(setCookie); + is_1.assert.function_(getCookieString); + /* istanbul ignore next: Horrible `tough-cookie` v3 check */ + if (setCookie.length === 4 && getCookieString.length === 0) { + setCookie = util_1.promisify(setCookie.bind(options.cookieJar)); + getCookieString = util_1.promisify(getCookieString.bind(options.cookieJar)); + options.cookieJar = { + setCookie, + // TODO: Fix this when upgrading to TypeScript 4. + // @ts-expect-error TypeScript thinks that promisifying callback(error, string) will result in Promise + getCookieString + }; + } + } + // `options.cache` + const { cache } = options; + if (cache) { + if (!cacheableStore.has(cache)) { + cacheableStore.set(cache, new CacheableRequest(((requestOptions, handler) => { + const result = requestOptions[kRequest](requestOptions, handler); + // TODO: remove this when `cacheable-request` supports async request functions. + if (is_1.default.promise(result)) { + // @ts-expect-error + // We only need to implement the error handler in order to support HTTP2 caching. + // The result will be a promise anyway. + result.once = (event, handler) => { + if (event === 'error') { + result.catch(handler); + } + else if (event === 'abort') { + // The empty catch is needed here in case when + // it rejects before it's `await`ed in `_makeRequest`. + (async () => { + try { + const request = (await result); + request.once('abort', handler); + } + catch (_a) { } + })(); + } + else { + /* istanbul ignore next: safety check */ + throw new Error(`Unknown HTTP2 promise event: ${event}`); + } + return result; + }; + } + return result; + }), cache)); + } + } + // `options.dnsCache` + if (options.dnsCache === true) { + options.dnsCache = new cacheable_lookup_1.default(); + } + else if (!is_1.default.undefined(options.dnsCache) && !options.dnsCache.lookup) { + throw new TypeError(`Parameter \`dnsCache\` must be a CacheableLookup instance or a boolean, got ${is_1.default(options.dnsCache)}`); + } + // `options.timeout` + if (is_1.default.number(options.timeout)) { + options.timeout = { request: options.timeout }; + } + else if (defaults && options.timeout !== defaults.timeout) { + options.timeout = { + ...defaults.timeout, + ...options.timeout + }; + } + else { + options.timeout = { ...options.timeout }; + } + // `options.context` + if (!options.context) { + options.context = {}; + } + // `options.hooks` + const areHooksDefault = options.hooks === (defaults === null || defaults === void 0 ? void 0 : defaults.hooks); + options.hooks = { ...options.hooks }; + for (const event of exports.knownHookEvents) { + if (event in options.hooks) { + if (is_1.default.array(options.hooks[event])) { + // See https://github.com/microsoft/TypeScript/issues/31445#issuecomment-576929044 + options.hooks[event] = [...options.hooks[event]]; + } + else { + throw new TypeError(`Parameter \`${event}\` must be an Array, got ${is_1.default(options.hooks[event])}`); + } + } + else { + options.hooks[event] = []; + } + } + if (defaults && !areHooksDefault) { + for (const event of exports.knownHookEvents) { + const defaultHooks = defaults.hooks[event]; + if (defaultHooks.length !== 0) { + // See https://github.com/microsoft/TypeScript/issues/31445#issuecomment-576929044 + options.hooks[event] = [ + ...defaults.hooks[event], + ...options.hooks[event] + ]; + } + } + } + // DNS options + if ('family' in options) { + deprecation_warning_1.default('"options.family" was never documented, please use "options.dnsLookupIpVersion"'); + } + // HTTPS options + if (defaults === null || defaults === void 0 ? void 0 : defaults.https) { + options.https = { ...defaults.https, ...options.https }; + } + if ('rejectUnauthorized' in options) { + deprecation_warning_1.default('"options.rejectUnauthorized" is now deprecated, please use "options.https.rejectUnauthorized"'); + } + if ('checkServerIdentity' in options) { + deprecation_warning_1.default('"options.checkServerIdentity" was never documented, please use "options.https.checkServerIdentity"'); + } + if ('ca' in options) { + deprecation_warning_1.default('"options.ca" was never documented, please use "options.https.certificateAuthority"'); + } + if ('key' in options) { + deprecation_warning_1.default('"options.key" was never documented, please use "options.https.key"'); + } + if ('cert' in options) { + deprecation_warning_1.default('"options.cert" was never documented, please use "options.https.certificate"'); + } + if ('passphrase' in options) { + deprecation_warning_1.default('"options.passphrase" was never documented, please use "options.https.passphrase"'); + } + // Other options + if ('followRedirects' in options) { + throw new TypeError('The `followRedirects` option does not exist. Use `followRedirect` instead.'); + } + if (options.agent) { + for (const key in options.agent) { + if (key !== 'http' && key !== 'https' && key !== 'http2') { + throw new TypeError(`Expected the \`options.agent\` properties to be \`http\`, \`https\` or \`http2\`, got \`${key}\``); + } + } + } + options.maxRedirects = (_d = options.maxRedirects) !== null && _d !== void 0 ? _d : 0; + // Set non-enumerable properties + exports.setNonEnumerableProperties([defaults, rawOptions], options); + return options; + } + _lockWrite() { + const onLockedWrite = () => { + throw new TypeError('The payload has been already provided'); + }; + this.write = onLockedWrite; + this.end = onLockedWrite; + } + _unlockWrite() { + this.write = super.write; + this.end = super.end; + } + async _finalizeBody() { + const { options } = this; + const { headers } = options; + const isForm = !is_1.default.undefined(options.form); + const isJSON = !is_1.default.undefined(options.json); + const isBody = !is_1.default.undefined(options.body); + const hasPayload = isForm || isJSON || isBody; + const cannotHaveBody = exports.withoutBody.has(options.method) && !(options.method === 'GET' && options.allowGetBody); + this._cannotHaveBody = cannotHaveBody; + if (hasPayload) { + if (cannotHaveBody) { + throw new TypeError(`The \`${options.method}\` method cannot be used with a body`); + } + if ([isBody, isForm, isJSON].filter(isTrue => isTrue).length > 1) { + throw new TypeError('The `body`, `json` and `form` options are mutually exclusive'); + } + if (isBody && + !(options.body instanceof stream_1.Readable) && + !is_1.default.string(options.body) && + !is_1.default.buffer(options.body) && + !is_form_data_1.default(options.body)) { + throw new TypeError('The `body` option must be a stream.Readable, string or Buffer'); + } + if (isForm && !is_1.default.object(options.form)) { + throw new TypeError('The `form` option must be an Object'); + } + { + // Serialize body + const noContentType = !is_1.default.string(headers['content-type']); + if (isBody) { + // Special case for https://github.com/form-data/form-data + if (is_form_data_1.default(options.body) && noContentType) { + headers['content-type'] = `multipart/form-data; boundary=${options.body.getBoundary()}`; + } + this[kBody] = options.body; + } + else if (isForm) { + if (noContentType) { + headers['content-type'] = 'application/x-www-form-urlencoded'; + } + this[kBody] = (new url_1.URLSearchParams(options.form)).toString(); + } + else { + if (noContentType) { + headers['content-type'] = 'application/json'; + } + this[kBody] = options.stringifyJson(options.json); + } + const uploadBodySize = await get_body_size_1.default(this[kBody], options.headers); + // See https://tools.ietf.org/html/rfc7230#section-3.3.2 + // A user agent SHOULD send a Content-Length in a request message when + // no Transfer-Encoding is sent and the request method defines a meaning + // for an enclosed payload body. For example, a Content-Length header + // field is normally sent in a POST request even when the value is 0 + // (indicating an empty payload body). A user agent SHOULD NOT send a + // Content-Length header field when the request message does not contain + // a payload body and the method semantics do not anticipate such a + // body. + if (is_1.default.undefined(headers['content-length']) && is_1.default.undefined(headers['transfer-encoding'])) { + if (!cannotHaveBody && !is_1.default.undefined(uploadBodySize)) { + headers['content-length'] = String(uploadBodySize); + } + } + } + } + else if (cannotHaveBody) { + this._lockWrite(); + } + else { + this._unlockWrite(); + } + this[kBodySize] = Number(headers['content-length']) || undefined; + } + async _onResponseBase(response) { + const { options } = this; + const { url } = options; + this[kOriginalResponse] = response; + if (options.decompress) { + response = decompressResponse(response); + } + const statusCode = response.statusCode; + const typedResponse = response; + typedResponse.statusMessage = typedResponse.statusMessage ? typedResponse.statusMessage : http.STATUS_CODES[statusCode]; + typedResponse.url = options.url.toString(); + typedResponse.requestUrl = this.requestUrl; + typedResponse.redirectUrls = this.redirects; + typedResponse.request = this; + typedResponse.isFromCache = response.fromCache || false; + typedResponse.ip = this.ip; + this[kIsFromCache] = typedResponse.isFromCache; + this[kResponseSize] = Number(response.headers['content-length']) || undefined; + this[kResponse] = response; + response.once('end', () => { + this[kResponseSize] = this[kDownloadedSize]; + this.emit('downloadProgress', this.downloadProgress); + }); + response.once('error', (error) => { + // Force clean-up, because some packages don't do this. + // TODO: Fix decompress-response + response.destroy(); + this._beforeError(new ReadError(error, this)); + }); + response.once('aborted', () => { + this._beforeError(new ReadError({ + name: 'Error', + message: 'The server aborted pending request', + code: 'ECONNRESET' + }, this)); + }); + this.emit('downloadProgress', this.downloadProgress); + const rawCookies = response.headers['set-cookie']; + if (is_1.default.object(options.cookieJar) && rawCookies) { + let promises = rawCookies.map(async (rawCookie) => options.cookieJar.setCookie(rawCookie, url.toString())); + if (options.ignoreInvalidCookies) { + promises = promises.map(async (p) => p.catch(() => { })); + } + try { + await Promise.all(promises); + } + catch (error) { + this._beforeError(error); + return; + } + } + if (options.followRedirect && response.headers.location && redirectCodes.has(statusCode)) { + // We're being redirected, we don't care about the response. + // It'd be besto to abort the request, but we can't because + // we would have to sacrifice the TCP connection. We don't want that. + response.resume(); + if (this[kRequest]) { + this[kCancelTimeouts](); + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete this[kRequest]; + this[kUnproxyEvents](); + } + const shouldBeGet = statusCode === 303 && options.method !== 'GET' && options.method !== 'HEAD'; + if (shouldBeGet || !options.methodRewriting) { + // Server responded with "see other", indicating that the resource exists at another location, + // and the client should request it from that location via GET or HEAD. + options.method = 'GET'; + if ('body' in options) { + delete options.body; + } + if ('json' in options) { + delete options.json; + } + if ('form' in options) { + delete options.form; + } + } + if (this.redirects.length >= options.maxRedirects) { + this._beforeError(new MaxRedirectsError(this)); + return; + } + try { + // Do not remove. See https://github.com/sindresorhus/got/pull/214 + const redirectBuffer = Buffer.from(response.headers.location, 'binary').toString(); + // Handles invalid URLs. See https://github.com/sindresorhus/got/issues/604 + const redirectUrl = new url_1.URL(redirectBuffer, url); + const redirectString = redirectUrl.toString(); + decodeURI(redirectString); + // Redirecting to a different site, clear sensitive data. + if (redirectUrl.hostname !== url.hostname) { + if ('host' in options.headers) { + delete options.headers.host; + } + if ('cookie' in options.headers) { + delete options.headers.cookie; + } + if ('authorization' in options.headers) { + delete options.headers.authorization; + } + if (options.username || options.password) { + delete options.username; + delete options.password; + } + } + this.redirects.push(redirectString); + options.url = redirectUrl; + for (const hook of options.hooks.beforeRedirect) { + // eslint-disable-next-line no-await-in-loop + await hook(options, typedResponse); + } + this.emit('redirect', typedResponse, options); + await this._makeRequest(); + } + catch (error) { + this._beforeError(error); + return; + } + return; + } + const limitStatusCode = options.followRedirect ? 299 : 399; + const isOk = (statusCode >= 200 && statusCode <= limitStatusCode) || statusCode === 304; + if (options.throwHttpErrors && !isOk) { + // Normally we would have to use `void [await] this._beforeError(error)` everywhere, + // but since there's `void (async () => { ... })()` inside of it, we don't have to. + this._beforeError(new HTTPError(typedResponse)); + // This is equivalent to this.destroyed + if (this[kStopReading]) { + return; + } + } + response.on('readable', () => { + if (this[kTriggerRead]) { + this._read(); + } + }); + this.on('resume', () => { + response.resume(); + }); + this.on('pause', () => { + response.pause(); + }); + response.once('end', () => { + this.push(null); + }); + this.emit('response', response); + for (const destination of this[kServerResponsesPiped]) { + if (destination.headersSent) { + continue; + } + // eslint-disable-next-line guard-for-in + for (const key in response.headers) { + const isAllowed = options.decompress ? key !== 'content-encoding' : true; + const value = response.headers[key]; + if (isAllowed) { + destination.setHeader(key, value); + } + } + destination.statusCode = statusCode; + } + } + async _onResponse(response) { + try { + await this._onResponseBase(response); + } + catch (error) { + this._beforeError(error); + } + } + _onRequest(request) { + const { options } = this; + const { timeout, url } = options; + http_timer_1.default(request); + this[kCancelTimeouts] = timed_out_1.default(request, timeout, url); + const responseEventName = options.cache ? 'cacheableResponse' : 'response'; + request.once(responseEventName, (response) => { + void this._onResponse(response); + }); + request.once('error', (error) => { + var _a; + // Force clean-up, because some packages (e.g. nock) don't do this. + request.destroy(); + // Node.js <= 12.18.2 mistakenly emits the response `end` first. + (_a = request.res) === null || _a === void 0 ? void 0 : _a.removeAllListeners('end'); + if (error instanceof timed_out_1.TimeoutError) { + error = new TimeoutError(error, this.timings, this); + } + else { + error = new RequestError(error.message, error, this); + } + this._beforeError(error); + }); + this[kUnproxyEvents] = proxy_events_1.default(request, this, proxiedRequestEvents); + this[kRequest] = request; + this.emit('uploadProgress', this.uploadProgress); + // Send body + const body = this[kBody]; + const currentRequest = this.redirects.length === 0 ? this : request; + if (is_1.default.nodeStream(body)) { + body.pipe(currentRequest); + body.once('error', (error) => { + this._beforeError(new UploadError(error, this)); + }); + body.once('end', () => { + delete options.body; + }); + } + else { + this._unlockWrite(); + if (!is_1.default.undefined(body)) { + this._writeRequest(body, undefined, () => { }); + currentRequest.end(); + this._lockWrite(); + } + else if (this._cannotHaveBody || this._noPipe) { + currentRequest.end(); + this._lockWrite(); + } + } + this.emit('request', request); + } + async _createCacheableRequest(url, options) { + return new Promise((resolve, reject) => { + // TODO: Remove `utils/url-to-options.ts` when `cacheable-request` is fixed + Object.assign(options, url_to_options_1.default(url)); + // `http-cache-semantics` checks this + delete options.url; + let request; + // This is ugly + const cacheRequest = cacheableStore.get(options.cache)(options, async (response) => { + // TODO: Fix `cacheable-response` + response._readableState.autoDestroy = false; + if (request) { + (await request).emit('cacheableResponse', response); + } + resolve(response); + }); + // Restore options + options.url = url; + cacheRequest.once('error', reject); + cacheRequest.once('request', async (requestOrPromise) => { + request = requestOrPromise; + resolve(request); + }); + }); + } + async _makeRequest() { + var _a; + const { options } = this; + const { headers } = options; + for (const key in headers) { + if (is_1.default.undefined(headers[key])) { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete headers[key]; + } + else if (is_1.default.null_(headers[key])) { + throw new TypeError(`Use \`undefined\` instead of \`null\` to delete the \`${key}\` header`); + } + } + if (options.decompress && is_1.default.undefined(headers['accept-encoding'])) { + headers['accept-encoding'] = supportsBrotli ? 'gzip, deflate, br' : 'gzip, deflate'; + } + // Set cookies + if (options.cookieJar) { + const cookieString = await options.cookieJar.getCookieString(options.url.toString()); + if (is_1.default.nonEmptyString(cookieString)) { + options.headers.cookie = cookieString; + } + } + for (const hook of options.hooks.beforeRequest) { + // eslint-disable-next-line no-await-in-loop + const result = await hook(options); + if (!is_1.default.undefined(result)) { + // @ts-expect-error Skip the type mismatch to support abstract responses + options.request = () => result; + break; + } + } + const { agent, request, timeout, url } = options; + if (options.dnsCache && !('lookup' in options)) { + options.lookup = options.dnsCache.lookup; + } + // UNIX sockets + if (url.hostname === 'unix') { + const matches = /(?.+?):(?.+)/.exec(`${url.pathname}${url.search}`); + if (matches === null || matches === void 0 ? void 0 : matches.groups) { + const { socketPath, path } = matches.groups; + Object.assign(options, { + socketPath, + path, + host: '' + }); + } + } + const isHttps = url.protocol === 'https:'; + // Fallback function + let fallbackFn; + if (options.http2) { + fallbackFn = http2wrapper.auto; + } + else { + fallbackFn = isHttps ? https.request : http.request; + } + const realFn = (_a = options.request) !== null && _a !== void 0 ? _a : fallbackFn; + // Cache support + const fn = options.cache ? this._createCacheableRequest : realFn; + // Pass an agent directly when HTTP2 is disabled + if (agent && !options.http2) { + options.agent = agent[isHttps ? 'https' : 'http']; + } + // Prepare plain HTTP request options + options[kRequest] = realFn; + delete options.request; + delete options.timeout; + const requestOptions = options; + // If `dnsLookupIpVersion` is not present do not override `family` + if (options.dnsLookupIpVersion !== undefined) { + try { + requestOptions.family = dns_ip_version_1.dnsLookupIpVersionToFamily(options.dnsLookupIpVersion); + } + catch (_b) { + throw new Error('Invalid `dnsLookupIpVersion` option value'); + } + } + // HTTPS options remapping + if (options.https) { + if ('rejectUnauthorized' in options.https) { + requestOptions.rejectUnauthorized = options.https.rejectUnauthorized; + } + if (options.https.checkServerIdentity) { + requestOptions.checkServerIdentity = options.https.checkServerIdentity; + } + if (options.https.certificateAuthority) { + requestOptions.ca = options.https.certificateAuthority; + } + if (options.https.certificate) { + requestOptions.cert = options.https.certificate; + } + if (options.https.key) { + requestOptions.key = options.https.key; + } + if (options.https.passphrase) { + requestOptions.passphrase = options.https.passphrase; + } + } + try { + let requestOrResponse = await fn(url, requestOptions); + if (is_1.default.undefined(requestOrResponse)) { + requestOrResponse = fallbackFn(url, requestOptions); + } + // Restore options + options.request = request; + options.timeout = timeout; + options.agent = agent; + if (isClientRequest(requestOrResponse)) { + this._onRequest(requestOrResponse); + // Emit the response after the stream has been ended + } + else if (this.writable) { + this.once('finish', () => { + void this._onResponse(requestOrResponse); + }); + this._unlockWrite(); + this.end(); + this._lockWrite(); + } + else { + void this._onResponse(requestOrResponse); + } + } + catch (error) { + if (error instanceof CacheableRequest.CacheError) { + throw new CacheError(error, this); + } + throw new RequestError(error.message, error, this); + } + } + _beforeError(error) { + if (this.destroyed) { + return; + } + this[kStopReading] = true; + if (!(error instanceof RequestError)) { + error = new RequestError(error.message, error, this); + } + void (async () => { + try { + const { response } = error; + if (response) { + response.setEncoding(this._readableState.encoding); + response.rawBody = await get_buffer_1.default(response); + response.body = response.rawBody.toString(); + } + } + catch (_a) { } + try { + for (const hook of this.options.hooks.beforeError) { + // eslint-disable-next-line no-await-in-loop + error = await hook(error); + } + } + catch (error_) { + error = new RequestError(error_.message, error_, this); + } + this.destroy(error); + })(); + } + _read() { + this[kTriggerRead] = true; + const response = this[kResponse]; + if (response && !this[kStopReading]) { + // We cannot put this in the `if` above + // because `.read()` also triggers the `end` event + if (response.readableLength) { + this[kTriggerRead] = false; + } + let data; + while ((data = response.read()) !== null) { + this[kDownloadedSize] += data.length; + this[kStartedReading] = true; + const progress = this.downloadProgress; + if (progress.percent < 1) { + this.emit('downloadProgress', progress); + } + this.push(data); } } } -} - -/*** - * Call the appropriate login endpoint and parse out the token in the response. - * @param {import('got').Got} client - * @param {string} method - * @param {any} payload - */ -async function getClientToken(client, method, payload) { - /** @type {'json'} */ - const responseType = 'json'; - var options = { - json: payload, - responseType, - }; - - core.debug(`Retrieving Vault Token from v1/auth/${method}/login endpoint`); - - /** @type {import('got').Response} */ - const response = await client.post(`v1/auth/${method}/login`, options); - if (response && response.body && response.body.auth && response.body.auth.client_token) { - core.debug('✔ Vault Token successfully retrieved'); - - core.startGroup('Token Info'); - core.debug(`Operating under policies: ${JSON.stringify(response.body.auth.policies)}`); - core.debug(`Token Metadata: ${JSON.stringify(response.body.auth.metadata)}`); - core.endGroup(); - - return response.body.auth.client_token; - } else { - throw Error(`Unable to retrieve token from ${method}'s login endpoint.`); - } -} - -/*** - * @typedef {Object} VaultLoginResponse - * @property {{ - * client_token: string; - * accessor: string; - * policies: string[]; - * metadata: unknown; - * lease_duration: number; - * renewable: boolean; - * }} auth - */ - -module.exports = { - retrieveToken, -}; - - -/***/ }), - -/***/ 349: -/***/ (function(module) { - -module.exports = eval("require")("got"); - - -/***/ }), - -/***/ 422: -/***/ (function(module) { - -module.exports = eval("require")("@actions/core/lib/command"); - - -/***/ }), - -/***/ 520: -/***/ (function(module, __unusedexports, __webpack_require__) { - -const jsonata = __webpack_require__(128); - - -/** - * @typedef {Object} SecretRequest - * @property {string} path - * @property {string} selector - */ - -/** - * @template {SecretRequest} TRequest - * @typedef {Object} SecretResponse - * @property {TRequest} request - * @property {string} value - * @property {boolean} cachedResponse - */ - - /** - * @template TRequest - * @param {Array} secretRequests - * @param {import('got').Got} client - * @return {Promise[]>} - */ -async function getSecrets(secretRequests, client) { - const responseCache = new Map(); - const results = []; - for (const secretRequest of secretRequests) { - const { path, selector } = secretRequest; - - const requestPath = `v1${path}`; - let body; - let cachedResponse = false; - if (responseCache.has(requestPath)) { - body = responseCache.get(requestPath); - cachedResponse = true; - } else { - const result = await client.get(requestPath); - body = result.body; - responseCache.set(requestPath, body); + // Node.js 12 has incorrect types, so the encoding must be a string + _write(chunk, encoding, callback) { + const write = () => { + this._writeRequest(chunk, encoding, callback); + }; + if (this.requestInitialized) { + write(); } - - const value = selectData(JSON.parse(body), selector); - results.push({ - request: secretRequest, - value, - cachedResponse + else { + this[kJobs].push(write); + } + } + _writeRequest(chunk, encoding, callback) { + this._progressCallbacks.push(() => { + this[kUploadedSize] += Buffer.byteLength(chunk, encoding); + const progress = this.uploadProgress; + if (progress.percent < 1) { + this.emit('uploadProgress', progress); + } + }); + // TODO: What happens if it's from cache? Then this[kRequest] won't be defined. + this[kRequest].write(chunk, encoding, (error) => { + if (!error && this._progressCallbacks.length !== 0) { + this._progressCallbacks.shift()(); + } + callback(error); }); } - return results; -} - -/** - * Uses a Jsonata selector retrieve a bit of data from the result - * @param {object} data - * @param {string} selector - */ -function selectData(data, selector) { - const ata = jsonata(selector); - let result = JSON.stringify(ata.evaluate(data)); - // Compat for custom engines - if (!result && ata.ast().type === "path" && ata.ast()['steps'].length === 1 && selector !== 'data' && 'data' in data) { - result = JSON.stringify(jsonata(`data.${selector}`).evaluate(data)); - } else if (!result) { - throw Error(`Unable to retrieve result for ${selector}. No match data was found. Double check your Key or Selector.`); + _final(callback) { + const endRequest = () => { + // FIX: Node.js 10 calls the write callback AFTER the end callback! + while (this._progressCallbacks.length !== 0) { + this._progressCallbacks.shift()(); + } + // We need to check if `this[kRequest]` is present, + // because it isn't when we use cache. + if (!(kRequest in this)) { + callback(); + return; + } + if (this[kRequest].destroyed) { + callback(); + return; + } + this[kRequest].end((error) => { + if (!error) { + this[kBodySize] = this[kUploadedSize]; + this.emit('uploadProgress', this.uploadProgress); + this[kRequest].emit('upload-complete'); + } + callback(error); + }); + }; + if (this.requestInitialized) { + endRequest(); + } + else { + this[kJobs].push(endRequest); + } } - - if (result.startsWith(`"`)) { - result = result.substring(1, result.length - 1); + _destroy(error, callback) { + var _a; + this[kStopReading] = true; + if (kRequest in this) { + this[kCancelTimeouts](); + // TODO: Remove the next `if` when these get fixed: + // - https://github.com/nodejs/node/issues/32851 + if (!((_a = this[kResponse]) === null || _a === void 0 ? void 0 : _a.complete)) { + this[kRequest].destroy(); + } + } + if (error !== null && !is_1.default.undefined(error) && !(error instanceof RequestError)) { + error = new RequestError(error.message, error, this); + } + callback(error); + } + get ip() { + var _a; + return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.socket.remoteAddress; + } + get aborted() { + var _a, _b, _c; + return ((_b = (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.destroyed) !== null && _b !== void 0 ? _b : this.destroyed) && !((_c = this[kOriginalResponse]) === null || _c === void 0 ? void 0 : _c.complete); + } + get socket() { + var _a; + return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.socket; + } + get downloadProgress() { + let percent; + if (this[kResponseSize]) { + percent = this[kDownloadedSize] / this[kResponseSize]; + } + else if (this[kResponseSize] === this[kDownloadedSize]) { + percent = 1; + } + else { + percent = 0; + } + return { + percent, + transferred: this[kDownloadedSize], + total: this[kResponseSize] + }; + } + get uploadProgress() { + let percent; + if (this[kBodySize]) { + percent = this[kUploadedSize] / this[kBodySize]; + } + else if (this[kBodySize] === this[kUploadedSize]) { + percent = 1; + } + else { + percent = 0; + } + return { + percent, + transferred: this[kUploadedSize], + total: this[kBodySize] + }; + } + get timings() { + var _a; + return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.timings; + } + get isFromCache() { + return this[kIsFromCache]; + } + get _response() { + return this[kResponse]; + } + pipe(destination, options) { + if (this[kStartedReading]) { + throw new Error('Failed to pipe. The response has been emitted already.'); + } + if (destination instanceof http_1.ServerResponse) { + this[kServerResponsesPiped].add(destination); + } + return super.pipe(destination, options); + } + unpipe(destination) { + if (destination instanceof http_1.ServerResponse) { + this[kServerResponsesPiped].delete(destination); + } + super.unpipe(destination); + return this; } - return result; } +exports.default = Request; -module.exports = { - getSecrets, - selectData -} /***/ }), -/***/ 897: +/***/ 988: /***/ (function(module, __unusedexports, __webpack_require__) { -const auth = __webpack_require__(238); -const secrets = __webpack_require__(520); +"use strict"; -module.exports = { - auth, - secrets +const http = __webpack_require__(605); +const https = __webpack_require__(211); +const resolveALPN = __webpack_require__(524); +const QuickLRU = __webpack_require__(904); +const Http2ClientRequest = __webpack_require__(181); +const calculateServerName = __webpack_require__(751); +const urlToOptions = __webpack_require__(507); + +const cache = new QuickLRU({maxSize: 100}); +const queue = new Map(); + +const installSocket = (agent, socket, options) => { + socket._httpMessage = {shouldKeepAlive: true}; + + const onFree = () => { + agent.emit('free', socket, options); + }; + + socket.on('free', onFree); + + const onClose = () => { + agent.removeSocket(socket, options); + }; + + socket.on('close', onClose); + + const onRemove = () => { + agent.removeSocket(socket, options); + socket.off('close', onClose); + socket.off('free', onFree); + socket.off('agentRemove', onRemove); + }; + + socket.on('agentRemove', onRemove); + + agent.emit('free', socket, options); }; +const resolveProtocol = async options => { + const name = `${options.host}:${options.port}:${options.ALPNProtocols.sort()}`; + + if (!cache.has(name)) { + if (queue.has(name)) { + const result = await queue.get(name); + return result.alpnProtocol; + } + + const {path, agent} = options; + options.path = options.socketPath; + + const resultPromise = resolveALPN(options); + queue.set(name, resultPromise); + + try { + const {socket, alpnProtocol} = await resultPromise; + cache.set(name, alpnProtocol); + + options.path = path; + + if (alpnProtocol === 'h2') { + // https://github.com/nodejs/node/issues/33343 + socket.destroy(); + } else { + const {globalAgent} = https; + const defaultCreateConnection = https.Agent.prototype.createConnection; + + if (agent) { + if (agent.createConnection === defaultCreateConnection) { + installSocket(agent, socket, options); + } else { + socket.destroy(); + } + } else if (globalAgent.createConnection === defaultCreateConnection) { + installSocket(globalAgent, socket, options); + } else { + socket.destroy(); + } + } + + queue.delete(name); + + return alpnProtocol; + } catch (error) { + queue.delete(name); + + throw error; + } + } + + return cache.get(name); +}; + +module.exports = async (input, options, callback) => { + if (typeof input === 'string' || input instanceof URL) { + input = urlToOptions(new URL(input)); + } + + if (typeof options === 'function') { + callback = options; + options = undefined; + } + + options = { + ALPNProtocols: ['h2', 'http/1.1'], + ...input, + ...options, + resolveSocket: true + }; + + if (!Array.isArray(options.ALPNProtocols) || options.ALPNProtocols.length === 0) { + throw new Error('The `ALPNProtocols` option must be an Array with at least one entry'); + } + + options.protocol = options.protocol || 'https:'; + const isHttps = options.protocol === 'https:'; + + options.host = options.hostname || options.host || 'localhost'; + options.session = options.tlsSession; + options.servername = options.servername || calculateServerName(options); + options.port = options.port || (isHttps ? 443 : 80); + options._defaultAgent = isHttps ? https.globalAgent : http.globalAgent; + + const agents = options.agent; + + if (agents) { + if (agents.addRequest) { + throw new Error('The `options.agent` object can contain only `http`, `https` or `http2` properties'); + } + + options.agent = agents[isHttps ? 'https' : 'http']; + } + + if (isHttps) { + const protocol = await resolveProtocol(options); + + if (protocol === 'h2') { + if (agents) { + options.agent = agents.http2; + } + + return new Http2ClientRequest(options, callback); + } + } + + return http.request(options, callback); +}; + +module.exports.protocolCache = cache; + + /***/ }), -/***/ 968: -/***/ (function(module) { +/***/ 997: +/***/ (function(module, __unusedexports, __webpack_require__) { -module.exports = eval("require")("@actions/core"); +"use strict"; + +const pump = __webpack_require__(453); +const bufferStream = __webpack_require__(375); + +class MaxBufferError extends Error { + constructor() { + super('maxBuffer exceeded'); + this.name = 'MaxBufferError'; + } +} + +async function getStream(inputStream, options) { + if (!inputStream) { + return Promise.reject(new Error('Expected a stream')); + } + + options = { + maxBuffer: Infinity, + ...options + }; + + const {maxBuffer} = options; + + let stream; + await new Promise((resolve, reject) => { + const rejectPromise = error => { + if (error) { // A null check + error.bufferedData = stream.getBufferedValue(); + } + + reject(error); + }; + + stream = pump(inputStream, bufferStream(options), error => { + if (error) { + rejectPromise(error); + return; + } + + resolve(); + }); + + stream.on('data', () => { + if (stream.getBufferedLength() > maxBuffer) { + rejectPromise(new MaxBufferError()); + } + }); + }); + + return stream.getBufferedValue(); +} + +module.exports = getStream; +// TODO: Remove this for the next major release +module.exports.default = getStream; +module.exports.buffer = (stream, options) => getStream(stream, {...options, encoding: 'buffer'}); +module.exports.array = (stream, options) => getStream(stream, {...options, array: true}); +module.exports.MaxBufferError = MaxBufferError; /***/ })